"""Generate Contracts analysis PDF for Cuong (May 22, 2026).

Incorporates all issue-level feedback from the SHEP grading engine,
model analysis comparisons, and conclusory-vs-proper rewrites.
"""

from reportlab.lib.pagesizes import letter
from reportlab.lib.units import inch
from reportlab.lib.colors import HexColor
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib.enums import TA_LEFT, TA_JUSTIFY, TA_CENTER
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, HRFlowable,
    KeepTogether,
)
import os

NAVY       = HexColor("#1a2744")
GOLD       = HexColor("#c9a227")
MED_TEXT   = HexColor("#2d3748")
LIGHT_TEXT = HexColor("#4a5568")
BORDER     = HexColor("#d4cfc5")
WARM_BG    = HexColor("#fdf6ee")
COOL_BG    = HexColor("#eef5ee")
ACTION_BG  = HexColor("#f0f4ff")
STRENGTH_BG = HexColor("#f0faf0")
REWRITE_BEFORE = HexColor("#fce8e8")
REWRITE_AFTER  = HexColor("#e2f0e2")
MISSED_BG  = HexColor("#fff3e6")
RED_ACCENT = HexColor("#c53030")
AMBER_ACCENT = HexColor("#b7791f")

def make_styles():
    s = {}
    s["title"] = ParagraphStyle("title", fontName="Helvetica-Bold", fontSize=15,
        textColor=NAVY, alignment=TA_LEFT, spaceAfter=1, leading=18)
    s["subtitle"] = ParagraphStyle("subtitle", fontName="Helvetica", fontSize=9,
        textColor=LIGHT_TEXT, alignment=TA_LEFT, spaceAfter=8, leading=11)
    s["h1"] = ParagraphStyle("h1", fontName="Helvetica-Bold", fontSize=11,
        textColor=NAVY, spaceBefore=10, spaceAfter=3, leading=13)
    s["h2"] = ParagraphStyle("h2", fontName="Helvetica-Bold", fontSize=9,
        textColor=MED_TEXT, spaceBefore=5, spaceAfter=2, leading=11)
    s["badge_needs_work"] = ParagraphStyle("badge_nw", fontName="Helvetica-Bold",
        fontSize=7.5, textColor=RED_ACCENT, spaceBefore=0, spaceAfter=2, leading=10)
    s["badge_missed"] = ParagraphStyle("badge_m", fontName="Helvetica-Bold",
        fontSize=7.5, textColor=AMBER_ACCENT, spaceBefore=0, spaceAfter=2, leading=10)
    s["body"] = ParagraphStyle("body", fontName="Helvetica", fontSize=8.5,
        textColor=MED_TEXT, alignment=TA_JUSTIFY, leading=11.5,
        spaceBefore=1, spaceAfter=3)
    s["essay"] = ParagraphStyle("essay", fontName="Helvetica", fontSize=8,
        textColor=HexColor("#374151"), alignment=TA_JUSTIFY, leading=10.5)
    s["action"] = ParagraphStyle("action", fontName="Helvetica", fontSize=8.5,
        textColor=HexColor("#1e3a5f"), alignment=TA_LEFT, leading=11.5,
        spaceBefore=1, spaceAfter=1)
    s["strength"] = ParagraphStyle("strength", fontName="Helvetica", fontSize=8.5,
        textColor=HexColor("#276749"), alignment=TA_LEFT, leading=11.5,
        spaceBefore=1, spaceAfter=1)
    s["rewrite_label"] = ParagraphStyle("rewrite_label", fontName="Helvetica-Bold",
        fontSize=7.5, textColor=LIGHT_TEXT, leading=10, spaceBefore=0, spaceAfter=1)
    s["rewrite"] = ParagraphStyle("rewrite", fontName="Helvetica", fontSize=8,
        textColor=MED_TEXT, alignment=TA_JUSTIFY, leading=10.5)
    s["footer"] = ParagraphStyle("footer", fontName="Helvetica", fontSize=7,
        textColor=LIGHT_TEXT, alignment=TA_LEFT, leading=9)
    s["section_label"] = ParagraphStyle("section_label", fontName="Helvetica-Bold",
        fontSize=8, textColor=GOLD, spaceBefore=8, spaceAfter=2, leading=10)
    return s

