Write me shortnotes on different electrolytes focused on sodium and potassium based on titinalli and harrisson of medicine Make pdf in short note format of each electrolyte and detail management

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

mkdir -p /tmp/workspace/electrolytes && ls /tmp/workspace/

Writing File

~/electrolytes/generate_pdf.py

"""
Electrolyte Short Notes PDF Generator
Source: Harrison's Principles of Internal Medicine 22E & Tintinalli's Emergency Medicine
"""

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

# ── Colour palette ──────────────────────────────────────────────────────────
C_NAVY    = colors.HexColor("#1B3A5C")
C_TEAL    = colors.HexColor("#0D7A8E")
C_ORANGE  = colors.HexColor("#E07B39")
C_RED     = colors.HexColor("#C0392B")
C_GREEN   = colors.HexColor("#1E7B47")
C_PURPLE  = colors.HexColor("#7B3FA0")
C_YELLOW  = colors.HexColor("#F5A623")
C_LTBLUE  = colors.HexColor("#EAF4F8")
C_LTORANGE= colors.HexColor("#FEF0E6")
C_LTGREEN = colors.HexColor("#E8F5EE")
C_LTPURPLE= colors.HexColor("#F3EAF9")
C_LTYELLOW= colors.HexColor("#FEF9ED")
C_WHITE   = colors.white
C_LTGRAY  = colors.HexColor("#F7F7F7")

W, H = A4

def build_styles():
    base = getSampleStyleSheet()
    styles = {}

    styles["title"] = ParagraphStyle(
        "title", parent=base["Normal"],
        fontName="Helvetica-Bold", fontSize=22,
        textColor=C_WHITE, spaceAfter=4, spaceBefore=0,
        alignment=TA_CENTER, leading=28
    )
    styles["subtitle"] = ParagraphStyle(
        "subtitle", parent=base["Normal"],
        fontName="Helvetica", fontSize=9,
        textColor=colors.HexColor("#CCE5FF"), spaceAfter=2,
        alignment=TA_CENTER, leading=13
    )
    styles["electrolyte_title"] = ParagraphStyle(
        "electrolyte_title", parent=base["Normal"],
        fontName="Helvetica-Bold", fontSize=16,
        textColor=C_WHITE, spaceAfter=2, spaceBefore=0,
        alignment=TA_LEFT, leading=20
    )
    styles["section_head"] = ParagraphStyle(
        "section_head", parent=base["Normal"],
        fontName="Helvetica-Bold", fontSize=10,
        textColor=C_WHITE, spaceAfter=2, spaceBefore=4,
        alignment=TA_LEFT, leading=14
    )
    styles["body"] = ParagraphStyle(
        "body", parent=base["Normal"],
        fontName="Helvetica", fontSize=9,
        textColor=colors.HexColor("#222222"), spaceAfter=3,
        alignment=TA_JUSTIFY, leading=13
    )
    styles["bullet"] = ParagraphStyle(
        "bullet", parent=base["Normal"],
        fontName="Helvetica", fontSize=9,
        textColor=colors.HexColor("#222222"),
        leftIndent=14, spaceAfter=2, leading=13,
        bulletIndent=4
    )
    styles["bold_bullet"] = ParagraphStyle(
        "bold_bullet", parent=base["Normal"],
        fontName="Helvetica-Bold", fontSize=9,
        textColor=colors.HexColor("#222222"),
        leftIndent=14, spaceAfter=2, leading=13,
        bulletIndent=4
    )
    styles["warning"] = ParagraphStyle(
        "warning", parent=base["Normal"],
        fontName="Helvetica-BoldOblique", fontSize=9,
        textColor=C_RED, spaceAfter=3, leading=13
    )
    styles["source"] = ParagraphStyle(
        "source", parent=base["Normal"],
        fontName="Helvetica-Oblique", fontSize=7.5,
        textColor=colors.HexColor("#666666"), spaceAfter=2,
        alignment=TA_CENTER, leading=11
    )
    styles["normal_small"] = ParagraphStyle(
        "normal_small", parent=base["Normal"],
        fontName="Helvetica", fontSize=8,
        textColor=colors.HexColor("#333333"), spaceAfter=2,
        leading=12
    )
    styles["table_head"] = ParagraphStyle(
        "table_head", parent=base["Normal"],
        fontName="Helvetica-Bold", fontSize=8.5,
        textColor=C_WHITE, alignment=TA_CENTER, leading=12
    )
    styles["table_cell"] = ParagraphStyle(
        "table_cell", parent=base["Normal"],
        fontName="Helvetica", fontSize=8.5,
        textColor=colors.HexColor("#222222"), leading=12
    )
    return styles


class ColoredBox(Flowable):
    """A filled rectangle used as a section header background."""
    def __init__(self, width, height, fill_color, label_text, style, radius=4):
        Flowable.__init__(self)
        self.box_width = width
        self.box_height = height
        self.fill_color = fill_color
        self.label_text = label_text
        self.style = style
        self.radius = radius
        self.width = width
        self.height = height

    def draw(self):
        c = self.canv
        c.setFillColor(self.fill_color)
        c.roundRect(0, 0, self.box_width, self.box_height,
                    self.radius, stroke=0, fill=1)
        c.setFillColor(C_WHITE)
        c.setFont(self.style.fontName, self.style.fontSize)
        c.drawString(8, (self.box_height - self.style.fontSize) / 2 + 1,
                     self.label_text)


def section_box(text, color, styles, width=None):
    """Return a colored header box as a KeepTogether block."""
    if width is None:
        width = W - 4*cm
    h = 18
    return ColoredBox(width, h, color, text, styles["section_head"], radius=3)


def bullet_row(text, styles, bold=False):
    key = "bold_bullet" if bold else "bullet"
    return Paragraph(f"•  {text}", styles[key])


def subbullet_row(text, styles):
    p = ParagraphStyle(
        "subbullet", parent=styles["bullet"],
        leftIndent=28, fontSize=8.5
    )
    return Paragraph(f"◦  {text}", p)


# ────────────────────────────────────────────────────────────────────────────
# Content Builders
# ────────────────────────────────────────────────────────────────────────────

def add_electrolyte_header(story, title, subtitle, color, styles):
    """Full-width colored header for each electrolyte section."""
    header_data = [[Paragraph(f"<b>{title}</b>", styles["electrolyte_title"]),
                    Paragraph(subtitle, styles["subtitle"])]]
    tbl = Table(header_data, colWidths=[W - 4*cm])
    tbl.setStyle(TableStyle([
        ("BACKGROUND", (0, 0), (-1, -1), color),
        ("ROUNDEDCORNERS", [6]),
        ("TOPPADDING", (0, 0), (-1, -1), 10),
        ("BOTTOMPADDING", (0, 0), (-1, -1), 10),
        ("LEFTPADDING", (0, 0), (-1, -1), 12),
        ("RIGHTPADDING", (0, 0), (-1, -1), 8),
        ("VALIGN", (0, 0), (-1, -1), "MIDDLE"),
    ]))
    story.append(tbl)
    story.append(Spacer(1, 6*mm))


def two_col_table(left_items, right_items, styles, left_head, right_head,
                  head_color=C_TEAL):
    """A two-column table with colored headers."""
    def make_cell(items):
        paras = []
        for item in items:
            paras.append(Paragraph(f"&#x2022; {item}", styles["normal_small"]))
        return paras

    left_cells = make_cell(left_items)
    right_cells = make_cell(right_items)
    max_rows = max(len(left_cells), len(right_cells))
    while len(left_cells) < max_rows:
        left_cells.append(Paragraph("", styles["normal_small"]))
    while len(right_cells) < max_rows:
        right_cells.append(Paragraph("", styles["normal_small"]))

    header_row = [
        Paragraph(left_head, styles["table_head"]),
        Paragraph(right_head, styles["table_head"])
    ]
    data = [header_row]
    for l, r in zip(left_cells, right_cells):
        data.append([l, r])

    col_w = (W - 4*cm) / 2
    tbl = Table(data, colWidths=[col_w, col_w])
    tbl.setStyle(TableStyle([
        ("BACKGROUND", (0, 0), (-1, 0), head_color),
        ("TEXTCOLOR", (0, 0), (-1, 0), C_WHITE),
        ("ROWBACKGROUNDS", (0, 1), (-1, -1), [C_LTGRAY, C_WHITE]),
        ("GRID", (0, 0), (-1, -1), 0.4, colors.HexColor("#CCCCCC")),
        ("TOPPADDING", (0, 0), (-1, -1), 4),
        ("BOTTOMPADDING", (0, 0), (-1, -1), 4),
        ("LEFTPADDING", (0, 0), (-1, -1), 6),
        ("RIGHTPADDING", (0, 0), (-1, -1), 6),
        ("VALIGN", (0, 0), (-1, -1), "TOP"),
    ]))
    return tbl


