Gall Bladder Mass Cytology/FNAC

Reading File
Finding Sources
Finding Sources
Searching PubMed

gallbladder mass FNAC cytology diagnosis

Finding Sources
Finding Sources
Searching PubMed

gallbladder carcinoma fine needle aspiration cytology

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

gallbladder mass FNAC cytology features adenocarcinoma diagnosis criteria 2024

Reading Web Page

https://pmc.ncbi.nlm.nih.gov/articles/PMC6367906

Loading Image
I now have comprehensive information to provide a thorough answer. Let me compile everything.

Gallbladder Mass Cytology / FNAC

Overview

FNAC (Fine Needle Aspiration Cytology) of gallbladder masses is an important pre-operative diagnostic tool, especially in patients with advanced or unresectable disease where histological confirmation guides management. US-guided percutaneous FNAC is the standard approach; EUS-guided FNA is preferred when the mass is not directly accessible or when hepaticobiliary expertise is available.
A 2021 systematic review (MDPI Diagnostics) reported an SROC AUC of 0.98, confirming high diagnostic accuracy with minimal complication rates.

Indications for FNAC

  • Gallbladder mass on imaging (US, CT, or MRI) suspicious for carcinoma
  • Pre-operative tissue diagnosis when surgical resection is not immediately planned
  • Staging workup - suspected metastatic disease
  • Distinguishing primary GBC from metastasis or lymphoma
  • Confirmation before palliative chemotherapy
Note: FNAC is generally avoided when the lesion is potentially resectable with curative intent, to prevent peritoneal seeding.

Technique

MethodDetails
US-guided percutaneous FNACMost common; outpatient; 22-25G needle
EUS-guided FNAPreferred for deep masses; allows concurrent staging; forward-viewing echoendoscope used especially post-Roux-en-Y reconstruction
CT-guidedUsed when US visualization is poor
ERCP-based cell blockFor associated biliary obstruction
Adequacy rate: ~90% (inadequacy ~9.9% in large series). Repeat FNAC recommended if initial is inadequate or atypical, especially with strong clinicoradiological suspicion - 76.9% of re-aspirated cases in one series were positive for adenocarcinoma.

Cytomorphological Classification System (5 Categories)

CategoryDiagnosisCytological Features
IInadequateScant cellularity, degenerated cells, blood/clot only
IIBenign/ReactiveMonolayered sheets, uniform chromatin, small nucleoli, mild atypia
IIIAtypicalHypercellular smears, cellular overlapping, mild pleomorphism, enlarged nuclei, pale cytoplasm, occasional mitosis - equivocal
IVSuspiciousFeatures concerning but not diagnostic for malignancy
VMalignantDefinitive malignant features

FNAC Features of Gallbladder Adenocarcinoma (Most Common - ~87%)

Architectural Patterns

  • Sheets (most common, 76.5%) and acini (71.6%)
  • Papillae (7.1%) - especially in well-differentiated papillary carcinoma
  • Rosettes (1.6%)

Cellular Morphology

  • Columnar cells predominate (39%)
  • Signet ring cells (0.9%) - mucinous variant
  • Squamous cells (6.1%) - adenosquamous variant
  • Small cells (1.6%) - neuroendocrine variant
  • Clear cells, spindle cells, giant cells (rare)

Nuclear Features

FeatureFrequency
Nuclear pleomorphism - moderate47.8%
Nuclear pleomorphism - severe32.8%
Vesicular chromatin59.2%
Coarse/irregular chromatin32.5%
Prominent nucleoliPresent
Mitotic figuresVariable

Background Features

  • Necrotic/inflammatory background
  • Desmoplastic stromal fragments
  • Intracellular mucin (6.1%)
Gallbladder Adenocarcinoma - Gross and Histology
Fig. (A) Opened gallbladder with large exophytic tumor filling the lumen. (B) H&E: Malignant glands infiltrating desmoplastic gallbladder wall stroma. (Robbins Pathology)

WHO Subtypes Diagnosable on FNAC

1. Conventional Adenocarcinoma (most common)

  • Acini and sheets of columnar/rounded cells
  • Vesicular chromatin, prominent nucleoli
  • Inflammatory or necrotic background

2. Papillary Carcinoma

  • Preponderance of true papillae with fibrovascular cores
  • Relatively mild cytological atypia
  • Fewer mitotic figures
  • Better prognosis than conventional adenocarcinoma

3. Mucinous Adenocarcinoma

  • Criterion: >50% extracellular mucin on smear
  • Comprises ~2.5% of all gallbladder carcinomas
  • Poor prognosis

4. Signet Ring Cell Carcinoma

  • Signet ring cells with intracellular mucin displacing nucleus to periphery
  • Often in mucin-rich background

5. Adenosquamous Carcinoma

  • Mixed glandular + atypical squamous cell component
  • More aggressive than pure adenocarcinoma

6. Squamous Cell Carcinoma

  • Atypical squamous cells with keratinization
  • Necrotic/inflammatory background
  • No glandular component
  • Comprises ~2.4% of GBC

7. Small Cell Carcinoma (Neuroendocrine)

  • Hyperchromatic nuclei with nuclear molding
  • Salt-and-pepper chromatin
  • Rosettes help distinguish from small cell carcinoma vs. pure NET
  • Poor prognosis, frequently presents with metastases

8. Undifferentiated Carcinoma (including Spindle & Giant Cell)

  • Pleomorphic spindle cells or bizarre multinucleate giant cells
  • Highly aggressive

Differential Diagnosis on FNAC

ConditionKey Distinguishing Features
Xanthogranulomatous cholecystitis (XGC)Most common mimic; foamy histiocytes, chronic inflammation, no true malignant epithelial atypia; can show reactive atypia - major pitfall
Adenomatous hyperplasia / AdenomaMonolayered sheets, uniform nuclei, small nucleoli, no high-grade atypia
Gallbladder tuberculosisEpithelioid cell granulomas in necrotic background; no carcinoma cells
Metastatic carcinomaClinical history + IHC; CK7/CK20, CDX2, TTF-1 panel helps
Cholangiocarcinoma extending to GBDifficult distinction cytologically; clinical/imaging correlation essential
XGC is the most treacherous mimic - smears can be hypercellular with reactive epithelial atypia. Features favoring malignancy over XGC: severe nuclear pleomorphism, coarse chromatin, high N:C ratio, necrotic diathesis, and true glandular formations.

Ancillary Studies on FNAC Material

  • Immunocytochemistry (ICC): CK7+, CK20 variable, CEA+, CA19-9+, MUC1+, MUC5AC+
  • HER2/ERBB2 (ERBB2): A 2024 study (J Clin Pathol, PMID 37221046) validated ICC and FISH for HER2 overexpression directly on FNA material in advanced GBC - relevant for targeted therapy eligibility
  • Cell block preparation: Allows IHC and molecular studies; ERCP-based cell block can serve as a "one-stop shop" for diagnosis and genomic profiling
  • FISH / Next-generation sequencing: Increasingly performed on FNA material for genomic profiling (EGFR, KRAS, TP53, HER2)

Diagnostic Performance

ParameterValue
Sensitivity~93-97%
Specificity~95-100%
SROC AUC0.98
Inadequacy rate~9.9-11%
Complication rateMinimal (bile leakage rare)

Gross Pathology Context

Per Robbins, Cotran & Kumar Pathologic Basis of Disease:
  • Gallbladder cancers are mainly adenocarcinomas, most often found in the fundus
  • Gross: firm, poorly circumscribed mass OR diffuse infiltration simulating chronic cholecystitis
  • Micro: malignant glands in desmoplastic stroma; perineural and vascular invasion help confirm diagnosis when atypia is minimal
  • Precursors: flat dysplasia/BilIN, intracholecystic papillary tubular neoplasm (ICPN), intestinal metaplasia

Key Risk Factors (for Clinical Correlation)

  • Cholelithiasis (present in 65-95% of cases) - especially stones >1 cm
  • Female sex (F:M = 2-3:1, up to 5:1 in high-incidence regions)
  • Porcelain gallbladder
  • Adenomatous polyps >1 cm
  • Pancreaticobiliary maljunction (AUPBD)
  • Chronic Salmonella carrier state
  • Primary sclerosing cholangitis
  • Lynch syndrome / FAP
  • High incidence regions: North India, South-Central Chile, Pakistan

Management After FNAC

  • Positive/suspicious for malignancy → Refer to HPB surgeon at advanced center for staging and resection planning
  • Inadequate or atypical → Repeat FNAC or proceed to surgery if clinical suspicion is high
  • Benign → Routine laparoscopic cholecystectomy (note: NPV ~71%, so ~30% false-negative rate - clinical judgment still required)

Sources: Robbins, Cotran & Kumar Pathologic Basis of Disease, p. 814 | Sleisenger & Fordtran's GI and Liver Disease | PMC6367906 - FNAC cytomorphological classification | CytoJournal - WHO classification on FNA material | PMID 37221046 (HER2 on FNA)

What are the specific Bethesda-like criteria for gallbladder cytology reporting?

Finding Sources
Searching PubMed

Papanicolaou Society cytopathology pancreatobiliary reporting system gallbladder

Searching the Web

Papanicolaou Society cytopathology reporting system gallbladder biliary 6 categories Bethesda criteria 2023 2024

Reading Web Page

https://www.pathologyoutlines.com/topic/pancreaspapanicolaou…

Searching PubMed

pancreatobiliary cytology reporting system categories risk malignancy PSC guidelines

Reading Web Page

https://www.pathologyoutlines.com/topic/gallbladdercarcinoma…

Searching PubMed

"pancreatobiliary cytology" reporting categories atypical suspicious malignant risk

Searching PubMed

Papanicolaou Society pancreaticobiliary cytology reporting system 2014

Searching the Web

PSC Papanicolaou Society pancreatobiliary cytology reporting system category III atypical IV neoplastic gallbladder criteria ROM

Excellent - I now have comprehensive data on both the PSC 6-tier system AND the newer WHO 7-tier system, with ROM data for each category. I have enough to give a thorough, well-structured answer.

Bethesda-like Reporting Systems for Gallbladder Cytology

There is no gallbladder-specific Bethesda system. Instead, gallbladder FNAC is reported under two internationally recognized pancreatobiliary frameworks:
  1. The Papanicolaou Society of Cytopathology (PSC) System - 6 categories (2014, widely used)
  2. The WHO System for Reporting Pancreaticobiliary Cytopathology - 7 categories (2019/2022, newer)
Both apply to gallbladder masses, bile duct brushings, and pancreatic FNAs under a single unified framework.

System 1: PSC 6-Tier System (The Standard Bethesda-Like Framework)

Published by the Papanicolaou Society of Cytopathology in 2014. Review by Saieg & Pitman (Diagn Cytopathol 2020, PMID 32031332) provides the most comprehensive validation.

Category I - Nondiagnostic

Average ROM: ~21-23% (higher than expected due to sampling bias in mass lesions)
Criteria:
  • Scant or absent epithelial cells
  • Obscuring blood, clot, or necrotic debris making interpretation impossible
  • Pure GI contamination (gastric/duodenal mucosa only, no biliary epithelium)
  • Acellular or near-acellular aspirate from a cystic lesion with no mucinous features
