# Format specification

Everything below was read out of the reference `.docx` by unzipping it and
inspecting the XML. Nothing here is inferred from how the document looks in a
viewer. Line references are to the reference document's `word/document.xml`
(141 paragraphs, 259 runs, 13 distinct paragraph shapes).

```bash
unzip -q reference.docx -d unpacked/
```

The package has 11 parts and nothing else — no headers, footers, images,
tables, text boxes, bookmarks, comments or content controls:

```
[Content_Types].xml  _rels/.rels  docProps/app.xml  docProps/core.xml
word/document.xml  word/_rels/document.xml.rels  word/fontTable.xml
word/numbering.xml  word/settings.xml  word/styles.xml
word/theme/theme1.xml  word/webSettings.xml
```

---

## 1. Page setup

```xml
<w:sectPr>
  <w:pgSz w:w="11906" w:h="16838" w:code="9"/>
  <w:pgMar w:top="720" w:right="720" w:bottom="720" w:left="720"
           w:header="709" w:footer="709" w:gutter="0"/>
  <w:cols w:space="708"/>
  <w:docGrid w:linePitch="360"/>
</w:sectPr>
```

A4 (`w:code="9"`), 0.5″ margins all round. Text area is 7.27″ × 10.69″.
US Letter is `w:w="12240" w:h="15840"` with `w:code="1"` — the `--page-size`
flag swaps exactly these three attributes and nothing else.

## 2. Style layer

### docDefaults (`styles.xml`)

```xml
<w:rPrDefault><w:rPr>
  <w:rFonts w:asciiTheme="minorHAnsi" w:eastAsiaTheme="minorHAnsi"
            w:hAnsiTheme="minorHAnsi" w:cstheme="minorBidi"/>
  <w:kern w:val="2"/>
  <w:sz w:val="24"/><w:szCs w:val="24"/>
  <w:lang w:val="en-US" w:eastAsia="en-US" w:bidi="ar-SA"/>
  <w14:ligatures w14:val="standardContextual"/>
</w:rPr></w:rPrDefault>
<w:pPrDefault><w:pPr>
  <w:spacing w:after="160" w:line="278" w:lineRule="auto"/>
</w:pPr></w:pPrDefault>
```

`kern`, `ligatures` and the theme-font mapping are inherited by everything and
are easy to miss. They are one of the main reasons the template-clone strategy
was chosen over generating a document from scratch.

### Normal

```xml
<w:style w:type="paragraph" w:default="1" w:styleId="Normal">
  <w:pPr><w:spacing w:after="0" w:line="240" w:lineRule="auto"/></w:pPr>
  <w:rPr><w:sz w:val="22"/><w:szCs w:val="22"/><w:lang w:val="en-IN"/></w:rPr>
</w:style>
```

**`Normal` is 11 pt, not 10 pt.** It also carries `w:lang="en-IN"`. The 10 pt
body size exists *only* as direct run formatting on every single run. A run
emitted without an explicit `<w:sz w:val="20"/>` silently renders at 11 pt in
the theme font (Aptos), not 10 pt Bookman. `validate.py` checks that every run
pins its own `rFonts` and `sz` for exactly this reason.

`spacing after="0" line="240" lineRule="auto"` = single-spaced, no gap between
paragraphs. Vertical rhythm in this resume comes entirely from deliberately
placed empty paragraphs (§5).

### ListParagraph

```xml
<w:style w:type="paragraph" w:styleId="ListParagraph">
  <w:basedOn w:val="Normal"/><w:uiPriority w:val="34"/>
  <w:pPr><w:ind w:left="720"/><w:contextualSpacing/></w:pPr>
</w:style>
```

`contextualSpacing` on bullets is **inherited from this style**, not written on
each paragraph. The `ind left="720"` is overridden per-paragraph to `360`.

### Hyperlink

```xml
<w:style w:type="character" w:styleId="Hyperlink">
  <w:rPr><w:color w:val="467886" w:themeColor="hyperlink"/><w:u w:val="single"/></w:rPr>
</w:style>
```

### Theme

`theme1.xml` sets major = **Aptos Display**, minor = **Aptos** (the Word 2024
defaults). Body runs use `w:cstheme="minorHAnsi"`, so complex-script fallback
resolves to Aptos while Latin text is Bookman Old Style.

## 3. Numbering

```xml
<w:num w:numId="1"><w:abstractNumId w:val="0"/></w:num>

<w:abstractNum w:abstractNumId="0">
  <w:nsid w:val="756D335C"/>
  <w:multiLevelType w:val="hybridMultilevel"/>
  <w:lvl w:ilvl="0" w:tplc="40090001">
    <w:start w:val="1"/>
    <w:numFmt w:val="bullet"/>
    <w:lvlText w:val="&#xF0B7;"/>          <!-- U+F0B7, Symbol-font bullet -->
    <w:lvlJc w:val="left"/>
    <w:pPr><w:ind w:left="720" w:hanging="360"/></w:pPr>
    <w:rPr><w:rFonts w:ascii="Symbol" w:hAnsi="Symbol" w:hint="default"/></w:rPr>
  </w:lvl>
  ...levels 1-8 tentative...
</w:abstractNum>
```

