Discuss the metabolism of bilirubin and types of jaundice

Reading File
Finding Sources
Finding Sources
Searching PubMed

bilirubin metabolism jaundice

Reading File
Reading File
Reading File
Reading File
Reading File
Loading Image
Reading File
Loading Image
Reading File
Reading File
I now have all the information needed for a comprehensive, well-cited response. Let me compile the full answer.

Bilirubin Metabolism and Types of Jaundice

1. Overview of Bilirubin

Bilirubin is the end product of heme catabolism. Approximately 70-90% is derived from the degradation of hemoglobin in senescent red blood cells; the remainder comes from myoglobin, cytochromes, and ineffective erythropoiesis. Normal total plasma bilirubin is <1 mg/dL, with the unconjugated fraction comprising the majority. - Harrison's Principles of Internal Medicine, 22E

2. Bilirubin Production: Heme to Bilirubin

Conversion of heme to bilirubin via heme oxygenase and biliverdin reductase
Fig. 28-5 from Ganong's Review of Medical Physiology: Two-step conversion of heme to bilirubin.
The breakdown occurs in two enzymatic steps:
  1. Heme oxygenase opens the porphyrin ring of heme, using NADPH + O₂, releasing CO, Fe³⁺, and the green pigment biliverdin (IXα).
  2. Biliverdin reductase reduces biliverdin to the yellow-orange bilirubin (IXα), consuming NADPH.
Unconjugated (free) bilirubin is water-insoluble and is held in its conformation by internal hydrogen bonding. It is potentially toxic (especially to neurons) and must be transported and conjugated before excretion. - Ganong's Review of Medical Physiology, 26th Ed.

3. Transport in the Blood

Unconjugated bilirubin (UCB) is tightly bound to serum albumin for transport in the circulation. This binding prevents glomerular filtration - therefore, unconjugated bilirubin does not appear in the urine. It traverses endothelial fenestrae to reach the hepatocyte surface (Space of Disse). - Harrison's 22E

4. Hepatic Processing: Four Key Steps

Hepatocellular bilirubin transport diagram - OATP, GST, UGT1A1, MRP2, MRP3 transporters
Hepatocellular bilirubin transport (Harrison's 22E, Fig. 349-1)

Step 1 - Hepatocellular Uptake

UCB dissociates from albumin and is taken up into hepatocytes via organic anion transporting polypeptides (OATPs) - particularly OATP1B1 and OATP1B3. The identity of the primary bilirubin transporter remains incompletely defined. Certain drugs (rifampicin, flavaspidic acid, novobiocin, cholecystographic contrast agents) can competitively inhibit this uptake. - Harrison's 22E

Step 2 - Intracellular Binding

Inside the hepatocyte, UCB is kept in solution by binding as a non-substrate ligand to glutathione-S-transferases (GSTs), formerly called ligandins. This prevents back-diffusion into the sinusoid. - Harrison's 22E

Step 3 - Conjugation (the critical step)

Bilirubin is conjugated with glucuronic acid by bilirubin-UDP-glucuronosyltransferase (UGT1A1), located in the smooth endoplasmic reticulum. Each bilirubin molecule reacts with two UDP-glucuronic acid (UDPGA) molecules to form:
  • Bilirubin monoglucuronide (BMG) - intermediate
  • Bilirubin diglucuronide (BDG) - the predominant excreted form
Conjugation disrupts the internal hydrogen bonds, making the molecule water-soluble. This is obligatory for excretion into bile. The UGT1A1 gene is part of the UGT1 gene complex on chromosome 2; mutations in the shared exons (2-5) affect all UGT1 isoforms, while mutations in the substrate-specific first exon (A1) affect only UGT1A1 - the basis for Crigler-Najjar and Gilbert syndromes. - Harrison's 22E; Ganong's 26th Ed.

Step 4 - Biliary Excretion

BDG and BMG are actively transported across the canalicular membrane into bile by MRP2 (multidrug resistance-associated protein 2, ABCC2) - an ATP-dependent transporter. A portion of the conjugates is also transported back into the portal circulation via MRP3 and then reuptaken by OATP1B1/1B3 (the hepatocyte-portal cycling pathway implicated in Rotor syndrome). - Harrison's 22E

5. Intestinal Fate and Enterohepatic Circulation

Once conjugated bilirubin reaches the duodenum:
  • The intestinal mucosa is impermeable to conjugated bilirubin (so it does not get directly reabsorbed)
  • Gut bacteria (in the terminal ileum and colon) convert it to urobilinogens - a series of colorless, water-soluble compounds
  • ~10-20% of urobilinogens are reabsorbed into the portal circulation - this is the enterohepatic circulation of bilirubin
  • Most reabsorbed urobilinogen is re-excreted by the liver
  • A small amount reaches the systemic circulation and is excreted in the urine as urobilinogen
  • The oxidized form, urobilin (stercobilin), gives stool its brown color
In biliary obstruction, no bilirubin reaches the gut → stool becomes pale/clay-colored and urobilinogen disappears from urine. - Ganong's 26th Ed.; Harrison's 22E

6. Renal Handling

  • Unconjugated bilirubin: NOT excreted in urine (too tightly albumin-bound; no tubular secretion mechanism)
  • Conjugated bilirubin: Filtered at the glomerulus and excreted in urine → bilirubinuria (dark urine) is a sign of conjugated hyperbilirubinemia

7. Jaundice (Icterus)

Jaundice is the yellow discoloration of skin, sclera, and mucous membranes caused by bilirubin deposition. It becomes clinically detectable when total plasma bilirubin exceeds 2 mg/dL (34 μmol/L). The sclera is affected earliest due to its high elastin content, which has a high affinity for bilirubin. - Ganong's 26th Ed.

8. Classification of Jaundice

A. Pre-hepatic (Hemolytic) Jaundice

Mechanism: Excess bilirubin production overwhelms normal hepatic conjugation capacity.
Causes:
  • Hemolytic anemias (sickle cell, hereditary spherocytosis, G6PD deficiency, autoimmune hemolysis)
  • Ineffective erythropoiesis (thalassemia major, megaloblastic anemia, lead poisoning, dyserythropoietic anemias)
  • Massive tissue hematomas/infarctions
  • Transfusion reactions
Key features:
  • Predominantly unconjugated hyperbilirubinemia
  • Bilirubin rarely exceeds 4 mg/dL with normal liver function (the liver can increase conjugation 8-fold, but cannot exceed this indefinitely)
  • No bilirubinuria (unconjugated bilirubin is not filtered)
  • Increased urobilinogen in urine and stool
  • Pigment gallstones (bilirubin stones) may form with chronic hemolysis
  • Reticulocytosis, anemia, raised LDH, low haptoglobin
  • Harrison's 22E

B. Hepatic (Hepatocellular) Jaundice

Mechanism: Damaged hepatocytes fail to take up, conjugate, or excrete bilirubin efficiently. Usually produces mixed (both conjugated and unconjugated) hyperbilirubinemia.
Causes:
  • Acute viral hepatitis (A, B, C, E)
  • Alcoholic hepatitis
  • Drug-induced liver injury (acetaminophen, isoniazid, etc.)
  • Autoimmune hepatitis
  • Cirrhosis
  • Leptospirosis, sepsis
Key features:
  • Mixed hyperbilirubinemia - both direct and indirect elevated
  • Bilirubinuria present (conjugated fraction filters)
  • Reduced urobilinogen (liver cannot re-excrete it from the portal blood)
  • Raised transaminases (AST, ALT) markedly elevated
  • Abnormal liver function tests (PT, albumin)
Inherited hepatocellular disorders are classified separately:
ConditionDefectBilirubin TypeFeatures
Gilbert SyndromeReduced UGT1A1 expression (A[TA]₇TAA promoter polymorphism) + mildly reduced uptakeUnconjugatedBenign, intermittent, triggered by fasting/stress; prevalence ~5-10%
Crigler-Najjar Type I (CN-I)Complete absence of UGT1A1Severe unconjugatedLife-threatening; kernicterus without phototherapy/transplant
Crigler-Najjar Type II (CN-II)Severely reduced UGT1A1 (<10% of normal)UnconjugatedLess severe; responds to phenobarbital
Dubin-Johnson Syndrome (DJS)Absent MRP2 (ABCC2) - defective canalicular excretionPredominantly conjugatedBenign; black liver pigment; typical BSP excretion pattern; worsened by pregnancy/OCPs
Rotor SyndromeAbsent OATP1B1 + OATP1B3 - defective hepatic reuptake of secreted conjugatesPredominantly conjugatedBenign; normal liver histology; no pigment deposits
  • Harrison's 22E

C. Post-hepatic (Obstructive/Cholestatic) Jaundice

Mechanism: Mechanical obstruction to bile flow causes conjugated bilirubin to regurgitate back into the bloodstream.
Causes:
  • Intrahepatic cholestasis: Primary biliary cholangitis (PBC), primary sclerosing cholangitis (PSC), intrahepatic cholestasis of pregnancy, drug-induced cholestasis, TPN-associated cholestasis
  • Extrahepatic obstruction: Choledocholithiasis (CBD stones), carcinoma of the head of pancreas, cholangiocarcinoma, ampullary carcinoma, biliary strictures, pancreatitis, lymph node compression
Key features:
  • Predominantly conjugated hyperbilirubinemia
  • Dark urine (bilirubinuria) - conjugated bilirubin is water-soluble and renally excreted
  • Pale/clay-colored stools - no bilirubin reaching the gut
  • No urobilinogen in urine or stool
  • Pruritus - bile salts deposited in skin
  • Raised alkaline phosphatase (ALP) and GGT - markers of biliary obstruction (much greater rise than in hepatocellular disease)
  • Steatorrhea - fat malabsorption due to lack of bile salts
  • Fat-soluble vitamin deficiency (A, D, E, K) - vitamin K deficiency causes coagulopathy
  • Dilated bile ducts on imaging
  • Ganong's 26th Ed.; Schwartz's Principles of Surgery, 11th Ed.

9. Neonatal (Physiological) Jaundice

A special category: most neonates develop mild unconjugated hyperbilirubinemia in the first week of life because:
  • Low UGT1A1 activity at birth
  • Underdeveloped intestinal flora - bacteria that convert bilirubin to urobilinogen are absent, so unconjugated bilirubin undergoes enterohepatic recirculation
  • Higher RBC turnover in newborns
The condition is normally self-limiting. Severe cases (>20 mg/dL in term infants) risk kernicterus (bilirubin deposition in the basal ganglia). Treatment is with phototherapy (blue light isomerizes unconjugated bilirubin to a water-soluble form) or exchange transfusion. - Harrison's 22E

10. Quick Differential Summary

FeaturePre-hepaticHepaticPost-hepatic
Bilirubin typeUnconjugatedMixedConjugated
Urine bilirubinAbsentPresentPresent
Urine urobilinogenIncreasedDecreasedAbsent
Stool colorNormal/darkNormal/palePale (clay)
ALP / GGTNormalMildly raisedMarkedly raised
ALT / ASTNormalMarkedly raisedMildly raised
PruritusNoVariableYes

Sources:
  • Harrison's Principles of Internal Medicine, 22E (2025), Chap. 349
  • Ganong's Review of Medical Physiology, 26th Ed., Chap. 28
  • Schwartz's Principles of Surgery, 11th Ed., Chap. 15

Generate a clinical summary PDF on diagnosing jaundice with decision trees

Reading File
Running Command

ls -la /home/daytona/workspace/ 2>/dev/null || mkdir -p /home/daytona/workspace && echo "workspace ready"

Running Command

mkdir -p /home/daytona/workspace/jaundice-pdf && echo "dir created"

Writing File

~/jaundice-pdf/generate_jaundice_pdf.py

"""
Clinical Summary PDF: Diagnosing Jaundice with Decision Trees
Uses reportlab for professional layout with embedded decision tree diagrams drawn programmatically.
"""

from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import mm, cm
from reportlab.lib.colors import (
    HexColor, black, white, Color
)
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
    HRFlowable, KeepTogether, PageBreak
)
from reportlab.platypus.flowables import Flowable
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_RIGHT, TA_JUSTIFY
from reportlab.graphics.shapes import (
    Drawing, Rect, String, Line, Polygon, Group, Circle
)
from reportlab.graphics.renderPDF import draw as renderDraw
from reportlab.graphics import renderPDF
from reportlab.platypus import Image as RLImage
import io

