# FuneralFinder — Flow Logic Reference

Technical reference for the FuneralFinder stepped search widget.
Use this when modifying the flow, adding steps, or integrating with a backend.

## Architecture Overview

The widget is a **single React component** with internal state. No external state
management required. The parent only needs to provide `funeralTypes`, optional
`themeOptions`, and an `onSearch` callback.

```
┌─────────────────────────────────────────┐
│  Header (h2 display + subheading)       │
│  ─────────────────────────────────      │
│                                         │
│  CompletedRows (stack of answered steps)│
│                                         │
│  Active Step (one at a time, Collapse)  │
│    Step 1 │ Step 2 │ Step 3 │ Step 4    │
│                                         │
│  ─── always visible ─────────────────── │
│  Location input                         │
│  [Find funeral providers] CTA           │
│  Free to use · No obligation            │
└─────────────────────────────────────────┘
```

## State

| State variable | Type | Default | Purpose |
|---|---|---|---|
| `intent` | `'arrange' \| 'preplan' \| null` | `null` | Step 1 answer |
| `planningFor` | `'myself' \| 'someone-else' \| null` | `null` | Step 2 answer (preplan only) |
| `typeSelection` | `string \| null` | `null` | Step 3 answer — funeral type ID or `'all'` |
| `servicePref` | `'with-service' \| 'without-service' \| 'either'` | `'either'` | Step 4 answer |
| `serviceAnswered` | `boolean` | `false` | Whether step 4 was explicitly answered |
| `selectedThemes` | `string[]` | `[]` | Optional theme filter IDs (multi-select) |
| `location` | `string` | `''` | Location input value |
| `locationError` | `string` | `''` | Validation error for location |
| `showIntentPrompt` | `boolean` | `false` | Show nudge when CTA clicked without intent |
| `editingStep` | `number \| null` | `null` | Which step is being re-edited (via "Change") |

## Step Flow

### Active Step Calculation

```typescript
const activeStep = (() => {
  if (editingStep !== null) return editingStep;  // User clicked "Change"
  if (!intent) return 1;                         // Need intent
  if (needsPlanningFor && !planningFor) return 2; // Need planning-for (preplan only)
  if (!typeSelection) return 3;                   // Need funeral type
  if (showServiceStep && !serviceAnswered) return 4; // Need service pref
  return 0;                                       // All complete
})();
```

`activeStep === 0` means all optional steps are answered. Only CompletedRows +
location + CTA are visible.

### Step Details

| Step | Question | Options | Auto-advances? | Conditional? |
|---|---|---|---|---|
| 1 | How can we help you today? | Arrange now / Pre-plan | Yes, on click | Always shown |
| 2 | Who are you planning for? | Myself / Someone else | Yes, on click | Only when `intent === 'preplan'` |
| 3 | What type of funeral? | TypeCards + Explore All + theme chips | Yes, on type card click | Always shown |
| 4 | Would you like a service? | With / No / Flexible (chips) | Yes, on chip click | Only when selected type has `hasServiceOption: true` |

### Auto-advance Mechanic

Steps 1, 2, and 4 auto-advance because selecting an option sets the state and
clears `editingStep`. The `activeStep` recalculation on the next render
determines the new step.

Step 3 also auto-advances when a type card is clicked. Theme preferences within
step 3 are optional — they're captured at whatever state they're in when the
type card click triggers collapse.

### Editing (reverting to a previous step)

Clicking "Change" on a CompletedRow calls `revertTo(stepNumber)`, which sets
`editingStep`. This overrides the `activeStep` calculation, reopening that step.
When the user makes a new selection, the handler clears `editingStep` and the
flow recalculates.

**Key behaviour:** Editing a step does NOT reset downstream answers. If you
change from Cremation to Burial (both have `hasServiceOption`), the service
preference carries forward. If you change to a type without `hasServiceOption`
(or to "Explore all"), `servicePref` resets to `'either'` and `serviceAnswered`
resets to `false`.

## CTA and Search Logic

### Minimum Requirements

