Give answer shortly

Reading File
Running Command

ls -la "/tmp/workspace/attachments/8b0b6d50-b3c6-4b1f-99be-e27a04e9fd79/" && file "/tmp/workspace/attachments/8b0b6d50-b3c6-4b1f-99be-e27a04e9fd79/DOC-20260715-WA0000."

Reading File
Running Command

python3 -c " import pdfplumber with pdfplumber.open('/tmp/workspace/attachments/8b0b6d50-b3c6-4b1f-99be-e27a04e9fd79/DOC-20260715-WA0000.') as pdf: for i, page in enumerate(pdf.pages): text = page.extract_text() print(f'--- Page {i+1} ---') print(text) "

Reading File
Finding Sources
Finding Sources
Reading File
Finding Sources
Reading File
Finding Sources
Reading File
I now have enough information from the textbooks to answer all 3 questions concisely. Here are the answers:

Assignment – Biomechanics of the Shoulder Complex


Q1. Biomechanics of Shoulder Abduction 0° to 180° (10 marks)

Phases of Shoulder Abduction

PhaseRangeKey Events
Phase 10-30°Pure glenohumeral (GH) motion; scapula "seeks a position of stability" with minimal rotation
Phase 230-90°GH + scapulothoracic (ST) motion begins; 2:1 rhythm established; supraspinatus initiates, deltoid takes over
Phase 390-180°Increasing ST contribution; external rotation of humerus required to clear the greater tuberosity under the acromion

Glenohumeral vs. Scapulothoracic Contribution

  • Glenohumeral joint contributes 120° of the total 180° arc (2/3).
  • Scapulothoracic joint contributes 60° (1/3) via upward rotation of the scapula.
  • As elevation increases above 90°, the proportion of scapulothoracic motion relative to GH motion increases. (Rheumatology 2022, p.700)

Scapulohumeral Rhythm - 2:1 Ratio

Scapulohumeral rhythm is the coordinated, synchronous movement between the glenohumeral and scapulothoracic joints during arm elevation. For every 3° of total shoulder abduction, 2° occurs at the GH joint and 1° occurs at the ST joint - giving a 2:1 GH:ST ratio. This rhythm optimizes the length-tension relationship of the deltoid and maintains the glenoid as a stable base under the humeral head throughout motion. Disturbance of this rhythm can result in secondary subacromial impingement. (Rheumatology 2022, p.700)

SC and AC Joint Movements During Abduction

  • Sternoclavicular (SC) joint: The clavicle elevates (up to 30-35°) and posteriorly rotates (up to 40°) during full abduction. This is the primary site of clavicular rotation.
  • Acromioclavicular (AC) joint: The clavicle rotates 30-40° through the AC joint during abduction and elevation. Stabilized by the conoid and trapezoid (coracoclavicular) ligaments, which maintain the scapula-clavicle relationship. (Rheumatology 2022, p.700)

Role of the Rotator Cuff in Shoulder Stability During Abduction

The rotator cuff (supraspinatus, infraspinatus, teres minor, subscapularis) maintains stability via three mechanisms:
  1. Joint compression - "Concavity-compression": compresses the humeral head into the glenoid, stabilizing against translating forces.
  2. Centering the humeral head - Coordinated contraction guides the head onto the center of the glenoid throughout abduction.
  3. Dynamization of glenohumeral ligaments - Cuff attachments tension the capsule at extremes of motion.
The cuff acts as "guy ropes" around the humeral head, counteracting the superior shear force of the deltoid. A 50% reduction in rotator cuff forces increases anterior displacement by 46% and posterior displacement by 31%. (Rockwood & Green's 2025, p.1272)

Diagram: Scapulohumeral Rhythm

      180° total abduction
      /        \
  120° GH     60° ST
  (2 parts)  (1 part)
  
  Ratio = 2:1 (GH:ST)
  
  For every 3° abduction:
  → 2° at GH joint
  → 1° at scapulothoracic joint

Q2. Structure and Biomechanics of the Sternoclavicular Joint (5 marks)

FeatureDetail
ClassificationDiarthrodial saddle joint (synovial); contains an intra-articular fibrocartilaginous disk
Articular surfacesMedial end of clavicle + clavicular notch of manubrium sterni + 1st costal cartilage
LigamentsAnterior SC ligament (resists superior displacement), Posterior SC ligament (primary AP restraint), Costoclavicular ligament (limits elevation/depression), Interclavicular ligament
Available movementsElevation/depression (~30-35°); Protraction/retraction (~15-30°); Axial rotation (~40-50°, mostly during full overhead elevation)
Functional importanceOnly bony link between the upper limb and the axial skeleton. All scapular and arm movements are transmitted through it. Clavicular rotation at this joint is the primary mechanism enabling full 180° arm elevation. Without SC joint motion, full abduction is impossible.
(Rheumatology 2022, p.700)

Q3. Force Couple Mechanism in Shoulder Abduction (5 marks)

Definition of Force Couple

A force couple is a pair of equal and opposite forces acting at different points on a body, producing pure rotation without linear translation. In the shoulder, this means the humeral head rotates cleanly in the glenoid without migrating superiorly.

Muscles Involved

Coronal plane (GH joint) force couple:
  • Deltoid - generates a large superiorly directed force (upward shear)
  • Rotator cuff (infraspinatus, teres minor, subscapularis) - generate a depressive/compressive force, counteracting the deltoid's shear
Scapular plane force couple (for scapular upward rotation):
  • Upper trapezius - pulls the clavicle up/back, rotating the scapula
  • Lower trapezius + Serratus anterior - pull the inferior angle of the scapula laterally and anteriorly

Mechanism During Shoulder Abduction

When the deltoid contracts to abduct the arm, it creates an upward shear force that would displace the humeral head superiorly into the acromion. The rotator cuff muscles (particularly infraspinatus, teres minor, subscapularis acting below the center of rotation) simultaneously contract to generate a downward and inward compressive force, converting the deltoid's vertical vector into pure rotation at the glenohumeral joint. The two opposing forces create a force couple - the humeral head rotates in the glenoid without translation. (Rheumatology 2022, p.700)

Importance in Normal Shoulder Biomechanics

  • Maintains the humeral head centered on the glenoid throughout abduction
  • Preserves subacromial space, preventing impingement
  • Maximizes the mechanical efficiency of abduction
  • Allows the deltoid to work effectively across the full range of motion

Clinical Significance if Force Couple is Disrupted

  • Rotator cuff tear → loss of inferior force → superior migration of humeral head → subacromial impingement; progressive cuff tear arthropathy
  • Painful arc syndrome (60-120°) due to pinching of supraspinatus tendon under acromion
  • Shoulder instability - inability to center the humeral head leads to subluxation/dislocation
  • Trapezius/serratus anterior palsy → loss of scapular upward rotation couple → scapular winging → reduced overhead reach

Create a detailed PDF reference guide with diagrams for these 3 questions

Reading File
Running Command

mkdir -p /tmp/workspace/shoulder-biomechanics && ls /tmp/workspace/shoulder-biomechanics/

Writing File

~/shoulder-biomechanics/create_pdf.py

from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import mm, cm
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
    HRFlowable, PageBreak, KeepTogether
)
from reportlab.graphics.shapes import (
    Drawing, Circle, Rect, Line, String, Arrow, Polygon,
    Ellipse, Path, Group
)
from reportlab.graphics import renderPDF
from reportlab.graphics.shapes import ArcPath
from reportlab.platypus.flowables import Flowable
import math

# ─────────────────────────── colours ───────────────────────────
DARK_BLUE   = colors.HexColor("#1A3A5C")
MID_BLUE    = colors.HexColor("#2E6DA4")
LIGHT_BLUE  = colors.HexColor("#D6E8F7")
ACCENT      = colors.HexColor("#E8760A")
GREEN       = colors.HexColor("#2E7D32")
LIGHT_GREEN = colors.HexColor("#E8F5E9")
RED         = colors.HexColor("#C62828")
LIGHT_RED   = colors.HexColor("#FFEBEE")
YELLOW_BG   = colors.HexColor("#FFFDE7")
PURPLE      = colors.HexColor("#6A1B9A")
LIGHT_PURPLE= colors.HexColor("#F3E5F5")
GREY_BG     = colors.HexColor("#F5F5F5")
DARK_GREY   = colors.HexColor("#424242")

W, H = A4  # 595 x 842 pts

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

def make_style(name, parent="Normal", **kw):
    return ParagraphStyle(name, parent=styles[parent], **kw)

