Spino pelvic relation in THR

Reading File
Finding Sources
Finding Sources
Searching PubMed

spinopelvic relationship total hip arthroplasty

Reading File
Reading File
Searching Images

spinopelvic relationship total hip arthroplasty pelvic tilt acetabular cup

This set of four lateral radiographs (labeled A-D) illustrates the dynamic relationship between pelvic orientation and total hip arthroplasty (THA) component position in patients with varying spinopelvic mobility. Images A and B depict a patient with spinopelvic hypermobility in standing (A) and sitting (B) positions. Note the significant change in pelvic tilt (PT) and acetabular anteinclination (AI) when moving from standing to sitting, characterized by a more vertical cup orientation in the seated position. Images C and D depict a patient with spinopelvic stiffness in standing (C) and sitting (D) positions. In the stiff patient, the pelvic orientation remains relatively fixed, resulting in a more horizontal cup position in the sitting phase compared to the hypermobile patient. Key measurements shown include Pelvic Tilt (PT), the angle between a vertical line and a line from the center of the femoral head to the midpoint of the sacral endplate, and Anteinclination (AI), representing a combined parameter of cup anteversion and inclination. These images demonstrate how limited lumbar and pelvic movement (stiffness) can adversely affect functional component position during postural transitions.

This set of four lateral radiographs (labeled A-D) illustrates the dynamic relationship between pelvic orientation and total hip arthroplasty (THA) component position in patients with varying spinopelvic mobility. Images A and B depict a patient with spinopelvic hypermobility in standing (A) and sitting (B) positions. Note the significant change in pelvic tilt (PT) and acetabular anteinclination (AI) when moving from standing to sitting, characterized by a more vertical cup orientation in the seated position. Images C and D depict a patient with spinopelvic stiffness in standing (C) and sitting (D) positions. In the stiff patient, the pelvic orientation remains relatively fixed, resulting in a more horizontal cup position in the sitting phase compared to the hypermobile patient. Key measurements shown include Pelvic Tilt (PT), the angle between a vertical line and a line from the center of the femoral head to the midpoint of the sacral endplate, and Anteinclination (AI), representing a combined parameter of cup anteversion and inclination. These images demonstrate how limited lumbar and pelvic movement (stiffness) can adversely affect functional component position during postural transitions.

This lateral standing radiograph of the full spine and pelvis illustrates two distinct methods for measuring pelvic tilt in the sagittal plane. The image depicts a patient with a total hip arthroplasty (THA), showing the femoral stem and acetabular cup in situ. Method 1 (Arthroplasty perspective) is marked by line 1, defining the Anterior Pelvic Plane Tilt (APPt) as the angle between the coronal plane and a line connecting the anterior superior iliac spine (ASIS) and the pubic symphysis. Method 2 (Spine surgeon perspective), marked by line 2, illustrates Spinopelvic Tilt (sPT), defined as the angle between a vertical reference line and a line from the center of the femoral heads to the midpoint of the S1 sacral endplate. The radiograph demonstrates clinical landmarks including the thoracic kyphosis, lumbar spine, sacrum, and iliac crests. These measurements are essential for assessing spinopelvic mobility and sagittal balance, particularly in preoperative planning for hip arthroplasty to minimize dislocation risk related to spinal alignment and pelvic retroversion.

This lateral standing radiograph of the full spine and pelvis illustrates two distinct methods for measuring pelvic tilt in the sagittal plane. The image depicts a patient with a total hip arthroplasty (THA), showing the femoral stem and acetabular cup in situ. Method 1 (Arthroplasty perspective) is marked by line 1, defining the Anterior Pelvic Plane Tilt (APPt) as the angle between the coronal plane and a line connecting the anterior superior iliac spine (ASIS) and the pubic symphysis. Method 2 (Spine surgeon perspective), marked by line 2, illustrates Spinopelvic Tilt (sPT), defined as the angle between a vertical reference line and a line from the center of the femoral heads to the midpoint of the S1 sacral endplate. The radiograph demonstrates clinical landmarks including the thoracic kyphosis, lumbar spine, sacrum, and iliac crests. These measurements are essential for assessing spinopelvic mobility and sagittal balance, particularly in preoperative planning for hip arthroplasty to minimize dislocation risk related to spinal alignment and pelvic retroversion.

A composite of three X-ray radiographs demonstrating the assessment of spinopelvic parameters and total hip replacement (THR) implant positioning. (A) Anteroposterior (AP) coronal radiograph of the pelvis showing a right-sided total hip arthroplasty. Yellow annotation lines indicate the measurement of acetabular cup inclination and anteversion relative to the transischial line. (B) and (C) are sagittal radiographs showing the lumbosacral spine and pelvis in sitting and standing positions, respectively. These images are annotated with surgical planning software lines (SurgiMap) to measure dynamic parameters: Lumbar Lordosis (LL), Sacral Slope (SS), Pelvic Incidence (PI), Spinopelvic Tilt (PT), Anterior Pelvic Plane Tilt (APPt), Pelvic Femoral Angle (PFA), and Acetabular Anteinclination (AI). The comparison illustrates how the lumbar spine flattens and the pelvis rotates posteriorly when moving from a standing to a sitting position, which is critical for evaluating risk factors like prosthetic impingement or dislocation in orthopedic follow-up.

A composite of three X-ray radiographs demonstrating the assessment of spinopelvic parameters and total hip replacement (THR) implant positioning. (A) Anteroposterior (AP) coronal radiograph of the pelvis showing a right-sided total hip arthroplasty. Yellow annotation lines indicate the measurement of acetabular cup inclination and anteversion relative to the transischial line. (B) and (C) are sagittal radiographs showing the lumbosacral spine and pelvis in sitting and standing positions, respectively. These images are annotated with surgical planning software lines (SurgiMap) to measure dynamic parameters: Lumbar Lordosis (LL), Sacral Slope (SS), Pelvic Incidence (PI), Spinopelvic Tilt (PT), Anterior Pelvic Plane Tilt (APPt), Pelvic Femoral Angle (PFA), and Acetabular Anteinclination (AI). The comparison illustrates how the lumbar spine flattens and the pelvis rotates posteriorly when moving from a standing to a sitting position, which is critical for evaluating risk factors like prosthetic impingement or dislocation in orthopedic follow-up.

This series of three anteroposterior (AP) pelvic radiographs demonstrates the impact of pelvic tilt and posture on total hip arthroplasty (THA) component orientation in a 64-year-old female. Image (a) shows the pelvis in a supine position, with the left THA acetabular cup displaying an inclination of 45° and anteversion of 25°. Image (b) shows the same patient in a standing position, where a marked posterior (backward) pelvic tilt is visible, causing a secondary increase in cup inclination to 59° and anteversion to 42°. This demonstrates the dynamic relationship between spinal-pelvic alignment and prosthetic orientation. Image (c) provides a clinical complication example: a post-THA radiograph showing an anterior dislocation of the left hip prosthesis, where the femoral head component is completely displaced from the acetabular cup. These images serve as educational material for orthopedic surgery, specifically focusing on preoperative planning, the effects of sagittal pelvic mobility on implant stability, and postoperative complication monitoring.

This series of three anteroposterior (AP) pelvic radiographs demonstrates the impact of pelvic tilt and posture on total hip arthroplasty (THA) component orientation in a 64-year-old female. Image (a) shows the pelvis in a supine position, with the left THA acetabular cup displaying an inclination of 45° and anteversion of 25°. Image (b) shows the same patient in a standing position, where a marked posterior (backward) pelvic tilt is visible, causing a secondary increase in cup inclination to 59° and anteversion to 42°. This demonstrates the dynamic relationship between spinal-pelvic alignment and prosthetic orientation. Image (c) provides a clinical complication example: a post-THA radiograph showing an anterior dislocation of the left hip prosthesis, where the femoral head component is completely displaced from the acetabular cup. These images serve as educational material for orthopedic surgery, specifically focusing on preoperative planning, the effects of sagittal pelvic mobility on implant stability, and postoperative complication monitoring.

