#!/usr/bin/env python3
"""
Generate Complete Master Architectural PDF Plan Book for 1:12 Scale Dollhouse.
Includes all technical specifications, schedules, joinery diagrams, step-by-step
assembly phases, exact 3D renders from dollhouse_visualizer.html,
and the complete 4' x 8' (35" + 35" + leftover) table saw cutting schedule.
"""

import os
import csv
from reportlab.lib.pagesizes import letter, landscape
from reportlab.lib import colors
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
    Image as RLImage, PageBreak, KeepTogether, HRFlowable
)
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.pdfgen import canvas

PDF_FILENAME = "1-12_Scale_Dollhouse_Master_Plans.pdf"
WORKSPACE_PDF = os.path.join(r"c:\code2026\house", PDF_FILENAME)
ARTIFACT_DIR = r"C:\Users\Hack\.gemini\antigravity\brain\4e8d02d4-938e-473d-bb3c-85a185863c59"
ARTIFACT_PDF = os.path.join(ARTIFACT_DIR, PDF_FILENAME)
IMAGES_DIR = r"c:\code2026\house\images"

# Exact 3D Visualizer Renders captured from dollhouse_visualizer.html
VISUALIZER_FRONT = os.path.join(IMAGES_DIR, "visualizer_3d_front_exterior.png")
VISUALIZER_REAR = os.path.join(IMAGES_DIR, "visualizer_3d_rear_open.png")
VISUALIZER_LANDSCAPE = os.path.join(IMAGES_DIR, "visualizer_3d_landscape_angle.png")

class NumberedCanvas(canvas.Canvas):
    """Two-pass canvas to dynamically compute and print 'Page X of Y' on all pages."""
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._saved_page_states = []

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

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

    def draw_page_decorations(self, page_count):
        if self._pageNumber == 1:
            return  # Suppress running header/footer on cover page

        self.saveState()
        self.setFont("Helvetica-Bold", 8)
        self.setFillColor(colors.HexColor("#1e293b"))
        
        # Running Header
        self.drawString(36, 582, "1:12 SCALE THREE-STORY VICTORIAN GOTHIC DOLLHOUSE")
        self.setFont("Helvetica", 8)
        self.setFillColor(colors.HexColor("#64748b"))
        self.drawRightString(756, 582, "MASTER ARCHITECTURAL SHOP PLANS & CUT SHEETS")
        
        self.setStrokeColor(colors.HexColor("#cbd5e1"))
        self.setLineWidth(0.75)
        self.line(36, 576, 756, 576)

        # Running Footer
        self.setStrokeColor(colors.HexColor("#cbd5e1"))
        self.setLineWidth(0.75)
        self.line(36, 40, 756, 40)
        
        self.setFont("Helvetica", 8)
        self.setFillColor(colors.HexColor("#64748b"))
        self.drawString(36, 28, "Scale: 1:12 (1\" = 1'0\" / 1 inch = 12 inches) • 1/4\" Baltic Birch Construction")
        
        page_str = f"Page {self._pageNumber} of {page_count}"
        self.setFont("Helvetica-Bold", 8)
        self.setFillColor(colors.HexColor("#1e3a8a"))
        self.drawRightString(756, 28, page_str)
        self.restoreState()


