agentman-pdf-style

Agentman brand styling for PDF report generation. Use when generating ANY PDF report using reportlab, fpdf2, or weasyprint. Provides mandatory color constants, table styles, document setup, typography, page layout, and a pre-generation checklist. Ensures every PDF uses the Agentman warm terracotta/charcoal palette instead of generic library defaults. Load this skill BEFORE writing any PDF generation code.

designv1.0.0
pdfreportlabfpdf2weasyprintbrand-guidelinesdesign-systemagentmanequity-research

Skill Instructions

# Agentman PDF Style Guide

## When to Use

Load this skill **before writing any PDF generation code** — whether using `reportlab`, `fpdf2`, or `weasyprint`. This skill provides the complete Agentman brand implementation for PDF output.

**The brand identity is quiet confidence** — charcoal suits, not fire trucks. Carbon and warm neutrals do the heavy lifting. Terracotta appears as a rare, deliberate accent.

## STOP — Read Before Writing Any Code

Before writing a SINGLE line of PDF generation code, you MUST:

1. **Define ALL color constants below** as the first lines of your script
2. **Set document author** to `"Agentman Equity Research Assistant"`
3. **Set document title** to `"Agentman — {Report Title}"`
4. **Display author on page 1** immediately after the report title: `"Author: Agentman Equity Research Assistant"`
5. **NEVER use library default colors** — override every color explicitly
6. **NEVER import** `colors.black`, `colors.grey`, `colors.blue`, `colors.navy`, `colors.darkblue`, `colors.lightgrey` from reportlab
7. **NEVER use** `getSampleStyleSheet()` without overriding every color

---

## Visual Weight Distribution

Every report must maintain this ratio:

- **70% Carbon/Charcoal** (`#141413`, `#292322`, `#3D3735`) — the backbone
- **20% Warm Neutrals** (`#F0EEE6`, `#F6EAE6`, `#E3DACC`, `#FAF9F5`, `#FFFFFF`) — warmth without color
- **10% Terracotta Accent** (`#CC785C`, `#D97757`, `#A65945`) — sparingly, max 3 per section

---

## reportlab Implementation

### Color Constants (Copy-Paste This Exactly)

```python
from reportlab.lib.colors import HexColor

# ============================================================
# AGENTMAN BRAND COLORS — MANDATORY for all PDF reports
# ============================================================

# Primary Brand (Terracotta) — use sparingly, max 3 per section
AGENTMAN_800 = HexColor('#703B2D')   # Badge text, deep emphasis (rare)
AGENTMAN_700 = HexColor('#8B4A38')   # Cover accent, metric box values (rare)
AGENTMAN_600 = HexColor('#A65945')   # Negative emphasis text
AGENTMAN_500 = HexColor('#CC785C')   # PRIMARY ACCENT — stat numbers, table header text, accent lines
AGENTMAN_400 = HexColor('#D97757')   # Highlight/caution text
AGENTMAN_200 = HexColor('#E6A890')   # Badge borders
AGENTMAN_150 = HexColor('#E3DACC')   # TABLE BORDERS, separators
AGENTMAN_100 = HexColor('#F6EAE6')   # TABLE HEADER FILL (light warm tint)
AGENTMAN_75  = HexColor('#F0EEE6')   # Highlight row bg, callout blocks
AGENTMAN_50  = HexColor('#FAF9F5')   # Alternate row bg (cream)

# Text Colors (Charcoal) — 70% of visual weight
CHARCOAL_950 = HexColor('#141413')   # PRIMARY BODY TEXT
CHARCOAL_900 = HexColor('#292322')   # Section titles, cover title
CHARCOAL_800 = HexColor('#3D3735')   # Secondary text

# Neutral (Slate)
SLATE_700    = HexColor('#334155')   # Secondary text on very light backgrounds
SLATE_600    = HexColor('#475569')   # Secondary body text, metric labels
SLATE_500    = HexColor('#64748B')   # Footer, muted captions
SLATE_400    = HexColor('#94A3B8')   # Descriptions, meta text
WHITE        = HexColor('#FFFFFF')

# ============================================================
# BANNED — NEVER import or use these:
# colors.black, colors.grey, colors.blue, colors.lightgrey
# colors.darkblue, colors.navy, colors.steelblue
# HexColor('#000000'), HexColor('#333333'), HexColor('#666666')
# Any hex not listed above
# ============================================================
```

