Make pdf all this instrument with their image there identification and there use with there sterilization

Reading File
Running Command

cd /home/daytona/workspace/attachments/636fc29d-11bb-457c-b446-0764431f0ce2 && p…

Running Command

cd /home/daytona/workspace/attachments/636fc29d-11bb-457c-b446-0764431f0ce2 && p…

Running Command

mkdir -p /home/daytona/workspace/surgical-instruments && ls /home/daytona/worksp…

Writing File

~/surgical-instruments/generate_pdf.py

#!/usr/bin/env python3
"""
Surgical Instruments Reference PDF Generator
Shalya Tantra - OT Department
"""

import os
import urllib.request
import urllib.error
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import cm, mm
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
    PageBreak, Image, HRFlowable, KeepTogether
)
from reportlab.platypus.flowables import Flowable
from reportlab.pdfgen import canvas
from io import BytesIO

OUTPUT_PATH = "/home/daytona/workspace/surgical-instruments/Surgical_Instruments_Reference.pdf"

# ──────────────────────────────────────────────────────────────
# INSTRUMENT DATA
# ──────────────────────────────────────────────────────────────
instruments = [
    {
        "no": 1,
        "name": "Needle Holding Forceps (Needle Driver)",
        "variants": "Available in Big, Medium, Small sizes",
        "identification": (
            "Scissor-grip handle with ratchet lock; short, broad, cross-hatched jaws "
            "designed to grip needles firmly. Distinguished from artery forceps by its "
            "heavier, blunt jaws and stronger build."
        ),
        "uses": (
            "• Holds suturing needles during wound closure\n"
            "• Guides curved needles through tissue layers\n"
            "• Used in all surgical wound closure procedures\n"
            "• Essential in OT, minor OT, and procedure rooms"
        ),
        "sterilization": (
            "• Preferred: Steam autoclave at 121°C/15 psi for 15 min (gravity) "
            "or 134°C/30 psi for 4 min (pre-vacuum)\n"
            "• Alternative: Chemical sterilization (glutaraldehyde 2% for 10 hrs)\n"
            "• Avoid prolonged soaking which can corrode the ratchet\n"
            "• Flash sterilization acceptable for urgent cases"
        ),
        "image_query": "needle holder forceps surgical instrument",
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/7/7f/Needle_holder.jpg/320px-Needle_holder.jpg"
    },
    {
        "no": 2,
        "name": "Dressing Drums",
        "variants": "Assorted sizes",
        "identification": (
            "Cylindrical stainless-steel or aluminum containers with perforated sides "
            "and a sliding band/cover to open/close ventilation holes. Larger drums for "
            "bulky dressings, smaller for individual items."
        ),
        "uses": (
            "• Storage and sterilization of dressings, gauze, cotton swabs, towels\n"
            "• Maintains sterility of contents after autoclaving\n"
            "• Used in OT, procedure rooms, and minor OT\n"
            "• Holes opened during sterilization, closed after to maintain sterility"
        ),
        "sterilization": (
            "• Steam autoclave (open holes during sterilization cycle)\n"
            "• Close holes immediately after cycle completes\n"
            "• Sterility maintained for 7 days if drum remains unopened\n"
            "• Drum itself cleaned with disinfectant between uses"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/3/38/Dressing_drum.jpg/320px-Dressing_drum.jpg"
    },
    {
        "no": 3,
        "name": "Cheatle's Forceps",
        "variants": "Standard size; 4 recommended per OT",
        "identification": (
            "Long-handled forceps with ring/scissor handles; blades are curved, "
            "spoon-shaped, and angled away from the handle. Kept in antiseptic/disinfectant "
            "solution in a tall jar. Handles remain above the solution."
        ),
        "uses": (
            "• Transfer sterile instruments and supplies without contaminating them\n"
            "• Handling sterile dressings, bowls, and equipment from sterile containers\n"
            "• NOT used directly on patients\n"
            "• Maintains aseptic technique in OT"
        ),
        "sterilization": (
            "• Autoclave sterilized and stored in disinfectant solution (e.g., Savlon 1:30)\n"
            "• Solution and container changed every 24 hours\n"
            "• Forceps tips must remain submerged at all times\n"
            "• Do not touch the handles with sterile-gloved hands"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/5/5e/Cheatle_forceps.jpg/320px-Cheatle_forceps.jpg"
    },
    {
        "no": 4,
        "name": "Mosquito Forceps (Halsted Mosquito Forceps)",
        "variants": "Straight and curved; 4–6 required",
        "identification": (
            "Small, delicate haemostatic clamp with fine, fully serrated jaws and a "
            "box-lock hinge. Smaller than standard artery forceps. Ratchet handle with "
            "finger rings. Tips are narrow and pointed."
        ),
        "uses": (
            "• Clamping small blood vessels and bleeding points\n"
            "• Grasping delicate tissue\n"
            "• Holding suture material\n"
            "• Dissection in fine, delicate surgical fields\n"
            "• Paediatric and plastic surgery procedures"
        ),
        "sterilization": (
            "• Steam autoclave: 134°C, 3–4 min (pre-vacuum) or 121°C, 15 min (gravity)\n"
            "• Must be autoclaved in open/unlocked position\n"
            "• Ethylene oxide (ETO) for heat-sensitive variants\n"
            "• Inspect ratchet and tip alignment before each use"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/7/71/Mosquito_forceps.jpg/320px-Mosquito_forceps.jpg"
    },
    {
        "no": 5,
        "name": "Scissors Straight (Tailor / Operating Scissors)",
        "variants": "Straight pattern; 4 required",
        "identification": (
            "Scissor with straight blades, sharp-sharp or sharp-blunt tips. Ring handles "
            "with box joint. Distinguish from curved scissors by the flat, unbent blades. "
            "Various tip configurations (S/S, S/B, B/B)."
        ),
        "uses": (
            "• Cutting sutures (stitch/suture scissors)\n"
            "• Cutting dressings, drapes, and gauze\n"
            "• General tissue cutting in surgery\n"
            "• Opening packages in sterile field"
        ),
        "sterilization": (
            "• Steam autoclave preferred\n"
            "• Keep blades slightly open during sterilization\n"
            "• Inspect blade sharpness and alignment before use\n"
            "• Chemical sterilization (glutaraldehyde) for heat-sensitive types\n"
            "• Lubricate hinge joint after cleaning"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/1/1e/Operating_scissors.jpg/320px-Operating_scissors.jpg"
    },
    {
        "no": 6,
        "name": "Scissors Curved",
        "variants": "Different sizes; 5 required",
        "identification": (
            "Scissors with blades curved along their length (upward curve). Ring handles. "
            "Mayo scissors are heavier with semi-blunt tips; Metzenbaum scissors are lighter "
            "with longer shanks relative to blades. Curved profile visible from the side."
        ),
        "uses": (
            "• Mayo curved: Cutting heavy tissues, fascia, muscle\n"
            "• Metzenbaum: Delicate tissue dissection in deeper surgical fields\n"
            "• Undermining tissues and blunt dissection\n"
            "• Cutting in confined or deep spaces"
        ),
        "sterilization": (
            "• Steam autoclave: standard cycle with blades open\n"
            "• ETO gas sterilization for delicate instruments\n"
            "• Dry heat at 160–170°C for 60–120 min (less preferred)\n"
            "• Regular sharpness testing with test material"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/5/5a/Mayo_scissors.jpg/320px-Mayo_scissors.jpg"
    },
    {
        "no": 7,
        "name": "Stitch Removal Scissors",
        "variants": "5 required",
        "identification": (
            "Small scissors with one pointed, hooked/curved lower blade that slides "
            "under sutures without injury. The hook distinguishes it immediately from "
            "other scissors. May be angled or straight."
        ),
        "uses": (
            "• Removal of non-absorbable skin sutures post-surgery\n"
            "• Hook blade slides under stitch loop to cut cleanly\n"
            "• Used in ward, OPD, and procedure rooms\n"
            "• Minimizes patient discomfort during suture removal"
        ),
        "sterilization": (
            "• Steam autoclave after each use\n"
            "• Individual patient use; discard single-use disposable variants\n"
            "• Clean, dry, and inspect hook blade for sharpness\n"
            "• Store in sterile pack or tray"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/e/e0/Stitch_scissors.jpg/320px-Stitch_scissors.jpg"
    },
    {
        "no": 8,
        "name": "Dissection Forceps (Tissue Forceps / Thumb Forceps)",
        "variants": "Toothed and non-toothed; 4 required",
        "identification": (
            "Tweezer-type instrument with no ring handles—held between thumb and fingers "
            "like a pen. Toothed (rat-tooth/mouse-tooth) or non-toothed (plain/atraumatic) "
            "tips. Spring tension keeps tips open at rest."
        ),
        "uses": (
            "• Toothed: Grasping skin, fascia, and tough tissues\n"
            "• Non-toothed: Handling delicate tissues, vessels, and mucosa\n"
            "• Stabilizing tissue during suturing\n"
            "• Assisting in wound exploration and dressing changes"
        ),
        "sterilization": (
            "• Steam autoclave: standard cycle\n"
            "• Chemical sterilization in glutaraldehyde 2% for 8–10 hrs\n"
            "• Inspect teeth alignment under magnification\n"
            "• Light lubrication of spring mechanism after sterilization"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3f/Tissue_forceps.jpg/320px-Tissue_forceps.jpg"
    },
    {
        "no": 9,
        "name": "Sinus Forceps",
        "variants": "4 required",
        "identification": (
            "Thin, probe-like forceps with narrow, blunt-tipped beaks. Often gently curved. "
            "Ring handles. Designed to enter and explore sinus tracts without causing trauma."
        ),
        "uses": (
            "• Exploration and probing of sinus tracts and fistulae\n"
            "• Holding dressing material within a wound cavity\n"
            "• Introducing packing material into sinuses\n"
            "• Pilonidal sinus, anal fistula, and wound sinus procedures"
        ),
        "sterilization": (
            "• Steam autoclave (121°C, 15 min)\n"
            "• Unlock ratchet before autoclaving\n"
            "• ETO gas sterilization acceptable\n"
            "• Inspect tip narrowness and alignment before use"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/0/07/Sinus_forceps.jpg/320px-Sinus_forceps.jpg"
    },
    {
        "no": 10,
        "name": "Probes (Assorted Sizes)",
        "variants": "Assorted sizes; 5 required",
        "identification": (
            "Slender, blunt-ended metal (stainless steel or silver) rods, straight or "
            "malleable. May have a groove (grooved director) or button tip. Graduated "
            "markings on some. Malleable silver probes can be bent to desired angle."
        ),
        "uses": (
            "• Probing wounds, sinuses, fistulae, and abscesses\n"
            "• Determining depth and direction of tracts\n"
            "• Guiding scalpel along grooved director during fistulotomy\n"
            "• Dilation of small orifices and passages\n"
            "• Shalya Tantra: tract identification in Bhagandara (fistula-in-ano)"
        ),
        "sterilization": (
            "• Steam autoclave: standard cycle\n"
            "• Chemical sterilization for silver probes (avoid high temp discolouration)\n"
            "• Boiling in water for 20 min as field alternative\n"
            "• Store dry to prevent corrosion"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/4/4b/Surgical_probe.jpg/320px-Surgical_probe.jpg"
    },
    {
        "no": 11,
        "name": "Pointed Scissors",
        "variants": "4 required",
        "identification": (
            "Scissors with both blades ending in sharp, pointed tips (sharp-sharp). "
            "Smaller profile than operating scissors. Ring handles. Used for precise, "
            "fine cutting where blunt tips would be inadequate."
        ),
        "uses": (
            "• Precise incisions into tissues\n"
            "• Cutting adhesions in confined spaces\n"
            "• Fine dissection around neurovascular bundles\n"
            "• Trimming wound edges"
        ),
        "sterilization": (
            "• Steam autoclave with blades open\n"
            "• Handle with care—sharp tips can puncture sterilization pouches\n"
            "• Protect tips with caps during storage\n"
            "• ETO for delicate/fine scissors"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/3/38/Sharp_scissors_surgical.jpg/320px-Sharp_scissors_surgical.jpg"
    },
    {
        "no": 12,
        "name": "Gastric and Intestinal Clamps",
        "variants": "Occlusive and crushing types",
        "identification": (
            "Long-handled clamps with serrated or cross-hatched jaws. Occlusive clamps "
            "(non-crushing) have atraumatic, padded jaws for temporary bowel occlusion. "
            "Crushing clamps have heavy, deeply serrated jaws to crush bowel wall. "
            "Payr clamp is a well-known crushing type."
        ),
        "uses": (
            "• Occlusive: Temporarily occlude bowel lumen without damage during anastomosis\n"
            "• Crushing: Mark resection line on bowel and crush tissue before resection\n"
            "• Prevent spillage of intestinal contents during bowel surgery\n"
            "• Gastric resections and intestinal anastomosis procedures"
        ),
        "sterilization": (
            "• Steam autoclave: standard pre-vacuum cycle\n"
            "• Unlock and open clamp jaws during sterilization\n"
            "• Remove any rubber/silicone jaw covers before autoclaving\n"
            "• Clean jaw serrations thoroughly with brush before sterilization"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/8/8f/Intestinal_clamp.jpg/320px-Intestinal_clamp.jpg"
    },
    {
        "no": 13,
        "name": "Abdominal Retractors",
        "variants": "5 required (e.g., Deaver, Morris, Richardson)",
        "identification": (
            "Large, blade-type retractors with a curved/angled blade attached to a handle. "
            "Deaver: wide curved blade. Morris: rectangular blade. Available in different "
            "blade widths and lengths. Usually held by an assistant."
        ),
        "uses": (
            "• Retracting abdominal wall during laparotomy\n"
            "• Exposing abdominal and pelvic organs\n"
            "• Holding viscera aside for operative field exposure\n"
            "• Biliary, gastric, colonic, and gynaecological procedures"
        ),
        "sterilization": (
            "• Steam autoclave: standard cycle\n"
            "• Wipe blade clean of tissue and blood before processing\n"
            "• No special precautions needed for solid metal retractors\n"
            "• Inspect blade edges for sharpness that could injure tissue"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/d3/Deaver_retractor.jpg/320px-Deaver_retractor.jpg"
    },
    {
        "no": 14,
        "name": "Tissue Forceps",
        "variants": "5 required (Allis, Lane's, etc.)",
        "identification": (
            "Ring-handled forceps with interlocking fine teeth at tips (multiple fine teeth). "
            "Allis forceps: curved tips with multiple fine teeth. Distinguish from Babcock's "
            "by teeth vs. smooth fenestrated blades. Ratchet mechanism."
        ),
        "uses": (
            "• Grasping and holding thick tissues (fascia, muscle)\n"
            "• Retraction of wound edges\n"
            "• Holding specimen for excision\n"
            "• Allis: holding bowel serosa, peritoneum, skin edges"
        ),
        "sterilization": (
            "• Steam autoclave in open/unlocked position\n"
            "• Clean teeth with brush to remove tissue debris\n"
            "• Inspect tooth alignment before each use\n"
            "• ETO sterilization as alternative"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/1/12/Allis_forceps.jpg/320px-Allis_forceps.jpg"
    },
    {
        "no": 15,
        "name": "Babcock's Forceps",
        "variants": "5 required",
        "identification": (
            "Ring-handled, ratcheted forceps with broad, fenestrated (windowed), "
            "triangular/spoon-shaped blade tips. No teeth. Tips are smooth and atraumatic. "
            "Wider jaw opening than Allis forceps."
        ),
        "uses": (
            "• Atraumatic grasping of hollow organs (bowel, fallopian tube, appendix)\n"
            "• Holding delicate structures without crushing\n"
            "• Retracting bowel loops\n"
            "• Appendicectomy, bowel surgery, gynaecological procedures"
        ),
        "sterilization": (
            "• Steam autoclave: standard cycle, instrument open\n"
            "• Fenestrated tips must be cleaned thoroughly inside the window\n"
            "• Inspect fenestration edges for smoothness\n"
            "• Chemical sterilization acceptable"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/b/b9/Babcock_forceps.jpg/320px-Babcock_forceps.jpg"
    },
    {
        "no": 16,
        "name": "Kocher's Forceps (Kocher Clamp)",
        "variants": "5 required",
        "identification": (
            "Heavy artery forceps with transverse serrations along the entire jaw length "
            "plus 1–2 interlocking teeth at the tip. The distinctive distal teeth "
            "immediately identify it. Strong ratchet, ring handles. Straight or curved."
        ),
        "uses": (
            "• Clamping the cystic duct and cystic artery during cholecystectomy\n"
            "• Holding tough structures and fascia\n"
            "• Thyroid gland clamping\n"
            "• Bone fragments and tough fibrous tissues\n"
            "• Crushing and holding tissue for excision"
        ),
        "sterilization": (
            "• Steam autoclave: standard cycle, open/unlocked\n"
            "• Clean between teeth with fine brush\n"
            "• Inspect teeth interdigitation\n"
            "• Flash sterilization for urgent cases"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/5/56/Kocher_forceps.jpg/320px-Kocher_forceps.jpg"
    },
    {
        "no": 17,
        "name": "Urethral Dilators",
        "variants": "1 set (bougie-à-boule / metal sounds, assorted sizes)",
        "identification": (
            "Series of graduated metal or plastic curved/straight rods with olive-shaped "
            "or tapered tips. Numbered by French gauge (Fr). Commonly Clutton's or "
            "McCrea's sounds; smaller ones for initial dilation, larger for maintenance."
        ),
        "uses": (
            "• Dilation of urethral strictures\n"
            "• Calibrating urethral lumen\n"
            "• Facilitating urethral catheterization in strictures\n"
            "• Diagnostic assessment of urethral patency"
        ),
        "sterilization": (
            "• Steam autoclave for metal dilators\n"
            "• Chemical sterilization (glutaraldehyde 2%) for plastic/rubber dilators\n"
            "• Single-use plastic dilators should be discarded after use\n"
            "• Calibrated instruments kept dry to prevent pitting"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/9/9a/Urethral_dilators.jpg/320px-Urethral_dilators.jpg"
    },
    {
        "no": 18,
        "name": "Rubber Catheters (Foley/Nelaton)",
        "variants": "Assorted sizes No. 10, 12; disposable",
        "identification": (
            "Flexible latex or silicone tubes of various diameters (Fr gauge). "
            "Foley catheter: balloon near tip for retention; two or three lumens. "
            "Nelaton: simple straight-tipped with one lumen and two lateral eyes. "
            "Colour-coded by size at funnel end."
        ),
        "uses": (
            "• Urinary drainage in retention or surgery\n"
            "• Continuous bladder irrigation\n"
            "• Measurement of urine output in ICU/OT\n"
            "• Nelaton: intermittent catheterization and urethral calibration"
        ),
        "sterilization": (
            "• SINGLE USE disposable—do not resterilize latex/silicone Foley catheters\n"
            "• Reusable rubber catheters: autoclave at 121°C (check manufacturer)\n"
            "• Chemical sterilization in glutaraldehyde 2% for 6–10 hrs for reusables\n"
            "• Store in sterile individual packs"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/4/4b/Foley_catheter.jpg/320px-Foley_catheter.jpg"
    },
    {
        "no": 19,
        "name": "Metal Catheters",
        "variants": "5 required; straight and curved",
        "identification": (
            "Rigid stainless-steel tubes with smooth, rounded tip and two lateral eyes. "
            "Male catheter: long and gently curved (J-shape). Female catheter: short and "
            "slightly curved. Funnel/Luer fitting at proximal end. French gauge marked."
        ),
        "uses": (
            "• Urinary catheterization when flexible catheter fails (strictures)\n"
            "• Irrigation of the bladder\n"
            "• Suprapubic drainage assistance\n"
            "• Calibration of urethra"
        ),
        "sterilization": (
            "• Steam autoclave: 134°C, 3–4 min (best for metal instruments)\n"
            "• Boiling in water for 20 min acceptable in resource-limited settings\n"
            "• Chemical sterilization in glutaraldehyde 2%\n"
            "• Dry thoroughly after cleaning before autoclaving"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/9/9b/Metal_catheter.jpg/320px-Metal_catheter.jpg"
    },
    {
        "no": 20,
        "name": "Suturing Needles",
        "variants": "Straight and curved, assorted sizes",
        "identification": (
            "Needles with cutting (triangular cross-section at tip) or round-bodied (tapered) "
            "profiles. Curved needles: 1/4, 3/8, 1/2, 5/8 circle. Straight needles for "
            "skin. Eye-type or atraumatic (swaged, eyeless) attachments. "
            "Cutting needle: 3 cutting edges visible on cross-section."
        ),
        "uses": (
            "• Cutting needles: skin, fascia, and tough tissues\n"
            "• Round-bodied needles: bowel, muscle, vessels\n"
            "• Curved needles: deep tissue suturing with needle holder\n"
            "• Straight needles: skin closure, subcuticular suturing"
        ),
        "sterilization": (
            "• Swaged (atraumatic) needles: single use, pre-sterilized by manufacturer\n"
            "• Reusable needles: steam autoclave\n"
            "• Inspect sharpness and bend before use; discard bent or blunt needles\n"
            "• Store in needle book or rack to prevent damage"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/9/9c/Suture_needles.jpg/320px-Suture_needles.jpg"
    },
    {
        "no": 21,
        "name": "Surgical Thread (Suture Material)",
        "variants": "Vicryl 2-0, 3-0; Ethilon 2-0, 3-0, 4-0",
        "identification": (
            "Vicryl (polyglactin 910): braided, absorbable, violet or undyed. "
            "Ethilon (nylon): monofilament, non-absorbable, black/blue. "
            "Size: 0 (thickest) to 10-0 (finest). Colour-coded needles and packets."
        ),
        "uses": (
            "• Vicryl 2-0/3-0: Subcutaneous tissue, fascial closure, peritoneum\n"
            "• Ethilon 2-0/3-0: Skin closure, tendon repair\n"
            "• Ethilon 4-0: Fine skin closure, vascular anastomosis\n"
            "• General wound closure in OT procedures"
        ),
        "sterilization": (
            "• Pre-sterilized by manufacturer (ETO or radiation)\n"
            "• Do NOT resterilize; once packet opened, use immediately\n"
            "• Store in cool, dry place away from moisture\n"
            "• Check expiry date before use"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/6/6a/Surgical_sutures.jpg/320px-Surgical_sutures.jpg"
    },
    {
        "no": 22,
        "name": "Sponge Holding Forceps (Rampley Sponge Holder)",
        "variants": "4 required",
        "identification": (
            "Long forceps with ring handles, ratchet, and oval fenestrated (ring-shaped) "
            "distal jaws. The large circular/ovoid fenestrated jaw tip is the key identifier. "
            "No teeth. Jaws open widely."
        ),
        "uses": (
            "• Holding swabs/sponges for skin preparation (antiseptic scrubbing)\n"
            "• Cleaning and draping operative site\n"
            "• Holding gauze for wound mopping during surgery\n"
            "• Applying antiseptic solutions (povidone-iodine, chlorhexidine)"
        ),
        "sterilization": (
            "• Steam autoclave: standard cycle, open position\n"
            "• Clean fenestrated jaw interior thoroughly\n"
            "• ETO acceptable\n"
            "• Inspect ratchet for smooth locking"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/a/a1/Sponge_holder_forceps.jpg/320px-Sponge_holder_forceps.jpg"
    },
    {
        "no": 23,
        "name": "Right Angle (Cholecystectomy) Forceps",
        "variants": "4 required",
        "identification": (
            "Forceps with jaws bent at 90° to the shank. Ring handles with ratchet. "
            "Slender, curved jaws allow passage behind structures. Mixter forceps is a "
            "common variant. The right-angle bend is the defining feature."
        ),
        "uses": (
            "• Dissection and isolation of cystic duct/artery in cholecystectomy\n"
            "• Passing ligatures behind vessels and ducts\n"
            "• Isolation of structures in deep confined spaces\n"
            "• Thyroidectomy, hernia repair"
        ),
        "sterilization": (
            "• Steam autoclave: open/unlocked\n"
            "• Clean curved jaw tips with fine brush\n"
            "• Inspect jaw alignment for proper closure\n"
            "• ETO for heat-sensitive variants"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/b/bc/Right_angle_forceps.jpg/320px-Right_angle_forceps.jpg"
    },
    {
        "no": 24,
        "name": "Stone Holding Forceps",
        "variants": "4 required",
        "identification": (
            "Designed for grasp and extraction of calculi. Mayo-Blake type: broad, "
            "spoon-shaped fenestrated jaws. Randall kidney stone forceps: curved, "
            "cupped jaws. Ring handles with ratchet."
        ),
        "uses": (
            "• Extraction of gallstones during cholecystectomy/choledocholithotomy\n"
            "• Bladder stone removal (cystolithotomy)\n"
            "• Kidney stone extraction (nephrolithotomy)\n"
            "• Salivary calculus removal"
        ),
        "sterilization": (
            "• Steam autoclave: standard cycle\n"
            "• Clean fenestrated cup jaws thoroughly\n"
            "• Inspect jaw curvature and closure\n"
            "• Chemical sterilization (glutaraldehyde) alternative"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/5/52/Stone_forceps.jpg/320px-Stone_forceps.jpg"
    },
    {
        "no": 25,
        "name": "Proctoscope",
        "variants": "With/without illuminator; 2 required",
        "identification": (
            "Short, tubular metal/plastic speculum with obturator. Standard adult size "
            "~28 Fr, 7 cm length. Illuminated models have a light carrier or fibre-optic "
            "attachment. Obturator removed after insertion to view rectal lumen."
        ),
        "uses": (
            "• Direct visualisation of anal canal and lower rectum\n"
            "• Diagnosis of haemorrhoids, polyps, fissures, fistulae\n"
            "• Banding of haemorrhoids (used with Barron gun)\n"
            "• Biopsy of rectal lesions\n"
            "• Shalya Tantra: Arsha (haemorrhoid) examination"
        ),
        "sterilization": (
            "• Metal proctoscopes: steam autoclave\n"
            "• Plastic/disposable: single use and discard\n"
            "• Illuminated reusable: High-level disinfection (glutaraldehyde 2%, 20 min) "
            "or autoclave metal parts only\n"
            "• Clean inside lumen with brushes before sterilization"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3d/Proctoscope.jpg/320px-Proctoscope.jpg"
    },
    {
        "no": 26,
        "name": "Bougies (Hegar's/Megar's Uterine Dilators)",
        "variants": "2 required; assorted sizes",
        "identification": (
            "Set of graduated, rounded-tipped metal or plastic dilators in ascending size. "
            "Hegar's dilators are double-ended with each end a different size. "
            "Numbered in millimetre diameter or French gauge."
        ),
        "uses": (
            "• Dilation of cervical os before intrauterine procedures\n"
            "• Dilatation and curettage (D&C)\n"
            "• Hysteroscopy preparation\n"
            "• Urethral dilation (urological bougies)"
        ),
        "sterilization": (
            "• Steam autoclave for metal bougies\n"
            "• Chemical sterilization for plastic variants\n"
            "• Must be sterilized between patients\n"
            "• Store dry; check for corrosion before use"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/5/5d/Hegar_dilators.jpg/320px-Hegar_dilators.jpg"
    },
    {
        "no": 27,
        "name": "Allis Forceps (Small and Large)",
        "variants": "4 each of small and big; total 8",
        "identification": (
            "Ring-handled, ratcheted forceps with multiple fine interlocking teeth at tips. "
            "Small: narrower jaw; Large: broader jaw. The multi-toothed tips are defining. "
            "Slightly curved working end."
        ),
        "uses": (
            "• Grasping and holding tissue for retraction or excision\n"
            "• Holding bowel, fascia, wound edges\n"
            "• Appendix holding during appendicectomy\n"
            "• Rectal and vaginal wall procedures"
        ),
        "sterilization": (
            "• Steam autoclave in open position\n"
            "• Clean teeth with fine wire brush\n"
            "• Inspect tooth interdigitation before use\n"
            "• Chemical sterilization as alternative"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/1/12/Allis_forceps.jpg/320px-Allis_forceps.jpg"
    },
    {
        "no": 28,
        "name": "Pile Holding Forceps",
        "variants": "4 required",
        "identification": (
            "Specifically designed for haemorrhoid surgery. Often similar to Allis or "
            "sponge forceps but may have smooth or lightly serrated jaws. Used in "
            "anorectal procedures. Ring handles with ratchet."
        ),
        "uses": (
            "• Grasping haemorrhoidal tissue for excision\n"
            "• Holding and reducing prolapsed haemorrhoids\n"
            "• Haemorrhoidectomy procedures\n"
            "• Shalya Tantra: Arsha chhedana (haemorrhoid excision)"
        ),
        "sterilization": (
            "• Steam autoclave: standard cycle\n"
            "• Thoroughly clean jaws before sterilization\n"
            "• Single-use alternatives preferred in high-risk patients\n"
            "• ETO acceptable"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/3/38/Pile_forceps.jpg/320px-Pile_forceps.jpg"
    },
    {
        "no": 29,
        "name": "Artery Forceps (Haemostats)",
        "variants": "Small, Medium, Large; 4 each",
        "identification": (
            "Ring-handled, ratcheted forceps with serrated jaws (fully serrated transversely). "
            "No teeth at tip (differentiates from Kocher's). Kelly forceps: half-serrated. "
            "Halsted mosquito: smallest. Straight or curved variants."
        ),
        "uses": (
            "• Clamping blood vessels to achieve haemostasis\n"
            "• Holding cut ends of vessels for ligation or cautery\n"
            "• General tissue grasping in surgery\n"
            "• Large: major vessels; Small: capillaries/small vessels"
        ),
        "sterilization": (
            "• Steam autoclave: standard cycle, instrument open/unlocked\n"
            "• Clean serrations with brush\n"
            "• Flash autoclave for urgent use\n"
            "• Inspect jaw alignment and ratchet function before use"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/8/8e/Artery_forceps.jpg/320px-Artery_forceps.jpg"
    },
    {
        "no": 30,
        "name": "Sigmoidoscope (Rigid or Flexible)",
        "variants": "1 required (optional in newer MSR)",
        "identification": (
            "Rigid: metal/plastic tube ~25 cm long with obturator and bellows insufflator. "
            "Flexible: fibre-optic/video-endoscope ~60 cm, with angulation controls and "
            "biopsy channel. Rigid is shorter and simpler."
        ),
        "uses": (
            "• Examination of sigmoid colon and rectum\n"
            "• Biopsy of lesions in distal colon\n"
            "• Polypectomy\n"
            "• Diagnosis of inflammatory bowel disease, carcinoma\n"
            "• Foreign body removal from sigmoid colon"
        ),
        "sterilization": (
            "• Rigid metal: steam autoclave\n"
            "• Flexible fibre-optic: HIGH-LEVEL DISINFECTION only (glutaraldehyde 2%, 20–45 min) "
            "or automated endoscope reprocessor (AER)\n"
            "• Never autoclave flexible endoscopes\n"
            "• Follow manufacturer's reprocessing guidelines strictly"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/9/9c/Sigmoidoscope.jpg/320px-Sigmoidoscope.jpg"
    },
    {
        "no": 31,
        "name": "Barron Pile's Gun (Haemorrhoid Banding Device)",
        "variants": "1 required",
        "identification": (
            "Pistol-grip device with a hollow cylindrical drum at the end. Loaded with "
            "rubber bands. Trigger mechanism fires rubber band onto haemorrhoidal tissue. "
            "Drum fits over proctoscope for placement."
        ),
        "uses": (
            "• Rubber band ligation of internal haemorrhoids (Grade I–III)\n"
            "• Outpatient/OPD procedure with proctoscope\n"
            "• Cuts off blood supply causing haemorrhoid to slough off\n"
            "• Shalya Tantra: Non-surgical management of Arsha"
        ),
        "sterilization": (
            "• Metal parts: steam autoclave\n"
            "• Plastic components: high-level disinfection or single-use\n"
            "• Rubber bands: sterile, single-use only\n"
            "• Disassemble and clean all components before sterilization"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/4/4a/Barron_banding.jpg/320px-Barron_banding.jpg"
    },
    {
        "no": 32,
        "name": "Laryngoscope (Paediatric/Adult)",
        "variants": "1 required",
        "identification": (
            "Handle (battery-containing) with detachable blade. Miller blade: straight. "
            "Macintosh blade: curved. Light at distal tip. Fibre-optic or conventional bulb. "
            "Paediatric blades: smaller, Miller 0–1. Adult: Miller 2–3, Mac 3–4."
        ),
        "uses": (
            "• Visualisation of glottis for endotracheal intubation\n"
            "• Airway management in anaesthesia\n"
            "• Foreign body removal from larynx/pharynx\n"
            "• Emergency airway access"
        ),
        "sterilization": (
            "• Blade: high-level disinfection (glutaraldehyde 2%) or autoclavable blades in autoclave\n"
            "• Handle: wipe with 70% isopropyl alcohol (do not immerse)\n"
            "• Fibre-optic cable end: clean with alcohol wipe\n"
            "• Test light before each use"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/6/62/Laryngoscope.jpg/320px-Laryngoscope.jpg"
    },
    {
        "no": 33,
        "name": "Ambu Bag (Self-Inflating Resuscitation Bag)",
        "variants": "2 required",
        "identification": (
            "Self-inflating silicone/rubber bag with one-way valve, mask connector, "
            "and oxygen reservoir bag. Adult: ~1600 mL bag. Paediatric: ~450–500 mL. "
            "Neonatal: ~240 mL. Transparent mask allows visualisation of lips."
        ),
        "uses": (
            "• Manual ventilation during cardiopulmonary resuscitation (CPR)\n"
            "• Pre-oxygenation before intubation\n"
            "• Assisting ventilation in respiratory failure\n"
            "• Emergency airway management"
        ),
        "sterilization": (
            "• Disassemble all components before sterilization\n"
            "• Bag and valve: autoclave or ETO (check manufacturer)\n"
            "• Mask: autoclave or glutaraldehyde 2%\n"
            "• Most modern bags are single-patient use or disposable"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/f/f6/Ambu_bag.jpg/320px-Ambu_bag.jpg"
    },
    {
        "no": 34,
        "name": "Suction Machine (Electric/Manual)",
        "variants": "1 required",
        "identification": (
            "Electric: motor-driven pump with collection canister, tubing, and suction tip. "
            "Yankauer suction tip: rigid, curved, large bore for oropharyngeal use. "
            "Frazier tip: fine, angled for surgical field. Manual (foot-pump) models exist for field use."
        ),
        "uses": (
            "• Clearing oropharyngeal secretions during surgery/anaesthesia\n"
            "• Wound drainage and field clearance during OT procedures\n"
            "• Gastric decompression\n"
            "• Neonatal resuscitation"
        ),
        "sterilization": (
            "• Suction tips (Yankauer, Frazier): autoclave or single-use\n"
            "• Tubing: single-use disposable\n"
            "• Collection canister: disinfect with hypochlorite before disposal\n"
            "• Machine external: wipe with 70% alcohol"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/5/55/Suction_machine.jpg/320px-Suction_machine.jpg"
    },
    {
        "no": 35,
        "name": "Skin Grafting Knife",
        "variants": "With handle; assorted",
        "identification": (
            "Long, thin, sharp blade mounted on a handle. Humby knife: has adjustable "
            "guard to set graft thickness. Watson modification common. Blair knife: "
            "longer freehand blade without guard."
        ),
        "uses": (
            "• Harvesting split-thickness skin grafts from donor site\n"
            "• Setting blade gap determines graft thickness (0.2–0.4 mm)\n"
            "• Plastic surgery, burn wound coverage\n"
            "• Donor sites: thigh, buttock, back"
        ),
        "sterilization": (
            "• Metal handle: steam autoclave\n"
            "• Blade: single-use, change for each procedure\n"
            "• Adjustable guard: autoclave after disassembly\n"
            "• Dry all metal parts before autoclaving"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/a/a5/Humby_knife.jpg/320px-Humby_knife.jpg"
    },
    {
        "no": 36,
        "name": "Surgical Blades",
        "variants": "No. 11 (1 box), No. 15 (1 box); assorted sizes",
        "identification": (
            "No. 10: large, curved belly — most common for general incisions. "
            "No. 11: pointed, triangular — stab incisions, drains. "
            "No. 15: small, curved — fine work, facial, paediatric. "
            "No. 22/23: larger curved belly for abdominal work."
        ),
        "uses": (
            "• No. 10: Skin incisions in general surgery\n"
            "• No. 11: Stab incisions, abscess drainage, arteriotomy\n"
            "• No. 15: Ear, nose, face, fine plastic surgery\n"
            "• Mounted on BP handle before use"
        ),
        "sterilization": (
            "• Pre-sterilized by manufacturer (ETO or radiation sterilized)\n"
            "• Single-use: NEVER resterilize or reuse\n"
            "• Dispose in sharps container immediately after use\n"
            "• Remove from handle using needle holder — never by hand"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/4/4e/Surgical_blades.jpg/320px-Surgical_blades.jpg"
    },
    {
        "no": 37,
        "name": "BP Handle (Scalpel Handle)",
        "variants": "Different sizes: No. 3, 4, 7; assorted",
        "identification": (
            "Stainless-steel handle with blade-mounting slot at distal end. "
            "No. 3 handle: takes blades 10, 11, 12, 15. "
            "No. 4 handle: takes blades 20, 21, 22, 23. "
            "No. 7: long, thin handle for fine work. Grip pattern varies."
        ),
        "uses": (
            "• Holds surgical blades for making incisions\n"
            "• Different handles for different blade sizes\n"
            "• Used in all surgical procedures requiring skin/tissue incision"
        ),
        "sterilization": (
            "• Steam autoclave: standard cycle\n"
            "• Remove blade before autoclaving\n"
            "• Clean blade slot to remove dried blood\n"
            "• Inspect slot mechanism for secure blade fitting"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/6/6c/Scalpel_handle.jpg/320px-Scalpel_handle.jpg"
    },
    {
        "no": 38,
        "name": "Self-Retaining Retractor",
        "variants": "Assorted (Weitlaner, Balfour, Finsen, Gelpi)",
        "identification": (
            "Retractor that holds itself open without assistant. Weitlaner: hinged "
            "with multiple-toothed arms, ratchet. Balfour: lateral blades + centre "
            "blade for abdominal work. Gelpi: single sharp-toothed arms. "
            "Ratchet mechanism keeps wound open."
        ),
        "uses": (
            "• Maintaining wound exposure without assistant holding\n"
            "• Abdominal, thoracic, and orthopaedic procedures\n"
            "• Balfour: laparotomy and pelvic surgery\n"
            "• Gelpi/Weitlaner: superficial wounds, neck, orthopaedics"
        ),
        "sterilization": (
            "• Steam autoclave: standard cycle\n"
            "• Inspect ratchet mechanism before use\n"
            "• Clean between teeth with brush\n"
            "• Lubricate ratchet after sterilization if stiff"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/0/04/Weitlaner_retractor.jpg/320px-Weitlaner_retractor.jpg"
    },
    {
        "no": 39,
        "name": "Bone Drill Machine",
        "variants": "1 required; electric or pneumatic",
        "identification": (
            "Power-driven drill with chuck to hold drill bits/K-wires/screws. "
            "Electric (corded/battery) or pneumatic (compressed air/nitrogen). "
            "Orthopaedic drills have variable speed and torque control. "
            "Jacob's chuck or AO-style coupling."
        ),
        "uses": (
            "• Drilling holes in bone for screws, wires, pins\n"
            "• Cortical and cancellous bone drilling\n"
            "• Internal fixation of fractures\n"
            "• Orthopaedic and maxillofacial procedures"
        ),
        "sterilization": (
            "• Autoclavable handpiece: steam autoclave\n"
            "• Non-autoclavable motors: ETO or surface disinfection per manufacturer\n"
            "• Drill bits: autoclave (reusable) or single-use\n"
            "• Battery packs: wipe with alcohol — do not autoclave"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/8/8b/Bone_drill.jpg/320px-Bone_drill.jpg"
    },
    {
        "no": 40,
        "name": "Bone Cutter",
        "variants": "2 required",
        "identification": (
            "Heavy-duty, plier-like instrument with sharp cutting blades at the tip. "
            "Liston bone cutter: side-cutting, straight blades. "
            "Rongeur: curved, scoop-shaped cutting edges for nibbling bone. "
            "Strong spring mechanism returns handles after each cut."
        ),
        "uses": (
            "• Cutting and shaping bone during orthopaedic and trauma surgery\n"
            "• Amputation procedures\n"
            "• Rongeur: removing bone fragments, laminectomy\n"
            "• Creating bone troughs and channels"
        ),
        "sterilization": (
            "• Steam autoclave: standard pre-vacuum cycle\n"
            "• Clean cutting edges and box-joint thoroughly\n"
            "• Lubricate joint with surgical lubricant after sterilization\n"
            "• Inspect cutting edges for nicks before use"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/a/a7/Bone_cutter.jpg/320px-Bone_cutter.jpg"
    },
    {
        "no": 41,
        "name": "Gigli Saw",
        "variants": "2 required",
        "identification": (
            "Flexible, twisted wire saw with two ring/T-bar handles at each end. "
            "The wire has small cutting teeth along its length. Flexible — can be "
            "passed around bone with a flexible guide."
        ),
        "uses": (
            "• Cutting bone in limited access areas\n"
            "• Craniotomy (cutting skull)\n"
            "• Amputation — cutting through long bones\n"
            "• Symphysiotomy (cutting pubic symphysis)"
        ),
        "sterilization": (
            "• Steam autoclave: can withstand standard cycle\n"
            "• Handles: autoclave separately\n"
            "• Single-use modern variants available — discard after use\n"
            "• Inspect wire for kinks or breaks before use"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/7/7c/Gigli_saw.jpg/320px-Gigli_saw.jpg"
    },
    {
        "no": 42,
        "name": "Bone Scoop (Curette)",
        "variants": "Assorted sizes",
        "identification": (
            "Spoon or cup-shaped working end at tip of a long handle. Volkmann spoon: "
            "double-ended with different sized spoons. Sharp or blunt cup. "
            "Handles may be flat or round."
        ),
        "uses": (
            "• Scooping out infected bone, granulation tissue, or sequestrum\n"
            "• Curettage of bone cavities and cysts\n"
            "• Removing disc material in spinal surgery\n"
            "• Bone marrow biopsy"
        ),
        "sterilization": (
            "• Steam autoclave: standard cycle\n"
            "• Clean cup interior with brush\n"
            "• Inspect sharpness of cup edges\n"
            "• ETO acceptable"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/9/9e/Bone_curette.jpg/320px-Bone_curette.jpg"
    },
    {
        "no": 43,
        "name": "Periosteum Elevator",
        "variants": "2 required (Faraboef, Langenbeck)",
        "identification": (
            "Flat, blade-like instrument with a blunt chisel-shaped working end. "
            "Rugine/Faraboef: flat, mirror-like blade. Langenbeck: L-shaped, double-ended. "
            "Solid metal handle with textured grip."
        ),
        "uses": (
            "• Stripping periosteum (bone membrane) from bone surface\n"
            "• Exposure of bone before cutting/plating\n"
            "• Fracture reduction and soft tissue dissection from bone\n"
            "• Used in all orthopaedic and oral/maxillofacial procedures"
        ),
        "sterilization": (
            "• Steam autoclave: standard cycle\n"
            "• No special precautions for solid metal instruments\n"
            "• Clean blade edge of tissue before autoclaving\n"
            "• Inspect blade for bending"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/b/b8/Periosteum_elevator.jpg/320px-Periosteum_elevator.jpg"
    },
    {
        "no": 44,
        "name": "Magill Forceps",
        "variants": "Assorted (adult and paediatric)",
        "identification": (
            "Angled (offset) ring forceps with oval, non-toothed blades. The handles "
            "are offset from the blades at nearly 90° allowing visualization while "
            "manipulating objects in the airway. Available in different sizes."
        ),
        "uses": (
            "• Guiding endotracheal tube through vocal cords during nasal intubation\n"
            "• Removing foreign bodies from pharynx/larynx\n"
            "• Directing nasogastric tubes\n"
            "• Airway management in anaesthesia"
        ),
        "sterilization": (
            "• Steam autoclave: standard cycle\n"
            "• High-level disinfection (glutaraldehyde 2%) acceptable\n"
            "• Clean inside jaw surfaces\n"
            "• Inspect jaw alignment before use"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/6/6f/Magill_forceps.jpg/320px-Magill_forceps.jpg"
    },
    {
        "no": 45,
        "name": "Endotracheal Tubes",
        "variants": "Assorted sizes (neonatal to adult)",
        "identification": (
            "Flexible PVC/silicone tube with standard 15mm connector, size markings (ID mm), "
            "radio-opaque line, depth markers, and inflatable cuff (cuffed) or no cuff "
            "(uncuffed, for children <8 years). Murphy eye at distal end."
        ),
        "uses": (
            "• Securing airway during general anaesthesia\n"
            "• Mechanical ventilation in ICU\n"
            "• Preventing aspiration\n"
            "• Drug administration route in cardiac arrest (lidocaine, epinephrine)"
        ),
        "sterilization": (
            "• SINGLE USE: modern PVC ETT are disposable — do not resterilize\n"
            "• Reusable silicone/rubber tubes: autoclave or ETO per manufacturer\n"
            "• Check cuff integrity before use (inflate with 10 mL air, check for leaks)\n"
            "• Store in individual sterile packs"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/4/4d/Endotracheal_tube.jpg/320px-Endotracheal_tube.jpg"
    },
    {
        "no": 46,
        "name": "High Pressure Autoclave",
        "variants": "1 required",
        "identification": (
            "Pressure vessel (horizontal or vertical) with sealed door, pressure gauge, "
            "temperature display, safety valve, and timer. High-pressure autoclave operates "
            "at >15 psi. Pre-vacuum models have vacuum pump for air removal before steam entry."
        ),
        "uses": (
            "• Steam sterilization of surgical instruments, dressing drums, gowns\n"
            "• Gravity cycle (121°C/15 psi, 15–30 min): unwrapped instruments\n"
            "• Pre-vacuum cycle (134°C/30 psi, 3–4 min): porous loads\n"
            "• Standard sterilization method for all heat-stable instruments in OT"
        ),
        "sterilization": (
            "• The autoclave IS the sterilization device — requires periodic validation\n"
            "• Bowie-Dick test for pre-vacuum autoclaves (daily)\n"
            "• Biological indicators (Geobacillus stearothermophilus spores) weekly\n"
            "• Chemical indicators in each pack; temperature and pressure log maintenance"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/7/7a/Autoclave.jpg/320px-Autoclave.jpg"
    },
    {
        "no": 47,
        "name": "Gabriel Syringe",
        "variants": "1 required",
        "identification": (
            "Specially designed syringe with a curved, olive-tipped nozzle (to fit into "
            "the anal canal or rectum). Large barrel (60–150 mL). "
            "Used in anorectal procedures. The angled/curved nozzle is the distinguishing feature."
        ),
        "uses": (
            "• Injection of sclerosant (5% phenol in oil) for haemorrhoids\n"
            "• Anorectal injection procedures\n"
            "• Shalya Tantra: Arsha (haemorrhoid) sclerotherapy\n"
            "• Rectal irrigation and instillation of agents"
        ),
        "sterilization": (
            "• Metal/glass syringe: steam autoclave\n"
            "• Plastic syringe body: single-use disposable\n"
            "• Nozzle: autoclave metal type; discard plastic after use\n"
            "• Ensure plunger moves freely after sterilization"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/g/g1/Gabriel_syringe.jpg/320px-Gabriel_syringe.jpg"
    },
    {
        "no": 48,
        "name": "Sim's Speculum",
        "variants": "3 required",
        "identification": (
            "Duck-bill shaped speculum with two blades, one larger than the other, "
            "angled. The blades are set at an angle (not directly opposite as in Cusco's). "
            "Used with patient in Sim's position (lateral). Single-use plastic or reusable metal."
        ),
        "uses": (
            "• Visualisation of vagina and cervix\n"
            "• Examination of vaginal walls and anterior/posterior fornices\n"
            "• Procedures requiring lateral vaginal wall exposure\n"
            "• Combined with anterior wall retractor"
        ),
        "sterilization": (
            "• Metal: steam autoclave\n"
            "• Plastic/disposable: single use only\n"
            "• High-level disinfection (glutaraldehyde) acceptable\n"
            "• Clean thoroughly before sterilization"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3c/Sim_speculum.jpg/320px-Sim_speculum.jpg"
    },
    {
        "no": 49,
        "name": "Cusco's Speculum (Bivalve Speculum)",
        "variants": "3 required",
        "identification": (
            "Self-retaining bivalve speculum with two blades that open by a screw/thumbscrew "
            "mechanism. Blades meet at a point when closed. Thumb-screw or ratchet holds blades open. "
            "Most common vaginal speculum. Available in small, medium, large."
        ),
        "uses": (
            "• Visualisation of cervix for gynaecological examination\n"
            "• Pap smear collection\n"
            "• Cervical biopsy, cryotherapy, LEEP procedure\n"
            "• IUD insertion and removal"
        ),
        "sterilization": (
            "• Metal: steam autoclave\n"
            "• Plastic: single use/high-level disinfection\n"
            "• Disassemble screw mechanism before autoclaving\n"
            "• Inspect screw mechanism for smooth operation after sterilization"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/6/6e/Cuscos_speculum.jpg/320px-Cuscos_speculum.jpg"
    },
    {
        "no": 50,
        "name": "Uterine Sound",
        "variants": "3 required",
        "identification": (
            "Thin, flexible or semi-rigid graduated metal probe (~30 cm long) with a "
            "small knob at the distal end. Centimetre markings along its length. "
            "Slightly curved at distal end to follow uterine axis."
        ),
        "uses": (
            "• Measuring uterine cavity depth before IUD insertion\n"
            "• Assessing uterine position and patency of cervical canal\n"
            "• Preliminary to D&C, hysteroscopy, and other intrauterine procedures"
        ),
        "sterilization": (
            "• Steam autoclave: standard cycle\n"
            "• High-level disinfection (glutaraldehyde 2%, 20 min) acceptable\n"
            "• Single-use disposable plastic variants available\n"
            "• Inspect for bends and markings before use"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/9/9d/Uterine_sound.jpg/320px-Uterine_sound.jpg"
    },
    {
        "no": 51,
        "name": "Anterior Vaginal Wall Retractor",
        "variants": "3 required",
        "identification": (
            "Flat, rectangular or slightly curved blade with a handle. Designed to retract "
            "the anterior vaginal wall. May be part of a set with Sim's speculum. "
            "Single-ended. Different from Sim's which has two blades."
        ),
        "uses": (
            "• Retracting anterior vaginal wall for posterior wall procedures\n"
            "• Exposure during colporrhaphy\n"
            "• Used alongside Sim's speculum for complete vaginal examination\n"
            "• Prolapse repair procedures"
        ),
        "sterilization": (
            "• Metal: steam autoclave\n"
            "• Plastic: single use\n"
            "• High-level disinfection acceptable\n"
            "• Store in sterile pack"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/a/a2/Vaginal_retractor.jpg/320px-Vaginal_retractor.jpg"
    },
    {
        "no": 52,
        "name": "Green-Armytage Forceps",
        "variants": "Required quantity as per MSR",
        "identification": (
            "Ring-handled forceps with specially shaped, atraumatic, broad serrated jaws. "
            "Used specifically in uterine surgery. The broad, flat serrated jaws are "
            "characteristic. Named after the obstetrician who designed them."
        ),
        "uses": (
            "• Haemostasis at uterine incision edges during caesarean section\n"
            "• Grasping uterine muscle edges during uterine repair\n"
            "• Controlling bleeding from uterine wound"
        ),
        "sterilization": (
            "• Steam autoclave: standard cycle, open/unlocked\n"
            "• Clean jaw serrations with brush\n"
            "• Inspect jaw alignment and ratchet function\n"
            "• ETO acceptable as alternative"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/g/g2/Green_Armytage.jpg/320px-Green_Armytage.jpg"
    },
    {
        "no": 53,
        "name": "Doyen's Retractor",
        "variants": "Required quantity as per MSR",
        "identification": (
            "Large, broad, flat, curved/angled blade on a long handle. Designed for "
            "retracting the uterus or bladder during lower segment caesarean section "
            "(LSCS). Blade has slightly upturned edges."
        ),
        "uses": (
            "• Retracting the bladder downward during LSCS to expose lower uterine segment\n"
            "• Abdominal retraction during pelvic surgery\n"
            "• Hysterectomy and other pelvic procedures"
        ),
        "sterilization": (
            "• Steam autoclave: standard cycle\n"
            "• Clean blade surface of tissue debris before sterilization\n"
            "• No special requirements for solid metal blade\n"
            "• Inspect for blade deformation"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/d3/Doyen_retractor.jpg/320px-Doyen_retractor.jpg"
    },
    {
        "no": 54,
        "name": "Agnikarma Kit",
        "variants": "2 required (Anushashtra Room)",
        "identification": (
            "Shalya Tantra-specific kit including: Shalaka (metal rod/probe for heat application), "
            "spirit lamp or electric heat source, clotting time and bleeding time estimation materials. "
            "Shalaka may be gold, silver, iron, or copper rods of varying thickness."
        ),
        "uses": (
            "• Agnikarma: therapeutic thermal cauterization in Ayurvedic surgery\n"
            "• Treatment of musculoskeletal pain, joint disorders, skin conditions\n"
            "• Achieving haemostasis by heat\n"
            "• Pre-procedure BT/CT testing ensures safe patient selection"
        ),
        "sterilization": (
            "• Shalaka (metal probes): flame sterilization or dry heat\n"
            "• All accessories: steam autoclave where applicable\n"
            "• Spirit lamp and stand: clean and disinfect with alcohol\n"
            "• BT/CT lancets: single-use disposable"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/a/a3/Agnikarma.jpg/320px-Agnikarma.jpg"
    },
    {
        "no": 55,
        "name": "Siravedha Kit (Bloodletting/Venepuncture Kit)",
        "variants": "2 required (Anushashtra Room)",
        "identification": (
            "Ayurvedic therapeutic kit containing: Suchi (needles), Viddha (lancets), "
            "collection vessels, clotting and bleeding time materials, antiseptic swabs. "
            "May include specialized needles or scarifiers."
        ),
        "uses": (
            "• Siravedha: therapeutic bloodletting (Ayurvedic Raktamokshana)\n"
            "• Treatment of skin diseases, joint disorders, vascular conditions\n"
            "• Venepuncture for therapeutic purposes\n"
            "• BT/CT testing before procedure for safety"
        ),
        "sterilization": (
            "• Needles: single-use disposable (strict universal precautions)\n"
            "• Collection vessels: autoclave or single-use\n"
            "• All instruments: steam autoclave\n"
            "• Strict aseptic technique required for all bloodletting procedures"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/s/s1/Siravedha.jpg/320px-Siravedha.jpg"
    },
    {
        "no": 56,
        "name": "Cupping Therapy Kit",
        "variants": "2 required (Anushashtra Room)",
        "identification": (
            "Set of glass, bamboo, or silicone cups of varying sizes; pump device "
            "(for modern suction cups) or flame source (for traditional fire cupping). "
            "Glass cups have smooth rims. Silicone cups are flexible and squeezable."
        ),
        "uses": (
            "• Cupping therapy (Ayurvedic Shringa/Alabu): suction therapy for pain, detox\n"
            "• Wet cupping: combined with skin scarification for therapeutic bloodletting\n"
            "• Dry cupping: suction only for myofascial pain, sports injuries\n"
            "• BT/CT testing before wet cupping procedures"
        ),
        "sterilization": (
            "• Glass cups: autoclave or boil in water for 20 min; dry thoroughly\n"
            "• Silicone cups: high-level disinfection (glutaraldehyde or chlorhexidine)\n"
            "• Bamboo cups: replace regularly; limited reuse\n"
            "• Wet cupping: strict single-use principles for lancets/scarifiers"
        ),
        "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/c/c1/Cupping_therapy.jpg/320px-Cupping_therapy.jpg"
    },
]

# ──────────────────────────────────────────────────────────────
# DOWNLOAD INSTRUMENT IMAGES (Wikimedia)
# ──────────────────────────────────────────────────────────────

IMAGE_DIR = "/home/daytona/workspace/surgical-instruments/images/"
os.makedirs(IMAGE_DIR, exist_ok=True)

def download_image(url, filename):
    """Try to download an image; return path if successful, else None."""
    dest = os.path.join(IMAGE_DIR, filename)
    if os.path.exists(dest) and os.path.getsize(dest) > 1000:
        return dest
    try:
        req = urllib.request.Request(url, headers={
            'User-Agent': 'Mozilla/5.0 (compatible; SurgicalInstrumentPDF/1.0)'
        })
        with urllib.request.urlopen(req, timeout=10) as resp, open(dest, 'wb') as f:
            f.write(resp.read())
        if os.path.getsize(dest) > 1000:
            return dest
    except Exception as e:
        print(f"  Failed to download {url}: {e}")
    return None

# ──────────────────────────────────────────────────────────────
# COLOUR SCHEME
# ──────────────────────────────────────────────────────────────

DARK_BLUE    = colors.HexColor("#1a3a5c")
MID_BLUE     = colors.HexColor("#2e6da4")
LIGHT_BLUE   = colors.HexColor("#d6e8f7")
ACCENT_RED   = colors.HexColor("#c0392b")
GOLD         = colors.HexColor("#d4a017")
LIGHT_GREY   = colors.HexColor("#f0f4f8")
BORDER_GREY  = colors.HexColor("#b0bec5")
TEXT_DARK    = colors.HexColor("#1c1c1c")
WHITE        = colors.white

# ──────────────────────────────────────────────────────────────
# STYLES
# ──────────────────────────────────────────────────────────────

styles = getSampleStyleSheet()

TITLE_STYLE = ParagraphStyle(
    'CustomTitle',
    parent=styles['Title'],
    fontSize=22,
    fontName='Helvetica-Bold',
    textColor=WHITE,
    alignment=TA_CENTER,
    spaceAfter=4,
    leading=28,
)
SUBTITLE_STYLE = ParagraphStyle(
    'CustomSubtitle',
    parent=styles['Normal'],
    fontSize=11,
    fontName='Helvetica',
    textColor=colors.HexColor("#aed6f1"),
    alignment=TA_CENTER,
    spaceAfter=2,
    leading=14,
)
SECTION_HEADING = ParagraphStyle(
    'SectionHeading',
    parent=styles['Heading2'],
    fontSize=12,
    fontName='Helvetica-Bold',
    textColor=WHITE,
    spaceAfter=4,
    spaceBefore=0,
    leading=16,
)
LABEL_STYLE = ParagraphStyle(
    'LabelStyle',
    parent=styles['Normal'],
    fontSize=9,
    fontName='Helvetica-Bold',
    textColor=MID_BLUE,
    spaceAfter=1,
    leading=11,
)
BODY_STYLE = ParagraphStyle(
    'BodyStyle',
    parent=styles['Normal'],
    fontSize=8.5,
    fontName='Helvetica',
    textColor=TEXT_DARK,
    spaceAfter=2,
    leading=11.5,
    leftIndent=2,
)
INSTRUMENT_NUMBER_STYLE = ParagraphStyle(
    'InstrumentNumber',
    parent=styles['Normal'],
    fontSize=10,
    fontName='Helvetica-Bold',
    textColor=WHITE,
    alignment=TA_CENTER,
    leading=13,
)
INSTRUMENT_NAME_STYLE = ParagraphStyle(
    'InstrumentName',
    parent=styles['Normal'],
    fontSize=14,
    fontName='Helvetica-Bold',
    textColor=DARK_BLUE,
    spaceAfter=3,
    leading=18,
)
VARIANT_STYLE = ParagraphStyle(
    'VariantStyle',
    parent=styles['Normal'],
    fontSize=8.5,
    fontName='Helvetica-Oblique',
    textColor=colors.HexColor("#555555"),
    spaceAfter=6,
    leading=11,
)
TOC_STYLE = ParagraphStyle(
    'TocStyle',
    parent=styles['Normal'],
    fontSize=9,
    fontName='Helvetica',
    textColor=TEXT_DARK,
    leading=13,
    leftIndent=10,
)
TOC_HEADER_STYLE = ParagraphStyle(
    'TocHeader',
    parent=styles['Normal'],
    fontSize=11,
    fontName='Helvetica-Bold',
    textColor=DARK_BLUE,
    leading=14,
    spaceAfter=4,
)
FOOTER_STYLE = ParagraphStyle(
    'FooterStyle',
    parent=styles['Normal'],
    fontSize=7.5,
    fontName='Helvetica',
    textColor=colors.HexColor("#777777"),
    alignment=TA_CENTER,
    leading=10,
)
STERILIZATION_STYLE = ParagraphStyle(
    'SterilizationStyle',
    parent=styles['Normal'],
    fontSize=8.5,
    fontName='Helvetica',
    textColor=TEXT_DARK,
    spaceAfter=2,
    leading=11.5,
    leftIndent=2,
)

# ──────────────────────────────────────────────────────────────
# PAGE TEMPLATES (header/footer)
# ──────────────────────────────────────────────────────────────

PAGE_W, PAGE_H = A4

def on_page(canvas_obj, doc):
    canvas_obj.saveState()
    # Header bar
    canvas_obj.setFillColor(DARK_BLUE)
    canvas_obj.rect(0, PAGE_H - 28*mm, PAGE_W, 28*mm, fill=1, stroke=0)
    canvas_obj.setFillColor(GOLD)
    canvas_obj.rect(0, PAGE_H - 30*mm, PAGE_W, 2*mm, fill=1, stroke=0)
    canvas_obj.setFont('Helvetica-Bold', 9)
    canvas_obj.setFillColor(WHITE)
    canvas_obj.drawString(15*mm, PAGE_H - 14*mm, "SHALYA TANTRA | OT INSTRUMENTS REFERENCE GUIDE")
    canvas_obj.setFont('Helvetica', 8)
    canvas_obj.drawRightString(PAGE_W - 15*mm, PAGE_H - 14*mm, f"Page {doc.page}")
    canvas_obj.setFillColor(colors.HexColor("#aed6f1"))
    canvas_obj.setFont('Helvetica', 7.5)
    canvas_obj.drawString(15*mm, PAGE_H - 21*mm, "Identification | Clinical Uses | Sterilization Protocol")
    # Footer bar
    canvas_obj.setFillColor(LIGHT_GREY)
    canvas_obj.rect(0, 0, PAGE_W, 14*mm, fill=1, stroke=0)
    canvas_obj.setFillColor(GOLD)
    canvas_obj.rect(0, 14*mm, PAGE_W, 0.5*mm, fill=1, stroke=0)
    canvas_obj.setFont('Helvetica', 7)
    canvas_obj.setFillColor(colors.HexColor("#555555"))
    canvas_obj.drawCentredString(PAGE_W/2, 8*mm, "Department of Shalya Tantra | Operation Theatre | Anushashtra Room")
    canvas_obj.drawCentredString(PAGE_W/2, 4*mm, "This document is for educational/departmental reference only.")
    canvas_obj.restoreState()

def on_cover(canvas_obj, doc):
    """Cover page — no header/footer bars."""
    pass

# ──────────────────────────────────────────────────────────────
# BUILD PDF
# ──────────────────────────────────────────────────────────────

def make_instrument_card(instr, img_path):
    elements = []

    # ── Instrument header strip ──
    header_data = [[
        Paragraph(f"#{instr['no']:02d}", INSTRUMENT_NUMBER_STYLE),
        Paragraph(instr['name'], ParagraphStyle(
            'INameHeader', parent=INSTRUMENT_NAME_STYLE,
            fontSize=13, textColor=WHITE, spaceBefore=0, spaceAfter=0, leading=16
        )),
    ]]
    header_table = Table(header_data, colWidths=[18*mm, 155*mm])
    header_table.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,-1), DARK_BLUE),
        ('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
        ('LEFTPADDING', (0,0), (0,0), 4),
        ('LEFTPADDING', (1,0), (1,0), 8),
        ('RIGHTPADDING', (-1,0), (-1,-1), 8),
        ('TOPPADDING', (0,0), (-1,-1), 5),
        ('BOTTOMPADDING', (0,0), (-1,-1), 5),
        ('ROUNDEDCORNERS', [4, 4, 0, 0]),
    ]))

    # ── Variant strip ──
    variant_data = [[Paragraph(f"Variants: {instr['variants']}", VARIANT_STYLE)]]
    variant_table = Table(variant_data, colWidths=[173*mm])
    variant_table.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,-1), LIGHT_BLUE),
        ('LEFTPADDING', (0,0), (-1,-1), 10),
        ('TOPPADDING', (0,0), (-1,-1), 3),
        ('BOTTOMPADDING', (0,0), (-1,-1), 3),
    ]))

    # ── Image + content ──
    IMG_W = 45*mm
    IMG_H = 38*mm

    if img_path:
        try:
            img = Image(img_path, width=IMG_W, height=IMG_H)
            img_cell = img
        except Exception:
            img_cell = Paragraph("(image\nnot\navailable)", ParagraphStyle(
                'NoImg', parent=styles['Normal'], fontSize=7,
                textColor=colors.grey, alignment=TA_CENTER
            ))
    else:
        img_cell = Paragraph("(image\nnot\navailable)", ParagraphStyle(
            'NoImg', parent=styles['Normal'], fontSize=7,
            textColor=colors.grey, alignment=TA_CENTER
        ))

    # Identification + uses + sterilization
    content_items = [
        Paragraph("IDENTIFICATION", LABEL_STYLE),
        Paragraph(instr['identification'].replace('\n', '<br/>'), BODY_STYLE),
        Spacer(1, 3),
        Paragraph("CLINICAL USES", LABEL_STYLE),
        Paragraph(instr['uses'].replace('\n', '<br/>').replace('•', '&#9679;'), BODY_STYLE),
        Spacer(1, 3),
        Paragraph("STERILIZATION", ParagraphStyle(
            'SterilLabel', parent=LABEL_STYLE, textColor=ACCENT_RED
        )),
        Paragraph(instr['sterilization'].replace('\n', '<br/>').replace('•', '&#9679;'), STERILIZATION_STYLE),
    ]

    # Wrap content in a table
    content_table_data = [[item] for item in content_items]
    content_table = Table(content_table_data, colWidths=[120*mm])
    content_table.setStyle(TableStyle([
        ('LEFTPADDING', (0,0), (-1,-1), 0),
        ('RIGHTPADDING', (0,0), (-1,-1), 0),
        ('TOPPADDING', (0,0), (-1,-1), 0),
        ('BOTTOMPADDING', (0,0), (-1,-1), 1),
        ('VALIGN', (0,0), (-1,-1), 'TOP'),
    ]))

    body_data = [[img_cell, content_table]]
    body_table = Table(body_data, colWidths=[49*mm, 124*mm])
    body_table.setStyle(TableStyle([
        ('VALIGN', (0,0), (-1,-1), 'TOP'),
        ('LEFTPADDING', (0,0), (0,-1), 5),
        ('RIGHTPADDING', (0,0), (0,-1), 8),
        ('LEFTPADDING', (1,0), (1,-1), 5),
        ('RIGHTPADDING', (1,0), (1,-1), 5),
        ('TOPPADDING', (0,0), (-1,-1), 7),
        ('BOTTOMPADDING', (0,0), (-1,-1), 7),
        ('BACKGROUND', (0,0), (-1,-1), colors.white),
    ]))

    # ── Outer border table ──
    outer_data = [
        [header_table],
        [variant_table],
        [body_table],
    ]
    outer_table = Table(outer_data, colWidths=[173*mm])
    outer_table.setStyle(TableStyle([
        ('BOX', (0,0), (-1,-1), 1, BORDER_GREY),
        ('TOPPADDING', (0,0), (-1,-1), 0),
        ('BOTTOMPADDING', (0,0), (-1,-1), 0),
        ('LEFTPADDING', (0,0), (-1,-1), 0),
        ('RIGHTPADDING', (0,0), (-1,-1), 0),
    ]))

    elements.append(KeepTogether([outer_table]))
    elements.append(Spacer(1, 10))
    return elements


def build_cover(elements):
    elements.append(Spacer(1, 20*mm))
    cover_data = [[
        Paragraph("SHALYA TANTRA", ParagraphStyle(
            'CoverMain', parent=styles['Title'],
            fontSize=30, fontName='Helvetica-Bold',
            textColor=WHITE, alignment=TA_CENTER, leading=36
        ))
    ]]
    cover_subtitle = Paragraph(
        "OPERATION THEATRE &amp; ANUSHASHTRA ROOM<br/>"
        "<font size=14>Surgical Instruments Reference Guide</font>",
        ParagraphStyle('CoverSub', parent=styles['Normal'],
                       fontSize=16, fontName='Helvetica',
                       textColor=colors.HexColor("#aed6f1"),
                       alignment=TA_CENTER, leading=22)
    )
    cover_table = Table([[cover_data[0][0]], [cover_subtitle]], colWidths=[173*mm])
    cover_table.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,-1), DARK_BLUE),
        ('ALIGN', (0,0), (-1,-1), 'CENTER'),
        ('TOPPADDING', (0,0), (0,0), 18),
        ('BOTTOMPADDING', (0,0), (0,0), 8),
        ('TOPPADDING', (0,1), (0,1), 4),
        ('BOTTOMPADDING', (0,1), (0,1), 18),
        ('BOX', (0,0), (-1,-1), 2, GOLD),
    ]))
    elements.append(cover_table)
    elements.append(Spacer(1, 10*mm))

    # Info box
    info_text = (
        "<b>Document Overview</b><br/><br/>"
        "This reference guide covers all surgical instruments listed under the Minimum Essential "
        "Standards (MES) for the Operation Theatre (OT) and Anushashtra Room of the Shalya Tantra "
        "department. Each entry includes:<br/><br/>"
        "&#9679; Instrument identification (physical features, variants, how to distinguish it)<br/>"
        "&#9679; Clinical uses (indications, procedures, Shalya Tantra applications)<br/>"
        "&#9679; Sterilization protocol (method, temperature, time, precautions)<br/><br/>"
        f"<b>Total instruments covered:</b> {len(instruments)}<br/>"
        "<b>Source:</b> Shalya Tantra OT Equipment List (MSR 2016, MES 2016, MES 2024)"
    )
    info_para = Paragraph(info_text, ParagraphStyle(
        'InfoPara', parent=styles['Normal'],
        fontSize=10, fontName='Helvetica',
        textColor=TEXT_DARK, leading=15, spaceAfter=6
    ))
    info_table = Table([[info_para]], colWidths=[173*mm])
    info_table.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,-1), LIGHT_BLUE),
        ('BOX', (0,0), (-1,-1), 1, MID_BLUE),
        ('LEFTPADDING', (0,0), (-1,-1), 15),
        ('RIGHTPADDING', (0,0), (-1,-1), 15),
        ('TOPPADDING', (0,0), (-1,-1), 12),
        ('BOTTOMPADDING', (0,0), (-1,-1), 12),
    ]))
    elements.append(info_table)
    elements.append(Spacer(1, 8*mm))

    # Sterilization legend
    legend_data = [
        [Paragraph("<b>Quick Sterilization Reference</b>", ParagraphStyle(
            'LegH', parent=styles['Normal'], fontSize=10, fontName='Helvetica-Bold',
            textColor=DARK_BLUE, alignment=TA_CENTER
        ))],
    ]
    methods = [
        ("Steam Autoclave", "121°C/15 psi/15 min (gravity) or 134°C/30 psi/4 min (pre-vac)", "#2980b9"),
        ("Ethylene Oxide (ETO)", "50–60°C, 1–6 hrs; aeration 8–12 hrs", "#27ae60"),
        ("Glutaraldehyde 2%", "High-level disinfection: 20–45 min; Sterilization: 8–10 hrs", "#8e44ad"),
        ("Dry Heat", "160°C/60 min or 170°C/30 min (for glass, oils, powders)", "#e67e22"),
        ("Single Use", "Pre-sterilized; do not resterilize; discard after use", "#c0392b"),
    ]
    row_data = []
    for method, detail, color in methods:
        row_data.append([
            Paragraph(f"<b>{method}</b>", ParagraphStyle(
                'LM', parent=styles['Normal'], fontSize=8, fontName='Helvetica-Bold',
                textColor=colors.HexColor(color)
            )),
            Paragraph(detail, ParagraphStyle(
                'LD', parent=styles['Normal'], fontSize=8, fontName='Helvetica', textColor=TEXT_DARK
            ))
        ])
    methods_table = Table(row_data, colWidths=[52*mm, 113*mm])
    methods_table.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,-1), colors.white),
        ('ROWBACKGROUNDS', (0,0), (-1,-1), [colors.white, LIGHT_GREY]),
        ('GRID', (0,0), (-1,-1), 0.5, BORDER_GREY),
        ('LEFTPADDING', (0,0), (-1,-1), 8),
        ('RIGHTPADDING', (0,0), (-1,-1), 8),
        ('TOPPADDING', (0,0), (-1,-1), 5),
        ('BOTTOMPADDING', (0,0), (-1,-1), 5),
        ('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
    ]))
    legend_outer = Table([[legend_data[0][0]], [methods_table]], colWidths=[173*mm])
    legend_outer.setStyle(TableStyle([
        ('BOX', (0,0), (-1,-1), 1, BORDER_GREY),
        ('BACKGROUND', (0,0), (0,0), LIGHT_BLUE),
        ('TOPPADDING', (0,0), (0,0), 8),
        ('BOTTOMPADDING', (0,0), (0,0), 8),
        ('LEFTPADDING', (0,0), (-1,-1), 0),
        ('RIGHTPADDING', (0,0), (-1,-1), 0),
    ]))
    elements.append(legend_outer)
    elements.append(PageBreak())


def build_toc(elements):
    elements.append(Spacer(1, 5*mm))
    elements.append(Paragraph("TABLE OF CONTENTS", ParagraphStyle(
        'TOCTitle', parent=styles['Normal'],
        fontSize=16, fontName='Helvetica-Bold',
        textColor=DARK_BLUE, spaceAfter=8, leading=20
    )))
    elements.append(HRFlowable(width="100%", thickness=2, color=GOLD, spaceAfter=8))

    col1, col2 = [], []
    mid = (len(instruments) + 1) // 2
    for instr in instruments[:mid]:
        col1.append(Paragraph(
            f"<b>{instr['no']:02d}.</b>  {instr['name']}",
            TOC_STYLE
        ))
    for instr in instruments[mid:]:
        col2.append(Paragraph(
            f"<b>{instr['no']:02d}.</b>  {instr['name']}",
            TOC_STYLE
        ))

    # Pad to equal length
    while len(col2) < len(col1):
        col2.append(Paragraph("", TOC_STYLE))

    rows = [[c1, c2] for c1, c2 in zip(col1, col2)]
    toc_table = Table(rows, colWidths=[85*mm, 85*mm])
    toc_table.setStyle(TableStyle([
        ('ROWBACKGROUNDS', (0,0), (-1,-1), [WHITE, LIGHT_GREY]),
        ('TOPPADDING', (0,0), (-1,-1), 3),
        ('BOTTOMPADDING', (0,0), (-1,-1), 3),
        ('LEFTPADDING', (0,0), (-1,-1), 8),
        ('RIGHTPADDING', (0,0), (-1,-1), 8),
        ('GRID', (0,0), (-1,-1), 0.3, BORDER_GREY),
    ]))
    elements.append(toc_table)
    elements.append(PageBreak())


def main():
    print("Downloading instrument images...")
    img_paths = {}
    for instr in instruments:
        fname = f"instrument_{instr['no']:02d}.jpg"
        dest = os.path.join(IMAGE_DIR, fname)
        if os.path.exists(dest) and os.path.getsize(dest) > 1000:
            img_paths[instr['no']] = dest
            print(f"  #{instr['no']:02d} already exists")
            continue
        path = download_image(instr.get('image_url', ''), fname)
        img_paths[instr['no']] = path
        if path:
            print(f"  #{instr['no']:02d} downloaded")
        else:
            print(f"  #{instr['no']:02d} no image available")

    print("\nBuilding PDF...")
    doc = SimpleDocTemplate(
        OUTPUT_PATH,
        pagesize=A4,
        topMargin=35*mm,
        bottomMargin=18*mm,
        leftMargin=18*mm,
        rightMargin=18*mm,
        title="Shalya Tantra Surgical Instruments Reference",
        author="OT Department",
    )

    elements = []
    build_cover(elements)
    build_toc(elements)

    for instr in instruments:
        img = img_paths.get(instr['no'])
        card = make_instrument_card(instr, img)
        elements.extend(card)

    doc.build(elements, onFirstPage=on_cover, onLaterPages=on_page)
    print(f"\nDone! PDF saved to: {OUTPUT_PATH}")


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

cd /home/daytona/workspace/surgical-instruments && python3 generate_pdf.py 2>&1 …

Running Command

python3 -c " import urllib.request # Test proper Wikimedia Commons API URL forma…

Writing File

~/surgical-instruments/download_images.py

#!/usr/bin/env python3
"""
Download surgical instrument images from Wikimedia Commons API
"""
import os
import urllib.request
import time

IMAGE_DIR = "/home/daytona/workspace/surgical-instruments/images/"
os.makedirs(IMAGE_DIR, exist_ok=True)

# Map instrument number -> Wikimedia Commons filename (exact file name on Commons)
IMAGE_FILES = {
    1:  "Needle_holder.jpg",
    2:  "Drum_dressing.jpg",
    3:  "Cheatle%27s_forceps.jpg",
    4:  "Halsted_mosquito_forceps.jpg",
    5:  "Operating_scissors_sharp_blunt.jpg",
    6:  "Mayo_scissors.jpg",
    7:  "Spencer_Wells_forceps.jpg",  # placeholder (stitch scissors not on commons)
    8:  "Thumb_forceps.jpg",
    9:  "Spencer_Wells_forceps.jpg",  # sinus forceps similar
    10: "Surgical_probe.jpg",
    11: "Operating_scissors_sharp_blunt.jpg",
    12: "Payr_intestinal_clamp.jpg",
    13: "Deaver_retractor.jpg",
    14: "Allis_forceps.jpg",
    15: "Babcock_forceps.jpg",
    16: "Kocher_forceps.jpg",
    17: "Urethral_sounds.jpg",
    18: "Foley_catheter.jpg",
    19: "Metal_catheter.jpg",
    20: "Surgical_needles.jpg",
    21: "Surgical_suture.jpg",
    22: "Rampley_sponge_forceps.jpg",
    23: "Mixter_forceps.jpg",
    24: "Stone_forceps_Randall.jpg",
    25: "Proctoscope.jpg",
    26: "Hegar_dilators.jpg",
    27: "Allis_forceps.jpg",
    28: "Allis_forceps.jpg",
    29: "Artery_forceps.jpg",
    30: "Sigmoidoscope.jpg",
    31: "Barron_pile_gun.jpg",
    32: "Laryngoscope.jpg",
    33: "Ambu_bag.jpg",
    34: "Yankauer_suction_catheter.jpg",
    35: "Humby_knife.jpg",
    36: "Surgical_blades.jpg",
    37: "Scalpel_handle.jpg",
    38: "Weitlaner_retractor.jpg",
    39: "Surgical_drill.jpg",
    40: "Bone_cutter.jpg",
    41: "Gigli_saw.jpg",
    42: "Bone_curette.jpg",
    43: "Periosteal_elevator.jpg",
    44: "Magill_forceps.jpg",
    45: "Endotracheal_tube.jpg",
    46: "Autoclave.jpg",
    47: "Gabriel_syringe.jpg",
    48: "Sim%27s_speculum.jpg",
    49: "Cusco_speculum.jpg",
    50: "Uterine_sound.jpg",
    51: "Vaginal_retractor.jpg",
    52: "Uterus_clamp.jpg",
    53: "Doyen_retractor.jpg",
    54: "Agnikarma_shalaka.jpg",
    55: "Hypodermic_needle_3_annotated.jpg",  # generic needle for siravedha
    56: "Cupping_therapy.jpg",
}

def download(file_name, dest_path, width=300):
    url = f"https://commons.wikimedia.org/w/index.php?title=Special:FilePath&file={file_name}&width={width}"
    req = urllib.request.Request(url, headers={
        'User-Agent': 'Mozilla/5.0 SurgicalInstrumentPDF/1.0 (educational reference)'
    })
    try:
        with urllib.request.urlopen(req, timeout=15) as resp:
            data = resp.read()
            ct = resp.headers.get('Content-Type', '')
            if len(data) > 2000 and ('image' in ct or data[:4] in (b'\xff\xd8\xff\xe0', b'\xff\xd8\xff\xe1', b'\x89PNG')):
                with open(dest_path, 'wb') as f:
                    f.write(data)
                return True
            else:
                print(f"  Skipped {file_name}: size={len(data)}, ct={ct}")
    except Exception as e:
        print(f"  Failed {file_name}: {e}")
    return False

results = {}
for num, fname in IMAGE_FILES.items():
    dest = os.path.join(IMAGE_DIR, f"instrument_{num:02d}.jpg")
    if os.path.exists(dest) and os.path.getsize(dest) > 2000:
        print(f"  #{num:02d} exists ({os.path.getsize(dest)} bytes)")
        results[num] = dest
        continue
    ok = download(fname, dest)
    if ok:
        print(f"  #{num:02d} OK  ({fname})")
        results[num] = dest
    else:
        print(f"  #{num:02d} MISS ({fname})")
        results[num] = None
    time.sleep(0.3)

ok_count = sum(1 for v in results.values() if v)
print(f"\nDownloaded {ok_count}/{len(IMAGE_FILES)} images")
Running Command

cd /home/daytona/workspace/surgical-instruments && python3 download_images.py 2>…

Running Command

python3 -c " import urllib.request, time # Test specific known Wikimedia Common…

Running Command

python3 -c " # Generate SVG instrument diagrams using ReportLab Drawing from rep…

Running Command

python3 -c " # Try matplotlib for instrument drawings, then PIL import matplotli…

Pdf

Writing File

~/surgical-instruments/draw_instruments.py

#!/usr/bin/env python3
"""
Draw surgical instrument diagrams using matplotlib patches.
Saves each as a 300x200 PNG.
"""
import os
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from matplotlib.patches import FancyBboxPatch, Arc, Circle, Ellipse, Rectangle, FancyArrowPatch
import matplotlib.patheffects as pe
import numpy as np

IMAGE_DIR = "/home/daytona/workspace/surgical-instruments/images/"
os.makedirs(IMAGE_DIR, exist_ok=True)

STEEL = "#b0b8c1"
DARK_STEEL = "#6b7a8d"
HANDLE_COLOR = "#8d9db6"
GOLD = "#c8a951"
SILVER = "#d0d4d8"
BLACK = "#1a1a1a"
DARK = "#2c3e50"

def fig_ax(title=""):
    fig, ax = plt.subplots(figsize=(3.5, 2.2), dpi=100)
    ax.set_xlim(0, 350)
    ax.set_ylim(0, 220)
    ax.set_aspect('equal')
    ax.axis('off')
    fig.patch.set_facecolor('#f8f9fa')
    ax.set_facecolor('#f8f9fa')
    if title:
        ax.text(175, 10, title, ha='center', va='bottom', fontsize=7,
                color='#2c3e50', fontweight='bold', style='italic')
    return fig, ax

def save(fig, num):
    path = os.path.join(IMAGE_DIR, f"instrument_{num:02d}.png")
    fig.savefig(path, dpi=100, bbox_inches='tight', facecolor=fig.get_facecolor())
    plt.close(fig)
    print(f"  #{num:02d} saved")
    return path

def ring_handle(ax, cx, cy, r=14, color=STEEL):
    """Draw a ring handle (finger ring)."""
    ring = Circle((cx, cy), r, fill=False, edgecolor=color, linewidth=2.5)
    ax.add_patch(ring)
    ring_inner = Circle((cx, cy), r-4, fill=False, edgecolor=color, linewidth=1)
    ax.add_patch(ring_inner)

def draw_forceps_base(ax, x_start, y_center, length, width=12, color=STEEL, tip_type='blunt'):
    """Draw a basic forceps shaft from x_start going right."""
    # Shaft
    shaft = Rectangle((x_start, y_center - width/2), length, width,
                       facecolor=color, edgecolor=DARK, linewidth=1)
    ax.add_patch(shaft)
    # Shading
    highlight = Rectangle((x_start, y_center + width/4), length, width/4,
                           facecolor='white', alpha=0.3, edgecolor='none')
    ax.add_patch(highlight)

def serrations(ax, x, y_center, h, n=8, color=DARK):
    """Draw horizontal serration lines."""
    for i in range(n):
        yy = y_center - h/2 + (i+0.5) * h/n
        ax.plot([x, x+6], [yy, yy], color=color, linewidth=0.5, alpha=0.7)


# ─── 1. NEEDLE HOLDER ───────────────────────────────────────────
def instr_01():
    fig, ax = fig_ax("Needle Holder (Needle Holding Forceps)")
    # Ring handles
    ring_handle(ax, 40, 130, 20)
    ring_handle(ax, 80, 130, 20)
    # Box lock
    ax.add_patch(Rectangle((95, 115), 20, 30, facecolor=DARK_STEEL, edgecolor=DARK, lw=1.5))
    # Ratchet
    for i in range(5):
        ax.plot([102, 108], [118+i*3, 120+i*3], color='white', lw=0.7)
    # Shaft
    ax.add_patch(Rectangle((115, 120), 170, 20, facecolor=STEEL, edgecolor=DARK, lw=1.5))
    ax.add_patch(Rectangle((115, 126), 170, 6, facecolor='white', alpha=0.25, edgecolor='none'))
    # Heavy jaws
    ax.add_patch(Rectangle((285, 115), 45, 30, facecolor=DARK_STEEL, edgecolor=DARK, lw=1.5))
    serrations(ax, 285, 130, 28, 10)
    serrations(ax, 295, 130, 28, 10)
    serrations(ax, 305, 130, 28, 10)
    # Label parts
    ax.annotate('Ring handles', xy=(60, 110), xytext=(30, 75),
                fontsize=6.5, color=DARK, arrowprops=dict(arrowstyle='->', color=DARK, lw=0.8))
    ax.annotate('Ratchet lock', xy=(105, 115), xytext=(90, 75),
                fontsize=6.5, color=DARK, arrowprops=dict(arrowstyle='->', color=DARK, lw=0.8))
    ax.annotate('Serrated jaws', xy=(305, 120), xytext=(270, 75),
                fontsize=6.5, color=DARK, arrowprops=dict(arrowstyle='->', color=DARK, lw=0.8))
    save(fig, 1)

# ─── 2. DRESSING DRUM ───────────────────────────────────────────
def instr_02():
    fig, ax = fig_ax("Dressing Drum (Stainless Steel)")
    # Cylinder body
    ax.add_patch(Rectangle((80, 50), 200, 130, facecolor='#c8cdd2', edgecolor=DARK, lw=2))
    # Lid top
    ax.add_patch(Ellipse((180, 180), 200, 28, facecolor=DARK_STEEL, edgecolor=DARK, lw=2))
    ax.add_patch(Ellipse((180, 50), 200, 28, facecolor='#9aa5b0', edgecolor=DARK, lw=2))
    # Sliding band
    ax.add_patch(Rectangle((80, 100), 200, 20, facecolor=DARK_STEEL, edgecolor=DARK, lw=1.5))
    # Perforation holes
    for i in range(6):
        for j in range(3):
            ax.add_patch(Ellipse((105+i*30, 65+j*30), 10, 8,
                                 facecolor=DARK, edgecolor='none', alpha=0.7))
    # Handle on lid
    ax.add_patch(Rectangle((155, 180), 50, 12, facecolor=DARK_STEEL, edgecolor=DARK, lw=1.5,
                            joinstyle='round'))
    ax.add_patch(Ellipse((155, 186), 10, 12, facecolor=DARK_STEEL, edgecolor=DARK, lw=1.5))
    ax.add_patch(Ellipse((205, 186), 10, 12, facecolor=DARK_STEEL, edgecolor=DARK, lw=1.5))
    ax.annotate('Perforations', xy=(145, 80), xytext=(30, 170),
                fontsize=6.5, color=DARK, arrowprops=dict(arrowstyle='->', color=DARK, lw=0.8))
    ax.annotate('Sliding band', xy=(180, 110), xytext=(230, 160),
                fontsize=6.5, color=DARK, arrowprops=dict(arrowstyle='->', color=DARK, lw=0.8))
    save(fig, 2)

# ─── 3. CHEATLE'S FORCEPS ───────────────────────────────────────
def instr_03():
    fig, ax = fig_ax("Cheatle's Forceps")
    # Long handles (angled)
    ax.add_patch(mpatches.FancyArrowPatch((30, 170), (160, 120),
                 arrowstyle='-', linewidth=6, color=STEEL))
    ax.add_patch(mpatches.FancyArrowPatch((60, 170), (190, 120),
                 arrowstyle='-', linewidth=6, color=STEEL))
    # Ring ends at top
    ring_handle(ax, 30, 175, 16)
    ring_handle(ax, 60, 175, 16)
    # Pivot
    ax.add_patch(Circle((175, 118), 7, facecolor=GOLD, edgecolor=DARK, lw=1.5))
    # Curved angled blades below pivot
    theta = np.linspace(1.1*np.pi, 1.6*np.pi, 30)
    x1 = 175 + 60*np.cos(theta)
    y1 = 118 + 80*np.sin(theta)
    ax.plot(x1, y1, color=DARK_STEEL, lw=4)
    theta2 = np.linspace(1.1*np.pi, 1.6*np.pi, 30)
    x2 = 185 + 60*np.cos(theta2)
    y2 = 118 + 80*np.sin(theta2)
    ax.plot(x2, y2, color=STEEL, lw=4)
    ax.annotate('Ring handles', xy=(45, 180), xytext=(100, 205),
                fontsize=6.5, color=DARK, arrowprops=dict(arrowstyle='->', color=DARK, lw=0.8))
    ax.annotate('Curved spoon blades', xy=(250, 55), xytext=(240, 30),
                fontsize=6.5, color=DARK, arrowprops=dict(arrowstyle='->', color=DARK, lw=0.8))
    save(fig, 3)

# ─── 4. MOSQUITO FORCEPS ────────────────────────────────────────
def instr_04():
    fig, ax = fig_ax("Mosquito Forceps (Halsted)")
    ring_handle(ax, 38, 150, 16)
    ring_handle(ax, 68, 150, 16)
    ax.add_patch(Rectangle((80, 128), 15, 24, facecolor=DARK_STEEL, edgecolor=DARK, lw=1.5))
    for i in range(4): ax.plot([82, 93], [131+i*4, 131+i*4], color='white', lw=0.6)
    ax.add_patch(Rectangle((95, 130), 190, 20, facecolor=STEEL, edgecolor=DARK, lw=1.2))
    ax.add_patch(Rectangle((95, 135), 190, 6, facecolor='white', alpha=0.25, edgecolor='none'))
    # Fine pointed jaws
    ax.add_patch(Rectangle((285, 132), 40, 8, facecolor=DARK_STEEL, edgecolor=DARK, lw=1))
    ax.add_patch(Rectangle((285, 140), 30, 8, facecolor=DARK_STEEL, edgecolor=DARK, lw=1))
    # Pointed tip
    pts = np.array([[325, 132], [325, 148], [340, 140]])
    ax.add_patch(plt.Polygon(pts, facecolor=DARK_STEEL, edgecolor=DARK, lw=1))
    ax.annotate('Fine pointed tips', xy=(310, 140), xytext=(270, 105),
                fontsize=6.5, color=DARK, arrowprops=dict(arrowstyle='->', color=DARK, lw=0.8))
    ax.annotate('Ratchet', xy=(87, 128), xytext=(60, 105),
                fontsize=6.5, color=DARK, arrowprops=dict(arrowstyle='->', color=DARK, lw=0.8))
    save(fig, 4)

# ─── 5. STRAIGHT SCISSORS ───────────────────────────────────────
def instr_05():
    fig, ax = fig_ax("Operating Scissors (Straight)")
    ring_handle(ax, 38, 150, 18)
    ring_handle(ax, 75, 150, 18)
    ax.add_patch(Circle((118, 130), 8, facecolor=GOLD, edgecolor=DARK, lw=1.5))
    # Upper blade
    ax.add_patch(Rectangle((126, 133), 190, 10, facecolor=STEEL, edgecolor=DARK, lw=1.2))
    pts1 = np.array([[316, 133], [316, 143], [340, 138]])
    ax.add_patch(plt.Polygon(pts1, facecolor=DARK_STEEL, edgecolor=DARK, lw=1))
    # Lower blade
    ax.add_patch(Rectangle((126, 117), 180, 10, facecolor=STEEL, edgecolor=DARK, lw=1.2))
    pts2 = np.array([[306, 117], [306, 127], [328, 122]])
    ax.add_patch(plt.Polygon(pts2, facecolor=DARK_STEEL, edgecolor=DARK, lw=1))
    ax.annotate('Pivot screw', xy=(118, 130), xytext=(80, 100),
                fontsize=6.5, color=DARK, arrowprops=dict(arrowstyle='->', color=DARK, lw=0.8))
    ax.annotate('Sharp tips (S/B)', xy=(330, 128), xytext=(270, 90),
                fontsize=6.5, color=DARK, arrowprops=dict(arrowstyle='->', color=DARK, lw=0.8))
    save(fig, 5)

# ─── 6. CURVED SCISSORS ─────────────────────────────────────────
def instr_06():
    fig, ax = fig_ax("Mayo Curved Scissors")
    ring_handle(ax, 38, 150, 18)
    ring_handle(ax, 75, 150, 18)
    ax.add_patch(Circle((118, 130), 8, facecolor=GOLD, edgecolor=DARK, lw=1.5))
    # Curved upper blade
    t = np.linspace(0, 0.5*np.pi, 40)
    x_u = 126 + 170*np.sin(t)
    y_u = 138 + 25*(1-np.cos(t))
    ax.fill_between(x_u, y_u, y_u-9, facecolor=STEEL, edgecolor=DARK, lw=1)
    # Curved lower blade
    y_l = 128 + 25*(1-np.cos(t))
    ax.fill_between(x_u, y_l-12, y_l-3, facecolor=STEEL, edgecolor=DARK, lw=1)
    ax.annotate('Curved blades', xy=(250, 150), xytext=(230, 185),
                fontsize=6.5, color=DARK, arrowprops=dict(arrowstyle='->', color=DARK, lw=0.8))
    save(fig, 6)

# ─── 7. STITCH REMOVAL SCISSORS ─────────────────────────────────
def instr_07():
    fig, ax = fig_ax("Stitch Removal Scissors")
    ring_handle(ax, 38, 150, 18)
    ring_handle(ax, 75, 150, 18)
    ax.add_patch(Circle((118, 130), 8, facecolor=GOLD, edgecolor=DARK, lw=1.5))
    ax.add_patch(Rectangle((126, 133), 190, 9, facecolor=STEEL, edgecolor=DARK, lw=1.2))
    # Hooked lower blade
    ax.add_patch(Rectangle((126, 119), 160, 9, facecolor=STEEL, edgecolor=DARK, lw=1.2))
    # Hook tip
    theta = np.linspace(0, np.pi, 30)
    hx = 286 + 12*np.cos(theta)
    hy = 128 + 12*np.sin(theta)
    ax.plot(hx, hy, color=DARK_STEEL, lw=4)
    ax.annotate('Hook blade (slides\nunder suture)', xy=(295, 140), xytext=(230, 175),
                fontsize=6.5, color='#c0392b',
                arrowprops=dict(arrowstyle='->', color='#c0392b', lw=0.8))
    save(fig, 7)

# ─── 8. DISSECTION FORCEPS ──────────────────────────────────────
def instr_08():
    fig, ax = fig_ax("Dissection / Thumb Forceps")
    # Spring-type (no rings)
    # Upper arm
    ax.add_patch(Rectangle((50, 150), 230, 10, facecolor=STEEL, edgecolor=DARK, lw=1.5,
                            joinstyle='round'))
    # Lower arm
    ax.add_patch(Rectangle((50, 100), 230, 10, facecolor=STEEL, edgecolor=DARK, lw=1.5,
                            joinstyle='round'))
    # Spring at proximal end
    ax.add_patch(Ellipse((50, 130), 20, 60, facecolor='none', edgecolor=DARK, lw=2.5))
    # Toothed tips
    for i in range(4):
        ax.add_patch(Rectangle((280+i*5, 150), 4, 12, facecolor=DARK_STEEL,
                               edgecolor=DARK, lw=0.8))
    for i in range(3):
        ax.add_patch(Rectangle((282+i*5, 98), 4, 12, facecolor=DARK_STEEL,
                               edgecolor=DARK, lw=0.8))
    ax.annotate('Spring (no rings)\nHeld like tweezers', xy=(50, 130), xytext=(80, 170),
                fontsize=6.5, color=DARK, arrowprops=dict(arrowstyle='->', color=DARK, lw=0.8))
    ax.annotate('Teeth (toothed type)', xy=(290, 150), xytext=(240, 185),
                fontsize=6.5, color=DARK, arrowprops=dict(arrowstyle='->', color=DARK, lw=0.8))
    save(fig, 8)

# ─── 9. SINUS FORCEPS ───────────────────────────────────────────
def instr_09():
    fig, ax = fig_ax("Sinus Forceps")
    ring_handle(ax, 38, 140, 16)
    ring_handle(ax, 68, 140, 16)
    ax.add_patch(Rectangle((82, 128), 15, 22, facecolor=DARK_STEEL, edgecolor=DARK, lw=1.5))
    ax.add_patch(Rectangle((97, 130), 180, 18, facecolor=STEEL, edgecolor=DARK, lw=1.2))
    # Narrow blunt tipped blades
    ax.add_patch(Rectangle((277, 133), 50, 6, facecolor=DARK_STEEL, edgecolor=DARK, lw=1))
    ax.add_patch(Rectangle((277, 139), 50, 6, facecolor=DARK_STEEL, edgecolor=DARK, lw=1))
    # Blunt rounded tips
    ax.add_patch(Ellipse((330, 136), 10, 12, facecolor=DARK_STEEL, edgecolor=DARK, lw=1))
    ax.annotate('Narrow blunt tips\n(for sinus tracts)', xy=(320, 136), xytext=(250, 100),
                fontsize=6.5, color=DARK, arrowprops=dict(arrowstyle='->', color=DARK, lw=0.8))
    save(fig, 9)

# ─── 10. PROBES ─────────────────────────────────────────────────
def instr_10():
    fig, ax = fig_ax("Surgical Probes (Assorted)")
    colors_p = [STEEL, '#e8d5a3', DARK_STEEL]
    labels_p = ['Steel probe', 'Silver probe', 'Grooved director']
    for i, (c, lbl) in enumerate(zip(colors_p, labels_p)):
        y = 80 + i*45
        ax.add_patch(Rectangle((30, y), 270, 9, facecolor=c, edgecolor=DARK, lw=1.5))
        # Button tip
        ax.add_patch(Ellipse((305, y+4), 14, 12, facecolor=c, edgecolor=DARK, lw=1.5))
        ax.text(35, y+12, lbl, fontsize=6.5, color=DARK)
        # Groove on director
        if i == 2:
            ax.plot([30, 300], [y+4, y+4], color='#555', lw=1, linestyle='--')
    ax.annotate('Button tip', xy=(305, 84), xytext=(280, 40),
                fontsize=6.5, color=DARK, arrowprops=dict(arrowstyle='->', color=DARK, lw=0.8))
    save(fig, 10)

# ─── 11. POINTED SCISSORS ───────────────────────────────────────
def instr_11():
    fig, ax = fig_ax("Pointed Scissors (Sharp-Sharp)")
    ring_handle(ax, 38, 150, 18)
    ring_handle(ax, 75, 150, 18)
    ax.add_patch(Circle((118, 130), 8, facecolor=GOLD, edgecolor=DARK, lw=1.5))
    ax.add_patch(Rectangle((126, 133), 190, 9, facecolor=STEEL, edgecolor=DARK, lw=1.2))
    pts1 = np.array([[316, 133], [316, 142], [345, 137]])
    ax.add_patch(plt.Polygon(pts1, facecolor=DARK_STEEL, edgecolor=DARK, lw=1))
    ax.add_patch(Rectangle((126, 118), 180, 9, facecolor=STEEL, edgecolor=DARK, lw=1.2))
    pts2 = np.array([[306, 118], [306, 127], [334, 122]])
    ax.add_patch(plt.Polygon(pts2, facecolor=DARK_STEEL, edgecolor=DARK, lw=1))
    ax.annotate('Sharp-sharp tips', xy=(335, 130), xytext=(270, 95),
                fontsize=6.5, color='#c0392b',
                arrowprops=dict(arrowstyle='->', color='#c0392b', lw=0.8))
    save(fig, 11)

# ─── 12. INTESTINAL CLAMP ───────────────────────────────────────
def instr_12():
    fig, ax = fig_ax("Gastric/Intestinal Clamp")
    ring_handle(ax, 38, 140, 18)
    ring_handle(ax, 78, 140, 18)
    ax.add_patch(Rectangle((95, 126), 18, 28, facecolor=DARK_STEEL, edgecolor=DARK, lw=1.5))
    ax.add_patch(Rectangle((113, 130), 190, 9, facecolor=STEEL, edgecolor=DARK, lw=1.2))
    ax.add_patch(Rectangle((113, 141), 190, 9, facecolor=STEEL, edgecolor=DARK, lw=1.2))
    # Long flat crushing jaws
    ax.add_patch(Rectangle((303, 127), 35, 26, facecolor=DARK_STEEL, edgecolor=DARK, lw=1.5))
    for i in range(8):
        ax.plot([305+i*4, 305+i*4], [129, 151], color='white', lw=0.6, alpha=0.6)
    # Rubber tubing on jaw (atraumatic)
    ax.add_patch(Rectangle((303, 127), 35, 6, facecolor='#27ae60', edgecolor=DARK, lw=1,
                            alpha=0.7))
    ax.annotate('Crushing serrated jaws', xy=(320, 140), xytext=(240, 100),
                fontsize=6.5, color=DARK, arrowprops=dict(arrowstyle='->', color=DARK, lw=0.8))
    ax.annotate('Rubber cover\n(atraumatic)', xy=(310, 128), xytext=(200, 175),
                fontsize=6.5, color='#27ae60',
                arrowprops=dict(arrowstyle='->', color='#27ae60', lw=0.8))
    save(fig, 12)

# ─── 13. ABDOMINAL RETRACTOR ────────────────────────────────────
def instr_13():
    fig, ax = fig_ax("Abdominal Retractor (Deaver Type)")
    # Handle
    ax.add_patch(Rectangle((30, 120), 100, 20, facecolor=STEEL, edgecolor=DARK, lw=1.5))
    # Neck/shaft
    ax.add_patch(Rectangle((130, 123), 80, 14, facecolor=STEEL, edgecolor=DARK, lw=1.5))
    # Wide curved blade
    theta = np.linspace(0, np.pi, 40)
    blade_x = 210 + 80*np.cos(theta)
    blade_y = 130 + 80*np.sin(theta)
    ax.fill_between(blade_x, blade_y - 12, blade_y, facecolor=STEEL, edgecolor=DARK, lw=1.5)
    # Shading on blade
    ax.fill_between(blade_x, blade_y - 4, blade_y, facecolor='white', alpha=0.25, edgecolor='none')
    ax.annotate('Wide curved blade\nretracts abdominal wall', xy=(270, 155), xytext=(200, 195),
                fontsize=6.5, color=DARK, arrowprops=dict(arrowstyle='->', color=DARK, lw=0.8))
    ax.annotate('Handle', xy=(80, 120), xytext=(60, 90),
                fontsize=6.5, color=DARK, arrowprops=dict(arrowstyle='->', color=DARK, lw=0.8))
    save(fig, 13)

# ─── 14. TISSUE FORCEPS (ALLIS) ─────────────────────────────────
def instr_14():
    fig, ax = fig_ax("Tissue Forceps (Allis Type)")
    ring_handle(ax, 38, 140, 18)
    ring_handle(ax, 78, 140, 18)
    ax.add_patch(Rectangle((95, 127), 18, 26, facecolor=DARK_STEEL, edgecolor=DARK, lw=1.5))
    for i in range(4):
        ax.plot([97, 111], [130+i*4, 130+i*4], color='white', lw=0.6)
    ax.add_patch(Rectangle((113, 131), 190, 8, facecolor=STEEL, edgecolor=DARK, lw=1.2))
    ax.add_patch(Rectangle((113, 141), 190, 8, facecolor=STEEL, edgecolor=DARK, lw=1.2))
    # Multiple teeth at tip
    for i in range(5):
        ax.add_patch(Rectangle((303+i*6, 129), 4, 12, facecolor=DARK_STEEL,
                               edgecolor=DARK, lw=0.7))
    for i in range(4):
        ax.add_patch(Rectangle((305+i*6, 141), 4, 10, facecolor=DARK_STEEL,
                               edgecolor=DARK, lw=0.7))
    ax.annotate('Multiple fine teeth', xy=(315, 136), xytext=(260, 100),
                fontsize=6.5, color=DARK, arrowprops=dict(arrowstyle='->', color=DARK, lw=0.8))
    save(fig, 14)

# ─── 15. BABCOCK'S FORCEPS ──────────────────────────────────────
def instr_15():
    fig, ax = fig_ax("Babcock's Forceps")
    ring_handle(ax, 38, 140, 18)
    ring_handle(ax, 78, 140, 18)
    ax.add_patch(Rectangle((95, 127), 18, 26, facecolor=DARK_STEEL, edgecolor=DARK, lw=1.5))
    ax.add_patch(Rectangle((113, 131), 160, 8, facecolor=STEEL, edgecolor=DARK, lw=1.2))
    ax.add_patch(Rectangle((113, 141), 160, 8, facecolor=STEEL, edgecolor=DARK, lw=1.2))
    # Fenestrated triangular jaws (no teeth)
    ax.add_patch(plt.Polygon([[273, 125], [340, 135], [273, 125]], closed=False,
                              facecolor='none', edgecolor=DARK, lw=1.5))
    jaw_top = plt.Polygon([[273, 125], [340, 135], [273, 145]], closed=True,
                           facecolor=STEEL, edgecolor=DARK, lw=1.5)
    jaw_bot = plt.Polygon([[273, 155], [330, 145], [273, 145]], closed=True,
                           facecolor=STEEL, edgecolor=DARK, lw=1.5)
    ax.add_patch(jaw_top)
    ax.add_patch(jaw_bot)
    # Fenestration (window)
    ax.add_patch(Ellipse((295, 138), 30, 14, facecolor='#f8f9fa', edgecolor=DARK, lw=1))
    ax.annotate('Fenestrated\natraumatic jaws\n(no teeth)', xy=(300, 138), xytext=(230, 185),
                fontsize=6.5, color='#27ae60',
                arrowprops=dict(arrowstyle='->', color='#27ae60', lw=0.8))
    save(fig, 15)

# ─── 16. KOCHER'S FORCEPS ───────────────────────────────────────
def instr_16():
    fig, ax = fig_ax("Kocher's Forceps")
    ring_handle(ax, 38, 140, 18)
    ring_handle(ax, 78, 140, 18)
    ax.add_patch(Rectangle((95, 127), 18, 26, facecolor=DARK_STEEL, edgecolor=DARK, lw=1.5))
    ax.add_patch(Rectangle((113, 131), 185, 8, facecolor=STEEL, edgecolor=DARK, lw=1.2))
    serrations(ax, 140, 135, 8, 8)
    ax.add_patch(Rectangle((113, 141), 185, 8, facecolor=STEEL, edgecolor=DARK, lw=1.2))
    serrations(ax, 140, 145, 8, 8)
    # Tip TEETH (1-2 interlocking teeth)
    ax.add_patch(Rectangle((298, 129), 10, 6, facecolor=DARK_STEEL, edgecolor=DARK, lw=1))
    ax.add_patch(Rectangle((300, 143), 10, 6, facecolor=DARK_STEEL, edgecolor=DARK, lw=1))
    ax.annotate('Interlocking teeth\nat tip (KEY FEATURE)', xy=(303, 136), xytext=(225, 100),
                fontsize=6.5, color='#c0392b',
                arrowprops=dict(arrowstyle='->', color='#c0392b', lw=0.8))
    save(fig, 16)

# ─── 17. URETHRAL DILATORS ──────────────────────────────────────
def instr_17():
    fig, ax = fig_ax("Urethral Dilators (Assorted Set)")
    widths = [4, 6, 8, 10, 12]
    colors_d = ['#e8d5a3', '#d0d4d8', '#b0b8c1', '#8d9db6', '#6b7a8d']
    for i, (w, c) in enumerate(zip(widths, colors_d)):
        y = 50 + i*30
        ax.add_patch(Rectangle((40, y), 250, w, facecolor=c, edgecolor=DARK, lw=1.2))
        ax.add_patch(Ellipse((295, y+w/2), 14, w+4, facecolor=c, edgecolor=DARK, lw=1.2))
        ax.text(300, y+w/2, f'Fr {10+i*2}', fontsize=6, color=DARK, va='center')
    ax.annotate('Graduated sizes\n(French gauge)', xy=(170, 60), xytext=(120, 190),
                fontsize=6.5, color=DARK, arrowprops=dict(arrowstyle='->', color=DARK, lw=0.8))
    ax.annotate('Tapered/Olive tip', xy=(295, 80), xytext=(270, 195),
                fontsize=6.5, color=DARK, arrowprops=dict(arrowstyle='->', color=DARK, lw=0.8))
    save(fig, 17)

# ─── 18. FOLEY CATHETER ─────────────────────────────────────────
def instr_18():
    fig, ax = fig_ax("Foley Catheter (Rubber)")
    # Tube body
    ax.add_patch(Rectangle((30, 105), 230, 20, facecolor='#f5e6c8', edgecolor='#c9963a', lw=2))
    ax.add_patch(Rectangle((30, 111), 230, 6, facecolor='white', alpha=0.35, edgecolor='none'))
    # Balloon
    ax.add_patch(Ellipse((280, 115), 50, 40, facecolor='#f5c6a0', edgecolor='#c9963a', lw=1.5,
                          alpha=0.85))
    # Eyes
    ax.add_patch(Ellipse((265, 115), 8, 10, facecolor='white', edgecolor='#c9963a', lw=1))
    # Funnel ports
    ax.add_patch(FancyBboxPatch((10, 100), 20, 12, boxstyle='round,pad=2',
                                facecolor='#27ae60', edgecolor=DARK, lw=1.5))  # drainage
    ax.add_patch(FancyBboxPatch((10, 118), 20, 10, boxstyle='round,pad=2',
                                facecolor='#e74c3c', edgecolor=DARK, lw=1.5))  # balloon
    ax.annotate('Inflatable balloon\n(retention)', xy=(280, 115), xytext=(220, 165),
                fontsize=6.5, color=DARK, arrowprops=dict(arrowstyle='->', color=DARK, lw=0.8))
    ax.annotate('Drainage port', xy=(10, 106), xytext=(30, 70),
                fontsize=6.5, color='#27ae60',
                arrowprops=dict(arrowstyle='->', color='#27ae60', lw=0.8))
    ax.annotate('Balloon port', xy=(10, 123), xytext=(30, 160),
                fontsize=6.5, color='#e74c3c',
                arrowprops=dict(arrowstyle='->', color='#e74c3c', lw=0.8))
    save(fig, 18)

# ─── 19. METAL CATHETER ─────────────────────────────────────────
def instr_19():
    fig, ax = fig_ax("Metal Catheter (Male/Female)")
    # Male - J-curved rigid metal
    ax.add_patch(Rectangle((30, 140), 200, 12, facecolor=STEEL, edgecolor=DARK, lw=1.5))
    ax.add_patch(Rectangle((30, 138), 14, 16, facecolor=DARK_STEEL, edgecolor=DARK, lw=1.5))
    # Curve
    theta = np.linspace(0, 0.6*np.pi, 30)
    cx = 230 + 40*np.cos(theta)
    cy = 146 + 40*np.sin(theta)
    ax.fill_between(cx, cy-6, cy+6, facecolor=STEEL, edgecolor=DARK, lw=1)
    ax.add_patch(Ellipse((248, 170), 12, 10, facecolor=STEEL, edgecolor=DARK, lw=1.5))
    ax.text(30, 170, 'Male catheter (long, J-curve)', fontsize=6.5, color=DARK)
    # Female - short straight
    ax.add_patch(Rectangle((30, 80), 110, 10, facecolor=STEEL, edgecolor=DARK, lw=1.5))
    ax.add_patch(Rectangle((30, 78), 12, 14, facecolor=DARK_STEEL, edgecolor=DARK, lw=1.5))
    ax.add_patch(Ellipse((143, 85), 12, 14, facecolor=STEEL, edgecolor=DARK, lw=1.5))
    ax.text(30, 70, 'Female catheter (short, slight curve)', fontsize=6.5, color=DARK)
    save(fig, 19)

# ─── 20. SUTURING NEEDLES ───────────────────────────────────────
def instr_20():
    fig, ax = fig_ax("Suturing Needles (Assorted)")
    # Curved needle
    theta = np.linspace(0, np.pi, 50)
    nx = 100 + 70*np.cos(theta)
    ny = 110 + 70*np.sin(theta)
    ax.plot(nx, ny, color=DARK_STEEL, lw=4)
    # Needle tip
    ax.annotate('', xy=(30, 110), xytext=(45, 110),
                arrowprops=dict(arrowstyle='->', color=DARK_STEEL, lw=2))
    ax.text(30, 95, 'Cutting tip', fontsize=6.5, color='#c0392b')
    # Swaged end
    ax.add_patch(Ellipse((170, 110), 20, 12, facecolor=DARK_STEEL, edgecolor=DARK, lw=1))
    ax.text(170, 100, 'Swaged\n(eyeless)', fontsize=6, color=DARK, ha='center')
    # Straight needle
    ax.plot([180, 320], [150, 150], color=DARK_STEEL, lw=3.5)
    pts = np.array([[316, 147], [316, 153], [328, 150]])
    ax.add_patch(plt.Polygon(pts, facecolor=DARK_STEEL, edgecolor=DARK, lw=1))
    ax.text(190, 160, 'Straight needle (skin)', fontsize=6.5, color=DARK)
    ax.text(80, 195, '3/8 Circle  |  1/2 Circle  |  Cutting  |  Round-bodied',
            fontsize=6, color=DARK)
    save(fig, 20)

# ─── 21. SURGICAL SUTURE ────────────────────────────────────────
def instr_21():
    fig, ax = fig_ax("Surgical Suture Thread")
    # Vicryl packet
    ax.add_patch(FancyBboxPatch((30, 120), 120, 70, boxstyle='round,pad=4',
                                facecolor='#8e44ad', edgecolor=DARK, lw=1.5))
    ax.text(90, 158, 'VICRYL', fontsize=9, color='white', ha='center', fontweight='bold')
    ax.text(90, 145, 'Polyglactin 910', fontsize=6.5, color='#dbc8ee', ha='center')
    ax.text(90, 133, 'Absorbable | Braided', fontsize=6, color='#dbc8ee', ha='center')
    # Ethilon packet
    ax.add_patch(FancyBboxPatch((200, 120), 120, 70, boxstyle='round,pad=4',
                                facecolor='#2c3e50', edgecolor=DARK, lw=1.5))
    ax.text(260, 158, 'ETHILON', fontsize=9, color='white', ha='center', fontweight='bold')
    ax.text(260, 145, 'Nylon', fontsize=6.5, color='#aed6f1', ha='center')
    ax.text(260, 133, 'Non-absorbable | Mono', fontsize=6, color='#aed6f1', ha='center')
    # Suture thread illustration
    theta = np.linspace(0, 4*np.pi, 200)
    sx = 50 + 250*(theta/(4*np.pi))
    sy = 95 + 10*np.sin(theta)
    ax.plot(sx, sy, color='#8e44ad', lw=2)
    ax.text(175, 75, 'Suture thread', fontsize=6.5, color=DARK, ha='center')
    save(fig, 21)

# ─── 22. SPONGE HOLDING FORCEPS ─────────────────────────────────
def instr_22():
    fig, ax = fig_ax("Sponge Holding Forceps (Rampley)")
    ring_handle(ax, 38, 140, 18)
    ring_handle(ax, 78, 140, 18)
    ax.add_patch(Rectangle((95, 127), 18, 26, facecolor=DARK_STEEL, edgecolor=DARK, lw=1.5))
    ax.add_patch(Rectangle((113, 131), 170, 8, facecolor=STEEL, edgecolor=DARK, lw=1.2))
    ax.add_patch(Rectangle((113, 141), 170, 8, facecolor=STEEL, edgecolor=DARK, lw=1.2))
    # Large oval fenestrated jaw
    ax.add_patch(Ellipse((315, 140), 55, 38, facecolor=STEEL, edgecolor=DARK, lw=1.5))
    ax.add_patch(Ellipse((315, 140), 40, 24, facecolor='#f8f9fa', edgecolor=DARK, lw=1))
    ax.annotate('Large oval\nfenestrated jaw\n(key feature)', xy=(315, 140), xytext=(240, 185),
                fontsize=6.5, color='#27ae60',
                arrowprops=dict(arrowstyle='->', color='#27ae60', lw=0.8))
    save(fig, 22)

# ─── 23. RIGHT ANGLE FORCEPS ────────────────────────────────────
def instr_23():
    fig, ax = fig_ax("Right Angle (Cholecystectomy) Forceps")
    ring_handle(ax, 38, 140, 18)
    ring_handle(ax, 78, 140, 18)
    ax.add_patch(Rectangle((95, 127), 18, 26, facecolor=DARK_STEEL, edgecolor=DARK, lw=1.5))
    ax.add_patch(Rectangle((113, 131), 170, 18, facecolor=STEEL, edgecolor=DARK, lw=1.2))
    # 90 degree bend
    ax.add_patch(Rectangle((283, 95), 18, 55, facecolor=STEEL, edgecolor=DARK, lw=1.5))
    # Jaws at bottom (90° to shaft)
    ax.add_patch(Rectangle((260, 95), 40, 8, facecolor=DARK_STEEL, edgecolor=DARK, lw=1))
    ax.add_patch(Rectangle((260, 87), 40, 8, facecolor=DARK_STEEL, edgecolor=DARK, lw=1))
    ax.annotate('90° angled jaws\n(KEY FEATURE)', xy=(280, 91), xytext=(180, 65),
                fontsize=6.5, color='#c0392b',
                arrowprops=dict(arrowstyle='->', color='#c0392b', lw=0.8))
    save(fig, 23)

# ─── 24. STONE HOLDING FORCEPS ──────────────────────────────────
def instr_24():
    fig, ax = fig_ax("Stone Holding Forceps")
    ring_handle(ax, 38, 140, 18)
    ring_handle(ax, 78, 140, 18)
    ax.add_patch(Rectangle((95, 127), 18, 26, facecolor=DARK_STEEL, edgecolor=DARK, lw=1.5))
    ax.add_patch(Rectangle((113, 131), 170, 8, facecolor=STEEL, edgecolor=DARK, lw=1.2))
    ax.add_patch(Rectangle((113, 141), 170, 8, facecolor=STEEL, edgecolor=DARK, lw=1.2))
    # Cupped jaws
    ax.add_patch(Ellipse((305, 130), 35, 20, facecolor=DARK_STEEL, edgecolor=DARK, lw=1.5))
    ax.add_patch(Ellipse((305, 150), 35, 20, facecolor=DARK_STEEL, edgecolor=DARK, lw=1.5))
    ax.add_patch(Ellipse((305, 140), 24, 16, facecolor='#f8f9fa', edgecolor=DARK, lw=1))
    ax.text(285, 90, 'Cup-shaped jaws\n(holds calculi)', fontsize=6.5, color=DARK)
    save(fig, 24)

# ─── 25. PROCTOSCOPE ────────────────────────────────────────────
def instr_25():
    fig, ax = fig_ax("Proctoscope (with Obturator)")
    # Outer tube
    ax.add_patch(Ellipse((175, 140), 80, 80, facecolor=STEEL, edgecolor=DARK, lw=2))
    ax.add_patch(Ellipse((175, 140), 60, 60, facecolor='#f8f9fa', edgecolor=DARK, lw=1.5))
    # Handle
    ax.add_patch(Rectangle((215, 128), 100, 24, facecolor=DARK_STEEL, edgecolor=DARK, lw=1.5))
    # Obturator
    ax.add_patch(Ellipse((175, 140), 52, 52, facecolor='#aed6f1', edgecolor=DARK, lw=1,
                          alpha=0.7))
    ax.add_patch(Circle((175, 140), 10, facecolor=DARK_STEEL, edgecolor=DARK, lw=1))
    ax.annotate('Outer tubular\nspeculum', xy=(135, 140), xytext=(50, 170),
                fontsize=6.5, color=DARK, arrowprops=dict(arrowstyle='->', color=DARK, lw=0.8))
    ax.annotate('Obturator\n(removable)', xy=(175, 140), xytext=(170, 195),
                fontsize=6.5, color='#2980b9',
                arrowprops=dict(arrowstyle='->', color='#2980b9', lw=0.8))
    ax.annotate('Handle', xy=(260, 140), xytext=(290, 175),
                fontsize=6.5, color=DARK, arrowprops=dict(arrowstyle='->', color=DARK, lw=0.8))
    save(fig, 25)

# ─── 26. HEGAR DILATORS ─────────────────────────────────────────
def instr_26():
    fig, ax = fig_ax("Hegar's Dilators (Uterine)")
    widths = [4, 6, 8, 10, 12, 14]
    cs = ['#e8d5a3', '#d0d4d8', '#b0b8c1', STEEL, DARK_STEEL, '#4a5568']
    for i, (w, c) in enumerate(zip(widths, cs)):
        y = 35 + i*28
        ax.add_patch(Rectangle((40, y), 240, w+2, facecolor=c, edgecolor=DARK, lw=1))
        ax.add_patch(Ellipse((45, y+(w+2)/2), 10, w+4, facecolor=c, edgecolor=DARK, lw=1))
        ax.add_patch(Ellipse((280, y+(w+2)/2), 10, w+4, facecolor=c, edgecolor=DARK, lw=1))
        ax.text(300, y+(w+2)/2, f'#{5+i*2}', fontsize=6, color=DARK, va='center')
    ax.text(40, 210, 'Double-ended (each end different size)', fontsize=6.5, color=DARK)
    save(fig, 26)

# ─── 27. ALLIS FORCEPS (SMALL) ──────────────────────────────────
def instr_27():
    fig, ax = fig_ax("Allis Forceps (Small)")
    ring_handle(ax, 38, 140, 18)
    ring_handle(ax, 78, 140, 18)
    ax.add_patch(Rectangle((95, 127), 18, 26, facecolor=DARK_STEEL, edgecolor=DARK, lw=1.5))
    for i in range(4): ax.plot([97, 111], [130+i*4, 130+i*4], color='white', lw=0.6)
    ax.add_patch(Rectangle((113, 131), 160, 7, facecolor=STEEL, edgecolor=DARK, lw=1.2))
    ax.add_patch(Rectangle((113, 142), 160, 7, facecolor=STEEL, edgecolor=DARK, lw=1.2))
    # Fine teeth
    for i in range(6):
        ax.add_patch(Rectangle((273+i*5, 129), 3, 10, facecolor=DARK_STEEL,
                               edgecolor=DARK, lw=0.7))
    for i in range(5):
        ax.add_patch(Rectangle((275+i*5, 142), 3, 8, facecolor=DARK_STEEL,
                               edgecolor=DARK, lw=0.7))
    ax.annotate('Fine interlocking teeth', xy=(285, 138), xytext=(215, 98),
                fontsize=6.5, color=DARK, arrowprops=dict(arrowstyle='->', color=DARK, lw=0.8))
    save(fig, 27)

# ─── 28. PILE HOLDING FORCEPS ───────────────────────────────────
def instr_28():
    fig, ax = fig_ax("Pile Holding Forceps")
    ring_handle(ax, 38, 140, 18)
    ring_handle(ax, 78, 140, 18)
    ax.add_patch(Rectangle((95, 127), 18, 26, facecolor=DARK_STEEL, edgecolor=DARK, lw=1.5))
    ax.add_patch(Rectangle((113, 131), 180, 8, facecolor=STEEL, edgecolor=DARK, lw=1.2))
    ax.add_patch(Rectangle((113, 141), 180, 8, facecolor=STEEL, edgecolor=DARK, lw=1.2))
    # Serrated flat jaws
    ax.add_patch(Rectangle((293, 127), 35, 26, facecolor=DARK_STEEL, edgecolor=DARK, lw=1.5))
    for i in range(6):
        ax.plot([296+i*4, 296+i*4], [129, 151], color='white', lw=0.6, alpha=0.7)
    ax.text(150, 100, 'For grasping haemorrhoidal tissue', fontsize=6.5, color=DARK, ha='center')
    save(fig, 28)

# ─── 29. ARTERY FORCEPS ─────────────────────────────────────────
def instr_29():
    fig, ax = fig_ax("Artery Forceps (Haemostat) - Small/Medium/Large")
    ys = [155, 125, 95]
    lbss = ['Small', 'Medium', 'Large']
    lens = [130, 170, 210]
    for y, lbl, ln in zip(ys, lbss, lens):
        ax.add_patch(Circle((25, y), 8, fill=False, edgecolor=STEEL, lw=1.5))
        ax.add_patch(Circle((40, y), 8, fill=False, edgecolor=STEEL, lw=1.5))
        ax.add_patch(Rectangle((50, y-8), 12, 16, facecolor=DARK_STEEL, edgecolor=DARK, lw=1))
        ax.add_patch(Rectangle((62, y-5), ln, 10, facecolor=STEEL, edgecolor=DARK, lw=1))
        # Jaws
        ax.add_patch(Rectangle((62+ln, y-6), 20, 8, facecolor=DARK_STEEL,
                               edgecolor=DARK, lw=1))
        ax.add_patch(Rectangle((62+ln, y-2), 20, 8, facecolor=DARK_STEEL,
                               edgecolor=DARK, lw=1))
        ax.text(62+ln+22, y, lbl, fontsize=6, color=DARK, va='center')
    ax.text(30, 40, 'No tip teeth - fully serrated jaws (vs Kocher = has teeth)',
            fontsize=6.5, color='#c0392b')
    save(fig, 29)

# ─── 30. SIGMOIDOSCOPE ──────────────────────────────────────────
def instr_30():
    fig, ax = fig_ax("Rigid Sigmoidoscope")
    # Tube
    ax.add_patch(Rectangle((30, 90), 250, 40, facecolor=DARK_STEEL, edgecolor=DARK, lw=2))
    ax.add_patch(Rectangle((30, 96), 250, 20, facecolor='#f8f9fa', edgecolor='none', alpha=0.2))
    ax.add_patch(Ellipse((30, 110), 40, 44, facecolor=STEEL, edgecolor=DARK, lw=2))
    ax.add_patch(Ellipse((280, 110), 40, 44, facecolor='#2c3e50', edgecolor=DARK, lw=2))
    # Eyepiece
    ax.add_patch(Rectangle((280, 100), 60, 20, facecolor=DARK_STEEL, edgecolor=DARK, lw=1.5))
    # Bellows/light
    ax.add_patch(FancyBboxPatch((285, 65), 40, 30, boxstyle='round,pad=3',
                                facecolor='#e67e22', edgecolor=DARK, lw=1.5))
    ax.text(305, 82, 'Bellows', fontsize=5.5, color='white', ha='center')
    ax.text(175, 145, '~25 cm rigid tube', fontsize=6.5, color=DARK, ha='center')
    ax.annotate('Obturator\n(distal)', xy=(30, 110), xytext=(50, 160),
                fontsize=6.5, color=DARK, arrowprops=dict(arrowstyle='->', color=DARK, lw=0.8))
    save(fig, 30)

# ─── 31. BARRON PILE GUN ────────────────────────────────────────
def instr_31():
    fig, ax = fig_ax("Barron Pile's Gun (Banding Device)")
    # Pistol grip handle
    ax.add_patch(FancyBboxPatch((30, 50), 60, 100, boxstyle='round,pad=5',
                                facecolor=DARK_STEEL, edgecolor=DARK, lw=2))
    # Barrel
    ax.add_patch(Rectangle((90, 95), 180, 30, facecolor=STEEL, edgecolor=DARK, lw=2))
    ax.add_patch(Rectangle((90, 100), 180, 14, facecolor='white', alpha=0.2, edgecolor='none'))
    # Drum at tip
    ax.add_patch(Ellipse((285, 110), 40, 40, facecolor=DARK_STEEL, edgecolor=DARK, lw=2))
    ax.add_patch(Ellipse((285, 110), 28, 28, facecolor='#f8f9fa', edgecolor=DARK, lw=1))
    # Trigger
    ax.add_patch(FancyBboxPatch((65, 70), 25, 50, boxstyle='round,pad=3',
                                facecolor='#c0392b', edgecolor=DARK, lw=1.5))
    ax.annotate('Rubber band\ndrum', xy=(285, 110), xytext=(235, 165),
                fontsize=6.5, color=DARK, arrowprops=dict(arrowstyle='->', color=DARK, lw=0.8))
    ax.annotate('Trigger', xy=(78, 85), xytext=(100, 50),
                fontsize=6.5, color='#c0392b',
                arrowprops=dict(arrowstyle='->', color='#c0392b', lw=0.8))
    save(fig, 31)

# ─── 32. LARYNGOSCOPE ───────────────────────────────────────────
def instr_32():
    fig, ax = fig_ax("Laryngoscope (Mac & Miller Blades)")
    # Handle
    ax.add_patch(Rectangle((30, 60), 30, 140, facecolor=DARK_STEEL, edgecolor=DARK, lw=2))
    # Horizontal lines on handle (grip)
    for i in range(8):
        ax.plot([30, 60], [70+i*14, 70+i*14], color='white', lw=0.7, alpha=0.5)
    # Macintosh blade (curved)
    theta = np.linspace(0, 0.7*np.pi, 40)
    bx = 60 + 140*np.sin(theta)
    by = 180 - 140*(1 - np.cos(theta))
    ax.fill_between(bx, by, by+10, facecolor=STEEL, edgecolor=DARK, lw=1.5)
    ax.text(130, 185, 'Mac (curved)', fontsize=6.5, color='#2980b9')
    # Miller blade (straight) below
    ax.add_patch(Rectangle((60, 70), 210, 12, facecolor=STEEL, edgecolor=DARK, lw=1.5))
    ax.text(130, 58, 'Miller (straight)', fontsize=6.5, color='#e67e22')
    # Light bulb
    ax.add_patch(Circle((250, 76), 8, facecolor='#f1c40f', edgecolor=DARK, lw=1))
    ax.annotate('Light at tip', xy=(255, 76), xytext=(280, 110),
                fontsize=6.5, color='#f1c40f',
                arrowprops=dict(arrowstyle='->', color='#f1c40f', lw=0.8))
    save(fig, 32)

# ─── 33. AMBU BAG ───────────────────────────────────────────────
def instr_33():
    fig, ax = fig_ax("Ambu Bag (BVM - Self-Inflating)")
    # Mask
    ax.add_patch(Ellipse((70, 120), 80, 100, facecolor='#aed6f1', edgecolor=DARK, lw=2,
                          alpha=0.8))
    ax.add_patch(Ellipse((70, 120), 60, 78, facecolor='#f8f9fa', edgecolor=DARK, lw=1))
    # Connector
    ax.add_patch(Rectangle((110, 112), 30, 16, facecolor=DARK_STEEL, edgecolor=DARK, lw=1.5))
    # Bag
    ax.add_patch(Ellipse((210, 120), 160, 90, facecolor='#27ae60', edgecolor=DARK, lw=2,
                          alpha=0.85))
    ax.add_patch(Ellipse((210, 120), 140, 70, facecolor='#2ecc71', edgecolor='none', alpha=0.4))
    # Valve
    ax.add_patch(Rectangle((140, 113), 25, 14, facecolor='#e74c3c', edgecolor=DARK, lw=1.5))
    # O2 reservoir
    ax.add_patch(Rectangle((295, 105), 40, 30, facecolor='#3498db', edgecolor=DARK, lw=1.5))
    ax.text(315, 120, 'O2\nReservoir', fontsize=5.5, color='white', ha='center')
    ax.annotate('Transparent mask', xy=(70, 120), xytext=(30, 175),
                fontsize=6.5, color=DARK, arrowprops=dict(arrowstyle='->', color=DARK, lw=0.8))
    ax.annotate('Self-inflating bag', xy=(210, 120), xytext=(170, 185),
                fontsize=6.5, color='#27ae60',
                arrowprops=dict(arrowstyle='->', color='#27ae60', lw=0.8))
    ax.annotate('One-way valve', xy=(152, 113), xytext=(150, 70),
                fontsize=6.5, color='#c0392b',
                arrowprops=dict(arrowstyle='->', color='#c0392b', lw=0.8))
    save(fig, 33)

# ─── 34. SUCTION MACHINE / YANKAUER ─────────────────────────────
def instr_34():
    fig, ax = fig_ax("Suction Machine & Yankauer Tip")
    # Machine box
    ax.add_patch(FancyBboxPatch((20, 80), 130, 110, boxstyle='round,pad=5',
                                facecolor=DARK_STEEL, edgecolor=DARK, lw=2))
    ax.add_patch(FancyBboxPatch((30, 140), 110, 40, boxstyle='round,pad=3',
                                facecolor='#1a1a2e', edgecolor='none'))
    ax.text(85, 165, 'Suction\nMachine', fontsize=7, color='white', ha='center')
    ax.add_patch(Circle((55, 110), 15, facecolor='#27ae60', edgecolor=DARK, lw=1.5))
    ax.text(55, 110, 'ON', fontsize=6, color='white', ha='center', va='center')
    # Canister
    ax.add_patch(FancyBboxPatch((30, 85), 60, 30, boxstyle='round,pad=3',
                                facecolor='#f5e6c8', edgecolor='#c9963a', lw=1.5))
    ax.text(60, 100, 'Canister', fontsize=5.5, color=DARK, ha='center')
    # Yankauer tip
    theta = np.linspace(0.3*np.pi, 0.9*np.pi, 40)
    yx = 220 + 80*np.cos(theta)
    yy = 130 + 80*np.sin(theta)
    ax.fill_between(yx, yy-8, yy+8, facecolor='#aed6f1', edgecolor=DARK, lw=1.5)
    ax.add_patch(Ellipse((230, 80), 24, 24, facecolor='#aed6f1', edgecolor=DARK, lw=1.5))
    ax.text(265, 55, 'Yankauer Suction Tip\n(rigid, oral use)', fontsize=6.5, color=DARK)
    save(fig, 34)

# ─── 35. SKIN GRAFTING KNIFE ────────────────────────────────────
def instr_35():
    fig, ax = fig_ax("Skin Grafting Knife (Humby)")
    # Handle
    ax.add_patch(Rectangle((30, 105), 120, 30, facecolor=DARK_STEEL, edgecolor=DARK, lw=2))
    for i in range(8):
        ax.plot([40+i*13, 40+i*13], [107, 133], color='white', lw=0.6, alpha=0.5)
    # Adjustable guard
    ax.add_patch(Rectangle((150, 112), 140, 8, facecolor=GOLD, edgecolor=DARK, lw=1.5))
    ax.text(175, 108, 'Adjustable guard (sets graft thickness)', fontsize=6, color=DARK)
    # Long blade
    ax.add_patch(Rectangle((150, 120), 170, 15, facecolor=STEEL, edgecolor=DARK, lw=1.5))
    ax.add_patch(Rectangle((150, 127), 170, 4, facecolor='white', alpha=0.3, edgecolor='none'))
    # Sharp edge
    pts = np.array([[320, 120], [340, 127], [320, 135]])
    ax.add_patch(plt.Polygon(pts, facecolor=DARK_STEEL, edgecolor=DARK, lw=1))
    ax.annotate('Sharp long blade', xy=(310, 127), xytext=(250, 165),
                fontsize=6.5, color='#c0392b',
                arrowprops=dict(arrowstyle='->', color='#c0392b', lw=0.8))
    ax.annotate('Adjustable guard', xy=(200, 112), xytext=(170, 75),
                fontsize=6.5, color=GOLD,
                arrowprops=dict(arrowstyle='->', color=GOLD, lw=0.8))
    save(fig, 35)

# ─── 36. SURGICAL BLADES ────────────────────────────────────────
def instr_36():
    fig, ax = fig_ax("Surgical Blades (Assorted Sizes)")
    blade_specs = [
        (10, "Large curved belly - general incision", '#6b7a8d'),
        (11, "Pointed triangular - stab/drain", '#8d9db6'),
        (15, "Small curved - fine/facial", '#b0b8c1'),
        (22, "Large belly - abdominal", '#c8cdd2'),
    ]
    for i, (num, desc, c) in enumerate(blade_specs):
        y = 170 - i*38
        # Blade shape
        if num in [10, 22]:  # curved belly
            pts = np.array([[40, y+5], [150, y+5], [165, y], [150, y-5], [40, y-5]])
        elif num == 11:  # triangular pointed
            pts = np.array([[40, y+5], [160, y+4], [175, y], [160, y-4], [40, y-5]])
        else:  # 15 small curved
            pts = np.array([[40, y+4], [130, y+4], [145, y], [130, y-4], [40, y-4]])
        ax.add_patch(plt.Polygon(pts, facecolor=c, edgecolor=DARK, lw=1.2))
        # Number slot
        ax.add_patch(Ellipse((60, y), 12, 8, facecolor='white', edgecolor=DARK, lw=0.8))
        ax.text(60, y, f'#{num}', fontsize=5, ha='center', va='center', color=DARK)
        ax.text(180, y, desc, fontsize=6, va='center', color=DARK)
    ax.text(80, 25, 'Pre-sterilized  |  SINGLE USE  |  Dispose in sharps container',
            fontsize=6, color='#c0392b', ha='center')
    save(fig, 36)

# ─── 37. BP HANDLE ──────────────────────────────────────────────
def instr_37():
    fig, ax = fig_ax("BP Handle (Scalpel Handle)")
    handles = [
        (3, 'No. 3  (blades 10,11,12,15)', 130, 165),
        (4, 'No. 4  (blades 20,21,22,23)', 200, 145),
        (7, 'No. 7  (long, thin – fine work)', 240, 125),
    ]
    ys = [165, 130, 100]
    lens = [130, 165, 200]
    for (num, lbl, ln, yv), y, l in zip(handles, ys, lens):
        ax.add_patch(Rectangle((30, y), l, 12, facecolor=DARK_STEEL, edgecolor=DARK, lw=1.5))
        for i in range(6):
            ax.plot([38+i*18, 38+i*18], [y+2, y+10], color='white', lw=0.6, alpha=0.5)
        # Blade slot
        ax.add_patch(Rectangle((30+l, y+1), 14, 10, facecolor=STEEL, edgecolor=DARK, lw=1.5))
        ax.text(30+l+20, y+6, f'#{num}', fontsize=6.5, color=DARK, va='center')
    ax.text(30, 55, 'Remove blade with needle holder – NEVER by hand',
            fontsize=6.5, color='#c0392b')
    save(fig, 37)

# ─── 38. SELF-RETAINING RETRACTOR ───────────────────────────────
def instr_38():
    fig, ax = fig_ax("Self-Retaining Retractor (Weitlaner)")
    # Hinge center
    ax.add_patch(Circle((175, 120), 10, facecolor=GOLD, edgecolor=DARK, lw=1.5))
    # Arms spreading outward
    ax.plot([175, 80], [120, 180], color=STEEL, lw=6)
    ax.plot([175, 270], [120, 180], color=STEEL, lw=6)
    ax.plot([175, 80], [120, 60], color=STEEL, lw=4)
    ax.plot([175, 270], [120, 60], color=STEEL, lw=4)
    # Teeth on arms
    for dx, dy in [(-95, 60), (95, 60), (-95, -60), (95, -60)]:
        tx, ty = 175+dx*0.85, 120+dy*0.85
        for i in range(3):
            ax.add_patch(Circle((tx+i*5, ty), 3, facecolor=DARK_STEEL, edgecolor=DARK, lw=0.5))
    # Ratchet bar
    ax.add_patch(Rectangle((130, 115), 90, 10, facecolor='#c8a951', edgecolor=DARK, lw=1))
    ax.annotate('Ratchet (self-locking)', xy=(175, 120), xytext=(185, 160),
                fontsize=6.5, color=DARK, arrowprops=dict(arrowstyle='->', color=DARK, lw=0.8))
    ax.annotate('Toothed blades', xy=(80, 170), xytext=(40, 190),
                fontsize=6.5, color=DARK, arrowprops=dict(arrowstyle='->', color=DARK, lw=0.8))
    save(fig, 38)

# ─── 39. BONE DRILL ─────────────────────────────────────────────
def instr_39():
    fig, ax = fig_ax("Bone Drill Machine (Orthopaedic)")
    # Body
    ax.add_patch(FancyBboxPatch((120, 80), 160, 80, boxstyle='round,pad=6',
                                facecolor=DARK_STEEL, edgecolor=DARK, lw=2))
    ax.add_patch(FancyBboxPatch((135, 90), 130, 55, boxstyle='round,pad=3',
                                facecolor='#1a1a2e', edgecolor='none'))
    ax.text(200, 120, 'BONE DRILL', fontsize=8, color='white', ha='center', va='center',
            fontweight='bold')
    # Handle/grip
    ax.add_patch(FancyBboxPatch((145, 40), 60, 40, boxstyle='round,pad=4',
                                facecolor='#2c3e50', edgecolor=DARK, lw=2))
    ax.text(175, 57, 'Grip', fontsize=6.5, color='white', ha='center')
    # Chuck & drill bit
    ax.add_patch(Rectangle((280, 108), 30, 24, facecolor=GOLD, edgecolor=DARK, lw=1.5))
    ax.text(295, 120, 'Chuck', fontsize=5.5, color=DARK, ha='center')
    ax.add_patch(Rectangle((310, 116), 40, 8, facecolor=STEEL, edgecolor=DARK, lw=1))
    # Drill bit tip
    pts = np.array([[350, 116], [350, 124], [340, 120]])
    ax.add_patch(plt.Polygon(pts, facecolor=DARK_STEEL, edgecolor=DARK, lw=1))
    # Battery
    ax.add_patch(FancyBboxPatch((30, 95), 85, 50, boxstyle='round,pad=4',
                                facecolor='#27ae60', edgecolor=DARK, lw=1.5))
    ax.text(72, 122, 'Battery\nPack', fontsize=6.5, color='white', ha='center')
    save(fig, 39)

# ─── 40. BONE CUTTER ────────────────────────────────────────────
def instr_40():
    fig, ax = fig_ax("Bone Cutter (Liston)")
    ring_handle(ax, 38, 130, 22)
    ring_handle(ax, 85, 130, 22)
    # Heavy spring
    ax.add_patch(Ellipse((58, 130), 25, 70, facecolor='none', edgecolor=DARK, lw=2.5))
    # Short heavy jaws
    ax.add_patch(Rectangle((105, 120), 20, 20, facecolor=DARK_STEEL, edgecolor=DARK, lw=2))
    # Cutting blades
    ax.add_patch(Rectangle((125, 120), 180, 10, facecolor=STEEL, edgecolor=DARK, lw=2))
    ax.add_patch(Rectangle((125, 130), 180, 10, facecolor=STEEL, edgecolor=DARK, lw=2))
    # Sharp cutting tip
    pts1 = np.array([[305, 120], [305, 130], [330, 125]])
    pts2 = np.array([[305, 130], [305, 140], [330, 135]])
    ax.add_patch(plt.Polygon(pts1, facecolor=DARK_STEEL, edgecolor=DARK, lw=1))
    ax.add_patch(plt.Polygon(pts2, facecolor=DARK_STEEL, edgecolor=DARK, lw=1))
    ax.annotate('Heavy cutting jaws', xy=(300, 130), xytext=(240, 95),
                fontsize=6.5, color=DARK, arrowprops=dict(arrowstyle='->', color=DARK, lw=0.8))
    save(fig, 40)

# ─── 41. GIGLI SAW ──────────────────────────────────────────────
def instr_41():
    fig, ax = fig_ax("Gigli Saw (Wire Saw)")
    # Wire with teeth
    t = np.linspace(0, 4*np.pi, 300)
    wx = 50 + 250*(t/(4*np.pi))
    wy = 120 + 6*np.sin(t*3)
    ax.plot(wx, wy, color=DARK_STEEL, lw=2.5)
    # T-bar handles
    ax.add_patch(Rectangle((35, 90), 12, 55, facecolor=STEEL, edgecolor=DARK, lw=2))
    ax.add_patch(Rectangle((20, 140), 40, 12, facecolor=STEEL, edgecolor=DARK, lw=2))
    ax.add_patch(Rectangle((307, 90), 12, 55, facecolor=STEEL, edgecolor=DARK, lw=2))
    ax.add_patch(Rectangle((293, 88), 40, 12, facecolor=STEEL, edgecolor=DARK, lw=2))
    ax.annotate('Flexible wire with\ncutting teeth', xy=(175, 120), xytext=(130, 165),
                fontsize=6.5, color=DARK, arrowprops=dict(arrowstyle='->', color=DARK, lw=0.8))
    ax.annotate('T-bar handle', xy=(40, 115), xytext=(50, 70),
                fontsize=6.5, color=DARK, arrowprops=dict(arrowstyle='->', color=DARK, lw=0.8))
    save(fig, 41)

# ─── 42. BONE SCOOP / CURETTE ───────────────────────────────────
def instr_42():
    fig, ax = fig_ax("Bone Scoop / Volkmann Curette")
    # Handle - double ended
    ax.add_patch(Rectangle((80, 114), 190, 12, facecolor=STEEL, edgecolor=DARK, lw=1.5))
    # Spoon tip 1 (left)
    ax.add_patch(Ellipse((70, 120), 28, 22, facecolor=DARK_STEEL, edgecolor=DARK, lw=1.5))
    ax.add_patch(Ellipse((70, 120), 18, 14, facecolor='#f8f9fa', edgecolor=DARK, lw=0.8))
    # Spoon tip 2 (right - different size)
    ax.add_patch(Ellipse((280, 120), 36, 28, facecolor=DARK_STEEL, edgecolor=DARK, lw=1.5))
    ax.add_patch(Ellipse((280, 120), 24, 18, facecolor='#f8f9fa', edgecolor=DARK, lw=0.8))
    ax.text(40, 155, 'Small cup', fontsize=6.5, color=DARK, ha='center')
    ax.text(280, 155, 'Large cup', fontsize=6.5, color=DARK, ha='center')
    ax.text(175, 90, 'Double-ended curette (Volkmann spoon)', fontsize=6.5, color=DARK,
            ha='center')
    save(fig, 42)

# ─── 43. PERIOSTEUM ELEVATOR ────────────────────────────────────
def instr_43():
    fig, ax = fig_ax("Periosteum Elevator (Rugine / Langenbeck)")
    # Handle
    ax.add_patch(Rectangle((30, 112), 130, 16, facecolor=DARK_STEEL, edgecolor=DARK, lw=2))
    for i in range(7):
        ax.plot([40+i*16, 40+i*16], [114, 126], color='white', lw=0.7, alpha=0.6)
    # Flat blade
    ax.add_patch(Rectangle((160, 107), 150, 26, facecolor=STEEL, edgecolor=DARK, lw=1.5))
    ax.add_patch(Rectangle((160, 113), 150, 8, facecolor='white', alpha=0.3, edgecolor='none'))
    # Chisel end
    pts = np.array([[310, 107], [310, 133], [335, 120]])
    ax.add_patch(plt.Polygon(pts, facecolor=DARK_STEEL, edgecolor=DARK, lw=1))
    ax.annotate('Flat wide blade\n(strips periosteum)', xy=(230, 120), xytext=(200, 170),
                fontsize=6.5, color=DARK, arrowprops=dict(arrowstyle='->', color=DARK, lw=0.8))
    ax.annotate('Blunt chisel tip', xy=(325, 120), xytext=(300, 80),
                fontsize=6.5, color=DARK, arrowprops=dict(arrowstyle='->', color=DARK, lw=0.8))
    save(fig, 43)

# ─── 44. MAGILL FORCEPS ─────────────────────────────────────────
def instr_44():
    fig, ax = fig_ax("Magill Forceps")
    ring_handle(ax, 38, 100, 18)
    ring_handle(ax, 78, 100, 18)
    ax.add_patch(Rectangle((95, 90), 15, 20, facecolor=DARK_STEEL, edgecolor=DARK, lw=1.5))
    # Angled shaft (~90 deg offset)
    ax.plot([110, 200, 290], [100, 100, 180], color=STEEL, lw=7)
    ax.plot([110, 200, 290], [100, 100, 180], color=DARK, lw=7, alpha=0.0)
    # Oval jaws at angled end
    ax.add_patch(Ellipse((295, 188), 35, 20, facecolor=STEEL, edgecolor=DARK, lw=1.5))
    ax.add_patch(Ellipse((295, 172), 30, 16, facecolor=STEEL, edgecolor=DARK, lw=1.5))
    ax.annotate('Offset/angled jaws\n(KEY FEATURE –\nallows airway view)', xy=(290, 180),
                xytext=(210, 55),
                fontsize=6.5, color='#c0392b',
                arrowprops=dict(arrowstyle='->', color='#c0392b', lw=0.8))
    save(fig, 44)

# ─── 45. ENDOTRACHEAL TUBE ──────────────────────────────────────
def instr_45():
    fig, ax = fig_ax("Endotracheal Tube (Cuffed)")
    # Tube
    ax.add_patch(Rectangle((30, 105), 240, 22, facecolor='#aed6f1', edgecolor='#2980b9', lw=2))
    ax.add_patch(Rectangle((30, 111), 240, 6, facecolor='white', alpha=0.35, edgecolor='none'))
    # Cuff
    ax.add_patch(Ellipse((290, 116), 55, 44, facecolor='#5dade2', edgecolor='#2980b9', lw=1.5,
                          alpha=0.7))
    # Pilot balloon line
    ax.plot([290, 290], [95, 60], color='#2980b9', lw=1.5)
    ax.add_patch(Ellipse((290, 55), 18, 14, facecolor='#aed6f1', edgecolor='#2980b9', lw=1.5))
    ax.text(290, 40, 'Pilot balloon', fontsize=5.5, color=DARK, ha='center')
    # Connector
    ax.add_patch(FancyBboxPatch((15, 100), 20, 30, boxstyle='round,pad=2',
                                facecolor=DARK_STEEL, edgecolor=DARK, lw=1.5))
    ax.text(25, 115, '15mm', fontsize=5, color='white', ha='center')
    # Murphy eye
    ax.add_patch(Ellipse((320, 116), 8, 12, facecolor='white', edgecolor='#2980b9', lw=1))
    ax.annotate('15 mm std. connector', xy=(25, 100), xytext=(30, 75),
                fontsize=6.5, color=DARK, arrowprops=dict(arrowstyle='->', color=DARK, lw=0.8))
    ax.annotate('Inflatable cuff', xy=(290, 116), xytext=(220, 160),
                fontsize=6.5, color='#2980b9',
                arrowprops=dict(arrowstyle='->', color='#2980b9', lw=0.8))
    ax.annotate("Murphy's eye", xy=(320, 116), xytext=(310, 160),
                fontsize=6.5, color=DARK, arrowprops=dict(arrowstyle='->', color=DARK, lw=0.8))
    save(fig, 45)

# ─── 46. AUTOCLAVE ──────────────────────────────────────────────
def instr_46():
    fig, ax = fig_ax("High Pressure Autoclave")
    # Main body
    ax.add_patch(FancyBboxPatch((40, 50), 240, 150, boxstyle='round,pad=8',
                                facecolor='#d0d4d8', edgecolor=DARK, lw=2))
    # Door
    ax.add_patch(FancyBboxPatch((55, 65), 100, 120, boxstyle='round,pad=5',
                                facecolor=STEEL, edgecolor=DARK, lw=2))
    # Door handle
    ax.add_patch(Rectangle((60, 120), 30, 10, facecolor=GOLD, edgecolor=DARK, lw=1.5))
    ax.text(75, 125, 'DOOR', fontsize=7, color=DARK, ha='center', va='center')
    # Controls panel
    ax.add_patch(FancyBboxPatch((175, 80), 90, 120, boxstyle='round,pad=4',
                                facecolor='#1a1a2e', edgecolor=DARK, lw=1.5))
    # Pressure gauge
    ax.add_patch(Circle((215, 170), 22, facecolor='white', edgecolor=DARK, lw=1.5))
    ax.text(215, 170, 'PSI', fontsize=6, ha='center', va='center', color=DARK)
    # Temp display
    ax.add_patch(FancyBboxPatch((185, 130), 50, 22, boxstyle='round,pad=2',
                                facecolor='#27ae60', edgecolor='none'))
    ax.text(210, 141, '134°C', fontsize=7, color='white', ha='center')
    # On/Off button
    ax.add_patch(Circle((210, 100), 10, facecolor='#e74c3c', edgecolor=DARK, lw=1))
    ax.text(210, 100, 'ON', fontsize=5, color='white', ha='center', va='center')
    ax.text(100, 30, 'Standard steam sterilization device for OT instruments',
            fontsize=6.5, color=DARK, ha='center')
    save(fig, 46)

# ─── 47. GABRIEL SYRINGE ────────────────────────────────────────
def instr_47():
    fig, ax = fig_ax("Gabriel Syringe (Haemorrhoid Injection)")
    # Barrel
    ax.add_patch(Rectangle((80, 100), 160, 30, facecolor='#d0d4d8', edgecolor=DARK, lw=2))
    ax.add_patch(Rectangle((80, 106), 160, 10, facecolor='white', alpha=0.3, edgecolor='none'))
    # Plunger
    ax.add_patch(Rectangle((55, 108), 25, 14, facecolor=DARK_STEEL, edgecolor=DARK, lw=1.5))
    ax.plot([30, 55], [115, 115], color=DARK_STEEL, lw=3)
    ax.add_patch(Circle((28, 115), 10, facecolor=DARK_STEEL, edgecolor=DARK, lw=1.5))
    # Curved nozzle
    theta = np.linspace(0.5*np.pi, np.pi, 30)
    nx = 240 + 35*np.cos(theta)
    ny = 115 + 35*np.sin(theta)
    ax.fill_between(nx, ny-5, ny+5, facecolor=STEEL, edgecolor=DARK, lw=1.2)
    # Olive tip
    ax.add_patch(Ellipse((205, 150), 16, 12, facecolor=GOLD, edgecolor=DARK, lw=1.5))
    ax.annotate('Curved nozzle &\nolive tip (KEY FEATURE\nfor anal canal)', xy=(230, 148),
                xytext=(240, 175),
                fontsize=6.5, color='#8e44ad',
                arrowprops=dict(arrowstyle='->', color='#8e44ad', lw=0.8))
    ax.text(130, 75, 'Large barrel (for sclerosant injection)', fontsize=6.5, color=DARK,
            ha='center')
    save(fig, 47)

# ─── 48. SIM'S SPECULUM ─────────────────────────────────────────
def instr_48():
    fig, ax = fig_ax("Sim's Speculum (Vaginal)")
    # Upper large blade
    theta_u = np.linspace(0, np.pi, 40)
    bx = 175 + 100*np.cos(theta_u)
    by = 150 + 50*np.sin(theta_u)
    ax.fill_between(bx, by-10, by, facecolor=STEEL, edgecolor=DARK, lw=1.5)
    # Lower smaller blade (angled)
    theta_l = np.linspace(0, np.pi, 40)
    bx2 = 175 + 80*np.cos(theta_l)
    by2 = 80 + 40*np.sin(theta_l)
    ax.fill_between(bx2, by2, by2+10, facecolor=DARK_STEEL, edgecolor=DARK, lw=1.5)
    # Handle
    ax.add_patch(Rectangle((135, 80), 80, 20, facecolor=DARK_STEEL, edgecolor=DARK, lw=1.5))
    ax.annotate('Upper (larger) blade', xy=(200, 155), xytext=(240, 185),
                fontsize=6.5, color=DARK, arrowprops=dict(arrowstyle='->', color=DARK, lw=0.8))
    ax.annotate('Lower (smaller) blade', xy=(200, 90), xytext=(240, 60),
                fontsize=6.5, color=DARK, arrowprops=dict(arrowstyle='->', color=DARK, lw=0.8))
    ax.text(175, 35, 'Used in Sim\'s position (lateral)', fontsize=6.5, color=DARK, ha='center')
    save(fig, 48)

# ─── 49. CUSCO'S SPECULUM ───────────────────────────────────────
def instr_49():
    fig, ax = fig_ax("Cusco's Speculum (Bivalve / Self-Retaining)")
    # Closed blades
    ax.add_patch(Rectangle((120, 80), 110, 14, facecolor=STEEL, edgecolor=DARK, lw=1.5))
    ax.add_patch(Rectangle((120, 96), 110, 14, facecolor=STEEL, edgecolor=DARK, lw=1.5))
    # Open jaws (bivalve)
    pts_top = np.array([[230, 94], [315, 150], [315, 138], [230, 94]])
    pts_bot = np.array([[230, 96], [315, 50], [315, 62], [230, 96]])
    ax.add_patch(plt.Polygon(pts_top, facecolor=STEEL, edgecolor=DARK, lw=1.5))
    ax.add_patch(plt.Polygon(pts_bot, facecolor=STEEL, edgecolor=DARK, lw=1.5))
    # Thumb screw
    ax.add_patch(Circle((175, 95), 12, facecolor=GOLD, edgecolor=DARK, lw=1.5))
    ax.text(175, 95, '⟳', fontsize=10, ha='center', va='center', color=DARK)
    # Handle
    ax.add_patch(Rectangle((60, 84), 60, 22, facecolor=DARK_STEEL, edgecolor=DARK, lw=1.5))
    ax.annotate('Thumb-screw\n(self-retaining)', xy=(175, 95), xytext=(130, 160),
                fontsize=6.5, color=GOLD,
                arrowprops=dict(arrowstyle='->', color=GOLD, lw=0.8))
    ax.annotate('Bivalve blades\n(open to expose cervix)', xy=(300, 105), xytext=(265, 175),
                fontsize=6.5, color=DARK, arrowprops=dict(arrowstyle='->', color=DARK, lw=0.8))
    save(fig, 49)

# ─── 50. UTERINE SOUND ──────────────────────────────────────────
def instr_50():
    fig, ax = fig_ax("Uterine Sound (Graduated Probe)")
    # Thin probe
    ax.add_patch(Rectangle((30, 116), 290, 8, facecolor=STEEL, edgecolor=DARK, lw=1.5))
    ax.add_patch(Rectangle((30, 118), 290, 3, facecolor='white', alpha=0.35, edgecolor='none'))
    # Slight distal curve
    theta = np.linspace(0, 0.3*np.pi, 20)
    cx = 290 + 25*np.cos(theta + np.pi)
    cy = 120 + 25*np.sin(theta + np.pi)
    ax.plot(cx, cy, color=STEEL, lw=6)
    # Knob at tip
    ax.add_patch(Circle((315, 105), 7, facecolor=GOLD, edgecolor=DARK, lw=1.5))
    # Graduation marks
    for i in range(10):
        x = 60 + i*24
        h = 10 if i % 5 == 0 else 6
        ax.plot([x, x], [108, 108+h], color=DARK, lw=1)
        if i % 5 == 0:
            ax.text(x, 104, f'{i+1}', fontsize=5, ha='center', color=DARK)
    ax.text(175, 145, 'Graduation marks (cm) – measures uterine cavity depth',
            fontsize=6.5, color=DARK, ha='center')
    ax.annotate('Knob tip', xy=(315, 105), xytext=(290, 75),
                fontsize=6.5, color=GOLD, arrowprops=dict(arrowstyle='->', color=GOLD, lw=0.8))
    save(fig, 50)

# ─── 51. ANTERIOR VAGINAL WALL RETRACTOR ────────────────────────
def instr_51():
    fig, ax = fig_ax("Anterior Vaginal Wall Retractor")
    # Handle
    ax.add_patch(Rectangle((30, 112), 120, 16, facecolor=DARK_STEEL, edgecolor=DARK, lw=2))
    for i in range(5):
        ax.plot([40+i*20, 40+i*20], [114, 126], color='white', lw=0.7, alpha=0.5)
    # Flat rectangular blade
    ax.add_patch(Rectangle((150, 90), 160, 60, facecolor=STEEL, edgecolor=DARK, lw=1.5))
    ax.add_patch(Rectangle((150, 98), 160, 20, facecolor='white', alpha=0.25, edgecolor='none'))
    # Slight upturn at end
    ax.add_patch(FancyBboxPatch((308, 100), 30, 40, boxstyle='round,pad=3',
                                facecolor=STEEL, edgecolor=DARK, lw=1.5))
    ax.annotate('Flat rectangular\nblade (single-ended)', xy=(230, 120), xytext=(210, 175),
                fontsize=6.5, color=DARK, arrowprops=dict(arrowstyle='->', color=DARK, lw=0.8))
    save(fig, 51)

# ─── 52. GREEN-ARMYTAGE FORCEPS ─────────────────────────────────
def instr_52():
    fig, ax = fig_ax("Green-Armytage Forceps (Uterine Haemostat)")
    ring_handle(ax, 38, 140, 18)
    ring_handle(ax, 78, 140, 18)
    ax.add_patch(Rectangle((95, 127), 18, 26, facecolor=DARK_STEEL, edgecolor=DARK, lw=1.5))
    ax.add_patch(Rectangle((113, 131), 170, 8, facecolor=STEEL, edgecolor=DARK, lw=1.2))
    ax.add_patch(Rectangle((113, 141), 170, 8, facecolor=STEEL, edgecolor=DARK, lw=1.2))
    # Wide flat serrated jaws
    ax.add_patch(Rectangle((283, 125), 45, 12, facecolor=DARK_STEEL, edgecolor=DARK, lw=1.5))
    ax.add_patch(Rectangle((283, 143), 45, 12, facecolor=DARK_STEEL, edgecolor=DARK, lw=1.5))
    for i in range(7):
        ax.plot([287+i*5, 287+i*5], [127, 135], color='white', lw=0.7, alpha=0.7)
        ax.plot([287+i*5, 287+i*5], [145, 153], color='white', lw=0.7, alpha=0.7)
    ax.text(30, 95, 'Used at uterine incision edges during LSCS', fontsize=6.5, color=DARK)
    ax.annotate('Broad serrated jaws\n(uterine haemostasis)', xy=(305, 140), xytext=(225, 185),
                fontsize=6.5, color='#c0392b',
                arrowprops=dict(arrowstyle='->', color='#c0392b', lw=0.8))
    save(fig, 52)

# ─── 53. DOYEN'S RETRACTOR ──────────────────────────────────────
def instr_53():
    fig, ax = fig_ax("Doyen's Retractor (Bladder Retractor / LSCS)")
    # Handle
    ax.add_patch(Rectangle((30, 112), 120, 18, facecolor=DARK_STEEL, edgecolor=DARK, lw=2))
    for i in range(6):
        ax.plot([40+i*17, 40+i*17], [114, 128], color='white', lw=0.7, alpha=0.5)
    # Wide flat curved blade
    theta = np.linspace(-0.25*np.pi, 0.25*np.pi, 40)
    bx = 150 + 140*np.cos(theta)
    by = 120 + 140*np.sin(theta)
    ax.fill_between(bx, by - 14, by, facecolor=STEEL, edgecolor=DARK, lw=1.5)
    ax.fill_between(bx, by - 5, by, facecolor='white', alpha=0.25, edgecolor='none')
    # Upturned edges
    ax.add_patch(Rectangle((150, 120), 12, 30, facecolor=STEEL, edgecolor=DARK, lw=1))
    ax.annotate('Wide curved blade\n(retracts bladder\nduring LSCS)', xy=(240, 130),
                xytext=(210, 185),
                fontsize=6.5, color=DARK, arrowprops=dict(arrowstyle='->', color=DARK, lw=0.8))
    save(fig, 53)

# ─── 54. AGNIKARMA KIT ──────────────────────────────────────────
def instr_54():
    fig, ax = fig_ax("Agnikarma Kit (Shalya Tantra)")
    # Shalaka rods
    for i, c in enumerate([GOLD, '#aed6f1', STEEL]):
        y = 80 + i*40
        ax.add_patch(Rectangle((30, y), 200, 10, facecolor=c, edgecolor=DARK, lw=1.5))
        ax.add_patch(Ellipse((235, y+5), 14, 14, facecolor=c, edgecolor=DARK, lw=1.5))
    ax.text(255, 85, 'Gold Shalaka', fontsize=6.5, color=GOLD, va='center')
    ax.text(255, 125, 'Silver Shalaka', fontsize=6.5, color='#2980b9', va='center')
    ax.text(255, 165, 'Iron Shalaka', fontsize=6.5, color=DARK, va='center')
    # Spirit lamp
    ax.add_patch(FancyBboxPatch((40, 170), 60, 40, boxstyle='round,pad=4',
                                facecolor='#f5e6c8', edgecolor='#c9963a', lw=1.5))
    ax.text(70, 190, 'Spirit\nLamp', fontsize=6, ha='center', color=DARK)
    # Flame
    pts = np.array([[68, 210], [72, 210], [80, 230], [70, 222], [65, 230], [62, 218]])
    ax.add_patch(plt.Polygon(pts, facecolor='#e74c3c', edgecolor='#e67e22', lw=1))
    ax.text(175, 205, 'Ayurvedic thermal cauterization', fontsize=6.5, color='#8e44ad',
            ha='center')
    save(fig, 54)

# ─── 55. SIRAVEDHA KIT ──────────────────────────────────────────
def instr_55():
    fig, ax = fig_ax("Siravedha Kit (Ayurvedic Bloodletting)")
    # Needle (Suchi)
    ax.add_patch(Rectangle((40, 140), 250, 8, facecolor=STEEL, edgecolor=DARK, lw=1.5))
    pts = np.array([[290, 140], [290, 148], [315, 144]])
    ax.add_patch(plt.Polygon(pts, facecolor=DARK_STEEL, edgecolor=DARK, lw=1))
    ax.text(160, 153, 'Suchi (needle)', fontsize=6.5, color=DARK, ha='center')
    # Lancet
    ax.add_patch(Rectangle((40, 110), 160, 8, facecolor=DARK_STEEL, edgecolor=DARK, lw=1.5))
    pts2 = np.array([[200, 110], [200, 118], [220, 114]])
    ax.add_patch(plt.Polygon(pts2, facecolor=DARK_STEEL, edgecolor=DARK, lw=1))
    ax.text(120, 125, 'Lancet (single-use)', fontsize=6.5, color='#c0392b', ha='center')
    # Collection cup
    ax.add_patch(FancyBboxPatch((40, 60), 80, 45, boxstyle='round,pad=4',
                                facecolor='#f5e6c8', edgecolor='#c9963a', lw=1.5))
    ax.text(80, 82, 'Collection\nvessel', fontsize=6, ha='center', color=DARK)
    ax.text(60, 30, 'BT/CT testing required before procedure | Strict aseptic technique',
            fontsize=6, color='#c0392b')
    save(fig, 55)

# ─── 56. CUPPING THERAPY KIT ────────────────────────────────────
def instr_56():
    fig, ax = fig_ax("Cupping Therapy Kit (Shringa/Alabu)")
    cup_data = [
        (80, 130, 45, 55, '#d0d4d8', 'Glass cup'),
        (200, 130, 40, 50, '#27ae60', 'Silicone cup'),
        (300, 130, 32, 42, '#c8a951', 'Small cup'),
    ]
    for cx, cy, rw, rh, c, lbl in cup_data:
        # Cup shape (half ellipse)
        theta = np.linspace(0, np.pi, 40)
        cupx = cx + rw*np.cos(theta)
        cupy = cy + rh*np.sin(theta)
        ax.fill_between(cupx, cy, cupy, facecolor=c, edgecolor=DARK, lw=1.5, alpha=0.8)
        ax.plot([cx-rw, cx+rw], [cy, cy], color=DARK, lw=1.5)
        ax.text(cx, cy-15, lbl, fontsize=6, ha='center', color=DARK)
    # Pump device
    ax.add_patch(FancyBboxPatch((70, 45), 80, 30, boxstyle='round,pad=4',
                                facecolor=DARK_STEEL, edgecolor=DARK, lw=1.5))
    ax.text(110, 60, 'Hand pump', fontsize=6, ha='center', color='white')
    ax.text(175, 25, 'Dry cupping (suction only)  |  Wet cupping (with scarification)',
            fontsize=6, color='#8e44ad', ha='center')
    save(fig, 56)

print("Drawing all instrument diagrams...")
funcs = [
    instr_01, instr_02, instr_03, instr_04, instr_05, instr_06, instr_07, instr_08,
    instr_09, instr_10, instr_11, instr_12, instr_13, instr_14, instr_15, instr_16,
    instr_17, instr_18, instr_19, instr_20, instr_21, instr_22, instr_23, instr_24,
    instr_25, instr_26, instr_27, instr_28, instr_29, instr_30, instr_31, instr_32,
    instr_33, instr_34, instr_35, instr_36, instr_37, instr_38, instr_39, instr_40,
    instr_41, instr_42, instr_43, instr_44, instr_45, instr_46, instr_47, instr_48,
    instr_49, instr_50, instr_51, instr_52, instr_53, instr_54, instr_55, instr_56,
]
for f in funcs:
    try:
        f()
    except Exception as e:
        print(f"  ERROR in {f.__name__}: {e}")

print(f"\nDone! {len(funcs)} diagrams generated.")
Running Command

cd /home/daytona/workspace/surgical-instruments && python3 draw_instruments.py 2…

New Chat Jul 3, 2026 Jaundice and Ascites Correction User Equipment and instruments 1 Computer with internet and printer 2 Spotlight (Shadowless ceiling fitted) 3 Needle holding Forceps(big-medium-small) 4 Dressing drums of Assorted size 5 Intravenous Stand 6 X-ray View Box(double) 7 Cheatle's Forceps 8 Mosquito forceps 9 Scissors straight (Tailor) 10 Scissors curved of different sizes 11 Stitch removal Scissors 12 Dissection forceps 13 Sinus Forceps 14 Probes-Assorted size 15 Pointed scissors 16 Gastric and Intestinal clamps (occlusive and crushing) 17 Abdominal Retractors 18 Tissue Forceps 19 Babcock's Forceps 20 Kocher's Forceps 21 Urethral Dilators 22 Rubber catheters of Assorted size 23 Metal Catheters 24 Suturing Needle (straight or curved) of Assorted size 25 Surgical Thread 26 Sponge holding forceps 27 Right Angle cholecystectomy Forceps 28 Stone holding forceps 29 Proctoscope with or without illuminator 30 Bougies (Megars) 31 Allies Forceps small 32 Allies Forceps big 33 Pile holding forceps 34 Artery Forceps small 35 Artery Forceps big 36 Artery Forceps Medium 37 Sigmoidoscope Rigid or flexible 38 Barron Pile's Gun 39 Laryngoscope Pediatric or Adult 40 Ambu Bag 41 Suction machine Electrical or Manual 42 Emergency light A 43 Skin grafting knife with handle 44 Surgical blades of different size 45 BP Handle of different size 46 Self-Retaining Retractor 47 Bone Drill Machine 48 Bone cutter 49 Giggly Saw 50 Scoop 51 Periosteum elevator 52 Maggler Forceps 53 Endotracheal Tubes of different sizes 54 High Pressure Autoclave 55 Fumigator 56 Refrigerator 57 Nitrous Oxide Cylinder 58 Hydrolic Operation Table 59 Anaesthesia Trolley or Boyle's Apparatus 60 Gabriel Syringe 61 Stretcher with trolley 62 Non mercury sphygmoman meter 63 Doyen's retractor 64 Green Armitage forceps 65 Sterilizer 66 Sim's speculum 67 Anterior vaginal wall retractor 68 Cusco's speculum 69 Uterine sound 70 Anterior Vaginal Wall retractor all this instrument with their identification and there use with there sterilization

Running Command

mkdir -p /home/daytona/workspace/shalya-instruments/images && ls /home/daytona/w…

Writing File

~/shalya-instruments/draw_all.py

#!/usr/bin/env python3
"""Draw all 70 surgical instrument diagrams using matplotlib."""
import os, numpy as np
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from matplotlib.patches import FancyBboxPatch, Circle, Ellipse, Rectangle, Arc
from matplotlib.path import Path
import matplotlib.patheffects as pe

DIR = "/home/daytona/workspace/shalya-instruments/images/"
os.makedirs(DIR, exist_ok=True)

# ── palette ──────────────────────────────────────────────────────
ST   = "#c8cdd2"   # steel
DST  = "#6b7a8d"   # dark steel
HST  = "#8d9db6"   # handle steel
GLD  = "#c8a951"   # gold
SLV  = "#e8d5a3"   # silver
WH   = "#f8f9fa"   # white-ish bg
DRK  = "#1c2833"   # dark
RED  = "#c0392b"
GRN  = "#27ae60"
BLU  = "#2980b9"
YEL  = "#f1c40f"
PRP  = "#8e44ad"
ORG  = "#e67e22"

def fig():
    f, a = plt.subplots(figsize=(3.6,2.3), dpi=96)
    a.set_xlim(0,360); a.set_ylim(0,230)
    a.set_aspect('equal'); a.axis('off')
    f.patch.set_facecolor(WH); a.set_facecolor(WH)
    return f, a

def save(f, n, label=""):
    p = os.path.join(DIR, f"i{n:02d}.png")
    f.savefig(p, dpi=96, bbox_inches='tight', facecolor=WH)
    plt.close(f)
    print(f"  #{n:02d} {label}")

def rings(a, x1, y, r=15):
    for x in [x1, x1+30]:
        a.add_patch(Circle((x,y), r, fill=False, ec=DST, lw=2.2))
        a.add_patch(Circle((x,y), r-5, fill=False, ec=DST, lw=0.8))

def ratchet(a, x, y, w=16, h=22):
    a.add_patch(Rectangle((x,y-h/2),w,h, fc=DST, ec=DRK, lw=1.3))
    for i in range(4): a.plot([x+2,x+w-2],[y-h/2+3+i*4,y-h/2+3+i*4], c='w', lw=0.6)

def shaft(a, x, y, L, th=14, c=ST):
    a.add_patch(Rectangle((x,y-th/2),L,th, fc=c, ec=DRK, lw=1.2))
    a.add_patch(Rectangle((x,y-th/2+th*0.55),L,th*0.28, fc='w', alpha=0.25, ec='none'))

def serrate(a, x, y, h, n=8):
    for i in range(n): a.plot([x,x+5],[y-h/2+(i+.5)*h/n]*2, c=DRK, lw=0.5, alpha=0.6)

def ann(a, txt, xy, xytext, c=DRK):
    a.annotate(txt, xy=xy, xytext=xytext, fontsize=6, color=c,
               arrowprops=dict(arrowstyle='->', color=c, lw=0.7),
               va='center')

# ════════════════════════════════════════════════════════════════
# 1  Computer / Printer  (equipment, not instrument)
def d01():
    f,a=fig()
    a.add_patch(FancyBboxPatch((60,80),220,120,boxstyle='round,pad=6',fc='#2c3e50',ec=DRK,lw=2))
    a.add_patch(Rectangle((80,95),180,90,fc='#1a252f',ec='none'))
    a.text(170,140,'COMPUTER',ha='center',va='center',fontsize=12,color=WH,fontweight='bold')
    a.add_patch(Rectangle((130,75),80,8,fc='#555',ec=DRK,lw=1))
    a.add_patch(FancyBboxPatch((90,40),180,30,boxstyle='round,pad=3',fc=DST,ec=DRK,lw=1.5))
    a.text(180,55,'KEYBOARD',ha='center',va='center',fontsize=7,color=WH)
    ann(a,'Monitor',( 170,200),(240,215))
    save(f,1,'Computer & Printer')

# 2  Shadowless Spotlight
def d02():
    f,a=fig()
    # Ceiling rail
    a.add_patch(Rectangle((40,195),280,12,fc=DST,ec=DRK,lw=1.5))
    # Arm
    a.plot([180,180],[183,155],c=DST,lw=6)
    # Lamp head
    a.add_patch(Circle((180,130),48,fc='#d0d4d8',ec=DRK,lw=2))
    a.add_patch(Circle((180,130),40,fc='#1a1a2e',ec=DRK,lw=1.5))
    # Light beams
    for ang in np.linspace(-60,60,7):
        r=np.radians(ang+270)
        a.plot([180,180+70*np.cos(r)],[130,130+70*np.sin(r)],c=YEL,lw=1.2,alpha=0.7)
    # Reflector petals
    for ang in np.linspace(0,360,9)[:-1]:
        r=np.radians(ang)
        a.add_patch(Ellipse((180+28*np.cos(r),130+28*np.sin(r)),10,6,
                            angle=ang,fc=YEL,ec='none',alpha=0.6))
    ann(a,'Reflector array',(180,130),(260,160))
    ann(a,'Ceiling rail',(180,201),(260,215),GLD)
    save(f,2,'Shadowless Spotlight')

# 3  Needle Holder
def d03():
    f,a=fig()
    rings(a,38,130)
    ratchet(a,84,130)
    shaft(a,100,130,185)
    # Heavy jaws
    a.add_patch(Rectangle((285,121),50,18,fc=DST,ec=DRK,lw=1.5))
    serrate(a,287,130,16,10); serrate(a,297,130,16,10); serrate(a,307,130,16,10)
    ann(a,'Ring handles',(53,130),(30,95))
    ann(a,'Ratchet lock',(92,130),(90,95))
    ann(a,'Serrated jaws',(310,130),(290,95))
    a.text(180,195,'Needle Holder  (big/medium/small)',ha='center',fontsize=7.5,
           color=DRK,fontweight='bold')
    save(f,3,'Needle Holder')

# 4  Dressing Drum
def d04():
    f,a=fig()
    a.add_patch(Rectangle((80,55),200,130,fc='#c8cdd2',ec=DRK,lw=2))
    a.add_patch(Ellipse((180,185),200,24,fc=DST,ec=DRK,lw=2))
    a.add_patch(Ellipse((180,55),200,24,fc='#9aa5b0',ec=DRK,lw=2))
    a.add_patch(Rectangle((80,105),200,18,fc=DST,ec=DRK,lw=1.5))
    for i in range(5):
        for j in range(2):
            a.add_patch(Ellipse((110+i*30,72+j*32),10,8,fc=DRK,ec='none',alpha=0.65))
    a.add_patch(Rectangle((150,185),60,12,fc=DST,ec=DRK,lw=1.5))
    ann(a,'Perforations',(140,85),(40,165))
    ann(a,'Sliding band',(180,114),(250,155),GLD)
    ann(a,'Handle',(180,191),(270,215))
    a.text(180,26,'Dressing Drum – stainless steel, assorted sizes',
           ha='center',fontsize=7,color=DRK,fontstyle='italic')
    save(f,4,'Dressing Drum')

# 5  IV Stand
def d05():
    f,a=fig()
    a.plot([180,180],[30,195],c=DST,lw=7)
    # Hooks
    for dx in [-1,1]:
        t=np.linspace(0,np.pi,30)
        a.plot(180+dx*20*np.cos(t),195+20*np.sin(t),c=ST,lw=4)
    # Base star
    for ang in np.linspace(0,360,6)[:-1]:
        r=np.radians(ang)
        a.plot([180,180+70*np.cos(r)],[30,30+20*np.sin(r)],c=DST,lw=5)
        a.add_patch(Circle((180+70*np.cos(r),30+20*np.sin(r)),7,fc=DRK,ec=DRK))
    # IV bag
    a.add_patch(FancyBboxPatch((130,175),60,40,boxstyle='round,pad=5',
                               fc='#aed6f1',ec=BLU,lw=1.5,alpha=0.8))
    a.text(160,195,'IV\nBAG',ha='center',va='center',fontsize=7,color=BLU)
    a.plot([160,160],[175,160],c=BLU,lw=1.5)
    ann(a,'Height-adjustable pole',(180,120),(230,120))
    save(f,5,'IV Stand')

# 6  X-Ray View Box
def d06():
    f,a=fig()
    a.add_patch(Rectangle((30,50),300,155,fc='#2c3e50',ec=DRK,lw=2.5))
    a.add_patch(Rectangle((40,60),280,135,fc='#e8f4f8',ec='#aed6f1',lw=1.5))
    # X-ray film silhouette
    a.add_patch(Rectangle((55,70),120,110,fc='#1a252f',ec='#555',lw=1,alpha=0.7))
    a.add_patch(Rectangle((195,70),110,110,fc='#1a252f',ec='#555',lw=1,alpha=0.7))
    # Chest silhouette
    for xo,yo in [(115,125),(250,125)]:
        a.add_patch(Ellipse((xo,yo),40,55,fc='none',ec='#7fb3d3',lw=1.5,alpha=0.7))
        a.plot([xo,xo],[yo-27,yo-40],c='#aed6f1',lw=1.5,alpha=0.7)
    # Switch
    a.add_patch(Circle((180,40),8,fc=GRN,ec=DRK,lw=1))
    a.text(180,40,'ON',ha='center',va='center',fontsize=5,color=WH)
    ann(a,'Illuminated viewing panel',(180,128),(280,155))
    ann(a,'Double panel (2 films)',(180,75),(280,60))
    save(f,6,'X-Ray View Box')

# 7  Cheatle's Forceps
def d07():
    f,a=fig()
    rings(a,28,185)
    a.plot([38,175],[170,120],c=ST,lw=6)
    a.plot([65,205],[170,120],c=ST,lw=6)
    a.add_patch(Circle((190,115),8,fc=GLD,ec=DRK,lw=1.5))
    t=np.linspace(1.05*np.pi,1.6*np.pi,30)
    for dx in [0,12]:
        a.plot(190+dx+65*np.cos(t),115+90*np.sin(t),c=DST,lw=5)
    ann(a,'Ring handles\n(above solution)',(46,185),(100,205))
    ann(a,'Curved spoon\nblades',(260,60),(290,35))
    ann(a,'Pivot',(190,115),(250,145),GLD)
    a.text(180,22,'Cheatle\'s Forceps – stored in antiseptic jar',
           ha='center',fontsize=7,color=DRK,fontstyle='italic')
    save(f,7,"Cheatle's Forceps")

# 8  Mosquito Forceps
def d08():
    f,a=fig()
    rings(a,30,130,13)
    ratchet(a,74,130,14,20)
    shaft(a,88,130,205,12)
    # Fine tips
    a.add_patch(Rectangle((293,126),32,6,fc=DST,ec=DRK,lw=1))
    a.add_patch(Rectangle((293,132),28,6,fc=DST,ec=DRK,lw=1))
    pts=np.array([[325,126],[325,138],[342,132]])
    a.add_patch(plt.Polygon(pts,fc=DST,ec=DRK,lw=1))
    ann(a,'Fine pointed tips\n(fully serrated)',(330,132),(290,100))
    a.text(180,195,'Mosquito Forceps (Halsted) – Small haemostat',
           ha='center',fontsize=7.5,color=DRK,fontweight='bold')
    save(f,8,'Mosquito Forceps')

# 9  Scissors Straight
def d09():
    f,a=fig()
    rings(a,30,130,16)
    a.add_patch(Circle((110,130),8,fc=GLD,ec=DRK,lw=1.5))
    shaft(a,118,135,195,9)
    shaft(a,118,121,185,9)
    for pts,fc in [([np.array([[313,135],[313,144],[340,139]])],DST),
                   ([np.array([[303,121],[303,130],[328,125]])],DST)]:
        a.add_patch(plt.Polygon(pts[0],fc=fc,ec=DRK,lw=1))
    ann(a,'Straight blades',(230,128),(230,100))
    ann(a,'Pivot',(110,130),(70,100),GLD)
    a.text(180,195,'Scissors Straight (Tailor) – S/B or S/S tips',
           ha='center',fontsize=7.5,color=DRK,fontweight='bold')
    save(f,9,'Scissors Straight')

# 10  Scissors Curved
def d10():
    f,a=fig()
    rings(a,30,130,16)
    a.add_patch(Circle((110,130),8,fc=GLD,ec=DRK,lw=1.5))
    t=np.linspace(0,.55*np.pi,40)
    for dy,th in [(135,9),(121,9)]:
        bx=118+175*np.sin(t); by=dy+30*(1-np.cos(t))
        a.fill_between(bx,by,by+th,fc=ST,ec=DRK,lw=1.2)
    ann(a,'Curved blades',(250,160),(270,190))
    a.text(180,195,'Curved Scissors (Mayo/Metzenbaum) – assorted sizes',
           ha='center',fontsize=7.5,color=DRK,fontweight='bold')
    save(f,10,'Scissors Curved')

# 11  Stitch Removal Scissors
def d11():
    f,a=fig()
    rings(a,30,130,16)
    a.add_patch(Circle((110,130),8,fc=GLD,ec=DRK,lw=1.5))
    shaft(a,118,135,195,9)
    shaft(a,118,121,160,9)
    t=np.linspace(0,np.pi,30)
    a.plot(278+12*np.cos(t),130+12*np.sin(t),c=DST,lw=4)
    ann(a,'Hook blade\n(slides under suture)',(285,142),(250,175),RED)
    a.text(180,195,'Stitch Removal Scissors – hook lower blade',
           ha='center',fontsize=7.5,color=DRK,fontweight='bold')
    save(f,11,'Stitch Removal Scissors')

# 12  Dissection Forceps
def d12():
    f,a=fig()
    # Spring-type tweezer
    shaft(a,55,148,250,9,ST)
    shaft(a,55,103,250,9,ST)
    a.add_patch(Ellipse((55,126),18,55,fc='none',ec=DRK,lw=2.5))
    for i in range(4):
        a.add_patch(Rectangle((305+i*5,148),4,11,fc=DST,ec=DRK,lw=0.7))
    for i in range(3):
        a.add_patch(Rectangle((307+i*5,100),4,10,fc=DST,ec=DRK,lw=0.7))
    ann(a,'Spring (no rings)\nheld like tweezers',(55,126),(100,165))
    ann(a,'Rat-tooth tips\n(toothed type)',(315,150),(285,185))
    a.text(180,25,'Dissection / Thumb Forceps – toothed & plain variants',
           ha='center',fontsize=7,color=DRK)
    save(f,12,'Dissection Forceps')

# 13  Sinus Forceps
def d13():
    f,a=fig()
    rings(a,30,130)
    ratchet(a,78,130)
    shaft(a,94,130,200,16)
    a.add_patch(Rectangle((294,127),45,5,fc=DST,ec=DRK,lw=1))
    a.add_patch(Rectangle((294,132),45,5,fc=DST,ec=DRK,lw=1))
    a.add_patch(Ellipse((342,130),10,12,fc=DST,ec=DRK,lw=1))
    ann(a,'Narrow blunt tips\n(enter sinus tracts)',(335,130),(290,100))
    a.text(180,195,'Sinus Forceps – thin blunt-tipped, slightly curved',
           ha='center',fontsize=7.5,color=DRK,fontweight='bold')
    save(f,13,'Sinus Forceps')

# 14  Probes
def d14():
    f,a=fig()
    cs=[ST,SLV,DST]; lbs=['Steel probe','Silver probe (malleable)','Grooved director']
    for i,(c,lb) in enumerate(zip(cs,lbs)):
        y=70+i*48
        a.add_patch(Rectangle((35,y),265,8,fc=c,ec=DRK,lw=1.4))
        a.add_patch(Ellipse((305,y+4),13,12,fc=c,ec=DRK,lw=1.4))
        a.text(38,y+12,lb,fontsize=6.5,color=DRK)
        if i==2:
            a.plot([35,300],[y+4,y+4],c='#555',lw=0.8,ls='--')
    ann(a,'Button tip',(305,74),(310,40))
    a.text(180,205,'Probes – assorted sizes; used to explore sinus/fistula tracts',
           ha='center',fontsize=7,color=DRK)
    save(f,14,'Probes')

# 15  Pointed Scissors
def d15():
    f,a=fig()
    rings(a,30,130,16)
    a.add_patch(Circle((110,130),8,fc=GLD,ec=DRK,lw=1.5))
    shaft(a,118,135,195,9)
    shaft(a,118,121,185,9)
    pts1=np.array([[313,135],[313,144],[342,138]])
    pts2=np.array([[303,121],[303,130],[330,125]])
    a.add_patch(plt.Polygon(pts1,fc=DST,ec=DRK,lw=1))
    a.add_patch(plt.Polygon(pts2,fc=DST,ec=DRK,lw=1))
    ann(a,'Sharp-sharp tips',(325,132),(285,100),RED)
    a.text(180,195,'Pointed Scissors (S/S) – precise fine cutting',
           ha='center',fontsize=7.5,color=DRK,fontweight='bold')
    save(f,15,'Pointed Scissors')

# 16  Gastric & Intestinal Clamps
def d16():
    f,a=fig()
    rings(a,28,130)
    ratchet(a,74,130)
    shaft(a,90,135,185,8)
    shaft(a,90,121,185,8)
    a.add_patch(Rectangle((275,120),55,24,fc=DST,ec=DRK,lw=1.5))
    for i in range(9): a.plot([278+i*5,278+i*5],[122,142],c='w',lw=0.5,alpha=0.6)
    a.add_patch(Rectangle((275,120),55,6,fc=GRN,ec=DRK,lw=1,alpha=0.7))
    ann(a,'Long crushing jaws\n(deeply serrated)',(302,132),(250,100))
    ann(a,'Rubber sleeve\n(atraumatic type)',(302,123),(230,170),GRN)
    a.text(180,22,'Gastric & Intestinal Clamps – occlusive and crushing',
           ha='center',fontsize=7,color=DRK)
    save(f,16,'Gastric/Intestinal Clamps')

# 17  Abdominal Retractors
def d17():
    f,a=fig()
    a.add_patch(Rectangle((30,118),120,18,fc=DST,ec=DRK,lw=2))
    for i in range(6): a.plot([42+i*17,42+i*17],[120,134],c='w',lw=0.6,alpha=0.5)
    a.add_patch(Rectangle((150,121),80,12,fc=ST,ec=DRK,lw=1.5))
    t=np.linspace(-0.3*np.pi,0.3*np.pi,40)
    bx=230+110*np.cos(t); by=127+110*np.sin(t)
    a.fill_between(bx,by-14,by,fc=ST,ec=DRK,lw=1.5)
    a.fill_between(bx,by-5,by,fc='w',alpha=0.25,ec='none')
    ann(a,'Wide curved blade\n(retracts abdom. wall)',(290,140),(280,185))
    ann(a,'Handle',(90,127),(70,95))
    a.text(180,22,'Abdominal Retractors – Deaver, Morris, Richardson',
           ha='center',fontsize=7,color=DRK)
    save(f,17,'Abdominal Retractors')

# 18  Tissue Forceps (Allis)
def d18():
    f,a=fig()
    rings(a,28,130)
    ratchet(a,74,130)
    shaft(a,90,134,185,8); shaft(a,90,122,185,8)
    for i in range(5):
        a.add_patch(Rectangle((275+i*6,120),4,12,fc=DST,ec=DRK,lw=0.7))
    for i in range(4):
        a.add_patch(Rectangle((277+i*6,132),4,10,fc=DST,ec=DRK,lw=0.7))
    ann(a,'Multiple fine teeth\n(Allis type)',(300,133),(260,100))
    a.text(180,195,'Tissue Forceps (Allis) – grasps fascia/bowel serosa',
           ha='center',fontsize=7.5,color=DRK,fontweight='bold')
    save(f,18,'Tissue Forceps')

# 19  Babcock's Forceps
def d19():
    f,a=fig()
    rings(a,28,130)
    ratchet(a,74,130)
    shaft(a,90,134,170,8); shaft(a,90,122,170,8)
    a.add_patch(plt.Polygon(np.array([[260,120],[330,130],[260,130]]),
                            fc=ST,ec=DRK,lw=1.5))
    a.add_patch(plt.Polygon(np.array([[260,130],[330,130],[260,140]]),
                            fc=ST,ec=DRK,lw=1.5))
    a.add_patch(Ellipse((290,130),34,16,fc=WH,ec=DRK,lw=1))
    ann(a,'Fenestrated\natraumatic jaws\n(NO teeth)',( 295,130),(235,185),GRN)
    a.text(180,22,"Babcock's Forceps – atraumatic hollow-organ grasper",
           ha='center',fontsize=7,color=DRK)
    save(f,19,"Babcock's Forceps")

# 20  Kocher's Forceps
def d20():
    f,a=fig()
    rings(a,28,130)
    ratchet(a,74,130)
    shaft(a,90,134,195,8); shaft(a,90,122,195,8)
    serrate(a,120,134,8,10); serrate(a,120,122,8,10)
    serrate(a,150,134,8,10); serrate(a,150,122,8,10)
    # Tip TEETH
    a.add_patch(Rectangle((285,128),10,6,fc=DST,ec=DRK,lw=1))
    a.add_patch(Rectangle((287,136),10,6,fc=DST,ec=DRK,lw=1))
    ann(a,'Tip teeth – KEY\ndistinguishing feature',(292,132),(240,100),RED)
    a.text(180,195,"Kocher's Forceps – serrated jaws + interlocking tip teeth",
           ha='center',fontsize=7.5,color=DRK,fontweight='bold')
    save(f,20,"Kocher's Forceps")

# 21  Urethral Dilators
def d21():
    f,a=fig()
    widths=[4,6,8,10,12]
    cs=['#e8d5a3','#d0d4d8',ST,HST,DST]
    for i,(w,c) in enumerate(zip(widths,cs)):
        y=45+i*33
        a.add_patch(Rectangle((40,y),250,w+2,fc=c,ec=DRK,lw=1.1))
        a.add_patch(Ellipse((295,y+(w+2)/2),13,w+4,fc=c,ec=DRK,lw=1.1))
        a.text(310,y+(w+2)/2,f'Fr {10+i*2}',fontsize=6,color=DRK,va='center')
    ann(a,'Graduated (French gauge)',(170,48),(130,185))
    ann(a,'Tapered/olive tip',(295,48),(290,190))
    save(f,21,'Urethral Dilators')

# 22  Rubber Catheters
def d22():
    f,a=fig()
    a.add_patch(Rectangle((30,100),240,22,fc='#f5e6c8',ec='#c9963a',lw=2))
    a.add_patch(Rectangle((30,107),240,6,fc='w',alpha=0.3,ec='none'))
    a.add_patch(Ellipse((288,111),55,44,fc='#f5c6a0',ec='#c9963a',lw=1.5,alpha=0.8))
    a.add_patch(Ellipse((272,111),8,10,fc='w',ec='#c9963a',lw=1))
    a.add_patch(FancyBboxPatch((10,97),20,13,boxstyle='round,pad=2',fc=GRN,ec=DRK,lw=1.5))
    a.add_patch(FancyBboxPatch((10,115),20,10,boxstyle='round,pad=2',fc=RED,ec=DRK,lw=1.5))
    ann(a,'Inflatable balloon\n(Foley – retention)',(288,111),(220,165))
    ann(a,'Drainage port',(20,97),(40,70),GRN)
    ann(a,'Balloon port',(20,120),(40,155),RED)
    a.text(155,55,'Rubber Catheter (Foley/Nelaton) – assorted Fr sizes',
           ha='center',fontsize=7,color=DRK)
    save(f,22,'Rubber Catheters')

# 23  Metal Catheters
def d23():
    f,a=fig()
    shaft(a,30,148,200,12,ST)
    a.add_patch(Rectangle((30,146),14,16,fc=DST,ec=DRK,lw=1.5))
    t=np.linspace(0,0.6*np.pi,30)
    cx=230+40*np.cos(t); cy=154+40*np.sin(t)
    a.fill_between(cx,cy-6,cy+6,fc=ST,ec=DRK,lw=1)
    a.add_patch(Ellipse((250,178),12,10,fc=ST,ec=DRK,lw=1.5))
    a.text(30,170,'Male – long J-curve',fontsize=7,color=DRK)
    shaft(a,30,88,110,10,ST)
    a.add_patch(Rectangle((30,86),12,14,fc=DST,ec=DRK,lw=1.5))
    a.add_patch(Ellipse((143,93),12,14,fc=ST,ec=DRK,lw=1.5))
    a.text(30,70,'Female – short, slight curve',fontsize=7,color=DRK)
    a.text(180,30,'Metal Catheter – rigid stainless steel',
           ha='center',fontsize=7,color=DRK)
    save(f,23,'Metal Catheters')

# 24  Suturing Needles
def d24():
    f,a=fig()
    t=np.linspace(0,np.pi,50)
    nx=105+72*np.cos(t); ny=118+72*np.sin(t)
    a.plot(nx,ny,c=DST,lw=4)
    a.annotate('',xy=(34,118),xytext=(50,118),
               arrowprops=dict(arrowstyle='->',color=DST,lw=2))
    a.text(25,108,'Cutting\ntip',fontsize=6,color=RED)
    a.add_patch(Ellipse((175,118),20,12,fc=DST,ec=DRK,lw=1))
    a.text(175,103,'Swaged',fontsize=6,color=DRK,ha='center')
    a.plot([190,330],[155,155],c=DST,lw=3.5)
    pts=np.array([[326,152],[326,158],[340,155]])
    a.add_patch(plt.Polygon(pts,fc=DST,ec=DRK,lw=1))
    a.text(255,165,'Straight (skin)',fontsize=6.5,color=DRK)
    a.text(180,200,'Suturing Needles – cutting & round-bodied, str/curved',
           ha='center',fontsize=7.5,color=DRK,fontweight='bold')
    save(f,24,'Suturing Needles')

# 25  Surgical Thread
def d25():
    f,a=fig()
    a.add_patch(FancyBboxPatch((25,115),130,75,boxstyle='round,pad=4',fc=PRP,ec=DRK,lw=1.5))
    a.text(90,155,'VICRYL',ha='center',fontsize=9,color='w',fontweight='bold')
    a.text(90,142,'Polyglactin 910',ha='center',fontsize=6.5,color='#dbc8ee')
    a.text(90,130,'Absorbable | Braided',ha='center',fontsize=6,color='#dbc8ee')
    a.add_patch(FancyBboxPatch((205,115),130,75,boxstyle='round,pad=4',fc='#2c3e50',ec=DRK,lw=1.5))
    a.text(270,155,'ETHILON',ha='center',fontsize=9,color='w',fontweight='bold')
    a.text(270,142,'Nylon',ha='center',fontsize=6.5,color='#aed6f1')
    a.text(270,130,'Non-absorbable|Mono',ha='center',fontsize=6,color='#aed6f1')
    t2=np.linspace(0,4*np.pi,200)
    a.plot(40+275*(t2/(4*np.pi)),95+8*np.sin(t2),c=PRP,lw=2)
    a.text(180,80,'Suture thread',ha='center',fontsize=6.5,color=DRK)
    a.text(180,25,'Vicryl 2-0,3-0 | Ethilon 2-0,3-0,4-0',
           ha='center',fontsize=7,color=DRK,fontstyle='italic')
    save(f,25,'Surgical Thread')

# 26  Sponge Holding Forceps
def d26():
    f,a=fig()
    rings(a,28,130)
    ratchet(a,74,130)
    shaft(a,90,134,180,8); shaft(a,90,122,180,8)
    a.add_patch(Ellipse((318,130),56,40,fc=ST,ec=DRK,lw=1.5))
    a.add_patch(Ellipse((318,130),40,26,fc=WH,ec=DRK,lw=1))
    ann(a,'Large oval\nfenestrated jaw',(318,130),(250,185),GRN)
    a.text(180,22,'Sponge Holding Forceps (Rampley) – skin prep & mopping',
           ha='center',fontsize=7,color=DRK)
    save(f,26,'Sponge Holding Forceps')

# 27  Right Angle Forceps
def d27():
    f,a=fig()
    rings(a,28,130)
    ratchet(a,74,130)
    shaft(a,90,127,185,16)
    a.add_patch(Rectangle((275,80),18,62,fc=ST,ec=DRK,lw=1.5))
    a.add_patch(Rectangle((253,80),42,8,fc=DST,ec=DRK,lw=1))
    a.add_patch(Rectangle((253,88),42,8,fc=DST,ec=DRK,lw=1))
    ann(a,'90° angled jaws\n(KEY FEATURE)',(275,84),(200,55),RED)
    a.text(180,205,'Right Angle Forceps – passes ligatures behind vessels/ducts',
           ha='center',fontsize=7.5,color=DRK,fontweight='bold')
    save(f,27,'Right Angle Forceps')

# 28  Stone Holding Forceps
def d28():
    f,a=fig()
    rings(a,28,130)
    ratchet(a,74,130)
    shaft(a,90,134,180,8); shaft(a,90,122,180,8)
    a.add_patch(Ellipse((308,122),35,22,fc=DST,ec=DRK,lw=1.5))
    a.add_patch(Ellipse((308,142),35,22,fc=DST,ec=DRK,lw=1.5))
    a.add_patch(Ellipse((308,132),25,18,fc=WH,ec=DRK,lw=1))
    ann(a,'Cup-shaped jaws\n(holds calculi)',(310,132),(255,185))
    a.text(180,22,'Stone Holding Forceps – extraction of gallstones/calculi',
           ha='center',fontsize=7,color=DRK)
    save(f,28,'Stone Holding Forceps')

# 29  Proctoscope
def d29():
    f,a=fig()
    a.add_patch(Ellipse((175,130),82,82,fc=ST,ec=DRK,lw=2))
    a.add_patch(Ellipse((175,130),62,62,fc=WH,ec=DRK,lw=1.5))
    a.add_patch(Rectangle((216,118),105,24,fc=DST,ec=DRK,lw=1.5))
    a.add_patch(Ellipse((175,130),52,52,fc='#aed6f1',ec=DRK,lw=1,alpha=0.7))
    a.add_patch(Circle((175,130),10,fc=DST,ec=DRK,lw=1))
    ann(a,'Outer tube',(138,130),(65,165))
    ann(a,'Obturator\n(removable)',(175,130),(175,195),BLU)
    ann(a,'Handle',(265,130),(295,165))
    a.text(175,25,'Proctoscope – 7 cm tube; views anal canal & lower rectum',
           ha='center',fontsize=7,color=DRK)
    save(f,29,'Proctoscope')

# 30  Bougies (Hegar's)
def d30():
    f,a=fig()
    ws=[4,6,8,10,12,14]; cs=['#e8d5a3','#d0d4d8',ST,ST,DST,'#4a5568']
    for i,(w,c) in enumerate(zip(ws,cs)):
        y=30+i*28
        a.add_patch(Rectangle((40,y),250,w+2,fc=c,ec=DRK,lw=1))
        a.add_patch(Ellipse((44,y+(w+2)/2),10,w+4,fc=c,ec=DRK,lw=1))
        a.add_patch(Ellipse((292,y+(w+2)/2),10,w+4,fc=c,ec=DRK,lw=1))
        a.text(308,y+(w+2)/2,f'#{5+i*2}',fontsize=6,color=DRK,va='center')
    a.text(170,205,'Hegar\'s Bougies – double-ended; cervical/urethral dilation',
           ha='center',fontsize=7,color=DRK)
    save(f,30,"Bougies (Hegar's)")

# 31  Allis Forceps Small
def d31():
    f,a=fig()
    rings(a,28,130,13)
    ratchet(a,72,130,14,20)
    shaft(a,86,133,170,7); shaft(a,86,122,170,7)
    for i in range(6):
        a.add_patch(Rectangle((256+i*5,121),3,10,fc=DST,ec=DRK,lw=0.7))
    for i in range(5):
        a.add_patch(Rectangle((258+i*5,133),3,8,fc=DST,ec=DRK,lw=0.7))
    ann(a,'Fine interlocking\nteeth (small jaws)',(270,129),(225,100))
    a.text(180,195,'Allis Forceps Small – narrow jaw, fine tissue grasping',
           ha='center',fontsize=7.5,color=DRK,fontweight='bold')
    save(f,31,'Allis Forceps Small')

# 32  Allis Forceps Big
def d32():
    f,a=fig()
    rings(a,28,130)
    ratchet(a,74,130)
    shaft(a,90,135,185,9); shaft(a,90,121,185,9)
    for i in range(7):
        a.add_patch(Rectangle((275+i*6,119),4,12,fc=DST,ec=DRK,lw=0.7))
    for i in range(6):
        a.add_patch(Rectangle((277+i*6,134),4,10,fc=DST,ec=DRK,lw=0.7))
    ann(a,'Broader jaw,\nmore teeth',(305,130),(260,100))
    a.text(180,195,'Allis Forceps Big – broader jaw for thicker tissue',
           ha='center',fontsize=7.5,color=DRK,fontweight='bold')
    save(f,32,'Allis Forceps Big')

# 33  Pile Holding Forceps
def d33():
    f,a=fig()
    rings(a,28,130)
    ratchet(a,74,130)
    shaft(a,90,134,185,8); shaft(a,90,122,185,8)
    a.add_patch(Rectangle((275,120),50,24,fc=DST,ec=DRK,lw=1.5))
    for i in range(8): a.plot([278+i*5,278+i*5],[122,142],c='w',lw=0.6,alpha=0.6)
    a.text(180,22,'Pile Holding Forceps – grasps haemorrhoidal tissue for excision',
           ha='center',fontsize=7,color=DRK)
    ann(a,'Flat serrated\njaws',(300,132),(250,100))
    save(f,33,'Pile Holding Forceps')

# 34  Artery Forceps Small
def d34():
    f,a=fig()
    rings(a,25,130,12)
    ratchet(a,64,130,12,18)
    shaft(a,76,130,170,11)
    a.add_patch(Rectangle((246,126),22,8,fc=DST,ec=DRK,lw=1))
    a.add_patch(Rectangle((246,134),18,8,fc=DST,ec=DRK,lw=1))
    serrate(a,248,130,16,8)
    ann(a,'Fully serrated\njaws (no tip teeth)',(258,130),(220,100))
    a.text(180,195,'Artery Forceps Small – clamps small vessels/capillaries',
           ha='center',fontsize=7.5,color=DRK,fontweight='bold')
    save(f,34,'Artery Forceps Small')

# 35  Artery Forceps Big
def d35():
    f,a=fig()
    rings(a,28,130)
    ratchet(a,74,130)
    shaft(a,90,134,195,9); shaft(a,90,122,195,9)
    a.add_patch(Rectangle((285,120),40,10,fc=DST,ec=DRK,lw=1))
    a.add_patch(Rectangle((285,134),36,10,fc=DST,ec=DRK,lw=1))
    serrate(a,287,130,20,10)
    ann(a,'Large serrated jaws\n(major vessels)',(300,130),(255,100))
    a.text(180,195,'Artery Forceps Big – clamps large vessels',
           ha='center',fontsize=7.5,color=DRK,fontweight='bold')
    save(f,35,'Artery Forceps Big')

# 36  Artery Forceps Medium
def d36():
    f,a=fig()
    rings(a,28,130,14)
    ratchet(a,72,130,13,20)
    shaft(a,85,133,190,8); shaft(a,85,121,190,8)
    a.add_patch(Rectangle((275,121),32,9,fc=DST,ec=DRK,lw=1))
    a.add_patch(Rectangle((275,133),28,9,fc=DST,ec=DRK,lw=1))
    serrate(a,278,130,18,9)
    ann(a,'Medium serrated\njaws',(292,130),(250,100))
    a.text(180,195,'Artery Forceps Medium – mid-calibre vessel haemostasis',
           ha='center',fontsize=7.5,color=DRK,fontweight='bold')
    save(f,36,'Artery Forceps Medium')

# 37  Sigmoidoscope
def d37():
    f,a=fig()
    a.add_patch(Rectangle((30,90),250,40,fc=DST,ec=DRK,lw=2))
    a.add_patch(Rectangle((30,97),250,20,fc=WH,ec='none',alpha=0.15))
    a.add_patch(Ellipse((30,110),40,44,fc=ST,ec=DRK,lw=2))
    a.add_patch(Ellipse((280,110),40,44,fc='#1a1a2e',ec=DRK,lw=2))
    a.add_patch(Rectangle((280,100),65,20,fc=DST,ec=DRK,lw=1.5))
    a.add_patch(FancyBboxPatch((285,62),42,32,boxstyle='round,pad=3',
                               fc=ORG,ec=DRK,lw=1.5))
    a.text(306,78,'Bellows',fontsize=5.5,color='w',ha='center')
    a.text(175,145,'~25 cm rigid tube',fontsize=6.5,color=DRK,ha='center')
    ann(a,'Obturator',(30,110),(50,155))
    ann(a,'Eyepiece',(310,110),(320,155))
    a.text(175,25,'Sigmoidoscope Rigid/Flexible – views sigmoid & rectum',
           ha='center',fontsize=7,color=DRK)
    save(f,37,'Sigmoidoscope')

# 38  Barron Pile's Gun
def d38():
    f,a=fig()
    a.add_patch(FancyBboxPatch((28,48),62,105,boxstyle='round,pad=5',fc=DST,ec=DRK,lw=2))
    a.add_patch(Rectangle((90,93),185,30,fc=ST,ec=DRK,lw=2))
    a.add_patch(Rectangle((90,98),185,14,fc='w',alpha=0.2,ec='none'))
    a.add_patch(Ellipse((288,108),42,42,fc=DST,ec=DRK,lw=2))
    a.add_patch(Ellipse((288,108),30,30,fc=WH,ec=DRK,lw=1))
    a.add_patch(FancyBboxPatch((63,68),26,52,boxstyle='round,pad=3',fc=RED,ec=DRK,lw=1.5))
    ann(a,'Rubber band drum',(288,108),(235,165))
    ann(a,'Trigger',(76,85),(100,50),RED)
    a.text(175,25,"Barron Pile's Gun – rubber band ligation of haemorrhoids",
           ha='center',fontsize=7,color=DRK)
    save(f,38,"Barron Pile's Gun")

# 39  Laryngoscope
def d39():
    f,a=fig()
    a.add_patch(Rectangle((28,58),30,145,fc=DST,ec=DRK,lw=2))
    for i in range(8): a.plot([28,58],[68+i*14,68+i*14],c='w',lw=0.7,alpha=0.5)
    t=np.linspace(0,0.68*np.pi,40)
    bx=58+145*np.sin(t); by=183-145*(1-np.cos(t))
    a.fill_between(bx,by,by+10,fc=ST,ec=DRK,lw=1.5)
    a.text(140,190,'Mac (curved)',fontsize=6.5,color=BLU)
    a.add_patch(Rectangle((58,68),215,12,fc=ST,ec=DRK,lw=1.5))
    a.text(140,57,'Miller (straight)',fontsize=6.5,color=ORG)
    a.add_patch(Circle((255,74),8,fc=YEL,ec=DRK,lw=1))
    ann(a,'Light at tip',(257,74),(285,105),YEL)
    a.text(175,28,'Laryngoscope – Mac (curved) & Miller (straight) blades',
           ha='center',fontsize=7,color=DRK)
    save(f,39,'Laryngoscope')

# 40  Ambu Bag
def d40():
    f,a=fig()
    a.add_patch(Ellipse((68,118),82,102,fc='#aed6f1',ec=DRK,lw=2,alpha=0.85))
    a.add_patch(Ellipse((68,118),62,80,fc=WH,ec=DRK,lw=1))
    a.add_patch(Rectangle((109,110),28,16,fc=DST,ec=DRK,lw=1.5))
    a.add_patch(Rectangle((137,112),26,12,fc=RED,ec=DRK,lw=1.5))
    a.add_patch(Ellipse((213,118),165,92,fc=GRN,ec=DRK,lw=2,alpha=0.85))
    a.add_patch(Ellipse((213,118),148,72,fc='#2ecc71',ec='none',alpha=0.35))
    a.add_patch(Rectangle((300,103),42,30,fc=BLU,ec=DRK,lw=1.5))
    a.text(321,118,'O2\nRes.',fontsize=5.5,color='w',ha='center')
    ann(a,'Mask',(68,118),(30,175))
    ann(a,'One-way valve',(150,112),(145,68),RED)
    ann(a,'Self-inflating bag',(213,118),(190,185),GRN)
    save(f,40,'Ambu Bag (BVM)')

# 41  Suction Machine
def d41():
    f,a=fig()
    a.add_patch(FancyBboxPatch((18,75),135,115,boxstyle='round,pad=5',fc=DST,ec=DRK,lw=2))
    a.add_patch(FancyBboxPatch((28,145),115,38,boxstyle='round,pad=3',fc='#1a1a2e',ec='none'))
    a.text(85,164,'Suction\nMachine',fontsize=7,color='w',ha='center')
    a.add_patch(Circle((53,108),14,fc=GRN,ec=DRK,lw=1.5))
    a.text(53,108,'ON',fontsize=6,color='w',ha='center',va='center')
    a.add_patch(FancyBboxPatch((28,82),60,28,boxstyle='round,pad=3',fc='#f5e6c8',ec=ORG,lw=1.5))
    a.text(58,96,'Canister',fontsize=5.5,color=DRK,ha='center')
    # Yankauer tip
    t=np.linspace(0.3*np.pi,0.9*np.pi,40)
    yx=225+80*np.cos(t); yy=128+80*np.sin(t)
    a.fill_between(yx,yy-9,yy+9,fc='#aed6f1',ec=DRK,lw=1.5)
    a.add_patch(Ellipse((232,77),24,24,fc='#aed6f1',ec=DRK,lw=1.5))
    a.text(270,50,'Yankauer tip\n(rigid/oral)',fontsize=6.5,color=DRK)
    save(f,41,'Suction Machine')

# 42  Emergency Light
def d42():
    f,a=fig()
    a.add_patch(FancyBboxPatch((100,80),160,110,boxstyle='round,pad=6',fc=DST,ec=DRK,lw=2))
    a.add_patch(Ellipse((180,135),80,70,fc=YEL,ec=ORG,lw=2,alpha=0.85))
    for ang in np.linspace(0,360,9)[:-1]:
        r=np.radians(ang)
        a.plot([180,180+65*np.cos(r)],[135,135+65*np.sin(r)],c=YEL,lw=1.2,alpha=0.55)
    a.add_patch(FancyBboxPatch((115,55),130,28,boxstyle='round,pad=3',fc='#2c3e50',ec=DRK,lw=1.5))
    a.text(180,69,'BATTERY PACK',fontsize=7,color='w',ha='center')
    ann(a,'Rechargeable battery\nbased emergency lamp',(180,135),(260,170))
    a.text(180,28,'Emergency Light – backup illumination during power failure',
           ha='center',fontsize=7,color=DRK)
    save(f,42,'Emergency Light')

# 43  Skin Grafting Knife
def d43():
    f,a=fig()
    a.add_patch(Rectangle((28,103),125,30,fc=DST,ec=DRK,lw=2))
    for i in range(7): a.plot([40+i*16,40+i*16],[105,131],c='w',lw=0.6,alpha=0.5)
    a.add_patch(Rectangle((153,110),10,8,fc=GLD,ec=DRK,lw=1.5))
    a.add_patch(Rectangle((163,115),175,16,fc=ST,ec=DRK,lw=1.5))
    a.add_patch(Rectangle((153,112),165,6,fc=GLD,ec=DRK,lw=1.5))
    pts=np.array([[338,115],[358,123],[338,131]])
    a.add_patch(plt.Polygon(pts,fc=DST,ec=DRK,lw=1))
    ann(a,'Long sharp blade',(325,123),(270,162),RED)
    ann(a,'Adjustable guard\n(sets graft thickness)',(220,112),(200,75),GLD)
    a.text(180,28,'Skin Grafting Knife (Humby) – split-thickness graft harvest',
           ha='center',fontsize=7,color=DRK)
    save(f,43,'Skin Grafting Knife')

# 44  Surgical Blades
def d44():
    f,a=fig()
    specs=[(10,'Large curved belly – general incision','#6b7a8d'),
           (11,'Pointed triangular – stab incision','#8d9db6'),
           (15,'Small curved – fine/facial surgery',ST),
           (22,'Large belly – abdominal surgery','#c8cdd2')]
    for i,(num,desc,c) in enumerate(specs):
        y=165-i*38
        if num in[10,22]:
            pts=np.array([[40,y+5],[155,y+5],[170,y],[155,y-5],[40,y-5]])
        elif num==11:
            pts=np.array([[40,y+5],[162,y+4],[178,y],[162,y-4],[40,y-5]])
        else:
            pts=np.array([[40,y+4],[132,y+4],[148,y],[132,y-4],[40,y-4]])
        a.add_patch(plt.Polygon(pts,fc=c,ec=DRK,lw=1.2))
        a.add_patch(Ellipse((60,y),12,8,fc='w',ec=DRK,lw=0.8))
        a.text(60,y,f'#{num}',fontsize=5,ha='center',va='center',color=DRK)
        a.text(185,y,desc,fontsize=6,va='center',color=DRK)
    a.text(175,25,'SINGLE USE · Pre-sterilized · Dispose in sharps container',
           ha='center',fontsize=6.5,color=RED)
    save(f,44,'Surgical Blades')

# 45  BP Handle
def d45():
    f,a=fig()
    data=[(3,'Blades 10,11,12,15',160,168),(4,'Blades 20,21,22,23',175,135),(7,'Long thin – fine work',200,102)]
    ys=[168,135,102]
    for (num,lbl,_,_),y in zip(data,ys):
        a.add_patch(Rectangle((30,y),y,12,fc=DST,ec=DRK,lw=1.5))
        for i in range(6): a.plot([40+i*20,40+i*20],[y+2,y+10],c='w',lw=0.6,alpha=0.5)
        a.add_patch(Rectangle((30+y,y+1),14,10,fc=ST,ec=DRK,lw=1.5))
        a.text(30+y+20,y+6,f'#{num}',fontsize=6.5,color=DRK,va='center')
        a.text(30+y+36,y+6,lbl,fontsize=6,color=DRK,va='center')
    a.text(180,55,'Remove blade with needle holder — NEVER by hand',
           ha='center',fontsize=6.5,color=RED)
    save(f,45,'BP Handle')

# 46  Self-Retaining Retractor
def d46():
    f,a=fig()
    a.add_patch(Circle((180,118),10,fc=GLD,ec=DRK,lw=1.5))
    for dx,dy in [(-100,65),(100,65),(-100,-65),(100,-65)]:
        a.plot([180,180+dx*0.9],[118,118+dy*0.9],c=ST,lw=6)
        tx,ty=180+dx*0.9,118+dy*0.9
        for i in range(3):
            a.add_patch(Circle((tx+dx*0.03*i,ty+dy*0.03*i),3.5,fc=DST,ec=DRK,lw=0.5))
    a.add_patch(Rectangle((132,113),96,10,fc=GLD,ec=DRK,lw=1))
    ann(a,'Ratchet bar\n(self-locking)',(180,118),(215,155))
    ann(a,'Toothed blades',(82,55),(42,45))
    a.text(180,22,'Self-Retaining Retractor (Weitlaner/Balfour) – hands-free',
           ha='center',fontsize=7,color=DRK)
    save(f,46,'Self-Retaining Retractor')

# 47  Bone Drill
def d47():
    f,a=fig()
    a.add_patch(FancyBboxPatch((118,78),162,82,boxstyle='round,pad=6',fc=DST,ec=DRK,lw=2))
    a.add_patch(FancyBboxPatch((133,88),132,58,boxstyle='round,pad=3',fc='#1a1a2e',ec='none'))
    a.text(199,117,'BONE DRILL',fontsize=8,color='w',ha='center',va='center',fontweight='bold')
    a.add_patch(FancyBboxPatch((143,38),55,42,boxstyle='round,pad=4',fc='#2c3e50',ec=DRK,lw=2))
    a.text(170,57,'Grip',fontsize=6.5,color='w',ha='center')
    a.add_patch(Rectangle((280,106),30,24,fc=GLD,ec=DRK,lw=1.5))
    a.text(295,118,'Chuck',fontsize=5.5,color=DRK,ha='center')
    a.add_patch(Rectangle((310,114),42,8,fc=ST,ec=DRK,lw=1))
    pts=np.array([[352,114],[352,122],[344,118]])
    a.add_patch(plt.Polygon(pts,fc=DST,ec=DRK,lw=1))
    a.add_patch(FancyBboxPatch((28,93),82,50,boxstyle='round,pad=4',fc=GRN,ec=DRK,lw=1.5))
    a.text(69,118,'Battery\nPack',fontsize=6.5,color='w',ha='center')
    save(f,47,'Bone Drill Machine')

# 48  Bone Cutter
def d48():
    f,a=fig()
    rings(a,30,130,20)
    a.add_patch(Ellipse((55,130),24,70,fc='none',ec=DRK,lw=2.5))
    a.add_patch(Rectangle((105,118),20,22,fc=DST,ec=DRK,lw=2))
    shaft(a,125,122,185,10,ST); shaft(a,125,132,185,10,ST)
    pts1=np.array([[310,122],[310,132],[338,127]])
    pts2=np.array([[310,132],[310,142],[335,137]])
    a.add_patch(plt.Polygon(pts1,fc=DST,ec=DRK,lw=1))
    a.add_patch(plt.Polygon(pts2,fc=DST,ec=DRK,lw=1))
    ann(a,'Heavy cutting jaws',(322,130),(270,98))
    a.text(180,22,'Bone Cutter (Liston/Rongeur) – cuts and shapes bone',
           ha='center',fontsize=7,color=DRK)
    save(f,48,'Bone Cutter')

# 49  Gigli Saw
def d49():
    f,a=fig()
    t=np.linspace(0,4*np.pi,300)
    wx=52+256*(t/(4*np.pi)); wy=120+6*np.sin(t*3)
    a.plot(wx,wy,c=DST,lw=2.5)
    a.add_patch(Rectangle((36,90),12,56,fc=ST,ec=DRK,lw=2))
    a.add_patch(Rectangle((21,140),40,12,fc=ST,ec=DRK,lw=2))
    a.add_patch(Rectangle((310,90),12,56,fc=ST,ec=DRK,lw=2))
    a.add_patch(Rectangle((295,88),40,12,fc=ST,ec=DRK,lw=2))
    ann(a,'Flexible wire saw\nwith cutting teeth',(180,120),(145,165))
    ann(a,'T-bar handle',(42,116),(45,70))
    a.text(180,28,'Gigli Saw – flexible wire saw for bone cutting in confined spaces',
           ha='center',fontsize=7,color=DRK)
    save(f,49,'Gigli Saw')

# 50  Scoop / Curette
def d50():
    f,a=fig()
    shaft(a,82,116),shaft(a,82,116,190,12,ST)
    a.add_patch(Ellipse((72,120),28,22,fc=DST,ec=DRK,lw=1.5))
    a.add_patch(Ellipse((72,120),18,14,fc=WH,ec=DRK,lw=0.8))
    a.add_patch(Ellipse((285,120),36,28,fc=DST,ec=DRK,lw=1.5))
    a.add_patch(Ellipse((285,120),24,18,fc=WH,ec=DRK,lw=0.8))
    a.text(42,150,'Small cup',fontsize=6.5,color=DRK,ha='center')
    a.text(285,150,'Large cup',fontsize=6.5,color=DRK,ha='center')
    a.text(180,195,'Scoop / Volkmann Curette – double-ended, scoops bone/tissue',
           ha='center',fontsize=7.5,color=DRK,fontweight='bold')
    save(f,50,'Scoop / Curette')

# 51  Periosteum Elevator
def d51():
    f,a=fig()
    a.add_patch(Rectangle((28,112),132,16,fc=DST,ec=DRK,lw=2))
    for i in range(7): a.plot([40+i*16,40+i*16],[114,126],c='w',lw=0.7,alpha=0.5)
    a.add_patch(Rectangle((160,107),155,26,fc=ST,ec=DRK,lw=1.5))
    a.add_patch(Rectangle((160,113),155,8,fc='w',alpha=0.3,ec='none'))
    pts=np.array([[315,107],[315,133],[340,120]])
    a.add_patch(plt.Polygon(pts,fc=DST,ec=DRK,lw=1))
    ann(a,'Flat wide blade\n(strips periosteum)',(235,120),(215,170))
    ann(a,'Blunt chisel tip',(330,120),(305,80))
    a.text(180,28,'Periosteum Elevator (Rugine/Langenbeck) – strips bone membrane',
           ha='center',fontsize=7,color=DRK)
    save(f,51,'Periosteum Elevator')

# 52  Magill (Magill/Maggler) Forceps
def d52():
    f,a=fig()
    rings(a,30,100)
    ratchet(a,76,100,15,20)
    a.plot([91,200,292],[100,100,182],c=ST,lw=7)
    a.add_patch(Ellipse((297,190),36,20,fc=ST,ec=DRK,lw=1.5))
    a.add_patch(Ellipse((297,173),30,16,fc=ST,ec=DRK,lw=1.5))
    ann(a,'Offset angled jaws\n(KEY – airway view)',(292,182),(215,55),RED)
    a.text(180,28,"Magill Forceps – guides ETT through cords; removes airway FBs",
           ha='center',fontsize=7,color=DRK)
    save(f,52,'Magill Forceps')

# 53  Endotracheal Tubes
def d53():
    f,a=fig()
    a.add_patch(Rectangle((28,103),242,22,fc='#aed6f1',ec=BLU,lw=2))
    a.add_patch(Rectangle((28,110),242,6,fc='w',alpha=0.3,ec='none'))
    a.add_patch(Ellipse((290,114),56,44,fc='#5dade2',ec=BLU,lw=1.5,alpha=0.7))
    a.plot([290,290],[94,60],c=BLU,lw=1.5)
    a.add_patch(Ellipse((290,54),18,14,fc='#aed6f1',ec=BLU,lw=1.5))
    a.text(290,40,'Pilot balloon',fontsize=5.5,color=DRK,ha='center')
    a.add_patch(FancyBboxPatch((12,99),18,30,boxstyle='round,pad=2',fc=DST,ec=DRK,lw=1.5))
    a.text(21,114,'15mm',fontsize=5,color='w',ha='center')
    a.add_patch(Ellipse((320,114),8,12,fc='w',ec=BLU,lw=1))
    ann(a,'15 mm connector',(21,99),(30,72))
    ann(a,'Inflatable cuff',(290,114),(222,162),BLU)
    ann(a,"Murphy's eye",(322,114),(313,162))
    save(f,53,'Endotracheal Tubes')

# 54  Autoclave
def d54():
    f,a=fig()
    a.add_patch(FancyBboxPatch((38,48),245,152,boxstyle='round,pad=8',fc='#d0d4d8',ec=DRK,lw=2))
    a.add_patch(FancyBboxPatch((52,62),105,122,boxstyle='round,pad=5',fc=ST,ec=DRK,lw=2))
    a.add_patch(Rectangle((58,118),30,10,fc=GLD,ec=DRK,lw=1.5))
    a.text(73,123,'DOOR',fontsize=7,color=DRK,ha='center',va='center')
    a.add_patch(FancyBboxPatch((175,78),90,122,boxstyle='round,pad=4',fc='#1a1a2e',ec=DRK,lw=1.5))
    a.add_patch(Circle((215,168),22,fc='w',ec=DRK,lw=1.5))
    a.text(215,168,'PSI',fontsize=6,ha='center',va='center',color=DRK)
    a.add_patch(FancyBboxPatch((183,128),50,22,boxstyle='round,pad=2',fc=GRN,ec='none'))
    a.text(208,139,'134°C',fontsize=7,color='w',ha='center')
    a.add_patch(Circle((208,98),10,fc=RED,ec=DRK,lw=1))
    a.text(208,98,'ON',fontsize=5,color='w',ha='center',va='center')
    a.text(160,28,'High Pressure Autoclave – primary OT sterilization device',
           ha='center',fontsize=7,color=DRK)
    save(f,54,'High Pressure Autoclave')

# 55  Fumigator
def d55():
    f,a=fig()
    a.add_patch(FancyBboxPatch((60,60),240,130,boxstyle='round,pad=7',fc='#2c3e50',ec=DRK,lw=2))
    a.add_patch(FancyBboxPatch((75,75),210,100,boxstyle='round,pad=4',fc='#1a252f',ec='none'))
    a.text(180,125,'FUMIGATOR',fontsize=11,color='w',ha='center',va='center',fontweight='bold')
    a.text(180,108,'Formaldehyde / H2O2',fontsize=7,color='#aed6f1',ha='center')
    # Steam/fog particles
    for xi,yi in [(110,200),(155,210),(200,205),(245,200),(290,210)]:
        a.add_patch(Ellipse((xi,yi),20,12,fc='#aed6f1',ec='none',alpha=0.6))
    # Vent holes
    for x in range(90,260,35): a.add_patch(Circle((x,62),5,fc='#555',ec='none'))
    ann(a,'Aerosol nozzle\n(room fumigation)',(180,200),(250,210),BLU)
    a.text(180,28,'Fumigator – terminal OT room disinfection (formaldehyde/H₂O₂)',
           ha='center',fontsize=7,color=DRK)
    save(f,55,'Fumigator')

# 56  Refrigerator
def d56():
    f,a=fig()
    a.add_patch(FancyBboxPatch((60,28),240,180,boxstyle='round,pad=5',fc='#d0d4d8',ec=DRK,lw=2))
    # Door
    a.add_patch(FancyBboxPatch((72,40),215,160,boxstyle='round,pad=4',fc=ST,ec=DRK,lw=1.5))
    # Handle
    a.add_patch(Rectangle((270,105),12,35,fc=DST,ec=DRK,lw=2,joinstyle='round'))
    # Temp display
    a.add_patch(FancyBboxPatch((82,155),100,30,boxstyle='round,pad=3',fc='#1a1a2e',ec='none'))
    a.text(132,170,'2–8 °C',fontsize=8,color=GRN,ha='center',va='center')
    # Shelves
    for y in [50,90,130]:
        a.plot([82,278],[y,y],c=DST,lw=1,alpha=0.5)
    ann(a,'Temperature 2–8°C\n(vaccine/drug storage)',(132,170),(210,185),GRN)
    a.text(180,15,'Refrigerator – stores vaccines, blood, medications',
           ha='center',fontsize=7,color=DRK)
    save(f,56,'Refrigerator')

# 57  Nitrous Oxide Cylinder
def d57():
    f,a=fig()
    a.add_patch(FancyBboxPatch((125,38),110,165,boxstyle='round,pad=10',fc=BLU,ec=DRK,lw=2))
    a.add_patch(FancyBboxPatch((150,38),60,165,boxstyle='round,pad=5',fc='#3498db',ec='none',alpha=0.4))
    a.text(180,148,'N₂O',fontsize=24,color='w',ha='center',fontweight='bold')
    a.text(180,125,'Nitrous\nOxide',fontsize=9,color='#aed6f1',ha='center')
    # Valve on top
    a.add_patch(Rectangle((158,203),44,14,fc=DST,ec=DRK,lw=1.5))
    a.add_patch(Rectangle((168,217),24,14,fc=DST,ec=DRK,lw=1.5))
    # Pressure gauge
    a.add_patch(Circle((280,150),28,fc='w',ec=DRK,lw=1.5))
    a.text(280,150,'PSI',fontsize=7,ha='center',va='center',color=DRK)
    a.plot([180,252],[203,165],c=DST,lw=2)
    ann(a,'Valve/regulator',(180,210),(120,215))
    a.text(180,20,'N₂O Cylinder – anaesthetic gas (blue coded)',
           ha='center',fontsize=7,color=DRK)
    save(f,57,'Nitrous Oxide Cylinder')

# 58  Hydraulic OT Table
def d58():
    f,a=fig()
    # Table top
    a.add_patch(FancyBboxPatch((30,150),300,30,boxstyle='round,pad=4',fc=ST,ec=DRK,lw=2))
    # Table sections
    for x in [130,210]: a.plot([x,x],[150,180],c=DST,lw=1,ls='--',alpha=0.5)
    # Central column
    a.add_patch(Rectangle((155,60),50,90,fc=DST,ec=DRK,lw=2))
    # Hydraulic cylinder
    a.add_patch(Rectangle((168,40),24,25,fc='#555',ec=DRK,lw=1.5))
    # Base
    a.add_patch(Rectangle((80,28),200,16,fc=DRK,ec=DRK,lw=1.5))
    for x in [100,170,245]: a.add_patch(Circle((x,22),7,fc=DRK,ec=DRK))
    # Side rail
    a.add_patch(Rectangle((325,155),18,22,fc=GLD,ec=DRK,lw=1.5))
    ann(a,'Table top\n(multi-section, adjustable)',(180,165),(240,195))
    ann(a,'Hydraulic\ncolumn (height)',(180,90),(240,105),BLU)
    a.text(180,10,'Hydraulic OT Table – adjustable height, tilt, and section angles',
           ha='center',fontsize=7,color=DRK)
    save(f,58,'Hydraulic OT Table')

# 59  Anaesthesia Trolley / Boyle's Apparatus
def d59():
    f,a=fig()
    # Cabinet
    a.add_patch(FancyBboxPatch((25,45),220,165,boxstyle='round,pad=5',fc=DST,ec=DRK,lw=2))
    # Drawers
    for y in [55,100,145]:
        a.add_patch(FancyBboxPatch((35,y),200,38,boxstyle='round,pad=3',fc=ST,ec=DRK,lw=1))
        a.add_patch(Circle((135,y+19),5,fc=GLD,ec=DRK,lw=1))
    a.text(135,75,'Drugs & equipment',fontsize=6,color=DRK,ha='center')
    a.text(135,119,'Airway management',fontsize=6,color=DRK,ha='center')
    a.text(135,162,'Monitoring supplies',fontsize=6,color=DRK,ha='center')
    # Gas cylinders
    a.add_patch(FancyBboxPatch((258,80),45,120,boxstyle='round,pad=5',fc=BLU,ec=DRK,lw=2))
    a.add_patch(FancyBboxPatch((310,80),45,120,boxstyle='round,pad=5',fc=GRN,ec=DRK,lw=2))
    a.text(280,140,'N₂O',fontsize=9,color='w',ha='center',fontweight='bold')
    a.text(332,140,'O₂',fontsize=9,color='w',ha='center',fontweight='bold')
    # Wheels
    for x in [50,200]: a.add_patch(Circle((x,40),12,fc=DRK,ec=DRK))
    ann(a,'O₂ & N₂O cylinders',(300,140),(300,195),BLU)
    a.text(180,22,"Anaesthesia Trolley / Boyle's Apparatus – anaesthetic delivery",
           ha='center',fontsize=7,color=DRK)
    save(f,59,"Anaesthesia Trolley / Boyle's")

# 60  Gabriel Syringe
def d60():
    f,a=fig()
    a.add_patch(Rectangle((78,98),165,30,fc='#d0d4d8',ec=DRK,lw=2))
    a.add_patch(Rectangle((78,105),165,10,fc='w',alpha=0.3,ec='none'))
    a.add_patch(Rectangle((53,106),25,14,fc=DST,ec=DRK,lw=1.5))
    a.plot([28,53],[113,113],c=DST,lw=3)
    a.add_patch(Circle((26,113),10,fc=DST,ec=DRK,lw=1.5))
    t=np.linspace(0.5*np.pi,np.pi,30)
    nx=243+35*np.cos(t); ny=113+35*np.sin(t)
    a.fill_between(nx,ny-5,ny+5,fc=ST,ec=DRK,lw=1.2)
    a.add_patch(Ellipse((208,148),16,12,fc=GLD,ec=DRK,lw=1.5))
    ann(a,'Curved nozzle &\nolive tip (KEY FEATURE)',(230,148),(245,182),PRP)
    a.text(155,72,'Gabriel Syringe – sclerosant injection for haemorrhoids',
           ha='center',fontsize=7,color=DRK)
    save(f,60,'Gabriel Syringe')

# 61  Stretcher with Trolley
def d61():
    f,a=fig()
    # Stretcher top
    a.add_patch(FancyBboxPatch((25,120),310,45,boxstyle='round,pad=5',fc=ST,ec=DRK,lw=2))
    a.add_patch(Rectangle((25,140),310,12,fc='w',alpha=0.2,ec='none'))
    # Side rails
    a.add_patch(Rectangle((20,125),8,35,fc=DST,ec=DRK,lw=1.5))
    a.add_patch(Rectangle((332,125),8,35,fc=DST,ec=DRK,lw=1.5))
    # Legs
    for x in [55,155,205,305]:
        a.plot([x,x],[120,65],c=DST,lw=4)
    # Wheels
    for x in [55,155,205,305]:
        a.add_patch(Circle((x,57),10,fc=DRK,ec=DRK))
    # Mattress
    a.add_patch(FancyBboxPatch((30,128),300,30,boxstyle='round,pad=3',
                               fc='#aed6f1',ec=BLU,lw=1,alpha=0.7))
    ann(a,'Side rails\n(foldable)',(24,142),(45,195))
    ann(a,'Castor wheels\n(lockable)',(130,57),(120,28))
    save(f,61,'Stretcher with Trolley')

# 62  Non-Mercury Sphygmomanometer
def d62():
    f,a=fig()
    # Display unit
    a.add_patch(FancyBboxPatch((100,90),160,100,boxstyle='round,pad=6',fc=DST,ec=DRK,lw=2))
    a.add_patch(FancyBboxPatch((115,105),130,70,boxstyle='round,pad=3',fc='#1a1a2e',ec='none'))
    a.text(180,148,'120/80',fontsize=14,color=GRN,ha='center',va='center',fontweight='bold')
    a.text(180,130,'mmHg',fontsize=7,color='#aed6f1',ha='center')
    # Cuff
    t=np.linspace(0,np.pi,40)
    cx=180+120*np.cos(t); cy=68+35*np.sin(t)
    a.fill_between(cx,cy-10,cy,fc=BLU,ec=DRK,lw=1.5,alpha=0.75)
    a.plot([60,60],[30,90],c=DRK,lw=1.5)
    a.plot([300,300],[30,90],c=DRK,lw=1.5)
    # Bulb
    a.add_patch(Ellipse((60,25),30,22,fc=RED,ec=DRK,lw=1.5))
    ann(a,'Digital display\n(non-mercury)',( 180,140),(255,185),GRN)
    ann(a,'Inflatable cuff',(180,65),(215,40))
    a.text(180,10,'Non-Mercury Sphygmomanometer – BP measurement',
           ha='center',fontsize=7,color=DRK)
    save(f,62,'Non-Mercury Sphygmomanometer')

# 63  Doyen's Retractor
def d63():
    f,a=fig()
    a.add_patch(Rectangle((28,111),122,18,fc=DST,ec=DRK,lw=2))
    for i in range(6): a.plot([40+i*17,40+i*17],[113,127],c='w',lw=0.7,alpha=0.5)
    t=np.linspace(-0.25*np.pi,0.25*np.pi,40)
    bx=150+145*np.cos(t); by=120+145*np.sin(t)
    a.fill_between(bx,by-14,by,fc=ST,ec=DRK,lw=1.5)
    a.fill_between(bx,by-5,by,fc='w',alpha=0.25,ec='none')
    a.add_patch(Rectangle((150,120),12,30,fc=ST,ec=DRK,lw=1))
    ann(a,'Wide curved blade\n(retracts bladder\nduring LSCS)',(240,130),(220,185))
    a.text(180,28,"Doyen's Retractor – LSCS bladder retraction; pelvic surgery",
           ha='center',fontsize=7,color=DRK)
    save(f,63,"Doyen's Retractor")

# 64  Green-Armytage Forceps
def d64():
    f,a=fig()
    rings(a,28,130)
    ratchet(a,74,130)
    shaft(a,90,134,180,8); shaft(a,90,122,180,8)
    a.add_patch(Rectangle((270,123),48,12,fc=DST,ec=DRK,lw=1.5))
    a.add_patch(Rectangle((270,141),48,12,fc=DST,ec=DRK,lw=1.5))
    for i in range(7):
        a.plot([274+i*5,274+i*5],[125,133],c='w',lw=0.7,alpha=0.7)
        a.plot([274+i*5,274+i*5],[143,151],c='w',lw=0.7,alpha=0.7)
    ann(a,'Broad serrated jaws\n(uterine haemostasis)',(294,138),(230,185),RED)
    a.text(180,22,'Green-Armytage Forceps – clamps uterine incision edges (LSCS)',
           ha='center',fontsize=7,color=DRK)
    save(f,64,'Green-Armytage Forceps')

# 65  Sterilizer (Drum/Hot Air Oven)
def d65():
    f,a=fig()
    a.add_patch(FancyBboxPatch((35,45),290,155,boxstyle='round,pad=7',fc='#c8cdd2',ec=DRK,lw=2))
    a.add_patch(FancyBboxPatch((50,58),180,128,boxstyle='round,pad=5',fc=ST,ec=DRK,lw=2))
    a.add_patch(Rectangle((56,64),12,10,fc=GLD,ec=DRK,lw=1.5))
    a.add_patch(Rectangle((56,115),12,10,fc=GLD,ec=DRK,lw=1.5))
    a.text(140,122,'STERILIZER',fontsize=10,color=DRK,ha='center',fontweight='bold')
    a.text(140,104,'Hot Air / Steam',fontsize=7,color=DRK,ha='center')
    a.add_patch(FancyBboxPatch((248,78),62,102,boxstyle='round,pad=4',fc='#1a1a2e',ec=DRK,lw=1.5))
    a.add_patch(FancyBboxPatch((258,120),42,22,boxstyle='round,pad=2',fc=GRN,ec='none'))
    a.text(279,131,'160°C',fontsize=7,color='w',ha='center')
    a.add_patch(Circle((279,100),10,fc=RED,ec=DRK,lw=1))
    a.text(279,100,'ON',fontsize=5,color='w',ha='center',va='center')
    a.text(180,25,'Sterilizer – dry heat (160°C/60 min) or steam autoclave',
           ha='center',fontsize=7,color=DRK)
    save(f,65,'Sterilizer')

# 66  Sim's Speculum
def d66():
    f,a=fig()
    t=np.linspace(0,np.pi,40)
    bx=175+100*np.cos(t); by=155+52*np.sin(t)
    a.fill_between(bx,by-10,by,fc=ST,ec=DRK,lw=1.5)
    t2=np.linspace(0,np.pi,40)
    bx2=175+80*np.cos(t2); by2=82+42*np.sin(t2)
    a.fill_between(bx2,by2,by2+10,fc=DST,ec=DRK,lw=1.5)
    a.add_patch(Rectangle((133,78),84,22,fc=DST,ec=DRK,lw=1.5))
    ann(a,'Upper (larger) blade',(200,158),(240,188))
    ann(a,'Lower (smaller) blade',(200,92),(240,60))
    a.text(175,32,"Sim's Speculum – used lateral; visualises vagina/cervix",
           ha='center',fontsize=7,color=DRK)
    save(f,66,"Sim's Speculum")

# 67  Anterior Vaginal Wall Retractor
def d67():
    f,a=fig()
    a.add_patch(Rectangle((28,111),122,18,fc=DST,ec=DRK,lw=2))
    for i in range(5): a.plot([40+i*20,40+i*20],[113,127],c='w',lw=0.7,alpha=0.5)
    a.add_patch(Rectangle((150,90),165,60,fc=ST,ec=DRK,lw=1.5))
    a.add_patch(Rectangle((150,98),165,20,fc='w',alpha=0.25,ec='none'))
    a.add_patch(FancyBboxPatch((313,100),32,40,boxstyle='round,pad=3',fc=ST,ec=DRK,lw=1.5))
    ann(a,'Flat rectangular\nblade',(230,120),(215,175))
    a.text(180,28,'Ant. Vaginal Wall Retractor – retracts anterior wall for posterior procedures',
           ha='center',fontsize=7,color=DRK)
    save(f,67,'Anterior Vaginal Wall Retractor')

# 68  Cusco's Speculum
def d68():
    f,a=fig()
    a.add_patch(Rectangle((118,78),112,14,fc=ST,ec=DRK,lw=1.5))
    a.add_patch(Rectangle((118,96),112,14,fc=ST,ec=DRK,lw=1.5))
    pts_t=np.array([[230,92],[318,152],[318,140],[230,92]])
    pts_b=np.array([[230,96],[318,46],[318,58],[230,96]])
    a.add_patch(plt.Polygon(pts_t,fc=ST,ec=DRK,lw=1.5))
    a.add_patch(plt.Polygon(pts_b,fc=ST,ec=DRK,lw=1.5))
    a.add_patch(Circle((174,93),13,fc=GLD,ec=DRK,lw=1.5))
    a.text(174,93,'⟳',fontsize=10,ha='center',va='center',color=DRK)
    a.add_patch(Rectangle((58,82),60,22,fc=DST,ec=DRK,lw=1.5))
    ann(a,'Thumb-screw\n(self-retaining)',( 174,93),(120,162),GLD)
    ann(a,'Bivalve blades\n(exposes cervix)',(300,99),(280,175))
    a.text(175,28,"Cusco's Speculum – bivalve self-retaining; Pap smear/IUD",
           ha='center',fontsize=7,color=DRK)
    save(f,68,"Cusco's Speculum")

# 69  Uterine Sound
def d69():
    f,a=fig()
    a.add_patch(Rectangle((28,115),295,8,fc=ST,ec=DRK,lw=1.5))
    a.add_patch(Rectangle((28,117),295,3,fc='w',alpha=0.35,ec='none'))
    t=np.linspace(0,0.3*np.pi,20)
    cx=293+25*np.cos(t+np.pi); cy=119+25*np.sin(t+np.pi)
    a.plot(cx,cy,c=ST,lw=6)
    a.add_patch(Circle((316,105),7,fc=GLD,ec=DRK,lw=1.5))
    for i in range(10):
        x=55+i*24; h=10 if i%5==0 else 6
        a.plot([x,x],[107,107+h],c=DRK,lw=1)
        if i%5==0: a.text(x,103,f'{i+1}',fontsize=5,ha='center',color=DRK)
    a.text(175,148,'Graduation marks (cm) – measures uterine cavity depth',
           fontsize=6.5,color=DRK,ha='center')
    ann(a,'Knob tip',(316,105),(295,75),GLD)
    a.text(175,28,'Uterine Sound – graduated probe; measures uterine depth before IUD/D&C',
           ha='center',fontsize=7,color=DRK)
    save(f,69,'Uterine Sound')

# 70  Anterior Vaginal Wall Retractor (repeat – same instrument listed twice)
def d70():
    f,a=fig()
    a.add_patch(Rectangle((28,111),122,18,fc=DST,ec=DRK,lw=2))
    for i in range(5): a.plot([40+i*20,40+i*20],[113,127],c='w',lw=0.7,alpha=0.5)
    a.add_patch(Rectangle((150,90),165,60,fc=ST,ec=DRK,lw=1.5))
    a.add_patch(Rectangle((150,98),165,20,fc='w',alpha=0.25,ec='none'))
    a.add_patch(FancyBboxPatch((313,100),32,40,boxstyle='round,pad=3',fc=ST,ec=DRK,lw=1.5))
    ann(a,'Flat rectangular\nblade',(230,120),(215,175))
    a.text(180,65,'(Same as #67 – listed twice in MSR)',
           ha='center',fontsize=7,color=RED,fontstyle='italic')
    a.text(180,28,'Anterior Vaginal Wall Retractor – retraction during colporrhaphy/prolapse repair',
           ha='center',fontsize=7,color=DRK)
    save(f,70,'Ant. Vaginal Wall Retractor (2)')

print("Generating diagrams...")
fns=[d01,d02,d03,d04,d05,d06,d07,d08,d09,d10,
     d11,d12,d13,d14,d15,d16,d17,d18,d19,d20,
     d21,d22,d23,d24,d25,d26,d27,d28,d29,d30,
     d31,d32,d33,d34,d35,d36,d37,d38,d39,d40,
     d41,d42,d43,d44,d45,d46,d47,d48,d49,d50,
     d51,d52,d53,d54,d55,d56,d57,d58,d59,d60,
     d61,d62,d63,d64,d65,d66,d67,d68,d69,d70]
for fn in fns:
    try: fn()
    except Exception as e: print(f"  ERR {fn.__name__}: {e}")
print(f"Done – {len(fns)} diagrams.")
Running Command

cd /home/daytona/workspace/shalya-instruments && python3 draw_all.py 2>&1

Running Command

cd /home/daytona/workspace/shalya-instruments && python3 -c " import matplotlib;…

This is a shared conversation. Sign in to Orris to start your own chat.