Gallbladder-specific note: Inadequacy rate for GB FNAC is ~9.9-11%. A necrotic center in a large GB mass commonly yields a Category I sample on first pass.
Management: Repeat FNAC with ROSE (Rapid On-Site Evaluation) if available. CT/EUS re-targeting. Do NOT dismiss clinically suspicious mass on single inadequate aspirate.

Category II - Negative for Malignancy

Average ROM: ~2.5-13% (false negative risk is real - NPV ~71%)
Criteria:
  • Adequate cellularity with orderly, monolayered sheets of biliary-type epithelium
  • Uniform nuclear size and shape, smooth nuclear membranes
  • Fine, evenly distributed chromatin
  • Inconspicuous or small nucleoli
  • Abundant pale cytoplasm; preserved cell polarity
  • Background: bile, foam cells, histiocytes, or inflammatory cells without atypia
Gallbladder-specific entities in this category:
  • Chronic cholecystitis
  • Xanthogranulomatous cholecystitis (XGC) - major pitfall; foamy histiocytes + reactive epithelial changes
  • Adenomatous hyperplasia / adenoma
  • Gallbladder tuberculosis (epithelioid granulomas in necrotic background)
  • Cholesterol polyp
Management: Conservative management; laparoscopic cholecystectomy if clinically indicated. Clinical/radiological correlation mandatory given ~30% false-negative rate for GB carcinoma.

Category III - Atypical

Average ROM: 25-74% (widest range - most heterogeneous category)
Criteria (quantitative threshold - not enough for Cat V but not clearly benign):
  • Hypercellular smears with cellular overlapping and crowding
  • Mild to moderate nuclear pleomorphism
  • Enlarged nuclei (2-3x normal biliary epithelial cell nucleus)
  • Pale/vacuolated cytoplasm with increased N:C ratio
  • Occasional mitotic figures
  • Mostly uniform chromatin (vesicular, but not coarse)
  • No clearly malignant architecture (no true 3D clusters, no necrotic diathesis)
Gallbladder-specific pitfalls driving Cat III overcalling:
  • XGC with reactive epithelial atypia (most common mimic)
  • Acute cholecystitis with reparative changes
  • Bile duct epithelium post-stent placement (reactive atypia)
  • High-grade BilIN (biliary intraepithelial neoplasia) sampled from brushings
Management: Repeat FNAC within 1 month, ancillary testing (FISH, IHC, molecular), multidisciplinary review. Surgery if clinicoradiological suspicion is strong.

Category IV - Neoplastic

Subcategory (a) Benign - ROM: 0%
  • Serous cystadenoma of gallbladder (rare)
  • Lymphangioma
Subcategory (b) Other/Uncertain malignant potential - ROM: 14-30%
  • Intracholecystic papillary tubular neoplasm (ICPTN) - the GB equivalent of IPMN
  • Neuroendocrine tumor (NET) - well-differentiated; rosettes, salt-and-pepper chromatin, no necrosis, minimal mitoses
  • Gastrointestinal stromal tumor
  • Any neoplasm where definitive malignant or benign classification cannot be made on cytology alone
Management: Surgical referral for Cat IV(b); conservative follow-up for Cat IV(a) with classic serous features.

Category V - Suspicious for Malignancy

Average ROM: 75-86%
Criteria (present but quantitatively insufficient for Category VI):
  • Hypercellular smear with 3D clusters, pseudoglands, or papillary fragments
  • High N:C ratio, irregular nuclear membranes
  • Coarse or irregularly distributed chromatin
  • Prominent macronucleoli
  • Background necrosis or "dirty" diathesis
  • Qualitatively malignant-looking cells but either too few in number OR partially obscured by blood/necrosis
Gallbladder-specific note: Well-differentiated GB adenocarcinoma with minimal atypia is the classic Category V trap - glands look almost normal but show perineural/lymphovascular invasion on biopsy. These are better reported as suspicious than malignant.
Management: Immediate HPB surgical referral. Treat as malignant for surgical planning purposes.

Category VI - Positive for Malignancy

Average ROM: 87-100%
Criteria (unequivocal malignancy):
For Adenocarcinoma (most common - ~87% of GB carcinomas):
  • Cohesive and discohesive sheets/acini/papillae of atypical columnar cells
  • Moderate to severe nuclear pleomorphism (47-33% of cases respectively)
  • Coarse, irregular chromatin (32.5%) or vesicular chromatin with macronucleoli (59.2%)
  • High N:C ratio; irregular nuclear contours
  • Gland-in-gland or cribriform architecture
  • Necrotic/inflammatory background diathesis
  • Intracellular mucin in mucinous variant
Subtype-specific criteria:
SubtypeKey Cytological Criteria
Papillary carcinomaTrue papillae with fibrovascular cores; mild atypia; fewer mitoses; better prognosis
Mucinous adenocarcinoma>50% extracellular mucin on smear; poor prognosis
Signet ring cellNucleus displaced to periphery by intracytoplasmic mucin globule
AdenosquamousMixed glandular + atypical squamous (keratinized) cells
Squamous cell carcinomaAtypical squamous cells, keratin pearls, necrotic background; no glands
Small cell (NEC)Hyperchromatic nuclei, nuclear molding, salt-and-pepper chromatin, scant cytoplasm, rosettes absent
Undifferentiated (spindle/giant cell)Pleomorphic spindle cells or bizarre multinucleate giant cells; no glandular differentiation
Management: Urgent referral to advanced HPB center for staging CT/MRI + resectability assessment.

System 2: WHO 7-Tier System (2019/2022 - Newer Framework)

The WHO system expands Category IV of the PSC into two separate tiers to better reflect precursor lesion biology:
PSC (6-tier)WHO (7-tier)Key Change
I. Nondiagnostic1. Insufficient/NondiagnosticSame
II. Negative2. Benign/Negative for MalignancySame
III. Atypical3. AtypicalSame
IV. Neoplastic - Benign4. PanN-Low (low-risk/grade neoplasm)BilIN-low, ICPTN low-grade
IV. Neoplastic - Other5. PanN-High (high-risk/grade neoplasm)BilIN-high, ICPTN high-grade
V. Suspicious6. Suspicious for MalignancySame
VI. Positive/Malignant7. MalignantSame
Gallbladder-specific WHO entities:
  • PanN-Low: BilIN-low grade, ICPTN with low-to-intermediate grade dysplasia
  • PanN-High: BilIN-high grade sampled by brushing (NOT high-grade PanIN), ICPTN with high-grade dysplasia
The split is clinically meaningful: PanN-Low ROM is essentially 0% (precursor, conservative management), while PanN-High ROM is ~92% (near-malignant, surgery indicated).

ROM Summary Table (Both Systems)

CategoryPSC NameWHO NameROM (aggregate)Management
INondiagnosticInsufficient/ND~21-23%Repeat FNA
IINegativeBenign/NFM~2.5-13%Conservative/cholecystectomy
IIIAtypicalAtypical25-74%Repeat/molecular/MDT
IV-aNeoplastic: BenignPanN-Low~0-14%Follow-up or surgery
IV-bNeoplastic: OtherPanN-High~27-92%Surgery referral
VSuspiciousSuspicious~75-86%HPB surgical referral
VIPositive/MalignantMalignant~87-100%Urgent HPB surgical referral

Ancillary Tests by Category (Gallbladder-Specific)

CategoryRecommended Ancillary Tests
Cat ICEA + amylase (cystic lesions); re-aspirate with ROSE
Cat IIIFISH (polysomy/deletion), SMAD4 IHC loss, p53 mutation, repeat FNA
Cat IV-bKi-67 (NET grading), Synaptophysin/Chromogranin (NEC), KRAS/GNAS
Cat V/VICK7, CK20, CEA, CA19-9 on cell block; HER2 IHC/FISH (targeted therapy eligibility per PMID 37221046); TP53 sequencing

Key Gallbladder-Specific Caveats

  1. XGC is the #1 pitfall - it can masquerade as Cat III or even Cat V. Foamy histiocytes + reactive epithelial atypia without true malignant architecture should trigger repeat FNA before surgical escalation.
  2. Well-differentiated GB adenocarcinoma is notoriously underdiagnosed on cytology (Cat II or III) because desmoplastic stroma and minimal atypia are not captured on FNA smears.
  3. Negative predictive value is only ~71% - a Category II result does NOT exclude carcinoma in a radiologically suspicious mass.
  4. ROSE (Rapid On-Site Evaluation) significantly reduces Cat I rates and should be used whenever possible.
  5. The PSC system was not designed exclusively for gallbladder - gallbladder-specific ROM data within the framework is more limited than pancreatic data.

Create a quick reference PDF comparing PSC vs WHO reporting systems for gallbladder cytology

Reading File
Running Command

ls /home/daytona/workspace/ 2>/dev/null || echo "workspace empty or not found"

Running Command

mkdir -p /home/daytona/workspace/gb-cytology-pdf && python3 -c "from reportlab.l…

Writing File

~/gb-cytology-pdf/build_pdf.py

#!/usr/bin/env python3
"""
Quick Reference PDF: PSC vs WHO Reporting Systems for Gallbladder Cytology
"""

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

OUTPUT = "/home/daytona/workspace/gb-cytology-pdf/GB_Cytology_PSC_vs_WHO.pdf"

# ── Colour palette ──────────────────────────────────────────────────────────
NAVY     = HexColor("#1B3A5C")
TEAL     = HexColor("#1E7A8C")
AMBER    = HexColor("#D4860B")
LIGHT_BG = HexColor("#EEF4F7")
ROW_ALT  = HexColor("#F5FBFC")
RED_RISK = HexColor("#C0392B")
GREEN_OK = HexColor("#1E8449")
ORANGE   = HexColor("#CA6F1E")
YELLOW   = HexColor("#D4AC0D")
GREY_BG  = HexColor("#F2F3F4")
WHITE    = colors.white
BLACK    = colors.black

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

H_TITLE = ParagraphStyle("htitle",
    fontSize=18, leading=22, textColor=WHITE,
    fontName="Helvetica-Bold", alignment=TA_CENTER)

H1 = ParagraphStyle("h1",
    fontSize=13, leading=16, textColor=NAVY,
    fontName="Helvetica-Bold", spaceAfter=4)

H2 = ParagraphStyle("h2",
    fontSize=10, leading=13, textColor=TEAL,
    fontName="Helvetica-Bold", spaceAfter=2)

BODY = ParagraphStyle("body",
    fontSize=8.5, leading=12, textColor=BLACK,
    fontName="Helvetica", spaceAfter=2)

SMALL = ParagraphStyle("small",
    fontSize=7.5, leading=10, textColor=HexColor("#444444"),
    fontName="Helvetica")

CELL = ParagraphStyle("cell",
    fontSize=8, leading=10.5, textColor=BLACK,
    fontName="Helvetica")

CELL_B = ParagraphStyle("cellb",
    fontSize=8, leading=10.5, textColor=BLACK,
    fontName="Helvetica-Bold")

