#!/usr/bin/env python3
"""
Activity Builders for Math Curriculum

Data-driven activity builders that generate HTML for worksheets.
Used with monday.py, tuesday.py, etc. day generators.

Usage:
    from activity_builders import build_number_bonds, build_fact_families
    html = build_number_bonds({"whole": 10, "pairs": [(3, 7), (4, 6)]})
"""

import os
import sys
sys.path.insert(0, os.path.dirname(__file__))
from math_helpers import b, BLUE, GREEN, RED, YELLOW, ORANGE, PURPLE, PINK, TEAL, GOLD


# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
# FOUNDATIONAL: Place Value Builders
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

def build_tens_ones_chart(data: dict) -> str:
    """
    Tens and ones place value chart.
    
    Data:
        - numbers: list of numbers to decompose
        - blank_tens: bool (blank tens column, default True)
        - blank_ones: bool (blank ones column, default True)
        - note: str (optional instruction)
    """
    numbers = data.get("numbers", [])
    blank_tens = data.get("blank_tens", True)
    blank_ones = data.get("blank_ones", True)
    note = data.get("note", "Fill in the tens and ones for each number.")
    
    html = f'<p class="note">{note}</p>'
    html += '<table class="tbl">\n'
    html += '<tr><th>Number</th><th>Tens</th><th>Ones</th></tr>\n'
    
    for num in numbers:
        tens = num // 10
        ones = num % 10
        
        html += f'<tr><td>{num}</td>'
        if blank_tens:
            html += f'<td>{b("5em")}</td>'
        else:
            html += f'<td>{tens}</td>'
        if blank_ones:
            html += f'<td>{b("5em")}</td>'
        else:
            html += f'<td>{ones}</td>'
        html += '</tr>\n'
    
    html += '</table>'
    return html


def build_expanded_form(data: dict) -> str:
    """
    Expanded form practice (number = tens + ones).
    
    Data:
        - numbers: list of numbers to write in expanded form
        - show_examples: bool (show example first, default True)
        - note: str (optional instruction)
    """
    numbers = data.get("numbers", [])
    show_examples = data.get("show_examples", True)
    note = data.get("note", "Write each number as tens + ones.")
    cols = data.get("cols", 2)
    
    html = f'<p class="note">{note}</p>'
    
    if show_examples:
        html += '<div class="abox" style="background:#e8eaf6; margin-bottom:8px;">'
        html += '<p><b>Expanded form</b> shows a number as tens + ones.</p>'
        html += '<p style="font-size:14px;">34 = 30 + 4  (example)  |  52 = 50 + 2  (example)</p>'
        html += '</div>'
    
    html += f'<div class="mgrid c{cols}">\n'
    
    for num in numbers:
        html += f'<div class="abox"><p>{num} = {b("4em")} + {b("4em")}</p></div>\n'
    
    html += '</div>'
    return html


def build_build_number(data: dict) -> str:
    """
    Build the number from expanded form.
    
    Data:
        - additions: list of (tens, ones) tuples
        - note: str (optional instruction)
    """
    additions = data.get("additions", [])
    note = data.get("note", "What number does the expanded form make?")
    cols = data.get("cols", 3)
    
    html = f'<p class="note">{note}</p>'
    html += f'<div class="mgrid c{cols}">\n'
    
    for tens, ones in additions:
        html += f'<div class="abox" style="text-align:center;"><p>{tens} + {ones} = {b("4em")}</p></div>\n'
    
    html += '</div>'
    return html


def build_compare_place_value(data: dict) -> str:
    """
    Compare place values using >, <, =.
    
    Data:
        - comparisons: list of dicts with "left", "right", and optional "type"
        - note: str (optional instruction)
    """
    comparisons = data.get("comparisons", [])
    note = data.get("note", "Write >, <, or =.")
    cols = data.get("cols", 2)
    
    html = f'<p class="note">{note}</p>'
    html += f'<div class="mgrid c{cols}">\n'
    
    for comp in comparisons:
        left = comp.get("left", "")
        right = comp.get("right", "")
        
        html += f'<div class="abox"><p>{left} {b("4em")} {right}</p></div>\n'
    
    html += '</div>'
    return html


def build_read_tens_ones(data: dict) -> str:
    """
    Read tens and ones, write the number.
    
    Data:
        - phrases: list of strings like "3 tens and 4 ones"
        - note: str (optional instruction)
    """
    phrases = data.get("phrases", [])
    note = data.get("note", "Write the number each set of tens and ones makes.")
    cols = data.get("cols", 2)
    
    html = f'<p class="note">{note}</p>'
    html += f'<div class="mgrid c{cols}">\n'
    
    for phrase in phrases:
        html += f'<div class="abox"><p>{phrase} = {b("4em")}</p></div>\n'
    
    html += '</div>'
    return html


def build_write_tens_ones(data: dict) -> str:
    """
    Write numbers as tens and ones.
    
    Data:
        - numbers: list of numbers to decompose
        - note: str (optional instruction)
    """
    numbers = data.get("numbers", [])
    note = data.get("note", "Break each number apart into tens and ones.")
    cols = data.get("cols", 2)
    
    html = f'<p class="note">{note}</p>'
    html += f'<div class="mgrid c{cols}">\n'
    
    for num in numbers:
        html += f'<div class="abox"><p>{num} = {b("4em")} tens and {b("4em")} ones</p></div>\n'
    
    html += '</div>'
    return html


def build_decompose_two_ways(data: dict) -> str:
    """
    Decompose a number in two different ways.
    
    Data:
        - numbers: list of numbers to decompose
        - note: str (optional instruction)
    """
    numbers = data.get("numbers", [])
    note = data.get("note", "Show each number as two different addends.")
    
    html = f'<p class="note">{note}</p>'
    html += '<div class="mgrid c2">\n'
    
    for num in numbers:
        html += f'<div class="abox"><p style="font-size:16px; font-weight:700;">{num}</p>'
        html += f'<p>{b("5em")} + {b("5em")}</p>'
        html += f'<p>{b("5em")} + {b("5em")}</p></div>\n'
    
    html += '</div>'
    return html


