Skip to content

Editor Core

This area covers 29 features. Watch the walkthrough, then use the reference below — each feature links to the exact moment it appears (▶).

Who this is for: Translator (bilingual contributor). Each feature below lists the role/permission it requires.

Features

Cell text editing ▶ 00:17

As a translator, I want to edit the target translation in each cell, so that I can add or modify the translated text.

How it works. User clicks on the target column to focus the TipTap editor, types or pastes text, and on idle (1.2s of keyboard pause) or blur, the cell value is committed via an outbox event. The value is sanitized to allow only inline formatting (bold, italic, underline, strikethrough, code). Plain text and HTML forms are serialized. Editing is blocked when another user holds the focus lock; a banner shows 'Alice is editing'.

WhoTranslator (bilingual contributor) PermissionsRead: viewer(100); edit target cells: contributor(400); edit source cells/reorder: project_lead(500)
Key files

src/components/TranslatedEditor.tsx:1-200 (editor setup, idle commit)
src/components/EditorTable.tsx:2099-2200 (TranslatedEditor integration)
src/hooks/useCells.ts:1009-1062 (optimistic edit patching)

Cell footnotes — inline USFM display and editing ▶ 00:00

As a translator, I want to view and edit USFM footnotes (\f...\f*) embedded in target text, so that I can maintain footnote content in Bible translations.

How it works. Target text containing \f...\f* is parsed and rendered as superscript marker chips, each hoverable to show the full footnote text. User can double-click a word to start a footnote insertion, or use menu to add a footnote at cursor/selection. Backspace/Delete on a marker shows a 'Delete footnote?' confirm dialog. Shift+Arrow selection extends correctly across footnote atoms. Footnote markers can be renumbered or reordered by editing caller syntax.

WhoTranslator (bilingual contributor) PermissionsRead: viewer(100); edit target cells: contributor(400); edit source cells/reorder: project_lead(500)
Key files

src/components/TranslatedEditor.tsx:66-72 (FootnoteInsertionAnchor interface)
src/components/TranslatedEditor.tsx:476-543 (insertFootnoteMarker / getFootnoteInsertionAnchor imperatives)
src/components/TranslatedEditor.tsx:845-868 (findFootnoteDeleteTarget / deleteFootnoteByIndex)
src/components/EditorTable.tsx:77-92 (AddFootnoteDialog imports)
src/components/EditorTable.tsx:2142-2175 (footnote state + extraction)

Cell validation (mark-as-complete) ▶ 00:11

As a reviewer, I want to mark a cell as validated to indicate the translation is complete and correct, so that the team knows which cells have been checked.

How it works. User clicks the validation pill (left gutter, target column) to toggle validation on/off. Icon changes based on validation status: empty/unvalidated = hollow circle; self-validated = filled circle with checkmark; fully validated by others (role check) = double checkmark. Validation is emitted as a cell.validate / cell.unvalidate event and requires role ≥ REVIEWER. Validation status appears in the validation history timeline on expansion.

WhoReviewer / translation consultant PermissionsValidate/unvalidate: reviewer(300)
Key files

src/components/EditorTable.tsx:2192-2231 (handleWaive / handleUnvalidate)
src/components/EditorTable.tsx:2506-2525 (validation emit + role check)
src/components/EditorTable.tsx:2920-2968 (validation pill rendering + tooltip)
src/lib/sync/events-emit.ts (emitCellValidate / emitCellUnvalidate)

Rule violations display and waiving ▶ 00:00

As a translator, I want to see which translation rules are violated in each cell, and I want to waive violations that are false positives, so that I can manage rule compliance.

How it works. Rule infractions render as colored underline blots in the target editor (major/minor severity driving hue). Clicking a blot opens the ViolationPopover showing the rule, the violation snippet, and options to view the full rule or waive it. Waiving opens a dialog with optional reason text. Waived infractions appear grayed out in the editor and are marked with a waiver icon. Waiver revocation (unwaive) removes the waiver record.

WhoTranslator (bilingual contributor) PermissionsRead: viewer(100); edit target cells: contributor(400); edit source cells/reorder: project_lead(500)
Key files

src/components/EditorTable.tsx:2187-2230 (handleWaive / handleUnwaive callbacks)
src/components/EditorTable.tsx:2373-2430 (ViolationPopover + waiver rendering)
src/components/EditorTable.tsx:1371-1376 (active vs waived partition)
src/lib/richtext/violation-decoration-plugin.ts (editor decorations)

