diff --git a/src/components/pages/IntroStep/IntroStep.stories.tsx b/src/components/pages/IntroStep/IntroStep.stories.tsx
new file mode 100644
index 0000000..63072f5
--- /dev/null
+++ b/src/components/pages/IntroStep/IntroStep.stories.tsx
@@ -0,0 +1,214 @@
+import { useState } from 'react';
+import type { Meta, StoryObj } from '@storybook/react';
+import { IntroStep } from './IntroStep';
+import type { IntroStepValues, IntroStepErrors } from './IntroStep';
+import { Navigation } from '../../organisms/Navigation';
+import Box from '@mui/material/Box';
+
+// ─── Helpers ─────────────────────────────────────────────────────────────────
+
+const FALogo = () => (
+
+
+
+
+);
+
+const nav = (
+ }
+ items={[
+ { label: 'FAQ', href: '/faq' },
+ { label: 'Contact Us', href: '/contact' },
+ { label: 'Log in', href: '/login' },
+ ]}
+ />
+);
+
+// ─── Meta ────────────────────────────────────────────────────────────────────
+
+const meta: Meta = {
+ title: 'Pages/IntroStep',
+ component: IntroStep,
+ tags: ['autodocs'],
+ parameters: {
+ layout: 'fullscreen',
+ },
+};
+
+export default meta;
+type Story = StoryObj;
+
+// ─── Interactive (default) ──────────────────────────────────────────────────
+
+/** Fully interactive — click through the progressive disclosure flow */
+export const Default: Story = {
+ render: () => {
+ const [values, setValues] = useState({
+ forWhom: null,
+ hasPassedAway: null,
+ });
+ const [errors, setErrors] = useState({});
+
+ const handleContinue = () => {
+ const newErrors: IntroStepErrors = {};
+ if (!values.forWhom) {
+ newErrors.forWhom = 'We need to know who the funeral is for to show you the right options.';
+ }
+ if (values.forWhom === 'someone' && !values.hasPassedAway) {
+ newErrors.hasPassedAway = 'This helps us tailor the process to your situation.';
+ }
+ setErrors(newErrors);
+ if (Object.keys(newErrors).length === 0) {
+ alert(`Continue with: ${JSON.stringify(values)}`);
+ }
+ };
+
+ return (
+ {
+ setValues(v);
+ setErrors({});
+ }}
+ onContinue={handleContinue}
+ errors={errors}
+ navigation={nav}
+ />
+ );
+ },
+};
+
+// ─── Pre-planning (myself) ──────────────────────────────────────────────────
+
+/** User has selected "Myself" — hasPassedAway auto-set to "no", no second question */
+export const PrePlanningMyself: Story = {
+ render: () => {
+ const [values, setValues] = useState({
+ forWhom: 'myself',
+ hasPassedAway: 'no',
+ });
+ return (
+ alert('Continue')}
+ navigation={nav}
+ />
+ );
+ },
+};
+
+// ─── At-need (someone, yes) ─────────────────────────────────────────────────
+
+/** User arranging for someone who has passed — at-need flow */
+export const AtNeedSomeone: Story = {
+ render: () => {
+ const [values, setValues] = useState({
+ forWhom: 'someone',
+ hasPassedAway: 'yes',
+ });
+ return (
+ alert('Continue')}
+ navigation={nav}
+ />
+ );
+ },
+};
+
+// ─── Pre-planning (someone, no) ─────────────────────────────────────────────
+
+/** User arranging for someone who is alive — pre-planning flow */
+export const PrePlanningSomeone: Story = {
+ render: () => {
+ const [values, setValues] = useState({
+ forWhom: 'someone',
+ hasPassedAway: 'no',
+ });
+ return (
+ alert('Continue')}
+ navigation={nav}
+ />
+ );
+ },
+};
+
+// ─── Validation errors ──────────────────────────────────────────────────────
+
+/** Both fields showing validation errors */
+export const WithErrors: Story = {
+ render: () => {
+ const [values, setValues] = useState({
+ forWhom: null,
+ hasPassedAway: null,
+ });
+ return (
+ {}}
+ errors={{
+ forWhom: 'We need to know who the funeral is for to show you the right options.',
+ }}
+ navigation={nav}
+ />
+ );
+ },
+};
+
+// ─── Loading state ──────────────────────────────────────────────────────────
+
+/** Continue button in loading state */
+export const Loading: Story = {
+ render: () => {
+ const [values, setValues] = useState({
+ forWhom: 'myself',
+ hasPassedAway: 'no',
+ });
+ return (
+ {}}
+ loading
+ navigation={nav}
+ />
+ );
+ },
+};
+
+// ─── No navigation (embedded) ───────────────────────────────────────────────
+
+/** Without navigation — for embedded/iframe use */
+export const Embedded: Story = {
+ render: () => {
+ const [values, setValues] = useState({
+ forWhom: null,
+ hasPassedAway: null,
+ });
+ return (
+ alert('Continue')}
+ hideHelpBar
+ />
+ );
+ },
+};
diff --git a/src/components/pages/IntroStep/IntroStep.tsx b/src/components/pages/IntroStep/IntroStep.tsx
new file mode 100644
index 0000000..616adee
--- /dev/null
+++ b/src/components/pages/IntroStep/IntroStep.tsx
@@ -0,0 +1,191 @@
+import React from 'react';
+import Box from '@mui/material/Box';
+import type { SxProps, Theme } from '@mui/material/styles';
+import { WizardLayout } from '../../templates/WizardLayout';
+import { ToggleButtonGroup } from '../../atoms/ToggleButtonGroup';
+import { Collapse } from '../../atoms/Collapse';
+import { Typography } from '../../atoms/Typography';
+import { Button } from '../../atoms/Button';
+import { Divider } from '../../atoms/Divider';
+
+// ─── Types ───────────────────────────────────────────────────────────────────
+
+/** Form values for the intro step */
+export interface IntroStepValues {
+ /** Who the funeral is being arranged for */
+ forWhom: 'myself' | 'someone' | null;
+ /** Whether the person has passed away (only relevant when forWhom="someone") */
+ hasPassedAway: 'yes' | 'no' | null;
+}
+
+/** Field-level error messages */
+export interface IntroStepErrors {
+ /** Error for the forWhom field */
+ forWhom?: string;
+ /** Error for the hasPassedAway field */
+ hasPassedAway?: string;
+}
+
+/** Props for the IntroStep page component */
+export interface IntroStepProps {
+ /** Current form values */
+ values: IntroStepValues;
+ /** Callback when any field value changes */
+ onChange: (values: IntroStepValues) => void;
+ /** Callback when the Continue button is clicked */
+ onContinue: () => void;
+ /** Field-level validation errors */
+ errors?: IntroStepErrors;
+ /** Whether the Continue button is in a loading state */
+ loading?: boolean;
+ /** Navigation bar — passed through to WizardLayout */
+ navigation?: React.ReactNode;
+ /** Hide the help bar */
+ hideHelpBar?: boolean;
+ /** MUI sx prop for the root */
+ sx?: SxProps;
+}
+
+// ─── Copy helpers ────────────────────────────────────────────────────────────
+
+function getSubheading(values: IntroStepValues): string {
+ if (values.forWhom === 'someone' && values.hasPassedAway === 'yes') {
+ return "We'll guide you through each step. You can save your progress and come back anytime.";
+ }
+ if (values.forWhom === 'myself' || values.hasPassedAway === 'no') {
+ return "Explore your options and plan at your own pace. Nothing is locked in until you're ready.";
+ }
+ return "We'll guide you through arranging a funeral, step by step. You can save your progress and come back anytime.";
+}
+
+// ─── Component ───────────────────────────────────────────────────────────────
+
+/**
+ * Step 1 — Intro page for the FA arrangement wizard.
+ *
+ * Entry point with urgency-sensitive segmentation. User selects who
+ * the funeral is for, and (if arranging for someone else) whether
+ * that person has died.
+ *
+ * Uses the Centered Form layout variant. Progressive disclosure:
+ * selecting "Someone else" reveals the hasPassedAway question.
+ * Selecting "Myself" auto-sets hasPassedAway to "no" (pre-planning).
+ *
+ * Pure presentation component — props in, callbacks out.
+ * No routing, state management, or API calls.
+ *
+ * Spec: documentation/steps/steps/01_intro.yaml
+ */
+export const IntroStep: React.FC = ({
+ values,
+ onChange,
+ onContinue,
+ errors,
+ loading = false,
+ navigation,
+ hideHelpBar,
+ sx,
+}) => {
+ const handleForWhomChange = (newValue: string | null) => {
+ const forWhom = newValue as IntroStepValues['forWhom'];
+ if (forWhom === 'myself') {
+ // Auto-set hasPassedAway to 'no' — user is alive, pre-planning
+ onChange({ forWhom, hasPassedAway: 'no' });
+ } else {
+ // Reset hasPassedAway when switching to "someone"
+ onChange({ forWhom, hasPassedAway: null });
+ }
+ };
+
+ const handleHasPassedAwayChange = (newValue: string | null) => {
+ onChange({ ...values, hasPassedAway: newValue as IntroStepValues['hasPassedAway'] });
+ };
+
+ const showHasPassedAway = values.forWhom === 'someone';
+
+ return (
+
+ {/* Page heading — receives focus on entry for screen readers */}
+
+ Let's get started
+
+
+
+ {getSubheading(values)}
+
+
+ {
+ e.preventDefault();
+ onContinue();
+ }}
+ >
+ {/* forWhom field */}
+
+
+
+
+ {/* hasPassedAway field — revealed when forWhom="someone" */}
+
+
+
+
+
+
+
+
+ {/* CTA */}
+
+
+
+
+
+ );
+};
+
+IntroStep.displayName = 'IntroStep';
+export default IntroStep;
diff --git a/src/components/pages/IntroStep/index.ts b/src/components/pages/IntroStep/index.ts
new file mode 100644
index 0000000..267bafe
--- /dev/null
+++ b/src/components/pages/IntroStep/index.ts
@@ -0,0 +1,2 @@
+export { default } from './IntroStep';
+export * from './IntroStep';
diff --git a/src/components/templates/WizardLayout/WizardLayout.tsx b/src/components/templates/WizardLayout/WizardLayout.tsx
index 9cf485f..5bce519 100644
--- a/src/components/templates/WizardLayout/WizardLayout.tsx
+++ b/src/components/templates/WizardLayout/WizardLayout.tsx
@@ -324,7 +324,9 @@ export const WizardLayout = React.forwardRef(
)}
{/* Main content area */}
- {children}
+
+ {children}
+
{/* Sticky help bar */}
{!hideHelpBar && }