Explain 10 common heart dieseases with ecg images in detail

Reading File
Finding Sources
Searching Images

ECG electrocardiogram myocardial infarction STEMI

Finding Sources
Searching Images

atrial fibrillation ECG irregular rhythm absent P waves

Searching Images

heart block complete AV block ECG bradycardia P wave dissociation

Searching Images

ventricular tachycardia ECG wide QRS tachycardia

Searching Images

left ventricular hypertrophy ECG high voltage Sokolow-Lyon criteria

Searching Images

pericarditis ECG saddle-shaped ST elevation diffuse PR depression

Searching Images

Wolff-Parkinson-White WPW syndrome ECG delta wave short PR interval

Searching Images

heart failure dilated cardiomyopathy ECG bundle branch block low voltage

Searching Images

hypertrophic cardiomyopathy ECG deep Q waves lateral leads

Searching Images

long QT syndrome ECG prolonged QT interval torsades de pointes

Here is a comprehensive guide to 10 common heart diseases with their ECG findings and representative images.

10 Common Heart Diseases β€” ECG Guide


1. πŸ«€ Acute Myocardial Infarction (STEMI)

Overview

A ST-Elevation Myocardial Infarction occurs when a coronary artery is completely occluded, causing transmural (full-thickness) myocardial necrosis. It is a life-threatening emergency requiring immediate reperfusion.

Pathophysiology

Plaque rupture β†’ thrombosis β†’ complete coronary occlusion β†’ ischemia β†’ injury β†’ necrosis. The affected myocardium progresses through hyperacute (minutes), acute (hours), and evolved (days–weeks) phases.

ECG Changes

PhaseFinding
HyperacuteTall, peaked "hyperacute" T-waves
Acute (hours)ST-segment elevation (convex/tombstone shape)
Evolving (days)Pathological Q-waves (>40 ms, >25% QRS height), T-wave inversion
Reciprocal changesST depression in leads facing the opposite wall
Localisation:
  • Anterior STEMI (LAD): ST elevation in V1–V4, reciprocal depression in II, III, aVF
  • Inferior STEMI (RCA): ST elevation in II, III, aVF, reciprocal depression in I, aVL
  • Lateral STEMI (LCx): ST elevation in I, aVL, V5–V6

ECG Image β€” Anterior STEMI (V2–V5 "tombstoning")

Anterior STEMI ECG β€” ST elevation V2–V5 with tombstoning morphology
Anterior STEMI: Convex "tombstoning" ST elevation in V2–V5, QS waves indicating necrosis, reciprocal inferior depression.

ECG Image β€” Inferior STEMI (II, III, aVF)

Inferior STEMI ECG β€” ST elevation in inferior leads with reciprocal changes in aVL
Inferior STEMI: β‰₯2 mm convex ST elevation in II, III, aVF; pathologic Q-waves; reciprocal ST depression in I and aVL.

2. πŸ«€ Atrial Fibrillation (AF)

Overview

AF is the most common sustained cardiac arrhythmia, affecting over 37 million people worldwide. Disorganized atrial electrical activity produces an irregular, often fast ventricular response with no effective atrial contraction, increasing stroke risk 5-fold.

Pathophysiology

Multiple re-entrant circuits in the atria (often triggered by pulmonary vein foci) cause rapid, chaotic depolarisation. The AV node filters impulses irregularly β†’ "irregularly irregular" ventricular rhythm.

ECG Changes

FeatureFinding
P wavesAbsent β€” replaced by fine fibrillatory baseline (best seen in V1)
RhythmIrregularly irregular R-R intervals
RateVentricular rate 100–180 bpm (uncontrolled); <100 bpm (controlled)
QRSUsually narrow (unless aberrant conduction or pre-excitation)

ECG Image β€” Atrial Fibrillation with Rapid Ventricular Response

Atrial Fibrillation ECG β€” Absent P waves, irregular R-R intervals, fibrillatory baseline
AF with RVR: No P waves, chaotic fibrillatory baseline visible in V1, classic irregularly irregular rhythm, narrow QRS.

3. πŸ«€ Complete Heart Block (3rd Degree AV Block)

Overview

Complete AV block is a failure of all atrial impulses to conduct to the ventricles. The atria and ventricles beat independently. It requires urgent pacemaker implantation and can cause syncope or sudden death.

Pathophysiology

Complete interruption of the conduction system (at the AV node, Bundle of His, or below) causes total AV dissociation. An escape rhythm (junctional or ventricular) takes over at a slow rate.

ECG Changes

FeatureFinding
P wavesRegular, at faster rate (60–100 bpm)
QRSIndependent, slower rate (20–50 bpm)
PR intervalNo fixed relationship β€” P waves and QRS completely dissociated
QRS morphologyNarrow if junctional escape; Wide (>120 ms) if ventricular escape

ECG Image β€” Complete Heart Block

Complete Heart Block ECG β€” P waves and QRS complexes fully dissociated
Third-degree AV block: Regular P waves at ~80 bpm, completely independent junctional escape rhythm at ~40 bpm, profound bradycardia, no fixed PR interval.

4. πŸ«€ Ventricular Tachycardia (VT)

Overview

VT is a potentially life-threatening arrhythmia arising from the ventricles, defined as β‰₯3 consecutive ventricular beats at rate >100 bpm. Sustained VT (>30 sec) causes hemodynamic compromise and can degenerate to ventricular fibrillation.

Pathophysiology

Re-entry circuits, triggered activity, or automaticity within diseased ventricular myocardium (scar, ischemia, cardiomyopathy) cause rapid, abnormal ventricular depolarisation bypassing the normal conduction system.

ECG Changes

FeatureFinding
Rate100–250 bpm
QRSWide (>120 ms), bizarre morphology
P wavesAbsent or dissociated from QRS (AV dissociation)
AxisOften extreme axis deviation
Fusion/capture beatsPathognomonic when present
Brugada criteria and Vereckei algorithm help differentiate VT from SVT with aberrancy.

ECG Image β€” Monomorphic Ventricular Tachycardia

Ventricular Tachycardia ECG β€” Wide complex regular tachycardia, no P waves, positive precordial concordance
Monomorphic VT: High-amplitude wide QRS at ~170 bpm, positive concordance V1–V6, superior axis, no visible P waves, consistent with structural heart disease origin.

5. πŸ«€ Acute Pericarditis

Overview

