Component library

Narrative

  • <Lead>

    Narrative

    Opening paragraph of a section. Larger and slightly muted, sets the tone.

    When to use: Use exactly once per section, immediately after the section's H1, to summarise the section's headline finding in 1–2 sentences.

    Preview

    Across two days with Acme's operations and finance teams, we mapped twelve candidate automation use cases. Six are recommended for the first wave.

    PropTypeNotes
    childrenReactNodeRequired. The opener prose.
    classNamestringOptional. Tailwind classes appended to the wrapper.

    Examples

    <Lead>
        Across two days with Acme's operations and finance teams, we mapped
        twelve candidate automation use cases. Six are recommended for the
        first wave.
    </Lead>
  • <KeyFinding>

    Narrative

    Highlighted observation from the workshop, with severity colour-coding.

    When to use: Call out an insight that materially affects the client's decisions. A 12-page report typically has 4–8 of these.

    Preview

    Key findingHigh impact

    Order intake passes through five inboxes before reaching ERP

    Every customer order today is touched by sales, customer service, operations, finance, and operations again. Each handover is a manual email or shared inbox forward.

    PropTypeNotes
    severity"low" | "medium" | "high"Optional. Default: medium. Visual emphasis. High = red ring, medium = amber, low = neutral.Values: low, medium, highExample: high
    titlestringOptional. Optional title shown above the body.Example: Order intake passes through five inboxes
    childrenReactNodeRequired. The finding's body prose.

    Examples

    <KeyFinding severity="high" title="Order intake passes through five inboxes before reaching ERP">
        Every customer order today is touched by sales, customer service,
        operations, finance, and operations again. Each handover is a manual
        email or shared inbox forward.
    </KeyFinding>
  • <Recommendation>

    Narrative

    Actionable next step with priority, owner, and effort.

    When to use: Whenever the report tells the client to do something. Always answer 'what should they do, and by when'. Use sparingly: a 12-page report typically has 5–8.

    Preview

    RecommendationDo nowEffort: MOwner: COO + IT Director

    Commit to a 12-week first wave covering invoice intake, order acks, and HR triage

    The three use cases share a deployment pattern (Lunnoa agent + ERP API + ticket fan-out) and together cover the largest concentration of manual hours we observed.

    PropTypeNotes
    priority"now" | "next" | "later"Optional. Default: next. Wave assignment. now = wave 1, next = wave 2, later = backlog.Values: now, next, laterExample: now
    titlestringRequired. The recommendation as an imperative sentence.Example: Commit to a 12-week first wave covering invoice intake, order acks, and HR triage
    ownerstringOptional. Named role or person responsible.Example: COO + IT Director
    effort"S" | "M" | "L" | "XL"Optional. T-shirt size estimate.Values: S, M, L, XLExample: M
    childrenReactNodeRequired. Rationale and next-step detail.

    Examples

    <Recommendation priority="now" title="Commit to a 12-week first wave covering invoice intake, order acks, and HR triage" effort="M" owner="COO + IT Director">
        The three use cases share a deployment pattern (Lunnoa agent + ERP
        API + ticket fan-out) and together cover the largest concentration of
        manual hours we observed.
    </Recommendation>
  • <Callout>

    Narrative

    Generic emphasis box for asides, important context, or warnings.

    When to use: Use when text needs visual separation from surrounding prose but isn't a finding or recommendation. Common: 'assumptions to validate', 'what we mean by X', 'next steps'.

    Preview

    PropTypeNotes
    tone"info" | "success" | "warning" | "danger"Optional. Default: info. Colour and icon.Values: info, success, warning, dangerExample: warning
    titlestringOptional. Optional headline.Example: Two assumptions to validate in week 1
    childrenReactNodeRequired. Callout body.

    Examples

    <Callout tone="warning" title="Two assumptions to validate in week 1">
        The projection above assumes the ERP middleware can be exposed within
        the first three weeks, and that the AP team is willing to triage
        exceptions in a Lunnoa queue rather than email.
    </Callout>
  • <Quote>

    Narrative

    Pull-quote from a workshop interview, with attribution.

    When to use: Use for high-signal, verbatim statements that capture how stakeholders actually framed a problem. One quote per finding is common.

    Preview

    We do the same five-step approval on every supplier invoice. The five steps make sense. Doing them all by hand five hundred times a month does not.

    Maria Fernandes, Head of Finance
    PropTypeNotes
    attributionstringOptional. Person's name.Example: Maria Fernandes
    rolestringOptional. Their role and/or department.Example: Head of Finance
    childrenReactNodeRequired. The quoted text. Do not include surrounding quotation marks — the component adds them.

    Examples

    <Quote attribution="Maria Fernandes" role="Head of Finance">
        We do the same five-step approval on every supplier invoice. The five
        steps make sense. Doing them all by hand five hundred times a month
        does not.
    </Quote>
  • <PersonCard>

    Narrative

    Workshop participant attribution card.

    When to use: Use in 'Workshop participants' sections to credit interviewees, sponsors, and reviewers.

    Preview

    Maria Fernandes
    Head of Finance · Finance
    Interviewee
    PropTypeNotes
    namestringRequired. Person's full name.Example: Maria Fernandes
    rolestringOptional. Job title.Example: Head of Finance
    departmentstringOptional. Team or department.Example: Finance
    contributedAs"Interviewee" | "Workshop participant" | "Sponsor" | "Reviewer"Optional. How the person engaged with the workshop.Values: Interviewee, Workshop participant, Sponsor, ReviewerExample: Interviewee

    Examples

    <PersonCard name="Maria Fernandes" role="Head of Finance" department="Finance" contributedAs="Interviewee" />
  • <TwoColumn>

    Narrative

    Side-by-side blocks. Stacks vertically on mobile.

    When to use: Use to balance two related blocks (e.g., two quotes, two side notes). For 'before vs. after' use BeforeAfter instead.

    Preview

    Maria Fernandes, Head of Finance
    James Okafor, Operations Manager
    PropTypeNotes
    reversebooleanOptional. Default: false. Swap column order on desktop.
    childrenReactNodeRequired. Two children, each rendered as one column.

    Examples

    <TwoColumn>
        <Quote attribution="Maria Fernandes" role="Head of Finance">…</Quote>
        <Quote attribution="James Okafor" role="Operations Manager">…</Quote>
    </TwoColumn>
  • <BeforeAfter>

    Narrative

    Explicit current-state vs proposed-state comparison block.

    When to use: Use to contrast the as-is process with the to-be process for one specific flow. Most reports have 1–2 of these maximum.

    Preview

    Today (manual)
    • Invoice arrives by email, forwarded to AP shared inbox.
    • AP coordinator opens PDF, retypes header data into ERP.
    After Wave 1 (Lunnoa-led)
    • Lunnoa agent receives the invoice, extracts header and lines.
    • Three-way match runs automatically; clean invoices post.
    PropTypeNotes
    beforeTitlestringOptional. Default: "Today". Label above the left card.
    beforeReactNodeRequired. Current-state content (often a <ul>).
    afterTitlestringOptional. Default: "After". Label above the right card.
    afterReactNodeRequired. Proposed-state content.

    Examples

    <BeforeAfter
        beforeTitle="Today (manual)"
        afterTitle="After Wave 1 (Lunnoa-led)"
        before={
            <ul>
                <li>Invoice arrives by email, forwarded to AP shared inbox.</li>
                <li>AP coordinator opens PDF, retypes header data into ERP.</li>
            </ul>
        }
        after={
            <ul>
                <li>Lunnoa agent receives the invoice, extracts header and lines.</li>
                <li>Three-way match runs automatically; clean invoices post.</li>
            </ul>
        }
    />
  • <SectionDivider>

    Narrative

    Visual divider between sub-sections, optionally labelled.

    When to use: Use sparingly to mark a clear topic shift inside one section, especially before participant lists or appendix-style content.

    Preview


    Workshop participants
    PropTypeNotes
    labelstringOptional. Small label centred on the divider.Example: Workshop participants

    Examples

    <SectionDivider label="Workshop participants" />

