sarcoidosis detailsb

Reading File
Finding Sources
Searching PubMed

sarcoidosis diagnosis treatment

Reading File
Reading File
Finding Sources
Finding Sources
Reading File
Finding Sources
Reading File
Reading File
Reading File
Finding Sources
Loading Image
Reading File
Loading Image
I now have comprehensive information to write a detailed answer. Let me compile everything.

Sarcoidosis - Comprehensive Overview

Definition & Etiology

Sarcoidosis is a multisystem granulomatous disease of unknown etiology, most likely caused by an aberrant immune response to an environmental or infectious agent in a genetically susceptible host. It is the most common granulomatous lung disease not attributed to a known infectious cause. The hallmark pathologic lesion is the noncaseating (non-necrotizing) granuloma.
  • The initial pulmonary lesion is an alveolitis characterized by accumulation of CD4+ T lymphocytes
  • This is followed by formation of epithelioid cell granulomas with multinucleated giant cells
  • Proposed triggers include mycobacteria, organic dusts, certain HLA haplotypes, and abnormalities of T-cell receptor signaling
  • Multiple cytokines (TNF-alpha, IL-2, IL-12) drive granuloma formation

Epidemiology

  • Affects people worldwide, but prevalence varies strongly by race and geography
  • African Americans: incidence ~35.5 per 100,000 (highest risk in the US); tends to be more severe and chronic
  • Scandinavians: also disproportionately affected
  • Slight female predominance
  • Peak onset: 20-40 years of age
  • Genetic loci for susceptibility identified through the SAGA study (African Americans) and broader racial/ethnic studies

Pathology

Noncaseating granulomas composed of:
  • Epithelioid histiocytes (macrophages)
  • Multinucleated Langhans-type giant cells
  • Surrounded by CD4+ T lymphocytes and fibroblasts
  • NO central necrosis (distinguishes from TB and fungal infection)
Sarcoidosis lymph node histology - noncaseating granulomas with giant cells (G), histiocytes (H), and lymphocytes (L)
Sarcoidosis in a lymph node: Noncaseating granulomas with giant cells (G), histiocytes (H), and lymphocytes (L) - Cummings Otolaryngology

Clinical Presentations

1. Acute Sarcoidosis - Lofgren Syndrome

  • Triad: Erythema nodosum + bilateral hilar adenopathy + polyarthritis (ankles, feet, knees)
  • May also include fever and uveitis
  • More common in Europeans and white populations; found in <5% of Black patients
  • ~10% have a normal chest radiograph
  • Excellent prognosis: 70-80% remission within several months
  • Managed with NSAIDs; corticosteroids if arthritis is disabling

2. Chronic/Progressive Form

  • Insidious onset of dyspnea, dry cough, hilar adenopathy + infiltrates
  • New skin lesions on trunk and extremities
  • Visual changes (uveitis)
  • Multiple flares requiring repeated treatment

3. Asymptomatic Sarcoidosis

  • Up to two-thirds of patients are asymptomatic, diagnosed incidentally from bilateral hilar adenopathy on chest X-ray
  • More common in white patients

Organ System Involvement

Pulmonary (90%)

  • Most common involved organ
  • Symptoms in 40-60% of patients: progressive cough (nonproductive), dyspnea on exertion, chest tightness, wheezing (with endobronchial disease)
  • Hemoptysis occurs in fibrocystic (stage IV) sarcoidosis with bronchiectasis
  • Physical exam is often normal; crackles heard in <20%; clubbing is rare
  • Progressive disease leads to restrictive pattern on PFTs with decreased DLCO

Chest Radiograph - Scadding Staging

StageFindingsFrequencyPrognosis
0Normal CXR<15%-
IBilateral hilar adenopathy only30-50%Excellent; often spontaneous remission
IIHilar adenopathy + pulmonary infiltrates40-60%Variable
IIIPulmonary infiltrates only (no adenopathy)10-20%Variable
IVPulmonary fibrosis/fibrocystic changes<15%Poor; mycetoma risk
Bilateral hilar adenopathy (BHA) often has a symmetric "potato node" appearance. Infiltrates most commonly appear as linear or reticulonodular markings in the middle and upper lung fields.
Chest X-ray showing bilateral hilar adenopathy and linear parenchymal densities in pulmonary sarcoidosis
Bilateral hilar adenopathy and linear parenchymal densities in pulmonary sarcoidosis - Cummings Otolaryngology

Cutaneous (25-35%)

  • Classic: Red-brown, non-scaly papules and plaques on the face (especially around nose and mouth)
  • Lupus pernio: Violaceous plaques on the alar rim of the nose - pathognomonic, highly associated with upper airway granulomatous infiltration, pulmonary fibrosis, and chronic disease
  • Lesions also form within scars and tattoos
  • Other: ichthyosis, alopecia, verrucous plaques, psoriasiform lesions
  • Erythema nodosum (non-specific, part of Lofgren syndrome) - panniculitis, NOT granulomas

Ocular (~25%)

  • Anterior uveitis (most common) - eye pain, redness, photophobia
  • Posterior uveitis, keratic precipitates
  • Lacrimal gland involvement causing dry eyes
  • Optic nerve involvement can cause blindness

Cardiac

  • Conduction defects (most common cardiac manifestation): AV block, bundle branch block
  • Ventricular arrhythmias and sudden cardiac death
  • Cardiomyopathy
  • Detected with ECG, echocardiography, MRI, and FDG-PET scanning

Neurologic (Neurosarcoidosis, ~5-10%)

  • CN VII palsy (most common cranial nerve involved)
  • Hypothalamic/pituitary dysfunction (diabetes insipidus)
  • Optic chiasm involvement - blindness
  • Peripheral neuropathy
  • Meningitis, encephalopathy, myelopathy
  • Hydrocephalus

Exocrine Glands - Heerfordt Syndrome (Uveoparotid Fever)

  • Tetrad: Parotid enlargement + uveitis + facial nerve palsy + low-grade fever
  • Usually accompanied by bilateral hilar adenopathy

Musculoskeletal

  • Polyarthritis (especially in Lofgren syndrome)
  • Bone involvement (usually asymptomatic, "lacy" pattern on plain X-ray)
  • Myopathy with elevated CPK, aldolase, weakness (uncommon)

Gastrointestinal / Hepatic

  • Hepatosplenomegaly; granulomatous hepatitis
  • Elevated liver enzymes

Endocrine/Metabolic

  • Hypercalciuria (more common than hypercalcemia) - due to 1α-hydroxylase activity of granuloma macrophages converting 25-OH-D to 1,25(OH)2-D
  • Hypercalcemia - can cause nephrocalcinosis and renal failure
  • Hypothalamic/pituitary sarcoidosis

Renal

  • Kidney stones (most common)
  • Granulomatous interstitial nephritis
  • Nephrocalcinosis from chronic hypercalcemia

ENT

  • Sinusitis, nasal mucosal involvement
  • Sensorineural hearing loss (SNHL)
  • Facial nerve paralysis (Heerfordt syndrome)
  • Laryngeal involvement

Diagnosis

Diagnosis is one of exclusion - established by:
  1. Compatible clinical and radiographic findings
  2. Tissue biopsy showing noncaseating granulomas
  3. Exclusion of other causes of NCG: TB, fungal infections, foreign body reaction, berylliosis, Langerhans cell histiocytosis, hypersensitivity pneumonitis