AI-powered target completion (search + generate) ▶ 00:00

As a translator, I want to get AI-suggested translations for empty cells by searching examples and generating text, so that I can accelerate translation work.

How it works. User clicks the sparkle (completeSingle) or selects multiple cells (completeBatch) to start completion. UI shows 'Searching…' then 'Generating…' phases. Streaming preview text appears in the target column in real time. Completion can be disabled if not configured or user does not have the role. Errors show an inline message. When not configured, the sparkle is disabled and a click shows 'Set up AI completion' prompt.

WhoTranslator (bilingual contributor) PermissionsRead: viewer(100); edit target cells: contributor(400); edit source cells/reorder: project_lead(500)
Key files

src/components/EditorTable.tsx:1137-1142 (completion state props + loading)
src/components/EditorTable.tsx:1378-1391 (completingState derivations)
src/components/EditorTable.tsx:2276-2400 (completion overlay + preview rendering)
src/hooks/useCells.ts:285 (optimistic target-edit path)

AI terminology chips — active concept highlighting ▶ 00:00

As a translator, I want to see which words in the target match active terminology concepts, so that I can ensure consistent term usage.

How it works. When terminology concepts are active in the project, words matching a concept's sourceTerm (case-insensitive, normalized) render with a tiny colored chip at the top-right. Clicking a chip opens TermLookupPopover showing the concept details and rendering options. The source column also shows term chips for active concepts, and selected source terms trigger an 'Add to term base' affordance.

WhoTranslator (bilingual contributor) PermissionsRead: viewer(100); edit target cells: contributor(400); edit source cells/reorder: project_lead(500)
Key files

src/components/EditorTable.tsx:1708-1818 (SourceWithTermLookup component)
src/components/EditorTable.tsx:1628-1697 (SelectionTermActions toolbar)
src/components/TranslatedEditor.tsx:122 (terminologyConcepts prop)
src/components/TranslatedEditor.tsx:252-254 (createTerminologyChipExtension)

Backtranslation (BT) — reverse translation display and editing ▶ 00:00

As a translator, I want to view and edit the backtranslation (reverse translation of my target back to source) to verify the meaning was preserved, so that I can catch semantic drift.

How it works. When backtranslation is configured, the BT tab in cell expansion shows the current BT text. User can toggle an edit mode and modify the BT inline, save it (emitting a cell.backtranslation.set event), or generate a fresh BT via the Generate button. BT generation can be polished (AI-enhanced) vs plain. Errors and in-flight state are shown. Stale BT (source advanced since target commit) shows an amber dot on the expansion chevron.

WhoTranslator (bilingual contributor) PermissionsRead: viewer(100); edit target cells: contributor(400); edit source cells/reorder: project_lead(500)
Key files

src/components/EditorTable.tsx:1146-1151 (backtranslation props + error handling)
src/components/EditorTable.tsx:2560-2630 (backtranslation tab rendering + edit state)
src/components/EditorTable.tsx:3350-3450 (BacktranslationEditor / SaveBTButton)

Cell expansion panel — rich metadata and actions ▶ 00:00

As a translator, I want to access detailed metadata and rich editing controls for each cell (history, comments, backtranslation, validation, issues), so that I can make well-informed edits.

How it works. Clicking the chevron in the action rail opens a drop-down panel below the cell row, revealing tabbed content: 'Issues' (rule violations + issue tracking), 'BT' (backtranslation), 'Decay' (health/confidence), 'History' (edit timeline), and optionally 'Footnotes'. Each tab shows relevant content and enables editing where applicable. Panel closes via the chevron click, Escape key, or outside click. Tabs can be directly jumped to from inline events (clicking a violation jumps to Issues tab).

WhoTranslator (bilingual contributor) PermissionsRead: viewer(100); edit target cells: contributor(400); edit source cells/reorder: project_lead(500)
Key files

src/components/CellExpansion.tsx (panel shell + tab management)
src/components/EditorTable.tsx:3340-3800 (expansion tabs integrated into EditorRow)
src/components/CellActionRail.tsx:84-106 (chevron button + rail reveal)

Cell action rail — floating sidebar with per-row controls ▶ 00:00