CELL_W = ParagraphStyle("cellw",
    fontSize=8, leading=10.5, textColor=WHITE,
    fontName="Helvetica-Bold", alignment=TA_CENTER)

FOOT = ParagraphStyle("foot",
    fontSize=7, leading=9, textColor=HexColor("#666666"),
    fontName="Helvetica-Oblique", alignment=TA_CENTER)

W = A4[0]          # page width
MARGIN = 1.5 * cm
COL = W - 2 * MARGIN

def p(text, style=BODY): return Paragraph(text, style)
def sp(h=0.25): return Spacer(1, h * cm)
def hr(): return HRFlowable(width="100%", thickness=0.6, color=TEAL, spaceAfter=6, spaceBefore=6)

# ── Risk badge helper ────────────────────────────────────────────────────────
def rom_color(rom_str):
    """Return background colour for a ROM string."""
    s = rom_str.lower()
    if "0" in s and "%" in s and not any(c.isdigit() and int(c) > 1 for c in s.split("%")[0][-3:]):
        return GREEN_OK
    for val in ["2","3","4","5","6","7","8","9","10","11","12","13"]:
        if s.startswith(val):
            return GREEN_OK
    if any(x in s for x in ["87","88","89","90","91","92","93","94","95","96","97","98","99","100"]):
        return RED_RISK
    if any(x in s for x in ["75","76","77","78","79","80","81","82","83","84","85","86"]):
        return ORANGE
    if any(x in s for x in ["25","26","27","28","29","30","40","44","50","60","62","70","71","72","73","74"]):
        return AMBER
    return TEAL

# ═══════════════════════════════════════════════════════════════════════════
# BUILD DOCUMENT
# ═══════════════════════════════════════════════════════════════════════════
doc = SimpleDocTemplate(
    OUTPUT,
    pagesize=A4,
    leftMargin=MARGIN, rightMargin=MARGIN,
    topMargin=1.2*cm, bottomMargin=1.5*cm,
    title="PSC vs WHO Gallbladder Cytology Quick Reference",
    author="Orris Clinical Reference"
)

story = []

# ── HEADER BANNER ───────────────────────────────────────────────────────────
header_data = [[
    p("Gallbladder Cytology Reporting Systems\nPSC 6-Tier  vs  WHO 7-Tier  —  Quick Reference", H_TITLE)
]]
header_tbl = Table(header_data, colWidths=[COL])
header_tbl.setStyle(TableStyle([
    ("BACKGROUND", (0,0), (-1,-1), NAVY),
    ("ROUNDEDCORNERS", [6, 6, 6, 6]),
    ("TOPPADDING",    (0,0), (-1,-1), 10),
    ("BOTTOMPADDING", (0,0), (-1,-1), 10),
]))
story.append(header_tbl)
story.append(sp(0.35))

# ── SUBTITLE BAR ────────────────────────────────────────────────────────────
sub_data = [[
    p("PSC = Papanicolaou Society of Cytopathology (2014)  |  WHO = World Health Organization Pancreaticobiliary System (2019/2022)  |  ROM = Risk of Malignancy",
      ParagraphStyle("sub", fontSize=7.5, leading=10, textColor=WHITE, fontName="Helvetica", alignment=TA_CENTER))
]]
sub_tbl = Table(sub_data, colWidths=[COL])
sub_tbl.setStyle(TableStyle([
    ("BACKGROUND", (0,0), (-1,-1), TEAL),
    ("TOPPADDING",    (0,0), (-1,-1), 5),
    ("BOTTOMPADDING", (0,0), (-1,-1), 5),
]))
story.append(sub_tbl)
story.append(sp(0.4))

# ════════════════════════════════════════════════════════════════════════════
# SECTION 1: SIDE-BY-SIDE COMPARISON TABLE
# ════════════════════════════════════════════════════════════════════════════
story.append(p("1. Category-by-Category Comparison", H1))
story.append(hr())

# Column widths: PSC#, PSC Name, WHO#, WHO Name, ROM, Management, GB Entities
CW = [COL * x for x in [0.07, 0.16, 0.07, 0.18, 0.12, 0.20, 0.20]]

col_hdr = [
    p("PSC\nCat.", CELL_W),
    p("PSC Name", CELL_W),
    p("WHO\nCat.", CELL_W),
    p("WHO Name", CELL_W),
    p("ROM\n(avg.)", CELL_W),
    p("Management", CELL_W),
    p("GB-Specific Entities", CELL_W),
]

rows = [
    # [PSC#, PSC Name, WHO#, WHO Name, ROM, Management, GB Entities]
    ["I",   "Nondiagnostic",             "1", "Insufficient /\nNondiagnostic",
     "~21–23%",
     "Repeat FNA; use ROSE;\nre-target with EUS/CT",
     "Scant cells; obscuring\nblood/necrosis; cyst\nfluid only"],

    ["II",  "Negative for\nMalignancy",  "2", "Benign /\nNeg. for Malignancy",
     "~2.5–13%",
     "Conservative; laparoscopic\ncholecystectomy if indicated",
     "Chronic cholecystitis;\nXGC*; adenoma;\nGB tuberculosis; cholesterol polyp"],

    ["III", "Atypical",                  "3", "Atypical",
     "25–74%",
     "Repeat FNA; ancillary\ntesting; MDT review;\nsurgery if high suspicion",
     "XGC with reactive\natypia (main pitfall);\nreparative changes\npost-cholecystitis"],

    ["IV-a","Neoplastic:\nBenign",        "4", "PanN-Low\n(low-risk/grade)",
     "0–14%",
     "Follow-up or elective\ncholecystectomy",
     "Serous cystadenoma;\nICPTN† low-grade;\nBilIN‡ low-grade"],

    ["IV-b","Neoplastic:\nOther",         "5", "PanN-High\n(high-risk/grade)",
     "27–92%",
     "Surgical referral;\nHPB consultation",
     "ICPTN high-grade;\nBilIN high-grade;\nNET (Ki-67 grading req'd)"],

    ["V",   "Suspicious for\nMalignancy","6", "Suspicious for\nMalignancy",
     "75–86%",
     "Urgent HPB referral;\ntreat as malignant for\nsurgical planning",
     "Well-diff. adenocarcinoma\n(classic Cat V trap);\npartially obscured\nmalignant cells"],

    ["VI",  "Positive /\nMalignant",     "7", "Malignant",
     "87–100%",
     "Urgent HPB centre;\nstaging CT/MRI;\nresectability assessment",
     "Adenocarcinoma (87%);\nSCC; mucinous; signet\nring; small cell;\nundifferentiated"],
]

table_data = [col_hdr]
for r in rows:
    row = [p(r[0], CELL_B), p(r[1], CELL), p(r[2], CELL_B), p(r[3], CELL),
           p(r[4], CELL_B), p(r[5], CELL), p(r[6], CELL)]
    table_data.append(row)

tbl_style = [
    # Header
    ("BACKGROUND",   (0,0), (-1,0), NAVY),
    ("TEXTCOLOR",    (0,0), (-1,0), WHITE),
    ("FONTNAME",     (0,0), (-1,0), "Helvetica-Bold"),
    ("FONTSIZE",     (0,0), (-1,0), 8),
    ("ALIGN",        (0,0), (-1,0), "CENTER"),
    ("VALIGN",       (0,0), (-1,-1), "TOP"),
    # Borders
    ("INNERGRID",    (0,0), (-1,-1), 0.4, HexColor("#BBBBBB")),
    ("BOX",          (0,0), (-1,-1), 0.8, NAVY),
    # Alternating rows
    ("BACKGROUND",   (0,1), (-1,1), LIGHT_BG),
    ("BACKGROUND",   (0,2), (-1,2), WHITE),
    ("BACKGROUND",   (0,3), (-1,3), LIGHT_BG),
    ("BACKGROUND",   (0,4), (-1,4), WHITE),
    ("BACKGROUND",   (0,5), (-1,5), LIGHT_BG),
    ("BACKGROUND",   (0,6), (-1,6), WHITE),
    ("BACKGROUND",   (0,7), (-1,7), LIGHT_BG),
    # Padding
    ("TOPPADDING",   (0,0), (-1,-1), 4),
    ("BOTTOMPADDING",(0,0), (-1,-1), 4),
    ("LEFTPADDING",  (0,0), (-1,-1), 4),
    ("RIGHTPADDING", (0,0), (-1,-1), 4),
    # Highlight ROM column background by risk
    ("BACKGROUND",   (4,1), (4,1), GREEN_OK),   # Cat I  ROM col
    ("BACKGROUND",   (4,2), (4,2), GREEN_OK),   # Cat II
    ("BACKGROUND",   (4,3), (4,3), AMBER),      # Cat III
    ("BACKGROUND",   (4,4), (4,4), GREEN_OK),   # Cat IV-a
    ("BACKGROUND",   (4,5), (4,5), ORANGE),     # Cat IV-b
    ("BACKGROUND",   (4,6), (4,6), ORANGE),     # Cat V
    ("BACKGROUND",   (4,7), (4,7), RED_RISK),   # Cat VI
    ("TEXTCOLOR",    (4,1), (4,7), WHITE),
    ("ALIGN",        (4,0), (4,-1), "CENTER"),
    ("FONTNAME",     (4,1), (4,7), "Helvetica-Bold"),
]
comp_tbl = Table(table_data, colWidths=CW, repeatRows=1)
comp_tbl.setStyle(TableStyle(tbl_style))
story.append(comp_tbl)

story.append(sp(0.2))
story.append(p(
    "* XGC = Xanthogranulomatous Cholecystitis (most common mimic of malignancy; foamy histiocytes + reactive atypia).  "
    "† ICPTN = Intracholecystic Papillary Tubular Neoplasm (GB equivalent of IPMN).  "
    "‡ BilIN = Biliary Intraepithelial Neoplasia (flat precursor lesion).", SMALL))
story.append(sp(0.5))

# ════════════════════════════════════════════════════════════════════════════
# SECTION 2: KEY CYTOLOGICAL CRITERIA PER CATEGORY
# ════════════════════════════════════════════════════════════════════════════
story.append(p("2. Key Cytological Criteria per Reporting Category (Gallbladder)", H1))
story.append(hr())