This composite of two diagnostic X-ray radiographs displays postoperative evaluations in an orthopedic context. Panel (a) is an anteroposterior radiograph of the pelvis and hips, demonstrating a right total hip arthroplasty (THA). The prosthesis consists of a cementless acetabular cup reinforced with screw fixation and a tapered wedge-type cementless femoral stem. The contralateral hip appears intact for comparison. Panel (b) shows a full-length lateral upright spine radiograph used for sagittal balance assessment. Annotated measurements illustrate global sagittal alignment, including the sagittal vertical axis (SVA), lumbar lordosis (LL) angle, and pelvic tilt (PT). The imaging highlights clinical findings of decreased lumbar lordosis (8°) and anterior trunk tilt (SVA 103 mm), indicating significant sagittal plane imbalance. These images demonstrate the relationship between hip replacement surgery and spinopelvic parameters, commonly analyzed in patients with complex degenerative conditions or Rapidly Destructive Coxopathy (RDC).

This composite of two diagnostic X-ray radiographs displays postoperative evaluations in an orthopedic context. Panel (a) is an anteroposterior radiograph of the pelvis and hips, demonstrating a right total hip arthroplasty (THA). The prosthesis consists of a cementless acetabular cup reinforced with screw fixation and a tapered wedge-type cementless femoral stem. The contralateral hip appears intact for comparison. Panel (b) shows a full-length lateral upright spine radiograph used for sagittal balance assessment. Annotated measurements illustrate global sagittal alignment, including the sagittal vertical axis (SVA), lumbar lordosis (LL) angle, and pelvic tilt (PT). The imaging highlights clinical findings of decreased lumbar lordosis (8°) and anterior trunk tilt (SVA 103 mm), indicating significant sagittal plane imbalance. These images demonstrate the relationship between hip replacement surgery and spinopelvic parameters, commonly analyzed in patients with complex degenerative conditions or Rapidly Destructive Coxopathy (RDC).

Reading File
Reading File
Finding Sources
Finding Sources
Reading File
Reading File
Reading File
Loading Image
Loading Image
I now have comprehensive material to deliver a thorough answer. Let me compile everything.

Spinopelvic Relationship in Total Hip Replacement (THR)

The spinopelvic relationship is one of the most important yet historically underappreciated concepts in THR planning. It refers to how the spine, pelvis, and hip joints function as a kinematic chain - and how abnormalities in one segment alter the functional orientation of implants in another.

Key Spinopelvic Parameters

ParameterDefinitionNormal Value
Pelvic Incidence (PI)Angle between a perpendicular to the S1 endplate and a line from midpoint of S1 endplate to the center of femoral heads. Fixed anatomical parameter.~50-55°
Sacral Slope (SS)Angle between the S1 superior endplate and a horizontal reference line~40°
Pelvic Tilt (PT)Angle between a vertical line and a line from femoral head center to midpoint of S1 endplate~12-15°
Lumbar Lordosis (LL)Sagittal curve of the lumbar spine (L1-S1)~50-60°
PI-LL mismatchDifference; >10° is abnormal (flatback deformity)<10°
The fundamental equation: PI = PT + SS (a fixed anatomical relationship)

Why It Matters for THR: The "Hip-Spine" Problem

A meta-analysis cited in Campbell's Operative Orthopaedics 15th Ed found that a history of spinal fusion imparts a 2-fold risk of early hip dislocation and a >3-fold risk of revision after THR. Critically, most early dislocations occur even with acetabular components placed within the traditional "safe zone" (Lewinnek zone: 40±10° inclination, 15±10° anteversion) - because this zone was defined in static, anatomical terms without accounting for dynamic pelvic motion.

The Concept of Functional vs Anatomical Cup Position

  • Anatomic Pelvic Plane (APP): defined by the two ASIS and the pubic symphysis on a lateral radiograph. Used traditionally for cup targeting.
  • Functional Pelvic Plane (FPP): the actual orientation of the pelvis during standing and weight-bearing activities - differs from APP in patients with spinal deformity or fusion.
When the lumbar spine has normal mobility, the pelvis tilts posteriorly during sitting, which increases functional cup anteversion by ~15.6° (approximately 0.7-0.8° per degree of pelvic tilt). This is protective - it reduces anterior impingement in flexion.
When the spine is stiff or fused, this compensation is lost.

Dynamic Pelvic Mechanics: Standing vs Sitting

Normal transition from standing to sitting:
  • Posterior pelvic tilt occurs
  • Lumbar lordosis (LL) decreases
  • Sacral slope (SS) flattens
  • Normal change in SS: 11-30°
Spinopelvic stiffness = change in SS of ≤10° from standing to sitting.
Dynamic spinopelvic relationship - hypermobile vs stiff spine in standing and sitting positions, showing pelvic tilt (PT) and acetabular anteinclination (AI) changes
Panels A-B: spinopelvic hypermobility - large pelvic tilt change from standing (A) to sitting (B). Panels C-D: spinopelvic stiffness - fixed pelvic orientation with minimal change, increasing anterior impingement risk in sitting.

Two Patterns of Spinal Pathology and Their Effects

1. Flatback Deformity (PI-LL mismatch >10°)

  • Caused by degenerative disc disease, failed spinal fusion, iatrogenic flatback
  • Results in chronic posterior pelvic tilt (retroversion)
  • Functionally, the acetabular cup appears more anteverted than its anatomical position
  • Risk: anterior dislocation (excessive anteversion in standing)

2. Spinopelvic Stiffness (SS change ≤10°)

  • Caused by lumbar fusion, severe degenerative spondylosis
  • Normal posterior pelvic tilt with sitting is lost
  • The hip must flex more to achieve seated position
  • Risk: anterior impingement and posterior dislocation when sitting/arising from chair
Measurement of pelvic tilt from the arthroplasty and spine surgeon perspectives - lateral spine radiograph with THA in situ
Line 1 = Anterior Pelvic Plane Tilt (APPt) used by arthroplasty surgeons; Line 2 = Spinopelvic Tilt (sPT) used by spine surgeons. Both are required for complete assessment.

Hip-Spine Classification and Cup Anteversion Targets