Best Biopsy Sites (by accessibility and yield)

  • Endobronchial/transbronchial lung biopsy (highest yield ~70-90%)
  • Skin lesions (if present)
  • Peripheral lymph nodes
  • Liver or conjunctiva (lower yield but accessible)
  • EBUS-guided mediastinal lymph node biopsy

Lab Findings

TestFindingNotes
Serum ACE (SACE)Elevated in ~60%Non-specific; correlates with granuloma burden; not diagnostic alone
CalciumHypercalciuria > hypercalcemiaDue to granuloma 1α-hydroxylase
25-OH Vitamin DLow1,25-OH Vit D often elevated
LFTsMildly elevatedHepatic involvement
CBCLymphopenia, anemiaNon-specific
sIL-2RElevatedMore sensitive than ACE but not specific
BALCD4:CD8 ratio >3.5Supportive but not diagnostic

Imaging

  • Chest X-ray: Scadding staging (see above)
  • HRCT chest: Superior to CXR; perilymphatic nodules, upper lobe predominance, lymphadenopathy
  • FDG-PET/CT: Best for detecting cardiac and neurologic involvement; assessing disease activity; monitoring therapy
  • MRI: For neurosarcoidosis and cardiac sarcoidosis

Poor Prognostic Features

  • Lupus pernio
  • Chronic uveitis
  • Hypercalcemia or nephrocalcinosis
  • Nasal mucosal involvement
  • Bone cysts
  • Neurosarcoidosis
  • Cardiac involvement
  • Black race
  • Stage III/IV chest radiograph
  • Prolonged disease (>2 years)

Treatment

No FDA-approved systemic therapies for sarcoidosis. Treatment is based on clinical need.

When NOT to Treat

  • Stage I asymptomatic sarcoidosis (often self-limited)
  • Lofgren syndrome (usually managed with NSAIDs + rest)
  • Many patients have spontaneous remission

Indications for Systemic Treatment (Table 53-6, Fishman's)

  • Threatened organ failure
  • Severe ocular, cardiac, or neurologic disease
  • Uveitis unresponsive to topical corticosteroids
  • Progressive, persistent, or symptomatic pulmonary disease
  • Disfiguring cutaneous disease, especially lupus pernio
  • Persistent hypercalcemia, renal or hepatic dysfunction
  • Symptomatic hepatic/splenic enlargement

Corticosteroids (First Line)

  • Prednisone 20-40 mg/day (or 0.5 mg/kg/day)
  • Provides prompt symptomatic relief and reverses organ dysfunction
  • Taper slowly over months (often 12-24 months total)
  • Renal sarcoidosis: start at 1 mg/kg/day (max 60 mg/day)
  • Cutaneous: intralesional injections every 2-4 weeks for localized disease; systemic for widespread disease

Steroid-Sparing / Second-Line Agents

  • Methotrexate (most commonly used steroid-sparer)
  • Hydroxychloroquine (especially for skin/hypercalcemia)
  • Azathioprine
  • Mycophenolate mofetil
  • Cyclophosphamide, chlorambucil (rarely)

Biologics (for Refractory Disease)

  • Infliximab (anti-TNF-alpha) - most evidence; effective for refractory pulmonary, cutaneous, and neurologic sarcoidosis
  • Adalimumab (anti-TNF-alpha)
  • Etanercept - less effective than infliximab
A 2025 meta-analysis (PMID 40393718) in Thorax confirmed the efficacy of biologic and targeted synthetic therapies in sarcoidosis, providing updated evidence for anti-TNF agents in refractory disease.

Monitoring After Remission

  • Monitoring for at least 3 years after presumed disease remission
  • Longer observation for serious pulmonary or extrapulmonary manifestations

Prognosis

FormPrognosis
Lofgren syndrome70-80% remission without treatment
Stage I asymptomatic>70% spontaneous remission
Stage II/III~50-70% remission with treatment
Stage IV (fibrocystic)Poor; risk of mycetoma, respiratory failure
Cardiac sarcoidosisIncreased mortality from arrhythmia/sudden death
NeurosarcoidosisIncreased morbidity; unpredictable

Key Associations to Remember

FindingAssociation
Bilateral hilar adenopathyMost common intrathoracic finding
Lupus pernioChronic disease, upper airway involvement, pulmonary fibrosis
Erythema nodosum + BHALofgren syndrome (good prognosis)
Heerfordt syndromeParotid + uveitis + CN VII palsy + fever
Hypercalcemia1α-hydroxylase activity in granuloma macrophages
Noncaseating granulomaPathologic hallmark
ACE elevated~60%; correlates with granuloma burden
CN VII most affected cranial nerveSarcoidosis neurologic involvement

Sources: Fishman's Pulmonary Diseases and Disorders | Textbook of Family Medicine 9e | Fitzpatrick's Dermatology | Andrews' Diseases of the Skin | Cummings Otolaryngology | Comprehensive Clinical Nephrology | PMID 40393718 - Systematic review of biologics in sarcoidosis, Thorax 2025

Create a reference PDF guide on sarcoidosis diagnosis and staging

Reading File
Reading File
Running Command

mkdir -p /tmp/workspace/sarcoidosis-pdf

Writing File

~/sarcoidosis-pdf/generate_pdf.py

from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
    HRFlowable, KeepTogether, PageBreak
)
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib import colors
from reportlab.lib.pagesizes import A4
from reportlab.lib.units import mm, cm
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
from reportlab.platypus.flowables import HRFlowable

# ── Color palette ──────────────────────────────────────────────────────────────
DARK_TEAL   = colors.HexColor("#1B4F6B")
MID_TEAL    = colors.HexColor("#2980B9")
LIGHT_TEAL  = colors.HexColor("#D6EAF8")
ACCENT      = colors.HexColor("#E74C3C")
GOLD        = colors.HexColor("#F39C12")
LIGHT_GREY  = colors.HexColor("#F2F3F4")
MID_GREY    = colors.HexColor("#BDC3C7")
DARK_GREY   = colors.HexColor("#2C3E50")
WHITE       = colors.white

W, H = A4
MARGIN = 20 * mm

# ── Document ───────────────────────────────────────────────────────────────────
doc = SimpleDocTemplate(
    "/tmp/workspace/sarcoidosis-pdf/Sarcoidosis_Diagnosis_Staging_Guide.pdf",
    pagesize=A4,
    leftMargin=MARGIN, rightMargin=MARGIN,
    topMargin=25 * mm, bottomMargin=20 * mm,
    title="Sarcoidosis: Diagnosis & Staging Reference Guide",
    author="Orris Medical Reference",
    subject="Sarcoidosis Clinical Reference",
)

styles = getSampleStyleSheet()

# Custom styles
def S(name, **kw):
    return ParagraphStyle(name, **kw)

COVER_TITLE = S("CoverTitle", fontSize=28, textColor=WHITE,
                fontName="Helvetica-Bold", alignment=TA_CENTER, leading=36)
COVER_SUB   = S("CoverSub",   fontSize=14, textColor=LIGHT_TEAL,
                fontName="Helvetica", alignment=TA_CENTER, leading=20)
COVER_DATE  = S("CoverDate",  fontSize=10, textColor=MID_GREY,
                fontName="Helvetica", alignment=TA_CENTER)

SEC_TITLE   = S("SecTitle",   fontSize=14, textColor=WHITE,
                fontName="Helvetica-Bold", leading=18)
