---
name: omega-point-chart-style
description: House style for data figures in the Omega Point idiom — dark canvas, one gold semantic accent, mono labels, serif numbers, 13 chart archetypes with typed configs. Use when producing a chart, figure, social card, or carousel slide in this style, or when adapting the system to another brand's tokens.
---

# Omega Point chart style

Source of truth: https://omegapoint.systems/chart-styles (human page) · https://omegapoint.systems/chart-styles/skill.md (this file) · https://omegapoint.systems/chart-styles/tokens.css (raw CSS tokens).

This is the figure system behind Omega Point's growth studies. It is generated from the code that renders the figures, so it matches what ships. Take it as-is, or swap the tokens for your own brand and keep the rules.

## How to use this skill

1. Pick the archetype whose **data shape** matches the numbers you actually have. Do not bend the data to a prettier chart.
2. Write the chart config against the TypeScript interface in the spec at the bottom. Every value that appears on the figure is pre-formatted text in the config; the renderer never rounds or formats for you.
3. Compose the figure: header, funnel bar, headline with one gold-italic span, chart, three stats, footer with a real source.
4. Check the rules below before you call it done. Gold means one thing. Sources are real.

## Tokens

All colors are OKLCH on a dark canvas. Swap the values, keep the roles.

| Token | Value | Role |
|---|---|---|
| `--bg` | `oklch(0.135 0.008 50)` | Canvas. Every figure is dark; there is no light variant. |
| `--ink` | `oklch(0.945 0.02 80)` | Primary text: headlines, big numbers, values. |
| `--ink-2` | `oklch(0.82 0.018 80)` | Secondary text: the card standfirst, the wordmark. |
| `--ink-4` | `oklch(0.47 0.012 80)` | Muted labels: axis text, stat labels, footer. |
| `--rule` | `oklch(0.32 0.010 50)` | Strongest hairline. |
| `--rule-2` | `oklch(0.26 0.009 50)` | Mid hairline. |
| `--rule-3` | `oklch(0.205 0.008 50)` | Faintest hairline: stat-row dividers, muted waffle cells, grid lines. |
| `--c1` | `oklch(0.78 0.12 75)` | Gold. The SEMANTIC accent only: the threshold, the median dot, the hero number, the headline emphasis, the "now" rule. Never decoration, never a data series. |
| `--c2` | `oklch(0.66 0.15 25)` | Oxblood. Data series. |
| `--c3` | `oklch(0.70 0.10 150)` | Forest. Data series. |
| `--c4` | `oklch(0.66 0.11 245)` | Navy. Data series. |
| `--c5` | `oklch(0.64 0.10 320)` | Plum. Data series. |
| `--c6` | `oklch(0.80 0.11 62)` | Amber. Data series. Warm, so keep it non-adjacent to gold in any ordered sweep. |
| `--serif` | `var(--serif-font), "Newsreader", serif` | Newsreader. Headlines, big numbers, standfirst. |
| `--sans` | `var(--sans-font), system-ui, sans-serif` | Geist. Rarely used on figures. |
| `--mono` | `var(--mono-font), "Geist Mono", monospace` | Geist Mono. Every label: axis, annotation, meta, stats, footer. Uppercase with tracking. |

Type: `--serif` for headlines and big numbers, `--mono` for every label (uppercase, letter-spacing 0.06–0.2em). Label classes on SVG text: `.axis` (mono 14px, --ink-4, uppercase) and `.anno` (mono 16px, gold); on 4:5 cards, `.axis-card` (28px) and `.anno-card` (30px).

## Compositions

### Figure — 16:9

- Export: 3200 × 1800 px
- Padding: 54px top · 64px sides · 40px bottom (at 1600 × 900)
- Chart viewBox: `0 0 1080 250`
- Header row: brand mark + mono wordmark left, uppercase mono meta right.
- Funnel bar: six AARRR segments; the active stage is thicker and labeled.
- Headline: serif, 58px, line-height 1.03, with one gold-italic emphasis span.
- Chart: the archetype SVG, full width, scaled to its viewBox.
- Stat row: three values at 28px with a gold <b> emphasis, mono uppercase labels beneath, hairline dividers.
- Footer: two mono uppercase lines, study name left, source right.

