"""
Build xaira-open-frontier-deck.pptx from outline using python-pptx.
Dark professional theme matching the HTML deck.
"""

from pptx import Presentation
from pptx.util import Inches, Pt, Emu
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN
from pptx.util import Pt
import sys

# ── Catppuccin Mocha palette ──────────────────────────────────────────────────
BG          = RGBColor(0x1e, 0x1e, 0x2e)   # slide background
SURFACE     = RGBColor(0x31, 0x32, 0x44)   # card background
TEXT_1      = RGBColor(0xcd, 0xd6, 0xf4)   # primary text
TEXT_2      = RGBColor(0xa6, 0xad, 0xc8)   # secondary text
TEXT_3      = RGBColor(0x7f, 0x84, 0x9c)   # muted text
ACCENT      = RGBColor(0xcb, 0xa6, 0xf7)   # purple
ACCENT_2    = RGBColor(0x89, 0xb4, 0xfa)   # blue
ACCENT_3    = RGBColor(0x94, 0xe2, 0xd5)   # teal
GOOD        = RGBColor(0xa6, 0xe3, 0xa1)   # green
WARN        = RGBColor(0xf9, 0xe2, 0xaf)   # yellow
WHITE       = RGBColor(0xff, 0xff, 0xff)

# Slide size 16:9
SLIDE_W = Inches(13.333)
SLIDE_H = Inches(7.5)

prs = Presentation()
prs.slide_width  = SLIDE_W
prs.slide_height = SLIDE_H

blank_layout = prs.slide_layouts[6]  # completely blank

def add_slide():
    return prs.slides.add_slide(blank_layout)

def bg(slide, color=BG):
    """Fill slide background."""
    bg = slide.background
    fill = bg.fill
    fill.solid()
    fill.fore_color.rgb = color

def box(slide, left, top, width, height,
        fill_color=None, border_color=None, border_pt=None):
    """Add a rectangle."""
    from pptx.util import Pt
    shape = slide.shapes.add_shape(
        1,  # MSO_SHAPE_TYPE.RECTANGLE
        Inches(left), Inches(top), Inches(width), Inches(height)
    )
    if fill_color:
        shape.fill.solid()
        shape.fill.fore_color.rgb = fill_color
    else:
        shape.fill.background()
    if border_color and border_pt:
        shape.line.color.rgb = border_color
        shape.line.width = Pt(border_pt)
    else:
        shape.line.fill.background()
    return shape

def textbox(slide, text, left, top, width, height,
            size=18, bold=False, color=TEXT_1,
            align=PP_ALIGN.LEFT, wrap=True, italic=False):
    """Add a text box."""
    txBox = slide.shapes.add_textbox(
        Inches(left), Inches(top), Inches(width), Inches(height)
    )
    tf = txBox.text_frame
    tf.word_wrap = wrap
    p = tf.paragraphs[0]
    p.alignment = align
    run = p.add_run()
    run.text = text
    run.font.size = Pt(size)
    run.font.bold = bold
    run.font.italic = italic
    run.font.color.rgb = color
    return txBox

def add_para(tf, text, size=14, bold=False, color=TEXT_2,
             align=PP_ALIGN.LEFT, italic=False, space_before=0):
    """Add a paragraph to an existing text frame."""
    from pptx.util import Pt
    p = tf.add_paragraph()
    p.alignment = align
    p.space_before = Pt(space_before)
    run = p.add_run()
    run.text = text
    run.font.size = Pt(size)
    run.font.bold = bold
    run.font.italic = italic
    run.font.color.rgb = color
    return p

def footer(slide, num, total=8):
    """Add footer bar."""
    textbox(slide, "Xaira Therapeutics — Confidential",
            0.3, 7.05, 9, 0.35, size=9, color=TEXT_3)
    textbox(slide, f"{num} / {total}",
            12.3, 7.05, 1, 0.35, size=9, color=TEXT_3, align=PP_ALIGN.RIGHT)
    # thin accent bar at bottom
    bar = box(slide, 0, 7.45, 13.333, 0.05, fill_color=ACCENT)