# ─────────────────────────── COLOURS ────────────────────────────
C_NAVY      = HexColor("#1A2E4A")
C_TEAL      = HexColor("#0E7C7B")
C_AMBER     = HexColor("#E8A838")
C_RED       = HexColor("#C0392B")
C_GREEN     = HexColor("#27AE60")
C_LIGHT_BG  = HexColor("#F0F4F8")
C_BORDER    = HexColor("#CBD5E0")
C_TEXT      = HexColor("#2D3748")
C_SUBTEXT   = HexColor("#718096")
C_YELLOW_BG = HexColor("#FFFBEB")
C_BLUE_BG   = HexColor("#EBF8FF")
C_GREEN_BG  = HexColor("#F0FFF4")
C_RED_BG    = HexColor("#FFF5F5")
C_ORANGE    = HexColor("#D97706")
C_PURPLE    = HexColor("#6B46C1")
C_PRE_HEPATIC = HexColor("#D97706")
C_HEPATIC     = HexColor("#2B6CB0")
C_POST_HEPATIC= HexColor("#276749")
C_BOX_PRE   = HexColor("#FFFBEB")
C_BOX_HEP   = HexColor("#EBF8FF")
C_BOX_POST  = HexColor("#F0FFF4")

W, H = A4  # 595.28 x 841.89 pts

# ─────────────────────────── STYLES ─────────────────────────────
styles = getSampleStyleSheet()

def make_style(name, parent="Normal", **kwargs):
    s = ParagraphStyle(name, parent=styles[parent], **kwargs)
    return s

S_TITLE    = make_style("Title2", fontSize=22, fontName="Helvetica-Bold",
                         textColor=C_NAVY, alignment=TA_CENTER, spaceAfter=4)
S_SUBTITLE = make_style("Subtitle2", fontSize=11, fontName="Helvetica",
                         textColor=C_TEAL, alignment=TA_CENTER, spaceAfter=2)
S_DATE     = make_style("Date2", fontSize=9, fontName="Helvetica",
                         textColor=C_SUBTEXT, alignment=TA_CENTER, spaceAfter=12)
S_H1       = make_style("H1", fontSize=13, fontName="Helvetica-Bold",
                         textColor=white, spaceAfter=6, spaceBefore=10,
                         leftIndent=6, rightIndent=6)
S_H2       = make_style("H2", fontSize=11, fontName="Helvetica-Bold",
                         textColor=C_NAVY, spaceAfter=4, spaceBefore=8,
                         borderPad=3)
S_BODY     = make_style("Body2", fontSize=9, fontName="Helvetica",
                         textColor=C_TEXT, spaceAfter=4, leading=14)
S_BODY_SMALL = make_style("BodySm", fontSize=8, fontName="Helvetica",
                           textColor=C_TEXT, spaceAfter=3, leading=12)
S_BULLET   = make_style("Bullet2", fontSize=9, fontName="Helvetica",
                         textColor=C_TEXT, leftIndent=14, firstLineIndent=-10,
                         spaceAfter=2, leading=13)
S_CAPTION  = make_style("Caption2", fontSize=8, fontName="Helvetica-Oblique",
                         textColor=C_SUBTEXT, alignment=TA_CENTER, spaceAfter=4)
S_TABLE_H  = make_style("TableH", fontSize=8, fontName="Helvetica-Bold",
                         textColor=white, alignment=TA_CENTER)
S_TABLE_C  = make_style("TableC", fontSize=8, fontName="Helvetica",
                         textColor=C_TEXT, alignment=TA_CENTER, leading=11)
S_TABLE_L  = make_style("TableL", fontSize=8, fontName="Helvetica",
                         textColor=C_TEXT, alignment=TA_LEFT, leading=11)