### Card — 4:5

- Export: 2400 × 3000 px
- Padding: 64px top · 60px sides · 52px bottom (at 1200 × 1500)
- Chart viewBox: `0 0 1080 680`
- Same config, same primitives, portrait. Text-forward: headline at 62px plus a 24px serif standfirst in --ink-2.
- The chart band is flex: 1 and vertically centers the archetype's taller card variant.
- Axis and annotation labels scale up (.axis-card / .anno-card) so a phone-sized view stays legible.

### Carousel — 4:5 per slide

- Export: one PNG per slide
- Padding: as Card
- Chart viewBox: `0 0 1080 680`
- Slide count = 3 + narrative lines: hook + chart, one story slide per narrative line paired with a stat, an education slide, a closing slide.
- Education slide pulls a metric definition and formula when metricSlug is set, else the AARRR stage glossary.
- Legibility first: big type, generous whitespace, because it is read small on a phone.

## Archetypes

| id | config type | use for |
|---|---|---|
| `line-threshold` | LineThresholdChart | A logistic S-curve with one threshold that separates outcomes. |
| `vertical-bar` | VerticalBarChart | Magnitude over time, or across a few categories. |
| `horizontal-range` | RangeChart | Low–high ranges with a median across categories: distributions, benchmark bands. |
| `donut` | DonutChart | Parts of a whole, five slices or fewer, with a centered total. |
| `heatmap` | HeatmapChart | A grid where the value is the cell's luminance: cohorts, benchmarks by segment. |
| `gantt` | GanttChart | Discrete intervals on a shared timeline with a gold "now" rule. |
| `small-multiples` | SmallMultiplesChart | The same mini-chart repeated across categories so they can be compared at a glance. |
| `waffle` | WaffleChart | One striking percentage out of 100. |
| `proportional-area` | ProportionalAreaChart | Relative magnitudes as area-sized bubbles, four or fewer. |
| `parliament` | ParliamentChart | Composition as seats in a semicircle; factions sum to roughly 100. |
| `venn` | VennChart | Three overlapping sets. A framework figure: the positions are editorial, not measured. **FRAMEWORK.** |
| `matrix` | MatrixChart | Two-axis positioning with one dot per actor. A framework figure: the positions are editorial, not measured. **FRAMEWORK.** |
| `number-plate` | NumberPlateChart | One hero number is the whole figure. |

### Line + threshold (`line-threshold`)

- **Use for:** A logistic S-curve with one threshold that separates outcomes.
- **Data shape:** Four logistic parameters (floor, amplitude, midpoint, steepness), a plateau, a threshold x, and one annotated marker.
- **Config type:** `LineThresholdChart` (spec below)
- **Reference:** "Slack found activation at two thousand messages." — https://omegapoint.systems/infographics/slack-activation/figure.png (16:9) · https://omegapoint.systems/infographics/slack-activation/card.png (4:5)

```json
{
  "fn": {
    "floor": 3,
    "amp": 90,
    "mid": 1450,
    "steep": 194
  },
  "xMax": 4000,
  "yMax": 100,
  "plateau": {
    "y": 93,
    "label": "93% RETAINED"
  },
  "threshold": {
    "x": 2000,
    "label": "2,000 MESSAGES"
  },
  "marker": {
    "x": 2000,
    "y": 88,
    "label": "≈ 88%"
  },
  "xAxisLabel": "4,000 MESSAGES SENT · TEAM LIFETIME"
}
```

### Vertical bar (`vertical-bar`)