criteria = [
    ("Cat I — Nondiagnostic", LIGHT_BG, [
        ("Adequacy failure:", "Scant biliary epithelium; degenerated cells; obscuring blood/necrosis/clot"),
        ("Cystic lesion:", "Acellular fluid with no mucinous features (no mucin, normal CEA, no KRAS/GNAS)"),
        ("Pitfall:", "DO NOT diagnose XGC or chronic cholecystitis from scant cells — call nondiagnostic"),
        ("Action:", "Repeat FNA; ROSE reduces Cat I rate significantly"),
    ]),
    ("Cat II — Negative for Malignancy", LIGHT_BG, [
        ("Architecture:", "Monolayered honeycomb sheets; preserved polarity; flat tissue fragments"),
        ("Nuclear features:", "Uniform size (1–2× RBC); smooth membranes; fine chromatin; small/absent nucleoli"),
        ("Cytoplasm:", "Abundant pale/granular; low N:C ratio; no intracellular mucin"),
        ("Background:", "Bile pigment; foamy histiocytes; occasional inflammatory cells"),
        ("XGC pattern:", "Sheets of foamy macrophages + reactive epithelial atypia WITHOUT high-grade features"),
        ("NPV caution:", "Only ~71% — negative result does NOT exclude carcinoma in suspicious mass"),
    ]),
    ("Cat III — Atypical (Most Heterogeneous)", HexColor("#FEF9E7"), [
        ("Threshold:", "Quantitatively insufficient for Suspicious, but qualitatively beyond benign"),
        ("Architecture:", "Hypercellular; cellular overlapping; mild 3D crowding; no clear glandular formations"),
        ("Nuclear features:", "Mild–moderate pleomorphism; enlarged nuclei (2–3× normal); mostly uniform chromatin"),
        ("Cytoplasm:", "Pale; vacuolated; slightly elevated N:C ratio"),
        ("Mitoses:", "Occasional; not atypical"),
        ("Background:", "May be inflammatory; no necrotic diathesis"),
        ("ROM range:", "25–74% — widest variance across studies; always requires follow-up"),
    ]),
    ("Cat IV-b / PanN-High — Neoplastic (Other)", LIGHT_BG, [
        ("ICPTN (high-grade):", "Papillary clusters with fibrovascular cores; columnar cells; moderate atypia"),
        ("BilIN high-grade:", "Typically from biliary brushings; high-grade dysplasia without invasive features"),
        ("NET:", "Rosettes/trabeculae; salt-and-pepper chromatin; plasmacytoid cells; low mitotic activity; positive Synaptophysin/Chromogranin; Ki-67 determines grade"),
        ("ROM:", "27–92% — overlaps with suspicious; surgical referral usually indicated"),
    ]),
    ("Cat V — Suspicious for Malignancy", HexColor("#FEF0E6"), [
        ("Threshold:", "Malignant-appearing cells present but quantitatively insufficient for definitive diagnosis"),
        ("Architecture:", "3D clusters; pseudoglandular formations; papillary fragments"),
        ("Nuclear features:", "High N:C ratio; irregular nuclear membranes; coarse/clumped chromatin"),
        ("Nucleoli:", "Prominent macronucleoli"),
        ("Background:", "Necrotic diathesis present but obscures cells"),
        ("Classic trap:", "Well-differentiated GB adenocarcinoma — glands look near-normal; classify Suspicious not Cat II"),
    ]),
    ("Cat VI — Positive/Malignant (Unequivocal)", HexColor("#FDEDEC"), [
        ("Adenocarcinoma (87%):", "Sheets/acini/papillae of columnar cells; mod–severe pleomorphism; coarse chromatin; macronucleoli; necrotic background"),
        ("Papillary Ca:", "True papillae with fibrovascular cores; mild atypia; fewer mitoses; better prognosis"),
        ("Mucinous Ca:", ">50% extracellular mucin; poor prognosis; signet ring cells possible"),
        ("SCC:", "Atypical squamous cells; keratin pearls; no glandular component; necrotic background"),
        ("Small cell NEC:", "Nuclear molding; hyperchromatic nuclei; scant cytoplasm; salt-and-pepper chromatin"),
        ("Undifferentiated:", "Pleomorphic spindle/giant cells; no glandular differentiation; extremely aggressive"),
    ]),
]

for title, bg, items in criteria:
    block = []
    block.append(p(title, H2))
    rows_c = [[p(k, CELL_B), p(v, CELL)] for k, v in items]
    inner = Table(rows_c, colWidths=[COL*0.22, COL*0.78])
    inner.setStyle(TableStyle([
        ("BACKGROUND",   (0,0), (-1,-1), bg),
        ("INNERGRID",    (0,0), (-1,-1), 0.3, HexColor("#CCCCCC")),
        ("BOX",          (0,0), (-1,-1), 0.5, TEAL),
        ("TOPPADDING",   (0,0), (-1,-1), 3),
        ("BOTTOMPADDING",(0,0), (-1,-1), 3),
        ("LEFTPADDING",  (0,0), (-1,-1), 5),
        ("RIGHTPADDING", (0,0), (-1,-1), 5),
        ("VALIGN",       (0,0), (-1,-1), "TOP"),
    ]))
    block.append(inner)
    block.append(sp(0.25))
    story.append(KeepTogether(block))

# ════════════════════════════════════════════════════════════════════════════
# SECTION 3: ROM SUMMARY + ANCILLARY TESTS
# ════════════════════════════════════════════════════════════════════════════
story.append(p("3. Risk of Malignancy (ROM) by Study & Ancillary Tests", H1))
story.append(hr())

# ROM table
rom_header = [p("Category", CELL_W), p("Layfield\net al.", CELL_W), p("Sung\net al.", CELL_W),
              p("Hoda\net al.", CELL_W), p("Avg.\nROM", CELL_W), p("Ancillary Tests\n(Gallbladder)", CELL_W)]

rom_rows = [
    ["I — Nondiagnostic",    "21%",  "4.5%",  "7.7%",   "~21–23%",
     "CEA + amylase (cystic); repeat with ROSE"],
    ["II — Negative",        "13%",  "2.5%",  "1.0%",   "~2.5–13%",
     "None routine; IHC if repeat shows atypia"],
    ["III — Atypical",       "74%",  "25.3%", "28.0%",  "25–74%",
     "FISH (polysomy/deletion); SMAD4 IHC; p53 mutation; repeat FNA"],
    ["IV-a — Neo: Benign",   "14%",  "0%",    "0%",     "~0%",
     "CEA cyst fluid; MUC stains"],
    ["IV-b — Neo: Other",    "14%",  "27.1%", "30.3%",  "~27–92%",
     "Synaptophysin/Chromogranin; Ki-67; KRAS/GNAS"],
    ["V — Suspicious",       "82%",  "75%",   "100%",   "75–86%",
     "Cell block IHC; CK7/CK20; CEA; CA19-9"],
    ["VI — Malignant",       "97%",  "87.9%", "100%",   "87–100%",
     "HER2 IHC/FISH (targeted Rx); NGS; TP53; EGFR"],
]

rom_cw = [COL * x for x in [0.24, 0.10, 0.10, 0.10, 0.12, 0.34]]
rom_data = [rom_header]
for i, r in enumerate(rom_rows):
    row = [p(r[0], CELL_B if i in [0,1,2,3,4,5,6] else CELL)]
    for val in r[1:4]:
        row.append(p(val, ParagraphStyle("romv", fontSize=8, leading=10, textColor=WHITE,
                                          fontName="Helvetica-Bold", alignment=TA_CENTER)))
    row.append(p(r[4], ParagraphStyle("romavg", fontSize=8, leading=10, textColor=WHITE,
                                       fontName="Helvetica-Bold", alignment=TA_CENTER)))
    row.append(p(r[5], CELL))
    rom_data.append(row)

rom_bg = [GREEN_OK, GREEN_OK, AMBER, GREEN_OK, ORANGE, ORANGE, RED_RISK]
rom_tbl_style = [
    ("BACKGROUND",   (0,0), (-1,0), NAVY),
    ("TEXTCOLOR",    (0,0), (-1,0), WHITE),
    ("FONTNAME",     (0,0), (-1,0), "Helvetica-Bold"),
    ("FONTSIZE",     (0,0), (-1,0), 8),
    ("ALIGN",        (0,0), (-1,0), "CENTER"),
    ("VALIGN",       (0,0), (-1,-1), "TOP"),
    ("INNERGRID",    (0,0), (-1,-1), 0.4, HexColor("#BBBBBB")),
    ("BOX",          (0,0), (-1,-1), 0.8, NAVY),
    ("TOPPADDING",   (0,0), (-1,-1), 4),
    ("BOTTOMPADDING",(0,0), (-1,-1), 4),
    ("LEFTPADDING",  (0,0), (-1,-1), 4),
    ("RIGHTPADDING", (0,0), (-1,-1), 4),
]
for i, bg in enumerate(rom_bg):
    row_i = i + 1
    for col_j in [1, 2, 3, 4]:
        rom_tbl_style.append(("BACKGROUND", (col_j, row_i), (col_j, row_i), bg))

rom_tbl = Table(rom_data, colWidths=rom_cw, repeatRows=1)
rom_tbl.setStyle(TableStyle(rom_tbl_style))
story.append(rom_tbl)
story.append(sp(0.5))

# ════════════════════════════════════════════════════════════════════════════
# SECTION 4: PITFALLS & CLINICAL PEARLS
# ════════════════════════════════════════════════════════════════════════════
story.append(p("4. Key Pitfalls & Clinical Pearls (Gallbladder-Specific)", H1))
story.append(hr())

pearls = [
    ("⚠ XGC is the #1 mimic",
     "Xanthogranulomatous cholecystitis produces foamy histiocytes + reactive epithelial atypia that can mimic Cat III–V. "
     "Features favouring XGC: monolayered reactive sheets, uniform chromatin, no coarse irregularity, macrophage-predominant background."),
    ("⚠ Well-Diff. Adenocarcinoma Trap",
     "Well-differentiated GB adenocarcinoma may show minimal atypia on FNAC (Cat II or III) because desmoplastic stroma "
     "and perineural invasion are architectural features not sampled by FNA. Always correlate with imaging."),
    ("⚠ Negative NPV ~71%",
     "A Category II (Negative) result does NOT exclude carcinoma in a radiologically suspicious GB mass. "
     "~30% of GB carcinomas are false-negative on FNAC. Clinical + radiological suspicion overrides cytology."),
    ("⚠ Avoid FNA in Resectable Disease",
     "FNAC of a GB mass that is potentially resectable with curative intent carries a risk of peritoneal seeding. "
     "Reserve FNA for advanced/unresectable lesions or when tissue confirmation is needed before palliative chemotherapy."),
    ("✔ Repeat if Category I or III",
     "Inadequate (Cat I) or atypical (Cat III) results should prompt repeat FNA within 1 month, especially with "
     "strong clinicoradiological suspicion. 76.9% of re-aspirated cases in one series were positive for adenocarcinoma."),
    ("✔ ROSE Reduces Cat I Rate",
     "Rapid On-Site Evaluation (ROSE) by a cytopathologist during EUS/US-guided FNA significantly reduces the "
     "nondiagnostic rate and guides needle re-passes to representative areas of the mass."),
    ("✔ HER2 Testing on FNA Material",
     "HER2/ERBB2 overexpression occurs in >60% of GB carcinomas. IHC and FISH can be performed directly on "
     "cell block material from FNA (validated 2024, J Clin Pathol PMID 37221046) — relevant for targeted therapy eligibility."),
    ("✔ WHO vs PSC: Practical Difference",
     "The main practical upgrade of WHO 7-tier over PSC 6-tier is splitting Cat IV into PanN-Low (ROM ~0%, watch) "
     "and PanN-High (ROM ~27–92%, surgery). Both systems use the same Cat I–III and Cat V–VI criteria for GB masses."),
]

pearl_rows = [[p(t, ParagraphStyle("pt", fontSize=8, leading=11, fontName="Helvetica-Bold",
                                    textColor=NAVY if t.startswith("✔") else HexColor("#7B241C"))),
               p(d, CELL)] for t, d in pearls]
