diff --git a/content/docs/learn/playground/alignment.mdx b/content/docs/learn/playground/alignment.mdx
new file mode 100644
index 0000000..e3b7d3f
--- /dev/null
+++ b/content/docs/learn/playground/alignment.mdx
@@ -0,0 +1,87 @@
+---
+title: "Align cells and wrap text"
+description: "Horizontal (left/center/right) and vertical (top/middle/bottom) alignment plus text wrap are per-cell format attributes; the toolbar sets them (Ctrl+Shift+L/E/R for horizontal), an explicit alignment overrides the type-based default, and a wrapped cell grows its row to fit its lines."
+---
+
+{/* GENERATED — do not edit by hand */}
+
+## What it does
+
+Select a cell or range and use the toolbar's **alignment** controls — a horizontal
+group (**left / center / right**), a vertical group (**top / middle / bottom**), and a
+**wrap** toggle — to place text inside each cell like Google Sheets. Horizontal
+alignment also has keyboard shortcuts: **Ctrl+Shift+L** (left), **Ctrl+Shift+E**
+(center), **Ctrl+Shift+R** (right).
+
+**Horizontal** alignment defaults to the cell type — numbers, dates, and booleans hug
+the right, text hugs the left. Choosing an explicit alignment **overrides** that default
+(left-align a number and it moves left); re-choosing the alignment a cell already has
+clears it back to automatic. **Vertical** alignment defaults to **bottom** (Sheets'
+default) and is visible when the row is taller than one line.
+
+**Wrap** breaks a value too wide for its column onto multiple lines at word boundaries
+(a word longer than the column breaks mid-word) and **grows the row** so every line is
+visible. With wrap off, long text is clipped at the cell edge and the row keeps its
+default height.
+
+All three are **presentation** attributes, separate from the cell's value: aligning or
+wrapping `=A1+B1` repositions the number without touching the formula. They are per-cell,
+survive value edits, and undo/redo like any other edit.
+
+## Verified behavior
+
+_Matches Google Sheets:_ In Google Sheets, horizontal alignment defaults to type-based (numbers/dates/booleans hug the right, text the left); an explicit Left/Center/Right (toolbar or Ctrl+Shift+L/E/R) overrides that default and re-selecting the current alignment clears it back to automatic. Vertical alignment (Top/Middle/Bottom, toolbar only) defaults to BOTTOM and is visible when a row is taller than one line. Text wrap breaks a value too wide for its column onto multiple lines at word boundaries (a word longer than the column breaks mid-word) and GROWS the row so every line is visible; with wrap off the text overflows/clips and the row stays default height. All three are per-cell presentation attributes: they survive value/formula edits, never change the computed value, and undo/redo like any edit. (Text rotation, also offered by Sheets, is a deferred follow-up — not built in this slice.)
+
+#### Ctrl+Shift+E centers the selected cell
+
+**Given**
+- **A1** = `hello`
+
+**When** I click **A1**, then press `Control+Shift+e`
+
+**Then**
+- **A1** is align=center
+
+#### an explicit left-align overrides a number's automatic right-align
+
+**Given**
+- **A1** = `42`
+
+**When** I click **A1**, then press `Control+Shift+l`
+
+**Then**
+- **A1** shows `42`
+- **A1** is align=left
+
+#### alignment is presentation only — the value and formula are untouched
+
+**Given**
+- **A1** = `=2+3`
+
+**When** I click **A1**, then press `Control+Shift+r`
+
+**Then**
+- **A1** shows `5`
+- **A1** has formula `=2+3`
+- **A1** is align=right
+
+#### alignment survives a value edit
+
+**Given**
+- **A1** = `x`
+
+**When** I click **A1**, then press `Control+Shift+e`, then type `changed` into **A1**
+
+**Then**
+- **A1** shows `changed`
+- **A1** is align=center
+
+#### alignment undoes and redoes with the command log
+
+**Given**
+- **A1** = `x`
+
+**When** I click **A1**, then press `Control+Shift+r`, then undo, then redo
+
+**Then**
+- **A1** is align=right
diff --git a/content/docs/learn/playground/autofit-row-height.mdx b/content/docs/learn/playground/autofit-row-height.mdx
new file mode 100644
index 0000000..7379ac4
--- /dev/null
+++ b/content/docs/learn/playground/autofit-row-height.mdx
@@ -0,0 +1,48 @@
+---
+title: "Rows grow to fit the tallest cell font"
+description: "Enlarging a cell's font grows its row to fit; reducing it (or clearing the cell) shrinks the row back."
+---
+
+{/* GENERATED — do not edit by hand */}
+
+## What it does
+
+Rows size themselves to their content. Type into a cell and make its font bigger — with the **[−] size [+]** stepper or **Ctrl+Shift+Period** — and the **whole row grows** so the larger text fits instead of overflowing into its neighbours. Shrink the font (or clear the cell) and the row **shrinks back**. The row always fits its **tallest** cell, so a big font in column A grows the entire row across every column.
+
+Only cells that actually **hold content** drive the height: a cell formatted large but left empty leaves its row at the default height — matching Google Sheets.
+
+## Verified behavior
+
+_Matches Google Sheets:_ In Google Sheets every row auto-fits its content: a row's height is the height of its tallest cell, so increasing a cell's font size grows the whole row to fit the larger text and reducing it (or clearing the cell) shrinks the row back. Only NON-EMPTY cells count — a cell that carries a large font but no content does not grow its row. The tallest cell in the row wins (a big font in column A grows the entire row across every column). Row height is a layout consequence of the per-cell font-size presentation attribute; it never changes the cell's value or formula and undoes/redoes with the edit that caused it. Studio floors row height at its 24px default (Sheets' default is ~21px) so unrelated rows never reflow, and models the grown height with a line-box formula rather than pixel-matching Sheets' proprietary canvas metrics.
+
+#### Enlarging a cell's font records the size that grows its row
+
+**Given**
+- **A1** = `Big`
+
+**When** I click **A1**, then press `Control+Shift+Period`
+
+**Then**
+- **A1** is fontSize=11
+
+#### Reducing the font records the size that shrinks the row back
+
+**Given**
+- **A1** = `Big`
+
+**When** I click **A1**, then press `Control+Shift+Comma`
+
+**Then**
+- **A1** is fontSize=9
+
+#### Row auto-fit is presentation — it never touches the value
+
+**Given**
+- **A1** = `=6*7`
+
+**When** I click **A1**, then press `Control+Shift+Period`
+
+**Then**
+- **A1** shows `42`
+- **A1** has formula `=6*7`
+- **A1** is fontSize=11
diff --git a/content/docs/learn/playground/cell-editing-modes.mdx b/content/docs/learn/playground/cell-editing-modes.mdx
new file mode 100644
index 0000000..0efb733
--- /dev/null
+++ b/content/docs/learn/playground/cell-editing-modes.mdx
@@ -0,0 +1,99 @@
+---
+title: "Enter mode vs edit mode"
+description: "Typing seeds a fresh value and arrows commit + move; F2 / Enter / double-click open the cell for editing so arrows move the caret; in a formula an arrow only steers a reference when the caret sits at a reference-valid position — so editing a formula never corrupts it."
+---
+
+{/* GENERATED — do not edit by hand */}
+
+## What it does
+
+There are two ways to start editing a cell, and they treat the arrow keys
+differently — exactly like Google Sheets:
+
+- **Enter mode** — _type a character_. It replaces the cell's contents, and pressing
+ an **arrow commits the value and moves the selection** one cell in that direction
+ (the fast data-entry flow: `42` → Down → `7` → Down …).
+- **Edit mode** — press **F2**, press **Enter** on a selected cell, or
+ **double-click**. The existing content opens with a **caret**, and **arrows move
+ the caret** through the text (**Home** / **End** jump to the ends). Double-click
+ drops the caret near where you clicked.
+
+## Editing a formula (point mode)
+
+While editing a **formula**, an arrow key does one of two things depending on where
+the caret is:
+
+- **Steer a reference** ("point mode") — only when the caret is at the **end** of the
+ text, right after `=`, an operator (`+ - * / ^ & < > =`), `(` or `,`. This is how you
+ build a formula by pointing: type `=`, arrow to a cell; type `+`, arrow to the next.
+- **Move the caret** — everywhere else. Moving the caret through a completed formula
+ such as `=B2+B1+C1` **never** rewrites a reference into it.
+
+This caret gate is the fix for a data-corruption bug: previously _any_ arrow, at any
+caret position, rewrote a reference into a formula you were only trying to move the
+caret through (`=B2+B1` → `=B2+B1+D42+C1`).
+
+## Try it
+
+
+
+## Verified behavior
+
+_Matches Google Sheets:_ In Google Sheets there are two ways an in-cell edit begins, and they behave differently. TYPING a character starts "enter" mode: the character replaces the cell's contents and pressing an arrow commits the value and moves the selection one cell in that direction. F2, Enter on a selected cell, or a DOUBLE-CLICK start "edit" mode: the existing text opens with a caret and arrows move the caret through it (Home/End jump to the ends). While editing a FORMULA, an arrow steers a cell reference ("point mode") ONLY when the caret sits at the end of the text right after "=", an operator, "(" or "," — i.e. the formula is expecting a reference next. Anywhere else the arrow moves the caret, so moving through an existing formula such as =B2+B1+C1 never rewrites a reference into it. Every edit is undoable.
+
+#### Typing a value then pressing an arrow commits it (enter mode)
+
+**Given**
+
+**When** I click **A1**, then press `4`, then press `2`, then press `ArrowDown`
+
+**Then**
+- **A1** shows `42`
+
+#### Editing a formula with arrows moves the caret and never corrupts it
+
+**Given**
+- **A1** = `=B2+B1+C1`
+- **B1** = `10`
+- **B2** = `20`
+- **C1** = `3`
+
+**When** I click **A1**, then press `F2`, then press `ArrowLeft`, then press `ArrowLeft`, then press `ArrowRight`, then press `Enter`
+
+**Then**
+- **A1** shows `33`
+- **A1** has formula `=B2+B1+C1`
+
+#### Arrowing from the start through a formula inserts no references
+
+**Given**
+- **A1** = `=B2+B1`
+- **B1** = `10`
+- **B2** = `20`
+
+**When** I click **A1**, then press `F2`, then press `Home`, then press `ArrowRight`, then press `ArrowRight`, then press `ArrowRight`, then press `Enter`
+
+**Then**
+- **A1** shows `30`
+- **A1** has formula `=B2+B1`
+
+#### Point mode — typing = then arrowing inserts a reference
+
+**Given**
+- **B1** = `10`
+
+**When** I click **A1**, then press `=`, then press `ArrowRight`, then press `Enter`
+
+**Then**
+- **A1** shows `10`
+- **A1** has formula `=B1`
+
+#### An edit is undoable
+
+**Given**
+- **A1** = `5`
+
+**When** I click **A1**, then press `9`, then press `Enter`, then undo
+
+**Then**
+- **A1** shows `5`
diff --git a/content/docs/learn/playground/clear-paint-format.mdx b/content/docs/learn/playground/clear-paint-format.mdx
new file mode 100644
index 0000000..1ac1c26
--- /dev/null
+++ b/content/docs/learn/playground/clear-paint-format.mdx
@@ -0,0 +1,72 @@
+---
+title: "Clear formatting + Paint format"
+description: "Ctrl+\\ (or the toolbar \"Tx\" button) resets a range's format to default without touching its value; the toolbar Paint format roller copies one range's full format onto another (single click = one-shot, double click = sticky until Escape or re-clicking the roller).\n"
+---
+
+{/* GENERATED — do not edit by hand */}
+
+## What it does
+
+**Clear formatting** — select a range and press **Ctrl+\\** (or click the **Tx** button in the toolbar) to reset every presentation attribute back to default: no bold/italic/strike, no font/color/fill overrides, no borders, no explicit number format, no alignment/wrap override. The cell's **value or formula is never touched** — clearing the format on `=A1+B1` resets its styling without changing what it computes to. It undoes and redoes like any other edit, and applying it to a range is **one** undoable command.
+
+**Paint format** — the roller icon in the toolbar copies a range's **full** format onto another range, exactly like Google Sheets' format painter:
+
+- **Single click** arms a **one-shot** paint: your very next click (or shift-click to extend to a range) applies the copied format and the tool immediately disarms.
+- **Double click** arms a **sticky** paint: it stays armed across repeated applies — keep clicking new targets — until you press **Escape** or click the roller again.
+
+Applying **replaces** the target's format wholesale, the same way Sheets does: if the target had its own border or fill that the source didn't carry, painting removes it too — this isn't a merge of the two formats. Painting onto a range bigger than the captured source tiles the source pattern across it (Sheets' own repeat-the-pattern behavior). A multi-cell apply is **one** undoable command regardless of size.
+
+## Verification
+
+The scenario runner can drive Ctrl+\\ end-to-end (a real keyboard chord), so clear-formatting's scenarios above are fully automated. Paint format's arm/apply gesture is a toolbar click followed by a click on a DIFFERENT target cell — the runner has no verb for driving a toolbar action button yet (the same gap `format-fill-color.mdx` documents for its color swatch), so it's browser-verified instead: arming via a single click on the roller shows the pressed toolbar state and the source range gets a dashed "copying" outline; clicking a target cell applies the source's format, replaces the target's own prior formatting, and the roller un-presses itself (one-shot); double-clicking the roller instead keeps it pressed across repeated target clicks until Escape.
+
+## Verified behavior
+
+_Matches Google Sheets:_ In Google Sheets, Format → Clear formatting (Ctrl+\ / Cmd+\ on Mac) resets every presentation attribute on the selection — bold/italic/strikethrough, fonts, colors, borders, number format, alignment, wrap — back to the default, without touching the cell's value or formula. It undoes/redoes like any edit. The Paint format toolbar button (the roller icon) captures the CURRENT selection's format pattern: a single click arms a ONE-SHOT paint that applies to the next click/drag target and immediately disarms; a double click arms a STICKY paint that stays armed — repeatedly applying to each subsequent click/drag target — until Escape is pressed or the roller is clicked again. Applying replaces the target's format wholesale (not a merge): a target's own fill/border/etc. that the source didn't carry is cleared too. A multi-cell target range is one undoable command regardless of size.
+
+#### Ctrl+\ resets a bold+bordered cell's format, value unchanged
+
+**Given**
+- **A1** = `hello`
+
+**When** I click **A1**, then press `Control+b`, then press `Control+Shift+7`, then press `Control+Backslash`
+
+**Then**
+- **A1** shows `hello`
+- **A1** is bold=false, borders=false
+
+#### Ctrl+\ on a formula cell clears format only — value and formula survive
+
+**Given**
+- **A1** = `5`
+- **A2** = `=A1*2`
+
+**When** I click **A2**, then press `Control+b`, then press `Control+Backslash`
+
+**Then**
+- **A2** shows `10`
+- **A2** has formula `=A1*2`
+- **A2** is bold=false
+
+#### Ctrl+\ a second time is a no-op (already-default format stays default)
+
+**Given**
+- **A1** = `hello`
+
+**When** I click **A1**, then press `Control+Backslash`, then press `Control+Backslash`
+
+**Then**
+- **A1** shows `hello`
+- **A1** is bold=false
+
+#### Selecting a formula cell leaves its value and formula unchanged (presentation-independence)
+
+**Given**
+- **A1** = `5`
+- **A2** = `=A1*2`
+
+**When** I click **A2**
+
+**Then**
+- **A2** shows `10`
+- **A2** has formula `=A1*2`
diff --git a/content/docs/learn/playground/clipboard.mdx b/content/docs/learn/playground/clipboard.mdx
index e691872..5f5aa34 100644
--- a/content/docs/learn/playground/clipboard.mdx
+++ b/content/docs/learn/playground/clipboard.mdx
@@ -3,6 +3,8 @@ title: "Copy, cut, and paste"
description: "Copy adjusts references to the paste target; cut moves a cell verbatim."
---
+{/* GENERATED — do not edit by hand */}
+
## What it does
Select a cell and press **Ctrl+C** to copy or **Ctrl+X** to cut, then select a target and press **Ctrl+V** to paste.
@@ -10,17 +12,9 @@ Select a cell and press **Ctrl+C** to copy or **Ctrl+X** to cut, then select a t
- **Copy + paste** adjusts the formula's relative references by the paste offset — `=A1+B1` copied from **C1** to **C3** becomes `=A3+B3`, while `$`-anchored parts stay put (`=$A$1+B1` → `=$A$1+B3`). The source is left untouched and the clipboard stays loaded, so you can paste it again.
- **Cut + paste** _moves_ the cell: the formula is written to the destination **verbatim** (no reference adjustment) and the source cell is cleared. The clipboard is consumed after that one paste.
-## Try it
-
-
-
-## How it works
-
-TrueCalc owns the calc. Copy and cut record the source cell's raw input; paste branches on which one it was. A copy adjusts the input (relative references shift, `$`-anchored references stay fixed) and writes the adjusted text to the target. A cut skips adjustment entirely — it writes the raw text unchanged and clears the source as a single undoable move — because a move should preserve exactly what the formula pointed at. Every result recomputes through the single engine, so the grid matches Google Sheets.
-
## Verified behavior
-_Matches Google Sheets:_ In Google Sheets, copying a formula and pasting it adjusts the formula's relative references by the row/column offset (copying "=A1+B1" from C1 to C3 yields "=A3+B3"), while $-anchored parts stay fixed ("=$A$1+B1" → "=$A$1+B3"); copy leaves the source and the clipboard intact, so it can be pasted again. Cutting and pasting instead MOVES the cell: the formula is written to the destination verbatim (no reference adjustment) and the source is cleared, and the clipboard is consumed after that one paste.
+_Matches Google Sheets:_ In Google Sheets, copying a formula and pasting it adjusts the formula's relative references by the row/column offset (copying "=A1+B1" from C1 to C3 yields "=A3+B3"), while $-anchored parts stay fixed ("=$A$1+B1" → "=$A$1+B3"); copy leaves the source and the clipboard intact, so it can be pasted again. Cutting and pasting instead MOVES the cell: the formula is written to the destination verbatim (no reference adjustment) and the source is cleared, and the clipboard is consumed after that one paste. (Sheets additionally rewrites OTHER formulas that referenced the moved cell so they keep pointing at it — that cross-reference remap needs engine support and is not yet modeled here; tracked as a follow-up.)
#### Copy a relative formula and paste — references shift by the offset
diff --git a/content/docs/learn/playground/command-history.mdx b/content/docs/learn/playground/command-history.mdx
new file mode 100644
index 0000000..dc0cc09
--- /dev/null
+++ b/content/docs/learn/playground/command-history.mdx
@@ -0,0 +1,35 @@
+---
+title: "Command history panel"
+description: "A togglable side panel lists every committed operation with a human label, the cells it touched, and a timestamp; clicking an entry reverts the document to that state."
+---
+
+{/* GENERATED — do not edit by hand */}
+
+## What it does
+
+A togglable **History** panel (the clock-arrow icon at the right of the toolbar) lists every committed operation — typed edits, fills, pastes, clears, and every formatting toggle — as a labeled row: a human-readable story ("Bold on B2", "Filled A2:A5"), the affected cell(s) in monospace, and a timestamp. Clicking a row **reverts the document to that state** and highlights what changed. The log isn't persisted across reloads yet.
+
+## Verified behavior
+
+_Matches Google Sheets:_ Google Sheets has a Version History feature (File > Version history), but it operates on named SNAPSHOTS of the whole spreadsheet at a point in time — it does not list individual operations (each keystroke, each formatting toggle) as discrete, clickable, labeled entries the way this panel does. There is no Sheets equivalent to reproduce: this is a TrueCalc-original feature, built from the Store's own command log rather than replicated oracle behavior.
+
+#### An edit and a bold each add one entry to the command log
+
+**Given**
+- **A1** = `10`
+
+**When** I type `20` into **A2**, then click **A2**, then press `Control+b`
+
+**Then**
+- **A2** shows `20`
+- **A2** is bold
+
+#### Undoing a command removes it from the visible (committed) log
+
+**Given**
+- **A1** = `10`
+
+**When** I type `20` into **A2**, then undo
+
+**Then**
+- **A2** shows `null`
diff --git a/content/docs/learn/playground/data-import.mdx b/content/docs/learn/playground/data-import.mdx
new file mode 100644
index 0000000..9dbe0f0
--- /dev/null
+++ b/content/docs/learn/playground/data-import.mdx
@@ -0,0 +1,71 @@
+---
+title: "Import data (CSV, TSV, custom separator, XLSX)"
+description: "Bring an external file into the grid — delimited text or a spreadsheet — with values, dates, and formulas coming in through the same coercion as typing, as one undoable step."
+---
+
+{/* GENERATED — do not edit by hand */}
+
+## What it does
+
+**Save, load, export → Import data…** brings an external file into Studio. Pick a file and the options dialog opens:
+
+- **Delimited text** — CSV, TSV, or a **custom single-character separator**. CSV-vs-TSV is auto-detected as the default; a custom field lets you type any separator (`;`, `|`, …).
+- **XLSX** — a real spreadsheet workbook. The first sheet imports by default; a multi-tab workbook lets you pick one sheet or bring in **all sheets** (one Studio sheet each).
+
+You choose the **destination**: a **new sheet** (named from the file — the default), or the **current sheet starting at your selection**.
+
+Everything happens **on your device** — the file is read with the browser's FileReader (delimited) or as an in-memory ArrayBuffer (XLSX) and never uploaded, so import works offline and in try mode.
+
+## Try it
+
+
+
+## Verified behavior
+
+_Matches Google Sheets:_ In Google Sheets, File → Import lets you bring in a CSV/TSV/custom-separator text file or an.xlsx workbook. Imported cells are interpreted exactly as if typed: a bare number becomes a number, a date string like "7/15/2026" becomes a real (computable) date, and a cell beginning "=" becomes a formula that evaluates. A delimited parser respects double-quoted fields, so a quoted field keeps embedded separators, embedded newlines, and doubled quotes ("") as literal text. You choose where the data lands (a new sheet, or the current sheet at the selection) and, for a multi-tab workbook, which sheet(s) to bring in. The whole import is a single action that one Undo reverts.
+
+#### Imported cells get the right types (number, date, formula)
+
+**Given**
+
+**When** I import `10,20,=A1+B1\n7/15/2026`
+
+**Then**
+- **A1** shows `10`
+- **B1** shows `20`
+- **C1** shows `30`
+- **A2** shows `46218`
+- **C1** has formula `=A1+B1`
+- **A2** is numberFormat=date
+
+#### One Undo reverts the whole import
+
+**Given**
+
+**When** I import `10,20,=A1+B1\n7/15/2026`, then click **A1**, then undo
+
+**Then**
+- **A1** shows `null`
+- **B1** shows `null`
+- **C1** shows `null`
+- **A2** shows `null`
+
+#### Quoted fields keep embedded separators
+
+**Given**
+
+**When** I import `"a,b",c`
+
+**Then**
+- **A1** shows `a,b`
+- **B1** shows `c`
+
+#### A custom separator splits columns
+
+**Given**
+
+**When** I import `p q` (separator ` `)
+
+**Then**
+- **A1** shows `p`
+- **B1** shows `q`
diff --git a/content/docs/learn/playground/date-parsing.mdx b/content/docs/learn/playground/date-parsing.mdx
new file mode 100644
index 0000000..871d33c
--- /dev/null
+++ b/content/docs/learn/playground/date-parsing.mdx
@@ -0,0 +1,116 @@
+---
+title: "Type a date and get a real date"
+description: "Typing a date string (7/15/2026, 2026-07-15, Jul 15 2026) stores a computable date serial and auto-formats the cell as a date — instead of leaving it as inert text."
+---
+
+{/* GENERATED — do not edit by hand */}
+
+## What it does
+
+Type a date the way you'd say it — **`7/15/2026`**, **`2026-07-15`**, **`Jul 15 2026`** —
+and Studio stores a real **date serial** and formats the cell as a date, exactly like
+Google Sheets. The value computes: put **`=A1+1`** next to a typed date and you get the
+**next day**. Without this, a typed date was inert **text**, and applying the date format
+ did nothing to it.
+
+Recognition is conservative — it never turns your text into a number by accident. A lone
+month name (`Jan`), ordinary words, and a bare number (`46218`, already numeric) are left
+exactly as typed. The month comes first (US locale): `7/15/2026` is July 15; `15/7/2026`
+has no 15th month, so it stays text. Impossible dates (`2/29/2026`, `13/13/2026`) stay
+text too.
+
+**Accepted formats** (all map `2026-07-15` → serial `46218`): `M/D/YYYY`, `M/D/YY`,
+`YYYY-MM-DD`, `YYYY/MM/DD`, `Mon D[,] YYYY`, `Month D[,] YYYY`, `D-Mon-YYYY`.
+
+## Verified behavior
+
+_Matches Google Sheets:_ In Google Sheets, typing a recognized date literal creates a REAL date, not text: the cell stores a date serial (days since the 1899-12-30 epoch; 2026-07-15 = 46218) and Sheets AUTO-APPLIES a date number format on recognition, so it displays as a date (M/D/YYYY in this US-locale workbook) and is right-aligned. Because the value is a serial, `=+1` gives the next day. Editing the cell (or reading the formula bar) shows the DATE (7/15/2026), never the raw serial — and because that date string is an accepted input, committing it unchanged re-parses to the same serial (round-trip). Only dates are shown formatted while editing; other number formats show the raw number (you edit 1234.5, not $1,234.50). The accepted formats (US M/D/Y locale) include M/D/YYYY, M/D/YY, YYYY-MM-DD, YYYY/MM/DD, Mon D[,] YYYY, Month D[,] YYYY, and D-Mon-YYYY — all mapping 2026-07-15 to serial 46218. Validity is enforced (2/29/2024 is a date; 2/29/2026 and 13/13/2026 are not, and stay text), and the month comes first (15/7/2026 is not a date in this locale). A lone month name (Jan), ordinary text, and a bare number (46218 is already numeric) are NOT auto-converted. This is the input-parsing companion to, which rendered a serial as a date but did nothing to typed text. Separately, a Date-TYPED engine value — =TODAY, =DATE(y,m,d), or date arithmetic that stays a date like =DATE(...)+1 — displays as a date automatically, with no explicit numberFormat, because the engine owns the date-ness (Sheets: arithmetic on a date stays a date). (A known gap: `=+1` currently shows the serial, because Studio stores a typed date as a plain-number serial rather than a Date-typed value — the engine has no date-typed literal input yet; tracked as a core follow-up.)
+
+#### Typing M/D/YYYY stores a serial and auto-applies a date format
+
+**Given**
+
+**When** I type `7/15/2026` into **A1**
+
+**Then**
+- **A1** shows `46218`
+- **A1** is numberFormat=date
+
+#### ISO YYYY-MM-DD is recognized the same way
+
+**Given**
+
+**When** I type `2026-07-15` into **A1**
+
+**Then**
+- **A1** shows `46218`
+- **A1** is numberFormat=date
+
+#### A month-name date (Mon D YYYY) is recognized
+
+**Given**
+
+**When** I type `Jul 15 2026` into **A1**
+
+**Then**
+- **A1** shows `46218`
+- **A1** is numberFormat=date
+
+#### A typed date computes as a serial — =A1+1 is the next day
+
+**Given**
+
+**When** I type `7/15/2026` into **A1**, then type `=A1+1` into **A2**
+
+**Then**
+- **A1** shows `46218`
+- **A2** shows `46219`
+
+#### A lone month name stays text (no false positive)
+
+**Given**
+
+**When** I type `Jan` into **A1**
+
+**Then**
+- **A1** shows `Jan`
+- **A1** is numberFormat=false
+
+#### A bare serial number is not auto-dated (stays a plain number)
+
+**Given**
+
+**When** I type `46218` into **A1**
+
+**Then**
+- **A1** shows `46218`
+- **A1** is numberFormat=false
+
+#### The coerced value and its auto date-format undo together
+
+**Given**
+
+**When** I type `7/15/2026` into **A1**, then undo
+
+**Then**
+- **A1** shows `null`
+- **A1** is numberFormat=false
+
+#### Re-committing the date the editor shows round-trips to the same serial
+
+**Given**
+
+**When** I type `7/15/2026` into **A1**, then type `7/15/2026` into **A1**
+
+**Then**
+- **A1** shows `46218`
+- **A1** is numberFormat=date
+
+#### A Date-typed formula result (=DATE+1) stays a date the ValueFormatter shows automatically
+
+**Given**
+
+**When** I type `=DATE(2026,7,15)+1` into **A1**
+
+**Then**
+- **A1** shows `46219`
diff --git a/content/docs/learn/playground/date-picker.mdx b/content/docs/learn/playground/date-picker.mdx
new file mode 100644
index 0000000..e779c63
--- /dev/null
+++ b/content/docs/learn/playground/date-picker.mdx
@@ -0,0 +1,112 @@
+---
+title: "Pick a date from a calendar"
+description: "Double-clicking a date cell opens a calendar popover; picking a day writes that date (serial + date format) through the normal edit path — the Google Sheets affordance, not a text edit."
+---
+
+{/* GENERATED — do not edit by hand */}
+
+## What it does
+
+Double-click a cell that holds a **date** and Studio opens a small **calendar popover**
+right at the cell — exactly like Google Sheets. Pick a day and that date lands in the cell:
+because the cell is a real date, it stores the date **serial** and keeps its date format, so
+it still reads as **`7/20/2026`** and **`=A1+1`** still gives the next day. It's the fast,
+visual way to enter or fix a date — the companion to typing a date and
+rendering one.
+
+The calendar opens on the **month of the cell's current date**, with that day highlighted;
+an empty date cell opens on the current month. Month and year controls page the view, and
+the header shows the displayed **Month Year**.
+
+It's fully **keyboard-accessible**: arrow keys move the highlighted day (left/right a day,
+up/down a week — crossing month boundaries), **Enter** picks it, and **Escape** closes the
+popover without changing the cell. A **non-date** cell is untouched — double-clicking it
+opens the normal in-cell editor, never the picker.
+
+## Verified behavior
+
+_Matches Google Sheets:_ In Google Sheets, double-clicking a DATE-formatted cell opens a small CALENDAR popover anchored at the cell, showing the month of the cell's current date with that day selected (an empty date cell opens on the current month with nothing selected). Picking a day writes that date into the cell: because the cell is a real date, the stored value is the date SERIAL (days since the 1899-12-30 epoch; 2026-07-15 = 46218) and the cell keeps its date number format, so it still displays as M/D/YYYY (US locale) and `=| +1` still gives the next day — real calc, not text. The popover has month and year navigation (a header showing the displayed Month Year) and is keyboard-accessible: arrow keys move the highlighted day (left/right by a day, up/down by a week, crossing month boundaries), Enter picks the highlighted day, and Escape closes WITHOUT committing (the cell keeps its prior value). A NON-date cell double-click opens the normal in-cell editor instead — the picker is specific to date cells, and the picker and the inline editor are never open at the same time. The value ground truth is anchored to the same engine DATEVALUE authority as (7/15/2026 = 46218, 7/16 = 46219, 7/20 = 46223, 7/22 = 46225): a picked day is written as a US M/D/YYYY literal, which re-parses to the serial through the existing date-literal coercion. This is the visual, faster companion to (typing a date) and (rendering a date).
+
+#### Double-clicking a date cell opens the calendar popover
+
+**Given**
+- **A1** = `7/15/2026`
+
+**When** I double-click **A1**
+
+**Then**
+- `[data-slot="date-picker"]` is visible containing "July 2026"
+
+#### Reopening the picker re-seeds the view to the cell's month, not a navigated one
+
+**Given**
+- **A1** = `7/15/2026`
+
+**When** I double-click **A1**, then press `PageDown`, then double-click **A1** again to reopen the calendar
+
+**Then**
+- `[data-slot="date-picker"]` is visible containing "July 2026"
+
+#### Picking a day commits the serial and keeps the date format
+
+**Given**
+- **A1** = `7/15/2026`
+
+**When** I double-click **A1**, then pick day **20** in the calendar
+
+**Then**
+- **A1** shows `46223`
+- **A1** is numberFormat=date
+
+#### A picked date still computes — =A1+1 is the next day
+
+**Given**
+- **A1** = `7/15/2026`
+
+**When** I double-click **A1**, then pick day **20** in the calendar, then type `=A1+1` into **A2**
+
+**Then**
+- **A1** shows `46223`
+- **A2** shows `46224`
+
+#### Keyboard — ArrowRight then Enter picks the next day
+
+**Given**
+- **A1** = `7/15/2026`
+
+**When** I double-click **A1**, then press `ArrowRight`, then press `Enter`
+
+**Then**
+- **A1** shows `46219`
+- **A1** is numberFormat=date
+
+#### Keyboard — ArrowDown then Enter picks a week later
+
+**Given**
+- **A1** = `7/15/2026`
+
+**When** I double-click **A1**, then press `ArrowDown`, then press `Enter`
+
+**Then**
+- **A1** shows `46225`
+
+#### Escape closes the picker without committing
+
+**Given**
+- **A1** = `7/15/2026`
+
+**When** I double-click **A1**, then press `Escape`
+
+**Then**
+- **A1** shows `46218`
+- `[data-slot="date-picker"]` is hidden
+
+#### A non-date cell double-click does not open the picker
+
+**Given**
+- **A1** = `hello`
+
+**When** I double-click **A1**
+
+**Then**
+- `[data-slot="date-picker"]` is hidden
diff --git a/content/docs/learn/playground/display-formatting.mdx b/content/docs/learn/playground/display-formatting.mdx
new file mode 100644
index 0000000..db983e0
--- /dev/null
+++ b/content/docs/learn/playground/display-formatting.mdx
@@ -0,0 +1,118 @@
+---
+title: "Format how a cell's value is displayed"
+description: "Show a number as currency, percent, a plain grouped number, or a date — and step its decimal places — without ever changing the underlying value."
+---
+
+{/* GENERATED — do not edit by hand */}
+
+## What it does
+
+Select cells and press **Ctrl+Shift+4** for currency (`$1,234.50`), **Ctrl+Shift+5** for
+percent (`0.1257` → `12.57%`), **Ctrl+Shift+1** for a plain grouped number (`1,234.50`),
+**Ctrl+Shift+3** to read a serial as a date (`45852` → `7/14/2025`), or **Ctrl+Shift+2**
+for a date + time (`46218.5` → `7/15/2026 12:00:00`) — or use the toolbar's
+**$ · % · decrease/increase-decimals · 123 · date · date-time** group. Re-applying an
+
+A value that carries a time-of-day reads as a date + time **automatically**, no format
+needed: `=NOW` comes back from the engine as a Date-typed fractional serial and shows
+as `7/15/2026 0:39:39` (Google Sheets' default), while a whole-day date like `=TODAY`
+shows as just `7/15/2026`.
+active category (or clicking its pressed button) reverts to automatic.
+
+Formatting is **presentation only**: it changes how the value reads, never the value
+itself. Format a `=A1/B1` cell as a percent and the formula and its computed ratio are
+untouched — only the display changes. Errors always show their code clearly (`#DIV/0!`,
+`#REF!`, `#VALUE!`, …), whatever number format the cell carries.
+
+## Verified behavior
+
+_Matches Google Sheets:_ A cell's number format is grid-owned PRESENTATION: it changes only the displayed string, never the stored value or formula (the formula bar still shows the raw value, and the format undoes/redoes like any edit). Applying currency to 1234.5 shows $1,234.50; to -42 shows -$42.00 (Google Sheets puts the minus before the $, not accounting parentheses). Percent scales by 100 and suffixes % with two decimals and no thousands grouping, so 0.1257 shows 12.57%. The Number format is thousands-grouped with two decimals (1,234.50). A date format renders a serial as M/D/YYYY (serial 45852 → 7/14/2025; Sheets' serial epoch is 1899-12-30). A datetime format renders a serial as M/D/YYYY H:MM:SS (the fraction is the time of day: 46218.5 → 7/15/2026 12:00:00; the whole-day 46218 → 7/15/2026 0:00:00). Google Sheets also DEFAULTS a value that carries a time-of-day to a datetime display: =NOW returns a Date-typed fractional serial and reads as date + time out of the box, while a whole-day date (=TODAY) reads as just the date. Increase/ decrease-decimals add or remove one decimal place from the current number format. Re-applying an already-active category clears it back to automatic. In Google Sheets the shortcuts are Number Ctrl+Shift+1, Date Ctrl+Shift+3, Currency Ctrl+Shift+4, Percent Ctrl+Shift+5, Time Ctrl+Shift+2 (Studio's datetime format takes that slot); decrease/increase-decimals are toolbar-only. A formula error shows its code clearly (#DIV/0!, #REF!, #VALUE!, …), regardless of any number format.
+
+#### Currency format is applied without changing the value
+
+**Given**
+- **A1** = `1234.5`
+
+**When** I click **A1**, then press `Control+Shift+4`
+
+**Then**
+- **A1** shows `1234.5`
+- **A1** is numberFormat=currency
+
+#### Percent format is applied without changing the value
+
+**Given**
+- **A1** = `0.1257`
+
+**When** I click **A1**, then press `Control+Shift+5`
+
+**Then**
+- **A1** shows `0.1257`
+- **A1** is numberFormat=percent
+
+#### Date format is applied to a serial without changing the value
+
+**Given**
+- **A1** = `45852`
+
+**When** I click **A1**, then press `Control+Shift+3`
+
+**Then**
+- **A1** shows `45852`
+- **A1** is numberFormat=date
+
+#### Number format is applied without changing the value
+
+**Given**
+- **A1** = `1234.5`
+
+**When** I click **A1**, then press `Control+Shift+1`
+
+**Then**
+- **A1** shows `1234.5`
+- **A1** is numberFormat=number
+
+#### Datetime format is applied to a serial without changing the value
+
+**Given**
+- **A1** = `46218.5`
+
+**When** I click **A1**, then press `Control+Shift+2`
+
+**Then**
+- **A1** shows `46218.5`
+- **A1** is numberFormat=datetime
+
+#### NOW is a Date-typed serial anchored to today (its time-of-day fraction reads as a datetime)
+
+**Given**
+- **A1** = `=NOW`
+- **B1** = `=INT(A1)=TODAY`
+
+**When** I click **A1**
+
+**Then**
+- **B1** shows `true`
+- **A1** has formula `=NOW`
+
+#### Re-applying the same category clears it back to automatic
+
+**Given**
+- **A1** = `1234.5`
+
+**When** I click **A1**, then press `Control+Shift+4`, then press `Control+Shift+4`
+
+**Then**
+- **A1** shows `1234.5`
+- **A1** is numberFormat=false
+
+#### A format is undoable and leaves the value untouched
+
+**Given**
+- **A1** = `0.1257`
+
+**When** I click **A1**, then press `Control+Shift+5`, then undo
+
+**Then**
+- **A1** shows `0.1257`
+- **A1** is numberFormat=false
diff --git a/content/docs/learn/playground/edit.mdx b/content/docs/learn/playground/edit.mdx
index 0333b98..cdf4cdd 100644
--- a/content/docs/learn/playground/edit.mdx
+++ b/content/docs/learn/playground/edit.mdx
@@ -3,6 +3,8 @@ title: "Edit a cell"
description: "Type into a cell to set a literal or a formula; the formula is stored and its computed value is shown."
---
+{/* GENERATED — do not edit by hand */}
+
## What it does
Select a cell and start typing (or press **Enter**/**F2** to open the in-cell editor) to set its contents. A bare value — `42`, `hello` — is stored as a literal. A leading `=` makes it a **formula**: TrueCalc stores the formula text and shows the computed result. Press **Enter** to commit and move down, or **Escape** to cancel.
@@ -11,10 +13,6 @@ Select a cell and start typing (or press **Enter**/**F2** to open the in-cell ed
-## How it works
-
-When you commit an edit, TrueCalc stores exactly what you typed. If it starts with `=` it is kept as the cell's formula and recomputed whenever a cell it depends on changes; otherwise it is stored as a literal. The same engine both keeps the formula text and computes the shown result, so editing behaves like Google Sheets.
-
## Verified behavior
_Matches Google Sheets:_ In Google Sheets, typing a bare number into a cell stores that literal and displays it. Typing a leading "=" makes the entry a formula: the cell stores the formula text and displays its computed result (e.g. with A1=10, entering "=A1*2" shows 20 while the formula bar still reads "=A1*2"). Typing into a non-empty cell replaces its prior contents — a literal overwrites a formula and vice-versa — and pressing Enter commits and moves the selection down.
@@ -23,7 +21,6 @@ _Matches Google Sheets:_ In Google Sheets, typing a bare number into a cell stor
**Given**
-
**When** I type `42` into **A1**
**Then**
diff --git a/content/docs/learn/playground/editable-formula-bar.mdx b/content/docs/learn/playground/editable-formula-bar.mdx
new file mode 100644
index 0000000..a3e34c0
--- /dev/null
+++ b/content/docs/learn/playground/editable-formula-bar.mdx
@@ -0,0 +1,66 @@
+---
+title: "Editable formula bar"
+description: "Click the formula bar to edit the active cell's value or formula — Enter commits and moves down, Escape reverts, and it shares the one in-cell editing model (enter/edit mode, point mode, reference highlighting)."
+---
+
+{/* GENERATED — do not edit by hand */}
+
+## What it does
+
+The formula bar above the grid is **editable**. Click it and the active cell opens
+for editing right there: type a literal or a formula, press **Enter** to commit
+(the cell recomputes and the selection moves **one row down**), or **Escape** to
+revert. It shows the cell's **raw content** — the `=…` formula, not the computed
+value — so it is where you read and rewrite a cell's formula.
+
+Editing in the bar is **the same edit** as editing in the cell — one editing
+session shown in two places. So everything the in-cell editor does, the bar does
+too: **enter-vs-edit mode**, caret-gated **formula point mode** (arrow keys steer
+a cell reference when the formula is expecting one), and **reference highlighting**
+— while you edit a formula, each distinct reference is **colored** in the bar and
+**outlined in the matching color** on the grid.
+
+## Try it
+
+With **A1 = 10** and **B1 = 20**, click **C1**, then click the **formula bar** and
+type `=A1+B1`. The references `A1` and `B1` light up in the bar (and outline on the
+grid) as you type. Press **Enter**: C1 shows **30**, the formula bar reads
+`=A1+B1`, and the selection drops to **C2**. Click **C1** again and press
+**Escape** midway through an edit to see it revert unchanged.
+
+## Verified behavior
+
+_Matches Google Sheets:_ In Google Sheets the formula bar shows the active cell's raw content (a literal, or the `=…` formula rather than the computed value) and is editable: clicking it places a text caret and lets you edit that cell as an alternative to editing in the cell. Pressing Enter commits the value to the active cell, recomputes, and moves the selection one row down; Escape abandons the edit and leaves the cell unchanged. The formula bar and the in-cell editor are the SAME edit kept in sync — two views of one editing session, not two independent editors — so editing a formula in the bar behaves exactly like editing it in the cell, including caret-gated point mode (arrow keys steer a reference when the formula expects one) and reference highlighting (each distinct reference token is colored to match the colored outlines on the grid). Editing changes content only, never the cell's number format.
+
+#### Editing a formula in the bar's model commits it to the active cell and recomputes
+
+**Given**
+- **A1** = `10`
+- **B1** = `20`
+
+**When** I type `=A1+B1` into **C1**
+
+**Then**
+- **C1** shows `30`
+- **C1** has formula `=A1+B1`
+
+#### Committing an edit replaces the cell's prior content
+
+**Given**
+- **A1** = `=5+5`
+
+**When** I type `42` into **A1**
+
+**Then**
+- **A1** shows `42`
+
+#### While editing a formula, its reference tokens are colored in the formula bar
+
+**Given**
+- **A1** = `10`
+- **B1** = `20`
+
+**When** I click **C1**, then press `=`, then press `A`, then press `1`
+
+**Then**
+- `[data-testid="formula-content"] span[data-ref-color]` is visible
diff --git a/content/docs/learn/playground/fill.mdx b/content/docs/learn/playground/fill.mdx
index 21446fc..0e13893 100644
--- a/content/docs/learn/playground/fill.mdx
+++ b/content/docs/learn/playground/fill.mdx
@@ -3,6 +3,8 @@ title: "Fill a formula across cells"
description: "Drag to extend a formula; references adjust automatically."
---
+{/* GENERATED — do not edit by hand */}
+
## What it does
Drag the fill handle at the bottom-right of a selection (or press **Ctrl+D** to fill down, **Ctrl+R** to fill right) to extend a formula. Relative references shift automatically, and TrueCalc recomputes every filled cell.
@@ -11,10 +13,6 @@ Drag the fill handle at the bottom-right of a selection (or press **Ctrl+D** to
-## How it works
-
-TrueCalc owns the calc. Filling adjusts the formula **text** — relative references shift, `$`-anchored ones stay fixed — and recomputes through the single engine. There is no second engine to reconcile, which is exactly why fill behaves like Google Sheets here.
-
## Verified behavior
_Matches Google Sheets:_ In Google Sheets, filling "=A1+1" down from A2 yields "=A2+1", "=A3+1", "=A4+1" — relative references shift by row, producing a column of values.
diff --git a/content/docs/learn/playground/format-bold.mdx b/content/docs/learn/playground/format-bold.mdx
index 0a9afc1..da36080 100644
--- a/content/docs/learn/playground/format-bold.mdx
+++ b/content/docs/learn/playground/format-bold.mdx
@@ -3,18 +3,12 @@ title: "Bold a cell"
description: "Ctrl+B (or the toolbar Bold button) toggles bold on the selection."
---
+{/* GENERATED — do not edit by hand */}
+
## What it does
Select cells and press **Ctrl+B** — or click the **Bold (B)** button in the toolbar — to toggle bold. Bold is **presentation**, separate from the cell's value: bolding `=A1+B1` styles the number without touching the formula. It undoes and redoes like any other edit.
-## Try it
-
-
-
-## How it works
-
-Bold is stored alongside the cell's value and rides the same undo/redo history, but it never reaches the calc engine — TrueCalc still owns the value; formatting is pure presentation. It is the foundation for the rest of the formatting toolbar (italic, font, color, borders, alignment, merge…), which add more attributes along the same edit and render path.
-
## Verified behavior
_Matches Google Sheets:_ In Google Sheets, Ctrl+B toggles bold on the selected cells. Bold is a presentation attribute independent of the cell's value or formula, and it undoes/redoes like any edit. Toggling again removes it.
diff --git a/content/docs/learn/playground/format-borders.mdx b/content/docs/learn/playground/format-borders.mdx
new file mode 100644
index 0000000..87ffd82
--- /dev/null
+++ b/content/docs/learn/playground/format-borders.mdx
@@ -0,0 +1,49 @@
+---
+title: "Add borders to cells"
+description: "The Borders toolbar popover applies Google-Sheets border modes (all/inner/outer/side/clear) in a chosen color + line style; Ctrl+Shift+7 toggles all borders."
+---
+
+{/* GENERATED — do not edit by hand */}
+
+## What it does
+
+Select a range and open the **Borders** button (the grid icon) in the toolbar to apply borders like Google Sheets. The popover offers a grid of **ten modes** — all borders, inner, inner-horizontal, inner-vertical, outer (box), the four single sides, and clear — plus a **border color** and a **line style** (thin / medium / thick / dashed / dotted / double). Pick a color and style, then click a mode to stroke the matching edges across the selection.
+
+Applying a mode is **additive across positions but a clean replace on each targeted edge** (verified against Sheets): adding a top border then a left border leaves both drawn, and applying _outer_ over _all borders_ keeps the interior lines — but re-applying a mode over an edge re-paints just that edge in the current pen (change the style to thick and re-apply and the edge becomes uniformly thick, never a doubled line). **Clear** removes every border. The keyboard chord **Ctrl+Shift+7** applies all borders (and toggles them off when the cell is already a full box).
+
+Borders are **presentation**, separate from the cell's value: bordering `=A1+B1` draws around the number without touching the formula. They undo and redo like any other edit.
+
+## Verified behavior
+
+_Matches Google Sheets:_ In Google Sheets the Borders toolbar button opens a popover with a grid of ten border modes — all, inner, inner-horizontal, inner-vertical, outer (box), the four single sides (left/top/right/bottom), and clear — plus a border-color picker and a line-style picker (thin/medium/thick/dashed/dotted/double, default thin solid black). Applying a mode sets the targeted edges of the selection to the current color+style and leaves other existing edges untouched (additive per edge); Clear removes all borders. Ctrl+Shift+7 applies all borders and toggles them off when the cell is already fully bordered. Borders are a presentation attribute independent of the cell's value or formula, and they undo/redo like any edit.
+
+#### Ctrl+Shift+7 adds all borders (thin solid black) to the selected cell
+
+**Given**
+- **A1** = `x`
+
+**When** I click **A1**, then press `Control+Shift+7`
+
+**Then**
+- **A1** is borders=`{"top":{"style":"solid","weight":"thin","color":"#000000"},"right":{"style":"solid","weight":"thin","color":"#000000"},"bottom":{"style":"solid","weight":"thin","color":"#000000"},"left":{"style":"solid","weight":"thin","color":"#000000"}}`
+
+#### Ctrl+Shift+7 a second time removes the borders
+
+**Given**
+- **A1** = `x`
+
+**When** I click **A1**, then press `Control+Shift+7`, then press `Control+Shift+7`
+
+**Then**
+- **A1** is borders=false
+
+#### borders are presentation only — the value is untouched
+
+**Given**
+- **A1** = `=2+3`
+
+**When** I click **A1**, then press `Control+Shift+7`
+
+**Then**
+- **A1** shows `5`
+- **A1** has formula `=2+3`
diff --git a/content/docs/learn/playground/format-fill-color.mdx b/content/docs/learn/playground/format-fill-color.mdx
new file mode 100644
index 0000000..4b23743
--- /dev/null
+++ b/content/docs/learn/playground/format-fill-color.mdx
@@ -0,0 +1,30 @@
+---
+title: "Set fill color"
+description: "The toolbar Fill color swatch sets the background (fill) color on the selection."
+---
+
+{/* GENERATED — do not edit by hand */}
+
+## What it does
+
+Select cells and click the **Fill color** swatch in the toolbar, then pick a color — the selected cells get that background color. Fill color is **presentation**, separate from the cell's value: filling `=A1+B1` paints the cell background without touching the formula or its computed value. It undoes and redoes like any other edit. Fill color is **toolbar-only** — Google Sheets has no keyboard shortcut for it, so Studio doesn't either.
+
+## Verification
+
+The scenario runner cannot drive a toolbar color swatch (it has no verb for it yet — a known gap tracked as a follow-up), so the manifest scenario above only asserts the driveable invariant (selecting a cell never changes its value/formula). The fill-application path itself is verified two other ways: the operation's unit tests assert `fillColorPatch('#fff2cc') === { fill: '#fff2cc' }` and the `styleContributor` wiring, and it is **browser-verified** — `applyFormat({ fill: '#fff2cc' })` paints the selected cell's background cream and undo reverts it. Full runner-driven E2E coverage lands once a toolbar-action verb exists.
+
+## Verified behavior
+
+_Matches Google Sheets:_ In Google Sheets, the Fill color control (the paint-bucket swatch in the toolbar) sets the background/fill color of the selected cells to a chosen color. Fill color is a presentation attribute independent of the cell's value or formula: it changes the cell's background, not what it computes to, and it undoes/redoes like any edit. Setting it does not alter the cell's stored value or formula. Google Sheets has no keyboard shortcut for fill color — it is toolbar-only.
+
+#### Selecting a formula cell leaves its value and formula unchanged (presentation-independence)
+
+**Given**
+- **A1** = `5`
+- **A2** = `=A1*2`
+
+**When** I click **A2**
+
+**Then**
+- **A2** shows `10`
+- **A2** has formula `=A1*2`
diff --git a/content/docs/learn/playground/format-font-family.mdx b/content/docs/learn/playground/format-font-family.mdx
new file mode 100644
index 0000000..d908a9c
--- /dev/null
+++ b/content/docs/learn/playground/format-font-family.mdx
@@ -0,0 +1,28 @@
+---
+title: "Change a cell's font family"
+description: "The toolbar Font dropdown sets the selection's font family (Default / Serif / Mono)."
+---
+
+{/* GENERATED — do not edit by hand */}
+
+## What it does
+
+Pick a font family for the selected cells from the **Font** dropdown in the toolbar — **Default** (`system-ui`), **Serif** (Georgia), or **Mono** (Courier New). Font family is **presentation**, separate from the cell's value: changing the family of `=A1+B1` styles the number without touching the formula. It undoes and redoes like any other edit.
+
+## Testing note
+
+The manifest scenario runner drives the canvas and keyboard only — it has no verb for clicking a toolbar control — so the family-change interaction is not yet expressible as a runnable scenario. It is verified end-to-end via agent-browser (change the Font `select`, then assert `window.__store.formatAt('A1').fontFamily`) and by colocated unit tests over the control's pure `apply`/`getValue` and the render `styleContributor`. Adding a `select` verb to the runner would let this feature carry a full runnable scenario like font size does.
+
+## Verified behavior
+
+_Matches Google Sheets:_ Font family is a per-cell presentation attribute, independent of the cell's value or formula, that undoes/redoes like any edit. In the Google editors it is chosen from a toolbar Font dropdown (there is no keyboard shortcut); choosing a family applies it to the whole selection, and an unformatted cell renders in the grid's default family until one is chosen.
+
+#### Selecting an unformatted cell keeps its value (default family)
+
+**Given**
+- **A1** = `x`
+
+**When** I click **A1**
+
+**Then**
+- **A1** shows `x`
diff --git a/content/docs/learn/playground/format-font-size.mdx b/content/docs/learn/playground/format-font-size.mdx
new file mode 100644
index 0000000..0e13297
--- /dev/null
+++ b/content/docs/learn/playground/format-font-size.mdx
@@ -0,0 +1,34 @@
+---
+title: "Change a cell's font size"
+description: "Ctrl+Shift+Period grows the font, Ctrl+Shift+Comma shrinks it (or use the toolbar stepper)."
+---
+
+{/* GENERATED — do not edit by hand */}
+
+## What it does
+
+Select cells and press **Ctrl+Shift+Period** to grow the font one step, or **Ctrl+Shift+Comma** to shrink it — or use the **[−] size [+]** stepper in the toolbar. Font size is **presentation**, separate from the cell's value: resizing `=A1+B1` styles the number without touching the formula. It undoes and redoes like any other edit, and stays within a sensible [6, 96] range.
+
+## Verified behavior
+
+_Matches Google Sheets:_ Font size is a per-cell presentation attribute, independent of the cell's value or formula, that undoes/redoes like any edit. Increasing bumps the size up one step and decreasing bumps it down one step, off the cell's current size. In the Google editors the keyboard shortcut to grow the font is Ctrl+Shift+Period and to shrink it is Ctrl+Shift+Comma. Studio's unformatted default is 10px (matching Google Sheets), so a single increase yields 11 and a single decrease yields 9; sizes are clamped to a sensible [6, 96] range.
+
+#### Ctrl+Shift+Period grows the font one step
+
+**Given**
+- **A1** = `x`
+
+**When** I click **A1**, then press `Control+Shift+Period`
+
+**Then**
+- **A1** is fontSize=11
+
+#### Ctrl+Shift+Comma shrinks the font one step
+
+**Given**
+- **A1** = `x`
+
+**When** I click **A1**, then press `Control+Shift+Comma`
+
+**Then**
+- **A1** is fontSize=9
diff --git a/content/docs/learn/playground/format-italic.mdx b/content/docs/learn/playground/format-italic.mdx
new file mode 100644
index 0000000..818db2b
--- /dev/null
+++ b/content/docs/learn/playground/format-italic.mdx
@@ -0,0 +1,34 @@
+---
+title: "Italicize a cell"
+description: "Ctrl+I (or the toolbar Italic button) toggles italic on the selection."
+---
+
+{/* GENERATED — do not edit by hand */}
+
+## What it does
+
+Select cells and press **Ctrl+I** — or click the **Italic (I)** button in the toolbar — to toggle italic. Italic is **presentation**, separate from the cell's value: italicizing `=A1+B1` styles the number without touching the formula. It undoes and redoes like any other edit.
+
+## Verified behavior
+
+_Matches Google Sheets:_ In Google Sheets, Ctrl+I toggles italic on the selected cells. Italic is a presentation attribute independent of the cell's value or formula, and it undoes/redoes like any edit. Toggling again removes it.
+
+#### Ctrl+I italicizes the selected cell
+
+**Given**
+- **A1** = `hello`
+
+**When** I click **A1**, then press `Control+i`
+
+**Then**
+- **A1** is italic
+
+#### Ctrl+I a second time removes italic
+
+**Given**
+- **A1** = `hello`
+
+**When** I click **A1**, then press `Control+i`, then press `Control+i`
+
+**Then**
+- **A1** is italic=false
diff --git a/content/docs/learn/playground/format-strike.mdx b/content/docs/learn/playground/format-strike.mdx
new file mode 100644
index 0000000..c353c1d
--- /dev/null
+++ b/content/docs/learn/playground/format-strike.mdx
@@ -0,0 +1,34 @@
+---
+title: "Strikethrough a cell"
+description: "Ctrl+Shift+X (or the toolbar Strikethrough button) toggles strikethrough on the selection."
+---
+
+{/* GENERATED — do not edit by hand */}
+
+## What it does
+
+Select cells and press **Ctrl+Shift+X** — or click the **Strikethrough (S)** button in the toolbar — to toggle strikethrough. Strikethrough is **presentation**, separate from the cell's value: striking `=A1+B1` draws a line through the number without touching the formula. It undoes and redoes like any other edit.
+
+## Verified behavior
+
+_Matches Google Sheets:_ In Google Sheets, Ctrl+Shift+X toggles strikethrough on the selected cells. Strikethrough is a presentation attribute independent of the cell's value or formula, and it undoes/redoes like any edit. Toggling again removes it.
+
+#### Ctrl+Shift+X strikes through the selected cell
+
+**Given**
+- **A1** = `hello`
+
+**When** I click **A1**, then press `Control+Shift+X`
+
+**Then**
+- **A1** is strike
+
+#### Ctrl+Shift+X a second time removes strikethrough
+
+**Given**
+- **A1** = `hello`
+
+**When** I click **A1**, then press `Control+Shift+X`, then press `Control+Shift+X`
+
+**Then**
+- **A1** is strike=false
diff --git a/content/docs/learn/playground/format-text-color.mdx b/content/docs/learn/playground/format-text-color.mdx
new file mode 100644
index 0000000..7e839a4
--- /dev/null
+++ b/content/docs/learn/playground/format-text-color.mdx
@@ -0,0 +1,30 @@
+---
+title: "Set text color"
+description: "The toolbar Text color swatch sets the text (foreground) color on the selection."
+---
+
+{/* GENERATED — do not edit by hand */}
+
+## What it does
+
+Select cells and click the **Text color** swatch in the toolbar, then pick a color — the selected cells' text is drawn in that color. Text color is **presentation**, separate from the cell's value: coloring `=A1+B1` restyles the number without touching the formula or its computed value. It undoes and redoes like any other edit. Text color is **toolbar-only** — Google Sheets has no keyboard shortcut for it, so Studio doesn't either.
+
+## Verification
+
+The scenario runner cannot drive a toolbar color swatch (it has no verb for it yet — a known gap tracked as a follow-up), so the manifest scenario above only asserts the driveable invariant (selecting a cell never changes its value/formula). The color-application path itself is verified two other ways: the operation's unit tests assert `textColorPatch('#ff0000') === { color: '#ff0000' }` and the `styleContributor` wiring, and it is **browser-verified** — `applyFormat({ color: '#ff0000' })` renders the selected cell's text red and undo reverts it. Full runner-driven E2E coverage lands once a toolbar-action verb exists.
+
+## Verified behavior
+
+_Matches Google Sheets:_ In Google Sheets, the Text color control (the "A" swatch in the toolbar) sets the foreground/text color of the selected cells to a chosen color. Text color is a presentation attribute independent of the cell's value or formula: it changes how the value is drawn, not what it computes to, and it undoes/redoes like any edit. Setting it does not alter the cell's stored value or formula. Google Sheets has no keyboard shortcut for text color — it is toolbar-only.
+
+#### Selecting a formula cell leaves its value and formula unchanged (presentation-independence)
+
+**Given**
+- **A1** = `5`
+- **A2** = `=A1*2`
+
+**When** I click **A2**
+
+**Then**
+- **A2** shows `10`
+- **A2** has formula `=A1*2`
diff --git a/content/docs/learn/playground/formula-autocomplete.mdx b/content/docs/learn/playground/formula-autocomplete.mdx
new file mode 100644
index 0000000..31779b7
--- /dev/null
+++ b/content/docs/learn/playground/formula-autocomplete.mdx
@@ -0,0 +1,130 @@
+---
+title: "Formula autocomplete"
+description: "Typing \"=\" then letters shows a dropdown of matching functions, navigable with the arrow keys; typing past an opening parenthesis replaces it with a one-line signature hint for the active argument."
+---
+
+{/* GENERATED — do not edit by hand */}
+
+## What it does
+
+Type `=` into a cell (or the formula bar) and keep typing letters — a dropdown
+opens beneath the cell listing every TrueCalc function whose name starts with
+what you've typed, each with a one-line summary and a **Learn more** link to
+its docs reference page. Press **ArrowDown**/**ArrowUp** to move a highlighted
+row through the list (it stops at either end — no wraparound). Click a
+suggestion, or press **Enter**/**Tab** while a row is highlighted, to insert
+that function's name and an opening `(`, ready for you to keep typing its
+arguments. The dropdown follows the caret: it only shows while you're
+mid-way through typing a bare name, and disappears as soon as you type past
+it.
+
+Once the caret is positioned INSIDE an active call's argument list — past its
+opening `(`, before its matching `)` — the name dropdown is replaced by a
+single-line **signature hint**: the function's full syntax (e.g.
+`SUM(value1,...)`), with the argument you're currently on highlighted. It
+tracks you live as you type or move across commas, and disappears once the
+caret leaves the call. The two popovers never show at once — Sheets shows one
+or the other, and so does Studio.
+
+## Try it
+
+
+
+## Verified behavior
+
+_Matches Google Sheets:_ In Google Sheets, typing "=" then letters into a cell (or the formula bar) opens a suggestion dropdown listing every function whose name starts with what you've typed so far, each with a short one-line description. ArrowDown/ ArrowUp move a highlighted row through the list (clamped at either end, no wraparound); clicking a suggestion — or pressing Enter/Tab while a row is highlighted — inserts that function's name followed by an opening parenthesis, replacing the partial text you typed, and leaves the cursor positioned to type arguments. The popover opens directly beneath wherever you're actually typing — under the cell for an in-cell edit, or under the formula bar itself when the edit was started by focusing the bar, even though the active cell may be scrolled far down the sheet. The dropdown tracks the caret: it stays open while you're typing a bare name and closes once you've moved past it (e.g. after the opening parenthesis). Once the caret is positioned INSIDE an active function call's argument list (past its opening parenthesis, before its matching close), Sheets replaces the name dropdown with a single-line signature hint showing that one function's syntax (e.g. "SUM(value1, [value2, …])") with the current argument position bolded, updating live as you type or move between comma-separated arguments — the two popovers are mutually exclusive, never shown together. The "Learn more" doc link on each name-list suggestion is Studio's own addition — Sheets has no equivalent to TrueCalc's public function reference pages, so there is no Sheets ground truth for that part; everything else above is the parity target.
+
+#### Typing "=SU" shows a dropdown of matching functions
+
+**Given**
+
+**When** I click **A1**, then press `=`, then press `S`, then press `U`
+
+**Then**
+- `[data-slot="formula-autocomplete"]` is visible containing "SUM"
+
+#### Selecting a suggestion inserts the function name and an opening parenthesis
+
+**Given**
+- **A1** = `5`
+
+**When** I click **A2**, then press `=`, then press `S`, then press `U`, then pick **SUM** from the autocomplete dropdown, then click **A1**, then press `)`, then press `Enter`
+
+**Then**
+- **A2** shows `5`
+- **A2** has formula `=SUM(A1)`
+
+#### ArrowDown highlights the second suggestion in the dropdown
+
+**Given**
+
+**When** I click **A1**, then press `=`, then press `S`, then press `U`, then press `M`, then press `ArrowDown`
+
+**Then**
+- `[data-slot="formula-autocomplete"] [aria-selected="true"]` is visible containing "SUMIF"
+
+#### ArrowDown then Enter inserts the SECOND match, not the top one
+
+**Given**
+- **A1** = `5`
+
+**When** I click **A2**, then press `=`, then press `S`, then press `U`, then press `M`, then press `ArrowDown`, then press `Enter`, then click **A1**, then press `)`, then press `Enter`
+
+**Then**
+- **A2** has formula `=SUMIF(A1)`
+
+#### Typing "=SUM(" shows a Tier 2 signature hint for SUM, and the name-list dropdown closes
+
+**Given**
+
+**When** I click **A1**, then press `=`, then press `S`, then press `U`, then press `M`, then press `(`
+
+**Then**
+- `[data-slot="formula-signature-help"]` is visible containing "SUM(value1,...)"
+- `[data-slot="formula-autocomplete"]` is hidden
+
+#### The signature hint tracks the argument position across commas
+
+**Given**
+
+**When** I click **A1**, then press `=`, then press `S`, then press `U`, then press `M`, then press `I`, then press `F`, then press `(`, then press `A`, then press `1`, then press `,`
+
+**Then**
+- `[data-slot="formula-signature-help"]` is visible containing "criterion"
+
+#### ArrowDown in the FORMULA BAR (not just the in-cell editor) also highlights the second suggestion
+
+**Given**
+
+**When** I click **A1**, then click the formula bar, then press `=`, then press `S`, then press `U`, then press `M`, then press `ArrowDown`
+
+**Then**
+- `[data-slot="formula-autocomplete"] [aria-selected="true"]` is visible containing "SUMIF"
+
+#### Tab in the formula bar accepts the highlighted match, not always the top one
+
+**Given**
+- **A1** = `5`
+
+**When** I click **A2**, then click the formula bar, then press `=`, then press `S`, then press `U`, then press `M`, then press `ArrowDown`, then press `Tab`, then click **A1**, then press `)`, then press `Enter`
+
+**Then**
+- **A2** has formula `=SUMIF(A1)`
+
+#### Typing in the FORMULA BAR anchors the dropdown near the bar, not the far-scrolled-down selected cell (product-owner finding — bar/cell anchor)
+
+**Given**
+
+**When** I click **A20**, then click the formula bar, then press `=`, then press `S`, then press `U`
+
+**Then**
+- `[data-slot="formula-autocomplete"]` is visible containing "SUM" near the top of the screen (≤250px)
+
+#### Re-editing a cell whose formula is unchanged from a prior edit session doesn't reuse a stale caret for the popover
+
+**Given**
+
+**When** I click **A1**, then press `=`, then press `S`, then press `U`, then press `M`, then press `(`, then press `Enter`, then click **A1**, then press `F2`, then press `Home`, then press `ArrowRight`, then press `ArrowRight`, then press `ArrowRight`, then press `ArrowRight`, then press `Escape`, then press `F2`, then press `ArrowDown`, then press `Enter`
+
+**Then**
+- **A1** has formula `=SUM(A2`
diff --git a/content/docs/learn/playground/growable-grid.mdx b/content/docs/learn/playground/growable-grid.mdx
new file mode 100644
index 0000000..c6dd8a9
--- /dev/null
+++ b/content/docs/learn/playground/growable-grid.mdx
@@ -0,0 +1,62 @@
+---
+title: "Growable grid dimensions"
+description: "The grid grows to fit its content and the cursor's reach — importing or pasting past the default 100×26, or navigating off the bottom/right edge, extends the grid instead of hiding data."
+---
+
+{/* GENERATED — do not edit by hand */}
+
+## What it does
+
+The grid used to be a fixed **100 rows × 26 columns**. The engine underneath is
+sparse (no dimension limit), so anything written past that rectangle — an
+imported CSV with more than 100 rows or more than 26 columns, a large paste — was
+**stored but unreachable**: you couldn't see it, scroll to it, or select it.
+
+Now the grid **grows to fit its content**. Its extent is derived from the active
+sheet: the larger of the compact default, the furthest cell that carries content,
+and wherever the cursor currently is. It's recomputed on every change — an edit,
+a **paste**, an **import**, undo/redo, a **sheet switch**, and a workbook load — so
+paint, scrolling, selection, and navigation (which all key off the extent) follow
+the data automatically.
+
+- **Import grows to fit.** After importing, the grid always includes the full
+ imported range. A 500-row / 40-column file is fully visible and scrollable.
+- **Grow on the edge.** Arrowing or Tabbing off the bottom/right edge adds a row
+ or column, Sheets-style, so you can always reach — and type into — the next
+ cell.
+- **Per sheet.** Each sheet sizes to its own content; switching sheets recomputes
+ the extent.
+
+Growth is bounded: imports are already capped by the import guardrail, so there
+is no unbounded dense span to size against.
+
+## Try it
+
+Import a CSV with a few hundred rows, or a wide one, and scroll down/right — every
+imported cell is there. Or select the last row and press the down arrow: the grid
+extends so the cursor keeps moving.
+
+## Verified behavior
+
+_Matches Google Sheets:_ In Google Sheets the grid is not a fixed rectangle: it grows to fit your data. Importing or pasting a range that extends past the current last row/column enlarges the sheet so every imported cell is visible and scrollable — nothing is silently hidden below or to the right. Pressing the arrow keys or Tab at the bottom/right edge adds more rows/columns (Sheets appends rows as you reach the bottom), so you can always navigate to, and type into, a cell beyond the current edge. Each sheet in a multi-sheet workbook sizes to its OWN content.
+
+#### Imported data below the default extent is visible and reachable
+
+**Given**
+
+**When** I import `x,y,z`, then select **A150**
+
+**Then**
+- **A150** shows `x`
+- **B150** shows `y`
+- **C150** shows `z`
+- the active cell is **A150**
+
+#### Arrowing past the last default row grows the grid
+
+**Given**
+
+**When** I select **A100**, then press `ArrowDown`
+
+**Then**
+- the active cell is **A101**
diff --git a/content/docs/learn/playground/history-revert.mdx b/content/docs/learn/playground/history-revert.mdx
new file mode 100644
index 0000000..585e1e3
--- /dev/null
+++ b/content/docs/learn/playground/history-revert.mdx
@@ -0,0 +1,43 @@
+---
+title: "Click-to-revert and where your data lives"
+description: "Clicking a history entry's Restore button non-destructively restores the document to that state — recording a new version and keeping every prior version (Google Sheets \"Restore this version\"), repeated identical ops collapse into one timeline row, and the chrome states plainly that the workbook + its history live in the browser only and warns before an unsaved tab closes."
+---
+
+{/* GENERATED — do not edit by hand */}
+
+## What it does
+
+The **History** panel is now interactive. Each entry carries an explicit, always-visible **"Restore" button** (Google Sheets Version History's "Restore this version" pattern — discoverable without hovering, keyboard-accessible); pressing it **non-destructively restores the document to that state** — it records a _new_ version at the top of the timeline and keeps every prior version, so nothing is lost (undo the restore to step back out of it). **Hovering** an entry previews it: the cells it touched light up on the grid (a transient amber highlight that clears when you move away), and the entry shows **what changed** — a `before → after` for a single value edit (e.g. `10 → 20`), or a short summary for a formatting change ("fill color") or a multi-cell edit ("3 cells"). Consecutive identical operations on the same cell (the 8× repeated date-format on `C5` seen in dogfooding) **collapse into a single row** with a `×N` badge, so the timeline stays legible. Timestamps are **human-readable** — "just now", "2 min ago", "Today 3:45 PM", "Yesterday", then a date like "Jul 14" for older entries — with the exact absolute time available on hover.
+
+The chrome now also tells you, at a glance, **where the current document and its history actually live**. Today the playground is standalone: the workbook is held **in your browser only** and is lost if you close the tab. A footer strip states this ("Not saved — this workbook lives in your browser and is lost if you close the tab."), the History panel header says "History is kept in this browser only", and closing the tab with unsaved edits **warns you first**.
+
+## Deferred to storage persistence
+
+The **persistent** half of this design waits on history persistence and the anonymous→account handoff. The restore semantics here are already the final, non-destructive ones; what's deferred is making the history and its versions _durable_:
+
+- **Persistent, cross-device version history** — the same non-destructive restore, but with versions saved server-side so they survive reload and other devices, rather than living only in this browser session. This PR ships the in-browser non-destructive restore; durability is.
+- **Account-saved history**, the `local` and `account` indicator states going live, and the **"Sign in to save"** upgrade CTA from standalone.
+
+## Verified behavior
+
+_Matches Google Sheets:_ Restoring to a past state matches Google Sheets File → Version history: picking a version and clicking "Restore this version" does NOT discard the versions in between — it makes a NEW current version equal to the chosen one and keeps the full history intact, and the restore is itself undoable. The standalone restore here is exactly that (non-destructive, create-a-new-version), driven from the timeline. The undo/redo of individual edits follows Sheets' Ctrl+Z — whole-edit steps, and a fresh edit clears the redo branch. The "where your data lives" signaling has no direct Sheets cell-behavior to replicate: Sheets always runs in one persistent, Drive-saved mode ("All changes saved in Drive") and never exposes a browser-only/at-risk mode, so the mode indicator + data-loss guard are a TrueCalc-original response to the playground being client-side with no account (a light oracle pass — this is Studio UX, not a cell computation).
+
+#### Undo steps back through edits and a new edit clears the redo branch (the restore substrate)
+
+**Given**
+- **A1** = `1`
+
+**When** I type `2` into **A1**, then type `3` into **A1**, then undo, then undo, then type `9` into **A1**, then redo
+
+**Then**
+- **A1** shows `9`
+
+#### The chrome states the workbook lives in the browser and is lost on close
+
+**Given**
+- **A1** = `1`
+
+**When** I click **A1**
+
+**Then**
+- `[aria-label="Storage status"]` is visible containing "lives in your browser"
diff --git a/content/docs/learn/playground/meta.json b/content/docs/learn/playground/meta.json
index e5784bb..dbc53c2 100644
--- a/content/docs/learn/playground/meta.json
+++ b/content/docs/learn/playground/meta.json
@@ -1,4 +1,39 @@
{
"title": "Playground",
- "pages": ["index", "edit", "fill", "clipboard", "undo", "format-bold"]
+ "pages": [
+ "index",
+ "edit",
+ "fill",
+ "clipboard",
+ "undo",
+ "format-bold",
+ "alignment",
+ "autofit-row-height",
+ "cell-editing-modes",
+ "clear-paint-format",
+ "command-history",
+ "data-import",
+ "date-parsing",
+ "date-picker",
+ "display-formatting",
+ "editable-formula-bar",
+ "format-borders",
+ "format-fill-color",
+ "format-font-family",
+ "format-font-size",
+ "format-italic",
+ "format-strike",
+ "format-text-color",
+ "formula-autocomplete",
+ "growable-grid",
+ "history-revert",
+ "multiple-sheets",
+ "navigate-range-select",
+ "persist",
+ "reference-highlighting",
+ "reference-translation",
+ "shortcuts",
+ "spill-rendering",
+ "view-workbook"
+ ]
}
diff --git a/content/docs/learn/playground/multiple-sheets.mdx b/content/docs/learn/playground/multiple-sheets.mdx
new file mode 100644
index 0000000..f7437e8
--- /dev/null
+++ b/content/docs/learn/playground/multiple-sheets.mdx
@@ -0,0 +1,90 @@
+---
+title: "Multiple sheets (tabs and cross-sheet references)"
+description: "A workbook holds several named sheets shown as tabs (add / switch / rename / delete / reorder); a formula on one sheet can reference another (=Sheet2!A1), computing and recalculating across sheets."
+---
+
+{/* GENERATED — do not edit by hand */}
+
+## What it does
+
+A workbook is no longer a single grid — it holds an ordered set of **sheets**, shown as **tabs** along a bottom bar (the Google-Sheets convention). You can:
+
+- **Switch** sheets by clicking a tab. Each sheet has its own independent cells and formats.
+- **Add** a sheet with the **`+`** button — it appends the next free `Sheet` and switches to it.
+- **Rename** a sheet by double-clicking its tab (or the tab menu → Rename). A blank or colliding name is rejected inline.
+- **Delete** a sheet from its tab menu (unavailable when only one sheet remains — a workbook always keeps at least one).
+- **Reorder** with the tab menu's **Move left / Move right** (clamped at the ends).
+
+And a formula on one sheet can **reference another**: `=Sheet2!A1` (a name with spaces is quoted, `='Q2 Data'!A1`). The value computes, and editing the precedent on the other sheet immediately recalculates the dependent. Add / rename / delete / reorder are all **undoable**; switching the active tab is a view change, not an undo step.
+
+## Verified behavior
+
+_Matches Google Sheets:_ In Google Sheets a workbook is a set of sheets shown as tabs along the bottom bar. The "+" button appends a new sheet named the next free `Sheet` and switches to it; clicking a tab activates that sheet (each sheet has its own independent cells and formats); a tab's menu (or right-click) offers Rename, Delete, and Move left / Move right (reorder), with Delete unavailable when only one sheet remains and Move clamped at the ends. A formula references another sheet with the SheetName!A1 qualifier (a name with spaces is quoted: ='Q2 Data'!A1); the referenced value computes, and editing the precedent on the other sheet immediately recalculates the dependent. Filling or copy-pasting a cross-sheet formula shifts the row/column by the usual relative-reference rules while the sheet name stays fixed. Deleting a sheet that other formulas reference makes those dependents show #REF!. Add / rename / delete / reorder are all undoable; switching the active tab is a view change, not an undo step. Two behaviors Studio defers (documented divergences, not silently missing): a rename does not yet rewrite references to that sheet living on OTHER sheets, and clicking another sheet's tab mid-formula to point-insert its reference isn't wired (typing the reference works fully).
+
+#### A reference to another sheet computes (=Sheet2!A1)
+
+**Given**
+
+**When** I add a sheet (switching to it), then type `5` into **A1**, then switch to sheet **Sheet1**, then type `=Sheet2!A1` into **A1**
+
+**Then**
+- **A1** shows `5`
+- **A1** has formula `=Sheet2!A1`
+
+#### Editing the precedent on Sheet2 recalcs the dependent on Sheet1
+
+**Given**
+
+**When** I add a sheet (switching to it), then type `5` into **A1**, then switch to sheet **Sheet1**, then type `=Sheet2!A1` into **A1**, then switch to sheet **Sheet2**, then type `9` into **A1**, then switch to sheet **Sheet1**
+
+**Then**
+- **A1** shows `9`
+
+#### Filling a cross-sheet formula down keeps the sheet name and shifts the row
+
+**Given**
+
+**When** I add a sheet (switching to it), then type `5` into **A1**, then type `6` into **A2**, then switch to sheet **Sheet1**, then type `=Sheet2!A1` into **A1**, then fill from **A1** to **A2**
+
+**Then**
+- **A1** shows `5`
+- **A2** shows `6`
+- **A1** has formula `=Sheet2!A1`
+- **A2** has formula `=Sheet2!A2`
+
+#### Add appends a new sheet and switches to it
+
+**Given**
+
+**When** I add a sheet (switching to it)
+
+**Then**
+- `[aria-label="Sheet tabs"]` is visible containing "Sheet2"
+
+#### Deleting a referenced sheet makes the dependent
+
+**Given**
+
+**When** I add a sheet (switching to it), then type `5` into **A1**, then switch to sheet **Sheet1**, then type `=Sheet2!A1` into **A1**, then delete sheet **Sheet2**
+
+**Then**
+- **A1** shows `#REF!`
+
+#### Undo restores a deleted sheet with its data and the reference recomputes
+
+**Given**
+
+**When** I add a sheet (switching to it), then type `5` into **A1**, then switch to sheet **Sheet1**, then type `=Sheet2!A1` into **A1**, then delete sheet **Sheet2**, then click **A1**, then undo
+
+**Then**
+- **A1** shows `5`
+
+#### Renaming the active sheet keeps its own cells reachable
+
+**Given**
+
+**When** I add a sheet (switching to it), then type `7` into **A1**, then rename sheet **Sheet2** to **Data**
+
+**Then**
+- **A1** shows `7`
+- `[aria-label="Sheet tabs"]` is visible containing "Data"
diff --git a/content/docs/learn/playground/navigate-range-select.mdx b/content/docs/learn/playground/navigate-range-select.mdx
new file mode 100644
index 0000000..503e7ce
--- /dev/null
+++ b/content/docs/learn/playground/navigate-range-select.mdx
@@ -0,0 +1,181 @@
+---
+title: "Navigate & range-select"
+description: "Click/shift-click/shift-arrow select a single cell or a range, which renders as a translucent highlight; Arrow/Tab/Enter move the active cell, and Ctrl+Arrow jumps to the edge of the contiguous data block (Sheets parity).\n"
+---
+
+{/* GENERATED — do not edit by hand */}
+
+## What it does
+
+**Click** a cell to select it. **Shift+click** or **Shift+Arrow** another cell
+to extend the selection into a range — it renders as a translucent highlight
+with a solid outline, anchored where you first clicked. **Arrow keys**, **Tab**,
+and **Enter** move the active cell (Tab right, Enter down after committing an
+edit).
+
+**Ctrl+Arrow** (**Cmd+Arrow** on Mac) jumps to the edge of a block of data,
+Sheets-style — the fast way to skip to the end of a column or row of numbers
+instead of holding the arrow key. Both chords work on every platform, but
+macOS reserves Ctrl+Arrow for switching Mission Control spaces and never
+delivers the keystroke to the browser at all, so Cmd+Arrow is the one that
+actually works on a Mac (matching Sheets' and Excel's own Mac bindings):
+
+- If the active cell AND the next cell in that direction both have content, it
+ jumps to the **last** cell of that contiguous run.
+- If the next cell is empty, it jumps forward to the **first** cell that has
+ content — or to the sheet's current edge if there's no more data ahead.
+- If the active cell is empty but the next cell has content, it lands on that
+ **immediately-adjacent** cell — the classic "double Ctrl+Down": from a blank
+ cell above a column of data, one press reaches the top, a second reaches the
+ bottom.
+- Already at the edge? Ctrl+Arrow doesn't move.
+- Ctrl+Shift+Arrow does the same jump while extending the range selection,
+ instead of relocating the lone active cell.
+
+## Try it
+
+
+
+Click A1, then press **Ctrl+→** to jump straight to C1 (the end of that run) —
+compare with holding the plain right arrow, which walks one cell at a time.
+
+## Verified behavior
+
+_Matches Google Sheets:_ In Google Sheets, clicking a cell selects it alone; shift-clicking or shift-arrowing another cell extends the selection into a rectangular range (anchored at the first click, with the moving corner following the pointer/ arrow), rendered as a translucent blue fill with a solid outline. Arrow keys, Tab, and Enter move the active cell one cell at a time (Tab right, Enter down after a commit). Ctrl+Arrow ("jump to data edge") is the fast-navigation chord: from the active cell, in the arrow's direction, it (a) jumps to the LAST cell of the contiguous non-empty run when BOTH the active cell and the adjacent cell have content, (b) jumps to the FIRST non-empty cell ahead when the adjacent cell is empty (regardless of whether the active cell itself has content), (c) lands on the immediately-adjacent cell when the active cell is empty but the adjacent cell has content — the classic "double Ctrl+Down" from a blank cell above a data column: one press reaches the TOP of the data, a second (now from a non-empty active cell) reaches the bottom — or (d) jumps to the edge of the sheet when there is no more data in that direction. At the sheet's edge already, Ctrl+Arrow is a no-op. Ctrl+Shift+ Arrow applies the same jump to the moving corner of the selection, extending the range instead of relocating the lone active cell.
+
+#### Clicking a cell makes it the sole active selection
+
+**Given**
+
+**When** I click **B2**
+
+**Then**
+- the active cell is **B2**
+
+#### Tab moves the active cell one column right
+
+**Given**
+
+**When** I click **A1**, then press `Tab`
+
+**Then**
+- the active cell is **B1**
+
+#### Committing an edit with Enter moves the active cell down
+
+**Given**
+- **A1** = `5`
+
+**When** I type `9` into **A1**
+
+**Then**
+- **A1** shows `9`
+- the active cell is **A2**
+
+#### Shift+Arrow extends the active cell without losing track of the anchor cell
+
+**Given**
+
+**When** I click **B2**, then press `Shift+ArrowDown`, then press `Shift+ArrowDown`
+
+**Then**
+- the active cell is **B4**
+
+#### Ctrl+Right, adjacent cell non-empty: jumps to the far edge of the contiguous run
+
+**Given**
+- **A1** = `1`
+- **B1** = `2`
+- **C1** = `3`
+
+**When** I click **A1**, then press `Control+ArrowRight`
+
+**Then**
+- the active cell is **C1**
+
+#### Ctrl+Right, adjacent cell empty: jumps forward to the first non-empty cell
+
+**Given**
+- **D2** = `9`
+
+**When** I click **A2**, then press `Control+ArrowRight`
+
+**Then**
+- the active cell is **D2**
+
+#### Ctrl+Down, active cell itself empty: same forward-jump rule applies
+
+**Given**
+- **A6** = `9`
+
+**When** I click **A2**, then press `Control+ArrowDown`
+
+**Then**
+- the active cell is **A6**
+
+#### Ctrl+Right with no more data ahead: jumps to the sheet's current edge
+
+**Given**
+
+**When** I click **A3**, then press `Control+ArrowRight`
+
+**Then**
+- the active cell is **Z3**
+
+#### Ctrl+Down mirrors the same rule vertically
+
+**Given**
+- **A5** = `1`
+- **A6** = `2`
+- **A7** = `3`
+
+**When** I click **A5**, then press `Control+ArrowDown`
+
+**Then**
+- the active cell is **A7**
+
+#### Ctrl+Arrow already at the sheet's edge is a no-op
+
+**Given**
+- **Z1** = `edge`
+
+**When** I select **Z1**, then press `Control+ArrowRight`
+
+**Then**
+- the active cell is **Z1**
+
+#### Ctrl+Down, active cell empty and adjacent cell non-empty: lands on the TOP of the data, not the bottom
+
+**Given**
+- **A2** = `1`
+- **A3** = `2`
+- **A4** = `3`
+
+**When** I click **A1**, then press `Control+ArrowDown`
+
+**Then**
+- the active cell is **A2**
+
+#### Ctrl+Shift+Down extends the range selection while jumping to the data edge
+
+**Given**
+- **A1** = `1`
+- **A2** = `2`
+- **A3** = `3`
+
+**When** I click **A1**, then press `Control+Shift+ArrowDown`
+
+**Then**
+- the active cell is **A3**
+
+#### Cmd+Down (Mac binding) jumps to the data edge, same as Ctrl+Down
+
+**Given**
+- **A5** = `1`
+- **A6** = `2`
+- **A7** = `3`
+
+**When** I click **A5**, then press `Meta+ArrowDown`
+
+**Then**
+- the active cell is **A7**
diff --git a/content/docs/learn/playground/persist.mdx b/content/docs/learn/playground/persist.mdx
new file mode 100644
index 0000000..4adfd0e
--- /dev/null
+++ b/content/docs/learn/playground/persist.mdx
@@ -0,0 +1,46 @@
+---
+title: "Save, load, and export a workbook"
+description: "Save/Load round-trip the whole workbook (cells + formats) through browser storage; Export writes it out as JSON, CSV, or XLSX."
+---
+
+{/* GENERATED — do not edit by hand */}
+
+## What it does
+
+A **File** control in the toolbar (the save icon, rightmost group) opens a menu:
+
+- **Save to browser** — writes the whole workbook (every cell's raw input + every cell's format) to local storage, in one slot.
+- **Load from browser** — reads that slot back and **replaces** the current workbook with it exactly: cells and formats not in the saved copy are cleared, not merged.
+- **Import JSON file…** — the same replace, from a `.json` file picked via a file input (for moving a workbook between browsers/machines, not just within one).
+- **Export JSON** — downloads the same canonical JSON as Save writes, as a file.
+- **Export CSV** — downloads the resolved grid values as CSV.
+- **Export XLSX** — downloads a `.xlsx` workbook (values + formulas + cell formatting) built with `exceljs`, lazy-loaded only when this button is used so it never bloats the app's initial bundle.
+
+## Verified behavior
+
+_Matches Google Sheets:_ Google Sheets autosaves continuously to Drive, and separately offers File → Download as a one-way export (XLSX / CSV / … ) of the current sheet's values and formulas. Studio has no server, so "save" here is the equivalent local primitive: File → Save writes the ENTIRE workbook — every cell's raw input (literal or formula) and every cell's presentation format (bold, italic, color, fill, …) — to the browser's local storage, keyed by one slot ("the workbook"); File → Load reads that slot back and REPLACES the current workbook with it exactly, byte-for-byte, the same way opening a saved Sheets file replaces what's on screen. Export mirrors Sheets' Download menu: JSON is Studio's own canonical schema (not a Sheets format, so there is no Sheets ground truth to match — it exists so the saved data is inspectable/portable); CSV is the resolved (computed) grid values, comma/quote/newline-escaped per RFC 4180, matching what Sheets' "Download → csv" produces for a single sheet; XLSX carries both the resolved value AND the formula text per cell (SheetJS `{t, v, f}` cells), like Sheets' "Download → xlsx" — opening it in Excel shows the same formula, best-effort (TrueCalc's formula syntax is Sheets-flavored and is written through unadjusted, so an Excel-specific function could fail to recalculate there; the resolved value is unaffected either way).
+
+#### A literal, a formula, and a format together are exactly what gets captured
+
+**Given**
+- **A1** = `10`
+
+**When** I type `=A1*2` into **A2**, then click **A2**, then press `Control+b`
+
+**Then**
+- **A1** shows `10`
+- **A2** shows `20`
+- **A2** has formula `=A1*2`
+- **A2** is bold
+
+#### Undo after formatting still leaves a value/formula round-trip-able
+
+**Given**
+- **A1** = `5`
+
+**When** I type `=A1+1` into **B1**, then click **B1**, then press `Control+b`, then undo
+
+**Then**
+- **B1** shows `6`
+- **B1** has formula `=A1+1`
+- **B1** is bold=false
diff --git a/content/docs/learn/playground/reference-highlighting.mdx b/content/docs/learn/playground/reference-highlighting.mdx
new file mode 100644
index 0000000..f26e248
--- /dev/null
+++ b/content/docs/learn/playground/reference-highlighting.mdx
@@ -0,0 +1,51 @@
+---
+title: "Colored formula references"
+description: "While editing a formula, each referenced cell/range is colored in the formula text and outlined in the same color on the grid."
+---
+
+{/* GENERATED — do not edit by hand */}
+
+## What it does
+
+Start editing a formula — type `=` in a cell (or double-click a formula cell) —
+and every reference lights up. Each distinct cell or range in the formula is
+given a **color** from a small palette: the reference **text** is colored in the
+in-cell editor and the **formula bar**, and the referenced cell — or a range like
+`A1:B2` drawn as one **block** — is **outlined in the same color** on the grid.
+This is the Google-Sheets/Excel behavior that makes a formula's inputs legible at
+a glance.
+
+Colors are assigned **in order of first appearance** and are **stable**: reuse
+`A1` twice and both tokens share one color and one outline; `$A$1` and `A1` point
+at the same cell, so they share a color too. In **point mode** (arrow keys or a
+click insert a reference while editing), the pointed cell is highlighted in its
+reference's color as it moves. Highlighting is **edit-scoped** — it appears only
+while editing and vanishes on commit or `Esc`, changing nothing about the value.
+
+## Verified behavior
+
+_Matches Google Sheets:_ In Google Sheets, while you edit a formula (a value beginning with `=`), each distinct cell/range reference is assigned a color from a small rotating palette in order of first appearance. The reference token in the formula text is shown in that color, and the referenced cell (or range, as a block) is outlined in the same color on the grid. A reference reused in the same formula keeps its color; absolute (`$A$1`) and relative (`A1`) references to the same cell share a color. Highlighting is presentation only — it appears only while editing and leaves no trace after the formula is committed.
+
+#### A formula reference colors its token and shows the live formula in the bar
+
+**Given**
+- **A1** = `10`
+- **B1** = `20`
+
+**When** I type `=A1+B1` into **C1**
+
+**Then**
+- **C1** shows `30`
+- **C1** has formula `=A1+B1`
+
+#### References resolve through the engine after commit (highlighting is presentation only)
+
+**Given**
+- **A1** = `5`
+- **B2** = `7`
+
+**When** I type `=A1+B2+A1` into **D1**
+
+**Then**
+- **D1** shows `17`
+- **D1** has formula `=A1+B2+A1`
diff --git a/content/docs/learn/playground/reference-translation.mdx b/content/docs/learn/playground/reference-translation.mdx
new file mode 100644
index 0000000..14015fb
--- /dev/null
+++ b/content/docs/learn/playground/reference-translation.mdx
@@ -0,0 +1,99 @@
+---
+title: "References adjust correctly on fill and paste"
+description: "Fill or paste a formula and its references retarget the Google-Sheets way — relative shift, $-anchored stay put (and now compute), text and function names untouched."
+---
+
+{/* GENERATED — do not edit by hand */}
+
+## What it does
+
+Fill a formula across a range (drag the fill handle, or **Ctrl+D** / **Ctrl+R**) or copy-and-paste it, and every reference retargets exactly the way Google Sheets does:
+
+- **Relative** references (`A1`) shift by the offset — down a row, across a column, or both.
+- **`$`-anchored** parts stay put: `$A$1` never moves, `$A1` keeps its column, `A$1` keeps its row — and they now **compute** as well as hold their text.
+- **Range** endpoints each adjust (`SUM(A1:A3)` → `SUM(A2:A4)`), and **cross-sheet** references keep their sheet (`Sheet1!A1` → `Sheet1!A2`).
+- References that only _look_ like cells are left alone: text inside a **string literal** (`"see A1"`), **function names** (`SUM`, even `LOG10`), and **`LET`/`LAMBDA`-bound names** are never rewritten.
+- A relative reference pushed **off the top or left edge** becomes `#REF!`, matching Sheets.
+
+## Try it
+
+
+
+## Verified behavior
+
+_Matches Google Sheets:_ In Google Sheets, filling or pasting a formula shifts each RELATIVE reference by the offset while `$`-anchored column/row parts stay fixed: "=U50+1" pasted one row down is "=U51+1"; "=$U$50+U50+$U50+U$50" pasted +2 rows/+3 cols is "=$U$50+X52+$U52+X$50"; range endpoints (SUM(U50:U51)→SUM(X52:X53)) and cross-sheet refs (Sheet1!U50→Sheet1!V51) adjust too; references inside a string literal ("lit U50") and function names (SUM) are never touched; and a relative reference pushed above row 1 or left of column A becomes #REF!.
+
+#### Relative references shift when filling down
+
+**Given**
+- **A1** = `10`
+- **A2** = `5`
+- **A3** = `6`
+- **B1** = `=A1*2`
+
+**When** I fill from **B1** to **B3**
+
+**Then**
+- **B2** shows `10`
+- **B3** shows `12`
+- **B2** has formula `=A2*2`
+- **B3** has formula `=A3*2`
+
+#### Relative references shift when filling right
+
+**Given**
+- **A1** = `2`
+- **B1** = `3`
+- **C1** = `4`
+- **A2** = `=A1*10`
+
+**When** I fill from **A2** to **C2**
+
+**Then**
+- **B2** shows `30`
+- **C2** shows `40`
+- **B2** has formula `=B1*10`
+- **C2** has formula `=C1*10`
+
+#### Absolute references stay fixed and evaluate when filling down
+
+**Given**
+- **A1** = `10`
+- **B1** = `=$A$1*2`
+
+**When** I fill from **B1** to **B3**
+
+**Then**
+- **B2** shows `20`
+- **B3** shows `20`
+- **B2** has formula `=$A$1*2`
+- **B3** has formula `=$A$1*2`
+
+#### Mixed references adjust only their relative part when filling down
+
+**Given**
+- **A1** = `1`
+- **A2** = `2`
+- **A3** = `3`
+- **B1** = `=$A1`
+
+**When** I fill from **B1** to **B3**
+
+**Then**
+- **B2** shows `2`
+- **B3** shows `3`
+- **B2** has formula `=$A2`
+- **B3** has formula `=$A3`
+
+#### A reference inside a string literal is not adjusted when filling down
+
+**Given**
+- **B1** = `="see A1"`
+
+**When** I fill from **B1** to **B3**
+
+**Then**
+- **B2** shows `see A1`
+- **B3** shows `see A1`
+- **B2** has formula `="see A1"`
+- **B3** has formula `="see A1"`
diff --git a/content/docs/learn/playground/shortcuts.mdx b/content/docs/learn/playground/shortcuts.mdx
new file mode 100644
index 0000000..ded08d5
--- /dev/null
+++ b/content/docs/learn/playground/shortcuts.mdx
@@ -0,0 +1,35 @@
+---
+title: "Keyboard shortcuts + the '?' cheat sheet"
+description: "Every keyboard shortcut is declared once, by its own operation; the same declaration drives both the key handler and a discoverable panel (press '?').\n"
+---
+
+{/* GENERATED — do not edit by hand */}
+
+## What it does
+
+Press **?** (Shift+/) anywhere in the grid to open a categorized list of every
+keyboard shortcut — grouped as **Edit**, **Formatting**, and **Help** — each
+row showing its action and its chord (e.g. "Bold — Ctrl+B"). Press **Escape**
+or click outside to close it.
+
+## Verified behavior
+
+_Matches Google Sheets:_ Individually, Studio's bindings already match Google Sheets: Ctrl+B bold, Ctrl+I italic, Ctrl+C/X/V copy/cut/paste, Ctrl+Z undo, Ctrl+Y (or Ctrl+Shift+Z) redo, Ctrl+D/R fill down/right, and Ctrl+Shift+7 all-borders are the same chords Sheets uses (each verified in its own feature manifest — format-bold.mdx, clipboard.mdx, undo.mdx, fill.mdx, format-borders.mdx — this manifest doesn't re-assert them, only that they're all reachable from one categorized list). The cheat-sheet TRIGGER itself is a deliberate divergence: Google Sheets opens its shortcut reference on Ctrl+/ (Cmd+/ on Mac). Studio binds a bare '?' (Shift+/) instead, following the newer cross-app convention (Gmail, Slack, GitHub, Trello, Notion) — chosen so it doesn't compete with Ctrl+/, which several browsers/extensions already reserve. Excel has no single-key shortcut-list binding to match either way (its shortcuts live in a Help-pane search), so there's no Excel parity claim to make here.
+
+#### Shift+/ opens a categorized panel listing the registry's shortcuts
+
+**Given**
+
+**When** I click **A1**, then press `Shift+?`
+
+**Then**
+- `[data-testid="shortcuts-panel"]` is visible containing "Bold"
+
+#### Escape closes the panel
+
+**Given**
+
+**When** I click **A1**, then press `Shift+?`, then press `Escape`
+
+**Then**
+- `[data-testid="shortcuts-panel"]` is hidden
diff --git a/content/docs/learn/playground/spill-rendering.mdx b/content/docs/learn/playground/spill-rendering.mdx
new file mode 100644
index 0000000..1603e3d
--- /dev/null
+++ b/content/docs/learn/playground/spill-rendering.mdx
@@ -0,0 +1,66 @@
+---
+title: "Spill a dynamic array from its anchor"
+description: "A dynamic-array formula (e.g. =SEQUENCE) renders across the grid from its anchor — the anchor shows the array's first value and the rest spill into read-only echo cells; obstructing the range shows a spill conflict."
+---
+
+{/* GENERATED — do not edit by hand */}
+
+## What it does
+
+Type a dynamic-array formula — `=SEQUENCE(3)`, `=SEQUENCE(2,3)`, `=A1:A3*2` — and the result **spills** across the grid from the cell you typed it into (the **anchor**). The anchor keeps the formula and shows the array's **top-left value**; the remaining cells fill with **read-only echoes** of the result. Selecting any cell in the range outlines the whole spilled region with a subtle **blue spill boundary**, matching Google Sheets. If a cell the array needs is already occupied, the spill can't land: the anchor shows a **spill conflict** and the blocking value is left untouched.
+
+## Verified behavior
+
+_Matches Google Sheets:_ In Google Sheets a dynamic-array formula computes at its ANCHOR cell and "spills" the result across neighbouring cells. The anchor holds the formula and displays the array's top-left value; the other cells are read-only echoes of the result and carry no formula of their own. Orientation follows the array's shape — =SEQUENCE(3) spills DOWN a 3x1 column (A1:A3 = 1,2,3), =SEQUENCE(1,3) spills ACROSS a 1x3 row (A1:C1 = 1,2,3), and =SEQUENCE(2,3) fills a 2x3 block. When a cell within a spill cell is selected, Sheets outlines the whole spilled range with a thin blue "spill boundary". If any target cell is occupied the array cannot spill: the anchor shows #SPILL!, no values spill, and the blocking value is preserved. (As of @truecalc/workbook 3.2.0 the engine spills N x 1 arrays VERTICALLY, matching Sheets — the orientation parity gap tracked in truecalc/core#707 is resolved. The blocked-spill error is still #SPILL! in Sheets vs a generic error in the current engine. See the oracle brief.)
+
+#### A single-column dynamic array spills DOWN a column (vertical, 3.2.0)
+
+**Given**
+
+**When** I type `=SEQUENCE(5)` into **E5**
+
+**Then**
+- **E6** shows `2`
+- **E7** shows `3`
+- **E8** shows `4`
+- **E9** shows `5`
+- **F5** shows `null`
+- **G5** shows `null`
+- **E5** has formula `=SEQUENCE(5)`
+
+#### A dynamic array spills across the row from its anchor
+
+**Given**
+
+**When** I type `=SEQUENCE(1,3)` into **E5**
+
+**Then**
+- **F5** shows `2`
+- **G5** shows `3`
+- **E5** has formula `=SEQUENCE(1,3)`
+
+#### A 2-D array spills a block; every echo is a value with no formula
+
+**Given**
+
+**When** I type `=SEQUENCE(2,3)` into **E5**
+
+**Then**
+- **F5** shows `2`
+- **G5** shows `3`
+- **E6** shows `4`
+- **F6** shows `5`
+- **G6** shows `6`
+- **E5** has formula `=SEQUENCE(2,3)`
+
+#### Obstructing the target range collapses the spill (spill conflict)
+
+**Given**
+- **G6** = `x`
+
+**When** I type `=SEQUENCE(2,3)` into **E5**
+
+**Then**
+- **G6** shows `x`
+- **F5** shows `null`
+- **E6** shows `null`
diff --git a/content/docs/learn/playground/undo.mdx b/content/docs/learn/playground/undo.mdx
index 205c00a..71edb7c 100644
--- a/content/docs/learn/playground/undo.mdx
+++ b/content/docs/learn/playground/undo.mdx
@@ -3,18 +3,12 @@ title: "Undo and redo edits"
description: "Ctrl+Z reverts the last change; Ctrl+Y (or Ctrl+Shift+Z) re-applies it."
---
+{/* GENERATED — do not edit by hand */}
+
## What it does
Every edit — typing a value, filling, pasting, clearing — is one undoable step. Press **Ctrl+Z** to revert the last step and **Ctrl+Y** (or **Ctrl+Shift+Z**) to re-apply it. A fresh edit after undoing clears the redo history, exactly like Google Sheets.
-## Try it
-
-
-
-## How it works
-
-Each edit records how to reverse it and how to re-apply it, so undo and redo simply replay the recorded step and recompute the affected cells through TrueCalc. Undo and redo act on whole edits rather than individual keystrokes, and a fresh edit after undoing discards the redo history.
-
## Verified behavior
_Matches Google Sheets:_ In Google Sheets, Ctrl+Z undoes the last edit — restoring the cell's prior contents — and Ctrl+Y (or Ctrl+Shift+Z) redoes it. Undo/redo apply to whole edits, not keystrokes, and a new edit clears the redo stack.
diff --git a/content/docs/learn/playground/view-workbook.mdx b/content/docs/learn/playground/view-workbook.mdx
new file mode 100644
index 0000000..321dbc4
--- /dev/null
+++ b/content/docs/learn/playground/view-workbook.mdx
@@ -0,0 +1,48 @@
+---
+title: "View a workbook"
+description: "A workbook's cell values render on the canvas grid — numbers and dates right-aligned, text left-aligned, formula errors shown distinctly — with only the visible cells drawn so scrolling stays smooth on a large sheet."
+---
+
+{/* GENERATED — do not edit by hand */}
+
+## What it does
+
+Every cell with content shows its computed value on the canvas grid, laid out the way Google Sheets lays it out: numbers and dates hug the right edge of the cell, text hugs the left — no explicit formatting needed, this is the automatic default. A formula that errors (`=1/0`, a broken reference, …) shows its error code (`#DIV/0!`, `#REF!`, `#VALUE!`, …) instead of a value, painted in a distinct error color so a broken formula is obvious without reading the text. Scrolling a large workbook stays smooth because only the rows/columns currently in (or near) the viewport are drawn.
+
+## Try it
+
+
+
+## Verified behavior
+
+_Matches Google Sheets:_ In Google Sheets, a cell's displayed value is type-driven by default: numbers and dates align to the right of the cell, text (and blank/unrecognized content) aligns to the left — matching the same automatic rule Sheets uses before any explicit alignment override is applied. A formula that errors (e.g. "=1/0") shows its error code ("#DIV/0!") in place of a value, rendered in a visually distinct (red) color so it reads as an error at a glance rather than as ordinary text. Sheets only ever draws/lays out the rows and columns in or near the viewport — scrolling a sheet with thousands of rows stays smooth because off-screen cells are never rendered.
+
+#### A literal number renders with no explicit alignment override (right-aligned by the type-based default — see How it works)
+
+**Given**
+- **A1** = `42`
+
+**When** I click **A1**
+
+**Then**
+- **A1** shows `42`
+
+#### A literal text value renders with no explicit alignment override (left-aligned by the type-based default — see How it works)
+
+**Given**
+- **A1** = `hello`
+
+**When** I click **A1**
+
+**Then**
+- **A1** shows `hello`
+
+#### A formula error displays its error code distinctly from a normal value
+
+**Given**
+- **A1** = `=1/0`
+
+**When** I click **A1**
+
+**Then**
+- **A1** shows `#DIV/0!`
| |