def kicker(slide, text, left=0.5, top=0.55, width=12):
    textbox(slide, text.upper(), left, top, width, 0.3,
            size=9, bold=True, color=ACCENT)

# ─────────────────────────────────────────────────────────────────────────────
# SLIDE 1 — TITLE
# ─────────────────────────────────────────────────────────────────────────────
s1 = add_slide(); bg(s1)

# Decorative accent bar
box(s1, 0.5, 1.15, 0.6, 0.06, fill_color=ACCENT)

kicker(s1, "Xaira Therapeutics · Internal Strategy", 0.5, 0.62)

textbox(s1, "Open Frontier Intelligence\nfor Drug Discovery",
        0.5, 1.3, 8.5, 2.6, size=46, bold=True, color=TEXT_1)

textbox(s1, "As general-purpose AI commoditizes, durable advantage shifts to\nthe deepest domain-specific scientific intelligence.",
        0.5, 4.05, 9, 1.0, size=16, color=TEXT_2, italic=True)

# Meta row
textbox(s1, "Bo Wang · Chief AI Scientist", 0.5, 5.25, 4.5, 0.35, size=11, color=TEXT_3)
textbox(s1, "July 22, 2026", 5.2, 5.25, 3, 0.35, size=11, color=TEXT_3)
textbox(s1, "Landscape updated: July 22, 2026", 8.2, 5.25, 4.8, 0.35, size=11, color=TEXT_3)

# Decorative large circle glow (just a pale large box)
circ = box(s1, 9.5, -1.5, 5, 5, fill_color=RGBColor(0x2a, 0x1e, 0x3e))
circ.line.fill.background()

footer(s1, 1)

# ─────────────────────────────────────────────────────────────────────────────
# SLIDE 2 — THE AI PLATFORM SHIFT
# ─────────────────────────────────────────────────────────────────────────────
s2 = add_slide(); bg(s2)
kicker(s2, "The Strategic Context")
textbox(s2, "The AI Platform Shift", 0.5, 0.9, 12, 0.9, size=38, bold=True, color=TEXT_1)

# Left card — Yesterday
b1 = box(s2, 0.5, 1.95, 5.9, 2.1, fill_color=SURFACE)
b1.line.color.rgb = TEXT_3; b1.line.width = Pt(1)
textbox(s2, "Yesterday's Question", 0.7, 2.0, 5.5, 0.4, size=10, bold=True, color=TEXT_3)
textbox(s2, "Can AI do this?", 0.7, 2.45, 5.5, 0.7, size=26, bold=True, color=TEXT_1)
textbox(s2, "Foundation models proved they can reason, code, and\nsynthesize knowledge. The answer is yes.",
        0.7, 3.15, 5.4, 0.8, size=12, color=TEXT_2)

# Right card — Today
b2 = box(s2, 6.9, 1.95, 5.9, 2.1, fill_color=SURFACE)
b2.line.color.rgb = ACCENT; b2.line.width = Pt(2)
textbox(s2, "Today's Question", 7.1, 2.0, 5.5, 0.4, size=10, bold=True, color=ACCENT)
textbox(s2, "Which layer holds\nthe durable advantage?", 7.1, 2.38, 5.5, 1.0, size=22, bold=True, color=TEXT_1)
textbox(s2, "General capability is rapidly commoditizing across\nboth closed and open-weight models.",
        7.1, 3.45, 5.5, 0.5, size=12, color=TEXT_2)

# Bottom insight box
bi = box(s2, 0.5, 4.35, 12.3, 1.45, fill_color=RGBColor(0x28, 0x20, 0x38))
bi.line.color.rgb = ACCENT; bi.line.width = Pt(1)
textbox(s2, "🧬  For drug discovery, the durable edge will not come from having the largest general model.\n"
            "It will come from the deepest scientific intelligence — grounded in proprietary biological data,\n"
            "experimental infrastructure, and expert feedback loops.",
        0.75, 4.5, 11.8, 1.1, size=13, color=TEXT_1)