S_WARN     = make_style("Warn", fontSize=9, fontName="Helvetica-Bold",
                         textColor=C_RED, spaceAfter=4)
S_NOTE     = make_style("Note", fontSize=8, fontName="Helvetica-Oblique",
                         textColor=C_SUBTEXT, spaceAfter=6)


# ═══════════════════════════════════════════════════════════════
#  CUSTOM FLOWABLES
# ═══════════════════════════════════════════════════════════════

class SectionHeader(Flowable):
    """Coloured section header bar."""
    def __init__(self, text, color=C_NAVY, width=None, height=24):
        super().__init__()
        self.text = text
        self.color = color
        self._width = width
        self.height = height

    def wrap(self, aw, ah):
        self._avail = aw
        w = self._width or aw
        return w, self.height

    def draw(self):
        w = self._width or self._avail
        c = self.canv
        c.setFillColor(self.color)
        c.roundRect(0, 0, w, self.height, 4, fill=1, stroke=0)
        c.setFillColor(white)
        c.setFont("Helvetica-Bold", 11)
        c.drawString(10, 7, self.text)


class DecisionTreeFlowable(Flowable):
    """
    Draws a vertical decision tree for jaundice diagnosis.
    The tree has a root node (question), two branches (yes/no),
    then sub-branches.
    """
    def __init__(self, width=None, height=None):
        super().__init__()
        self._w = width or (W - 80)
        self._h = height or 340

    def wrap(self, aw, ah):
        self._avail = aw
        return self._w, self._h

    def draw(self):
        c = self.canv
        w = self._w
        # ── helper functions ──────────────────────────────────────
        def box(x, y, bw, bh, fill_color, stroke_color, text,
                font="Helvetica", fsize=8, text_color=white, radius=5):
            c.setFillColor(fill_color)
            c.setStrokeColor(stroke_color)
            c.setLineWidth(1)
            c.roundRect(x, y, bw, bh, radius, fill=1, stroke=1)
            c.setFillColor(text_color)
            c.setFont(font, fsize)
            # wrap text manually
            lines = text.split('\n')
            total_h = len(lines) * (fsize + 2)
            start_y = y + bh/2 + total_h/2 - (fsize + 2)
            for i, line in enumerate(lines):
                c.drawCentredString(x + bw/2, start_y - i*(fsize+2), line)

        def diamond(x, y, dw, dh, fill_color, stroke_color, text,
                    fsize=8, text_color=white):
            pts = [x+dw/2, y+dh, x+dw, y+dh/2, x+dw/2, y, x, y+dh/2]
            c.setFillColor(fill_color)
            c.setStrokeColor(stroke_color)
            c.setLineWidth(1.2)
            c.setDash([])
            p = c.beginPath()
            p.moveTo(pts[0], pts[1])
            p.lineTo(pts[2], pts[3])
            p.lineTo(pts[4], pts[5])
            p.lineTo(pts[6], pts[7])
            p.close()
            c.drawPath(p, fill=1, stroke=1)
            c.setFillColor(text_color)
            c.setFont("Helvetica-Bold", fsize)
            lines = text.split('\n')
            for i, line in enumerate(lines):
                offset = (len(lines)-1)/2 - i
                c.drawCentredString(x + dw/2, y + dh/2 - fsize/2 + offset*(fsize+2), line)

        def arrow(x1, y1, x2, y2, label="", label_side="right"):
            c.setStrokeColor(C_NAVY)
            c.setLineWidth(1.2)
            c.setFillColor(C_NAVY)
            c.setDash([])
            c.line(x1, y1, x2, y2)
            # arrowhead
            import math
            angle = math.atan2(y2-y1, x2-x1)
            al = 7
            aw2 = 3
            ax1 = x2 - al*math.cos(angle) + aw2*math.sin(angle)
            ay1 = y2 - al*math.sin(angle) - aw2*math.cos(angle)
            ax2 = x2 - al*math.cos(angle) - aw2*math.sin(angle)
            ay2 = y2 - al*math.sin(angle) + aw2*math.cos(angle)
            p = c.beginPath()
            p.moveTo(x2, y2)
            p.lineTo(ax1, ay1)
            p.lineTo(ax2, ay2)
            p.close()
            c.drawPath(p, fill=1, stroke=0)
            if label:
                c.setFillColor(C_SUBTEXT)
                c.setFont("Helvetica-Oblique", 7)
                if label_side == "right":
                    c.drawString(x2+3, (y1+y2)/2, label)
                elif label_side == "left":
                    c.drawRightString(x2-3, (y1+y2)/2, label)
                else:
                    c.drawCentredString((x1+x2)/2, (y1+y2)/2+4, label)

        def horiz_line(x1, y, x2):
            c.setStrokeColor(C_NAVY)
            c.setLineWidth(1.2)
            c.line(x1, y, x2, y)

        # ── layout constants ──────────────────────────────────────
        TOP = self._h - 10
        bw_root = 260; bh_root = 32
        bw_q = 160;    bh_q = 36
        bw_leaf = 140; bh_leaf = 46
        dw = 140;      dh = 36

        # ─────────── Row 1: ROOT ──────────────────────────────────
        rx = w/2 - bw_root/2
        ry = TOP - bh_root
        box(rx, ry, bw_root, bh_root, C_NAVY, C_NAVY,
            "Patient presents with JAUNDICE", "Helvetica-Bold", 10)

        # arrow down
        arrow(w/2, ry, w/2, ry - 22)

        # ─────────── Row 2: Q1 diamond ────────────────────────────
        q1y = ry - 22 - dh
        q1x = w/2 - dw/2
        diamond(q1x, q1y, dw, dh, C_TEAL, C_TEAL,
                "Is bilirubin\nconjugated?", 8)

        arrow_top = q1y + dh/2

        # ─────────── Row 3: two branches ─────────────────────────
        branch_y = q1y - 30

        # LEFT branch: Unconjugated (NO)
        left_cx = w * 0.20
        left_box_x = left_cx - bw_q/2
        left_box_y = branch_y - bh_q

        # RIGHT branch: Conjugated (YES)
        right_cx = w * 0.80
        right_box_x = right_cx - bw_q/2
        right_box_y = branch_y - bh_q

        # horizontal line from diamond to branches
        horiz_line(left_cx, branch_y, right_cx, branch_y)
        # vertical from diamond to horiz
        c.setStrokeColor(C_NAVY); c.setLineWidth(1.2)
        c.line(w/2, q1y, w/2, branch_y)
        # vertical drops to boxes
        c.line(left_cx, branch_y, left_cx, left_box_y + bh_q)
        c.line(right_cx, branch_y, right_cx, right_box_y + bh_q)

        # labels on horizontal
        c.setFillColor(C_RED); c.setFont("Helvetica-Bold", 8)
        c.drawCentredString(left_cx + 18, branch_y + 4, "NO (Unconjugated)")
        c.setFillColor(C_GREEN); c.setFont("Helvetica-Bold", 8)
        c.drawCentredString(right_cx - 18, branch_y + 4, "YES (Conjugated)")

        # Q2 LEFT: elevated?
        box(left_box_x, left_box_y, bw_q, bh_q, C_AMBER, C_AMBER,
            "Check: Haemolysis\nmarkers (LDH, retics,\nhaptoglobin)", "Helvetica", 7.5)

        # Q2 RIGHT
        box(right_box_x, right_box_y, bw_q, bh_q, C_TEAL, C_TEAL,
            "Check: ALP, GGT\nvs ALT, AST\nRatio", "Helvetica", 7.5)

        # ─────────── Row 4: leaf nodes ────────────────────────────
        row4_y = left_box_y - 50
        leaf_h = 48

        # Left branch sub-nodes
        ll_cx = w * 0.10
        lm_cx = w * 0.30
        box(ll_cx - bw_leaf/2, row4_y - leaf_h, bw_leaf, leaf_h,
            C_BOX_PRE, C_PRE_HEPATIC,
            "PRE-HEPATIC\nHaemolytic anaemia\nIneffective erythropoiesis\nPigment stones",
            "Helvetica", 7, C_PRE_HEPATIC)
        box(lm_cx - bw_leaf/2, row4_y - leaf_h, bw_leaf, leaf_h,
            C_BOX_PRE, C_ORANGE,
            "INHERITED (Unconj.)\nGilbert syndrome\nCrigler-Najjar I/II\nNeonatal jaundice",
            "Helvetica", 7, C_ORANGE)

        # Right branch sub-nodes
        rl_cx = w * 0.65
        rr_cx = w * 0.87
        box(rl_cx - bw_leaf/2, row4_y - leaf_h, bw_leaf, leaf_h,
            C_BOX_HEP, C_HEPATIC,
            "HEPATOCELLULAR\nViral/Alcoholic hepatitis\nDrug injury, Cirrhosis\nAST/ALT >>ALP",
            "Helvetica", 7, C_HEPATIC)
        box(rr_cx - bw_leaf/2, row4_y - leaf_h, bw_leaf, leaf_h,
            C_BOX_POST, C_POST_HEPATIC,
            "POST-HEPATIC\nCBD stone, Pancreatic Ca\nCholangiocarcinoma\nALP/GGT >> AST",
            "Helvetica", 7, C_POST_HEPATIC)

        # connecting arrows to leaf nodes
        # left box bottom -> two sub-nodes
        lbx_cx = left_cx
        lbx_by = left_box_y
        # horizontal
        c.setStrokeColor(C_NAVY); c.setLineWidth(1)
        c.line(ll_cx, row4_y, lm_cx, row4_y)
        c.line(lbx_cx, lbx_by, lbx_cx, row4_y)
        c.line(ll_cx, row4_y, ll_cx, row4_y - leaf_h + leaf_h)
        c.line(lm_cx, row4_y, lm_cx, row4_y)
        arrow(ll_cx, row4_y, ll_cx, row4_y - leaf_h + leaf_h + 2)
        arrow(lm_cx, row4_y, lm_cx, row4_y - leaf_h + leaf_h + 2)

        # right box -> two sub-nodes
        rbx_cx = right_cx
        rbx_by = right_box_y
        c.line(rl_cx, row4_y, rr_cx, row4_y)
        c.line(rbx_cx, rbx_by, rbx_cx, row4_y)
        arrow(rl_cx, row4_y, rl_cx, row4_y - leaf_h + leaf_h + 2)
        arrow(rr_cx, row4_y, rr_cx, row4_y - leaf_h + leaf_h + 2)

        # ── legend ────────────────────────────────────────────────
        leg_y = row4_y - leaf_h - 18
        c.setFont("Helvetica-Bold", 7.5)
        c.setFillColor(C_PRE_HEPATIC)
        c.rect(10, leg_y, 10, 8, fill=1, stroke=0)
        c.setFillColor(C_TEXT)
        c.drawString(24, leg_y + 1, "Pre-hepatic")

        c.setFillColor(C_HEPATIC)
        c.rect(95, leg_y, 10, 8, fill=1, stroke=0)
        c.setFillColor(C_TEXT)
        c.drawString(109, leg_y + 1, "Hepatocellular")

        c.setFillColor(C_POST_HEPATIC)
        c.rect(200, leg_y, 10, 8, fill=1, stroke=0)
        c.setFillColor(C_TEXT)
        c.drawString(214, leg_y + 1, "Post-hepatic")

        c.setFillColor(C_TEAL)
        c.rect(295, leg_y, 10, 8, fill=1, stroke=0)
        c.setFillColor(C_TEXT)
        c.drawString(309, leg_y + 1, "Decision node")


