# PDF Generation — Pagination & Layout

Applies to: `src/lib/pdf.ts` in spq-v2.

## Page Break Logic

The correct page-break pattern for jsPDF 4.x:

```typescript
const CONTENT_BOTTOM = PAGE_H - MARGIN; // 772
const FOOTER_HEIGHT = 35; // reserved for footer separator + text + page number

// Before rendering each service, check if it fits:
let svcH = 0;
svcH += 5;  // decision badge (if present)
svcH += 6;  // name + price
svcH += 16; // priority (if present)
svcH += 8;  // recommendation (if present)
svcH += doc.splitTextToSize(svc.explanation, CW).length * 5 + 2; // explanation
svcH += 8;  // parts flags (each, if present)
svcH += 6;  // separator + spacing

if (y + svcH > CONTENT_BOTTOM - FOOTER_HEIGHT) {
    y = newPage(doc);  // doc.addPage() + y = MARGIN
    // Redraw section header on new page
    doc.setFont(...); doc.text(currentSectionLabel, MARGIN, y); y += 8;
}
```

**Do NOT use `yPos > PAGE_HEIGHT * 0.7` as a guard** — it blocks breaks in the top 70% of the page, forcing content past the page edge.

## Footer Rendering

```typescript
function drawFooter(doc, pageNum, totalPages) {
    doc.setPage(pageNum);
    const fy = PAGE_H - MARGIN - FOOTER_HEIGHT + 5;
    doc.line(MARGIN, fy, PAGE_W - MARGIN, fy);
    doc.text('message', PAGE_W / 2, fy + 15, { align: 'center' });
    doc.text(`Page ${pageNum} of ${totalPages}`, PAGE_W - MARGIN, PAGE_H - 10, { align: 'right' });
}
// After all content: loop over pages to draw footer on EVERY page
for (let i = 1; i <= doc.getNumberOfPages(); i++) drawFooter(doc, i, totalPages);
```

## Text Rendering (jspdf 4.x)

Never pass arrays to `doc.text()`. Render `splitTextToSize` results one line at a time:

```typescript
// ❌ Causes overlap in jspdf 4.x:
doc.text(lines, MARGIN, yPos);
yPos += lines.length * 6;

// ✅ Correct:
lines.forEach((line) => { doc.text(line, MARGIN, yPos); yPos += 6; });
```

## Height Estimator Constants

The height estimator must match actual yPos increments exactly:

| Element | yPos Advance |
|---------|-------------|
| Decision badge | 5 |
| Name + price | 6 |
| Priority label | 6 |
| Priority reason | 10 |
| Recommendation | 8 |
| Explanation line | 5 |
| Explanation gap | 2 |
| Parts flag | 8 |
| Aftermarket flag | 8 |
| Separator + spacing | 6 |
