> ## Documentation Index
> Fetch the complete documentation index at: https://docs.funeli.top/llms.txt
> Use this file to discover all available pages before exploring further.

# Components: results & personalisation

> Reference for the Funeli results & personalisation components: what each one is for, every setting with its type and default, and a working example.

Show the visitor what their answers mean. Add these components to a step in the editor and change their settings in the inspector, or ask an agent to; the settings below are the same in both.

## Projection chart

Type `ProjectionChart`. Charts progress toward a target. Smooth curve between a start and a target value (both usually placeholders from earlier answers, e.g. weight); rises or falls with the target. Static; read out as "From X to Y".

| Setting | Type | Required | Description |
| - | - | - | - |
| `start` | string | yes | Starting value: a number or a placeholder like "\{\{weight\|80}}"; non-numeric text is drawn as 0 |
| `target` | string | yes | Goal value: a number or placeholder like "\{\{target\_weight\|72}}"; non-numeric text falls back to the start value. The curve rises if target > start, falls otherwise |
| `unit` | string | no | Unit label printed after both values (aim \<=6 chars), or a placeholder like "\{\{weight\_unit}}" Default: `"kg"`. |
| `startLabel` | string | no | Axis label under the start point, aim \<=12 chars; placeholders ok Default: `"Today"`. |
| `endLabel` | string | no | Axis label under the target point, aim \<=12 chars; \{\{date:+N}} = N days from today Default: `"{{date:+28}}"`. |
| `caption` | string | no | Small centred note under the chart, keep \<=70 chars, e.g. "Based on 50,000+ users like you" |

> [accordion] Example
>
> ```json theme={null}
> {
>   "start": "{{weight|78}}",
>   "target": "{{target_weight|66}}",
>   "unit": "{{weight_unit|kg}}",
>   "startLabel": "Today",
>   "endLabel": "{{date:+28}}",
>   "caption": "Your projected path based on the answers you gave us"
> }
> ```

## Metric scale

Type `MetricScale`. Puts a metric on a labelled scale. Banded bar with a marker at value and the active band named top right (e.g. BMI). Last band is open-ended; non-numeric value hides marker and band name. Feed value from set\_computed; keep band labels \<=12 chars.

| Setting | Type | Required | Description |
| - | - | - | - |
| `title` | string | no | Label top-left, keep \<=24 chars, e.g. "Your BMI" |
| `value` | string | yes | The measured number or a placeholder resolved at render time, e.g. "\{\{bmi\|24}}" (feed it from set\_computed). Non-numeric hides the marker and band name; out-of-range values pin the marker to the nearest end |
| `unit` | string | no | Unit after the value, e.g. "kg"; literal Default: `""`. |
| `min` | number | no | Axis value at the left end Default: `15`. |
| `max` | number | no | Axis value at the right end; must exceed min Default: `40`. |
| `bands` | object\[] | yes | 2-5 bands ordered low to high; segment widths follow the bounds. Colour is never the only cue: each band is also labelled |
| `caption` | string | no | Small note under the scale, keep \<=70 chars, e.g. where the number comes from |

> [accordion] Example
>
> ```json theme={null}
> {
>   "title": "Your BMI",
>   "value": "{{bmi|27.4}}",
>   "min": 15,
>   "max": 40,
>   "bands": [
>     {
>       "below": 18.5,
>       "label": "Underweight",
>       "tone": "primary"
>     },
>     {
>       "below": 25,
>       "label": "Normal",
>       "tone": "success"
>     },
>     {
>       "below": 30,
>       "label": "Overweight",
>       "tone": "warning"
>     },
>     {
>       "label": "Obese",
>       "tone": "danger"
>     }
>   ],
>   "caption": "Calculated from your height and weight"
> }
> ```

## Profile summary

Type `ProfileSummary`. Recaps the visitor's answers. Label/value rows (1-8) filled by placeholders, each value shown with a leading capital; locked rows blur the value behind a lock as a teaser before the paywall (visual only, text stays in the page). Pairs with LockedPreview. Static.

| Setting | Type | Required | Description |
| - | - | - | - |
| `title` | string | no | Card heading, keep \<=30 chars Default: `"Your profile"`. |
| `rows` | object\[] | yes | 1-8 label/value rows |

> [accordion] Example
>
> ```json theme={null}
> {
>   "title": "Your profile",
>   "rows": [
>     {
>       "icon": "target",
>       "label": "Goal",
>       "value": "{{goal|Build strength}}"
>     },
>     {
>       "icon": "scale",
>       "label": "Weight",
>       "value": "{{weight|78}} {{weight_unit|kg}}"
>     },
>     {
>       "icon": "ruler",
>       "label": "Height",
>       "value": "{{height|168}} cm"
>     },
>     {
>       "icon": "clock",
>       "label": "Time per day",
>       "value": "{{time|20 min}}"
>     },
>     {
>       "icon": "activity",
>       "label": "Starting level",
>       "value": "Moderate"
>     },
>     {
>       "icon": "calendar-check",
>       "label": "Plan length",
>       "value": "28 days"
>     }
>   ]
> }
> ```