SUBSEC      = S("SubSec",     fontSize=11, textColor=DARK_TEAL,
                fontName="Helvetica-Bold", leading=15, spaceBefore=8)
BODY        = S("Body",       fontSize=9,  textColor=DARK_GREY,
                fontName="Helvetica", leading=13, alignment=TA_JUSTIFY)
BULLET      = S("Bullet",     fontSize=9,  textColor=DARK_GREY,
                fontName="Helvetica", leading=13, leftIndent=12,
                firstLineIndent=-8)
BOLD_BODY   = S("BoldBody",   fontSize=9,  textColor=DARK_GREY,
                fontName="Helvetica-Bold", leading=13)
CAPTION     = S("Caption",    fontSize=8,  textColor=colors.HexColor("#7F8C8D"),
                fontName="Helvetica-Oblique", alignment=TA_CENTER, leading=11)
TABLE_HDR   = S("TblHdr",     fontSize=8.5, textColor=WHITE,
                fontName="Helvetica-Bold", alignment=TA_CENTER, leading=11)
TABLE_CELL  = S("TblCell",    fontSize=8.5, textColor=DARK_GREY,
                fontName="Helvetica", leading=11)
TABLE_CELL_C= S("TblCellC",   fontSize=8.5, textColor=DARK_GREY,
                fontName="Helvetica", alignment=TA_CENTER, leading=11)
WARN_TEXT   = S("WarnText",   fontSize=8.5, textColor=colors.HexColor("#6C1B1B"),
                fontName="Helvetica-Bold", leading=12)
NOTE_TEXT   = S("NoteText",   fontSize=8.5, textColor=colors.HexColor("#154360"),
                fontName="Helvetica", leading=12, alignment=TA_JUSTIFY)
FOOTER_TEXT = S("FooterText", fontSize=7, textColor=MID_GREY,
                fontName="Helvetica-Oblique", alignment=TA_CENTER)

TW = W - 2 * MARGIN  # usable text width

# ─────────────────────────────────────────────────────────────────────────────
# Helper functions
# ─────────────────────────────────────────────────────────────────────────────
def section_header(title, color=DARK_TEAL):
    tbl = Table([[Paragraph(title, SEC_TITLE)]], colWidths=[TW])
    tbl.setStyle(TableStyle([
        ("BACKGROUND",  (0,0), (-1,-1), color),
        ("TOPPADDING",  (0,0), (-1,-1), 6),
        ("BOTTOMPADDING",(0,0),(-1,-1), 6),
        ("LEFTPADDING", (0,0), (-1,-1), 10),
        ("RIGHTPADDING",(0,0), (-1,-1), 10),
        ("ROUNDEDCORNERS", [4,4,4,4]),
    ]))
    return tbl

def bullet(text, bold_prefix=""):
    if bold_prefix:
        content = f"<b>{bold_prefix}</b> {text}"
    else:
        content = text
    return Paragraph(f"• {content}", BULLET)

def hr(color=MID_GREY, thickness=0.5):
    return HRFlowable(width="100%", thickness=thickness, color=color, spaceAfter=4, spaceBefore=4)

def note_box(text, bg=colors.HexColor("#EBF5FB"), border=MID_TEAL):
    tbl = Table([[Paragraph(text, NOTE_TEXT)]], colWidths=[TW])
    tbl.setStyle(TableStyle([
        ("BACKGROUND",  (0,0), (-1,-1), bg),
        ("LINEABOVE",   (0,0), (-1, 0), 2, border),
        ("TOPPADDING",  (0,0), (-1,-1), 8),
        ("BOTTOMPADDING",(0,0),(-1,-1), 8),
        ("LEFTPADDING", (0,0), (-1,-1), 10),
        ("RIGHTPADDING",(0,0), (-1,-1), 10),
    ]))
    return tbl

def warn_box(text):
    return note_box(f"⚠  {text}",
                    bg=colors.HexColor("#FDEDEC"),
                    border=ACCENT)

def key_box(text):
    return note_box(f"🔑  {text}",
                    bg=colors.HexColor("#FDFEFE"),
                    border=GOLD)

def styled_table(header_row, data_rows, col_widths, alt_color=LIGHT_GREY,
                 header_color=DARK_TEAL):
    """Build a styled table with alternating row shading."""
    all_rows = [header_row] + data_rows
    # convert strings to Paragraphs
    formatted = []
    for i, row in enumerate(all_rows):
        if i == 0:
            fmt_row = [Paragraph(str(c), TABLE_HDR) for c in row]
        else:
            fmt_row = [Paragraph(str(c), TABLE_CELL) for c in row]
        formatted.append(fmt_row)

    tbl = Table(formatted, colWidths=col_widths, repeatRows=1)
    style_cmds = [
        ("BACKGROUND",   (0,0), (-1,0),  header_color),
        ("TEXTCOLOR",    (0,0), (-1,0),  WHITE),
        ("FONTNAME",     (0,0), (-1,0),  "Helvetica-Bold"),
        ("FONTSIZE",     (0,0), (-1,-1), 8.5),
        ("TOPPADDING",   (0,0), (-1,-1), 5),
        ("BOTTOMPADDING",(0,0), (-1,-1), 5),
        ("LEFTPADDING",  (0,0), (-1,-1), 6),
        ("RIGHTPADDING", (0,0), (-1,-1), 6),
        ("GRID",         (0,0), (-1,-1), 0.4, MID_GREY),
        ("VALIGN",       (0,0), (-1,-1), "MIDDLE"),
    ]
    for i in range(1, len(all_rows)):
        if i % 2 == 0:
            style_cmds.append(("BACKGROUND", (0,i), (-1,i), alt_color))
    tbl.setStyle(TableStyle(style_cmds))
    return tbl