- **Use for:** Magnitude over time, or across a few categories.
- **Data shape:** An ordered list of bars, each with a label, a value, and a pre-formatted display string.
- **Config type:** `VerticalBarChart` (spec below)
- **Reference:** "Duolingo's organic engine more than doubled MAU in two years." — https://omegapoint.systems/infographics/duolingo-mau-growth/figure.png (16:9) · https://omegapoint.systems/infographics/duolingo-mau-growth/card.png (4:5)

```json
{
  "bars": [
    {
      "label": "2021",
      "value": 42.4,
      "display": "42M"
    },
    {
      "label": "2022",
      "value": 60.7,
      "display": "61M"
    },
    {
      "label": "2023",
      "value": 88.4,
      "display": "88M"
    }
  ],
  "domainMax": 100,
  "gridTicks": [
    0,
    25,
    50,
    75,
    100
  ],
  "unit": "M",
  "yAxisLabel": "YEAR-END (Q4) MONTHLY ACTIVE USERS"
}
```

### Horizontal range (`horizontal-range`)

- **Use for:** Low–high ranges with a median across categories: distributions, benchmark bands.
- **Data shape:** Rows of label + low + high on a shared domain, with explicit grid ticks and a unit.
- **Config type:** `RangeChart` (spec below)
- **Reference:** "Gross margin is set by your business model." — https://omegapoint.systems/infographics/gross-margin-by-model/figure.png (16:9) · https://omegapoint.systems/infographics/gross-margin-by-model/card.png (4:5)

```json
{
  "rows": [
    {
      "label": "Marketplace",
      "low": 80,
      "high": 90
    },
    {
      "label": "SaaS",
      "low": 70,
      "high": 85
    },
    {
      "label": "Usage-based",
      "low": 50,
      "high": 80
    },
    {
      "label": "Hardware",
      "low": 40,
      "high": 60
    },
    {
      "label": "Media",
      "low": 35,
      "high": 50
    },
    {
      "label": "E-commerce",
      "low": 30,
      "high": 50
    }
  ],
  "domainMax": 100,
  "gridTicks": [
    0,
    25,
    50,
    75,
    100
  ],
  "unit": "%"
}
```

### Donut (`donut`)

- **Use for:** Parts of a whole, five slices or fewer, with a centered total.
- **Data shape:** Slices with label, value, color token, and a pre-formatted percent; a center value and label.
- **Config type:** `DonutChart` (spec below)
- **Reference:** "Most of Shopify's revenue is transactional, not subscription." — https://omegapoint.systems/infographics/shopify-revenue-mix/figure.png (16:9) · https://omegapoint.systems/infographics/shopify-revenue-mix/card.png (4:5)

```json
{
  "slices": [
    {
      "label": "Merchant Solutions",
      "value": 73,
      "color": "var(--c3)",
      "pct": "73%"
    },
    {
      "label": "Subscription Solutions",
      "value": 27,
      "color": "var(--c6)",
      "pct": "27%"
    }
  ],
  "centerValue": "$7.1B",
  "centerLabel": "2023 REVENUE"
}
```

### Heatmap (`heatmap`)

- **Use for:** A grid where the value is the cell's luminance: cohorts, benchmarks by segment.
- **Data shape:** Row labels × column labels, a matching cell grid of label + value, one base color, and the value that maps to full opacity.
- **Config type:** `HeatmapChart` (spec below)
- **Reference:** "Games lose most players in the first week." — https://omegapoint.systems/infographics/retention-benchmarks-heatmap/figure.png (16:9) · https://omegapoint.systems/infographics/retention-benchmarks-heatmap/card.png (4:5)

```json
{
  "rows": [
    "Social / Consumer",
    "Games"
  ],
  "cols": [
    "D1",
    "D7",
    "D30"
  ],
  "cells": [
    [
      {
        "label": "50–70%",
        "value": 60
      },
      {
        "label": "35–50%",
        "value": 42.5
      },
      {
        "label": "20–30%",
        "value": 25
      }
    ],
    [
      {
        "label": "~22%",
        "value": 22
      },
      {
        "label": "3–4%",
        "value": 3.5
      },
      {
        "label": "<3%",
        "value": 2
      }
    ]
  ],
  "colorBase": "var(--c3)",
  "maxValue": 70
}
```