textbox(s2, "The base model is the commodity. The scientific intelligence built on top of it is the moat.",
        0.75, 5.6, 11.8, 0.35, size=11, color=TEXT_3, italic=True)

footer(s2, 2)

# ─────────────────────────────────────────────────────────────────────────────
# SLIDE 3 — THE OPEN FRONTIER IS ACCELERATING
# ─────────────────────────────────────────────────────────────────────────────
s3 = add_slide(); bg(s3)
kicker(s3, "Competitive Landscape · Updated July 22, 2026")
textbox(s3, "The Open Frontier Is Accelerating", 0.5, 0.9, 12, 0.7, size=34, bold=True, color=TEXT_1)

# Table header
ROW_H = 0.58
TABLE_TOP = 1.75
COL = [0.5, 2.35, 4.05, 6.0, 7.1, 9.8]  # left edges
WIDTHS = [1.8, 1.65, 1.9, 1.05, 2.65, 3.3]

HEADERS = ["Model", "Developer", "Weights", "Context", "Key Capability", "Xaira Relevance"]
for i, (h, w) in enumerate(zip(HEADERS, WIDTHS)):
    hb = box(s3, COL[i], TABLE_TOP, w, 0.42, fill_color=RGBColor(0x18, 0x18, 0x25))
    hb.line.fill.background()
    textbox(s3, h.upper(), COL[i]+0.05, TABLE_TOP+0.03, w-0.1, 0.35,
            size=8, bold=True, color=ACCENT)

ROWS = [
    ("GLM-5.2", "Z.ai (Zhipu AI)", "✓ MIT\nDownloadable", "1M tokens",
     "Long-horizon reasoning,\nagentic tool use, coding",
     "Long-context analysis;\nscientific reasoning chains"),
    ("Kimi K2.6", "Moonshot AI", "✓ Open weights", "256K tokens",
     "Multimodal agentic;\nswarm orchestration",
     "Multi-step task decomp;\nvision input"),
    ("Kimi K3\n(announced)", "Moonshot AI", "⏳ Expected\nJuly 27, 2026", "TBA",
     "3T-class; new attention\narchitecture; frontier",
     "Cannot verify —\nweights not public yet"),
    ("Inkling", "Thinking\nMachines Lab", "✓ Open weights\n(Acceptable Use)", "1M tokens",
     "Text + image + audio;\ncalibrated confidence",
     "Multimodal bio data;\nuncertainty quantification"),
    ("DeepSeek-\nV4-Pro", "DeepSeek", "✓ Open weights", "1M tokens",
     "Top open-source\nreasoning & knowledge",
     "Scientific literature;\ncomplex reasoning chains"),
]

WEIGHT_COLORS = [GOOD, GOOD, WARN, GOOD, GOOD]

for r, (row, wc) in enumerate(zip(ROWS, WEIGHT_COLORS)):
    ry = TABLE_TOP + 0.42 + r * ROW_H
    row_bg = RGBColor(0x28, 0x28, 0x38) if r % 2 == 0 else RGBColor(0x25, 0x25, 0x35)
    for ci, (cell, w) in enumerate(zip(row, WIDTHS)):
        cb = box(s3, COL[ci], ry, w, ROW_H, fill_color=row_bg)
        cb.line.fill.background()
        col_color = TEXT_1 if ci == 0 else (wc if ci == 2 else TEXT_2)
        textbox(s3, cell, COL[ci]+0.05, ry+0.05, w-0.1, ROW_H-0.08,
                size=9, color=col_color, bold=(ci == 0))