pearl_tbl = Table(pearl_rows, colWidths=[COL*0.28, COL*0.72])
pearl_tbl.setStyle(TableStyle([
    ("BACKGROUND",   (0,0), (-1,-1), GREY_BG),
    ("ROWBACKGROUNDS",(0,0), (-1,-1), [WHITE, LIGHT_BG]),
    ("INNERGRID",    (0,0), (-1,-1), 0.3, HexColor("#CCCCCC")),
    ("BOX",          (0,0), (-1,-1), 0.6, NAVY),
    ("VALIGN",       (0,0), (-1,-1), "TOP"),
    ("TOPPADDING",   (0,0), (-1,-1), 5),
    ("BOTTOMPADDING",(0,0), (-1,-1), 5),
    ("LEFTPADDING",  (0,0), (-1,-1), 6),
    ("RIGHTPADDING", (0,0), (-1,-1), 6),
]))
story.append(pearl_tbl)
story.append(sp(0.4))

# ════════════════════════════════════════════════════════════════════════════
# SECTION 5: WHO 7-TIER EXPANDED DETAIL
# ════════════════════════════════════════════════════════════════════════════
story.append(p("5. WHO 7-Tier System — What's New vs. PSC (Gallbladder Focus)", H1))
story.append(hr())

who_data = [
    [p("WHO Category", CELL_W), p("Key Change from PSC", CELL_W), p("GB-Specific Lesions", CELL_W), p("Clinical Impact", CELL_W)]
]
who_rows = [
    ["1. Insufficient/ND", "Same as PSC Cat I", "Same", "No change"],
    ["2. Benign/NFM",       "Same as PSC Cat II", "Same", "No change"],
    ["3. Atypical",         "Same as PSC Cat III; acknowledges wider ROM range (28–61% for bile duct)", "XGC reactive atypia; reparative changes", "MDT + molecular"],
    ["4. PanN-Low\n(PSC IV-a Benign)",
     "Renamed; now includes ALL low-grade neoplasms with formally low ROM (~0–14%)",
     "ICPTN low-grade; BilIN low-grade;\nserous cystadenoma",
     "Conservative follow-up; elective cholecystectomy"],
    ["5. PanN-High\n(PSC IV-b Other)",
     "NEW SEPARATE TIER — high-grade precursors with ROM 27–92%; triggers surgical urgency",
     "ICPTN high-grade; BilIN high-grade;\nNET (grade-dependent)",
     "Surgical referral to HPB centre"],
    ["6. Suspicious",       "Same as PSC Cat V", "Well-diff. adenocarcinoma trap", "Urgent HPB referral"],
    ["7. Malignant",        "Same as PSC Cat VI", "Adenocarcinoma; SCC; NEC; undiff.", "Urgent HPB centre + staging"],
]
for r in who_rows:
    who_data.append([p(r[0], CELL_B), p(r[1], CELL), p(r[2], CELL), p(r[3], CELL)])

who_cw = [COL*x for x in [0.18, 0.28, 0.28, 0.26]]
who_tbl = Table(who_data, colWidths=who_cw, repeatRows=1)
who_tbl.setStyle(TableStyle([
    ("BACKGROUND",   (0,0), (-1,0), NAVY),
    ("TEXTCOLOR",    (0,0), (-1,0), WHITE),
    ("FONTNAME",     (0,0), (-1,0), "Helvetica-Bold"),
    ("FONTSIZE",     (0,0), (-1,0), 8),
    ("ALIGN",        (0,0), (-1,0), "CENTER"),
    ("VALIGN",       (0,0), (-1,-1), "TOP"),
    ("BACKGROUND",   (0,4), (-1,4), HexColor("#FEF9E7")),  # highlight PanN-High
    ("BACKGROUND",   (0,3), (-1,3), LIGHT_BG),
    ("INNERGRID",    (0,0), (-1,-1), 0.4, HexColor("#BBBBBB")),
    ("BOX",          (0,0), (-1,-1), 0.8, NAVY),
    ("TOPPADDING",   (0,0), (-1,-1), 4),
    ("BOTTOMPADDING",(0,0), (-1,-1), 4),
    ("LEFTPADDING",  (0,0), (-1,-1), 5),
    ("RIGHTPADDING", (0,0), (-1,-1), 5),
]))
story.append(who_tbl)
story.append(sp(0.5))

# ── FOOTER ───────────────────────────────────────────────────────────────────
story.append(HRFlowable(width="100%", thickness=0.6, color=TEAL))
story.append(sp(0.1))
story.append(p(
    "Sources: PSC System — Papanicolaou Society of Cytopathology 2014 Guidelines; Saieg & Pitman, Diagn Cytopathol 2020 (PMID 32031332); "
    "Sung et al. Cancer Cytopathol 2020 (PMID 31722125) | WHO System — WHO Pancreaticobiliary Cytopathology 2019/2022; "
    "JCTP 2024 (PMC11969574) | GB Cytology — PMC6367906; CytoJournal WHO Classification on FNA Material; "
    "Verma et al. J Clin Pathol 2024 (PMID 37221046) | Robbins Pathology 10e p.814\n"
    "Quick Reference compiled by Orris Clinical AI · July 2026 · For educational use only. Verify criteria against current institutional guidelines.",
    FOOT))

# ── BUILD PDF ─────────────────────────────────────────────────────────────────
doc.build(story)
print(f"PDF created: {OUTPUT}")
Running Command

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

Running Command

ls -lh /home/daytona/workspace/gb-cytology-pdf/GB_Cytology_PSC_vs_WHO.pdf

Your quick reference PDF is ready:

GB Cytology PSC vs WHO

PDF Document · PDF


What's inside (5 sections, A4, colour-coded):
  1. Category-by-Category Comparison Table - PSC Cat I–VI alongside WHO Cat 1–7, with ROM colour-coded by risk level (green → amber → orange → red), management, and gallbladder-specific entities per tier
  2. Key Cytological Criteria - Detailed per-category cytomorphology tables covering architecture, nuclear features, background, and gallbladder-specific pitfalls for each reporting category
  3. ROM Summary Table - Three landmark studies (Layfield, Sung, Hoda) side-by-side with average ROM and recommended ancillary tests per category (including HER2 FISH for Cat VI)
  4. Pitfalls & Clinical Pearls - 8 colour-flagged callouts covering XGC as #1 mimic, the well-differentiated adenocarcinoma trap, NPV limitations, when to avoid FNA, ROSE, HER2 testing, and the practical PSC vs WHO difference
  5. WHO 7-Tier Expanded Detail - Highlights what changed from PSC, with emphasis on the PanN-High split (the most clinically impactful addition) and gallbladder-specific lesion mapping

Unable to open

Running Command

python3 -c " from pypdf import PdfReader r = PdfReader('/home/daytona/workspace/…

Running Command

cp /home/daytona/workspace/gb-cytology-pdf/GB_Cytology_PSC_vs_WHO.pdf /home/dayt…

The file is valid and 16KB. Try this direct path link:

GB Cytology PSC vs WHO

PDF Document · PDF

If that still doesn't work, the PDF is confirmed valid (4 pages, 16KB). The issue may be a browser/viewer limitation on your end. As an alternative, I can:
  1. Convert to HTML - open directly in any browser, no download needed
  2. Rebuild as a larger, richer PDF using a different renderer (WeasyPrint via HTML)
  3. Export as a PowerPoint (.pptx) slide deck instead
Which would you prefer?

.pptx

Reading File
Reading File
Reading File
Writing File

~/gb-cytology-pdf/build_pptx.js

const pptxgen = require("pptxgenjs");

const pres = new pptxgen();
pres.layout = "LAYOUT_WIDE"; // 13.3" x 7.5"
pres.title = "GB Cytology: PSC vs WHO Reporting Systems";
pres.author = "Orris Clinical Reference";

// ── Palette ──────────────────────────────────────────────────────────────
const NAVY   = "1B3A5C";
const TEAL   = "1E7A8C";
const AMBER  = "D4860B";
const RED    = "C0392B";
const ORANGE = "CA6F1E";
const GREEN  = "1E8449";
const WHITE  = "FFFFFF";
const LGREY  = "EEF4F7";
const MGREY  = "CCCCCC";
const BLACK  = "222222";
const YELLOW = "F9E79F";

// ── Helpers ───────────────────────────────────────────────────────────────
const W = 13.3, H = 7.5;

function titleBar(slide, text, sub = "") {
  // Dark banner top
  slide.addShape(pres.shapes.RECTANGLE, { x: 0, y: 0, w: W, h: 1.1, fill: { color: NAVY }, line: { color: NAVY } });
  slide.addText(text, { x: 0.3, y: 0.1, w: W - 0.6, h: 0.65, fontSize: 22, bold: true, color: WHITE, fontFace: "Calibri", valign: "middle" });
  if (sub) slide.addText(sub, { x: 0.3, y: 0.72, w: W - 0.6, h: 0.35, fontSize: 10, color: "A8D4DF", fontFace: "Calibri", italic: true });
  // Teal accent strip
  slide.addShape(pres.shapes.RECTANGLE, { x: 0, y: 1.1, w: W, h: 0.08, fill: { color: TEAL }, line: { color: TEAL } });
}

function footerBar(slide, page) {
  slide.addShape(pres.shapes.RECTANGLE, { x: 0, y: H - 0.32, w: W, h: 0.32, fill: { color: NAVY }, line: { color: NAVY } });
  slide.addText("PSC vs WHO Gallbladder Cytology Reporting · Orris Clinical Reference · July 2026 · Educational Use Only", {
    x: 0.2, y: H - 0.30, w: W - 1.2, h: 0.28, fontSize: 7, color: "A8D4DF", fontFace: "Calibri"
  });
  slide.addText(`${page}`, { x: W - 0.8, y: H - 0.30, w: 0.6, h: 0.28, fontSize: 8, color: WHITE, align: "right", fontFace: "Calibri" });
}

function romBadge(slide, x, y, w, h, rom, cat) {
  const colors = { green: GREEN, amber: AMBER, orange: ORANGE, red: RED, teal: TEAL };
  let bg = TEAL;
  if (cat <= 2) bg = GREEN;
  else if (cat === 3) bg = AMBER;
  else if (cat === 4) bg = ORANGE;
  else if (cat >= 5) bg = RED;
  slide.addShape(pres.shapes.ROUNDED_RECTANGLE, { x, y, w, h, fill: { color: bg }, line: { color: bg }, rectRadius: 0.06 });
  slide.addText(rom, { x, y, w, h, fontSize: 8, bold: true, color: WHITE, align: "center", valign: "middle", fontFace: "Calibri" });
}