ST = make_styles()

def shaded(text, bg, pad=6):
    t = Table([[Paragraph(text, ST["essay"])]], colWidths=["100%"])
    t.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,-1), bg),
        ("LEFTPADDING", (0,0), (-1,-1), pad), ("RIGHTPADDING", (0,0), (-1,-1), pad),
        ("TOPPADDING", (0,0), (-1,-1), pad), ("BOTTOMPADDING", (0,0), (-1,-1), pad),
        ("BOX", (0,0), (-1,-1), 0.3, BORDER),
    ]))
    return t

def action_box(text):
    t = Table([[Paragraph(text, ST["action"])]], colWidths=["100%"])
    t.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,-1), ACTION_BG),
        ("LEFTPADDING", (0,0), (-1,-1), 8), ("RIGHTPADDING", (0,0), (-1,-1), 8),
        ("TOPPADDING", (0,0), (-1,-1), 5), ("BOTTOMPADDING", (0,0), (-1,-1), 5),
    ]))
    return t

def strength_box(text):
    t = Table([[Paragraph(text, ST["strength"])]], colWidths=["100%"])
    t.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,-1), STRENGTH_BG),
        ("LEFTPADDING", (0,0), (-1,-1), 8), ("RIGHTPADDING", (0,0), (-1,-1), 8),
        ("TOPPADDING", (0,0), (-1,-1), 5), ("BOTTOMPADDING", (0,0), (-1,-1), 5),
        ("BOX", (0,0), (-1,-1), 0.3, HexColor("#9ae6b4")),
    ]))
    return t

def rewrite_pair(before_text, after_text):
    before_parts = [
        [Paragraph("<b>CONCLUSORY</b>", ST["rewrite_label"]),
         Paragraph(before_text, ST["rewrite"])]
    ]
    after_parts = [
        [Paragraph("<b>REWRITTEN</b>", ST["rewrite_label"]),
         Paragraph(after_text, ST["rewrite"])]
    ]
    bt = Table(before_parts, colWidths=["100%"])
    bt.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,-1), REWRITE_BEFORE),
        ("LEFTPADDING", (0,0), (-1,-1), 6), ("RIGHTPADDING", (0,0), (-1,-1), 6),
        ("TOPPADDING", (0,0), (-1,-1), 4), ("BOTTOMPADDING", (0,0), (-1,-1), 4),
        ("BOX", (0,0), (-1,-1), 0.3, BORDER),
    ]))
    at = Table(after_parts, colWidths=["100%"])
    at.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,-1), REWRITE_AFTER),
        ("LEFTPADDING", (0,0), (-1,-1), 6), ("RIGHTPADDING", (0,0), (-1,-1), 6),
        ("TOPPADDING", (0,0), (-1,-1), 4), ("BOTTOMPADDING", (0,0), (-1,-1), 4),
        ("BOX", (0,0), (-1,-1), 0.3, BORDER),
    ]))
    return [bt, Spacer(1, 2), at, Spacer(1, 6)]

def thin_hr():
    return HRFlowable(width="100%", thickness=0.4, color=BORDER, spaceAfter=3, spaceBefore=4)

def gold_hr():
    return HRFlowable(width="25%", thickness=1, color=GOLD, spaceAfter=4, spaceBefore=2)


