diff --git a/README.md b/README.md
index b8e62a1..1507413 100644
--- a/README.md
+++ b/README.md
@@ -1,233 +1,145 @@
-

+

-# flan
.
+# Flan
-**A statically typed Lisp for making games.**
-Clojure's brackets. C's memory. No garbage collector. A REPL into the running process.
+**A statically typed Lisp for native games and interactive development.**
----
+Flan is an experimental, ahead-of-time compiled Lisp for programs that need
+predictable memory use and a fast edit–run loop. It combines S-expressions,
+static types, explicit ownership, and a development session that can replace a
+function in a running program without resetting its state.
-Flan is what happens when you want Odin's memory model and Common Lisp's debugger
-at the same time and refuse to pick.
+It is being built around games, but the interesting part is broader: a compiled
+language where the running program remains available for inspection,
+experimentation, and small changes.
-There are no object headers, so a Flan struct **is** its C struct. There is no
-collector, so nothing runs between your frames that you did not write. And the
-program you are running is not a build artifact you replace — it is a thing you
-can edit while it is still going.
+In practical terms: you get parentheses, a debugger that would like to have a
+conversation, and no garbage collector quietly choosing the dramatic moment to
+join your frame loop.
-```
-Edit the code, keep the sand.
-```
+## What it has
-## The twenty-millisecond loop
+- Native compilation through LLVM, plus an in-progress direct x86-64 backend.
+- C-like data layout: structs, fixed arrays, pointers, slices, and explicit
+ allocation. There is no garbage collector.
+- Owned `Vec` and `Map` containers, plus checked moves and borrowing-oriented
+ slice operations.
+- Generics, algebraic unions, enums, macros, packages, `defer`, and a C FFI.
+- Conditions and restarts for recoverable failures and interactive debugging.
+- A raylib package and a collection of ported raylib examples.
+- Native, WASI, and web build targets. The cross targets are useful but less
+ complete than the native development workflow.
-Run `flan dev sand.flan`. A window opens, sand falls, and a socket appears next to
-your source file. Open Emacs, hit `C-c C-z`, and you are attached to the live process.
+The project is exploratory software, not a stable language release. Some
+features are deliberately refused while their semantics are still undecided;
+the compiler aims to say why rather than quietly accepting a partial version.
+It has opinions, but at least they arrive as error messages.
-Now put your cursor in a function and press `C-c C-c`.
+## Quick start
-That function is recompiled and installed into the running program at its next frame
-boundary. The whole round trip is about **19 milliseconds**, of which the actual
-hot-swap — `dlopen` plus `dlsym` — is **0.04 ms**. The rest is the compiler doing
-its job. It reads your *buffer*, not your saved file, so there is no ceremony.
-
-The window does not blink. The grid does not reset. Your sand keeps falling.
-
-| | |
-|---|---|
-| `C-c C-c` | recompile this function into the live program |
-| `C-x C-e` | run this expression **inside** the running process — `(len enemies)` returns the real number |
-| `C-u C-c C-c` | set a breakpoint without editing the buffer |
-| `C-c C-m` | expand a macro, one step or to the fixpoint |
-| `C-c C-b` | open the break loop when something goes wrong |
-| `C-c C-r` | a REPL, scoped to the program |
-
-## When it breaks, it does not die
-
-Most languages give you two options when something goes wrong: crash, or have
-guessed in advance what you wanted. Flan has Common Lisp's third option.
-
-```
-flan: unhandled Missing — stopped, not dead.
- 0. restart: carry-on
- 1. restart: use-placeholder
-```
-
-The program is sitting on the frame where the error happened, with everything it
-had still in scope, waiting for you to decide. Pick a restart and it carries on —
-it never unwound, so there is nothing to reconstruct.
-
-Handlers run **on the signalling frame, without unwinding**, which means you can
-also just… not have an error:
-
-```lisp
-;; A handler that returns normally accumulates and lets the signaller run on.
-(handler-bind [(AssetMissing [c] (set seen (+ seen (i64 (.id c)))))]
- (load-all))
-```
-
-No monad. No `Result` threading. No early return. The signaller carries on.
-
-A breakpoint, incidentally, is not a feature. `pause` is an ordinary function that
-signals a `Pause` condition, and a breakpoint is just a condition nobody handled.
-
-## The language
-
-**Four container types, four honest ownership stories.** `[n T]` is a fixed array
-and it is a *value* — it copies. `[T]` is a slice: a pointer and a length that owns
-nothing. `(Vec T)` and `(Map K V)` own their storage and move rather than copy.
-
-```lisp
-;; No initialiser means all-bytes-zero, so this lives in BSS and costs nothing.
-(defvar grid [rows [cols i32]])
-
-(let [row (slice (at grid 1) 0 cols)] ; ptr+len, borrows
- (set (at row 0) 5)
- (println (at grid 1 0))) ; 5 — the same storage
-
-(set grid (zeroed)) ; a memset, not an allocation
-```
-
-**`defer` is a compile-time construct**, copied into the exit paths. Innermost
-first. There is no runtime stack of thunks to pay for.
-
-```lisp
-(defn work [n i32] i32
- (defer (println "second"))
- (defer (println "first"))
- (when (< n 0)
- (return 0)) ; runs both defers above it
- n)
-```
-
-**Generics are monomorphised, and checked once.** The body is verified abstractly at
-the definition, so an unsupported operation is an error where you wrote it — not at
-whichever call site happened to pass a type that worked.
-
-```lisp
-(defn clamp-to [x $t lo $t hi $t] $t
- {:where (ordered? $t)}
- (min (max x lo) hi))
-```
-
-**Macros are compiled, dlopened, and called.** There is no interpreter in this
-project and there is not going to be one — compiling is the only way a form is ever
-run, so there is no second evaluator to disagree with the first. Running a file that
-calls a macro means the compiler built a shared object and loaded it into *itself*
-before parsing your first line.
-
-**The FFI is one line per function.** No wrapper, no `shim.c`:
-
-```lisp
-(declare-c init-window [width i32 height i32 title string] "InitWindow")
-(declare-c window-should-close? [] bool "WindowShouldClose")
-```
-
-`Color` crosses by value. `Vector2` comes back by value. The compiler writes the
-flattening C so you don't. There are **more than 470** raylib bindings in the box, and
-**29** of raylib's own examples ported.
-
-**Keywords are enums with an integer's ABI:**
-
-```lisp
-(defenum Key [space 32 escape 256 left 263 right 262])
-(key-pressed? :space) ; resolved at compile time; a typo is an error here
-```
-
-## The demo
-
-`sand.flan` is a falling-sand toy: 180×120 grains at 120fps. Hold the mouse and
-sand pours out of the cursor, release and the colour cycles, `R` clears it.
-
-It is 206 lines, and it deliberately uses almost nothing — no `Vec`, no `Map`, no
-generics, no macros of its own, no allocator beyond the stack and static storage.
-The grid is `[rows [cols u32]]`: flat, unboxed, in BSS, exactly `rows*cols*4` bytes.
-The same memory the Odin port has. Nothing in the frame loop allocates.
-
-The same file is also the regression test. `sand-headless.flan` imports it as a
-package and — because the linker follows what the program actually reaches — pulls
-in neither a window nor libraylib. It runs N frames and hashes the grid:
-
-```
-15595743031174623232
-```
-
-That number is byte-identical on native x86-64 and on wasm32-wasi, at `-O2` and at
-`-O0`. The random number generator is written in Flan rather than borrowed from
-libc precisely so that it would be.
-
-## Two backends that agree
-
-There is the LLVM backend, and there is a second one — about 3,400 lines of OCaml
-that emits x86-64 machine code directly, byte by byte, because `llc` was most of
-those 19 milliseconds and that was annoying.
-
-Every program in the test corpus produces **byte-identical stdout, stderr and exit
-status under both backends**. Not similar. Identical. There is a script that checks
-this and it is the only reason anyone trusts the second one.
-
-It also has a rule, stated in capital letters in two separate file headers:
-
-> There is still no aggregate classifier and there must not be one.
-
-## What works, and what doesn't
-
-**Works, and is tested at `-O2`, `-O0` and `--dev`** — often under Valgrind and
-ASan too: structs, unions, enums, `match`, generics, `Option`, macros, packages,
-conditions and restarts, `defer`, arenas and explicit allocators, all four
-containers, handles and pools, bounds checking, arithmetic errors as conditions,
-the raylib FFI, UTF-8 strings, compile-time `embed`, the whole Emacs loop, a
-wasm32 target, and DWARF debug info you can step through in lldb.
-
-**Designed, refused by name, not built yet:** `(Result T E)` and `try`, `errdefer`,
-`handler-case`, `find-restart`, user-written allocators, threading macros, and
-`await`. The compiler will tell you which milestone each belongs to rather than
-producing a confusing parse error.
-
-**Honest limits.** Redefinition cannot patch a frame that is currently executing and
-resume at the same instruction — its register allocation belonged to the old
-compilation. "Resume" means re-entering from a restart. And changing a function's
-*signature* is rejected rather than applied; changing its *body* is always safe,
-because old code is never unloaded.
-
-## Try it
+Building requires a current OCaml/Dune toolchain, LLVM/Clang, and the native C
+toolchain. Raylib is only needed for programs that use the bundled graphics
+package.
```sh
-dune build # the compiler
-dune test # 232 checks
-flan run sand.flan # falling sand
-flan dev sand.flan # falling sand you can edit
+dune build
+dune exec ./bin/main.exe -- run web/examples/hello.flan
```
-Then in Emacs: `M-x flan-dev`, or `C-c C-z` to attach to the one you just started.
+To build a standalone native executable:
-## Reading further
+```sh
+dune exec ./bin/main.exe -- build web/examples/hello.flan -o hello
+./hello
+```
-- [`plan.org`](plan.org) — the design and the open decisions
-- [`spec-memory.md`](spec-memory.md) — ownership, containers, generics
-- [`spec-conditions.md`](spec-conditions.md) — what a restart actually is
-- [`emacs/MANUAL.md`](emacs/MANUAL.md) — every key binding and what it does
-- [`web/index.html`](web/index.html) — the language reference, including a table of
- everything that is *not* implemented and the exact words the compiler uses to
- refuse it
-- [`docs/`](docs/) — the reports and the history, with an index saying which of
- them are still true
+The falling-sand demo uses raylib:
-## Licence
+```sh
+dune exec ./bin/main.exe -- run sand.flan
+```
-MIT. See [`LICENSE`](LICENSE).
+Once you are iterating regularly, put the built executable on your `PATH` if
+you want to use the shorter `flan` commands shown below.
-The `vendor/` directory carries other people's work under their own terms —
-raylib is zlib/libpng, and the committed `raylib-5.5.h` stays under it.
+## The live development loop
----
+Start a long-lived development session:
-
+```sh
+flan dev sand.flan
+```
-Flan is a custard.
+The program runs normally and publishes a local socket beside the source file.
+The bundled Emacs mode can attach to it, evaluate expressions in the live
+process, inspect a stopped program, and recompile a top-level function from
+the buffer. A body change takes effect on the next call; changing a function's
+signature is intentionally rejected. The program keeps its state, which is
+especially nice when you have finally arranged the sand into something almost
+worth saving.
-
+To set up the mode:
+
+```elisp
+(add-to-list 'load-path "~/path/to/flan/emacs")
+(require 'flan-mode)
+```
+
+Then use `M-x flan-dev` to start and attach, or `C-c C-z` to attach to a
+session started in a terminal. The editor workflow is documented in
+[emacs/MANUAL.md](emacs/MANUAL.md).
+
+## A small example
+
+```lisp
+(defstruct AssetMissing [id i32])
+
+(defn load-asset [id i32] i32
+ (signal (AssetMissing {.id id}))
+ 100)
+
+(defn asset-or-placeholder [id i32] i32
+ (restart-case (load-asset id)
+ (use-placeholder [] -1)))
+
+(defn main [] ()
+ (handler-bind [(AssetMissing [_] (invoke-restart 'use-placeholder))]
+ (println (asset-or-placeholder 7))))
+```
+
+Here a missing asset signals a typed condition. The handler chooses a restart,
+so execution continues with a placeholder instead of requiring error values to
+be threaded through every caller. See
+[web/examples/restart.flan](web/examples/restart.flan) for a runnable version.
+
+## Commands
+
+```text
+flan check type-check a program
+flan run [args...] build and run it
+flan build [-o out] [options] build a native executable
+flan dev [-s socket] start a live development session
+```
+
+Useful build options include `--debug`, `--sanitize`, `--no-bounds-checks`,
+`--x86`, and `--target=wasm32-wasi|web`. `run` is native-only; cross-built
+output should be run with an appropriate WASI runtime or browser. A `.wasm`
+file is not a tiny native executable in a trench coat.
+
+## Project map
+
+- [web/index.html](web/index.html) — language reference and fuller examples.
+- [spec-memory.md](spec-memory.md) — ownership, containers, and generics.
+- [spec-conditions.md](spec-conditions.md) — conditions, handlers, and restarts.
+- [emacs/MANUAL.md](emacs/MANUAL.md) — the interactive editor workflow.
+- [docs/BUILT.md](docs/BUILT.md) — implementation rationale.
+- [NEXT.md](NEXT.md) — current work and known limits.
+
+## License
+
+Flan is released under the [MIT License](LICENSE). Third-party material under
+`vendor/` is distributed under its own licenses.