# Quote
qb = box(s3, 0.5, 5.3, 12.3, 0.88, fill_color=RGBColor(0x22, 0x1a, 0x32))
qb.line.color.rgb = ACCENT; qb.line.width = Pt(1)
textbox(s3, '"The strategic question is no longer whether open-weight models will become capable enough.\n'
            'It is how Xaira can turn rapidly improving general capabilities into proprietary scientific intelligence."',
        0.7, 5.38, 11.9, 0.75, size=11, color=TEXT_1, italic=True)

footer(s3, 3)

# ─────────────────────────────────────────────────────────────────────────────
# SLIDE 4 — BUILD ABOVE THE BASE-MODEL LAYER
# ─────────────────────────────────────────────────────────────────────────────
s4 = add_slide(); bg(s4)
kicker(s4, "The Architecture")
textbox(s4, "Build Above the Base-Model Layer", 0.5, 0.9, 8.5, 0.7, size=34, bold=True, color=TEXT_1)
textbox(s4, "Xaira's durable asset is the scientific intelligence built above the\nbase-model layer — not dependence on any single external model.",
        0.5, 1.7, 7.2, 0.8, size=13, color=TEXT_2)

# Insight box
ib = box(s4, 0.5, 2.62, 7.0, 0.85, fill_color=RGBColor(0x1e, 0x28, 0x20))
ib.line.color.rgb = GOOD; ib.line.width = Pt(1)
textbox(s4, "The base model is interchangeable. As open-weight models improve, Xaira\ncan swap in a better foundation without rebuilding the proprietary stack above.",
        0.7, 2.72, 6.7, 0.65, size=11, color=TEXT_1)

# Architecture diagram (right side)
ARCH_LEFT = 8.0
ARCH_W = 5.0
LAYER_H = 0.75
ARROW_H = 0.22
ARCH_TOP = 0.75

LAYERS = [
    ("INTERCHANGEABLE · BASE LAYER", "Open Frontier Model",
     "GLM-5.2 · Kimi K3 · Inkling · DeepSeek-V4-Pro",
     RGBColor(0x2a, 0x20, 0x3e), ACCENT),
    ("XAIRA — POST-TRAINING", "Scientific Reasoning Layer",
     "Domain-specific fine-tuning + RL from expert feedback",
     RGBColor(0x2a, 0x20, 0x3e), ACCENT),
    ("XAIRA — KNOWLEDGE", "Proprietary Biological Knowledge",
     "Internal publications · program context · RAG",
     RGBColor(0x1a, 0x22, 0x38), ACCENT_2),
    ("XAIRA — INFRASTRUCTURE", "X-Cell · Models · Agents · Tools",
     "Causal virtual cell (X-Atlas) · experimental systems",
     RGBColor(0x1a, 0x22, 0x38), ACCENT_2),
    ("OUTPUT", "Therapeutic Decisions & Wet-Lab Outcomes",
     "Closed loop: experiments validate and update the stack",
     RGBColor(0x1a, 0x28, 0x1a), GOOD),
]

y = ARCH_TOP
for label, title, sub, fill, border in LAYERS:
    lb = box(s4, ARCH_LEFT, y, ARCH_W, LAYER_H, fill_color=fill)
    lb.line.color.rgb = border; lb.line.width = Pt(1)
    textbox(s4, label, ARCH_LEFT+0.1, y+0.04, ARCH_W-0.2, 0.2, size=7, bold=True, color=border)
    textbox(s4, title, ARCH_LEFT+0.1, y+0.22, ARCH_W-0.2, 0.3, size=11, bold=True, color=TEXT_1)
    textbox(s4, sub, ARCH_LEFT+0.1, y+0.52, ARCH_W-0.2, 0.2, size=8, color=TEXT_3)
    y += LAYER_H
    if label != "OUTPUT":
        # Arrow
        textbox(s4, "↓", ARCH_LEFT + ARCH_W/2 - 0.15, y + 0.01, 0.4, 0.22,
                size=10, color=TEXT_3, align=PP_ALIGN.CENTER)
        y += ARROW_H

footer(s4, 4)

