Blocks
A dashboard is a list of sections; a section is a list of blocks. These are all 37 of them, rendered live from the JSON shown beside each one. The machine-readable contract is at /api/schema. The older names `text` and `image` are still accepted and parse as `prose` and `figure`.
Layout
A section is a grid; a block is a rectangle on it. These eleven fields are the whole layout system — there is no width class and no wide variant of any block.
on a section
| columns | Track width for this section. Default 12, max 24. Default spans scale with it. |
| gap | none · xs · sm · md (default) · lg · xl — the space between cells. |
| rowHeight | Row height in px. Without it every row sizes to its tallest cell and `rowSpan` does nothing. |
| align | start · center · end · stretch (default) — how blocks fill the height of their row. |
| dense | Let later blocks backfill earlier gaps. Off by default: it reorders the visual sequence away from the document order. |
| hidden | In the document, not on the page. |
| only | 'screen' or 'print'. |
on a block
| span | Columns wide. Clamped to the section track. |
| rowSpan | Rows tall. Needs the section to set `rowHeight`. |
| colStart | The column this block starts in, 1-based. This is what leaves a deliberate gap, or lines a block up with one in another section. |
| rowStart | The row this block starts in, 1-based. Rarely needed. |
| align | Overrides the section's alignment for one block. |
| minHeight | Explicit pixels, when a row count is the wrong unit. |
| hidden / only | Same two flags, per block. |
Below 768px the track collapses to two columns and every span is quantised to half or full, so a strip of KPI tiles stays a strip on a phone. Explicit starts are dropped there — a gap designed for twelve columns is not a gap in two. Inside the print document the section’s own track always applies, whatever the window is doing.
What survives an html block
Markup is sanitised when the spec is parsed, not when it is drawn, so what is stored is already safe and everything outside these lists is gone before it reaches the page. Removal is silent: a POST that failed on a formatting detail would be worse than one that renders slightly plainer than intended.
pbrhrspandivstrongbemiusdelinssmallsubsupmarkabbrciteqblockquotetimeaddresscodeprekbdsampvaraulollidldtddtablecaptioncolgroupcoltheadtbodytfoottrthtdh2h3h4h5h6sectionarticleheaderfooterfigurefigcaptionhreftitleclasslangdircolspanrowspanscopeheadersspandatetimecitestartreversedvaluehb-gridhb-grid-2hb-grid-3hb-grid-4hb-rowhb-stackhb-cardhb-panelhb-dividerhb-badgehb-keyhb-valuehb-mutedhb-stronghb-monohb-righthb-centerhb-filleyebrowmeasuremeasure-widenumhangingtone-neutraltone-positivetone-warningtone-dangertone-infotone-accentEverything else is dropped, including script, every on* handler, inline style, id, img, iframe, and any href that is not http, https or mailto. A class outside the list is removed while the element around it stays, so a fragment degrades to plain rather than disappearing. For a third-party PAGE use embed, which is a sandboxed iframe.
stat
One KPI. `rail` draws a progress bar, `delta` shows movement, `spark` the history.
{
"type": "stat",
"label": "Open commitments",
"value": 4,
"sublabel": "2 past 7 days",
"tone": "warning",
"rail": 0.4,
"delta": {
"value": "+2",
"direction": "up",
"tone": "danger"
},
"spark": [
1,
1,
2,
2,
3,
2,
4
]
}stats
A row of KPIs — the strip most reports open with.
{
"type": "stats",
"title": "Customers monitored",
"items": [
{
"label": "Monitored",
"value": 12
},
{
"label": "Red",
"value": 1,
"tone": "danger",
"delta": {
"value": "+1",
"direction": "up",
"tone": "danger"
}
},
{
"label": "Yellow",
"value": 3,
"tone": "warning"
},
{
"label": "Green",
"value": 8,
"tone": "positive",
"spark": [
6,
7,
7,
8,
8
]
}
]
}bignumber
One number, sized to be read across a room.
Carried by missing and differing invoices — 84.9% of the range total.
{
"type": "bignumber",
"value": "261,009.25",
"label": "Value at risk",
"context": "Carried by missing and differing invoices — 84.9% of the range total.",
"tone": "danger"
}gauge
A value against NAMED thresholds. Without `bands` this is a `stat` with a rail and more pixels — use the stat.
{
"type": "gauge",
"title": "Response SLA",
"value": 72,
"min": 0,
"max": 100,
"unit": "%",
"label": "Threads replied to inside 24 hours",
"tone": "warning",
"bands": [
{
"upTo": 60,
"tone": "danger"
},
{
"upTo": 85,
"tone": "warning"
},
{
"upTo": 100,
"tone": "positive"
}
]
}segments
Parts of a whole in one bar, with a legend.
{
"type": "segments",
"title": "Value at risk",
"total": "307,499.25",
"totalLabel": "Total invoice value in range",
"segments": [
{
"label": "Missing in eSpeed",
"value": 79393.75,
"tone": "danger"
},
{
"label": "Differing",
"value": 181615.5,
"tone": "warning"
},
{
"label": "Incomplete",
"value": 19980,
"tone": "info"
},
{
"label": "Matching",
"value": 26510,
"tone": "positive"
}
]
}progressList
Labelled bars. Scales to the largest value unless you set `max`.
{
"type": "progressList",
"title": "Discrepancies by field",
"items": [
{
"label": "payment.date",
"value": 5,
"tone": "warning"
},
{
"label": "payment.amount",
"value": 1,
"tone": "danger"
},
{
"label": "vendor.name",
"value": 3
}
]
}scorecard
Weighted criteria and the grade they add up to. Says why, not just what.
Delivery is holding; the drag is entirely on responsiveness and billing hygiene.
- Build delivery vs plan×388/100
- Response time to escalations×254/100
Median 31h against a 24h commitment.
- Invoice accuracy×262/100
- Commitment closure×171/100
{
"type": "scorecard",
"title": "Account health — Ripple Fiber",
"scale": 100,
"overall": {
"grade": "C+",
"label": "Watch",
"detail": "Delivery is holding; the drag is entirely on responsiveness and billing hygiene.",
"tone": "warning"
},
"criteria": [
{
"label": "Build delivery vs plan",
"score": 88,
"weight": 3,
"tone": "positive"
},
{
"label": "Response time to escalations",
"score": 54,
"weight": 2,
"tone": "danger",
"note": "Median 31h against a 24h commitment."
},
{
"label": "Invoice accuracy",
"score": 62,
"weight": 2,
"tone": "warning"
},
{
"label": "Commitment closure",
"score": 71,
"weight": 1
}
]
}Data and records
Charts, matrices, tables, people.
chart
bar · barStacked · barHorizontal · line · area · pie · donut · scatter · radar · funnel — or a raw ECharts `option`.
{
"type": "chart",
"title": "Escalation keywords by week",
"kind": "area",
"categories": [
"W28",
"W29",
"W30",
"W31",
"W32"
],
"series": [
{
"name": "delay",
"data": [
1,
2,
2,
4,
6
],
"tone": "danger"
},
{
"name": "invoice",
"data": [
0,
1,
3,
2,
3
],
"tone": "warning"
}
],
"unit": "",
"height": 240
}heatmap
A numeric matrix. `values` is row-major: `values[y][x]`.
{
"type": "heatmap",
"title": "Contact volume",
"xLabels": [
"Mon",
"Tue",
"Wed",
"Thu",
"Fri"
],
"yLabels": [
"Email",
"Call",
"Meeting"
],
"values": [
[
12,
8,
15,
6,
9
],
[
2,
4,
1,
3,
5
],
[
0,
1,
2,
0,
1
]
],
"height": 220
}statusMatrix
Entities down, checks across, a status in every cell. Categorical, where heatmap is numeric.
{
"type": "statusMatrix",
"title": "Build readiness by route",
"columns": [
{
"label": "GIS survey",
"short": "GIS"
},
{
"label": "Wayleave",
"short": "WAY"
},
{
"label": "Permit",
"short": "PRM"
},
{
"label": "Splice plan",
"short": "SPL"
},
{
"label": "As-built",
"short": "ABT"
}
],
"rows": [
{
"label": "Route 14 — Bridgend spine",
"note": "4.2 km, 288f",
"cells": [
{
"tone": "positive",
"label": "✓"
},
{
"tone": "positive",
"label": "✓"
},
{
"tone": "positive",
"label": "✓"
},
{
"tone": "warning",
"label": "!",
"note": "Awaiting closure schedule from Uros"
},
null
]
},
{
"label": "Route 17 — Llanelli lateral",
"cells": [
{
"tone": "positive",
"label": "✓"
},
{
"tone": "warning",
"label": "!"
},
{
"tone": "danger",
"label": "✗",
"note": "Highways rejected 06 Aug — resubmission not filed"
},
null,
null
]
},
{
"label": "Route 21 — Docks feeder",
"cells": [
{
"tone": "positive",
"label": "✓"
},
{
"tone": "positive",
"label": "✓"
},
{
"tone": "positive",
"label": "✓"
},
{
"tone": "positive",
"label": "✓"
},
{
"tone": "positive",
"label": "✓"
}
]
}
],
"legend": [
{
"label": "Complete",
"tone": "positive"
},
{
"label": "In progress",
"tone": "warning"
},
{
"label": "Blocked",
"tone": "danger"
}
]
}comparison
Two states of the same rows, with the movement between them written down.
Route 14 — Bridgend spine, closed 11 Aug.
{
"type": "comparison",
"title": "Quoted against as-built",
"caption": "Route 14 — Bridgend spine, closed 11 Aug.",
"numeric": true,
"left": {
"label": "Quoted",
"caption": "Bid pack, 02 Jun"
},
"right": {
"label": "As-built",
"caption": "Survey, 11 Aug"
},
"rows": [
{
"label": "Duct (m)",
"left": 4200,
"right": 4470,
"delta": "+270",
"tone": "warning",
"note": "Diversion around the culvert at ch. 2100."
},
{
"label": "Chambers",
"left": 18,
"right": 18,
"delta": "0",
"tone": "positive"
},
{
"label": "Splice closures",
"left": 9,
"right": 11,
"delta": "+2",
"tone": "warning"
},
{
"label": "Days on site",
"left": 22,
"right": 19,
"delta": "−3",
"tone": "positive"
}
]
}table
Cells can be plain, or carry a tone, a pill, a sparkline, a link or an inline bar.
{
"type": "table",
"title": "Open commitments",
"searchable": true,
"columns": [
{
"key": "commitment",
"label": "Commitment"
},
{
"key": "owner",
"label": "Owner"
},
{
"key": "due",
"label": "Flagged / due"
},
{
"key": "status",
"label": "Status / age"
}
],
"rows": [
{
"commitment": "Site walk confirmation to Ripple ops",
"owner": "Lindon",
"due": "2026-08-04",
"status": {
"value": "10 days open",
"tone": "warning",
"badge": true
}
},
{
"commitment": "Invoice dispute resolution date",
"owner": "Unassigned",
"due": "2026-07-29",
"status": {
"value": "16 days open",
"tone": "danger",
"badge": true
}
},
{
"commitment": "Fibre splice schedule shared",
"owner": "Uros",
"due": "2026-08-08",
"status": {
"value": "CLOSED — sent 11 Aug",
"tone": "positive",
"badge": true
}
}
]
}list
A flat list, optionally numbered, each item with its own tone.
- 1This weekConfirm the site walk is scheduled
Lindon to confirm with the Ripple account owner this week.
- 2TodayGive the invoice dispute a resolution date
Unowned for 16 days — the longest-running open item.
{
"type": "list",
"title": "Recommended actions",
"ordered": true,
"items": [
{
"title": "Confirm the site walk is scheduled",
"detail": "Lindon to confirm with the Ripple account owner this week.",
"meta": "This week",
"tone": "warning"
},
{
"title": "Give the invoice dispute a resolution date",
"detail": "Unowned for 16 days — the longest-running open item.",
"meta": "Today",
"tone": "danger"
}
]
}timeline
Dated events on a rail. `when` is free text, not a date type.
- 29 JulWeekly call
Tone neutral. Invoice dispute raised.
- 04 AugSite walk committed
No confirmation since.
- 11 AugSplice schedule sent
{
"type": "timeline",
"title": "Account timeline",
"events": [
{
"when": "29 Jul",
"title": "Weekly call",
"detail": "Tone neutral. Invoice dispute raised.",
"tone": "neutral"
},
{
"when": "04 Aug",
"title": "Site walk committed",
"detail": "No confirmation since.",
"tone": "warning"
},
{
"when": "11 Aug",
"title": "Splice schedule sent",
"tone": "positive"
}
]
}fields
Labelled values — the key/value and definition-list block. `inline` gives the spec-sheet form.
- Direction
- Declining — second consecutive week of slower replies.
- Most repeated concern
- "delay" appeared 6 times, up from 1 last week.
- Improving
- Splice scheduling is now shared proactively.
{
"type": "fields",
"title": "Trend analysis",
"fields": [
{
"label": "Direction",
"value": "Declining — second consecutive week of slower replies.",
"tone": "warning"
},
{
"label": "Most repeated concern",
"value": "\"delay\" appeared 6 times, up from 1 last week."
},
{
"label": "Improving",
"value": "Splice scheduling is now shared proactively.",
"tone": "positive"
}
]
}pills
A row of tags — statuses, applied filters, counts. `solid` makes it a status strip.
{
"type": "pills",
"title": "Sources",
"pills": [
{
"label": "SharePoint emails",
"value": 287,
"tone": "info"
},
{
"label": "Call transcripts",
"value": 23,
"tone": "info"
},
{
"label": "eSpeed",
"value": "not connected",
"tone": "neutral"
}
]
}people
Named people — owners, contacts, the escalation path.
- Lindon MarshprimaryAccount lead, Essentia
- Uros PetrovicBuild manager
Owns the splice schedule and closure bookings.
- Dana WhitfieldescalationRipple Fiber — ops director
- Unassigned16 daysBilling dispute owner
{
"type": "people",
"title": "Escalation path",
"columns": 2,
"people": [
{
"name": "Lindon Marsh",
"role": "Account lead, Essentia",
"status": "primary",
"tone": "positive"
},
{
"name": "Uros Petrovic",
"role": "Build manager",
"detail": "Owns the splice schedule and closure bookings."
},
{
"name": "Dana Whitfield",
"role": "Ripple Fiber — ops director",
"status": "escalation",
"tone": "warning"
},
{
"name": "Unassigned",
"role": "Billing dispute owner",
"status": "16 days",
"tone": "danger"
}
]
}Editorial
The blocks that carry an argument rather than a number.
title
The opener for a part of a report: eyebrow, headline, standfirst, byline.
Ripple Fiber is delivering on the build and slipping on everything around it
Construction is ahead of plan on all three live routes. The risk is administrative: replies are slower every week, and a billing dispute has now gone sixteen days without an owner.
{
"type": "title",
"eyebrow": "Account review · Week 33",
"headline": "Ripple Fiber is delivering on the build and slipping on everything around it",
"standfirst": "Construction is ahead of plan on all three live routes. The risk is administrative: replies are slower every week, and a billing dispute has now gone sixteen days without an owner.",
"byline": "Sentiment agent",
"dateline": "14 August 2026",
"rule": true
}heading
A heading inside a section. Levels 2–4, for structure a section boundary would over-state.
Where the time is going
{
"type": "heading",
"eyebrow": "Part two",
"text": "Where the time is going",
"level": 2,
"rule": true
}paragraph
One paragraph of plain text, capped at a readable measure. `lead` sets the opener.
Three of the four escalations this fortnight started as a question about an invoice and became a question about the build. That is the pattern worth breaking: billing friction is being read by the customer as delivery risk, and the delivery numbers do not support that reading.
{
"type": "paragraph",
"lead": true,
"text": "Three of the four escalations this fortnight started as a question about an invoice and became a question about the build. That is the pattern worth breaking: billing friction is being read by the customer as delivery risk, and the delivery numbers do not support that reading."
}prose
Markdown with GFM — tables, task lists, fenced code. `text` is accepted as the older name for this block.
Sentiment is scored from three sources, weighted by how directly the customer speaks in them:
| Source | Volume | Weight |
|---|---|---|
| Call transcripts | 23 | 0.5 |
| SharePoint email | 287 | 0.35 |
| Ticket comments | 61 | 0.15 |
A message counts as an escalation signal when it contains a delay, cost or quality term within two
sentences of a named route. Folder-level webUrls resolve via the app-only Graph token; clicking one
still enforces your own SharePoint permissions.
- Transcript ingest running daily
- Email ingest running daily
- eSpeed invoice feed — not connected
{
"type": "prose",
"title": "Method",
"markdown": "Sentiment is scored from **three sources**, weighted by how directly the customer speaks in them:\n\n| Source | Volume | Weight |\n| --- | ---: | ---: |\n| Call transcripts | 23 | 0.5 |\n| SharePoint email | 287 | 0.35 |\n| Ticket comments | 61 | 0.15 |\n\nA message counts as an *escalation signal* when it contains a delay, cost or quality term within two\nsentences of a named route. Folder-level `webUrl`s resolve via the app-only Graph token; clicking one\nstill enforces your own SharePoint permissions.\n\n- [x] Transcript ingest running daily\n- [x] Email ingest running daily\n- [ ] eSpeed invoice feed — not connected"
}callout
A note set apart from the copy. One knob — `tone` — and the icon follows from it.
{
"type": "callout",
"tone": "warning",
"title": "Known gap in this report",
"markdown": "eSpeed is **not connected**, so invoice figures come from the customer-supplied statement rather than our own ledger. Treat the value-at-risk total as a floor, not a measurement."
}quote
The sentence from the transcript that made the case.
The crews are fine — we can see the routes going in. What we cannot see is who is answering the billing question, and it has been two weeks.
{
"type": "quote",
"quote": "The crews are fine — we can see the routes going in. What we cannot see is who is answering the billing question, and it has been two weeks.",
"attribution": "Dana Whitfield",
"role": "Operations director, Ripple Fiber",
"source": "Call transcript, 11 August 2026",
"tone": "warning"
}code
Code, a config fragment, a payload. Monospaced and unhighlighted; `highlight` shades lines.
{
"type": "code",
"title": "Escalation rule",
"language": "json",
"filename": "rules/escalation.json",
"lineNumbers": true,
"highlight": [
4
],
"code": "{\n \"terms\": [\"delay\", \"slipped\", \"chase\", \"dispute\"],\n \"window_sentences\": 2,\n \"requires_route_reference\": true,\n \"min_confidence\": 0.6\n}"
}steps
A numbered procedure. `status` drives both the marker and the colour, so they cannot disagree.
- Pull the three disputed invoices from the statementDone 05 Aug
- Reconcile against the as-built quantitiesDone 08 Aug
- Assign an owner and a resolution dateOverdue
This is the step everything else is waiting on — sixteen days unowned.
- 4Issue a credit note or a written rejection
{
"type": "steps",
"title": "Resolving the billing dispute",
"steps": [
{
"title": "Pull the three disputed invoices from the statement",
"status": "done",
"meta": "Done 05 Aug"
},
{
"title": "Reconcile against the as-built quantities",
"status": "done",
"meta": "Done 08 Aug"
},
{
"title": "Assign an owner and a resolution date",
"detail": "This is the step everything else is waiting on — sixteen days unowned.",
"status": "blocked",
"meta": "Overdue"
},
{
"title": "Issue a credit note or a written rejection",
"status": "todo"
}
]
}checklist
Things to be ticked off, with the count at the top. Unordered, where `steps` is ordered.
- As-built GIS export lodgedUros
- Splice matrix signed offUros
- Chamber photos uploaded
- Wayleave copies attached
Two originals still with the landowner’s solicitor.
- Reinstatement sign-offDue 20 Aug
- Traffic management debrief
Not required — footway works only.
{
"type": "checklist",
"title": "Pre-handover pack — Route 14",
"items": [
{
"label": "As-built GIS export lodged",
"state": "done",
"meta": "Uros"
},
{
"label": "Splice matrix signed off",
"state": "done",
"meta": "Uros"
},
{
"label": "Chamber photos uploaded",
"state": "done"
},
{
"label": "Wayleave copies attached",
"state": "blocked",
"detail": "Two originals still with the landowner’s solicitor."
},
{
"label": "Reinstatement sign-off",
"state": "todo",
"meta": "Due 20 Aug"
},
{
"label": "Traffic management debrief",
"state": "na",
"detail": "Not required — footway works only."
}
]
}figure
An image with a caption and a credit. `image` is accepted as the older name.

