immigration.fyiGenogram Generator

Casework

Asylum Interview ReviewerAuditorBundle BuilderCW Form CompletionCase Law FinderCase SummaryChronology DrafterCompilerCountry ScheduleDate ConversionsDocument CompareDocument NotepadEvidence ExhibitorEvidence MatrixFilerGenogram GeneratorGov.UK Archive CheckImage AnnotatorPassage PickerResearch

Genogram Generator

What this is

Genogram Generator draws a client's family / safeguarding diagram following the social-work genogram convention and exports it for tribunal bundles, Local Authority referrals, statements, or letters. Matter-agnostic, asylum family composition, settlement sponsor chain, child-safeguarding all use the same tool.

Typical workflow

  1. On the Build tab, either paste the family into Build from written text and press Build the genogram, or add People one by one. Mark exactly one Person as Client.
  2. Set each person's Sex (square = M, circle = F, D-shape = NB, ? = unknown), Status (alive, deceased, unborn, abortion, miscarriage, stillborn), and any Key codes (LD, PD, MH…). Trans / gay / lesbian render the nested or inverted-triangle insets automatically.
  3. Add Relationships. Couple covers married / cohabiting / divorced / separated / widowed (pick the kind). Parent Child covers biological / adopted / foster (pick the kind). Overlay draws the abuse / closeness lines on top of the structural diagram, pick the kind (violence / physical / emotional / sexual / hostile / neglect / very-close / close / distant) and the direction (perpetrator victim).
  4. Open Diagram: the genogram renders top-down with the client centred, oldest generation at the top, oldest sibling on the left, male on the left within each couple. Mouse-wheel zooms, click-drag pans, double-click resets.
  5. Reopen a previous genogram at any time with Import sidecar or PDF on the Build tab: either the .genogram.json sidecar, or a PDF this tool exported with its data embedded.
  6. Open Export for PNG, A4 PDF (with PRIVATE & CONFIDENTIAL header + optional DRAFT watermark), JSON sidecar (drops into ~/Work/00 Open Cases/Case Summaries/ as <ref>.genogram.json), or a plain-prose paragraph for a witness statement. PNG and PDF both carry a key to the symbols this genogram uses.

Building it from written text

The panel at the top of the Build tab reads two shapes, mixed freely, and builds every person and line from them.

  • Sentences, the same shape the Export tab writes: Melania Trump is Donald Trump's wife, born 1970 in Manchester, UK. The client is whoever the sentences are about. Sentences carry sex, dates of birth and death, and places. was instead of is means the person has died.
  • Relationship rows, the same shape the Relationships list prints: Donald Trump ⟢ adopted parent of ⟢ Barron Trump, Donald Trump ⟷ divorced ⟷ Ivana Trump with start 1977 Β· end 1992 on the line below, Donald Trump hostile Ivana Trump. Rows connect any two people, not only the client, and carry the exact edge kind, which a sentence cannot.

Both round trip: what the tool prints, it can read back. Paste both and they merge on the name, so the paragraph in the witness statement plus the relationship list rebuilds the whole diagram.

Sex is never taken from a name. It comes from the relationship word (mother female), so anyone reached only through a row draws as a dashed square until you set it. The one exception is a bare placeholder name like Mother or Unknown Father, which is a role word standing in for a name; the report lists every one it read that way.

The report under the panel says what was built, what was inferred (a placeholder parent added so an aunt has a side to hang from, two children sharing a date of birth drawn as twins), and every line that was not used. Nothing is dropped in silence. Building replaces what is already entered, so download the sidecar JSON first if you want to keep it.

Conventions reference

The full social-work convention: every shape, line variant, overlay kind, and key code, is embedded below. It is the distilled version of the published Genogram Explanation PDF (07 October 2022) used by Local Authority safeguarding teams. Edit at gmiau-specs/GENOGRAM-GUIDE.md; immigrationfyi-tools update refreshes the embedding.

Show convention reference (GENOGRAM-GUIDE.md)
# Genogram Convention: Reference

Distilled from "Genogram Explanation" (07/10/2022). Source PDF at
`gmiau-specs/source-docs/genogram-guide-07102022.pdf`. This is the
social-work / safeguarding convention; it is what colleagues and the
Local Authority will expect when a genogram is filed with a case.

A **genogram** (a structured family tree) shows current family
relationships across **at least three generations**: it is intended to
make clear who is in the family, who lives with the child, and what the
significant medical, health and emotional dynamics are.

Only **family members** belong on a genogram. Friends, professionals,
and supporters belong on an *ecomap*, not here.

Each person should carry at minimum: **name** and **date of birth (not
age)**. Other dates, marriage, divorce / separation, death: are added
where known. Significant life events may be noted next to the person.

---

## 1. Layout rules

- Orientation: **landscape**.
- Within each couple: **male on the LEFT, female on the RIGHT**, joined
 by a horizontal line.
- Children: drawn **below** the parental line, **oldest on the LEFT,
 youngest on the RIGHT**.
- Generations stack vertically: **oldest generation at the TOP, youngest
 at the BOTTOM**. Typically four tiers, great-grandparents,
 grandparents, parents (and parents' siblings), child(ren).

---

## 2. Person symbols

```
 
 Male Female
 
 
 
 Non-binary Unborn baby
 (D-shape) ____ (triangle; show EDD)
 
 
 Trans FM Trans MF
 
 (square outer, (circle outer,
 circle inner) square inner)
 
 
 Lesbian Gay
 
 (circle with (square with
 inverted triangle) inverted triangle)
```

- **Deceased**: large **X** across the symbol.
- **Abortion**: triangle with an **X** through it.
- **Miscarriage**: triangle with an **X** through it (drawn smaller /
 with a single stroke variation; see PDF p.4).
- **Stillborn**: an X-crossed shape drawn smaller than a live-born
 symbol (square for male, circle for female).
- **Pet**: **diamond** with the pet's name beside it. (Pets are
 included so households are accurately represented.)

---

## 3. Couple-line variants

The horizontal line between two partners encodes the relationship
status. Date labels go **on or just under** the line.

```
 Married. Label: m.MM.YYYY
 (or m.DD.MM.YYYY)

 / Separated, legal procedure for
 divorce has started.

 Γ— Separated, no longer living
 together.

 // Divorced. Label: m.YYYY d.YYYY

 X One partner died during the
 marriage. (Different from the
 "no longer living together" X, this is on the line, the
 deceased's symbol carries the
 across-shape X too.)

 – – – – – – – – Cohabiting / civil partnership.
 Label: start date.

 – – – – / – – – Cohabiting couple separated
 (living apart).

 – – – – / – – – 2018–2021 Civil-partnership couple
 (with dates) or officially separated; both start
 01.02.18 and end dates labelled.
 – 25.06.21
```

---

## 4. Parent–child line variants

The vertical line(s) from the parental line down to each child encode
the legal / biological tie.

```
 Biological child, solid line.

 + dashes Adopted child: a solid line and a dashed line
 together (one represents biological origin, one
 represents adoptive parents).

 Foster child, dashed line.
```

Twins / multiples share a single descender that fans out at the bottom:

```
 
 
 
 
 
 non-identical identical triplets
 twins twins (a fan of 3+)
 (horizontal bar
 between the
 twins)
```

---

## 5. Household boundary

A **free-form loop drawn around** a group of people indicates who is
**living in the same household**. The loop may include half-siblings,
step-children, pets, whoever sleeps under the same roof. There is no
formal shape; it just encloses the relevant symbols.

---

## 6. Key information (next to each person)

