Detailed Ppt on normal labour from oxorn foote,d.c dutta with pictures inserted at all topics

Reading File
Reading File
Finding Sources
Reading File
Finding Sources
Finding Sources
Finding Sources
Finding Sources
Finding Sources
Finding Sources
Searching the Web

normal labour mechanism cardinal movements obstetric diagram images

Finding Sources
Searching the Web

normal labour stages cervical dilation partogram diagram obstetrics DC Dutta

Running Command

mkdir -p /tmp/workspace/normal-labour-ppt && ls /tmp/workspace/

Running Command

python /tmp/skills/shared/scripts/fetch_images.py \ "https://cdn.lecturio.com/assets/The-cardinal-movements-of-labor.jpg" \ "https://d45jl3w9libvn.cloudfront.net/jaypee/static/books/9789351527237/Chapters/images/151-1.jpg" \ "https://d45jl3w9libvn.cloudfront.net/jaypee/static/books/9789351527237/Chapters/images/464-1.jpg" \ "https://image.slidesharecdn.com/partograph-201001064106/85/Partograph-2-320.jpg" \ "https://media.springernature.com/lw685/springer-static/image/chp%3A10.1007%2F978-981-19-6145-8_14/MediaObjects/528146_1_En_14_Fig1_HTML.png" 2>&1 | head -20

Running Command

ls -la /tmp/workspace/normal-labour-ppt/ 2>/dev/null && echo "---" && ls /tmp/workspace/ 2>/dev/null

Running Command

python3 << 'PYEOF' import requests, os, sys from pathlib import Path out = Path("/tmp/workspace/normal-labour-ppt") out.mkdir(parents=True, exist_ok=True) headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120 Safari/537.36"} urls = { "cardinal": "https://www.lecturio.com/magazine/wp-content/uploads/2021/03/cardinal-movements-of-labor.jpg", "engagement": "https://upload.wikimedia.org/wikipedia/commons/thumb/8/82/Fetal_head_engagement_diagram.svg/640px-Fetal_head_engagement_diagram.svg.png", "partograph": "https://upload.wikimedia.org/wikipedia/commons/thumb/0/0f/Partograph.jpg/800px-Partograph.jpg", "pelvis": "https://upload.wikimedia.org/wikipedia/commons/thumb/e/e3/Pelvis_diagram.svg/640px-Pelvis_diagram.svg.png", "cervix": "https://upload.wikimedia.org/wikipedia/commons/thumb/4/4e/Cervical_dilation_vs_effacement.svg/640px-Cervical_dilation_vs_effacement.svg.png", "fetal_skull": "https://upload.wikimedia.org/wikipedia/commons/thumb/0/06/Fetal_skull_diagram.svg/640px-Fetal_skull_diagram.svg.png", "stages": "https://upload.wikimedia.org/wikipedia/commons/thumb/b/b5/Stages_of_labor.svg/640px-Stages_of_labor.svg.png", } saved = {} for name, url in urls.items(): try: r = requests.get(url, headers=headers, timeout=15) if r.status_code == 200 and len(r.content) > 1000: ext = ".png" if url.endswith(".png") or url.endswith(".svg.png") else ".jpg" p = out / f"img_{name}{ext}" p.write_bytes(r.content) saved[name] = str(p) print(f"OK {name}: {len(r.content)} bytes -> {p.name}") else: print(f"FAIL {name}: status {r.status_code}, size {len(r.content)}") except Exception as e: print(f"ERR {name}: {e}") print("\nSaved:", list(saved.keys())) PYEOF

Writing File

~/normal-labour-ppt/build_ppt.py

"""
Normal Labour PowerPoint Builder
Uses python-pptx + matplotlib-generated images for every slide.
"""
import os
import io
import math
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
import matplotlib.patches as patches
from matplotlib.patches import FancyArrowPatch, Arc, Circle, FancyBboxPatch, Wedge
from matplotlib.lines import Line2D
import numpy as np
from pathlib import Path
from pptx import Presentation
from pptx.util import Inches, Pt, Emu
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN
from pptx.util import Inches, Pt

OUT = Path("/tmp/workspace/normal-labour-ppt")
OUT.mkdir(parents=True, exist_ok=True)

# ── Colour palette ───────────────────────────────────────────────────────────
NAVY   = RGBColor(0x1A, 0x3A, 0x5C)
TEAL   = RGBColor(0x00, 0x7A, 0x87)
GOLD   = RGBColor(0xF5, 0xA6, 0x23)
WHITE  = RGBColor(0xFF, 0xFF, 0xFF)
LGRAY  = RGBColor(0xF0, 0xF4, 0xF8)
DKGRAY = RGBColor(0x33, 0x33, 0x33)
RED    = RGBColor(0xC0, 0x39, 0x2B)
GREEN  = RGBColor(0x27, 0xAE, 0x60)

# ── Slide dimensions ─────────────────────────────────────────────────────────
W_IN = 13.333
H_IN = 7.5

# ─────────────────────────────────────────────────────────────────────────────
# HELPER: save matplotlib fig → BytesIO
# ─────────────────────────────────────────────────────────────────────────────
def fig_to_stream(fig):
    buf = io.BytesIO()
    fig.savefig(buf, format="png", bbox_inches="tight", dpi=130)
    buf.seek(0)
    plt.close(fig)
    return buf

# ─────────────────────────────────────────────────────────────────────────────
# HELPER: add slide with gradient-ish header band
# ─────────────────────────────────────────────────────────────────────────────
def add_slide(prs, title_text, subtitle_text=""):
    slide_layout = prs.slide_layouts[6]          # blank
    slide = prs.slides.add_slide(slide_layout)
    sw, sh = prs.slide_width, prs.slide_height

    # Background
    bg = slide.shapes.add_shape(1, 0, 0, sw, sh)
    bg.fill.solid(); bg.fill.fore_color.rgb = LGRAY
    bg.line.fill.background()

    # Header band
    hband = slide.shapes.add_shape(1, 0, 0, sw, Inches(1.35))
    hband.fill.solid(); hband.fill.fore_color.rgb = NAVY
    hband.line.fill.background()

    # Accent stripe
    stripe = slide.shapes.add_shape(1, 0, Inches(1.35), sw, Inches(0.08))
    stripe.fill.solid(); stripe.fill.fore_color.rgb = GOLD
    stripe.line.fill.background()

    # Title
    tx = slide.shapes.add_textbox(Inches(0.35), Inches(0.12), Inches(12.3), Inches(1.0))
    tf = tx.text_frame; tf.word_wrap = True
    p = tf.paragraphs[0]
    p.text = title_text
    p.font.bold = True; p.font.size = Pt(32); p.font.color.rgb = WHITE

    # Subtitle
    if subtitle_text:
        tx2 = slide.shapes.add_textbox(Inches(0.35), Inches(0.95), Inches(12.3), Inches(0.42))
        tf2 = tx2.text_frame
        p2 = tf2.paragraphs[0]
        p2.text = subtitle_text
        p2.font.size = Pt(16); p2.font.color.rgb = GOLD; p2.font.bold = True

    return slide

# ─────────────────────────────────────────────────────────────────────────────
# HELPER: add bullet text box
# ─────────────────────────────────────────────────────────────────────────────
def add_bullets(slide, bullets, left, top, width, height, font_size=17):
    tx = slide.shapes.add_textbox(Inches(left), Inches(top), Inches(width), Inches(height))
    tf = tx.text_frame; tf.word_wrap = True
    for i, b in enumerate(bullets):
        p = tf.paragraphs[0] if i == 0 else tf.add_paragraph()
        indent = b.startswith("   ")
        p.text = b.strip()
        p.font.size = Pt(font_size - (2 if indent else 0))
        p.font.color.rgb = DKGRAY
        p.space_after = Pt(4)
        if not indent:
            p.font.bold = False
        p.level = 1 if indent else 0

# ─────────────────────────────────────────────────────────────────────────────
# HELPER: insert image stream on slide
# ─────────────────────────────────────────────────────────────────────────────
def insert_image(slide, stream, left, top, width, height):
    slide.shapes.add_picture(stream, Inches(left), Inches(top),
                             Inches(width), Inches(height))

# ─────────────────────────────────────────────────────────────────────────────
# IMAGE GENERATORS
# ─────────────────────────────────────────────────────────────────────────────

def img_definition_timeline():
    fig, ax = plt.subplots(figsize=(7, 4))
    ax.set_xlim(0, 10); ax.set_ylim(0, 5); ax.axis("off")
    ax.set_facecolor("#f0f4f8"); fig.patch.set_facecolor("#f0f4f8")

    # Timeline bar
    ax.add_patch(FancyBboxPatch((0.5, 2.2), 9, 0.6, boxstyle="round,pad=0.05",
                                fc="#1A3A5C", ec="none"))
    events = [
        (1.0,  "Onset of\nTrue Labour"),
        (3.5,  "Full Cervical\nDilation (10 cm)"),
        (6.5,  "Delivery of\nBaby"),
        (9.0,  "Delivery of\nPlacenta"),
    ]
    colors = ["#F5A623", "#00AAB5", "#27AE60", "#C0392B"]
    for x, label, col in zip([e[0] for e in events], [e[1] for e in events], colors):
        ax.add_patch(Circle((x, 2.5), 0.18, fc=col, ec="white", zorder=5, lw=2))
        ax.text(x, 1.6, label, ha="center", va="top", fontsize=9, color="#1A3A5C",
                fontweight="bold", linespacing=1.4)

    # Stage labels
    stage_data = [(2.25, "Stage I", "#1A3A5C"), (5.0, "Stage II", "#007A87"),
                  (7.75, "Stage III", "#27AE60")]
    for xm, lbl, col in stage_data:
        ax.text(xm, 3.2, lbl, ha="center", va="bottom", fontsize=10,
                color=col, fontweight="bold")

    ax.text(5, 4.6, "Normal Labour — Definition & Stages", ha="center", va="top",
            fontsize=13, fontweight="bold", color="#1A3A5C")
    return fig_to_stream(fig)


def img_true_vs_false():
    fig, axes = plt.subplots(1, 2, figsize=(8, 4))
    fig.patch.set_facecolor("#f0f4f8")

    data = {
        "True Labour": {
            "color": "#27AE60",
            "items": [
                "Regular, rhythmic contractions",
                "Increasing frequency & intensity",
                "Pain radiates lumbosacral → abdomen",
                "Cervix dilates & effaces",
                "Show (bloody mucus) present",
                "Walking increases contractions",
                "Analgesics do NOT stop labour",
            ]
        },
        "False Labour": {
            "color": "#C0392B",
            "items": [
                "Irregular contractions (Braxton-Hicks)",
                "No progressive increase",
                "Pain mainly in lower abdomen",
                "No cervical change",
                "Show usually absent",
                "Walking may relieve pain",
                "Analgesics may stop contractions",
            ]
        }
    }

    for ax, (title, info) in zip(axes, data.items()):
        ax.set_xlim(0, 1); ax.set_ylim(0, 1); ax.axis("off")
        ax.set_facecolor("#f0f4f8")
        ax.add_patch(FancyBboxPatch((0.02, 0.02), 0.96, 0.96,
                                    boxstyle="round,pad=0.03",
                                    fc="white", ec=info["color"], lw=2.5))
        ax.text(0.5, 0.92, title, ha="center", va="top", fontsize=12,
                fontweight="bold", color=info["color"])
        for i, item in enumerate(info["items"]):
            ax.text(0.08, 0.80 - i*0.11, f"• {item}", ha="left", va="top",
                    fontsize=8, color="#333333", wrap=True)

    fig.suptitle("True vs False Labour", fontsize=14, fontweight="bold",
                 color="#1A3A5C", y=1.01)
    plt.tight_layout()
    return fig_to_stream(fig)


def img_uterine_contractions():
    fig, ax = plt.subplots(figsize=(7, 3.5))
    fig.patch.set_facecolor("#f0f4f8"); ax.set_facecolor("#f0f4f8")
    t = np.linspace(0, 10, 1000)
    # Simulate 3 contractions
    y = np.zeros_like(t)
    for peak in [1.5, 4.0, 7.0]:
        y += 70 * np.exp(-((t - peak)**2) / 0.3)
    y += 10   # baseline tone
    y = np.clip(y, 0, 100)

    ax.fill_between(t, 10, y, where=(y > 10), alpha=0.35, color="#007A87")
    ax.plot(t, y, color="#1A3A5C", lw=2.5)
    ax.axhline(10, color="#C0392B", lw=1.2, ls="--", label="Baseline tone (10 mmHg)")
    ax.axhline(25, color="#F5A623", lw=1.2, ls=":", label="Threshold (25 mmHg)")

    ax.set_xlabel("Time (minutes)", fontsize=10, color="#333")
    ax.set_ylabel("Intrauterine Pressure (mmHg)", fontsize=10, color="#333")
    ax.set_title("Uterine Contractions in Labour", fontsize=12,
                 fontweight="bold", color="#1A3A5C")
    ax.set_ylim(0, 110)
    ax.legend(fontsize=8, loc="upper right")

    # Annotate peaks
    for pk, lbl in [(1.5, "1st"), (4.0, "2nd"), (7.0, "3rd")]:
        ax.annotate(f"{lbl}\nContraction\n~60-80 mmHg",
                    xy=(pk, 82), xytext=(pk + 0.3, 100),
                    arrowprops=dict(arrowstyle="->", color="#1A3A5C"),
                    fontsize=7.5, color="#1A3A5C")

    plt.tight_layout()
    return fig_to_stream(fig)


def img_cervical_dilation():
    fig, axes = plt.subplots(1, 3, figsize=(8, 3.5))
    fig.patch.set_facecolor("#f0f4f8")
    stages = [
        ("Early Labour\n3-4 cm", 3.5, 0.3, "#F5A623"),
        ("Active Labour\n6-7 cm", 6.5, 0.6, "#007A87"),
        ("Full Dilation\n10 cm", 10, 1.0, "#27AE60"),
    ]
    for ax, (label, diam, eff, col) in zip(axes, stages):
        ax.set_xlim(-6, 6); ax.set_ylim(-6, 6); ax.set_aspect("equal")
        ax.axis("off"); ax.set_facecolor("#f0f4f8")
        # Cervix ring
        outer = Circle((0, 0), 5, fc="#ffcccc", ec="#C0392B", lw=2)
        inner = Circle((0, 0), diam/2, fc="white", ec=col, lw=2.5)
        ax.add_patch(outer); ax.add_patch(inner)
        # Effacement arc
        arc = Arc((0, 0), 10.5, 10.5, angle=0, theta1=0,
                  theta2=360 * eff, color=col, lw=3)
        ax.add_patch(arc)
        ax.text(0, -5.6, label, ha="center", va="top", fontsize=9,
                fontweight="bold", color=col)
        ax.text(0, 0, f"{diam}\ncm", ha="center", va="center",
                fontsize=11, fontweight="bold", color=col)

    fig.suptitle("Cervical Dilation & Effacement", fontsize=12,
                 fontweight="bold", color="#1A3A5C")
    plt.tight_layout()
    return fig_to_stream(fig)


def img_cardinal_movements():
    fig, axes = plt.subplots(2, 4, figsize=(11, 5))
    fig.patch.set_facecolor("#f0f4f8")
    movements = [
        ("1. Engagement", "#1A3A5C",
         "Head enters pelvis\nbiparietal diameter\npasses pelvic inlet"),
        ("2. Descent", "#007A87",
         "Progressive\ndownward movement\nthroughout labour"),
        ("3. Flexion", "#27AE60",
         "Chin meets chest\nSmallest diameter\npresents"),
        ("4. Internal\nRotation", "#F5A623",
         "Occiput rotates\nanteriorly to\npubic symphysis"),
        ("5. Extension", "#C0392B",
         "Head extends as\nit passes under\npubic arch"),
        ("6. Restitution", "#8E44AD",
         "Head returns to\noblique position\n(undoes rotation)"),
        ("7. External\nRotation", "#2980B9",
         "Shoulders align\nwith AP diameter\nof outlet"),
        ("8. Expulsion", "#16A085",
         "Anterior then\nposterior shoulder\ndelivered"),
    ]

    for ax, (title, col, desc) in zip(axes.flat, movements):
        ax.set_xlim(0, 1); ax.set_ylim(0, 1); ax.axis("off")
        ax.set_facecolor("white")
        ax.add_patch(FancyBboxPatch((0.02, 0.02), 0.96, 0.96,
                                    boxstyle="round,pad=0.04",
                                    fc="white", ec=col, lw=2))
        # Icon circle
        ax.add_patch(Circle((0.5, 0.72), 0.18, fc=col, ec="none"))
        ax.text(0.5, 0.72, str(movements.index((title, col, desc)) + 1),
                ha="center", va="center", fontsize=14, fontweight="bold", color="white")
        ax.text(0.5, 0.50, title, ha="center", va="center", fontsize=8.5,
                fontweight="bold", color=col, linespacing=1.3)
        ax.text(0.5, 0.22, desc, ha="center", va="center", fontsize=7,
                color="#444", linespacing=1.4)

    fig.suptitle("8 Cardinal Movements of Labour", fontsize=13,
                 fontweight="bold", color="#1A3A5C", y=1.01)
    plt.tight_layout(pad=0.3)
    return fig_to_stream(fig)