def build_place_value_table_full(data: dict) -> str:
    """
    Full place value table with expanded form column.
    
    Data:
        - numbers: list of numbers
        - note: str (optional instruction)
    """
    numbers = data.get("numbers", [])
    note = data.get("note", "Fill in the table.")
    
    html = f'<p class="note">{note}</p>'
    html += '<table class="tbl">\n'
    html += '<tr><th>Number</th><th>Tens</th><th>Ones</th><th>Expanded Form</th></tr>\n'
    
    for num in numbers:
        html += f'<tr><td>{num}</td>'
        html += f'<td>{b("4em")}</td>'
        html += f'<td>{b("4em")}</td>'
        html += f'<td>{b("4em")} + {b("4em")}</td>'
        html += '</tr>\n'
    
    html += '</table>'
    return html


def build_compare_numbers(data: dict) -> str:
    """
    Compare numbers using <, >, = symbols.
    
    Data:
        - pairs: list of (left, right) tuples
        - note: str (optional instruction)
        - cols: int (number of columns, default 3)
        - show_alligator: bool (mention alligator mnemonic, default False)
    """
    pairs = data.get("pairs", [])
    note = data.get("note", "Fill in <, >, or =.")
    cols = data.get("cols", 3)
    show_alligator = data.get("show_alligator", False)
    
    if show_alligator:
        note = "The alligator always eats the bigger number! Fill in <, >, or =."
    
    html = f'<p class="note">{note}</p>'
    html += f'<div class="mgrid c{cols}">\n'
    
    for left, right in pairs:
        html += f'<div class="abox"><p style="font-size:16px;">{left} ___ {right}</p></div>\n'
    
    html += '</div>'
    return html


def build_order_numbers(data: dict) -> str:
    """
    Order numbers from least to greatest or greatest to least.
    
    Data:
        - sets: list of number lists to order
        - direction: str ("least_to_greatest" or "greatest_to_least")
        - note: str (optional instruction)
        - cols: int (number of columns, default 2)
    """
    sets = data.get("sets", [])
    direction = data.get("direction", "least_to_greatest")
    cols = data.get("cols", 2)
    
    if direction == "least_to_greatest":
        note = data.get("note", "Write the numbers from least to greatest.")
    else:
        note = data.get("note", "Write the numbers from greatest to least.")
    
    html = f'<p class="note">{note}</p>'
    html += f'<div class="mgrid c{cols}">\n'
    
    for nums in sets:
        blank_html = ", ".join([b("5em") for _ in nums])
        html += f'<div class="abox"><p style="font-size:14px;">{", ".join(map(str, nums))}</p>'
        html += f'<p>{blank_html}</p></div>\n'
    
    html += '</div>'
    return html


def build_true_false_comparison(data: dict) -> str:
    """
    True or false comparison statements.
    
    Data:
        - statements: list of comparison strings (e.g., "34 > 28")
        - note: str (optional instruction)
        - cols: int (number of columns, default 2)
    """
    statements = data.get("statements", [])
    note = data.get("note", "Write T for true, F for false.")
    cols = data.get("cols", 2)
    
    html = f'<p class="note">{note}</p>'
    html += f'<div class="mgrid c{cols}">\n'
    
    for stmt in statements:
        html += f'<div class="abox"><p style="font-size:16px;">{stmt} ___</p></div>\n'
    
    html += '</div>'
    return html


def build_compare_word_problems(data: dict) -> str:
    """
    Word problems involving comparison.
    
    Data:
        - problems: list of dicts with "text" and "answer_type" (who/which)
        - note: str (optional instruction)
    """
    problems = data.get("problems", [])
    note = data.get("note", "Read carefully and answer the question.")
    
    html = f'<div class="wp">\n'
    for problem in problems:
        text = problem.get("text", "")
        answer_type = problem.get("answer_type", "name")
        html += f'<p>{text} {b("6em")}</p>\n'
    html += '</div>\n'
    return html


def build_circle_greater(data: dict) -> str:
    """
    Circle the greater number in pairs.
    
    Data:
        - pairs: list of (left, right) tuples
        - note: str (optional instruction)
        - cols: int (number of columns, default 2)
    """
    pairs = data.get("pairs", [])
    note = data.get("note", "Circle the greater number.")
    cols = data.get("cols", 2)
    
    html = f'<p class="note">{note}</p>'
    html += f'<div class="mgrid c{cols}">\n'
    
    for left, right in pairs:
        html += f'<div class="abox"><p style="font-size:16px;">{left}  or  {right}</p></div>\n'
    
    html += '</div>'
    return html


def build_circle_less(data: dict) -> str:
    """
    Circle the lesser number in pairs.
    
    Data:
        - pairs: list of (left, right) tuples
        - note: str (optional instruction)
        - cols: int (number of columns, default 2)
    """
    pairs = data.get("pairs", [])
    note = data.get("note", "Circle the lesser number.")
    cols = data.get("cols", 2)
    
    html = f'<p class="note">{note}</p>'
    html += f'<div class="mgrid c{cols}">\n'
    
    for left, right in pairs:
        html += f'<div class="abox"><p style="font-size:16px;">{left}  or  {right}</p></div>\n'
    
    html += '</div>'
    return html


def build_compare_with_explanation(data: dict) -> str:
    """
    Compare numbers with step-by-step explanation prompts.
    
    Data:
        - comparisons: list of dicts with "left", "right", "explanation"
        - note: str (optional instruction)
        - cols: int (number of columns, default 2)
    """
    comparisons = data.get("comparisons", [])
    note = data.get("note", "Look at the tens place first. The number with more tens is greater.")
    cols = data.get("cols", 2)
    
    html = f'<p class="note">{note}</p>'
    html += f'<div class="mgrid c{cols}">\n'
    
    for comp in comparisons:
        html += f'<div class="abox"><p>{comp.get("explanation", "")}</p>'
        html += f'<p>{comp.get("left", "")} ___ {comp.get("right", "")}</p></div>\n'
    
    html += '</div>'
    return html


# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
# PHASE 1: Addition & Subtraction Builders
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

def build_number_bonds(data: dict) -> str:
    """
    Number bonds (circles with connecting lines).
    
    Data:
        - whole: int (the total, e.g., 10)
        - pairs: list of (part1, part2) or (part1, None) for blanks
        - note: str (optional instruction)
        - show_visual: bool (include visual circles, default True)
    """
    whole = data.get("whole", 10)
    pairs = data.get("pairs", [])
    note = data.get("note", "Fill in the missing part of each number bond.")
    show_visual = data.get("show_visual", True)
    
    html = f'<p class="note">{note}</p>'
    html += '<div class="mgrid c2">\n'
    
    for part1, part2 in pairs:
        if show_visual:
            # Visual number bond with circles
            p1_display = str(part1) if part1 is not None else b("2em")
            p2_display = str(part2) if part2 is not None else b("2em")
            html += f'<div class="abox" style="text-align:center;">'
            html += f'<div style="display:flex; justify-content:center; gap:20px; margin-bottom:8px;">'
            html += f'<div style="border:2px solid #3f51b5; border-radius:50%; width:40px; height:40px; display:flex; align-items:center; justify-content:center; font-weight:700;">{p1_display}</div>'
            html += f'<div style="border:2px solid #3f51b5; border-radius:50%; width:40px; height:40px; display:flex; align-items:center; justify-content:center; font-weight:700;">{p2_display}</div>'
            html += f'</div>'
            html += f'<div style="border:2px solid #3f51b5; border-radius:50%; width:40px; height:40px; display:flex; align-items:center; justify-content:center; font-weight:700; margin:0 auto;">{whole}</div>'
            html += f'</div>\n'
        else:
            # Text-only format
            p1_display = str(part1) if part1 is not None else b("3em")
            p2_display = str(part2) if part2 is not None else b("3em")
            html += f'<div class="abox" style="text-align:center;"><p>{whole}</p><p>{p1_display} and {p2_display}</p></div>\n'
    
    html += '</div>'
    return html


def build_fact_families(data: dict) -> str:
    """
    Fact family triangles/houses.
    
    Data:
        - numbers: list of 3 numbers that form a fact family (e.g., [3, 7, 10])
        - note: str (optional instruction)
        - show_triangle: bool (visual triangle layout, default True)
        - include_subtraction: bool (include subtraction facts, default True)
    """
    families = data.get("families", [])  # List of [a, b, sum] triples
    note = data.get("note", "Write all the fact family equations.")
    show_triangle = data.get("show_triangle", True)
    include_subtraction = data.get("include_subtraction", True)
    
    html = f'<p class="note">{note}</p>'
    html += '<div class="mgrid c2">\n'
    
    for family in families:
        num_a, num_b, total = family[0], family[1], family[2]
        
        if show_triangle:
            # Triangle/house visual
            html += f'<div class="abox" style="padding:10px;">'
            html += f'<div style="text-align:center; margin-bottom:8px;">'
            html += f'<div style="display:inline-block; border:2px solid #3f51b5; border-radius:50%; width:36px; height:36px; line-height:36px; font-weight:700; margin:0 4px;">{num_a}</div>'
            html += f'<div style="display:inline-block; border:2px solid #3f51b5; border-radius:50%; width:36px; height:36px; line-height:36px; font-weight:700; margin:0 4px;">{num_b}</div>'
            html += f'<div style="display:inline-block; border:2px solid #3f51b5; border-radius:50%; width:36px; height:36px; line-height:36px; font-weight:700; margin:0 4px;">{total}</div>'
            html += f'</div>'
            html += f'<div style="font-size:11px;">'
            html += f'<p>{num_a} + {num_b} = {b("4em")}</p>'
            html += f'<p>{num_b} + {num_a} = {b("4em")}</p>'
            if include_subtraction:
                html += f'<p>{total} - {num_a} = {b("4em")}</p>'
                html += f'<p>{total} - {num_b} = {b("4em")}</p>'
            html += f'</div></div>\n'
        else:
            # Text-only format
            html += f'<div class="abox">'
            html += f'<p><b>Fact Family: {num_a}, {num_b}, {total}</b></p>'
            html += f'<p>{num_a} + {num_b} = {b("4em")}</p>'
            html += f'<p>{num_b} + {num_a} = {b("4em")}</p>'
            if include_subtraction:
                html += f'<p>{total} - {num_a} = {b("4em")}</p>'
                html += f'<p>{total} - {num_b} = {b("4em")}</p>'
            html += f'</div>\n'
    
    html += '</div>'
    return html


def build_make_10_strategy(data: dict) -> str:
    """
    Make-a-10 strategy step-by-step activities.
    
    Data:
        - problems: list of (a, b) pairs where a >= 5
        - show_steps: bool (show step-by-step blanks, default True)
        - note: str (optional instruction)
    """
    problems = data.get("problems", [])
    show_steps = data.get("show_steps", True)
    note = data.get("note", "Use make-a-10 strategy. Break apart the second number.")
    
    html = f'<p class="note">{note}</p>'
    html += '<div class="mgrid c2">\n'
    
    for num_a, num_b in problems:
        if show_steps:
            # Calculate the make-a-10 steps
            needed_for_10 = 10 - num_a
            remainder = num_b - needed_for_10
            
            html += f'<div class="abox">'
            html += f'<p style="font-size:14px; font-weight:700;">{num_a} + {num_b} = {b("4em")}</p>'
            html += f'<p style="font-size:11px;">{num_a} + {b("1.5em")} = 10, 10 + {b("1.5em")} = {b("4em")}</p>'
            html += f'</div>\n'
        else:
            html += f'<div class="abox" style="text-align:center;"><p>{num_a} + {num_b} = {b("4em")}</p></div>\n'
    
    html += '</div>'
    return html