Pericarditis is inflammation of the pericardial sac, most commonly viral (Coxsackievirus, Echovirus) but also autoimmune, bacterial, or post-MI (Dressler's syndrome). Presents with sharp pleuritic chest pain relieved by leaning forward.

Pathophysiology

Pericardial inflammation causes superficial myocardial irritation, producing diffuse repolarisation changes. Unlike STEMI, changes affect all leads simultaneously (not a single coronary territory).

ECG Changes (4 Classic Stages)

StageFinding
Stage I (acute)Diffuse concave ("saddle-shaped") ST elevation in all leads except aVR and V1; PR depression (most specific sign)
Stage II (days)ST returns to baseline; T-waves flatten
Stage III (weeks)T-wave inversion
Stage IVECG normalises
Key distinguishing features from STEMI:
  • ST elevation is concave (not convex)
  • Diffuse (not localised to one territory)
  • PR depression in inferior and lateral leads
  • No reciprocal ST depression (except aVR)
  • Spodick's sign: downsloping TP segment

ECG Image β€” Acute Pericarditis

Acute Pericarditis ECG β€” Saddle-shaped ST elevation, PR depression in multiple leads, Spodick's sign
Acute pericarditis: Diffuse saddle-shaped ST elevation in I, II, III, aVF, V2–V6; PR depression in II; PR elevation in aVR; Spodick's sign visible. Classic features distinguishing pericarditis from STEMI.

6. πŸ«€ Left Ventricular Hypertrophy (LVH)

Overview

LVH is thickening of the left ventricular walls, most commonly from chronic hypertension, aortic stenosis, or hypertrophic cardiomyopathy. LVH is an independent risk factor for heart failure, arrhythmias, and sudden death.

Pathophysiology

Chronic pressure overload β†’ myocyte hypertrophy β†’ increased muscle mass β†’ greater electrical voltage recorded on ECG. Impaired subendocardial perfusion causes the characteristic "strain" pattern.

ECG Changes

FeatureFinding
Sokolow-Lyon criterionS in V1 + R in V5 or V6 β‰₯35 mm
Cornell criterionR in aVL + S in V3 β‰₯28 mm (men), β‰₯20 mm (women)
Strain patternST depression and T-wave inversion in lateral leads (I, aVL, V5, V6)
Left axis deviationCommon
P waveBroad, notched P (P mitrale) suggesting left atrial enlargement

ECG Image β€” LVH with Strain Pattern

Left Ventricular Hypertrophy ECG β€” High voltage, ST depression and T-wave inversion lateral leads (strain pattern)
LVH: Tall R-waves in V5–V6, deep S-waves in V1–V2 (meeting Sokolow-Lyon criteria); ST depression and T-inversion in II, III, aVF, V5, V6 β€” the classic "strain pattern" of pressure overload.

7. πŸ«€ Wolff-Parkinson-White (WPW) Syndrome

Overview

WPW is a pre-excitation syndrome caused by an accessory pathway (Bundle of Kent) bypassing the AV node, connecting atria directly to ventricles. It can cause paroxysmal supraventricular tachycardia and, rarely, sudden cardiac death via rapid conduction of AF to the ventricles.

Pathophysiology

The accessory pathway conducts faster than the AV node, activating part of the ventricle early ("pre-excitation"). This produces the delta wave and widened QRS. During tachycardia, re-entry circuits use the AV node and accessory pathway.

ECG Changes (Classic Triad)

FeatureFinding
PR intervalShort (<120 ms)
Delta waveSlurred upstroke at start of QRS
QRSWidened (>120 ms) due to delta wave
ST-T changesDiscordant β€” secondary to abnormal depolarisation
Pseudo-infarctionNegative delta waves can mimic Q-waves
Localisation: Delta wave polarity in V1 and inferior leads identifies accessory pathway location.

ECG Image β€” WPW Syndrome

WPW Syndrome ECG β€” Short PR interval, delta waves, widened QRS in multiple leads
WPW: Short PR (<120 ms), prominent delta waves (slurred QRS upstroke) in II, III, aVF, V2–V6, secondary ST-T discordance β€” classic triad of ventricular pre-excitation.

8. πŸ«€ Hypertrophic Cardiomyopathy (HCM)

Overview

HCM is a genetic disorder (autosomal dominant, most commonly MYH7 or MYBPC3 mutations) causing asymmetric left ventricular hypertrophy, usually of the interventricular septum. It is the leading cause of sudden cardiac death in young athletes.

Pathophysiology

Myofiber disarray and fibrosis disrupt normal electrical conduction, producing abnormal repolarisation. Dynamic LV outflow tract obstruction contributes to symptoms (exertional dyspnea, syncope, chest pain).

ECG Changes

FeatureFinding
VoltageHigh QRS voltage (LVH criteria met in ~90%)
Q-wavesDeep narrow Q-waves in lateral/inferior leads (due to septal hypertrophy β€” septal depolarisation vector is abnormally prominent)
T-wave inversionsDeep symmetric T-inversion in lateral leads; "giant" T-inversion in apical HCM (Yamaguchi syndrome, >10 mm)
Left axis deviationCommon
ArrhythmiasAF, VT β€” common in advanced disease

ECG Image β€” Apical HCM (Yamaguchi Syndrome)

Hypertrophic Cardiomyopathy ECG β€” Giant T-wave inversions in precordial leads V2–V6
Apical HCM: Sinus rhythm, high-voltage precordial QRS, giant symmetric T-wave inversions most prominent in V4–V6 with ST depression β€” the hallmark of apical (Yamaguchi) HCM.

9. πŸ«€ Dilated Cardiomyopathy (DCM) / Heart Failure with Reduced EF

Overview

DCM is characterised by ventricular dilatation and systolic dysfunction (reduced EF <40%). Causes include idiopathic, viral myocarditis, alcohol, chemotherapy, and genetic. It leads to progressive heart failure and carries a high risk of sudden cardiac death.

Pathophysiology

Myocyte loss and fibrosis impair both contraction and electrical conduction. Ventricular dilation distorts the His-Purkinje system, commonly producing bundle branch blocks. Fibrosis creates re-entry substrates for VT.

ECG Changes

FeatureFinding
LBBBQRS >120 ms, broad slurred R in I, aVL, V5–V6; rS in V1 β€” most common finding
RBBBRSR' in V1, broad S in I, V5–V6
Low voltageQRS amplitude <5 mm in limb leads (if extensive fibrosis)
PR prolongation1st degree AV block common
AFFrequent β€” marker of poor prognosis
Nonspecific ST-T changesDiffuse T-inversion

ECG Image β€” DCM with LBBB

Dilated Cardiomyopathy ECG β€” Left Bundle Branch Block with prolonged QRS, 1st degree AV block
DCM with NYHA Class IV: 1st degree AV block (PR = 280 ms) and wide LBBB (QRS = 145 ms), indicating ventricular dyssynchrony β€” a key indication for Cardiac Resynchronization Therapy (CRT).

10. πŸ«€ Long QT Syndrome (LQTS)

Overview

LQTS is a disorder of ventricular repolarisation causing a prolonged QT interval. It can be congenital (mutations in cardiac ion channel genes: KCNQ1, KCNH2, SCN5A β€” LQT1, LQT2, LQT3) or acquired (drugs, hypokalemia, hypomagnesemia). The major risk is Torsades de Pointes (TdP) β€” a polymorphic VT that can degenerate to ventricular fibrillation and sudden death.

Pathophysiology

Reduced outward K⁺ current or increased inward Na⁺/Ca²⁺ current prolongs phase 3 of the action potential β†’ prolonged repolarisation β†’ early afterdepolarisations β†’ triggered activity β†’ TdP.

ECG Changes

FeatureFinding
QTcβ‰₯450 ms (men), β‰₯460 ms (women) β€” abnormal; β‰₯500 ms β€” high risk
QTc formulaQT ÷ √RR (Bazett formula)
T-wave morphologyLQT1: broad-based T; LQT2: bifid/notched T; LQT3: long flat ST segment, peaked T
T-wave alternansBeat-to-beat variation in T amplitude β€” precursor to TdP
Torsades de PointesPolymorphic VT with QRS complexes "twisting" around the isoelectric line; initiated by "short-long-short" sequence

ECG Image β€” Long QT with Torsades de Pointes

Long QT Syndrome and Torsades de Pointes ECG β€” Prolonged QTc, T-wave alternans, onset of polymorphic VT
LQTS: Panel (a) shows markedly prolonged QTc (478 ms) in sinus rhythm; Panel (b) shows the classic "twisting" polymorphic QRS complexes of Torsades de Pointes triggered by the prolonged repolarisation.

Quick Reference Summary

#DiseaseKey ECG Feature
1Anterior STEMIConvex/tombstone ST elevation V1–V4; Q-waves
2Atrial FibrillationAbsent P waves; irregularly irregular RR; fibrillatory baseline
3Complete Heart BlockP waves and QRS totally dissociated; slow escape rhythm
4Ventricular TachycardiaWide QRS >120 ms; rate >100; AV dissociation
5PericarditisDiffuse saddle-shaped ST elevation; PR depression; Spodick's sign
6LVHHigh voltage (Sokolow β‰₯35 mm); lateral strain pattern
7WPW SyndromeShort PR <120 ms; delta wave; widened QRS
8HCMDeep narrow Q-waves lateral leads; giant T-inversions (apical)
9DCM/Heart FailureLBBB; 1st degree AV block; low voltage; AF
10Long QT SyndromeQTc β‰₯500 ms; T alternans; Torsades de Pointes

Clinical note: ECG interpretation must always be correlated with clinical history, examination, and additional investigations (troponin, echocardiography, cardiac MRI). No single ECG finding is pathognomonic in isolation.
Sources: Braunwald's Heart Disease (10th ed.), Rosen's Emergency Medicine; ECG images from PMC Clinical VQA dataset.

Create a pdf of this

Reading File
Running Command

ls -la /home/daytona/workspace/ 2>/dev/null || echo "workspace empty or not foun…

Running Command

mkdir -p /home/daytona/workspace/heart-diseases-pdf

Writing File

~/heart-diseases-pdf/generate_pdf.py

import requests
import os
from io import BytesIO
from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import cm, mm
from reportlab.lib.colors import HexColor, white, black
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Image, Table, TableStyle,
    PageBreak, HRFlowable, KeepTogether
)
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_JUSTIFY
from reportlab.lib import colors
from PIL import Image as PILImage