class UrineStoolDiagram(Flowable):
    """Visual summary of urine/stool findings per jaundice type."""
    def __init__(self, width=None):
        super().__init__()
        self._w = width or (W - 80)
        self._h = 110

    def wrap(self, aw, ah):
        return self._w, self._h

    def draw(self):
        c = self.canv
        w = self._w
        col_w = w / 3.2
        titles = ["PRE-HEPATIC", "HEPATOCELLULAR", "POST-HEPATIC"]
        colors = [C_PRE_HEPATIC, C_HEPATIC, C_POST_HEPATIC]
        data = [
            [
                ("Bilirubin (serum)", "Unconjugated ↑"),
                ("Urine bilirubin", "Absent"),
                ("Urine urobilinogen", "Increased ↑"),
                ("Stool colour", "Normal / Dark"),
                ("ALP/GGT", "Normal"),
                ("AST/ALT", "Normal"),
            ],
            [
                ("Bilirubin (serum)", "Mixed ↑↑"),
                ("Urine bilirubin", "Present (+)"),
                ("Urine urobilinogen", "Decreased"),
                ("Stool colour", "Normal / Pale"),
                ("ALP/GGT", "Mildly raised"),
                ("AST/ALT", "Markedly raised"),
            ],
            [
                ("Bilirubin (serum)", "Conjugated ↑↑"),
                ("Urine bilirubin", "Strongly Present"),
                ("Urine urobilinogen", "Absent"),
                ("Stool colour", "Pale / Clay"),
                ("ALP/GGT", "Markedly raised"),
                ("AST/ALT", "Mildly raised"),
            ],
        ]
        for i, (title, col, entries) in enumerate(zip(titles, colors, data)):
            x = i * (w / 3) + 2
            # header
            c.setFillColor(col)
            c.roundRect(x, self._h - 18, col_w - 4, 16, 3, fill=1, stroke=0)
            c.setFillColor(white)
            c.setFont("Helvetica-Bold", 8)
            c.drawCentredString(x + (col_w-4)/2, self._h - 12, title)
            # rows
            row_h = 13
            for j, (label, value) in enumerate(entries):
                row_y = self._h - 18 - (j+1)*row_h
                bg = C_LIGHT_BG if j % 2 == 0 else white
                c.setFillColor(bg)
                c.rect(x, row_y, col_w - 4, row_h - 1, fill=1, stroke=0)
                c.setFillColor(C_TEXT)
                c.setFont("Helvetica", 7)
                c.drawString(x + 3, row_y + 3, label + ":")
                c.setFont("Helvetica-Bold", 7)
                # colour code value
                if "Normal" in value or "Absent" == value or "Decreased" in value:
                    c.setFillColor(C_SUBTEXT)
                elif "Present" in value or "raised" in value.lower() or "↑" in value:
                    c.setFillColor(col)
                else:
                    c.setFillColor(C_TEXT)
                c.drawRightString(x + col_w - 6, row_y + 3, value)