### Table Style (Copy-Paste This Exactly)

```python
from reportlab.platypus import Table, TableStyle

def agentman_table_style():
    """Return the MANDATORY Agentman table style. Use for ALL tables."""
    return TableStyle([
        # Header row — LIGHT warm fill + terracotta text
        ('BACKGROUND', (0, 0), (-1, 0), AGENTMAN_100),     # #F6EAE6 light fill
        ('TEXTCOLOR',  (0, 0), (-1, 0), AGENTMAN_500),     # #CC785C terracotta text
        ('FONTNAME',   (0, 0), (-1, 0), 'Helvetica-Bold'),
        ('FONTSIZE',   (0, 0), (-1, 0), 11),
        ('BOTTOMPADDING', (0, 0), (-1, 0), 8),
        ('TOPPADDING',    (0, 0), (-1, 0), 8),

        # Body rows — charcoal text
        ('TEXTCOLOR',  (0, 1), (-1, -1), CHARCOAL_950),    # #141413 body text
        ('FONTNAME',   (0, 1), (-1, -1), 'Helvetica'),
        ('FONTSIZE',   (0, 1), (-1, -1), 10),
        ('BOTTOMPADDING', (0, 1), (-1, -1), 6),
        ('TOPPADDING',    (0, 1), (-1, -1), 6),

        # First column — bold labels (same color as body, NOT dark fill)
        ('FONTNAME',   (0, 1), (0, -1), 'Helvetica-Bold'),

        # Alternating row backgrounds
        ('ROWBACKGROUNDS', (0, 1), (-1, -1), [WHITE, AGENTMAN_50]),

        # Grid — warm brand borders
        ('GRID',       (0, 0), (-1, -1), 0.5, AGENTMAN_150),  # #E3DACC warm

        # Alignment
        ('ALIGN',      (0, 0), (-1, 0), 'CENTER'),   # Header centered
        ('ALIGN',      (0, 1), (0, -1), 'LEFT'),     # First col left
        ('ALIGN',      (1, 1), (-1, -1), 'CENTER'),  # Data centered
        ('VALIGN',     (0, 0), (-1, -1), 'MIDDLE'),
    ])


def agentman_highlight_table_style():
    """Table style variant with highlight rows for totals/summaries."""
    base = agentman_table_style()
    # Add highlight row styling — apply to specific rows after creation:
    # table_style.add('BACKGROUND', (0, row_idx), (-1, row_idx), AGENTMAN_75)
    return base
```

### Document Setup (Copy-Paste This Exactly)

```python
from reportlab.lib.pagesizes import letter
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, PageBreak
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib.units import inch
from reportlab.lib.enums import TA_LEFT, TA_CENTER

def create_agentman_doc(filename, title, date_str):
    """Create an Agentman-branded PDF document."""
    doc = SimpleDocTemplate(filename, pagesize=letter,
                           leftMargin=0.75*inch, rightMargin=0.75*inch,
                           topMargin=0.75*inch, bottomMargin=0.75*inch,
                           title=f"Agentman — {title}",
                           author="Agentman Equity Research Assistant")
    return doc
```

### Paragraph Styles (Copy-Paste This Exactly)