title_style = make_style("DocTitle", "Title",
    fontSize=22, textColor=DARK_BLUE, spaceAfter=6,
    fontName="Helvetica-Bold", alignment=TA_CENTER)

subtitle_style = make_style("DocSubtitle", "Normal",
    fontSize=12, textColor=MID_BLUE, spaceAfter=4,
    fontName="Helvetica", alignment=TA_CENTER)

q_header_style = make_style("QHeader",
    fontSize=14, textColor=colors.white, fontName="Helvetica-Bold",
    spaceAfter=4, spaceBefore=16, leftIndent=6)

h2_style = make_style("H2",
    fontSize=12, textColor=DARK_BLUE, fontName="Helvetica-Bold",
    spaceBefore=10, spaceAfter=4, leftIndent=0)

h3_style = make_style("H3",
    fontSize=10.5, textColor=MID_BLUE, fontName="Helvetica-Bold",
    spaceBefore=7, spaceAfter=3)

body_style = make_style("Body",
    fontSize=9.5, leading=14, spaceAfter=4,
    fontName="Helvetica", alignment=TA_JUSTIFY)

bullet_style = make_style("Bullet",
    fontSize=9.5, leading=13, spaceAfter=2,
    fontName="Helvetica", leftIndent=14, firstLineIndent=-10)

caption_style = make_style("Caption",
    fontSize=8.5, textColor=DARK_GREY, fontName="Helvetica-Oblique",
    alignment=TA_CENTER, spaceAfter=6)

box_style = make_style("Box",
    fontSize=9, leading=13, fontName="Helvetica",
    leftIndent=8, rightIndent=8, spaceAfter=2)

# ──────────────────── helper flowables ────────────────────────
def q_banner(text, color=DARK_BLUE):
    """A coloured question banner paragraph."""
    data = [[Paragraph(f'<font color="white"><b>{text}</b></font>', q_header_style)]]
    t = Table(data, colWidths=[W - 72])
    t.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,-1), color),
        ('ROUNDEDCORNERS', [4]),
        ('TOPPADDING',  (0,0), (-1,-1), 6),
        ('BOTTOMPADDING', (0,0), (-1,-1), 6),
        ('LEFTPADDING', (0,0), (-1,-1), 10),
    ]))
    return t

def section_header(text, color=MID_BLUE):
    data = [[Paragraph(f'<b>{text}</b>', make_style("SH",
        fontSize=10.5, textColor=colors.white, fontName="Helvetica-Bold"))]]
    t = Table(data, colWidths=[W - 72])
    t.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,-1), color),
        ('TOPPADDING',  (0,0), (-1,-1), 4),
        ('BOTTOMPADDING', (0,0), (-1,-1), 4),
        ('LEFTPADDING', (0,0), (-1,-1), 8),
    ]))
    return t

def info_box(title, items, bg=LIGHT_BLUE, title_color=DARK_BLUE):
    rows = []
    if title:
        rows.append([Paragraph(f'<b>{title}</b>',
            make_style("IBT", fontSize=9.5, textColor=title_color,
                       fontName="Helvetica-Bold"))])
    for item in items:
        rows.append([Paragraph(item, box_style)])
    t = Table(rows, colWidths=[W - 72])
    t.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,-1), bg),
        ('LINEABOVE', (0,0), (-1,0), 1.5, title_color),
        ('LINEBEFORE', (0,0), (0,-1), 3, title_color),
        ('TOPPADDING',  (0,0), (-1,-1), 3),
        ('BOTTOMPADDING', (0,0), (-1,-1), 3),
        ('LEFTPADDING', (0,0), (-1,-1), 8),
    ]))
    return t

def clinical_box(title, items):
    return info_box(title, items, bg=LIGHT_RED, title_color=RED)

def note_box(title, items):
    return info_box(title, items, bg=LIGHT_GREEN, title_color=GREEN)

def make_table(headers, rows, col_widths):
    data = [headers] + rows
    t = Table(data, colWidths=col_widths)
    ts = TableStyle([
        ('BACKGROUND', (0,0), (-1,0), DARK_BLUE),
        ('TEXTCOLOR',  (0,0), (-1,0), colors.white),
        ('FONTNAME',   (0,0), (-1,0), 'Helvetica-Bold'),
        ('FONTSIZE',   (0,0), (-1,-1), 9),
        ('ALIGN',      (0,0), (-1,-1), 'LEFT'),
        ('VALIGN',     (0,0), (-1,-1), 'MIDDLE'),
        ('ROWBACKGROUNDS', (0,1), (-1,-1), [colors.white, LIGHT_BLUE]),
        ('GRID',       (0,0), (-1,-1), 0.5, colors.HexColor("#BDBDBD")),
        ('TOPPADDING', (0,0), (-1,-1), 4),
        ('BOTTOMPADDING', (0,0), (-1,-1), 4),
        ('LEFTPADDING', (0,0), (-1,-1), 6),
    ])
    t.setStyle(ts)
    return t

def bp(text):
    return Paragraph(f"• {text}", bullet_style)

def sp(n=6):
    return Spacer(1, n)

# ═══════════════════════════════════════════════════════════════
#  DIAGRAMS  (pure reportlab Drawing objects)
# ═══════════════════════════════════════════════════════════════