def build_pairs_to_make(data: dict) -> str:
    """
    Pairs that make a target number.
    
    Data:
        - target: int (the target sum, e.g., 10)
        - pairs: list of (a, b) or (a, None) for blanks
        - note: str (optional instruction)
    """
    target = data.get("target", 10)
    pairs = data.get("pairs", [])
    note = data.get("note", f"Fill in the missing number to make {target}.")
    cols = data.get("cols", 3)
    
    html = f'<p class="note">{note}</p>'
    html += f'<div class="mgrid c{cols}">\n'
    
    for num_a, num_b in pairs:
        a_display = str(num_a) if num_a is not None else b("3em")
        b_display = str(num_b) if num_b is not None else b("3em")
        html += f'<div class="abox" style="text-align:center;"><p>{a_display} + {b_display} = {target}</p></div>\n'
    
    html += '</div>'
    return html


def build_missing_addend(data: dict) -> str:
    """
    Missing addend problems.
    
    Data:
        - problems: list of (known, target) where unknown + known = target
        - note: str (optional instruction)
    """
    problems = data.get("problems", [])
    note = data.get("note", "What number is missing?")
    cols = data.get("cols", 2)
    
    html = f'<p class="note">{note}</p>'
    html += f'<div class="mgrid c{cols}">\n'
    
    for known, target in problems:
        html += f'<div class="abox" style="text-align:center;"><p>{b("4em")} + {known} = {target}</p></div>\n'
    
    html += '</div>'
    return html


def build_missing_subtrahend(data: dict) -> str:
    """
    Missing subtrahend problems.
    
    Data:
        - problems: list of (minuend, difference) where minuend - unknown = difference
        - note: str (optional instruction)
    """
    problems = data.get("problems", [])
    note = data.get("note", "What number is missing?")
    cols = data.get("cols", 2)
    
    html = f'<p class="note">{note}</p>'
    html += f'<div class="mgrid c{cols}">\n'
    
    for minuend, difference in problems:
        html += f'<div class="abox" style="text-align:center;"><p>{minuend} - {b("4em")} = {difference}</p></div>\n'
    
    html += '</div>'
    return html


# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
# PHASE 2: Column Math Builders
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

def build_column_addition(data: dict) -> str:
    """
    Vertical column addition with regrouping.
    
    Data:
        - problems: list of (a, b) tuples
        - digits: int (2 or 3 digit problems, default 2)
        - show_regrouping: bool (show regrouping circles, default False)
        - note: str (optional instruction)
    """
    from math_helpers import col_add
    problems = data.get("problems", [])
    digits = data.get("digits", 2)
    note = data.get("note", "Add using columns. Regroup if needed.")
    cols = data.get("cols", 3)
    
    html = f'<p class="note">{note}</p>'
    html += f'<div class="mgrid c{cols}" style="gap:10px;">\n'
    
    for a, b_val in problems:
        html += f'<div class="abox" style="text-align:center;">{col_add(a, b_val, digits)}</div>\n'
    
    html += '</div>'
    return html


def build_column_subtraction(data: dict) -> str:
    """
    Vertical column subtraction with regrouping.
    
    Data:
        - problems: list of (a, b) tuples
        - digits: int (2 or 3 digit problems, default 2)
        - note: str (optional instruction)
    """
    from math_helpers import col_sub
    problems = data.get("problems", [])
    digits = data.get("digits", 2)
    note = data.get("note", "Subtract using columns. Regroup if needed.")
    cols = data.get("cols", 3)
    
    html = f'<p class="note">{note}</p>'
    html += f'<div class="mgrid c{cols}" style="gap:10px;">\n'
    
    for a, b_val in problems:
        html += f'<div class="abox" style="text-align:center;">{col_sub(a, b_val, digits)}</div>\n'
    
    html += '</div>'
    return html


def build_regrouping_visual(data: dict) -> str:
    """
    Visual regrouping with base-10 blocks explanation.
    
    Data:
        - problems: list of (a, b, operation) where operation is "add" or "subtract"
        - note: str (optional instruction)
    """
    problems = data.get("problems", [])
    note = data.get("note", "Show the regrouping with base-10 blocks.")
    
    html = f'<p class="note">{note}</p>'
    html += '<div class="mgrid c2">\n'
    
    for a, b, operation in problems:
        html += f'<div class="abox">'
        if operation == "add":
            html += f'<p style="font-size:14px; font-weight:700;">{a} + {b}</p>'
            html += f'<p style="font-size:11px;">Regroup 10 ones as 1 ten</p>'
            html += f'<div class="draw-box" style="min-height:50px;"></div>'
        else:
            html += f'<p style="font-size:14px; font-weight:700;">{a} - {b}</p>'
            html += f'<p style="font-size:11px;">Regroup 1 ten as 10 ones</p>'
            html += f'<div class="draw-box" style="min-height:50px;"></div>'
        html += f'</div>\n'
    
    html += '</div>'
    return html


def build_two_step_problems(data: dict) -> str:
    """
    Two-step word problems.
    
    Data:
        - problems: list of dicts with "text", "step1", "step2", "blanks"
        - note: str (optional instruction)
    """
    problems = data.get("problems", [])
    note = data.get("note", "Read carefully. These problems have two steps!")
    
    html = f'<p class="note">{note}</p>'
    html += '<div class="wp">\n'
    
    for i, problem in enumerate(problems, 1):
        text = problem.get("text", "")
        blanks = problem.get("blanks", 2)
        blank_html = " ".join([b("4em") for _ in range(blanks)])
        
        html += f'<div style="margin-bottom:8px;">'
        html += f'<p><b>{i}.</b> {text}</p>'
        html += f'<p>{blank_html}</p>'
        html += f'<div class="draw-box" style="min-height:40px;"></div>'
        html += f'</div>\n'
    
    html += '</div>'
    return html


# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
# PHASE 3: Measurement, Time & Money Builders
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

