Proptosis one slide complied everything etiology classifications examinations management guidelines facts required at residency level in detailed version with charts images and graph

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

proptosis classification etiology residency ophthalmology CAS score Hertel exophthalmometer

Loading Image
Loading Image
Running Command

mkdir -p /home/daytona/workspace/proptosis-pptx && ls /home/daytona/workspace/proptosis-pptx/

Writing File

~/proptosis-pptx/build.js

const pptxgen = require("pptxgenjs");
const { execSync } = require("child_process");

// Fetch images as base64
function fetchImages(urls) {
  try {
    const result = execSync(
      `node /home/daytona/skills/shared/scripts/fetch_images.js ${urls.map(u => `"${u}"`).join(" ")}`
    ).toString();
    return JSON.parse(result);
  } catch(e) {
    console.error("Image fetch error:", e.message);
    return urls.map(u => ({ url: u, base64: null, error: e.message }));
  }
}

const imgUrls = [
  "https://cdn.orris.care/cdss_images/3efe8cae9b67dd794499f1dacce0aa87c80a5cc7a7df2794139cfefe05fd6367.png",  // lid signs TED
  "https://cdn.orris.care/cdss_images/ecb379b38d8a7e399f46854c2a04031b26948eb6ca268008cb787309e27a4031.png",  // proptosis in TED
  "https://cdn.orris.care/cdss_images/8abf13c5a940a0a114de480d9cea3886fb6ad81b96e5c11aa4181aa4da0d80e7.png",  // soft tissue TED
  "https://cdn.orris.care/cdss_images/7a1e9a2052317aa918d444bd2e2230470e6853e8bc7dd8a4b9bf96b60adb76da.png",  // restrictive myopathy
];

console.log("Fetching images...");
const imgs = fetchImages(imgUrls);
console.log("Images fetched:", imgs.map(i => ({ url: i.url.slice(-20), ok: !i.error })));

// ─── Theme ───────────────────────────────────────────────────────────────────
const C = {
  navy:    "0A1628",
  teal:    "0E7C7B",
  amber:   "E8A317",
  coral:   "E05C5C",
  slate:   "2D3A4A",
  light:   "EBF4F8",
  white:   "FFFFFF",
  muted:   "8FA3B1",
  success: "27AE60",
  danger:  "C0392B",
  purple:  "6C3483",
};

let pres = new pptxgen();
pres.layout = "LAYOUT_WIDE"; // 13.3" x 7.5"
pres.title = "Proptosis (Exophthalmos) – Residency Level";
pres.author = "Orris Medical AI";

// ─── Helper Functions ─────────────────────────────────────────────────────────
function hdr(slide, title, subtitle) {
  // Dark navy header bar
  slide.addShape(pres.ShapeType.rect, { x: 0, y: 0, w: 13.3, h: 0.75, fill: { color: C.navy } });
  slide.addText(title, {
    x: 0.2, y: 0, w: 9.5, h: 0.75,
    fontSize: 22, bold: true, color: C.white, valign: "middle", margin: 0
  });
  if (subtitle) {
    slide.addText(subtitle, {
      x: 9.7, y: 0, w: 3.4, h: 0.75,
      fontSize: 11, color: C.amber, valign: "middle", align: "right", margin: 0
    });
  }
  // Accent line
  slide.addShape(pres.ShapeType.rect, { x: 0, y: 0.75, w: 13.3, h: 0.04, fill: { color: C.teal } });
}

function footer(slide, num, total) {
  slide.addShape(pres.ShapeType.rect, { x: 0, y: 7.25, w: 13.3, h: 0.25, fill: { color: C.navy } });
  slide.addText(`Proptosis – Residency Reference  |  Slide ${num}/${total}`, {
    x: 0.2, y: 7.25, w: 12.9, h: 0.25,
    fontSize: 8, color: C.muted, valign: "middle", margin: 0
  });
}

function sectionCard(slide, x, y, w, h, title, lines, accent) {
  slide.addShape(pres.ShapeType.rect, { x, y, w, h, fill: { color: C.light }, line: { color: accent, width: 2 } });
  slide.addShape(pres.ShapeType.rect, { x, y, w, h: 0.32, fill: { color: accent } });
  slide.addText(title, { x: x+0.08, y, w: w-0.1, h: 0.32, fontSize: 11, bold: true, color: C.white, valign: "middle", margin: 0 });
  const items = lines.map((l, i) => ({ text: l, options: { bullet: l.startsWith("•") ? false : true, breakLine: i < lines.length-1, fontSize: 9.5, color: C.slate } }));
  slide.addText(items, { x: x+0.1, y: y+0.36, w: w-0.2, h: h-0.44, valign: "top", margin: 0 });
}

function tableSlide(slide, headers, rows, x, y, w, h, colW) {
  const tableData = [
    headers.map(h => ({ text: h, options: { bold: true, color: C.white, fontSize: 10, align: "center" } })),
    ...rows.map(row => row.map(cell => ({ text: cell, options: { fontSize: 9, color: C.slate } })))
  ];
  slide.addTable(tableData, {
    x, y, w, colW,
    rowH: 0.32,
    fill: { color: C.white },
    border: { type: "solid", color: "CCDDEA", pt: 1 },
    fontFace: "Calibri",
    autoPage: false,
  });
  // Manually style header row
  slide.addShape(pres.ShapeType.rect, { x, y, w, h: 0.36, fill: { color: C.navy } });
  slide.addText(headers.map((h,i) => ({ text: h, options: { breakLine: false } })).map(t => t.text).join("   "), {
    x: x+0.05, y, w: w-0.1, h: 0.36,
    fontSize: 10, bold: true, color: C.white, valign: "middle", margin: 0
  });
}

// ─────────────────────────────────────────────────────────────────────────────
// SLIDE 1 – Title
// ─────────────────────────────────────────────────────────────────────────────
{
  let sl = pres.addSlide();
  sl.addShape(pres.ShapeType.rect, { x: 0, y: 0, w: 13.3, h: 7.5, fill: { color: C.navy } });
  // Gradient overlay band
  sl.addShape(pres.ShapeType.rect, { x: 0, y: 2.8, w: 13.3, h: 2.5, fill: { color: C.teal }, transparency: 80 });
  sl.addText("PROPTOSIS", {
    x: 0.5, y: 0.6, w: 12.3, h: 1.5,
    fontSize: 72, bold: true, color: C.white, align: "center", charSpacing: 8
  });
  sl.addText("EXOPHTHALMOS", {
    x: 0.5, y: 1.9, w: 12.3, h: 0.9,
    fontSize: 36, bold: false, color: C.amber, align: "center", charSpacing: 6
  });
  sl.addShape(pres.ShapeType.rect, { x: 3, y: 2.85, w: 7.3, h: 0.05, fill: { color: C.teal } });

  const topics = ["Etiology & Classification", "Clinical Examination", "Grading Systems", "Investigations", "Management Guidelines", "Key Facts & Mnemonics"];
  topics.forEach((t, i) => {
    const col = i % 3;
    const row = Math.floor(i / 3);
    sl.addShape(pres.ShapeType.rect, {
      x: 0.7 + col*4.1, y: 3.1 + row*0.72, w: 3.8, h: 0.58,
      fill: { color: "FFFFFF", transparency: 85 },
      line: { color: C.teal, width: 1 }
    });
    sl.addText(t, {
      x: 0.7 + col*4.1, y: 3.1 + row*0.72, w: 3.8, h: 0.58,
      fontSize: 12, color: C.white, align: "center", valign: "middle", bold: true
    });
  });

  sl.addText("Residency-Level Reference  |  Ophthalmology & ENT", {
    x: 0.5, y: 6.7, w: 12.3, h: 0.5,
    fontSize: 13, color: C.muted, align: "center"
  });
}

// ─────────────────────────────────────────────────────────────────────────────
// SLIDE 2 – Definition & Anatomy
// ─────────────────────────────────────────────────────────────────────────────
{
  let sl = pres.addSlide();
  sl.addShape(pres.ShapeType.rect, { x: 0, y: 0, w: 13.3, h: 7.5, fill: { color: C.light } });
  hdr(sl, "DEFINITION & ORBITAL ANATOMY", "Slide 2");

  // Definition box
  sl.addShape(pres.ShapeType.rect, { x: 0.3, y: 0.95, w: 8.3, h: 1.2, fill: { color: C.navy }, line: { color: C.teal, width: 2 } });
  sl.addText([
    { text: "PROPTOSIS / EXOPHTHALMOS: ", options: { bold: true, color: C.amber, fontSize: 12 } },
    { text: "Anterior displacement of the globe beyond the orbital rim.", options: { color: C.white, fontSize: 11 } }
  ], { x: 0.4, y: 0.97, w: 8.1, h: 0.55, valign: "middle" });
  sl.addText([
    { text: "Normal: ", options: { bold: true, color: C.amber, fontSize: 11 } },
    { text: "≤21 mm from lateral orbital rim to corneal apex (Hertel)  |  ", options: { color: C.white, fontSize: 10.5 } },
    { text: "Asymmetry: >2 mm difference between eyes is significant", options: { bold: true, color: C.coral, fontSize: 10.5 } }
  ], { x: 0.4, y: 1.5, w: 8.1, h: 0.55, valign: "middle" });

  // Racial norms
  sl.addShape(pres.ShapeType.rect, { x: 0.3, y: 2.28, w: 8.3, h: 1.0, fill: { color: C.white }, line: { color: C.amber, width: 2 } });
  sl.addText("Average Hertel Measurements by Race", { x: 0.4, y: 2.3, w: 8.1, h: 0.3, fontSize: 11, bold: true, color: C.navy });
  const raceData = [["Asian", "18 mm"], ["White / Caucasian", "20 mm"], ["Black / African", "22 mm"]];
  raceData.forEach(([race, val], i) => {
    sl.addShape(pres.ShapeType.rect, { x: 0.4 + i*2.7, y: 2.65, w: 2.5, h: 0.5, fill: { color: C.teal }, transparency: i*20 });
    sl.addText(`${race}\n${val}`, { x: 0.4 + i*2.7, y: 2.65, w: 2.5, h: 0.5, fontSize: 10, bold: true, color: C.white, align: "center", valign: "middle" });
  });

  // Anatomy facts
  sl.addShape(pres.ShapeType.rect, { x: 0.3, y: 3.42, w: 8.3, h: 3.5, fill: { color: C.white }, line: { color: C.navy, width: 1 } });
  sl.addShape(pres.ShapeType.rect, { x: 0.3, y: 3.42, w: 8.3, h: 0.32, fill: { color: C.slate } });
  sl.addText("ORBITAL ANATOMY ESSENTIALS", { x: 0.4, y: 3.42, w: 8.1, h: 0.32, fontSize: 11, bold: true, color: C.white, valign: "middle" });

  const anatomyFacts = [
    "Volume: ~30 mL  |  Globe occupies ~7.5 mL",
    "Orbit is a pyramid with apex at the optic canal and base anteriorly",
    "Walls: Roof = frontal bone + lesser wing of sphenoid",
    "Floor = maxillary + zygomatic + palatine bones (thinnest wall)",
    "Medial wall = ethmoid (lamina papyracea) – thinnest, adjacent to sinuses",
    "Lateral wall = zygomatic + greater wing of sphenoid (strongest)",
    "Annulus of Zinn: fibrous ring at orbital apex giving origin to 4 recti",
    "Muscle cone: intraconal space contains optic nerve, ophthalmic artery",
    "Extraconal space: between muscles and periorbita – communicates with sinuses",
    "Venous drainage: via superior/inferior ophthalmic veins → cavernous sinus",
  ];
  anatomyFacts.forEach((f, i) => {
    sl.addText([
      { text: "▸ ", options: { color: C.teal, bold: true } },
      { text: f, options: { color: C.slate } }
    ], { x: 0.4, y: 3.8 + i*0.3, w: 8.1, h: 0.28, fontSize: 9.5 });
  });

  // Side panel
  sl.addShape(pres.ShapeType.rect, { x: 8.8, y: 0.9, w: 4.2, h: 5.8, fill: { color: C.navy }, line: { color: C.teal, width: 1.5 } });
  sl.addText("KEY DISTINCTIONS", { x: 8.9, y: 0.95, w: 4.0, h: 0.35, fontSize: 12, bold: true, color: C.amber, align: "center" });

  const distinctions = [
    { term: "Proptosis", def: "Forward displacement of globe regardless of cause" },
    { term: "Exophthalmos", def: "Proptosis specifically due to thyroid eye disease (convention)" },
    { term: "Pseudo-proptosis", def: "Apparent displacement: high myopia, contralateral enophthalmos, facial asymmetry" },
    { term: "Enophthalmos", def: "Posterior displacement (opposite): blow-out fracture, post-irradiation, scirrhous metastasis" },
    { term: "Pulsatile proptosis", def: "Arterial pulsation transmitted to globe – think CCF or bony defect" },
    { term: "Axial proptosis", def: "Straight forward – intraconal lesion (e.g., optic nerve tumor)" },
    { term: "Non-axial proptosis", def: "Displaced direction opposite to lesion (extraconal)" },
  ];
  distinctions.forEach((d, i) => {
    sl.addText(d.term, { x: 8.9, y: 1.42 + i*0.68, w: 4.0, h: 0.24, fontSize: 10, bold: true, color: C.amber });
    sl.addText(d.def, { x: 8.9, y: 1.66 + i*0.68, w: 4.0, h: 0.38, fontSize: 9, color: C.white });
  });

  footer(sl, 2, 11);
}

// ─────────────────────────────────────────────────────────────────────────────
// SLIDE 3 – Etiology Classification
// ─────────────────────────────────────────────────────────────────────────────
{
  let sl = pres.addSlide();
  sl.addShape(pres.ShapeType.rect, { x: 0, y: 0, w: 13.3, h: 7.5, fill: { color: C.light } });
  hdr(sl, "ETIOLOGY & CLASSIFICATION", "Slide 3");

  // Most common callout
  sl.addShape(pres.ShapeType.rect, { x: 0.3, y: 0.9, w: 12.7, h: 0.62, fill: { color: C.amber }, transparency: 15 });
  sl.addText([
    { text: "ADULTS: ", options: { bold: true, color: C.navy, fontSize: 12 } },
    { text: "Most common = Graves' Disease (Thyroid Eye Disease)   |   ", options: { color: C.navy, fontSize: 11 } },
    { text: "CHILDREN: ", options: { bold: true, color: C.navy, fontSize: 12 } },
    { text: "Most common = Orbital Cellulitis", options: { color: C.navy, fontSize: 11 } }
  ], { x: 0.4, y: 0.9, w: 12.5, h: 0.62, valign: "middle" });

  // 6 category cards
  const categories = [
    {
      title: "THYROID / ENDOCRINE",
      accent: C.teal,
      items: ["Graves' orbitopathy (#1 in adults)", "Thyroid dysfunction (hypo/hyper/euthyroid)", "TSH-receptor antibody mediated", "Bilateral, axial proptosis typical"]
    },
    {
      title: "INFLAMMATORY / AUTOIMMUNE",
      accent: C.purple,
      items: ["Orbital pseudotumor (idiopathic orbital inflammation)", "Granulomatosis with polyangiitis (Wegener's) – c-ANCA+", "Sarcoidosis – lacrimal gland, orbital fat", "Orbital myositis – EOM involvement"]
    },
    {
      title: "NEOPLASTIC – PRIMARY",
      accent: C.coral,
      items: ["Cavernous hemangioma (most common benign adult)", "Optic nerve glioma (children, NF1)", "Optic nerve sheath meningioma", "Dermoid/epidermoid cyst, lacrimal gland tumors", "Lymphoproliferative lesions, schwannoma"]
    },
    {
      title: "NEOPLASTIC – SECONDARY/METASTATIC",
      accent: C.danger,
      items: ["Metastatic: Breast (most common) > Prostate > Melanoma > Lung", "Direct extension: Paranasal sinus tumors (SCC, adenoCA)", "Rhabdomyosarcoma (#1 primary malignant in children)", "Neuroblastoma metastasis (raccoon eyes)"]
    },
    {
      title: "INFECTIOUS / VASCULAR",
      accent: C.amber,
      items: ["Orbital cellulitis – post-septal (Chandler classification)", "Subperiosteal / orbital abscess", "Carotid-cavernous fistula (direct/indirect) – pulsatile!", "Mucormycosis (immunocompromised – emergency)", "Aspergillosis, fungal sinusitis"]
    },
    {
      title: "STRUCTURAL / OTHER",
      accent: C.navy,
      items: ["Sinus disease: frontal osteoma, mucocele", "Blow-out fracture complications", "Arteriovenous malformation", "Lymphangioma – bleeds → sudden proptosis", "Cholesterol granuloma"]
    }
  ];

  categories.forEach((cat, i) => {
    const col = i % 3;
    const row = Math.floor(i / 3);
    const x = 0.3 + col * 4.35;
    const y = 1.65 + row * 2.7;
    sectionCard(sl, x, y, 4.15, 2.55, cat.title, cat.items, cat.accent);
  });

  footer(sl, 3, 11);
}