# ── Diagram 1: Scapulohumeral Rhythm ──────────────────────────
class ScapulohuméralRhythmDiagram(Flowable):
    def __init__(self, width=500, height=260):
        super().__init__()
        self.width = width
        self.height = height

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

        # Background
        c.setFillColor(colors.HexColor("#EBF5FB"))
        c.roundRect(0, 0, w, h, 8, fill=1, stroke=0)
        c.setStrokeColor(MID_BLUE)
        c.setLineWidth(1.5)
        c.roundRect(0, 0, w, h, 8, fill=0, stroke=1)

        # Title
        c.setFont("Helvetica-Bold", 11)
        c.setFillColor(DARK_BLUE)
        c.drawCentredString(w/2, h-20, "Scapulohumeral Rhythm  —  2:1 Ratio")

        # ── Left panel: arm arcs ──────────────────────────────
        cx, cy = 140, 110  # center (glenohumeral joint)
        arm_len = 80

        # draw body stick
        c.setStrokeColor(DARK_GREY)
        c.setLineWidth(2)
        c.line(cx, cy, cx, cy - 60)  # trunk
        c.line(cx, cy - 20, cx - 20, cy - 55)  # left arm hanging

        # Three arm positions
        arms = [
            (0,   colors.HexColor("#BDBDBD"), "0°"),
            (90,  ACCENT,                     "90°"),
            (180, DARK_BLUE,                  "180°"),
        ]
        for angle, col, label in arms:
            rad = math.radians(angle)
            ex = cx + arm_len * math.sin(rad)
            ey = cy + arm_len * math.cos(rad)
            c.setStrokeColor(col)
            c.setLineWidth(2.5)
            c.line(cx, cy, ex, ey)
            # arrowhead
            c.setFillColor(col)
            c.circle(ex, ey, 3, fill=1, stroke=0)
            c.setFont("Helvetica-Bold", 8)
            c.setFillColor(col)
            lx = cx + (arm_len + 14) * math.sin(rad)
            ly = cy + (arm_len + 14) * math.cos(rad)
            c.drawCentredString(lx, ly - 3, label)

        # GH joint dot
        c.setFillColor(RED)
        c.circle(cx, cy, 6, fill=1, stroke=0)
        c.setFont("Helvetica-Bold", 8)
        c.setFillColor(RED)
        c.drawString(cx + 8, cy - 3, "GH")

        # Scapula outline (simplified triangle)
        c.setStrokeColor(GREEN)
        c.setFillColor(colors.HexColor("#C8E6C9"))
        c.setLineWidth(1.5)
        scap = [(cx-8, cy-30), (cx-30, cy-70), (cx+12, cy-60)]
        path = c.beginPath()
        path.moveTo(*scap[0])
        for pt in scap[1:]:
            path.lineTo(*pt)
        path.close()
        c.drawPath(path, fill=1, stroke=1)
        c.setFont("Helvetica", 7.5)
        c.setFillColor(GREEN)
        c.drawString(cx-35, cy-80, "Scapula")

        # Thorax rectangle
        c.setStrokeColor(DARK_GREY)
        c.setFillColor(colors.HexColor("#ECEFF1"))
        c.setLineWidth(1)
        c.roundRect(cx-18, cy-100, 36, 45, 4, fill=1, stroke=1)
        c.setFont("Helvetica", 7)
        c.setFillColor(DARK_GREY)
        c.drawCentredString(cx, cy-80, "Thorax")

        # ── Right panel: ratio breakdown bars ─────────────────
        rx = 290
        bar_y_start = h - 50
        bar_h = 16
        gap = 10
        labels = [
            ("0–30°\n(Phase 1)", 30, 0, "Pure GH motion\n(scapula stabilises)"),
            ("30–90°\n(Phase 2)", 40, 20, "2:1 rhythm begins\n(GH 40° : ST 20°)"),
            ("90–180°\n(Phase 3)", 40, 30, "Increasing ST\n(GH 40° : ST 30°)"),
        ]
        bar_total_w = 200

        c.setFont("Helvetica-Bold", 8.5)
        c.setFillColor(DARK_BLUE)
        c.drawString(rx, bar_y_start + 6, "Phase Breakdown  (total = 180°)")

        y = bar_y_start - 20
        for phase_label, gh, st, note in labels:
            total = gh + st if (gh + st) > 0 else 30
            scale = bar_total_w / 180

            # Phase label
            c.setFont("Helvetica-Bold", 8)
            c.setFillColor(DARK_GREY)
            for i, line in enumerate(phase_label.split("\n")):
                c.drawString(rx, y + bar_h - 9 + (1-i)*9, line)

            bx = rx + 68

            if st == 0:
                # Pure GH
                c.setFillColor(MID_BLUE)
                c.rect(bx, y, gh * scale, bar_h, fill=1, stroke=0)
                c.setFont("Helvetica-Bold", 8)
                c.setFillColor(colors.white)
                c.drawCentredString(bx + gh*scale/2, y+4, f"GH {gh}°")
            else:
                c.setFillColor(MID_BLUE)
                c.rect(bx, y, gh * scale, bar_h, fill=1, stroke=0)
                c.setFillColor(GREEN)
                c.rect(bx + gh * scale, y, st * scale, bar_h, fill=1, stroke=0)
                c.setFont("Helvetica-Bold", 8)
                c.setFillColor(colors.white)
                c.drawCentredString(bx + gh*scale/2, y+4, f"GH {gh}°")
                c.drawCentredString(bx + gh*scale + st*scale/2, y+4, f"ST {st}°")

            # note
            c.setFont("Helvetica-Oblique", 7.5)
            c.setFillColor(DARK_GREY)
            for i, line in enumerate(note.split("\n")):
                c.drawString(bx + (gh+st)*scale + 8, y + bar_h - 8 + (1-i)*9, line)

            y -= (bar_h + gap + 6)

        # Legend
        lx, ly = rx, y - 4
        c.setFillColor(MID_BLUE)
        c.rect(lx, ly, 14, 10, fill=1, stroke=0)
        c.setFont("Helvetica", 8)
        c.setFillColor(DARK_GREY)
        c.drawString(lx+17, ly+1, "GH = Glenohumeral")
        c.setFillColor(GREEN)
        c.rect(lx+130, ly, 14, 10, fill=1, stroke=0)
        c.setFillColor(DARK_GREY)
        c.drawString(lx+147, ly+1, "ST = Scapulothoracic")

        # 2:1 ratio callout box
        c.setStrokeColor(ACCENT)
        c.setFillColor(colors.HexColor("#FFF3E0"))
        c.setLineWidth(2)
        c.roundRect(rx, ly-46, 210, 38, 6, fill=1, stroke=1)
        c.setFont("Helvetica-Bold", 9)
        c.setFillColor(ACCENT)
        c.drawCentredString(rx+105, ly-12, "2:1 RATIO")
        c.setFont("Helvetica", 8.5)
        c.setFillColor(DARK_GREY)
        c.drawCentredString(rx+105, ly-26, "For every 3° abduction:")
        c.drawCentredString(rx+105, ly-40, "2° at GH  +  1° at ST  =  3° total")


# ── Diagram 2: Sternoclavicular Joint ─────────────────────────
class SCJointDiagram(Flowable):
    def __init__(self, width=500, height=220):
        super().__init__()
        self.width = width
        self.height = height

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

        c.setFillColor(colors.HexColor("#FFF8E1"))
        c.roundRect(0, 0, w, h, 8, fill=1, stroke=0)
        c.setStrokeColor(ACCENT)
        c.setLineWidth(1.5)
        c.roundRect(0, 0, w, h, 8, fill=0, stroke=1)

        c.setFont("Helvetica-Bold", 11)
        c.setFillColor(DARK_BLUE)
        c.drawCentredString(w/2, h-18, "Sternoclavicular (SC) Joint  —  Structure & Movements")

        # ── Joint cross-section schematic ──────────────────────
        jx, jy = 130, 115

        # Manubrium sterni
        c.setFillColor(colors.HexColor("#FFE0B2"))
        c.setStrokeColor(DARK_GREY)
        c.setLineWidth(1)
        c.roundRect(jx-55, jy-40, 55, 80, 5, fill=1, stroke=1)
        c.setFont("Helvetica-Bold", 8)
        c.setFillColor(DARK_GREY)
        c.drawCentredString(jx-28, jy-50, "Manubrium")
        c.drawCentredString(jx-28, jy-60, "Sterni")

        # Intra-articular disk
        c.setFillColor(colors.HexColor("#B39DDB"))
        c.setStrokeColor(PURPLE)
        c.setLineWidth(1)
        c.ellipse(jx-4, jy-28, jx+4, jy+28, fill=1, stroke=1)
        c.setFont("Helvetica-Bold", 7.5)
        c.setFillColor(PURPLE)
        c.drawString(jx+8, jy+10, "Intra-articular")
        c.drawString(jx+8, jy, "fibrocartilage")
        c.drawString(jx+8, jy-10, "disk")

        # Clavicle
        c.setFillColor(colors.HexColor("#B2EBF2"))
        c.setStrokeColor(MID_BLUE)
        c.setLineWidth(1)
        c.roundRect(jx+4, jy-30, 70, 60, 5, fill=1, stroke=1)
        c.setFont("Helvetica-Bold", 8)
        c.setFillColor(MID_BLUE)
        c.drawCentredString(jx+39, jy-40, "Clavicle")
        c.drawCentredString(jx+39, jy-50, "(medial end)")

        # Joint cavity
        c.setStrokeColor(colors.HexColor("#90CAF9"))
        c.setLineWidth(1)
        c.setFillColor(colors.HexColor("#E3F2FD"))
        c.rect(jx-3, jy-28, 7, 56, fill=1, stroke=1)

        # Ligaments (arrows)
        def draw_lig(x1, y1, x2, y2, label, col):
            c.setStrokeColor(col)
            c.setLineWidth(1.5)
            c.line(x1, y1, x2, y2)
            c.setFillColor(col)
            c.circle(x1, y1, 2.5, fill=1, stroke=0)
            c.setFont("Helvetica", 7.5)
            c.setFillColor(col)
            c.drawString(x2+2, y2-3, label)

        draw_lig(jx+20, jy+30, jx+80, jy+60, "Ant. SC lig.", RED)
        draw_lig(jx+20, jy-30, jx+80, jy-60, "Post. SC lig.", DARK_BLUE)
        draw_lig(jx+50, jy-28, jx+100, jy-75, "Costoclavicular lig.", GREEN)
        draw_lig(jx-30, jy+28, jx-80, jy+65, "Interclavicular lig.", PURPLE)

        # ── Movements table on right ───────────────────────────
        tx = 295
        c.setFont("Helvetica-Bold", 9)
        c.setFillColor(DARK_BLUE)
        c.drawString(tx, h-38, "Available Movements")

        movements = [
            ("Elevation / Depression", "~30–35°", MID_BLUE),
            ("Protraction / Retraction", "~15–30°", GREEN),
            ("Axial Rotation", "~40–50°", ACCENT),
        ]
        my = h - 55
        for name, range_, col in movements:
            c.setFillColor(col)
            c.roundRect(tx, my, 205, 20, 3, fill=1, stroke=0)
            c.setFillColor(colors.white)
            c.setFont("Helvetica-Bold", 8.5)
            c.drawString(tx+6, my+6, name)
            c.setFont("Helvetica", 8.5)
            c.drawRightString(tx+200, my+6, range_)
            my -= 26

        # Classification box
        c.setFillColor(LIGHT_GREEN)
        c.setStrokeColor(GREEN)
        c.setLineWidth(1)
        c.roundRect(tx, my-10, 205, 42, 4, fill=1, stroke=1)
        c.setFont("Helvetica-Bold", 8.5)
        c.setFillColor(GREEN)
        c.drawString(tx+6, my+26, "Classification:")
        c.setFont("Helvetica", 8.5)
        c.setFillColor(DARK_GREY)
        c.drawString(tx+6, my+14, "Diarthrodial saddle (synovial) joint")
        c.drawString(tx+6, my+2, "Contains intra-articular fibrocartilage disk")

        # Functional importance
        c.setFillColor(LIGHT_BLUE)
        c.setStrokeColor(MID_BLUE)
        c.roundRect(tx, 8, 205, 50, 4, fill=1, stroke=1)
        c.setFont("Helvetica-Bold", 8.5)
        c.setFillColor(DARK_BLUE)
        c.drawString(tx+6, 52, "Functional Importance:")
        c.setFont("Helvetica", 8)
        c.setFillColor(DARK_GREY)
        lines = [
            "Only bony link: upper limb  axial skeleton",
            "Transmits all forces from arm to thorax",
            "Clavicular rotation here enables 180° elevation",
        ]
        for i, line in enumerate(lines):
            c.drawString(tx+6, 40 - i*12, line)