# ─────────────────────────────────────────────────────────────────────────────
# SLIDE 5 — MODEL EVALUATION FRAMEWORK
# ─────────────────────────────────────────────────────────────────────────────
s5 = add_slide(); bg(s5)
kicker(s5, "Internal Capability Assessment")
textbox(s5, "Model Evaluation Framework", 0.5, 0.9, 12, 0.7, size=34, bold=True, color=TEXT_1)
textbox(s5, "Propose an internal program comparing open-weight candidates on drug-discovery tasks.\n"
            "Key principle: use proprietary Xaira scientific tasks — not public benchmarks alone.",
        0.5, 1.7, 12, 0.65, size=13, color=TEXT_2)

EVALS = [
    ("🧬", "Bio & chem knowledge", "Domain knowledge depth", ACCENT),
    ("🔗", "Causal reasoning", "Perturbation → phenotype logic", ACCENT_2),
    ("📄", "Long-context analysis", "Multi-document synthesis", ACCENT_3),
    ("🖼", "Multimodal understanding", "Omics · imaging · structure", GOOD),
    ("🧪", "Experiment planning", "Hypothesis → assay design", WARN),
    ("📊", "Perturbation / omics", "X-Atlas CRISPR data reasoning", RGBColor(0xf3, 0x8b, 0xa8)),
    ("🎯", "Uncertainty calibration", "Confidence vs. correctness", ACCENT),
    ("🔧", "Tool use & agentic exec", "Multi-step autonomous tasks", ACCENT_2),
    ("⚡", "Latency · cost · privacy", "License · fine-tune feasibility", ACCENT_3),
]

COL_COUNT = 3
ITEM_W = 4.2
ITEM_H = 0.85
GAP_X = 0.22
GAP_Y = 0.14
START_X = 0.5
START_Y = 2.55

for i, (icon, title, sub, color) in enumerate(EVALS):
    col = i % COL_COUNT
    row = i // COL_COUNT
    ix = START_X + col * (ITEM_W + GAP_X)
    iy = START_Y + row * (ITEM_H + GAP_Y)
    eb = box(s5, ix, iy, ITEM_W, ITEM_H, fill_color=SURFACE)
    eb.line.fill.background()
    # colored left stripe
    stripe = box(s5, ix, iy, 0.07, ITEM_H, fill_color=color)
    stripe.line.fill.background()
    textbox(s5, icon, ix+0.1, iy+0.08, 0.45, 0.55, size=18)
    textbox(s5, title, ix+0.6, iy+0.1, ITEM_W-0.7, 0.35, size=11, bold=True, color=TEXT_1)
    textbox(s5, sub, ix+0.6, iy+0.48, ITEM_W-0.7, 0.3, size=9, color=TEXT_3)

footer(s5, 5)

# ─────────────────────────────────────────────────────────────────────────────
# SLIDE 6 — THREE STRATEGIC ROLES
# ─────────────────────────────────────────────────────────────────────────────
s6 = add_slide(); bg(s6)
kicker(s6, "Strategic Options")
textbox(s6, "Three Strategic Roles for Open Frontier Models", 0.5, 0.9, 12, 0.7, size=32, bold=True, color=TEXT_1)

ROLES = [
    ("1", "Base-model candidates", "Role 1 · Post-training",
     "Start from an open-weight checkpoint and fine-tune on Xaira's scientific data.\n"
     "Full control over model behavior; no API dependency.",
     ["Fine-tune feasible", "Data privacy"], ACCENT, RGBColor(0x28, 0x20, 0x3e)),
    ("2", "Teacher models", "Role 2 · Synthetic data",
     "Use a capable open-weight model to generate and critique scientific reasoning traces.\n"
     "Distill its knowledge into a smaller, Xaira-tuned production model.",
     ["No inference cost", "No deployment risk"], ACCENT_2, RGBColor(0x1a, 0x22, 0x38)),
    ("3", "Orchestrated model portfolio", "Role 3 · Orchestration",
     "Dynamically route tasks to the best model based on capability, cost, modality,\nprivacy, and latency. No single permanent foundation model.",
     ["Resilient", "Future-proof"], ACCENT_3, RGBColor(0x1a, 0x28, 0x28)),
]

