diff --git a/docs/Website/Assets/guru.svg b/docs/Website/Assets/guru.svg
new file mode 100644
index 0000000..a9d4ed4
--- /dev/null
+++ b/docs/Website/Assets/guru.svg
@@ -0,0 +1,12 @@
+
diff --git a/docs/Website/Assets/shift.svg b/docs/Website/Assets/shift.svg
new file mode 100644
index 0000000..ed9d5ba
--- /dev/null
+++ b/docs/Website/Assets/shift.svg
@@ -0,0 +1,27 @@
+
diff --git a/docs/Website/Assets/uberblock.svg b/docs/Website/Assets/uberblock.svg
new file mode 100644
index 0000000..ff67457
--- /dev/null
+++ b/docs/Website/Assets/uberblock.svg
@@ -0,0 +1,44 @@
+
diff --git a/docs/Website/README.md b/docs/Website/README.md
new file mode 100644
index 0000000..4007a92
--- /dev/null
+++ b/docs/Website/README.md
@@ -0,0 +1,50 @@
+
+# The website
+
+Three files and a folder of marks. No build step, no dependencies, no
+framework — open `index.html` and it works, including from a `file://` URL.
+
+```
+index.html the page
+style.css the palette, transcribed from include/theme.h
+app.js two behaviours: the boot log types, sections fade in
+Assets/ shift.svg (the logo), uberblock.svg (SchFS), guru.svg (PSOGM)
+```
+
+## The rules it follows
+
+**The palette is not a design decision.** It is `include/theme.h`, copied. A
+site describing an operating system should look like the operating system, and
+copying the header means the two cannot drift apart by accident.
+
+**PSOGM purple appears exactly once.** `#4B1D9A` is on the crash-screen section
+and nowhere else, because [../branding/README.md](../branding/README.md) says
+it means *the machine stopped* and that a colour used everywhere means nothing.
+If it shows up in a second place, that is a bug in the page.
+
+**The typefaces are the ones the OS ships.** Roboto for text and JetBrains Mono
+for the system voice — both already in `ports/`, both already in `NOTICE`. They
+are requested from the system rather than downloaded: a font file would be
+larger than the entire rest of the site.
+
+**The script is optional.** If `app.js` fails to load the page is still
+complete and still readable. Everything it does is decoration, and it honours
+`prefers-reduced-motion` by doing none of it.
+
+**The boot log is real.** The lines in `app.js` are from an actual boot, not
+invented. The timer calibration line is there deliberately; see section 13 of
+[../history/kernel.md](../history/kernel.md) for what it cost.
+
+## Editing it
+
+The numbers in the *Numbers* section are measured, not estimated, and they go
+stale. To refresh them:
+
+```sh
+find kernel include -name '*.c' -o -name '*.h' | xargs wc -l | tail -1
+grep -c '^SYSCALL' kernel/sys/table.inc
+```
+
+The feature cards carry a tag — `runs`, `in progress`, or `designed` — and the
+tag is a promise. Anything marked `runs` is in the tree and works today. When
+that stops being true, change the tag rather than the wording.
diff --git a/docs/Website/app.js b/docs/Website/app.js
new file mode 100644
index 0000000..3198b19
--- /dev/null
+++ b/docs/Website/app.js
@@ -0,0 +1,92 @@
+/* SPDX-License-Identifier: Apache-2.0
+ *
+ * Two behaviours, no framework:
+ * 1. the hero console types a real boot log
+ * 2. sections fade in once, as they arrive
+ *
+ * Both are decoration. If this file fails to load the page is still complete
+ * and still readable, which is the test any script on a documentation site
+ * should have to pass.
+ */
+(function () {
+ "use strict";
+
+ var reduced = window.matchMedia &&
+ window.matchMedia("(prefers-reduced-motion: reduce)").matches;
+
+ /* ---- 1. the boot log ------------------------------------------------
+ * These are real lines from a real boot, not invented ones. The timer
+ * calibration line in particular is there because of what it cost: the
+ * clock ran seven times slow until a graphics driver was made to
+ * acknowledge its own interrupt. See docs/history/kernel.md.
+ */
+ var LINES = [
+ { t: "[ 0.000]", k: "SchIFT", s: " 64-bit, 2 cpus" },
+ { t: "[ 0.004]", k: "cpu0", s: ": lapic timer 999949260 Hz, tsc 3792 MHz" },
+ { t: "[ 0.061]", k: "virtio-gpu", s: ": 1280x800, 4000 KB framebuffer, irq 11" },
+ { t: "[ 0.088]", k: "e1000", s: ": 52:54:00:12:34:56, link up, irq 11" },
+ { t: "[ 0.143]", k: "schfs0", s: ": mounted, generation 41" },
+ { t: "[ 0.169]", k: "net", s: ": eth0 10.0.2.15/255.255.255.0" },
+ { t: "[ 0.204]", k: "schinit", s: ": 4 jobs", g: true },
+ { t: "", k: "", s: "# " }
+ ];
+
+ var out = document.getElementById("bootlog");
+
+ function span(cls, text) {
+ var e = document.createElement("span");
+ if (cls) e.className = cls;
+ e.textContent = text;
+ return e;
+ }
+
+ function emit(line) {
+ if (line.t) {
+ out.appendChild(span("t", line.t + " "));
+ out.appendChild(span("k", line.k));
+ }
+ out.appendChild(span(line.g ? "g" : "", line.s));
+ out.appendChild(document.createTextNode("\n"));
+ }
+
+ function cursor() {
+ var c = document.createElement("i");
+ c.className = "cursor";
+ out.appendChild(c);
+ }
+
+ if (!out) {
+ /* nothing to do */
+ } else if (reduced) {
+ LINES.forEach(emit); // the whole log at once, no animation
+ cursor();
+ } else {
+ var i = 0;
+ (function step() {
+ if (i >= LINES.length) { cursor(); return; }
+ emit(LINES[i++]);
+ /* Uneven on purpose: a boot that ticks out at a metronome's pace reads
+ as a progress bar rather than as a machine doing different work. */
+ setTimeout(step, 180 + Math.random() * 220);
+ })();
+ }
+
+ /* ---- 2. reveal on scroll ------------------------------------------ */
+
+ var targets = document.querySelectorAll(".reveal");
+
+ if (reduced || !("IntersectionObserver" in window)) {
+ Array.prototype.forEach.call(targets, function (el) { el.classList.add("in"); });
+ return;
+ }
+
+ var io = new IntersectionObserver(function (entries) {
+ entries.forEach(function (entry) {
+ if (!entry.isIntersecting) return;
+ entry.target.classList.add("in");
+ io.unobserve(entry.target); // once, not on every scroll past
+ });
+ }, { rootMargin: "0px 0px -12% 0px", threshold: 0.05 });
+
+ Array.prototype.forEach.call(targets, function (el) { io.observe(el); });
+})();
diff --git a/docs/Website/index.html b/docs/Website/index.html
new file mode 100644
index 0000000..619b2a6
--- /dev/null
+++ b/docs/Website/index.html
@@ -0,0 +1,294 @@
+
+
+
+
+
+SchOS — a 64-bit operating system written from scratch
+
+
+
+
+
+
+
+
+ A 64-bit operating system written from scratch — its own kernel, its own
+ filesystem, its own bootloader. It boots on UEFI, drives real hardware,
+ and runs unmodified musl and toybox on top.
+
It started as a teaching kernel and stopped being one
+
+ SchOS began as xv6, MIT's
+ teaching operating system: 32-bit, single core, one filesystem, a few dozen
+ syscalls. Almost none of that survives. What follows is what replaced it.
+
+
+
+
+ First
+
64-bit, and more than one core
+
+ Four-level paging, a real SMP bring-up, per-CPU state and a trap path
+ that survives both. This is the part that invalidates most of what a
+ 32-bit uniprocessor kernel assumes.
+
+
+
+ Then
+
Its own filesystem
+
+ xv6's log-structured filesystem was replaced by SchFS: a
+ copy-on-write B-tree with an uberblock ring, extents and checksummed
+ metadata. A crash leaves either the old tree or the new one.
+
+
+
+ Then
+
A Linux-shaped ABI
+
+ The syscall table adopted Linux numbering, which is what makes an
+ unmodified musl and an unmodified toybox run. The table is one
+ .inc file, and it is the seam the whole system is built around.
+
+
+
+ Now
+
Hardware, not emulation
+
+ A UEFI loader, xHCI and USB storage, an e1000 with a real TCP/IP stack,
+ virtio-gpu and VBE framebuffers, and a device model where a driver never
+ names its consumer.
+
+
+
+
+
+
+
+
+
Architecture
+
Two seams hold the whole thing together
+
+ Above the syscall table, software is ordinary and portable. Below the device
+ model, a driver never names its consumer and a consumer never names a driver.
+ Everything else is arrangement.
+
+
+
+
userlandtoybox, mksh, musl, and the first-party programs in cmd/
+
schinitPID 1 — job manifests and supervision
+
syscall tableLinux numbering, one .inc file — the ABI seam
+ uname -s reports SchIFT — the kernel — the way every Unix
+ reports its kernel rather than its distribution.
+
+
+
+
+
+
+
What it has
+
Built and running, and honest about the rest
+
+ Every card marked runs is in the tree and works today. The tags are
+ not aspirational; the things that are not finished say so.
+
+
+
+
+ runs
+
SchFS
+
+ Copy-on-write B-tree, a 128-slot uberblock ring, extents with refcounts
+ and reflink, CRC32c on metadata, and free space derived from the tree
+ rather than stored beside it — so there is no second structure to
+ disagree with the first. There is nothing to repair after a crash.
+
+
+
+ runs
+
SMP x86-64 kernel
+
+ Four-level paging, multiple cores, preemptive scheduling, a calibrated
+ LAPIC timer, real signal delivery and job control. 205 syscalls on
+ Linux numbering.
+
+
+
+ runs
+
Networking
+
+ An e1000 driver under lwIP, with sockets for AF_INET, AF_UNIX and
+ AF_NETLINK. DHCP brings the interface up at boot; ping
+ reaches the outside world.
+
+
+
+ runs
+
UEFI boot
+
+ A PE/COFF loader built with clang and lld-link that sets up its own page
+ tables and hands the kernel a framebuffer. No BIOS path, no chainloading.
+
+
+
+ runs
+
USB
+
+ xHCI, hubs, HID and mass storage — a keyboard and a USB disk that the
+ kernel can mount, through the same device model as everything else.
+
+
+
+ runs
+
A real userland
+
+ musl and toybox, both unmodified, plus mksh as /bin/sh.
+ fastfetch builds from upstream source with an empty
+ patches/ directory — when it could not detect something, the
+ answer was to give the kernel what it was looking for.
+
+
+
+ in progress
+
Schcom
+
+ The compositor that will own the display. v0 runs. The desktop above it
+ is the next large piece of work, and it is deliberately Wayland-shaped.
+
+
+
+ designed
+
SchAPI
+
+ The native programming interface — specified, not built, and deliberately
+ so. It should be designed from the compositor's experience rather
+ than before it.
+
+
+
+
+
+
+
+
+
Numbers
+
The size of it
+
+
43,701lines in kernel/ and include/
+
205syscalls, Linux numbering
+
128uberblock ring slots
+
0patches carried against fastfetch
+
+
+
+
+
+
+
+
+
When it stops
+
PSOGM
+
+ When the kernel panics it does not scroll a stack trace past you. It draws
+ a full-screen report — what failed, where, and the state that mattered —
+ and encodes the whole thing as a QR code so it can leave the machine
+ without being retyped.
+
+
+ This purple is #4B1D9A, and the theme reserves it for exactly
+ this. Normal states are calm and dark; only failure is loud. It is the one
+ colour in the system that means something, which is why it appears once on
+ this page and nowhere else.
+
+
+
+
+
+
+
+
Reading
+
Where to look
+
+ The documentation is written to outlive the work. Anything finished is
+ described in architecture/; anything intended lives in
+ plan/; and the bugs worth remembering are kept in
+ history/, in full, including the wrong turns.
+