diff --git a/.github/skills/skill-creator/SKILL.md b/.github/skills/skill-creator/SKILL.md
index 942bfe8..5bb069c 100644
--- a/.github/skills/skill-creator/SKILL.md
+++ b/.github/skills/skill-creator/SKILL.md
@@ -138,6 +138,13 @@ Output: feat(auth): implement JWT-based authentication
Try to explain to the model why things are important in lieu of heavy-handed musty MUSTs. Use theory of mind and try to make the skill general and not super-narrow to specific examples. Start by writing a draft and then look at it with fresh eyes and improve it.
+### Quality Gate Before Finalizing
+
+Before saving any SKILL.md, do a quick self-check:
+
+- **American English only** — this project enforces US spelling throughout all code, comments, and documentation. Scan for common UK variants and replace: `behaviour→behavior`, `colour→color`, `customisation→customization`, `customising→customizing`, `organisation→organization`, `recognise→recognize`, `favour→favor`, `neighbour→neighbor`, `analyse→analyze`, `initialise→initialize`, `finalise→finalize`.
+- All examples follow the project's coding conventions (naming, formatting, structure).
+
### Test Cases
After writing the skill draft, come up with 2-3 realistic test prompts — the kind of thing a real user would actually say. Share them with the user: [you don't have to use this exact language] "Here are a few test cases I'd like to try. Do these look right, or do you want to add more?" Then run them.
diff --git a/.github/skills/stepper-command-dialog/SKILL.md b/.github/skills/stepper-command-dialog/SKILL.md
new file mode 100644
index 0000000..736b068
--- /dev/null
+++ b/.github/skills/stepper-command-dialog/SKILL.md
@@ -0,0 +1,234 @@
+---
+name: stepper-command-dialog
+description: Step-by-step guidance for building a multi-step wizard dialog (StepperCommandDialog) in a Cratis Arc application. Use whenever a command requires gathering information across multiple steps, implementing a wizard flow, breaking a complex form into named stages, or using StepperCommandDialog, StepperPanel, validateOnInit, or wizard-style navigation.
+---
+
+# StepperCommandDialog — Wizard Dialogs
+
+`StepperCommandDialog` organizes a single command form across multiple named steps. Users navigate with **Previous** and **Next** buttons; **Submit** only appears on the last step when every field across all steps is valid.
+
+Use this instead of `CommandDialog` when:
+- The form has too many fields to show at once
+- Fields can be grouped into logical stages (e.g. "Contact Info → Project Details → Summary")
+- You want guided, linear input with per-step validation feedback
+- The operation feels like a wizard or an onboarding flow
+
+---
+
+## Step 1 — Define the command
+
+A single command collects all fields across all steps. Each step contributes properties to the same command instance.
+
+```tsx
+// API/Projects/CreateProject.ts (proxy-generated — run dotnet build first)
+// C# side:
+[Command]
+public record CreateProject(string Name, string Email, string Description, decimal Budget)
+{
+ public ProjectCreated Handle() => new(Name, Email, Description, Budget);
+}
+```
+
+---
+
+## Step 2 — Build the dialog component
+
+```tsx
+import { StepperCommandDialog } from '@cratis/components/CommandDialog';
+import { StepperPanel } from 'primereact/stepperpanel';
+import { InputTextField, TextAreaField, NumberField } from '@cratis/components/CommandForm/fields';
+import { DialogResult, useDialogContext } from '@cratis/arc.react/dialogs';
+import { CreateProject } from '../api/Projects/CreateProject';
+
+const CreateProjectDialog = () => {
+ const { closeDialog } = useDialogContext();
+
+ return (
+
+ command={CreateProject}
+ title="Create New Project"
+ okLabel="Create"
+ onConfirm={() => closeDialog(DialogResult.Ok)}
+ onCancel={() => closeDialog(DialogResult.Cancelled)}
+ >
+
+
+ value={c => c.email}
+ title="Contact Email"
+ placeholder="Enter contact email"
+ type="email"
+ />
+
+
+
+ value={c => c.name}
+ title="Project Name"
+ placeholder="Enter project name"
+ />
+
+ value={c => c.description}
+ title="Description"
+ placeholder="Describe the project"
+ rows={4}
+ />
+
+
+
+ value={c => c.budget}
+ title="Budget"
+ placeholder="Enter budget"
+ />
+
+
+ );
+};
+```
+
+**Rules:**
+- Each `StepperPanel` takes a `header` string — this is the step label shown in the wizard navigation bar
+- All `CommandForm` fields inside any `StepperPanel` are bound to the **same** command instance
+- Fields map to command properties via the `value={c => c.propertyName}` accessor
+- The `Next` button is disabled while the current step has validation errors
+- `Submit` only appears on the **last** step when all fields (across all steps) are valid
+
+---
+
+## Step 3 — Wire the dialog to a parent component
+
+```tsx
+import { useDialog } from '@cratis/arc.react/dialogs';
+
+export const ProjectsPage = () => {
+ const [CreateProjectDialogWrapper, showCreateProject] = useDialog(CreateProjectDialog);
+
+ return (
+ <>
+
+
+ >
+ );
+};
+```
+
+---
+
+## Navigation behavior
+
+| Step | Footer buttons shown |
+|------|---------------------|
+| First step | **Next** |
+| Middle step | **Previous**, **Next** |
+| Last step (any step invalid) | **Previous** |
+| Last step (all valid) | **Previous**, **Submit** |
+
+Cancel is always available via the **×** button in the dialog header.
+
+---
+
+## Validation indicators
+
+Step number circles in the wizard navigation bar change color to reflect validity:
+
+- **Red circle** — the step contains at least one field with a validation error
+- **Green circle** — the step has been visited (navigated through) and all its fields are valid
+- **Default color** — the step has not been visited yet
+- **Dimmed** — a step that is not the currently active step
+
+To trigger validation immediately on open (before the user types anything), pass `validateOnInit`:
+
+```tsx
+
+```
+
+This is useful when the dialog opens with pre-populated values that may already be invalid.
+
+---
+
+## Customizing step labels
+
+The `okLabel`, `nextLabel`, and `previousLabel` props override the default button text:
+
+```tsx
+
+```
+
+---
+
+## Pre-populating values (edit wizard)
+
+Use `currentValues` for fields the user can change and `initialValues` for hidden/fixed fields (e.g. an ID):
+
+```tsx
+
+```
+
+---
+
+## Stepper orientation
+
+For longer wizards, vertical orientation can be more readable:
+
+```tsx
+
+ ...
+ ...
+
+```
+
+---
+
+## Props reference
+
+| Prop | Type | Default | Description |
+|------|------|---------|-------------|
+| `command` | `Constructor` | — | **Required.** Command class constructor |
+| `title` | `string` | — | **Required.** Dialog header title |
+| `children` | `StepperPanel[]` | — | **Required.** Wizard steps |
+| `okLabel` | `string` | `'Submit'` | Submit button label (last step) |
+| `nextLabel` | `string` | `'Next'` | Next button label |
+| `previousLabel` | `string` | `'Previous'` | Previous button label |
+| `visible` | `boolean` | `true` | Controls dialog visibility |
+| `width` | `string` | `'600px'` | Dialog width |
+| `isValid` | `boolean` | — | Extra validity gate combined with form validity |
+| `validateOnInit` | `boolean` | — | Run validation on mount to show errors immediately |
+| `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Stepper layout direction |
+| `linear` | `boolean` | `true` | Require steps to be completed in order |
+| `initialValues` | `Partial` | — | Fixed initial values (not shown to user as editable) |
+| `currentValues` | `Partial` | — | Pre-populated editable values |
+| `onConfirm` | `() => void \| Promise` | — | Called after successful execute |
+| `onCancel` | `() => void \| Promise` | — | Called when user dismisses dialog |
+| `onBeforeExecute` | `(values: TCommand) => TCommand` | — | Transform values before execution |
+| `pt` | `StepperProps['pt']` | — | PrimeReact PassThrough for deep DOM customization |
+
+---
+
+## Common mistakes
+
+| Mistake | Fix |
+|---------|-----|
+| Putting a Cancel button in the footer | Don't — the × in the dialog header is the cancel action |
+| One step per field | Group related fields; aim for 2–5 fields per step |
+| Fields from different steps sharing the same `value` accessor | Each property should appear on exactly one step |
+| Forgetting `header` on `StepperPanel` | Always set `header` — it is the navigation label |
+| Using `CommandDialog` for a 4+ field form | Consider `StepperCommandDialog` to reduce cognitive load |
diff --git a/.github/skills/write-documentation/SKILL.md b/.github/skills/write-documentation/SKILL.md
index b1cc792..10c1693 100644
--- a/.github/skills/write-documentation/SKILL.md
+++ b/.github/skills/write-documentation/SKILL.md
@@ -91,6 +91,7 @@ The project's voice is **direct, practical, and opinionated**. Write like an exp
- Use headings, lists, and code blocks to organize content — dense paragraphs lose readers.
- Focus on public APIs and features — never internal implementation.
- Do not document third-party libraries.
+- **American English only.** Always use US spellings: `color` not `colour`, `behavior` not `behaviour`, `customize` not `customise`, `organize` not `organise`, `recognize` not `recognise`, `analyze` not `analyse`, `initialize` not `initialise`.
## Code Examples
diff --git a/Documentation/CommandDialog/stepper-command-dialog.md b/Documentation/CommandDialog/stepper-command-dialog.md
new file mode 100644
index 0000000..8bac44c
--- /dev/null
+++ b/Documentation/CommandDialog/stepper-command-dialog.md
@@ -0,0 +1,170 @@
+# StepperCommandDialog
+
+The `StepperCommandDialog` component provides a multi-step wizard dialog interface for executing commands, powered by the PrimeReact Stepper.
+
+## Purpose
+
+`StepperCommandDialog` organizes a command form across multiple steps, guiding users through a wizard-like workflow. All steps gather into the same underlying command — the Submit button only **appears** when all fields across every step are valid and the user has reached the last step.
+
+## Key Features
+
+- Multi-step wizard navigation with Previous and Next buttons
+- All steps share a single command form — one command is submitted at the end
+- Submit button only appears on the last step when all fields are valid
+- Previous button hidden on the first step; Next button hidden on the last step
+- Cancel via the X button in the upper-right corner — no footer Cancel button
+- Step number circles change color to indicate validation state (red = errors, green = visited and valid)
+- Non-active steps are visually dimmed to keep focus on the current step
+- Busy state management during command execution
+- All PrimeReact `Stepper` customization props available directly (orientation, headerPosition, pt, etc.)
+- Supports any `CommandForm` field types inside each `StepperPanel`
+- Full integration with Cratis Arc command system
+
+## Basic Usage
+
+```typescript
+import { StepperCommandDialog } from '@cratis/components/CommandDialog';
+import { StepperPanel } from 'primereact/stepperpanel';
+import { InputTextField, TextAreaField, NumberField } from '@cratis/components/CommandForm/fields';
+import { CommandResult } from '@cratis/arc/commands';
+import { DialogResult, useDialog, useDialogContext } from '@cratis/arc.react/dialogs';
+
+const CreateProjectDialog = () => {
+ const { closeDialog } = useDialogContext>();
+
+ return (
+
+ command={CreateProject}
+ title="Create New Project"
+ okLabel="Create"
+ onConfirm={() => closeDialog(DialogResult.Ok)}
+ onCancel={() => closeDialog(DialogResult.Cancelled)}
+ >
+
+ value={c => c.name} title="Project Name" />
+ value={c => c.email} title="Contact Email" type="email" />
+
+
+ value={c => c.description} title="Description" rows={4} />
+ value={c => c.budget} title="Budget" />
+
+
+ );
+};
+
+function MyComponent() {
+ const [CreateProjectDialogWrapper, showCreateProjectDialog] = useDialog(CreateProjectDialog);
+
+ return (
+ <>
+
+
+ >
+ );
+}
+```
+
+## Props
+
+### Required Props
+
+- `command`: Constructor for the command type
+- `title`: Dialog title text
+- `children`: `StepperPanel` elements defining each step
+
+### Dialog Props
+
+- `visible`: Boolean controlling dialog visibility (defaults to `true`)
+- `initialValues`: Initial values for the command form
+- `currentValues`: Current values to populate the form
+- `onConfirm`: Confirm callback — called only after successful command execution
+- `onCancel`: Cancel callback — invoked when the X button is clicked
+- `onClose`: Fallback close callback
+- `okLabel`: Label for the submit button shown on the last step when valid (default: `'Submit'`)
+- `nextLabel`: Label for the next step button (default: `'Next'`)
+- `previousLabel`: Label for the previous step button (default: `'Previous'`)
+- `isValid`: Additional validity gate combined with command form validity
+- `width`: Dialog width (default: `'600px'`)
+- `resizable`: Whether the dialog can be resized
+- `style`: Custom CSS styles
+- `onFieldValidate`: Custom validation function for fields
+- `onFieldChange`: Callback when field values change
+- `onBeforeExecute`: Transform command values before execution
+
+### Stepper Props
+
+All [PrimeReact Stepper](https://primereact.org/stepper/) customization props are available directly:
+
+- `orientation`: `'horizontal'` (default) or `'vertical'`
+- `headerPosition`: `'top'`, `'right'`, `'bottom'`, or `'left'`
+- `linear`: Whether steps must be completed in order (default: `true`)
+- `onChangeStep`: Callback when the active step changes
+- `start`: Custom content rendered before the stepper navigation
+- `end`: Custom content rendered after the stepper navigation
+- `pt`: PrimeReact PassThrough options for deep DOM customization
+- `ptOptions`: PassThrough configuration options
+- `unstyled`: Removes built-in component styles
+
+## Validation Indicators
+
+The step number circles in the wizard navigation bar reflect the validation state of each step:
+
+| Circle color | Meaning |
+|---|---|
+| **Red** | The step contains at least one field with a validation error |
+| **Green** | The step has been visited (navigated through) and all its fields are valid |
+| **Default** (theme primary) | The step has not been visited yet |
+
+Steps that are not currently active are dimmed to keep visual focus on the current step.
+
+To show validation indicators immediately on open — before the user has touched any fields — pass the `validateOnInit` prop:
+
+```tsx
+
+```
+
+This is useful when the dialog opens with pre-populated values that may already be partially invalid.
+
+## Navigation and Submit
+
+| Step position | Footer content |
+|---|---|
+| First step | Next |
+| Middle step | Previous, Next |
+| Last step (invalid) | Previous |
+| Last step (valid) | Previous, Submit |
+
+Cancel is always available via the X button in the dialog header. The Submit button is hidden until the user reaches the last step **and** all command form fields across every step pass validation.
+
+## Busy State
+
+`StepperCommandDialog` automatically manages a busy state during command execution:
+
+- When Submit is clicked, the Submit button shows a loading spinner and all navigation buttons are disabled.
+- Once execution completes (success or failure), the buttons return to their normal state.
+
+## Step Structure
+
+Each step is defined by a `StepperPanel` from `primereact/stepperpanel`. The `header` prop sets the step title shown in the stepper navigation:
+
+```tsx
+
+ value={c => c.email} title="Email" />
+
+```
+
+CommandForm fields placed inside a `StepperPanel` are automatically bound to the same command instance, regardless of which step they are on.
+
+## Integration
+
+`StepperCommandDialog` integrates with:
+
+- `@cratis/arc/commands` for command execution
+- `@cratis/arc.react/commands` for form handling
+- PrimeReact `Stepper` and `StepperPanel` components for the wizard UI
+- PrimeReact `Dialog` component for the modal wrapper
+
diff --git a/Documentation/CommandDialog/toc.yml b/Documentation/CommandDialog/toc.yml
index b3f9d27..91d3ea2 100644
--- a/Documentation/CommandDialog/toc.yml
+++ b/Documentation/CommandDialog/toc.yml
@@ -2,3 +2,5 @@
href: index.md
- name: Advanced Features
href: advanced-features.md
+- name: StepperCommandDialog
+ href: stepper-command-dialog.md
diff --git a/Source/CommandDialog/StepperCommandDialog.css b/Source/CommandDialog/StepperCommandDialog.css
new file mode 100644
index 0000000..e363720
--- /dev/null
+++ b/Source/CommandDialog/StepperCommandDialog.css
@@ -0,0 +1,14 @@
+/* Copyright (c) Cratis. All rights reserved. */
+/* Licensed under the MIT license. See LICENSE file in the project root for full license information. */
+
+/* ── Step validation colors ─────────────────────────────────────────────── */
+/* The circle background colour for error/valid states is applied via inline
+ style on the number span through PrimeReact's passthrough API, so no CSS
+ class rules are needed here for those states.
+
+ Non-active steps are dimmed using PrimeReact's own data-p-active attribute
+ so no extra passthrough class injection is required. */
+
+.p-stepper [data-pc-section="header"][data-p-active="false"] {
+ opacity: 0.5;
+}
diff --git a/Source/CommandDialog/StepperCommandDialog.stories.tsx b/Source/CommandDialog/StepperCommandDialog.stories.tsx
new file mode 100644
index 0000000..df5e6d8
--- /dev/null
+++ b/Source/CommandDialog/StepperCommandDialog.stories.tsx
@@ -0,0 +1,335 @@
+// Copyright (c) Cratis. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+import React, { useState } from 'react';
+import { Meta, StoryObj } from '@storybook/react';
+import { StepperCommandDialog } from './StepperCommandDialog';
+import { Command, CommandResult, CommandValidator } from '@cratis/arc/commands';
+import { PropertyDescriptor } from '@cratis/arc/reflection';
+import { InputTextField, NumberField, TextAreaField } from '../CommandForm/fields';
+import { DialogResult, useDialog, useDialogContext } from '@cratis/arc.react/dialogs';
+import { StepperPanel } from 'primereact/stepperpanel';
+import '@cratis/arc/validation';
+
+const meta: Meta = {
+ title: 'CommandDialog/StepperCommandDialog',
+ component: StepperCommandDialog,
+};
+
+export default meta;
+type Story = StoryObj;
+
+class CreateProjectValidator extends CommandValidator {
+ constructor() {
+ super();
+ this.ruleFor((c: CreateProjectCommand) => c.name).notEmpty().minLength(2).maxLength(100);
+ this.ruleFor((c: CreateProjectCommand) => c.email).notEmpty().emailAddress();
+ this.ruleFor((c: CreateProjectCommand) => c.description).notEmpty().minLength(10);
+ this.ruleFor((c: CreateProjectCommand) => c.budget).greaterThan(0);
+ }
+}
+
+class CreateProjectCommand extends Command