```python
# ============================================================
# AGENTMAN PARAGRAPH STYLES — use these for ALL text elements
# ============================================================

STYLE_TITLE = ParagraphStyle('AgentmanTitle',
    fontName='Helvetica-Bold', fontSize=24, textColor=CHARCOAL_900,
    spaceAfter=4, leading=30, alignment=TA_LEFT)

STYLE_SUBTITLE = ParagraphStyle('AgentmanSubtitle',
    fontName='Helvetica', fontSize=13, textColor=SLATE_600,
    spaceBefore=2, spaceAfter=6, leading=17, alignment=TA_LEFT)

STYLE_AUTHOR = ParagraphStyle('AgentmanAuthor',
    fontName='Helvetica', fontSize=11, textColor=SLATE_600,
    spaceAfter=12, leading=14, alignment=TA_LEFT)

STYLE_SECTION = ParagraphStyle('AgentmanSection',
    fontName='Helvetica-Bold', fontSize=16, textColor=CHARCOAL_900,
    spaceBefore=18, spaceAfter=6, alignment=TA_LEFT)

STYLE_SUBSECTION = ParagraphStyle('AgentmanSubsection',
    fontName='Helvetica-Bold', fontSize=13, textColor=CHARCOAL_900,
    spaceBefore=12, spaceAfter=4, alignment=TA_LEFT)

STYLE_BODY = ParagraphStyle('AgentmanBody',
    fontName='Helvetica', fontSize=11, textColor=CHARCOAL_950,
    spaceAfter=8, leading=15, alignment=TA_LEFT)

STYLE_BODY_SECONDARY = ParagraphStyle('AgentmanBodySecondary',
    fontName='Helvetica', fontSize=10, textColor=SLATE_600,
    spaceAfter=6, leading=14, alignment=TA_LEFT)

STYLE_STAT_NUMBER = ParagraphStyle('AgentmanStatNumber',
    fontName='Helvetica-Bold', fontSize=28, textColor=AGENTMAN_500,
    spaceBefore=6, spaceAfter=2, leading=34, alignment=TA_CENTER)

STYLE_STAT_LABEL = ParagraphStyle('AgentmanStatLabel',
    fontName='Helvetica', fontSize=10, textColor=SLATE_600,
    spaceBefore=0, spaceAfter=8, leading=13, alignment=TA_CENTER)

STYLE_FOOTER = ParagraphStyle('AgentmanFooter',
    fontName='Helvetica-Oblique', fontSize=8, textColor=SLATE_500,
    alignment=TA_CENTER)

STYLE_DISCLAIMER = ParagraphStyle('AgentmanDisclaimer',
    fontName='Helvetica-Oblique', fontSize=8, textColor=SLATE_500,
    spaceAfter=4, leading=11, alignment=TA_CENTER)

STYLE_CALLOUT = ParagraphStyle('AgentmanCallout',
    fontName='Helvetica', fontSize=11, textColor=CHARCOAL_950,
    spaceAfter=8, leading=15, alignment=TA_LEFT,
    backColor=AGENTMAN_75, borderPadding=12)
```

### Stat Block Layout (MANDATORY — Prevents Overlap)

**CRITICAL: Never place stat blocks as consecutive centered paragraphs.** Large font stat numbers (28pt) will overlap their labels and adjacent stats. Always use a **Table** to contain side-by-side stat blocks.