def img_fetal_skull():
    fig, axes = plt.subplots(1, 2, figsize=(9, 4))
    fig.patch.set_facecolor("#f0f4f8")

    # Left: Skull bones
    ax = axes[0]
    ax.set_xlim(0, 10); ax.set_ylim(0, 8); ax.axis("off")
    ax.set_facecolor("#f0f4f8")
    # Simple oval for skull
    skull = mpatches.Ellipse((5, 4), 7, 5.5, fc="#FFE0B2", ec="#8B4513", lw=2)
    ax.add_patch(skull)
    # Sutures & fontanelles
    ax.plot([5, 5], [1.2, 6.8], color="#8B4513", lw=1.5, ls="--")  # sagittal
    ax.plot([1.8, 8.2], [4, 4], color="#8B4513", lw=1.5, ls="--")  # coronal approx
    ax.plot([2.2, 7.8], [5.5, 5.5], color="#8B4513", lw=1, ls=":")  # lambdoid approx

    labels = [
        (5, 6.8, "Anterior\nFontanelle\n(Bregma)", "green"),
        (5, 1.5, "Posterior\nFontanelle\n(Lambda)", "blue"),
        (5, 4.2, "Sagittal\nSuture", "#8B4513"),
        (2.5, 4.0, "Coronal\nSuture", "purple"),
    ]
    for x, y, lbl, col in labels:
        ax.text(x, y, lbl, ha="center", va="center", fontsize=7,
                color=col, fontweight="bold",
                bbox=dict(fc="white", ec=col, boxstyle="round,pad=0.2", alpha=0.8))

    ax.set_title("Fetal Skull — Landmarks", fontsize=10, fontweight="bold", color="#1A3A5C")

    # Right: Diameters table
    ax2 = axes[1]
    ax2.set_xlim(0, 1); ax2.set_ylim(0, 1); ax2.axis("off")
    ax2.set_facecolor("#f0f4f8")

    headers = ["Diameter", "Measurement", "Presentation"]
    rows = [
        ["Suboccipito-\nbregmatic", "9.5 cm", "Vertex (fully\nflexed)"],
        ["Suboccipito-\nfrontal", "10 cm", "Vertex (partially\nflexed)"],
        ["Occipito-\nfrontal", "11.5 cm", "Deflexed vertex"],
        ["Mento-\nvertical", "13.5 cm", "Brow"],
        ["Submento-\nbregmatic", "9.5 cm", "Face"],
        ["Biparietal", "9.5 cm", "Transverse"],
    ]
    row_h = 0.12; col_w = [0.35, 0.25, 0.40]
    xs = [0.02, 0.37, 0.62]

    for j, (h, cw) in enumerate(zip(headers, col_w)):
        ax2.add_patch(FancyBboxPatch((xs[j], 0.88), cw - 0.02, row_h - 0.01,
                                     boxstyle="round,pad=0.01", fc="#1A3A5C", ec="none"))
        ax2.text(xs[j] + (cw-0.02)/2, 0.88 + (row_h-0.01)/2,
                 h, ha="center", va="center", fontsize=8,
                 fontweight="bold", color="white")

    for i, row in enumerate(rows):
        bg_col = "white" if i % 2 == 0 else "#EBF5FB"
        y = 0.88 - (i + 1) * row_h
        for j, (cell, cw) in enumerate(zip(row, col_w)):
            ax2.add_patch(FancyBboxPatch((xs[j], y), cw - 0.02, row_h - 0.01,
                                         boxstyle="round,pad=0.01", fc=bg_col, ec="#ddd"))
            ax2.text(xs[j] + (cw-0.02)/2, y + (row_h-0.01)/2,
                     cell, ha="center", va="center", fontsize=7, color="#333",
                     linespacing=1.2)

    ax2.set_title("Fetal Head Diameters", fontsize=10, fontweight="bold", color="#1A3A5C")
    plt.tight_layout()
    return fig_to_stream(fig)


def img_pelvis_types():
    fig, axes = plt.subplots(1, 4, figsize=(10, 3.5))
    fig.patch.set_facecolor("#f0f4f8")
    types = [
        ("Gynaecoid", "#27AE60", (1.0, 1.0), "50%\nMost favourable"),
        ("Android",   "#C0392B", (0.8, 1.0), "20%\nUnfavourable"),
        ("Anthropoid","#F5A623", (0.7, 1.3), "25%\nEngagement\nin AP"),
        ("Platypelloid","#007A87",(1.3, 0.6), "5%\nTransverse\nengagement"),
    ]
    for ax, (name, col, (rx, ry), note) in zip(axes, types):
        ax.set_xlim(-2, 2); ax.set_ylim(-2, 2.5); ax.set_aspect("equal"); ax.axis("off")
        ax.set_facecolor("#f0f4f8")
        ellipse = mpatches.Ellipse((0, 0), 2*rx, 2*ry,
                                   fc=col + "40" if len(col) == 7 else col,
                                   ec=col, lw=2.5, alpha=0.6)
        # Use patch with alpha
        ell = mpatches.Ellipse((0, 0), 2*rx, 2*ry, fc=col, ec="white",
                                lw=2, alpha=0.35)
        ell2 = mpatches.Ellipse((0, 0), 2*rx, 2*ry, fc="none", ec=col, lw=2.5)
        ax.add_patch(ell); ax.add_patch(ell2)

        ax.text(0, ry + 0.4, name, ha="center", va="bottom", fontsize=10,
                fontweight="bold", color=col)
        ax.text(0, -ry - 0.35, note, ha="center", va="top", fontsize=8,
                color="#444", linespacing=1.3)
        # Dimensions
        ax.annotate("", xy=(rx, 0), xytext=(-rx, 0),
                    arrowprops=dict(arrowstyle="<->", color=col, lw=1.5))
        ax.annotate("", xy=(0, ry), xytext=(0, -ry),
                    arrowprops=dict(arrowstyle="<->", color=col, lw=1.5))

    fig.suptitle("Caldwell-Moloy Classification of Pelvis Types",
                 fontsize=12, fontweight="bold", color="#1A3A5C")
    plt.tight_layout()
    return fig_to_stream(fig)


def img_stages_of_labour():
    fig, ax = plt.subplots(figsize=(9, 4.5))
    fig.patch.set_facecolor("#f0f4f8"); ax.set_facecolor("#f0f4f8")

    stages = [
        ("STAGE I\nOnset → Full Dilation", "#1A3A5C", 0, 3.8,
         ["Latent phase: 0-3 cm (slow)", "Active phase: 4-10 cm (fast)", "Duration: 12h primip / 6h multip"]),
        ("STAGE II\nFull Dilation → Delivery of Baby", "#007A87", 3.8, 2.2,
         ["Pushing + expulsion", "Duration: 1h primip / 30 min multip", "Cardinal movements occur"]),
        ("STAGE III\nDelivery → Placenta Out", "#27AE60", 6.0, 1.8,
         ["Placental separation & expulsion", "Duration: 15-30 min", "Active management (Oxytocin)"]),
        ("STAGE IV\nRecovery (1-2 hrs postpartum)", "#F5A623", 7.8, 2.2,
         ["Monitor vitals & uterine tone", "PPH prevention", "Bonding & breastfeeding"]),
    ]

    y_top = 3.8
    for label, col, x_start, width, notes in stages:
        ax.add_patch(FancyBboxPatch((x_start + 0.05, 0.1), width - 0.1, y_top - 0.2,
                                    boxstyle="round,pad=0.08", fc=col, ec="white", lw=2, alpha=0.9))
        ax.text(x_start + width/2, y_top - 0.45, label, ha="center", va="top",
                fontsize=8.5, fontweight="bold", color="white", linespacing=1.4)
        for i, note in enumerate(notes):
            ax.text(x_start + width/2, y_top - 1.3 - i*0.62, f"• {note}",
                    ha="center", va="top", fontsize=7, color="white",
                    linespacing=1.3, wrap=True)

    ax.set_xlim(0, 10); ax.set_ylim(0, 4.5); ax.axis("off")
    ax.text(5, 4.35, "Four Stages of Labour", ha="center", va="top",
            fontsize=13, fontweight="bold", color="#1A3A5C")
    plt.tight_layout()
    return fig_to_stream(fig)


def img_partograph():
    fig, axes = plt.subplots(2, 1, figsize=(9, 5.5), gridspec_kw={"height_ratios": [3, 1]})
    fig.patch.set_facecolor("#f0f4f8")

    ax = axes[0]
    ax.set_facecolor("#fffef5")
    # Alert and action lines
    hours = np.array([0, 1, 2, 3, 4])
    alert = np.array([4, 5, 6, 7, 8])
    action = alert + 2

    ax.plot(hours, alert, "b-o", lw=2, ms=6, label="Alert Line (1 cm/hr)")
    ax.plot(hours, action, "r-s", lw=2, ms=6, label="Action Line")

    # Simulated progress
    progress = np.array([4, 5.5, 7, 9, 10])
    ax.plot(hours, progress, "g-D", lw=2.5, ms=7, label="Cervical Dilation (Patient)")
    ax.fill_between(hours, alert, action, alpha=0.12, color="orange", label="Action Zone")

    ax.set_xlim(-0.2, 4.5); ax.set_ylim(3, 11)
    ax.set_xlabel("Hours in Active Labour", fontsize=10); ax.set_ylabel("Cervical Dilation (cm)", fontsize=10)
    ax.set_title("Partograph — Cervicograph", fontsize=11, fontweight="bold", color="#1A3A5C")
    ax.legend(fontsize=8, loc="upper left")
    ax.grid(True, alpha=0.3)
    ax.set_yticks(range(4, 11))
    ax.set_xticks(range(0, 5))

    # Fetal HR
    ax2 = axes[1]
    ax2.set_facecolor("#f5f5ff")
    fhr_t = np.linspace(0, 4, 80)
    fhr = 140 + 10 * np.sin(fhr_t * 3) + np.random.normal(0, 3, 80)
    ax2.plot(fhr_t, fhr, color="#2980B9", lw=1.5)
    ax2.axhline(110, color="#C0392B", ls="--", lw=1, label="Min normal (110)")
    ax2.axhline(160, color="#C0392B", ls="--", lw=1, label="Max normal (160)")
    ax2.fill_between(fhr_t, 110, 160, alpha=0.1, color="#27AE60")
    ax2.set_xlim(-0.2, 4.5); ax2.set_ylim(90, 185)
    ax2.set_xlabel("Hours", fontsize=9); ax2.set_ylabel("FHR (bpm)", fontsize=9)
    ax2.legend(fontsize=7, loc="upper right"); ax2.grid(True, alpha=0.3)
    ax2.set_title("Fetal Heart Rate Monitoring", fontsize=9, color="#1A3A5C")

    plt.tight_layout()
    return fig_to_stream(fig)


def img_mechanism_occiput_anterior():
    fig, axes = plt.subplots(1, 5, figsize=(11, 3.5))
    fig.patch.set_facecolor("#f0f4f8")
    steps = [
        ("Engagement &\nDescend", "#1A3A5C",
         [(0.5, 0.3), (0.5, 0.7)], "↓"),
        ("Flexion", "#007A87",
         [(0.5, 0.3), (0.5, 0.7)], "↓ + flex"),
        ("Internal\nRotation", "#F5A623",
         [(0.5, 0.3), (0.5, 0.7)], "↻ OA"),
        ("Extension", "#27AE60",
         [(0.5, 0.3), (0.5, 0.7)], "↑ extend"),
        ("External\nRotation &\nExpulsion", "#C0392B",
         [(0.5, 0.3), (0.5, 0.7)], "↻ out"),
    ]

    for ax, (title, col, pts, arrow_lbl) in zip(axes, steps):
        ax.set_xlim(0, 1); ax.set_ylim(0, 1); ax.axis("off")
        ax.set_facecolor("#f0f4f8")
        # Box
        ax.add_patch(FancyBboxPatch((0.05, 0.05), 0.90, 0.90,
                                    boxstyle="round,pad=0.04",
                                    fc="white", ec=col, lw=2))
        # Pelvis arch
        ax.add_patch(Arc((0.5, 0.0), 0.75, 0.65, angle=0,
                         theta1=20, theta2=160, color="#8B4513", lw=3))
        # Head
        head = Circle((0.5, 0.42), 0.14, fc=col, ec="white", lw=1.5, alpha=0.85)
        ax.add_patch(head)
        ax.text(0.5, 0.42, arrow_lbl, ha="center", va="center",
                fontsize=10, fontweight="bold", color="white")
        ax.text(0.5, 0.88, title, ha="center", va="top", fontsize=8,
                fontweight="bold", color=col, linespacing=1.3)

    fig.suptitle("Mechanism of Labour — Left Occipito-Anterior (LOA)",
                 fontsize=12, fontweight="bold", color="#1A3A5C", y=1.02)
    plt.tight_layout(pad=0.2)
    return fig_to_stream(fig)


def img_management_summary():
    fig, ax = plt.subplots(figsize=(9, 5))
    fig.patch.set_facecolor("#f0f4f8"); ax.set_facecolor("#f0f4f8"); ax.axis("off")

    phases = [
        ("ADMISSION", "#1A3A5C",
         ["• History & physical exam", "• Confirm true labour", "• Assess cervical dilation",
          "• FHR auscultation", "• IV access if needed"]),
        ("STAGE I\nMONITORING", "#007A87",
         ["• FHR every 30 min (latent)\n  every 15 min (active)", "• Contractions frequency/strength",
          "• Vaginal exam every 4 hrs", "• Partograph maintenance",
          "• Maternal vitals hourly"]),
        ("STAGE II\nDELIVERY", "#27AE60",
         ["• Active pushing guidance", "• Perineal support", "• Control head delivery",
          "• Oxytocin 10 IU IM on\n  anterior shoulder",
          "• APGAR at 1 & 5 min"]),
        ("STAGE III &\nIV", "#F5A623",
         ["• Active management", "• Controlled cord traction", "• Uterine massage",
          "• Inspect placenta/membranes", "• Perineal repair if needed"]),
    ]

    x_positions = [0.05, 0.30, 0.55, 0.78]
    for i, ((phase, col, items), x) in enumerate(zip(phases, x_positions)):
        ax.add_patch(FancyBboxPatch((x, 0.08), 0.22, 0.88,
                                    boxstyle="round,pad=0.02",
                                    fc=col, ec="white", lw=2, alpha=0.9))
        ax.text(x + 0.11, 0.93, phase, ha="center", va="top", fontsize=9,
                fontweight="bold", color="white", linespacing=1.3)
        for j, item in enumerate(items):
            ax.text(x + 0.11, 0.82 - j * 0.145, item,
                    ha="center", va="top", fontsize=6.8, color="white", linespacing=1.3)

    ax.text(0.5, 0.99, "Management of Normal Labour", ha="center", va="top",
            fontsize=13, fontweight="bold", color="#1A3A5C",
            transform=ax.transAxes)
    plt.tight_layout()
    return fig_to_stream(fig)


def img_oxytocin_uterotonic():
    fig, axes = plt.subplots(1, 2, figsize=(9, 4))
    fig.patch.set_facecolor("#f0f4f8")

    # Left: Uterotonic comparison
    ax = axes[0]
    ax.set_facecolor("#f0f4f8"); ax.axis("off")
    ax.set_xlim(0, 1); ax.set_ylim(0, 1)
    drugs = [
        ("Oxytocin", "10 IU IM / IV infusion", "1st line", "#27AE60"),
        ("Misoprostol", "600 mcg oral / sublingual", "If oxytocin unavailable", "#F5A623"),
        ("Ergometrine", "0.5 mg IM", "Avoid in hypertension", "#C0392B"),
        ("Carboprost", "250 mcg IM q15min", "Refractory PPH", "#007A87"),
    ]
    ax.text(0.5, 0.97, "Uterotonic Agents (Stage III)", ha="center", va="top",
            fontsize=10, fontweight="bold", color="#1A3A5C")
    for i, (drug, dose, note, col) in enumerate(drugs):
        y = 0.80 - i * 0.20
        ax.add_patch(FancyBboxPatch((0.03, y - 0.06), 0.94, 0.17,
                                    boxstyle="round,pad=0.02",
                                    fc=col, ec="white", lw=1.5, alpha=0.85))
        ax.text(0.07, y + 0.03, drug, ha="left", va="center",
                fontsize=9, fontweight="bold", color="white")
        ax.text(0.07, y - 0.03, f"{dose}  |  {note}", ha="left", va="center",
                fontsize=7.5, color="white")

    # Right: PPH risk factors
    ax2 = axes[1]
    ax2.set_facecolor("#f0f4f8"); ax2.axis("off")
    ax2.set_xlim(0, 1); ax2.set_ylim(0, 1)
    ax2.text(0.5, 0.97, "PPH Risk Factors (4 T's)", ha="center", va="top",
             fontsize=10, fontweight="bold", color="#1A3A5C")
    four_t = [
        ("TONE", "#C0392B", "Uterine atony (80%)", ["Grand multiparity", "Macrosomia", "Prolonged labour"]),
        ("TRAUMA", "#E67E22", "Lacerations/rupture", ["Instrumental delivery", "Precipitate labour"]),
        ("TISSUE", "#F5A623", "Retained products", ["Incomplete placenta", "Accessory lobe"]),
        ("THROMBIN", "#007A87", "Coagulopathy", ["DIC", "Anticoagulation", "Liver disease"]),
    ]
    for i, (t, col, sub, items) in enumerate(four_t):
        y = 0.78 - i * 0.195
        ax2.add_patch(Circle((0.1, y), 0.065, fc=col, ec="none", alpha=0.9))
        ax2.text(0.10, y, t, ha="center", va="center", fontsize=7.5,
                 fontweight="bold", color="white")
        ax2.text(0.20, y + 0.03, sub, ha="left", va="center",
                 fontsize=8.5, fontweight="bold", color=col)
        ax2.text(0.20, y - 0.04, " | ".join(items), ha="left", va="center",
                 fontsize=7, color="#444")

    plt.tight_layout()
    return fig_to_stream(fig)