def build_ruler_measurement(data: dict) -> str:
    """
    Ruler measurement activities.
    
    Data:
        - lines: list of {"length_px": int, "label": str}
        - unit: str ("inches" or "cm")
        - include_ruler: bool (show ruler graphic, default True)
        - note: str (optional instruction)
    """
    from math_helpers import ruler_inches, ruler_cm, measure_line
    lines = data.get("lines", [])
    unit = data.get("unit", "inches")
    include_ruler = data.get("include_ruler", True)
    note = data.get("note", "Use the ruler to measure each line.")
    
    html = f'<p class="note">{note}</p>'
    
    if include_ruler:
        html += ruler_inches() if unit == "inches" else ruler_cm()
        html += '<br><br>'
    
    html += '<div class="mgrid c2">\n'
    
    for line in lines:
        length_px = line.get("length_px", 100)
        label = line.get("label", "")
        html += f'<div class="abox">'
        html += f'<div style="display:flex; align-items:center; gap:6px;">'
        html += f'<div class="measure-line" style="width:{length_px}px;"></div>'
        html += f'<span>{label if label else ""} {b("2em")} {unit}</span>'
        html += f'</div></div>\n'
    
    html += '</div>'
    return html


def build_clock_telling(data: dict) -> str:
    """
    Analog clock time-telling activities.
    
    Data:
        - times: list of {"hour": int, "minute": int, "blank": bool}
        - show_digital: bool (include digital time blank, default True)
        - note: str (optional instruction)
    """
    from math_helpers import clock_svg
    times = data.get("times", [])
    show_digital = data.get("show_digital", True)
    note = data.get("note", "Write the time shown on each clock.")
    
    html = f'<p class="note">{note}</p>'
    html += '<div class="mgrid c3">\n'
    
    for time in times:
        hour = time.get("hour", 12)
        minute = time.get("minute", 0)
        blank = time.get("blank", False)
        
        html += f'<div class="abox" style="text-align:center;">'
        html += f'{clock_svg(hour, minute, size=50)}'
        html += f'<p style="margin-top:6px;">'
        if blank:
            html += f'{b("3em")}:{b("3em")}'
        else:
            html += f'{hour:02d}:{minute:02d}'
        html += f'</p></div>\n'
    
    html += '</div>'
    return html


def build_coin_identification(data: dict) -> str:
    """
    Coin identification and values.
    
    Data:
        - coins: list of {"type": str, "count": int, "blank_value": bool}
        - note: str (optional instruction)
    """
    from math_helpers import coin_svg
    coins = data.get("coins", [])
    note = data.get("note", "Identify each coin and write its value.")
    
    # Coin values
    values = {"penny": 1, "nickel": 5, "dime": 10, "quarter": 25}
    names = {"penny": "Penny", "nickel": "Nickel", "dime": "Dime", "quarter": "Quarter"}
    
    html = f'<p class="note">{note}</p>'
    html += '<div class="mgrid c3">\n'
    
    for coin in coins:
        coin_type = coin.get("type", "penny")
        count = coin.get("count", 1)
        blank_value = coin.get("blank_value", False)
        
        html += f'<div class="abox" style="text-align:center;">'
        # Show the coin(s)
        for _ in range(count):
            html += f'{coin_svg(coin_type, size=24)}'
        html += f'<p style="margin-top:4px;">{names.get(coin_type, coin_type)}</p>'
        if blank_value:
            html += f'<p>{b("3em")}ยข</p>'
        else:
            html += f'<p>{values.get(coin_type, 0)}ยข</p>'
        html += f'</div>\n'
    
    html += '</div>'
    return html


def build_money_adding(data: dict) -> str:
    """
    Adding money with coins.
    
    Data:
        - problems: list of {"coins": [{"type": str, "count": int}, ...], "blank": bool}
        - note: str (optional instruction)
    """
    from math_helpers import coin_svg
    problems = data.get("problems", [])
    note = data.get("note", "Count the coins and write the total.")
    
    values = {"penny": 1, "nickel": 5, "dime": 10, "quarter": 25}
    
    html = f'<p class="note">{note}</p>'
    html += '<div class="mgrid c2">\n'
    
    for problem in problems:
        coins = problem.get("coins", [])
        blank = problem.get("blank", True)
        
        # Calculate total
        total = sum(values.get(c["type"], 0) * c["count"] for c in coins)
        
        html += f'<div class="abox" style="text-align:center;">'
        # Show all coins
        for coin in coins:
            for _ in range(coin.get("count", 1)):
                html += f'{coin_svg(coin["type"], size=20)}'
        html += f'<p style="margin-top:6px;">$'
        if blank:
            html += f'{b("2em")}.{b("2em")}'
        else:
            html += f'{total // 100}.{total % 100:02d}'
        html += f'</p></div>\n'
    
    html += '</div>'
    return html


# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
# PHASE 4: Geometry & Data Builders
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

