CNS examination in detail for MD medicine resistant based on standards book

Reading File
Finding Sources
Finding Sources
Finding Sources
Finding Sources
Finding Sources
Finding Sources
Reading File
Reading File
Reading File
Reading File
Reading File
Finding Sources
Reading File
Reading File
Reading File
Reading File
Reading File
Reading File
I now have comprehensive content. Let me compile the full detailed CNS examination answer.

CNS Examination in Detail

Based on: Adams and Victor's Principles of Neurology, 12th Edition; Bradley and Daroff's Neurology in Clinical Practice; Harrison's Principles of Internal Medicine 22E

Overview and Approach

The neurological examination begins before the patient reaches the examination table - observations during the waiting room, the walk to the room, and history-taking all yield diagnostic information. Lack of facial expression (hypomimia) may suggest parkinsonism; a worried or astonished expression may suggest progressive supranuclear palsy; ptosis suggests myasthenia gravis or a brainstem lesion. Speech patterns may reveal dysarthria, aphasia, or spasmodic dysphonia.
The full neurological examination systematically covers every CNS region, peripheral nerve, muscle, sensory modality, and reflex. In practice, a screening examination is combined with a focused examination directed by the history.

I. MENTAL STATUS EXAMINATION

A. Level of Consciousness and Attention

The first and most fundamental assessment. Graded as:
  • Alert - fully awake and responsive
  • Drowsy/Somnolent - arousable by verbal stimuli
  • Stupor - arousable only by vigorous/painful stimuli
  • Coma - unarousable
Bedside attention tests:
  • Digit Span Test - Forward: read digits at 1/sec, patient repeats; Normal = 7 ± 2 digits. Backward: Normal = 5 ± 1
  • Serial Subtraction - Subtract 7s from 100 (100, 93, 86...) or 3s; tests concentration
  • "A" Vigilance Test - Tap each time letter "A" is heard in a random letter sequence
  • Palm-Side-Fist sequence - Three-step motor sequence (frontal lobe function)

B. Orientation

Test orientation to:
  • Time: Date, day, month, year, time of day (off by >3 days on date or >4 hours on time = significantly disoriented)
  • Place: Hospital/clinic, type of place, city
  • Person: Own name (rarely lost except in severe dementia)

C. Memory

TypeTest
Immediate recallRepeat 3 words immediately
Short-term (recent) memoryRecall 3 words after 5 minutes
Remote memoryHistorical facts, past personal events
EpisodicRecall of specific dated personal events
Also test: Registration of examiner's name; day-to-day recollection of recent incidents; dates of hospitalization.

D. Language

If aphasia is suspected, test all modalities:
ComponentTest
Spontaneous speechFluency, content, word-finding
Comprehension"Point to the window," execute commands
RepetitionRepeat "no ifs, ands, or buts"
NamingName objects, parts of objects (anomia)
ReadingAloud and for comprehension
WritingSpontaneous and to dictation
SpellingSpell simple words forward/backward
Note: Distinguish the language of confusion (inattentive, with intrusions, perseverations) from true aphasia (consistent pattern of language breakdown).

E. Praxis (Motor Planning)

  • Commanded and imitated gestures: hammering a nail, blowing out a candle, throwing dice
  • Sequential hand positions (Luria three-step: fist-edge-palm)
  • Dressing apraxia: observed while patient dresses

F. Visuospatial Function / Gnosis

  • Clock drawing test: Draw clock face, place numbers, set time to 10 past 11
  • Line bisection: Bisect a horizontal line (neglect = bisects off-center)
  • Copy figures: Pentagon intersection, cube
  • Floor plan drawing: Sketch layout of own home
  • Object recognition (Gnosis): Name objects by sight, by touch (stereognosis), by sound

G. Executive Function and Judgment

  • Abstract reasoning: Interpret proverbs ("A rolling stone gathers no moss")
  • Similarities: "In what way are an apple and an orange alike?"
  • Judgment: "What would you do if you found a stamped, addressed envelope on the street?"
  • Insight into own illness: Does patient recognize their deficit?

H. Frontal Lobe Specific Tests

  • Go/No-Go task: Tap when examiner taps once; do not tap when examiner taps twice
  • Perseveration: Alternate sequence drawing (square-triangle-square-triangle); Luria loops
  • Grasp reflex, Palmomental reflex, Gegenhalten (paratonia)

II. CRANIAL NERVE EXAMINATION

CN I - Olfactory Nerve

Test separately for each nostril with familiar odors (coffee, vanilla, clove). Loss = anosmia.
  • Indicated in: head trauma, suspected Parkinson's disease, anterior cranial fossa lesions, meningioma.