OUTPUT_DIR = "/home/daytona/workspace/heart-diseases-pdf"
OUTPUT_PDF = os.path.join(OUTPUT_DIR, "10_Common_Heart_Diseases_ECG.pdf")

# ── Colours ────────────────────────────────────────────────────────────────────
PRIMARY   = HexColor("#C0392B")   # deep red
SECONDARY = HexColor("#2C3E50")   # dark blue-grey
ACCENT    = HexColor("#ECF0F1")   # light grey
GOLD      = HexColor("#F39C12")
LIGHT_RED = HexColor("#FDEDEC")
TABLE_HDR = HexColor("#C0392B")
TABLE_ROW = HexColor("#FDFEFE")
TABLE_ALT = HexColor("#FDEDEC")

# ── Image URLs ─────────────────────────────────────────────────────────────────
IMAGES = {
    "anterior_stemi":  "https://cdn.orris.care/cdss_images/pmc_clinical_VQA_646aa0cfb0d976b56d82780dedb4cd025e7f67b7aa7d1163a2f05a58a08c514e.jpg",
    "inferior_stemi":  "https://cdn.orris.care/cdss_images/pmc_clinical_VQA_4ff7e7260a139d9714f42c31b67dc93d733282c66f59a068801ebd1d27bf5755.jpg",
    "af":              "https://cdn.orris.care/cdss_images/pmc_clinical_VQA_28d2cb3373e01d2a7923241cc616540e9e0ca97e3606845e1d284e6dc853b8a1.jpg",
    "chb":             "https://cdn.orris.care/cdss_images/pmc_clinical_VQA_c35f07d2fcf70adcbf7d659598ff56a2fb6afc98f9691c2b0a02cceecd406901.jpg",
    "vt":              "https://cdn.orris.care/cdss_images/pmc_clinical_VQA_d15a92de4e89b46f273b22d254d1e30fcd6a190c6d581b193e6dd338338c6bb6.jpg",
    "pericarditis":    "https://cdn.orris.care/cdss_images/pmc_clinical_VQA_cd4008531c1b1a2a99ab6d9b4282dcdcedfcf77c302b6ecbb7f7e01c5c985cf9.jpg",
    "lvh":             "https://cdn.orris.care/cdss_images/pmc_clinical_VQA_03152b722b1cb86ef1d57f4d755aa687989488df7da9defa36d7d35f9ab80e28.jpg",
    "wpw":             "https://cdn.orris.care/cdss_images/pmc_clinical_VQA_fd0d58cc1913acd56cbc62cbf42657e4a465f052a9bb2ea0d3893584ef6f70e6.jpg",
    "hcm":             "https://cdn.orris.care/cdss_images/pmc_clinical_VQA_e63f8c2bf7f414e8bcda02609659e537443d073bcd3511675eda15172cbf83d8.jpg",
    "dcm":             "https://cdn.orris.care/cdss_images/pmc_clinical_VQA_111d6dc25b3e3f5e713bbc1a0b5163136c3f000287b93b6e2067e605976164f1.jpg",
    "lqts":            "https://cdn.orris.care/cdss_images/pmc_clinical_VQA_e4cf687e3417d7c536bebc83c4c367527e3ec6bc5ab1f314f252ca25c3be49b8.jpg",
}

def download_image(url, name):
    path = os.path.join(OUTPUT_DIR, f"{name}.jpg")
    if os.path.exists(path):
        return path
    try:
        r = requests.get(url, timeout=20)
        r.raise_for_status()
        img = PILImage.open(BytesIO(r.content)).convert("RGB")
        img.save(path, "JPEG", quality=92)
        print(f"  βœ“ {name}")
        return path
    except Exception as e:
        print(f"  βœ— {name}: {e}")
        return None

def get_image_flowable(path, max_w=15*cm, max_h=7*cm):
    if not path or not os.path.exists(path):
        return Spacer(1, 0.5*cm)
    img = PILImage.open(path)
    w, h = img.size
    ratio = w / h
    if w / max_w > h / max_h:
        nw = max_w
        nh = max_w / ratio
    else:
        nh = max_h
        nw = max_h * ratio
    return Image(path, width=nw, height=nh)