# ── Diagram 3: Force Couple Mechanism ─────────────────────────
class ForceCoupleD(Flowable):
    def __init__(self, width=500, height=270):
        super().__init__()
        self.width = width
        self.height = height

    def _arrow(self, c, x1, y1, x2, y2, col, lw=2.0, head=8):
        c.setStrokeColor(col)
        c.setFillColor(col)
        c.setLineWidth(lw)
        c.line(x1, y1, x2, y2)
        # arrowhead
        angle = math.atan2(y2-y1, x2-x1)
        for da in [0.4, -0.4]:
            ex = x2 - head * math.cos(angle + da)
            ey = y2 - head * math.sin(angle + da)
            c.line(x2, y2, ex, ey)

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

        c.setFillColor(colors.HexColor("#F3E5F5"))
        c.roundRect(0, 0, w, h, 8, fill=1, stroke=0)
        c.setStrokeColor(PURPLE)
        c.setLineWidth(1.5)
        c.roundRect(0, 0, w, h, 8, fill=0, stroke=1)

        c.setFont("Helvetica-Bold", 11)
        c.setFillColor(DARK_BLUE)
        c.drawCentredString(w/2, h-18, "Force Couple Mechanism in Shoulder Abduction")

        # ── Left: GH coronal plane force couple ─────────────
        cx, cy = 130, 120

        # Humeral head (circle)
        c.setFillColor(colors.HexColor("#FFE0B2"))
        c.setStrokeColor(DARK_GREY)
        c.setLineWidth(1.5)
        c.circle(cx, cy, 30, fill=1, stroke=1)
        c.setFont("Helvetica-Bold", 8)
        c.setFillColor(DARK_GREY)
        c.drawCentredString(cx, cy+2, "Humeral")
        c.drawCentredString(cx, cy-9, "Head")

        # Glenoid (arc)
        c.setStrokeColor(MID_BLUE)
        c.setFillColor(colors.HexColor("#BBDEFB"))
        c.setLineWidth(2)
        path = c.beginPath()
        path.arc(cx-50, cy-35, cx-20, cy+35, 90, 200)
        c.drawPath(path, fill=0, stroke=1)
        c.setFont("Helvetica", 7.5)
        c.setFillColor(MID_BLUE)
        c.drawString(cx-75, cy, "Glenoid")

        # Deltoid arrow (upward shear)
        self._arrow(c, cx+20, cy+20, cx+20, cy+80, RED, lw=3, head=10)
        c.setFont("Helvetica-Bold", 8.5)
        c.setFillColor(RED)
        c.drawString(cx+24, cy+55, "Deltoid")
        c.drawString(cx+24, cy+44, "(Superior")
        c.drawString(cx+24, cy+33, "shear force)")

        # Rotator cuff arrow (downward compressive)
        self._arrow(c, cx+18, cy-20, cx+18, cy-75, DARK_BLUE, lw=3, head=10)
        c.setFont("Helvetica-Bold", 8.5)
        c.setFillColor(DARK_BLUE)
        c.drawString(cx+24, cy-40, "Rotator Cuff")
        c.drawString(cx+24, cy-52, "(Inferior +")
        c.drawString(cx+24, cy-63, "compressive)")

        # Rotation arc
        c.setStrokeColor(GREEN)
        c.setLineWidth(2)
        path2 = c.beginPath()
        path2.arc(cx-25, cy-25, cx+25, cy+25, 30, 300)
        c.drawPath(path2, fill=0, stroke=1)
        c.setFillColor(GREEN)
        c.circle(cx+22, cy+18, 4, fill=1, stroke=0)
        c.setFont("Helvetica-Bold", 8)
        c.setFillColor(GREEN)
        c.drawString(cx-48, cy-38, "Pure rotation")
        c.drawString(cx-42, cy-50, "(no translation)")

        # Left panel label
        c.setFont("Helvetica-Bold", 9)
        c.setFillColor(DARK_BLUE)
        c.drawCentredString(cx, 18, "GH Joint  —  Coronal Plane")

        # ── Right: Scapular force couple ─────────────────────
        sx, sy = 370, 130

        # Scapula shape
        c.setFillColor(colors.HexColor("#E0F7FA"))
        c.setStrokeColor(MID_BLUE)
        c.setLineWidth(1.5)
        pts = [(sx-10, sy+55), (sx-50, sy-30), (sx+30, sy-50), (sx+35, sy+20)]
        path3 = c.beginPath()
        path3.moveTo(*pts[0])
        for pt in pts[1:]:
            path3.lineTo(*pt)
        path3.close()
        c.drawPath(path3, fill=1, stroke=1)
        c.setFont("Helvetica-Bold", 9)
        c.setFillColor(MID_BLUE)
        c.drawCentredString(sx, sy, "Scapula")

        # AC joint point
        c.setFillColor(ACCENT)
        c.circle(sx+30, sy+20, 5, fill=1, stroke=0)

        # Upper trapezius
        self._arrow(c, sx-10, sy+55, sx-60, sy+100, RED, lw=2.5)
        c.setFont("Helvetica-Bold", 8)
        c.setFillColor(RED)
        c.drawString(sx-95, sy+95, "Upper Trapezius")
        c.drawString(sx-85, sy+84, "(elevates clavicle)")

        # Lower trapezius
        self._arrow(c, sx+30, sy-50, sx+80, sy-90, DARK_BLUE, lw=2.5)
        c.setFont("Helvetica-Bold", 8)
        c.setFillColor(DARK_BLUE)
        c.drawString(sx+28, sy-95, "Lower Trap.")

        # Serratus anterior
        self._arrow(c, sx-50, sy-30, sx-100, sy-55, GREEN, lw=2.5)
        c.setFont("Helvetica-Bold", 8)
        c.setFillColor(GREEN)
        c.drawString(sx-155, sy-58, "Serratus Ant.")

        # Rotation arrow on scapula
        c.setStrokeColor(PURPLE)
        c.setLineWidth(2)
        path4 = c.beginPath()
        path4.arc(sx-20, sy-20, sx+20, sy+20, 0, 270)
        c.drawPath(path4, fill=0, stroke=1)
        c.setFillColor(PURPLE)
        c.circle(sx+20, sy, 4, fill=1, stroke=0)
        c.setFont("Helvetica-Bold", 8)
        c.setFillColor(PURPLE)
        c.drawString(sx-18, sy-35, "Upward rotation")

        # Right panel label
        c.setFont("Helvetica-Bold", 9)
        c.setFillColor(DARK_BLUE)
        c.drawCentredString(sx, 18, "Scapular Force Couple")

        # Divider
        c.setStrokeColor(colors.HexColor("#CE93D8"))
        c.setLineWidth(1)
        c.line(255, 30, 255, h-35)

        # Consequence box at bottom centre
        c.setFillColor(LIGHT_RED)
        c.setStrokeColor(RED)
        c.setLineWidth(1.5)
        c.roundRect(180, 4, 155, 30, 4, fill=1, stroke=1)
        c.setFont("Helvetica-Bold", 8)
        c.setFillColor(RED)
        c.drawCentredString(258, 23, "If disrupted:")
        c.setFont("Helvetica", 8)
        c.setFillColor(DARK_GREY)
        c.drawCentredString(258, 11, "Superior migration + Impingement")


# ═══════════════════════════════════════════════════════════════
#  BUILD THE DOCUMENT
# ═══════════════════════════════════════════════════════════════