These abbreviations are drawn **next to the person's symbol** (not
inside it) to flag significant health / risk / status information. The
PDF stresses this list is a **guide only**, not exhaustive.

```
 LD Learning Disability
 PD Physical Disability
 SD Sensory Disability
 MH Mental Health
 AM Alcohol Misuse
 SM Substance Misuse
 D Depression
 P In Prison
 CAR Child At Risk
 SI Severe Illness
 LI Lifelong Illness
 SA Sexual Abuse
 PA Physical Abuse
 A Anxiety (or: experience of being Abused as a Child)
 EA Emotional Abuse
```

Multiple codes are separated by spaces / commas, e.g. `LD MH D`.

---

## 7. Emotional-relationship overlay

These are drawn as **arrows** or as **plain lines** between two
people's symbols, *in addition* to the structural family lines. Arrows
point **from the perpetrator to the victim**.

### Abuse / harm (coloured arrows)

```
 v v v v v v Violence (red, wavy + arrow)
 w w w w w w Physical Abuse (blue, wavy + arrow)
 w w w w w w Emotional Abuse (light green, wavy + arrow)
 w w w w w w Sexual Abuse (purple, DOUBLE wavy + arrow)
 w w w w w w Hostile relationship (light blue, wavy + arrow)
 – – – – – – Neglect (black, DASHED arrow)
```

A **double-headed arrow** ( ) between two people indicates **abuse
in both directions** (mutual).

### Closeness / quality (no arrowheads, plain lines between symbols)

```
 Very close relationship (double line)
 Close relationship (single line)
 – – – – – – – – Distant / poor (dashed line)
```

---

## 8. Examples (from the PDF)

The PDF includes three worked example genograms (PDF pp.10–14). They
illustrate, in combination:

- a four-generation tree with X-crossed deceased grandparents,
 multiple marriages with `m.YYYY d.YYYY` labels, biological + adopted
 + foster children, identical twins, a household boundary loop, plus
 the violence / hostile / sexual-abuse arrows;
- couples shown with cohabiting (dashed) and married (solid) lines,
 including separated-then-cohabiting transitions;
- pets shown as diamonds inside the household loop;
- key-info codes (LD, P, CAR, AM) rendered next to the relevant
 person's symbol.

The genogram is a snapshot, it documents the **current** family as
known to the worker, with as much accuracy as can reasonably be
obtained. Information that is unknown is shown with `?` (e.g. `b.?` or
`m.19??`); it is not fabricated.

Tips & gotchas

  • Mark one Client. Diagram + prose are anchored on the Client. Without a Client the diagram won't render.
  • Relationships drive the diagram, not roles. Setting role = "parent" does not auto-connect to the Client. Add a Parent Child relationship for the line to appear.
  • Siblings. Two people with at least one common Parent edge are siblings automatically. Use the Sibling relationship type only when no common Parent has been entered.
  • Twins / triplets. Use the Multiple-birth group field on each twin / triplet: siblings sharing a group ID get a fan-of-N descender. Pick Identical to add the horizontal bar.
  • Deceased / abortion / miscarriage / stillborn. Set Status: the appropriate X-crossed shape draws automatically. Prose uses "was".
  • Pet. Set role = Pet: renders as a diamond (no body fields needed beyond Name).
  • Storage. Build state autosaves to localStorage; reopening this file restores it. Use Download JSON for a persistent sidecar.
  • Importing a v1 Family Tree sidecar. The tool will accept it and fill missing v2 fields with defaults, revisit Sex / Status / kind on each row afterwards.
  • Two current partners. Text saying someone has two husbands is taken at its word and both couple lines are drawn, which puts the two spouses side by side rather than either side of the client. The report flags it: if one relationship has ended, set it to divorced or separated and the line redraws correctly.
  • The key on the export. It lists only the symbols this genogram actually uses, so it grows and shrinks with the diagram, and it cites the convention it follows. It shares a sheet only when that costs the diagram nothing; normally it takes its own page, scaled up to fill it. Turn it off in Config.
  • Legibility comes before page count. Names never print below 8 pt: a genogram too big for one sheet at that size is tiled across sheets rather than shrunk. Nothing is allowed to rule through a label either, so an overlay arrow crossing a name passes behind it.
  • Reopening a PDF. A PDF exported by this tool carries the whole genogram inside it as an attachment, so Import sidecar or PDF reopens it. A PDF made before 29 August 2026, or exported with embedding switched off, is only a picture: the tool will say so and offer the client name and case ref, which is all such a file contains.
  • What travels with the file. The embedded copy is the whole record, including notes, key codes and the abuse / closeness edges, whether or not the diagram draws them. For an exhibit leaving the office, either encrypt it or set Genogram data inside the PDF to Leave out in Config. A lost passphrase cannot be recovered.

Confidentiality: runs entirely in your browser, nothing is uploaded. The sidecar JSON is plaintext; store it under the case's Case Summaries/ folder, same as every other case sidecar.

Build from written text

Paste a paragraph, a list of relationships, or both, and the genogram is built from it. Everyone and every line is created for you; nothing is guessed silently, and the report below says what was read and what was not.

Two shapes are understood, mixed freely, one per line or flowing in a paragraph.
Sentences, exactly what the Export tab writes: Melania Trump is Donald Trump's wife, born 1970 in Manchester, UK. Β· Barron Trump is Donald Trump's son, born 2006-07-28. Β· Fred Trump was Donald Trump's father, born 1905 and died 1999.
Relationship rows, exactly what the list on the right prints: Donald Trump ⟢ biological parent of ⟢ Barron Trump · Donald Trump ⟷ divorced ⟷ Ivana Trump then start 1977 · end 1992 on the next line · Donald Trump hostile Ivana Trump.
Sentences carry sex, dates and places; rows carry any pair of people and the exact edge kind. Sex is never taken from a name.

People

Mark exactly one person as Client. Add everyone the genogram needs.

Relationships

Each row connects two people. Couple = horizontal line; Parent Child = vertical line; Overlay = on-top abuse / closeness arrow.

Wheel = zoom Β· Drag = pan Β· Double-click = reset
No genogram yet. On the Build tab, add at least one person and mark them as Client.

Image & PDF

PNG renders the current genogram at print resolution; PDF wraps it on A4 with the canonical PRIVATE & CONFIDENTIAL header, a footer band (case ref Β· date Β· page), and an optional DRAFT watermark (toggle in Config). Both carry a key to the symbols used in this genogram: on the sheet where it fits, on its own final page where it does not. Turn it off in Config.

PNG:
PDF: (same filename for either orientation)

Sidecar JSON

Downloads <ref>.genogram.json for the case's Case Summaries/ folder. The case-summary file itself is never touched. v1 Family Tree sidecars (<ref>.family-tree.json) can be imported.

Filename:

Plain-prose paragraph

A single paragraph walking the family outwards from the client. Drop into a witness statement or covering letter; edit as needed. Overlay edges (abuse / closeness) are deliberately NOT emitted, they're safeguarding metadata, not biographical fact. This paragraph pastes back into Build from written text on the Build tab and rebuilds the same diagram, so the family can live in the statement rather than in a sidecar.

Backlog (per GENOGRAM-SPEC Β§11)

Household-boundary freeform loop Β· distinct step-parent dashed-double line Β· EB Garamond inline Β· qpdf-wasm AES-256 encryption Β· auto-import from gmiau-case-summary/1. The PDF here is unencrypted; encrypt downstream via pdf-encrypt.html if needed.

Render mode

