# S05 Research: Frontend — Nested Rendering, TOC, Citations

## Summary

Straightforward frontend work. The v2 data model is well-defined (S01/S03), the existing TechniquePage component has clear seams for modification, and no new libraries are needed. The main work is: detect format version → render v2 sections with H2/H3 nesting → add a TOC → make `[N]` citation markers clickable links to key moments.

## Recommendation

Light research — known patterns, established codebase. Three tasks: (1) update TypeScript types + build format-aware rendering, (2) add TOC component, (3) wire citation markers to key moment anchors. All in existing files plus one new component.

## Implementation Landscape

### Current State

**TechniquePage.tsx** (~350 lines) fetches technique by slug, supports version switching, renders body_sections with `Object.entries()` treating it as `Record<string, unknown>` (v1 dict format). Falls back to JSON dump for non-string content. Already has hash-based scrolling for `#km-{id}` fragments on key moments.

**public-client.ts** — `TechniquePageDetail` interface is missing `body_sections_format` and `source_videos` fields that S03 added to the backend schema. `body_sections` is typed as `Record<string, unknown> | null` — needs widening to `Record<string, unknown> | BodySectionV2[] | null`.

**App.css** (~5000 lines) has `.technique-prose`, `.technique-prose__section`, `.technique-prose__section h2/p` styles. Dark theme with CSS custom properties. No existing TOC styles.

### V2 Data Format

From `backend/pipeline/schemas.py`:

```
BodySection:
  heading: str        # H2
  content: str        # prose (empty string if substance is in subsections — see D024)
  subsections: list[BodySubSection]  # optional H3s

BodySubSection:
  heading: str        # H3
  content: str        # prose
```

Citations are `[N]` markers in content strings where N is a 0-based index into the page's key_moments array. Multi-cite: `[N,M]`. Validated by `citation_utils.py`.

### Format Detection

Backend returns `body_sections_format: "v1"` (existing pages, dict) or `"v2"` (new pages, list of objects). The field has a server-side default of `"v1"` so all existing data works unchanged.

### Key Anchors Already Exist

Each key moment renders with `id={`km-${km.id}`}`. The `[N]` citation index maps to `technique.key_moments[N].id`. The scroll-to-hash pattern (`scrollIntoView`) is already implemented for `#km-` fragments.

### What Needs to Change

1. **Type updates** (`public-client.ts`):
   - Add `body_sections_format: string` to `TechniquePageDetail`
   - Add `source_videos` field (array of `{id, filename, added_at}`)
   - Widen `body_sections` type to `Record<string, unknown> | unknown[] | null`

2. **Format-aware rendering** (`TechniquePage.tsx`):
   - Check `body_sections_format` — if `"v2"`, render list-of-objects; else existing dict logic
   - V2 renderer: iterate sections → H2 heading + content paragraph + subsections (H3 + content)
   - Per D024, sections with subsections have empty-string content — skip rendering empty `<p>`

3. **TOC component** (new, e.g. `components/TableOfContents.tsx`):
   - Takes v2 sections array, generates anchor IDs from headings (slugify)
   - Renders nested list: H2 entries with indented H3 sub-entries
   - Each item is an `<a href="#section-slug">` that smooth-scrolls
   - Add `id` attributes to rendered H2/H3 elements for anchor targets
   - Position: inside `.technique-columns__main`, above the prose, or as a sticky sidebar element

4. **Citation rendering**:
   - Parse `[N]` and `[N,M]` patterns in content strings
   - Replace with `<a href="#km-{momentId}" class="citation-link">[N]</a>`
   - Need the key_moments array to map index → moment ID
   - Superscript styling for citation markers

5. **CSS additions** (`App.css`):
   - `.technique-toc` — TOC container, possibly sticky
   - `.technique-toc__item`, `.technique-toc__sub-item` — nested list styles
   - `.technique-prose__subsection` — H3 sections
   - `.citation-link` — superscript, accent-colored, hover underline
   - Section anchor scroll offset (account for sticky nav)

### Slug Generation for Section IDs

Need a simple `toSlug()` utility: lowercase, replace spaces/special chars with hyphens, strip duplicates. No existing utility in the codebase. Can be a 3-line function inline or in `utils/`.

### Version Overlay Consideration

The `snapshotToOverlay()` function extracts `body_sections` from historical versions. It currently casts to `Record<string, unknown>`. For v2 snapshots, it should pass through as-is (list). The format-aware renderer handles both — no change needed to overlay logic as long as the type is widened.

### No External Dependencies

All work uses React, standard DOM APIs, and existing CSS patterns. No new npm packages needed.

## Verification Approach

1. **Type check**: `npm run build` passes (no TypeScript errors)
2. **V1 backward compat**: Existing technique pages render identically (format detection routes to old logic)
3. **V2 rendering**: If any v2 pages exist in the DB (from S04 pipeline runs), they render with H2/H3 nesting
4. **TOC**: TOC appears on v2 pages with clickable section links that scroll to the correct heading
5. **Citations**: `[N]` markers render as clickable links that scroll to the matching key moment
6. **Visual**: Browser verification on ub01:8096 for both v1 and v2 pages