The glyph is real list numbering, not a typed character. **No `•` ever appears
inside a `<w:t>` element** — `validate.py` asserts this, because a typed bullet
both breaks list semantics and confuses ATS parsers.

## 4. The paragraph kinds

XML samples below show the real markup with **fictional placeholder text**
substituted for the reference author's details.

Every run in the document carries the same font block:

```xml
<w:rFonts w:ascii="Bookman Old Style" w:hAnsi="Bookman Old Style" w:cstheme="minorHAnsi"/>
<w:sz w:val="20"/><w:szCs w:val="20"/>
```

`rPr` children must appear in schema order: `rStyle, rFonts, b, bCs, sz, szCs, u`.
`pPr` children likewise: `pStyle, numPr, ind, contextualSpacing, jc, rPr`.

### 4.1 Name and target title (paragraphs 0, 1)

```xml
<w:p><w:pPr><w:jc w:val="center"/>
  <w:rPr>{FONT}<w:b/><w:bCs/>{SIZE}</w:rPr></w:pPr>
  <w:r><w:rPr>{FONT}<w:b/><w:bCs/>{SIZE}</w:rPr><w:t>Jordan Rivera</w:t></w:r>
</w:p>
```

### 4.2 Contact line (paragraph 2)

Centered and bold like the two above, but the email is wrapped in a hyperlink:

```xml
<w:hyperlink r:id="rId5" w:history="1">
  <w:r><w:rPr><w:rStyle w:val="Hyperlink"/>{FONT}<w:b/><w:bCs/>{SIZE}</w:rPr>
    <w:t>jordan.rivera@example.com</w:t></w:r>
</w:hyperlink>
<w:r><w:rPr>{FONT}<w:b/><w:bCs/>{SIZE}</w:rPr>
  <w:t xml:space="preserve"> | +1 555-0100</w:t></w:r>
```

with, in `word/_rels/document.xml.rels`:

```xml
<Relationship Id="rId5"
  Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink"
  Target="mailto:jordan.rivera@example.com" TargetMode="External"/>
```

Only the email is linked. A LinkedIn URL or city on the same line stays plain
text, matching the reference. When the input has no email, the generator drops
the `rId5` relationship entirely rather than emitting an empty target.

### 4.3 Section headers (paragraphs 4, 22, 32, 138)

```xml
<w:p><w:pPr>
  <w:rPr>{FONT}<w:b/><w:bCs/>{SIZE}<w:u w:val="single"/></w:rPr></w:pPr>
  <w:r><w:rPr>{FONT}<w:b/><w:bCs/>{SIZE}<w:u w:val="single"/></w:rPr>
    <w:t>PROFESSIONAL SUMMARY:</w:t></w:r>
</w:p>
```

ALL CAPS, trailing colon, bold, single underline, left-aligned, no indent.

> **Reference inconsistency.** `EDUCATION:` (paragraph 138) additionally carries
> `<w:jc w:val="both"/>`; the other three do not. On a single line that never
> wraps, justification is indistinguishable from left alignment. The generator
> emits the 3-of-4 majority form for all four.

### 4.4 Bullets (107 paragraphs)

```xml
<w:p><w:pPr>
  <w:pStyle w:val="ListParagraph"/>
  <w:numPr><w:ilvl w:val="0"/><w:numId w:val="1"/></w:numPr>
  <w:ind w:left="360"/>
  <w:jc w:val="both"/>
  <w:rPr>{FONT}{SIZE}</w:rPr></w:pPr>
  <w:r><w:rPr>{FONT}{SIZE}</w:rPr><w:t>…</w:t></w:r>
</w:p>
```

Regular weight — **zero of the 107 bullets contains a bold run**. The
per-paragraph `ind left="360"` overrides the style's `720`, while the abstract
level's `hanging="360"` still applies, so the glyph sits at 0″ and text at 0.25″.

### 4.5 Skills lines (8 paragraphs) — not bullets

```xml
<w:p><w:pPr>
  <w:contextualSpacing/><w:jc w:val="both"/>
  <w:rPr>{FONT}{SIZE}</w:rPr></w:pPr>
  <w:r><w:rPr>{FONT}<w:b/><w:bCs/>{SIZE}</w:rPr><w:t>Programming &amp; Data:</w:t></w:r>
  <w:r><w:rPr>{FONT}{SIZE}</w:rPr>
    <w:t xml:space="preserve"> Python, SQL, Pandas, …</w:t></w:r>
</w:p>
```

Bold label including the colon, then the separating space at the head of the
regular run.

> **Reference inconsistency.** 7 of 8 skills lines put the separating space in
> the regular run; the 8th (`AI Platforms, Observability & Delivery: `) puts it
> inside the bold run. A bold space is fractionally wider, so that one line's
> justification differs by well under a pixel. The generator uses the 7-of-8
> majority form.