class ObstructionTreeFlowable(Flowable):
    """Decision tree for differentiating intrahepatic vs extrahepatic cholestasis."""
    def __init__(self, width=None):
        super().__init__()
        self._w = width or (W - 80)
        self._h = 260

    def wrap(self, aw, ah):
        return self._w, self._h

    def draw(self):
        c = self.canv
        w = self._w

        def box(x, y, bw, bh, fc, sc, text, font="Helvetica", fs=8, tc=white, r=5):
            c.setFillColor(fc); c.setStrokeColor(sc)
            c.setLineWidth(1); c.roundRect(x, y, bw, bh, r, fill=1, stroke=1)
            c.setFillColor(tc); c.setFont(font, fs)
            lines = text.split('\n')
            th = len(lines) * (fs + 2)
            sy = y + bh/2 + th/2 - (fs + 2)
            for i, l in enumerate(lines):
                c.drawCentredString(x+bw/2, sy - i*(fs+2), l)

        def arr(x1, y1, x2, y2):
            import math
            c.setStrokeColor(C_NAVY); c.setLineWidth(1.2); c.setDash([])
            c.line(x1, y1, x2, y2)
            c.setFillColor(C_NAVY)
            ang = math.atan2(y2-y1, x2-x1)
            al=6; aw2=2.5
            ax1 = x2 - al*math.cos(ang) + aw2*math.sin(ang)
            ay1 = y2 - al*math.sin(ang) - aw2*math.cos(ang)
            ax2 = x2 - al*math.cos(ang) - aw2*math.sin(ang)
            ay2 = y2 - al*math.sin(ang) + aw2*math.cos(ang)
            p = c.beginPath()
            p.moveTo(x2, y2); p.lineTo(ax1,ay1); p.lineTo(ax2,ay2); p.close()
            c.drawPath(p, fill=1, stroke=0)

        def hline(x1, y, x2):
            c.setStrokeColor(C_NAVY); c.setLineWidth(1.2); c.line(x1,y,x2,y)

        TOP = self._h - 8
        bw=200; bh=28
        # Root
        box(w/2-bw/2, TOP-bh, bw, bh, C_TEAL, C_TEAL,
            "Conjugated hyperbilirubinaemia confirmed", "Helvetica-Bold", 9)
        arr(w/2, TOP-bh, w/2, TOP-bh-22)

        # Q1 diamond
        dw=180; dh=32
        q1y = TOP-bh-22-dh; q1x=w/2-dw/2
        # draw diamond
        pts = [w/2, q1y+dh, q1x+dw, q1y+dh/2, w/2, q1y, q1x, q1y+dh/2]
        c.setFillColor(C_NAVY); c.setStrokeColor(C_NAVY); c.setLineWidth(1)
        p = c.beginPath()
        p.moveTo(pts[0],pts[1]); p.lineTo(pts[2],pts[3])
        p.lineTo(pts[4],pts[5]); p.lineTo(pts[6],pts[7]); p.close()
        c.drawPath(p, fill=1, stroke=1)
        c.setFillColor(white); c.setFont("Helvetica-Bold", 8)
        c.drawCentredString(w/2, q1y+dh/2-4, "Ultrasound: Bile duct dilated?")

        branch_y = q1y - 25
        lcx = w*0.22; rcx = w*0.78
        hline(lcx, branch_y, rcx, )
        c.setStrokeColor(C_NAVY); c.setLineWidth(1.2)
        c.line(w/2, q1y, w/2, branch_y)
        c.line(lcx, branch_y, lcx, branch_y-28)
        c.line(rcx, branch_y, rcx, branch_y-28)

        c.setFillColor(C_RED); c.setFont("Helvetica-Bold", 8)
        c.drawCentredString(lcx+14, branch_y+4, "NO")
        c.setFillColor(C_GREEN); c.setFont("Helvetica-Bold", 8)
        c.drawCentredString(rcx-14, branch_y+4, "YES")

        bw2=170; bh2=32
        # LEFT
        box(lcx-bw2/2, branch_y-28-bh2, bw2, bh2, C_BOX_HEP, C_HEPATIC,
            "INTRAHEPATIC cholestasis\nLiver biopsy / LFT pattern / serology",
            "Helvetica", 7.5, C_HEPATIC)
        # RIGHT
        box(rcx-bw2/2, branch_y-28-bh2, bw2, bh2, C_BOX_POST, C_POST_HEPATIC,
            "EXTRAHEPATIC obstruction\nMRCP / ERCP / CT abdomen",
            "Helvetica", 7.5, C_POST_HEPATIC)

        leaf_y = branch_y - 28 - bh2 - 22
        # Left sub-nodes
        bw3=120; bh3=36
        ll=w*0.12; lm=w*0.34
        box(ll-bw3/2, leaf_y-bh3, bw3, bh3, C_BOX_HEP, C_HEPATIC,
            "PBC / PSC\nAIH / Drug-induced\nAlcoholic hepatitis",
            "Helvetica", 7, C_HEPATIC)
        box(lm-bw3/2, leaf_y-bh3, bw3, bh3, C_BOX_HEP, HexColor("#2C5282"),
            "Viral hepatitis\nWilson's disease\nInfiltrative liver dz",
            "Helvetica", 7, HexColor("#2C5282"))
        arr(lcx-bw2/2+bw2*0.33, branch_y-28-bh2, ll, leaf_y)
        arr(lcx-bw2/2+bw2*0.67, branch_y-28-bh2, lm, leaf_y)

        # Right sub-nodes
        rl=w*0.64; rr=w*0.88
        box(rl-bw3/2, leaf_y-bh3, bw3, bh3, C_BOX_POST, C_POST_HEPATIC,
            "Choledocholithiasis\nBiliary stricture\nPancreatitis",
            "Helvetica", 7, C_POST_HEPATIC)
        box(rr-bw3/2, leaf_y-bh3, bw3, bh3, C_BOX_POST, HexColor("#1C4532"),
            "Pancreatic Ca\nCholangiocarcinoma\nAmpullary Ca",
            "Helvetica", 7, HexColor("#1C4532"))
        arr(rcx-bw2/2+bw2*0.33, branch_y-28-bh2, rl, leaf_y)
        arr(rcx-bw2/2+bw2*0.67, branch_y-28-bh2, rr, leaf_y)


# ═══════════════════════════════════════════════════════════════
#  PAGE TEMPLATES
# ═══════════════════════════════════════════════════════════════

def header_footer(canvas, doc):
    canvas.saveState()
    page = doc.page
    # Header stripe
    canvas.setFillColor(C_NAVY)
    canvas.rect(0, H - 28, W, 28, fill=1, stroke=0)
    canvas.setFillColor(white)
    canvas.setFont("Helvetica-Bold", 9)
    canvas.drawString(20, H - 18, "CLINICAL SUMMARY: DIAGNOSING JAUNDICE")
    canvas.setFont("Helvetica", 8)
    canvas.drawRightString(W - 20, H - 18, f"Page {page}")
    # Footer
    canvas.setFillColor(C_LIGHT_BG)
    canvas.rect(0, 0, W, 20, fill=1, stroke=0)
    canvas.setFillColor(C_SUBTEXT)
    canvas.setFont("Helvetica-Oblique", 7)
    canvas.drawString(20, 6, "For educational use only. Not a substitute for clinical judgment. Sources: Harrison's 22E, Ganong's 26E, Schwartz's Surgery 11E.")
    canvas.drawRightString(W - 20, 6, "June 2026")
    canvas.restoreState()


# ═══════════════════════════════════════════════════════════════
#  BUILD DOCUMENT
# ═══════════════════════════════════════════════════════════════

OUT = "/home/daytona/workspace/jaundice-pdf/Diagnosing_Jaundice_Clinical_Summary.pdf"

doc = SimpleDocTemplate(
    OUT,
    pagesize=A4,
    leftMargin=25*mm, rightMargin=25*mm,
    topMargin=22*mm, bottomMargin=18*mm,
    title="Clinical Summary: Diagnosing Jaundice",
    author="Orris Clinical AI",
    subject="Jaundice Diagnosis with Decision Trees",
)

story = []
P = Paragraph
SP = lambda n=6: Spacer(1, n)
HR = lambda: HRFlowable(width="100%", thickness=0.5, color=C_BORDER, spaceAfter=4)

# ──────────────────────────────────────────────────────────────
# PAGE 1: TITLE + OVERVIEW + METABOLISM SUMMARY
# ──────────────────────────────────────────────────────────────

story.append(SP(8))
story.append(P("CLINICAL SUMMARY", S_TITLE))
story.append(P("Diagnosing Jaundice: A Systematic Approach with Decision Trees", S_SUBTITLE))
story.append(P("Based on Harrison's 22E | Ganong's Review of Medical Physiology 26E | Schwartz's Surgery 11E", S_DATE))
story.append(HR())
story.append(SP(4))

# Definition box
def_data = [[P(
    "<b>JAUNDICE (Icterus)</b>: Yellow discoloration of skin, sclera, and mucous membranes caused by "
    "bilirubin deposition. Clinically detectable when total serum bilirubin <b>&gt;2 mg/dL (34 μmol/L)</b>. "
    "Normal range: 0.2–1.0 mg/dL. The sclera is affected earliest due to its high elastin content.",
    S_BODY)]]
story.append(Table(def_data, colWidths=["100%"],
    style=TableStyle([
        ("BACKGROUND", (0,0), (-1,-1), C_YELLOW_BG),
        ("BOX", (0,0), (-1,-1), 1, C_AMBER),
        ("LEFTPADDING", (0,0), (-1,-1), 10),
        ("RIGHTPADDING", (0,0), (-1,-1), 10),
        ("TOPPADDING", (0,0), (-1,-1), 8),
        ("BOTTOMPADDING", (0,0), (-1,-1), 8),
        ("ROUNDEDCORNERS", [5]),
    ])))