## Locked preview

Type `LockedPreview`. Blurred teaser of a result, unlocked after the paywall. Decorative blurred photo or skeleton lines with a lock and a caption; variant image requires image. Place before the paywall; unlocks nothing by itself.

| Setting | Type | Required | Description |
| - | - | - | - |
| `variant` | "image" \| "lines" | no | image = a blurred photo (needs image); lines = blurred skeleton text rows Default: `"lines"`. |
| `image` | string | no | Photo shown blurred under a lock (decorative, no alt); required when variant is image |
| `lines` | integer | no | Number of blurred skeleton lines; ignored when variant is image Default: `3`. |
| `caption` | string | no | Text under the preview, keep \<=40 chars; it carries the meaning since the preview is decorative Default: `"Unlock your full report"`. |

> [accordion] Example
>
> ```json theme={null}
> {
>   "variant": "lines",
>   "lines": 5,
>   "caption": "Unlock your full personalised report"
> }
> ```

## Feature list

Type `FeatureList`. Lists benefits at a glance. 1-10 rows in four real layouts (list, stacked, carousel, row); carousel is swipeable, loops and is keyboard-focusable; row ignores marker and item text. Static, writes no answer; keep titles \<=40 chars.

| Setting | Type | Required | Description |
| - | - | - | - |
| `title` | string | no | Heading above the list, keep \<=40 chars |
| `items` | string \| object\[] | yes | 1-10 rows: plain strings for a checklist, or \{title, text, icon} objects for rows with their own icon and a description; mixing is allowed |
| `layout` | "list" \| "stacked" \| "carousel" \| "row" \| "cards" \| "grid" | no | list = compact checklist (3-10 short rows). stacked = large full-width benefit cards (2-5 items). carousel = swipeable, looping cards with the next one peeking (3-6 items). row = one strip of short icon+label items, e.g. "Encrypted · Anonymous · Instant" (2-4 items). cards and grid are legacy aliases kept for older drafts: do not use them for new work Default: `"list"`. |
| `marker` | "check" \| "dot" \| "number" | no | Bullet for rows without their own icon: check, dot, or 1-based number (use number for ordered steps); ignored in the row layout Default: `"check"`. |

> [accordion] Example
>
> ```json theme={null}
> {
>   "title": "Your plan includes",
>   "layout": "list",
>   "marker": "check",
>   "items": [
>     {
>       "title": "Daily sessions",
>       "text": "Short routines matched to your available time.",
>       "icon": "timer"
>     },
>     {
>       "title": "Progress tracking",
>       "text": "See milestones and weekly changes at a glance.",
>       "icon": "trending-up"
>     },
>     {
>       "title": "Smart adjustments",
>       "text": "Recommendations adapt as your goals change.",
>       "icon": "sliders"
>     },
>     {
>       "title": "Practical guidance",
>       "text": "Simple next actions instead of generic advice.",
>       "icon": "lightbulb"
>     }
>   ]
> }
> ```

## Info card

Type `InfoCard`. Highlights a useful fact or tip. Tinted callout with optional icon and bold title; supports links. Static; one per screen as a micro-reward or note.

