]` | [`label`]($func/label) |
+| Reference | `[@intro]` | [`ref`]($func/ref) |
+| Heading | `[= Heading]` | [`heading`]($func/heading) |
+| Bullet list | `[- item]` | [`list`]($func/list) |
+| Numbered list | `[+ item]` | [`enum`]($func/enum) |
+| Term list | `[/ Term: description]` | [`terms`]($func/terms) |
+| Math | `[$x^2$]` | [Math]($category/math) |
+| Line break | `[\]` | [`linebreak`]($func/linebreak) |
+| Smart quote | `['single' or "double"]` | [`smartquote`]($func/smartquote) |
+| Symbol shorthand | `[~, ---]` | [Symbols]($category/symbols/sym) |
+| Code expression | `[#rect(width: 1cm)]` | [Scripting]($scripting/#expressions) |
+| Character escape | `[Tweet at us \#ad]` | [Below](#escapes) |
+| Comment | `[/* block */, // line]` | [Below](#comments) |
+
+## Math mode { #math }
+Math mode is a special markup mode that is used to typeset mathematical
+formulas. It is entered by wrapping a formula in `[$]` characters. The formula
+will be typeset into its own block if it starts and ends with at least one space
+(e.g. `[$ x^2 $]`). Inline math can be produced by omitting the whitespace (e.g.
+`[$x^2$]`). An overview over the syntax specific to math mode follows:
+
+| Name | Example | See |
+| ---------------------- | ------------------------ | ------------------------ |
+| Inline math | `[$x^2$]` | [Math]($category/math) |
+| Block-level math | `[$ x^2 $]` | [Math]($category/math) |
+| Bottom attachment | `[$x_1$]` | [`attach`]($category/math/attach) |
+| Top attachment | `[$x^2$]` | [`attach`]($category/math/attach) |
+| Fraction | `[$1 + (a+b)/5$]` | [`frac`]($func/frac) |
+| Line break | `[$x \ y ]` | [`linebreak`]($func/linebreak) |
+| Alignment point | `[$x &= 2 \ &= 3$]` | [Math]($category/math) |
+| Variable access | `[$pi$]` | [Math]($category/math) |
+| Field access | `[$arrow.r.long$]` | [Scripting]($scripting/#fields) |
+| Implied multiplication | `[$x y$]` | [Math]($category/math) |
+| Symbol shorthand | `[$->, !=$]` | [Symbols]($category/symbols/sym) |
+| Text/string in math | `[$a "is natural"$]` | [Math]($category/math) |
+| Math function call | `[$floor(x)$]` | [Math]($category/math) |
+| Code expression | `[$#rect(width: 1cm)$]` | [Scripting]($scripting/#expressions) |
+| Character escape | `[$x\^2$]` | [Below](#escapes) |
+| Comment | `[$/* comment */$]` | [Below](#comments) |
+
+## Code mode { #code }
+Within code blocks and expressions, new expressions can start without a leading
+`#` character. Many syntactic elements are specific to expressions. Below is
+a table listing all syntax that is available in code mode:
+
+| Name | Example | See |
+| ------------------------ | ----------------------------- | ---------------------------------------------------- |
+| Variable access | `{x}` | [Scripting]($scripting/#blocks) |
+| Any literal | `{1pt, "hey"}` | [Types]($types) |
+| Code block | `{{ let x = 1; x + 2 }}` | [Scripting]($scripting/#blocks) |
+| Content block | `{[*Hello*]}` | [Scripting]($scripting/#blocks) |
+| Parenthesized expression | `{(1 + 2)}` | [Scripting]($scripting/#blocks) |
+| Array | `{(1, 2, 3)}` | [Array]($type/array) |
+| Dictionary | `{(a: "hi", b: 2)}` | [Dictionary]($type/dictionary) |
+| Unary operator | `{-x}` | [Scripting]($scripting/#operators) |
+| Binary operator | `{x + y}` | [Scripting]($scripting/#operators) |
+| Assignment | `{x = 1}` | [Scripting]($scripting/#operators) |
+| Field access | `{x.y}` | [Scripting]($scripting/#fields) |
+| Method call | `{x.flatten()}` | [Scripting]($scripting/#methods) |
+| Function call | `{min(x, y)}` | [Function]($type/function) |
+| Unnamed function | `{(x, y) => x + y}` | [Function]($type/function) |
+| Let binding | `{let x = 1}` | [Scripting]($scripting/#bindings) |
+| Named function | `{let f(x) = 2 * x}` | [Function]($type/function) |
+| Set rule | `{set text(14pt)}` | [Styling]($styling/#set-rules) |
+| Set-if rule | `{set text(..) if .. }` | [Styling]($styling/#set-rules) |
+| Show rule | `{show raw: it => {..}}` | [Styling]($styling/#set-rules) |
+| Show-set rule | `{show par: set block(..)}` | [Styling]($styling/#show-rules) |
+| Conditional | `{if x == 1 {..} else {..}}` | [Scripting]($scripting/#conditionals) |
+| For loop | `{for x in (1, 2, 3) {..}}` | [Scripting]($scripting/#loops) |
+| While loop | `{while x < 10 {..}}` | [Scripting]($scripting/#loops) |
+| Loop control flow | `{break, continue}` | [Scripting]($scripting/#loops) |
+| Return from function | `{return x}` | [Function]($type/function) |
+| Include module | `{include "bar.typ"}` | [Scripting]($scripting/#modules) |
+| Import module | `{import "bar.typ"}` | [Scripting]($scripting/#modules) |
+| Import items from module | `{import "bar.typ": a, b, c}` | [Scripting]($scripting/#modules) |
+| Comment | `[/* block */, // line]` | [Below](#comments) |
+
+## Comments { #comments }
+Comments are ignored by Typst and will not be included in the output. This
+is useful to exclude old versions or to add annotations.
+To comment out a single line, start it with `//`:
+```example
+// our data barely supports
+// this claim
+
+We show with $p < 0.05$
+that the difference is
+significant.
+```
+
+Comments can also be wrapped between `/*` and `*/`. In this case, the comment
+can span over multiple lines:
+```example
+Our study design is as follows:
+/* Somebody write this up:
+ - 1000 participants.
+ - 2x2 data design. */
+```
+
+## Escape sequences { #escapes }
+Escape sequences are used to insert special characters that are otherwise have
+special meaning in Typst. To escape a character, precede it with a backslash. To
+insert any Unicode codepoint, you can write a hexadecimal escape sequence:
+`[\u{1f600}]`. The same kind of escape sequences also work in
+[strings]($type/string).
+
+```example
+I got an ice cream for
+\$1.50! \u{1f600}
+```
diff --git a/docs/src/reference/types.md b/docs/src/reference/types.md
new file mode 100644
index 000000000..744e1abd1
--- /dev/null
+++ b/docs/src/reference/types.md
@@ -0,0 +1,837 @@
+# None
+A type that indicates the absence of any other value.
+
+The none type has exactly one value: `{none}`.
+
+When inserted into the document, it is not visible.
+This is also the value that is produced by empty code blocks.
+It can be [joined]($scripting/#blocks) with any value, yielding
+the other value.
+
+## Example
+```example
+Not visible: #none
+```
+
+# Auto
+A type that indicates a smart default.
+
+The auto type has exactly one value: `{auto}`.
+
+Parameters that support the `{auto}` value have some smart default or contextual
+behaviour. A good example is the [text direction]($func/text.dir) parameter.
+Setting it to `{auto}` lets Typst automatically determine the direction from the
+[text language]($func/text.lang).
+
+# Boolean
+A value with two states.
+
+The boolean type has two values: `{true}` and `{false}`. It denotes whether
+something is active or enabled.
+
+## Example
+```example
+#false \
+#true \
+#(1 < 2)
+```
+
+# Integer
+A whole number.
+
+The number can be negative, zero, or positive. As Typst uses 64 bits to store
+integers, integers cannot be smaller than `{-9223372036854775808}` or larger than
+`{9223372036854775807}`.
+
+## Example
+```example
+#(1 + 2) \
+#(2 - 5) \
+#(3 + 4 < 8)
+```
+
+# Float
+A floating-pointer number.
+
+A limited-precision representation of a real number. Typst uses 64 bits to
+store floats. Wherever a float is expected, you can also pass an
+[integer]($type/integer).
+
+## Example
+```example
+#3.14 \
+#1e4 \
+#(10 / 4)
+```
+
+# Length
+A size or distance, possibly expressed with contextual units.
+Typst supports the following length units:
+
+- Points: `{72pt}`
+- Millimeters: `{254mm}`
+- Centimeters: `{2.54cm}`
+- Inches: `{1in}`
+- Relative to font size: `{2.5em}`
+
+## Example
+```example
+#rect(width: 20pt) \
+#rect(width: 2em) \
+#rect(width: 1in)
+```
+
+# Angle
+An angle describing a rotation.
+Typst supports the following angular units:
+
+- Degrees: `{180deg}`
+- Radians: `{3.14rad}`
+
+## Example
+```example
+#rotate(10deg)[Hello there!]
+```
+
+# Ratio
+A ratio of a whole.
+
+Written as a number, followed by a percent sign.
+
+## Example
+```example
+#set align(center)
+#scale(x: 150%)[
+ Scaled apart.
+]
+```
+
+# Relative Length
+A length in relation to some known length.
+
+This type is a combination of a [length]($type/length) with a
+[ratio]($type/ratio). It results from addition and subtraction
+of a length and a ratio. Wherever a relative length is expected, you can also
+use a bare length or ratio.
+
+## Example
+```example
+#rect(width: 100% - 50pt)
+```
+
+# Fraction
+Defines how the the remaining space in a layout is distributed.
+
+Each fractionally sized element gets space based on the ratio of its fraction to
+the sum of all fractions.
+
+For more details, also see the [h]($func/h) and [v]($func/v) functions and the
+[grid function]($func/grid).
+
+## Example
+```example
+Left #h(1fr) Left-ish #h(2fr) Right
+```
+
+# Color
+A color in a specific color space.
+
+Typst supports:
+- sRGB through the [`rgb` function]($func/rgb)
+- Device CMYK through [`cmyk` function]($func/cmyk)
+- D65 Gray through the [`luma` function]($func/luma)
+
+Furthermore, Typst provides the following built-in colors:
+
+`black`, `gray`, `silver`, `white`, `navy`, `blue`, `aqua`, `teal`, `eastern`,
+`purple`, `fuchsia`, `maroon`, `red`, `orange`, `yellow`, `olive`, `green`, and
+`lime`.
+
+## Methods
+### lighten()
+Lightens a color.
+
+- amount: ratio (positional, required)
+ The factor to lighten the color by.
+
+### darken()
+Darkens a color.
+
+- amount: ratio (positional, required)
+ The factor to darken the color by.
+
+### negate()
+Produces the negative of the color.
+
+# Symbol
+A Unicode symbol.
+
+Typst defines common symbols so that they can easily be written with standard
+keyboards. The symbols are defined in modules, from which they can be accessed
+using [field access notation]($scripting/#fields):
+
+- General symbols are defined in the [`sym` module]($category/symbols/sym)
+- Emoji are defined in the [`emoji` module]($category/symbols/emoji)
+
+Moreover, you can define custom symbols with the [symbol]($func/symbol)
+function.
+
+```example
+#sym.arrow.r \
+#sym.gt.eq.not \
+$gt.eq.not$ \
+#emoji.face.halo
+```
+
+Many symbols have different variants, which can be selected by appending the
+modifiers with dot notation. The order of the modifiers is not relevant. Visit
+the documentation pages of the symbol modules and click on a symbol to see its
+available variants.
+
+```example
+$arrow.l$ \
+$arrow.r$ \
+$arrow.t.quad$
+```
+
+# String
+A sequence of Unicode codepoints.
+
+You can iterate over the characters (or rather, grapheme clusters) of the string
+using a [for loop]($scripting/#loops). Strings can be added with
+the `+` operator, [joined together]($scripting/#blocks) and
+multiplied with integers.
+
+Typst provides utility methods for string manipulation. Many of these methods
+(e.g., `split`, `trim` and `replace`) operate on _patterns:_ A pattern can be
+either a string or a [regular expression]($func/regex). This makes the methods
+quite versatile.
+
+_Note:_ Currently all lengths and indices are expressed in terms of UTF-8 bytes.
+This _might_ change to grapheme clusters in the future.
+
+### Example
+```example
+#"hello world!" \
+#"\"hello\n world\"!" \
+#"1 2 3".split() \
+#"1,2;3".split(regex("[,;]")) \
+#(regex("\d+") in "ten euros") \
+#(regex("\d+") in "10 euros")
+```
+
+### Escape sequences
+Just like in markup, you can escape a few symbols in strings:
+- `[\\]` for a backslash
+- `[\"]` for a quote
+- `[\n]` for a newline
+- `[\r]` for a carriage return
+- `[\t]` for a tab
+- `[\u{1f600}]` for a hexadecimal Unicode escape sequence
+
+## Methods
+### len()
+The length of the string in UTF-8 encoded bytes.
+
+- returns: integer
+
+### first()
+Extract the first character (or rather, grapheme cluster) of the string.
+Fails with an error if the string is empty.
+
+- returns: any
+
+### last()
+Extract the last character (or rather, grapheme cluster) of the string.
+Fails with an error if the string is empty.
+
+- returns: any
+
+### at()
+Extract the first character (or rather, grapheme cluster) after the specified
+index. Fails with an error if the index is out of bounds.
+
+- index: integer (positional, required)
+ The byte index.
+- returns: string
+
+### slice()
+Extract a substring of the string.
+Fails with an error if the start or end index is out of bounds.
+
+- start: integer (positional, required)
+ The start byte index (inclusive).
+- end: integer (positional)
+ The end byte index (exclusive). If omitted, the whole slice until the end of the
+ string is extracted.
+- count: integer (named)
+ The number of bytes to extract. This is equivalent to passing `start + count`
+ as the `end` position. Mutually exclusive with `end`.
+- returns: string
+
+### contains()
+Whether the string contains the specified pattern.
+
+This method also has dedicated syntax: You can write `{"bc" in "abcd"}` instead
+of `{"abcd".contains("bc")}`.
+
+- pattern: string or regex (positional, required)
+ The pattern to search for.
+- returns: boolean
+
+### starts-with()
+Whether the string starts with the specified pattern.
+
+- pattern: string or regex (positional, required)
+ The pattern the string might start with.
+- returns: boolean
+
+### ends-with()
+Whether the string ends with the specified pattern.
+
+- pattern: string or regex (positional, required)
+ The pattern the string might end with.
+- returns: boolean
+
+### find()
+Searches for the specified pattern in the string and returns the first match
+as a string or `{none}` if there is no match.
+
+- pattern: string or regex (positional, required)
+ The pattern to search for.
+- returns: string or none
+
+### position()
+Searches for the specified pattern in the string and returns the index of the
+first match as an integer or `{none}` if there is no match.
+
+- pattern: string or regex (positional, required)
+ The pattern to search for.
+- returns: integer or none
+
+### match()
+Searches for the specified pattern in the string and returns a dictionary
+with details about the first match or `{none}` if there is no match.
+
+The returned dictionary has the following keys:
+* `start`: The start offset of the match
+* `end`: The end offset of the match
+* `text`: The text that matched.
+* `captures`: An array containing a string for each matched capturing group. The
+ first item of the array contains the first matched capturing, not the whole
+ match! This is empty unless the `pattern` was a regex with capturing groups.
+
+- pattern: string or regex (positional, required)
+ The pattern to search for.
+- returns: dictionary or none
+
+### matches()
+Searches for the specified pattern in the string and returns an array of
+dictionaries with details about all matches. For details about the returned
+dictionaries, see above.
+
+- pattern: string or regex (positional, required)
+ The pattern to search for.
+- returns: array
+
+### replace()
+Replaces all or a specified number of matches of a pattern with a replacement
+string and returns the resulting string.
+
+- pattern: string or regex (positional, required)
+ The pattern to search for.
+- replacement: string (positional, required)
+ The string to replace the matches with.
+- count: integer (named)
+ If given, only the first `count` matches of the pattern are placed.
+- returns: string
+
+### trim()
+Removes matches of a pattern from one or both sides of the string, once or
+repeatedly and returns the resulting string.
+
+- pattern: string or regex (positional, required)
+ The pattern to search for.
+- at: alignment (named)
+ Can be `start` or `end` to only trim the start or end of the string.
+ If omitted, both sides are trimmed.
+- repeat: boolean (named)
+ Whether to repeatedly removes matches of the pattern or just once.
+ Defaults to `{true}`.
+- returns: string
+
+### split()
+Splits a string at matches of a specified pattern and returns an array of
+the resulting parts.
+
+- pattern: string or regex (positional)
+ The pattern to split at. Defaults to whitespace.
+- returns: array
+
+# Content
+Representation of text, elements, and more.
+
+This type is at the heart of Typst. All markup you write and most
+[functions]($type/function) you call produce content values. You
+can create a content value by enclosing markup in square brackets. This is also
+how you pass content to functions. Typst does not allow you to peek into
+content, but you can affect its appearance in various ways using set and show
+rules. See the chapter on [styling]($styling) for more details.
+
+```example
+#type([*Hello!*]) \
+#strong[Hello!]
+```
+
+Content can be added with the `+` operator,
+[joined together]($scripting/#blocks) and multiplied with
+integers. Wherever content is expected, you can also pass a
+[string]($type/string) or `{none}`.
+
+### Reactivity
+Content is reactive to the styles that are active where it is inserted.
+When you write a set or show rule, content that was _created_ before the show
+rule is stilled affected by the show rule if it is _inserted_ after the show
+rule.
+
+```example
+// Content is created here.
+#let mytext = [= A heading]
+
+// But still affected by the
+// styles that are active here.
+#show heading: set text(green)
+#mytext
+```
+
+# Array
+A sequence of values.
+
+You can construct an array by enclosing a comma-separated sequence of values
+in parentheses. The values do not have to be of the same type.
+
+You can access and update array elements with the `.at()` method. Indices are
+zero-based and negative indices wrap around to the end of the array. You can
+iterate over an array using a [for loop]($scripting/#loops).
+Arrays can be added together with the `+` operator,
+[joined together]($scripting/#blocks) and multiplied with
+integers.
+
+Empty parenthesis yield an array of length zero and a parentheses-wrapped value
+with trailing comma yields an array of length one.
+
+## Example
+```example
+#let values = (1, 7, 4, -3, 2)
+
+#values.at(0) \
+#(values.at(0) = 3)
+#values.at(-1) \
+#values.find(calc.even) \
+#values.filter(calc.odd) \
+#values.map(calc.abs) \
+#values.rev() \
+#(1, (2, 3)).flatten() \
+#(("A", "B", "C")
+ .join(", ", last: " and "))
+```
+
+## Methods
+### len()
+The number of values in the array.
+
+- returns: integer
+
+### first()
+Returns the first element in the array.
+May be used on the left-hand side of an assignment.
+Fails with an error if the array is empty.
+
+- returns: any
+
+### last()
+Returns the last element in the array.
+May be used on the left-hand side of an assignment.
+Fails with an error if the array is empty.
+
+- returns: any
+
+### at()
+Returns the element at the specified index in the array.
+May be used on the left-hand side of an assignment.
+Fails with an error if the index is out of bounds.
+
+- index: integer (positional, required)
+ The index at which to retrieve the element.
+- returns: any
+
+### push()
+Add a value to the end of the array.
+
+- value: any (positional, required)
+ The value to insert at the end of the array.
+
+### pop()
+Remove the last element from the array and return it.
+Fails with an error if the array is empty.
+
+- returns: any
+ The removed last value.
+
+### insert()
+Insert a value into the array at the specified index.
+Fails with an error if the index is out of bounds.
+
+- index: integer (positional, required)
+ The index at which to insert the element.
+- value: any (positional, required)
+ The value to insert into the array.
+
+### remove()
+Remove the value at the specified index from the array and return it.
+
+- index: integer (positional, required)
+ The index at which to remove the element.
+- returns: any
+
+### slice()
+Extract a subslice of the array.
+Fails with an error if the start or index is out of bounds.
+
+- start: integer (positional, required)
+ The start index (inclusive).
+- end: integer (positional)
+ The end index (exclusive). If omitted, the whole slice until the end of the
+ array is extracted.
+- count: integer (named)
+ The number of elements to extract. This is equivalent to passing `start +
+ count` as the `end` position. Mutually exclusive with `end`.
+- returns: array
+
+### contains()
+Whether the array contains the specified value.
+
+This method also has dedicated syntax: You can write `{2 in (1, 2, 3)}` instead
+of `{(1, 2, 3).contains(2)}`.
+
+- value: any (positional, required)
+ The value to search for.
+- returns: boolean
+
+### find()
+Searches for an element for which the given function returns `{true}` and
+returns the first match or `{none}` if there is no match.
+
+- searcher: function (positional, required)
+ The function to apply to each element. Must return a boolean.
+- returns: any or none
+
+### position()
+Searches for an element for which the given function returns `{true}` and
+returns the index of the first match or `{none}` if there is no match.
+
+- searcher: function (positional, required)
+ The function to apply to each element. Must return a boolean.
+- returns: integer or none
+
+### filter()
+Produces a new array with only the elements from the original one for which the
+given function returns true.
+
+- test: function (positional, required)
+ The function to apply to each element. Must return a boolean.
+- returns: array
+
+### map()
+Produces a new array in which all elements from the original one were
+transformed with the given function.
+
+- mapper: function (positional, required)
+ The function to apply to each element.
+- returns: array
+
+### fold()
+Folds all elements into a single value using an accumulator function.
+
+- init: any (positional, required)
+ The initial value to start with.
+- folder: function (positional, required)
+ The folding function. Must have two parameters: One for the accumulated value
+ and one for an element.
+- returns: any
+
+### any()
+Whether the given function returns `{true}` for any element in the array.
+
+- test: function (positional, required)
+ The function to apply to each element. Must return a boolean.
+- returns: boolean
+
+### all()
+Whether the given function returns `{true}` for all elements in the array.
+
+- test: function (positional, required)
+ The function to apply to each element. Must return a boolean.
+- returns: boolean
+
+### flatten()
+Combine all nested arrays into a single flat one.
+
+- returns: array
+
+### rev()
+Return a new array with the same elements, but in reverse order.
+
+- returns: array
+
+### join()
+Combine all elements in the array into one.
+
+- separator: any (positional)
+ A value to insert between each element of the array.
+- last: any (named)
+ An alternative separator between the last two elements
+- returns: any
+
+### sorted()
+Return a new array with the same elements, but sorted.
+
+- returns: array
+
+# Dictionary
+A map from string keys to values.
+
+You can construct a dictionary by enclosing comma-separated `key: value` pairs
+in parentheses. The values do not have to be of the same type.
+
+A dictionary is conceptually similar to an array, but it is indexed by strings
+instead of integers. You can access and create dictionary entries with the
+`.at()` method. If you know the key statically, you can alternatively use
+[field access notation]($scripting/#fields) (`.key`) to access
+the value. Dictionaries can be added with the `+` operator and
+[joined together]($scripting/#blocks).
+
+You can iterate over the pairs in a dictionary using a
+[for loop]($scripting/#loops).
+Dictionaries are always ordered by key.
+
+Since empty parentheses already yield an empty array, you have to use the
+special `(:)` syntax to create an empty dictionary.
+
+
+## Example
+```example
+#let dict = (
+ name: "Typst",
+ born: 2019,
+)
+
+#dict.name \
+#(dict.launch = 20)
+#dict.len() \
+#dict.keys() \
+#dict.values() \
+#dict.at("born") \
+#dict.insert("city", "Berlin ")
+```
+
+## Methods
+### len()
+The number of pairs in the dictionary.
+
+- returns: integer
+
+### at()
+Returns the value associated with the specified key in the dictionary.
+May be used on the left-hand side of an assignment if the key is already
+present in the dictionary.
+Fails with an error if the key is not part of the dictionary.
+
+- index: integer (positional, required)
+ The index at which to retrieve the element.
+- returns: any
+
+### insert()
+Insert a new pair into the dictionary and return the value.
+
+- key: string (positional, required)
+ The key of the pair that should be inserted.
+- value: any (positional, required)
+ The value of the pair that should be inserted.
+
+### keys()
+Returns the keys of the dictionary as an array in sorted order.
+
+- returns: array
+
+### values()
+Returns the values of the dictionary as an array in key-order.
+
+- returns: array
+
+### pairs()
+Call a function for each key-value pair and return the results in an array.
+
+- mapper: function (positional, required)
+ The function to apply to each pair. Gets passed the key and value as two
+ separate arguments.
+- returns: array
+
+### remove()
+Remove a pair from the dictionary by key and return the value.
+
+- key: string (positional, required)
+ The key of the pair that should be removed.
+- returns: any
+
+# Function
+A mapping from argument values to a return value.
+
+You can call a function by writing a comma-separated list of function
+_arguments_ enclosed in parentheses directly after the function name.
+Additionally, you can pass any number of trailing content blocks arguments to a
+function _after_ the normal argument list. If the normal argument list would
+become empty, it can be omitted. Typst supports positional and named arguments.
+The former are identified by position and type, while the later are written as
+`name: value`.
+
+Within math mode, function calls have special behaviour. See the
+[math documentation]($category/math) for more details.
+
+### Example
+```example
+// Call a function.
+#list([A], [B])
+
+// Named arguments and trailing
+// content blocks.
+#enum(start: 2)[A][B]
+
+// Version without parentheses.
+#list[A][B]
+```
+
+Functions are a fundamental building block of Typst. Typst provides functions
+for a variety of typesetting tasks. Moreover, the markup you write is backed by
+functions and all styling happens through functions. This reference lists all
+available functions and how you can use them. Please also refer to the
+documentation about [set]($styling/#set-rules) and
+[show]($styling/#show-rules) rules to learn about additional ways
+you can work with functions in Typst.
+
+### Defining functions { #definitions }
+You can define your own function with a
+[let binding]($scripting/#bindings) that has a parameter list after
+the binding's name. The parameter list can contain positional parameters,
+named parameters with default values and
+[argument sinks]($type/arguments).
+The right-hand side of the binding can be a block or any other expression. It
+defines the function's return value and can depend on the parameters.
+
+```example
+#let alert(body, fill: red) = {
+ set text(white)
+ set align(center)
+ rect(
+ fill: fill,
+ inset: 8pt,
+ radius: 4pt,
+ [*Warning:\ #body*],
+ )
+}
+
+#alert[
+ Danger is imminent!
+]
+
+#alert(fill: blue)[
+ KEEP OFF TRACKS
+]
+```
+
+### Unnamed functions { #unnamed }
+You can also created an unnamed function without creating a binding by
+specifying a parameter list followed by `=>` and the function body. If your
+function has just one parameter, the parentheses around the parameter list are
+optional. Unnamed functions are mainly useful for show rules, but also for
+settable properties that take functions like the page function's
+[`footer`]($func/page.footer) property.
+
+```example
+#show "once?": it => [#it #it]
+once?
+```
+
+### Notable fact
+In Typst, all functions are _pure._ This means that for the same
+arguments, they always return the same result. They cannot "remember" things to
+produce another value when they are called a second time.
+
+## Methods
+### with()
+Returns a new function that has the given arguments pre-applied.
+
+- arguments: any (variadic)
+ The named and positional arguments to apply.
+- returns: function
+
+### where()
+Returns a selector that filters for elements belonging to this function
+whose fields have the values of the given arguments.
+
+- fields: any (named, variadic)
+ The field values to filter by.
+- returns: selector
+
+# Arguments
+Captured arguments to a function.
+
+Like built-in functions, custom functions can also take a variable number of
+arguments. You can specify an _argument sink_ which collects all excess
+arguments as `..sink`. The resulting `sink` value is of the `arguments` type. It
+exposes methods to access the positional and named arguments and is iterable
+with a [for loop]($scripting/#loops). Inversely, you can spread
+arguments, arrays and dictionaries into a function call with the spread operator:
+`{func(..args)}`.
+
+## Example
+```example
+#let format(title, ..authors) = [
+ *{title}* \
+ _Written by {authors
+ .pos()
+ .join(", ", last: " and ")}._
+]
+
+#format("ArtosFlow", "Jane", "Joe")
+```
+
+## Methods
+### pos()
+Returns the captured positional arguments as an array.
+
+- returns: array
+
+### named()
+Returns the captured named arguments as a dictionary.
+
+- returns: dictionary
+
+# Module
+An evaluated module, either built-in or resulting from a file.
+
+You can access definitions from the module using
+[field access notation]($scripting/#fields) and interact with it using the
+[import and include syntaxes]($scripting/#modules).
+
+## Example
+```example
+<<< #import "utils.typ"
+<<< #utils.add(2, 5)
+
+<<< #import utils: sub
+<<< #sub(1, 4)
+>>> #7
+>>>
+>>> #(-3)
+```
diff --git a/docs/src/reference/welcome.md b/docs/src/reference/welcome.md
new file mode 100644
index 000000000..dc38663da
--- /dev/null
+++ b/docs/src/reference/welcome.md
@@ -0,0 +1,29 @@
+---
+description: |
+ The Typst reference is a systematic and comprehensive guide to the Typst
+ typesetting language.
+---
+
+# Reference
+This reference documentation is a comprehensive guide to all of Typst's
+syntax, concepts, types, and functions. If you are completely new to Typst, we
+recommend starting with the [tutorial]($tutorial) and then coming back to
+the reference to learn more about Typst's features as you need them.
+
+## Language
+The reference starts with a language part that gives an overview over [Typst's
+syntax]($syntax) and contains information about concepts involved in
+[styling documents,]($styling) using
+[Typst's scripting capabilities,]($scripting) and a detailed documentation of
+all [data types]($types) in Typst.
+
+## Functions
+The second part includes chapters on all functions used to insert, style, transform,
+and layout content in Typst documents. Each function is documented with a
+description of its purpose, a list of its parameters, and examples of how to use
+it.
+
+The final part of the reference explains all functions that are used within
+Typst's code mode to manipulate and transform data. Just as in the previous
+part, each function is documented with a description of its purpose, a list of
+its parameters, and examples of how to use it.
diff --git a/docs/src/tutorial/1-writing.md b/docs/src/tutorial/1-writing.md
new file mode 100644
index 000000000..037801b5c
--- /dev/null
+++ b/docs/src/tutorial/1-writing.md
@@ -0,0 +1,269 @@
+---
+description: Typst's tutorial.
+---
+
+# Writing in Typst
+Let's get started! Suppose you got assigned to write a technical report for
+university. It will contain prose, maths, headings, and figures. To get started,
+you create a new project on the Typst app. You'll be taken to the editor where
+you see two panels: A source panel where you compose your document and a
+preview panel where you see the rendered document.
+
+
+
+You already have a good angle for your report in mind. So let's start by writing
+the introduction. Enter some text in the editor panel. You'll notice that the
+text immediately appears on the previewed page.
+
+```example
+In this report, we will explore the
+various factors that influence fluid
+dynamics in glaciers and how they
+contribute to the formation and
+behavior of these natural structures.
+```
+
+_Throughout this tutorial, we'll show code examples like this one. Just like in the app, the first panel contains markup and the second panel shows a preview. We shrunk the page to fit the examples so you can see what's going on._
+
+The next step is to add a heading and emphasize some text. Typst uses simple
+markup for the most common formatting tasks. To add a heading, enter the `=`
+character and to emphasize some text with italics, enclose it in
+`[_underscores_]`.
+
+```example
+= Introduction
+In this report, we will explore the
+various factors that influence _fluid
+dynamics_ in glaciers and how they
+contribute to the formation and
+behavior of these natural structures.
+```
+
+That was easy! To add a new paragraph, just add a blank line in between two
+lines of text. If that paragraph needs a subheading, produce it by typing `==`
+instead of `=`. The number of `=` characters determines the nesting level of the
+heading.
+
+Now we want to list a few of the circumstances that influence glacier dynamics.
+To do that, we use a numbered list. For each item of the list, we type a `+`
+character at the beginning of the line. Typst will automatically number the
+items.
+
+```example
++ The climate
++ The topography
++ The geology
+```
+
+If we wanted to add a bulleted list, we would use the `-` character instead of the
+`+` character. We can also nest lists: For example, we can add a sub-list to the
+first item of the list above by indenting it.
+
+```example
++ The climate
+ - Temperature
+ - Precipitation
++ The topography
++ The geology
+```
+
+## Adding images
+You think that your report would benefit from a figure. Let's add one. Typst
+supports images in the formats PNG, JPEG, GIF, and SVG. To add an image file to
+your project, first open the _file panel_ by clicking the box icon in the left
+sidebar. Here, you can see a list of all files in your project. Currently, there
+is only one: The main Typst file you are writing in. To upload another file,
+click the button with the arrow in the top-right corner. This opens the upload
+dialog, in which you can pick files to upload from your computer. Select an
+image file for your report.
+
+
+
+We have seen before that specific symbols (called _markup_) have specific
+meaning in Typst. We can use `=`, `-`, `+`, and `_` to create headings, lists
+and emphasized text, respectively. However, having a special symbol for
+everything we want to insert into our document would soon become cryptic and
+unwieldy. For this reason, Typst reserves markup symbols only for the most common
+things. Everything else is inserted with _functions._ For our image to show up
+on the page, we use Typst's [`image`]($func/image) function.
+
+```example
+#image("glacier.jpg")
+```
+
+In general, a function produces some output for a set of _arguments_. When you
+_call_ a function within markup, you provide the arguments and Typst inserts the
+result (the function's _return value_) into the document. In our case, the
+`image` function takes one argument: The path to the image file. To call a
+function, we first need to type the `#` character, immediately followed by the
+name of the function. Then, we enclose the arguments in parentheses. Typst
+recognizes many different data types within argument lists. Our file path is a
+short [string of text]($type/string), so we need to enclose it
+in double quotes.
+
+The inserted image uses the whole width of the page. To change that, pass the
+`width` argument to the `image` function. This is a _named_ argument and
+therefore specified as a `name: value` pair. If there are multiple arguments,
+they are separated by commas, so we first need to put a comma behind the path.
+
+```example
+#image("glacier.jpg", width: 70%)
+```
+
+The `width` argument is a
+[relative length]($type/relative-length). In our case, we specified a
+percentage, determining that the image shall take up `{70%}` of the page's
+width. We also could have specified an absolute value like `{1cm}` or `{0.7in}`.
+
+Just like text, the image is aligned at the left side of the page by default.
+That looks a bit awkward. Let's center it and add a caption. We achieve this by
+using the [`align`]($func/align) function. This function takes an `alignment` as
+its first argument and then the content that we want to align as the second
+argument:
+
+```example
+#align(center)[
+ #image("glacier.jpg", width: 70%)
+
+ _Glaciers form an important
+ part of the earth's climate
+ system._
+]
+```
+
+Did you spot the closing bracket `]` at the end of the example? Both the
+argument and the caption are enclosed in the same square brackets. Therefore,
+the `align` function centers both of them.
+
+But wait, shouldn't the arguments of a function be specified within parentheses?
+Why is there a second set of square brackets with the aligned content after the
+parentheses?
+
+The answer is that, as passing content to a function is such a common thing
+to do in Typst, there is special syntax for it: Instead of putting the content
+inside of the argument list, you can write it in square brackets directly after
+the normal arguments, saving on punctuation. We call markup in square brackets
+a _content block._
+
+
+
+So far, we've passed content blocks (markup in square brackets) and strings
+(text in double quotes) to our functions. Both seem to contain text. What's the
+difference?
+
+A content block can contain text, but also any other kind of markup, function
+calls, and more, whereas a string is really just a _sequence of characters_ and
+nothing else.
+
+For example, the image function expects a path to an image file.
+It would not make sense to pass, e.g., a paragraph of text or a another image as
+the image's path parameter. That's why only strings are allowed here.
+On the contrary, strings work wherever content is expected because text is a
+kind of valid content.
+
+
+## Maths
+After fleshing out the introduction, you move on to the meat of the document:
+Your equations. Typst has built-in mathematical typesetting and uses its own
+math notation. Let's start with a simple equation. We wrap it in `[$]` signs
+to let Typst know it should expect a mathematical expression:
+
+```example
+The equation $Q = rho A v + C$
+defines the glacial flow rate.
+```
+
+The equation is typeset inline, on the same line as the surrounding text. If you
+want to have it on its own line instead, you should insert a single space at its
+start and end:
+
+```example
+The flow rate of a glacier is
+defined by the following equation:
+
+$ Q = rho A v + C $
+```
+
+We can see that Typst displayed the single letters `Q`, `A`, `v`, and `C` as-is,
+while it translated `rho` into a Greek letter. Math mode will always show single
+letters verbatim. Multiple letters, however, are interpreted as symbols,
+variables, or function names. To imply a multiplication between single letters,
+put spaces between them.
+
+If you want to have a variable that consists of multiple letters, you can
+enclose it in quotes:
+
+```example
+The flow rate of a glacier is given by
+the following equation:
+
+$ Q = rho A v + "time offset" $
+```
+
+You'll also need a sum formula in your paper. We can use the `sum` symbol and
+then specify the range of the summation in sub- and superscripts:
+
+```example
+Total displaced soil by glacial flow:
+
+$ 7.32 beta +
+ sum_(i=0)^nabla Q_i / 2 $
+```
+
+To add a subscript to a symbol or variable, type a `_` character and then the
+superscript. Similarly, use the `^` character for a superscript. If your
+sub- or superscript consists of multiple things, you must enclose them
+in round parentheses.
+
+The above example also showed us how to insert fractions: Simply put a `/`
+character between the numerator and the denominator and Typst will automatically
+turn it into a fraction. Parentheses are smartly resolved, so you can enter your
+expression as you would into a calculator and Typst will replace parenthesized
+sub-expressions with the appropriate notation.
+
+```example
+Total displaced soil by glacial flow:
+
+$ 7.32 beta +
+ sum_(i=0)^nabla
+ (Q_i (a_i - epsilon)) / 2 $
+```
+
+Not all math constructs have special syntax. Instead, we use functions, just
+like the `image` function we have seen before. For example, to insert a column
+vector, we can use the [`vec`]($func/vec) function. Within math mode, function
+calls don't need to start with the `#` character.
+
+```example
+$ v := vec(x_1, x_2, x_3) $
+```
+
+Some functions are only available within math mode. For example, the
+[`cal`]($func/cal) function is used to typeset calligraphic letters commonly used for
+sets. The [math section of the reference]($category/math) provides a
+complete list of all functions that math mode makes available.
+
+One more thing: Many symbols, such as the arrow, have a lot of variants. You can
+select among these variants by appending a dot and a modifier name to a symbol's
+name:
+
+```example
+$ a arrow.squiggly b $
+```
+
+This notation is also available in markup mode, but the complete symbol name
+with modifiers must then be enclosed in colons. See the documentation of the [text]($category/text) and [math sections]($category/math) for more details.
+
+## Review
+You have now seen how to write a basic document in Typst. You learned how to
+emphasize text, write lists, insert images, align content, and typeset
+mathematical expressions. You also learned about Typst's functions. There are
+many more kinds of content that Typst lets you insert into your document, such
+as [tables]($func/table), [shapes]($category/visualize), and
+[code blocks]($func/raw). You can peruse the [reference]($reference) to learn
+more about these and other features.
+
+For the moment, you have completed writing your report. You have already saved a
+PDF by clicking on the download button in the top right corner. However, you
+think the report could look a bit less plain. In the next section, we'll learn
+how to customize the look of our document.
diff --git a/docs/src/tutorial/2-formatting.md b/docs/src/tutorial/2-formatting.md
new file mode 100644
index 000000000..dd0352bd7
--- /dev/null
+++ b/docs/src/tutorial/2-formatting.md
@@ -0,0 +1,273 @@
+---
+description: Typst's tutorial.
+---
+
+# Formatting
+So far, you have written a report with some text, a few equations and images.
+However, it still looks very plain. Your teaching assistant does not yet know
+that you are using a new typesetting system, and you want your report to fit in
+with the other student's submissions. In this chapter, we will see how to format
+your report using Typst's styling system.
+
+## Set rules
+As we have seen in the previous chapter, Typst has functions that _insert_
+content (e.g. the [`image`]($func/image) function) and others that _manipulate_
+content that they received as arguments (e.g. the [`align`]($func/align)
+function). The first impulse you might have when you want, for example, to
+justify the report, could be to look for a function that does that and wrap the
+complete document in it.
+
+```example
+#par(justify: true)[
+ = Background
+ In the case of glaciers, fluid
+ dynamics principles can be used
+ to understand how the movement
+ and behavior of the ice is
+ influenced by factors such as
+ temperature, pressure, and the
+ presence of other fluids (such as
+ water).
+]
+```
+
+As seen above, that works. The [`par`]($func/par) function justifies all
+paragraphs within it. However, wrapping the document in countless functions and
+applying styles selectively and in-situ can quickly become cumbersome.
+
+Fortunately, Typst has a more elegant solution. With _set rules,_ you can apply
+style properties to all occurrences of some kind of content. You write a set
+rule by entering the `{set}` keyword, followed by the name of the function whose
+properties you want to set, and a list of arguments in parentheses.
+
+```example
+#set par(justify: true)
+
+= Background
+In the case of glaciers, fluid
+dynamics principles can be used
+to understand how the movement
+and behavior of the ice is
+influenced by factors such as
+temperature, pressure, and the
+presence of other fluids (such as
+water).
+```
+
+
+
+Want to know in more technical terms what is happening here?
+
+Set rules can be conceptualized as setting default values
+for some of the parameters of a function for all future
+uses of that function.
+
+
+## The autocomplete panel
+If you followed along and tried a few things in the app, you might have noticed
+that always after you enter a `#` character, a panel pops up to show you the
+available functions, and, within an argument list, the available parameters.
+That's the autocomplete panel. It can be very useful while you are writing your
+document: You can apply its suggestions by hitting the Return key or navigate to
+the desired completion with the arrow keys. The panel can be dismissed by
+hitting the Escape key and opened again by typing `#` or hitting
+Ctrl + Space. Use the autocomplete panel to discover the
+right arguments for functions. Most suggestions come with a small description of
+what they do.
+
+
+
+## Set up the page
+Back to set rules: When writing a rule, you choose the function depending on
+what type of element you want to style. Here is a list of some functions that
+are commonly used in set rules:
+
+- [`text`]($func/text) to set font family, size, color, and other properties of
+ text
+- [`page`]($func/page) to set the page size, margins, headers, enable columns,
+ and footers
+- [`par`]($func/par) to justify paragraphs, set line spacing, and more
+- [`heading`]($func/heading) to set the appearance of headings and enable
+ numbering
+- [`document`]($func/document) to set the metadata contained in the PDF output,
+ such as title and author
+
+Not all function parameters can be set. In general, only parameters that tell
+a function _how_ to do something can be set, not those that tell it _what_ to
+do it with. The function reference pages indicate which parameters are settable.
+
+Let's add a few more styles to our document. We want larger margins and a serif
+font. For the purposes of the example, we'll also set another page size.
+
+```example
+#set text(10pt, "Latin Modern Roman")
+#set page(
+ "a6",
+ margin: (x: 1.8cm, y: 1.5cm),
+)
+#set par(
+ justify: true,
+ leading: 0.52em,
+)
+
+= Introduction
+In this report, we will explore the
+various factors that influence fluid
+dynamics in glaciers and how they
+contribute to the formation and
+behavior of these natural structures.
+
+>>> Glacier displacement is influenced
+>>> by a number of factors, including
+>>> + The climate
+>>> + The topography
+>>> + The geology
+>>>
+>>> This report will present a physical
+>>> model of glacier displacement and
+>>> dynamics, and will explore the
+>>> influence of these factors on the
+>>> movement of large bodies of ice.
+<<< ...
+
+#align(center + bottom)[
+ #image("glacier.jpg", width: 70%)
+
+ *Glaciers form an important
+ part of the earth's climate
+ system.*
+]
+```
+
+There are a few things of note here.
+
+First is the [`page`]($func/page) set rule. It receives two arguments: the page
+size and margins for the page. The page size is a string. Typst accepts
+[many standard page sizes,]($func/page.paper) but you can also specify a custom
+page size. The margins are specified as a [dictionary.]($type/dictionary)
+Dictionaries are a collection of key-value pairs. In this case, the keys are `x`
+and `y`, and the values are the horizontal and vertical margins, respectively.
+We could also have specified separate margins for each side by passing a
+dictionary with the keys `{left}`, `{right}`, `{top}`, and `{bottom}`.
+
+Next is the set [`text`]($func/text) set rule. Here, we set the font size to
+`{10pt}` and font family to `{"Latin Modern Roman"}`. The Typst app comes with
+many fonts that you can try for your document. When you are in the text
+function's argument list, you can discover the available fonts in the
+autocomplete panel.
+
+We have also set the spacing between lines (a.k.a. leading): It is specified as
+a [length]($type/length) value, and we used the `em` unit to specify the leading
+relative to the size of the font: `{1em}` is equivalent to the current font size
+(which defaults to `{11pt}`).
+
+Finally, we have bottom aligned our image by adding a vertical alignment to our
+center alignment. Vertical and horizontal alignments can be combined with the
+`{+}` operator to yield a 2D alignment.
+
+## A hint of sophistication
+To structure our document more clearly, we now want to number our headings. We
+can do this by setting the `numbering` parameter of the
+[`heading`]($func/heading) function.
+
+```example
+>>> #set text("Latin Modern Roman")
+#set heading(numbering: "1.")
+
+= Introduction
+#lorem(10)
+
+== Background
+#lorem(12)
+
+== Methods
+#lorem(15)
+```
+
+We specified the string `{"1."}` as the numbering parameter. This tells Typst to
+number the headings with arabic numerals and to put a dot between the number of
+each level. We can also use
+[letters, roman numerals, and symbols]($func/numbering) for our headings:
+
+```example
+>>> #set text("Latin Modern Roman")
+#set heading(numbering: "1.a")
+
+= Introduction
+#lorem(10)
+
+== Background
+#lorem(12)
+
+== Methods
+#lorem(15)
+```
+
+This example also uses the [`lorem`]($func/lorem) function to generate some
+placeholder text. This function takes a number as an argument and generates that
+many words of _Lorem Ipsum_ text.
+
+
+
+Did you wonder why the headings and text set rules apply to all text and headings,
+even if they are not produced with the respective functions?
+
+Typst internally calls the `heading` function every time you write `[= Conclusion]`.
+In fact, the function call `[#heading[Conclusion]]` is equivalent to the heading
+markup above. Other markup elements work similarly, they are only
+_syntax sugar_ for the corresponding function calls.
+
+
+## Show rules
+You are already pretty happy with how this turned out. But one last thing needs
+to be fixed: The report you are writing is intended for a larger project and
+that project's name should always be accompanied by a logo, even in prose.
+
+You consider your options. You could add an `[#image("logo.svg")]` call before
+every instance of the logo using search and replace. That sounds very tedious.
+Instead, you could maybe
+[define a custom function]($type/function/#definitions) that always yields the logo with its image. However, there is an even easier way:
+
+With show rules, you can redefine how Typst displays certain elements. You
+specify which elements Typst should show differently and how they should look.
+Show rules can be applied to instances of text, many functions, and even the
+whole document.
+
+```example
+#show "ArtosFlow": name => box[
+ #box(image(
+ "logo.svg",
+ height: 0.7em,
+ ))
+ #name
+]
+
+This report is embedded in the
+ArtosFlow project. ArtosFlow is a
+project of the Artos Institute.
+```
+
+There is a lot of new syntax in this example: We write the `{show}` keyword,
+followed by a string of text we want to show differently and a colon. Then, we
+write a function that takes the content that shall be shown as an argument.
+Here, we called that argument `name`. We can now use the `name` variable in the
+function's body to print the ArtosFlow name. Our show rule adds the logo image
+in front of the name and puts the result into a box to prevent linebreaks from
+occurring between logo and name. The image is also put inside of a box, so that
+it does not appear in its own paragraph.
+
+The calls to the first box function and the image function did not require a
+leading `#` because they were not embedded directly in markup. When Typst
+expects code instead of markup, the leading `#` is not needed to access
+functions, keywords, and variables. This can be observed in parameter lists,
+function definitions, and [code blocks]($scripting).
+
+## Review
+You now know how to apply basic formatting to your Typst documents. You learned
+how to set the font, justify your paragraphs, change the page dimensions, and
+add numbering to your headings with set rules. You also learned how to use a
+basic show rule to change how text appears throughout your document.
+
+You have handed in your report. Your supervisor was so happy with it that they
+want to adapt it into a conference paper! In the next section, we will learn how
+to format your document as a paper using more advanced show rules and functions.
diff --git a/docs/src/tutorial/3-advanced.md b/docs/src/tutorial/3-advanced.md
new file mode 100644
index 000000000..08a8e8f79
--- /dev/null
+++ b/docs/src/tutorial/3-advanced.md
@@ -0,0 +1,531 @@
+---
+description: Typst's tutorial.
+---
+
+# Advanced Styling
+In the previous two chapters of this tutorial, you have learned how to write a
+document in Typst and how to change its formatting. The report you wrote
+throughout the last two chapters got a straight A and your supervisor wants to
+base a conference paper on it! The report will of course have to comply with the
+conference's style guide. Let's see how we can achieve that.
+
+Before we start, let's create a team, invite your supervisor and add them to the
+team. You can do this by going back to the app dashboard with the four-circles
+icon in the top left corner of the editor. Then, choose the plus icon in the
+left toolbar and create a team. Finally, click on the new team and go to its
+settings by clicking 'manage team' next to the team name. Now you can invite
+your supervisor by email.
+
+
+
+Next, move your project into the team: Open it, going to its settings by
+choosing the gear icon in the left toolbar and selecting your new team from the
+owners dropdown. Don't forget to save your changes!
+
+Now, your supervisor can also edit the project and you can both see the changes
+in real time. You can join our [Discord server](https://discord.gg/2uDybryKPe)
+to find others with preview access and try teams with them!
+
+## The conference guidelines
+The layout guidelines are available on the conference website. Let's take a look
+at them:
+
+- The font should be an 11pt serif font
+- The title should be in 17pt and bold
+- The paper contains a single-column abstract and two-column main text
+- The abstract should be centered
+- The main text should be justified
+- First level section headings should be 13pt, centered, and rendered in small
+ capitals
+- Second level headings are run-ins, italicized and have the same size as the
+ body text
+- Finally, the pages should be US letter sized, numbered in the center of the
+ footer and the top left corner of each page should contain the title of the
+ paper
+
+We already know how to do many of these things, but for some of them, we'll need
+to learn some new tricks.
+
+## Writing the right set rules
+Let's start by writing some set rules for the document.
+
+```example
+#set text(11pt, "Linux Libertine")
+#set par(justify: true)
+
+#set page(
+>>> margin: auto,
+ paper: "us-letter",
+ header: align(right + horizon)[
+ A fluid dynamic model for
+ glacier flow
+ ],
+ footer: nr => align(
+ center + horizon,
+ [#nr],
+ ),
+)
+
+#lorem(600)
+```
+
+You are already familiar with most of what is going on here. We set the text
+size to `{11pt}` and the font to Linux Libertine. We also enable paragraph
+justification and set the page size to US letter.
+
+The `header` and `footer` arguments are new: With these, we can provide content
+to fill the top and bottom margins of every page. In the header, we specify our
+paper's title as requested by the conference style guide. We use the `align`
+function to align the text to the right and the `horizon` keyword to make sure
+that it is vertically centered in the margin.
+
+Because we need a page number in the footer, we have to put different content
+onto each page. To do that, we can pass a
+[custom function]($type/function) to the footer argument that defines
+how the footer should look for a given page number. Typst provides the page
+number to this function. Once more, we use the `align` function to center the
+page number horizontally and vertically.
+
+We have to put the page variable into square brackets and prefix it with a
+hashtag because the align function expects
+[content,]($type/content) but the page number is an
+[integer]($type/integer).
+
+## Creating a title and abstract
+Now, let's add a title and an abstract. We'll start with the title. We center
+align it and increase its font weight by enclosing it in `[*stars*]`.
+
+```example
+>>> #set page(width: 300pt, margin: 30pt)
+>>> #set text(11pt, "Linux Libertine")
+#align(center, text(17pt)[
+ *A fluid dynamic model
+ for glacier flow*
+])
+```
+
+This looks right. We used the `text` function to override the previous text
+set rule locally, increasing the size to 17pt for the function's argument. Let's
+also add the author list: Since we are writing this paper together with our
+supervisor, we'll add our own and their name.
+
+```example
+>>> #set page(width: 300pt, margin: 30pt)
+>>> #set text(11pt, "Linux Libertine")
+>>>
+>>> #align(center, text(17pt)[
+>>> *A fluid dynamic model
+>>> for glacier flow*
+>>> ])
+#grid(
+ columns: (1fr, 1fr),
+ align(center)[
+ Therese Tungsten \
+ Artos Institute \
+ #link("mailto:tung@artos.edu")
+ ],
+ align(center)[
+ Dr. John Doe \
+ Artos Institute \
+ #link("mailto:doe@artos.edu")
+ ]
+)
+```
+
+The two author blocks are laid out next to each other. We use the
+[`grid`]($func/grid) function to create this layout. With a grid, we can control
+exactly how large each column is and which content goes into which cell. The
+`columns` argument takes an array of [relative lengths]($type/relative-length)
+or [fractions]($type/fraction). In this case, we passed it two equal fractional
+sizes, telling it to split the available space into two equal columns. We then
+passed two content arguments to the grid function. The first with our own
+details, and the second with our supervisors'. We again use the `align` function
+to center the content within the column. The grid takes an arbitrary number of
+content arguments specifying the cells. Rows are added automatically, but they
+can also be manually sized with the `rows` argument.
+
+Now, let's add the abstract. Remember that the conference wants the abstract to
+be set ragged and centered.
+
+```example:0,0,612,317.5
+>>> #set text(11pt, "Linux Libertine")
+>>> #set par(justify: true)
+>>> #set page(
+>>> "us-letter",
+>>> margin: auto,
+>>> header: align(right + horizon)[
+>>> A fluid dynamic model for
+>>> glacier flow
+>>> ],
+>>> footer: page => align(
+>>> center+horizon,
+>>> [#page]
+>>> ),
+>>> )
+>>>
+>>> #align(center, text(17pt)[
+>>> *A fluid dynamic model
+>>> for glacier flow*
+>>> ])
+>>>
+>>> #grid(
+>>> columns: (1fr, 1fr),
+>>> align(center)[
+>>> Therese Tungsten \
+>>> Artos Institute \
+>>> #link("mailto:tung@artos.edu")
+>>> ],
+>>> align(center)[
+>>> Dr. John Doe \
+>>> Artos Institute \
+>>> #link("mailto:doe@artos.edu")
+>>> ]
+>>> )
+>>>
+<<< ...
+
+#align(center)[
+ #set par(justify: false)
+ *Abstract* \
+ #lorem(80)
+]
+>>> #lorem(600)
+```
+
+Well done! One notable thing is that we used a set rule within the content
+argument of `align` to turn off justification for the abstract. This does not
+affect the remainder of the document even though it was specified after the
+first set rule because content blocks _scope_ styling. Anything set within a
+content block will only affect the content within that block.
+
+Another tweak could be to save the paper title in a variable, so that we do not
+have to type it twice, for header and title. We can do that with the `{let}`
+keyword:
+
+```example
+#let title = [
+ A fluid dynamic model
+ for glacier flow
+]
+
+<<< ...
+
+>>> #set text(11pt, "Linux Libertine")
+>>> #set par(justify: true)
+#set page(
+>>> "us-letter",
+>>> margin: auto,
+ header: align(
+ right + horizon,
+ title
+ ),
+<<< ...
+>>> footer: page => align(
+>>> center+horizon,
+>>> [#page]
+>>> ),
+)
+
+#align(center, text(17pt)[
+ *#title*
+])
+
+<<< ...
+
+>>> #grid(
+>>> columns: (1fr, 1fr),
+>>> align(center)[
+>>> Therese Tungsten \
+>>> Artos Institute \
+>>> #link("mailto:tung@artos.edu")
+>>> ],
+>>> align(center)[
+>>> Dr. John Doe \
+>>> Artos Institute \
+>>> #link("mailto:doe@artos.edu")
+>>> ]
+>>> )
+>>>
+>>> #align(center)[
+>>> #set par(justify: false)
+>>> *Abstract* \
+>>> #lorem(80)
+>>> ]
+>>>
+>>> #lorem(600)
+```
+
+After we bound the content to the `title` variable, we can use it in functions
+and also within markup (prefixed by `#`, like functions). This way, if we decide
+on another title, we can easily change it in one place.
+
+## Adding columns and headings
+The paper above unfortunately looks like a wall of lead. To fix that, let's add
+some headings and switch our paper to a two-column layout. The
+[`columns`]($func/columns) function takes a number and content, and layouts the
+content into the specified number of columns. Since we want everything after the
+abstract to be in two columns, we need to apply the column function to our whole
+document.
+
+Instead of wrapping the whole document in a giant function call, we can use an
+"everything" show rule. This show rule does not feature a colon. Instead, we
+directly provide a function that is given the rest of the document as a
+parameter. We have called the parameter `rest` here, but you are free to choose
+any name. The rule can then do anything with this content. In our case, it
+passes it on to the `columns` function.
+
+```example
+>>> #let title = [
+>>> A fluid dynamic model
+>>> for glacier flow
+>>> ]
+>>>
+>>> #set text(11pt, "Linux Libertine")
+>>> #set par(justify: true)
+>>> #set page(
+>>> "us-letter",
+>>> margin: auto,
+>>> header: align(
+>>> right + horizon,
+>>> title
+>>> ),
+>>> footer: page => align(
+>>> center+horizon,
+>>> [#page]
+>>> ),
+>>> )
+>>>
+>>> #align(center, text(
+>>> 17pt,
+>>> weight: "bold",
+>>> title,
+>>> ))
+>>>
+>>> #grid(
+>>> columns: (1fr, 1fr),
+>>> align(center)[
+>>> Therese Tungsten \
+>>> Artos Institute \
+>>> #link("mailto:tung@artos.edu")
+>>> ],
+>>> align(center)[
+>>> Dr. John Doe \
+>>> Artos Institute \
+>>> #link("mailto:doe@artos.edu")
+>>> ]
+>>> )
+>>>
+>>> #align(center)[
+>>> #set par(justify: false)
+>>> *Abstract* \
+>>> #lorem(80)
+>>> ]
+>>> #v(4mm)
+<<< ...
+
+#show rest => columns(2, rest)
+
+= Introduction
+#lorem(300)
+
+= Related Work
+#lorem(200)
+```
+
+Now there is only one thing left to do: Style our headings. We need to make them
+centered and use small capitals. Because the `heading` function does not offer
+a way to set any of that, we need to write our own heading show rule.
+
+```example:50,250,265,270
+>>> #let title = [
+>>> A fluid dynamic model
+>>> for glacier flow
+>>> ]
+>>>
+>>> #set text(11pt, "Linux Libertine")
+>>> #set par(justify: true)
+>>> #set page(
+>>> "us-letter",
+>>> margin: auto,
+>>> header: align(
+>>> right + horizon,
+>>> title
+>>> ),
+>>> footer: page => align(
+>>> center + horizon,
+>>> [#page]
+>>> ),
+>>> )
+#show heading: it => block[
+ #set align(center)
+ #set text(12pt, weight: "regular")
+ #smallcaps(it.title)
+]
+
+<<< ...
+>>>
+>>> #align(center, text(
+>>> 17pt,
+>>> weight: "bold",
+>>> title,
+>>> ))
+>>>
+>>> #grid(
+>>> columns: (1fr, 1fr),
+>>> align(center)[
+>>> Therese Tungsten \
+>>> Artos Institute \
+>>> #link("mailto:tung@artos.edu")
+>>> ],
+>>> align(center)[
+>>> Dr. John Doe \
+>>> Artos Institute \
+>>> #link("mailto:doe@artos.edu")
+>>> ]
+>>> )
+>>>
+>>> #align(center)[
+>>> #set par(justify: false)
+>>> *Abstract* \
+>>> #lorem(80)
+>>> ]
+>>>
+>>> #v(4mm)
+>>> #show rest => columns(2, rest)
+>>>
+>>> = Introduction
+>>> #lorem(35)
+>>>
+>>> == Motivation
+>>> #lorem(45)
+```
+
+This looks great! We used a show rule that applies to all headings. We give it a
+function that gets passed the heading as a parameter. That parameter can be used
+as content but it also has some fields like `title`, `numbers`, and `level` from
+which we can compose a custom look. Here, we are center-aligning, setting the
+font weight to `{"regular"}` because headings are bold by default, and use the
+[`smallcaps`]($func/smallcaps) function to render the heading's title in small capitals.
+
+The only remaining problem is that all headings look the same now. The
+"Motivation" and "Problem Statement" subsections ought to be italic run in
+headers, but right now, they look indistinguishable from the section headings. We
+can fix that by using a `where` selector on our set rule: This is a
+[method]($scripting/#methods) we can call on headings (and other
+elements) that allows us to filter them by their level. We can use it to
+differentiate between section and subsection headings:
+
+```example:50,250,265,245
+>>> #let title = [
+>>> A fluid dynamic model
+>>> for glacier flow
+>>> ]
+>>>
+>>> #set text(11pt, "Linux Libertine")
+>>> #set par(justify: true)
+>>> #set page(
+>>> "us-letter",
+>>> margin: auto,
+>>> header: align(
+>>> right + horizon,
+>>> title
+>>> ),
+>>> footer: page => align(
+>>> center + horizon,
+>>> [#page]
+>>> ),
+>>> )
+>>>
+#show heading.where(
+ level: 1
+): it => block[
+ #set align(center)
+ #set text(12pt, weight: "regular")
+ #smallcaps(it.title)
+]
+
+#show heading.where(
+ level: 2
+): it => text(
+ size: 11pt,
+ weight: "regular",
+ style: "italic",
+ it.title + [.],
+)
+>>>
+>>> #align(center, text(
+>>> 17pt,
+>>> weight: "bold",
+>>> title,
+>>> ))
+>>>
+>>> #grid(
+>>> columns: (1fr, 1fr),
+>>> align(center)[
+>>> Therese Tungsten \
+>>> Artos Institute \
+>>> #link("mailto:tung@artos.edu")
+>>> ],
+>>> align(center)[
+>>> Dr. John Doe \
+>>> Artos Institute \
+>>> #link("mailto:doe@artos.edu")
+>>> ]
+>>> )
+>>>
+>>> #align(center)[
+>>> #set par(justify: false)
+>>> *Abstract* \
+>>> #lorem(80)
+>>> ]
+>>>
+>>> #v(4mm)
+>>> #show rest => columns(2, rest)
+>>>
+>>> = Introduction
+>>> #lorem(35)
+>>>
+>>> == Motivation
+>>> #lorem(45)
+```
+
+This looks great! We wrote two show rules that each selectively apply to the
+first and second level headings. We used a `where` selector to filter the
+headings by their level. We then rendered the subsection headings as run-ins. We
+also automatically add a period to the end of the subsection headings.
+
+Let's review the conference's style guide:
+- The font should be an 11pt serif font ✓
+- The title should be in 17pt and bold ✓
+- The paper contains a single-column abstract and two-column main text ✓
+- The abstract should be centered ✓
+- The main text should be justified ✓
+- First level section headings should be centered, rendered in small caps and in 13pt ✓
+- Second level headings are run-ins, italicized and have the same size as the
+ body text ✓
+- Finally, the pages should be US letter sized, numbered in the center and the
+ top left corner of each page should contain the title of the paper ✓
+
+We are now in compliance with all of these styles and can submit the paper to
+the conference! The finished paper looks like this:
+
+
+
+## Review
+You have now learned how to create headers and footers, how to use functions and
+scopes to locally override styles, how to create more complex layouts with the [`grid`]($func/grid) function and how to write show rules for individual functions, and the whole document. You also learned how to use the
+[`where` selector]($styling/#show-rules) to filter the headings by their level.
+
+The paper was a great success! You've met a lot of like-minded researchers at
+the conference and are planning a project which you hope to publish at the same
+venue next year. You'll need to write a new paper using the same style guide
+though, so maybe now you want to create a time-saving template for you and your
+team?
+
+In the next section, we will learn how to create templates that can be reused in
+multiple documents. This is a more advanced topic, so feel free to come back
+to it later if you don't feel up to it right now.
diff --git a/docs/src/tutorial/4-template.md b/docs/src/tutorial/4-template.md
new file mode 100644
index 000000000..db012db75
--- /dev/null
+++ b/docs/src/tutorial/4-template.md
@@ -0,0 +1,378 @@
+---
+description: Typst's tutorial.
+---
+
+# Making a Template
+In the previous three chapters of this tutorial, you have learned how to write a
+document in Typst, apply basic styles, and customize its appearance in-depth to
+comply with a publisher's style guide. Because the paper you wrote in the
+previous chapter was a tremendous success, you have been asked to write a
+follow-up article for the same conference. This time, you want to take the style
+you created in the previous chapter and turn it into a reusable template. In
+this chapter you will learn how to create a template that you and your team can
+use with just one show rule. Let's get started!
+
+## A toy template
+In Typst, templates are functions in which you can wrap your whole document. To
+learn how to do that, let's first review how to write your very own functions.
+They can do anything you want them to, so why not go a bit crazy?
+
+```example
+#let amazed(term) = box[✨ #term ✨]
+
+You are #amazed[beautiful]!
+```
+
+This function takes a single argument, `term`, and returns a content block with
+the `term` surrounded by sparkles. We also put the whole thing in a box so that
+the term we are amazed by cannot be separated from its sparkles by a line break.
+
+Many functions that come with Typst have optional named parameters. Our
+functions can also have them. Let's add a parameter to our function that lets us
+choose the color of the text. We need to provide a default color in case the
+parameter isn't given.
+
+```example
+#let amazed(term, color: blue) = {
+ text(color, box[✨ #term ✨])
+}
+
+You are #amazed[beautiful]!
+I am #amazed(color: purple)[amazed]!
+```
+
+Templates now work by using an "everything" show rule that applies the custom
+function to our whole document. Let's do that with our `amazed` function.
+
+```example
+>>> #let amazed(term, color: blue) = {
+>>> text(color, box[✨ #term ✨])
+>>> }
+#show amazed
+I choose to focus on the good
+in my life and let go of any
+negative thoughts or beliefs.
+In fact, I am amazing!
+```
+
+Our whole document will now be passed to the `amazed` function, as if we
+wrapped it around it. This is not especially useful with this particular
+function, but when combined with set rules and named arguments, it can be very
+powerful.
+
+## Embedding set and show rules
+To apply some set and show rules to our template, we can use `set` and `show`
+within a content block in our function and then insert the document into
+that content block.
+
+```example
+#let template(doc) = [
+ #set text("Inria Serif")
+ #show "something cool": [Typst]
+ #doc
+]
+
+#show template
+I am learning something cool today.
+It's going great so far!
+```
+
+Just like we already discovered in the previous chapter, set rules will apply to
+everything within their content block. Since the everything show rule passes our
+whole document to the `template` function, the text set rule and string show
+rule in our template will apply to the whole document. Let's use this knowledge
+to create a template that reproduces the body style of the paper we wrote in the
+previous chapter.
+
+```example
+#let conf(title, doc) = {
+ set text(11pt, "Linux Libertine")
+ set par(justify: true)
+ set page(
+ paper: "us-letter",
+>>> margin: auto,
+ header: align(
+ right + horizon,
+ title
+ ),
+<<< ...
+ )
+
+ // Heading show rules.
+<<< ...
+>>> show heading.where(
+>>> level: 1
+>>> ): it => block(
+>>> align(center,
+>>> text(
+>>> 13pt,
+>>> weight: "regular",
+>>> smallcaps(it.title),
+>>> )
+>>> ),
+>>> )
+>>> show heading.where(
+>>> level: 2
+>>> ): it => box(
+>>> text(
+>>> 11pt,
+>>> weight: "regular",
+>>> style: "italic",
+>>> it.title + [.],
+>>> )
+>>> )
+
+ columns(2, doc)
+}
+
+#show doc => conf([Paper title], doc)
+
+= Introduction
+#lorem(90)
+
+<<< ...
+>>> == Motivation
+>>> #lorem(140)
+>>>
+>>> == Problem Statement
+>>> #lorem(50)
+>>>
+>>> = Related Work
+>>> #lorem(200)
+```
+
+We copy-pasted most of that code from the previous chapter. The only two
+differences are that we wrapped everything in the function `conf` and are
+calling the columns function directly on the `doc` argument as it already
+contains the content of the document. Moreover, we used a curly-braced code
+block instead of a content block. This way, we don't need to prefix all set
+rules and function calls with a `#`. In exchange, we cannot write markup
+directly into it anymore.
+
+Also note where the title comes from: We previously had it inside of a variable.
+Now, we are receiving it as the first parameter of the template function.
+Thus, we must specify it in the show rule where we call the template.
+
+## Templates with named arguments
+Our paper in the previous chapter had a title and an author list. Lets add these
+things to our template. In addition to the title, we want our template to accept
+a list of authors with their affiliations and the paper's abstract. To keep
+things readable, we'll add those as named arguments. In the end, we want it to
+work like this:
+
+```typ
+#show doc => conf(
+ title: [Towards Improved Modelling],
+ authors: (
+ (
+ name: "Theresa Tungsten",
+ affiliation: "Artos Institute",
+ email: "tung@artos.edu",
+ ),
+ (
+ name: "Eugene Deklan",
+ affiliation: "Honduras State",
+ email: "e.deklan@hstate.hn",
+ ),
+ ),
+ abstract: lorem(80),
+ doc,
+)
+
+...
+```
+
+Let's build this new template function. First, we add a default value to the
+`title` argument. This way, we can call the template without specifying a title.
+We also add the named `authors` and `abstract` parameters with empty defaults.
+Next, we copy the code that generates title, abstract and authors from the
+previous chapter into the template, replacing the fixed details with the
+parameters.
+
+The new `authors` parameter expects an [array]($type/array) of
+[dictionaries]($type/dictionary) with the keys `name`, `affiliation` and
+`email`. Because we can have an arbitrary number of authors, we dynamically
+determine if we need one, two or three columns for the author list. First, we
+determine the number of authors using the [`.len()`]($type/array.len) method on
+the `authors` array. Then, we set the number of columns as the minimum of this
+count and three, so that we never create more than three columns. If there are
+more than three authors, a new row will be inserted instead. For this purpose,
+we have also added a `row-gutter` parameter to the `grid` function. Otherwise,
+the rows would be too close together. To extract the details about the authors
+from the dictionary, we use the [field access syntax]($scripting/#fields).
+
+We still have to provide an argument to the grid for each author: Here is where
+the array's [`map` method]($type/array.map) comes in handy. It takes a function
+as an argument that gets called with each element of the array. We pass it a
+function that formats the details for each author and returns a new array
+containing content values. We've now got one array of values that we'd like to
+use as multiple arguments for the grid. We can do that by using the
+[`spread` operator]($type/arguments). It takes an array and applies each of its elements as a separate argument to the function.
+
+The resulting template function looks like this:
+
+```typ
+#let conf(
+ title: none,
+ authors: (),
+ abstract: [],
+ doc,
+) = {
+ // Set and show rules from before.
+<<< ...
+
+ set align(center)
+ text(17pt, title)
+
+ let count = authors.len()
+ let ncols = calc.min(count, 3)
+ grid(
+ columns: (1fr,) * ncols,
+ row-gutter: 24pt,
+ ..authors.map(author => [
+ {author.name} \
+ {author.affiliation} \
+ #link("mailto:" + author.email)
+ ]),
+ )
+
+ par(justify: false)[
+ *Abstract* \
+ #abstract
+ ]
+
+ set align(left)
+ columns(2, doc)
+}
+```
+
+## A separate file
+Most of the time, a template is specified in a different file and then imported
+into the document. This way, the main file you write in is kept clutter free and
+your template is easily reused. Create a new text file in the file panel by
+clicking the plus button and name it `conf.typ`. Move the `conf` function
+definition inside of that new file. Now you can access it from your main file by
+adding an import before the show rule. Name the function that you want to import
+from another file between the `{import}` and `{from}` keywords and specify the
+path of the file after the `{from}` keyword.
+
+```example
+>>> #let conf(
+>>> title: none,
+>>> authors: (),
+>>> abstract: [],
+>>> doc,
+>>> ) = {
+>>> set text(11pt, "Linux Libertine")
+>>> set par(justify: true)
+>>> set page(
+>>> "us-letter",
+>>> margin: auto,
+>>> header: align(
+>>> right + horizon,
+>>> title
+>>> ),
+>>> footer: page => align(
+>>> center + horizon,
+>>> [#page]
+>>> ),
+>>> )
+>>>
+>>> show heading.where(
+>>> level: 1
+>>> ): it => block(
+>>> align(center,
+>>> text(
+>>> 13pt,
+>>> weight: "regular",
+>>> smallcaps(it.title),
+>>> )
+>>> ),
+>>> )
+>>> show heading.where(
+>>> level: 2
+>>> ): it => box(
+>>> text(
+>>> 11pt,
+>>> weight: "regular",
+>>> style: "italic",
+>>> it.title + [.],
+>>> )
+>>> )
+>>>
+>>> set align(center)
+>>> text(17pt, title)
+>>>
+>>> let count = calc.min(authors.len(), 3)
+>>> grid(
+>>> columns: (1fr,) * count,
+>>> row-gutter: 24pt,
+>>> ..authors.map(author => [
+>>> {author.name} \
+>>> {author.affiliation} \
+>>> #link("mailto:" + author.email)
+>>> ]),
+>>> )
+>>>
+>>> par(justify: false)[
+>>> *Abstract* \
+>>> #abstract
+>>> ]
+>>>
+>>> set align(left)
+>>> columns(2, doc)
+>>>}
+<<< #import conf from "conf.typ"
+
+#show doc => conf(
+ title: [
+ Towards Improved Modelling
+ ],
+ authors: (
+ (
+ name: "Theresa Tungsten",
+ affiliation: "Artos Institute",
+ email: "tung@artos.edu",
+ ),
+ (
+ name: "Eugene Deklan",
+ affiliation: "Honduras State",
+ email: "e.deklan@hstate.hn",
+ ),
+ ),
+ abstract: lorem(80),
+ doc,
+)
+
+= Introduction
+#lorem(90)
+
+== Motivation
+#lorem(140)
+
+== Problem Statement
+#lorem(50)
+
+= Related Work
+#lorem(200)
+```
+
+We have now converted the conference paper into a reusable template for that
+conference! Why not share it on
+[Typst's Discord server](https://discord.gg/2uDybryKPe) so that others can use
+it too?
+
+## Review
+Congratulations, you have completed Typst's Tutorial! In this section, you have
+learned how to define your own functions and how to create and apply templates
+that define reusable document styles. You've made it far and learned a lot. You
+can now use Typst to write your own documents and share them with others.
+
+We are still a super young project and are looking for feedback. If you have any
+questions, suggestions or found a bug, please let us know on
+[Typst's Discord server](https://discord.gg/2uDybryKPe), on our
+[contact form](https://typst.app/contact), or on
+[social media.](https://twitter.com/typstapp)
+
+So what are you waiting for? Go forth and write something! Or, if you haven't
+got access yet, [sign up for the wait list](https://typst.app).
diff --git a/docs/src/tutorial/welcome.md b/docs/src/tutorial/welcome.md
new file mode 100644
index 000000000..b2733da10
--- /dev/null
+++ b/docs/src/tutorial/welcome.md
@@ -0,0 +1,54 @@
+---
+description: Typst's tutorial.
+---
+
+# Tutorial
+Welcome to Typst's tutorial! In this tutorial, you will learn how to write and
+format documents in Typst. We will start with everyday tasks and gradually
+introduce more advanced features. This tutorial does not assume prior knowledge
+of Typst, other Markup languages, or programming. We do assume that you know how
+to edit a text file.
+
+The best way to start is to open the Typst app and follow along with the
+steps below. Do not have preview access yet?
+[Sign up for the wait list](https://typst.app).
+
+## When to use Typst
+Before we get started, let's check what Typst is and when to use it. Typst is a
+markup language for typesetting documents. It is designed to be easy to learn,
+fast, and versatile. Typst takes text files with markup in them and outputs
+PDFs.
+
+Typst is a good choice for writing any long form text such as essays, articles,
+scientific papers, books, reports, and homework assignments. Moreover, Typst is
+a great fit for any documents containing mathematical notation, such as papers
+in the math, physics, and engineering fields. Finally, due to its strong styling
+and automation features, it is an excellent choice for any set of documents that
+share a common style, such as a book series.
+
+## How to get access to Typst
+Starting December 26th, 2022, we are conducting a closed infrastructure test
+into which we invite a small number of people from our wait list. A few months
+later we will start a public beta of our web app and open source Typst
+(currently planned for March of 2023).
+
+At that point, we will also give you the option to download a local command line
+compiler for Typst. This way, you can always compile your documents
+independently from our web app. Still, the web app offers many useful feature
+like live preview, autocompletion and collaboration for teams.
+
+## What you will learn
+This tutorial has four chapters. Each chapter builds on the previous one. Here is
+what you will learn in each of them:
+
+1. [Writing in Typst:]($tutorial/writing-in-typst) Learn how to write text and
+ insert images, equations, and other elements.
+2. [Formatting:]($tutorial/formatting) Learn how to adjust the formatting
+ of your document, including font size, heading styles, and more.
+3. [Advanced Styling:]($tutorial/advanced-styling) Create a complex page
+ layout for a scientific paper with typographic features such as an author
+ list and run-in headings.
+4. [Making a Template:]($tutorial/making-a-template) Build a reusable template
+ from the paper you created in the previous chapter.
+
+We hope you'll enjoy Typst!