story.append(SP(8))

# ── Bilirubin Metabolism ──────────────────────────────────────
story.append(SectionHeader("  BILIRUBIN METABOLISM", C_NAVY))
story.append(SP(6))

story.append(P("<b>Source:</b> 70–90% from senescent RBC haemoglobin breakdown; remainder from myoglobin, cytochromes, ineffective erythropoiesis.", S_BODY))
story.append(SP(4))

metab_steps = [
    ["Step", "Location", "Process", "Key Enzyme/Transporter"],
    ["1. Production", "RE System\n(spleen, liver)", "Haem → Biliverdin → Bilirubin\n(unconjugated, lipid-soluble)", "Haem oxygenase\nBiliverdin reductase"],
    ["2. Transport", "Blood", "Bound to albumin\n(not filtered by kidney)", "Albumin (tight binding)"],
    ["3. Uptake", "Hepatocyte\nsinusoidal surface", "UCB dissociates from albumin\nEnters hepatocyte", "OATP1B1 / OATP1B3"],
    ["4. Binding", "Hepatocyte\ncytoplasm", "Bound to ligandins\n(prevents back-diffusion)", "Glutathione-S-transferases\n(GST)"],
    ["5. Conjugation", "Smooth ER\n(hepatocyte)", "Bilirubin + 2× UDPGA →\nBilirubin diglucuronide (water-soluble)", "UGT1A1\n(bilirubin-UDP-glucuronosyltransferase)"],
    ["6. Excretion", "Bile canaliculus", "Active transport of conjugated\nbilirubin into bile", "MRP2 (ABCC2)"],
    ["7. Gut metabolism", "Terminal ileum\n& colon", "Conjugated bilirubin → Urobilinogen\n(by intestinal bacteria)", "Bacterial reduction"],
    ["8. Enterohepatic\ncirculation", "Portal circulation\n→ liver → kidney", "~10–20% urobilinogen reabsorbed;\nrest excreted in stool as stercobilin", "Liver re-excretion\nRenal filtration"],
]

