root = Card([header, health, actionsBar, projectsSection, milestonesSection, budgetSection, teamSection, paceSection, risksSection, blockedSection], "card", "column", "l")
header = CardHeader("Project Portfolio Review", "Q3 FY25 · Snapshot Oct 14, 2025")
health = Callout("success", "Portfolio health", "On track — 2 green, 2 amber, 1 red; one delivery risk to watch, budgets within tolerance.")
actionsBar = Card([quarterForm, exportGroup, requestDialog, archiveDialog], "sunk", "row", "m")
quarterForm = Form("quarter-form", quarterButtons, [quarterField])
quarterField = FormControl("Quarter", quarterSelect)
quarterSelect = Select("quarter", [quarterQ2, quarterQ3], "Select quarter", null, "Q3 2025")
quarterQ2 = SelectItem("q2-2025", "Q2 2025")
quarterQ3 = SelectItem("q3-2025", "Q3 2025")
quarterButtons = Buttons([applyBtn])
applyBtn = Button("Apply", null, "secondary")
exportGroup = Buttons([exportBtn])
exportBtn = Button("Export review", null, "secondary")
requestDialog = DialogBlock("Request more people", "Request additional headcount", "Tell us which project needs more people.", [requestForm])
requestForm = Form("resource-request", requestButtons, [requestProjectField, requestRoleField, requestCountField])
requestProjectField = FormControl("Project", requestProjectSelect)
requestProjectSelect = Select("request_project", [requestAtlas, requestNova, requestOrion, requestVega, requestLyra], "Select project")
requestAtlas = SelectItem("atlas", "Atlas")
requestNova = SelectItem("nova", "Nova")
requestOrion = SelectItem("orion", "Orion")
requestVega = SelectItem("vega", "Vega")
requestLyra = SelectItem("lyra", "Lyra")
requestRoleField = FormControl("Role", requestRoleSelect)
requestRoleSelect = Select("role", [roleEng, roleDesign, roleQA], "Select role")
roleEng = SelectItem("engineer", "Engineer")
roleDesign = SelectItem("design", "Designer")
roleQA = SelectItem("qa", "QA Lead")
requestCountField = FormControl("Number of people", requestCountInput)
requestCountInput = Input("count", "e.g., 2", "number")
requestButtons = Buttons([requestSubmitBtn])
requestSubmitBtn = Button("Submit request", null, "primary")
archiveDialog = DialogBlock("Archive finished project", "Archive a project", "Choose the completed project to move out of the active portfolio.", [archiveForm])
archiveForm = Form("archive-form", archiveButtons, [archiveProjectField])
archiveProjectField = FormControl("Project", archiveProjectSelect)
archiveProjectSelect = Select("archive_project", [archHelios, archPolaris], "Select project")
archHelios = SelectItem("helios", "Helios — completed Sep 30")
archPolaris = SelectItem("polaris", "Polaris — completed Sep 15")
archiveButtons = Buttons([archiveSubmitBtn])
archiveSubmitBtn = Button("Archive project", null, "primary")
projectsSection = Card([projectsHeading, projectsTable], "sunk")
projectsHeading = Heading("Active projects", "h2")
projectsTable = Table([projectNameCol, ownerCol, deadlineCol, healthCol])
projectNameCol = Col("Project", ["Project Atlas", "Project Nova", "Project Orion", "Project Vega", "Project Lyra"])
ownerCol = Col("Owner", ["Maya Chen", "Raj Patel", "Lena Fischer", "Tom Okafor", "Sofia Rossi"])
deadlineCol = Col("Deadline", ["Oct 24", "Nov 08", "Oct 18", "Nov 30", "Nov 15"])
healthCol = Col("Health", ["Green", "Amber", "Green", "Red", "Amber"])
milestonesSection = Card([milestonesTitle, overdueHeading, overdueList, upcomingHeading, upcomingList], "sunk")
milestonesTitle = Heading("Milestones", "h2")
overdueHeading = Heading("Overdue milestones", "h3")
overdueList = ListBlock([overdue1, overdue2, overdue3], "number")
overdue1 = ListItem("Atlas — Design sign-off", "Due Oct 02 · 12 days overdue")
overdue2 = ListItem("Nova — API integration", "Due Oct 05 · 9 days overdue")
overdue3 = ListItem("Vega — UAT sign-off", "Due Oct 08 · 6 days overdue")
upcomingHeading = Heading("Deadlines in next 2 weeks", "h3")
upcomingList = ListBlock([upcoming1, upcoming2, upcoming3, upcoming4], "number")
upcoming1 = ListItem("Orion — Security review", "Due Oct 21 · Security team")
upcoming2 = ListItem("Atlas — Beta release", "Due Oct 20 · Engineering")
upcoming3 = ListItem("Lyra — Marketing launch", "Due Oct 24 · Marketing")
upcoming4 = ListItem("Nova — Sprint review", "Due Oct 26 · Product")
budgetSection = Card([budgetHeading, budgetChart], "sunk")
budgetHeading = Heading("Budget spent vs allocation", "h2")
budgetChart = BarChart(["Atlas", "Nova", "Orion", "Vega", "Lyra"], [allocSeries, spentSeries], "grouped", "Project", "USD")
allocSeries = Series("Allocated", [320000, 210000, 280000, 390000, 175000])
spentSeries = Series("Spent", [278000, 198000, 142000, 361000, 98000])
teamSection = Card([teamHeading, utilizationEng, utilizationDes, utilizationProd, utilizationQA], "sunk")
teamHeading = Heading("Team utilization", "h2")
utilizationEng = Progress(87, "Engineering")
utilizationDes = Progress(72, "Design")
utilizationProd = Progress(81, "Product")
utilizationQA = Progress(64, "QA")
paceSection = Card([paceHeading, paceChart], "sunk")
paceHeading = Heading("Delivery pace — last 6 sprints", "h2")
paceChart = LineChart(["S1", "S2", "S3", "S4", "S5", "S6"], [committedSeries, deliveredSeries], "linear", "Sprint", "Points")
committedSeries = Series("Committed", [120, 118, 125, 130, 128, 132])
deliveredSeries = Series("Delivered", [112, 118, 108, 124, 126, 119])
risksSection = Card([risksHeading, risksTable], "sunk")
risksHeading = Heading("Risks", "h2")
risksTable = Table([riskNameCol, riskSeverityCol, riskOwnerCol, riskStatusCol])
riskNameCol = Col("Risk", ["Vendor API delay", "Key person departure", "Scope creep on Orion", "Cloud cost overrun"])
riskSeverityCol = Col("Severity", ["High", "High", "Medium", "Low"])
riskOwnerCol = Col("Owner", ["Maya Chen", "Lena Fischer", "Sofia Rossi", "Tom Okafor"])
riskStatusCol = Col("Status", ["Monitoring", "Mitigating", "Accepted", "Watching"])
blockedSection = Card([blockedHeading, blockedTable], "sunk")
blockedHeading = Heading("Blocked work items", "h2")
blockedTable = Table([blockedItemCol, blockedProjectCol, blockedReasonCol, blockedSinceCol])
blockedItemCol = Col("Item", ["Payment webhook", "Design system audit", "Data warehouse ETL", "App store submission"])
blockedProjectCol = Col("Project", ["Atlas", "Lyra", "Vega", "Orion"])
blockedReasonCol = Col("Blocked by", ["Vendor API", "Awaiting legal", "Infra access", "Compliance review"])
blockedSinceCol = Col("Since", ["3 days", "5 days", "1 day", "7 days"])