Based on Vigdorchik et al., the following classification guides cup anteversion targeting (Campbell's Operative Orthopaedics 15th Ed 2026):
GroupClassificationPathologyAnteversion Target
1ANormal alignment + Normal mobilityAPP ≈ FPP; normal kinematics20-25° (to APP)
1BNormal alignment + Stiff spine (SS change ≤10°)APP ≈ FPP but no posterior tilt compensation30° (to FPP) - higher target to prevent posterior dislocation when sitting
2AFlatback deformity (PI-LL >10°) + Normal mobilityFPP differs from APP due to posterior pelvic tilt at baseline25-30° (to FPP)
2BFlatback deformity + Stiff spineBoth problems combined - FPP is retroverted AND stiff30° (to FPP) - must account for deformity AND stiffness
APP = anatomic pelvic plane; FPP = functional pelvic plane; LL = lumbar lordosis; PI = pelvic incidence
  • From Campbell's Operative Orthopaedics 15th Ed 2026, p. 254

Preoperative Assessment Protocol

For all patients undergoing THR, assess:
  1. History of prior lumbar fusion or spinal deformity
  2. Symptoms of lumbar stenosis, flatback, or sagittal imbalance
For patients with spinal history or stiffness, obtain:
  • Lateral lumbar/pelvis radiograph in standing position
  • Lateral lumbar/pelvis radiograph in forward-flexed sitting position
  • Standing AP pelvis
  • EOS imaging (provides 3D spinopelvic parameters with low radiation)
Measure: SS in both positions to calculate SS change and classify spinopelvic mobility.

Surgical Implications and Techniques

  1. Cup anteversion must be adjusted based on the classification above - the traditional 15° Lewinnek target is insufficient for hip-spine patients.
  2. Dual mobility cups are recommended for patients with prior lumbar fusion (associated with significantly reduced dislocation rates in registry studies - PMID 38090893).
  3. Robotic-assisted THR improves accuracy of functional safe zone targeting, especially relevant in hip-spine patients where component positioning errors have greater consequences (Rice et al., 2024).
  4. Constrained liners may be necessary in severe cases where spinopelvic imbalance cannot be corrected by cup positioning alone.
  5. Patients with prior lumbar fusion > 2 levels are at highest risk and may warrant combined spine-hip surgical planning.

Clinical Scenarios

Scenario 1 - Posterior dislocation in sitting: Patient with prior multi-level lumbar fusion (stiff spine) undergoes THR with standard cup anteversion (15-20°). When sitting, the pelvis cannot tilt posteriorly - the hip must achieve full ROM. The cup does not gain its normal functional anteversion increment, and the femoral neck impinges posteriorly on the cup rim → posterior dislocation.
Scenario 2 - Anterior dislocation in standing/extension: Patient with longstanding degenerative flatback (PI-LL mismatch) has a chronically retroverted pelvis. Standard cup anteversion on the APP translates to excessive functional anteversion on the FPP when standing → anterior impingement and dislocation.

Key References

  • Campbell's Operative Orthopaedics 15th Ed 2026, Chapter 4: "The Hip-Spine Relationship," pp. 253-254
  • Dennis DA et al. (2024) - Total Hip Arthroplasty and the Spinopelvic Relationship: What's the Latest! Instr Course Lect
  • Di Martino A et al. (2024) - Hip-spine relationship: clinical evidence and biomechanical issues. Arch Orthop Trauma Surg
  • Kouyoumdjian P (2024) - How the hip-spine relationship influences total hip arthroplasty. Orthop Traumatol Surg Res
  • Rice SJ et al. (2024) - Robotic-assisted Total Hip Arthroplasty and Spinopelvic Parameters. Hip Pelvis

Bottom line: The spine and hip are not independent - they function as a kinematic chain. Spinal stiffness or deformity alters dynamic cup orientation, making the standard Lewinnek safe zone unreliable. Preoperative spinopelvic assessment (standing + sitting lateral radiographs, SS change, PI-LL mismatch) and adjusting cup anteversion using the functional pelvic plane rather than the anatomic pelvic plane is now standard of care for "hip-spine" patients, with dual mobility cups as the implant of choice in high-risk cases.

Make a presentation summarizing hip-spine classification and surgical recommendations

Reading File
Reading File
Reading File
Running Command

mkdir -p /tmp/workspace/hip-spine-pptx && ls /tmp/workspace/hip-spine-pptx/

Writing File

~/hip-spine-pptx/build_pptx.py

"""
Hip-Spine Classification & Surgical Recommendations in THR
Professional PPTX presentation builder
"""

from pptx import Presentation
from pptx.util import Inches, Pt, Emu
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN, MSO_ANCHOR
from pptx.enum.shapes import MSO_SHAPE
from io import BytesIO
import json, subprocess, base64

# ── Colour palette ──────────────────────────────────────────────
NAVY      = RGBColor(0x0D, 0x2B, 0x55)   # deep navy – headers / title bg
TEAL      = RGBColor(0x00, 0x7B, 0x8E)   # teal accent
GOLD      = RGBColor(0xE8, 0xA8, 0x00)   # gold accent
LTBLUE    = RGBColor(0xD6, 0xEA, 0xF3)   # light blue – slide bg
WHITE     = RGBColor(0xFF, 0xFF, 0xFF)
DARK_GREY = RGBColor(0x33, 0x33, 0x33)
MID_GREY  = RGBColor(0x66, 0x66, 0x66)
RED       = RGBColor(0xC0, 0x39, 0x2B)
GREEN     = RGBColor(0x1A, 0x7A, 0x3C)
ORANGE    = RGBColor(0xE6, 0x7E, 0x22)

# group colours
G1A_COL = RGBColor(0x1A, 0x7A, 0x3C)   # green
G1B_COL = RGBColor(0xE6, 0x7E, 0x22)   # orange
G2A_COL = RGBColor(0x21, 0x6B, 0xA0)   # blue
G2B_COL = RGBColor(0xC0, 0x39, 0x2B)   # red

W = Inches(13.333)
H = Inches(7.5)

# ── Helper functions ─────────────────────────────────────────────

def new_prs():
    prs = Presentation()
    prs.slide_width  = W
    prs.slide_height = H
    return prs

def blank_slide(prs):
    return prs.slides.add_slide(prs.slide_layouts[6])

def bg_rect(slide, color, left=0, top=0, width=None, height=None):
    """Full or partial background rectangle."""
    w = width  if width  is not None else W
    h = height if height is not None else H
    shp = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE,
                                  Emu(left), Emu(top), Emu(w), Emu(h))
    shp.fill.solid()
    shp.fill.fore_color.rgb = color
    shp.line.fill.background()
    shp.shadow.inherit = False
    return shp

def textbox(slide, text, left, top, width, height,
            font_name="Calibri", font_size=18, bold=False, italic=False,
            color=DARK_GREY, align=PP_ALIGN.LEFT, wrap=True,
            v_anchor=MSO_ANCHOR.TOP, margins=(0,0,0,0)):
    tb = slide.shapes.add_textbox(Emu(left), Emu(top), Emu(width), Emu(height))
    tf = tb.text_frame
    tf.word_wrap = wrap
    tf.vertical_anchor = v_anchor
    tf.margin_left   = Pt(margins[0])
    tf.margin_right  = Pt(margins[1])
    tf.margin_top    = Pt(margins[2])
    tf.margin_bottom = Pt(margins[3])
    p = tf.paragraphs[0]
    p.alignment = align
    run = p.add_run()
    run.text = text
    run.font.name  = font_name
    run.font.size  = Pt(font_size)
    run.font.bold  = bold
    run.font.italic= italic
    run.font.color.rgb = color
    return tb, tf

def add_para(tf, text, font_size=16, bold=False, italic=False,
             color=DARK_GREY, align=PP_ALIGN.LEFT, level=0, indent_pt=0):
    p = tf.add_paragraph()
    p.alignment = align
    p.level = level
    if indent_pt:
        from pptx.oxml.ns import qn
        from lxml import etree
        pPr = p._pPr
        if pPr is None:
            pPr = p._p.get_or_add_pPr()
        pPr.set('indent', str(int(-indent_pt * 12700)))
        pPr.set('marL',   str(int( indent_pt * 12700)))
    run = p.add_run()
    run.text = text
    run.font.size  = Pt(font_size)
    run.font.bold  = bold
    run.font.color.rgb = color
    return p

def colored_rect_with_text(slide, rect_color, text, left, top, width, height,
                            font_size=14, bold=True, text_color=WHITE,
                            align=PP_ALIGN.CENTER):
    shp = slide.shapes.add_shape(MSO_SHAPE.ROUNDED_RECTANGLE,
                                  Emu(left), Emu(top), Emu(width), Emu(height))
    shp.fill.solid()
    shp.fill.fore_color.rgb = rect_color
    shp.line.fill.background()
    shp.shadow.inherit = False
    tf = shp.text_frame
    tf.word_wrap = True
    tf.vertical_anchor = MSO_ANCHOR.MIDDLE
    tf.margin_left = tf.margin_right = tf.margin_top = tf.margin_bottom = Pt(4)
    p = tf.paragraphs[0]
    p.alignment = align
    run = p.add_run()
    run.text = text
    run.font.name  = "Calibri"
    run.font.size  = Pt(font_size)
    run.font.bold  = bold
    run.font.color.rgb = text_color
    return shp

def divider_line(slide, y_emu, color=TEAL, width_pct=0.9):
    from pptx.enum.shapes import MSO_CONNECTOR
    x0 = int(W * (1 - width_pct) / 2)
    x1 = int(W * (1 - (1 - width_pct) / 2))
    ln = slide.shapes.add_connector(MSO_CONNECTOR.STRAIGHT,
                                     Emu(x0), Emu(y_emu), Emu(x1), Emu(y_emu))
    ln.line.color.rgb = color
    ln.line.width = Pt(1.5)

def slide_header(slide, title, subtitle=None, title_size=32):
    """Navy header bar at top with white title text."""
    bg_rect(slide, LTBLUE)
    # header bar
    bg_rect(slide, NAVY, left=0, top=0, width=int(W), height=Inches(1.15))
    # gold accent strip
    bg_rect(slide, GOLD, left=0, top=int(Inches(1.15)), width=int(W), height=int(Inches(0.06)))
    # title
    textbox(slide, title,
            left=Inches(0.5), top=Inches(0.12),
            width=Inches(12.3), height=Inches(0.95),
            font_size=title_size, bold=True, color=WHITE,
            align=PP_ALIGN.LEFT, v_anchor=MSO_ANCHOR.MIDDLE,
            margins=(4, 4, 2, 2))
    if subtitle:
        textbox(slide, subtitle,
                left=Inches(0.5), top=Inches(1.2),
                width=Inches(12.3), height=Inches(0.4),
                font_size=16, bold=False, italic=True, color=MID_GREY,
                align=PP_ALIGN.LEFT)