def management_table(rows, styles, title_color=C_TEAL):
    """Treatment step table: [Step, Agent, Dose/Notes]"""
    header = [
        Paragraph("Purpose", styles["table_head"]),
        Paragraph("Agent / Action", styles["table_head"]),
        Paragraph("Dose / Details", styles["table_head"]),
    ]
    data = [header]
    for row in rows:
        data.append([
            Paragraph(row[0], styles["normal_small"]),
            Paragraph(f"<b>{row[1]}</b>", styles["normal_small"]),
            Paragraph(row[2], styles["normal_small"]),
        ])
    col_w = W - 4*cm
    tbl = Table(data, colWidths=[col_w*0.22, col_w*0.32, col_w*0.46])
    tbl.setStyle(TableStyle([
        ("BACKGROUND", (0, 0), (-1, 0), title_color),
        ("ROWBACKGROUNDS", (0, 1), (-1, -1), [C_LTGRAY, C_WHITE]),
        ("GRID", (0, 0), (-1, -1), 0.4, colors.HexColor("#CCCCCC")),
        ("TOPPADDING", (0, 0), (-1, -1), 4),
        ("BOTTOMPADDING", (0, 0), (-1, -1), 4),
        ("LEFTPADDING", (0, 0), (-1, -1), 6),
        ("RIGHTPADDING", (0, 0), (-1, -1), 6),
        ("VALIGN", (0, 0), (-1, -1), "TOP"),
    ]))
    return tbl


# ────────────────────────────────────────────────────────────────────────────
# PAGE: Cover
# ────────────────────────────────────────────────────────────────────────────

def cover_page(story, styles):
    story.append(Spacer(1, 3*cm))

    # Big title box
    title_data = [[Paragraph("ELECTROLYTE DISORDERS", styles["title"])]]
    title_tbl = Table(title_data, colWidths=[W - 4*cm])
    title_tbl.setStyle(TableStyle([
        ("BACKGROUND", (0, 0), (-1, -1), C_NAVY),
        ("TOPPADDING", (0, 0), (-1, -1), 20),
        ("BOTTOMPADDING", (0, 0), (-1, -1), 14),
        ("LEFTPADDING", (0, 0), (-1, -1), 14),
        ("RIGHTPADDING", (0, 0), (-1, -1), 14),
    ]))
    story.append(title_tbl)
    story.append(Spacer(1, 4*mm))

    sub_data = [[Paragraph("SHORT NOTES - FOCUSED ON SODIUM &amp; POTASSIUM", styles["title"])]]
    sub_tbl = Table(sub_data, colWidths=[W - 4*cm])
    sub_tbl.setStyle(TableStyle([
        ("BACKGROUND", (0, 0), (-1, -1), C_TEAL),
        ("TOPPADDING", (0, 0), (-1, -1), 10),
        ("BOTTOMPADDING", (0, 0), (-1, -1), 10),
        ("LEFTPADDING", (0, 0), (-1, -1), 14),
        ("RIGHTPADDING", (0, 0), (-1, -1), 14),
    ]))
    story.append(sub_tbl)
    story.append(Spacer(1, 8*mm))

    # Source
    src = "Sources: Harrison's Principles of Internal Medicine, 22nd Edition (2025) | Tintinalli's Emergency Medicine, 9th Edition"
    story.append(Paragraph(src, styles["source"]))
    story.append(Spacer(1, 6*mm))

    # Contents box
    toc_items = [
        ("1", "SODIUM (Na+)", "Hyponatremia & Hypernatremia", C_TEAL),
        ("2", "POTASSIUM (K+)", "Hypokalemia & Hyperkalemia", C_ORANGE),
        ("3", "CALCIUM (Ca2+)", "Hypocalcemia & Hypercalcemia", C_GREEN),
        ("4", "MAGNESIUM (Mg2+)", "Hypomagnesemia & Hypermagnesemia", C_PURPLE),
        ("5", "PHOSPHATE (PO4)", "Hypophosphatemia & Hyperphosphatemia", C_RED),
    ]
    toc_data = [[
        Paragraph("#", styles["table_head"]),
        Paragraph("Electrolyte", styles["table_head"]),
        Paragraph("Disorders Covered", styles["table_head"]),
    ]]
    for num, ele, dis, _ in toc_items:
        toc_data.append([
            Paragraph(f"<b>{num}</b>", styles["normal_small"]),
            Paragraph(f"<b>{ele}</b>", styles["normal_small"]),
            Paragraph(dis, styles["normal_small"]),
        ])
    col_w = W - 4*cm
    toc_tbl = Table(toc_data, colWidths=[col_w*0.08, col_w*0.35, col_w*0.57])
    toc_tbl.setStyle(TableStyle([
        ("BACKGROUND", (0, 0), (-1, 0), C_NAVY),
        ("ROWBACKGROUNDS", (0, 1), (-1, -1), [C_LTBLUE, C_WHITE]),
        ("GRID", (0, 0), (-1, -1), 0.4, colors.HexColor("#AAAAAA")),
        ("TOPPADDING", (0, 0), (-1, -1), 5),
        ("BOTTOMPADDING", (0, 0), (-1, -1), 5),
        ("LEFTPADDING", (0, 0), (-1, -1), 8),
        ("RIGHTPADDING", (0, 0), (-1, -1), 8),
        ("VALIGN", (0, 0), (-1, -1), "MIDDLE"),
    ]))
    story.append(toc_tbl)
    story.append(PageBreak())


# ────────────────────────────────────────────────────────────────────────────
# 1. SODIUM
# ────────────────────────────────────────────────────────────────────────────