As a translator, I want quick access to cell-specific actions (expand, complete, validate, comment, record audio) without cluttering the row, so that I can work efficiently.

How it works. On the right edge of each cell row, a floating action rail appears on hover or focus, containing buttons for: AI complete (sparkle), record audio (mic), TTS generate (volume), open comments (chat), and expand (chevron). Buttons show tooltips, disabled state when not applicable, and tiny attention dots when the cell has open comments or needs attention. The rail springs into view with a bouncy animation. The chevron is always faintly visible (configurable via alwaysShowChevron).

WhoReviewer / translation consultant PermissionsValidate/unvalidate: reviewer(300)
Key files

src/components/CellActionRail.tsx (rail container + button animations)
src/components/EditorTable.tsx:3900-4000 (rail integration into EditorRow)

Comment/discussion threads ▶ 00:00

As a team member, I want to leave comments on cells to discuss translations, ask questions, or note issues, so that we can collaborate asynchronously.

How it works. Clicking the comment button opens the comments drawer (external to EditorTable). Number of open comments shows as a badge on the rail button. Comments can be filtered, searched, and navigated per-file. Each comment shows author, timestamp, and can be replied to or resolved. Resolved comments can be shown/hidden via a filter toggle.

WhoTranslator (bilingual contributor) PermissionsRead: viewer(100); edit target cells: contributor(400); edit source cells/reorder: project_lead(500)
Key files

src/components/EditorTable.tsx:420-422 (cellOpenCommentCount, onOpenComments, onOpenHistory callbacks)
src/components/EditorTable.tsx:1455 (open-comment badge rendering)

Cell history — edit timeline with author and snapshot ▶ 00:00

As a reviewer, I want to see the history of edits to a cell (who changed it, when, what the value was), so that I can track the evolution of translations.

How it works. History tab in expansion shows a timeline of past edits, newest first, with author name, date, and a snippet of the value. Clicking an entry expands to show all validators for that state, with timestamps. The current state is always shown (at bottom, above the divider). History is loaded on-demand from the D1 event log.

WhoTranslator (bilingual contributor) PermissionsRead: viewer(100); edit target cells: contributor(400); edit source cells/reorder: project_lead(500)
Key files

src/components/EditorTable.tsx:247-312 (ValidationHistoryTimeline component)
src/hooks/useCells.ts:30-39 (EditValidationSummary interface)
src/hooks/useCells.ts:76-77 (history / validationHistory empty arrays)

Cell validation history — per-state validator timeline ▶ 00:00

As a manager, I want to see which team members have validated each translation state, so that I can track QA progress.

How it works. In the validation popover (right-click the validation circle), the history section shows a timeline of past validation states (value snapshots), each expandable to list all validators who marked that state complete, with their names and dates. Deleted validators show as grayed out and struck through.

WhoTranslator (bilingual contributor) PermissionsRead: viewer(100); edit target cells: contributor(400); edit source cells/reorder: project_lead(500)
Key files

src/components/EditorTable.tsx:247-312 (ValidationHistoryTimeline rendering)
src/components/EditorTable.tsx:3040-3110 (validation circle popover integration)

Cell selection and bulk actions ▶ 00:00

As a translator, I want to select multiple cells at once and apply actions to them all (bulk validate, bulk AI complete), so that I can work faster on repetitive tasks.

How it works. User clicks a cell's selection button (left gutter) or uses pointer drag to multi-select a range. Selection shows a highlight band. Selected cells can be bulk-validated or bulk-completed via toolbar buttons. Selection count shows in the status bar. Esc or clicking clear-selection clears the selection.

WhoReviewer / translation consultant PermissionsValidate/unvalidate: reviewer(300)
Key files

src/components/EditorTable.tsx:864-927 (handleSelectionPointerDown + drag logic)
src/components/EditorTable.tsx:747-795 (selectRangeByIndexes, updateSelectionFromPointer)
src/lib/audio/selection.ts (getSelectedIds, setSelection, toggleSelected)

Cell virtualization — high-performance rendering ▶ 00:00

As a translator, I want the editor to remain responsive even when opening large Bible books with thousands of verses, so that I can work smoothly without lag.

How it works. Cell rows are virtualized using @tanstack/react-virtual. Only visible rows (plus a configurable overscan) are rendered in the DOM. Scrolling is smooth and memory usage stays constant. Row height is estimated (90px by default) but dynamic measurement adjusts the virtualizer on-the-fly when rows grow (e.g., multi-line cells).