# ── Styles ──────────────────────────────────────────────────────────────────────
def build_styles():
    base = getSampleStyleSheet()
    styles = {}

    styles["cover_title"] = ParagraphStyle(
        "cover_title", parent=base["Title"],
        fontSize=32, textColor=white, alignment=TA_CENTER,
        spaceAfter=8, fontName="Helvetica-Bold"
    )
    styles["cover_sub"] = ParagraphStyle(
        "cover_sub", parent=base["Normal"],
        fontSize=14, textColor=HexColor("#ECF0F1"), alignment=TA_CENTER,
        spaceAfter=4, fontName="Helvetica"
    )
    styles["disease_num"] = ParagraphStyle(
        "disease_num", parent=base["Normal"],
        fontSize=11, textColor=white, fontName="Helvetica-Bold",
        alignment=TA_CENTER
    )
    styles["disease_title"] = ParagraphStyle(
        "disease_title", parent=base["Heading1"],
        fontSize=18, textColor=white, fontName="Helvetica-Bold",
        spaceAfter=0, spaceBefore=0
    )
    styles["section_h2"] = ParagraphStyle(
        "section_h2", parent=base["Heading2"],
        fontSize=12, textColor=PRIMARY, fontName="Helvetica-Bold",
        spaceBefore=10, spaceAfter=4, borderPadding=(0,0,2,0)
    )
    styles["body"] = ParagraphStyle(
        "body", parent=base["Normal"],
        fontSize=10, textColor=SECONDARY, fontName="Helvetica",
        leading=15, spaceAfter=4, alignment=TA_JUSTIFY
    )
    styles["bullet"] = ParagraphStyle(
        "bullet", parent=base["Normal"],
        fontSize=10, textColor=SECONDARY, fontName="Helvetica",
        leading=14, leftIndent=14, spaceAfter=3,
        bulletIndent=4, bulletFontName="Helvetica"
    )
    styles["caption"] = ParagraphStyle(
        "caption", parent=base["Normal"],
        fontSize=8.5, textColor=HexColor("#7F8C8D"), fontName="Helvetica-Oblique",
        alignment=TA_CENTER, spaceAfter=6
    )
    styles["img_label"] = ParagraphStyle(
        "img_label", parent=base["Normal"],
        fontSize=9.5, textColor=PRIMARY, fontName="Helvetica-Bold",
        spaceBefore=8, spaceAfter=3
    )
    styles["footer_note"] = ParagraphStyle(
        "footer_note", parent=base["Normal"],
        fontSize=8, textColor=HexColor("#95A5A6"), fontName="Helvetica-Oblique",
        alignment=TA_CENTER
    )
    styles["summary_title"] = ParagraphStyle(
        "summary_title", parent=base["Heading1"],
        fontSize=20, textColor=PRIMARY, fontName="Helvetica-Bold",
        spaceAfter=12, alignment=TA_CENTER
    )
    return styles

# ── Cover page ──────────────────────────────────────────────────────────────────
def cover_page(styles):
    story = []

    # Red banner
    banner = Table(
        [[Paragraph("10 Common Heart Diseases", styles["cover_title"]),
          Paragraph("with ECG Findings &amp; Images", styles["cover_sub"])]],
        colWidths=[17*cm],
        rowHeights=[None]
    )
    banner.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,-1), PRIMARY),
        ("ALIGN", (0,0), (-1,-1), "CENTER"),
        ("VALIGN", (0,0), (-1,-1), "MIDDLE"),
        ("TOPPADDING", (0,0), (-1,-1), 30),
        ("BOTTOMPADDING", (0,0), (-1,-1), 30),
        ("LEFTPADDING", (0,0), (-1,-1), 20),
        ("RIGHTPADDING", (0,0), (-1,-1), 20),
    ]))

    # Stack title lines in the banner
    cover_tbl = Table(
        [[Paragraph("10 Common Heart Diseases", styles["cover_title"])],
         [Paragraph("with ECG Findings &amp; Images", styles["cover_sub"])],
         [Spacer(1, 8)],
         [Paragraph("A Clinical ECG Reference Guide", ParagraphStyle(
             "cs3", fontSize=12, textColor=HexColor("#BDC3C7"),
             fontName="Helvetica", alignment=TA_CENTER
         ))]],
        colWidths=[17*cm]
    )
    cover_tbl.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,-1), PRIMARY),
        ("ALIGN", (0,0), (-1,-1), "CENTER"),
        ("TOPPADDING", (0,0), (-1,-1), 8),
        ("BOTTOMPADDING", (0,0), (-1,-1), 8),
    ]))

    story.append(Spacer(1, 3*cm))
    story.append(cover_tbl)
    story.append(Spacer(1, 1.5*cm))

    # TOC box
    toc_data = [
        ["#", "Disease", "Key ECG Feature"],
        ["1", "Acute MI (STEMI)", "ST elevation, Q-waves"],
        ["2", "Atrial Fibrillation", "Absent P waves, irregular RR"],
        ["3", "Complete Heart Block", "AV dissociation, slow escape"],
        ["4", "Ventricular Tachycardia", "Wide QRS >120 ms, AV dissociation"],
        ["5", "Acute Pericarditis", "Saddle-shaped ST elevation, PR depression"],
        ["6", "Left Ventricular Hypertrophy", "High voltage, lateral strain pattern"],
        ["7", "WPW Syndrome", "Short PR, delta wave, widened QRS"],
        ["8", "Hypertrophic Cardiomyopathy", "Giant T-inversions, deep Q-waves"],
        ["9", "Dilated Cardiomyopathy", "LBBB, low voltage, AF"],
        ["10", "Long QT Syndrome", "QTc β‰₯500 ms, Torsades de Pointes"],
    ]
    toc_style = TableStyle([
        ("BACKGROUND", (0,0), (-1,0), PRIMARY),
        ("TEXTCOLOR", (0,0), (-1,0), white),
        ("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
        ("FONTSIZE", (0,0), (-1,0), 10),
        ("ALIGN", (0,0), (0,-1), "CENTER"),
        ("ALIGN", (1,0), (-1,-1), "LEFT"),
        ("FONTNAME", (0,1), (-1,-1), "Helvetica"),
        ("FONTSIZE", (0,1), (-1,-1), 9),
        ("TEXTCOLOR", (0,1), (-1,-1), SECONDARY),
        ("ROWBACKGROUNDS", (0,1), (-1,-1), [TABLE_ROW, TABLE_ALT]),
        ("GRID", (0,0), (-1,-1), 0.5, HexColor("#BDC3C7")),
        ("TOPPADDING", (0,0), (-1,-1), 5),
        ("BOTTOMPADDING", (0,0), (-1,-1), 5),
        ("LEFTPADDING", (0,0), (-1,-1), 8),
        ("RIGHTPADDING", (0,0), (-1,-1), 8),
    ])
    toc_table = Table(toc_data, colWidths=[1.2*cm, 5.8*cm, 9*cm])
    toc_table.setStyle(toc_style)
    story.append(toc_table)
    story.append(Spacer(1, 2*cm))
    story.append(Paragraph(
        "Sources: Braunwald's Heart Disease (10th ed.) Β· Rosen's Emergency Medicine Β· PMC Clinical VQA Dataset",
        styles["footer_note"]
    ))
    story.append(PageBreak())
    return story

# ── Disease header ──────────────────────────────────────────────────────────────
def disease_header(num, title, styles):
    num_cell = Paragraph(f"#{num}", styles["disease_num"])
    title_cell = Paragraph(title, styles["disease_title"])
    t = Table([[num_cell, title_cell]], colWidths=[1.5*cm, 15*cm])
    t.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,-1), PRIMARY),
        ("VALIGN", (0,0), (-1,-1), "MIDDLE"),
        ("TOPPADDING", (0,0), (-1,-1), 10),
        ("BOTTOMPADDING", (0,0), (-1,-1), 10),
        ("LEFTPADDING", (0,0), (-1,-1), 10),
        ("RIGHTPADDING", (0,0), (-1,-1), 10),
        ("LINEBELOW", (0,0), (-1,0), 3, GOLD),
    ]))
    return t

