#!/usr/bin/env python3
"""
Generate Complete Standalone Architectural PDF Plans for:
1:12 Scale Outdoor Deluxe Craftsman Dog House with Covered Porch & Lift-Off Roof.
"""

import os
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_Outdoor_Doghouse_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)

EXTERIOR_IMG = os.path.join(r"c:\code2026\house\images", "doghouse_1_12_exterior.jpg")
WORKSHOP_IMG = os.path.join(r"c:\code2026\house\images", "doghouse_1_12_workshop.jpg")

class DoghouseNumberedCanvas(canvas.Canvas):
    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 on cover page

        self.saveState()
        self.setFont("Helvetica-Bold", 8)
        self.setFillColor(colors.HexColor("#78350f")) # Warm Cedar Brown
        
        # Running Header
        self.drawString(36, 582, "1:12 SCALE OUTDOOR DELUXE CRAFTSMAN DOG HOUSE (3.50\" × 4.50\")")
        self.setFont("Helvetica", 8)
        self.setFillColor(colors.HexColor("#64748b"))
        self.drawRightString(756, 582, "ARCHITECTURAL BLUEPRINT & PRECISION WORKSHOP PLANS")
        
        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) • Stock: 1/8\" Baltic Birch / Basswood")
        
        page_str = f"Page {self._pageNumber} of {page_count}"
        self.setFont("Helvetica-Bold", 8)
        self.setFillColor(colors.HexColor("#78350f"))
        self.drawRightString(756, 28, page_str)
        self.restoreState()


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

    PRIMARY = colors.HexColor("#78350f")      # Deep Amber / Cedar
    SECONDARY = colors.HexColor("#065f46")    # Forest Green
    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

    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=15,
        textColor=colors.HexColor("#fef3c7"),
        alignment=1
    )

    style_h1 = ParagraphStyle(
        'Heading1_Custom',
        fontName='Helvetica-Bold',
        fontSize=13,
        leading=16,
        textColor=PRIMARY,
        spaceBefore=8,
        spaceAfter=4,
        keepWithNext=True
    )
    style_h2 = ParagraphStyle(
        'Heading2_Custom',
        fontName='Helvetica-Bold',
        fontSize=10,
        leading=13,
        textColor=SECONDARY,
        spaceBefore=6,
        spaceAfter=2,
        keepWithNext=True
    )
    style_body = ParagraphStyle(
        'Body_Custom',
        fontName='Helvetica',
        fontSize=8,
        leading=11,
        textColor=TEXT_DARK,
        spaceAfter=3
    )
    style_body_bold = ParagraphStyle(
        'Body_Bold_Custom',
        fontName='Helvetica-Bold',
        fontSize=8,
        leading=11,
        textColor=TEXT_DARK,
        spaceAfter=3
    )
    style_table_cell = ParagraphStyle(
        'TableCell',
        fontName='Helvetica',
        fontSize=7.5,
        leading=9.5,
        textColor=TEXT_DARK
    )
    style_table_cell_bold = ParagraphStyle(
        'TableCellBold',
        fontName='Helvetica-Bold',
        fontSize=7.5,
        leading=9.5,
        textColor=TEXT_DARK
    )
    style_table_header = ParagraphStyle(
        'TableHeader',
        fontName='Helvetica-Bold',
        fontSize=7.5,
        leading=9.5,
        textColor=colors.HexColor("#ffffff"),
        alignment=1
    )
    style_callout = ParagraphStyle(
        'Callout',
        fontName='Helvetica-Oblique',
        fontSize=8,
        leading=11,
        textColor=colors.HexColor("#92400e")
    )
    style_mono = ParagraphStyle(
        'Mono_Custom',
        fontName='Courier',
        fontSize=6.5,
        leading=8.5,
        textColor=colors.HexColor("#0f172a")
    )

    story = []

    # ================= PAGE 1: COVER & EXECUTIVE BLUEPRINT =================
    # Top Hero Header Table
    header_data = [
        [
            Paragraph("<b>1:12 SCALE OUTDOOR DELUXE CRAFTSMAN DOG HOUSE</b>", style_cover_title)
        ],
        [
            Paragraph("Miniature Canine Architecture with Covered Lounging Porch, Lift-Off Roof, & Moisture Barrier Skid Base", style_cover_sub)
        ]
    ]
    t_header = Table(header_data, colWidths=[720])
    t_header.setStyle(TableStyle([
        ('BACKGROUND', (0, 0), (-1, -1), PRIMARY),
        ('ALIGN', (0, 0), (-1, -1), 'CENTER'),
        ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
        ('TOPPADDING', (0, 0), (-1, -1), 10),
        ('BOTTOMPADDING', (0, 0), (-1, -1), 10),
        ('ROUNDEDCORNERS', [4, 4, 4, 4]),
    ]))
    story.append(t_header)
    story.append(Spacer(1, 10))

    # Two column layout: Left = Exterior Render + Specs, Right = Workshop Photo + Key Highlights
    left_cells = []
    if os.path.exists(EXTERIOR_IMG):
        left_cells.append(RLImage(EXTERIOR_IMG, width=350, height=262))
    else:
        left_cells.append(Paragraph("<b>[Exterior Blueprint Image]</b>", style_body))

    right_cells = []
    if os.path.exists(WORKSHOP_IMG):
        right_cells.append(RLImage(WORKSHOP_IMG, width=350, height=262))
    else:
        right_cells.append(Paragraph("<b>[Workshop Assembly Image]</b>", style_body))

    img_table = Table([[left_cells[0], right_cells[0]]], colWidths=[356, 356])
    img_table.setStyle(TableStyle([
        ('ALIGN', (0, 0), (-1, -1), 'CENTER'),
        ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
        ('LEFTPADDING', (0, 0), (-1, -1), 2),
        ('RIGHTPADDING', (0, 0), (-1, -1), 2),
        ('TOPPADDING', (0, 0), (-1, -1), 0),
        ('BOTTOMPADDING', (0, 0), (-1, -1), 0),
    ]))
    story.append(img_table)
    story.append(Spacer(1, 10))

    # Key Architectural Specification Summary Table
    specs_data = [
        [
            Paragraph("<b>Scale Factor:</b> 1:12 (1\" = 1'0\" / 1:12th)", style_table_cell_bold),
            Paragraph("<b>Overall Footprint:</b> 3.50\" W × 4.50\" D × 4.25\" H", style_table_cell_bold),
            Paragraph("<b>Real-World Scale:</b> 3'6\" W × 4'6\" D × 4'3\" H", style_table_cell_bold),
            Paragraph("<b>Stock:</b> 1/8\" Baltic Birch / Basswood", style_table_cell_bold)
        ],
        [
            Paragraph("<b>Door Opening:</b> 1.25\" W × 1.75\" H Arched", style_table_cell),
            Paragraph("<b>Porch Deck:</b> 3.50\" W × 1.25\" D with Skids", style_table_cell),
            Paragraph("<b>Roof Pitch:</b> 45° Pitch (12/12 Rafter Slope)", style_table_cell),
            Paragraph("<b>Access:</b> Gravitational Lift-Off Cleats", style_table_cell)
        ]
    ]
    t_specs = Table(specs_data, colWidths=[180, 180, 180, 180])
    t_specs.setStyle(TableStyle([
        ('BACKGROUND', (0, 0), (-1, -1), colors.HexColor("#fef3c7")),
        ('BOX', (0, 0), (-1, -1), 1, colors.HexColor("#d97706")),
        ('INNERGRID', (0, 0), (-1, -1), 0.5, colors.HexColor("#fde68a")),
        ('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(t_specs)
    story.append(PageBreak())

    # ================= PAGE 2: MASTER CUT LIST & DIMENSION SCHEDULE =================
    story.append(Paragraph("MASTER COMPONENT CUT LIST & DIMENSION SCHEDULE (DUAL-UNIT)", style_h1))
    story.append(Paragraph(
        "All miniature parts are sized for cutting from <b>1/8\" (3mm) Baltic Birch Plywood or Basswood sheets</b>, "
        "paired with <b>1/4\" × 1/4\" basswood square stock</b> for the foundation skid runners and porch posts.",
        style_body
    ))
    story.append(Spacer(1, 4))

    cut_table_data = [
        [
            Paragraph("<b>ID</b>", style_table_header),
            Paragraph("<b>Part Description</b>", style_table_header),
            Paragraph("<b>Qty</b>", style_table_header),
            Paragraph("<b>Mini Fraction</b>", style_table_header),
            Paragraph("<b>Decimal (In)</b>", style_table_header),
            Paragraph("<b>Metric (mm)</b>", style_table_header),
            Paragraph("<b>1:12 Real Scale</b>", style_table_header),
            Paragraph("<b>Material & Function Notes</b>", style_table_header)
        ],
        [
            Paragraph("<b>P1</b>", style_table_cell_bold),
            Paragraph("Base Floor & Porch Deck", style_table_cell),
            Paragraph("1", style_table_cell),
            Paragraph("3-1/2\" × 4-1/2\"", style_table_cell),
            Paragraph("3.500 × 4.500", style_table_cell),
            Paragraph("88.9 × 114.3", style_table_cell),
            Paragraph("3'6\" × 4'6\"", style_table_cell),
            Paragraph("Subfloor with scored 1/4\" cedar plank lines", style_table_cell)
        ],
        [
            Paragraph("<b>P2</b>", style_table_cell_bold),
            Paragraph("Front Gable Wall", style_table_cell),
            Paragraph("1", style_table_cell),
            Paragraph("3-1/2\" × 4-0\"", style_table_cell),
            Paragraph("3.500 × 4.000", style_table_cell),
            Paragraph("88.9 × 101.6", style_table_cell),
            Paragraph("3'6\" × 4'0\"", style_table_cell),
            Paragraph("45° peak (2.25\" eave); 1.25\" × 1.75\" arched doorway", style_table_cell)
        ],
        [
            Paragraph("<b>P3</b>", style_table_cell_bold),
            Paragraph("Rear Gable Wall", style_table_cell),
            Paragraph("1", style_table_cell),
            Paragraph("3-1/2\" × 4-0\"", style_table_cell),
            Paragraph("3.500 × 4.000", style_table_cell),
            Paragraph("88.9 × 101.6", style_table_cell),
            Paragraph("3'6\" × 4'0\"", style_table_cell),
            Paragraph("Solid rear gable; optional 3/8\" high circular vent", style_table_cell)
        ],
        [
            Paragraph("<b>P4</b>", style_table_cell_bold),
            Paragraph("Left Side Wall", style_table_cell),
            Paragraph("1", style_table_cell),
            Paragraph("3-0\" × 2-1/4\"", style_table_cell),
            Paragraph("3.000 × 2.250", style_table_cell),
            Paragraph("76.2 × 57.2", style_table_cell),
            Paragraph("3'0\" × 2'3\"", style_table_cell),
            Paragraph("Solid side panel; optional 1.0\" × 1.0\" 4-pane window", style_table_cell)
        ],
        [
            Paragraph("<b>P5</b>", style_table_cell_bold),
            Paragraph("Right Side Wall", style_table_cell),
            Paragraph("1", style_table_cell),
            Paragraph("3-0\" × 2-1/4\"", style_table_cell),
            Paragraph("3.000 × 2.250", style_table_cell),
            Paragraph("76.2 × 57.2", style_table_cell),
            Paragraph("3'0\" × 2'3\"", style_table_cell),
            Paragraph("Exterior scribed with 3/16\" lap siding grooves", style_table_cell)
        ],
        [
            Paragraph("<b>P6-A</b>", style_table_cell_bold),
            Paragraph("Roof Slope (Left)", style_table_cell),
            Paragraph("1", style_table_cell),
            Paragraph("2-3/4\" × 5-1/8\"", style_table_cell),
            Paragraph("2.750 × 5.125", style_table_cell),
            Paragraph("69.9 × 130.2", style_table_cell),
            Paragraph("2'9\" × 5'1.5\"", style_table_cell),
            Paragraph("45° beveled ridge; extends 1.50\" over porch", style_table_cell)
        ],
        [
            Paragraph("<b>P6-B</b>", style_table_cell_bold),
            Paragraph("Roof Slope (Right)", style_table_cell),
            Paragraph("1", style_table_cell),
            Paragraph("2-3/4\" × 5-1/8\"", style_table_cell),
            Paragraph("2.750 × 5.125", style_table_cell),
            Paragraph("69.9 × 130.2", style_table_cell),
            Paragraph("2'9\" × 5'1.5\"", style_table_cell),
            Paragraph("Mating 45° slope; glued at ridge for lift-off roof", style_table_cell)
        ],
        [
            Paragraph("<b>P7</b>", style_table_cell_bold),
            Paragraph("Roof Alignment Cleats", style_table_cell),
            Paragraph("2", style_table_cell),
            Paragraph("2-1/4\" × 1-1/8\"", style_table_cell),
            Paragraph("2.250 × 1.125", style_table_cell),
            Paragraph("57.2 × 28.6", style_table_cell),
            Paragraph("2'3\" × 1'1.5\"", style_table_cell),
            Paragraph("Triangular guides glued to roof underside", style_table_cell)
        ],
        [
            Paragraph("<b>T1</b>", style_table_cell_bold),
            Paragraph("Ground Skid Runners", style_table_cell),
            Paragraph("2", style_table_cell),
            Paragraph("1/4\" × 1/4\" × 4-1/2\"", style_table_cell),
            Paragraph("0.250 × 0.250 × 4.5", style_table_cell),
            Paragraph("6.4 × 6.4 × 114.3", style_table_cell),
            Paragraph("3\" × 3\" × 4'6\"", style_table_cell),
            Paragraph("Basswood square stock; lifts deck 1/4\" (moisture barrier)", style_table_cell)
        ],
        [
            Paragraph("<b>T2</b>", style_table_cell_bold),
            Paragraph("Porch Support Posts", style_table_cell),
            Paragraph("2", style_table_cell),
            Paragraph("1/4\" × 1/4\" × 2-1/4\"", style_table_cell),
            Paragraph("0.250 × 0.250 × 2.25", style_table_cell),
            Paragraph("6.4 × 6.4 × 57.2", style_table_cell),
            Paragraph("3\" × 3\" × 2'3\"", style_table_cell),
            Paragraph("Vertical porch timber columns supporting roof overhang", style_table_cell)
        ],
        [
            Paragraph("<b>T3</b>", style_table_cell_bold),
            Paragraph("Doorway Arch Casing", style_table_cell),
            Paragraph("1", style_table_cell),
            Paragraph("1/16\" × 1-1/2\" × 2\"", style_table_cell),
            Paragraph("0.062 × 1.50 × 2.0", style_table_cell),
            Paragraph("1.6 × 38.1 × 50.8", style_table_cell),
            Paragraph("1\" × 1'6\" × 2'0\"", style_table_cell),
            Paragraph("Face trim ring painted crisp white around doorway", style_table_cell)
        ],
        [
            Paragraph("<b>T4</b>", style_table_cell_bold),
            Paragraph("Corner Trim Strips", style_table_cell),
            Paragraph("4", style_table_cell),
            Paragraph("1/16\" × 3/16\" × 2-1/4\"", style_table_cell),
            Paragraph("0.062 × 0.188 × 2.25", style_table_cell),
            Paragraph("1.6 × 4.8 × 57.2", style_table_cell),
            Paragraph("1\" × 2.25\" × 2'3\"", style_table_cell),
            Paragraph("Conceals corner butt joints; painted craftsman white", style_table_cell)
        ],
        [
            Paragraph("<b>A1-3</b>", style_table_cell_bold),
            Paragraph("Steps, Bowls & Bone Plaque", style_table_cell),
            Paragraph("1 ea", style_table_cell),
            Paragraph("Miniature Accents", style_table_cell),
            Paragraph("Assorted", style_table_cell),
            Paragraph("Assorted", style_table_cell),
            Paragraph("Assorted", style_table_cell),
            Paragraph("2-step entry ramp, twin feeding bowls, bone name plaque", style_table_cell)
        ]
    ]

    t_cut = Table(cut_table_data, colWidths=[36, 120, 26, 80, 75, 75, 68, 240])
    t_cut.setStyle(TableStyle([
        ('BACKGROUND', (0, 0), (-1, 0), PRIMARY),
        ('ALIGN', (0, 0), (-1, -1), 'LEFT'),
        ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
        ('GRID', (0, 0), (-1, -1), 0.5, BORDER_COL),
        ('ROWBACKGROUNDS', (0, 1), (-1, -1), [colors.HexColor("#ffffff"), BG_LIGHT]),
        ('TOPPADDING', (0, 0), (-1, -1), 3),
        ('BOTTOMPADDING', (0, 0), (-1, -1), 3),
        ('LEFTPADDING', (0, 0), (-1, -1), 4),
        ('RIGHTPADDING', (0, 0), (-1, -1), 4),
    ]))
    story.append(t_cut)
    story.append(Spacer(1, 8))

    # Architectural ASCII Layout Diagram
    story.append(Paragraph("ELEVATION SCHEMATICS & JOINERY GEOMETRY", style_h2))
    ascii_front = """
    FRONT ELEVATION & PORCH (Looking North)                  SIDE ELEVATION & LONGITUDINAL PROFILE (Looking East)
           /\\  <-- Ridge Peak Apex: Y = 4.00"                      |<---- CABIN ENCLOSED (3.25") ---->|<-- PORCH (1.25") -->|
          /  \\                                                     +--------------------------------------------------------+
         / 45°\\     Roof Overhang: 1/4" Side Eaves                  \\                 45° ROOF SLOPE (P6)                   /
        /      \\                                                     \\                                                      /
       +--------+  <-- Eave Baseline: Y = 2.25"                       +----------------------------------------+-----------+ <-- Eave Y=2.25"
       | [BONE] |  <-- Plaque: 1.00" x 0.38"                          |                                        |           |
  |=== | .----. | ===|  <-- Twin Porch Timber Posts (T2)              |         CABIN WALL (P4 / P5)           |   PORCH   | <-- Front Post (T2)
  | T2 | |    | | T2 |                                                |            (3.00" x 2.25")             |   POST    |
  |    | |    | |    |  <-- Arched Entry Opening: 1.25" W x 1.75" H   |                                        |   (T2)    |
 +=====+========+=====+ <-- Deck Surface: Y = 0.25"             +-----+----------------------------------------+-----------+--+ <-- Deck (P1)
 | [=== SKID T1 ===]  | <-- Skid Clearance: 0.25" (3" Real)     |                 FOUNDATION SKID RUNNER (T1)              | <-- 0.25" H
 +--------------------+                                         +==========================================================+
        |== A1 ==|      <-- 2-Step Porch Ramp
    """
    story.append(Paragraph(f"<pre>{ascii_front}</pre>", style_mono))
    story.append(PageBreak())

    # ================= PAGE 3: STEP-BY-STEP WORKSHOP ASSEMBLY GUIDE =================
    story.append(Paragraph("STEP-BY-STEP WORKSHOP CRAFTSMAN MANUAL", style_h1))
    story.append(Spacer(1, 2))

    steps_data = [
        [
            Paragraph("<b>PHASE 1: MATERIAL PREPARATION & SCRIBING</b>", style_table_header),
            Paragraph("<b>PHASE 2: BASE SKIDS & CABIN JOINERY</b>", style_table_header)
        ],
        [
            Paragraph(
                "• <b>Plank Deck Scribing:</b> On top face of deck <code>P1</code> (3.50\" × 4.50\"), score parallel longitudinal lines every 1/4\" (0.250\") using a craft scribe or back of razor blade. Simulates rustic 3\" outdoor cedar planks.<br/>"
                "• <b>Siding Scribing:</b> On exterior faces of walls <code>P2</code>, <code>P3</code>, <code>P4</code>, <code>P5</code>, score horizontal lines every 3/16\" (0.188\") to mimic American craftsman lap siding.<br/>"
                "• <b>45° Gable Cuts:</b> On <code>P2</code> and <code>P3</code>, mark center at X = 1.75\" and eave at Y = 2.25\". Cut straight lines to apex at Y = 4.00\" (exact 45° angle, 1.75\" rise over 1.75\" run).<br/>"
                "• <b>Arched Door Cutout:</b> On <code>P2</code>, lay out doorway 1.25\" wide centered (X = 1.125\" to 2.375\"). Cut straight vertical jambs to Y = 1.125\", then carefully score a 5/8\" (0.625\") radius semi-circle arch up to Y = 1.75\".",
                style_table_cell
            ),
            Paragraph(
                "• <b>Glue Skid Runners:</b> Invert deck <code>P1</code>. Glue the two 1/4\" × 1/4\" × 4.50\" basswood skids <code>T1</code> flush along the bottom edges. Provides 1/4\" (3\" real) air clearance to prevent moisture rot.<br/>"
                "• <b>Erect Rear Gable (P3):</b> Glue <code>P3</code> flush to the rear edge of deck <code>P1</code> at a strict 90° angle using miniature machinist squares.<br/>"
                "• <b>Attach Side Walls (P4, P5):</b> Glue side walls to the floor, inset 1/8\" from outer edges, running forward 3.00\" from the rear wall.<br/>"
                "• <b>Install Front Gable (P2):</b> Glue front gable against front edges of side walls. The remaining 1.25\" of deck projects forward forming the open lounging porch!<br/>"
                "• <b>Corner Trim (T4):</b> Glue four 1/16\" × 3/16\" × 2.25\" strips vertically over the exterior corners to conceal plywood edges.",
                style_table_cell
            )
        ],
        [
            Paragraph("<b>PHASE 3: PORCH COLUMNS & ARCH TRIM</b>", style_table_header),
            Paragraph("<b>PHASE 4: REMOVABLE ROOF & SHINGLING</b>", style_table_header)
        ],
        [
            Paragraph(
                "• <b>Seat Front Posts (T2):</b> Glue the two 1/4\" × 1/4\" × 2.25\" timber posts vertically on the front outer corners of the deck (1.25\" forward of front wall). Tops align flush with eave baseline.<br/>"
                "• <b>Arched Door Casing (T3):</b> Cut matching arch trim from 1/16\" basswood or dense white cardstock and glue around doorway with a 1/16\" reveal.<br/>"
                "• <b>Porch Steps (A1):</b> Glue two stepped 1/8\" slabs together (1.25\" × 0.75\" base, 1.25\" × 0.40\" top) and adhere centered at front edge of deck.<br/>"
                "• <b>Bone Name Plaque (A3):</b> Cut 1.00\" × 0.38\" bone plaque, letter pet name (e.g. 'ROVER'), and mount centered above the door arch.",
                style_table_cell
            ),
            Paragraph(
                "• <b>Bevel Ridge & Glue:</b> Sand a 45° bevel along top edge of <code>P6-A</code> and <code>P6-B</code>. Tape exterior joint with blue painter's tape, fold open like a book, run wood glue into groove, and fold to 90°.<br/>"
                "• <b>Triangular Cleats (P7):</b> Place glued roof on cabin. Mark inner gable locations on roof underside. Glue the two triangular cleats <code>P7</code> 1/32\" inside marks. <b>Result: Roof lifts off freely for interior access yet locks squarely under gravity!</b><br/>"
                "• <b>Asphalt Shingle Simulation:</b> Cut 120-grit black emery sandpaper or heavy charcoal paper into 1/2\" strips with 1/4\" tab slits. Glue with 1/8\" reveal, topped with a folded ridge cap.",
                style_table_cell
            )
        ]
    ]

    t_steps = Table(steps_data, colWidths=[356, 356])
    t_steps.setStyle(TableStyle([
        ('BACKGROUND', (0, 0), (0, 0), SECONDARY),
        ('BACKGROUND', (1, 0), (1, 0), SECONDARY),
        ('BACKGROUND', (0, 2), (0, 2), PRIMARY),
        ('BACKGROUND', (1, 2), (1, 2), PRIMARY),
        ('VALIGN', (0, 0), (-1, -1), 'TOP'),
        ('GRID', (0, 0), (-1, -1), 0.5, BORDER_COL),
        ('TOPPADDING', (0, 0), (-1, -1), 4),
        ('BOTTOMPADDING', (0, 0), (-1, -1), 5),
        ('LEFTPADDING', (0, 0), (-1, -1), 6),
        ('RIGHTPADDING', (0, 0), (-1, -1), 6),
    ]))
    story.append(t_steps)
    story.append(Spacer(1, 8))

    # Bottom Finishing & Decor Tips Callout Box
    decor_data = [
        [
            Paragraph(
                "<b>CRAFTSMAN FINISHING & STYLING TIPS:</b><br/>"
                "• <b>Stain:</b> Apply Minwax Golden Oak or Natural Cedar oil stain to the siding and deck for authentic wood grain.<br/>"
                "• <b>Trim Contrast:</b> Paint corner boards, porch columns, and arched doorway casing in crisp satin white or forest green.<br/>"
                "• <b>Interior Comfort:</b> Fold a 2.5\" × 2.5\" swatch of flannel plaid fabric into a miniature pet cushion for inside the cabin.<br/>"
                "• <b>Feeding Station:</b> Glue two small silver metal eyelets or beads into the porch deck beside the doorway to simulate food & water bowls!",
                style_callout
            )
        ]
    ]
    t_decor = Table(decor_data, colWidths=[720])
    t_decor.setStyle(TableStyle([
        ('BACKGROUND', (0, 0), (-1, -1), colors.HexColor("#fffbeb")),
        ('BOX', (0, 0), (-1, -1), 1, colors.HexColor("#f59e0b")),
        ('TOPPADDING', (0, 0), (-1, -1), 6),
        ('BOTTOMPADDING', (0, 0), (-1, -1), 6),
        ('LEFTPADDING', (0, 0), (-1, -1), 10),
        ('RIGHTPADDING', (0, 0), (-1, -1), 10),
        ('ROUNDEDCORNERS', [4, 4, 4, 4]),
    ]))
    story.append(t_decor)

    # Build the PDF document
    doc.build(story, canvasmaker=DoghouseNumberedCanvas)
    print(f"Successfully generated: {WORKSPACE_PDF}")

    # Copy to artifact dir if exists
    if os.path.exists(ARTIFACT_DIR):
        import shutil
        shutil.copyfile(WORKSPACE_PDF, ARTIFACT_PDF)
        print(f"Copied to artifact: {ARTIFACT_PDF}")

if __name__ == "__main__":
    build_doghouse_pdf()