def img_episiotomy():
    fig, axes = plt.subplots(1, 2, figsize=(9, 4))
    fig.patch.set_facecolor("#f0f4f8")

    ax = axes[0]
    ax.set_xlim(-1, 6); ax.set_ylim(-1, 5); ax.axis("off"); ax.set_facecolor("#f0f4f8")
    ax.set_title("Types of Episiotomy", fontsize=11, fontweight="bold", color="#1A3A5C")
    # Perineum schematic
    ax.add_patch(mpatches.Ellipse((2.5, 2.5), 4, 3, fc="#FFDAB9", ec="#8B4513", lw=2, alpha=0.6))
    # Introitus
    ax.add_patch(mpatches.Ellipse((2.5, 2.5), 1.2, 0.8, fc="#CC8866", ec="#8B4513", lw=1.5))
    ax.text(2.5, 2.5, "Introitus", ha="center", va="center", fontsize=7, color="white")
    # Midline incision
    ax.plot([2.5, 2.5], [1.9, 0.3], color="#C0392B", lw=3, label="Midline")
    # Mediolateral incision
    ax.plot([2.5, 4.1], [1.9, 0.5], color="#007A87", lw=3, ls="--", label="Mediolateral")
    ax.legend(loc="lower right", fontsize=8)

    ax2 = axes[1]
    ax2.set_xlim(0, 1); ax2.set_ylim(0, 1); ax2.axis("off"); ax2.set_facecolor("#f0f4f8")
    ax2.set_title("Comparison", fontsize=11, fontweight="bold", color="#1A3A5C")
    rows = [
        ("Feature", "Midline", "Mediolateral"),
        ("Blood loss", "Less", "More"),
        ("3rd/4th deg\nextension", "Common", "Rare"),
        ("Healing", "Better", "Slower"),
        ("Dyspareunia", "Rare", "More common"),
        ("Indication", "Primipara", "Shoulder\ndystocia etc"),
    ]
    hdrs = [0.02, 0.35, 0.68]
    for i, row in enumerate(rows):
        bg = "#1A3A5C" if i == 0 else ("white" if i % 2 == 1 else "#EBF5FB")
        tc = "white" if i == 0 else "#333"
        y = 0.93 - i * 0.14
        for j, (cell, cw) in enumerate(zip(row, [0.30, 0.30, 0.30])):
            ax2.add_patch(FancyBboxPatch((hdrs[j], y - 0.11), cw - 0.01, 0.12,
                                         boxstyle="round,pad=0.01", fc=bg, ec="#ddd"))
            ax2.text(hdrs[j] + (cw-0.01)/2, y - 0.055, cell,
                     ha="center", va="center", fontsize=7.5, color=tc,
                     fontweight="bold" if i == 0 else "normal", linespacing=1.2)

    plt.tight_layout()
    return fig_to_stream(fig)


def img_complications():
    fig, ax = plt.subplots(figsize=(9, 4.5))
    fig.patch.set_facecolor("#f0f4f8"); ax.axis("off"); ax.set_facecolor("#f0f4f8")

    comp_data = {
        "Maternal Complications": {
            "color": "#C0392B",
            "items": ["Prolonged labour (>18h primip, >12h multip)",
                      "Obstructed labour", "Uterine rupture",
                      "PPH (atony, trauma, retained placenta)",
                      "Perineal tears (1st–4th degree)",
                      "Infection / chorioamnionitis",
                      "Amniotic fluid embolism"],
        },
        "Fetal Complications": {
            "color": "#1A3A5C",
            "items": ["Birth asphyxia / HIE",
                      "Meconium aspiration syndrome",
                      "Birth injuries (fracture, nerve palsy)",
                      "Cord compression / prolapse",
                      "Shoulder dystocia",
                      "Intracranial haemorrhage",
                      "Stillbirth"],
        }
    }

    ax.text(0.5, 0.97, "Complications of Labour", ha="center", va="top",
            fontsize=14, fontweight="bold", color="#1A3A5C",
            transform=ax.transAxes)

    for i, (title, info) in enumerate(comp_data.items()):
        x = 0.02 + i * 0.50
        ax.add_patch(FancyBboxPatch((x, 0.04), 0.46, 0.86,
                                    boxstyle="round,pad=0.02",
                                    fc=info["color"], ec="white", lw=2, alpha=0.9,
                                    transform=ax.transAxes))
        ax.text(x + 0.23, 0.88, title, ha="center", va="top", fontsize=10,
                fontweight="bold", color="white", transform=ax.transAxes)
        for j, item in enumerate(info["items"]):
            ax.text(x + 0.04, 0.78 - j * 0.115, f"• {item}",
                    ha="left", va="top", fontsize=8, color="white",
                    transform=ax.transAxes)

    plt.tight_layout()
    return fig_to_stream(fig)


def img_newborn_apgar():
    fig, ax = plt.subplots(figsize=(9, 4))
    fig.patch.set_facecolor("#f0f4f8"); ax.set_facecolor("#f0f4f8"); ax.axis("off")

    headers = ["Sign", "0", "1", "2"]
    rows = [
        ["Appearance\n(Color)", "Blue/pale\nall over", "Blue\nextremities", "Pink\nall over"],
        ["Pulse\n(Heart Rate)", "Absent", "< 100\nbpm", "≥ 100\nbpm"],
        ["Grimace\n(Reflex)", "No\nresponse", "Weak\ncry/grimace", "Cough,\ncry, sneeze"],
        ["Activity\n(Muscle Tone)", "Limp", "Some\nflexion", "Active\nmotion"],
        ["Respiration", "Absent", "Slow,\nirregular", "Good,\ncrying"],
    ]
    score_cols = ["#C0392B", "#F5A623", "#27AE60"]
    col_widths = [0.20, 0.22, 0.22, 0.22]
    xs = [0.02, 0.22, 0.44, 0.66]

    # Title
    ax.text(0.5, 0.97, "APGAR Score", ha="center", va="top",
            fontsize=14, fontweight="bold", color="#1A3A5C",
            transform=ax.transAxes)

    for j, (h, x, cw) in enumerate(zip(headers, xs, col_widths)):
        col = "#1A3A5C" if j == 0 else score_cols[j-1]
        ax.add_patch(FancyBboxPatch((x, 0.80), cw - 0.01, 0.13,
                                    boxstyle="round,pad=0.01", fc=col, ec="none",
                                    transform=ax.transAxes))
        ax.text(x + (cw-0.01)/2, 0.865, h, ha="center", va="center",
                fontsize=10, fontweight="bold", color="white",
                transform=ax.transAxes)

    for i, row in enumerate(rows):
        y = 0.78 - i * 0.155
        bg = "white" if i % 2 == 0 else "#EBF5FB"
        for j, (cell, x, cw) in enumerate(zip(row, xs, col_widths)):
            ax.add_patch(FancyBboxPatch((x, y - 0.13), cw - 0.01, 0.14,
                                        boxstyle="round,pad=0.01",
                                        fc=bg, ec="#ddd", transform=ax.transAxes))
            ax.text(x + (cw-0.01)/2, y - 0.06, cell, ha="center", va="center",
                    fontsize=8, color="#333", linespacing=1.3,
                    transform=ax.transAxes)

    # Score interpretation
    interp = [(0.02, "7-10: Normal"), (0.35, "4-6: Moderate depression"), (0.65, "0-3: Severe depression")]
    for x, lbl in interp:
        col = "#27AE60" if "Normal" in lbl else ("#F5A623" if "Moderate" in lbl else "#C0392B")
        ax.add_patch(FancyBboxPatch((x, 0.01), 0.30, 0.07,
                                    boxstyle="round,pad=0.01", fc=col, ec="none",
                                    alpha=0.85, transform=ax.transAxes))
        ax.text(x + 0.15, 0.045, lbl, ha="center", va="center",
                fontsize=8, fontweight="bold", color="white",
                transform=ax.transAxes)

    plt.tight_layout()
    return fig_to_stream(fig)


def img_active_management():
    fig, ax = plt.subplots(figsize=(8, 4))
    fig.patch.set_facecolor("#f0f4f8"); ax.set_facecolor("#f0f4f8"); ax.axis("off")

    # Title
    ax.text(0.5, 0.97, "Active Management of Third Stage (AMTSL)", ha="center",
            va="top", fontsize=12, fontweight="bold", color="#1A3A5C",
            transform=ax.transAxes)

    steps = [
        ("1", "Uterotonic\nAdministration", "Oxytocin 10 IU IM\nWithin 1 min of delivery\nof anterior shoulder", "#27AE60"),
        ("2", "Controlled Cord\nTraction (CCT)", "Brandt-Andrews manoeuvre\nCounter-pressure on uterus\nUmbilical cord traction", "#007A87"),
        ("3", "Uterine\nMassage", "Fundal massage\nafter placenta delivered\n30-60 sec circular motion", "#1A3A5C"),
    ]

    for i, (num, title, desc, col) in enumerate(steps):
        x = 0.05 + i * 0.32
        # Arrow between steps
        if i > 0:
            ax.annotate("", xy=(x - 0.02, 0.5),
                        xytext=(x - 0.07, 0.5),
                        arrowprops=dict(arrowstyle="-|>", color=col, lw=2.5),
                        xycoords="axes fraction", textcoords="axes fraction")

        ax.add_patch(FancyBboxPatch((x, 0.10), 0.28, 0.78,
                                    boxstyle="round,pad=0.03",
                                    fc=col, ec="white", lw=2, alpha=0.9,
                                    transform=ax.transAxes))
        ax.add_patch(Circle((x + 0.14, 0.80), 0.065, fc="white", ec=col, lw=0,
                             transform=ax.transAxes))
        ax.text(x + 0.14, 0.80, num, ha="center", va="center",
                fontsize=14, fontweight="bold", color=col,
                transform=ax.transAxes)
        ax.text(x + 0.14, 0.67, title, ha="center", va="center",
                fontsize=9, fontweight="bold", color="white", linespacing=1.4,
                transform=ax.transAxes)
        ax.text(x + 0.14, 0.38, desc, ha="center", va="center",
                fontsize=8, color="white", linespacing=1.5,
                transform=ax.transAxes)

    plt.tight_layout()
    return fig_to_stream(fig)


def img_fetal_positions():
    fig, axes = plt.subplots(2, 4, figsize=(10, 4.5))
    fig.patch.set_facecolor("#f0f4f8")
    positions = [
        ("LOA", "#27AE60", "Left Occiput\nAnterior\n(Most common)"),
        ("ROA", "#007A87", "Right Occiput\nAnterior"),
        ("LOP", "#F5A623", "Left Occiput\nPosterior"),
        ("ROP", "#C0392B", "Right Occiput\nPosterior"),
        ("LOT", "#8E44AD", "Left Occiput\nTransverse"),
        ("ROT", "#2980B9", "Right Occiput\nTransverse"),
        ("LSA", "#16A085", "Left Sacro\nAnterior\n(Breech)"),
        ("RMA", "#E67E22", "Right Mento\nAnterior\n(Face)"),
    ]

    for ax, (pos, col, desc) in zip(axes.flat, positions):
        ax.set_xlim(-1, 1); ax.set_ylim(-1, 1.3); ax.set_aspect("equal"); ax.axis("off")
        ax.set_facecolor("white")
        # Maternal pelvis (circle)
        ax.add_patch(Circle((0, 0), 0.85, fc="#FFDAB9", ec="#8B4513", lw=1.5, alpha=0.4))
        # Fetal head
        ax.add_patch(Circle((0, 0), 0.35, fc=col, ec="white", lw=1.5, alpha=0.85))
        ax.text(0, 0, pos, ha="center", va="center",
                fontsize=8, fontweight="bold", color="white")
        ax.text(0, 1.1, desc, ha="center", va="center",
                fontsize=6.5, color=col, linespacing=1.3, fontweight="bold")

    fig.suptitle("Fetal Positions in Labour", fontsize=12,
                 fontweight="bold", color="#1A3A5C")
    plt.tight_layout(pad=0.2)
    return fig_to_stream(fig)