def ecg_table(data, styles):
    """Renders a 2-col ECG features table."""
    header = [
        Paragraph("ECG Feature", ParagraphStyle("th", fontSize=10, fontName="Helvetica-Bold", textColor=white)),
        Paragraph("Finding", ParagraphStyle("th2", fontSize=10, fontName="Helvetica-Bold", textColor=white)),
    ]
    rows = [header]
    for feat, finding in data:
        rows.append([
            Paragraph(feat, ParagraphStyle("td1", fontSize=9.5, fontName="Helvetica-Bold", textColor=SECONDARY, leading=13)),
            Paragraph(finding, ParagraphStyle("td2", fontSize=9.5, fontName="Helvetica", textColor=SECONDARY, leading=13)),
        ])
    t = Table(rows, colWidths=[5.5*cm, 11*cm])
    t.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,0), SECONDARY),
        ("ROWBACKGROUNDS", (0,1), (-1,-1), [TABLE_ROW, TABLE_ALT]),
        ("GRID", (0,0), (-1,-1), 0.4, HexColor("#BDC3C7")),
        ("TOPPADDING", (0,0), (-1,-1), 5),
        ("BOTTOMPADDING", (0,0), (-1,-1), 5),
        ("LEFTPADDING", (0,0), (-1,-1), 8),
        ("RIGHTPADDING", (0,0), (-1,-1), 8),
        ("VALIGN", (0,0), (-1,-1), "MIDDLE"),
    ]))
    return t

# ── ECG image block ─────────────────────────────────────────────────────────────
def ecg_block(img_path, label, caption, styles):
    items = []
    items.append(Paragraph(f"ECG: {label}", styles["img_label"]))
    items.append(get_image_flowable(img_path))
    items.append(Paragraph(caption, styles["caption"]))
    return items