OUTPUT = "/tmp/workspace/shoulder-biomechanics/Shoulder_Biomechanics_Reference.pdf"

doc = SimpleDocTemplate(
    OUTPUT,
    pagesize=A4,
    leftMargin=36, rightMargin=36,
    topMargin=40, bottomMargin=36,
    title="Biomechanics of the Shoulder Complex – Reference Guide",
    author="Orris AI",
)

story = []

# ── Cover title ──────────────────────────────────────────────
story.append(Spacer(1, 10))
story.append(Paragraph("Biomechanics of the Shoulder Complex", title_style))
story.append(Paragraph("Complete Reference Guide", subtitle_style))
story.append(Paragraph("Assignment Answer Key  |  Orris Medical Library", subtitle_style))
story.append(HRFlowable(width="100%", thickness=2, color=DARK_BLUE, spaceAfter=12))

# ════════════════════════════════════════════════════════
#  Q1 – SHOULDER ABDUCTION 0–180°
# ════════════════════════════════════════════════════════
story.append(q_banner("Q1. Biomechanics of Shoulder Abduction 0° to 180°  (10 marks)"))
story.append(sp(6))

# --- 1a Phases
story.append(section_header("1a. Phases of Shoulder Abduction  (2 marks)"))
story.append(sp(4))
story.append(Paragraph(
    "Shoulder abduction occurs in three recognisable phases, each characterised by "
    "changing joint contributions and muscle activity:",
    body_style))
story.append(sp(4))

phases = [
    [Paragraph("<b>Phase 1\n0–30°</b>", make_style("PT", fontSize=9, fontName="Helvetica-Bold",
        textColor=colors.white)),
     Paragraph("<b>Pure Glenohumeral (GH) Motion</b>", make_style("PH", fontSize=9, fontName="Helvetica-Bold")),
     Paragraph(
       "The scapula 'sets' itself against the thorax and seeks a stable base. Deltoid and "
       "supraspinatus initiate abduction. Minimal scapular rotation occurs. The subacromial "
       "space is relatively open.",
       make_style("PB", fontSize=9, leading=13))],
    [Paragraph("<b>Phase 2\n30–90°</b>", make_style("PT2", fontSize=9, fontName="Helvetica-Bold",
        textColor=colors.white)),
     Paragraph("<b>Combined GH + Scapulothoracic (ST) Motion</b>", make_style("PH2", fontSize=9, fontName="Helvetica-Bold")),
     Paragraph(
       "Scapulohumeral rhythm (2:1 ratio) is fully established. The deltoid becomes the "
       "primary abductor; the rotator cuff depresses the humeral head. Scapular upward "
       "rotation begins — serratus anterior and trapezius activate.",
       make_style("PB2", fontSize=9, leading=13))],
    [Paragraph("<b>Phase 3\n90–180°</b>", make_style("PT3", fontSize=9, fontName="Helvetica-Bold",
        textColor=colors.white)),
     Paragraph("<b>Increasing Scapulothoracic Contribution</b>", make_style("PH3", fontSize=9, fontName="Helvetica-Bold")),
     Paragraph(
       "External rotation of the humerus is required to clear the greater tuberosity under "
       "the coracoacromial arch. Scapulothoracic motion contributes a higher proportion. "
       "The AC and SC joints contribute additional rotation (30–40°) to complete the arc.",
       make_style("PB3", fontSize=9, leading=13))],
]
col_w = [(W-72)*0.13, (W-72)*0.30, (W-72)*0.57]
pt = Table(phases, colWidths=col_w)
pt.setStyle(TableStyle([
    ('BACKGROUND', (0,0), (0,0), MID_BLUE),
    ('BACKGROUND', (0,1), (0,1), ACCENT),
    ('BACKGROUND', (0,2), (0,2), DARK_BLUE),
    ('ROWBACKGROUNDS', (1,0), (-1,-1), [LIGHT_BLUE, colors.HexColor("#FFF3E0"), colors.HexColor("#E8EAF6")]),
    ('VALIGN',   (0,0), (-1,-1), 'TOP'),
    ('GRID',     (0,0), (-1,-1), 0.5, colors.HexColor("#BDBDBD")),
    ('TOPPADDING', (0,0), (-1,-1), 5),
    ('BOTTOMPADDING', (0,0), (-1,-1), 5),
    ('LEFTPADDING', (0,0), (-1,-1), 6),
    ('RIGHTPADDING', (0,0), (-1,-1), 6),
    ('ALIGN', (0,0), (0,-1), 'CENTER'),
]))
story.append(pt)
story.append(sp(10))

# --- 1b GH vs ST
story.append(section_header("1b. Glenohumeral vs. Scapulothoracic Contribution  (2 marks)"))
story.append(sp(4))
story.append(Paragraph(
    "Total shoulder abduction = 180°, divided between two joints in a fixed ratio:",
    body_style))
story.append(sp(4))
gh_data = [
    [Paragraph("<b>Joint</b>", make_style("GHH", fontSize=9.5, fontName="Helvetica-Bold", textColor=colors.white)),
     Paragraph("<b>Contribution</b>", make_style("GHH2", fontSize=9.5, fontName="Helvetica-Bold", textColor=colors.white)),
     Paragraph("<b>Mechanism</b>", make_style("GHH3", fontSize=9.5, fontName="Helvetica-Bold", textColor=colors.white))],
    [Paragraph("Glenohumeral (GH)", body_style),
     Paragraph("120° (2/3 of total)", make_style("GHB", fontSize=9.5, fontName="Helvetica-Bold", textColor=MID_BLUE)),
     Paragraph("Ball-and-socket movement of humeral head on glenoid fossa; controlled by rotator cuff", body_style)],
    [Paragraph("Scapulothoracic (ST)", body_style),
     Paragraph("60° (1/3 of total)", make_style("GHB2", fontSize=9.5, fontName="Helvetica-Bold", textColor=GREEN)),
     Paragraph("Upward rotation of scapula on thoracic wall; driven by serratus anterior and trapezius", body_style)],
]
gt = make_table(
    gh_data[0], gh_data[1:],
    [(W-72)*0.25, (W-72)*0.22, (W-72)*0.53]
)
story.append(gt)
story.append(sp(4))
story.append(Paragraph(
    "As elevation increases above 90°, the proportion of scapulothoracic motion relative "
    "to glenohumeral motion increases progressively. — <i>Rheumatology 2022, p.700</i>",
    make_style("Cite", parent="Normal", fontSize=9, textColor=DARK_GREY,
               fontName="Helvetica-Oblique", leftIndent=10)))
story.append(sp(10))

# --- 1c Scapulohumeral rhythm
story.append(section_header("1c. Scapulohumeral Rhythm and the 2:1 Ratio  (2 marks)"))
story.append(sp(4))
story.append(info_box(
    "Definition",
    ["Scapulohumeral rhythm is the coordinated, synchronous movement between the "
     "glenohumeral and scapulothoracic joints during arm elevation, maintaining "
     "the glenoid as a stable base under the humeral head throughout the range of motion."],
    bg=LIGHT_BLUE, title_color=DARK_BLUE))
story.append(sp(4))
story.append(Paragraph("<b>The 2:1 Ratio:</b>", h3_style))
story.append(bp("For every 3° of total shoulder abduction:"))
story.append(bp("   → 2° occurs at the <b>glenohumeral joint</b>"))
story.append(bp("   → 1° occurs at the <b>scapulothoracic joint</b>"))
story.append(sp(4))
story.append(Paragraph("<b>Significance of this rhythm:</b>", h3_style))
story.append(bp("Maintains optimal length-tension relationship of the deltoid throughout abduction"))
story.append(bp("Prevents superior migration of the humeral head (prevents impingement)"))
story.append(bp("Keeps the glenoid oriented under the humeral head, providing a stable base"))
story.append(bp("Disruption of the rhythm → secondary subacromial impingement"))
story.append(sp(10))

# ── DIAGRAM 1 ──
story.append(section_header("Diagram: Scapulohumeral Rhythm", color=GREEN))
story.append(sp(4))
story.append(ScapulohuméralRhythmDiagram(width=W-72, height=260))
story.append(Paragraph(
    "Figure 1. Scapulohumeral rhythm: arm positions at 0°, 90° and 180° (left), and "
    "phase-by-phase GH vs. ST contribution with the 2:1 ratio callout (right).",
    caption_style))
story.append(sp(10))