### Gantt (`gantt`)

- **Use for:** Discrete intervals on a shared timeline with a gold "now" rule.
- **Data shape:** Items with a label and a start year, a domain start/end, the year of the now-rule, and axis ticks.
- **Config type:** `GanttChart` (spec below)
- **Reference:** "Amazon spent 17 years making Prime impossible to quit." — https://omegapoint.systems/infographics/amazon-prime-benefit-stack/figure.png (16:9) · https://omegapoint.systems/infographics/amazon-prime-benefit-stack/card.png (4:5)

```json
{
  "items": [
    {
      "label": "Free 2-day shipping",
      "start": 2005
    },
    {
      "label": "Prime Video",
      "start": 2011
    },
    {
      "label": "Prime Music",
      "start": 2014
    },
    {
      "label": "Prime Photos",
      "start": 2015
    },
    {
      "label": "Prime Gaming",
      "start": 2016
    },
    {
      "label": "Whole Foods",
      "start": 2017
    },
    {
      "label": "Same-day delivery",
      "start": 2019
    },
    {
      "label": "Pharmacy + Grubhub+",
      "start": 2022
    }
  ],
  "domainStart": 2004,
  "domainEnd": 2025,
  "nowYear": 2025,
  "axisTicks": [
    2005,
    2010,
    2015,
    2020,
    2025
  ]
}
```

### Small multiples (`small-multiples`)

- **Use for:** The same mini-chart repeated across categories so they can be compared at a glance.
- **Data shape:** Panels with label, value, and display string on one shared domain, plus one reference gridline.
- **Config type:** `SmallMultiplesChart` (spec below)
- **Reference:** "CAC payback stretches as you move upmarket." — https://omegapoint.systems/infographics/cac-payback-by-segment/figure.png (16:9) · https://omegapoint.systems/infographics/cac-payback-by-segment/card.png (4:5)

```json
{
  "panels": [
    {
      "label": "DTC",
      "value": 6,
      "display": "≤ 6 MO"
    },
    {
      "label": "SMB",
      "value": 12,
      "display": "≤ 12 MO"
    },
    {
      "label": "Mid-market",
      "value": 18,
      "display": "≤ 18 MO"
    },
    {
      "label": "Enterprise",
      "value": 24,
      "display": "≤ 24 MO"
    }
  ],
  "domainMax": 24,
  "unit": "MO",
  "refTick": 12
}
```

### Waffle (`waffle`)

- **Use for:** One striking percentage out of 100.
- **Data shape:** A filled count out of a total (10×10 grid), the two cell colors, a caption, and which count the big fraction shows.
- **Config type:** `WaffleChart` (spec below)
- **Reference:** "By day seven, ninety-six of a hundred players are gone." — https://omegapoint.systems/infographics/game-d7-retention-waffle/figure.png (16:9) · https://omegapoint.systems/infographics/game-d7-retention-waffle/card.png (4:5)

```json
{
  "value": 4,
  "total": 100,
  "emphasis": "empty",
  "filledColor": "var(--c1)",
  "mutedColor": "var(--rule-3)",
  "label": "players gone by day 7"
}
```

### Proportional area (`proportional-area`)

- **Use for:** Relative magnitudes as area-sized bubbles, four or fewer.
- **Data shape:** Bubbles with label, value (area ∝ value), display string, and an optional muted flag for the incumbent.
- **Config type:** `ProportionalAreaChart` (spec below)
- **Reference:** "Neobanks still earn less per user than the banks they disrupt." — https://omegapoint.systems/infographics/neobank-arpu-bubbles/figure.png (16:9) · https://omegapoint.systems/infographics/neobank-arpu-bubbles/card.png (4:5)