### 4.6 Job blocks

Three bold lines, then bullets:

| Line | `pPr` rPr | Run |
|---|---|---|
| `Company, City ST \| Month Year – Month Year` | *not* bold | bold |
| `Job Title` | **bold** | bold |
| `Responsibilities:` | *not* bold | bold |

The paragraph-mark formatting differs between line 2 and lines 1/3. It affects
only the ¶ glyph, never rendered text; the generator reproduces it anyway
because it costs nothing.

All four date ranges use **EN DASH U+2013** with a space either side. The whole
document contains exactly 5 non-ASCII characters: 5 × U+2013, plus 2 × U+00A0
(see §7).

### 4.7 Blank paragraphs — the vertical rhythm

Seven empty paragraphs, and their placement is the entire spacing system:

- exactly one **before each of the 4 section headers** (¶3, ¶21, ¶31, ¶137)
- exactly one **before each job block after the first** (¶64, ¶93, ¶120)
- **none** between a header and its first content line
- **none** at the end of the document

> **Reference inconsistency.** Those 7 paragraphs have 5 different `pPr`, each
> having inherited whatever the preceding block's paragraph mark carried —
> `jc=center`+bold, `ListParagraph`+`ind 360`+`jc=both`, `contextualSpacing`,
> plain (×3), and `jc=both`. All seven render as an identical empty 10 pt line
> because `Normal` sets `after="0"` and none of those properties affects an
> empty paragraph's height. This is Word editing residue, not design. The
> generator emits one canonical form (the 3-of-7 plurality: `pPr` with only the
> font/size run-mark properties).

## 5. settings.xml

`<w:defaultTabStop w:val="720"/>`, `compatibilityMode = 15`, `themeFontLang
en-US`, `characterSpacingControl doNotCompress`. No tabs are used anywhere in
the body — the 259 runs contain 259 `<w:t>` elements and zero `<w:tab/>` or
`<w:br/>`.

## 6. fontTable.xml

Declares Symbol, Times New Roman, Courier New, Wingdings, Aptos, Aptos Display
and:

```xml
<w:font w:name="Bookman Old Style">
  <w:panose1 w:val="02050604050505020204"/>
  <w:charset w:val="00"/><w:family w:val="roman"/><w:pitch w:val="variable"/>
  <w:sig w:usb0="00000287" .../>
</w:font>
```

The panose and signature are what let Word pick a sane substitute on a machine
without the font. `validate.py` asserts every `w:ascii` used in the body is
declared here, so `--font Georgia` would be flagged if Georgia were missing.

## 7. Content defects in the reference (not formatting)

- ¶5 ends `…across the USA and India.. ` — a doubled period and a trailing space,
  from two runs each contributing a `.`
- ¶50 and ¶54 end with a trailing **U+00A0** non-breaking space

The parser strips trailing whitespace including NBSP, so these three vanish on
round-trip. The doubled period survives (it is content, and the skill does not
edit content).

## 8. What was stripped to make `assets/template/`

Performed once by `reference/make-template.py`:

| Removed | From | Why |
|---|---|---|
| `w:rsid*` attributes (~530) | document, settings, styles | editing-session fingerprints |
| `w14:paraId` / `w14:textId` (282) | document | per-paragraph ids |
| `<w:proofErr>` (116) | document | spell/grammar markers |
| `<w:lastRenderedPageBreak>` (4) | document | stale pagination cache |
| `<w:rsids>`, `w14:docId`, `w15:docId` GUID, `<w:proofState>` | settings | document identity |
| `dc:creator`, `cp:lastModifiedBy`, `cp:revision`, timestamps | core.xml | authorship |
| `Pages/Words/Characters/Lines/Paragraphs/TotalTime/Company` | app.xml | stale statistics |
| the entire `<w:body>` content | document | replaced by a `{{BODY}}` token |
| `Target="mailto:…"` | document.xml.rels | replaced by a `{{MAILTO}}` token |

`styles.xml` (all 376 latent style entries), `numbering.xml`, `fontTable.xml`,
`theme1.xml`, `webSettings.xml`, `[Content_Types].xml` and `_rels/.rels` are
otherwise byte-identical to the original.

## 9. Fidelity evidence

Extracting the reference back to plain text and rebuilding it produces a body
XML diff of **7 hunks in 3,215 lines**, every one of them in the documented
invisible set: 4 blank-paragraph `pPr` variants, the stray `jc` on `EDUCATION:`,
and 3 trailing-whitespace normalizations. Paragraph count matches (141 = 141)
and paragraph text matches in order.

Rendered through Microsoft Word to PDF on a machine with Bookman Old Style
installed, `pdftotext -layout` output of the original and the rebuild is
**identical — 289 lines, zero differences**: every line wraps at the same word
and both documents are 5 pages. A raster diff at 100 dpi shows 2,339 of
4,837,950 pixels differing (0.048%), max delta 65/255, all of it anti-aliasing
around the one bold-space skills line described in §4.5.