```python
def agentman_stat_row(stat_pairs, page_width=7.0*inch):
    """Build a row of stat blocks using a Table to prevent overlap.

    Args:
        stat_pairs: list of (value, label) tuples, e.g. [("$264.35", "AAPL Price"), ...]
        page_width: available content width (letter page minus margins)

    Returns:
        A Table flowable with stat blocks that will NOT overlap.
    """
    n = len(stat_pairs)
    col_w = page_width / n

    # Row 1: stat numbers (large terracotta)
    # Row 2: stat labels (small slate)
    data = [
        [Paragraph(val, STYLE_STAT_NUMBER) for val, _ in stat_pairs],
        [Paragraph(lbl, STYLE_STAT_LABEL) for _, lbl in stat_pairs],
    ]
    t = Table(data, colWidths=[col_w] * n)
    t.setStyle(TableStyle([
        ('ALIGN',   (0, 0), (-1, -1), 'CENTER'),
        ('VALIGN',  (0, 0), (-1, -1), 'MIDDLE'),
        ('TOPPADDING',    (0, 0), (-1, 0), 10),
        ('BOTTOMPADDING', (0, 0), (-1, 0), 2),
        ('TOPPADDING',    (0, 1), (-1, 1), 0),
        ('BOTTOMPADDING', (0, 1), (-1, 1), 10),
        ('GRID',    (0, 0), (-1, -1), 0, WHITE),  # invisible grid
    ]))
    return t

# Usage example — NEVER do Paragraph+Paragraph stacking for stats:
# WRONG (causes overlap):
#   elements.append(Paragraph("$264.35", STYLE_STAT_NUMBER))
#   elements.append(Paragraph("AAPL Price", STYLE_STAT_LABEL))
#
# CORRECT:
#   elements.append(agentman_stat_row([
#       ("$264.35", "AAPL Price"),
#       ("$303.33", "GOOGL Price"),
#   ]))
```

### Section Divider (Terracotta Accent Line)

```python
from reportlab.platypus import HRFlowable

def agentman_section_divider():
    """Create a 3pt terracotta accent line between sections."""
    return HRFlowable(
        width="100%", thickness=3,
        color=AGENTMAN_500, spaceBefore=6, spaceAfter=12
    )

def agentman_thin_divider():
    """Create a 1pt warm separator."""
    return HRFlowable(
        width="100%", thickness=1,
        color=AGENTMAN_150, spaceBefore=4, spaceAfter=8
    )
```

### Page 1 Template (Mandatory First-Page Content)

```python
def build_cover_page(elements, report_title, date_str, subtitle=None):
    """Build the mandatory Agentman cover page content.

    Args:
        elements: list of flowables to append to
        report_title: main title (will be prefixed with "Agentman —")
        date_str: date string for the report
        subtitle: optional subtitle (e.g. "Stock Comparison Report")
    """
    # Title — must start with "Agentman —"
    elements.append(Paragraph(f"Agentman — {report_title}", STYLE_TITLE))

    # Subtitle — if provided, uses smaller font with clear spacing
    if subtitle:
        elements.append(Paragraph(subtitle, STYLE_SUBTITLE))

    # Author — MUST appear immediately after title, non-negotiable
    elements.append(Paragraph("Author: Agentman Equity Research Assistant", STYLE_AUTHOR))

    # Date and data source
    elements.append(Paragraph(f"{date_str} | Data Source: Financial Modeling Prep", STYLE_AUTHOR))

    # Terracotta accent line
    elements.append(agentman_section_divider())

    return elements
```

### Footer (Apply to Every Page Except Cover)

```python
from reportlab.platypus import Frame, PageTemplate
from reportlab.lib.units import inch
from datetime import date

def agentman_footer(canvas, doc):
    """Draw Agentman footer on every page except the first."""
    if doc.page > 1:
        canvas.saveState()
        # Footer rule — terracotta accent line
        canvas.setStrokeColor(AGENTMAN_500)
        canvas.setLineWidth(1)
        canvas.line(0.75*inch, 0.6*inch, doc.width + 0.75*inch, 0.6*inch)
        # Footer text
        canvas.setFont('Helvetica', 8)
        canvas.setFillColor(SLATE_500)
        footer_text = f"Agentman Equity Research Assistant · {date.today().strftime('%B %d, %Y')} · Data: FMP"
        canvas.drawCentredString(doc.width/2 + 0.75*inch, 0.4*inch, footer_text)
        # Page number
        canvas.drawRightString(doc.width + 0.75*inch, 0.4*inch, f"{doc.page}")
        canvas.restoreState()
```

### Chart Colors (matplotlib)