CARD_W = 4.1
for i, (num, title, label, body, tags, color, fill) in enumerate(ROLES):
    cx = 0.5 + i * (CARD_W + 0.22)
    cb = box(s6, cx, 1.8, CARD_W, 4.0, fill_color=fill)
    cb.line.color.rgb = color; cb.line.width = Pt(1.5)
    # Big number watermark
    textbox(s6, num, cx + CARD_W - 0.95, 1.85, 0.8, 0.9, size=52, bold=True, color=SURFACE)
    textbox(s6, label.upper(), cx+0.2, 1.88, CARD_W-0.3, 0.28, size=8, bold=True, color=color)
    textbox(s6, title, cx+0.2, 2.2, CARD_W-0.3, 0.55, size=16, bold=True, color=TEXT_1)
    textbox(s6, body, cx+0.2, 2.82, CARD_W-0.3, 1.3, size=11, color=TEXT_2)
    for t, tag in enumerate(tags):
        tag_b = box(s6, cx+0.2 + t*1.55, 4.28, 1.45, 0.28,
                    fill_color=RGBColor(0x20, 0x20, 0x30))
        tag_b.line.color.rgb = color; tag_b.line.width = Pt(0.5)
        textbox(s6, tag, cx+0.25 + t*1.55, 4.3, 1.35, 0.24, size=9, color=color)

# Bottom recommendation
rb = box(s6, 0.5, 5.95, 12.3, 0.85, fill_color=RGBColor(0x1a, 0x28, 0x28))
rb.line.color.rgb = ACCENT_3; rb.line.width = Pt(1)
textbox(s6, "Recommendation: evaluate whether Role 3 (portfolio orchestration) offers more resilience than committing to one permanent\nfoundation model. The open frontier moves fast enough that today's best checkpoint may not be tomorrow's.",
        0.7, 6.03, 12.0, 0.72, size=11, color=TEXT_1)

footer(s6, 6)

# ─────────────────────────────────────────────────────────────────────────────
# SLIDE 7 — XAIRA'S PROPRIETARY MOAT
# ─────────────────────────────────────────────────────────────────────────────
s7 = add_slide(); bg(s7)
kicker(s7, "Xaira's Durable Advantage")
textbox(s7, "What Transforms General Intelligence\ninto Scientific Intelligence",
        0.5, 0.9, 11, 0.9, size=34, bold=True, color=TEXT_1)

MOAT = [
    ("Domain-specific post-training", "Fine-tuning on Xaira's scientific tasks and expert feedback", ACCENT),
    ("Proprietary multimodal biological data", "Internal omics, imaging, structural, and experimental datasets", ACCENT_2),
    ("X-Cell grounding", "Causal virtual cell trained on X-Atlas CRISPR perturbation data", ACCENT_3),
    ("Retrieval over internal knowledge", "Scientific publications, program history, clinical context", GOOD),
    ("Specialized scientific tools", "Structure prediction, docking, analysis pipelines", WARN),
    ("Agent orchestration", "Multi-agent planning, long-horizon execution, tool calling", RGBColor(0xf3, 0x8b, 0xa8)),
    ("Expert feedback loops", "Biologists, chemists, clinicians in the training loop", ACCENT),
    ("Wet-lab validation", "Experimental outcomes close the learning loop", ACCENT_2),
    ("Continual learning from experiments", "Every assay, every experiment updates the system — competitors without wet-lab infrastructure cannot replicate this", ACCENT_3),
]

ITEM_W2 = 6.0
ITEM_H2 = 0.56
GAP2 = 0.1
START_X2 = 0.5
START_Y2 = 2.0