def footer(slide, text="Campbell's Operative Orthopaedics 15th Ed 2026 | Vigdorchik et al."):
    bg_rect(slide, NAVY, left=0, top=int(H - Inches(0.35)), width=int(W), height=int(Inches(0.35)))
    textbox(slide, text,
            left=Inches(0.4), top=int(H - Inches(0.34)),
            width=Inches(12.5), height=Inches(0.32),
            font_size=9, color=WHITE, align=PP_ALIGN.LEFT,
            v_anchor=MSO_ANCHOR.MIDDLE, margins=(2,2,0,0))


# ── SLIDE BUILDERS ───────────────────────────────────────────────

def slide_01_title(prs):
    """Title slide"""
    slide = blank_slide(prs)
    # full navy bg
    bg_rect(slide, NAVY)
    # gold bottom strip
    bg_rect(slide, GOLD, left=0, top=int(H - Inches(0.6)), width=int(W), height=int(Inches(0.6)))
    # teal accent left bar
    bg_rect(slide, TEAL, left=0, top=0, width=int(Inches(0.55)), height=int(H))
    # big title
    textbox(slide, "Hip-Spine Relationship in\nTotal Hip Replacement",
            left=Inches(0.9), top=Inches(1.8),
            width=Inches(11), height=Inches(2.4),
            font_size=44, bold=True, color=WHITE,
            align=PP_ALIGN.LEFT, v_anchor=MSO_ANCHOR.MIDDLE)
    # subtitle
    textbox(slide, "Classification, Biomechanics & Surgical Recommendations",
            left=Inches(0.9), top=Inches(4.1),
            width=Inches(11), height=Inches(0.55),
            font_size=22, bold=False, italic=True, color=GOLD,
            align=PP_ALIGN.LEFT)
    # source
    textbox(slide, "Based on Campbell's Operative Orthopaedics 15th Ed 2026\nVigdorchik JM et al. | Dennis DA et al. 2024",
            left=Inches(0.9), top=Inches(4.75),
            width=Inches(11), height=Inches(0.7),
            font_size=14, color=LTBLUE, align=PP_ALIGN.LEFT)


def slide_02_overview(prs):
    """Why it matters"""
    slide = blank_slide(prs)
    slide_header(slide, "Why the Hip-Spine Relationship Matters")
    footer(slide)

    # 3 big stat boxes
    stats = [
        ("2x", "increased risk of\nearly hip dislocation\nwith prior spinal fusion", RED),
        (">3x", "increased risk of\nrevision THR in\nspine-fused patients", ORANGE),
        ("Lewinnek\nZone Fails", "Most early dislocations occur\nwith components placed IN\nthe traditional safe zone", TEAL),
    ]
    bx_w = Inches(3.7)
    bx_h = Inches(2.4)
    tops = int(Inches(1.8))
    lefts = [Inches(0.5), Inches(4.8), Inches(9.1)]
    for (num, desc, col), lft in zip(stats, lefts):
        shp = slide.shapes.add_shape(MSO_SHAPE.ROUNDED_RECTANGLE,
                                      Emu(lft), Emu(tops), Emu(bx_w), Emu(bx_h))
        shp.fill.solid()
        shp.fill.fore_color.rgb = col
        shp.line.fill.background()
        shp.shadow.inherit = False
        tf = shp.text_frame
        tf.word_wrap = True
        tf.vertical_anchor = MSO_ANCHOR.MIDDLE
        tf.margin_left = tf.margin_right = Pt(8)
        tf.margin_top = tf.margin_bottom = Pt(6)
        p0 = tf.paragraphs[0]
        p0.alignment = PP_ALIGN.CENTER
        r0 = p0.add_run()
        r0.text = num
        r0.font.size = Pt(36)
        r0.font.bold = True
        r0.font.color.rgb = WHITE
        p1 = tf.add_paragraph()
        p1.alignment = PP_ALIGN.CENTER
        r1 = p1.add_run()
        r1.text = desc
        r1.font.size = Pt(14)
        r1.font.bold = False
        r1.font.color.rgb = WHITE

    # Concept box
    shp2 = slide.shapes.add_shape(MSO_SHAPE.ROUNDED_RECTANGLE,
                                   Inches(0.5), Inches(4.45), Inches(12.3), Inches(2.5))
    shp2.fill.solid()
    shp2.fill.fore_color.rgb = RGBColor(0xE8, 0xF4, 0xF8)
    shp2.line.color.rgb = TEAL
    shp2.line.width = Pt(1.5)
    shp2.shadow.inherit = False
    tf2 = shp2.text_frame
    tf2.word_wrap = True
    tf2.vertical_anchor = MSO_ANCHOR.TOP
    tf2.margin_left = tf2.margin_right = Pt(12)
    tf2.margin_top = tf2.margin_bottom = Pt(8)
    p = tf2.paragraphs[0]
    p.alignment = PP_ALIGN.LEFT
    r = p.add_run()
    r.text = "Core Concept:"
    r.font.size = Pt(16)
    r.font.bold = True
    r.font.color.rgb = NAVY
    add_para(tf2, "The spine, pelvis, and hip form a kinematic chain. "
             "Normal sitting requires posterior pelvic tilt (11-30 deg sacral slope change), "
             "which increases functional cup anteversion by ~15.6 deg. "
             "Spinal stiffness or deformity disrupts this compensation, "
             "changing the FUNCTIONAL orientation of the acetabular component regardless of its anatomical position.",
             font_size=15, color=DARK_GREY)


def slide_03_parameters(prs):
    """Key Spinopelvic Parameters"""
    slide = blank_slide(prs)
    slide_header(slide, "Key Spinopelvic Parameters")
    footer(slide)

    params = [
        ("Pelvic Incidence (PI)", "Angle: perpendicular to S1 endplate midpoint -> femoral head center.\nFIXED anatomical constant. ~50-55 deg.\nPI = PT + SS  (fundamental equation)", NAVY),
        ("Sacral Slope (SS)", "Angle between S1 superior endplate and horizontal.\nNormal: ~40 deg standing.\nFalls 11-30 deg on sitting (normal).\nStiffness: change <=10 deg", TEAL),
        ("Pelvic Tilt (PT)", "Angle: vertical line vs line from femoral head to S1 midpoint.\nNormal: ~12-15 deg.\nIncreases with posterior pelvic rotation (sitting)", G2A_COL),
        ("PI-LL Mismatch", "Pelvic Incidence minus Lumbar Lordosis.\nNormal: <10 deg.\n>10 deg = FLATBACK DEFORMITY.\nCauses chronic posterior pelvic tilt", G2B_COL),
        ("APP vs FPP", "APP = Anatomic Pelvic Plane (ASIS + pubic symphysis).\nFPP = Functional Pelvic Plane (actual standing orientation).\nIn flatback: FPP =/= APP -- cup appears more anteverted than measured!", G1B_COL),
    ]

    row_h = Inches(0.94)
    top_start = Inches(1.35)
    for i, (title, desc, col) in enumerate(params):
        y = int(top_start) + i * int(row_h)
        # colour tab
        shp = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE,
                                      Inches(0.35), Emu(y + int(Inches(0.05))),
                                      Inches(0.18), Emu(int(row_h) - int(Inches(0.1))))
        shp.fill.solid()
        shp.fill.fore_color.rgb = col
        shp.line.fill.background()
        shp.shadow.inherit = False
        # title
        textbox(slide, title,
                left=Inches(0.65), top=Emu(y + int(Inches(0.05))),
                width=Inches(3.1), height=Emu(int(row_h) - int(Inches(0.1))),
                font_size=14, bold=True, color=col,
                align=PP_ALIGN.LEFT, v_anchor=MSO_ANCHOR.MIDDLE)
        # description
        textbox(slide, desc,
                left=Inches(3.85), top=Emu(y + int(Inches(0.05))),
                width=Inches(9.1), height=Emu(int(row_h) - int(Inches(0.1))),
                font_size=12, bold=False, color=DARK_GREY,
                align=PP_ALIGN.LEFT, v_anchor=MSO_ANCHOR.MIDDLE, wrap=True)
        # thin divider
        if i < len(params) - 1:
            divider_line(slide, y + int(row_h), color=RGBColor(0xCC,0xCC,0xCC), width_pct=0.95)