Dashboard

  • <KPI>

    Dashboard

    Single metric tile with optional trend indicator.

    When to use: Use for headline numbers (manual hours, error rate, ROI). Always inside a <KPIGrid>.

    Often with: KPIGrid

    Preview

    Manual hours / week
    142
    Across operations, finance and HR
    PropTypeNotes
    labelstringRequired. Short metric label.Example: Manual hours / week
    valueReactNodeRequired. The displayed value.Example: 142
    trend"up" | "down" | "flat" | "baseline"Optional. Default: baseline. Movement direction. 'baseline' means this is a starting measurement.Values: up, down, flat, baselineExample: down
    trendValuestringOptional. Magnitude of the trend.Example: −42%
    isPositiveDirection"higher" | "lower"Optional. Default: lower. Whether higher is better. Affects whether up/down arrow is green or red.Values: higher, lowerExample: lower
    descriptionstringOptional. Optional supporting line beneath the value.Example: Across operations, finance and HR

    Examples

    <KPI label="Manual hours / week" value="142" description="Across operations, finance and HR" trend="baseline" />
  • <KPIGrid>

    Dashboard

    Grid of KPI tiles. Responsive: collapses to 1 column on mobile.

    When to use: Wrap 2–4 KPIs together. Place at the top of a section under '## At a glance'.

    Often with: KPI

    Preview

    Use cases identified
    12
    First-wave candidates
    6
    Hours saved / month
    380
    −42% manual effort
    PropTypeNotes
    columns2 | 3 | 4Optional. Default: 4. Desktop column count.Values: 2, 3, 4Example: 3
    childrenReactNodeRequired. One or more <KPI> elements.

    Examples

    <KPIGrid columns={3}>
        <KPI label="Use cases identified" value="12" />
        <KPI label="First-wave candidates" value="6" />
        <KPI label="Hours saved / month" value="380" trend="down" trendValue="−42% manual effort" isPositiveDirection="lower" />
    </KPIGrid>
  • <MaturityRadar>

    Dashboard

    Radar chart of dimensions (axes) vs. maturity score (radial). Optional target overlay.

    When to use: Use to show a multi-dimensional capability assessment, typically in 'current state' or 'executive summary' sections.

    Preview

    Acme automation maturity — current vs. 6-month target

    Scores from 11 stakeholder interviews using the Lunnoa maturity framework (1 = ad hoc, 5 = optimised).

    Current Target
    PropTypeNotes
    dimensionsstring[]Required. Axis labels, in order.Example: ["Data", "Tooling", "Process", "Governance", "Skills"]
    currentnumber[]Required. Current scores, same order as dimensions.Example: [3, 2, 3, 2, 2]
    targetnumber[]Optional. Optional target scores, drawn as a dashed overlay.
    maxnumberOptional. Default: 5. Maximum value on the radial axis.
    titlestringOptional. Chart title.
    descriptionstringOptional. Subtitle / methodology note.

    Examples

    <MaturityRadar
        dimensions={["Data quality", "Tooling", "Process clarity", "Governance", "Automation skills"]}
        current={[3, 2, 3, 2, 2]}
        target={[4, 4, 4, 4, 3]}
        title="Acme automation maturity — current vs. 6-month target"
        description="Scores from 11 stakeholder interviews using the Lunnoa maturity framework (1 = ad hoc, 5 = optimised)."
    />
  • <MaturityHeatmap>

    Dashboard

    Stage-based maturity table (rows = dimensions, columns = levels). One highlighted cell per row.

    When to use: Use as an alternative to MaturityRadar when the maturity framework is stage-based (e.g. CMMI-style: ad hoc / repeatable / defined / managed / optimised).

    Preview

    Current vs 6-month target

    DimensionAd hocRepeatableDefinedManagedOptimised
    Data quality
    Tooling
    Current Target
    PropTypeNotes
    rowsstring[]Required. Row labels (dimensions).Example: ["Data quality", "Tooling", "Process clarity"]
    columnsstring[]Required. Column labels (maturity levels).Example: ["Ad hoc", "Repeatable", "Defined", "Managed", "Optimised"]
    currentboolean[][]Required. 2D array [row][col]. Each row should have at most one true cell.
    targetboolean[][]Optional. Optional target cells, drawn as a ring.
    titlestringOptional. Chart title.

    Examples

    <MaturityHeatmap
        rows={["Data quality", "Tooling"]}
        columns={["Ad hoc", "Repeatable", "Defined", "Managed", "Optimised"]}
        current={[[false, false, true, false, false], [false, true, false, false, false]]}
        target={[[false, false, false, true, false], [false, false, false, true, false]]}
        title="Current vs 6-month target"
    />
  • <OpportunityMatrix>

    Dashboard

    Effort × impact scatter plot (effort increases left to right, impact increases bottom to top). Top-left (low effort, high impact) is the usual quick-win zone. Optional category (or name) tabs match the DataTable filter pattern.

    When to use: Use to visualise a portfolio of candidate use cases / opportunities scored on two dimensions. Typically immediately before the recommended use case grid.

    Preview

    Twelve use cases scored on effort and impact

    FinanceOperationsPeople

    Portfolio by function

    FinanceOperations
    PropTypeNotes
    items{ name: string; effort: number; impact: number; category?: string }[]Required. Each item is plotted as a dot. effort and impact are 1–5.
    titlestringOptional. Chart title.
    descriptionstringOptional. Subtitle.
    tabs{ id: string; label: string }[]Optional. Optional filter tabs (same pattern as DataTable). Include `{ id: "all", label: "All" }` first. Other tab ids slug-match `tabFilterKey` on each item (default: category names, e.g. `finance` for Finance).
    tabFilterKey"category" | "name"Optional. Default: category. Which item field tab ids match after slugify.Values: category, name

    Examples

    <OpportunityMatrix
        title="Twelve use cases scored on effort and impact"
        items={[
            { name: "Supplier invoice intake", effort: 2, impact: 5, category: "Finance" },
            { name: "Order acknowledgements", effort: 2, impact: 4, category: "Operations" },
            { name: "HR ticket triage", effort: 1, impact: 3, category: "People" }
        ]}
    />
    <OpportunityMatrix
        title="Portfolio by function"
        tabs={[
            { id: "all", label: "All" },
            { id: "finance", label: "Finance" },
            { id: "operations", label: "Operations" }
        ]}
        items={[
            { name: "Invoice intake", effort: 2, impact: 5, category: "Finance" },
            { name: "Order acknowledgements", effort: 2, impact: 4, category: "Operations" }
        ]}
    />
  • <RoadmapTimeline>

    Dashboard

    Vertical phased timeline with status badges per phase.

    When to use: Use in roadmap / delivery plan sections to sequence work across phases (typically 3–5 phases).

    Preview

    Wave 1 — 12-week delivery

    1. Phase 1 — Foundation
      Weeks 1–3
      • ERP middleware exposed
      • Lunnoa platform deployed
    2. Phase 2 — Build the three flows
      Weeks 4–8
      • HR triage live week 5
      • Invoice intake live week 8
    PropTypeNotes
    phases{ label: string; when: string; status?: 'done'|'active'|'planned'; items?: string[] }[]Required. Phases in chronological order.
    titlestringOptional. Section title for the timeline.
    descriptionstringOptional. Subtitle.

    Examples

    <RoadmapTimeline
        title="Wave 1 — 12-week delivery"
        phases={[
            { label: "Phase 1 — Foundation", when: "Weeks 1–3", status: "active", items: ["ERP middleware exposed", "Lunnoa platform deployed"] },
            { label: "Phase 2 — Build the three flows", when: "Weeks 4–8", status: "planned", items: ["HR triage live week 5", "Invoice intake live week 8"] }
        ]}
    />
  • <UseCaseCard>

    Dashboard

    One use case tile with priority, area, owner, impact, effort, and time-to-value.

    When to use: Use to surface recommended automation use cases. Always inside <UseCaseGrid>.

    Often with: UseCaseGrid

    Preview

    Wave 1 Finance

    Supplier invoice intake

    Automate three-way matching between supplier invoices, purchase orders, and goods receipts.
    Impact
    ≈ 220 hrs / month
    Effort
    M
    TTV
    6 weeks
    Owner: Maria Fernandes
    PropTypeNotes
    titlestringRequired. Short use case name.Example: Supplier invoice intake
    descriptionReactNodeRequired. One-paragraph description of what gets automated.
    areastringOptional. Functional area.Example: Finance
    ownerstringOptional. Process owner.Example: Maria Fernandes
    priority"now" | "next" | "later"Optional. Default: next. Wave assignment.Values: now, next, laterExample: now
    impactstringOptional. Estimated impact.Example: ≈ 220 hrs / month
    effortstringOptional. Estimated effort, T-shirt size or duration.Example: M
    timeToValuestringOptional. Estimated time to first value.Example: 6 weeks

    Examples

    <UseCaseCard
        title="Supplier invoice intake"
        priority="now"
        area="Finance"
        owner="Maria Fernandes"
        impact="≈ 220 hrs / month"
        effort="M"
        timeToValue="6 weeks"
        description="Automate three-way matching between supplier invoices, purchase orders, and goods receipts."
    />
  • <UseCaseGrid>

    Dashboard

    Grid of UseCaseCards.

    When to use: Wrap recommended use cases together. Use 2 columns by default; 3 only if you have many small cards.

    Often with: UseCaseCard

    Preview

    Wave 1 Finance

    Supplier invoice intake

    Automate three-way matching between supplier invoices, purchase orders, and goods receipts.
    Impact
    ≈ 220 hrs / month
    Effort
    M
    TTV
    6 weeks
    Owner: Maria Fernandes
    Wave 1 Operations

    Order acknowledgements

    Auto-generate acknowledgement emails within five minutes.
    Impact
    ≈ 90 hrs / month
    Effort
    S
    TTV
    4 weeks
    Owner: James Okafor
    PropTypeNotes
    columns2 | 3Optional. Default: 2. Desktop column count.Values: 2, 3Example: 2
    childrenReactNodeRequired. One or more <UseCaseCard> elements.

    Examples

    <UseCaseGrid columns={2}>
        <UseCaseCard
            title="Supplier invoice intake"
            priority="now"
            area="Finance"
            owner="Maria Fernandes"
            impact="≈ 220 hrs / month"
            effort="M"
            timeToValue="6 weeks"
            description="Automate three-way matching between supplier invoices, purchase orders, and goods receipts."
        />
        <UseCaseCard
            title="Order acknowledgements"
            priority="now"
            area="Operations"
            owner="James Okafor"
            impact="≈ 90 hrs / month"
            effort="S"
            timeToValue="4 weeks"
            description="Auto-generate acknowledgement emails within five minutes."
        />
    </UseCaseGrid>
  • <ComparisonBar>

    Dashboard

    Horizontal grouped bar chart: one row per process, with a grey Before bar and a coloured After bar. Before/After legend is rendered automatically below the chart.

    When to use: Use to project the impact of a wave of automation across multiple processes. Most useful immediately after a use case grid. Put the process or use-case name in each row's `label` (Y-axis). Do not use Before/After wording such as "Manual today" or "After automation" in `label`; those states are already shown by bar colour and the chart legend.

    Often with: UseCaseGridBeforeAfter

    Preview

    Manual hours per month — before vs. projected after Wave 1

    BeforeAfter
    PropTypeNotes
    rows{ label: string; before: number; after: number }[]Required. One object per process being compared. Each row renders once on the Y-axis with two grouped bars (before, then after).
    rows[].labelstringRequired. Process or use-case name shown on the Y axis (e.g. "Supplier invoice intake"). Not the Before/After state.Example: Order acknowledgements
    rows[].beforenumberRequired. Current / baseline numeric value. Rendered as the grey bar.Example: 280
    rows[].afternumberRequired. Projected / proposed numeric value. Rendered as the coloured After bar.Example: 60
    unitstringOptional. Default: "". Suffix for tick labels and tooltips.Example: hrs
    isPositiveDirection"higher" | "lower"Optional. Default: lower. Whether smaller numbers are better. Drives the After bar colour.Values: higher, lowerExample: lower
    titlestringOptional. Chart title.
    descriptionstringOptional. Subtitle / methodology note.

    Examples

    <ComparisonBar
        title="Manual hours per month — before vs. projected after Wave 1"
        unit=" hrs"
        isPositiveDirection="lower"
        rows={[
            { label: "Supplier invoice intake", before: 280, after: 60 },
            { label: "Order acknowledgements", before: 110, after: 20 }
        ]}
    />
  • <DataTable>

    Dashboard

    Card-style data grid with optional status tabs, sortable columns, status badges, row checkboxes (illustrative), filter affordance, and client-side pagination.

    When to use: Use for tabular workshop appendices (delivery lists, backlog queues, milestone trackers) where readers benefit from filtering and paging without leaving the report. Keep row counts modest for print; prefer `pageSize` 5–10.

    Preview

    Delivery activities

    Track your recent shipping activities

    SelectRouteStatus
    #324112ElectronicsAcme10 Apr 2028 2:15 pmBerlin–Milan$1,250.00Delivered

    Showing 1 to 1 of 1

    PropTypeNotes
    titlestringRequired. Table card title.Example: Delivery activities
    descriptionstringOptional. Subtitle under the title.Example: Track your recent shipping activities
    columns{ key: string; label: string; sortable?: boolean }[]Required. Column definitions; `key` matches keys on each row object.
    rowsRecord<string, string>[]Required. Row data; values are strings so they serialise cleanly inside MDX.
    tabs{ id: string; label: string }[]Optional. Optional filter tabs. Include `{ id: "all", label: "All" }` first for an unfiltered view. Other `id` values slug-match `tabFilterKey` on each row (e.g. status "In Transit" matches tab `in-transit`).
    tabFilterKeystringOptional. Default: status. Row field used when filtering by tab.
    badgeColumnKeystringOptional. Default: status. Column whose cells render as coloured status pills.
    showCheckboxesbooleanOptional. Default: true. Show a leading illustrative checkbox column.
    showFilterButtonbooleanOptional. Default: true. Show a secondary Filter button (illustrative; no sheet wired).
    filterButtonLabelstringOptional. Default: "Filter". Label for the filter button.
    pageSizenumberOptional. Default: 5. Rows per page; set `0` to disable pagination footer.

    Examples

    <DataTable
        title="Delivery activities"
        description="Track your recent shipping activities"
        tabs={[
            { id: "all", label: "All" },
            { id: "delivered", label: "Delivered" },
            { id: "in-transit", label: "In transit" },
            { id: "pending", label: "Pending" },
            { id: "processing", label: "Processing" }
        ]}
        columns={[
            { key: "orderId", label: "Order ID", sortable: true },
            { key: "category", label: "Category", sortable: true },
            { key: "company", label: "Company", sortable: true },
            { key: "arrival", label: "Arrival", sortable: true },
            { key: "route", label: "Route", sortable: false },
            { key: "price", label: "Price", sortable: true },
            { key: "status", label: "Status", sortable: false }
        ]}
        rows={[{ orderId: "#324112", category: "Electronics", company: "Acme", arrival: "10 Apr 2028 2:15 pm", route: "Berlin–Milan", price: "$1,250.00", status: "Delivered" }]}
    />
  • <SimpleDataTable>

    Dashboard

    Read-only text grid: a header row from string labels and body rows from string arrays. Bordered card, optional caption, optional zebra striping, optional bold emphasis on chosen body rows (totals, subtotals). No sorting, filters, or pagination.

    When to use: Use when you only need to present a small matrix (assumption register, RACI, scorecard, inventory snapshot) without the interactive chrome of `<DataTable>`. Prefer markdown tables for narrative prose; use this when MDX expressions make row data easier to maintain.

    Often with: DataTable

    Preview

    Workshop attendance (illustrative)
    NameTeamDay 1Day 2
    Alex ExampleOperationsYesYes
    Jordan ExampleITYesNo
    Hours by workstream (illustrative)
    WorkstreamManual hrs / week
    Invoice intake42
    Order acknowledgements28
    Total70
    PropTypeNotes
    columnsstring[]Required. Header labels in column order.Example: ["Role", "Name", "Availability"]
    rowsstring[][]Required. Each entry is one row: an array of cell strings aligned to `columns`.Example: [["Sponsor", "Alex Example", "Weekly"], ["Owner", "Jordan Example", "Daily"]]
    captionstringOptional. Optional title or context line above the table.
    stripedbooleanOptional. Default: true. Whether odd body rows use a subtle banded background.
    boldRowIndicesnumber[]Optional. 0-based indices of body rows to render with semibold text (e.g. `[0, 2]`). Indices outside the row range are ignored.Example: [1]
    boldLastRowbooleanOptional. Default: false. When true, semibold the last body row (typical for a totals or summary line). Combines with `boldRowIndices`.

    Examples

    <SimpleDataTable
        caption="Workshop attendance (illustrative)"
        columns={["Name", "Team", "Day 1", "Day 2"]}
        rows={[
            ["Alex Example", "Operations", "Yes", "Yes"],
            ["Jordan Example", "IT", "Yes", "No"]
        ]}
    />
    <SimpleDataTable
        caption="Hours by workstream (illustrative)"
        columns={["Workstream", "Manual hrs / week"]}
        rows={[
            ["Invoice intake", "42"],
            ["Order acknowledgements", "28"],
            ["Total", "70"]
        ]}
        boldLastRow
    />
  • <AnalyticsCard>

    Dashboard

    Single-card analytics summary: title and subtitle, optional kebab affordance, a hero row (icon + label + large value), and a 2×2 grid of sub-metrics with tinted icon chips, values, and either a static caption or a success-styled trend line.

    When to use: Use for workshop readouts that mirror product analytics tiles (adoption, pipeline, programme health). Pair two cards inside `<TwoColumn>` for side-by-side comparisons. Keep copy factual; do not invent metrics.

    Preview

    User analytics

    Platform user insights

    Total users

    10,590

    Free users

    10,074

    95% of total users

    Paid users

    516

    +2.5% from last month

    New users

    1,200

    +5,238 from last month

    Most active

    480

    Weekly active

    PropTypeNotes
    titlestringRequired. Card title.Example: User analytics
    subtitlestringOptional. Muted line under the title.Example: Platform user insights
    heroIcon"users" | "folder" | "projects"Required. Icon in the hero row.Values: users, folder, projectsExample: users
    heroLabelstringRequired. Label beside the hero icon.Example: Total users
    heroValuestringRequired. Large headline figure.Example: 10,590
    statsAnalyticsCardStat[]Required. Exactly four objects (2×2 grid). Each stat: `label`, `value`, `icon` (users|folder|pie|trend|clock|target|percent|building), `iconTone` (brand|warning|success|indigo|emerald|rose|slate), optional `caption`, or `trendValue` + optional `trendSuffix` for a green trend line.
    showMenubooleanOptional. Default: true. Show the illustrative kebab button.

    Examples

    <AnalyticsCard
        title="User analytics"
        subtitle="Platform user insights"
        heroIcon="users"
        heroLabel="Total users"
        heroValue="10,590"
        stats={[
            { label: "Free users", value: "10,074", icon: "pie", iconTone: "indigo", caption: "95% of total users" },
            { label: "Paid users", value: "516", icon: "percent", iconTone: "emerald", trendValue: "+2.5%", trendSuffix: "from last month" },
            { label: "New users", value: "1,200", icon: "users", iconTone: "brand", trendValue: "+5,238", trendSuffix: "from last month" },
            { label: "Most active", value: "480", icon: "clock", iconTone: "warning", caption: "Weekly active" }
        ]}
    />
  • <UsageDonutCard>

    Dashboard

    Donut chart with centre headline figures, a breakdown list (avatar initials, title, subtitle, coloured dot and usage line), and an optional footer link.

    When to use: Use when a workshop report needs a share-style visual (usage by vendor, cost by workstream, incidents by severity). Pass numeric `segments[].value` for slice proportions; `rows[].usageDisplay` is free-form text for the right column. Prefer 3–5 segments for legibility.

    Preview

    API token usages

    13.5M

    2 450 keys

    • GPT

      2 API keys configured

      7M

      Tokens used

    • Gemini

      1 API key configured

      2.5M

      Tokens used

    • xAI

      2 API keys configured

      4.5M

      Tokens used

    PropTypeNotes
    titlestringRequired. Card title.Example: API token usages
    centerValuestringRequired. Large text in the donut centre.Example: 13.5M
    centerSubtextstringOptional. Muted line under the centre value.Example: 2 450 API keys
    segments{ id: string; value: number; color: "brand"|"purple"|"sky"|"fuchsia"|"emerald" }[]Required. Slice weights and semantic colours (maps to theme tokens).
    rows{ segmentId: string; title: string; subtitle: string; usageDisplay: string; avatarText?: string; usageCaption?: string }[]Required. One row per slice; `segmentId` must match a `segments[].id` for the dot colour.
    footerLabelstringOptional. Link text below the list.
    footerHrefstringOptional. URL for the footer link (internal or external).
    showMenubooleanOptional. Default: true. Show the illustrative kebab button.

    Examples

    <UsageDonutCard
        title="API token usages"
        centerValue="13.5M"
        centerSubtext="2 450 keys"
        segments={[
            { id: "gpt", value: 7, color: "brand" },
            { id: "gemini", value: 2.5, color: "purple" },
            { id: "xai", value: 4.5, color: "sky" }
        ]}
        rows={[
            { segmentId: "gpt", avatarText: "GP", title: "GPT", subtitle: "2 API keys configured", usageDisplay: "7M" },
            { segmentId: "gemini", avatarText: "Ge", title: "Gemini", subtitle: "1 API key configured", usageDisplay: "2.5M" },
            { segmentId: "xai", avatarText: "xA", title: "xAI", subtitle: "2 API keys configured", usageDisplay: "4.5M" }
        ]}
        footerLabel="View all usage details"
        footerHref="#"
    />
  • <FinanceOverviewCard>

    Dashboard

    Two-column financial headline: left block (title, date range, total revenue with optional delta and footnote), right block (product condition headline, optional trend pill, year selector, multi-series monotone area chart), plus a three-column footer row for supporting metrics.

    When to use: Use for executive-style revenue or adoption snapshots in workshop readouts. Pass `chartMonths` and aligned `chartSeries[].values`; include at least one series (empty shows a muted placeholder). Keep series count to 2–4 for clarity. Footer `stats` should contain exactly three items.

    Preview

    Financial income

    1 Aug 2025 – 1 Nov 2025

    Total revenue+9.78%

    £8,240.00

    Increased 15% from last month

    Product condition

    75%2.8%

    Selling product

    £335,000

    Followers

    1,500

    Campaign

    560

    PropTypeNotes
    dateRangestringRequired. Human-readable range under the income title.Example: 1 Aug 2025 – 1 Nov 2025
    totalRevenueValuestringRequired. Large headline currency string.Example: £8,240.00
    incomeTitlestringOptional. Default: "Financial income". Left column title.
    totalRevenueLabelstringOptional. Default: "Total revenue". Label above the headline figure.
    totalRevenueDeltastringOptional. Optional percentage chip; leading + uses success colour, leading − uses error.
    totalRevenueFootnotestringOptional. Muted line under the revenue figure.
    conditionValuestringRequired. Large headline next to the chart header.Example: 75%
    conditionTitlestringOptional. Default: "Product condition". Muted label above the condition value.
    conditionBadgeTextstringOptional. Pill text beside the condition value.Example: 2.8%
    conditionBadgeTrend"up" | "down"Optional. Default: down. Arrow direction inside the pill.Values: up, down
    yearsstring[]Optional. Default: ["2025"]. Year `<select>` options.
    chartMonthsstring[]Required. X-axis labels in order (e.g. Jan … Jul).
    chartSeries{ name: string; values: number[]; colorKey: "brand"|"sky"|"slate" }[]Required. One or more series; each `values` array must match `chartMonths` length. If empty, a muted placeholder is shown instead of the chart.
    stats{ label: string; value: string; labelTone?: "brand"|"sky"|"emerald" }[]Required. Exactly three footer metrics.

    Examples

    <FinanceOverviewCard
        dateRange="1 Aug 2025 – 1 Nov 2025"
        totalRevenueDelta="+9.78%"
        totalRevenueValue="£8,240.00"
        totalRevenueFootnote="Increased 15% from last month"
        conditionValue="75%"
        conditionBadgeText="2.8%"
        years={["2024", "2025", "2026"]}
        chartMonths={["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul"]}
        chartSeries={[
            { name: "Series A", values: [1200, 1800, 2200, 2600, 3000, 3400, 3800], colorKey: "sky" },
            { name: "Series B", values: [800, 1100, 1400, 1700, 2100, 2500, 2900], colorKey: "brand" },
            { name: "Series C", values: [400, 500, 600, 700, 800, 900, 1000], colorKey: "slate" }
        ]}
        stats={[
            { label: "Selling product", value: "£335,000", labelTone: "sky" },
            { label: "Followers", value: "1,500", labelTone: "sky" },
            { label: "Campaign", value: "560", labelTone: "emerald" }
        ]}
    />
  • <MiniStatCard>

    Dashboard

    Compact metric card with a headline value, a coloured label, and a badge icon. The body below the header is configurable via `display`: a sparkline/donut chart, a short supporting paragraph, or empty (header only). Use inside `<MiniStatGrid>`.

    When to use: Use when a report needs a 2×2 (or wider) grid of at-a-glance metrics. Mix `display="chart"` for tiles that show a trend, `display="text"` for tiles that need a short qualitative note (e.g. "Up from £8,100 last quarter"), and `display="none"` for compact value-only tiles. Pair with `<MiniStatGrid>` to lay out tiles side by side.

    Often with: MiniStatGridKPIKPIGrid

    Preview

    $10,230

    Expense

    $65,432

    Sales

    £48k

    Pipeline

    Up from £42k last month, driven by Q3 supplier renewals.

    3

    Active workshops

    PropTypeNotes
    valuestringRequired. Main headline metric, e.g. "$10,230".Example: $10,230
    labelstringRequired. Short label below the value, e.g. "Expense".Example: Expense
    icon"credit-card" | "shopping-bag" | "shopping-cart" | "bar-chart" | "trend-up" | "line-chart" | "wallet"Required. Icon displayed in the top-right badge.Values: credit-card, shopping-bag, shopping-cart, bar-chart, trend-up, line-chart, walletExample: credit-card
    display"chart" | "text" | "none"Optional. What to render below the header. `chart` requires `chart` + `data`; `text` requires `text`; `none` renders the header only. If omitted, the type is inferred from which props are present (chart+data → chart, otherwise text → text, otherwise none).Values: chart, text, noneExample: chart
    chart"donut" | "bar" | "line"Optional. Type of embedded chart (only used when `display="chart"`). `donut` = ring chart (2+ segments); `bar` = vertical bar sparkline; `line` = monotone line sparkline.Values: donut, bar, lineExample: donut
    datanumber[]Optional. Data for the chart (only used when `display="chart"`). Donut: two or more segment values (proportional), e.g. [70, 30]. Bar/Line: ordered series of values, e.g. [3, 8, 5, 12, 7, 15, 9].Example: [70, 30]
    textstringOptional. Short supporting paragraph (only used when `display="text"`). Keep it to one or two sentences; longer copy is cropped visually.Example: Up from £8,100 last quarter, driven by Q3 supplier renewals.

    Examples

    <MiniStatGrid columns={2}>
      <MiniStatCard value="$10,230" label="Expense" icon="credit-card" display="chart" chart="donut" data={[70, 30]} />
      <MiniStatCard value="$65,432" label="Sales" icon="shopping-bag" display="chart" chart="bar" data={[8, 10, 9, 11, 10, 12, 11, 13, 12, 14, 13, 15]} />
      <MiniStatCard value="£48k" label="Pipeline" icon="trend-up" display="text" text="Up from £42k last month, driven by Q3 supplier renewals." />
      <MiniStatCard value="3" label="Active workshops" icon="bar-chart" display="none" />
    </MiniStatGrid>
  • <MiniStatGrid>

    Dashboard

    Responsive grid wrapper for `<MiniStatCard>` tiles, defaulting to a 2-column layout.

    When to use: Wrap two or more `<MiniStatCard>` components inside `<MiniStatGrid>` to produce the 2×2 (or wider) tile layout. Set `columns` to 3 or 4 if you have more tiles.

    Often with: MiniStatCard

    Preview

    $10,230

    Expense

    $65,432

    Sales

    £48k

    Pipeline

    Up from £42k last month, driven by Q3 supplier renewals.

    3

    Active workshops

    PropTypeNotes
    childrenReactNodeRequired. `<MiniStatCard>` elements.
    columns2 | 3 | 4Optional. Default: 2. Number of columns at the desktop breakpoint.Values: 2, 3, 4Example: 2

    Examples

    <MiniStatGrid columns={2}>
      <MiniStatCard value="$10,230" label="Expense" icon="credit-card" display="chart" chart="donut" data={[70, 30]} />
      <MiniStatCard value="$65,432" label="Sales" icon="shopping-bag" display="chart" chart="bar" data={[8, 10, 9, 11, 10, 12, 11, 13, 12, 14, 13, 15]} />
      <MiniStatCard value="£48k" label="Pipeline" icon="trend-up" display="text" text="Up from £42k last month, driven by Q3 supplier renewals." />
      <MiniStatCard value="3" label="Active workshops" icon="bar-chart" display="none" />
    </MiniStatGrid>