# ─────────────────────────────────────────────────────────────────────────────
# COVER PAGE
# ─────────────────────────────────────────────────────────────────────────────
def make_cover():
    elements = []

    # Full-width teal banner
    banner = Table(
        [[Paragraph("SARCOIDOSIS", COVER_TITLE)],
         [Paragraph("Diagnosis &amp; Staging", COVER_TITLE)],
         [Paragraph("Reference Guide", COVER_TITLE)],
         [Spacer(1, 6)],
         [Paragraph("Pulmonary · Extrapulmonary · Radiologic Staging · Diagnostic Workup", COVER_SUB)],
        ],
        colWidths=[TW]
    )
    banner.setStyle(TableStyle([
        ("BACKGROUND",   (0,0), (-1,-1), DARK_TEAL),
        ("TOPPADDING",   (0,0), (-1,-1), 14),
        ("BOTTOMPADDING",(0,0), (-1,-1), 14),
        ("LEFTPADDING",  (0,0), (-1,-1), 20),
        ("RIGHTPADDING", (0,0), (-1,-1), 20),
    ]))
    elements.append(Spacer(1, 20*mm))
    elements.append(banner)
    elements.append(Spacer(1, 10*mm))

    # Subtitle strip
    strip = Table([[
        Paragraph("Clinical Quick Reference for Physicians &amp; Medical Students", COVER_SUB)
    ]], colWidths=[TW])
    strip.setStyle(TableStyle([
        ("BACKGROUND",   (0,0), (-1,-1), MID_TEAL),
        ("TOPPADDING",   (0,0), (-1,-1), 8),
        ("BOTTOMPADDING",(0,0), (-1,-1), 8),
    ]))
    elements.append(strip)
    elements.append(Spacer(1, 8*mm))

    # Contents overview box
    contents = [
        ["01", "Definition &amp; Pathogenesis"],
        ["02", "Epidemiology &amp; Risk Factors"],
        ["03", "Clinical Presentations (Acute vs. Chronic)"],
        ["04", "Organ System Involvement"],
        ["05", "Scadding Radiologic Staging System"],
        ["06", "Diagnostic Criteria &amp; Workup"],
        ["07", "Laboratory &amp; Biomarker Findings"],
        ["08", "Indications for Treatment"],
        ["09", "Prognostic Features"],
        ["10", "Key Clinical Associations"],
    ]
    tbl = Table(
        [[Paragraph(f"<b>{n}</b>", ParagraphStyle("cn", fontSize=11, textColor=MID_TEAL,
                    fontName="Helvetica-Bold")),
          Paragraph(t, ParagraphStyle("ct", fontSize=11, textColor=DARK_GREY,
                    fontName="Helvetica"))]
         for n, t in contents],
        colWidths=[18*mm, TW - 18*mm]
    )
    tbl.setStyle(TableStyle([
        ("TOPPADDING",   (0,0), (-1,-1), 5),
        ("BOTTOMPADDING",(0,0), (-1,-1), 5),
        ("LEFTPADDING",  (0,0), (-1,-1), 8),
        ("LINEBELOW",    (0,0), (-1,-2), 0.3, MID_GREY),
        ("BACKGROUND",   (0,0), (-1,-1), LIGHT_GREY),
        ("BACKGROUND",   (0,0), (-1, 0), colors.HexColor("#FDFEFE")),
    ]))
    elements.append(tbl)
    elements.append(Spacer(1, 8*mm))
    elements.append(Paragraph(
        "Sources: Fishman's Pulmonary Diseases · Textbook of Family Medicine 9e · "
        "Fitzpatrick's Dermatology · Cummings Otolaryngology · Harrison's Principles of "
        "Internal Medicine 22E · Thorax 2025 (PMID 40393718)",
        FOOTER_TEXT
    ))
    elements.append(PageBreak())
    return elements

# ─────────────────────────────────────────────────────────────────────────────
# SECTION 1 – Definition & Pathogenesis
# ─────────────────────────────────────────────────────────────────────────────
def sec1():
    E = []
    E.append(section_header("01  DEFINITION & PATHOGENESIS"))
    E.append(Spacer(1, 4))
    E.append(Paragraph(
        "Sarcoidosis is a <b>multisystem granulomatous disease of unknown etiology</b>, "
        "most likely caused by an aberrant immune response to an environmental or infectious "
        "antigen in a genetically susceptible host. It is the most common granulomatous lung "
        "disease not attributed to a known infectious cause.",
        BODY))
    E.append(Spacer(1, 5))

    E.append(Paragraph("Pathogenesis Sequence", SUBSEC))
    steps = [
        ("Step 1 – Antigen exposure",
         "An unidentified antigen (possibly mycobacterial, organic dust, or beryllium) is "
         "inhaled or encountered in susceptible individuals."),
        ("Step 2 – Alveolitis",
         "CD4+ T lymphocytes accumulate at sites of inflammation, releasing IL-2, IFN-γ, "
         "and TNF-α, driving macrophage activation."),
        ("Step 3 – Granuloma formation",
         "Activated macrophages differentiate into epithelioid cells, which cluster to form "
         "noncaseating granulomas with multinucleated Langhans-type giant cells."),
        ("Step 4 – Resolution or fibrosis",
         "In ~70% of cases, granulomas resolve spontaneously. In chronic disease, persistent "
         "inflammation leads to irreversible fibrosis (stage IV)."),
    ]
    for bold, text in steps:
        E.append(bullet(text, bold_prefix=bold))
    E.append(Spacer(1, 4))

    E.append(key_box(
        "Hallmark Pathology: Noncaseating (non-necrotizing) epithelioid granulomas — "
        "NO central necrosis. Central necrosis suggests tuberculosis or fungal infection."
    ))
    E.append(Spacer(1, 5))

    E.append(Paragraph("Implicated Triggers", SUBSEC))
    triggers = [
        ("Infectious", "Mycobacteria, Propionibacterium acnes"),
        ("Environmental", "Organic dusts, mold, pine tree pollen, insecticides"),
        ("Occupational", "Beryllium (chronic beryllium disease must be excluded)"),
        ("Genetic", "HLA-DRB1 alleles, BTNL2 gene polymorphisms (SAGA study — African Americans)"),
    ]
    tbl = styled_table(
        ["Category", "Proposed Trigger"],
        [[a, b] for a, b in triggers],
        col_widths=[35*mm, TW - 35*mm],
        header_color=MID_TEAL,
    )
    E.append(tbl)
    E.append(Spacer(1, 8))
    return E

# ─────────────────────────────────────────────────────────────────────────────
# SECTION 2 – Epidemiology
# ─────────────────────────────────────────────────────────────────────────────
def sec2():
    E = []
    E.append(section_header("02  EPIDEMIOLOGY & RISK FACTORS"))
    E.append(Spacer(1, 4))

    epi = [
        ["Peak age of onset", "20–40 years (bimodal peak in women: 45–65 yrs)"],
        ["Sex", "Slight female predominance"],
        ["African Americans (US)", "Incidence ~35.5 per 100,000; more severe, chronic course"],
        ["Scandinavians", "High incidence; often acute (Lofgren syndrome)"],
        ["European whites", "Lower incidence; favor acute presentation"],
        ["Worldwide", "Any race/ethnicity; global distribution"],
    ]
    tbl = styled_table(
        ["Parameter", "Detail"],
        epi,
        col_widths=[55*mm, TW - 55*mm],
        header_color=DARK_TEAL,
    )
    E.append(tbl)
    E.append(Spacer(1, 6))
    E.append(note_box(
        "<b>Note:</b> Sarcoidosis is considered a 'great imitator' alongside syphilis due to its "
        "protean presentations and ability to mimic almost any organ system disease."
    ))
    E.append(Spacer(1, 8))
    return E

# ─────────────────────────────────────────────────────────────────────────────
# SECTION 3 – Clinical Presentations
# ─────────────────────────────────────────────────────────────────────────────
def sec3():
    E = []
    E.append(section_header("03  CLINICAL PRESENTATIONS"))
    E.append(Spacer(1, 4))

    # Two-column layout
    col1 = [
        Paragraph("Acute – Lofgren Syndrome", SUBSEC),
        Spacer(1, 3),
        bullet("Erythema nodosum (tender, red-brown shin nodules)"),
        bullet("Bilateral hilar adenopathy on CXR"),
        bullet("Polyarthritis — ankles, feet, knees"),
        bullet("Low-grade fever and uveitis"),
        Spacer(1, 4),
        Paragraph("Prognosis: <b>70–80% remission</b> within months", BODY),
        Spacer(1, 4),
        note_box("• More common in Europeans\n• &lt;5% of Black patients\n• ~10% have normal CXR"),
    ]
    col2 = [
        Paragraph("Chronic / Progressive", SUBSEC),
        Spacer(1, 3),
        bullet("Insidious dyspnea and dry, nonproductive cough"),
        bullet("Bilateral hilar adenopathy + infiltrates on CXR"),
        bullet("Skin lesions: trunk and extremities"),
        bullet("Uveitis, vision changes"),
        bullet("Multiple flares requiring repeated treatment"),
        Spacer(1, 4),
        Paragraph("Course: relapsing-remitting; may progress to fibrosis", BODY),
        Spacer(1, 4),
        note_box("Poor prognostic features:\n• Lupus pernio\n• Chronic uveitis\n• Cardiac/neuro involvement"),
    ]

    content = [[col1, col2]]
    side_tbl = Table(content, colWidths=[(TW/2)-3, (TW/2)-3], hAlign="LEFT")
    side_tbl.setStyle(TableStyle([
        ("VALIGN",       (0,0), (-1,-1), "TOP"),
        ("LEFTPADDING",  (0,0), (-1,-1), 4),
        ("RIGHTPADDING", (0,0), (-1,-1), 4),
    ]))
    E.append(side_tbl)
    E.append(Spacer(1, 8))

    # Also asymptomatic
    E.append(Paragraph("Asymptomatic Sarcoidosis", SUBSEC))
    E.append(Paragraph(
        "Up to <b>two-thirds of patients</b> are asymptomatic and diagnosed incidentally after "
        "a routine chest radiograph showing bilateral hilar adenopathy. This pattern is more "
        "common in white patients. Many of these patients will have spontaneous resolution "
        "without any treatment.",
        BODY))
    E.append(Spacer(1, 8))
    return E

