The HTML behind it
Report card template HTML
This is the whole template. The fields in double braces fill from your data on every render. Copy it into your account and change anything you like.

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<style data-thirds="base">
html,body{margin:0}
[data-thirds="canvas"]{position:relative;overflow:hidden}
[data-thirds="canvas"] [data-thirds]{position:absolute;box-sizing:border-box;margin:0}
[data-thirds="text"]{white-space:pre-wrap;overflow-wrap:anywhere}
[data-thirds="image"]{display:block}
[data-thirds="table"]{border-collapse:collapse}
[data-thirds="table"] th,[data-thirds="table"] td{padding:4px 6px;text-align:left;vertical-align:top;border-bottom:1px solid var(--rule,#dde1e8)}
[data-thirds="table"] th{background:var(--head,transparent)}
[data-thirds="list"]{display:flex;flex-direction:column}
[data-thirds="canvas"] [data-thirds="item"]{position:relative;flex:none}
</style>
<style>@page { size: 794px 1123px; margin: 0; } body { font-family: Arial, Helvetica, sans-serif; } [data-thirds="table"] th,[data-thirds="table"] td { padding: 9px 10px; }</style>
</head>
<body>
<div data-thirds="canvas" data-thirds-kind="pdf" data-thirds-page="p1" data-thirds-name="Page 1" style="width:794px;height:1123px;background:#ffffff">
<div data-thirds="box" data-thirds-id="header-band" data-thirds-name="Header band" style="left:0px;top:0px;width:794px;height:190px;background:#0f172a"></div>
<div data-thirds="text" data-thirds-id="organisation" data-thirds-name="Organisation" style="left:56px;top:50px;width:500px;height:28px;font-size:13px;color:#93c5fd;font-weight:700;line-height:1.2;letter-spacing:1.3px;text-transform:uppercase;">{{ organisation }}</div>
<div data-thirds="text" data-thirds-id="title" data-thirds-name="Title" style="left:56px;top:84px;width:600px;height:50px;font-size:36px;color:#ffffff;font-weight:800;line-height:1.2;letter-spacing:-1px;" data-thirds-fit="shrink" data-thirds-min-font="20" data-thirds-max-font="36">{{ title }}</div>
<div data-thirds="text" data-thirds-id="period" data-thirds-name="Period" style="left:56px;top:140px;width:500px;height:26px;font-size:15px;color:#cbd5e1;font-weight:400;line-height:1.2;">{{ period }}</div>
<div data-thirds="box" data-thirds-id="score-box" data-thirds-name="Score box" style="left:600px;top:44px;width:138px;height:110px;background:#2563eb;border-radius:12px"></div>
<div data-thirds="text" data-thirds-id="score-label" data-thirds-name="Score label" style="left:600px;top:58px;width:138px;height:20px;font-size:11px;color:#dbeafe;font-weight:700;line-height:1.2;letter-spacing:1.3px;text-transform:uppercase;text-align:center;">{{ score_label }}</div>
<div data-thirds="text" data-thirds-id="score" data-thirds-name="Score" style="left:600px;top:80px;width:138px;height:64px;font-size:48px;color:#ffffff;font-weight:800;line-height:1.1;text-align:center;">{{ score }}</div>
<div data-thirds="text" data-thirds-id="metrics-label" data-thirds-name="Metrics label" style="left:56px;top:230px;width:300px;height:20px;font-size:11px;color:#0f172a;font-weight:700;line-height:1.2;letter-spacing:1.3px;text-transform:uppercase;">Metrics against target</div>
<table data-thirds="table" data-thirds-id="metrics" data-thirds-name="Metrics" style="left:56px;top:260px;width:682px;font-size:13px;--head:#e0f2fe;"><thead><tr><th>Metric</th><th style="text-align:right">Target</th><th style="text-align:right">Result</th><th>Status</th></tr></thead><tbody>{% for metric in metrics %}<tr><td>{{ metric.name }}</td><td style="text-align:right">{{ metric.target }}</td><td style="text-align:right"><strong>{{ metric.result }}</strong></td><td>{{ metric.status }}</td></tr>{% endfor %}</tbody></table>
<div data-thirds="text" data-thirds-id="summary-label" data-thirds-name="Summary label" style="left:56px;top:720px;width:300px;height:20px;font-size:11px;color:#0f172a;font-weight:700;line-height:1.2;letter-spacing:1.3px;text-transform:uppercase;">Summary</div>
<div data-thirds="text" data-thirds-id="summary" data-thirds-name="Summary" style="left:56px;top:746px;width:682px;height:110px;font-size:14px;color:#1e293b;font-weight:400;line-height:1.55;">{{ summary }}</div>
<div data-thirds="box" data-thirds-id="next-box" data-thirds-name="Next steps box" style="left:56px;top:890px;width:682px;height:110px;background:#f1f5f9;border-left:6px solid #2563eb"></div>
<div data-thirds="text" data-thirds-id="next-label" data-thirds-name="Next steps label" style="left:80px;top:906px;width:300px;height:20px;font-size:11px;color:#2563eb;font-weight:700;line-height:1.2;letter-spacing:1.3px;text-transform:uppercase;">Next steps</div>
<div data-thirds="text" data-thirds-id="next-steps" data-thirds-name="Next steps" style="left:80px;top:930px;width:640px;height:60px;font-size:13px;color:#1e293b;font-weight:400;line-height:1.5;">{{ next_steps }}</div>
<div data-thirds="text" data-thirds-id="prepared-by" data-thirds-name="Prepared by" style="left:56px;top:1064px;width:682px;height:20px;font-size:11px;color:#64748b;font-weight:400;line-height:1.2;">{{ prepared_by }}</div>
</div>
</body>
</html>
Sample data
The values that made the picture.
Change any of them, and the same HTML makes a new file.
{
"organisation": "Orbit Analytics",
"title": "Quarterly scorecard",
"period": "July to September 2026",
"score_label": "Overall",
"score": "A-",
"metrics": [
{
"name": "New customers",
"target": "120",
"result": "134",
"status": "Above target"
},
{
"name": "Monthly churn",
"target": "2.0%",
"result": "1.7%",
"status": "Above target"
},
{
"name": "Support reply time",
"target": "1 hour",
"result": "52 min",
"status": "On target"
},
{
"name": "Uptime",
"target": "99.9%",
"result": "99.95%",
"status": "On target"
},
{
"name": "Net revenue",
"target": "$410k",
"result": "$388k",
"status": "Below target"
},
{
"name": "Feature releases",
"target": "6",
"result": "5",
"status": "Below target"
}
],
"summary": "Growth and service both beat their targets this quarter. Revenue came in five percent below plan because two large renewals moved into October. Release pace slowed while the team fixed the reporting engine.",
"next_steps": "Close the two delayed renewals in October. Ship the two held releases by mid November. Keep support staffing at current levels.",
"prepared_by": "Prepared by the Orbit Analytics operations team on 6 September 2026"
}