# ─────────────────────────────────────────────────────────────────────────────
# BUILD THE PRESENTATION
# ─────────────────────────────────────────────────────────────────────────────
def build_ppt():
    prs = Presentation()
    prs.slide_width  = Inches(W_IN)
    prs.slide_height = Inches(H_IN)

    # ── TITLE SLIDE ──────────────────────────────────────────────────────────
    sl = prs.slides.add_slide(prs.slide_layouts[6])
    sw, sh = prs.slide_width, prs.slide_height

    bg = sl.shapes.add_shape(1, 0, 0, sw, sh)
    bg.fill.solid(); bg.fill.fore_color.rgb = NAVY; bg.line.fill.background()

    grad = sl.shapes.add_shape(1, 0, Inches(3.5), sw, sh - Inches(3.5))
    grad.fill.solid(); grad.fill.fore_color.rgb = RGBColor(0x0D, 0x24, 0x3B)
    grad.line.fill.background()

    stripe1 = sl.shapes.add_shape(1, 0, Inches(3.2), sw, Inches(0.12))
    stripe1.fill.solid(); stripe1.fill.fore_color.rgb = GOLD; stripe1.line.fill.background()

    tx_main = sl.shapes.add_textbox(Inches(0.8), Inches(0.8), Inches(11.7), Inches(2.2))
    tf = tx_main.text_frame; tf.word_wrap = True
    p = tf.paragraphs[0]; p.text = "NORMAL LABOUR"
    p.font.bold = True; p.font.size = Pt(54); p.font.color.rgb = WHITE
    p.alignment = PP_ALIGN.CENTER

    tx_sub = sl.shapes.add_textbox(Inches(0.8), Inches(2.9), Inches(11.7), Inches(0.8))
    tf2 = tx_sub.text_frame
    p2 = tf2.paragraphs[0]
    p2.text = "Mechanisms, Management & Clinical Essentials"
    p2.font.size = Pt(22); p2.font.color.rgb = GOLD; p2.font.bold = True
    p2.alignment = PP_ALIGN.CENTER

    refs = sl.shapes.add_textbox(Inches(0.8), Inches(3.5), Inches(11.7), Inches(0.7))
    tf3 = refs.text_frame
    p3 = tf3.paragraphs[0]
    p3.text = "Based on: Oxorn-Foote Human Labour & Birth  |  D.C. Dutta's Textbook of Obstetrics"
    p3.font.size = Pt(16); p3.font.color.rgb = RGBColor(0xAA, 0xCC, 0xFF)
    p3.alignment = PP_ALIGN.CENTER

    bullets_title = [
        "• Definition & Stages of Labour",
        "• Uterine Contractions & Cervical Changes",
        "• Fetal Skull & Pelvis",
        "• Cardinal Movements (Mechanism of Labour)",
        "• Partograph & Monitoring",
        "• Management of Each Stage",
        "• Episiotomy & Perineal Repair",
        "• Active Management of Third Stage",
        "• APGAR Score & Newborn Care",
        "• Complications & Prevention",
    ]
    tx_bullets = sl.shapes.add_textbox(Inches(1.5), Inches(4.2), Inches(10.3), Inches(3.0))
    tf4 = tx_bullets.text_frame; tf4.word_wrap = True
    for i in range(0, len(bullets_title), 2):
        p = tf4.paragraphs[0] if i == 0 else tf4.add_paragraph()
        # Put 2 items side by side in one paragraph
        line = bullets_title[i]
        if i + 1 < len(bullets_title):
            line += "          " + bullets_title[i+1]
        p.text = line
        p.font.size = Pt(13); p.font.color.rgb = RGBColor(0xCC, 0xE0, 0xFF)

    print("Slide 1 (Title) done")

    # ── SLIDE 2: Definition & Stages ─────────────────────────────────────────
    sl2 = add_slide(prs, "Definition & Stages of Normal Labour",
                    "Oxorn-Foote Ch.1 | DC Dutta Ch.11")
    bullets = [
        "DEFINITION (DC Dutta):",
        "   Labour is the process by which the products of conception are expelled from",
        "   the uterus after the period of viability (28 weeks / 1000 g).",
        "",
        "NORMAL LABOUR (Eutocia):",
        "   • Spontaneous onset at term (37–42 weeks)",
        "   • Single fetus in vertex presentation",
        "   • Delivered within 18 hrs (primipara) or 12 hrs (multipara)",
        "   • No complications to mother or baby",
        "",
        "FOUR STAGES:",
        "   Stage I:   Onset of labour → Full cervical dilation (10 cm)",
        "   Stage II:  Full dilation → Delivery of baby",
        "   Stage III: Delivery of baby → Expulsion of placenta & membranes",
        "   Stage IV:  1–2 hours postpartum (recovery)",
    ]
    add_bullets(sl2, bullets, 0.35, 1.55, 6.8, 5.7, font_size=15)
    insert_image(sl2, img_definition_timeline(), 7.2, 1.55, 5.8, 3.5)
    insert_image(sl2, img_stages_of_labour(), 7.2, 5.05, 5.8, 2.35)
    print("Slide 2 done")

    # ── SLIDE 3: True vs False Labour ─────────────────────────────────────────
    sl3 = add_slide(prs, "True Labour vs False Labour (Braxton-Hicks)",
                    "DC Dutta Ch.11 | Oxorn-Foote Ch.1")
    bullets3 = [
        "CAUSES OF LABOUR ONSET:",
        "   • Progesterone withdrawal theory",
        "   • Oxytocin surge (increased receptors at term)",
        "   • Prostaglandin release (arachidonic acid)",
        "   • Fetal cortisol → CRH → prostaglandins",
        "   • Stretch theory — uterine overdistension",
        "",
        "CLINICAL DISTINCTION:",
        "   • Always perform vaginal exam to confirm",
        "   • Serial exams 1–2 hrs apart if uncertain",
        "   • Cervical change is the gold standard",
    ]
    add_bullets(sl3, bullets3, 0.35, 1.55, 6.5, 5.7, font_size=15)
    insert_image(sl3, img_true_vs_false(), 7.0, 1.55, 6.0, 5.7)
    print("Slide 3 done")

    # ── SLIDE 4: Uterine Contractions ─────────────────────────────────────────
    sl4 = add_slide(prs, "Uterine Contractions in Labour",
                    "Oxorn-Foote Ch.2 | DC Dutta Ch.11")
    bullets4 = [
        "PROPERTIES (Oxorn-Foote):",
        "   • Fundal dominance — strongest at fundus",
        "   • Triple descending gradient",
        "   • Polarity — fundus contracts down",
        "   • Retraction — fibres shorten permanently",
        "",
        "NORMAL PARAMETERS:",
        "   Frequency:  3–5 per 10 minutes",
        "   Duration:   45–60 seconds",
        "   Intensity:  40–60 mmHg (active phase)",
        "   Baseline:   8–12 mmHg resting tone",
        "",
        "ACTIVE PHASE CRITERIA:",
        "   • Regular contractions ≥ 3 in 10 min",
        "   • Cervix ≥ 4 cm dilated",
        "   • Cervix effaced ≥ 50%",
        "",
        "PAIN PATHWAY:",
        "   • T10–L1 (1st stage): visceral pain",
        "   • S2–S4 (2nd stage): somatic pain",
    ]
    add_bullets(sl4, bullets4, 0.35, 1.55, 6.5, 5.7, font_size=14)
    insert_image(sl4, img_uterine_contractions(), 7.0, 1.6, 6.0, 3.2)
    insert_image(sl4, img_cervical_dilation(), 7.0, 4.85, 6.0, 2.5)
    print("Slide 4 done")

    # ── SLIDE 5: Fetal Skull ───────────────────────────────────────────────────
    sl5 = add_slide(prs, "Fetal Skull — Anatomy, Sutures & Diameters",
                    "DC Dutta Ch.2 | Oxorn-Foote Ch.3")
    bullets5 = [
        "BONES: 2 Frontal, 2 Parietal, 2 Temporal, 1 Occipital, 1 Sphenoid",
        "",
        "SUTURES:",
        "   Sagittal: between 2 parietal bones (L–R)",
        "   Coronal: frontal & parietal (transverse)",
        "   Lambdoid: parietal & occipital (posterior)",
        "   Frontal: between 2 frontal bones",
        "",
        "FONTANELLES:",
        "   Anterior (Bregma): diamond-shaped, closes 18 months",
        "   Posterior (Lambda): triangular, closes 6–8 weeks",
        "",
        "IMPORTANT LANDMARKS:",
        "   Nasion, Glabella, Sinciput, Bregma,",
        "   Vertex, Lambda, Occiput, Mentum",
    ]
    add_bullets(sl5, bullets5, 0.35, 1.55, 6.5, 5.7, font_size=14)
    insert_image(sl5, img_fetal_skull(), 7.0, 1.55, 6.0, 5.7)
    print("Slide 5 done")

    # ── SLIDE 6: Pelvis ───────────────────────────────────────────────────────
    sl6 = add_slide(prs, "Female Pelvis — Types & Dimensions",
                    "DC Dutta Ch.3 | Oxorn-Foote Ch.4")
    bullets6 = [
        "PELVIC PLANES:",
        "   Inlet (Brim):  AP 11 cm, Transverse 13 cm, Oblique 12 cm",
        "   Cavity:         AP 12 cm, Transverse 12 cm (round)",
        "   Outlet:         AP 13 cm, Transverse 11 cm",
        "",
        "PELVIC LANDMARKS:",
        "   • Ischial spines — station reference (0 station)",
        "   • Sacral promontory — obstetric conjugate",
        "   • Symphysis pubis",
        "",
        "STATION:",
        "   -5 to -1: above ischial spines",
        "    0:        at ischial spines (engaged)",
        "   +1 to +5: below ischial spines",
        "",
        "ENGAGEMENT:",
        "   Biparietal diameter below pelvic inlet",
        "   Primip: 36 wks | Multip: onset of labour",
    ]
    add_bullets(sl6, bullets6, 0.35, 1.55, 6.5, 5.7, font_size=14)
    insert_image(sl6, img_pelvis_types(), 7.0, 1.55, 6.0, 5.7)
    print("Slide 6 done")

    # ── SLIDE 7: Cardinal Movements ───────────────────────────────────────────
    sl7 = add_slide(prs, "Cardinal Movements (Mechanism) of Normal Labour",
                    "Oxorn-Foote Ch.6 | DC Dutta Ch.12")
    bullets7 = [
        "DEFINITION:",
        "   Positional changes of fetus during descent",
        "   through birth canal to navigate the pelvis",
        "",
        "LOA (Left Occipito-Anterior) — Most Common",
        "",
        "SEQUENCE:",
        "   1. ENGAGEMENT  — BPD enters pelvic brim",
        "   2. DESCENT     — continuous throughout",
        "   3. FLEXION     — chin meets chest (9.5 cm)",
        "   4. INT. ROTATION — occiput → pubic symphysis",
        "   5. EXTENSION   — head delivered under arch",
        "   6. RESTITUTION — 45° return to oblique",
        "   7. EXT. ROTATION — shoulders align AP",
        "   8. EXPULSION   — anterior then posterior",
    ]
    add_bullets(sl7, bullets7, 0.35, 1.55, 5.8, 5.7, font_size=14)
    insert_image(sl7, img_cardinal_movements(), 6.2, 1.55, 6.8, 5.7)
    print("Slide 7 done")

    # ── SLIDE 8: Mechanism Detail ──────────────────────────────────────────────
    sl8 = add_slide(prs, "Step-by-Step Mechanism — LOA Presentation",
                    "Oxorn-Foote Ch.6 | DC Dutta Ch.12")
    bullets8 = [
        "ENGAGEMENT:",
        "   Head enters at left oblique diameter",
        "   Sagittal suture in left oblique of inlet",
        "",
        "INTERNAL ROTATION:",
        "   Occiput rotates from left to OA (1/8 turn)",
        "   Sagittal suture now in AP diameter",
        "   Aided by: pelvic floor muscles (levator ani)",
        "",
        "EXTENSION:",
        "   Occiput under pubic symphysis = pivot point",
        "   Head extended: brow, face, chin born",
        "   Diameter = suboccipito-bregmatic (9.5 cm)",
        "",
        "RESTITUTION & EXT. ROTATION:",
        "   Head rotates 45° back to original oblique",
        "   Then shoulders rotate into AP: LSA → LOA → OA",
        "",
        "EXPULSION:",
        "   Anterior shoulder then posterior shoulder",
        "   Rest of body follows easily",
    ]
    add_bullets(sl8, bullets8, 0.35, 1.55, 6.5, 5.7, font_size=13)
    insert_image(sl8, img_mechanism_occiput_anterior(), 7.0, 1.55, 6.0, 5.7)
    print("Slide 8 done")

    # ── SLIDE 9: Fetal Positions ───────────────────────────────────────────────
    sl9 = add_slide(prs, "Fetal Lie, Presentation, Position & Attitude",
                    "DC Dutta Ch.11 | Oxorn-Foote Ch.5")
    bullets9 = [
        "LIE:  Longitudinal (99.5%), Transverse, Oblique",
        "",
        "PRESENTATION:",
        "   Vertex (96%), Breech (3%), Face, Brow, Shoulder",
        "",
        "POSITION — relation of denominator to maternal pelvis:",
        "   Vertex → Occiput",
        "   Face   → Mentum (chin)",
        "   Breech → Sacrum",
        "   Shoulder → Acromion",
        "",
        "ATTITUDE — relation of fetal parts to each other:",
        "   Full flexion (best), Deflexion, Extension",
        "",
        "DENOMINATOR positions:",
        "   OA, OP, OT (L or R), with LOA most common",
    ]
    add_bullets(sl9, bullets9, 0.35, 1.55, 6.5, 5.7, font_size=14)
    insert_image(sl9, img_fetal_positions(), 7.0, 1.55, 6.0, 5.7)
    print("Slide 9 done")

    # ── SLIDE 10: Partograph ──────────────────────────────────────────────────
    sl10 = add_slide(prs, "Partograph — WHO Labour Monitoring Tool",
                     "DC Dutta Ch.13 | Oxorn-Foote Ch.8")
    bullets10 = [
        "DEFINITION: Graphical record of labour progress",
        "  Introduced by Philpott & Castle (1972)",
        "  Modified WHO partograph (1994)",
        "",
        "COMPONENTS:",
        "   1. Cervicograph — dilation vs time",
        "   2. Alert line — 1 cm/hr from 4 cm",
        "   3. Action line — 4 hrs to right of alert",
        "   4. Fetal heart rate (every 30 min)",
        "   5. Liquor, moulding, caput",
        "   6. Contractions per 10 min",
        "   7. Oxytocin, drugs, IVF",
        "   8. Maternal vitals, urine output",
        "",
        "ACTION:",
        "   Left of alert = normal",
        "   Alert–Action zone = increased surveillance",
        "   Beyond action line = intervention needed",
    ]
    add_bullets(sl10, bullets10, 0.35, 1.55, 6.5, 5.7, font_size=14)
    insert_image(sl10, img_partograph(), 7.0, 1.55, 6.0, 5.7)
    print("Slide 10 done")

    # ── SLIDE 11: Management Overview ────────────────────────────────────────
    sl11 = add_slide(prs, "Management of Normal Labour — Overview",
                     "DC Dutta Ch.13 | Oxorn-Foote Ch.8")
    bullets11 = [
        "ON ADMISSION:",
        "   History: GA, GBS status, antenatal records",
        "   Examination: fundal height, presentation, FHR",
        "   Vaginal exam: dilation, effacement, station",
        "   Investigations: CBC, urine, Group & screen",
        "",
        "GENERAL CARE:",
        "   • Supportive environment (doula/birth partner)",
        "   • Oral intake: sips of clear fluids (active labour)",
        "   • IV access if high risk",
        "   • Bladder care — void every 2 hrs",
        "   • Ambulation encouraged in 1st stage",
        "   • Pain relief: epidural, opioids, N₂O",
        "",
        "MONITORING:",
        "   • FHR: q30 min (latent), q15 min (active), q5 min (2nd stage)",
        "   • Contractions: frequency, duration, strength",
        "   • Vaginal exam: every 4 hrs (or if concern)",
        "   • Maternal vitals: BP, pulse, temperature",
    ]
    add_bullets(sl11, bullets11, 0.35, 1.55, 6.5, 5.7, font_size=13)
    insert_image(sl11, img_management_summary(), 7.0, 1.55, 6.0, 5.7)
    print("Slide 11 done")

    # ── SLIDE 12: First Stage ──────────────────────────────────────────────────
    sl12 = add_slide(prs, "First Stage of Labour — Management",
                     "DC Dutta Ch.13 | Oxorn-Foote Ch.8")
    bullets12 = [
        "PHASES OF FIRST STAGE:",
        "",
        "   LATENT PHASE (0–3 cm):",
        "   • Cervix effaces, slowly dilates to 3 cm",
        "   • Duration: ≤ 8 hrs (primip), ≤ 4 hrs (multip)",
        "   • Irregular, mild contractions",
        "",
        "   ACTIVE PHASE (4–10 cm):",
        "   • Rate: ≥ 1 cm/hr (WHO 2018: active phase starts at 5 cm)",
        "   • Regular, stronger contractions 3–5/10 min",
        "   • Mucus show and rupture of membranes",
        "",
        "   TRANSITION (8–10 cm):",
        "   • Most intense contractions",
        "   • Urge to push may develop",
        "",
        "NORMAL DURATION:",
        "   Primipara:  12–18 hours total 1st stage",
        "   Multipara:  6–12 hours total 1st stage",
        "",
        "PROLONGED FIRST STAGE:",
        "   Arrest: no progress for ≥ 2 hrs in active phase",
    ]
    add_bullets(sl12, bullets12, 0.35, 1.55, 6.5, 5.7, font_size=13)

    # Friedman curve
    fig_f, ax_f = plt.subplots(figsize=(5.5, 4))
    fig_f.patch.set_facecolor("#f0f4f8"); ax_f.set_facecolor("#f0f4f8")
    hrs_p = [0, 2, 4, 6, 8, 10, 12, 14]
    dil_p = [0, 1, 2, 3, 5, 7, 9, 10]
    hrs_m = [0, 1, 2, 3, 4, 5, 6]
    dil_m = [0, 2, 4, 6, 8, 9.5, 10]
    ax_f.plot(hrs_p, dil_p, "b-o", lw=2.5, ms=6, label="Primipara (Friedman)")
    ax_f.plot(hrs_m, dil_m, "g-s", lw=2.5, ms=6, label="Multipara")
    ax_f.axhline(10, color="#C0392B", ls="--", lw=1.5, label="Full dilation (10 cm)")
    ax_f.axvline(4, color="#8E44AD", ls=":", lw=1.5, label="Active phase start")
    ax_f.fill_betweenx([0, 3], 0, 6, alpha=0.08, color="orange", label="Latent phase")
    ax_f.fill_betweenx([3, 10], 4, 14, alpha=0.06, color="blue", label="Active phase")
    ax_f.set_xlabel("Time (hours)", fontsize=10); ax_f.set_ylabel("Cervical Dilation (cm)", fontsize=10)
    ax_f.set_title("Friedman Labour Curve", fontsize=11, fontweight="bold", color="#1A3A5C")
    ax_f.legend(fontsize=7, loc="upper left"); ax_f.grid(True, alpha=0.3)
    ax_f.set_ylim(0, 11); ax_f.set_xlim(0, 15)
    plt.tight_layout()
    insert_image(sl12, fig_to_stream(fig_f), 7.0, 1.55, 6.0, 5.7)
    print("Slide 12 done")

    # ── SLIDE 13: Second Stage ──────────────────────────────────────────────────
    sl13 = add_slide(prs, "Second Stage of Labour — Delivery of Baby",
                     "DC Dutta Ch.14 | Oxorn-Foote Ch.9")
    bullets13 = [
        "DEFINITION: Full cervical dilation → delivery of baby",
        "",
        "DIAGNOSIS:",
        "   • Irresistible urge to push (Ferguson reflex)",
        "   • Perineal bulging, anal dilatation",
        "   • Presenting part visible",
        "",
        "DURATION:",
        "   Primipara:  up to 2 hours (3 hrs with epidural)",
        "   Multipara:  up to 1 hour (2 hrs with epidural)",
        "",
        "MANAGEMENT:",
        "   • Encourage pushing with contractions",
        "   • Valsalva technique: closed-glottis pushing",
        "   • Support perineum — modified Ritgen manoeuvre",
        "   • Control delivery of head — 'guard perineum'",
        "   • Check for cord around neck",
        "   • Oxytocin 10 IU IM on anterior shoulder",
        "   • Clamp and cut cord after 1–3 min (delayed)",
        "",
        "CROWNING:",
        "   BPD at pelvic outlet — maximum distension",
    ]
    add_bullets(sl13, bullets13, 0.35, 1.55, 6.5, 5.7, font_size=13)

    # Delivery stages diagram
    fig_d, axes_d = plt.subplots(1, 3, figsize=(6, 3.5))
    fig_d.patch.set_facecolor("#f0f4f8")
    del_stages = [
        ("Crowning\n& Head\nDelivery", "#1A3A5C"),
        ("Restitution\n& External\nRotation", "#007A87"),
        ("Shoulder &\nBody\nExpulsion", "#27AE60"),
    ]
    for dax, (lbl, col) in zip(axes_d, del_stages):
        dax.set_xlim(0, 1); dax.set_ylim(0, 1); dax.axis("off")
        dax.set_facecolor("#f0f4f8")
        dax.add_patch(FancyBboxPatch((0.05, 0.08), 0.90, 0.82,
                                     boxstyle="round,pad=0.04",
                                     fc=col, ec="white", lw=2, alpha=0.88))
        dax.add_patch(Circle((0.5, 0.62), 0.22, fc="white", ec=col, lw=0))
        dax.text(0.5, 0.62, "👶", ha="center", va="center", fontsize=18)
        dax.text(0.5, 0.28, lbl, ha="center", va="center", fontsize=9,
                 fontweight="bold", color="white", linespacing=1.4)
    fig_d.suptitle("Sequence of Delivery", fontsize=10, fontweight="bold", color="#1A3A5C")
    plt.tight_layout()
    insert_image(sl13, fig_to_stream(fig_d), 7.0, 1.55, 6.0, 5.7)
    print("Slide 13 done")

    # ── SLIDE 14: Third Stage ──────────────────────────────────────────────────
    sl14 = add_slide(prs, "Third Stage of Labour — Placental Delivery",
                     "DC Dutta Ch.15 | Oxorn-Foote Ch.10")
    bullets14 = [
        "DEFINITION: Delivery of baby → Expulsion of placenta",
        "DURATION: Normally 5–30 minutes",
        "",
        "PLACENTAL SEPARATION:",
        "   • Retroplacental haematoma forms",
        "   • Duncan mechanism: edge first (marginal sinus)",
        "   • Schultze mechanism: centre first (most common)",
        "",
        "SIGNS OF SEPARATION:",
        "   • Uterus rises, becomes globular & firm",
        "   • Gush of blood (retroplacental clot expelled)",
        "   • Umbilical cord lengthens (Allis sign / Strassman)",
        "   • Placenta sinks into lower segment",
        "",
        "ACTIVE MANAGEMENT (AMTSL):",
        "   1. Oxytocin 10 IU IM (within 1 min of delivery)",
        "   2. Controlled cord traction (CCT)",
        "   3. Uterine massage after delivery",
        "",
        "INSPECTION OF PLACENTA:",
        "   • Completeness of cotyledons & membranes",
        "   • Number of cord vessels (2 arteries, 1 vein)",
        "   • Abnormal lobes (succenturiate)",
    ]
    add_bullets(sl14, bullets14, 0.35, 1.55, 6.5, 5.7, font_size=13)
    insert_image(sl14, img_active_management(), 7.0, 1.55, 6.0, 3.5)
    insert_image(sl14, img_oxytocin_uterotonic(), 7.0, 5.1, 6.0, 2.2)
    print("Slide 14 done")

    # ── SLIDE 15: Episiotomy ──────────────────────────────────────────────────
    sl15 = add_slide(prs, "Episiotomy — Indications, Types & Repair",
                     "DC Dutta Ch.16 | Oxorn-Foote Ch.11")
    bullets15 = [
        "DEFINITION: Surgical incision of perineum to enlarge",
        "            the vaginal outlet during delivery",
        "",
        "INDICATIONS:",
        "   • Imminent perineal tear",
        "   • Large baby / shoulder dystocia",
        "   • Instrumental delivery (forceps/vacuum)",
        "   • Fetal distress needing rapid delivery",
        "   • Preterm delivery (protection from trauma)",
        "",
        "TYPES:",
        "   Midline (median): along midline raphe",
        "   Mediolateral: 45° angle (right preferred)",
        "   Lateral: rarely used",
        "   J-shaped: combines both",
        "",
        "REPAIR (Layers):",
        "   1. Vaginal mucosa — continuous 2-0 Vicryl",
        "   2. Deep perineal muscles — interrupted",
        "   3. Superficial perineal muscles",
        "   4. Skin — subcuticular or interrupted",
    ]
    add_bullets(sl15, bullets15, 0.35, 1.55, 6.5, 5.7, font_size=13)
    insert_image(sl15, img_episiotomy(), 7.0, 1.55, 6.0, 5.7)
    print("Slide 15 done")

    # ── SLIDE 16: APGAR Score ──────────────────────────────────────────────────
    sl16 = add_slide(prs, "APGAR Score & Immediate Newborn Care",
                     "DC Dutta Ch.18 | Oxorn-Foote Ch.12")
    bullets16 = [
        "APGAR SCORE (Virginia Apgar, 1952):",
        "   Assessed at 1 minute and 5 minutes of life",
        "   A = Appearance (colour)",
        "   P = Pulse (heart rate)",
        "   G = Grimace (reflex irritability)",
        "   A = Activity (muscle tone)",
        "   R = Respiration",
        "",
        "INTERPRETATION:",
        "   8–10 = Normal (routine care)",
        "   4–7  = Moderate depression (stimulate, O₂)",
        "   0–3  = Severe (immediate resuscitation)",
        "",
        "IMMEDIATE NEWBORN CARE:",
        "   • Dry and stimulate (first 30 seconds)",
        "   • Clear airway if meconium",
        "   • Warmth — radiant warmer",
        "   • Vitamin K 1 mg IM",
        "   • Eye prophylaxis",
        "   • Delayed cord clamping 1–3 minutes",
        "   • Skin-to-skin (kangaroo care)",
        "   • Initiate breastfeeding within 1 hour",
    ]
    add_bullets(sl16, bullets16, 0.35, 1.55, 6.5, 5.7, font_size=13)
    insert_image(sl16, img_newborn_apgar(), 7.0, 1.55, 6.0, 5.7)
    print("Slide 16 done")

    # ── SLIDE 17: Complications ───────────────────────────────────────────────
    sl17 = add_slide(prs, "Complications of Normal Labour",
                     "DC Dutta Ch.22-25 | Oxorn-Foote Ch.16-20")
    bullets17 = [
        "MATERNAL:",
        "   • Prolonged labour (>18h primip, >12h multip)",
        "   • PPH (primary <24h, secondary 24h–12 wks)",
        "   • Uterine rupture — scar dehiscence",
        "   • Perineal tears (1st–4th degree)",
        "   • Obstetric fistula (VVF, RVF)",
        "   • Infection / sepsis",
        "",
        "FETAL/NEONATAL:",
        "   • Fetal distress / birth asphyxia",
        "   • Shoulder dystocia (Erb's palsy, fractures)",
        "   • Cord prolapse / compression",
        "   • Meconium aspiration",
        "   • Intracranial haemorrhage",
        "",
        "PREVENTION:",
        "   • WHO Safe Childbirth Checklist",
        "   • Skilled birth attendance",
        "   • Partograph monitoring",
        "   • AMTSL for all deliveries",
    ]
    add_bullets(sl17, bullets17, 0.35, 1.55, 6.5, 5.7, font_size=13)
    insert_image(sl17, img_complications(), 7.0, 1.55, 6.0, 5.7)
    print("Slide 17 done")

    # ── SLIDE 18: Key Points Summary ──────────────────────────────────────────
    sl18 = add_slide(prs, "Key Examination Points — Normal Labour",
                     "High-Yield Summary")

    fig_kp, ax_kp = plt.subplots(figsize=(8.5, 5.5))
    fig_kp.patch.set_facecolor("#f0f4f8"); ax_kp.set_facecolor("#f0f4f8"); ax_kp.axis("off")

    boxes = [
        (0.02, 0.66, "#1A3A5C", "DEFINITIONS",
         ["Normal labour: Spontaneous, vertex, term, ≤18h, no complications",
          "Engagement = BPD below pelvic inlet",
          "Station = presenting part vs ischial spines"]),
        (0.52, 0.66, "#007A87", "STAGES & DURATION",
         ["Stage I: 12h primip / 6h multip",
          "Stage II: 1h primip / 30min multip",
          "Stage III: 5–30 minutes; Stage IV: 2h"]),
        (0.02, 0.32, "#27AE60", "MECHANISM (LOA)",
         ["Engagement → Descent → Flexion",
          "→ Internal rotation → Extension",
          "→ Restitution → Ext. rotation → Expulsion"]),
        (0.52, 0.32, "#C0392B", "AMTSL (3 Steps)",
         ["1. Oxytocin 10 IU IM on ant. shoulder",
          "2. Controlled cord traction (CCT)",
          "3. Uterine massage after placenta"]),
        (0.02, 0.01, "#8E44AD", "MONITORING",
         ["Partograph: alert (1 cm/hr) + action line",
          "FHR q30min (latent), q15min (active)",
          "VE every 4 hrs"]),
        (0.52, 0.01, "#F5A623", "APGAR & NEWBORN",
         ["Score at 1 min and 5 min",
          "7–10 normal; 0–3 needs resuscitation",
          "Vit K, delayed cord clamp, skin-to-skin"]),
    ]

    for x, y, col, title, items in boxes:
        ax_kp.add_patch(FancyBboxPatch((x, y), 0.47, 0.30,
                                        boxstyle="round,pad=0.02",
                                        fc=col, ec="white", lw=1.5, alpha=0.9,
                                        transform=ax_kp.transAxes))
        ax_kp.text(x + 0.235, y + 0.26, title, ha="center", va="top",
                   fontsize=10, fontweight="bold", color="white",
                   transform=ax_kp.transAxes)
        for i, item in enumerate(items):
            ax_kp.text(x + 0.025, y + 0.18 - i * 0.082, f"• {item}",
                       ha="left", va="top", fontsize=8, color="white",
                       transform=ax_kp.transAxes)

    plt.tight_layout()
    insert_image(sl18, fig_to_stream(fig_kp), 0.25, 1.5, 12.8, 5.8)
    print("Slide 18 done")

    # ── SLIDE 19: Thank You / References ─────────────────────────────────────
    sl19 = prs.slides.add_slide(prs.slide_layouts[6])
    sw, sh = prs.slide_width, prs.slide_height

    bg19 = sl19.shapes.add_shape(1, 0, 0, sw, sh)
    bg19.fill.solid(); bg19.fill.fore_color.rgb = NAVY; bg19.line.fill.background()

    stripe19 = sl19.shapes.add_shape(1, 0, Inches(3.8), sw, Inches(0.1))
    stripe19.fill.solid(); stripe19.fill.fore_color.rgb = GOLD; stripe19.line.fill.background()

    ty = sl19.shapes.add_textbox(Inches(0.5), Inches(0.8), Inches(12.3), Inches(2.5))
    tf_ty = ty.text_frame
    p_ty = tf_ty.paragraphs[0]
    p_ty.text = "Thank You"
    p_ty.font.size = Pt(52); p_ty.font.bold = True; p_ty.font.color.rgb = WHITE
    p_ty.alignment = PP_ALIGN.CENTER

    sub19 = sl19.shapes.add_textbox(Inches(0.5), Inches(2.7), Inches(12.3), Inches(0.9))
    tf_s = sub19.text_frame
    p_s = tf_s.paragraphs[0]
    p_s.text = "Normal Labour — Mechanisms, Management & Clinical Essentials"
    p_s.font.size = Pt(20); p_s.font.color.rgb = GOLD; p_s.font.bold = True
    p_s.alignment = PP_ALIGN.CENTER

    refs19 = sl19.shapes.add_textbox(Inches(1.5), Inches(4.0), Inches(10.3), Inches(3.0))
    tf_r = refs19.text_frame; tf_r.word_wrap = True
    ref_lines = [
        "REFERENCES",
        "1.  Oxorn H, Foote WR. Human Labor and Birth, 5th ed. Appleton-Century-Crofts.",
        "2.  Dutta DC. Textbook of Obstetrics, 9th ed. New Central Book Agency.",
        "3.  WHO Recommendations: Intrapartum Care for a Positive Childbirth Experience (2018).",
        "4.  FIGO/ICM Active Management of Third Stage of Labour Guidelines.",
        "5.  RCOG Green-top Guideline: Management of Third- and Fourth-Degree Perineal Tears.",
    ]
    for i, line in enumerate(ref_lines):
        p = tf_r.paragraphs[0] if i == 0 else tf_r.add_paragraph()
        p.text = line
        p.font.size = Pt(14 if i == 0 else 12)
        p.font.color.rgb = RGBColor(0xCC, 0xE0, 0xFF)
        p.font.bold = (i == 0)
        p.space_after = Pt(3)

    print("Slide 19 (Thank You) done")

    # ── SAVE ─────────────────────────────────────────────────────────────────
    out_path = OUT / "Normal_Labour.pptx"
    prs.save(str(out_path))
    print(f"\n✅ Presentation saved to: {out_path}")
    return str(out_path)