# ── Main content ────────────────────────────────────────────────────────────────
def build_story(img_paths, styles):
    story = []
    story += cover_page(styles)

    # ── 1. STEMI ──────────────────────────────────────────────────────────────
    story.append(disease_header(1, "Acute Myocardial Infarction (STEMI)", styles))
    story.append(Spacer(1, 6))
    story.append(Paragraph(
        "A <b>ST-Elevation Myocardial Infarction</b> occurs when complete coronary artery occlusion causes transmural (full-thickness) myocardial necrosis. Plaque rupture β†’ thrombosis β†’ complete occlusion β†’ ischemia β†’ injury β†’ necrosis. It is a life-threatening emergency requiring immediate reperfusion (PCI within 90 minutes).",
        styles["body"]
    ))
    story.append(Paragraph("<b>ECG Findings:</b>", styles["section_h2"]))
    story.append(ecg_table([
        ("Hyperacute phase", "Tall, peaked 'hyperacute' T-waves (minutes)"),
        ("Acute phase (hours)", "Convex/tombstone ST elevation β‰₯1 mm (β‰₯2 mm in V1–V3)"),
        ("Evolving (days)", "Pathological Q-waves (>40 ms, >25% QRS height), T-wave inversion"),
        ("Anterior STEMI", "ST elevation V1–V4; reciprocal depression II, III, aVF (LAD occlusion)"),
        ("Inferior STEMI", "ST elevation II, III, aVF; reciprocal depression I, aVL (RCA occlusion)"),
        ("Lateral STEMI", "ST elevation I, aVL, V5–V6 (LCx occlusion)"),
    ], styles))
    story.append(Spacer(1, 8))
    story += ecg_block(img_paths.get("anterior_stemi"), "Anterior STEMI",
        "Convex 'tombstoning' ST elevation in V2–V5, QS waves indicating necrosis (LAD occlusion). Reciprocal ST depression in inferior leads.",
        styles)
    story += ecg_block(img_paths.get("inferior_stemi"), "Inferior STEMI",
        "β‰₯2 mm convex ST elevation in II, III, aVF; pathologic Q-waves; reciprocal ST depression in I and aVL (RCA occlusion).",
        styles)
    story.append(PageBreak())

    # ── 2. AF ─────────────────────────────────────────────────────────────────
    story.append(disease_header(2, "Atrial Fibrillation (AF)", styles))
    story.append(Spacer(1, 6))
    story.append(Paragraph(
        "AF is the most common sustained cardiac arrhythmia, affecting over 37 million people worldwide. Disorganized atrial electrical activity produces an irregular, often rapid ventricular response with no effective atrial contraction, increasing stroke risk 5-fold. Multiple re-entrant circuits (often triggered by pulmonary vein foci) cause the AV node to conduct impulses irregularly.",
        styles["body"]
    ))
    story.append(Paragraph("<b>ECG Findings:</b>", styles["section_h2"]))
    story.append(ecg_table([
        ("P waves", "Absent β€” replaced by fine fibrillatory baseline (best seen in V1)"),
        ("Rhythm", "Irregularly irregular R-R intervals"),
        ("Rate", "Ventricular rate 100–180 bpm (uncontrolled); <100 bpm (rate-controlled)"),
        ("QRS", "Usually narrow unless aberrant conduction or pre-excitation"),
    ], styles))
    story.append(Spacer(1, 8))
    story += ecg_block(img_paths.get("af"), "Atrial Fibrillation with Rapid Ventricular Response",
        "No P waves, chaotic fibrillatory baseline in V1, classic irregularly irregular rhythm, narrow QRS complexes, rapid ventricular rate.",
        styles)
    story.append(PageBreak())

    # ── 3. Complete Heart Block ───────────────────────────────────────────────
    story.append(disease_header(3, "Complete Heart Block (3rd Degree AV Block)", styles))
    story.append(Spacer(1, 6))
    story.append(Paragraph(
        "<b>Complete AV block</b> is failure of all atrial impulses to conduct to the ventricles. The atria and ventricles beat independently. It requires urgent pacemaker implantation and can cause syncope or sudden death. Complete interruption at the AV node, Bundle of His, or below causes total AV dissociation; an escape rhythm (junctional or ventricular) maintains a slow heart rate.",
        styles["body"]
    ))
    story.append(Paragraph("<b>ECG Findings:</b>", styles["section_h2"]))
    story.append(ecg_table([
        ("P waves", "Regular, at faster rate (60–100 bpm) β€” independent"),
        ("QRS complexes", "Regular, at slower escape rate (20–50 bpm) β€” independent"),
        ("PR interval", "No fixed relationship β€” complete AV dissociation"),
        ("QRS morphology", "Narrow if junctional escape; Wide (>120 ms) if ventricular escape"),
        ("Clinical", "Profound bradycardia, syncope (Stokes-Adams attacks), haemodynamic compromise"),
    ], styles))
    story.append(Spacer(1, 8))
    story += ecg_block(img_paths.get("chb"), "Complete Heart Block",
        "Regular P waves at ~80 bpm completely independent from junctional escape QRS complexes at ~40 bpm. No fixed PR interval β€” hallmark of 3rd degree AV block.",
        styles)
    story.append(PageBreak())

    # ── 4. VT ─────────────────────────────────────────────────────────────────
    story.append(disease_header(4, "Ventricular Tachycardia (VT)", styles))
    story.append(Spacer(1, 6))
    story.append(Paragraph(
        "VT is a potentially life-threatening arrhythmia arising from the ventricles, defined as β‰₯3 consecutive ventricular beats at rate >100 bpm. Sustained VT (>30 seconds) causes haemodynamic compromise and can degenerate to ventricular fibrillation (VF). Re-entry circuits within diseased ventricular myocardium (scar, ischaemia, cardiomyopathy) are the most common mechanism.",
        styles["body"]
    ))
    story.append(Paragraph("<b>ECG Findings:</b>", styles["section_h2"]))
    story.append(ecg_table([
        ("Rate", "100–250 bpm, usually regular"),
        ("QRS duration", "Wide (>120 ms), bizarre morphology"),
        ("P waves", "Absent or dissociated from QRS (AV dissociation β€” pathognomonic)"),
        ("Axis", "Often extreme axis deviation (northwest axis)"),
        ("Fusion beats", "Pathognomonic when present β€” simultaneous sinus + VT activation"),
        ("Capture beats", "Narrow QRS during VT β€” confirms AV dissociation"),
        ("Concordance", "Positive concordance V1–V6 strongly suggests VT over SVT"),
    ], styles))
    story.append(Spacer(1, 8))
    story += ecg_block(img_paths.get("vt"), "Monomorphic Ventricular Tachycardia",
        "High-amplitude wide QRS at ~170 bpm, positive concordance V1–V6, superior axis, no visible P waves. Consistent with VT from structural heart disease.",
        styles)
    story.append(PageBreak())

    # ── 5. Pericarditis ───────────────────────────────────────────────────────
    story.append(disease_header(5, "Acute Pericarditis", styles))
    story.append(Spacer(1, 6))
    story.append(Paragraph(
        "Pericarditis is inflammation of the pericardial sac, most commonly viral (Coxsackievirus, Echovirus) but also autoimmune, bacterial, or post-MI (Dressler's syndrome). Classic presentation: sharp pleuritic chest pain relieved by leaning forward. Pericardial inflammation causes superficial myocardial irritation producing <i>diffuse</i> repolarisation changes across all leads simultaneously β€” unlike STEMI which follows a single coronary territory.",
        styles["body"]
    ))
    story.append(Paragraph("<b>ECG Findings (4 Classic Stages):</b>", styles["section_h2"]))
    story.append(ecg_table([
        ("Stage I (acute)", "Diffuse concave 'saddle-shaped' ST elevation in all leads except aVR and V1"),
        ("PR depression", "PR segment depression in inferior/lateral leads β€” MOST SPECIFIC sign"),
        ("aVR", "Reciprocal ST depression AND PR elevation in aVR"),
        ("Spodick's sign", "Downsloping TP segment β€” high specificity for pericarditis"),
        ("Stage II (days)", "ST returns to baseline; T-waves flatten"),
        ("Stage III (weeks)", "T-wave inversion in previously elevated leads"),
        ("vs STEMI", "Diffuse (not localised), concave (not convex), no reciprocal ST depression"),
    ], styles))
    story.append(Spacer(1, 8))
    story += ecg_block(img_paths.get("pericarditis"), "Acute Pericarditis",
        "Diffuse saddle-shaped ST elevation in I, II, III, aVF, V2–V6; PR depression in II; PR elevation in aVR; Spodick's sign. Classic features distinguishing pericarditis from STEMI.",
        styles)
    story.append(PageBreak())

    # ── 6. LVH ────────────────────────────────────────────────────────────────
    story.append(disease_header(6, "Left Ventricular Hypertrophy (LVH)", styles))
    story.append(Spacer(1, 6))
    story.append(Paragraph(
        "LVH is thickening of the left ventricular walls, most commonly from chronic hypertension, aortic stenosis, or hypertrophic cardiomyopathy. LVH is an independent risk factor for heart failure, arrhythmias, and sudden death. Chronic pressure overload causes myocyte hypertrophy β†’ increased muscle mass β†’ greater electrical voltage on ECG. Impaired subendocardial perfusion produces the characteristic 'strain' pattern.",
        styles["body"]
    ))
    story.append(Paragraph("<b>ECG Findings:</b>", styles["section_h2"]))
    story.append(ecg_table([
        ("Sokolow-Lyon criterion", "S in V1 + R in V5 or V6 β‰₯35 mm"),
        ("Cornell criterion", "R in aVL + S in V3 β‰₯28 mm (men), β‰₯20 mm (women)"),
        ("Strain pattern", "ST depression + T-wave inversion in lateral leads (I, aVL, V5, V6)"),
        ("Left axis deviation", "Common in LVH"),
        ("P mitrale", "Broad, notched P wave β€” suggests associated left atrial enlargement"),
    ], styles))
    story.append(Spacer(1, 8))
    story += ecg_block(img_paths.get("lvh"), "LVH with Lateral Strain Pattern",
        "Tall R-waves in V5–V6 and deep S-waves in V1–V2 (meeting Sokolow-Lyon criteria); ST depression and T-inversion in II, III, aVF, V5, V6 β€” classic pressure-overload 'strain pattern'.",
        styles)
    story.append(PageBreak())

    # ── 7. WPW ────────────────────────────────────────────────────────────────
    story.append(disease_header(7, "Wolff-Parkinson-White (WPW) Syndrome", styles))
    story.append(Spacer(1, 6))
    story.append(Paragraph(
        "WPW is a pre-excitation syndrome caused by an accessory pathway (Bundle of Kent) bypassing the AV node, connecting atria directly to ventricles. The accessory pathway conducts faster than the AV node, activating part of the ventricle early β€” producing the delta wave and widened QRS. During tachycardia, re-entry circuits use both the AV node and accessory pathway. Risk of sudden death via rapid conduction of AF to the ventricles.",
        styles["body"]
    ))
    story.append(Paragraph("<b>ECG Findings (Classic Triad):</b>", styles["section_h2"]))
    story.append(ecg_table([
        ("PR interval", "Short <120 ms (bypasses AV nodal delay)"),
        ("Delta wave", "Slurred upstroke at start of QRS β€” hallmark"),
        ("QRS duration", "Widened >120 ms due to delta wave"),
        ("ST-T changes", "Discordant β€” secondary to abnormal depolarisation"),
        ("Pseudo-infarction", "Negative delta waves can mimic Q-waves (inferior leads)"),
        ("Localisation", "Delta wave polarity in V1 and inferior leads identifies accessory pathway location"),
    ], styles))
    story.append(Spacer(1, 8))
    story += ecg_block(img_paths.get("wpw"), "Wolff-Parkinson-White Syndrome",
        "Short PR (<120 ms), prominent delta waves (slurred QRS upstroke) in II, III, aVF, V2–V6, secondary ST-T discordance β€” classic triad of ventricular pre-excitation.",
        styles)
    story.append(PageBreak())

    # ── 8. HCM ────────────────────────────────────────────────────────────────
    story.append(disease_header(8, "Hypertrophic Cardiomyopathy (HCM)", styles))
    story.append(Spacer(1, 6))
    story.append(Paragraph(
        "HCM is a genetic disorder (autosomal dominant, most commonly <i>MYH7</i> or <i>MYBPC3</i> mutations) causing asymmetric left ventricular hypertrophy, usually of the interventricular septum. Myofiber disarray and fibrosis disrupt normal electrical conduction. It is the leading cause of sudden cardiac death in young athletes. Dynamic LV outflow tract obstruction causes exertional dyspnea, syncope, and chest pain.",
        styles["body"]
    ))
    story.append(Paragraph("<b>ECG Findings:</b>", styles["section_h2"]))
    story.append(ecg_table([
        ("LVH voltage", "High QRS voltage meeting LVH criteria in ~90% of patients"),
        ("Q-waves", "Deep narrow Q-waves in lateral/inferior leads (abnormal septal depolarisation)"),
        ("T-wave inversions", "Deep symmetric T-inversion in lateral leads; 'giant' T-inversion in apical HCM (Yamaguchi, >10 mm)"),
        ("Left axis deviation", "Common"),
        ("Apical HCM (Yamaguchi)", "Giant symmetric T-wave inversions V3–V6, high precordial voltage β€” 'ace of spades' LV on imaging"),
        ("Arrhythmias", "AF, VT common in advanced disease; risk of sudden cardiac death"),
    ], styles))
    story.append(Spacer(1, 8))
    story += ecg_block(img_paths.get("hcm"), "Apical HCM (Yamaguchi Syndrome)",
        "Sinus rhythm, high-voltage precordial QRS, giant symmetric T-wave inversions most prominent in V4–V6 with ST depression β€” hallmark of apical hypertrophic cardiomyopathy.",
        styles)
    story.append(PageBreak())

    # ── 9. DCM ────────────────────────────────────────────────────────────────
    story.append(disease_header(9, "Dilated Cardiomyopathy (DCM) / Heart Failure with Reduced EF", styles))
    story.append(Spacer(1, 6))
    story.append(Paragraph(
        "DCM is characterised by ventricular dilatation and systolic dysfunction (reduced EF <40%). Causes include idiopathic, viral myocarditis, alcohol, chemotherapy, and genetic. Myocyte loss and fibrosis impair both contraction and electrical conduction. Ventricular dilation distorts the His-Purkinje system, commonly producing bundle branch blocks. Fibrosis creates re-entry substrates for VT and sudden cardiac death.",
        styles["body"]
    ))
    story.append(Paragraph("<b>ECG Findings:</b>", styles["section_h2"]))
    story.append(ecg_table([
        ("LBBB", "QRS >120 ms, broad slurred R in I, aVL, V5–V6; rS in V1 β€” most common finding in DCM"),
        ("RBBB", "RSR' in V1, broad S in I, V5–V6"),
        ("Low voltage", "QRS <5 mm in limb leads (extensive fibrosis/pericardial effusion)"),
        ("PR prolongation", "1st degree AV block common"),
        ("Atrial fibrillation", "Frequent β€” marker of poor prognosis"),
        ("ST-T changes", "Diffuse nonspecific T-inversion, secondary repolarisation changes"),
        ("Indication for CRT", "LBBB with QRS >150 ms + EF ≀35% β€” cardiac resynchronisation therapy"),
    ], styles))
    story.append(Spacer(1, 8))
    story += ecg_block(img_paths.get("dcm"), "DCM with LBBB β€” Indication for CRT",
        "1st degree AV block (PR = 280 ms) and wide LBBB (QRS = 145 ms) in a patient with NYHA Class IV DCM. Indicates ventricular dyssynchrony β€” key indication for Cardiac Resynchronization Therapy.",
        styles)
    story.append(PageBreak())

    # ── 10. LQTS ──────────────────────────────────────────────────────────────
    story.append(disease_header(10, "Long QT Syndrome (LQTS)", styles))
    story.append(Spacer(1, 6))
    story.append(Paragraph(
        "LQTS is a disorder of ventricular repolarisation causing a prolonged QT interval. It can be <b>congenital</b> (mutations in cardiac ion channels: <i>KCNQ1</i> β€” LQT1, <i>KCNH2</i> β€” LQT2, <i>SCN5A</i> β€” LQT3) or <b>acquired</b> (drugs, hypokalaemia, hypomagnesaemia). Reduced outward K⁺ current or increased inward Na⁺/Ca²⁺ current prolongs action potential phase 3 β†’ early afterdepolarisations β†’ <b>Torsades de Pointes (TdP)</b> β€” polymorphic VT that can degenerate to VF and sudden death.",
        styles["body"]
    ))
    story.append(Paragraph("<b>ECG Findings:</b>", styles["section_h2"]))
    story.append(ecg_table([
        ("QTc (Bazett)", "QT Γ· √RR; β‰₯450 ms (men), β‰₯460 ms (women) = abnormal; β‰₯500 ms = high risk"),
        ("LQT1 morphology", "Broad-based, symmetric T-wave (K channel defect; triggers with exercise)"),
        ("LQT2 morphology", "Bifid or notched T-wave (triggers with sudden auditory stimuli)"),
        ("LQT3 morphology", "Long flat isoelectric ST, late-peaked narrow T-wave (triggers at rest/sleep)"),
        ("T-wave alternans", "Beat-to-beat variation in T amplitude β€” precursor to TdP"),
        ("Torsades de Pointes", "Polymorphic VT; QRS 'twisting' around isoelectric line; initiated by short-long-short sequence"),
    ], styles))
    story.append(Spacer(1, 8))
    story += ecg_block(img_paths.get("lqts"), "Long QT Syndrome and Torsades de Pointes",
        "Panel (a): Markedly prolonged QTc (478 ms) in sinus rhythm. Panel (b): Classic 'twisting' polymorphic Torsades de Pointes triggered by the prolonged repolarisation β€” a precursor to ventricular fibrillation.",
        styles)
    story.append(PageBreak())

    # ── Summary Table ─────────────────────────────────────────────────────────
    story.append(Paragraph("Quick Reference Summary", styles["summary_title"]))
    story.append(HRFlowable(width="100%", thickness=2, color=PRIMARY))
    story.append(Spacer(1, 10))

    summary_data = [
        ["#", "Disease", "Key ECG Feature", "Emergency?"],
        ["1",  "Anterior STEMI",               "Convex ST elevation V1–V4; Q-waves",           "YES"],
        ["2",  "Atrial Fibrillation",           "Absent P waves; irregularly irregular",         "If RVR"],
        ["3",  "Complete Heart Block",          "AV dissociation; slow escape rhythm",           "YES"],
        ["4",  "Ventricular Tachycardia",       "Wide QRS >120 ms; AV dissociation",             "YES"],
        ["5",  "Acute Pericarditis",            "Saddle ST elevation; PR depression",            "Monitor"],
        ["6",  "LVH",                           "High voltage β‰₯35 mm; lateral strain",           "No"],
        ["7",  "WPW Syndrome",                  "Short PR; delta wave; widened QRS",             "If SVT/AF"],
        ["8",  "Hypertrophic CMP",              "Giant T-inversions V3–V6; deep Q",              "Athlete SCD"],
        ["9",  "Dilated CMP / HFrEF",           "LBBB; 1st AV block; low voltage; AF",           "If decompensated"],
        ["10", "Long QT Syndrome",              "QTc β‰₯500 ms; T alternans; TdP",                 "YES"],
    ]

    def cell(txt, bold=False, color=SECONDARY, size=9.5):
        style = ParagraphStyle("sc", fontSize=size, fontName="Helvetica-Bold" if bold else "Helvetica",
                               textColor=color, leading=13)
        return Paragraph(txt, style)

    formatted = [[cell(r[0], bold=True), cell(r[1], bold=True), cell(r[2]), cell(r[3])]
                 for r in summary_data[1:]]
    header_row = [cell(h, bold=True, color=white, size=10) for h in summary_data[0]]

    sum_style = TableStyle([
        ("BACKGROUND", (0,0), (-1,0), PRIMARY),
        ("ROWBACKGROUNDS", (0,1), (-1,-1), [TABLE_ROW, TABLE_ALT]),
        ("GRID", (0,0), (-1,-1), 0.4, HexColor("#BDC3C7")),
        ("TOPPADDING", (0,0), (-1,-1), 6),
        ("BOTTOMPADDING", (0,0), (-1,-1), 6),
        ("LEFTPADDING", (0,0), (-1,-1), 8),
        ("RIGHTPADDING", (0,0), (-1,-1), 8),
        ("ALIGN", (0,0), (0,-1), "CENTER"),
        ("ALIGN", (-1,1), (-1,-1), "CENTER"),
        ("BACKGROUND", (-1,1), (-1,1), HexColor("#E74C3C")),   # YES red
        ("BACKGROUND", (-1,3), (-1,3), HexColor("#E74C3C")),
        ("BACKGROUND", (-1,4), (-1,4), HexColor("#E74C3C")),
        ("BACKGROUND", (-1,10), (-1,10), HexColor("#E74C3C")),
        ("TEXTCOLOR", (-1,1), (-1,1), white),
        ("TEXTCOLOR", (-1,3), (-1,3), white),
        ("TEXTCOLOR", (-1,4), (-1,4), white),
        ("TEXTCOLOR", (-1,10), (-1,10), white),
        ("FONTNAME", (-1,1), (-1,1), "Helvetica-Bold"),
        ("FONTNAME", (-1,3), (-1,3), "Helvetica-Bold"),
        ("FONTNAME", (-1,4), (-1,4), "Helvetica-Bold"),
        ("FONTNAME", (-1,10), (-1,10), "Helvetica-Bold"),
    ])

    sum_tbl = Table([header_row] + formatted, colWidths=[1*cm, 5*cm, 8.5*cm, 2.5*cm])
    sum_tbl.setStyle(sum_style)
    story.append(sum_tbl)

    story.append(Spacer(1, 1.5*cm))
    story.append(HRFlowable(width="100%", thickness=1, color=HexColor("#BDC3C7")))
    story.append(Spacer(1, 6))
    story.append(Paragraph(
        "⚠ Clinical Note: ECG interpretation must always be correlated with clinical history, physical examination, and additional investigations (troponin, echocardiography, cardiac MRI). No single ECG finding is pathognomonic in isolation.",
        ParagraphStyle("note", fontSize=9, fontName="Helvetica-Oblique", textColor=HexColor("#7F8C8D"),
                       alignment=TA_CENTER, leading=13)
    ))
    story.append(Spacer(1, 4))
    story.append(Paragraph(
        "Sources: Braunwald's Heart Disease (10th ed.) Β· Rosen's Emergency Medicine Β· PMC Clinical VQA Dataset",
        styles["footer_note"]
    ))
    return story

