# FunnelChart

> A vertical column funnel with a summary panel.

## Facts

- **Product**: Max
- **Family**: Analytics
- **Global**: `window.DigitalCrew.FunnelChart`
- **Source**: `max-agent: src/features/analytics/ui/funnel-chart.tsx`
- **Import in the app**: `import { FunnelChart } from "@/features/analytics/ui/funnel-chart";`
- **Live preview**: /design/components/funnel-chart/preview.html
- **Page**: /design/components/funnel-chart

## Guidelines

Each stage shows its count above a bar, a soft area joins the bar tops, numbered markers sit on a connector, and each label carries its conversion to the next stage. The panel shows the overall and per-step conversion.

**Use it for** acquisition and campaign funnels. In Max the funnel runs Leads enrolled → Engaged (opens + connections) → Replied (email + LinkedIn + WhatsApp) → Meetings booked. The grid adapts to any number of stages.

**What you provide**: `title`, optional `subtitle`, `isLoading`, and `stages: AnalyticsFunnelStageDto[]`. Each stage is `{ key, label, count, pct_of_first, pct_of_previous }`, with percentages from 0 to 100. The chart prints them as `${value}%`, so pass them already rounded; the server rounds to one decimal.

**Anatomy**: the panel is `glass-panel border-glass rounded-3xl px-6 py-6` and the whole chart uses one hue, `var(--chart-2)`. The bar zone is 176px tall. Bars are `max-w-14` (`sm:max-w-16`) and `rounded-t-md`. A non-zero stage keeps at least 4% height; a true zero draws nothing. The joining area is filled at 10% opacity. Markers are 28px circles in `border-glass bg-background`. The summary aside is `rounded-2xl bg-muted/40`, `lg:w-60`, and sits beside the chart from `lg`.

**States**: while loading, a 4-column skeleton. When every count is 0: "No funnel activity in the selected period".

**Don't** count activity rows or pass non-monotonic stages. Count distinct leads and fold each stage into the one before it, so no conversion exceeds 100%.

## API

```ts
/**
 * Vertical column funnel: one column per stage with the count above a
 * baseline-anchored bar, a soft area joining stage tops, numbered step
 * markers on a connector line, and stage-over-stage conversion beneath.
 * A summary panel carries the overall + per-step conversion rates.
 */
export declare function FunnelChart({ title, subtitle, stages, isLoading, }: {
    title: string;
    subtitle?: string;
    stages: AnalyticsFunnelStageDto[];
    isLoading: boolean;
}): JSX.Element;
```

## Example

```html
<div id="root" class="p-6"></div>
<script>
(function () {
  var h = React.createElement, DC = window.DigitalCrew, I = DC.Icons;
  // AnalyticsFunnelStageDto[]: distinct leads per stage, monotonic, with the
  // same rounding as core/acquisition-funnel.ts (one decimal place).
  function pct(n, d) { return d === 0 ? 0 : Math.round((n / d) * 1000) / 10; }
  var RAW = [
    { key: "contacted", label: "Leads enrolled", count: 1240 },
    { key: "engaged", label: "Engaged (opens + connections)", count: 684 },
    { key: "replied", label: "Replied (email + LinkedIn + WhatsApp)", count: 173 },
    { key: "meetings", label: "Meetings booked", count: 41 },
    { key: "deals", label: "Deals created", count: 15 }
  ];
  var STAGES = RAW.map(function (s, i) {
    return {
      key: s.key, label: s.label, count: s.count,
      pct_of_first: pct(s.count, RAW[0].count),
      pct_of_previous: pct(s.count, i === 0 ? RAW[0].count : RAW[i - 1].count)
    };
  });
  function App() {
    return h(DC.FunnelChart, {
      title: "Acquisition funnel",
      subtitle: "Q3 outbound · from first touch to deal created",
      stages: STAGES,
      isLoading: false
    });
  }
  ReactDOM.createRoot(document.getElementById("root")).render(h(DC.TooltipProvider, { delayDuration: 80 }, h(App)));
})();
</script>
```

## More in Analytics

- [ActivityChart](/design/components/activity-chart.md): A day-bucketed Recharts chart in Max's glass panel: each series is drawn as an area, a line or a bar, with a dot legend and a popover tooltip.
- [ChartContainer](/design/components/chart-container.md): The shell every Crew OS chart sits in: it wraps a Recharts 2 chart in a `ResponsiveContainer`, turns a config object into per-series colour variables and styles axes, grid, tooltip and legend with the theme tokens.
- [InsightsCard](/design/components/insights-card.md): A meeting's numbers (sentiment, stat tiles, per-speaker talk share, an over-talk hint), all derived on the client from the transcript.
- [QualificationContent](/design/components/qualification-content.md): The body of a BANT qualification snapshot: a score, a verdict, a confirmed count, and one tile per criterion with its status, rationale and first piece of transcript evidence.
