tawasuyu

tawasuyu/pineal

Backend-agnostic GPU visualization — a catalog of painters over one Canvas trait, in Rust.

chore: refresco desde el monorepo (pin tawasuyu.git v0.2.0)

Regenerado con scripts/actualizar-standalone.py: código del dominio al día,
git-deps repineadas a v0.2.0 y doble-fuente resuelta con [patch] al source git.
cargo check --workspace verde.

df7773b4f2df1f1227adb0afd7afd60935e384e9 · por Sergio · (hace 3 meses) · padre 18a001b6a · ver el árbol en este commit

Delta semántico · 44

00_unanchay/pineal/demos/pineal-galeria-demo/examples/pantallazo_pineal.rs

00_unanchay/pineal/demos/pineal-galeria-demo/examples/pineal_showreel.rs

00_unanchay/pineal/demos/pineal-galeria-demo/src/main.rs

75 archivos · +3535 −562

diff --git a/00_unanchay/pineal/LEEME.md b/00_unanchay/pineal/LEEME.md
index 2b68415..366b2d9 100644
--- a/00_unanchay/pineal/LEEME.md
+++ b/00_unanchay/pineal/LEEME.md
@@ -2,6 +2,8 @@
 
 > Visualización agnóstica del backend. El "tercer ojo" del monorepo.
 
+![la galería de painters de pineal renderizada headless: una grilla de 11 tiles — cartesiano con senoide, pie/donut, radar, treemap squarified, heatmap Viridis, hexbin, isolíneas de contour, un sankey de presupuesto, un mesh force-directed, barras con un valor negativo y un histograma](https://tawasuyu.net/00_unanchay/pineal/pantallazo.png)
+
 Pineal es un catálogo de painters especializados — cartesiano, polar,
 mesh, treemap, phosphor, flow, heatmap, stream, financial, hexbin,
 contour y bars — sobre una **única abstracción de pintura**, el trait
@@ -116,8 +118,8 @@ pasada vello hermana) cuando hagan falta etiquetas.
 
 ## Consideraciones
 
-- pineal **no calcula** — sólo dibuja. Para correr una simulación hablás
-  con `dominium`, `tinkuy`, `cosmos`, etc., y le pasás el resultado.
+- pineal **no calcula** — sólo dibuja. Para correr una simulación hablas
+  con `dominium`, `tinkuy`, `cosmos`, etc., y le pasas el resultado.
 - El export a SVG es vector real (no captura de píxeles). El PNG es un
   rasterizador software diminuto — sin stack gráfico nativo. El texto se
   omite en PNG y en el camino GPU (usar SVG o una pasada vello hermana).
diff --git a/00_unanchay/pineal/README.md b/00_unanchay/pineal/README.md
index 553b8bb..ab07a83 100644
--- a/00_unanchay/pineal/README.md
+++ b/00_unanchay/pineal/README.md
@@ -2,6 +2,8 @@
 
 > Backend-agnostic visualization. The "third eye" of the monorepo.
 