# --- 1d SC and AC joints
story.append(section_header("1d. SC and AC Joint Movements During Abduction  (2 marks)"))
story.append(sp(4))
jt_data = [
    [Paragraph("<b>Joint</b>", make_style("JH", fontSize=9.5, fontName="Helvetica-Bold", textColor=colors.white)),
     Paragraph("<b>Movement During Abduction</b>", make_style("JH2", fontSize=9.5, fontName="Helvetica-Bold", textColor=colors.white)),
     Paragraph("<b>Key Stabilisers</b>", make_style("JH3", fontSize=9.5, fontName="Helvetica-Bold", textColor=colors.white))],
    [Paragraph("<b>Sternoclavicular (SC)</b>", make_style("JB1", fontSize=9.5, fontName="Helvetica-Bold")),
     Paragraph("Elevation (30–35°) + posterior axial rotation (40–50°). "
               "This rotation is the primary mechanism enabling the clavicle to transmit full overhead elevation. "
               "Clavicular rotation occurs largely at the SC joint.",
               make_style("JBT", fontSize=9, leading=13)),
     Paragraph("Anterior SC ligament (resists superior displacement), "
               "Posterior SC ligament (primary AP restraint), Costoclavicular ligament",
               make_style("JBT2", fontSize=9, leading=13))],
    [Paragraph("<b>Acromioclavicular (AC)</b>", make_style("JB2", fontSize=9.5, fontName="Helvetica-Bold")),
     Paragraph("Allows 30–40° rotation during abduction and elevation. "
               "The AC joint fine-tunes the scapular position relative to the clavicle.",
               make_style("JBT3", fontSize=9, leading=13)),
     Paragraph("Conoid and trapezoid (coracoclavicular) ligaments — maintain the scapula-clavicle "
               "relationship. Posterior transverse ligament and inferior AC ligament.",
               make_style("JBT4", fontSize=9, leading=13))],
]
jt = make_table(jt_data[0], jt_data[1:],
    [(W-72)*0.20, (W-72)*0.43, (W-72)*0.37])
story.append(jt)
story.append(sp(10))

# --- 1e Rotator cuff
story.append(section_header("1e. Rotator Cuff Role in Shoulder Stability  (2 marks)"))
story.append(sp(4))
story.append(Paragraph(
    "The rotator cuff (supraspinatus, infraspinatus, teres minor, subscapularis) maintains "
    "glenohumeral stability through three distinct mechanisms:",
    body_style))
story.append(sp(4))

rc_data = [
    [Paragraph("<b>Mechanism</b>", make_style("RH1", fontSize=9.5, fontName="Helvetica-Bold", textColor=colors.white)),
     Paragraph("<b>Description</b>", make_style("RH2", fontSize=9.5, fontName="Helvetica-Bold", textColor=colors.white)),
     Paragraph("<b>Evidence</b>", make_style("RH3", fontSize=9.5, fontName="Helvetica-Bold", textColor=colors.white))],
    [Paragraph("1. Concavity-Compression", make_style("RB1", fontSize=9, fontName="Helvetica-Bold")),
     Paragraph("Compression of the humeral head into the glenoid cavity stabilises it "
               "against translating forces. Most effective in midranges when capsuloligamentous "
               "structures are lax.", make_style("RBT", fontSize=9, leading=13)),
     Paragraph("50% reduction in cuff forces → 46% increase in anterior displacement",
               make_style("RBE", fontSize=9, leading=13, textColor=RED))],
    [Paragraph("2. Centering the Head", make_style("RB2", fontSize=9, fontName="Helvetica-Bold")),
     Paragraph("Coordinated contraction guides the humeral head onto the centre of the glenoid "
               "throughout abduction, preventing superior migration caused by the deltoid's shear force.",
               make_style("RBT2", fontSize=9, leading=13)),
     Paragraph("EMG studies show reduced supraspinatus activity (30–60°) in instability patients",
               make_style("RBE2", fontSize=9, leading=13, textColor=DARK_BLUE))],
    [Paragraph("3. Ligament Dynamisation", make_style("RB3", fontSize=9, fontName="Helvetica-Bold")),
     Paragraph("At extremes of motion, cuff attachments tension the glenohumeral ligaments, "
               "enhancing passive stability when the joint is at the end of its range.",
               make_style("RBT3", fontSize=9, leading=13)),
     Paragraph("Capsuloligamentous structures stretched at extremes to enhance contribution to stability",
               make_style("RBE3", fontSize=9, leading=13, textColor=GREEN))],
]
rc = make_table(rc_data[0], rc_data[1:],
    [(W-72)*0.23, (W-72)*0.46, (W-72)*0.31])
story.append(rc)
story.append(sp(4))
story.append(note_box(
    "Key Point",
    ["The four rotator cuff tendons grip the humeral head like 'guy ropes', counteracting "
     "the superiorly directed shear force of the deltoid and converting it into pure rotation "
     "at the glenohumeral joint.  — Rheumatology 2022, p.700 | Rockwood & Green's 2025, p.1272"]))
story.append(PageBreak())

# ════════════════════════════════════════════════════════
#  Q2 – STERNOCLAVICULAR JOINT
# ════════════════════════════════════════════════════════
story.append(q_banner("Q2. Structure and Biomechanics of the Sternoclavicular Joint  (5 marks)",
    color=colors.HexColor("#4A148C")))
story.append(sp(8))

story.append(section_header("Classification and Articular Surfaces", color=PURPLE))
story.append(sp(4))
story.append(Paragraph(
    "The sternoclavicular (SC) joint is classified as a <b>diarthrodial saddle-type synovial joint</b>. "
    "Despite its saddle geometry, it functions as a <b>multiaxial joint</b> due to the presence of "
    "a fibrocartilaginous intra-articular disk that divides the joint into two compartments and "
    "compensates for the incongruence of the articular surfaces.",
    body_style))
story.append(sp(6))

art_data = [
    [Paragraph("<b>Component</b>", make_style("AH1", fontSize=9.5, fontName="Helvetica-Bold", textColor=colors.white)),
     Paragraph("<b>Detail</b>", make_style("AH2", fontSize=9.5, fontName="Helvetica-Bold", textColor=colors.white))],
    [Paragraph("Classification", body_style),
     Paragraph("Diarthrodial saddle joint (synovial); functionally multiaxial", body_style)],
    [Paragraph("Medial articular surface", body_style),
     Paragraph("Medial (sternal) end of the clavicle — convex anteroposteriorly, "
               "concave superoinferiorly", body_style)],
    [Paragraph("Lateral articular surface", body_style),
     Paragraph("Clavicular notch of the manubrium sterni + upper surface of 1st costal cartilage", body_style)],
    [Paragraph("Intra-articular disk", body_style),
     Paragraph("Fibrocartilaginous disk divides joint into two compartments; compensates for "
               "incongruent surfaces; acts as a shock absorber; attached superiorly to clavicle "
               "and inferiorly to 1st costal cartilage", body_style)],
    [Paragraph("Joint capsule", body_style),
     Paragraph("Thick fibrous capsule reinforced by all surrounding ligaments", body_style)],
]
at = make_table(art_data[0], art_data[1:], [(W-72)*0.25, (W-72)*0.75])
story.append(at)
story.append(sp(10))

story.append(section_header("Ligaments Supporting the SC Joint", color=PURPLE))
story.append(sp(4))
lig_data = [
    [Paragraph("<b>Ligament</b>", make_style("LH1", fontSize=9.5, fontName="Helvetica-Bold", textColor=colors.white)),
     Paragraph("<b>Location</b>", make_style("LH2", fontSize=9.5, fontName="Helvetica-Bold", textColor=colors.white)),
     Paragraph("<b>Primary Function</b>", make_style("LH3", fontSize=9.5, fontName="Helvetica-Bold", textColor=colors.white))],
    [Paragraph("Anterior SC ligament", body_style),
     Paragraph("Anterior capsule", body_style),
     Paragraph("Resists <b>superior displacement</b> of medial clavicle", body_style)],
    [Paragraph("Posterior SC ligament", body_style),
     Paragraph("Posterior capsule", body_style),
     Paragraph("<b>Primary restraint</b> for anteroposterior (AP) stability", body_style)],
    [Paragraph("Costoclavicular ligament", body_style),
     Paragraph("Clavicle to 1st rib / costal cartilage", body_style),
     Paragraph("Limits elevation, depression, and rotation of clavicle", body_style)],
    [Paragraph("Interclavicular ligament", body_style),
     Paragraph("Connects both clavicular heads across the manubrium", body_style),
     Paragraph("Resists lateral and inferior displacement of medial clavicle", body_style)],
]
lt = make_table(lig_data[0], lig_data[1:],
    [(W-72)*0.28, (W-72)*0.30, (W-72)*0.42])
