git.lucas.co / cce-ui
GPU-accelerated UI toolkit (Vulkan)
git clone https://git.lucas.co/cce-ui.git

commit180af574fc8adc68b2f7115b28eb479e6471c0e5
parentd190c43b46
authorLucas Galante <[email protected]>
date2026-06-09 10:10
Refactor and adapt widgets to use UiContext and scale factor

 examples/test_svg_render.rs            |   21 +-
 src/backend/mod.rs                     |    8 +
 src/backend/wgpu_adapter.rs            |  121 +
 src/backend/window_runner.rs           | 1900 ++++++++++++
 src/context.rs                         |  372 +++
 src/engine.rs                          | 1900 +-----------
 src/layout.rs                          | 1641 +++++++----
 src/lib.rs                             |    3 +
 src/main.rs                            |  111 +-
 src/shader.wgsl                        |   13 +-
 src/widget/container.rs                | 5076 --------------------------------
 src/widget/container/breadcrumb.rs     |  115 +
 src/widget/container/container.rs      |   36 +
 src/widget/container/content_bg.rs     |  236 ++
 src/widget/container/header.rs         |   19 +
 src/widget/container/menu.rs           | 1297 ++++++++
 src/widget/container/mod.rs            |   25 +
 src/widget/container/paginator.rs      | 1002 +++++++
 src/widget/container/parameters_bg.rs  | 1028 +++++++
 src/widget/container/plate.rs          |  527 ++++
 src/widget/container/scroll_box.rs     |  248 ++
 src/widget/container/scrolling_list.rs |  117 +
 src/widget/container/spreadsheet.rs    |  397 +++
 src/widget/container/viewport_bg.rs    |   20 +
 src/widget/core.rs                     |   54 +-
 src/widget/display.rs                  | 2357 ---------------
 src/widget/display/float3.rs           |  309 ++
 src/widget/display/font_preview.rs     |   91 +
 src/widget/display/graph.rs            |  499 ++++
 src/widget/display/info_box.rs         |   71 +
 src/widget/display/label.rs            |  172 ++
 src/widget/display/layout_preview.rs   |  277 ++
 src/widget/display/list_item.rs        |  137 +
 src/widget/display/mod.rs              |   40 +
 src/widget/display/node.rs             |  189 ++
 src/widget/display/panel.rs            |   89 +
 src/widget/display/progress_bar.rs     |   28 +
 src/widget/display/separator.rs        |   33 +
 src/widget/display/serialize.rs        |   76 +
 src/widget/display/sidebar.rs          |   19 +
 src/widget/display/splitter.rs         |   68 +
 src/widget/display/status_bar.rs       |  106 +
 src/widget/display/status_dot.rs       |   42 +
 src/widget/display/svg.rs              |   90 +
 src/widget/display/text_label.rs       |   91 +
 src/widget/display/usage_bar.rs        |   44 +
 src/widget/editor.rs                   |  216 ++
 src/widget/input.rs                    | 3076 -------------------
 src/widget/input/button.rs             |  179 ++
 src/widget/input/canvas.rs             |   19 +
 src/widget/input/checkbox.rs           |  224 ++
 src/widget/input/color_selector.rs     |  542 ++++
 src/widget/input/dropdown.rs           |  376 +++
 src/widget/input/font_selector.rs      |  143 +
 src/widget/input/mod.rs                |   24 +
 src/widget/input/slider.rs             |  638 ++++
 src/widget/input/spinbox.rs            |  334 +++
 src/widget/input/text_box.rs           | 1030 +++++++
 src/widget/input/trackpad.rs           |  164 ++
 src/widget/json_layout.rs              |  459 ++-
 src/widget/mod.rs                      |  277 +-
 61 files changed, 15498 insertions(+), 13318 deletions(-)

diff --git a/examples/test_svg_render.rs b/examples/test_svg_render.rs
index e8012b2..5ddc751 100644
--- a/examples/test_svg_render.rs
+++ b/examples/test_svg_render.rs
@@ -1,25 +1,32 @@
 fn main() {
     let opt = resvg::usvg::Options::default();
-    let mut fontdb = resvg::usvg::fontdb::Database::new();
-    fontdb.load_system_fonts();
+    let fontdb = clear_ui::widget::get_font_db();
 
     // 1. Original 32x120
     let svg_32_120 = r##"<svg width="32" height="120" xmlns="http://www.w3.org/2000/svg">
-  <text x="16" y="60" font-family="sans-serif" font-size="12" fill="#E6E6F2" text-anchor="middle" dominant-baseline="middle" transform="rotate(-90 16 60)">Audio</text>
+  <text x="16" y="60" font-family="Berkeley Mono" font-size="12" fill="#E6E6F2" text-anchor="middle" dominant-baseline="middle" transform="rotate(-90 16 60)">Audio</text>
 </svg>"##;
-    let tree1 = resvg::usvg::Tree::from_data(svg_32_120.as_bytes(), &opt, &fontdb).unwrap();
+    let tree1 = resvg::usvg::Tree::from_data(svg_32_120.as_bytes(), &opt, fontdb).unwrap();
     let mut pixmap1 = resvg::tiny_skia::Pixmap::new(32, 120).unwrap();
     resvg::render(&tree1, resvg::tiny_skia::Transform::default(), &mut pixmap1.as_mut());
     pixmap1.save_png("/home/lsgalante/Dropbox/Clear/scratch/test_32_120.png").unwrap();
 
     // 2. Square 120x120
     let svg_120_120 = r##"<svg width="120" height="120" xmlns="http://www.w3.org/2000/svg">
-  <text x="60" y="60" font-family="sans-serif" font-size="12" fill="#E6E6F2" text-anchor="middle" dominant-baseline="middle" transform="rotate(-90 60 60)">Audio</text>
+  <text x="60" y="60" font-family="Berkeley Mono" font-size="12" fill="#E6E6F2" text-anchor="middle" dominant-baseline="middle" transform="rotate(-90 60 60)">Audio</text>
 </svg>"##;
-    let tree2 = resvg::usvg::Tree::from_data(svg_120_120.as_bytes(), &opt, &fontdb).unwrap();
+    let tree2 = resvg::usvg::Tree::from_data(svg_120_120.as_bytes(), &opt, fontdb).unwrap();
     let mut pixmap2 = resvg::tiny_skia::Pixmap::new(120, 120).unwrap();
     resvg::render(&tree2, resvg::tiny_skia::Transform::default(), &mut pixmap2.as_mut());
     pixmap2.save_png("/home/lsgalante/Dropbox/Clear/scratch/test_120_120.png").unwrap();
 
-    println!("Both images rendered successfully.");
+    // Count non-transparent pixels
+    let mut count = 0;
+    for &alpha in pixmap1.data().iter().skip(3).step_by(4) {
+        if alpha > 0 {
+            count += 1;
+        }
+    }
+    println!("test_32_120.png has {} non-transparent pixels.", count);
 }
+
diff --git a/src/backend/mod.rs b/src/backend/mod.rs
new file mode 100644
index 0000000..d15d26d
--- /dev/null
+++ b/src/backend/mod.rs
@@ -0,0 +1,8 @@
+pub mod wgpu_adapter;
+pub mod window_runner;
+
+pub use wgpu_adapter::WgpuAdapter;
+pub use window_runner::{
+    EngineState, WindowSettings, LogicalPosition, LogicalSize, Application, run,
+    Vertex, LineCap, ActivePopup, PressedKey,
+};
diff --git a/src/backend/wgpu_adapter.rs b/src/backend/wgpu_adapter.rs
new file mode 100644
index 0000000..fd16a54
--- /dev/null
+++ b/src/backend/wgpu_adapter.rs
@@ -0,0 +1,121 @@
+use crate::wayland::WaylandSurfaceHandle;
+use glyphon::{Cache, FontSystem, Resolution, SwashCache, TextAtlas, TextRenderer, Viewport};
+
+pub struct WgpuAdapter {
+    pub instance: wgpu::Instance,
+    pub surface: wgpu::Surface<'static>,
+    pub adapter: wgpu::Adapter,
+    pub device: wgpu::Device,
+    pub queue: wgpu::Queue,
+    pub config: wgpu::SurfaceConfiguration,
+    pub font_system: FontSystem,
+    pub swash_cache: SwashCache,
+    pub text_atlas: TextAtlas,
+    pub text_renderer: TextRenderer,
+    pub text_viewport: Viewport,
+}
+
+impl WgpuAdapter {
+    pub async fn new(
+        display_ptr: *mut std::ffi::c_void,
+        surface_ptr: *mut std::ffi::c_void,
+        width: u32,
+        height: u32,
+    ) -> Self {
+        let wayland_handle = Box::leak(Box::new(WaylandSurfaceHandle {
+            display_ptr,
+            surface_ptr,
+        }));
+
+        let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor {
+            backends: wgpu::Backends::VULKAN,
+            ..Default::default()
+        });
+
+        let surface = instance
+            .create_surface(wayland_handle)
+            .expect("Failed to create surface");
+
+        let adapter = instance
+            .request_adapter(&wgpu::RequestAdapterOptions {
+                power_preference: wgpu::PowerPreference::LowPower,
+                compatible_surface: Some(&surface),
+                force_fallback_adapter: false,
+            })
+            .await
+            .expect("Failed to find adapter");
+
+        let (device, queue) = adapter
+            .request_device(
+                &wgpu::DeviceDescriptor {
+                    label: Some("GPU Device"),
+                    required_features: wgpu::Features::empty(),
+                    required_limits: wgpu::Limits::downlevel_webgl2_defaults().using_resolution(adapter.limits()),
+                    memory_hints: wgpu::MemoryHints::MemoryUsage,
+                },
+                None,
+            )
+            .await
+            .expect("Failed to create device");
+
+        let mut config = surface
+            .get_default_config(&adapter, width.max(1), height.max(1))
+            .expect("Failed to get surface default config");
+
+        let capabilities = surface.get_capabilities(&adapter);
+        let alpha_mode = if capabilities.alpha_modes.contains(&wgpu::CompositeAlphaMode::PreMultiplied) {
+            wgpu::CompositeAlphaMode::PreMultiplied
+        } else if capabilities.alpha_modes.contains(&wgpu::CompositeAlphaMode::PostMultiplied) {
+            wgpu::CompositeAlphaMode::PostMultiplied
+        } else {
+            capabilities.alpha_modes[0]
+        };
+        config.alpha_mode = alpha_mode;
+        config.usage = wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_DST;
+
+        let present_mode = if capabilities.present_modes.contains(&wgpu::PresentMode::Mailbox) {
+            wgpu::PresentMode::Mailbox
+        } else if capabilities.present_modes.contains(&wgpu::PresentMode::Immediate) {
+            wgpu::PresentMode::Immediate
+        } else if capabilities.present_modes.contains(&wgpu::PresentMode::FifoRelaxed) {
+            wgpu::PresentMode::FifoRelaxed
+        } else {
+            wgpu::PresentMode::Fifo
+        };
+        config.present_mode = present_mode;
+
+        surface.configure(&device, &config);
+
+        // Initialize text rendering
+        let font_system = FontSystem::new();
+        let swash_cache = SwashCache::new();
+        let cache = Cache::new(&device);
+        let mut text_atlas = TextAtlas::new(&device, &queue, &cache, config.format);
+        let text_renderer = TextRenderer::new(&mut text_atlas, &device, wgpu::MultisampleState::default(), None);
+        let mut text_viewport = Viewport::new(&device, &cache);
+        text_viewport.update(&queue, Resolution { width: width.max(1), height: height.max(1) });
+
+        Self {
+            instance,
+            surface,
+            adapter,
+            device,
+            queue,
+            config,
+            font_system,
+            swash_cache,
+            text_atlas,
+            text_renderer,
+            text_viewport,
+        }
+    }
+
+    pub fn resize(&mut self, width: u32, height: u32) {
+        if width > 0 && height > 0 {
+            self.config.width = width;
+            self.config.height = height;
+            self.surface.configure(&self.device, &self.config);
+            self.text_viewport.update(&self.queue, Resolution { width, height });
+        }
+    }
+}
diff --git a/src/backend/window_runner.rs b/src/backend/window_runner.rs
new file mode 100644
index 0000000..2f9d4d5
--- /dev/null
+++ b/src/backend/window_runner.rs
@@ -0,0 +1,1900 @@
+use std::time::Instant;
+use smithay_client_toolkit::{
+    compositor::{CompositorHandler, CompositorState},
+    delegate_compositor, delegate_keyboard, delegate_pointer, delegate_registry,
+    delegate_seat, delegate_shm, delegate_xdg_shell, delegate_xdg_window, delegate_output, delegate_xdg_popup,
+    registry::{ProvidesRegistryState, RegistryState},
+    output::{OutputHandler, OutputState},
+    seat::{
+        keyboard::KeyboardHandler,
+        pointer::{PointerHandler, ThemedPointer, ThemeSpec, CursorIcon},
+        Capability, SeatHandler, SeatState,
+    },
+    shell::{
+        xdg::{
+            window::{Window as XdgWindow, WindowConfigure, WindowHandler, WindowDecorations},
+            XdgShell,
+        },
+        WaylandSurface,
+    },
+    shm::{Shm, ShmHandler},
+};
+use wayland_client::{
+    globals::{registry_queue_init, GlobalList},
+    protocol::{wl_keyboard, wl_output, wl_pointer, wl_seat, wl_shm, wl_surface, wl_registry},
+    Connection, QueueHandle, Proxy,
+};
+use smithay_client_toolkit::shell::xdg::popup::{Popup, PopupHandler, PopupConfigure};
+use smithay_client_toolkit::shell::xdg::{XdgPositioner, XdgSurface};
+use smithay_client_toolkit::reexports::protocols::xdg::shell::client::xdg_positioner::{Anchor, Gravity, ConstraintAdjustment};
+
+use calloop::EventLoop;
+use calloop_wayland_source::WaylandSource;
+use glyphon::{
+    Cache, FontSystem, Resolution, SwashCache, TextArea, TextAtlas,
+    TextBounds, TextRenderer, Viewport, Buffer, Attrs, Metrics,
+};
+use crate::widget::{TextItem, MouseButton, ElementState, MouseScrollDelta, KeyEvent, Key, NamedKey};
+use crate::wayland::{WaylandSurfaceHandle, detect_scale_factor};
+use crate::backend::WgpuAdapter;
+
+pub struct ActivePopup {
+    pub sctk_popup: Popup,
+    pub wgpu_surface: wgpu::Surface<'static>,
+    pub config: wgpu::SurfaceConfiguration,
+    pub logical_width: f32,
+    pub logical_height: f32,
+    pub configured: bool,
+    pub viewport: Viewport,
+    pub x: f32,
+    pub y: f32,
+    pub vertex_buffer: Option<wgpu::Buffer>,
+}
+
+fn make_text_buffer_with_font(fs: &mut FontSystem, text: &str, size: f32, font: Option<&str>) -> Buffer {
+    let scale = crate::scale::scale_factor();
+    let mut font_size = size;
+    let mut family_name = None;
+
+    if let Some(font_str) = font {
+        let (parsed_family, parsed_size) = crate::layout::parse_font_string(font_str);
+        if let Some(ps) = parsed_size {
+            font_size = ps;
+        }
+        family_name = Some(parsed_family);
+    }
+
+    let physical_size = font_size * scale;
+    let metrics = Metrics::new(physical_size, physical_size * 1.4);
+    let mut buf = Buffer::new(fs, metrics);
+    let mut attrs = Attrs::new();
+    if let Some(ref font_family) = family_name {
+        let family = match font_family.as_str() {
+            "monospace" => glyphon::Family::Monospace,
+            "sans-serif" => glyphon::Family::SansSerif,
+            "serif" => glyphon::Family::Serif,
+            name => glyphon::Family::Name(name),
+        };
+        attrs = attrs.family(family);
+    }
+    buf.set_text(fs, text, attrs, glyphon::Shaping::Advanced);
+    buf.shape_until_scroll(fs, true);
+    buf
+}
+
+#[repr(C)]
+#[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
+pub struct Vertex {
+    pub position: [f32; 2],
+    pub color: [f32; 4],
+    pub clip_circle: [f32; 3], // [cx, cy, r]
+}
+
+impl Vertex {
+    const ATTRIBS: [wgpu::VertexAttribute; 3] = wgpu::vertex_attr_array![
+        0 => Float32x2,
+        1 => Float32x4,
+        2 => Float32x3,
+    ];
+
+    pub fn desc() -> wgpu::VertexBufferLayout<'static> {
+        wgpu::VertexBufferLayout {
+            array_stride: std::mem::size_of::<Vertex>() as wgpu::BufferAddress,
+            step_mode: wgpu::VertexStepMode::Vertex,
+            attributes: &Self::ATTRIBS,
+        }
+    }
+}
+
+pub fn quad_vertices(x: f32, y: f32, w: f32, h: f32, sw: f32, sh: f32, c: [f32; 4]) -> [Vertex; 6] {
+    let x0 = (x / sw) * 2.0 - 1.0;
+    let y0 = 1.0 - (y / sh) * 2.0;
+    let x1 = ((x + w) / sw) * 2.0 - 1.0;
+    let y1 = 1.0 - ((y + h) / sh) * 2.0;
+    [
+        Vertex { position: [x0, y0], color: c, clip_circle: [0.0, 0.0, 0.0] },
+        Vertex { position: [x1, y0], color: c, clip_circle: [0.0, 0.0, 0.0] },
+        Vertex { position: [x0, y1], color: c, clip_circle: [0.0, 0.0, 0.0] },
+        Vertex { position: [x1, y0], color: c, clip_circle: [0.0, 0.0, 0.0] },
+        Vertex { position: [x1, y1], color: c, clip_circle: [0.0, 0.0, 0.0] },
+        Vertex { position: [x0, y1], color: c, clip_circle: [0.0, 0.0, 0.0] },
+    ]
+}
+
+pub fn quad_vertices_with_clip(
+    x: f32, y: f32, w: f32, h: f32,
+    sw: f32, sh: f32,
+    color: [f32; 4],
+    clip_circle: [f32; 3],
+) -> [Vertex; 6] {
+    let x0 = (x / sw) * 2.0 - 1.0;
+    let y0 = 1.0 - (y / sh) * 2.0;
+    let x1 = ((x + w) / sw) * 2.0 - 1.0;
+    let y1 = 1.0 - ((y + h) / sh) * 2.0;
+    [
+        Vertex { position: [x0, y0], color, clip_circle },
+        Vertex { position: [x1, y0], color, clip_circle },
+        Vertex { position: [x0, y1], color, clip_circle },
+        Vertex { position: [x1, y0], color, clip_circle },
+        Vertex { position: [x1, y1], color, clip_circle },
+        Vertex { position: [x0, y1], color, clip_circle },
+    ]
+}
+
+pub fn quad_vertices_clipped(
+    x: f32, y: f32, w: f32, h: f32,
+    surface_w: f32, surface_h: f32,
+    color: [f32; 4],
+    clip: (f32, f32, f32, f32),
+    clip_circle: [f32; 3],
+) -> Vec<Vertex> {
+    let (cx0, cy0, cx1, cy1) = clip;
+    let ix0 = x.max(cx0);
+    let iy0 = y.max(cy0);
+    let ix1 = (x + w).min(cx1);
+    let iy1 = (y + h).min(cy1);
+    if ix1 <= ix0 || iy1 <= iy0 {
+        return Vec::new();
+    }
+    quad_vertices_with_clip(ix0, iy0, ix1 - ix0, iy1 - iy0, surface_w, surface_h, color, clip_circle).to_vec()
+}
+
+pub fn line_vertices(
+    x1: f32, y1: f32, x2: f32, y2: f32,
+    thickness: f32,
+    sw: f32, sh: f32,
+    c: [f32; 4]
+) -> [Vertex; 6] {
+    let dx = x2 - x1;
+    let dy = y2 - y1;
+    let len = (dx * dx + dy * dy).sqrt();
+    if len < 0.001 {
+        return quad_vertices(x1 - thickness/2.0, y1 - thickness/2.0, thickness, thickness, sw, sh, c);
+    }
+    let ux = dx / len;
+    let uy = dy / len;
+    let nx = -uy;
+    let ny = ux;
+    
+    let half_t = thickness * 0.5;
+    let p0x = x1 + nx * half_t;
+    let p0y = y1 + ny * half_t;
+    let p1x = x1 - nx * half_t;
+    let p1y = y1 - ny * half_t;
+    let p2x = x2 - nx * half_t;
+    let p2y = y2 - ny * half_t;
+    let p3x = x2 + nx * half_t;
+    let p3y = y2 + ny * half_t;
+
+    let ndc_p0x = (p0x / sw) * 2.0 - 1.0;
+    let ndc_p0y = 1.0 - (p0y / sh) * 2.0;
+    let ndc_p1x = (p1x / sw) * 2.0 - 1.0;
+    let ndc_p1y = 1.0 - (p1y / sh) * 2.0;
+    let ndc_p2x = (p2x / sw) * 2.0 - 1.0;
+    let ndc_p2y = 1.0 - (p2y / sh) * 2.0;
+    let ndc_p3x = (p3x / sw) * 2.0 - 1.0;
+    let ndc_p3y = 1.0 - (p3y / sh) * 2.0;
+
+    let clip_circle = [0.0, 0.0, 0.0];
+    [
+        Vertex { position: [ndc_p0x, ndc_p0y], color: c, clip_circle },
+        Vertex { position: [ndc_p1x, ndc_p1y], color: c, clip_circle },
+        Vertex { position: [ndc_p2x, ndc_p2y], color: c, clip_circle },
+        Vertex { position: [ndc_p0x, ndc_p0y], color: c, clip_circle },
+        Vertex { position: [ndc_p2x, ndc_p2y], color: c, clip_circle },
+        Vertex { position: [ndc_p3x, ndc_p3y], color: c, clip_circle },
+    ]
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, serde::Serialize, serde::Deserialize)]
+pub enum LineCap {
+    Arrow,
+    Round,
+    Flat,
+}
+
+pub fn vector_vertices(
+    x1: f32, y1: f32, x2: f32, y2: f32,
+    thickness: f32,
+    sw: f32, sh: f32,
+    c: [f32; 4],
+    line_cap: LineCap,
+) -> Vec<Vertex> {
+    let mut verts = Vec::new();
+    let dx = x2 - x1;
+    let dy = y2 - y1;
+    let len = (dx * dx + dy * dy).sqrt();
+    if len < 0.001 {
+        return quad_vertices(x1 - thickness/2.0, y1 - thickness/2.0, thickness, thickness, sw, sh, c).to_vec();
+    }
+    
+    match line_cap {
+        LineCap::Arrow => {
+            let ux = dx / len;
+            let uy = dy / len;
+            let nx = -uy;
+            let ny = ux;
+            
+            let arrow_len = (thickness * 3.0).max(10.0).min(len);
+            let arrow_width = (thickness * 2.5).max(8.0);
+            
+            let line_x2 = x2 - ux * arrow_len;
+            let line_y2 = y2 - uy * arrow_len;
+            
+            if len > arrow_len {
+                verts.extend_from_slice(&line_vertices(x1, y1, line_x2, line_y2, thickness, sw, sh, c));
+            }
+            
+            let bx = line_x2;
+            let by = line_y2;
+            
+            let w1x = bx + nx * (arrow_width * 0.5);
+            let w1y = by + ny * (arrow_width * 0.5);
+            let w2x = bx - nx * (arrow_width * 0.5);
+            let w2y = by - ny * (arrow_width * 0.5);
+            
+            let ndc_tip_x = (x2 / sw) * 2.0 - 1.0;
+            let ndc_tip_y = 1.0 - (y2 / sh) * 2.0;
+            let ndc_w1x = (w1x / sw) * 2.0 - 1.0;
+            let ndc_w1y = 1.0 - (w1y / sh) * 2.0;
+            let ndc_w2x = (w2x / sw) * 2.0 - 1.0;
+            let ndc_w2y = 1.0 - (w2y / sh) * 2.0;
+            
+            let clip_circle = [0.0, 0.0, 0.0];
+            verts.push(Vertex { position: [ndc_tip_x, ndc_tip_y], color: c, clip_circle });
+            verts.push(Vertex { position: [ndc_w1x, ndc_w1y], color: c, clip_circle });
+            verts.push(Vertex { position: [ndc_w2x, ndc_w2y], color: c, clip_circle });
+        }
+        LineCap::Round => {
+            verts.extend_from_slice(&line_vertices(x1, y1, x2, y2, thickness, sw, sh, c));
+            let clip_circle = [0.0, 0.0, 0.0];
+            verts.extend(circle_vertices(x2, y2, thickness / 2.0, sw, sh, c, 16, clip_circle));
+        }
+        LineCap::Flat => {
+            verts.extend_from_slice(&line_vertices(x1, y1, x2, y2, thickness, sw, sh, c));
+        }
+    }
+    
+    verts
+}
+
+pub fn rounded_rect_vertices_corners(
+    x: f32, y: f32, ww: f32, h: f32,
+    r: f32,
+    sw: f32, sh: f32,
+    color: [f32; 4],
+    clip_circle: [f32; 3],
+    corners: (bool, bool, bool, bool),
+    clip_rect: Option<(f32, f32, f32, f32)>,
+) -> Vec<Vertex> {
+    let mut verts = Vec::new();
+    push_rounded_rect_vertices_corners(x, y, ww, h, r, sw, sh, color, clip_circle, corners, clip_rect, &mut verts);
+    verts
+}
+
+pub fn push_rounded_rect_vertices_corners(
+    x: f32, y: f32, ww: f32, h: f32,
+    r: f32,
+    sw: f32, sh: f32,
+    color: [f32; 4],
+    clip_circle: [f32; 3],
+    corners: (bool, bool, bool, bool),
+    clip_rect: Option<(f32, f32, f32, f32)>,
+    out: &mut Vec<Vertex>,
+) {
+    let r = r.min(ww * 0.5).min(h * 0.5);
+
+    let clamp_x = |val: f32| -> f32 {
+        if let Some((cx0, _, cx1, _)) = clip_rect {
+            val.max(cx0).min(cx1)
+        } else {
+            val
+        }
+    };
+    let clamp_y = |val: f32| -> f32 {
+        if let Some((_, cy0, _, cy1)) = clip_rect {
+            val.max(cy0).min(cy1)
+        } else {
+            val
+        }
+    };
+
+    let push_quad = |verts: &mut Vec<Vertex>, qx: f32, qy: f32, qw: f32, qh: f32| {
+        let x0 = clamp_x(qx);
+        let y0 = clamp_y(qy);
+        let x1 = clamp_x(qx + qw);
+        let y1 = clamp_y(qy + qh);
+        
+        if x1 <= x0 || y1 <= y0 {
+            return;
+        }
+
+        let ndc_x0 = (x0 / sw) * 2.0 - 1.0;
+        let ndc_y0 = 1.0 - (y0 / sh) * 2.0;
+        let ndc_x1 = (x1 / sw) * 2.0 - 1.0;
+        let ndc_y1 = 1.0 - (y1 / sh) * 2.0;
+        
+        verts.push(Vertex { position: [ndc_x0, ndc_y0], color, clip_circle });
+        verts.push(Vertex { position: [ndc_x1, ndc_y0], color, clip_circle });
+        verts.push(Vertex { position: [ndc_x0, ndc_y1], color, clip_circle });
+        verts.push(Vertex { position: [ndc_x1, ndc_y0], color, clip_circle });
+        verts.push(Vertex { position: [ndc_x1, ndc_y1], color, clip_circle });
+        verts.push(Vertex { position: [ndc_x0, ndc_y1], color, clip_circle });
+    };
+
+    if r <= 0.1 || (!corners.0 && !corners.1 && !corners.2 && !corners.3) {
+        push_quad(out, x, y, ww, h);
+        return;
+    }
+
+    // 1. Center rectangle
+    push_quad(out, x + r, y, ww - 2.0 * r, h);
+    
+    // 2. Left rectangle
+    push_quad(out, x, y + r, r, h - 2.0 * r);
+    
+    // 3. Right rectangle
+    push_quad(out, x + ww - r, y + r, r, h - 2.0 * r);
+
+    // 4. Four corners
+    let corner_configs = [
+        // Top-left
+        (corners.0, x, y, x + r, y + r, std::f32::consts::PI, 1.5 * std::f32::consts::PI),
+        // Top-right
+        (corners.1, x + ww - r, y, x + ww - r, y + r, 1.5 * std::f32::consts::PI, 2.0 * std::f32::consts::PI),
+        // Bottom-right
+        (corners.2, x + ww - r, y + h - r, x + ww - r, y + h - r, 0.0, 0.5 * std::f32::consts::PI),
+        // Bottom-left
+        (corners.3, x, y + h - r, x + r, y + h - r, 0.5 * std::f32::consts::PI, std::f32::consts::PI),
+    ];
+
+    let segments = 16;
+    for &(is_rounded, sqx, sqy, cx, cy, start, end) in &corner_configs {
+        if is_rounded {
+            for i in 0..segments {
+                let theta1 = start + (i as f32) * (end - start) / (segments as f32);
+                let theta2 = start + ((i + 1) as f32) * (end - start) / (segments as f32);
+                
+                let x0 = clamp_x(cx);
+                let y0 = clamp_y(cy);
+                let x1 = clamp_x(cx + r * theta1.cos());
+                let y1 = clamp_y(cy + r * theta1.sin());
+                let x2 = clamp_x(cx + r * theta2.cos());
+                let y2 = clamp_y(cy + r * theta2.sin());
+                
+                let ndc_x0 = (x0 / sw) * 2.0 - 1.0;
+                let ndc_y0 = 1.0 - (y0 / sh) * 2.0;
+                let ndc_x1 = (x1 / sw) * 2.0 - 1.0;
+                let ndc_y1 = 1.0 - (y1 / sh) * 2.0;
+                let ndc_x2 = (x2 / sw) * 2.0 - 1.0;
+                let ndc_y2 = 1.0 - (y2 / sh) * 2.0;
+                
+                out.push(Vertex { position: [ndc_x0, ndc_y0], color, clip_circle });
+                out.push(Vertex { position: [ndc_x1, ndc_y1], color, clip_circle });
+                out.push(Vertex { position: [ndc_x2, ndc_y2], color, clip_circle });
+            }
+        } else {
+            push_quad(out, sqx, sqy, r, r);
+        }
+    }
+}
+
+pub fn rounded_rect_vertices(
+    x: f32, y: f32, ww: f32, h: f32,
+    r: f32,
+    sw: f32, sh: f32,
+    color: [f32; 4],
+    clip_circle: [f32; 3],
+) -> Vec<Vertex> {
+    rounded_rect_vertices_corners(x, y, ww, h, r, sw, sh, color, clip_circle, (true, true, true, true), None)
+}
+
+pub fn push_rounded_rect_vertices(
+    x: f32, y: f32, ww: f32, h: f32,
+    r: f32,
+    sw: f32, sh: f32,
+    color: [f32; 4],
+    clip_circle: [f32; 3],
+    out: &mut Vec<Vertex>,
+) {
+    push_rounded_rect_vertices_corners(x, y, ww, h, r, sw, sh, color, clip_circle, (true, true, true, true), None, out);
+}
+
+pub fn plate_bevel_vertices(
+    x: f32, y: f32, ww: f32, h: f32,
+    r: f32,
+    t: f32,
+    sw: f32, sh: f32,
+    clip_circle: [f32; 3],
+) -> Vec<Vertex> {
+    let mut verts = Vec::new();
+    push_plate_bevel_vertices(x, y, ww, h, r, t, sw, sh, clip_circle, &mut verts);
+    verts
+}
+
+pub fn push_plate_bevel_vertices(
+    x: f32, y: f32, ww: f32, h: f32,
+    r: f32,
+    t: f32,
+    sw: f32, sh: f32,
+    clip_circle: [f32; 3],
+    out: &mut Vec<Vertex>,
+) {
+    let highlight_color = [1.0, 1.0, 1.0, 0.15];
+    let shadow_color = [0.0, 0.0, 0.0, 0.25];
+
+    out.extend_from_slice(&quad_vertices_with_clip(x + r, y, ww - 2.0 * r, t, sw, sh, highlight_color, clip_circle));
+    out.extend_from_slice(&quad_vertices_with_clip(x, y + r, t, h - 2.0 * r, sw, sh, highlight_color, clip_circle));
+    out.extend_from_slice(&quad_vertices_with_clip(x + r, y + h - t, ww - 2.0 * r, t, sw, sh, shadow_color, clip_circle));
+    out.extend_from_slice(&quad_vertices_with_clip(x + ww - t, y + r, t, h - 2.0 * r, sw, sh, shadow_color, clip_circle));
+
+    let segments = 16;
+
+    push_arc_background_vertices(
+        x + r, y + r, r, t,
+        std::f32::consts::PI, 1.5 * std::f32::consts::PI,
+        sw, sh, highlight_color, segments, clip_circle,
+        out,
+    );
+
+    push_arc_background_vertices(
+        x + ww - r, y + r, r, t,
+        1.5 * std::f32::consts::PI, 1.75 * std::f32::consts::PI,
+        sw, sh, highlight_color, segments / 2, clip_circle,
+        out,
+    );
+    push_arc_background_vertices(
+        x + ww - r, y + r, r, t,
+        1.75 * std::f32::consts::PI, 2.0 * std::f32::consts::PI,
+        sw, sh, shadow_color, segments / 2, clip_circle,
+        out,
+    );
+
+    push_arc_background_vertices(
+        x + ww - r, y + h - r, r, t,
+        0.0, 0.5 * std::f32::consts::PI,
+        sw, sh, shadow_color, segments, clip_circle,
+        out,
+    );
+
+    push_arc_background_vertices(
+        x + r, y + h - r, r, t,
+        0.5 * std::f32::consts::PI, 0.75 * std::f32::consts::PI,
+        sw, sh, shadow_color, segments / 2, clip_circle,
+        out,
+    );
+    push_arc_background_vertices(
+        x + r, y + h - r, r, t,
+        0.75 * std::f32::consts::PI, std::f32::consts::PI,
+        sw, sh, highlight_color, segments / 2, clip_circle,
+        out,
+    );
+}
+
+pub fn widget_vertices(w: &dyn crate::widget::Element, sw: f32, sh: f32, clip_circle: [f32; 3]) -> Vec<Vertex> {
+    let mut verts = Vec::new();
+    push_widget_vertices(w, sw, sh, clip_circle, &mut verts);
+    verts
+}
+
+pub fn push_widget_vertices(w: &dyn crate::widget::Element, sw: f32, sh: f32, clip_circle: [f32; 3], out: &mut Vec<Vertex>) {
+    let (x, y, ww, h) = w.rect();
+    let corners = w.rounded_corners();
+    if corners != (false, false, false, false) {
+        push_rounded_rect_vertices_corners(x, y, ww, h, 12.0, sw, sh, w.color(), clip_circle, corners, None, out);
+    } else {
+        out.extend_from_slice(&quad_vertices_with_clip(x, y, ww, h, sw, sh, w.color(), clip_circle));
+    }
+
+    if w.is_plate() {
+        push_plate_bevel_vertices(x, y, ww, h, 12.0, 1.5, sw, sh, clip_circle, out);
+    }
+}
+
+pub fn extra_quad_vertices(
+    w: &dyn crate::widget::Element,
+    qx: f32, qy: f32, qw: f32, qh: f32,
+    sw: f32, sh: f32,
+    qc: [f32; 4],
+    clip_circle: [f32; 3],
+) -> Vec<Vertex> {
+    let mut verts = Vec::new();
+    push_extra_quad_vertices(w, qx, qy, qw, qh, sw, sh, qc, clip_circle, &mut verts);
+    verts
+}
+
+pub fn push_extra_quad_vertices(
+    w: &dyn crate::widget::Element,
+    qx: f32, qy: f32, qw: f32, qh: f32,
+    sw: f32, sh: f32,
+    qc: [f32; 4],
+    clip_circle: [f32; 3],
+    out: &mut Vec<Vertex>,
+) {
+    let corners = w.rounded_corners();
+    if corners == (false, false, false, false) {
+        out.extend_from_slice(&quad_vertices_with_clip(qx, qy, qw, qh, sw, sh, qc, clip_circle));
+        return;
+    }
+
+    let (wx, wy, ww, wh) = w.rect();
+    let extra_corners = (
+        corners.0 && qx <= wx + 0.1 && qy <= wy + 0.1,
+        corners.1 && qx + qw >= wx + ww - 0.1 && qy <= wy + 0.1,
+        corners.2 && qx + qw >= wx + ww - 0.1 && qy + qh >= wy + wh - 0.1,
+        corners.3 && qx <= wx + 0.1 && qy + qh >= wy + wh - 0.1,
+    );
+
+    push_rounded_rect_vertices_corners(qx, qy, qw, qh, 12.0, sw, sh, qc, clip_circle, extra_corners, None, out);
+}
+
+pub fn extra_quad_vertices_clipped(
+    w: &dyn crate::widget::Element,
+    qx: f32, qy: f32, qw: f32, qh: f32,
+    sw: f32, sh: f32,
+    qc: [f32; 4],
+    clip: (f32, f32, f32, f32),
+    clip_circle: [f32; 3],
+) -> Vec<Vertex> {
+    let mut verts = Vec::new();
+    push_extra_quad_vertices_clipped(w, qx, qy, qw, qh, sw, sh, qc, clip, clip_circle, &mut verts);
+    verts
+}
+
+pub fn push_extra_quad_vertices_clipped(
+    w: &dyn crate::widget::Element,
+    qx: f32, qy: f32, qw: f32, qh: f32,
+    sw: f32, sh: f32,
+    qc: [f32; 4],
+    clip: (f32, f32, f32, f32),
+    clip_circle: [f32; 3],
+    out: &mut Vec<Vertex>,
+) {
+    let corners = w.rounded_corners();
+    if corners == (false, false, false, false) {
+        let (cx0, cy0, cx1, cy1) = clip;
+        let ix0 = qx.max(cx0);
+        let iy0 = qy.max(cy0);
+        let ix1 = (qx + qw).min(cx1);
+        let iy1 = (qy + qh).min(cy1);
+        if ix1 <= ix0 || iy1 <= iy0 {
+            return;
+        }
+        out.extend_from_slice(&quad_vertices_with_clip(ix0, iy0, ix1 - ix0, iy1 - iy0, sw, sh, qc, clip_circle));
+        return;
+    }
+
+    let (wx, wy, ww, wh) = w.rect();
+    let extra_corners = (
+        corners.0 && qx <= wx + 0.1 && qy <= wy + 0.1,
+        corners.1 && qx + qw >= wx + ww - 0.1 && qy <= wy + 0.1,
+        corners.2 && qx + qw >= wx + ww - 0.1 && qy + qh >= wy + wh - 0.1,
+        corners.3 && qx <= wx + 0.1 && qy + qh >= wy + wh - 0.1,
+    );
+
+    push_rounded_rect_vertices_corners(qx, qy, qw, qh, 12.0, sw, sh, qc, clip_circle, extra_corners, Some(clip), out);
+}
+
+pub fn circle_vertices(
+    cx: f32, cy: f32, r: f32,
+    sw: f32, sh: f32,
+    color: [f32; 4],
+    segments: usize,
+    clip_circle: [f32; 3],
+) -> Vec<Vertex> {
+    let mut verts = Vec::new();
+    for i in 0..segments {
+        let theta1 = (i as f32) * 2.0 * std::f32::consts::PI / (segments as f32);
+        let theta2 = ((i + 1) as f32) * 2.0 * std::f32::consts::PI / (segments as f32);
+        let x0 = cx;
+        let y0 = cy;
+        let x1 = cx + r * theta1.cos();
+        let y1 = cy + r * theta1.sin();
+        let x2 = cx + r * theta2.cos();
+        let y2 = cy + r * theta2.sin();
+        
+        let ndc_x0 = (x0 / sw) * 2.0 - 1.0;
+        let ndc_y0 = 1.0 - (y0 / sh) * 2.0;
+        let ndc_x1 = (x1 / sw) * 2.0 - 1.0;
+        let ndc_y1 = 1.0 - (y1 / sh) * 2.0;
+        let ndc_x2 = (x2 / sw) * 2.0 - 1.0;
+        let ndc_y2 = 1.0 - (y2 / sh) * 2.0;
+        
+        verts.push(Vertex { position: [ndc_x0, ndc_y0], color, clip_circle });
+        verts.push(Vertex { position: [ndc_x1, ndc_y1], color, clip_circle });
+        verts.push(Vertex { position: [ndc_x2, ndc_y2], color, clip_circle });
+    }
+    verts
+}
+
+pub fn circle_border_vertices(
+    cx: f32, cy: f32, r: f32,
+    thickness: f32,
+    sw: f32, sh: f32,
+    color: [f32; 4],
+    segments: usize,
+    clip_circle: [f32; 3],
+) -> Vec<Vertex> {
+    let mut verts = Vec::new();
+    for i in 0..segments {
+        let theta1 = (i as f32) * 2.0 * std::f32::consts::PI / (segments as f32);
+        let theta2 = ((i + 1) as f32) * 2.0 * std::f32::consts::PI / (segments as f32);
+        
+        let x0 = cx + (r - thickness) * theta1.cos();
+        let y0 = cy + (r - thickness) * theta1.sin();
+        let x1 = cx + r * theta1.cos();
+        let y1 = cy + r * theta1.sin();
+        
+        let x2 = cx + r * theta2.cos();
+        let y2 = cy + r * theta2.sin();
+        let x3 = cx + (r - thickness) * theta2.cos();
+        let y3 = cy + (r - thickness) * theta2.sin();
+        
+        let ndc_x0 = (x0 / sw) * 2.0 - 1.0; let ndc_y0 = 1.0 - (y0 / sh) * 2.0;
+        let ndc_x1 = (x1 / sw) * 2.0 - 1.0; let ndc_y1 = 1.0 - (y1 / sh) * 2.0;
+        let ndc_x2 = (x2 / sw) * 2.0 - 1.0; let ndc_y2 = 1.0 - (y2 / sh) * 2.0;
+        let ndc_x3 = (x3 / sw) * 2.0 - 1.0; let ndc_y3 = 1.0 - (y3 / sh) * 2.0;
+        
+        verts.push(Vertex { position: [ndc_x0, ndc_y0], color, clip_circle });
+        verts.push(Vertex { position: [ndc_x1, ndc_y1], color, clip_circle });
+        verts.push(Vertex { position: [ndc_x2, ndc_y2], color, clip_circle });
+        
+        verts.push(Vertex { position: [ndc_x0, ndc_y0], color, clip_circle });
+        verts.push(Vertex { position: [ndc_x2, ndc_y2], color, clip_circle });
+        verts.push(Vertex { position: [ndc_x3, ndc_y3], color, clip_circle });
+    }
+    verts
+}
+
+pub fn arc_background_vertices(
+    cx: f32, cy: f32, r: f32,
+    thickness: f32,
+    start_angle: f32, end_angle: f32,
+    sw: f32, sh: f32,
+    color: [f32; 4],
+    segments: usize,
+    clip_circle: [f32; 3],
+) -> Vec<Vertex> {
+    let mut verts = Vec::new();
+    push_arc_background_vertices(cx, cy, r, thickness, start_angle, end_angle, sw, sh, color, segments, clip_circle, &mut verts);
+    verts
+}
+
+pub fn push_arc_background_vertices(
+    cx: f32, cy: f32, r: f32,
+    thickness: f32,
+    start_angle: f32, end_angle: f32,
+    sw: f32, sh: f32,
+    color: [f32; 4],
+    segments: usize,
+    clip_circle: [f32; 3],
+    out: &mut Vec<Vertex>,
+) {
+    for i in 0..segments {
+        let theta1 = start_angle + (i as f32) * (end_angle - start_angle) / (segments as f32);
+        let theta2 = start_angle + ((i + 1) as f32) * (end_angle - start_angle) / (segments as f32);
+        
+        let x0 = cx + (r - thickness) * theta1.cos();
+        let y0 = cy + (r - thickness) * theta1.sin();
+        let x1 = cx + r * theta1.cos();
+        let y1 = cy + r * theta1.sin();
+        
+        let x2 = cx + r * theta2.cos();
+        let y2 = cy + r * theta2.sin();
+        let x3 = cx + (r - thickness) * theta2.cos();
+        let y3 = cy + (r - thickness) * theta2.sin();
+        
+        let ndc_x0 = (x0 / sw) * 2.0 - 1.0; let ndc_y0 = 1.0 - (y0 / sh) * 2.0;
+        let ndc_x1 = (x1 / sw) * 2.0 - 1.0; let ndc_y1 = 1.0 - (y1 / sh) * 2.0;
+        let ndc_x2 = (x2 / sw) * 2.0 - 1.0; let ndc_y2 = 1.0 - (y2 / sh) * 2.0;
+        let ndc_x3 = (x3 / sw) * 2.0 - 1.0; let ndc_y3 = 1.0 - (y3 / sh) * 2.0;
+        
+        out.push(Vertex { position: [ndc_x0, ndc_y0], color, clip_circle });
+        out.push(Vertex { position: [ndc_x1, ndc_y1], color, clip_circle });
+        out.push(Vertex { position: [ndc_x2, ndc_y2], color, clip_circle });
+        
+        out.push(Vertex { position: [ndc_x0, ndc_y0], color, clip_circle });
+        out.push(Vertex { position: [ndc_x2, ndc_y2], color, clip_circle });
+        out.push(Vertex { position: [ndc_x3, ndc_y3], color, clip_circle });
+    }
+}
+
+#[derive(Debug, Clone)]
+pub struct WindowSettings {
+    pub title: String,
+    pub app_id: String,
+    pub width: u32,
+    pub height: u32,
+    pub fullscreen: bool,
+    pub min_size: Option<(u32, u32)>,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq)]
+pub struct LogicalPosition {
+    pub x: f32,
+    pub y: f32,
+}
+
+impl LogicalPosition {
+    pub fn new(x: f32, y: f32) -> Self {
+        Self { x, y }
+    }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq)]
+pub struct LogicalSize {
+    pub width: f32,
+    pub height: f32,
+}
+
+impl LogicalSize {
+    pub fn new(width: f32, height: f32) -> Self {
+        Self { width, height }
+    }
+}
+
+pub struct RenderContext<'a> {
+    pub font_system: &'a mut FontSystem,
+}
+
+pub trait Application: Sized + 'static {
+    type Message: Send + Clone + 'static;
+
+    fn new(qh: &QueueHandle<EngineState<Self>>, sender: calloop::channel::Sender<Self::Message>) -> Self;
+    fn settings(&self) -> WindowSettings;
+    fn update(&mut self, msg: Self::Message, needs_rebuild: &mut bool, exit: &mut bool);
+    fn tick(&mut self, dt: f32, needs_rebuild: &mut bool);
+    fn view(&mut self, quads: &mut Vec<(f32, f32, f32, f32, [f32; 4])>, size: LogicalSize, scale: f64);
+    fn view_vectors(&mut self, _vectors: &mut Vec<(f32, f32, f32, f32, f32, [f32; 4], LineCap)>, _size: LogicalSize, _scale: f64) {}
+    fn overlay_quads(&mut self, _quads: &mut Vec<(f32, f32, f32, f32, [f32; 4])>, _size: LogicalSize, _scale: f64) {}
+    fn text_items(&self) -> &[TextItem];
+    fn render_popovers(&self, _pc: &mut dyn crate::layout::RenderTarget) {}
+    
+    fn text_areas(&self, scale_f32: f32, bounds: TextBounds) -> Vec<TextArea<'_>> {
+        self.text_items().iter().map(|ti| {
+            let item_bounds = if let Some([l, t, r, b]) = ti.bounds {
+                TextBounds {
+                    left: (l * scale_f32).round() as i32,
+                    top: (t * scale_f32).round() as i32,
+                    right: (r * scale_f32).round() as i32,
+                    bottom: (b * scale_f32).round() as i32,
+                }
+            } else {
+                bounds
+            };
+            TextArea {
+                buffer: &ti.buffer,
+                left: (ti.x * scale_f32).round(),
+                top: (ti.y * scale_f32).round(),
+                scale: 1.0,
+                bounds: item_bounds,
+                default_color: ti.color,
+                custom_glyphs: &[],
+            }
+        }).collect()
+    }
+    
+    fn clear_color(&self) -> [f32; 4] {
+        [0.0, 0.0, 0.0, 0.0]
+    }
+    
+    fn handle_pointer_move(&mut self, pos: LogicalPosition, needs_rebuild: &mut bool);
+    fn handle_mouse_input(&mut self, button: MouseButton, state: ElementState, pos: LogicalPosition, needs_rebuild: &mut bool) -> Option<Self::Message>;
+    fn handle_mouse_wheel(&mut self, delta: &MouseScrollDelta, pos: LogicalPosition, needs_rebuild: &mut bool);
+    fn handle_key_input(&mut self, event: &KeyEvent, needs_rebuild: &mut bool) -> Option<Self::Message>;
+}
+
+pub struct PressedKey {
+    pub logical_key: Key,
+    pub text: Option<String>,
+    pub first_pressed: Instant,
+    pub last_repeated: Instant,
+}
+
+fn is_repeatable_key(key: &Key) -> bool {
+    match key {
+        Key::Named(NamedKey::Backspace) |
+        Key::Named(NamedKey::Delete) |
+        Key::Named(NamedKey::ArrowLeft) |
+        Key::Named(NamedKey::ArrowRight) |
+        Key::Named(NamedKey::ArrowUp) |
+        Key::Named(NamedKey::ArrowDown) |
+        Key::Named(NamedKey::Home) |
+        Key::Named(NamedKey::End) |
+        Key::Character(_) => true,
+        _ => false,
+    }
+}
+
+pub struct EngineState<A: Application> {
+    pub registry_state: RegistryState,
+    pub compositor_state: CompositorState,
+    pub xdg_shell_state: XdgShell,
+    pub shm_state: Shm,
+    pub seat_state: SeatState,
+    pub output_state: OutputState,
+    pub seats: Vec<wl_seat::WlSeat>,
+    pub pointer: Option<ThemedPointer>,
+    pub keyboard: Option<wl_keyboard::WlKeyboard>,
+    
+    pub window: Option<XdgWindow>,
+    pub surface: Option<wl_surface::WlSurface>,
+    
+    pub inner: A,
+    
+    pub wgpu_adapter: Option<WgpuAdapter>,
+    pub render_pipeline: Option<wgpu::RenderPipeline>,
+    pub vertex_buffer: Option<wgpu::Buffer>,
+    pub vertex_count: u32,
+    pub overlay_vertex_buffer: Option<wgpu::Buffer>,
+    pub overlay_vertex_count: u32,
+    
+    pub scale_factor: f64,
+    pub logical_width: f32,
+    pub logical_height: f32,
+    
+    pub exit: bool,
+    pub redraw: bool,
+    pub first_configure_received: bool,
+    pub ctrl_pressed: bool,
+    pub shift_pressed: bool,
+    pub pressed_key: Option<PressedKey>,
+    pub sender: calloop::channel::Sender<A::Message>,
+    pub active_popup: Option<ActivePopup>,
+    pub qh: QueueHandle<EngineState<A>>,
+}
+
+impl<A: Application> EngineState<A> {
+    pub async fn init_gpu(&mut self, conn: &Connection, width_logical: f32, height_logical: f32) {
+        let s = self.scale_factor as f32;
+        let pw = (width_logical * s) as u32;
+        let ph = (height_logical * s) as u32;
+        
+        let surface = self.surface.as_ref().expect("surface missing");
+        
+        let display_ptr = conn.backend().display_id().as_ptr() as *mut std::ffi::c_void;
+        let surface_ptr = surface.id().as_ptr() as *mut std::ffi::c_void;
+        
+        let mut adapter = WgpuAdapter::new(display_ptr, surface_ptr, pw, ph).await;
+        
+        let shader = adapter.device.create_shader_module(wgpu::ShaderModuleDescriptor {
+            label: Some("Shader"),
+            source: wgpu::ShaderSource::Wgsl(crate::SHADER.into()),
+        });
+        
+        let pipeline_layout = adapter.device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
+            label: Some("Pipeline Layout"),
+            bind_group_layouts: &[],
+            push_constant_ranges: &[],
+        });
+        
+        let render_pipeline = adapter.device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
+            label: Some("Render Pipeline"),
+            layout: Some(&pipeline_layout),
+            vertex: wgpu::VertexState {
+                module: &shader,
+                entry_point: Some("vs_main"),
+                buffers: &[Vertex::desc()],
+                compilation_options: Default::default(),
+            },
+            fragment: Some(wgpu::FragmentState {
+                module: &shader,
+                entry_point: Some("fs_main"),
+                targets: &[Some(wgpu::ColorTargetState {
+                    format: adapter.config.format,
+                    blend: Some(wgpu::BlendState::ALPHA_BLENDING),
+                    write_mask: wgpu::ColorWrites::ALL,
+                })],
+                compilation_options: Default::default(),
+            }),
+            primitive: wgpu::PrimitiveState {
+                topology: wgpu::PrimitiveTopology::TriangleList,
+                front_face: wgpu::FrontFace::Ccw,
+                cull_mode: None,
+                polygon_mode: wgpu::PolygonMode::Fill,
+                unclipped_depth: false,
+                conservative: false,
+                strip_index_format: None,
+            },
+            depth_stencil: None,
+            multisample: wgpu::MultisampleState { count: 1, mask: !0, alpha_to_coverage_enabled: false },
+            multiview: None,
+            cache: None,
+        });
+        
+        let vertex_buffer = adapter.device.create_buffer(&wgpu::BufferDescriptor {
+            label: Some("Vertex Buffer"),
+            size: 1,
+            usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
+            mapped_at_creation: false,
+        });
+
+        let overlay_vertex_buffer = adapter.device.create_buffer(&wgpu::BufferDescriptor {
+            label: Some("Overlay Vertex Buffer"),
+            size: 1,
+            usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
+            mapped_at_creation: false,
+        });
+        
+        self.wgpu_adapter = Some(adapter);
+        self.render_pipeline = Some(render_pipeline);
+        self.vertex_buffer = Some(vertex_buffer);
+        self.overlay_vertex_buffer = Some(overlay_vertex_buffer);
+        self.logical_width = width_logical;
+        self.logical_height = height_logical;
+    }
+    
+    pub fn resize(&mut self, w: f32, h: f32) {
+        if w > 0.0 && h > 0.0 {
+            self.logical_width = w;
+            self.logical_height = h;
+            if let Some(ref mut adapter) = self.wgpu_adapter {
+                adapter.resize((w as f64 * self.scale_factor) as u32, (h as f64 * self.scale_factor) as u32);
+            }
+        }
+    }
+    
+    pub fn render(&mut self) {
+        let logical_w = self.logical_width;
+        let logical_h = self.logical_height;
+        let scale_factor = self.scale_factor;
+        
+        let mut quads = Vec::new();
+        self.inner.view(&mut quads, LogicalSize::new(logical_w, logical_h), scale_factor);
+        
+        let mut vectors = Vec::new();
+        self.inner.view_vectors(&mut vectors, LogicalSize::new(logical_w, logical_h), scale_factor);
+        
+        let adapter = self.wgpu_adapter.as_mut().unwrap();
+        let render_pipeline = self.render_pipeline.as_ref().unwrap();
+        
+        // 1. Build and upload vertex buffer
+        let mut verts = Vec::new();
+        for &(qx, qy, qw, qh, qc) in &quads {
+            verts.extend(quad_vertices(qx, qy, qw, qh, logical_w, logical_h, qc));
+        }
+        for &(vx1, vy1, vx2, vy2, vthickness, vcolor, vcap) in &vectors {
+            verts.extend(vector_vertices(vx1, vy1, vx2, vy2, vthickness, logical_w, logical_h, vcolor, vcap));
+        }
+        self.vertex_count = verts.len() as u32;
+        if self.vertex_count > 0 {
+            let data = bytemuck::cast_slice(&verts);
+            let needed = data.len() as wgpu::BufferAddress;
+            let mut vbuf = self.vertex_buffer.as_ref().unwrap();
+            if needed > vbuf.size() {
+                let new_vbuf = adapter.device.create_buffer(&wgpu::BufferDescriptor {
+                    label: Some("Vertex Buffer"),
+                    size: needed,
+                    usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
+                    mapped_at_creation: false,
+                });
+                self.vertex_buffer = Some(new_vbuf);
+                vbuf = self.vertex_buffer.as_ref().unwrap();
+            }
+            adapter.queue.write_buffer(vbuf, 0, data);
+        }
+
+        // 1b. Build and upload overlay vertex buffer
+        let mut overlay_quads = Vec::new();
+        self.inner.overlay_quads(&mut overlay_quads, LogicalSize::new(logical_w, logical_h), scale_factor);
+        let mut overlay_verts = Vec::new();
+        for &(qx, qy, qw, qh, qc) in &overlay_quads {
+            overlay_verts.extend(quad_vertices(qx, qy, qw, qh, logical_w, logical_h, qc));
+        }
+        self.overlay_vertex_count = overlay_verts.len() as u32;
+        if self.overlay_vertex_count > 0 {
+            let data = bytemuck::cast_slice(&overlay_verts);
+            let needed = data.len() as wgpu::BufferAddress;
+            let mut ovbuf = self.overlay_vertex_buffer.as_ref().unwrap();
+            if needed > ovbuf.size() {
+                let new_ovbuf = adapter.device.create_buffer(&wgpu::BufferDescriptor {
+                    label: Some("Overlay Vertex Buffer"),
+                    size: needed,
+                    usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
+                    mapped_at_creation: false,
+                });
+                self.overlay_vertex_buffer = Some(new_ovbuf);
+                ovbuf = self.overlay_vertex_buffer.as_ref().unwrap();
+            }
+            adapter.queue.write_buffer(ovbuf, 0, data);
+        }
+        
+        // 2. Prepare text
+        let scale_f32 = scale_factor as f32;
+        let pw = (logical_w * scale_f32) as u32;
+        let ph = (logical_h * scale_f32) as u32;
+        adapter.text_viewport.update(&adapter.queue, Resolution { width: pw, height: ph });
+        
+        let bounds = TextBounds { left: 0, top: 0, right: pw as i32, bottom: ph as i32 };
+        let areas = self.inner.text_areas(scale_f32, bounds);
+        
+        adapter.text_renderer.prepare(&adapter.device, &adapter.queue, &mut adapter.font_system, &mut adapter.text_atlas, &adapter.text_viewport, areas, &mut adapter.swash_cache).unwrap();
+        
+        // 3. Render Pass
+        let output = match adapter.surface.get_current_texture() {
+            Ok(t) => t,
+            Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => {
+                adapter.surface.configure(&adapter.device, &adapter.config);
+                return;
+            }
+            Err(wgpu::SurfaceError::Timeout) => return,
+            Err(e) => {
+                eprintln!("Surface error: {e:?}");
+                return;
+            }
+        };
+        let view = output.texture.create_view(&wgpu::TextureViewDescriptor::default());
+        let mut encoder = adapter.device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
+            label: Some("Encoder"),
+        });
+        
+        {
+            let cc = self.inner.clear_color();
+            let r_clear = (cc[0] as f64).powf(2.2);
+            let g_clear = (cc[1] as f64).powf(2.2);
+            let b_clear = (cc[2] as f64).powf(2.2);
+            let a_clear = cc[3] as f64;
+            
+            let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
+                label: Some("Render Pass"),
+                color_attachments: &[Some(wgpu::RenderPassColorAttachment {
+                    view: &view,
+                    resolve_target: None,
+                    ops: wgpu::Operations {
+                        load: wgpu::LoadOp::Clear(wgpu::Color {
+                            r: r_clear,
+                            g: g_clear,
+                            b: b_clear,
+                            a: a_clear,
+                        }),
+                        store: wgpu::StoreOp::Store,
+                    },
+                })],
+                depth_stencil_attachment: None,
+                timestamp_writes: None,
+                occlusion_query_set: None,
+            });
+            
+            if self.vertex_count > 0 {
+                pass.set_pipeline(render_pipeline);
+                pass.set_vertex_buffer(0, self.vertex_buffer.as_ref().unwrap().slice(..));
+                pass.draw(0..self.vertex_count, 0..1);
+            }
+            
+            adapter.text_renderer.render(&adapter.text_atlas, &adapter.text_viewport, &mut pass).unwrap();
+ 
+            if self.overlay_vertex_count > 0 {
+                pass.set_pipeline(render_pipeline);
+                pass.set_vertex_buffer(0, self.overlay_vertex_buffer.as_ref().unwrap().slice(..));
+                pass.draw(0..self.overlay_vertex_count, 0..1);
+            }
+        }
+        
+        adapter.queue.submit(std::iter::once(encoder.finish()));
+        output.present();
+        
+        if let Some(ref mut popup) = self.active_popup {
+            if popup.configured {
+                let mut collector = crate::layout::PopoverCollector::new();
+                self.inner.render_popovers(&mut collector);
+ 
+                let mut verts = Vec::new();
+                for &(color, qx, qy, qw, qh) in &collector.rects {
+                    let qx_local = qx - popup.x;
+                    let qy_local = qy - popup.y;
+                    verts.extend(quad_vertices(qx_local, qy_local, qw, qh, popup.logical_width, popup.logical_height, color));
+                }
+ 
+                let vertex_count = verts.len() as u32;
+                if vertex_count > 0 {
+                    let data = bytemuck::cast_slice(&verts);
+                    let needed = data.len() as wgpu::BufferAddress;
+                    let mut vbuf = popup.vertex_buffer.as_ref();
+                    if vbuf.map_or(true, |v| needed > v.size()) {
+                        let new_vbuf = adapter.device.create_buffer(&wgpu::BufferDescriptor {
+                            label: Some("Popup Vertex Buffer"),
+                            size: needed,
+                            usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
+                            mapped_at_creation: false,
+                        });
+                        popup.vertex_buffer = Some(new_vbuf);
+                        vbuf = popup.vertex_buffer.as_ref();
+                    }
+                    adapter.queue.write_buffer(vbuf.unwrap(), 0, data);
+                }
+ 
+                let mut text_items = Vec::new();
+                for (content, size, tx, ty, color, font, bounds) in collector.texts {
+                    let tx_local = tx - popup.x;
+                    let ty_local = ty - popup.y;
+                    text_items.push(TextItem {
+                        buffer: make_text_buffer_with_font(&mut adapter.font_system, &content, size, font.as_deref()),
+                        x: tx_local,
+                        y: ty_local,
+                        color: glyphon::Color::rgb(
+                            (color[0] * 255.0).clamp(0.0, 255.0) as u8,
+                            (color[1] * 255.0).clamp(0.0, 255.0) as u8,
+                            (color[2] * 255.0).clamp(0.0, 255.0) as u8,
+                        ),
+                        bounds,
+                    });
+                }
+ 
+                let scale_f32 = scale_factor as f32;
+                let bounds = TextBounds {
+                    left: 0,
+                    top: 0,
+                    right: (popup.logical_width * scale_f32) as i32,
+                    bottom: (popup.logical_height * scale_f32) as i32,
+                };
+                let areas: Vec<TextArea<'_>> = text_items.iter().map(|ti| TextArea {
+                    buffer: &ti.buffer,
+                    left: (ti.x * scale_f32).round(),
+                    top: (ti.y * scale_f32).round(),
+                    scale: 1.0,
+                    bounds,
+                    default_color: ti.color,
+                    custom_glyphs: &[],
+                }).collect();
+ 
+                popup.viewport.update(&adapter.queue, Resolution {
+                    width: (popup.logical_width * scale_f32) as u32,
+                    height: (popup.logical_height * scale_f32) as u32,
+                });
+ 
+                adapter.text_renderer.prepare(
+                    &adapter.device,
+                    &adapter.queue,
+                    &mut adapter.font_system,
+                    &mut adapter.text_atlas,
+                    &popup.viewport,
+                    areas,
+                    &mut adapter.swash_cache,
+                ).unwrap();
+ 
+                let popup_output = match popup.wgpu_surface.get_current_texture() {
+                    Ok(t) => t,
+                    Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => {
+                        popup.wgpu_surface.configure(&adapter.device, &popup.config);
+                        return;
+                    }
+                    Err(wgpu::SurfaceError::Timeout) => return,
+                    Err(e) => {
+                        eprintln!("Popup surface error: {e:?}");
+                        return;
+                    }
+                };
+                let popup_view = popup_output.texture.create_view(&wgpu::TextureViewDescriptor::default());
+                let mut popup_encoder = adapter.device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
+                    label: Some("Popup Encoder"),
+                });
+ 
+                {
+                    let mut pass = popup_encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
+                        label: Some("Popup Render Pass"),
+                        color_attachments: &[Some(wgpu::RenderPassColorAttachment {
+                            view: &popup_view,
+                            resolve_target: None,
+                            ops: wgpu::Operations {
+                                load: wgpu::LoadOp::Clear(wgpu::Color { r: 0.0, g: 0.0, b: 0.0, a: 0.0 }),
+                                store: wgpu::StoreOp::Store,
+                            },
+                        })],
+                        depth_stencil_attachment: None,
+                        timestamp_writes: None,
+                        occlusion_query_set: None,
+                    });
+ 
+                    if vertex_count > 0 {
+                        pass.set_pipeline(render_pipeline);
+                        pass.set_vertex_buffer(0, popup.vertex_buffer.as_ref().unwrap().slice(..));
+                        pass.draw(0..vertex_count, 0..1);
+                    }
+ 
+                    adapter.text_renderer.render(&adapter.text_atlas, &popup.viewport, &mut pass).unwrap();
+                }
+ 
+                adapter.queue.submit(std::iter::once(popup_encoder.finish()));
+                popup_output.present();
+            }
+        }
+ 
+        adapter.text_atlas.trim();
+    }
+}
+
+impl<A: Application> Drop for EngineState<A> {
+    fn drop(&mut self) {
+        self.wgpu_adapter = None;
+        self.render_pipeline = None;
+        self.vertex_buffer = None;
+        self.overlay_vertex_buffer = None;
+    }
+}
+
+impl<A: Application> CompositorHandler for EngineState<A> {
+    fn scale_factor_changed(
+        &mut self,
+        _conn: &Connection,
+        _qh: &QueueHandle<Self>,
+        _surface: &wl_surface::WlSurface,
+        scale_factor: i32,
+    ) {
+        _surface.set_buffer_scale(scale_factor);
+        self.scale_factor = scale_factor as f64;
+        self.resize(self.logical_width, self.logical_height);
+        self.redraw = true;
+    }
+    
+    fn transform_changed(
+        &mut self,
+        _conn: &Connection,
+        _qh: &QueueHandle<Self>,
+        _surface: &wl_surface::WlSurface,
+        _new_transform: wl_output::Transform,
+    ) {}
+    
+    fn frame(
+        &mut self,
+        _conn: &Connection,
+        _qh: &QueueHandle<Self>,
+        _surface: &wl_surface::WlSurface,
+        _time: u32,
+    ) {}
+    
+    fn surface_enter(
+        &mut self,
+        _conn: &Connection,
+        _qh: &QueueHandle<Self>,
+        _surface: &wl_surface::WlSurface,
+        _output: &wl_output::WlOutput,
+    ) {}
+    
+    fn surface_leave(
+        &mut self,
+        _conn: &Connection,
+        _qh: &QueueHandle<Self>,
+        _surface: &wl_surface::WlSurface,
+        _output: &wl_output::WlOutput,
+    ) {}
+}
+
+impl<A: Application> OutputHandler for EngineState<A> {
+    fn output_state(&mut self) -> &mut OutputState {
+        &mut self.output_state
+    }
+    
+    fn new_output(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, _output: wl_output::WlOutput) {}
+    fn update_output(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, _output: wl_output::WlOutput) {}
+    fn output_destroyed(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, _output: wl_output::WlOutput) {}
+}
+
+impl<A: Application> ShmHandler for EngineState<A> {
+    fn shm_state(&mut self) -> &mut Shm {
+        &mut self.shm_state
+    }
+}
+
+impl<A: Application> ProvidesRegistryState for EngineState<A> {
+    fn registry(&mut self) -> &mut RegistryState {
+        &mut self.registry_state
+    }
+    
+    fn runtime_add_global(
+        &mut self,
+        _conn: &Connection,
+        _qh: &QueueHandle<Self>,
+        _name: u32,
+        _interface: &str,
+        _version: u32,
+    ) {}
+    
+    fn runtime_remove_global(
+        &mut self,
+        _conn: &Connection,
+        _qh: &QueueHandle<Self>,
+        _name: u32,
+        _interface: &str,
+    ) {}
+}
+
+impl<A: Application> WindowHandler for EngineState<A> {
+    fn configure(
+        &mut self,
+        _conn: &Connection,
+        _qh: &QueueHandle<Self>,
+        _window: &XdgWindow,
+        configure: WindowConfigure,
+        _serial: u32,
+    ) {
+        let (w, h) = configure.new_size;
+        if let (Some(w), Some(h)) = (w, h) {
+            let width = w.get();
+            let height = h.get();
+            self.resize(width as f32, height as f32);
+        } else {
+            let settings = self.inner.settings();
+            let w = settings.width as f32;
+            let h = settings.height as f32;
+            self.resize(w, h);
+        }
+        self.redraw = true;
+        self.first_configure_received = true;
+    }
+    
+    fn request_close(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, _window: &XdgWindow) {
+        self.exit = true;
+    }
+}
+
+impl<A: Application> SeatHandler for EngineState<A> {
+    fn seat_state(&mut self) -> &mut SeatState {
+        &mut self.seat_state
+    }
+    
+    fn new_seat(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, seat: wl_seat::WlSeat) {
+        self.seats.push(seat);
+    }
+    
+    fn new_capability(
+        &mut self,
+        _conn: &Connection,
+        qh: &QueueHandle<Self>,
+        seat: wl_seat::WlSeat,
+        capability: Capability,
+    ) {
+        if capability == Capability::Pointer && self.pointer.is_none() {
+            let surface = self.compositor_state.create_surface(qh);
+            let themed_pointer = self.seat_state.get_pointer_with_theme(
+                qh,
+                &seat,
+                self.shm_state.wl_shm(),
+                surface,
+                ThemeSpec::System,
+            ).unwrap();
+            self.pointer = Some(themed_pointer);
+        }
+        if capability == Capability::Keyboard && self.keyboard.is_none() {
+            let keyboard = self.seat_state.get_keyboard(qh, &seat, None).unwrap();
+            self.keyboard = Some(keyboard);
+        }
+    }
+    
+    fn remove_capability(
+        &mut self,
+        _conn: &Connection,
+        _qh: &QueueHandle<Self>,
+        _seat: wl_seat::WlSeat,
+        capability: Capability,
+    ) {
+        if capability == Capability::Pointer {
+            self.pointer = None;
+        }
+        if capability == Capability::Keyboard {
+            self.keyboard = None;
+        }
+    }
+    
+    fn remove_seat(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, seat: wl_seat::WlSeat) {
+        self.seats.retain(|s| s != &seat);
+    }
+}
+
+impl<A: Application> PointerHandler for EngineState<A> {
+    fn pointer_frame(
+        &mut self,
+        _conn: &Connection,
+        _qh: &QueueHandle<Self>,
+        _pointer: &wl_pointer::WlPointer,
+        events: &[smithay_client_toolkit::seat::pointer::PointerEvent],
+    ) {
+        use smithay_client_toolkit::seat::pointer::PointerEventKind;
+        for event in events {
+            let (x, y) = event.position;
+            let mut lx = x as f32;
+            let mut ly = y as f32;
+            
+            if let Some(ref popup) = self.active_popup {
+                if event.surface == *popup.sctk_popup.wl_surface() {
+                    lx += popup.x;
+                    ly += popup.y;
+                }
+            }
+            
+            match &event.kind {
+                PointerEventKind::Enter { .. } => {
+                    if let Some(ref themed_pointer) = self.pointer {
+                        let _ = themed_pointer.set_cursor(_conn, CursorIcon::Default);
+                    }
+                }
+                PointerEventKind::Leave { .. } => {}
+                PointerEventKind::Motion { .. } => {
+                    let mut rebuild = false;
+                    self.inner.handle_pointer_move(LogicalPosition::new(lx, ly), &mut rebuild);
+                    if rebuild {
+                        self.redraw = true;
+                    }
+                }
+                PointerEventKind::Press { button, .. } => {
+                    let btn = match *button {
+                        272 => MouseButton::Left,
+                        273 => MouseButton::Right,
+                        274 => MouseButton::Middle,
+                        _ => continue,
+                    };
+                    let mut rebuild = false;
+                    if let Some(msg) = self.inner.handle_mouse_input(btn, ElementState::Pressed, LogicalPosition::new(lx, ly), &mut rebuild) {
+                        let mut update_rebuild = false;
+                        self.inner.update(msg, &mut update_rebuild, &mut self.exit);
+                        if update_rebuild {
+                            rebuild = true;
+                        }
+                    }
+                    if rebuild {
+                        self.redraw = true;
+                    }
+                }
+                PointerEventKind::Release { button, .. } => {
+                    let btn = match *button {
+                        272 => MouseButton::Left,
+                        273 => MouseButton::Right,
+                        274 => MouseButton::Middle,
+                        _ => continue,
+                    };
+                    let mut rebuild = false;
+                    if let Some(msg) = self.inner.handle_mouse_input(btn, ElementState::Released, LogicalPosition::new(lx, ly), &mut rebuild) {
+                        let mut update_rebuild = false;
+                        self.inner.update(msg, &mut update_rebuild, &mut self.exit);
+                        if update_rebuild {
+                            rebuild = true;
+                        }
+                    }
+                    if rebuild {
+                        self.redraw = true;
+                    }
+                }
+                PointerEventKind::Axis { horizontal, vertical, .. } => {
+                    let h_scroll = horizontal.absolute as f32;
+                    let v_scroll = vertical.absolute as f32;
+                    let delta = MouseScrollDelta::LineDelta(-h_scroll / 10.0, -v_scroll / 10.0);
+                    let mut rebuild = false;
+                    self.inner.handle_mouse_wheel(&delta, LogicalPosition::new(lx, ly), &mut rebuild);
+                    if rebuild {
+                        self.redraw = true;
+                    }
+                }
+            }
+        }
+    }
+}
+
+impl<A: Application> KeyboardHandler for EngineState<A> {
+    fn enter(
+        &mut self,
+        _conn: &Connection,
+        _qh: &QueueHandle<Self>,
+        _keyboard: &wl_keyboard::WlKeyboard,
+        _surface: &wl_surface::WlSurface,
+        _serial: u32,
+        _raw_modifiers: &[u32],
+        _keysyms: &[xkeysym::Keysym],
+    ) {}
+    
+    fn leave(
+        &mut self,
+        _conn: &Connection,
+        _qh: &QueueHandle<Self>,
+        _keyboard: &wl_keyboard::WlKeyboard,
+        _surface: &wl_surface::WlSurface,
+        _serial: u32,
+    ) {
+        self.pressed_key = None;
+        self.ctrl_pressed = false;
+        self.shift_pressed = false;
+    }
+    
+    fn press_key(
+        &mut self,
+        _conn: &Connection,
+        _qh: &QueueHandle<Self>,
+        _keyboard: &wl_keyboard::WlKeyboard,
+        _serial: u32,
+        event: smithay_client_toolkit::seat::keyboard::KeyEvent,
+    ) {
+        self.handle_key(event, ElementState::Pressed);
+    }
+    
+    fn release_key(
+        &mut self,
+        _conn: &Connection,
+        _qh: &QueueHandle<Self>,
+        _keyboard: &wl_keyboard::WlKeyboard,
+        _serial: u32,
+        event: smithay_client_toolkit::seat::keyboard::KeyEvent,
+    ) {
+        self.handle_key(event, ElementState::Released);
+    }
+    
+    fn update_modifiers(
+        &mut self,
+        _conn: &Connection,
+        _qh: &QueueHandle<Self>,
+        _keyboard: &wl_keyboard::WlKeyboard,
+        _serial: u32,
+        modifiers: smithay_client_toolkit::seat::keyboard::Modifiers,
+        _layout: u32,
+    ) {
+        self.ctrl_pressed = modifiers.ctrl;
+        self.shift_pressed = modifiers.shift;
+    }
+}
+
+impl<A: Application> EngineState<A> {
+    fn handle_key(&mut self, event: smithay_client_toolkit::seat::keyboard::KeyEvent, state: ElementState) {
+        let logical_key = match event.keysym {
+            xkeysym::Keysym::Escape => Key::Named(NamedKey::Escape),
+            xkeysym::Keysym::Return => Key::Named(NamedKey::Enter),
+            xkeysym::Keysym::BackSpace => Key::Named(NamedKey::Backspace),
+            xkeysym::Keysym::Down => Key::Named(NamedKey::ArrowDown),
+            xkeysym::Keysym::Up => Key::Named(NamedKey::ArrowUp),
+            xkeysym::Keysym::Left => Key::Named(NamedKey::ArrowLeft),
+            xkeysym::Keysym::Right => Key::Named(NamedKey::ArrowRight),
+            xkeysym::Keysym::Tab => Key::Named(NamedKey::Tab),
+            xkeysym::Keysym::Delete => Key::Named(NamedKey::Delete),
+            xkeysym::Keysym::space => Key::Named(NamedKey::Space),
+            _ => {
+                if let Some(ref text) = event.utf8 {
+                    Key::Character(text.clone())
+                } else if let Some(ch) = event.keysym.key_char() {
+                    Key::Character(ch.to_string())
+                } else {
+                    return;
+                }
+            }
+        };
+
+        let custom_event = KeyEvent {
+            state,
+            logical_key,
+            text: event.utf8.clone(),
+            repeat: false,
+            ctrl: self.ctrl_pressed,
+            shift: self.shift_pressed,
+        };
+
+        if state == ElementState::Pressed {
+            if is_repeatable_key(&custom_event.logical_key) {
+                self.pressed_key = Some(PressedKey {
+                    logical_key: custom_event.logical_key.clone(),
+                    text: custom_event.text.clone(),
+                    first_pressed: Instant::now(),
+                    last_repeated: Instant::now(),
+                });
+            } else {
+                self.pressed_key = None;
+            }
+        } else if state == ElementState::Released {
+            if let Some(ref pk) = self.pressed_key {
+                if pk.logical_key == custom_event.logical_key {
+                    self.pressed_key = None;
+                }
+            }
+        }
+
+        let mut rebuild = false;
+        if let Some(msg) = self.inner.handle_key_input(&custom_event, &mut rebuild) {
+            let mut update_rebuild = false;
+            self.inner.update(msg, &mut update_rebuild, &mut self.exit);
+            if update_rebuild {
+                rebuild = true;
+            }
+        }
+        if rebuild {
+            self.redraw = true;
+        }
+    }
+}
+
+impl<A: Application> wayland_client::Dispatch<wl_registry::WlRegistry, GlobalList, Self> for EngineState<A> {
+    fn event(
+        _state: &mut Self,
+        _proxy: &wl_registry::WlRegistry,
+        _event: wl_registry::Event,
+        _data: &GlobalList,
+        _conn: &Connection,
+        _qh: &QueueHandle<Self>,
+    ) {}
+}
+
+impl<A: Application> wayland_client::Dispatch<crate::protocol::zclear_inspector_v1::ZclearInspectorV1, ()> for EngineState<A> {
+    fn event(
+        _state: &mut Self,
+        _proxy: &crate::protocol::zclear_inspector_v1::ZclearInspectorV1,
+        _event: crate::protocol::zclear_inspector_v1::Event,
+        _data: &(),
+        _conn: &Connection,
+        _qh: &QueueHandle<Self>,
+    ) {}
+}
+
+impl<A: Application> PopupHandler for EngineState<A> {
+    fn configure(
+        &mut self,
+        _conn: &Connection,
+        _qh: &QueueHandle<Self>,
+        _popup: &Popup,
+        _config: PopupConfigure,
+    ) {
+        if let Some(ref mut p) = self.active_popup {
+            p.configured = true;
+        }
+        self.redraw = true;
+    }
+
+    fn done(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, _popup: &Popup) {
+        for popover_ptr in crate::widget::popovers::get_active() {
+            unsafe {
+                let popover = &mut *(popover_ptr as *mut dyn crate::widget::Element);
+                popover.unfocus();
+            }
+        }
+        self.active_popup = None;
+        self.redraw = true;
+    }
+}
+
+delegate_compositor!(@<A: Application> EngineState<A>);
+delegate_xdg_shell!(@<A: Application> EngineState<A>);
+delegate_xdg_window!(@<A: Application> EngineState<A>);
+delegate_xdg_popup!(@<A: Application> EngineState<A>);
+delegate_shm!(@<A: Application> EngineState<A>);
+delegate_seat!(@<A: Application> EngineState<A>);
+delegate_pointer!(@<A: Application> EngineState<A>);
+delegate_keyboard!(@<A: Application> EngineState<A>);
+delegate_registry!(@<A: Application> EngineState<A>);
+delegate_output!(@<A: Application> EngineState<A>);
+
+pub fn run<A: Application>() {
+    let conn = Connection::connect_to_env().unwrap();
+    let (globals, mut event_queue) = registry_queue_init(&conn).unwrap();
+    let qh = event_queue.handle();
+
+    let compositor_state = CompositorState::bind(&globals, &qh).unwrap();
+    let xdg_shell_state = XdgShell::bind(&globals, &qh).unwrap();
+    let shm_state = Shm::bind(&globals, &qh).unwrap();
+    let seat_state = SeatState::new(&globals, &qh);
+    let output_state = OutputState::new(&globals, &qh);
+
+    let (sender, channel) = calloop::channel::channel::<A::Message>();
+
+    let inner = A::new(&qh, sender.clone());
+    let settings = inner.settings();
+
+    let mut engine_state = EngineState {
+        registry_state: RegistryState::new(&globals),
+        compositor_state,
+        xdg_shell_state,
+        shm_state,
+        seat_state,
+        output_state,
+        seats: Vec::new(),
+        pointer: None,
+        keyboard: None,
+        window: None,
+        surface: None,
+        inner,
+        wgpu_adapter: None,
+        render_pipeline: None,
+        vertex_buffer: None,
+        vertex_count: 0,
+        overlay_vertex_buffer: None,
+        overlay_vertex_count: 0,
+        scale_factor: 1.0,
+        logical_width: settings.width as f32,
+        logical_height: settings.height as f32,
+        exit: false,
+        redraw: false,
+        first_configure_received: false,
+        ctrl_pressed: false,
+        shift_pressed: false,
+        pressed_key: None,
+        sender,
+        active_popup: None,
+        qh: qh.clone(),
+    };
+
+    event_queue.roundtrip(&mut engine_state).unwrap();
+
+    let scale = detect_scale_factor(&engine_state.output_state);
+    engine_state.scale_factor = scale;
+
+    let surface = engine_state.compositor_state.create_surface(&qh);
+    surface.set_buffer_scale(scale as i32);
+    let window = engine_state.xdg_shell_state.create_window(surface.clone(), WindowDecorations::None, &qh);
+    window.set_title(&settings.title);
+    window.set_app_id(&settings.app_id);
+    if settings.fullscreen {
+        window.set_fullscreen(None);
+    }
+    if let Some((min_w, min_h)) = settings.min_size {
+        window.set_min_size(Some((min_w, min_h)));
+    }
+    window.commit();
+
+    engine_state.window = Some(window);
+    engine_state.surface = Some(surface);
+
+    pollster::block_on(engine_state.init_gpu(&conn, settings.width as f32, settings.height as f32));
+
+    let mut event_loop = EventLoop::try_new().unwrap();
+    let loop_handle = event_loop.handle();
+    WaylandSource::new(conn.clone(), event_queue).insert(loop_handle.clone()).unwrap();
+
+    loop_handle.insert_source(channel, |event, _metadata, app_state: &mut EngineState<A>| {
+        if let calloop::channel::Event::Msg(msg) = event {
+            let mut rebuild = false;
+            app_state.inner.update(msg, &mut rebuild, &mut app_state.exit);
+            if rebuild {
+                app_state.redraw = true;
+            }
+        }
+    }).unwrap();
+
+    const KEY_REPEAT_DELAY: std::time::Duration = std::time::Duration::from_millis(500);
+    const KEY_REPEAT_INTERVAL: std::time::Duration = std::time::Duration::from_millis(50);
+
+    let mut last_title = settings.title.clone();
+    let mut last_tick = std::time::Instant::now();
+    loop {
+        event_loop.dispatch(std::time::Duration::from_millis(16), &mut engine_state).unwrap();
+        if engine_state.exit {
+            break;
+        }
+
+        let now = std::time::Instant::now();
+        let mut dt = now.duration_since(last_tick).as_secs_f32();
+        last_tick = now;
+        if dt > 0.1 {
+            dt = 0.1;
+        }
+
+        let mut rebuild = false;
+        engine_state.inner.tick(dt, &mut rebuild);
+        if rebuild {
+            engine_state.redraw = true;
+        }
+
+        if let Some(ref mut pk) = engine_state.pressed_key {
+            let now = std::time::Instant::now();
+            if now.duration_since(pk.first_pressed) >= KEY_REPEAT_DELAY {
+                if now.duration_since(pk.last_repeated) >= KEY_REPEAT_INTERVAL {
+                    pk.last_repeated = now;
+                    let custom_event = KeyEvent {
+                        state: ElementState::Pressed,
+                        logical_key: pk.logical_key.clone(),
+                        text: pk.text.clone(),
+                        repeat: true,
+                        ctrl: engine_state.ctrl_pressed,
+                        shift: engine_state.shift_pressed,
+                    };
+                    let mut key_rebuild = false;
+                    if let Some(msg) = engine_state.inner.handle_key_input(&custom_event, &mut key_rebuild) {
+                        let mut update_rebuild = false;
+                        engine_state.inner.update(msg, &mut update_rebuild, &mut engine_state.exit);
+                        if update_rebuild {
+                            key_rebuild = true;
+                        }
+                    }
+                    if key_rebuild {
+                        engine_state.redraw = true;
+                    }
+                }
+            }
+        }
+        let current_title = engine_state.inner.settings().title;
+        if current_title != last_title {
+            if let Some(ref window) = engine_state.window {
+                window.set_title(&current_title);
+                window.commit();
+            }
+            last_title = current_title;
+        }
+
+        let active_popovers = crate::widget::popovers::get_active();
+        if !active_popovers.is_empty() {
+            let popover_widget = unsafe { &*active_popovers[0] };
+            if let Some((px, py, pw, ph)) = popover_widget.popover_rect() {
+                if engine_state.active_popup.is_none() {
+                    let wl_surface = engine_state.compositor_state.create_surface(&engine_state.qh);
+                    wl_surface.set_buffer_scale(engine_state.scale_factor as i32);
+                    
+                    let positioner = XdgPositioner::new(&engine_state.xdg_shell_state).unwrap();
+                    positioner.set_size(pw as i32, ph as i32);
+                    
+                    let (rx, ry, rw, rh) = popover_widget.rect();
+                    positioner.set_anchor_rect(rx as i32, ry as i32, rw as i32, rh as i32);
+                    positioner.set_anchor(Anchor::BottomLeft);
+                    positioner.set_gravity(Gravity::BottomRight);
+                    positioner.set_constraint_adjustment(
+                        ConstraintAdjustment::SlideX | ConstraintAdjustment::SlideY
+                    );
+                    
+                    let parent_xdg_surface = XdgSurface::xdg_surface(engine_state.window.as_ref().unwrap());
+                    let sctk_popup = Popup::new(
+                        parent_xdg_surface,
+                        &positioner,
+                        &engine_state.qh,
+                        &engine_state.compositor_state,
+                        &engine_state.xdg_shell_state,
+                    ).unwrap();
+                    
+                    let display_ptr = conn.backend().display_id().as_ptr() as *mut std::ffi::c_void;
+                    let surface_ptr = sctk_popup.wl_surface().id().as_ptr() as *mut std::ffi::c_void;
+                    let wayland_handle = Box::leak(Box::new(WaylandSurfaceHandle {
+                        display_ptr,
+                        surface_ptr,
+                    }));
+                    let instance = &engine_state.wgpu_adapter.as_ref().unwrap().instance;
+                    let wgpu_surface = instance.create_surface(wayland_handle).expect("failed to create popup wgpu surface");
+                    
+                    let device = &engine_state.wgpu_adapter.as_ref().unwrap().device;
+                    let main_config = &engine_state.wgpu_adapter.as_ref().unwrap().config;
+                    
+                    let scale_f32 = engine_state.scale_factor as f32;
+                    let mut popup_config = main_config.clone();
+                    popup_config.width = (pw * scale_f32) as u32;
+                    popup_config.height = (ph * scale_f32) as u32;
+                    wgpu_surface.configure(device, &popup_config);
+                    
+                    let cache = Cache::new(device);
+                    let viewport = Viewport::new(device, &cache);
+                    
+                    engine_state.active_popup = Some(ActivePopup {
+                        sctk_popup,
+                        wgpu_surface,
+                        config: popup_config,
+                        logical_width: pw,
+                        logical_height: ph,
+                        configured: false,
+                        viewport,
+                        x: px,
+                        y: py,
+                        vertex_buffer: None,
+                    });
+                }
+            }
+        } else {
+            if engine_state.active_popup.is_some() {
+                engine_state.active_popup = None;
+                engine_state.redraw = true;
+            }
+        }
+
+        if engine_state.redraw {
+            engine_state.redraw = false;
+            if engine_state.first_configure_received {
+                engine_state.render();
+            }
+        }
+    }
+}
diff --git a/src/context.rs b/src/context.rs
new file mode 100644
index 0000000..681b90c
--- /dev/null
+++ b/src/context.rs
@@ -0,0 +1,372 @@
+use std::collections::HashMap;
+use crate::widget::{Element, WidgetId, LayoutTree, Key, KeyEvent, MouseButton, ElementState, MouseScrollDelta};
+use crate::widget::core::hover_animation::HoverState;
+use crate::widget::core::context_menu::ContextMenuState;
+use crate::widget::TextBox;
+
+pub struct UiContext {
+    pub layout_tree: LayoutTree,
+    pub widget_registry: HashMap<WidgetId, *mut (dyn Element + 'static)>,
+    pub focused_widget: Option<*mut (dyn Element + 'static)>,
+    pub active_popovers: Vec<*const (dyn Element + 'static)>,
+    pub hover_state: HoverState,
+    pub cursor_pos: (f32, f32),
+    pub context_menu: ContextMenuState,
+}
+
+impl UiContext {
+    pub fn new() -> Self {
+        Self {
+            layout_tree: LayoutTree {
+                parents: HashMap::new(),
+                children: HashMap::new(),
+            },
+            widget_registry: HashMap::new(),
+            focused_widget: None,
+            active_popovers: Vec::new(),
+            hover_state: HoverState::new(),
+            cursor_pos: (0.0, 0.0),
+            context_menu: ContextMenuState::new(),
+        }
+    }
+
+    // --- Focus management ---
+    pub fn set_focused(&mut self, w: &mut dyn Element) {
+        let new_ptr = unsafe {
+            std::mem::transmute::<*mut dyn Element, *mut (dyn Element + 'static)>(w as *mut dyn Element)
+        };
+        self.set_focused_ptr(new_ptr);
+    }
+
+    pub fn set_focused_ptr(&mut self, new_ptr: *mut (dyn Element + 'static)) {
+        if let Some(old_ptr) = self.focused_widget {
+            let old_data = old_ptr as *mut () as usize;
+            let new_data = new_ptr as *mut () as usize;
+            if old_data != new_data {
+                unsafe {
+                    (*old_ptr).unfocus();
+                }
+                self.focused_widget = Some(new_ptr);
+            }
+        } else {
+            self.focused_widget = Some(new_ptr);
+        }
+    }
+
+    pub fn is_focused(&self, w: &dyn Element) -> bool {
+        let addr = w as *const dyn Element as *const () as usize;
+        self.is_focused_addr(addr)
+    }
+
+    pub fn is_focused_addr(&self, addr: usize) -> bool {
+        if let Some(ptr) = self.focused_widget {
+            let current_data = ptr as *const () as usize;
+            current_data == addr
+        } else {
+            false
+        }
+    }
+
+    pub fn clear_focus(&mut self) {
+        if let Some(ptr) = self.focused_widget.take() {
+            unsafe {
+                (*ptr).unfocus();
+            }
+        }
+    }
+
+    pub fn clear_if_matches(&mut self, w: &dyn Element) {
+        let query_data = w as *const dyn Element as *const () as usize;
+        if let Some(ptr) = self.focused_widget {
+            let current_data = ptr as *const () as usize;
+            if current_data == query_data {
+                self.focused_widget = None;
+            }
+        }
+    }
+
+    pub fn has_focus(&self) -> bool {
+        self.focused_widget.is_some()
+    }
+
+    pub fn navigate_focus(&mut self, key: &Key, ctrl: bool) -> bool {
+        let ptr = match self.focused_widget {
+            Some(p) => p,
+            None => return false,
+        };
+
+        unsafe {
+            match (key, ctrl) {
+                (Key::Character(c), true) if c == "u" || c == "U" => {
+                    if let Some(parent_ptr) = (*ptr).parent(self) {
+                        let parent_ref = &mut *parent_ptr;
+                        self.set_focused(parent_ref);
+                        parent_ref.focus();
+                        return true;
+                    }
+                }
+                (Key::Character(c), true) if c == "i" || c == "I" => {
+                    let mut children = (*ptr).children(self);
+                    if !children.is_empty() {
+                        let child_ref = &mut *children[0];
+                        self.set_focused(child_ref);
+                        child_ref.focus();
+                        return true;
+                    }
+                }
+                (Key::Character(c), true) if c == "j" || c == "J" => {
+                    if let Some(parent_ptr) = (*ptr).parent(self) {
+                        let mut siblings = (*parent_ptr).children(self);
+                        let current_idx = siblings.iter().position(|&x| {
+                            let a = x as *mut () as usize;
+                            let b = ptr as *mut () as usize;
+                            a == b
+                        });
+                        if let Some(idx) = current_idx {
+                            let next_idx = (idx + 1) % siblings.len();
+                            let sibling_ref = &mut *siblings[next_idx];
+                            self.set_focused(sibling_ref);
+                            sibling_ref.focus();
+                            return true;
+                        }
+                    }
+                }
+                (Key::Character(c), true) if c == "k" || c == "K" => {
+                    if let Some(parent_ptr) = (*ptr).parent(self) {
+                        let mut siblings = (*parent_ptr).children(self);
+                        let current_idx = siblings.iter().position(|&x| {
+                            let a = x as *mut () as usize;
+                            let b = ptr as *mut () as usize;
+                            a == b
+                        });
+                        if let Some(idx) = current_idx {
+                            let prev_idx = if idx == 0 { siblings.len() - 1 } else { idx - 1 };
+                            let sibling_ref = &mut *siblings[prev_idx];
+                            self.set_focused(sibling_ref);
+                            sibling_ref.focus();
+                            return true;
+                        }
+                    }
+                }
+                _ => {}
+            }
+        }
+        false
+    }
+
+    // --- Registry ---
+    pub fn register_widget(&mut self, id: WidgetId, ptr: *mut (dyn Element + 'static)) {
+        self.widget_registry.insert(id, ptr);
+    }
+
+    pub fn link_ids(&mut self, parent: WidgetId, child: WidgetId) {
+        self.layout_tree.parents.insert(child, parent);
+        let children = self.layout_tree.children.entry(parent).or_default();
+        if !children.contains(&child) {
+            children.push(child);
+        }
+    }
+
+    pub fn unlink_child(&mut self, parent: WidgetId, child: WidgetId) {
+        self.layout_tree.parents.remove(&child);
+        if let Some(children) = self.layout_tree.children.get_mut(&parent) {
+            children.retain(|&x| x != child);
+        }
+    }
+
+    pub fn clear_children_ids(&mut self, parent: WidgetId) {
+        if let Some(children) = self.layout_tree.children.remove(&parent) {
+            for child in children {
+                self.layout_tree.parents.remove(&child);
+            }
+        }
+    }
+
+    pub fn clear_hierarchy(&mut self) {
+        self.layout_tree.parents.clear();
+        self.layout_tree.children.clear();
+        self.widget_registry.clear();
+    }
+
+    // --- Popovers ---
+    pub fn clear_popovers(&mut self) {
+        self.active_popovers.clear();
+    }
+
+    pub fn register_popover(&mut self, w: &(dyn Element + 'static)) {
+        let ptr = w as *const (dyn Element + 'static);
+        if !self.active_popovers.contains(&ptr) {
+            self.active_popovers.push(ptr);
+        }
+    }
+
+    pub fn register_popover_ptr(&mut self, ptr: *mut (dyn Element + 'static)) {
+        let const_ptr = ptr as *const (dyn Element + 'static);
+        if !self.active_popovers.contains(&const_ptr) {
+            self.active_popovers.push(const_ptr);
+        }
+    }
+
+    pub fn is_coordinate_covered(&self, query_address: usize, px: f32, py: f32) -> bool {
+        for popover_ptr in self.active_popovers.iter() {
+            let current_data = *popover_ptr as *const () as usize;
+            if query_address == current_data {
+                continue;
+            }
+            unsafe {
+                if let Some(popover) = popover_ptr.as_ref() {
+                    if let Some((x, y, width, height)) = popover.popover_rect() {
+                        if px >= x && px <= x + width && py >= y && py <= y + height {
+                            return true;
+                        }
+                    }
+                }
+            }
+        }
+        false
+    }
+
+    // --- Hover State ---
+    pub fn set_cursor_pos(&mut self, x: f32, y: f32) {
+        self.cursor_pos = (x, y);
+    }
+
+    pub fn reset_frame_registration(&mut self) {
+        self.hover_state.registered_this_frame = false;
+    }
+
+    pub fn set_scroll_offset(&mut self, offset: f32) {
+        self.hover_state.scroll_offset = offset;
+    }
+
+    pub fn get_scroll_offset(&self) -> f32 {
+        self.hover_state.scroll_offset
+    }
+
+    pub fn register_hovered(&mut self, x: f32, y: f32, w: f32, h: f32, color: [f32; 4]) {
+        self.hover_state.target_x = Some(x);
+        self.hover_state.target_y = Some(y);
+        self.hover_state.target_w = Some(w);
+        self.hover_state.target_h = Some(h);
+        self.hover_state.target_alpha = color[3];
+        self.hover_state.registered_this_frame = true;
+    }
+
+    pub fn post_render_check(&mut self) {
+        if !self.hover_state.registered_this_frame {
+            self.hover_state.target_alpha = 0.0;
+            let (cx, cy) = self.cursor_pos;
+            self.hover_state.target_x = Some(cx);
+            self.hover_state.target_y = Some(cy + self.hover_state.scroll_offset);
+            self.hover_state.target_w = Some(0.0);
+            self.hover_state.target_h = Some(0.0);
+        }
+    }
+
+    pub fn tick_hover(&mut self, dt: f32) -> bool {
+        let s = &mut self.hover_state;
+        let decay = 15.0;
+        let mut changed = false;
+
+        if s.current_alpha <= 0.001 && s.target_alpha > 0.0 {
+            if let (Some(tx), Some(ty), Some(tw), Some(th)) = (s.target_x, s.target_y, s.target_w, s.target_h) {
+                s.current_x = tx;
+                s.current_y = ty;
+                s.current_w = tw;
+                s.current_h = th;
+            }
+        }
+
+        if (s.current_alpha - s.target_alpha).abs() > 0.001 {
+            s.current_alpha += (s.target_alpha - s.current_alpha) * (1.0 - (-decay * dt).exp());
+            changed = true;
+        } else if s.current_alpha != s.target_alpha {
+            s.current_alpha = s.target_alpha;
+            changed = true;
+        }
+
+        if let (Some(tx), Some(ty), Some(tw), Some(th)) = (s.target_x, s.target_y, s.target_w, s.target_h) {
+            if (s.current_x - tx).abs() > 0.1 {
+                s.current_x += (tx - s.current_x) * (1.0 - (-decay * dt).exp());
+                changed = true;
+            } else if s.current_x != tx {
+                s.current_x = tx;
+                changed = true;
+            }
+
+            if (s.current_y - ty).abs() > 0.1 {
+                s.current_y += (ty - s.current_y) * (1.0 - (-decay * dt).exp());
+                changed = true;
+            } else if s.current_y != ty {
+                s.current_y = ty;
+                changed = true;
+            }
+
+            if (s.current_w - tw).abs() > 0.1 {
+                s.current_w += (tw - s.current_w) * (1.0 - (-decay * dt).exp());
+                changed = true;
+            } else if s.current_w != tw {
+                s.current_w = tw;
+                changed = true;
+            }
+
+            if (s.current_h - th).abs() > 0.1 {
+                s.current_h += (th - s.current_h) * (1.0 - (-decay * dt).exp());
+                changed = true;
+            } else if s.current_h != th {
+                s.current_h = th;
+                changed = true;
+            }
+        }
+
+        changed
+    }
+
+    pub fn get_hover_quad(&self) -> Option<(f32, f32, f32, f32, [f32; 4])> {
+        let s = &self.hover_state;
+        if s.current_alpha > 0.001 {
+            Some((
+                s.current_x,
+                s.current_y,
+                s.current_w,
+                s.current_h,
+                [1.0, 1.0, 1.0, s.current_alpha],
+            ))
+        } else {
+            None
+        }
+    }
+
+    // --- Context Menu ---
+    pub fn is_context_menu_visible(&self) -> bool {
+        self.context_menu.visible
+    }
+
+    pub fn show_context_menu(&mut self, x: f32, y: f32, options: Vec<String>, target: *mut TextBox) {
+        self.context_menu.show(x, y, options, target);
+    }
+
+    pub fn hide_context_menu(&mut self) {
+        self.context_menu.hide();
+    }
+
+    pub fn hit_test_context_menu(&self, px: f32, py: f32) -> bool {
+        self.context_menu.hit_test(px, py)
+    }
+
+    pub fn cursor_moved_context_menu(&mut self, px: f32, py: f32) -> bool {
+        self.context_menu.cursor_moved(px, py)
+    }
+
+    pub fn mouse_input_context_menu(&mut self, button: MouseButton, state: ElementState, px: f32, py: f32) -> bool {
+        self.context_menu.mouse_input(button, state, px, py)
+    }
+
+    pub fn context_menu_quads(&self) -> Vec<(f32, f32, f32, f32, [f32; 4])> {
+        self.context_menu.extra_quads()
+    }
+
+    pub fn context_menu_labels(&self) -> Vec<crate::widget::display::TextLabel> {
+        self.context_menu.text_labels()
+    }
+}
diff --git a/src/engine.rs b/src/engine.rs
index f51d8e0..c8bfaf2 100644
--- a/src/engine.rs
+++ b/src/engine.rs
@@ -1,1891 +1,11 @@
-use std::time::Instant;
-use smithay_client_toolkit::{
-    compositor::{CompositorHandler, CompositorState},
-    delegate_compositor, delegate_keyboard, delegate_pointer, delegate_registry,
-    delegate_seat, delegate_shm, delegate_xdg_shell, delegate_xdg_window, delegate_output, delegate_xdg_popup,
-    registry::{ProvidesRegistryState, RegistryState},
-    output::{OutputHandler, OutputState},
-    seat::{
-        keyboard::KeyboardHandler,
-        pointer::{PointerHandler, ThemedPointer, ThemeSpec, CursorIcon},
-        Capability, SeatHandler, SeatState,
-    },
-    shell::{
-        xdg::{
-            window::{Window as XdgWindow, WindowConfigure, WindowHandler, WindowDecorations},
-            XdgShell,
-        },
-        WaylandSurface,
-    },
-    shm::{Shm, ShmHandler},
+pub use crate::backend::window_runner::{
+    ActivePopup, Vertex, LineCap, WindowSettings, LogicalPosition, LogicalSize,
+    RenderContext, Application, PressedKey, EngineState, run,
+    quad_vertices, quad_vertices_with_clip, quad_vertices_clipped, line_vertices,
+    vector_vertices, rounded_rect_vertices_corners, push_rounded_rect_vertices_corners,
+    rounded_rect_vertices, push_rounded_rect_vertices, plate_bevel_vertices,
+    push_plate_bevel_vertices, widget_vertices, push_widget_vertices,
+    extra_quad_vertices, push_extra_quad_vertices, extra_quad_vertices_clipped,
+    push_extra_quad_vertices_clipped, circle_vertices, circle_border_vertices,
+    arc_background_vertices, push_arc_background_vertices,
 };
-use wayland_client::{
-    globals::{registry_queue_init, GlobalList},
-    protocol::{wl_keyboard, wl_output, wl_pointer, wl_seat, wl_shm, wl_surface, wl_registry},
-    Connection, QueueHandle, Proxy,
-};
-use smithay_client_toolkit::shell::xdg::popup::{Popup, PopupHandler, PopupConfigure};
-use smithay_client_toolkit::shell::xdg::{XdgPositioner, XdgSurface};
-use smithay_client_toolkit::reexports::protocols::xdg::shell::client::xdg_positioner::{Anchor, Gravity, ConstraintAdjustment};
-
-use calloop::EventLoop;
-use calloop_wayland_source::WaylandSource;
-use glyphon::{
-    Cache, FontSystem, Resolution, SwashCache, TextArea, TextAtlas,
-    TextBounds, TextRenderer, Viewport, Buffer, Attrs, Metrics,
-};
-use crate::widget::{TextItem, MouseButton, ElementState, MouseScrollDelta, KeyEvent, Key, NamedKey};
-
-pub struct ActivePopup {
-    pub sctk_popup: Popup,
-    pub wgpu_surface: wgpu::Surface<'static>,
-    pub config: wgpu::SurfaceConfiguration,
-    pub logical_width: f32,
-    pub logical_height: f32,
-    pub configured: bool,
-    pub viewport: Viewport,
-    pub x: f32,
-    pub y: f32,
-    pub vertex_buffer: Option<wgpu::Buffer>,
-}
-
-fn make_text_buffer_with_font(fs: &mut FontSystem, text: &str, size: f32, font: Option<&str>) -> Buffer {
-    let metrics = Metrics::new(size, size * 1.4);
-    let mut buf = Buffer::new(fs, metrics);
-    let mut attrs = Attrs::new();
-    if let Some(font_name) = font {
-        let family = match font_name {
-            "monospace" => glyphon::Family::Monospace,
-            "sans-serif" => glyphon::Family::SansSerif,
-            "serif" => glyphon::Family::Serif,
-            _ => glyphon::Family::Name(font_name),
-        };
-        attrs = attrs.family(family);
-    }
-    buf.set_text(fs, text, attrs, glyphon::Shaping::Advanced);
-    buf.shape_until_scroll(fs, true);
-    buf
-}
-use crate::wayland::{WaylandSurfaceHandle, detect_scale_factor};
-
-#[repr(C)]
-#[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
-pub struct Vertex {
-    pub position: [f32; 2],
-    pub color: [f32; 4],
-    pub clip_circle: [f32; 3], // [cx, cy, r]
-}
-
-impl Vertex {
-    const ATTRIBS: [wgpu::VertexAttribute; 3] = wgpu::vertex_attr_array![
-        0 => Float32x2,
-        1 => Float32x4,
-        2 => Float32x3,
-    ];
-
-    pub fn desc() -> wgpu::VertexBufferLayout<'static> {
-        wgpu::VertexBufferLayout {
-            array_stride: std::mem::size_of::<Vertex>() as wgpu::BufferAddress,
-            step_mode: wgpu::VertexStepMode::Vertex,
-            attributes: &Self::ATTRIBS,
-        }
-    }
-}
-
-pub fn quad_vertices(x: f32, y: f32, w: f32, h: f32, sw: f32, sh: f32, c: [f32; 4]) -> [Vertex; 6] {
-    let x0 = (x / sw) * 2.0 - 1.0;
-    let y0 = 1.0 - (y / sh) * 2.0;
-    let x1 = ((x + w) / sw) * 2.0 - 1.0;
-    let y1 = 1.0 - ((y + h) / sh) * 2.0;
-    [
-        Vertex { position: [x0, y0], color: c, clip_circle: [0.0, 0.0, 0.0] },
-        Vertex { position: [x1, y0], color: c, clip_circle: [0.0, 0.0, 0.0] },
-        Vertex { position: [x0, y1], color: c, clip_circle: [0.0, 0.0, 0.0] },
-        Vertex { position: [x1, y0], color: c, clip_circle: [0.0, 0.0, 0.0] },
-        Vertex { position: [x1, y1], color: c, clip_circle: [0.0, 0.0, 0.0] },
-        Vertex { position: [x0, y1], color: c, clip_circle: [0.0, 0.0, 0.0] },
-    ]
-}
-
-pub fn quad_vertices_with_clip(
-    x: f32, y: f32, w: f32, h: f32,
-    sw: f32, sh: f32,
-    color: [f32; 4],
-    clip_circle: [f32; 3],
-) -> [Vertex; 6] {
-    let x0 = (x / sw) * 2.0 - 1.0;
-    let y0 = 1.0 - (y / sh) * 2.0;
-    let x1 = ((x + w) / sw) * 2.0 - 1.0;
-    let y1 = 1.0 - ((y + h) / sh) * 2.0;
-    [
-        Vertex { position: [x0, y0], color, clip_circle },
-        Vertex { position: [x1, y0], color, clip_circle },
-        Vertex { position: [x0, y1], color, clip_circle },
-        Vertex { position: [x1, y0], color, clip_circle },
-        Vertex { position: [x1, y1], color, clip_circle },
-        Vertex { position: [x0, y1], color, clip_circle },
-    ]
-}
-
-pub fn quad_vertices_clipped(
-    x: f32, y: f32, w: f32, h: f32,
-    surface_w: f32, surface_h: f32,
-    color: [f32; 4],
-    clip: (f32, f32, f32, f32),
-    clip_circle: [f32; 3],
-) -> Vec<Vertex> {
-    let (cx0, cy0, cx1, cy1) = clip;
-    let ix0 = x.max(cx0);
-    let iy0 = y.max(cy0);
-    let ix1 = (x + w).min(cx1);
-    let iy1 = (y + h).min(cy1);
-    if ix1 <= ix0 || iy1 <= iy0 {
-        return Vec::new();
-    }
-    quad_vertices_with_clip(ix0, iy0, ix1 - ix0, iy1 - iy0, surface_w, surface_h, color, clip_circle).to_vec()
-}
-
-pub fn line_vertices(
-    x1: f32, y1: f32, x2: f32, y2: f32,
-    thickness: f32,
-    sw: f32, sh: f32,
-    c: [f32; 4]
-) -> [Vertex; 6] {
-    let dx = x2 - x1;
-    let dy = y2 - y1;
-    let len = (dx * dx + dy * dy).sqrt();
-    if len < 0.001 {
-        return quad_vertices(x1 - thickness/2.0, y1 - thickness/2.0, thickness, thickness, sw, sh, c);
-    }
-    let ux = dx / len;
-    let uy = dy / len;
-    let nx = -uy;
-    let ny = ux;
-    
-    let half_t = thickness * 0.5;
-    let p0x = x1 + nx * half_t;
-    let p0y = y1 + ny * half_t;
-    let p1x = x1 - nx * half_t;
-    let p1y = y1 - ny * half_t;
-    let p2x = x2 - nx * half_t;
-    let p2y = y2 - ny * half_t;
-    let p3x = x2 + nx * half_t;
-    let p3y = y2 + ny * half_t;
-
-    let ndc_p0x = (p0x / sw) * 2.0 - 1.0;
-    let ndc_p0y = 1.0 - (p0y / sh) * 2.0;
-    let ndc_p1x = (p1x / sw) * 2.0 - 1.0;
-    let ndc_p1y = 1.0 - (p1y / sh) * 2.0;
-    let ndc_p2x = (p2x / sw) * 2.0 - 1.0;
-    let ndc_p2y = 1.0 - (p2y / sh) * 2.0;
-    let ndc_p3x = (p3x / sw) * 2.0 - 1.0;
-    let ndc_p3y = 1.0 - (p3y / sh) * 2.0;
-
-    let clip_circle = [0.0, 0.0, 0.0];
-    [
-        Vertex { position: [ndc_p0x, ndc_p0y], color: c, clip_circle },
-        Vertex { position: [ndc_p1x, ndc_p1y], color: c, clip_circle },
-        Vertex { position: [ndc_p2x, ndc_p2y], color: c, clip_circle },
-        Vertex { position: [ndc_p0x, ndc_p0y], color: c, clip_circle },
-        Vertex { position: [ndc_p2x, ndc_p2y], color: c, clip_circle },
-        Vertex { position: [ndc_p3x, ndc_p3y], color: c, clip_circle },
-    ]
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, serde::Serialize, serde::Deserialize)]
-pub enum LineCap {
-    Arrow,
-    Round,
-    Flat,
-}
-
-pub fn vector_vertices(
-    x1: f32, y1: f32, x2: f32, y2: f32,
-    thickness: f32,
-    sw: f32, sh: f32,
-    c: [f32; 4],
-    line_cap: LineCap,
-) -> Vec<Vertex> {
-    let mut verts = Vec::new();
-    let dx = x2 - x1;
-    let dy = y2 - y1;
-    let len = (dx * dx + dy * dy).sqrt();
-    if len < 0.001 {
-        return quad_vertices(x1 - thickness/2.0, y1 - thickness/2.0, thickness, thickness, sw, sh, c).to_vec();
-    }
-    
-    match line_cap {
-        LineCap::Arrow => {
-            let ux = dx / len;
-            let uy = dy / len;
-            let nx = -uy;
-            let ny = ux;
-            
-            let arrow_len = (thickness * 3.0).max(10.0).min(len);
-            let arrow_width = (thickness * 2.5).max(8.0);
-            
-            let line_x2 = x2 - ux * arrow_len;
-            let line_y2 = y2 - uy * arrow_len;
-            
-            if len > arrow_len {
-                verts.extend_from_slice(&line_vertices(x1, y1, line_x2, line_y2, thickness, sw, sh, c));
-            }
-            
-            let bx = line_x2;
-            let by = line_y2;
-            
-            let w1x = bx + nx * (arrow_width * 0.5);
-            let w1y = by + ny * (arrow_width * 0.5);
-            let w2x = bx - nx * (arrow_width * 0.5);
-            let w2y = by - ny * (arrow_width * 0.5);
-            
-            let ndc_tip_x = (x2 / sw) * 2.0 - 1.0;
-            let ndc_tip_y = 1.0 - (y2 / sh) * 2.0;
-            let ndc_w1x = (w1x / sw) * 2.0 - 1.0;
-            let ndc_w1y = 1.0 - (w1y / sh) * 2.0;
-            let ndc_w2x = (w2x / sw) * 2.0 - 1.0;
-            let ndc_w2y = 1.0 - (w2y / sh) * 2.0;
-            
-            let clip_circle = [0.0, 0.0, 0.0];
-            verts.push(Vertex { position: [ndc_tip_x, ndc_tip_y], color: c, clip_circle });
-            verts.push(Vertex { position: [ndc_w1x, ndc_w1y], color: c, clip_circle });
-            verts.push(Vertex { position: [ndc_w2x, ndc_w2y], color: c, clip_circle });
-        }
-        LineCap::Round => {
-            verts.extend_from_slice(&line_vertices(x1, y1, x2, y2, thickness, sw, sh, c));
-            let clip_circle = [0.0, 0.0, 0.0];
-            verts.extend(circle_vertices(x2, y2, thickness / 2.0, sw, sh, c, 16, clip_circle));
-        }
-        LineCap::Flat => {
-            verts.extend_from_slice(&line_vertices(x1, y1, x2, y2, thickness, sw, sh, c));
-        }
-    }
-    
-    verts
-}
-
-pub fn rounded_rect_vertices_corners(
-    x: f32, y: f32, ww: f32, h: f32,
-    r: f32,
-    sw: f32, sh: f32,
-    color: [f32; 4],
-    clip_circle: [f32; 3],
-    corners: (bool, bool, bool, bool),
-    clip_rect: Option<(f32, f32, f32, f32)>,
-) -> Vec<Vertex> {
-    let mut verts = Vec::new();
-    let r = r.min(ww * 0.5).min(h * 0.5);
-
-    let clamp_x = |val: f32| -> f32 {
-        if let Some((cx0, _, cx1, _)) = clip_rect {
-            val.max(cx0).min(cx1)
-        } else {
-            val
-        }
-    };
-    let clamp_y = |val: f32| -> f32 {
-        if let Some((_, cy0, _, cy1)) = clip_rect {
-            val.max(cy0).min(cy1)
-        } else {
-            val
-        }
-    };
-
-    let push_quad = |verts: &mut Vec<Vertex>, qx: f32, qy: f32, qw: f32, qh: f32| {
-        let x0 = clamp_x(qx);
-        let y0 = clamp_y(qy);
-        let x1 = clamp_x(qx + qw);
-        let y1 = clamp_y(qy + qh);
-        
-        if x1 <= x0 || y1 <= y0 {
-            return;
-        }
-
-        let ndc_x0 = (x0 / sw) * 2.0 - 1.0;
-        let ndc_y0 = 1.0 - (y0 / sh) * 2.0;
-        let ndc_x1 = (x1 / sw) * 2.0 - 1.0;
-        let ndc_y1 = 1.0 - (y1 / sh) * 2.0;
-        
-        verts.push(Vertex { position: [ndc_x0, ndc_y0], color, clip_circle });
-        verts.push(Vertex { position: [ndc_x1, ndc_y0], color, clip_circle });
-        verts.push(Vertex { position: [ndc_x0, ndc_y1], color, clip_circle });
-        verts.push(Vertex { position: [ndc_x1, ndc_y0], color, clip_circle });
-        verts.push(Vertex { position: [ndc_x1, ndc_y1], color, clip_circle });
-        verts.push(Vertex { position: [ndc_x0, ndc_y1], color, clip_circle });
-    };
-
-    if r <= 0.1 || (!corners.0 && !corners.1 && !corners.2 && !corners.3) {
-        push_quad(&mut verts, x, y, ww, h);
-        return verts;
-    }
-
-    // 1. Center rectangle
-    push_quad(&mut verts, x + r, y, ww - 2.0 * r, h);
-    
-    // 2. Left rectangle
-    push_quad(&mut verts, x, y + r, r, h - 2.0 * r);
-    
-    // 3. Right rectangle
-    push_quad(&mut verts, x + ww - r, y + r, r, h - 2.0 * r);
-
-    // 4. Four corners
-    let corner_configs = [
-        // Top-left
-        (corners.0, x, y, x + r, y + r, std::f32::consts::PI, 1.5 * std::f32::consts::PI),
-        // Top-right
-        (corners.1, x + ww - r, y, x + ww - r, y + r, 1.5 * std::f32::consts::PI, 2.0 * std::f32::consts::PI),
-        // Bottom-right
-        (corners.2, x + ww - r, y + h - r, x + ww - r, y + h - r, 0.0, 0.5 * std::f32::consts::PI),
-        // Bottom-left
-        (corners.3, x, y + h - r, x + r, y + h - r, 0.5 * std::f32::consts::PI, std::f32::consts::PI),
-    ];
-
-    let segments = 16;
-    for &(is_rounded, sqx, sqy, cx, cy, start, end) in &corner_configs {
-        if is_rounded {
-            for i in 0..segments {
-                let theta1 = start + (i as f32) * (end - start) / (segments as f32);
-                let theta2 = start + ((i + 1) as f32) * (end - start) / (segments as f32);
-                
-                let x0 = clamp_x(cx);
-                let y0 = clamp_y(cy);
-                let x1 = clamp_x(cx + r * theta1.cos());
-                let y1 = clamp_y(cy + r * theta1.sin());
-                let x2 = clamp_x(cx + r * theta2.cos());
-                let y2 = clamp_y(cy + r * theta2.sin());
-                
-                let ndc_x0 = (x0 / sw) * 2.0 - 1.0;
-                let ndc_y0 = 1.0 - (y0 / sh) * 2.0;
-                let ndc_x1 = (x1 / sw) * 2.0 - 1.0;
-                let ndc_y1 = 1.0 - (y1 / sh) * 2.0;
-                let ndc_x2 = (x2 / sw) * 2.0 - 1.0;
-                let ndc_y2 = 1.0 - (y2 / sh) * 2.0;
-                
-                verts.push(Vertex { position: [ndc_x0, ndc_y0], color, clip_circle });
-                verts.push(Vertex { position: [ndc_x1, ndc_y1], color, clip_circle });
-                verts.push(Vertex { position: [ndc_x2, ndc_y2], color, clip_circle });
-            }
-        } else {
-            push_quad(&mut verts, sqx, sqy, r, r);
-        }
-    }
-
-    verts
-}
-
-pub fn rounded_rect_vertices(
-    x: f32, y: f32, ww: f32, h: f32,
-    r: f32,
-    sw: f32, sh: f32,
-    color: [f32; 4],
-    clip_circle: [f32; 3],
-) -> Vec<Vertex> {
-    rounded_rect_vertices_corners(x, y, ww, h, r, sw, sh, color, clip_circle, (true, true, true, true), None)
-}
-
-pub fn plate_bevel_vertices(
-    x: f32, y: f32, ww: f32, h: f32,
-    r: f32,
-    t: f32,
-    sw: f32, sh: f32,
-    clip_circle: [f32; 3],
-) -> Vec<Vertex> {
-    let mut verts = Vec::new();
-
-    // 3D Bevel Colors
-    // Highlight (light) for top and left edges
-    let highlight_color = [1.0, 1.0, 1.0, 0.15];
-    // Shadow (dark) for bottom and right edges
-    let shadow_color = [0.0, 0.0, 0.0, 0.25];
-
-    // Straight borders on the inside edge
-    // Top border (light)
-    verts.extend(quad_vertices_with_clip(x + r, y, ww - 2.0 * r, t, sw, sh, highlight_color, clip_circle));
-    // Left border (light)
-    verts.extend(quad_vertices_with_clip(x, y + r, t, h - 2.0 * r, sw, sh, highlight_color, clip_circle));
-    // Bottom border (dark)
-    verts.extend(quad_vertices_with_clip(x + r, y + h - t, ww - 2.0 * r, t, sw, sh, shadow_color, clip_circle));
-    // Right border (dark)
-    verts.extend(quad_vertices_with_clip(x + ww - t, y + r, t, h - 2.0 * r, sw, sh, shadow_color, clip_circle));
-
-    // Corner arcs on the inside edge
-    let segments = 16;
-
-    // Top-left corner (fully light)
-    verts.extend(arc_background_vertices(
-        x + r, y + r, r, t,
-        std::f32::consts::PI, 1.5 * std::f32::consts::PI,
-        sw, sh, highlight_color, segments, clip_circle,
-    ));
-
-    // Top-right corner (split at 45 degrees)
-    verts.extend(arc_background_vertices(
-        x + ww - r, y + r, r, t,
-        1.5 * std::f32::consts::PI, 1.75 * std::f32::consts::PI,
-        sw, sh, highlight_color, segments / 2, clip_circle,
-    ));
-    verts.extend(arc_background_vertices(
-        x + ww - r, y + r, r, t,
-        1.75 * std::f32::consts::PI, 2.0 * std::f32::consts::PI,
-        sw, sh, shadow_color, segments / 2, clip_circle,
-    ));
-
-    // Bottom-right corner (fully dark)
-    verts.extend(arc_background_vertices(
-        x + ww - r, y + h - r, r, t,
-        0.0, 0.5 * std::f32::consts::PI,
-        sw, sh, shadow_color, segments, clip_circle,
-    ));
-
-    // Bottom-left corner (split at 45 degrees)
-    verts.extend(arc_background_vertices(
-        x + r, y + h - r, r, t,
-        0.5 * std::f32::consts::PI, 0.75 * std::f32::consts::PI,
-        sw, sh, shadow_color, segments / 2, clip_circle,
-    ));
-    verts.extend(arc_background_vertices(
-        x + r, y + h - r, r, t,
-        0.75 * std::f32::consts::PI, std::f32::consts::PI,
-        sw, sh, highlight_color, segments / 2, clip_circle,
-    ));
-
-    verts
-}
-
-pub fn widget_vertices(w: &dyn crate::widget::Element, sw: f32, sh: f32, clip_circle: [f32; 3]) -> Vec<Vertex> {
-    let (x, y, ww, h) = w.rect();
-    let corners = w.rounded_corners();
-    let mut verts = if corners != (false, false, false, false) {
-        rounded_rect_vertices_corners(x, y, ww, h, 12.0, sw, sh, w.color(), clip_circle, corners, None)
-    } else {
-        quad_vertices_with_clip(x, y, ww, h, sw, sh, w.color(), clip_circle).to_vec()
-    };
-
-    if w.is_plate() {
-        verts.extend(plate_bevel_vertices(x, y, ww, h, 12.0, 1.5, sw, sh, clip_circle));
-    }
-
-    verts
-}
-
-pub fn extra_quad_vertices(
-    w: &dyn crate::widget::Element,
-    qx: f32, qy: f32, qw: f32, qh: f32,
-    sw: f32, sh: f32,
-    qc: [f32; 4],
-    clip_circle: [f32; 3],
-) -> Vec<Vertex> {
-    let corners = w.rounded_corners();
-    if corners == (false, false, false, false) {
-        return quad_vertices_with_clip(qx, qy, qw, qh, sw, sh, qc, clip_circle).to_vec();
-    }
-
-    let (wx, wy, ww, wh) = w.rect();
-    let extra_corners = (
-        corners.0 && qx <= wx + 0.1 && qy <= wy + 0.1,
-        corners.1 && qx + qw >= wx + ww - 0.1 && qy <= wy + 0.1,
-        corners.2 && qx + qw >= wx + ww - 0.1 && qy + qh >= wy + wh - 0.1,
-        corners.3 && qx <= wx + 0.1 && qy + qh >= wy + wh - 0.1,
-    );
-
-    rounded_rect_vertices_corners(qx, qy, qw, qh, 12.0, sw, sh, qc, clip_circle, extra_corners, None)
-}
-
-pub fn extra_quad_vertices_clipped(
-    w: &dyn crate::widget::Element,
-    qx: f32, qy: f32, qw: f32, qh: f32,
-    sw: f32, sh: f32,
-    qc: [f32; 4],
-    clip: (f32, f32, f32, f32),
-    clip_circle: [f32; 3],
-) -> Vec<Vertex> {
-    let corners = w.rounded_corners();
-    if corners == (false, false, false, false) {
-        let (cx0, cy0, cx1, cy1) = clip;
-        let ix0 = qx.max(cx0);
-        let iy0 = qy.max(cy0);
-        let ix1 = (qx + qw).min(cx1);
-        let iy1 = (qy + qh).min(cy1);
-        if ix1 <= ix0 || iy1 <= iy0 {
-            return Vec::new();
-        }
-        return quad_vertices_with_clip(ix0, iy0, ix1 - ix0, iy1 - iy0, sw, sh, qc, clip_circle).to_vec();
-    }
-
-    let (wx, wy, ww, wh) = w.rect();
-    let extra_corners = (
-        corners.0 && qx <= wx + 0.1 && qy <= wy + 0.1,
-        corners.1 && qx + qw >= wx + ww - 0.1 && qy <= wy + 0.1,
-        corners.2 && qx + qw >= wx + ww - 0.1 && qy + qh >= wy + wh - 0.1,
-        corners.3 && qx <= wx + 0.1 && qy + qh >= wy + wh - 0.1,
-    );
-
-    rounded_rect_vertices_corners(qx, qy, qw, qh, 12.0, sw, sh, qc, clip_circle, extra_corners, Some(clip))
-}
-
-pub fn circle_vertices(
-    cx: f32, cy: f32, r: f32,
-    sw: f32, sh: f32,
-    color: [f32; 4],
-    segments: usize,
-    clip_circle: [f32; 3],
-) -> Vec<Vertex> {
-    let mut verts = Vec::new();
-    for i in 0..segments {
-        let theta1 = (i as f32) * 2.0 * std::f32::consts::PI / (segments as f32);
-        let theta2 = ((i + 1) as f32) * 2.0 * std::f32::consts::PI / (segments as f32);
-        let x0 = cx;
-        let y0 = cy;
-        let x1 = cx + r * theta1.cos();
-        let y1 = cy + r * theta1.sin();
-        let x2 = cx + r * theta2.cos();
-        let y2 = cy + r * theta2.sin();
-        
-        let ndc_x0 = (x0 / sw) * 2.0 - 1.0;
-        let ndc_y0 = 1.0 - (y0 / sh) * 2.0;
-        let ndc_x1 = (x1 / sw) * 2.0 - 1.0;
-        let ndc_y1 = 1.0 - (y1 / sh) * 2.0;
-        let ndc_x2 = (x2 / sw) * 2.0 - 1.0;
-        let ndc_y2 = 1.0 - (y2 / sh) * 2.0;
-        
-        verts.push(Vertex { position: [ndc_x0, ndc_y0], color, clip_circle });
-        verts.push(Vertex { position: [ndc_x1, ndc_y1], color, clip_circle });
-        verts.push(Vertex { position: [ndc_x2, ndc_y2], color, clip_circle });
-    }
-    verts
-}
-
-pub fn circle_border_vertices(
-    cx: f32, cy: f32, r: f32,
-    thickness: f32,
-    sw: f32, sh: f32,
-    color: [f32; 4],
-    segments: usize,
-    clip_circle: [f32; 3],
-) -> Vec<Vertex> {
-    let mut verts = Vec::new();
-    for i in 0..segments {
-        let theta1 = (i as f32) * 2.0 * std::f32::consts::PI / (segments as f32);
-        let theta2 = ((i + 1) as f32) * 2.0 * std::f32::consts::PI / (segments as f32);
-        
-        let x0 = cx + (r - thickness) * theta1.cos();
-        let y0 = cy + (r - thickness) * theta1.sin();
-        let x1 = cx + r * theta1.cos();
-        let y1 = cy + r * theta1.sin();
-        
-        let x2 = cx + r * theta2.cos();
-        let y2 = cy + r * theta2.sin();
-        let x3 = cx + (r - thickness) * theta2.cos();
-        let y3 = cy + (r - thickness) * theta2.sin();
-        
-        let ndc_x0 = (x0 / sw) * 2.0 - 1.0; let ndc_y0 = 1.0 - (y0 / sh) * 2.0;
-        let ndc_x1 = (x1 / sw) * 2.0 - 1.0; let ndc_y1 = 1.0 - (y1 / sh) * 2.0;
-        let ndc_x2 = (x2 / sw) * 2.0 - 1.0; let ndc_y2 = 1.0 - (y2 / sh) * 2.0;
-        let ndc_x3 = (x3 / sw) * 2.0 - 1.0; let ndc_y3 = 1.0 - (y3 / sh) * 2.0;
-        
-        verts.push(Vertex { position: [ndc_x0, ndc_y0], color, clip_circle });
-        verts.push(Vertex { position: [ndc_x1, ndc_y1], color, clip_circle });
-        verts.push(Vertex { position: [ndc_x2, ndc_y2], color, clip_circle });
-        
-        verts.push(Vertex { position: [ndc_x0, ndc_y0], color, clip_circle });
-        verts.push(Vertex { position: [ndc_x2, ndc_y2], color, clip_circle });
-        verts.push(Vertex { position: [ndc_x3, ndc_y3], color, clip_circle });
-    }
-    verts
-}
-
-pub fn arc_background_vertices(
-    cx: f32, cy: f32, r: f32,
-    thickness: f32,
-    start_angle: f32, end_angle: f32,
-    sw: f32, sh: f32,
-    color: [f32; 4],
-    segments: usize,
-    clip_circle: [f32; 3],
-) -> Vec<Vertex> {
-    let mut verts = Vec::new();
-    for i in 0..segments {
-        let theta1 = start_angle + (i as f32) * (end_angle - start_angle) / (segments as f32);
-        let theta2 = start_angle + ((i + 1) as f32) * (end_angle - start_angle) / (segments as f32);
-        
-        let x0 = cx + (r - thickness) * theta1.cos();
-        let y0 = cy + (r - thickness) * theta1.sin();
-        let x1 = cx + r * theta1.cos();
-        let y1 = cy + r * theta1.sin();
-        
-        let x2 = cx + r * theta2.cos();
-        let y2 = cy + r * theta2.sin();
-        let x3 = cx + (r - thickness) * theta2.cos();
-        let y3 = cy + (r - thickness) * theta2.sin();
-        
-        let ndc_x0 = (x0 / sw) * 2.0 - 1.0; let ndc_y0 = 1.0 - (y0 / sh) * 2.0;
-        let ndc_x1 = (x1 / sw) * 2.0 - 1.0; let ndc_y1 = 1.0 - (y1 / sh) * 2.0;
-        let ndc_x2 = (x2 / sw) * 2.0 - 1.0; let ndc_y2 = 1.0 - (y2 / sh) * 2.0;
-        let ndc_x3 = (x3 / sw) * 2.0 - 1.0; let ndc_y3 = 1.0 - (y3 / sh) * 2.0;
-        
-        verts.push(Vertex { position: [ndc_x0, ndc_y0], color, clip_circle });
-        verts.push(Vertex { position: [ndc_x1, ndc_y1], color, clip_circle });
-        verts.push(Vertex { position: [ndc_x2, ndc_y2], color, clip_circle });
-        
-        verts.push(Vertex { position: [ndc_x0, ndc_y0], color, clip_circle });
-        verts.push(Vertex { position: [ndc_x2, ndc_y2], color, clip_circle });
-        verts.push(Vertex { position: [ndc_x3, ndc_y3], color, clip_circle });
-    }
-    verts
-}
-
-
-#[derive(Debug, Clone)]
-pub struct WindowSettings {
-    pub title: String,
-    pub app_id: String,
-    pub width: u32,
-    pub height: u32,
-    pub fullscreen: bool,
-    pub min_size: Option<(u32, u32)>,
-}
-
-#[derive(Debug, Clone, Copy, PartialEq)]
-pub struct LogicalPosition {
-    pub x: f32,
-    pub y: f32,
-}
-
-impl LogicalPosition {
-    pub fn new(x: f32, y: f32) -> Self {
-        Self { x, y }
-    }
-}
-
-#[derive(Debug, Clone, Copy, PartialEq)]
-pub struct LogicalSize {
-    pub width: f32,
-    pub height: f32,
-}
-
-impl LogicalSize {
-    pub fn new(width: f32, height: f32) -> Self {
-        Self { width, height }
-    }
-}
-
-pub struct RenderContext<'a> {
-    pub font_system: &'a mut FontSystem,
-}
-
-pub trait Application: Sized + 'static {
-    type Message: Send + Clone + 'static;
-
-    fn new(qh: &QueueHandle<EngineState<Self>>, sender: calloop::channel::Sender<Self::Message>) -> Self;
-    fn settings(&self) -> WindowSettings;
-    fn update(&mut self, msg: Self::Message, needs_rebuild: &mut bool, exit: &mut bool);
-    fn tick(&mut self, dt: f32, needs_rebuild: &mut bool);
-    fn view(&mut self, quads: &mut Vec<(f32, f32, f32, f32, [f32; 4])>, size: LogicalSize, scale: f64);
-    fn view_vectors(&mut self, _vectors: &mut Vec<(f32, f32, f32, f32, f32, [f32; 4], LineCap)>, _size: LogicalSize, _scale: f64) {}
-    fn overlay_quads(&mut self, _quads: &mut Vec<(f32, f32, f32, f32, [f32; 4])>, _size: LogicalSize, _scale: f64) {}
-    fn text_items(&self) -> &[TextItem];
-    
-    fn text_areas(&self, scale_f32: f32, bounds: TextBounds) -> Vec<TextArea<'_>> {
-        self.text_items().iter().map(|ti| {
-            let item_bounds = if let Some([l, t, r, b]) = ti.bounds {
-                TextBounds {
-                    left: (l * scale_f32).round() as i32,
-                    top: (t * scale_f32).round() as i32,
-                    right: (r * scale_f32).round() as i32,
-                    bottom: (b * scale_f32).round() as i32,
-                }
-            } else {
-                bounds
-            };
-            TextArea {
-                buffer: &ti.buffer,
-                left: ti.x * scale_f32,
-                top: ti.y * scale_f32,
-                scale: scale_f32,
-                bounds: item_bounds,
-                default_color: ti.color,
-                custom_glyphs: &[],
-            }
-        }).collect()
-    }
-    
-    fn clear_color(&self) -> [f32; 4] {
-        [0.0, 0.0, 0.0, 0.0]
-    }
-    
-    fn handle_pointer_move(&mut self, pos: LogicalPosition, needs_rebuild: &mut bool);
-    fn handle_mouse_input(&mut self, button: MouseButton, state: ElementState, pos: LogicalPosition, needs_rebuild: &mut bool) -> Option<Self::Message>;
-    fn handle_mouse_wheel(&mut self, delta: &MouseScrollDelta, pos: LogicalPosition, needs_rebuild: &mut bool);
-    fn handle_key_input(&mut self, event: &KeyEvent, needs_rebuild: &mut bool) -> Option<Self::Message>;
-}
-
-pub struct PressedKey {
-    pub logical_key: Key,
-    pub text: Option<String>,
-    pub first_pressed: Instant,
-    pub last_repeated: Instant,
-}
-
-fn is_repeatable_key(key: &Key) -> bool {
-    match key {
-        Key::Named(NamedKey::Backspace) |
-        Key::Named(NamedKey::Delete) |
-        Key::Named(NamedKey::ArrowLeft) |
-        Key::Named(NamedKey::ArrowRight) |
-        Key::Named(NamedKey::ArrowUp) |
-        Key::Named(NamedKey::ArrowDown) |
-        Key::Named(NamedKey::Home) |
-        Key::Named(NamedKey::End) |
-        Key::Character(_) => true,
-        _ => false,
-    }
-}
-
-pub struct EngineState<A: Application> {
-    pub registry_state: RegistryState,
-    pub compositor_state: CompositorState,
-    pub xdg_shell_state: XdgShell,
-    pub shm_state: Shm,
-    pub seat_state: SeatState,
-    pub output_state: OutputState,
-    pub seats: Vec<wl_seat::WlSeat>,
-    pub pointer: Option<ThemedPointer>,
-    pub keyboard: Option<wl_keyboard::WlKeyboard>,
-    
-    pub window: Option<XdgWindow>,
-    pub surface: Option<wl_surface::WlSurface>,
-    
-    pub inner: A,
-    
-    pub instance: Option<wgpu::Instance>,
-    pub wgpu_surface: Option<wgpu::Surface<'static>>,
-    pub device: Option<wgpu::Device>,
-    pub queue: Option<wgpu::Queue>,
-    pub config: Option<wgpu::SurfaceConfiguration>,
-    pub render_pipeline: Option<wgpu::RenderPipeline>,
-    pub vertex_buffer: Option<wgpu::Buffer>,
-    pub vertex_count: u32,
-    pub overlay_vertex_buffer: Option<wgpu::Buffer>,
-    pub overlay_vertex_count: u32,
-    
-    pub font_system: FontSystem,
-    pub swash_cache: SwashCache,
-    pub text_atlas: Option<TextAtlas>,
-    pub text_renderer: Option<TextRenderer>,
-    pub text_viewport: Option<Viewport>,
-    
-    pub scale_factor: f64,
-    pub logical_width: f32,
-    pub logical_height: f32,
-    
-    pub exit: bool,
-    pub redraw: bool,
-    pub first_configure_received: bool,
-    pub ctrl_pressed: bool,
-    pub shift_pressed: bool,
-    pub pressed_key: Option<PressedKey>,
-    pub sender: calloop::channel::Sender<A::Message>,
-    pub active_popup: Option<ActivePopup>,
-    pub qh: QueueHandle<EngineState<A>>,
-}
-
-impl<A: Application> EngineState<A> {
-    pub async fn init_gpu(&mut self, conn: &Connection, width_logical: f32, height_logical: f32) {
-        let s = self.scale_factor as f32;
-        let pw = (width_logical * s) as u32;
-        let ph = (height_logical * s) as u32;
-        
-        let surface = self.surface.as_ref().expect("surface missing");
-        
-        let wayland_handle = Box::leak(Box::new(WaylandSurfaceHandle {
-            display_ptr: conn.backend().display_id().as_ptr() as *mut std::ffi::c_void,
-            surface_ptr: surface.id().as_ptr() as *mut std::ffi::c_void,
-        }));
-        
-        let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor {
-            backends: wgpu::Backends::VULKAN,
-            ..Default::default()
-        });
-        self.instance = Some(instance.clone());
-        
-        let wgpu_surface = instance.create_surface(wayland_handle).expect("failed to create wgpu surface");
-        let adapter = instance.request_adapter(&wgpu::RequestAdapterOptions {
-            power_preference: wgpu::PowerPreference::LowPower,
-            compatible_surface: Some(&wgpu_surface),
-            force_fallback_adapter: false,
-        }).await.expect("failed to request adapter");
-        
-        let (device, queue) = adapter.request_device(&wgpu::DeviceDescriptor {
-            label: Some("GPU Device"),
-            required_features: wgpu::Features::empty(),
-            required_limits: wgpu::Limits::downlevel_webgl2_defaults().using_resolution(adapter.limits()),
-            memory_hints: wgpu::MemoryHints::MemoryUsage,
-        }, None).await.expect("failed to request device");
-        
-        let mut config = wgpu_surface.get_default_config(&adapter, pw.max(1), ph.max(1)).expect("failed to get surface configuration");
-        let capabilities = wgpu_surface.get_capabilities(&adapter);
-        let alpha_mode = if capabilities.alpha_modes.contains(&wgpu::CompositeAlphaMode::PreMultiplied) {
-            wgpu::CompositeAlphaMode::PreMultiplied
-        } else if capabilities.alpha_modes.contains(&wgpu::CompositeAlphaMode::PostMultiplied) {
-            wgpu::CompositeAlphaMode::PostMultiplied
-        } else {
-            capabilities.alpha_modes[0]
-        };
-        config.alpha_mode = alpha_mode;
-        
-        let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
-            label: Some("Shader"),
-            source: wgpu::ShaderSource::Wgsl(crate::SHADER.into()),
-        });
-        
-        let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
-            label: Some("Pipeline Layout"),
-            bind_group_layouts: &[],
-            push_constant_ranges: &[],
-        });
-        
-        let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
-            label: Some("Render Pipeline"),
-            layout: Some(&pipeline_layout),
-            vertex: wgpu::VertexState {
-                module: &shader,
-                entry_point: Some("vs_main"),
-                buffers: &[Vertex::desc()],
-                compilation_options: Default::default(),
-            },
-            fragment: Some(wgpu::FragmentState {
-                module: &shader,
-                entry_point: Some("fs_main"),
-                targets: &[Some(wgpu::ColorTargetState {
-                    format: config.format,
-                    blend: Some(wgpu::BlendState::ALPHA_BLENDING),
-                    write_mask: wgpu::ColorWrites::ALL,
-                })],
-                compilation_options: Default::default(),
-            }),
-            primitive: wgpu::PrimitiveState {
-                topology: wgpu::PrimitiveTopology::TriangleList,
-                front_face: wgpu::FrontFace::Ccw,
-                cull_mode: None,
-                polygon_mode: wgpu::PolygonMode::Fill,
-                unclipped_depth: false,
-                conservative: false,
-                strip_index_format: None,
-            },
-            depth_stencil: None,
-            multisample: wgpu::MultisampleState { count: 1, mask: !0, alpha_to_coverage_enabled: false },
-            multiview: None,
-            cache: None,
-        });
-        
-        let cache = Cache::new(&device);
-        let mut text_atlas = TextAtlas::new(&device, &queue, &cache, config.format);
-        let text_renderer = TextRenderer::new(&mut text_atlas, &device, wgpu::MultisampleState::default(), None);
-        let mut text_viewport = Viewport::new(&device, &cache);
-        text_viewport.update(&queue, Resolution { width: pw, height: ph });
-        
-        let vertex_buffer = device.create_buffer(&wgpu::BufferDescriptor {
-            label: Some("Vertex Buffer"),
-            size: 1,
-            usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
-            mapped_at_creation: false,
-        });
-
-        let overlay_vertex_buffer = device.create_buffer(&wgpu::BufferDescriptor {
-            label: Some("Overlay Vertex Buffer"),
-            size: 1,
-            usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
-            mapped_at_creation: false,
-        });
-        
-        self.wgpu_surface = Some(wgpu_surface);
-        self.device = Some(device);
-        self.queue = Some(queue);
-        self.config = Some(config);
-        self.render_pipeline = Some(render_pipeline);
-        self.text_atlas = Some(text_atlas);
-        self.text_renderer = Some(text_renderer);
-        self.text_viewport = Some(text_viewport);
-        self.vertex_buffer = Some(vertex_buffer);
-        self.overlay_vertex_buffer = Some(overlay_vertex_buffer);
-        self.logical_width = width_logical;
-        self.logical_height = height_logical;
-    }
-    
-    pub fn resize(&mut self, w: f32, h: f32) {
-        if w > 0.0 && h > 0.0 {
-            self.logical_width = w;
-            self.logical_height = h;
-            if let (Some(device), Some(surface), Some(config)) = (&self.device, &self.wgpu_surface, &mut self.config) {
-                config.width = (w as f64 * self.scale_factor) as u32;
-                config.height = (h as f64 * self.scale_factor) as u32;
-                surface.configure(device, config);
-            }
-        }
-    }
-    
-    pub fn render(&mut self) {
-        let logical_w = self.logical_width;
-        let logical_h = self.logical_height;
-        let scale_factor = self.scale_factor;
-        
-        let mut quads = Vec::new();
-        self.inner.view(&mut quads, LogicalSize::new(logical_w, logical_h), scale_factor);
-        
-        let mut vectors = Vec::new();
-        self.inner.view_vectors(&mut vectors, LogicalSize::new(logical_w, logical_h), scale_factor);
-        
-        let device = self.device.as_ref().unwrap();
-        let queue = self.queue.as_ref().unwrap();
-        let wgpu_surface = self.wgpu_surface.as_ref().unwrap();
-        let render_pipeline = self.render_pipeline.as_ref().unwrap();
-        let text_renderer = self.text_renderer.as_mut().unwrap();
-        let text_atlas = self.text_atlas.as_mut().unwrap();
-        let text_viewport = self.text_viewport.as_mut().unwrap();
-        let config = self.config.as_ref().unwrap();
-        
-        // 1. Build and upload vertex buffer
-        let mut verts = Vec::new();
-        for &(qx, qy, qw, qh, qc) in &quads {
-            verts.extend(quad_vertices(qx, qy, qw, qh, logical_w, logical_h, qc));
-        }
-        for &(vx1, vy1, vx2, vy2, vthickness, vcolor, vcap) in &vectors {
-            verts.extend(vector_vertices(vx1, vy1, vx2, vy2, vthickness, logical_w, logical_h, vcolor, vcap));
-        }
-        self.vertex_count = verts.len() as u32;
-        if self.vertex_count > 0 {
-            let data = bytemuck::cast_slice(&verts);
-            let needed = data.len() as wgpu::BufferAddress;
-            let mut vbuf = self.vertex_buffer.as_ref().unwrap();
-            if needed > vbuf.size() {
-                let new_vbuf = device.create_buffer(&wgpu::BufferDescriptor {
-                    label: Some("Vertex Buffer"),
-                    size: needed,
-                    usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
-                    mapped_at_creation: false,
-                });
-                self.vertex_buffer = Some(new_vbuf);
-                vbuf = self.vertex_buffer.as_ref().unwrap();
-            }
-            queue.write_buffer(vbuf, 0, data);
-        }
-
-        // 1b. Build and upload overlay vertex buffer
-        let mut overlay_quads = Vec::new();
-        self.inner.overlay_quads(&mut overlay_quads, LogicalSize::new(logical_w, logical_h), scale_factor);
-        let mut overlay_verts = Vec::new();
-        for &(qx, qy, qw, qh, qc) in &overlay_quads {
-            overlay_verts.extend(quad_vertices(qx, qy, qw, qh, logical_w, logical_h, qc));
-        }
-        self.overlay_vertex_count = overlay_verts.len() as u32;
-        if self.overlay_vertex_count > 0 {
-            let data = bytemuck::cast_slice(&overlay_verts);
-            let needed = data.len() as wgpu::BufferAddress;
-            let mut ovbuf = self.overlay_vertex_buffer.as_ref().unwrap();
-            if needed > ovbuf.size() {
-                let new_ovbuf = device.create_buffer(&wgpu::BufferDescriptor {
-                    label: Some("Overlay Vertex Buffer"),
-                    size: needed,
-                    usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
-                    mapped_at_creation: false,
-                });
-                self.overlay_vertex_buffer = Some(new_ovbuf);
-                ovbuf = self.overlay_vertex_buffer.as_ref().unwrap();
-            }
-            queue.write_buffer(ovbuf, 0, data);
-        }
-        
-        // 2. Prepare text
-        let scale_f32 = scale_factor as f32;
-        let pw = (logical_w * scale_f32) as u32;
-        let ph = (logical_h * scale_f32) as u32;
-        text_viewport.update(queue, Resolution { width: pw, height: ph });
-        
-        let bounds = TextBounds { left: 0, top: 0, right: pw as i32, bottom: ph as i32 };
-        let areas = self.inner.text_areas(scale_f32, bounds);
-        
-        text_renderer.prepare(device, queue, &mut self.font_system, text_atlas, text_viewport, areas, &mut self.swash_cache).unwrap();
-        
-        // 3. Render Pass
-        let output = match wgpu_surface.get_current_texture() {
-            Ok(t) => t,
-            Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => {
-                wgpu_surface.configure(device, config);
-                return;
-            }
-            Err(wgpu::SurfaceError::Timeout) => return,
-            Err(e) => {
-                eprintln!("Surface error: {e:?}");
-                return;
-            }
-        };
-        let view = output.texture.create_view(&wgpu::TextureViewDescriptor::default());
-        let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
-            label: Some("Encoder"),
-        });
-        
-        {
-            let cc = self.inner.clear_color();
-            let r_clear = (cc[0] as f64).powf(2.2);
-            let g_clear = (cc[1] as f64).powf(2.2);
-            let b_clear = (cc[2] as f64).powf(2.2);
-            let a_clear = cc[3] as f64;
-            
-            let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
-                label: Some("Render Pass"),
-                color_attachments: &[Some(wgpu::RenderPassColorAttachment {
-                    view: &view,
-                    resolve_target: None,
-                    ops: wgpu::Operations {
-                        load: wgpu::LoadOp::Clear(wgpu::Color {
-                            r: r_clear,
-                            g: g_clear,
-                            b: b_clear,
-                            a: a_clear,
-                        }),
-                        store: wgpu::StoreOp::Store,
-                    },
-                })],
-                depth_stencil_attachment: None,
-                timestamp_writes: None,
-                occlusion_query_set: None,
-            });
-            
-            if self.vertex_count > 0 {
-                pass.set_pipeline(render_pipeline);
-                pass.set_vertex_buffer(0, self.vertex_buffer.as_ref().unwrap().slice(..));
-                pass.draw(0..self.vertex_count, 0..1);
-            }
-            
-            text_renderer.render(text_atlas, text_viewport, &mut pass).unwrap();
-
-            if self.overlay_vertex_count > 0 {
-                pass.set_pipeline(render_pipeline);
-                pass.set_vertex_buffer(0, self.overlay_vertex_buffer.as_ref().unwrap().slice(..));
-                pass.draw(0..self.overlay_vertex_count, 0..1);
-            }
-        }
-        
-        queue.submit(std::iter::once(encoder.finish()));
-        output.present();
-        
-        if let Some(ref mut popup) = self.active_popup {
-            if popup.configured {
-                let mut collector = crate::layout::PopoverCollector::new();
-                crate::layout::render_popovers(&mut collector);
-
-                let mut verts = Vec::new();
-                for &(color, qx, qy, qw, qh) in &collector.rects {
-                    let qx_local = qx - popup.x;
-                    let qy_local = qy - popup.y;
-                    verts.extend(quad_vertices(qx_local, qy_local, qw, qh, popup.logical_width, popup.logical_height, color));
-                }
-
-                let vertex_count = verts.len() as u32;
-                if vertex_count > 0 {
-                    let data = bytemuck::cast_slice(&verts);
-                    let needed = data.len() as wgpu::BufferAddress;
-                    let mut vbuf = popup.vertex_buffer.as_ref();
-                    if vbuf.map_or(true, |v| needed > v.size()) {
-                        let new_vbuf = device.create_buffer(&wgpu::BufferDescriptor {
-                            label: Some("Popup Vertex Buffer"),
-                            size: needed,
-                            usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
-                            mapped_at_creation: false,
-                        });
-                        popup.vertex_buffer = Some(new_vbuf);
-                        vbuf = popup.vertex_buffer.as_ref();
-                    }
-                    queue.write_buffer(vbuf.unwrap(), 0, data);
-                }
-
-                let mut text_items = Vec::new();
-                for (content, size, tx, ty, color, font, bounds) in collector.texts {
-                    let tx_local = tx - popup.x;
-                    let ty_local = ty - popup.y;
-                    text_items.push(TextItem {
-                        buffer: make_text_buffer_with_font(&mut self.font_system, &content, size, font.as_deref()),
-                        x: tx_local,
-                        y: ty_local,
-                        color: glyphon::Color::rgb(
-                            (color[0] * 255.0).clamp(0.0, 255.0) as u8,
-                            (color[1] * 255.0).clamp(0.0, 255.0) as u8,
-                            (color[2] * 255.0).clamp(0.0, 255.0) as u8,
-                        ),
-                        bounds,
-                    });
-                }
-
-                let scale_f32 = scale_factor as f32;
-                let bounds = TextBounds {
-                    left: 0,
-                    top: 0,
-                    right: (popup.logical_width * scale_f32) as i32,
-                    bottom: (popup.logical_height * scale_f32) as i32,
-                };
-                let areas: Vec<TextArea<'_>> = text_items.iter().map(|ti| TextArea {
-                    buffer: &ti.buffer,
-                    left: ti.x * scale_f32,
-                    top: ti.y * scale_f32,
-                    scale: scale_f32,
-                    bounds,
-                    default_color: ti.color,
-                    custom_glyphs: &[],
-                }).collect();
-
-                popup.viewport.update(queue, Resolution {
-                    width: (popup.logical_width * scale_f32) as u32,
-                    height: (popup.logical_height * scale_f32) as u32,
-                });
-
-                text_renderer.prepare(
-                    device,
-                    queue,
-                    &mut self.font_system,
-                    text_atlas,
-                    &popup.viewport,
-                    areas,
-                    &mut self.swash_cache,
-                ).unwrap();
-
-                let popup_output = match popup.wgpu_surface.get_current_texture() {
-                    Ok(t) => t,
-                    Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => {
-                        popup.wgpu_surface.configure(device, &popup.config);
-                        return;
-                    }
-                    Err(wgpu::SurfaceError::Timeout) => return,
-                    Err(e) => {
-                        eprintln!("Popup surface error: {e:?}");
-                        return;
-                    }
-                };
-                let popup_view = popup_output.texture.create_view(&wgpu::TextureViewDescriptor::default());
-                let mut popup_encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
-                    label: Some("Popup Encoder"),
-                });
-
-                {
-                    let mut pass = popup_encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
-                        label: Some("Popup Render Pass"),
-                        color_attachments: &[Some(wgpu::RenderPassColorAttachment {
-                            view: &popup_view,
-                            resolve_target: None,
-                            ops: wgpu::Operations {
-                                load: wgpu::LoadOp::Clear(wgpu::Color { r: 0.0, g: 0.0, b: 0.0, a: 0.0 }),
-                                store: wgpu::StoreOp::Store,
-                            },
-                        })],
-                        depth_stencil_attachment: None,
-                        timestamp_writes: None,
-                        occlusion_query_set: None,
-                    });
-
-                    if vertex_count > 0 {
-                        pass.set_pipeline(render_pipeline);
-                        pass.set_vertex_buffer(0, popup.vertex_buffer.as_ref().unwrap().slice(..));
-                        pass.draw(0..vertex_count, 0..1);
-                    }
-
-                    text_renderer.render(text_atlas, &popup.viewport, &mut pass).unwrap();
-                }
-
-                queue.submit(std::iter::once(popup_encoder.finish()));
-                popup_output.present();
-            }
-        }
-
-        text_atlas.trim();
-    }
-}
-
-impl<A: Application> Drop for EngineState<A> {
-    fn drop(&mut self) {
-        self.instance = None;
-        self.wgpu_surface = None;
-        self.device = None;
-        self.queue = None;
-        self.render_pipeline = None;
-        self.vertex_buffer = None;
-        self.overlay_vertex_buffer = None;
-        self.text_atlas = None;
-        self.text_renderer = None;
-        self.text_viewport = None;
-    }
-}
-
-impl<A: Application> CompositorHandler for EngineState<A> {
-    fn scale_factor_changed(
-        &mut self,
-        _conn: &Connection,
-        _qh: &QueueHandle<Self>,
-        _surface: &wl_surface::WlSurface,
-        scale_factor: i32,
-    ) {
-        _surface.set_buffer_scale(scale_factor);
-        self.scale_factor = scale_factor as f64;
-        self.resize(self.logical_width, self.logical_height);
-        self.redraw = true;
-    }
-    
-    fn transform_changed(
-        &mut self,
-        _conn: &Connection,
-        _qh: &QueueHandle<Self>,
-        _surface: &wl_surface::WlSurface,
-        _new_transform: wl_output::Transform,
-    ) {}
-    
-    fn frame(
-        &mut self,
-        _conn: &Connection,
-        _qh: &QueueHandle<Self>,
-        _surface: &wl_surface::WlSurface,
-        _time: u32,
-    ) {}
-    
-    fn surface_enter(
-        &mut self,
-        _conn: &Connection,
-        _qh: &QueueHandle<Self>,
-        _surface: &wl_surface::WlSurface,
-        _output: &wl_output::WlOutput,
-    ) {}
-    
-    fn surface_leave(
-        &mut self,
-        _conn: &Connection,
-        _qh: &QueueHandle<Self>,
-        _surface: &wl_surface::WlSurface,
-        _output: &wl_output::WlOutput,
-    ) {}
-}
-
-impl<A: Application> OutputHandler for EngineState<A> {
-    fn output_state(&mut self) -> &mut OutputState {
-        &mut self.output_state
-    }
-    
-    fn new_output(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, _output: wl_output::WlOutput) {}
-    fn update_output(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, _output: wl_output::WlOutput) {}
-    fn output_destroyed(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, _output: wl_output::WlOutput) {}
-}
-
-impl<A: Application> ShmHandler for EngineState<A> {
-    fn shm_state(&mut self) -> &mut Shm {
-        &mut self.shm_state
-    }
-}
-
-impl<A: Application> ProvidesRegistryState for EngineState<A> {
-    fn registry(&mut self) -> &mut RegistryState {
-        &mut self.registry_state
-    }
-    
-    fn runtime_add_global(
-        &mut self,
-        _conn: &Connection,
-        _qh: &QueueHandle<Self>,
-        _name: u32,
-        _interface: &str,
-        _version: u32,
-    ) {}
-    
-    fn runtime_remove_global(
-        &mut self,
-        _conn: &Connection,
-        _qh: &QueueHandle<Self>,
-        _name: u32,
-        _interface: &str,
-    ) {}
-}
-
-impl<A: Application> WindowHandler for EngineState<A> {
-    fn configure(
-        &mut self,
-        _conn: &Connection,
-        _qh: &QueueHandle<Self>,
-        _window: &XdgWindow,
-        configure: WindowConfigure,
-        _serial: u32,
-    ) {
-        let (w, h) = configure.new_size;
-        if let (Some(w), Some(h)) = (w, h) {
-            let width = w.get();
-            let height = h.get();
-            self.resize(width as f32, height as f32);
-        }
-        self.redraw = true;
-        self.first_configure_received = true;
-    }
-    
-    fn request_close(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, _window: &XdgWindow) {
-        self.exit = true;
-    }
-}
-
-impl<A: Application> SeatHandler for EngineState<A> {
-    fn seat_state(&mut self) -> &mut SeatState {
-        &mut self.seat_state
-    }
-    
-    fn new_seat(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, seat: wl_seat::WlSeat) {
-        self.seats.push(seat);
-    }
-    
-    fn new_capability(
-        &mut self,
-        _conn: &Connection,
-        qh: &QueueHandle<Self>,
-        seat: wl_seat::WlSeat,
-        capability: Capability,
-    ) {
-        if capability == Capability::Pointer && self.pointer.is_none() {
-            let surface = self.compositor_state.create_surface(qh);
-            let themed_pointer = self.seat_state.get_pointer_with_theme(
-                qh,
-                &seat,
-                self.shm_state.wl_shm(),
-                surface,
-                ThemeSpec::System,
-            ).unwrap();
-            self.pointer = Some(themed_pointer);
-        }
-        if capability == Capability::Keyboard && self.keyboard.is_none() {
-            let keyboard = self.seat_state.get_keyboard(qh, &seat, None).unwrap();
-            self.keyboard = Some(keyboard);
-        }
-    }
-    
-    fn remove_capability(
-        &mut self,
-        _conn: &Connection,
-        _qh: &QueueHandle<Self>,
-        _seat: wl_seat::WlSeat,
-        capability: Capability,
-    ) {
-        if capability == Capability::Pointer {
-            self.pointer = None;
-        }
-        if capability == Capability::Keyboard {
-            self.keyboard = None;
-        }
-    }
-    
-    fn remove_seat(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, seat: wl_seat::WlSeat) {
-        self.seats.retain(|s| s != &seat);
-    }
-}
-
-impl<A: Application> PointerHandler for EngineState<A> {
-    fn pointer_frame(
-        &mut self,
-        _conn: &Connection,
-        _qh: &QueueHandle<Self>,
-        _pointer: &wl_pointer::WlPointer,
-        events: &[smithay_client_toolkit::seat::pointer::PointerEvent],
-    ) {
-        use smithay_client_toolkit::seat::pointer::PointerEventKind;
-        for event in events {
-            let (x, y) = event.position;
-            let mut lx = x as f32;
-            let mut ly = y as f32;
-            
-            if let Some(ref popup) = self.active_popup {
-                if event.surface == *popup.sctk_popup.wl_surface() {
-                    lx += popup.x;
-                    ly += popup.y;
-                }
-            }
-            
-            match &event.kind {
-                PointerEventKind::Enter { .. } => {
-                    if let Some(ref themed_pointer) = self.pointer {
-                        let _ = themed_pointer.set_cursor(_conn, CursorIcon::Default);
-                    }
-                }
-                PointerEventKind::Leave { .. } => {}
-                PointerEventKind::Motion { .. } => {
-                    let mut rebuild = false;
-                    self.inner.handle_pointer_move(LogicalPosition::new(lx, ly), &mut rebuild);
-                    if rebuild {
-                        self.redraw = true;
-                    }
-                }
-                PointerEventKind::Press { button, .. } => {
-                    let btn = match *button {
-                        272 => MouseButton::Left,
-                        273 => MouseButton::Right,
-                        274 => MouseButton::Middle,
-                        _ => continue,
-                    };
-                    let mut rebuild = false;
-                    if let Some(msg) = self.inner.handle_mouse_input(btn, ElementState::Pressed, LogicalPosition::new(lx, ly), &mut rebuild) {
-                        let mut update_rebuild = false;
-                        self.inner.update(msg, &mut update_rebuild, &mut self.exit);
-                        if update_rebuild {
-                            rebuild = true;
-                        }
-                    }
-                    if rebuild {
-                        self.redraw = true;
-                    }
-                }
-                PointerEventKind::Release { button, .. } => {
-                    let btn = match *button {
-                        272 => MouseButton::Left,
-                        273 => MouseButton::Right,
-                        274 => MouseButton::Middle,
-                        _ => continue,
-                    };
-                    let mut rebuild = false;
-                    if let Some(msg) = self.inner.handle_mouse_input(btn, ElementState::Released, LogicalPosition::new(lx, ly), &mut rebuild) {
-                        let mut update_rebuild = false;
-                        self.inner.update(msg, &mut update_rebuild, &mut self.exit);
-                        if update_rebuild {
-                            rebuild = true;
-                        }
-                    }
-                    if rebuild {
-                        self.redraw = true;
-                    }
-                }
-                PointerEventKind::Axis { horizontal, vertical, .. } => {
-                    let h_scroll = horizontal.absolute as f32;
-                    let v_scroll = vertical.absolute as f32;
-                    let delta = MouseScrollDelta::LineDelta(-h_scroll / 10.0, -v_scroll / 10.0);
-                    let mut rebuild = false;
-                    self.inner.handle_mouse_wheel(&delta, LogicalPosition::new(lx, ly), &mut rebuild);
-                    if rebuild {
-                        self.redraw = true;
-                    }
-                }
-            }
-        }
-    }
-}
-
-impl<A: Application> KeyboardHandler for EngineState<A> {
-    fn enter(
-        &mut self,
-        _conn: &Connection,
-        _qh: &QueueHandle<Self>,
-        _keyboard: &wl_keyboard::WlKeyboard,
-        _surface: &wl_surface::WlSurface,
-        _serial: u32,
-        _raw_modifiers: &[u32],
-        _keysyms: &[xkeysym::Keysym],
-    ) {}
-    
-    fn leave(
-        &mut self,
-        _conn: &Connection,
-        _qh: &QueueHandle<Self>,
-        _keyboard: &wl_keyboard::WlKeyboard,
-        _surface: &wl_surface::WlSurface,
-        _serial: u32,
-    ) {
-        self.pressed_key = None;
-        self.ctrl_pressed = false;
-        self.shift_pressed = false;
-    }
-    
-    fn press_key(
-        &mut self,
-        _conn: &Connection,
-        _qh: &QueueHandle<Self>,
-        _keyboard: &wl_keyboard::WlKeyboard,
-        _serial: u32,
-        event: smithay_client_toolkit::seat::keyboard::KeyEvent,
-    ) {
-        self.handle_key(event, ElementState::Pressed);
-    }
-    
-    fn release_key(
-        &mut self,
-        _conn: &Connection,
-        _qh: &QueueHandle<Self>,
-        _keyboard: &wl_keyboard::WlKeyboard,
-        _serial: u32,
-        event: smithay_client_toolkit::seat::keyboard::KeyEvent,
-    ) {
-        self.handle_key(event, ElementState::Released);
-    }
-    
-    fn update_modifiers(
-        &mut self,
-        _conn: &Connection,
-        _qh: &QueueHandle<Self>,
-        _keyboard: &wl_keyboard::WlKeyboard,
-        _serial: u32,
-        modifiers: smithay_client_toolkit::seat::keyboard::Modifiers,
-        _layout: u32,
-    ) {
-        self.ctrl_pressed = modifiers.ctrl;
-        self.shift_pressed = modifiers.shift;
-    }
-}
-
-impl<A: Application> EngineState<A> {
-    fn handle_key(&mut self, event: smithay_client_toolkit::seat::keyboard::KeyEvent, state: ElementState) {
-        let logical_key = match event.keysym {
-            xkeysym::Keysym::Escape => Key::Named(NamedKey::Escape),
-            xkeysym::Keysym::Return => Key::Named(NamedKey::Enter),
-            xkeysym::Keysym::BackSpace => Key::Named(NamedKey::Backspace),
-            xkeysym::Keysym::Down => Key::Named(NamedKey::ArrowDown),
-            xkeysym::Keysym::Up => Key::Named(NamedKey::ArrowUp),
-            xkeysym::Keysym::Left => Key::Named(NamedKey::ArrowLeft),
-            xkeysym::Keysym::Right => Key::Named(NamedKey::ArrowRight),
-            xkeysym::Keysym::Tab => Key::Named(NamedKey::Tab),
-            xkeysym::Keysym::Delete => Key::Named(NamedKey::Delete),
-            xkeysym::Keysym::space => Key::Named(NamedKey::Space),
-            _ => {
-                if let Some(ref text) = event.utf8 {
-                    Key::Character(text.clone())
-                } else if let Some(ch) = event.keysym.key_char() {
-                    Key::Character(ch.to_string())
-                } else {
-                    return;
-                }
-            }
-        };
-
-        let custom_event = KeyEvent {
-            state,
-            logical_key,
-            text: event.utf8.clone(),
-            repeat: false,
-            ctrl: self.ctrl_pressed,
-            shift: self.shift_pressed,
-        };
-
-        if state == ElementState::Pressed {
-            if is_repeatable_key(&custom_event.logical_key) {
-                self.pressed_key = Some(PressedKey {
-                    logical_key: custom_event.logical_key.clone(),
-                    text: custom_event.text.clone(),
-                    first_pressed: Instant::now(),
-                    last_repeated: Instant::now(),
-                });
-            } else {
-                self.pressed_key = None;
-            }
-        } else if state == ElementState::Released {
-            if let Some(ref pk) = self.pressed_key {
-                if pk.logical_key == custom_event.logical_key {
-                    self.pressed_key = None;
-                }
-            }
-        }
-
-        let mut rebuild = false;
-        if let Some(msg) = self.inner.handle_key_input(&custom_event, &mut rebuild) {
-            let mut update_rebuild = false;
-            self.inner.update(msg, &mut update_rebuild, &mut self.exit);
-            if update_rebuild {
-                rebuild = true;
-            }
-        }
-        if rebuild {
-            self.redraw = true;
-        }
-    }
-}
-
-impl<A: Application> wayland_client::Dispatch<wl_registry::WlRegistry, GlobalList, Self> for EngineState<A> {
-    fn event(
-        _state: &mut Self,
-        _proxy: &wl_registry::WlRegistry,
-        _event: wl_registry::Event,
-        _data: &GlobalList,
-        _conn: &Connection,
-        _qh: &QueueHandle<Self>,
-    ) {}
-}
-
-impl<A: Application> wayland_client::Dispatch<crate::protocol::zclear_inspector_v1::ZclearInspectorV1, ()> for EngineState<A> {
-    fn event(
-        _state: &mut Self,
-        _proxy: &crate::protocol::zclear_inspector_v1::ZclearInspectorV1,
-        _event: crate::protocol::zclear_inspector_v1::Event,
-        _data: &(),
-        _conn: &Connection,
-        _qh: &QueueHandle<Self>,
-    ) {}
-}
-
-impl<A: Application> PopupHandler for EngineState<A> {
-    fn configure(
-        &mut self,
-        _conn: &Connection,
-        _qh: &QueueHandle<Self>,
-        _popup: &Popup,
-        _config: PopupConfigure,
-    ) {
-        if let Some(ref mut p) = self.active_popup {
-            p.configured = true;
-        }
-        self.redraw = true;
-    }
-
-    fn done(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, _popup: &Popup) {
-        for popover_ptr in crate::widget::popovers::get_active() {
-            unsafe {
-                let popover = &mut *(popover_ptr as *mut dyn crate::widget::Element);
-                popover.unfocus();
-            }
-        }
-        self.active_popup = None;
-        self.redraw = true;
-    }
-}
-
-// Delegate macros for generic EngineState
-delegate_compositor!(@<A: Application> EngineState<A>);
-delegate_xdg_shell!(@<A: Application> EngineState<A>);
-delegate_xdg_window!(@<A: Application> EngineState<A>);
-delegate_xdg_popup!(@<A: Application> EngineState<A>);
-delegate_shm!(@<A: Application> EngineState<A>);
-delegate_seat!(@<A: Application> EngineState<A>);
-delegate_pointer!(@<A: Application> EngineState<A>);
-delegate_keyboard!(@<A: Application> EngineState<A>);
-delegate_registry!(@<A: Application> EngineState<A>);
-delegate_output!(@<A: Application> EngineState<A>);
-
-pub fn run<A: Application>() {
-    let conn = Connection::connect_to_env().unwrap();
-    let (globals, mut event_queue) = registry_queue_init(&conn).unwrap();
-    let qh = event_queue.handle();
-
-    let compositor_state = CompositorState::bind(&globals, &qh).unwrap();
-    let xdg_shell_state = XdgShell::bind(&globals, &qh).unwrap();
-    let shm_state = Shm::bind(&globals, &qh).unwrap();
-    let seat_state = SeatState::new(&globals, &qh);
-    let output_state = OutputState::new(&globals, &qh);
-
-    let (sender, channel) = calloop::channel::channel::<A::Message>();
-
-    let inner = A::new(&qh, sender.clone());
-    let settings = inner.settings();
-
-    let font_system = FontSystem::new();
-    let swash_cache = SwashCache::new();
-
-    let mut engine_state = EngineState {
-        registry_state: RegistryState::new(&globals),
-        compositor_state,
-        xdg_shell_state,
-        shm_state,
-        seat_state,
-        output_state,
-        seats: Vec::new(),
-        pointer: None,
-        keyboard: None,
-        window: None,
-        surface: None,
-        inner,
-        instance: None,
-        wgpu_surface: None,
-        device: None,
-        queue: None,
-        config: None,
-        render_pipeline: None,
-        vertex_buffer: None,
-        vertex_count: 0,
-        overlay_vertex_buffer: None,
-        overlay_vertex_count: 0,
-        font_system,
-        swash_cache,
-        text_atlas: None,
-        text_renderer: None,
-        text_viewport: None,
-        scale_factor: 1.0,
-        logical_width: settings.width as f32,
-        logical_height: settings.height as f32,
-        exit: false,
-        redraw: false,
-        first_configure_received: false,
-        ctrl_pressed: false,
-        shift_pressed: false,
-        pressed_key: None,
-        sender,
-        active_popup: None,
-        qh: qh.clone(),
-    };
-
-    event_queue.roundtrip(&mut engine_state).unwrap();
-
-    let scale = detect_scale_factor(&engine_state.output_state);
-    engine_state.scale_factor = scale;
-
-    let surface = engine_state.compositor_state.create_surface(&qh);
-    surface.set_buffer_scale(scale as i32);
-    let window = engine_state.xdg_shell_state.create_window(surface.clone(), WindowDecorations::None, &qh);
-    window.set_title(&settings.title);
-    window.set_app_id(&settings.app_id);
-    if settings.fullscreen {
-        window.set_fullscreen(None);
-    }
-    if let Some((min_w, min_h)) = settings.min_size {
-        window.set_min_size(Some((min_w, min_h)));
-    }
-    window.commit();
-
-    engine_state.window = Some(window);
-    engine_state.surface = Some(surface);
-
-    pollster::block_on(engine_state.init_gpu(&conn, settings.width as f32, settings.height as f32));
-
-    let mut event_loop = EventLoop::try_new().unwrap();
-    let loop_handle = event_loop.handle();
-    WaylandSource::new(conn.clone(), event_queue).insert(loop_handle.clone()).unwrap();
-
-    loop_handle.insert_source(channel, |event, _metadata, app_state: &mut EngineState<A>| {
-        if let calloop::channel::Event::Msg(msg) = event {
-            let mut rebuild = false;
-            app_state.inner.update(msg, &mut rebuild, &mut app_state.exit);
-            if rebuild {
-                app_state.redraw = true;
-            }
-        }
-    }).unwrap();
-
-    const KEY_REPEAT_DELAY: std::time::Duration = std::time::Duration::from_millis(500);
-    const KEY_REPEAT_INTERVAL: std::time::Duration = std::time::Duration::from_millis(50);
-
-    let mut last_title = settings.title.clone();
-    let mut last_tick = std::time::Instant::now();
-    loop {
-        event_loop.dispatch(std::time::Duration::from_millis(16), &mut engine_state).unwrap();
-        if engine_state.exit {
-            break;
-        }
-
-        let now = std::time::Instant::now();
-        let mut dt = now.duration_since(last_tick).as_secs_f32();
-        last_tick = now;
-        if dt > 0.1 {
-            dt = 0.1;
-        }
-
-        let mut rebuild = false;
-        engine_state.inner.tick(dt, &mut rebuild);
-        if rebuild {
-            engine_state.redraw = true;
-        }
-
-        if let Some(ref mut pk) = engine_state.pressed_key {
-            let now = std::time::Instant::now();
-            if now.duration_since(pk.first_pressed) >= KEY_REPEAT_DELAY {
-                if now.duration_since(pk.last_repeated) >= KEY_REPEAT_INTERVAL {
-                    pk.last_repeated = now;
-                    let custom_event = KeyEvent {
-                        state: ElementState::Pressed,
-                        logical_key: pk.logical_key.clone(),
-                        text: pk.text.clone(),
-                        repeat: true,
-                        ctrl: engine_state.ctrl_pressed,
-                        shift: engine_state.shift_pressed,
-                    };
-                    let mut key_rebuild = false;
-                    if let Some(msg) = engine_state.inner.handle_key_input(&custom_event, &mut key_rebuild) {
-                        let mut update_rebuild = false;
-                        engine_state.inner.update(msg, &mut update_rebuild, &mut engine_state.exit);
-                        if update_rebuild {
-                            key_rebuild = true;
-                        }
-                    }
-                    if key_rebuild {
-                        engine_state.redraw = true;
-                    }
-                }
-            }
-        }
-        let current_title = engine_state.inner.settings().title;
-        if current_title != last_title {
-            if let Some(ref window) = engine_state.window {
-                window.set_title(&current_title);
-                window.commit();
-            }
-            last_title = current_title;
-        }
-
-        let active_popovers = crate::widget::popovers::get_active();
-        if !active_popovers.is_empty() {
-            let popover_widget = unsafe { &*active_popovers[0] };
-            if let Some((px, py, pw, ph)) = popover_widget.popover_rect() {
-                if engine_state.active_popup.is_none() {
-                    let wl_surface = engine_state.compositor_state.create_surface(&engine_state.qh);
-                    wl_surface.set_buffer_scale(engine_state.scale_factor as i32);
-                    
-                    let positioner = XdgPositioner::new(&engine_state.xdg_shell_state).unwrap();
-                    positioner.set_size(pw as i32, ph as i32);
-                    
-                    let (rx, ry, rw, rh) = popover_widget.rect();
-                    positioner.set_anchor_rect(rx as i32, ry as i32, rw as i32, rh as i32);
-                    positioner.set_anchor(Anchor::BottomLeft);
-                    positioner.set_gravity(Gravity::BottomRight);
-                    positioner.set_constraint_adjustment(
-                        ConstraintAdjustment::SlideX | ConstraintAdjustment::SlideY
-                    );
-                    
-                    let parent_xdg_surface = XdgSurface::xdg_surface(engine_state.window.as_ref().unwrap());
-                    let sctk_popup = Popup::new(
-                        parent_xdg_surface,
-                        &positioner,
-                        &engine_state.qh,
-                        &engine_state.compositor_state,
-                        &engine_state.xdg_shell_state,
-                    ).unwrap();
-                    
-                    let display_ptr = conn.backend().display_id().as_ptr() as *mut std::ffi::c_void;
-                    let surface_ptr = sctk_popup.wl_surface().id().as_ptr() as *mut std::ffi::c_void;
-                    let wayland_handle = Box::leak(Box::new(WaylandSurfaceHandle {
-                        display_ptr,
-                        surface_ptr,
-                    }));
-                    let instance = engine_state.instance.as_ref().unwrap();
-                    let wgpu_surface = instance.create_surface(wayland_handle).expect("failed to create popup wgpu surface");
-                    
-                    let device = engine_state.device.as_ref().unwrap();
-                    let main_config = engine_state.config.as_ref().unwrap();
-                    
-                    let scale_f32 = engine_state.scale_factor as f32;
-                    let mut popup_config = main_config.clone();
-                    popup_config.width = (pw * scale_f32) as u32;
-                    popup_config.height = (ph * scale_f32) as u32;
-                    wgpu_surface.configure(device, &popup_config);
-                    
-                    let cache = Cache::new(device);
-                    let viewport = Viewport::new(device, &cache);
-                    
-                    engine_state.active_popup = Some(ActivePopup {
-                        sctk_popup,
-                        wgpu_surface,
-                        config: popup_config,
-                        logical_width: pw,
-                        logical_height: ph,
-                        configured: false,
-                        viewport,
-                        x: px,
-                        y: py,
-                        vertex_buffer: None,
-                    });
-                }
-            }
-        } else {
-            if engine_state.active_popup.is_some() {
-                engine_state.active_popup = None;
-                engine_state.redraw = true;
-            }
-        }
-
-        if engine_state.redraw {
-            engine_state.redraw = false;
-            if engine_state.first_configure_received {
-                engine_state.render();
-            }
-        }
-    }
-    drop(engine_state);
-}
diff --git a/src/layout.rs b/src/layout.rs
index 32f9612..f47ed8b 100644
--- a/src/layout.rs
+++ b/src/layout.rs
@@ -1,4 +1,816 @@
 use crate::widget::Element;
+use crate::context::UiContext;
+use std::sync::RwLock;
+
+fn read_config() -> Option<String> {
+    let paths = [
+        "/home/lsgalante/.config/cce/config.toml",
+        "/home/lsgalante/.config/ccec/config.toml",
+    ];
+    for path in &paths {
+        if let Ok(content) = std::fs::read_to_string(path) {
+            return Some(content);
+        }
+    }
+    None
+}
+
+pub fn parse_font_string(s: &str) -> (String, Option<f32>) {
+    let s = s.trim();
+    if let Some(last_space_idx) = s.rfind(' ') {
+        let (family, size_str) = s.split_at(last_space_idx);
+        let size_str = size_str.trim();
+        if let Ok(size) = size_str.parse::<f32>() {
+            return (family.trim().to_string(), Some(size));
+        }
+    }
+    (s.to_string(), None)
+}
+
+static SECTION_PADDING: RwLock<f32> = RwLock::new(8.0);
+static SPINBOX_HEIGHT: RwLock<f32> = RwLock::new(26.0);
+static COLOR_SELECTOR_HEIGHT: RwLock<f32> = RwLock::new(22.0);
+static TEXTBOX_HEIGHT: RwLock<f32> = RwLock::new(44.0);
+static FONT_SELECTOR_HEIGHT: RwLock<f32> = RwLock::new(44.0);
+static SLIDER_HEIGHT: RwLock<f32> = RwLock::new(28.0);
+static TOGGLE_HEIGHT: RwLock<f32> = RwLock::new(44.0);
+static COLOR_SELECTOR_FONT: RwLock<String> = RwLock::new(String::new());
+static COLOR_SELECTOR_PREVIEW_CORNER_RADIUS: RwLock<f32> = RwLock::new(4.0);
+static COLOR_SELECTOR_PREVIEW_MARGIN: RwLock<f32> = RwLock::new(0.0);
+static MENUBAR_FONT: RwLock<String> = RwLock::new(String::new());
+static MENUBAR_FONT_CACHED: RwLock<Option<(String, f32)>> = RwLock::new(None);
+static SECTION_LABEL_FONT: RwLock<String> = RwLock::new(String::new());
+static NESTED_SECTION_LABEL_FONT: RwLock<String> = RwLock::new(String::new());
+
+static PAGINATOR_TAB_MARGIN_X: RwLock<f32> = RwLock::new(5.0);
+static PAGINATOR_TAB_MARGIN_Y: RwLock<f32> = RwLock::new(10.0);
+static PAGINATOR_TAB_PADDING_X: RwLock<f32> = RwLock::new(10.0);
+static PAGINATOR_TAB_PADDING_Y: RwLock<f32> = RwLock::new(14.0);
+
+static PLATE_PADDING: RwLock<f32> = RwLock::new(20.0);
+static DROPDOWN_HEIGHT: RwLock<f32> = RwLock::new(44.0);
+static NESTED_SECTION_LABEL_ALIGNMENT: RwLock<u8> = RwLock::new(0);
+
+static LABEL_MARGIN: RwLock<f32> = RwLock::new(6.0);
+
+pub fn label_margin() -> f32 {
+    use std::sync::Once;
+    static INIT: Once = Once::new();
+    INIT.call_once(|| {
+        if let Some(content) = read_config() {
+            for line in content.lines() {
+                let trimmed = line.trim();
+                if let Some(rest) = trimmed.strip_prefix("label_margin") {
+                    let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=' || c == '"');
+                    let val_str = rest.trim_end_matches('"').trim();
+                    if let Ok(val) = val_str.parse::<f32>() {
+                        if let Ok(mut lock) = LABEL_MARGIN.write() {
+                            *lock = val;
+                        }
+                    }
+                }
+            }
+        }
+    });
+    *LABEL_MARGIN.read().unwrap()
+}
+
+pub fn set_label_margin(margin: f32) {
+    if let Ok(mut lock) = LABEL_MARGIN.write() {
+        *lock = margin;
+    }
+}
+
+pub fn nested_section_label_alignment() -> u8 {
+    use std::sync::Once;
+    static INIT: Once = Once::new();
+    INIT.call_once(|| {
+        if let Some(content) = read_config() {
+            for line in content.lines() {
+                let trimmed = line.trim();
+                if let Some(rest) = trimmed.strip_prefix("nested_section_label_alignment") {
+                    let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=' || c == '"');
+                    let val_str = rest.trim_end_matches('"').trim();
+                    if let Ok(val) = val_str.parse::<u8>() {
+                        if let Ok(mut lock) = NESTED_SECTION_LABEL_ALIGNMENT.write() {
+                            *lock = val;
+                        }
+                    }
+                }
+            }
+        }
+    });
+    *NESTED_SECTION_LABEL_ALIGNMENT.read().unwrap()
+}
+
+pub fn set_nested_section_label_alignment(align: u8) {
+    if let Ok(mut lock) = NESTED_SECTION_LABEL_ALIGNMENT.write() {
+        *lock = align;
+    }
+}
+
+static NESTED_SECTION_LABEL_OFFSET: RwLock<f32> = RwLock::new(0.0);
+
+pub fn nested_section_label_offset() -> f32 {
+    use std::sync::Once;
+    static INIT: Once = Once::new();
+    INIT.call_once(|| {
+        if let Some(content) = read_config() {
+            for line in content.lines() {
+                let trimmed = line.trim();
+                if let Some(rest) = trimmed.strip_prefix("nested_section_label_offset") {
+                    let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=' || c == '"');
+                    let val_str = rest.trim_end_matches('"').trim();
+                    if let Ok(val) = val_str.parse::<f32>() {
+                        if let Ok(mut lock) = NESTED_SECTION_LABEL_OFFSET.write() {
+                            *lock = val;
+                        }
+                    }
+                }
+            }
+        }
+    });
+    *NESTED_SECTION_LABEL_OFFSET.read().unwrap()
+}
+
+pub fn set_nested_section_label_offset(offset: f32) {
+    if let Ok(mut lock) = NESTED_SECTION_LABEL_OFFSET.write() {
+        *lock = offset;
+    }
+}
+
+
+pub fn plate_padding() -> f32 {
+    use std::sync::Once;
+    static INIT: Once = Once::new();
+    INIT.call_once(|| {
+        if let Some(content) = read_config() {
+            for line in content.lines() {
+                let trimmed = line.trim();
+                if let Some(rest) = trimmed.strip_prefix("plate_padding") {
+                    let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=' || c == '"');
+                    let val_str = rest.trim_end_matches('"').trim();
+                    if let Ok(val) = val_str.parse::<f32>() {
+                        if let Ok(mut lock) = PLATE_PADDING.write() {
+                            *lock = val;
+                        }
+                    }
+                }
+            }
+        }
+    });
+    *PLATE_PADDING.read().unwrap()
+}
+
+pub fn set_plate_padding(padding: f32) {
+    if let Ok(mut lock) = PLATE_PADDING.write() {
+        *lock = padding;
+    }
+}
+
+static PAGE_MARGIN: RwLock<f32> = RwLock::new(20.0);
+
+pub fn page_margin() -> f32 {
+    use std::sync::Once;
+    static INIT: Once = Once::new();
+    INIT.call_once(|| {
+        if let Some(content) = read_config() {
+            for line in content.lines() {
+                let trimmed = line.trim();
+                if let Some(rest) = trimmed.strip_prefix("page_margin") {
+                    let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=' || c == '"');
+                    let val_str = rest.trim_end_matches('"').trim();
+                    if let Ok(val) = val_str.parse::<f32>() {
+                        if let Ok(mut lock) = PAGE_MARGIN.write() {
+                            *lock = val;
+                        }
+                    }
+                }
+            }
+        }
+    });
+    *PAGE_MARGIN.read().unwrap()
+}
+
+pub fn set_page_margin(margin: f32) {
+    if let Ok(mut lock) = PAGE_MARGIN.write() {
+        *lock = margin;
+    }
+}
+
+static GRID_MIN_COL_WIDTH: RwLock<f32> = RwLock::new(260.0);
+
+pub fn grid_min_col_width() -> f32 {
+    use std::sync::Once;
+    static INIT: Once = Once::new();
+    INIT.call_once(|| {
+        if let Some(content) = read_config() {
+            for line in content.lines() {
+                let trimmed = line.trim();
+                if let Some(rest) = trimmed.strip_prefix("grid_min_col_width") {
+                    let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=' || c == '"');
+                    let val_str = rest.trim_end_matches('"').trim();
+                    if let Ok(val) = val_str.parse::<f32>() {
+                        if let Ok(mut lock) = GRID_MIN_COL_WIDTH.write() {
+                            *lock = val;
+                        }
+                    }
+                }
+            }
+        }
+    });
+    *GRID_MIN_COL_WIDTH.read().unwrap()
+}
+
+pub fn set_grid_min_col_width(width: f32) {
+    if let Ok(mut lock) = GRID_MIN_COL_WIDTH.write() {
+        *lock = width;
+    }
+}
+
+pub fn section_padding() -> f32 {
+    use std::sync::Once;
+    static INIT: Once = Once::new();
+    INIT.call_once(|| {
+        if let Some(content) = read_config() {
+            for line in content.lines() {
+                let trimmed = line.trim();
+                if let Some(rest) = trimmed.strip_prefix("section_padding") {
+                    let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=' || c == '"');
+                    let val_str = rest.trim_end_matches('"').trim();
+                    if let Ok(val) = val_str.parse::<f32>() {
+                        if let Ok(mut lock) = SECTION_PADDING.write() {
+                            *lock = val;
+                        }
+                    }
+                }
+            }
+        }
+    });
+    *SECTION_PADDING.read().unwrap()
+}
+
+pub fn set_section_padding(padding: f32) {
+    if let Ok(mut lock) = SECTION_PADDING.write() {
+        *lock = padding;
+    }
+}
+
+pub fn spinbox_height() -> f32 {
+    use std::sync::Once;
+    static INIT: Once = Once::new();
+    INIT.call_once(|| {
+        if let Some(content) = read_config() {
+            for line in content.lines() {
+                let trimmed = line.trim();
+                if let Some(rest) = trimmed.strip_prefix("spinbox_height") {
+                    let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=' || c == '"');
+                    let val_str = rest.trim_end_matches('"').trim();
+                    if let Ok(val) = val_str.parse::<f32>() {
+                        if let Ok(mut lock) = SPINBOX_HEIGHT.write() {
+                            *lock = val;
+                        }
+                    }
+                }
+            }
+        }
+    });
+    *SPINBOX_HEIGHT.read().unwrap()
+}
+
+pub fn set_spinbox_height(height: f32) {
+    if let Ok(mut lock) = SPINBOX_HEIGHT.write() {
+        *lock = height;
+    }
+}
+
+pub fn toggle_height() -> f32 {
+    use std::sync::Once;
+    static INIT: Once = Once::new();
+    INIT.call_once(|| {
+        if let Some(content) = read_config() {
+            for line in content.lines() {
+                let trimmed = line.trim();
+                if let Some(rest) = trimmed.strip_prefix("toggle_height") {
+                    let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=' || c == '"');
+                    let val_str = rest.trim_end_matches('"').trim();
+                    if let Ok(val) = val_str.parse::<f32>() {
+                        if let Ok(mut lock) = TOGGLE_HEIGHT.write() {
+                            *lock = val;
+                        }
+                    }
+                }
+            }
+        }
+    });
+    *TOGGLE_HEIGHT.read().unwrap()
+}
+
+pub fn set_toggle_height(height: f32) {
+    if let Ok(mut lock) = TOGGLE_HEIGHT.write() {
+        *lock = height;
+    }
+}
+
+pub fn color_selector_height() -> f32 {
+    use std::sync::Once;
+    static INIT: Once = Once::new();
+    INIT.call_once(|| {
+        if let Some(content) = read_config() {
+            for line in content.lines() {
+                let trimmed = line.trim();
+                if let Some(rest) = trimmed.strip_prefix("color_selector_height") {
+                    let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=' || c == '"');
+                    let val_str = rest.trim_end_matches('"').trim();
+                    if let Ok(val) = val_str.parse::<f32>() {
+                        if let Ok(mut lock) = COLOR_SELECTOR_HEIGHT.write() {
+                            *lock = val;
+                        }
+                    }
+                }
+            }
+        }
+    });
+    *COLOR_SELECTOR_HEIGHT.read().unwrap()
+}
+
+pub fn set_color_selector_height(height: f32) {
+    if let Ok(mut lock) = COLOR_SELECTOR_HEIGHT.write() {
+        *lock = height;
+    }
+}
+
+pub fn font_selector_height() -> f32 {
+    use std::sync::Once;
+    static INIT: Once = Once::new();
+    INIT.call_once(|| {
+        if let Some(content) = read_config() {
+            for line in content.lines() {
+                let trimmed = line.trim();
+                if let Some(rest) = trimmed.strip_prefix("font_selector_height") {
+                    let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=' || c == '"');
+                    let val_str = rest.trim_end_matches('"').trim();
+                    if let Ok(val) = val_str.parse::<f32>() {
+                        if let Ok(mut lock) = FONT_SELECTOR_HEIGHT.write() {
+                            *lock = val;
+                        }
+                    }
+                }
+            }
+        }
+    });
+    *FONT_SELECTOR_HEIGHT.read().unwrap()
+}
+
+pub fn set_font_selector_height(height: f32) {
+    if let Ok(mut lock) = FONT_SELECTOR_HEIGHT.write() {
+        *lock = height;
+    }
+}
+
+pub fn color_selector_font() -> String {
+    use std::sync::Once;
+    static INIT: Once = Once::new();
+    INIT.call_once(|| {
+        let mut font = "monospace".to_string();
+        if let Some(content) = read_config() {
+            for line in content.lines() {
+                let trimmed = line.trim();
+                if let Some(rest) = trimmed.strip_prefix("color_selector_font") {
+                    let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=');
+                    let rest = rest.trim();
+                    let val_str = if rest.starts_with('"') && rest.ends_with('"') && rest.len() >= 2 {
+                        &rest[1..rest.len() - 1]
+                    } else {
+                        rest
+                    };
+                    font = val_str.trim().to_string();
+                }
+            }
+        }
+        if let Ok(mut lock) = COLOR_SELECTOR_FONT.write() {
+            *lock = font;
+        }
+    });
+    let lock = COLOR_SELECTOR_FONT.read().unwrap();
+    if lock.is_empty() {
+        "monospace".to_string()
+    } else {
+        lock.clone()
+    }
+}
+
+pub fn set_color_selector_font(font: &str) {
+    if let Ok(mut lock) = COLOR_SELECTOR_FONT.write() {
+        *lock = font.to_string();
+    }
+}
+
+pub fn menubar_font() -> String {
+    use std::sync::Once;
+    static INIT: Once = Once::new();
+    INIT.call_once(|| {
+        let mut font = "Outfit".to_string();
+        if let Some(content) = read_config() {
+            for line in content.lines() {
+                let trimmed = line.trim();
+                if let Some(rest) = trimmed.strip_prefix("menubar_font") {
+                    let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=');
+                    let rest = rest.trim();
+                    let val_str = if rest.starts_with('"') && rest.ends_with('"') && rest.len() >= 2 {
+                        &rest[1..rest.len() - 1]
+                    } else {
+                        rest
+                    };
+                    font = val_str.trim().to_string();
+                }
+            }
+        }
+        if let Ok(mut lock) = MENUBAR_FONT.write() {
+            *lock = font;
+        }
+    });
+    let lock = MENUBAR_FONT.read().unwrap();
+    if lock.is_empty() {
+        "Outfit".to_string()
+    } else {
+        lock.clone()
+    }
+}
+
+pub fn menubar_font_parsed() -> (String, f32) {
+    if let Ok(lock) = MENUBAR_FONT_CACHED.read() {
+        if let Some(ref val) = *lock {
+            return val.clone();
+        }
+    }
+    let font_str = menubar_font();
+    let parsed = parse_font_string(&font_str);
+    let size = parsed.1.unwrap_or(12.0);
+    let val = (parsed.0, size);
+    if let Ok(mut lock) = MENUBAR_FONT_CACHED.write() {
+        *lock = Some(val.clone());
+    }
+    val
+}
+
+pub fn set_menubar_font(font: &str) {
+    if let Ok(mut lock) = MENUBAR_FONT.write() {
+        *lock = font.to_string();
+    }
+    if let Ok(mut lock) = MENUBAR_FONT_CACHED.write() {
+        *lock = None;
+    }
+}
+
+pub fn section_label_font() -> String {
+    use std::sync::Once;
+    static INIT: Once = Once::new();
+    INIT.call_once(|| {
+        let mut font = "Outfit".to_string();
+        if let Some(content) = read_config() {
+            for line in content.lines() {
+                let trimmed = line.trim();
+                if let Some(rest) = trimmed.strip_prefix("section_label_font") {
+                    let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=');
+                    let rest = rest.trim();
+                    let val_str = if rest.starts_with('"') && rest.ends_with('"') && rest.len() >= 2 {
+                        &rest[1..rest.len() - 1]
+                    } else {
+                        rest
+                    };
+                    font = val_str.trim().to_string();
+                }
+            }
+        }
+        if let Ok(mut lock) = SECTION_LABEL_FONT.write() {
+            *lock = font;
+        }
+    });
+    let lock = SECTION_LABEL_FONT.read().unwrap();
+    if lock.is_empty() {
+        "Outfit".to_string()
+    } else {
+        lock.clone()
+    }
+}
+
+pub fn set_section_label_font(font: &str) {
+    if let Ok(mut lock) = SECTION_LABEL_FONT.write() {
+        *lock = font.to_string();
+    }
+}
+
+pub fn nested_section_label_font() -> String {
+    use std::sync::Once;
+    static INIT: Once = Once::new();
+    INIT.call_once(|| {
+        let mut font = "Outfit".to_string();
+        if let Some(content) = read_config() {
+            for line in content.lines() {
+                let trimmed = line.trim();
+                if let Some(rest) = trimmed.strip_prefix("nested_section_label_font") {
+                    let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=');
+                    let rest = rest.trim();
+                    let val_str = if rest.starts_with('"') && rest.ends_with('"') && rest.len() >= 2 {
+                        &rest[1..rest.len() - 1]
+                    } else {
+                        rest
+                    };
+                    font = val_str.trim().to_string();
+                }
+            }
+        }
+        if let Ok(mut lock) = NESTED_SECTION_LABEL_FONT.write() {
+            *lock = font;
+        }
+    });
+    let lock = NESTED_SECTION_LABEL_FONT.read().unwrap();
+    if lock.is_empty() {
+        "Outfit".to_string()
+    } else {
+        lock.clone()
+    }
+}
+
+pub fn set_nested_section_label_font(font: &str) {
+    if let Ok(mut lock) = NESTED_SECTION_LABEL_FONT.write() {
+        *lock = font.to_string();
+    }
+}
+
+pub fn color_selector_preview_corner_radius() -> f32 {
+    use std::sync::Once;
+    static INIT: Once = Once::new();
+    INIT.call_once(|| {
+        if let Some(content) = read_config() {
+            for line in content.lines() {
+                let trimmed = line.trim();
+                if let Some(rest) = trimmed.strip_prefix("color_selector_preview_corner_radius") {
+                    let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=' || c == '"');
+                    let val_str = rest.trim_end_matches('"').trim();
+                    if let Ok(val) = val_str.parse::<f32>() {
+                        if let Ok(mut lock) = COLOR_SELECTOR_PREVIEW_CORNER_RADIUS.write() {
+                            *lock = val;
+                        }
+                    }
+                }
+            }
+        }
+    });
+    *COLOR_SELECTOR_PREVIEW_CORNER_RADIUS.read().unwrap()
+}
+
+pub fn set_color_selector_preview_corner_radius(radius: f32) {
+    if let Ok(mut lock) = COLOR_SELECTOR_PREVIEW_CORNER_RADIUS.write() {
+        *lock = radius;
+    }
+}
+
+pub fn color_selector_preview_margin() -> f32 {
+    use std::sync::Once;
+    static INIT: Once = Once::new();
+    INIT.call_once(|| {
+        if let Some(content) = read_config() {
+            for line in content.lines() {
+                let trimmed = line.trim();
+                if let Some(rest) = trimmed.strip_prefix("color_selector_preview_margin") {
+                    let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=' || c == '"');
+                    let val_str = rest.trim_end_matches('"').trim();
+                    if let Ok(val) = val_str.parse::<f32>() {
+                        if let Ok(mut lock) = COLOR_SELECTOR_PREVIEW_MARGIN.write() {
+                            *lock = val;
+                        }
+                    }
+                }
+            }
+        }
+    });
+    *COLOR_SELECTOR_PREVIEW_MARGIN.read().unwrap()
+}
+
+pub fn set_color_selector_preview_margin(margin: f32) {
+    if let Ok(mut lock) = COLOR_SELECTOR_PREVIEW_MARGIN.write() {
+        *lock = margin;
+    }
+}
+
+pub fn paginator_tab_margin_x() -> f32 {
+    use std::sync::Once;
+    static INIT: Once = Once::new();
+    INIT.call_once(|| {
+        if let Some(content) = read_config() {
+            let mut general_margin = None;
+            let mut x_margin = None;
+            for line in content.lines() {
+                let trimmed = line.trim();
+                if let Some(rest) = trimmed.strip_prefix("paginator_tab_margin_x") {
+                    let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=' || c == '"');
+                    let val_str = rest.trim_end_matches('"').trim();
+                    if let Ok(val) = val_str.parse::<f32>() {
+                        x_margin = Some(val);
+                    }
+                } else if let Some(rest) = trimmed.strip_prefix("paginator_tab_margin") {
+                    let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=' || c == '"');
+                    let val_str = rest.trim_end_matches('"').trim();
+                    if let Ok(val) = val_str.parse::<f32>() {
+                        general_margin = Some(val);
+                    }
+                }
+            }
+            let val = x_margin.or(general_margin).unwrap_or(5.0);
+            if let Ok(mut lock) = PAGINATOR_TAB_MARGIN_X.write() {
+                *lock = val;
+            }
+        }
+    });
+    *PAGINATOR_TAB_MARGIN_X.read().unwrap()
+}
+
+pub fn set_paginator_tab_margin_x(margin: f32) {
+    if let Ok(mut lock) = PAGINATOR_TAB_MARGIN_X.write() {
+        *lock = margin;
+    }
+}
+
+pub fn paginator_tab_margin_y() -> f32 {
+    use std::sync::Once;
+    static INIT: Once = Once::new();
+    INIT.call_once(|| {
+        if let Some(content) = read_config() {
+            let mut general_margin = None;
+            let mut y_margin = None;
+            for line in content.lines() {
+                let trimmed = line.trim();
+                if let Some(rest) = trimmed.strip_prefix("paginator_tab_margin_y") {
+                    let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=' || c == '"');
+                    let val_str = rest.trim_end_matches('"').trim();
+                    if let Ok(val) = val_str.parse::<f32>() {
+                        y_margin = Some(val);
+                    }
+                } else if let Some(rest) = trimmed.strip_prefix("paginator_tab_margin") {
+                    let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=' || c == '"');
+                    let val_str = rest.trim_end_matches('"').trim();
+                    if let Ok(val) = val_str.parse::<f32>() {
+                        general_margin = Some(val);
+                    }
+                }
+            }
+            let val = y_margin.or(general_margin).unwrap_or(10.0);
+            if let Ok(mut lock) = PAGINATOR_TAB_MARGIN_Y.write() {
+                *lock = val;
+            }
+        }
+    });
+    *PAGINATOR_TAB_MARGIN_Y.read().unwrap()
+}
+
+pub fn set_paginator_tab_margin_y(margin: f32) {
+    if let Ok(mut lock) = PAGINATOR_TAB_MARGIN_Y.write() {
+        *lock = margin;
+    }
+}
+
+pub fn paginator_tab_padding_x() -> f32 {
+    use std::sync::Once;
+    static INIT: Once = Once::new();
+    INIT.call_once(|| {
+        if let Some(content) = read_config() {
+            for line in content.lines() {
+                let trimmed = line.trim();
+                if let Some(rest) = trimmed.strip_prefix("paginator_tab_padding_x") {
+                    let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=' || c == '"');
+                    let val_str = rest.trim_end_matches('"').trim();
+                    if let Ok(val) = val_str.parse::<f32>() {
+                        if let Ok(mut lock) = PAGINATOR_TAB_PADDING_X.write() {
+                            *lock = val;
+                        }
+                    }
+                }
+            }
+        }
+    });
+    *PAGINATOR_TAB_PADDING_X.read().unwrap()
+}
+
+pub fn set_paginator_tab_padding_x(padding: f32) {
+    if let Ok(mut lock) = PAGINATOR_TAB_PADDING_X.write() {
+        *lock = padding;
+    }
+}
+
+pub fn paginator_tab_padding_y() -> f32 {
+    use std::sync::Once;
+    static INIT: Once = Once::new();
+    INIT.call_once(|| {
+        if let Some(content) = read_config() {
+            for line in content.lines() {
+                let trimmed = line.trim();
+                if let Some(rest) = trimmed.strip_prefix("paginator_tab_padding_y") {
+                    let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=' || c == '"');
+                    let val_str = rest.trim_end_matches('"').trim();
+                    if let Ok(val) = val_str.parse::<f32>() {
+                        if let Ok(mut lock) = PAGINATOR_TAB_PADDING_Y.write() {
+                            *lock = val;
+                        }
+                    }
+                }
+            }
+        }
+    });
+    *PAGINATOR_TAB_PADDING_Y.read().unwrap()
+}
+
+pub fn set_paginator_tab_padding_y(padding: f32) {
+    if let Ok(mut lock) = PAGINATOR_TAB_PADDING_Y.write() {
+        *lock = padding;
+    }
+}
+
+pub fn textbox_height() -> f32 {
+    use std::sync::Once;
+    static INIT: Once = Once::new();
+    INIT.call_once(|| {
+        if let Some(content) = read_config() {
+            for line in content.lines() {
+                let trimmed = line.trim();
+                if let Some(rest) = trimmed.strip_prefix("textbox_height") {
+                    let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=' || c == '"');
+                    let val_str = rest.trim_end_matches('"').trim();
+                    if let Ok(val) = val_str.parse::<f32>() {
+                        if let Ok(mut lock) = TEXTBOX_HEIGHT.write() {
+                            *lock = val;
+                        }
+                    }
+                }
+            }
+        }
+    });
+    *TEXTBOX_HEIGHT.read().unwrap()
+}
+
+pub fn set_textbox_height(height: f32) {
+    if let Ok(mut lock) = TEXTBOX_HEIGHT.write() {
+        *lock = height;
+    }
+}
+
+pub fn dropdown_height() -> f32 {
+    use std::sync::Once;
+    static INIT: Once = Once::new();
+    INIT.call_once(|| {
+        if let Some(content) = read_config() {
+            for line in content.lines() {
+                let trimmed = line.trim();
+                if let Some(rest) = trimmed.strip_prefix("dropdown_height") {
+                    let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=' || c == '"');
+                    let val_str = rest.trim_end_matches('"').trim();
+                    if let Ok(val) = val_str.parse::<f32>() {
+                        if let Ok(mut lock) = DROPDOWN_HEIGHT.write() {
+                            *lock = val;
+                        }
+                    }
+                }
+            }
+        }
+    });
+    *DROPDOWN_HEIGHT.read().unwrap()
+}
+
+pub fn set_dropdown_height(height: f32) {
+    if let Ok(mut lock) = DROPDOWN_HEIGHT.write() {
+        *lock = height;
+    }
+}
+
+pub fn slider_height() -> f32 {
+    use std::sync::Once;
+    static INIT: Once = Once::new();
+    INIT.call_once(|| {
+        if let Some(content) = read_config() {
+            for line in content.lines() {
+                let trimmed = line.trim();
+                if let Some(rest) = trimmed.strip_prefix("slider_height") {
+                    let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=' || c == '"');
+                    let val_str = rest.trim_end_matches('"').trim();
+                    if let Ok(val) = val_str.parse::<f32>() {
+                        if let Ok(mut lock) = SLIDER_HEIGHT.write() {
+                            *lock = val;
+                        }
+                    }
+                }
+            }
+        }
+    });
+    *SLIDER_HEIGHT.read().unwrap()
+}
+
+pub fn set_slider_height(height: f32) {
+    if let Ok(mut lock) = SLIDER_HEIGHT.write() {
+        *lock = height;
+    }
+}
+
 
 pub trait RenderTarget {
     fn rect(&mut self, color: [f32; 4], x: f32, y: f32, w: f32, h: f32);
@@ -47,13 +859,13 @@ impl RenderTarget for PopoverCollector {
     }
 }
 
-pub fn render_widget<T: Element + 'static>(pc: &mut dyn RenderTarget, w: &mut T, x: f32, y: f32, ww: f32, wh: f32) {
-    w.set_rect(x, y, ww, wh);
-    for (qx, qy, qw, qh, qc) in w.all_quads() {
+pub fn render_widget<T: Element + 'static>(pc: &mut dyn RenderTarget, w: &mut T, x: f32, y: f32, ww: f32, wh: f32, ctx: &mut UiContext) {
+    w.layout(crate::widget::Point { x, y }, crate::widget::LayoutConstraints::new(ww, ww, wh, wh), ctx);
+    for (qx, qy, qw, qh, qc) in w.all_quads(ctx) {
         pc.rect(qc, qx, qy, qw, qh);
     }
     let font_opt = w.widget_font();
-    for (label, bounds) in w.text_labels_with_bounds() {
+    for (label, bounds) in w.text_labels_with_bounds(ctx) {
         let color_f32 = [
             label.color[0] as f32 / 255.0,
             label.color[1] as f32 / 255.0,
@@ -67,14 +879,14 @@ pub fn render_widget<T: Element + 'static>(pc: &mut dyn RenderTarget, w: &mut T,
         }
     }
     if w.popover_rect().is_some() {
-        crate::widget::popovers::register(w);
+        ctx.register_popover(w);
     }
 }
 
-pub fn render_popovers(pc: &mut dyn RenderTarget) {
-    for popover_ptr in crate::widget::popovers::get_active() {
+pub fn render_popovers(pc: &mut dyn RenderTarget, ctx: &UiContext) {
+    for popover_ptr in &ctx.active_popovers {
         unsafe {
-            (*popover_ptr).render_popover(pc);
+            (**popover_ptr).render_popover(pc);
         }
     }
 }
@@ -146,253 +958,86 @@ impl Column {
         self.y += 22.0;
     }
 
-    pub fn text(&mut self, pc: &mut dyn RenderTarget, text: &str, x_off: f32, y_off: f32, font_size: f32, color: [f32; 4]) {
-        let x = self.ax(x_off);
-        let y = self.ay() + y_off;
-        pc.text(text, x, y, font_size, color);
-    }
-
-    pub fn widget<T: Element + 'static>(&mut self, pc: &mut dyn RenderTarget, w: &mut T, x_off: f32, ww: f32, wh: f32) {
-        let top_room = crate::widget::label_offset(w);
-        let total_h = wh + top_room;
-        let x = self.ax(x_off);
-        let y = self.ay();
-        w.set_row_rect(self.ox + self.cx + 8.0, self.cw - 16.0);
-        let clamped_w = ww.min((self.cw - x_off).max(0.0));
-        render_widget(pc, w, x, y, clamped_w, total_h);
-        self.y += total_h;
-    }
-
-    pub fn row<F: FnOnce(&mut Row)>(&mut self, pc: &mut dyn RenderTarget, h: f32, f: F) {
-        let row_y = self.ay();
-        let mut row = Row {
-            pc: &mut *pc,
-            base_x: self.ox + self.cx,
-            y: row_y,
-            cursor_x: 0.0,
-            spacing: 8.0,
-        };
-        f(&mut row);
-        self.y = self.y + h;
-    }
-}
-
-pub struct Row<'a> {
-    pc: &'a mut dyn RenderTarget,
-    base_x: f32,
-    y: f32,
-    pub cursor_x: f32,
-    pub spacing: f32,
-}
-
-impl<'a> Row<'a> {
-    pub fn set_spacing(&mut self, spacing: f32) {
-        self.spacing = spacing;
-    }
-
-    pub fn gap(&mut self, width: f32) {
-        self.cursor_x += width;
-    }
-
-    pub fn text(&mut self, text: &str, y_off: f32, font_size: f32, color: [f32; 4], width: f32) {
-        self.pc
-            .text(text, self.base_x + self.cursor_x, self.y + y_off, font_size, color);
-        self.cursor_x += width + self.spacing;
-    }
-
-    pub fn widget<T: Element + 'static>(&mut self, w: &mut T, ww: f32, wh: f32) {
-        render_widget(self.pc, w, self.base_x + self.cursor_x, self.y, ww, wh);
-        self.cursor_x += ww + self.spacing;
-    }
-}
-
-pub struct Section {
-    left: f32,
-    top: f32,
-    pub content_y: f32,
-    pub cw: f32,
-    label_width: f32,
-}
-
-impl Section {
-    pub const ROW_PADDING_X: f32 = 8.0;
-    pub const DEFAULT_MARGIN_X: f32 = 12.0;
-    pub const DEFAULT_ROW_GAP: f32 = 8.0;
-
-    fn estimate_label_width(label: &str) -> f32 {
-        let mut width = 0.0;
-        for c in label.chars() {
-            let factor = match c {
-                'i' | 'l' | 't' | 'j' | 'f' | 'I' | ' ' | '.' | ',' | '!' | ';' | ':' | '\'' | '"' | '(' | ')' | '[' | ']' | '-' => 0.28,
-                'r' | 's' | 'J' | 'c' | 'z' => 0.42,
-                'm' | 'w' | 'M' | 'W' | '&' | '@' => 0.80,
-                'A'..='Z' => 0.68,
-                _ => 0.55,
-            };
-            width += factor * 14.0;
-        }
-        width
-    }
-
-    pub fn new(pc: &mut dyn RenderTarget, left: f32, top: f32, cw: f32, label: &str) -> Self {
-        let label_width = Self::estimate_label_width(label);
-        let label_x = left + (cw - label_width) / 2.0;
-        pc.text(label, label_x, top, 14.0, [0.83, 0.83, 0.83, 1.0]);
-        Self { left, top, content_y: top + 19.0, cw, label_width }
-    }
-
-    pub fn ax(&self, x_off: f32) -> f32 {
-        let shift = if x_off >= 12.0 { 8.0 } else { 0.0 };
-        self.left + x_off + shift
-    }
-
-    pub fn ay(&self) -> f32 { self.content_y }
-
-    pub fn spacing(&mut self, dy: f32) { self.content_y += dy; }
-
-    pub fn text(&mut self, pc: &mut dyn RenderTarget, text: &str, x_off: f32, y_off: f32, font_size: f32, color: [f32; 4]) {
-        pc.text(text, self.ax(x_off), self.ay() + y_off, font_size, color);
-    }
-
-    pub fn widget<T: Element + 'static>(&mut self, pc: &mut dyn RenderTarget, w: &mut T, x_off: f32, ww: f32, wh: f32) {
-        w.set_row_rect(self.left + Self::ROW_PADDING_X, self.cw - 2.0 * Self::ROW_PADDING_X);
-        let x = self.ax(x_off);
-        let right_edge = self.left + self.cw - Self::ROW_PADDING_X;
-        let clamped_w = ww.min((right_edge - x).max(0.0));
-        let top_room = crate::widget::label_offset(w);
-        let total_h = wh + top_room;
-        render_widget(pc, w, x, self.ay(), clamped_w, total_h);
-        self.content_y += total_h;
-    }
-
-    pub fn widget_full<T: Element + 'static>(&mut self, pc: &mut dyn RenderTarget, w: &mut T, wh: f32) {
-        let x_off = 12.0;
-        let ww = self.cw - 2.0 * (Self::ROW_PADDING_X + x_off); // cw - 40.0
-        self.widget(pc, w, x_off, ww, wh);
-    }
-
-    pub fn separator(&mut self, pc: &mut dyn RenderTarget) {
-        let x = self.ax(Self::ROW_PADDING_X);
-        let y = self.ay();
-        pc.rect([0.18, 0.18, 0.27, 1.0], x, y, self.cw - 2.0 * Self::ROW_PADDING_X, 1.0);
-        self.content_y += 8.0;
-    }
-
-    pub fn rect(&mut self, pc: &mut dyn RenderTarget, color: [f32; 4], x_off: f32, w: f32, h: f32) {
-        pc.rect(color, self.ax(x_off), self.ay(), w, h);
-        self.content_y += h;
-    }
-
-    pub fn row_layout(&self, count: usize, gap: f32) -> Vec<(f32, f32)> {
-        let margin_x = Self::ROW_PADDING_X + 12.0; // 20.0 px (12.0 px inner padding)
-        let usable_w = self.cw - 2.0 * margin_x; // padding on left and right inside outer bounds
-        if count == 0 {
-            return Vec::new();
-        }
-        let total_gap = gap * (count - 1) as f32;
-        let col_w = (usable_w - total_gap).max(0.0) / count as f32;
-
-        let mut cols = Vec::with_capacity(count);
-        for i in 0..count {
-            let x = self.left + margin_x + i as f32 * (col_w + gap);
-            cols.push((x, col_w));
-        }
-        cols
-    }
-
-    pub fn row<F>(&mut self, count: usize, gap: f32, h: f32, mut f: F)
-    where
-        F: FnMut(usize, f32, f32),
-    {
-        let cols = self.row_layout(count, gap);
-        for (i, &(x, w)) in cols.iter().enumerate() {
-            f(i, x, w);
-        }
-        self.content_y += h;
-    }
-
-    pub fn finish(&mut self, pc: &mut dyn RenderTarget) -> f32 {
-        self.finish_focused(pc, false)
-    }
-
-    pub fn finish_focused(&mut self, pc: &mut dyn RenderTarget, focused: bool) -> f32 {
-        let border: [f32; 4] = if focused {
-            [0.30, 0.50, 0.32, 1.0] // Focused green
-        } else {
-            [0.25, 0.25, 0.35, 1.0] // Default gray
-        };
-        let x = self.left + Self::ROW_PADDING_X;
-        let y = self.top + 7.0;
-        let w = self.cw - 2.0 * Self::ROW_PADDING_X;
-        let h = self.content_y - y;
-        
-        let left_edge = x;
-        let right_edge = x + w;
-        if self.label_width > 0.0 {
-            let label_x = self.left + (self.cw - self.label_width) / 2.0;
-            let gap_margin = 6.0;
-            let gap_start = label_x - gap_margin;
-            let gap_end = label_x + self.label_width + gap_margin;
-            if gap_start > left_edge {
-                pc.rect(border, left_edge, y, gap_start - left_edge, 1.0);
-            }
-            if right_edge > gap_end {
-                pc.rect(border, gap_end, y, right_edge - gap_end, 1.0);
-            }
-        } else {
-            pc.rect(border, left_edge, y, w, 1.0);
-        }
-
-        pc.rect(border, x, y + h + 12.0, w, 1.0);
-        pc.rect(border, x, y, 1.0, h + 12.0);
-        pc.rect(border, x + w - 1.0, y, 1.0, h + 12.0);
-        self.content_y + 20.0
+    pub fn text(&mut self, pc: &mut dyn RenderTarget, text: &str, x_off: f32, y_off: f32, font_size: f32, color: [f32; 4]) {
+        let x = self.ax(x_off);
+        let y = self.ay() + y_off;
+        pc.text(text, x, y, font_size, color);
     }
 
-    pub fn vstack<'a>(&'a mut self, pc: &'a mut dyn RenderTarget, spacing: f32) -> SectionVStack<'a> {
-        SectionVStack {
-            section: self,
-            pc,
-            spacing,
+    pub fn widget<T: Element + 'static>(&mut self, pc: &mut dyn RenderTarget, w: &mut T, x_off: f32, ww: f32, mut wh: f32, ctx: &mut UiContext) {
+        if let Some(pref) = w.preferred_height() {
+            wh = pref;
         }
+        let top_room = crate::widget::label_offset(w);
+        let total_h = wh + top_room;
+        let x = self.ax(x_off);
+        let y = self.ay();
+        w.set_row_rect(self.ox + self.cx + 8.0, self.cw - 16.0);
+        let clamped_w = ww.min((self.cw - x_off).max(0.0));
+        render_widget(pc, w, x, y, clamped_w, total_h, ctx);
+        self.y += total_h;
+    }
+
+    pub fn row<F: FnOnce(&mut Row)>(&mut self, pc: &mut dyn RenderTarget, h: f32, f: F) {
+        let row_y = self.ay();
+        let mut row = Row {
+            pc: &mut *pc,
+            base_x: self.ox + self.cx,
+            y: row_y,
+            cursor_x: 0.0,
+            spacing: 8.0,
+        };
+        f(&mut row);
+        self.y = self.y + h;
     }
 }
 
-pub struct SectionVStack<'a> {
-    section: &'a mut Section,
+pub struct Row<'a> {
     pc: &'a mut dyn RenderTarget,
-    spacing: f32,
+    base_x: f32,
+    y: f32,
+    pub cursor_x: f32,
+    pub spacing: f32,
 }
 
-impl<'a> SectionVStack<'a> {
-    pub fn add_widget<T: Element + 'static>(&mut self, w: &mut T, ww: f32, wh: f32) {
-        self.section.widget(self.pc, w, Section::DEFAULT_MARGIN_X, ww, wh);
-        self.section.spacing(self.spacing);
+impl<'a> Row<'a> {
+    pub fn set_spacing(&mut self, spacing: f32) {
+        self.spacing = spacing;
     }
 
-    pub fn add_row<F>(&mut self, count: usize, gap: f32, h: f32, f: F)
-    where
-        F: FnMut(usize, f32, f32),
-    {
-        self.section.row(count, gap, h, f);
-        self.section.spacing(self.spacing);
+    pub fn gap(&mut self, width: f32) {
+        self.cursor_x += width;
+    }
+
+    pub fn text(&mut self, text: &str, y_off: f32, font_size: f32, color: [f32; 4], width: f32) {
+        self.pc
+            .text(text, self.base_x + self.cursor_x, self.y + y_off, font_size, color);
+        self.cursor_x += width + self.spacing;
+    }
+
+    pub fn widget<T: Element + 'static>(&mut self, w: &mut T, ww: f32, mut wh: f32, ctx: &mut UiContext) {
+        if let Some(pref) = w.preferred_height() {
+            wh = pref;
+        }
+        render_widget(self.pc, w, self.base_x + self.cursor_x, self.y, ww, wh, ctx);
+        self.cursor_x += ww + self.spacing;
     }
 }
 
-pub struct Subsection {
-    left: f32,
-    top: f32,
+pub struct Section {
+    pub left: f32,
+    pub top: f32,
     pub content_y: f32,
     pub cw: f32,
-    label_width: f32,
+    pub label_width: f32,
+    pub is_child: bool,
 }
 
-impl Subsection {
-    pub const ROW_PADDING_X: f32 = 8.0;
+impl Section {
     pub const DEFAULT_MARGIN_X: f32 = 12.0;
     pub const DEFAULT_ROW_GAP: f32 = 8.0;
 
-    fn estimate_label_width(label: &str) -> f32 {
+    fn estimate_label_width(label: &str, font_size: f32) -> f32 {
         let mut width = 0.0;
         for c in label.chars() {
             let factor = match c {
@@ -402,20 +1047,50 @@ impl Subsection {
                 'A'..='Z' => 0.68,
                 _ => 0.55,
             };
-            width += factor * 12.0;
+            width += factor * font_size;
         }
         width
     }
 
+    pub fn padding(&self) -> f32 {
+        if self.is_child {
+            section_padding().max(8.0)
+        } else {
+            section_padding()
+        }
+    }
+
     pub fn new(pc: &mut dyn RenderTarget, left: f32, top: f32, cw: f32, label: &str) -> Self {
-        let label_width = Self::estimate_label_width(label);
-        let label_x = left + (cw - label_width) / 2.0;
-        pc.text(label, label_x, top, 12.0, [0.53, 0.53, 0.60, 1.0]);
-        Self { left, top, content_y: top + 17.0, cw, label_width }
+        Self::new_opt(pc, left, top, cw, label, false)
+    }
+
+    pub fn new_opt(pc: &mut dyn RenderTarget, left: f32, top: f32, cw: f32, label: &str, is_child: bool) -> Self {
+        let font_setting = if is_child {
+            nested_section_label_font()
+        } else {
+            section_label_font()
+        };
+        let (font_fam, font_size_opt) = parse_font_string(&font_setting);
+        let font_size = font_size_opt.unwrap_or(if is_child { 12.0 } else { 14.0 });
+        let font_color = if is_child { [0.53, 0.53, 0.60, 1.0] } else { [0.83, 0.83, 0.83, 1.0] };
+        let label_width = Self::estimate_label_width(label, font_size);
+        let label_x = if is_child {
+            let base_x = match nested_section_label_alignment() {
+                0 => left + 12.0,
+                1 => left + (cw - label_width) / 2.0,
+                2 => left + cw - 12.0 - label_width,
+                _ => left + 12.0,
+            };
+            base_x + nested_section_label_offset()
+        } else {
+            left + (cw - label_width) / 2.0
+        };
+        pc.text_with_font(label, label_x, top, font_size, font_color, &font_fam);
+        Self { left, top, content_y: top + font_size + 5.0, cw, label_width, is_child }
     }
 
     pub fn ax(&self, x_off: f32) -> f32 {
-        let shift = if x_off >= 12.0 { 8.0 } else { 0.0 };
+        let shift = if x_off >= 12.0 { self.padding() } else { 0.0 };
         self.left + x_off + shift
     }
 
@@ -427,27 +1102,32 @@ impl Subsection {
         pc.text(text, self.ax(x_off), self.ay() + y_off, font_size, color);
     }
 
-    pub fn widget<T: Element + 'static>(&mut self, pc: &mut dyn RenderTarget, w: &mut T, x_off: f32, ww: f32, wh: f32) {
-        w.set_row_rect(self.left + Self::ROW_PADDING_X, self.cw - 2.0 * Self::ROW_PADDING_X);
+    pub fn widget<T: Element + 'static>(&mut self, pc: &mut dyn RenderTarget, w: &mut T, x_off: f32, ww: f32, mut wh: f32, ctx: &mut UiContext) {
+        if let Some(pref) = w.preferred_height() {
+            wh = pref;
+        }
+        let pad = self.padding();
+        w.set_row_rect(self.left + pad, self.cw - 2.0 * pad);
         let x = self.ax(x_off);
-        let right_edge = self.left + self.cw - Self::ROW_PADDING_X;
+        let right_edge = self.left + self.cw - pad;
         let clamped_w = ww.min((right_edge - x).max(0.0));
         let top_room = crate::widget::label_offset(w);
         let total_h = wh + top_room;
-        render_widget(pc, w, x, self.ay(), clamped_w, total_h);
+        render_widget(pc, w, x, self.ay(), clamped_w, total_h, ctx);
         self.content_y += total_h;
     }
 
-    pub fn widget_full<T: Element + 'static>(&mut self, pc: &mut dyn RenderTarget, w: &mut T, wh: f32) {
+    pub fn widget_full<T: Element + 'static>(&mut self, pc: &mut dyn RenderTarget, w: &mut T, wh: f32, ctx: &mut UiContext) {
         let x_off = 12.0;
-        let ww = self.cw - 2.0 * (Self::ROW_PADDING_X + x_off);
-        self.widget(pc, w, x_off, ww, wh);
+        let ww = self.cw - 2.0 * (self.padding() + x_off);
+        self.widget(pc, w, x_off, ww, wh, ctx);
     }
 
     pub fn separator(&mut self, pc: &mut dyn RenderTarget) {
-        let x = self.ax(Self::ROW_PADDING_X);
+        let pad = self.padding();
+        let x = self.ax(pad);
         let y = self.ay();
-        pc.rect([0.15, 0.15, 0.22, 1.0], x, y, self.cw - 2.0 * Self::ROW_PADDING_X, 1.0);
+        pc.rect([0.18, 0.18, 0.27, 1.0], x, y, self.cw - 2.0 * pad, 1.0);
         self.content_y += 8.0;
     }
 
@@ -457,7 +1137,7 @@ impl Subsection {
     }
 
     pub fn row_layout(&self, count: usize, gap: f32) -> Vec<(f32, f32)> {
-        let margin_x = Self::ROW_PADDING_X + 12.0;
+        let margin_x = self.padding() + 12.0;
         let usable_w = self.cw - 2.0 * margin_x;
         if count == 0 {
             return Vec::new();
@@ -489,20 +1169,39 @@ impl Subsection {
     }
 
     pub fn finish_focused(&mut self, pc: &mut dyn RenderTarget, focused: bool) -> f32 {
-        let border: [f32; 4] = if focused {
-            [0.22, 0.38, 0.24, 1.0]
+        let border: [f32; 4] = if self.is_child {
+            if focused {
+                [0.22, 0.38, 0.24, 1.0]
+            } else {
+                [0.18, 0.18, 0.25, 1.0]
+            }
         } else {
-            [0.18, 0.18, 0.25, 1.0]
+            if focused {
+                [0.30, 0.50, 0.32, 1.0]
+            } else {
+                [0.25, 0.25, 0.35, 1.0]
+            }
         };
-        let x = self.left + Self::ROW_PADDING_X;
+        let pad = self.padding();
+        let x = self.left + pad;
         let y = self.top + 7.0;
-        let w = self.cw - 2.0 * Self::ROW_PADDING_X;
+        let w = self.cw - 2.0 * pad;
         let h = self.content_y - y;
         
         let left_edge = x;
         let right_edge = x + w;
         if self.label_width > 0.0 {
-            let label_x = self.left + (self.cw - self.label_width) / 2.0;
+            let label_x = if self.is_child {
+                let base_x = match nested_section_label_alignment() {
+                    0 => self.left + 12.0,
+                    1 => self.left + (self.cw - self.label_width) / 2.0,
+                    2 => self.left + self.cw - 12.0 - self.label_width,
+                    _ => self.left + 12.0,
+                };
+                base_x + nested_section_label_offset()
+            } else {
+                self.left + (self.cw - self.label_width) / 2.0
+            };
             let gap_margin = 6.0;
             let gap_start = label_x - gap_margin;
             let gap_end = label_x + self.label_width + gap_margin;
@@ -522,33 +1221,33 @@ impl Subsection {
         self.content_y + 20.0
     }
 
-    pub fn vstack<'a>(&'a mut self, pc: &'a mut dyn RenderTarget, spacing: f32) -> SubsectionVStack<'a> {
-        SubsectionVStack {
-            subsection: self,
+    pub fn vstack<'a>(&'a mut self, pc: &'a mut dyn RenderTarget, spacing: f32) -> SectionVStack<'a> {
+        SectionVStack {
+            section: self,
             pc,
             spacing,
         }
     }
 }
 
-pub struct SubsectionVStack<'a> {
-    subsection: &'a mut Subsection,
+pub struct SectionVStack<'a> {
+    section: &'a mut Section,
     pc: &'a mut dyn RenderTarget,
     spacing: f32,
 }
 
-impl<'a> SubsectionVStack<'a> {
-    pub fn add_widget<T: Element + 'static>(&mut self, w: &mut T, ww: f32, wh: f32) {
-        self.subsection.widget(self.pc, w, Subsection::DEFAULT_MARGIN_X, ww, wh);
-        self.subsection.spacing(self.spacing);
+impl<'a> SectionVStack<'a> {
+    pub fn add_widget<T: Element + 'static>(&mut self, w: &mut T, ww: f32, wh: f32, ctx: &mut UiContext) {
+        self.section.widget(self.pc, w, Section::DEFAULT_MARGIN_X, ww, wh, ctx);
+        self.section.spacing(self.spacing);
     }
 
     pub fn add_row<F>(&mut self, count: usize, gap: f32, h: f32, f: F)
     where
         F: FnMut(usize, f32, f32),
     {
-        self.subsection.row(count, gap, h, f);
-        self.subsection.spacing(self.spacing);
+        self.section.row(count, gap, h, f);
+        self.section.spacing(self.spacing);
     }
 }
 
@@ -620,14 +1319,12 @@ pub struct Grid {
 impl Grid {
     pub fn new(left: f32, top: f32, width: f32, min_col_width: f32, gap: f32, count: usize) -> Self {
         let total_gap = gap * (count - 1) as f32;
-        let total_grid_width = count as f32 * min_col_width + total_gap;
-        let left_offset = if total_grid_width < width {
-            (width - total_grid_width) / 2.0
+        let col_width = if count > 0 {
+            (width - total_gap).max(0.0) / count as f32
         } else {
-            0.0
+            min_col_width
         };
-
-        let col_width = min_col_width;
+        let left_offset = 0.0;
 
         let mut col_lefts = Vec::with_capacity(count);
         let col_heights = vec![top; count];
@@ -750,7 +1447,7 @@ impl Radial {
         }
     }
 
-    pub fn layout_widgets<T: Element + 'static>(&self, widgets: &mut [&mut T]) {
+    pub fn layout_widgets<T: Element + 'static>(&self, widgets: &mut [&mut T], ctx: &mut UiContext) {
         let mut active_idx = 0;
         for w in widgets.iter_mut() {
             if !w.layout_ignore() {
@@ -758,13 +1455,13 @@ impl Radial {
                 let use_w = if ww > 0.0 { ww } else { 100.0 };
                 let use_h = if wh > 0.0 { wh } else { 50.0 };
                 let (x, y, rw, rh) = self.widget_rect(active_idx, use_w, use_h);
-                w.set_rect(x, y, rw, rh);
+                w.layout(crate::widget::Point { x, y }, crate::widget::LayoutConstraints::new(rw, rw, rh, rh), ctx);
                 active_idx += 1;
             }
         }
     }
 
-    pub fn layout_widget_ptors(&self, widgets: &[*mut (dyn Element + 'static)]) {
+    pub fn layout_widget_ptors(&self, widgets: &[*mut (dyn Element + 'static)], ctx: &mut UiContext) {
         let mut active_idx = 0;
         for &w_ptr in widgets {
             let w = unsafe { &mut *w_ptr };
@@ -773,7 +1470,7 @@ impl Radial {
                 let use_w = if ww > 0.0 { ww } else { 100.0 };
                 let use_h = if wh > 0.0 { wh } else { 50.0 };
                 let (x, y, rw, rh) = self.widget_rect(active_idx, use_w, use_h);
-                w.set_rect(x, y, rw, rh);
+                w.layout(crate::widget::Point { x, y }, crate::widget::LayoutConstraints::new(rw, rw, rh, rh), ctx);
                 active_idx += 1;
             }
         }
@@ -784,6 +1481,7 @@ pub trait LayoutStrategy {
     fn init(&mut self, left: f32, top: f32, width: f32, height: f32);
     fn allocate(&mut self, ww: f32, wh: f32) -> (f32, f32, f32, f32);
     fn set_section_count(&mut self, _count: usize) {}
+    fn get_column_width(&self) -> Option<f32> { None }
 }
 
 pub struct ColumnLayout {
@@ -820,6 +1518,10 @@ impl LayoutStrategy for ColumnLayout {
         self.current_y += wh + self.gap;
         (x, y, ww, wh)
     }
+
+    fn get_column_width(&self) -> Option<f32> {
+        Some(self.width)
+    }
 }
 
 pub struct GridLayout {
@@ -842,13 +1544,14 @@ impl GridLayout {
 
 impl LayoutStrategy for GridLayout {
     fn init(&mut self, left: f32, top: f32, width: f32, _height: f32) {
-        let max_cols = ((width + self.gap) / (self.min_col_width + self.gap)).floor().max(1.0) as usize;
+        let min_col_width = crate::layout::grid_min_col_width();
+        let max_cols = ((width + self.gap) / (min_col_width + self.gap)).floor().max(1.0) as usize;
         let count = if let Some(n) = self.num_sections {
             n.min(max_cols).max(1)
         } else {
             max_cols
         };
-        self.grid = Some(Grid::new(left, top, width, self.min_col_width, self.gap, count));
+        self.grid = Some(Grid::new(left, top, width, min_col_width, self.gap, count));
     }
 
     fn allocate(&mut self, _ww: f32, wh: f32) -> (f32, f32, f32, f32) {
@@ -866,6 +1569,10 @@ impl LayoutStrategy for GridLayout {
     fn set_section_count(&mut self, count: usize) {
         self.num_sections = Some(count);
     }
+
+    fn get_column_width(&self) -> Option<f32> {
+        self.grid.as_ref().map(|g| g.col_width)
+    }
 }
 
 pub struct RadialLayout {
@@ -954,12 +1661,13 @@ impl<'a, P: RenderTarget + Default> PageLayoutBuilder<'a, P> {
     where
         F: FnMut(&mut SectionContext<'_, P>),
     {
+        let width = self.strategy.get_column_width().unwrap_or(self.section_width);
         let mut dummy = P::default();
-        let mut dummy_ctx = SectionContext::new(&mut dummy, 0.0, 0.0, self.section_width, label, focused);
+        let mut dummy_ctx = SectionContext::new(&mut dummy, 0.0, 0.0, width, label, focused, false);
         render_fn(&mut dummy_ctx);
         let wh = dummy_ctx.finish();
-        let (rx, ry, _, _) = self.strategy.allocate(self.section_width, wh);
-        let mut real_ctx = SectionContext::new(final_pc, rx, ry, self.section_width, label, focused);
+        let (rx, ry, rw, _) = self.strategy.allocate(width, wh);
+        let mut real_ctx = SectionContext::new(final_pc, rx, ry, rw, label, focused, false);
         render_fn(&mut real_ctx);
         real_ctx.finish();
         self.idx += 1;
@@ -970,11 +1678,11 @@ impl<'a, P: RenderTarget + Default> PageLayoutBuilder<'a, P> {
         F: FnMut(&mut SectionContext<'_, P>),
     {
         let mut dummy = P::default();
-        let mut dummy_ctx = SectionContext::new(&mut dummy, 0.0, 0.0, width, label, focused);
+        let mut dummy_ctx = SectionContext::new(&mut dummy, 0.0, 0.0, width, label, focused, false);
         render_fn(&mut dummy_ctx);
         let wh = dummy_ctx.finish();
-        let (rx, ry, _, _) = self.strategy.allocate(width, wh);
-        let mut real_ctx = SectionContext::new(final_pc, rx, ry, width, label, focused);
+        let (rx, ry, rw, _) = self.strategy.allocate(width, wh);
+        let mut real_ctx = SectionContext::new(final_pc, rx, ry, rw, label, focused, false);
         render_fn(&mut real_ctx);
         real_ctx.finish();
         self.idx += 1;
@@ -989,14 +1697,14 @@ pub struct SectionContext<'a, P> {
     pub cw: f32,
     pub label_width: f32,
     pub focused: bool,
+    pub is_child: bool,
 }
 
 impl<'a, P: RenderTarget> SectionContext<'a, P> {
-    pub const ROW_PADDING_X: f32 = 8.0;
     pub const DEFAULT_MARGIN_X: f32 = 12.0;
     pub const DEFAULT_ROW_GAP: f32 = 8.0;
 
-    fn estimate_label_width(label: &str) -> f32 {
+    fn estimate_label_width(label: &str, font_size: f32) -> f32 {
         let mut width = 0.0;
         for c in label.chars() {
             let factor = match c {
@@ -1006,28 +1714,55 @@ impl<'a, P: RenderTarget> SectionContext<'a, P> {
                 'A'..='Z' => 0.68,
                 _ => 0.55,
             };
-            width += factor * 14.0;
+            width += factor * font_size;
         }
         width
     }
 
-    pub fn new(pc: &'a mut P, left: f32, top: f32, cw: f32, label: &str, focused: bool) -> Self {
-        let label_width = Self::estimate_label_width(label);
-        let label_x = left + (cw - label_width) / 2.0;
-        pc.text(label, label_x, top, 14.0, [0.83, 0.83, 0.83, 1.0]);
+    pub fn padding(&self) -> f32 {
+        if self.is_child {
+            section_padding().max(8.0)
+        } else {
+            section_padding()
+        }
+    }
+
+    pub fn new(pc: &'a mut P, left: f32, top: f32, cw: f32, label: &str, focused: bool, is_child: bool) -> Self {
+        let font_setting = if is_child {
+            nested_section_label_font()
+        } else {
+            section_label_font()
+        };
+        let (font_fam, font_size_opt) = parse_font_string(&font_setting);
+        let font_size = font_size_opt.unwrap_or(if is_child { 12.0 } else { 14.0 });
+        let font_color = if is_child { [0.53, 0.53, 0.60, 1.0] } else { [0.83, 0.83, 0.83, 1.0] };
+        let label_width = Self::estimate_label_width(label, font_size);
+        let label_x = if is_child {
+            let base_x = match nested_section_label_alignment() {
+                0 => left + 12.0,
+                1 => left + (cw - label_width) / 2.0,
+                2 => left + cw - 12.0 - label_width,
+                _ => left + 12.0,
+            };
+            base_x + nested_section_label_offset()
+        } else {
+            left + (cw - label_width) / 2.0
+        };
+        pc.text_with_font(label, label_x, top, font_size, font_color, &font_fam);
         Self {
             pc,
             left,
             top,
-            content_y: top + 19.0,
+            content_y: top + font_size + 5.0,
             cw,
             label_width,
             focused,
+            is_child,
         }
     }
 
     pub fn ax(&self, x_off: f32) -> f32 {
-        let shift = if x_off >= 12.0 { 8.0 } else { 0.0 };
+        let shift = if x_off >= 12.0 { self.padding() } else { 0.0 };
         self.left + x_off + shift
     }
 
@@ -1043,28 +1778,33 @@ impl<'a, P: RenderTarget> SectionContext<'a, P> {
         self.pc.text(text, self.ax(x_off), self.ay() + y_off, font_size, color);
     }
 
-    pub fn widget<T: Element + 'static>(&mut self, w: &mut T, x_off: f32, ww: f32, wh: f32) {
-        w.set_row_rect(self.left + Self::ROW_PADDING_X, self.cw - 2.0 * Self::ROW_PADDING_X);
+    pub fn widget<T: Element + 'static>(&mut self, w: &mut T, x_off: f32, ww: f32, mut wh: f32, ctx: &mut UiContext) {
+        if let Some(pref) = w.preferred_height() {
+            wh = pref;
+        }
+        let pad = self.padding();
+        w.set_row_rect(self.left + pad, self.cw - 2.0 * pad);
         let x = self.ax(x_off);
         let y = self.ay();
-        let right_edge = self.left + self.cw - Self::ROW_PADDING_X;
+        let right_edge = self.left + self.cw - pad;
         let clamped_w = ww.min((right_edge - x).max(0.0));
         let top_room = crate::widget::label_offset(w);
         let total_h = wh + top_room;
-        render_widget(self.pc, w, x, y, clamped_w, total_h);
+        render_widget(self.pc, w, x, y, clamped_w, total_h, ctx);
         self.content_y += total_h;
     }
 
-    pub fn widget_full<T: Element + 'static>(&mut self, w: &mut T, wh: f32) {
+    pub fn widget_full<T: Element + 'static>(&mut self, w: &mut T, wh: f32, ctx: &mut UiContext) {
         let x_off = 12.0;
-        let ww = self.cw - 2.0 * (Self::ROW_PADDING_X + x_off); // cw - 40.0
-        self.widget(w, x_off, ww, wh);
+        let ww = self.cw - 2.0 * (self.padding() + x_off); // cw - 40.0
+        self.widget(w, x_off, ww, wh, ctx);
     }
 
     pub fn separator(&mut self) {
-        let x = self.ax(Self::ROW_PADDING_X);
+        let pad = self.padding();
+        let x = self.ax(pad);
         let y = self.ay();
-        self.pc.rect([0.18, 0.18, 0.27, 1.0], x, y, self.cw - 2.0 * Self::ROW_PADDING_X, 1.0);
+        self.pc.rect([0.18, 0.18, 0.27, 1.0], x, y, self.cw - 2.0 * pad, 1.0);
         self.content_y += 8.0;
     }
 
@@ -1074,7 +1814,7 @@ impl<'a, P: RenderTarget> SectionContext<'a, P> {
     }
 
     pub fn row_layout(&self, count: usize, gap: f32) -> Vec<(f32, f32)> {
-        let margin_x = Self::ROW_PADDING_X + 12.0;
+        let margin_x = self.padding() + 12.0;
         let usable_w = self.cw - 2.0 * margin_x;
         if count == 0 {
             return Vec::new();
@@ -1108,189 +1848,54 @@ impl<'a, P: RenderTarget> SectionContext<'a, P> {
         }
     }
 
-    pub fn add_subsection<F>(&mut self, label: &str, focused: bool, mut render_fn: F)
+    pub fn add_section<F>(&mut self, label: &str, focused: bool, mut render_fn: F)
     where
-        F: FnMut(&mut SubsectionContext<'_, P>),
+        F: FnMut(&mut SectionContext<'_, P>),
     {
-        let left = self.ax(0.0) + Self::ROW_PADDING_X;
+        let pad = self.padding();
+        let left = self.ax(0.0) + pad;
         let top = self.content_y;
-        let cw = self.cw - 2.0 * Self::ROW_PADDING_X;
+        let cw = self.cw - 2.0 * pad;
 
-        let mut sub_ctx = SubsectionContext::new(self.pc, left, top, cw, label, focused);
+        let mut sub_ctx = SectionContext::new(self.pc, left, top, cw, label, focused, true);
         render_fn(&mut sub_ctx);
         self.content_y = sub_ctx.finish();
     }
 
     pub fn finish(self) -> f32 {
-        let border: [f32; 4] = if self.focused {
-            [0.30, 0.50, 0.32, 1.0] // Focused green
-        } else {
-            [0.25, 0.25, 0.35, 1.0] // Default gray
-        };
-        let x = self.left + Self::ROW_PADDING_X;
-        let y = self.top + 7.0;
-        let w = self.cw - 2.0 * Self::ROW_PADDING_X;
-        let h = self.content_y - y;
-
-        let left_edge = x;
-        let right_edge = x + w;
-        if self.label_width > 0.0 {
-            let label_x = self.left + (self.cw - self.label_width) / 2.0;
-            let gap_margin = 6.0;
-            let gap_start = label_x - gap_margin;
-            let gap_end = label_x + self.label_width + gap_margin;
-            if gap_start > left_edge {
-                self.pc.rect(border, left_edge, y, gap_start - left_edge, 1.0);
+        let border: [f32; 4] = if self.is_child {
+            if self.focused {
+                [0.22, 0.38, 0.24, 1.0]
+            } else {
+                [0.18, 0.18, 0.25, 1.0]
             }
-            if right_edge > gap_end {
-                self.pc.rect(border, gap_end, y, right_edge - gap_end, 1.0);
-            }
-        } else {
-            self.pc.rect(border, left_edge, y, w, 1.0);
-        }
-
-        self.pc.rect(border, x, y + h + 12.0, w, 1.0);
-        self.pc.rect(border, x, y, 1.0, h + 12.0);
-        self.pc.rect(border, x + w - 1.0, y, 1.0, h + 12.0);
-        self.content_y + 20.0
-    }
-}
-
-pub struct SubsectionContext<'a, P> {
-    pub pc: &'a mut P,
-    pub left: f32,
-    pub top: f32,
-    pub content_y: f32,
-    pub cw: f32,
-    pub label_width: f32,
-    pub focused: bool,
-}
-
-impl<'a, P: RenderTarget> SubsectionContext<'a, P> {
-    pub const ROW_PADDING_X: f32 = 8.0;
-    pub const DEFAULT_MARGIN_X: f32 = 12.0;
-    pub const DEFAULT_ROW_GAP: f32 = 8.0;
-
-    fn estimate_label_width(label: &str) -> f32 {
-        let mut width = 0.0;
-        for c in label.chars() {
-            let factor = match c {
-                'i' | 'l' | 't' | 'j' | 'f' | 'I' | ' ' | '.' | ',' | '!' | ';' | ':' | '\'' | '"' | '(' | ')' | '[' | ']' | '-' => 0.28,
-                'r' | 's' | 'J' | 'c' | 'z' => 0.42,
-                'm' | 'w' | 'M' | 'W' | '&' | '@' => 0.80,
-                'A'..='Z' => 0.68,
-                _ => 0.55,
-            };
-            width += factor * 12.0;
-        }
-        width
-    }
-
-    pub fn new(pc: &'a mut P, left: f32, top: f32, cw: f32, label: &str, focused: bool) -> Self {
-        let label_width = Self::estimate_label_width(label);
-        let label_x = left + (cw - label_width) / 2.0;
-        pc.text(label, label_x, top, 12.0, [0.53, 0.53, 0.60, 1.0]);
-        Self {
-            pc,
-            left,
-            top,
-            content_y: top + 17.0,
-            cw,
-            label_width,
-            focused,
-        }
-    }
-
-    pub fn ax(&self, x_off: f32) -> f32 {
-        let shift = if x_off >= 12.0 { 8.0 } else { 0.0 };
-        self.left + x_off + shift
-    }
-
-    pub fn ay(&self) -> f32 {
-        self.content_y
-    }
-
-    pub fn spacing(&mut self, dy: f32) {
-        self.content_y += dy;
-    }
-
-    pub fn text(&mut self, text: &str, x_off: f32, y_off: f32, font_size: f32, color: [f32; 4]) {
-        self.pc.text(text, self.ax(x_off), self.ay() + y_off, font_size, color);
-    }
-
-    pub fn widget<T: Element + 'static>(&mut self, w: &mut T, x_off: f32, ww: f32, wh: f32) {
-        w.set_row_rect(self.left + Self::ROW_PADDING_X, self.cw - 2.0 * Self::ROW_PADDING_X);
-        let x = self.ax(x_off);
-        let y = self.ay();
-        let right_edge = self.left + self.cw - Self::ROW_PADDING_X;
-        let clamped_w = ww.min((right_edge - x).max(0.0));
-        let top_room = crate::widget::label_offset(w);
-        let total_h = wh + top_room;
-        render_widget(self.pc, w, x, y, clamped_w, total_h);
-        self.content_y += total_h;
-    }
-
-    pub fn widget_full<T: Element + 'static>(&mut self, w: &mut T, wh: f32) {
-        let x_off = 12.0;
-        let ww = self.cw - 2.0 * (Self::ROW_PADDING_X + x_off);
-        self.widget(w, x_off, ww, wh);
-    }
-
-    pub fn separator(&mut self) {
-        let x = self.ax(Self::ROW_PADDING_X);
-        let y = self.ay();
-        self.pc.rect([0.15, 0.15, 0.22, 1.0], x, y, self.cw - 2.0 * Self::ROW_PADDING_X, 1.0);
-        self.content_y += 8.0;
-    }
-
-    pub fn rect(&mut self, color: [f32; 4], x_off: f32, w: f32, h: f32) {
-        self.pc.rect(color, self.ax(x_off), self.ay(), w, h);
-        self.content_y += h;
-    }
-
-    pub fn row_layout(&self, count: usize, gap: f32) -> Vec<(f32, f32)> {
-        let margin_x = Self::ROW_PADDING_X + 12.0;
-        let usable_w = self.cw - 2.0 * margin_x;
-        if count == 0 {
-            return Vec::new();
-        }
-        let total_gap = gap * (count - 1) as f32;
-        let col_w = (usable_w - total_gap).max(0.0) / count as f32;
-
-        let mut cols = Vec::with_capacity(count);
-        for i in 0..count {
-            let x = self.left + margin_x + i as f32 * (col_w + gap);
-            cols.push((x, col_w));
-        }
-        cols
-    }
-
-    pub fn row<F>(&mut self, count: usize, gap: f32, h: f32, mut f: F)
-    where
-        F: FnMut(usize, f32, f32),
-    {
-        let cols = self.row_layout(count, gap);
-        for (i, &(x, w)) in cols.iter().enumerate() {
-            f(i, x, w);
-        }
-        self.content_y += h;
-    }
-
-    pub fn finish(self) -> f32 {
-        let border: [f32; 4] = if self.focused {
-            [0.22, 0.38, 0.24, 1.0]
         } else {
-            [0.18, 0.18, 0.25, 1.0]
+            if self.focused {
+                [0.30, 0.50, 0.32, 1.0] // Focused green
+            } else {
+                [0.25, 0.25, 0.35, 1.0] // Default gray
+            }
         };
-        let x = self.left + Self::ROW_PADDING_X;
+        let pad = self.padding();
+        let x = self.left + pad;
         let y = self.top + 7.0;
-        let w = self.cw - 2.0 * Self::ROW_PADDING_X;
+        let w = self.cw - 2.0 * pad;
         let h = self.content_y - y;
 
         let left_edge = x;
         let right_edge = x + w;
         if self.label_width > 0.0 {
-            let label_x = self.left + (self.cw - self.label_width) / 2.0;
+            let label_x = if self.is_child {
+                let base_x = match nested_section_label_alignment() {
+                    0 => self.left + 12.0,
+                    1 => self.left + (self.cw - self.label_width) / 2.0,
+                    2 => self.left + self.cw - 12.0 - self.label_width,
+                    _ => self.left + 12.0,
+                };
+                base_x + nested_section_label_offset()
+            } else {
+                self.left + (self.cw - self.label_width) / 2.0
+            };
             let gap_margin = 6.0;
             let gap_start = label_x - gap_margin;
             let gap_end = label_x + self.label_width + gap_margin;
@@ -1311,14 +1916,15 @@ impl<'a, P: RenderTarget> SubsectionContext<'a, P> {
     }
 }
 
+
 pub struct VStack<'b, 'a, P> {
     context: &'b mut SectionContext<'a, P>,
     spacing: f32,
 }
 
 impl<'b, 'a, P: RenderTarget> VStack<'b, 'a, P> {
-    pub fn add_widget<T: Element + 'static>(&mut self, w: &mut T, ww: f32, wh: f32) {
-        self.context.widget(w, SectionContext::<P>::DEFAULT_MARGIN_X, ww, wh);
+    pub fn add_widget<T: Element + 'static>(&mut self, w: &mut T, ww: f32, wh: f32, ctx: &mut UiContext) {
+        self.context.widget(w, SectionContext::<P>::DEFAULT_MARGIN_X, ww, wh, ctx);
         self.context.spacing(self.spacing);
     }
 
@@ -1369,57 +1975,66 @@ mod tests {
     }
 
     struct MockWidgetWithLabel {
-        x: f32,
-        y: f32,
-        w: f32,
-        h: f32,
-        top_room: f32,
+        base: crate::widget::Widget,
     }
 
     impl Element for MockWidgetWithLabel {
+        fn base(&self) -> Option<&crate::widget::Widget> {
+            Some(&self.base)
+        }
+        fn base_mut(&mut self) -> Option<&mut crate::widget::Widget> {
+            Some(&mut self.base)
+        }
         fn rect(&self) -> (f32, f32, f32, f32) {
-            (self.x, self.y - self.top_room, self.w, self.h + self.top_room)
+            let offset = crate::widget::label_offset(self);
+            (self.base.x, self.base.y - offset, self.base.w, self.base.h + offset)
         }
         fn set_rect(&mut self, x: f32, y: f32, w: f32, h: f32) {
-            self.x = x;
-            self.y = y + self.top_room;
-            self.w = w;
-            self.h = (h - self.top_room).max(0.0);
+            let offset = crate::widget::label_offset(self);
+            self.base.x = x;
+            self.base.y = y + offset;
+            self.base.w = w;
+            self.base.h = (h - offset).max(0.0);
         }
         fn color(&self) -> [f32; 4] {
             [0.0, 0.0, 0.0, 0.0]
         }
-        fn top_room(&self) -> f32 {
-            self.top_room
-        }
     }
 
     #[test]
     fn test_vstack_flow() {
+        let orig_margin = label_margin();
+        set_label_margin(6.0);
+        let _ = section_padding();
+        set_section_padding(8.0);
         let mut mock_pc = MockRenderTarget { rects: Vec::new() };
         let mut sec = Section::new(&mut mock_pc, 10.0, 20.0, 200.0, "Test Section");
         
         let start_y = sec.ay();
         let mut stack = sec.vstack(&mut mock_pc, 10.0);
 
+        let mut dummy = crate::context::UiContext::new();
         let mut w1 = MockWidget { x: 0.0, y: 0.0, w: 0.0, h: 0.0 };
-        stack.add_widget(&mut w1, 50.0, 30.0);
+        stack.add_widget(&mut w1, 50.0, 30.0, &mut dummy);
 
         // Standard margin should be applied
         assert_eq!(w1.x, 30.0);
         assert_eq!(w1.y, start_y);
 
         let mut w2 = MockWidget { x: 0.0, y: 0.0, w: 0.0, h: 0.0 };
-        stack.add_widget(&mut w2, 60.0, 40.0);
+        stack.add_widget(&mut w2, 60.0, 40.0, &mut dummy);
 
         // Second widget should start after first widget height + vstack spacing
         assert_eq!(w2.y, start_y + 30.0 + 10.0);
 
-        let mut w3 = MockWidgetWithLabel { x: 0.0, y: 0.0, w: 0.0, h: 0.0, top_room: 15.0 };
-        stack.add_widget(&mut w3, 70.0, 50.0);
+        let mut base = crate::widget::Widget::new();
+        base.label = Some("Test Label".to_string());
+        let mut w3 = MockWidgetWithLabel { base };
+        stack.add_widget(&mut w3, 70.0, 50.0, &mut dummy);
 
-        // Third widget has top_room = 15.0, so its y should be shifted by 15.0
-        assert_eq!(w3.y, start_y + 30.0 + 10.0 + 40.0 + 10.0 + 15.0);
+        // Third widget has label, so its y should be shifted by 18.0
+        assert_eq!(w3.base.y, start_y + 30.0 + 10.0 + 40.0 + 10.0 + 18.0);
+        set_label_margin(orig_margin);
     }
 
     #[test]
@@ -1428,7 +2043,7 @@ mod tests {
         let grid1 = Grid::new(10.0, 20.0, 200.0, 300.0, 10.0, 1);
         assert_eq!(grid1.col_heights.len(), 1);
         assert_eq!(grid1.col_lefts[0], 10.0);
-        assert_eq!(grid1.col_width, 300.0);
+        assert_eq!(grid1.col_width, 200.0);
 
         // Test multi column layout (width = 700, min_col_width = 300, gap = 20)
         // count = floor((700 + 20) / (300 + 20)) = floor(720 / 320) = 2.
@@ -1436,9 +2051,9 @@ mod tests {
         // col_width = (700 - 20) / 2 = 340.
         let mut grid2 = Grid::new(5.0, 15.0, 700.0, 300.0, 20.0, 2);
         assert_eq!(grid2.col_heights.len(), 2);
-        assert_eq!(grid2.col_lefts[0], 45.0);
+        assert_eq!(grid2.col_lefts[0], 5.0);
         assert_eq!(grid2.col_lefts[1], 365.0);
-        assert_eq!(grid2.col_width, 300.0);
+        assert_eq!(grid2.col_width, 340.0);
 
         assert_eq!(grid2.next_column(), 0);
         grid2.col_heights[0] += 50.0; // Column 0 height becomes 65.0
@@ -1454,13 +2069,15 @@ mod tests {
     #[test]
     fn test_subsection() {
         let mut pc = PopoverCollector::new();
-        let mut subsec = Subsection::new(&mut pc, 10.0, 20.0, 300.0, "Test Subsec");
+        let mut subsec = Section::new_opt(&mut pc, 10.0, 20.0, 300.0, "Test Subsec", true);
         assert_eq!(subsec.left, 10.0);
         assert_eq!(subsec.top, 20.0);
         assert_eq!(subsec.cw, 300.0);
+        assert!(subsec.is_child);
         
+        let mut dummy = crate::context::UiContext::new();
         let mut w = MockWidget { x: 0.0, y: 0.0, w: 0.0, h: 0.0 };
-        subsec.widget(&mut pc, &mut w, 12.0, 100.0, 40.0);
+        subsec.widget(&mut pc, &mut w, 12.0, 100.0, 40.0, &mut dummy);
         
         let bottom = subsec.finish(&mut pc);
         assert!(bottom > 20.0);
@@ -1490,5 +2107,39 @@ mod tests {
         assert!(d7 > d1);
         assert!(d1 > 0.0);
     }
+
+    #[test]
+    fn test_nested_section_label_alignment() {
+        let _ = nested_section_label_alignment();
+        set_nested_section_label_alignment(1);
+        assert_eq!(nested_section_label_alignment(), 1);
+        set_nested_section_label_alignment(2);
+        assert_eq!(nested_section_label_alignment(), 2);
+        set_nested_section_label_alignment(0);
+        assert_eq!(nested_section_label_alignment(), 0);
+
+        let _ = nested_section_label_offset();
+        set_nested_section_label_offset(15.0);
+        assert_eq!(nested_section_label_offset(), 15.0);
+        set_nested_section_label_offset(0.0);
+        assert_eq!(nested_section_label_offset(), 0.0);
+    }
+
+    #[test]
+    fn test_dropdown_height() {
+        let _ = dropdown_height();
+        set_dropdown_height(48.0);
+        assert_eq!(dropdown_height(), 48.0);
+        set_dropdown_height(44.0);
+        assert_eq!(dropdown_height(), 44.0);
+    }
+
+    #[test]
+    fn test_print_fonts() {
+        let db = crate::widget::get_font_db();
+        for face in db.faces() {
+            println!("FAMILY: {:?}", face.families);
+        }
+    }
 }
 
diff --git a/src/lib.rs b/src/lib.rs
index b5fec9f..6642d0e 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -5,9 +5,12 @@ pub mod wayland;
 pub mod protocol;
 pub mod engine;
 pub mod scale;
+pub mod backend;
+pub mod context;
 
 pub mod colors {
     pub use crate::color::*;
 }
 
 pub const SHADER: &str = include_str!("shader.wgsl");
+
diff --git a/src/main.rs b/src/main.rs
index f293378..6ab74c3 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -263,6 +263,7 @@ struct State {
     scale: f64,
     json_layout: Option<JsonLayoutWidget>,
     layout_mode: bool,
+    ui_context: clear_ui::context::UiContext,
 }
 
 impl State {
@@ -284,53 +285,58 @@ impl State {
             ..Default::default()
         });
 
-        let surface = instance
-            .create_surface(wayland_handle)
-            .expect("Failed to create surface");
+        let surface = unsafe { instance.create_surface(wayland_handle).unwrap() };
 
         let adapter = instance
             .request_adapter(&wgpu::RequestAdapterOptions {
-                power_preference: wgpu::PowerPreference::LowPower,
+                power_preference: wgpu::PowerPreference::HighPerformance,
                 compatible_surface: Some(&surface),
                 force_fallback_adapter: false,
             })
             .await
-            .expect("Failed to find adapter");
+            .unwrap();
 
         let (device, queue) = adapter
             .request_device(
                 &wgpu::DeviceDescriptor {
-                    label: Some("GPU Device"),
+                    label: None,
                     required_features: wgpu::Features::empty(),
-                    required_limits: wgpu::Limits::downlevel_webgl2_defaults()
-                        .using_resolution(adapter.limits()),
+                    required_limits: wgpu::Limits::default(),
                     memory_hints: wgpu::MemoryHints::MemoryUsage,
                 },
                 None,
             )
             .await
-            .expect("Failed to create device");
+            .unwrap();
 
-        let mut config = surface
-            .get_default_config(&adapter, pw, ph)
-            .expect("Failed to get surface config");
-        config.present_mode = wgpu::PresentMode::Fifo;
+        let surface_caps = surface.get_capabilities(&adapter);
+        let surface_format = surface_caps
+            .formats
+            .iter()
+            .copied()
+            .find(|f| f.is_srgb())
+            .unwrap_or(surface_caps.formats[0]);
+        let mut config = wgpu::SurfaceConfiguration {
+            usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
+            format: surface_format,
+            width: pw,
+            height: ph,
+            present_mode: wgpu::PresentMode::Fifo,
+            alpha_mode: surface_caps.alpha_modes[0],
+            view_formats: vec![],
+            desired_maximum_frame_latency: 2,
+        };
         surface.configure(&device, &config);
 
-        let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
-            label: Some("Shader"),
-            source: wgpu::ShaderSource::Wgsl(clear_ui::SHADER.into()),
-        });
-
-        let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
-            label: Some("Pipeline Layout"),
+        let shader = device.create_shader_module(wgpu::include_wgsl!("shader.wgsl"));
+        let render_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
+            label: Some("Render Pipeline Layout"),
             bind_group_layouts: &[],
             push_constant_ranges: &[],
         });
-
         let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
             label: Some("Render Pipeline"),
-            layout: Some(&pipeline_layout),
+            layout: Some(&render_pipeline_layout),
             vertex: wgpu::VertexState {
                 module: &shader,
                 entry_point: Some("vs_main"),
@@ -352,8 +358,8 @@ impl State {
                 strip_index_format: None,
                 front_face: wgpu::FrontFace::Ccw,
                 cull_mode: None,
-                polygon_mode: wgpu::PolygonMode::Fill,
                 unclipped_depth: false,
+                polygon_mode: wgpu::PolygonMode::Fill,
                 conservative: false,
             },
             depth_stencil: None,
@@ -366,18 +372,18 @@ impl State {
             cache: None,
         });
 
-        // Initialize text rendering
         let mut font_system = FontSystem::new();
         let swash_cache = SwashCache::new();
         let cache = Cache::new(&device);
-        let mut text_atlas = TextAtlas::new(&device, &queue, &cache, config.format);
-        let text_renderer = TextRenderer::new(&mut text_atlas, &device, wgpu::MultisampleState::default(), None);
+        let viewport = Viewport::new(&device, &cache);
+        let mut text_atlas = TextAtlas::new(&device, &queue, &cache, surface_format);
+        let text_renderer =
+            TextRenderer::new(&mut text_atlas, &device, wgpu::MultisampleState::default(), None);
 
-        let mut text_viewport = Viewport::new(&device, &cache);
-        text_viewport.update(&queue, Resolution { width: pw, height: ph });
+        let label_buffer = make_text_buffer(&mut font_system, "Design System Playground", 16.0);
+        let status_buffer = make_text_buffer(&mut font_system, "Ready", 12.0);
 
-        let label_buffer = make_text_buffer(&mut font_system, "Hello, Clear UI!", 16.0);
-        let status_buffer = make_text_buffer(&mut font_system, "Click a button to interact", 12.0);
+        let text_viewport = viewport;
 
         let layout_mode = json_layout_config.is_some();
         let mut widgets: Vec<Box<dyn Element>> = Vec::new();
@@ -446,6 +452,7 @@ impl State {
             scale,
             json_layout,
             layout_mode,
+            ui_context: clear_ui::context::UiContext::new(),
         };
 
         state.apply_layout();
@@ -555,6 +562,7 @@ impl State {
             scale,
             ref json_layout,
             layout_mode,
+            ref ui_context,
             ..
         } = self;
 
@@ -599,14 +607,14 @@ impl State {
         let mut widget_labels: Vec<(TextLabel, Option<[f32; 4]>)> = Vec::new();
         if *layout_mode {
             if let Some(jl) = json_layout {
-                for (label, font, bounds) in jl.text_labels_with_font_and_bounds() {
+                for (label, font, bounds) in jl.text_labels_with_font_and_bounds(ui_context) {
                     widget_buffers.push(make_text_buffer_with_font(font_system, &label.text, label.font_size, font.as_deref()));
                     widget_labels.push((label, bounds));
                 }
             }
         } else {
             for (i, w) in self.widgets.iter().enumerate() {
-                for (label, font, bounds) in w.text_labels_with_font_and_bounds() {
+                for (label, font, bounds) in w.text_labels_with_font_and_bounds(ui_context) {
                     let mut covered = false;
                     for (pi, pw) in self.widgets.iter().enumerate() {
                         if pi != i {
@@ -1016,7 +1024,7 @@ impl PointerHandler for AppState {
                             }
                         } else if state.layout_mode {
                             if let Some(jl) = &mut state.json_layout {
-                                if jl.cursor_moved(state.cursor_x, state.cursor_y) {
+                                if jl.cursor_moved(state.cursor_x, state.cursor_y, &mut state.ui_context) {
                                     changed = true;
                                 }
                             }
@@ -1028,7 +1036,7 @@ impl PointerHandler for AppState {
                             }
                             if state.drag_widget.is_none() {
                                 for w in &mut state.widgets {
-                                    if w.cursor_moved(state.cursor_x, state.cursor_y) {
+                                    if w.cursor_moved(state.cursor_x, state.cursor_y, &mut state.ui_context) {
                                         changed = true;
                                     }
                                 }
@@ -1055,14 +1063,14 @@ impl PointerHandler for AppState {
                             }
                         } else if st.layout_mode {
                             if let Some(jl) = &mut st.json_layout {
-                                if jl.mouse_input(btn, clear_ui::widget::ElementState::Pressed, st.cursor_x, st.cursor_y) {
+                                if jl.mouse_input(btn, clear_ui::widget::ElementState::Pressed, st.cursor_x, st.cursor_y, &mut st.ui_context) {
                                     changed = true;
                                 }
                             }
                         } else {
                             let mut clicked_idx = None;
                             for i in (0..st.widgets.len()).rev() {
-                                if st.widgets[i].hit_test(st.cursor_x, st.cursor_y) {
+                                if st.widgets[i].hit_test(st.cursor_x, st.cursor_y, &st.ui_context) {
                                     clicked_idx = Some(i);
                                     break;
                                 }
@@ -1081,6 +1089,7 @@ impl PointerHandler for AppState {
                                     clear_ui::widget::ElementState::Pressed,
                                     st.cursor_x,
                                     st.cursor_y,
+                                    &mut st.ui_context,
                                 ) {
                                     changed = true;
                                 }
@@ -1117,7 +1126,7 @@ impl PointerHandler for AppState {
                         } else if st.layout_mode {
                             let mut clicked_btn_id = None;
                             if let Some(jl) = &mut st.json_layout {
-                                if jl.mouse_input(btn, clear_ui::widget::ElementState::Released, st.cursor_x, st.cursor_y) {
+                                if jl.mouse_input(btn, clear_ui::widget::ElementState::Released, st.cursor_x, st.cursor_y, &mut st.ui_context) {
                                     changed = true;
                                 }
                                 if btn == clear_ui::widget::MouseButton::Left {
@@ -1126,7 +1135,7 @@ impl PointerHandler for AppState {
                                         if w.page_idx != active_page {
                                             continue;
                                         }
-                                        if let Some(btn_w) = &mut w.button {
+                                        if let Some(btn_w) = w.widget.as_any_mut().downcast_mut::<clear_ui::widget::Button>() {
                                             if btn_w.take_click() {
                                                 clicked_btn_id = Some(w.id.clone());
                                                 break;
@@ -1142,13 +1151,13 @@ impl PointerHandler for AppState {
                                 let mut sliders = std::collections::HashMap::new();
                                 if let Some(jl) = &st.json_layout {
                                     for w in &jl.widgets {
-                                        if let Some(cb) = &w.checkbox {
+                                        if let Some(cb) = w.widget.as_any().downcast_ref::<clear_ui::widget::Checkbox>() {
                                             checkboxes.insert(w.id.clone(), cb.checked());
-                                        } else if let Some(sb) = &w.spinbox {
+                                        } else if let Some(sb) = w.widget.as_any().downcast_ref::<clear_ui::widget::Spinbox>() {
                                             spinboxes.insert(w.id.clone(), sb.value);
-                                        } else if let Some(cs) = &w.color_selector {
+                                        } else if let Some(cs) = w.widget.as_any().downcast_ref::<clear_ui::widget::ColorSelector>() {
                                             colors.insert(w.id.clone(), cs.color);
-                                        } else if let Some(sl) = &w.slider {
+                                        } else if let Some(sl) = w.widget.as_any().downcast_ref::<clear_ui::widget::Slider>() {
                                             sliders.insert(w.id.clone(), sl.get_scaled_value());
                                         }
                                     }
@@ -1172,7 +1181,7 @@ impl PointerHandler for AppState {
                                 }
                             }
                             for w in &mut st.widgets {
-                                if w.mouse_input(btn, clear_ui::widget::ElementState::Released, st.cursor_x, st.cursor_y) {
+                                if w.mouse_input(btn, clear_ui::widget::ElementState::Released, st.cursor_x, st.cursor_y, &mut st.ui_context) {
                                     changed = true;
                                 }
                             }
@@ -1207,13 +1216,13 @@ impl PointerHandler for AppState {
                         let mut changed = false;
                         if !state.layout_mode {
                             for w in &mut state.widgets {
-                                if w.mouse_wheel(&delta, state.cursor_x, state.cursor_y) {
+                                if w.mouse_wheel(&delta, state.cursor_x, state.cursor_y, &mut state.ui_context) {
                                     changed = true;
                                 }
                             }
                         } else {
                             if let Some(jl) = &mut state.json_layout {
-                                if jl.mouse_wheel(&delta, state.cursor_x, state.cursor_y) {
+                                if jl.mouse_wheel(&delta, state.cursor_x, state.cursor_y, &mut state.ui_context) {
                                     changed = true;
                                 }
                             }
@@ -1366,7 +1375,7 @@ impl AppState {
         if let Some(st) = &mut self.state {
             if st.layout_mode {
                 if let Some(jl) = &mut st.json_layout {
-                    let mut changed = jl.keyboard_input(&custom_event);
+                    let changed = jl.keyboard_input(&custom_event, &mut st.ui_context);
                     if changed {
                         st.upload_vertices();
                         self.redraw = true;
@@ -1374,7 +1383,7 @@ impl AppState {
                 }
             } else if let Some(idx) = st.focused_widget {
                 let val = st.widgets[idx].value();
-                let mut changed = st.widgets[idx].keyboard_input(&custom_event);
+                let mut changed = st.widgets[idx].keyboard_input(&custom_event, &mut st.ui_context);
                 if st.widgets[idx].value() != val {
                     changed = true;
                 }
@@ -1597,13 +1606,13 @@ fn main() {
             let mut tick_changed = false;
             if st.layout_mode {
                 if let Some(ref mut jl) = &mut st.json_layout {
-                    if jl.tick(dt) {
+                    if jl.tick(dt, &mut st.ui_context) {
                         tick_changed = true;
                     }
                 }
             } else {
                 for w in &mut st.widgets {
-                    if w.tick(dt) {
+                    if w.tick(dt, &mut st.ui_context) {
                         tick_changed = true;
                     }
                 }
@@ -1630,7 +1639,7 @@ fn main() {
                     if let Some(st) = &mut app.state {
                         if st.layout_mode {
                             if let Some(jl) = &mut st.json_layout {
-                                let mut changed = jl.keyboard_input(&custom_event);
+                                let changed = jl.keyboard_input(&custom_event, &mut st.ui_context);
                                 if changed {
                                     st.upload_vertices();
                                     app.redraw = true;
@@ -1638,7 +1647,7 @@ fn main() {
                             }
                         } else if let Some(idx) = st.focused_widget {
                             let val = st.widgets[idx].value();
-                            let mut changed = st.widgets[idx].keyboard_input(&custom_event);
+                            let mut changed = st.widgets[idx].keyboard_input(&custom_event, &mut st.ui_context);
                             if st.widgets[idx].value() != val {
                                 changed = true;
                             }
diff --git a/src/shader.wgsl b/src/shader.wgsl
index bc75fcc..2d36cda 100644
--- a/src/shader.wgsl
+++ b/src/shader.wgsl
@@ -37,16 +37,5 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4f {
             discard;
         }
     }
-    var final_color = in.color;
-    if (in.is_background > 0.999) {
-        // Feather the background quad edges
-        let dist_x = 1.0 - abs(in.ndc_position.x);
-        let dist_y = 1.0 - abs(in.ndc_position.y);
-        let min_dist = min(dist_x, dist_y);
-
-        let feather = 0.15; // Size of the feathering zone (15% of the half-width/height)
-        let fade = smoothstep(0.0, 1.0, min_dist / feather);
-        final_color.a = final_color.a * fade;
-    }
-    return final_color;
+    return in.color;
 }
diff --git a/src/widget/container.rs b/src/widget/container.rs
deleted file mode 100644
index 0daecb7..0000000
--- a/src/widget/container.rs
+++ /dev/null
@@ -1,5076 +0,0 @@
-use crate::colors;
-use crate::widget::*;
-use crate::widget::display::make_widget_text_buffer;
-use crate::widget::input::{BREADCRUMB_PADDING, SEGMENT_GAP};
-
-#[derive(Clone)]
-pub struct Container {
-    pub parent: Option<*mut (dyn Element + 'static)>,
-    pub children: Vec<*mut (dyn Element + 'static)>,
-}
-
-impl Container {
-    pub fn new() -> Self {
-        Self { parent: None, children: Vec::new() }
-    }
-}
-
-impl Element for Container {
-    fn rect(&self) -> (f32, f32, f32, f32) { (0.0, 0.0, 0.0, 0.0) }
-    fn set_rect(&mut self, _x: f32, _y: f32, _w: f32, _h: f32) {}
-    fn color(&self) -> [f32; 4] { [0.0, 0.0, 0.0, 0.0] }
-
-    fn focus(&mut self) {
-        focus::set_focused(self);
-    }
-    fn unfocus(&mut self) {}
-
-    fn parent(&self) -> Option<*mut (dyn Element + 'static)> { self.parent }
-    fn set_parent(&mut self, parent: Option<*mut (dyn Element + 'static)>) { self.parent = parent; }
-    fn children(&self) -> Vec<*mut (dyn Element + 'static)> { self.children.clone() }
-    fn add_child(&mut self, child: *mut (dyn Element + 'static)) { self.children.push(child); }
-    fn clear_children(&mut self) { self.children.clear(); }
-}
-
-impl Drop for Container {
-    fn drop(&mut self) {
-        focus::clear_if_matches(self);
-    }
-}
-
-
-pub struct Header {
-    x: f32, y: f32, w: f32, h: f32,
-    hovered: bool,
-}
-
-impl Header {
-    pub fn new() -> Self { Self { x: 0.0, y: 0.0, w: 0.0, h: 0.0, hovered: false } }
-}
-
-impl Element for Header {
-    fn rect(&self) -> (f32, f32, f32, f32) { (self.x, self.y, self.w, self.h) }
-    fn set_rect(&mut self, x: f32, y: f32, w: f32, h: f32) { self.x = x; self.y = y; self.w = w; self.h = h; }
-    fn color(&self) -> [f32; 4] { colors::HEADER_BG }
-    fn set_hovered(&mut self, v: bool) { self.hovered = v; }
-    fn hovered(&self) -> bool { self.hovered }
-}
-
-pub struct ContentBg {
-    x: f32, y: f32, w: f32, h: f32,
-    hovered: bool,
-    show_network_grid: bool,
-    grid_size_x: f32,
-    grid_size_y: f32,
-    grid_origin_x: f32,
-    grid_origin_y: f32,
-    skipped_row_h: f32,
-    skipped_col_w: f32,
-}
-
-impl ContentBg {
-    pub fn new() -> Self {
-        Self { x: 0.0, y: 0.0, w: 0.0, h: 0.0, hovered: false, show_network_grid: false, grid_size_x: 150.0, grid_size_y: 75.0, grid_origin_x: 0.0, grid_origin_y: 0.0, skipped_row_h: 37.5, skipped_col_w: 37.5 }
-    }
-}
-
-impl Element for ContentBg {
-    fn rect(&self) -> (f32, f32, f32, f32) { (self.x, self.y, self.w, self.h) }
-    fn set_rect(&mut self, x: f32, y: f32, w: f32, h: f32) { self.x = x; self.y = y; self.w = w; self.h = h; }
-    fn color(&self) -> [f32; 4] {
-        if self.show_network_grid {
-            [0.0, 0.0, 0.0, 0.0]
-        } else {
-            colors::CONTENT_BG
-        }
-    }
-    fn set_hovered(&mut self, v: bool) { self.hovered = v; }
-    fn hovered(&self) -> bool { self.hovered }
-    fn hit_test(&self, _px: f32, _py: f32) -> bool { false }
-
-    fn set_show_network_grid(&mut self, show: bool) { self.show_network_grid = show; }
-    fn set_grid_sizes(&mut self, gx: f32, gy: f32) { self.grid_size_x = gx; self.grid_size_y = gy; }
-    fn set_grid_origin(&mut self, ox: f32, oy: f32) { self.grid_origin_x = ox; self.grid_origin_y = oy; }
-    fn set_skipped_sizes(&mut self, row_h: f32, col_w: f32) { self.skipped_row_h = row_h; self.skipped_col_w = col_w; }
-
-    fn extra_quads(&self) -> Vec<(f32, f32, f32, f32, [f32; 4])> {
-        if !self.show_network_grid || self.grid_size_x <= 0.0 || self.grid_size_y <= 0.0 {
-            return vec![];
-        }
-        let mut quads = Vec::new();
-        let grid_color = [0.0, 0.0, 0.0, 0.0];
-        let max_alpha = colors::CONTENT_BG[3]; // Peak opacity in the middle of gradient cells matches non-gradient cells
-        let steps = 20; // Silky-smooth gradient transition
-
-        let step_y = self.grid_size_y + self.skipped_row_h;
-        let step_x = self.grid_size_x + self.skipped_col_w;
-
-        if step_y >= 4.0 && step_x >= 4.0 {
-            let ry_start = ((self.y - self.grid_origin_y) / step_y).floor() as i32 - 1;
-            let ry_end = ((self.y + self.h - self.grid_origin_y) / step_y).ceil() as i32 + 1;
-            let ry_start = ry_start.max(-100_000);
-            let ry_end = ry_end.min(100_000);
-
-            let cx_start = ((self.x - self.grid_origin_x) / step_x).floor() as i32 - 1;
-            let cx_end = ((self.x + self.w - self.grid_origin_x) / step_x).ceil() as i32 + 1;
-            let cx_start = cx_start.max(-100_000);
-            let cx_end = cx_end.min(100_000);
-
-            // Draw individual cell backgrounds to avoid stacking with gradients
-            for ry in ry_start..=ry_end {
-                let y1 = self.grid_origin_y + (ry as f32) * step_y;
-                let draw_start_y = y1.max(self.y);
-                let draw_end_y = (y1 + self.grid_size_y).min(self.y + self.h);
-                if draw_start_y < draw_end_y {
-                    for cx in cx_start..=cx_end {
-                        let x1 = self.grid_origin_x + (cx as f32) * step_x;
-                        let draw_start_x = x1.max(self.x);
-                        let draw_end_x = (x1 + self.grid_size_x).min(self.x + self.w);
-                        if draw_start_x < draw_end_x {
-                            quads.push((draw_start_x, draw_start_y, draw_end_x - draw_start_x, draw_end_y - draw_start_y, colors::CONTENT_BG));
-                        }
-                    }
-                }
-            }
-        }
-
-        // Draw interstitial row gradients (horizontal bands fading to 0 alpha at left and right sides)
-        if self.skipped_row_h > 0.0 {
-            let step_y = self.grid_size_y + self.skipped_row_h;
-            let step_x = self.grid_size_x + self.skipped_col_w;
-            if step_y >= 4.0 && step_x >= 4.0 {
-                let k_start = ((self.y - self.grid_origin_y) / step_y).floor() as i32 - 1;
-                let k_end = ((self.y + self.h - self.grid_origin_y) / step_y).ceil() as i32 + 1;
-                let k_start = k_start.max(-100_000);
-                let k_end = k_end.min(100_000);
-
-                let cx_start = ((self.x - self.grid_origin_x) / step_x).floor() as i32 - 1;
-                let cx_end = ((self.x + self.w - self.grid_origin_x) / step_x).ceil() as i32 + 1;
-                let cx_start = cx_start.max(-100_000);
-                let cx_end = cx_end.min(100_000);
-
-                for k in k_start..=k_end {
-                    let y1 = self.grid_origin_y + (k as f32) * step_y;
-                    let y2 = y1 + self.grid_size_y;
-                    if y1 >= self.y + self.h {
-                        continue;
-                    }
-                    let draw_start_y = y2.max(self.y);
-                    let draw_end_y = (y2 + self.skipped_row_h).min(self.y + self.h);
-                    if draw_start_y >= draw_end_y {
-                        continue;
-                    }
-
-                    for cx in cx_start..=cx_end {
-                        let x1 = self.grid_origin_x + (cx as f32) * step_x;
-                        let x_mid = x1 + self.grid_size_x / 2.0;
-                        let w_total = self.grid_size_x;
-                        let sub_w = w_total / steps as f32;
-
-                        for i in 0..steps {
-                            let sx_start = x1 + i as f32 * sub_w;
-                            let sx_end = sx_start + sub_w;
-                            let draw_start_x = sx_start.max(self.x);
-                            let draw_end_x = sx_end.min(self.x + self.w);
-                            if draw_start_x < draw_end_x {
-                                let sx_mid = (sx_start + sx_end) / 2.0;
-                                let dist = (sx_mid - x_mid).abs();
-                                let d = (dist / (w_total / 2.0)).min(1.0);
-                                
-                                // Fade the cell background color from max_alpha in the middle to transparent at the edges
-                                let alpha = max_alpha * (1.0 - d);
-                                if alpha > 0.001 {
-                                    quads.push((draw_start_x, draw_start_y, draw_end_x - draw_start_x, draw_end_y - draw_start_y, [colors::CONTENT_BG[0], colors::CONTENT_BG[1], colors::CONTENT_BG[2], alpha]));
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        // Draw interstitial column gradients (vertical bands fading to 0 alpha at top and bottom)
-        if self.skipped_col_w > 0.0 {
-            let step_y = self.grid_size_y + self.skipped_row_h;
-            let step_x = self.grid_size_x + self.skipped_col_w;
-            if step_y >= 4.0 && step_x >= 4.0 {
-                let k_start = ((self.x - self.grid_origin_x) / step_x).floor() as i32 - 1;
-                let k_end = ((self.x + self.w - self.grid_origin_x) / step_x).ceil() as i32 + 1;
-                let k_start = k_start.max(-100_000);
-                let k_end = k_end.min(100_000);
-
-                let ry_start = ((self.y - self.grid_origin_y) / step_y).floor() as i32 - 1;
-                let ry_end = ((self.y + self.h - self.grid_origin_y) / step_y).ceil() as i32 + 1;
-                let ry_start = ry_start.max(-100_000);
-                let ry_end = ry_end.min(100_000);
-
-                for k in k_start..=k_end {
-                    let x1 = self.grid_origin_x + (k as f32) * step_x;
-                    let x2 = x1 + self.grid_size_x;
-                    if x1 >= self.x + self.w {
-                        continue;
-                    }
-                    let draw_start_x = x2.max(self.x);
-                    let draw_end_x = (x2 + self.skipped_col_w).min(self.x + self.w);
-                    if draw_start_x >= draw_end_x {
-                        continue;
-                    }
-
-                    for ry in ry_start..=ry_end {
-                        let y1 = self.grid_origin_y + (ry as f32) * step_y;
-                        let y_mid = y1 + self.grid_size_y / 2.0;
-                        let h_total = self.grid_size_y;
-                        let sub_h = h_total / steps as f32;
-
-                        for i in 0..steps {
-                            let sy_start = y1 + i as f32 * sub_h;
-                            let sy_end = sy_start + sub_h;
-                            let draw_start_y = sy_start.max(self.y);
-                            let draw_end_y = sy_end.min(self.y + self.h);
-                            if draw_start_y < draw_end_y {
-                                let sy_mid = (sy_start + sy_end) / 2.0;
-                                let dist = (sy_mid - y_mid).abs();
-                                let d = (dist / (h_total / 2.0)).min(1.0);
-                                
-                                // Fade the cell background color from max_alpha in the middle to transparent at the edges
-                                let alpha = max_alpha * (1.0 - d);
-                                if alpha > 0.001 {
-                                    quads.push((draw_start_x, draw_start_y, draw_end_x - draw_start_x, draw_end_y - draw_start_y, [colors::CONTENT_BG[0], colors::CONTENT_BG[1], colors::CONTENT_BG[2], alpha]));
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        // Draw the grid borders
-        let step_y = self.grid_size_y + self.skipped_row_h;
-        if step_y >= 4.0 {
-            let k_start = ((self.y - self.grid_origin_y) / step_y).floor() as i32 - 1;
-            let k_end = ((self.y + self.h - self.grid_origin_y) / step_y).ceil() as i32 + 1;
-            let k_start = k_start.max(-100_000);
-            let k_end = k_end.min(100_000);
-            for k in k_start..=k_end {
-                let y1 = self.grid_origin_y + (k as f32) * step_y;
-                let y2 = y1 + self.grid_size_y;
-                if y1 >= self.y + self.h {
-                    continue;
-                }
-                if y1 >= self.y {
-                    quads.push((self.x, y1, self.w, 1.0, grid_color));
-                }
-                if y2 >= self.y && y2 < self.y + self.h {
-                    quads.push((self.x, y2, self.w, 1.0, grid_color));
-                }
-            }
-        }
-
-        let step_x = self.grid_size_x + self.skipped_col_w;
-        if step_x >= 4.0 {
-            let k_start = ((self.x - self.grid_origin_x) / step_x).floor() as i32 - 1;
-            let k_end = ((self.x + self.w - self.grid_origin_x) / step_x).ceil() as i32 + 1;
-            let k_start = k_start.max(-100_000);
-            let k_end = k_end.min(100_000);
-            for k in k_start..=k_end {
-                let x1 = self.grid_origin_x + (k as f32) * step_x;
-                let x2 = x1 + self.grid_size_x;
-                if x1 >= self.x + self.w {
-                    continue;
-                }
-                if x1 >= self.x {
-                    quads.push((x1, self.y, 1.0, self.h, grid_color));
-                }
-                if x2 >= self.x && x2 < self.x + self.w {
-                    quads.push((x2, self.y, 1.0, self.h, grid_color));
-                }
-            }
-        }
-        quads
-    }
-}
-
-pub struct ViewportBg {
-    x: f32, y: f32, w: f32, h: f32,
-    hovered: bool,
-}
-
-impl ViewportBg {
-    pub fn new() -> Self { Self { x: 0.0, y: 0.0, w: 0.0, h: 0.0, hovered: false } }
-}
-
-impl Element for ViewportBg {
-    fn rect(&self) -> (f32, f32, f32, f32) { (self.x, self.y, self.w, self.h) }
-    fn set_rect(&mut self, x: f32, y: f32, w: f32, h: f32) { self.x = x; self.y = y; self.w = w; self.h = h; }
-    fn color(&self) -> [f32; 4] { colors::VIEWPORT_BG }
-    fn set_hovered(&mut self, v: bool) { self.hovered = v; }
-    fn hovered(&self) -> bool { self.hovered }
-    fn hit_test(&self, _px: f32, _py: f32) -> bool { false }
-}
-
-pub struct ParametersBg {
-    x: f32, y: f32, w: f32, h: f32,
-    hovered: bool,
-    display_params: Vec<(String, String, String)>,
-    dragging_param: Option<usize>,
-    pub focused_param: Option<usize>,
-    mouse_pos: Option<(f32, f32)>,
-    sliders: Vec<Option<Slider>>,
-    float3s: Vec<Option<Float3>>,
-    spinboxes: Vec<Option<Spinbox>>,
-    visible: bool,
-}
-
-impl ParametersBg {
-    pub fn new() -> Self {
-        Self {
-            x: 0.0,
-            y: 0.0,
-            w: 0.0,
-            h: 0.0,
-            hovered: false,
-            display_params: Vec::new(),
-            dragging_param: None,
-            focused_param: None,
-            mouse_pos: None,
-            sliders: Vec::new(),
-            float3s: Vec::new(),
-            spinboxes: Vec::new(),
-            visible: true,
-        }
-    }
-
-    pub fn get_param_rects(&self) -> Vec<(f32, f32, f32, f32)> {
-        let mut rects = Vec::new();
-        let mut cur_y = self.y + 30.0;
-        for p in &self.display_params {
-            let h = if p.2 == "code" {
-                200.0
-            } else if p.2 == "section" {
-                24.0
-            } else if p.2.starts_with("float3") {
-                108.0
-            } else if p.2 == "text" {
-                24.0
-            } else {
-                20.0
-            };
-            rects.push((self.x + 8.0, cur_y, self.w - 16.0, h));
-            cur_y += h + 8.0;
-        }
-        rects
-    }
-
-    fn update_slider_rects(&mut self) {
-        let rects = self.get_param_rects();
-        for (i, s_opt) in self.sliders.iter_mut().enumerate() {
-            if let Some(s) = s_opt {
-                let r = rects[i];
-                let track_x = self.x + 100.0;
-                let track_w = (self.w - 100.0 - 20.0).max(10.0);
-                let track_y = r.1 + 4.0;
-                let track_h = 12.0;
-                s.set_rect(track_x, track_y, track_w, track_h);
-            }
-        }
-        for (i, f_opt) in self.float3s.iter_mut().enumerate() {
-            if let Some(f) = f_opt {
-                let r = rects[i];
-                f.set_rect(r.0, r.1, r.2, r.3);
-            }
-        }
-        for (i, sb_opt) in self.spinboxes.iter_mut().enumerate() {
-            if let Some(sb) = sb_opt {
-                let r = rects[i];
-                let box_x = self.x + 100.0;
-                let box_w = (self.w - 100.0 - 16.0).max(10.0);
-                sb.set_rect(box_x, r.1, box_w, r.3);
-            }
-        }
-    }
-}
-
-fn parse_slider_range(ptype: &str) -> (f32, f32) {
-    if ptype.starts_with("slider:") || ptype.starts_with("float3:") {
-        let parts: Vec<&str> = ptype.split(':').collect();
-        if parts.len() >= 3 {
-            if let (Ok(min), Ok(max)) = (parts[1].parse::<f32>(), parts[2].parse::<f32>()) {
-                return (min, max);
-            }
-        }
-    }
-    (0.0, 2.0)
-}
-
-fn parse_spinbox_range(ptype: &str) -> (i32, i32, i32) {
-    if ptype.starts_with("spinbox:") {
-        let parts: Vec<&str> = ptype.split(':').collect();
-        if parts.len() >= 4 {
-            if let (Ok(min), Ok(max), Ok(step)) = (parts[1].parse::<i32>(), parts[2].parse::<i32>(), parts[3].parse::<i32>()) {
-                return (min, max, step);
-            }
-        } else if parts.len() == 3 {
-            if let (Ok(min), Ok(max)) = (parts[1].parse::<i32>(), parts[2].parse::<i32>()) {
-                return (min, max, 1);
-            }
-        }
-    }
-    (0, 10000, 1)
-}
-
-fn parse_float3_value(val_str: &str, min: f32, max: f32) -> [f32; 3] {
-    let mut out = [0.5, 0.5, 0.5];
-    let parts: Vec<&str> = val_str
-        .split(|c| c == ':' || c == ',' || c == ' ')
-        .filter(|s| !s.is_empty())
-        .collect();
-    for i in 0..3 {
-        if i < parts.len() {
-            if let Ok(v) = parts[i].parse::<f32>() {
-                let range = max - min;
-                if range != 0.0 {
-                    out[i] = ((v - min) / range).clamp(0.0, 1.0);
-                } else {
-                    out[i] = 0.0;
-                }
-            }
-        }
-    }
-    out
-}
-
-impl Element for ParametersBg {
-    fn rect(&self) -> (f32, f32, f32, f32) { (self.x, self.y, self.w, self.h) }
-    fn set_rect(&mut self, x: f32, y: f32, w: f32, h: f32) {
-        self.x = x; self.y = y; self.w = w; self.h = h;
-        self.update_slider_rects();
-    }
-    fn color(&self) -> [f32; 4] {
-        if !self.visible {
-            return [0.0, 0.0, 0.0, 0.0];
-        }
-        colors::PARAM_BG
-    }
-    fn set_hovered(&mut self, v: bool) { self.hovered = v; }
-    fn hovered(&self) -> bool { self.hovered }
-    fn set_visible(&mut self, visible: bool) {
-        self.visible = visible;
-    }
-    fn visible(&self) -> bool {
-        self.visible
-    }
-    fn hit_test(&self, px: f32, py: f32) -> bool {
-        if !self.visible {
-            return false;
-        }
-        if crate::widget::popovers::is_coordinate_covered(self as *const Self as *const () as usize, px, py) {
-            return false;
-        }
-        px >= self.x && px <= self.x + self.w && py >= self.y && py <= self.y + self.h
-    }
-
-    fn set_display_params(&mut self, params: &[(String, String, String)]) {
-        let mut layout_changed = self.display_params.len() != params.len();
-        if !layout_changed {
-            for (p_old, p_new) in self.display_params.iter().zip(params.iter()) {
-                if p_old.0 != p_new.0 || p_old.2 != p_new.2 {
-                    layout_changed = true;
-                    break;
-                }
-            }
-        }
-
-        if layout_changed {
-            self.display_params = params.to_vec();
-            self.focused_param = None;
-            self.sliders = self.display_params.iter().map(|p| {
-                if p.2.starts_with("slider") {
-                    let val = p.1.parse::<f32>().unwrap_or(0.0);
-                    let (min, max) = parse_slider_range(&p.2);
-                    let t = if max - min != 0.0 {
-                        ((val - min) / (max - min)).clamp(0.0, 1.0)
-                    } else {
-                        0.0
-                    };
-                    Some(Slider::new().with_value(t).with_range(min, max).with_readout(true))
-                } else {
-                    None
-                }
-            }).collect();
-            self.float3s = self.display_params.iter().map(|p| {
-                if p.2.starts_with("float3") {
-                    let (min, max) = parse_slider_range(&p.2);
-                    let vals = parse_float3_value(&p.1, min, max);
-                    Some(Float3::new().with_values(vals).with_range(min, max).with_label(&p.0))
-                } else {
-                    None
-                }
-            }).collect();
-            self.spinboxes = self.display_params.iter().map(|p| {
-                if p.2.starts_with("spinbox") {
-                    let (min, max, step) = parse_spinbox_range(&p.2);
-                    let val = p.1.parse::<i32>().unwrap_or(min);
-                    Some(Spinbox::new(val, min, max, step))
-                } else {
-                    None
-                }
-            }).collect();
-        } else {
-            for (i, p_new) in params.iter().enumerate() {
-                if Some(i) != self.focused_param && Some(i) != self.dragging_param {
-                    self.display_params[i].1 = p_new.1.clone();
-                    if let Some(ref mut s) = self.sliders[i] {
-                        let val = p_new.1.parse::<f32>().unwrap_or(0.0);
-                        let (min, max) = parse_slider_range(&p_new.2);
-                        let t = if max - min != 0.0 {
-                            ((val - min) / (max - min)).clamp(0.0, 1.0)
-                        } else {
-                            0.0
-                        };
-                        s.set_value(t);
-                    } else if let Some(ref mut f) = self.float3s[i] {
-                        let (min, max) = parse_slider_range(&p_new.2);
-                        let vals = parse_float3_value(&p_new.1, min, max);
-                        f.set_values(vals);
-                    } else if let Some(ref mut sb) = self.spinboxes[i] {
-                        if !sb.editing {
-                            let (min, _max, _step) = parse_spinbox_range(&p_new.2);
-                            let val = p_new.1.parse::<i32>().unwrap_or(min);
-                            sb.value = val;
-                        }
-                    }
-                }
-            }
-        }
-        self.update_slider_rects();
-    }
-
-    fn unfocus(&mut self) {
-        if let Some(idx) = self.focused_param {
-            if idx < self.display_params.len() {
-                let p = &mut self.display_params[idx];
-                if p.2.starts_with("spinbox") {
-                    if let Some(sb) = &mut self.spinboxes[idx] {
-                        sb.unfocus();
-                        p.1 = sb.value.to_string();
-                    }
-                } else if p.2.starts_with("slider") {
-                    if let Some(s) = &mut self.sliders[idx] {
-                        s.unfocus();
-                        let (min, max) = parse_slider_range(&p.2);
-                        let new_val = min + s.value * (max - min);
-                        p.1 = format!("{:.2}", new_val);
-                    }
-                } else if p.2.starts_with("float3") {
-                    if let Some(f) = &mut self.float3s[idx] {
-                        f.unfocus();
-                        let (min, max) = parse_slider_range(&p.2);
-                        let val0 = min + f.values[0] * (max - min);
-                        let val1 = min + f.values[1] * (max - min);
-                        let val2 = min + f.values[2] * (max - min);
-                        p.1 = format!("{:.2}:{:.2}:{:.2}", val0, val1, val2);
-                    }
-                }
-            }
-        }
-        self.focused_param = None;
-    }
-
-    fn node_params(&self) -> Vec<(String, String, String)> {
-        self.display_params.clone()
-    }
-
-    fn draggable(&self) -> bool {
-        self.dragging_param.is_some() 
-            || self.display_params.iter().any(|p| p.2.starts_with("slider") || p.2.starts_with("float3"))
-    }
-
-    fn is_dragging(&self) -> bool {
-        self.dragging_param.is_some()
-    }
-
-    fn drag_begin(&mut self, px: f32, py: f32) {
-        let rects = self.get_param_rects();
-        for (i, p) in self.display_params.iter().enumerate() {
-            if p.2.starts_with("slider") {
-                let r = rects[i];
-                let row_y = r.1;
-                if py >= row_y - 2.0 && py <= row_y + 18.0 {
-                    if let Some(s) = &mut self.sliders[i] {
-                        s.drag_begin(px, py);
-                        self.dragging_param = Some(i);
-                        break;
-                    }
-                }
-            } else if p.2.starts_with("float3") {
-                let r = rects[i];
-                if py >= r.1 && py <= r.1 + r.3 {
-                    if let Some(f) = &mut self.float3s[i] {
-                        if f.mouse_input(MouseButton::Left, ElementState::Pressed, px, py) {
-                            self.dragging_param = Some(i);
-                            break;
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    fn drag_update(&mut self, px: f32, py: f32) -> bool {
-        if let Some(i) = self.dragging_param {
-            if let Some(s) = &mut self.sliders[i] {
-                if s.drag_update(px, py) {
-                    let (min, max) = parse_slider_range(&self.display_params[i].2);
-                    let new_val = min + s.value * (max - min);
-                    let old_val = &self.display_params[i].1;
-                    let new_val_str = format!("{:.2}", new_val);
-                    if *old_val != new_val_str {
-                        self.display_params[i].1 = new_val_str;
-                        return true;
-                    }
-                }
-            } else if let Some(f) = &mut self.float3s[i] {
-                if f.drag_update(px, py) {
-                    let (min, max) = parse_slider_range(&self.display_params[i].2);
-                    let val0 = min + f.values[0] * (max - min);
-                    let val1 = min + f.values[1] * (max - min);
-                    let val2 = min + f.values[2] * (max - min);
-                    let new_val_str = format!("{:.2}:{:.2}:{:.2}", val0, val1, val2);
-                    let old_val = &self.display_params[i].1;
-                    if *old_val != new_val_str {
-                        self.display_params[i].1 = new_val_str;
-                        return true;
-                    }
-                }
-            }
-        }
-        false
-    }
-
-    fn drag_end(&mut self) {
-        if let Some(i) = self.dragging_param.take() {
-            if let Some(s) = &mut self.sliders[i] {
-                s.drag_end();
-            } else if let Some(f) = &mut self.float3s[i] {
-                f.drag_end();
-            }
-        }
-    }
-
-    fn on_cursor_moved(&mut self, px: f32, py: f32) -> bool {
-        self.mouse_pos = Some((px, py));
-        true
-    }
-
-    fn mouse_input(&mut self, button: MouseButton, state: ElementState, px: f32, py: f32) -> bool {
-        if button == MouseButton::Left && state == ElementState::Pressed {
-            let rects = self.get_param_rects();
-            let mut clicked_any_focusable = false;
-            for (i, p) in self.display_params.iter_mut().enumerate() {
-                if p.2 == "code" {
-                    let r = rects[i];
-                    if px >= r.0 && px <= r.0 + r.2 && py >= r.1 + 18.0 && py <= r.1 + r.3 {
-                        self.focused_param = Some(i);
-                        clicked_any_focusable = true;
-                        break;
-                    }
-                } else if p.2 == "text" {
-                    let box_x = self.x + 100.0;
-                    let box_w = (self.w - 100.0 - 16.0).max(10.0);
-                    let r = rects[i];
-                    if px >= box_x && px <= box_x + box_w && py >= r.1 && py <= r.1 + r.3 {
-                        self.focused_param = Some(i);
-                        clicked_any_focusable = true;
-                        break;
-                    }
-                } else if p.2.starts_with("spinbox") {
-                    if let Some(sb) = &mut self.spinboxes[i] {
-                        if sb.mouse_input(button, state, px, py) {
-                            p.1 = sb.value.to_string();
-                            if sb.editing {
-                                self.focused_param = Some(i);
-                            } else {
-                                self.unfocus();
-                            }
-                            return true;
-                        }
-                    }
-                } else if p.2.starts_with("slider") {
-                    let r = rects[i];
-                    if py >= r.1 && py <= r.1 + r.3 {
-                        if let Some(s) = &mut self.sliders[i] {
-                            if s.mouse_input(button, state, px, py) {
-                                if s.editing {
-                                    self.focused_param = Some(i);
-                                    clicked_any_focusable = true;
-                                }
-                                break;
-                            }
-                        }
-                    }
-                } else if p.2.starts_with("float3") {
-                    let r = rects[i];
-                    if py >= r.1 && py <= r.1 + r.3 {
-                        if let Some(f) = &mut self.float3s[i] {
-                            if f.mouse_input(button, state, px, py) {
-                                if f.editing_idx.is_some() {
-                                    self.focused_param = Some(i);
-                                    clicked_any_focusable = true;
-                                }
-                                break;
-                            }
-                        }
-                    }
-                }
-            }
-            if !clicked_any_focusable {
-                self.unfocus();
-            }
-            return true;
-        }
-        false
-    }
-
-    fn keyboard_input(&mut self, event: &KeyEvent) -> bool {
-        if let Some(idx) = self.focused_param {
-            if event.state == ElementState::Pressed {
-                let p = &mut self.display_params[idx];
-                if p.2 == "code" {
-                    match &event.logical_key {
-                        Key::Named(NamedKey::Backspace) => {
-                            if !p.1.is_empty() {
-                                p.1.pop();
-                                return true;
-                            }
-                        }
-                        Key::Named(NamedKey::Enter) => {
-                            p.1.push('\n');
-                            return true;
-                        }
-                        Key::Named(NamedKey::Escape) => {
-                            self.focused_param = None;
-                            return true;
-                        }
-                        Key::Character(s) => {
-                            p.1.push_str(s);
-                            return true;
-                        }
-                        _ => {}
-                    }
-                } else if p.2 == "text" {
-                    match &event.logical_key {
-                        Key::Named(NamedKey::Backspace) => {
-                            if !p.1.is_empty() {
-                                p.1.pop();
-                                return true;
-                            }
-                        }
-                        Key::Named(NamedKey::Enter) | Key::Named(NamedKey::Escape) => {
-                            self.focused_param = None;
-                            return true;
-                        }
-                        Key::Character(s) => {
-                            p.1.push_str(s);
-                            return true;
-                        }
-                        _ => {}
-                    }
-                } else if p.2.starts_with("spinbox") {
-                    if let Some(sb) = &mut self.spinboxes[idx] {
-                        if sb.keyboard_input(event) {
-                            if !sb.editing {
-                                p.1 = sb.value.to_string();
-                                self.focused_param = None;
-                            } else {
-                                p.1 = sb.edit_buffer.clone();
-                            }
-                            return true;
-                        }
-                    }
-                } else if p.2.starts_with("slider") {
-                    if let Some(s) = &mut self.sliders[idx] {
-                        if s.keyboard_input(event) {
-                            let (min, max) = parse_slider_range(&p.2);
-                            let new_val = min + s.value * (max - min);
-                            p.1 = format!("{:.2}", new_val);
-                            if !s.editing {
-                                self.focused_param = None;
-                            }
-                            return true;
-                        }
-                    }
-                } else if p.2.starts_with("float3") {
-                    if let Some(f) = &mut self.float3s[idx] {
-                        if f.keyboard_input(event) {
-                            let (min, max) = parse_slider_range(&p.2);
-                            let val0 = min + f.values[0] * (max - min);
-                            let val1 = min + f.values[1] * (max - min);
-                            let val2 = min + f.values[2] * (max - min);
-                            p.1 = format!("{:.2}:{:.2}:{:.2}", val0, val1, val2);
-                            if f.editing_idx.is_none() {
-                                self.focused_param = None;
-                            }
-                            return true;
-                        }
-                    }
-                }
-            }
-        }
-        false
-    }
-
-    fn mouse_wheel(&mut self, delta: &MouseScrollDelta, px: f32, py: f32) -> bool {
-        let mut changed = false;
-        let rects = self.get_param_rects();
-        for (i, p) in self.display_params.iter_mut().enumerate() {
-            if p.2.starts_with("slider") {
-                let r = rects[i];
-                let row_y = r.1;
-                if py >= row_y - 2.0 && py <= row_y + 18.0 && px >= self.x && px <= self.x + self.w {
-                    if let Some(s) = &mut self.sliders[i] {
-                        let was_scroll = s.scroll_enabled;
-                        s.set_scroll(true);
-                        if s.mouse_wheel(delta, px, py) {
-                            let (min, max) = parse_slider_range(&p.2);
-                            let new_val = min + s.value * (max - min);
-                            let old_val = &p.1;
-                            let new_val_str = format!("{:.2}", new_val);
-                            if *old_val != new_val_str {
-                                p.1 = new_val_str;
-                                changed = true;
-                            }
-                        }
-                        s.set_scroll(was_scroll);
-                    }
-                }
-            } else if p.2.starts_with("float3") {
-                let r = rects[i];
-                let row_y = r.1;
-                if py >= row_y && py <= row_y + r.3 && px >= self.x && px <= self.x + self.w {
-                    if let Some(f) = &mut self.float3s[i] {
-                        let rects_inner = f.get_row_rects();
-                        for j in 0..3 {
-                            let r_inner = rects_inner[j];
-                            if py >= r_inner.1 && py <= r_inner.1 + r_inner.3 {
-                                let scroll_amount = match delta {
-                                    MouseScrollDelta::LineDelta(_x, y) => *y,
-                                    MouseScrollDelta::PixelDelta(pos) => (pos.y as f32) / 120.0,
-                                };
-                                let step = 0.02;
-                                let new_val = (f.values[j] - scroll_amount * step).clamp(0.0, 1.0);
-                                if (new_val - f.values[j]).abs() > 0.0001 {
-                                    f.values[j] = new_val;
-                                    if f.editing_idx == Some(j) {
-                                        let scaled_val = f.mins[j] + f.values[j] * (f.maxs[j] - f.mins[j]);
-                                        f.edit_buffer = format!("{:.2}", scaled_val);
-                                    }
-                                    let (min, max) = parse_slider_range(&p.2);
-                                    let val0 = min + f.values[0] * (max - min);
-                                    let val1 = min + f.values[1] * (max - min);
-                                    let val2 = min + f.values[2] * (max - min);
-                                    let new_val_str = format!("{:.2}:{:.2}:{:.2}", val0, val1, val2);
-                                    if p.1 != new_val_str {
-                                        p.1 = new_val_str;
-                                        changed = true;
-                                    }
-                                }
-                            }
-                        }
-                    }
-                }
-            } else if p.2.starts_with("spinbox") {
-                let r = rects[i];
-                let row_y = r.1;
-                if py >= row_y && py <= row_y + r.3 && px >= self.x && px <= self.x + self.w {
-                    if let Some(sb) = &mut self.spinboxes[i] {
-                        let scroll_amount = match delta {
-                            MouseScrollDelta::LineDelta(_x, y) => *y as i32,
-                            MouseScrollDelta::PixelDelta(pos) => {
-                                let dy = pos.y;
-                                if dy > 0.0 { 1 } else if dy < 0.0 { -1 } else { 0 }
-                            }
-                        };
-                        let new_val = (sb.value + scroll_amount * sb.step).clamp(sb.min, sb.max);
-                        if sb.value != new_val {
-                            sb.value = new_val;
-                            p.1 = new_val.to_string();
-                            changed = true;
-                        }
-                    }
-                }
-            }
-        }
-        changed
-    }
-
-    fn extra_quads(&self) -> Vec<(f32, f32, f32, f32, [f32; 4])> {
-        if !self.visible {
-            return Vec::new();
-        }
-        let mut quads = Vec::new();
-        let rects = self.get_param_rects();
-
-        // Find sections and their ranges
-        let mut sections = Vec::new();
-        let mut current_section: Option<(usize, usize)> = None;
-        let mut in_section = false;
-        for (i, p) in self.display_params.iter().enumerate() {
-            if p.2 == "section" {
-                if let Some((start, end)) = current_section {
-                    sections.push((start, end));
-                }
-                current_section = None;
-                in_section = true;
-            } else {
-                if in_section {
-                    if let Some((_, ref mut end)) = current_section {
-                        *end = i;
-                    } else {
-                        current_section = Some((i, i));
-                    }
-                }
-            }
-        }
-        if let Some((start, end)) = current_section {
-            sections.push((start, end));
-        }
-
-        // Draw section border boxes
-        for (start, end) in sections {
-            if start <= end && start < rects.len() && end < rects.len() {
-                let r_start = rects[start];
-                let r_end = rects[end];
-                let bx = self.x + 4.0;
-                let bw = self.w - 8.0;
-                let by = r_start.1 - 4.0;
-                let bh = (r_end.1 + r_end.3 + 4.0) - by;
-                
-                let border_color = [0.18, 0.18, 0.27, 1.0];
-                let border_t = 1.0;
-                
-                // Top border
-                quads.push((bx, by, bw, border_t, border_color));
-                // Bottom border
-                quads.push((bx, by + bh - border_t, bw, border_t, border_color));
-                // Left border
-                quads.push((bx, by, border_t, bh, border_color));
-                // Right border
-                quads.push((bx + bw - border_t, by, border_t, bh, border_color));
-            }
-        }
-
-        for (i, p) in self.display_params.iter().enumerate() {
-            let r = rects[i];
-            if p.2.starts_with("slider") {
-                if let Some(s) = &self.sliders[i] {
-                    let (sx, sy, sw, sh) = s.rect();
-                    quads.push((sx, sy, sw, sh, s.color()));
-                    quads.extend(s.extra_quads());
-                }
-            } else if p.2 == "section" {
-                // Section header line is handled by the border box top border now
-            } else if p.2.starts_with("float3") {
-                if let Some(f) = &self.float3s[i] {
-                    quads.extend(f.extra_quads());
-                }
-            } else if p.2 == "code" {
-                quads.push((r.0, r.1 + 18.0, r.2, r.3 - 18.0, [0.08, 0.08, 0.10, 1.0]));
-                let border_color = if self.focused_param == Some(i) {
-                    [0.25, 0.45, 0.85, 1.0]
-                } else {
-                    [0.20, 0.20, 0.25, 1.0]
-                };
-                let (bx, by, bw, bh) = (r.0, r.1 + 18.0, r.2, r.3 - 18.0);
-                quads.push((bx, by, bw, 1.0, border_color));
-                quads.push((bx, by + bh - 1.0, bw, 1.0, border_color));
-                quads.push((bx, by, 1.0, bh, border_color));
-                quads.push((bx + bw - 1.0, by, 1.0, bh, border_color));
-            } else if p.2 == "text" {
-                let box_x = self.x + 100.0;
-                let box_w = (self.w - 100.0 - 16.0).max(10.0);
-                quads.push((box_x, r.1, box_w, r.3, [0.08, 0.08, 0.10, 1.0]));
-                let border_color = if self.focused_param == Some(i) {
-                    [0.25, 0.45, 0.85, 1.0]
-                } else {
-                    [0.20, 0.20, 0.25, 1.0]
-                };
-                let (bx, by, bw, bh) = (box_x, r.1, box_w, r.3);
-                let border_t = 1.0;
-                quads.push((bx, by, bw, border_t, border_color));
-                quads.push((bx, by + bh - border_t, bw, border_t, border_color));
-                quads.push((bx, by, border_t, bh, border_color));
-                quads.push((bx + bw - border_t, by, border_t, bh, border_color));
-            } else if p.2.starts_with("spinbox") {
-                if let Some(sb) = &self.spinboxes[i] {
-                    quads.push((sb.base.x, sb.base.y, sb.base.w, sb.base.h, sb.color()));
-                    quads.extend(sb.extra_quads());
-                }
-            }
-        }
-        quads
-    }
-
-    fn text_labels(&self) -> Vec<TextLabel> {
-        if !self.visible {
-            return Vec::new();
-        }
-        let rects = self.get_param_rects();
-        let mut labels = Vec::new();
-        for (i, (name, value, ptype)) in self.display_params.iter().enumerate() {
-            let r = rects[i];
-            if ptype.starts_with("slider") {
-                labels.push(TextLabel {
-                    text: name.clone(),
-                    x: self.x + 8.0,
-                    y: r.1,
-                    font_size: 12.0,
-                    color: [0xaa, 0xaa, 0xbb],
-                });
-                if let Some(s) = &self.sliders[i] {
-                    labels.extend(s.text_labels());
-                }
-            } else if ptype.starts_with("float3") {
-                if let Some(f) = &self.float3s[i] {
-                    labels.extend(f.text_labels());
-                }
-            } else if ptype == "section" {
-                labels.push(TextLabel {
-                    text: name.clone(),
-                    x: self.x + 12.0,
-                    y: r.1 + 2.0,
-                    font_size: 13.0,
-                    color: [0xee, 0xee, 0xf0],
-                });
-            } else if ptype == "code" {
-                labels.push(TextLabel {
-                    text: format!("{}:\n{}", name, value),
-                    x: self.x + 12.0,
-                    y: r.1,
-                    font_size: 12.0,
-                    color: [0xaa, 0xaa, 0xbb],
-                });
-            } else if ptype.starts_with("spinbox") {
-                labels.push(TextLabel {
-                    text: name.clone(),
-                    x: self.x + 8.0,
-                    y: r.1 + (r.3 - 12.0) / 2.0 - 2.0,
-                    font_size: 12.0,
-                    color: [0xaa, 0xaa, 0xbb],
-                });
-                if let Some(sb) = &self.spinboxes[i] {
-                    labels.extend(sb.text_labels());
-                }
-            } else if ptype == "text" {
-                labels.push(TextLabel {
-                    text: name.clone(),
-                    x: self.x + 8.0,
-                    y: r.1 + (r.3 - 12.0) / 2.0 - 2.0,
-                    font_size: 12.0,
-                    color: [0xaa, 0xaa, 0xbb],
-                });
-                let val_text = if self.focused_param == Some(i) {
-                    format!("{}|", value)
-                } else {
-                    value.clone()
-                };
-                labels.push(TextLabel {
-                    text: val_text,
-                    x: self.x + 106.0,
-                    y: r.1 + (r.3 - 12.0) / 2.0 - 2.0,
-                    font_size: 12.0,
-                    color: [0xee, 0xee, 0xf0],
-                });
-            } else {
-                labels.push(TextLabel {
-                    text: format!("{}: {}", name, value),
-                    x: self.x + 8.0,
-                    y: r.1,
-                    font_size: 12.0,
-                    color: [0xaa, 0xaa, 0xbb],
-                });
-            }
-        }
-        labels
-    }
-}
-
-pub struct MenuBar {
-    x: f32, y: f32, w: f32, h: f32,
-    hovering: bool,
-    pub title: String,
-    pub menus: Vec<Box<Menu>>,
-    pub menu_items: Vec<String>,
-    pub vertical_items: Vec<String>,
-    pub menu_dropdowns: Vec<Vec<String>>,
-    pub menu_dropdown_checked: Vec<Vec<Option<bool>>>,
-    pub hovered_menu: Option<usize>,
-    pub open_menu: Option<usize>,
-    pub hovered_dropdown: Option<usize>,
-    pub clicked_dropdown: Option<(usize, usize)>,
-    pub was_open: Option<usize>,
-    pub vertical: bool,
-    pub visible: bool,
-    pub focused: bool,
-    pub z_level: i32,
-    pub center_items: bool,
-    pub curved_circle: Option<(f32, f32, f32)>,
-    pub title_pos: Option<(f32, f32)>,
-    pub title_buf: Option<glyphon::Buffer>,
-    pub curved_title_char_bufs: Vec<glyphon::Buffer>,
-    pub network_opacity: f32,
-}
-
-impl MenuBar {
-    pub fn new(x: f32, y: f32, w: f32, h: f32) -> Self {
-        Self {
-            x, y, w, h, hovering: false,
-            title: String::new(),
-            menus: Vec::new(),
-            menu_items: Vec::new(),
-            vertical_items: Vec::new(),
-            menu_dropdowns: Vec::new(),
-            menu_dropdown_checked: Vec::new(),
-            hovered_menu: None,
-            open_menu: None,
-            hovered_dropdown: None,
-            clicked_dropdown: None,
-            was_open: None,
-            vertical: false,
-            visible: true,
-            focused: false,
-            z_level: 100,
-            center_items: false,
-            curved_circle: None,
-            title_pos: None,
-            title_buf: None,
-            curved_title_char_bufs: Vec::new(),
-            network_opacity: 1.0,
-        }
-    }
-
-    pub fn with_center_items(mut self, center: bool) -> Self {
-        self.center_items = center;
-        self
-    }
-
-    pub fn with_title(mut self, title: &str) -> Self {
-        self.title = title.to_string();
-        self
-    }
-
-    pub fn with_item(mut self, label: &str, items: &[&str]) -> Self {
-        self.menu_items.push(label.to_string());
-        self.vertical_items.push(label.to_string());
-        self.menu_dropdowns.push(items.iter().map(|s| s.to_string()).collect());
-        self.menu_dropdown_checked.push(vec![None; items.len()]);
-
-        let item_strs: Vec<String> = items.iter().map(|s| s.to_string()).collect();
-        let mut menu = Menu::new(label, label, &item_strs);
-        menu.vertical = self.vertical;
-        self.menus.push(Box::new(menu));
-        self
-    }
-
-    pub fn with_item_vh(mut self, horizontal_label: &str, vertical_label: &str, items: &[&str]) -> Self {
-        self.menu_items.push(horizontal_label.to_string());
-        self.vertical_items.push(vertical_label.to_string());
-        self.menu_dropdowns.push(items.iter().map(|s| s.to_string()).collect());
-        self.menu_dropdown_checked.push(vec![None; items.len()]);
-
-        let item_strs: Vec<String> = items.iter().map(|s| s.to_string()).collect();
-        let mut menu = Menu::new(horizontal_label, vertical_label, &item_strs);
-        menu.vertical = self.vertical;
-        self.menus.push(Box::new(menu));
-        self
-    }
-
-    pub fn with_vertical(mut self, vertical: bool) -> Self {
-        self.vertical = vertical;
-        for menu in &mut self.menus {
-            menu.vertical = vertical;
-        }
-        self
-    }
-
-    pub fn with_z_index(mut self, z: i32) -> Self {
-        self.z_level = z;
-        self
-    }
-
-    fn item_y_vertical(&self, idx: usize) -> f32 {
-        let mut y = 8.0;
-        if !self.title.is_empty() {
-            y += 24.0;
-        }
-        y + idx as f32 * 24.0
-    }
-
-    fn item_h_vertical(&self) -> f32 {
-        24.0
-    }
-}
-
-impl Element for MenuBar {
-    fn rect(&self) -> (f32, f32, f32, f32) {
-        if !self.visible {
-            return (0.0, 0.0, 0.0, 0.0);
-        }
-        if self.vertical {
-            let total_h = if self.menus.is_empty() {
-                self.h
-            } else {
-                let last_idx = self.menus.len() - 1;
-                self.item_y_vertical(last_idx) + self.item_h_vertical()
-            };
-            (self.x, self.y, self.w, total_h)
-        } else {
-            (self.x, self.y, self.w, self.h)
-        }
-    }
-
-    fn set_curved_circle(&mut self, circle: Option<(f32, f32, f32)>) {
-        self.curved_circle = circle;
-        if circle.is_none() {
-            for menu in &mut self.menus {
-                menu.curved_arc = None;
-            }
-        }
-    }
-
-    fn set_rect(&mut self, x: f32, y: f32, w: f32, h: f32) {
-        self.x = x;
-        self.y = y;
-        self.w = w;
-        self.h = h;
-
-        let parent_ptr = self as *mut MenuBar as *mut (dyn Element + 'static);
-
-        if self.vertical {
-            let mut cy = 8.0;
-            if !self.title.is_empty() {
-                cy += 24.0;
-            }
-            for menu in &mut self.menus {
-                let ih = 24.0;
-                menu.set_rect(x, y + cy, w, ih);
-                menu.set_parent(Some(parent_ptr));
-                cy += ih;
-            }
-        } else {
-            if let Some((ccx, ccy, ccr)) = self.curved_circle {
-                let r_mid = ccr - h / 2.0;
-                let mut total_width = 8.0;
-                if !self.title.is_empty() {
-                    total_width += self.title.len() as f32 * 7.5 + 24.0;
-                }
-                for menu in &self.menus {
-                    total_width += menu.active_title().len() as f32 * 7.5 + 16.0;
-                }
-                
-                let total_angular_width = total_width / r_mid;
-                let start_angle = 1.5 * std::f32::consts::PI - total_angular_width / 2.0;
-                let mut current_angle = start_angle;
-                
-                if !self.title.is_empty() {
-                    let title_w = self.title.len() as f32 * 7.5 + 24.0;
-                    let dtheta_title = title_w / r_mid;
-                    let theta_title = current_angle + dtheta_title / 2.0;
-                    
-                    let tx = ccx + r_mid * theta_title.cos() - title_w / 2.0 + 8.0;
-                    let ty = ccy + r_mid * theta_title.sin() - h / 2.0;
-                    self.title_pos = Some((tx, ty));
-                    current_angle += dtheta_title;
-                } else {
-                    self.title_pos = None;
-                }
-                
-                for menu in &mut self.menus {
-                    let iw = menu.active_title().len() as f32 * 7.5 + 16.0;
-                    let dtheta_menu = iw / r_mid;
-                    let theta_menu = current_angle + dtheta_menu / 2.0;
-                    
-                    let mx = ccx + r_mid * theta_menu.cos() - iw / 2.0;
-                    let my = ccy + r_mid * theta_menu.sin() - h / 2.0;
-                    
-                    menu.set_rect(mx, my, iw, h);
-                    menu.set_parent(Some(parent_ptr));
-                    menu.curved_arc = Some((ccx, ccy, ccr, h, current_angle, current_angle + dtheta_menu));
-                    current_angle += dtheta_menu;
-                }
-            } else {
-                self.title_pos = None;
-                let mut cx = 8.0;
-                if self.center_items {
-                    let mut total_width = 8.0;
-                    if !self.title.is_empty() {
-                        total_width += self.title.len() as f32 * 7.5 + 24.0;
-                    }
-                    for menu in &self.menus {
-                        total_width += menu.active_title().len() as f32 * 7.5 + 16.0;
-                    }
-                    if self.w > total_width {
-                        cx = (self.w - total_width) / 2.0;
-                    }
-                }
-                if !self.title.is_empty() {
-                    cx += self.title.len() as f32 * 7.5 + 24.0;
-                }
-                for menu in &mut self.menus {
-                    let iw = menu.active_title().len() as f32 * 7.5 + 16.0;
-                    menu.set_rect(x + cx, y, iw, h);
-                    menu.set_parent(Some(parent_ptr));
-                    cx += iw;
-                }
-            }
-        }
-    }
-
-    fn set_network_opacity(&mut self, opacity: f32) {
-        self.network_opacity = opacity;
-    }
-
-    fn color(&self) -> [f32; 4] {
-        if !self.visible {
-            [0.0, 0.0, 0.0, 0.0]
-        } else if self.focused {
-            let mut c = colors::PANEL_MENU_FOCUSED;
-            c[3] *= self.network_opacity;
-            c
-        } else {
-            let mut c = colors::PANEL_MENU_BG;
-            c[3] *= self.network_opacity;
-            c
-        }
-    }
-
-    fn set_hovered(&mut self, v: bool) {
-        self.hovering = v;
-    }
-
-    fn hovered(&self) -> bool {
-        self.hovering
-    }
-
-    fn hit_test(&self, px: f32, py: f32) -> bool {
-        if !self.visible {
-            return false;
-        }
-        if crate::widget::popovers::is_coordinate_covered(self as *const Self as *const () as usize, px, py) {
-            return false;
-        }
-        if let Some((ccx, ccy, ccr)) = self.curved_circle {
-            let dx = px - ccx;
-            let dy = py - ccy;
-            let dist = (dx * dx + dy * dy).sqrt();
-            if dist >= ccr - self.h && dist <= ccr {
-                let angle = dy.atan2(dx);
-                let mut norm_angle = angle;
-                if norm_angle < 0.0 {
-                    norm_angle += 2.0 * std::f32::consts::PI;
-                }
-                
-                let r_mid = ccr - self.h / 2.0;
-                let mut total_width = 8.0;
-                if !self.title.is_empty() {
-                    total_width += self.title.len() as f32 * 7.5 + 24.0;
-                }
-                for menu in &self.menus {
-                    total_width += menu.active_title().len() as f32 * 7.5 + 16.0;
-                }
-                let total_angular_width = total_width / r_mid;
-                let start_angle = 1.5 * std::f32::consts::PI - total_angular_width / 2.0;
-                let end_angle = 1.5 * std::f32::consts::PI + total_angular_width / 2.0;
-                
-                if norm_angle >= start_angle && norm_angle <= end_angle {
-                    return true;
-                }
-            }
-            for menu in &self.menus {
-                if menu.hit_test(px, py) {
-                    return true;
-                }
-            }
-            return false;
-        }
-        let (rx, ry, rw, rh) = self.rect();
-        if px >= rx && px <= rx + rw && py >= ry && py <= ry + rh {
-            return true;
-        }
-        for menu in &self.menus {
-            if menu.hit_test(px, py) {
-                return true;
-            }
-        }
-        false
-    }
-
-    fn on_cursor_moved(&mut self, px: f32, py: f32) -> bool {
-        if !self.visible {
-            return false;
-        }
-        let (rx, ry, rw, rh) = self.rect();
-        self.set_rect(rx, ry, rw, rh);
-
-        if let Ok(mut f) = std::fs::OpenOptions::new().create(true).append(true).open("/home/lsgalante/Dropbox/Clear/debug.txt") {
-            use std::io::Write;
-            let _ = writeln!(f, "MenuBar::on_cursor_moved px={}, py={} curved={:?} rect={:?}", px, py, self.curved_circle, (rx, ry, rw, rh));
-        }
-
-        let mut changed = false;
-        self.hovered_menu = None;
-        for (idx, menu) in self.menus.iter_mut().enumerate() {
-            if menu.cursor_moved(px, py) {
-                changed = true;
-            }
-            if let Ok(mut f) = std::fs::OpenOptions::new().create(true).append(true).open("/home/lsgalante/Dropbox/Clear/debug.txt") {
-                use std::io::Write;
-                let _ = writeln!(f, "  Menu[{}] active_title={} curved={:?} hovered={} hit={}", idx, menu.active_title(), menu.curved_arc, menu.hovered(), menu.hit_test(px, py));
-            }
-            if menu.hovered() {
-                self.hovered_menu = Some(idx);
-            }
-        }
-        changed
-    }
-
-    fn mouse_input(&mut self, button: MouseButton, state: ElementState, px: f32, py: f32) -> bool {
-        if !self.visible {
-            return false;
-        }
-        let (rx, ry, rw, rh) = self.rect();
-        self.set_rect(rx, ry, rw, rh);
-
-        let mut changed = false;
-        for menu in &mut self.menus {
-            let res = menu.mouse_input(button, state, px, py);
-            if res {
-                changed = true;
-            }
-        }
-        if !self.is_menu_open() {
-            self.unfocus();
-        }
-        changed
-    }
-
-    fn focus(&mut self) {
-        if self.is_menu_open() {
-            self.focused = true;
-            for menu in &mut self.menus {
-                if menu.is_menu_open() {
-                    menu.focus();
-                    return;
-                }
-            }
-        } else {
-            self.focused = false;
-            focus::clear_if_matches(self);
-            return;
-        }
-        self.focused = true;
-        focus::set_focused(self);
-    }
-
-    fn unfocus(&mut self) {
-        self.focused = false;
-        focus::clear_if_matches(self);
-        for menu in &mut self.menus {
-            menu.unfocus();
-        }
-    }
-
-    fn focused(&self) -> bool {
-        self.focused || self.is_menu_open()
-    }
-
-    fn set_selected(&mut self, selected: bool) {
-        self.focused = selected;
-        if !selected {
-            for menu in &mut self.menus {
-                menu.set_selected(false);
-            }
-        }
-    }
-
-    fn set_modifiers(&mut self, ctrl: bool, shift: bool, alt: bool) {
-        for menu in &mut self.menus {
-            menu.set_modifiers(ctrl, shift, alt);
-        }
-    }
-
-    fn menu_names(&self) -> Vec<String> {
-        self.menu_items.clone()
-    }
-
-    fn menu_click(&mut self) -> Option<(usize, usize)> {
-        for (idx, menu) in self.menus.iter_mut().enumerate() {
-            if let Some((_, item_idx)) = menu.menu_click() {
-                return Some((idx, item_idx));
-            }
-        }
-        None
-    }
-
-    fn set_item_checked(&mut self, menu_idx: usize, item_idx: usize, checked: bool) {
-        if let Some(menu) = self.menu_dropdown_checked.get_mut(menu_idx) {
-            if item_idx < menu.len() {
-                menu[item_idx] = Some(checked);
-            }
-        }
-        if let Some(menu) = self.menus.get_mut(menu_idx) {
-            menu.set_item_checked(0, item_idx, checked);
-        }
-    }
-
-    fn set_menu_items(&mut self, menu_idx: usize, items: &[String]) {
-        if menu_idx < self.menu_dropdowns.len() {
-            self.menu_dropdowns[menu_idx] = items.to_vec();
-            self.menu_dropdown_checked[menu_idx] = vec![Some(false); items.len()];
-        }
-        if let Some(menu) = self.menus.get_mut(menu_idx) {
-            menu.items = items.to_vec();
-            menu.item_checked = vec![Some(false); items.len()];
-            menu.item_bufs.clear();
-        }
-    }
-
-    fn is_menu_bar(&self) -> bool {
-        self.visible
-    }
-
-    fn get_menu_items_at(&self, px: f32, py: f32) -> Option<(usize, String, Vec<String>, f32, f32, f32, f32)> {
-        if !self.visible {
-            return None;
-        }
-        for (idx, menu) in self.menus.iter().enumerate() {
-            if menu.hit_test(px, py) {
-                let mut formatted_items = Vec::new();
-                for (i, item) in menu.items.iter().enumerate() {
-                    let checked = menu.item_checked.get(i).and_then(|&v| v);
-                    let prefix = match checked {
-                        Some(true) => "✓ ",
-                        Some(false) => "  ",
-                        None => "",
-                    };
-                    formatted_items.push(format!("{}{}", prefix, item));
-                }
-                return Some((idx, menu.active_title().to_string(), formatted_items, menu.base.x, menu.base.y, menu.base.w, menu.base.h));
-            }
-        }
-        None
-    }
-
-    fn trigger_menu_click(&mut self, menu_idx: usize, item_idx: usize) {
-        if let Some(menu) = self.menus.get_mut(menu_idx) {
-            menu.clicked_item = Some(item_idx);
-        }
-    }
-
-    fn is_menu_open(&self) -> bool {
-        self.visible && self.menus.iter().any(|m| m.is_menu_open())
-    }
-
-    fn extra_quads(&self) -> Vec<(f32, f32, f32, f32, [f32; 4])> {
-        if !self.visible {
-            return Vec::new();
-        }
-        let mut quads = Vec::new();
-        for menu in &self.menus {
-            quads.extend(menu.all_quads());
-        }
-        quads
-    }
-
-    fn extra_arcs(&self) -> Vec<(f32, f32, f32, f32, f32, f32, [f32; 4])> {
-        if !self.visible {
-            return Vec::new();
-        }
-        let mut arcs = Vec::new();
-        for menu in &self.menus {
-            arcs.extend(menu.extra_arcs());
-        }
-        arcs
-    }
-
-    fn prepare_text(&mut self, fs: &mut glyphon::FontSystem) {
-        if !self.visible {
-            return;
-        }
-        if !self.title.is_empty() {
-            if let Some((_ccx, _ccy, _ccr)) = self.curved_circle {
-                if self.curved_title_char_bufs.len() != self.title.chars().count() {
-                    self.curved_title_char_bufs = self.title.chars()
-                        .map(|c| make_widget_text_buffer(fs, &c.to_string(), 12.0, "Outfit"))
-                        .collect();
-                }
-                self.title_buf = None;
-            } else {
-                if self.title_buf.is_none() {
-                    self.title_buf = Some(make_widget_text_buffer(fs, &self.title, 12.0, "Outfit"));
-                }
-                self.curved_title_char_bufs.clear();
-            }
-        } else {
-            self.title_buf = None;
-            self.curved_title_char_bufs.clear();
-        }
-        for menu in &mut self.menus {
-            menu.prepare_text(fs);
-        }
-    }
-
-    fn get_text_items(&self) -> Vec<(&glyphon::Buffer, f32, f32, glyphon::Color)> {
-        if !self.visible {
-            return Vec::new();
-        }
-        let mut items = Vec::new();
-        let color = glyphon::Color::rgb(0xaa, 0xaa, 0xbb);
-
-        if let Some((ccx, ccy, ccr)) = self.curved_circle {
-            let r_mid = ccr - self.h / 2.0;
-            let mut total_width = 8.0;
-            if !self.title.is_empty() {
-                total_width += self.title.len() as f32 * 7.5 + 24.0;
-            }
-            for menu in &self.menus {
-                total_width += menu.active_title().len() as f32 * 7.5 + 16.0;
-            }
-            let total_angular_width = total_width / r_mid;
-            let start_angle = 1.5 * std::f32::consts::PI - total_angular_width / 2.0;
-            let current_angle = start_angle;
-
-            if !self.title.is_empty() {
-                let title_w = self.title.len() as f32 * 7.5 + 24.0;
-                let dtheta_title = title_w / r_mid;
-
-                let char_widths: Vec<f32> = self.title.chars().map(|c| {
-                    TextLabel::estimate_width(&c.to_string(), 12.0)
-                }).collect();
-                let total_chars_width: f32 = char_widths.iter().sum();
-                let mid_angle = (current_angle + current_angle + dtheta_title) / 2.0;
-                let angular_width = total_chars_width / r_mid;
-                let text_start_angle = mid_angle - angular_width / 2.0;
-                let mut cur_char_angle = text_start_angle;
-
-                for (char_idx, c_buf) in self.curved_title_char_bufs.iter().enumerate() {
-                    let cw = char_widths[char_idx];
-                    let dtheta = cw / r_mid;
-                    let char_center_angle = cur_char_angle + dtheta / 2.0;
-
-                    let tx = ccx + r_mid * char_center_angle.cos() - cw / 2.0;
-                    let ty = ccy + r_mid * char_center_angle.sin() - 12.0 / 2.0;
-
-                    items.push((c_buf, tx, ty, color));
-                    cur_char_angle += dtheta;
-                }
-            }
-        } else {
-            let mut start_x = 8.0;
-            if self.center_items {
-                let mut total_width = 8.0;
-                if !self.title.is_empty() {
-                    total_width += self.title.len() as f32 * 7.5 + 24.0;
-                }
-                for menu in &self.menus {
-                    total_width += menu.active_title().len() as f32 * 7.5 + 16.0;
-                }
-                if self.w > total_width {
-                    start_x = (self.w - total_width) / 2.0;
-                }
-            }
-            if let Some(ref title_buf) = self.title_buf {
-                items.push((title_buf, self.x + start_x, self.y + 7.0, color));
-            }
-        }
-
-        for menu in &self.menus {
-            items.extend(menu.get_text_items());
-        }
-        items
-    }
-
-    fn text_labels(&self) -> Vec<TextLabel> {
-        if !self.visible {
-            return Vec::new();
-        }
-        let mut labels = Vec::new();
-        if let Some((ccx, ccy, ccr)) = self.curved_circle {
-            let r_mid = ccr - self.h / 2.0;
-            let mut total_width = 8.0;
-            if !self.title.is_empty() {
-                total_width += self.title.len() as f32 * 7.5 + 24.0;
-            }
-            for menu in &self.menus {
-                total_width += menu.active_title().len() as f32 * 7.5 + 16.0;
-            }
-            let total_angular_width = total_width / r_mid;
-            let start_angle = 1.5 * std::f32::consts::PI - total_angular_width / 2.0;
-            let current_angle = start_angle;
-
-            if !self.title.is_empty() {
-                let title_w = self.title.len() as f32 * 7.5 + 24.0;
-                let dtheta_title = title_w / r_mid;
-                labels.extend(TextLabel::curved_layout(
-                    &self.title,
-                    ccx, ccy, r_mid,
-                    current_angle, current_angle + dtheta_title,
-                    12.0,
-                    [0xaa, 0xaa, 0xbb],
-                ));
-            }
-        } else {
-            let mut start_x = 8.0;
-            if self.center_items {
-                let mut total_width = 8.0;
-                if !self.title.is_empty() {
-                    total_width += self.title.len() as f32 * 7.5 + 24.0;
-                }
-                for menu in &self.menus {
-                    total_width += menu.active_title().len() as f32 * 7.5 + 16.0;
-                }
-                if self.w > total_width {
-                    start_x = (self.w - total_width) / 2.0;
-                }
-            }
-            if !self.title.is_empty() {
-                labels.push(TextLabel {
-                    text: self.title.clone(),
-                    x: self.x + start_x,
-                    y: self.y + 7.0,
-                    font_size: 12.0,
-                    color: [0xaa, 0xaa, 0xbb],
-                });
-            }
-        }
-        for menu in &self.menus {
-            labels.extend(menu.text_labels());
-        }
-        labels
-    }
-
-    fn set_visible(&mut self, visible: bool) {
-        self.visible = visible;
-        for menu in &mut self.menus {
-            menu.set_visible(visible);
-        }
-    }
-
-    fn visible(&self) -> bool {
-        self.visible
-    }
-
-    fn children(&self) -> Vec<*mut (dyn Element + 'static)> {
-        self.menus.iter().map(|m| {
-            let ptr: *const dyn Element = &**m as &dyn Element;
-            ptr as *mut (dyn Element + 'static)
-        }).collect()
-    }
-
-    fn z_index(&self) -> i32 {
-        self.z_level
-    }
-
-    fn set_center_items(&mut self, center: bool) {
-        self.center_items = center;
-    }
-
-    fn menu_items_list(&self) -> Vec<Vec<String>> {
-        self.menu_dropdowns.clone()
-    }
-
-    fn menu_checked_list(&self) -> Vec<Vec<Option<bool>>> {
-        self.menu_dropdown_checked.clone()
-    }
-}
-
-impl Drop for MenuBar {
-    fn drop(&mut self) {
-        focus::clear_if_matches(self);
-    }
-}
-
-#[derive(Debug, Clone)]
-pub struct Menu {
-    pub base: Widget,
-    pub title: String,
-    pub vertical_title: String,
-    pub items: Vec<String>,
-    pub item_checked: Vec<Option<bool>>,
-    pub open: bool,
-    pub vertical: bool,
-    hovered_item: Option<usize>,
-    clicked_item: Option<usize>,
-    was_open: Option<usize>,
-    pub parent: Option<*mut (dyn Element + 'static)>,
-    pub children: Vec<*mut (dyn Element + 'static)>,
-    pub curved_arc: Option<(f32, f32, f32, f32, f32, f32)>,
-    pub title_buf: Option<glyphon::Buffer>,
-    pub item_bufs: Vec<glyphon::Buffer>,
-    pub check_buf: Option<glyphon::Buffer>,
-    pub curved_char_bufs: Vec<glyphon::Buffer>,
-}
-
-impl Menu {
-    pub fn new(title: &str, vertical_title: &str, items: &[String]) -> Self {
-        Self {
-            base: Widget::new(),
-            title: title.to_string(),
-            vertical_title: vertical_title.to_string(),
-            items: items.to_vec(),
-            item_checked: vec![None; items.len()],
-            open: false,
-            vertical: false,
-            hovered_item: None,
-            clicked_item: None,
-            was_open: None,
-            parent: None,
-            children: Vec::new(),
-            curved_arc: None,
-            title_buf: None,
-            item_bufs: Vec::new(),
-            check_buf: None,
-            curved_char_bufs: Vec::new(),
-        }
-    }
-
-    pub fn active_title(&self) -> &str {
-        if self.vertical {
-            &self.vertical_title
-        } else {
-            &self.title
-        }
-    }
-
-    fn dropdown_rect(&self) -> (f32, f32, f32, f32) {
-        let dh = self.items.len() as f32 * DROPDOWN_ITEM_H;
-        let mut max_len = 0;
-        for item in &self.items {
-            max_len = max_len.max(item.len());
-        }
-        let dw = (max_len as f32 * 7.5 + 40.0).max(120.0);
-        let dx = if self.vertical {
-            self.base.x + self.base.w
-        } else {
-            self.base.x
-        };
-        let dy = if self.vertical {
-            self.base.y
-        } else {
-            self.base.y + self.base.h
-        };
-        (dx, dy, dw, dh)
-    }
-}
-impl Element for Menu {
-    crate::impl_widget_base!(Menu);
-
-    fn label(&self) -> Option<String> {
-        Some(self.active_title().to_string())
-    }
-
-    fn color(&self) -> [f32; 4] {
-        [0.0, 0.0, 0.0, 0.0]
-    }
-
-    fn hit_test(&self, px: f32, py: f32) -> bool {
-        if crate::widget::popovers::is_coordinate_covered(self as *const Self as *const () as usize, px, py) {
-            return false;
-        }
-        if let Some((cx, cy, r, thickness, start_angle, end_angle)) = self.curved_arc {
-            let dx = px - cx;
-            let dy = py - cy;
-            let dist = (dx * dx + dy * dy).sqrt();
-            if dist >= r - thickness && dist <= r {
-                let angle = dy.atan2(dx);
-                let mut norm_angle = angle;
-                if norm_angle < 0.0 {
-                    norm_angle += 2.0 * std::f32::consts::PI;
-                }
-                if norm_angle >= start_angle && norm_angle <= end_angle {
-                    return true;
-                }
-            }
-            if self.open {
-                let (dx, dy, dw, dh) = self.dropdown_rect();
-                if dh > 0.0 && px >= dx && px < dx + dw && py >= dy && py < dy + dh {
-                    return true;
-                }
-            }
-            return false;
-        }
-        let (rx, ry, rw, rh) = self.rect();
-        if px >= rx && px <= rx + rw && py >= ry && py <= ry + rh {
-            return true;
-        }
-        if self.open {
-            let (dx, dy, dw, dh) = self.dropdown_rect();
-            if dh > 0.0 && px >= dx && px < dx + dw && py >= dy && py < dy + dh {
-                return true;
-            }
-        }
-        false
-    }
-
-    fn on_cursor_moved(&mut self, px: f32, py: f32) -> bool {
-        self.was_open = None;
-        let was_hovering = self.base.hovered;
-        self.base.hovered = self.hit_test(px, py);
-        let old_item = self.hovered_item;
-        self.hovered_item = None;
-
-        if self.open {
-            let (dx, dy, dw, dh) = self.dropdown_rect();
-            if px >= dx && px < dx + dw && py >= dy && py < dy + dh {
-                let di = ((py - dy) / DROPDOWN_ITEM_H) as usize;
-                if di < self.items.len() {
-                    self.hovered_item = Some(di);
-                }
-            }
-        }
-
-        was_hovering != self.base.hovered || old_item != self.hovered_item
-    }
-
-    fn mouse_input(&mut self, button: MouseButton, state: ElementState, px: f32, py: f32) -> bool {
-        if button != MouseButton::Left || state != ElementState::Pressed {
-            return false;
-        }
-        if !self.hit_test(px, py) {
-            return false;
-        }
-
-        // Check dropdown click if open
-        if self.open {
-            let (dx, dy, dw, dh) = self.dropdown_rect();
-            if dh > 0.0 && px >= dx && px < dx + dw && py >= dy && py < dy + dh {
-                let di = ((py - dy) / DROPDOWN_ITEM_H) as usize;
-                if di < self.items.len() {
-                    self.clicked_item = Some(di);
-                    self.open = false;
-                    return true;
-                }
-            }
-        }
-
-        // Since we passed hit_test and didn't click dropdown, it's a click on the header title
-        if self.was_open == Some(0) || self.open {
-            self.open = false;
-            self.was_open = None;
-        } else {
-            self.open = true;
-            self.was_open = None;
-            focus::set_focused(self);
-        }
-        true
-    }
-
-    fn focus(&mut self) {
-        self.open = true;
-        self.base.focused = true;
-        focus::set_focused(self);
-    }
-
-    fn unfocus(&mut self) {
-        if self.open {
-            self.was_open = Some(0);
-        }
-        self.open = false;
-        self.base.focused = false;
-        focus::clear_if_matches(self);
-        self.hovered_item = None;
-    }
-
-    fn set_selected(&mut self, selected: bool) {
-        self.base.focused = selected;
-        if !selected {
-            self.open = false;
-            self.was_open = None;
-            self.hovered_item = None;
-            focus::clear_if_matches(self);
-        }
-    }
-
-    fn menu_click(&mut self) -> Option<(usize, usize)> {
-        self.clicked_item.take().map(|i| (0, i))
-    }
-
-    fn set_item_checked(&mut self, _menu_idx: usize, item_idx: usize, checked: bool) {
-        if item_idx < self.item_checked.len() {
-            self.item_checked[item_idx] = Some(checked);
-            self.item_bufs.clear();
-        }
-    }
-
-    fn is_menu_open(&self) -> bool {
-        self.open
-    }
-
-    fn highlight_quad(&self) -> Option<(f32, f32, f32, f32, [f32; 4])> {
-        if self.curved_arc.is_some() {
-            None
-        } else {
-            let hc = self.highlight_color()?;
-            Some((self.base.x, self.base.y, self.base.w, self.base.h, hc))
-        }
-    }
-
-    fn extra_quads(&self) -> Vec<(f32, f32, f32, f32, [f32; 4])> {
-        let mut quads = Vec::new();
-        if self.open {
-            let (dx, dy, dw, dh) = self.dropdown_rect();
-            if dh > 0.0 {
-                quads.push((dx, dy, dw, dh, colors::PANEL_MENU_BG));
-                if let Some(di) = self.hovered_item {
-                    quads.push((dx, dy + di as f32 * DROPDOWN_ITEM_H, dw, DROPDOWN_ITEM_H, colors::PANEL_MENU_HOVER));
-                }
-            }
-        }
-        quads
-    }
-
-    fn extra_arcs(&self) -> Vec<(f32, f32, f32, f32, f32, f32, [f32; 4])> {
-        let mut arcs = Vec::new();
-        if let Some((cx, cy, r, thickness, start_angle, end_angle)) = self.curved_arc {
-            if self.base.hovered && !self.open {
-                arcs.push((cx, cy, r, thickness, start_angle, end_angle, colors::PANEL_MENU_HOVER));
-            }
-        }
-        arcs
-    }
-
-    fn text_labels(&self) -> Vec<TextLabel> {
-        let mut labels = Vec::new();
-        if let Some((cx, cy, r, thickness, start_angle, end_angle)) = self.curved_arc {
-            let r_mid = r - thickness / 2.0;
-            labels.extend(TextLabel::curved_layout(
-                &self.active_title(),
-                cx, cy, r_mid,
-                start_angle, end_angle,
-                12.0,
-                [0xcc, 0xcc, 0xd4],
-            ));
-        } else {
-            labels.push(TextLabel {
-                text: self.active_title().to_string(),
-                x: self.base.x + 8.0,
-                y: self.base.y + 7.0,
-                font_size: 12.0,
-                color: [0xcc, 0xcc, 0xd4],
-            });
-        }
-        if self.open {
-            let (dx, dy, _, _) = self.dropdown_rect();
-            for (i, item) in self.items.iter().enumerate() {
-                let checked = self.item_checked.get(i).and_then(|&v| v);
-                let prefix = match checked {
-                    Some(true) => "\u{2713} ",
-                    Some(false) => "  ",
-                    None => "",
-                };
-                labels.push(TextLabel {
-                    text: format!("{}{}", prefix, item),
-                    x: dx + 8.0,
-                    y: dy + i as f32 * DROPDOWN_ITEM_H + 5.0,
-                    font_size: 12.0,
-                    color: [0xcc, 0xcc, 0xd4],
-                });
-            }
-        }
-        labels
-    }
-
-    fn set_visible(&mut self, visible: bool) {
-        self.base.hovered = false;
-        if !visible {
-            self.open = false;
-            self.was_open = None;
-            self.hovered_item = None;
-            focus::clear_if_matches(self);
-        }
-    }
-
-    fn parent(&self) -> Option<*mut (dyn Element + 'static)> {
-        self.parent
-    }
-
-    fn set_parent(&mut self, parent: Option<*mut (dyn Element + 'static)>) {
-        self.parent = parent;
-    }
-
-    fn z_index(&self) -> i32 {
-        100
-    }
-
-    fn menu_items(&self) -> Vec<String> {
-        self.items.clone()
-    }
-
-    fn menu_item_checked(&self) -> Vec<Option<bool>> {
-        self.item_checked.clone()
-    }
-
-    fn is_vertical(&self) -> bool {
-        self.vertical
-    }
-
-    fn focused(&self) -> bool {
-        self.base.focused
-    }
-
-    fn prepare_text(&mut self, fs: &mut glyphon::FontSystem) {
-        let title_text = self.active_title();
-        if let Some((_cx, _cy, _r, _thickness, _start_angle, _end_angle)) = self.curved_arc {
-            if self.curved_char_bufs.len() != title_text.chars().count() {
-                self.curved_char_bufs = title_text.chars()
-                    .map(|c| make_widget_text_buffer(fs, &c.to_string(), 12.0, "Outfit"))
-                    .collect();
-            }
-            self.title_buf = None;
-        } else {
-            if self.title_buf.is_none() {
-                self.title_buf = Some(make_widget_text_buffer(fs, title_text, 12.0, "Outfit"));
-            }
-            self.curved_char_bufs.clear();
-        }
-
-        if self.open {
-            if self.item_bufs.len() != self.items.len() {
-                self.item_bufs = self.items.iter().enumerate().map(|(i, item)| {
-                    let checked = self.item_checked.get(i).and_then(|&v| v);
-                    let prefix = match checked {
-                        Some(true) => "\u{2713} ",
-                        Some(false) => "  ",
-                        None => "",
-                    };
-                    let text = format!("{}{}", prefix, item);
-                    make_widget_text_buffer(fs, &text, 12.0, "Outfit")
-                }).collect();
-            }
-        } else {
-            self.item_bufs.clear();
-        }
-    }
-
-    fn get_text_items(&self) -> Vec<(&glyphon::Buffer, f32, f32, glyphon::Color)> {
-        let mut items = Vec::new();
-        let color = glyphon::Color::rgb(0xcc, 0xcc, 0xd4);
-
-        if let Some((cx, cy, r, thickness, start_angle, end_angle)) = self.curved_arc {
-            let r_mid = r - thickness / 2.0;
-            let active_title = self.active_title();
-            
-            let char_widths: Vec<f32> = active_title.chars().map(|c| {
-                TextLabel::estimate_width(&c.to_string(), 12.0)
-            }).collect();
-            let total_chars_width: f32 = char_widths.iter().sum();
-            
-            let angular_width = total_chars_width / r_mid;
-            let text_start_angle = (start_angle + end_angle) / 2.0 - angular_width / 2.0;
-            let mut cur_char_angle = text_start_angle;
-            
-            for (char_idx, c_buf) in self.curved_char_bufs.iter().enumerate() {
-                if char_idx < char_widths.len() {
-                    let cw = char_widths[char_idx];
-                    let dtheta = cw / r_mid;
-                    let char_center_angle = cur_char_angle + dtheta / 2.0;
-                    
-                    let tx = cx + r_mid * char_center_angle.cos() - cw / 2.0;
-                    let ty = cy + r_mid * char_center_angle.sin() - 12.0 / 2.0;
-                    
-                    items.push((c_buf, tx, ty, color));
-                    cur_char_angle += dtheta;
-                }
-            }
-        } else {
-            if let Some(ref title_buf) = self.title_buf {
-                items.push((title_buf, self.base.x + 8.0, self.base.y + 7.0, color));
-            }
-        }
-
-        if self.open {
-            let (dx, dy, _, _) = self.dropdown_rect();
-            for (i, item_buf) in self.item_bufs.iter().enumerate() {
-                items.push((
-                    item_buf,
-                    dx + 8.0,
-                    dy + i as f32 * DROPDOWN_ITEM_H + 5.0,
-                    color,
-                ));
-            }
-        }
-
-        items
-    }
-}
-
-unsafe impl Send for Menu {}
-unsafe impl Sync for Menu {}
-
-impl Drop for Menu {
-    fn drop(&mut self) {
-        focus::clear_if_matches(self);
-    }
-}
-
-
-
-#[derive(Debug, Clone)]
-pub struct Breadcrumb {
-    x: f32, y: f32, w: f32, h: f32,
-    hovered: bool,
-    path: Vec<String>,
-    hovered_seg: Option<usize>,
-    clicked_seg: Option<usize>,
-    pub network_opacity: f32,
-}
-
-impl Breadcrumb {
-    pub fn new() -> Self {
-        Self { x: 0.0, y: 0.0, w: 0.0, h: 0.0, hovered: false,
-               path: Vec::new(), hovered_seg: None, clicked_seg: None, network_opacity: 1.0 }
-    }
-
-    fn seg_at(&self, px: f32) -> Option<usize> {
-        let mut cx = self.x + BREADCRUMB_PADDING;
-        for (i, seg) in self.path.iter().enumerate() {
-            let w = seg.len() as f32 * 7.5;
-            if px >= cx && px < cx + w {
-                return Some(i);
-            }
-            cx += w + SEGMENT_GAP;
-        }
-        None
-    }
-}
-
-impl Element for Breadcrumb {
-    fn rect(&self) -> (f32, f32, f32, f32) { (self.x, self.y, self.w, self.h) }
-    fn set_rect(&mut self, x: f32, y: f32, w: f32, h: f32) { self.x = x; self.y = y; self.w = w; self.h = h; }
-    fn set_network_opacity(&mut self, opacity: f32) { self.network_opacity = opacity; }
-    fn color(&self) -> [f32; 4] { [0.10, 0.10, 0.14, self.network_opacity] }
-    fn set_hovered(&mut self, v: bool) { self.hovered = v; }
-    fn hovered(&self) -> bool { self.hovered }
-
-    fn on_cursor_moved(&mut self, px: f32, py: f32) -> bool {
-        let was = self.hovered;
-        self.hovered = self.hit_test(px, py);
-        let old = self.hovered_seg;
-        self.hovered_seg = if self.hovered { self.seg_at(px) } else { None };
-        was != self.hovered || old != self.hovered_seg
-    }
-
-    fn mouse_input(&mut self, button: MouseButton, state: ElementState, px: f32, _py: f32) -> bool {
-        if button != MouseButton::Left || state != ElementState::Pressed { return false; }
-        if let Some(i) = self.seg_at(px) {
-            if i < self.path.len() - 1 {
-                self.clicked_seg = Some(i);
-                return true;
-            }
-        }
-        false
-    }
-
-    fn set_path(&mut self, segments: &[String]) {
-        let mut s = Vec::with_capacity(segments.len().max(1));
-        if segments.is_empty() || (segments.len() == 1 && segments[0].is_empty()) {
-            s.push("/".to_string());
-        } else {
-            s.push("/".to_string());
-            for name in segments {
-                s.push(format!(" \u{203A} {}", name));
-            }
-        }
-        self.path = s;
-    }
-
-    fn path_click(&mut self) -> Option<usize> { self.clicked_seg.take() }
-
-    fn extra_quads(&self) -> Vec<(f32, f32, f32, f32, [f32; 4])> {
-        let mut quads = Vec::new();
-        if let Some(i) = self.hovered_seg {
-            let mut cx = self.x + BREADCRUMB_PADDING;
-            for j in 0..i {
-                let w = self.path[j].len() as f32 * 7.5;
-                cx += w + SEGMENT_GAP;
-            }
-            let w = self.path[i].len() as f32 * 7.5;
-            quads.push((cx, self.y, w, self.h, [1.0, 1.0, 1.0, 0.06]));
-        }
-        quads
-    }
-
-    fn text_labels(&self) -> Vec<TextLabel> {
-        let mut labels = Vec::new();
-        let mut cx = self.x + BREADCRUMB_PADDING;
-        for (i, seg) in self.path.iter().enumerate() {
-            labels.push(TextLabel {
-                text: seg.clone(),
-                x: cx,
-                y: self.y + 6.0,
-                font_size: 12.0,
-                color: if i == self.path.len() - 1 { [0xcc, 0xcc, 0xd4] } else { [0x88, 0x88, 0x99] },
-            });
-            cx += seg.len() as f32 * 7.5 + SEGMENT_GAP;
-        }
-        labels
-    }
-}
-
-
-pub struct Spreadsheet {
-    x: f32,
-    y: f32,
-    w: f32,
-    h: f32,
-    hovered: bool,
-    visible: bool,
-    headers: Vec<String>,
-    rows: Vec<Vec<String>>,
-    scroll_y: f32,
-    scroll_velocity: f32,
-    dragging_scrollbar: bool,
-    drag_offset_y: f32,
-    scrollbar_hovered: bool,
-    scrollbar_thumb_hovered: bool,
-}
-
-impl Spreadsheet {
-    pub fn new() -> Self {
-        Self {
-            x: 0.0,
-            y: 0.0,
-            w: 0.0,
-            h: 0.0,
-            hovered: false,
-            visible: false,
-            headers: Vec::new(),
-            rows: Vec::new(),
-            scroll_y: 0.0,
-            scroll_velocity: 0.0,
-            dragging_scrollbar: false,
-            drag_offset_y: 0.0,
-            scrollbar_hovered: false,
-            scrollbar_thumb_hovered: false,
-        }
-    }
-}
-
-impl Element for Spreadsheet {
-    fn rect(&self) -> (f32, f32, f32, f32) {
-        if !self.visible {
-            (0.0, 0.0, 0.0, 0.0)
-        } else {
-            (self.x, self.y, self.w, self.h)
-        }
-    }
-
-    fn set_rect(&mut self, x: f32, y: f32, w: f32, h: f32) {
-        self.x = x;
-        self.y = y;
-        self.w = w;
-        self.h = h;
-    }
-
-    fn color(&self) -> [f32; 4] {
-        if !self.visible {
-            [0.0, 0.0, 0.0, 0.0]
-        } else {
-            colors::PARAM_BG
-        }
-    }
-
-    fn set_hovered(&mut self, v: bool) {
-        self.hovered = v;
-    }
-
-    fn hovered(&self) -> bool {
-        self.hovered
-    }
-
-    fn hit_test(&self, px: f32, py: f32) -> bool {
-        if !self.visible {
-            return false;
-        }
-        if crate::widget::popovers::is_coordinate_covered(self as *const Self as *const () as usize, px, py) {
-            return false;
-        }
-        let (rx, ry, rw, rh) = self.rect();
-        px >= rx && px <= rx + rw && py >= ry && py <= ry + rh
-    }
-
-    fn set_visible(&mut self, visible: bool) {
-        self.visible = visible;
-    }
-
-    fn visible(&self) -> bool {
-        self.visible
-    }
-
-    fn set_spreadsheet_data(&mut self, headers: Vec<String>, rows: Vec<Vec<String>>) {
-        self.headers = headers;
-        self.rows = rows;
-        
-        // Clamp scroll_y to new bounds
-        let content_h = self.rows.len() as f32 * 24.0;
-        let visible_h = (self.h - 24.0).max(0.0);
-        let max_scroll_y = (content_h - visible_h).max(0.0);
-        self.scroll_y = self.scroll_y.clamp(0.0, max_scroll_y);
-    }
-
-    fn on_cursor_moved(&mut self, px: f32, py: f32) -> bool {
-        let was_hovered = self.hovered;
-        self.hovered = self.hit_test(px, py);
-
-        let was_sb_hovered = self.scrollbar_hovered;
-        let was_thumb_hovered = self.scrollbar_thumb_hovered;
-
-        let content_h = self.rows.len() as f32 * 24.0;
-        let visible_h = (self.h - 24.0).max(0.0);
-        if visible_h > 0.0 && content_h > visible_h {
-            let scrollbar_w = 6.0;
-            let scrollbar_padding = 2.0;
-            let scrollbar_x = self.x + self.w - scrollbar_w - scrollbar_padding;
-            let track_y = self.y + 24.0;
-
-            self.scrollbar_hovered = px >= scrollbar_x - 2.0 && px <= self.x + self.w
-                && py >= track_y && py <= self.y + self.h;
-
-            let thumb_h = ((visible_h / content_h) * visible_h).clamp(15.0_f32.min(visible_h), visible_h);
-            let max_scroll_y = content_h - visible_h;
-            let scroll_ratio = self.scroll_y / max_scroll_y;
-            let track_scroll_range = visible_h - thumb_h;
-            let thumb_y = track_y + scroll_ratio * track_scroll_range;
-
-            self.scrollbar_thumb_hovered = px >= scrollbar_x - 2.0 && px <= self.x + self.w
-                && py >= thumb_y && py <= thumb_y + thumb_h;
-        } else {
-            self.scrollbar_hovered = false;
-            self.scrollbar_thumb_hovered = false;
-        }
-
-        was_hovered != self.hovered
-            || was_sb_hovered != self.scrollbar_hovered
-            || was_thumb_hovered != self.scrollbar_thumb_hovered
-    }
-
-    fn mouse_wheel(&mut self, delta: &MouseScrollDelta, px: f32, py: f32) -> bool {
-        if !self.visible {
-            return false;
-        }
-        if self.hit_test(px, py) {
-            let content_h = self.rows.len() as f32 * 24.0;
-            let visible_h = (self.h - 24.0).max(0.0);
-            if visible_h > 0.0 && content_h > visible_h {
-                let scroll_amount = match delta {
-                    MouseScrollDelta::LineDelta(_x, y) => *y * 24.0,
-                    MouseScrollDelta::PixelDelta(pos) => pos.y as f32,
-                };
-                self.scroll_velocity += scroll_amount * 12.0;
-                return true;
-            }
-        }
-        false
-    }
-
-    fn draggable(&self) -> bool {
-        if !self.visible {
-            return false;
-        }
-        let content_h = self.rows.len() as f32 * 24.0;
-        let visible_h = (self.h - 24.0).max(0.0);
-        visible_h > 0.0 && content_h > visible_h
-    }
-
-    fn is_dragging(&self) -> bool {
-        self.dragging_scrollbar
-    }
-
-    fn drag_begin(&mut self, px: f32, py: f32) {
-        self.scroll_velocity = 0.0;
-        let content_h = self.rows.len() as f32 * 24.0;
-        let visible_h = (self.h - 24.0).max(0.0);
-        if visible_h > 0.0 && content_h > visible_h {
-            let scrollbar_w = 6.0;
-            let scrollbar_padding = 2.0;
-            let scrollbar_x = self.x + self.w - scrollbar_w - scrollbar_padding;
-            let track_y = self.y + 24.0;
-
-            if px >= scrollbar_x - 4.0 && px <= self.x + self.w
-                && py >= track_y && py <= self.y + self.h
-            {
-                self.dragging_scrollbar = true;
-
-                let thumb_h = ((visible_h / content_h) * visible_h).clamp(15.0_f32.min(visible_h), visible_h);
-                let max_scroll_y = content_h - visible_h;
-                let scroll_ratio = self.scroll_y / max_scroll_y;
-                let track_scroll_range = visible_h - thumb_h;
-                let thumb_y = track_y + scroll_ratio * track_scroll_range;
-
-                if py >= thumb_y && py <= thumb_y + thumb_h {
-                    self.drag_offset_y = py - thumb_y;
-                } else {
-                    self.drag_offset_y = thumb_h / 2.0;
-                    let new_thumb_y = py - self.drag_offset_y;
-                    let scroll_ratio = if track_scroll_range > 0.0 {
-                        ((new_thumb_y - track_y) / track_scroll_range).clamp(0.0, 1.0)
-                    } else {
-                        0.0
-                    };
-                    self.scroll_y = scroll_ratio * max_scroll_y;
-                }
-            }
-        }
-    }
-
-    fn drag_update(&mut self, _px: f32, py: f32) -> bool {
-        if self.dragging_scrollbar {
-            self.scroll_velocity = 0.0;
-            let content_h = self.rows.len() as f32 * 24.0;
-            let visible_h = (self.h - 24.0).max(0.0);
-            if visible_h > 0.0 && content_h > visible_h {
-                let thumb_h = ((visible_h / content_h) * visible_h).clamp(15.0_f32.min(visible_h), visible_h);
-                let max_scroll_y = content_h - visible_h;
-                let track_y = self.y + 24.0;
-                let track_scroll_range = visible_h - thumb_h;
-
-                let new_thumb_y = py - self.drag_offset_y;
-                let scroll_ratio = if track_scroll_range > 0.0 {
-                    ((new_thumb_y - track_y) / track_scroll_range).clamp(0.0, 1.0)
-                } else {
-                    0.0
-                };
-                let old_scroll_y = self.scroll_y;
-                self.scroll_y = scroll_ratio * max_scroll_y;
-
-                return (self.scroll_y - old_scroll_y).abs() > 0.01;
-            }
-        }
-        false
-    }
-
-    fn drag_end(&mut self) {
-        self.dragging_scrollbar = false;
-        self.scroll_velocity = 0.0;
-    }
-
-    fn tick(&mut self, dt: f32) -> bool {
-        if self.scroll_velocity.abs() > 0.01 {
-            let content_h = self.rows.len() as f32 * 24.0;
-            let visible_h = (self.h - 24.0).max(0.0);
-            let max_scroll_y = (content_h - visible_h).max(0.0);
-            let old_scroll_y = self.scroll_y;
-
-            self.scroll_y = (self.scroll_y + self.scroll_velocity * dt).clamp(0.0, max_scroll_y);
-
-            // Decelerate with friction (exponential decay)
-            let friction = 8.0;
-            self.scroll_velocity *= (-friction * dt).exp();
-
-            if self.scroll_y == 0.0 || self.scroll_y == max_scroll_y {
-                self.scroll_velocity = 0.0;
-            }
-
-            if self.scroll_velocity.abs() < 5.0 {
-                self.scroll_velocity = 0.0;
-            }
-
-            (self.scroll_y - old_scroll_y).abs() > 0.01
-        } else {
-            false
-        }
-    }
-
-    fn extra_quads(&self) -> Vec<(f32, f32, f32, f32, [f32; 4])> {
-        if !self.visible {
-            return Vec::new();
-        }
-        let mut quads = Vec::new();
-        
-        // Header bg
-        quads.push((self.x, self.y, self.w, 24.0, [0.12, 0.12, 0.16, 0.4]));
-
-        // Zebra rows
-        let row_h = 24.0;
-        let body_top = self.y + 24.0;
-        let body_bottom = self.y + self.h;
-        for i in 0..self.rows.len() {
-            let ry = self.y + 24.0 + i as f32 * row_h - self.scroll_y;
-            if ry + row_h <= body_top || ry >= body_bottom {
-                continue;
-            }
-            let draw_y = ry.max(body_top);
-            let draw_h = (ry + row_h).min(body_bottom) - draw_y;
-            if draw_h > 0.0 {
-                let row_color = if i % 2 == 0 {
-                    [0.10, 0.10, 0.13, 0.15]
-                } else {
-                    [0.08, 0.08, 0.11, 0.05]
-                };
-                quads.push((self.x, draw_y, self.w, draw_h, row_color));
-
-                // Horizontal row separator
-                let sep_y = ry + row_h;
-                if sep_y >= body_top && sep_y < body_bottom {
-                    quads.push((self.x, sep_y, self.w, 1.0, [0.20, 0.20, 0.25, 0.15]));
-                }
-            }
-        }
-
-        // Header separator
-        quads.push((self.x, self.y + 24.0, self.w, 1.0, [0.20, 0.20, 0.25, 0.25]));
-
-        // Vertical separators
-        let divider_h = self.h;
-        if divider_h > 0.0 && !self.headers.is_empty() {
-            let n_cols = self.headers.len();
-            for i in 1..n_cols {
-                let r = i as f32 / n_cols as f32;
-                quads.push((self.x + self.w * r, self.y, 1.0, divider_h, [0.20, 0.20, 0.25, 0.15]));
-            }
-        }
-
-        // Scrollbar track & thumb
-        let content_h = self.rows.len() as f32 * row_h;
-        let visible_h = (self.h - 24.0).max(0.0);
-        if visible_h > 0.0 && content_h > visible_h {
-            let scrollbar_w = 6.0;
-            let scrollbar_padding = 2.0;
-            let scrollbar_x = self.x + self.w - scrollbar_w - scrollbar_padding;
-            let track_y = self.y + 24.0;
-            let track_h = visible_h;
-
-            // Track BG
-            quads.push((scrollbar_x, track_y, scrollbar_w, track_h, [0.05, 0.05, 0.08, 0.15]));
-
-            // Thumb
-            let thumb_h = ((visible_h / content_h) * visible_h).clamp(15.0_f32.min(visible_h), visible_h);
-            let max_scroll_y = content_h - visible_h;
-            let scroll_ratio = self.scroll_y / max_scroll_y;
-            let track_scroll_range = visible_h - thumb_h;
-            let thumb_y = track_y + scroll_ratio * track_scroll_range;
-
-            let thumb_color = if self.dragging_scrollbar {
-                [0.40, 0.40, 0.48, 1.0]
-            } else if self.scrollbar_thumb_hovered {
-                [0.32, 0.32, 0.38, 1.0]
-            } else if self.scrollbar_hovered {
-                [0.24, 0.24, 0.30, 0.9]
-            } else {
-                [0.18, 0.18, 0.24, 0.7]
-            };
-
-            quads.push((scrollbar_x, thumb_y, scrollbar_w, thumb_h, thumb_color));
-        }
-
-        quads
-    }
-
-    fn text_labels(&self) -> Vec<TextLabel> {
-        if !self.visible {
-            return Vec::new();
-        }
-        let mut labels = Vec::new();
-        if self.headers.is_empty() {
-            return labels;
-        }
-
-        let n_cols = self.headers.len();
-        for (i, header) in self.headers.iter().enumerate() {
-            let cx = self.x + self.w * (i as f32 / n_cols as f32) + 8.0;
-            labels.push(TextLabel {
-                text: header.clone(),
-                x: cx,
-                y: self.y + 6.0,
-                font_size: 12.0,
-                color: [0xdd, 0xdd, 0xee],
-            });
-        }
-
-        let row_h = 24.0;
-        let body_top = self.y + 24.0;
-        let body_bottom = self.y + self.h;
-        for (i, row) in self.rows.iter().enumerate() {
-            let ry = self.y + 24.0 + i as f32 * row_h - self.scroll_y;
-            // Only show text if the row is fully inside the spreadsheet body
-            if ry < body_top || ry + row_h > body_bottom {
-                continue;
-            }
-
-            for (col_idx, val) in row.iter().enumerate().take(n_cols) {
-                let cx = self.x + self.w * (col_idx as f32 / n_cols as f32) + 8.0;
-                labels.push(TextLabel {
-                    text: val.clone(),
-                    x: cx,
-                    y: ry + 6.0,
-                    font_size: 12.0,
-                    color: [0xbb, 0xbb, 0xcc],
-                });
-            }
-        }
-        labels
-    }
-}
-
-#[derive(Debug, Clone)]
-pub struct ScrollBox {
-    x: f32, y: f32, w: f32, h: f32,
-    pub scroll_y: f32,
-    pub content_h: f32,
-    pub viewport_y: f32,
-    pub viewport_h: f32,
-    hovered: bool,
-    pub show_border: bool,
-    pub parent: Option<*mut (dyn Element + 'static)>,
-    pub children: Vec<*mut (dyn Element + 'static)>,
-}
-
-impl ScrollBox {
-    pub fn new() -> Self {
-        Self {
-            x: 0.0, y: 0.0, w: 0.0, h: 0.0,
-            scroll_y: 0.0,
-            content_h: 0.0,
-            viewport_y: 0.0,
-            viewport_h: 0.0,
-            hovered: false,
-            show_border: true,
-            parent: None,
-            children: Vec::new(),
-        }
-    }
-
-    pub fn update_bounds(&mut self, content_h: f32, viewport_y: f32, viewport_h: f32) {
-        self.content_h = content_h;
-        self.viewport_y = viewport_y;
-        self.viewport_h = viewport_h;
-        let max_scroll = (content_h - viewport_h).max(0.0);
-        self.scroll_y = self.scroll_y.clamp(0.0, max_scroll);
-    }
-
-    pub fn get_item_draw_y(&self, virtual_y: f32, item_h: f32) -> Option<f32> {
-        let draw_y = self.viewport_y + virtual_y - self.scroll_y;
-        if draw_y >= self.viewport_y - 1.0 && draw_y + item_h <= self.viewport_y + self.viewport_h + 1.0 {
-            Some(draw_y)
-        } else {
-            None
-        }
-    }
-}
-
-impl Element for ScrollBox {
-    fn rect(&self) -> (f32, f32, f32, f32) { (self.x, self.y, self.w, self.h) }
-    fn set_rect(&mut self, x: f32, y: f32, w: f32, h: f32) { self.x = x; self.y = y; self.w = w; self.h = h; }
-    fn color(&self) -> [f32; 4] { [0.08, 0.08, 0.12, 0.3] }
-    fn set_hovered(&mut self, v: bool) { self.hovered = v; }
-    fn hovered(&self) -> bool { self.hovered }
-    fn highlight_color(&self) -> Option<[f32; 4]> { None }
-
-    fn focus(&mut self) {
-        focus::set_focused(self);
-    }
-    fn unfocus(&mut self) {}
-
-    fn mouse_input(&mut self, button: MouseButton, state: ElementState, px: f32, py: f32) -> bool {
-        if button == MouseButton::Left && state == ElementState::Pressed {
-            if self.hit_test(px, py) {
-                self.focus();
-                return true;
-            }
-        }
-        false
-    }
-
-    fn on_cursor_moved(&mut self, px: f32, py: f32) -> bool {
-        let was = self.hovered;
-        self.hovered = self.hit_test(px, py);
-        was != self.hovered
-    }
-
-    fn mouse_wheel(&mut self, delta: &MouseScrollDelta, px: f32, py: f32) -> bool {
-        if self.hit_test(px, py) {
-            let scroll_speed = 24.0;
-            let dy = match delta {
-                MouseScrollDelta::LineDelta(_, y) => -y * scroll_speed,
-                MouseScrollDelta::PixelDelta(pos) => -pos.y as f32,
-            };
-            let old_scroll = self.scroll_y;
-            let max_scroll = (self.content_h - self.viewport_h).max(0.0);
-            self.scroll_y = (self.scroll_y + dy).clamp(0.0, max_scroll);
-            (self.scroll_y - old_scroll).abs() > 0.01
-        } else {
-            false
-        }
-    }
-
-    fn extra_quads(&self) -> Vec<(f32, f32, f32, f32, [f32; 4])> {
-        let mut quads = Vec::new();
-        
-        // Background
-        quads.push((self.x, self.y, self.w, self.h, [0.08, 0.08, 0.12, 0.3]));
-
-        // Border lines
-        let box_border_color = if focus::is_focused(self) {
-            [0.30, 0.50, 0.32, 1.0] // Focused green
-        } else if self.hovered {
-            [0.25, 0.25, 0.35, 1.0] // Hovered
-        } else {
-            [0.18, 0.18, 0.24, 1.0] // Default
-        };
-        quads.push((self.x, self.y, self.w, 1.0, box_border_color)); // Top
-        quads.push((self.x, self.y + self.h - 1.0, self.w, 1.0, box_border_color)); // Bottom
-        quads.push((self.x, self.y, 1.0, self.h, box_border_color)); // Left
-        quads.push((self.x + self.w - 1.0, self.y, 1.0, self.h, box_border_color)); // Right
-
-        // Scrollbar
-        if self.content_h > self.viewport_h {
-            let sb_x = self.x + self.w - 8.0;
-            let sb_w = 4.0;
-            let sb_track_h = self.viewport_h - 8.0;
-            let sb_track_y = self.viewport_y + 4.0;
-
-            // Track
-            quads.push((sb_x, sb_track_y, sb_w, sb_track_h, [0.15, 0.15, 0.20, 0.3]));
-
-            // Thumb
-            let visible_ratio = self.viewport_h / self.content_h;
-            let thumb_h = (sb_track_h * visible_ratio).clamp(20.0, sb_track_h);
-            let max_scroll = (self.content_h - self.viewport_h).max(0.0);
-            let scroll_ratio = if max_scroll > 0.0 { self.scroll_y / max_scroll } else { 0.0 };
-            let thumb_y = sb_track_y + scroll_ratio * (sb_track_h - thumb_h);
-
-            quads.push((sb_x, thumb_y, sb_w, thumb_h, [0.60, 0.60, 0.65, 0.4]));
-        }
-
-        quads
-    }
-
-    fn keyboard_input(&mut self, event: &KeyEvent) -> bool {
-        if !focus::is_focused(self) {
-            return false;
-        }
-        if event.state != ElementState::Pressed {
-            return false;
-        }
-        if event.ctrl {
-            match &event.logical_key {
-                Key::Character(c) if c == "n" || c == "N" => {
-                    let old_scroll = self.scroll_y;
-                    let max_scroll = (self.content_h - self.viewport_h).max(0.0);
-                    self.scroll_y = (self.scroll_y + 24.0).clamp(0.0, max_scroll);
-                    (self.scroll_y - old_scroll).abs() > 0.01
-                }
-                Key::Character(c) if c == "p" || c == "P" => {
-                    let old_scroll = self.scroll_y;
-                    let max_scroll = (self.content_h - self.viewport_h).max(0.0);
-                    self.scroll_y = (self.scroll_y - 24.0).clamp(0.0, max_scroll);
-                    (self.scroll_y - old_scroll).abs() > 0.01
-                }
-                _ => false,
-            }
-        } else {
-            match &event.logical_key {
-                Key::Named(NamedKey::ArrowDown) => {
-                    let old_scroll = self.scroll_y;
-                    let max_scroll = (self.content_h - self.viewport_h).max(0.0);
-                    self.scroll_y = (self.scroll_y + 24.0).clamp(0.0, max_scroll);
-                    (self.scroll_y - old_scroll).abs() > 0.01
-                }
-                Key::Named(NamedKey::ArrowUp) => {
-                    let old_scroll = self.scroll_y;
-                    let max_scroll = (self.content_h - self.viewport_h).max(0.0);
-                    self.scroll_y = (self.scroll_y - 24.0).clamp(0.0, max_scroll);
-                    (self.scroll_y - old_scroll).abs() > 0.01
-                }
-                _ => false,
-            }
-        }
-    }
-
-    fn parent(&self) -> Option<*mut (dyn Element + 'static)> { self.parent }
-    fn set_parent(&mut self, parent: Option<*mut (dyn Element + 'static)>) { self.parent = parent; }
-    fn children(&self) -> Vec<*mut (dyn Element + 'static)> { self.children.clone() }
-    fn add_child(&mut self, child: *mut (dyn Element + 'static)) { self.children.push(child); }
-    fn clear_children(&mut self) { self.children.clear(); }
-}
-
-impl Drop for ScrollBox {
-    fn drop(&mut self) {
-        focus::clear_if_matches(self);
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    fn menu_item_x(title: &str, menu_items: &[String], idx: usize) -> f32 {
-        let mut x = 8.0;
-        if !title.is_empty() {
-            x += title.len() as f32 * 7.5 + 24.0;
-        }
-        for i in 0..idx {
-            x += menu_items[i].len() as f32 * 7.5 + 16.0;
-        }
-        x
-    }
-
-    fn menu_item_w(menu_items: &[String], idx: usize) -> f32 {
-        menu_items[idx].len() as f32 * 7.5 + 16.0
-    }
-
-    #[test]
-    fn test_rangeslider_interaction() {
-        let mut rs = RangeSlider::new();
-        rs.set_rect(10.0, 10.0, 200.0, 20.0);
-
-        // Low value: 0.2, High value: 0.8
-        let (low, high) = rs.values();
-        assert_eq!(low, 0.2);
-        assert_eq!(high, 0.8);
-
-        // Thumb size = h * 0.9 = 18.0
-        // Range = w - thumb_size = 200.0 - 18.0 = 182.0
-        // Thumb low center: x + 0.2 * 182.0 + 9.0 = 10.0 + 36.4 + 9.0 = 55.4
-        // Thumb high center: x + 0.8 * 182.0 + 9.0 = 10.0 + 145.6 + 9.0 = 164.6
-
-        // 1. Drag Low thumb from 0.2 to 0.45
-        // Click at px = 55.4 (center of low thumb)
-        rs.drag_begin(55.4, 20.0);
-        assert_eq!(rs.active_thumb, Some(ActiveThumb::Low));
-
-        // Drag to px = 100.9 (new low value = (100.9 - offset(9.0) - 10.0) / 182.0 = 81.9 / 182.0 = 0.45)
-        let changed = rs.drag_update(100.9, 20.0);
-        assert!(changed);
-        assert!((rs.values().0 - 0.45).abs() < 0.01);
-        assert_eq!(rs.values().1, 0.8); // High value unchanged
-
-        rs.drag_end();
-        assert_eq!(rs.active_thumb, None);
-
-        // 2. Drag High thumb from 0.8 to 0.6
-        // Click at px = 164.6 (center of high thumb)
-        rs.drag_begin(164.6, 20.0);
-        assert_eq!(rs.active_thumb, Some(ActiveThumb::High));
-
-        // Drag to px = 128.2 (new high value = (128.2 - offset(9.0) - 10.0) / 182.0 = 109.2 / 182.0 = 0.6)
-        let changed = rs.drag_update(128.2, 20.0);
-        assert!(changed);
-        assert!((rs.values().1 - 0.6).abs() < 0.01);
-
-        rs.drag_end();
-    }
-
-    #[test]
-    fn test_rangeslider_overlap() {
-        let mut rs = RangeSlider::new().with_values(0.5, 0.5);
-        rs.set_rect(10.0, 10.0, 200.0, 20.0);
-
-        // Both low and high are 0.5. Thumb center = 10.0 + 0.5 * 182.0 + 9.0 = 110.0
-        // Click to the left of center should select Low thumb
-        rs.drag_begin(109.0, 20.0);
-        assert_eq!(rs.active_thumb, Some(ActiveThumb::Low));
-        rs.drag_end();
-
-        // Click to the right of center should select High thumb
-        rs.drag_begin(111.0, 20.0);
-        assert_eq!(rs.active_thumb, Some(ActiveThumb::High));
-        rs.drag_end();
-
-        // Drag Low thumb past High value (0.5). It should be constrained to 0.5
-        rs.drag_begin(110.0, 20.0); // selects low
-        rs.drag_update(150.0, 20.0); // drag past high
-        assert_eq!(rs.values().0, 0.5); // constrained
-        rs.drag_end();
-    }
-
-
-    #[test]
-    fn test_node_toggle_geometry_visibility() {
-        let mut node = Node::new(100.0, 100.0, 200.0, 50.0, "Test Node");
-
-        // 1. Initial state
-        assert!(node.geom_visible());
-        assert!(!node.take_geom_toggle());
-        assert!(node.draggable());
-
-        // Get the toggle rect
-        let (tx, ty, tw, th) = node.toggle_rect();
-        
-        // 2. Hover toggle area
-        // Move cursor inside toggle area
-        let changed = node.cursor_moved(tx + tw / 2.0, ty + th / 2.0);
-        assert!(changed);
-        assert!(node.toggle_hovered);
-        assert!(!node.draggable(), "Node should not be draggable when hovering over the toggle widget");
-
-        // Move cursor outside toggle area but inside node
-        let changed2 = node.cursor_moved(tx - 10.0, ty + th / 2.0);
-        assert!(changed2);
-        assert!(!node.toggle_hovered);
-        assert!(node.draggable());
-
-        // 3. Click toggle area
-        // Move cursor back inside toggle area
-        node.cursor_moved(tx + tw / 2.0, ty + th / 2.0);
-        // Press Left button
-        let input_changed = node.mouse_input(MouseButton::Left, ElementState::Pressed, tx + tw / 2.0, ty + th / 2.0);
-        assert!(input_changed);
-        assert!(!node.geom_visible(), "Geometry visibility should be toggled off");
-        assert!(node.take_geom_toggle(), "take_geom_toggle should return true after toggle click");
-        assert!(!node.take_geom_toggle(), "take_geom_toggle should clear state after being called once");
-
-        // Click again to toggle back on
-        let input_changed2 = node.mouse_input(MouseButton::Left, ElementState::Pressed, tx + tw / 2.0, ty + th / 2.0);
-        assert!(input_changed2);
-        assert!(node.geom_visible(), "Geometry visibility should be toggled back on");
-        assert!(node.take_geom_toggle());
-    }
-
-    #[test]
-    fn test_graph_interaction() {
-        let mut graph = Graph::new();
-        graph.set_rect(0.0, 0.0, 800.0, 600.0);
-        graph.set_grid_sizes(100.0, 50.0);
-        graph.set_skipped_sizes(10.0, 20.0);
-        graph.set_grid_origin(0.0, 0.0);
-
-        let nodes = vec![
-            GraphNode {
-                name: "Node A".to_string(),
-                position: (0.0, 0.0),
-                parameters: vec![],
-                geom_visible: true,
-            },
-            GraphNode {
-                name: "Node B".to_string(),
-                position: (2.0, 1.0),
-                parameters: vec![],
-                geom_visible: true,
-            },
-        ];
-        graph.set_nodes(&nodes);
-
-        // 1. Initial State
-        assert_eq!(graph.get_nodes().len(), 2);
-        assert_eq!(graph.selected_node(), None);
-
-        // 2. Select Node A
-        // Node A screen rect: (0, 0, 100, 50)
-        let clicked = graph.mouse_input(MouseButton::Left, ElementState::Pressed, 50.0, 25.0);
-        assert!(clicked);
-        assert_eq!(graph.selected_node(), Some(0));
-        assert!(graph.draggable());
-
-        // 3. Drag Node A
-        graph.drag_begin(50.0, 25.0);
-        assert!(graph.is_dragging());
-
-        // Enable snapping
-        graph.set_grid_snap_enabled(true);
-        graph.drag_update(170.0, 85.0); // drag offset from Node A center (50, 25): nx = 170 - 50 = 120, ny = 85 - 25 = 60
-        assert_eq!(graph.drag_node_pos, Some((120.0, 60.0)));
-
-        graph.drag_end();
-        assert_eq!(graph.get_nodes()[0].position, (1.0, 1.0)); // Snapped grid position: (120/120, 60/60)
-
-        // 4. Toggle geometry visibility of Node B
-        // Node B screen rect: (2 * 120 = 240, 1 * 60 = 60, 100, 50)
-        // Toggle button: tx = 240 + 100 - 30 = 310, ty = 60 + (50 - 18)/2 = 76, tw = 18, th = 18
-        let clicked_toggle = graph.mouse_input(MouseButton::Left, ElementState::Pressed, 319.0, 85.0);
-        assert!(clicked_toggle);
-        assert_eq!(graph.take_node_geom_toggle(), Some((1, false)));
-    }
-
-
-    #[test]
-    fn test_menubar_vertical_horizontal_labels() {
-        // Create MenuBar with custom horizontal and vertical labels
-        let mut menubar = MenuBar::new(0.0, 0.0, 120.0, 30.0)
-            .with_title("App")
-            .with_item_vh("FileH", "FileV", &["Open", "Save"])
-            .with_item("Edit", &["Undo"]);
-
-        // 1. Horizontal mode (default)
-        assert!(!menubar.vertical);
-        let labels_h = menubar.text_labels();
-        // Title should be "App", item 0 should be "FileH", item 1 should be "Edit"
-        assert_eq!(labels_h[0].text, "App");
-        assert_eq!(labels_h[1].text, "FileH");
-        assert_eq!(labels_h[2].text, "Edit");
-
-        // Hover test in horizontal layout
-        let ix = menu_item_x("App", &["FileH".to_string(), "Edit".to_string()], 0);
-        let iw = menu_item_w(&["FileH".to_string(), "Edit".to_string()], 0);
-        
-        // Move cursor inside "FileH" bounds
-        menubar.cursor_moved(ix + iw / 2.0, 15.0);
-        assert_eq!(menubar.hovered_menu, Some(0));
-
-        // 2. Vertical mode
-        let mut menubar_v = menubar.with_vertical(true);
-        assert!(menubar_v.vertical);
-        let labels_v = menubar_v.text_labels();
-        // Title should be "App", item 0 should be "FileV", item 1 should be "Edit"
-        assert_eq!(labels_v[0].text, "App");
-        assert_eq!(labels_v[1].text, "FileV");
-        assert_eq!(labels_v[2].text, "Edit");
-
-        // Hover test in vertical layout
-        let iy = menubar_v.item_y_vertical(0);
-        let ih = menubar_v.item_h_vertical();
-
-        // Move cursor inside "FileV" bounds
-        menubar_v.cursor_moved(20.0, iy + ih / 2.0);
-        assert_eq!(menubar_v.hovered_menu, Some(0));
-    }
-
-    #[test]
-    fn test_spreadsheet_dynamic() {
-        let mut spreadsheet = Spreadsheet::new();
-        spreadsheet.set_rect(10.0, 10.0, 100.0, 200.0);
-        spreadsheet.set_visible(true);
-
-        // Initially empty
-        assert!(spreadsheet.headers.is_empty());
-        assert!(spreadsheet.rows.is_empty());
-        assert!(spreadsheet.text_labels().is_empty());
-
-        // Set dynamic headers and rows
-        let headers = vec!["ColA".to_string(), "ColB".to_string()];
-        let rows = vec![
-            vec!["Val1".to_string(), "Val2".to_string()],
-            vec!["Val3".to_string(), "Val4".to_string()],
-        ];
-        spreadsheet.set_spreadsheet_data(headers, rows);
-
-        assert_eq!(spreadsheet.headers.len(), 2);
-        assert_eq!(spreadsheet.rows.len(), 2);
-
-        // Verify labels generated
-        let labels = spreadsheet.text_labels();
-        // 2 headers + 4 cell values = 6 labels total
-        assert_eq!(labels.len(), 6);
-        assert_eq!(labels[0].text, "ColA");
-        assert_eq!(labels[1].text, "ColB");
-        assert_eq!(labels[2].text, "Val1");
-        assert_eq!(labels[3].text, "Val2");
-        assert_eq!(labels[4].text, "Val3");
-        assert_eq!(labels[5].text, "Val4");
-
-        // Verify positions are correct (col 0 starts at x = 10.0 + 8.0 = 18.0)
-        assert_eq!(labels[0].x, 18.0);
-        // Col 1 starts at x = 10.0 + 100.0 * 0.5 + 8.0 = 68.0
-        assert_eq!(labels[1].x, 68.0);
-        assert_eq!(labels[2].x, 18.0);
-        assert_eq!(labels[3].x, 68.0);
-    }
-
-    #[test]
-    fn test_spreadsheet_scrolling() {
-        let mut spreadsheet = Spreadsheet::new();
-        // Visible height is 100px. Header is 24px, so body is 76px.
-        spreadsheet.set_rect(0.0, 0.0, 100.0, 100.0);
-        spreadsheet.set_visible(true);
-
-        let headers = vec!["ColA".to_string()];
-        // Each row is 24px. With 10 rows, content_h = 240px.
-        let mut rows = Vec::new();
-        for i in 0..10 {
-            rows.push(vec![format!("Row{}", i)]);
-        }
-        spreadsheet.set_spreadsheet_data(headers, rows);
-
-        // Content height is 240px, visible height is 100px (body is 76px).
-        // Since content height > visible body height, it should be draggable.
-        assert!(spreadsheet.draggable());
-
-        // Max scroll height = 240.0 - 76.0 = 164.0
-        
-        // Initial scroll position should be 0.0
-        assert_eq!(spreadsheet.scroll_y, 0.0);
-
-        // Scroll down via mouse wheel (positive delta scrolls content down, scroll_y increases via tick)
-        let delta = MouseScrollDelta::LineDelta(0.0, 2.0);
-        // Mouse over spreadsheet (50, 50)
-        let changed = spreadsheet.mouse_wheel(&delta, 50.0, 50.0);
-        assert!(changed);
-        assert_eq!(spreadsheet.scroll_y, 0.0);
-        assert!(spreadsheet.scroll_velocity > 0.0);
-
-        // Tick to apply velocity
-        let mut ticked_change = false;
-        for _ in 0..100 {
-            if spreadsheet.tick(0.016) {
-                ticked_change = true;
-            }
-        }
-        assert!(ticked_change);
-        assert!(spreadsheet.scroll_y > 0.0);
-        assert_eq!(spreadsheet.scroll_velocity, 0.0);
-
-        // Scroll back to top
-        let delta_up = MouseScrollDelta::LineDelta(0.0, -10.0);
-        spreadsheet.mouse_wheel(&delta_up, 50.0, 50.0);
-        assert!(spreadsheet.scroll_velocity < 0.0);
-
-        // Tick back to top
-        for _ in 0..100 {
-            spreadsheet.tick(0.016);
-        }
-        assert_eq!(spreadsheet.scroll_y, 0.0);
-        assert_eq!(spreadsheet.scroll_velocity, 0.0);
-
-        // Drag test
-        // Scrollbar width is 6px. Padding is 2px. Width is 100px.
-        // Scrollbar track x is from 92px to 98px.
-        // Let's drag. Click at (94, 50).
-        spreadsheet.drag_begin(94.0, 50.0);
-        assert!(spreadsheet.is_dragging());
-
-        // Update drag to y = 80
-        let changed_drag = spreadsheet.drag_update(94.0, 80.0);
-        assert!(changed_drag);
-        assert!(spreadsheet.scroll_y > 0.0);
-
-        // End drag
-        spreadsheet.drag_end();
-        assert!(!spreadsheet.is_dragging());
-    }
-
-    #[test]
-    fn test_spreadsheet_zero_height_no_panic() {
-        let mut spreadsheet = Spreadsheet::new();
-        // Visible height is set to 0.0
-        spreadsheet.set_rect(0.0, 0.0, 100.0, 0.0);
-        spreadsheet.set_visible(true);
-
-        let headers = vec!["ColA".to_string()];
-        let mut rows = Vec::new();
-        for i in 0..10 {
-            rows.push(vec![format!("Row{}", i)]);
-        }
-        // This should not panic
-        spreadsheet.set_spreadsheet_data(headers, rows);
-
-        // This should not panic
-        spreadsheet.cursor_moved(50.0, 50.0);
-        
-        let delta = MouseScrollDelta::LineDelta(0.0, -2.0);
-        // This should not panic
-        spreadsheet.mouse_wheel(&delta, 50.0, 50.0);
-
-        // This should not panic
-        assert!(!spreadsheet.draggable());
-
-        // This should not panic
-        spreadsheet.drag_begin(94.0, 50.0);
-        spreadsheet.drag_update(94.0, 80.0);
-        spreadsheet.drag_end();
-
-        // This should not panic and return empty quads for scrollbar
-        let _quads = spreadsheet.extra_quads();
-        // The header quad and divider (if any) are drawn, but scrollbar is not
-        // Let's verify that the scrollbar was not drawn
-        // (the last quad would be the scrollbar thumb with thumb_color if drawn,
-        // but here scrollbar track & thumb shouldn't be added)
-        assert_eq!(spreadsheet.scroll_y, 0.0);
-        
-        // Let's check text labels (should be empty because self.h is 0)
-        let labels = spreadsheet.text_labels();
-        // Headers labels are still generated since they don't depend on scroll/height,
-        // but rows shouldn't be
-        assert_eq!(labels.len(), 1); // Only header ColA
-    }
-
-    #[test]
-    fn test_scroll_box_bounds_scrolling() {
-        let mut sb = ScrollBox::new();
-        sb.set_rect(10.0, 20.0, 100.0, 100.0);
-        
-        // 1. Initially scroll is 0
-        assert_eq!(sb.scroll_y, 0.0);
-
-        // 2. Update bounds: content_h = 150 (greater than viewport_h = 100)
-        sb.update_bounds(150.0, 20.0, 100.0);
-        assert_eq!(sb.scroll_y, 0.0);
-        assert_eq!(sb.content_h, 150.0);
-        assert_eq!(sb.viewport_h, 100.0);
-
-        // 3. Scroll inside bounds
-        let delta = MouseScrollDelta::LineDelta(0.0, -2.0); // scroll down by 2 lines (48px)
-        let changed = sb.mouse_wheel(&delta, 50.0, 50.0);
-        assert!(changed);
-        assert_eq!(sb.scroll_y, 48.0);
-
-        // 4. Clamps at max scroll: 150 - 100 = 50
-        let delta_large = MouseScrollDelta::LineDelta(0.0, -10.0);
-        sb.mouse_wheel(&delta_large, 50.0, 50.0);
-        assert_eq!(sb.scroll_y, 50.0);
-
-        // 5. Test item draw coordinates
-        // Virtual item at virtual_y = 10, item_h = 24
-        // Screen draw y = viewport_y + virtual_y - scroll_y = 20 + 10 - 50 = -20
-        // -20 < viewport_y + 2.0 (22.0), so it should return None (not visible)
-        assert!(sb.get_item_draw_y(10.0, 24.0).is_none());
-
-        // Virtual item at virtual_y = 60, item_h = 24
-        // Screen draw y = 20 + 60 - 50 = 30
-        // 30 >= 22.0 and 30 + 24 <= 118.0, so it should return Some(30.0)
-        assert_eq!(sb.get_item_draw_y(60.0, 24.0), Some(30.0));
-    }
-
-    #[test]
-    fn test_dropdown_widget_interaction() {
-        let options = vec!["Option A".to_string(), "Option B".to_string(), "Option C".to_string()];
-        let mut dd = Dropdown::new(options, 0);
-        dd.set_rect(10.0, 10.0, 100.0, 24.0);
-
-        // 1. Initial State
-        assert!(!dd.open);
-        assert_eq!(dd.selected, 0);
-
-        // 2. Click trigger area opens dropdown
-        let input_changed = dd.mouse_input(MouseButton::Left, ElementState::Pressed, 50.0, 20.0);
-        assert!(input_changed);
-        assert!(dd.open);
-
-        // 3. Hovering options inside popover
-        // Popover starts at y = 10 + 24 = 34. Options are of height 24 each.
-        // Hover option B at y = 34 + 24 + 12 = 70.0
-        let move_changed = dd.cursor_moved(50.0, 70.0);
-        assert!(move_changed);
-        assert_eq!(dd.hovered_item, Some(1));
-
-        // 4. Click option B selects it and closes dropdown
-        let select_changed = dd.mouse_input(MouseButton::Left, ElementState::Pressed, 50.0, 70.0);
-        assert!(select_changed);
-        assert!(!dd.open);
-        assert_eq!(dd.selected, 1);
-        assert!(dd.take_change());
-    }
-
-    #[test]
-    fn test_textbox_selection_highlight() {
-        let mut tb = TextBox::new("Initial Text".to_string());
-        tb.set_rect(10.0, 10.0, 200.0, 30.0);
-
-        // 1. Initial state
-        assert!(!tb.editing);
-        assert!(!tb.all_selected);
-
-        // 2. Click focuses and triggers highlighting
-        let clicked = tb.mouse_input(MouseButton::Left, ElementState::Pressed, 50.0, 20.0);
-        assert!(clicked);
-        assert!(tb.editing);
-        assert!(tb.all_selected);
-        assert_eq!(tb.edit_buffer, "Initial Text");
-
-        // 3. Typing a key replaces all text
-        let key_ev = KeyEvent {
-            state: ElementState::Pressed,
-            logical_key: Key::Character("A".to_string()),
-            text: Some("A".to_string()),
-            repeat: false,
-            ctrl: false,
-            shift: false,
-        };
-        let handled = tb.keyboard_input(&key_ev);
-        assert!(handled);
-        assert!(!tb.all_selected);
-        assert_eq!(tb.edit_buffer, "A");
-
-        // 4. Pressing Enter commits change
-        let enter_ev = KeyEvent {
-            state: ElementState::Pressed,
-            logical_key: Key::Named(NamedKey::Enter),
-            text: None,
-            repeat: false,
-            ctrl: false,
-            shift: false,
-        };
-        let handled_enter = tb.keyboard_input(&enter_ev);
-        assert!(handled_enter);
-        assert!(!tb.editing);
-        assert_eq!(tb.text, "A");
-        assert!(tb.take_change());
-    }
-
-    #[test]
-    fn test_textbox_drag_and_modifier_selection() {
-        let mut tb = TextBox::new("Hello World".to_string());
-        tb.set_rect(10.0, 10.0, 200.0, 30.0);
-
-        // 1. Initial click focuses and selects all
-        let pressed = tb.mouse_input(MouseButton::Left, ElementState::Pressed, 50.0, 20.0);
-        assert!(pressed);
-        let released = tb.mouse_input(MouseButton::Left, ElementState::Released, 50.0, 20.0);
-        assert!(released);
-        assert!(tb.editing);
-        assert!(tb.all_selected);
-        assert_eq!(tb.cursor_idx, 11);
-        assert_eq!(tb.select_anchor, Some(0));
-
-        // 2. Click inside placed caret at index 5 (x = 10 + 8 + 5 * 7.2 = 54)
-        let pressed_inside = tb.mouse_input(MouseButton::Left, ElementState::Pressed, 54.0, 20.0);
-        assert!(pressed_inside);
-        assert_eq!(tb.cursor_idx, 5);
-        assert_eq!(tb.select_anchor, Some(5));
-        assert!(!tb.all_selected);
-
-        // 3. Drag to index 11 (x = 10 + 8 + 11 * 7.2 = 97.2)
-        tb.drag_begin(54.0, 20.0);
-        let updated = tb.drag_update(97.2, 20.0);
-        assert!(updated);
-        assert_eq!(tb.cursor_idx, 11);
-        assert_eq!(tb.select_anchor, Some(5));
-        tb.drag_end();
-
-        // 4. Keyboard ArrowLeft with Shift shrinks selection from 11 to 10
-        let left_shift_ev = KeyEvent {
-            state: ElementState::Pressed,
-            logical_key: Key::Named(NamedKey::ArrowLeft),
-            text: None,
-            repeat: false,
-            ctrl: false,
-            shift: true,
-        };
-        let handled = tb.keyboard_input(&left_shift_ev);
-        assert!(handled);
-        assert_eq!(tb.cursor_idx, 10);
-        assert_eq!(tb.select_anchor, Some(5));
-
-        // 5. Keyboard ArrowLeft without Shift collapses selection to start (index 5)
-        let left_ev = KeyEvent {
-            state: ElementState::Pressed,
-            logical_key: Key::Named(NamedKey::ArrowLeft),
-            text: None,
-            repeat: false,
-            ctrl: false,
-            shift: false,
-        };
-        let handled = tb.keyboard_input(&left_ev);
-        assert!(handled);
-        assert_eq!(tb.cursor_idx, 5);
-        assert_eq!(tb.select_anchor, None);
-
-        // 6. Keyboard Shift+Up highlights to beginning (cursor 0, anchor 5)
-        let up_shift_ev = KeyEvent {
-            state: ElementState::Pressed,
-            logical_key: Key::Named(NamedKey::ArrowUp),
-            text: None,
-            repeat: false,
-            ctrl: false,
-            shift: true,
-        };
-        let handled = tb.keyboard_input(&up_shift_ev);
-        assert!(handled);
-        assert_eq!(tb.cursor_idx, 0);
-        assert_eq!(tb.select_anchor, Some(5));
-
-        // 7. Typing a key replaces selected range "Hello" with "Rust"
-        let rust_ev = KeyEvent {
-            state: ElementState::Pressed,
-            logical_key: Key::Character("Rust".to_string()),
-            text: Some("Rust".to_string()),
-            repeat: false,
-            ctrl: false,
-            shift: false,
-        };
-        let handled = tb.keyboard_input(&rust_ev);
-        assert!(handled);
-        assert_eq!(tb.edit_buffer, "Rust World");
-        assert_eq!(tb.cursor_idx, 4);
-        assert_eq!(tb.select_anchor, None);
-    }
-
-    #[test]
-    fn test_paginator_rotated_tabs() {
-        let pages = vec!["📁 Browse".to_string(), "🌐 Network".to_string()];
-        let mut paginator = Paginator::new(56.0, pages)
-            .with_tab_y_offset(100.0)
-            .with_tabs_rotated(true);
-        
-        paginator.set_rect(0.0, 0.0, 1000.0, 600.0);
-
-        // Verify target_y is updated correctly
-        // selected_page = 0: tab_y_offset = 100.0
-        assert_eq!(paginator.target_y, 100.0);
-
-        paginator.set_selected_page(1);
-        // selected_page = 1: tab_y_offset + 1 * (120.0 + 10.0) = 230.0
-        assert_eq!(paginator.target_y, 230.0);
-
-        // Verify hover coordinates
-        // Tab 0 bx/by bounds:
-        // tab_w = 32.0, tab_h = 120.0, spacing = 10.0, sidebar_w = 48.0
-        // bx = self.x + (sidebar_w - tab_w) / 2 = 8.0
-        // by = self.y + tab_y_offset + i * 130.0 = 100.0
-        // bx range: [8.0, 40.0], by range: [100.0, 220.0]
-        
-        // Hover at (24.0, 150.0) should hit Tab 0
-        let hover_tab0 = paginator.on_cursor_moved(24.0, 150.0);
-        assert!(hover_tab0);
-        assert_eq!(paginator.hovered_tab, Some(0));
-
-        // Hover at (24.0, 280.0) should hit Tab 1 (by range: [230.0, 350.0])
-        let hover_tab1 = paginator.on_cursor_moved(24.0, 280.0);
-        assert!(hover_tab1);
-        assert_eq!(paginator.hovered_tab, Some(1));
-
-        // Clicking Tab 0 selects it
-        let click_tab0 = paginator.mouse_input(MouseButton::Left, ElementState::Pressed, 24.0, 150.0);
-        assert!(click_tab0);
-        assert_eq!(paginator.pressed_tab, Some(0));
-
-        let release_tab0 = paginator.mouse_input(MouseButton::Left, ElementState::Released, 24.0, 150.0);
-        assert!(release_tab0);
-        assert_eq!(paginator.selected_page, 0);
-
-        // Check vertical text formatting
-        let labels = paginator.text_labels();
-        assert_eq!(labels.len(), 2);
-        assert_eq!(labels[0].text, "📁");
-        assert_eq!(labels[1].text, "🌐");
-        
-        // Check that rotated text quads were generated
-        assert!(!paginator.tab_text_quads[0].is_empty());
-        assert!(!paginator.tab_text_quads[1].is_empty());
-    }
-
-    #[test]
-    fn test_svg_text_rendering() {
-        let svg_data = r##"<svg width="32" height="120" xmlns="http://www.w3.org/2000/svg">
-  <text x="16" y="60" font-family="sans-serif" font-size="12" fill="#E6E6F2" text-anchor="middle" dominant-baseline="middle" transform="rotate(-90 16 60)">Audio</text>
-</svg>"##.as_bytes();
-
-        let opt = resvg::usvg::Options::default();
-        let mut fontdb = resvg::usvg::fontdb::Database::new();
-        fontdb.load_system_fonts();
-        let tree = resvg::usvg::Tree::from_data(svg_data, &opt, &fontdb).unwrap();
-        
-        let mut pixmap = resvg::tiny_skia::Pixmap::new(32, 120).unwrap();
-        resvg::render(&tree, resvg::tiny_skia::Transform::default(), &mut pixmap.as_mut());
-        
-        pixmap.save_png("/home/lsgalante/Dropbox/Clear/scratch/test_svg.png").unwrap();
-
-        // Check that some pixels were drawn (are non-transparent)
-        let pixels = pixmap.data();
-        let mut non_transparent = 0;
-        for i in (3..pixels.len()).step_by(4) {
-            if pixels[i] > 0 {
-                non_transparent += 1;
-            }
-        }
-        assert!(non_transparent > 0, "Should have rendered some text pixels");
-    }
-}
-
-// Generic text item layout wrapper
-#[derive(Debug, Clone)]
-pub struct ScrollingList {
-    pub scroll_box: ScrollBox,
-    pub item_height: f32,
-    pub item_gap: f32,
-}
-
-impl ScrollingList {
-    pub fn new(item_height: f32, item_gap: f32) -> Self {
-        Self {
-            scroll_box: ScrollBox::new(),
-            item_height,
-            item_gap,
-        }
-    }
-
-    pub fn update_bounds(&mut self, count: usize, viewport_y: f32, viewport_h: f32) {
-        let item_height_full = self.item_height + self.item_gap;
-        let content_h = count as f32 * item_height_full;
-        self.scroll_box.update_bounds(content_h, viewport_y, viewport_h);
-    }
-
-    pub fn get_item_draw_y(&self, idx: usize, offset: f32) -> Option<f32> {
-        let item_height_full = self.item_height + self.item_gap;
-        let virtual_y = idx as f32 * item_height_full + offset;
-        self.scroll_box.get_item_draw_y(virtual_y, self.item_height)
-    }
-
-    pub fn scroll_y(&self) -> f32 {
-        self.scroll_box.scroll_y
-    }
-
-    pub fn set_scroll_y(&mut self, val: f32) {
-        self.scroll_box.scroll_y = val;
-    }
-}
-
-impl Default for ScrollingList {
-    fn default() -> Self {
-        Self::new(24.0, 4.0)
-    }
-}
-
-impl Element for ScrollingList {
-    fn rect(&self) -> (f32, f32, f32, f32) {
-        self.scroll_box.rect()
-    }
-
-    fn set_rect(&mut self, x: f32, y: f32, w: f32, h: f32) {
-        self.scroll_box.set_rect(x, y, w, h);
-    }
-
-    fn color(&self) -> [f32; 4] {
-        self.scroll_box.color()
-    }
-
-    fn set_hovered(&mut self, v: bool) {
-        self.scroll_box.set_hovered(v);
-    }
-
-    fn hovered(&self) -> bool {
-        self.scroll_box.hovered()
-    }
-
-    fn highlight_color(&self) -> Option<[f32; 4]> {
-        self.scroll_box.highlight_color()
-    }
-
-    fn on_cursor_moved(&mut self, px: f32, py: f32) -> bool {
-        self.scroll_box.cursor_moved(px, py)
-    }
-
-    fn mouse_wheel(&mut self, delta: &MouseScrollDelta, px: f32, py: f32) -> bool {
-        self.scroll_box.mouse_wheel(delta, px, py)
-    }
-
-    fn mouse_input(&mut self, button: MouseButton, state: ElementState, px: f32, py: f32) -> bool {
-        self.scroll_box.mouse_input(button, state, px, py)
-    }
-
-    fn focus(&mut self) {
-        self.scroll_box.focus();
-    }
-
-    fn unfocus(&mut self) {
-        self.scroll_box.unfocus();
-    }
-
-    fn extra_quads(&self) -> Vec<(f32, f32, f32, f32, [f32; 4])> {
-        self.scroll_box.extra_quads()
-    }
-
-    fn keyboard_input(&mut self, event: &KeyEvent) -> bool {
-        self.scroll_box.keyboard_input(event)
-    }
-
-    fn parent(&self) -> Option<*mut (dyn Element + 'static)> { self.scroll_box.parent() }
-    fn set_parent(&mut self, parent: Option<*mut (dyn Element + 'static)>) { self.scroll_box.set_parent(parent); }
-    fn children(&self) -> Vec<*mut (dyn Element + 'static)> { self.scroll_box.children() }
-    fn add_child(&mut self, child: *mut (dyn Element + 'static)) { self.scroll_box.add_child(child); }
-    fn clear_children(&mut self) { self.scroll_box.clear_children(); }
-
-    fn update_bounds(&mut self, count: usize, viewport_y: f32, viewport_h: f32) {
-        self.update_bounds(count, viewport_y, viewport_h);
-    }
-
-    fn get_item_draw_y(&self, idx: usize, offset: f32) -> Option<f32> {
-        self.get_item_draw_y(idx, offset)
-    }
-}
-
-unsafe impl Send for Container {}
-unsafe impl Sync for Container {}
-unsafe impl Send for ScrollBox {}
-unsafe impl Sync for ScrollBox {}
-unsafe impl Send for Spinbox {}
-unsafe impl Sync for Spinbox {}
-unsafe impl Send for ColorSelector {}
-unsafe impl Sync for ColorSelector {}
-unsafe impl Send for ScrollingList {}
-unsafe impl Sync for ScrollingList {}
-
-// ── Dropdown Widget ──
-
-#[derive(Debug, Clone)]
-pub struct Plate {
-    pub base: Widget,
-    pub dragging: bool,
-    pub drag_ox: f32,
-    pub drag_oy: f32,
-    pub drag_start_x: f32,
-    pub drag_start_y: f32,
-    pub bounds: Option<(f32, f32, f32, f32)>,
-    pub color: Option<[f32; 4]>,
-    pub curved_circle: Option<(f32, f32, f32)>,
-    pub network_opacity: f32,
-    pub blur: bool,
-    pub children: Vec<*mut (dyn Element + 'static)>,
-    pub parent: Option<*mut (dyn Element + 'static)>,
-    pub visible: bool,
-    pub column_layout: bool,
-}
-
-impl Plate {
-    pub fn new(x: f32, y: f32, w: f32, h: f32) -> Self {
-        Self {
-            base: Widget::new_rect(x, y, w, h),
-            dragging: false,
-            drag_ox: 0.0,
-            drag_oy: 0.0,
-            drag_start_x: 0.0,
-            drag_start_y: 0.0,
-            bounds: None,
-            color: None,
-            curved_circle: None,
-            network_opacity: 1.0,
-            blur: true,
-            children: Vec::new(),
-            parent: None,
-            visible: true,
-            column_layout: false,
-        }
-    }
-
-    pub fn with_color(mut self, color: [f32; 4]) -> Self {
-        self.color = Some(color);
-        self
-    }
-
-    pub fn with_label(mut self, label: &str) -> Self {
-        self.base.label = Some(label.to_string());
-        self
-    }
-
-    pub fn with_blur(mut self, blur: bool) -> Self {
-        self.blur = blur;
-        self
-    }
-
-    pub fn set_bounds(&mut self, bx: f32, by: f32, bw: f32, bh: f32) {
-        self.bounds = Some((bx, by, bw, bh));
-    }
-}
-
-impl Element for Plate {
-    crate::impl_widget_base!(Plate);
-    fn is_plate(&self) -> bool { true }
-    fn rounded_corners(&self) -> (bool, bool, bool, bool) { (true, true, true, true) }
-    fn highlight_quad(&self) -> Option<(f32, f32, f32, f32, [f32; 4])> { None }
-
-    fn set_modifiers(&mut self, ctrl: bool, shift: bool, alt: bool) {
-        for &child_ptr in &self.children {
-            unsafe {
-                (*child_ptr).set_modifiers(ctrl, shift, alt);
-            }
-        }
-    }
-
-    fn visible(&self) -> bool {
-        self.visible
-    }
-
-    fn color(&self) -> [f32; 4] {
-        let mut c = if let Some(c) = self.color {
-            c
-        } else if self.dragging {
-            colors::PANEL_DRAG
-        } else {
-            colors::PANEL_IDLE
-        };
-        c[3] *= self.network_opacity;
-        if self.blur {
-            c[3] = -c[3].abs();
-        }
-        c
-    }
-
-    fn set_network_opacity(&mut self, opacity: f32) {
-        self.network_opacity = opacity;
-    }
-
-    fn set_drag_bounds(&mut self, bx: f32, by: f32, bw: f32, bh: f32) {
-        self.bounds = Some((bx, by, bw, bh));
-    }
-
-    fn set_curved_circle(&mut self, circle: Option<(f32, f32, f32)>) {
-        self.curved_circle = circle;
-    }
-
-    fn hit_test(&self, px: f32, py: f32) -> bool {
-        if crate::widget::popovers::is_coordinate_covered(self as *const Self as *const () as usize, px, py) {
-            return false;
-        }
-        if let Some((cx, cy, r)) = self.curved_circle {
-            let dx = px - cx;
-            let dy = py - cy;
-            return dx * dx + dy * dy <= r * r;
-        }
-        
-        let (x, y, w, h) = self.rect();
-        if px < x || px >= x + w || py < y || py >= y + h {
-            return false;
-        }
-        
-        let r = 12.0f32.min(w * 0.5).min(h * 0.5);
-        if r <= 0.1 {
-            return true;
-        }
-        
-        // Check corners
-        if px < x + r && py < y + r {
-            let dx = px - (x + r);
-            let dy = py - (y + r);
-            return dx * dx + dy * dy <= r * r;
-        }
-        if px >= x + w - r && py < y + r {
-            let dx = px - (x + w - r);
-            let dy = py - (y + r);
-            return dx * dx + dy * dy <= r * r;
-        }
-        if px >= x + w - r && py >= y + h - r {
-            let dx = px - (x + w - r);
-            let dy = py - (y + h - r);
-            return dx * dx + dy * dy <= r * r;
-        }
-        if px < x + r && py >= y + h - r {
-            let dx = px - (x + r);
-            let dy = py - (y + h - r);
-            return dx * dx + dy * dy <= r * r;
-        }
-        
-        true
-    }
-
-    fn set_rect(&mut self, x: f32, y: f32, w: f32, h: f32) {
-        if let Some(b) = self.base_mut() {
-            b.x = x;
-            b.y = y;
-            b.w = w;
-            b.h = h;
-        }
-
-        if !self.visible {
-            return;
-        }
-
-        let padding_x = 20.0;
-        let padding_y = 20.0;
-        let left_x = x + padding_x;
-        let available_w = (w - 2.0 * padding_x).max(1.0);
-        let start_y = y + padding_y;
-        let available_h = (h - 2.0 * padding_y).max(1.0);
-
-        let center_x = left_x + available_w / 2.0;
-        let center_y = start_y + available_h / 2.0;
-        let aspect_ratio = available_w / available_h;
-
-        let mut active_widgets = Vec::new();
-        for &w_ptr in &self.children {
-            let w = unsafe { &*w_ptr };
-            if !w.layout_ignore() {
-                active_widgets.push(w_ptr);
-            }
-        }
-
-        if self.column_layout {
-            let mut current_y = start_y;
-            let spacing = 12.0;
-            for &w_ptr in &active_widgets {
-                let w = unsafe { &mut *w_ptr };
-                let (_, _, ww, wh) = w.rect();
-                let use_w = if ww > 0.0 { ww.min(available_w) } else { available_w };
-                let top = crate::widget::label_offset(w);
-                let use_h = if wh > 0.0 { wh } else { 24.0 + top };
-
-                w.set_rect(left_x, current_y, use_w, use_h);
-                current_y += use_h + spacing;
-            }
-        } else {
-            let mut total_diagonal = 0.0;
-            let mut count = 0;
-            for &w_ptr in &active_widgets {
-                let w = unsafe { &*w_ptr };
-                let (_, _, ww, wh) = w.rect();
-                let use_w = if ww > 0.0 { ww.min(available_w) } else { available_w };
-                let use_h = if wh > 0.0 { wh } else { 50.0 };
-                total_diagonal += (use_w * use_w + use_h * use_h).sqrt();
-                count += 1;
-            }
-            let avg_diagonal = if count > 0 { total_diagonal / count as f32 } else { 100.0 };
-            let base_spacing = (avg_diagonal * 0.55).max(60.0);
-
-            for (i, &w_ptr) in active_widgets.iter().enumerate() {
-                let w = unsafe { &mut *w_ptr };
-                let (_, _, ww, wh) = w.rect();
-                let use_w = if ww > 0.0 { ww.min(available_w) } else { available_w };
-                let use_h = if wh > 0.0 { wh } else { 50.0 };
-
-                if i == 0 {
-                    w.set_rect(center_x - use_w / 2.0, center_y - use_h / 2.0, use_w, use_h);
-                } else {
-                    let mut ring = 1;
-                    let mut ring_start = 1;
-                    let mut placed = false;
-                    while !placed {
-                        let ring_capacity = ring * 6;
-                        if i < ring_start + ring_capacity {
-                            let pos_in_ring = i - ring_start;
-                            let angle = (pos_in_ring as f32) * (2.0 * std::f32::consts::PI / ring_capacity as f32);
-                            let radius = (ring as f32) * base_spacing;
-
-                            let x_offset = radius * angle.cos() * aspect_ratio;
-                            let y_offset = radius * angle.sin();
-
-                            w.set_rect(
-                                center_x + x_offset - use_w / 2.0,
-                                center_y + y_offset - use_h / 2.0,
-                                use_w,
-                                use_h,
-                            );
-                            placed = true;
-                        } else {
-                            ring_start += ring_capacity;
-                            ring += 1;
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    fn parent(&self) -> Option<*mut (dyn Element + 'static)> {
-        self.parent
-    }
-
-    fn set_parent(&mut self, parent: Option<*mut (dyn Element + 'static)>) {
-        self.parent = parent;
-    }
-
-    fn children(&self) -> Vec<*mut (dyn Element + 'static)> {
-        self.children.clone()
-    }
-
-    fn add_child(&mut self, child: *mut (dyn Element + 'static)) {
-        self.children.push(child);
-    }
-
-    fn clear_children(&mut self) {
-        self.children.clear();
-    }
-
-    fn extra_quads(&self) -> Vec<(f32, f32, f32, f32, [f32; 4])> {
-        if !self.visible {
-            return Vec::new();
-        }
-        let mut quads = Vec::new();
-        let (px, py, pw, ph) = self.rect();
-        quads.push((px, py, pw, ph, self.color()));
-
-        for &child_ptr in &self.children {
-            let widget = unsafe { &*child_ptr };
-            let c = widget.color();
-            if c[3] > 0.0 {
-                let (wx, wy, ww, wh) = widget.rect();
-                quads.push((wx, wy, ww, wh, c));
-            }
-            quads.extend(widget.all_quads());
-        }
-        quads
-    }
-
-    fn text_labels(&self) -> Vec<TextLabel> {
-        if !self.visible {
-            return Vec::new();
-        }
-        let mut labels = Vec::new();
-        if let Some(ref label) = self.base.label {
-            labels.push(TextLabel {
-                text: label.clone(),
-                x: self.base.x,
-                y: self.base.y - 18.0,
-                font_size: 12.0,
-                color: [0x83, 0x83, 0x8a],
-            });
-        }
-        for &child_ptr in &self.children {
-            let widget = unsafe { &*child_ptr };
-            labels.extend(widget.text_labels());
-        }
-        labels
-    }
-
-    fn text_labels_with_bounds(&self) -> Vec<(TextLabel, Option<[f32; 4]>)> {
-        if !self.visible {
-            return Vec::new();
-        }
-        let mut result = Vec::new();
-        if let Some(ref label) = self.base.label {
-            result.push((
-                TextLabel {
-                    text: label.clone(),
-                    x: self.base.x,
-                    y: self.base.y - 18.0,
-                    font_size: 12.0,
-                    color: [0x83, 0x83, 0x8a],
-                },
-                None,
-            ));
-        }
-        for &child_ptr in &self.children {
-            let widget = unsafe { &*child_ptr };
-            result.extend(widget.text_labels_with_bounds());
-        }
-        result
-    }
-
-    fn on_cursor_moved(&mut self, px: f32, py: f32) -> bool {
-        if !self.visible {
-            return false;
-        }
-        let mut changed = false;
-        for &widget_ptr in &self.children {
-            let widget = unsafe { &mut *widget_ptr };
-            if widget.is_dragging() {
-                if widget.drag_update(px, py) {
-                    changed = true;
-                }
-            } else if widget.cursor_moved(px, py) {
-                changed = true;
-            }
-        }
-        changed
-    }
-
-    fn mouse_input(&mut self, button: MouseButton, state: ElementState, px: f32, py: f32) -> bool {
-        if !self.visible {
-            return false;
-        }
-        for &widget_ptr in self.children.iter().rev() {
-            let widget = unsafe { &mut *widget_ptr };
-            if widget.popover_rect().is_some() {
-                if widget.mouse_input(button, state, px, py) {
-                    return true;
-                }
-            }
-        }
-        for &widget_ptr in self.children.iter().rev() {
-            let widget = unsafe { &mut *widget_ptr };
-            if widget.mouse_input(button, state, px, py) {
-                return true;
-            }
-            if state == ElementState::Pressed && !widget.hit_test(px, py) {
-                widget.unfocus();
-            }
-        }
-
-        if button != MouseButton::Left { return false; }
-        match state {
-            ElementState::Pressed => {
-                if self.hit_test(px, py) {
-                    self.drag_begin(px, py);
-                    return true;
-                }
-            }
-            ElementState::Released => {
-                if self.dragging { self.drag_end(); return true; }
-            }
-        }
-        false
-    }
-
-    fn keyboard_input(&mut self, event: &KeyEvent) -> bool {
-        if !self.visible {
-            return false;
-        }
-        for &widget_ptr in &self.children {
-            let widget = unsafe { &mut *widget_ptr };
-            if widget.keyboard_input(event) {
-                return true;
-            }
-        }
-        false
-    }
-
-    fn mouse_wheel(&mut self, delta: &MouseScrollDelta, px: f32, py: f32) -> bool {
-        if !self.visible {
-            return false;
-        }
-        for &widget_ptr in self.children.iter().rev() {
-            let widget = unsafe { &mut *widget_ptr };
-            if widget.mouse_wheel(delta, px, py) {
-                return true;
-            }
-        }
-        false
-    }
-
-    fn popover_rect(&self) -> Option<(f32, f32, f32, f32)> {
-        if !self.visible {
-            return None;
-        }
-        for &widget_ptr in self.children.iter().rev() {
-            let widget = unsafe { &*widget_ptr };
-            if let Some(r) = widget.popover_rect() {
-                return Some(r);
-            }
-        }
-        None
-    }
-
-    fn render_popover(&self, pc: &mut dyn crate::layout::RenderTarget) {
-        if !self.visible {
-            return;
-        }
-        for &widget_ptr in self.children.iter().rev() {
-            let widget = unsafe { &*widget_ptr };
-            widget.render_popover(pc);
-        }
-    }
-
-    fn tick(&mut self, dt: f32) -> bool {
-        if !self.visible {
-            return false;
-        }
-        let mut changed = false;
-        for &widget_ptr in &self.children {
-            let widget = unsafe { &mut *widget_ptr };
-            if widget.tick(dt) {
-                changed = true;
-            }
-        }
-        changed
-    }
-
-    fn drag_update(&mut self, px: f32, py: f32) -> bool {
-        let nx = px - self.drag_ox;
-        let ny = py - self.drag_oy;
-        let (nx, ny) = if let Some((bx, by, bw, bh)) = self.bounds {
-            (nx.clamp(bx, bx + bw - self.base.w), ny.clamp(by, by + bh - self.base.h))
-        } else {
-            (nx, ny)
-        };
-        if (nx - self.base.x).abs() > 0.01 || (ny - self.base.y).abs() > 0.01 {
-            let dx = nx - self.base.x;
-            let dy = ny - self.base.y;
-            self.base.x = nx;
-            self.base.y = ny;
-            
-            for &child_ptr in &self.children {
-                unsafe {
-                    let (cx, cy, cw, ch) = (*child_ptr).rect();
-                    (*child_ptr).set_rect(cx + dx, cy + dy, cw, ch);
-                }
-            }
-            return true;
-        }
-        false
-    }
-
-    fn drag_begin(&mut self, px: f32, py: f32) {
-        self.dragging = true;
-        self.drag_ox = px - self.base.x;
-        self.drag_oy = py - self.base.y;
-        self.drag_start_x = self.base.x;
-        self.drag_start_y = self.base.y;
-    }
-
-    fn drag_end(&mut self) { self.dragging = false; }
-}
-
-unsafe impl Send for Plate {}
-unsafe impl Sync for Plate {}
-
-pub struct Paginator {
-    x: f32,
-    y: f32,
-    w: f32,
-    h: f32,
-    hovered: bool,
-    pub sidebar_menu: MenuBar,
-    pub plates: Vec<Plate>,
-    pub selected_page: usize,
-    pub page_changed: bool,
-    pub sidebar_label: Option<String>,
-    pub tab_text_quads: Vec<Vec<(f32, f32, f32, f32, [f32; 4])>>,
-    pub sidebar_scroll_y: f32,
-    pub scale_factor: f32,
-    pub sidebar_mode: bool,
-    pub page_hidden: bool,
-    pub sidebar_w: f32,
-    pub pages: Vec<String>,
-    pub tabs_at_top: bool,
-    pub tab_y_offset: f32,
-    pub tabs_rotated: bool,
-    pub hovered_tab: Option<usize>,
-    pub pressed_tab: Option<usize>,
-    pub target_y: f32,
-    pub target_x: f32,
-    pub current_y: Option<f32>,
-    pub current_x: Option<f32>,
-    parent: Option<*mut (dyn Element + 'static)>,
-}
-
-impl Paginator {
-    pub fn sidebar_w(&self) -> f32 {
-        self.sidebar_w
-    }
-
-    pub fn new(sidebar_w: f32, pages: Vec<String>) -> Self {
-        let num_pages = pages.len();
-        
-        let mut sidebar_menu = MenuBar::new(0.0, 0.0, sidebar_w, 0.0)
-            .with_vertical(true);
-        for page in &pages {
-            sidebar_menu = sidebar_menu.with_item(page, &[]);
-        }
-
-        let mut plates = Vec::new();
-        for _ in 0..num_pages {
-            let mut plate = Plate::new(0.0, 0.0, 0.0, 0.0);
-            plate.visible = false;
-            plates.push(plate);
-        }
-        if num_pages > 0 {
-            plates[0].visible = true;
-            sidebar_menu.menus[0].set_selected(true);
-        }
-
-        let mut pag = Self {
-            x: 0.0,
-            y: 0.0,
-            w: 0.0,
-            h: 0.0,
-            hovered: false,
-            sidebar_menu,
-            plates,
-            selected_page: 0,
-            page_changed: false,
-            sidebar_label: None,
-            tab_text_quads: Vec::new(),
-            sidebar_scroll_y: 0.0,
-            scale_factor: 1.0,
-            sidebar_mode: true,
-            page_hidden: false,
-            sidebar_w,
-            pages,
-            tabs_at_top: false,
-            tab_y_offset: 10.0,
-            tabs_rotated: true,
-            hovered_tab: None,
-            pressed_tab: None,
-            target_y: 10.0,
-            target_x: 0.0,
-            current_y: Some(10.0),
-            current_x: Some(0.0),
-            parent: None,
-        };
-        pag.update_target_pos();
-        pag
-    }
-
-    pub fn tab_rect(&self, idx: usize) -> (f32, f32, f32, f32) {
-        if idx >= self.pages.len() {
-            return (0.0, 0.0, 0.0, 0.0);
-        }
-        if self.tabs_at_top {
-            let tab_w = if self.pages.is_empty() { 0.0 } else { self.w / self.pages.len() as f32 };
-            (self.x + idx as f32 * tab_w, self.y, tab_w, 40.0)
-        } else if self.tabs_rotated {
-            let (tab_w, tab_h) = self.vertical_tab_size();
-            let spacing = 10.0;
-            (
-                self.x + (self.sidebar_w - tab_w) / 2.0,
-                self.y + self.tab_y_offset + idx as f32 * (tab_h + spacing) - self.sidebar_scroll_y,
-                tab_w,
-                tab_h,
-            )
-        } else {
-            let bw = self.sidebar_w - 10.0;
-            (self.x + 5.0, self.y + self.tab_y_offset + idx as f32 * 50.0 - self.sidebar_scroll_y, bw, 40.0)
-        }
-    }
-
-    pub fn tab_size(&self, idx: usize) -> (f32, f32) {
-        let r = self.tab_rect(idx);
-        (r.2, r.3)
-    }
-
-    pub fn with_column_layout(mut self, enabled: bool) -> Self {
-        for plate in &mut self.plates {
-            plate.column_layout = enabled;
-        }
-        self
-    }
-
-    pub fn with_sidebar_mode(mut self, enabled: bool) -> Self {
-        self.sidebar_mode = enabled;
-        self
-    }
-
-    pub fn with_sidebar_label(mut self, label: &str) -> Self {
-        self.sidebar_label = Some(label.to_string());
-        self
-    }
-
-    pub fn sidebar_label_height(&self) -> f32 {
-        if self.sidebar_label.is_some() {
-            24.0
-        } else {
-            0.0
-        }
-    }
-
-    pub fn is_page_hidden(&self) -> bool {
-        self.page_hidden
-    }
-
-    pub fn set_page_hidden(&mut self, hidden: bool) {
-        self.page_hidden = hidden;
-    }
-
-    pub fn set_sidebar_mode(&mut self, enabled: bool) {
-        self.sidebar_mode = enabled;
-    }
-
-    pub fn add_widget_to_page(&mut self, page_idx: usize, widget: *mut (dyn Element + 'static)) {
-        if page_idx < self.plates.len() {
-            self.plates[page_idx].add_child(widget);
-            unsafe {
-                (*widget).set_parent(Some(&mut self.plates[page_idx] as *mut _));
-            }
-        }
-    }
-
-    pub fn clear_page_widgets(&mut self, page_idx: usize) {
-        if page_idx < self.plates.len() {
-            self.plates[page_idx].clear_children();
-        }
-    }
-
-    pub fn with_tabs_at_top(mut self, top: bool) -> Self {
-        self.tabs_at_top = top;
-        self.update_target_pos();
-        self
-    }
-
-    pub fn with_tab_y_offset(mut self, offset: f32) -> Self {
-        self.tab_y_offset = offset;
-        if self.current_y == Some(10.0) {
-            self.current_y = Some(offset);
-        }
-        self.update_target_pos();
-        self
-    }
-
-    pub fn with_tabs_rotated(mut self, rotated: bool) -> Self {
-        self.tabs_rotated = rotated;
-        self.update_target_pos();
-        self
-    }
-
-    pub fn selected_page(&self) -> usize {
-        self.selected_page
-    }
-
-    pub fn set_selected_page(&mut self, page: usize) {
-        if page < self.plates.len() {
-            if self.selected_page != page {
-                self.plates[self.selected_page].visible = false;
-                self.selected_page = page;
-                self.plates[self.selected_page].visible = true;
-                self.update_target_pos();
-                
-                // Update MenuBar focus/selection
-                for (i, menu) in self.sidebar_menu.menus.iter_mut().enumerate() {
-                    menu.set_selected(i == page);
-                }
-            }
-        }
-    }
-
-    pub fn set_pages(&mut self, pages: Vec<String>) {
-        self.pages = pages.clone();
-        let num_pages = pages.len();
-        
-        let mut sidebar_menu = MenuBar::new(0.0, 0.0, self.sidebar_w, 0.0)
-            .with_vertical(true);
-        for page in &pages {
-            sidebar_menu = sidebar_menu.with_item(page, &[]);
-        }
-        self.sidebar_menu = sidebar_menu;
-
-        let mut plates = Vec::new();
-        for _ in 0..num_pages {
-            let mut plate = Plate::new(0.0, 0.0, 0.0, 0.0);
-            plate.visible = false;
-            plates.push(plate);
-        }
-        self.plates = plates;
-        if self.selected_page >= num_pages {
-            self.selected_page = 0;
-        }
-        if !self.plates.is_empty() {
-            self.plates[self.selected_page].visible = true;
-            self.sidebar_menu.menus[self.selected_page].set_selected(true);
-        }
-        self.update_target_pos();
-    }
-
-    pub fn set_scale_factor(&mut self, scale: f32) {
-        self.scale_factor = scale;
-    }
-
-    pub fn vertical_tab_size(&self) -> (f32, f32) {
-        if self.tabs_rotated {
-            ((self.sidebar_w - 16.0).clamp(24.0, 120.0), 120.0)
-        } else {
-            (self.sidebar_w - 10.0, 40.0)
-        }
-    }
-
-    fn total_sidebar_height(&self) -> f32 {
-        let (_, tab_h) = self.vertical_tab_size();
-        let spacing = 10.0;
-        let step = if self.tabs_rotated { tab_h + spacing } else { 50.0 };
-        self.tab_y_offset + self.pages.len() as f32 * step - spacing
-    }
-
-    fn update_target_pos(&mut self) {
-        if self.tabs_at_top {
-            let tab_w = if self.pages.is_empty() { 0.0 } else { self.w / self.pages.len() as f32 };
-            self.target_x = self.selected_page as f32 * tab_w;
-            if self.current_x.is_none() {
-                self.current_x = Some(self.target_x);
-            }
-        } else if self.tabs_rotated {
-            let (_, tab_h) = self.vertical_tab_size();
-            let spacing = 10.0;
-            let target = self.tab_y_offset + self.selected_page as f32 * (tab_h + spacing);
-            self.target_y = target;
-            if self.current_y.is_none() {
-                self.current_y = Some(target);
-            }
-        } else {
-            let target = self.tab_y_offset + self.selected_page as f32 * 50.0;
-            self.target_y = target;
-            if self.current_y.is_none() {
-                self.current_y = Some(target);
-            }
-        }
-        self.generate_tab_quads();
-    }
-
-    fn generate_tab_quads(&mut self) {
-        self.tab_text_quads.clear();
-        let (tab_w, _) = self.vertical_tab_size();
-        let active_color = colors::paginator_tab_label_color();
-        let active_srgb = colors::to_srgb(active_color);
-        let active_r = (active_srgb[0] * 255.0) as u8;
-        let active_g = (active_srgb[1] * 255.0) as u8;
-        let active_b = (active_srgb[2] * 255.0) as u8;
-        let inactive_r = (active_r as f32 * 0.78) as u8;
-        let inactive_g = (active_g as f32 * 0.78) as u8;
-        let inactive_b = (active_b as f32 * 0.78) as u8;
-
-        for (i, page_name) in self.pages.iter().enumerate() {
-            let color = if self.selected_page == i {
-                [active_r, active_g, active_b]
-            } else {
-                [inactive_r, inactive_g, inactive_b]
-            };
-            let hex_color = format!("#{:02X}{:02X}{:02X}", color[0], color[1], color[2]);
-
-            let trimmed = page_name.trim();
-            let has_icon = trimmed.find(' ').is_some();
-            let label_text = if let Some(space_idx) = trimmed.find(' ') {
-                trimmed.split_at(space_idx).1.trim()
-            } else {
-                trimmed
-            };
-
-            let w_px = tab_w as u32;
-            let h_px = if has_icon { 80 } else { 120 };
-
-            if w_px == 0 || h_px == 0 {
-                self.tab_text_quads.push(Vec::new());
-                continue;
-            }
-
-            let svg_data = format!(
-                r##"<svg width="{}" height="{}" xmlns="http://www.w3.org/2000/svg">
-  <text x="{}" y="{}" font-family="sans-serif" font-size="12" fill="{}" text-anchor="middle" dominant-baseline="middle" transform="rotate(-90 {} {})">{}</text>
-</svg>"##,
-                w_px, h_px,
-                w_px as f32 / 2.0, h_px as f32 / 2.0,
-                hex_color,
-                w_px as f32 / 2.0, h_px as f32 / 2.0,
-                label_text
-            );
-
-            let opt = resvg::usvg::Options::default();
-            let fontdb = get_font_db();
-            
-            let mut page_quads = Vec::new();
-            if let Ok(tree) = resvg::usvg::Tree::from_data(svg_data.as_bytes(), &opt, fontdb) {
-                if let Some(mut pixmap) = resvg::tiny_skia::Pixmap::new(w_px, h_px) {
-                    resvg::render(&tree, resvg::tiny_skia::Transform::default(), &mut pixmap.as_mut());
-                    let pixels = pixmap.data();
-                    for row in 0..h_px {
-                        for col in 0..w_px {
-                            let idx = ((row * w_px + col) * 4) as usize;
-                            if idx + 3 < pixels.len() {
-                                let a = pixels[idx + 3] as f32 / 255.0;
-                                if a > 0.0 {
-                                    let r = pixels[idx] as f32 / 255.0;
-                                    let g = pixels[idx + 1] as f32 / 255.0;
-                                    let b = pixels[idx + 2] as f32 / 255.0;
-                                    page_quads.push((
-                                        col as f32,
-                                        row as f32,
-                                        1.0,
-                                        1.0,
-                                        [r, g, b, a],
-                                    ));
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-            self.tab_text_quads.push(page_quads);
-        }
-    }
-}
-
-impl Element for Paginator {
-    fn rect(&self) -> (f32, f32, f32, f32) {
-        (self.x, self.y, self.w, self.h)
-    }
-
-    fn set_rect(&mut self, x: f32, y: f32, w: f32, h: f32) {
-        self.x = x;
-        self.y = y;
-        self.w = w;
-        self.h = h;
-        self.update_target_pos();
-
-        let self_ptr = self as *mut Paginator;
-        self.sidebar_menu.set_parent(Some(self_ptr));
-        for plate in &mut self.plates {
-            plate.set_parent(Some(self_ptr));
-        }
-
-        let tabs_at_top = self.tabs_at_top;
-        if tabs_at_top {
-            self.sidebar_menu.set_rect(self.x, self.y, self.w, 40.0);
-            for plate in &mut self.plates {
-                plate.set_rect(self.x, self.y + 40.0, self.w, (self.h - 40.0).max(0.0));
-            }
-        } else {
-            let sidebar_w = self.sidebar_w;
-            self.sidebar_menu.set_rect(self.x, self.y, sidebar_w, self.h);
-            for plate in &mut self.plates {
-                plate.set_rect(self.x + sidebar_w, self.y, (self.w - sidebar_w).max(0.0), self.h);
-            }
-        }
-    }
-
-    fn color(&self) -> [f32; 4] {
-        [0.0, 0.0, 0.0, 0.0]
-    }
-
-    fn set_hovered(&mut self, v: bool) {
-        self.hovered = v;
-    }
-
-    fn hovered(&self) -> bool {
-        self.hovered
-    }
-
-    fn highlight_quad(&self) -> Option<(f32, f32, f32, f32, [f32; 4])> {
-        if let Some(i) = self.hovered_tab {
-            if self.tabs_at_top {
-                let tab_w = if self.pages.is_empty() { 0.0 } else { self.w / self.pages.len() as f32 };
-                let bx = self.x + i as f32 * tab_w + 2.0;
-                let by = self.y + 2.0;
-                let bw = tab_w - 4.0;
-                let bh = 40.0 - 4.0;
-                let scroll_offset = hover_animation::get_scroll_offset();
-                Some((bx, by + scroll_offset, bw, bh, colors::HIGHLIGHT_SECONDARY))
-            } else {
-                let (bx, by, bw, bh) = if self.tabs_rotated {
-                    let (tab_w, tab_h) = self.vertical_tab_size();
-                    let spacing = 10.0;
-                    (
-                        self.x + (self.sidebar_w - tab_w) / 2.0,
-                        self.y + self.tab_y_offset + i as f32 * (tab_h + spacing) - self.sidebar_scroll_y,
-                        tab_w,
-                        tab_h,
-                    )
-                } else {
-                    let bw = self.sidebar_w - 10.0;
-                    (
-                        self.x + 5.0,
-                        self.y + self.tab_y_offset + i as f32 * 50.0 - self.sidebar_scroll_y,
-                        bw,
-                        40.0,
-                    )
-                };
-                let scroll_offset = hover_animation::get_scroll_offset();
-                let qy = by + scroll_offset;
-                let qh = bh;
-
-                let min_y = self.y;
-                let max_y = self.y + self.h;
-                let ry1 = qy.max(min_y);
-                let ry2 = (qy + qh).min(max_y);
-                let rh = ry2 - ry1;
-                if rh > 0.0 {
-                    Some((bx, ry1, bw, rh, colors::HIGHLIGHT_SECONDARY))
-                } else {
-                    None
-                }
-            }
-        } else {
-            None
-        }
-    }
-
-    fn extra_quads(&self) -> Vec<(f32, f32, f32, f32, [f32; 4])> {
-        let mut quads = Vec::new();
-        if self.tabs_rotated {
-            quads.push((self.x, self.y, self.sidebar_w, self.h, colors::sidebar_bg_color()));
-            
-            let (tab_w, tab_h) = self.vertical_tab_size();
-            let spacing = 10.0;
-            let min_y = self.y;
-            let max_y = self.y + self.h;
-
-            // Draw primary highlight for the selected active tab
-            if let Some(cy) = self.current_y {
-                let bx = self.x + (self.sidebar_w - tab_w) / 2.0;
-                let by = self.y + cy - self.sidebar_scroll_y;
-                let ry1 = by.max(min_y);
-                let ry2 = (by + tab_h).min(max_y);
-                let rh = ry2 - ry1;
-                if rh > 0.0 {
-                    quads.push((bx, ry1, tab_w, rh, colors::highlight_primary_color()));
-                }
-            }
-
-            for (i, page_name) in self.pages.iter().enumerate() {
-                let bx = self.x + (self.sidebar_w - tab_w) / 2.0;
-                let by = self.y + self.tab_y_offset + i as f32 * (tab_h + spacing) - self.sidebar_scroll_y;
-
-                let trimmed = page_name.trim();
-                let has_icon = trimmed.find(' ').is_some();
-                let y_offset = if has_icon { 40.0 } else { 0.0 };
-
-                if i < self.tab_text_quads.len() {
-                    for &(qx, qy, qw, qh, qc) in &self.tab_text_quads[i] {
-                        let absolute_x = bx + qx;
-                        let absolute_y = by + y_offset + qy;
-                        
-                        let ry1 = absolute_y.max(min_y);
-                        let ry2 = (absolute_y + qh).min(max_y);
-                        let rh = ry2 - ry1;
-                        if rh > 0.0 {
-                            quads.push((absolute_x, ry1, qw, rh, qc));
-                        }
-                    }
-                }
-            }
-        } else {
-            quads.extend(self.sidebar_menu.extra_quads());
-        }
-
-        if self.selected_page < self.plates.len() {
-            quads.extend(self.plates[self.selected_page].extra_quads());
-        }
-        quads
-    }
-
-    fn text_labels(&self) -> Vec<TextLabel> {
-        let mut labels = Vec::new();
-        if self.tabs_rotated {
-            let (tab_w, tab_h) = self.vertical_tab_size();
-            let spacing = 10.0;
-            let active_color = colors::paginator_tab_label_color();
-            let active_srgb = colors::to_srgb(active_color);
-            let active_r = (active_srgb[0] * 255.0) as u8;
-            let active_g = (active_srgb[1] * 255.0) as u8;
-            let active_b = (active_srgb[2] * 255.0) as u8;
-            let inactive_r = (active_r as f32 * 0.78) as u8;
-            let inactive_g = (active_g as f32 * 0.78) as u8;
-            let inactive_b = (active_b as f32 * 0.78) as u8;
-
-            for (i, page_name) in self.pages.iter().enumerate() {
-                let color = if self.selected_page == i {
-                    [active_r, active_g, active_b]
-                } else {
-                    [inactive_r, inactive_g, inactive_b]
-                };
-                let bx = self.x + (self.sidebar_w - tab_w) / 2.0;
-                let by = self.y + self.tab_y_offset + i as f32 * (tab_h + spacing) - self.sidebar_scroll_y;
-                let bw = tab_w;
-
-                let trimmed = page_name.trim();
-                let has_icon = trimmed.find(' ').is_some();
-                if has_icon {
-                    if let Some(space_idx) = trimmed.find(' ') {
-                        let (icon, _) = trimmed.split_at(space_idx);
-                        let icon = icon.trim();
-                        if !icon.is_empty() {
-                            let icon_font_size = 14.0;
-                            let est_icon_w = TextLabel::estimate_width(icon, icon_font_size);
-                            let icon_y = by + 12.0;
-                            if icon_y >= self.y && icon_y + icon_font_size <= self.y + self.h {
-                                labels.push(TextLabel {
-                                    text: icon.to_string(),
-                                    x: bx + (bw - est_icon_w) / 2.0,
-                                    y: icon_y,
-                                    font_size: icon_font_size,
-                                    color,
-                                });
-                            }
-                        }
-                    }
-                }
-            }
-        } else {
-            labels.extend(self.sidebar_menu.text_labels());
-        }
-
-        if self.selected_page < self.plates.len() {
-            labels.extend(self.plates[self.selected_page].text_labels());
-        }
-        labels
-    }
-
-    fn text_labels_with_bounds(&self) -> Vec<(TextLabel, Option<[f32; 4]>)> {
-        let mut labels = Vec::new();
-        for l in self.text_labels() {
-            labels.push((l, None));
-        }
-        labels
-    }
-
-    fn on_cursor_moved(&mut self, px: f32, py: f32) -> bool {
-        let mut changed = false;
-        let was_hovered_tab = self.hovered_tab;
-        self.hovered_tab = None;
-        for i in 0..self.pages.len() {
-            let (bx, by, bw, bh) = if self.tabs_at_top {
-                let tab_w = if self.pages.is_empty() { 0.0 } else { self.w / self.pages.len() as f32 };
-                (self.x + i as f32 * tab_w, self.y, tab_w, 40.0)
-            } else if self.tabs_rotated {
-                let (tab_w, tab_h) = self.vertical_tab_size();
-                let spacing = 10.0;
-                (
-                    self.x + (self.sidebar_w - tab_w) / 2.0,
-                    self.y + self.tab_y_offset + i as f32 * (tab_h + spacing) - self.sidebar_scroll_y,
-                    tab_w,
-                    tab_h,
-                )
-            } else {
-                let bw = self.sidebar_w - 10.0;
-                (self.x + 5.0, self.y + self.tab_y_offset + i as f32 * 50.0 - self.sidebar_scroll_y, bw, 40.0)
-            };
-            if px >= bx && px <= bx + bw && py >= by && py <= by + bh && py >= self.y && py <= self.y + self.h {
-                self.hovered_tab = Some(i);
-                break;
-            }
-        }
-        if was_hovered_tab != self.hovered_tab {
-            changed = true;
-        }
-
-        if self.sidebar_menu.cursor_moved(px, py) {
-            changed = true;
-        }
-        if self.selected_page < self.plates.len() {
-            if self.plates[self.selected_page].cursor_moved(px, py) {
-                changed = true;
-            }
-        }
-
-        changed
-    }
-
-    fn mouse_input(&mut self, button: MouseButton, state: ElementState, px: f32, py: f32) -> bool {
-        if self.selected_page < self.plates.len() {
-            if self.plates[self.selected_page].popover_rect().is_some() {
-                if self.plates[self.selected_page].mouse_input(button, state, px, py) {
-                    return true;
-                }
-            }
-        }
-
-        let mut clicked_tab = false;
-        if button == MouseButton::Left {
-            match state {
-                ElementState::Pressed => {
-                    self.pressed_tab = None;
-                    for i in 0..self.pages.len() {
-                        let (bx, by, bw, bh) = if self.tabs_at_top {
-                            let tab_w = if self.pages.is_empty() { 0.0 } else { self.w / self.pages.len() as f32 };
-                            (self.x + i as f32 * tab_w, self.y, tab_w, 40.0)
-                        } else if self.tabs_rotated {
-                            let (tab_w, tab_h) = self.vertical_tab_size();
-                            let spacing = 10.0;
-                            (
-                                self.x + (self.sidebar_w - tab_w) / 2.0,
-                                self.y + self.tab_y_offset + i as f32 * (tab_h + spacing) - self.sidebar_scroll_y,
-                                tab_w,
-                                tab_h,
-                            )
-                        } else {
-                            let bw = self.sidebar_w - 10.0;
-                            (self.x + 5.0, self.y + self.tab_y_offset + i as f32 * 50.0 - self.sidebar_scroll_y, bw, 40.0)
-                        };
-                        if px >= bx && px <= bx + bw && py >= by && py <= by + bh && py >= self.y && py <= self.y + self.h {
-                            self.pressed_tab = Some(i);
-                            clicked_tab = true;
-                            break;
-                        }
-                    }
-                }
-                ElementState::Released => {
-                    if let Some(i) = self.pressed_tab.take() {
-                        let (bx, by, bw, bh) = if self.tabs_at_top {
-                            let tab_w = if self.pages.is_empty() { 0.0 } else { self.w / self.pages.len() as f32 };
-                            (self.x + i as f32 * tab_w, self.y, tab_w, 40.0)
-                        } else if self.tabs_rotated {
-                            let (tab_w, tab_h) = self.vertical_tab_size();
-                            let spacing = 10.0;
-                            (
-                                self.x + (self.sidebar_w - tab_w) / 2.0,
-                                self.y + self.tab_y_offset + i as f32 * (tab_h + spacing) - self.sidebar_scroll_y,
-                                tab_w,
-                                tab_h,
-                            )
-                        } else {
-                            let bw = self.sidebar_w - 10.0;
-                            (self.x + 5.0, self.y + self.tab_y_offset + i as f32 * 50.0 - self.sidebar_scroll_y, bw, 40.0)
-                        };
-                        if px >= bx && px <= bx + bw && py >= by && py <= by + bh && py >= self.y && py <= self.y + self.h {
-                            if self.selected_page != i {
-                                self.set_selected_page(i);
-                                self.page_changed = true;
-                            }
-                            clicked_tab = true;
-                        }
-                    }
-                }
-            }
-        }
-
-        if clicked_tab {
-            return true;
-        }
-
-        if self.sidebar_menu.mouse_input(button, state, px, py) {
-            return true;
-        }
-
-        if self.selected_page < self.plates.len() {
-            if self.plates[self.selected_page].mouse_input(button, state, px, py) {
-                return true;
-            }
-        }
-
-        false
-    }
-
-    fn keyboard_input(&mut self, event: &KeyEvent) -> bool {
-        if self.sidebar_menu.keyboard_input(event) {
-            return true;
-        }
-        if self.selected_page < self.plates.len() {
-            if self.plates[self.selected_page].keyboard_input(event) {
-                return true;
-            }
-        }
-        false
-    }
-
-    fn mouse_wheel(&mut self, delta: &MouseScrollDelta, px: f32, py: f32) -> bool {
-        if !self.tabs_at_top {
-            let bx = self.x;
-            let by = self.y;
-            let bw = self.sidebar_w;
-            let bh = self.h;
-            if px >= bx && px <= bx + bw && py >= by && py <= by + bh {
-                let scroll_speed = 24.0;
-                let dy = match delta {
-                    MouseScrollDelta::LineDelta(_, y) => -y * scroll_speed,
-                    MouseScrollDelta::PixelDelta(pos) => -pos.y as f32,
-                };
-                let old_scroll = self.sidebar_scroll_y;
-                let max_scroll = (self.total_sidebar_height() - self.h).max(0.0);
-                self.sidebar_scroll_y = (self.sidebar_scroll_y + dy).clamp(0.0, max_scroll);
-                if (self.sidebar_scroll_y - old_scroll).abs() > 0.01 {
-                    self.update_target_pos();
-                    return true;
-                }
-            }
-        }
-
-        if self.sidebar_menu.mouse_wheel(delta, px, py) {
-            return true;
-        }
-
-        if self.selected_page < self.plates.len() {
-            if self.plates[self.selected_page].mouse_wheel(delta, px, py) {
-                return true;
-            }
-        }
-        false
-    }
-
-    fn popover_rect(&self) -> Option<(f32, f32, f32, f32)> {
-        if let Some(r) = self.sidebar_menu.popover_rect() {
-            return Some(r);
-        }
-        if self.selected_page < self.plates.len() {
-            if let Some(r) = self.plates[self.selected_page].popover_rect() {
-                return Some(r);
-            }
-        }
-        None
-    }
-
-    fn render_popover(&self, pc: &mut dyn crate::layout::RenderTarget) {
-        self.sidebar_menu.render_popover(pc);
-        if self.selected_page < self.plates.len() {
-            self.plates[self.selected_page].render_popover(pc);
-        }
-    }
-
-    fn take_click(&mut self) -> bool {
-        if self.page_changed {
-            self.page_changed = false;
-            true
-        } else {
-            false
-        }
-    }
-
-    fn value(&self) -> i32 {
-        self.selected_page as i32
-    }
-
-    fn tick(&mut self, dt: f32) -> bool {
-        let mut changed = false;
-        if let Some(current) = self.current_y {
-            let diff = self.target_y - current;
-            if diff.abs() > 0.1 {
-                let decay = 15.0;
-                let next = current + diff * (1.0 - (-decay * dt).exp());
-                self.current_y = Some(next);
-                changed = true;
-            } else {
-                self.current_y = Some(self.target_y);
-            }
-        }
-        if let Some(current) = self.current_x {
-            let diff = self.target_x - current;
-            if diff.abs() > 0.1 {
-                let decay = 15.0;
-                let next = current + diff * (1.0 - (-decay * dt).exp());
-                self.current_x = Some(next);
-                changed = true;
-            } else {
-                self.current_x = Some(self.target_x);
-            }
-        }
-
-        let self_ptr = self as *mut Paginator;
-        self.sidebar_menu.set_parent(Some(self_ptr));
-        for plate in &mut self.plates {
-            plate.set_parent(Some(self_ptr));
-        }
-
-        if self.sidebar_menu.tick(dt) {
-            changed = true;
-        }
-
-        for plate in &mut self.plates {
-            if plate.tick(dt) {
-                changed = true;
-            }
-        }
-
-        changed
-    }
-
-    fn parent(&self) -> Option<*mut (dyn Element + 'static)> {
-        self.parent
-    }
-
-    fn set_parent(&mut self, parent: Option<*mut (dyn Element + 'static)>) {
-        self.parent = parent;
-    }
-
-    fn children(&self) -> Vec<*mut (dyn Element + 'static)> {
-        let mut list = Vec::new();
-        list.push(&self.sidebar_menu as *const dyn Element as *mut dyn Element);
-        for plate in &self.plates {
-            list.push(plate as *const dyn Element as *mut dyn Element);
-        }
-        list
-    }
-
-    fn add_child(&mut self, _child: *mut (dyn Element + 'static)) {}
-    fn clear_children(&mut self) {}
-
-    fn set_modifiers(&mut self, ctrl: bool, shift: bool, alt: bool) {
-        self.sidebar_menu.set_modifiers(ctrl, shift, alt);
-        if self.selected_page < self.plates.len() {
-            self.plates[self.selected_page].set_modifiers(ctrl, shift, alt);
-        }
-    }
-    fn menu_names(&self) -> Vec<String> {
-        self.pages.clone()
-    }
-    fn selected_page(&self) -> usize {
-        self.selected_page()
-    }
-    fn set_selected_page(&mut self, page: usize) {
-        self.set_selected_page(page);
-    }
-    fn is_page_hidden(&self) -> bool {
-        self.is_page_hidden()
-    }
-    fn set_page_hidden(&mut self, hidden: bool) {
-        self.set_page_hidden(hidden);
-    }
-    fn set_pages(&mut self, pages: Vec<String>) {
-        self.set_pages(pages);
-    }
-    fn sidebar_w(&self) -> f32 {
-        self.sidebar_w()
-    }
-    fn set_sidebar_mode(&mut self, enabled: bool) {
-        self.set_sidebar_mode(enabled);
-    }
-    fn set_sidebar_label(&mut self, label: Option<String>) {
-        self.sidebar_label = label;
-        self.update_target_pos();
-    }
-    fn add_widget_to_page(&mut self, page_idx: usize, widget: *mut (dyn Element + 'static)) {
-        self.add_widget_to_page(page_idx, widget);
-    }
-    fn clear_page_widgets(&mut self, page_idx: usize) {
-        self.clear_page_widgets(page_idx);
-    }
-    fn menu_items_list(&self) -> Vec<Vec<String>> {
-        self.sidebar_menu.menu_items_list()
-    }
-    fn menu_checked_list(&self) -> Vec<Vec<Option<bool>>> {
-        self.sidebar_menu.menu_checked_list()
-    }
-}
-
-unsafe impl Send for Paginator {}
-unsafe impl Sync for Paginator {}
-
diff --git a/src/widget/container/breadcrumb.rs b/src/widget/container/breadcrumb.rs
new file mode 100644
index 0000000..a2f381d
--- /dev/null
+++ b/src/widget/container/breadcrumb.rs
@@ -0,0 +1,115 @@
+use crate::widget::*;
+use crate::widget::input::{BREADCRUMB_PADDING, SEGMENT_GAP};
+use crate::widget::display::TextLabel;
+
+#[derive(Debug, Clone)]
+pub struct Breadcrumb {
+    x: f32, y: f32, w: f32, h: f32,
+    hovered: bool,
+    path: Vec<String>,
+    hovered_seg: Option<usize>,
+    clicked_seg: Option<usize>,
+    pub network_opacity: f32,
+}
+
+impl Breadcrumb {
+    pub fn set_network_opacity(&mut self, opacity: f32) {
+        self.network_opacity = opacity;
+    }
+
+    pub fn new() -> Self {
+        Self { x: 0.0, y: 0.0, w: 0.0, h: 0.0, hovered: false,
+               path: Vec::new(), hovered_seg: None, clicked_seg: None, network_opacity: 1.0 }
+    }
+
+    fn seg_at(&self, px: f32) -> Option<usize> {
+        let mut cx = self.x + BREADCRUMB_PADDING;
+        for (i, seg) in self.path.iter().enumerate() {
+            let w = seg.len() as f32 * 7.5;
+            if px >= cx && px < cx + w {
+                return Some(i);
+            }
+            cx += w + SEGMENT_GAP;
+        }
+        None
+    }
+}
+
+impl Element for Breadcrumb {
+    fn as_any(&self) -> &dyn std::any::Any { self }
+    fn as_any_mut(&mut self) -> &mut dyn std::any::Any { self }
+    fn as_ptr(&self) -> *mut (dyn Element + 'static) {
+        self as *const Self as *mut Self as *mut (dyn Element + 'static)
+    }
+
+    fn rect(&self) -> (f32, f32, f32, f32) { (self.x, self.y, self.w, self.h) }
+    fn set_rect(&mut self, x: f32, y: f32, w: f32, h: f32) { self.x = x; self.y = y; self.w = w; self.h = h; }
+
+    fn color(&self) -> [f32; 4] { [0.10, 0.10, 0.14, self.network_opacity] }
+    fn set_hovered(&mut self, v: bool) { self.hovered = v; }
+    fn hovered(&self) -> bool { self.hovered }
+
+    fn on_cursor_moved(&mut self, px: f32, py: f32, ctx: &mut UiContext) -> bool {
+        let was = self.hovered;
+        self.hovered = self.hit_test(px, py, ctx);
+        let old = self.hovered_seg;
+        self.hovered_seg = if self.hovered { self.seg_at(px) } else { None };
+        was != self.hovered || old != self.hovered_seg
+    }
+
+    fn mouse_input(&mut self, button: MouseButton, state: ElementState, px: f32, _py: f32, ctx: &mut UiContext) -> bool {
+        if button != MouseButton::Left || state != ElementState::Pressed { return false; }
+        if let Some(i) = self.seg_at(px) {
+            if i < self.path.len() - 1 {
+                self.clicked_seg = Some(i);
+                return true;
+            }
+        }
+        false
+    }
+
+    fn set_path(&mut self, segments: &[String]) {
+        let mut s = Vec::with_capacity(segments.len().max(1));
+        if segments.is_empty() || (segments.len() == 1 && segments[0].is_empty()) {
+            s.push("/".to_string());
+        } else {
+            s.push("/".to_string());
+            for name in segments {
+                s.push(format!(" \u{203A} {}", name));
+            }
+        }
+        self.path = s;
+    }
+
+    fn path_click(&mut self) -> Option<usize> { self.clicked_seg.take() }
+
+    fn extra_quads(&self) -> Vec<(f32, f32, f32, f32, [f32; 4])> {
+        let mut quads = Vec::new();
+        if let Some(i) = self.hovered_seg {
+            let mut cx = self.x + BREADCRUMB_PADDING;
+            for j in 0..i {
+                let w = self.path[j].len() as f32 * 7.5;
+                cx += w + SEGMENT_GAP;
+            }
+            let w = self.path[i].len() as f32 * 7.5;
+            quads.push((cx, self.y, w, self.h, [1.0, 1.0, 1.0, 0.06]));
+        }
+        quads
+    }
+
+    fn text_labels(&self) -> Vec<TextLabel> {
+        let mut labels = Vec::new();
+        let mut cx = self.x + BREADCRUMB_PADDING;
+        for (i, seg) in self.path.iter().enumerate() {
+            labels.push(TextLabel {
+                text: seg.clone(),
+                x: cx,
+                y: self.y + 6.0,
+                font_size: 12.0,
+                color: if i == self.path.len() - 1 { [0xcc, 0xcc, 0xd4] } else { [0x88, 0x88, 0x99] },
+            });
+            cx += seg.len() as f32 * 7.5 + SEGMENT_GAP;
+        }
+        labels
+    }
+}
diff --git a/src/widget/container/container.rs b/src/widget/container/container.rs
new file mode 100644
index 0000000..0e96a0d
--- /dev/null
+++ b/src/widget/container/container.rs
@@ -0,0 +1,36 @@
+use crate::widget::*;
+
+#[derive(Clone)]
+pub struct Container {
+    pub parent: Option<*mut (dyn Element + 'static)>,
+    pub children: Vec<*mut (dyn Element + 'static)>,
+}
+
+impl Container {
+    pub fn new() -> Self {
+        Self { parent: None, children: Vec::new() }
+    }
+}
+
+impl Element for Container {
+    fn rect(&self) -> (f32, f32, f32, f32) { (0.0, 0.0, 0.0, 0.0) }
+    fn set_rect(&mut self, _x: f32, _y: f32, _w: f32, _h: f32) {}
+    fn color(&self) -> [f32; 4] { [0.0, 0.0, 0.0, 0.0] }
+
+    fn focus(&mut self) {
+        focus::set_focused(self);
+    }
+    fn unfocus(&mut self) {}
+
+    fn parent(&self, ctx: &UiContext) -> Option<*mut (dyn Element + 'static)> { self.parent }
+    fn set_parent(&mut self, parent: Option<*mut (dyn Element + 'static)>, ctx: &mut UiContext) { self.parent = parent; }
+    fn children(&self, ctx: &UiContext) -> Vec<*mut (dyn Element + 'static)> { self.children.clone() }
+    fn add_child(&mut self, child: *mut (dyn Element + 'static), ctx: &mut UiContext) { self.children.push(child); }
+    fn clear_children(&mut self, ctx: &mut UiContext) { self.children.clear(); }
+}
+
+impl Drop for Container {
+    fn drop(&mut self) {
+        focus::clear_if_matches(self);
+    }
+}
diff --git a/src/widget/container/content_bg.rs b/src/widget/container/content_bg.rs
new file mode 100644
index 0000000..5e53ad8
--- /dev/null
+++ b/src/widget/container/content_bg.rs
@@ -0,0 +1,236 @@
+use crate::colors;
+use crate::widget::*;
+
+pub struct ContentBg {
+    x: f32, y: f32, w: f32, h: f32,
+    hovered: bool,
+    show_network_grid: bool,
+    grid_size_x: f32,
+    grid_size_y: f32,
+    grid_origin_x: f32,
+    grid_origin_y: f32,
+    skipped_row_h: f32,
+    skipped_col_w: f32,
+}
+
+impl ContentBg {
+    pub fn new() -> Self {
+        Self { x: 0.0, y: 0.0, w: 0.0, h: 0.0, hovered: false, show_network_grid: false, grid_size_x: 150.0, grid_size_y: 75.0, grid_origin_x: 0.0, grid_origin_y: 0.0, skipped_row_h: 37.5, skipped_col_w: 37.5 }
+    }
+}
+
+impl Element for ContentBg {
+    fn rect(&self) -> (f32, f32, f32, f32) { (self.x, self.y, self.w, self.h) }
+    fn set_rect(&mut self, x: f32, y: f32, w: f32, h: f32) { self.x = x; self.y = y; self.w = w; self.h = h; }
+    fn color(&self) -> [f32; 4] {
+        if self.show_network_grid {
+            [0.0, 0.0, 0.0, 0.0]
+        } else {
+            colors::CONTENT_BG
+        }
+    }
+    fn set_hovered(&mut self, v: bool) { self.hovered = v; }
+    fn hovered(&self) -> bool { self.hovered }
+    fn hit_test(&self, _px: f32, _py: f32, _ctx: &UiContext) -> bool { false }
+
+    fn set_show_network_grid(&mut self, show: bool) { self.show_network_grid = show; }
+    fn set_grid_sizes(&mut self, gx: f32, gy: f32) { self.grid_size_x = gx; self.grid_size_y = gy; }
+    fn set_grid_origin(&mut self, ox: f32, oy: f32) { self.grid_origin_x = ox; self.grid_origin_y = oy; }
+    fn set_skipped_sizes(&mut self, row_h: f32, col_w: f32) { self.skipped_row_h = row_h; self.skipped_col_w = col_w; }
+
+    fn extra_quads(&self) -> Vec<(f32, f32, f32, f32, [f32; 4])> {
+        if !self.show_network_grid || self.grid_size_x <= 0.0 || self.grid_size_y <= 0.0 {
+            return vec![];
+        }
+        let mut quads = Vec::new();
+        let grid_color = [0.0, 0.0, 0.0, 0.0];
+        let max_alpha = colors::CONTENT_BG[3]; // Peak opacity in the middle of gradient cells matches non-gradient cells
+        let steps = 20; // Silky-smooth gradient transition
+
+        let step_y = self.grid_size_y + self.skipped_row_h;
+        let step_x = self.grid_size_x + self.skipped_col_w;
+
+        if step_y >= 4.0 && step_x >= 4.0 {
+            let ry_start = ((self.y - self.grid_origin_y) / step_y).floor() as i32 - 1;
+            let ry_end = ((self.y + self.h - self.grid_origin_y) / step_y).ceil() as i32 + 1;
+            let ry_start = ry_start.max(-100_000);
+            let ry_end = ry_end.min(100_000);
+
+            let cx_start = ((self.x - self.grid_origin_x) / step_x).floor() as i32 - 1;
+            let cx_end = ((self.x + self.w - self.grid_origin_x) / step_x).ceil() as i32 + 1;
+            let cx_start = cx_start.max(-100_000);
+            let cx_end = cx_end.min(100_000);
+
+            // Draw individual cell backgrounds to avoid stacking with gradients
+            for ry in ry_start..=ry_end {
+                let y1 = self.grid_origin_y + (ry as f32) * step_y;
+                let draw_start_y = y1.max(self.y);
+                let draw_end_y = (y1 + self.grid_size_y).min(self.y + self.h);
+                if draw_start_y < draw_end_y {
+                    for cx in cx_start..=cx_end {
+                        let x1 = self.grid_origin_x + (cx as f32) * step_x;
+                        let draw_start_x = x1.max(self.x);
+                        let draw_end_x = (x1 + self.grid_size_x).min(self.x + self.w);
+                        if draw_start_x < draw_end_x {
+                            quads.push((draw_start_x, draw_start_y, draw_end_x - draw_start_x, draw_end_y - draw_start_y, colors::CONTENT_BG));
+                        }
+                    }
+                }
+            }
+        }
+
+        // Draw interstitial row gradients (horizontal bands fading to 0 alpha at left and right sides)
+        if self.skipped_row_h > 0.0 {
+            let step_y = self.grid_size_y + self.skipped_row_h;
+            let step_x = self.grid_size_x + self.skipped_col_w;
+            if step_y >= 4.0 && step_x >= 4.0 {
+                let k_start = ((self.y - self.grid_origin_y) / step_y).floor() as i32 - 1;
+                let k_end = ((self.y + self.h - self.grid_origin_y) / step_y).ceil() as i32 + 1;
+                let k_start = k_start.max(-100_000);
+                let k_end = k_end.min(100_000);
+
+                let cx_start = ((self.x - self.grid_origin_x) / step_x).floor() as i32 - 1;
+                let cx_end = ((self.x + self.w - self.grid_origin_x) / step_x).ceil() as i32 + 1;
+                let cx_start = cx_start.max(-100_000);
+                let cx_end = cx_end.min(100_000);
+
+                for k in k_start..=k_end {
+                    let y1 = self.grid_origin_y + (k as f32) * step_y;
+                    let y2 = y1 + self.grid_size_y;
+                    if y1 >= self.y + self.h {
+                        continue;
+                    }
+                    let draw_start_y = y2.max(self.y);
+                    let draw_end_y = (y2 + self.skipped_row_h).min(self.y + self.h);
+                    if draw_start_y >= draw_end_y {
+                        continue;
+                    }
+
+                    for cx in cx_start..=cx_end {
+                        let x1 = self.grid_origin_x + (cx as f32) * step_x;
+                        let x_mid = x1 + self.grid_size_x / 2.0;
+                        let w_total = self.grid_size_x;
+                        let sub_w = w_total / steps as f32;
+
+                        for i in 0..steps {
+                            let sx_start = x1 + i as f32 * sub_w;
+                            let sx_end = sx_start + sub_w;
+                            let draw_start_x = sx_start.max(self.x);
+                            let draw_end_x = sx_end.min(self.x + self.w);
+                            if draw_start_x < draw_end_x {
+                                let sx_mid = (sx_start + sx_end) / 2.0;
+                                let dist = (sx_mid - x_mid).abs();
+                                let d = (dist / (w_total / 2.0)).min(1.0);
+                                
+                                // Fade the cell background color from max_alpha in the middle to transparent at the edges
+                                let alpha = max_alpha * (1.0 - d);
+                                if alpha > 0.001 {
+                                    quads.push((draw_start_x, draw_start_y, draw_end_x - draw_start_x, draw_end_y - draw_start_y, [colors::CONTENT_BG[0], colors::CONTENT_BG[1], colors::CONTENT_BG[2], alpha]));
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        // Draw interstitial column gradients (vertical bands fading to 0 alpha at top and bottom)
+        if self.skipped_col_w > 0.0 {
+            let step_y = self.grid_size_y + self.skipped_row_h;
+            let step_x = self.grid_size_x + self.skipped_col_w;
+            if step_y >= 4.0 && step_x >= 4.0 {
+                let k_start = ((self.x - self.grid_origin_x) / step_x).floor() as i32 - 1;
+                let k_end = ((self.x + self.w - self.grid_origin_x) / step_x).ceil() as i32 + 1;
+                let k_start = k_start.max(-100_000);
+                let k_end = k_end.min(100_000);
+
+                let ry_start = ((self.y - self.grid_origin_y) / step_y).floor() as i32 - 1;
+                let ry_end = ((self.y + self.h - self.grid_origin_y) / step_y).ceil() as i32 + 1;
+                let ry_start = ry_start.max(-100_000);
+                let ry_end = ry_end.min(100_000);
+
+                for k in k_start..=k_end {
+                    let x1 = self.grid_origin_x + (k as f32) * step_x;
+                    let x2 = x1 + self.grid_size_x;
+                    if x1 >= self.x + self.w {
+                        continue;
+                    }
+                    let draw_start_x = x2.max(self.x);
+                    let draw_end_x = (x2 + self.skipped_col_w).min(self.x + self.w);
+                    if draw_start_x >= draw_end_x {
+                        continue;
+                    }
+
+                    for ry in ry_start..=ry_end {
+                        let y1 = self.grid_origin_y + (ry as f32) * step_y;
+                        let y_mid = y1 + self.grid_size_y / 2.0;
+                        let h_total = self.grid_size_y;
+                        let sub_h = h_total / steps as f32;
+
+                        for i in 0..steps {
+                            let sy_start = y1 + i as f32 * sub_h;
+                            let sy_end = sy_start + sub_h;
+                            let draw_start_y = sy_start.max(self.y);
+                            let draw_end_y = sy_end.min(self.y + self.h);
+                            if draw_start_y < draw_end_y {
+                                let sy_mid = (sy_start + sy_end) / 2.0;
+                                let dist = (sy_mid - y_mid).abs();
+                                let d = (dist / (h_total / 2.0)).min(1.0);
+                                
+                                // Fade the cell background color from max_alpha in the middle to transparent at the edges
+                                let alpha = max_alpha * (1.0 - d);
+                                if alpha > 0.001 {
+                                    quads.push((draw_start_x, draw_start_y, draw_end_x - draw_start_x, draw_end_y - draw_start_y, [colors::CONTENT_BG[0], colors::CONTENT_BG[1], colors::CONTENT_BG[2], alpha]));
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        // Draw the grid borders
+        let step_y = self.grid_size_y + self.skipped_row_h;
+        if step_y >= 4.0 {
+            let k_start = ((self.y - self.grid_origin_y) / step_y).floor() as i32 - 1;
+            let k_end = ((self.y + self.h - self.grid_origin_y) / step_y).ceil() as i32 + 1;
+            let k_start = k_start.max(-100_000);
+            let k_end = k_end.min(100_000);
+            for k in k_start..=k_end {
+                let y1 = self.grid_origin_y + (k as f32) * step_y;
+                let y2 = y1 + self.grid_size_y;
+                if y1 >= self.y + self.h {
+                    continue;
+                }
+                if y1 >= self.y {
+                    quads.push((self.x, y1, self.w, 1.0, grid_color));
+                }
+                if y2 >= self.y && y2 < self.y + self.h {
+                    quads.push((self.x, y2, self.w, 1.0, grid_color));
+                }
+            }
+        }
+
+        let step_x = self.grid_size_x + self.skipped_col_w;
+        if step_x >= 4.0 {
+            let k_start = ((self.x - self.grid_origin_x) / step_x).floor() as i32 - 1;
+            let k_end = ((self.x + self.w - self.grid_origin_x) / step_x).ceil() as i32 + 1;
+            let k_start = k_start.max(-100_000);
+            let k_end = k_end.min(100_000);
+            for k in k_start..=k_end {
+                let x1 = self.grid_origin_x + (k as f32) * step_x;
+                let x2 = x1 + self.grid_size_x;
+                if x1 >= self.x + self.w {
+                    continue;
+                }
+                if x1 >= self.x {
+                    quads.push((x1, self.y, 1.0, self.h, grid_color));
+                }
+                if x2 >= self.x && x2 < self.x + self.w {
+                    quads.push((x2, self.y, 1.0, self.h, grid_color));
+                }
+            }
+        }
+        quads
+    }
+}
diff --git a/src/widget/container/header.rs b/src/widget/container/header.rs
new file mode 100644
index 0000000..44d6754
--- /dev/null
+++ b/src/widget/container/header.rs
@@ -0,0 +1,19 @@
+use crate::colors;
+use crate::widget::*;
+
+pub struct Header {
+    x: f32, y: f32, w: f32, h: f32,
+    hovered: bool,
+}
+
+impl Header {
+    pub fn new() -> Self { Self { x: 0.0, y: 0.0, w: 0.0, h: 0.0, hovered: false } }
+}
+
+impl Element for Header {
+    fn rect(&self) -> (f32, f32, f32, f32) { (self.x, self.y, self.w, self.h) }
+    fn set_rect(&mut self, x: f32, y: f32, w: f32, h: f32) { self.x = x; self.y = y; self.w = w; self.h = h; }
+    fn color(&self) -> [f32; 4] { colors::HEADER_BG }
+    fn set_hovered(&mut self, v: bool) { self.hovered = v; }
+    fn hovered(&self) -> bool { self.hovered }
+}
diff --git a/src/widget/container/menu.rs b/src/widget/container/menu.rs
new file mode 100644
index 0000000..fddd71d
--- /dev/null
+++ b/src/widget/container/menu.rs
@@ -0,0 +1,1297 @@
+use crate::colors;
+use crate::widget::*;
+use crate::widget::display::{make_widget_text_buffer, TextLabel};
+
+pub struct MenuBar {
+    x: f32, y: f32, w: f32, h: f32,
+    hovering: bool,
+    pub title: String,
+    pub menus: Vec<Box<Menu>>,
+    pub menu_items: Vec<String>,
+    pub vertical_items: Vec<String>,
+    pub menu_dropdowns: Vec<Vec<String>>,
+    pub menu_dropdown_checked: Vec<Vec<Option<bool>>>,
+    pub hovered_menu: Option<usize>,
+    pub open_menu: Option<usize>,
+    pub hovered_dropdown: Option<usize>,
+    pub clicked_dropdown: Option<(usize, usize)>,
+    pub was_open: Option<usize>,
+    pub vertical: bool,
+    pub visible: bool,
+    pub focused: bool,
+    pub z_level: i32,
+    pub center_items: bool,
+    pub curved_circle: Option<(f32, f32, f32)>,
+    pub title_pos: Option<(f32, f32)>,
+    pub title_buf: Option<glyphon::Buffer>,
+    pub curved_title_char_bufs: Vec<glyphon::Buffer>,
+    pub network_opacity: f32,
+    pub font_family: String,
+    pub label: Option<String>,
+}
+
+impl MenuBar {
+    pub fn set_curved_circle(&mut self, circle: Option<(f32, f32, f32)>) {
+        self.curved_circle = circle;
+        if circle.is_none() {
+            for menu in &mut self.menus {
+                menu.curved_arc = None;
+            }
+        }
+    }
+
+    pub fn set_network_opacity(&mut self, opacity: f32) {
+        self.network_opacity = opacity;
+    }
+
+    pub fn new(x: f32, y: f32, w: f32, h: f32) -> Self {
+        Self {
+            x, y, w, h, hovering: false,
+            title: String::new(),
+            menus: Vec::new(),
+            menu_items: Vec::new(),
+            vertical_items: Vec::new(),
+            menu_dropdowns: Vec::new(),
+            menu_dropdown_checked: Vec::new(),
+            hovered_menu: None,
+            open_menu: None,
+            hovered_dropdown: None,
+            clicked_dropdown: None,
+            was_open: None,
+            vertical: false,
+            visible: true,
+            focused: false,
+            z_level: 100,
+            center_items: false,
+            curved_circle: None,
+            title_pos: None,
+            title_buf: None,
+            curved_title_char_bufs: Vec::new(),
+            network_opacity: 1.0,
+            font_family: crate::layout::menubar_font(),
+            label: None,
+        }
+    }
+
+    pub fn with_label(mut self, label: &str) -> Self {
+        self.label = Some(label.to_string());
+        self
+    }
+
+    pub fn set_label(&mut self, label: &str) {
+        self.label = Some(label.to_string());
+    }
+
+    pub fn with_center_items(mut self, center: bool) -> Self {
+        self.center_items = center;
+        self
+    }
+
+    pub fn with_title(mut self, title: &str) -> Self {
+        self.title = title.to_string();
+        self
+    }
+
+    pub fn with_item(mut self, label: &str, items: &[&str]) -> Self {
+        self.menu_items.push(label.to_string());
+        self.vertical_items.push(label.to_string());
+        self.menu_dropdowns.push(items.iter().map(|s| s.to_string()).collect());
+        self.menu_dropdown_checked.push(vec![None; items.len()]);
+
+        let item_strs: Vec<String> = items.iter().map(|s| s.to_string()).collect();
+        let mut menu = Menu::new(label, label, &item_strs);
+        menu.vertical = self.vertical;
+        self.menus.push(Box::new(menu));
+        self
+    }
+
+    pub fn with_item_vh(mut self, horizontal_label: &str, vertical_label: &str, items: &[&str]) -> Self {
+        self.menu_items.push(horizontal_label.to_string());
+        self.vertical_items.push(vertical_label.to_string());
+        self.menu_dropdowns.push(items.iter().map(|s| s.to_string()).collect());
+        self.menu_dropdown_checked.push(vec![None; items.len()]);
+
+        let item_strs: Vec<String> = items.iter().map(|s| s.to_string()).collect();
+        let mut menu = Menu::new(horizontal_label, vertical_label, &item_strs);
+        menu.vertical = self.vertical;
+        self.menus.push(Box::new(menu));
+        self
+    }
+
+    pub fn with_vertical(mut self, vertical: bool) -> Self {
+        self.vertical = vertical;
+        for menu in &mut self.menus {
+            menu.vertical = vertical;
+        }
+        self
+    }
+
+    pub fn with_z_index(mut self, z: i32) -> Self {
+        self.z_level = z;
+        self
+    }
+
+    fn item_h_vertical(&self, idx: usize) -> f32 {
+        let font_size = 12.0;
+        let line_height = font_size * 1.2;
+        let padding_y = 12.0;
+        if let Some(menu) = self.menus.get(idx) {
+            let label_len = menu.active_title().chars().count() as f32;
+            label_len * line_height + padding_y
+        } else {
+            24.0
+        }
+    }
+
+    fn item_y_vertical(&self, idx: usize) -> f32 {
+        let mut y = 8.0;
+        if !self.title.is_empty() {
+            let font_size = 12.0;
+            let line_height = font_size * 1.2;
+            let title_h = self.title.chars().count() as f32 * line_height;
+            y += title_h + 8.0;
+        }
+        for i in 0..idx {
+            y += self.item_h_vertical(i);
+        }
+        y
+    }
+}
+
+impl Element for MenuBar {
+    fn as_any(&self) -> &dyn std::any::Any { self }
+    fn as_any_mut(&mut self) -> &mut dyn std::any::Any { self }
+    fn as_ptr(&self) -> *mut (dyn Element + 'static) {
+        self as *const Self as *mut Self as *mut (dyn Element + 'static)
+    }
+
+    fn label(&self) -> Option<String> {
+        self.label.clone()
+    }
+
+    fn set_text(&mut self, text: &str) {
+        self.label = Some(text.to_string());
+    }
+
+    fn rect(&self) -> (f32, f32, f32, f32) {
+        if !self.visible {
+            return (0.0, 0.0, 0.0, 0.0);
+        }
+        if self.vertical {
+            let total_h = if self.menus.is_empty() {
+                self.h
+            } else {
+                let last_idx = self.menus.len() - 1;
+                self.item_y_vertical(last_idx) + self.item_h_vertical(last_idx)
+            };
+            (self.x, self.y, self.w, total_h)
+        } else {
+            (self.x, self.y, self.w, self.h)
+        }
+    }
+
+    fn set_rect(&mut self, x: f32, y: f32, w: f32, h: f32) {
+        self.x = x;
+        self.y = y;
+        self.w = w;
+        self.h = h;
+
+        let parent_ptr = self as *mut MenuBar as *mut (dyn Element + 'static);
+
+        let font_setting = crate::layout::menubar_font();
+        let (_, font_size_opt) = crate::layout::parse_font_string(&font_setting);
+        let font_size = font_size_opt.unwrap_or(12.0);
+        let char_w = 7.5 * (font_size / 12.0);
+        let ih = font_size + 12.0;
+
+        if self.vertical {
+            let mut cy = 8.0;
+            if !self.title.is_empty() {
+                let font_size = 12.0;
+                let line_height = font_size * 1.2;
+                let title_h = self.title.chars().count() as f32 * line_height;
+                cy += title_h + 8.0;
+            }
+            let item_heights: Vec<f32> = (0..self.menus.len())
+                .map(|idx| self.item_h_vertical(idx))
+                .collect();
+            let mut dummy = crate::context::UiContext::new();
+            for (idx, menu) in self.menus.iter_mut().enumerate() {
+                let item_h = item_heights[idx];
+                menu.set_rect(x, y + cy, w, item_h);
+                menu.set_parent(Some(parent_ptr), &mut dummy);
+                cy += item_h;
+            }
+        } else {
+            let padding_x = crate::layout::paginator_tab_padding_x();
+            if let Some((ccx, ccy, ccr)) = self.curved_circle {
+                let r_mid = ccr - h / 2.0;
+                let mut total_width = 8.0;
+                if !self.title.is_empty() {
+                    total_width += self.title.len() as f32 * char_w + 24.0;
+                }
+                for menu in &self.menus {
+                    total_width += menu.active_title().len() as f32 * char_w + 2.0 * padding_x;
+                }
+                
+                let total_angular_width = total_width / r_mid;
+                let start_angle = 1.5 * std::f32::consts::PI - total_angular_width / 2.0;
+                let mut current_angle = start_angle;
+                
+                if !self.title.is_empty() {
+                    let title_w = self.title.len() as f32 * char_w + 24.0;
+                    let dtheta_title = title_w / r_mid;
+                    let theta_title = current_angle + dtheta_title / 2.0;
+                    
+                    let tx = ccx + r_mid * theta_title.cos() - title_w / 2.0 + 8.0;
+                    let ty = ccy + r_mid * theta_title.sin() - h / 2.0;
+                    self.title_pos = Some((tx, ty));
+                    current_angle += dtheta_title;
+                } else {
+                    self.title_pos = None;
+                }
+                
+                let mut dummy = crate::context::UiContext::new();
+                for menu in &mut self.menus {
+                    let iw = menu.active_title().len() as f32 * char_w + 2.0 * padding_x;
+                    let dtheta_menu = iw / r_mid;
+                    let theta_menu = current_angle + dtheta_menu / 2.0;
+                    
+                    let mx = ccx + r_mid * theta_menu.cos() - iw / 2.0;
+                    let my = ccy + r_mid * theta_menu.sin() - h / 2.0;
+                    
+                    menu.set_rect(mx, my, iw, h);
+                    menu.set_parent(Some(parent_ptr), &mut dummy);
+                    menu.curved_arc = Some((ccx, ccy, ccr, h, current_angle, current_angle + dtheta_menu));
+                    current_angle += dtheta_menu;
+                }
+            } else {
+                self.title_pos = None;
+                let mut cx = 8.0;
+                if self.center_items {
+                    let mut total_width = 8.0;
+                    if !self.title.is_empty() {
+                        total_width += self.title.len() as f32 * char_w + 24.0;
+                    }
+                    for menu in &self.menus {
+                        total_width += menu.active_title().len() as f32 * char_w + 2.0 * padding_x;
+                    }
+                    if self.w > total_width {
+                        cx = (self.w - total_width) / 2.0;
+                    }
+                }
+                if !self.title.is_empty() {
+                    cx += self.title.len() as f32 * char_w + 24.0;
+                }
+                let mut dummy = crate::context::UiContext::new();
+                for menu in &mut self.menus {
+                    let iw = menu.active_title().len() as f32 * char_w + 2.0 * padding_x;
+                    menu.set_rect(x + cx, y, iw, h);
+                    menu.set_parent(Some(parent_ptr), &mut dummy);
+                    cx += iw;
+                }
+            }
+        }
+    }
+
+    fn color(&self) -> [f32; 4] {
+        if !self.visible {
+            [0.0, 0.0, 0.0, 0.0]
+        } else if self.focused {
+            let mut c = colors::PANEL_MENU_FOCUSED;
+            c[3] *= self.network_opacity;
+            c
+        } else {
+            let mut c = colors::PANEL_MENU_BG;
+            c[3] *= self.network_opacity;
+            c
+        }
+    }
+
+    fn set_hovered(&mut self, v: bool) {
+        self.hovering = v;
+    }
+
+    fn hovered(&self) -> bool {
+        self.hovering
+    }
+
+    fn hit_test(&self, px: f32, py: f32, ctx: &UiContext) -> bool {
+        if !self.visible {
+            return false;
+        }
+        if crate::widget::popovers::is_coordinate_covered(self as *const Self as *const () as usize, px, py) {
+            return false;
+        }
+        let font_setting = crate::layout::menubar_font();
+        let (_, font_size_opt) = crate::layout::parse_font_string(&font_setting);
+        let font_size = font_size_opt.unwrap_or(12.0);
+        let char_w = 7.5 * (font_size / 12.0);
+
+        if let Some((ccx, ccy, ccr)) = self.curved_circle {
+            let dx = px - ccx;
+            let dy = py - ccy;
+            let dist = (dx * dx + dy * dy).sqrt();
+            if dist >= ccr - self.h && dist <= ccr {
+                let angle = dy.atan2(dx);
+                let mut norm_angle = angle;
+                if norm_angle < 0.0 {
+                    norm_angle += 2.0 * std::f32::consts::PI;
+                }
+                
+                let r_mid = ccr - self.h / 2.0;
+                let mut total_width = 8.0;
+                if !self.title.is_empty() {
+                    total_width += self.title.len() as f32 * char_w + 24.0;
+                }
+                let padding_x = crate::layout::paginator_tab_padding_x();
+                for menu in &self.menus {
+                    total_width += menu.active_title().len() as f32 * char_w + 2.0 * padding_x;
+                }
+                let total_angular_width = total_width / r_mid;
+                let start_angle = 1.5 * std::f32::consts::PI - total_angular_width / 2.0;
+                let end_angle = 1.5 * std::f32::consts::PI + total_angular_width / 2.0;
+                
+                if norm_angle >= start_angle && norm_angle <= end_angle {
+                    return true;
+                }
+            }
+            for menu in &self.menus {
+                if menu.hit_test(px, py, ctx) {
+                    return true;
+                }
+            }
+            return false;
+        }
+        let (rx, ry, rw, rh) = self.rect();
+        if px >= rx && px <= rx + rw && py >= ry && py <= ry + rh {
+            return true;
+        }
+        for menu in &self.menus {
+            if menu.hit_test(px, py, ctx) {
+                return true;
+            }
+        }
+        false
+    }
+
+    fn on_cursor_moved(&mut self, px: f32, py: f32, ctx: &mut UiContext) -> bool {
+        if !self.visible {
+            return false;
+        }
+        let (rx, ry, rw, rh) = self.rect();
+        self.set_rect(rx, ry, rw, rh);
+
+        let mut changed = false;
+        self.hovered_menu = None;
+        for (idx, menu) in self.menus.iter_mut().enumerate() {
+            if menu.cursor_moved(px, py, ctx) {
+                changed = true;
+            }
+            if menu.hovered() {
+                self.hovered_menu = Some(idx);
+            }
+        }
+        changed
+    }
+
+    fn mouse_input(&mut self, button: MouseButton, state: ElementState, px: f32, py: f32, ctx: &mut UiContext) -> bool {
+        if !self.visible {
+            return false;
+        }
+        let (rx, ry, rw, rh) = self.rect();
+        self.set_rect(rx, ry, rw, rh);
+
+        let mut changed = false;
+        for menu in &mut self.menus {
+            let res = menu.mouse_input(button, state, px, py, ctx);
+            if res {
+                changed = true;
+            }
+        }
+        if !self.is_menu_open() {
+            self.unfocus();
+        }
+        changed
+    }
+
+    fn focus(&mut self) {
+        if self.is_menu_open() {
+            self.focused = true;
+            for menu in &mut self.menus {
+                if menu.is_menu_open() {
+                    menu.focus();
+                    return;
+                }
+            }
+        } else {
+            self.focused = false;
+            focus::clear_if_matches(self);
+            return;
+        }
+        self.focused = true;
+        focus::set_focused(self);
+    }
+
+    fn unfocus(&mut self) {
+        self.focused = false;
+        focus::clear_if_matches(self);
+        for menu in &mut self.menus {
+            menu.unfocus();
+        }
+    }
+
+    fn focused(&self, ctx: &UiContext) -> bool {
+        self.focused || self.is_menu_open()
+    }
+
+    fn set_selected(&mut self, selected: bool) {
+        self.focused = selected;
+        if !selected {
+            for menu in &mut self.menus {
+                menu.set_selected(false);
+            }
+        }
+    }
+
+    fn set_modifiers(&mut self, ctrl: bool, shift: bool, alt: bool) {
+        for menu in &mut self.menus {
+            menu.set_modifiers(ctrl, shift, alt);
+        }
+    }
+
+    fn menu_names(&self) -> Vec<String> {
+        self.menu_items.clone()
+    }
+
+    fn menu_click(&mut self) -> Option<(usize, usize)> {
+        for (idx, menu) in self.menus.iter_mut().enumerate() {
+            if let Some((_, item_idx)) = menu.menu_click() {
+                return Some((idx, item_idx));
+            }
+        }
+        None
+    }
+
+    fn set_item_checked(&mut self, menu_idx: usize, item_idx: usize, checked: bool) {
+        if let Some(menu) = self.menu_dropdown_checked.get_mut(menu_idx) {
+            if item_idx < menu.len() {
+                menu[item_idx] = Some(checked);
+            }
+        }
+        if let Some(menu) = self.menus.get_mut(menu_idx) {
+            menu.set_item_checked(0, item_idx, checked);
+        }
+    }
+
+    fn set_menu_items(&mut self, menu_idx: usize, items: &[String]) {
+        if menu_idx < self.menu_dropdowns.len() {
+            self.menu_dropdowns[menu_idx] = items.to_vec();
+            self.menu_dropdown_checked[menu_idx] = vec![Some(false); items.len()];
+        }
+        if let Some(menu) = self.menus.get_mut(menu_idx) {
+            menu.items = items.to_vec();
+            menu.item_checked = vec![Some(false); items.len()];
+            menu.item_bufs.clear();
+        }
+    }
+
+    fn is_menu_bar(&self) -> bool {
+        self.visible
+    }
+
+    fn get_menu_items_at(&self, px: f32, py: f32) -> Option<(usize, String, Vec<String>, f32, f32, f32, f32)> {
+        if !self.visible {
+            return None;
+        }
+        let dummy = crate::context::UiContext::new();
+        for (idx, menu) in self.menus.iter().enumerate() {
+            if menu.hit_test(px, py, &dummy) {
+                let mut formatted_items = Vec::new();
+                for (i, item) in menu.items.iter().enumerate() {
+                    let checked = menu.item_checked.get(i).and_then(|&v| v);
+                    let prefix = match checked {
+                        Some(true) => "✓ ",
+                        Some(false) => "  ",
+                        None => "",
+                    };
+                    formatted_items.push(format!("{}{}", prefix, item));
+                }
+                return Some((idx, menu.active_title().to_string(), formatted_items, menu.base.x, menu.base.y, menu.base.w, menu.base.h));
+            }
+        }
+        None
+    }
+
+    fn trigger_menu_click(&mut self, menu_idx: usize, item_idx: usize) {
+        if let Some(menu) = self.menus.get_mut(menu_idx) {
+            menu.clicked_item = Some(item_idx);
+        }
+    }
+
+    fn is_menu_open(&self) -> bool {
+        self.visible && self.menus.iter().any(|m| m.is_menu_open())
+    }
+
+    fn all_quads(&self, ctx: &UiContext) -> Vec<(f32, f32, f32, f32, [f32; 4])> {
+        if !self.visible {
+            return Vec::new();
+        }
+        let mut quads = Vec::new();
+        for menu in &self.menus {
+            quads.extend(menu.all_quads(ctx));
+        }
+        quads
+    }
+
+    fn extra_arcs(&self) -> Vec<(f32, f32, f32, f32, f32, f32, [f32; 4])> {
+        if !self.visible {
+            return Vec::new();
+        }
+        let mut arcs = Vec::new();
+        for menu in &self.menus {
+            arcs.extend(menu.extra_arcs());
+        }
+        arcs
+    }
+
+    fn prepare_text(&mut self, fs: &mut glyphon::FontSystem) {
+        if !self.visible {
+            return;
+        }
+        let current_font = crate::layout::menubar_font();
+        if self.font_family != current_font {
+            self.font_family = current_font;
+            self.title_buf = None;
+            self.curved_title_char_bufs.clear();
+            for menu in &mut self.menus {
+                menu.title_buf = None;
+                menu.curved_char_bufs.clear();
+                menu.item_bufs.clear();
+            }
+        }
+        let (font_fam, font_size_opt) = crate::layout::parse_font_string(&self.font_family);
+        let font_size = font_size_opt.unwrap_or(12.0);
+
+        if !self.title.is_empty() {
+            if let Some((_ccx, _ccy, _ccr)) = self.curved_circle {
+                if self.curved_title_char_bufs.len() != self.title.chars().count() {
+                    let font_fam_clone = font_fam.clone();
+                    self.curved_title_char_bufs = self.title.chars()
+                        .map(|c| make_widget_text_buffer(fs, &c.to_string(), font_size, &font_fam_clone))
+                        .collect();
+                }
+                self.title_buf = None;
+            } else if self.vertical {
+                self.title_buf = None;
+                self.curved_title_char_bufs.clear();
+            } else {
+                if self.title_buf.is_none() {
+                    self.title_buf = Some(make_widget_text_buffer(fs, &self.title, font_size, &font_fam));
+                }
+                self.curved_title_char_bufs.clear();
+            }
+        } else {
+            self.title_buf = None;
+            self.curved_title_char_bufs.clear();
+        }
+        for menu in &mut self.menus {
+            menu.prepare_text(fs);
+        }
+    }
+
+    fn get_text_items(&self) -> Vec<(&glyphon::Buffer, f32, f32, glyphon::Color)> {
+        if !self.visible {
+            return Vec::new();
+        }
+        let mut items = Vec::new();
+        let color = glyphon::Color::rgb(0xaa, 0xaa, 0xbb);
+
+        let font_setting = crate::layout::menubar_font();
+        let (_, font_size_opt) = crate::layout::parse_font_string(&font_setting);
+        let font_size = font_size_opt.unwrap_or(12.0);
+        let char_w = 7.5 * (font_size / 12.0);
+
+        let padding_x = crate::layout::paginator_tab_padding_x();
+        if let Some((ccx, ccy, ccr)) = self.curved_circle {
+            let r_mid = ccr - self.h / 2.0;
+            let mut total_width = 8.0;
+            if !self.title.is_empty() {
+                total_width += self.title.len() as f32 * char_w + 24.0;
+            }
+            for menu in &self.menus {
+                total_width += menu.active_title().len() as f32 * char_w + 2.0 * padding_x;
+            }
+            let total_angular_width = total_width / r_mid;
+            let start_angle = 1.5 * std::f32::consts::PI - total_angular_width / 2.0;
+            let current_angle = start_angle;
+
+            if !self.title.is_empty() {
+                let title_w = self.title.len() as f32 * char_w + 24.0;
+                let dtheta_title = title_w / r_mid;
+
+                let char_widths: Vec<f32> = self.title.chars().map(|c| {
+                    TextLabel::estimate_width(&c.to_string(), font_size)
+                }).collect();
+                let total_chars_width: f32 = char_widths.iter().sum();
+                let mid_angle = (current_angle + current_angle + dtheta_title) / 2.0;
+                let angular_width = total_chars_width / r_mid;
+                let text_start_angle = mid_angle - angular_width / 2.0;
+                let mut cur_char_angle = text_start_angle;
+
+                for (char_idx, c_buf) in self.curved_title_char_bufs.iter().enumerate() {
+                    let cw = char_widths[char_idx];
+                    let dtheta = cw / r_mid;
+                    let char_center_angle = cur_char_angle + dtheta / 2.0;
+
+                    let tx = ccx + r_mid * char_center_angle.cos() - cw / 2.0;
+                    let ty = ccy + r_mid * char_center_angle.sin() - font_size / 2.0;
+
+                    items.push((c_buf, tx, ty, color));
+                    cur_char_angle += dtheta;
+                }
+            }
+        } else if !self.vertical {
+            let mut start_x = 8.0;
+            if self.center_items {
+                let mut total_width = 8.0;
+                if !self.title.is_empty() {
+                    total_width += self.title.len() as f32 * char_w + 24.0;
+                }
+                for menu in &self.menus {
+                    total_width += menu.active_title().len() as f32 * char_w + 2.0 * padding_x;
+                }
+                if self.w > total_width {
+                    start_x = (self.w - total_width) / 2.0;
+                }
+            }
+            if let Some(ref title_buf) = self.title_buf {
+                let text_y = self.y + (self.h - font_size) / 2.0;
+                items.push((title_buf, self.x + start_x, text_y, color));
+            }
+        }
+
+        for menu in &self.menus {
+            items.extend(menu.get_text_items());
+        }
+        items
+    }
+
+    fn text_labels(&self) -> Vec<TextLabel> {
+        if !self.visible {
+            return Vec::new();
+        }
+        let padding_x = crate::layout::paginator_tab_padding_x();
+        let mut labels = Vec::new();
+
+        if let Some(ref label) = self.label {
+            if self.vertical {
+                let font_size = 12.0;
+                let line_height = font_size * 1.2;
+                let label_len = label.chars().count() as f32;
+                let total_h = label_len * line_height;
+                let start_y = self.y - (crate::layout::label_margin() + total_h);
+                for (i, c) in label.chars().enumerate() {
+                    let char_str = c.to_string();
+                    let char_w = TextLabel::estimate_width(&char_str, font_size);
+                    let x_pos = self.x + (self.w - char_w) / 2.0;
+                    let y_pos = start_y + i as f32 * line_height;
+                    labels.push(TextLabel {
+                        text: char_str,
+                        x: x_pos,
+                        y: y_pos,
+                        font_size,
+                        color: [0x83, 0x83, 0x8a],
+                    });
+                }
+            } else {
+                labels.push(TextLabel {
+                    text: label.clone(),
+                    x: self.x,
+                    y: self.y - (12.0 + crate::layout::label_margin()),
+                    font_size: 12.0,
+                    color: [0x83, 0x83, 0x8a],
+                });
+            }
+        }
+
+        let font_setting = crate::layout::menubar_font();
+        let (_, font_size_opt) = crate::layout::parse_font_string(&font_setting);
+        let font_size = font_size_opt.unwrap_or(12.0);
+        let char_w = 7.5 * (font_size / 12.0);
+
+        if let Some((ccx, ccy, ccr)) = self.curved_circle {
+            let r_mid = ccr - self.h / 2.0;
+            let mut total_width = 8.0;
+            if !self.title.is_empty() {
+                total_width += self.title.len() as f32 * char_w + 24.0;
+            }
+            for menu in &self.menus {
+                total_width += menu.active_title().len() as f32 * char_w + 2.0 * padding_x;
+            }
+            let total_angular_width = total_width / r_mid;
+            let start_angle = 1.5 * std::f32::consts::PI - total_angular_width / 2.0;
+            let current_angle = start_angle;
+
+            if !self.title.is_empty() {
+                let title_w = self.title.len() as f32 * char_w + 24.0;
+                let dtheta_title = title_w / r_mid;
+                labels.extend(TextLabel::curved_layout(
+                    &self.title,
+                    ccx, ccy, r_mid,
+                    current_angle, current_angle + dtheta_title,
+                    font_size,
+                    [0xaa, 0xaa, 0xbb],
+                ));
+            }
+        } else if self.vertical {
+            if !self.title.is_empty() {
+                let line_height = font_size * 1.2;
+                let start_y = self.y + 8.0;
+                for (i, c) in self.title.chars().enumerate() {
+                    let char_str = c.to_string();
+                    let char_w = TextLabel::estimate_width(&char_str, font_size);
+                    let x_pos = self.x + (self.w 
diff truncated