```python
import matplotlib
import matplotlib.pyplot as plt

# Agentman chart palette — warm, monochromatic progression
CHART_COLORS = [
    '#CC785C',   # AGENTMAN_500 — primary series
    '#292322',   # CHARCOAL_900 — secondary series
    '#E3DACC',   # AGENTMAN_150 — tertiary series
    '#A65945',   # AGENTMAN_600 — fourth series
    '#8B4A38',   # AGENTMAN_700 — fifth series
    '#D97757',   # AGENTMAN_400 — sixth series
    '#475569',   # SLATE_600 — seventh series
    '#703B2D',   # AGENTMAN_800 — eighth series
]

def apply_agentman_chart_style():
    """Apply Agentman brand colors to matplotlib globally."""
    matplotlib.rcParams['axes.prop_cycle'] = matplotlib.cycler(color=CHART_COLORS)
    matplotlib.rcParams['axes.edgecolor'] = '#292322'
    matplotlib.rcParams['axes.labelcolor'] = '#141413'
    matplotlib.rcParams['xtick.color'] = '#475569'
    matplotlib.rcParams['ytick.color'] = '#475569'
    matplotlib.rcParams['grid.color'] = '#E3DACC'
    matplotlib.rcParams['figure.facecolor'] = '#FFFFFF'
    matplotlib.rcParams['font.family'] = 'sans-serif'
    matplotlib.rcParams['font.sans-serif'] = ['Calibri', 'Helvetica', 'Arial']
```

---

## fpdf2 Implementation

### Color Constants (Copy-Paste This Exactly)

```python
from fpdf import FPDF

# ============================================================
# AGENTMAN BRAND COLORS — fpdf2 RGB tuples
# ============================================================
AGENTMAN_800_RGB = (112, 59, 45)     # Badge text, deep emphasis
AGENTMAN_700_RGB = (139, 74, 56)     # Cover accent
AGENTMAN_600_RGB = (166, 89, 69)     # Negative emphasis
AGENTMAN_500_RGB = (204, 120, 92)    # PRIMARY ACCENT
AGENTMAN_400_RGB = (217, 119, 87)    # Highlight text
AGENTMAN_200_RGB = (230, 168, 144)   # Badge borders
AGENTMAN_150_RGB = (227, 218, 204)   # Table borders
AGENTMAN_100_RGB = (246, 234, 230)   # Table header fill
AGENTMAN_75_RGB  = (240, 238, 230)   # Highlight row bg
AGENTMAN_50_RGB  = (250, 249, 245)   # Alternate row bg
CHARCOAL_950_RGB = (20, 20, 19)      # Body text
CHARCOAL_900_RGB = (41, 35, 34)      # Section titles
CHARCOAL_800_RGB = (61, 55, 53)      # Secondary text
SLATE_600_RGB    = (71, 85, 105)     # Secondary text
SLATE_500_RGB    = (100, 116, 139)   # Footer text
WHITE_RGB        = (255, 255, 255)
```

### fpdf2 Table Helper

```python
def fpdf2_draw_agentman_table(pdf, headers, data, col_widths=None):
    """Draw an Agentman-styled table using fpdf2."""
    if col_widths is None:
        col_widths = [pdf.epw / len(headers)] * len(headers)

    # Header row — light warm fill + terracotta text
    pdf.set_fill_color(*AGENTMAN_100_RGB)
    pdf.set_text_color(*AGENTMAN_500_RGB)
    pdf.set_draw_color(*AGENTMAN_150_RGB)
    pdf.set_font('Helvetica', 'B', 11)
    for i, header in enumerate(headers):
        pdf.cell(col_widths[i], 10, header, border=1, fill=True, align='C')
    pdf.ln()

    # Body rows — alternating backgrounds
    pdf.set_font('Helvetica', '', 10)
    for row_idx, row in enumerate(data):
        if row_idx % 2 == 0:
            pdf.set_fill_color(*WHITE_RGB)
        else:
            pdf.set_fill_color(*AGENTMAN_50_RGB)
        pdf.set_text_color(*CHARCOAL_950_RGB)
        for i, cell in enumerate(row):
            font_style = 'B' if i == 0 else ''
            pdf.set_font('Helvetica', font_style, 10)
            pdf.cell(col_widths[i], 9, str(cell), border=1, fill=True,
                     align='L' if i == 0 else 'C')
        pdf.ln()
```