# ─────────────────────────────────────────────────────────────────────────────
# SECTION 4 – Organ System Involvement
# ─────────────────────────────────────────────────────────────────────────────
def sec4():
    E = []
    E.append(section_header("04  ORGAN SYSTEM INVOLVEMENT"))
    E.append(Spacer(1, 4))

    organs = [
        ["Pulmonary", "90%",
         "Cough (nonproductive), dyspnea on exertion, chest tightness. "
         "Crackles in &lt;20%. Restrictive pattern + ↓DLCO on PFTs in progressive disease."],
        ["Cutaneous", "25–35%",
         "Red-brown papules/plaques on face (nose, mouth). Lupus pernio (alar nose). "
         "Lesions in scars/tattoos. Erythema nodosum (Lofgren)."],
        ["Ocular", "~25%",
         "Anterior uveitis (most common). Posterior uveitis, lacrimal gland enlargement, "
         "keratic precipitates, optic nerve involvement → blindness."],
        ["Lymphatic", "Common",
         "Bilateral hilar adenopathy is the most common intrathoracic finding. "
         "Peripheral lymphadenopathy in ~40%."],
        ["Hepatic/GI", "~50–70% subclinical",
         "Granulomatous hepatitis, hepatosplenomegaly, elevated LFTs. "
         "Bowel involvement rare."],
        ["Cardiac", "~5–10%",
         "Conduction defects (AV block, BBB), ventricular arrhythmias, sudden death, "
         "cardiomyopathy. FDG-PET/CMR for detection."],
        ["Neurologic", "~5–10%",
         "CN VII palsy (most common). Hypothalamic/pituitary involvement (DI). "
         "Optic chiasm → blindness. Meningitis, hydrocephalus, myelopathy."],
        ["Renal", "Underestimated",
         "Nephrolithiasis (most common). Granulomatous interstitial nephritis. "
         "Nephrocalcinosis from hypercalcemia."],
        ["ENT", "Variable",
         "Nasal mucosal disease, sinusitis. SNHL, CN VII palsy (Heerfordt syndrome). "
         "Laryngeal involvement."],
        ["Musculoskeletal", "10–40%",
         "Polyarthritis (esp. Lofgren). Bone cysts (lacy pattern). "
         "Myopathy with ↑CPK (uncommon)."],
        ["Endocrine/Metabolic", "10–20%",
         "Hypercalciuria &gt; hypercalcemia. ↑1,25-OH-Vit D from granuloma 1α-hydroxylase. "
         "Hypopituitarism in neurosarcoidosis."],
        ["Exocrine Glands", "&lt;5%",
         "Heerfordt syndrome: parotitis + uveitis + CN VII palsy + fever. "
         "Sicca syndrome (dry mouth/eyes)."],
    ]

    tbl = styled_table(
        ["System", "Frequency", "Key Features"],
        organs,
        col_widths=[32*mm, 22*mm, TW - 54*mm],
        header_color=DARK_TEAL,
    )
    E.append(tbl)
    E.append(Spacer(1, 8))
    return E

# ─────────────────────────────────────────────────────────────────────────────
# SECTION 5 – Scadding Staging
# ─────────────────────────────────────────────────────────────────────────────
def sec5():
    E = []
    E.append(section_header("05  SCADDING RADIOLOGIC STAGING SYSTEM", color=colors.HexColor("#117864")))
    E.append(Spacer(1, 4))
    E.append(Paragraph(
        "Chest radiography is abnormal in &gt;90% of cases and provides important prognostic "
        "information. The international Scadding system classifies findings into five stages:",
        BODY))
    E.append(Spacer(1, 5))

    stages = [
        ["Stage 0", "Normal", "&lt;15%",
         "Normal chest radiograph. May still have active extrapulmonary disease.",
         "Spontaneous remission likely if acute presentation."],
        ["Stage I", "BHA only", "30–50%",
         "Bilateral hilar adenopathy (BHA). Often with right paratracheal adenopathy. "
         "Symmetric 'potato node' appearance. Calcification uncommon.",
         "Excellent — most resolve spontaneously. Systemic therapy usually NOT needed."],
        ["Stage II", "BHA + Infiltrates", "40–60%",
         "Pulmonary infiltrates WITH bilateral hilar adenopathy. Linear or "
         "reticulonodular markings in middle/upper lung fields.",
         "Variable; ~50–70% improve with treatment."],
        ["Stage III", "Infiltrates only", "10–20%",
         "Pulmonary infiltrates WITHOUT hilar adenopathy. May mimic infectious "
         "pneumonia, GPA, or malignancy.",
         "Variable; tends toward more chronic disease."],
        ["Stage IV", "Fibrosis", "&lt;15%",
         "Fibrocystic changes, cephalad hilar retraction, honeycombing, "
         "bronchiectasis. Mycetoma complication risk.",
         "Poor. Irreversible fibrosis. Risk of mycetoma, respiratory failure."],
    ]

    # Custom colors per stage
    stage_colors = [
        MID_GREY,
        colors.HexColor("#27AE60"),  # green – good
        colors.HexColor("#F39C12"),  # gold – variable
        colors.HexColor("#E67E22"),  # orange – chronic
        colors.HexColor("#C0392B"),  # red – poor
    ]

    # header
    hdr = [Paragraph(h, TABLE_HDR) for h in
           ["Stage", "Descriptor", "Freq.", "CXR Findings", "Prognosis"]]
    rows = []
    for s in stages:
        rows.append([Paragraph(s[i], TABLE_CELL) for i in range(5)])

    tbl = Table([hdr] + rows, colWidths=[17*mm, 22*mm, 13*mm, 65*mm, TW-117*mm], repeatRows=1)
    ts = [
        ("FONTNAME",     (0,0), (-1, 0), "Helvetica-Bold"),
        ("FONTSIZE",     (0,0), (-1,-1), 8.5),
        ("TOPPADDING",   (0,0), (-1,-1), 5),
        ("BOTTOMPADDING",(0,0), (-1,-1), 5),
        ("LEFTPADDING",  (0,0), (-1,-1), 5),
        ("RIGHTPADDING", (0,0), (-1,-1), 5),
        ("GRID",         (0,0), (-1,-1), 0.4, MID_GREY),
        ("VALIGN",       (0,0), (-1,-1), "MIDDLE"),
        ("BACKGROUND",   (0,0), (-1, 0), colors.HexColor("#117864")),
        ("TEXTCOLOR",    (0,0), (-1, 0), WHITE),
    ]
    for i, sc in enumerate(stage_colors):
        ts.append(("BACKGROUND", (0, i+1), (0, i+1), sc))
        ts.append(("TEXTCOLOR",  (0, i+1), (0, i+1), WHITE))
        ts.append(("FONTNAME",   (0, i+1), (0, i+1), "Helvetica-Bold"))

    tbl.setStyle(TableStyle(ts))
    E.append(tbl)
    E.append(Spacer(1, 6))
    E.append(note_box(
        "<b>Important:</b> There is only a weak correlation between stages I, II, and III and "
        "actual clinical status (ACCESS study). Stage IV is associated with poor prognosis. "
        "Always correlate radiograph stage with symptoms, PFTs, and HRCT findings."
    ))
    E.append(Spacer(1, 6))

    E.append(Paragraph("HRCT Chest Findings in Sarcoidosis", SUBSEC))
    hrct = [
        ["Perilymphatic nodules", "Peribronchovascular, subpleural — highly characteristic"],
        ["Upper lobe predominance", "Most common distribution; middle zones also affected"],
        ["'Galaxy sign'", "Central mass of nodules with satellite lesions — specific for sarcoidosis"],
        ["'Sausage sign'", "Nodular beading along bronchovascular bundles"],
        ["Ground-glass opacity", "Less common; suggests active alveolitis"],
        ["Fibrocystic changes", "Stage IV: honeycombing, traction bronchiectasis, hilar retraction"],
        ["Lymphadenopathy", "Bilateral hilar; right paratracheal; mediastinal"],
    ]
    tbl2 = styled_table(
        ["HRCT Feature", "Description"],
        hrct,
        col_widths=[45*mm, TW - 45*mm],
        header_color=colors.HexColor("#117864"),
    )
    E.append(tbl2)
    E.append(Spacer(1, 8))
    return E