+![the pineal painter gallery rendered headless: an 11-tile grid — sine cartesian, pie/donut, radar, squarified treemap, Viridis heatmap, hexbin, contour isolines, a budget sankey, a force-directed mesh, bars with a negative value and a histogram](https://tawasuyu.net/00_unanchay/pineal/pantallazo.png)
+
 Pineal is a catalog of specialized painters — cartesian, polar, mesh,
 treemap, phosphor, flow, heatmap, stream, financial, hexbin, contour and
 bars — over a **single painter abstraction**, the `Canvas` trait. Any
diff --git a/00_unanchay/pineal/README.qu.md b/00_unanchay/pineal/README.qu.md
index 9f1658a..52d44ca 100644
--- a/00_unanchay/pineal/README.qu.md
+++ b/00_unanchay/pineal/README.qu.md
@@ -22,7 +22,7 @@ cargo run --release -p pineal-stream-demo
 
 ## Crateskuna
 
-Tukuy crates yachachiyninwan iskay versionkunapi: [`pineal-core`](pineal-core/README.md) escena modelo, [`pineal-render`](pineal-render/README.md) Llimphi rikuchikuy, [`pineal-{cartesian,polar,mesh,treemap,phosphor,flow,heatmap,stream,financial,umbrella}`](pineal-cartesian/README.md) sapanka layakuna, [`pineal-export`](pineal-export/README.md) PNG/SVG/GIF qatinapaq, [`pineal-{demo,financial-demo,phosphor-demo,stream-demo}`](pineal-demo/README.md) muestrakuna.
+Tukuy crates yachachiyninwan iskay versionkunapi: [`pineal-core`](pineal-core/README.md) escena modelo, [`pineal-render`](pineal-render/README.md) Llimphi rikuchikuy, [`pineal-{cartesian,polar,mesh,treemap,phosphor,flow,heatmap,stream,financial,umbrella}`](pineal-cartesian/README.md) sapanka layakuna, [`pineal-export`](pineal-export/README.md) PNG/SVG/GIF qatinapaq, [`pineal-{demo,financial-demo,phosphor-demo,stream-demo}`](demos/pineal-demo/README.md) muestrakuna.
 
 ## Yuyaykunaq
 
diff --git a/00_unanchay/pineal/demos/pineal-bars-demo/LEEME.md b/00_unanchay/pineal/demos/pineal-bars-demo/LEEME.md
new file mode 100644
index 0000000..9dc651c
--- /dev/null
+++ b/00_unanchay/pineal/demos/pineal-bars-demo/LEEME.md
@@ -0,0 +1,27 @@
+# pineal-bars-demo
+
+*Read this in English: [README.md](README.md).*
+
+El painter de barras del catálogo, en sus cinco modos, sobre una grilla estática:
+
+- **Columnas** (vertical simple, con un valor negativo para mostrar el
+  baseline).
+- **Barras** (horizontal simple).
+- **Agrupadas** (dos series clustered por categoría).
+- **Apiladas** (segmentos sobre un baseline común).
+- **Histograma** (muestra gaussiana sintética bineada).
+
+Datos 100 % deterministas (sin `Date::now`/random): la muestra del
+histograma sale de un LCG sembrado con constante. Es un showcase, sin
+animación. Las etiquetas de eje no las pinta el painter (regla del
+SDD: el texto va aparte) — aquí los títulos viven en Views hermanas.
+
+## Uso
+
+```sh
+cargo run --release -p pineal-bars-demo
+```
+
+---
+
+Parte de **pineal** — ver [pineal](../../LEEME.md).
diff --git a/00_unanchay/pineal/demos/pineal-bars-demo/README.md b/00_unanchay/pineal/demos/pineal-bars-demo/README.md
new file mode 100644
index 0000000..7f90be8
--- /dev/null
+++ b/00_unanchay/pineal/demos/pineal-bars-demo/README.md
@@ -0,0 +1,12 @@
+# pineal-bars-demo
+
+The catalog's bar painter in its five modes, over a static grid:
+
+- **Columns** (simple vertical, with a negative value to show the baseline).
+- **Bars** (simple horizontal).
+- **Grouped** (two series clustered per category).
+- **Stacked** and the remaining variants.
+
+---
+
+Part of **pineal** — see [pineal](../../README.md).
diff --git a/00_unanchay/pineal/demos/pineal-bars-demo/src/main.rs b/00_unanchay/pineal/demos/pineal-bars-demo/src/main.rs
index 7fb8a98..dd6fcec 100644
--- a/00_unanchay/pineal/demos/pineal-bars-demo/src/main.rs
+++ b/00_unanchay/pineal/demos/pineal-bars-demo/src/main.rs
@@ -11,7 +11,7 @@
 //! Datos 100 % deterministas (sin `Date::now`/random): la muestra del
 //! histograma sale de un LCG sembrado con constante. Es un showcase, sin
 //! animación. Las etiquetas de eje no las pinta el painter (regla del
-//! SDD: el texto va aparte) — acá los títulos viven en Views hermanas.
+//! SDD: el texto va aparte) — aquí los títulos viven en Views hermanas.
 
 use std::sync::Arc;
 
diff --git a/00_unanchay/pineal/demos/pineal-contour-demo/LEEME.md b/00_unanchay/pineal/demos/pineal-contour-demo/LEEME.md
new file mode 100644
index 0000000..27aa06a
--- /dev/null
+++ b/00_unanchay/pineal/demos/pineal-contour-demo/LEEME.md
@@ -0,0 +1,21 @@
+# pineal-contour-demo
+
+*Read this in English: [README.md](README.md).*
+
+Campo escalar con 8 isolíneas + heatmap base.
+
+Renderiza primero el heatmap Viridis del campo (para contexto), y
+encima 8 isolíneas extraídas por marching squares con gradiente
+azul→rojo. Matriz 64×48; el campo es
+`sin(x · 0.4 - t · 0.1) + cos(y · 0.4 + t · 0.07)` con un tick lento
+para que se vea la deformación.
+
+## Uso
+
+```sh
+cargo run --release -p pineal-contour-demo
+```
+
+---
+
+Parte de **pineal** — ver [pineal](../../LEEME.md).
diff --git a/00_unanchay/pineal/demos/pineal-contour-demo/README.md b/00_unanchay/pineal/demos/pineal-contour-demo/README.md
new file mode 100644
index 0000000..564978d
--- /dev/null
+++ b/00_unanchay/pineal/demos/pineal-contour-demo/README.md
@@ -0,0 +1,11 @@
+# pineal-contour-demo
+
+A scalar field with 8 isolines plus a base heatmap.
+
+It first renders the field's Viridis heatmap (for context), and over it 8 isolines
+extracted by marching squares with a blue→red gradient. A 64×48 matrix; the field
+is `sin(x · 0.4 - t · 0.1) + cos(y · 0.4 + t · 0.07)` with a slow tick.
+
+---
+
+Part of **pineal** — see [pineal](../../README.md).
diff --git a/00_unanchay/pineal/demos/pineal-demo/LEEME.md b/00_unanchay/pineal/demos/pineal-demo/LEEME.md
index 2abfcf7..ec11829 100644
--- a/00_unanchay/pineal/demos/pineal-demo/LEEME.md
+++ b/00_unanchay/pineal/demos/pineal-demo/LEEME.md
@@ -1,8 +1,8 @@
 # pineal-demo
 
-> Demo gallery del catálogo de [pineal](../README.md).
+> Demo gallery del catálogo de [pineal](../../README.md).
 
-Binario que muestra todos los backends (`cartesian`, `polar`, `mesh`, `treemap`, `flow`, `heatmap`, `umbrella`) en una grid Llimphi. Click en una demo abre la vista completa. Sirve como **regression visual**: si rompés un backend, la demo lo grita.
+Binario que muestra todos los backends (`cartesian`, `polar`, `mesh`, `treemap`, `flow`, `heatmap`, `umbrella`) en una grid Llimphi. Click en una demo abre la vista completa. Sirve como **regression visual**: si rompes un backend, la demo lo grita.
 
 ## Uso
 
@@ -13,4 +13,4 @@ cargo run --release -p pineal-demo
 ## Deps
 
 - Todos los backends de pineal
-- [`llimphi-ui`](../../../02_ruway/llimphi/)
+- [`llimphi-ui`](../../../../02_ruway/llimphi/)
diff --git a/00_unanchay/pineal/demos/pineal-demo/README.md b/00_unanchay/pineal/demos/pineal-demo/README.md
index 7dc0fc3..a41a74f 100644
--- a/00_unanchay/pineal/demos/pineal-demo/README.md
+++ b/00_unanchay/pineal/demos/pineal-demo/README.md
@@ -1,6 +1,6 @@
 # pineal-demo
 
-> Catalog demo gallery for [pineal](../README.md).
+> Catalog demo gallery for [pineal](../../README.md).
 
 Binary that shows all backends (`cartesian`, `polar`, `mesh`, `treemap`, `flow`, `heatmap`, `umbrella`) in a Llimphi grid. Click on a demo opens the full view. Works as **visual regression**: if you break a backend, the demo screams.
 
@@ -13,4 +13,4 @@ cargo run --release -p pineal-demo
 ## Deps
 
 - All pineal backends
-- [`llimphi-ui`](../../../02_ruway/llimphi/)
+- [`llimphi-ui`](../../../../02_ruway/llimphi/)
diff --git a/00_unanchay/pineal/demos/pineal-financial-demo/LEEME.md b/00_unanchay/pineal/demos/pineal-financial-demo/LEEME.md
index 0b3098b..054cd50 100644
--- a/00_unanchay/pineal/demos/pineal-financial-demo/LEEME.md
+++ b/00_unanchay/pineal/demos/pineal-financial-demo/LEEME.md
@@ -1,8 +1,8 @@
 # pineal-financial-demo
 
-> Demo del backend financiero de [pineal](../README.md).
+> Demo del backend financiero de [pineal](../../README.md).
 
-Carga bars OHLCV sintéticos (o de un CSV pasado por CLI), aplica overlays (SMA, EMA, Bollinger) y los muestra. Si pasás un símbolo y tenés conexión, intenta cargar bars desde un endpoint público — pero por defecto opera offline con datos generados.
+Carga bars OHLCV sintéticos (o de un CSV pasado por CLI), aplica overlays (SMA, EMA, Bollinger) y los muestra. Si pasas un símbolo y tienes conexión, intenta cargar bars desde un endpoint público — pero por defecto opera offline con datos generados.
 
 ## Uso
 
@@ -13,4 +13,4 @@ cargo run --release -p pineal-financial-demo -- --csv bars.csv
 
 ## Deps
 
-- [`pineal-financial`](../pineal-financial/README.md), [`pineal-render`](../pineal-render/README.md)
+- [`pineal-financial`](../../pineal-financial/README.md), [`pineal-render`](../../pineal-render/README.md)
diff --git a/00_unanchay/pineal/demos/pineal-financial-demo/README.md b/00_unanchay/pineal/demos/pineal-financial-demo/README.md
index 91619e0..99a6358 100644
--- a/00_unanchay/pineal/demos/pineal-financial-demo/README.md
+++ b/00_unanchay/pineal/demos/pineal-financial-demo/README.md
@@ -1,6 +1,6 @@
 # pineal-financial-demo
 
-> Financial backend demo for [pineal](../README.md).
+> Financial backend demo for [pineal](../../README.md).
 
 Loads synthetic OHLCV bars (or from a CSV passed via CLI), applies overlays (SMA, EMA, Bollinger) and renders them. If you pass a symbol with connectivity, it tries to load bars from a public endpoint — but by default it works offline with generated data.
 
@@ -13,4 +13,4 @@ cargo run --release -p pineal-financial-demo -- --csv bars.csv
 
 ## Deps
 
-- [`pineal-financial`](../pineal-financial/README.md), [`pineal-render`](../pineal-render/README.md)
+- [`pineal-financial`](../../pineal-financial/README.md), [`pineal-render`](../../pineal-render/README.md)
diff --git a/00_unanchay/pineal/demos/pineal-flow-demo/LEEME.md b/00_unanchay/pineal/demos/pineal-flow-demo/LEEME.md
new file mode 100644
index 0000000..aaec5a5
--- /dev/null
+++ b/00_unanchay/pineal/demos/pineal-flow-demo/LEEME.md
@@ -0,0 +1,20 @@
+# pineal-flow-demo
+
+*Read this in English: [README.md](README.md).*
+
+Sankey de presupuesto familiar.
+
+4 fuentes de ingreso → 5 categorías de gasto → 1 nodo de ahorro.
+El algoritmo de layout (longest-path + barycenter) ubica los
+nodos en columnas y minimiza cruces; las bandas se tesselan con
+curva S (smoothstep) y se rendean como triangle strips.
+
+## Uso
+
+```sh
+cargo run --release -p pineal-flow-demo
+```
+
+---
+
+Parte de **pineal** — ver [pineal](../../LEEME.md).
diff --git a/00_unanchay/pineal/demos/pineal-flow-demo/README.md b/00_unanchay/pineal/demos/pineal-flow-demo/README.md
new file mode 100644
index 0000000..bbf564a
--- /dev/null
+++ b/00_unanchay/pineal/demos/pineal-flow-demo/README.md
@@ -0,0 +1,12 @@
+# pineal-flow-demo
+
+A Sankey of a household budget.
+
+Four income sources → five spending categories → one savings node. The layout
+algorithm (longest-path + barycenter) places the nodes in columns and minimizes
+crossings; the bands are tessellated with an S curve (smoothstep) and rendered as
+triangle strips.
+
+---
+
+Part of **pineal** — see [pineal](../../README.md).
diff --git a/00_unanchay/pineal/demos/pineal-galeria-demo/Cargo.toml b/00_unanchay/pineal/demos/pineal-galeria-demo/Cargo.toml
index 5648b7b..2402f8e 100644
--- a/00_unanchay/pineal/demos/pineal-galeria-demo/Cargo.toml
+++ b/00_unanchay/pineal/demos/pineal-galeria-demo/Cargo.toml
@@ -21,3 +21,11 @@ pineal-contour = { path = "../../pineal-contour" }
 pineal-flow = { path = "../../pineal-flow" }
 pineal-mesh = { path = "../../pineal-mesh" }
 pineal-bars = { path = "../../pineal-bars" }
+
+[dev-dependencies]
+# Sólo para los examples headless (`pantallazo_pineal`, `pineal_showreel`).
+pollster = { workspace = true }
+png = { workspace = true }
+# Beats animados extra del showreel (no los usa la galería estática).
+pineal-financial = { path = "../../pineal-financial" }
+pineal-cartesian = { path = "../../pineal-cartesian" }
diff --git a/00_unanchay/pineal/demos/pineal-galeria-demo/LEEME.md b/00_unanchay/pineal/demos/pineal-galeria-demo/LEEME.md
new file mode 100644
index 0000000..bad7227
--- /dev/null
+++ b/00_unanchay/pineal/demos/pineal-galeria-demo/LEEME.md
@@ -0,0 +1,33 @@
+# pineal-galeria-demo
+
+*Read this in English: [README.md](README.md).*
+
+Galería estática de TODO el catálogo de painters.
+
+Una sola ventana con una grilla 3×3 de tiles; cada tile pinta un
+painter distinto de pineal con datos sintéticos deterministas (sin
+timers, sin RNG de sistema): cartesian (sinusoide a mano), polar
+(pie/donut y radar), treemap, heatmap, hexbin, contour, flow (Sankey)
+y mesh (grafo force-directed pre-relajado).
+
+Los painters animados (phosphor, stream) y el financial tienen su
+propio demo en vivo y quedan FUERA — la galería es un showcase
+estático. El tile #9 es un cartesiano extra (segunda señal) para
+completar la grilla.
+
+Cada tile reusa la construcción de datos de su demo suelto en
+`pineal-<painter>-demo`, así que las firmas son las reales. Cada
+closure de `paint_with` captura SUS datos por `move`.
+
+Cableado de UI: barra de menú principal mínima (Archivo / Ver). Los
+tiles son canvas estáticos sin edición ni clipboard.
+
+## Uso
+
+```sh
+cargo run --release -p pineal-galeria-demo
+```
+
+---
+
+Parte de **pineal** — ver [pineal](../../LEEME.md).
diff --git a/00_unanchay/pineal/demos/pineal-galeria-demo/README.md b/00_unanchay/pineal/demos/pineal-galeria-demo/README.md
new file mode 100644
index 0000000..ee5c733
--- /dev/null
+++ b/00_unanchay/pineal/demos/pineal-galeria-demo/README.md
@@ -0,0 +1,12 @@
+# pineal-galeria-demo
+
+A static gallery of the WHOLE painter catalog.
+
+One window with a 3×3 grid of tiles; each tile paints a different pineal painter
+with deterministic synthetic data (no timers, no system RNG): cartesian (a
+hand-made sinusoid), polar (pie/donut and radar), treemap, heatmap, hexbin,
+contour, flow (Sankey) and the rest.
+
+---
+
+Part of **pineal** — see [pineal](../../README.md).
diff --git a/00_unanchay/pineal/demos/pineal-galeria-demo/examples/pantallazo_pineal.rs b/00_unanchay/pineal/demos/pineal-galeria-demo/examples/pantallazo_pineal.rs
new file mode 100644
index 0000000..6533044
--- /dev/null
+++ b/00_unanchay/pineal/demos/pineal-galeria-demo/examples/pantallazo_pineal.rs
@@ -0,0 +1,159 @@
+//! Pantallazo headless de `pineal` — la galería completa de painters.
+//!
+//! Monta la **view real** de `pineal-galeria-demo` (grilla de 11 tiles:
+//! cartesian, pie/donut, radar, treemap, heatmap, hexbin, contour, sankey,
+//! mesh force-directed, bars e histograma — todos con datos sintéticos
+//! deterministas, sin reloj ni RNG de sistema) reutilizando el `init` y el
+//! `view` del demo vía `#[path]`, con un `Handle::for_test()` (no hay
+//! event loop).
+//!
+//! Pinta a una textura wgpu sin ventana y vuelca PNG (mismo patrón que
+//! `tullpu-app-llimphi/examples/pantallazo_tullpu.rs`).
+//!
+//! `cargo run -p pineal-galeria-demo --example pantallazo_pineal --release -- [out.png]`
+#![allow(dead_code)]
+
+// El demo es un crate binario sin lib: incluimos su `main.rs` real por
+// `#[path]` para montar exactamente la misma view que pinta la ventana.
+#[path = "../src/main.rs"]
+mod galeria;
+
+use std::fs::File;
+use std::io::BufWriter;
+
+use llimphi_theme::Theme;
+use llimphi_ui::llimphi_hal::{wgpu, Hal};
+use llimphi_ui::llimphi_layout::taffy;
+use llimphi_ui::llimphi_layout::LayoutTree;
+use llimphi_ui::llimphi_raster::peniko::Color;
+use llimphi_ui::llimphi_raster::{vello, Renderer};
+use llimphi_ui::llimphi_text::Typesetter;
+use llimphi_ui::{measure_text_node, mount, paint, App, Handle};
+
+use crate::galeria::GaleriaDemo;
+
+const W: u32 = 1280;
+const H: u32 = 860;
+const FMT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba8Unorm;
+
+fn main() {
+    let out = std::env::args()
+        .nth(1)
+        .unwrap_or_else(|| "/tmp/shots/pineal.png".to_string());
+    if let Some(dir) = std::path::Path::new(&out).parent() {
+        std::fs::create_dir_all(dir).ok();
+    }
+
+    // El init real del demo: relaja el grafo del tile mesh, bina el hexbin
+    // y computa el campo escalar. El handle de test descarta dispatches.
+    let handle: Handle<galeria::Msg> = Handle::for_test();
+    let model = GaleriaDemo::init(&handle);
+    let root = GaleriaDemo::view(&model);
+
+    // view → layout → scene (misma secuencia que el eventloop real).
+    let mut layout = LayoutTree::new();
+    let mounted = mount(&mut layout, root);
+    let mut ts = Typesetter::new();
+    let computed = {
+        let tmap = &mounted.text_measures;
+        layout
+            .compute_with_measure(mounted.root, (W as f32, H as f32), |nid, known, avail| {
+                match tmap.get(&nid) {
+                    Some(tm) => measure_text_node(&mut ts, tm, known, avail),
+                    None => taffy::Size::ZERO,
+                }
+            })
+            .expect("layout")
+    };
+    let mut scene = vello::Scene::new();
+    paint(&mut scene, &mounted, &computed, &mut ts, None, None);
+
+    let hal = pollster::block_on(Hal::new(None)).expect("hal");
+    let mut renderer = Renderer::new(&hal).expect("renderer");
+    let target = hal.device.create_texture(&wgpu::TextureDescriptor {
+        label: Some("pantallazo-pineal"),
+        size: wgpu::Extent3d {
+            width: W,
+            height: H,
+            depth_or_array_layers: 1,
+        },
+        mip_level_count: 1,
+        sample_count: 1,
+        dimension: wgpu::TextureDimension::D2,
+        format: FMT,
+        usage: wgpu::TextureUsages::STORAGE_BINDING
+            | wgpu::TextureUsages::RENDER_ATTACHMENT
+            | wgpu::TextureUsages::COPY_SRC,
+        view_formats: &[],
+    });
+    let view = target.create_view(&wgpu::TextureViewDescriptor::default());
+    // Mismo fondo que el init del demo (Theme::dark) — el campo `theme`
+    // del Model es privado al módulo incluido.
+    let [r, g, b, _] = Theme::dark().bg_app.components;
+    let bg = Color::from_rgba8((r * 255.0) as u8, (g * 255.0) as u8, (b * 255.0) as u8, 255);
+    renderer
+        .render_to_view(&hal, &scene, &view, W, H, bg)
+        .expect("render_to_view");
+
+    write_png(&hal, &target, &out);
+    eprintln!("pantallazo_pineal: escrito {out} ({W}x{H})");
+}
+
+/// Lee la textura a CPU y la vuelca como PNG RGBA8.
+fn write_png(hal: &Hal, target: &wgpu::Texture, path: &str) {
+    let unpadded = (W * 4) as usize;
+    let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT as usize;
+    let padded = unpadded.div_ceil(align) * align;
+    let buf = hal.device.create_buffer(&wgpu::BufferDescriptor {
+        label: Some("readback"),
+        size: (padded * H as usize) as u64,
+        usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
+        mapped_at_creation: false,
+    });
+    let mut enc = hal
+        .device
+        .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
+    enc.copy_texture_to_buffer(
+        wgpu::TexelCopyTextureInfo {
+            texture: target,
+            mip_level: 0,
+            origin: wgpu::Origin3d::ZERO,
+            aspect: wgpu::TextureAspect::All,
+        },
+        wgpu::TexelCopyBufferInfo {
+            buffer: &buf,
+            layout: wgpu::TexelCopyBufferLayout {
+                offset: 0,
+                bytes_per_row: Some(padded as u32),
+                rows_per_image: Some(H),
+            },
+        },
+        wgpu::Extent3d {
+            width: W,
+            height: H,
+            depth_or_array_layers: 1,
+        },
+    );
+    hal.queue.submit(std::iter::once(enc.finish()));
+    let slice = buf.slice(..);
+    let (tx, rx) = std::sync::mpsc::channel();
+    slice.map_async(wgpu::MapMode::Read, move |r| {
+        let _ = tx.send(r);
+    });
+    let _ = hal.device.poll(wgpu::PollType::wait_indefinitely());
+    rx.recv().unwrap().unwrap();
+    let data = slice.get_mapped_range();
+    let mut pixels = Vec::with_capacity((W * H * 4) as usize);
+    for row in 0..H as usize {
+        let s = row * padded;
+        pixels.extend_from_slice(&data[s..s + unpadded]);
+    }
+    drop(data);
+    buf.unmap();
+    let file = File::create(path).expect("png");
+    let mut enc = png::Encoder::new(BufWriter::new(file), W, H);
+    enc.set_color(png::ColorType::Rgba);
+    enc.set_depth(png::BitDepth::Eight);
+    let mut w = enc.write_header().unwrap();
+    w.write_image_data(&pixels).unwrap();
+}
diff --git a/00_unanchay/pineal/demos/pineal-galeria-demo/examples/pineal_showreel.rs b/00_unanchay/pineal/demos/pineal-galeria-demo/examples/pineal_showreel.rs
new file mode 100644
index 0000000..d5013aa
--- /dev/null
+++ b/00_unanchay/pineal/demos/pineal-galeria-demo/examples/pineal_showreel.rs
@@ -0,0 +1,819 @@
+//! **Showreel** de `pineal` — montaje de tipos de gráfico para el README
+//! del standalone (audiencia r/rust). NO es eye-candy abstracto: cada beat
+//! invoca un **painter REAL** del catálogo de pineal (`paint_bars`,
+//! `paint_heatmap`, `paint_contours`, `paint_pie`, `paint_radar`,
+//! `paint_treemap`, `paint_hexbin`, `paint_sankey`, el grafo force-directed
+//! de `pineal-mesh` y los candlesticks de `pineal-financial`) sobre el
+//! `Canvas` de `pineal-render` (backend vello/wgpu de Llimphi).
+//!
+//! El render es **headless y determinista** (sin reloj, sin runtime, sin
+//! winit): frame `i` de `N` → `t = i/(N-1)` → `vello::Scene` → wgpu → PNG.
+//! El stage central cruza de un gráfico al siguiente con **cross-fade**
+//! (capas vello con alpha), y los datos de cada gráfico se derivan de su
+//! progreso local para que *animen* mientras están en pantalla. Cold-open
+//! sobrio (trazo bezier draw-on) + wordmark final **"pineal"** con
+//! subtítulo "data visualization, in Rust".
+//!
+//! ```text
+//! cargo run -p pineal-galeria-demo --example pineal_showreel --release -- \
+//!     [out_dir] [n_frames] [W] [H]
+//! ```
+//! Defaults: `out_dir=showreel_frames_pineal`, `n_frames=300`, `W=1600`, `H=900`.
+#![allow(clippy::too_many_arguments)]
+
+use std::fs::{create_dir_all, File};
+use std::io::BufWriter;
+
+use llimphi_ui::llimphi_hal::{wgpu, Hal};
+use llimphi_ui::llimphi_raster::kurbo::{Affine, BezPath, Circle, Point as KPoint, Stroke};
+use llimphi_ui::llimphi_raster::peniko::{self, Color as PColor, Fill, Gradient};
+use llimphi_ui::llimphi_raster::{vello, Renderer};
+use llimphi_ui::llimphi_text::{
+    draw_layout_brush_xf, measurement, Alignment, Typesetter,
+};
+
+use pineal_bars::{paint_bars, Bar, BarStyle, Histogram};
+use pineal_cartesian::{ChartViewport, CoordinateSystem};
+use pineal_contour::paint_contours;
+use pineal_financial::{paint_candlesticks, CandlestickStyle, OhlcBuffer};
+use pineal_flow::{compute_layout, paint_sankey, SankeyLink, SankeyNode};
+use pineal_heatmap::{paint as paint_heatmap, HeatmapMatrix, Ramp};
+use pineal_mesh::{EdgeBuffer, ForceLayout, ForceParams, NodeBuffer};
+use pineal_polar::{paint_pie, paint_radar, Slice};
+use pineal_render::{Canvas as _, Color, Point, Rect, SceneCanvas, StrokeStyle};
+use pineal_treemap::{paint_treemap, Tile};
+
+const FMT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba8Unorm;
+
+// ───────────────────────── utilidades de tiempo / color ─────────────────────────
+
+/// Reescala `t` desde el subintervalo `[lo,hi]` a `[0,1]`, clampado.
+fn seg(t: f32, lo: f32, hi: f32) -> f32 {
+    ((t - lo) / (hi - lo)).clamp(0.0, 1.0)
+}
+
+fn lerp(a: f64, b: f64, t: f64) -> f64 {
+    a + (b - a) * t
+}
+
+fn ease_in_out_cubic(t: f32) -> f32 {
+    if t < 0.5 {
+        4.0 * t * t * t
+    } else {
+        let f = 2.0 * t - 2.0;
+        0.5 * f * f * f + 1.0
+    }
+}
+
+fn ease_out_cubic(t: f32) -> f32 {
+    let f = t - 1.0;
+    f * f * f + 1.0
+}
+
+fn ease_out_back(t: f32) -> f32 {
+    let c1 = 1.70158_f32;
+    let c3 = c1 + 1.0;
+    let f = t - 1.0;
+    1.0 + c3 * f * f * f + c1 * f * f
+}
+
+/// `PColor` (peniko) con alpha sobreescrito.
+fn palpha(c: PColor, a: f32) -> PColor {
+    let [r, g, b, _] = c.components;
+    PColor::new([r, g, b, a.clamp(0.0, 1.0)])
+}
+
+// ───────────────────────── paleta del showreel ─────────────────────────
+
+struct Skin {
+    bg: PColor,
+    panel_hi: PColor,
+    panel_lo: PColor,
+    border: PColor,
+    accent: PColor,
+    fg: PColor,
+    fg_muted: PColor,
+    /// Fondo del área de plot dentro del stage (oscuro como en la galería).
+    plot_bg: Color,
+}
+
+fn skin() -> Skin {
+    Skin {
+        bg: PColor::from_rgba8(0x0C, 0x0E, 0x12, 0xFF),
+        panel_hi: PColor::from_rgba8(0x18, 0x1D, 0x26, 0xFF),
+        panel_lo: PColor::from_rgba8(0x10, 0x14, 0x1B, 0xFF),
+        border: PColor::from_rgba8(0x2A, 0x31, 0x3E, 0xFF),
+        accent: PColor::from_rgba8(0x2B, 0xD9, 0xA6, 0xFF), // teal firma
+        fg: PColor::from_rgba8(0xE8, 0xEC, 0xF2, 0xFF),
+        fg_muted: PColor::from_rgba8(0x8A, 0x93, 0xA3, 0xFF),
+        plot_bg: Color::rgba(0.043, 0.055, 0.078, 1.0),
+    }
+}
+
+// ───────────────────────── stage central ─────────────────────────
+
+/// Rect (pineal) interior del stage donde pinta el painter, con padding.
+fn plot_rect_of(stage: KurboRectLite, pad: f64) -> Rect {
+    Rect::new(
+        (stage.x + pad) as f32,
+        (stage.y + pad) as f32,
+        (stage.w - 2.0 * pad) as f32,
+        (stage.h - 2.0 * pad) as f32,
+    )
+}
+
+#[derive(Clone, Copy)]
+struct KurboRectLite {
+    x: f64,
+    y: f64,
+    w: f64,
+    h: f64,
+}
+
+// ───────────────────────── los 8 beats: cada uno un painter REAL ─────────────────────────
+//
+// `lp ∈ [0,1]` = progreso local del beat (para animar datos). `area` = rect
+// de plot (coords pineal). Todos hablan contra el trait `Canvas` vía
+// `SceneCanvas`. El fondo del plot ya está pintado por el caller.
+
+const N_BEATS: usize = 8;
+
+fn beat_label(i: usize) -> (&'static str, &'static str) {
+    match i {
+        0 => ("bars", "columnas + histograma"),
+        1 => ("heatmap", "campo escalar · Viridis"),
+        2 => ("contour", "marching squares · 8 niveles"),
+        3 => ("polar", "pie / donut + radar"),
+        4 => ("treemap", "squarified"),
+        5 => ("financial", "candlesticks OHLC"),
+        6 => ("flow", "diagrama Sankey"),
+        _ => ("mesh", "grafo force-directed"),
+    }
+}
+
+/// Despacha el painter del beat `i` con progreso local `lp`.
+fn paint_beat(i: usize, lp: f32, canvas: &mut SceneCanvas<'_>, area: Rect, plotbg: Color) {
+    canvas.fill_rect(area, plotbg);
+    match i {
+        0 => beat_bars(lp, canvas, area),
+        1 => beat_heatmap(lp, canvas, area),
+        2 => beat_contour(lp, canvas, area),
+        3 => beat_polar(lp, canvas, area),
+        4 => beat_treemap(lp, canvas, area),
+        5 => beat_financial(lp, canvas, area),
+        6 => beat_sankey(lp, canvas, area),
+        _ => beat_mesh(lp, canvas, area),
+    }
+}
+
+/// 0 — bars: barras a la izquierda (con un negativo) + histograma a la
+/// derecha. Las alturas crecen desde el baseline con `lp`.
+fn beat_bars(lp: f32, canvas: &mut SceneCanvas<'_>, area: Rect) {
+    let g = ease_out_cubic(seg(lp, 0.0, 0.85));
+    let raw = [4.0, 7.0, 2.0, -3.0, 5.0, 6.5, 3.5, 5.8];
+    let bars: Vec<Bar> = raw
+        .iter()
+        .map(|&v| {
+            let c = if v < 0.0 { 0xd08770 } else { 0x2bd9a6 };
+            Bar::new(v * g as f64, Color::from_hex(c))
+        })
+        .collect();
+    let half = area.w * 0.5 - 12.0;
+    let left = Rect::new(area.x + 8.0, area.y + 8.0, half - 8.0, area.h - 16.0);
+    paint_bars(&bars, left, &BarStyle::vertical().with_gap(0.18), canvas);
+
+    // Histograma (gaussiana de un LCG sembrado), altura escalada por lp.
+    let mut rng: u32 = 0x0BAD_F00D;
+    let mut next = || {
+        rng = rng.wrapping_mul(1_664_525).wrapping_add(1_013_904_223);
+        (rng >> 8) as f32 / (1u32 << 24) as f32
+    };
+    let mut sample = Vec::with_capacity(4000);
+    for _ in 0..4000 {
+        let gg: f32 = (0..6).map(|_| next()).sum::<f32>() / 6.0;
+        sample.push((gg - 0.5) * 6.0);
+    }
+    let mut hbars = Histogram::new(&sample, 28).to_bars(Color::from_hex(0xb48ead));
+    for b in hbars.iter_mut() {
+        b.value *= g as f64;
+    }
+    let right = Rect::new(area.x + half + 16.0, area.y + 8.0, half - 8.0, area.h - 16.0);
+    paint_bars(&hbars, right, &BarStyle::vertical().with_gap(0.04), canvas);
+}
+
+/// Campo escalar 64×40 animado por `lp` (la fase de las ondas avanza).
+fn animated_field(lp: f32) -> HeatmapMatrix {
+    const W: usize = 64;
+    const H: usize = 40;
+    let ph = lp * std::f32::consts::TAU * 0.5;
+    let mut m = HeatmapMatrix::new(W, H);
+    let mut data = Vec::with_capacity(W * H);
+    for y in 0..H {
+        for x in 0..W {
+            let v = (x as f32 * 0.22 + ph).sin() + (y as f32 * 0.26 - ph * 0.7).cos()
+                + 0.6 * ((x as f32 * 0.1).sin() * (y as f32 * 0.12).cos());
+            data.push(v);
+        }
+    }
+    m.replace_data(data);
+    m
+}
+
+/// 1 — heatmap Viridis del campo animado.
+fn beat_heatmap(lp: f32, canvas: &mut SceneCanvas<'_>, area: Rect) {
+    let field = animated_field(lp);
+    paint_heatmap(&field, Ramp::Viridis, area, canvas);
+}
+
+/// 2 — contour: el mismo campo + isolíneas (marching squares). El nº de
+/// niveles sube de 3 a 9 con `lp` (las curvas "aparecen").
+fn beat_contour(lp: f32, canvas: &mut SceneCanvas<'_>, area: Rect) {
+    let field = animated_field(lp * 0.6);
+    paint_heatmap(&field, Ramp::Viridis, area, canvas);
+    let levels = (3.0 + 6.0 * ease_out_cubic(seg(lp, 0.0, 0.9))) as usize;
+    paint_contours(
+        &field,
+        levels.max(2),
+        area,
+        Color::rgba(0.35, 0.62, 1.0, 0.9),
+        Color::rgba(1.0, 0.42, 0.32, 0.95),
+        1.3,
+        canvas,
+    );
+}
+
+/// 3 — polar: donut a la izquierda (barrido por lp) + radar a la derecha
+/// (vértices que crecen). Dos painters de pineal-polar en un beat.
+fn beat_polar(lp: f32, canvas: &mut SceneCanvas<'_>, area: Rect) {
+    let g = ease_out_cubic(seg(lp, 0.0, 0.85));
+    // Donut.
+    let cx = area.x + area.w * 0.27;
+    let cy = area.y + area.h * 0.5;
+    let r_out = (area.w.min(area.h) * 0.34).max(20.0);
+    let r_in = r_out * 0.5;
+    let base = [28.0_f32, 18.0, 14.0, 12.0, 10.0, 8.0];
+    let pal = [0x2bd9a6, 0xd08770, 0xa3be8c, 0xebcb8b, 0xb48ead, 0x5e81ac];
+    let slices: Vec<Slice> = base
+        .iter()
+        .zip(pal.iter())
+        .map(|(&v, &c)| Slice::new(v * g, Color::from_hex(c)))
+        .collect();
+    if g > 0.01 {
+        paint_pie(&slices, Point::new(cx, cy), r_out, r_in, canvas);
+    }
+    // Radar.
+    let rcx = area.x + area.w * 0.73;
+    let rcy = cy;
+    let rr = (area.w.min(area.h) * 0.34).max(20.0);
+    for step in 1..=4 {
+        let t = step as f32 / 4.0;
+        let ring: Vec<f32> = (0..=72)
+            .flat_map(|k| {
+                let a = (k as f32 / 72.0) * std::f32::consts::TAU - std::f32::consts::FRAC_PI_2;
+                [rcx + (rr * t) * a.cos(), rcy + (rr * t) * a.sin()]
+            })
+            .collect();
+        canvas.stroke_polyline(&ring, StrokeStyle::new(0.6, Color::rgba(0.5, 0.56, 0.66, 0.35)));
+    }
+    let values: Vec<f32> = [8.0_f32, 6.5, 9.0, 4.0, 7.0, 5.5].iter().map(|v| v * g).collect();
+    paint_radar(
+        &values,
+        10.0,
+        Point::new(rcx, rcy),
+        rr,
+        Color::rgba(0.169, 0.851, 0.651, 0.30),
+        StrokeStyle::new(1.8, Color::from_hex(0x2bd9a6)),
+        canvas,
+    );
+}
+
+/// 4 — treemap squarified: 14 tiles, los pesos respiran con `lp`.
+fn beat_treemap(lp: f32, canvas: &mut SceneCanvas<'_>, area: Rect) {
+    let pal = [
+        0x2bd9a6, 0xd08770, 0xa3be8c, 0xebcb8b, 0xb48ead, 0x5e81ac, 0x81a1c1, 0xbf616a,
+        0x8fbcbb, 0xd8dee9, 0x88c0d0, 0xebcb8b, 0xa3be8c, 0x5e81ac,
+    ];
+    let base = [40.0, 28.0, 22.0, 18.0, 14.0, 12.0, 10.0, 8.0, 6.0, 5.0, 4.0, 3.5, 3.0, 2.0];
+    let wob = lp * std::f32::consts::TAU;
+    let tiles: Vec<Tile> = base
+        .iter()
+        .zip(pal.iter())
+        .enumerate()
+        .map(|(k, (&w, &c))| {
+            let m = 1.0 + 0.25 * (wob + k as f32 * 0.6).sin();
+            Tile::new((w * m) as f64, Color::from_hex(c))
+        })
+        .collect();
+    let inset = Rect::new(area.x + 6.0, area.y + 6.0, area.w - 12.0, area.h - 12.0);
+    paint_treemap(&tiles, inset, 3.0, canvas);
+}
+
+/// 5 — financial: candlesticks OHLC (random-walk determinista). El nº de
+/// velas visibles crece con `lp` (se van "imprimiendo" de izq a der).
+fn beat_financial(lp: f32, canvas: &mut SceneCanvas<'_>, area: Rect) {
+    const N: usize = 64;
+    let mut buf = OhlcBuffer::with_capacity(N);
+    let mut rng: u32 = 0xCAFE_1234;
+    let mut next = || {
+        rng = rng.wrapping_mul(1_664_525).wrapping_add(1_013_904_223);
+        ((rng >> 8) as f32 / (1u32 << 24) as f32) - 0.5
+    };
+    let mut price = 100.0_f32;
+    let mut lo_all = f32::MAX;
+    let mut hi_all = f32::MIN;
+    let shown = ((N as f32) * ease_out_cubic(seg(lp, 0.0, 0.95))).round() as usize;
+    let shown = shown.clamp(2, N);
+    for i in 0..shown {
+        let o = price;
+        let drift = next() * 4.0 + 0.25 * (i as f32 * 0.3).sin();
+        let c = (o + drift).max(5.0);
+        let hi = o.max(c) + next().abs() * 3.0;
+        let lo = o.min(c) - next().abs() * 3.0;
+        buf.push_values(i as f32, o, hi, lo, c, 1.0);
+        price = c;
+        lo_all = lo_all.min(lo);
+        hi_all = hi_all.max(hi);
+    }
+    if hi_all <= lo_all {
+        return;
+    }
+    let pad = (hi_all - lo_all) * 0.08;
+    let vp = ChartViewport::new(
+        -1.0,
+        N as f64,
+        (lo_all - pad) as f64,
+        (hi_all + pad) as f64,
+    );
+    let plot = Rect::new(area.x + 6.0, area.y + 6.0, area.w - 12.0, area.h - 12.0);
+    let cs = CoordinateSystem::new(vp, plot);
+    let mut style = CandlestickStyle::default();
+    style.bull_color = Color::from_hex(0x2bd9a6);
+    style.bear_color = Color::from_hex(0xe06c75);
+    style.body_width_ratio = 0.68;
+    paint_candlesticks(canvas, &cs, &buf, style);
+}
+
+/// 6 — flow: diagrama Sankey de un presupuesto. Los ribbons aparecen con
+/// `lp` vía el alpha del color de link.
+fn beat_sankey(lp: f32, canvas: &mut SceneCanvas<'_>, area: Rect) {
+    let nodes: Vec<SankeyNode> = [
+        "Sueldo", "Freelance", "Renta", "Dividendos", "Vivienda", "Comida", "Transporte",
+        "Ocio", "Salud", "Ahorro",
+    ]
+    .iter()
+    .map(|n| SankeyNode::new(*n))
+    .collect();
+    let links: Vec<SankeyLink> = vec![
+        SankeyLink { source: 0, target: 4, value: 1200.0 },
+        SankeyLink { source: 0, target: 5, value: 600.0 },
+        SankeyLink { source: 0, target: 6, value: 250.0 },
+        SankeyLink { source: 0, target: 9, value: 950.0 },
+        SankeyLink { source: 1, target: 5, value: 200.0 },
+        SankeyLink { source: 1, target: 7, value: 300.0 },
+        SankeyLink { source: 1, target: 9, value: 400.0 },
+        SankeyLink { source: 2, target: 4, value: 400.0 },
+        SankeyLink { source: 2, target: 8, value: 150.0 },
+        SankeyLink { source: 3, target: 9, value: 350.0 },
+        SankeyLink { source: 3, target: 7, value: 80.0 },
+    ];
+    let inset = Rect::new(area.x + 16.0, area.y + 16.0, area.w - 32.0, area.h - 32.0);
+    let layout = compute_layout(&nodes, &links, inset, 16.0, 8.0);
+    let ribbon_a = 0.10 + 0.40 * ease_out_cubic(seg(lp, 0.0, 0.85));
+    paint_sankey(
+        &layout,
+        Color::from_hex(0xe5e9f0),
+        Color::rgba(0.169, 0.851, 0.651, ribbon_a),
+        canvas,
+    );
+}
+
+/// 7 — mesh: grafo force-directed pre-relajado, que rota lentamente con
+/// `lp` para que se note la profundidad de la maraña.
+fn beat_mesh(lp: f32, canvas: &mut SceneCanvas<'_>, area: Rect) {
+    let g = relaxed_graph();
+    let cx = area.x + area.w * 0.5;
+    let cy = area.y + area.h * 0.5;
+    let scale = (area.w.min(area.h) / 170.0).max(0.5);
+    let ang = lp * std::f32::consts::TAU * 0.15;
+    let (sa, ca) = ang.sin_cos();
+    let rot = |x: f32, y: f32| -> (f32, f32) {
+        (cx + (x * ca - y * sa) * scale, cy + (x * sa + y * ca) * scale)
+    };
+    let edge = StrokeStyle::new(1.0, Color::rgba(0.55, 0.62, 0.7, 0.4));
+    for (u, v) in g.edges.iter() {
+        let (xu, yu) = g.nodes.pos(u);
+        let (xv, yv) = g.nodes.pos(v);
+        let (ax, ay) = rot(xu, yu);
+        let (bx, by) = rot(xv, yv);
+        canvas.stroke_line(Point::new(ax, ay), Point::new(bx, by), edge);
+    }
+    let n = g.nodes.len();
+    for i in 0..n {
+        let (x, y) = g.nodes.pos(i);
+        let r = g.nodes.radius(i) * scale;
+        let (px, py) = rot(x, y);
+        let color = if i < N_RING { Color::from_hex(0x2bd9a6) } else { Color::from_hex(0xa3be8c) };
+        canvas.fill_rect(Rect::new(px - r, py - r, r * 2.0, r * 2.0), color);
+    }
+}
+
+// ── grafo del beat mesh (mismo armado que pineal-mesh-demo / la galería) ──
+
+const N_RING: usize = 12;
+const N_SAT: usize = 12;
+
+struct Graph {
+    nodes: NodeBuffer,
+    edges: EdgeBuffer,
+}
+
+fn relaxed_graph() -> Graph {
+    let mut nodes = NodeBuffer::new();
+    for i in 0..N_RING {
+        let a = (i as f32 / N_RING as f32) * std::f32::consts::TAU;
+        nodes.push(20.0 * a.cos(), 20.0 * a.sin(), 6.0);
+    }
+    for i in 0..N_SAT {
+        let a = (i as f32 / N_SAT as f32) * std::f32::consts::TAU + 0.13;
+        nodes.push(60.0 * a.cos(), 60.0 * a.sin(), 4.5);
+    }
+    let mut edges = EdgeBuffer::new();
+    for i in 0..N_RING {
+        edges.push(i, (i + 1) % N_RING);
+    }
+    for i in 0..N_RING {
+        edges.push(i, (i + 3) % N_RING);
+    }
+    for i in 0..N_SAT {
+        edges.push(i, N_RING + i);
+    }
+    let mut sim = ForceLayout::new(ForceParams { k: 38.0, temperature: 60.0, cooling: 0.985 });
+    for _ in 0..400 {
+        let _ = sim.step(&mut nodes, &edges);
+    }
+    Graph { nodes, edges }
+}
+
+// ───────────────────────── overlays vector (cold-open + wordmark + chrome) ─────────────────────────
+
+fn signature_path(cw: f64, ch: f64) -> BezPath {
+    let cx = cw / 2.0;
+    let cy = ch / 2.0;
+    let mut p = BezPath::new();
+    p.move_to((cx - 380.0, cy + 30.0));
+    p.curve_to(
+        (cx - 150.0, cy - 200.0),
+        (cx + 150.0, cy + 200.0),
+        (cx + 380.0, cy - 30.0),
+    );
+    p
+}
+
+fn trim_path(full: &BezPath, prog: f64) -> (BezPath, KPoint) {
+    use vello::kurbo::ParamCurve;
+    let prog = prog.clamp(0.0, 1.0);
+    let mut cubic = None;
+    let mut start = KPoint::ZERO;
+    for el in full.elements() {
+        match el {
+            vello::kurbo::PathEl::MoveTo(p) => start = *p,
+            vello::kurbo::PathEl::CurveTo(c1, c2, p) => {
+                cubic = Some(vello::kurbo::CubicBez::new(start, *c1, *c2, *p));
+            }
+            _ => {}
+        }
+    }
+    let mut out = BezPath::new();
+    let mut head = start;
+    if let Some(cb) = cubic {
+        out.move_to(cb.p0);
+        let steps = 96;
+        for i in 1..=steps {
+            let u = (i as f64 / steps as f64) * prog;
+            let pt = cb.eval(u);
+            out.line_to(pt);
+            head = pt;
+        }
+    }
+    (out, head)
+}
+
+// ───────────────────────── la escena por frame ─────────────────────────
+
+fn render_frame(scene: &mut vello::Scene, ts: &mut Typesetter, t: f32, cw: f64, ch: f64, s: &Skin) {
+    // Fondo (gradiente sutil).
+    let bg_rect = vello::kurbo::Rect::new(0.0, 0.0, cw, ch);
+    let grad = Gradient::new_linear(KPoint::new(0.0, 0.0), KPoint::new(0.0, ch))
+        .with_stops([s.panel_lo, s.bg].as_slice());
+    scene.fill(Fill::NonZero, Affine::IDENTITY, &grad, None, &bg_rect);
+
+    // Geometría del stage central (gran panel donde viven los gráficos).
+    let margin_x = 200.0_f64;
+    let stage_top = 178.0_f64;
+    let stage_bottom = 120.0_f64;
+    let stage = KurboRectLite {
+        x: margin_x,
+        y: stage_top,
+        w: cw - 2.0 * margin_x,
+        h: ch - stage_top - stage_bottom,
+    };
+
+    // Aparición del chrome (panel) en el cold-open; desaparición antes del wordmark.
+    let chrome_in = ease_out_cubic(seg(t, 0.06, 0.16));
+    let chrome_out = 1.0 - ease_in_out_cubic(seg(t, 0.86, 0.93));
+    let chrome_a = (chrome_in * chrome_out).clamp(0.0, 1.0);
+
+    // Ventana de los beats en la timeline.
+    let beats_lo = 0.12_f32;
+    let beats_hi = 0.86_f32;
+
+    if chrome_a > 0.001 {
+        // Panel del stage (card con borde + gradiente + sombra simulada).
+        let r = vello::kurbo::RoundedRect::new(
+            stage.x,
+            stage.y,
+            stage.x + stage.w,
+            stage.y + stage.h,
+            18.0,
+        );
+        // Sombra simple.
+        let shadow = vello::kurbo::RoundedRect::new(
+            stage.x,
+            stage.y + 10.0,
+            stage.x + stage.w,
+            stage.y + stage.h + 10.0,
+            18.0,
+        );
+        scene.fill(Fill::NonZero, Affine::IDENTITY, palpha(PColor::BLACK, 0.45 * chrome_a), None, &shadow);
+        let pgrad = Gradient::new_linear(KPoint::new(0.0, stage.y), KPoint::new(0.0, stage.y + stage.h))
+            .with_stops([palpha(s.panel_hi, chrome_a), palpha(s.panel_lo, chrome_a)].as_slice());
+        scene.fill(Fill::NonZero, Affine::IDENTITY, &pgrad, None, &r);
+        scene.stroke(&Stroke::new(1.2), Affine::IDENTITY, palpha(s.border, chrome_a), None, &r);
+
+        // ── beats: cross-fade entre gráfico saliente y entrante ──
+        let phase = seg(t, beats_lo, beats_hi); // 0..1 sobre todos los beats
+        let scaled = phase * N_BEATS as f32;
+        let cur = (scaled.floor() as usize).min(N_BEATS - 1);
+        let frac = scaled - cur as f32; // 0..1 dentro del beat actual
+        // Cross-fade en el último 22% de cada beat hacia el siguiente.
+        let xf = seg(frac, 0.78, 1.0);
+
+        let plot = plot_rect_of(stage, 18.0);
+        let clip = vello::kurbo::RoundedRect::new(
+            plot.x as f64,
+            plot.y as f64,
+            (plot.x + plot.w) as f64,
+            (plot.y + plot.h) as f64,
+            10.0,
+        );
+
+        // Progreso local "vivo" del beat actual: 0..1 a lo largo de su tramo.
+        let lp_cur = frac;
+        // Gráfico actual (se desvanece durante el cross-fade final).
+        let a_cur = (1.0 - xf) * chrome_a;
+        if a_cur > 0.004 {
+            scene.push_layer(Fill::NonZero, peniko::Mix::Normal, a_cur, Affine::IDENTITY, &clip);
+            {
+                let mut canvas = SceneCanvas::new(scene, ts);
+                paint_beat(cur, lp_cur, &mut canvas, plot, s.plot_bg);
+            }
+            scene.pop_layer();
+        }
+        // Gráfico entrante (aparece durante el cross-fade).
+        if xf > 0.004 && cur + 1 < N_BEATS {
+            let a_next = xf * chrome_a;
+            scene.push_layer(Fill::NonZero, peniko::Mix::Normal, a_next, Affine::IDENTITY, &clip);
+            {
+                let mut canvas = SceneCanvas::new(scene, ts);
+                // El entrante arranca su animación desde 0.
+                paint_beat(cur + 1, 0.0, &mut canvas, plot, s.plot_bg);
+            }
+            scene.pop_layer();
+        }
+
+        // ── chrome textual: título del beat + subtítulo + barra de progreso ──
+        let (name, subtitle) = beat_label(cur);
+        let head_a = chrome_a * (1.0 - seg(frac, 0.86, 1.0)); // pequeño parpadeo al cambiar
+        let head_a = head_a.max(chrome_a * 0.0);
+        // Título arriba del stage.
+        let tl = ts.layout(name, 30.0, None, Alignment::Start, 1.0, false, None, 800.0, false, false, 0.0, 0.0);
+        let m = measurement(&tl);
+        let tx = stage.x + 6.0;
+        let ty = stage.y - 48.0;
+        // Punto teal de acento a la izquierda del título.
+        scene.fill(
+            Fill::NonZero,
+            Affine::IDENTITY,
+            palpha(s.accent, head_a),
+            None,
+            &Circle::new(KPoint::new(tx + 6.0, ty + m.height as f64 * 0.5), 6.0),
+        );
+        let brush = peniko::Brush::Solid(palpha(s.fg, head_a));
+        draw_layout_brush_xf(scene, &tl, &brush, Affine::translate((tx + 22.0, ty)));
+        // Subtítulo a la derecha del título.
+        let sl = ts.layout(subtitle, 15.0, None, Alignment::Start, 1.0, false, None, 400.0, false, false, 0.0, 0.0);
+        let sbrush = peniko::Brush::Solid(palpha(s.fg_muted, head_a));
+        draw_layout_brush_xf(
+            scene,
+            &sl,
+            &sbrush,
+            Affine::translate((tx + 28.0 + m.width as f64, ty + 12.0)),
+        );
+
+        // Barra de progreso de la timeline (debajo del stage).
+        let bar_y = stage.y + stage.h + 30.0;
+        let bar_x = stage.x;
+        let bar_w = stage.w;
+        let track = vello::kurbo::RoundedRect::new(bar_x, bar_y, bar_x + bar_w, bar_y + 4.0, 2.0);
+        scene.fill(Fill::NonZero, Affine::IDENTITY, palpha(s.border, chrome_a), None, &track);
+        let fillw = bar_w * phase as f64;
+        if fillw > 1.0 {
+            let fr = vello::kurbo::RoundedRect::new(bar_x, bar_y, bar_x + fillw, bar_y + 4.0, 2.0);
+            scene.fill(Fill::NonZero, Affine::IDENTITY, palpha(s.accent, chrome_a), None, &fr);
+        }
+        // Marcas de los beats (tics).
+        for k in 0..=N_BEATS {
+            let mx = bar_x + bar_w * (k as f64 / N_BEATS as f64);
+            let on = (k as f32 / N_BEATS as f32) <= phase + 0.001;
+            let col = if on { s.accent } else { s.border };
+            scene.fill(
+                Fill::NonZero,
+                Affine::IDENTITY,
+                palpha(col, chrome_a),
+                None,
+                &Circle::new(KPoint::new(mx, bar_y + 2.0), 3.0),
+            );
+        }
+
+        // Wordmark pequeño persistente arriba a la izquierda (marca).
+        let wm = ts.layout("pineal", 22.0, None, Alignment::Start, 1.0, false, None, 800.0, false, false, 0.0, 0.0);
+        let wmb = peniko::Brush::Solid(palpha(s.fg, chrome_a * 0.9));
+        draw_layout_brush_xf(scene, &wm, &wmb, Affine::translate((margin_x, 60.0)));
+        let tagl = ts.layout(
+            "data visualization, in Rust",
+            13.0, None, Alignment::Start, 1.0, false, None, 400.0, false, false, 0.0, 0.0,
+        );
+        let tagb = peniko::Brush::Solid(palpha(s.fg_muted, chrome_a * 0.9));
+        draw_layout_brush_xf(scene, &tagl, &tagb, Affine::translate((margin_x + 86.0, 68.0)));
+    }
+
+    // ── COLD OPEN: trazo bezier draw-on (0–14%) ──
+    let line_vis = 1.0 - seg(t, 0.13, 0.20);
+    if line_vis > 0.001 && t < 0.21 {
+        let path = signature_path(cw, ch);
+        let draw_on = ease_out_cubic(seg(t, 0.0, 0.12)) as f64;
+        let (trimmed, head) = trim_path(&path, draw_on);
+        scene.stroke(
+            &Stroke::new(2.2),
+            Affine::IDENTITY,
+            palpha(s.accent, 0.9 * line_vis),
+            None,
+            &trimmed,
+        );
+        let pop = ease_out_back(seg(t, 0.0, 0.1));
+        let r = (4.0 + 7.0 * pop as f64).max(0.0);
+        let dot_a = (seg(t, 0.0, 0.1) * line_vis).clamp(0.0, 1.0);
+        scene.fill(Fill::NonZero, Affine::IDENTITY, palpha(s.accent, 0.18 * dot_a), None, &Circle::new(head, r * 3.2));
+        scene.fill(Fill::NonZero, Affine::IDENTITY, palpha(s.accent, dot_a), None, &Circle::new(head, r));
+    }
+
+    // ── WORDMARK final (88–100%) ──
+    let word_a = ease_out_cubic(seg(t, 0.90, 0.98));
+    if word_a > 0.001 {
+        let size = 150.0_f32;
+        let layout = ts.layout("pineal", size, None, Alignment::Start, 1.0, false, None, 800.0, false, false, 0.0, 0.0);
+        let m = measurement(&layout);
+        let rise = lerp(28.0, 0.0, word_a as f64);
+        let ox = (cw - m.width as f64) / 2.0;
+        let oy = (ch - m.height as f64) / 2.0 - 24.0 + rise;
+        let brush = peniko::Brush::Solid(palpha(s.fg, word_a));
+        draw_layout_brush_xf(scene, &layout, &brush, Affine::translate((ox, oy)));
+
+        let sub_a = ease_out_cubic(seg(t, 0.93, 1.0));
+        if sub_a > 0.001 {
+            let sy = oy + m.height as f64 + 16.0;
+            // Subtítulo con punto teal a la izquierda.
+            let sl = ts.layout(
+                "data visualization, in Rust",
+                28.0, None, Alignment::Start, 1.0, false, None, 400.0, false, false, 0.0, 0.0,
+            );
+            let sm = measurement(&sl);
+            let dot_r = 6.0;
+            let block_w = sm.width as f64 + dot_r * 2.0 + 14.0;
+            let sx = (cw - block_w) / 2.0;
+            scene.fill(
+                Fill::NonZero,
+                Affine::IDENTITY,
+                palpha(s.accent, sub_a),
+                None,
+                &Circle::new(KPoint::new(sx + dot_r, sy + 28.0 * 0.5), dot_r),
+            );
+            let sbrush = peniko::Brush::Solid(palpha(s.fg_muted, sub_a));
+            draw_layout_brush_xf(scene, &sl, &sbrush, Affine::translate((sx + dot_r * 2.0 + 14.0, sy)));
+        }
+    }
+}
+
+// ───────────────────────── main: N frames headless → PNG ─────────────────────────
+
+fn main() {
+    let mut args = std::env::args().skip(1);
+    let out_dir = args.next().unwrap_or_else(|| "showreel_frames_pineal".to_string());
+    let n: usize = args.next().and_then(|v| v.parse().ok()).unwrap_or(300);
+    let w: u32 = args.next().and_then(|v| v.parse().ok()).unwrap_or(1600);
+    let h: u32 = args.next().and_then(|v| v.parse().ok()).unwrap_or(900);
+    create_dir_all(&out_dir).expect("mkdir out_dir");
+
+    let s = skin();
+    let [br, bgc, bb, _] = s.bg.components;
+    let base = PColor::from_rgba8((br * 255.0) as u8, (bgc * 255.0) as u8, (bb * 255.0) as u8, 255);
+
+    // GPU una sola vez; reusar device/renderer/target para los N frames.
+    let hal = pollster::block_on(Hal::new(None)).expect("hal");
+    let mut renderer = Renderer::new(&hal).expect("renderer");
+    let target = hal.device.create_texture(&wgpu::TextureDescriptor {
+        label: Some("showreel-pineal"),
+        size: wgpu::Extent3d { width: w, height: h, depth_or_array_layers: 1 },
+        mip_level_count: 1,
+        sample_count: 1,
+        dimension: wgpu::TextureDimension::D2,
+        format: FMT,
+        usage: wgpu::TextureUsages::STORAGE_BINDING
+            | wgpu::TextureUsages::RENDER_ATTACHMENT
+            | wgpu::TextureUsages::COPY_SRC,
+        view_formats: &[],
+    });
+    let view = target.create_view(&wgpu::TextureViewDescriptor::default());
+    let mut ts = Typesetter::new();
+    let cw = w as f64;
+    let ch = h as f64;
+
+    for i in 0..n {
+        let t = if n <= 1 { 0.0 } else { i as f32 / (n as f32 - 1.0) };
+        let mut scene = vello::Scene::new();
+        render_frame(&mut scene, &mut ts, t, cw, ch, &s);
+        renderer
+            .render_to_view(&hal, &scene, &view, w, h, base)
+            .expect("render_to_view");
+        let path = format!("{out_dir}/frame_{i:04}.png");
+        write_png(&hal, &target, &path, w, h);
+        if i % 30 == 0 || i == n - 1 {
+            eprintln!("pineal_showreel: frame {}/{} (t={:.3})", i + 1, n, t);
+        }
+    }
+    eprintln!("pineal_showreel: {n} frames en {out_dir}/ ({w}x{h})");
+}
+
+fn write_png(hal: &Hal, target: &wgpu::Texture, path: &str, w: u32, h: u32) {
+    let unpadded = (w * 4) as usize;
+    let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT as usize;
+    let padded = unpadded.div_ceil(align) * align;
+    let buf = hal.device.create_buffer(&wgpu::BufferDescriptor {
+        label: Some("readback"),
+        size: (padded * h as usize) as u64,
+        usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
+        mapped_at_creation: false,
+    });
+    let mut enc = hal
+        .device
+        .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
+    enc.copy_texture_to_buffer(
+        wgpu::TexelCopyTextureInfo {
+            texture: target,
+            mip_level: 0,
+            origin: wgpu::Origin3d::ZERO,
+            aspect: wgpu::TextureAspect::All,
+        },
+        wgpu::TexelCopyBufferInfo {
+            buffer: &buf,
+            layout: wgpu::TexelCopyBufferLayout {
+                offset: 0,
+                bytes_per_row: Some(padded as u32),
+                rows_per_image: Some(h),
+            },
+        },
+        wgpu::Extent3d { width: w, height: h, depth_or_array_layers: 1 },
+    );
+    hal.queue.submit(std::iter::once(enc.finish()));
+    let slice = buf.slice(..);
+    let (tx, rx) = std::sync::mpsc::channel();
+    slice.map_async(wgpu::MapMode::Read, move |r| {
+        let _ = tx.send(r);
+    });
+    let _ = hal.device.poll(wgpu::PollType::wait_indefinitely());
+    rx.recv().unwrap().unwrap();
+    let data = slice.get_mapped_range();
+    let mut pixels = Vec::with_capacity((w * h * 4) as usize);
+    for r in 0..h as usize {
+        let sidx = r * padded;
+        pixels.extend_from_slice(&data[sidx..sidx + unpadded]);
+    }
+    drop(data);
+    buf.unmap();
+    let file = File::create(path).expect("png");
+    let mut enc = png::Encoder::new(BufWriter::new(file), w, h);
+    enc.set_color(png::ColorType::Rgba);
+    enc.set_depth(png::BitDepth::Eight);
+    let mut wr = enc.write_header().unwrap();
+    wr.write_image_data(&pixels).unwrap();
+}
diff --git a/00_unanchay/pineal/demos/pineal-galeria-demo/src/main.rs b/00_unanchay/pineal/demos/pineal-galeria-demo/src/main.rs
index aff5954..0284292 100644
--- a/00_unanchay/pineal/demos/pineal-galeria-demo/src/main.rs
+++ b/00_unanchay/pineal/demos/pineal-galeria-demo/src/main.rs
@@ -43,8 +43,11 @@ use pineal_treemap::{paint_treemap, Tile};
 // Modelo y mensajes
 // =====================================================================
 
+// `pub(crate)` (aquí y en `Model`/`GaleriaDemo`): el example
+// `pantallazo_pineal` incluye este archivo por `#[path]` para montar la
+// MISMA view de la galería sin ventana. Para el binario no cambia nada.
 #[derive(Clone)]
-enum Msg {
+pub(crate) enum Msg {
     /// Barra de menú principal: abrir/cerrar un menú raíz (`None` cierra).
     MenuOpen(Option<usize>),
     /// Comando elegido en la barra → se traduce al `Msg` real.
@@ -53,7 +56,7 @@ enum Msg {
     CycleTheme,
 }
 
-struct Model {
+pub(crate) struct Model {
     theme: Theme,
     menu_open: Option<usize>,
     /// Grafo del tile mesh, ya relajado en el init (posiciones fijas).
@@ -64,7 +67,7 @@ struct Model {
     field: Arc<HeatmapMatrix>,
 }
 
-struct GaleriaDemo;
+pub(crate) struct GaleriaDemo;
 
 impl App for GaleriaDemo {
     type Model = Model;
diff --git a/00_unanchay/pineal/demos/pineal-gpu-demo/LEEME.md b/00_unanchay/pineal/demos/pineal-gpu-demo/LEEME.md
new file mode 100644
index 0000000..dfcb110
--- /dev/null
+++ b/00_unanchay/pineal/demos/pineal-gpu-demo/LEEME.md
@@ -0,0 +1,43 @@
+# pineal-gpu-demo
+
+*Read this in English: [README.md](README.md).*
+
+Starfield warp 3D sobre el camino GPU directo.
+
+Es el **primer consumidor real de `GpuSceneCanvas`** (Fase 4 del SDD
+de pineal). El resto del catálogo sólo tenía el backend GPU
+documentado y testeado; aquí se pinta de verdad: el demo emite hasta
+1 M de `fill_rect` por frame contra el trait `Canvas`, pero detrás
+del trait está `GpuSceneCanvas` empujando a un `GpuBatch` que despacha
+todo en **una sola draw call instanciada** (P3 del SDD).
+
+Reparto de responsabilidades (Elm + el split CPU/GPU del SDD):
+
+- `update(Tick)` avanza la simulación en CPU: cada estrella vuela
+  hacia el observador (`z -= speed`) y se recicla al fondo al cruzar
+  el plano cercano. Estado en un `Vec<f32>` plano interleaved
+  `[x,y,z, ...]`, mutado in-place (P1 + P2: zero boxing, zero alloc
+  en hot path).
+- `gpu_paint_with(...)` proyecta perspectiva y dibuja: lee el campo
+  de estrellas, calcula el pixel de cada una contra el rect del nodo
+  y llama `canvas.fill_rect(...)`. Ni el demo ni el painter saben de
+  `wgpu` — sólo hablan el trait `Canvas`.
+
+El campo se comparte entre `update` (escribe) y el painter (lee) por
+`Arc<Mutex<StarField>>`. Ambos corren en el hilo de UI, así que el
+mutex nunca se disputa; el `Arc` existe sólo porque la closure GPU
+debe ser `'static` y no puede tomar prestado el `Model`.
+
+Las `GpuPipelines` (shaders + render pipelines) se compilan una vez y
+se cachean en un `OnceLock` que vive en el `Model`, no en `view()` —
+recompilarlas por frame mataría el framerate.
+
+## Uso
+
+```sh
+cargo run --release -p pineal-gpu-demo
+```
+
+---
+
+Parte de **pineal** — ver [pineal](../../LEEME.md).
diff --git a/00_unanchay/pineal/demos/pineal-gpu-demo/README.md b/00_unanchay/pineal/demos/pineal-gpu-demo/README.md
new file mode 100644
index 0000000..9ba97fc
--- /dev/null
+++ b/00_unanchay/pineal/demos/pineal-gpu-demo/README.md
@@ -0,0 +1,12 @@
+# pineal-gpu-demo
+
+A 3D starfield warp over the direct GPU path.
+
+It is the **first real consumer of `GpuSceneCanvas`** (Phase 4 of pineal's SDD).
+The rest of the catalog only had the GPU backend documented and tested; here it
+actually paints: the demo emits up to 1M `fill_rect` calls per frame against the
+`Canvas` trait, but behind it the GPU path takes over.
+
+---
+
+Part of **pineal** — see [pineal](../../README.md).
diff --git a/00_unanchay/pineal/demos/pineal-heatmap-demo/LEEME.md b/00_unanchay/pineal/demos/pineal-heatmap-demo/LEEME.md
new file mode 100644
index 0000000..cc788d9
--- /dev/null
+++ b/00_unanchay/pineal/demos/pineal-heatmap-demo/LEEME.md
@@ -0,0 +1,24 @@
+# pineal-heatmap-demo
+
+*Read this in English: [README.md](README.md).*
+
+Campo 2D con onda viajera.
+
+Matriz 48×32 que se reescribe cada 33 ms (≈ 30 Hz). El valor de
+cada celda combina dos sinusoides con desplazamiento de fase
+ligado al tick: `sin(x·0.25 - t·0.1) + cos(y·0.30 + t·0.07)`.
+El ramp Viridis mapea `[min, max]` de la matriz a color.
+
+Cableado de UI: barra de menú principal (Archivo / Ver / Ayuda) +
+menú contextual sobre el plot (right-click). Como es un canvas sin
+texto editable no hay menú Editar ni clipboard.
+
+## Uso
+
+```sh
+cargo run --release -p pineal-heatmap-demo
+```
+
+---
+
+Parte de **pineal** — ver [pineal](../../LEEME.md).
diff --git a/00_unanchay/pineal/demos/pineal-heatmap-demo/README.md b/00_unanchay/pineal/demos/pineal-heatmap-demo/README.md
new file mode 100644
index 0000000..fefd0b5
--- /dev/null
+++ b/00_unanchay/pineal/demos/pineal-heatmap-demo/README.md
@@ -0,0 +1,12 @@
+# pineal-heatmap-demo
+
+A 2D field with a travelling wave.
+
+A 48×32 matrix rewritten every 33 ms (≈ 30 Hz). Each cell's value combines two
+sinusoids with a phase shift tied to the tick:
+`sin(x·0.25 - t·0.1) + cos(y·0.30 + t·0.07)`. The Viridis ramp maps the matrix's
+`[min, max]` to colour.
+
+---
+
+Part of **pineal** — see [pineal](../../README.md).
diff --git a/00_unanchay/pineal/demos/pineal-hexbin-demo/LEEME.md b/00_unanchay/pineal/demos/pineal-hexbin-demo/LEEME.md
new file mode 100644
index 0000000..3c1f236
--- /dev/null
+++ b/00_unanchay/pineal/demos/pineal-hexbin-demo/LEEME.md
@@ -0,0 +1,24 @@
+# pineal-hexbin-demo
+
+*Read this in English: [README.md](README.md).*
+
+5 000 puntos sintéticos bineados.
+
+Generador determinista (LCG sobre `t`) que produce dos clusters
+gaussianos solapados. El hexbin revela la densidad — cada celda se
+colorea con Viridis según count. Sin animación: el chart se computa
+una vez al iniciar.
+
+Cableado de UI: barra de menú principal (Archivo / Ver / Ayuda) +
+menú contextual sobre el plot (right-click). Como es un canvas sin
+texto editable no hay menú Editar ni clipboard.
+
+## Uso
+
+```sh
+cargo run --release -p pineal-hexbin-demo
+```
+
+---
+
+Parte de **pineal** — ver [pineal](../../LEEME.md).
diff --git a/00_unanchay/pineal/demos/pineal-hexbin-demo/README.md b/00_unanchay/pineal/demos/pineal-hexbin-demo/README.md
new file mode 100644
index 0000000..1052396
--- /dev/null
+++ b/00_unanchay/pineal/demos/pineal-hexbin-demo/README.md
@@ -0,0 +1,11 @@
+# pineal-hexbin-demo
+
+5,000 synthetic points, binned.
+
+A deterministic generator (an LCG over `t`) producing two overlapping Gaussian
+clusters. The hexbin reveals the density — each cell is coloured with Viridis by
+count. No animation: the chart is computed once at startup.
+
+---
+
+Part of **pineal** — see [pineal](../../README.md).
diff --git a/00_unanchay/pineal/demos/pineal-mesh-demo/LEEME.md b/00_unanchay/pineal/demos/pineal-mesh-demo/LEEME.md
new file mode 100644
index 0000000..3f412f3
--- /dev/null
+++ b/00_unanchay/pineal/demos/pineal-mesh-demo/LEEME.md
@@ -0,0 +1,26 @@
+# pineal-mesh-demo
+
+*Read this in English: [README.md](README.md).*
+
+Grafo de 24 nodos relajándose en vivo.
+
+Topología: ciclo de 12 nodos + cordales aleatorios + 12 nodos
+satélites enganchados al exterior. La simulación Fruchterman-Reingold
+corre un paso por tick (≈ 60 Hz) hasta enfriarse; el panel muestra
+aristas (gris) y nodos (círculos de discos rellenos) sobre fondo
+oscuro. Cuando la temperatura cae bajo el umbral, el sistema queda
+estacionario hasta que un reset lo recalienta.
+
+Cableado de UI: barra de menú principal (Archivo / Ver / Ayuda) +
+menú contextual sobre el plot (right-click). Como es un canvas sin
+texto editable no hay menú Editar ni clipboard.
+
+## Uso
+
+```sh
+cargo run --release -p pineal-mesh-demo
+```
+
+---
+
+Parte de **pineal** — ver [pineal](../../LEEME.md).
diff --git a/00_unanchay/pineal/demos/pineal-mesh-demo/README.md b/00_unanchay/pineal/demos/pineal-mesh-demo/README.md
new file mode 100644
index 0000000..9fc9d71
--- /dev/null
+++ b/00_unanchay/pineal/demos/pineal-mesh-demo/README.md
@@ -0,0 +1,11 @@
+# pineal-mesh-demo
+
+A 24-node graph relaxing live.
+
+Topology: a 12-node cycle + random chords + 12 satellite nodes hooked to the
+outside. The Fruchterman-Reingold simulation runs one step per tick (≈ 60 Hz)
+until it cools; the panel shows edges (grey) and nodes (filled discs).
+
+---
+
+Part of **pineal** — see [pineal](../../README.md).
diff --git a/00_unanchay/pineal/demos/pineal-phosphor-demo/LEEME.md b/00_unanchay/pineal/demos/pineal-phosphor-demo/LEEME.md
index e933a9b..4be56aa 100644
--- a/00_unanchay/pineal/demos/pineal-phosphor-demo/LEEME.md
+++ b/00_unanchay/pineal/demos/pineal-phosphor-demo/LEEME.md
@@ -1,8 +1,8 @@
 # pineal-phosphor-demo
 
-> Demo del backend fósforo de [pineal](../README.md).
+> Demo del backend fósforo de [pineal](../../README.md).
 
-Genera waveforms (seno, lissajous, ruido) y los pasa por [`pineal-phosphor`](../pineal-phosphor/README.md) con distintos `decay`. Slider para ajustar decay en vivo. Útil para entender qué hace el parámetro antes de usarlo en una app real.
+Genera waveforms (seno, lissajous, ruido) y los pasa por [`pineal-phosphor`](../../pineal-phosphor/README.md) con distintos `decay`. Slider para ajustar decay en vivo. Útil para entender qué hace el parámetro antes de usarlo en una app real.
 
 ## Uso
 
@@ -12,4 +12,4 @@ cargo run --release -p pineal-phosphor-demo
 
 ## Deps
 
-- [`pineal-phosphor`](../pineal-phosphor/README.md), [`pineal-render`](../pineal-render/README.md)
+- [`pineal-phosphor`](../../pineal-phosphor/README.md), [`pineal-render`](../../pineal-render/README.md)
diff --git a/00_unanchay/pineal/demos/pineal-phosphor-demo/README.md b/00_unanchay/pineal/demos/pineal-phosphor-demo/README.md
index 666d885..799e44b 100644
--- a/00_unanchay/pineal/demos/pineal-phosphor-demo/README.md
+++ b/00_unanchay/pineal/demos/pineal-phosphor-demo/README.md
@@ -1,8 +1,8 @@
 # pineal-phosphor-demo
 
-> Phosphor backend demo for [pineal](../README.md).
+> Phosphor backend demo for [pineal](../../README.md).
 
-Generates waveforms (sine, lissajous, noise) and pipes them through [`pineal-phosphor`](../pineal-phosphor/README.md) with various `decay` values. Slider to tune decay live. Useful to understand the parameter before using it in a real app.
+Generates waveforms (sine, lissajous, noise) and pipes them through [`pineal-phosphor`](../../pineal-phosphor/README.md) with various `decay` values. Slider to tune decay live. Useful to understand the parameter before using it in a real app.
 
 ## Usage
 
@@ -12,4 +12,4 @@ cargo run --release -p pineal-phosphor-demo
 
 ## Deps
 
-- [`pineal-phosphor`](../pineal-phosphor/README.md), [`pineal-render`](../pineal-render/README.md)
+- [`pineal-phosphor`](../../pineal-phosphor/README.md), [`pineal-render`](../../pineal-render/README.md)
diff --git a/00_unanchay/pineal/demos/pineal-polar-demo/LEEME.md b/00_unanchay/pineal/demos/pineal-polar-demo/LEEME.md
new file mode 100644
index 0000000..ee648fb
--- /dev/null
+++ b/00_unanchay/pineal/demos/pineal-polar-demo/LEEME.md
@@ -0,0 +1,26 @@
+# pineal-polar-demo
+
+*Read this in English: [README.md](README.md).*
+
+Pie/donut + radar sobre Llimphi.
+
+Dos paneles uno al lado del otro:
+- **Pie chart (donut)** — 6 porciones de un presupuesto sintético.
+- **Radar (spider)** — perfil de 6 atributos contra un máximo común.
+
+Ambos se ajustan al rect del panel: el centro y el radio se calculan
+en el closure de `paint_with` a partir del `PaintRect` recibido.
+
+Lleva barra de menú principal (Archivo/Ver/Ayuda) + un menú
+contextual sobre los plots. Son canvas estáticos sin edición ni
+clipboard — el contextual sólo ofrece cambiar tema.
+
+## Uso
+
+```sh
+cargo run --release -p pineal-polar-demo
+```
+
+---
+
+Parte de **pineal** — ver [pineal](../../LEEME.md).
diff --git a/00_unanchay/pineal/demos/pineal-polar-demo/README.md b/00_unanchay/pineal/demos/pineal-polar-demo/README.md
new file mode 100644
index 0000000..1a7c5a4
--- /dev/null
+++ b/00_unanchay/pineal/demos/pineal-polar-demo/README.md
@@ -0,0 +1,12 @@
+# pineal-polar-demo
+
+Pie/donut + radar over Llimphi.
+
+Two panels side by side:
+
+- **Pie chart (donut)** — six slices of a synthetic budget.
+- **Radar (spider)** — a profile of six attributes against a common maximum.
+
+---
+
+Part of **pineal** — see [pineal](../../README.md).
diff --git a/00_unanchay/pineal/demos/pineal-stream-demo/LEEME.md b/00_unanchay/pineal/demos/pineal-stream-demo/LEEME.md
index db2dac2..a8ef2b3 100644
--- a/00_unanchay/pineal/demos/pineal-stream-demo/LEEME.md
+++ b/00_unanchay/pineal/demos/pineal-stream-demo/LEEME.md
@@ -1,8 +1,8 @@
 # pineal-stream-demo
 
-> Demo del backend streaming de [pineal](../README.md).
+> Demo del backend streaming de [pineal](../../README.md).
 
-Genera muestras a 60 Hz (varias series simultáneas) y las pushea a [`pineal-stream`](../pineal-stream/README.md). Demuestra el scroll suave y la composición de múltiples series sobre el mismo eje de tiempo.
+Genera muestras a 60 Hz (varias series simultáneas) y las pushea a [`pineal-stream`](../../pineal-stream/README.md). Demuestra el scroll suave y la composición de múltiples series sobre el mismo eje de tiempo.
 
 ## Uso
 
@@ -12,4 +12,4 @@ cargo run --release -p pineal-stream-demo
 
 ## Deps
 
-- [`pineal-stream`](../pineal-stream/README.md), [`pineal-render`](../pineal-render/README.md)
+- [`pineal-stream`](../../pineal-stream/README.md), [`pineal-render`](../../pineal-render/README.md)
diff --git a/00_unanchay/pineal/demos/pineal-stream-demo/README.md b/00_unanchay/pineal/demos/pineal-stream-demo/README.md
index e81e3f8..b108989 100644
--- a/00_unanchay/pineal/demos/pineal-stream-demo/README.md
+++ b/00_unanchay/pineal/demos/pineal-stream-demo/README.md
@@ -1,8 +1,8 @@
 # pineal-stream-demo
 
-> Streaming backend demo for [pineal](../README.md).
+> Streaming backend demo for [pineal](../../README.md).
 
-Generates samples at 60 Hz (several simultaneous series) and pushes them to [`pineal-stream`](../pineal-stream/README.md). Demonstrates smooth scrolling and composition of multiple series on the same time axis.
+Generates samples at 60 Hz (several simultaneous series) and pushes them to [`pineal-stream`](../../pineal-stream/README.md). Demonstrates smooth scrolling and composition of multiple series on the same time axis.
 
 ## Usage
 
@@ -12,4 +12,4 @@ cargo run --release -p pineal-stream-demo
 
 ## Deps
 
-- [`pineal-stream`](../pineal-stream/README.md), [`pineal-render`](../pineal-render/README.md)
+- [`pineal-stream`](../../pineal-stream/README.md), [`pineal-render`](../../pineal-render/README.md)
diff --git a/00_unanchay/pineal/demos/pineal-treemap-demo/LEEME.md b/00_unanchay/pineal/demos/pineal-treemap-demo/LEEME.md
new file mode 100644
index 0000000..a632e46
--- /dev/null
+++ b/00_unanchay/pineal/demos/pineal-treemap-demo/LEEME.md
@@ -0,0 +1,25 @@
+# pineal-treemap-demo
+
+*Read this in English: [README.md](README.md).*
+
+Treemap squarified con 12 tiles.
+
+Pesos escogidos a mano para mostrar el algoritmo cuando hay
+mezcla de tiles grandes y chicas. El squarified minimiza el
+peor aspect ratio en cada fila/columna; las tiles chicas
+quedan amontonadas en una banda angosta.
+
+Lleva barra de menú principal (Archivo/Ver/Ayuda) + un menú
+contextual sobre el plot. No hay edición ni clipboard — el treemap
+es un canvas estático, así que el contextual sólo ofrece cambiar
+tema.
+
+## Uso
+
+```sh
+cargo run --release -p pineal-treemap-demo
+```
+
+---
+
+Parte de **pineal** — ver [pineal](../../LEEME.md).
diff --git a/00_unanchay/pineal/demos/pineal-treemap-demo/README.md b/00_unanchay/pineal/demos/pineal-treemap-demo/README.md
new file mode 100644
index 0000000..c623a8a
--- /dev/null
+++ b/00_unanchay/pineal/demos/pineal-treemap-demo/README.md
@@ -0,0 +1,11 @@
+# pineal-treemap-demo
+
+A squarified treemap with 12 tiles.
+
+Weights chosen by hand to show the algorithm when there is a mix of large and
+small tiles. Squarifying minimizes the worst aspect ratio in each row or column;
+the small tiles end up crowded into a narrow band.
+
+---
+
+Part of **pineal** — see [pineal](../../README.md).
diff --git a/00_unanchay/pineal/pantallazo.png b/00_unanchay/pineal/pantallazo.png
new file mode 100644
index 0000000..f9dff58
Binary files /dev/null and b/00_unanchay/pineal/pantallazo.png differ
diff --git a/00_unanchay/pineal/pineal-bars/Cargo.toml b/00_unanchay/pineal/pineal-bars/Cargo.toml
index 97cd70a..8406a8c 100644
--- a/00_unanchay/pineal/pineal-bars/Cargo.toml
+++ b/00_unanchay/pineal/pineal-bars/Cargo.toml
@@ -4,9 +4,9 @@ version = { workspace = true }
 edition = { workspace = true }
 license = { workspace = true }
 authors = { workspace = true }
-publish = { workspace = true }
-description = "Lapaloma — barras: columnas/barras simples, agrupadas y apiladas (vertical u horizontal) + histograma. Painter agnóstico sobre el trait Canvas."
+repository = { workspace = true }
+publish = true
+description = "Pineal — barras: columnas/barras simples, agrupadas y apiladas (vertical u horizontal) + histograma. Painter agnóstico sobre el trait Canvas."
 
 [dependencies]
-pineal-core = { path = "../pineal-core" }
-pineal-render = { path = "../pineal-render" }
+pineal-render = { path = "../pineal-render", version = "0.1.0" }
diff --git a/00_unanchay/pineal/pineal-bars/LEEME.md b/00_unanchay/pineal/pineal-bars/LEEME.md
new file mode 100644
index 0000000..0eb77d6
--- /dev/null
+++ b/00_unanchay/pineal/pineal-bars/LEEME.md
@@ -0,0 +1,26 @@
+# pineal-bars
+
+*Read this in English: [README.md](README.md).*
+
+El painter de barras del catálogo.
+
+El gráfico más común que faltaba: columnas/barras. Como todo en
+pineal, es agnóstico del backend — sólo emite `fill_rect` contra el
+trait `pineal_render::Canvas`, así que sirve igual sobre
+vello/llimphi, PNG, SVG, PDF o el camino GPU directo.
+
+- `paint_bars` — una serie: barras desde un baseline, vertical u
+  horizontal, con soporte para valores negativos.
+- `paint_grouped` — varias series por categoría, agrupadas
+  (clustered) lado a lado.
+- `paint_stacked` — segmentos apilados sobre un baseline común.
+- `Histogram` — bineado de una muestra `&[f32]` en conteos, listo
+  para pasar a `paint_bars` como histograma.
+
+El eje, los ticks y las etiquetas no son responsabilidad del painter
+(regla del SDD: el texto va por una pasada vello hermana). Aquí sólo
+viven los rectángulos.
+
+---
+
+Parte de **pineal** — ver [pineal](../LEEME.md).
diff --git a/00_unanchay/pineal/pineal-bars/README.md b/00_unanchay/pineal/pineal-bars/README.md
new file mode 100644
index 0000000..059f7ad
--- /dev/null
+++ b/00_unanchay/pineal/pineal-bars/README.md
@@ -0,0 +1,12 @@
+# pineal-bars
+
+The catalog's bar painter.
+
+The most common chart that was missing: columns and bars. Like everything in
+pineal it is backend-agnostic — it only emits `fill_rect` against the
+`pineal_render::Canvas` trait, so it works equally over vello/llimphi, PNG, SVG,
+PDF or the direct GPU path.
+
+---
+
+Part of **pineal** — see [pineal](../README.md).
diff --git a/00_unanchay/pineal/pineal-bars/src/lib.rs b/00_unanchay/pineal/pineal-bars/src/lib.rs
index 58bea63..db4620b 100644
--- a/00_unanchay/pineal/pineal-bars/src/lib.rs
+++ b/00_unanchay/pineal/pineal-bars/src/lib.rs
@@ -14,7 +14,7 @@
 //!   para pasar a `paint_bars` como histograma.
 //!
 //! El eje, los ticks y las etiquetas no son responsabilidad del painter
-//! (regla del SDD: el texto va por una pasada vello hermana). Acá sólo
+//! (regla del SDD: el texto va por una pasada vello hermana). Aquí sólo
 //! viven los rectángulos.
 
 #![forbid(unsafe_code)]
diff --git a/00_unanchay/pineal/pineal-bars/src/paint.rs b/00_unanchay/pineal/pineal-bars/src/paint.rs
index 3bfb32a..8b9478e 100644
--- a/00_unanchay/pineal/pineal-bars/src/paint.rs
+++ b/00_unanchay/pineal/pineal-bars/src/paint.rs
@@ -33,7 +33,7 @@ pub struct BarStyle {
     /// Fracción del slot de cada categoría que va a separación, en
     /// `[0, 1)`. `0.0` = barras pegadas; `0.2` = 20 % de aire.
     pub gap_ratio: f32,
-    /// Valor del cero. Las barras nacen acá; valores por debajo crecen
+    /// Valor del cero. Las barras nacen aquí; valores por debajo crecen
     /// en sentido contrario. Normalmente `0.0`.
     pub baseline: f64,
     /// Override del rango de valor. `None` = automático a partir de los
diff --git a/00_unanchay/pineal/pineal-cartesian/Cargo.toml b/00_unanchay/pineal/pineal-cartesian/Cargo.toml
index 9f0c30b..9c0aa56 100644
--- a/00_unanchay/pineal/pineal-cartesian/Cargo.toml
+++ b/00_unanchay/pineal/pineal-cartesian/Cargo.toml
@@ -4,10 +4,18 @@ version = { workspace = true }
 edition = { workspace = true }
 license = { workspace = true }
 authors = { workspace = true }
-publish = { workspace = true }
-description = "Lapaloma — gráficos cartesianos: LineSeries / BarSeries / AreaSeries, viewport con pan/zoom, picture cache, ejes con decimación, tooltips."
+repository = { workspace = true }
+publish = true
+description = "Pineal — gráficos cartesianos: LineSeries / BarSeries / AreaSeries, viewport con pan/zoom, picture cache, ejes con decimación, tooltips."
 
 [dependencies]
-pineal-core = { path = "../pineal-core" }
-pineal-render = { path = "../pineal-render" }
-llimphi-ui = { workspace = true }
+pineal-core = { path = "../pineal-core", version = "0.1.0" }
+pineal-render = { path = "../pineal-render", version = "0.1.0", default-features = false }
+llimphi-ui = { workspace = true, optional = true }
+
+# `llimphi` (default) habilita `ChartView` (el `View<Msg>` declarativo).
+# Sin él quedan los painters puros (axis/series/coord_system/viewport),
+# consumibles headless contra cualquier `Canvas` (p. ej. `PlanRecorder`).
+[features]
+default = ["llimphi"]
+llimphi = ["dep:llimphi-ui", "pineal-render/llimphi"]
diff --git a/00_unanchay/pineal/pineal-cartesian/src/axis.rs b/00_unanchay/pineal/pineal-cartesian/src/axis.rs
index ed1775d..2400151 100644
--- a/00_unanchay/pineal/pineal-cartesian/src/axis.rs
+++ b/00_unanchay/pineal/pineal-cartesian/src/axis.rs
@@ -67,7 +67,7 @@ pub fn ticks_nice(min: f64, max: f64, target_ticks: usize) -> Vec<f64> {
 ///
 /// `min_spacing_px` es la distancia mínima entre el borde
 /// derecho de un label aprobado y el borde izquierdo del
-/// siguiente. Si no tenés el ancho del label, pasá un valor
+/// siguiente. Si no tienes el ancho del label, pasa un valor
 /// conservador (≈ 48 px del Flutter doc).
 pub fn decimate_labels(
     positions_px: &[f32],
diff --git a/00_unanchay/pineal/pineal-cartesian/src/lib.rs b/00_unanchay/pineal/pineal-cartesian/src/lib.rs
index f112432..5f4da81 100644
--- a/00_unanchay/pineal/pineal-cartesian/src/lib.rs
+++ b/00_unanchay/pineal/pineal-cartesian/src/lib.rs
@@ -21,12 +21,14 @@ pub mod viewport;
 pub mod coord_system;
 pub mod series;
 pub mod axis;
+#[cfg(feature = "llimphi")]
 pub mod view;
 
 pub use viewport::ChartViewport;
 pub use coord_system::CoordinateSystem;
 pub use series::{LineSeries, PaintCtx, RenderMode, Series};
 
+#[cfg(feature = "llimphi")]
 pub use view::{
     chart_cache, lapaloma_chart_view, ChartCache, ChartCacheHandle, ChartSeriesItem, ChartView,
 };
diff --git a/00_unanchay/pineal/pineal-cartesian/src/viewport.rs b/00_unanchay/pineal/pineal-cartesian/src/viewport.rs
index 63fbe96..7bc38e1 100644
--- a/00_unanchay/pineal/pineal-cartesian/src/viewport.rs
+++ b/00_unanchay/pineal/pineal-cartesian/src/viewport.rs
@@ -2,7 +2,7 @@
 //!
 //! El viewport NO conoce pixeles. Sólo describe qué rango de
 //! valores X/Y es visible. La proyección a píxeles la hace
-//! [`crate::coord_system::CoordinateSystem`] cuando le pasás
+//! [`crate::coord_system::CoordinateSystem`] cuando le pasas
 //! el `plot_rect`.
 //!
 //! Pan y zoom mutan el viewport, no los datos. Esto preserva el
diff --git a/00_unanchay/pineal/pineal-contour/Cargo.toml b/00_unanchay/pineal/pineal-contour/Cargo.toml
index 1dc73f1..b2709d7 100644
--- a/00_unanchay/pineal/pineal-contour/Cargo.toml
+++ b/00_unanchay/pineal/pineal-contour/Cargo.toml
@@ -4,9 +4,10 @@ version = { workspace = true }
 edition = { workspace = true }
 license = { workspace = true }
 authors = { workspace = true }
-publish = { workspace = true }
-description = "Lapaloma — contour: marching squares sobre matriz escalar → polilíneas por isolínea."
+repository = { workspace = true }
+publish = true
+description = "Pineal — contour: marching squares sobre matriz escalar → polilíneas por isolínea."
 
 [dependencies]
-pineal-render = { path = "../pineal-render" }
-pineal-heatmap = { path = "../pineal-heatmap" }
+pineal-render = { path = "../pineal-render", version = "0.1.0" }
+pineal-heatmap = { path = "../pineal-heatmap", version = "0.1.0" }
diff --git a/00_unanchay/pineal/pineal-contour/LEEME.md b/00_unanchay/pineal/pineal-contour/LEEME.md
new file mode 100644
index 0000000..721c933
--- /dev/null
+++ b/00_unanchay/pineal/pineal-contour/LEEME.md
@@ -0,0 +1,24 @@
+# pineal-contour
+
+*Read this in English: [README.md](README.md).*
+
+Isolíneas vía marching squares.
+
+Dada una `HeatmapMatrix` y un nivel `iso`, extrae los segmentos
+donde la función vale exactamente `iso` aproximada por interpolación
+lineal en los bordes de cada celda. Compone con `pineal-cartesian`
+para mapas topográficos, líneas de presión, equipotenciales, etc.
+
+- `extract_contour` — un nivel → `Vec` de segmentos `[x0,y0,x1,y1]`.
+- `extract_contours` — N niveles equiespaciados.
+- `paint_contours` — pinta cada nivel como polilínea contra `Canvas`.
+
+Implementación: marching squares clásico — para cada celda 2×2 se
+computa un índice 4-bit a partir del signo de cada esquina respecto
+a `iso`; un lookup table de 16 casos da los 0/1/2 segmentos. Sin
+ambigüedad resolution (los casos 5 y 10 saddle se rompen siempre
+del mismo lado — suficiente para charts).
+
+---
+
+Parte de **pineal** — ver [pineal](../LEEME.md).
diff --git a/00_unanchay/pineal/pineal-contour/README.md b/00_unanchay/pineal/pineal-contour/README.md
new file mode 100644
index 0000000..9c2f3aa
--- /dev/null
+++ b/00_unanchay/pineal/pineal-contour/README.md
@@ -0,0 +1,12 @@
+# pineal-contour
+
+Isolines through marching squares.
+
+Given a `HeatmapMatrix` and an `iso` level, it extracts the segments where the
+function is exactly `iso`, approximated by linear interpolation on each cell's
+edges. It composes with `pineal-cartesian` for topographic maps, pressure lines,
+equipotentials and so on.
+
+---
+
+Part of **pineal** — see [pineal](../README.md).
diff --git a/00_unanchay/pineal/pineal-core/Cargo.toml b/00_unanchay/pineal/pineal-core/Cargo.toml
index bdab6ad..579568f 100644
--- a/00_unanchay/pineal/pineal-core/Cargo.toml
+++ b/00_unanchay/pineal/pineal-core/Cargo.toml
@@ -4,8 +4,9 @@ version = { workspace = true }
 edition = { workspace = true }
 license = { workspace = true }
 authors = { workspace = true }
-publish = { workspace = true }
-description = "Lapaloma — primitivas agnósticas: DataBuffer interleaved, RingBuffer streaming, SpatialIndex, LTTB, escalas. Cero deps de UI, cero alloc en hot path."
+repository = { workspace = true }
+publish = true
+description = "Pineal — primitivas agnósticas: DataBuffer interleaved, RingBuffer streaming, SpatialIndex, LTTB, escalas. Cero deps de UI, cero alloc en hot path."
 
 [dependencies]
 
diff --git a/00_unanchay/pineal/pineal-core/src/buffer.rs b/00_unanchay/pineal/pineal-core/src/buffer.rs
index a80469a..d15420f 100644
--- a/00_unanchay/pineal/pineal-core/src/buffer.rs
+++ b/00_unanchay/pineal/pineal-core/src/buffer.rs
@@ -23,7 +23,7 @@ impl DataBuffer {
         Self::default()
     }
 
-    /// Reserva espacio para `n` puntos sin agregarlos. Usalo al
+    /// Reserva espacio para `n` puntos sin agregarlos. Úsalo al
     /// montar el widget para que `push` no realloque después.
     pub fn with_capacity(n: usize) -> Self {
         Self {
diff --git a/00_unanchay/pineal/pineal-core/src/lib.rs b/00_unanchay/pineal/pineal-core/src/lib.rs
index 0ea74f9..dea586b 100644
--- a/00_unanchay/pineal/pineal-core/src/lib.rs
+++ b/00_unanchay/pineal/pineal-core/src/lib.rs
@@ -1,6 +1,6 @@
 //! `pineal-core` — primitivas agnósticas de Lapaloma.
 //!
-//! Cero `gpui`, cero `wgpu`, cero I/O. Todo lo que vive acá puede
+//! Cero `gpui`, cero `wgpu`, cero I/O. Todo lo que vive aquí puede
 //! correr en un test unitario, en un worker thread o en un export
 //! a SVG. Las tres reglas del documento de arquitectura aplican:
 //!
@@ -10,7 +10,7 @@
 //! - **P2 Zero alloc en hot path.** Buffers se reservan al construir,
 //!   se mutan in-place para siempre. Helpers escriben a `&mut Vec`
 //!   provistos por el caller, no devuelven `Vec` nuevos.
-//! - **P3 Una draw call por capa.** Acá no se dibuja; pero los
+//! - **P3 Una draw call por capa.** Aquí no se dibuja; pero los
 //!   tipos exponen slices contiguos listos para mandar al GPU
 //!   sin copia.
 //!
@@ -28,7 +28,7 @@ pub mod lttb;
 pub mod scale;
 
 // Algoritmos de layout: cada uno vive en el crate de la viz que lo
-// usa, no acá. Esto es deliberado — `pineal-core` no debe arrastrar
+// usa, no aquí. Esto es deliberado — `pineal-core` no debe arrastrar
 // dependencias de visualización.
 //
 // - Barnes-Hut + Sugiyama + tree layout: `pineal-mesh`.
diff --git a/00_unanchay/pineal/pineal-core/src/spatial.rs b/00_unanchay/pineal/pineal-core/src/spatial.rs
index 630778e..8d6eb0f 100644
--- a/00_unanchay/pineal/pineal-core/src/spatial.rs
+++ b/00_unanchay/pineal/pineal-core/src/spatial.rs
@@ -4,7 +4,7 @@
 //! temporales) un binary search basta y es O(log n) sin estructuras
 //! auxiliares. Para nodos que se mueven cada frame (mesh graph)
 //! corresponde un spatial hash uniforme — ese va en `pineal-mesh`,
-//! no acá.
+//! no aquí.
 
 /// View sobre un buffer interleaved `[x0,y0,x1,y1,…]` sorted-asc por X.
 ///
diff --git a/00_unanchay/pineal/pineal-export/Cargo.toml b/00_unanchay/pineal/pineal-export/Cargo.toml
index 644a770..63000a9 100644
--- a/00_unanchay/pineal/pineal-export/Cargo.toml
+++ b/00_unanchay/pineal/pineal-export/Cargo.toml
@@ -4,10 +4,11 @@ version = { workspace = true }
 edition = { workspace = true }
 license = { workspace = true }
 authors = { workspace = true }
-publish = { workspace = true }
-description = "Lapaloma — exporters. SVG primero, PDF después. Decimación contextual por DPI: target = width_inches × dpi × vertices_per_pixel."
+repository = { workspace = true }
+publish = true
+description = "Pineal — exporters. SVG primero, PDF después. Decimación contextual por DPI: target = width_inches × dpi × vertices_per_pixel."
 
 [dependencies]
-pineal-core = { path = "../pineal-core" }
-pineal-render = { path = "../pineal-render" }
+pineal-core = { path = "../pineal-core", version = "0.1.0" }
+pineal-render = { path = "../pineal-render", version = "0.1.0", default-features = false }
 png = { workspace = true }
diff --git a/00_unanchay/pineal/pineal-export/src/png.rs b/00_unanchay/pineal/pineal-export/src/png.rs
index 45b66eb..70e8290 100644
--- a/00_unanchay/pineal/pineal-export/src/png.rs
+++ b/00_unanchay/pineal/pineal-export/src/png.rs
@@ -2,7 +2,7 @@
 //! codifica como PNG.
 //!
 //! La estrategia es la misma que `svg.rs`: el painter grabó cada comando
-//! en un plan; acá lo replayeamos contra un rasterizador software
+//! en un plan; aquí lo replayeamos contra un rasterizador software
 //! propio. No depende de `tiny-skia` ni `cairo` — sólo `png` (ya en el
 //! workspace) y aritmética f32. Esto mantiene la cadena
 //! `core → render → export` libre de stack gráfico nativo.
diff --git a/00_unanchay/pineal/pineal-export/src/svg.rs b/00_unanchay/pineal/pineal-export/src/svg.rs
index 93f418b..64cd045 100644
--- a/00_unanchay/pineal/pineal-export/src/svg.rs
+++ b/00_unanchay/pineal/pineal-export/src/svg.rs
@@ -1,7 +1,7 @@
 //! Exporter SVG: un [`RenderPlan`] → documento SVG completo.
 //!
 //! El mismo painter que dibuja en pantalla (vía el trait `Canvas`) se
-//! graba con un `PlanRecorder` y el plan resultante se vuelca acá. Un
+//! graba con un `PlanRecorder` y el plan resultante se vuelca aquí. Un
 //! solo camino de código para screen y export.
 //!
 //! v1: los comandos de clip (`PushClip`/`PopClip`) se ignoran — el
diff --git a/00_unanchay/pineal/pineal-financial/Cargo.toml b/00_unanchay/pineal/pineal-financial/Cargo.toml
index 7cf01ac..51c0741 100644
--- a/00_unanchay/pineal/pineal-financial/Cargo.toml
+++ b/00_unanchay/pineal/pineal-financial/Cargo.toml
@@ -4,11 +4,11 @@ version = { workspace = true }
 edition = { workspace = true }
 license = { workspace = true }
 authors = { workspace = true }
-publish = { workspace = true }
-description = "Lapaloma — gráficos financieros. OHLC / candlesticks con agregación que preserva volatilidad (no LTTB, time-bucketing con max/min de wicks)."
+repository = { workspace = true }
+publish = true
+description = "Pineal — gráficos financieros. OHLC / candlesticks con agregación que preserva volatilidad (no LTTB, time-bucketing con max/min de wicks)."
 
 [dependencies]
-pineal-core = { path = "../pineal-core" }
-pineal-render = { path = "../pineal-render" }
-pineal-cartesian = { path = "../pineal-cartesian" }
+pineal-render = { path = "../pineal-render", version = "0.1.0" }
+pineal-cartesian = { path = "../pineal-cartesian", version = "0.1.0" }
 llimphi-ui = { workspace = true }
diff --git a/00_unanchay/pineal/pineal-financial/src/aggregate.rs b/00_unanchay/pineal/pineal-financial/src/aggregate.rs
index cfe117a..31ee216 100644
--- a/00_unanchay/pineal/pineal-financial/src/aggregate.rs
+++ b/00_unanchay/pineal/pineal-financial/src/aggregate.rs
@@ -10,7 +10,7 @@
 //! - `volume` = sum(`volume`) del bucket.
 //! - `t` = timestamp del primer bar del bucket (canónico para
 //!   ploteo; el doc original sugiere usar el inicio del bucket
-//!   pero acá preferimos el sample real para no introducir bias).
+//!   pero aquí preferimos el sample real para no introducir bias).
 //!
 //! Buckets vacíos no se emiten — el length de salida es ≤ inputs.
 //! Fallback a index-bucketing si el span temporal es cero (todos
diff --git a/00_unanchay/pineal/pineal-flow/Cargo.toml b/00_unanchay/pineal/pineal-flow/Cargo.toml
index 6fa6d98..a3c6465 100644
--- a/00_unanchay/pineal/pineal-flow/Cargo.toml
+++ b/00_unanchay/pineal/pineal-flow/Cargo.toml
@@ -4,9 +4,9 @@ version = { workspace = true }
 edition = { workspace = true }
 license = { workspace = true }
 authors = { workspace = true }
-publish = { workspace = true }
-description = "Lapaloma — diagramas de flujo Sankey: columnas topológicas + barycenter ordering + ribbons como triangle strips de béziers."
+repository = { workspace = true }
+publish = true
+description = "Pineal — diagramas de flujo Sankey: columnas topológicas + barycenter ordering + ribbons como triangle strips de béziers."
 
 [dependencies]
-pineal-core = { path = "../pineal-core" }
-pineal-render = { path = "../pineal-render" }
+pineal-render = { path = "../pineal-render", version = "0.1.0" }
diff --git a/00_unanchay/pineal/pineal-heatmap/Cargo.toml b/00_unanchay/pineal/pineal-heatmap/Cargo.toml
index b6f1979..00d291f 100644
--- a/00_unanchay/pineal/pineal-heatmap/Cargo.toml
+++ b/00_unanchay/pineal/pineal-heatmap/Cargo.toml
@@ -4,9 +4,9 @@ version = { workspace = true }
 edition = { workspace = true }
 license = { workspace = true }
 authors = { workspace = true }
-publish = { workspace = true }
-description = "Lapaloma — heatmap. Matriz [width × height] de f32 → imagen pre-encodeada que se rendea con un sólo drawImageRect."
+repository = { workspace = true }
+publish = true
+description = "Pineal — heatmap. Matriz [width × height] de f32 → imagen pre-encodeada que se rendea con un sólo drawImageRect."
 
 [dependencies]
-pineal-core = { path = "../pineal-core" }
-pineal-render = { path = "../pineal-render" }
+pineal-render = { path = "../pineal-render", version = "0.1.0" }
diff --git a/00_unanchay/pineal/pineal-hexbin/Cargo.toml b/00_unanchay/pineal/pineal-hexbin/Cargo.toml
index 4a4b58d..21d39b1 100644
--- a/00_unanchay/pineal/pineal-hexbin/Cargo.toml
+++ b/00_unanchay/pineal/pineal-hexbin/Cargo.toml
@@ -4,9 +4,10 @@ version = { workspace = true }
 edition = { workspace = true }
 license = { workspace = true }
 authors = { workspace = true }
-publish = { workspace = true }
-description = "Lapaloma — hexbin: bineado hexagonal pointy-top de un dataset 2D + painter."
+repository = { workspace = true }
+publish = true
+description = "Pineal — hexbin: bineado hexagonal pointy-top de un dataset 2D + painter."
 
 [dependencies]
-pineal-render = { path = "../pineal-render" }
-pineal-heatmap = { path = "../pineal-heatmap" }
+pineal-render = { path = "../pineal-render", version = "0.1.0" }
+pineal-heatmap = { path = "../pineal-heatmap", version = "0.1.0" }
diff --git a/00_unanchay/pineal/pineal-hexbin/LEEME.md b/00_unanchay/pineal/pineal-hexbin/LEEME.md
new file mode 100644
index 0000000..a5d0985
--- /dev/null
+++ b/00_unanchay/pineal/pineal-hexbin/LEEME.md
@@ -0,0 +1,21 @@
+# pineal-hexbin
+
+*Read this in English: [README.md](README.md).*
+
+Bineado hexagonal de scatter densos.
+
+Cuando un scatter tiene tantos puntos que la nube tapa el patrón,
+binear sobre una rejilla hexagonal pointy-top y colorear por
+densidad es la forma estándar de revelar la distribución. La
+topología hexagonal evita los "estrías" rectangulares de un binning
+cuadrado y respeta mejor la isotropía de la nube.
+
+- `HexGrid` — calcula el bin de cada `(x, y)` con la rejilla
+  pointy-top (hex con dos vértices arriba/abajo). Cell size = radio
+  del circumcírculo.
+- `paint_hexbin` — pinta los bins no vacíos como hexágonos
+  rellenos coloreados por densidad vía `pineal_heatmap::Ramp`.
+
+---
+
+Parte de **pineal** — ver [pineal](../LEEME.md).
diff --git a/00_unanchay/pineal/pineal-hexbin/README.md b/00_unanchay/pineal/pineal-hexbin/README.md
new file mode 100644
index 0000000..bbe6324
--- /dev/null
+++ b/00_unanchay/pineal/pineal-hexbin/README.md
@@ -0,0 +1,12 @@
+# pineal-hexbin
+
+Hexagonal binning of dense scatters.
+
+When a scatter has so many points that the cloud hides the pattern, binning over a
+pointy-top hexagonal grid and colouring by density is the standard way to reveal
+the distribution. The hexagonal topology avoids the rectangular "striping" of a
+square binning.
+
+---
+
+Part of **pineal** — see [pineal](../README.md).
diff --git a/00_unanchay/pineal/pineal-mesh/Cargo.toml b/00_unanchay/pineal/pineal-mesh/Cargo.toml
index 80a7135..5996872 100644
--- a/00_unanchay/pineal/pineal-mesh/Cargo.toml
+++ b/00_unanchay/pineal/pineal-mesh/Cargo.toml
@@ -4,9 +4,9 @@ version = { workspace = true }
 edition = { workspace = true }
 license = { workspace = true }
 authors = { workspace = true }
-publish = { workspace = true }
-description = "Lapaloma — grafos. NodeBuffer / EdgeBuffer + layouts (force-directed con Barnes-Hut, Sugiyama-lite jerárquico, subtree-width)."
+repository = { workspace = true }
+publish = true
+description = "Pineal — grafos. NodeBuffer / EdgeBuffer + layouts (force-directed con Barnes-Hut, Sugiyama-lite jerárquico, subtree-width)."
 
 [dependencies]
-pineal-core = { path = "../pineal-core" }
-pineal-render = { path = "../pineal-render" }
+pineal-render = { path = "../pineal-render", version = "0.1.0" }
diff --git a/00_unanchay/pineal/pineal-mesh/LEEME.md b/00_unanchay/pineal/pineal-mesh/LEEME.md
index 2ac9f96..2b3b55d 100644
--- a/00_unanchay/pineal/pineal-mesh/LEEME.md
+++ b/00_unanchay/pineal/pineal-mesh/LEEME.md
@@ -2,7 +2,7 @@
 
 > Canvas de triángulos arbitrarios para [pineal](../README.md).
 
-Backend para geometría libre: tomá una malla `(vertices, indices, colors)` y se dibuja con shader-like flexibility. Útil para terrenos, geometría 2D compleja, ilustraciones generativas.
+Backend para geometría libre: toma una malla `(vertices, indices, colors)` y se dibuja con shader-like flexibility. Útil para terrenos, geometría 2D compleja, ilustraciones generativas.
 
 ## API
 
diff --git a/00_unanchay/pineal/pineal-mesh/src/barnes_hut.rs b/00_unanchay/pineal/pineal-mesh/src/barnes_hut.rs
index 18e33b6..1fbbddb 100644
--- a/00_unanchay/pineal/pineal-mesh/src/barnes_hut.rs
+++ b/00_unanchay/pineal/pineal-mesh/src/barnes_hut.rs
@@ -6,7 +6,7 @@
 //! Theta típico: 0.5 — más bajo = más preciso, más lento.
 //!
 //! Representación: `Vec<QuadNode>` indexado. El doc original de
-//! `pineal-core::barnes_hut` propone stride 7 plano; acá optamos por la
+//! `pineal-core::barnes_hut` propone stride 7 plano; aquí optamos por la
 //! versión idiomática — clara y suficiente para los rangos en que el
 //! force-directed corre interactivo (hasta ~50 K nodos).
 
@@ -22,7 +22,7 @@ struct QuadNode {
     /// Center of mass acumulado.
     cm_x: f32,
     cm_y: f32,
-    /// Masa total (suma de pesos — acá usamos 1.0 por nodo).
+    /// Masa total (suma de pesos — aquí usamos 1.0 por nodo).
     mass: f32,
     /// Hoja con exactamente 1 cuerpo: índice del cuerpo en `positions`.
     body: Option<usize>,
diff --git a/00_unanchay/pineal/pineal-phosphor/Cargo.toml b/00_unanchay/pineal/pineal-phosphor/Cargo.toml
index 4bf10e8..4f8d01e 100644
--- a/00_unanchay/pineal/pineal-phosphor/Cargo.toml
+++ b/00_unanchay/pineal/pineal-phosphor/Cargo.toml
@@ -4,10 +4,11 @@ version = { workspace = true }
 edition = { workspace = true }
 license = { workspace = true }
 authors = { workspace = true }
-publish = { workspace = true }
-description = "Lapaloma — decoración CRT sobre lapaloma-stream: trail con alpha decay por edad, ghost, anotaciones magnéticas ancladas a sample index."
+repository = { workspace = true }
+publish = true
+description = "Pineal — decoración CRT sobre lapaloma-stream: trail con alpha decay por edad, ghost, anotaciones magnéticas ancladas a sample index."
 
 [dependencies]
-pineal-core = { path = "../pineal-core" }
-pineal-render = { path = "../pineal-render" }
+pineal-core = { path = "../pineal-core", version = "0.1.0" }
+pineal-render = { path = "../pineal-render", version = "0.1.0" }
 llimphi-ui = { workspace = true }
diff --git a/00_unanchay/pineal/pineal-polar/Cargo.toml b/00_unanchay/pineal/pineal-polar/Cargo.toml
index f3587dc..b41a572 100644
--- a/00_unanchay/pineal/pineal-polar/Cargo.toml
+++ b/00_unanchay/pineal/pineal-polar/Cargo.toml
@@ -4,9 +4,9 @@ version = { workspace = true }
 edition = { workspace = true }
 license = { workspace = true }
 authors = { workspace = true }
-publish = { workspace = true }
-description = "Lapaloma — gráficos polares: pie chart, donut, radar."
+repository = { workspace = true }
+publish = true
+description = "Pineal — gráficos polares: pie chart, donut, radar."
 
 [dependencies]
-pineal-core = { path = "../pineal-core" }
-pineal-render = { path = "../pineal-render" }
+pineal-render = { path = "../pineal-render", version = "0.1.0" }
diff --git a/00_unanchay/pineal/pineal-render/Cargo.toml b/00_unanchay/pineal/pineal-render/Cargo.toml
index 11571df..78474b0 100644
--- a/00_unanchay/pineal/pineal-render/Cargo.toml
+++ b/00_unanchay/pineal/pineal-render/Cargo.toml
@@ -4,9 +4,16 @@ version = { workspace = true }
 edition = { workspace = true }
 license = { workspace = true }
 authors = { workspace = true }
-publish = { workspace = true }
-description = "Lapaloma — abstracción de painter: trait Canvas + RenderPlan + color helpers. Backend Llimphi (vello/wgpu) consumido por demos y consumidores upstream sin tocar a los painters."
+repository = { workspace = true }
+publish = true
+description = "Pineal — abstracción de painter: trait Canvas + RenderPlan + color helpers. Backend Llimphi (vello/wgpu) consumido por demos y consumidores upstream sin tocar a los painters."
 
 [dependencies]
-pineal-core = { path = "../pineal-core" }
-llimphi-ui = { workspace = true }
+llimphi-ui = { workspace = true, optional = true }
+
+# `llimphi` (default) habilita los backends SceneCanvas/GpuSceneCanvas.
+# Sin él queda el núcleo agnóstico (Canvas + RenderPlan + PlanRecorder),
+# consumible headless sin arrastrar el stack gráfico.
+[features]
+default = ["llimphi"]
+llimphi = ["dep:llimphi-ui"]
diff --git a/00_unanchay/pineal/pineal-render/src/lib.rs b/00_unanchay/pineal/pineal-render/src/lib.rs
index d43fa83..3f5245f 100644
--- a/00_unanchay/pineal/pineal-render/src/lib.rs
+++ b/00_unanchay/pineal/pineal-render/src/lib.rs
@@ -1,13 +1,13 @@
 //! `pineal-render` — abstracción de painter.
 //!
 //! Los crates de visualización (cartesian, mesh, polar…) no conocen
-//! el runtime de UI: hablan contra el trait [`Canvas`] definido acá.
+//! el runtime de UI: hablan contra el trait [`Canvas`] definido aquí.
 //! Eso deja a la cadena `core → render → painter` agnóstica.
 //!
 //! Backends activos:
 //!
 //! - **Llimphi/vello** ([`llimphi_backend::SceneCanvas`]) — canónico
-//!   para apps gioser; pinta dentro de un `paint_with` del `View<Msg>`
+//!   para apps tawasuyu; pinta dentro de un `paint_with` del `View<Msg>`
 //!   declarativo.
 //! - **PlanRecorder** ([`recorder::PlanRecorder`]) — graba cada llamada
 //!   como `RenderCmd`. Consumido por `pineal-export` para emitir SVG,
@@ -18,7 +18,7 @@
 //!   en lugar de `paint_with`. Los painters no cambian. Sin texto y sin
 //!   AA fino — ver doc del módulo para trade-offs.
 //!
-//! Tipos primitivos (`Color`, `Point`, `Rect`) viven acá para no
+//! Tipos primitivos (`Color`, `Point`, `Rect`) viven aquí para no
 //! atarlos al tipo de color/punto de ningún runtime.
 
 #![forbid(unsafe_code)]
@@ -29,7 +29,9 @@ pub mod canvas;
 pub mod plan;
 pub mod recorder;
 
+#[cfg(feature = "llimphi")]
 pub mod llimphi_backend;
+#[cfg(feature = "llimphi")]
 pub mod gpu_canvas;
 
 pub use color::Color;
@@ -38,5 +40,7 @@ pub use canvas::{Canvas, StrokeStyle};
 pub use plan::{RenderCmd, RenderPlan};
 pub use recorder::PlanRecorder;
 
+#[cfg(feature = "llimphi")]
 pub use llimphi_backend::SceneCanvas;
+#[cfg(feature = "llimphi")]
 pub use gpu_canvas::GpuSceneCanvas;
diff --git a/00_unanchay/pineal/pineal-stream/Cargo.toml b/00_unanchay/pineal/pineal-stream/Cargo.toml
index ec09d8e..f9342bd 100644
--- a/00_unanchay/pineal/pineal-stream/Cargo.toml
+++ b/00_unanchay/pineal/pineal-stream/Cargo.toml
@@ -4,10 +4,11 @@ version = { workspace = true }
 edition = { workspace = true }
 license = { workspace = true }
 authors = { workspace = true }
-publish = { workspace = true }
-description = "Lapaloma — widget de telemetría tipo osciloscopio. Ring buffer + envelope min/max por columna + render en dos segmentos (split at head)."
+repository = { workspace = true }
+publish = true
+description = "Pineal — widget de telemetría tipo osciloscopio. Ring buffer + envelope min/max por columna + render en dos segmentos (split at head)."
 
 [dependencies]
-pineal-core = { path = "../pineal-core" }
-pineal-render = { path = "../pineal-render" }
+pineal-core = { path = "../pineal-core", version = "0.1.0" }
+pineal-render = { path = "../pineal-render", version = "0.1.0" }
 llimphi-ui = { workspace = true }
diff --git a/00_unanchay/pineal/pineal-treemap/Cargo.toml b/00_unanchay/pineal/pineal-treemap/Cargo.toml
index 6497650..40ca105 100644
--- a/00_unanchay/pineal/pineal-treemap/Cargo.toml
+++ b/00_unanchay/pineal/pineal-treemap/Cargo.toml
@@ -4,9 +4,9 @@ version = { workspace = true }
 edition = { workspace = true }
 license = { workspace = true }
 authors = { workspace = true }
-publish = { workspace = true }
-description = "Lapaloma — treemap con algoritmo squarified (Bruls / d3-hierarchy formulation)."
+repository = { workspace = true }
+publish = true
+description = "Pineal — treemap con algoritmo squarified (Bruls / d3-hierarchy formulation)."
 
 [dependencies]
-pineal-core = { path = "../pineal-core" }
-pineal-render = { path = "../pineal-render" }
+pineal-render = { path = "../pineal-render", version = "0.1.0" }
diff --git a/00_unanchay/pineal/pineal-umbrella/Cargo.toml b/00_unanchay/pineal/pineal-umbrella/Cargo.toml
index 146e239..458248b 100644
--- a/00_unanchay/pineal/pineal-umbrella/Cargo.toml
+++ b/00_unanchay/pineal/pineal-umbrella/Cargo.toml
@@ -4,25 +4,28 @@ version = { workspace = true }
 edition = { workspace = true }
 license = { workspace = true }
 authors = { workspace = true }
-publish = { workspace = true }
-description = "Lapaloma — paraguas: re-exporta los módulos para prototipos. En producción importar los crates hoja directamente para que tree-shaking descarte lo no usado."
+repository = { workspace = true }
+publish = true
+description = "Pineal — paraguas: re-exporta los módulos para prototipos. En producción importar los crates hoja directamente para que tree-shaking descarte lo no usado."
+keywords = ["visualization", "charts", "plotting", "graph", "svg"]
+categories = ["visualization", "graphics", "rendering"]
 
 [dependencies]
-pineal-core = { path = "../pineal-core", optional = true }
-pineal-render = { path = "../pineal-render", optional = true }
-pineal-cartesian = { path = "../pineal-cartesian", optional = true }
-pineal-stream = { path = "../pineal-stream", optional = true }
-pineal-mesh = { path = "../pineal-mesh", optional = true }
-pineal-financial = { path = "../pineal-financial", optional = true }
-pineal-polar = { path = "../pineal-polar", optional = true }
-pineal-heatmap = { path = "../pineal-heatmap", optional = true }
-pineal-treemap = { path = "../pineal-treemap", optional = true }
-pineal-flow = { path = "../pineal-flow", optional = true }
-pineal-phosphor = { path = "../pineal-phosphor", optional = true }
-pineal-export = { path = "../pineal-export", optional = true }
-pineal-hexbin = { path = "../pineal-hexbin", optional = true }
-pineal-contour = { path = "../pineal-contour", optional = true }
-pineal-bars = { path = "../pineal-bars", optional = true }
+pineal-core = { path = "../pineal-core", version = "0.1.0", optional = true }
+pineal-render = { path = "../pineal-render", version = "0.1.0", optional = true }
+pineal-cartesian = { path = "../pineal-cartesian", version = "0.1.0", optional = true }
+pineal-stream = { path = "../pineal-stream", version = "0.1.0", optional = true }
+pineal-mesh = { path = "../pineal-mesh", version = "0.1.0", optional = true }
+pineal-financial = { path = "../pineal-financial", version = "0.1.0", optional = true }
+pineal-polar = { path = "../pineal-polar", version = "0.1.0", optional = true }
+pineal-heatmap = { path = "../pineal-heatmap", version = "0.1.0", optional = true }
+pineal-treemap = { path = "../pineal-treemap", version = "0.1.0", optional = true }
+pineal-flow = { path = "../pineal-flow", version = "0.1.0", optional = true }
+pineal-phosphor = { path = "../pineal-phosphor", version = "0.1.0", optional = true }
+pineal-export = { path = "../pineal-export", version = "0.1.0", optional = true }
+pineal-hexbin = { path = "../pineal-hexbin", version = "0.1.0", optional = true }
+pineal-contour = { path = "../pineal-contour", version = "0.1.0", optional = true }
+pineal-bars = { path = "../pineal-bars", version = "0.1.0", optional = true }
 
 [features]
 default = ["full"]
diff --git a/Cargo.lock b/Cargo.lock
index 2baba81..dbb9cf6 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -18,6 +18,114 @@ version = "0.1.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618"
 
+[[package]]
+name = "accesskit"
+version = "0.24.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3b7f7f85a7e5f68090000ed7622545829afd484d210358702ae4cb97dd0c320"
+dependencies = [
+ "uuid",
+]
+
+[[package]]
+name = "accesskit_atspi_common"
+version = "0.19.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "023da0e5097f46df7092d5280b02efb9bbf8d93298daeced42652463e357d636"
+dependencies = [
+ "accesskit",
+ "accesskit_consumer",
+ "atspi-common",
+ "phf",
+ "serde",
+ "zvariant",
+]
+
+[[package]]
+name = "accesskit_consumer"
+version = "0.38.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d10a236f96f87d70732e44520046785431ef01d5bcd6b041317bfadd2f88245"
+dependencies = [
+ "accesskit",
+ "hashbrown 0.16.1",
+]
+
+[[package]]
+name = "accesskit_ios"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "750c4e9f6ce888dfe8a10c0f1b5ceb646a7854fd46579d74919219d1bb314083"
+dependencies = [
+ "accesskit",
+ "accesskit_consumer",
+ "hashbrown 0.16.1",
+ "objc2 0.5.2",
+ "objc2-foundation 0.2.2",
+ "objc2-ui-kit",
+]
+
+[[package]]
+name = "accesskit_macos"
+version = "0.26.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce02dc63b43f0c9296af9ac946312a2dc8814427d7a64d2d600971dac55b6076"
+dependencies = [
+ "accesskit",
+ "accesskit_consumer",
+ "hashbrown 0.16.1",
+ "objc2 0.5.2",
+ "objc2-app-kit 0.2.2",
+ "objc2-foundation 0.2.2",
+]
+
+[[package]]
+name = "accesskit_unix"
+version = "0.22.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03e156ed3802e35eefe894ef2671bc6c889303d8a7e110b5e1b48f504b91362f"
+dependencies = [
+ "accesskit",
+ "accesskit_atspi_common",
+ "async-channel",
+ "async-executor",
+ "async-task",
+ "atspi",
+ "futures-lite",
+ "futures-util",
+ "serde",
+ "zbus",
+]
+
+[[package]]
+name = "accesskit_windows"
+version = "0.34.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "106c2b961215864d1c2e703ee63269c25c4e80a577ffb2c1017b9c17dcdf83a1"
+dependencies = [
+ "accesskit",
+ "accesskit_consumer",
+ "hashbrown 0.16.1",
+ "static_assertions",
+ "windows 0.62.2",
+ "windows-core 0.62.2",
+]
+
+[[package]]
+name = "accesskit_winit"
+version = "0.33.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d5b41e63a69f36d9f1f41e70464c7e5f72eee485ef26aab19f0b4f86e6c0a84c"
+dependencies = [
+ "accesskit",
+ "accesskit_ios",
+ "accesskit_macos",
+ "accesskit_unix",
+ "accesskit_windows",
+ "raw-window-handle",
+ "winit",
+]
+
 [[package]]
 name = "adler2"
 version = "2.0.1"
@@ -50,16 +158,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0f2a1bb052857d5dd49572219344a7332b31b76405648eabac5bc68978251bcd"
 dependencies = [
  "android-properties",
- "bitflags 2.12.1",
+ "bitflags 2.13.1",
  "cc",
  "jni",
  "libc",
  "log",
  "ndk",
  "ndk-context",
- "ndk-sys 0.6.0+11769913",
+ "ndk-sys",
  "num_enum",
- "thiserror 2.0.18",
+ "thiserror 2.0.19",
 ]
 
 [[package]]
@@ -80,13 +188,34 @@ dependencies = [
 [[package]]
 name = "app-bus"
 version = "0.1.0"
-source = "git+https://gitea.tawasuyu.net/sergio/llimphi.git?tag=v0.1.0#c1b11b2c01d7623a4f346519b9d1ec435be30b95"
+source = "git+https://git.tawasuyu.net/tawasuyu/llimphi.git?tag=v0.2.0#93253cc227130680427a9a83bdc504bd82ca71ca"
 dependencies = [
  "directories",
  "serde",
  "toml",
 ]
 
+[[package]]
+name = "arboard"
+version = "3.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0348a1c054491f4bfe6ab86a7b6ab1e44e45d899005de92f58b3df180b36ddaf"
+dependencies = [
+ "clipboard-win",
+ "image",
+ "log",
+ "objc2 0.6.4",
+ "objc2-app-kit 0.3.2",
+ "objc2-core-foundation",
+ "objc2-core-graphics",
+ "objc2-foundation 0.3.2",
+ "parking_lot",
+ "percent-encoding",
+ "windows-sys 0.60.2",
+ "wl-clipboard-rs",
+ "x11rb",
+]
+
 [[package]]
 name = "arrayref"
 version = "0.3.9"
@@ -95,9 +224,9 @@ checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb"
 
 [[package]]
 name = "arrayvec"
-version = "0.7.6"
+version = "0.7.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
+checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56"
 
 [[package]]
 name = "as-raw-xcb-connection"
@@ -114,12 +243,180 @@ dependencies = [
  "libloading",
 ]
 
+[[package]]
+name = "async-broadcast"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532"
+dependencies = [
+ "event-listener",
+ "event-listener-strategy",
+ "futures-core",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "async-channel"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2"
+dependencies = [
+ "concurrent-queue",
+ "event-listener-strategy",
+ "futures-core",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "async-executor"
+version = "1.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a"
+dependencies = [
+ "async-task",
+ "concurrent-queue",
+ "fastrand",
+ "futures-lite",
+ "pin-project-lite",
+ "slab",
+]
+
+[[package]]
+name = "async-io"
+version = "2.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc"
+dependencies = [
+ "autocfg",
+ "cfg-if",
+ "concurrent-queue",
+ "futures-io",
+ "futures-lite",
+ "parking",
+ "polling",
+ "rustix 1.1.4",
+ "slab",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "async-lock"
+version = "3.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311"
+dependencies = [
+ "event-listener",
+ "event-listener-strategy",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "async-process"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75"
+dependencies = [
+ "async-channel",
+ "async-io",
+ "async-lock",
+ "async-signal",
+ "async-task",
+ "blocking",
+ "cfg-if",
+ "event-listener",
+ "futures-lite",
+ "rustix 1.1.4",
+]
+
+[[package]]
+name = "async-recursion"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+]
+
+[[package]]
+name = "async-signal"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "52b5aaafa020cf5053a01f2a60e8ff5dccf550f0f77ec54a4e47285ac2bab485"
+dependencies = [
+ "async-io",
+ "async-lock",
+ "atomic-waker",
+ "cfg-if",
+ "futures-core",
+ "futures-io",
+ "rustix 1.1.4",
+ "signal-hook-registry",
+ "slab",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "async-task"
+version = "4.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de"
+
+[[package]]
+name = "async-trait"
+version = "0.1.91"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae36dc4177970ef04fde5178d3e2429882def40e57a451f919c098f72baa6cec"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 3.0.3",
+]
+
 [[package]]
 name = "atomic-waker"
 version = "1.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
 
+[[package]]
+name = "atspi"
+version = "0.29.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c77886257be21c9cd89a4ae7e64860c6f0eefca799bb79127913052bd0eefb3d"
+dependencies = [
+ "atspi-common",
+ "atspi-proxies",
+]
+
+[[package]]
+name = "atspi-common"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "20c5617155740c98003016429ad13fe43ce7a77b007479350a9f8bf95a29f63d"
+dependencies = [
+ "enumflags2",
+ "serde",
+ "static_assertions",
+ "zbus",
+ "zbus-lockstep",
+ "zbus-lockstep-macros",
+ "zbus_names",
+ "zvariant",
+]
+
+[[package]]
+name = "atspi-proxies"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2230e48787ed3eb4088996eab66a32ca20c0b67bbd4fd6cdfe79f04f1f04c9fc"
+dependencies = [
+ "atspi-common",
+ "serde",
+ "zbus",
+]
+
 [[package]]
 name = "autocfg"
 version = "1.5.1"
@@ -149,12 +446,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
 
 [[package]]
 name = "bitflags"
-version = "2.12.1"
+version = "2.13.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "84d7ced0ae9557296835c32bf1b1e02b44c746701f898460fb000d7eaa84f00a"
-dependencies = [
- "serde_core",
-]
+checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da"
 
 [[package]]
 name = "block"
@@ -171,6 +465,19 @@ dependencies = [
  "objc2 0.5.2",
 ]
 
+[[package]]
+name = "blocking"
+version = "1.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21"
+dependencies = [
+ "async-channel",
+ "async-task",
+ "futures-io",
+ "futures-lite",
+ "piper",
+]
+
 [[package]]
 name = "bumpalo"
 version = "3.20.3"
@@ -179,29 +486,35 @@ checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
 
 [[package]]
 name = "bytemuck"
-version = "1.25.0"
+version = "1.25.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec"
+checksum = "95832e849adfb21180ccb6826a99da14e5d266ae5c2e668e1602cf234f153797"
 dependencies = [
  "bytemuck_derive",
 ]
 
 [[package]]
 name = "bytemuck_derive"
-version = "1.10.2"
+version = "1.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff"
+checksum = "f65693059b6b9c588b9f62fed1cedbf0a8b805631457ea162d68f0de186f3de5"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.119",
 ]
 
+[[package]]
+name = "byteorder-lite"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
+
 [[package]]
 name = "bytes"
-version = "1.11.1"
+version = "1.12.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
+checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04"
 
 [[package]]
 name = "calloop"
@@ -209,7 +522,7 @@ version = "0.13.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec"
 dependencies = [
- "bitflags 2.12.1",
+ "bitflags 2.13.1",
  "log",
  "polling",
  "rustix 0.38.44",
@@ -231,9 +544,9 @@ dependencies = [
 
 [[package]]
 name = "cc"
-version = "1.2.63"
+version = "1.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f"
+checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9"
 dependencies = [
  "find-msvc-tools",
  "jobserver",
@@ -249,16 +562,26 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
 
 [[package]]
 name = "cfg_aliases"
-version = "0.2.1"
+version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
+checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527"
+
+[[package]]
+name = "clipboard-win"
+version = "5.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bde03770d3df201d4fb868f2c9c59e66a3e4e2bd06692a0fe701e7103c7e84d4"
+dependencies = [
+ "error-code",
+]
 
 [[package]]
 name = "codespan-reporting"
-version = "0.11.1"
+version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
+checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81"
 dependencies = [
+ "serde",
  "termcolor",
  "unicode-width",
 ]
@@ -298,6 +621,16 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "core-foundation"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
 [[package]]
 name = "core-foundation-sys"
 version = "0.8.7"
@@ -311,8 +644,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081"
 dependencies = [
  "bitflags 1.3.2",
- "core-foundation",
- "core-graphics-types",
+ "core-foundation 0.9.4",
+ "core-graphics-types 0.1.3",
  "foreign-types",
  "libc",
 ]
@@ -324,10 +657,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf"
 dependencies = [
  "bitflags 1.3.2",
- "core-foundation",
+ "core-foundation 0.9.4",
+ "libc",
+]
+
+[[package]]
+name = "core-graphics-types"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb"
+dependencies = [
+ "bitflags 2.13.1",
+ "core-foundation 0.10.1",
  "libc",
 ]
 
+[[package]]
+name = "core_maths"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77745e017f5edba1a9c1d854f6f3a52dac8a12dd5af5d2f54aecf61e43d80d30"
+dependencies = [
+ "libm",
+]
+
 [[package]]
 name = "crc32fast"
 version = "1.5.0"
@@ -339,9 +692,15 @@ dependencies = [
 
 [[package]]
 name = "crossbeam-utils"
-version = "0.8.21"
+version = "0.8.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17"
+
+[[package]]
+name = "crunchy"
+version = "0.2.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
+checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
 
 [[package]]
 name = "cursor-icon"
@@ -376,6 +735,16 @@ version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b"
 
+[[package]]
+name = "dispatch2"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38"
+dependencies = [
+ "bitflags 2.13.1",
+ "objc2 0.6.4",
+]
+
 [[package]]
 name = "displaydoc"
 version = "0.2.6"
@@ -384,7 +753,7 @@ checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.119",
 ]
 
 [[package]]
@@ -417,6 +786,33 @@ version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76"
 
+[[package]]
+name = "endi"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099"
+
+[[package]]
+name = "enumflags2"
+version = "0.7.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef"
+dependencies = [
+ "enumflags2_derive",
+ "serde",
+]
+
+[[package]]
+name = "enumflags2_derive"
+version = "0.7.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+]
+
 [[package]]
 name = "equivalent"
 version = "1.0.2"
@@ -433,6 +829,12 @@ dependencies = [
  "windows-sys 0.61.2",
 ]
 
+[[package]]
+name = "error-code"
+version = "3.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59"
+
 [[package]]
 name = "euclid"
 version = "0.22.14"
@@ -442,6 +844,39 @@ dependencies = [
  "num-traits",
 ]
 
+[[package]]
+name = "event-listener"
+version = "5.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab"
+dependencies = [
+ "concurrent-queue",
+ "parking",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "event-listener-strategy"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93"
+dependencies = [
+ "event-listener",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "fastrand"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223"
+
+[[package]]
+name = "fax"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "caf1079563223d5d59d83c85886a56e586cfd5c1a26292e971a0fa266531ac5a"
+
 [[package]]
 name = "fdeflate"
 version = "0.3.7"
@@ -457,6 +892,12 @@ version = "0.1.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
 
+[[package]]
+name = "fixedbitset"
+version = "0.5.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99"
+
 [[package]]
 name = "flate2"
 version = "1.1.9"
@@ -473,11 +914,17 @@ version = "0.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
 
+[[package]]
+name = "foldhash"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
+
 [[package]]
 name = "font-types"
-version = "0.9.0"
+version = "0.10.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "02a596f5713680923a2080d86de50fe472fb290693cf0f701187a1c8b36996b7"
+checksum = "39a654f404bbcbd48ea58c617c2993ee91d1cb63727a37bf2323a4edeed1b8c5"
 dependencies = [
  "bytemuck",
 ]
@@ -492,36 +939,35 @@ dependencies = [
 ]
 
 [[package]]
-name = "fontconfig-cache-parser"
-version = "0.2.0"
+name = "font-types"
+version = "0.12.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7f8afb20c8069fd676d27b214559a337cc619a605d25a87baa90b49a06f3b18"
+checksum = "0a7299a780854a6d391be2ae1c8521c9368471b559dbfd6a8dbd9f407eaff100"
 dependencies = [
  "bytemuck",
- "thiserror 1.0.69",
 ]
 
 [[package]]
 name = "fontique"
-version = "0.4.0"
+version = "0.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "64763d1f274c8383333851435b6cdf071c31cfcdb39fd5860d20943205a007a7"
+checksum = "ff3336bc0b87fe42305047263fa60d2eabd650d29cbe62fdeb2a66c7a0a595f9"
 dependencies = [
  "bytemuck",
- "fontconfig-cache-parser",
  "hashbrown 0.15.5",
- "icu_locid",
+ "icu_locale_core",
+ "linebender_resource_handle",
  "memmap2",
  "objc2 0.6.4",
  "objc2-core-foundation",
  "objc2-core-text",
  "objc2-foundation 0.3.2",
- "peniko",
- "read-fonts 0.29.3",
+ "read-fonts 0.35.0",
  "roxmltree",
  "smallvec",
- "windows",
- "windows-core",
+ "windows 0.58.0",
+ "windows-core 0.58.0",
+ "yeslogic-fontconfig-sys",
 ]
 
 [[package]]
@@ -536,13 +982,13 @@ dependencies = [
 
 [[package]]
 name = "foreign-types-macros"
-version = "0.2.3"
+version = "0.2.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742"
+checksum = "ea5190182e6915eb873ddbc16e23b711b6eb1f9c00a0d0a3a91b5f6228475225"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 3.0.3",
 ]
 
 [[package]]
@@ -553,9 +999,9 @@ checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b"
 
 [[package]]
 name = "futures-core"
-version = "0.3.32"
+version = "0.3.33"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
+checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7"
 
 [[package]]
 name = "futures-intrusive"
@@ -568,19 +1014,50 @@ dependencies = [
  "parking_lot",
 ]
 
+[[package]]
+name = "futures-io"
+version = "0.3.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4577ecaa3c4f96589d473f679a71b596316f6641bc350038b962a5daf0085d7a"
+
+[[package]]
+name = "futures-lite"
+version = "2.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad"
+dependencies = [
+ "fastrand",
+ "futures-core",
+ "futures-io",
+ "parking",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "futures-macro"
+version = "0.3.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2d6d3cde68c518367be28956066ddfef33813991b77a55005a69dae04bf3b10b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+]
+
 [[package]]
 name = "futures-task"
-version = "0.3.32"
+version = "0.3.33"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
+checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109"
 
 [[package]]
 name = "futures-util"
-version = "0.3.32"
+version = "0.3.33"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
+checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa"
 dependencies = [
  "futures-core",
+ "futures-macro",
  "futures-task",
  "pin-project-lite",
  "slab",
@@ -615,10 +1092,21 @@ checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
 dependencies = [
  "cfg-if",
  "libc",
- "r-efi",
+ "r-efi 5.3.0",
  "wasip2",
 ]
 
+[[package]]
+name = "getrandom"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "r-efi 6.0.0",
+]
+
 [[package]]
 name = "gl_generator"
 version = "0.14.0"
@@ -653,21 +1141,21 @@ dependencies = [
 
 [[package]]
 name = "gpu-alloc"
-version = "0.6.0"
+version = "0.6.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171"
+checksum = "45cf04b2726f02df5508c6de726acdc90cdf97ac771a9a0ffd8ba10a6e696bf9"
 dependencies = [
- "bitflags 2.12.1",
+ "bitflags 2.13.1",
  "gpu-alloc-types",
 ]
 
 [[package]]
 name = "gpu-alloc-types"
-version = "0.3.0"
+version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4"
+checksum = "b2bbed164dd10ed526c2e4fe3e721ca4a71c61730e5aafac6844b417b3227058"
 dependencies = [
- "bitflags 2.12.1",
+ "bitflags 2.13.1",
 ]
 
 [[package]]
@@ -679,7 +1167,7 @@ dependencies = [
  "log",
  "presser",
  "thiserror 1.0.69",
- "windows",
+ "windows 0.58.0",
 ]
 
 [[package]]
@@ -688,7 +1176,7 @@ version = "0.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b89c83349105e3732062a895becfc71a8f921bb71ecbbdd8ff99263e3b53a0ca"
 dependencies = [
- "bitflags 2.12.1",
+ "bitflags 2.13.1",
  "gpu-descriptor-types",
  "hashbrown 0.15.5",
 ]
@@ -699,7 +1187,7 @@ version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91"
 dependencies = [
- "bitflags 2.12.1",
+ "bitflags 2.13.1",
 ]
 
 [[package]]
@@ -718,6 +1206,31 @@ dependencies = [
  "svg_fmt",
 ]
 
+[[package]]
+name = "half"
+version = "2.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b"
+dependencies = [
+ "cfg-if",
+ "crunchy",
+ "num-traits",
+ "zerocopy",
+]
+
+[[package]]
+name = "harfrust"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92c020db12c71d8a12a3fe7607873cade3a01a6287e29d540c8723276221b9d8"
+dependencies = [
+ "bitflags 2.13.1",
+ "bytemuck",
+ "core_maths",
+ "read-fonts 0.35.0",
+ "smallvec",
+]
+
 [[package]]
 name = "hashbrown"
 version = "0.15.5"
@@ -726,20 +1239,23 @@ checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
 dependencies = [
  "allocator-api2",
  "equivalent",
- "foldhash",
+ "foldhash 0.1.5",
 ]
 
 [[package]]
 name = "hashbrown"
-version = "0.17.1"
+version = "0.16.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
+checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
+dependencies = [
+ "foldhash 0.2.0",
+]
 
 [[package]]
-name = "heck"
-version = "0.5.0"
+name = "hashbrown"
+version = "0.17.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
 
 [[package]]
 name = "hermit-abi"
@@ -747,6 +1263,12 @@ version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
 
+[[package]]
+name = "hex"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
+
 [[package]]
 name = "hexf-parse"
 version = "0.2.1"
@@ -754,17 +1276,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
 
 [[package]]
-name = "icu_locid"
-version = "1.5.0"
+name = "icu_locale_core"
+version = "2.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637"
+checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29"
 dependencies = [
  "displaydoc",
  "litemap",
+ "serde",
  "tinystr",
  "writeable",
 ]
 
+[[package]]
+name = "image"
+version = "0.25.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104"
+dependencies = [
+ "bytemuck",
+ "byteorder-lite",
+ "moxcms",
+ "num-traits",
+ "png 0.18.1",
+ "tiff",
+]
+
 [[package]]
 name = "indexmap"
 version = "2.14.0"
@@ -787,7 +1324,7 @@ dependencies = [
  "jni-sys 0.4.1",
  "log",
  "simd_cesu8",
- "thiserror 2.0.18",
+ "thiserror 2.0.19",
  "walkdir",
  "windows-link",
 ]
@@ -802,7 +1339,7 @@ dependencies = [
  "quote",
  "rustc_version",
  "simd_cesu8",
- "syn",
+ "syn 2.0.119",
 ]
 
 [[package]]
@@ -830,28 +1367,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264"
 dependencies = [
  "quote",
- "syn",
+ "syn 2.0.119",
 ]
 
 [[package]]
 name = "jobserver"
-version = "0.1.34"
+version = "0.1.35"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33"
+checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3"
 dependencies = [
- "getrandom 0.3.4",
+ "getrandom 0.4.3",
  "libc",
 ]
 
 [[package]]
 name = "js-sys"
-version = "0.3.99"
+version = "0.3.103"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "142bc4740e452c1e57ade0cbc129f139c9093e354346f0872ef985f4f5cf5f11"
+checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102"
 dependencies = [
  "cfg-if",
  "futures-util",
- "once_cell",
  "wasm-bindgen",
 ]
 
@@ -874,20 +1410,21 @@ checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
 
 [[package]]
 name = "kurbo"
-version = "0.11.3"
+version = "0.13.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c62026ae44756f8a599ba21140f350303d4f08dcdcc71b5ad9c9bb8128c13c62"
+checksum = "4b60dfc32f652b926df6192e55525b16d186c69d47876c3ead4da5cc9f8450e2"
 dependencies = [
  "arrayvec",
  "euclid",
+ "polycool",
  "smallvec",
 ]
 
 [[package]]
 name = "libc"
-version = "0.2.186"
+version = "0.2.189"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
+checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
 
 [[package]]
 name = "libloading"
@@ -899,16 +1436,22 @@ dependencies = [
  "windows-link",
 ]
 
+[[package]]
+name = "libm"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981"
+
 [[package]]
 name = "libredox"
-version = "0.1.17"
+version = "0.1.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f02ab6bace2054fb888a3c16f990117b579d14a3088e472d63c6011fa185c9d3"
+checksum = "c943259e342f1e06ff2da7a83eabdfe7f92ce10262688dbf1895ff0b3e6e4652"
 dependencies = [
- "bitflags 2.12.1",
+ "bitflags 2.13.1",
  "libc",
  "plain",
- "redox_syscall 0.8.1",
+ "redox_syscall 0.9.0",
 ]
 
 [[package]]
@@ -931,9 +1474,9 @@ checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
 
 [[package]]
 name = "litemap"
-version = "0.7.5"
+version = "0.8.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856"
+checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0"
 
 [[package]]
 name = "litrs"
@@ -944,7 +1487,7 @@ checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092"
 [[package]]
 name = "llimphi-compositor"
 version = "0.1.0"
-source = "git+https://gitea.tawasuyu.net/sergio/llimphi.git?tag=v0.1.0#c1b11b2c01d7623a4f346519b9d1ec435be30b95"
+source = "git+https://git.tawasuyu.net/tawasuyu/llimphi.git?tag=v0.2.0#93253cc227130680427a9a83bdc504bd82ca71ca"
 dependencies = [
  "llimphi-layout",
  "llimphi-text",
@@ -955,7 +1498,7 @@ dependencies = [
 [[package]]
 name = "llimphi-hal"
 version = "0.1.0"
-source = "git+https://gitea.tawasuyu.net/sergio/llimphi.git?tag=v0.1.0#c1b11b2c01d7623a4f346519b9d1ec435be30b95"
+source = "git+https://git.tawasuyu.net/tawasuyu/llimphi.git?tag=v0.2.0#93253cc227130680427a9a83bdc504bd82ca71ca"
 dependencies = [
  "pollster",
  "raw-window-handle",
@@ -963,10 +1506,18 @@ dependencies = [
  "winit",
 ]
 
+[[package]]
+name = "llimphi-icons"
+version = "0.1.0"
+source = "git+https://git.tawasuyu.net/tawasuyu/llimphi.git?tag=v0.2.0#93253cc227130680427a9a83bdc504bd82ca71ca"
+dependencies = [
+ "llimphi-ui",
+]
+
 [[package]]
 name = "llimphi-layout"
 version = "0.1.0"
-source = "git+https://gitea.tawasuyu.net/sergio/llimphi.git?tag=v0.1.0#c1b11b2c01d7623a4f346519b9d1ec435be30b95"
+source = "git+https://git.tawasuyu.net/tawasuyu/llimphi.git?tag=v0.2.0#93253cc227130680427a9a83bdc504bd82ca71ca"
 dependencies = [
  "taffy",
 ]
@@ -974,7 +1525,7 @@ dependencies = [
 [[package]]
 name = "llimphi-raster"
 version = "0.1.0"
-source = "git+https://gitea.tawasuyu.net/sergio/llimphi.git?tag=v0.1.0#c1b11b2c01d7623a4f346519b9d1ec435be30b95"
+source = "git+https://git.tawasuyu.net/tawasuyu/llimphi.git?tag=v0.2.0#93253cc227130680427a9a83bdc504bd82ca71ca"
 dependencies = [
  "llimphi-hal",
  "pollster",
@@ -984,7 +1535,7 @@ dependencies = [
 [[package]]
 name = "llimphi-text"
 version = "0.1.0"
-source = "git+https://gitea.tawasuyu.net/sergio/llimphi.git?tag=v0.1.0#c1b11b2c01d7623a4f346519b9d1ec435be30b95"
+source = "git+https://git.tawasuyu.net/tawasuyu/llimphi.git?tag=v0.2.0#93253cc227130680427a9a83bdc504bd82ca71ca"
 dependencies = [
  "parley",
  "vello",
@@ -993,7 +1544,7 @@ dependencies = [
 [[package]]
 name = "llimphi-theme"
 version = "0.1.0"
-source = "git+https://gitea.tawasuyu.net/sergio/llimphi.git?tag=v0.1.0#c1b11b2c01d7623a4f346519b9d1ec435be30b95"
+source = "git+https://git.tawasuyu.net/tawasuyu/llimphi.git?tag=v0.2.0#93253cc227130680427a9a83bdc504bd82ca71ca"
 dependencies = [
  "llimphi-raster",
 ]
@@ -1001,20 +1552,25 @@ dependencies = [
 [[package]]
 name = "llimphi-ui"
 version = "0.1.0"
-source = "git+https://gitea.tawasuyu.net/sergio/llimphi.git?tag=v0.1.0#c1b11b2c01d7623a4f346519b9d1ec435be30b95"
+source = "git+https://git.tawasuyu.net/tawasuyu/llimphi.git?tag=v0.2.0#93253cc227130680427a9a83bdc504bd82ca71ca"
 dependencies = [
+ "accesskit",
+ "accesskit_winit",
+ "arboard",
  "llimphi-compositor",
  "llimphi-hal",
  "llimphi-layout",
  "llimphi-raster",
  "llimphi-text",
+ "log",
  "pollster",
+ "uuid",
 ]
 
 [[package]]
 name = "llimphi-widget-button"
 version = "0.1.0"
-source = "git+https://gitea.tawasuyu.net/sergio/llimphi.git?tag=v0.1.0#c1b11b2c01d7623a4f346519b9d1ec435be30b95"
+source = "git+https://git.tawasuyu.net/tawasuyu/llimphi.git?tag=v0.2.0#93253cc227130680427a9a83bdc504bd82ca71ca"
 dependencies = [
  "llimphi-theme",
  "llimphi-ui",
@@ -1023,8 +1579,9 @@ dependencies = [
 [[package]]
 name = "llimphi-widget-context-menu"
 version = "0.1.0"
-source = "git+https://gitea.tawasuyu.net/sergio/llimphi.git?tag=v0.1.0#c1b11b2c01d7623a4f346519b9d1ec435be30b95"
+source = "git+https://git.tawasuyu.net/tawasuyu/llimphi.git?tag=v0.2.0#93253cc227130680427a9a83bdc504bd82ca71ca"
 dependencies = [
+ "llimphi-icons",
  "llimphi-theme",
  "llimphi-ui",
  "llimphi-widget-panel",
@@ -1033,7 +1590,7 @@ dependencies = [
 [[package]]
 name = "llimphi-widget-menubar"
 version = "0.1.0"
-source = "git+https://gitea.tawasuyu.net/sergio/llimphi.git?tag=v0.1.0#c1b11b2c01d7623a4f346519b9d1ec435be30b95"
+source = "git+https://git.tawasuyu.net/tawasuyu/llimphi.git?tag=v0.2.0#93253cc227130680427a9a83bdc504bd82ca71ca"
 dependencies = [
  "app-bus",
  "llimphi-theme",
@@ -1045,7 +1602,7 @@ dependencies = [
 [[package]]
 name = "llimphi-widget-panel"
 version = "0.1.0"
-source = "git+https://gitea.tawasuyu.net/sergio/llimphi.git?tag=v0.1.0#c1b11b2c01d7623a4f346519b9d1ec435be30b95"
+source = "git+https://git.tawasuyu.net/tawasuyu/llimphi.git?tag=v0.2.0#93253cc227130680427a9a83bdc504bd82ca71ca"
 dependencies = [
  "llimphi-theme",
  "llimphi-ui",
@@ -1062,9 +1619,9 @@ dependencies = [
 
 [[package]]
 name = "log"
-version = "0.4.32"
+version = "0.4.33"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a"
+checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad"
 
 [[package]]
 name = "malloc_buf"
@@ -1077,28 +1634,37 @@ dependencies = [
 
 [[package]]
 name = "memchr"
-version = "2.8.1"
+version = "2.8.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8"
+checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
 
 [[package]]
 name = "memmap2"
-version = "0.9.10"
+version = "0.9.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3"
+checksum = "d1219ed1b7f229ee7104d281dd01d6802fe28bb6e95d292942c4daacdeb798c0"
 dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "memoffset"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
+dependencies = [
+ "autocfg",
+]
+
 [[package]]
 name = "metal"
-version = "0.31.0"
+version = "0.32.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f569fb946490b5743ad69813cb19629130ce9374034abe31614a36402d18f99e"
+checksum = "00c15a6f673ff72ddcc22394663290f870fb224c1bfce55734a75c414150e605"
 dependencies = [
- "bitflags 2.12.1",
+ "bitflags 2.13.1",
  "block",
- "core-graphics-types",
+ "core-graphics-types 0.2.0",
  "foreign-types",
  "log",
  "objc",
@@ -1115,26 +1681,40 @@ dependencies = [
  "simd-adler32",
 ]
 
+[[package]]
+name = "moxcms"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b"
+dependencies = [
+ "num-traits",
+ "pxfm",
+]
+
 [[package]]
 name = "naga"
-version = "24.0.0"
+version = "27.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e380993072e52eef724eddfcde0ed013b0c023c3f0417336ed041aa9f076994e"
+checksum = "066cf25f0e8b11ee0df221219010f213ad429855f57c494f995590c861a9a7d8"
 dependencies = [
  "arrayvec",
  "bit-set",
- "bitflags 2.12.1",
+ "bitflags 2.13.1",
+ "cfg-if",
  "cfg_aliases",
  "codespan-reporting",
+ "half",
+ "hashbrown 0.16.1",
  "hexf-parse",
  "indexmap",
+ "libm",
  "log",
+ "num-traits",
+ "once_cell",
  "rustc-hash",
  "spirv",
- "strum",
- "termcolor",
- "thiserror 2.0.18",
- "unicode-xid",
+ "thiserror 2.0.19",
+ "unicode-ident",
 ]
 
 [[package]]
@@ -1143,10 +1723,10 @@ version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4"
 dependencies = [
- "bitflags 2.12.1",
+ "bitflags 2.13.1",
  "jni-sys 0.3.1",
  "log",
- "ndk-sys 0.6.0+11769913",
+ "ndk-sys",
  "num_enum",
  "raw-window-handle",
  "thiserror 1.0.69",
@@ -1160,20 +1740,20 @@ checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b"
 
 [[package]]
 name = "ndk-sys"
-version = "0.5.0+25.2.9519653"
+version = "0.6.0+11769913"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691"
+checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873"
 dependencies = [
  "jni-sys 0.3.1",
 ]
 
 [[package]]
-name = "ndk-sys"
-version = "0.6.0+11769913"
+name = "nom"
+version = "8.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873"
+checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405"
 dependencies = [
- "jni-sys 0.3.1",
+ "memchr",
 ]
 
 [[package]]
@@ -1183,6 +1763,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
 dependencies = [
  "autocfg",
+ "libm",
 ]
 
 [[package]]
@@ -1204,7 +1785,7 @@ dependencies = [
  "proc-macro-crate",
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.119",
 ]
 
 [[package]]
@@ -1247,7 +1828,7 @@ version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff"
 dependencies = [
- "bitflags 2.12.1",
+ "bitflags 2.13.1",
  "block2",
  "libc",
  "objc2 0.5.2",
@@ -1257,13 +1838,25 @@ dependencies = [
  "objc2-quartz-core",
 ]
 
+[[package]]
+name = "objc2-app-kit"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c"
+dependencies = [
+ "bitflags 2.13.1",
+ "objc2 0.6.4",
+ "objc2-core-graphics",
+ "objc2-foundation 0.3.2",
+]
+
 [[package]]
 name = "objc2-cloud-kit"
 version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009"
 dependencies = [
- "bitflags 2.12.1",
+ "bitflags 2.13.1",
  "block2",
  "objc2 0.5.2",
  "objc2-core-location",
@@ -1287,7 +1880,7 @@ version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef"
 dependencies = [
- "bitflags 2.12.1",
+ "bitflags 2.13.1",
  "block2",
  "objc2 0.5.2",
  "objc2-foundation 0.2.2",
@@ -1299,7 +1892,22 @@ version = "0.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536"
 dependencies = [
- "bitflags 2.12.1",
+ "bitflags 2.13.1",
+ "dispatch2",
+ "objc2 0.6.4",
+]
+
+[[package]]
+name = "objc2-core-graphics"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807"
+dependencies = [
+ "bitflags 2.13.1",
+ "dispatch2",
+ "objc2 0.6.4",
+ "objc2-core-foundation",
+ "objc2-io-surface",
 ]
 
 [[package]]
@@ -1332,7 +1940,7 @@ version = "0.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d"
 dependencies = [
- "bitflags 2.12.1",
+ "bitflags 2.13.1",
  "objc2-core-foundation",
 ]
 
@@ -1348,7 +1956,7 @@ version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8"
 dependencies = [
- "bitflags 2.12.1",
+ "bitflags 2.13.1",
  "block2",
  "dispatch",
  "libc",
@@ -1361,8 +1969,20 @@ version = "0.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272"
 dependencies = [
- "bitflags 2.12.1",
+ "bitflags 2.13.1",
+ "objc2 0.6.4",
+ "objc2-core-foundation",
+]
+
+[[package]]
+name = "objc2-io-surface"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d"
+dependencies = [
+ "bitflags 2.13.1",
  "objc2 0.6.4",
+ "objc2-core-foundation",
 ]
 
 [[package]]
@@ -1373,7 +1993,7 @@ checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398"
 dependencies = [
  "block2",
  "objc2 0.5.2",
- "objc2-app-kit",
+ "objc2-app-kit 0.2.2",
  "objc2-foundation 0.2.2",
 ]
 
@@ -1383,7 +2003,7 @@ version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6"
 dependencies = [
- "bitflags 2.12.1",
+ "bitflags 2.13.1",
  "block2",
  "objc2 0.5.2",
  "objc2-foundation 0.2.2",
@@ -1395,7 +2015,7 @@ version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a"
 dependencies = [
- "bitflags 2.12.1",
+ "bitflags 2.13.1",
  "block2",
  "objc2 0.5.2",
  "objc2-foundation 0.2.2",
@@ -1418,7 +2038,7 @@ version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f"
 dependencies = [
- "bitflags 2.12.1",
+ "bitflags 2.13.1",
  "block2",
  "objc2 0.5.2",
  "objc2-cloud-kit",
@@ -1450,7 +2070,7 @@ version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3"
 dependencies = [
- "bitflags 2.12.1",
+ "bitflags 2.13.1",
  "block2",
  "objc2 0.5.2",
  "objc2-core-location",
@@ -1481,13 +2101,33 @@ dependencies = [
 
 [[package]]
 name = "ordered-float"
-version = "4.6.0"
+version = "5.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7bb71e1b3fa6ca1c61f383464aaf2bb0e2f8e772a1f01d486832464de363b951"
+checksum = "b7d950ca161dc355eaf28f82b11345ed76c6e1f6eb1f4f4479e0323b9e2fbd0e"
 dependencies = [
  "num-traits",
 ]
 
+[[package]]
+name = "ordered-stream"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50"
+dependencies = [
+ "futures-core",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "os_pipe"
+version = "1.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967"
+dependencies = [
+ "libc",
+ "windows-sys 0.61.2",
+]
+
 [[package]]
 name = "owned_ttf_parser"
 version = "0.25.1"
@@ -1497,6 +2137,12 @@ dependencies = [
  "ttf-parser",
 ]
 
+[[package]]
+name = "parking"
+version = "2.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba"
+
 [[package]]
 name = "parking_lot"
 version = "0.12.5"
@@ -1522,14 +2168,15 @@ dependencies = [
 
 [[package]]
 name = "parley"
-version = "0.4.0"
+version = "0.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e28dadbe655332fd7d996794ec8d0c376695f6ca47bc75aa01e0967c7f28e42a"
+checksum = "26746861bb76dbc9bcd5ed1b0b55d2fedf291100961251702a031ab2abd2ce52"
 dependencies = [
  "fontique",
+ "harfrust",
  "hashbrown 0.15.5",
- "peniko",
- "skrifa 0.31.3",
+ "linebender_resource_handle",
+ "skrifa 0.37.0",
  "swash",
 ]
 
@@ -1541,9 +2188,9 @@ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
 
 [[package]]
 name = "peniko"
-version = "0.4.1"
+version = "0.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b44f9ddd2f480176b34278eb653ec1c8062f3b143a4e16eeff5ffac3334e288"
+checksum = "839c8299360d2e998bdb106dc0a6cd71dcc5f4df51df1b620361bf50e283cca6"
 dependencies = [
  "color",
  "kurbo",
@@ -1557,6 +2204,60 @@ version = "2.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
 
+[[package]]
+name = "petgraph"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455"
+dependencies = [
+ "fixedbitset",
+ "hashbrown 0.15.5",
+ "indexmap",
+]
+
+[[package]]
+name = "phf"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf"
+dependencies = [
+ "phf_macros",
+ "phf_shared",
+ "serde",
+]
+
+[[package]]
+name = "phf_generator"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737"
+dependencies = [
+ "fastrand",
+ "phf_shared",
+]
+
+[[package]]
+name = "phf_macros"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef"
+dependencies = [
+ "phf_generator",
+ "phf_shared",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+]
+
+[[package]]
+name = "phf_shared"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266"
+dependencies = [
+ "siphasher",
+]
+
 [[package]]
 name = "pin-project"
 version = "1.1.13"
@@ -1574,7 +2275,7 @@ checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.119",
 ]
 
 [[package]]
@@ -1608,7 +2309,6 @@ dependencies = [
 name = "pineal-bars"
 version = "0.1.0"
 dependencies = [
- "pineal-core",
  "pineal-render",
 ]
 
@@ -1689,7 +2389,6 @@ version = "0.1.0"
 dependencies = [
  "llimphi-ui",
  "pineal-cartesian",
- "pineal-core",
  "pineal-render",
 ]
 
@@ -1711,7 +2410,6 @@ dependencies = [
 name = "pineal-flow"
 version = "0.1.0"
 dependencies = [
- "pineal-core",
  "pineal-render",
 ]
 
@@ -1737,7 +2435,9 @@ dependencies = [
  "llimphi-ui",
  "llimphi-widget-menubar",
  "pineal-bars",
+ "pineal-cartesian",
  "pineal-contour",
+ "pineal-financial",
  "pineal-flow",
  "pineal-heatmap",
  "pineal-hexbin",
@@ -1745,6 +2445,8 @@ dependencies = [
  "pineal-polar",
  "pineal-render",
  "pineal-treemap",
+ "png 0.18.1",
+ "pollster",
 ]
 
 [[package]]
@@ -1764,7 +2466,6 @@ dependencies = [
 name = "pineal-heatmap"
 version = "0.1.0"
 dependencies = [
- "pineal-core",
  "pineal-render",
 ]
 
@@ -1807,7 +2508,6 @@ dependencies = [
 name = "pineal-mesh"
 version = "0.1.0"
 dependencies = [
- "pineal-core",
  "pineal-render",
 ]
 
@@ -1851,7 +2551,6 @@ dependencies = [
 name = "pineal-polar"
 version = "0.1.0"
 dependencies = [
- "pineal-core",
  "pineal-render",
 ]
 
@@ -1873,7 +2572,6 @@ name = "pineal-render"
 version = "0.1.0"
 dependencies = [
  "llimphi-ui",
- "pineal-core",
 ]
 
 [[package]]
@@ -1903,7 +2601,6 @@ dependencies = [
 name = "pineal-treemap"
 version = "0.1.0"
 dependencies = [
- "pineal-core",
  "pineal-render",
 ]
 
@@ -1920,6 +2617,17 @@ dependencies = [
  "pineal-treemap",
 ]
 
+[[package]]
+name = "piper"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1"
+dependencies = [
+ "atomic-waker",
+ "fastrand",
+ "futures-io",
+]
+
 [[package]]
 name = "pkg-config"
 version = "0.3.33"
@@ -1951,7 +2659,7 @@ version = "0.18.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61"
 dependencies = [
- "bitflags 2.12.1",
+ "bitflags 2.13.1",
  "crc32fast",
  "fdeflate",
  "flate2",
@@ -1978,6 +2686,30 @@ version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3"
 
+[[package]]
+name = "polycool"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50596ddc09eb5ad5f75cacd40209568e66df71baf86e1499a0e99c4cff12a5a6"
+dependencies = [
+ "arrayvec",
+]
+
+[[package]]
+name = "portable-atomic"
+version = "1.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d20d5497ef88037a52ff98267d066e7f11fcc5e99bbfbd58a42336193aacec3"
+
+[[package]]
+name = "portable-atomic-util"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618"
+dependencies = [
+ "portable-atomic",
+]
+
 [[package]]
 name = "presser"
 version = "0.3.1"
@@ -1990,14 +2722,14 @@ version = "3.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f"
 dependencies = [
- "toml_edit 0.25.12+spec-1.1.0",
+ "toml_edit 0.25.13+spec-1.1.0",
 ]
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.106"
+version = "1.0.107"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
+checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
 dependencies = [
  "unicode-ident",
 ]
@@ -2008,20 +2740,32 @@ version = "1.0.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3d595e54a326bc53c1c197b32d295e14b169e3cfeaa8dc82b529f947fba6bcf5"
 
+[[package]]
+name = "pxfm"
+version = "0.1.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d55d956fa96f5ec02be2e13af0e20391a5aa83d6a074e3ad368959d0fab299ea"
+
+[[package]]
+name = "quick-error"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
+
 [[package]]
 name = "quick-xml"
-version = "0.39.4"
+version = "0.41.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cdcc8dd4e2f670d309a5f0e83fe36dfdc05af317008fea29144da1a2ac858e5e"
+checksum = "e660451e55124f798a69a5af3f49ccfbefbd41910eefd25caf2393e1f3473ec1"
 dependencies = [
  "memchr",
 ]
 
 [[package]]
 name = "quote"
-version = "1.0.45"
+version = "1.0.47"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
+checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
 dependencies = [
  "proc-macro2",
 ]
@@ -2032,6 +2776,12 @@ version = "5.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
 
+[[package]]
+name = "r-efi"
+version = "6.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
+
 [[package]]
 name = "range-alloc"
 version = "0.1.5"
@@ -2046,32 +2796,34 @@ checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539"
 
 [[package]]
 name = "read-fonts"
-version = "0.29.3"
+version = "0.35.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "04ca636dac446b5664bd16c069c00a9621806895b8bb02c2dc68542b23b8f25d"
+checksum = "6717cf23b488adf64b9d711329542ba34de147df262370221940dfabc2c91358"
 dependencies = [
  "bytemuck",
- "font-types 0.9.0",
+ "core_maths",
+ "font-types 0.10.1",
 ]
 
 [[package]]
 name = "read-fonts"
-version = "0.33.1"
+version = "0.37.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50ea612a55c08586a1d15134be8a776186c440c312ebda3b9e8efbfe4255b7f4"
+checksum = "7b634fabf032fab15307ffd272149b622260f55974d9fad689292a5d33df02e5"
 dependencies = [
  "bytemuck",
- "font-types 0.9.0",
+ "font-types 0.11.3",
 ]
 
 [[package]]
 name = "read-fonts"
-version = "0.37.0"
+version = "0.41.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b634fabf032fab15307ffd272149b622260f55974d9fad689292a5d33df02e5"
+checksum = "046a7d674daf459825b32f5062056d6882db0d2f5a479fbd76ccfc870ac18709"
 dependencies = [
  "bytemuck",
- "font-types 0.11.3",
+ "font-types 0.12.2",
+ "once_cell",
 ]
 
 [[package]]
@@ -2089,16 +2841,16 @@ version = "0.5.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
 dependencies = [
- "bitflags 2.12.1",
+ "bitflags 2.13.1",
 ]
 
 [[package]]
 name = "redox_syscall"
-version = "0.8.1"
+version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b44b894f2a6e36457d665d1e08c3866add6ed5e70050c1b4ba8a8ddedb02ce7"
+checksum = "c5102a6aaa05aa011a238e178e6bca86d2cb56fc9f586d37cb80f5bca6e07759"
 dependencies = [
- "bitflags 2.12.1",
+ "bitflags 2.13.1",
 ]
 
 [[package]]
@@ -2145,7 +2897,7 @@ version = "0.38.44"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
 dependencies = [
- "bitflags 2.12.1",
+ "bitflags 2.13.1",
  "errno",
  "libc",
  "linux-raw-sys 0.4.15",
@@ -2158,7 +2910,7 @@ version = "1.1.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
 dependencies = [
- "bitflags 2.12.1",
+ "bitflags 2.13.1",
  "errno",
  "libc",
  "linux-raw-sys 0.12.1",
@@ -2167,9 +2919,9 @@ dependencies = [
 
 [[package]]
 name = "rustversion"
-version = "1.0.22"
+version = "1.0.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
+checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
 
 [[package]]
 name = "same-file"
@@ -2213,9 +2965,9 @@ checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
 
 [[package]]
 name = "serde"
-version = "1.0.228"
+version = "1.0.229"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
+checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
 dependencies = [
  "serde_core",
  "serde_derive",
@@ -2223,22 +2975,33 @@ dependencies = [
 
 [[package]]
 name = "serde_core"
-version = "1.0.228"
+version = "1.0.229"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
+checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.228"
+version = "1.0.229"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 3.0.3",
+]
+
+[[package]]
+name = "serde_repr"
+version = "0.1.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
+checksum = "8d3b1629de253c70a0508c3899572da79ca359fdab27c7920ff00406df418906"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 3.0.3",
 ]
 
 [[package]]
@@ -2256,17 +3019,27 @@ version = "2.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
 
+[[package]]
+name = "signal-hook-registry"
+version = "1.4.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b"
+dependencies = [
+ "errno",
+ "libc",
+]
+
 [[package]]
 name = "simd-adler32"
-version = "0.3.9"
+version = "0.3.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214"
+checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea"
 
 [[package]]
 name = "simd_cesu8"
-version = "1.1.1"
+version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "94f90157bb87cddf702797c5dadfa0be7d266cdf49e22da2fcaa32eff75b2c33"
+checksum = "11031e251abf8611c80f460e19dbdeb54a66db918e49c65a7065b46ac7aec520"
 dependencies = [
  "rustc_version",
  "simdutf8",
@@ -2278,34 +3051,40 @@ version = "0.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e"
 
+[[package]]
+name = "siphasher"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649"
+
 [[package]]
 name = "skrifa"
-version = "0.31.3"
+version = "0.37.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dbeb4ca4399663735553a09dd17ce7e49a0a0203f03b706b39628c4d913a8607"
+checksum = "8c31071dedf532758ecf3fed987cdb4bd9509f900e026ab684b4ecb81ea49841"
 dependencies = [
  "bytemuck",
- "read-fonts 0.29.3",
+ "read-fonts 0.35.0",
 ]
 
 [[package]]
 name = "skrifa"
-version = "0.35.0"
+version = "0.40.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "576e60c7de4bb6a803a0312f9bef17e78cf1e8d25a80e1ade76770d7a0237955"
+checksum = "7fbdfe3d2475fbd7ddd1f3e5cf8288a30eb3e5f95832829570cd88115a7434ac"
 dependencies = [
  "bytemuck",
- "read-fonts 0.33.1",
+ "read-fonts 0.37.0",
 ]
 
 [[package]]
 name = "skrifa"
-version = "0.40.0"
+version = "0.44.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7fbdfe3d2475fbd7ddd1f3e5cf8288a30eb3e5f95832829570cd88115a7434ac"
+checksum = "819ab7d62b1d3e72d9d9dea5650bac30424f9111364bb94928dbf5ecad1baa68"
 dependencies = [
  "bytemuck",
- "read-fonts 0.37.0",
+ "read-fonts 0.41.0",
 ]
 
 [[package]]
@@ -2325,9 +3104,9 @@ dependencies = [
 
 [[package]]
 name = "smallvec"
-version = "1.15.1"
+version = "1.15.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
+checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90"
 
 [[package]]
 name = "smithay-client-toolkit"
@@ -2335,7 +3114,7 @@ version = "0.19.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016"
 dependencies = [
- "bitflags 2.12.1",
+ "bitflags 2.13.1",
  "calloop",
  "calloop-wayland-source",
  "cursor-icon",
@@ -2369,7 +3148,7 @@ version = "0.3.0+sdk-1.3.268.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844"
 dependencies = [
- "bitflags 2.12.1",
+ "bitflags 2.13.1",
 ]
 
 [[package]]
@@ -2384,28 +3163,6 @@ version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731"
 
-[[package]]
-name = "strum"
-version = "0.26.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06"
-dependencies = [
- "strum_macros",
-]
-
-[[package]]
-name = "strum_macros"
-version = "0.26.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be"
-dependencies = [
- "heck",
- "proc-macro2",
- "quote",
- "rustversion",
- "syn",
-]
-
 [[package]]
 name = "svg_fmt"
 version = "0.4.5"
@@ -2414,20 +3171,31 @@ checksum = "0193cc4331cfd2f3d2011ef287590868599a2f33c3e69bc22c1a3d3acf9e02fb"
 
 [[package]]
 name = "swash"
-version = "0.2.7"
+version = "0.2.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "842f3cd369c2ba38966204f983eaa5e54a8e84a7d7159ed36ade2b6c335aae64"
+checksum = "6c2499c2d826531388872b2268718aed907a39bd785ab0dcfe57fab26283f92e"
 dependencies = [
- "skrifa 0.40.0",
+ "skrifa 0.44.0",
  "yazi",
  "zeno",
 ]
 
 [[package]]
 name = "syn"
-version = "2.0.117"
+version = "2.0.119"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "3.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
+checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -2446,6 +3214,19 @@ dependencies = [
  "slotmap",
 ]
 
+[[package]]
+name = "tempfile"
+version = "3.27.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
+dependencies = [
+ "fastrand",
+ "getrandom 0.4.3",
+ "once_cell",
+ "rustix 1.1.4",
+ "windows-sys 0.61.2",
+]
+
 [[package]]
 name = "termcolor"
 version = "1.4.1"
@@ -2466,11 +3247,11 @@ dependencies = [
 
 [[package]]
 name = "thiserror"
-version = "2.0.18"
+version = "2.0.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
+checksum = "09a43598840e33d5b0331f38c5e30d13bb11c11210a4b58f0d9b18a5a5eefcd9"
 dependencies = [
- "thiserror-impl 2.0.18",
+ "thiserror-impl 2.0.19",
 ]
 
 [[package]]
@@ -2481,18 +3262,32 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.119",
 ]
 
 [[package]]
 name = "thiserror-impl"
-version = "2.0.18"
+version = "2.0.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
+checksum = "43cbfe0cf76104d42a574802844187e84a305e531ed54455f11fbde0f10541cd"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 3.0.3",
+]
+
+[[package]]
+name = "tiff"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b63feaf3343d35b6ca4d50483f94843803b0f51634937cc2ec519fc32232bc52"
+dependencies = [
+ "fax",
+ "flate2",
+ "half",
+ "quick-error",
+ "weezl",
+ "zune-jpeg",
 ]
 
 [[package]]
@@ -2522,11 +3317,13 @@ dependencies = [
 
 [[package]]
 name = "tinystr"
-version = "0.7.6"
+version = "0.8.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f"
+checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d"
 dependencies = [
  "displaydoc",
+ "serde_core",
+ "zerovec",
 ]
 
 [[package]]
@@ -2575,14 +3372,14 @@ dependencies = [
 
 [[package]]
 name = "toml_edit"
-version = "0.25.12+spec-1.1.0"
+version = "0.25.13+spec-1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7"
+checksum = "6975367e4d2ef766d86af01ffad14b622fecc8d4357a998fbc4deb6e9bacaf9b"
 dependencies = [
  "indexmap",
  "toml_datetime 1.1.1+spec-1.1.0",
  "toml_parser",
- "winnow 1.0.3",
+ "winnow 1.0.4",
 ]
 
 [[package]]
@@ -2591,7 +3388,7 @@ version = "1.1.2+spec-1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526"
 dependencies = [
- "winnow 1.0.3",
+ "winnow 1.0.4",
 ]
 
 [[package]]
@@ -2607,14 +3404,40 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
 dependencies = [
  "pin-project-lite",
+ "tracing-attributes",
  "tracing-core",
 ]
 
+[[package]]
+name = "tracing-attributes"
+version = "0.1.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+]
+
 [[package]]
 name = "tracing-core"
 version = "0.1.36"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
+dependencies = [
+ "once_cell",
+]
+
+[[package]]
+name = "tree_magic_mini"
+version = "3.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8765b90061cba6c22b5831f675da109ae5561588290f9fa2317adab2714d5a6"
+dependencies = [
+ "memchr",
+ "nom",
+ "petgraph",
+]
 
 [[package]]
 name = "ttf-parser"
@@ -2622,6 +3445,17 @@ version = "0.25.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31"
 
+[[package]]
+name = "uds_windows"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e"
+dependencies = [
+ "memoffset",
+ "tempfile",
+ "windows-sys 0.61.2",
+]
+
 [[package]]
 name = "unicode-ident"
 version = "1.0.24"
@@ -2636,30 +3470,36 @@ checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8"
 
 [[package]]
 name = "unicode-width"
-version = "0.1.14"
+version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
+checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
 
 [[package]]
-name = "unicode-xid"
-version = "0.2.6"
+name = "uuid"
+version = "1.24.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
+checksum = "bf3923a6f5c4c6382e0b653c4117f48d631ea17f38ed86e2a828e6f7412f5239"
+dependencies = [
+ "getrandom 0.4.3",
+ "js-sys",
+ "serde_core",
+ "wasm-bindgen",
+]
 
 [[package]]
 name = "vello"
-version = "0.5.1"
+version = "0.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa3f8a53870a2ee699ce05b738a3f9974c92c35ed4874de86052ac68d214811c"
+checksum = "72fef40773530322d5c2ffe3c1107e9874bd8239ac137d1c2b6c1edad695146e"
 dependencies = [
  "bytemuck",
  "futures-intrusive",
  "log",
  "peniko",
  "png 0.17.16",
- "skrifa 0.35.0",
+ "skrifa 0.40.0",
  "static_assertions",
- "thiserror 2.0.18",
+ "thiserror 2.0.19",
  "vello_encoding",
  "vello_shaders",
  "wgpu",
@@ -2667,26 +3507,27 @@ dependencies = [
 
 [[package]]
 name = "vello_encoding"
-version = "0.5.1"
+version = "0.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c69b0fe94b0ac7e47619c504ee2c377355174f5c46353c46d03fa5f7e435922b"
+checksum = "24c91203ec4b483440614a9a5c7c2d991932af72c5349659a63ec49476f0b79c"
 dependencies = [
  "bytemuck",
  "guillotiere",
  "peniko",
- "skrifa 0.35.0",
+ "skrifa 0.40.0",
  "smallvec",
 ]
 
 [[package]]
 name = "vello_shaders"
-version = "0.5.1"
+version = "0.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b2ebea426bb2f95b7610bca09178b03d809ede1d3c500a9acf6eca43e8f200be"
+checksum = "7a765d44d4bd354146e44f9a860f4e92effd91a97302549be9e47f0a18d8128c"
 dependencies = [
  "bytemuck",
+ "log",
  "naga",
- "thiserror 2.0.18",
+ "thiserror 2.0.19",
  "vello_encoding",
 ]
 
@@ -2714,18 +3555,18 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
 
 [[package]]
 name = "wasip2"
-version = "1.0.3+wasi-0.2.9"
+version = "1.0.4+wasi-0.2.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6"
+checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487"
 dependencies = [
  "wit-bindgen",
 ]
 
 [[package]]
 name = "wasm-bindgen"
-version = "0.2.122"
+version = "0.2.126"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3ed04576f974d2b2fba0f38c51dbc5518011e38c36bf1143164be765528fd409"
+checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4"
 dependencies = [
  "cfg-if",
  "once_cell",
@@ -2736,9 +3577,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-futures"
-version = "0.4.72"
+version = "0.4.76"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9473dbd2991ae90b6291c3c32c30c6187ac49aa32f9905d1cce280ec1e110b0f"
+checksum = "c62df1340f32221cb9c54d6a27b030e3dba64361d4a95bed55f9aacb44da291d"
 dependencies = [
  "js-sys",
  "wasm-bindgen",
@@ -2746,9 +3587,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro"
-version = "0.2.122"
+version = "0.2.126"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "916151b09da36bd82f6615cbf3a419e2f0ba23a03c6160e8e92eb6bd4aa1dec6"
+checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1"
 dependencies = [
  "quote",
  "wasm-bindgen-macro-support",
@@ -2756,31 +3597,31 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro-support"
-version = "0.2.122"
+version = "0.2.126"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "299047362ccbfce148b67ab7e73349f77748e00c8296f9542adfad2ad82c5c5e"
+checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e"
 dependencies = [
  "bumpalo",
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.119",
  "wasm-bindgen-shared",
 ]
 
 [[package]]
 name = "wasm-bindgen-shared"
-version = "0.2.122"
+version = "0.2.126"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a929b2c61f11ba3e9bc35b50c1f25cb38e0e892c0c231ae2b8cf78d5dad4437"
+checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24"
 dependencies = [
  "unicode-ident",
 ]
 
 [[package]]
 name = "wayland-backend"
-version = "0.3.15"
+version = "0.3.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2857dd20b54e916ec7253b3d6b4d5c4d7d4ca2c33c2e11c6c76a99bd8744755d"
+checksum = "016ccf01d1c58b6f8999612813e17c9b2390f7d70671428869913310f83f54b8"
 dependencies = [
  "cc",
  "downcast-rs",
@@ -2792,11 +3633,11 @@ dependencies = [
 
 [[package]]
 name = "wayland-client"
-version = "0.31.14"
+version = "0.31.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "645c7c96bb74690c3189b5c9cb4ca1627062bb23693a4fad9d8c3de958260144"
+checksum = "e3c36a0f861ad76d0901f2800b46321410d9f73f2ea88aac0650d86c32688073"
 dependencies = [
- "bitflags 2.12.1",
+ "bitflags 2.13.1",
  "rustix 1.1.4",
  "wayland-backend",
  "wayland-scanner",
@@ -2808,7 +3649,7 @@ version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e"
 dependencies = [
- "bitflags 2.12.1",
+ "bitflags 2.13.1",
  "cursor-icon",
  "wayland-backend",
 ]
@@ -2826,11 +3667,11 @@ dependencies = [
 
 [[package]]
 name = "wayland-protocols"
-version = "0.32.12"
+version = "0.32.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "563a85523cade2429938e790815fd7319062103b9f4a2dc806e9b53b95982d8f"
+checksum = "23d0c813de3daa2ed6520af85a3bd49b0e722a3078506899aa9686fea58dc4b6"
 dependencies = [
- "bitflags 2.12.1",
+ "bitflags 2.13.1",
  "wayland-backend",
  "wayland-client",
  "wayland-scanner",
@@ -2842,7 +3683,7 @@ version = "0.3.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2b6d8cf1eb2c1c31ed1f5643c88a6e53538129d4af80030c8cabd1f9fa884d91"
 dependencies = [
- "bitflags 2.12.1",
+ "bitflags 2.13.1",
  "wayland-backend",
  "wayland-client",
  "wayland-protocols",
@@ -2855,7 +3696,7 @@ version = "0.3.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "eb04e52f7836d7c7976c78ca0250d61e33873c34156a2a1fc9474828ec268234"
 dependencies = [
- "bitflags 2.12.1",
+ "bitflags 2.13.1",
  "wayland-backend",
  "wayland-client",
  "wayland-protocols",
@@ -2864,9 +3705,9 @@ dependencies = [
 
 [[package]]
 name = "wayland-scanner"
-version = "0.31.10"
+version = "0.31.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c324a910fd86ebdc364a3e61ec1f11737d3b1d6c273c0239ee8ff4bc0d24b4a"
+checksum = "338e30461b3a2b67d70eb30a6d89f8e0c93a833e07d2ae89085cd070c4a00ac0"
 dependencies = [
  "proc-macro2",
  "quick-xml",
@@ -2887,9 +3728,9 @@ dependencies = [
 
 [[package]]
 name = "web-sys"
-version = "0.3.99"
+version = "0.3.103"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6d621441cfc37b84979402712047321980c178f299193a3589d05b99e8763436"
+checksum = "8622dcb61c0bcc9fffa6938bed81210af2da9a7e4a1a834b2e37a59b6dfb6141"
 dependencies = [
  "js-sys",
  "wasm-bindgen",
@@ -2905,20 +3746,29 @@ dependencies = [
  "wasm-bindgen",
 ]
 
+[[package]]
+name = "weezl"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88"
+
 [[package]]
 name = "wgpu"
-version = "24.0.5"
+version = "27.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6b0b3436f0729f6cdf2e6e9201f3d39dc95813fad61d826c1ed07918b4539353"
+checksum = "bfe68bac7cde125de7a731c3400723cadaaf1703795ad3f4805f187459cd7a77"
 dependencies = [
  "arrayvec",
- "bitflags 2.12.1",
+ "bitflags 2.13.1",
+ "cfg-if",
  "cfg_aliases",
  "document-features",
+ "hashbrown 0.16.1",
  "js-sys",
  "log",
  "naga",
  "parking_lot",
+ "portable-atomic",
  "profiling",
  "raw-window-handle",
  "smallvec",
@@ -2933,49 +3783,85 @@ dependencies = [
 
 [[package]]
 name = "wgpu-core"
-version = "24.0.5"
+version = "27.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f0aa306497a238d169b9dc70659105b4a096859a34894544ca81719242e1499"
+checksum = "27a75de515543b1897b26119f93731b385a19aea165a1ec5f0e3acecc229cae7"
 dependencies = [
  "arrayvec",
+ "bit-set",
  "bit-vec",
- "bitflags 2.12.1",
+ "bitflags 2.13.1",
+ "bytemuck",
  "cfg_aliases",
  "document-features",
+ "hashbrown 0.16.1",
  "indexmap",
  "log",
  "naga",
  "once_cell",
  "parking_lot",
+ "portable-atomic",
  "profiling",
  "raw-window-handle",
  "rustc-hash",
  "smallvec",
- "thiserror 2.0.18",
+ "thiserror 2.0.19",
+ "wgpu-core-deps-apple",
+ "wgpu-core-deps-emscripten",
+ "wgpu-core-deps-windows-linux-android",
  "wgpu-hal",
  "wgpu-types",
 ]
 
+[[package]]
+name = "wgpu-core-deps-apple"
+version = "27.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0772ae958e9be0c729561d5e3fd9a19679bcdfb945b8b1a1969d9bfe8056d233"
+dependencies = [
+ "wgpu-hal",
+]
+
+[[package]]
+name = "wgpu-core-deps-emscripten"
+version = "27.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b06ac3444a95b0813ecfd81ddb2774b66220b264b3e2031152a4a29fda4da6b5"
+dependencies = [
+ "wgpu-hal",
+]
+
+[[package]]
+name = "wgpu-core-deps-windows-linux-android"
+version = "27.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "71197027d61a71748e4120f05a9242b2ad142e3c01f8c1b47707945a879a03c3"
+dependencies = [
+ "wgpu-hal",
+]
+
 [[package]]
 name = "wgpu-hal"
-version = "24.0.4"
+version = "27.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f112f464674ca69f3533248508ee30cb84c67cf06c25ff6800685f5e0294e259"
+checksum = "5b21cb61c57ee198bc4aff71aeadff4cbb80b927beb912506af9c780d64313ce"
 dependencies = [
  "android_system_properties",
  "arrayvec",
  "ash",
  "bit-set",
- "bitflags 2.12.1",
+ "bitflags 2.13.1",
  "block",
  "bytemuck",
+ "cfg-if",
  "cfg_aliases",
- "core-graphics-types",
+ "core-graphics-types 0.2.0",
  "glow",
  "glutin_wgl_sys",
  "gpu-alloc",
  "gpu-allocator",
  "gpu-descriptor",
+ "hashbrown 0.16.1",
  "js-sys",
  "khronos-egl",
  "libc",
@@ -2983,34 +3869,37 @@ dependencies = [
  "log",
  "metal",
  "naga",
- "ndk-sys 0.5.0+25.2.9519653",
+ "ndk-sys",
  "objc",
  "once_cell",
  "ordered-float",
  "parking_lot",
+ "portable-atomic",
+ "portable-atomic-util",
  "profiling",
  "range-alloc",
  "raw-window-handle",
  "renderdoc-sys",
- "rustc-hash",
  "smallvec",
- "thiserror 2.0.18",
+ "thiserror 2.0.19",
  "wasm-bindgen",
  "web-sys",
  "wgpu-types",
- "windows",
- "windows-core",
+ "windows 0.58.0",
+ "windows-core 0.58.0",
 ]
 
 [[package]]
 name = "wgpu-types"
-version = "24.0.0"
+version = "27.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50ac044c0e76c03a0378e7786ac505d010a873665e2d51383dcff8dd227dc69c"
+checksum = "afdcf84c395990db737f2dd91628706cb31e86d72e53482320d368e52b5da5eb"
 dependencies = [
- "bitflags 2.12.1",
+ "bitflags 2.13.1",
+ "bytemuck",
  "js-sys",
  "log",
+ "thiserror 2.0.19",
  "web-sys",
 ]
 
@@ -3029,23 +3918,68 @@ version = "0.58.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6"
 dependencies = [
- "windows-core",
+ "windows-core 0.58.0",
  "windows-targets 0.52.6",
 ]
 
+[[package]]
+name = "windows"
+version = "0.62.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580"
+dependencies = [
+ "windows-collections",
+ "windows-core 0.62.2",
+ "windows-future",
+ "windows-numerics",
+]
+
+[[package]]
+name = "windows-collections"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610"
+dependencies = [
+ "windows-core 0.62.2",
+]
+
 [[package]]
 name = "windows-core"
 version = "0.58.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99"
 dependencies = [
- "windows-implement",
- "windows-interface",
- "windows-result",
- "windows-strings",
+ "windows-implement 0.58.0",
+ "windows-interface 0.58.0",
+ "windows-result 0.2.0",
+ "windows-strings 0.1.0",
  "windows-targets 0.52.6",
 ]
 
+[[package]]
+name = "windows-core"
+version = "0.62.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
+dependencies = [
+ "windows-implement 0.60.2",
+ "windows-interface 0.59.3",
+ "windows-link",
+ "windows-result 0.4.1",
+ "windows-strings 0.5.1",
+]
+
+[[package]]
+name = "windows-future"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb"
+dependencies = [
+ "windows-core 0.62.2",
+ "windows-link",
+ "windows-threading",
+]
+
 [[package]]
 name = "windows-implement"
 version = "0.58.0"
@@ -3054,7 +3988,18 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.119",
+]
+
+[[package]]
+name = "windows-implement"
+version = "0.60.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
 ]
 
 [[package]]
@@ -3065,7 +4010,18 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.119",
+]
+
+[[package]]
+name = "windows-interface"
+version = "0.59.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
 ]
 
 [[package]]
@@ -3074,6 +4030,16 @@ version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
 
+[[package]]
+name = "windows-numerics"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26"
+dependencies = [
+ "windows-core 0.62.2",
+ "windows-link",
+]
+
 [[package]]
 name = "windows-result"
 version = "0.2.0"
@@ -3083,16 +4049,34 @@ dependencies = [
  "windows-targets 0.52.6",
 ]
 
+[[package]]
+name = "windows-result"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
+dependencies = [
+ "windows-link",
+]
+
 [[package]]
 name = "windows-strings"
 version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10"
 dependencies = [
- "windows-result",
+ "windows-result 0.2.0",
  "windows-targets 0.52.6",
 ]
 
+[[package]]
+name = "windows-strings"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
+dependencies = [
+ "windows-link",
+]
+
 [[package]]
 name = "windows-sys"
 version = "0.48.0"
@@ -3120,6 +4104,15 @@ dependencies = [
  "windows-targets 0.52.6",
 ]
 
+[[package]]
+name = "windows-sys"
+version = "0.60.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
+dependencies = [
+ "windows-targets 0.53.5",
+]
+
 [[package]]
 name = "windows-sys"
 version = "0.61.2"
@@ -3153,13 +4146,39 @@ dependencies = [
  "windows_aarch64_gnullvm 0.52.6",
  "windows_aarch64_msvc 0.52.6",
  "windows_i686_gnu 0.52.6",
- "windows_i686_gnullvm",
+ "windows_i686_gnullvm 0.52.6",
  "windows_i686_msvc 0.52.6",
  "windows_x86_64_gnu 0.52.6",
  "windows_x86_64_gnullvm 0.52.6",
  "windows_x86_64_msvc 0.52.6",
 ]
 
+[[package]]
+name = "windows-targets"
+version = "0.53.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
+dependencies = [
+ "windows-link",
+ "windows_aarch64_gnullvm 0.53.1",
+ "windows_aarch64_msvc 0.53.1",
+ "windows_i686_gnu 0.53.1",
+ "windows_i686_gnullvm 0.53.1",
+ "windows_i686_msvc 0.53.1",
+ "windows_x86_64_gnu 0.53.1",
+ "windows_x86_64_gnullvm 0.53.1",
+ "windows_x86_64_msvc 0.53.1",
+]
+
+[[package]]
+name = "windows-threading"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37"
+dependencies = [
+ "windows-link",
+]
+
 [[package]]
 name = "windows_aarch64_gnullvm"
 version = "0.48.5"
@@ -3172,6 +4191,12 @@ version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
 
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
+
 [[package]]
 name = "windows_aarch64_msvc"
 version = "0.48.5"
@@ -3184,6 +4209,12 @@ version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
 
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
+
 [[package]]
 name = "windows_i686_gnu"
 version = "0.48.5"
@@ -3196,12 +4227,24 @@ version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
 
+[[package]]
+name = "windows_i686_gnu"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
+
 [[package]]
 name = "windows_i686_gnullvm"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
 
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
+
 [[package]]
 name = "windows_i686_msvc"
 version = "0.48.5"
@@ -3214,6 +4257,12 @@ version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
 
+[[package]]
+name = "windows_i686_msvc"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
+
 [[package]]
 name = "windows_x86_64_gnu"
 version = "0.48.5"
@@ -3226,6 +4275,12 @@ version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
 
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
+
 [[package]]
 name = "windows_x86_64_gnullvm"
 version = "0.48.5"
@@ -3238,6 +4293,12 @@ version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
 
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
+
 [[package]]
 name = "windows_x86_64_msvc"
 version = "0.48.5"
@@ -3250,6 +4311,12 @@ version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
 
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
+
 [[package]]
 name = "winit"
 version = "0.30.13"
@@ -3259,13 +4326,13 @@ dependencies = [
  "ahash",
  "android-activity",
  "atomic-waker",
- "bitflags 2.12.1",
+ "bitflags 2.13.1",
  "block2",
  "bytemuck",
  "calloop",
  "cfg_aliases",
  "concurrent-queue",
- "core-foundation",
+ "core-foundation 0.9.4",
  "core-graphics",
  "cursor-icon",
  "dpi",
@@ -3274,7 +4341,7 @@ dependencies = [
  "memmap2",
  "ndk",
  "objc2 0.5.2",
- "objc2-app-kit",
+ "objc2-app-kit 0.2.2",
  "objc2-foundation 0.2.2",
  "objc2-ui-kit",
  "orbclient",
@@ -3313,9 +4380,9 @@ dependencies = [
 
 [[package]]
 name = "winnow"
-version = "1.0.3"
+version = "1.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1"
+checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81"
 dependencies = [
  "memchr",
 ]
@@ -3326,11 +4393,29 @@ version = "0.57.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e"
 
+[[package]]
+name = "wl-clipboard-rs"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e9651471a32e87d96ef3a127715382b2d11cc7c8bb9822ded8a7cc94072eb0a3"
+dependencies = [
+ "libc",
+ "log",
+ "os_pipe",
+ "rustix 1.1.4",
+ "thiserror 2.0.19",
+ "tree_magic_mini",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-protocols",
+ "wayland-protocols-wlr",
+]
+
 [[package]]
 name = "writeable"
-version = "0.5.5"
+version = "0.6.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
+checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4"
 
 [[package]]
 name = "x11-dl"
@@ -3376,7 +4461,7 @@ version = "0.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5"
 dependencies = [
- "bitflags 2.12.1",
+ "bitflags 2.13.1",
  "dlib",
  "log",
  "once_cell",
@@ -3401,6 +4486,114 @@ version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e01738255b5a16e78bbb83e7fbba0a1e7dd506905cfc53f4622d89015a03fbb5"
 
+[[package]]
+name = "yeslogic-fontconfig-sys"
+version = "6.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d8b8abf912b9a29ff112e1671c97c33636903d13a69712037190e6805af4f76"
+dependencies = [
+ "dlib",
+ "once_cell",
+ "pkg-config",
+]
+
+[[package]]
+name = "zbus"
+version = "5.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe18fb60dc696039e738717b76eaea21e7a4489bbb1885020b43c94236d7e98a"
+dependencies = [
+ "async-broadcast",
+ "async-executor",
+ "async-io",
+ "async-lock",
+ "async-process",
+ "async-recursion",
+ "async-task",
+ "async-trait",
+ "blocking",
+ "enumflags2",
+ "event-listener",
+ "futures-core",
+ "futures-lite",
+ "hex",
+ "libc",
+ "ordered-stream",
+ "rustix 1.1.4",
+ "serde",
+ "serde_repr",
+ "tracing",
+ "uds_windows",
+ "uuid",
+ "windows-sys 0.61.2",
+ "winnow 1.0.4",
+ "zbus_macros",
+ "zbus_names",
+ "zvariant",
+]
+
+[[package]]
+name = "zbus-lockstep"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6998de05217a084b7578728a9443d04ea4cd80f2a0839b8d78770b76ccd45863"
+dependencies = [
+ "zbus_xml",
+ "zvariant",
+]
+
+[[package]]
+name = "zbus-lockstep-macros"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "10da05367f3a7b7553c8cdf8fa91aee6b64afebe32b51c95177957efc47ca3a0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+ "zbus-lockstep",
+ "zbus_xml",
+ "zvariant",
+]
+
+[[package]]
+name = "zbus_macros"
+version = "5.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe96480bed92df2b442a1a30df364e12d08eed03aeb061f2b8dc6afb2be91119"
+dependencies = [
+ "proc-macro-crate",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+ "zbus_names",
+ "zvariant",
+ "zvariant_utils",
+]
+
+[[package]]
+name = "zbus_names"
+version = "4.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8bf88b4a3ff53e883001e0e0115b297a9d53c31b9c1edd2bfdd853e3428624e"
+dependencies = [
+ "serde",
+ "winnow 1.0.4",
+ "zvariant",
+]
+
+[[package]]
+name = "zbus_xml"
+version = "5.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d1586c021a01ca0a9216dcd874e546382e156a5cbab5fab6cb5f10087e22682a"
+dependencies = [
+ "serde",
+ "winnow 1.0.4",
+ "zbus_names",
+ "zvariant",
+]
+
 [[package]]
 name = "zeno"
 version = "0.3.3"
@@ -3409,20 +4602,91 @@ checksum = "6df3dc4292935e51816d896edcd52aa30bc297907c26167fec31e2b0c6a32524"
 
 [[package]]
 name = "zerocopy"
-version = "0.8.50"
+version = "0.8.55"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3b065d4f0e55f82fae73202e189638116a87c55ab6b8e6c2721e13dd9d854ad1"
+checksum = "b5a105cd7b140f6eeec8acff2ea38135d3cab283ada58540f629fe51e46696eb"
 dependencies = [
  "zerocopy-derive",
 ]
 
 [[package]]
 name = "zerocopy-derive"
-version = "0.8.50"
+version = "0.8.55"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fe976fb70c78cd64cccfe3a6fc142244e8a77b70959b30faf9d0ac37ee228eb"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+]
+
+[[package]]
+name = "zerofrom"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272"
+
+[[package]]
+name = "zerovec"
+version = "0.11.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239"
+dependencies = [
+ "serde",
+ "zerofrom",
+]
+
+[[package]]
+name = "zune-core"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb8a0807f7c01457d0379ba880ba6322660448ddebc890ce29bb64da71fb40f9"
+
+[[package]]
+name = "zune-jpeg"
+version = "0.5.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296"
+dependencies = [
+ "zune-core",
+]
+
+[[package]]
+name = "zvariant"
+version = "5.13.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b631b19d36a892ab55420c92dbc83ccd79274f25be714855d3074aa71cab639"
+checksum = "bee2a0bcd2a907786a456fff45aaaaf54c9ba5f50b71ae9ec1a4edd200c94911"
+dependencies = [
+ "endi",
+ "enumflags2",
+ "serde",
+ "winnow 1.0.4",
+ "zvariant_derive",
+ "zvariant_utils",
+]
+
+[[package]]
+name = "zvariant_derive"
+version = "5.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38a708216a18780796770bfe3f4739c7c83a3e8f789b755534bbbc06e4e23e12"
+dependencies = [
+ "proc-macro-crate",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+ "zvariant_utils",
+]
+
+[[package]]
+name = "zvariant_utils"
+version = "3.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90cb9383f9b45290407a1258b202d3f8f01db719eb60b4e4055c6375af4fc7c7"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "serde",
+ "syn 2.0.119",
+ "winnow 1.0.4",
 ]
diff --git a/Cargo.toml b/Cargo.toml
index d59d193..50efd74 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,7 +1,13 @@
-# Cargo.toml raíz STANDALONE de pineal — front-door sobre Llimphi.
-# Solo el código de pineal; Llimphi y lo fundacional por git-dep del monorepo gioser.git.
+# Cargo.toml raíz STANDALONE de pineal — front-door público.
+# GENERADO por scripts/actualizar-standalone.py desde el monorepo tawasuyu.git
+# (v0.2.0). No editar a mano: se regenera. Lo fundacional se consume por
+# git-dep al tag; sólo el código del dominio vive en este repo.
 [workspace]
 resolver = "2"
+
+# ============================================================
+# Cuadrantes — agregar a medida que se migran
+# ============================================================
 members = [
     "00_unanchay/pineal/demos/pineal-bars-demo",
     "00_unanchay/pineal/demos/pineal-contour-demo",
@@ -35,6 +41,11 @@ members = [
     "00_unanchay/pineal/pineal-umbrella",
 ]
 
+# `wawa/` se excluye del workspace global porque corre en target
+# `x86_64-unknown-none` y `panic = "abort"`, incompatibles con los
+# perfiles globales. Los crates compartidos se referencian por `path`
+# cruzando la frontera.
+
 [workspace.package]
 version = "0.1.0"
 edition = "2021"
@@ -45,9 +56,32 @@ publish = false
 repository = "https://git.tawasuyu.net/tawasuyu/pineal"
 
 [workspace.dependencies]
-
+# === Configuración declarativa (vocabulario de esquemas) ===
+allichay = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+marca = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+# Telemetría local de desarrollo (stderr+panics+tracing → $XDG_STATE_HOME/tawasuyu/<dominio>/<bin>.log)
+bitacora = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+# default = [] (núcleo liviano sin vello); `bake` es opt-in. Declaramos
+# default-features = false aquí para que el `default-features = false` de los
+# consumidores (splash/greeter/compositor) se honre y no quede el warning.
+mirada-fondo = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+mirada-teclado-core = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+mirada-teclado-widget = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
 # === Registro de apps / menú global ===
-app-bus = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
+app-bus = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+app-iconset = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+# === Centro de eventos (notificaciones + capturas + clipboard + …) ===
+willay-core = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+willay-checkpoint = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+willay-hilo = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+willay-store = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+willay-emit = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+rag-motor = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+churay-core = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+churay-welcome = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+churay-welcome-runner = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+# === Puente de drag-and-drop compositor → app (suple winit en Wayland) ===
+drop-bridge = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
 # === Serialización ===
 serde = { version = "1", features = ["derive"] }
 serde_json = "1"
@@ -74,8 +108,12 @@ tracing = "0.1"
 tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] }
 
 # === Linux primitives (arje) ===
-nix = { version = "0.29", features = ["signal", "process", "sched", "mount", "fs", "socket", "net", "user"] }
+nix = { version = "0.29", features = ["signal", "process", "sched", "mount", "fs", "socket", "net", "user", "reboot"] }
 libc = "0.2"
+# DRM/KMS legacy (dumb buffer) para el splash nativo del arranque sin parpadeo.
+# Misma versión que reexporta smithay (mirada-compositor) — el lock ya la fija.
+drm = "0.14"
+font8x8 = { version = "0.3", default-features = false }
 
 # === IDs / Hash / Crypto ===
 ulid = { version = "1", features = ["serde"] }
@@ -89,10 +127,15 @@ argon2 = "0.5"
 rand = "0.8"
 
 # === WASM (arje) ===
-# wasmi 1.0: unifica la versión con renaser (su kernel ya corre 1.0), para
+# wasmi 1.0: unifica la versión con wawa (su kernel ya corre 1.0), para
 # que el ABI WASM del host sea idéntico en Linux y en bare-metal.
 wasmi = "1.0"
 wat = "1"
+# wasmtime 46 (Fase 1 del pipeline JS): tier 0 de puriy-js pasa de wasmi
+# (intérprete) a Wasmtime AOT (Cranelift → nativo) en Linux — quita la doble
+# interpretación de QuickJS.wasm. En wawa bare-metal el runtime es Pulley (spike
+# Q1 cerrado); mismo crate, distinto backend.
+wasmtime = "46"
 
 # === Storage / DB ===
 sled = "0.34"
@@ -107,6 +150,10 @@ bzip2 = "0.4"
 
 # === Compresión (minga multi-bundle) ===
 zstd = "0.13"
+xz2 = "0.1"
+# Contenedores adicionales del visor Archive de nahual (sólo listado).
+sevenz-rust = "0.6"
+unrar = "0.5"
 
 # === HTTP server (iniy-server) ===
 axum = "0.7"
@@ -116,7 +163,7 @@ tower = "0.5"
 instant-distance = "0.6"
 
 # === P2P (minga) ===
-libp2p = { version = "0.56", features = ["tokio", "tcp", "noise", "yamux", "macros", "kad", "identify", "relay", "dcutr", "autonat", "mdns"] }
+libp2p = { version = "0.56", features = ["tokio", "tcp", "dns", "noise", "yamux", "macros", "kad", "identify", "relay", "dcutr", "autonat", "mdns"] }
 libp2p-stream = "=0.4.0-alpha"
 libp2p-allow-block-list = "0.6"
 
@@ -131,7 +178,11 @@ libm = "0.2"
 midly = "0.5"
 
 # === Code parsing (minga) ===
-arboard = "3"
+# `wayland-data-control` trae el backend wl-clipboard-rs: bajo mirada (Wayland
+# propio, sin Xwayland para apps nativas) arboard-x11 fallaba a no-op silencioso
+# y el clipboard de los text-editor quedaba muerto. Con la feature habla
+# zwlr_data_control_manager_v1 (que mirada expone) y cae a X11 si no hay Wayland.
+arboard = { version = "3", features = ["wayland-data-control"] }
 ropey = "1.6"
 tree-sitter = "0.24"
 tree-sitter-rust = "0.23"
@@ -150,7 +201,19 @@ petgraph = "0.6"
 # default-features = false: nos quedamos con PNG + JPEG + WebP (lossless).
 # tullpu-render exporta a las tres; AVIF/TIFF/… los habilitamos si una app
 # los pide específicamente.
-image = { version = "0.25", default-features = false, features = ["png", "jpeg", "webp"] }
+# Decoders puro-Rust baratos; avif queda fuera (arrastra dav1d/rav1e).
+image = { version = "0.25", default-features = false, features = ["png", "jpeg", "webp", "gif", "bmp", "ico", "tiff", "tga", "qoi"] }
+# ONNX Runtime para los proveedores de píxel reales (pixel-verbo-onnx: segmentación
+# u2net/isnet, inpaint, restyle…). `download-binaries` (default) baja el runtime nativo
+# solo; ya lo arrastraba fastembed (rimay-verbo) transitivo — aquí lo declaramos directo.
+ort = "2.0.0-rc.9"
+
+# Rasterizador vectorial CPU (relleno/trazo anti-aliased) para capas vectoriales de tullpu.
+tiny-skia = "0.11"
+# Booleanos de polígonos robustos (union/intersection/difference/xor) para el
+# clipper vectorial de tullpu — restar/intersecar paths que se solapan
+# parcialmente, que la regla de relleno no expresa. Ya estaba en el lock.
+geo = "0.28"
 
 # === FUSE (minga-vfs) ===
 # default-features = false: prescinde de pkg-config/libfuse-dev en build.
@@ -172,91 +235,164 @@ tempfile = "3"
 
 # === Llimphi (motor gráfico soberano) ===
 # wgpu sobre Vulkan/Metal/DX12, winit para ventana en dev Linux.
-# raw-window-handle 0.6 alinea winit 0.30 con wgpu 24.
-# vello 0.5 = rasterizador vectorial sobre wgpu 24.
+# raw-window-handle 0.6 alinea winit 0.30 con wgpu 27.
+# vello 0.7 = rasterizador vectorial sobre wgpu 27 (renderer GPU "wgpu" + opt-in "hybrid" CPU+GPU).
 # taffy 0.9 = motor Flexbox/Grid puro Rust (ya pulled por transitivos, lo alineamos).
-# parley 0.2 = shaping/layout de texto compatible con peniko 0.4 (que vello 0.5 expone).
-wgpu = "24"
+# parley 0.6 = shaping/layout de texto compatible con peniko 0.6 (que vello 0.7 expone).
+wgpu = "27"
 winit = "0.30"
 raw-window-handle = "0.6"
 pollster = "0.4"
-vello = "0.5"
+vello = "0.7"
+# Renderer hybrid CPU+GPU sin compute shaders (mejor compat WebGL2/Adreno viejas).
+# Opt-in; el renderer "wgpu" sigue siendo el default.
+vello_hybrid = "0.0.9"
+# foreign-lottie = fork vendorizado de velato 0.9 (Lottie → vello::Scene),
+# completado para no paniquear ante features no soportadas. Lo consume
+# llimphi-lottie. Ver shared/foreign-lottie.
+foreign-lottie = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+# Árbol de accesibilidad para lectores de pantalla (NVDA/VoiceOver/Orca/TalkBack).
+# Lo consume el runtime de Llimphi (iter 2/3 del plan AccessKit); el modelo
+# `SemanticsSpec` del compositor es independiente de estas crates.
+accesskit = "0.24"
+accesskit_winit = "0.33"
 taffy = "0.9"
 # parley = shaping completo (bidi, ligatures, fallback CJK/emoji vía fontique, line break).
-parley = "0.4"
+parley = "0.6"
 # Bucle Elm (input→update→view→layout→raster→present). Lo consumen las apps.
-llimphi-ui = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
+llimphi-ui = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+# Tier 3 — apps WASM con UI Llimphi real. El guest pinta un WireNode (IR
+# serializable), el host (runner) lo materializa en View<Msg> y rebota los
+# eventos al update del guest. SDK = lado guest, runner = lado host.
+llimphi-wire-view = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-wasm-app-sdk = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-wasm-runner = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+# Núcleo puro de distribución (CAS + verificación + resolve), SIN runner/GPU.
+llimphi-wasm-core = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+# Cara con baterías: re-exporta core + puente al runner para correr la app.
+llimphi-wasm-dist = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-wasm-open = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-wasm-registry = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-wasm-wasi = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+# Transporte P2P del bytecode por hash sobre BrahmanNet (card-net).
+llimphi-wasm-net = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-3d = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+# Runner wlr-layer-shell: corre un `App` de Llimphi como barra anclada a un
+# borde (no como ventana), reusando la plumbing sctk+wgpu que pata probó.
+llimphi-layer = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
 # Paleta semántica compartida por las apps y los widgets.
-llimphi-theme = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
+llimphi-theme = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
 # Tweens y helpers de animación sobre el bucle Elm.
-llimphi-motion = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
+llimphi-motion = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+# Máquina de estados de animación (estilo Rive) clip-agnóstica. Núcleo puro;
+# el render lo cablea el consumidor (llimphi-lottie). Escalón sobre el playback.
+llimphi-anim = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+# Studio de animación «rive»: además del editor (bin), expone como lib los
+# documentos serializables (Doc + RigDoc + Project) que carga mirada-fondo.
+llimphi-anim-studio = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+# Render de mallas deformables (skel::Mesh) a vello Scene: vectorial y texturizada.
+llimphi-mesh = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
 # Iconos vectoriales (BezPath en grid 24×24) compartidos por todas las apps.
-llimphi-icons = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
+llimphi-icons = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+# Puente fino vello_svg → Llimphi para SVG arbitrario (íconos .desktop, logos).
+llimphi-svg = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+# Puente fino velato (Lottie) → Llimphi para animación vectorial autorada (.json).
+llimphi-lottie = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-image = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+# Decodificador de protocolos de gráficos de terminal (kitty/sixel) → RGBA.
+llimphi-term-graphics = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
 # Widgets reusables sobre llimphi-ui — uno por crate.
-llimphi-widget-app-header = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
-llimphi-widget-banner = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
-llimphi-widget-button = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
-llimphi-widget-card = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
-llimphi-clipboard = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
-llimphi-widget-context-menu = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
-llimphi-widget-edit-menu = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
-llimphi-widget-menubar = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
-llimphi-widget-list = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
-llimphi-widget-grid = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
-llimphi-widget-slider = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
-llimphi-widget-scroll = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
-llimphi-widget-splitter = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
-llimphi-widget-stat-card = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
-llimphi-widget-tabs = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
-llimphi-module-command-palette = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
-llimphi-module-diff-viewer = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
-llimphi-module-fif = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
-llimphi-module-file-picker = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
-llimphi-module-bookmarks = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
-llimphi-module-mini-map = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
-llimphi-module-shuma-term = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
-llimphi-module-symbol-outline = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
-llimphi-plugin-host = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
-llimphi-widget-theme-switcher = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
-llimphi-widget-text-area = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
-llimphi-widget-text-editor-core = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
-llimphi-widget-text-editor = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
-llimphi-widget-text-editor-lsp = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
-llimphi-widget-text-input = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
-llimphi-widget-tiled = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
-llimphi-widget-nodegraph = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
-llimphi-widget-tree = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
-llimphi-widget-navigator = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
+llimphi-widget-app-header = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-banner = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-button = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-rive-button = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-card = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-clipboard = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-context-menu = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-edit-menu = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-menubar = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-list = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-grid = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-table = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-color-picker = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-slider = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-scroll = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-lazy-list = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-splitter = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-stat-card = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-tabs = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-module-allichay = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-module-command-palette = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-module-diff-viewer = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-module-fif = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-module-file-picker = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-module-bookmarks = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-module-mini-map = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-module-shuma-term = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-module-symbol-outline = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-plugin-host = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-theme-switcher = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-text-area = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-text-editor-core = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-text-editor = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-text-editor-lsp = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-text-input = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-tiled = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-nodegraph = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-tree = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+grafo-nav = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+llimphi-widget-navigator = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-detail-table = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-select = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-terminal = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
 # Sello vectorial wawa (rombo + W implícita + Merkle Core).
-llimphi-widget-wawa-mark = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
+llimphi-widget-wawa-mark = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
 # Widgets de elegancia transversal (tooltip, spinner, progress, toast,
 # modal, empty, status-bar, shortcuts-help, splash).
-llimphi-widget-tooltip = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
-llimphi-widget-spinner = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
-llimphi-widget-progress = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
-llimphi-widget-toast = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
-llimphi-widget-modal = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
-llimphi-widget-empty = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
-llimphi-widget-status-bar = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
-llimphi-widget-shortcuts-help = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
-llimphi-widget-timeline = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
-llimphi-widget-splash = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
+llimphi-widget-tooltip = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-spinner = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-progress = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-toast = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-modal = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-empty = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-status-bar = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-shortcuts-help = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-timeline = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-transport = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-waveform = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-splash = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
 # Controles de formulario y signaling (switch, segmented, breadcrumb,
 # badge, avatar, skeleton, field).
-llimphi-widget-switch = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
-llimphi-widget-segmented = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
-llimphi-widget-dock-rail = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
-llimphi-widget-breadcrumb = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
-llimphi-widget-badge = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
-llimphi-widget-avatar = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
-llimphi-widget-skeleton = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
-llimphi-widget-field = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
+llimphi-widget-switch = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-segmented = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-rag-sidebar = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-dock-rail = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-toolbar = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-breadcrumb = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-badge = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-avatar = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-skeleton = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-field = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
 # Firma visual transversal (gradient sutil + hairline accent).
-llimphi-widget-panel = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
-llimphi-widget-panes = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
-llimphi-workspace = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
+llimphi-widget-panel = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-panes = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+# Widgets Flutter-like — composición sobre primitivas Tier 1/3 ya
+# expuestas (sombra, gradient, animated, ripple).
+llimphi-widget-chip = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-fab = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-wrap = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-range-slider = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-calendar = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-fitted-box = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-carousel = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-rating = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-gauge = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-scaffold = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-hero = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-widget-router = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
+llimphi-workspace = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
 # Abstracción Selector — host (paths) + wawa (khipus).
-llimphi-module-selector = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.1.0" }
+llimphi-module-selector = { git = "https://git.tawasuyu.net/tawasuyu/llimphi.git", tag = "v0.2.0" }
 
 # === Filesystem helpers ===
 directories = "5"
@@ -290,7 +426,7 @@ hex = "0.4"
 portable-pty = "0.9"
 vt100 = "0.16"
 
-# === WASM web (gioser) ===
+# === WASM web (tawasuyu) ===
 wasm-bindgen = "0.2"
 wasm-bindgen-futures = "0.4"
 js-sys = "0.3"
@@ -315,23 +451,46 @@ ttf-parser = "0.25"
 # ============================================================
 # Intra-workspace deps de nahual (referenciadas por workspace = true)
 # ============================================================
-nahual-text-viewer-llimphi = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.1.0" }
-nahual-image-viewer-llimphi = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.1.0" }
-nahual-thumb-core = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.1.0" }
-nahual-gallery-llimphi = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.1.0" }
-nahual-video-viewer-llimphi = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.1.0" }
-nahual-card-viewer-llimphi = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.1.0" }
-nahual-audio-viewer-llimphi = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.1.0" }
-nahual-tree-viewer-llimphi = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.1.0" }
-nahual-hex-viewer-llimphi = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.1.0" }
-nahual-table-viewer-llimphi = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.1.0" }
-nahual-markdown-viewer-llimphi = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.1.0" }
-nahual-archive-viewer-llimphi = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.1.0" }
-nahual-font-viewer-llimphi = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.1.0" }
-nahual-map-viewer-llimphi = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.1.0" }
-nahual-geo-core = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.1.0" }
-nahual-viewer-core = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.1.0" }
-nahual-file-explorer-llimphi = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.1.0" }
+nahual-text-viewer-llimphi = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+nahual-image-viewer-llimphi = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+nahual-pdf-viewer-llimphi = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+nahual-cbz-viewer-llimphi = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+nahual-thumb-core = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+nahual-gallery-llimphi = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+nahual-video-viewer-llimphi = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+nahual-card-viewer-llimphi = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+nahual-audio-viewer-llimphi = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+media-module = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+tullpu-module = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+tullpu-core = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+tullpu-icon-core = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+tullpu-icon-cli = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+tullpu-icon-llimphi = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+tullpu-icon-llm = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+foreign-svg = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+foreign-pdf = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+tullpu-render = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+tullpu-render-gpu = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+tullpu-ops = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+tullpu-paint = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+nahual-tree-viewer-llimphi = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+nahual-hex-viewer-llimphi = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+nahual-svg-viewer-llimphi = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+nahual-sheet-viewer-llimphi = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+nahual-dbf-viewer-llimphi = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+nahual-table-viewer-llimphi = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+nahual-markdown-viewer-llimphi = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+nahual-pluma-viewer-llimphi = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+nahual-cotejo-llimphi = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+nahual-deck-viewer-llimphi = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+nahual-archive-viewer-llimphi = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+nahual-font-viewer-llimphi = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+nahual-map-viewer-llimphi = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+nahual-geo-core = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+nahual-geo-voxel = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+nahual-iconos = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+nahual-viewer-core = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+nahual-file-explorer-llimphi = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
 
 # ============================================================
 # Intra-workspace deps de pineal (módulo de gráficos)
@@ -356,20 +515,28 @@ pineal = { path = "00_unanchay/pineal/pineal-umbrella" }
 # ============================================================
 # Intra-workspace deps de iniy (laboratorio semántico de creencias)
 # ============================================================
-iniy-core = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.1.0" }
-iniy-ingest = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.1.0" }
-iniy-extract = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.1.0" }
-iniy-nli = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.1.0" }
-iniy-nli-llm = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.1.0" }
-iniy-graph = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.1.0" }
-iniy-store = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.1.0" }
+iniy-core = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+iniy-evidencia = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+iniy-derive = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+iniy-emisores = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+iniy-cache = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+iniy-daemon = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+iniy-ingest = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+iniy-extract = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+iniy-nli = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+iniy-nli-llm = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+iniy-graph = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+iniy-store = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+# la física de decaimiento de khipu la importa iniy-derive (SDD iniy §3)
+khipu-gravity = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
 
 # === auto: declarados por crates internos faltantes ===
-cosmos-coords = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.1.0" }
-cosmos-core = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.1.0" }
-cosmos-ephemeris = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.1.0" }
-cosmos-time = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.1.0" }
-cosmos-wcs = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.1.0" }
+cosmos-coords = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+cosmos-cities = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+cosmos-core = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+cosmos-ephemeris = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+cosmos-time = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
+cosmos-wcs = { git = "https://git.tawasuyu.net/tawasuyu/tawasuyu.git", tag = "v0.2.0" }
 
 # === auto: externas de eternal ===
 celestial-eop-data = { version = "0.1"}
@@ -377,6 +544,7 @@ approx = "0.5"
 byteorder = "1.5"
 cc = "1.0"
 chrono = "0.4"
+chrono-tz = "0.10"
 crc32fast = "1.4"
 criterion = "0.5"
 csv = "1.4"
@@ -385,6 +553,11 @@ glob = "0.3"
 indicatif = "0.18"
 lz4_flex = "0.11"
 memmap2 = "0.9"
+# Cliente Wayland (lado cliente) — lo usa hapiy para hablar zwlr_screencopy
+# contra mirada (u otro compositor wlroots) y capturar la pantalla.
+wayland-client = "0.31"
+wayland-protocols-wlr = { version = "0.3", features = ["client"] }
+wayland-protocols-misc = { version = "0.3", features = ["client"] }
 mockito = "1.0"
 ndarray = "0.15"
 num-traits = "0.2"
@@ -418,6 +591,9 @@ markup5ever_rcdom = "0.39"
 cssparser = "0.35"
 url = "2"
 ureq = { version = "2", default-features = false, features = ["tls"] }
+# Charset de la web real: no todo es UTF-8 (google.com sirve ISO-8859-1).
+# Ya venía transitivo por el stack de Servo — aquí se hace explícito.
+encoding_rs = "0.8"
 
 # === takiy-synth (SoundFont MIDI) ===
 # rustysynth = sintetizador SF2 puro Rust, MIT. Reemplaza el oscilador
@@ -446,7 +622,7 @@ hound = "3.5"
 symphonia = { version = "0.5", default-features = false, features = ["mp3", "flac", "vorbis", "ogg"] }
 
 # === media-source-opus (decoder Opus NATIVO puro-Rust) ===
-# Opus es el formato de audio nativo de gioser (par del video AV1). ogg
+# Opus es el formato de audio nativo de tawasuyu (par del video AV1). ogg
 # demuxea las páginas Ogg; opus-wave es un port puro-Rust de libopus
 # (SILK+CELT, sin C ni FFI) — par del rav1d del lado video.
 ogg = "0.9"
@@ -457,3 +633,21 @@ opus-wave = "3"
 # paquetes de los tracks V_AV1 y A_OPUS para alimentar a media-source-av1
 # y media-source-opus — un .webm AV1+Opus se reproduce 100% nativo.
 matroska-demuxer = "0.7"
+
+# === Perfil `dist`: binarios de reparto livianos (equipos chicos / poca RAM) ===
+# Hereda de `release` pero prioriza HUELLA sobre tiempo de compilación: LTO thin
+# (poda código muerto entre crates), un solo codegen-unit (mejor inlining/DCE) y
+# `strip` de símbolos. Reduce el tamaño en disco Y el segmento de código mapeado
+# en RAM de cada binario (mirada, pata, apps Llimphi). No toca `cargo build
+# --release` de iteración diaria; se usa sólo al empaquetar:
+#   cargo build --profile dist -p mirada-compositor -p pata-host …
+# `panic = "unwind"` se mantiene a propósito: mirada-plugin-host aísla plugins
+# con `catch_unwind`, así que `abort` rompería el sandbox.
+[profile.dist]
+inherits = "release"
+lto = "thin"
+codegen-units = 1
+strip = "symbols"
+panic = "unwind"
+
+