story.append(lt)
story.append(sp(10))

story.append(section_header("Available Movements", color=PURPLE))
story.append(sp(4))
mv_data = [
    [Paragraph("<b>Movement</b>", make_style("MH1", fontSize=9.5, fontName="Helvetica-Bold", textColor=colors.white)),
     Paragraph("<b>Range</b>", make_style("MH2", fontSize=9.5, fontName="Helvetica-Bold", textColor=colors.white)),
     Paragraph("<b>Occurs when</b>", make_style("MH3", fontSize=9.5, fontName="Helvetica-Bold", textColor=colors.white))],
    [Paragraph("Elevation / Depression", body_style),
     Paragraph("~30–35°", make_style("MV1", fontSize=9.5, fontName="Helvetica-Bold", textColor=MID_BLUE)),
     Paragraph("Shrugging / depressing the shoulder girdle", body_style)],
    [Paragraph("Protraction / Retraction", body_style),
     Paragraph("~15–30°", make_style("MV2", fontSize=9.5, fontName="Helvetica-Bold", textColor=GREEN)),
     Paragraph("Reaching forward / bracing back the shoulder", body_style)],
    [Paragraph("Axial (long axis) Rotation", body_style),
     Paragraph("~40–50°", make_style("MV3", fontSize=9.5, fontName="Helvetica-Bold", textColor=ACCENT)),
     Paragraph("During full overhead elevation (above 90°) — the clavicle rotates backwards, "
               "allowing full scapular upward rotation", body_style)],
]
mvt = make_table(mv_data[0], mv_data[1:],
    [(W-72)*0.28, (W-72)*0.14, (W-72)*0.58])
story.append(mvt)
story.append(sp(10))

story.append(section_header("Functional Importance", color=PURPLE))
story.append(sp(4))
story.append(info_box(
    "Why the SC Joint is Indispensable",
    ["<b>Only bony link</b> between the entire upper limb and the axial skeleton — all forces "
     "generated by the arm are transmitted through this joint.",
     "Without SC joint motion, <b>full 180° abduction is impossible</b> — clavicular posterior "
     "rotation at the SC joint is the obligatory mechanism that allows the scapula to complete "
     "its upward rotation.",
     "Provides the pivot point about which the shoulder girdle (clavicle + scapula) moves in "
     "three dimensions during all upper limb activities.",
     "Despite high mobility, it is the <b>least commonly dislocated major joint</b> due to its "
     "strong ligamentous support — but when dislocated posteriorly, it can threaten the great "
     "vessels and trachea.",
    ], bg=LIGHT_PURPLE, title_color=PURPLE))
story.append(sp(10))

# DIAGRAM 2
story.append(section_header("Diagram: Sternoclavicular Joint", color=PURPLE))
story.append(sp(4))
story.append(SCJointDiagram(width=W-72, height=220))
story.append(Paragraph(
    "Figure 2. Cross-sectional schematic of the SC joint showing articular surfaces, intra-articular "
    "disk, and major ligaments (left); available movements and functional classification (right).",
    caption_style))
story.append(PageBreak())

# ════════════════════════════════════════════════════════
#  Q3 – FORCE COUPLE
# ════════════════════════════════════════════════════════
story.append(q_banner("Q3. Force Couple Mechanism in Shoulder Abduction  (5 marks)",
    color=colors.HexColor("#1B5E20")))
story.append(sp(8))

story.append(section_header("Definition of Force Couple", color=GREEN))
story.append(sp(4))
story.append(info_box(
    "Force Couple  —  Definition",
    ["A <b>force couple</b> is a pair of equal and opposite forces acting at different "
     "points on the same body, producing <b>pure rotation without linear translation</b>. "
     "The net force is zero, but the net torque (moment) is non-zero.",
     "In the shoulder, the force couple ensures the humeral head rotates cleanly within "
     "the glenoid fossa without migrating superiorly or inferiorly."],
    bg=LIGHT_GREEN, title_color=GREEN))
story.append(sp(10))

story.append(section_header("Muscles Involved", color=GREEN))
story.append(sp(4))
story.append(Paragraph("<b>1. Glenohumeral Force Couple (Coronal Plane):</b>", h3_style))
story.append(sp(3))

fc1_data = [
    [Paragraph("<b>Muscle</b>", make_style("FCH1", fontSize=9.5, fontName="Helvetica-Bold", textColor=colors.white)),
     Paragraph("<b>Force Direction</b>", make_style("FCH2", fontSize=9.5, fontName="Helvetica-Bold", textColor=colors.white)),
     Paragraph("<b>Role</b>", make_style("FCH3", fontSize=9.5, fontName="Helvetica-Bold", textColor=colors.white))],
    [Paragraph("Deltoid (middle)", body_style),
     Paragraph("⬆ Superior shear", make_style("FCV1", fontSize=9.5, textColor=RED, fontName="Helvetica-Bold")),
     Paragraph("Primary abductor — generates strong superiorly directed force on the "
               "humeral head. Would cause subacromial impingement if unchecked.", body_style)],
    [Paragraph("Infraspinatus, Teres Minor, Subscapularis", body_style),
     Paragraph("⬇ Inferior + compressive", make_style("FCV2", fontSize=9.5, textColor=DARK_BLUE, fontName="Helvetica-Bold")),
     Paragraph("Act below the GH joint centre of rotation — depress and compress the "
               "humeral head, counteracting the deltoid's shear vector. Convert upward "
               "force into rotation.", body_style)],
    [Paragraph("Supraspinatus", body_style),
     Paragraph("→ Compressive", make_style("FCV3", fontSize=9.5, textColor=MID_BLUE, fontName="Helvetica-Bold")),
     Paragraph("Compresses the humeral head into the glenoid; assists deltoid in "
               "initiating abduction (most active 30–60°).", body_style)],
]
fc1 = make_table(fc1_data[0], fc1_data[1:],
    [(W-72)*0.28, (W-72)*0.22, (W-72)*0.50])
story.append(fc1)
story.append(sp(8))

story.append(Paragraph("<b>2. Scapular Force Couple (for upward rotation):</b>", h3_style))
story.append(sp(3))
fc2_data = [
    [Paragraph("<b>Muscle</b>", make_style("FC2H1", fontSize=9.5, fontName="Helvetica-Bold", textColor=colors.white)),
     Paragraph("<b>Action on Scapula</b>", make_style("FC2H2", fontSize=9.5, fontName="Helvetica-Bold", textColor=colors.white)),
     Paragraph("<b>Net Effect</b>", make_style("FC2H3", fontSize=9.5, fontName="Helvetica-Bold", textColor=colors.white))],
    [Paragraph("Upper Trapezius", body_style),
     Paragraph("Elevates and retracts clavicle, pulls scapula superiorly", body_style),
     Paragraph(rowspan := "Together produce upward rotation of scapula as a force couple — "
               "upper fibres pull upward on the clavicle/acromion while lower fibres + serratus "
               "pull the inferior angle laterally and forward. Net result: glenoid faces upward.",
               body_style)],
    [Paragraph("Lower Trapezius", body_style),
     Paragraph("Depresses and adducts the scapula (pulls inferior angle medially)", body_style),
     Paragraph("", body_style)],
    [Paragraph("Serratus Anterior", body_style),
     Paragraph("Protracts and rotates inferior angle of scapula laterally", body_style),
     Paragraph("", body_style)],
]
# Just do a simpler table for this
fc2_simple = [
    [Paragraph("<b>Muscle</b>", make_style("FC2H1", fontSize=9.5, fontName="Helvetica-Bold", textColor=colors.white)),
     Paragraph("<b>Action</b>", make_style("FC2H2", fontSize=9.5, fontName="Helvetica-Bold", textColor=colors.white))],
    [Paragraph("Upper Trapezius", body_style),
     Paragraph("Elevates acromion / lateral clavicle — rotates glenoid upward", body_style)],
    [Paragraph("Lower Trapezius", body_style),
     Paragraph("Depresses scapular spine — creates inferior moment arm for rotation", body_style)],
    [Paragraph("Serratus Anterior", body_style),
     Paragraph("Protracts and upward-rotates inferior angle of scapula laterally", body_style)],
]
fc2t = make_table(fc2_simple[0], fc2_simple[1:],
    [(W-72)*0.28, (W-72)*0.72])
story.append(fc2t)
story.append(sp(10))