def sodium_section(story, styles):
    # ── HYPONATREMIA ──────────────────────────────────────────────────────────
    add_electrolyte_header(story,
        "1. SODIUM (Na+)  -  Part A: HYPONATREMIA",
        "Plasma Na+ < 135 mEq/L  |  Most common electrolyte disorder in hospitalised patients (~22%)",
        C_TEAL, styles)

    story.append(section_box("DEFINITION & PATHOPHYSIOLOGY", C_TEAL, styles))
    story.append(Spacer(1, 2*mm))
    story.append(Paragraph(
        "Hyponatremia = plasma Na+ &lt;135 mM. Almost always results from an increase in circulating "
        "AVP (antidiuretic hormone) and/or increased renal sensitivity to AVP combined with free water intake. "
        "The absolute plasma Na+ tells nothing about volume status - always assess ECFV clinically.",
        styles["body"]))
    story.append(Spacer(1, 3*mm))

    story.append(section_box("CLASSIFICATION BY VOLUME STATUS", C_TEAL, styles))
    story.append(Spacer(1, 2*mm))
    vol_data = [
        [Paragraph("<b>HYPOVOLEMIC</b>", styles["table_head"]),
         Paragraph("<b>EUVOLEMIC (SIAD)</b>", styles["table_head"]),
         Paragraph("<b>HYPERVOLEMIC</b>", styles["table_head"])],
        [
            Paragraph(
                "- GI losses (vomiting, diarrhea)\n- Insensible losses (sweat, burns)\n"
                "- Adrenal insufficiency\n- Thiazide diuretics\n- Salt-wasting nephropathy\n"
                "Urine Na+ &lt;20 mEq/L (non-renal) or &gt;20 mEq/L (renal)",
                styles["normal_small"]),
            Paragraph(
                "- SIAD (most common)\n- Hypothyroidism\n- Secondary adrenal failure\n"
                "- Beer potomania / low solute\n- Drugs (SSRIs, opioids, NSAIDs)\n"
                "- CNS disorders, pulmonary disease\n"
                "Urine Na+ &gt;40 mEq/L, Urine Osm &gt;100",
                styles["normal_small"]),
            Paragraph(
                "- Congestive heart failure\n- Cirrhosis\n- Nephrotic syndrome\n"
                "- Advanced renal failure\n"
                "Urine Na+ &lt;20 mEq/L (CHF, cirrhosis)",
                styles["normal_small"]),
        ]
    ]
    col_w = (W - 4*cm) / 3
    vol_tbl = Table(vol_data, colWidths=[col_w, col_w, col_w])
    vol_tbl.setStyle(TableStyle([
        ("BACKGROUND", (0, 0), (-1, 0), C_TEAL),
        ("ROWBACKGROUNDS", (0, 1), (-1, -1), [C_LTBLUE, C_WHITE]),
        ("GRID", (0, 0), (-1, -1), 0.5, colors.HexColor("#AACCDD")),
        ("TOPPADDING", (0, 0), (-1, -1), 5),
        ("BOTTOMPADDING", (0, 0), (-1, -1), 5),
        ("LEFTPADDING", (0, 0), (-1, -1), 6),
        ("RIGHTPADDING", (0, 0), (-1, -1), 6),
        ("VALIGN", (0, 0), (-1, -1), "TOP"),
    ]))
    story.append(vol_tbl)
    story.append(Spacer(1, 3*mm))

    story.append(section_box("CLINICAL FEATURES", C_TEAL, styles))
    story.append(Spacer(1, 2*mm))
    feats = [
        "Acute (&lt;48 h): Headache, nausea, vomiting, confusion, seizures, herniation (cerebral oedema)",
        "Chronic (&gt;48 h): Often asymptomatic; gait instability, falls, cognitive impairment",
        "Severe (&lt;120 mEq/L): Obtundation, respiratory arrest, death",
        "Risk of ODS (osmotic demyelination syndrome) with overcorrection of chronic hyponatremia",
    ]
    for f in feats:
        story.append(bullet_row(f, styles))
    story.append(Spacer(1, 3*mm))

    story.append(section_box("DIAGNOSTIC WORKUP", C_TEAL, styles))
    story.append(Spacer(1, 2*mm))
    diag = [
        "Serum Na+, Osmolality, Urine Na+, Urine Osmolality",
        "Rule out pseudohyponatremia: hyperglycaemia (correct Na+ by +1.6 mEq/L per 100 mg/dL glucose rise), hyperlipidaemia, hyperproteinaemia",
        "Urine-to-plasma electrolyte ratio (U[Na+]+U[K+]) / P[Na+]: ratio &gt;1 = aggressive fluid restriction required",
        "Thyroid function tests, 9am cortisol for euvolemic hyponatremia",
        "Urine Na+ &lt;20 = extra-renal loss or oedematous state; Urine Na+ &gt;40 = SIAD, diuretics, adrenal insufficiency",
    ]
    for d in diag:
        story.append(bullet_row(d, styles))
    story.append(Spacer(1, 3*mm))

    story.append(section_box("MANAGEMENT", C_TEAL, styles))
    story.append(Spacer(1, 2*mm))
    story.append(Paragraph(
        "<b>Key principle:</b> Correct at &lt;8-10 mEq/L in first 24 h and &lt;18 mEq/L in first 48 h "
        "to prevent ODS. Monitor plasma Na+ every 2-4 hours during active correction.",
        styles["body"]))
    story.append(Spacer(1, 2*mm))

    mgmt_rows = [
        ("Acute symptomatic\n(seizures, herniation)",
         "3% Hypertonic Saline",
         "100 mL IV bolus over 10 min; repeat up to 3x if needed. Target: raise Na+ by 4-6 mEq/L acutely.\n"
         "Then slow correction to avoid ODS."),
        ("Chronic SIAD\n(euvolemic)",
         "Fluid restriction",
         "Fluid restrict to 500-700 mL/d (if urine:plasma ratio ~1) or &lt;500 mL/d (if ratio &gt;1)"),
        ("Chronic SIAD\n(drug therapy)",
         "Vaptans (Tolvaptan, Conivaptan)",
         "Tolvaptan 15 mg PO once daily; titrate to max 60 mg/d. "
         "Avoid in liver disease. Monitor Na+ closely - risk of overcorrection."),
        ("Hypovolemic\nhyponatremia",
         "0.9% Normal saline",
         "IV infusion - rapidly suppresses AVP and induces water diuresis. "
         "Reduce rate if hyponatremia is likely chronic (&gt;48 h)."),
        ("Hypervolemic\n(CHF, cirrhosis)",
         "Sodium + water restriction + diuretics",
         "Loop diuretics (furosemide). ACE inhibitors in CHF. "
         "Fluid restrict &lt;1.5 L/d. Tolvaptan if refractory."),
        ("Beer potomania /\nlow solute",
         "IV saline + diet",
         "High risk of overcorrection - monitor very carefully. Resume normal diet."),
        ("Adrenal insufficiency",
         "Hydrocortisone",
         "100 mg IV stat, then 50 mg q6h. Na+ will normalise with glucocorticoid replacement."),
    ]
    story.append(management_table(mgmt_rows, styles, C_TEAL))
    story.append(Spacer(1, 3*mm))

    story.append(Paragraph(
        "<b>WARNING:</b> Overcorrection of chronic hyponatremia causes ODS (central pontine myelinolysis). "
        "If overcorrection occurs: hypotonic fluids (D5W) or DDAVP 2 mcg IV to re-lower Na+.",
        styles["warning"]))

    story.append(PageBreak())

    # ── HYPERNATREMIA ─────────────────────────────────────────────────────────
    add_electrolyte_header(story,
        "1. SODIUM (Na+)  -  Part B: HYPERNATREMIA",
        "Plasma Na+ > 145 mEq/L  |  Mortality 40-60% (reflects severity of underlying disease)",
        C_NAVY, styles)

    story.append(section_box("DEFINITION & PATHOPHYSIOLOGY", C_NAVY, styles))
    story.append(Spacer(1, 2*mm))
    story.append(Paragraph(
        "Hypernatremia = plasma Na+ &gt;145 mM. Usually a combined water and electrolyte deficit, "
        "with losses of H2O in excess of Na+. Less frequently due to excess Na+ administration. "
        "Elderly patients with reduced thirst are at highest risk.",
        styles["body"]))
    story.append(Spacer(1, 3*mm))

    story.append(section_box("CAUSES", C_NAVY, styles))
    story.append(Spacer(1, 2*mm))
    causes_data = [
        [Paragraph("<b>Water Loss (most common)</b>", styles["table_head"]),
         Paragraph("<b>Excess Sodium Gain</b>", styles["table_head"]),
         Paragraph("<b>Diabetes Insipidus</b>", styles["table_head"])],
        [
            Paragraph(
                "Insensible loss: fever, burns, exercise, mechanical ventilation\n\n"
                "GI: Osmotic diarrhea, NG drainage\n\n"
                "Renal: Osmotic diuresis (hyperglycaemia, mannitol, urea), post-obstructive diuresis\n\n"
                "Inadequate water intake (elderly, CNS disease)",
                styles["normal_small"]),
            Paragraph(
                "Hypertonic saline infusion\n\nHypertonic NaHCO3 infusion\n\nSodium tablet ingestion\n\n"
                "Sea-water ingestion\n\nMineralocorticoid excess",
                styles["normal_small"]),
            Paragraph(
                "Central DI: pituitary surgery, trauma, tumour, sarcoidosis, anterior communicating artery occlusion\n\n"
                "Nephrogenic DI: lithium, ifosfamide, antivirals, hypercalcaemia, hypokalemia, "
                "X-linked V2 receptor mutation, AQP-2 mutations",
                styles["normal_small"]),
        ]
    ]
    col_w = (W - 4*cm) / 3
    causes_tbl = Table(causes_data, colWidths=[col_w, col_w, col_w])
    causes_tbl.setStyle(TableStyle([
        ("BACKGROUND", (0, 0), (-1, 0), C_NAVY),
        ("ROWBACKGROUNDS", (0, 1), (-1, -1), [C_LTBLUE, C_WHITE]),
        ("GRID", (0, 0), (-1, -1), 0.5, colors.HexColor("#AAAACC")),
        ("TOPPADDING", (0, 0), (-1, -1), 5),
        ("BOTTOMPADDING", (0, 0), (-1, -1), 5),
        ("LEFTPADDING", (0, 0), (-1, -1), 6),
        ("RIGHTPADDING", (0, 0), (-1, -1), 6),
        ("VALIGN", (0, 0), (-1, -1), "TOP"),
    ]))
    story.append(causes_tbl)
    story.append(Spacer(1, 3*mm))

    story.append(section_box("CLINICAL FEATURES", C_NAVY, styles))
    story.append(Spacer(1, 2*mm))
    feats = [
        "Cellular dehydration as free water moves from intracellular to extracellular space",
        "Mental status changes, lethargy, irritability, confusion",
        "Muscular weakness, ataxia, tremors, hyperreflexia, brisk peripheral tone",
        "Seizures, unresponsiveness, intracerebral haemorrhage, permanent neurologic dysfunction",
        "Na+ &gt;160 mEq/L: immediate attention required - risk of intellectual deficit and seizure disorder",
        "Infants: high-pitched cry, nuchal rigidity, myoclonus, asterixis, chorea",
        "Dipsogenic: polyuria and polydipsia with normal or elevated serum Na+",
    ]
    for f in feats:
        story.append(bullet_row(f, styles))
    story.append(Spacer(1, 3*mm))

    story.append(section_box("MANAGEMENT", C_NAVY, styles))
    story.append(Spacer(1, 2*mm))
    story.append(Paragraph(
        "<b>Key principle (Tintinalli):</b> Correct Na+ NO faster than 1 mEq/L/h and NO more than "
        "10-12 mEq/L in first 24 h. Rapid correction risks cerebral oedema.",
        styles["body"]))
    story.append(Spacer(1, 2*mm))
    story.append(Paragraph(
        "<b>Free Water Deficit Formula:</b>  FWD (mL) = 4 x body weight (kg) x desired fall in Na+ (mEq/L)",
        styles["body"]))
    story.append(Spacer(1, 2*mm))

    hyper_na_rows = [
        ("Haemodynamically\nunstable",
         "0.9% Normal Saline bolus",
         "20 mL/kg IV bolus; reassess after each bolus. Restore perfusion before addressing Na+."),
        ("Hypovolemic /\neuvolemic (stable)",
         "Free water replacement",
         "Calculate FWD. Give half the deficit over 8 h, remainder over next 16 h. "
         "Add maintenance requirements + ongoing losses. Monitor Na+ hourly until stable."),
        ("Fluid type\n(moderate hypernatremia)",
         "D5W or 0.45% NaCl",
         "Prefer hypotonic fluids once haemodynamically stable. "
         "0.45% NaCl if moderate hypernatremia; D5W for severe."),
        ("Central DI",
         "Desmopressin (DDAVP)",
         "2-4 mcg SC/IV q12-24h OR 10-20 mcg intranasal. "
         "Monitor for hyponatremia with overtreatment."),
        ("Nephrogenic DI",
         "Low-salt diet + thiazide + amiloride",
         "Hydrochlorothiazide 25 mg/d reduces urine volume by ~50% via paradoxical mechanism. "
         "Amiloride blocks Li+ entry into principal cells (Li-induced NDI). "
         "Remove offending drug (lithium, etc.)."),
        ("Hypervolemic\nhypernatremia",
         "Loop diuretics + D5W",
         "Furosemide to excrete excess Na+. Replace free water with D5W. "
         "May require dialysis (haemofilter) if severe."),
    ]
    story.append(management_table(hyper_na_rows, styles, C_NAVY))
    story.append(Spacer(1, 3*mm))

    story.append(Paragraph(
        "<b>Monitoring:</b> Check serum Na+ every hour initially. Monitor urine output closely "
        "(risk of acute tubular necrosis). Complete correction may require &gt;48 hours.",
        styles["body"]))

    story.append(PageBreak())