---

## weasyprint Implementation

### Complete CSS Stylesheet (Copy-Paste This Exactly)

```css
/* ============================================================
   AGENTMAN BRAND STYLESHEET — weasyprint
   Apply to ALL HTML before converting to PDF
   ============================================================ */
@page {
  size: letter;
  margin: 0.75in;
  @bottom-center {
    content: "Agentman Equity Research Assistant · Data: FMP";
    font-family: Calibri, 'Segoe UI', sans-serif;
    font-size: 8pt;
    color: #64748B;
  }
  @bottom-right {
    content: counter(page);
    font-family: Calibri, 'Segoe UI', sans-serif;
    font-size: 8pt;
    color: #64748B;
  }
}

* { font-family: Calibri, 'Segoe UI', sans-serif; box-sizing: border-box; }
body { color: #141413; font-size: 11pt; line-height: 1.4; }

/* HEADINGS — charcoal, NEVER colored */
h1 { color: #292322; font-size: 24pt; font-weight: bold; margin-bottom: 4pt; }
h2 { color: #292322; font-size: 18pt; font-weight: bold; border-bottom: 3px solid #CC785C; padding-bottom: 4pt; margin-top: 18pt; }
h3 { color: #292322; font-size: 14pt; font-weight: bold; border-bottom: 2px solid #CC785C; padding-bottom: 3pt; margin-top: 14pt; }
h4 { color: #3D3735; font-size: 12pt; font-weight: bold; margin-top: 10pt; }

/* AUTHOR LINE — must appear on page 1 */
.author { color: #475569; font-size: 11pt; margin-bottom: 12pt; }
.date { color: #475569; font-size: 11pt; margin-bottom: 16pt; }

/* TABLE HEADERS — CRITICAL: light fill + terracotta text, NEVER dark */
table { border-collapse: collapse; width: 100%; margin: 12pt 0; }
th, thead td {
  background-color: #F6EAE6 !important;
  color: #CC785C !important;
  font-weight: 600;
  font-size: 11pt;
  padding: 9px 14px;
  border-bottom: 1px solid #E3DACC;
  text-align: center;
}

/* TABLE BODY */
td {
  color: #141413;
  padding: 9px 14px;
  border-bottom: 1px solid #E3DACC;
  font-size: 10pt;
}
td:first-child { font-weight: 600; color: #141413; text-align: left; }
tr:nth-child(odd) td { background-color: #FFFFFF; }
tr:nth-child(even) td { background-color: #FAF9F5; }
tr.highlight td { background-color: #F0EEE6; }

/* STAT BLOCKS — use .stat-row container to prevent overlap */
.stat-row {
  display: flex;
  justify-content: space-evenly;
  align-items: flex-start;
  gap: 16px;
  margin: 18pt 0;
}
.stat-block {
  flex: 1;
  text-align: center;
  padding: 12px 8px;
  min-width: 0;         /* prevent flex overflow */
}
.stat-number {
  font-size: 28pt;
  font-weight: bold;
  color: #CC785C;
  text-align: center;
  line-height: 1.2;     /* CRITICAL: prevents overlap with label */
  margin-bottom: 4pt;
}
.stat-label {
  font-size: 10pt;
  color: #475569;
  text-align: center;
  line-height: 1.3;
  margin-top: 0;
}
/* NEVER place .stat-number and .stat-label as siblings outside a .stat-block container */

/* EMPHASIS — terracotta family only, NEVER green/red/blue */
.positive { color: #CC785C; font-weight: 700; }
.negative { color: #A65945; font-weight: 700; }
.highlight-text { color: #D97757; font-weight: 600; }
.neutral { color: #141413; font-weight: 600; }

/* CALLOUT BLOCKS */
.callout { background: #F0EEE6; border-radius: 4px; padding: 16px 20px; margin: 12pt 0; }
.callout h3, .callout h4 { color: #141413; font-weight: bold; border: none; margin-top: 0; }
.callout p { color: #475569; }

/* BADGES — all same color, tier via text wording */
.badge {
  display: inline-block;
  background: #F6EAE6; color: #703B2D;
  border: 1px solid #E6A890;
  padding: 3px 10px; border-radius: 4px;
  font-size: 10pt; font-weight: 600;
}

/* SCORE BARS */
.score-bar { background: #E3DACC; height: 6px; border-radius: 3px; width: 100%; }
.score-fill { background: #CC785C; height: 6px; border-radius: 3px; }

/* FOOTER */
footer, .footer { font-size: 8pt; color: #64748B; text-align: center; margin-top: 24pt; }

/* DISCLAIMER */
.disclaimer { font-size: 8pt; color: #64748B; font-style: italic; text-align: center; margin-top: 24pt; }

/* ACCENT LINE */
hr { border: none; border-top: 3px solid #CC785C; margin: 12pt 0; }
hr.thin { border-top: 1px solid #E3DACC; }

/* BANNED: green, red, amber, blue, navy, gradients, shadows, opacity, box-shadow */
```