```json
{
  "bubbles": [
    {
      "label": "Traditional bank",
      "value": 350,
      "display": "$350",
      "muted": true
    },
    {
      "label": "Chime",
      "value": 214,
      "display": "$214"
    },
    {
      "label": "Wise",
      "value": 111,
      "display": "$111"
    },
    {
      "label": "Nubank",
      "value": 102,
      "display": "$102"
    }
  ],
  "unit": "ARPU / USER / YEAR"
}
```

### Parliament (`parliament`)

- **Use for:** Composition as seats in a semicircle; factions sum to roughly 100.
- **Data shape:** Factions with label, seat count, color token, and percent label; a total seat count.
- **Config type:** `ParliamentChart` (spec below)
- **Reference:** "Most B2B SaaS traffic is already yours — direct, not paid." — https://omegapoint.systems/infographics/saas-traffic-mix/figure.png (16:9) · https://omegapoint.systems/infographics/saas-traffic-mix/card.png (4:5)

```json
{
  "factions": [
    {
      "label": "Direct",
      "value": 54,
      "color": "var(--c4)",
      "pct": "54%"
    },
    {
      "label": "Organic search",
      "value": 26,
      "color": "var(--c3)",
      "pct": "26%"
    },
    {
      "label": "Other",
      "value": 18,
      "color": "var(--c5)",
      "pct": "18%"
    },
    {
      "label": "Paid search",
      "value": 2,
      "color": "var(--c2)",
      "pct": "<2%"
    }
  ],
  "totalSeats": 100
}
```

### Venn (`venn`)

- **Use for:** Three overlapping sets. A framework figure: the positions are editorial, not measured.
- **Data shape:** Exactly three sets (label + color token) and a gold center label naming the overlap.
- **Config type:** `VennChart` (spec below)
- **Reference:** "Durable retention needs all three forces." — https://omegapoint.systems/infographics/retention-moat-venn/figure.png (16:9) · https://omegapoint.systems/infographics/retention-moat-venn/card.png (4:5)

```json
{
  "sets": [
    {
      "label": "Habit",
      "color": "var(--c4)"
    },
    {
      "label": "Switching costs",
      "color": "var(--c3)"
    },
    {
      "label": "Value density",
      "color": "var(--c6)"
    }
  ],
  "centerLabel": "RETENTION MOAT"
}
```

### 2×2 matrix (`matrix`)

- **Use for:** Two-axis positioning with one dot per actor. A framework figure: the positions are editorial, not measured.
- **Data shape:** Two axes (label + low/high poles), four quadrant labels, and dots at normalized 0–100 x/y positions.
- **Config type:** `MatrixChart` (spec below)
- **Reference:** "The best usage models compound — high NRR, fast payback." — https://omegapoint.systems/infographics/nrr-payback-matrix/figure.png (16:9) · https://omegapoint.systems/infographics/nrr-payback-matrix/card.png (4:5)

```json
{
  "xAxis": {
    "label": "CAC payback",
    "lowLabel": "faster",
    "highLabel": "slower"
  },
  "yAxis": {
    "label": "NRR",
    "lowLabel": "low",
    "highLabel": "high"
  },
  "quadrants": [
    "Efficient compounder",
    "Enterprise bet",
    "Leaky bucket",
    "Danger zone"
  ],
  "dots": [
    {
      "label": "Snowflake",
      "x": 35,
      "y": 88
    },
    {
      "label": "Datadog",
      "x": 42,
      "y": 82
    },
    {
      "label": "Figma",
      "x": 40,
      "y": 85
    },
    {
      "label": "Amplitude",
      "x": 55,
      "y": 72
    }
  ]
}
```

### Number plate (`number-plate`)

- **Use for:** One hero number is the whole figure.
- **Data shape:** A giant display value, a sublabel, and either a before/after comparison pair or a short note.
- **Config type:** `NumberPlateChart` (spec below)
- **Reference:** "Dropbox's referral loop turned 100K into 4M in fifteen months." — https://omegapoint.systems/infographics/dropbox-referral-growth/figure.png (16:9) · https://omegapoint.systems/infographics/dropbox-referral-growth/card.png (4:5)