Simple: rounded rectangles for everyone, plain solid couple / parent lines, deceased shown with a single diagonal slash, no key codes, no abuse / closeness overlay. Filenames say Family Tree. Genogram: the published social-work convention: shapes by sex / trans / orientation, X-across deceased, key codes, abuse / closeness overlay arrows. Filenames say Genogram. The data model is the same in both modes: switching never loses what you've entered.

Diagram options

Under the name is the layout in the social-work guide: name, then dates, then the place, in a line under the symbol. Above the symbol follows the Standard Genogram Format instead and sets the place apart from the name, in its own colour above the symbol and to the right of centre, with any key codes to the left. Route around the text takes the couple and parent lines below and around each label block, so no line crosses a name, a date or a place of birth; Straight through is the older behaviour, which ran the line across and relied on the white halo behind the text.

PNG and PDF export

The key explains only the symbols this genogram actually uses. In the PNG it sits under the diagram; in the PDF it shares the sheet when there is room and takes its own final page when there is not.

Adds the plain-prose paragraph from the ↓ Export tab as its own page after the diagram, set as real text at 11 pt so it can be selected, searched and copied out of the bundle rather than read off a picture. It runs to more than one page if the family needs it.

Embedding attaches the full genogram to the PDF as a file, so the PDF reopens in this tool. It is the whole record: notes, key codes and the abuse / closeness overlay edges, whether or not the diagram draws them. Anyone holding the file can extract it. For an exhibit that leaves the office, either encrypt it (AES-256, passphrase on the ↓ Export tab) or set this to Leave out. A passphrase cannot be recovered: lose it and the data in that PDF is gone.

Orientation is chosen at export time via the two PDF buttons on the Export tab. Filenames follow EXPORT-SPEC.md Β§1: YYMMDD CLIENTREF SEQ Genogram.{pdf,png}. The sidecar JSON uses <ref>.genogram.json. Both formats are unencrypted: pipe through pdf-encrypt.html if AES-256 is needed.

App font

App appearance

Tab visibility

Syncs across every GMIAU Shell tool via ifyi_hide_guide_tab + ifyi_hide_config_tab.

Tool spec (reference)

The current GENOGRAM-SPEC.md is inlined at build time. Edit the spec at gmiau-specs/GENOGRAM-SPEC.md and run immigrationfyi-tools update to refresh.

Show GENOGRAM-SPEC.md
# Genogram Generator: Tool Spec

**Version 2.3 Β· 2026-08-29**

`genogram-generator.html` is a GMIAU Shell tool for drawing a client's
**genogram**, a family / relationship diagram following the
social-work convention summarised in `GENOGRAM-GUIDE.md`. The output is
intended to drop into a tribunal bundle exhibit, into a safeguarding
referral, or into a witness statement (as a paragraph of plain prose).

This tool **supersedes the Family Tree tool** that shipped on
2026-05-14. The Family Tree tool used rectangles for every person and
collapsed all couple relationships into one line type; Genogram
Generator follows the published social-work convention (different
shapes by sex, distinct couple-line variants, parent-child line types,
key-info codes, abuse / closeness overlay).

Cross-referenced specs:

- `gmiau-specs/GENOGRAM-GUIDE.md`, the social-work convention itself
 (distilled from the published Genogram Explanation PDF). Embedded
 verbatim in the Guide tab via `@@SPEC:GENOGRAM-GUIDE@@`.
- `gmiau-specs/GMIAU-STYLE-GUIDE.md`, shell scaffold, tabs, settings,
 themed scrollbars, panel width (Β§4A), Guide-tab visibility (Β§2C).
- `gmiau-specs/PDF-OUTPUT-SPEC.md`, A4 margins, fonts, page setup.
- `gmiau-specs/GMIAU-ICONS-SPEC.md`, icon design.
- `gmiau-specs/EXPORT-SPEC.md`: export filename convention.

Source PDF: `gmiau-specs/source-docs/genogram-guide-07102022.pdf`.

---

## 1. Purpose

One tool, one diagram. The caseworker enters every relevant person
plus each pairwise relationship; the tool lays them out generationally
(top-down) and exports:

1. **A4 PDF** (landscape default) for a bundle exhibit, with the
 canonical PDF protection pattern (PRIVATE & CONFIDENTIAL header,
 optional DRAFT watermark), see Β§5.
2. **PNG** at 2Γ— device pixels, white background, see Β§5.1.
3. **Interactive HTML SVG** in the Diagram tab (pan / zoom).
4. **Sidecar JSON** at
 `<open_cases>/Case Summaries/<ref>.genogram.json`, see Β§6.
5. **Plain-prose paragraph** for pasting into a witness statement or
 letter, see Β§7. It reads back in: Β§7.2.

The PNG and the PDF both carry a **key to the symbols the genogram
uses**, see Β§5.2. The PDF also carries the genogram **data** (Β§5.3),
optionally encrypted, so it can be reopened; and optionally the prose
paragraph as text pages (Β§5.4).

Entry is by hand or by pasting written text (Β§7.2). No GEDCOM, no
auto-import from case summary, no household boundary, no radial layout
(the v1.2 radial-layout stub is removed, top-down is the only laid-out
form and matches the convention).

Anything in `GENOGRAM-GUIDE.md` that is **not** implemented here is
listed in Β§11 as backlog.

---

## 2. Tabs

Per `GMIAU-STYLE-GUIDE.md` Β§2A (tab-btn style), Β§2C (Guide / Config
hideable toggles). Minimum-5 ordering. Primary tool tab
(`build`) is default-active per the 2026-05-13 reversal
(`feedback_guide_default_flip`).

| Order | Tab | id | Default-active | Hideable |
|-------|---------------|--------------|----------------|----------|
| 1 | Guide | `guide` | no | yes |
| 2 | Build | `build` | **yes** | no |
| 3 | Diagram | `diagram` | no | no |
| 4 | Export | `export` | no | no |
| 5 | Config | `config` | no | yes |
| 6 | Settings | `settings` | no | no |
| 7 | Index | (link) | n/a | no |

`window.IFYI_PRIMARY_TAB = 'build'`.

The **Guide tab** holds: a hand-written intro to using the tool, then a
`<details>`-wrapped `@@SPEC:GENOGRAM-GUIDE@@` block holding the
inlined social-work convention.

The **Settings tab** holds the canonical 6 toggles (Mode, Theme,
App-font Family, App-font Size, Hide Guide, Hide Config) plus a
`<details>`-wrapped `@@SPEC:GENOGRAM-SPEC@@` block holding this
spec.

---

## 3. Data model

### 3.1 Person

```ts
type Person = {
 id: string; // 'p1', 'p2', … (stable across a session)
 role: 'client' // exactly one Person should have role='client'
 | 'spouse'
 | 'parent'
 | 'child'
 | 'sibling'
 | 'grandparent'
 | 'maternal-aunt' | 'paternal-aunt'
 | 'maternal-uncle' | 'paternal-uncle'
 | 'cousin'
 | 'in-law'
 | 'pet'
 | 'other';
 name: string;

 // Symbol attributes
 sex: 'M' | 'F' | 'NB' | '?'; // displayed shape (square/circle/D/?)
 trans?: 'F-to-M' | 'M-to-F'; // renders nested inner shape
 orientation?: 'gay' | 'lesbian'; // renders inverted-triangle inset

 // Life status
 status: 'alive' | 'deceased' | 'unborn' | 'abortion' | 'miscarriage'
 | 'stillborn' | 'unknown';
 dob?: string; // 'YYYY-MM-DD' | 'YYYY' | '', never required
 dod?: string; // date of death (only if status='deceased')
 edd?: string; // expected delivery date (only if status='unborn')

 // Optional context
 location?: string; // 'Kabul, Afghanistan' / 'UK' / etc.
 keyCodes?: string; // free-text, space-or-comma separated: 'LD MH D'
 // rendered next to (not inside) the symbol. See Β§3.4.
 notes?: string; // free text, risk profile, witness availability, etc.

 // Multiple births
 multipleId?: string; // shared group id ('m1', 'm2', …), siblings with
 // the same multipleId render as a fan-of-N descender
 multipleKind?: 'identical' | 'fraternal'; // identical adds a horizontal bar
};
```