metab_col_w = [28*mm, 32*mm, 65*mm, 55*mm]
metab_table = Table(
    [[P(str(c), S_TABLE_H if r==0 else (S_TABLE_L if j<2 else S_TABLE_L)) for j, c in enumerate(row)] for r, row in enumerate(metab_steps)],
    colWidths=metab_col_w,
    repeatRows=1
)
metab_table.setStyle(TableStyle([
    ("BACKGROUND", (0,0), (-1,0), C_NAVY),
    ("BACKGROUND", (0,1), (-1,1), C_BLUE_BG),
    ("BACKGROUND", (0,2), (-1,2), white),
    ("BACKGROUND", (0,3), (-1,3), C_BLUE_BG),
    ("BACKGROUND", (0,4), (-1,4), white),
    ("BACKGROUND", (0,5), (-1,5), C_BLUE_BG),
    ("BACKGROUND", (0,6), (-1,6), white),
    ("BACKGROUND", (0,7), (-1,7), C_BLUE_BG),
    ("BACKGROUND", (0,8), (-1,8), white),
    ("GRID", (0,0), (-1,-1), 0.3, C_BORDER),
    ("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
    ("FONTSIZE", (0,0), (-1,-1), 8),
    ("TOPPADDING", (0,0), (-1,-1), 4),
    ("BOTTOMPADDING", (0,0), (-1,-1), 4),
    ("LEFTPADDING", (0,0), (-1,-1), 5),
    ("RIGHTPADDING", (0,0), (-1,-1), 5),
    ("VALIGN", (0,0), (-1,-1), "MIDDLE"),
    ("TEXTCOLOR", (0,0), (-1,0), white),
    ("ROWBACKGROUNDS", (0,1), (-1,-1), [C_BLUE_BG, white]),
]))
story.append(metab_table)
story.append(SP(6))
story.append(P(
    "<b>Key point:</b> Conjugation by UGT1A1 converts water-insoluble unconjugated bilirubin to water-soluble "
    "bilirubin diglucuronide, enabling excretion into bile. Only <b>conjugated bilirubin</b> appears in urine.",
    S_NOTE))

# ──────────────────────────────────────────────────────────────
# PAGE 2: MAIN DECISION TREE
# ──────────────────────────────────────────────────────────────
story.append(PageBreak())
story.append(SectionHeader("  DIAGNOSTIC DECISION TREE — OVERVIEW", C_NAVY))
story.append(SP(8))
story.append(DecisionTreeFlowable(width=W - 50*mm, height=330))
story.append(SP(4))
story.append(P(
    "Start by determining whether the predominant bilirubin fraction is <b>conjugated (direct) or unconjugated (indirect)</b>. "
    "This single test narrows the differential significantly before further workup.",
    S_CAPTION))

story.append(SP(10))
story.append(HR())
story.append(SectionHeader("  URINE, STOOL & LAB PATTERN SUMMARY", C_TEAL))
story.append(SP(6))
story.append(UrineStoolDiagram(width=W - 50*mm))
story.append(SP(4))
story.append(P(
    "<b>Note:</b> ALP = alkaline phosphatase; GGT = gamma-glutamyl transferase; "
    "AST/ALT = aminotransferases. Cholestatic pattern: ALP/GGT >> AST. "
    "Hepatocellular pattern: AST/ALT >> ALP.",
    S_NOTE))

# ──────────────────────────────────────────────────────────────
# PAGE 3: TYPES OF JAUNDICE + INHERITED SYNDROMES
# ──────────────────────────────────────────────────────────────
story.append(PageBreak())
story.append(SectionHeader("  TYPES OF JAUNDICE — DETAILED COMPARISON", C_NAVY))
story.append(SP(8))

types_data = [
    [P("Feature", S_TABLE_H), P("Pre-Hepatic", S_TABLE_H), P("Hepatocellular", S_TABLE_H), P("Post-Hepatic", S_TABLE_H)],
    [P("Mechanism", S_TABLE_L), P("Excess bilirubin production", S_TABLE_C), P("Impaired uptake/conjugation/excretion", S_TABLE_C), P("Bile flow obstruction", S_TABLE_C)],
    [P("Bilirubin type", S_TABLE_L), P("Unconjugated ↑", S_TABLE_C), P("Mixed ↑↑ (both fractions)", S_TABLE_C), P("Conjugated ↑↑", S_TABLE_C)],
    [P("Max bilirubin\n(isolated)", S_TABLE_L), P("~4 mg/dL\n(liver compensates)", S_TABLE_C), P("Variable (up to 30+)", S_TABLE_C), P("Variable (progressive)", S_TABLE_C)],
    [P("Urine colour", S_TABLE_L), P("Normal", S_TABLE_C), P("Dark (bilirubin)", S_TABLE_C), P("Very dark (bilirubin)", S_TABLE_C)],
    [P("Urine bilirubin", S_TABLE_L), P("Absent", S_TABLE_C), P("Present", S_TABLE_C), P("Strongly present", S_TABLE_C)],
    [P("Urine urobilinogen", S_TABLE_L), P("Increased ↑↑", S_TABLE_C), P("Decreased", S_TABLE_C), P("Absent", S_TABLE_C)],
    [P("Stool colour", S_TABLE_L), P("Normal / Dark", S_TABLE_C), P("Normal / Pale", S_TABLE_C), P("Pale / Clay (acholic)", S_TABLE_C)],
    [P("ALP / GGT", S_TABLE_L), P("Normal", S_TABLE_C), P("Mildly raised", S_TABLE_C), P("Markedly raised ↑↑↑", S_TABLE_C)],
    [P("AST / ALT", S_TABLE_L), P("Normal", S_TABLE_C), P("Markedly raised ↑↑↑", S_TABLE_C), P("Mildly raised", S_TABLE_C)],
    [P("Pruritus", S_TABLE_L), P("No", S_TABLE_C), P("Variable", S_TABLE_C), P("Yes (bile salts in skin)", S_TABLE_C)],
    [P("Splenomegaly", S_TABLE_L), P("Yes (haemolysis)", S_TABLE_C), P("Yes (portal HTN)", S_TABLE_C), P("Sometimes", S_TABLE_C)],
    [P("Key investigations", S_TABLE_L), P("Blood film, reticulocytes,\nLDH, haptoglobin, Coombs", S_TABLE_C), P("Viral serology, ANA,\nSMA, LKM, liver biopsy", S_TABLE_C), P("US/CT abdomen, MRCP,\nCA 19-9, ERCP", S_TABLE_C)],
    [P("Common causes", S_TABLE_L), P("Haemolytic anaemia,\nG6PD, thalassaemia,\nPigment gallstones", S_TABLE_C), P("Viral hepatitis A/B/C/E,\nalcoholic hepatitis,\ndrug-induced, cirrhosis", S_TABLE_C), P("CBD stone, pancreatic Ca,\ncholangiocarcinoma,\nPSC, PBC, stricture", S_TABLE_C)],
]

tw = W - 50*mm
types_col_w = [tw*0.22, tw*0.26, tw*0.26, tw*0.26]
types_table = Table(types_data, colWidths=types_col_w, repeatRows=1)
types_table.setStyle(TableStyle([
    ("BACKGROUND", (0,0), (0,-1), C_LIGHT_BG),
    ("BACKGROUND", (0,0), (-1,0), C_NAVY),
    ("BACKGROUND", (1,0), (1,0), C_PRE_HEPATIC),
    ("BACKGROUND", (2,0), (2,0), C_HEPATIC),
    ("BACKGROUND", (3,0), (3,0), C_POST_HEPATIC),
    ("GRID", (0,0), (-1,-1), 0.3, C_BORDER),
    ("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
    ("TOPPADDING", (0,0), (-1,-1), 4),
    ("BOTTOMPADDING", (0,0), (-1,-1), 4),
    ("LEFTPADDING", (0,0), (-1,-1), 5),
    ("RIGHTPADDING", (0,0), (-1,-1), 5),
    ("VALIGN", (0,0), (-1,-1), "MIDDLE"),
    ("ROWBACKGROUNDS", (0,1), (-1,-1), [C_LIGHT_BG, white]),
    ("FONTNAME", (0,1), (0,-1), "Helvetica-Bold"),
]))
story.append(types_table)

story.append(SP(10))
story.append(SectionHeader("  INHERITED DISORDERS OF BILIRUBIN METABOLISM", C_PURPLE))
story.append(SP(6))

inh_data = [
    [P("Disorder", S_TABLE_H), P("Defect", S_TABLE_H), P("Bilirubin\nType", S_TABLE_H), P("Level", S_TABLE_H), P("Key Features", S_TABLE_H), P("Treatment", S_TABLE_H)],
    [P("Gilbert Syndrome", S_TABLE_L), P("↓ UGT1A1 (promoter TATAA box)", S_TABLE_L), P("Unconj.", S_TABLE_C), P("<3 mg/dL", S_TABLE_C), P("Benign; triggered by fasting, illness, stress; 5–10% prevalence", S_TABLE_L), P("Reassurance only", S_TABLE_L)],
    [P("Crigler-Najjar\nType I", S_TABLE_L), P("Complete absence of UGT1A1", S_TABLE_L), P("Unconj.", S_TABLE_C), P(">20 mg/dL", S_TABLE_C), P("Severe; kernicterus risk; requires lifelong phototherapy", S_TABLE_L), P("16–18h phototherapy/day;\nliver transplant", S_TABLE_L)],
    [P("Crigler-Najjar\nType II", S_TABLE_L), P("Severely ↓ UGT1A1 (<10%)", S_TABLE_L), P("Unconj.", S_TABLE_C), P("6–20 mg/dL", S_TABLE_C), P("Milder; responds to phenobarbital", S_TABLE_L), P("Phenobarbital", S_TABLE_L)],
    [P("Dubin-Johnson\nSyndrome", S_TABLE_L), P("Absent MRP2 (ABCC2) –\ncanalicular excretion defect", S_TABLE_L), P("Conj.", S_TABLE_C), P("2–5 mg/dL\n(up to 25)", S_TABLE_C), P("Benign; black liver pigment on biopsy; BSP test abnormal; worsened by OCP/pregnancy", S_TABLE_L), P("None required", S_TABLE_L)],
    [P("Rotor Syndrome", S_TABLE_L), P("Absent OATP1B1+1B3 –\ndefective hepatic reuptake", S_TABLE_L), P("Conj.", S_TABLE_C), P("2–7 mg/dL", S_TABLE_C), P("Benign; normal liver histology (no pigment); elevated coproporphyrin I in urine", S_TABLE_L), P("None required", S_TABLE_L)],
]

inh_col_w = [tw*0.16, tw*0.20, tw*0.09, tw*0.09, tw*0.28, tw*0.18]
inh_table = Table(inh_data, colWidths=inh_col_w, repeatRows=1)
inh_table.setStyle(TableStyle([
    ("BACKGROUND", (0,0), (-1,0), C_PURPLE),
    ("GRID", (0,0), (-1,-1), 0.3, C_BORDER),
    ("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
    ("TOPPADDING", (0,0), (-1,-1), 4),
    ("BOTTOMPADDING", (0,0), (-1,-1), 4),
    ("LEFTPADDING", (0,0), (-1,-1), 5),
    ("RIGHTPADDING", (0,0), (-1,-1), 5),
    ("VALIGN", (0,0), (-1,-1), "MIDDLE"),
    ("ROWBACKGROUNDS", (0,1), (-1,-1), [C_LIGHT_BG, white]),
    ("TEXTCOLOR", (0,0), (-1,0), white),
]))
story.append(inh_table)

# ──────────────────────────────────────────────────────────────
# PAGE 4: CHOLESTASIS DECISION TREE + INVESTIGATIONS + RED FLAGS
# ──────────────────────────────────────────────────────────────
story.append(PageBreak())
story.append(SectionHeader("  DIFFERENTIATING INTRAHEPATIC vs EXTRAHEPATIC CHOLESTASIS", C_POST_HEPATIC))
story.append(SP(8))
story.append(ObstructionTreeFlowable(width=W - 50*mm))
story.append(SP(4))
story.append(P(
    "Ultrasound is the first-line imaging for jaundice. Dilated bile ducts strongly suggest extrahepatic obstruction. "
    "MRCP is preferred over ERCP for initial diagnosis; ERCP is reserved for cases where therapeutic intervention is planned.",
    S_CAPTION))

story.append(SP(8))
story.append(HR())

# ── Investigations table ──────────────────────────────────────
story.append(SectionHeader("  STEP-WISE INVESTIGATIONS FOR JAUNDICE", C_NAVY))
story.append(SP(6))

inv_data = [
    [P("Step", S_TABLE_H), P("Investigation", S_TABLE_H), P("Purpose / Interpretation", S_TABLE_H)],
    [P("1st line\n(all patients)", S_TABLE_C),
     P("Serum bilirubin (total, direct, indirect)\nLFTs: ALT, AST, ALP, GGT, albumin\nCBC, PT/INR\nUrine dipstick for bilirubin", S_TABLE_L),
     P("Fractionation guides pre/hepatic/post differentiation.\nAlbumin & PT reflect synthetic function.\nBilirubinuria = conjugated hyperbilirubinaemia.", S_TABLE_L)],
    [P("2nd line\n(if hepatocellular)", S_TABLE_C),
     P("Viral serology: HBsAg, anti-HBc, anti-HCV, anti-HAV IgM\nAutoimmune: ANA, ASMA, AMA, anti-LKM1\nSerum immunoglobulins\nAlpha-1-antitrypsin, ceruloplasmin (if <40 yrs)", S_TABLE_L),
     P("Differentiate viral vs autoimmune vs metabolic liver disease.\nCeruloplasmin low in Wilson's disease.\nAMA positive in primary biliary cholangitis (PBC).", S_TABLE_L)],
    [P("2nd line\n(if haemolytic)", S_TABLE_C),
     P("Peripheral blood film\nReticulocyte count\nLDH, haptoglobin\nDirect Coombs test\nHaemoglobin electrophoresis", S_TABLE_L),
     P("Reticulocytosis + low haptoglobin + high LDH = haemolysis.\nCoombs+ = autoimmune haemolysis.\nElectrophoresis for haemoglobinopathies.", S_TABLE_L)],
    [P("2nd line\n(if obstructive)", S_TABLE_C),
     P("Abdominal ultrasound (FIRST)\nCA 19-9, CEA\nMRCP\nCT abdomen with contrast\nERCP (if intervention needed)", S_TABLE_L),
     P("US: bile duct dilatation, stones, mass.\nCA 19-9 raised in pancreatic/biliary malignancy.\nMRCP: non-invasive ductal imaging.\nERCP: diagnostic + therapeutic (stent, stone removal).", S_TABLE_L)],
    [P("Liver biopsy", S_TABLE_C),
     P("Percutaneous / transjugular liver biopsy", S_TABLE_L),
     P("When diagnosis remains uncertain after non-invasive workup. Transjugular preferred if coagulopathy or ascites.", S_TABLE_L)],
]

inv_col_w = [tw*0.15, tw*0.38, tw*0.47]
inv_table = Table(inv_data, colWidths=inv_col_w, repeatRows=1)
inv_table.setStyle(TableStyle([
    ("BACKGROUND", (0,0), (-1,0), C_NAVY),
    ("BACKGROUND", (0,1), (0,-1), C_LIGHT_BG),
    ("GRID", (0,0), (-1,-1), 0.3, C_BORDER),
    ("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
    ("TOPPADDING", (0,0), (-1,-1), 5),
    ("BOTTOMPADDING", (0,0), (-1,-1), 5),
    ("LEFTPADDING", (0,0), (-1,-1), 5),
    ("RIGHTPADDING", (0,0), (-1,-1), 5),
    ("VALIGN", (0,0), (-1,-1), "TOP"),
    ("TEXTCOLOR", (0,0), (-1,0), white),
    ("ROWBACKGROUNDS", (0,1), (-1,-1), [C_LIGHT_BG, white]),
    ("FONTNAME", (0,1), (0,-1), "Helvetica-Bold"),
    ("FONTSIZE", (0,1), (0,-1), 8),
]))
story.append(inv_table)

story.append(SP(10))
story.append(HR())

# ── Red Flags ─────────────────────────────────────────────────
story.append(SectionHeader("  RED FLAG FEATURES REQUIRING URGENT EVALUATION", C_RED))
story.append(SP(6))

red_flags = [
    ("Encephalopathy + jaundice", "Suggests acute liver failure — URGENT referral to liver unit"),
    ("PT/INR >1.5 + jaundice", "Indicates significant hepatic synthetic failure"),
    ("Painless progressive jaundice in >50 yrs", "Pancreatic/biliary malignancy until proven otherwise"),
    ("Courvoisier's sign (palpable GB + jaundice)", "Malignant CBD obstruction (pancreatic Ca likely)"),
    ("Fever + rigors + jaundice (Charcot's triad)", "Ascending cholangitis — URGENT biliary decompression"),
    ("Charcot's triad + hypotension + confusion (Reynolds' pentad)", "Suppurative cholangitis — emergency ERCP/surgery"),
    ("Jaundice + ascites + spider naevi", "Decompensated cirrhosis — hepatology referral"),
    ("Neonatal jaundice: >20 mg/dL total or direct bilirubin >1 mg/dL", "Risk of kernicterus — urgent phototherapy/exchange transfusion"),
]

red_data = [[P(f"<b>⚠ {flag}</b>", S_BODY_SMALL), P(action, S_BODY_SMALL)] for flag, action in red_flags]
red_table = Table(red_data, colWidths=[tw*0.45, tw*0.55])
red_table.setStyle(TableStyle([
    ("GRID", (0,0), (-1,-1), 0.3, C_BORDER),
    ("BACKGROUND", (0,0), (0,-1), C_RED_BG),
    ("BACKGROUND", (1,0), (1,-1), white),
    ("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"),
    ("ROWBACKGROUNDS", (0,0), (-1,-1), [C_RED_BG, HexColor("#FFF0F0")]),
]))
story.append(red_table)

story.append(SP(10))
# ── Special populations note ──────────────────────────────────
special_data = [[
    P("<b>Special populations</b>: "
      "(1) <b>Neonates:</b> Physiological jaundice peaks at day 3–5; treat with phototherapy if above threshold. "
      "(2) <b>Pregnancy:</b> Intrahepatic cholestasis of pregnancy (ICP) — intense pruritus, elevated bile acids, risk of fetal distress. "
      "(3) <b>HIV/immunocompromised:</b> Consider opportunistic infections (CMV, MAC, Cryptosporidium), drug hepatotoxicity, and lymphoma. "
      "(4) <b>Elderly:</b> Malignancy must be excluded early even with mild symptoms.",
      S_BODY_SMALL)
]]
story.append(Table(special_data, colWidths=["100%"],
    style=TableStyle([
        ("BACKGROUND", (0,0), (-1,-1), C_GREEN_BG),
        ("BOX", (0,0), (-1,-1), 1, C_GREEN),
        ("LEFTPADDING", (0,0), (-1,-1), 10),
        ("RIGHTPADDING", (0,0), (-1,-1), 10),
        ("TOPPADDING", (0,0), (-1,-1), 8),
        ("BOTTOMPADDING", (0,0), (-1,-1), 8),
    ])))

story.append(SP(6))
story.append(P(
    "Sources: Harrison's Principles of Internal Medicine 22E (2025) | "
    "Ganong's Review of Medical Physiology 26E | Schwartz's Principles of Surgery 11E | "
    "Sleisenger and Fordtran's Gastrointestinal and Liver Disease",
    S_NOTE))

# ─────────────────────────── BUILD ───────────────────────────
doc.build(story, onFirstPage=header_footer, onLaterPages=header_footer)
print(f"PDF saved to: {OUT}")
Running Command

cd /home/daytona/workspace/jaundice-pdf && python generate_jaundice_pdf.py 2>&1

Editing File

~/jaundice-pdf/generate_jaundice_pdf.py

Successfully replaced 1 instance(s) of the string in '/home/daytona/workspace/jaundice-pdf/generate_jaundice_pdf.py'
Running Command

cd /home/daytona/workspace/jaundice-pdf && python generate_jaundice_pdf.py 2>&1

Running Command

ls -lh /home/daytona/workspace/jaundice-pdf/Diagnosing_Jaundice_Clinical_Summary.pdf

Your PDF is ready:

Diagnosing Jaundice Clinical Summary

PDF Document · PDF


Here's what's inside the 4-page clinical summary:
Page 1 - Bilirubin Metabolism
  • Definition of jaundice with clinical detection threshold
  • 8-step metabolism table: heme oxygenase → biliverdin reductase → albumin transport → OATP uptake → GST binding → UGT1A1 conjugation → MRP2 excretion → enterohepatic circulation
Page 2 - Main Diagnostic Decision Tree
  • Visual tree starting from "Patient presents with jaundice," branching on conjugated vs unconjugated bilirubin, then into 4 leaf categories (pre-hepatic, inherited unconjugated, hepatocellular, post-hepatic)
  • Color-coded urine/stool/lab pattern summary panel (ALP, GGT, AST, ALT across all three types)
Page 3 - Detailed Comparison + Inherited Syndromes
  • 13-row comparison table across pre-hepatic, hepatocellular, and post-hepatic jaundice (mechanism, bilirubin type, urine, stool, enzymes, investigations, causes)
  • Inherited disorders table: Gilbert, Crigler-Najjar I/II, Dubin-Johnson, Rotor syndromes with defects, levels, features, and treatment
Page 4 - Cholestasis Tree + Investigations + Red Flags
  • Second decision tree: conjugated hyperbilirubinemia → ultrasound → intrahepatic vs extrahepatic differential with specific diagnoses
  • Step-wise investigations guide (1st line → viral serology → haemolysis workup → obstructive imaging)
  • 8 red flag features with urgency labels (Charcot's triad, Reynolds' pentad, Courvoisier's sign, etc.)
  • Special population notes (neonates, pregnancy, HIV, elderly)
This is a shared conversation. Sign in to Orris to start your own chat.