```json
{
  "value": "39×",
  "sublabel": "100K → 4M USERS · 15 MONTHS",
  "compare": {
    "beforeLabel": "100K · before the loop",
    "beforeValue": 100000,
    "afterLabel": "4M · 15 months later",
    "afterValue": 4000000,
    "caption": "Registered users"
  }
}
```

## Rules

1. Gold (--c1) is the semantic accent only. It marks the one thing the figure is about: the threshold, the median, the hero number, the emphasis span. Data series use --c2 through --c6 and --ink.
2. One idea per figure. The headline states it in words; the chart proves it; the stat row quantifies it; the footer sources it.
3. Every label is mono, uppercase, tracked. Every number that matters is serif and big.
4. Rounded corners are small (rx ≈ 4). Hairlines use the rule ramp, never a solid mid-grey.
5. Funnel bar order is fixed: awareness --c5, acquisition --c4, activation --c3, retention --c6, revenue --c2, referral --c1. It reads as a consistent legend across every figure and keeps the two warm tones apart.
6. Honest sourcing. Cite a real source in the footer. Label derived, directional, or estimated values as such. Framework archetypes (venn, matrix) must say FRAMEWORK · POSITIONS EDITORIAL on the figure. Never invent precision.
7. Look at the output. Export the PNG at full size and judge it by eye before it ships; a figure that passes the type-checker can still be wrong.

## Adapting to your brand

- Replace the six `--c*` values and the ink/rule ramps in `tokens.css`. Keep one accent that is used semantically and five series colors that are not.
- Keep the label/number split: one mono face for labels, one display face for numbers and headlines.
- Keep the composition order. The headline-states, chart-proves, stats-quantify, footer-sources sequence is what makes the figures read at a glance.
- Keep the sourcing rule. A figure that cannot cite its numbers is not finished.

## Type spec (`src/lib/infographics/types.ts`, verbatim)

