A 30 year old female with history of recurrent sinusitis with elevated igE levels . CT showing b/l turbinate hypertrophy. Give me the correct harrison based approach , investigations and appropriate treatment , MD Medicine Style

Reading File
Finding Sources
Finding Sources
Searching PubMed

"chronic rhinosinusitis" AND "allergic rhinitis" AND management

Finding Sources
Finding Sources
Searching PubMed

"chronic rhinosinusitis" AND treatment

Finding Sources
Finding Sources
Reading File
Reading File
Reading File
Reading File
Reading File
Reading File
Reading File
Reading File
Finding Sources
Finding Sources
Reading File
Reading File
Reading File
Reading File
I now have comprehensive data from Goldman-Cecil Medicine and Cummings Otolaryngology. Let me compile the complete MD Medicine-style answer.

Recurrent Sinusitis with Elevated IgE + Bilateral Turbinate Hypertrophy - Harrison/MD Medicine Approach

Clinical Framework: What's Actually Going On

This 30-year-old female has a classic presentation of Chronic Rhinosinusitis (CRS) on an Allergic Background - specifically, what Goldman-Cecil classifies as hyperplastic eosinophilic CRS, the subset driven by Type 2 (TH2-mediated) inflammation. The elevated IgE is the key distinguishing marker. The bilateral turbinate hypertrophy on CT is the structural consequence of chronic mucosal inflammation.
The unified airway concept applies here: allergic rhinitis and recurrent sinusitis share common TH2 pathophysiology. ~80% of patients with allergic asthma also have allergic rhinitis; screen for lower airway involvement.

Step 1: Diagnosis Refinement - Is This Allergic CRS?

Diagnostic Criteria for CRS (EPOS/Goldman-Cecil, Table 394-5)

Symptoms persisting >3 months with TWO or more of:
  • Mucopurulent nasal drainage (anterior or posterior)
  • Nasal obstruction/congestion
  • Facial pressure/fullness
  • Reduced sense of smell (hyposmia/anosmia)
PLUS at least one objective finding:
  • Nasal/middle meatus polyps on endoscopy
  • Edema or purulence in middle meatus
  • Paranasal sinus opacification on CT
This patient satisfies both clinical and radiologic criteria.

Differentials to Exclude First (Goldman-Cecil Table 394-3)