// ─────────────────────────────────────────────────────────────────────────────
// SLIDE 4 – Chandler & EUGOGO Classification
// ─────────────────────────────────────────────────────────────────────────────
{
  let sl = pres.addSlide();
  sl.addShape(pres.ShapeType.rect, { x: 0, y: 0, w: 13.3, h: 7.5, fill: { color: C.light } });
  hdr(sl, "GRADING CLASSIFICATIONS", "Slide 4");

  // Chandler Classification
  sl.addShape(pres.ShapeType.rect, { x: 0.3, y: 0.9, w: 5.9, h: 5.2, fill: { color: C.white }, line: { color: C.coral, width: 2 } });
  sl.addShape(pres.ShapeType.rect, { x: 0.3, y: 0.9, w: 5.9, h: 0.38, fill: { color: C.coral } });
  sl.addText("CHANDLER CLASSIFICATION – Orbital Cellulitis", { x: 0.35, y: 0.9, w: 5.8, h: 0.38, fontSize: 11, bold: true, color: C.white, valign: "middle" });

  const chandler = [
    ["I", "Preseptal (Periorbital) Cellulitis", "Eyelid edema only, no proptosis, no limitation of EOM"],
    ["II", "Orbital Cellulitis", "Edema of orbital fat, mild proptosis, no abscess"],
    ["III", "Subperiosteal Abscess", "Pus between periorbita & bony wall, globe displacement"],
    ["IV", "Orbital Abscess", "True abscess within orbital fat, severe proptosis, ophthalmoplegia, pain"],
    ["V", "Cavernous Sinus Thrombosis", "Bilateral signs, sepsis, meningism, high mortality"],
  ];

  chandler.forEach(([num, name, desc], i) => {
    const rowColors = [C.success, C.teal, C.amber, C.coral, C.danger];
    sl.addShape(pres.ShapeType.rect, { x: 0.35, y: 1.38 + i*0.76, w: 0.38, h: 0.62, fill: { color: rowColors[i] } });
    sl.addText(num, { x: 0.35, y: 1.38 + i*0.76, w: 0.38, h: 0.62, fontSize: 14, bold: true, color: C.white, align: "center", valign: "middle" });
    sl.addShape(pres.ShapeType.rect, { x: 0.73, y: 1.38 + i*0.76, w: 5.47, h: 0.62, fill: { color: i % 2 === 0 ? "F0F8FA" : C.white } });
    sl.addText(name, { x: 0.78, y: 1.38 + i*0.76, w: 5.3, h: 0.25, fontSize: 10, bold: true, color: C.slate });
    sl.addText(desc, { x: 0.78, y: 1.63 + i*0.76, w: 5.3, h: 0.3, fontSize: 8.5, color: C.muted });
  });

  // EUGOGO / CAS
  sl.addShape(pres.ShapeType.rect, { x: 6.5, y: 0.9, w: 6.5, h: 5.2, fill: { color: C.white }, line: { color: C.teal, width: 2 } });
  sl.addShape(pres.ShapeType.rect, { x: 6.5, y: 0.9, w: 6.5, h: 0.38, fill: { color: C.teal } });
  sl.addText("CLINICAL ACTIVITY SCORE (CAS) – Thyroid Eye Disease", { x: 6.55, y: 0.9, w: 6.4, h: 0.38, fontSize: 11, bold: true, color: C.white, valign: "middle" });

  sl.addText("Score ≥3/7 (initial visit) or ≥4/10 (follow-up) = active disease → immunosuppression indicated", {
    x: 6.55, y: 1.35, w: 6.3, h: 0.38, fontSize: 9.5, color: C.danger, bold: true
  });

  const casItems = [
    "1. Spontaneous retrobulbar ache",
    "2. Pain on eye movement",
    "3. Eyelid redness",
    "4. Conjunctival injection",
    "5. Chemosis (conjunctival edema)",
    "6. Swelling of caruncle or plica",
    "7. Eyelid edema / fullness",
    "— Follow-up additions —",
    "8. Increase in proptosis ≥2 mm",
    "9. Decrease in EOM excursion ≥8°",
    "10. Decrease in VA ≥1 Snellen line",
  ];

  casItems.forEach((item, i) => {
    const isHeader = item.startsWith("—");
    if (isHeader) {
      sl.addText(item, { x: 6.55, y: 1.8 + i*0.29, w: 6.3, h: 0.26, fontSize: 9, color: C.amber, bold: true, italic: true });
    } else {
      const num = parseInt(item);
      const color = num >= 8 ? C.purple : C.teal;
      sl.addShape(pres.ShapeType.rect, { x: 6.55, y: 1.8 + i*0.29, w: 0.22, h: 0.22, fill: { color } });
      sl.addText(item, { x: 6.8, y: 1.8 + i*0.29, w: 6.1, h: 0.24, fontSize: 9, color: C.slate });
    }
  });

  // EUGOGO severity
  sl.addShape(pres.ShapeType.rect, { x: 0.3, y: 6.2, w: 12.7, h: 0.85, fill: { color: C.navy } });
  sl.addText("EUGOGO SEVERITY", { x: 0.5, y: 6.2, w: 2.0, h: 0.85, fontSize: 11, bold: true, color: C.amber, valign: "middle" });
  const eugogo = [
    ["MILD", "Minor impact on daily life; corneal exposure, minor soft tissue signs", C.success],
    ["MODERATE-SEVERE", "Significant impact; lid retraction ≥2mm, proptosis ≥3mm above normal, diplopia", C.amber],
    ["SIGHT-THREATENING", "Dysthyroid optic neuropathy (DON) or corneal breakdown", C.danger],
  ];
  eugogo.forEach(([sev, desc, col], i) => {
    sl.addShape(pres.ShapeType.rect, { x: 2.5 + i*3.5, y: 6.24, w: 3.3, h: 0.77, fill: { color: col }, transparency: 20 });
    sl.addText(`${sev}\n${desc}`, { x: 2.55 + i*3.5, y: 6.24, w: 3.2, h: 0.77, fontSize: 8.5, color: C.white, bold: i===2, align: "center", valign: "middle" });
  });

  footer(sl, 4, 11);
}

// ─────────────────────────────────────────────────────────────────────────────
// SLIDE 5 – Clinical Examination
// ─────────────────────────────────────────────────────────────────────────────
{
  let sl = pres.addSlide();
  sl.addShape(pres.ShapeType.rect, { x: 0, y: 0, w: 13.3, h: 7.5, fill: { color: C.light } });
  hdr(sl, "CLINICAL EXAMINATION OF PROPTOSIS", "Slide 5");

  // History box
  sl.addShape(pres.ShapeType.rect, { x: 0.3, y: 0.9, w: 4.0, h: 5.5, fill: { color: C.white }, line: { color: C.navy, width: 1.5 } });
  sl.addShape(pres.ShapeType.rect, { x: 0.3, y: 0.9, w: 4.0, h: 0.34, fill: { color: C.navy } });
  sl.addText("HISTORY", { x: 0.4, y: 0.9, w: 3.8, h: 0.34, fontSize: 11, bold: true, color: C.white, valign: "middle" });
  const hxItems = [
    { q: "Onset", v: "Acute (hrs-days) = infective/inflammatory\nSubacute (weeks) = Graves'\nChronic (months) = neoplastic" },
    { q: "Laterality", v: "Bilateral → thyroid disease\nUnilateral → infection, tumor, trauma" },
    { q: "Pain", v: "Severe → cellulitis, pseudotumor\nWith EOM → myositis\nPainless → neoplasm" },
    { q: "Diplopia", v: "Restrictive (fibrosis) vs neurogenic" },
    { q: "Visual loss", v: "Compressive optic neuropathy – urgent!" },
    { q: "Pulsation", v: "Carotid-cavernous fistula, bony defect" },
    { q: "Systemic", v: "Thyroid Sx, sinus disease, malignancy hx, trauma, smoking" },
  ];
  hxItems.forEach((h, i) => {
    sl.addText(h.q, { x: 0.4, y: 1.32 + i*0.63, w: 3.8, h: 0.2, fontSize: 9.5, bold: true, color: C.teal });
    sl.addText(h.v, { x: 0.4, y: 1.52 + i*0.63, w: 3.8, h: 0.38, fontSize: 8.5, color: C.slate });
  });

  // Examination
  sl.addShape(pres.ShapeType.rect, { x: 4.5, y: 0.9, w: 5.0, h: 5.5, fill: { color: C.white }, line: { color: C.teal, width: 1.5 } });
  sl.addShape(pres.ShapeType.rect, { x: 4.5, y: 0.9, w: 5.0, h: 0.34, fill: { color: C.teal } });
  sl.addText("EXAMINATION", { x: 4.6, y: 0.9, w: 4.8, h: 0.34, fontSize: 11, bold: true, color: C.white, valign: "middle" });

  const examSections = [
    { title: "Hertel Exophthalmometry", items: ["Measure lateral rim → corneal apex", "Normal ≤21 mm | >2 mm asymmetry significant", "Same base width each visit", "Note: variability ±1-2 mm is inherent"] },
    { title: "Globe Displacement", items: ["Axial = intraconal lesion", "Superior displacement = floor lesion", "Inferior displacement = roof/superior lesion", "Medial = lacrimal gland tumor (superolateral → inferomedial)"] },
    { title: "Eyelid Signs (TED)", items: ["Dalrymple sign = lid retraction (widened fissure)", "von Graefe sign = lid lag on downgaze", "Kocher sign = staring appearance", "Stellwag sign = infrequent blinking"] },
    { title: "EOM & Vision", items: ["Ocular motility – all 9 positions of gaze", "Forced duction test for restrictive vs neurogenic", "VA, color vision, VF – optic nerve function", "RAPD = optic neuropathy"] },
  ];

  examSections.forEach((sec, i) => {
    sl.addShape(pres.ShapeType.rect, { x: 4.55, y: 1.32 + i*1.22, w: 4.9, h: 0.2, fill: { color: C.light } });
    sl.addText(sec.title, { x: 4.6, y: 1.32 + i*1.22, w: 4.85, h: 0.2, fontSize: 9.5, bold: true, color: C.navy });
    sec.items.forEach((item, j) => {
      sl.addText([
        { text: "• ", options: { color: C.teal, bold: true } },
        { text: item, options: { color: C.slate } }
      ], { x: 4.6, y: 1.55 + i*1.22 + j*0.22, w: 4.85, h: 0.2, fontSize: 8.5 });
    });
  });

  // Image panel
  sl.addShape(pres.ShapeType.rect, { x: 9.7, y: 0.9, w: 3.3, h: 5.5, fill: { color: C.navy } });
  sl.addText("Lid Signs in TED", { x: 9.75, y: 0.9, w: 3.2, h: 0.35, fontSize: 10, bold: true, color: C.amber, align: "center" });
  if (imgs[0] && !imgs[0].error) {
    sl.addImage({ data: imgs[0].base64, x: 9.8, y: 1.28, w: 3.1, h: 3.5 });
  }
  sl.addText([
    { text: "A: ", options: { bold: true, color: C.amber } },
    { text: "Mild L lid retraction\n", options: { color: C.white } },
    { text: "B: ", options: { bold: true, color: C.amber } },
    { text: "Bilateral – Dalrymple\n", options: { color: C.white } },
    { text: "C: ", options: { bold: true, color: C.amber } },
    { text: "Severe – Kocher sign\n", options: { color: C.white } },
    { text: "D: ", options: { bold: true, color: C.amber } },
    { text: "Lid lag – von Graefe", options: { color: C.white } },
  ], { x: 9.75, y: 4.85, w: 3.2, h: 1.2, fontSize: 8.5, valign: "top" });

  // Bottom strip
  sl.addShape(pres.ShapeType.rect, { x: 0.3, y: 6.5, w: 12.7, h: 0.72, fill: { color: C.amber }, transparency: 15 });
  sl.addText([
    { text: "⚠ URGENT REFERRAL: ", options: { bold: true, color: C.danger, fontSize: 11 } },
    { text: "Visual loss | RAPD | Colour desaturation | Corneal exposure | Pulsatile proptosis | Fever + proptosis (cellulitis/mucormycosis)", options: { color: C.navy, fontSize: 10 } }
  ], { x: 0.5, y: 6.5, w: 12.5, h: 0.72, valign: "middle" });

  footer(sl, 5, 11);
}

// ─────────────────────────────────────────────────────────────────────────────
// SLIDE 6 – Clinical Photos (TED)
// ─────────────────────────────────────────────────────────────────────────────
{
  let sl = pres.addSlide();
  sl.addShape(pres.ShapeType.rect, { x: 0, y: 0, w: 13.3, h: 7.5, fill: { color: C.navy } });
  hdr(sl, "CLINICAL IMAGES – THYROID EYE DISEASE", "Slide 6");

  // Left image: proptosis types
  sl.addShape(pres.ShapeType.rect, { x: 0.3, y: 0.88, w: 5.8, h: 0.35, fill: { color: C.teal } });
  sl.addText("Proptosis Patterns in TED", { x: 0.35, y: 0.88, w: 5.7, h: 0.35, fontSize: 11, bold: true, color: C.white, valign: "middle" });
  if (imgs[1] && !imgs[1].error) {
    sl.addImage({ data: imgs[1].base64, x: 0.3, y: 1.26, w: 5.8, h: 4.5 });
  }
  sl.addText([
    { text: "A: Symmetrical bilateral proptosis\n", options: { color: C.white, fontSize: 10 } },
    { text: "B: Asymmetrical – may mimic unilateral disease\n", options: { color: C.white, fontSize: 10 } },
    { text: "C: Severe exposure → bacterial keratitis (corneal ulcer)", options: { color: C.coral, bold: true, fontSize: 10 } },
  ], { x: 0.35, y: 5.82, w: 5.7, h: 0.8, valign: "top" });

  // Right image: soft tissue TED
  sl.addShape(pres.ShapeType.rect, { x: 6.5, y: 0.88, w: 6.5, h: 0.35, fill: { color: C.purple } });
  sl.addText("Soft Tissue Involvement in TED", { x: 6.55, y: 0.88, w: 6.4, h: 0.35, fontSize: 11, bold: true, color: C.white, valign: "middle" });
  if (imgs[2] && !imgs[2].error) {
    sl.addImage({ data: imgs[2].base64, x: 6.5, y: 1.26, w: 6.5, h: 3.5 });
  }
  sl.addText([
    { text: "A: Epibulbar hyperaemia over horizontal rectus\n", options: { color: C.white, fontSize: 10 } },
    { text: "B: Periorbital oedema, chemosis, prolapsed fat\n", options: { color: C.white, fontSize: 10 } },
    { text: "C: Superior limbic keratoconjunctivitis (SLK)", options: { color: C.amber, fontSize: 10 } },
  ], { x: 6.55, y: 4.82, w: 6.3, h: 0.7, valign: "top" });

  // Bottom facts
  sl.addShape(pres.ShapeType.rect, { x: 6.5, y: 5.6, w: 6.5, h: 1.62, fill: { color: "1A2942" } });
  sl.addText("TED CLINICAL PEARLS", { x: 6.6, y: 5.62, w: 6.3, h: 0.28, fontSize: 11, bold: true, color: C.amber });
  const pearls = [
    "Most common cause of unilateral AND bilateral proptosis in adults",
    "Up to 50% of TED patients have EOM restriction (inferior > medial > superior > lateral)",
    "Optic nerve compression can occur WITHOUT significant proptosis",
    "Graves' orbitopathy can occur in euthyroid (5-10%) and hypothyroid (10%) patients",
    "Smoking is the single most important modifiable risk factor for TED progression",
  ];
  pearls.forEach((p, i) => {
    sl.addText([
      { text: "★ ", options: { color: C.amber, bold: true } },
      { text: p, options: { color: C.white } }
    ], { x: 6.6, y: 5.95 + i*0.24, w: 6.3, h: 0.22, fontSize: 8.5 });
  });

  footer(sl, 6, 11);
}

// ─────────────────────────────────────────────────────────────────────────────
// SLIDE 7 – Investigations
// ─────────────────────────────────────────────────────────────────────────────
{
  let sl = pres.addSlide();
  sl.addShape(pres.ShapeType.rect, { x: 0, y: 0, w: 13.3, h: 7.5, fill: { color: C.light } });
  hdr(sl, "INVESTIGATIONS", "Slide 7");

  const invColumns = [
    {
      title: "BLOODS",
      accent: C.teal,
      items: [
        "TFTs: TSH, Free T4, Free T3",
        "TSH receptor antibodies (TRAb/TSI)",
        "Thyroid peroxidase antibodies",
        "CBC + differential (infection)",
        "ESR / CRP (inflammation)",
        "ACE level (sarcoidosis)",
        "ANCA: c-ANCA (Wegener's)",
        "Blood cultures (orbital cellulitis)",
        "LDH / β2-microglobulin (lymphoma)",
      ]
    },
    {
      title: "IMAGING – CT ORBIT",
      accent: C.coral,
      items: [
        "FIRST-LINE imaging for acute proptosis",
        "Bone detail: fractures, sinus involvement",
        "Chandler staging of orbital cellulitis",
        "Enlarged EOMs in TED (fusiform, sparing tendon)",
        "Apical crowding → optic nerve compression",
        "Sinus tumors, mucocele",
        "Guide for drainage (abscess)",
        "TED: inferior rectus → medial rectus → superior rectus",
      ]
    },
    {
      title: "IMAGING – MRI ORBIT",
      accent: C.purple,
      items: [
        "Better soft tissue contrast than CT",
        "Optic nerve & sheath evaluation",
        "Distinguish tumor types (T1/T2 signal)",
        "Cavernous hemangioma: T2 hyperintense",
        "Optic nerve glioma: fusiform enlargement",
        "Orbital pseudotumor vs lymphoma vs TED",
        "No radiation – preferred in children",
        "With gadolinium for vascular lesions",
      ]
    },
  ];

  invColumns.forEach((col, i) => {
    sl.addShape(pres.ShapeType.rect, { x: 0.3 + i*4.35, y: 0.9, w: 4.15, h: 5.8, fill: { color: C.white }, line: { color: col.accent, width: 2 } });
    sl.addShape(pres.ShapeType.rect, { x: 0.3 + i*4.35, y: 0.9, w: 4.15, h: 0.34, fill: { color: col.accent } });
    sl.addText(col.title, { x: 0.35 + i*4.35, y: 0.9, w: 4.05, h: 0.34, fontSize: 11, bold: true, color: C.white, valign: "middle" });
    col.items.forEach((item, j) => {
      sl.addText([
        { text: "▸ ", options: { color: col.accent, bold: true, fontSize: 9 } },
        { text: item, options: { color: C.slate, fontSize: 9 } }
      ], { x: 0.4 + i*4.35, y: 1.3 + j*0.52, w: 4.0, h: 0.48, valign: "middle" });
    });
  });

  // Special investigations
  sl.addShape(pres.ShapeType.rect, { x: 0.3, y: 6.8, w: 12.7, h: 0.5, fill: { color: C.navy } });
  sl.addText("OTHER: Orbital Doppler US (vascular lesions / CCF) | Biopsy (tissue diagnosis) | PET-CT (metastatic workup) | Thyroid US | Slit-lamp (corneal exposure, IOP) | VEP / OCT (optic nerve monitoring)", {
    x: 0.4, y: 6.8, w: 12.5, h: 0.5, fontSize: 8.5, color: C.white, valign: "middle"
  });

  footer(sl, 7, 11);
}