# ─────────────────────────────────────────────────────────────────────────────
# SECTION 6 – Diagnostic Criteria & Workup
# ─────────────────────────────────────────────────────────────────────────────
def sec6():
    E = []
    E.append(PageBreak())
    E.append(section_header("06  DIAGNOSTIC CRITERIA & WORKUP"))
    E.append(Spacer(1, 4))

    E.append(warn_box(
        "Sarcoidosis is a DIAGNOSIS OF EXCLUSION. Always rule out tuberculosis, fungal "
        "infections, berylliosis, Langerhans cell histiocytosis, and lymphoma before confirming."
    ))
    E.append(Spacer(1, 5))

    E.append(Paragraph("Diagnostic Triad", SUBSEC))
    E.append(bullet("Compatible <b>clinical and radiographic findings</b>"))
    E.append(bullet("<b>Tissue biopsy</b> showing noncaseating granulomas"))
    E.append(bullet("<b>Exclusion</b> of other known causes of noncaseating granuloma"))
    E.append(Spacer(1, 5))

    E.append(Paragraph("Biopsy Sites — Yield by Location", SUBSEC))
    biopsy = [
        ["Transbronchial lung biopsy (TBLB)", "60–90%", "First-line in pulmonary disease"],
        ["EBUS-guided mediastinal LN biopsy", "80–90%", "Preferred over mediastinoscopy"],
        ["Skin lesions (if present)", "~95%", "Highly accessible; avoid erythema nodosum"],
        ["Peripheral lymph node", "~75%", "If palpable"],
        ["Liver biopsy", "~60%", "Low specificity for sarcoidosis"],
        ["Conjunctival biopsy", "~20–30%", "Low yield but easy access"],
        ["Bone marrow biopsy", "Variable", "For suspected marrow involvement"],
    ]
    tbl = styled_table(
        ["Biopsy Site", "Yield", "Notes"],
        biopsy,
        col_widths=[55*mm, 18*mm, TW-73*mm],
        header_color=DARK_TEAL,
    )
    E.append(tbl)
    E.append(Spacer(1, 6))

    E.append(Paragraph("Minimum Diagnostic Workup", SUBSEC))
    workup = [
        ("History &amp; Physical", "Exposure history, race/ethnicity, symptom duration, organ review"),
        ("Chest radiograph (PA + Lateral)", "Initial imaging; Scadding staging"),
        ("HRCT chest", "Superior to CXR; define extent of parenchymal disease"),
        ("Pulmonary function tests", "Spirometry + DLCO (restrictive pattern, ↓DLCO in advanced disease)"),
        ("Ophthalmologic evaluation", "Slit-lamp exam; mandatory even if asymptomatic"),
        ("ECG", "Screen for cardiac conduction disease"),
        ("Serum ACE", "Elevated in ~60%; supports but does not confirm diagnosis"),
        ("Serum calcium + 24-h urine calcium", "Hypercalciuria &gt; hypercalcemia"),
        ("LFTs, renal function (BMP)", "Hepatic and renal involvement"),
        ("CBC with differential", "Lymphopenia, anemia"),
        ("Urinalysis", "Granulomatous nephritis, hematuria"),
        ("Tissue biopsy", "Noncaseating granuloma on H&amp;E; special stains for AFB and fungi"),
    ]
    tbl2 = styled_table(
        ["Investigation", "Purpose / Findings"],
        workup,
        col_widths=[60*mm, TW-60*mm],
        header_color=DARK_TEAL,
    )
    E.append(tbl2)
    E.append(Spacer(1, 6))

    E.append(Paragraph("Additional Investigations by Suspected Organ", SUBSEC))
    extra = [
        ["Cardiac", "Echo, 24-h Holter, CMR with gadolinium, FDG-PET/CT"],
        ["Neurologic", "Brain/spine MRI with contrast, CSF analysis, nerve biopsy"],
        ["Skin", "Punch biopsy (avoid erythema nodosum — non-specific panniculitis)"],
        ["Calcium", "PTH, 25-OH-Vit D, 1,25-OH-Vit D, 24-h urine Ca"],
        ["Bone", "Skeletal survey, bone scan"],
        ["Endocrine", "AM cortisol, TFTs, prolactin, serum osmolality (if DI suspected)"],
    ]
    tbl3 = styled_table(
        ["Organ System", "Targeted Tests"],
        extra,
        col_widths=[35*mm, TW-35*mm],
        header_color=MID_TEAL,
    )
    E.append(tbl3)
    E.append(Spacer(1, 8))
    return E