---

## Element-to-Color Quick Reference

| Report Element | Color | Hex | Constant |
|---|---|---|---|
| Table header fill | Light brand | #F6EAE6 | `AGENTMAN_100` |
| Table header text | Terracotta | #CC785C | `AGENTMAN_500` |
| Table borders | Warm separator | #E3DACC | `AGENTMAN_150` |
| Section titles | Charcoal | #292322 | `CHARCOAL_900` |
| Section underline | Terracotta | #CC785C | `AGENTMAN_500` |
| Body text | Charcoal | #141413 | `CHARCOAL_950` |
| Secondary text | Slate | #475569 | `SLATE_600` |
| Footer/meta | Slate muted | #64748B | `SLATE_500` |
| Alternate row bg | Cream | #FAF9F5 | `AGENTMAN_50` |
| Highlight row bg | Warm cream | #F0EEE6 | `AGENTMAN_75` |
| Stat numbers | Terracotta | #CC785C | `AGENTMAN_500` |
| Positive emphasis | Terracotta bold | #CC785C | `AGENTMAN_500` |
| Negative emphasis | Deep terracotta bold | #A65945 | `AGENTMAN_600` |
| Badges (all tiers) | Light brand bg + deep text | #F6EAE6 bg, #703B2D text | `AGENTMAN_100` + `AGENTMAN_800` |
| Cover accent | Terracotta | #8B4A38 | `AGENTMAN_700` |

---

## Typography

**Font**: Helvetica (built-in to reportlab) or Calibri (if registered). Use Calibri when available, Helvetica as fallback.

| Element | Size | Weight | Alignment |
|---|---|---|---|
| Report title | 24pt (leading 30) | Bold | Left |
| Subtitle | 13pt (leading 17) | Regular | Left |
| Section heading (h2) | 16–18pt | Bold | Left |
| Subsection heading (h3) | 13–14pt | Bold | Left |
| Body text | 10.5–11pt (leading 15) | Regular | Left |
| Stat number | 28pt (leading 34) | Bold | Center (in table cell) |
| Stat label | 10pt (leading 13) | Regular | Center (in table cell) |
| Table header | 11pt | Bold (600) | Center |
| Table body | 10pt | Regular | Center (data), Left (labels) |
| Footer | 8pt | Italic | Center |