Figure 1Route 14 approaching the culvert at chainage 2100, where the as-built diverges 270 m from the quoted alignment.
Essentia GIS · survey flight 11 Aug 2026
{
"type": "figure",
"url": "https://essentia-inc.com/wp-content/uploads/2024/05/Broadband-v2-768x432.png",
"alt": "Aerial view of a fibre route running alongside a carriageway",
"label": "Figure 1",
"figcaption": "Route 14 approaching the culvert at chainage 2100, where the as-built diverges 270 m from the quoted alignment.",
"credit": "Essentia GIS · survey flight 11 Aug 2026",
"aspect": "16/9",
"fit": "cover"
}links
Outbound references — sources, tickets, folders. The host is always shown.
{
"type": "links",
"title": "Sources and working files",
"links": [
{
"label": "Ripple Fiber — correspondence folder",
"url": "https://example.com/sharepoint/ripple-fiber/correspondence",
"source": "SharePoint",
"note": "Everything ingested for this report, in date order."
},
{
"label": "Route 14 as-built package",
"url": "https://example.com/gis/route-14/as-built",
"source": "GIS",
"tone": "positive"
},
{
"label": "Billing dispute — RF-2261",
"url": "https://example.com/tickets/RF-2261",
"source": "Tickets",
"note": "Unassigned since 29 July.",
"tone": "danger"
}
]
}footnotes
The small print at the end — method notes, caveats, exclusions.
- 1Response time is measured from the customer’s message to the first substantive reply, excluding auto-acknowledgements.
- 2Value at risk excludes the two credit notes issued on 06 August, which are settled but not yet reflected in the customer statement.
- †Route 21 figures are provisional pending the reinstatement inspection.
{
"type": "footnotes",
"title": "Notes",
"notes": [
{
"text": "Response time is measured from the customer’s message to the first substantive reply, excluding auto-acknowledgements."
},
{
"text": "Value at risk excludes the two credit notes issued on 06 August, which are settled but not yet reflected in the customer statement."
},
{
"label": "†",
"text": "Route 21 figures are provisional pending the reinstatement inspection."
}
]
}Structure
Layout, containers and punctuation.
html
Raw markup, sanitised at parse time and restricted to design-system classes. What is stored is already safe; what is not on the allowlists is dropped silently.
Quoted verbatim from the landowner agreement — the nesting is theirs.
- Grantor
- Bridgend Estates Ltd
- Term
- 25 years from first splice, break at 10
Access is permitted between 07:00 and 18:00 on weekdays, save that no works shall be carried out during lambing (01 Feb – 15 Apr) without fourteen days’ written notice.
Lambing window Route 14 chainage 1800–2100 only
{
"type": "html",
"title": "Wayleave clause 4.2, as written",
"caption": "Quoted verbatim from the landowner agreement — the nesting is theirs.",
"html": "<dl class=\"hb-grid hb-grid-2\">\n <dt class=\"hb-key\">Grantor</dt>\n <dd>Bridgend Estates Ltd</dd>\n <dt class=\"hb-key\">Term</dt>\n <dd>25 years from first splice, break at 10</dd>\n</dl>\n<blockquote>Access is permitted between 07:00 and 18:00 on weekdays, save that\nno works shall be carried out during lambing (01 Feb – 15 Apr) without\n<strong>fourteen days’</strong> written notice.</blockquote>\n<p class=\"hb-row\"><span class=\"hb-badge tone-warning\">Lambing window</span>\n<span class=\"hb-muted\">Route 14 chainage 1800–2100 only</span></p>"
}embed
A third-party page in a sandboxed iframe (no same-origin access).
{
"type": "embed",
"title": "Embedded page",
"url": "https://example.com/",
"height": 200
}divider
A labelled rule, or `dots` for a soft break between passages.
{
"type": "divider",
"label": "Detail"
}spacer
Measured empty space, so air between parts never needs an empty card.
{
"type": "spacer",
"size": "lg"
}columns
A split inside one block, for two things that belong together and must page-break together.
Why the diversion happened
The quoted alignment crossed a culvert that the desktop survey recorded as disused. It is not. The 270 m diversion was agreed on site on 22 July and is the single largest variance on the route.
- Added duct
- 270 m
- Added closures
- 2
- Days impact
- −3
{
"type": "columns",
"gap": "lg",
"columns": [
{
"span": 7,
"blocks": [
{
"type": "heading",
"text": "Why the diversion happened",
"level": 3
},
{
"type": "paragraph",
"text": "The quoted alignment crossed a culvert that the desktop survey recorded as disused. It is not. The 270 m diversion was agreed on site on 22 July and is the single largest variance on the route."
}
]
},
{
"span": 5,
"blocks": [
{
"type": "fields",
"title": "Variance",
"inline": true,
"fields": [
{
"label": "Added duct",
"value": "270 m",
"tone": "warning"
},
{
"label": "Added closures",
"value": "2"
},
{
"label": "Days impact",
"value": "−3",
"tone": "positive"
}
]
}
]
}
]
}tabs
Peer panels the reader chooses between. Every panel prints, because a PDF has no tabs.
- 22 JulCulvert diversion agreed on site
- 11 AugAs-built lodged, handover pack opened
Complete and inspected. No open items.
{
"type": "tabs",
"title": "Escalations by route",
"tabs": [
{
"label": "Route 14",
"blocks": [
{
"type": "list",
"items": [
{
"title": "Culvert diversion agreed on site",
"meta": "22 Jul",
"tone": "warning"
},
{
"title": "As-built lodged, handover pack opened",
"meta": "11 Aug",
"tone": "positive"
}
]
}
]
},
{
"label": "Route 17",
"blocks": [
{
"type": "callout",
"tone": "danger",
"title": "Permit rejected",
"markdown": "Highways rejected the section 50 on **6 August**. No resubmission has been filed."
}
]
},
{
"label": "Route 21",
"blocks": [
{
"type": "paragraph",
"text": "Complete and inspected. No open items."
}
]
}
]
}accordion
Detail worth having and not worth reading first — the appendix.
Full escalation log11 signals across the fortnight, newest first.
- 11 AugBilling question repeated on the weekly call
- 06 AugSection 50 rejected — Route 17
- 29 JulInvoice dispute raised
Scoring weightsHow the grade on page one is arrived at.
- Delivery
- 0.4
- Responsiveness
- 0.35
- Billing hygiene
- 0.25
{
"type": "accordion",
"title": "Appendix",
"items": [
{
"label": "Full escalation log",
"summary": "11 signals across the fortnight, newest first.",
"blocks": [
{
"type": "timeline",
"events": [
{
"when": "11 Aug",
"title": "Billing question repeated on the weekly call",
"tone": "danger"
},
{
"when": "06 Aug",
"title": "Section 50 rejected — Route 17",
"tone": "danger"
},
{
"when": "29 Jul",
"title": "Invoice dispute raised",
"tone": "warning"
}
]
}
]
},
{
"label": "Scoring weights",
"summary": "How the grade on page one is arrived at.",
"tone": "neutral",
"blocks": [
{
"type": "fields",
"inline": true,
"fields": [
{
"label": "Delivery",
"value": "0.4"
},
{
"label": "Responsiveness",
"value": "0.35"
},
{
"label": "Billing hygiene",
"value": "0.25"
}
]
}
]
}
]
}