// ─────────────────────────────────────────────────────────────────────────────
// SLIDE 8 – Management Algorithm
// ─────────────────────────────────────────────────────────────────────────────
{
  let sl = pres.addSlide();
  sl.addShape(pres.ShapeType.rect, { x: 0, y: 0, w: 13.3, h: 7.5, fill: { color: C.light } });
  hdr(sl, "MANAGEMENT OVERVIEW", "Slide 8");

  // Algorithm flow
  const steps = [
    { label: "PROPTOSIS\nDETECTED", color: C.navy, x: 0.4, y: 1.0, w: 2.0, h: 0.8 },
    { label: "EMERGENCY?\nCellulitis/Mucormycosis\nVision loss", color: C.danger, x: 3.0, y: 1.0, w: 2.2, h: 0.8 },
    { label: "THYROID\nEYE DISEASE?", color: C.teal, x: 6.0, y: 1.0, w: 2.2, h: 0.8 },
    { label: "NEOPLASM\nor STRUCTURAL?", color: C.purple, x: 9.2, y: 1.0, w: 2.0, h: 0.8 },
  ];

  steps.forEach(s => {
    sl.addShape(pres.ShapeType.rect, { x: s.x, y: s.y, w: s.w, h: s.h, fill: { color: s.color }, line: { color: C.white, width: 1 } });
    sl.addText(s.label, { x: s.x, y: s.y, w: s.w, h: s.h, fontSize: 9, bold: true, color: C.white, align: "center", valign: "middle" });
  });

  // Arrows
  [2.4, 5.2, 8.2].forEach(x => {
    sl.addShape(pres.ShapeType.rect, { x, y: 1.32, w: 0.5, h: 0.15, fill: { color: C.muted } });
  });

  // Management boxes
  const mgmtCards = [
    {
      title: "EMERGENCY MANAGEMENT",
      accent: C.danger,
      x: 0.3, y: 2.05, w: 3.9,
      items: [
        "Orbital cellulitis: IV antibiotics (ampicillin-sulbactam / co-amoxiclav)",
        "CT scan URGENTLY – stage with Chandler",
        "Chandler III-IV: Surgical drainage (anterior/medial orbitotomy)",
        "Fungal (mucormycosis): Liposomal amphotericin B + urgent surgical debridement",
        "CCF: Endovascular embolization (first line)",
        "Compressive optic neuropathy: IV methylprednisolone 1g/day × 3 days → urgent decompression",
      ]
    },
    {
      title: "THYROID EYE DISEASE",
      accent: C.teal,
      x: 4.5, y: 2.05, w: 4.2,
      items: [
        "MILD active: Selenium 200μg/day × 6mo (EU guidelines) + lubricants",
        "MODERATE-SEVERE active: IV methylprednisolone 0.5g × 6wk → 0.25g × 6wk",
        "+ Mycophenolate sodium 720mg/day (superior to steroid monotherapy)",
        "2nd line: Orbital radiotherapy (6 weeks) or Rituximab/Tocilizumab",
        "Teprotumumab (IGF-1R inhibitor): FDA approved, reduces proptosis",
        "Inactive severe: Orbital decompression → strabismus surgery → lid surgery (in sequence)",
        "AVOID radioiodine in active TED (use methimazole / PTU / thyroidectomy instead)",
        "STOP SMOKING – worsens TED, reduces treatment response",
      ]
    },
    {
      title: "NEOPLASTIC / STRUCTURAL",
      accent: C.purple,
      x: 9.0, y: 2.05, w: 4.0,
      items: [
        "Benign (cavernous hemangioma): Observation if small; excision if symptomatic",
        "Optic nerve glioma (children): Observe → chemo if progressive; avoid RT in young",
        "Meningioma: Stereotactic RT or surgery depending on vision",
        "Dermoid/Epidermoid: Complete excision",
        "Lymphoma: Radiotherapy (20 Gy) ± chemotherapy",
        "Metastatic: Systemic chemo + palliative RT; avoid exenteration",
        "Sinus tumors: En bloc resection ± orbital exenteration",
        "Pseudotumor: High-dose oral prednisolone (diagnostic + therapeutic)",
      ]
    }
  ];

  mgmtCards.forEach(card => {
    sl.addShape(pres.ShapeType.rect, { x: card.x, y: card.y, w: card.w, h: 4.75, fill: { color: C.white }, line: { color: card.accent, width: 1.5 } });
    sl.addShape(pres.ShapeType.rect, { x: card.x, y: card.y, w: card.w, h: 0.3, fill: { color: card.accent } });
    sl.addText(card.title, { x: card.x + 0.05, y: card.y, w: card.w - 0.1, h: 0.3, fontSize: 10, bold: true, color: C.white, valign: "middle" });
    card.items.forEach((item, j) => {
      sl.addText([
        { text: "• ", options: { color: card.accent, bold: true } },
        { text: item, options: { color: C.slate } }
      ], { x: card.x + 0.1, y: card.y + 0.36 + j*0.54, w: card.w - 0.2, h: 0.5, fontSize: 8.5, valign: "top" });
    });
  });

  footer(sl, 8, 11);
}

// ─────────────────────────────────────────────────────────────────────────────
// SLIDE 9 – TED Surgery Sequence & Decompression
// ─────────────────────────────────────────────────────────────────────────────
{
  let sl = pres.addSlide();
  sl.addShape(pres.ShapeType.rect, { x: 0, y: 0, w: 13.3, h: 7.5, fill: { color: C.light } });
  hdr(sl, "TED SURGICAL MANAGEMENT & ORBITAL DECOMPRESSION", "Slide 9");

  // Surgery sequence
  sl.addShape(pres.ShapeType.rect, { x: 0.3, y: 0.9, w: 12.7, h: 0.55, fill: { color: C.navy }, transparency: 5 });
  sl.addText("SURGICAL SEQUENCE (only in INACTIVE / CICATRIZING phase)", {
    x: 0.4, y: 0.9, w: 12.5, h: 0.55, fontSize: 13, bold: true, color: C.amber, valign: "middle", align: "center"
  });

  const seqSteps = [
    { num: "1", title: "ORBITAL\nDECOMPRESSION", sub: "Reduces proptosis\nAddress compressive optic neuropathy", color: C.teal },
    { num: "→", title: "", sub: "", color: "transparent" },
    { num: "2", title: "STRABISMUS\nSURGERY", sub: "Corrects diplopia\nAligns gaze for prism correction", color: C.purple },
    { num: "→", title: "", sub: "", color: "transparent" },
    { num: "3", title: "LID SURGERY\n(Retraction/Lagophthalmos)", sub: "Protects cornea\nCosmetic rehabilitation", color: C.coral },
    { num: "→", title: "", sub: "", color: "transparent" },
    { num: "4", title: "BLEPHAROPLASTY", sub: "Functional &\ncosmetic completion", color: C.amber },
  ];

  seqSteps.forEach((s, i) => {
    if (s.num === "→") {
      sl.addText("→", { x: 0.3 + i*1.85, y: 1.6, w: 0.5, h: 1.2, fontSize: 28, bold: true, color: C.muted, align: "center", valign: "middle" });
    } else {
      sl.addShape(pres.ShapeType.rect, { x: 0.3 + i*1.85, y: 1.6, w: 1.65, h: 1.2, fill: { color: s.color }, line: { color: C.white, width: 1 } });
      sl.addText(`${s.num}`, { x: 0.3 + i*1.85, y: 1.6, w: 0.4, h: 0.3, fontSize: 16, bold: true, color: "FFFFFF88" });
      sl.addText(s.title, { x: 0.3 + i*1.85, y: 1.75, w: 1.65, h: 0.5, fontSize: 9, bold: true, color: C.white, align: "center", valign: "middle" });
      sl.addText(s.sub, { x: 0.3 + i*1.85, y: 2.28, w: 1.65, h: 0.45, fontSize: 7.5, color: C.white, align: "center", valign: "middle" });
    }
  });

  // Decompression details
  sl.addShape(pres.ShapeType.rect, { x: 0.3, y: 2.95, w: 6.0, h: 3.9, fill: { color: C.white }, line: { color: C.teal, width: 1.5 } });
  sl.addShape(pres.ShapeType.rect, { x: 0.3, y: 2.95, w: 6.0, h: 0.32, fill: { color: C.teal } });
  sl.addText("ORBITAL DECOMPRESSION TECHNIQUES", { x: 0.35, y: 2.95, w: 5.9, h: 0.32, fontSize: 11, bold: true, color: C.white, valign: "middle" });

  const decompRows = [
    ["1-Wall", "Medial wall (ethmoid/lamina papyracea)", "~2-3 mm reduction"],
    ["2-Wall", "Medial + inferior floor", "~3-5 mm reduction"],
    ["3-Wall", "+ Lateral wall", "5-8+ mm reduction"],
    ["Fat decompression", "Orbital fat removal (intraconal)", "Adjunct – reduces diplopia risk"],
    ["Endoscopic (preferred)", "Transnasal medial wall + floor via ethmoidectomy", "3.2-5.1 mm avg reduction, lower diplopia risk"],
  ];

  sl.addText(["Wall/Approach", "    Walls Removed", "    Expected Proptosis Reduction"].join(""), {
    x: 0.35, y: 3.32, w: 5.9, h: 0.26, fontSize: 9, bold: true, color: C.navy
  });

  decompRows.forEach(([wall, walls, result], i) => {
    const bgColor = i % 2 === 0 ? "F0F8FA" : C.white;
    sl.addShape(pres.ShapeType.rect, { x: 0.35, y: 3.6 + i*0.48, w: 5.9, h: 0.44, fill: { color: bgColor } });
    sl.addText(wall, { x: 0.4, y: 3.62 + i*0.48, w: 1.3, h: 0.38, fontSize: 9, bold: true, color: C.teal, valign: "middle" });
    sl.addText(walls, { x: 1.75, y: 3.62 + i*0.48, w: 2.7, h: 0.38, fontSize: 8.5, color: C.slate, valign: "middle" });
    sl.addText(result, { x: 4.5, y: 3.62 + i*0.48, w: 1.7, h: 0.38, fontSize: 8.5, color: C.success, bold: true, valign: "middle" });
  });

  // Indications
  sl.addShape(pres.ShapeType.rect, { x: 6.6, y: 2.95, w: 6.4, h: 3.9, fill: { color: C.white }, line: { color: C.coral, width: 1.5 } });
  sl.addShape(pres.ShapeType.rect, { x: 6.6, y: 2.95, w: 6.4, h: 0.32, fill: { color: C.coral } });
  sl.addText("INDICATIONS & COMPLICATIONS", { x: 6.65, y: 2.95, w: 6.3, h: 0.32, fontSize: 11, bold: true, color: C.white, valign: "middle" });

  sl.addText("Indications for Orbital Decompression", { x: 6.65, y: 3.32, w: 6.3, h: 0.24, fontSize: 10, bold: true, color: C.navy });
  const indications = [
    "Dysthyroid optic neuropathy (DON) – sight-threatening",
    "Severe exposure keratopathy / corneal ulceration",
    "Severe disfigurement with significant proptosis",
    "Globe subluxation",
    "Intractable orbital pain",
    "Failed medical therapy (steroids + rituximab)",
  ];
  indications.forEach((ind, i) => {
    sl.addText([
      { text: "✓ ", options: { color: C.success, bold: true } },
      { text: ind, options: { color: C.slate } }
    ], { x: 6.65, y: 3.6 + i*0.3, w: 6.3, h: 0.27, fontSize: 9 });
  });

  sl.addText("Complications", { x: 6.65, y: 5.46, w: 6.3, h: 0.24, fontSize: 10, bold: true, color: C.danger });
  const complications = ["New or worsened diplopia (most common)", "Infraorbital nerve hypoesthesia", "CSF leak (endoscopic approach)", "Sinusitis, orbital cellulitis"];
  complications.forEach((c, i) => {
    sl.addText([
      { text: "⚠ ", options: { color: C.danger, bold: true } },
      { text: c, options: { color: C.slate } }
    ], { x: 6.65, y: 5.74 + i*0.26, w: 6.3, h: 0.24, fontSize: 9 });
  });

  footer(sl, 9, 11);
}

// ─────────────────────────────────────────────────────────────────────────────
// SLIDE 10 – Key Facts, Mnemonics & Differentials
// ─────────────────────────────────────────────────────────────────────────────
{
  let sl = pres.addSlide();
  sl.addShape(pres.ShapeType.rect, { x: 0, y: 0, w: 13.3, h: 7.5, fill: { color: C.light } });
  hdr(sl, "KEY FACTS, MNEMONICS & DIFFERENTIALS", "Slide 10");

  // High yield facts
  sl.addShape(pres.ShapeType.rect, { x: 0.3, y: 0.9, w: 6.3, h: 5.0, fill: { color: C.navy }, line: { color: C.amber, width: 2 } });
  sl.addText("★  HIGH-YIELD EXAMINATION FACTS", { x: 0.4, y: 0.93, w: 6.1, h: 0.32, fontSize: 12, bold: true, color: C.amber });

  const facts = [
    "#1 cause bilateral proptosis in adults = Graves' disease",
    "#1 cause unilateral proptosis in adults = Graves' disease",
    "#1 cause proptosis in children = Orbital cellulitis",
    "#1 benign orbital tumor in adults = Cavernous hemangioma (T2 bright on MRI)",
    "#1 primary malignant orbital tumor in children = Rhabdomyosarcoma",
    "#1 metastatic cause in adults = Breast cancer",
    "Optic nerve glioma → NF Type 1 (pilocytic astrocytoma)",
    "Optic nerve sheath meningioma → 'Tram-track' calcification on CT",
    "Pulsatile proptosis + bruit = Carotid-Cavernous Fistula",
    "Intermittent proptosis (Valsalva) = Orbital varix / lymphangioma",
    "Enophthalmos = blow-out fracture, scirrhous breast met, post-RT",
    "Dermoid cyst = superolateral orbit → dermoid of frontozygomatic suture",
    "Periorbital ecchymosis (raccoon eyes) + proptosis in child = Neuroblastoma mets",
    "Wegener's (GPA) = saddle-nose, c-ANCA positive, nasal septum perforation + proptosis",
    "TED EOM involvement: Inferior > Medial > Superior > Lateral (I'M SLow)",
  ];

  facts.forEach((f, i) => {
    const isFirsts = f.startsWith("#");
    sl.addText([
      { text: isFirsts ? "★ " : "• ", options: { color: C.amber, bold: true, fontSize: isFirsts ? 11 : 9.5 } },
      { text: f, options: { color: C.white, fontSize: 9.5, bold: isFirsts } }
    ], { x: 0.4, y: 1.32 + i*0.35, w: 6.1, h: 0.33, valign: "middle" });
  });

  // Mnemonics
  sl.addShape(pres.ShapeType.rect, { x: 6.8, y: 0.9, w: 6.2, h: 2.3, fill: { color: C.white }, line: { color: C.teal, width: 1.5 } });
  sl.addShape(pres.ShapeType.rect, { x: 6.8, y: 0.9, w: 6.2, h: 0.3, fill: { color: C.teal } });
  sl.addText("MNEMONICS", { x: 6.85, y: 0.9, w: 6.1, h: 0.3, fontSize: 11, bold: true, color: C.white, valign: "middle" });

  const mnemonics = [
    { mnem: "IMSLOW", full: "EOM involvement order in TED: Inferior → Medial → Superior → Lateral" },
    { mnem: "VISA", full: "TED grading: Vision, Inflammation, Strabismus, Appearance (North America)" },
    { mnem: "EUGOGO", full: "European TED grading: Mild / Moderate-Severe / Sight-threatening" },
    { mnem: "NO SPECS", full: "Werner's old TED classification (historical only – no longer used clinically)" },
    { mnem: "Chandler I-V", full: "Orbital cellulitis grading: Preseptal → Cellulitis → Subperiosteal → Orbital abscess → CST" },
  ];

  mnemonics.forEach((m, i) => {
    sl.addText(m.mnem, { x: 6.88, y: 1.28 + i*0.38, w: 1.3, h: 0.3, fontSize: 10, bold: true, color: C.teal });
    sl.addText(m.full, { x: 8.2, y: 1.28 + i*0.38, w: 4.7, h: 0.3, fontSize: 8.5, color: C.slate });
  });

  // Differential diagnosis by laterality
  sl.addShape(pres.ShapeType.rect, { x: 6.8, y: 3.35, w: 6.2, h: 2.55, fill: { color: C.white }, line: { color: C.purple, width: 1.5 } });
  sl.addShape(pres.ShapeType.rect, { x: 6.8, y: 3.35, w: 6.2, h: 0.3, fill: { color: C.purple } });
  sl.addText("DIFFERENTIAL: UNILATERAL vs BILATERAL", { x: 6.85, y: 3.35, w: 6.1, h: 0.3, fontSize: 11, bold: true, color: C.white, valign: "middle" });

  sl.addText("UNILATERAL", { x: 6.85, y: 3.72, w: 2.8, h: 0.24, fontSize: 10, bold: true, color: C.purple });
  sl.addText("BILATERAL", { x: 9.85, y: 3.72, w: 2.8, h: 0.24, fontSize: 10, bold: true, color: C.teal });

  const unilateral = ["Orbital cellulitis/abscess", "Orbital pseudotumor", "Primary orbital tumor", "Metastatic tumor", "CCF (traumatic > spontaneous)", "Sinus disease / mucocele", "Lymphangioma, dermoid cyst"];
  const bilateral = ["Graves' orbitopathy (#1)", "Lymphoma", "Metastases (bilateral)", "Bilateral pseudotumor (rare)", "Cavernous sinus thrombosis", "Sarcoidosis", "Amyloidosis"];

  unilateral.forEach((u, i) => {
    sl.addText([{ text: "• ", options: { color: C.purple, bold: true } }, { text: u, options: { color: C.slate } }],
      { x: 6.85, y: 4.0 + i*0.28, w: 2.9, h: 0.25, fontSize: 8.5 });
  });
  bilateral.forEach((b, i) => {
    sl.addText([{ text: "• ", options: { color: C.teal, bold: true } }, { text: b, options: { color: C.slate } }],
      { x: 9.85, y: 4.0 + i*0.28, w: 2.9, h: 0.25, fontSize: 8.5 });
  });

  // Quick ref bottom
  sl.addShape(pres.ShapeType.rect, { x: 0.3, y: 6.0, w: 12.7, h: 1.2, fill: { color: C.slate } });
  sl.addText("WHEN TO REFER URGENTLY", { x: 0.4, y: 6.02, w: 3.5, h: 0.3, fontSize: 11, bold: true, color: C.amber });
  const urgentReferral = ["VA ↓ / RAPD / Colour desaturation", "Fever + proptosis + pain", "Pulsatile proptosis + bruit", "Corneal exposure / ulceration", "Rapid onset + bony erosion on CT", "Globe subluxation"];
  urgentReferral.forEach((r, i) => {
    const col = i % 3;
    const row = Math.floor(i / 3);
    sl.addShape(pres.ShapeType.rect, { x: 0.4 + col*4.2, y: 6.38 + row*0.35, w: 4.0, h: 0.3, fill: { color: C.danger }, transparency: 30 });
    sl.addText([{ text: "⚠ ", options: { color: C.danger, bold: true } }, { text: r, options: { color: C.white } }],
      { x: 0.45 + col*4.2, y: 6.38 + row*0.35, w: 3.9, h: 0.3, fontSize: 9, valign: "middle" });
  });

  footer(sl, 10, 11);
}