# ── Page template with header/footer ───────────────────────────────────────────
def on_page(canvas, doc):
    canvas.saveState()
    w, h = A4
    # Header bar on non-cover pages
    if doc.page > 1:
        canvas.setFillColor(PRIMARY)
        canvas.rect(0, h - 1.2*cm, w, 1.2*cm, fill=1, stroke=0)
        canvas.setFont("Helvetica-Bold", 9)
        canvas.setFillColor(white)
        canvas.drawString(1.5*cm, h - 0.8*cm, "10 Common Heart Diseases β€” ECG Reference Guide")
        canvas.drawRightString(w - 1.5*cm, h - 0.8*cm, f"Page {doc.page}")
    # Footer
    canvas.setFont("Helvetica", 7.5)
    canvas.setFillColor(HexColor("#95A5A6"))
    canvas.drawCentredString(w/2, 0.7*cm, "Sources: Braunwald's Heart Disease Β· Rosen's Emergency Medicine Β· PMC Clinical VQA")
    canvas.restoreState()

# ── Build PDF ───────────────────────────────────────────────────────────────────
def main():
    print("Downloading ECG images...")
    img_paths = {}
    for name, url in IMAGES.items():
        img_paths[name] = download_image(url, name)

    print("Building PDF...")
    styles = build_styles()
    story = build_story(img_paths, styles)

    doc = SimpleDocTemplate(
        OUTPUT_PDF, pagesize=A4,
        leftMargin=2*cm, rightMargin=2*cm,
        topMargin=1.8*cm, bottomMargin=1.6*cm,
        title="10 Common Heart Diseases β€” ECG Reference Guide",
        author="Orris Medical AI",
        subject="Cardiology ECG Reference"
    )
    doc.build(story, onFirstPage=on_page, onLaterPages=on_page)
    print(f"\nβœ“ PDF created: {OUTPUT_PDF}")

if __name__ == "__main__":
    main()
You've run out of free credits. Sign up for a free account to continue using Orris.
This is a shared conversation. Sign in to Orris to start your own chat.