Cranial nerve 1 to 12 examination in detail

Reading File
Finding Sources
Finding Sources
Reading File
Reading File
Reading File
Finding Sources
Searching Images

cranial nerve examination clinical

Reading File
Reading File
Loading Image
Loading Image
Here is a detailed, structured examination guide for all 12 cranial nerves, compiled from Harrison's Principles of Internal Medicine 22E, Textbook of Family Medicine 9e, and Goldman-Cecil Medicine.

Cranial Nerve Examination: CN I–XII

Minimum screen: Check fundi, visual fields, pupil size and reactivity, extraocular movements, and facial movements. For a full exam, test all CNs in numerical order (grouping III, IV, VI together).

CN I — Olfactory Nerve

Function: Smell
When to test: Often omitted in routine exams. Indicated when there is suspected inferior frontal lobe disease (e.g., meningioma, head trauma, Parkinson's disease, Alzheimer's disease).
Technique:
  • Ask the patient to close their eyes
  • Occlude one nostril at a time
  • Present a mild, non-irritating odorant — toothpaste, coffee, vanilla, or cloves
  • Ask the patient to identify the smell (identification) and confirm they can detect it (detection)
Abnormal findings:
  • Anosmia — absent smell; unilateral anosmia suggests a focal lesion (frontal meningioma, trauma); bilateral anosmia suggests neurodegenerative disease or post-viral etiology
  • Note: Ammonia tests CN V (trigeminal), not CN I — do not use it

CN II — Optic Nerve

Function: Vision (afferent limb of pupillary light reflex)
Components to test:

1. Visual Acuity

  • Test each eye separately with best correction (glasses/contacts)
  • Use a Snellen chart at 6 m (20 ft) or a near card at 35 cm
  • Record as fraction (20/20, 20/200, etc.)

2. Visual Fields by Confrontation

  • Sit ~0.6–1.0 m (2–3 ft) from the patient
  • Patient fixes gaze on your nose
  • Hold hands at the periphery of your visual field, equidistant between you and the patient
  • Wiggle a finger in each quadrant (inferior then superior) — monocularly and simultaneously
  • Binocular simultaneous testing screens for visual neglect; individual eye testing detects monocular defects
  • Formal perimetry (Humphrey/Goldmann) if an abnormality is found

3. Fundoscopy

Examine with an ophthalmoscope. Note:
  • Optic disc: color (pale = atrophy), margins (blurred = papilledema), cup-to-disc ratio
  • Retinal vessels: arteriovenous nicking, caliber changes, hemorrhages, exudates
  • Retina: pigmentary changes, emboli, macular appearance

4. Pupillary Light Reflex (afferent limb = CN II)

  • Shine a bright light in each eye; assess for briskness of constriction
  • Relative Afferent Pupillary Defect (RAPD / Marcus Gunn pupil): in the swinging flashlight test, the affected eye dilates when light swings to it, indicating optic nerve dysfunction on that side

CN III, IV, VI — Oculomotor, Trochlear, Abducens

Functions:
NerveMuscle(s)Action
CN IIIMedial, superior, inferior rectus; inferior oblique; levator palpebrae; pupillary constrictorElevation, depression, adduction; eyelid elevation; pupil constriction
CN IVSuperior obliqueIntorsion, depression in adduction
CN VILateral rectusAbduction

Pupils

  • Describe size, shape, symmetry (document in mm if possible)
  • Direct reflex: light → same eye constricts (efferent = CN III)
  • Consensual reflex: light → opposite eye constricts
  • Accommodation reflex: ask patient to follow your finger as it moves toward their nose → convergence + miosis + lens thickening
  • Anisocoria: if >1 mm difference and one pupil is larger with ptosis → CN III palsy (parasympathetic compression — compressive lesions affect outer fibers first); if smaller pupil with ptosis and anhidrosis → Horner syndrome
CN III palsy — complete right ptosis with dilated non-reactive pupil revealed on eyelid elevation

Extraocular Movements (EOM)

  • Ask patient to keep head still and track your finger tip
  • Move target in an H-pattern (horizontal, then diagonals)
  • Test each direction: lateral (CN VI), medial (CN III), up-and-out (CN III superior rectus), down-and-out (CN III inferior rectus), up-and-in (CN III inferior oblique), down-and-in (CN IV superior oblique)
  • Observe for: paresis (limitation), nystagmus, saccadic pursuit, diplopia (ask patient)
  • True diplopia resolves on covering one eye
  • Test for horizontal nystagmus at 45° lateral gaze (not extreme gaze); hold position for several seconds

CN V — Trigeminal Nerve

Function: Facial sensation (3 divisions) + motor to muscles of mastication + afferent limb of corneal reflex

Sensory Testing

Test all three divisions on each side of the face:
DivisionTerritory
V1 (Ophthalmic)Forehead, scalp, upper eyelid, cornea
V2 (Maxillary)Cheek, lower eyelid, upper lip, upper teeth
V3 (Mandibular)Lower jaw, lower lip, lower teeth, anterior tongue
  • Test light touch (cotton wisp) and pinprick or temperature (cold tuning fork)
  • Compare left vs. right and between divisions

Corneal Reflex

  • Touch cornea (not conjunctiva) with a fine wisp of cotton or sterile saline
  • Normal: bilateral brisk eye closure
  • Afferent limb = CN V1; efferent limb = CN VII
  • Absent reflex on stimulus side → CN V lesion; absent closure only on stimulus side → CN VII lesion

Motor

  • Ask patient to clench jaw → palpate masseter and temporalis muscles for bulk and symmetry
  • Ask patient to open mouth against resistance
  • With a unilateral lesion, jaw deviates toward the weak (ipsilateral) side on opening

CN VII — Facial Nerve

Function: Muscles of facial expression; taste anterior 2/3 of tongue; innervates lacrimal, sublingual, and submaxillary glands

