diff --git a/app/pages/index.up b/app/pages/index.up
new file mode 100644
index 0000000..7e25d7b
--- /dev/null
+++ b/app/pages/index.up
@@ -0,0 +1,103 @@
+
+
+
+ Pushup web framework
+
+
+
+
+
A server-side, page-oriented web framework for the Go programming language.
+
+
Pushup’s goal is to make it faster to develop and easier to maintain server-side web applications using Go.
+
+
Pushup is preview, pre-release software in early-stage development. It is not yet suitable for production use. Expect breaking changes.
+
+
+
+
+
+
Pushup example
+
^^import"time"
+
+^^{
+ title := "Hello, from Pushup!"
+}
+
+<h1>^^title</h1>
+
+<p>The time is now ^^time.Now().String().</p>
+
+^^iftime.Now().Weekday() == time.Friday {
+ <p>It's Friday! Enjoy the start to your weekend.</p>
+} ^^else {
+ <p>Have a great day, we're glad you're here.</p>
+}
+
+
File app/pages/hello.up → /hello URL path
+
+
+
+
Features
+
+
+
+ Page-oriented
+
Pushup’s .up files are self-contained units of web app development, gluing HTML & Go together with routing logic
+
+
+ File-based routing
+
Filesystem path names of Pushup pages map to URL paths, with dynamic escape hatches
Enhanced hypertext support via inline partials for better client-side interactivity with fewer JavaScript sit-ups
+
+
+ Compiled
+
Pushup apps compile to pure Go, built on the standard net/http package. Fast static binary executables for easy deployment. Easy to integrate into larger Go apps
+
+
+ Hot reload dev mode
+
App is recompiled and reloaded in the browser while files change during development. This is fast thanks to the Go compiler
+
diff --git a/app/pkg/app.go b/app/pkg/app.go
new file mode 100644
index 0000000..edd09bf
--- /dev/null
+++ b/app/pkg/app.go
@@ -0,0 +1,16 @@
+package build
+
+import (
+ "bytes"
+ "html/template"
+
+ "github.com/yuin/goldmark"
+)
+
+func convertMarkdown(text []byte) template.HTML {
+ var buf bytes.Buffer
+ if err := goldmark.Convert(text, &buf); err != nil {
+ panic(err)
+ }
+ return template.HTML(buf.String())
+}
diff --git a/app/static/adhoc-logo.png b/app/static/adhoc-logo.png
new file mode 100644
index 0000000..a093aab
Binary files /dev/null and b/app/static/adhoc-logo.png differ
diff --git a/app/static/docs/getting-started.md b/app/static/docs/getting-started.md
new file mode 100644
index 0000000..57091e7
--- /dev/null
+++ b/app/static/docs/getting-started.md
@@ -0,0 +1,114 @@
+# Getting started
+
+To make a new Pushup app, first install the main Pushup executable.
+
+### Installing Pushup
+
+#### Prerequisites
+
+- go 1.18 or later
+
+Make sure the directory where the go tool installs executables is in your
+`$PATH`. It is `$(go env GOPATH)/bin`. You can check if this is the case with:
+
+```shell
+echo $PATH | grep $(go env GOPATH)/bin > /dev/null && echo yes || echo no
+```
+
+#### Install via official release
+
+Binary executables for multiple platforms are available for download on the
+[project releases page](https://github.com/adhocteam/pushup/releases).
+
+#### Install via git
+
+```shell
+git clone https://github.com/adhocteam/pushup
+cd pushup
+make
+```
+
+#### Install via `go install`
+
+Make sure you have Go installed (at least version 1.18), and type:
+
+```shell
+go install github.com/adhocteam/pushup@latest
+```
+
+#### Install via `homebrew`
+
+Coming soon.
+
+#### Install via Linux package managers
+
+Coming soon.
+
+### Creating a new Pushup project
+
+To create a new Pushup project, use the `pushup new` command.
+
+```shell
+pushup new
+```
+
+Without any additional arguments, it will attempt to create a scaffolded new
+project in the current directory. However, the directory must be completely
+empty, or the command will abort. To simulataneously make a new directory
+and generate a scaffolded project, pass a relative path as argument:
+
+```shell
+pushup new myproject
+```
+
+The scaffolded new project directory consists of a directory structure for
+.up files and auxiliary project Go code, and a go.mod file.
+
+Change to the new project directory if necessary, then do a `pushup run`,
+which compiles the Pushup project to Go code, builds the app, and starts up
+the server.
+
+```shell
+pushup run
+```
+
+If all goes well, you should see a message on the terminal that the Pushup app
+is running and listening on a port:
+
+```
+↑↑ Pushup ready and listening on 0.0.0.0:8080 ↑↑
+```
+
+By default it listens on port 8080, but with the `-port` or `-unix-socket`
+flags you can pick your own listener.
+
+Open [http://localhost:8080/](http://localhost:8080/) in your browser to see
+the default layout and a welcome index page.
+
+### Listing routes
+
+You can print a list of all the routes in your Pushup project with the command
+`pushup routes`.
+
+The lefthand column is the URL route, where any dynamic path segments are
+denoted with a leading `:` colon. The righthand column is the corresponding
+Pushup page.
+
+For example:
+
+```shell
+$ pushup routes
+/about about.up
+/album/:id album/$id.up
+/album/delete/:id album/delete/$id.up
+/album/edit/:id album/edit/$id.up
+/album/new album/new.up
+/album/ album/index.up
+/dyn/:name dyn/$name.up
+/htmx/active-search htmx/active-search.up
+/htmx/click-to-load htmx/click-to-load.up
+/htmx/ htmx/index.up
+/htmx/value-select htmx/value-select.up
+/ index.up
+/projects/:pid/users/:uid projects/$pid/users/$uid.up
+```
diff --git a/app/static/docs/guides.md b/app/static/docs/guides.md
new file mode 100644
index 0000000..f712994
--- /dev/null
+++ b/app/static/docs/guides.md
@@ -0,0 +1,24 @@
+# Guides
+
+_Coming soon_
+
+## Enhanced hypermedia
+
+- How to use htmx with Pushup
+
+## Deployment
+
+- How to deploy a Pushup app using fly.io
+- How to deploy a Pushup app using Netlify
+- How to deploy a Pushup app using Render
+- How to deploy a Pushup app using AWS
+
+## Databases
+
+- How to make Pushup app with SQLite
+- How to make Pushup app with PostgreSQL
+
+## App types
+
+- How to make a CRUD app with Pushup
+- How to make a static site with Pushup
diff --git a/app/static/docs/intro.md b/app/static/docs/intro.md
new file mode 100644
index 0000000..0da98ad
--- /dev/null
+++ b/app/static/docs/intro.md
@@ -0,0 +1,77 @@
+# Introduction to Pushup
+
+Pushup is an experimental new project that is exploring the viability of a new
+approach to web frameworks in Go.
+
+Pushup seeks to make building page-oriented, server-side web apps using Go
+easy. It embraces the server, while acknowledging the reality of modern web
+apps and their improvements to UI interactivity over previous generations.
+
+## What is Pushup?
+
+Pushup is a program that compiles projects developed with the Pushup markup
+language into standalone web app servers.
+
+There are three main aspects to Pushup:
+
+1. A **file format (.up files)** with an opinionated app directory structure that enables **file-based routing**,
+1. A **lightweight markup** alternative to traditional web framework templates
+ that combines Go code for control flow and imperative, view-controller-like
+ code with HTML markup, and
+1. A **compiler** that parses that markup and generates pure Go code,
+ building standalone web apps on top of the Go stdlib `net/http` package.
+
+### Example Pushup app directory structure
+
+```
+/path/to/mypushupapp
+├── layouts
+│ └── default.up
+├── pages
+│ └── index.up
+├── pkg
+│ └── app.go
+└── static
+ ├── app.css
+ └── htmx.min.js
+```
+
+### Pages in Pushup
+
+The core object in Pushup is the "page": a file with the `.up` extension that
+is a mix of HTML, Go code, and a lightweight markup language that glues them
+together. Pushup pages participate in URL routing by virtue of their path in
+the filesystem. Pushup pages are compiled into pure Go which is then built
+along with a thin runtime into a standalone web app server (which is all
+`net/http` under the hood).
+
+The main proposition motivating Pushup is that the page is the right level of
+abstraction for most kinds of server-side web apps.
+
+The syntax of the Pushup markup language looks like this:
+
+```pushup
+
+^import "time"
+
+^{
+ title := "Hello, from Pushup!"
+}
+
+
^title
+
+
The time is now ^time.Now().String().
+
+^if time.Now().Weekday() == time.Friday {
+
It's Friday! Enjoy the start to your weekend.
+} ^else {
+
Have a great day, we're glad you're here.
+}
+
+```
+
+You would then place this code in a file somewhere in your `app/pages`
+directory, like `hello.up`. The `.up` extension is important and tells
+the compiler that it is a Pushup page. Once you build and run your Pushup app,
+that page is automatically mapped to the URL path `/hello`.
+
diff --git a/app/static/docs/syntax.md b/app/static/docs/syntax.md
new file mode 100644
index 0000000..2fa5f6b
--- /dev/null
+++ b/app/static/docs/syntax.md
@@ -0,0 +1,349 @@
+# Syntax
+
+Pushup is a mix of a new syntax consisting of Pushup directives and keywords,
+Go code, and HTML markup.
+
+### How it works
+
+Parsing a `.up` file always starts out in HTML mode, so you can just put
+plain HTML in a file and that's a valid Pushup page.
+
+When the parser encounters a '^' character (caret, ASCII 0x5e) while in
+HTML mode, it switches to parsing Pushup syntax, which consists of simple
+directives, control flow statements, block delimiters, and Go expressions. It
+then switches to the Go code parser. Once it detects the end of the directive,
+statement, or expression, it switches back to HTML mode, and parsing continues
+in a similar fashion.
+
+Pushup uses the tokenizers from the [go/scanner][scannerpkg] and
+[golang.org/x/net/html][htmlpkg] packages, so it should be able to handle
+any valid syntax from either language.
+
+[scannerpkg]: https://pkg.go.dev/go/scanner#Scanner
+[htmlpkg]: https://pkg.go.dev/golang.org/x/net/html#Tokenizer
+
+### Directives
+
+#### `^import`
+
+Use `^import` to import a Go package into the current Pushup page. The syntax
+for `^import` is the same as a regular [Go import declaration](https://go.dev/ref/spec#Import_declarations)
+
+Example:
+
+```pushup
+^import "strings"
+^import "strconv"
+```
+
+```pushup
+^import . "strings"
+```
+
+#### `^layout`
+
+Layouts are HTML templates that enclose the contents of a Pushup page.
+
+The `^layout` directive instructs Pushup what layout to apply the contents of
+the current page.
+
+The name of the layout following the directive is the filename in the
+`layouts` directory minus the `.up` extension. For example, `^layout main`
+would try to apply the layout located at `app/layouts/main.up`.
+
+`^layout` is optional - if it is not specified, pages automatically get the
+"default" layout (`app/layouts/default.up`).
+
+Example:
+
+```pushup
+^layout homepage
+```
+
+##### `^layout !` - no layout
+
+A page may choose to have no layout applied - that is, the contents of the page
+itself are sent directly to the client with no enclosing template. In this case,
+use the `!` name:
+
+```pushup
+^layout !
+```
+
+### Go code blocks
+
+#### `^{`
+
+To include statements of Go in a Pushup page, type `^{` followed by your
+Go code, terminating with a closing `}`.
+
+The scope of a `^{ ... }` in the compiled Go code is equal to its surrounding
+markup, so you can define a variable and immediately use it:
+
+```pushup
+^{
+ name := "world"
+}
+
Hello, ^name!
+```
+
+Because the Pushup parser is only looking for a balanced closing `}`, blocks
+can be one-liners:
+
+```pushup
+^{ name := "world"; greeting := "Hello" }
+
^greeting, ^name!
+```
+
+A Pushup page can have zero or many `^{ ... }` blocks.
+
+#### `^handler`
+
+A handler is similar to `^{ ... }`. The difference is that there may be at most
+one handler per page, and it is run prior to any other code or markup on the
+page.
+
+A handler is the appropriate place to do "controller"-like (in the MVC sense)
+actions, such as HTTP redirects and errors. In other words, any control flow
+based on the nature of the request, for example, redirecting after a successful
+POST to create a new object in a CRUD operation.
+
+Example:
+
+```pushup
+^handler {
+ if req.Method == "POST" && formValid(req) {
+ if err := createObjectFromForm(req.Form); err == nil {
+ return http.Redirect(w, req, "/success/", http.StatusSeeOther)
+ return nil
+ } else {
+ // error handling
+ ...
+ }
+ ...
+}
+...
+```
+
+Note that handlers (and all Pushup code) run in a method on a receiver that
+implements Pushup's `Responder` interface, which is
+
+```go
+interface Responder {
+ Respond(http.ResponseWriter, *http.Request) error
+}
+```
+
+To exit from a page early in a handler (i.e., prior to any normal content being
+rendered), return from the method with a nil (for success) or an error (which
+will in general respond with HTTP 500 to the client).
+
+### Control flow statements
+
+#### `^if`
+
+`^if` takes a boolean Go expression and a block to conditionally render.
+
+Example:
+
+```pushup
+^if query := req.FormValue("query"); query != "" {
+
Query: ^query
+}
+```
+
+#### `^for`
+
+`^for` takes a Go "for" statement condition, clause, or range, and a block,
+and repeatedly executes the block.
+
+Example:
+
+```pushup
+^for i := 0; i < 10; i++ {
+
Number ^i
+}
+```
+
+### Expressions
+
+#### Simple expressions
+
+Simple Go expressions can be written with just `^` followed by the expression.
+"Simple" means:
+
+- variable names (eg., `^x`)
+- dotted field name access of structs (eg., `^account.name`)
+- function and method calls (eg., `^strings.Repeat("x", 3)`)
+- index expressions (eg., `a[x]`)
+
+Example:
+
+```pushup
+^{ name := "Paul" }
+
Hello, ^name!
+```
+
+Outputs:
+
+```html
+
Hello, Paul!
+```
+
+Notice that the parser stops on the "!" because it knows it is not part of a
+Go variable name.
+
+Example:
+
+```pushup
+
+```
+
+#### Explicit expressions
+
+Explicit expressions are written with `^` and followed by any valid Go
+expression grouped by parentheses.
+
+Example:
+
+```pushup
+^{ numPeople := 4 }
+
With ^numPeople people there are ^(numPeople * 2) hands
+```
+
+Outputs:
+
+```html
+
With 4 people there are 8 hands
+```
+
+### Layout and templates
+
+#### `^section`
+
+Pushup layouts can have sections within the HTML document that Pushup pages
+can define with their own content to be rendered into those locations.
+
+For example, a layout could have a sidebar section, and each page can set
+its own sidebar content.
+
+In a Pushup page, sections are defined with the keyword like so:
+
+```pushup
+^section sidebar {
+
+
This is my sidebar content
+
More to come
+
+}
+```
+
+Layouts can output sections with the `outputSection` function.
+
+```pushup
+
+```
+
+Layouts can also make sections optional, by first checking if a page has set a
+section with `sectionDefined()`, which returns a boolean.
+
+```pushup
+^if sectionDefined("sidebar") {
+
+}
+```
+
+Checking for if a section was set by a page lets a layout designer provide
+default markup that can be overridden by a page.
+
+```pushup
+^if sectionDefined("title") {
+
+ ^outputSection("title")
+
+} ^else {
+ Welcome to our site
+}
+```
+
+#### `^partial`
+
+Pushup pages can declare and define inline partials with the `^partial`
+keyword.
+
+```pushup
+...
+
+
Elements
+ ^partial list {
+
+
Ag
+
Na
+
C
+
+ }
+
+...
+```
+
+A request to the page containing the initial partial will render normally,
+as if the block where not wrapped in `^partial list {` ... `}`.
+
+A request to the page with the name of the partial appended to the URL path
+will respond with just the content scoped by the partial block.
+
+For example, if the page above had the route `/elements/`, then a request to
+`/elements/list` would output:
+
+```html
+