**NEVER center body paragraphs.** Left-align all body text.

---

## Banned Colors

| Color | Hex | Why Banned |
|---|---|---|
| Green (success) | #10B981 | Traffic-light aesthetic (except ✓ symbol) |
| Amber (warning) | #F59E0B | Dashboard alert feel |
| Red (error) | #EF4444 | Alarm aesthetic (except ✗ symbol) |
| Blue (info) | #3B82F6 | Generic corporate, dilutes brand |
| Navy | #1B3A5C | Corporate default, not Agentman |
| Cyan | #0891B2 | Too techy |
| Pure black | #000000 | Use #141413 (CHARCOAL_950) instead |
| Generic grays | #333, #666, #999, #ccc | Use brand palette equivalents |
| Any gradient | N/A | Solid fills only |

**The ONE exception**: `#10B981` (green ✓) and `#EF4444` (red ✗) for comparison table symbols ONLY — never for text, backgrounds, borders, or any other element.

---

## Overlap Prevention Rules

**Text overlap is the #1 layout defect.** Follow these rules strictly:

1. **Stat blocks MUST use `agentman_stat_row()` table layout.** Never stack `STYLE_STAT_NUMBER` and `STYLE_STAT_LABEL` paragraphs sequentially — the 28pt number will overlap the 10pt label. Always wrap stat pairs in a Table.
2. **Every ParagraphStyle MUST have explicit `leading`** (line height). The `leading` value must be at least 1.2x the `fontSize`. Without explicit leading, reportlab defaults can cause text to overlap.
3. **Title + subtitle need `STYLE_TITLE` (leading=30) then `STYLE_SUBTITLE` (leading=17).** Never use the same style for both — the large title font will collide with text below if spacing is insufficient.
4. **Use `Spacer(1, 12)` between unrelated content blocks** to guarantee breathing room.
5. **Side-by-side content MUST use a Table**, not separate centered Paragraphs. Centered paragraphs stack vertically and overlap.
6. **Set `spaceBefore` and `spaceAfter` on every style.** Never rely on implicit spacing.

---

## Pre-Generation Checklist

Before executing ANY PDF generation code, verify:

- [ ] **No text overlap** — Stat blocks use `agentman_stat_row()` table layout, title/subtitle have separate styles with adequate leading
- [ ] **Color constants defined** — ALL Agentman constants are in the script
- [ ] **No library defaults** — No `colors.black`, `colors.grey`, `getSampleStyleSheet()`, default table styles
- [ ] **Document author set** — `"Agentman Equity Research Assistant"` in metadata
- [ ] **Page 1 author line** — "Author: Agentman Equity Research Assistant" displayed before data
- [ ] **Title starts with "Agentman —"**
- [ ] **Table headers are LIGHT** — `AGENTMAN_100` (#F6EAE6) fill + `AGENTMAN_500` (#CC785C) text
- [ ] **No dark table headers** — No navy, dark blue, dark brown, charcoal, or any saturated color as header fill
- [ ] **No banned colors** — No green, red, amber, blue, navy, or generic grays
- [ ] **Warm borders** — `AGENTMAN_150` (#E3DACC), not black/gray
- [ ] **Alternating rows** — White / `AGENTMAN_50` (#FAF9F5) cream
- [ ] **Terracotta budget** — Max 3 `AGENTMAN_500` elements per section
- [ ] **Footer on every page** — "Agentman Equity Research Assistant · {date} · Data: FMP"
- [ ] **No gradients, shadows, or opacity** — Solid fills only
- [ ] **Charts use brand palette** — Never library defaults
- [ ] **Visual weight feels 70/20/10** — Mostly charcoal, some cream, rare terracotta

Included Files

  • SKILL.md(25.8 KB)
  • _archive/skill-package.zip(8.4 KB)

Ready to use this skill?

Try it now in your favorite AI, or set up MCP for persistent access.