# ────────────────────────────────────────────────────────────────────────────
# 2. POTASSIUM
# ────────────────────────────────────────────────────────────────────────────

def potassium_section(story, styles):
    # ── HYPOKALEMIA ───────────────────────────────────────────────────────────
    add_electrolyte_header(story,
        "2. POTASSIUM (K+)  -  Part A: HYPOKALEMIA",
        "Plasma K+ < 3.5 mEq/L  |  Occurs in ~20% of hospitalised patients  |  "
        "10x increase in in-hospital mortality",
        C_ORANGE, styles)

    story.append(section_box("DEFINITION & CAUSES", C_ORANGE, styles))
    story.append(Spacer(1, 2*mm))
    story.append(Paragraph(
        "Hypokalemia = plasma K+ &lt;3.5 mM (Tintinalli: &lt;3.4 mEq/L). "
        "Caused by redistribution into cells, decreased intake, or increased losses (renal or non-renal). "
        "<b>Systemic hypomagnesaemia</b> causes treatment-resistant hypokalemia (always correct Mg2+ first).",
        styles["body"]))
    story.append(Spacer(1, 2*mm))

    causes_left = [
        "<b>DECREASED INTAKE:</b> Starvation, malnutrition",
        "<b>REDISTRIBUTION INTO CELLS:</b>",
        "Metabolic alkalosis",
        "Insulin excess, TPN",
        "B2-adrenergic agonists (salbutamol, terbutaline)",
        "Thyrotoxic periodic paralysis",
        "Theophylline, caffeine (stimulate Na+/K+-ATPase)",
        "Vitamin B12 / folate therapy (erythropoiesis)",
        "Hypothermia",
        "Familial hypokalemic periodic paralysis",
        "Barium toxicity (blocks K+ leak channels)",
    ]
    causes_right = [
        "<b>INCREASED RENAL LOSSES:</b>",
        "Loop & thiazide diuretics (most common)",
        "Osmotic diuresis (DKA, mannitol)",
        "Mineralocorticoid excess (hyperaldosteronism, Cushing's)",
        "Bartter's syndrome / Gitelman's syndrome",
        "Renal tubular acidosis (type 1, 2)",
        "Hypomagnesaemia",
        "<b>INCREASED NON-RENAL LOSSES:</b>",
        "GI losses: diarrhea, vomiting, NG suction, laxative abuse",
        "Sweat (excessive exercise, cystic fibrosis)",
        "DKA (osmotic diuresis - serum K+ may be normal/high due to acidosis)",
    ]
    story.append(two_col_table(causes_left, causes_right, styles,
                               "Decreased Intake / Redistribution",
                               "Increased Losses", C_ORANGE))
    story.append(Spacer(1, 3*mm))

    story.append(section_box("CLINICAL FEATURES & ECG CHANGES", C_ORANGE, styles))
    story.append(Spacer(1, 2*mm))
    ecg_data = [
        [Paragraph("<b>K+ Level</b>", styles["table_head"]),
         Paragraph("<b>Symptoms</b>", styles["table_head"]),
         Paragraph("<b>ECG Changes</b>", styles["table_head"])],
        [Paragraph("3.0-3.5 mEq/L\n(Mild)", styles["normal_small"]),
         Paragraph("Usually asymptomatic, mild fatigue, constipation", styles["normal_small"]),
         Paragraph("Flattened T waves, prominent U wave", styles["normal_small"])],
        [Paragraph("2.5-3.0 mEq/L\n(Moderate)", styles["normal_small"]),
         Paragraph("Muscle weakness, cramps, paralytic ileus, polyuria", styles["normal_small"]),
         Paragraph("Prominent U wave, ST depression, T-U fusion", styles["normal_small"])],
        [Paragraph("&lt;2.5 mEq/L\n(Severe)", styles["normal_small"]),
         Paragraph("Skeletal muscle paralysis, respiratory failure, rhabdomyolysis, ileus", styles["normal_small"]),
         Paragraph("Biphasic T waves, ventricular ectopy, torsades de pointes, VF", styles["normal_small"])],
    ]
    col_w = W - 4*cm
    ecg_tbl = Table(ecg_data, colWidths=[col_w*0.2, col_w*0.42, col_w*0.38])
    ecg_tbl.setStyle(TableStyle([
        ("BACKGROUND", (0, 0), (-1, 0), C_ORANGE),
        ("ROWBACKGROUNDS", (0, 1), (-1, -1), [C_LTORANGE, C_WHITE]),
        ("GRID", (0, 0), (-1, -1), 0.4, colors.HexColor("#DDAA88")),
        ("TOPPADDING", (0, 0), (-1, -1), 4),
        ("BOTTOMPADDING", (0, 0), (-1, -1), 4),
        ("LEFTPADDING", (0, 0), (-1, -1), 6),
        ("RIGHTPADDING", (0, 0), (-1, -1), 6),
        ("VALIGN", (0, 0), (-1, -1), "TOP"),
    ]))
    story.append(ecg_tbl)
    story.append(Spacer(1, 3*mm))

    story.append(section_box("MANAGEMENT (Harrison's 22E + Tintinalli)", C_ORANGE, styles))
    story.append(Spacer(1, 2*mm))
    story.append(Paragraph(
        "<b>Deficit estimation (Harrison's):</b> Serum K+ drops ~0.27 mM per 100 mmol reduction in total-body K+. "
        "Loss of 400-800 mmol results in reduction of ~2.0 mM. Replace gradually over 24-48 h with frequent monitoring.",
        styles["body"]))
    story.append(Spacer(1, 2*mm))

    hypok_rows = [
        ("Mild-moderate\nasymptomatic",
         "Oral KCl",
         "40-80 mEq/day in divided doses. "
         "Max 40 mEq per dose (Tintinalli: 2-5 mEq/kg/d in 2-3 divided doses, max 40 mEq/dose). "
         "Preferred route."),
        ("Hypomagnesaemia\n(always correct first)",
         "Magnesium repletion",
         "Mg2+ deficiency causes treatment-resistant hypokalemia. "
         "Give MgSO4 1-2 g IV or oral Mg supplement. Cannot correct K+ without correcting Mg2+."),
        ("IV replacement\n(unable to take oral)",
         "IV KCl in saline",
         "0.2-0.3 mEq/kg/h (Tintinalli). ALWAYS in saline, NEVER in dextrose. "
         "Max concentration 40 mEq/L via peripheral, &gt;60 mEq/L via central line. "
         "Cardiac monitoring required."),
        ("Severe / urgent\n(paralysis, arrhythmia)",
         "IV KCl rapid",
         "0.5 mEq/kg/h max (Tintinalli) with continuous ECG monitoring. "
         "Max 20 mEq/dose via peripheral (max 60 mEq/L concentration). "
         "Central line preferred for high concentrations."),
        ("Redistributive\n(TPP, theophylline, head injury)",
         "Propranolol",
         "High-dose propranolol 3 mg/kg. Non-selective B-blocker. "
         "Corrects hypokalemia without risk of rebound hyperkalemia."),
        ("DKA-associated\nhypokalemia",
         "Early K+ repletion",
         "Begin K+ early. Serum K+ shifts into cells as acidosis is corrected - "
         "can cause profound hypokalemia. Add K+ to IV fluids when K+ &lt;5.0 and urine output adequate."),
        ("Concomitant\nhypophosphatemia",
         "Potassium phosphate",
         "IV or oral K-phosphate appropriate when both K+ and PO4 are low."),
        ("Metabolic acidosis +\nhypokalemia",
         "Potassium bicarbonate\nor citrate",
         "Use K-bicarbonate or K-citrate instead of KCl."),
    ]
    story.append(management_table(hypok_rows, styles, C_ORANGE))
    story.append(Spacer(1, 3*mm))
    story.append(Paragraph(
        "<b>Caution:</b> Monitor plasma K+ frequently. Renal function, diabetes, and medications "
        "affect risk of overcorrection. Patients with arrhythmias or prolonged QTc need cardiac telemetry.",
        styles["warning"]))

    story.append(PageBreak())

    # ── HYPERKALEMIA ──────────────────────────────────────────────────────────
    add_electrolyte_header(story,
        "2. POTASSIUM (K+)  -  Part B: HYPERKALEMIA",
        "Plasma K+ > 5.5 mEq/L  |  Severe (&gt;6.0 mM) in ~1% of inpatients  |  "
        "Significantly increased mortality",
        C_RED, styles)

    story.append(section_box("DEFINITION & CAUSES", C_RED, styles))
    story.append(Spacer(1, 2*mm))
    story.append(Paragraph(
        "Hyperkalemia = plasma K+ &gt;5.5 mM (Harrison's) / &gt;5.5 mEq/L (Tintinalli). "
        "Reduction in renal K+ excretion is the most common underlying cause. "
        "<b>Always exclude pseudohyperkalemia</b> (haemolysis, thrombocytosis, leukocytosis) before treatment.",
        styles["body"]))
    story.append(Spacer(1, 2*mm))

    causes_l = [
        "<b>PSEUDOHYPERKALEMIA:</b> In vitro haemolysis, thrombocytosis (&gt;1 million), leukocytosis (&gt;100,000), erythrocytosis",
        "<b>SHIFT (Intra- to Extracellular):</b>",
        "Acidosis (metabolic or respiratory)",
        "Hyperosmolality (contrast, hypertonic dextrose, mannitol)",
        "Insulin deficiency (DKA)",
        "B2-adrenergic blockade (propranolol)",
        "Digoxin toxicity (inhibits Na+/K+-ATPase)",
        "Succinylcholine (in burns, crush, neuromuscular injury)",
        "Hyperkalemic periodic paralysis",
        "Rapid tumour lysis syndrome",
    ]
    causes_r = [
        "<b>INADEQUATE EXCRETION:</b>",
        "RAAS blockade: ACE-I, ARBs, renin inhibitors",
        "Aldosterone blockers: spironolactone, eplerenone",
        "ENaC blockers: amiloride, triamterene, trimethoprim",
        "CKD / acute kidney injury (most common in real-world)",
        "Addison's disease / adrenal insufficiency",
        "Pseudohypoaldosteronism type I/II",
        "Hyporeninaemic hypoaldosteronism (Type 4 RTA): DM, CKD, NSAIDs",
        "<b>EXCESS INTAKE (rare):</b>",
        "K+ supplements, K+-containing salt substitutes",
        "Massive blood transfusion",
    ]
    story.append(two_col_table(causes_l, causes_r, styles,
                               "Shift / Redistribution", "Inadequate Excretion", C_RED))
    story.append(Spacer(1, 3*mm))

    story.append(section_box("ECG CHANGES (in order of severity)", C_RED, styles))
    story.append(Spacer(1, 2*mm))
    ecg2_data = [
        [Paragraph("<b>K+ Level</b>", styles["table_head"]),
         Paragraph("<b>ECG Change</b>", styles["table_head"]),
         Paragraph("<b>Clinical Significance</b>", styles["table_head"])],
        [Paragraph("5.5-6.5", styles["normal_small"]),
         Paragraph("Tall, peaked (tented) T waves - narrow base", styles["normal_small"]),
         Paragraph("Earliest sign; high specificity", styles["normal_small"])],
        [Paragraph("6.5-7.5", styles["normal_small"]),
         Paragraph("Prolonged PR interval, widening QRS, flat P waves", styles["normal_small"]),
         Paragraph("Conduction delay; urgent therapy", styles["normal_small"])],
        [Paragraph("7.5-8.5", styles["normal_small"]),
         Paragraph("Absent P waves, very wide QRS ('sine wave' pattern)", styles["normal_small"]),
         Paragraph("Pre-terminal; immediate emergency treatment", styles["normal_small"])],
        [Paragraph("&gt;8.5", styles["normal_small"]),
         Paragraph("Ventricular fibrillation, asystole", styles["normal_small"]),
         Paragraph("Cardiac arrest", styles["normal_small"])],
    ]
    col_w = W - 4*cm
    ecg2_tbl = Table(ecg2_data, colWidths=[col_w*0.15, col_w*0.50, col_w*0.35])
    ecg2_tbl.setStyle(TableStyle([
        ("BACKGROUND", (0, 0), (-1, 0), C_RED),
        ("ROWBACKGROUNDS", (0, 1), (-1, -1), [colors.HexColor("#FDECEA"), C_WHITE]),
        ("GRID", (0, 0), (-1, -1), 0.4, colors.HexColor("#DD9988")),
        ("TOPPADDING", (0, 0), (-1, -1), 4),
        ("BOTTOMPADDING", (0, 0), (-1, -1), 4),
        ("LEFTPADDING", (0, 0), (-1, -1), 6),
        ("RIGHTPADDING", (0, 0), (-1, -1), 6),
        ("VALIGN", (0, 0), (-1, -1), "TOP"),
    ]))
    story.append(ecg2_tbl)
    story.append(Spacer(1, 3*mm))

    story.append(section_box("EMERGENCY MANAGEMENT - 5 Steps", C_RED, styles))
    story.append(Spacer(1, 2*mm))
    story.append(Paragraph(
        "<b>STEP 1 - Stabilise the myocardium | STEP 2 - Shift K+ into cells | "
        "STEP 3 - Remove K+ from body | STEP 4 - Prevent recurrence | STEP 5 - Monitor</b>",
        styles["body"]))
    story.append(Spacer(1, 2*mm))

    hyperk_rows = [
        ("STEP 1\nStabilise Myocardium\n(onset 1-3 min, duration 30-60 min)",
         "Calcium gluconate 10%\n(or Calcium chloride)",
         "100 mg/kg IV (1 mL/kg of 10% solution) at &lt;100 mg/min; max 3 g/dose. "
         "Repeat if ECG changes persist. Peripheral or central. "
         "Does NOT lower K+ - only protects the heart."),
        ("STEP 2a\nShift K+ into cells\n(onset 15-30 min)",
         "Insulin + Dextrose",
         "Regular insulin 10 units IV + 50 mL of 50% dextrose (D50W). "
         "In adults. In children: 0.1 unit/kg insulin + 2 mL/kg D25W. "
         "Lowers K+ by 0.5-1.5 mEq/L. Duration 2-6 h. Monitor glucose."),
        ("STEP 2b\nShift K+ into cells\n(onset 30-60 min)",
         "Salbutamol (Albuterol)",
         "10-20 mg nebulised (10 mg in children). "
         "B2-agonist: drives K+ into cells via Na+/K+-ATPase. "
         "Lowers K+ by 0.5-1.5 mEq/L. Additive to insulin effect."),
        ("STEP 2c\nAlkalinisation\n(onset slow; limited data)",
         "Sodium bicarbonate",
         "1-2 mEq/kg IV. Effective mainly in metabolic acidosis. "
         "Less reliable as sole therapy. Do NOT mix with calcium."),
        ("STEP 3a\nRemove K+ from body",
         "Loop diuretics\n(if adequate renal function)",
         "Furosemide 40-80 mg IV. Increases urinary K+ excretion. "
         "Combine with IV saline if patient is volume depleted."),
        ("STEP 3b\nRemove K+ from body",
         "Cation exchange resins\n(Sodium polystyrene sulfonate / Patiromer)",
         "SPS: 15-60 g PO or 30-50 g PR in sorbitol. Onset 1-2 h. "
         "Patiromer: 8.4 g PO once daily; better tolerated, onset 7 h. "
         "Sodium zirconium cyclosilicate (ZS-9): 10 g TID x 48 h (faster onset ~1 h)."),
        ("STEP 3c\nRemove K+ from body\n(severe refractory)",
         "Haemodialysis / CRRT",
         "Most effective method for K+ removal. Indications: K+ &gt;7 mEq/L with ECG changes, "
         "renal failure, haemodynamic instability, or failure of medical therapy."),
        ("STEP 4\nPrevent recurrence",
         "Remove precipitants",
         "Stop ACE-I, ARBs, K+-sparing diuretics, NSAIDs, K+ supplements. "
         "Low-potassium diet (&lt;2 g/day). Treat underlying cause (CKD, adrenal insufficiency)."),
    ]
    story.append(management_table(hyperk_rows, styles, C_RED))
    story.append(Spacer(1, 3*mm))
    story.append(Paragraph(
        "<b>Tintinalli note (Paediatric):</b> Calcium gluconate 1 mL/kg (10% solution) IV, "
        "insulin 0.1 unit/kg + D25W 2 mL/kg. Sodium bicarbonate 1-2 mEq/kg IV if acidotic.",
        styles["body"]))

    story.append(PageBreak())