story.append(section_header("Mechanism of Action During Abduction", color=GREEN))
story.append(sp(4))
story.append(Paragraph(
    "As the <b>deltoid</b> contracts to initiate abduction, it generates an upward shear vector "
    "that would displace the humeral head into the acromion. Simultaneously, the "
    "<b>rotator cuff muscles</b> (particularly infraspinatus, teres minor, subscapularis — "
    "which all insert below the GH joint centre) generate a downward + compressive force vector. "
    "These two opposing forces constitute a force couple:",
    body_style))
story.append(sp(4))
story.append(bp("The net translational force on the humeral head = zero (forces cancel)"))
story.append(bp("The net torque = rotation of the humeral head within the glenoid"))
story.append(bp("The humeral head remains centered on the glenoid throughout the arc"))
story.append(sp(4))
story.append(Paragraph(
    "The rotator cuff tendons act like 'guy ropes' around the humeral head, redirecting "
    "the deltoid's vertical force vector into pure glenohumeral rotation. "
    "— <i>Rheumatology 2022, p.700</i>",
    make_style("Cite2", parent="Normal", fontSize=9, textColor=DARK_GREY,
               fontName="Helvetica-Oblique", leftIndent=10)))
story.append(sp(10))

story.append(section_header("Importance in Normal Shoulder Biomechanics", color=GREEN))
story.append(sp(4))
story.append(note_box("Key Functions of the Force Couple", [
    "Maintains the humeral head centered on the glenoid fossa throughout 0–180° of abduction",
    "Preserves the subacromial space (~9–10 mm), preventing impingement of the supraspinatus tendon",
    "Maximises the mechanical efficiency of abduction by converting shear into rotation",
    "Allows the deltoid to act as a powerful abductor across the full range of motion",
    "Enables smooth, coordinated scapulohumeral rhythm",
]))
story.append(sp(10))

story.append(section_header("Clinical Significance if the Force Couple is Disrupted", color=RED))
story.append(sp(4))
story.append(clinical_box("Clinical Consequences of Force Couple Disruption", [
    "<b>Rotator Cuff Tear:</b> Loss of inferior cuff force → unchecked superior migration of humeral "
    "head → subacromial impingement → progressive cuff tear arthropathy (Hamada classification).",
    "<b>Painful Arc Syndrome:</b> Occurs at 60–120° — supraspinatus and subacromial bursa are "
    "pinched under the coracoacromial arch due to impaired head depression.",
    "<b>Glenohumeral Instability:</b> Inability to centre the humeral head → subluxation or "
    "dislocation, especially with overhead activities.",
    "<b>Serratus Anterior / Trapezius Palsy:</b> Loss of scapular upward rotation couple → "
    "scapular winging → reduced overhead reach (cannot complete 180° elevation).",
    "<b>Post-operative Significance:</b> Rotator cuff repair rehabilitation must restore the "
    "force couple before progressive strengthening is applied.",
]))
story.append(sp(10))

# DIAGRAM 3
story.append(section_header("Diagram: Force Couple Mechanism", color=GREEN))
story.append(sp(4))
story.append(ForceCoupleD(width=W-72, height=270))
story.append(Paragraph(
    "Figure 3. Left — GH joint coronal plane force couple: deltoid (red, superior) vs. rotator cuff "
    "(blue, inferior + compressive), producing pure rotation (green arc). "
    "Right — scapular force couple: upper trapezius (red), lower trapezius (blue), "
    "serratus anterior (green) produce scapular upward rotation.",
    caption_style))
story.append(sp(10))

# ── Summary table ─────────────────────────────────────────────
story.append(HRFlowable(width="100%", thickness=2, color=DARK_BLUE, spaceAfter=8))
story.append(Paragraph("Quick-Reference Summary", make_style("SUM",
    fontSize=13, fontName="Helvetica-Bold", textColor=DARK_BLUE,
    spaceAfter=6, alignment=TA_CENTER)))
story.append(sp(4))
sum_data = [
    [Paragraph("<b>Topic</b>", make_style("SH1", fontSize=9.5, fontName="Helvetica-Bold", textColor=colors.white)),
     Paragraph("<b>Key Fact</b>", make_style("SH2", fontSize=9.5, fontName="Helvetica-Bold", textColor=colors.white))],
    [Paragraph("Total shoulder abduction", body_style),
     Paragraph("180° = GH 120° (2/3) + ST 60° (1/3)", body_style)],
    [Paragraph("Scapulohumeral rhythm", body_style),
     Paragraph("2:1 ratio — 2° GH : 1° ST for every 3° total abduction", body_style)],
    [Paragraph("Phase 1 (0–30°)", body_style),
     Paragraph("Pure GH motion; scapula stabilises", body_style)],
    [Paragraph("Phase 2 (30–90°)", body_style),
     Paragraph("2:1 rhythm established; rotator cuff + deltoid active", body_style)],
    [Paragraph("Phase 3 (90–180°)", body_style),
     Paragraph("Increased ST; external rotation of humerus; AC/SC contribute", body_style)],
    [Paragraph("SC joint classification", body_style),
     Paragraph("Diarthrodial saddle synovial joint with intra-articular fibrocartilage disk", body_style)],
    [Paragraph("SC joint movements", body_style),
     Paragraph("Elevation/depression (~35°), protraction/retraction (~30°), axial rotation (~50°)", body_style)],
    [Paragraph("Force couple (GH)", body_style),
     Paragraph("Deltoid (upward shear) vs. infraspinatus/teres minor/subscapularis (inferior+compressive) → pure rotation", body_style)],
    [Paragraph("Force couple (scapular)", body_style),
     Paragraph("Upper trapezius vs. lower trapezius + serratus anterior → scapular upward rotation", body_style)],
    [Paragraph("Disruption consequence", body_style),
     Paragraph("Superior migration of humeral head → subacromial impingement, painful arc, instability", body_style)],
]
st = make_table(sum_data[0], sum_data[1:], [(W-72)*0.32, (W-72)*0.68])
story.append(st)
story.append(sp(8))
story.append(Paragraph(
    "Sources: Rheumatology 2022 (Elsevier), p.700 | Rockwood & Green's Fractures in Adults 10th Ed 2025, p.1272",
    make_style("Src", parent="Normal", fontSize=8, textColor=DARK_GREY,
               fontName="Helvetica-Oblique", alignment=TA_CENTER)))

# ── Build PDF ─────────────────────────────────────────────────
doc.build(story)
print("PDF created:", OUTPUT)
Running Command

cd /tmp/workspace/shoulder-biomechanics && python3 create_pdf.py 2>&1

Editing File

~/shoulder-biomechanics/create_pdf.py

Successfully replaced 1 instance(s) of the string in '/tmp/workspace/shoulder-biomechanics/create_pdf.py'
Running Command

cd /tmp/workspace/shoulder-biomechanics && python3 create_pdf.py 2>&1

Running Command

ls -lh /tmp/workspace/shoulder-biomechanics/Shoulder_Biomechanics_Reference.pdf

Your PDF reference guide is ready:

Shoulder Biomechanics Reference

PDF Document · PDF


Here's what's inside the 3-page guide:
Page 1 — Q1: Shoulder Abduction 0–180°
  • Colour-coded phase table (Phase 1/2/3 with joint roles)
  • GH vs. ST contribution comparison table (120° + 60°)
  • Scapulohumeral rhythm definition and 2:1 ratio explanation
  • SC & AC joint movements table
  • Rotator cuff stability mechanisms (3-mechanism table with evidence)
  • Diagram 1: Arm positions at 0°/90°/180° with phase bar chart showing GH vs. ST split and 2:1 ratio callout box
Page 2 — Q2: Sternoclavicular Joint
  • Classification and articular surfaces table
  • Ligaments table (all 4 ligaments with primary functions)
  • Available movements table with ranges
  • Functional importance summary box
  • Diagram 2: Cross-section schematic showing manubrium, intra-articular disk, clavicle, all 4 ligaments labelled with colour arrows, movements panel, and functional importance box
Page 3 — Q3: Force Couple Mechanism
  • Definition box
  • GH force couple muscles table (deltoid vs. rotator cuff)
  • Scapular force couple muscles table (upper/lower trap + serratus)
  • Mechanism of action explanation
  • Clinical significance box (5 conditions)
  • Diagram 3: Humeral head with opposing deltoid (red) and rotator cuff (blue) arrows showing pure rotation; scapular force couple with all 3 muscles labelled
  • Quick-Reference Summary table covering all 3 questions
This is a shared conversation. Sign in to Orris to start your own chat.