# ─────────────────────────────────────────────────────────────────────────────
# SECTION 7 – Labs & Biomarkers
# ─────────────────────────────────────────────────────────────────────────────
def sec7():
    E = []
    E.append(section_header("07  LABORATORY & BIOMARKER FINDINGS"))
    E.append(Spacer(1, 4))

    labs = [
        ["Serum ACE (SACE)", "Elevated in ~60%",
         "Correlates with granuloma burden. Not diagnostic alone. Decreases with "
         "corticosteroids or disease remission. Highly variable; poor prognostic value."],
        ["Serum calcium", "Elevated in ~10%",
         "Due to granuloma 1α-hydroxylase converting 25-OH-D → 1,25(OH)₂-D."],
        ["24-h urine calcium", "Elevated in ~40%",
         "More sensitive than serum calcium. Can cause nephrolithiasis and nephrocalcinosis."],
        ["1,25-OH Vitamin D", "Often elevated",
         "Produced by granuloma macrophages. 25-OH-Vit D may be low."],
        ["Serum sIL-2R", "Elevated",
         "More sensitive than ACE; reflects T-cell activation. Not widely standardized."],
        ["BAL CD4:CD8 ratio", "&gt;3.5 (supportive)",
         "Elevated ratio supports diagnosis. Neither sensitive nor specific enough alone."],
        ["LFTs (AST/ALT/ALP/GGT)", "May be elevated",
         "Hepatic granulomatous involvement; ALP often elevated disproportionately."],
        ["CBC", "Lymphopenia, mild anemia",
         "Non-specific; may see hypersplenism if splenic involvement."],
        ["CRP / ESR", "Elevated in active disease",
         "Non-specific inflammatory markers; may track disease activity."],
        ["Ferritin", "May be elevated",
         "Especially in acute disease; non-specific."],
        ["Urinalysis", "Hematuria, proteinuria",
         "Granulomatous interstitial nephritis; renal tubular acidosis possible."],
    ]

    tbl = styled_table(
        ["Biomarker", "Typical Finding", "Clinical Significance"],
        labs,
        col_widths=[40*mm, 28*mm, TW-68*mm],
        header_color=DARK_TEAL,
    )
    E.append(tbl)
    E.append(Spacer(1, 6))
    E.append(note_box(
        "<b>No single biomarker is diagnostic of sarcoidosis.</b> ACE and calcium levels are "
        "helpful supporting evidence but must always be interpreted alongside clinical, "
        "radiologic, and histologic findings. Transcriptomic signatures are under investigation "
        "as future prognostic tools."
    ))
    E.append(Spacer(1, 8))
    return E

# ─────────────────────────────────────────────────────────────────────────────
# SECTION 8 – Indications for Treatment
# ─────────────────────────────────────────────────────────────────────────────
def sec8():
    E = []
    E.append(section_header("08  INDICATIONS FOR TREATMENT"))
    E.append(Spacer(1, 4))
    E.append(Paragraph(
        "There are <b>no FDA-approved systemic therapies</b> for sarcoidosis. Treatment "
        "decisions are guided by organ involvement, disease severity, and rate of progression. "
        "Many patients, especially those with stage I disease, do NOT require systemic therapy.",
        BODY))
    E.append(Spacer(1, 5))

    # Two-column: treat vs. observe
    col1_items = [
        Paragraph("<b>TREAT — Indications</b>", ParagraphStyle(
            "th", fontSize=9.5, textColor=WHITE, fontName="Helvetica-Bold",
            alignment=TA_CENTER)),
        Spacer(1, 3),
        bullet("Threatened organ failure (cardiac, neurologic, ocular)"),
        bullet("Uveitis unresponsive to topical corticosteroids"),
        bullet("Progressive symptomatic pulmonary disease"),
        bullet("Disfiguring cutaneous disease (esp. lupus pernio)"),
        bullet("Persistent hypercalcemia or hypercalciuria with renal risk"),
        bullet("Renal or hepatic dysfunction"),
        bullet("Symptomatic hepatic / splenic enlargement"),
        bullet("Neurosarcoidosis"),
        bullet("Cardiac conduction disease or ventricular arrhythmias"),
        bullet("Disabling arthritis (Lofgren)"),
    ]
    col2_items = [
        Paragraph("<b>OBSERVE — No Immediate Treatment</b>", ParagraphStyle(
            "th2", fontSize=9.5, textColor=WHITE, fontName="Helvetica-Bold",
            alignment=TA_CENTER)),
        Spacer(1, 3),
        bullet("Stage I asymptomatic — spontaneous remission common"),
        bullet("Lofgren syndrome (NSAID + rest first)"),
        bullet("Stable, mild pulmonary disease without functional impairment"),
        bullet("Incidental bilateral hilar adenopathy in young patient"),
        Spacer(1, 6),
        Paragraph("Monitoring for ≥3 years after 'remission'", BOLD_BODY),
        Spacer(1, 3),
        Paragraph(
            "Longer observation required for serious pulmonary or "
            "extrapulmonary manifestations. Relapse is common.",
            BODY),
    ]

    cols_tbl = Table(
        [[col1_items, col2_items]],
        colWidths=[(TW/2)-2, (TW/2)-2],
    )
    cols_tbl.setStyle(TableStyle([
        ("BACKGROUND",   (0,0), (0,0), colors.HexColor("#E74C3C")),
        ("BACKGROUND",   (1,0), (1,0), colors.HexColor("#117864")),
        ("TOPPADDING",   (0,0), (-1,-1), 8),
        ("BOTTOMPADDING",(0,0), (-1,-1), 8),
        ("LEFTPADDING",  (0,0), (-1,-1), 8),
        ("RIGHTPADDING", (0,0), (-1,-1), 8),
        ("VALIGN",       (0,0), (-1,-1), "TOP"),
    ]))
    E.append(cols_tbl)
    E.append(Spacer(1, 6))

    E.append(Paragraph("Treatment Ladder", SUBSEC))
    tx = [
        ["First Line", "Corticosteroids",
         "Prednisone 20–40 mg/day (0.5 mg/kg/day). Tapered over 12–24 months. "
         "Renal sarcoidosis: 1 mg/kg/day (max 60 mg/day)."],
        ["Second Line (steroid-sparing)", "Methotrexate",
         "Most commonly used. 10–15 mg/week + folic acid. Monitor LFTs."],
        ["Second Line", "Hydroxychloroquine",
         "Best for cutaneous disease and hypercalcemia. 200–400 mg/day."],
        ["Second Line", "Azathioprine / MMF",
         "Alternative steroid-sparing agents for pulmonary/extrapulmonary."],
        ["Third Line (refractory)", "Infliximab (anti-TNF-α)",
         "Most evidence among biologics. Effective for refractory pulmonary, "
         "cutaneous, and neurologic disease. 2025 meta-analysis (PMID 40393718) confirms efficacy."],
        ["Third Line", "Adalimumab",
         "Alternative anti-TNF agent. Less data than infliximab."],
        ["Local", "Intralesional corticosteroids",
         "For localized cutaneous disease. Repeated every 2–4 weeks."],
    ]
    tbl = styled_table(
        ["Tier", "Agent", "Details"],
        tx,
        col_widths=[28*mm, 35*mm, TW-63*mm],
        header_color=DARK_TEAL,
    )
    E.append(tbl)
    E.append(Spacer(1, 8))
    return E