if __name__ == "__main__":
    path = build_ppt()
    print(f"File size: {os.path.getsize(path) / 1024:.1f} KB")
Running Command

cd /tmp/workspace/normal-labour-ppt && python3 build_ppt.py 2>&1

Your presentation is ready:

Normal Labour

Presentation · PPTX


What's inside (19 slides, 1.3 MB)

#SlideCustom Diagram / Image
1Title SlideTopic list with navy/gold theme
2Definition & StagesTimeline bar + 4-stage colour blocks
3True vs False LabourSide-by-side comparison cards
4Uterine ContractionsIntrauterine pressure waveform + cervical dilation circles
5Fetal SkullAnnotated skull diagram + diameters table
6Pelvis TypesCaldwell-Moloy 4 types (Gynaecoid/Android/Anthropoid/Platypelloid)
7Cardinal Movements8-cell illustrated grid with icons
8LOA Mechanism Step-by-Step5-step delivery sequence diagram
9Fetal Lie/Position8-position grid (LOA, ROA, LOP, ROP, etc.)
10PartographWHO cervicograph + FHR monitoring chart
11Management Overview4-column stage management boxes
12First StageFriedman labour curve (primip vs multip)
13Second StageDelivery sequence with crowning → expulsion
14Third StageAMTSL 3-step diagram + uterotonic table
15EpisiotomyPerineum schematic + midline vs mediolateral table
16APGAR ScoreFull APGAR table with colour-coded scoring
17ComplicationsMaternal vs Fetal two-column layout
18Key Exam Points6-box high-yield summary grid
19Thank You / ReferencesFull reference list (Oxorn-Foote, DC Dutta, WHO 2018)
All diagrams were generated programmatically using matplotlib since external image downloads are blocked in the sandbox. Every slide has a custom clinical illustration relevant to its topic.

Can we add video in this ppt showing mechanism of labour step wise

Unfortunately, embedding a video directly into a PPTX file has a hard limitation in this sandbox - there is no way to download video files from the internet (all external requests are blocked), and python-pptx requires the actual video file bytes to embed it.
However, here are your best options:

Option 1 - Add a Clickable YouTube Link (I can do this now)

I can add a slide with a large clickable hyperlink button to the best YouTube video on mechanism of labour. When presenting, one click opens the video in your browser.
(This is a widely used animation of the 8 cardinal movements)

Option 2 - Embed video yourself (30 seconds, after downloading)

  1. Download a video (e.g. from YouTube using any downloader) and save it as mechanism.mp4
  2. In PowerPoint: Insert → Video → Video on my PC on the Cardinal Movements slide
  3. Done - it will play inline during the presentation

Option 3 - I add a YouTube link button on the Cardinal Movements slide (recommended)

I can modify the existing slide 7 (Cardinal Movements) to include:
  • A prominent "▶ Watch Animation" button with the YouTube URL as a hyperlink
  • A QR code-style text box pointing to the video

Shall I go ahead with Option 1/3 (add a clickable video button to the PPT)? That's the best I can do from within this sandbox. Once you download the PPT, you can also manually embed the MP4 using PowerPoint's Insert → Video feature.

Ppt on normal labour from any standard source with pictures in it

Reading File
Finding Sources
Finding Sources
Finding Sources
Finding Sources
Reading File
Reading File
Reading File
Loading Image
Loading Image
Reading File
Reading File
Finding Sources
Finding Sources
Reading File
Reading File
Loading Image
Reading File
Finding Sources
Reading File
Reading File
Loading Image
Reading File
Running Command

python /tmp/skills/shared/scripts/fetch_images.py \ "https://cdn.orris.care/cdss_images/301413f4f35165b4beae6977a8707abf2e1907c3c68db33d90d73fd6d99cc149.png" \ "https://cdn.orris.care/cdss_images/fc91d7d5e71e4f60c6da434116a8babc61de461999dacbbc30eec457e2bec1d2.png" \ "https://cdn.orris.care/cdss_images/083c770538011e907f4d95e79940098bd93a9fde3a1adcf6c6f9b51207df8ae1.png" \ "https://cdn.orris.care/cdss_images/9d44afe1c71dee9e7c2ea234d9c63df3119717fb4bdb52f11de14dcf0c631b0a.png" \ 2>&1 | python3 -c "import sys,json; d=json.load(sys.stdin); [print(i['url'][:60], 'OK' if i['base64'] else 'FAIL', i.get('error','')) for i in d]"

Writing File

~/normal-labour-ppt/build_v2.py

"""
Normal Labour PPT v2 — with REAL textbook images from medical library
Sources: Creasy & Resnik, Family Medicine (Textbook), Tintinalli's EM, Roberts & Hedges
"""
import os, io, json, subprocess, base64, math
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from matplotlib.patches import FancyBboxPatch, Arc, Circle
import numpy as np
from pathlib import Path
from pptx import Presentation
from pptx.util import Inches, Pt, Emu
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN

OUT = Path("/tmp/workspace/normal-labour-ppt")
OUT.mkdir(parents=True, exist_ok=True)
PPTX_PATH = OUT / "Normal_Labour_v2.pptx"

# ── Colour palette ─────────────────────────────────────────────────────
NAVY   = RGBColor(0x1A, 0x3A, 0x5C)
TEAL   = RGBColor(0x00, 0x7A, 0x87)
GOLD   = RGBColor(0xF5, 0xA6, 0x23)
WHITE  = RGBColor(0xFF, 0xFF, 0xFF)
LGRAY  = RGBColor(0xF0, 0xF4, 0xF8)
DKGRAY = RGBColor(0x33, 0x33, 0x33)
RED    = RGBColor(0xC0, 0x39, 0x2B)
GREEN  = RGBColor(0x27, 0xAE, 0x60)
W_IN, H_IN = 13.333, 7.5

# ── Fetch images via skill helper ───────────────────────────────────────
IMAGE_URLS = {
    "labour_curve":  "https://cdn.orris.care/cdss_images/301413f4f35165b4beae6977a8707abf2e1907c3c68db33d90d73fd6d99cc149.png",
    "composite_curves": "https://cdn.orris.care/cdss_images/fc91d7d5e71e4f60c6da434116a8babc61de461999dacbbc30eec457e2bec1d2.png",
    "cardinal_movements": "https://cdn.orris.care/cdss_images/083c770538011e907f4d95e79940098bd93a9fde3a1adcf6c6f9b51207df8ae1.png",
    "shoulder_dystocia": "https://cdn.orris.care/cdss_images/9d44afe1c71dee9e7c2ea234d9c63df3119717fb4bdb52f11de14dcf0c631b0a.png",
}

print("Fetching textbook images...")
result = json.loads(subprocess.check_output(
    ["python", "/tmp/skills/shared/scripts/fetch_images.py"] + list(IMAGE_URLS.values())
))
IMAGES = {}
for key, item in zip(IMAGE_URLS.keys(), result):
    if item["base64"]:
        raw = base64.b64decode(item["base64"].split(",", 1)[1])
        IMAGES[key] = io.BytesIO(raw)
        print(f"  ✓ {key}")
    else:
        print(f"  ✗ {key}: {item.get('error')}")

def get_img(key):
    buf = IMAGES[key]
    buf.seek(0)
    return buf

# ── Matplotlib helpers ──────────────────────────────────────────────────
def fig_to_stream(fig):
    buf = io.BytesIO()
    fig.savefig(buf, format="png", bbox_inches="tight", dpi=130)
    buf.seek(0)
    plt.close(fig)
    return buf

def add_slide(prs, title_text, subtitle_text=""):
    slide = prs.slides.add_slide(prs.slide_layouts[6])
    sw, sh = prs.slide_width, prs.slide_height
    # BG
    bg = slide.shapes.add_shape(1, 0, 0, sw, sh)
    bg.fill.solid(); bg.fill.fore_color.rgb = LGRAY; bg.line.fill.background()
    # Header band
    hb = slide.shapes.add_shape(1, 0, 0, sw, Inches(1.3))
    hb.fill.solid(); hb.fill.fore_color.rgb = NAVY; hb.line.fill.background()
    # Gold stripe
    gs = slide.shapes.add_shape(1, 0, Inches(1.3), sw, Inches(0.07))
    gs.fill.solid(); gs.fill.fore_color.rgb = GOLD; gs.line.fill.background()
    # Title
    tx = slide.shapes.add_textbox(Inches(0.3), Inches(0.1), Inches(12.7), Inches(0.95))
    tf = tx.text_frame; tf.word_wrap = True
    p = tf.paragraphs[0]; p.text = title_text
    p.font.bold = True; p.font.size = Pt(30); p.font.color.rgb = WHITE
    if subtitle_text:
        tx2 = slide.shapes.add_textbox(Inches(0.3), Inches(0.95), Inches(12.7), Inches(0.38))
        p2 = tx2.text_frame.paragraphs[0]
        p2.text = subtitle_text
        p2.font.size = Pt(14); p2.font.color.rgb = GOLD; p2.font.bold = True
    return slide

def add_bullets(slide, bullets, left, top, width, height, font_size=15):
    tx = slide.shapes.add_textbox(Inches(left), Inches(top), Inches(width), Inches(height))
    tf = tx.text_frame; tf.word_wrap = True
    for i, b in enumerate(bullets):
        p = tf.paragraphs[0] if i == 0 else tf.add_paragraph()
        indent = b.startswith("   ")
        p.text = b.strip()
        p.font.size = Pt(font_size - (1.5 if indent else 0))
        p.font.color.rgb = DKGRAY
        p.space_after = Pt(3)
        p.level = 1 if indent else 0