# ────────────────────────────────────────────────────────────────────────────
# 3. CALCIUM
# ────────────────────────────────────────────────────────────────────────────

def calcium_section(story, styles):
    add_electrolyte_header(story,
        "3. CALCIUM (Ca2+)",
        "Normal: 8.5-10.5 mg/dL (2.1-2.6 mM)  |  Source: Harrison's 22E, Chapter 57",
        C_GREEN, styles)

    story.append(section_box("PHYSIOLOGY & REGULATION", C_GREEN, styles))
    story.append(Spacer(1, 2*mm))
    story.append(Paragraph(
        "Calcium is the most abundant mineral in the body. Regulated by PTH and vitamin D metabolites. "
        "PTH increases osteoclast bone resorption (via RANKL), distal renal Ca2+ reabsorption, and renal "
        "1,25(OH)2D production. 1,25(OH)2D increases intestinal Ca2+ absorption. "
        "~50% of serum Ca2+ is protein-bound (mainly albumin); only ionised (free) Ca2+ is biologically active. "
        "<b>Corrected Ca2+ = measured Ca2+ + 0.8 x (4.0 - albumin g/dL)</b>",
        styles["body"]))
    story.append(Spacer(1, 3*mm))

    story.append(section_box("HYPERCALCEMIA  (Ca2+ > 10.5 mg/dL)", C_GREEN, styles))
    story.append(Spacer(1, 2*mm))
    hca_l = [
        "<b>Primary hyperparathyroidism</b> (parathyroid adenoma/hyperplasia, MEN): most common outpatient cause",
        "Malignancy: PTHrP secretion (solid tumours), lytic metastases (breast, myeloma), 1,25(OH)2D (lymphoma)",
        "Granulomatous disease: sarcoidosis, TB, fungal - macrophage 1,25(OH)2D production",
        "Vitamin D intoxication",
        "Familial hypocalciuric hypercalcaemia (FHH) - benign",
        "Thiazide diuretics, lithium, vitamin A excess",
    ]
    hca_r = [
        "<b>Clinical: Bones, Stones, Groans, Moans:</b>",
        "Bones: Osteitis fibrosa cystica, subperiosteal resorption, Brown tumours",
        "Stones: Nephrocalcinosis, nephrolithiasis, polyuria/polydipsia",
        "Groans: N&V, anorexia, constipation, pancreatitis, peptic ulcers",
        "Moans: Depression, cognitive impairment, lethargy, obtundation",
        "ECG: Shortened QT interval",
        "Metastatic calcification, band keratopathy",
    ]
    story.append(two_col_table(hca_l, hca_r, styles, "Causes", "Clinical Features", C_GREEN))
    story.append(Spacer(1, 3*mm))

    hypercal_rows = [
        ("All symptomatic\nhypercalcaemia",
         "IV Normal Saline",
         "200-300 mL/h to expand ECF volume and increase renal Ca2+ excretion. "
         "Target urine output 100-150 mL/h. Most important first step."),
        ("After rehydration\n(moderate-severe)",
         "Loop diuretics",
         "Furosemide 20-80 mg IV: inhibits TALH Ca2+ reabsorption. "
         "Only after adequate rehydration. Avoid before volume repletion."),
        ("Hypercalcaemia of\nmalignancy / severe",
         "Bisphosphonates",
         "Zoledronic acid 4 mg IV over 15 min (most potent) or Pamidronate 60-90 mg IV over 2-4 h. "
         "Onset 24-72 h, peak 4-7 days, duration weeks."),
        ("Malignancy /\nbisphosphonate failure",
         "Denosumab",
         "120 mg SC q4 weeks. Anti-RANKL monoclonal antibody. Effective in bisphosphonate-refractory cases."),
        ("Granulomatous /\nVit D intoxication",
         "Glucocorticoids",
         "Prednisone 40-60 mg/day PO. Inhibits macrophage 1,25(OH)2D production. "
         "Response within 5-7 days."),
        ("Severe hypercalcaemia\nwith renal failure",
         "Haemodialysis",
         "Low-calcium dialysate. Definitive treatment when medical therapy fails."),
        ("Calcimimetic (primary\nhyperparathyroidism)",
         "Cinacalcet",
         "30-90 mg PO BD. Activates CaSR, suppresses PTH. "
         "For patients who cannot undergo parathyroidectomy."),
        ("Definitive (primary\nhyperparathyroidism)",
         "Parathyroidectomy",
         "Indications: symptomatic, Ca2+ &gt;1.0 mg/dL above normal, T-score &lt;-2.5, "
         "age &lt;50 years, eGFR &lt;60, urine Ca2+ &gt;400 mg/24h."),
    ]
    story.append(management_table(hypercal_rows, styles, C_GREEN))
    story.append(Spacer(1, 3*mm))

    story.append(section_box("HYPOCALCEMIA  (Ca2+ < 8.5 mg/dL / ionised Ca2+ < 1.1 mM)", C_GREEN, styles))
    story.append(Spacer(1, 2*mm))
    hypo_ca_l = [
        "Hypoparathyroidism: post-surgical (thyroidectomy, parathyroidectomy), autoimmune",
        "Vitamin D deficiency / resistance",
        "Hypomagnesaemia (impairs PTH secretion and action)",
        "Hungry bone syndrome (post-parathyroidectomy)",
        "Acute pancreatitis",
        "Massive blood transfusion (citrate chelates Ca2+)",
        "Acute rhabdomyolysis",
        "Pseudohypoparathyroidism (GNAS mutations)",
        "Drugs: bisphosphonates, denosumab, foscarnet, phenytoin",
        "CKD: impaired 1,25(OH)2D synthesis and phosphate retention",
    ]
    hypo_ca_r = [
        "Neuromuscular: Chvostek's sign (tap facial nerve), Trousseau's sign (carpal spasm with BP cuff)",
        "Tetany, paraesthesias (perioral, fingers, toes)",
        "Laryngospasm, bronchospasm",
        "Seizures (hypocalcaemic)",
        "Psychiatric: anxiety, depression, psychosis",
        "Cardiac: prolonged QT interval, heart failure, arrhythmia",
        "Cataracts (chronic)",
        "Osteitis fibrosa cystica (secondary hyperparathyroidism in CKD)",
    ]
    story.append(two_col_table(hypo_ca_l, hypo_ca_r, styles, "Causes", "Clinical Features", C_GREEN))
    story.append(Spacer(1, 3*mm))

    hypocal_rows = [
        ("Symptomatic / severe\n(tetany, seizures)",
         "IV Calcium gluconate",
         "1-2 g IV (93-186 mg elemental Ca2+) in 50 mL D5W over 10-20 min. "
         "Then infusion: 0.5-1.5 mg/kg/h elemental Ca2+. Monitor ECG. "
         "Calcium chloride has 3x more elemental Ca2+ but must be given centrally."),
        ("Asymptomatic /\nchronic",
         "Oral calcium + Vit D",
         "Calcium carbonate 1-2 g elemental Ca2+ daily in divided doses with meals. "
         "Plus calcitriol (1,25(OH)2D) 0.25-2 mcg/day or vitamin D3 1000-2000 IU/day."),
        ("Hypomagnesaemia\n(always correct first)",
         "MgSO4",
         "1-2 g IV over 10 min, then 1 g/h x 3-4 h. "
         "Hypocalcaemia is refractory to Ca2+ replacement until Mg2+ is corrected."),
        ("CKD-associated\nhypocalcaemia",
         "Calcitriol + Phosphate binders",
         "Calcitriol 0.25-0.5 mcg/day. Sevelamer or calcium carbonate as phosphate binders. "
         "Aim Ca x PO4 product &lt;55."),
        ("Hypoparathyroidism\n(long-term)",
         "Recombinant PTH 1-84\n(Natpara)",
         "100 mcg SC daily. For hypoparathyroidism requiring &gt;600 mg elemental Ca2+/day. "
         "Reduces requirements for Ca2+ and active vitamin D."),
    ]
    story.append(management_table(hypocal_rows, styles, C_GREEN))
    story.append(PageBreak())