// ════════════════════════════════════════════════════════════════════════
// SLIDE 1 – Title / Overview
// ════════════════════════════════════════════════════════════════════════
{
  const s = pres.addSlide();
  // Full dark bg
  s.addShape(pres.shapes.RECTANGLE, { x: 0, y: 0, w: W, h: H, fill: { color: NAVY }, line: { color: NAVY } });
  // Teal accent side bar
  s.addShape(pres.shapes.RECTANGLE, { x: 0, y: 0, w: 0.18, h: H, fill: { color: TEAL }, line: { color: TEAL } });
  // Title
  s.addText("Gallbladder Cytology", { x: 0.5, y: 1.0, w: 12.3, h: 1.1, fontSize: 40, bold: true, color: WHITE, fontFace: "Calibri", charSpacing: 1 });
  s.addText("Reporting Systems Quick Reference", { x: 0.5, y: 2.0, w: 12.3, h: 0.85, fontSize: 30, color: "A8D4DF", fontFace: "Calibri" });
  // Divider
  s.addShape(pres.shapes.RECTANGLE, { x: 0.5, y: 3.0, w: 8, h: 0.06, fill: { color: TEAL }, line: { color: TEAL } });
  // System labels
  s.addText([
    { text: "PSC  ", options: { bold: true, color: WHITE } },
    { text: "Papanicolaou Society of Cytopathology 6-Tier System (2014)", options: { color: "CCDDEE" } }
  ], { x: 0.5, y: 3.3, w: 12, h: 0.45, fontSize: 13, fontFace: "Calibri" });
  s.addText([
    { text: "WHO  ", options: { bold: true, color: WHITE } },
    { text: "World Health Organization 7-Tier Pancreaticobiliary System (2019/2022)", options: { color: "CCDDEE" } }
  ], { x: 0.5, y: 3.8, w: 12, h: 0.45, fontSize: 13, fontFace: "Calibri" });
  s.addText([
    { text: "ROM  ", options: { bold: true, color: WHITE } },
    { text: "Risk of Malignancy per category", options: { color: "CCDDEE" } }
  ], { x: 0.5, y: 4.3, w: 12, h: 0.45, fontSize: 13, fontFace: "Calibri" });
  // Slides outline
  s.addShape(pres.shapes.RECTANGLE, { x: 0.5, y: 5.1, w: 12.3, h: 0.06, fill: { color: "2E86AB" }, line: { color: "2E86AB" } });
  const topics = ["Slide 2: Category Comparison", "Slide 3: Cytological Criteria", "Slide 4: ROM Data & Ancillary Tests", "Slide 5: Pitfalls & Pearls", "Slide 6: WHO vs PSC — What's New"];
  topics.forEach((t, i) => {
    s.addText(`${t}`, { x: 0.6, y: 5.25 + i * 0.36, w: 11, h: 0.32, fontSize: 11, color: "A8D4DF", fontFace: "Calibri" });
  });
  footerBar(s, "1 / 6");
}

// ════════════════════════════════════════════════════════════════════════
// SLIDE 2 – Side-by-Side Category Comparison
// ════════════════════════════════════════════════════════════════════════
{
  const s = pres.addSlide();
  s.addShape(pres.shapes.RECTANGLE, { x: 0, y: 0, w: W, h: H, fill: { color: "F7FAFC" }, line: { color: "F7FAFC" } });
  titleBar(s, "Category-by-Category Comparison", "PSC 6-Tier  vs  WHO 7-Tier — Gallbladder Mass FNAC");

  // Table header
  const hY = 1.25;
  const cols = [
    { x: 0.15, w: 0.55, label: "PSC" },
    { x: 0.72, w: 1.45, label: "PSC Name" },
    { x: 2.19, w: 0.55, label: "WHO" },
    { x: 2.76, w: 1.65, label: "WHO Name" },
    { x: 4.43, w: 1.05, label: "ROM" },
    { x: 5.50, w: 3.60, label: "Management" },
    { x: 9.12, w: 3.95, label: "GB-Specific Entities" },
  ];
  cols.forEach(c => {
    s.addShape(pres.shapes.RECTANGLE, { x: c.x, y: hY, w: c.w, h: 0.38, fill: { color: NAVY }, line: { color: NAVY } });
    s.addText(c.label, { x: c.x, y: hY, w: c.w, h: 0.38, fontSize: 9, bold: true, color: WHITE, align: "center", valign: "middle", fontFace: "Calibri" });
  });

  const rows = [
    { psc: "I",    pscName: "Nondiagnostic",           who: "1", whoName: "Insufficient / Nondiagnostic",        rom: "~21–23%", romCat: 1, mgmt: "Repeat FNA; ROSE; re-target EUS/CT",                         entities: "Scant cells; obscuring blood/necrosis; acellular cyst fluid",                   bg: "FFFFFF" },
    { psc: "II",   pscName: "Negative for Malignancy", who: "2", whoName: "Benign / Neg. for Malignancy",        rom: "~2.5–13%",romCat: 1, mgmt: "Conservative; laparoscopic cholecystectomy if indicated",       entities: "Chronic cholecystitis; XGC; adenoma; GB TB; cholesterol polyp",                   bg: LGREY },
    { psc: "III",  pscName: "Atypical",                 who: "3", whoName: "Atypical",                            rom: "25–74%",  romCat: 3, mgmt: "Repeat FNA; ancillary tests (FISH/SMAD4); MDT review",          entities: "XGC with reactive atypia (main pitfall); reparative post-cholecystitis",           bg: "FFFFFF" },
    { psc: "IV-a", pscName: "Neoplastic: Benign",       who: "4", whoName: "PanN-Low (low-risk/grade)",          rom: "0–14%",   romCat: 1, mgmt: "Follow-up or elective cholecystectomy",                         entities: "Serous cystadenoma; ICPTN low-grade; BilIN low-grade",                            bg: LGREY },
    { psc: "IV-b", pscName: "Neoplastic: Other",        who: "5", whoName: "PanN-High (high-risk/grade) ★NEW",  rom: "27–92%",  romCat: 4, mgmt: "HPB surgical referral; ancillary testing",                        entities: "ICPTN high-grade; BilIN high-grade; NET (Ki-67 grading)",                         bg: "FFFBE6" },
    { psc: "V",    pscName: "Suspicious for Malignancy",who: "6", whoName: "Suspicious for Malignancy",          rom: "75–86%",  romCat: 4, mgmt: "Urgent HPB referral; treat as malignant for surgical planning",  entities: "Well-diff. adenocarcinoma (Cat V trap); partially obscured cells",                bg: "FFF0E6" },
    { psc: "VI",   pscName: "Positive / Malignant",     who: "7", whoName: "Malignant",                          rom: "87–100%", romCat: 5, mgmt: "Urgent HPB centre; staging CT/MRI; resectability assessment",    entities: "Adenocarcinoma (87%); SCC; mucinous; signet ring; small cell; undifferentiated",  bg: "FDECEA" },
  ];

  const rowH = 0.72;
  rows.forEach((r, i) => {
    const y = hY + 0.38 + i * rowH;
    // Row bg
    s.addShape(pres.shapes.RECTANGLE, { x: 0.15, y, w: W - 0.3, h: rowH - 0.03, fill: { color: r.bg }, line: { color: MGREY, width: 0.4 } });

    s.addText(r.psc,    { x: cols[0].x + 0.02, y, w: cols[0].w - 0.04, h: rowH - 0.03, fontSize: 9, bold: true, color: NAVY,  align: "center", valign: "middle", fontFace: "Calibri" });
    s.addText(r.pscName,{ x: cols[1].x + 0.04, y, w: cols[1].w - 0.08, h: rowH - 0.03, fontSize: 8.5, bold: false, color: BLACK, valign: "middle", fontFace: "Calibri" });
    s.addText(r.who,    { x: cols[2].x + 0.02, y, w: cols[2].w - 0.04, h: rowH - 0.03, fontSize: 9, bold: true, color: TEAL, align: "center", valign: "middle", fontFace: "Calibri" });
    s.addText(r.whoName,{ x: cols[3].x + 0.04, y, w: cols[3].w - 0.08, h: rowH - 0.03, fontSize: 8.5, color: BLACK, valign: "middle", fontFace: "Calibri" });

    // ROM badge
    const romBg = r.romCat <= 1 ? GREEN : r.romCat === 3 ? AMBER : r.romCat === 4 ? ORANGE : RED;
    s.addShape(pres.shapes.ROUNDED_RECTANGLE, { x: cols[4].x + 0.05, y: y + 0.18, w: cols[4].w - 0.10, h: rowH - 0.40, fill: { color: romBg }, line: { color: romBg }, rectRadius: 0.05 });
    s.addText(r.rom, { x: cols[4].x + 0.05, y: y + 0.18, w: cols[4].w - 0.10, h: rowH - 0.40, fontSize: 8, bold: true, color: WHITE, align: "center", valign: "middle", fontFace: "Calibri" });

    s.addText(r.mgmt,   { x: cols[5].x + 0.04, y, w: cols[5].w - 0.08, h: rowH - 0.03, fontSize: 8, color: BLACK, valign: "middle", fontFace: "Calibri" });
    s.addText(r.entities,{x: cols[6].x + 0.04, y, w: cols[6].w - 0.08, h: rowH - 0.03, fontSize: 7.5, color: BLACK, valign: "middle", fontFace: "Calibri" });

    // Vertical grid lines
    [cols[1].x, cols[2].x, cols[3].x, cols[4].x, cols[5].x, cols[6].x].forEach(lx => {
      s.addShape(pres.shapes.RECTANGLE, { x: lx, y, w: 0.01, h: rowH - 0.03, fill: { color: MGREY }, line: { color: MGREY } });
    });
  });

  // Legend
  s.addText("★ NEW = WHO-only category (split from PSC Cat IV)  |  XGC = Xanthogranulomatous Cholecystitis  |  ICPTN = Intracholecystic Papillary Tubular Neoplasm  |  BilIN = Biliary Intraepithelial Neoplasia", {
    x: 0.15, y: H - 0.62, w: W - 0.3, h: 0.28, fontSize: 7, color: "555555", fontFace: "Calibri", italic: true
  });
  footerBar(s, "2 / 6");
}