def slide_04_classification(prs):
    """Hip-Spine Classification Table"""
    slide = blank_slide(prs)
    slide_header(slide, "Hip-Spine Classification System (Vigdorchik et al.)")
    footer(slide)

    groups = [
        {
            "group": "1A",
            "label": "Normal Alignment\n+ Normal Mobility",
            "patho": "Normal anatomy. APP = FPP.\nNormal spinopelvic kinematics.\nSS change >10 deg from stand-to-sit.",
            "target": "Anteversion: 20-25 deg\n(relative to APP)",
            "risk": "Low dislocation risk",
            "col": G1A_COL,
        },
        {
            "group": "1B",
            "label": "Normal Alignment\n+ STIFF Spine",
            "patho": "APP = FPP BUT spine cannot tilt posteriorly.\nSS change <=10 deg on sit.\nHip must flex further to sit -- posterior impingement.",
            "target": "Anteversion: 30 deg\n(relative to FPP/standing AP)\nHigher target -- protect vs posterior dislocation",
            "risk": "Posterior dislocation risk in sitting",
            "col": G1B_COL,
        },
        {
            "group": "2A",
            "label": "FLATBACK Deformity\n+ Normal Mobility",
            "patho": "PI-LL >10 deg. Chronic posterior pelvic tilt.\nFPP =/= APP.\nFunctional cup anteversion > anatomical anteversion.",
            "target": "Anteversion: 25-30 deg\n(relative to FPP, standing AP)\nAccount for retroverted FPP",
            "risk": "Anterior dislocation risk in standing/extension",
            "col": G2A_COL,
        },
        {
            "group": "2B",
            "label": "FLATBACK Deformity\n+ STIFF Spine",
            "patho": "PI-LL >10 deg AND SS change <=10 deg.\nWorst combination: retroverted pelvis + no sit accommodation.\nMost complex planning required.",
            "target": "Anteversion: 30 deg\n(relative to FPP)\nHighest risk -- dual mobility cup strongly recommended",
            "risk": "HIGH risk -- both anterior & posterior dislocation",
            "col": G2B_COL,
        },
    ]

    # column headers
    headers = ["Group", "Classification", "Pathology", "Cup Anteversion Target", "Risk"]
    col_widths = [Inches(0.75), Inches(2.0), Inches(3.8), Inches(3.2), Inches(3.0)]
    col_x = [Inches(0.35)]
    for cw in col_widths[:-1]:
        col_x.append(col_x[-1] + cw)

    hdr_y = Inches(1.32)
    hdr_h = Inches(0.42)
    # header bg
    bg_rect(slide, NAVY, left=int(Inches(0.35)), top=int(hdr_y),
            width=int(sum(col_widths)), height=int(hdr_h))
    for hdr, cx, cw in zip(headers, col_x, col_widths):
        textbox(slide, hdr, left=cx+int(Inches(0.05)), top=int(hdr_y),
                width=cw-int(Inches(0.1)), height=int(hdr_h),
                font_size=13, bold=True, color=WHITE, align=PP_ALIGN.CENTER,
                v_anchor=MSO_ANCHOR.MIDDLE)

    row_h = Inches(1.33)
    row_start = Inches(1.74)
    for i, g in enumerate(groups):
        ry = int(row_start) + i * int(row_h)
        row_bg = RGBColor(0xF5, 0xF5, 0xF5) if i % 2 == 0 else WHITE
        bg_rect(slide, row_bg, left=int(Inches(0.35)), top=ry,
                width=int(sum(col_widths)), height=int(row_h))
        # group badge
        shp = slide.shapes.add_shape(MSO_SHAPE.ROUNDED_RECTANGLE,
                                      col_x[0] + int(Inches(0.05)), ry + int(Inches(0.25)),
                                      int(col_widths[0]) - int(Inches(0.1)), int(Inches(0.82)))
        shp.fill.solid()
        shp.fill.fore_color.rgb = g["col"]
        shp.line.fill.background()
        shp.shadow.inherit = False
        tf = shp.text_frame
        tf.vertical_anchor = MSO_ANCHOR.MIDDLE
        tf.margin_left = tf.margin_right = tf.margin_top = tf.margin_bottom = Pt(2)
        p = tf.paragraphs[0]
        p.alignment = PP_ALIGN.CENTER
        r = p.add_run()
        r.text = g["group"]
        r.font.size = Pt(22)
        r.font.bold = True
        r.font.color.rgb = WHITE

        data = [g["label"], g["patho"], g["target"], g["risk"]]
        cols_idx = [1, 2, 3, 4]
        font_sizes = [13, 12, 12, 12]
        bolds = [True, False, True, False]
        for di, ci, fs, bd in zip(data, cols_idx, font_sizes, bolds):
            textbox(slide, di,
                    left=col_x[ci]+int(Inches(0.05)), top=ry+int(Inches(0.06)),
                    width=col_widths[ci]-int(Inches(0.1)), height=int(row_h)-int(Inches(0.12)),
                    font_size=fs, bold=bd, color=DARK_GREY if ci != 4 else g["col"],
                    align=PP_ALIGN.LEFT, v_anchor=MSO_ANCHOR.MIDDLE, wrap=True)
        # bottom border
        if i < 3:
            divider_line(slide, ry + int(row_h), color=RGBColor(0xCC,0xCC,0xCC), width_pct=0.94)


def slide_05_biomechanics(prs):
    """Biomechanics: standing vs sitting"""
    slide = blank_slide(prs)
    slide_header(slide, "Biomechanics: Stand-to-Sit Transition & Pelvic Compensation")
    footer(slide)

    # Left panel - Normal mobility
    bg_rect(slide, RGBColor(0xE8, 0xF8, 0xEE), left=int(Inches(0.35)), top=int(Inches(1.35)),
            width=int(Inches(5.9)), height=int(Inches(5.7)))
    shp = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE,
                                  int(Inches(0.35)), int(Inches(1.35)),
                                  int(Inches(5.9)), int(Inches(0.38)))
    shp.fill.solid()
    shp.fill.fore_color.rgb = G1A_COL
    shp.line.fill.background()
    shp.shadow.inherit = False
    textbox(slide, "NORMAL MOBILITY  (SS change >10 deg)",
            left=Inches(0.45), top=Inches(1.35), width=Inches(5.7), height=Inches(0.38),
            font_size=14, bold=True, color=WHITE, v_anchor=MSO_ANCHOR.MIDDLE)

    normals = [
        ("Standing:", "Pelvis in neutral, SS ~40 deg, normal lumbar lordosis"),
        ("Sitting:", "Posterior pelvic tilt occurs, LL decreases, SS falls 11-30 deg"),
        ("Effect on cup:", "+15.6 deg functional anteversion on sitting"),
        ("Protection:", "Prevents anterior impingement in hip flexion"),
        ("Clinical result:", "Standard cup anteversion (20-25 deg) is adequate"),
    ]
    tb_n, tf_n = textbox(slide, "",
                          left=Inches(0.5), top=Inches(1.82),
                          width=Inches(5.6), height=Inches(4.9),
                          font_size=14, v_anchor=MSO_ANCHOR.TOP)
    tf_n.paragraphs[0].runs  # clear
    first = True
    for label, val in normals:
        if first:
            p = tf_n.paragraphs[0]
            first = False
        else:
            p = tf_n.add_paragraph()
        p.alignment = PP_ALIGN.LEFT
        r1 = p.add_run()
        r1.text = label + "  "
        r1.font.size = Pt(13)
        r1.font.bold = True
        r1.font.color.rgb = G1A_COL
        r2 = p.add_run()
        r2.text = val
        r2.font.size = Pt(13)
        r2.font.bold = False
        r2.font.color.rgb = DARK_GREY
        # blank spacer
        sp = tf_n.add_paragraph()
        sp.add_run().text = ""
        sp.runs[0].font.size = Pt(5)

    # Right panel - Stiff spine
    bg_rect(slide, RGBColor(0xFD, 0xF0, 0xE8), left=int(Inches(6.6)), top=int(Inches(1.35)),
            width=int(Inches(6.4)), height=int(Inches(5.7)))
    shp2 = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE,
                                   int(Inches(6.6)), int(Inches(1.35)),
                                   int(Inches(6.4)), int(Inches(0.38)))
    shp2.fill.solid()
    shp2.fill.fore_color.rgb = G1B_COL
    shp2.line.fill.background()
    shp2.shadow.inherit = False
    textbox(slide, "SPINOPELVIC STIFFNESS  (SS change <=10 deg)",
            left=Inches(6.7), top=Inches(1.35), width=Inches(6.2), height=Inches(0.38),
            font_size=14, bold=True, color=WHITE, v_anchor=MSO_ANCHOR.MIDDLE)

    stiffs = [
        ("Cause:", "Lumbar fusion, severe degenerative spondylosis"),
        ("Standing:", "Pelvis may be neutral"),
        ("Sitting:", "Pelvis CANNOT tilt posteriorly -- SS unchanged"),
        ("Effect on cup:", "No functional anteversion gain -- hip must flex more"),
        ("Risk:", "Posterior impingement -> posterior dislocation when sitting/rising"),
        ("Management:", "Increase cup anteversion target to 30 deg (FPP reference)"),
    ]
    tb_s, tf_s = textbox(slide, "",
                          left=Inches(6.7), top=Inches(1.82),
                          width=Inches(6.1), height=Inches(4.9),
                          font_size=14, v_anchor=MSO_ANCHOR.TOP)
    first = True
    for label, val in stiffs:
        if first:
            p = tf_s.paragraphs[0]
            first = False
        else:
            p = tf_s.add_paragraph()
        p.alignment = PP_ALIGN.LEFT
        r1 = p.add_run()
        r1.text = label + "  "
        r1.font.size = Pt(13)
        r1.font.bold = True
        r1.font.color.rgb = G1B_COL
        r2 = p.add_run()
        r2.text = val
        r2.font.size = Pt(13)
        r2.font.bold = False
        r2.font.color.rgb = DARK_GREY
        sp = tf_s.add_paragraph()
        sp.add_run().text = ""
        sp.runs[0].font.size = Pt(5)