for i, (title, sub, color) in enumerate(MOAT):
    col = i % 2
    row = i // 2
    mx = START_X2 + col * (ITEM_W2 + 0.3)
    my = START_Y2 + row * (ITEM_H2 + GAP2)
    if i == 8:  # last item full width
        mx = START_X2
        my = START_Y2 + 4 * (ITEM_H2 + GAP2)
        mb = box(s7, mx, my, ITEM_W2*2+0.3, ITEM_H2, fill_color=SURFACE)
    else:
        mb = box(s7, mx, my, ITEM_W2, ITEM_H2, fill_color=SURFACE)
    mb.line.fill.background()
    dot = box(s7, mx+0.12, my + ITEM_H2/2 - 0.05, 0.1, 0.1, fill_color=color)
    dot.line.fill.background()
    textbox(s7, title, mx+0.32, my+0.04, ITEM_W2-0.4, 0.26, size=11, bold=True, color=TEXT_1)
    textbox(s7, sub, mx+0.32, my+0.3, ITEM_W2-0.4, 0.24, size=9, color=TEXT_3)

footer(s7, 7)

# ─────────────────────────────────────────────────────────────────────────────
# SLIDE 8 — CALL TO ACTION
# ─────────────────────────────────────────────────────────────────────────────
s8 = add_slide(); bg(s8)
kicker(s8, "Call to Action")
textbox(s8, "Proposed Next Steps", 0.5, 0.9, 12, 0.65, size=38, bold=True, color=TEXT_1)
textbox(s8, "Three concrete actions to build Xaira's open frontier intelligence capability within 90 days.",
        0.5, 1.65, 11, 0.45, size=14, color=TEXT_2)

STEPS = [
    ("1", "Launch a structured internal model evaluation",
     "Design a benchmark suite using real Xaira scientific tasks across the 9 evaluation dimensions.\n"
     "Test GLM-5.2, Inkling, DeepSeek-V4-Pro, and Kimi K2.6. Include Kimi K3 when released (expected July 27, 2026).",
     ACCENT),
    ("2", "Designate a model strategy owner",
     "Assign a senior technical lead to own the open-weight model strategy: evaluate candidates,\n"
     "monitor license terms, track the landscape, and recommend the architecture. Sustained function, not a one-time analysis.",
     ACCENT_2),
    ("3", "Establish a 90-day pilot",
     "Select one high-value drug discovery task. Deploy an open-weight model (Role 1 or 3).\n"
     "Integrate with X-Cell, retrieval systems, and at least one wet-lab feedback loop.\n"
     "Goal: the closed-loop learning system — not a demo.",
     ACCENT_3),
]

STEP_H = 1.55
STEP_Y = 2.3
for i, (num, title, body, color) in enumerate(STEPS):
    sy = STEP_Y + i * (STEP_H + 0.12)
    sb = box(s8, 0.5, sy, 12.3, STEP_H, fill_color=SURFACE)
    sb.line.fill.background()
    # Number circle (simulated with small box)
    nc = box(s8, 0.6, sy + STEP_H/2 - 0.22, 0.44, 0.44, fill_color=color)
    nc.line.fill.background()
    textbox(s8, num, 0.63, sy + STEP_H/2 - 0.20, 0.38, 0.4,
            size=16, bold=True, color=BG, align=PP_ALIGN.CENTER)
    textbox(s8, title, 1.2, sy + 0.14, 11.0, 0.38, size=14, bold=True, color=TEXT_1)
    textbox(s8, body, 1.2, sy + 0.56, 11.0, 0.9, size=11, color=TEXT_2)

footer(s8, 8)

# ─────────────────────────────────────────────────────────────────────────────
# SAVE
# ─────────────────────────────────────────────────────────────────────────────
out = "/Users/bowang/.openclaw/workspace/xaira-open-frontier-deck.pptx"
prs.save(out)
print(f"Saved: {out}")
print(f"Slides: {len(prs.slides)}")