WhoTranslator (bilingual contributor) PermissionsRead: viewer(100); edit target cells: contributor(400); edit source cells/reorder: project_lead(500)
Key files

src/components/EditorTable.tsx:2-3 (useVirtualizer import)
src/components/EditorTable.tsx:641-645 (virtualizer setup)
src/components/EditorTable.tsx:1087-1199 (virtual row rendering with measureElement)

Cell navigation — keyboard and arrow navigation ▶ 00:00

As a translator, I want to move between cells using keyboard (arrow keys, Tab) without lifting my hands from the keyboard, so that I can work faster.

How it works. In edit mode, Up/Down arrows navigate to the previous/next cell (only at first/last visual line of multi-line cells). Tab / Shift+Tab always move to the next/previous cell. In grid-focus mode (FRO-297), Up/Down/j/k navigate between rows. Esc in edit mode commits pending edits and returns focus to the grid-row wrapper.

WhoTranslator (bilingual contributor) PermissionsRead: viewer(100); edit target cells: contributor(400); edit source cells/reorder: project_lead(500)
Key files

src/components/TranslatedEditor.tsx:344-410 (cell navigation keydown handler)
src/components/EditorTable.tsx:726-745 (handleNavigateCell + grid-row nav)
src/components/EditorTable.tsx:682-704 (focusCellEditorByIndex)

Audio timings — karaoke-style playback sync ▶ 00:00

As a voice actor, I want to see which word is currently playing during audio playback, so that I can verify timing alignment.

How it works. When audioTimings are present (word-level timing data from ASR or manual alignment), the target editor highlights the active word as playback progresses. Clicking a word with timing performs an alt+click seek to that word's start time via onSeekToTime callback.

WhoTranslator (bilingual contributor) PermissionsRead: viewer(100); edit target cells: contributor(400); edit source cells/reorder: project_lead(500)
Key files

src/components/TranslatedEditor.tsx:97-101 (audioTimings prop + audioCurrentTime)
src/components/TranslatedEditor.tsx:614-621 (karaoke state updates)
src/lib/richtext/karaoke-plugin.ts (word highlighting decoration)

Audio voice assignment and TTS generation ▶ 00:00

As a voice director, I want to assign voices to cells (cast assignment) and generate audio for them, so that I can produce polished translations with voice acting.

How it works. In Audio mode (audioLens prop set), the source column is replaced with CellVoicePanel showing a speaker chip for the assigned voice, a Generate button to produce audio, and a Play button. Drag-and-drop a voice chip onto the audio area to assign it. Generate calls onCompleteSingle and streams audio via frontier. Play queues the cell audio in the shared player. Status badges (Voicing / Audio failed) show progress/errors.

WhoTranslator (bilingual contributor) PermissionsRead: viewer(100); edit target cells: contributor(400); edit source cells/reorder: project_lead(500)
Key files

src/components/EditorTable.tsx:334-357 (AudioLensContext interface)
src/components/EditorTable.tsx:588-596 (displayCells filtering for time-ordered media)
src/components/EditorTable.tsx:127-245 (SynthStatusBadge component)

Audio recording — per-cell manual recording ▶ 00:00

As a voice actor, I want to record my own audio for translations directly in the editor, so that I can capture performances without leaving the app.

How it works. Clicking the mic button opens a recording modal (owned by the parent). The modal records audio, saves it as an attachment, and associates it with the current cell. Mic access is requested; if denied, a help popover explains how to grant access (FRO-237).

WhoTranslator (bilingual contributor) PermissionsRead: viewer(100); edit target cells: contributor(400); edit source cells/reorder: project_lead(500)
Key files

src/components/EditorTable.tsx:434-435 (onOpenRecording callback)
src/components/EditorTable.tsx:2131-2132 (showMicDeniedHelp state)

Alignment panel — word-level source↔target alignment ▶ 00:00

As a linguist, I want to align individual words between source and target, so that I can improve downstream NMT and improve terminology extraction.

How it works. When alignmentModel is provided, the Alignment tab in cell expansion shows source and target words side-by-side, with lines connecting aligned pairs. User can click to create or edit alignments. Confirming fires onAlignmentSeedChange so the parent persists the alignment and rebuilds the model.