CN II - Optic Nerve

  1. Visual Acuity - Snellen chart at 6 metres (20 feet) for each eye separately; pinhole corrects refractive error; near card (Rosenbaum) for bedside
  2. Visual Fields - Confrontation testing (each eye tested separately, examiner's face as central fixation point); count fingers or wiggling fingers in four quadrants; for detailed mapping - perimetry/Goldman fields
  3. Color vision - Ishihara plates (sensitive for optic nerve disease)
  4. Fundoscopy - Optic disc (pallor, papilloedema, cupping), vessels, macula

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

  1. Pupillary examination:
    • Size (normal 2-5 mm), shape, symmetry
    • Direct and consensual light reflex (afferent = CN II, efferent = CN III)
    • Swinging flashlight test - RAPD (relative afferent pupillary defect) = optic nerve or severe retinal disease
    • Near reflex (accommodation-convergence)
    • Anisocoria: >1 mm difference - test in light and dark (Horner vs CN III)
  2. Eye movements:
    • Saccades: rapid voluntary movements ("Look left/right quickly")
    • Smooth pursuit: follow slowly moving finger (H-pattern)
    • Vergence: convergence
    • Vestibulo-ocular reflex (VOR): doll's head maneuver
    • Nystagmus: direction, type (horizontal/vertical/torsional), fast vs slow phase
  3. Ptosis: CN III (complete, with pupil dilation) vs Horner's (partial, with miosis, anhidrosis)

CN V - Trigeminal Nerve

  1. Sensation - Test pinprick and light touch in all three divisions (V1 ophthalmic, V2 maxillary, V3 mandibular) bilaterally; also test cornea
  2. Corneal reflex - Touch cornea gently with wisp of cotton; normal = bilateral blink (afferent CN V1, efferent CN VII); come from side to avoid visual stimulus
  3. Motor division - Jaw strength: clench teeth (masseter/temporalis bulk), open jaw against resistance (pterygoids); jaw deviates to weak side on opening
  4. Jaw jerk - Tap chin with finger; brisk = upper motor neuron lesion above pons

CN VII - Facial Nerve

  1. Observe at rest, during speech and spontaneous smiling (subtle weakness more evident)
  2. Upper face: Raise eyebrows (frontalis), tightly close eyes (resist opening - orbicularis oculi)
  3. Lower face: Puff cheeks, whistle, show teeth (smile to command), purse lips
  4. Upper vs Lower motor neuron distinction: UMN (central) lesion spares forehead due to bilateral cortical innervation; LMN (Bell's palsy) involves entire ipsilateral hemiface
  5. Taste: Anterior 2/3 tongue (chorda tympani, branches of CN VII) - sweet, salt, sour on tongue tip
  6. Hyperacusis: Loud sounds perceived more intensely (stapedius muscle paralysis - LMN CN VII)

CN VIII - Vestibulocochlear

Cochlear (hearing):
  1. Whispered voice test: Whisper 2-syllable words at 60 cm behind each ear separately
  2. Rinne test: 512 Hz tuning fork - compare air conduction (ear canal) vs bone conduction (mastoid); Normal/SNHL: AC > BC (Rinne positive); Conductive loss: BC > AC (Rinne negative)
  3. Weber test: Vibrating fork on center of forehead; lateralizes to better ear in SNHL; lateralizes to worse ear in conductive loss
  4. Formal audiometry if abnormality detected
Vestibular:
  1. Romberg test: Feet together, arms at sides; maintain balance eyes open then eyes closed (positive = falls with eyes closed = proprioceptive or vestibular loss)
  2. Marching test (Unterberger): March on spot with eyes closed; rotates to side of vestibular lesion
  3. Dix-Hallpike test: For BPPV
  4. Head impulse test: Rapid horizontal head thrust; corrective saccade = vestibular hypofunction

CN IX & X - Glossopharyngeal and Vagus

  1. Phonation: Ask "aah" - observe soft palate elevation; deviates to normal side if unilateral weakness; bilateral weakness = nasal voice, regurgitation
  2. Gag reflex: Touch posterior pharyngeal wall or tonsillar pillar - afferent IX, efferent X; reduced in lower motor neuron lesions
  3. Swallowing: Observe for coughing, nasal regurgitation; CN X lesion causes dysphagia
  4. Voice quality: Hoarseness or bovine cough = recurrent laryngeal nerve (CN X) lesion

CN XI - Accessory Nerve

  1. Sternocleidomastoid: Turn head against resistance (turns head to contralateral side); test each separately
  2. Trapezius: Shoulder shrug against resistance; ipsilateral weakness

CN XII - Hypoglossal Nerve

  1. Tongue at rest: Observe for fasciculations, atrophy (LMN lesion)
  2. Tongue protrusion: Deviates to side of weakness (LMN) or away from lesion (UMN)
  3. Rapid tongue movements: "La-la-la" (lingual sounds)
  4. Dysarthria: "Linguo-labial" sounds affected

III. MOTOR SYSTEM EXAMINATION

A. Inspection

  • Muscle bulk: Wasting/atrophy (LMN, disuse, myopathy), hypertrophy (muscular dystrophy, hypothyroid myopathy)
  • Fasciculations: Spontaneous visible muscle twitching at rest (LMN / anterior horn cell disease)
  • Abnormal movements: Tremor (rest vs action), chorea, athetosis, ballismus, dystonia, myoclonus, tics
  • Posture at rest: External rotation of hip (UMN lesion), hemiplegic posture

B. Tone

Examine with patient relaxed; examiner passively moves limbs through full range:
  • Normal: Mild resistance to passive movement
  • Hypotonia: Reduced resistance (cerebellar, LMN, acute spinal shock, chorea)
  • Spasticity: Velocity-dependent increased resistance (pyramidal/UMN lesion); "clasp-knife" - give-way at end; greater in arm flexors and leg extensors
  • Rigidity: Uniform increased resistance throughout range (extrapyramidal); "lead-pipe" or "cogwheel" (Parkinson's - Froment's sign: cogwheeling enhanced by contralateral arm movement)
  • Gegenhalten (Paratonia): Variable resistance, mirrors examiner's pressure (frontal lobe lesion)
  • Clonus: Rhythmic oscillating contractions at sustained stretch (brisk at ankle); >5 beats = pathological

C. Power (Strength)

Use MRC Grading Scale (Medical Research Council):
GradeDescription
0No contraction
1Flicker of contraction, no movement
2Movement with gravity eliminated
3Movement against gravity but not resistance
4Movement against some resistance (4-, 4, 4+ variants)
5Normal power
Systematic muscle testing by region:
Upper Limbs:
  • Shoulder abduction (C5, deltoid), adduction (C6-8, pectoral/lat)
  • Elbow flexion (C5-6, biceps), extension (C7, triceps)
  • Wrist extension (C6-7, extensors), flexion (C6-7, flexors)
  • Finger extension (C7, extensor digitorum), flexion (C8, FDP/FDS)
  • Finger abduction/adduction (T1, interossei)
  • Thumb opposition (C8-T1, abductor pollicis brevis)
Lower Limbs:
  • Hip flexion (L1-2, iliopsoas), extension (L5-S1, gluteus maximus)
  • Hip abduction (L4-5, gluteus medius), adduction (L2-4, adductors)
  • Knee extension (L3-4, quadriceps), flexion (L5-S1, hamstrings)
  • Ankle dorsiflexion (L4-5, tibialis anterior), plantarflexion (S1-2, gastrocnemius)
  • Hallux extension (L5, EHL), toe flexion (S1-2)
Quick screening maneuvers:
  • Pronator drift: Arms outstretched, palms up, eyes closed for 10 seconds; pronation and downward drift = UMN lesion; upward drift = sensory ataxia; arm "orbiting" away = subtle weakness
  • Barre's sign (legs): Patient prone, knees bent at 90°; affected leg drifts down
  • Heel-walking, toe-walking: Foot drop (L4-5) vs S1 weakness; "rise on toes" - assess with patient standing at wall

D. Coordination (Cerebellar Function)

Upper limbs:
  • Finger-nose-finger test: Patient touches own nose then examiner's finger back and forth; look for intention tremor, dysmetria, past-pointing
  • Rapid alternating movements (Dysdiadochokinesia): Alternating pronation/supination ("patting the back of hand"), or rapidly touching each fingertip with thumb
  • Rebound test: Suddenly release flexed arm against examiner's resistance; cerebellar disease = delayed check/overshoot
Lower limbs:
  • Heel-shin test: Heel placed on opposite knee, run smoothly down shin to ankle; cerebellar disease = ataxic, irregular path
  • Foot tapping: Tap foot rapidly on examiner's hand
  • Toe-finger test
Truncal ataxia: Inability to sit upright steadily without support (midline cerebellar vermis lesion)

IV. REFLEXES

A. Deep Tendon Reflexes (Muscle Stretch Reflexes)

ReflexStimulus SiteRootResponse
BicepsBiceps tendonC5-C6Elbow flexion
Supinator (Brachioradialis)Radial styloidC5-C6Forearm supination/flexion
TricepsTriceps tendonC7-C8Elbow extension
Finger flexionExaminer's fingers on patient'sC8-T1Finger flexion
Patellar (Knee jerk)Patellar tendonL3-L4Knee extension
Achilles (Ankle jerk)Achilles tendonS1-S2Plantarflexion
Grading:
  • 0 = Absent
  • 1+ = Present only with reinforcement (Jendrassik maneuver - clasp hands and pull)
  • 2+ = Normal
  • 3+ = Brisk (may be normal)
  • 4+ = Very brisk with clonus (pathological - UMN)
Jendrassik Maneuver: Patient hooks fingers together and pulls; examiner taps lower limb reflexes simultaneously; reinforces barely obtainable reflexes.

B. Plantar Response (Babinski Sign)

  • Stroke the lateral aspect of sole from heel toward ball and across to big toe
  • Normal (flexor response): Plantar flexion of toes
  • Abnormal (Babinski sign, extensor response): Dorsiflexion of big toe ± fanning of other toes = damage to corticospinal tract (UMN)
  • Withdrawal response: Quick withdrawal of whole foot (not Babinski)
  • Triple flexion response: Flexion of hip, knee, dorsiflexion of foot (pathological, similar significance to Babinski)
Equivalent signs:
  • Oppenheim: Knuckle pressed firmly down the shin
  • Gordon: Squeezing the calf
  • Chaddock: Lateral aspect of dorsum of foot from lateral malleolus forward
  • Bing: Prick dorsum of foot with pin

C. Superficial Reflexes

  • Abdominal reflexes: Stroke skin from lateral to midline in each quadrant; reflex absent ipsilateral to pyramidal lesion; absent bilaterally in obese patients (less reliable)
  • Cremasteric reflex: Stroke inner thigh; cremasteric muscle contracts, scrotal sac rises; Lost in UMN lesions, ipsilateral cord/root lesion
  • Anal reflex: Perianal skin scratch; anal sphincter contracts; Loss = S3-S5 lower motor neuron
  • Bulbocavernosus reflex: Squeeze glans penis/tap pubic symphysis; contraction of bulbocavernosus; Loss = sacral cord/conus lesion

D. Primitive (Pathological) Reflexes

  • Grasp reflex: Stimulate palm; involuntary grasping (frontal lobe lesion)
  • Palmomental reflex: Scratch thenar eminence; ipsilateral chin muscle contracts (frontal lobe)
  • Snout reflex: Tap philtrum; pursing of lips (bilateral frontal disease)
  • Glabellar tap: Repeated taps on glabella; normal = habituates after 2-3 taps; Parkinson's = does not habituate (Myerson's sign)
  • Rooting and sucking reflexes: Normally present in infants; return in severe dementia/frontal lobe disease

V. SENSORY EXAMINATION

Requires full patient cooperation; test one side against the other; move from area of reduced sensation outward.

A. Primary (Modality-Specific) Sensations

ModalityPathwayTest
PainSpinothalamic (contralateral)New disposable pin; sharp vs dull
TemperatureSpinothalamic (contralateral)Cold (metal tuning fork) vs warm; or test tubes
VibrationPosterior column (ipsilateral)128 Hz tuning fork on bony prominences (toes, medial malleolus, tibial crest, knee, ASIS, fingers, wrist, elbow, shoulder)
Proprioception (Joint position sense - JPS)Posterior column (ipsilateral)Move distal phalanx up or down with fingers on lateral sides; start distally (toes, fingers); normal = detect small movements
Light touchPosterior column + spinothalamicWisp of cotton; compare sides

B. Cortical Sensory Modalities

(Require intact primary sensation; lesion in parietal cortex)
  • Stereognosis: Identify common objects (key, coin, pen) placed in hand without looking
  • Graphesthesia: Identify numbers or letters written on palm
  • Two-point discrimination: Minimum distance for perceiving two distinct touch stimuli; fingertip normal = 2-5 mm; loss = parietal lesion
  • Sensory extinction (double simultaneous stimulation): Touch both sides simultaneously; patient reports only one side (extinction/neglect = parietal lobe, usually non-dominant)
  • Point localization: Patient points to where examiner touches with eyes closed

C. Patterns of Sensory Loss

PatternLesion Site
Glove-and-stockingPeripheral neuropathy (dying-back)
DermatomalNerve root (radiculopathy)
Dissociated (pain/temp lost, vibration/JPS intact)Spinothalamic only: syringomyelia, Brown-Sequard
Contralateral hemibody (all modalities)Internal capsule/thalamus
Ipsilateral body + contralateral faceLateral medullary syndrome (Wallenberg)
Saddle distributionCauda equina lesion
Cortical sensory loss with normal primary sensationParietal lobe lesion

VI. GAIT AND STATION

Observe the patient rising from a chair (proximal muscle strength), and then:
Gait TypeFeaturesLocalization
HemiplegicArm flexed, adducted; leg circumductsCorticospinal (contralateral)
Scissors (Spastic-bilateral)Both legs stiff, cross in frontBilateral corticospinal
Steppage (foot drop)High-step, foot slaps floor; cannot heel-walkL4-5, peroneal nerve
Trendelenburg (waddling)Pelvis dips to opposite side on stepHip abductors (L4-5, S1)
Cerebellar ataxicWide-based, reeling, irregular; falls to side of lesionCerebellum/cerebellar tracts
Sensory ataxicWide-based, high-stepping, "stamping"; worsens eyes closed (Romberg +)Posterior columns / peripheral neuropathy
ParkinsonianShuffling small steps; stooped; reduced arm swing; festination; en-bloc turnsBasal ganglia
Apraxic (Magnetic)"Feet glued to floor"; small shuffling steps; normal on lyingFrontal lobe (NPH, frontal mass)
AntalgicShortened stance phase on painful limbMusculoskeletal
Romberg Test: Stand with feet together, eyes open (tests cerebellar function) then eyes closed (proprioception). Positive = significant sway/falling with eyes closed = sensory ataxia (posterior column / large-fiber neuropathy). A cerebellar patient sways with eyes both open and closed.
Tandem (heel-to-toe) Gait: Unmasks subtle cerebellar or vestibular imbalance.
Hopping/one-leg stand: Detects unilateral weakness or imbalance.

VII. SCREENING NEUROLOGICAL EXAMINATION (Quick Reference)

(From Bradley & Daroff's Table 1.1 and Adams & Victor Table 1-4)
ComponentKey Tests
Mental StatusOrientation, memory (3-word recall), language (spontaneous speech, naming), attention (serial 7s)
CN ISmell (if indicated: trauma, Parkinson's)
CN IIVisual acuity, confrontation fields, fundoscopy, pupil reactions
CN III, IV, VIEye movements (H-pattern), pupils, nystagmus
CN VPin/touch on face, corneal reflex
CN VIIRaise brows, close eyes tightly, smile to command
CN VIIIWhisper test, Rinne & Weber
CN IX-XPalate elevation, gag, voice quality
CN XIShoulder shrug, head turn
CN XIITongue protrusion, fasciculations
MotorTone, power (proximal and distal), pronator drift, heel/toe walk
ReflexesBiceps (C5-6), triceps (C7), supinator (C6), knee (L3-4), ankle (S1), plantars
CoordinationFinger-nose-finger, heel-shin, rapid alternating movements
SensoryPin, vibration, joint position sense (fingers and toes)
Gait/StationObservation, Romberg, tandem walk

VIII. SPECIAL EXAMINATION CONSIDERATIONS

Meningeal Signs

  • Neck stiffness (Nuchal rigidity): Passive neck flexion resisted; chin-to-chest test; indicates meningeal irritation (meningitis, subarachnoid hemorrhage)
  • Kernig's sign: Flex hip to 90° then extend knee - resistance/pain at <135° = positive
  • Brudzinski's sign: Passive neck flexion causes involuntary hip and knee flexion
  • Jolt accentuation: Horizontal head rotation at 2-3 Hz worsens headache = meningitis

Signs of Raised Intracranial Pressure

  • Papilloedema on fundoscopy
  • Sixth nerve palsy (false localizing sign)
  • Cushing's triad: bradycardia, hypertension, irregular respiration

Upper vs Lower Motor Neuron Differentiation

FeatureUMN (Central)LMN (Peripheral)
ToneSpastic (clasp-knife)Flaccid (hypotonia)
WeaknessPyramidal pattern (extensors in arm, flexors in leg)Distribution of nerve/root/muscle
ReflexesBrisk, clonusReduced/absent
PlantarExtensor (Babinski)Flexor
FasciculationsAbsentPresent
WastingLate, disuse onlyEarly, prominent
Abdominal reflexesAbsentPresent

Sources

  • Adams and Victor's Principles of Neurology, 12th Edition - Chapter 1 (Approach to the Patient with Neurologic Disease), pp. 18-21
  • Bradley and Daroff's Neurology in Clinical Practice - Chapter 1 (Neurological Examination), Table 1.1
  • Harrison's Principles of Internal Medicine, 22nd Edition - Mental Status Examination section

Create a comprehensive PDF reference card of CNS examination techniques

Reading File
Running Command

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

Running Command

mkdir -p /home/daytona/workspace/cns-reference-card && python3 -c "import reportlab; print(reportlab.Version)"

Writing File

~/cns-reference-card/generate_cns_pdf.py

#!/usr/bin/env python3
"""
CNS Examination Reference Card - Comprehensive PDF Generator
Based on Adams & Victor's Principles of Neurology 12th Ed and
Bradley & Daroff's Neurology in Clinical Practice
"""

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

OUTPUT_PATH = "/home/daytona/workspace/cns-reference-card/CNS_Examination_Reference_Card.pdf"

# ── Colour Palette ──────────────────────────────────────────────────────────
NAVY       = HexColor("#1A2E4A")
TEAL       = HexColor("#1E7B7B")
TEAL_LIGHT = HexColor("#E8F5F5")
BLUE_MID   = HexColor("#2D5F8A")
BLUE_LIGHT = HexColor("#EBF3FA")
AMBER      = HexColor("#D97706")
AMBER_LT   = HexColor("#FFF8E7")
RED_DK     = HexColor("#8B1A1A")
RED_LT     = HexColor("#FDF0F0")
GREEN_DK   = HexColor("#1A5C2E")
GREEN_LT   = HexColor("#EDF7F0")
GREY_BG    = HexColor("#F4F6F9")
GREY_RULE  = HexColor("#C8D4E0")
WHITE      = colors.white
BLACK      = colors.black
TEXT_DARK  = HexColor("#1C2B3A")


# ── Page Number Footer ───────────────────────────────────────────────────────
class NumberedCanvas(canvas.Canvas):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._saved_page_states = []

    def showPage(self):
        self._saved_page_states.append(dict(self.__dict__))
        self._startPage()

    def save(self):
        num_pages = len(self._saved_page_states)
        for state in self._saved_page_states:
            self.__dict__.update(state)
            self.draw_page_number(num_pages)
            canvas.Canvas.showPage(self)
        canvas.Canvas.save(self)

    def draw_page_number(self, page_count):
        page_num = self._saved_page_states.index(dict(self.__dict__)) + 1
        self.saveState()
        self.setFont("Helvetica", 8)
        self.setFillColor(HexColor("#6B7C8F"))
        w, h = A4
        self.drawCentredString(w / 2, 12 * mm,
                               f"CNS Examination Reference Card  |  Page {page_num} of {page_count}  |  Based on Adams & Victor 12e · Bradley & Daroff")
        # footer rule
        self.setStrokeColor(GREY_RULE)
        self.setLineWidth(0.5)
        self.line(15 * mm, 16 * mm, w - 15 * mm, 16 * mm)
        self.restoreState()


# ── Styles ───────────────────────────────────────────────────────────────────
def make_styles():
    base = getSampleStyleSheet()

    styles = {}

    styles['cover_title'] = ParagraphStyle(
        'cover_title', fontName='Helvetica-Bold', fontSize=26,
        textColor=WHITE, alignment=TA_CENTER, spaceAfter=6, leading=32)

    styles['cover_sub'] = ParagraphStyle(
        'cover_sub', fontName='Helvetica', fontSize=13,
        textColor=HexColor("#B8D4E8"), alignment=TA_CENTER, spaceAfter=4)

    styles['cover_note'] = ParagraphStyle(
        'cover_note', fontName='Helvetica', fontSize=9,
        textColor=HexColor("#8AAFC8"), alignment=TA_CENTER)

    styles['section_header'] = ParagraphStyle(
        'section_header', fontName='Helvetica-Bold', fontSize=13,
        textColor=WHITE, alignment=TA_LEFT, leading=16)

    styles['sub_header'] = ParagraphStyle(
        'sub_header', fontName='Helvetica-Bold', fontSize=10,
        textColor=NAVY, spaceAfter=2, spaceBefore=4, leading=13)

    styles['body'] = ParagraphStyle(
        'body', fontName='Helvetica', fontSize=8.5,
        textColor=TEXT_DARK, spaceAfter=2, leading=12)

    styles['body_bold'] = ParagraphStyle(
        'body_bold', fontName='Helvetica-Bold', fontSize=8.5,
        textColor=TEXT_DARK, spaceAfter=2, leading=12)

    styles['bullet'] = ParagraphStyle(
        'bullet', fontName='Helvetica', fontSize=8.2,
        textColor=TEXT_DARK, leftIndent=10, firstLineIndent=-8,
        spaceAfter=1.5, leading=11.5,
        bulletText='•')

    styles['small'] = ParagraphStyle(
        'small', fontName='Helvetica', fontSize=7.5,
        textColor=HexColor("#4A5568"), leading=10)

    styles['cell_hdr'] = ParagraphStyle(
        'cell_hdr', fontName='Helvetica-Bold', fontSize=8,
        textColor=WHITE, alignment=TA_CENTER, leading=10)

    styles['cell_body'] = ParagraphStyle(
        'cell_body', fontName='Helvetica', fontSize=7.8,
        textColor=TEXT_DARK, alignment=TA_LEFT, leading=10.5)

    styles['cell_center'] = ParagraphStyle(
        'cell_center', fontName='Helvetica', fontSize=7.8,
        textColor=TEXT_DARK, alignment=TA_CENTER, leading=10.5)

    styles['warning'] = ParagraphStyle(
        'warning', fontName='Helvetica-Bold', fontSize=8.5,
        textColor=RED_DK, leading=12)

    styles['clinical_pearl'] = ParagraphStyle(
        'clinical_pearl', fontName='Helvetica', fontSize=8.2,
        textColor=GREEN_DK, leading=12, leftIndent=8)

    styles['table_title'] = ParagraphStyle(
        'table_title', fontName='Helvetica-Bold', fontSize=9,
        textColor=NAVY, spaceAfter=2, spaceBefore=6)

    return styles


S = make_styles()
W, H = A4
MARGIN = 15 * mm
CONTENT_W = W - 2 * MARGIN


# ── Helper Flowables ─────────────────────────────────────────────────────────
def section_banner(text, color=NAVY, icon=""):
    """Full-width coloured section banner."""
    label = f"{icon}  {text}" if icon else text
    p = Paragraph(label, S['section_header'])
    t = Table([[p]], colWidths=[CONTENT_W],
              rowHeights=[None])
    t.setStyle(TableStyle([
        ('BACKGROUND', (0, 0), (-1, -1), color),
        ('TOPPADDING',    (0, 0), (-1, -1), 5),
        ('BOTTOMPADDING', (0, 0), (-1, -1), 5),
        ('LEFTPADDING',   (0, 0), (-1, -1), 8),
        ('RIGHTPADDING',  (0, 0), (-1, -1), 8),
        ('ROUNDEDCORNERS', [4, 4, 4, 4]),
    ]))
    return t


def shaded_box(content_flowables, bg=BLUE_LIGHT, border=BLUE_MID, radius=4):
    """Wrap flowables in a shaded rounded box."""
    inner = Table([[f] for f in content_flowables],
                  colWidths=[CONTENT_W - 14])
    t = Table([[inner]], colWidths=[CONTENT_W])
    t.setStyle(TableStyle([
        ('BACKGROUND',   (0, 0), (-1, -1), bg),
        ('BOX',          (0, 0), (-1, -1), 0.8, border),
        ('TOPPADDING',    (0, 0), (-1, -1), 6),
        ('BOTTOMPADDING', (0, 0), (-1, -1), 6),
        ('LEFTPADDING',   (0, 0), (-1, -1), 7),
        ('RIGHTPADDING',  (0, 0), (-1, -1), 7),
    ]))
    return t


def two_col(left_items, right_items, left_w_frac=0.5):
    """Two-column layout. Each arg is a list of flowables."""
    lw = CONTENT_W * left_w_frac - 3 * mm
    rw = CONTENT_W * (1 - left_w_frac) - 3 * mm
    left_col  = Table([[f] for f in left_items],  colWidths=[lw])
    right_col = Table([[f] for f in right_items], colWidths=[rw])
    t = Table([[left_col, right_col]], colWidths=[lw + 3 * mm, rw + 3 * mm])
    t.setStyle(TableStyle([
        ('VALIGN',        (0, 0), (-1, -1), 'TOP'),
        ('LEFTPADDING',   (0, 0), (-1, -1), 0),
        ('RIGHTPADDING',  (0, 0), (-1, -1), 0),
        ('TOPPADDING',    (0, 0), (-1, -1), 0),
        ('BOTTOMPADDING', (0, 0), (-1, -1), 0),
    ]))
    return t


def standard_table(headers, rows, col_widths=None, hdr_color=NAVY):
    """Styled data table with coloured header row."""
    hdr_cells = [Paragraph(h, S['cell_hdr']) for h in headers]
    data = [hdr_cells]
    for r in rows:
        data.append([Paragraph(str(c), S['cell_body']) for c in r])

    if col_widths is None:
        col_widths = [CONTENT_W / len(headers)] * len(headers)

    t = Table(data, colWidths=col_widths, repeatRows=1)
    style = [
        ('BACKGROUND',   (0, 0), (-1, 0),  hdr_color),
        ('TEXTCOLOR',    (0, 0), (-1, 0),  WHITE),
        ('ROWBACKGROUNDS', (0, 1), (-1, -1), [WHITE, GREY_BG]),
        ('GRID',         (0, 0), (-1, -1), 0.4, GREY_RULE),
        ('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), 'MIDDLE'),
    ]
    t.setStyle(TableStyle(style))
    return t


def pearl(text):
    return shaded_box(
        [Paragraph(f"<b>Clinical Pearl:</b> {text}", S['clinical_pearl'])],
        bg=GREEN_LT, border=GREEN_DK)


def alert_box(text):
    return shaded_box(
        [Paragraph(f"<b>Important:</b> {text}", S['warning'])],
        bg=RED_LT, border=RED_DK)


def sp(h=4):
    return Spacer(1, h * mm)


# ── Cover Page ───────────────────────────────────────────────────────────────
class CoverPage(Flowable):
    def __init__(self):
        super().__init__()
        self.width = CONTENT_W
        self.height = H - 2 * MARGIN

    def draw(self):
        c = self.canv
        w, h = self.width, self.height

        # Background gradient-like blocks
        c.setFillColor(NAVY)
        c.roundRect(0, h - 120 * mm, w, 120 * mm, 8, fill=1, stroke=0)

        c.setFillColor(TEAL)
        c.roundRect(0, h - 130 * mm, w, 18 * mm, 4, fill=1, stroke=0)

        # Title
        c.setFillColor(WHITE)
        c.setFont("Helvetica-Bold", 30)
        c.drawCentredString(w / 2, h - 38 * mm, "CNS EXAMINATION")
        c.setFont("Helvetica-Bold", 22)
        c.drawCentredString(w / 2, h - 52 * mm, "REFERENCE CARD")

        c.setFillColor(HexColor("#B8D4E8"))
        c.setFont("Helvetica", 12)
        c.drawCentredString(w / 2, h - 68 * mm, "Comprehensive Neurological Examination Guide")
        c.drawCentredString(w / 2, h - 78 * mm, "for MD Internal Medicine")

        # Teal stripe text
        c.setFillColor(WHITE)
        c.setFont("Helvetica-Bold", 9)
        c.drawCentredString(w / 2, h - 124 * mm,
            "Adams & Victor 12e  ·  Bradley & Daroff  ·  Harrison's 22e")

        # Section index boxes
        sections = [
            ("I", "Mental Status"),
            ("II", "Cranial Nerves"),
            ("III", "Motor System"),
            ("IV", "Reflexes"),
            ("V", "Sensory"),
            ("VI", "Gait & Station"),
            ("VII", "Special Signs"),
            ("VIII", "Quick Screens"),
        ]
        box_w = (w - 10 * mm) / 4
        box_h = 22 * mm
        start_y = h - 175 * mm
        for i, (num, name) in enumerate(sections):
            col = i % 4
            row = i // 4
            x = col * (box_w + 2 * mm)
            y = start_y - row * (box_h + 3 * mm)
            # box
            c.setFillColor(BLUE_MID if row == 0 else TEAL)
            c.roundRect(x, y, box_w, box_h, 5, fill=1, stroke=0)
            c.setFillColor(WHITE)
            c.setFont("Helvetica-Bold", 16)
            c.drawCentredString(x + box_w / 2, y + 11 * mm, num)
            c.setFont("Helvetica", 8)
            c.drawCentredString(x + box_w / 2, y + 4 * mm, name)

        # Bottom note
        c.setFillColor(GREY_RULE)
        c.setFont("Helvetica", 8)
        c.drawCentredString(w / 2, 8 * mm,
            "For educational and clinical reference. Always apply clinical judgment.")


# ── Content Builders ─────────────────────────────────────────────────────────

def build_mental_status():
    elems = []
    elems.append(section_banner("I. MENTAL STATUS EXAMINATION", NAVY))
    elems.append(sp(3))

    # Consciousness
    elems.append(Paragraph("A. Level of Consciousness", S['sub_header']))
    cons_rows = [
        ["Alert",     "Fully awake, responds normally"],
        ["Drowsy",    "Arousable by verbal stimuli; may drift off"],
        ["Stuporous", "Arousable only by vigorous/painful stimuli; minimal verbal"],
        ["Comatose",  "Unarousable; no purposeful response to stimuli"],
    ]
    elems.append(standard_table(["State", "Description"],
                                cons_rows,
                                [40*mm, CONTENT_W - 40*mm],
                                hdr_color=BLUE_MID))
    elems.append(sp(3))

    # Attention & Orientation
    elems.append(Paragraph("B. Attention Tests", S['sub_header']))
    att_rows = [
        ["Digit Span", "Forward: read digits 1/sec, patient repeats. Normal = 7 ± 2 digits\nBackward: Normal ≥ 5"],
        ["Serial Subtraction", "Subtract 7s from 100 (100, 93, 86…) or 3s from 20"],
        ['"A" Vigilance', "Tap on hearing letter A in random letter sequence read aloud"],
        ["Palm-Side-Fist", "Repeat three-step motor sequence — tests frontal function"],
        ["Spelling Reverse", "Spell WORLD backwards (D-L-R-O-W)"],
    ]
    elems.append(standard_table(["Test", "Method / Normal"],
                                att_rows,
                                [38*mm, CONTENT_W - 38*mm],
                                hdr_color=BLUE_MID))
    elems.append(sp(3))

    # Orientation
    elems.append(Paragraph("C. Orientation (Time · Place · Person)", S['sub_header']))
    elems.append(shaded_box([
        Paragraph("<b>Time:</b> Date (off >3 days = significant), day, month, year, time (off >4 hours = significant)", S['body']),
        Paragraph("<b>Place:</b> Name of hospital/clinic, type of place, city, country", S['body']),
        Paragraph("<b>Person:</b> Own name (lost only in very severe dementia)", S['body']),
    ], bg=BLUE_LIGHT, border=BLUE_MID))
    elems.append(sp(3))

    # Memory
    elems.append(Paragraph("D. Memory", S['sub_header']))
    mem_rows = [
        ["Immediate recall", "Repeat 3 words immediately after hearing them"],
        ["Short-term / Recent", "Recall 3 words after 5 minutes (note number recalled)"],
        ["Remote memory", "Historical facts (PM of India, last Olympics)"],
        ["Episodic memory", "Recall of specific dated personal events"],
        ["Anterograde", "Learning new information after an event"],
        ["Retrograde", "Recall of information before an event"],
    ]
    elems.append(standard_table(["Type", "Test"], mem_rows,
                                [45*mm, CONTENT_W - 45*mm], TEAL))
    elems.append(sp(3))

    # Language
    elems.append(Paragraph("E. Language (test if aphasia suspected)", S['sub_header']))
    lang_rows = [
        ["Spontaneous speech", "Fluency, content, word-finding pauses, paraphrasias"],
        ["Comprehension", "'Point to the window'; execute one/two/three-step commands"],
        ["Repetition", "Repeat 'No ifs, ands, or buts'"],
        ["Naming", "Name objects and parts of objects (pen cap, watch strap)"],
        ["Reading", "Aloud and for comprehension"],
        ["Writing", "Spontaneous sentence and to dictation"],
    ]
    elems.append(standard_table(["Component", "Method"], lang_rows,
                                [42*mm, CONTENT_W - 42*mm], TEAL))
    elems.append(sp(2))
    elems.append(pearl("Distinguish language of confusion (inattentive, intrusions, perseverations) from true aphasia (consistent breakdown in one or more language modalities)."))
    elems.append(sp(3))

    # Higher cortical
    elems.append(Paragraph("F. Higher Cortical Functions", S['sub_header']))

    left = [
        Paragraph("<b>Praxis</b>", S['body_bold']),
        Paragraph("• Hammer a nail (ideomotor)", S['bullet']),
        Paragraph("• Blow out a candle", S['bullet']),
        Paragraph("• Fist-edge-palm sequence (Luria)", S['bullet']),
        Paragraph("• Dressing apraxia (observe while dressing)", S['bullet']),
        sp(3),
        Paragraph("<b>Executive / Frontal</b>", S['body_bold']),
        Paragraph("• Abstract reasoning: interpret proverbs", S['bullet']),
        Paragraph("• Similarities: apple vs orange", S['bullet']),
        Paragraph("• Go/No-Go task", S['bullet']),
        Paragraph("• Alternate sequence drawing", S['bullet']),
        Paragraph("• Insight and judgment", S['bullet']),
    ]
    right = [
        Paragraph("<b>Visuospatial / Gnosis</b>", S['body_bold']),
        Paragraph("• Clock drawing (set to 10 past 11)", S['bullet']),
        Paragraph("• Line bisection (neglect = off-centre)", S['bullet']),
        Paragraph("• Copy intersecting pentagons, cube", S['bullet']),
        Paragraph("• Stereognosis: identify object by touch", S['bullet']),
        Paragraph("• Graphesthesia: number written on palm", S['bullet']),
        Paragraph("• Two-point discrimination (fingertip: 2–5 mm)", S['bullet']),
        sp(3),
        Paragraph("<b>Primitive Reflexes (frontal release)</b>", S['body_bold']),
        Paragraph("• Grasp reflex (frontal lesion)", S['bullet']),
        Paragraph("• Palmomental (scratch thenar → ipsilateral chin)", S['bullet']),
        Paragraph("• Snout reflex (bilateral frontal disease)", S['bullet']),
        Paragraph("• Glabellar tap: no habituation = Parkinson's", S['bullet']),
    ]
    elems.append(two_col(left, right))
    elems.append(sp(2))
    return elems


def build_cranial_nerves():
    elems = []
    elems.append(section_banner("II. CRANIAL NERVE EXAMINATION", TEAL))
    elems.append(sp(3))

    cn_rows = [
        ["CN I\nOlfactory",
         "Each nostril separately with familiar odors (coffee, vanilla, clove)",
         "Anosmia → frontal mass, Parkinson's, head trauma",
         "Test when: head trauma, suspected PD, anosmia"],
        ["CN II\nOptic",
         "Visual acuity (Snellen/Rosenbaum); confrontation fields; fundoscopy; colour vision (Ishihara)",
         "Reduced acuity → lens, optic nerve; Field defects → chiasm/tract/cortex",
         "RAPD = optic nerve or severe retinal disease"],
        ["CN III\nOculomotor",
         "Pupil size, shape, direct + consensual light reflex; near reflex; eyelid position",
         "Complete CN III palsy: ptosis + dilated pupil + eye 'down and out'",
         "Pupil-involving = surgical CN III until proven otherwise"],
        ["CN III, IV, VI\nEye Movements",
         "H-pattern pursuit; saccades; VOR (doll's head); nystagmus direction",
         "Diplopia on gaze → specific nerve palsy; INO → MLF lesion",
         "Fast phase of nystagmus defines direction"],
        ["CN V\nTrigeminal",
         "Pinprick + light touch: V1/V2/V3 bilaterally; corneal reflex; jaw strength + jaw jerk",
         "Reduced corneal reflex: CN V1 or VII; Jaw deviates to weak side",
         "Jaw jerk brisk = UMN above pons"],
        ["CN VII\nFacial",
         "Raise brows, close eyes tight, smile to command, puff cheeks, show teeth",
         "UMN: forehead spared (bilateral cortical supply); LMN: entire hemiface",
         "Hyperacusis = stapedius branch involvement"],
        ["CN VIII\nVestibulocochlear",
         "Whisper test; Rinne (512 Hz fork): AC vs BC; Weber (midline forehead)",
         "Rinne −ve = conductive; Weber → worse ear (conductive) / better ear (SNHL)",
         "Romberg test; Dix-Hallpike for BPPV"],
        ["CN IX, X\nGlossopharyngeal\nVagus",
         "'Aah' — palate elevation + symmetry; gag reflex; swallowing; voice quality",
         "Palate deviates to normal side (unilateral X palsy)",
         "Hoarse/bovine cough = recurrent laryngeal n."],
        ["CN XI\nAccessory",
         "SCM: head turn against resistance; Trapezius: shoulder shrug against resistance",
         "Ipsilateral weakness; SCM turns head to opposite side",
         ""],
        ["CN XII\nHypoglossal",
         "Tongue at rest (fasciculations, atrophy); protrusion; rapid movements 'La-la-la'",
         "LMN: deviates to side of lesion; UMN: deviates away",
         "Fasciculations + wasting = LMN / MND"],
    ]

    elems.append(Paragraph("Summary Table", S['table_title']))
    t = Table(
        [[Paragraph(h, S['cell_hdr']) for h in ["Nerve", "Test Method", "Key Finding / Localisation", "Notes"]]]+
        [[Paragraph(str(r[i]), S['cell_body']) for i in range(4)] for r in cn_rows],
        colWidths=[28*mm, 54*mm, 60*mm, 38*mm],
        repeatRows=1
    )
    t.setStyle(TableStyle([
        ('BACKGROUND',    (0, 0), (-1, 0),  TEAL),
        ('TEXTCOLOR',     (0, 0), (-1, 0),  WHITE),
        ('ROWBACKGROUNDS',(0, 1), (-1, -1), [WHITE, TEAL_LIGHT]),
        ('GRID',          (0, 0), (-1, -1), 0.4, GREY_RULE),
        ('TOPPADDING',     (0, 0), (-1, -1), 3),
        ('BOTTOMPADDING',  (0, 0), (-1, -1), 3),
        ('LEFTPADDING',    (0, 0), (-1, -1), 4),
        ('RIGHTPADDING',   (0, 0), (-1, -1), 4),
        ('VALIGN',        (0, 0), (-1, -1), 'TOP'),
    ]))
    elems.append(t)
    elems.append(sp(3))

    elems.append(Paragraph("Swinging Flashlight Test (RAPD)", S['sub_header']))
    elems.append(shaded_box([
        Paragraph("Alternate the light between eyes every 3 seconds. Normally both pupils constrict equally.", S['body']),
        Paragraph("RAPD (+): When light swings to affected eye → that pupil dilates (paradoxical dilatation) = afferent defect (optic nerve disease or severe retinal lesion).", S['body']),
    ], bg=AMBER_LT, border=AMBER))
    elems.append(sp(3))

    elems.append(Paragraph("Rinne and Weber Test Interpretation", S['sub_header']))
    rw_rows = [
        ["Normal", "AC > BC (Rinne +ve)", "No lateralisation"],
        ["Conductive loss", "BC > AC (Rinne −ve) ipsilateral ear", "Lateralises to WORSE (deaf) ear"],
        ["Sensorineural loss (SNHL)", "AC > BC (Rinne +ve, but both reduced)", "Lateralises to BETTER ear"],
    ]
    elems.append(standard_table(
        ["Condition", "Rinne Result", "Weber Result"],
        rw_rows,
        [48*mm, 65*mm, 67*mm],
        TEAL
    ))
    elems.append(sp(2))
    return elems


def build_motor():
    elems = []
    elems.append(section_banner("III. MOTOR SYSTEM EXAMINATION", BLUE_MID))
    elems.append(sp(3))

    # Inspection & Tone side by side
    left = [
        Paragraph("A. Inspection", S['sub_header']),
        Paragraph("• Muscle bulk: wasting (LMN/disuse/myopathy), hypertrophy", S['bullet']),
        Paragraph("• Fasciculations: spontaneous twitching at rest (LMN/AHC)", S['bullet']),
        Paragraph("• Abnormal movements: tremor, chorea, dystonia, myoclonus, tics", S['bullet']),
        Paragraph("• Posture: external hip rotation (UMN), hemiplegic posture", S['bullet']),
        Paragraph("• Atrophy pattern: global vs focal vs dermatomal", S['bullet']),
    ]
    right = [
        Paragraph("B. Tone (passive movement)", S['sub_header']),
        Paragraph("• <b>Hypotonia:</b> LMN, cerebellar, acute shock, chorea", S['bullet']),
        Paragraph("• <b>Spasticity:</b> velocity-dependent (UMN); clasp-knife; flexors > extensors in arms", S['bullet']),
        Paragraph("• <b>Rigidity:</b> lead-pipe (extrapyramidal); cogwheel (PD — enhanced by Froment's)", S['bullet']),
        Paragraph("• <b>Gegenhalten:</b> mirrors examiner's pressure (frontal lobe)", S['bullet']),
        Paragraph("• <b>Clonus:</b> >5 beats = pathological (brisk at ankle); UMN lesion", S['bullet']),
    ]
    elems.append(two_col(left, right))
    elems.append(sp(3))

    # MRC scale
    elems.append(Paragraph("C. MRC Power Grading Scale", S['sub_header']))
    mrc_rows = [
        ["0", "No contraction visible or palpable"],
        ["1", "Flicker/trace of contraction; no joint movement"],
        ["2", "Full range of movement with gravity eliminated (horizontal plane)"],
        ["3", "Movement against gravity but not against examiner's resistance"],
        ["4−/4/4+", "Movement against mild / moderate / strong resistance"],
        ["5", "Normal power against full resistance"],
    ]
    elems.append(standard_table(["Grade", "Description"], mrc_rows,
                                [20*mm, CONTENT_W - 20*mm], BLUE_MID))
    elems.append(sp(3))

    # Muscle testing
    elems.append(Paragraph("D. Key Muscle Testing (Root & Nerve)", S['sub_header']))

    ul_rows = [
        ["Shoulder abduction",    "C5",   "Deltoid",          "Axillary nerve"],
        ["Elbow flexion",         "C5-6", "Biceps",           "Musculocutaneous"],
        ["Elbow extension",       "C7",   "Triceps",          "Radial nerve"],
        ["Wrist extension",       "C6-7", "Ext. carpi rad.",  "Radial nerve"],
        ["Wrist flexion",         "C6-7", "Flex. carpi rad.", "Median nerve"],
        ["Finger extension",      "C7",   "Ext. digitorum",   "Posterior interosseous"],
        ["Finger flexion (DIP)",  "C8",   "FDP",              "Median/Ulnar"],
        ["Finger abduction",      "T1",   "Dorsal interossei","Ulnar nerve"],
        ["Thumb opposition",      "C8-T1","Abductor poll. br.","Median nerve (recurrent br.)"],
    ]
    ll_rows = [
        ["Hip flexion",           "L1-2", "Iliopsoas",        "Femoral/lumbar plexus"],
        ["Hip extension",         "L5-S1","Glut. maximus",    "Inferior gluteal"],
        ["Hip abduction",         "L4-5", "Glut. medius",     "Superior gluteal"],
        ["Knee extension",        "L3-4", "Quadriceps",       "Femoral nerve"],
        ["Knee flexion",          "L5-S1","Hamstrings",       "Sciatic nerve"],
        ["Ankle dorsiflexion",    "L4-5", "Tibialis anterior","Deep peroneal"],
        ["Ankle plantarflexion",  "S1-2", "Gastrocnemius",    "Tibial nerve"],
        ["Hallux extension",      "L5",   "EHL",              "Deep peroneal"],
        ["Toe flexion",           "S1-2", "FHL/FDL",          "Tibial nerve"],
    ]

    col_w = [(CONTENT_W - 4*mm) / 2 - 1*mm] * 2

    ul_t = Table(
        [[Paragraph(h, S['cell_hdr']) for h in ["Movement", "Root", "Muscle", "Nerve"]]]+
        [[Paragraph(r[i], S['cell_body']) for i in range(4)] for r in ul_rows],
        colWidths=[40*mm, 12*mm, 35*mm, 40*mm],
        repeatRows=1
    )
    ul_t.setStyle(TableStyle([
        ('BACKGROUND',    (0,0),(-1,0), BLUE_MID),
        ('TEXTCOLOR',     (0,0),(-1,0), WHITE),
        ('ROWBACKGROUNDS',(0,1),(-1,-1),[WHITE, BLUE_LIGHT]),
        ('GRID',          (0,0),(-1,-1), 0.4, GREY_RULE),
        ('TOPPADDING',    (0,0),(-1,-1), 2), ('BOTTOMPADDING',(0,0),(-1,-1),2),
        ('LEFTPADDING',   (0,0),(-1,-1), 3), ('RIGHTPADDING', (0,0),(-1,-1),3),
        ('FONTSIZE',      (0,1),(-1,-1), 7.5),
    ]))

    ll_t = Table(
        [[Paragraph(h, S['cell_hdr']) for h in ["Movement", "Root", "Muscle", "Nerve"]]]+
        [[Paragraph(r[i], S['cell_body']) for i in range(4)] for r in ll_rows],
        colWidths=[40*mm, 12*mm, 35*mm, 40*mm],
        repeatRows=1
    )
    ll_t.setStyle(TableStyle([
        ('BACKGROUND',    (0,0),(-1,0), TEAL),
        ('TEXTCOLOR',     (0,0),(-1,0), WHITE),
        ('ROWBACKGROUNDS',(0,1),(-1,-1),[WHITE, TEAL_LIGHT]),
        ('GRID',          (0,0),(-1,-1), 0.4, GREY_RULE),
        ('TOPPADDING',    (0,0),(-1,-1), 2), ('BOTTOMPADDING',(0,0),(-1,-1),2),
        ('LEFTPADDING',   (0,0),(-1,-1), 3), ('RIGHTPADDING', (0,0),(-1,-1),3),
        ('FONTSIZE',      (0,1),(-1,-1), 7.5),
    ]))

    elems.append(Paragraph("Upper Limb", S['body_bold']))
    elems.append(ul_t)
    elems.append(sp(2))
    elems.append(Paragraph("Lower Limb", S['body_bold']))
    elems.append(ll_t)
    elems.append(sp(3))

    # Screening manoeuvres
    elems.append(Paragraph("E. Quick Screening Manoeuvres", S['sub_header']))
    sm_rows = [
        ["Pronator Drift", "Arms outstretched, palms up, eyes closed ×10 sec; pronation/downward drift = UMN; upward drift = sensory ataxia"],
        ["Barre's Sign (legs)", "Patient prone, knees flexed 90°; weak leg drifts down = UMN"],
        ["Heel-walking", "Tests L4-5 (tibialis anterior); foot drop = cannot heel-walk"],
        ["Toe-walking", "Tests S1 (gastrocnemius); S1 lesion = cannot toe-walk"],
        ["Rise from floor", "Without hand support; tests proximal muscle power (hip extensors, quadriceps)"],
        ["Arm 'orbiting'", "Asymmetric orbiting of one forearm around the other = subtle contralateral UMN weakness"],
    ]
    elems.append(standard_table(["Manoeuvre", "Interpretation"], sm_rows,
                                [40*mm, CONTENT_W - 40*mm], BLUE_MID))
    elems.append(sp(3))

    # Coordination
    elems.append(Paragraph("F. Coordination (Cerebellar Function)", S['sub_header']))
    coord_left = [
        Paragraph("<b>Upper Limb Tests</b>", S['body_bold']),
        Paragraph("• Finger-nose-finger: intention tremor, dysmetria, past-pointing", S['bullet']),
        Paragraph("• Rapid alternating movements (dysdiadochokinesia): alternating pronation/supination", S['bullet']),
        Paragraph("• Rapid finger-thumb tapping", S['bullet']),
        Paragraph("• Rebound test: delayed check / overshoot on sudden release", S['bullet']),
    ]
    coord_right = [
        Paragraph("<b>Lower Limb + Truncal</b>", S['body_bold']),
        Paragraph("• Heel-shin test: run heel down front of shin smoothly", S['bullet']),
        Paragraph("• Foot tapping on examiner's hand", S['bullet']),
        Paragraph("• Truncal ataxia: inability to sit upright unsupported (vermis lesion)", S['bullet']),
        Paragraph("• Romberg — eyes open sway = cerebellar (not Romberg positive)", S['bullet']),
    ]
    elems.append(two_col(coord_left, coord_right))
    elems.append(sp(2))
    elems.append(pearl("DANISH mnemonic for cerebellar signs: Dysdiadochokinesia, Ataxia, Nystagmus, Intention tremor, Scanning speech, Hypotonia/Heel-shin impairment"))
    elems.append(sp(2))
    return elems


def build_reflexes():
    elems = []
    elems.append(section_banner("IV. REFLEXES", NAVY))
    elems.append(sp(3))

    # DTRs
    elems.append(Paragraph("A. Deep Tendon Reflexes (Muscle Stretch Reflexes)", S['sub_header']))
    dtr_rows = [
        ["Biceps",         "Biceps tendon (antecubital fossa)",    "C5–C6", "Elbow flexion"],
        ["Supinator (BR)", "Radial styloid process",               "C5–C6", "Forearm supination + flexion"],
        ["Triceps",        "Triceps tendon (above olecranon)",      "C7–C8", "Elbow extension"],
        ["Finger flexion", "Examiner's fingers over patient's DIP", "C8–T1","Finger flexion (Hoffman equivalent)"],
        ["Patellar",       "Patellar tendon (just below patella)",  "L3–L4", "Knee extension"],
        ["Achilles",       "Achilles tendon",                       "S1–S2", "Plantarflexion"],
    ]
    elems.append(standard_table(
        ["Reflex", "Stimulus Site", "Root", "Response"],
        dtr_rows,
        [32*mm, 55*mm, 18*mm, CONTENT_W - 105*mm], NAVY))
    elems.append(sp(3))

    # Grading
    elems.append(Paragraph("B. DTR Grading", S['sub_header']))
    grade_rows = [
        ["0",   "Absent (even with reinforcement)"],
        ["1+",  "Present only with Jendrassik reinforcement; hypoactive"],
        ["2+",  "Normal"],
        ["3+",  "Brisk; may spread to adjacent muscles; may be normal in anxious patient"],
        ["4+",  "Very brisk; clonus present; pathological — UMN lesion"],
    ]
    elems.append(standard_table(["Grade", "Description"], grade_rows,
                                [20*mm, CONTENT_W - 20*mm], BLUE_MID))
    elems.append(sp(2))
    elems.append(shaded_box([
        Paragraph("<b>Jendrassik Manoeuvre:</b> Patient hooks interlocked fingers and pulls outward simultaneously as examiner taps lower limb reflexes. Reinforces barely obtainable reflexes by increasing gamma motor neuron activity.", S['body']),
    ], bg=AMBER_LT, border=AMBER))
    elems.append(sp(3))

    # Plantar response
    elems.append(Paragraph("C. Plantar Response (Babinski Sign)", S['sub_header']))
    elems.append(shaded_box([
        Paragraph("<b>Technique:</b> Stroke the lateral sole firmly from heel toward the ball and across to the base of the big toe using a blunt instrument (orange stick).", S['body']),
        Paragraph("<b>Flexor response (Normal):</b> Plantarflexion of all toes.", S['body']),
        Paragraph("<b>Extensor response (Babinski +ve):</b> Dorsiflexion of big toe ± fanning of other toes = corticospinal tract damage (UMN).", S['warning']),
        Paragraph("<b>Withdrawal:</b> Quick whole-foot withdrawal — not Babinski; no pathological significance.", S['body']),
        Paragraph("<b>Triple flexion:</b> Hip + knee flexion + foot dorsiflexion — same significance as Babinski; severe spinal lesion.", S['body']),
    ], bg=RED_LT, border=RED_DK))
    elems.append(sp(3))

    # Equivalent signs
    elems.append(Paragraph("D. Babinski Equivalent Signs", S['sub_header']))
    bab_rows = [
        ["Oppenheim",  "Knuckle pressed firmly down shin", "Extensor toe response"],
        ["Gordon",     "Squeeze the calf muscle",          "Extensor toe response"],
        ["Chaddock",   "Stroke lateral dorsum of foot from lateral malleolus forward", "Extensor toe response"],
        ["Bing",       "Prick dorsum of foot with pin",    "Extensor toe response"],
        ["Gonda",      "Flick or extend fourth toe down then release", "Extensor toe response"],
    ]
    elems.append(standard_table(
        ["Sign", "Technique", "Response"],
        bab_rows,
        [28*mm, 95*mm, CONTENT_W - 123*mm], TEAL))
    elems.append(sp(3))

    # Superficial reflexes
    elems.append(Paragraph("E. Superficial Reflexes", S['sub_header']))
    sup_rows = [
        ["Abdominal", "Stroke skin lateral → midline in each quadrant", "T7-12",
         "Absent ipsilateral to pyramidal lesion; absent bilaterally (obese, multiparous)"],
        ["Cremasteric", "Stroke inner thigh", "L1-2",
         "Loss: UMN lesion, ipsilateral cord/root lesion"],
        ["Anal", "Perianal skin scratch", "S3-5",
         "Sphincter contracts; Loss = sacral/conus/LMN"],
        ["Bulbocavernosus", "Squeeze glans penis / tap pubic symphysis", "S3-4",
         "Bulbocavernosus contraction; Loss = sacral cord/conus"],
    ]
    elems.append(standard_table(
        ["Reflex", "Stimulus", "Segment", "Significance"],
        sup_rows,
        [30*mm, 45*mm, 22*mm, CONTENT_W - 97*mm], NAVY))
    elems.append(sp(2))
    return elems


def build_sensory():
    elems = []
    elems.append(section_banner("V. SENSORY EXAMINATION", TEAL))
    elems.append(sp(3))

    elems.append(shaded_box([
        Paragraph("Require full patient cooperation. Test one side against the other. Move from area of reduced sensation outward to normal. Start distally for vibration and JPS.", S['body']),
    ], bg=AMBER_LT, border=AMBER))
    elems.append(sp(3))

    # Primary modalities
    elems.append(Paragraph("A. Primary Sensory Modalities", S['sub_header']))
    prim_rows = [
        ["Pain (nociception)", "Spinothalamic tract (contralateral)",
         "Fresh disposable pin; sharp vs dull; compare both sides",
         "Use fresh pin; do not draw blood"],
        ["Temperature", "Spinothalamic tract (contralateral)",
         "Cold metal tuning fork or cold/warm test tubes",
         "Cold: 5°C; Warm: 40°C"],
        ["Vibration", "Posterior column (ipsilateral)",
         "128 Hz tuning fork on bony prominences: toes → medial malleolus → tibial crest → ASIS → fingers → wrist → elbow",
         "Ask: 'Is it buzzing or still?'; move proximally until felt"],
        ["Joint Position Sense (JPS)", "Posterior column (ipsilateral)",
         "Hold distal phalanx laterally; move up/down; patient says 'up' or 'down'; start distally",
         "Normal: detect 1–2° movements of DIP/MTP"],
        ["Light touch", "Post. column + spinothalamic",
         "Wisp of cotton wool; compare both sides symmetrically",
         "Not dermatomal 'dragging'"],
    ]
    elems.append(standard_table(
        ["Modality", "Pathway", "Test Method", "Notes"],
        prim_rows,
        [32*mm, 40*mm, 65*mm, CONTENT_W - 137*mm], TEAL))
    elems.append(sp(3))

    # Cortical sensory
    elems.append(Paragraph("B. Cortical Sensory Modalities (require intact primary sensation)", S['sub_header']))
    cort_rows = [
        ["Stereognosis", "Identify common objects (key, coin, pen) placed in hand without looking", "Parietal lobe"],
        ["Graphesthesia", "Identify numbers / letters written on palm", "Parietal lobe"],
        ["Two-point discrimination", "Min. distance for 2 distinct stimuli; fingertip normal = 2–5 mm", "Parietal lobe"],
        ["Sensory extinction", "Touch both sides simultaneously; patient reports only one side = extinction/neglect", "Contralateral parietal (often non-dominant)"],
        ["Point localisation", "Patient points (eyes closed) to where examiner touched", "Parietal lobe"],
    ]
    elems.append(standard_table(
        ["Test", "Method", "Lesion Site"],
        cort_rows,
        [38*mm, 100*mm, CONTENT_W - 138*mm], BLUE_MID))
    elems.append(sp(3))

    # Patterns
    elems.append(Paragraph("C. Patterns of Sensory Loss — Localisation", S['sub_header']))
    pat_rows = [
        ["Glove-and-stocking", "Peripheral neuropathy (length-dependent / dying-back)",
         "Pain + vibration + JPS; starts distally"],
        ["Dermatomal", "Single nerve root (radiculopathy)",
         "Follows dermatome; associated radicular pain"],
        ["Single peripheral nerve", "Mononeuropathy (trauma, entrapment)",
         "Distribution of specific nerve territory"],
        ["Dissociated loss\n(pain/temp ↓, vibration/JPS intact)", "Spinothalamic only\n(hemisection: Brown-Séquard, syringomyelia)",
         "Crossed: lesion side loses vibration; opposite side loses pain/temp"],
        ["Contralateral hemibody (all modalities)", "Internal capsule or thalamus (VPL nucleus)",
         "Sudden onset → vascular; deep thalamic lesion"],
        ["Ipsilateral body + contralateral face", "Lateral medullary syndrome (Wallenberg)",
         "PICA territory infarct"],
        ["Saddle (perineal) distribution", "Cauda equina or conus medullaris",
         "S3–S5; bladder/bowel involved"],
        ["Cortical sensory loss only\n(primary sensation intact)", "Parietal cortex lesion",
         "Astereognosis, agraphesthesia, extinction, reduced 2-PD"],
    ]
    elems.append(standard_table(
        ["Pattern", "Lesion", "Key Features"],
        pat_rows,
        [45*mm, 65*mm, CONTENT_W - 110*mm], TEAL))
    elems.append(sp(2))
    return elems


def build_gait():
    elems = []
    elems.append(section_banner("VI. GAIT AND STATION", BLUE_MID))
    elems.append(sp(3))

    gait_rows = [
        ["Hemiplegic", "Arm flexed/adducted, leg circumducts (swings out in arc)",
         "Contralateral corticospinal tract lesion", "UMN signs on affected side"],
        ["Spastic-Bilateral (Scissors)", "Both legs stiff, adducted, crossing each other; small steps",
         "Bilateral corticospinal (cervical myelopathy, CP)", "Bilateral UMN signs"],
        ["Steppage (Foot Drop)", "Exaggerated hip/knee flexion to clear dropped foot; foot slap",
         "L4-5; common peroneal nerve; deep peroneal", "Cannot heel-walk on affected side"],
        ["Trendelenburg (Waddling)", "Pelvis dips to opposite side on weight-bearing step",
         "Hip abductors (L4-5, S1); bilateral = waddling gait",
         "Proximal myopathy / muscular dystrophy"],
        ["Cerebellar Ataxic", "Wide-based, reeling, irregular, falls to side of lesion; worse on tandem",
         "Ipsilateral cerebellum / cerebellar tracts",
         "Sway with eyes open AND closed (not Romberg)"],
        ["Sensory Ataxic", "Wide-based, high-stepping, 'stamping'; watches feet; worsens in dark",
         "Posterior columns or large-fiber peripheral neuropathy",
         "Romberg positive (eyes closed markedly worse)"],
        ["Parkinsonian", "Shuffling small steps; stooped; reduced arm swing; festination; en-bloc turns",
         "Basal ganglia (dopaminergic pathway)",
         "Retropulsion on pull test; hypomimia"],
        ["Apraxic / Magnetic", "'Feet glued to floor'; short shuffling steps; normal arm swing; normal on lying",
         "Frontal lobe (NPH, bilateral frontal lesions)",
         "Classic triad of NPH: gait + dementia + incontinence"],
        ["Antalgic", "Shortened stance phase on painful limb; limp",
         "Pain (musculoskeletal / radicular)",
         "Not neurological in origin"],
        ["Myopathic", "Wide-based; Trendelenburg; difficulty rising from chair; waddling",
         "Proximal muscle weakness (myopathy, muscular dystrophy)",
         "Gowers' sign positive"],
    ]

    t = Table(
        [[Paragraph(h, S['cell_hdr']) for h in ["Gait Type", "Features", "Localization", "Associated Signs"]]]+
        [[Paragraph(str(r[i]), S['cell_body']) for i in range(4)] for r in gait_rows],
        colWidths=[30*mm, 55*mm, 58*mm, 37*mm],
        repeatRows=1
    )
    t.setStyle(TableStyle([
        ('BACKGROUND',    (0,0),(-1,0), BLUE_MID),
        ('TEXTCOLOR',     (0,0),(-1,0), WHITE),
        ('ROWBACKGROUNDS',(0,1),(-1,-1),[WHITE, BLUE_LIGHT]),
        ('GRID',          (0,0),(-1,-1), 0.4, GREY_RULE),
        ('TOPPADDING',    (0,0),(-1,-1),3),('BOTTOMPADDING',(0,0),(-1,-1),3),
        ('LEFTPADDING',   (0,0),(-1,-1),4),('RIGHTPADDING', (0,0),(-1,-1),4),
        ('VALIGN',        (0,0),(-1,-1),'TOP'),
    ]))
    elems.append(t)
    elems.append(sp(3))

    elems.append(Paragraph("Romberg Test", S['sub_header']))
    elems.append(shaded_box([
        Paragraph("<b>Technique:</b> Patient stands with feet together, arms at sides. Observe for 30 seconds with eyes open, then 30 seconds with eyes closed. Stand close to catch the patient.", S['body']),
        Paragraph("<b>Positive Romberg:</b> Markedly increased sway or falls with eyes closed = proprioceptive/posterior column loss or large-fiber peripheral neuropathy. Vision was compensating for lost proprioception.", S['body']),
        Paragraph("<b>Important:</b> Cerebellar ataxia produces sway with eyes BOTH open and closed — this is NOT a positive Romberg test.", S['warning']),
    ], bg=BLUE_LIGHT, border=BLUE_MID))
    elems.append(sp(3))

    elems.append(Paragraph("Additional Station Tests", S['sub_header']))
    add_rows = [
        ["Tandem (heel-to-toe) gait", "Walk on a straight line; unmasks subtle cerebellar or vestibular imbalance"],
        ["One-leg standing", "Stand on each leg separately; Trendelenburg detected; detects unilateral weakness/imbalance"],
        ["Hopping on one foot", "Inability = proximal weakness or balance impairment"],
        ["Rising from chair", "Without arm support tests quadriceps and hip extensors (L3-L4, L5-S1)"],
        ["Walk on heels / toes", "Foot drop = cannot heel-walk (L4-5); S1 weakness = cannot toe-walk"],
    ]
    elems.append(standard_table(["Test", "Purpose"], add_rows,
                                [55*mm, CONTENT_W - 55*mm], TEAL))
    elems.append(sp(2))
    return elems


def build_special_signs():
    elems = []
    elems.append(section_banner("VII. SPECIAL SIGNS & SYNDROMES", TEAL))
    elems.append(sp(3))

    # UMN vs LMN
    elems.append(Paragraph("A. UMN vs LMN Differentiation", S['sub_header']))
    ulm_rows = [
        ["Tone",            "Spastic (clasp-knife); velocity-dependent", "Flaccid / hypotonia"],
        ["Weakness pattern","Pyramidal: extensors↓ in arm, flexors↓ in leg", "Distribution of nerve/root/muscle"],
        ["Reflexes",        "Brisk; clonus; spread", "Reduced or absent"],
        ["Plantar response","Extensor (Babinski +ve)",  "Flexor (normal)"],
        ["Fasciculations",  "Absent",                   "Present (anterior horn / LMN)"],
        ["Wasting",         "Late; disuse atrophy only", "Early; prominent; severe"],
        ["Abdominal reflex","Absent ipsilaterally",      "Present"],
        ["Clonus",          "May be present",            "Absent"],
    ]
    elems.append(standard_table(
        ["Feature", "UMN (Central)", "LMN (Peripheral)"],
        ulm_rows,
        [35*mm, 85*mm, 60*mm], NAVY))
    elems.append(sp(3))

    # Meningeal signs
    elems.append(Paragraph("B. Meningeal Irritation Signs", S['sub_header']))
    mening_rows = [
        ["Nuchal rigidity", "Passive neck flexion resisted; chin cannot touch chest",
         "Meningitis, SAH, cervical spondylosis"],
        ["Kernig's sign", "Flex hip 90°; extend knee; resistance / pain at <135° of extension",
         "Meningitis, SAH"],
        ["Brudzinski's sign", "Passive neck flexion → involuntary hip + knee flexion",
         "Meningitis, SAH"],
        ["Jolt accentuation", "Horizontal head rotation 2–3 Hz worsens headache",
         "Meningitis (sensitivity ~97%)"],
    ]
    elems.append(standard_table(
        ["Sign", "Technique", "Significance"],
        mening_rows,
        [35*mm, 80*mm, CONTENT_W - 115*mm], RED_DK))
    elems.append(sp(3))

    # Raised ICP
    elems.append(Paragraph("C. Signs of Raised Intracranial Pressure", S['sub_header']))
    elems.append(shaded_box([
        Paragraph("• <b>Papilloedema</b> on fundoscopy (blurring of disc margins, loss of venous pulsation, haemorrhages)", S['bullet']),
        Paragraph("• <b>Sixth nerve palsy</b> (false localising sign — CN VI has long intracranial course)", S['bullet']),
        Paragraph("• <b>Cushing's Triad:</b> Bradycardia + Hypertension + Irregular respiration (late ominous sign of herniation)", S['bullet']),
        Paragraph("• Declining level of consciousness, new cranial nerve palsies", S['bullet']),
    ], bg=RED_LT, border=RED_DK))
    elems.append(sp(3))

    # Cerebellar signs
    elems.append(Paragraph("D. Cerebellar Signs (DANISH)", S['sub_header']))
    danish_rows = [
        ["D", "Dysdiadochokinesia", "Impaired rapid alternating movements"],
        ["A", "Ataxia",            "Gait/truncal/limb ataxia; wide-based gait"],
        ["N", "Nystagmus",         "Horizontal; fast phase toward lesion (ipsilateral)"],
        ["I", "Intention tremor",  "Worsens as target approached; finger-nose test"],
        ["S", "Scanning speech",   "Dysarthria; slow, slurred, monotone ('staccato')"],
        ["H", "Hypotonia + Heel-shin", "Reduced tone; impaired heel-shin coordination"],
    ]
    elems.append(standard_table(
        ["Letter", "Sign", "Description"],
        danish_rows,
        [12*mm, 40*mm, CONTENT_W - 52*mm], TEAL))
    elems.append(sp(3))

    # Autonomic examination
    elems.append(Paragraph("E. Autonomic Nervous System Assessment", S['sub_header']))
    auto_left = [
        Paragraph("<b>Cardiovascular</b>", S['body_bold']),
        Paragraph("• BP lying and standing (orthostatic hypotension: ≥20 mmHg systolic drop)", S['bullet']),
        Paragraph("• Heart rate variability (R-R intervals on ECG)", S['bullet']),
        Paragraph("• Valsalva manoeuvre response", S['bullet']),
        Paragraph("• Carotid sinus massage (with care)", S['bullet']),
    ]
    auto_right = [
        Paragraph("<b>Other Autonomic Functions</b>", S['body_bold']),
        Paragraph("• Pupil: Horner's syndrome (miosis, ptosis, anhidrosis)", S['bullet']),
        Paragraph("• Sweating: anhydrosis pattern", S['bullet']),
        Paragraph("• Bladder/bowel function", S['bullet']),
        Paragraph("• Sexual function", S['bullet']),
        Paragraph("• Skin: temperature, colour, trophic changes", S['bullet']),
    ]
    elems.append(two_col(auto_left, auto_right))
    elems.append(sp(2))
    return elems


def build_quick_screen():
    elems = []
    elems.append(section_banner("VIII. QUICK SCREENING EXAMINATION & MNEMONICS", NAVY))
    elems.append(sp(3))

    elems.append(Paragraph("A. 10-Point Bedside Neurological Screen", S['sub_header']))
    elems.append(Paragraph("(Adapted from Adams & Victor Table 1-4 — adds only minutes to the physical examination)", S['small']))
    elems.append(sp(2))

    screen_rows = [
        ["1", "Mental Status",     "Orientation, insight, language — assessed during history"],
        ["2", "Cranial Nerves II", "Pupil size, reactivity; visual acuity; fundoscopy"],
        ["3", "CN III, IV, VI, VII", "Eye movements; facial movements during speech and smile"],
        ["4", "Upper limb motor", "Pronator drift ×10 sec; grip strength; wrist dorsiflexion"],
        ["5", "Upper limb reflexes", "Biceps (C5-6), supinator (C6), triceps (C7)"],
        ["6", "Lower limb inspection", "Active flexion/extension of hips, knees, feet — observe"],
        ["7", "Lower limb reflexes", "Knee jerk (L3-4), ankle jerk (S1-2), plantars"],
        ["8", "Vibration sensation", "128 Hz tuning fork — toes and fingers"],
        ["9", "Coordination", "Finger-nose-finger; heel-shin"],
        ["10","Gait + Romberg", "Observation of gait; tandem walk; Romberg test"],
    ]

    t = Table(
        [[Paragraph(h, S['cell_hdr']) for h in ["#", "Component", "Key Test"]]]+
        [[Paragraph(r[0], S['cell_hdr']), Paragraph(r[1], S['body_bold']), Paragraph(r[2], S['cell_body'])]
         for r in screen_rows],
        colWidths=[10*mm, 45*mm, CONTENT_W - 55*mm],
        repeatRows=1
    )
    t.setStyle(TableStyle([
        ('BACKGROUND',    (0,0),(-1,0), NAVY),
        ('BACKGROUND',    (0,1),(-1,-1), WHITE),
        ('BACKGROUND',    (0,2),(-1,2), GREY_BG),
        ('BACKGROUND',    (0,4),(-1,4), GREY_BG),
        ('BACKGROUND',    (0,6),(-1,6), GREY_BG),
        ('BACKGROUND',    (0,8),(-1,8), GREY_BG),
        ('BACKGROUND',    (0,10),(-1,10), GREY_BG),
        ('TEXTCOLOR',     (0,0),(-1,0), WHITE),
        ('TEXTCOLOR',     (0,1),(-1,-1), NAVY),
        ('GRID',          (0,0),(-1,-1), 0.4, GREY_RULE),
        ('TOPPADDING',    (0,0),(-1,-1),4),('BOTTOMPADDING',(0,0),(-1,-1),4),
        ('LEFTPADDING',   (0,0),(-1,-1),5),('RIGHTPADDING', (0,0),(-1,-1),5),
        ('VALIGN',        (0,0),(-1,-1),'MIDDLE'),
        ('ALIGN',         (0,0),(0,-1), 'CENTER'),
    ]))
    elems.append(t)
    elems.append(sp(3))

    # Mnemonics
    elems.append(Paragraph("B. Key Mnemonics", S['sub_header']))

    left_mn = [
        Paragraph("<b>Cranial Nerves (I–XII)</b>", S['body_bold']),
        Paragraph('"Oh Oh Oh, To Touch And Feel Very Good Velvet, AHH"', S['body']),
        Paragraph("Olfactory, Optic, Oculomotor, Trochlear, Trigeminal, Abducens, Facial, Vestibulocochlear, Glossopharyngeal, Vagus, Accessory, Hypoglossal", S['small']),
        sp(3),
        Paragraph("<b>CN Function (Sensory/Motor/Both)</b>", S['body_bold']),
        Paragraph('"Some Say Marry Money But My Brother Says Big Brains Matter More"', S['body']),
        Paragraph("S M B M B B S B B M M B → Sensory/Motor/Both for CN I–XII", S['small']),
        sp(3),
        Paragraph("<b>Cerebellar signs: DANISH</b>", S['body_bold']),
        Paragraph("Dysdiadochokinesia, Ataxia, Nystagmus, Intention tremor, Scanning speech, Hypotonia", S['body']),
    ]

    right_mn = [
        Paragraph("<b>Dermatomes — Quick reference</b>", S['body_bold']),
        Paragraph("C3-4: Shoulder cape; C6: Thumb; C7: Middle finger; C8: Little finger", S['small']),
        Paragraph("T4: Nipple line; T10: Umbilicus; T12: Inguinal ligament", S['small']),
        Paragraph("L2: Anterior thigh; L3: Medial knee; L4: Medial leg; L5: Dorsum foot; S1: Lateral foot + sole", S['small']),
        sp(3),
        Paragraph("<b>MRC Scale: 0–5</b>", S['body_bold']),
        Paragraph("0 = None; 1 = Flicker; 2 = Gravity-free; 3 = vs Gravity; 4 = vs Resistance; 5 = Normal", S['body']),
        sp(3),
        Paragraph("<b>Aphasia types</b>", S['body_bold']),
        Paragraph("Broca's (expressive): fluency↓, comprehension intact, repetition↓", S['small']),
        Paragraph("Wernicke's (receptive): fluency normal, comprehension↓, repetition↓", S['small']),
        Paragraph("Conduction: fluency ± normal, comprehension intact, repetition↓↓", S['small']),
        Paragraph("Global: all modalities impaired (large MCA territory)", S['small']),
    ]
    elems.append(two_col(left_mn, right_mn))
    elems.append(sp(3))

    # Sources
    elems.append(HRFlowable(width=CONTENT_W, thickness=0.5, color=GREY_RULE))
    elems.append(sp(2))
    elems.append(Paragraph("<b>References / Sources</b>", S['small']))
    elems.append(Paragraph(
        "1. Adams and Victor's Principles of Neurology, 12th Edition (2023), McGraw-Hill — Chapter 1, pp. 14–21\n"
        "2. Bradley and Daroff's Neurology in Clinical Practice, 8th Edition — Chapter 1: Neurological Examination, Table 1.1\n"
        "3. Harrison's Principles of Internal Medicine, 22nd Edition (2025) — Neurological Examination sections\n"
        "4. DeMyer's Technique of the Neurologic Examination, 6th Edition", S['small']))
    elems.append(sp(2))
    return elems


# ── Main Build ───────────────────────────────────────────────────────────────
def build_pdf():
    doc = SimpleDocTemplate(
        OUTPUT_PATH,
        pagesize=A4,
        leftMargin=MARGIN, rightMargin=MARGIN,
        topMargin=MARGIN, bottomMargin=22 * mm,
        title="CNS Examination Reference Card",
        author="Orris Medical AI",
        subject="Comprehensive Neurological Examination",
    )

    story = []

    # Cover
    story.append(CoverPage())
    story.append(PageBreak())

    # Sections
    story.extend(build_mental_status())
    story.append(PageBreak())

    story.extend(build_cranial_nerves())
    story.append(PageBreak())

    story.extend(build_motor())
    story.append(PageBreak())

    story.extend(build_reflexes())
    story.append(PageBreak())

    story.extend(build_sensory())
    story.append(PageBreak())

    story.extend(build_gait())
    story.append(PageBreak())

    story.extend(build_special_signs())
    story.append(PageBreak())

    story.extend(build_quick_screen())

    doc.build(story, canvasmaker=NumberedCanvas)
    print(f"PDF generated: {OUTPUT_PATH}")
    return OUTPUT_PATH


if __name__ == "__main__":
    build_pdf()
Running Command

cd /home/daytona/workspace/cns-reference-card && python3 generate_cns_pdf.py 2>&1

Editing File

~/cns-reference-card/generate_cns_pdf.py

Successfully replaced 1 instance(s) of the string in '/home/daytona/workspace/cns-reference-card/generate_cns_pdf.py'
Editing File

~/cns-reference-card/generate_cns_pdf.py

Successfully replaced 1 instance(s) of the string in '/home/daytona/workspace/cns-reference-card/generate_cns_pdf.py'
Running Command

cd /home/daytona/workspace/cns-reference-card && python3 generate_cns_pdf.py 2>&1

Running Command

python3 -c " from pypdf import PdfReader r = PdfReader('/home/daytona/workspace/cns-reference-card/CNS_Examination_Reference_Card.pdf') import os size = os.path.getsize('/home/daytona/workspace/cns-reference-card/CNS_Examination_Reference_Card.pdf') print(f'Pages: {len(r.pages)}') print(f'Size: {size/1024:.1f} KB') print(f'Title: {r.metadata.title}') "

Your reference card is ready:

CNS Examination Reference Card

PDF Document · PDF


What's inside (12 pages)

PageSectionContents
1CoverFull navy/teal design with 8-section index
2Mental StatusConsciousness grading, attention tests, orientation, memory types, language components, praxis, visuospatial, frontal tests
3Cranial NervesAll 12 CNs - technique, key finding, localisation; RAPD test; Rinne/Weber interpretation table
4Motor SystemInspection, tone types (spasticity/rigidity/clonus), MRC 0-5 scale, upper + lower limb muscle testing with roots & nerves
5Motor cont.Screening manoeuvres (pronator drift, Barré, heel/toe walk), coordination tests (DANISH mnemonic)
6ReflexesDTR table with roots, grading 0-4+, Jendrassik, Babinski technique + equivalents (Oppenheim, Gordon, Chaddock, Bing), superficial reflexes
7SensoryPrimary modalities with pathways, cortical sensory tests, 8-pattern localisation table
8Gait & Station10 gait patterns with features and localisation, Romberg test, additional station tests
9Special SignsUMN vs LMN differentiation table, meningeal signs (Kernig, Brudzinski, jolt), raised ICP signs, autonomic examination
10-11Quick Screens10-point bedside neurological screen, key mnemonics (cranial nerves, dermatomes, aphasia types)
12ReferencesAdams & Victor 12e, Bradley & Daroff, Harrison's 22e, DeMyer's

Pdf not opening

Running Command

python3 -c " from pypdf import PdfReader r = PdfReader('/home/daytona/workspace/cns-reference-card/CNS_Examination_Reference_Card.pdf') print('Pages:', len(r.pages)) # Try reading first page text print('Page 1 text sample:', r.pages[0].extract_text()[:200]) " 2>&1

Your credits are exhausted. Your daily credits reset tomorrow, or you can purchase a top-up for more.

Generate a new PDF with simpler styling without pypdf merging

Your credits are exhausted. Your daily credits reset tomorrow, or you can purchase a top-up for more.

Localisation of stroke according to history and cns examination findings

Reading File
Finding Sources
Finding Sources
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.