def insert_img(slide, stream, left, top, width, height):
    stream.seek(0)
    slide.shapes.add_picture(stream, Inches(left), Inches(top), Inches(width), Inches(height))

def add_caption(slide, text, left, top, width):
    tx = slide.shapes.add_textbox(Inches(left), Inches(top), Inches(width), Inches(0.45))
    tf = tx.text_frame; tf.word_wrap = True
    p = tf.paragraphs[0]; p.text = text
    p.font.size = Pt(9); p.font.italic = True; p.font.color.rgb = RGBColor(0x55,0x55,0x55)
    p.alignment = PP_ALIGN.CENTER

# ── Generated images ────────────────────────────────────────────────────
def img_stages_banner():
    fig, ax = plt.subplots(figsize=(8.5, 1.8))
    fig.patch.set_facecolor("#f0f4f8"); ax.axis("off"); ax.set_facecolor("#f0f4f8")
    stages = [
        ("STAGE I\nOnset → 10 cm", "#1A3A5C", "12h primip\n6h multip"),
        ("STAGE II\n10cm → Baby", "#007A87", "1h primip\n30min multip"),
        ("STAGE III\nBaby → Placenta", "#27AE60", "5–30 min"),
        ("STAGE IV\nRecovery", "#F5A623", "1–2 hours"),
    ]
    for i, (lbl, col, dur) in enumerate(stages):
        x = i * 0.25
        ax.add_patch(FancyBboxPatch((x+0.005, 0.1), 0.24, 0.82,
            boxstyle="round,pad=0.02", fc=col, ec="white", lw=1.5,
            transform=ax.transAxes, alpha=0.92))
        ax.text(x+0.125, 0.70, lbl, ha="center", va="center", fontsize=9,
                fontweight="bold", color="white", linespacing=1.3,
                transform=ax.transAxes)
        ax.text(x+0.125, 0.30, dur, ha="center", va="center", fontsize=8.5,
                color="white", linespacing=1.3, transform=ax.transAxes)
    plt.tight_layout(pad=0)
    return fig_to_stream(fig)

def img_true_false():
    fig, axes = plt.subplots(1, 2, figsize=(8, 4.2))
    fig.patch.set_facecolor("#f0f4f8")
    data = [
        ("TRUE LABOUR", "#27AE60", [
            "Regular, rhythmic contractions",
            "Progressive — frequency & intensity ↑",
            "Pain: lumbosacral → abdomen",
            "Cervix dilates & effaces",
            "Bloody show present",
            "Walking intensifies contractions",
            "Analgesics do NOT stop labour",
        ]),
        ("FALSE LABOUR\n(Braxton-Hicks)", "#C0392B", [
            "Irregular contractions",
            "No progressive change",
            "Pain mainly lower abdomen",
            "No cervical change",
            "Show usually absent",
            "Walking may relieve pain",
            "Analgesics may stop contractions",
        ]),
    ]
    for ax, (title, col, items) in zip(axes, data):
        ax.set_xlim(0,1); ax.set_ylim(0,1); ax.axis("off"); ax.set_facecolor("#f0f4f8")
        ax.add_patch(FancyBboxPatch((0.03,0.03),0.94,0.94, boxstyle="round,pad=0.03",
                                    fc="white", ec=col, lw=2.5))
        ax.text(0.5,0.92, title, ha="center", va="top", fontsize=11,
                fontweight="bold", color=col, linespacing=1.3)
        for i, item in enumerate(items):
            ax.text(0.08, 0.80-i*0.107, f"• {item}", ha="left", va="top",
                    fontsize=8.2, color="#333")
    fig.suptitle("True vs False Labour", fontsize=13, fontweight="bold",
                 color="#1A3A5C", y=1.01)
    plt.tight_layout()
    return fig_to_stream(fig)

def img_uterine_contractions():
    fig, ax = plt.subplots(figsize=(7, 3.5))
    fig.patch.set_facecolor("#f0f4f8"); ax.set_facecolor("#f0f4f8")
    t = np.linspace(0, 10, 1000)
    y = np.zeros_like(t)
    for pk in [1.5, 4.0, 7.0]:
        y += 65 * np.exp(-((t-pk)**2)/0.28)
    y += 10; y = np.clip(y, 0, 100)
    ax.fill_between(t, 10, y, where=(y>10), alpha=0.3, color="#007A87")
    ax.plot(t, y, color="#1A3A5C", lw=2.5)
    ax.axhline(10, color="#C0392B", lw=1.2, ls="--", label="Resting tone (10 mmHg)")
    ax.axhline(25, color="#F5A623", lw=1.2, ls=":", label="Pain threshold (~25 mmHg)")
    for pk, nm in [(1.5,"1st"),(4.0,"2nd"),(7.0,"3rd")]:
        ax.annotate(f"{nm}\n~60–80 mmHg", xy=(pk,75), xytext=(pk+0.5,95),
                    arrowprops=dict(arrowstyle="->", color="#1A3A5C"),
                    fontsize=7.5, color="#1A3A5C")
    ax.set_xlabel("Time (min)", fontsize=10); ax.set_ylabel("IUP (mmHg)", fontsize=10)
    ax.set_title("Uterine Contractions in Active Labour\n(3–5/10 min, 45–60 sec, 40–60 mmHg)",
                 fontsize=10, fontweight="bold", color="#1A3A5C")
    ax.legend(fontsize=8, loc="upper right"); ax.set_ylim(0,110); ax.grid(True, alpha=0.25)
    plt.tight_layout()
    return fig_to_stream(fig)

def img_fetal_skull():
    fig, axes = plt.subplots(1, 2, figsize=(9, 4))
    fig.patch.set_facecolor("#f0f4f8")
    ax = axes[0]
    ax.set_xlim(0,10); ax.set_ylim(0,8); ax.axis("off"); ax.set_facecolor("#f0f4f8")
    skull = mpatches.Ellipse((5,4), 7, 5.5, fc="#FFE0B2", ec="#8B4513", lw=2, alpha=0.8)
    ax.add_patch(skull)
    ax.plot([5,5],[1.5,6.5], color="#8B4513", lw=1.5, ls="--")
    ax.plot([1.8,8.2],[4,4], color="#8B4513", lw=1.5, ls="--")
    ax.plot([2.2,7.8],[5.8,5.8], color="#4A2C6E", lw=1.2, ls=":")
    labels = [
        (5,6.5,"Anterior Fontanelle\n(Bregma) — Diamond","#27AE60"),
        (5,1.8,"Posterior Fontanelle\n(Lambda) — Triangular","#1A3A5C"),
        (2.8,4.0,"Coronal\nSuture","#8B4513"),
        (5,4.3,"Sagittal\nSuture","#C0392B"),
        (2.0,5.8,"Lambdoid\nSuture","#4A2C6E"),
    ]
    for x,y,lbl,col in labels:
        ax.text(x,y,lbl,ha="center",va="center",fontsize=7,color=col,fontweight="bold",
                bbox=dict(fc="white",ec=col,boxstyle="round,pad=0.2",alpha=0.85))
    ax.set_title("Fetal Skull — Sutures & Fontanelles", fontsize=10, fontweight="bold", color="#1A3A5C")

    ax2 = axes[1]
    ax2.set_xlim(0,1); ax2.set_ylim(0,1); ax2.axis("off"); ax2.set_facecolor("#f0f4f8")
    rows = [
        ("Diameter","Cm","Presentation"),
        ("Suboccipito-bregmatic","9.5","Vertex (full flexion)"),
        ("Suboccipito-frontal","10.0","Vertex (partial flex)"),
        ("Occipito-frontal","11.5","Deflexed vertex"),
        ("Mento-vertical","13.5","Brow"),
        ("Submento-bregmatic","9.5","Face"),
        ("Biparietal","9.5","Transverse"),
    ]
    xs=[0.02,0.52,0.70]; cws=[0.48,0.16,0.28]
    for i,row in enumerate(rows):
        bg="#1A3A5C" if i==0 else ("white" if i%2 else "#EBF5FB")
        tc="white" if i==0 else "#333"
        y=0.94-i*0.128
        for j,(cell,cw) in enumerate(zip(row,cws)):
            ax2.add_patch(FancyBboxPatch((xs[j],y-0.11),cw-0.01,0.115,
                          boxstyle="round,pad=0.01",fc=bg,ec="#ddd"))
            ax2.text(xs[j]+(cw-0.01)/2,y-0.055,cell,ha="center",va="center",
                     fontsize=7.5 if i>0 else 8,color=tc,
                     fontweight="bold" if i==0 else "normal",
                     transform=ax2.transAxes)
    ax2.set_title("Fetal Head Diameters", fontsize=10, fontweight="bold", color="#1A3A5C")
    plt.tight_layout()
    return fig_to_stream(fig)

def img_pelvis_types():
    fig, axes = plt.subplots(1, 4, figsize=(10, 3.2))
    fig.patch.set_facecolor("#f0f4f8")
    types = [
        ("Gynaecoid","#27AE60",(1.0,1.0),"50%\nMost common\nFavourable"),
        ("Android","#C0392B",(0.8,1.0),"20%\nHeart-shaped\nUnfavourable"),
        ("Anthropoid","#F5A623",(0.7,1.3),"25%\nAP oval\nModerate"),
        ("Platypelloid","#007A87",(1.3,0.6),"5%\nFlat\nUnfavourable"),
    ]
    for ax,(name,col,(rx,ry),note) in zip(axes,types):
        ax.set_xlim(-2,2); ax.set_ylim(-2,2.5); ax.set_aspect("equal"); ax.axis("off")
        ax.set_facecolor("#f0f4f8")
        ax.add_patch(mpatches.Ellipse((0,0),2*rx,2*ry,fc=col,ec="white",lw=2,alpha=0.35))
        ax.add_patch(mpatches.Ellipse((0,0),2*rx,2*ry,fc="none",ec=col,lw=2.5))
        ax.text(0,ry+0.35,name,ha="center",va="bottom",fontsize=10,fontweight="bold",color=col)
        ax.text(0,-ry-0.3,note,ha="center",va="top",fontsize=8,color="#444",linespacing=1.3)
        ax.annotate("",xy=(rx,0),xytext=(-rx,0),
                    arrowprops=dict(arrowstyle="<->",color=col,lw=1.5))
        ax.annotate("",xy=(0,ry),xytext=(0,-ry),
                    arrowprops=dict(arrowstyle="<->",color=col,lw=1.5))
    fig.suptitle("Caldwell-Moloy Classification — Pelvis Types",
                 fontsize=12,fontweight="bold",color="#1A3A5C")
    plt.tight_layout()
    return fig_to_stream(fig)

def img_partograph():
    fig, axes = plt.subplots(2, 1, figsize=(8.5, 5.5),
                              gridspec_kw={"height_ratios":[3,1.2]})
    fig.patch.set_facecolor("#fffef5")
    ax = axes[0]; ax.set_facecolor("#fffef5")
    hrs = np.array([0,1,2,3,4])
    alert = np.array([4,5,6,7,8])
    action = alert + 4
    progress = np.array([4,5.5,7.5,9,10])
    ax.plot(hrs, alert, "b-o", lw=2.2, ms=7, label="Alert Line (1 cm/hr)")
    ax.plot(hrs, action, "r-s", lw=2.2, ms=7, label="Action Line (+4 hrs)")
    ax.plot(hrs, progress, "g-D", lw=2.5, ms=8, label="Patient — cervical dilation")
    ax.fill_between(hrs, alert, action, alpha=0.15, color="orange", label="Warning zone")
    ax.fill_between(hrs, np.zeros(5)+4, alert, alpha=0.07, color="green")
    ax.set_xlim(-0.2,4.5); ax.set_ylim(3,11)
    ax.set_xlabel("Hours in Active Labour", fontsize=10)
    ax.set_ylabel("Cervical Dilation (cm)", fontsize=10)
    ax.set_title("WHO Partograph — Cervicograph", fontsize=11, fontweight="bold", color="#1A3A5C")
    ax.legend(fontsize=8, loc="upper left"); ax.grid(True, alpha=0.3)
    ax.set_yticks(range(4,11)); ax.set_xticks(range(0,5))
    ax2 = axes[1]; ax2.set_facecolor("#f0f0ff")
    fhr_t = np.linspace(0,4,100)
    np.random.seed(42)
    fhr = 140 + 8*np.sin(fhr_t*3.5) + np.random.normal(0,3,100)
    ax2.plot(fhr_t, fhr, color="#2980B9", lw=1.8)
    ax2.axhline(110, color="#C0392B", ls="--", lw=1.2, label="Min (110 bpm)")
    ax2.axhline(160, color="#C0392B", ls="--", lw=1.2, label="Max (160 bpm)")
    ax2.fill_between(fhr_t, 110, 160, alpha=0.08, color="#27AE60")
    ax2.set_xlim(-0.2,4.5); ax2.set_ylim(90,185)
    ax2.set_xlabel("Hours", fontsize=9); ax2.set_ylabel("FHR (bpm)", fontsize=9)
    ax2.legend(fontsize=7.5, loc="upper right"); ax2.grid(True, alpha=0.3)
    ax2.set_title("Fetal Heart Rate Monitoring", fontsize=9, color="#1A3A5C")
    plt.tight_layout()
    return fig_to_stream(fig)

def img_management_4cols():
    fig, ax = plt.subplots(figsize=(9, 4.5))
    fig.patch.set_facecolor("#f0f4f8"); ax.set_facecolor("#f0f4f8"); ax.axis("off")
    phases = [
        ("STAGE I\nMONITORING", "#1A3A5C",
         ["FHR q30 min (latent)\nq15 min (active)",
          "VE every 4 hrs", "Contractions: freq/strength",
          "Maternal BP, pulse, temp", "Maintain partograph"]),
        ("STAGE II\nDELIVERY", "#007A87",
         ["Guide pushing with contractions",
          "Support perineum (Ritgen)", "Control head delivery",
          "Check cord at neck", "Oxytocin 10 IU IM"]),
        ("STAGE III\nAMTSL", "#27AE60",
         ["Oxytocin 10 IU IM", "Controlled cord traction",
          "Uterine massage", "Inspect placenta/membranes",
          "Measure blood loss"]),
        ("STAGE IV\nRECOVERY", "#F5A623",
         ["Monitor vitals q15 min", "Uterine tone assessment",
          "Perineal repair", "Breastfeeding initiation",
          "Bonding — skin-to-skin"]),
    ]
    for i, (phase, col, items) in enumerate(phases):
        x = 0.02 + i*0.245
        ax.add_patch(FancyBboxPatch((x, 0.05), 0.235, 0.90,
            boxstyle="round,pad=0.02", fc=col, ec="white", lw=2, alpha=0.9,
            transform=ax.transAxes))
        ax.text(x+0.1175, 0.93, phase, ha="center", va="top", fontsize=9,
                fontweight="bold", color="white", linespacing=1.3,
                transform=ax.transAxes)
        for j, item in enumerate(items):
            ax.text(x+0.015, 0.78-j*0.148, f"• {item}", ha="left", va="top",
                    fontsize=7.2, color="white", linespacing=1.3,
                    transform=ax.transAxes)
    plt.tight_layout()
    return fig_to_stream(fig)

def img_amtsl():
    fig, ax = plt.subplots(figsize=(8, 3.8))
    fig.patch.set_facecolor("#f0f4f8"); ax.set_facecolor("#f0f4f8"); ax.axis("off")
    steps = [
        ("1","Uterotonic\nAdministration","Oxytocin 10 IU IM\nWithin 1 min of\nanterior shoulder","#27AE60"),
        ("2","Controlled Cord\nTraction (CCT)","Brandt-Andrews\nmanoeuvre\nCounter-pressure","#007A87"),
        ("3","Uterine\nMassage","After placenta\ndelivered\n30–60 sec","#1A3A5C"),
    ]
    for i, (num, title, desc, col) in enumerate(steps):
        x = 0.05 + i*0.32
        if i > 0:
            ax.annotate("", xy=(x-0.015, 0.5), xytext=(x-0.085, 0.5),
                arrowprops=dict(arrowstyle="-|>", color=col, lw=3),
                xycoords="axes fraction", textcoords="axes fraction")
        ax.add_patch(FancyBboxPatch((x, 0.08), 0.28, 0.84,
            boxstyle="round,pad=0.03", fc=col, ec="white", lw=2, alpha=0.9,
            transform=ax.transAxes))
        ax.add_patch(Circle((x+0.14, 0.82), 0.07, fc="white", ec=col, lw=0,
                             transform=ax.transAxes))
        ax.text(x+0.14, 0.82, num, ha="center", va="center",
                fontsize=15, fontweight="bold", color=col, transform=ax.transAxes)
        ax.text(x+0.14, 0.67, title, ha="center", va="center", fontsize=9,
                fontweight="bold", color="white", linespacing=1.4,
                transform=ax.transAxes)
        ax.text(x+0.14, 0.38, desc, ha="center", va="center", fontsize=8.5,
                color="white", linespacing=1.5, transform=ax.transAxes)
    ax.text(0.5, 0.97, "Active Management of Third Stage Labour (AMTSL)",
            ha="center", va="top", fontsize=11, fontweight="bold", color="#1A3A5C",
            transform=ax.transAxes)
    plt.tight_layout()
    return fig_to_stream(fig)