WhoTranslator (bilingual contributor) PermissionsRead: viewer(100); edit target cells: contributor(400); edit source cells/reorder: project_lead(500)
Key files

src/components/EditorTable.tsx:451-457 (alignmentModel + onAlignmentSeedChange props)
src/components/InterlinearAlignmentPanel.tsx (alignment UI)

Stale-source indicator — source advance warning ▶ 00:00

As a translator, I want to know when the source text has advanced since my target translation, so that I can review and update my translation accordingly.

How it works. When a cell's source has a newer version (tracked via sourceEventId) than the target's pinned sourceEventId, a small badge appears next to the validation circle, warning that the source is stale. Clicking the badge or hovering shows the hint. The cell is included in the staleCellIds set (computed once per file by the parent).

WhoProject lead (source steward) PermissionsSource content edits: project_lead(500)
Key files

src/components/EditorTable.tsx:1125 (isStaleSource prop)
src/components/EditorTable.tsx:3105-3120 (stale-source badge rendering)
src/hooks/useCells.ts:62-65 (sourceEventId + targetSourceEventId tracking)

Source language display with syntax highlighting ▶ 00:00

As a translator, I want to see the source text clearly with optional markup/formatting preserved, so that I can understand what I'm translating.

How it works. Source column displays the original text, rendered via SourceWithTermLookup (or UsfmSourceText for USFM files). USFM markers are unwrapped for display (\w ..\w* → plain text; \f...\f* → superscript chips). Completion examples are highlighted via HighlightedText. Term lookup popovers appear on matching words when concepts are active.

WhoProject lead (source steward) PermissionsSource content edits: project_lead(500)
Key files

src/components/EditorTable.tsx:1708-1818 (SourceWithTermLookup)
src/components/EditorTable.tsx:2019-2064 (UsfmSourceText for display segmentation)
src/components/EditorTable.tsx:3200-3300 (source column rendering)

Health/decay indicator — quality confidence score ▶ 00:00

As a manager, I want to see a confidence/quality score for each translation, so that I can prioritize review work on weaker translations.

How it works. A colored ring (HealthRing) around the validation circle shows the health score (0–100%) as a visual gauge. Green/amber/red hues indicate quality levels. Hovering shows the numeric score and the confidence reasoning (e.g., 'Good coverage, recent validation'). The decay tab in expansion shows detailed health factors (completion, validation freshness, rule violations).

WhoTranslator (bilingual contributor) PermissionsRead: viewer(100); edit target cells: contributor(400); edit source cells/reorder: project_lead(500)
Key files

src/components/EditorTable.tsx:2552 (health = healthMap.get(cellId))
src/components/EditorTable.tsx:2950-2980 (HealthRing rendering + tooltip)
src/components/HealthRing.tsx (health visualization)

Context display — chapter/verse reference and section labels ▶ 00:00

As a translator, I want to see the passage reference and section context for each cell, so that I can understand the literary structure.

How it works. Cell context shows: canonical reference (e.g., 'GEN 1:1') in the group field, section label (e.g., 'Genesis'), and timing cue context for audio-ordered files (VTT timecode like '00:00:05 --> 00:00:10'). A sticky section indicator at the top of the viewport updates as the user scrolls, showing the current chapter/section (FRO-250). Cell label (ordinal index) is toggled on/off via View Settings.

WhoTranslator (bilingual contributor) PermissionsRead: viewer(100); edit target cells: contributor(400); edit source cells/reorder: project_lead(500)
Key files

src/components/EditorTable.tsx:625-639 (sectionByIndex derivation)
src/components/EditorTable.tsx:975-988 (sticky section label + ref tracking)
src/components/EditorTable.tsx:1045-1055 (sticky section rendering)

Pre-acceptance warnings — terminology consistency ▶ 00:00

As a translator, I want to be warned when my target uses terminology inconsistently with the termbase, so that I can maintain consistent terminology.

How it works. PreAcceptanceWarningBand appears above or below the target editor, showing warnings detected by detectPreAcceptanceWarnings (e.g., 'Key term used inconsistently'). Warnings include optional actions (e.g., 'Apply standard form'). Dismissing the banner hides it for that edit session.

WhoTranslator (bilingual contributor) PermissionsRead: viewer(100); edit target cells: contributor(400); edit source cells/reorder: project_lead(500)
Key files

