diff --git a/src/components/pages/CrematoriumStep/CrematoriumStep.stories.tsx b/src/components/pages/CrematoriumStep/CrematoriumStep.stories.tsx
new file mode 100644
index 0000000..ff57a58
--- /dev/null
+++ b/src/components/pages/CrematoriumStep/CrematoriumStep.stories.tsx
@@ -0,0 +1,217 @@
+import { useState } from 'react';
+import type { Meta, StoryObj } from '@storybook/react';
+import { CrematoriumStep } from './CrematoriumStep';
+import type { CrematoriumStepValues, CrematoriumStepErrors, Crematorium } from './CrematoriumStep';
+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' },
+ ]}
+ />
+);
+
+const singleCrematorium: Crematorium[] = [
+ {
+ id: 'warrill-park',
+ name: 'Warrill Park Crematorium',
+ location: 'Ipswich',
+ distance: '15 min from venue',
+ price: 850,
+ },
+];
+
+const multipleCrematoriums: Crematorium[] = [
+ {
+ id: 'warrill-park',
+ name: 'Warrill Park Crematorium',
+ location: 'Ipswich',
+ distance: '15 min from venue',
+ price: 850,
+ },
+ {
+ id: 'mt-gravatt',
+ name: 'Mt Gravatt Crematorium',
+ location: 'Mt Gravatt',
+ distance: '25 min from venue',
+ price: 920,
+ },
+ {
+ id: 'pinnaroo',
+ name: 'Pinnaroo Valley Memorial Park',
+ location: 'Padstow',
+ distance: '35 min from venue',
+ price: 780,
+ },
+];
+
+const defaultValues: CrematoriumStepValues = {
+ selectedCrematoriumId: null,
+ attend: 'no',
+ priority: '',
+ hasInstructions: 'no',
+ specialInstructions: '',
+};
+
+// ─── Meta ────────────────────────────────────────────────────────────────────
+
+const meta: Meta = {
+ title: 'Pages/CrematoriumStep',
+ component: CrematoriumStep,
+ tags: ['autodocs'],
+ parameters: {
+ layout: 'fullscreen',
+ },
+};
+
+export default meta;
+type Story = StoryObj;
+
+// ─── Single crematorium (most common) ───────────────────────────────────────
+
+/** Single pre-selected crematorium — confirmation pattern */
+export const Default: Story = {
+ render: () => {
+ const [values, setValues] = useState({
+ ...defaultValues,
+ selectedCrematoriumId: 'warrill-park',
+ });
+ return (
+ alert('Continue')}
+ onBack={() => alert('Back')}
+ onSaveAndExit={() => alert('Save')}
+ crematoriums={singleCrematorium}
+ deceasedName="Margaret"
+ navigation={nav}
+ />
+ );
+ },
+};
+
+// ─── Multiple crematoriums ──────────────────────────────────────────────────
+
+/** Multiple options — card grid with selection */
+export const MultipleCrematoriums: Story = {
+ render: () => {
+ const [values, setValues] = useState({ ...defaultValues });
+ const [errors, setErrors] = useState({});
+
+ const handleContinue = () => {
+ if (!values.selectedCrematoriumId) {
+ setErrors({ selectedCrematoriumId: 'Please confirm the crematorium.' });
+ return;
+ }
+ alert(`Selected: ${values.selectedCrematoriumId}`);
+ };
+
+ return (
+ {
+ setValues(v);
+ setErrors({});
+ }}
+ onContinue={handleContinue}
+ onBack={() => alert('Back')}
+ errors={errors}
+ crematoriums={multipleCrematoriums}
+ deceasedName="Margaret"
+ navigation={nav}
+ />
+ );
+ },
+};
+
+// ─── With special instructions ──────────────────────────────────────────────
+
+/** Special instructions textarea revealed */
+export const WithInstructions: Story = {
+ render: () => {
+ const [values, setValues] = useState({
+ ...defaultValues,
+ selectedCrematoriumId: 'warrill-park',
+ attend: 'yes',
+ hasInstructions: 'yes',
+ specialInstructions: 'Please ensure the family has 10 minutes for a private farewell.',
+ });
+ return (
+ alert('Continue')}
+ onBack={() => alert('Back')}
+ crematoriums={singleCrematorium}
+ deceasedName="Margaret"
+ navigation={nav}
+ />
+ );
+ },
+};
+
+// ─── Pre-planning ───────────────────────────────────────────────────────────
+
+/** Pre-planning variant — softer helper text */
+export const PrePlanning: Story = {
+ render: () => {
+ const [values, setValues] = useState({
+ ...defaultValues,
+ selectedCrematoriumId: 'warrill-park',
+ });
+ return (
+ alert('Continue')}
+ onBack={() => alert('Back')}
+ crematoriums={singleCrematorium}
+ isPrePlanning
+ navigation={nav}
+ />
+ );
+ },
+};
+
+// ─── Validation error ───────────────────────────────────────────────────────
+
+/** No crematorium selected with error */
+export const WithError: Story = {
+ render: () => {
+ const [values, setValues] = useState({ ...defaultValues });
+ return (
+ {}}
+ errors={{ selectedCrematoriumId: 'Please confirm the crematorium.' }}
+ crematoriums={multipleCrematoriums}
+ navigation={nav}
+ />
+ );
+ },
+};
diff --git a/src/components/pages/CrematoriumStep/CrematoriumStep.tsx b/src/components/pages/CrematoriumStep/CrematoriumStep.tsx
new file mode 100644
index 0000000..1638104
--- /dev/null
+++ b/src/components/pages/CrematoriumStep/CrematoriumStep.tsx
@@ -0,0 +1,340 @@
+import React from 'react';
+import Box from '@mui/material/Box';
+import TextField from '@mui/material/TextField';
+import FormControl from '@mui/material/FormControl';
+import FormLabel from '@mui/material/FormLabel';
+import FormControlLabel from '@mui/material/FormControlLabel';
+import RadioGroup from '@mui/material/RadioGroup';
+import Radio from '@mui/material/Radio';
+import type { SxProps, Theme } from '@mui/material/styles';
+import { WizardLayout } from '../../templates/WizardLayout';
+import { Card } from '../../atoms/Card';
+import { Collapse } from '../../atoms/Collapse';
+import { Typography } from '../../atoms/Typography';
+import { Button } from '../../atoms/Button';
+import { Divider } from '../../atoms/Divider';
+
+// ─── Types ───────────────────────────────────────────────────────────────────
+
+/** A crematorium available for selection */
+export interface Crematorium {
+ id: string;
+ name: string;
+ location: string;
+ distance?: string;
+ price?: number;
+}
+
+/** Form values for the crematorium step */
+export interface CrematoriumStepValues {
+ /** Selected crematorium ID */
+ selectedCrematoriumId: string | null;
+ /** Will anyone follow the hearse? */
+ attend: 'yes' | 'no';
+ /** Cremation timing preference */
+ priority: string;
+ /** Has special instructions */
+ hasInstructions: 'yes' | 'no';
+ /** Special instructions text */
+ specialInstructions: string;
+}
+
+/** Field-level error messages */
+export interface CrematoriumStepErrors {
+ selectedCrematoriumId?: string;
+ attend?: string;
+}
+
+/** Props for the CrematoriumStep page component */
+export interface CrematoriumStepProps {
+ /** Current form values */
+ values: CrematoriumStepValues;
+ /** Callback when any field value changes */
+ onChange: (values: CrematoriumStepValues) => void;
+ /** Callback when the Continue button is clicked */
+ onContinue: () => void;
+ /** Callback for back navigation */
+ onBack?: () => void;
+ /** Callback for save-and-exit */
+ onSaveAndExit?: () => void;
+ /** Field-level validation errors */
+ errors?: CrematoriumStepErrors;
+ /** Whether the Continue button is in a loading state */
+ loading?: boolean;
+ /** Available crematoriums */
+ crematoriums: Crematorium[];
+ /** Priority/timing options (provider-specific) */
+ priorityOptions?: Array<{ value: string; label: string }>;
+ /** Deceased first name — used to personalise copy */
+ deceasedName?: string;
+ /** Whether this is a pre-planning flow */
+ isPrePlanning?: boolean;
+ /** Navigation bar — passed through to WizardLayout */
+ navigation?: React.ReactNode;
+ /** Progress stepper */
+ progressStepper?: React.ReactNode;
+ /** Running total */
+ runningTotal?: React.ReactNode;
+ /** Hide the help bar */
+ hideHelpBar?: boolean;
+ /** MUI sx prop for the root */
+ sx?: SxProps;
+}
+
+// ─── Component ───────────────────────────────────────────────────────────────
+
+/**
+ * Step 8 — Crematorium for the FA arrangement wizard.
+ *
+ * Select a crematorium and cremation witness/attendance preferences.
+ * Only shown for cremation-type funerals.
+ *
+ * Often a single pre-selected crematorium (provider's default). When
+ * multiple options exist, shows a card grid with radiogroup pattern.
+ *
+ * Personalises witness question with deceased name when available.
+ *
+ * Pure presentation component — props in, callbacks out.
+ *
+ * Spec: documentation/steps/steps/08_crematorium.yaml
+ */
+export const CrematoriumStep: React.FC = ({
+ values,
+ onChange,
+ onContinue,
+ onBack,
+ onSaveAndExit,
+ errors,
+ loading = false,
+ crematoriums,
+ priorityOptions = [],
+ deceasedName,
+ isPrePlanning = false,
+ navigation,
+ progressStepper,
+ runningTotal,
+ hideHelpBar,
+ sx,
+}) => {
+ const isSingle = crematoriums.length === 1;
+
+ const handleFieldChange = (
+ field: K,
+ value: CrematoriumStepValues[K],
+ ) => {
+ onChange({ ...values, [field]: value });
+ };
+
+ const witnessHelper = deceasedName
+ ? `Please indicate if you wish to follow the hearse in your own vehicles to escort ${deceasedName} to the crematorium.`
+ : isPrePlanning
+ ? 'This can be decided closer to the time.'
+ : 'Please indicate if anyone wishes to follow the hearse to the crematorium.';
+
+ return (
+
+ {/* Page heading */}
+
+ Crematorium
+
+
+ {
+ e.preventDefault();
+ onContinue();
+ }}
+ >
+ {/* ─── Crematorium selection ─── */}
+
+ {isSingle ? (
+ // Single crematorium — presented as confirmation
+ handleFieldChange('selectedCrematoriumId', crematoriums[0].id)}
+ role="radio"
+ aria-checked={values.selectedCrematoriumId === crematoriums[0].id}
+ tabIndex={0}
+ sx={{ p: 3 }}
+ >
+ {crematoriums[0].name}
+
+ {crematoriums[0].location}
+ {crematoriums[0].distance && ` \u2022 ${crematoriums[0].distance}`}
+
+ {crematoriums[0].price != null && (
+
+ ${crematoriums[0].price.toLocaleString('en-AU')}
+
+ )}
+
+ ) : (
+ // Multiple crematoriums — radiogroup grid
+
+
+ {crematoriums.map((crem, index) => (
+ handleFieldChange('selectedCrematoriumId', crem.id)}
+ role="radio"
+ aria-checked={crem.id === values.selectedCrematoriumId}
+ tabIndex={
+ values.selectedCrematoriumId === null
+ ? index === 0
+ ? 0
+ : -1
+ : crem.id === values.selectedCrematoriumId
+ ? 0
+ : -1
+ }
+ sx={{ p: 3 }}
+ >
+ {crem.name}
+
+ {crem.location}
+ {crem.distance && ` \u2022 ${crem.distance}`}
+
+ {crem.price != null && (
+
+ ${crem.price.toLocaleString('en-AU')}
+
+ )}
+
+ ))}
+
+
+ )}
+
+ {errors?.selectedCrematoriumId && (
+
+ {errors.selectedCrematoriumId}
+
+ )}
+
+
+ {/* ─── Witness / attendance question ─── */}
+
+
+ Will anyone be following the hearse to the crematorium?
+
+
+ {witnessHelper}
+
+
+ handleFieldChange('attend', e.target.value as CrematoriumStepValues['attend'])
+ }
+ >
+ } label="Yes" />
+ } label="No" />
+
+ {errors?.attend && (
+
+ {errors.attend}
+
+ )}
+
+
+ {/* ─── Priority / timing preference (optional, provider-specific) ─── */}
+ {priorityOptions.length > 0 && (
+ handleFieldChange('priority', e.target.value)}
+ placeholder="Select timing preference (optional)"
+ fullWidth
+ sx={{ mb: 3 }}
+ >
+ {priorityOptions.map((opt) => (
+
+ ))}
+
+ )}
+
+ {/* ─── Special instructions ─── */}
+
+
+ Do you have any special requests for the cremation?
+
+
+ handleFieldChange(
+ 'hasInstructions',
+ e.target.value as CrematoriumStepValues['hasInstructions'],
+ )
+ }
+ >
+ } label="No" />
+ } label="Yes" />
+
+
+
+
+ handleFieldChange('specialInstructions', e.target.value)}
+ placeholder="Enter any special requests or instructions..."
+ multiline
+ minRows={3}
+ maxRows={8}
+ fullWidth
+ sx={{ mb: 3 }}
+ />
+
+
+
+
+ {/* CTAs */}
+
+ {onSaveAndExit ? (
+
+ ) : (
+
+ )}
+
+
+
+
+ );
+};
+
+CrematoriumStep.displayName = 'CrematoriumStep';
+export default CrematoriumStep;
diff --git a/src/components/pages/CrematoriumStep/index.ts b/src/components/pages/CrematoriumStep/index.ts
new file mode 100644
index 0000000..6a9aa85
--- /dev/null
+++ b/src/components/pages/CrematoriumStep/index.ts
@@ -0,0 +1,7 @@
+export { CrematoriumStep, default } from './CrematoriumStep';
+export type {
+ CrematoriumStepProps,
+ CrematoriumStepValues,
+ CrematoriumStepErrors,
+ Crematorium,
+} from './CrematoriumStep';