// ════════════════════════════════════════════════════════════════════════
// SLIDE 3 – Cytological Criteria
// ════════════════════════════════════════════════════════════════════════
{
  const s = pres.addSlide();
  s.addShape(pres.shapes.RECTANGLE, { x: 0, y: 0, w: W, h: H, fill: { color: "F7FAFC" }, line: { color: "F7FAFC" } });
  titleBar(s, "Key Cytological Criteria per Category", "Gallbladder Mass FNAC — Cytomorphological Features");

  // 6 cards, 2 rows × 3 cols
  const cards = [
    {
      cat: "Cat I — Nondiagnostic",  bg: "EAF4F7", border: TEAL,
      items: [
        ["Adequacy failure:", "Scant biliary epithelium; degenerated/lysed cells; obscuring blood, clot or necrotic debris"],
        ["Cystic lesion:", "Acellular fluid with no mucin, normal CEA, no KRAS/GNAS mutations"],
        ["Action:", "Repeat FNA with ROSE; EUS-guided re-targeting to viable tumour edge"],
      ]
    },
    {
      cat: "Cat II — Negative for Malignancy",  bg: "EAF7EE", border: GREEN,
      items: [
        ["Architecture:", "Monolayered honeycomb sheets; flat fragments; preserved polarity"],
        ["Nuclei:", "Uniform; smooth membranes; fine chromatin; small/absent nucleoli; low N:C"],
        ["Background:", "Bile pigment; foamy histiocytes; sparse inflammation"],
        ["⚠ NPV ~71%:", "Negative result does NOT exclude carcinoma in suspicious mass"],
      ]
    },
    {
      cat: "Cat III — Atypical (Widest Variance)",  bg: "FEF9E7", border: AMBER,
      items: [
        ["Threshold:", "Qualitatively beyond benign but quantitatively below Suspicious"],
        ["Nuclear features:", "Mild–moderate pleomorphism; enlarged nuclei (2–3× normal); mostly uniform chromatin"],
        ["Architecture:", "Hypercellular; overlapping; no clear glandular formations; occasional mitoses"],
        ["ROM:", "25–74% — widest range; always requires repeat or MDT review"],
      ]
    },
    {
      cat: "Cat IV-b / PanN-High — Neoplastic: Other",  bg: "FEF0E6", border: ORANGE,
      items: [
        ["ICPTN high-grade:", "Papillary clusters with fibrovascular cores; columnar cells; moderate atypia"],
        ["BilIN high-grade:", "High-grade dysplasia without invasive features (biliary brushings)"],
        ["NET:", "Rosettes/trabeculae; salt-and-pepper chromatin; plasmacytoid; low mitoses; Synaptophysin+"],
        ["ROM:", "27–92% — surgical referral usually indicated"],
      ]
    },
    {
      cat: "Cat V — Suspicious for Malignancy",  bg: "FDE8E8", border: ORANGE,
      items: [
        ["Architecture:", "3D clusters; pseudoglandular formations; papillary fragments"],
        ["Nuclei:", "High N:C; irregular membranes; coarse/clumped chromatin; macronucleoli"],
        ["Background:", "Necrotic diathesis — partially obscures malignant cells"],
        ["Classic trap:", "Well-diff. GB adenocarcinoma: near-normal glands on cytology; classify Suspicious, NOT Cat II"],
      ]
    },
    {
      cat: "Cat VI — Positive / Malignant",  bg: "FDEDEC", border: RED,
      items: [
        ["Adenocarcinoma (87%):", "Sheets/acini/papillae; coarse chromatin; macronucleoli; necrotic background"],
        ["Mucinous Ca:", ">50% extracellular mucin; poor prognosis; signet ring cells possible"],
        ["Small cell NEC:", "Nuclear molding; hyperchromatic; salt-and-pepper chromatin; scant cytoplasm"],
        ["SCC / Undiff.:", "Keratin pearls / bizarre giant or spindle cells; no glandular component"],
      ]
    },
  ];

  const cardW = (W - 0.45) / 3;
  const cardH = 2.7;
  const startY = 1.25;

  cards.forEach((c, i) => {
    const col = i % 3;
    const row = Math.floor(i / 3);
    const x = 0.15 + col * (cardW + 0.075);
    const y = startY + row * (cardH + 0.15);

    s.addShape(pres.shapes.RECTANGLE, { x, y, w: cardW, h: cardH, fill: { color: c.bg }, line: { color: c.border, width: 1.2 }, shadow: { type: "outer", color: "000000", blur: 4, offset: 2, angle: 135, opacity: 0.10 } });
    // Header strip
    s.addShape(pres.shapes.RECTANGLE, { x, y, w: cardW, h: 0.35, fill: { color: c.border }, line: { color: c.border } });
    s.addText(c.cat, { x: x + 0.05, y, w: cardW - 0.1, h: 0.35, fontSize: 8.5, bold: true, color: WHITE, valign: "middle", fontFace: "Calibri" });

    let itemY = y + 0.42;
    c.items.forEach(([label, val]) => {
      s.addText([
        { text: label + " ", options: { bold: true, color: NAVY } },
        { text: val, options: { color: BLACK } }
      ], { x: x + 0.08, y: itemY, w: cardW - 0.16, h: 0.52, fontSize: 7.5, fontFace: "Calibri", valign: "top" });
      itemY += 0.54;
    });
  });

  footerBar(s, "3 / 6");
}

// ════════════════════════════════════════════════════════════════════════
// SLIDE 4 – ROM Data & Ancillary Tests
// ════════════════════════════════════════════════════════════════════════
{
  const s = pres.addSlide();
  s.addShape(pres.shapes.RECTANGLE, { x: 0, y: 0, w: W, h: H, fill: { color: "F7FAFC" }, line: { color: "F7FAFC" } });
  titleBar(s, "Risk of Malignancy (ROM) by Study & Ancillary Tests", "Aggregate data from Layfield et al., Sung et al., and Hoda et al.");

  // ROM Table
  const tblY = 1.3;
  const tblCols = [
    { x: 0.15, w: 2.60, label: "Category" },
    { x: 2.77, w: 1.10, label: "Layfield" },
    { x: 3.89, w: 1.10, label: "Sung" },
    { x: 5.01, w: 1.10, label: "Hoda" },
    { x: 6.13, w: 1.20, label: "Avg. ROM" },
    { x: 7.35, w: 5.75, label: "Ancillary Tests (Gallbladder-Specific)" },
  ];
  // Header
  tblCols.forEach(c => {
    s.addShape(pres.shapes.RECTANGLE, { x: c.x, y: tblY, w: c.w, h: 0.38, fill: { color: NAVY }, line: { color: NAVY } });
    s.addText(c.label, { x: c.x, y: tblY, w: c.w, h: 0.38, fontSize: 9, bold: true, color: WHITE, align: "center", valign: "middle", fontFace: "Calibri" });
  });

  const romRows = [
    { cat: "I — Nondiagnostic",     l: "21%",   su: "4.5%",  h: "7.7%",  avg: "~21–23%", rc: 2, anc: "CEA + amylase (cyst fluid); repeat FNA with ROSE", bg: "FFFFFF" },
    { cat: "II — Negative",          l: "13%",   su: "2.5%",  h: "1.0%",  avg: "~2.5–13%",rc: 1, anc: "None routine; IHC if repeat shows atypia", bg: LGREY },
    { cat: "III — Atypical",         l: "74%",   su: "25.3%", h: "28.0%", avg: "25–74%",  rc: 3, anc: "FISH (polysomy/deletion); SMAD4 IHC loss; p53 mutation analysis; repeat FNA", bg: "FFFFFF" },
    { cat: "IV-a — Neo: Benign",     l: "14%",   su: "0%",    h: "0%",    avg: "~0–14%",  rc: 1, anc: "CEA cyst fluid; MUC stains; consider elective resection", bg: LGREY },
    { cat: "IV-b — Neo: Other",      l: "14%",   su: "27.1%", h: "30.3%", avg: "~27–92%", rc: 4, anc: "Synaptophysin/Chromogranin A; Ki-67 index (NET grading); KRAS/GNAS mutation", bg: "FFFBE6" },
    { cat: "V — Suspicious",         l: "82%",   su: "75%",   h: "100%",  avg: "75–86%",  rc: 4, anc: "Cell block IHC; CK7/CK20; CEA; CA19-9; p53 IHC", bg: "FFF0E6" },
    { cat: "VI — Malignant",         l: "97%",   su: "87.9%", h: "100%",  avg: "87–100%", rc: 5, anc: "HER2 IHC + FISH (targeted Rx eligibility); NGS panel; TP53; EGFR/KRAS", bg: "FDECEA" },
  ];

  const rowH = 0.68;
  romRows.forEach((r, i) => {
    const y = tblY + 0.38 + i * rowH;
    s.addShape(pres.shapes.RECTANGLE, { x: 0.15, y, w: W - 0.3, h: rowH - 0.03, fill: { color: r.bg }, line: { color: MGREY, width: 0.4 } });
    s.addText(r.cat, { x: tblCols[0].x + 0.05, y, w: tblCols[0].w - 0.10, h: rowH - 0.03, fontSize: 8.5, bold: true, color: NAVY, valign: "middle", fontFace: "Calibri" });

    // Coloured ROM value cells
    const romColors = [null, null, null, null]; // neutral for layfield/sung/hoda
    const romBg2 = r.rc <= 1 ? GREEN : r.rc === 3 ? AMBER : r.rc === 4 ? ORANGE : RED;
    [r.l, r.su, r.h].forEach((val, vi) => {
      const col = tblCols[1 + vi];
      s.addText(val, { x: col.x, y, w: col.w, h: rowH - 0.03, fontSize: 9, bold: true, color: romBg2, align: "center", valign: "middle", fontFace: "Calibri" });
    });
    // Avg ROM badge
    const ac = tblCols[4];
    s.addShape(pres.shapes.ROUNDED_RECTANGLE, { x: ac.x + 0.06, y: y + 0.15, w: ac.w - 0.12, h: rowH - 0.35, fill: { color: romBg2 }, line: { color: romBg2 }, rectRadius: 0.05 });
    s.addText(r.avg, { x: ac.x + 0.06, y: y + 0.15, w: ac.w - 0.12, h: rowH - 0.35, fontSize: 8, bold: true, color: WHITE, align: "center", valign: "middle", fontFace: "Calibri" });

    s.addText(r.anc, { x: tblCols[5].x + 0.06, y, w: tblCols[5].w - 0.10, h: rowH - 0.03, fontSize: 8, color: BLACK, valign: "middle", fontFace: "Calibri" });

    // Vertical lines
    [tblCols[1].x, tblCols[2].x, tblCols[3].x, tblCols[4].x, tblCols[5].x].forEach(lx => {
      s.addShape(pres.shapes.RECTANGLE, { x: lx, y, w: 0.01, h: rowH - 0.03, fill: { color: MGREY }, line: { color: MGREY } });
    });
  });

  // Source note
  s.addText("Sources: Layfield et al. (Cancer Cytopathol 2020;128:29) · Sung et al. (Cancer Cytopathol 2020; PMID 31722125) · Hoda et al. · HER2 validation: Verma et al. J Clin Pathol 2024 (PMID 37221046)", {
    x: 0.15, y: H - 0.60, w: W - 0.3, h: 0.26, fontSize: 7, color: "666666", fontFace: "Calibri", italic: true
  });
  footerBar(s, "4 / 6");
}