The CTA button is **always visible and always enabled** (except during loading).
Minimum search requirements: **intent + location (3+ chars)**.

### Submit Behaviour

```
User clicks "Find funeral providers"
  │
  ├─ intent is null?
  │   → Show intent prompt (role="alert"), keep step 1 visible
  │   → Return (don't search)
  │
  ├─ location < 3 chars?
  │   → Show error on location input
  │   → Return (don't search)
  │
  └─ Both present?
      → Call onSearch() with smart defaults for missing optional fields
```

### Smart Defaults

| Field | If not explicitly answered | Default value |
|---|---|---|
| `funeralTypeId` | User didn't select a type | `null` (= show all types) |
| `servicePreference` | User didn't answer service step | `'either'` (= show all) |
| `themes` | User didn't select any themes | `[]` (= no filter) |
| `planningFor` | User on preplan path but didn't answer step 2 | `undefined` |

This means a user can: select intent → type location → click CTA. Everything
else defaults to "show all."

### Search Params Shape

```typescript
interface FuneralSearchParams {
  intent: 'arrange' | 'preplan';
  planningFor?: 'myself' | 'someone-else';   // Only on preplan path
  funeralTypeId: string | null;               // null = all types
  servicePreference: 'with-service' | 'without-service' | 'either';
  themes: string[];                           // May be empty
  location: string;                           // Trimmed, 3+ chars
}
```

## Conditional Logic Map

```
intent === 'preplan'
  └─ Shows step 2 (planning-for)

typeSelection !== 'all' && selectedType.hasServiceOption === true
  └─ Shows step 4 (service preference)

typeSelection !== null
  └─ CompletedRow for type shows (with theme summary if any selected)

serviceAnswered && showServiceStep
  └─ CompletedRow for service shows

themeOptions.length > 0
  └─ Theme chips appear within step 3 (always, not gated by type selection)

loading === true
  └─ CTA button shows spinner, button disabled
```

## Props Reference

| Prop | Type | Default | Notes |
|---|---|---|---|
| `funeralTypes` | `FuneralTypeOption[]` | required | Each has `id`, `label`, optional `description`, `note`, `hasServiceOption` |
| `themeOptions` | `ThemeOption[]` | `[]` | Each has `id`, `label`. Shown as optional chips in step 3 |
| `onSearch` | `(params: FuneralSearchParams) => void` | — | Called on valid submit |
| `loading` | `boolean` | `false` | Shows spinner on CTA, disables button |
| `heading` | `string` | `'Find funeral directors near you'` | Main h2 heading |
| `subheading` | `string` | `'Tell us a little about...'` | Below heading |
| `showExploreAll` | `boolean` | `true` | Show "Explore all options" TypeCard |
| `sx` | `SxProps<Theme>` | — | MUI sx override on root card |

## Sub-components (internal)

| Component | Purpose | Used in |
|---|---|---|
| `StepHeading` | Centered bodyLg heading with bottom margin | Steps 1-4 |
| `ChoiceCard` | Full-width radio card with label + description | Steps 1, 2 |
| `TypeCard` | Compact radio card with label + optional description/note | Step 3 |
| `CompletedRow` | Summary row: question + bold answer + "Change" link | All completed steps |

## Adding a New Step

1. Add state variable(s) for the new step's answer
2. Add a condition in `activeStep` calculation (between existing steps)
3. Add a `<Collapse in={activeStep === N}>` block in the render
4. Add a `<Collapse>` for the CompletedRow (with appropriate visibility condition)
5. Include the new data in `handleSubmit` → `onSearch()` params
6. Update `FuneralSearchParams` type

## Known Limitations (deferred)

- **No progress indicator** — users can't see how many steps remain
- **No roving tabindex** — radiogroups use button elements with `role="radio"` but
  arrow-key navigation between options is not implemented
- **No location autocomplete** — free text input only, validated on length
- **CSS vars used directly** — some styling uses `var(--fa-*)` tokens instead of
  MUI theme paths; works but doesn't support dynamic theme switching