# ────────────────────────────────────────────────────────────────────────────
# 4. MAGNESIUM
# ────────────────────────────────────────────────────────────────────────────

def magnesium_section(story, styles):
    add_electrolyte_header(story,
        "4. MAGNESIUM (Mg2+)",
        "Normal: 1.7-2.2 mg/dL (0.7-0.9 mEq/L)  |  Second most abundant intracellular cation",
        C_PURPLE, styles)

    story.append(section_box("PHYSIOLOGY", C_PURPLE, styles))
    story.append(Spacer(1, 2*mm))
    story.append(Paragraph(
        "Mg2+ is the second most abundant intracellular cation. It is an essential cofactor for over 300 "
        "enzymes, including Na+/K+-ATPase, ATP synthesis, and DNA polymerases. "
        "Regulates K+ and Ca2+ transport - hypomagnesaemia causes refractory hypokalemia and hypocalcaemia. "
        "Absorbed in the small intestine; excreted primarily by the kidneys.",
        styles["body"]))
    story.append(Spacer(1, 3*mm))

    story.append(section_box("HYPOMAGNESEMIA  (Mg2+ < 1.7 mg/dL)", C_PURPLE, styles))
    story.append(Spacer(1, 2*mm))
    mg_l = [
        "<b>INADEQUATE INTAKE:</b> Malnutrition, alcoholism, prolonged TPN without Mg",
        "<b>INCREASED GI LOSSES:</b> Severe diarrhea, vomiting, NG suction, malabsorption, short bowel",
        "<b>INCREASED RENAL LOSSES:</b>",
        "Diuretics (loop, thiazides)",
        "DKA / osmotic diuresis",
        "Hyperaldosteronism, SIADH",
        "Hypercalcaemia (competes with Mg2+ for transport)",
        "Post-transplant: cyclosporine, tacrolimus",
        "EGF receptor blockers (cetuximab, panitumumab)",
        "Cisplatin, amphotericin B",
        "Gitelman's syndrome (SLC12A3 mutation)",
    ]
    mg_r = [
        "<b>CLINICAL FEATURES:</b>",
        "Neuromuscular: tremor, tetany, positive Chvostek & Trousseau signs",
        "Refractory hypokalemia and hypocalcaemia",
        "Cardiac: atrial/ventricular arrhythmias, prolonged QT, torsades de pointes",
        "ECG: peaked T waves (early), wide QRS, prolonged QT",
        "Hypertension",
        "CNS: agitation, confusion, hallucinations",
        "Severe (&lt;0.5 mEq/L): generalised seizures, coma",
        "N.B.: K+ and Ca2+ must be supplemented together with Mg2+",
    ]
    story.append(two_col_table(mg_l, mg_r, styles, "Causes", "Clinical Features", C_PURPLE))
    story.append(Spacer(1, 3*mm))

    hypoMg_rows = [
        ("Severe / symptomatic\n(arrhythmia, seizures)",
         "IV MgSO4",
         "1-2 g (8-16 mEq) IV over 15 min. Then 6 g over next 24 h. "
         "For torsades de pointes: 2 g IV bolus over 1-2 min. "
         "Monitor deep tendon reflexes, urine output, and respiratory rate."),
        ("Moderate / asymptomatic",
         "Oral Mg",
         "Mg oxide 400-800 mg/day or Mg gluconate 500-1000 mg TID. "
         "Oral Mg causes diarrhoea at high doses; titrate to tolerance."),
        ("Concurrent hypokalemia",
         "Correct Mg FIRST",
         "Hypokalemia is refractory until Mg2+ is replete. "
         "Always check and correct Mg2+ before/during K+ replacement."),
        ("Concurrent hypocalcaemia",
         "Correct Mg FIRST",
         "Mg2+ deficiency impairs PTH secretion and action. "
         "Hypocalcaemia will not respond until Mg2+ is corrected."),
        ("Renal failure\n(caution)",
         "Reduce dose",
         "Reduce Mg2+ replacement by 25-50% in CKD. Monitor levels. "
         "Risk of hypermagnesaemia."),
    ]
    story.append(management_table(hypoMg_rows, styles, C_PURPLE))
    story.append(Spacer(1, 3*mm))

    story.append(section_box("HYPERMAGNESEMIA  (Mg2+ > 2.2 mg/dL)", C_PURPLE, styles))
    story.append(Spacer(1, 2*mm))
    hMg_l = [
        "Exogenous: IV/IM MgSO4 in eclampsia treatment (most common iatrogenic cause)",
        "Mg2+-containing antacids, laxatives, cathartics in renal failure",
        "Magnesium enemas in renal failure",
        "Renal failure (decreased excretion)",
        "Adrenal insufficiency",
        "Hypothyroidism",
        "Lithium therapy",
        "Familial hypocalciuric hypercalcaemia",
    ]
    hMg_r = [
        "&lt;4 mg/dL: Nausea, flushing, headache",
        "4-6 mg/dL: Loss of deep tendon reflexes (first sign of toxicity)",
        "6-8 mg/dL: Somnolence, hypotension, ECG changes (prolonged PR, QRS, QT)",
        "&gt;8 mg/dL: Respiratory paralysis",
        "&gt;15 mg/dL: Cardiac arrest",
        "ECG: Bradycardia, prolonged PR, wide QRS, heart block",
        "Paradoxically: may cause hypocalcaemia (suppresses PTH)",
    ]
    story.append(two_col_table(hMg_l, hMg_r, styles, "Causes", "Clinical Features", C_PURPLE))
    story.append(Spacer(1, 3*mm))

    hyperMg_rows = [
        ("Cardiac protection\n(ECG changes / respiratory depression)",
         "Calcium gluconate 10%",
         "1-2 g IV over 5-10 min. Directly antagonises Mg2+ membrane effects. "
         "Temporary measure until Mg2+ is eliminated."),
        ("Enhance Mg2+ excretion\n(adequate renal function)",
         "IV Normal Saline + Furosemide",
         "Aggressive saline diuresis: 1-2 L NS over 2-4 h. "
         "Furosemide 40-80 mg IV promotes Mg2+ excretion. "
         "Monitor electrolytes during diuresis."),
        ("Severe / renal failure",
         "Haemodialysis",
         "Most effective method. Indicated for Mg2+ &gt;8 mg/dL with symptoms, "
         "or in renal failure where diuresis is not possible."),
        ("Stop source",
         "Discontinue Mg2+ containing drugs",
         "Stop Mg2+ supplements, antacids, laxatives, IV MgSO4. "
         "Treat underlying cause (e.g., discontinue obstetric MgSO4 infusion)."),
    ]
    story.append(management_table(hyperMg_rows, styles, C_PURPLE))
    story.append(PageBreak())