// ─────────────────────────────────────────────────────────────────────────────
// SLIDE 11 – Summary / Quick Reference Chart
// ─────────────────────────────────────────────────────────────────────────────
{
  let sl = pres.addSlide();
  sl.addShape(pres.ShapeType.rect, { x: 0, y: 0, w: 13.3, h: 7.5, fill: { color: C.navy } });
  hdr(sl, "QUICK REFERENCE SUMMARY CHART", "Slide 11");

  // Big table
  const tableHeaders = ["Etiology", "Onset", "Laterality", "Key Features", "Investigation", "Treatment"];
  const tableRows = [
    ["Graves' TED", "Subacute (weeks)", "Bilateral > Unilateral", "Lid retraction, lid lag, EOM restriction", "TFT, TRAb, CT/MRI orbit", "Selenium (mild); IV steroids; Decompression (inactive)"],
    ["Orbital Cellulitis", "Acute (hours-days)", "Unilateral", "Pain, fever, ophthalmoplegia, WCC↑", "CT orbit (Chandler), blood cultures", "IV amoxicillin-clavulanate; drain abscess (III-IV)"],
    ["Orbital Pseudotumor", "Acute, painful", "Usually Unilateral", "Severe pain with EOM, responds to steroids", "MRI orbit, exclude infection", "High-dose oral prednisolone (diagnostic/therapeutic)"],
    ["Cavernous Hemangioma", "Chronic (months)", "Unilateral", "Painless, axial, middle-aged women", "MRI: T2 bright, well-circumscribed", "Observation; excision if symptomatic"],
    ["CCF (Carotid-Cav)", "Acute/Subacute", "Unilateral (may bilateral)", "Pulsatile proptosis, bruit, chemosis, dilated vessels", "Angiography (gold standard)", "Endovascular embolization"],
    ["Rhabdomyosarcoma", "Rapid (children)", "Unilateral", "Child 5-15y, most common pediatric malignancy", "MRI + biopsy", "Chemo + RT (exenteration rare now)"],
    ["Wegener's (GPA)", "Subacute", "Bilateral possible", "Saddle nose, sinus disease, glomerulonephritis", "c-ANCA, biopsy", "Cyclophosphamide + prednisolone; Rituximab"],
    ["Metastasis", "Chronic", "Unilateral or bilateral", "Breast > prostate > melanoma > lung history", "CT/MRI, PET-CT, biopsy", "Systemic chemo + palliative RT"],
    ["Mucormycosis", "Acute emergency", "Unilateral initially", "Immunocompromised (DKA), black eschar, necrosis", "MRI, KOH prep, biopsy", "Amphotericin B + urgent surgical debridement"],
  ];

  // Draw header manually
  sl.addShape(pres.ShapeType.rect, { x: 0.2, y: 0.88, w: 12.9, h: 0.35, fill: { color: C.teal } });
  const colWidths = [1.8, 1.2, 1.5, 2.8, 2.1, 3.3];
  const colX = [0.25, 2.05, 3.25, 4.75, 7.55, 9.65];
  tableHeaders.forEach((h, i) => {
    sl.addText(h, { x: colX[i], y: 0.88, w: colWidths[i], h: 0.35, fontSize: 9.5, bold: true, color: C.white, valign: "middle" });
  });

  tableRows.forEach((row, ri) => {
    const bg = ri % 2 === 0 ? "EBF4F8" : C.white;
    sl.addShape(pres.ShapeType.rect, { x: 0.2, y: 1.26 + ri*0.66, w: 12.9, h: 0.63, fill: { color: bg } });
    row.forEach((cell, ci) => {
      const isBold = ci === 0;
      const color = ci === 0 ? C.navy : (ci === 4 ? C.purple : (ci === 5 ? C.teal : C.slate));
      sl.addText(cell, { x: colX[ci], y: 1.27 + ri*0.66, w: colWidths[ci], h: 0.6, fontSize: 7.5, bold: isBold, color, valign: "middle", wrap: true });
    });
  });

  // Dividing lines
  colX.slice(1).forEach(x => {
    sl.addShape(pres.ShapeType.rect, { x: x - 0.02, y: 0.88, w: 0.02, h: 0.35 + tableRows.length * 0.66, fill: { color: "CCDDEA" } });
  });

  footer(sl, 11, 11);
}

// ─────────────────────────────────────────────────────────────────────────────
// SAVE
// ─────────────────────────────────────────────────────────────────────────────
pres.writeFile({ fileName: "/home/daytona/workspace/proptosis-pptx/Proptosis_Residency_Reference.pptx" })
  .then(() => console.log("✅ Presentation saved successfully!"))
  .catch(err => console.error("❌ Error:", err));
Running Command

cd /home/daytona/workspace/proptosis-pptx && node build.js 2>&1

Editing File

~/proptosis-pptx/build.js

Successfully replaced 1 instance(s) of the string in '/home/daytona/workspace/proptosis-pptx/build.js'
Editing File

~/proptosis-pptx/build.js

Error: String not found in file: '{ text: `${s.num}`, x: 0.3 + i*1.85, y: 1.6, w: 0.4, h: 0.3, fontSize: 16, bold: true, color: "FFFFFF88" }'
Running Command

grep -n "FFFFFF88" /home/daytona/workspace/proptosis-pptx/build.js

Running Command

sed -i 's/color: "FFFFFF88"/color: "FFFFFF"/g' /home/daytona/workspace/proptosis-pptx/build.js && cd /home/daytona/workspace/proptosis-pptx && node build.js 2>&1

Reading File
Editing File

~/proptosis-pptx/build.js

Successfully replaced 1 instance(s) of the string in '/home/daytona/workspace/proptosis-pptx/build.js'
Running Command

cd /home/daytona/workspace/proptosis-pptx && node build.js 2>&1

Your residency-level Proptosis presentation is ready with all 4 clinical photos embedded:

Proptosis Residency Reference

Presentation · PPTX


Here's what's inside the 11 slides (LAYOUT_WIDE 13.3" × 7.5", dark navy/teal/amber theme):
SlideContent
1Title slide with topic index
2Definition, normal Hertel values by race (Asian 18mm / White 20mm / Black 22mm), orbital anatomy, key distinctions (axial vs non-axial, pseudo-proptosis, enophthalmos)
3Etiology in 6 color-coded categories - Thyroid/Endocrine, Inflammatory/Autoimmune, Primary Neoplastic, Secondary/Metastatic, Infectious/Vascular, Structural
4Chandler Classification I-V (orbital cellulitis) + CAS scoring (7-item initial, 10-item follow-up) + EUGOGO severity (Mild/Moderate-Severe/Sight-threatening)
5Systematic clinical examination - history framework, Hertel technique, globe displacement patterns, TED lid signs (Dalrymple/von Graefe/Kocher/Stellwag), with clinical photo
6Clinical photos - symmetrical vs asymmetrical proptosis, bacterial keratitis from exposure, soft tissue involvement (chemosis, SLK), TED pearls
7Investigations - bloods (TFTs, TRAb, ANCA, ACE), CT orbit indications, MRI orbit indications, special investigations
8Management algorithm - Emergency (cellulitis → IV abx, Chandler staging, mucormycosis → amphotericin), TED (mild → selenium; moderate-severe → IV methylprednisolone + mycophenolate; teprotumumab), Neoplastic
9Surgical sequence (Decompression → Strabismus → Lid surgery → Blepharoplasty, INACTIVE phase only), decompression techniques (1-wall through 3-wall, expected mm reduction), indications and complications
10High-yield exam facts (15 #1 facts), mnemonics (IMSLOW, VISA, EUGOGO), unilateral vs bilateral differentials, urgent referral criteria
11Master summary table - 9 diagnoses × 6 columns (onset, laterality, key features, investigations, treatment)
Sources: Kanski's Clinical Ophthalmology 10th Ed, KJ Lee's Essential Otolaryngology, Goldman-Cecil Medicine, Cummings Otolaryngology, Harrison's 22E, supplemented by Merck Manual proptosis guidelines and current EUGOGO/teprotumumab evidence.

More detailed version in this pdf of examination and causes of Proptosis about type

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

proptosis types axial pulsatile intermittent non-axial examination orbital causes detailed ophthalmology residency

Reading File
Reading Web Page

https://entokey.com/diagnostic-approach-to-the-patient-with-…

Finding Sources
Finding Sources
Loading Image
Loading Image
Loading Image
Running Command

mkdir -p /home/daytona/workspace/proptosis-pdf && python3 -c "import reportlab; print('reportlab OK'); import urllib.request; print('urllib OK')"

Writing File

~/proptosis-pdf/build_pdf.py

#!/usr/bin/env python3
"""
Proptosis: Types & Examination – Detailed Residency-Level PDF
"""

import urllib.request, os, io
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import cm, mm
from reportlab.lib.styles import ParagraphStyle, getSampleStyleSheet
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_RIGHT, TA_JUSTIFY
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
    HRFlowable, KeepTogether, PageBreak, Image as RLImage
)
from reportlab.graphics.shapes import Drawing, Rect, String, Line, Polygon, Circle
from reportlab.graphics.charts.barcharts import VerticalBarChart
from reportlab.graphics import renderPDF
from reportlab.pdfgen import canvas
from reportlab.platypus.flowables import Flowable

# ── Palette ──────────────────────────────────────────────────────────────────
NAVY   = colors.HexColor("#0A1628")
TEAL   = colors.HexColor("#0E7C7B")
AMBER  = colors.HexColor("#E8A317")
CORAL  = colors.HexColor("#E05C5C")
SLATE  = colors.HexColor("#2D3A4A")
LIGHT  = colors.HexColor("#EBF4F8")
WHITE  = colors.white
MUTED  = colors.HexColor("#8FA3B1")
GREEN  = colors.HexColor("#27AE60")
RED    = colors.HexColor("#C0392B")
PURPLE = colors.HexColor("#6C3483")
ORANGE = colors.HexColor("#E67E22")
GREY   = colors.HexColor("#ECF0F1")

W, H = A4

# ── Download images ───────────────────────────────────────────────────────────
IMG_DIR = "/home/daytona/workspace/proptosis-pdf/imgs"
os.makedirs(IMG_DIR, exist_ok=True)

def dl(url, fname):
    path = f"{IMG_DIR}/{fname}"
    if not os.path.exists(path):
        try:
            urllib.request.urlretrieve(url, path)
            print(f"Downloaded {fname}")
        except Exception as e:
            print(f"Failed {fname}: {e}")
            return None
    return path

img_proptosis = dl("https://cdn.orris.care/cdss_images/ecb379b38d8a7e399f46854c2a04031b26948eb6ca268008cb787309e27a4031.png", "proptosis_ted.png")
img_lidsigns  = dl("https://cdn.orris.care/cdss_images/3efe8cae9b67dd794499f1dacce0aa87c80a5cc7a7df2794139cfefe05fd6367.png", "lid_signs.png")
img_myopathy  = dl("https://cdn.orris.care/cdss_images/7a1e9a2052317aa918d444bd2e2230470e6853e8bc7dd8a4b9bf96b60adb76da.png", "myopathy.png")
img_softtissue= dl("https://cdn.orris.care/cdss_images/8abf13c5a940a0a114de480d9cea3886fb6ad81b96e5c11aa4181aa4da0d80e7.png", "soft_tissue.png")

# ── Styles ───────────────────────────────────────────────────────────────────
styles = getSampleStyleSheet()

def S(name, **kwargs):
    return ParagraphStyle(name, **kwargs)

# Base styles
sTitleMain = S("TitleMain", fontSize=28, textColor=WHITE, fontName="Helvetica-Bold",
               alignment=TA_CENTER, spaceAfter=4)
sSubtitle  = S("Subtitle", fontSize=14, textColor=AMBER, fontName="Helvetica",
               alignment=TA_CENTER, spaceAfter=8)
sH1 = S("H1", fontSize=16, textColor=WHITE, fontName="Helvetica-Bold",
        alignment=TA_LEFT, spaceBefore=8, spaceAfter=4, leftIndent=0)
sH2 = S("H2", fontSize=13, textColor=NAVY, fontName="Helvetica-Bold",
        alignment=TA_LEFT, spaceBefore=6, spaceAfter=3, leftIndent=0)
sH3 = S("H3", fontSize=11, textColor=TEAL, fontName="Helvetica-Bold",
        alignment=TA_LEFT, spaceBefore=4, spaceAfter=2)
sBody = S("Body", fontSize=9.5, textColor=SLATE, fontName="Helvetica",
          alignment=TA_JUSTIFY, spaceAfter=3, leading=13)
sBullet = S("Bullet", fontSize=9.5, textColor=SLATE, fontName="Helvetica",
            alignment=TA_LEFT, spaceAfter=2, leftIndent=12, leading=13,
            bulletIndent=4)
sSmall = S("Small", fontSize=8, textColor=MUTED, fontName="Helvetica",
           alignment=TA_LEFT, spaceAfter=2)
sCaption = S("Caption", fontSize=8, textColor=SLATE, fontName="Helvetica-Oblique",
             alignment=TA_CENTER, spaceAfter=4)
sBold = S("Bold", fontSize=9.5, textColor=NAVY, fontName="Helvetica-Bold",
          alignment=TA_LEFT, spaceAfter=2)
sWarning = S("Warning", fontSize=10, textColor=RED, fontName="Helvetica-Bold",
             alignment=TA_LEFT, spaceAfter=3)
sNote = S("Note", fontSize=9, textColor=PURPLE, fontName="Helvetica-Oblique",
          alignment=TA_LEFT, spaceAfter=3)
sTableHdr = S("TableHdr", fontSize=9, textColor=WHITE, fontName="Helvetica-Bold",
              alignment=TA_CENTER)
sTableCell = S("TableCell", fontSize=8.5, textColor=SLATE, fontName="Helvetica",
               alignment=TA_LEFT, leading=11)

# ── Custom Flowables ──────────────────────────────────────────────────────────
class ColorBox(Flowable):
    """A colored header bar."""
    def __init__(self, text, bg=NAVY, fg=WHITE, h=0.7*cm, fs=12, bold=True, radius=3):
        super().__init__()
        self.text, self.bg, self.fg = text, bg, fg
        self.bh, self.fs, self.bold, self.radius = h, fs, bold, radius
        self.width = W - 3*cm

    def wrap(self, aw, ah):
        self.width = aw
        return aw, self.bh + 2*mm

    def draw(self):
        c = self.canv
        c.setFillColor(self.bg)
        c.roundRect(0, 0, self.width, self.bh, self.radius, fill=1, stroke=0)
        c.setFillColor(self.fg)
        c.setFont("Helvetica-Bold" if self.bold else "Helvetica", self.fs)
        c.drawString(6*mm, 3*mm, self.text)