def slide_06_flatback(prs):
    """Flatback deformity"""
    slide = blank_slide(prs)
    slide_header(slide, "Flatback Deformity & Its Impact on THR")
    footer(slide)

    # PI equation box
    shp = slide.shapes.add_shape(MSO_SHAPE.ROUNDED_RECTANGLE,
                                  Inches(0.35), Inches(1.38),
                                  Inches(12.6), Inches(0.72))
    shp.fill.solid()
    shp.fill.fore_color.rgb = NAVY
    shp.line.fill.background()
    shp.shadow.inherit = False
    tf = shp.text_frame
    tf.word_wrap = True
    tf.vertical_anchor = MSO_ANCHOR.MIDDLE
    tf.margin_left = tf.margin_right = Pt(12)
    p = tf.paragraphs[0]
    p.alignment = PP_ALIGN.CENTER
    r = p.add_run()
    r.text = "PI = PT + SS  (fixed equation)   |   Flatback = PI - LL >10 deg   |   Chronic posterior pelvic tilt = elevated PT -> apparent FPP anteversion"
    r.font.size = Pt(15)
    r.font.bold = True
    r.font.color.rgb = GOLD

    # Two scenario columns
    scenarios = [
        {
            "title": "Flatback with NORMAL mobility (Group 2A)",
            "col": G2A_COL,
            "bg": RGBColor(0xE8, 0xF0, 0xFA),
            "items": [
                "Chronic posterior pelvic tilt due to PI-LL mismatch",
                "FPP is retroverted relative to APP",
                "Cup positioned at 15 deg to APP actually has >25 deg FPP anteversion",
                "Risk: ANTERIOR dislocation in standing / extension",
                "Target 25-30 deg anteversion relative to FPP (standing AP pelvis)",
                "Must measure SS on standing lateral to determine FPP",
            ]
        },
        {
            "title": "Flatback + STIFF spine (Group 2B) -- Highest Risk",
            "col": G2B_COL,
            "bg": RGBColor(0xFBE9E7),
            "items": [
                "Both: retroverted pelvis (FPP =/= APP) AND no sit accommodation",
                "Standing: excessive functional anteversion -> anterior dislocation",
                "Sitting: no posterior tilt -> posterior impingement -> posterior dislocation",
                "Target 30 deg relative to FPP",
                "Dual mobility cup strongly recommended",
                "Consider combined spine + hip surgical planning",
            ]
        },
    ]

    for i, sc in enumerate(scenarios):
        lft = Inches(0.35) + i * Inches(6.5)
        # BG
        bg_rect(slide, sc["bg"], left=int(lft), top=int(Inches(2.22)),
                width=int(Inches(6.2)), height=int(Inches(4.75)))
        # Header bar
        hshp = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE,
                                       int(lft), int(Inches(2.22)),
                                       int(Inches(6.2)), int(Inches(0.44)))
        hshp.fill.solid()
        hshp.fill.fore_color.rgb = sc["col"]
        hshp.line.fill.background()
        hshp.shadow.inherit = False
        textbox(slide, sc["title"],
                left=lft + int(Inches(0.1)), top=Inches(2.22),
                width=Inches(6.0), height=Inches(0.44),
                font_size=13, bold=True, color=WHITE, v_anchor=MSO_ANCHOR.MIDDLE)
        # Items
        tb, tf = textbox(slide, "",
                          left=lft + int(Inches(0.15)), top=Inches(2.75),
                          width=Inches(5.9), height=Inches(4.1),
                          font_size=13, v_anchor=MSO_ANCHOR.TOP, wrap=True)
        first = True
        for item in sc["items"]:
            if first:
                p = tf.paragraphs[0]
                first = False
            else:
                p = tf.add_paragraph()
            p.alignment = PP_ALIGN.LEFT
            r = p.add_run()
            r.text = "  " + item
            r.font.size = Pt(13)
            r.font.color.rgb = DARK_GREY
            sp = tf.add_paragraph()
            sp.add_run().text = ""
            sp.runs[0].font.size = Pt(4)


def slide_07_preop(prs):
    """Preoperative Evaluation"""
    slide = blank_slide(prs)
    slide_header(slide, "Preoperative Evaluation: Who Needs Spinopelvic Assessment?")
    footer(slide)

    # Flag section
    flag_items = [
        ("History of lumbar fusion (any levels)", RED),
        ("Prior spinal deformity correction surgery", RED),
        ("Symptoms of flatback deformity (forward stooped posture)", ORANGE),
        ("Symptomatic lumbar stenosis or severe DDD", ORANGE),
        ("Previously failed THR with recurrent dislocation", RED),
        ("Significant sagittal imbalance on clinical exam", ORANGE),
    ]

    textbox(slide, "Red Flags Requiring Full Spinopelvic Workup",
            left=Inches(0.4), top=Inches(1.38),
            width=Inches(5.8), height=Inches(0.38),
            font_size=15, bold=True, color=NAVY)

    for i, (item, col) in enumerate(flag_items):
        y = Inches(1.82) + i * Inches(0.6)
        dot = slide.shapes.add_shape(MSO_SHAPE.OVAL,
                                      Inches(0.45), y + int(Inches(0.12)),
                                      Inches(0.22), Inches(0.22))
        dot.fill.solid()
        dot.fill.fore_color.rgb = col
        dot.line.fill.background()
        dot.shadow.inherit = False
        textbox(slide, item,
                left=Inches(0.82), top=y,
                width=Inches(5.3), height=Inches(0.5),
                font_size=13, bold=False, color=DARK_GREY,
                v_anchor=MSO_ANCHOR.MIDDLE)

    # Divider
    ln = slide.shapes.add_connector(
        __import__('pptx.enum.shapes', fromlist=['MSO_CONNECTOR']).MSO_CONNECTOR.STRAIGHT,
        Inches(6.5), Inches(1.35), Inches(6.5), Inches(7.1))
    ln.line.color.rgb = RGBColor(0xCC,0xCC,0xCC)
    ln.line.width = Pt(1.0)

    # Imaging protocol
    textbox(slide, "Imaging Protocol",
            left=Inches(6.7), top=Inches(1.38),
            width=Inches(6.1), height=Inches(0.38),
            font_size=15, bold=True, color=NAVY)

    imaging = [
        ("MINIMUM (all spine-risk patients)", [
            "Lateral lumbar + pelvis -- STANDING",
            "Lateral lumbar + pelvis -- SEATED (forward-flexed)",
            "Standing AP pelvis",
        ], TEAL),
        ("MEASURE", [
            "Sacral slope (SS) in standing AND sitting",
            "Calculate SS change: >10 deg = normal; <=10 deg = stiff",
            "PI-LL mismatch on standing lateral",
            "APP vs FPP discrepancy",
        ], G2A_COL),
        ("ADVANCED (complex/revision)", [
            "EOS imaging -- low-dose 3D spinopelvic parameters",
            "CT scan -- assess femoral version, cup position",
            "Robotic planning software with functional zone targeting",
        ], NAVY),
    ]

    y = Inches(1.88)
    for label, items, col in imaging:
        textbox(slide, label,
                left=Inches(6.7), top=y,
                width=Inches(6.1), height=Inches(0.35),
                font_size=13, bold=True, color=col)
        y += Inches(0.35)
        for it in items:
            dot = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE,
                                          Inches(6.82), y + int(Inches(0.12)),
                                          Inches(0.14), Inches(0.14))
            dot.fill.solid()
            dot.fill.fore_color.rgb = col
            dot.line.fill.background()
            dot.shadow.inherit = False
            textbox(slide, it,
                    left=Inches(7.1), top=y,
                    width=Inches(5.8), height=Inches(0.38),
                    font_size=12, color=DARK_GREY, v_anchor=MSO_ANCHOR.MIDDLE)
            y += Inches(0.38)
        y += Inches(0.12)