# ────────────────────────────────────────────────────────────────────────────
# 5. PHOSPHATE
# ────────────────────────────────────────────────────────────────────────────

def phosphate_section(story, styles):
    add_electrolyte_header(story,
        "5. PHOSPHATE (PO4)",
        "Normal: 2.5-4.5 mg/dL (0.8-1.5 mM)  |  Critical for energy metabolism, bone mineralisation",
        C_YELLOW, styles)

    story.append(section_box("PHYSIOLOGY", C_YELLOW, styles))
    story.append(Spacer(1, 2*mm))
    story.append(Paragraph(
        "Phosphate is the primary intracellular anion. Critical for ATP synthesis, 2,3-DPG "
        "(O2 release from Hb), bone mineralisation (hydroxyapatite), and DNA/RNA structure. "
        "Regulated by PTH (promotes renal excretion), 1,25(OH)2D (promotes intestinal absorption), "
        "and FGF-23 (promotes renal excretion). ~80% of body's phosphate is in bone.",
        styles["body"]))
    story.append(Spacer(1, 3*mm))

    story.append(section_box("HYPOPHOSPHATEMIA  (PO4 < 2.5 mg/dL)", C_YELLOW, styles))
    story.append(Spacer(1, 2*mm))
    po4_l = [
        "<b>REDISTRIBUTION INTO CELLS:</b>",
        "Refeeding syndrome (insulin-driven cellular uptake of phosphate)",
        "DKA treatment (insulin drives PO4 into cells)",
        "Respiratory alkalosis",
        "Hungry bone syndrome (post-parathyroidectomy)",
        "<b>DECREASED INTESTINAL ABSORPTION:</b>",
        "Malnutrition, malabsorption (Crohn's, celiac)",
        "Phosphate binders (calcium carbonate, sevelamer, Al3+ antacids)",
        "Vitamin D deficiency",
        "<b>INCREASED RENAL LOSSES:</b>",
        "Hyperparathyroidism",
        "Fanconi syndrome, RTA",
        "X-linked hypophosphataemia (PHEX mutation)",
        "Oncogenic osteomalacia (FGF-23 excess)",
        "Alcoholism",
    ]
    po4_r = [
        "<b>CLINICAL FEATURES:</b>",
        "<b>Severe (&lt;1 mg/dL):</b>",
        "Haemolytic anaemia (RBC fragility due to low ATP)",
        "Respiratory failure (diaphragm weakness; low 2,3-DPG shifts O2 curve left)",
        "Rhabdomyolysis",
        "Cardiac dysfunction (reduced contractility)",
        "Encephalopathy, irritability, numbness",
        "Seizures, coma",
        "<b>Moderate:</b>",
        "Muscle weakness, myalgia",
        "Bone pain, osteomalacia (chronic)",
        "Impaired phagocytosis (immune dysfunction)",
        "Peripheral neuropathy",
    ]
    story.append(two_col_table(po4_l, po4_r, styles, "Causes", "Clinical Features", C_YELLOW))
    story.append(Spacer(1, 3*mm))

    hypopo4_rows = [
        ("Mild-moderate\nasymptomatic\n(1.0-2.5 mg/dL)",
         "Oral phosphate",
         "Neutral Sodium/Potassium phosphate (Neutra-Phos): 500-1000 mg PO TID-QID. "
         "Skim milk: 1 g PO4 per litre. "
         "Oral preferred; diarrhoea is dose-limiting side effect."),
        ("Severe symptomatic\n(&lt;1.0 mg/dL)",
         "IV Sodium or Potassium phosphate",
         "0.08-0.32 mmol/kg IV over 4-6 h. "
         "Sodium phosphate or K-phosphate (use K-phos if concurrent hypokalemia). "
         "Monitor Ca2+, K+, Mg2+ during infusion (risk of hypocalcaemia with rapid infusion)."),
        ("Refeeding syndrome\n(prevention)",
         "Gradual caloric increase + PO4 supplementation",
         "Introduce nutrition slowly (10 kcal/kg/day, increase over 5-7 days). "
         "Supplement PO4, K+, Mg2+, and thiamine before and during refeeding."),
        ("Vitamin D deficiency",
         "Vitamin D3 / Calcitriol",
         "Cholecalciferol 1000-4000 IU/day. "
         "Correct underlying deficiency to improve intestinal PO4 absorption."),
        ("X-linked\nhypophosphataemia",
         "Burosumab (anti-FGF23 antibody)",
         "0.8 mg/kg SC q2 weeks (adults). "
         "OR conventional: oral phosphate + calcitriol."),
    ]
    story.append(management_table(hypopo4_rows, styles, C_YELLOW))
    story.append(Spacer(1, 3*mm))

    story.append(section_box("HYPERPHOSPHATEMIA  (PO4 > 4.5 mg/dL)", C_YELLOW, styles))
    story.append(Spacer(1, 2*mm))
    hyperpo4_l = [
        "Chronic kidney disease (most common) - reduced renal excretion",
        "Acute kidney injury (AKI) - oliguric phase",
        "Hypoparathyroidism (reduced PTH-driven excretion)",
        "Pseudohypoparathyroidism",
        "Massive cellular release: tumour lysis syndrome, rhabdomyolysis, haemolysis",
        "Excessive exogenous intake: Phosphate-containing laxatives, enemas, TPN",
        "Vitamin D intoxication",
        "Acromegaly, thyrotoxicosis",
    ]
    hyperpo4_r = [
        "Often asymptomatic acutely",
        "Hypocalcaemia (Ca-PO4 precipitation): tetany, seizures",
        "Metastatic calcification in soft tissues, vessels, cornea, lungs",
        "Ca x PO4 product &gt;55 = risk of vascular calcification (coronary artery disease risk)",
        "Pruritus (from Ca-PO4 skin deposits in CKD)",
        "Renal osteodystrophy (secondary hyperparathyroidism)",
        "Vascular calcification - major cause of CV mortality in CKD",
    ]
    story.append(two_col_table(hyperpo4_l, hyperpo4_r, styles, "Causes", "Clinical Features", C_YELLOW))
    story.append(Spacer(1, 3*mm))

    hyperpo4_rows = [
        ("CKD - Dietary",
         "Low-phosphate diet",
         "Restrict PO4 to &lt;800-1000 mg/day. "
         "Avoid dairy, cola drinks, processed foods, nuts."),
        ("With each meal\n(CKD / hyperphosphataemia)",
         "Phosphate binders",
         "Calcium carbonate 500-1500 mg with meals (raises Ca, risk if Ca x PO4 high). "
         "Sevelamer 800-1600 mg TID with meals (non-calcium binder; preferred in vascular disease). "
         "Lanthanum carbonate 250-1000 mg TID. "
         "Al3+ hydroxide (short-term only; neurotoxic)."),
        ("Secondary\nhyperparathyroidism\n(CKD)",
         "Active Vitamin D / Cinacalcet",
         "Calcitriol 0.25-0.5 mcg/day suppresses PTH. "
         "Cinacalcet 30-90 mg/day (calcimimetic - reduces PTH without raising Ca or PO4). "
         "Paricalcitol / doxercalciferol (synthetic Vit D analogues)."),
        ("Severe / AKI /\ntumour lysis",
         "Dialysis",
         "Haemodialysis or CRRT. "
         "Indications: severe hyperphosphataemia with renal failure or life-threatening hypocalcaemia."),
        ("Hypoparathyroidism",
         "PTH replacement / Ca + calcitriol",
         "Increase Ca2+ to compensate for low PTH-driven excretion. "
         "Thiazide diuretics reduce urine Ca excretion (useful in hypoparathyroidism)."),
    ]
    story.append(management_table(hyperpo4_rows, styles, C_YELLOW))
    story.append(PageBreak())