CategoryExamples
AllergicSeasonal AR, Perennial AR
InflammatoryNARES (non-allergic rhinitis with eosinophilia), CRS with/without polyps
AnatomicSeptal deviation, neoplasm
HormonalHypothyroidism, OCP use
VasculiticGranulomatosis with Polyangiitis (Wegener's) - check for saddle nose, hemoptysis
Aspirin-exacerbatedSamter's triad (nasal polyps + asthma + ASA sensitivity)
Allergic fungal sinusitisElevated IgE + hyperdense sinus opacification on CT + specific IgE to fungi
Important: Elevated total IgE with specific unilateral expansion on CT should raise suspicion for Allergic Fungal Sinusitis (AFS) - characterized by elevated total IgE, specific IgE to colonizing fungi (Aspergillus, Alternaria), characteristic hyperdense mucin on CT, and eosinophilic mucin. This is a specific subtype requiring surgical management.

Step 2: Investigations (Systematic, MD Medicine Style)

A. Allergy Workup (Mandatory - Confirms Atopic Driver)

Skin Prick Testing (SPT) - First Line
  • Positive if wheal ≥3 mm larger than negative control
  • Sensitivity 80-100%, specificity 70-90% (Cummings ORL)
  • Stop antihistamines 2-7 days prior; stop omalizumab 8 weeks prior
  • Tests for: house dust mite, cockroach, moulds (Alternaria, Aspergillus, Cladosporium), pollens (trees, grasses, weeds), animal dander
Serum Specific IgE (ImmunoCAP/RAST)
  • Preferred if: severe dermatographism, active skin disease, inability to stop antihistamines, pregnancy
  • Total IgE > 100 IU/mL is strongly suggestive of atopic disease
Intradermal Testing
  • More sensitive than SPT; used to determine immunotherapy starting dilution (endpoint dilution method)
  • Reserved for when SPT is negative but clinical suspicion remains high

B. Nasal Assessment

Anterior Rhinoscopy / Nasal Endoscopy
  • Assess turbinate size, mucosal pallor/edema (bluish/pale mucosa = allergic)
  • Look for: nasal polyps (middle meatus), septal deviation, purulent discharge, allergic crease
Nasal Smear for Eosinophils
  • Distinguishes allergic rhinitis (eosinophils present) from NARES vs. vasomotor rhinitis

C. Imaging

CT Paranasal Sinuses (Non-contrast) - Already done
  • Bilateral turbinate hypertrophy confirmed
  • Assess: degree of ostiomeatal complex (OMC) obstruction, air-fluid levels, mucosal thickening, polyps, bony erosion
  • Lund-Mackay scoring for severity grading (each sinus 0-2; max 24)
  • Look for: hyperattenuating mucin within sinuses (AFS), bony expansion/erosion
Additional CT Finding Interpretation:
  • Mucosal thickening >3mm = significant
  • Complete opacification = advanced disease
  • Hyperdense material within sinus cavity = fungal mucin (suggestive of AFS)

D. Laboratory Investigations

InvestigationRationale
CBC with differentialEosinophilia (>500/µL supports atopic/eosinophilic CRS)
Total serum IgEQuantify allergic burden; helps dose omalizumab if needed
ESR, CRPActive infection vs. chronic inflammation
ANA, ANCA (c-ANCA/PR3)Exclude GPA (Wegener's) if unusual features
Serum IgG, IgA, IgMExclude common variable immunodeficiency (CVID) in recurrent sinusitis
Sweat chloride / CFTR mutationExclude cystic fibrosis if bilateral bronchiectasis, recurrent pulmonary infections
Nasal NO (fractional)Exclude primary ciliary dyskinesia
Aspergillus/Alternaria specific IgEIf AFS is suspected on CT
Beta-2 transferrin (nasal fluid)Exclude CSF leak if clear, unilateral discharge

E. Pulmonary Assessment

  • Spirometry with bronchodilator reversibility
  • Fractional exhaled nitric oxide (FeNO) if asthma suspected
  • (Remember: 80% of allergic asthma patients have co-existing allergic rhinitis)

Step 3: Treatment - Stepwise Approach

Pillar 1: Allergen Avoidance (Primary Prevention)

Per Goldman-Cecil (Prevention section):
  • House dust mite: Allergen-impermeable mattress/pillow covers; humidity <50%; hot water washing of bedding (>130°F); vacuum with HEPA filter
  • Moulds: Dehumidification; treat window/shower areas with dilute bleach; avoid indoor plants in bedroom
  • Cockroaches: Extermination, sealing food sources
  • Pet dander: Ideally rehome; HEPA air purifier; exclude from bedroom; regular bathing of pets
  • Pollen: Air conditioning (filtered), close windows during high pollen season

Pillar 2: Pharmacotherapy

Step 1 - Mild/Intermittent Disease

Second-generation Oral H1 Antihistamines (once-daily, non-sedating):
  • Cetirizine 10 mg OD
  • Levocetirizine 5 mg OD
  • Loratadine 10 mg OD
  • Desloratadine 5 mg OD
  • Fexofenadine 180 mg OD
Antihistamines are most effective for sneezing, rhinorrhea, and pruritus. Their benefit diminishes with continuous allergen exposure (perennial AR). Provide only 20-30% symptom reduction.
Intranasal Antihistamines (faster onset, also effective for non-allergic rhinitis):
  • Azelastine nasal spray
  • Olopatadine nasal spray

Step 2 - Moderate/Persistent Disease (First-line for this patient)

Intranasal Corticosteroids (INCSs) - GOLD STANDARD (Goldman-Cecil, Table 394-6):
DrugDose/actuationDosing
Mometasone furoate50 µg2 sprays/nostril OD
Fluticasone propionate50 µg2 sprays/nostril OD
Fluticasone furoate27.5 µg2 sprays/nostril OD
Budesonide32 µg2 sprays/nostril BD
Triamcinolone acetonide55 µg2 sprays/nostril OD
Ciclesonide50 µg2 sprays/nostril OD
  • Provide 50-90% symptom reduction vs. 20-30% for antihistamines
  • Treatment of choice for moderate-severe or perennial AR
  • Also address turbinate hypertrophy - INCSs directly reduce mucosal edema
  • If adding antihistamine to INCS, prefer intranasal antihistamine over oral
Leukotriene Receptor Antagonists (LTRAs):
  • Montelukast 10 mg OD - efficacy comparable to antihistamines; useful add-on
  • Zileuton 1200 mg BD; Zafirlukast 20 mg BD (alternatives)
  • Particularly useful in Samter's triad (ASA-exacerbated disease)
Nasal Saline Irrigation:
  • Isotonic or hypertonic saline BD-TDS
  • Mechanical clearance of allergens and mucus; enhances ciliary function
  • Evidence-based adjunct in both AR and CRS
Oral Decongestants (short-term only):
  • Pseudoephedrine 30-60 mg q4-6h (max 240 mg/day)
  • Use for acute flares; risk of rebound congestion with prolonged use
Nasal Cromolyn:
  • Mast cell stabilizer; less effective than INCS
  • Useful preventively (pre-exposure to triggers), especially in mild-moderate disease
  • Requires QID dosing; less practical

Step 3 - Severe/Refractory Disease

Short Course Oral Corticosteroids:
  • Methylprednisolone taper (e.g., 8mg QID x1d → 4mg QID x2d → taper over 6 days)
  • Reserved for severe exacerbations; acute control before surgery
Ipratropium Bromide 0.03-0.06% (2 sprays per nostril q12h):
  • For rhinorrhea-predominant symptoms; useful in infectious rhinitis component

Antibiotics (for infectious exacerbations of CRS):

Acute bacterial rhinosinusitis (persisting >7-10 days without improvement):
  • First line: Amoxicillin 500 mg TDS OR Amoxicillin-clavulanate 875/125 mg BD x 7 days
  • Severe (fever >39°C, immunocompromised, recent abx use): Amoxicillin-clavulanate 2000/125 mg BD
  • Penicillin allergy: Doxycycline 100 mg BD OR Levofloxacin 500 mg OD OR Moxifloxacin 400 mg OD
  • Avoid: macrolides, 2nd/3rd generation cephalosporins (high resistance rates)
  • Note: Watchful waiting acceptable for 7-10 days first; 85% resolve without antibiotics
For chronic sinusitis - antibiotics are NOT the mainstay; cultures guide therapy if needed (direct sinus aspiration or endoscopy-guided middle meatus swab).

Pillar 3: Immunotherapy (Definitive/Disease-Modifying Therapy)

Subcutaneous Immunotherapy (SCIT) - Allergen Immunotherapy
  • Indicated: Failed adequate pharmacotherapy OR desire for durable benefit
  • Duration: 3-5 years total (build-up phase: weekly injections escalating over months; maintenance: monthly)
  • Evidence: Multiple systematic reviews demonstrate significant reduction in rhinitis/rhinoconjunctivitis symptoms, QOL improvement, reduced medication use (Cummings ORL)
  • Also prevents new sensitizations and reduces risk of asthma development
  • Contraindications: Poorly controlled asthma, active autoimmune disease, malignancy, pregnancy (initiation), beta-blocker use (relative)
  • Observe for 30 minutes post-injection (risk of anaphylaxis, rate 0.1%; fatalities extremely rare)
Sublingual Immunotherapy (SLIT)
  • FDA-approved for grass and house dust mite (tablets)
  • Better safety profile than SCIT; growing acceptance
  • Options: Grass pollen tablets, HDM tablets (Odactra/Acarizax)

Pillar 4: Biologics (For Refractory CRS with Nasal Polyps / Type 2 Inflammation)

If CRS with nasal polyps (CRSwNP) is confirmed and refractory to above:
BiologicDoseTargetApproval
Dupilumab300 mg SC q2 weeksIL-4Rα (IL-4 + IL-13)CRSwNP
OmalizumabSC (dose by weight + IgE)IgECRSwNP
Mepolizumab100 mg SC q4 weeksIL-5CRSwNP
  • Reserve for: recurrent disease post-FESS, or comorbidities precluding surgery
  • Tezepelumab (anti-TSLP, NEJM 2025 Phase III RCT - PMID 40106374) is emerging as another option
Recent evidence (Cai S et al., Allergy 2025, PMID 39985317 - meta-analysis): Biologics demonstrate superior efficacy and safety in CRSwNP in real-world settings.

Pillar 5: Surgical Options

For Turbinate Hypertrophy (if medical therapy fails):

Per Cummings Otolaryngology:
  • Mucosal redundancy or bony turbinate hypertrophy will NOT respond to medical therapy - these patients are surgical candidates
  • Do NOT perform total turbinectomy - risks "empty nose syndrome" (atrophic rhinitis, paradoxical obstruction, chronic dryness/crusting)
Surgical Options for Inferior Turbinate Hypertrophy:
  1. Partial inferior turbinectomy (submucous resection)
  2. Turbinoplasty (modified Mabry technique - preferred; preserves mucociliary function)
  3. Radiofrequency volume reduction (RFVTR/Coblation) - minimally invasive, good functional preservation
  4. Laser-assisted turbinoplasty (Holmium:YAG or KTP)
  5. Microdebrider-assisted turbinoplasty
  6. Cryosurgery
  7. Infrared coagulation / electrical coagulation
Key surgical principle: Preserve enough turbinate tissue to maintain mucociliary clearance and prevent empty nose syndrome.

For Chronic Rhinosinusitis (if medical therapy fails - 3+ months of maximal therapy):

Functional Endoscopic Sinus Surgery (FESS)
  • Opens OMC; removes obstructing tissue; improves mucociliary drainage
  • Performed endoscopically (endonasal, no skin incisions)
  • Evidence: Benefits patients with recurrent acute sinusitis and CRS refractory to medical treatment
  • Post-operative saline irrigations + INCS maintenance essential
  • Note: Even with aggressive therapy, patients with severe Type 2 eosinophilic inflammation may have early recurrence with nasal polyps

When to Refer to ENT (Goldman-Cecil Table 394-7)

  • Temperature >39°C with orbital edema, severe headache, visual disturbance, altered mental status, meningeal signs (complications of acute sinusitis - emergency)
  • Failure to respond to >2 courses of antimicrobial therapy
  • Anatomic abnormalities, nosocomial infection
  • Immunocompromised or multiple comorbidities
  • Unusual/resistant pathogens
  • Fungal sinusitis or granulomatous disease
  • Recurrent episodes suggesting chronic sinusitis (this patient)

Summary Management Algorithm for this Patient

STEP 1: Confirm CRS + Allergic Background
  → Nasal endoscopy + allergy testing (SPT/serum IgE)
  → CBC, IgG/IgA/IgM, ANCA (exclude GPA/CVID)
  → Spirometry (co-existing asthma screen)

STEP 2: Allergen Avoidance + Education

STEP 3: Pharmacotherapy
  → INCS (mometasone/fluticasone) + nasal saline irrigation (daily)
  → Add: montelukast 10mg OD + 2nd gen antihistamine PRN
  → Antibiotic course only during acute bacterial exacerbations

STEP 4: Persist 3 months → reassess
  If adequate control → continue + consider SLIT/SCIT

STEP 5: If inadequate control → ENT referral
  → Nasal endoscopy + CT review (Lund-Mackay)
  → Consider: FESS + turbinate reduction
  → Post-surgical INCS maintenance

STEP 6: CRSwNP/Type 2 refractory disease
  → Dupilumab / Omalizumab / Mepolizumab

Sources:
  • Goldman-Cecil Medicine (International Edition), Chapter 394 (Rhinitis, Sinusitis, Rhinosinusitis) - Primary reference
  • Cummings Otolaryngology Head & Neck Surgery, Chapters 35, 29
  • Scott-Brown's Otorhinolaryngology, Vol. 1 & 2
  • Recent: Cai S et al. Allergy 2025 [PMID 39985317] - Biologics meta-analysis; Lipworth BJ et al. NEJM 2025 [PMID 40106374] - Tezepelumab RCT in CRSwNP

Create a management flowchart/PDF for this case

Reading File
Running Command

mkdir -p /home/daytona/workspace/sinusitis-flowchart && ls /home/daytona/workspace/sinusitis-flowchart

Writing File

~/sinusitis-flowchart/generate_flowchart.py

"""
Management Flowchart PDF for:
Recurrent Sinusitis + Elevated IgE + Bilateral Turbinate Hypertrophy
MD Medicine Style (Harrison/Goldman-Cecil based)
"""

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

# ─── Color Palette ────────────────────────────────────────────────────────────
NAVY       = colors.HexColor("#1B3A6B")
STEEL      = colors.HexColor("#2E6DA4")
TEAL       = colors.HexColor("#1A7A7A")
AMBER      = colors.HexColor("#D4850A")
RED_DARK   = colors.HexColor("#B03030")
GREEN_DARK = colors.HexColor("#2A7A2A")
PURPLE     = colors.HexColor("#5B3A8C")
LIGHT_BLUE = colors.HexColor("#D6E8F7")
LIGHT_TEAL = colors.HexColor("#D0EFEF")
LIGHT_AMBER= colors.HexColor("#FEF3DC")
LIGHT_RED  = colors.HexColor("#FAE0E0")
LIGHT_GREEN= colors.HexColor("#DCF0DC")
LIGHT_PURP = colors.HexColor("#EDE5F5")
LIGHT_GREY = colors.HexColor("#F4F4F4")
MID_GREY   = colors.HexColor("#CCCCCC")
WHITE      = colors.white
BLACK      = colors.black

W, H = A4  # 595.27 x 841.89

# ─── Document Setup ───────────────────────────────────────────────────────────
doc = SimpleDocTemplate(
    "/home/daytona/workspace/sinusitis-flowchart/Sinusitis_Management_Flowchart.pdf",
    pagesize=A4,
    leftMargin=15*mm, rightMargin=15*mm,
    topMargin=12*mm,  bottomMargin=12*mm,
    title="Sinusitis Management Flowchart",
    author="Orris MD Medicine Reference"
)

styles = getSampleStyleSheet()

# Custom styles
def S(name, **kw):
    return ParagraphStyle(name, **kw)

title_style = S("Title2",
    fontName="Helvetica-Bold", fontSize=15, textColor=WHITE,
    alignment=TA_CENTER, spaceAfter=2, leading=18)

subtitle_style = S("Subtitle2",
    fontName="Helvetica", fontSize=9, textColor=WHITE,
    alignment=TA_CENTER, spaceAfter=0, leading=12)

section_hdr = S("SectionHdr",
    fontName="Helvetica-Bold", fontSize=9.5, textColor=WHITE,
    alignment=TA_CENTER, leading=12, spaceAfter=0)

body_bold = S("BodyBold",
    fontName="Helvetica-Bold", fontSize=8, textColor=BLACK,
    leading=10, spaceAfter=1)

body_small = S("BodySmall",
    fontName="Helvetica", fontSize=7.5, textColor=BLACK,
    leading=9.5, spaceAfter=1)

body_tiny = S("BodyTiny",
    fontName="Helvetica", fontSize=7, textColor=BLACK,
    leading=9, spaceAfter=0)

drug_style = S("Drug",
    fontName="Helvetica-Oblique", fontSize=7.5, textColor=colors.HexColor("#1B3A6B"),
    leading=9.5)

warn_style = S("Warn",
    fontName="Helvetica-BoldOblique", fontSize=7.5, textColor=RED_DARK,
    leading=9.5)

note_style = S("Note",
    fontName="Helvetica-Oblique", fontSize=7, textColor=colors.HexColor("#444444"),
    leading=9)

# ─── Helper: make a colored header cell ───────────────────────────────────────
def hdr_cell(text, bg=NAVY, fg=WHITE, size=9.5, bold=True):
    fn = "Helvetica-Bold" if bold else "Helvetica"
    st = S(f"hdr_{text[:8]}", fontName=fn, fontSize=size,
           textColor=fg, alignment=TA_CENTER, leading=12)
    return Paragraph(text, st)

def body_cell(text, bold=False, color=BLACK, size=7.5, align=TA_LEFT):
    fn = "Helvetica-Bold" if bold else "Helvetica"
    st = S(f"bc_{text[:8]}", fontName=fn, fontSize=size,
           textColor=color, alignment=align, leading=9.5)
    return Paragraph(text, st)

def bullet(items, color=BLACK, size=7.5):
    lines = []
    for item in items:
        lines.append(Paragraph(f"• {item}", S("blt",
            fontName="Helvetica", fontSize=size, textColor=color,
            leading=9.5, leftIndent=6)))
    return lines

# ─── Arrow Flowable ───────────────────────────────────────────────────────────
class Arrow(Flowable):
    def __init__(self, width=40, height=14, color=STEEL, label=""):
        super().__init__()
        self.width = width
        self.height = height
        self._color = color
        self.label = label

    def draw(self):
        c = self.canv
        w, h = self.width, self.height
        c.setFillColor(self._color)
        c.setStrokeColor(self._color)
        shaft_w = w * 0.55
        shaft_h = h * 0.38
        tip_w = w * 0.45
        cx = w / 2
        # shaft
        c.rect(cx - shaft_w/2, h*0.38, shaft_w, shaft_h, fill=1, stroke=0)
        # arrowhead
        path = c.beginPath()
        path.moveTo(cx, 0)
        path.lineTo(cx - tip_w/2, h*0.42)
        path.lineTo(cx + tip_w/2, h*0.42)
        path.close()
        c.drawPath(path, fill=1, stroke=0)
        if self.label:
            c.setFont("Helvetica-Bold", 7)
            c.setFillColor(WHITE)
            c.drawCentredString(cx, h*0.52, self.label)

    def wrap(self, *args):
        return self.width, self.height

class CentredArrow(Flowable):
    """Arrow centred in available width"""
    def __init__(self, avail_width, color=STEEL, label=""):
        super().__init__()
        self._avail = avail_width
        self._color = color
        self.label = label
        self.height = 14

    def draw(self):
        c = self.canv
        w, h = 50, self.height
        ox = (self._avail - w) / 2
        c.saveState()
        c.translate(ox, 0)
        shaft_w = w * 0.5
        shaft_h = h * 0.38
        tip_w = w * 0.4
        cx = w / 2
        c.setFillColor(self._color)
        c.setStrokeColor(self._color)
        c.rect(cx - shaft_w/2, h*0.38, shaft_w, shaft_h, fill=1, stroke=0)
        path = c.beginPath()
        path.moveTo(cx, 0)
        path.lineTo(cx - tip_w/2, h*0.42)
        path.lineTo(cx + tip_w/2, h*0.42)
        path.close()
        c.drawPath(path, fill=1, stroke=0)
        if self.label:
            c.setFont("Helvetica-Bold", 7)
            c.setFillColor(WHITE)
            c.drawCentredString(cx, h*0.52, self.label)
        c.restoreState()

    def wrap(self, avail_w, avail_h):
        self._avail = avail_w
        return avail_w, self.height

# ─── Build Content ────────────────────────────────────────────────────────────
story = []
FULL_W = W - 30*mm  # 165mm usable

# ══════════════════════════════════════════════════════════════════════════════
# TITLE BANNER
# ══════════════════════════════════════════════════════════════════════════════
title_data = [[
    hdr_cell("MANAGEMENT FLOWCHART", bg=NAVY, fg=WHITE, size=14),
    hdr_cell("Recurrent Sinusitis + Elevated IgE + Bilateral Turbinate Hypertrophy", bg=NAVY, fg=colors.HexColor("#AADDFF"), size=9, bold=False),
]]
title_tbl = Table(title_data, colWidths=[FULL_W * 0.38, FULL_W * 0.62])
title_tbl.setStyle(TableStyle([
    ("BACKGROUND", (0,0), (-1,-1), NAVY),
    ("ALIGN", (0,0), (-1,-1), "CENTER"),
    ("VALIGN", (0,0), (-1,-1), "MIDDLE"),
    ("TOPPADDING", (0,0), (-1,-1), 8),
    ("BOTTOMPADDING", (0,0), (-1,-1), 8),
    ("LEFTPADDING", (0,0), (-1,-1), 6),
    ("RIGHTPADDING", (0,0), (-1,-1), 6),
    ("ROUNDEDCORNERS", [4,4,4,4]),
]))
story.append(title_tbl)

# Subtitle bar
sub_data = [[
    hdr_cell("Female, 30 yrs  |  Hx: Recurrent Sinusitis  |  ↑ Total IgE  |  CT: B/L Turbinate Hypertrophy",
             bg=STEEL, fg=WHITE, size=8.5, bold=False)
]]
sub_tbl = Table(sub_data, colWidths=[FULL_W])
sub_tbl.setStyle(TableStyle([
    ("BACKGROUND", (0,0), (-1,-1), STEEL),
    ("ALIGN", (0,0), (-1,-1), "CENTER"),
    ("VALIGN", (0,0), (-1,-1), "MIDDLE"),
    ("TOPPADDING", (0,0), (-1,-1), 5),
    ("BOTTOMPADDING", (0,0), (-1,-1), 5),
]))
story.append(sub_tbl)
story.append(Spacer(1, 4*mm))

# ══════════════════════════════════════════════════════════════════════════════
# STEP 1: INITIAL ASSESSMENT
# ══════════════════════════════════════════════════════════════════════════════
def make_step_header(text, bg=NAVY, width=FULL_W):
    d = [[hdr_cell(text, bg=bg, size=9)]]
    t = Table(d, colWidths=[width])
    t.setStyle(TableStyle([
        ("BACKGROUND",(0,0),(-1,-1), bg),
        ("ALIGN",(0,0),(-1,-1),"CENTER"),
        ("VALIGN",(0,0),(-1,-1),"MIDDLE"),
        ("TOPPADDING",(0,0),(-1,-1),5),
        ("BOTTOMPADDING",(0,0),(-1,-1),5),
        ("LEFTPADDING",(0,0),(-1,-1),6),
        ("RIGHTPADDING",(0,0),(-1,-1),6),
    ]))
    return t

# STEP 1
story.append(make_step_header("STEP 1 — CLINICAL DIAGNOSIS & DIFFERENTIAL", bg=NAVY))
story.append(Spacer(1, 1.5*mm))

diag_data = [
    [
        # Col 1: CRS Criteria
        [
            Paragraph("<b>CRS Diagnostic Criteria (EPOS/Goldman-Cecil)</b>", S("x", fontName="Helvetica-Bold", fontSize=8, textColor=NAVY, leading=10)),
            Paragraph("Symptoms >3 months (≥2 of):", body_small),
        ] + bullet([
            "Mucopurulent nasal drainage",
            "Nasal obstruction / congestion",
            "Facial pressure / fullness",
            "Hyposmia / anosmia",
        ], color=colors.HexColor("#1B3A6B")) + [
            Paragraph("<b>PLUS objective finding:</b>", S("x2", fontName="Helvetica-Bold", fontSize=7.5, textColor=NAVY, leading=9.5)),
        ] + bullet([
            "Nasal/middle meatus polyps",
            "Edema / purulence in middle meatus",
            "CT: sinus opacification",
        ], color=colors.HexColor("#1B3A6B")),

        # Col 2: Differentials
        [
            Paragraph("<b>Rule Out (Key Differentials)</b>", S("x3", fontName="Helvetica-Bold", fontSize=8, textColor=RED_DARK, leading=10)),
        ] + bullet([
            "GPA (Wegener's): ANCA, saddle nose, hemoptysis",
            "NARES: nasal eosinophils, skin-test negative",
            "Vasomotor rhinitis: no eosinophils, irritant-driven",
            "Allergic Fungal Sinusitis: ↑IgE + hyperdense CT mucin",
            "Samter's Triad: polyps + asthma + ASA sensitivity",
            "CVID: ↓IgG/IgA/IgM, recurrent infections",
            "CSF rhinorrhea: β2-transferrin test",
            "Hypothyroidism / OCP-related rhinitis",
        ], color=RED_DARK),
    ]
]

diag_tbl = Table(diag_data, colWidths=[FULL_W*0.47, FULL_W*0.53])
diag_tbl.setStyle(TableStyle([
    ("BACKGROUND", (0,0), (0,0), LIGHT_BLUE),
    ("BACKGROUND", (1,0), (1,0), LIGHT_RED),
    ("VALIGN", (0,0), (-1,-1), "TOP"),
    ("TOPPADDING", (0,0), (-1,-1), 5),
    ("BOTTOMPADDING", (0,0), (-1,-1), 5),
    ("LEFTPADDING", (0,0), (-1,-1), 6),
    ("RIGHTPADDING", (0,0), (-1,-1), 5),
    ("BOX", (0,0), (-1,-1), 0.5, MID_GREY),
    ("INNERGRID", (0,0), (-1,-1), 0.5, MID_GREY),
]))
story.append(diag_tbl)
story.append(Spacer(1, 2*mm))
story.append(CentredArrow(FULL_W, color=STEEL))
story.append(Spacer(1, 1*mm))

# ══════════════════════════════════════════════════════════════════════════════
# STEP 2: INVESTIGATIONS
# ══════════════════════════════════════════════════════════════════════════════
story.append(make_step_header("STEP 2 — INVESTIGATIONS", bg=TEAL))
story.append(Spacer(1, 1.5*mm))

inv_data = [
    # Header row
    [
        hdr_cell("ALLERGY WORKUP", bg=TEAL, fg=WHITE, size=8),
        hdr_cell("NASAL / IMAGING", bg=TEAL, fg=WHITE, size=8),
        hdr_cell("LAB PANEL", bg=TEAL, fg=WHITE, size=8),
    ],
    [
        # Allergy
        [
            Paragraph("<b>Skin Prick Test (First Line)</b>", S("al1", fontName="Helvetica-Bold", fontSize=7.5, textColor=TEAL, leading=9.5)),
        ] + bullet([
            "HDM, cockroach, moulds",
            "Pollens, animal dander",
            "Positive: wheal ≥3mm > control",
            "Stop antihistamines 2–7 days prior",
            "Sensitivity 80–100%, Spec 70–90%",
        ], size=7) + [
            Paragraph("<b>Serum Specific IgE (ImmunoCAP)</b>", S("al2", fontName="Helvetica-Bold", fontSize=7.5, textColor=TEAL, leading=9.5)),
        ] + bullet([
            "If SPT contraindicated / skin disease",
            "Total IgE >100 IU/mL = strongly atopic",
            "Aspergillus/Alternaria IgE (if AFS suspected)",
        ], size=7),
        # Nasal/Imaging
        [
            Paragraph("<b>Nasal Endoscopy</b>", S("ni1", fontName="Helvetica-Bold", fontSize=7.5, textColor=TEAL, leading=9.5)),
        ] + bullet([
            "Turbinate size & mucosal colour",
            "Polyps (middle meatus)",
            "Purulent discharge",
            "Pale/bluish mucosa = allergic",
        ], size=7) + [
            Paragraph("<b>CT PNS (Non-contrast)</b>", S("ni2", fontName="Helvetica-Bold", fontSize=7.5, textColor=TEAL, leading=9.5)),
        ] + bullet([
            "Lund-Mackay score (max 24)",
            "OMC obstruction / polyps",
            "Hyperdense mucin → AFS",
            "Bony erosion → invasive fungal",
        ], size=7) + [
            Paragraph("<b>Nasal Smear</b>", S("ni3", fontName="Helvetica-Bold", fontSize=7.5, textColor=TEAL, leading=9.5)),
        ] + bullet([
            "Eosinophils = allergic / NARES",
            "Absent eos = vasomotor",
        ], size=7),
        # Labs
        [
            Paragraph("<b>Mandatory</b>", S("lb1", fontName="Helvetica-Bold", fontSize=7.5, textColor=TEAL, leading=9.5)),
        ] + bullet([
            "CBC + differential (eosinophilia?)",
            "Total serum IgE (quantify, dose biologic)",
            "ESR, CRP (inflammatory activity)",
        ], size=7) + [
            Paragraph("<b>If Clinically Indicated</b>", S("lb2", fontName="Helvetica-Bold", fontSize=7.5, textColor=TEAL, leading=9.5)),
        ] + bullet([
            "c-ANCA/p-ANCA, ANA → GPA",
            "IgG, IgA, IgM → exclude CVID",
            "Sweat chloride / CFTR → CF",
            "Nasal NO → primary ciliary dyskinesia",
            "β2-transferrin → CSF rhinorrhea",
            "Spirometry + FeNO → co-morbid asthma",
        ], size=7),
    ]
]

inv_tbl = Table(inv_data, colWidths=[FULL_W*0.33, FULL_W*0.35, FULL_W*0.32])
inv_tbl.setStyle(TableStyle([
    ("BACKGROUND", (0,0), (-1,0), TEAL),
    ("BACKGROUND", (0,1), (-1,1), LIGHT_TEAL),
    ("VALIGN", (0,0), (-1,-1), "TOP"),
    ("ALIGN", (0,0), (-1,0), "CENTER"),
    ("TOPPADDING", (0,0), (-1,-1), 5),
    ("BOTTOMPADDING", (0,0), (-1,-1), 5),
    ("LEFTPADDING", (0,0), (-1,-1), 5),
    ("RIGHTPADDING", (0,0), (-1,-1), 4),
    ("BOX", (0,0), (-1,-1), 0.5, MID_GREY),
    ("INNERGRID", (0,0), (-1,-1), 0.5, MID_GREY),
]))
story.append(inv_tbl)
story.append(Spacer(1, 2*mm))
story.append(CentredArrow(FULL_W, color=AMBER))
story.append(Spacer(1, 1*mm))

# ══════════════════════════════════════════════════════════════════════════════
# STEP 3: TREATMENT — ALLERGEN AVOIDANCE
# ══════════════════════════════════════════════════════════════════════════════
story.append(make_step_header("STEP 3 — TREATMENT: PILLAR 1 — ALLERGEN AVOIDANCE (Always First)", bg=AMBER))
story.append(Spacer(1, 1.5*mm))

avoid_data = [
    [
        [Paragraph("<b>HDM</b>", S("av1", fontName="Helvetica-Bold", fontSize=7.5, textColor=AMBER, leading=9))] +
        bullet(["Allergen-impermeable covers (mattress/pillow)", "Humidity <50%", "Hot water wash bedding >130°F", "HEPA vacuum"], size=7),
        [Paragraph("<b>Moulds</b>", S("av2", fontName="Helvetica-Bold", fontSize=7.5, textColor=AMBER, leading=9))] +
        bullet(["Dehumidify home", "Dilute bleach on windows/shower", "No indoor plants in bedroom"], size=7),
        [Paragraph("<b>Pollens</b>", S("av3", fontName="Helvetica-Bold", fontSize=7.5, textColor=AMBER, leading=9))] +
        bullet(["AC (filtered) in season", "Close windows during high pollen", "Shower after outdoor exposure"], size=7),
        [Paragraph("<b>Pets / Cockroaches</b>", S("av4", fontName="Helvetica-Bold", fontSize=7.5, textColor=AMBER, leading=9))] +
        bullet(["Rehome pet ideally; exclude from bedroom", "HEPA air purifier", "Exterminate cockroaches", "Seal food sources"], size=7),
    ]
]
avoid_tbl = Table(avoid_data, colWidths=[FULL_W*0.27, FULL_W*0.24, FULL_W*0.24, FULL_W*0.25])
avoid_tbl.setStyle(TableStyle([
    ("BACKGROUND", (0,0), (-1,-1), LIGHT_AMBER),
    ("VALIGN", (0,0), (-1,-1), "TOP"),
    ("TOPPADDING", (0,0), (-1,-1), 5),
    ("BOTTOMPADDING", (0,0), (-1,-1), 5),
    ("LEFTPADDING", (0,0), (-1,-1), 5),
    ("RIGHTPADDING", (0,0), (-1,-1), 4),
    ("BOX", (0,0), (-1,-1), 0.5, MID_GREY),
    ("INNERGRID", (0,0), (-1,-1), 0.5, MID_GREY),
]))
story.append(avoid_tbl)
story.append(Spacer(1, 2*mm))
story.append(CentredArrow(FULL_W, color=AMBER))
story.append(Spacer(1, 1*mm))

# ══════════════════════════════════════════════════════════════════════════════
# STEP 4: PHARMACOTHERAPY (main table)
# ══════════════════════════════════════════════════════════════════════════════
story.append(make_step_header("STEP 4 — TREATMENT: PILLAR 2 — PHARMACOTHERAPY (Stepwise)", bg=STEEL))
story.append(Spacer(1, 1.5*mm))

pharm_hdr = [
    hdr_cell("STEP", bg=STEEL, size=8),
    hdr_cell("DISEASE SEVERITY", bg=STEEL, size=8),
    hdr_cell("DRUG CLASS", bg=STEEL, size=8),
    hdr_cell("AGENTS & DOSES", bg=STEEL, size=8),
    hdr_cell("NOTES", bg=STEEL, size=8),
]

def mk(t, bold=False, col=BLACK, size=7.5):
    return Paragraph(t, S("c", fontName="Helvetica-Bold" if bold else "Helvetica",
                           fontSize=size, textColor=col, leading=9.5))

pharm_rows = [
    pharm_hdr,
    [
        mk("1", bold=True, col=STEEL),
        mk("Mild / Intermittent"),
        mk("2nd-gen Antihistamines\n(oral)", bold=True),
        [
            mk("• Cetirizine 10 mg OD", size=7),
            mk("• Levocetirizine 5 mg OD", size=7),
            mk("• Loratadine 10 mg OD", size=7),
            mk("• Fexofenadine 180 mg OD", size=7),
            mk("• Desloratadine 5 mg OD", size=7),
        ],
        mk("Targets: sneezing, rhinorrhea, itch.\n20–30% symptom reduction.\nDiminished efficacy with continuous exposure.", size=7),
    ],
    [
        mk("", bold=True, col=STEEL),
        mk(""),
        mk("Intranasal Antihistamines", bold=True),
        [mk("• Azelastine nasal spray", size=7),
         mk("• Olopatadine nasal spray", size=7)],
        mk("Faster onset than oral. Also\neffective for non-allergic rhinitis.", size=7),
    ],
    [
        mk("2", bold=True, col=GREEN_DARK),
        mk("Moderate–Severe /\nPerennial (THIS PATIENT)", bold=True, col=GREEN_DARK),
        mk("Intranasal Corticosteroids\n★ GOLD STANDARD", bold=True, col=GREEN_DARK),
        [
            mk("• Mometasone furoate 50µg — 2 sprays/nostril OD", size=7),
            mk("• Fluticasone propionate 50µg — 2 sprays/nostril OD", size=7),
            mk("• Fluticasone furoate 27.5µg — 2 sprays/nostril OD", size=7),
            mk("• Budesonide 32µg — 2 sprays/nostril BD", size=7),
            mk("• Triamcinolone 55µg — 2 sprays/nostril OD", size=7),
        ],
        mk("50–90% symptom reduction.\nAlso reduces turbinate hypertrophy.\nPreferred for perennial AR.\nIf adding antihistamine to INCS,\nuse INTRANASAL antihistamine.", size=7, col=GREEN_DARK),
    ],
    [
        mk("", bold=True),
        mk("Add-on Therapy"),
        mk("Leukotriene Antagonists"),
        [mk("• Montelukast 10 mg OD (preferred)", size=7),
         mk("• Zileuton 1200 mg BD", size=7),
         mk("• Zafirlukast 20 mg BD", size=7)],
        mk("Comparable to antihistamines.\nKey role in Samter's triad.\nContinue with SPT (no interference).", size=7),
    ],
    [
        mk("", bold=True),
        mk("All stages — adjunct"),
        mk("Nasal Saline Irrigation"),
        [mk("• Isotonic or hypertonic saline", size=7),
         mk("• BD–TDS irrigation", size=7)],
        mk("Clears allergens & mucus.\nImproves ciliary function.\nEvidence-based in CRS.", size=7),
    ],
    [
        mk("", bold=True),
        mk("Acute congestion only"),
        mk("Oral Decongestants\n(short-term)"),
        [mk("• Pseudoephedrine 30–60 mg q4–6h", size=7),
         mk("  (max 240 mg/day)", size=7)],
        mk("Risk of rebound congestion.\nDo NOT use >3–5 days.", size=7, col=RED_DARK),
    ],
    [
        mk("3", bold=True, col=AMBER),
        mk("Severe exacerbation /\nPre-surgical"),
        mk("Oral Corticosteroids\n(short course)", bold=True),
        [mk("• Methylprednisolone 8mg QID×1d", size=7),
         mk("  → 4mg QID×2d → taper over 6d", size=7)],
        mk("Reserve for acute severe flares.\nBridge to surgery.", size=7, col=AMBER),
    ],
    [
        mk("", bold=True),
        mk("Acute bacterial\nexacerbation"),
        mk("Antibiotics\n(targeted use)", bold=True),
        [mk("1st line: Amoxicillin 500mg TDS", size=7),
         mk("         OR Amox-clav 875/125mg BD × 7d", size=7),
         mk("PCN allergy: Doxycycline 100mg BD", size=7),
         mk("            OR Levofloxacin 500mg OD", size=7),
         mk("Severe: Amox-clav 2000/125mg BD", size=7)],
        mk("Only if: symptoms >7–10d without improvement.\nWatchful waiting acceptable initially (85% resolve).\nAvoid macrolides / 2nd–3rd gen cephalosporins.", size=7),
    ],
]

pharm_tbl = Table(pharm_rows,
    colWidths=[FULL_W*0.05, FULL_W*0.14, FULL_W*0.17, FULL_W*0.38, FULL_W*0.26])

row_bgs = [STEEL, LIGHT_BLUE, LIGHT_BLUE, LIGHT_GREEN, LIGHT_BLUE, LIGHT_BLUE, LIGHT_AMBER, LIGHT_AMBER, LIGHT_RED]
ts = [
    ("BACKGROUND", (0,0), (-1,0), STEEL),
    ("VALIGN", (0,0), (-1,-1), "TOP"),
    ("ALIGN", (0,0), (0,-1), "CENTER"),
    ("TOPPADDING", (0,0), (-1,-1), 4),
    ("BOTTOMPADDING", (0,0), (-1,-1), 4),
    ("LEFTPADDING", (0,0), (-1,-1), 4),
    ("RIGHTPADDING", (0,0), (-1,-1), 4),
    ("BOX", (0,0), (-1,-1), 0.5, MID_GREY),
    ("INNERGRID", (0,0), (-1,-1), 0.5, MID_GREY),
    ("BACKGROUND", (0,1), (-1,1), LIGHT_BLUE),
    ("BACKGROUND", (0,2), (-1,2), LIGHT_BLUE),
    ("BACKGROUND", (0,3), (-1,3), LIGHT_GREEN),
    ("BACKGROUND", (0,4), (-1,4), LIGHT_BLUE),
    ("BACKGROUND", (0,5), (-1,5), LIGHT_BLUE),
    ("BACKGROUND", (0,6), (-1,6), LIGHT_AMBER),
    ("BACKGROUND", (0,7), (-1,7), LIGHT_AMBER),
    ("BACKGROUND", (0,8), (-1,8), LIGHT_RED),
    ("SPAN", (0,1), (0,2)),
    ("SPAN", (0,3), (0,5)),
    ("SPAN", (0,6), (0,7)),
]
pharm_tbl.setStyle(TableStyle(ts))
story.append(pharm_tbl)
story.append(Spacer(1, 2*mm))
story.append(CentredArrow(FULL_W, color=PURPLE))
story.append(Spacer(1, 1*mm))

# ══════════════════════════════════════════════════════════════════════════════
# STEP 5: IMMUNOTHERAPY
# ══════════════════════════════════════════════════════════════════════════════
story.append(make_step_header("STEP 5 — TREATMENT: PILLAR 3 — ALLERGEN IMMUNOTHERAPY (Disease-Modifying)", bg=PURPLE))
story.append(Spacer(1, 1.5*mm))

immuno_data = [
    [
        hdr_cell("SUBCUTANEOUS (SCIT)", bg=PURPLE, size=8),
        hdr_cell("SUBLINGUAL (SLIT)", bg=PURPLE, size=8),
        hdr_cell("INDICATIONS / CONTRA", bg=PURPLE, size=8),
    ],
    [
        [
            Paragraph("<b>Build-up phase:</b> Weekly injections, escalating over 3–6 months", S("i1", fontName="Helvetica", fontSize=7.5, textColor=BLACK, leading=9.5)),
        ] + bullet([
            "Maintenance: Monthly injections",
            "Total duration: 3–5 years",
            "Observe 30 min post-injection",
            "Systemic reaction rate: 0.1%",
            "Polysensitized: multi-allergen mix",
        ], size=7),
        [
            Paragraph("<b>FDA-approved tablets:</b>", S("i2", fontName="Helvetica", fontSize=7.5, textColor=BLACK, leading=9.5)),
        ] + bullet([
            "Grass pollen (Grastek)",
            "HDM (Odactra/Acarizax)",
            "Better safety profile than SCIT",
            "European first-choice for single allergen",
            "Daily sublingual dosing at home",
        ], size=7),
        [
            Paragraph("<b>Indications:</b>", S("i3", fontName="Helvetica-Bold", fontSize=7.5, textColor=GREEN_DARK, leading=9.5)),
        ] + bullet([
            "Failed pharmacotherapy",
            "Desire for durable benefit",
            "High-risk for developing asthma",
            "New sensitization prevention",
        ], size=7, color=GREEN_DARK) + [
            Paragraph("<b>Contraindications:</b>", S("i4", fontName="Helvetica-Bold", fontSize=7.5, textColor=RED_DARK, leading=9.5)),
        ] + bullet([
            "Poorly controlled asthma",
            "Active malignancy / autoimmune",
            "Initiating during pregnancy",
            "Beta-blockers (relative)",
        ], size=7, color=RED_DARK),
    ]
]
immuno_tbl = Table(immuno_data, colWidths=[FULL_W*0.35, FULL_W*0.32, FULL_W*0.33])
immuno_tbl.setStyle(TableStyle([
    ("BACKGROUND", (0,0), (-1,0), PURPLE),
    ("BACKGROUND", (0,1), (-1,1), LIGHT_PURP),
    ("VALIGN", (0,0), (-1,-1), "TOP"),
    ("TOPPADDING", (0,0), (-1,-1), 5),
    ("BOTTOMPADDING", (0,0), (-1,-1), 5),
    ("LEFTPADDING", (0,0), (-1,-1), 5),
    ("RIGHTPADDING", (0,0), (-1,-1), 5),
    ("BOX", (0,0), (-1,-1), 0.5, MID_GREY),
    ("INNERGRID", (0,0), (-1,-1), 0.5, MID_GREY),
]))
story.append(immuno_tbl)
story.append(Spacer(1, 2*mm))
story.append(CentredArrow(FULL_W, color=RED_DARK, label="Refractory?"))
story.append(Spacer(1, 1*mm))

# ══════════════════════════════════════════════════════════════════════════════
# STEP 6: SURGICAL + BIOLOGICS (side by side)
# ══════════════════════════════════════════════════════════════════════════════
story.append(make_step_header("STEP 6 — REFRACTORY DISEASE: SURGERY + BIOLOGICS", bg=RED_DARK))
story.append(Spacer(1, 1.5*mm))

surg_bio_data = [
    [
        hdr_cell("TURBINATE SURGERY\n(if medical therapy fails)", bg=colors.HexColor("#8B2020"), size=8),
        hdr_cell("FUNCTIONAL ENDOSCOPIC\nSINUS SURGERY (FESS)", bg=colors.HexColor("#8B2020"), size=8),
        hdr_cell("BIOLOGICS\n(CRSwNP + Type 2 Inflammation)", bg=colors.HexColor("#8B2020"), size=8),
    ],
    [
        [
            Paragraph("<b>⚠ Do NOT total turbinectomy</b> → Empty nose syndrome", S("s1", fontName="Helvetica-Bold", fontSize=7, textColor=RED_DARK, leading=9)),
            Paragraph("<b>Options (preserve mucociliary function):</b>", S("s2", fontName="Helvetica-Bold", fontSize=7.5, textColor=BLACK, leading=9.5)),
        ] + bullet([
            "Radiofrequency volume reduction (RFVTR)",
            "Turbinoplasty (modified Mabry technique)",
            "Microdebrider-assisted turbinoplasty",
            "Laser (Holmium:YAG / KTP)",
            "Partial inferior turbinectomy",
            "Cryosurgery / infrared coagulation",
        ], size=7),
        bullet([
            "Opens ostiomeatal complex (OMC)",
            "Removes obstructive tissue",
            "Improves mucus drainage",
            "Enhances future topical drug delivery",
            "Post-op: INCS + saline irrigation mandatory",
            "For recurrent acute + CRS refractory to 3+ months medical Rx",
        ], size=7) + [
            Paragraph("<b>Indication:</b> Failure after maximal\nmedical therapy (≥3 months)", S("s3", fontName="Helvetica-Bold", fontSize=7.5, textColor=RED_DARK, leading=9.5)),
        ],
        [
            Paragraph("<b>Agents (Goldman-Cecil):</b>", S("bio1", fontName="Helvetica-Bold", fontSize=7.5, textColor=BLACK, leading=9.5)),
        ] + [
            Paragraph("• <b>Dupilumab</b> 300mg SC q2wks (anti-IL-4Rα)", S("bio2", fontName="Helvetica", fontSize=7, textColor=BLACK, leading=9)),
            Paragraph("• <b>Omalizumab</b> SC (dose by weight + IgE) (anti-IgE)", S("bio3", fontName="Helvetica", fontSize=7, textColor=BLACK, leading=9)),
            Paragraph("• <b>Mepolizumab</b> 100mg SC q4wks (anti-IL-5)", S("bio4", fontName="Helvetica", fontSize=7, textColor=BLACK, leading=9)),
            Paragraph("• <b>Tezepelumab</b> (anti-TSLP) — Phase III 2025 [NEJM]", S("bio5", fontName="Helvetica", fontSize=7, textColor=BLACK, leading=9)),
            Spacer(1, 2),
            Paragraph("<b>Reserve for:</b>", S("bio6", fontName="Helvetica-Bold", fontSize=7.5, textColor=BLACK, leading=9.5)),
        ] + bullet([
            "Recurrent disease post-FESS",
            "Comorbidities precluding surgery",
            "Severe Type 2 eosinophilic CRS",
        ], size=7),
    ]
]
surg_bio_tbl = Table(surg_bio_data, colWidths=[FULL_W*0.31, FULL_W*0.34, FULL_W*0.35])
surg_bio_tbl.setStyle(TableStyle([
    ("BACKGROUND", (0,0), (-1,0), colors.HexColor("#8B2020")),
    ("BACKGROUND", (0,1), (-1,1), LIGHT_RED),
    ("VALIGN", (0,0), (-1,-1), "TOP"),
    ("TOPPADDING", (0,0), (-1,-1), 5),
    ("BOTTOMPADDING", (0,0), (-1,-1), 5),
    ("LEFTPADDING", (0,0), (-1,-1), 5),
    ("RIGHTPADDING", (0,0), (-1,-1), 5),
    ("BOX", (0,0), (-1,-1), 0.5, MID_GREY),
    ("INNERGRID", (0,0), (-1,-1), 0.5, MID_GREY),
]))
story.append(surg_bio_tbl)
story.append(Spacer(1, 2*mm))

# ══════════════════════════════════════════════════════════════════════════════
# WHEN TO REFER BOX
# ══════════════════════════════════════════════════════════════════════════════
refer_data = [
    [hdr_cell("⚠  WHEN TO REFER TO ENT IMMEDIATELY (Goldman-Cecil)", bg=colors.HexColor("#7B1515"), size=9)],
    [[
        Paragraph(
            "Temperature >39°C + orbital edema / severe headache / visual disturbance / altered mental status / meningeal signs  |  "
            "Failure to respond to >2 antibiotic courses  |  Anatomic abnormality / nosocomial infection  |  "
            "Immunocompromised  |  Unusual / resistant pathogens  |  Fungal sinusitis / granulomatous disease  |  "
            "Recurrent episodes suggesting CRS (this patient ✓)",
            S("ref", fontName="Helvetica", fontSize=7.5, textColor=BLACK, leading=9.5, alignment=TA_JUSTIFY)
        )
    ]]
]
refer_tbl = Table(refer_data, colWidths=[FULL_W])
refer_tbl.setStyle(TableStyle([
    ("BACKGROUND", (0,0), (-1,0), colors.HexColor("#7B1515")),
    ("BACKGROUND", (0,1), (-1,1), colors.HexColor("#FFF0F0")),
    ("VALIGN", (0,0), (-1,-1), "MIDDLE"),
    ("TOPPADDING", (0,0), (-1,-1), 5),
    ("BOTTOMPADDING", (0,0), (-1,-1), 5),
    ("LEFTPADDING", (0,0), (-1,-1), 6),
    ("RIGHTPADDING", (0,0), (-1,-1), 6),
    ("BOX", (0,0), (-1,-1), 1, colors.HexColor("#7B1515")),
]))
story.append(refer_tbl)
story.append(Spacer(1, 2*mm))

# ══════════════════════════════════════════════════════════════════════════════
# FOOTER
# ══════════════════════════════════════════════════════════════════════════════
footer_data = [[
    Paragraph(
        "<b>Sources:</b> Goldman-Cecil Medicine (Intl Ed.) Ch.394 • Cummings Otolaryngology HNS Ch.29,35 • "
        "Scott-Brown's ORL Vol.1&2 • EPOS Guidelines • "
        "Cai S et al. <i>Allergy</i> 2025 [PMID:39985317] • Lipworth BJ et al. <i>NEJM</i> 2025 [PMID:40106374]",
        S("foot", fontName="Helvetica", fontSize=6.5, textColor=colors.HexColor("#555555"), leading=9, alignment=TA_CENTER)
    ),
    Paragraph(
        "<b>Orris MD Reference</b> | June 2026",
        S("foot2", fontName="Helvetica-Bold", fontSize=6.5, textColor=colors.HexColor("#555555"), leading=9, alignment=TA_CENTER)
    ),
]]
footer_tbl = Table(footer_data, colWidths=[FULL_W*0.78, FULL_W*0.22])
footer_tbl.setStyle(TableStyle([
    ("BACKGROUND", (0,0), (-1,-1), LIGHT_GREY),
    ("VALIGN", (0,0), (-1,-1), "MIDDLE"),
    ("TOPPADDING", (0,0), (-1,-1), 5),
    ("BOTTOMPADDING", (0,0), (-1,-1), 5),
    ("LEFTPADDING", (0,0), (-1,-1), 6),
    ("RIGHTPADDING", (0,0), (-1,-1), 6),
    ("BOX", (0,0), (-1,-1), 0.5, MID_GREY),
]))
story.append(footer_tbl)

# ── Build ──────────────────────────────────────────────────────────────────────
doc.build(story)
print("PDF generated successfully.")
Running Command

cd /home/daytona/workspace/sinusitis-flowchart && python generate_flowchart.py 2>&1

Running Command

ls -lh /home/daytona/workspace/sinusitis-flowchart/

The PDF has been generated successfully (13 KB, single A4 page). Here's your download link:

Sinusitis Management Flowchart

PDF Document · PDF


What's inside the flowchart (single A4, print-ready):
SectionContent
Title BannerPatient profile: 30F, recurrent sinusitis, ↑IgE, B/L turbinate hypertrophy
Step 1CRS diagnostic criteria (EPOS) + full differential diagnosis table
Step 2Investigations - allergy workup, nasal/imaging, and lab panel (side-by-side)
Step 3Allergen avoidance - HDM, moulds, pollens, pets
Step 4Pharmacotherapy stepwise table - antihistamines → INCS (gold standard with doses) → LTRAs → saline → decongestants → OCS → antibiotics
Step 5Immunotherapy - SCIT vs SLIT, indications, contraindications
Step 6Surgical options (turbinate reduction techniques + FESS) + Biologics table (Dupilumab, Omalizumab, Mepolizumab, Tezepelumab)
Emergency Refer BoxENT referral criteria (red-flag conditions)
FooterFull source citations including 2025 NEJM + Allergy meta-analysis
Color-coded by severity tier (blue → green → amber → red) with directional arrows between each step.
This is a shared conversation. Sign in to Orris to start your own chat.