| Setting | Type | Required | Description |
| - | - | - | - |
| `icon` | "activity" \| "alarm" \| "apple" \| "award" \| "baby" \| "battery" \| "bed" \| "bike" \| "brain" \| "calendar" \| "check" \| "chart" \| "clock" \| "coffee" \| "dumbbell" \| "flame" \| "flower" \| "footprints" \| "gift" \| "heart" \| "leaf" \| "lightning" \| "lock" \| "moon" \| "mountain" \| "music" \| "person" \| "ruler" \| "salad" \| "scale" \| "shield" \| "smile" \| "sparkles" \| "star" \| "sun" \| "target" \| "thumbs-up" \| "timer" \| "trophy" \| "upload" \| "users" \| "water" \| "yoga" \| "x" \| "zap" \| "heart-pulse" \| "stethoscope" \| "pill" \| "syringe" \| "thermometer" \| "weight" \| "hand-heart" \| "eye" \| "ear" \| "bone" \| "carrot" \| "cherry" \| "cookie" \| "pizza" \| "sandwich" \| "egg" \| "fish" \| "beef" \| "wheat" \| "cup-soda" \| "wine" \| "candy" \| "soup" \| "milk" \| "waves" \| "medal" \| "flag" \| "goal" \| "bed-double" \| "cloud-moon" \| "sunrise" \| "sunset" \| "wind" \| "feather" \| "brain-circuit" \| "book-open" \| "headphones" \| "glasses" \| "home" \| "briefcase" \| "car" \| "plane" \| "map-pin" \| "globe" \| "dog" \| "cat" \| "shopping-bag" \| "wallet" \| "credit-card" \| "piggy-bank" \| "gamepad" \| "tv" \| "smartphone" \| "laptop" \| "camera" \| "palette" \| "graduation-cap" \| "smile-plus" \| "frown" \| "meh" \| "laugh" \| "angry" \| "heart-crack" \| "party-popper" \| "check-circle" \| "x-circle" \| "alert-triangle" \| "info" \| "help-circle" \| "plus" \| "minus" \| "arrow-right" \| "trending-up" \| "trending-down" \| "bar-chart" \| "pie-chart" \| "percent" \| "tag" \| "badge-check" \| "crown" \| "gem" \| "rocket" \| "key" \| "bell" \| "mail" \| "phone" \| "message-circle" \| "search" \| "settings" \| "sliders" \| "refresh" \| "infinity" \| "hourglass" \| "calendar-check" \| "list-checks" \| "clipboard" \| "file-text" \| "lightbulb" \| "puzzle" \| "magnet" \| "anchor" \| "user-check" \| "user-plus" \| "users-round" \| "handshake" \| "thumbs-down" \| "star-half" | no | Leading icon tinted to match tone |
| `title` | string | no | Bold first line, keep \<=40 chars; omit for a one-line note |
| `text` | string | yes | Body, keep \<=200 chars. Supports [label](https://...) links |
| `tone` | "neutral" \| "primary" \| "success" \| "warning" | no | Tint: neutral grey, primary brand, success green, warning = accent colour (not red) Default: `"neutral"`. |
| `preset` | "info" \| "warn" \| "danger" \| "success" | no | Semantic preset: tint, left accent bar, default icon and colours come from the theme (info = primary, warn = accent, danger, success). Overrides: icon, accentBar:false, or appearance colour tokens. Takes precedence over tone |
| `accentBar` | boolean | no | With a preset: show the left accent bar (default true) |

> [accordion] Example
>
> ```json theme={null}
> {
>   "icon": "lightbulb",
>   "title": "Good to know",
>   "text": "Your recommendations update as your answers become more specific.",
>   "tone": "primary"
> }
> ```

## Media info row

Type `MediaInfoRow`. Shows a compact image with a small label and status title. The image can come from a FileUpload answer via imageField or from a normal URL; optional status dot and scanning-line motion suit face/photo analysis states. Static; writes no answer.

| Setting | Type | Required | Description |
| - | - | - | - |
| `image` | string | no | Static image URL or fallback image. When imageField contains a non-empty URL answer, that uploaded/runtime image takes precedence |
| `imageField` | string | no | Optional answer field containing an image URL, typically the field written by FileUpload (for example photo\_url). The answer takes precedence over image |
| `alt` | string | no | What the image shows for screen readers, \<=200 chars. Empty marks the image decorative; use a meaningful alt when the photo itself carries information Default: `""`. |
| `imageFit` | "cover" \| "contain" | no | How the image fits its square thumbnail: cover fills and may crop; contain shows the full image on the theme background Default: `"cover"`. |
| `label` | string | no | Small uppercase label above the title, keep \<=30 chars, e.g. "ANALYZING PHOTO" |
| `title` | string | yes | Main row title, keep \<=50 chars, e.g. "Face detected" |
| `statusDot` | "none" \| "success" \| "primary" \| "danger" | no | Optional dot beside the title: success green, primary brand colour, danger red, or none. The title must carry the meaning because colour is decorative Default: `"none"`. |
| `scanLine` | boolean | no | Animate a theme-coloured scanning shimmer vertically across the thumbnail. Motion is removed when the visitor prefers reduced motion Default: `false`. |
| `hideIfMissing` | boolean | no | Hide the whole row when neither imageField nor image resolves to an image; false keeps a neutral placeholder thumbnail Default: `false`. |

> [accordion] Example
>
> ```json theme={null}
> {
>   "imageField": "photo_url",
>   "image": "https://images.unsplash.com/photo-1494790108377-be9c29b29330?auto=format&fit=crop&w=320&q=80",
>   "alt": "Uploaded portrait",
>   "label": "ANALYZING PHOTO",
>   "title": "Face detected",
>   "statusDot": "success",
>   "scanLine": true
> }
> ```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.