diff --git a/.cargo/config.toml b/.cargo/config.toml
new file mode 100644
index 0000000..35049cb
--- /dev/null
+++ b/.cargo/config.toml
@@ -0,0 +1,2 @@
+[alias]
+xtask = "run --package xtask --"
diff --git a/Cargo.lock b/Cargo.lock
index eb4a27c..7498835 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2928,6 +2928,23 @@ dependencies = [
"sheetcraft-engine",
]
+[[package]]
+name = "sheetcraft-web"
+version = "0.1.0"
+dependencies = [
+ "eframe",
+ "egui",
+ "js-sys",
+ "log",
+ "rfd",
+ "serde_json",
+ "sheetcraft-engine",
+ "sheetcraft-ui-egui",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+]
+
[[package]]
name = "sheetcraft-xlsx"
version = "0.1.0"
diff --git a/ROADMAP.md b/ROADMAP.md
new file mode 100644
index 0000000..61538de
--- /dev/null
+++ b/ROADMAP.md
@@ -0,0 +1,84 @@
+# SheetCraft roadmap
+
+SheetCraft aims at full Microsoft Excel parity — and to be better: faster, open (XLSX is the
+native format), fully scriptable by agents (MCP + control channel + CLI), and available on the
+web.
+
+## Status (2026-10-07)
+
+**Parity estimate**
+
+| Measure | Value | Notes |
+|---|---|---|
+| Ribbon/menu command catalog | **60%** (169 / 281) | `cargo xtask parity` → [`docs/parity.md`](docs/parity.md). Home is 97%. |
+| Worksheet functions | **~93%** (478 + 32 evaluator built-ins of ~545) | Missing: ODDF*/ODDL*, BAHTTEXT, IMAGE, TRANSLATE, COPILOT/PY, STOCKHISTORY, RTD |
+| Feature depth (weighted) | **~38%** | Pivot tables, Power Query, print/PDF, page layout view, chart formatting depth, ink, macros/scripts, threaded collaboration and solver/analysis tools are the big missing areas |
+
+**Estimated remaining effort to full parity: ~220 wall-clock hours of Claude Opus 5.5 agent
+work** (≈ 9–10 days running continuously with 3–4 parallel agents), broken down below.
+
+**Working today**
+- Engine: A1/R1C1 formula parser with reference adjustment, dependency-graph recalculation with
+ dynamic arrays (spill + `#SPILL!`), cycle detection, LET/LAMBDA/MAP/REDUCE/SCAN/BYROW/BYCOL/
+ MAKEARRAY, structured table references, defined names, INDIRECT/OFFSET/INDEX references,
+ 478 worksheet functions (math, statistics and distributions, text and regex, lookup incl.
+ XLOOKUP/FILTER/SORT/UNIQUE/VSTACK, dates, financial incl. bonds, engineering incl. complex
+ numbers and Bessel, database, information).
+- Number formats: Excel's full format-code language (sections, conditions, colours, dates and
+ elapsed time, fractions, scientific, accounting fill), General narrowing to column width.
+- Files: XLSX read/write (styles, themes, merges, CF, validation, tables, comments, hyperlinks,
+ charts, pictures, sparklines, print settings, defined names, shared/array/dynamic formulas),
+ CSV/TSV with encoding and delimiter detection, HTML export, JSON.
+- Editing: cell entry with type detection and auto-formats, in-cell and formula-bar editing,
+ point mode (click/drag/arrow references), reference colouring, F4 anchor cycling, function
+ autocomplete and argument hints, copy/cut/paste and Paste Special (values, formats, transpose,
+ operations, link, skip blanks), AutoFill series (numbers, dates, months, weekdays, custom lists,
+ text+number), Fill Series, Flash Fill, find/replace with wildcards, Go To / Go To Special,
+ undo/redo with history, format painter.
+- Formatting: fonts, fills, all border styles, alignment (wrap, indent, rotation, merge), number
+ formats, 47 cell styles, 60 table styles, conditional formatting (cell rules, text, dates,
+ duplicates, top/bottom, averages, data bars, colour scales, icon sets, formulas), automatic
+ row heights, autofit, hide/unhide, freeze panes.
+- Data: sort (multi-level, by colour, custom lists), AutoFilter (values, custom, top 10,
+ average, colour), tables with totals rows, remove duplicates, text to columns, data validation
+ with error alerts, grouping/outline, subtotals.
+- Charts: column, bar, line, area, pie, doughnut, scatter, bubble, radar, histogram, waterfall,
+ funnel, treemap, sunburst, box & whisker, stock and combo charts; sparklines.
+- UI (egui): Excel-style title bar with Quick Access Toolbar and AutoSave, the full ribbon with
+ galleries (cell styles, table styles, colour palettes, conditional formatting menus),
+ contextual Table/Chart Design tabs, formula bar with Name Box, virtualised grid (smooth
+ scrolling, zoom, frozen panes, text overflow), sheet tabs (rename, reorder, colour, hide),
+ status bar with Sum/Count/Average and zoom, dialogs (Format Cells, Formula Builder, Find and
+ Replace, Sort, Name Manager, Data Validation, Paste Special…), command palette, dark mode.
+- Agents: every action is an engine command; JSON control channel in the desktop app (pointer,
+ keyboard, dialogs, screenshots); MCP server (headless or bridged); CLI (`info`, `convert`,
+ `eval`, `cat`, `run`, `commands`, `functions`, `mcp`, `send`).
+- Platforms: macOS, Windows, Linux, FreeBSD; web (WASM via trunk). Release workflows for
+ signed/notarized macOS universal DMG, signed Windows x64/x86 MSI + zip, Linux AppImage/deb/rpm/
+ tar.gz + Flatpak manifest, FreeBSD tarball and a web zip.
+
+## Milestones
+
+| # | Milestone | Status | Est. hours left |
+|---|---|---|---|
+| M0 | Foundation: core, number formats, formula language, functions, model, calc, XLSX/CSV, engine | ✅ done | — |
+| M1 | Excel look: chrome, ribbon, formula bar, grid, tabs, status bar, control channel | ✅ done (polish continues) | 8 |
+| M2 | Formatting completeness (Format Cells parity, borders drawing, themes fonts/effects, styles authoring) | 🟡 most | 10 |
+| M3 | Editing power (drag-move/copy with Alt, insert copied cells, AutoComplete in column, AutoCorrect, spelling) | 🟡 most | 12 |
+| M4 | Data (advanced filter, consolidate, what-if: goal seek, scenarios, data tables; Get & Transform lite) | 🟡 partial | 24 |
+| M5 | Formulas tab (trace arrows on the grid, watch window, evaluate stepper UI, error-check UI, remaining functions) | 🟡 partial | 12 |
+| M6 | Conditional formatting manager parity (rule editor dialog, all icon sets drawn, stop-if-true UI) | 🟡 most | 6 |
+| M7 | Charts parity (element selection & Format pane, axes options, trendlines, error bars, chart sheets, styles/colours galleries, PivotCharts) | 🟡 partial | 28 |
+| M8 | Insert & Review (icons library of our own, SmartArt-lite, equations, threaded comments pane, show changes, accessibility pane) | 🟡 partial | 18 |
+| M9 | Page layout & print (Page Layout and Page Break Preview views, header/footer editor, print to PDF, print preview) | ⬜ | 26 |
+| M10 | PivotTables (field list, layouts, grouping, calculated fields, slicers, timelines), Solver, Analysis ToolPak | ⬜ | 40 |
+| M11 | Automation (record actions → replayable scripts, Rhai script editor, form controls) | ⬜ | 14 |
+| M12 | Performance (1M-row files, parallel recalc, incremental geometry caches), accessibility, i18n | 🟡 partial | 16 |
+| M13 | Release & polish (signed builds green on every platform, side-by-side pixel tuning, README screenshots) | 🟡 tooling ready | 6 |
+| | **Total** | | **≈ 220** |
+
+## How to measure
+
+- `cargo xtask parity` — catalog coverage (feature names from Excel's ribbon and menus).
+- `cargo test --workspace` — engine, formula, function, XLSX and UI behaviour.
+- `cargo run --release -p sheetcraft-ui-egui --example snapshot -- --sample sales out.png` — look at the UI offscreen.
diff --git a/apps/sheetcraft-web/Cargo.toml b/apps/sheetcraft-web/Cargo.toml
new file mode 100644
index 0000000..aad352e
--- /dev/null
+++ b/apps/sheetcraft-web/Cargo.toml
@@ -0,0 +1,29 @@
+[package]
+name = "sheetcraft-web"
+description = "SheetCraft in the browser (eframe web runner, WebGPU with WebGL2 fallback)"
+version.workspace = true
+edition.workspace = true
+license.workspace = true
+publish = false
+
+[lints]
+workspace = true
+
+[[bin]]
+name = "sheetcraft-web"
+path = "src/main.rs"
+
+[dependencies]
+sheetcraft-engine = { workspace = true }
+sheetcraft-ui-egui = { workspace = true }
+egui = { workspace = true }
+log = { workspace = true }
+serde_json = { workspace = true }
+
+[target.'cfg(target_arch = "wasm32")'.dependencies]
+eframe = { workspace = true, features = ["default_fonts", "wgpu", "accesskit"] }
+rfd = { workspace = true }
+wasm-bindgen = "0.2"
+wasm-bindgen-futures = "0.4"
+js-sys = "0.3"
+web-sys = { version = "0.3", features = ["Window", "Document", "Element", "HtmlElement", "HtmlCanvasElement", "HtmlAnchorElement", "Blob", "BlobPropertyBag", "Url", "Location", "CssStyleDeclaration"] }
diff --git a/apps/sheetcraft-web/Trunk.toml b/apps/sheetcraft-web/Trunk.toml
new file mode 100644
index 0000000..948d368
--- /dev/null
+++ b/apps/sheetcraft-web/Trunk.toml
@@ -0,0 +1,10 @@
+# Build with: trunk build --release (from apps/sheetcraft-web)
+[build]
+target = "index.html"
+dist = "../../dist/web"
+public_url = "./"
+filehash = true
+
+[serve]
+addresses = ["127.0.0.1"]
+port = 8771
diff --git a/apps/sheetcraft-web/index.html b/apps/sheetcraft-web/index.html
new file mode 100644
index 0000000..bf979bd
--- /dev/null
+++ b/apps/sheetcraft-web/index.html
@@ -0,0 +1,19 @@
+
+
+
+
+
+ SheetCraft
+
+
+
+
+
+ Loading SheetCraft…
+
+
diff --git a/apps/sheetcraft-web/src/main.rs b/apps/sheetcraft-web/src/main.rs
new file mode 100644
index 0000000..599ccdb
--- /dev/null
+++ b/apps/sheetcraft-web/src/main.rs
@@ -0,0 +1,25 @@
+//! SheetCraft in the browser.
+//!
+//! Runs the same [`sheetcraft_ui_egui::SheetApp`] as the desktop app through eframe's web runner
+//! (wgpu: WebGPU where available, WebGL2 otherwise). Build with `trunk build --release` from
+//! this directory (output in `dist/web`).
+//!
+//! Differences from the desktop app: no TCP control channel; Open uses the browser file picker
+//! (bytes arrive asynchronously through `Services::inbox`); Save downloads the file; dropped
+//! files are read asynchronously.
+//!
+//! URL query flags: `?webgl` forces WebGL2; `?sample=sales|budget|grades` opens a sample.
+#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)]
+
+#[cfg(target_arch = "wasm32")]
+mod web;
+
+#[cfg(target_arch = "wasm32")]
+fn main() {
+ web::start();
+}
+
+#[cfg(not(target_arch = "wasm32"))]
+fn main() {
+ eprintln!("sheetcraft-web only runs in the browser: build it with `trunk build --release` in apps/sheetcraft-web");
+}
diff --git a/apps/sheetcraft-web/src/web.rs b/apps/sheetcraft-web/src/web.rs
new file mode 100644
index 0000000..8393b7f
--- /dev/null
+++ b/apps/sheetcraft-web/src/web.rs
@@ -0,0 +1,146 @@
+//! The browser shell: web `Services`, drag-and-drop, and the eframe web runner.
+
+use sheetcraft_engine::Session;
+use sheetcraft_ui_egui::{Inbox, Services, SheetApp};
+use wasm_bindgen::JsCast as _;
+
+const CANVAS_ID: &str = "sheetcraft_canvas";
+const LOADING_ID: &str = "sheetcraft_loading";
+
+pub fn start() {
+ eframe::WebLogger::init(log::LevelFilter::Info).ok();
+ wasm_bindgen_futures::spawn_local(async {
+ let Some(document) = web_sys::window().and_then(|w| w.document()) else {
+ log::error!("no document");
+ return;
+ };
+ let Some(canvas) = document.get_element_by_id(CANVAS_ID).and_then(|e| e.dyn_into::().ok()) else {
+ log::error!("missing