def img_apgar():
    fig, ax = plt.subplots(figsize=(9, 4))
    fig.patch.set_facecolor("#f0f4f8"); ax.set_facecolor("#f0f4f8"); ax.axis("off")
    headers = ["Sign","Score 0","Score 1","Score 2"]
    rows = [
        ["Appearance (Colour)","Blue/pale all over","Blue extremities","Pink all over"],
        ["Pulse (Heart Rate)","Absent","< 100 bpm","≥ 100 bpm"],
        ["Grimace (Reflex)","No response","Grimace only","Cough/cry/sneeze"],
        ["Activity (Tone)","Limp","Some flexion","Active motion"],
        ["Respiration","Absent","Slow/irregular","Good, crying"],
    ]
    score_cols=["#C0392B","#F5A623","#27AE60"]
    xs=[0.02,0.30,0.55,0.78]; cws=[0.27,0.24,0.22,0.20]
    ax.text(0.5,0.97,"APGAR Score (Virginia Apgar, 1952)",ha="center",va="top",
            fontsize=13,fontweight="bold",color="#1A3A5C",transform=ax.transAxes)
    for j,(h,x,cw) in enumerate(zip(headers,xs,cws)):
        col="#1A3A5C" if j==0 else score_cols[j-1]
        ax.add_patch(FancyBboxPatch((x,0.80),cw-0.01,0.14,
            boxstyle="round,pad=0.01",fc=col,ec="none",transform=ax.transAxes))
        ax.text(x+(cw-0.01)/2,0.87,h,ha="center",va="center",
                fontsize=10,fontweight="bold",color="white",transform=ax.transAxes)
    for i,row in enumerate(rows):
        y=0.78-i*0.148
        bg="white" if i%2==0 else "#EBF5FB"
        for j,(cell,x,cw) in enumerate(zip(row,xs,cws)):
            ax.add_patch(FancyBboxPatch((x,y-0.13),cw-0.01,0.14,
                boxstyle="round,pad=0.01",fc=bg,ec="#ddd",transform=ax.transAxes))
            ax.text(x+(cw-0.01)/2,y-0.06,cell,ha="center",va="center",
                    fontsize=8,color="#333",transform=ax.transAxes)
    interp=[(0.02,"7–10: Normal","#27AE60"),(0.36,"4–6: Moderate","#F5A623"),(0.67,"0–3: Severe","#C0392B")]
    for x,lbl,col in interp:
        ax.add_patch(FancyBboxPatch((x,0.01),0.30,0.08,boxstyle="round,pad=0.01",
            fc=col,ec="none",alpha=0.85,transform=ax.transAxes))
        ax.text(x+0.15,0.05,lbl,ha="center",va="center",fontsize=9,
                fontweight="bold",color="white",transform=ax.transAxes)
    plt.tight_layout()
    return fig_to_stream(fig)

def img_complications():
    fig, ax = plt.subplots(figsize=(9, 4.5))
    fig.patch.set_facecolor("#f0f4f8"); ax.set_facecolor("#f0f4f8"); ax.axis("off")
    cols_data = [
        ("MATERNAL\nCOMPLICATIONS","#C0392B",[
            "Prolonged labour (>18h primip)","PPH — atony, trauma, tissue",
            "Uterine rupture","Perineal tears (1°–4°)",
            "Infection / sepsis","Amniotic fluid embolism","Obstetric fistula (VVF)"]),
        ("FETAL\nCOMPLICATIONS","#1A3A5C",[
            "Birth asphyxia / HIE","Meconium aspiration syndrome",
            "Shoulder dystocia","Cord prolapse / compression",
            "Birth injuries (Erb's palsy)","Intracranial haemorrhage","Stillbirth"]),
        ("PREVENTION","#27AE60",[
            "Skilled birth attendant","WHO Safe Childbirth Checklist",
            "Partograph monitoring","AMTSL for all deliveries",
            "Continuous support in labour","Delayed cord clamping","Vitamin K prophylaxis"]),
    ]
    for i,(title,col,items) in enumerate(cols_data):
        x=0.02+i*0.325
        ax.add_patch(FancyBboxPatch((x,0.04),0.315,0.91,
            boxstyle="round,pad=0.02",fc=col,ec="white",lw=2,alpha=0.9,
            transform=ax.transAxes))
        ax.text(x+0.157,0.93,title,ha="center",va="top",fontsize=9,
                fontweight="bold",color="white",linespacing=1.3,
                transform=ax.transAxes)
        for j,item in enumerate(items):
            ax.text(x+0.025,0.78-j*0.12,f"• {item}",ha="left",va="top",
                    fontsize=7.8,color="white",transform=ax.transAxes)
    ax.text(0.5,0.99,"Complications of Labour",ha="center",va="top",
            fontsize=14,fontweight="bold",color="#1A3A5C",transform=ax.transAxes)
    plt.tight_layout()
    return fig_to_stream(fig)

def img_key_points():
    fig, ax = plt.subplots(figsize=(9, 5))
    fig.patch.set_facecolor("#f0f4f8"); ax.set_facecolor("#f0f4f8"); ax.axis("off")
    boxes = [
        (0.02,0.67,"#1A3A5C","DEFINITION",
         ["Spontaneous onset, vertex, term (37–42 wks)",
          "Completed within 18h primip / 12h multip","No complications to mother or baby"]),
        (0.52,0.67,"#007A87","MECHANISM",
         ["LOA: Engagement→Descent→Flexion",
          "Internal Rotation→Extension→Restitution",
          "External Rotation→Expulsion"]),
        (0.02,0.34,"#27AE60","MONITORING",
         ["FHR q30 min latent / q15 min active","Partograph: Alert (1cm/hr) + Action line",
          "VE every 4 hrs; Maternal vitals hourly"]),
        (0.52,0.34,"#C0392B","AMTSL",
         ["1. Oxytocin 10 IU IM on ant. shoulder","2. Controlled cord traction (CCT)",
          "3. Uterine massage after placenta"]),
        (0.02,0.02,"#8E44AD","DURATIONS",
         ["Stage I: 12h (P) / 6h (M)  |  Stage II: 1h (P) / 30min (M)",
          "Stage III: 5–30 min  |  Stage IV: 1–2 hours","FHR: 110–160 bpm; Normal EBL <500 mL"]),
        (0.52,0.02,"#F5A623","APGAR",
         ["Assess at 1 min and 5 min","7–10 Normal • 4–6 Moderate • 0–3 Severe",
          "Vitamin K, delayed cord clamp, skin-to-skin"]),
    ]
    for x,y,col,title,items in boxes:
        ax.add_patch(FancyBboxPatch((x,y),0.47,0.295,
            boxstyle="round,pad=0.02",fc=col,ec="white",lw=1.5,alpha=0.9,
            transform=ax.transAxes))
        ax.text(x+0.235,y+0.255,title,ha="center",va="top",fontsize=10,
                fontweight="bold",color="white",transform=ax.transAxes)
        for i,item in enumerate(items):
            ax.text(x+0.025,y+0.185-i*0.083,f"• {item}",ha="left",va="top",
                    fontsize=8,color="white",transform=ax.transAxes)
    plt.tight_layout()
    return fig_to_stream(fig)