def slide_08_surgical(prs):
    """Surgical Recommendations"""
    slide = blank_slide(prs)
    slide_header(slide, "Surgical Recommendations by Group")
    footer(slide)

    # Summary anteversion targets
    targets = [
        ("1A", "20-25 deg\nto APP", G1A_COL),
        ("1B", "30 deg\nto FPP", G1B_COL),
        ("2A", "25-30 deg\nto FPP", G2A_COL),
        ("2B", "30 deg\nto FPP", G2B_COL),
    ]
    for i, (grp, tgt, col) in enumerate(targets):
        lft = Inches(0.35) + i * Inches(3.25)
        shp = slide.shapes.add_shape(MSO_SHAPE.ROUNDED_RECTANGLE,
                                      int(lft), int(Inches(1.38)),
                                      int(Inches(3.05)), int(Inches(1.1)))
        shp.fill.solid()
        shp.fill.fore_color.rgb = col
        shp.line.fill.background()
        shp.shadow.inherit = False
        tf = shp.text_frame
        tf.word_wrap = True
        tf.vertical_anchor = MSO_ANCHOR.MIDDLE
        tf.margin_left = tf.margin_right = Pt(6)
        tf.margin_top = tf.margin_bottom = Pt(4)
        p = tf.paragraphs[0]
        p.alignment = PP_ALIGN.CENTER
        r = p.add_run()
        r.text = "Group " + grp
        r.font.size = Pt(18)
        r.font.bold = True
        r.font.color.rgb = WHITE
        p2 = tf.add_paragraph()
        p2.alignment = PP_ALIGN.CENTER
        r2 = p2.add_run()
        r2.text = tgt
        r2.font.size = Pt(14)
        r2.font.bold = False
        r2.font.color.rgb = WHITE

    # General surgical principles
    principles = [
        ("Cup Positioning", TEAL,
         "Target FUNCTIONAL safe zone, not anatomical Lewinnek zone.\n"
         "Use FPP as reference in groups 1B, 2A, 2B.\n"
         "In Group 1A, APP is adequate (APP = FPP)."),
        ("Implant Choice", G2B_COL,
         "Dual mobility cup: strongly recommended for all spine-fused patients.\n"
         "Constrained liner: consider for severe spinopelvic imbalance.\n"
         "Large femoral head (36 mm+): improves jump distance and stability."),
        ("Technology Assist", G2A_COL,
         "Robotic-arm assisted THR: improves accuracy of functional zone targeting.\n"
         "CT-based navigation: accounts for patient-specific pelvic orientation.\n"
         "EOS imaging: pre-op 3D planning with low radiation dose."),
        ("Combined Planning", NAVY,
         "Patients with >2-level lumbar fusion: coordinate with spine surgeon.\n"
         "Plan hip AFTER spine if simultaneous correction needed.\n"
         "Document cup orientation with post-op CT if high risk."),
    ]

    for i, (title, col, desc) in enumerate(principles):
        row = i // 2
        col_idx = i % 2
        lft = Inches(0.35) + col_idx * Inches(6.5)
        top = Inches(2.62) + row * Inches(2.25)
        bg_rect(slide, RGBColor(0xF5, 0xF5, 0xF5),
                left=int(lft), top=int(top),
                width=int(Inches(6.2)), height=int(Inches(2.1)))
        hshp = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE,
                                       int(lft), int(top),
                                       int(Inches(6.2)), int(Inches(0.4)))
        hshp.fill.solid()
        hshp.fill.fore_color.rgb = col
        hshp.line.fill.background()
        hshp.shadow.inherit = False
        textbox(slide, title,
                left=lft + int(Inches(0.1)), top=top,
                width=Inches(6.0), height=Inches(0.4),
                font_size=14, bold=True, color=WHITE, v_anchor=MSO_ANCHOR.MIDDLE)
        textbox(slide, desc,
                left=lft + int(Inches(0.15)), top=top + int(Inches(0.44)),
                width=Inches(5.9), height=Inches(1.6),
                font_size=12, color=DARK_GREY, v_anchor=MSO_ANCHOR.TOP, wrap=True)


def slide_09_clinical(prs):
    """Clinical Scenarios"""
    slide = blank_slide(prs)
    slide_header(slide, "Clinical Scenarios: Applying the Classification")
    footer(slide)

    scenarios = [
        {
            "title": "Case 1 - Posterior Dislocation While Sitting",
            "col": G1B_COL,
            "patient": "65F, prior L3-S5 fusion, elective THR for OA",
            "mechanism": "Standard cup anteversion placed (15-20 deg). Spine cannot tilt posteriorly on sitting. Hip must flex further. Posterior neck-cup impingement occurs.",
            "outcome": "Posterior dislocation on first post-op sit-to-stand",
            "classification": "Group 1B (normal alignment, stiff spine)",
            "correction": "Should have targeted 30 deg anteversion to FPP. Dual mobility cup indicated.",
        },
        {
            "title": "Case 2 - Anterior Dislocation in Standing",
            "col": G2A_COL,
            "patient": "70M, longstanding degenerative flatback (PI-LL 18 deg), elective THR",
            "mechanism": "Cup placed at 20 deg to APP. Due to chronic posterior pelvic tilt, FPP is retroverted -- functional anteversion is actually 35 deg. In extension/standing, anterior dislocation.",
            "outcome": "Anterior dislocation at 6 weeks post-op during standing from a low chair",
            "classification": "Group 2A (flatback, normal mobility)",
            "correction": "Should have referenced FPP on standing lateral. Target 25-30 deg to FPP.",
        },
        {
            "title": "Case 3 - Recurrent Dislocation (Both Directions)",
            "col": G2B_COL,
            "patient": "72F, sagittal imbalance, L2-pelvis fusion, progressive OA THR",
            "mechanism": "Flatback + stiff spine. Retroverted pelvis at baseline + no standing-to-sitting accommodation. Cup is too anteverted when standing AND impinges posteriorly when sitting.",
            "outcome": "Two dislocations: one anterior (week 2), one posterior (week 8)",
            "classification": "Group 2B (flatback + stiff spine) -- HIGHEST RISK",
            "correction": "Pre-op spinopelvic workup, robotic targeting to FPP at 30 deg, dual mobility cup mandatory.",
        },
    ]

    for i, sc in enumerate(scenarios):
        top = Inches(1.38) + i * Inches(2.0)
        bg_rect(slide, RGBColor(0xF8, 0xF8, 0xF8),
                left=int(Inches(0.35)), top=int(top),
                width=int(Inches(12.6)), height=int(Inches(1.88)))
        # left bar
        bar = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE,
                                      int(Inches(0.35)), int(top),
                                      int(Inches(0.18)), int(Inches(1.88)))
        bar.fill.solid()
        bar.fill.fore_color.rgb = sc["col"]
        bar.line.fill.background()
        bar.shadow.inherit = False
        # title
        textbox(slide, sc["title"],
                left=Inches(0.65), top=top,
                width=Inches(12.1), height=Inches(0.38),
                font_size=14, bold=True, color=sc["col"])

        # 5 mini-fields in columns
        fields = [
            ("Patient:", sc["patient"]),
            ("Mechanism:", sc["mechanism"]),
            ("Classification:", sc["classification"]),
            ("Correction:", sc["correction"]),
        ]
        field_top = top + int(Inches(0.38))
        col_w_l = Inches(0.9)
        col_w_v = Inches(2.25)
        n_cols = 4
        for j, (lbl, val) in enumerate(fields):
            fx = Inches(0.62) + j * Inches(3.1)
            textbox(slide, lbl,
                    left=fx, top=field_top,
                    width=col_w_l, height=Inches(0.28),
                    font_size=10, bold=True, color=sc["col"])
            textbox(slide, val,
                    left=fx, top=field_top + int(Inches(0.28)),
                    width=col_w_v + Inches(0.7), height=Inches(1.15),
                    font_size=10, color=DARK_GREY, wrap=True, v_anchor=MSO_ANCHOR.TOP)

        if i < 2:
            divider_line(slide, int(top) + int(Inches(1.88)),
                         color=RGBColor(0xCC,0xCC,0xCC), width_pct=0.95)