# ────────────────────────────────────────────────────────────────────────────
# Build the document
# ────────────────────────────────────────────────────────────────────────────

def build_pdf(output_path):
    doc = SimpleDocTemplate(
        output_path,
        pagesize=A4,
        rightMargin=2*cm,
        leftMargin=2*cm,
        topMargin=2*cm,
        bottomMargin=2*cm,
        title="Electrolyte Disorders - Short Notes",
        author="Orris Medical AI | Harrison's 22E + Tintinalli's EM"
    )

    styles = build_styles()
    story = []

    cover_page(story, styles)
    sodium_section(story, styles)
    potassium_section(story, styles)
    calcium_section(story, styles)
    magnesium_section(story, styles)
    phosphate_section(story, styles)

    doc.build(story)
    print(f"PDF saved to: {output_path}")


if __name__ == "__main__":
    build_pdf("/tmp/workspace/electrolytes/Electrolyte_Short_Notes.pdf")
Running Command

cd /tmp/workspace/electrolytes && python generate_pdf.py 2>&1

Running Command

ls -lh /tmp/workspace/electrolytes/

Your PDF is ready for download:

Electrolyte Short Notes

PDF Document · PDF


Here is a summary of what the PDF contains, sourced directly from Harrison's Principles of Internal Medicine 22E (2025) and Tintinalli's Emergency Medicine, 9th Edition:

What's Inside (10 Short Notes across 5 Electrolytes)

1. SODIUM (Na+) - 2 notes

Hyponatremia (Na+ <135 mEq/L)
  • Classification by volume status: hypovolemic / euvolemic (SIAD) / hypervolemic
  • Diagnostic approach: urine Na+, urine osmolality, urine-to-plasma electrolyte ratio
  • Management table: 3% hypertonic saline for acute symptomatic, tolvaptan for SIAD, isotonic saline for hypovolemic, fluid restriction for SIAD
  • Correction limit: <8-10 mEq/L in 24h, <18 mEq/L in 48h to prevent ODS (osmotic demyelination syndrome)
Hypernatremia (Na+ >145 mEq/L)
  • Causes: water loss, excess Na+, central/nephrogenic DI
  • Correction: max 1 mEq/L/h, max 10-12 mEq/L in 24h
  • Formula: Free Water Deficit = 4 x weight (kg) x desired Na+ fall
  • DDAVP for central DI; thiazide + amiloride for nephrogenic DI

2. POTASSIUM (K+) - 2 notes

Hypokalemia (K+ <3.5 mEq/L)
  • ECG staging: U waves → ST depression → ventricular arrhythmias
  • Key: Always correct hypomagnesaemia first (refractory hypokalemia until Mg2+ replete)
  • IV KCl: 0.2-0.3 mEq/kg/h; max 0.5 mEq/kg/h if urgent
  • Deficit estimate: K+ drops 0.27 mM per 100 mmol total body K+ loss
Hyperkalemia (K+ >5.5 mEq/L)
  • 5-step approach: Stabilise (calcium gluconate) → Shift (insulin+dextrose, salbutamol) → Remove (diuretics, resins, dialysis) → Prevent → Monitor
  • ECG progression: peaked T waves → wide QRS → sine wave → VF

3. CALCIUM (Ca2+) - 2 notes

  • Corrected Ca2+ formula, Bones-Stones-Groans-Moans for hypercalcaemia
  • Bisphosphonates (zoledronic acid), denosumab, glucocorticoids for hypercalcaemia
  • Calcium gluconate IV for acute symptomatic hypocalcaemia; Chvostek & Trousseau signs

4. MAGNESIUM (Mg2+) - 2 notes

  • Loss of deep tendon reflexes = first sign of hypermagnesaemia toxicity
  • MgSO4 2g IV for torsades de pointes
  • Refractory hypokalemia and hypocalcaemia: correct Mg2+ first

5. PHOSPHATE (PO4) - 2 notes

  • Severe hypophosphataemia (<1 mg/dL): haemolytic anaemia, respiratory failure, rhabdomyolysis
  • Refeeding syndrome prevention protocol
  • CKD hyperphosphataemia: sevelamer vs calcium carbonate as phosphate binders
This is a shared conversation. Sign in to Orris to start your own chat.