increased capillary permeability causes edema

Reading File
Finding Sources
Finding Sources
Reading File
Finding Sources
Reading File
Loading Image
Finding Sources
Reading File
Reading File
Loading Image
I now have comprehensive, well-sourced content to give a thorough answer. Here it is:

Increased Capillary Permeability and Edema

Normal Capillary Fluid Balance (Starling Forces)

Under normal conditions, fluid movement across the capillary wall is governed by the Starling equation:
Q = K × [(Pc - Pi) - σ(πc - πi)]
Where:
  • Q = net fluid flow across the capillary
  • Pc / Pi = capillary and interstitial hydrostatic pressures
  • πc / πi = capillary and interstitial oncotic pressures
  • K = filtration coefficient (related to capillary surface area)
  • σ = reflection coefficient (endothelial permeability to albumin; 1 = impermeable, 0 = freely permeable)
In a healthy state, hydrostatic pressure pushing fluid outward is nearly balanced by oncotic pressure drawing it back in. A small net outward flow (~10-20 mL/h in the lungs) is cleared by lymphatics.
(Morgan and Mikhail's Clinical Anesthesiology, 7e)

How Increased Permeability Causes Edema

The capillary membrane normally does two things:
  1. Keeps fluid inside the intravascular space
  2. Prevents large proteins from equilibrating between the intravascular and interstitial compartments, thereby maintaining the oncotic pressure gradient that opposes fluid efflux
When permeability increases, both fluid AND proteins leak into the interstitium. This is the key distinction from other causes of edema:
  • Proteins leave alongside fluid
  • Interstitial oncotic pressure rises (because proteins accumulate there)
  • This further reduces the gradient drawing fluid back in
  • The result is a self-amplifying net efflux into the interstitial space
The resulting fluid is an exudate - protein-rich (high protein content), with possible cellular debris - in contrast to a transudate, which is protein-poor and results from hydrostatic or osmotic imbalance without increased permeability.
(Robbins & Kumar Basic Pathology)
Exudate vs Transudate - showing how opening of interendothelial spaces leads to protein-rich fluid and protein leakage during inflammation, versus low-protein fluid leakage in transudate states

Cellular Mechanism

The principal mechanism of increased vascular permeability is contraction of endothelial cells, which opens interendothelial gaps. This is triggered by chemical mediators - primarily histamine and bradykinin, as well as leukotrienes. The response is rapid (within 15-30 minutes of mediator exposure) and usually short-lived.
In more severe injury (e.g., burns), direct endothelial cell injury causes immediate, sustained leakage until vessels are thrombosed or repaired.
(Robbins & Kumar Basic Pathology)

Characteristics of the Resulting Edema

Permeability-type edema is distinct because it can be pitting or non-pitting and localized or generalized, depending on the underlying cause - unlike hydrostatic edema (always pitting) or hypoalbuminemia (typically generalized and pitting).
(Frameworks for Internal Medicine)
The overall framework of edema causes is shown below:
Peripheral edema classification by mechanism - hydrostatic pressure (heart failure, renal failure, cirrhosis, DVT), oncotic pressure (liver disease, malnutrition, nephrotic syndrome, lymphedema, myxedema), and permeability (local inflammation, preeclampsia, angioedema, idiopathic edema, capillary leak syndrome)

Causes of Edema from Increased Capillary Permeability

ConditionKey Features
Local inflammationTrauma, infection (cellulitis), insect stings, burns. Localized, nonpitting.
PreeclampsiaEndothelial dysfunction + hypoalbuminemia. Affects lower extremities, may extend to lungs and brain. Resolves after delivery.
AngioedemaLocalized, nondependent, nonpitting; typically face/hands/genitalia. Mediated by histamine (allergic) or bradykinin (ACE inhibitor-induced).
Idiopathic edemaAffects menstruating women in 3rd-4th decade; worsens upright through day; lower limbs, face, hands. Not cycle-related.
Systemic capillary leak syndromeRare, life-threatening. Episodes of rapid generalized edema + hypovolemic shock + hemoconcentration (elevated hematocrit) + hypoalbuminemia. Most have monoclonal gammopathy. Pathogenesis unknown.
(Frameworks for Internal Medicine)

Pulmonary Edema from Increased Permeability

In the lung, increased permeability causes noncardiogenic pulmonary edema (as in ARDS), in contrast to cardiogenic edema from elevated hydrostatic pressure. The edema fluid in permeability edema has a high protein content, while hemodynamic edema fluid has a low protein content - a key distinguishing feature. The reflection coefficient (σ) approaches 0, meaning albumin passes freely and the oncotic gradient that normally limits lung fluid accumulation is lost.
(Morgan and Mikhail's Clinical Anesthesiology; Robbins, Cotran & Kumar Pathologic Basis of Disease)

Key Points Summary

  • Increased capillary permeability causes exudative edema: fluid AND proteins leak into the interstitium
  • The protein leak raises interstitial oncotic pressure, amplifying net outward flow
  • The cellular mechanism is endothelial contraction, opening interendothelial gaps
  • Main chemical mediators: histamine, bradykinin, leukotrienes
  • Can be pitting/non-pitting, localized/generalized
  • Causes include local inflammation, preeclampsia, angioedema, ACE inhibitor use, capillary leak syndrome, and burns
  • Lung equivalent: noncardiogenic (permeability) pulmonary edema with protein-rich fluid

give case scenario for edema for better understanding

Finding Sources
Finding Sources
Finding Sources
Finding Sources
Finding Sources
Reading File
Finding Sources
Finding Sources
I have excellent material for all four major mechanisms. Here are comprehensive clinical case scenarios:

Clinical Case Scenarios: Edema

The four major mechanisms of edema are:
  1. Increased hydrostatic pressure (e.g., heart failure, DVT)
  2. Decreased oncotic pressure (e.g., nephrotic syndrome, cirrhosis)
  3. Increased capillary permeability (e.g., inflammation, angioedema)
  4. Lymphatic obstruction (e.g., lymphedema, myxedema)

CASE 1 - Increased Hydrostatic Pressure

"The Breathless Retired Teacher"

Presentation: Mr. R, 68 years old, retired teacher, presents to the emergency department with worsening breathlessness for 3 weeks, especially when lying flat at night. He has to sleep with 3 pillows (orthopnea). His wife noticed his ankles have been swelling progressively. He had a heart attack 2 years ago and takes enalapril but admits he has been "forgetting" his medications.
Examination:
  • Both ankles and shins: bilateral pitting edema (pressing a finger leaves a dent that fills slowly)
  • Jugular veins visibly distended when sitting upright (elevated JVP)
  • Fine crackles heard at both lung bases
  • S3 gallop on cardiac auscultation
  • Weight gain of 4 kg over 3 weeks
Investigations:
  • BNP: markedly elevated
  • Echocardiogram: ejection fraction 30% (severely reduced)
  • Chest X-ray: cardiomegaly, bilateral basal haziness (pulmonary congestion)
Mechanism - Step by Step:
Damaged left ventricle (post-MI) → poor forward output → blood backs up → left atrial pressure rises → pulmonary venous pressure rises → fluid pushed into lung interstitium (pulmonary edema) → right heart also stressed → systemic venous pressure rises → elevated hydrostatic pressure in peripheral capillaries → fluid forced into interstitial tissues of legs → bilateral pitting edema
Key Teaching Points:
  • Bilateral, symmetric, pitting, and dependent (worse in ankles while standing, shifts to sacrum when lying down)
  • JVP is HIGH (distinguishes this from hypoalbuminemia, where JVP is low)
  • Fluid is a transudate (low protein - pure hydrostatic push, no permeability change)
  • Responds to diuretics (furosemide), which reduce the hydrostatic load
(Symptom to Diagnosis, 4th Edition; Frameworks for Internal Medicine)

CASE 2 - Decreased Oncotic Pressure (Renal)

"The Child with Puffy Eyes"

Presentation: Amara, 6 years old, brought by her mother who noticed her eyes were "puffy" every morning for 2 weeks. The puffiness improves by afternoon but returns the next morning. Over the past week, her ankles have also started swelling, and her abdomen appears distended. No pain, no blood in urine, no fever.
Examination:
  • Periorbital (around the eyes) pitting edema - worse in the morning
  • Bilateral pedal (ankle) pitting edema
  • Mild ascites (fluid in the abdomen)
  • Blood pressure normal
Investigations:
  • Urine dipstick: 4+ protein (massive proteinuria)
  • 24-hour urine protein: 6.5 g/day (normal < 150 mg/day)
  • Serum albumin: 1.8 g/dL (normal: 3.5-5.0 g/dL) - severely low
  • Serum cholesterol: 380 mg/dL (elevated)
  • Kidney biopsy: minimal change disease (podocyte foot process effacement)
Mechanism - Step by Step:
Damaged glomerular filtration barrier → massive protein leak into urine → serum albumin falls dramatically → plasma oncotic pressure drops → the "suction force" that normally pulls fluid back into capillaries is lost → fluid seeps into interstitial tissues → generalized edema → periorbital in children (loose periorbital tissue fills first), then ankles and abdomen
Key Teaching Points:
  • Periorbital edema is the hallmark in children (morning, because lying flat overnight redistributes fluid to the face)
  • Bilateral, generalized, pitting edema
  • JVP is LOW or normal (no venous congestion - this is oncotic, not hydrostatic)
  • Fluid is a transudate (low protein - no permeability problem)
  • The full tetrad: massive proteinuria + hypoalbuminemia + edema + hyperlipidemia = Nephrotic Syndrome
  • Treatment: corticosteroids (for minimal change disease, which responds dramatically)
(Robbins, Cotran & Kumar; Comprehensive Clinical Nephrology, 7th Ed.)

CASE 3 - Decreased Oncotic Pressure (Hepatic)

"The Swollen Abdomen in a Drinker"

Presentation: Mr. S, 52 years old, heavy alcohol use for 20 years. Presents with increasing abdominal girth over 3 months and bilateral leg swelling. He also complains of easy bruising and fatigue. He has noticed yellowing of his eyes.
Examination:
  • Massive ascites (fluid in abdomen - shifting dullness and fluid thrill positive)
  • Bilateral pitting edema of legs
  • Jaundice (yellow sclerae)
  • Spider angiomata on chest
  • Palmar erythema
  • Caput medusae (dilated veins around the navel)
  • JVP: low-normal
Investigations:
  • Serum albumin: 2.1 g/dL (severely low)
  • PT/INR: prolonged (liver not making clotting factors)
  • Bilirubin: elevated
  • Ultrasound: nodular liver (cirrhosis), large volume ascites, splenomegaly
  • SAAG (serum-ascites albumin gradient): > 1.1 g/dL → portal hypertension confirmed
Mechanism - Step by Step (dual mechanism):
Cirrhotic liver → poor albumin synthesis → low oncotic pressure (fails to retain fluid) PLUS → fibrotic liver → obstructs portal blood flow → portal hypertension (increased hydrostatic pressure in abdominal veins) → these two forces combine → fluid pours into the peritoneal cavity (ascites) and leg tissues (edema)
Key Teaching Points:
  • This is a dual mechanism - both reduced oncotic AND increased hydrostatic (portal hypertension)
  • Ascites + leg edema combination is classic for cirrhosis
  • Fluid is a transudate (SAAG > 1.1 confirms portal hypertension)
  • JVP is LOW (not congested heart - rather, splanchnic vasodilation means the effective circulating volume is actually reduced)
  • Treatment: sodium restriction, spironolactone + furosemide, beta-blockers for portal hypertension
(Frameworks for Internal Medicine; Tintinalli's Emergency Medicine)

CASE 4 - Increased Capillary Permeability

"The Sudden Face Swelling After a Tablet"

Presentation: Mr. K, 46 years old, hypertensive, started lisinopril (ACE inhibitor) 2 days ago. Wakes up at 3 AM with sudden swelling of his lips, tongue, and throat. He is anxious, has a muffled voice, and feels like his throat is "closing up." No rash, no itching.
Examination:
  • Grossly swollen lips and tongue
  • Stridor (high-pitched breathing noise) - upper airway compromise
  • No urticaria (no hives - key difference from allergic angioedema)
  • Normal JVP, no peripheral edema of limbs
  • No proteinuria
Mechanism - Step by Step:
ACE inhibitor blocks ACE enzyme → bradykinin cannot be degraded (accumulates) → bradykinin binds B2 receptors on endothelial cells → endothelial cells contract, opening interendothelial gaps → capillary permeability increases in the face/tongue/throat → protein-rich fluid (exudate) floods into the submucosal tissue → rapid localized non-pitting swelling (angioedema)
Key Teaching Points:
  • NON-pitting edema (proteins in the fluid prevent pitting)
  • Localized, not generalized
  • Bradykinin-mediated (NOT histamine) - so antihistamines and steroids have limited effect
  • Treatment: stop ACE inhibitor immediately + fresh frozen plasma (has ACE to break down bradykinin) or icatibant (bradykinin receptor blocker)
  • This is a medical emergency - tongue and throat swelling can obstruct the airway
  • If histamine-mediated angioedema (e.g., bee sting, peanut allergy): urticaria IS present + treat with epinephrine + antihistamines
(Tintinalli's Emergency Medicine; Frameworks for Internal Medicine)

CASE 5 - Increased Capillary Permeability (Inflammatory)

"The Red Hot Swollen Leg"

Presentation: Mrs. P, 55 years old, diabetic, noticed a painful red, hot, swollen area on her right lower leg that started 2 days ago after a minor cut. The swelling has spread rapidly. She has a fever of 38.8°C.
Examination:
  • Right lower leg: warm, erythematous (red), swollen, tender
  • Edema is localized, firm (non-pitting or mildly pitting)
  • Well-defined advancing border of redness
  • Small entry wound visible near the ankle
  • Left leg: completely normal
Investigations:
  • WBC: 16,000 (elevated - infection)
  • CRP: markedly elevated
  • Blood cultures: taken
  • Diagnosis: Cellulitis (bacterial skin infection)
Mechanism - Step by Step:
Bacteria enter via skin wound → tissue infection → immune cells release histamine, bradykinin, prostaglandins, leukotrienes → endothelial cells contract → interendothelial gaps open → massive increase in local capillary permeability → protein-rich exudate floods the interstitium → localized inflammatory edema (the classic signs: rubor, calor, tumor, dolor = redness, heat, swelling, pain)
Key Teaching Points:
  • Localized, unilateral edema - always ask: is this one leg or two? (unilateral = local cause; bilateral = systemic cause)
  • Non-pitting or mildly pitting
  • The exudate is protein-rich - this is why inflammatory edema can feel "firmer"
  • Lymphangitis may develop (red streaks tracking up the limb along lymphatics)
  • Treatment: antibiotics; the edema resolves as infection is controlled
(Robbins & Kumar Basic Pathology; Frameworks for Internal Medicine)

CASE 6 - Lymphatic Obstruction

"The Swollen Leg After Cancer Treatment"

Presentation: Ms. T, 48 years old, 18 months after left axillary lymph node dissection + radiotherapy for breast cancer. She reports progressive swelling of her left arm over the past 6 months. It is not painful. The skin has become thickened and the arm feels heavy.
Examination:
  • Left arm: non-pitting, firm/hard edema
  • Skin texture: thickened and indurated ("peau d'orange" appearance)
  • Stemmer's sign positive (cannot pinch a fold of skin on the dorsum of the fingers)
  • Right arm: completely normal
Mechanism - Step by Step:
Surgery + radiotherapy destroys/obstructs lymphatic channels → lymphatic fluid cannot drain from tissues → protein-rich interstitial fluid accumulates (lymph is normally protein-rich) → proteins draw in more water → progressive tissue swelling → over time, chronic protein accumulation triggers fibroblast activation → tissue fibrosis and skin thickening (irreversible if untreated)
Key Teaching Points:
  • Non-pitting (the protein-rich fluid and fibrosis prevent indentation)
  • Proteins in interstitium are the key - unlike hydrostatic or oncotic transudates
  • No response to diuretics (there is no excess hydrostatic pressure to reduce)
  • Treatment: compression garments, manual lymphatic drainage (physiotherapy)
  • Other causes of lymphedema: filariasis (parasitic infection - most common worldwide), malignant infiltration of nodes
(Frameworks for Internal Medicine)

Quick Comparison Table

FeatureCardiac (↑Hydrostatic)Nephrotic/Cirrhosis (↓Oncotic)Inflammatory (↑Permeability)Lymphatic
DistributionBilateral, dependentBilateral, generalizedLocalized (or generalized)Unilateral or regional
Pitting?YesYesVariable (often no)No
JVPHIGHLow/normalNormalNormal
Fluid typeTransudate (low protein)Transudate (low protein)Exudate (high protein)High protein lymph
Key lab↑BNP, ↓EF↓Albumin, ↑Proteinuria↑CRP, ↑WBC-
Response to diureticsGoodModeratePoorNone
Classic causeHeart failureNephrotic syndromeCellulitis, angioedemaPost-surgical, filariasis

The One Rule That Ties It All Together

"Edema = fluid trapped in the interstitium because the balance of Starling forces is disrupted." Either too much pressure pushing fluid out (hydrostatic), too little force pulling it back in (oncotic), the wall becomes leaky (permeability), or the drainage system is blocked (lymphatics).
Every case scenario above is simply a different way that nature breaks this balance.

Create a diagnostic flowchart for approaching edema

Finding Sources
Finding Sources
Finding Sources
Reading File
Reading File
Reading File
Reading File
Running Command

mkdir -p /tmp/workspace/edema-flowchart && ls /tmp/workspace/

Writing File

~/edema-flowchart/build_flowchart.py

import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from matplotlib.patches import FancyBboxPatch, FancyArrowPatch

fig, ax = plt.subplots(figsize=(22, 28))
ax.set_xlim(0, 22)
ax.set_ylim(0, 28)
ax.axis('off')
fig.patch.set_facecolor('#F8F9FA')

# ── Color palette ──────────────────────────────────────────────
C_TITLE   = '#1A237E'   # deep navy
C_START   = '#1565C0'   # dark blue  – start box
C_Q       = '#0288D1'   # mid blue   – question diamond
C_HYDRO   = '#1976D2'   # blue       – hydrostatic branch
C_ONCO    = '#7B1FA2'   # purple     – oncotic branch
C_PERM    = '#C62828'   # red        – permeability branch
C_LYMPH   = '#2E7D32'   # green      – lymphatic branch
C_DX      = '#E65100'   # orange     – diagnosis boxes
C_KEY     = '#37474F'   # slate      – key points box
C_ARROW   = '#455A64'
WHITE     = '#FFFFFF'
LIGHT_GREY= '#ECEFF1'

def draw_box(ax, x, y, w, h, text, facecolor, textcolor=WHITE,
             fontsize=9, bold=False, radius=0.3, alpha=1.0, valign='center'):
    box = FancyBboxPatch((x - w/2, y - h/2), w, h,
                         boxstyle=f"round,pad=0.05,rounding_size={radius}",
                         facecolor=facecolor, edgecolor='white',
                         linewidth=1.5, zorder=3, alpha=alpha)
    ax.add_patch(box)
    weight = 'bold' if bold else 'normal'
    ax.text(x, y, text, ha='center', va=valign, fontsize=fontsize,
            color=textcolor, weight=weight, zorder=4,
            wrap=True, multialignment='center',
            transform=ax.transData)

def draw_diamond(ax, x, y, w, h, text, facecolor, textcolor=WHITE, fontsize=9):
    dx, dy = w/2, h/2
    diamond = plt.Polygon([[x, y+dy],[x+dx, y],[x, y-dy],[x-dx, y]],
                           closed=True, facecolor=facecolor,
                           edgecolor='white', linewidth=1.5, zorder=3)
    ax.add_patch(diamond)
    ax.text(x, y, text, ha='center', va='center', fontsize=fontsize,
            color=textcolor, weight='bold', zorder=4, multialignment='center')

def arrow(ax, x1, y1, x2, y2, label='', color=C_ARROW, lw=1.8):
    ax.annotate('', xy=(x2, y2), xytext=(x1, y1),
                arrowprops=dict(arrowstyle='->', color=color,
                                lw=lw, connectionstyle='arc3,rad=0.0'),
                zorder=2)
    if label:
        mx, my = (x1+x2)/2, (y1+y2)/2
        ax.text(mx+0.15, my, label, fontsize=8, color=color,
                weight='bold', va='center', zorder=5)

def darrow(ax, x1, y1, x2, y2, label='', color=C_ARROW, lw=1.8, rad=0.0):
    ax.annotate('', xy=(x2, y2), xytext=(x1, y1),
                arrowprops=dict(arrowstyle='->', color=color, lw=lw,
                                connectionstyle=f'arc3,rad={rad}'),
                zorder=2)
    if label:
        mx, my = (x1+x2)/2, (y1+y2)/2
        ax.text(mx, my+0.18, label, fontsize=8, color=color,
                weight='bold', ha='center', zorder=5)

# ═══════════════════════════════════════════════════════════════
# TITLE
# ═══════════════════════════════════════════════════════════════
ax.text(11, 27.4, 'DIAGNOSTIC APPROACH TO EDEMA', ha='center', va='center',
        fontsize=17, color=WHITE, weight='bold', zorder=5)
title_box = FancyBboxPatch((1.5, 26.9), 19, 1.0,
                            boxstyle="round,pad=0.1,rounding_size=0.4",
                            facecolor=C_TITLE, edgecolor='none', zorder=3)
ax.add_patch(title_box)
ax.text(11, 27.4, 'DIAGNOSTIC APPROACH TO EDEMA', ha='center', va='center',
        fontsize=17, color=WHITE, weight='bold', zorder=5)

# ═══════════════════════════════════════════════════════════════
# STEP 1 – START
# ═══════════════════════════════════════════════════════════════
draw_box(ax, 11, 26.0, 7, 0.65,
         'Patient presents with EDEMA\n(excess fluid in interstitial space)',
         C_START, fontsize=10, bold=True)

arrow(ax, 11, 25.67, 11, 25.1)

# ═══════════════════════════════════════════════════════════════
# STEP 2 – Q1: Unilateral or Bilateral?
# ═══════════════════════════════════════════════════════════════
draw_diamond(ax, 11, 24.55, 7.5, 0.95,
             'STEP 1:  Unilateral  or  Bilateral?', C_Q, fontsize=10)

# ── UNILATERAL → LEFT ──────────────────────────────────────────
arrow(ax, 7.25, 24.55, 4.8, 24.55, label='Unilateral', color=C_HYDRO)

draw_box(ax, 3.2, 24.55, 3.0, 0.65,
         'UNILATERAL EDEMA\n(Local cause)', C_HYDRO, fontsize=9, bold=True)

arrow(ax, 3.2, 24.22, 3.2, 23.55)

draw_box(ax, 3.2, 23.2, 3.2, 0.65,
         'Pitting? Pain? Erythema?\nWarmth?', '#0D47A1', fontsize=8.5)

# Sub-branches from unilateral
arrow(ax, 1.6, 23.2, 1.1, 22.45)
draw_box(ax, 1.1, 22.1, 2.0, 0.65,
         'DVT\n(+Doppler US)', C_DX, fontsize=8.5, bold=True)

arrow(ax, 3.2, 22.87, 3.2, 22.45)
draw_box(ax, 3.2, 22.1, 2.0, 0.65,
         'Cellulitis /\nInflammation', C_PERM, fontsize=8.5, bold=True)

arrow(ax, 4.8, 23.2, 5.3, 22.45)
draw_box(ax, 5.3, 22.1, 2.0, 0.65,
         'Lymphedema /\nVenous Insuff.', C_LYMPH, fontsize=8.5, bold=True)

# ── BILATERAL → DOWN ───────────────────────────────────────────
arrow(ax, 11, 24.08, 11, 23.45, label='Bilateral', color=C_Q)

# ═══════════════════════════════════════════════════════════════
# STEP 3 – Q2: Pitting?
# ═══════════════════════════════════════════════════════════════
draw_diamond(ax, 11, 22.95, 6.5, 0.9,
             'STEP 2:  Pitting  or  Non-Pitting?', C_Q, fontsize=10)

# ── NON-PITTING → RIGHT ────────────────────────────────────────
arrow(ax, 14.25, 22.95, 17.5, 22.95, label='Non-pitting', color=C_LYMPH)

draw_box(ax, 19.2, 22.95, 3.2, 0.65,
         'NON-PITTING EDEMA', C_LYMPH, fontsize=9, bold=True)

arrow(ax, 19.2, 22.62, 19.2, 22.05)
draw_box(ax, 19.2, 21.7, 3.2, 0.65,
         'Lymphedema\n(post-surgical, filariasis)', C_LYMPH, fontsize=8.5, bold=True)
arrow(ax, 19.2, 21.37, 19.2, 20.8)
draw_box(ax, 19.2, 20.45, 3.2, 0.65,
         'Myxedema\n(hypothyroidism)', C_LYMPH, fontsize=8.5, bold=True)
arrow(ax, 19.2, 20.12, 19.2, 19.55)
draw_box(ax, 19.2, 19.2, 3.2, 0.65,
         'Angioedema\n(face, lips, tongue)', C_PERM, fontsize=8.5, bold=True)

# ── PITTING → DOWN ─────────────────────────────────────────────
arrow(ax, 11, 22.5, 11, 21.85, label='Pitting', color=C_Q)

# ═══════════════════════════════════════════════════════════════
# STEP 4 – Q3: JVP?
# ═══════════════════════════════════════════════════════════════
draw_diamond(ax, 11, 21.35, 7.0, 0.9,
             'STEP 3:  Jugular Venous Pressure (JVP)?', C_Q, fontsize=10)

# ── HIGH JVP → LEFT ────────────────────────────────────────────
arrow(ax, 7.5, 21.35, 5.0, 21.35, label='High', color=C_HYDRO)

draw_box(ax, 3.4, 21.35, 3.0, 0.65,
         '↑ Hydrostatic Pressure\n(Venous congestion)', C_HYDRO, fontsize=9, bold=True)

arrow(ax, 3.4, 21.02, 3.4, 20.4)
draw_box(ax, 3.4, 20.05, 3.2, 0.65,
         'Check: ECG, Echo,\nBNP, Chest X-ray', '#0D47A1', fontsize=8.5)

arrow(ax, 3.4, 19.72, 3.4, 19.15)
draw_box(ax, 1.9, 18.8, 1.9, 0.6,
         'Heart Failure\n(↓EF, S3, crackles)', C_DX, fontsize=8, bold=True)
arrow(ax, 3.4, 19.72, 3.4, 19.15)
draw_box(ax, 3.9, 18.8, 1.9, 0.6,
         'Renal Failure\n(↑Cr, ↓GFR)', C_DX, fontsize=8, bold=True)
draw_box(ax, 5.6, 18.8, 1.7, 0.6,
         'Constrictive\nPericarditis', C_DX, fontsize=8, bold=True)

# ── LOW/NORMAL JVP → DOWN ──────────────────────────────────────
arrow(ax, 11, 20.9, 11, 20.3, label='Low/Normal', color=C_Q)

# ═══════════════════════════════════════════════════════════════
# STEP 5 – Q4: Albumin / Proteinuria
# ═══════════════════════════════════════════════════════════════
draw_diamond(ax, 11, 19.75, 8.0, 0.95,
             'STEP 4:  Check Serum Albumin + Urine Protein', C_Q, fontsize=10)

# ── LOW ALBUMIN → LEFT ─────────────────────────────────────────
arrow(ax, 7.0, 19.75, 5.2, 19.75, label='↓Albumin', color=C_ONCO)

draw_box(ax, 3.5, 19.75, 3.0, 0.65,
         '↓ Oncotic Pressure\n(Hypoalbuminemia)', C_ONCO, fontsize=9, bold=True)

arrow(ax, 3.5, 19.42, 3.5, 18.75)
draw_box(ax, 3.5, 18.45, 3.2, 0.55,
         'Proteinuria present?', '#4A148C', fontsize=8.5)

arrow(ax, 2.0, 18.45, 1.1, 17.9, label='YES', color=C_ONCO)
draw_box(ax, 1.1, 17.6, 1.9, 0.55,
         'Nephrotic\nSyndrome', C_DX, fontsize=8, bold=True)

arrow(ax, 3.5, 18.17, 3.5, 17.6)
draw_box(ax, 3.5, 17.3, 1.9, 0.55,
         'Liver Disease /\nMalnutrition', C_DX, fontsize=8, bold=True)

arrow(ax, 5.0, 18.45, 5.9, 17.9, label='No proteinuria', color=C_ONCO)
draw_box(ax, 5.9, 17.6, 2.2, 0.55,
         'Protein-losing\nEnteropathy', C_DX, fontsize=8, bold=True)

# ── NORMAL ALBUMIN → DOWN ──────────────────────────────────────
arrow(ax, 11, 19.27, 11, 18.65, label='Normal albumin', color=C_Q)

# ═══════════════════════════════════════════════════════════════
# STEP 6 – Q5: Inflammatory signs?
# ═══════════════════════════════════════════════════════════════
draw_diamond(ax, 11, 18.15, 8.5, 0.9,
             'STEP 5:  Inflammatory signs?\n(Redness, Heat, Pain, Fever, Trigger?)', C_Q, fontsize=9.5)

# ── YES → RIGHT ────────────────────────────────────────────────
arrow(ax, 15.25, 18.15, 16.8, 18.15, label='YES', color=C_PERM)

draw_box(ax, 18.3, 18.15, 2.9, 0.65,
         '↑ Capillary Permeability', C_PERM, fontsize=9, bold=True)

arrow(ax, 18.3, 17.82, 18.3, 17.25)
draw_box(ax, 18.3, 16.9, 3.0, 0.65,
         'Local inflammation /\nCellulitis / Burn', C_DX, fontsize=8.5, bold=True)
arrow(ax, 18.3, 16.57, 18.3, 16.0)
draw_box(ax, 18.3, 15.65, 3.0, 0.65,
         'Angioedema\n(ACE-i / Allergy)', C_DX, fontsize=8.5, bold=True)
arrow(ax, 18.3, 15.32, 18.3, 14.75)
draw_box(ax, 18.3, 14.4, 3.0, 0.65,
         'Preeclampsia\n(Pregnant + HTN + Protein)', C_DX, fontsize=8.5, bold=True)
arrow(ax, 18.3, 14.07, 18.3, 13.5)
draw_box(ax, 18.3, 13.15, 3.0, 0.65,
         'Capillary Leak Syndrome\n(Hemoconcentration)', C_DX, fontsize=8.5, bold=True)

# ── NO → DOWN ──────────────────────────────────────────────────
arrow(ax, 11, 17.7, 11, 17.1, label='NO', color=C_Q)

# ═══════════════════════════════════════════════════════════════
# STEP 7 – Consider other/medication causes
# ═══════════════════════════════════════════════════════════════
draw_box(ax, 11, 16.7, 8.0, 0.65,
         'STEP 6:  Review Medications & Special Contexts', '#455A64',
         fontsize=9.5, bold=True)

arrow(ax, 11, 16.37, 11, 15.77)

# Three sub-boxes
draw_box(ax, 7.8, 15.45, 3.5, 0.55,
         'CCBs, Steroids,\nNSAIDs, Thiazolidinediones', '#546E7A', fontsize=8.5)
draw_box(ax, 11.8, 15.45, 3.0, 0.55,
         'Pregnancy\n(physiologic)', '#546E7A', fontsize=8.5)
draw_box(ax, 15.2, 15.45, 2.5, 0.55,
         'Idiopathic\n(women 30–40s)', '#546E7A', fontsize=8.5)

# Horizontal connector at y=15.77
ax.plot([7.8, 15.2], [15.77, 15.77], color=C_ARROW, lw=1.5, zorder=2)
arrow(ax, 7.8, 15.77, 7.8, 15.72)
arrow(ax, 11.8, 15.77, 11.8, 15.72)
arrow(ax, 15.2, 15.77, 15.2, 15.72)

# ═══════════════════════════════════════════════════════════════
# KEY POINTS BOX
# ═══════════════════════════════════════════════════════════════
key_box = FancyBboxPatch((0.8, 12.3), 20.4, 2.7,
                          boxstyle="round,pad=0.1,rounding_size=0.35",
                          facecolor='#E3F2FD', edgecolor=C_TITLE,
                          linewidth=2.0, zorder=3)
ax.add_patch(key_box)

ax.text(11, 14.75, 'KEY DIFFERENTIATING FEATURES AT A GLANCE',
        ha='center', va='center', fontsize=10, color=C_TITLE,
        weight='bold', zorder=5)

# Table header
headers = ['Feature', '↑ Hydrostatic', '↓ Oncotic', '↑ Permeability', 'Lymphatic']
cols     = [2.2, 5.5, 8.8, 12.1, 15.4]
hdr_y    = 14.3
for i, (h, x) in enumerate(zip(headers, cols)):
    fc = C_TITLE if i == 0 else [C_HYDRO, C_ONCO, C_PERM, C_LYMPH][i-1] if i > 0 else '#37474F'
    ax.text(x, hdr_y, h, ha='center', va='center',
            fontsize=8.5, color=fc, weight='bold', zorder=5)

rows = [
    ('Distribution',    'Bilateral dependent', 'Generalized', 'Local or general', 'Unilateral/regional'),
    ('Pitting',         'Yes',                 'Yes',          'Variable (often No)', 'No'),
    ('JVP',             'HIGH ↑',              'Low/Normal',   'Normal',           'Normal'),
    ('Fluid type',      'Transudate (low prot)', 'Transudate', 'Exudate (high prot)', 'High-protein lymph'),
    ('Key lab',         '↑BNP, ↓EF',          '↓Albumin, ↑Prot-uria', '↑CRP, ↑WBC', 'Clinical / US'),
]
row_colors = ['#EEF2FF', '#F3E5F5', '#FFEBEE', '#E8F5E9']
for ri, row in enumerate(rows):
    ry = hdr_y - 0.34*(ri+1)
    for ci, (val, x) in enumerate(zip(row, cols)):
        color = ['#263238', C_HYDRO, C_ONCO, C_PERM, C_LYMPH][ci]
        ax.text(x, ry, val, ha='center', va='center',
                fontsize=7.8, color=color, zorder=5)

# ═══════════════════════════════════════════════════════════════
# LEGEND
# ═══════════════════════════════════════════════════════════════
legend_items = [
    (C_Q,    'Decision step'),
    (C_HYDRO,'↑ Hydrostatic pressure'),
    (C_ONCO, '↓ Oncotic pressure'),
    (C_PERM, '↑ Capillary permeability'),
    (C_LYMPH,'Lymphatic obstruction'),
    (C_DX,   'Diagnosis'),
]
lx, ly = 1.0, 12.0
ax.text(lx, ly, 'LEGEND:', fontsize=8.5, color=C_KEY, weight='bold')
for i, (col, label) in enumerate(legend_items):
    patch = mpatches.Patch(color=col, label=label)
    rx = lx + i*3.5
    rect = FancyBboxPatch((rx, ly-0.45), 0.55, 0.32,
                           boxstyle="round,pad=0.03",
                           facecolor=col, edgecolor='none', zorder=4)
    ax.add_patch(rect)
    ax.text(rx + 0.65, ly - 0.29, label, fontsize=7.8, color=C_KEY, va='center', zorder=5)

# ═══════════════════════════════════════════════════════════════
# SOURCE NOTE
# ═══════════════════════════════════════════════════════════════
ax.text(11, 11.6,
        'Sources: Frameworks for Internal Medicine | Robbins & Kumar Basic Pathology | '
        'Morgan & Mikhail\'s Clinical Anesthesiology',
        ha='center', va='center', fontsize=7.5, color='#78909C', style='italic', zorder=5)

plt.tight_layout(pad=0.3)
plt.savefig('/tmp/workspace/edema-flowchart/edema_diagnostic_flowchart.png',
            dpi=180, bbox_inches='tight', facecolor=fig.get_facecolor())
print("Saved successfully.")
Running Command

cd /tmp/workspace/edema-flowchart && python build_flowchart.py 2>&1

Here is your diagnostic flowchart:

edema diagnostic flowchart

Image · PNG


How to Read the Flowchart

The flowchart walks through 6 sequential clinical questions that systematically narrow the cause of edema:

Step 1 - Unilateral or Bilateral?

This is the single most important first question.
  • Unilateral = always a local cause (DVT, cellulitis, venous insufficiency, lymphedema)
  • Bilateral = almost always a systemic cause - continue down the flowchart

Step 2 - Pitting or Non-Pitting?

Press firmly for 5 seconds over the shin/ankle.
  • Pitting = fluid can be displaced (typical of hydrostatic or oncotic edema)
  • Non-pitting = proteins or fibrosis prevent displacement (lymphedema, myxedema, angioedema)

Step 3 - JVP (Jugular Venous Pressure)?

This separates the two most common systemic causes immediately.
  • High JVP = venous congestion = increased hydrostatic pressure → think heart failure, renal failure, constrictive pericarditis
  • Low/normal JVP = no venous backup → the problem is not excess hydrostatic pressure

Step 4 - Serum Albumin + Urine Protein?

  • Low albumin + massive proteinuriaNephrotic syndrome
  • Low albumin, no proteinuriaLiver disease / Malnutrition / Protein-losing enteropathy
  • Normal albumin → not an oncotic problem → continue

Step 5 - Inflammatory Signs?

(Redness, warmth, pain, fever, recent drug/trigger exposure)
  • YES → increased capillary permeability → Cellulitis, angioedema (ACE inhibitor / allergy), preeclampsia, capillary leak syndrome
  • NO → consider medications and special contexts

Step 6 - Medications & Special Contexts

Many drugs cause edema as a side effect:
  • Calcium channel blockers (amlodipine)
  • Corticosteroids
  • NSAIDs
  • Thiazolidinediones (diabetes medications)
  • Also consider: physiologic pregnancy edema, idiopathic edema (women 30-40s)

Quick Reference Table (bottom of chart)

Feature↑ Hydrostatic↓ Oncotic↑ PermeabilityLymphatic
DistributionBilateral dependentGeneralizedLocal or generalUnilateral/regional
PittingYesYesVariable (often No)No
JVPHIGH ↑Low/NormalNormalNormal
Fluid typeTransudateTransudateExudateHigh-protein lymph
Key lab↑BNP, ↓EF↓Albumin, ↑Proteinuria↑CRP, ↑WBCClinical/US
(Sources: Frameworks for Internal Medicine; Robbins & Kumar Basic Pathology; Morgan & Mikhail's Clinical Anesthesiology)

make a presentation on edema etiology/pathophysiology

Reading File
Reading File
Reading File
Running Command

ls /tmp/workspace/edema-flowchart/

Writing File

~/edema-flowchart/build_pptx.py

"""
Edema Etiology & Pathophysiology — PowerPoint Presentation
Built with python-pptx  |  16 slides
"""

from pptx import Presentation
from pptx.util import Inches, Pt, Emu
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN, MSO_ANCHOR
from pptx.enum.shapes import MSO_SHAPE, MSO_CONNECTOR
from pptx.oxml.ns import qn
from lxml import etree
from io import BytesIO
import copy

# ─────────────────────────────────────────────
# PALETTE
# ─────────────────────────────────────────────
NAVY      = RGBColor(0x0D, 0x2B, 0x5E)
BLUE      = RGBColor(0x15, 0x65, 0xC0)
LIGHTBLUE = RGBColor(0xBB, 0xDE, 0xFB)
PURPLE    = RGBColor(0x6A, 0x1B, 0x9A)
LPURPLE   = RGBColor(0xE1, 0xBE, 0xE7)
RED       = RGBColor(0xB7, 0x1C, 0x1C)
LRED      = RGBColor(0xFF, 0xCD, 0xD2)
GREEN     = RGBColor(0x1B, 0x5E, 0x20)
LGREEN    = RGBColor(0xC8, 0xE6, 0xC9)
ORANGE    = RGBColor(0xE6, 0x51, 0x00)
LORANGE   = RGBColor(0xFF, 0xE0, 0xB2)
TEAL      = RGBColor(0x00, 0x69, 0x64)
LTEAL     = RGBColor(0xB2, 0xDF, 0xDB)
WHITE     = RGBColor(0xFF, 0xFF, 0xFF)
OFFWHITE  = RGBColor(0xF5, 0xF7, 0xFA)
DARKGREY  = RGBColor(0x37, 0x47, 0x4F)
MIDGREY   = RGBColor(0x78, 0x90, 0x9C)
LGREY     = RGBColor(0xEC, 0xEF, 0xF1)

W = 13.333
H = 7.5

prs = Presentation()
prs.slide_width  = Inches(W)
prs.slide_height = Inches(H)
blank_layout   = prs.slide_layouts[6]   # blank
content_layout = prs.slide_layouts[1]   # title + content


# ─────────────────────────────────────────────
# HELPERS
# ─────────────────────────────────────────────
def bg(slide, color):
    bg_shape = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE,
        Inches(0), Inches(0), Inches(W), Inches(H))
    bg_shape.fill.solid()
    bg_shape.fill.fore_color.rgb = color
    bg_shape.line.fill.background()
    return bg_shape

def rect(slide, x, y, w, h, fill, line_color=None, line_w=None, radius=False):
    shape_type = MSO_SHAPE.ROUNDED_RECTANGLE if radius else MSO_SHAPE.RECTANGLE
    shp = slide.shapes.add_shape(shape_type, Inches(x), Inches(y), Inches(w), Inches(h))
    shp.fill.solid()
    shp.fill.fore_color.rgb = fill
    if line_color:
        shp.line.color.rgb = line_color
        if line_w:
            shp.line.width = Pt(line_w)
    else:
        shp.line.fill.background()
    shp.shadow.inherit = False
    return shp

def textbox(slide, x, y, w, h, text, size, color, bold=False,
            align=PP_ALIGN.LEFT, italic=False, font='Calibri',
            v_anchor=MSO_ANCHOR.TOP, wrap=True):
    tb = slide.shapes.add_textbox(Inches(x), Inches(y), Inches(w), Inches(h))
    tf = tb.text_frame
    tf.word_wrap = wrap
    tf.vertical_anchor = v_anchor
    tf.margin_left = tf.margin_right = tf.margin_top = tf.margin_bottom = 0
    p = tf.paragraphs[0]
    p.alignment = align
    r = p.add_run()
    r.text = text
    r.font.name = font
    r.font.size = Pt(size)
    r.font.bold = bold
    r.font.italic = italic
    r.font.color.rgb = color
    return tb

def add_paragraph(tf, text, size, color, bold=False, italic=False,
                  align=PP_ALIGN.LEFT, level=0, font='Calibri', space_before=None):
    p = tf.add_paragraph()
    p.alignment = align
    p.level = level
    if space_before:
        p.space_before = Pt(space_before)
    r = p.add_run()
    r.text = text
    r.font.name = font
    r.font.size = Pt(size)
    r.font.bold = bold
    r.font.italic = italic
    r.font.color.rgb = color
    return p

def shape_text(shp, text, size, color, bold=False, align=PP_ALIGN.CENTER,
               v_anchor=MSO_ANCHOR.MIDDLE, font='Calibri'):
    tf = shp.text_frame
    tf.word_wrap = True
    tf.vertical_anchor = v_anchor
    tf.margin_left = Pt(4)
    tf.margin_right = Pt(4)
    tf.margin_top = Pt(2)
    tf.margin_bottom = Pt(2)
    p = tf.paragraphs[0]
    p.alignment = align
    r = p.add_run()
    r.text = text
    r.font.name = font
    r.font.size = Pt(size)
    r.font.bold = bold
    r.font.color.rgb = color

def accent_bar(slide, color, y=0.82, h=0.07):
    bar = rect(slide, 0, y, W, h, color)
    return bar

def slide_header(slide, title, subtitle=None,
                 title_color=WHITE, bar_color=NAVY, bg_color=OFFWHITE):
    bg(slide, bg_color)
    rect(slide, 0, 0, W, 1.15, bar_color)
    accent_bar(slide, BLUE, y=1.15, h=0.06)
    textbox(slide, 0.35, 0.12, 12.5, 0.75,
            title, 26, WHITE, bold=True, align=PP_ALIGN.LEFT, font='Calibri')
    if subtitle:
        textbox(slide, 0.35, 0.82, 12.5, 0.36,
                subtitle, 13, LIGHTBLUE, bold=False, align=PP_ALIGN.LEFT, font='Calibri')

def bullet_card(slide, x, y, w, h, header, bullets, hdr_color, hdr_bg,
                bullet_size=11, line_spacing=1.2):
    card = rect(slide, x, y, w, h, WHITE, line_color=hdr_bg, line_w=1.2, radius=True)
    hdr  = rect(slide, x, y, w, 0.42, hdr_bg, radius=True)
    textbox(slide, x+0.12, y+0.04, w-0.2, 0.38,
            header, 12, hdr_color, bold=True, font='Calibri')
    tb = slide.shapes.add_textbox(
        Inches(x+0.12), Inches(y+0.5), Inches(w-0.24), Inches(h-0.58))
    tf = tb.text_frame
    tf.word_wrap = True
    first = True
    for b in bullets:
        if first:
            p = tf.paragraphs[0]; first = False
        else:
            p = tf.add_paragraph()
        p.alignment = PP_ALIGN.LEFT
        r = p.add_run()
        r.text = b
        r.font.name = 'Calibri'
        r.font.size = Pt(bullet_size)
        r.font.color.rgb = DARKGREY
    return card

def connector(slide, x1, y1, x2, y2, color=MIDGREY, lw=1.5):
    ln = slide.shapes.add_connector(MSO_CONNECTOR.STRAIGHT,
        Inches(x1), Inches(y1), Inches(x2), Inches(y2))
    ln.line.color.rgb = color
    ln.line.width = Pt(lw)

def pill(slide, x, y, w, h, text, fill, text_color=WHITE, size=10.5, bold=True):
    shp = rect(slide, x, y, w, h, fill, radius=True)
    shape_text(shp, text, size, text_color, bold=bold)
    return shp

def divider(slide, y, color=LGREY):
    connector(slide, 0.35, y, W-0.35, y, color=color, lw=0.8)

# ═════════════════════════════════════════════════════════════════
# SLIDE 1 – TITLE
# ═════════════════════════════════════════════════════════════════
s1 = prs.slides.add_slide(blank_layout)
bg(s1, NAVY)
# Gradient overlay strip
rect(s1, 0, 0, 5.5, H, BLUE)
rect(s1, 0, 5.8, W, 1.7, RGBColor(0x0A, 0x1F, 0x4A))

# Decorative circles
for cx, cy, cr, alpha_color in [
    (1.8, 1.8, 1.6, RGBColor(0x1A, 0x4A, 0x8A)),
    (3.5, 5.5, 0.9, RGBColor(0x1A, 0x4A, 0x8A)),
    (4.8, 2.8, 0.55, RGBColor(0x0D, 0x3A, 0x7A)),
]:
    circ = s1.shapes.add_shape(MSO_SHAPE.OVAL,
        Inches(cx-cr/2), Inches(cy-cr/2), Inches(cr), Inches(cr))
    circ.fill.solid(); circ.fill.fore_color.rgb = alpha_color
    circ.line.fill.background(); circ.shadow.inherit = False

# Title text
textbox(s1, 5.8, 1.4, 7.1, 1.1,
        'EDEMA', 54, WHITE, bold=True, align=PP_ALIGN.LEFT, font='Calibri')
textbox(s1, 5.8, 2.45, 7.1, 0.7,
        'Etiology & Pathophysiology', 26, LIGHTBLUE, bold=False,
        align=PP_ALIGN.LEFT, font='Calibri')

accent_bar(s1, ORANGE, y=3.2, h=0.055)

textbox(s1, 5.8, 3.35, 7.0, 0.4,
        'Understanding the Starling Forces, Mechanisms & Clinical Causes',
        13, MIDGREY, align=PP_ALIGN.LEFT, font='Calibri')

# Bottom tags
for i, tag in enumerate(['Pathophysiology', 'Internal Medicine', 'Clinical Sciences']):
    pill(s1, 5.8 + i*2.42, 5.95, 2.2, 0.42, tag, BLUE, size=10)

textbox(s1, 5.8, 6.6, 7.0, 0.5,
        'Sources: Frameworks for Internal Medicine | Robbins & Kumar | Morgan & Mikhail',
        9, MIDGREY, align=PP_ALIGN.LEFT, font='Calibri', italic=True)

# ═════════════════════════════════════════════════════════════════
# SLIDE 2 – OVERVIEW / OUTLINE
# ═════════════════════════════════════════════════════════════════
s2 = prs.slides.add_slide(blank_layout)
slide_header(s2, 'Presentation Overview', 'What we will cover in this session')

topics = [
    ('01', 'Definition of Edema',             BLUE),
    ('02', 'Normal Fluid Homeostasis',         TEAL),
    ('03', 'Starling Equation Explained',      PURPLE),
    ('04', 'Mechanism 1 — ↑ Hydrostatic Pressure', BLUE),
    ('05', 'Mechanism 2 — ↓ Oncotic Pressure',     PURPLE),
    ('06', 'Mechanism 3 — ↑ Capillary Permeability', RED),
    ('07', 'Mechanism 4 — Lymphatic Obstruction',  GREEN),
    ('08', 'Exudate vs. Transudate',           ORANGE),
    ('09', 'Clinical Etiologies Summary',      TEAL),
    ('10', 'Key Points & Take-Aways',          NAVY),
]

cols = 2
rows_per_col = 5
for i, (num, text, color) in enumerate(topics):
    col = i // rows_per_col
    row = i % rows_per_col
    x = 0.4 + col * 6.5
    y = 1.45 + row * 1.0
    pill(s2, x, y, 0.55, 0.45, num, color, size=11)
    textbox(s2, x+0.68, y+0.03, 5.6, 0.42,
            text, 13, DARKGREY, bold=False, font='Calibri')
    if row < rows_per_col - 1 or col == 0:
        divider(s2, y+0.54, color=LGREY)

# ═════════════════════════════════════════════════════════════════
# SLIDE 3 – DEFINITION
# ═════════════════════════════════════════════════════════════════
s3 = prs.slides.add_slide(blank_layout)
slide_header(s3, 'What is Edema?', 'Definition and basic concepts')

# Large definition box
def_box = rect(s3, 0.4, 1.38, 12.5, 1.45, LIGHTBLUE, line_color=BLUE, line_w=1.5, radius=True)
textbox(s3, 0.7, 1.42, 12.0, 1.38,
        '"Edema is the abnormal accumulation of excess fluid within the '
        'interstitial tissue spaces or body cavities, occurring when the '
        'normal balance of Starling forces is disrupted."',
        16, NAVY, bold=True, align=PP_ALIGN.CENTER, font='Calibri')

# Three sub-points
card_data = [
    (BLUE, WHITE, 'Interstitial Edema',
     ['Fluid in tissue spaces', 'e.g. ankle swelling, pulmonary edema',
      'Most clinically apparent form']),
    (TEAL, WHITE, 'Intracavitary Fluid',
     ['Ascites (peritoneal cavity)', 'Pleural effusion (pleural space)',
      'Pericardial effusion']),
    (PURPLE, WHITE, 'Two Fluid Types',
     ['Transudate — low protein', 'Exudate — high protein, cells',
      'Type guides the diagnosis']),
]
for i, (hc, tc, title, buls) in enumerate(card_data):
    bullet_card(s3, 0.4 + i*4.32, 3.0, 4.0, 3.9,
                title, tc, hc, buls, bullet_size=12)

textbox(s3, 0.4, 6.98, 12.5, 0.35,
        'Edema itself is a sign, not a diagnosis — the underlying mechanism must always be identified.',
        10, MIDGREY, italic=True, font='Calibri')

# ═════════════════════════════════════════════════════════════════
# SLIDE 4 – NORMAL FLUID HOMEOSTASIS
# ═════════════════════════════════════════════════════════════════
s4 = prs.slides.add_slide(blank_layout)
slide_header(s4, 'Normal Fluid Homeostasis', 'How the body keeps fluid in the right compartment')

# Capillary diagram (schematic)
# Arterial end
rect(s4, 0.4, 2.3, 3.2, 1.5, LIGHTBLUE, line_color=BLUE, line_w=1.5, radius=True)
textbox(s4, 0.5, 2.35, 3.0, 0.4, 'ARTERIAL END', 11, BLUE, bold=True, align=PP_ALIGN.CENTER)
textbox(s4, 0.5, 2.75, 3.0, 0.9,
        'Hydrostatic P: ~32 mmHg\nOncotic P: ~25 mmHg\nNet: +7 mmHg OUT →',
        11, DARKGREY, align=PP_ALIGN.CENTER, font='Calibri')

# Capillary bed center
rect(s4, 3.8, 2.1, 5.7, 1.9, LGREY, line_color=MIDGREY, line_w=1, radius=True)
textbox(s4, 3.9, 2.2, 5.5, 0.4, 'CAPILLARY BED', 11, DARKGREY, bold=True, align=PP_ALIGN.CENTER)
textbox(s4, 3.9, 2.65, 5.5, 1.2,
        'Fluid exchange governed by STARLING FORCES\n'
        'Net outward flow (~small volume)\nis drained by lymphatics',
        11, DARKGREY, align=PP_ALIGN.CENTER, font='Calibri')

# Venous end
rect(s4, 9.7, 2.3, 3.2, 1.5, LGREEN, line_color=GREEN, line_w=1.5, radius=True)
textbox(s4, 9.8, 2.35, 3.0, 0.4, 'VENOUS END', 11, GREEN, bold=True, align=PP_ALIGN.CENTER)
textbox(s4, 9.8, 2.75, 3.0, 0.9,
        'Hydrostatic P: ~12 mmHg\nOncotic P: ~25 mmHg\nNet: -13 mmHg ← IN',
        11, DARKGREY, align=PP_ALIGN.CENTER, font='Calibri')

# Interstitium box
rect(s4, 3.8, 4.2, 5.7, 1.1, LORANGE, line_color=ORANGE, line_w=1.2, radius=True)
textbox(s4, 4.0, 4.28, 5.3, 0.45, 'INTERSTITIUM', 11, ORANGE, bold=True, align=PP_ALIGN.CENTER)
textbox(s4, 4.0, 4.68, 5.3, 0.55,
        'Small amount of fluid + proteins  →  drained by lymphatics', 10.5, DARKGREY, align=PP_ALIGN.CENTER)

# Lymphatics
rect(s4, 4.6, 5.5, 4.1, 0.62, LTEAL, line_color=TEAL, line_w=1.2, radius=True)
textbox(s4, 4.7, 5.55, 3.9, 0.52, 'LYMPHATIC DRAINAGE  →  venous system', 11, TEAL, bold=True, align=PP_ALIGN.CENTER)

# Arrows
connector(s4, 3.6, 3.05, 4.0, 4.42, color=ORANGE, lw=1.8)
connector(s4, 9.5, 3.05, 9.3, 4.42, color=GREEN, lw=1.8)
connector(s4, 6.65, 5.3, 6.65, 5.5, color=TEAL, lw=1.8)

# Key insight box
rect(s4, 0.4, 5.55, 3.8, 1.65, NAVY, radius=True)
textbox(s4, 0.6, 5.65, 3.5, 1.5,
        'In HEALTH:\n• Forces are nearly balanced\n• Net filtrate is minimal\n• Lymphatics clear excess',
        11.5, WHITE, font='Calibri')

textbox(s4, 0.4, 7.15, 12.5, 0.28,
        'Edema forms when net filtration EXCEEDS lymphatic drainage capacity.',
        10, MIDGREY, italic=True, font='Calibri')

# ═════════════════════════════════════════════════════════════════
# SLIDE 5 – STARLING EQUATION
# ═════════════════════════════════════════════════════════════════
s5 = prs.slides.add_slide(blank_layout)
slide_header(s5, 'The Starling Equation', 'The mathematical basis of fluid movement across capillaries')

# Equation box
eq_box = rect(s5, 0.9, 1.38, 11.5, 1.1, NAVY, radius=True)
textbox(s5, 1.1, 1.42, 11.2, 1.0,
        'Q  =  K  ×  [ (Pc − Pi)  −  σ (πc − πi) ]',
        26, WHITE, bold=True, align=PP_ALIGN.CENTER, font='Calibri')

# Variable explanations
vars_data = [
    ('Q', 'Net fluid flow across capillary wall\n(+ = out; − = reabsorption)', BLUE),
    ('K', 'Filtration coefficient\n(capillary surface area × hydraulic conductance)', TEAL),
    ('Pc − Pi', 'Hydrostatic pressure gradient\n(capillary minus interstitial)\nDrives fluid OUT', RED),
    ('σ', 'Reflection coefficient\n(0 = freely permeable; 1 = impermeable)\nMeasures permeability', ORANGE),
    ('πc − πi', 'Oncotic pressure gradient\n(capillary minus interstitial)\nDraws fluid IN', GREEN),
]
for i, (sym, desc, col) in enumerate(vars_data):
    cx = 0.35 + i * 2.6
    pill(s5, cx, 2.7, 0.9, 0.5, sym, col, size=13)
    tb = s5.shapes.add_textbox(Inches(cx-0.15), Inches(3.28),
        Inches(2.5), Inches(1.5))
    tf = tb.text_frame; tf.word_wrap = True
    p = tf.paragraphs[0]
    r = p.add_run(); r.text = desc
    r.font.name = 'Calibri'; r.font.size = Pt(10); r.font.color.rgb = DARKGREY

# Divider line
connector(s5, 0.35, 4.85, W-0.35, 4.85, color=LGREY)

# Two ways to get edema
textbox(s5, 0.35, 5.0, 12.5, 0.38,
        'Edema occurs when Q becomes persistently positive beyond lymphatic capacity, via:', 12.5, DARKGREY)

pill_data = [
    (0.4,  '↑ Hydrostatic gradient\n(Pc↑ or Pi↓)', BLUE),
    (3.5,  '↓ Oncotic gradient\n(πc↓ or πi↑)', PURPLE),
    (6.6,  '↑ Permeability\n(σ approaches 0)', RED),
    (9.7,  'Lymphatic failure\n(drainage overwhelmed)', GREEN),
]
for x, txt, col in pill_data:
    shp = rect(s5, x, 5.52, 2.8, 1.65, col, radius=True)
    shape_text(shp, txt, 11.5, WHITE, bold=True)

textbox(s5, 0.35, 7.22, 12.5, 0.22,
        'Source: Morgan & Mikhail\'s Clinical Anesthesiology, 7e; Frameworks for Internal Medicine',
        9, MIDGREY, italic=True)

# ═════════════════════════════════════════════════════════════════
# SLIDE 6 – MECHANISM 1: INCREASED HYDROSTATIC PRESSURE
# ═════════════════════════════════════════════════════════════════
s6 = prs.slides.add_slide(blank_layout)
slide_header(s6, 'Mechanism 1 — ↑ Hydrostatic Pressure',
             'Increased capillary hydrostatic pressure pushes fluid out')

# Mechanism box
rect(s6, 0.4, 1.38, 8.2, 2.35, LIGHTBLUE, line_color=BLUE, line_w=1.5, radius=True)
textbox(s6, 0.6, 1.45, 7.8, 0.42,
        'PATHOPHYSIOLOGY', 11, BLUE, bold=True)
textbox(s6, 0.6, 1.88, 7.8, 1.75,
        'When venous or capillary pressure rises, the driving force for fluid '
        'efflux (Pc − Pi) increases. This overwhelms the oncotic reabsorption force '
        'and lymphatic drainage → net accumulation of fluid in the interstitium.\n\n'
        'Systemic hypertension does NOT cause edema because precapillary '
        'sphincters prevent arterial pressure from reaching the capillary bed.',
        11.5, DARKGREY, wrap=True)

# Causes cards (right column)
causes = [
    ('Heart Failure',          'Venous congestion → ↑Pc\nBilateral, pitting, dependent\nHigh JVP, S3, ↑BNP', BLUE),
    ('Renal Failure',          'Na+ & H₂O retention → ↑volume\nSimilar to right heart failure\nTreated by dialysis', TEAL),
    ('Cirrhosis',              'Portal hypertension → ↑Pc\n+ Hypoalbuminemia (dual)\nAscites + leg edema', ORANGE),
    ('DVT / Venous Insuff.',   'Local venous obstruction → ↑Pc\nUnilateral, pitting, painful\nDoppler US confirms', PURPLE),
]
for i, (title, desc, col) in enumerate(causes):
    cx = 8.85
    cy = 1.38 + i * 1.53
    shp = rect(s6, cx, cy, 4.1, 1.38, col, radius=True)
    textbox(s6, cx+0.1, cy+0.05, 3.8, 0.38, title, 11.5, WHITE, bold=True)
    textbox(s6, cx+0.1, cy+0.44, 3.8, 0.9, desc, 9.8, WHITE)

# General features
rect(s6, 0.4, 3.9, 8.2, 2.8, LGREY, line_color=MIDGREY, line_w=0.8, radius=True)
textbox(s6, 0.6, 3.98, 7.8, 0.38, 'GENERAL FEATURES OF HYDROSTATIC EDEMA', 11, BLUE, bold=True)
features = [
    '✔  Bilateral (if systemic cause) or Unilateral (if local obstruction)',
    '✔  Dependent — worsens on standing, shifts to sacrum when supine',
    '✔  Pitting — finger pressure leaves a pit for several seconds',
    '✔  Fluid is a TRANSUDATE (low protein) — no increase in permeability',
    '✔  JVP is ELEVATED — key distinguishing feature from oncotic edema',
]
tb6 = s6.shapes.add_textbox(Inches(0.6), Inches(4.42), Inches(7.8), Inches(2.1))
tf6 = tb6.text_frame; tf6.word_wrap = True
first = True
for feat in features:
    if first: p = tf6.paragraphs[0]; first = False
    else: p = tf6.add_paragraph()
    r = p.add_run(); r.text = feat
    r.font.name = 'Calibri'; r.font.size = Pt(10.5); r.font.color.rgb = DARKGREY

# ═════════════════════════════════════════════════════════════════
# SLIDE 7 – MECHANISM 2: DECREASED ONCOTIC PRESSURE
# ═════════════════════════════════════════════════════════════════
s7 = prs.slides.add_slide(blank_layout)
slide_header(s7, 'Mechanism 2 — ↓ Oncotic (Colloid Osmotic) Pressure',
             'Loss of plasma proteins removes the "suction" force that retains fluid')

rect(s7, 0.4, 1.38, 8.2, 2.5, LPURPLE, line_color=PURPLE, line_w=1.5, radius=True)
textbox(s7, 0.6, 1.45, 7.8, 0.4, 'PATHOPHYSIOLOGY', 11, PURPLE, bold=True)
textbox(s7, 0.6, 1.88, 7.8, 1.85,
        'Plasma albumin (made exclusively in the liver at ~15 g/day) generates '
        '~25 mmHg of oncotic pressure. When albumin falls below 2 g/dL, '
        'this reabsorptive force is lost. Fluid leaks out at ALL points '
        'of the capillary bed.\n\n'
        'Key: Both fluid AND proteins remain separated (no permeability change). '
        'The fluid is protein-poor — a transudate.',
        11.5, DARKGREY, wrap=True)

causes7 = [
    ('Nephrotic Syndrome',  '≥3.5g/day proteinuria → ↓albumin\nPeriorbital + generalised\nHyperlipidaemia, lipiduria', PURPLE),
    ('Liver Disease',       '↓Albumin synthesis (cirrhosis)\nAscites + leg oedema\nSpider naevi, jaundice', ORANGE),
    ('Malnutrition',        'Kwashiorkor — protein deficiency\nGeneralised oedema\n+ hair/skin changes', RED),
    ('Prot.-losing Enteropathy', 'GI protein loss\nNo proteinuria\nInflammatory bowel disease', TEAL),
]
for i, (title, desc, col) in enumerate(causes7):
    cy = 1.38 + i * 1.53
    shp = rect(s7, 8.85, cy, 4.1, 1.38, col, radius=True)
    textbox(s7, 8.95, cy+0.05, 3.8, 0.38, title, 11.5, WHITE, bold=True)
    textbox(s7, 8.95, cy+0.44, 3.8, 0.9, desc, 9.8, WHITE)

rect(s7, 0.4, 4.05, 8.2, 2.65, LGREY, line_color=MIDGREY, line_w=0.8, radius=True)
textbox(s7, 0.6, 4.13, 7.8, 0.38, 'GENERAL FEATURES OF ONCOTIC EDEMA', 11, PURPLE, bold=True)
feats7 = [
    '✔  Bilateral, generalised — gravity-dependent but diffuse',
    '✔  Pitting — fluid is mobile, no protein scaffold',
    '✔  JVP is LOW/NORMAL — distinguish from cardiac oedema',
    '✔  Fluid is a TRANSUDATE — low protein, low specific gravity',
    '✔  Often accompanied by ascites and pleural effusions',
    '✔  Serum albumin < 2 g/dL is diagnostic threshold',
]
tb7 = s7.shapes.add_textbox(Inches(0.6), Inches(4.55), Inches(7.8), Inches(2.0))
tf7 = tb7.text_frame; tf7.word_wrap = True
first = True
for feat in feats7:
    if first: p = tf7.paragraphs[0]; first = False
    else: p = tf7.add_paragraph()
    r = p.add_run(); r.text = feat
    r.font.name = 'Calibri'; r.font.size = Pt(10.5); r.font.color.rgb = DARKGREY

# ═════════════════════════════════════════════════════════════════
# SLIDE 8 – MECHANISM 3: INCREASED CAPILLARY PERMEABILITY
# ═════════════════════════════════════════════════════════════════
s8 = prs.slides.add_slide(blank_layout)
slide_header(s8, 'Mechanism 3 — ↑ Capillary Permeability',
             'Endothelial disruption allows both fluid and protein to escape')

rect(s8, 0.4, 1.38, 8.2, 2.8, LRED, line_color=RED, line_w=1.5, radius=True)
textbox(s8, 0.6, 1.45, 7.8, 0.4, 'PATHOPHYSIOLOGY', 11, RED, bold=True)
textbox(s8, 0.6, 1.88, 7.8, 2.2,
        'Chemical mediators (histamine, bradykinin, leukotrienes, cytokines) cause '
        'endothelial cell contraction, opening inter-endothelial gaps within '
        '15-30 minutes. Both FLUID and PROTEINS escape into the interstitium.\n\n'
        'Result: interstitial oncotic pressure RISES (because proteins accumulate '
        'there), further drawing fluid out — a self-amplifying cycle.\n\n'
        'The fluid formed is an EXUDATE (high protein, may contain cells).',
        11.5, DARKGREY, wrap=True)

causes8 = [
    ('Local Inflammation',  'Trauma, cellulitis, burns, insect stings\nLocalized, non-pitting\nRubor, calor, dolor, tumor', RED),
    ('Angioedema',          'Histamine (allergy) or bradykinin (ACEi)\nFace/lips/tongue/larynx\nCan be life-threatening!', ORANGE),
    ('Preeclampsia',        'Endothelial dysfunction + hypoalbuminaemia\nLower limbs → pulmonary/cerebral\nResolves after delivery', PURPLE),
    ('Capillary Leak Synd.','Rare; rapid generalized oedema + shock\nHemoconcentration (↑Hct)\nMonoclonal gammopathy in most', BLUE),
]
for i, (title, desc, col) in enumerate(causes8):
    cy = 1.38 + i * 1.53
    shp = rect(s8, 8.85, cy, 4.1, 1.38, col, radius=True)
    textbox(s8, 8.95, cy+0.05, 3.8, 0.38, title, 11.5, WHITE, bold=True)
    textbox(s8, 8.95, cy+0.44, 3.8, 0.9, desc, 9.8, WHITE)

rect(s8, 0.4, 4.35, 8.2, 2.35, LGREY, line_color=MIDGREY, line_w=0.8, radius=True)
textbox(s8, 0.6, 4.43, 7.8, 0.38, 'GENERAL FEATURES OF PERMEABILITY EDEMA', 11, RED, bold=True)
feats8 = [
    '✔  Can be localized or generalized depending on trigger',
    '✔  Pitting OR non-pitting (proteins in fluid resist pitting)',
    '✔  JVP is NORMAL (no venous congestion)',
    '✔  Fluid is an EXUDATE — high protein, possible cellular debris',
    '✔  Key mediators: histamine (fast, IgE-mediated) & bradykinin (ACEi)',
    '✔  Direct endothelial injury (burns) causes sustained, immediate leakage',
]
tb8 = s8.shapes.add_textbox(Inches(0.6), Inches(4.85), Inches(7.8), Inches(1.75))
tf8 = tb8.text_frame; tf8.word_wrap = True
first = True
for feat in feats8:
    if first: p = tf8.paragraphs[0]; first = False
    else: p = tf8.add_paragraph()
    r = p.add_run(); r.text = feat
    r.font.name = 'Calibri'; r.font.size = Pt(10.5); r.font.color.rgb = DARKGREY

# ═════════════════════════════════════════════════════════════════
# SLIDE 9 – MECHANISM 4: LYMPHATIC OBSTRUCTION
# ═════════════════════════════════════════════════════════════════
s9 = prs.slides.add_slide(blank_layout)
slide_header(s9, 'Mechanism 4 — Lymphatic Obstruction',
             'Failure of lymphatic drainage leads to protein-rich fluid accumulation')

rect(s9, 0.4, 1.38, 8.2, 2.65, LGREEN, line_color=GREEN, line_w=1.5, radius=True)
textbox(s9, 0.6, 1.45, 7.8, 0.4, 'PATHOPHYSIOLOGY', 11, GREEN, bold=True)
textbox(s9, 0.6, 1.88, 7.8, 2.05,
        'Under normal conditions, lymphatics drain the small amount of protein-rich '
        'fluid that is filtered but not reabsorbed at the capillary bed. When '
        'lymphatics are blocked or destroyed, this protein-rich fluid '
        'accumulates in the interstitium.\n\n'
        'Over time, stagnant proteins stimulate FIBROBLAST ACTIVATION → '
        'tissue fibrosis and skin hardening. This is the reason lymphedema '
        'is notoriously difficult to reverse once established.',
        11.5, DARKGREY, wrap=True)

causes9 = [
    ('Filariasis',          'Wuchereria bancrofti (nematode)\nMost common worldwide cause\nChronic elephantiasis', GREEN),
    ('Post-Surgical',       'Axillary/inguinal node dissection\nBreast cancer, melanoma surgery\nUnilateral limb edema', TEAL),
    ('Malignant Infiltr.',  'Tumour invades lymph nodes\nMay be first sign of malignancy\nHard, non-tender nodes', RED),
    ('Myxedema',            'Hypothyroidism → GAG deposition\nNon-pitting (pretibial)\nTSH diagnostic; treated with T4', BLUE),
]
for i, (title, desc, col) in enumerate(causes9):
    cy = 1.38 + i * 1.53
    shp = rect(s9, 8.85, cy, 4.1, 1.38, col, radius=True)
    textbox(s9, 8.95, cy+0.05, 3.8, 0.38, title, 11.5, WHITE, bold=True)
    textbox(s9, 8.95, cy+0.44, 3.8, 0.9, desc, 9.8, WHITE)

rect(s9, 0.4, 4.2, 8.2, 2.5, LGREY, line_color=MIDGREY, line_w=0.8, radius=True)
textbox(s9, 0.6, 4.28, 7.8, 0.38, 'GENERAL FEATURES OF LYMPHATIC EDEMA', 11, GREEN, bold=True)
feats9 = [
    '✔  NON-PITTING — fibrosis and protein scaffold prevent indentation',
    '✔  Stemmer\'s sign positive (cannot pinch skin fold on dorsum of toe/finger)',
    '✔  Unilateral or regional — does not become bilateral unless bilateral nodes affected',
    '✔  Does NOT respond to diuretics (no excess hydrostatic pressure to offload)',
    '✔  Fluid has HIGH protein content (unlike transudates)',
    '✔  Treatment: compression garments, manual lymphatic drainage',
]
tb9 = s9.shapes.add_textbox(Inches(0.6), Inches(4.7), Inches(7.8), Inches(1.9))
tf9 = tb9.text_frame; tf9.word_wrap = True
first = True
for feat in feats9:
    if first: p = tf9.paragraphs[0]; first = False
    else: p = tf9.add_paragraph()
    r = p.add_run(); r.text = feat
    r.font.name = 'Calibri'; r.font.size = Pt(10.5); r.font.color.rgb = DARKGREY

# ═════════════════════════════════════════════════════════════════
# SLIDE 10 – EXUDATE VS TRANSUDATE
# ═════════════════════════════════════════════════════════════════
s10 = prs.slides.add_slide(blank_layout)
slide_header(s10, 'Exudate vs. Transudate',
             'Fluid characteristics help identify the underlying mechanism')

# Header row
rect(s10, 0.4,  1.38, 3.9, 0.52, MIDGREY, radius=True)
rect(s10, 4.42, 1.38, 4.2, 0.52, BLUE, radius=True)
rect(s10, 8.75, 1.38, 4.2, 0.52, RED, radius=True)
textbox(s10, 0.4,  1.42, 3.9, 0.45, 'CHARACTERISTIC', 12, WHITE, bold=True, align=PP_ALIGN.CENTER)
textbox(s10, 4.42, 1.42, 4.2, 0.45, 'TRANSUDATE', 14, WHITE, bold=True, align=PP_ALIGN.CENTER)
textbox(s10, 8.75, 1.42, 4.2, 0.45, 'EXUDATE', 14, WHITE, bold=True, align=PP_ALIGN.CENTER)

rows = [
    ('Protein content',  '< 3 g/dL (low)',           '> 3 g/dL (high)'),
    ('Specific gravity', '< 1.012',                   '> 1.020'),
    ('LDH',              'Low',                        'High (> 2/3 serum)'),
    ('Cells',            'Few (mainly RBCs)',          'WBCs, debris, organisms'),
    ('Appearance',       'Clear, straw-coloured',     'Turbid, cloudy, bloody'),
    ('Pathophysiology',  '↑Hydrostatic or ↓Oncotic', '↑Permeability or lymphatic'),
    ('Clinical causes',  'Heart failure, nephrotic,\ncirrhosis', 'Infection, inflammation,\nmalignancy, burns'),
    ('Response to Rx',   'Diuretics effective',       'Treat underlying cause'),
]
row_bg_a = RGBColor(0xF8, 0xF9, 0xFF)
row_bg_b = RGBColor(0xFF, 0xF3, 0xF3)
for ri, (char, trans, exud) in enumerate(rows):
    ry = 2.02 + ri * 0.61
    bg_color = row_bg_a if ri % 2 == 0 else OFFWHITE
    rect(s10, 0.4,  ry, 3.9, 0.58, bg_color)
    rect(s10, 4.42, ry, 4.2, 0.58, LIGHTBLUE if ri % 2 == 0 else RGBColor(0xE3, 0xF2, 0xFD))
    rect(s10, 8.75, ry, 4.2, 0.58, LRED if ri % 2 == 0 else RGBColor(0xFF, 0xEB, 0xEE))
    textbox(s10, 0.5,  ry+0.05, 3.7, 0.5, char,  10.5, DARKGREY, bold=True)
    textbox(s10, 4.52, ry+0.05, 3.9, 0.5, trans, 10.5, NAVY)
    textbox(s10, 8.85, ry+0.05, 3.9, 0.5, exud,  10.5, RED)

textbox(s10, 0.4, 7.18, 12.5, 0.25,
        'Light\'s Criteria (for pleural fluid): exudate if protein ratio > 0.5 OR LDH ratio > 0.6 OR LDH > 2/3 upper normal.',
        9, MIDGREY, italic=True)

# ═════════════════════════════════════════════════════════════════
# SLIDE 11 – MEDIATORS OF INCREASED PERMEABILITY
# ═════════════════════════════════════════════════════════════════
s11 = prs.slides.add_slide(blank_layout)
slide_header(s11, 'Key Mediators of Vascular Permeability',
             'Chemical signals that open inter-endothelial gaps')

mediators = [
    ('HISTAMINE', BLUE,
     ['Released from mast cells & basophils',
      'Most important early mediator',
      'IgE-mediated (allergic) release',
      'Acts within 15–30 minutes',
      'Short-lived effect',
      'Target: H1 receptors on endothelium',
      'Tx: antihistamines, epinephrine']),
    ('BRADYKININ', PURPLE,
     ['Kallikrein-kinin system',
      'ACE inhibitors → bradykinin ↑↑↑',
      'Slower onset than histamine',
      'Angioedema: face, tongue, larynx',
      'No urticaria (not histamine)',
      'NOT treated with antihistamines',
      'Tx: FFP, icatibant, C1-INH']),
    ('LEUKOTRIENES', RED,
     ['Arachidonic acid pathway',
      'Released in inflammation & allergy',
      'Increase vascular permeability',
      'Also cause bronchoconstriction',
      'Involved in asthma + anaphylaxis',
      'Tx: montelukast (leukotriene blocker)',
      'Slower but more sustained effect']),
    ('CYTOKINES\n(IL-1, TNF, VEGF)', ORANGE,
     ['Systemic inflammatory states',
      'Sepsis, ARDS, burns',
      'VEGF → angiogenesis + permeability',
      'TNF-α → endothelial activation',
      'Causes generalized edema',
      'Capillary leak / "third spacing"',
      'Tx: treat underlying cause']),
]
for i, (name, col, items) in enumerate(mediators):
    cx = 0.4 + i * 3.22
    shp = rect(s11, cx, 1.42, 3.0, 0.52, col, radius=True)
    shape_text(shp, name, 12, WHITE, bold=True)
    card = rect(s11, cx, 2.02, 3.0, 5.2, OFFWHITE, line_color=col, line_w=1.2, radius=True)
    tb = s11.shapes.add_textbox(Inches(cx+0.12), Inches(2.1), Inches(2.76), Inches(5.0))
    tf = tb.text_frame; tf.word_wrap = True
    first = True
    for item in items:
        if first: p = tf.paragraphs[0]; first = False
        else: p = tf.add_paragraph()
        r = p.add_run(); r.text = '• ' + item
        r.font.name = 'Calibri'; r.font.size = Pt(10.5); r.font.color.rgb = DARKGREY

textbox(s11, 0.4, 7.22, 12.5, 0.22,
        'Source: Robbins & Kumar Basic Pathology; Barash Clinical Anesthesia 9e',
        9, MIDGREY, italic=True)

# ═════════════════════════════════════════════════════════════════
# SLIDE 12 – PULMONARY EDEMA (special focus)
# ═════════════════════════════════════════════════════════════════
s12 = prs.slides.add_slide(blank_layout)
slide_header(s12, 'Special Focus — Pulmonary Edema',
             'Excess fluid in the lung interstitium and alveoli')

# Two types side by side
rect(s12, 0.4, 1.38, 5.9, 5.75, LIGHTBLUE, line_color=BLUE, line_w=2.0, radius=True)
rect(s12, 6.9, 1.38, 5.9, 5.75, LRED,      line_color=RED,  line_w=2.0, radius=True)

pill(s12, 0.5, 1.42, 5.7, 0.55, 'TYPE 1 — Cardiogenic (Hydrostatic)', BLUE, size=12)
pill(s12, 7.0, 1.42, 5.7, 0.55, 'TYPE 2 — Non-Cardiogenic (Permeability)', RED, size=12)

card_items_L = [
    ('Mechanism', 'Left heart failure → ↑pulmonary capillary wedge pressure (PCWP > 18 mmHg) → fluid forced into alveoli'),
    ('Fluid type', 'Transudate — low protein'),
    ('Onset', 'Often gradual; may flash-onset in acute MI'),
    ('Signs', 'Orthopnea, PND, S3 gallop, crackles, frothy pink sputum'),
    ('CXR', 'Bilateral basal haziness, Kerley B lines, cardiomegaly'),
    ('Treatment', 'Diuretics (furosemide), oxygen, nitrates, treat underlying cause'),
]
card_items_R = [
    ('Mechanism', 'Microvascular injury (ARDS, sepsis, aspiration) → ↑permeability → protein-rich fluid floods alveoli'),
    ('Fluid type', 'Exudate — HIGH protein content'),
    ('Onset', 'Rapid (hours); PCWP normal (≤ 18 mmHg)'),
    ('Signs', 'Severe hypoxia (PaO₂/FiO₂ < 300), bilateral infiltrates, refractory to O₂'),
    ('CXR', 'Bilateral diffuse infiltrates without cardiomegaly'),
    ('Treatment', 'Treat underlying cause, lung-protective ventilation, prone positioning'),
]
for i, (label, val) in enumerate(card_items_L):
    ty = 2.1 + i * 0.82
    textbox(s12, 0.6, ty, 1.5, 0.38, label+':', 10, BLUE, bold=True)
    textbox(s12, 2.1, ty, 3.9, 0.75, val, 10, DARKGREY, wrap=True)

for i, (label, val) in enumerate(card_items_R):
    ty = 2.1 + i * 0.82
    textbox(s12, 7.1, ty, 1.5, 0.38, label+':', 10, RED, bold=True)
    textbox(s12, 8.6, ty, 3.9, 0.75, val, 10, DARKGREY, wrap=True)

textbox(s12, 0.4, 7.2, 12.5, 0.25,
        'Key distinguishing test: Protein content of edema fluid — low = cardiogenic; high = permeability. '
        'Source: Morgan & Mikhail Clinical Anesthesiology 7e',
        9, MIDGREY, italic=True)

# ═════════════════════════════════════════════════════════════════
# SLIDE 13 – CLINICAL ETIOLOGIES SUMMARY TABLE
# ═════════════════════════════════════════════════════════════════
s13 = prs.slides.add_slide(blank_layout)
slide_header(s13, 'Clinical Etiologies — Summary Table',
             'Causes organised by mechanism with key clinical features')

# Table header
cols_w = [2.5, 1.6, 1.4, 1.4, 1.5, 1.5, 2.8]
col_x = [0.35]
for cw in cols_w[:-1]:
    col_x.append(col_x[-1] + cw + 0.04)

headers = ['Condition', 'Mechanism', 'Laterality', 'Pitting?', 'JVP', 'Fluid', 'Key Clue']
hdr_y = 1.38
for ci, (hdr, cx, cw) in enumerate(zip(headers, col_x, cols_w)):
    shp = rect(s13, cx, hdr_y, cw, 0.48, NAVY, radius=False)
    textbox(s13, cx+0.06, hdr_y+0.06, cw-0.1, 0.38, hdr, 10, WHITE, bold=True, align=PP_ALIGN.CENTER)

rows13 = [
    ('Heart Failure',       '↑Hydrostatic', 'Bilateral', 'Yes', 'HIGH', 'Transudate', '↑BNP, S3 gallop, crackles',          BLUE),
    ('Renal Failure',       '↑Hydrostatic', 'Bilateral', 'Yes', 'High', 'Transudate', '↑Creatinine, ↓GFR, oliguria',        TEAL),
    ('DVT',                 '↑Hydrostatic', 'Unilateral', 'Yes', 'Nml', 'Transudate', 'Pain, erythema, + Doppler US',        BLUE),
    ('Cirrhosis',           'Dual (↑Hp+↓Op)', 'Bilateral', 'Yes', 'Low', 'Transudate', 'Ascites, spider naevi, SAAG>1.1',  ORANGE),
    ('Nephrotic Syndrome',  '↓Oncotic',     'Bilateral', 'Yes', 'Low', 'Transudate', 'Periorbital, >3.5g/day proteinuria', PURPLE),
    ('Malnutrition',        '↓Oncotic',     'Bilateral', 'Yes', 'Low', 'Transudate', 'Kwashiorkor, ↓albumin, ↓weight',     PURPLE),
    ('Cellulitis/Inflam.',  '↑Permeability','Unilateral','Var.','Nml', 'Exudate',   'Hot, red, tender skin, ↑CRP',         RED),
    ('ACEi Angioedema',     '↑Permeability','Localized', 'No',  'Nml', 'Exudate',   'Tongue/lips/throat, on ACEi',         RED),
    ('Preeclampsia',        '↑Permeability','Bilateral', 'Yes', 'Nml', 'Exudate',   'Pregnant + HTN + proteinuria',        ORANGE),
    ('Lymphedema',          'Lymphatic obs.','Unilateral','No', 'Nml', 'High-prot.','Stemmer +ve, fibrosis, non-pitting',  GREEN),
    ('Myxedema',            'Lymphatic/GAG','Bilateral', 'No',  'Nml', 'Mucin',     'Hypothyroid, pretibial, ↑TSH',        GREEN),
]
for ri, row in enumerate(rows13):
    ry = 1.98 + ri * 0.465
    row_vals = list(row[:-1])
    col = row[-1]
    row_bg = OFFWHITE if ri % 2 == 0 else LGREY
    for ci, (val, cx, cw) in enumerate(zip(row_vals, col_x, cols_w)):
        c = col if ci == 1 else DARKGREY
        bg_c = row_bg
        shp2 = rect(s13, cx, ry, cw, 0.44, bg_c)
        textbox(s13, cx+0.05, ry+0.03, cw-0.08, 0.4, val, 9.2, c,
                bold=(ci==1), align=PP_ALIGN.CENTER)

textbox(s13, 0.35, 7.18, 12.5, 0.25,
        'Var. = Variable | Nml = Normal | High-prot. = High-protein lymph fluid | Hp = hydrostatic | Op = oncotic',
        9, MIDGREY, italic=True)

# ═════════════════════════════════════════════════════════════════
# SLIDE 14 – DIAGNOSTIC APPROACH
# ═════════════════════════════════════════════════════════════════
s14 = prs.slides.add_slide(blank_layout)
slide_header(s14, 'Diagnostic Approach to Edema',
             'A stepwise clinical framework')

steps = [
    ('STEP 1', 'Unilateral or Bilateral?',
     'Unilateral → local cause (DVT, cellulitis, lymphedema)\nBilateral → systemic cause (heart, kidney, liver, nutrition)',
     BLUE),
    ('STEP 2', 'Pitting or Non-Pitting?',
     'Pitting → hydrostatic or oncotic\nNon-pitting → lymphatic, myxedema, angioedema',
     TEAL),
    ('STEP 3', 'JVP: High or Low?',
     'High JVP → ↑Hydrostatic (heart, renal failure)\nLow/Normal → ↓Oncotic or ↑Permeability',
     PURPLE),
    ('STEP 4', 'Serum Albumin + Urine Protein',
     'Low albumin + heavy proteinuria → Nephrotic syndrome\nLow albumin, no proteinuria → Liver disease/malnutrition',
     ORANGE),
    ('STEP 5', 'Inflammatory signs?',
     'Redness, heat, fever, recent trigger or drug → ↑Capillary permeability\nAngioedema? Check for ACEi use',
     RED),
    ('STEP 6', 'Medications & Context',
     'CCBs, NSAIDs, steroids, thiazolidinediones\nPregnancy (physiological) | Idiopathic (women 30–40s)',
     GREEN),
]

for i, (step, title, desc, col) in enumerate(steps):
    cy = 1.4 + i * 1.0
    pill(s14, 0.35, cy, 1.0, 0.52, step, col, size=9.5)
    rect(s14, 1.48, cy, 11.5, 0.88, OFFWHITE, line_color=col, line_w=1.2, radius=True)
    textbox(s14, 1.62, cy+0.05, 3.2, 0.38, title, 12, col, bold=True)
    textbox(s14, 1.62, cy+0.43, 11.1, 0.42, desc, 10, DARKGREY, wrap=True)

textbox(s14, 0.35, 7.42, 12.5, 0.22,
        'Always investigate the CAUSE — edema is a sign, not a diagnosis. | Source: Frameworks for Internal Medicine',
        9, MIDGREY, italic=True)

# ═════════════════════════════════════════════════════════════════
# SLIDE 15 – TREATMENT PRINCIPLES
# ═════════════════════════════════════════════════════════════════
s15 = prs.slides.add_slide(blank_layout)
slide_header(s15, 'Treatment Principles',
             'Management is mechanism-specific — always treat the underlying cause')

treatments = [
    ('↑ HYDROSTATIC\nPRESSURE', BLUE,
     ['Loop diuretics (furosemide) — mainstay',
      'Sodium and fluid restriction',
      'Treat underlying cause (HF, renal failure)',
      'ACEi/ARBs for cardiac remodelling',
      'Compression stockings for venous edema',
      'Anticoagulation if DVT is the cause']),
    ('↓ ONCOTIC\nPRESSURE', PURPLE,
     ['Treat underlying protein loss/deficiency',
      'Nephrotic: steroids (minimal change disease)',
      'Cirrhosis: spironolactone + furosemide',
      'Nutritional: protein repletion',
      'Salt restriction and fluid management',
      'Albumin infusion in severe cases (selected)']),
    ('↑ CAPILLARY\nPERMEABILITY', RED,
     ['Histamine-mediated: antihistamines + epinephrine',
      'Bradykinin (ACEi): STOP the ACEi immediately',
      'ACEi angioedema: icatibant, FFP, C1-INH concentrate',
      'Inflammatory edema: antibiotics, anti-inflammatories',
      'Preeclampsia: MgSO₄ + antihypertensives; deliver',
      'ARDS: lung-protective ventilation, treat sepsis']),
    ('LYMPHATIC\nOBSTRUCTION', GREEN,
     ['Compression garments (cornerstone of therapy)',
      'Manual lymphatic drainage (physiotherapy)',
      'Pneumatic compression devices',
      'Skin care and infection prevention',
      'Surgical: lymphovenous bypass (specialist)',
      'Diuretics are NOT effective in lymphedema']),
]
for i, (name, col, items) in enumerate(treatments):
    cx = 0.35 + i * 3.24
    shp = rect(s15, cx, 1.38, 3.05, 0.72, col, radius=True)
    shape_text(shp, name, 11, WHITE, bold=True)
    card = rect(s15, cx, 2.18, 3.05, 5.0, OFFWHITE, line_color=col, line_w=1.2, radius=True)
    tb = s15.shapes.add_textbox(Inches(cx+0.1), Inches(2.27), Inches(2.85), Inches(4.8))
    tf = tb.text_frame; tf.word_wrap = True
    first = True
    for item in items:
        if first: p = tf.paragraphs[0]; first = False
        else: p = tf.add_paragraph()
        r = p.add_run(); r.text = '• ' + item
        r.font.name = 'Calibri'; r.font.size = Pt(10.5); r.font.color.rgb = DARKGREY

textbox(s15, 0.35, 7.25, 12.5, 0.22,
        'Always address the root cause. Symptomatic diuresis without addressing the mechanism leads to recurrence.',
        9.5, MIDGREY, italic=True)

# ═════════════════════════════════════════════════════════════════
# SLIDE 16 – KEY TAKE-AWAYS
# ═════════════════════════════════════════════════════════════════
s16 = prs.slides.add_slide(blank_layout)
bg(s16, NAVY)
rect(s16, 0, 0, W, 1.25, BLUE)
textbox(s16, 0.4, 0.22, 12.5, 0.75,
        'Key Take-Away Points', 30, WHITE, bold=True, align=PP_ALIGN.LEFT)

takeaways = [
    (BLUE,   '1', 'Edema = excess interstitial fluid when net capillary filtration exceeds lymphatic drainage'),
    (TEAL,   '2', 'Four mechanisms: ↑Hydrostatic | ↓Oncotic | ↑Permeability | Lymphatic obstruction'),
    (PURPLE, '3', 'The Starling equation (Q = K[(Pc−Pi) − σ(πc−πi)]) explains ALL four mechanisms mathematically'),
    (RED,    '4', 'Transudate (low protein) = hydrostatic or oncotic cause; Exudate (high protein) = permeability or lymphatic'),
    (ORANGE, '5', 'JVP distinguishes cardiac (HIGH JVP) from hepatic/renal oncotic edema (LOW/NORMAL JVP)'),
    (GREEN,  '6', 'Non-pitting edema = lymphedema, myxedema, or angioedema; pitting = hydrostatic or oncotic'),
    (BLUE,   '7', 'Histamine mediates fast, IgE-driven angioedema; bradykinin mediates ACE inhibitor-induced angioedema'),
    (TEAL,   '8', 'Edema is a SIGN — always identify and treat the root cause, not just the fluid'),
]
for i, (col, num, text) in enumerate(takeaways):
    cy = 1.42 + i * 0.74
    pill(s16, 0.4, cy, 0.55, 0.5, num, col, size=13)
    textbox(s16, 1.12, cy+0.04, 11.8, 0.55, text, 13, WHITE, font='Calibri', bold=False, wrap=True)

rect(s16, 0.4, 7.15, 12.5, 0.28, RGBColor(0x0A, 0x1F, 0x4A))
textbox(s16, 0.4, 7.17, 12.5, 0.25,
        'References: Frameworks for Internal Medicine | Robbins & Kumar Basic Pathology | '
        'Morgan & Mikhail\'s Clinical Anesthesiology 7e | Comprehensive Clinical Nephrology 7e',
        8.5, MIDGREY, italic=True, align=PP_ALIGN.CENTER)

# ─────────────────────────────────────────────
# SAVE
# ─────────────────────────────────────────────
out = '/tmp/workspace/edema-flowchart/Edema_Etiology_Pathophysiology.pptx'
buf = BytesIO()
prs.save(buf)
with open(out, 'wb') as f:
    f.write(buf.getvalue())
print(f"Saved: {out}")
Your credits are exhausted. You can purchase a top-up or subscribe to a plan to continue using Orris. If you're already subscribed, your daily credits will reset tomorrow.
This is a shared conversation. Sign in to Orris to start your own chat.