Motor Testing (Upper and Lower Face)

  • Forehead wrinkling — raise eyebrows
  • Eye closure — close eyes tightly (Bell's phenomenon: upward eye deviation is normal)
  • Cheek puff — puff out both cheeks; press to check for air escape
  • Smile / show teeth — assess symmetry
  • Platysma — grimace, pull mouth corners downward
Key distinction:
PatternLesion
Weakness of lower 2/3 face, forehead sparedUpper motor neuron (UMN) — contralateral cortex/corticobulbar tract
Weakness of entire ipsilateral face (forehead + lower)Lower motor neuron (LMN) — CN VII nucleus or nerve itself (Bell's palsy)

Taste

  • Test anterior 2/3 of tongue on each side separately
  • Apply sweet (sugar) or bitter (quinine) solution with cotton applicator
  • Patient identifies while tongue is extended (to avoid spreading solution)
Lower motor neuron facial palsy — absent forehead wrinkling, lagophthalmos, asymmetric smile, inability to puff cheeks on affected side

CN VIII — Vestibulocochlear Nerve

Function: Hearing (cochlear division) + balance (vestibular division)

Hearing (Cochlear Division)

  • Screening: Rub fingers next to each ear, or whisper a number at 60 cm; test one ear at a time while the other is masked
  • Rinne Test (512 Hz tuning fork):
    • Place vibrating fork on mastoid process (bone conduction) → when no longer heard, place beside the ear canal (air conduction)
    • Normal (Rinne positive): air conduction > bone conduction
    • Conductive hearing loss (Rinne negative): bone conduction > air conduction
    • Sensorineural loss: both diminished but air > bone maintained
  • Weber Test:
    • Place vibrating fork on forehead midline
    • Normal: equal sound in both ears
    • Sound lateralizes to the worse ear → conductive loss on that side
    • Sound lateralizes to the better ear → sensorineural loss on the opposite side
  • Formal audiometry if any abnormality detected

Vestibular Division

  • Not routinely tested in standard exams
  • In dizziness or coma: ice-water caloric stimulation (oculocephalic reflex), Dix-Hallpike maneuver

CN IX — Glossopharyngeal Nerve

Function: Sensation to pharynx and tonsillar fossa; taste to posterior 1/3 of tongue; afferent limb of gag reflex
Testing:
  • Elicit the gag reflex by touching the posterior pharyngeal wall on each side separately with a tongue depressor or blunt probe
  • Observe for symmetric response; may be absent in some normal individuals
  • Taste on posterior 1/3 of tongue is rarely formally tested clinically

CN X — Vagus Nerve

Function: Motor to pharynx, soft palate, larynx; parasympathetic to thoracic and abdominal viscera; efferent limb of gag reflex
Testing:
  • Ask patient to say "aah" — observe soft palate and uvula
  • Normal: both sides of palate elevate symmetrically; uvula remains midline
  • Unilateral vagus lesion: palate drops on the affected side; uvula deviates away from the lesion (toward the normal side)
  • Hoarseness or dysphonia → suggests recurrent laryngeal nerve damage (branch of CN X)
  • Gag reflex efferent arc — brisk bilateral contraction of pharyngeal muscles

CN XI — Spinal Accessory Nerve

Function: Motor to sternocleidomastoid (SCM) and upper trapezius
Testing:
  • Trapezius (shoulder shrug): Ask patient to shrug both shoulders against downward resistance from your hands; compare sides for strength and bulk
  • Sternocleidomastoid (head rotation): Ask patient to turn head to each side against your resisting hand placed on the cheek; the active muscle is contralateral to the direction of rotation (turning left = left SCM contracts on the side you resist)
  • Inspect for muscle atrophy or asymmetry
CN XI intact — patient can elevate the left shoulder; CN XII palsy — tongue deviation with hemiatrophy

CN XII — Hypoglossal Nerve

Function: Motor to all intrinsic and most extrinsic tongue muscles
Testing:
  • Inspect tongue at rest in the floor of the mouth: look for atrophy (hemiatrophy) and fasciculations (LMN lesion)
  • Ask patient to protrude the tongue: midline is normal
  • Tongue deviates toward the side of the lesion (LMN — the weak side cannot push)
  • UMN lesion: tongue deviates away from the lesion (contralateral), no atrophy or fasciculations
  • Test tongue strength: ask patient to press tongue against each inner cheek while you press from outside

Summary Table

CNNameKey FunctionsTest MethodAbnormal Finding
IOlfactorySmellIdentify odorant (eyes closed)Anosmia
IIOpticVision, afferent pupillaryAcuity, fields, fundoscopy, RAPDDecreased acuity, field defects, RAPD
IIIOculomotorEOM (except SO, LR), ptosis, pupil constrictionPupil light reflex, accommodation, H-patternPtosis, mydriasis, "down and out" eye
IVTrochlearSuperior oblique (down + in)H-pattern, head tiltVertical diplopia, worse going down stairs
VTrigeminalFacial sensation, masticationTouch/pin/temp in 3 divisions, jaw clench, corneal reflexHemisensory loss, absent corneal reflex, jaw deviation
VIAbducensLateral rectus (abduction)H-pattern (lateral gaze)Esotropia, failure to abduct
VIIFacialFacial expression, taste ant. 2/3 tongueWrinkle forehead, close eyes, smile, puff cheeks; tasteLMN: entire face weak; UMN: forehead spared
VIIIVestibulocochlearHearing, balanceWhisper test, Rinne, WeberConductive vs. sensorineural loss
IXGlossopharyngealPharyngeal sensation, taste post. 1/3 tongueGag reflex (afferent)Loss of gag (afferent)
XVagusPalate/larynx motor, gag efferent"Aah," uvula, gag reflex (efferent)Uvula deviation, hoarseness, absent gag (efferent)
XISpinal AccessorySCM, trapeziusShoulder shrug, head turn against resistanceWeakness, atrophy of SCM/trapezius
XIIHypoglossalTongue movementProtrude tongue, press against cheeksDeviation toward lesion, atrophy, fasciculations

Sources:
  • Harrison's Principles of Internal Medicine 22E (McGraw Hill, 2025) — CN Examination chapter
  • Textbook of Family Medicine 9e — Cranial Nerve Examination
  • Goldman-Cecil Medicine International Edition — Cranial Nerve & Coma Examination

Create a study guide PDF for cranial nerve testing I can print or share

Reading File
Running Command

ls -la /home/daytona/workspace/ 2>/dev/null || echo "workspace empty or missing"

Running Command

mkdir -p /home/daytona/workspace/cranial-nerve-guide && python3 -c "import reportlab; print('reportlab ok')" && python3 -c "from reportlab.lib.pagesizes import A4; print('A4 ok')"

Writing File

~/cranial-nerve-guide/build_pdf.py

#!/usr/bin/env python3
"""
Cranial Nerve Examination Study Guide PDF Builder
Uses ReportLab to produce a clean, print-ready A4 PDF.
"""

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

OUTPUT = "/home/daytona/workspace/cranial-nerve-guide/Cranial_Nerve_Examination_Study_Guide.pdf"

# ── Colour palette ────────────────────────────────────────────────────────────
NAVY      = colors.HexColor("#1B3A6B")
TEAL      = colors.HexColor("#0A7E8C")
LIGHT_BG  = colors.HexColor("#EAF4F6")
AMBER     = colors.HexColor("#E8A020")
GREY_TEXT = colors.HexColor("#444444")
WHITE     = colors.white
LIGHT_ROW = colors.HexColor("#F5FBFC")
ALT_ROW   = colors.HexColor("#DCEEF2")

# ── Document ──────────────────────────────────────────────────────────────────
doc = SimpleDocTemplate(
    OUTPUT,
    pagesize=A4,
    leftMargin=1.8*cm, rightMargin=1.8*cm,
    topMargin=2*cm, bottomMargin=2*cm,
    title="Cranial Nerve Examination Study Guide",
    author="Orris Medical Reference"
)

W, H = A4
CONTENT_W = W - 3.6*cm   # usable width

styles = getSampleStyleSheet()

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

cover_title = S("CoverTitle",
    fontSize=28, leading=34, alignment=TA_CENTER,
    textColor=WHITE, fontName="Helvetica-Bold")

cover_sub = S("CoverSub",
    fontSize=13, leading=18, alignment=TA_CENTER,
    textColor=colors.HexColor("#C8E8F0"), fontName="Helvetica")

section_head = S("SectionHead",
    fontSize=13, leading=16, spaceBefore=14, spaceAfter=4,
    textColor=WHITE, fontName="Helvetica-Bold",
    backColor=NAVY, borderPad=5,
    leftIndent=0)

cn_head = S("CNHead",
    fontSize=11, leading=14, spaceBefore=10, spaceAfter=3,
    textColor=NAVY, fontName="Helvetica-Bold")

body = S("Body",
    fontSize=9, leading=13, textColor=GREY_TEXT,
    fontName="Helvetica", spaceAfter=2, alignment=TA_JUSTIFY)

bullet = S("Bullet",
    fontSize=9, leading=13, textColor=GREY_TEXT,
    fontName="Helvetica", leftIndent=12, firstLineIndent=-8,
    spaceAfter=2)

label = S("Label",
    fontSize=8.5, leading=12, textColor=TEAL,
    fontName="Helvetica-Bold")

caption = S("Caption",
    fontSize=7.5, leading=11, textColor=colors.HexColor("#666666"),
    fontName="Helvetica-Oblique", alignment=TA_CENTER)

footer_style = S("Footer",
    fontSize=7.5, textColor=colors.HexColor("#888888"),
    fontName="Helvetica", alignment=TA_CENTER)

# ── Helper: coloured box paragraph ───────────────────────────────────────────
def tip_box(text, bg=LIGHT_BG, text_color=NAVY):
    return Table(
        [[Paragraph(text, S("tip", fontSize=8.5, leading=12,
                            textColor=text_color, fontName="Helvetica"))]],
        colWidths=[CONTENT_W],
        style=TableStyle([
            ("BACKGROUND", (0,0), (-1,-1), bg),
            ("TOPPADDING",    (0,0), (-1,-1), 6),
            ("BOTTOMPADDING", (0,0), (-1,-1), 6),
            ("LEFTPADDING",   (0,0), (-1,-1), 10),
            ("RIGHTPADDING",  (0,0), (-1,-1), 10),
            ("BOX", (0,0), (-1,-1), 0.5, TEAL),
        ])
    )

def section_banner(text):
    return Table(
        [[Paragraph(text, section_head)]],
        colWidths=[CONTENT_W],
        style=TableStyle([
            ("BACKGROUND", (0,0), (-1,-1), NAVY),
            ("TOPPADDING",    (0,0), (-1,-1), 6),
            ("BOTTOMPADDING", (0,0), (-1,-1), 6),
            ("LEFTPADDING",   (0,0), (-1,-1), 10),
            ("RIGHTPADDING",  (0,0), (-1,-1), 10),
        ])
    )

def cn_banner(num, name, function_text):
    left = Paragraph(f"<b>CN {num} — {name}</b>", S("cnb_l",
        fontSize=11, leading=14, textColor=WHITE, fontName="Helvetica-Bold"))
    right = Paragraph(f"<i>{function_text}</i>", S("cnb_r",
        fontSize=9, leading=12, textColor=colors.HexColor("#BEE8F0"),
        fontName="Helvetica-Oblique", alignment=TA_LEFT))
    return Table(
        [[left, right]],
        colWidths=[CONTENT_W*0.38, CONTENT_W*0.62],
        style=TableStyle([
            ("BACKGROUND", (0,0), (-1,-1), TEAL),
            ("TOPPADDING",    (0,0), (-1,-1), 5),
            ("BOTTOMPADDING", (0,0), (-1,-1), 5),
            ("LEFTPADDING",   (0,0), (-1,-1), 8),
            ("RIGHTPADDING",  (0,0), (-1,-1), 8),
            ("VALIGN", (0,0), (-1,-1), "MIDDLE"),
        ])
    )

def two_col_table(rows, col_ratio=(0.32, 0.68)):
    w0 = CONTENT_W * col_ratio[0]
    w1 = CONTENT_W * col_ratio[1]
    data = []
    for label_text, value_text in rows:
        data.append([
            Paragraph(f"<b>{label_text}</b>", S("tl", fontSize=8.5, leading=12,
                textColor=NAVY, fontName="Helvetica-Bold")),
            Paragraph(value_text, S("tv", fontSize=8.5, leading=12,
                textColor=GREY_TEXT, fontName="Helvetica"))
        ])
    ts = TableStyle([
        ("BACKGROUND",    (0,0), (-1,-1), LIGHT_ROW),
        ("ROWBACKGROUNDS",(0,0), (-1,-1), [LIGHT_ROW, ALT_ROW]),
        ("TOPPADDING",    (0,0), (-1,-1), 4),
        ("BOTTOMPADDING", (0,0), (-1,-1), 4),
        ("LEFTPADDING",   (0,0), (-1,-1), 6),
        ("RIGHTPADDING",  (0,0), (-1,-1), 6),
        ("VALIGN",        (0,0), (-1,-1), "TOP"),
        ("BOX",           (0,0), (-1,-1), 0.5, colors.HexColor("#BBBBBB")),
        ("LINEBELOW",     (0,0), (-1,-1), 0.3, colors.HexColor("#CCCCCC")),
    ])
    return Table(data, colWidths=[w0, w1], style=ts, hAlign="LEFT")

# ── Page numbering ────────────────────────────────────────────────────────────
def on_page(canvas, doc):
    canvas.saveState()
    # Header bar
    canvas.setFillColor(NAVY)
    canvas.rect(1.8*cm, H - 1.3*cm, CONTENT_W, 0.55*cm, fill=1, stroke=0)
    canvas.setFillColor(WHITE)
    canvas.setFont("Helvetica-Bold", 8)
    canvas.drawString(1.9*cm, H - 1.05*cm, "CRANIAL NERVE EXAMINATION  |  STUDY GUIDE")
    canvas.setFont("Helvetica", 8)
    canvas.drawRightString(1.8*cm + CONTENT_W - 0.1*cm, H - 1.05*cm,
                           f"Page {doc.page}")
    # Footer
    canvas.setFillColor(colors.HexColor("#888888"))
    canvas.setFont("Helvetica", 7)
    canvas.drawCentredString(W/2, 1.3*cm,
        "Sources: Harrison's Principles of Internal Medicine 22E • Textbook of Family Medicine 9e • Goldman-Cecil Medicine")
    canvas.restoreState()

def on_first_page(canvas, doc):
    # Solid navy cover background for page 1
    canvas.saveState()
    canvas.setFillColor(NAVY)
    canvas.rect(0, 0, W, H, fill=1, stroke=0)
    canvas.restoreState()

# ── Build content ─────────────────────────────────────────────────────────────
story = []

# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# COVER PAGE
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
story.append(Spacer(1, 5.5*cm))
story.append(Paragraph("CRANIAL NERVE", cover_title))
story.append(Paragraph("EXAMINATION", cover_title))
story.append(Spacer(1, 0.6*cm))
story.append(Paragraph("Complete Study Guide  •  CN I – XII", cover_sub))
story.append(Spacer(1, 0.4*cm))
story.append(Paragraph("Testing Techniques  •  Normal Findings  •  Abnormal Signs  •  Clinical Pearls", cover_sub))
story.append(Spacer(1, 3.5*cm))

# Decorative accent line
story.append(Table(
    [[""]],
    colWidths=[CONTENT_W],
    style=TableStyle([
        ("LINEABOVE", (0,0), (-1,-1), 2, AMBER),
        ("TOPPADDING", (0,0), (-1,-1), 0),
        ("BOTTOMPADDING", (0,0), (-1,-1), 0),
    ])
))
story.append(Spacer(1, 0.5*cm))
story.append(Paragraph(
    "Based on Harrison's Principles of Internal Medicine 22E,<br/>"
    "Textbook of Family Medicine 9e &amp; Goldman-Cecil Medicine",
    S("src", fontSize=9, leading=13, alignment=TA_CENTER,
      textColor=colors.HexColor("#A0C8D8"), fontName="Helvetica-Oblique")))
story.append(PageBreak())

# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# QUICK REFERENCE TABLE  (page 2)
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
story.append(Spacer(1, 0.3*cm))
story.append(section_banner("QUICK REFERENCE — ALL 12 CRANIAL NERVES"))
story.append(Spacer(1, 0.3*cm))

qr_headers = ["CN", "Name", "Type", "Key Function", "Test Method", "Abnormal Finding"]
qr_col_w = [1.0*cm, 2.8*cm, 1.4*cm, 3.5*cm, 4.3*cm, 4.3*cm]

qr_data = [
    ["I",    "Olfactory",       "Sensory", "Smell",
     "Identify odorant (eyes closed)",
     "Anosmia — unilateral (mass) or bilateral (neurodegen./post-viral)"],
    ["II",   "Optic",           "Sensory", "Vision; afferent pupil reflex",
     "Snellen acuity; confrontation fields; fundoscopy; RAPD",
     "↓ acuity; field defect; papilledema; RAPD"],
    ["III",  "Oculomotor",      "Motor+PS", "EOM (MR,IR,SR,IO); lid; pupil constriction",
     "Pupil light/accommodation; H-pattern EOM",
     "Ptosis; fixed dilated pupil; 'down & out' eye"],
    ["IV",   "Trochlear",       "Motor", "Superior oblique (depression in adduction)",
     "H-pattern; vertical diplopia on downward gaze",
     "Vertical diplopia; head tilt away from lesion"],
    ["V",    "Trigeminal",      "Mixed", "Facial sensation; mastication; corneal afferent",
     "Touch/pin/temp in V1,V2,V3; jaw clench; corneal reflex",
     "Hemisensory loss; absent corneal reflex; jaw deviation"],
    ["VI",   "Abducens",        "Motor", "Lateral rectus (abduction)",
     "Lateral gaze — H-pattern",
     "Esotropia; failure to abduct; horizontal diplopia"],
    ["VII",  "Facial",          "Mixed", "Facial expression; taste ant. 2/3 tongue",
     "Wrinkle forehead; close eyes; smile; puff cheeks; taste",
     "LMN: whole face weak; UMN: forehead spared"],
    ["VIII", "Vestibulocochlear","Sensory", "Hearing; balance",
     "Whisper; Rinne (512 Hz); Weber; finger rub",
     "Conductive vs sensorineural loss; lateralising Weber"],
    ["IX",   "Glossopharyngeal","Mixed", "Pharyngeal sensation; taste post. 1/3 tongue",
     "Gag reflex (afferent arc)",
     "Absent gag (afferent); loss of posterior tongue taste"],
    ["X",    "Vagus",           "Mixed", "Palate/larynx motor; parasympathetics",
     "Say 'Aah' — palate/uvula elevation; voice",
     "Uvula deviates away from lesion; hoarseness"],
    ["XI",   "Spinal Accessory","Motor", "Sternocleidomastoid; trapezius",
     "Shoulder shrug vs resistance; head turn vs resistance",
     "Weakness/wasting of SCM or trapezius"],
    ["XII",  "Hypoglossal",     "Motor", "Tongue movement",
     "Protrude tongue; press tongue against cheek",
     "Tongue deviates toward lesion; atrophy; fasciculations"],
]

# Header row
qr_table_data = [[Paragraph(f"<b>{h}</b>", S("qh", fontSize=8, leading=10,
    textColor=WHITE, fontName="Helvetica-Bold")) for h in qr_headers]]

for i, row in enumerate(qr_data):
    qr_table_data.append([
        Paragraph(f"<b>{row[0]}</b>", S("qcn", fontSize=8.5, leading=11,
            textColor=NAVY, fontName="Helvetica-Bold")),
        Paragraph(f"<b>{row[1]}</b>", S("qn", fontSize=7.5, leading=11,
            textColor=GREY_TEXT, fontName="Helvetica-Bold")),
        Paragraph(row[2], S("qt", fontSize=7, leading=10,
            textColor=colors.HexColor("#666666"), fontName="Helvetica-Oblique")),
        Paragraph(row[3], S("qf", fontSize=7.5, leading=11,
            textColor=GREY_TEXT, fontName="Helvetica")),
        Paragraph(row[4], S("qtm", fontSize=7.5, leading=11,
            textColor=GREY_TEXT, fontName="Helvetica")),
        Paragraph(row[5], S("qa", fontSize=7.5, leading=11,
            textColor=colors.HexColor("#8B1A1A"), fontName="Helvetica")),
    ])

qr_ts = TableStyle([
    ("BACKGROUND",    (0,0), (-1,0),  NAVY),
    ("ROWBACKGROUNDS",(0,1), (-1,-1), [LIGHT_ROW, ALT_ROW]),
    ("TOPPADDING",    (0,0), (-1,-1), 4),
    ("BOTTOMPADDING", (0,0), (-1,-1), 4),
    ("LEFTPADDING",   (0,0), (-1,-1), 4),
    ("RIGHTPADDING",  (0,0), (-1,-1), 4),
    ("VALIGN",        (0,0), (-1,-1), "TOP"),
    ("BOX",           (0,0), (-1,-1), 0.8, NAVY),
    ("LINEBELOW",     (0,0), (-1,-1), 0.3, colors.HexColor("#BBBBBB")),
    ("LINEAFTER",     (0,0), (-1,-1), 0.3, colors.HexColor("#BBBBBB")),
])
qr_table = Table(qr_table_data, colWidths=qr_col_w, style=qr_ts, repeatRows=1)
story.append(qr_table)

# Mnemonic
story.append(Spacer(1, 0.4*cm))
story.append(tip_box(
    "<b>Mnemonic — Type (S=Sensory, M=Motor, B=Both):</b>  "
    "\"<b>Some Say Money Matters But My Brother Says Big Brains Matter More</b>\"<br/>"
    "CN I(S) II(S) III(M) IV(M) V(B) VI(M) VII(B) VIII(S) IX(B) X(B) XI(M) XII(M)",
    bg=colors.HexColor("#FFF8E7"), text_color=colors.HexColor("#5A3800")
))

story.append(PageBreak())

# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# DETAILED CN SECTIONS  (pages 3–7)
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

def sp(n=0.2): return Spacer(1, n*cm)

# ── CN I ─────────────────────────────────────────────────────────────────────
story.append(section_banner("DETAILED CRANIAL NERVE EXAMINATION"))
story.append(sp(0.3))
story.append(cn_banner("I", "Olfactory", "Smell"))
story.append(sp(0.2))
story.append(two_col_table([
    ("When to test",   "Suspected inferior frontal lobe disease, head trauma, or neurodegenerative disease (Parkinson's, Alzheimer's). Often omitted in routine screening."),
    ("Technique",      "Patient closes eyes. Occlude one nostril. Present mild, non-irritant odorant (coffee, toothpaste, vanilla) to the other nostril. Ask patient to identify the smell. Repeat opposite side."),
    ("Normal",         "Patient correctly identifies odorant bilaterally."),
    ("Abnormal",       "Anosmia (absent) or hyposmia (reduced). Unilateral: meningioma, frontal lobe lesion, head trauma. Bilateral: neurodegenerative disease, post-viral (e.g., post-COVID-19)."),
    ("⚠ Caution",      "Do NOT use ammonia — it stimulates CN V (trigeminal pain fibres), not CN I."),
]))
story.append(sp(0.4))

# ── CN II ────────────────────────────────────────────────────────────────────
story.append(cn_banner("II", "Optic", "Vision • Afferent pupillary reflex"))
story.append(sp(0.2))
story.append(two_col_table([
    ("Visual Acuity",    "Test each eye separately with best correction (glasses/contacts). Use Snellen chart at 6 m or near card at 35 cm. Record as fraction (e.g., 20/20, 20/200). If no chart: count fingers (CF), hand motion (HM), light perception (LP)."),
    ("Visual Fields",    "Sit 0.6–1.0 m from patient. Patient fixes on your nose. Hold both hands at periphery of your visual field. Wiggle finger in 4 quadrants (inferior then superior). Test both eyes simultaneously for neglect, then each eye individually. Formal perimetry if defect found."),
    ("Fundoscopy",       "Examine optic disc (color, margins, cup-to-disc ratio), retinal vessels (AV nicking, caliber, hemorrhage, exudates), and macula. Pale disc = optic atrophy. Blurred disc margins = papilledema."),
    ("Pupillary Reflex", "Shine bright light in each eye. Normal: brisk, equal constriction. Swinging flashlight test for RAPD: affected eye dilates when light swings to it (optic nerve lesion)."),
    ("Abnormal",         "↓ Acuity (optic neuritis, glaucoma); bitemporal hemianopia (chiasm — pituitary tumour); homonymous hemianopia (optic tract/radiation); RAPD (MS, optic neuritis); papilledema (↑ICP)."),
]))
story.append(sp(0.4))

# ── CN III, IV, VI ───────────────────────────────────────────────────────────
story.append(cn_banner("III, IV, VI", "Oculomotor • Trochlear • Abducens", "Extraocular movements • Pupil constriction • Eyelid"))
story.append(sp(0.2))
story.append(Paragraph("EOM Muscle Map:", label))
eom_data = [
    [Paragraph("<b>Nerve</b>", S("eh",fontSize=8,textColor=WHITE,fontName="Helvetica-Bold")),
     Paragraph("<b>Muscle(s)</b>", S("eh",fontSize=8,textColor=WHITE,fontName="Helvetica-Bold")),
     Paragraph("<b>Action</b>", S("eh",fontSize=8,textColor=WHITE,fontName="Helvetica-Bold")),
     Paragraph("<b>Test direction</b>", S("eh",fontSize=8,textColor=WHITE,fontName="Helvetica-Bold"))],
    ["CN III", "Medial rectus", "Adduction", "Look IN (nasally)"],
    ["CN III", "Superior rectus", "Elevation in abduction", "Look UP + OUT"],
    ["CN III", "Inferior rectus", "Depression in abduction", "Look DOWN + OUT"],
    ["CN III", "Inferior oblique", "Elevation in adduction", "Look UP + IN"],
    ["CN III", "Levator palpebrae", "Eyelid elevation", "Inspect for ptosis"],
    ["CN III (PS)", "Pupillary constrictor", "Miosis", "Light reflex"],
    ["CN IV", "Superior oblique", "Depression + intorsion in adduction", "Look DOWN + IN"],
    ["CN VI", "Lateral rectus", "Abduction", "Look OUT (laterally)"],
]
eom_ts = TableStyle([
    ("BACKGROUND",    (0,0), (-1,0),  TEAL),
    ("ROWBACKGROUNDS",(0,1), (-1,-1), [LIGHT_ROW, ALT_ROW]),
    ("TOPPADDING",    (0,0), (-1,-1), 3), ("BOTTOMPADDING", (0,0), (-1,-1), 3),
    ("LEFTPADDING",   (0,0), (-1,-1), 5), ("RIGHTPADDING",  (0,0), (-1,-1), 5),
    ("FONTNAME",      (0,1), (-1,-1), "Helvetica"),
    ("FONTSIZE",      (0,1), (-1,-1), 8),
    ("BOX",           (0,0), (-1,-1), 0.5, TEAL),
    ("LINEBELOW",     (0,0), (-1,-1), 0.3, colors.HexColor("#CCCCCC")),
])
story.append(Table(eom_data,
    colWidths=[1.5*cm, 3.5*cm, 4.5*cm, CONTENT_W-9.5*cm],
    style=eom_ts))
story.append(sp(0.2))
story.append(two_col_table([
    ("EOM Testing",     "Ask patient to keep head still. Track your finger slowly in an H-pattern (horizontal then each diagonal). Hold lateral positions for a few seconds. Assess for limitation, nystagmus, or saccadic pursuit. Ask patient to report diplopia in any position — resolves on covering one eye."),
    ("Pupils",          "Describe size in mm and symmetry. Direct reflex: light → same eye constricts (CN III efferent). Consensual: light → opposite eye constricts. Accommodation: convergence + miosis as target approaches nose."),
    ("Nystagmus",       "Test at 45° lateral gaze (not extreme). Hold position ≥3 s. Horizontal nystagmus: jerk direction = fast phase. Vertical nystagmus: central lesion until proven otherwise."),
    ("Abnormal CN III", "Ptosis, fixed dilated pupil (mydriasis), eye deviated 'down and out'. Surgical CN III (compressive): pupil affected FIRST (outer parasympathetic fibres compressed). Medical CN III (diabetic): pupil SPARED."),
    ("Abnormal CN IV",  "Vertical diplopia worse going downstairs. Patient tilts head away from affected side. CN IV palsy = most common cause of acquired vertical diplopia."),
    ("Abnormal CN VI",  "Esotropia (convergent squint). Failure to abduct. Horizontal diplopia maximal on ipsilateral gaze. False localising sign with ↑ICP (long intracranial course)."),
    ("Horner Syndrome", "Miosis + ptosis + anhidrosis (± enophthalmos). Sympathetic chain disruption. Differentiate from CN III: Horner = small pupil; CN III = large pupil."),
]))
story.append(PageBreak())

# ── CN V ─────────────────────────────────────────────────────────────────────
story.append(cn_banner("V", "Trigeminal", "Facial sensation • Mastication • Corneal reflex (afferent)"))
story.append(sp(0.2))
story.append(two_col_table([
    ("Divisions",       "V1 Ophthalmic: forehead, upper eyelid, scalp to vertex, cornea, nasal tip. V2 Maxillary: cheek, lower eyelid, upper lip, upper teeth, nasal ala. V3 Mandibular: lower jaw, lower lip, lower teeth, anterior 2/3 tongue (touch only — not taste)."),
    ("Sensory Testing", "Apply light touch (cotton wisp) and pinprick (or cold tuning fork for temperature) in all three divisions on each side. Compare left vs right and across divisions. Ask if sensation feels equal."),
    ("Corneal Reflex",  "Touch cornea (not conjunctiva) with fine cotton wisp or sterile saline. Normal: bilateral brisk eye closure. Afferent = CN V1; efferent = CN VII. Absent on stimulus side only: CN V lesion. Absent closure ipsilaterally only (sensation intact): CN VII lesion."),
    ("Motor (V3)",      "Ask patient to clench jaw. Palpate masseter and temporalis for bulk and symmetry. Ask patient to open mouth against resistance — jaw deviates toward the weak side (ipsilateral to lesion)."),
    ("Jaw Jerk Reflex", "Tap chin (lower jaw) gently with patient's mouth slightly open. Normal: minimal or absent. Exaggerated jaw jerk = bilateral UMN lesion above the pons (pseudobulbar palsy)."),
    ("Abnormal",        "Unilateral facial numbness (tumour, MS, stroke). Trigeminal neuralgia: lancinating pain V2/V3 distribution, triggered by touch. Absent corneal reflex: CN V or CN VII lesion, or corneal disease."),
]))
story.append(sp(0.4))

# ── CN VII ───────────────────────────────────────────────────────────────────
story.append(cn_banner("VII", "Facial", "Facial expression • Taste ant. 2/3 tongue • Lacrimal/salivary glands"))
story.append(sp(0.2))
story.append(two_col_table([
    ("Motor Testing",   "1. Raise eyebrows / wrinkle forehead. 2. Close eyes tightly (try to open them). Bell's phenomenon: normal upward globe rotation. 3. Puff out both cheeks — press to check air escape. 4. Show teeth / smile — assess symmetry. 5. Whistle or purse lips."),
    ("UMN vs LMN",      "UMN lesion (contralateral cortex): lower 2/3 face weak, forehead SPARED (bilateral cortical representation). LMN lesion (ipsilateral nucleus or nerve): ENTIRE face weak including forehead — Bell's palsy, parotid tumour, skull base fracture."),
    ("Taste",           "Test anterior 2/3 of tongue on each side with tongue extended (prevents contralateral spread). Apply sweet (sugar) or bitter (quinine) solution with cotton applicator. Patient identifies taste."),
    ("Other functions", "Hyperacusis: nerve to stapedius (in middle ear). Decreased lacrimation: greater petrosal nerve branch. Loss of taste + hyperacusis: lesion at geniculate ganglion or proximal to it."),
    ("Abnormal",        "Bell's palsy (LMN, idiopathic), Ramsay Hunt syndrome (VZV at geniculate ganglion — vesicles in ear), parotid tumour, stroke (UMN), MS, Lyme disease, sarcoidosis."),
]))
story.append(sp(0.4))

# ── CN VIII ──────────────────────────────────────────────────────────────────
story.append(cn_banner("VIII", "Vestibulocochlear", "Hearing (cochlear) • Balance (vestibular)"))
story.append(sp(0.2))
story.append(two_col_table([
    ("Screening",      "Rub fingers next to each ear or whisper a 2-syllable word (e.g., 'baseball') at 60 cm. Ask patient to repeat. Mask other ear by rubbing tragus. Test each side separately."),
    ("Rinne Test",     "Use 512 Hz tuning fork. Strike fork, place on mastoid (bone conduction — BC). When no longer heard, move to ear canal entrance (air conduction — AC). Rinne POSITIVE (normal): AC > BC. Rinne NEGATIVE: BC > AC = conductive loss on that side. Sensorineural loss: both reduced but AC still > BC."),
    ("Weber Test",     "Place vibrating 512 Hz fork on forehead midline. Normal: equal in both ears (or 'centre'). Lateralises to WORSE ear: conductive loss on that side (bone conducts better without air competition). Lateralises to BETTER ear: sensorineural loss on the opposite side."),
    ("Vestibular",     "Not routinely tested. In dizziness: Dix-Hallpike maneuver (BPPV). In coma: ice-water caloric (oculocephalic reflex). Head impulse test for semicircular canal function."),
    ("Abnormal",       "Conductive loss: wax, otitis media, otosclerosis. Sensorineural: presbycusis, acoustic neuroma (CN VIII schwannoma — also causes tinnitus and loss of corneal reflex), noise trauma, ototoxic drugs (aminoglycosides, cisplatin)."),
]))
story.append(PageBreak())

# ── CN IX & X ────────────────────────────────────────────────────────────────
story.append(cn_banner("IX & X", "Glossopharyngeal & Vagus", "Pharynx, palate, larynx • Gag reflex • Parasympathetics"))
story.append(sp(0.2))
story.append(two_col_table([
    ("Palate / Uvula",   "Ask patient to say 'Aah'. Observe: symmetric elevation of soft palate; uvula remains MIDLINE. Unilateral vagal lesion: palate drops on AFFECTED side; uvula deviates AWAY from lesion (toward normal side)."),
    ("Gag Reflex",       "Afferent = CN IX (posterior pharyngeal sensation). Efferent = CN X (pharyngeal contraction). Touch posterior pharyngeal wall on each side with tongue blade. Normal: brisk bilateral contraction of pharynx. May be absent in 20% of normal individuals — absence alone is not pathological."),
    ("CN IX specifics",  "Sensation to posterior 1/3 of tongue and pharynx. Taste to posterior 1/3 of tongue. Glossopharyngeal neuralgia: severe pain in posterior tongue, tonsillar fossa, ear — triggered by swallowing."),
    ("CN X specifics",   "Motor to pharynx, soft palate, larynx. Hoarseness or dysphonia: recurrent laryngeal nerve injury (left RLN at aortic arch, right RLN at subclavian artery). Parasympathetic: heart, lungs, gut to splenic flexure."),
    ("Abnormal",         "Bulbar palsy (LMN CN IX/X/XII): nasal dysphonia, dysphagia, pooling of secretions, absent gag. Pseudobulbar palsy (UMN bilateral): exaggerated gag, emotional lability. Jugular foramen syndrome: CN IX, X, XI palsy together."),
]))
story.append(sp(0.4))

# ── CN XI ────────────────────────────────────────────────────────────────────
story.append(cn_banner("XI", "Spinal Accessory", "Sternocleidomastoid • Trapezius"))
story.append(sp(0.2))
story.append(two_col_table([
    ("Trapezius test",       "Stand behind patient. Place hands on both shoulders. Ask patient to shrug upward against downward resistance. Compare strength and bulk bilaterally."),
    ("SCM test",             "Place your hand on patient's cheek. Ask them to turn their head to that side against resistance. The active SCM is on the SAME side as head rotation (contralateral to the direction faced). Test both sides."),
    ("Inspection",           "Inspect for wasting or asymmetry of trapezius (shoulder droop) and SCM."),
    ("Cortical representation", "Each SCM receives ipsilateral cortical input (unusual — most cranial nerve motor nuclei receive contralateral input). Therefore UMN lesion (e.g., stroke) causes weakness of the IPSILATERAL SCM and CONTRALATERAL trapezius."),
    ("Abnormal",             "Neck dissection (surgical injury). Jugular foramen lesions. Shoulder drooping, inability to turn head. Accessory nerve mononeuropathy."),
]))
story.append(sp(0.4))

# ── CN XII ───────────────────────────────────────────────────────────────────
story.append(cn_banner("XII", "Hypoglossal", "Tongue movement — all intrinsic + most extrinsic tongue muscles"))
story.append(sp(0.2))
story.append(two_col_table([
    ("Inspection at rest",  "Inspect tongue in floor of mouth (before protrusion). Look for: atrophy (hemiatrophy = LMN lesion), fasciculations (LMN — best seen at rest on floor of mouth)."),
    ("Protrusion",          "Ask patient to 'stick out tongue'. LMN lesion: tongue deviates TOWARD the lesion (weak side cannot push). UMN lesion: tongue deviates AWAY from lesion (contralateral), no atrophy or fasciculations."),
    ("Strength",            "Ask patient to press tongue against inner cheek on each side while you press from outside. Compare resistance bilaterally."),
    ("Rapid movements",     "Ask patient to wag tongue rapidly side to side — tests speed and coordination of tongue movement (dysarthria assessment)."),
    ("Abnormal",            "LMN (nucleus/nerve): atrophy, fasciculations, tongue deviation toward lesion — motor neuron disease, hypoglossal canal tumour, trauma. UMN (bilateral, above nucleus): pseudobulbar palsy, spastic tongue, no atrophy. Tongue weakness → dysarthria, dysphagia."),
]))
story.append(sp(0.5))

# ── CLINICAL PEARLS ──────────────────────────────────────────────────────────
story.append(PageBreak())
story.append(section_banner("CLINICAL PEARLS & COMMON EXAM TRAPS"))
story.append(sp(0.3))

pearls = [
    ("CN III: Surgical vs Medical palsy",
     "Compressive (surgical) CN III palsy — e.g., posterior communicating artery aneurysm — affects pupil FIRST (parasympathetic fibres on outside of nerve). Pupil is dilated and unreactive. Diabetic (medical) CN III palsy spares the pupil because small vessel ischaemia affects the core motor fibres. Key rule: CN III palsy + dilated pupil = aneurysm until proven otherwise."),
    ("CN VI: False localising sign",
     "CN VI has the longest intracranial course. Raised ICP can stretch CN VI causing a lateral rectus palsy that does NOT indicate a CN VI-territory lesion. Always consider ↑ICP when CN VI palsy lacks other localising signs."),
    ("UMN vs LMN Facial Palsy",
     "UMN (cortical stroke): forehead spared, lower face weak. LMN (Bell's palsy, parotid): entire ipsilateral face weak, forehead involved. Corneal reflex may be lost in LMN. Test forehead wrinkling FIRST — it is the key discriminating feature."),
    ("RAPD (Marcus Gunn Pupil)",
     "In the swinging flashlight test, the affected eye DILATES when light swings to it. Both pupils are equal in size; the reflex is what changes. Indicates unilateral optic nerve or severe retinal disease. Absent in cortical blindness (post-chiasmal) — pupils remain equal and reactive."),
    ("Tongue Deviation Direction",
     "LMN hypoglossal palsy: tongue deviates TOWARD the lesion. UMN hypoglossal palsy: tongue deviates AWAY from the lesion (toward the contralateral weak side). Atrophy and fasciculations = LMN only."),
    ("Rinne + Weber Combined Interpretation",
     "To determine which ear is pathological: Rinne negative (BC > AC) + Weber lateralises to SAME side = CONDUCTIVE loss on Rinne-negative side. Rinne positive (AC > BC) but reduced + Weber lateralises to OPPOSITE side = SENSORINEURAL loss on that side."),
    ("Gag Reflex Absence",
     "The gag reflex is absent in up to 20% of normal adults. Its absence alone is NOT pathological. The key is asymmetry (one side absent, one intact) or complete absence in the context of other lower cranial nerve signs."),
    ("Corneal Reflex Clinical Use",
     "Useful in comatose patients to assess CN V and CN VII integrity. Also an early, sensitive sign of ipsilateral CN V lesion. Contact lens wearers may have reduced corneal sensitivity — ask beforehand."),
]

for title_text, detail_text in pearls:
    story.append(KeepTogether([
        Paragraph(f"▸  {title_text}", S("pt", fontSize=9.5, leading=13,
            textColor=NAVY, fontName="Helvetica-Bold", spaceBefore=8, spaceAfter=2)),
        Paragraph(detail_text, S("pd", fontSize=8.5, leading=13,
            textColor=GREY_TEXT, fontName="Helvetica",
            leftIndent=14, spaceAfter=4)),
        HRFlowable(width="100%", thickness=0.3, color=colors.HexColor("#CCCCCC"),
                   spaceAfter=2)
    ]))

story.append(sp(0.4))

# ── EXAMINATION SEQUENCE CHECKLIST ───────────────────────────────────────────
story.append(section_banner("BEDSIDE EXAMINATION SEQUENCE CHECKLIST"))
story.append(sp(0.2))

checklist = [
    ("□ CN I",   "Smell test with mild odorant (if indicated)"),
    ("□ CN II",  "Visual acuity (Snellen / near card)"),
    ("□ CN II",  "Visual fields — confrontation (bilateral then monocular)"),
    ("□ CN II",  "Fundoscopy — disc, vessels, macula"),
    ("□ CN II+III", "Pupil inspection (size, shape, symmetry)"),
    ("□ CN II+III", "Direct and consensual light reflex"),
    ("□ CN III", "Accommodation reflex (convergence + miosis)"),
    ("□ CN III+IV+VI", "Extraocular movements — H-pattern"),
    ("□ CN III+IV+VI", "Nystagmus at 45° lateral gaze"),
    ("□ CN V",   "Facial sensation — light touch and pin/temp, all 3 divisions"),
    ("□ CN V+VII", "Corneal reflex"),
    ("□ CN V",   "Jaw clench (masseter/temporalis) — motor V3"),
    ("□ CN VII", "Raise eyebrows / wrinkle forehead"),
    ("□ CN VII", "Close eyes tightly"),
    ("□ CN VII", "Puff cheeks / smile / show teeth"),
    ("□ CN VII", "Taste — anterior 2/3 tongue (if indicated)"),
    ("□ CN VIII","Whisper test / finger rub — each ear"),
    ("□ CN VIII","Rinne test (512 Hz tuning fork)"),
    ("□ CN VIII","Weber test (512 Hz tuning fork)"),
    ("□ CN IX+X","Say 'Aah' — palate elevation, uvula position"),
    ("□ CN IX+X","Gag reflex — both sides"),
    ("□ CN XI",  "Shoulder shrug against resistance (trapezius)"),
    ("□ CN XI",  "Head rotation against resistance (SCM)"),
    ("□ CN XII", "Inspect tongue at rest (atrophy, fasciculations)"),
    ("□ CN XII", "Protrude tongue — note deviation"),
    ("□ CN XII", "Tongue strength vs cheek"),
]

chk_data = [[
    Paragraph(cn_t, S("ck", fontSize=8.5, leading=12, textColor=TEAL,
        fontName="Helvetica-Bold")),
    Paragraph(test_t, S("ck2", fontSize=8.5, leading=12, textColor=GREY_TEXT,
        fontName="Helvetica"))
] for cn_t, test_t in checklist]

chk_ts = TableStyle([
    ("ROWBACKGROUNDS",(0,0), (-1,-1), [WHITE, colors.HexColor("#F3F9FA")]),
    ("TOPPADDING",    (0,0), (-1,-1), 3),
    ("BOTTOMPADDING", (0,0), (-1,-1), 3),
    ("LEFTPADDING",   (0,0), (-1,-1), 5),
    ("RIGHTPADDING",  (0,0), (-1,-1), 5),
    ("LINEBELOW",     (0,0), (-1,-1), 0.2, colors.HexColor("#CCCCCC")),
    ("BOX",           (0,0), (-1,-1), 0.5, colors.HexColor("#AAAAAA")),
])
story.append(Table(chk_data, colWidths=[2.5*cm, CONTENT_W-2.5*cm], style=chk_ts))

story.append(sp(0.5))
story.append(tip_box(
    "<b>Minimum screening exam (Harrison's):</b> Check the fundi, visual fields, "
    "pupil size and reactivity, extraocular movements, and facial movements. "
    "Examine CNs in numerical order, grouping CN III, IV, VI together.",
    bg=LIGHT_BG, text_color=NAVY
))

# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# BUILD
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
doc.build(story, onFirstPage=on_first_page, onLaterPages=on_page)
print(f"PDF created: {OUTPUT}")
Running Command

cd /home/daytona/workspace/cranial-nerve-guide && python3 build_pdf.py 2>&1

Running Command

ls -lh /home/daytona/workspace/cranial-nerve-guide/Cranial_Nerve_Examination_Study_Guide.pdf

Your credits are exhausted. Your daily credits reset tomorrow, or you can purchase a top-up for more.
This is a shared conversation. Sign in to Orris to start your own chat.