src/components/EditorTable.tsx:73-74 (detectPreAcceptanceWarnings import)
src/components/EditorTable.tsx:3600-3650 (PreAcceptanceWarningBand integration)

Read-only indicators — role-based and lock-based restrictions ▶ 00:00

As a viewer-role user, I want to understand why I cannot edit cells, so that I can request edit access appropriately.

How it works. When user role is viewer/commenter/reviewer (FRO-273), a read-only badge appears at the top of the editor indicating their role and constraints. The target editor is disabled (contenteditable=false). When another user holds the focus lock on a cell, an 'Alice is editing' banner appears inside the editor and the cell is read-only until they release the lock.

WhoTranslator (bilingual contributor) PermissionsRead: viewer(100); edit target cells: contributor(400); edit source cells/reorder: project_lead(500)
Key files

src/components/EditorTable.tsx:1057-1062 (role badge rendering)
src/components/EditorTable.tsx:522 (useEditorCapabilities for role check)
src/components/TranslatedEditor.tsx:216, 652-658 (editor read-only state + lock banner)

Remote change detection and reconciliation ▶ 00:00

As a translator, I want to know when another user edits my cell while I'm editing it, so that I can decide whether to discard my work or keep my version.

How it works. When a remote event.applied arrives for a cell while the user is focused on it (has the lock), a banner appears: 'This cell changed elsewhere while you were editing.' The user can click 'Discard and reload' to abandon local edits and reload the server value, or keep editing and commit their local work (which will race or override depending on event ordering).

WhoTranslator (bilingual contributor) PermissionsRead: viewer(100); edit target cells: contributor(400); edit source cells/reorder: project_lead(500)
Key files

src/components/EditorTable.tsx:390-391 (cellsWithRemoteChange prop)
src/components/TranslatedEditor.tsx:107-108, 660-670 (remote-change banner + onDiscardLocal)

Font size preferences — per-file source and target sizing ▶ 00:00

As a user with vision needs, I want to adjust the text size in each column independently and per-file, so that I can read comfortably.

How it works. View Settings menu (eye icon) has source/target font-size sliders (e.g., 10–20px). Selections are persisted in localStorage keyed by fileId. Source and target columns inherit the pref as an inline font-size style. The pref survives file switches (per-file, not global).

WhoTranslator (bilingual contributor) PermissionsRead: viewer(100); edit target cells: contributor(400); edit source cells/reorder: project_lead(500)
Key files

src/components/EditorTable.tsx:619-620 (useFileFontSizes hook)
src/components/EditorTable.tsx:1183-1184 (sourceFontSize + targetFontSize props)
src/components/EditorTable.tsx:3180-3210 (font-size inline styles)

Assignment indicator — cell workload tracking ▶ 00:00

As a manager, I want to see which team member is responsible for each cell, so that I can track workload distribution.

How it works. When a cell has an active assignment (FRO-192), a small badge appears showing the assignee's name and an optional scope label (e.g., 'Alice (Chapter 1)'). Hovering shows the full assignment details. Assigning cells is done externally (via the project workload view).

WhoTranslator (bilingual contributor) PermissionsRead: viewer(100); edit target cells: contributor(400); edit source cells/reorder: project_lead(500)
Key files

src/components/EditorTable.tsx:461, 1185-1186 (assignmentsByCellId prop + rendering)
src/components/EditorTable.tsx:3900-3950 (assignee badge rendering)

Footnote inline rendering and management — USFM editorial ▶ 00:00

As a Bible translator, I want to see footnotes rendered inline below each cell (when toggled on) and edit them directly, so that I can manage USFM notes without leaving the editor.

How it works. When showFootnotesInline=true, source and target footnotes extracted from cells render as a panel below the cell row, showing numbered markers and full text. User can click to edit or delete footnotes, insert new ones via dialog, or view full footnote content. Footnotes are persisted as part of the cell value (in USFM format).

WhoTranslator (bilingual contributor) PermissionsRead: viewer(100); edit target cells: contributor(400); edit source cells/reorder: project_lead(500)
Key files

src/components/EditorTable.tsx:2154-2173 (footnote extraction and display prep)
src/components/EditorTable.tsx:3600-3700 (inline footnote panel rendering)
src/components/footnotes/FootnoteInline.tsx (footnote display)