def build_pdf():
    doc = SimpleDocTemplate(
        WORKSPACE_PDF,
        pagesize=landscape(letter),
        leftMargin=36,
        rightMargin=36,
        topMargin=42,
        bottomMargin=46
    )

    styles = getSampleStyleSheet()

    # Custom Color Palette
    PRIMARY = colors.HexColor("#0f172a")      # Slate 900
    SECONDARY = colors.HexColor("#1e3a8a")    # Blue 900
    ACCENT_AMBER = colors.HexColor("#d97706") # Amber 600
    BORDER_COL = colors.HexColor("#cbd5e1")   # Slate 300
    BG_LIGHT = colors.HexColor("#f8fafc")     # Slate 50
    TEXT_DARK = colors.HexColor("#0f172a")    # Slate 900
    TEXT_MUTED = colors.HexColor("#475569")   # Slate 600

    # Typography Styles
    style_cover_title = ParagraphStyle(
        'CoverTitle',
        fontName='Helvetica-Bold',
        fontSize=24,
        leading=28,
        textColor=colors.HexColor("#ffffff"),
        alignment=1
    )
    style_cover_sub = ParagraphStyle(
        'CoverSub',
        fontName='Helvetica',
        fontSize=11,
        leading=14,
        textColor=colors.HexColor("#93c5fd"),
        alignment=1
    )
    style_cover_badge = ParagraphStyle(
        'CoverBadge',
        fontName='Helvetica-Bold',
        fontSize=9,
        leading=12,
        textColor=colors.HexColor("#ffffff"),
        alignment=1
    )

    style_h1 = ParagraphStyle(
        'Heading1_Custom',
        fontName='Helvetica-Bold',
        fontSize=15,
        leading=19,
        textColor=SECONDARY,
        spaceBefore=12,
        spaceAfter=6,
        keepWithNext=True
    )
    style_h2 = ParagraphStyle(
        'Heading2_Custom',
        fontName='Helvetica-Bold',
        fontSize=11,
        leading=15,
        textColor=PRIMARY,
        spaceBefore=8,
        spaceAfter=3,
        keepWithNext=True
    )
    style_body = ParagraphStyle(
        'Body_Custom',
        fontName='Helvetica',
        fontSize=8,
        leading=11.5,
        textColor=TEXT_DARK,
        spaceAfter=4
    )
    style_body_bold = ParagraphStyle(
        'BodyBold_Custom',
        fontName='Helvetica-Bold',
        fontSize=8.5,
        leading=12,
        textColor=TEXT_DARK,
        spaceAfter=4
    )
    style_callout = ParagraphStyle(
        'Callout_Custom',
        fontName='Helvetica',
        fontSize=8,
        leading=11,
        textColor=TEXT_MUTED,
        spaceAfter=3
    )
    style_th = ParagraphStyle(
        'TableHeader',
        fontName='Helvetica-Bold',
        fontSize=7.5,
        leading=9.5,
        textColor=colors.white,
        alignment=1
    )
    style_td = ParagraphStyle(
        'TableCell',
        fontName='Helvetica',
        fontSize=7.5,
        leading=9.5,
        textColor=TEXT_DARK
    )
    style_td_center = ParagraphStyle(
        'TableCellCenter',
        fontName='Helvetica',
        fontSize=7.5,
        leading=9.5,
        textColor=TEXT_DARK,
        alignment=1
    )
    style_td_bold = ParagraphStyle(
        'TableCellBold',
        fontName='Helvetica-Bold',
        fontSize=7.5,
        leading=9.5,
        textColor=TEXT_DARK
    )

    story = []

    # =========================================================================
    # PAGE 1: ARCHITECTURAL COVER PAGE WITH 3D VISUALIZER SPREAD
    # =========================================================================
    story.append(Spacer(1, 4))
    
    # Outer Cover Banner Box
    cover_data = [
        [Paragraph("VICTORIAN GOTHIC REVIVAL DOLLHOUSE (1:12 SCALE)", style_cover_title)],
        [Spacer(1, 2)],
        [Paragraph("Complete Workshop Master Architectural Plan Set  •  Interactive 3D WebGL Model Companion", style_cover_sub)],
        [Spacer(1, 3)],
        [Paragraph("<b>OVERALL CARCASS: 32.00\" WIDE × 12.00\" DEEP × 24.90\" PEAK HEIGHT (54.5° PITCH)</b>", style_cover_badge)],
    ]
    cover_table = Table(cover_data, colWidths=[720])
    cover_table.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,-1), colors.HexColor("#0f172a")),
        ('ALIGN', (0,0), (-1,-1), 'CENTER'),
        ('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
        ('TOPPADDING', (0,0), (-1,-1), 8),
        ('BOTTOMPADDING', (0,0), (-1,-1), 8),
        ('LEFTPADDING', (0,0), (-1,-1), 16),
        ('RIGHTPADDING', (0,0), (-1,-1), 16),
        ('ROUNDEDCORNERS', [4, 4, 4, 4]),
    ]))
    story.append(cover_table)
    story.append(Spacer(1, 6))

    # Hero 3D Spread from dollhouse_visualizer.html!
    img_front_rl = RLImage(VISUALIZER_FRONT, width=354, height=199)
    img_rear_rl = RLImage(VISUALIZER_REAR, width=354, height=199)
    
    hero_table_data = [
        [img_front_rl, img_rear_rl],
        [
            Paragraph("<b>3D Model: Front Gothic Facade, 45° Bay Window & Steep Gable</b>", style_td_center),
            Paragraph("<b>3D Model: Rear Open-Back Layout (3 Stories, 6 Rooms, Fireplace & Stairs)</b>", style_td_center)
        ]
    ]
    hero_table = Table(hero_table_data, colWidths=[358, 358])
    hero_table.setStyle(TableStyle([
        ('ALIGN', (0,0), (-1,-1), 'CENTER'),
        ('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
        ('TOPPADDING', (0,0), (-1,-1), 2),
        ('BOTTOMPADDING', (0,0), (-1,-1), 3),
        ('LEFTPADDING', (0,0), (-1,-1), 2),
        ('RIGHTPADDING', (0,0), (-1,-1), 2),
    ]))
    story.append(hero_table)
    story.append(Spacer(1, 6))

    # Key Architectural Specification Cards (Updated for 4' x 8' Pre-Cut Sheet)
    spec_cards = [
        [
            Paragraph("<b>PROJECT SPECIFICATIONS</b>", style_body_bold),
            Paragraph("<b>SCALE & HEADROOM</b>", style_body_bold),
            Paragraph("<b>MATERIALS & STOCK</b>", style_body_bold),
            Paragraph("<b>WORKSHOP CUT SHEETS</b>", style_body_bold),
        ],
        [
            Paragraph("• <b>Style:</b> Victorian Gothic Revival<br/>• <b>Layout:</b> 3 Stories, 6 Rooms + Attic<br/>• <b>Levels:</b> Base, 2nd Fl, Attic Deck<br/>• <b>Width:</b> 32\" (32.00\" Carcass)<br/>• <b>Depth:</b> 12\" (12.00\" Carcass)<br/>• <b>Peak Height:</b> 24.90\" (24-7/8\" to Ridge)", style_body),
            Paragraph("• <b>Scale:</b> 1:12 (1\" = 1'0\" True Scale)<br/>• <b>Level 1 Ceilings:</b> 8.00\" Clear Height<br/>• <b>Level 2 Ceilings:</b> 8.00\" Clear Height<br/>• <b>Finished Attic:</b> 8.40\" (8'5\" Headroom)<br/>• <b>Roof Pitch:</b> 54.5° Gable (16.8/12)<br/>• <b>Slope Run:</b> 12-1/16\" (12.04\")", style_body),
            Paragraph("• <b>Carcass Wood:</b> 1/4\" Baltic Birch<br/>• <b>Purchased Stock:</b> One (1) 4' × 8' Sheet<br/>• <b>Pre-Cut Sections:</b> 35\" + 35\" + Leftover (~26\")<br/>• <b>Joinery:</b> 1/8\" D × 1/4\" W Dados & Rabbets<br/>• <b>Saw Method:</b> 4-Fence Table Saw Batch<br/>• <b>Bonus Yield:</b> 2.75+ sq ft for Dog House!", style_body),
            Paragraph("• <b>Blueprints:</b> 11 High-Res Cut Sheets<br/>• <b>3D Interactive:</b> dollhouse_visualizer.html<br/>• <b>Windows:</b> 7 Identical 2-1/4\"×5\" (1/8\" flange)<br/>• <b>Front Door:</b> Birch 2-3/4\"×6-3/4\" (Flush)<br/>• <b>Rear Roof:</b> 27\" × 8.40\" Peek-In Cutout<br/>• <b>Stairs:</b> 2\" W Lands at Z=6\" (mid-foyer)", style_body),
        ]
    ]
    card_table = Table(spec_cards, colWidths=[180, 180, 180, 180])
    card_table.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,-1), BG_LIGHT),
        ('BOX', (0,0), (-1,-1), 1, BORDER_COL),
        ('INNERGRID', (0,0), (-1,-1), 0.5, BORDER_COL),
        ('TOPPADDING', (0,0), (-1,-1), 5),
        ('BOTTOMPADDING', (0,0), (-1,-1), 5),
        ('LEFTPADDING', (0,0), (-1,-1), 8),
        ('RIGHTPADDING', (0,0), (-1,-1), 8),
    ]))
    story.append(card_table)
    story.append(PageBreak())

    # =========================================================================
    # PAGE 2: 3D MODEL VISUALIZATION & ARCHITECTURAL OVERVIEW
    # =========================================================================
    story.append(Paragraph("1. Interactive 3D Model Architecture & Spatial Analysis", style_h1))
    story.append(Paragraph(
        "Rendered directly from the companion interactive WebGL engine (<code>dollhouse_visualizer.html</code>), "
        "the dollhouse implements a balanced dual-axis architectural plan: equal room spans, aligned window centers, "
        "and clear spatial flow between living zones.",
        style_body
    ))
    story.append(Spacer(1, 4))

    img_land_rl = RLImage(VISUALIZER_LANDSCAPE, width=440, height=247)
    land_box = [
        [
            img_land_rl,
            Paragraph(
                "<b>KEY SPATIAL FEATURES IN 3D:</b><br/><br/>"
                "• <b>Equal Room Spans (31.00\" Net Width):</b><br/>"
                "&nbsp;&nbsp;1st Floor: Living (11.50\") + Kitchen (11.50\") + Foyer (8.00\") = 31.00\"<br/>"
                "&nbsp;&nbsp;2nd Floor: Bathroom (6.00\") + Women (12.50\") + Mans (12.50\") = 31.00\"<br/><br/>"
                "• <b>Foyer Ergonomics:</b><br/>"
                "&nbsp;&nbsp;The 2\" wide staircase lands in the middle of room depth ($Z = 6.00\"$) and ascends forward toward the front wall ($Z = 0.00\"$). "
                "The Foyer window is positioned at $Z = 9.00\"$, leaving an exact 1-7/8\" clear margin forward of the stairs.<br/><br/>"
                "• <b>Rear Roof Peek-In Cutout:</b><br/>"
                "&nbsp;&nbsp;A 27\" wide × 8.40\" high triangular opening in rear panel E2 restores the steep gable roofline from behind while allowing direct hands-on access to the attic floor.",
                style_body
            )
        ]
    ]
    t_land = Table(land_box, colWidths=[445, 275])
    t_land.setStyle(TableStyle([
        ('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
        ('TOPPADDING', (0,0), (-1,-1), 4),
        ('BOTTOMPADDING', (0,0), (-1,-1), 4),
        ('LEFTPADDING', (0,0), (-1,-1), 6),
        ('RIGHTPADDING', (0,0), (-1,-1), 6),
        ('BACKGROUND', (0,0), (-1,-1), BG_LIGHT),
        ('BOX', (0,0), (-1,-1), 1, BORDER_COL),
    ]))
    story.append(t_land)
    story.append(Spacer(1, 8))

    # =========================================================================
    # EXECUTIVE SUMMARY & MASTER DIMENSION SCHEDULE
    # =========================================================================
    story.append(Paragraph("Master Dimension Schedule (Dual-Unit Quick Reference)", style_h2))
    dim_headers = [
        Paragraph("Architectural Feature", style_th),
        Paragraph("Decimal (Inches)", style_th),
        Paragraph("Ruler Fraction", style_th),
        Paragraph("1:12 Real Scale", style_th),
        Paragraph("Engineering Function & Architectural Specification", style_th),
    ]
    dim_rows = [
        dim_headers,
        [Paragraph("Overall Carcass Width", style_td_bold), Paragraph("32.000\"", style_td_center), Paragraph("32\"", style_td_center), Paragraph("32 ft 0 in", style_td_center), Paragraph("Base plate A1, 2nd Fl A2, Attic A3, Front Facade C1", style_td)],
        [Paragraph("Overall Carcass Depth", style_td_bold), Paragraph("12.000\"", style_td_center), Paragraph("12\"", style_td_center), Paragraph("12 ft 0 in", style_td_center), Paragraph("Depth of floor decks and side gable walls B1 & B2", style_td)],
        [Paragraph("Total Height to Roof Ridge", style_td_bold), Paragraph("24.900\"", style_td_center), Paragraph("24-7/8\"", style_td_center), Paragraph("24 ft 11 in", style_td_center), Paragraph("Baseline to apex (16-1/2\" eave line + 8.40\" peak rise)", style_td)],
        [Paragraph("Level 1 Clear Ceiling", style_td_bold), Paragraph("8.000\"", style_td_center), Paragraph("8\"", style_td_center), Paragraph("8 ft 0 in", style_td_center), Paragraph("Top face of A1 to underside face of A2 (8.25\" partitions)", style_td)],
        [Paragraph("Level 2 Clear Ceiling", style_td_bold), Paragraph("8.000\"", style_td_center), Paragraph("8\"", style_td_center), Paragraph("8 ft 0 in", style_td_center), Paragraph("Top face of A2 to underside face of A3 (8.25\" partitions)", style_td)],
        [Paragraph("Attic Peak Headroom", style_td_bold), Paragraph("8.400\"", style_td_center), Paragraph("8-3/8\"", style_td_center), Paragraph("8 ft 5 in", style_td_center), Paragraph("Cathedral headroom above deck A3 (exact 20% reduction from 10.50\")", style_td)],
        [Paragraph("Roof Slope Angle (Pitch)", style_td_bold), Paragraph("54.462°", style_td_center), Paragraph("54.5°", style_td_center), Paragraph("16.8 / 12 Pitch", style_td_center), Paragraph("Proportional gable pitch: arctan(8.40\" rise / 6.00\" run)", style_td)],
        [Paragraph("Roof Slope Rafter Run", style_td_bold), Paragraph("12.043\"", style_td_center), Paragraph("12-1/16\"", style_td_center), Paragraph("12 ft 0-1/2 in", style_td_center), Paragraph("With 1.00\" horizontal eave overhang: 7.00\" / cos(54.462°)", style_td)],
        [Paragraph("Front Roof Panel (E1)", style_td_bold), Paragraph("32.00\" × 12.04\"", style_td_center), Paragraph("32\" × 12-1/16\"", style_td_center), Paragraph("32 ft 0 in × 12 ft 1/2 in", style_td_center), Paragraph("Continuous unbroken front roof sheathing (flush with 32.00\" carcass gables)", style_td)],
        [Paragraph("Rear Roof Panel (E2)", style_td_bold), Paragraph("32.00\" × 12.04\"", style_td_center), Paragraph("32\" × 12-1/16\"", style_td_center), Paragraph("32 ft 0 in × 12 ft 1/2 in", style_td_center), Paragraph("27\" base × 8.40\" alt triangular cutout framing rear attic with hands-on reach (flush with gables)", style_td)],
        [Paragraph("Living Room Clear Width", style_td_bold), Paragraph("11.500\"", style_td_center), Paragraph("11-1/2\"", style_td_center), Paragraph("11 ft 6 in", style_td_center), Paragraph("From left wall inner face to divider D1-A; 2-1/4\" x 5\" window", style_td)],
        [Paragraph("Kitchen Clear Width", style_td_bold), Paragraph("11.500\"", style_td_center), Paragraph("11-1/2\"", style_td_center), Paragraph("11 ft 6 in", style_td_center), Paragraph("Between D1-A and D1-B; identical width to Living Room!", style_td)],
        [Paragraph("Foyer Clear Width", style_td_bold), Paragraph("8.000\"", style_td_center), Paragraph("8\"", style_td_center), Paragraph("8 ft 0 in", style_td_center), Paragraph("Between D1-B and right gable wall; houses 5\" fireplace & stairs", style_td)],
        [Paragraph("Bathroom Clear Width", style_td_bold), Paragraph("6.000\"", style_td_center), Paragraph("6\"", style_td_center), Paragraph("6 ft 0 in", style_td_center), Paragraph("Expanded 6\" width accommodates clawfoot tub along front wall", style_td)],
        [Paragraph("Women's Bedroom Width", style_td_bold), Paragraph("12.500\"", style_td_center), Paragraph("12-1/2\"", style_td_center), Paragraph("12 ft 6 in", style_td_center), Paragraph("Between D2-A and D2-B; window centered with equal 5-1/8\" borders", style_td)],
        [Paragraph("Man's Bedroom Width", style_td_bold), Paragraph("12.500\"", style_td_center), Paragraph("12-1/2\"", style_td_center), Paragraph("12 ft 6 in", style_td_center), Paragraph("Between D2-B and right wall; window aligns over front door", style_td)],
        [Paragraph("Staircase Assembly", style_td_bold), Paragraph("2.00\" W × 6.00\" L", style_td_center), Paragraph("2\" W × 6\" L", style_td_center), Paragraph("2 ft W × 6 ft L", style_td_center), Paragraph("12 risers (11/16\" rise, 1/2\" run); lands in middle of 1st floor (Z=6\")", style_td)],
    ]
    t_dim = Table(dim_rows, colWidths=[120, 75, 75, 75, 375])
    t_dim.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,0), SECONDARY),
        ('BOX', (0,0), (-1,-1), 1, BORDER_COL),
        ('INNERGRID', (0,0), (-1,-1), 0.5, BORDER_COL),
        ('ROWBACKGROUNDS', (0,1), (-1,-1), [colors.white, BG_LIGHT]),
        ('TOPPADDING', (0,0), (-1,-1), 2.5),
        ('BOTTOMPADDING', (0,0), (-1,-1), 2.5),
        ('LEFTPADDING', (0,0), (-1,-1), 5),
        ('RIGHTPADDING', (0,0), (-1,-1), 5),
        ('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
    ]))
    story.append(t_dim)
    story.append(PageBreak())

    # =========================================================================
    # PAGE 3: MASTER CUTOUT SCHEDULE & GROOVE/RABBET SPECIFICATIONS
    # =========================================================================
    story.append(Paragraph("2. Master Rough Opening & Cartesian Cutout Schedule", style_h1))
    story.append(Paragraph(
        "Every opening is indexed from the <b>bottom-left corner</b> of the respective flat panel before assembly. "
        "Partitions (D1 & D2) have cutouts starting at <b>Y = 1/8\" (0.125\")</b> above the bottom edge so that walkthroughs "
        "and interior doors finish flush with the floor after seating into their deck grooves.",
        style_body
    ))
    story.append(Spacer(1, 4))

    cutout_headers = [
        Paragraph("Panel ID", style_th),
        Paragraph("Feature / Opening", style_th),
        Paragraph("Cutout (W × H)", style_th),
        Paragraph("Decimal Cutout", style_th),
        Paragraph("Horizontal Range (X/Z)", style_th),
        Paragraph("Vertical Range (Y)", style_th),
        Paragraph("Centerline", style_th),
        Paragraph("Shop Notes & Architectural Purpose", style_th),
    ]
    cutout_rows = [
        cutout_headers,
        [Paragraph("C1 (Front)", style_td_bold), Paragraph("Living Room Window", style_td), Paragraph("2-1/4\" × 5\"", style_td_center), Paragraph("2.25\" × 5.00\"", style_td_center), Paragraph("X = 4-7/8\" to 7-1/8\"", style_td_center), Paragraph("Y = 1-1/2\" to 6-1/2\"", style_td_center), Paragraph("X = 6.00\"", style_td_center), Paragraph("Standard 1:12 window; 1/8\" exterior flange", style_td)],
        [Paragraph("C1 (Front)", style_td_bold), Paragraph("Kitchen Window", style_td), Paragraph("2-1/4\" × 5\"", style_td_center), Paragraph("2.25\" × 5.00\"", style_td_center), Paragraph("X = 14-7/8\" to 17-1/8\"", style_td_center), Paragraph("Y = 1-1/2\" to 6-1/2\"", style_td_center), Paragraph("X = 16.00\"", style_td_center), Paragraph("Centered on house centerline; standard 1:12 window", style_td)],
        [Paragraph("C1 (Front)", style_td_bold), Paragraph("Birch Front Door", style_td), Paragraph("2-3/4\" × 6-3/4\"", style_td_center), Paragraph("2.75\" × 6.75\"", style_td_center), Paragraph("X = 25-1/8\" to 27-7/8\"", style_td_center), Paragraph("Y = 0\" to 6-3/4\"", style_td_center), Paragraph("X = 26.50\"", style_td_center), Paragraph("Flush with base plate; authentic 6-panel door", style_td)],
        [Paragraph("C1 (Front)", style_td_bold), Paragraph("Bathroom Window", style_td), Paragraph("2-1/4\" × 5\"", style_td_center), Paragraph("2.25\" × 5.00\"", style_td_center), Paragraph("X = 2-1/8\" to 4-3/8\"", style_td_center), Paragraph("Y = 9-3/4\" to 14-3/4\"", style_td_center), Paragraph("X = 3.25\"", style_td_center), Paragraph("Centered over clawfoot tub in 6\" bathroom", style_td)],
        [Paragraph("C1 (Front)", style_td_bold), Paragraph("Women Room Window", style_td), Paragraph("2-1/4\" × 5\"", style_td_center), Paragraph("2.25\" × 5.00\"", style_td_center), Paragraph("X = 11-5/8\" to 13-7/8\"", style_td_center), Paragraph("Y = 9-3/4\" to 14-3/4\"", style_td_center), Paragraph("X = 12.75\"", style_td_center), Paragraph("Centered in 12-1/2\" room with equal 5-1/8\" borders!", style_td)],
        [Paragraph("C1 (Front)", style_td_bold), Paragraph("Mans Room Window", style_td), Paragraph("2-1/4\" × 5\"", style_td_center), Paragraph("2.25\" × 5.00\"", style_td_center), Paragraph("X = 25-3/8\" to 27-5/8\"", style_td_center), Paragraph("Y = 9-3/4\" to 14-3/4\"", style_td_center), Paragraph("X = 26.50\"", style_td_center), Paragraph("Vertical alignment directly above Front Door below!", style_td)],
        [Paragraph("B1 (Left)", style_td_bold), Paragraph("Living Side Window", style_td), Paragraph("2-1/4\" × 5\"", style_td_center), Paragraph("2.25\" × 5.00\"", style_td_center), Paragraph("Z = 4-7/8\" to 7-1/8\"", style_td_center), Paragraph("Y = 1-1/2\" to 6-1/2\"", style_td_center), Paragraph("Z = 6.00\"", style_td_center), Paragraph("Centered in room depth; 2nd floor above is SOLID", style_td)],
        [Paragraph("B2 (Right)", style_td_bold), Paragraph("Foyer Side Window", style_td), Paragraph("2-1/4\" × 5\"", style_td_center), Paragraph("2.25\" × 5.00\"", style_td_center), Paragraph("Z = 7-7/8\" to 10-1/8\"", style_td_center), Paragraph("Y = 1-1/2\" to 6-1/2\"", style_td_center), Paragraph("Z = 9.00\"", style_td_center), Paragraph("Moved back! Leaves 1-7/8\" clear of stairs landing", style_td)],
        [Paragraph("D1-A (Div)", style_td_bold), Paragraph("Living ↔ Kitchen Walk", style_td), Paragraph("3\" × 6-1/2\"", style_td_center), Paragraph("3.00\" × 6.50\"", style_td_center), Paragraph("Z = 1/2\" to 3-1/2\"", style_td_center), Paragraph("Y = 1/8\" to 6-5/8\"", style_td_center), Paragraph("Z = 2.00\"", style_td_center), Paragraph("Open cased walkthrough; no door slab on 1st floor", style_td)],
        [Paragraph("D1-B (Div)", style_td_bold), Paragraph("Kitchen ↔ Foyer Walk", style_td), Paragraph("3\" × 6-1/2\"", style_td_center), Paragraph("3.00\" × 6.50\"", style_td_center), Paragraph("Z = 7-1/2\" to 10-1/2\"", style_td_center), Paragraph("Y = 1/8\" to 6-5/8\"", style_td_center), Paragraph("Z = 9.00\"", style_td_center), Paragraph("At rear; leaves front (Z=1/2\" to 5-1/2\") for 5\" fireplace", style_td)],
        [Paragraph("D2-A (Div)", style_td_bold), Paragraph("Bath ↔ Women Door", style_td), Paragraph("3-7/16\" × 6-5/16\"", style_td_center), Paragraph("3.46\" × 6.29\"", style_td_center), Paragraph("Z = 3-1/2\" to 7\"", style_td_center), Paragraph("Y = 1/8\" to 6-7/16\"", style_td_center), Paragraph("Z = 5.25\"", style_td_center), Paragraph("Cutout for 1:12 inside door; tub at front, toilet at rear", style_td)],
        [Paragraph("D2-B (Div)", style_td_bold), Paragraph("Women ↔ Mans Door", style_td), Paragraph("3-7/16\" × 6-5/16\"", style_td_center), Paragraph("3.46\" × 6.29\"", style_td_center), Paragraph("Z = 1\" to 4-7/16\"", style_td_center), Paragraph("Y = 1/8\" to 6-7/16\"", style_td_center), Paragraph("Z = 2.72\"", style_td_center), Paragraph("Cutout for 1:12 inside door connecting bedrooms", style_td)],
        [Paragraph("A2 (Deck)", style_td_bold), Paragraph("Mans Stair Well Open", style_td), Paragraph("2-1/4\" × 6\"", style_td_center), Paragraph("2.25\" × 6.00\"", style_td_center), Paragraph("X = 29-1/2\" to 31-3/4\"", style_td_center), Paragraph("Z = 0\" to 6\" (Front)", style_td_center), Paragraph("—", style_td_center), Paragraph("Stair opening flushed against front wall and right gable", style_td)],
        [Paragraph("E2 (Roof)", style_td_bold), Paragraph("Attic Triangular Cutout", style_td), Paragraph("27\" Base × 8.40\" Alt", style_td_center), Paragraph("27.00\" × 8.40\"", style_td_center), Paragraph("X = 3-1/2\" to 30-1/2\"", style_td_center), Paragraph("Slope 0\" to 8.40\"", style_td_center), Paragraph("X = 17.00\"", style_td_center), Paragraph("Preserves roofline & eaves from back view while providing full hands-on reach into attic", style_td)],
    ]
    t_cut = Table(cutout_rows, colWidths=[65, 95, 75, 70, 95, 95, 60, 165])
    t_cut.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,0), SECONDARY),
        ('BOX', (0,0), (-1,-1), 1, BORDER_COL),
        ('INNERGRID', (0,0), (-1,-1), 0.5, BORDER_COL),
        ('ROWBACKGROUNDS', (0,1), (-1,-1), [colors.white, BG_LIGHT]),
        ('TOPPADDING', (0,0), (-1,-1), 2.5),
        ('BOTTOMPADDING', (0,0), (-1,-1), 2.5),
        ('LEFTPADDING', (0,0), (-1,-1), 4),
        ('RIGHTPADDING', (0,0), (-1,-1), 4),
        ('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
    ]))
    story.append(t_cut)
    story.append(Spacer(1, 10))

    # Groove & Dado Schedule Table
    story.append(Paragraph("<b>Table Saw / Router Fence Settings for 1/8\" (0.125\") Grooves:</b>", style_h2))
    groove_data = [
        [Paragraph("Deck Panel", style_th), Paragraph("Face", style_th), Paragraph("Target Partition", style_th), Paragraph("Ruler Range (X)", style_th), Paragraph("Centerline", style_th), Paragraph("Blade Height", style_th), Paragraph("Fence Setting & Setup Instructions", style_th)],
        [Paragraph("A1 (Base)", style_td_bold), Paragraph("Top Face", style_td_center), Paragraph("D1-A (Living/Kit)", style_td), Paragraph("11-3/4\" to 12\"", style_td_center), Paragraph("11-7/8\"", style_td_center), Paragraph("1/8\" (0.125\")", style_td_center), Paragraph("Fence at 11-3/4\". Two passes with 1/8\" blade yield 1/4\" width.", style_td)],
        [Paragraph("A1 (Base)", style_td_bold), Paragraph("Top Face", style_td_center), Paragraph("D1-B (Kit/Foyer)", style_td), Paragraph("23-1/2\" to 23-3/4\"", style_td_center), Paragraph("23-5/8\"", style_td_center), Paragraph("1/8\" (0.125\")", style_td_center), Paragraph("Fence at 23-1/2\". Two passes with 1/8\" blade yield 1/4\" width.", style_td)],
        [Paragraph("A2 (2nd Fl)", style_td_bold), Paragraph("Underside", style_td_center), Paragraph("D1-A Top Edge", style_td), Paragraph("11-3/4\" to 12\"", style_td_center), Paragraph("11-7/8\"", style_td_center), Paragraph("1/8\" (0.125\")", style_td_center), Paragraph("Fence at 11-3/4\". Ceiling channel locks D1-A top edge square.", style_td)],
        [Paragraph("A2 (2nd Fl)", style_td_bold), Paragraph("Underside", style_td_center), Paragraph("D1-B Top Edge", style_td), Paragraph("23-1/2\" to 23-3/4\"", style_td_center), Paragraph("23-5/8\"", style_td_center), Paragraph("1/8\" (0.125\")", style_td_center), Paragraph("Fence at 23-1/2\". Ceiling channel locks D1-B top edge square.", style_td)],
        [Paragraph("A2 (2nd Fl)", style_td_bold), Paragraph("Top Face", style_td_center), Paragraph("D2-A (Bath/Women)", style_td), Paragraph("6-1/4\" to 6-1/2\"", style_td_center), Paragraph("6-3/8\"", style_td_center), Paragraph("1/8\" (0.125\")", style_td_center), Paragraph("Fence at 6-1/4\". 2nd floor flooring channel for 6\" bathroom wall.", style_td)],
        [Paragraph("A2 (2nd Fl)", style_td_bold), Paragraph("Top Face", style_td_center), Paragraph("D2-B (Women/Mans)", style_td), Paragraph("19\" to 19-1/4\"", style_td_center), Paragraph("19-1/8\"", style_td_center), Paragraph("1/8\" (0.125\")", style_td_center), Paragraph("Fence at 19-0\". Creates equal 12-1/2\" width for both bedrooms.", style_td)],
        [Paragraph("A3 (Attic)", style_td_bold), Paragraph("Underside", style_td_center), Paragraph("D2-A Top Edge", style_td), Paragraph("6-1/4\" to 6-1/2\"", style_td_center), Paragraph("6-3/8\"", style_td_center), Paragraph("1/8\" (0.125\")", style_td_center), Paragraph("Fence at 6-1/4\". Locks D2-A ceiling into underside of attic deck.", style_td)],
        [Paragraph("A3 (Attic)", style_td_bold), Paragraph("Underside", style_td_center), Paragraph("D2-B Top Edge", style_td), Paragraph("19\" to 19-1/4\"", style_td_center), Paragraph("19-1/8\"", style_td_center), Paragraph("1/8\" (0.125\")", style_td_center), Paragraph("Fence at 19-0\". Locks D2-B ceiling into underside of attic deck.", style_td)],
    ]
    t_groove = Table(groove_data, colWidths=[70, 60, 95, 80, 65, 65, 285])
    t_groove.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,0), PRIMARY),
        ('BOX', (0,0), (-1,-1), 1, BORDER_COL),
        ('INNERGRID', (0,0), (-1,-1), 0.5, BORDER_COL),
        ('ROWBACKGROUNDS', (0,1), (-1,-1), [colors.white, BG_LIGHT]),
        ('TOPPADDING', (0,0), (-1,-1), 2.5),
        ('BOTTOMPADDING', (0,0), (-1,-1), 2.5),
        ('LEFTPADDING', (0,0), (-1,-1), 4),
        ('RIGHTPADDING', (0,0), (-1,-1), 4),
        ('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
    ]))
    story.append(t_groove)
    story.append(PageBreak())

    # =========================================================================
    # BLUEPRINT GALLERY: CUTS 01 TO 11 (ONE DRAWING PER PAGE)
    # =========================================================================
    blueprints = [
        ("cut_01_A1_base_deck.png", "Cut Sheet 01 — Base Platform Deck A1 (Level 1)", "32\" × 12\" (32.00\" × 12.00\")  |  Top Grooves & Outer End Rabbets"),
        ("cut_02_A2_2nd_floor_deck.png", "Cut Sheet 02 — Second Floor Deck A2 (Dual-Sided)", "32\" × 12\" (32.00\" × 12.00\")  |  Top Grooves, Underside Ceiling Grooves & 2-1/4\" × 6\" Stair Opening"),
        ("cut_03_A3_attic_floor_deck.png", "Cut Sheet 03 — Attic Floor Deck A3 (Level 3)", "32\" × 12\" (32.00\" × 12.00\")  |  Underside Ceiling Grooves & Solid Finished Attic Deck"),
        ("cut_04_B1_left_gable_wall.png", "Cut Sheet 04 — Left Exterior Gable Side Wall B1", "12\" × 24-7/8\" (12.00\" × 24.90\")  |  16-1/2\" Eave + 8.40\" Peak Rise (54.5° Pitch), Deck Dados & 2-1/4\" × 5\" Living Window"),
        ("cut_05_B2_right_gable_wall.png", "Cut Sheet 05 — Right Exterior Gable Side Wall B2", "12\" × 24-7/8\" (12.00\" × 24.90\")  |  16-1/2\" Eave + 8.40\" Peak Rise, Deck Dados & Offset Foyer Window (Z=9\")"),
        ("cut_06_C1_front_facade.png", "Cut Sheet 06 — Front Facade Wall Plate C1", "32\" × 16-1/2\" (32.00\" × 16.50\")  |  5 Aligned Windows (2-1/4\" × 5\"), Birch Door (2-3/4\" × 6-3/4\") & Outer Rabbet Lock"),
        ("cut_07_D1_1st_fl_partitions.png", "Cut Sheet 07 — 1st Floor Partitions D1-A & D1-B", "12\" × 8-1/4\" (12.00\" × 8.25\")  |  8.00\" Clear Height + 1/8\" Tongues, Cased Walkthroughs (No Doors)"),
        ("cut_08_D2_2nd_fl_partitions.png", "Cut Sheet 08 — 2nd Floor Partitions D2-A & D2-B", "12\" × 8-1/4\" (12.00\" × 8.25\")  |  8.00\" Clear Height + 1/8\" Tongues, Authentic 1:12 Inside Door Openings"),
        ("cut_09_E1_E2_roof_panels.png", "Cut Sheet 09 — Main Roof Slope Panels E1 & E2", "32\" × 12-1/16\" (32.00\" × 12.04\")  |  Front Solid E1 & Rear E2 with 27\" × 8.40\" Hands-On Attic Cutout"),
        ("cut_10_master_interlock_overview.png", "Cut Sheet 10 — Master Carcass Interlock & Cross-Section", "System Exploded Assembly  |  Monolithic 1/8\" Interlocking Joinery (24.90\" Carcass Height)"),
        ("cut_11_plywood_4x8_sheet_layout.png", "Cut Sheet 11 — Master Project Panel Nesting & Workshop Layout", "All 12 Carcass Panels on 1/4\" Baltic Birch Stock with Dedicated Craft Offcuts"),
    ]

    for img_file, title_text, sub_text in blueprints:
        img_path = os.path.join(IMAGES_DIR, img_file)
        if not os.path.exists(img_path):
            continue

        story.append(Paragraph(title_text, style_h1))
        story.append(Paragraph(sub_text, style_callout))
        story.append(Spacer(1, 4))

        # Scaled to fit landscape page with maximum resolution: 690 pt x 440 pt
        img_rl = RLImage(img_path, width=690, height=440)
        story.append(img_rl)
        story.append(PageBreak())

    # =========================================================================
    # PAGE: MASTER 10-PHASE STEP-BY-STEP CONSTRUCTION ORDER
    # =========================================================================
    story.append(Paragraph("3. Master 10-Phase Workshop Construction & Build Sequence", style_h1))
    story.append(Paragraph(
        "To achieve seamless heirloom alignment and prevent trapped work, follow this proven 10-phase sequence. "
        "<b>Rule #1 of Miniature Joinery:</b> Perform all grooving, rabbeting, window cutouts, and interior painting FLAT on the bench before assembly!",
        style_body
    ))
    story.append(Spacer(1, 4))

    phases = [
        ("Phase 1: Stock Preparation & Breakdown", "Break down your 4' × 8' Baltic birch plywood sheet into rectangular blanks according to Section 4: Sheet 1 (E1 + E2 + C1), Sheet 2 (A1 + A2 + A3 + D1-A + D1-B), and Sheet 3 (B1 + B2 + D2-A + D2-B + Bonus Stock). Label FRONT reference edge on all pieces."),
        ("Phase 2: Bench Grooving & Piercing", "Route 1/8\" deep × 1/4\" wide grooves on top of A1, top/underside of A2, and underside of A3 per table saw fence schedule. Route 1/4\" W × 1/8\" D vertical corner rabbets on B1/B2 and deck dados. Pierce all window and door rough openings using jigsaw/scroll saw."),
        ("Phase 3: Pre-Finishing & Dry Fit", "Sand all interior faces to 220 grit. Paint interior walls, apply wallpaper, stain wood floor deck boards, and paint ceiling undersides BEFORE assembly. Perform a full dry-fit without glue to verify tongue-in-groove engagement across all stories."),
        ("Phase 4: Base Foundation & Watertable", "Prepare separate 48\" × 32\" grounds plinth A0 (1/2\" MDF recommended). Build 1/2\" stone watertable perimeter rim under house base plate A1 (32\" × 12\"). Glue base plate to plinth."),
        ("Phase 5: 1st Floor Carcass & Partition Slide-In", "Apply wood glue into vertical corner rabbets of B1/B2 and seat Front Facade C1. Seat B1/B2 over A1 bottom end rabbets; clamp 90° square. Slide Living/Kitchen divider D1-A and Kitchen/Foyer divider D1-B into top grooves of A1 from the open rear until they meet C1."),
        ("Phase 6: Fireplace & Staircase Installation", "Build 5\" W × 1-1/2\" D × 4-1/2\" H fireplace on the bench; glue against kitchen divider D1-B facing Foyer. Assemble 2\" wide staircase flight (12 steps, 11/16\" rise, 1/2\" run); glue against right gable B2 (bottom step lands at Z=6\", mid-depth; top step reaches front wall at Z=0\")."),
        ("Phase 7: 2nd Floor Deck & Partitions", "Seat 2nd Floor Deck A2 onto carcass, sliding underside ceiling grooves over D1-A and D1-B. Slide Bath/Women divider D2-A and Women/Mans divider D2-B into top grooves of A2. Pre-hang authentic inside doors ($12.47). Install bathroom suite (clawfoot tub along front wall, sink, toilet)."),
        ("Phase 8: Attic Deck & Cathedral Roof", "Install Attic Deck A3, seating underside grooves over D2-A and D2-B. Glue continuous front roof panel E1 (32\" × 12-1/16\") across rafters flush with carcass gables. Glue rear roof panel E2 (32\" × 12-1/16\") with 27\" × 8.40\" triangular cutout framing cathedral attic with full hands-on accessibility."),
        ("Phase 9: Windows, Exterior Doors & Trim", "Install authentic Birch Front Door (2-3/4\" × 6-3/4\") with 2-tier entry steps (NO PORCH). Install 7 identical 2-1/4\" × 5\" standard window units with 1/8\" exterior flanges. Install Alomejor 45° three-sided bay window in Living Room. Apply vertical corner trim."),
        ("Phase 10: Exterior Grounds & Final Inspection", "Landscape Level 0 plinth (48\" × 32\") with grass turf, flagstone path, garden beds, and side garden bench. Install 1:12 outdoor dog house on backyard lawn!"),
    ]

    phase_rows = [[Paragraph("Build Phase", style_th), Paragraph("Workshop Instructions & Quality Control Directives", style_th)]]
    for p_title, p_desc in phases:
        phase_rows.append([
            Paragraph(f"<b>{p_title}</b>", style_td_bold),
            Paragraph(p_desc, style_td)
        ])

    t_phase = Table(phase_rows, colWidths=[180, 540])
    t_phase.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,0), PRIMARY),
        ('BOX', (0,0), (-1,-1), 1, BORDER_COL),
        ('INNERGRID', (0,0), (-1,-1), 0.5, BORDER_COL),
        ('ROWBACKGROUNDS', (0,1), (-1,-1), [colors.white, BG_LIGHT]),
        ('TOPPADDING', (0,0), (-1,-1), 3),
        ('BOTTOMPADDING', (0,0), (-1,-1), 3),
        ('LEFTPADDING', (0,0), (-1,-1), 5),
        ('RIGHTPADDING', (0,0), (-1,-1), 5),
        ('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
    ]))
    story.append(t_phase)
    story.append(PageBreak())

    # =========================================================================
    # PAGE: 4' x 8' SHEET TABLE SAW BATCH CUTTING GUIDE (35" + 35" + LEFTOVER)
    # =========================================================================
    story.append(Paragraph("4. Master 4' × 8' Sheet Table Saw Batch Cutting Plan", style_h1))
    story.append(Paragraph(
        "<b>Purchased Material:</b> One (1) full 4' × 8' sheet of 1/4\" Baltic Birch Plywood pre-cut at the lumber store into "
        "<b>Two (2) Panels at 35\" × 48\"</b> and <b>One (1) Leftover Panel at ~25.75\" to 26\" × 48\"</b>. "
        "This breakdown matches your dollhouse perfectly, as all six major panels (A1, A2, A3, C1, E1, E2) are identically 32.00\" long! "
        "Follow the <b>4-Fence Batch Rip Sequence</b> below to eliminate fence adjustments and guarantee identical parts:",
        style_body
    ))
    story.append(Spacer(1, 4))

    batch_cut_data = [
        [
            Paragraph("<b>Fence Setting</b>", style_th),
            Paragraph("<b>Stock Fed Through Saw</b>", style_th),
            Paragraph("<b>Resulting 35\" Long Strips</b>", style_th),
            Paragraph("<b>Cross-Cut Stop (Length)</b>", style_th),
            Paragraph("<b>Destination Parts & Function</b>", style_th)
        ],
        [
            Paragraph("<b>1. Fence @ 12.00\" (12\")</b>", style_td_bold),
            Paragraph("• Sheet 2 (Feed 3 times)<br/>• Sheet 3 (Feed 2 times)", style_td),
            Paragraph("• Three 12\" × 35\" strips<br/>• Two 12\" × ~26\" strips", style_td),
            Paragraph("• Stop @ 32.00\" (Sheet 2)<br/>• Stop @ 24.90\" (Sheet 3)", style_td),
            Paragraph("• <b>A1, A2, A3</b> Floor Decks (32\" × 12\")<br/>• <b>B1, B2</b> Side Gable Walls (24-7/8\" × 12\")<br/><i>(All five 12\" boards cut with 1 fence setting!)</i>", style_td)
        ],
        [
            Paragraph("<b>2. Fence @ 12-1/16\" (12.04\")</b>", style_td_bold),
            Paragraph("• Sheet 1 (Feed 2 times)", style_td),
            Paragraph("• Two 12-1/16\" × 35\" strips", style_td),
            Paragraph("• Stop @ 32.00\" (32\")", style_td),
            Paragraph("• <b>E1</b> Front Roof Slope (32\" × 12-1/16\")<br/>• <b>E2</b> Rear Roof Slope (32\" × 12-1/16\")<br/><i>(Shares identical 32\" stop with floors & facade!)</i>", style_td)
        ],
        [
            Paragraph("<b>3. Fence @ 16-1/2\" (16.50\")</b>", style_td_bold),
            Paragraph("• Sheet 1 (Feed 1 time)", style_td),
            Paragraph("• One 16-1/2\" × 35\" strip", style_td),
            Paragraph("• Stop @ 32.00\" (32\")", style_td),
            Paragraph("• <b>C1</b> Front Facade Wall Plate (32\" × 16-1/2\")<br/><i>(Leaves 35\" × 7\" offcut strip for trim)</i>", style_td)
        ],
        [
            Paragraph("<b>4. Fence @ 8-1/4\" (8.25\")</b>", style_td_bold),
            Paragraph("• Sheet 2 Offcut (Feed 1x)<br/>• Sheet 3 Offcut (Feed 1x)", style_td),
            Paragraph("• One 8-1/4\" × 35\" strip<br/>• One 8-1/4\" × ~26\" strip", style_td),
            Paragraph("• Stop @ 12.00\" (Cut 2x)<br/>• Stop @ 12.00\" (Cut 2x)", style_td),
            Paragraph("• <b>D1-A & D1-B</b> 1st Fl Partitions (12\" × 8-1/4\")<br/>• <b>D2-A & D2-B</b> 2nd Fl Partitions (12\" × 8-1/4\")<br/><i>(Seats 1/8\" into dados, yields 8.00\" clear ceiling!)</i>", style_td)
        ],
    ]
    t_batch = Table(batch_cut_data, colWidths=[110, 125, 125, 110, 250])
    t_batch.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,0), PRIMARY),
        ('BOX', (0,0), (-1,-1), 1, BORDER_COL),
        ('INNERGRID', (0,0), (-1,-1), 0.5, BORDER_COL),
        ('ROWBACKGROUNDS', (0,1), (-1,-1), [colors.white, BG_LIGHT]),
        ('TOPPADDING', (0,0), (-1,-1), 4),
        ('BOTTOMPADDING', (0,0), (-1,-1), 4),
        ('LEFTPADDING', (0,0), (-1,-1), 5),
        ('RIGHTPADDING', (0,0), (-1,-1), 5),
        ('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
    ]))
    story.append(t_batch)
    story.append(Spacer(1, 8))

    # Standoff Block Kickback Warning Callout
    standoff_box = [
        [
            Paragraph(
                "<b>CRITICAL TABLE SAW SAFETY — USE A STANDOFF BLOCK WHEN TRIMMING LENGTHS:</b><br/>"
                "• <b>Danger:</b> Never push the narrow edge of a board with a miter gauge while letting the outer end ride against the rip fence! The cut piece will twist, pinch against the spinning blade, and violently kick back.<br/>"
                "• <b>The Solution:</b> Clamp a scrap block of 3/4\" wood to your rip fence <i>before the blade</i>. For a 32.00\" cut, set your fence to 32.75\" (32-3/4\"). "
                "Slide your workpiece against the standoff block to set the distance. As you push forward, the board clears the block before reaching the blade, passing through freely with zero pinch risk!<br/>"
                "• <b>Bonus Material:</b> Sheet 3 leaves a massive <b>~25.75\" × 15-3/8\" panel (395+ sq in)</b>—more than enough wood to build the entire 1:12 scale outdoor dog house, fireplace hearth, and stair stringers!",
                style_callout
            )
        ]
    ]
    t_standoff = Table(standoff_box, colWidths=[720])
    t_standoff.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,-1), colors.HexColor("#fffbeb")),
        ('BOX', (0,0), (-1,-1), 1, colors.HexColor("#d97706")),
        ('TOPPADDING', (0,0), (-1,-1), 5),
        ('BOTTOMPADDING', (0,0), (-1,-1), 5),
        ('LEFTPADDING', (0,0), (-1,-1), 8),
        ('RIGHTPADDING', (0,0), (-1,-1), 8),
        ('ROUNDEDCORNERS', [4, 4, 4, 4]),
    ]))
    story.append(t_standoff)
    story.append(PageBreak())

    # =========================================================================
    # MASTER CSV CUT LIST SCHEDULE (FULL TABULAR BREAKDOWN)
    # =========================================================================
    story.append(Paragraph("5. Master Component Cut List Schedule", style_h1))
    story.append(Spacer(1, 2))

    csv_rows = []
    csv_path = os.path.join(r"c:\code2026\house", "cut_list.csv")
    if os.path.exists(csv_path):
        with open(csv_path, mode='r', encoding='utf-8') as f:
            reader = csv.reader(f)
            header = next(reader)
            csv_rows.append([
                Paragraph("<b>ID</b>", style_th),
                Paragraph("<b>Component Name</b>", style_th),
                Paragraph("<b>Qty</b>", style_th),
                Paragraph("<b>Thick</b>", style_th),
                Paragraph("<b>Ruler W × H</b>", style_th),
                Paragraph("<b>Decimal W × H</b>", style_th),
                Paragraph("<b>Description & Workshop Directives</b>", style_th)
            ])
            for r in reader:
                if not r or len(r) < 9:
                    continue
                ruler_dim = f"{r[4]} × {r[5]}"
                decimal_dim = f"{r[6]}\" × {r[7]}\""
                csv_rows.append([
                    Paragraph(f"<b>{r[0]}</b>", style_td_bold),
                    Paragraph(r[1], style_td),
                    Paragraph(r[2], style_td_center),
                    Paragraph(r[3], style_td_center),
                    Paragraph(ruler_dim, style_td_center),
                    Paragraph(decimal_dim, style_td_center),
                    Paragraph(r[8], style_td)
                ])

    t_csv = Table(csv_rows, colWidths=[45, 120, 30, 65, 80, 80, 300])
    t_csv.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,0), PRIMARY),
        ('BOX', (0,0), (-1,-1), 1, BORDER_COL),
        ('INNERGRID', (0,0), (-1,-1), 0.5, BORDER_COL),
        ('ROWBACKGROUNDS', (0,1), (-1,-1), [colors.white, BG_LIGHT]),
        ('TOPPADDING', (0,0), (-1,-1), 2),
        ('BOTTOMPADDING', (0,0), (-1,-1), 2),
        ('LEFTPADDING', (0,0), (-1,-1), 4),
        ('RIGHTPADDING', (0,0), (-1,-1), 4),
        ('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
    ]))
    story.append(t_csv)

    # Build PDF with dynamic NumberedCanvas
    doc.build(story, canvasmaker=NumberedCanvas)
    print(f"Master Architectural PDF built successfully: {WORKSPACE_PDF}", flush=True)

    # Copy to Artifact directory
    if os.path.exists(WORKSPACE_PDF):
        import shutil
        shutil.copyfile(WORKSPACE_PDF, ARTIFACT_PDF)
        print(f"Copied to Artifact directory: {ARTIFACT_PDF}", flush=True)

if __name__ == "__main__":
    build_pdf()