def build_contracts():
    output_dir = r"C:\Users\sallen\Desktop\SHEP\BAR PREP by SHEP\STUDENT ANSWERS\Cuong"
    output_path = os.path.join(output_dir, "Cuong_Contracts_Analysis.pdf")
    doc = SimpleDocTemplate(output_path, pagesize=letter,
        leftMargin=0.65*inch, rightMargin=0.65*inch,
        topMargin=0.55*inch, bottomMargin=0.5*inch)
    story = []

    # ================================================================
    # HEADER
    # ================================================================
    story.append(Paragraph("Deepening Your Analysis: Contracts", ST["title"]))
    story.append(gold_hr())
    story.append(Paragraph("Cuong  |  Band 2/6  |  May 22, 2026  |  949 words", ST["subtitle"]))

    # ================================================================
    # OVERALL FEEDBACK
    # ================================================================
    story.append(Paragraph("OVERALL FEEDBACK", ST["section_label"]))
    story.append(Paragraph(
        "This essay has critical problems that would likely result in a failing score. Your "
        "performance is characterized by a failure to apply legal rules to the facts. You "
        "frequently state a conclusion without providing the <b>analytical bridge</b> required "
        "to reach it. An analytical bridge requires you to state the rule, identify the relevant "
        "facts, and explain how those facts satisfy or fail the elements of the rule. For "
        "example, when you conclude that the neighbor cannot sue the painter, you simply state "
        "there is no assignment. A stronger version: <i>'To establish breach of contract, the "
        "plaintiff must show privity. The neighbor was not a party to the original agreement. "
        "Because there was no valid assignment, the neighbor lacks privity and cannot maintain "
        "a cause of action against the painter.'</i>",
        ST["body"]))
    story.append(Paragraph(
        "Your grasp of fundamental contract doctrines is inconsistent. You conflate assignment "
        "and delegation, misapply substantial performance, and completely overlook the "
        "third-party beneficiary issue. These are foundational errors that prevent you from "
        "correctly resolving the legal questions. To improve, move away from conclusory "
        "statements and focus on the <i>why</i> behind your conclusions. Rigorously practice "
        "identifying the elements of a rule and explicitly linking them to the facts.",
        ST["body"]))

    # ================================================================
    # STRUCTURAL STRENGTHS (FROM SHEP'S EYES)
    # ================================================================
    story.append(Paragraph("WHAT YOU DID WELL", ST["section_label"]))
    story.append(strength_box(
        "<b>Good structural instincts.</b> Your use of clear headings makes the essay easy to "
        "navigate -- a grader can instantly see where you are in your analysis. You are doing the "
        "hard work of separating your thoughts into distinct IRAC sections, which is exactly what "
        "graders want to see. Your conclusions need strengthening -- instead of just restating "
        "your analysis, tie the rule and facts together one final time: <i>'Because the painter "
        "never consented to the assignment of duties, there is no privity, and the neighbor's "
        "claim must fail.'</i> That closes the loop on the argument."))
    story.append(Spacer(1, 4))

    # ================================================================
    # ISSUE 1: Assignment of Rights
    # ================================================================
    story.append(thin_hr())
    story.append(Paragraph("NEEDS WORK", ST["badge_needs_work"]))
    story.append(Paragraph("Issue 1: Assignment of Rights from Homeowner to Neighbor", ST["h1"]))
    story.append(Paragraph("Your analysis:", ST["h2"]))
    story.append(shaded(
        "When one party changes the terms or transfers the contract to another person without "
        "the other party's consent, then there is no more valid contract between the original "
        "parties. A proper assignment allows an original party to assign the contractual "
        "obligations to a third party and letting the other original party knows about the "
        "assignment. The neighbor will not succeed because there is not a valid assignment to "
        "the neighbor from the homeowner.",
        WARM_BG))
    story.append(Paragraph("Model analysis:", ST["h2"]))
    story.append(shaded(
        "The neighbor was not a party to the original homeowner-painter contract. An assignment "
        "transfers a <b>right</b> to receive performance -- for example, the homeowner's right "
        "to have his house painted. A delegation transfers a <b>duty</b> to perform. These are "
        "distinct concepts. Here, the homeowner attempted to redirect the painter's performance "
        "to a different house entirely -- this changes the nature and location of the work, "
        "making it a material modification rather than a simple assignment of rights. The "
        "homeowner cannot unilaterally assign rights in a way that materially changes the "
        "obligor's duties without the obligor's consent.",
        COOL_BG))
    story.append(Paragraph(
        "<b>Critical doctrinal error:</b> You conflate assignment of rights with delegation of "
        "duties. You wrote that the homeowner tried to assign \"contractual obligations\" -- but "
        "obligations are <i>duties</i>, not rights. An assignment involves the transfer of a "
        "right to receive performance; a delegation involves the transfer of a duty to perform. "
        "This confusion signals to the grader that you do not understand the distinction, which "
        "is a core concept the question is testing. Additionally, your analysis is entirely "
        "conclusory -- you state there is no valid assignment but never explain what a valid "
        "assignment requires or why the facts here fail those requirements.",
        ST["body"]))
    story.append(action_box(
        "<b>Build this habit:</b> Before writing about assignment or delegation, write out the "
        "definitions: \"Assignment transfers a right to receive performance. Delegation "
        "transfers a duty to perform.\" Then identify which one is actually happening in the "
        "facts. This prevents the conflation and earns separate points for each concept."))

    # ================================================================
    # ISSUE 2: Privity / Breach of Contract Against Neighbor
    # ================================================================
    story.append(thin_hr())
    story.append(Paragraph("NEEDS WORK", ST["badge_needs_work"]))
    story.append(Paragraph("Issue 2: Painter's Breach of Contract Against Neighbor", ST["h1"]))
    story.append(Paragraph("Your analysis:", ST["h2"]))
    story.append(shaded(
        "The neighbor will not succeed in a breach of contract action against the painter "
        "because there is not a valid assignment to the neighbor from the homeowner. Because "
        "there is no valid assignment of the contract between the two original parties to the "
        "neighbor, there is no valid contract between the neighbor and the painter.",
        WARM_BG))
    story.append(Paragraph("Model analysis:", ST["h2"]))
    story.append(shaded(
        "A breach of contract claim requires the existence of a valid contract between the "
        "parties -- that is, <b>privity of contract</b>. The neighbor was not a party to the "
        "original homeowner-painter contract. For the neighbor to have standing, she would need "
        "to establish privity through either (a) a valid assignment of rights from the homeowner, "
        "(b) a new contract formed directly between the neighbor and the painter, or (c) status "
        "as an intended third-party beneficiary. None of these apply: the assignment was invalid "
        "(as discussed above), no separate agreement was formed with the neighbor, and the "
        "original contract was not made for the neighbor's benefit. Without privity, the "
        "neighbor cannot maintain a breach of contract action.",
        COOL_BG))
    story.append(Paragraph(
        "You identified the right outcome but omitted the essential legal framework: "
        "<b>privity of contract</b>. A breach of contract claim requires a valid contract "
        "between the parties. You relied on circular reasoning -- 'the claim fails because "
        "there is no valid assignment' -- without explaining the legal standard for privity or "
        "demonstrating why the facts show no such relationship was created between the neighbor "
        "and the painter. The grader needs to see you name the doctrine (privity), state its "
        "requirements, and apply the facts to those requirements.",
        ST["body"]))
    story.append(action_box(
        "<b>Build this habit:</b> For any breach of contract claim, start with the threshold "
        "question: \"A breach of contract requires a valid contract between the parties "
        "(privity). Here, the neighbor and the painter had no contract because...\" This one "
        "sentence frames the entire analysis and tells the grader you know the foundational "
        "requirement."))

    # ================================================================
    # ISSUE 3: Neighbor's Liability to Painter
    # ================================================================
    story.append(thin_hr())
    story.append(Paragraph("NEEDS WORK", ST["badge_needs_work"]))
    story.append(Paragraph("Issue 3: Neighbor's Liability to Painter for Non-Payment", ST["h1"]))
    story.append(Paragraph("Your analysis:", ST["h2"]))
    story.append(shaded(
        "Presuming that the painter paints the neighbor's house, that performed party made "
        "substantial performance to complete the contract. When the painter substantially "
        "performed to paint the house under an implied in fact contract, then the painter is "
        "a valid party to the valid contract with the neighbor. A court would find that the "
        "painter will get the $6,000 because he substantially performed his part of the "
        "contract with the neighbor.",
        WARM_BG))
    story.append(Paragraph("Model analysis:", ST["h2"]))
    story.append(shaded(
        "There is no express contract between the painter and the neighbor. The painter may "
        "recover under two theories. First, <b>implied-in-fact contract</b>: if the neighbor "
        "knew the painter was painting her house and accepted the benefit without objecting, "
        "her conduct manifests assent to pay for the services. The elements are mutual assent "
        "shown through conduct and an expectation of compensation. Second, <b>unjust "
        "enrichment</b> (quasi-contract): the neighbor received a valuable benefit (a painted "
        "house), the painter conferred it expecting payment, and it would be unjust for the "
        "neighbor to retain the benefit without paying. Recovery would be the reasonable value "
        "of the services, which may differ from the $6,000 contract price.",
        COOL_BG))
    story.append(Paragraph(
        "<b>Misapplied doctrine:</b> Substantial performance determines whether a party has "
        "fulfilled enough of their obligations under an <i>existing</i> contract -- it does not "
        "create a contract where none existed. You used it as if completing the painting creates "
        "contractual privity with the neighbor. It does not. The correct framework is implied-in-"
        "fact contract (mutual assent through conduct) or unjust enrichment (quasi-contract). "
        "You must explain the requirements for an implied-in-fact contract -- specifically, the "
        "manifestation of mutual assent through conduct -- and apply the facts to those elements.",
        ST["body"]))
    story.append(action_box(
        "<b>Build this habit:</b> Before using a legal doctrine, define it in one sentence. "
        "\"Substantial performance means a party has completed enough of their duties to trigger "
        "the other side's obligation to pay.\" If that definition does not fit the situation "
        "you are analyzing -- as here, where no contract existed -- you need a different "
        "doctrine."))

    # ================================================================
    # ISSUE 4: Homeowner's Continuing Liability
    # ================================================================
    story.append(thin_hr())
    story.append(Paragraph("NEEDS WORK", ST["badge_needs_work"]))
    story.append(Paragraph("Issue 4: Homeowner's Continuing Liability to Painter", ST["h1"]))
    story.append(Paragraph("Your analysis:", ST["h2"]))
    story.append(shaded(
        "The painter would succeed in a contract claim against the homeowner because the "
        "homeowner transferred the contract obligations to the neighbor without the painter's "
        "consent. When a party transfers the rights to a third party without the other party "
        "knowing it, then there is a breach of contract.",
        WARM_BG))
    story.append(Paragraph("Model analysis:", ST["h2"]))
    story.append(shaded(
        "The homeowner is the original contracting party and remains liable. A <b>delegation "
        "of duties does not automatically release the delegator</b> from liability -- the "
        "delegator remains responsible unless there is a <b>novation</b>. A novation requires "
        "the express agreement of all three parties (homeowner, painter, and neighbor) to "
        "substitute the neighbor for the homeowner as the obligor. Here, the painter never "
        "agreed to release the homeowner -- in fact, the painter was unhappy about the change. "
        "Without a novation, the homeowner's obligation to pay $6,000 remains intact. The "
        "painter performed as the homeowner directed and can recover the full contract price.",
        COOL_BG))
    story.append(Paragraph(
        "You correctly identified that the painter can recover from the homeowner, but your "
        "analysis is purely conclusory. The core legal principle here is the <b>delegator's "
        "continuing liability</b>: when a party delegates duties to a third party, the "
        "delegator remains liable unless there is a novation. You failed to mention novation "
        "or the legal effect of the delegator's ongoing duty, which is the heart of this issue. "
        "The grader needs to see that you understand <i>why</i> the homeowner is still on the "
        "hook -- not just that he is.",
        ST["body"]))
    story.append(action_box(
        "<b>Build this habit:</b> When one party tries to hand off their obligations, always "
        "ask: \"Was there a novation?\" A novation requires all parties to agree to substitute "
        "a new obligor. If the answer is no, the original party remains liable. Name the "
        "doctrine, check the facts, and state the conclusion -- that sequence earns full marks."))

    # ================================================================
    # ISSUE 5: MISSED -- Third-Party Beneficiary
    # ================================================================
    story.append(thin_hr())
    story.append(Paragraph("MISSED ISSUE", ST["badge_missed"]))
    story.append(Paragraph("Issue 5: Retiree's Third-Party Beneficiary Status", ST["h1"]))
    story.append(Paragraph("Your analysis:", ST["h2"]))
    story.append(shaded(
        "The retiree would not succeed in a breach of contract action because the retiree is "
        "not a valid assignee or not part of the delegation of duties from the original parties. "
        "Although the retiree wanted both the homeowner and neighbor to paint their houses to "
        "increase their property values, the retiree is not part of the homeowner and the "
        "painter's original contract. The retiree has no interest whatsoever in the contracts "
        "between the homeowner and the painter.",
        WARM_BG))
    story.append(Paragraph("Model analysis:", ST["h2"]))
    story.append(shaded(
        "The retiree's claim depends on <b>third-party beneficiary status</b>, not assignment "
        "or delegation. A third-party beneficiary can enforce a contract if the contracting "
        "parties <b>intended</b> the performance to benefit the third party (an intended "
        "beneficiary). If the benefit is merely incidental, the third party has no standing. "
        "Here, the homeowner contracted to have his house painted for his own benefit. The "
        "retiree's benefit -- improved property values and neighborhood aesthetics -- is a "
        "byproduct, not the purpose of the contract. The retiree is an <b>incidental "
        "beneficiary</b> and therefore cannot enforce the contract against the painter.",
        COOL_BG))
    story.append(Paragraph(
        "<b>Critical omission:</b> You failed to address third-party beneficiary status "
        "entirely. This is the doctrine the question is testing for the retiree's claim, and "
        "you analyzed it through assignment and delegation instead -- which are the wrong "
        "frameworks. The retiree never received a transfer of rights from anyone. The question "
        "is whether the retiree was an <i>intended</i> or <i>incidental</i> beneficiary of the "
        "original contract. An intended beneficiary can sue; an incidental beneficiary cannot. "
        "You must learn to identify when a contract involves a third party and apply the rules "
        "regarding intended versus incidental beneficiaries. Spotting the correct doctrine is "
        "worth more than a full analysis of the wrong one.",
        ST["body"]))
    story.append(action_box(
        "<b>Build this habit:</b> When a non-party wants to enforce a contract, run through "
        "this checklist: (1) Are they an assignee? (2) Are they a delegatee? (3) Are they an "
        "intended third-party beneficiary? (4) Are they merely an incidental beneficiary? Each "
        "theory has different elements. Identify which theory fits before analyzing it. Here, "
        "the retiree is none of the first three -- they are incidental."))

    # ================================================================
    # CONCLUSORY vs. PROPER ANALYSIS
    # ================================================================
    story.append(Spacer(1, 4))
    story.append(HRFlowable(width="100%", thickness=0.8, color=GOLD, spaceAfter=4, spaceBefore=6))
    story.append(Paragraph("Conclusory Analysis vs. Proper Analysis", ST["h1"]))
    story.append(Paragraph(
        "Below are specific sentences from your essay rewritten to show the difference. The "
        "conclusory version states the outcome. The rewritten version explains <i>why</i> the "
        "facts lead there.",
        ST["body"]))

    # Rewrite 1
    story.append(Paragraph("<i>Issue 1 -- Assignment vs. delegation</i>", ST["h2"]))
    story.extend(rewrite_pair(
        "A proper assignment allows an original party to assign the contractual obligations "
        "to a third party. There was no valid assignment of the homeowner's contractual "
        "obligations to the neighbor.",
        "An assignment transfers a right to receive performance; a delegation transfers a duty "
        "to perform. Here, the homeowner attempted to redirect the painter's performance to a "
        "different house -- this materially changes the nature and location of the work. Because "
        "the painter never consented to this change, no valid assignment or delegation occurred, "
        "and the neighbor has no contractual relationship with the painter."
    ))

    # Rewrite 2
    story.append(Paragraph("<i>Issue 2 -- Privity of contract</i>", ST["h2"]))
    story.extend(rewrite_pair(
        "Because there is no valid assignment of the contract between the two original parties "
        "to the neighbor, there is no valid contract between the neighbor and the painter.",
        "A breach of contract claim requires privity -- a valid contract between the plaintiff "
        "and the defendant. The neighbor was not a party to the original homeowner-painter "
        "contract. No valid assignment created privity, no separate agreement was formed, and "
        "the neighbor was not an intended third-party beneficiary. Without privity, the "
        "neighbor's breach claim fails."
    ))

    # Rewrite 3
    story.append(Paragraph("<i>Issue 3 -- Implied contract, not substantial performance</i>", ST["h2"]))
    story.extend(rewrite_pair(
        "When the painter substantially performed to paint the house under an implied in fact "
        "contract, then the painter is a valid party to the valid contract with the neighbor.",
        "There is no express contract between the painter and the neighbor. However, if the "
        "neighbor knew the painting was happening and accepted the benefit without objecting, "
        "her conduct manifests assent to an implied-in-fact contract. Alternatively, the painter "
        "can recover under unjust enrichment: the neighbor received a valuable benefit and it "
        "would be unjust to keep it without paying."
    ))

    # Rewrite 4
    story.append(Paragraph("<i>Issue 4 -- Novation and delegator liability</i>", ST["h2"]))
    story.extend(rewrite_pair(
        "The painter would succeed because the homeowner transferred the contract obligations "
        "to the neighbor without the painter's consent.",
        "A delegation of duties does not release the delegator unless there is a novation -- an "
        "agreement among all parties to substitute a new obligor. The painter never agreed to "
        "release the homeowner. Without a novation, the homeowner remains liable for the full "
        "$6,000 contract price, and the painter can recover."
    ))

    # Rewrite 5
    story.append(Paragraph("<i>Issue 5 -- Incidental beneficiary</i>", ST["h2"]))
    story.extend(rewrite_pair(
        "The retiree has no interest whatsoever in the contracts between the homeowner and the "
        "painter and the homeowner and the neighbor.",
        "The retiree may have benefited from the painting (improved property values), but this "
        "benefit was incidental to the homeowner's purpose. The homeowner contracted to have "
        "his own house painted, not to benefit the retiree. As an incidental beneficiary, the "
        "retiree has no standing to enforce the contract."
    ))

    # ================================================================
    # CLOSING
    # ================================================================
    story.append(HRFlowable(width="100%", thickness=0.4, color=BORDER, spaceAfter=4, spaceBefore=6))
    story.append(Paragraph(
        "This essay shows that you are engaging with the material and attempting to use legal "
        "structure -- you organized by issue, used headings, and tried to follow IRAC. That "
        "structural foundation matters and is something many students struggle with. The gaps "
        "are in two areas that are fixable with focused practice.",
        ST["body"]))
    story.append(Paragraph(
        "First, <b>doctrinal precision</b>. You must distinguish assignment from delegation, "
        "know what substantial performance actually does (it evaluates performance under an "
        "existing contract, not create one), recognize novation as the mechanism that releases "
        "a delegator, and learn to identify third-party beneficiary questions. These are "
        "discrete rules you can drill -- and each one is a concept that appears repeatedly "
        "across MEE prompts.",
        ST["body"]))
    story.append(Paragraph(
        "Second, <b>analytical bridges</b>. Every conclusion needs a sentence that connects "
        "the specific facts to the specific elements of the rule. \"The neighbor cannot sue\" "
        "is a conclusion. \"The neighbor cannot sue because she was not a party to the contract, "
        "was not assigned any rights, and was not an intended beneficiary\" is analysis. The "
        "difference between those two sentences is the difference between a 2 and a 4.",
        ST["body"]))
    story.append(Spacer(1, 10))
    story.append(Paragraph("Bar Prep by SHEP  |  May 22, 2026", ST["footer"]))
    doc.build(story)
    return output_path


if __name__ == "__main__":
    path = build_contracts()
    print(f"Contracts: {path}")