The `role` field is **descriptive labelling only**. Graph structure
comes from the Relationships list. Pets render as a small diamond per
the convention; everything else uses the Β§3.3 shape table.

### 3.2 Relationship

```ts
type Relationship =
 | { type: 'couple'; a: PersonId; b: PersonId;
 kind: 'married' | 'separated-legal' | 'separated-living-apart'
 | 'divorced' | 'widowed' | 'cohabiting'
 | 'cohabiting-separated' | 'civil-separated';
 startDate?: string; // m.YYYY or m.DD.MM.YYYY for married/cohabiting
 endDate?: string; // d.YYYY for divorced/widowed
 }
 | { type: 'parent'; parent: PersonId; child: PersonId;
 kind: 'biological' | 'adopted' | 'foster';
 }
 | { type: 'sibling'; a: PersonId; b: PersonId; } // shorthand, see Β§3.5
 | { type: 'overlay'; a: PersonId; b: PersonId;
 kind: 'violence' | 'physical-abuse' | 'emotional-abuse'
 | 'sexual-abuse' | 'hostile' | 'neglect'
 | 'very-close' | 'close' | 'distant';
 direction: 'a-to-b' | 'b-to-a' | 'both' | 'none';
 // direction='none' is the only valid choice for very-close / close /
 // distant (plain lines, no arrowhead). For the abuse + neglect
 // kinds, direction must be 'a-to-b', 'b-to-a', or 'both'.
 };
```

Note the v1.2 v2.0 changes:

- `spouse` + `partner` a single `couple` with a `kind` field.
- `adoptive-parent` / `step-parent` a single `parent` with a `kind`
 field (`biological` / `adopted` / `foster`). "Step-parent" is not in
 the social-work convention as a distinct line type; render a
 step-parent as a `couple` edge plus a `parent` edge of kind
 `adopted` where the step-relationship is legally formalised, else
 leave the parent edge absent. (Backlog item: explicit step-parent
 dashed-double-line.)
- New `overlay` edge type carries the abuse / closeness lines that
 sit on top of the structural diagram.

### 3.3 Shape table (rendered)