def build_picture_graph(data: dict) -> str:
    """
    Picture graph reading/creating.
    
    Data:
        - categories: list of {"name": str, "count": int, "symbol": str, "blank": bool}
        - key_value: int (what each symbol represents, default 1)
        - note: str (optional instruction)
    """
    categories = data.get("categories", [])
    key_value = data.get("key_value", 1)
    note = data.get("note", "Read the picture graph and answer the questions.")
    symbol = data.get("symbol", "โ˜…")
    
    html = f'<p class="note">{note}</p>'
    html += '<div class="abox" style="margin-bottom:8px;">'
    html += f'<p><b>Key:</b> {symbol} = {key_value}</p></div>\n'
    
    html += '<div class="mgrid c1">\n'
    
    for cat in categories:
        name = cat.get("name", "")
        count = cat.get("count", 0)
        blank = cat.get("blank", False)
        
        html += f'<div class="abox">'
        html += f'<p><b>{name}:</b> '
        if blank:
            html += f'{b("6em")} items'
        else:
            # Show symbols
            symbols = symbol * (count // key_value)
            html += f'{symbols} ({count} items)'
        html += f'</p></div>\n'
    
    html += '</div>'
    return html


def build_bar_graph(data: dict) -> str:
    """
    Bar graph reading/creating.
    
    Data:
        - data: list of {"label": str, "value": int, "blank": bool}
        - max_value: int (maximum value for scale)
        - note: str (optional instruction)
    """
    from math_helpers import bar_graph_svg
    entries = data.get("entries", [])
    max_value = data.get("max_value", 20)
    note = data.get("note", "Read the bar graph.")
    
    # Extract values and labels
    values = [e["value"] for e in entries if not e.get("blank", False)]
    labels = [e["label"] for e in entries]
    colors = [BLUE, GREEN, RED, ORANGE, PURPLE, PINK, TEAL, GOLD][:len(entries)]
    
    html = f'<p class="note">{note}</p>'
    
    if values:
        html += bar_graph_svg(values, colors, labels, width=250, height=120)
        html += '<br><br>'
    
    html += '<div class="mgrid c2">\n'
    
    for entry in entries:
        label = entry.get("label", "")
        value = entry.get("value", 0)
        blank = entry.get("blank", False)
        
        html += f'<div class="abox"><p><b>{label}:</b> '
        if blank:
            html += f'{b("4em")}'
        else:
            html += f'{value}'
        html += f'</p></div>\n'
    
    html += '</div>'
    return html


def build_shape_attributes(data: dict) -> str:
    """
    Shape attribute identification (sides, vertices, angles).
    
    Data:
        - shapes: list of {"name": str, "sides": int, "vertices": int, "blanks": list}
        - note: str (optional instruction)
    """
    from math_helpers import shape_svg
    shapes = data.get("shapes", [])
    note = data.get("note", "Count the sides and vertices of each shape.")
    
    html = f'<p class="note">{note}</p>'
    html += '<table class="tbl">\n'
    html += '<tr><th>Shape</th><th>Picture</th><th>Sides</th><th>Vertices</th></tr>\n'
    
    for shape in shapes:
        name = shape.get("name", "")
        sides = shape.get("sides", 0)
        vertices = shape.get("vertices", 0)
        blanks = shape.get("blanks", [])  # e.g., ["sides", "vertices"]
        
        html += f'<tr>'
        html += f'<td>{name}</td>'
        html += f'<td style="text-align:center;">{shape_svg(name.lower(), size=30)}</td>'
        
        if "sides" in blanks:
            html += f'<td>{b("3em")}</td>'
        else:
            html += f'<td>{sides}</td>'
        
        if "vertices" in blanks:
            html += f'<td>{b("3em")}</td>'
        else:
            html += f'<td>{vertices}</td>'
        
        html += f'</tr>\n'
    
    html += '</table>'
    return html


def build_symmetry(data: dict) -> str:
    """
    Line of symmetry activities.
    
    Data:
        - shapes: list of {"name": str, "has_symmetry": bool, "lines": int}
        - draw_activity: bool (include drawing lines of symmetry)
        - note: str (optional instruction)
    """
    shapes = data.get("shapes", [])
    draw_activity = data.get("draw_activity", True)
    note = data.get("note", "Draw the line(s) of symmetry.")
    
    html = f'<p class="note">{note}</p>'
    html += '<div class="mgrid c3">\n'
    
    for shape in shapes:
        name = shape.get("name", "")
        lines = shape.get("lines", 0)
        
        html += f'<div class="abox" style="text-align:center; padding:15px;">'
        html += f'<p style="font-size:11px; margin-bottom:4px;"><b>{name}</b></p>'
        html += f'<div style="border:2px solid #333; border-radius:4px; width:60px; height:60px; margin:0 auto; position:relative;"></div>'
        html += f'<p style="font-size:10px; margin-top:4px;">{b("2em")} line(s) of symmetry</p>'
        html += f'</div>\n'
    
    html += '</div>'
    return html


def build_area_perimeter(data: dict) -> str:
    """
    Area and perimeter grid activities.
    
    Data:
        - shapes: list of {"rows": int, "cols": int, "blank_area": bool, "blank_perimeter": bool}
        - note: str (optional instruction)
    """
    from math_helpers import grid_svg
    shapes = data.get("shapes", [])
    note = data.get("note", "Count the squares for area. Count the units around for perimeter.")
    
    html = f'<p class="note">{note}</p>'
    html += '<div class="mgrid c2">\n'
    
    for shape in shapes:
        rows = shape.get("rows", 2)
        cols = shape.get("cols", 3)
        blank_area = shape.get("blank_area", True)
        blank_perimeter = shape.get("blank_perimeter", True)
        
        area = rows * cols
        perimeter = 2 * (rows + cols)
        
        html += f'<div class="abox">'
        html += f'{grid_svg(rows, cols, cell=12, gap=1, color=BLUE)}'
        html += f'<p style="margin-top:6px;">'
        html += f'Area: '
        if blank_area:
            html += f'{b("4em")} squares'
        else:
            html += f'{area} squares'
        html += f'</p>'
        html += f'<p>Perimeter: '
        if blank_perimeter:
            html += f'{b("4em")} units'
        else:
            html += f'{perimeter} units'
        html += f'</p></div>\n'
    
    html += '</div>'
    return html


def build_fraction_circle(data: dict) -> str:
    """
    Fraction circle/pie chart activities.
    
    Data:
        - fractions: list of {"numerator": int, "denominator": int, "blank": bool}
        - note: str (optional instruction)
    """
    from math_helpers import fraction_svg
    fractions = data.get("fractions", [])
    note = data.get("note", "Write the fraction shown.")
    
    html = f'<p class="note">{note}</p>'
    html += '<div class="mgrid c4">\n'
    
    for frac in fractions:
        num = frac.get("numerator", 1)
        denom = frac.get("denominator", 2)
        blank = frac.get("blank", True)
        
        html += f'<div class="abox" style="text-align:center;">'
        html += f'{fraction_svg(num, denom, size=40)}'
        html += f'<p style="margin-top:4px; font-size:14px;">'
        if blank:
            html += f'<span style="border-bottom:1.5px solid #333; min-width:2em; display:inline-block;">&nbsp;</span>'
            html += f'<span style="border-bottom:1.5px solid #333; min-width:2em; display:inline-block;">&nbsp;</span>'
        else:
            html += f'{num}/{denom}'
        html += f'</p></div>\n'
    
    html += '</div>'
    return html


# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
# PHASE 5: Multiplication & Division Builders
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

def build_equal_groups(data: dict) -> str:
    """
    Equal groups/multiplication introduction.
    
    Data:
        - groups: list of {"num_groups": int, "items_per_group": int, "blank_total": bool}
        - note: str (optional instruction)
    """
    from math_helpers import eq_group_svg, circle_svg
    groups = data.get("groups", [])
    note = data.get("note", "Count the equal groups and write the total.")
    
    html = f'<p class="note">{note}</p>'
    html += '<div class="mgrid c2">\n'
    
    for group in groups:
        num_groups = group.get("num_groups", 2)
        items_per_group = group.get("items_per_group", 3)
        blank_total = group.get("blank_total", True)
        
        total = num_groups * items_per_group
        
        html += f'<div class="abox">'
        html += f'<p style="font-size:11px; margin-bottom:4px;">{num_groups} groups of {items_per_group}</p>'
        # Show the groups
        for _ in range(num_groups):
            html += f'{eq_group_svg(items_per_group, BLUE)}'
        html += f'<p style="margin-top:6px;">Total: '
        if blank_total:
            html += f'{b("4em")}'
        else:
            html += f'{total}'
        html += f'</p></div>\n'
    
    html += '</div>'
    return html


def build_skip_counting(data: dict) -> str:
    """
    Skip counting with blanks.
    
    Data:
        - sequences: list of {"start": int, "step": int, "count": int, "blank_indices": list}
        - note: str (optional instruction)
    """
    from math_helpers import skip_count_html
    sequences = data.get("sequences", [])
    note = data.get("note", "Skip count by the given number.")
    
    html = f'<p class="note">{note}</p>'
    html += '<div class="mgrid c2">\n'
    
    for seq in sequences:
        start = seq.get("start", 0)
        step = seq.get("step", 5)
        count = seq.get("count", 10)
        blank_indices = seq.get("blank_indices", [])
        
        # Generate sequence
        numbers = [start + i * step for i in range(count)]
        
        html += f'<div class="abox"><p style="font-size:11px; margin-bottom:4px;">Count by {step}:</p>'
        html += f'<div class="skrow">{skip_count_html(numbers, blank_indices, BLUE)}</div></div>\n'
    
    html += '</div>'
    return html


def build_times_table(data: dict) -> str:
    """
    Times table practice.
    
    Data:
        - factor: int (which times table, e.g., 5)
        - blanks: list of indices to blank out (0-10)
        - note: str (optional instruction)
    """
    from math_helpers import times_table_html
    factor = data.get("factor", 5)
    blanks = data.get("blanks", [])
    note = data.get("note", f"Practice the {factor}s times table.")
    color = data.get("color", BLUE)
    
    html = f'<p class="note">{note}</p>'
    html += '<div class="abox">\n'
    
    # Build custom times table with blanks
    html += f'<h4 style="color:{color}; margin-bottom:8px;">The {factor}s</h4>'
    for i in range(11):
        result = factor * i
        if i in blanks:
            html += f'<div style="padding:2px 0; font-size:13px;"><span>{factor} ร— {i} = </span>{b("3em")}</div>'
        else:
            html += f'<div style="padding:2px 0; font-size:13px;"><span>{factor} ร— {i} = </span><span style="color:{color}; font-weight:700;">{result}</span></div>'
    
    html += '</div>'
    return html


# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
# CELEBRATION: Scavenger Hunt & Certificate
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

def build_scavenger_hunt(data: dict) -> str:
    """
    Math scavenger hunt cards.
    
    Data:
        - clues: list of {"question": str, "answer": str, "hint": str}
        - note: str (optional instruction)
    """
    clues = data.get("clues", [])
    note = data.get("note", "Solve each clue and find the answer!")
    
    html = f'<p class="note">{note}</p>'
    html += '<div class="mgrid c2">\n'
    
    for i, clue in enumerate(clues, 1):
        question = clue.get("question", "")
        hint = clue.get("hint", "")
        
        html += f'<div class="scavenger-card">'
        html += f'<p class="clue"><b>Clue {i}:</b> {question}</p>'
        if hint:
            html += f'<p style="font-size:10px; color:#999; margin-top:4px;">Hint: {hint}</p>'
        html += f'<p style="margin-top:6px;">Answer: {b("6em")}</p>'
        html += f'</div>\n'
    
    html += '</div>'
    return html


def build_certificate(data: dict) -> str:
    """
    Completion certificate.
    
    Data:
        - title: str (certificate title)
        - subtitle: str (subtitle)
        - achievement: str (what was achieved)
        - include_name: bool (include name line, default True)
    """
    title = data.get("title", "Certificate of Achievement")
    subtitle = data.get("subtitle", "This certifies that")
    achievement = data.get("achievement", "has completed 2nd Grade Math")
    include_name = data.get("include_name", True)
    
    html = '<div class="cert-border">\n'
    html += f'<div class="cert-title">{title}</div>\n'
    html += f'<div class="cert-subtitle">{subtitle}</div>\n'
    if include_name:
        html += f'<div class="cert-name"></div>\n'
    html += f'<div class="cert-body">{achievement}</div>\n'
    html += f'<div style="margin-top:16px;">'
    html += f'<div style="border-top:2px solid #333; width:120px; margin:0 auto;"></div>'
    html += f'<p style="font-size:10px; margin-top:4px;">Teacher Signature</p>'
    html += f'</div>'
    html += f'<div style="margin-top:16px;">'
    html += f'<div style="border-top:2px solid #333; width:100px; margin:0 auto;"></div>'
    html += f'<p style="font-size:10px; margin-top:4px;">Date</p>'
    html += f'</div>\n'
    html += '</div>'
    return html


# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
# Utility: Get all available builders
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

def build_addition_facts(data: dict) -> str:
    """
    Simple addition fact practice.
    
    Data:
        - facts: list of strings like "8 + 5" or tuples (left, right)
        - note: str (optional instruction)
        - cols: int (number of columns, default 2)
        - font_size: int (default 16)
    """
    facts = data.get("facts", [])
    note = data.get("note", "Solve each problem.")
    cols = data.get("cols", 2)
    font_size = data.get("font_size", 16)
    
    html = f'<p class="note">{note}</p>'
    html += f'<div class="mgrid c{cols}">\n'
    
    for fact in facts:
        if isinstance(fact, tuple):
            fact_str = f"{fact[0]} + {fact[1]}"
        else:
            fact_str = fact
        html += f'<div class="abox"><p style="font-size:{font_size}px;">{fact_str} = {b("4em")}</p></div>\n'
    
    html += '</div>'
    return html


def build_addition_word_problems(data: dict) -> str:
    """
    Addition word problems.
    
    Data:
        - problems: list of dicts with "text" key
        - note: str (optional instruction)
    """
    problems = data.get("problems", [])
    note = data.get("note", "Read carefully and solve.")
    
    html = f'<div class="wp">\n'
    for problem in problems:
        text = problem.get("text", "")
        html += f'<p>{text} {b("6em")}</p>\n'
    html += '</div>\n'
    return html


def build_missing_addend_facts(data: dict) -> str:
    """
    Missing addend practice (e.g., 5 + ___ = 10).
    
    Data:
        - facts: list of strings like "5 + ___ = 10"
        - note: str (optional instruction)
        - cols: int (number of columns, default 2)
    """
    facts = data.get("facts", [])
    note = data.get("note", "What number is missing?")
    cols = data.get("cols", 2)
    
    html = f'<p class="note">{note}</p>'
    html += f'<div class="mgrid c{cols}">\n'
    
    for fact in facts:
        html += f'<div class="abox" style="text-align:center;"><p>{fact}</p></div>\n'
    
    html += '</div>'
    return html


def build_subtraction_facts(data: dict) -> str:
    """
    Simple subtraction fact practice.
    
    Data:
        - facts: list of tuples (minuend, subtrahend) or strings like "10 - 3"
        - note: str (optional instruction)
        - cols: int (number of columns, default 2)
        - font_size: int (font size in px, default 16)
    """
    facts = data.get("facts", [])
    note = data.get("note", "Solve each subtraction problem.")
    cols = data.get("cols", 2)
    font_size = data.get("font_size", 16)
    
    html = f'<p class="note">{note}</p>'
    html += f'<div class="mgrid c{cols}">\n'
    
    for fact in facts:
        if isinstance(fact, tuple):
            minuend, subtrahend = fact
            fact_str = f"{minuend} โˆ’ {subtrahend} = {b('4em')}"
        else:
            # Parse string like "10 - 3"
            parts = fact.replace("-", "").replace("โˆ’", "").split()
            minuend, subtrahend = int(parts[0]), int(parts[1])
            fact_str = f"{minuend} โˆ’ {subtrahend} = {b('4em')}"
        
        html += f'<div class="abox" style="text-align:center; font-size:{font_size}px;"><p>{fact_str}</p></div>\n'
    
    html += '</div>'
    return html


def build_subtraction_word_problems(data: dict) -> str:
    """
    Subtraction word problems.
    
    Data:
        - problems: list of dicts with "text" key
        - note: str (optional instruction)
    """
    problems = data.get("problems", [])
    note = data.get("note", "Read each problem carefully and solve it.")
    
    html = f'<div class="wp">\n'
    for problem in problems:
        text = problem.get("text", "")
        html += f'<p>{text}</p>\n'
    html += '</div>\n'
    return html


def build_fact_families_subtraction(data: dict) -> str:
    """
    Fact family practice with subtraction focus.
    
    Data:
        - families: list of [addend1, addend2, sum] triples
        - note: str (optional instruction)
        - cols: int (number of columns, default 2)
    """
    families = data.get("families", [])
    note = data.get("note", "Write the related subtraction facts.")
    cols = data.get("cols", 2)
    
    html = f'<p class="note">{note}</p>'
    html += f'<div class="mgrid c{cols}">\n'
    
    for family in families:
        addend1, addend2, total = family[0], family[1], family[2]
        html += f'<div class="abox"><p>{addend1} + {addend2} = {total}</p>'
        html += f'<p>{total} โˆ’ {addend1} = {b("4em")}</p>'
        html += f'<p>{total} โˆ’ {addend2} = {b("4em")}</p></div>\n'
    
    html += '</div>'
    return html


def build_challenge_box(data: dict) -> str:
    """
    Challenge box with star border.
    
    Data:
        - problems: list of dicts with "text" key (HTML allowed)
    """
    problems = data.get("problems", [])
    
    html = '<div class="starbox"><p><b>Challenge</b></p>'
    for problem in problems:
        html += f'<p>{problem.get("text", "")}</p>'
    html += '</div>'
    return html


def get_available_builders() -> list:
    """Return list of all available builder function names."""
    return [
        # Phase 1
        "build_number_bonds",
        "build_fact_families",
        "build_make_10_strategy",
        "build_pairs_to_make",
        "build_missing_addend",
        "build_missing_subtrahend",
        "build_addition_facts",
        "build_addition_word_problems",
        "build_missing_addend_facts",
        "build_challenge_box",
        "build_subtraction_facts",
        "build_subtraction_word_problems",
        "build_fact_families_subtraction",
        # Phase 2
        "build_column_addition",
        "build_column_subtraction",
        "build_regrouping_visual",
        "build_two_step_problems",
        # Phase 3
        "build_ruler_measurement",
        "build_clock_telling",
        "build_coin_identification",
        "build_money_adding",
        # Phase 4
        "build_picture_graph",
        "build_bar_graph",
        "build_shape_attributes",
        "build_symmetry",
        "build_area_perimeter",
        "build_fraction_circle",
        # Phase 5
        "build_equal_groups",
        "build_skip_counting",
        "build_times_table",
        # Celebration
        "build_scavenger_hunt",
        "build_certificate",
    ]


if __name__ == "__main__":
    print("Available activity builders:")
    for name in get_available_builders():
        print(f"  โ€ข {name}")
    print(f"\nTotal: {len(get_available_builders())} builders")