class AccentLine(Flowable):
    def __init__(self, color=TEAL, thick=3):
        super().__init__()
        self.color, self.thick = color, thick

    def wrap(self, aw, ah):
        self.width = aw
        return aw, self.thick + 2

    def draw(self):
        self.canv.setFillColor(self.color)
        self.canv.rect(0, 0, self.width, self.thick, fill=1, stroke=0)

class TypeCard(Flowable):
    """A colored card for each type of proptosis."""
    def __init__(self, number, title, subtitle, features, causes, bg_color, card_w=None):
        super().__init__()
        self.number = number
        self.title = title
        self.subtitle = subtitle
        self.features = features
        self.causes = causes
        self.bg = bg_color
        self.card_w = card_w

    def wrap(self, aw, ah):
        self.width = self.card_w or aw
        return self.width, 4.5*cm

    def draw(self):
        c = self.canv
        w = self.width
        h = 4.3*cm
        # Card background
        c.setFillColor(self.bg)
        c.roundRect(0, 0, w, h, 5, fill=1, stroke=0)
        # Number circle
        c.setFillColor(WHITE)
        c.circle(0.7*cm, h - 0.7*cm, 0.5*cm, fill=1, stroke=0)
        c.setFillColor(self.bg)
        c.setFont("Helvetica-Bold", 14)
        c.drawCentredString(0.7*cm, h - 0.85*cm, str(self.number))
        # Title
        c.setFillColor(WHITE)
        c.setFont("Helvetica-Bold", 11)
        c.drawString(1.5*cm, h - 0.8*cm, self.title)
        # Subtitle
        c.setFillColor(colors.HexColor("#FFFFFFAA") if False else WHITE)
        c.setFont("Helvetica-Oblique", 8.5)
        c.drawString(1.5*cm, h - 1.2*cm, self.subtitle)
        # Divider
        c.setFillColor(WHITE)
        c.setFillColorRGB(1, 1, 1, 0.3)
        c.rect(0.3*cm, h - 1.5*cm, w - 0.6*cm, 0.5, fill=1, stroke=0)
        # Features
        c.setFillColor(WHITE)
        c.setFont("Helvetica-Bold", 8)
        c.drawString(0.4*cm, h - 1.9*cm, "KEY FEATURES:")
        c.setFont("Helvetica", 8)
        y = h - 2.3*cm
        for f in self.features[:3]:
            c.drawString(0.6*cm, y, f"\u25b8 {f}")
            y -= 0.38*cm
        # Causes
        c.setFont("Helvetica-Bold", 8)
        c.drawString(0.4*cm, y - 0.1*cm, "CAUSES:")
        c.setFont("Helvetica", 8)
        y -= 0.5*cm
        for ca in self.causes[:3]:
            c.drawString(0.6*cm, y, f"\u2022 {ca}")
            y -= 0.38*cm

# ── Page template with header/footer ─────────────────────────────────────────
class MyDocTemplate(SimpleDocTemplate):
    def __init__(self, *args, **kwargs):
        self.page_num = 0
        super().__init__(*args, **kwargs)

    def handle_pageBegin(self):
        self.page_num += 1
        super().handle_pageBegin()

    def afterPage(self):
        c = self._doc.canv
        # Footer bar
        c.setFillColor(NAVY)
        c.rect(0, 0, W, 0.7*cm, fill=1, stroke=0)
        c.setFillColor(MUTED)
        c.setFont("Helvetica", 7.5)
        c.drawString(1.5*cm, 2*mm, "PROPTOSIS: TYPES & EXAMINATION  |  RESIDENCY REFERENCE")
        c.drawRightString(W - 1.5*cm, 2*mm, f"Page {self._doc.page}")
        c.setFillColor(TEAL)
        c.rect(0, 0.7*cm, W, 1.5, fill=1, stroke=0)

# ── Build story ───────────────────────────────────────────────────────────────
story = []
M = 1.5*cm  # margin

def add_section_header(title, subtitle=None, color=NAVY, accent=TEAL):
    story.append(Spacer(1, 0.3*cm))
    story.append(ColorBox(title, bg=color, h=0.85*cm, fs=14))
    story.append(AccentLine(color=accent))
    if subtitle:
        story.append(Paragraph(subtitle, sNote))
    story.append(Spacer(1, 0.2*cm))

def bullet(text, color=TEAL, indent=12):
    return Paragraph(f'<font color="#{color.hexval()[2:]}">&#9658;</font> {text}', sBullet)

def sub_bullet(text):
    return Paragraph(f'&nbsp;&nbsp;&nbsp;&nbsp;&#8226; {text}',
                     S("sub", fontSize=9, textColor=SLATE, fontName="Helvetica",
                       leftIndent=20, spaceAfter=1, leading=12))

def highlight_box(text, bg=LIGHT, border=TEAL):
    data = [[Paragraph(text, sBody)]]
    t = Table(data, colWidths=[W - 3.2*cm])
    t.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,-1), bg),
        ("LEFTPADDING", (0,0), (-1,-1), 8),
        ("RIGHTPADDING", (0,0), (-1,-1), 8),
        ("TOPPADDING", (0,0), (-1,-1), 6),
        ("BOTTOMPADDING", (0,0), (-1,-1), 6),
        ("LINECOLOR", (0,0), (-1,-1), border),
        ("LINEBEFORE", (0,0), (0,-1), 4, border),
        ("ROUNDEDCORNERS", [3]),
    ]))
    return t