# ─────────────────────────────────────────────────────────────────────────────
# SECTION 9 – Prognosis
# ─────────────────────────────────────────────────────────────────────────────
def sec9():
    E = []
    E.append(section_header("09  PROGNOSTIC FEATURES"))
    E.append(Spacer(1, 4))

    E.append(Paragraph("Prognosis by Presentation / Stage", SUBSEC))
    prog = [
        ["Lofgren syndrome", "Excellent", "70–80% remission; often without treatment"],
        ["Stage I (asymptomatic BHA)", "Excellent", "&gt;70% spontaneous remission"],
        ["Stage II–III", "Variable", "~50–70% improve with treatment; some progress"],
        ["Stage IV (fibrocystic)", "Poor", "Irreversible fibrosis; mycetoma risk; respiratory failure"],
        ["Cardiac sarcoidosis", "Guarded", "Increased mortality; sudden cardiac death; ICD may be needed"],
        ["Neurosarcoidosis", "Guarded", "Unpredictable; high morbidity; may require long-term immunosuppression"],
        ["Black race, chronic disease", "Worse", "Higher rates of persistent disease, organ damage, mortality"],
    ]
    tbl = styled_table(
        ["Presentation / Stage", "Prognosis", "Notes"],
        prog,
        col_widths=[50*mm, 22*mm, TW-72*mm],
        header_color=DARK_TEAL,
    )
    E.append(tbl)
    E.append(Spacer(1, 5))

    E.append(Paragraph("Poor Prognostic Markers", SUBSEC))
    poor = [
        "Lupus pernio",
        "Chronic uveitis",
        "Hypercalcemia or nephrocalcinosis",
        "Nasal mucosal involvement",
        "Bone cysts",
        "Neurosarcoidosis",
        "Cardiac involvement",
        "Age &gt;40 at onset",
        "Disease duration &gt;2 years",
        "African American race",
        "Stage III–IV radiographic disease",
        "Severe restrictive defect on PFTs (FVC &lt;60% predicted)",
    ]
    # 2-column bullet layout
    mid = len(poor) // 2
    col1 = [bullet(p) for p in poor[:mid]]
    col2 = [bullet(p) for p in poor[mid:]]
    bt = Table([[col1, col2]], colWidths=[TW/2, TW/2])
    bt.setStyle(TableStyle([
        ("VALIGN", (0,0), (-1,-1), "TOP"),
        ("LEFTPADDING", (0,0), (-1,-1), 2),
        ("RIGHTPADDING", (0,0), (-1,-1), 2),
    ]))
    E.append(bt)
    E.append(Spacer(1, 8))
    return E

# ─────────────────────────────────────────────────────────────────────────────
# SECTION 10 – Key Associations
# ─────────────────────────────────────────────────────────────────────────────
def sec10():
    E = []
    E.append(section_header("10  KEY CLINICAL ASSOCIATIONS"))
    E.append(Spacer(1, 4))

    assoc = [
        ["Bilateral hilar adenopathy", "Most common intrathoracic radiographic finding"],
        ["Lupus pernio", "Violaceous nasal plaques → chronic disease, upper airway involvement, pulmonary fibrosis"],
        ["Erythema nodosum + BHA", "Lofgren syndrome — good prognosis (NOT granuloma on biopsy — panniculitis)"],
        ["Heerfordt syndrome", "Parotitis + uveitis + CN VII palsy + fever (uveoparotid fever)"],
        ["Hypercalcemia / hypercalciuria", "Granuloma 1α-hydroxylase activity → ↑1,25-OH-Vit D"],
        ["Noncaseating granuloma", "Pathologic hallmark — absence of necrosis differentiates from TB"],
        ["Elevated ACE", "~60% of patients; reflects granuloma burden; not diagnostic"],
        ["CN VII most affected cranial nerve", "Often bilateral; may be sudden onset; Heerfordt syndrome"],
        ["'Great imitator'", "Mimics TB, lymphoma, fungal disease, vasculitis — always biopsy"],
        ["Mycetoma", "Complication of stage IV fibrocystic sarcoidosis"],
        ["Galaxy sign on HRCT", "Highly specific for sarcoidosis — central mass of nodules with satellites"],
        ["Potato nodes", "Symmetric bilateral hilar lymphadenopathy appearance on CXR"],
        ["Stage I — no fibrosis, good prognosis", "Systemic steroids usually not indicated"],
        ["Spontaneous remission", "Occurs in ~70% of acute-onset cases within 2–3 years"],
    ]

    tbl = styled_table(
        ["Finding / Association", "Clinical Significance"],
        assoc,
        col_widths=[65*mm, TW-65*mm],
        header_color=colors.HexColor("#6C3483"),
    )
    E.append(tbl)
    E.append(Spacer(1, 6))

    E.append(hr(color=MID_TEAL, thickness=1))
    E.append(Spacer(1, 4))
    E.append(Paragraph(
        "References: Fishman's Pulmonary Diseases and Disorders, 2-Volume Set · "
        "Textbook of Family Medicine 9e · Fitzpatrick's Dermatology, Vol 1–2 · "
        "Andrews' Diseases of the Skin · Cummings Otolaryngology Head and Neck Surgery · "
        "Harrison's Principles of Internal Medicine 22E (2025) · "
        "Bechman K et al. Thorax 2025 (PMID 40393718) — Systematic review of biologics in sarcoidosis",
        FOOTER_TEXT
    ))
    E.append(Spacer(1, 3))
    E.append(Paragraph(
        "Generated by Orris Medical Reference · July 2026 · For educational use only — "
        "not a substitute for clinical judgment.",
        FOOTER_TEXT
    ))
    return E

# ─────────────────────────────────────────────────────────────────────────────
# PAGE NUMBERS
# ─────────────────────────────────────────────────────────────────────────────
def on_each_page(canvas, doc):
    canvas.saveState()
    page_num = canvas.getPageNumber()
    if page_num > 1:
        canvas.setFont("Helvetica", 7)
        canvas.setFillColor(MID_GREY)
        canvas.drawRightString(W - MARGIN, 12*mm, f"Page {page_num}")
        canvas.drawString(MARGIN, 12*mm, "Sarcoidosis: Diagnosis & Staging Reference Guide")
        canvas.setStrokeColor(MID_GREY)
        canvas.setLineWidth(0.3)
        canvas.line(MARGIN, 14.5*mm, W - MARGIN, 14.5*mm)
    canvas.restoreState()

# ─────────────────────────────────────────────────────────────────────────────
# BUILD
# ─────────────────────────────────────────────────────────────────────────────
story = []
story += make_cover()
story += sec1()
story += sec2()
story += sec3()
story += sec4()
story += sec5()
story += sec6()
story += sec7()
story += sec8()
story += sec9()
story += sec10()

doc.build(story, onFirstPage=on_each_page, onLaterPages=on_each_page)
print("PDF generated successfully.")
Running Command

cd /tmp/workspace/sarcoidosis-pdf && python generate_pdf.py 2>&1

Running Command

ls -lh /tmp/workspace/sarcoidosis-pdf/Sarcoidosis_Diagnosis_Staging_Guide.pdf

Your reference PDF guide is ready:

Sarcoidosis Diagnosis Staging Guide

PDF Document · PDF


What's inside (10 sections, A4, color-coded):
SectionContent
01Definition & Pathogenesis (step-by-step granuloma formation, implicated triggers table)
02Epidemiology & Risk Factors (incidence by race/geography)
03Clinical Presentations - Acute (Lofgren) vs. Chronic vs. Asymptomatic (two-column layout)
04Organ System Involvement - 12 systems with frequency and key features
05Scadding Radiologic Staging - color-coded stage 0-IV table + HRCT features
06Diagnostic Criteria & Workup - biopsy yields by site, minimum workup checklist
07Laboratory & Biomarker Findings - ACE, calcium, sIL-2R, BAL, etc.
08Indications for Treatment - Treat vs. Observe layout + full treatment ladder
09Prognostic Features - by stage/presentation + poor prognostic markers
10Key Clinical Associations - 14-row quick-reference table
The guide uses color-coded section headers (teal/green/purple), alternating-row tables, highlighted note/warning boxes, and page footers throughout.
This is a shared conversation. Sign in to Orris to start your own chat.