def slide_10_summary(prs):
    """Summary & Key Takeaways"""
    slide = blank_slide(prs)
    slide_header(slide, "Key Takeaways")
    footer(slide)

    takeaways = [
        (TEAL, "Kinematic Chain",
         "The spine, pelvis, and hip are one functional unit. Spinal pathology directly alters "
         "the FUNCTIONAL orientation of the acetabular cup during activities of daily living."),
        (G2B_COL, "Lewinnek Zone is Insufficient",
         "The traditional 15+/-10 deg anteversion / 40+/-10 deg inclination safe zone was defined "
         "in static, anatomical terms. Most dislocations in hip-spine patients occur within this zone."),
        (G2A_COL, "Measure SS Change",
         "A standing AND sitting lateral radiograph to calculate sacral slope change is the minimum "
         "workup. SS change <=10 deg defines spinopelvic stiffness requiring anteversion correction."),
        (G1B_COL, "Use FPP as Reference",
         "In groups 1B, 2A, and 2B, always target cup anteversion relative to the Functional Pelvic "
         "Plane (FPP), not the APP. Reference the standing AP pelvis in these patients."),
        (NAVY, "Dual Mobility + Robotic",
         "Dual mobility cups are the implant of choice for prior spinal fusion patients. "
         "Robotic-assisted THR significantly improves accuracy of functional zone targeting."),
    ]

    for i, (col, title, desc) in enumerate(takeaways):
        top = Inches(1.38) + i * Inches(1.15)
        # number badge
        shp = slide.shapes.add_shape(MSO_SHAPE.OVAL,
                                      int(Inches(0.35)), int(top) + int(Inches(0.15)),
                                      int(Inches(0.55)), int(Inches(0.55)))
        shp.fill.solid()
        shp.fill.fore_color.rgb = col
        shp.line.fill.background()
        shp.shadow.inherit = False
        tf = shp.text_frame
        tf.vertical_anchor = MSO_ANCHOR.MIDDLE
        p = tf.paragraphs[0]
        p.alignment = PP_ALIGN.CENTER
        r = p.add_run()
        r.text = str(i + 1)
        r.font.size = Pt(16)
        r.font.bold = True
        r.font.color.rgb = WHITE
        # title
        textbox(slide, title,
                left=Inches(1.05), top=top,
                width=Inches(2.5), height=Inches(0.9),
                font_size=15, bold=True, color=col, v_anchor=MSO_ANCHOR.MIDDLE)
        # desc
        textbox(slide, desc,
                left=Inches(3.7), top=top,
                width=Inches(9.25), height=Inches(0.9),
                font_size=13, color=DARK_GREY, v_anchor=MSO_ANCHOR.MIDDLE, wrap=True)
        if i < 4:
            divider_line(slide, int(top) + int(Inches(1.05)),
                         color=RGBColor(0xDD,0xDD,0xDD), width_pct=0.94)


# ── MAIN ─────────────────────────────────────────────────────────

def main():
    prs = new_prs()
    slide_01_title(prs)
    slide_02_overview(prs)
    slide_03_parameters(prs)
    slide_04_classification(prs)
    slide_05_biomechanics(prs)
    slide_06_flatback(prs)
    slide_07_preop(prs)
    slide_08_surgical(prs)
    slide_09_clinical(prs)
    slide_10_summary(prs)

    out_path = "/tmp/workspace/hip-spine-pptx/HipSpine_THR_Classification.pptx"
    buf = BytesIO()
    prs.save(buf)
    with open(out_path, "wb") as f:
        f.write(buf.getvalue())
    print(f"Saved: {out_path}")
    import os
    size_kb = os.path.getsize(out_path) // 1024
    print(f"File size: {size_kb} KB | Slides: {len(prs.slides)}")

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

cd /tmp/workspace/hip-spine-pptx && python build_pptx.py 2>&1

Editing File

~/hip-spine-pptx/build_pptx.py

Successfully replaced 1 instance(s) of the string in '/tmp/workspace/hip-spine-pptx/build_pptx.py'
Running Command

cd /tmp/workspace/hip-spine-pptx && python build_pptx.py 2>&1

Your presentation is ready:

HipSpine THR Classification

Presentation · PPTX


10 slides, 16:9 widescreen, professional navy/teal/gold colour scheme:
SlideContent
1Title slide
2Why the hip-spine relationship matters (2x dislocation risk, Lewinnek zone failure)
3Key spinopelvic parameters (PI, SS, PT, PI-LL mismatch, APP vs FPP)
4Hip-Spine Classification Table (Groups 1A / 1B / 2A / 2B with pathology + anteversion targets)
5Biomechanics - normal mobility vs spinopelvic stiffness (stand-to-sit)
6Flatback deformity and its impact on THR (Groups 2A vs 2B)
7Preoperative evaluation - red flags + imaging protocol
8Surgical recommendations by group (anteversion targets, implant choice, robotic planning)
9Three clinical scenarios (real-world application of the classification)
10Key takeaways summary
Each group is colour-coded consistently throughout (green=1A, orange=1B, blue=2A, red=2B) to make the classification easy to follow at a glance.

We increase anterversion in 2A ?

Great question — this is a common point of confusion.
No — in Group 2A (flatback + normal mobility), you actually need LESS anteversion than you think, or you target anteversion relative to the FPP rather than the APP.
Here is the key reasoning:

Group 2A: Flatback Deformity + Normal Mobility

The problem: Chronic posterior pelvic tilt from PI-LL mismatch means the pelvis is already retroverted at baseline. The FPP is tilted posteriorly relative to the APP.
Effect on cup:
  • If you place the cup at, say, 20° to the APP...
  • ...because the pelvis is already retroverted, the cup's functional anteversion (relative to vertical/weight-bearing line) is already more than 20° — maybe 30-35°
  • The cup is effectively over-anteverted in the standing position
  • Risk = ANTERIOR dislocation in standing/extension

What the 25-30° target actually means

The 25-30° target in Group 2A is measured to the FPP (on standing AP pelvis), not the APP.
Since the FPP is already retroverted (posterior tilt), targeting 25-30° to the FPP compensates downward — you are placing the cup at less anatomical anteversion than you would in a normal patient, so that the functional result is appropriate.

Comparison table

GroupPelvic PostureRiskAnteversion targetDirection of change
1ANormalNeither20-25° to APPBaseline
1BNormal but stiffPosterior dislocation (sitting)30° to FPPIncrease vs 1A
2ARetroverted (flatback)Anterior dislocation (standing)25-30° to FPPAppears similar numerically but means LESS anatomical anteversion because FPP is already retroverted
2BRetroverted + stiffBoth directions30° to FPPComplex — reference FPP to avoid over-anteversion

Bottom line: In Group 2A, the cup target is referenced to the FPP, not the APP. Because the FPP is posteriorly tilted, placing the cup at 25-30° to FPP actually means placing it at fewer degrees anatomically than in a normal patient. The danger in Group 2A is inadvertently placing too much anteversion (anterior dislocation), not too little. The correction is to use the FPP as your reference so the functional result is appropriate — not to blindly increase anteversion.
This is a shared conversation. Sign in to Orris to start your own chat.