def make_table(headers, rows, col_widths=None, stripe=True):
    if col_widths is None:
        n = len(headers)
        col_widths = [(W - 3.2*cm) / n] * n
    hrow = [Paragraph(h, sTableHdr) for h in headers]
    data = [hrow]
    for row in rows:
        data.append([Paragraph(str(c), sTableCell) for c in row])
    t = Table(data, colWidths=col_widths)
    style = [
        ("BACKGROUND", (0,0), (-1,0), NAVY),
        ("ROWBACKGROUNDS", (0,1), (-1,-1), [GREY, WHITE] if stripe else [WHITE]),
        ("FONTNAME", (0,0), (-1,-1), "Helvetica"),
        ("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
        ("FONTSIZE", (0,0), (-1,-1), 8.5),
        ("GRID", (0,0), (-1,-1), 0.5, colors.HexColor("#CCDDEA")),
        ("LEFTPADDING", (0,0), (-1,-1), 5),
        ("RIGHTPADDING", (0,0), (-1,-1), 5),
        ("TOPPADDING", (0,0), (-1,-1), 4),
        ("BOTTOMPADDING", (0,0), (-1,-1), 4),
        ("VALIGN", (0,0), (-1,-1), "MIDDLE"),
    ]
    t.setStyle(TableStyle(style))
    return t

# ═════════════════════════════════════════════════════════════════════════════
# COVER PAGE
# ═════════════════════════════════════════════════════════════════════════════
class CoverPage(Flowable):
    def wrap(self, aw, ah): return aw, 10*cm
    def draw(self):
        c = self.canv
        # Dark gradient bg
        c.setFillColor(NAVY); c.rect(0, 0, W-3*cm, 10*cm, fill=1, stroke=0)
        # Accent stripe
        c.setFillColor(TEAL); c.rect(0, 9.7*cm, W-3*cm, 0.3*cm, fill=1, stroke=0)
        c.setFillColor(AMBER); c.rect(0, 9.4*cm, W-3*cm, 0.15*cm, fill=1, stroke=0)
        # Title
        c.setFillColor(WHITE)
        c.setFont("Helvetica-Bold", 36)
        c.drawCentredString((W-3*cm)/2, 7.8*cm, "PROPTOSIS")
        c.setFillColor(TEAL)
        c.setFont("Helvetica", 20)
        c.drawCentredString((W-3*cm)/2, 7.1*cm, "Types & Examination")
        # Subtitle
        c.setFillColor(AMBER)
        c.setFont("Helvetica-Bold", 13)
        c.drawCentredString((W-3*cm)/2, 6.4*cm, "Detailed Residency Reference  |  Ophthalmology")
        # Topic chips
        topics = ["Axial / Non-Axial", "Pulsatile", "Intermittent",
                  "Unilateral / Bilateral", "Acute / Chronic",
                  "Hertel Technique", "Full Examination Protocol",
                  "Cause-by-Type Mapping"]
        chip_w = 3.8*cm; chip_h = 0.5*cm
        cols = 4; gap_x = (W - 3*cm - cols*chip_w) / (cols+1)
        for i, t in enumerate(topics):
            col = i % cols; row = i // cols
            x = gap_x + col*(chip_w + gap_x)
            y = 4.8*cm - row*0.75*cm
            c.setFillColor(TEAL); c.roundRect(x, y, chip_w, chip_h, 3, fill=1, stroke=0)
            c.setFillColor(WHITE); c.setFont("Helvetica-Bold", 8)
            c.drawCentredString(x + chip_w/2, y + 1.5*mm, t)
        # Bottom bar
        c.setFillColor(AMBER)
        c.setFont("Helvetica", 9)
        c.drawCentredString((W-3*cm)/2, 0.4*cm, "Sources: Kanski's Clinical Ophthalmology 10e | Cummings Otolaryngology | Harrison's 22E | Etiologies of Proptosis PMC Review")

story.append(CoverPage())
story.append(Spacer(1, 0.5*cm))

# ═════════════════════════════════════════════════════════════════════════════
# SECTION 1 – DEFINITION & NORMAL VALUES
# ═════════════════════════════════════════════════════════════════════════════
add_section_header("1. DEFINITION & NORMAL VALUES", color=NAVY)

story.append(highlight_box(
    '<b>Proptosis / Exophthalmos:</b> Anterior displacement of the globe beyond the orbital rim. '
    'The terms are often used interchangeably; by convention <i>exophthalmos</i> refers specifically '
    'to proptosis caused by thyroid eye disease.',
    bg=LIGHT, border=TEAL
))
story.append(Spacer(1, 0.3*cm))

# Normal values table
story.append(Paragraph("Normal Hertel Exophthalmometry Values", sH3))
nv_data = [
    ["Race / Ethnicity", "Normal Upper Limit", "Significant Asymmetry", "Notes"],
    ["Asian", "≤18 mm", ">2 mm between eyes", "Lower threshold – do not use Caucasian normals"],
    ["Caucasian / White", "≤20 mm", ">2 mm between eyes", "Most quoted standard in literature"],
    ["Black / African", "≤22 mm", ">2 mm between eyes", "Higher physiological prominence"],
    ["Children", "≤16 mm", ">2 mm between eyes", "Orbital volume smaller; interpret with caution"],
]
story.append(make_table(
    nv_data[0], nv_data[1:],
    col_widths=[4.5*cm, 3.2*cm, 4.0*cm, 5.5*cm]
))
story.append(Spacer(1, 0.3*cm))

story.append(Paragraph('<b>Key principle:</b> Asymmetry (&gt;2 mm difference between the two eyes) is often MORE significant than the absolute value. A patient with 19 mm bilaterally may be normal; a patient with 18 mm right vs 15 mm left has significant asymmetry.', sBody))
story.append(Spacer(1, 0.2*cm))

# Measurement tip box
data = [[
    Paragraph('<b>HERTEL EXOPHTHALMOMETER TECHNIQUE</b>', sBold),
    Paragraph(
        '1. Place notched footplates against <b>lateral orbital rims</b> bilaterally<br/>'
        '2. Note the <b>base width</b> (distance between footplates) – record this EVERY visit<br/>'
        '3. Look through mirror at <b>corneal apex</b> reflected against mm scale<br/>'
        '4. Measure <b>anterior corneal surface to lateral rim</b> in mm<br/>'
        '5. Repeat for the other eye<br/>'
        '<font color="#C0392B"><b>Pitfalls:</b></font> Different base settings give different values; interobserver variation ±1–2 mm; '
        'fat atrophy/fracture alters lateral rim landmark',
        sBody
    )
]]
t = Table(data, colWidths=[4.5*cm, 12.5*cm])
t.setStyle(TableStyle([
    ("BACKGROUND", (0,0), (0,-1), TEAL),
    ("BACKGROUND", (1,0), (1,-1), LIGHT),
    ("TEXTCOLOR", (0,0), (0,-1), WHITE),
    ("VALIGN", (0,0), (-1,-1), "TOP"),
    ("LEFTPADDING", (0,0), (-1,-1), 6),
    ("TOPPADDING", (0,0), (-1,-1), 6),
    ("BOTTOMPADDING", (0,0), (-1,-1), 6),
    ("GRID", (0,0), (-1,-1), 0.5, MUTED),
]))
story.append(t)
story.append(Spacer(1, 0.4*cm))

# ═════════════════════════════════════════════════════════════════════════════
# SECTION 2 – TYPES OF PROPTOSIS
# ═════════════════════════════════════════════════════════════════════════════
add_section_header("2. TYPES OF PROPTOSIS", "Classification based on direction, onset, laterality and dynamics", color=SLATE)

story.append(Paragraph("2A. CLASSIFICATION BY DIRECTION OF GLOBE DISPLACEMENT", sH2))
story.append(AccentLine(color=TEAL, thick=2))
story.append(Spacer(1, 0.2*cm))

# Axial / Non-axial table
dir_data = [
    ["Type", "Direction", "What it means", "Typical Causes", "Key Exam Finding"],
    ["AXIAL Proptosis",
     "Straight forward\n(anterior only)",
     "Intraconal lesion: mass is INSIDE the muscle cone, pushing globe directly forward",
     "• Thyroid Eye Disease (EOM enlargement)\n• Cavernous hemangioma\n• Optic nerve glioma\n• Optic nerve sheath meningioma\n• Arteriovenous malformation",
     "Globe protrudes straight forward; no vertical or horizontal displacement on primary gaze"],
    ["NON-AXIAL Proptosis\n(Dystopia)",
     "Downward\n(Hypoglobus)",
     "Extraconal lesion ABOVE or SUPEROLATERAL – mass pushes globe inferiorly",
     "• Lacrimal gland tumor (superolateral → inferomedial displacement)\n• Frontal sinus mucocele\n• Encephalocele\n• Dermoid cyst (frontozygomatic)\n• Superior orbital roof defect",
     "Globe displaced DOWN and IN from superolateral lesion; palpable mass near lacrimal fossa"],
    ["NON-AXIAL Proptosis",
     "Upward\n(Hyperglobus)",
     "Extraconal lesion BELOW – mass pushes globe superiorly",
     "• Maxillary sinus tumor / antral carcinoma\n• Blow-out fracture with orbital floor entrapment\n• Infraorbital mass",
     "Globe elevated; restricted downgaze; may have infraorbital hypoesthesia (V2)"],
    ["NON-AXIAL Proptosis",
     "Lateral\n(Outward)",
     "Medial extraconal lesion pushes globe laterally",
     "• Ethmoid sinus tumor / mucocele\n• Medial orbital wall lesion\n• Dacryocystocele (nasolacrimal duct cyst)",
     "Globe pushed outward from nose; restricted abduction"],
    ["NON-AXIAL Proptosis",
     "Medial\n(Inward)",
     "Lateral extraconal lesion pushes globe medially",
     "• Lacrimal gland enlargement (less common direction)\n• Temporal fossa lesion with orbital extension\n• Sphenoid wing meningioma",
     "Globe pushed toward midline; temporal fullness on palpation"],
]
story.append(make_table(
    dir_data[0], dir_data[1:],
    col_widths=[3.5*cm, 2.8*cm, 4.0*cm, 5.2*cm, 4.0*cm]
))
story.append(Spacer(1, 0.25*cm))

story.append(highlight_box(
    '<b>CLINICAL RULE: Globe goes OPPOSITE the lesion.</b> An orbital mass will displace the globe away from where it sits. '
    'Exception: axial proptosis (intraconal lesion) = globe goes straight forward. '
    'Note the direction of non-axial displacement on exam to localise the probable site of disease.',
    bg=colors.HexColor("#FFF8E7"), border=AMBER
))
story.append(Spacer(1, 0.4*cm))

# ────────────────────────────────────────────────────────────────────────────
story.append(Paragraph("2B. CLASSIFICATION BY ONSET / TEMPORAL PATTERN", sH2))
story.append(AccentLine(color=CORAL, thick=2))
story.append(Spacer(1, 0.2*cm))

onset_data = [
    ["Onset Type", "Timeframe", "Typical Causes", "Emergency?"],
    ["ACUTE Proptosis",
     "Hours to days",
     "Orbital cellulitis / abscess\nOrbital haemorrhage (trauma, anticoagulation, rupture of varix/lymphangioma)\nOrbital emphysema (medial wall fracture with nose blow)\nCavernous sinus thrombosis\nMucormycosis (in DKA/immunocompromised)\nRupture of ethmoidal mucocele",
     "YES – sight-threatening emergency. Urgent CT orbit and ophthalmology review."],
    ["SUBACUTE Proptosis",
     "Days to weeks",
     "Thyroid Eye Disease (Graves' orbitopathy)\nOrbital pseudotumor (idiopathic orbital inflammation)\nOrbital myositis\nRhabdomyosarcoma (children – rapid in weeks)\nCavernous sinus thrombosis (slower form)",
     "URGENT – review within days; may threaten vision."],
    ["CHRONIC Proptosis",
     "Months to years",
     "Benign orbital tumors (cavernous hemangioma, dermoid)\nOptic nerve glioma\nOptic nerve sheath meningioma\nMetastatic tumor\nLacrimal gland tumor\nOrbital lymphoma\nFibrous dysplasia\nSarcoidosis / Wegener's GPA",
     "NON-URGENT – but important to investigate. Slow progression does not exclude malignancy."],
    ["INTERMITTENT Proptosis",
     "Comes and goes",
     "Orbital varix (increases with Valsalva, coughing, bending forward)\nPeriodic orbital oedema\nRecurrent orbital haemorrhage (into lymphangioma – 'chocolate cyst')\nHighly vascular tumours\nEncephalocele (varies with ICP)",
     "INVESTIGATE – CT/MRI; if Valsalva-related think orbital varix."],
    ["PULSATILE Proptosis",
     "Synchronous with\nheart beat",
     "Carotico-cavernous fistula (CCF) – direct or indirect Barrow type\nSaccular aneurysm of ophthalmic artery\nTransmitted cerebral pulsations with bony defect:\n  • Congenital meningocele / meningoencephalocele\n  • Neurofibromatosis (absent sphenoid wing)\n  • Post-surgical bone defect",
     "URGENT – CCF requires angiography; risk of visual loss and haemorrhage."],
]
story.append(make_table(
    onset_data[0], onset_data[1:],
    col_widths=[3.8*cm, 3.0*cm, 8.5*cm, 3.5*cm]
))
story.append(Spacer(1, 0.4*cm))

# ────────────────────────────────────────────────────────────────────────────
story.append(Paragraph("2C. CLASSIFICATION BY LATERALITY", sH2))
story.append(AccentLine(color=PURPLE, thick=2))
story.append(Spacer(1, 0.2*cm))

lat_data = [
    ["UNILATERAL Proptosis", "BILATERAL Proptosis"],
    [
        "<b>INFLAMMATORY / INFECTIOUS</b>\n"
        "• Orbital cellulitis / subperiosteal abscess\n"
        "• Orbital pseudotumor (usually unilateral)\n"
        "• Cavernous sinus thrombosis\n"
        "• Dacryoadenitis\n\n"
        "<b>NEOPLASTIC</b>\n"
        "• All primary orbital tumors\n"
        "• Metastases (usually unilateral initially)\n"
        "• Rhabdomyosarcoma (children)\n"
        "• Sinus tumors with orbital extension\n\n"
        "<b>VASCULAR</b>\n"
        "• CCF (unilateral direct trauma)\n"
        "• Orbital varix / lymphangioma\n\n"
        "<b>STRUCTURAL</b>\n"
        "• Dermoid / epidermoid cyst\n"
        "• Mucocele of paranasal sinuses\n"
        "• Orbital haemorrhage\n"
        "• Blow-out fracture\n\n"
        "<b>THYROID EYE DISEASE</b> – can present as apparent unilateral\n"
        "(bilateral but asymmetric in ~70% of TED)",
        "<b>ENDOCRINE</b>\n"
        "• Graves' orbitopathy (#1 bilateral cause)\n\n"
        "<b>DEVELOPMENTAL / BONE</b>\n"
        "• Craniosynostosis (oxycephaly, crouzon)\n"
        "• Osteopathies: rickets, acromegaly, Paget's\n\n"
        "<b>TUMORS</b>\n"
        "• Lymphoma / leukaemia\n"
        "• Ewing's sarcoma\n"
        "• Neuroblastoma (children – raccoon eyes)\n\n"
        "<b>INFLAMMATORY / SYSTEMIC</b>\n"
        "• Fungal granuloma (bilateral involvement)\n"
        "• Mikulicz syndrome (bilateral lacrimal + parotid swelling)\n"
        "• Sarcoidosis (bilateral orbital)\n"
        "• Wegener's GPA (bilateral orbital extension)\n\n"
        "<b>VASCULAR</b>\n"
        "• Bilateral CCF (cavernous sinus involvement)\n"
        "• Venous outflow obstruction"
    ]
]
t = Table(
    [[Paragraph(h, S("lath", fontSize=11, fontName="Helvetica-Bold", textColor=WHITE, alignment=TA_CENTER)) for h in lat_data[0]],
     [Paragraph(lat_data[1][0].replace("\n", "<br/>"), sBody),
      Paragraph(lat_data[1][1].replace("\n", "<br/>"), sBody)]],
    colWidths=[(W-3.2*cm)/2, (W-3.2*cm)/2]
)
t.setStyle(TableStyle([
    ("BACKGROUND", (0,0), (0,0), CORAL),
    ("BACKGROUND", (1,0), (1,0), PURPLE),
    ("BACKGROUND", (0,1), (0,1), colors.HexColor("#FEF0F0")),
    ("BACKGROUND", (1,1), (1,1), colors.HexColor("#F3EDF8")),
    ("VALIGN", (0,0), (-1,-1), "TOP"),
    ("GRID", (0,0), (-1,-1), 0.5, MUTED),
    ("LEFTPADDING", (0,0), (-1,-1), 6),
    ("TOPPADDING", (0,0), (-1,-1), 6),
    ("BOTTOMPADDING", (0,0), (-1,-1), 6),
]))
story.append(t)
story.append(Spacer(1, 0.4*cm))

# ────────────────────────────────────────────────────────────────────────────
story.append(Paragraph("2D. DETAILED CAUSE PROFILES BY TYPE", sH2))
story.append(AccentLine(color=TEAL, thick=2))
story.append(Spacer(1, 0.2*cm))

story.append(Paragraph('<b>AXIAL PROPTOSIS – Intraconal Lesions</b>', sH3))
axial_data = [
    ["Condition", "Age / Sex", "Onset", "Key Features", "Imaging", "Management"],
    ["Thyroid Eye Disease", "30-60y, F>M (4:1)", "Subacute weeks", "Bilateral, lid retraction, von Graefe sign, Dalrymple sign; TED signs; EOM enlargement (inferior > medial)", "CT: fusiform EOM enlargement sparing tendons; MRI: fat prolapse", "Selenium (mild); IV steroids; teprotumumab; decompression"],
    ["Cavernous Hemangioma", "Middle-aged adults, F>M", "Months–years", "Painless, slowly progressive, may have diplopia; most common benign orbital tumor in adults", "MRI: T2 very bright, well-circumscribed; 'progressive fill' on dynamic CE-MRI", "Observation if small; surgical excision if vision threatened"],
    ["Optic Nerve Glioma", "Children (usually <10y)", "Months", "Visual loss + proptosis + optic atrophy; 15-40% NF1 association", "MRI: fusiform optic nerve enlargement; can extend to chiasm", "Observe; chemotherapy (carboplatin/vincristine) if progressive"],
    ["Optic Nerve Sheath Meningioma", "Middle-aged women (40-50y)", "Months–years", "Chronic painless proptosis, progressive VA loss, optociliary shunt vessels on fundus", "CT: 'tram-track' calcification; MRI: enhancing sheath around nerve", "Stereotactic RT (fractionated); surgery only if blind"],
    ["AV Malformation / CCF", "Any age; trauma (CCF)", "Acute-subacute", "Pulsatile proptosis + bruit (auscultate!), chemosis, dilated conjunctival vessels, raised IOP", "Angiography (gold standard); MRI: dilated superior ophthalmic vein", "Endovascular embolisation (first line)"],
    ["Orbital Lymphoma", "Adults >50y", "Weeks–months", "Painless, 'salmon-patch' conjunctival lesion, diffuse orbital mass; bilateral possible", "MRI: T1 isointense to muscle, T2 variable, homogeneous, moulds to orbital structures", "Orbital radiotherapy 20 Gy; systemic chemo if disseminated"],
]
story.append(make_table(axial_data[0], axial_data[1:],
    col_widths=[3.5*cm, 2.8*cm, 2.2*cm, 5.0*cm, 3.5*cm, 5.5*cm]))
story.append(Spacer(1, 0.35*cm))

story.append(Paragraph('<b>NON-AXIAL PROPTOSIS – Extraconal & Other Lesions</b>', sH3))
nonaxial_data = [
    ["Condition", "Typical Direction", "Onset", "Key Features", "Management"],
    ["Lacrimal Gland Tumor\n(Pleomorphic adenoma / AdCC)", "Inferior + medial\n(inferomedial)", "Months–years", "Painless mass superolateral quadrant; adenoid cystic CA: PAINFUL, perineural invasion, bone erosion", "Excision (pleomorphic); radical excision (AdCC); RT for malignant"],
    ["Orbital Cellulitis", "Axial or downward", "Acute hours-days", "Fever, erythema, restricted EOM, pain, ↑WCC; preceded by sinusitis", "IV antibiotics; CT staging (Chandler); surgical drainage (grade III-IV)"],
    ["Rhabdomyosarcoma", "Inferonasal is classic", "Weeks (rapid)", "Children 5-15y; rapidly progressive; most common PRIMARY malignant orbital tumor in children", "Biopsy → chemo + radiotherapy; orbital exenteration now rare"],
    ["Dermoid Cyst", "Superolateral (frontozygomatic suture)", "Months–years", "Painless, smooth, non-tender, may have dimple on skin; dumbbell type spans orbital rim", "Complete surgical excision; avoid rupture (chemical dacryocystitis)"],
    ["Sinus Mucocele", "Medial / superior", "Months–years", "Frontoethmoidal most common; slow expansion; previous sinusitis/surgery history; translucent swelling", "Endoscopic marsupialization (preferred); open orbitotomy if needed"],
    ["Metastatic Tumor", "Variable, often non-axial", "Variable (weeks)", "Breast (most common), scirrhous type causes ENOPHTHALMOS; prostate, melanoma, lung; history of primary", "Systemic chemo + palliative RT; avoid exenteration; biopsy to confirm"],
    ["Neuroblastoma Mets", "Bilateral, periorbital", "Acute / subacute", "Children <5y; bilateral periorbital ecchymosis 'raccoon eyes'; proptosis; +/-abdominal mass", "Chemotherapy ± radiotherapy; staging essential"],
    ["Frontal Sinus Osteoma", "Downward + forward", "Months–years", "Young males; dense bony mass on CT; headaches; may block frontal recess", "Surgical excision if symptomatic"],
    ["Wegener's GPA", "Variable, often bilateral", "Weeks", "Saddle-nose deformity; sinus/pulmonary/renal triad; c-ANCA+; dacryocystitis; scleritis", "Prednisolone + cyclophosphamide or rituximab"],
    ["Encephalocele", "Medial / superior + pulsatile", "Congenital", "Pulsates with respiration/Valsalva; bony defect on CT; soft, compressible", "Neurosurgical repair; do NOT biopsy blindly"],
    ["Orbital Varix", "Axial, intermittent", "Intermittent", "Increases with Valsalva / bending forward; venous malformation; can acutely thrombose", "Conservative; sclerotherapy or excision for recurrent thrombosis"],
]
story.append(make_table(nonaxial_data[0], nonaxial_data[1:],
    col_widths=[4.2*cm, 3.2*cm, 2.5*cm, 5.8*cm, 4.5*cm]))
story.append(Spacer(1, 0.4*cm))

story.append(Paragraph('<b>SPECIAL TYPES: Pulsatile & Intermittent Proptosis – Full Differential</b>', sH3))

pulsatile_data = [
    ["PULSATILE PROPTOSIS – Causes", "Mechanism", "How to Confirm"],
    ["Direct CCF (traumatic)\n(Barrow Type A)", "Direct fistula ICA → cavernous sinus; high flow; acute onset", "Angiography; MRI shows dilated SOV; orbital bruit"],
    ["Indirect CCF (dural fistula)\n(Barrow Types B/C/D)", "Meningeal branches of ICA/ECA → cavernous sinus; low flow; spontaneous", "DSA angiography gold standard; orbital Doppler"],
    ["Ophthalmic artery aneurysm", "Arterial pulsation transmitted to orbit", "MRI/MRA; angiography"],
    ["Encephalocele (meningocele)", "Brain pulsation transmitted through bony defect in orbital roof/medial wall", "CT: bony defect; MRI: herniated brain/meninges"],
    ["Neurofibromatosis (NF1)", "Absent greater wing of sphenoid → direct brain pulsation", "CT: sphenoid dysplasia; plexiform neurofibroma"],
    ["Post-surgical / traumatic bony defect", "Orbital roof defect allows cerebral pulsation transmission", "CT orbit: bone gap"],
]
story.append(make_table(pulsatile_data[0], pulsatile_data[1:],
    col_widths=[5.5*cm, 7.0*cm, 7.5*cm]))
story.append(Spacer(1, 0.3*cm))

intermittent_data = [
    ["INTERMITTENT PROPTOSIS – Causes", "Trigger", "Key Test"],
    ["Orbital Varix\n(most common)", "Increases with Valsalva, coughing, bending forward, crying", "CT/MRI in prone + Valsalva position – distended vessel"],
    ["Lymphangioma\n(macrocystic/microcystic)", "Spontaneous haemorrhage ('chocolate cyst'); URTI triggers sudden swelling", "MRI: fluid-fluid levels; no contrast enhancement"],
    ["Periodic orbital oedema", "Recurrent episodic oedema, often allergic component", "Clinical; response to antihistamines/steroids"],
    ["Encephalocele", "Straining / crying / Valsalva", "CT: bony defect; MRI: brain herniation"],
    ["Highly vascular tumour", "Dependent position; Valsalva", "MRI with contrast; angiography"],
    ["Recurrent orbital haemorrhage", "Anticoagulation; haematological disorders", "CT: blood in orbit; coagulation screen"],
]
story.append(make_table(intermittent_data[0], intermittent_data[1:],
    col_widths=[5.0*cm, 7.5*cm, 7.5*cm]))
story.append(Spacer(1, 0.4*cm))

# ────────────────────────────────────────────────────────────────────────────
story.append(Paragraph("2E. PSEUDO-PROPTOSIS (Apparent Proptosis Without True Forward Displacement)", sH2))
story.append(AccentLine(color=ORANGE, thick=2))
story.append(Spacer(1, 0.2*cm))

story.append(Paragraph(
    'Pseudo-proptosis refers to conditions that create the <i>appearance</i> of proptosis without true anterior globe displacement. '
    'Hertel measurement will be normal or symmetrical. This is a key differential to exclude before pursuing orbital investigations.',
    sBody
))
story.append(Spacer(1, 0.2*cm))

pseudo_data = [
    ["Condition", "Why it looks like proptosis", "How to differentiate"],
    ["Contralateral enophthalmos", "The sunken opposite eye makes the normal eye look proptotic", "Hertel both eyes; the 'proptotic' eye is actually normal; CT orbit for fracture/atrophy"],
    ["High myopia (axial myopia)", "Longer globe protrudes further in orbit", "Refraction; axial length on biometry; often bilateral"],
    ["Lid retraction\n(TED, sympathomimetic)", "Wider palpebral aperture exposes more sclera, mimics proptosis", "Hertel normal; lid crease height elevated; TFTs"],
    ["Orbital fat prolapse", "Age-related or post-surgical fat prolapse anteriorly", "Normal Hertel; palpable fat globules without firm mass"],
    ["Buphthalmos\n(enlarged globe)", "Globe enlarged due to congenital glaucoma", "Corneal diameter >12 mm; elevated IOP; Haab's striae"],
    ["Facial asymmetry / shallow orbit", "Unilateral orbital dysplasia; craniosynostosis; small contralateral orbit", "CT facial bones; symmetry of orbital rims"],
    ["Hyperthyroidism without infiltrative disease", "Sympathetic overactivity → lid retraction; NOT true proptosis", "Hertel normal; no EOM enlargement on CT"],
    ["Severe obesity / Cushing's disease", "Periorbital fat deposits mimic proptosis", "Hertel normal; no orbital mass"],
]
story.append(make_table(pseudo_data[0], pseudo_data[1:],
    col_widths=[4.5*cm, 7.0*cm, 8.5*cm]))
story.append(Spacer(1, 0.4*cm))

story.append(highlight_box(
    '<b>ENOPHTHALMOS (opposite of proptosis)</b> – Posterior globe displacement.<br/>'
    'Causes: (1) Blow-out fracture of orbital floor/medial wall (acute); '
    '(2) Scirrhous carcinoma of breast metastasis to orbit (fibrotic contraction); '
    '(3) Post-irradiation orbital atrophy; (4) Hemifacial atrophy (Parry-Romberg); '
    '(5) Fat atrophy (post-inflammatory / lipoatrophy); (6) Silent sinus syndrome. '
    'Key: scirrhous breast metastasis causes ENOPHTHALMOS not proptosis – a classic exam point.',
    bg=colors.HexColor("#EDF8ED"), border=GREEN
))
story.append(Spacer(1, 0.4*cm))

# ═════════════════════════════════════════════════════════════════════════════
# PAGE BREAK for Examination section
# ═════════════════════════════════════════════════════════════════════════════
story.append(PageBreak())

# ═════════════════════════════════════════════════════════════════════════════
# SECTION 3 – EXAMINATION OF PROPTOSIS
# ═════════════════════════════════════════════════════════════════════════════
add_section_header("3. EXAMINATION OF PROPTOSIS – FULL PROTOCOL", "Systematic residency-level examination framework", color=TEAL)

story.append(Paragraph("OVERVIEW: THE ORBITAL EXAMINATION SEQUENCE", sH2))
story.append(AccentLine(color=AMBER, thick=2))
story.append(Spacer(1, 0.2*cm))

story.append(Paragraph(
    'The orbital examination follows a logical sequence moving from observation → quantification → '
    'palpation → function → vision assessment. Each step provides diagnostic information that narrows the differential.',
    sBody
))
story.append(Spacer(1, 0.2*cm))

# Sequence as numbered table
seq_data = [
    ["Step", "Component", "What to Assess", "Diagnostic Value"],
    ["1", "Observation\n(Inspection)", "Face symmetry, periorbital changes, globe position, lids, skin colour, chemosis, proptosis direction", "Asymmetry; lid signs; direction of displacement; signs of inflammation or mass"],
    ["2", "Hertel\nExophthalmometry", "Quantify globe protrusion from lateral orbital rim; base setting", "Degree of proptosis; asymmetry; change over time"],
    ["3", "Globe Position\nAssessment", "Axial vs non-axial displacement; vertical / horizontal dystopia", "Localises lesion: intraconal (axial) vs extraconal (non-axial)"],
    ["4", "Palpation", "Orbital rims (tenderness, step-off), anterior orbit, lacrimal fossa, regional lymph nodes, thyroid", "Mass location/consistency; tenderness (inflammatory); lymphadenopathy; thyroid enlargement"],
    ["5", "Retropulsion\nTest", "Gentle pressure on closed lids to push globe back; compare resistance both eyes", "Resistance → mass or inflammatory infiltrate; easy retropulsion → normal or muscle fibrosis"],
    ["6", "Auscultation", "Listen over globe / temporal area for bruit", "Orbital bruit → CCF or AV malformation (pulsatile proptosis)"],
    ["7", "Eyelid\nExamination", "Palpebral fissure height; lid retraction; lid lag; lagophthalmos; ptosis; margin position", "Lid retraction (TED); lid lag = von Graefe; lagophthalmos → exposure risk"],
    ["8", "Extraocular\nMotility (EOM)", "9 positions of gaze; identify restriction pattern; pain on movement", "Restrictive (fibrosis – forced duction positive) vs neurogenic; TED pattern; cellulitis"],
    ["9", "Forced Duction\nTest", "Grasp episclera with forceps; attempt to move eye in restricted direction", "Positive (resistance) = restrictive myopathy (fibrosis/entrapment); Negative = neurogenic palsy"],
    ["10", "Visual\nAcuity", "Best corrected VA each eye", "↓VA → optic nerve compression, corneal exposure, glaucoma"],
    ["11", "Colour Vision", "Ishihara plates / red colour saturation test", "Colour desaturation = early optic nerve compression (often before VA change!)"],
    ["12", "Pupil Assessment", "Direct + consensual; RAPD (relative afferent pupillary defect)", "RAPD → optic neuropathy (compressive/ischaemic); urgency indicator"],
    ["13", "Visual Field", "Confrontation initially; formal perimetry if needed", "Central scotoma, nerve fibre bundle defects = compressive optic neuropathy"],
    ["14", "Intraocular\nPressure (IOP)", "Tonometry; measure in primary gaze AND upgaze", "IOP rise >4 mmHg on upgaze in TED = fibrotic inferior rectus compressing vortex veins"],
    ["15", "Slit Lamp\nExamination", "Corneal exposure; punctate epitheliopathy; conjunctival chemosis; epibulbar hyperaemia over recti", "Exposure keratopathy; dry eye; inflammation markers"],
    ["16", "Fundoscopy /\nOphthalmoscopy", "Disc: swelling, pallor; optociliary shunt vessels; choroidal folds", "Disc swelling = raised IOP/optic nerve compression; optociliary shunts = optic nerve sheath meningioma; choroidal folds = axial displacement"],
]
story.append(make_table(seq_data[0], seq_data[1:],
    col_widths=[1.5*cm, 3.5*cm, 6.0*cm, 9.0*cm]))
story.append(Spacer(1, 0.4*cm))

# ─────────────────────────────────────────────────────────────────────────────
story.append(Paragraph("3A. DETAILED: EYELID EXAMINATION IN PROPTOSIS", sH2))
story.append(AccentLine(color=TEAL, thick=2))
story.append(Spacer(1, 0.2*cm))

lid_data = [
    ["Sign", "Definition", "What it Indicates"],
    ["Dalrymple Sign", "Lid retraction in PRIMARY gaze → widened palpebral fissure; sclera visible above limbus (upper lid) or below limbus (lower lid)", "Thyroid Eye Disease (TED) – levator / Müller muscle spasm or fibrosis"],
    ["Von Graefe Sign", "Retarded descent of upper lid on DOWNWARD gaze (lid lag on downgaze)", "TED – upper lid fails to follow globe downward due to levator fibrosis/spasm"],
    ["Stellwag Sign", "Infrequent or incomplete blinking", "TED – sympathomimetic and surface exposure"],
    ["Kocher Sign", "Staring / frightened expression particularly on attentive fixation", "TED – bilateral lid retraction + proptosis + reduced blink"],
    ["Rosenbach Sign", "Fine tremor of closed eyelids", "TED / hyperthyroidism (tremor of levator)"],
    ["Griffith Sign", "Lower lid lag on upward gaze", "TED – lower lid retraction – sclera shows inferiorly on upgaze"],
    ["Boston Sign", "Jerky, irregular movement of upper lid on downward gaze", "TED"],
    ["Enroth Sign", "Oedema of lower eyelid", "TED – oedematous phase"],
    ["Lagophthalmos", "Inability to fully close lids → corneal exposure", "Severe TED; facial nerve palsy; cicatricial ectropion; post-surgical"],
    ["Lid Retraction\n(upper lid)", "Upper lid margin level with or above superior limbus (normal = 2 mm below limbus)", "TED; contralateral ptosis; sympathomimetic drops; Parinaud's (pretectal)"],
    ["Lid Retraction\n(lower lid)", "Lower lid margin below inferior limbus (normal = at limbus)", "TED; post-surgical; cicatricial changes"],
    ["Ptosis", "Upper lid covers >2 mm of superior cornea / asymmetric low lid", "Horner's (trauma, CCF); III nerve palsy; pseudoptosis in proptosis (globe pushed up, lid appears low)"],
]
story.append(make_table(lid_data[0], lid_data[1:],
    col_widths=[4.0*cm, 8.5*cm, 7.5*cm]))
story.append(Spacer(1, 0.3*cm))

# Add lid signs image
if img_lidsigns:
    try:
        img = RLImage(img_lidsigns, width=8*cm, height=9*cm)
        cap = Paragraph(
            "Fig. 1 – Lid Signs in Thyroid Eye Disease (Kanski's Clinical Ophthalmology 10e):<br/>"
            "(A) Mild left lid retraction with scleral show; (B) Bilateral Dalrymple sign; "
            "(C) Severe bilateral Kocher sign; (D) Right lid lag on downgaze – von Graefe sign",
            sCaption
        )
        t = Table([[img, cap]], colWidths=[8.5*cm, 11.0*cm])
        t.setStyle(TableStyle([
            ("VALIGN", (0,0), (-1,-1), "TOP"),
            ("LEFTPADDING", (0,0), (-1,-1), 4),
        ]))
        story.append(KeepTogether([t]))
    except Exception as e:
        print(f"Image insert error: {e}")
story.append(Spacer(1, 0.4*cm))

# ─────────────────────────────────────────────────────────────────────────────
story.append(Paragraph("3B. EXTRAOCULAR MOTILITY & RESTRICTIVE MYOPATHY", sH2))
story.append(AccentLine(color=CORAL, thick=2))
story.append(Spacer(1, 0.2*cm))

story.append(Paragraph(
    'EOM assessment is critical in proptosis. The key distinction is between '
    '<b>restrictive myopathy</b> (fibrosis/entrapment – forced duction positive) and '
    '<b>neurogenic palsy</b> (nerve damage – forced duction negative).',
    sBody
))
story.append(Spacer(1, 0.2*cm))

eom_data = [
    ["EOM Pattern", "Muscle Affected", "Cause", "Distinguishing Test"],
    ["Restricted ELEVATION\n(can't look up)", "Inferior rectus fibrosis / entrapment", "TED (most common EOM in TED = inferior rectus); blow-out fracture of orbital floor with IR entrapment", "Forced duction test (+) = restrictive; (-) = neurogenic; CT orbit: thickened IR or trap-door fracture"],
    ["Restricted ABDUCTION\n(looks like VI nerve palsy)", "Medial rectus fibrosis", "TED – medial rectus is 2nd most common; cavernous sinus pathology", "Forced duction (+) in TED; (-) in true VI palsy; IOP ↑ on attempted abduction in TED"],
    ["Restricted DEPRESSION\n(can't look down)", "Superior rectus fibrosis", "TED – 3rd most common", "Forced duction test; CT: SR thickening"],
    ["Restricted ADDUCTION", "Lateral rectus fibrosis", "TED – least common; orbital floor fracture medial extension", "Forced duction test; CT"],
    ["Complete ophthalmoplegia", "All muscles", "Orbital apex syndrome; orbital cellulitis with abscess; cavernous sinus thrombosis", "Emergency; CT/MRI; systemic sepsis signs"],
    ["Painful EOM restriction", "Often multiple", "Orbital pseudotumor/myositis; orbital cellulitis; TED (active phase)", "CT: tendon involvement (pseudotumor) vs sparing (TED); response to steroids"],
]
story.append(make_table(eom_data[0], eom_data[1:],
    col_widths=[4.0*cm, 4.5*cm, 6.5*cm, 5.5*cm]))
story.append(Spacer(1, 0.2*cm))

story.append(highlight_box(
    '<b>TED EOM INVOLVEMENT ORDER (mnemonic: I\'M SLow):</b><br/>'
    'Inferior rectus → Medial rectus → Superior rectus → Lateral rectus<br/>'
    'The muscle belly enlarges (fusiform) with TENDON SPARING – key distinguishing feature from orbital myositis (which involves the tendon).',
    bg=colors.HexColor("#E8F4F8"), border=TEAL
))
story.append(Spacer(1, 0.3*cm))

# Add myopathy image
if img_myopathy:
    try:
        img = RLImage(img_myopathy, width=8*cm, height=7.5*cm)
        cap = Paragraph(
            "Fig. 2 – Restrictive Myopathy in TED (Kanski's Clinical Ophthalmology 10e):<br/>"
            "(A) Defective elevation – inferior rectus fibrosis; (B) Reduced abduction – medial rectus fibrosis; "
            "(C) Defective depression – superior rectus fibrosis",
            sCaption
        )
        t = Table([[img, cap]], colWidths=[8.5*cm, 11.0*cm])
        t.setStyle(TableStyle([
            ("VALIGN", (0,0), (-1,-1), "TOP"),
            ("LEFTPADDING", (0,0), (-1,-1), 4),
        ]))
        story.append(KeepTogether([t]))
    except Exception as e:
        print(f"Image insert error: {e}")
story.append(Spacer(1, 0.4*cm))

# ─────────────────────────────────────────────────────────────────────────────
story.append(Paragraph("3C. OPTIC NERVE ASSESSMENT – SIGHT-THREATENING SIGNS", sH2))
story.append(AccentLine(color=RED, thick=2))
story.append(Spacer(1, 0.2*cm))

story.append(highlight_box(
    '<b>CRITICAL:</b> Compressive optic neuropathy (DON – Dysthyroid Optic Neuropathy) can occur WITHOUT '
    'significant proptosis. The nerve is compressed by congested recti at the orbital apex. '
    'This is a <b>sight-threatening emergency</b> requiring IV steroids or urgent orbital decompression.',
    bg=colors.HexColor("#FDF0F0"), border=RED
))
story.append(Spacer(1, 0.2*cm))

on_data = [
    ["Sign", "Method", "Significance", "Action if Abnormal"],
    ["Reduced Visual Acuity", "Snellen chart (each eye separately)", "VA ↓ in optic neuropathy; may be preserved initially in compressive DON", "Urgent: IV methylprednisolone; orbital decompression within 24-48h if no response"],
    ["Colour Desaturation\n(MOST SENSITIVE early sign)", "Compare red bottle cap brightness each eye / Ishihara plates", "Colour desaturation often precedes VA loss in compressive optic neuropathy", "Treat as DON until proven otherwise"],
    ["RAPD\n(Relative Afferent Pupillary Defect)", "Swinging flashlight test; look for ipsilateral pupil dilation", "Indicates significant asymmetric optic nerve dysfunction", "Emergency; indicates >0.3 log unit difference in afferent function"],
    ["Visual Field Defects", "Confrontation → formal perimetry (Humphrey)", "Central scotoma, nerve fibre defects; may be confused with glaucoma", "Urgent imaging; neuro-ophthalmic input"],
    ["Fundoscopy – Disc Swelling", "Direct ophthalmoscopy / slit lamp with 90D lens", "Raised ICP / severe orbital congestion; papilloedema", "Urgent; exclude cavernous sinus thrombosis"],
    ["Fundoscopy – Disc Pallor", "As above", "Previous/chronic optic nerve damage; poor prognosis for vision recovery", "Implies chronic untreated compression"],
    ["Optociliary Shunt Vessels", "Fundoscopy: collateral vessels at disc surface", "PATHOGNOMONIC of optic nerve sheath meningioma (chronic venous compression)", "MRI + neurosurgical/radiation oncology input"],
    ["Choroidal Folds", "Fundoscopy / OCT", "Axial compression of posterior globe; seen in severe proptosis or retrobulbar mass", "CT/MRI for posterior segment pathology"],
    ["IOP on Upgaze", "Goldmann tonometry in primary gaze then upgaze", "IOP ↑ >4 mmHg on upgaze = inferior rectus fibrosis compressing vortex veins (TED)", "Confirms restrictive inferior rectus involvement"],
]
story.append(make_table(on_data[0], on_data[1:],
    col_widths=[4.0*cm, 4.0*cm, 6.0*cm, 6.0*cm]))
story.append(Spacer(1, 0.4*cm))

# ─────────────────────────────────────────────────────────────────────────────
story.append(Paragraph("3D. PALPATION & SPECIAL TESTS", sH2))
story.append(AccentLine(color=PURPLE, thick=2))
story.append(Spacer(1, 0.2*cm))

palp_data = [
    ["Test / Finding", "Technique", "Positive Finding", "Diagnosis Suggested"],
    ["Orbital Palpation\n(rims + anterior orbit)", "Four-finger orbital rim palpation; palpate through closed lids for anterior orbital mass; note tenderness, consistency, mobility", "Palpable mass; tenderness over orbit; step-off deformity at rim", "Mass: tumour/cyst; Tenderness: cellulitis/pseudotumor; Step-off: fracture"],
    ["Lacrimal Fossa\nPalpation", "Press superolateral orbital rim gently; palpate fullness", "Hard, smooth, slowly growing mass; irregular/painful mass", "Smooth = benign (pleomorphic adenoma); Irregular+painful = adenoid cystic carcinoma (malignant)"],
    ["Retropulsion Test", "Place index fingers over closed lids; apply gentle posterior pressure; compare both sides", "Resistance to retropulsion (eye doesn't push back easily)", "Orbital mass, fibrosis, inflammation; normal eye retropulses easily"],
    ["Pulsation Test", "Observe proptotic eye for rhythmic pulsation synchronous with pulse", "Visible pulsation", "CCF; orbital encephalocele; AV malformation; bony defect"],
    ["Auscultation\nover orbit", "Place stethoscope bell over closed lid; also temporal bone", "Orbital bruit (soft continuous murmur)", "CCF or AV malformation; can also hear with patient's own ear canal (subjective bruit)"],
    ["Valsalva Test", "Ask patient to blow nose (occlude nostrils) while you watch proptosis", "Proptosis increases with Valsalva", "Orbital varix (most specific); any vascular lesion"],
    ["Reduce on pressure\n(reducibility)", "Gently push globe posteriorly with closed lid", "Globe reduces = compressible; does NOT reduce = firm lesion", "Compressible = varix/lymphangioma; Non-compressible = solid tumour"],
    ["Transillumination", "Shine pen torch through lids in dark room", "Transilluminates = cystic lesion (dermoid, mucocele)", "Cystic vs solid lesion distinction"],
    ["Regional LN\nPalpation", "Palpate pre-auricular, submandibular, cervical nodes", "Lymphadenopathy", "Lymphoma; metastatic disease; infectious"],
    ["Thyroid Exam", "Palpate thyroid; check for goitre, nodules, bruit", "Goitre; thyroid bruit; tender thyroid", "TED (Graves' disease); thyroiditis"],
]
story.append(make_table(palp_data[0], palp_data[1:],
    col_widths=[4.0*cm, 5.5*cm, 5.0*cm, 5.5*cm]))
story.append(Spacer(1, 0.4*cm))

# ─────────────────────────────────────────────────────────────────────────────
story.append(PageBreak())
story.append(Paragraph("3E. SYSTEMIC EXAMINATION IN PROPTOSIS", sH2))
story.append(AccentLine(color=NAVY, thick=2))
story.append(Spacer(1, 0.2*cm))

syst_data = [
    ["System", "What to Examine", "Positive Finding → Diagnosis"],
    ["Thyroid / Endocrine", "Thyroid size, consistency, bruit; heart rate, tremor, warm/moist skin; pre-tibial myxoedema; thyroid acropachy", "Goitre + tachycardia + warm skin → Graves' TED; Pretibial myxoedema (pathognomonic of Graves')"],
    ["Skin / Nails", "Café-au-lait spots; neurofibromas (subcutaneous); skin crease laxity; pallor", "Café-au-lait + neurofibromas → NF1 (orbital plexiform neurofibroma, sphenoid dysplasia, pulsatile proptosis)"],
    ["Ears / Nose /\nThroat", "Nasal septum (saddle-nose); sinus tenderness; nasal polyps; epistaxis; hearing", "Saddle-nose + nasal perforation → Wegener's GPA; Sinus tenderness → source of orbital cellulitis"],
    ["Respiratory", "Chest auscultation; signs of pneumonitis, haemoptysis", "Bilateral hilar lymphadenopathy on CXR → sarcoidosis; Pulmonary haemorrhage → Wegener's"],
    ["Renal", "BP; urinalysis; oedema", "Haematuria + proteinuria → Wegener's GPA (glomerulonephritis triad)"],
    ["Lymph nodes /\nAbdomen", "All lymph node chains; liver/spleen", "Generalised lymphadenopathy → lymphoma/leukaemia; Hepatosplenomegaly → leukaemia/neuroblastoma"],
    ["Neurological", "Cranial nerve function (II-VI); cerebellar signs; peripheral neuropathy", "Multiple CN palsies → cavernous sinus lesion; Café-au-lait + Lisch nodules → NF1"],
    ["Breast / Prostate", "Breast exam; PSA enquiry in men; prior cancer history", "Prior breast/prostate/lung cancer → orbital metastasis"],
    ["Paediatric\n(children only)", "Abdominal mass; fever; weight loss; developmental milestones; other café-au-lait macules", "Abdominal mass + raccoon eyes → neuroblastoma metastasis (urgent)"],
]
story.append(make_table(syst_data[0], syst_data[1:],
    col_widths=[3.5*cm, 6.5*cm, 10.0*cm]))
story.append(Spacer(1, 0.4*cm))

# ─────────────────────────────────────────────────────────────────────────────
story.append(Paragraph("3F. CLINICAL PHOTO GALLERY – PROPTOSIS PATTERNS", sH2))
story.append(AccentLine(color=CORAL, thick=2))
story.append(Spacer(1, 0.2*cm))

if img_proptosis:
    try:
        img = RLImage(img_proptosis, width=8*cm, height=9*cm)
        cap = Paragraph(
            "<b>Fig. 3 – Proptosis in TED (Kanski's Clinical Ophthalmology 10e):</b><br/>"
            "(A) Bilateral symmetrical proptosis with chemosis and scleral show – classic TED;<br/>"
            "(B) Bilateral asymmetrical proptosis – can mimic unilateral disease;<br/>"
            "(C) Severe bilateral proptosis with exposure leading to bilateral bacterial keratitis (corneal ulceration).<br/>"
            "<i>Note: Corneal breakdown in C represents a sight-threatening emergency – urgent lubricants, tarsorrhaphy, orbital decompression.</i>",
            sCaption
        )
        t = Table([[img, cap]], colWidths=[8.5*cm, 11.0*cm])
        t.setStyle(TableStyle([("VALIGN", (0,0), (-1,-1), "TOP"), ("LEFTPADDING", (0,0), (-1,-1), 4)]))
        story.append(KeepTogether([t]))
    except Exception as e:
        print(f"Image error: {e}")
story.append(Spacer(1, 0.3*cm))

if img_softtissue:
    try:
        img = RLImage(img_softtissue, width=8*cm, height=6*cm)
        cap = Paragraph(
            "<b>Fig. 4 – Soft Tissue Changes in TED (Kanski's Clinical Ophthalmology 10e):</b><br/>"
            "(A) Epibulbar hyperaemia over a horizontal rectus muscle insertion – sign of active inflammation;<br/>"
            "(B) Periorbital oedema, conjunctival chemosis and prolapse of orbital fat into eyelids;<br/>"
            "(C) Superior limbic keratoconjunctivitis (SLK) – characteristic superior corneal/conjunctival inflammation in TED.",
            sCaption
        )
        t = Table([[img, cap]], colWidths=[8.5*cm, 11.0*cm])
        t.setStyle(TableStyle([("VALIGN", (0,0), (-1,-1), "TOP"), ("LEFTPADDING", (0,0), (-1,-1), 4)]))
        story.append(KeepTogether([t]))
    except Exception as e:
        print(f"Image error: {e}")
story.append(Spacer(1, 0.4*cm))

# ═════════════════════════════════════════════════════════════════════════════
# SECTION 4 – DIAGNOSTIC FLOWCHART (as table)
# ═════════════════════════════════════════════════════════════════════════════
story.append(PageBreak())
add_section_header("4. DIAGNOSTIC APPROACH – STEP-BY-STEP ALGORITHM", color=NAVY)

story.append(Paragraph("HISTORY → EXAM → INVESTIGATION PATHWAY", sH2))
story.append(AccentLine(color=AMBER, thick=2))
story.append(Spacer(1, 0.2*cm))

algo_data = [
    ["Step", "Question to Ask", "If YES → Think", "If NO / Normal → Continue"],
    ["1. Onset", "Acute (hours-days)?",
     "Orbital cellulitis → IV antibiotics + CT\nMucormycosis (immunocompromised) → EMERGENCY\nOrbital haemorrhage → CT\nCCF (trauma) → angiography",
     "Go to step 2 (subacute/chronic)"],
    ["2. Laterality", "Bilateral proptosis?",
     "Graves' TED (#1 bilateral) → TFTs, TRAb, CT orbit\nLymphoma/leukaemia\nCraniosynostosis (developmental)\nBilateral pseudotumor (rare)",
     "Unilateral → Go to step 3"],
    ["3. Pulsatile?", "Does proptosis pulsate with heartbeat? Bruit?",
     "CCF → angiography (gold standard)\nEncephalocele → CT/MRI bony defect\nAV malformation → MRI/angiography\nNF1 with sphenoid dysplasia",
     "Go to step 4"],
    ["4. Intermittent?", "Proptosis comes and goes with Valsalva/position?",
     "Orbital varix → CT/MRI in Valsalva position\nLymphangioma → MRI (fluid-fluid levels)\nEncephalocele (ICP dependent)",
     "Persistent proptosis → Go to step 5"],
    ["5. Direction", "Is displacement non-axial (globe pushed sideways/up/down)?",
     "Extraconal lesion:\nDown-in = Lacrimal gland tumor (superolateral)\nDown = Frontal sinus mucocele/roof tumor\nLateral = Ethmoid disease\nUp = Maxillary/floor lesion\nMRI/CT to confirm",
     "Axial proptosis → intraconal lesion → Go to step 6"],
    ["6. Axial Proptosis\nAge + pain", "Child? Painful?",
     "Child + rapid = Rhabdomyosarcoma → URGENT MRI + biopsy\nChild + painless + NF1 history = optic nerve glioma\nChild + fever = orbital cellulitis",
     "Adult → Go to step 7"],
    ["7. Adult axial,\nAge + thyroid", "Thyroid disease / TED signs (lid retraction, lid lag)?",
     "Graves' TED → TFTs, TRAb, orbital CT (EOM enlargement fusiform, tendon sparing)",
     "No TED signs → Go to step 8"],
    ["8. Adult, no TED", "Painful? Tenderness? Response to steroids?",
     "Painful + periorbital = orbital pseudotumor → steroids (diagnostic + therapeutic)\nPainful + fever = cellulitis\nMyositis (EOM pain on movement)",
     "Painless → neoplasm likely → Go to step 9"],
    ["9. Painless adult\nneoplasm workup", "Prior malignancy? Lymphadenopathy?",
     "Metastasis → CT chest/abdomen/pelvis; PET-CT; biopsy\nLymphoma → MRI; whole-body PET-CT; biopsy",
     "No prior cancer → primary benign/malignant orbital tumor → MRI features determine type"],
    ["10. Final imaging\ncharacterisation", "MRI findings?",
     "T2 very bright + well-circumscribed = cavernous hemangioma\nFusiform optic nerve = glioma or meningioma\n'Tram-track' calcification = ONSM\nT1 intermediate, moulds to orbit = lymphoma\nFluid-fluid levels = lymphangioma",
     "Inconclusive → biopsy (medial/lateral orbitotomy)"],
]
story.append(make_table(algo_data[0], algo_data[1:],
    col_widths=[3.5*cm, 4.0*cm, 7.5*cm, 5.0*cm]))
story.append(Spacer(1, 0.4*cm))

# ═════════════════════════════════════════════════════════════════════════════
# SECTION 5 – HIGH YIELD FACTS
# ═════════════════════════════════════════════════════════════════════════════
add_section_header("5. HIGH-YIELD EXAM FACTS & ASSOCIATIONS", color=SLATE)

hyfacts = [
    ("#1 cause of bilateral proptosis in adults", "Graves' Thyroid Eye Disease", RED),
    ("#1 cause of unilateral proptosis in adults", "Graves' TED (often asymmetric bilateral)", RED),
    ("#1 cause of proptosis in children", "Orbital cellulitis", CORAL),
    ("#1 benign orbital tumour in adults", "Cavernous hemangioma (T2 very bright on MRI)", TEAL),
    ("#1 primary malignant orbital tumour in children", "Rhabdomyosarcoma (inferonasal, rapid onset)", RED),
    ("#1 metastatic cause of orbital proptosis", "Breast carcinoma", CORAL),
    ("Breast metastasis causing ENOPHTHALMOS (not proptosis)", "Scirrhous carcinoma → fibrotic orbital contraction", ORANGE),
    ("Optociliary shunt vessels on fundoscopy", "Optic nerve sheath meningioma (chronic venous obstruction)", PURPLE),
    ("'Tram-track' calcification on CT", "Optic nerve sheath meningioma", PURPLE),
    ("Pulsatile proptosis + bruit", "Carotid-cavernous fistula (CCF)", RED),
    ("Intermittent proptosis increases with Valsalva", "Orbital varix (venous malformation)", TEAL),
    ("'Chocolate cyst' / sudden proptosis in child + URTI", "Lymphangioma haemorrhage", AMBER),
    ("Raccoon eyes + proptosis in child under 5", "Neuroblastoma metastasis", RED),
    ("Saddle nose + sinus disease + proptosis + c-ANCA", "Granulomatosis with Polyangiitis (Wegener's)", PURPLE),
    ("TED EOM order (I'M SLow)", "Inferior > Medial > Superior > Lateral rectus (belly only, tendon spared)", TEAL),
    ("Optic neuropathy without significant proptosis", "Can occur in TED – apical crowding compresses nerve", RED),
    ("IOP rise >4 mmHg on upgaze", "Inferior rectus fibrosis in TED compresses vortex veins", NAVY),
    ("Colour desaturation before VA loss", "Earliest sign of compressive optic neuropathy", RED),
    ("Forced duction (+) = restrictive; (-) = neurogenic", "Key distinction for EOM palsy in proptosis", TEAL),
    ("Fusiform EOM enlargement WITH tendon sparing", "Thyroid Eye Disease (vs orbital myositis = involves tendon)", NAVY),
    ("Optic nerve glioma + bilateral", "Neurofibromatosis Type 1 (15-40% have NF1)", PURPLE),
    ("Absent greater wing of sphenoid on CT", "NF1 → pulsating proptosis (brain pulsation transmitted)", PURPLE),
    ("Salmon-patch conjunctival lesion", "Orbital lymphoma", TEAL),
    ("Compressible, increases with Valsalva, no bruit", "Orbital varix", TEAL),
    ("TED can occur in euthyroid state", "5-10% of TED patients are euthyroid; 10% hypothyroid", ORANGE),
    ("Single most modifiable risk factor in TED", "SMOKING – worsens severity and reduces treatment response", RED),
    ("Teprotumumab mechanism", "IGF-1R inhibitor (insulin-like growth factor 1 receptor) – FDA approved for TED proptosis", NAVY),
    ("Orbital decompression sequence (INACTIVE phase only)", "Decompression → Strabismus surgery → Lid surgery → Blepharoplasty", TEAL),
]

fact_rows = []
for item, detail, col in hyfacts:
    fact_rows.append([
        Paragraph(f'<font color="#{col.hexval()[2:]}">★</font> <b>{item}</b>', sBullet),
        Paragraph(detail, sBody)
    ])

t = Table(fact_rows, colWidths=[8.0*cm, 12.0*cm])
t.setStyle(TableStyle([
    ("ROWBACKGROUNDS", (0,0), (-1,-1), [GREY, WHITE]),
    ("GRID", (0,0), (-1,-1), 0.5, colors.HexColor("#CCDDEA")),
    ("LEFTPADDING", (0,0), (-1,-1), 5),
    ("RIGHTPADDING", (0,0), (-1,-1), 5),
    ("TOPPADDING", (0,0), (-1,-1), 3),
    ("BOTTOMPADDING", (0,0), (-1,-1), 3),
    ("VALIGN", (0,0), (-1,-1), "MIDDLE"),
]))
story.append(t)
story.append(Spacer(1, 0.4*cm))

# ═════════════════════════════════════════════════════════════════════════════
# SECTION 6 – URGENT REFERRAL CRITERIA
# ═════════════════════════════════════════════════════════════════════════════
add_section_header("6. URGENT REFERRAL & EMERGENCY CRITERIA", color=RED)

urgent_data = [
    ["Sign / Finding", "Why Urgent", "Immediate Action"],
    ["VA reduced + RAPD", "Compressive optic neuropathy – irreversible vision loss imminent", "Same-day ophthalmology; IV methylprednisolone 1g/day × 3; CT/MRI orbital apex; decompression within 24-48h if no response"],
    ["Colour desaturation without VA change", "Earliest sign of optic nerve compression", "Urgent same-day review; treat as DON; imaging"],
    ["Fever + proptosis + EOM limitation", "Orbital cellulitis / abscess – Chandler III-IV risk; meningitis / CST risk", "Admit; IV antibiotics; urgent CT orbit; ophthalmology + ENT + neurosurgery"],
    ["Pulsatile proptosis + bruit", "CCF – risk of vitreous haemorrhage, ischaemia, raised IOP", "Urgent angiography; neuroradiology; endovascular intervention"],
    ["Corneal ulceration / exposure keratitis", "Corneal melt → perforation → blindness", "Urgent lubricants; lateral tarsorrhaphy; same-day referral"],
    ["Globe subluxation\n(beyond orbital rim)", "Optic nerve stretch; corneal exposure; loss of vision", "Emergency reduction by ophthalmologist; IV steroids"],
    ["Rapidly progressive in child (weeks)", "Rhabdomyosarcoma – most common primary malignant orbital tumour in children; rapid growth", "Urgent MRI orbit + biopsy; paediatric oncology"],
    ["Immunocompromised + proptosis + black eschar", "Mucormycosis – fatal within days if untreated", "Resuscitate; liposomal amphotericin B; emergency surgical debridement"],
    ["Bilateral signs + confusion + meningism", "Cavernous sinus thrombosis – high mortality", "ICU; IV antibiotics; anticoagulation (controversial); neurosurgery"],
]
story.append(make_table(urgent_data[0], urgent_data[1:],
    col_widths=[5.0*cm, 6.0*cm, 9.0*cm]))
story.append(Spacer(1, 0.3*cm))

# Final summary
story.append(highlight_box(
    '<b>SOURCES:</b> Kanski\'s Clinical Ophthalmology: A Systematic Approach, 10th Edition | '
    'KJ Lee\'s Essential Otolaryngology | Goldman-Cecil Medicine (International Ed.) | '
    'Harrison\'s Principles of Internal Medicine 22E | Cummings Otolaryngology | '
    'Bradley and Daroff\'s Neurology in Clinical Practice | '
    'Etiologies of Proptosis: A review (PMC7204542) | '
    'Diagnostic Approach to the Patient with Proptosis (Entokey)',
    bg=GREY, border=MUTED
))

# ─────────────────────────────────────────────────────────────────────────────
# BUILD PDF
# ─────────────────────────────────────────────────────────────────────────────
OUT = "/home/daytona/workspace/proptosis-pdf/Proptosis_Types_Examination_Detailed.pdf"
doc = SimpleDocTemplate(
    OUT,
    pagesize=A4,
    leftMargin=1.5*cm, rightMargin=1.5*cm,
    topMargin=1.2*cm, bottomMargin=1.2*cm,
    title="Proptosis: Types & Examination – Residency Reference",
    author="Orris Medical AI",
    subject="Ophthalmology Residency Reference"
)
doc.build(story)
print(f"PDF saved: {OUT}")
Running Command

cd /home/daytona/workspace/proptosis-pdf && python3 build_pdf.py 2>&1

Running Command

ls -lh /home/daytona/workspace/proptosis-pdf/Proptosis_Types_Examination_Detailed.pdf

Send the pdf

Here are both files ready for download:
Detailed PDF (Types & Examination – A4, 18MB with clinical images):

Proptosis Types Examination Detailed

PDF Document · PDF

Original PPTX (Full 11-slide presentation):

Proptosis Residency Reference

Presentation · PPTX


What's in the PDF:
SectionContent
1. Definition & Normal ValuesHertel technique step-by-step, racial norms (Asian 18mm / White 20mm / Black 22mm), asymmetry rules
2A. Types by DirectionAxial vs Non-axial (5 displacement directions mapped to lesion locations)
2B. Types by OnsetAcute / Subacute / Chronic / Intermittent / Pulsatile – full differentials per category
2C. Types by LateralityUnilateral vs Bilateral – complete cause lists
2D. Cause ProfilesDetailed tables for every axial and non-axial lesion (age, onset, features, imaging, management)
2E. Pseudo-proptosis & EnophthalmosAll mimics + scirrhous breast met causing enophthalmos
3. Full Examination Protocol16-step sequence from inspection → fundoscopy
3A-3FLid signs (all 8 TED signs), EOM/forced duction, optic nerve assessment, palpation tests, systemic exam, clinical photos
4. Diagnostic Algorithm10-step stepwise flowchart for any case of proptosis
5. High-Yield Facts28 board-level facts with #1 causes, associations, mnemonics
6. Urgent Referral Criteria9 emergency scenarios with immediate actions
This is a shared conversation. Sign in to Orris to start your own chat.