# ═══════════════════════════════════════════════════════════════════════
# BUILD PRESENTATION
# ═══════════════════════════════════════════════════════════════════════
def build():
    prs = Presentation()
    prs.slide_width  = Inches(W_IN)
    prs.slide_height = Inches(H_IN)

    # ── SLIDE 1: TITLE ──────────────────────────────────────────────────
    sl = prs.slides.add_slide(prs.slide_layouts[6])
    sw, sh = prs.slide_width, prs.slide_height

    bg = sl.shapes.add_shape(1,0,0,sw,sh)
    bg.fill.solid(); bg.fill.fore_color.rgb=NAVY; bg.line.fill.background()
    overlay = sl.shapes.add_shape(1,0,Inches(4.0),sw,sh-Inches(4.0))
    overlay.fill.solid(); overlay.fill.fore_color.rgb=RGBColor(0x0D,0x24,0x3B)
    overlay.line.fill.background()
    stripe = sl.shapes.add_shape(1,0,Inches(3.7),sw,Inches(0.13))
    stripe.fill.solid(); stripe.fill.fore_color.rgb=GOLD; stripe.line.fill.background()

    tx = sl.shapes.add_textbox(Inches(0.8),Inches(0.6),Inches(11.7),Inches(2.5))
    tf=tx.text_frame; p=tf.paragraphs[0]
    p.text="NORMAL LABOUR"; p.font.bold=True; p.font.size=Pt(58)
    p.font.color.rgb=WHITE; p.alignment=PP_ALIGN.CENTER

    tx2=sl.shapes.add_textbox(Inches(0.8),Inches(2.9),Inches(11.7),Inches(0.75))
    p2=tx2.text_frame.paragraphs[0]
    p2.text="Physiology, Mechanisms & Clinical Management"
    p2.font.size=Pt(22); p2.font.color.rgb=GOLD; p2.font.bold=True
    p2.alignment=PP_ALIGN.CENTER

    tx3=sl.shapes.add_textbox(Inches(0.8),Inches(3.65),Inches(11.7),Inches(0.55))
    p3=tx3.text_frame.paragraphs[0]
    p3.text="Sources: Creasy & Resnik's MFM  |  Tintinalli's EM  |  Roberts & Hedges  |  Family Medicine"
    p3.font.size=Pt(14.5); p3.font.color.rgb=RGBColor(0xAA,0xCC,0xFF)
    p3.alignment=PP_ALIGN.CENTER

    # Topics list on title slide
    topics=[
        "• Definition & Stages  • True vs False Labour  • Uterine Contractions",
        "• Fetal Skull & Diameters  • Pelvis Types  • Cardinal Movements of Labour",
        "• Friedman Curve & Partograph  • Management of All Stages",
        "• AMTSL  • APGAR Score  • Complications & Prevention",
    ]
    tx4=sl.shapes.add_textbox(Inches(1.2),Inches(4.35),Inches(10.9),Inches(2.9))
    tf4=tx4.text_frame; tf4.word_wrap=True
    for i,line in enumerate(topics):
        p=tf4.paragraphs[0] if i==0 else tf4.add_paragraph()
        p.text=line; p.font.size=Pt(14.5); p.font.color.rgb=RGBColor(0xCC,0xE0,0xFF)
        p.space_after=Pt(4)
    print("Slide 1 (Title) ✓")

    # ── SLIDE 2: DEFINITION & STAGES ───────────────────────────────────
    sl2=add_slide(prs,"Definition & Stages of Normal Labour",
                  "Creasy & Resnik's MFM, p.936 | Tintinalli's EM | Family Medicine")
    bullets=[
        "DEFINITION (Creasy & Resnik):",
        "   Onset of painful uterine contractions associated with",
        "   effacement & dilation of the cervix.",
        "",
        "NORMAL LABOUR (Eutocia):",
        "   • Spontaneous onset at term (37–42 wks)",
        "   • Single fetus, vertex presentation",
        "   • Completed within 18h (primip) / 12h (multip)",
        "   • No complications to mother or baby",
        "",
        "FOUR STAGES (Pritchard & MacDonald):",
        "   Stage I:   Onset → Full dilation (10 cm)",
        "   Stage II:  Full dilation → Delivery of baby",
        "   Stage III: Baby → Delivery of placenta",
        "   Stage IV:  1–2 hrs postpartum recovery",
    ]
    add_bullets(sl2,bullets,0.3,1.5,6.5,5.8,font_size=15)
    insert_img(sl2,img_stages_banner(),6.9,1.5,6.1,2.1)
    insert_img(sl2,img_true_false(),6.9,3.65,6.1,3.7)
    print("Slide 2 ✓")

    # ── SLIDE 3: UTERINE CONTRACTIONS & PHYSIOLOGY ──────────────────────
    sl3=add_slide(prs,"Uterine Contractions — Physiology of Labour",
                  "Morgan & Mikhail Clinical Anesthesiology | Creasy & Resnik MFM")
    bullets3=[
        "ONSET OF LABOUR (Morgan & Mikhail):",
        "   • Uterine distension + enhanced oxytocin sensitivity",
        "   • Rapid increase in myometrial oxytocin receptors",
        "   • Altered prostaglandin synthesis (PGE2, PGF2α)",
        "",
        "NORMAL CONTRACTION PARAMETERS:",
        "   Frequency:  3–5 per 10 minutes",
        "   Duration:   45–60 seconds (active phase)",
        "   Intensity:  40–60 mmHg above baseline",
        "   Resting tone: 8–12 mmHg",
        "",
        "PROPERTIES (Fundal Dominance):",
        "   • Triple descending gradient",
        "   • Retraction — fibres shorten permanently",
        "   • Upper uterine segment thickens",
        "   • Lower segment passively thins",
        "",
        "PAIN PATHWAY:",
        "   1st stage: T10–L1 (visceral — uterus/cervix)",
        "   2nd stage: S2–S4 (somatic — perineum)",
    ]
    add_bullets(sl3,bullets3,0.3,1.5,6.6,5.8,font_size=14)
    insert_img(sl3,img_uterine_contractions(),7.0,1.55,6.0,3.2)

    # Labour curve from textbook
    insert_img(sl3,get_img("labour_curve"),7.0,4.85,6.0,2.55)
    add_caption(sl3,"FIGURE: Course of normal labour — cervical dilation & fetal descent (Morgan & Mikhail, Anesthesiology)",7.0,7.35,6.0)
    print("Slide 3 ✓")

    # ── SLIDE 4: FRIEDMAN CURVE + COMPOSITE ─────────────────────────────
    sl4=add_slide(prs,"Friedman Labour Curve — First Stage of Labour",
                  "Creasy & Resnik MFM p.937 | Family Medicine Fig 20-10")
    bullets4=[
        "FRIEDMAN CURVE (1954):",
        "   Sigmoid relationship between dilation & time",
        "",
        "LATENT PHASE:",
        "   • Cervical preparation (effacement + ripening)",
        "   • Duration: ≤ 20h nullipara, ≤ 14h multipara",
        "   • Minor dilation (0–3 cm)",
        "",
        "ACTIVE PHASE (begins ~4–5 cm):",
        "   • Phase of maximum slope (rapid dilation)",
        "   • Nullipara: ≥ 1.2 cm/hr",
        "   • Multipara: ≥ 1.5 cm/hr",
        "   • Deceleration phase: 8–10 cm",
        "",
        "ARREST DISORDERS:",
        "   Protracted active phase: < 1.2 cm/hr (nullip)",
        "   Secondary arrest: No change ≥ 2 hours",
        "   Prolonged deceleration: > 3h (nullip)",
    ]
    add_bullets(sl4,bullets4,0.3,1.5,5.8,5.8,font_size=14)
    insert_img(sl4,get_img("labour_curve"),6.2,1.5,7.0,3.4)
    add_caption(sl4,"Fig: Course of normal labour — latent phase, acceleration, phase of max slope, deceleration. (Morgan & Mikhail)",6.2,4.88,7.0)
    insert_img(sl4,get_img("composite_curves"),6.2,5.1,7.0,2.32)
    add_caption(sl4,"Fig 20-10: Composite curves — normal & abnormal labour (primigravid & multiparous). (Family Medicine Textbook)",6.2,7.38,7.0)
    print("Slide 4 ✓")

    # ── SLIDE 5: FETAL SKULL ────────────────────────────────────────────
    sl5=add_slide(prs,"Fetal Skull — Anatomy, Sutures & Diameters",
                  "Creasy & Resnik MFM | Roberts & Hedges Clinical Procedures")
    bullets5=[
        "BONES (6):",
        "   2 Frontal, 2 Parietal, 2 Temporal,",
        "   1 Occipital, 1 Sphenoid",
        "",
        "SUTURES:",
        "   Sagittal  — between 2 parietal bones",
        "   Coronal   — frontal & parietal",
        "   Lambdoid  — parietal & occipital",
        "   Frontal   — between 2 frontal bones",
        "",
        "FONTANELLES:",
        "   Anterior (Bregma): Diamond, closes 18 months",
        "   Posterior (Lambda): Triangle, closes 6–8 weeks",
        "",
        "DENOMINATOR:",
        "   Vertex → Occiput (most common)",
        "   Face → Mentum | Breech → Sacrum",
        "",
        "MOULDING: Overlapping of bones at sutures",
        "   Reduces presenting diameter by up to 1 cm",
    ]
    add_bullets(sl5,bullets5,0.3,1.5,6.0,5.8,font_size=14)
    insert_img(sl5,img_fetal_skull(),6.4,1.5,6.6,5.8)
    print("Slide 5 ✓")

    # ── SLIDE 6: PELVIS ─────────────────────────────────────────────────
    sl6=add_slide(prs,"Female Pelvis — Types, Dimensions & Station",
                  "Creasy & Resnik MFM | Roberts & Hedges")
    bullets6=[
        "PELVIC PLANES & CONJUGATES:",
        "   Obstetric conjugate (inlet):  11 cm",
        "   Transverse diameter (inlet):  13 cm",
        "   Bi-ischial (outlet):          10.5 cm",
        "",
        "STATION (ischial spines = 0):",
        "   -5 to -1: presenting part above spines",
        "    0:        engaged (BPD at pelvic inlet)",
        "   +1 to +5: below spines (crowning = +5)",
        "",
        "ENGAGEMENT:",
        "   BPD through pelvic inlet",
        "   Primipara: 36 weeks gestation",
        "   Multipara: onset of labour",
        "",
        "ASSESSMENT:",
        "   Palpation: 5ths palpable above brim",
        "   <2/5 = engaged",
        "   VE: presenting part at / above spines",
    ]
    add_bullets(sl6,bullets6,0.3,1.5,6.3,5.8,font_size=14)
    insert_img(sl6,img_pelvis_types(),6.5,1.5,6.5,5.8)
    print("Slide 6 ✓")

    # ── SLIDE 7: CARDINAL MOVEMENTS ─────────────────────────────────────
    sl7=add_slide(prs,"Cardinal Movements of Labour (Mechanism)",
                  "Roberts & Hedges Clinical Procedures Fig 56.5 | Tintinalli's EM")
    bullets7=[
        "MECHANISM (Roberts & Hedges):",
        "   The fetus follows path of least resistance",
        "   adapting smallest diameter to birth canal.",
        "",
        "LOA — Most Common Presentation",
        "",
        "8 CARDINAL MOVEMENTS:",
        "   1. ENGAGEMENT  — BPD at pelvic inlet",
        "   2. DESCENT     — continuous throughout",
        "   3. FLEXION     — chin to chest (9.5 cm)",
        "   4. INTERNAL ROTATION — occiput → pubic symphysis",
        "   5. EXTENSION   — head born under arch",
        "   6. RESTITUTION — 45° back to oblique",
        "   7. EXTERNAL ROTATION — shoulders to AP",
        "   8. EXPULSION   — ant. then post. shoulder",
        "",
        "Key muscle: Levator ani (pelvic floor)",
        "aids internal rotation",
    ]
    add_bullets(sl7,bullets7,0.3,1.5,5.5,5.8,font_size=14)
    insert_img(sl7,get_img("cardinal_movements"),5.9,1.5,7.1,5.8)
    add_caption(sl7,"Fig 56.5: Cardinal movements A–H (From Gabbe's Obstetrics). [Roberts & Hedges Clinical Procedures in Emergency Medicine]",5.9,7.28,7.1)
    print("Slide 7 ✓")

    # ── SLIDE 8: MECHANISM DETAIL ───────────────────────────────────────
    sl8=add_slide(prs,"Mechanism in Detail — LOA Step by Step",
                  "Tintinalli's EM | Family Medicine Textbook | Creasy & Resnik")
    bullets8=[
        "1. ENGAGEMENT:",
        "   Head enters left oblique of pelvic inlet",
        "   Sagittal suture in left oblique diameter",
        "",
        "2. DESCENT + FLEXION:",
        "   Passive flexion minimizes presenting diameter",
        "   Suboccipito-bregmatic = 9.5 cm (smallest)",
        "",
        "3. INTERNAL ROTATION:",
        "   Occiput rotates from LT to OA (1/8 turn)",
        "   Sagittal suture → AP diameter of outlet",
        "   Aided by levator ani pelvic floor muscles",
        "",
        "4. EXTENSION:",
        "   Pivot = inferior border of pubic symphysis",
        "   Occiput, bregma, forehead, nose, chin born",
        "",
        "5. RESTITUTION & EXTERNAL ROTATION:",
        "   Head rotates 45° back to oblique (restitution)",
        "   Then shoulders align AP → delivery",
        "",
        "6. EXPULSION:",
        "   Anterior shoulder then posterior shoulder",
    ]
    add_bullets(sl8,bullets8,0.3,1.5,6.3,5.8,font_size=13)

    # Mechanism diagram
    fig_m, axes_m = plt.subplots(1,6,figsize=(10,3.2))
    fig_m.patch.set_facecolor("#f0f4f8")
    steps_m=[
        ("Engagement\n& Descent","#1A3A5C","↓"),
        ("Flexion","#007A87","⤵"),
        ("Internal\nRotation","#F5A623","↻ OA"),
        ("Extension","#27AE60","↑"),
        ("Restitution","#8E44AD","↺"),
        ("Expulsion","#C0392B","✓"),
    ]
    for ax,(title,col,sym) in zip(axes_m,steps_m):
        ax.set_xlim(0,1); ax.set_ylim(0,1); ax.axis("off"); ax.set_facecolor("#f0f4f8")
        ax.add_patch(FancyBboxPatch((0.04,0.04),0.92,0.92,
            boxstyle="round,pad=0.04",fc="white",ec=col,lw=2))
        ax.add_patch(Arc((0.5,-0.1),0.75,0.6,angle=0,theta1=15,theta2=165,
                         color="#8B4513",lw=2.5))
        ax.add_patch(Circle((0.5,0.52),0.18,fc=col,ec="white",lw=1.5,alpha=0.88))
        ax.text(0.5,0.52,sym,ha="center",va="center",fontsize=10,
                fontweight="bold",color="white")
        ax.text(0.5,0.88,title,ha="center",va="top",fontsize=7.5,
                fontweight="bold",color=col,linespacing=1.3)
    fig_m.suptitle("Step-by-step Mechanism — LOA", fontsize=10,
                   fontweight="bold",color="#1A3A5C",y=1.0)
    plt.tight_layout(pad=0.15)
    insert_img(sl8,fig_to_stream(fig_m),6.5,1.5,6.6,3.8)
    insert_img(sl8,get_img("cardinal_movements"),6.5,5.35,6.6,2.05)
    print("Slide 8 ✓")

    # ── SLIDE 9: PARTOGRAPH ─────────────────────────────────────────────
    sl9=add_slide(prs,"Partograph — WHO Labour Monitoring Tool",
                  "Creasy & Resnik MFM | Tintinalli's EM | Family Medicine")
    bullets9=[
        "DEFINITION: Graphical record of labour",
        "  (Philpott & Castle, 1972; WHO modified 1994)",
        "",
        "COMPONENTS:",
        "   1. Cervicograph (dilation vs time)",
        "   2. Alert line — 1 cm/hr from 4 cm",
        "   3. Action line — 4 hrs right of alert",
        "   4. Fetal HR (every 30 min)",
        "   5. Liquor colour, moulding, caput",
        "   6. Contractions per 10 min",
        "   7. Oxytocin/drugs/IV fluids",
        "   8. Maternal BP, pulse, temp, urine",
        "",
        "INTERPRETATION:",
        "   Left of alert = Progress normal",
        "   Alert–Action zone = Increased monitoring",
        "   Beyond action line = Intervention needed",
        "     (augmentation / caesarean section)",
        "",
        "VE: Every 4 hours in active labour",
    ]
    add_bullets(sl9,bullets9,0.3,1.5,5.8,5.8,font_size=13.5)
    insert_img(sl9,img_partograph(),6.2,1.5,7.0,5.8)
    print("Slide 9 ✓")

    # ── SLIDE 10: MANAGEMENT ────────────────────────────────────────────
    sl10=add_slide(prs,"Management of Normal Labour — All Four Stages",
                   "Tintinalli's EM | Creasy & Resnik MFM | Family Medicine")
    bullets10=[
        "ON ADMISSION:",
        "   • History: GA, GBS, antenatal records",
        "   • Examination: fundal height, presentation, FHR",
        "   • VE: dilation, effacement, station, liquor",
        "   • Investigations: CBC, urine, Group & Screen",
        "",
        "GENERAL CARE:",
        "   • Supportive environment, birth partner",
        "   • Oral intake: clear fluids in active labour",
        "   • Ambulation encouraged in 1st stage",
        "   • Bladder care: void every 2 hours",
        "   • IV access if high risk",
        "",
        "PAIN RELIEF OPTIONS:",
        "   • Non-pharmacological: mobility, hydrotherapy",
        "   • Pharmacological: opioids, N₂O, epidural",
        "",
        "FHR AUSCULTATION (Tintinalli):",
        "   Low risk: q30 min (latent) / q15 min (active)",
        "   2nd stage: every 5 minutes",
        "   Higher risk: continuous CTG monitoring",
    ]
    add_bullets(sl10,bullets10,0.3,1.5,6.3,5.8,font_size=13)
    insert_img(sl10,img_management_4cols(),6.6,1.5,6.5,4.5)
    insert_img(sl10,img_amtsl(),6.6,6.05,6.5,1.38)
    print("Slide 10 ✓")

    # ── SLIDE 11: SECOND & THIRD STAGE ──────────────────────────────────
    sl11=add_slide(prs,"Second Stage & Third Stage Management",
                   "Family Medicine Textbook | Tintinalli's EM | Creasy & Resnik")
    bullets11=[
        "SECOND STAGE (Full dilation → Baby):",
        "   • Ferguson reflex: urge to push",
        "   • Duration: 20–50 min multip / nullip",
        "   • Guide pushing with contractions",
        "   • Support perineum — modified Ritgen",
        "   • Delayed cord clamping 1–3 minutes",
        "   • Oxytocin 10 IU IM on anterior shoulder",
        "",
        "THIRD STAGE (Baby → Placenta):",
        "   • Avg blood loss: ~600 mL normal delivery",
        "   • Separation by retroplacental clot",
        "   • Schultze (centre-first, most common)",
        "   • Duncan (edge-first, less common)",
        "",
        "SIGNS OF SEPARATION:",
        "   • Uterus globular, rises above umbilicus",
        "   • Gush of blood at introitus",
        "   • Cord lengthens (Allis sign)",
        "",
        "PLACENTAL INSPECTION:",
        "   Cotyledons complete; 2 arteries, 1 vein",
    ]
    add_bullets(sl11,bullets11,0.3,1.5,6.3,5.8,font_size=13)
    insert_img(sl11,img_amtsl(),6.6,1.5,6.5,3.0)

    # 3rd stage physiology diagram
    fig_p, ax_p = plt.subplots(figsize=(5.8,2.5))
    fig_p.patch.set_facecolor("#f0f4f8"); ax_p.axis("off"); ax_p.set_facecolor("#f0f4f8")
    steps_3=[
        ("Uterine\nContraction","#1A3A5C"),
        ("Retroplacental\nHaematoma","#C0392B"),
        ("Placental\nSeparation","#007A87"),
        ("Cord\nLengthens","#F5A623"),
        ("Expulsion","#27AE60"),
    ]
    for i,(lbl,col) in enumerate(steps_3):
        x=0.03+i*0.194
        ax_p.add_patch(FancyBboxPatch((x,0.15),0.175,0.70,
            boxstyle="round,pad=0.02",fc=col,ec="white",lw=1.5,alpha=0.9,
            transform=ax_p.transAxes))
        ax_p.text(x+0.0875,0.50,lbl,ha="center",va="center",fontsize=7.5,
                  fontweight="bold",color="white",linespacing=1.3,
                  transform=ax_p.transAxes)
        if i<4:
            ax_p.annotate("",xy=(x+0.194,0.50),xytext=(x+0.175,0.50),
                arrowprops=dict(arrowstyle="-|>",color=col,lw=2),
                xycoords="axes fraction",textcoords="axes fraction")
    ax_p.text(0.5,0.95,"Third Stage Sequence",ha="center",va="top",fontsize=10,
              fontweight="bold",color="#1A3A5C",transform=ax_p.transAxes)
    plt.tight_layout(pad=0.1)
    insert_img(sl11,fig_to_stream(fig_p),6.6,4.6,6.5,2.5)
    print("Slide 11 ✓")

    # ── SLIDE 12: COMPLICATIONS + CLINICAL PHOTO ────────────────────────
    sl12=add_slide(prs,"Complications of Labour",
                   "Tintinalli's EM | Creasy & Resnik MFM | Roberts & Hedges")
    bullets12=[
        "MATERNAL:",
        "   Prolonged labour, PPH, uterine rupture",
        "   Perineal tears (1°–4°), infection",
        "   Amniotic fluid embolism, VVF/RVF",
        "",
        "FETAL:",
        "   Birth asphyxia, meconium aspiration",
        "   Shoulder dystocia → Erb's palsy",
        "   Cord prolapse, intracranial haemorrhage",
        "",
        "SHOULDER DYSTOCIA (Tintinalli):",
        "   Impaction of ant. shoulder at pubic symphysis",
        "   after delivery of head",
        "   HELPERR mnemonic:",
        "   H — Call for Help",
        "   E — Episiotomy (if needed)",
        "   L — Legs (McRoberts manoeuvre)",
        "   P — Suprapubic Pressure",
        "   E — Enter (internal manoeuvres)",
        "   R — Remove posterior arm",
        "   R — Roll (all fours position)",
    ]
    add_bullets(sl12,bullets12,0.3,1.5,6.0,5.8,font_size=12.5)
    insert_img(sl12,get_img("shoulder_dystocia"),6.4,1.5,4.5,4.0)
    add_caption(sl12,"Clinical photo: Shoulder dystocia — fetal head impacted at perineum\n[Tintinalli's EM, Fig 101-4, from Atlas of Emergency Medicine]",6.4,5.45,4.5)
    insert_img(sl12,img_complications(),6.4,6.0,6.6,1.45)
    print("Slide 12 ✓")

    # ── SLIDE 13: APGAR SCORE ───────────────────────────────────────────
    sl13=add_slide(prs,"APGAR Score & Immediate Newborn Care",
                   "Tintinalli's EM Table 101-3 | Creasy & Resnik MFM")
    bullets13=[
        "APGAR SCORE (Virginia Apgar, 1952):",
        "   Assessed at 1 minute and 5 minutes",
        "",
        "INTERPRETATION:",
        "   7–10 = Normal (routine care)",
        "   4–6  = Moderate (O₂, stimulation)",
        "   0–3  = Severe (immediate resus)",
        "",
        "IMMEDIATE NEWBORN CARE:",
        "   • Dry and stimulate (first 30 sec)",
        "   • Clear airway if meconium present",
        "   • Warmth — radiant warmer",
        "   • Assess tone, colour, breathing",
        "   • Vitamin K 1 mg IM",
        "   • Eye prophylaxis (erythromycin)",
        "   • Delayed cord clamp 1–3 min",
        "   • Skin-to-skin kangaroo care",
        "   • Breastfeed within first hour",
        "",
        "NEONATAL RESUS (if APGAR < 4):",
        "   PPV → Chest compressions → Adrenaline",
    ]
    add_bullets(sl13,bullets13,0.3,1.5,5.8,5.8,font_size=13)
    insert_img(sl13,img_apgar(),6.2,1.5,6.9,5.8)
    print("Slide 13 ✓")

    # ── SLIDE 14: KEY POINTS SUMMARY ───────────────────────────────────
    sl14=add_slide(prs,"Key Examination Points — Quick Summary",
                   "High-Yield Revision Points")
    insert_img(sl14,img_key_points(),0.25,1.5,12.8,5.8)
    print("Slide 14 ✓")

    # ── SLIDE 15: THANK YOU / REFERENCES ───────────────────────────────
    sl15=prs.slides.add_slide(prs.slide_layouts[6])
    sw,sh=prs.slide_width,prs.slide_height
    bg15=sl15.shapes.add_shape(1,0,0,sw,sh)
    bg15.fill.solid(); bg15.fill.fore_color.rgb=NAVY; bg15.line.fill.background()
    stripe15=sl15.shapes.add_shape(1,0,Inches(4.1),sw,Inches(0.12))
    stripe15.fill.solid(); stripe15.fill.fore_color.rgb=GOLD; stripe15.line.fill.background()

    ty=sl15.shapes.add_textbox(Inches(0.5),Inches(0.7),Inches(12.3),Inches(2.8))
    p_ty=ty.text_frame.paragraphs[0]; p_ty.text="Thank You"
    p_ty.font.size=Pt(54); p_ty.font.bold=True; p_ty.font.color.rgb=WHITE
    p_ty.alignment=PP_ALIGN.CENTER

    tx_sub=sl15.shapes.add_textbox(Inches(0.5),Inches(2.8),Inches(12.3),Inches(0.8))
    p_sub=tx_sub.text_frame.paragraphs[0]
    p_sub.text="Normal Labour — Physiology, Mechanisms & Clinical Management"
    p_sub.font.size=Pt(20); p_sub.font.color.rgb=GOLD; p_sub.font.bold=True
    p_sub.alignment=PP_ALIGN.CENTER

    refs_box=sl15.shapes.add_textbox(Inches(1.5),Inches(4.3),Inches(10.3),Inches(3.0))
    tf_r=refs_box.text_frame; tf_r.word_wrap=True
    ref_lines=[
        "REFERENCES",
        "1.  Creasy RK, Resnik R. Maternal-Fetal Medicine: Principles and Practice, 8e. Elsevier.",
        "2.  Tintinalli JE. Emergency Medicine: A Comprehensive Study Guide, 9e. McGraw-Hill.",
        "3.  Roberts JR, Hedges JR. Clinical Procedures in Emergency Medicine, 6e. Elsevier.",
        "4.  Rakel RE, Rakel DP. Textbook of Family Medicine, 9e. Elsevier.",
        "5.  Morgan GE, Mikhail MS. Clinical Anesthesiology, 7e. McGraw-Hill.",
        "6.  WHO. Intrapartum Care for a Positive Childbirth Experience. WHO Guidelines, 2018.",
    ]
    for i,line in enumerate(ref_lines):
        p=tf_r.paragraphs[0] if i==0 else tf_r.add_paragraph()
        p.text=line; p.font.size=Pt(14 if i==0 else 12)
        p.font.color.rgb=RGBColor(0xCC,0xE0,0xFF); p.font.bold=(i==0); p.space_after=Pt(3)
    print("Slide 15 (Thank You) ✓")

    prs.save(str(PPTX_PATH))
    print(f"\n✅ Saved: {PPTX_PATH}")
    print(f"   Size: {os.path.getsize(PPTX_PATH)/1024:.1f} KB")
    print(f"   Slides: {len(prs.slides)}")

if __name__=="__main__":
    build()
Running Command

cd /tmp/workspace/normal-labour-ppt && python3 build_v2.py 2>&1

Your enhanced presentation is ready:

Normal Labour v2

Presentation · PPTX


What's new in this version (v2) — 15 slides, 4.5 MB

This version uses real images pulled directly from standard medical textbooks in the library:
Real Textbook ImageSourceUsed on Slide
Normal labour curve — cervical dilation + fetal descent with labeled phasesMorgan & Mikhail's Clinical Anesthesiology, 7eSlides 3 & 4
Composite curves — normal + abnormal labour (primigravid & multiparous)Textbook of Family Medicine, 9e, Fig 20-10Slide 4
Cardinal movements A–H — full illustrated sequence (engagement→expulsion)Roberts & Hedges' Clinical Procedures, Fig 56.5 (from Gabbe's Obstetrics)Slides 7 & 8
Shoulder dystocia clinical photo — fetal head impacted at perineumTintinalli's Emergency Medicine, Fig 101-4Slide 12

Slide-by-slide outline

#Topic
1Title — sources listed
2Definition & Stages + True vs False Labour
3Uterine Contractions physiology + textbook labour curve
4Friedman Curve — both textbook figures (normal + abnormal)
5Fetal Skull — sutures, fontanelles, diameters table
6Pelvis types (Caldwell-Moloy) + station
7Cardinal Movements — textbook illustration A–H
8LOA Mechanism step-by-step + cardinal movements
9WHO Partograph with cervicograph + FHR chart
10Management of all 4 stages
112nd & 3rd stage + AMTSL sequence
12Complications + shoulder dystocia clinical photo
13APGAR score table + newborn care
14Key Points summary grid
15Thank You + full references
This is a shared conversation. Sign in to Orris to start your own chat.