// ════════════════════════════════════════════════════════════════════════
// SLIDE 5 – Pitfalls & Clinical Pearls
// ════════════════════════════════════════════════════════════════════════
{
  const s = pres.addSlide();
  s.addShape(pres.shapes.RECTANGLE, { x: 0, y: 0, w: W, h: H, fill: { color: "F7FAFC" }, line: { color: "F7FAFC" } });
  titleBar(s, "Pitfalls & Clinical Pearls — Gallbladder-Specific", "Critical points for accurate reporting and patient management");

  const warnings = [
    { icon: "⚠", color: RED,    title: "XGC is the #1 Mimic",                body: "Xanthogranulomatous cholecystitis produces foamy histiocytes + reactive epithelial atypia mimicking Cat III–V. Key features favouring XGC over malignancy: monolayered reactive sheets, uniform chromatin, macrophage-predominant background, no coarse nuclear irregularity." },
    { icon: "⚠", color: RED,    title: "Well-Diff. Adenocarcinoma Trap",      body: "Well-differentiated GB adenocarcinoma may show minimal cytological atypia (reported as Cat II or III) because desmoplastic stroma and perineural invasion are architectural features NOT captured by FNA needle. Always correlate with imaging." },
    { icon: "⚠", color: ORANGE, title: "Negative NPV is Only ~71%",           body: "A Category II (Negative) result does NOT exclude carcinoma in a radiologically suspicious gallbladder mass. Approximately 30% of GB carcinomas are false-negative on FNAC. Clinical + radiological suspicion overrides cytology alone." },
    { icon: "⚠", color: ORANGE, title: "Avoid FNA in Resectable Disease",     body: "FNAC of a potentially resectable GB mass carries a risk of peritoneal seeding. Reserve FNAC for advanced/unresectable lesions where tissue confirmation is needed before palliative chemotherapy or targeted therapy." },
    { icon: "✔", color: GREEN,  title: "Repeat Cat I or Cat III",             body: "Inadequate (Cat I) or atypical (Cat III) results should prompt repeat FNA within 1 month when clinicoradiological suspicion is high. In one series, 76.9% of re-aspirated cases were positive for adenocarcinoma." },
    { icon: "✔", color: GREEN,  title: "ROSE Reduces Inadequacy Rate",        body: "Rapid On-Site Evaluation (ROSE) by a cytopathologist during EUS/US-guided FNA significantly reduces the Cat I nondiagnostic rate and guides needle re-passes to viable tumour areas rather than necrotic centre." },
    { icon: "✔", color: TEAL,   title: "HER2 Testing on FNA Cell Block",      body: "HER2/ERBB2 overexpression in >60% of GB carcinomas. IHC and FISH validated directly on FNA cell block material (Verma et al., J Clin Pathol 2024, PMID 37221046). Relevant for targeted therapy eligibility in advanced/unresectable disease." },
    { icon: "✔", color: TEAL,   title: "WHO vs PSC — Practical Difference",   body: "The main clinical upgrade of WHO 7-tier over PSC 6-tier is splitting Cat IV into PanN-Low (ROM ~0%, conservative) and PanN-High (ROM 27–92%, surgical urgency). Categories I–III and V–VI criteria remain identical between systems." },
  ];

  const cardW2 = (W - 0.45) / 2;
  const cardH2 = 1.30;

  warnings.forEach((w, i) => {
    const col = i % 2;
    const row = Math.floor(i / 2);
    const x = 0.15 + col * (cardW2 + 0.15);
    const y = 1.30 + row * (cardH2 + 0.10);

    s.addShape(pres.shapes.RECTANGLE, { x, y, w: cardW2, h: cardH2, fill: { color: "FFFFFF" }, line: { color: w.color, width: 1.5 }, shadow: { type: "outer", color: "000000", blur: 4, offset: 2, angle: 135, opacity: 0.08 } });
    // Left accent bar
    s.addShape(pres.shapes.RECTANGLE, { x, y, w: 0.15, h: cardH2, fill: { color: w.color }, line: { color: w.color } });
    // Icon
    s.addText(w.icon, { x: x + 0.18, y: y + 0.08, w: 0.4, h: 0.4, fontSize: 16, color: w.color, fontFace: "Calibri", valign: "middle" });
    // Title
    s.addText(w.title, { x: x + 0.55, y: y + 0.08, w: cardW2 - 0.65, h: 0.38, fontSize: 9.5, bold: true, color: w.color, fontFace: "Calibri", valign: "middle" });
    // Body
    s.addText(w.body, { x: x + 0.20, y: y + 0.48, w: cardW2 - 0.30, h: cardH2 - 0.55, fontSize: 8, color: BLACK, fontFace: "Calibri", valign: "top" });
  });

  footerBar(s, "5 / 6");
}

// ════════════════════════════════════════════════════════════════════════
// SLIDE 6 – WHO 7-Tier Expanded + Summary
// ════════════════════════════════════════════════════════════════════════
{
  const s = pres.addSlide();
  s.addShape(pres.shapes.RECTANGLE, { x: 0, y: 0, w: W, h: H, fill: { color: "F7FAFC" }, line: { color: "F7FAFC" } });
  titleBar(s, "WHO 7-Tier System — What's New vs. PSC (Gallbladder Focus)", "Key changes, gallbladder-specific lesion mapping, and clinical impact");

  const whoY = 1.30;
  const whoCols = [
    { x: 0.15, w: 1.85, label: "WHO Category" },
    { x: 2.02, w: 3.20, label: "Key Change from PSC" },
    { x: 5.24, w: 3.80, label: "Gallbladder-Specific Lesions" },
    { x: 9.06, w: 4.05, label: "Clinical Impact" },
  ];
  whoCols.forEach(c => {
    s.addShape(pres.shapes.RECTANGLE, { x: c.x, y: whoY, w: c.w, h: 0.38, fill: { color: TEAL }, line: { color: TEAL } });
    s.addText(c.label, { x: c.x, y: whoY, w: c.w, h: 0.38, fontSize: 9, bold: true, color: WHITE, align: "center", valign: "middle", fontFace: "Calibri" });
  });

  const whoRows = [
    { cat: "1. Insufficient/ND",   bg: "FFFFFF", change: "Same as PSC Cat I — scant/uninterpretable material",                                                     lesions: "Same",                                                                              impact: "Repeat FNA; no change from PSC" },
    { cat: "2. Benign/NFM",        bg: LGREY,    change: "Same as PSC Cat II — normal biliary epithelium",                                                          lesions: "Chronic cholecystitis; XGC; adenoma; GB tuberculosis",                              impact: "Conservative; elective cholecystectomy" },
    { cat: "3. Atypical",          bg: "FFFFFF", change: "Same criteria as PSC Cat III; WHO acknowledges wider ROM range for bile duct (25–61%)",                    lesions: "XGC reactive atypia; reparative changes post-cholecystitis",                        impact: "MDT review; repeat FNA; molecular testing" },
    { cat: "4. PanN-Low\n★ NEW",   bg: "EAF7EE", change: "Replaces PSC Cat IV-a (Benign); explicitly covers ALL low-grade intraductal/cystic neoplasms (ROM ~0–14%)", lesions: "ICPTN low-grade; BilIN low-grade; serous cystadenoma",                             impact: "Follow-up or elective cholecystectomy; no urgency" },
    { cat: "5. PanN-High\n★ NEW",  bg: "FFFBE6", change: "SEPARATE TIER — high-grade precursors split from Cat IV-b; ROM 27–92%; triggers surgical urgency",         lesions: "ICPTN high-grade; BilIN high-grade (biliary brushings); NET grade-dependent",      impact: "Surgical referral to HPB centre; ancillary testing" },
    { cat: "6. Suspicious",        bg: "FFF0E6", change: "Same as PSC Cat V — malignant features present but insufficient for definitive diagnosis",                  lesions: "Well-differentiated adenocarcinoma (classic trap)",                                 impact: "Urgent HPB referral; treat as malignant" },
    { cat: "7. Malignant",         bg: "FDECEA", change: "Same as PSC Cat VI — unequivocal malignant cytology",                                                     lesions: "Adenocarcinoma (87%); SCC; NEC; undifferentiated",                                  impact: "Urgent HPB centre; staging; resectability" },
  ];

  const rH = 0.68;
  whoRows.forEach((r, i) => {
    const y = whoY + 0.38 + i * rH;
    s.addShape(pres.shapes.RECTANGLE, { x: 0.15, y, w: W - 0.3, h: rH - 0.03, fill: { color: r.bg }, line: { color: MGREY, width: 0.4 } });

    const isCatNew = r.cat.includes("NEW");
    s.addText(r.cat, { x: whoCols[0].x + 0.04, y, w: whoCols[0].w - 0.08, h: rH - 0.03, fontSize: 8.5, bold: true, color: isCatNew ? TEAL : NAVY, valign: "middle", fontFace: "Calibri" });
    s.addText(r.change,  { x: whoCols[1].x + 0.05, y, w: whoCols[1].w - 0.10, h: rH - 0.03, fontSize: 8, color: BLACK, valign: "middle", fontFace: "Calibri" });
    s.addText(r.lesions, { x: whoCols[2].x + 0.05, y, w: whoCols[2].w - 0.10, h: rH - 0.03, fontSize: 8, color: BLACK, valign: "middle", fontFace: "Calibri" });
    s.addText(r.impact,  { x: whoCols[3].x + 0.05, y, w: whoCols[3].w - 0.10, h: rH - 0.03, fontSize: 8, color: BLACK, valign: "middle", fontFace: "Calibri" });

    [whoCols[1].x, whoCols[2].x, whoCols[3].x].forEach(lx => {
      s.addShape(pres.shapes.RECTANGLE, { x: lx, y, w: 0.01, h: rH - 0.03, fill: { color: MGREY }, line: { color: MGREY } });
    });
  });

  // Take-home box
  s.addShape(pres.shapes.RECTANGLE, { x: 0.15, y: H - 0.88, w: W - 0.30, h: 0.52, fill: { color: NAVY }, line: { color: NAVY }, shadow: { type: "outer", color: "000000", blur: 4, offset: 2, angle: 135, opacity: 0.12 } });
  s.addText([
    { text: "Take-Home: ", options: { bold: true, color: "A8D4DF" } },
    { text: "PSC and WHO are equivalent for GB masses across Cat I–III and Cat V–VI. The sole major clinical difference is the WHO split of PSC Cat IV into PanN-Low (ROM ~0%, watch) vs PanN-High (ROM 27–92%, surgery). Both systems require clinical + radiological correlation — cytology alone has ~71% NPV for GB carcinoma.", options: { color: WHITE } }
  ], { x: 0.25, y: H - 0.88, w: W - 0.50, h: 0.52, fontSize: 8.5, fontFace: "Calibri", valign: "middle" });

  footerBar(s, "6 / 6");
}

// ── Write file ────────────────────────────────────────────────────────────
pres.writeFile({ fileName: "/home/daytona/workspace/GB_Cytology_PSC_vs_WHO.pptx" })
  .then(() => console.log("PPTX created: /home/daytona/workspace/GB_Cytology_PSC_vs_WHO.pptx"))
  .catch(e => { console.error(e); process.exit(1); });
Running Command

cd /home/daytona/workspace/gb-cytology-pdf && node build_pptx.js 2>&1

Running Command

ls -lh /home/daytona/workspace/GB_Cytology_PSC_vs_WHO.pptx

Your PowerPoint is ready (341 KB, 6 slides):

GB Cytology PSC vs WHO

Presentation · PPTX


What's in each slide:
SlideContent
1Title / overview with systems defined and slide roadmap
2Full side-by-side comparison table — PSC vs WHO, colour-coded ROM badges (green → red), management, and GB-specific entities per category
36 cytological criteria cards (one per reportable category) — architecture, nuclear features, pitfalls, ROM range
4ROM data table with three studies (Layfield, Sung, Hoda) side by side + ancillary tests per category
58 pitfall/pearl cards — warnings (red/orange) and best-practice tips (green/teal)
6WHO 7-tier expanded table + take-home summary banner at the bottom
This is a shared conversation. Sign in to Orris to start your own chat.