| sex | trans | orientation | Renders |
|-----|------------|-------------|--------------------------------------------------|
| M |, |: | Square |
| F |: |: | Circle (oval) |
| NB |: |: | D-shape (Word's "Flowchart: Delay") |
| ? |: |: | A question mark inside a dashed square |
| M |, | gay | Square with inverted-triangle () inset |
| F |, | lesbian | Circle with inverted-triangle () inset |
| M | `F-to-M` |, | Square outer, circle inner (born-female) |
| F | `M-to-F` |, | Circle outer, square inner (born-male) |

A `status='unborn'` symbol is **always** a triangle regardless of sex.
A `status='deceased'` symbol carries a large `X` across whatever shape
the Β§3.3 table would otherwise draw. `status='abortion'` and
`status='miscarriage'` render an X-through small triangle.
`status='stillborn'` renders an X-through small square (M) or oval (F).

### 3.4 keyCodes

A free-text field. The tool does not validate against the
GUIDE Β§6 list, caseworkers should follow it but the convention is
self-documenting and stable extensions (e.g. `ASD`) are routine.
Rendered as a short line of text **above** the person's symbol, in the
shell's muted text colour. Comma-separated codes are accepted but
displayed verbatim.

### 3.5 Sibling shorthand

`{ type: 'sibling', a, b }` is shorthand for "share at least one
parent" when the user hasn't entered grandparent rows. The diagram
engine treats two people with at least one common `parent` edge as
siblings automatically, sibling rows are only needed when no common
parent has been entered.

### 3.6 Sidecar JSON shape

```json
{
 "_v": 2,
 "_tool": "genogram-generator",
 "_exportedAt": "2026-05-15T14:21:00.000Z",
 "case_ref": "RB12345",
 "people": [ { "id": "p1", "role": "client", "name": "…", … } ],
 "relationships": [ { "type": "couple", "a": "p1", "b": "p2", … }, … ],
 "options": {
 "show_dob": true,
 "show_location": true,
 "show_status": true,
 "show_keycodes": true,
 "show_overlay": true
 }
}
```

The `_v: 2` bump distinguishes Genogram sidecars from v1 Family Tree
sidecars (which had `_tool: "family-tree"`). v1 sidecars are not
auto-migrated in v2.0, the import will accept them but warn that
fields are missing.

---

## 4. Diagram engine

Top-down only. The v1.2 radial layout is removed.

### 4.1 Tier assignment

BFS from the client (tier 0). Each `parent` edge moves +1 toward the
parent (older), each `parent` edge from the other side moves βˆ’1
(younger). Couple partners share a tier.

### 4.2 Within-tier ordering

1. **Couples sit adjacent**: the male symbol is on the LEFT, the
 female symbol on the RIGHT, joined by a horizontal couple-line.
 Non-binary partners take the position dictated by the partner's sex
 (NB M NB on the right; NB F NB on the left; NB NB 
 insertion order).
2. **Siblings cluster** and are sorted **DOB-ascending, left-to-right**
 (oldest on the LEFT). Siblings without a DOB hold their insertion
 order at the right end of the cluster.
3. **Stable order** otherwise, preserve insertion order when
 underdetermined.

### 4.2a Sub-row wrapping (large families)

A tier only ever grows sideways, so a wide generation is fitted
width-first on A4 and most of the page height goes unused, which is
what made names in a large family unreadable. A tier is therefore
allowed to wrap into **stacked sub-rows**, spending that height to buy
back scale.

Wrapping is automatic and conservative:

- It is attempted **only** when the whole diagram would otherwise print
 below **9 pt** on a single A4 sheet. At or above that, the strict
 one-row-per-generation layout of Β§4.1 is kept untouched.
- Candidate sub-row widths are tried widest-first, and a narrower split
 must beat the incumbent by **more than 3%** (and the unwrapped layout
 by more than 5%) to be adopted. The layout closest to the convention
 therefore wins ties.
- A **couple is never split** across sub-rows: partners travel as one
 unit.
- A sibling group too wide for one sub-row is given **exclusive**
 sub-rows, which keeps the margin to its left clear.
- The parent bus (Β§4.3) then draws **one bus per sub-row**, the lower
 ones fed by a vertical drop-line running down that clear left margin.

Worked example: 14 siblings on a 92-unit pitch is 1260 units wide and
prints names at 5.6 pt. Wrapped to 2 sub-rows of 7 it is 616 Γ— 369 and
prints at 10.8 pt.

### 4.3 Edges

- **Couple line**: horizontal, between the two symbols. The Β§4.6 table
 governs stroke style (solid / dashed) and stroke decorations
 (single slash, X, double slash, etc.).
- **Parent–child line**: vertical line down from the midpoint of the
 couple-line (or from a single parent's symbol) to a horizontal
 *sibling bus*; from the bus, one vertical descender per child. The
 Β§4.7 table governs stroke style (solid / dashed / solid+dashed).
- **Twins**: a single descender that fans out into N legs at the
 bottom. `multipleKind='identical'` adds a horizontal bar joining
 the legs.
- **Overlay edges**: drawn ON TOP of the structural diagram, after all
 structural lines, with their own per-kind colour and dash pattern.
 Overlay edges may bend around symbols (route via simple orthogonal
 segments), straight lines are acceptable if no symbol is on the
 direct path.

### 4.4 Symbol sizing and label fitting

Each symbol is 64 Γ— 44 SVG units, on a column pitch of 92 (`BOX_W +
H_GAP`). Labels:

- **Name** on line 1 (under the symbol).
- **`b.YYYY-MM-DD`** on line 2 (replaces line 2 with `b.?` if absent),
 with `d.YYYY-MM-DD` appended when deceased.
- **Location** on line 3 (hidden when `options.show_location=false`).
- **keyCodes** is rendered ABOVE the symbol, line 0, when present.

Every label is **measured and wrapped to 86 units** (the column pitch
less a 6-unit gutter), never cut at a fixed character count, a
character count cannot know how wide the text is, so it both truncated
readable names and let long ones overlap the next column. Widths are
measured against the export font (Helvetica / Arial) so screen and PDF
agree.

Those sizes are **SVG user units, not UI type**, and every one of them is
set in exactly one place: `LBL_SPEC`. Each label is drawn with an inline
`font-size` taken straight from the spec it was measured against, so no
stylesheet can move a label away from the width the fitter reserved for
it. The diagram CSS and `_EXPORT_SVG_CSS` are additionally wrapped in
`/* ifyi:fixed-px */ … /* ifyi:/fixed-px */`, which tells `build-www.py`
to leave them out of its px-to-`var(--app-font-size)` rescale.

Without the fence the built site rescaled the location line from 8 to
`calc(var(--app-font-size) * 0.53)`: 10.6px on screen at the default base
of 20, and 16px in the exported SVG, where `--app-font-size` is not
defined at all and the `calc()` collapses to the inherited default. The
place of birth then printed larger than the person's name and ran over
the next column. Anything the tool both measures and prints belongs
inside the fence.

- Name wraps to at most **2 lines**, breaking on spaces.
- The `b.` / `d.` pair sits on one line when it fits, otherwise one
 date per line. A date is never truncated.
- Location wraps to at most 2 lines; keyCodes stay on one.
- Only a single unbreakable word longer than the column is ellipsized.
- Row height is derived from the tallest label block in that row, so
 rows are exactly as tall as their content needs.

Couple-line labels sit in the 28-unit gap between the two symbols, so
they are stacked one part per line and shrunk (8 px down to a 6 px
floor) to fit that gap rather than run across both symbols. They are
year-level (`m.YYYY`, `m.YYYY d.YYYY`), a full ISO date cannot be set
legibly in that gap, and the exact day is carried by the Prose tab.

### 4.4a Structural lines are routed around the text

A halo behind a label (Β§4.6b) stops a crossing line from striking a glyph out,
but on a document that goes to a tribunal a line through a place of birth still
reads as a mistake. The couple and parent-child lines are therefore routed
around the label blocks rather than over them. There is a 28-unit gutter
between columns and labels are capped at 86 on a 92 pitch, so a vertical always
has somewhere to go.

- Every label is measured into a band: the block under the symbol, and the line
 above it. `_labelBands()` collects them; `_vClearX()` tests a vertical
 against them.
- A family's bus sits **below** the parents' whole label block, not at the
 bottom edge of their symbols, so the horizontal cannot cross a name, a date
 or a place of birth.
- The stub from the parents down to that bus runs straight when its x is clear.
 A couple's is the midpoint between two adjacent symbols, which is the gutter,
 so it always is. A single parent's is the centre of their own label block, so
 it is not: the stub leaves along the bottom edge of the symbol, which is above
 all text, into whichever gutter is nearer the children, and descends there.
- Sub-row and sibling drop lines run down the gutter centre (`H_GAP / 2` left of
 the leftmost box) and step a further column left if that is occupied. A
 sibling bus sits above whatever the row carries above its boxes, not at a
 fixed 14 units, which a two-line place name grew past.
- What sits above a symbol is held off its centre: key codes end 3 units left of
 it, the place of birth (when set above) starts 3 units right. Each side may
 use half the label width plus half the column gap (`SIDE_W`), and the place
 is allowed two lines, stacked upward. They are measured as **two** bands, one
 per side, never one union: a single band would cover the centre, which is the
 whole point of holding them apart. That empty centre is what lets a descender
 reach the top of the box without crossing a word, and is why the descender now
 meets the symbol instead of stopping short above the key codes.

Overlay edges (Β§4.8) are **not** routed. They are point-to-point marks about a
relationship rather than lines joining family members, and they keep the halo.

`Straight through` in Config restores the older behaviour in full: no bands are
built, the bus returns to the bottom edge of the symbols, and every line is
drawn direct.

### 4.4b Where the place of birth goes

Two positions, both defensible, chosen in Config:

- **Under the name** (default) is the layout in the social-work guide: name,
 then `b.` / `d.`, then the place, stacked under the symbol. This is what a
 Local Authority reader expects.
- **Above the symbol** follows the Standard Genogram Format (McGoldrick, 2023),
 which sets the place apart from the name in its own colour rather than
 stacking it with the dates. It is drawn above the symbol and right of centre,
 in slate (`#2a6b8a` in print), with any key codes to the left of centre on
 the same line, capped at half the label width.

### 4.5 Deceased / abortion / miscarriage / stillborn

X is stroked at 1.5Γ— the regular symbol stroke weight, across the
full extent of whatever shape the symbol uses, corner to corner.

### 4.6 Couple-line variants

| `kind` | Stroke | Decoration | Label format |
|--------------------------|----------|-------------------------------------|---------------------|
| `married` | solid | none | `m.MM.YYYY` |
| `separated-legal` | solid | single `/` slash through the line | (start date if any) |
| `separated-living-apart` | solid | `Γ—` cross through the line | (start date if any) |
| `divorced` | solid | double `//` slashes | `m.YYYY d.YYYY` |
| `widowed` | solid | `X` on the line | `m.YYYY d.YYYY` |
| `cohabiting` | dashed | none | start date |
| `cohabiting-separated` | dashed | single `/` slash | start date |
| `civil-separated` | dashed | `/` slash | `YYYY-YYYY` range |

### 4.7 Parent-child variants

| `kind` | Stroke |
|---------------|------------------------------------------------|
| `biological` | solid |
| `adopted` | one solid descender + one dashed descender |
| `foster` | dashed |

### 4.8 Overlay variants

| `kind` | Colour | Stroke | Arrow |
|------------------|--------------|---------------------|---------------|
| `violence` | red | wavy + solid base | yes |
| `physical-abuse` | blue | wavy + solid base | yes |
| `emotional-abuse`| light green | wavy + solid base | yes |
| `sexual-abuse` | purple | DOUBLE wavy | yes |
| `hostile` | light blue | wavy + solid base | yes |
| `neglect` | black | dashed | yes |
| `very-close` | black | DOUBLE solid | no |
| `close` | black | solid | no |
| `distant` | black | dashed | no |

`direction='both'` renders a double-headed arrow.

### 4.6a Couple labels and decorations are placed clear of symbols

A second spouse is laid out beside the first, not either side of the client,
so that couple's line spans **across** the intervening symbol. Symbols are
drawn after the couple edges, so anything placed at the line's midpoint ends
up behind a white shape: on 2026-08-29 an `m.1977` was printing as a clipped
smudge above a circle. `_clearSpan()` therefore finds the widest part of the
line that no symbol covers, and both the label and the stroke decoration are
placed there. The label is sized to that gap, not to the whole span.

### 4.6b Every label carries a white halo

`paint-order: stroke fill` with a 2.4px stroke in the surface colour, on
every node and couple label. A structural or overlay edge crossing a label
then passes visually behind the text instead of striking it out, a
`very-close` overlay was ruling through a client's mother's name, place of
birth and date of birth all at once. This is cheaper and more reliable than
routing every edge around every label, and it covers couple lines, parent
buses and overlays alike. It is in both the in-app CSS and
`_EXPORT_SVG_CSS`, so screen and print agree.

The halo only works if the text paints last, and the overlay pass runs after
the labels, so every `<text>` is re-appended to the end of the SVG once the
overlays are drawn. Without that an abuse or closeness arrow drew straight over
a name with the halo underneath it, doing nothing. Structural edges are routed
around the labels instead (Β§4.4a); the halo is what covers the overlays, which
are point-to-point marks about a relationship rather than lines joining family
members and so are deliberately left direct.

### 4.9 Pan / zoom

Native SVG via `viewBox` mutation. Mouse-wheel zooms, click-drag pans,
double-click resets. Touch support is not required in v2.0.

---

## 5. PDF export

Per `ref_ifyi_pdf_protection` and `feedback_private_and_confidential_glyph`:

- **Page**: A4 (297 Γ— 210 mm). Orientation is chosen at export time, the Export tab has two buttons, ** Landscape PDF** and ** Portrait
 PDF**. No Config toggle (removed in v2.0.1).
- **Fit to page**: the exporter always uses the FULL content bbox
 (via `SVGSVGElement.getBBox()`), independent of whatever pan / zoom
 the user has left in the Diagram tab. Aspect-preserving fit centres
 the diagram between the header and footer with 12 mm horizontal
 margins.
- **Readability floor and multi-sheet spill**: names are held at a
 minimum of **8 pt** (raised from 6 pt on 2026-08-29: the exhibit is a
 legal document, read on paper and often photocopied, so the floor is set
 where it survives that rather than at the smallest legible size). If the diagram cannot fit one sheet at that
 size, it is **tiled across up to 9 A4 sheets** rather than shrunk
 further. Tiles are spread evenly over the content, so neighbouring
 sheets overlap slightly and the far edge is covered exactly. Past 9
 sheets the exporter falls back to a single fitted sheet. Each sheet
 repeats the header and is titled `… (sheet N of M)`; the footer
 numbers them `Page N of M`.
- **Orientation hint**: because Β§4.2a optimises the layout for
 whichever orientation suits the diagram, the Export tab shows the
 sheet count and resulting name size for BOTH orientations, marking
 the better one as recommended.
- **Header**: `PRIVATE & CONFIDENTIAL` (ampersand, never "AND") centred
 at the top, ~12 pt Helvetica bold (EB Garamond when inlined, see
 Β§11).
- **Watermark**: `DRAFT` diagonal stamp, light grey, 25% opacity
 (Config toggle, off by default).
- **Footer band**: case ref, date generated, page number. No GMIAU
 logo on the genogram page itself.
- **Encryption**: deferred. v2.0 ships an unencrypted PDF; pipe
 through `pdf-encrypt.html` (qpdf-wasm AES-256) if the bundle
 requires it.
- **Filename**: `YYMMDD CLIENTREF SEQ Genogram.pdf` per
 `EXPORT-SPEC.md Β§1` (single-space separator), built via the
 canonical `_exportName()` helper inlined in the tool.

### 5.2 Key to symbols

A genogram is only readable by someone who already knows the convention, and
a tribunal panel does not. Every PNG and PDF therefore carries a key.

- **Only what is used.** The key is built by walking the entered people and
 relationships: a plain family tree of four gets four entries, a safeguarding
 genogram gets the couple-line variants, the overlay kinds and the multiple
 birth it actually contains. It is never the whole vocabulary.
- **Drawn by the diagram's own code.** Each vignette calls `_drawSymbol`,
 `_drawCoupleLine` or `_drawOverlay`, so a key entry cannot drift away from
 the symbol it explains. The parent-child entry draws the sibling bus and a
 descender, not a bare horizontal line, which would read as a couple line.
- **Layout.** Rows of `vignette | label`, at most 8 rows per column and at
 most 3 columns, in a bordered block titled `Key to symbols`. A closing
 italic line, `An overlay arrow points at the person on the receiving end.`,
 appears only when a directional overlay is present.
- **PNG**: composed under the diagram in the same image, centred, with a
 10 px gutter at 1Γ— (`_stackPng`).
- **PDF**: the diagram is fitted to the **whole page first**, and the key
 only shares that sheet when doing so shrinks the diagram by no more than
 5% (`KEY_MAX_COST`). Otherwise it takes its own final page, headed
 `<title>, key to symbols` and numbered in the same `Page N of M`
 sequence. **A second sheet is the normal outcome, and that is correct**:
 reserving the key's space before fitting was tried on 2026-08-29 and
 reverted the same day, because it bought a one-sheet exhibit by dropping
 the names from 15.3 pt to 9.8 pt. On a legal document the size of the
 names beats the page count (RB).
- **Scale**: 0.3 mm per SVG unit, about 8 pt labels, when it shares a sheet.
 On its own page it is scaled up to fill the sheet (capped at 2.2Γ—), and
 the column count is chosen to maximise that scale: one tall column
 usually wins, because a page has far more height going spare than width.
- **Source line**: the key closes with
 `Symbols follow the standard social-work genogram convention (Genogram
 Explanation, 7 October 2022).` A reader of the bundle is entitled to know
 which standard the symbols follow, and the convention is not this tool's
 invention. Above it, when a directional overlay is present, sits
 `An overlay arrow points at the person on the receiving end.`
- Config toggle `ifyi_gg_show_key`, default on.

### 5.3 The genogram data inside the PDF

A PDF exported before 2026-08-29 carries **nothing but a picture**: the
diagram is one raster image at 150–200 dpi and the only real text is the
`PRIVATE & CONFIDENTIAL` header, the `Genogram: <client>` title and the
footer band. No metadata, no attachments. A family cannot be read back out
of one, and no amount of work on the importer will change that.

From 2.3 the sidecar travels with the file, as a **standard PDF embedded
file attachment** (`/Names /EmbeddedFiles` in the catalog), named
`<ref>.genogram.json`. Acrobat lists it in its attachments pane, `pdfdetach`
extracts it, and pdf.js `getAttachments()` reads it back.

- **What is in it**: the **whole** sidecar, people, notes, key codes and the
 abuse / closeness overlay edges, whether or not the diagram is currently
 drawing them. Decided by RB on 2026-08-29, so a PDF always reopens as
 exactly the genogram it came from.
- **What that means**: the file carries safeguarding metadata that is not
 visible on the page, and anyone holding it can extract it. Config toggle
 `ifyi_gg_embed` (default on) turns it off for an exhibit that should be a
 picture only.
- **Encryption** (`ifyi_gg_embed_encrypt`, default off): AES-GCM-256 with a
 PBKDF2-SHA256 key at 210,000 iterations, 16-byte salt, 12-byte IV, all
 base64 in a self-describing envelope. The attachment is then named
 `<ref>.genogram.encrypted.json`. WebCrypto only, no library; `file://`
 counts as a secure context so it works in the offline twin. The
 passphrase is typed on the Export tab and is **never persisted**, it is
 a secret, not a setting: and cannot be recovered: lose it and the data
 in that PDF is gone.
- **Implementation note**: jsPDF 2.5.1 has no attachment API. It publishes
 `putCatalog` from *inside* the catalog dictionary, which is exactly where
 `/Names` belongs, and `postPutResources` before it, where the stream and
 Filespec objects can be written. Those two hooks are all this needs. The
 payload is forced to pure ASCII so its byte length equals its string
 length and the stream needs no encoding declaration.

Reading one back is Β§6.

### 5.4 The paragraph in the PDF

Config toggle `ifyi_gg_pdf_prose`, default **off**. When on, the Β§7
plain-prose paragraph is added as its own page or pages after the diagram
and before the key, headed `<title>, the family in words`.

It is set as **real text** at 11 pt on a 5.6 mm lead, not as an image, so it
can be selected, searched and copied out of the bundle. It is laid out
before the page count is fixed, so the footers number every sheet correctly,
and it runs to as many pages as the family needs.

### 5.1 PNG export

- **Resolution**: 2Γ— CSS-px scale of the current SVG viewBox.
- **Inline CSS**: the tool clones the live SVG and injects a fixed
 light palette before serialising (`_EXPORT_SVG_CSS` constant) so
 the export is independent of the in-app theme.
- **Filename**: `YYMMDD CLIENTREF SEQ Genogram.png`.
- **Library**: none, pure DOM (`new Image(); canvas.toBlob`).

---

## 6. Sidecar JSON I/O

Write-back target:

```
<open_cases>/Case Summaries/<ref>.genogram.json
```

`<open_cases>` resolves from the One File config (`paths.openCases`)
when loaded, else falls back to `~/Work/00 Open Cases/`. The
case-summary JSON itself is **untouched**.

Import in the Build tab's ` Import sidecar or PDF` button reads that path
(via `<input type=file>` since FSA is disabled on `file://`). On
save, the tool offers a download with the canonical filename.

**It also accepts a PDF.** If the PDF carries an embedded sidecar (Β§5.3) it
is loaded, decrypting first where the envelope says so, a wrong passphrase
is refused with a plain message and nothing is imported. If it carries no
payload, the tool does **not** fail silently: it reads the first page's text
and offers the only two facts a data-less genogram PDF contains, the client
name from the `Genogram: <client>` title and the case ref from the footer,
so the caseworker gets a started genogram rather than a dead end. A PDF this
tool did not write is rejected outright.

Both routes share `applySidecarObject()`, so a JSON sidecar and a PDF behave
identically once the object is in hand.

**v1 Family Tree sidecars** (`<ref>.family-tree.json`, `_v: 1`) are
accepted on import; missing v2 fields are filled with defaults
(`sex: '?'`, `status: 'alive'`, `couple.kind: 'married'`,
`parent.kind: 'biological'`). The tool surfaces a one-time banner
when a v1 sidecar is imported so the caseworker knows to revisit the
new fields.

---

## 7. Plain-prose generator

Generates a single paragraph by walking the graph from the client
outwards. Sentence templates:

| Edge type from client | Sentence |
|------------------------------------------------|---------------------------------------------------------------------------|
| Couple, married, alive | "{X} is the {client}'s {wife\|husband}, born {dob} in {location}." |
| Couple, divorced | "{X} is the {client}'s ex-{wife\|husband}, married {m} and divorced {d}." |
| Couple, cohabiting | "{X} is the {client}'s partner, in a relationship since {start}." |
| Parent (mother / father by sex) | "{X} is the {client}'s {mother\|father}, born {dob} in {location}." |
| Parent, deceased | "{X} was the {client}'s {mother\|father}, born {dob} and died {dod}." |
| Parent, adopted | "{X} adopted the {client}, …" |
| Parent, foster | "{X} is the {client}'s foster {mother\|father}, …" |
| Child (biological) | "{X} is the {client}'s {daughter\|son\|child}, born {dob}." |
| Sibling | "{X} is the {client}'s {sister\|brother\|sibling}, born {dob}." |
| Aunt/uncle (derived: sibling of a parent) | "{X} is the {client}'s {maternal\|paternal} {aunt\|uncle}, …" |

Order: couple parents children siblings aunts/uncles 
grandparents others. Punctuation uses commas + ` and ` (no Oxford
comma, UK style). Each person is emitted at most once.

Overlay edges are NOT emitted in prose (they're confidential
safeguarding metadata, not biographical fact).

### 7.2 Reading text back in

The Build tab's **Build from written text** panel is the inverse of the two
things this tool prints, so both round trip. Either shape may be pasted, and
they may be mixed in one go; they merge on the person's name.

**1. Prose sentences**, the Β§7 output shape:

```
{Name} {is|was} {Client}'s {relationship}[, {clauses}].
```

Clauses are found by keyword, longest first: `in a relationship since`,
`who died`, `born`, `died`, `married`, `divorced`, `separated since`, `in`.
They are NOT split on commas or on ` and `, because `born 1912 in Trinidad
and Tobago and died 2000` has to survive intact.

`was` means the person has died. `in Unknown Location` (and `Unknown`,
`Not known`, `No fixed abode`, `N/A`) is read as no location.

**2. Relationship rows**, what `_relSummary()` + `_relMeta()` print in the
Build tab list:

```
Anna ⟢ biological parent of ⟢ Maxwell
Anna ⟷ separated-living-apart ⟷ Hassan
start 1996 Β· end 1998
Anna hostile Hassan
```

The ASCII a keyboard produces (`->`, `<->`, `<-`, `--`) is accepted for each
glyph. A `start … Β· end …` line applies to the row above it. Rows connect any
two people, not only the client, and carry the exact edge `kind`, which a
sentence cannot express. The overlay arrow gives the `direction`; the three
closeness kinds are forced to `direction: 'none'` per Β§3.2 whatever was typed.

**The client** is the name in the possessive, most often. With rows alone
there is no possessive, so it is the most connected person. Either way the
choice is reported.

**Sex comes from the relationship noun** (`mother` F), never from a name: a
name is not evidence of sex and a wrong shape on a tribunal exhibit is worse
than an admitted unknown. The single exception is a bare placeholder name
(`Mother`, `Unknown Father`, `Aunt`), which is a role word standing in for a
name; each one is listed in the report.

**Derived structure.** An aunt or uncle becomes a sibling of the named parent
on that side, and a grandparent a parent of it; where that parent has not
been entered, an `Unknown Mother` / `Unknown Father` placeholder is created
and reported. A grandparent with no maternal / paternal side is left
unlinked rather than attached to a guess. Children of the client sharing a
full `YYYY-MM-DD` date of birth are given a shared `multipleId` and
`multipleKind: 'fraternal'`, identical is not something the words can
evidence.

**Nothing is dropped in silence.** The panel reports what was built, every
inference (placeholder parents, twins, placeholder-name sexes, dropped
duplicate edges) and every line that could not be used, with the line quoted.
Two live couple edges on the client are flagged rather than silently
converted to a divorce.

Applying a parse **replaces** the current people and relationships, behind a
confirm. Merging a re-paste into an edited diagram would double every person
whose name had been tidied up in between.

The reading rules are pinned by `tests/test_genogram_parse.py`, which
extracts the block from this tool and runs `tests/genogram-parse.test.mjs`
against it, so the test reads the shipped code rather than a copy.

### 7.1 Placeholder convention (form inputs)

Per `REFERENCES-SPEC.md Β§104-105`, form placeholders use the canonical
example strings directly (no `e.g.` prefix). Seeded placeholders:
person name **`David Pountney`**, location **`Manchester, UK`**, case
ref **`RB12345`**.

---

## 8. Settings (canonical 6)

The tool inherits the canonical 6 toggles unchanged (Mode, Theme,
App-font Family, App-font Size, Hide Guide, Hide Config). No tool-local
matter-toggles in the Settings tab.

The Settings tab also includes a `<details>` block with the inlined
`@@SPEC:GENOGRAM-SPEC@@` (this file) for in-tool reference.

---

## 9. Config (per-tool)

| Field | Default | Notes |
|--------------------------------|--------------|------------------------------------------------------------------------|
| Show DOB | on | matter-toggle |
| Show place of birth | on | " (`ifyi_gg_show_loc`) |
| Place of birth position | under | matter-toggle: Under the name / Above the symbol (`ifyi_gg_loc_placement`). See Β§4.4b. |
| Place of birth weight | muted | matter-toggle: Match the print / Faint (`ifyi_gg_loc_tone`). Muted is `--text-muted` on screen, `#555` in print; faint is the older `--text-faint` / `#777`. |
| Family lines | clear | matter-toggle: Route around the text / Straight through (`ifyi_gg_edge_routing`). See Β§4.4a. |
| Show status | on | controls whether status markers (`X`, triangle for unborn) render |
| Show keyCodes | on | " |
| Show overlay | on | hide the abuse / closeness overlay for non-safeguarding exhibits |
| Key to symbols on the export | on | matter-toggle (`ifyi_gg_show_key`). See Β§5.2. |
| Include the written paragraph | off | matter-toggle (`ifyi_gg_pdf_prose`). See Β§5.4. |
| Genogram data inside the PDF | on | matter-toggle (`ifyi_gg_embed`). See Β§5.3. |
| Encrypt that data | off | matter-toggle (`ifyi_gg_embed_encrypt`). Passphrase is on the Export tab, never persisted. |
| DRAFT watermark | off | matter-toggle: On / Off (`ifyi_gg_pdf_draft`). Orientation lives on the Export tab, no Config toggle. |

All persist to `localStorage` under namespaced keys `ifyi_gg_<setting>`.

---

## 10. File layout

```
immigrationfyi-tools/source/genogram-generator.html edit here
immigrationfyi-tools/offline/genogram-generator-offline.html regen by build-all-offline.py
gmiau-specs/GENOGRAM-SPEC.md this file
gmiau-specs/GENOGRAM-GUIDE.md social-work convention, inlined in Guide tab
gmiau-specs/source-docs/genogram-guide-07102022.pdf original PDF
gmiau-specs/icons/genogram-generator.{svg,png} icon (frame + glyph)
```

Vault docs:

```
~/Notes/Resources/Cheatsheets/Genogram Generator Cheatsheet.md
~/Notes/Resources/Reference/Genogram Generator Reference.md
```

GitHub Pages mirror (Filer / Bundle Builder / Evidence Exhibitor /
Auditor companion): `ryanbestford.github.io/tools/genogram-generator.html`,
encrypted via `~/Projects/rjb/tools/publish.fish`.

---

## 11. Backlog (deferred from v2.0)

- **Household boundary**, freeform loop drawn around co-resident
 symbols (GUIDE Β§5). Postponed because freeform-path UX in SVG is
 awkward; a rectangular bounding-box approximation may land in v2.1.
- **Step-parent line**, distinct dashed-double-line style.
- **EB Garamond** inline for PDF body text (currently Helvetica).
- **qpdf-wasm AES-256** PDF encryption (pipe through `pdf-encrypt.html`
 in the meantime).
- **Auto-import from `gmiau-case-summary/1`**, read parents, spouse,
 children from the case-summary JSON to pre-fill the Build tab.
- **GEDCOM import / export**, unsupported; would need its own spec.

---

## 12. Versioning

- **2.3 Β· 2026-08-29**, **The PDF carries its own data** (Β§5.3): the full
 sidecar as a standard embedded file attachment, optionally AES-256
 encrypted with a passphrase, so an exported PDF reopens in the tool.
 Import (Β§6) now takes a PDF as well as a JSON sidecar, and tells the
 caseworker plainly when a PDF predates this and carries only a picture,
 offering the client name and case ref it can still read. **Legibility**,
 this being a legal document: the name floor is raised to 8 pt, the key no
 longer shrinks the diagram to share a sheet (Β§5.2), a key alone on a page
 is scaled up to fill it, couple labels and decorations are placed clear of
 symbols (Β§4.6a), and every label carries a white halo so no edge can rule
 through it (Β§4.6b). The key now cites its source. New: the prose paragraph
 as text pages in the PDF (Β§5.4). Adds pdf.js 3.11.174, which the offline
 builder inlines along with its worker; the twin grows from 0.6 MB to
 2.3 MB.
- **2.2 Β· 2026-08-29**, **Build from written text** (Β§7.2): the Build tab
 reads back the Export tab's prose paragraph and the Build tab's own
 relationship rows, mixed, and builds the People and Relationships from
 them, reporting every inference and every unused line. **Key to symbols**
 (Β§5.2) on every PNG and PDF, listing only the symbols the genogram
 actually uses; on the PDF, room for it is reserved before the diagram is
 fitted, so a one-sheet exhibit stays one sheet. New Config toggle
 `ifyi_gg_show_key` (default on). Parser rules pinned by
 `tests/test_genogram_parse.py`.
- **2.1 Β· 2026-05-15**, Render-mode toggle added to the Config tab: **Simple (family tree)** vs **Genogram (procedure)**, persisted to `localStorage.ifyi_gg_render_mode` (default: `genogram`). Simple mode renders rounded rectangles for every person regardless of sex / trans / orientation, draws plain solid couple / parent lines (no per-kind decorations or dashing), uses the original diagonal-slash for deceased (no X-across-shape), and skips key-codes + overlay edges entirely. Couple labels condense to `m.YYYY Β· div.YYYY`. Export filenames + PDF title switch to `Family Tree`. Data model is unchanged, switching modes never loses entered data; sidecar JSON does not store the mode (Settings-level preference per [[ref_ifyi_one_file_config]]-style stickiness, not per-case).
- **2.0.1 Β· 2026-05-15**, Export-tab PDF buttons split into ** Landscape PDF** + ** Portrait PDF** (orientation chosen at export, not via Config). Export now uses `SVGSVGElement.getBBox()` to fit the FULL content (the previous version inherited the user's pan / zoom, which clipped the leftmost column in `RB12345.genogram.json`). Diagram-tab **Fit** button and `dblclick` now call `fitToContent()` directly (no full re-render).
- **2.0 Β· 2026-05-15**, Genogram Generator. Renamed from Family Tree;
 conventions adopted from `gmiau-specs/source-docs/genogram-guide-07102022.pdf`.
 Visual core (shapes by sex, deceased X, twins/triplets, trans
 nesting, gay/lesbian inverted-triangle) + couple-line variants
 (married/separated/divorced/widowed/cohabiting/civil-separated) +
 parent-child line types (biological/adopted/foster) + male-left /
 oldest-left layout + keyCodes field + emotional-relationship
 overlay (7 kinds Γ— 4 directions). Sidecar `_v` bumps to 2; v1
 sidecars are accepted on import with defaults filled.

Prior versions of the predecessor tool (Family Tree v1.0 / v1.1 / v1.2,
2026-05-13 2026-05-14) are archived in
`gmiau-backup/genogram-rename-2026-05-15/` for reference.