```ts
/**
 * Shared archetype types for the infographic machine.
 * Imported by both src/data/infographics.tsx and component modules.
 * Data/config must NOT import from component modules.
 */

// ─── Chart render variant ─────────────────────────────────────────────────────

/**
 * Chart render variant. "figure" (default) is the wide 16:9 idiom; "card" is the
 * tall portrait variant that fills a 4:5 SocialCard's chart band. Only the four
 * highest-use archetypes accept this; the rest are figure-only.
 */
export type ChartVariant = "figure" | "card";

// ─── Logistic curve parameters ────────────────────────────────────────────────

export interface LogisticConfig {
  floor: number;
  amp: number;
  mid: number;
  steep: number;
}

// ─── LineThreshold chart config ───────────────────────────────────────────────

export interface LineThresholdChart {
  fn: LogisticConfig;
  xMax: number;
  yMax: number;
  plateau: { y: number; label: string };
  threshold: { x: number; label: string };
  marker: { x: number; y: number; label: string };
  xAxisLabel: string;
}

// ─── RangeChart config (horizontal-range archetype) ───────────────────────────

export interface RangeRow {
  label: string;
  low: number;
  high: number;
}

export interface RangeChart {
  /** Rows, rendered top→bottom in given order (sort upstream in data). */
  rows: RangeRow[];
  /** Domain max for the plot x-axis (e.g. 100 for %). */
  domainMax: number;
  /** Grid tick values along x (e.g. [0,25,50,75,100]). */
  gridTicks: number[];
  /** Axis unit suffix on tick labels (e.g. "%"). */
  unit: string;
}

// ─── DonutChart config (donut archetype) ──────────────────────────────────────

export interface DonutSlice {
  label: string;
  value: number;
  /** CSS color var for the slice (e.g. "var(--c3)"). */
  color: string;
  /** Pre-formatted percent label for the legend (e.g. "73%"). */
  pct: string;
}

export interface DonutChart {
  slices: DonutSlice[];
  centerValue: string;
  centerLabel: string;
}

// ─── NumberPlateChart config (number-plate archetype) ─────────────────────────

export interface NumberPlateCompare {
  beforeLabel: string;
  beforeValue: number;
  afterLabel: string;
  afterValue: number;
  /** Optional caption beneath the bars, e.g. "REGISTERED USERS". */
  caption?: string;
}

export interface NumberPlateChart {
  /** Giant display value, e.g. "39×". */
  value: string;
  /** Sublabel under the number, e.g. "100K → 4M USERS · 15 MONTHS". */
  sublabel: string;
  /** Proportional before/after bars filling the canvas to the right of the number. */
  compare?: NumberPlateCompare;
  /** Optional restrained right-rail note (used only when `compare` is absent). */
  note?: string;
}

// ─── VerticalBarChart config (vertical-bar archetype) ─────────────────────────

export interface VerticalBar {
  /** Below-axis label, e.g. "2023". */
  label: string;
  /** Numeric value driving bar height. */
  value: number;
  /** Pre-formatted value label atop the bar, e.g. "88M". */
  display: string;
}

export interface VerticalBarChart {
  /** Bars, rendered left→right in given order. */
  bars: VerticalBar[];
  /** Domain max for the y-axis (e.g. 100). */
  domainMax: number;
  /** Grid tick values along y (e.g. [0,25,50,75,100]). */
  gridTicks: number[];
  /** Axis unit suffix on y tick labels (e.g. "M"). */
  unit: string;
  /** Muted axis caption beneath the bars, e.g. "YEAR-END (Q4) MAU". */
  yAxisLabel: string;
}

// ─── HeatmapChart config (heatmap archetype) ──────────────────────────────────

export interface HeatmapCell {
  /** Range/value text centered in the cell, e.g. "50–70%". */
  label: string;
  /** Midpoint value driving cell opacity (opacity = value / maxValue). */
  value: number;
}

export interface HeatmapChart {
  /** Row labels (left gutter), top→bottom. */
  rows: string[];
  /** Column labels (top headers), left→right. */
  cols: string[];
  /** Cell grid indexed [row][col]; dims must match rows × cols. */
  cells: HeatmapCell[][];
  /** CSS color var for cell fill (e.g. "var(--c3)"). */
  colorBase: string;
  /** Value mapped to full opacity; per-cell opacity = value / maxValue. */
  maxValue: number;
}

// ─── GanttChart config (gantt archetype) ──────────────────────────────────────

export interface GanttItem {
  /** Row label in the left gutter, e.g. "Free 2-day shipping". */
  label: string;
  /** Start year; bar spans start → right edge of the domain. */
  start: number;
}

export interface GanttChart {
  /** Bars, rendered top→bottom in given order. */
  items: GanttItem[];
  /** Left edge of the time domain (e.g. 2004). */
  domainStart: number;
  /** Right edge of the time domain (e.g. 2025). */
  domainEnd: number;
  /** Year for the gold "NOW" rule (e.g. 2025). */
  nowYear: number;
  /** Year tick values along the x-axis (e.g. [2005,2010,2015,2020,2025]). */
  axisTicks: number[];
}

// ─── WaffleChart config (waffle archetype) ────────────────────────────────────

export interface WaffleChart {
  /** Number of filled (semantic) cells, e.g. 4 survivors. */
  value: number;
  /** Total cells in the grid (e.g. 100 for a 10×10). */
  total: number;
  /** CSS color var for the filled cells (e.g. "var(--c1)"). */
  filledColor: string;
  /** CSS color var for the muted cells (e.g. "var(--rule-3)"). */
  mutedColor: string;
  /** Right-side caption beneath the big fraction, e.g. "players gone by day 7". */
  label: string;
  /**
   * Which count the big fraction shows: "filled" (the value cells, default —
   * e.g. "77 from referrals") or "empty" (the complement total−value — e.g.
   * game retention's "96 gone").
   */
  emphasis?: "filled" | "empty";
}

// ─── SmallMultiplesChart config (small-multiples archetype) ───────────────────

export interface SmallMultiplePanel {
  /** Panel title (segment), e.g. "DTC". */
  label: string;
  /** Numeric value driving bar length. */
  value: number;
  /** Pre-formatted value label, e.g. "≤ 6 MO". */
  display: string;
}

export interface SmallMultiplesChart {
  /** Panels, rendered left→right in given order. */
  panels: SmallMultiplePanel[];
  /** Shared domain max across every panel (e.g. 24). */
  domainMax: number;
  /** Axis unit suffix (e.g. "MO"). */
  unit: string;
  /** Shared reference gridline value across panels (e.g. 12). */
  refTick: number;
}

// ─── ProportionalAreaChart config (proportional-area archetype) ───────────────

export interface ProportionalAreaBubble {
  /** Name label above the circle, e.g. "Chime". */
  label: string;
  /** Numeric value; circle area ∝ value (radius ∝ √value). */
  value: number;
  /** Pre-formatted value label, e.g. "$214". */
  display: string;
  /** When true, render muted (the incumbent / old guard). */
  muted?: boolean;
}

export interface ProportionalAreaChart {
  /** Circles, rendered left→right in given order. */
  bubbles: ProportionalAreaBubble[];
  /** Unit caption (e.g. "ARPU / USER / YEAR"). */
  unit: string;
}

// ─── ParliamentChart config (parliament archetype) ────────────────────────────

export interface ParliamentFaction {
  /** Legend label, e.g. "Direct". */
  label: string;
  /** Seat count for this faction (sums across factions ≈ totalSeats). */
  value: number;
  /** CSS color var for this faction's seats (e.g. "var(--c4)"). */
  color: string;
  /** Pre-formatted percent label for the legend (e.g. "54%"). */
  pct: string;
}

export interface ParliamentChart {
  /** Factions, seated in given order (left→right around the arc). */
  factions: ParliamentFaction[];
  /** Total seats laid out in the semicircle (e.g. 100). */
  totalSeats: number;
}

// ─── VennChart config (venn archetype — FRAMEWORK) ────────────────────────────

export interface VennSet {
  /** Set label placed outside its circle, e.g. "Habit". */
  label: string;
  /** CSS color var for this circle's stroke + tinted fill (e.g. "var(--c4)"). */
  color: string;
}

export interface VennChart {
  /** Exactly three sets, arranged in a triangular overlap. */
  sets: [VennSet, VennSet, VennSet];
  /** Gold center label naming the moat, e.g. "RETENTION MOAT". */
  centerLabel: string;
}

// ─── MatrixChart config (2×2 matrix archetype — FRAMEWORK) ────────────────────

export interface MatrixAxis {
  /** Axis name, e.g. "CAC payback". */
  label: string;
  /** Low-end pole label (left for X, bottom for Y). */
  lowLabel: string;
  /** High-end pole label (right for X, top for Y). */
  highLabel: string;
}

export interface MatrixDot {
  /** Company / position label beside the dot. */
  label: string;
  /** Normalized x in 0–100 (0 = left, 100 = right). */
  x: number;
  /** Normalized y in 0–100 (0 = bottom, 100 = top). */
  y: number;
}

export interface MatrixChart {
  /** Horizontal axis (left = lowLabel, right = highLabel). */
  xAxis: MatrixAxis;
  /** Vertical axis (bottom = lowLabel, top = highLabel). */
  yAxis: MatrixAxis;
  /** Quadrant labels: [top-left, top-right, bottom-left, bottom-right]. */
  quadrants: [string, string, string, string];
  /** Plotted positions (editorial/directional), gold dots. */
  dots: MatrixDot[];
}
```

---
Omega Point · https://omegapoint.systems · generated from the repository at build time.
