# Registry Docs

A portable export of the Circle Health UI registry documentation. Interactive components are available in the bundled PDF and on the live site.

## Contents

- [Overview](#page-home)
- [Install](#page-getting-started)
- [Concepts](#page-concepts)
- [Technical setup](#page-technical-setup)
- [Themes](#page-themes)
- [Skills](#page-skills)
- [Copy Review](#page-skills-copy-review)
- [Link Check](#page-skills-link-check)
- [Changelog](#page-changelog)
- [AGENTS.MD](#page-agents)
- [Components](#page-components)
- [Alert](#page-components-actions-status-alert)
- [Badge](#page-components-actions-status-badge)
- [Button](#page-components-actions-status-button)
- [Progress Bar](#page-components-actions-status-progress-bar)
- [Rating](#page-components-actions-status-rating)
- [Spinner](#page-components-actions-status-spinner)
- [Tag](#page-components-actions-status-tag)
- [Checkbox](#page-components-forms-checkbox)
- [Date Picker](#page-components-forms-date-picker)
- [Form Fields](#page-components-forms-form)
- [Switch](#page-components-forms-switch)
- [Back Button](#page-components-navigation-overlays-back-button)
- [Link](#page-components-navigation-overlays-link)
- [Overlay Action](#page-components-navigation-overlays-overlay-action)
- [Sheet](#page-components-navigation-overlays-sheet)
- [Tabs](#page-components-navigation-overlays-tabs)
- [Tooltip](#page-components-navigation-overlays-tooltip)
- [Card](#page-components-layout-content-card)
- [Container](#page-components-layout-content-container)
- [Empty](#page-components-layout-content-empty)
- [Text](#page-components-layout-content-text)
- [Text With Icon](#page-components-layout-content-text-with-icon)
- [Timeline](#page-components-layout-content-timeline)
- [Blocks](#page-blocks)
- [Flow](#page-blocks-flows-flow)
- [Floating Flow Actions](#page-blocks-flows-floating-flow-actions)
- [Top Bar](#page-blocks-flows-top-bar)
- [Fillout Flow](#page-blocks-flows-fillout-flow)
- [Commerce](#page-blocks-commerce)
- [Booking Timeline](#page-blocks-booking-timeline)
- [Media](#page-blocks-media-support-media)
- [Map](#page-blocks-media-support-map)
- [Address](#page-blocks-media-support-address)
- [Support](#page-blocks-media-support-support)
- [Cookie Consent](#page-blocks-cookie-consent)
- [Metric Donut Card](#page-blocks-reporting-metric-donut-card)
- [Metric Range Card](#page-blocks-reporting-metric-range-card)
- [Metric Split Card](#page-blocks-reporting-metric-split-card)
- [Metric Stat Card](#page-blocks-reporting-metric-stat-card)
- [Text Columns Card](#page-blocks-reporting-text-columns-card)
- [Segment Muscle Analysis](#page-blocks-reporting-segment-muscle-analysis)
- [Patient Results Editor](#page-blocks-patient-results-patient-results-editor)
- [Patient Results Report](#page-blocks-patient-results-patient-results-report)
- [Patient Results PDF](#page-blocks-patient-results-patient-results-pdf)
- [Patient Results Suite](#page-blocks-patient-results-patient-results-blocks)
- [Utilities](#page-utilities)
- [Nextra Export](#page-utilities-nextra-export)
- [Analytics](#page-utilities-analytics)
- [Circle Mixpanel Config](#page-utilities-circle-mixpanel-config)
- [CircleOS PII Mask](#page-utilities-pii-mask)
- [Lab Reports](#page-sample-data)
- [Intake Forms](#page-sample-data-intake-forms)
- [Overview](#page-sample-data-brands-overview)
- [Praxis Kessler](#page-sample-data-brands-praxis-kessler)
- [Decades](#page-sample-data-brands-decades)
- [Soma Studio](#page-sample-data-brands-soma-studio)

---

<a id="page-home"></a>

<!-- source: / -->

# Registry

The Circle Health Registry serves installable components for Circle Health
products through the [shadcn CLI](https://ui.shadcn.com/docs/cli). Consumers
copy components into their own codebase, own the generated files, and can edit
them locally.

The Registry has four layers. Components are small primitives. Blocks combine
them into product modules. Utilities provide behavior without a UI workflow.
Themes provide the tokens that style all three.

## Where to go

| Page                        | Use it for                                                                |
| --------------------------- | ------------------------------------------------------------------------- |
| [Install](#page-getting-started) | Configure a consumer and install its first item.                          |
| [Concepts](#page-concepts)       | Registry ownership, namespaces, dependencies, and generated output.       |
| [Components](#page-components)   | Actions, form controls, navigation, layout, and content primitives.       |
| [Blocks](#page-blocks)           | Flows, commerce, booking, media, reporting, and patient-results patterns. |
| [Utilities](#page-utilities)     | Analytics helpers and browser tools with no UI workflow attached.         |
| [Themes](#page-themes)           | The token sets available to Circle Health surfaces.                       |
| [Sample Data](#page-sample-data) | Synthetic PDFs and brands for development and demos.                      |
| [Skills](#page-skills)           | Copy-review and link-check skills.                                        |

## Source of truth

Authored [React](https://react.dev/) source lives under `packages/ui` and
`registry/`. The registry build writes
[shadcn-compatible](https://ui.shadcn.com/docs/registry) JSON to
`apps/docs/public`. Do not hand-edit generated registry JSON.

The documentation and public payloads ship from the same host:
`registry.circle.health`.



[View the live page](https://registry.circle.health/)

---

<a id="page-getting-started"></a>

<!-- source: /getting-started -->

# Install

The Circle Health Registry distributes source through the
[shadcn CLI](https://ui.shadcn.com/docs/cli). The CLI downloads source into the
consumer; it does not add a runtime package.

## 1. Start with a [shadcn project](https://ui.shadcn.com/docs/installation)

The consumer needs a valid `components.json`. If it does not have one yet, run:

```bash
pnpm dlx shadcn@latest init
```

## 2. Add the registries

Add the Circle Health namespaces to `components.json`:

```json
{
  "$schema": "https://ui.shadcn.com/schema.json",
  "registries": {
    "@circle-ui": "https://registry.circle.health/circle-ui/{name}.json",
    "@blocks": "https://registry.circle.health/blocks/{name}.json",
    "@utilities": "https://registry.circle.health/utilities/{name}.json",
    "@i18n": "https://registry.circle.health/i18n/{name}.json",
    "@skills": "https://registry.circle.health/skills/{name}.json"
  }
}
```

`@circle-ui` contains primitives. `@blocks` contains product modules.
`@utilities` contains behavior with no UI workflow attached.
`@i18n` contains optional locale dictionaries referenced by those modules.
`@skills` contains agent workflows installed under `.agents/skills`.

## 3. Install the base style

Install the shared tokens and defaults once per consumer:

```bash
pnpm dlx shadcn@latest add @circle-ui/circle
```

## 4. Add what the product needs

Install items by namespace and name:

```bash
pnpm dlx shadcn@latest add @circle-ui/button
pnpm dlx shadcn@latest add @blocks/booking-timeline
pnpm dlx shadcn@latest add @utilities/analytics
```

The CLI resolves registry dependencies and writes the files into the consumer.
Review those files like any other source change.

## Direct URL installation

When a consumer cannot edit `components.json`, install from the public JSON
URL:

```bash
pnpm dlx shadcn@latest add https://registry.circle.health/circle-ui/button.json
```

## Next

Use [Components](#page-components) for primitives, [Blocks](#page-blocks) for assembled
product patterns, [Utilities](#page-utilities) for behavior-only helpers, and
[Concepts](#page-concepts) for the ownership and dependency model.



[View the live page](https://registry.circle.health/getting-started)

---

<a id="page-concepts"></a>

<!-- source: /concepts -->

# Concepts

## Installed source

The Circle Health Registry distributes source rather than a runtime component
package. Installing an item copies files into the consumer. The consumer then
owns those files and can change them without waiting for a central package
release.

Reinstalling an item may replace local changes. Inspect the diff before
accepting an update.

## Components

[Components](#page-components) are low-level UI with no product workflow attached.
Each registry entry maps to one source module and uses the `@circle-ui`
namespace.

## Blocks

[Blocks](#page-blocks) are product modules assembled from components. A block may
pull in private hooks, types, or serialization helpers, but those implementation
files are not separate catalog choices.

Install one focused block for one product capability. Use an umbrella entry
only when the capability is intentionally shipped as a coordinated family.

## Utilities

[Utilities](#page-utilities) provide behavior without a visual primitive or product
workflow. They use the `@utilities` namespace so consumers can distinguish
instrumentation and browser helpers from UI blocks.

## Localized copy

Optional locale dictionaries use the `@i18n` namespace. They stay separate from
UI source so consumers own translation loading and locale boundaries. The
owning block documents the relevant dictionary.

## Themes

[Themes](#page-themes) are CSS-variable sets. Components and blocks refer to those
variables rather than hard-coded product colors, so a consumer can change a
surface without rewriting component logic.

## Dependencies

Each registry item declares two dependency kinds:

| Kind                                      | Resolution                                                                       |
| ----------------------------------------- | -------------------------------------------------------------------------------- |
| Registry dependency                       | The [shadcn CLI](https://ui.shadcn.com/docs/cli) installs another Registry item. |
| [npm](https://docs.npmjs.com/) dependency | The consumer's package manager installs a third-party package.                   |

Registry dependencies use their full namespace in generated JSON. The docs
show both dependency kinds on every reference page.

## Authoring and generated output

Authoring source lives in `packages/ui`, `packages/styles`, and `registry`.
Configuration lives in `config/registry`. `pnpm registry:build` produces the
public JSON under `apps/docs/public`.

Treat generated JSON as build output. Change the authoring source or registry
configuration, rebuild, and verify the generated diff.



[View the live page](https://registry.circle.health/concepts)

---

<a id="page-technical-setup"></a>

<!-- source: /technical-setup -->

# Technical setup

This repository is a [pnpm](https://pnpm.io/) workspace managed by
[Turborepo](https://turbo.build/repo). It builds two surfaces from the same
source: the Nextra documentation site and the shadcn-compatible JSON consumed by
Circle Health applications.

The workspace uses pnpm, Turborepo, TypeScript, Next.js, React, Nextra, Tailwind CSS, and shadcn.

## System boundary

The Registry distributes source, not a runtime UI package. `@circle/ui` is the
private authoring package inside this workspace. Consumers use the
[shadcn CLI](https://ui.shadcn.com/docs/cli), receive files in their own source
tree, and own those files from then on.

| Boundary      | Responsibility                                                         |
| ------------- | ---------------------------------------------------------------------- |
| Authoring     | React modules, CSS tokens, localized copy, skills, and item metadata.  |
| Registry      | Import rewriting, dependency discovery, targets, and item manifests.   |
| Documentation | Catalog navigation, previews, usage, sample data, and public payloads. |
| Consumer      | Installed source, application integration, and later local changes.    |

## Build pipeline

1. Author source and configuration.
2. Compile registry manifests.
3. Publish static JSON payloads.
4. Install application-owned source.

### 1. Author

The durable sources are:

- `packages/ui/src` for components, blocks, utilities, types, and localized
  copy
- `packages/styles/src/globals.css` for base CSS variables and typography
- `skills` for installable agent workflows
- `config/registry/items.json` for item metadata and target overrides
- `config/registry/styles.json` for theme variants
- `config/registry/site.json` for namespaces, aliases, and the public host
- `config/docs/registry.json` for catalog grouping and navigation

Every root component must have one explicit registry item. An entry exported
from `packages/ui/src/index.ts` can also receive a live documentation preview.

### 2. Compile

`pnpm registry:sync` runs `scripts/registry/build.mjs`. The script:

1. reads authored source and registry configuration;
2. maps each source module to one registry item;
3. rewrites local imports to namespaced registry paths;
4. derives npm and registry dependencies from imports;
5. writes transformed source to `registry/berlin`; and
6. writes manifests and catalog metadata to `config/registry/generated` and
   `registry.json`.

Explicit dependencies and file targets in `items.json` supplement values that
cannot be derived from imports.

### 3. Publish

`pnpm registry:build` performs the registry sync, then runs `shadcn build` once
per namespace. The resulting JSON is written below `apps/docs/public`:

```text
/circle-ui/{name}.json
/blocks/{name}.json
/utilities/{name}.json
/i18n/{name}.json
/skills/{name}.json
```

The documentation application and these static payloads are served from
`registry.circle.health`.

### 4. Generate documentation

`pnpm docs:sync` combines generated item metadata with docs configuration. It
produces catalog pages, sidebar maps, skill pages, the CircleOS PII-mask page,
and the rendered changelog.

Handwritten pages such as this one explain durable concepts. Item reference
pages are generated so their install commands and dependencies stay aligned
with the public payload. An item listed in its namespace's `authoredItems`
configuration keeps its handwritten page while still using live
`RegistryItem` sections for installation and dependency data.

## Workspace map

| Path                  | Owns                                                           |
| --------------------- | -------------------------------------------------------------- |
| `packages/ui`         | Installable React and TypeScript source.                       |
| `packages/styles`     | Base CSS and Circle Health design tokens.                      |
| `config/registry`     | Registry definitions plus generated manifests.                 |
| `config/docs`         | Catalog information architecture.                              |
| `registry/berlin`     | Transformed source passed to the shadcn builder.               |
| `apps/docs/content`   | Handwritten and generated Nextra MDX.                          |
| `apps/docs/public`    | Public registry JSON, fonts, brand assets, and synthetic data. |
| `scripts/registry`    | Registry compilation and public-payload builds.                |
| `scripts/docs`        | Documentation synchronization.                                 |
| `scripts/sample-data` | Synthetic lab-report and intake-form generation.               |

## Documentation runtime

The docs app uses the [Next.js](https://nextjs.org/) App Router with
[Nextra](https://nextra.site/). The catch-all route at
`apps/docs/app/[[...mdxPath]]/page.tsx` imports pages from `apps/docs/content`.
`apps/docs/content/_meta.tsx` owns the top-level navigation.

The docs package runs registry and docs generation in both `predev` and
`prebuild`. A local or production Next.js build therefore uses current
generated artifacts instead of whatever happened to be on disk beforehand.

The root layout marks the site `noindex`, `nofollow`, and `noarchive`. That is a
crawler policy, not access control.

## Generated boundaries

Change the source on the left, then regenerate the output on the right.

| Source of truth                                 | Generated output                                        |
| ----------------------------------------------- | ------------------------------------------------------- |
| `packages/ui`, `packages/styles`, `skills`      | `registry/berlin`                                       |
| `config/registry/*.json` and authored source    | `config/registry/generated`, `registry.json`            |
| Generated namespace manifests                   | Registry JSON under `apps/docs/public`                  |
| Generated item metadata + `config/docs`         | Component, block, utility, and skill catalog pages      |
| `CHANGELOG.md`                                  | `apps/docs/content/changelog.mdx`                       |
| `scripts/circle-os/pii-mask.js`                 | `apps/docs/content/utilities/pii-mask.mdx`              |
| `scripts/sample-data` configuration and sources | PDFs and manifests under `apps/docs/public/sample-data` |

Do not hand-edit generated output. The relevant build removes and recreates
managed directories.

## Local workflow

Start the docs application:

```bash
pnpm install
pnpm dev:docs
```

After changing registry source or configuration:

```bash
pnpm registry:build
pnpm docs:sync
pnpm typecheck
pnpm build
```

`pnpm build` is the production gate. Turborepo type-checks the private UI
package, rebuilds registry and docs artifacts through the docs package hooks,
and runs the Next.js production build.

## Failure modes

- An unconfigured root component stops the registry build.
- A catalogued item missing from `config/docs/registry.json` stops docs sync.
- An `authoredItems` entry without its handwritten page stops docs sync.
- Hand edits below generated paths are overwritten on the next build.
- Reinstalling a registry item in a consumer may replace its local changes;
  inspect the consumer diff before accepting an update.
- The generated JSON is committed. Unexpected payload changes should be
  investigated before merge, not hidden from the diff.



[View the live page](https://registry.circle.health/technical-setup)

---

<a id="page-themes"></a>

<!-- source: /themes -->

# Themes

Themes are CSS-variable sets consumed by components and blocks. Install the base
style first, then choose the token set that matches the product surface.

The catalog below is generated from `config/registry/styles.json`.

- **Berlin** (`berlin`) — The default Circle Health product palette with warm sand surfaces and blue actions.
- **Linen** (`linen`) — A brighter editorial variant with softer neutrals and a greener accent.
- **Graphite** (`graphite`) — A cooler, higher-contrast operating palette for dense internal tooling surfaces.



[View the live page](https://registry.circle.health/themes)

---

<a id="page-skills"></a>

<!-- source: /skills -->

# Skills

Installable AI skills for common Circle Health review and QA tasks.

- [Copy Review](#page-skills-copy-review) — Review customer-facing copy for healing promises, language issues, and formal Sie phrasing.
- [Link Check](#page-skills-link-check) — Audit links for failures, redirects, insecure URLs, and mismatched destinations.



[View the live page](https://registry.circle.health/skills)

---

<a id="page-skills-copy-review"></a>

<!-- source: /skills/copy-review -->

# Copy Review

Review customer-facing copy for healing promises, language issues, and formal Sie phrasing.

## Install

```bash
pnpm dlx shadcn@latest add @skills/copy-review
```

## Skill

#### Copy Review SKILL.md

````
```md
---
name: copy-review
description: Review customer-facing Circle Health copy for healing promises, German or English language issues, and formal Sie phrasing. Use before publishing UI text, websites, emails, notifications, translations, or other user-facing copy.
---

# Copy review

Review the supplied copy and:

- Flag cures, guaranteed outcomes, or specific medical results. Suggest cautious alternatives such as "kann unterstützen", "zielt darauf ab", or "may help support".
- Correct spelling, grammar, and awkward phrasing in German or English.
- Replace formal German "Sie", "Ihnen", and "Ihr" with warm, informal "Du", "dir", and "dein" forms.

For every issue, quote the original text, name the category, and provide a replacement. If no issues remain, confirm that the copy is ready.
````

```
```



[View the live page](https://registry.circle.health/skills/copy-review)

---

<a id="page-skills-link-check"></a>

<!-- source: /skills/link-check -->

# Link Check

Audit links for failures, redirects, insecure URLs, and mismatched destinations.

## Install

```bash
pnpm dlx shadcn@latest add @skills/link-check
```

## Skill

#### Link Check SKILL.md

````
```md
---
name: link-check
description: Audit links in documentation, websites, or other content for failures, redirects, insecure HTTP URLs, and mismatched destinations. Use when reviewing content with Markdown, HTML, raw, or relative links.
---

# Link check

Extract every link from the supplied content, including Markdown links, HTML links, raw URLs, and relative paths.

Test each destination when network access is available. Report the link text, URL, status, final destination after redirects, and whether the destination matches the surrounding context. Classify each result as OK, Redirect, Broken, Insecure, or Not tested.

Finish with totals and concrete recommendations for links that should be updated or removed.
````

```
```



[View the live page](https://registry.circle.health/skills/link-check)

---

<a id="page-changelog"></a>

<!-- source: /changelog -->

# Changelog

Release history for The Circle Health Registry.

## v2.0.0 — Canonical Registry

### Added

- [Nextra](https://nextra.site/) documentation with nested pages for components,
  blocks, utilities, skills, themes, sample data, concepts, and installation
  guidance.
- Copy Review and Link Check skills, published through the registry with
  copyable agent installation guidance.
- Brand reference pages for Decades, Praxis Kessler, and Soma Studio.
- Direct download and copyable URL references for synthetic lab reports and
  intake forms.

### Changed

- Registry entries now map one-to-one to source modules.
- Supporting hooks, types, serializers, and copy dictionaries remain
  installable dependencies but are no longer separate catalog pages.
- `Rating` is a component and `Support` is a block; the mixed `general` module
  has been removed.
- Patient-results metric cards use the neutral reporting modules directly.
- Analytics helpers now live in the `utilities` namespace.
- Documentation navigation now follows the registry's component and block
  categories.
- Registry documentation and metadata consistently use the Circle Health
  Registry name.
- Flow previews now fill their available surface.

### Removed

- Duplicate install aliases. Install the owning module instead:
  `form`, `address`, `booking-timeline`, `commerce`, `flow`, `media`, or
  `patient-results-report`.
- The redundant `Spacer` component.
- The legacy unnamespaced `/r` registry endpoint. Use `/circle-ui`, `/blocks`,
  or `/i18n`.
- The unused [Storybook](https://storybook.js.org/) workspace and superseded
  standalone prompts document.

## v1.0.0 — Initial Release

The first public release of The Circle Health Registry at `registry.circle.health`.

### Added

#### Pages

- **Components** — Catalog browser for core UI primitives, grouped by function
- **Blocks** — Pre-assembled flows and domain patterns built from core UI
- **I18n** — Shared locale dictionaries and copy packs
- **Utilities** — Utility reference page
- **Themes** — Theme browser with live previews
- **Showcase** — Featured compositions and usage examples
- **Get Started** — Onboarding guides for Components and Blocks

#### CMS (Registry Content System)

- **Registry Item Catalog** (`items.json`) — 98 indexed component entries with titles, descriptions, namespace assignments, and file mappings
- **Site Configuration** (`site.json`) — Namespace definitions, public paths, source policy, and base style/utils metadata
- **Styles Catalog** (`styles.json`) — Theme definitions with token overrides
- **Registry JSON API** (`/r/[name]`) — Serves cached registry payloads per item with cache headers for CLI consumption
- **Build Pipeline** — Automated scripts generating [shadcn-compatible](https://ui.shadcn.com/docs/registry) registry payloads from source

#### Core UI Components (30 primitives)

| Group                   | Items                                                                                            |
| ----------------------- | ------------------------------------------------------------------------------------------------ |
| Actions & Feedback      | Alert, Badge, Button, Progress Bar, Spinner, Tag                                                 |
| Inputs & Forms          | Checkbox, Switch, Date Picker, Dropdown, Form, Phone Input, Select With Input, TextArea, TextBox |
| Navigation & Disclosure | Back Button, Link, Sheet, Tabs, Text Link                                                        |
| Layout & Content        | Card, Container, Empty, Flex, Spacer, Text, Text With Icon, Timeline                             |

#### Blocks (62 compositions)

| Group                | Items                                                                                                                                                                                                                                                                                                                                                        |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Flow Patterns        | Floating Flow Actions, Flow, Flow Actions, Flow Button, Flow Head, Flow Options, Flow Progress, Top Bar                                                                                                                                                                                                                                                      |
| Commerce             | Cart Line Item, Commerce, Discount Input, Input Discount, Pricing Table, Pricing Table Row, Product Card, Product Item, Quantity Adjustor                                                                                                                                                                                                                    |
| Media                | Hero Gallery, Image Lightbox, Image With Lightbox, Lightbox, Media, Media Slide, Testimonials Carousel, Text Only Testimonial, Video                                                                                                                                                                                                                         |
| Booking              | Booking Timeline, Booking Timeline Item, Booking Timeline Indicator, Booking Timeline Progress Dot, Booking Timeline Progress Separator, Booking Timeline Step, Booking Timeline Step Basics, Booking Timeline Step Content Notes, Booking Timeline Step Content Directions, Booking Timeline Step Content Todo List, Booking Timeline Todo Status Indicator |
| Address & Support    | Address, Address Input, Auto Address Inputs, General, Map, Manual Address Inputs, Rating, Support                                                                                                                                                                                                                                                            |
| Compliance           | Cookie Consent                                                                                                                                                                                                                                                                                                                                               |
| Reporting Primitives | Metric Range Card, Metric Donut Card, Metric Split Card, Metric Stat Card, Text Columns Card                                                                                                                                                                                                                                                                 |
| Patient Results      | Patient Results Blocks (umbrella), Segment Muscle Analysis, Diagnostic Report Header, Diagnostic Report Footer, Diagnostic Report Intro, Diagnostic Report Page Shell, Patient Results Editor, Patient Results Report, Patient Results PDF, VO2 Max Report, VNS Report, Body Composition Report                                                              |

#### I18n Copy Packs

- Cookie Consent Copy — Shared locale dictionary for the cookie consent block

#### Themes

- **Berlin** — Default Circle Health product palette with warm sand surfaces and blue actions
- **Linen** — Brighter editorial variant with softer neutrals and a greener accent
- **Graphite** — Cooler, higher-contrast palette for dense internal tooling surfaces

#### Infrastructure

- [shadcn CLI](https://ui.shadcn.com/docs/cli) compatibility — All items installable via `npx shadcn@latest add`
- Namespace-based organization — `circle-ui`, `blocks`, `i18n`
- Berlin registry style — App-first Circle Health tokens and defaults
- Registry dependency graph — Items declare internal dependencies for automatic resolution
- Source policy — Primary (`circle-app`) and secondary (`legacy-design-system`) source references
- [Storybook 10](https://storybook.js.org/) integration — Isolated component previews



[View the live page](https://registry.circle.health/changelog)

---

<a id="page-agents"></a>

<!-- source: /agents -->

# AGENTS.MD

General guidance for agents working across Circle Health repositories. More specific
instructions closer to the code being changed take precedence.

## Sources of truth

- Read the relevant code, configuration, and project documentation before
  editing. Do not rely on remembered framework behaviour when the installed
  version can answer the question.
- For [Next.js](https://nextjs.org/docs) work, read the relevant guide in
  `node_modules/next/dist/docs/` before writing code. The installed version may
  contain breaking API, convention, or file-structure changes. Heed
  deprecation notices.
- When a task references [Figma](https://www.figma.com/) or another design
  source, inspect that source and match its copy, layout, styles, and assets. Do
  not invent missing design decisions.
- Preserve explicit product constraints such as privacy, indexing, security,
  and public visibility unless the task changes them.

## Change discipline

- Keep changes small, local, and directly related to the feature or bug.
- Preserve unrelated work in a dirty worktree.
- Reuse existing primitives, tokens, and patterns before introducing a new
  abstraction.
- Verify changes with the narrowest useful tests, type checks, lint checks, or
  build steps for the affected area.

## Structure and naming

- Prefer cute, neat names and logical domain boundaries. Use
  `commerce/readiness/medusa.ts`, not `commerce-readiness.medusa.ts`.
- Keep one clear responsibility per module and place it beside the domain that
  owns it.
- Prefer functional factories and functions over classes when they make
  dependency injection and testing simpler.
- Quote route-segment paths in shell commands so characters such as `[]` and
  `()` are not expanded by the shell.

## Types and boundaries

- Do not introduce `any`, `as any`, `@ts-ignore`, or broad double casts when a
  practical typed alternative exists.
- Prefer generated types, schema-inferred types, `unknown` with narrowing, or a
  small explicit interface.
- If an escape hatch is unavoidable, keep it at the untyped boundary and
  explain why it is needed.
- Keep input validation and authorization checks close to the API, data, or
  service boundary they protect.

## Interface work

- In localized surfaces, keep user-facing copy in the localization system.
  Preserve German as a first-class locale, update both English and German
  messages, and prefer message interpolation over string concatenation.
- Use the existing design system and component primitives instead of ad-hoc
  colours, typography, spacing, or controls.
- For technologies, AI models, and integrations, use the service's
  `domain.com/favicon.ico` when a recognizable brand mark is needed and no
  canonical local asset exists.

## Copy AGENTS.md

```md
# Agent guidance

Use these rules throughout this repository. More specific instructions closer to
the code being changed take precedence.

## Sources of truth

- Read the relevant code, configuration, and project documentation before
  editing. Do not rely on remembered framework behaviour when the installed
  version can answer the question.
- For Next.js work, read the relevant guide in
  `node_modules/next/dist/docs/` before writing code. The installed version may
  contain breaking API, convention, or file-structure changes. Heed
  deprecation notices.
- When a task references Figma or another design source, inspect that source and
  match its copy, layout, styles, and assets. Do not invent missing design
  decisions.
- Preserve explicit product constraints such as privacy, indexing, security,
  and public visibility unless the task changes them.

## Change discipline

- Keep changes small, local, and directly related to the feature or bug.
- Preserve unrelated work in a dirty worktree.
- Reuse existing primitives, tokens, and patterns before introducing a new
  abstraction.
- Verify changes with the narrowest useful tests, type checks, lint checks, or
  build steps for the affected area.

## Structure and naming

- Prefer cute, neat names and logical domain boundaries. Use
  `commerce/readiness/medusa.ts`, not `commerce-readiness.medusa.ts`.
- Keep one clear responsibility per module and place it beside the domain that
  owns it.
- Prefer functional factories and functions over classes when they make
  dependency injection and testing simpler.
- Quote route-segment paths in shell commands so characters such as `[]` and
  `()` are not expanded by the shell.

## Types and boundaries

- Do not introduce `any`, `as any`, `@ts-ignore`, or broad double casts when a
  practical typed alternative exists.
- Prefer generated types, schema-inferred types, `unknown` with narrowing, or a
  small explicit interface.
- If an escape hatch is unavoidable, keep it at the untyped boundary and
  explain why it is needed.
- Keep input validation and authorization checks close to the API, data, or
  service boundary they protect.

## Interface work

- In localized surfaces, keep user-facing copy in the localization system.
  Preserve German as a first-class locale, update both English and German
  messages, and prefer message interpolation over string concatenation.
- Use the existing design system and component primitives instead of ad-hoc
  colours, typography, spacing, or controls.
- For technologies, AI models, and integrations, use the service's
  `domain.com/favicon.ico` when a recognizable brand mark is needed and no
  canonical local asset exists.
```



[View the live page](https://registry.circle.health/agents)

---

<a id="page-components"></a>

<!-- source: /components -->

# Components

Low-level UI with no product workflow attached.

- **Actions & Status** — Trigger actions and communicate state.
- **Forms** — Collect, validate, and submit user input.
- **Navigation & Overlays** — Move between views and reveal transient interfaces.
- **Layout & Content** — Structure pages and present content.



[View the live page](https://registry.circle.health/components)

---

<a id="page-components-actions-status-alert"></a>

<!-- source: /components/actions-status/alert -->

# Alert

Status callout for inline feedback.

> **Interactive preview: Alert**
>
> [Open the rendered preview on the live page](#page-components-actions-status-alert)

## Install

```bash
pnpm dlx shadcn@latest add @circle-ui/alert
```

## Use

```tsx
import { AlertCard } from "@circle/ui";
```

**Registry dependencies:** `@circle-ui/utils`



[View the live page](https://registry.circle.health/components/actions-status/alert)

---

<a id="page-components-actions-status-badge"></a>

<!-- source: /components/actions-status/badge -->

# Badge

Compact status or category label.

> **Interactive preview: Badge**
>
> [Open the rendered preview on the live page](#page-components-actions-status-badge)

## Install

```bash
pnpm dlx shadcn@latest add @circle-ui/badge
```

## Use

```tsx
import { Badge } from "@circle/ui";
```

**Registry dependencies:** `@circle-ui/utils`



[View the live page](https://registry.circle.health/components/actions-status/badge)

---

<a id="page-components-actions-status-button"></a>

<!-- source: /components/actions-status/button -->

# Button

Primary, secondary, and text actions.

> **Interactive preview: Button**
>
> [Open the rendered preview on the live page](#page-components-actions-status-button)

## Install

```bash
pnpm dlx shadcn@latest add @circle-ui/button
```

## Use

```tsx
import { Button } from "@circle/ui";
```

**Registry dependencies:** `@circle-ui/utils`

**Package dependencies:** `@radix-ui/react-slot`, `class-variance-authority`



[View the live page](https://registry.circle.health/components/actions-status/button)

---

<a id="page-components-actions-status-progress-bar"></a>

<!-- source: /components/actions-status/progress-bar -->

# Progress Bar

Determinate progress track and indicator.

> **Interactive preview: Progress Bar**
>
> [Open the rendered preview on the live page](#page-components-actions-status-progress-bar)

## Install

```bash
pnpm dlx shadcn@latest add @circle-ui/progress-bar
```

## Use

```tsx
import { ProgressBar } from "@circle/ui";
```

**Registry dependencies:** `@circle-ui/utils`



[View the live page](https://registry.circle.health/components/actions-status/progress-bar)

---

<a id="page-components-actions-status-rating"></a>

<!-- source: /components/actions-status/rating -->

# Rating

Rating value, stars, and review count.

> **Interactive preview: Rating**
>
> [Open the rendered preview on the live page](#page-components-actions-status-rating)

## Install

```bash
pnpm dlx shadcn@latest add @circle-ui/rating
```

## Use

```tsx
import { Rating } from "@circle/ui";
```

**Registry dependencies:** `@circle-ui/utils`



[View the live page](https://registry.circle.health/components/actions-status/rating)

---

<a id="page-components-actions-status-spinner"></a>

<!-- source: /components/actions-status/spinner -->

# Spinner

Indeterminate loading indicator.

> **Interactive preview: Spinner**
>
> [Open the rendered preview on the live page](#page-components-actions-status-spinner)

## Install

```bash
pnpm dlx shadcn@latest add @circle-ui/spinner
```

## Use

```tsx
import { Spinner } from "@circle/ui";
```

**Registry dependencies:** `@circle-ui/utils`



[View the live page](https://registry.circle.health/components/actions-status/spinner)

---

<a id="page-components-actions-status-tag"></a>

<!-- source: /components/actions-status/tag -->

# Tag

Removable or static metadata label.

> **Interactive preview: Tag**
>
> [Open the rendered preview on the live page](#page-components-actions-status-tag)

## Install

```bash
pnpm dlx shadcn@latest add @circle-ui/tag
```

## Use

```tsx
import { Tag } from "@circle/ui";
```

**Registry dependencies:** `@circle-ui/utils`



[View the live page](https://registry.circle.health/components/actions-status/tag)

---

<a id="page-components-forms-checkbox"></a>

<!-- source: /components/forms/checkbox -->

# Checkbox

Controlled or uncontrolled checkbox.

> **Interactive preview: Checkbox**
>
> [Open the rendered preview on the live page](#page-components-forms-checkbox)

## Install

```bash
pnpm dlx shadcn@latest add @circle-ui/checkbox
```

## Use

```tsx
import { Checkbox } from "@circle/ui";
```

**Registry dependencies:** `@circle-ui/utils`

**Package dependencies:** `@radix-ui/react-checkbox`



[View the live page](https://registry.circle.health/components/forms/checkbox)

---

<a id="page-components-forms-date-picker"></a>

<!-- source: /components/forms/date-picker -->

# Date Picker

Single-date calendar picker.

> **Interactive preview: Date Picker**
>
> [Open the rendered preview on the live page](#page-components-forms-date-picker)

## Install

```bash
pnpm dlx shadcn@latest add @circle-ui/date-picker
```

## Use

```tsx
import { DatePicker } from "@circle/ui";
```

**Registry dependencies:** `@circle-ui/utils`

**Package dependencies:** `react-day-picker`



[View the live page](https://registry.circle.health/components/forms/date-picker)

---

<a id="page-components-forms-form"></a>

<!-- source: /components/forms/form -->

# Form Fields

Form state, text fields, text areas, selects, and phone input.

> **Interactive preview: Form Fields**
>
> [Open the rendered preview on the live page](#page-components-forms-form)

## Install

```bash
pnpm dlx shadcn@latest add @circle-ui/form
```

## Use

```tsx
import { Form } from "@circle/ui";
```

**Registry dependencies:** `@circle-ui/utils`

**Package dependencies:** `react-international-phone`



[View the live page](https://registry.circle.health/components/forms/form)

---

<a id="page-components-forms-switch"></a>

<!-- source: /components/forms/switch -->

# Switch

Binary setting control.

> **Interactive preview: Switch**
>
> [Open the rendered preview on the live page](#page-components-forms-switch)

## Install

```bash
pnpm dlx shadcn@latest add @circle-ui/switch
```

## Use

```tsx
import { Switch } from "@circle/ui";
```

**Registry dependencies:** `@circle-ui/utils`

**Package dependencies:** `@radix-ui/react-switch`



[View the live page](https://registry.circle.health/components/forms/switch)

---

<a id="page-components-navigation-overlays-back-button"></a>

<!-- source: /components/navigation-overlays/back-button -->

# Back Button

Back control with label, icon, and chevron variants.

> **Interactive preview: Back Button**
>
> [Open the rendered preview on the live page](#page-components-navigation-overlays-back-button)

## Install

```bash
pnpm dlx shadcn@latest add @circle-ui/back-button
```

## Use

```tsx
import { BackButton } from "@circle/ui";
```

**Registry dependencies:** `@circle-ui/utils`



[View the live page](https://registry.circle.health/components/navigation-overlays/back-button)

---

<a id="page-components-navigation-overlays-link"></a>

<!-- source: /components/navigation-overlays/link -->

# Link

Inline link with Circle Health typography and focus treatment.

> **Interactive preview: Link**
>
> [Open the rendered preview on the live page](#page-components-navigation-overlays-link)

## Install

```bash
pnpm dlx shadcn@latest add @circle-ui/link
```

## Use

```tsx
import { TextLink } from "@circle/ui";
```

**Registry dependencies:** `@circle-ui/utils`



[View the live page](https://registry.circle.health/components/navigation-overlays/link)

---

<a id="page-components-navigation-overlays-overlay-action"></a>

<!-- source: /components/navigation-overlays/overlay-action -->

# Overlay Action

Accessible action placed over media or another visual surface.

> **Interactive preview: Overlay Action**
>
> [Open the rendered preview on the live page](#page-components-navigation-overlays-overlay-action)

## Install

```bash
pnpm dlx shadcn@latest add @circle-ui/overlay-action
```

## Use

```tsx
import { OverlayAction } from "@circle/ui";
```

**Registry dependencies:** `@circle-ui/utils`

**Package dependencies:** `class-variance-authority`



[View the live page](https://registry.circle.health/components/navigation-overlays/overlay-action)

---

<a id="page-components-navigation-overlays-sheet"></a>

<!-- source: /components/navigation-overlays/sheet -->

# Sheet

Modal sheet with trigger, overlay, content, and close controls.

> **Interactive preview: Sheet**
>
> [Open the rendered preview on the live page](#page-components-navigation-overlays-sheet)

## Install

```bash
pnpm dlx shadcn@latest add @circle-ui/sheet
```

## Use

```tsx
import { Sheet } from "@circle/ui";
```

**Registry dependencies:** `@circle-ui/overlay-action`, `@circle-ui/utils`

**Package dependencies:** `class-variance-authority`, `vaul`



[View the live page](https://registry.circle.health/components/navigation-overlays/sheet)

---

<a id="page-components-navigation-overlays-tabs"></a>

<!-- source: /components/navigation-overlays/tabs -->

# Tabs

Tabbed navigation and content panels.

> **Interactive preview: Tabs**
>
> [Open the rendered preview on the live page](#page-components-navigation-overlays-tabs)

## Install

```bash
pnpm dlx shadcn@latest add @circle-ui/tabs
```

## Use

```tsx
import { TabRoot } from "@circle/ui";
```

**Registry dependencies:** `@circle-ui/utils`

**Package dependencies:** `@radix-ui/react-tabs`



[View the live page](https://registry.circle.health/components/navigation-overlays/tabs)

---

<a id="page-components-navigation-overlays-tooltip"></a>

<!-- source: /components/navigation-overlays/tooltip -->

# Tooltip

Contextual help with default or custom triggers.

> **Interactive preview: Tooltip**
>
> [Open the rendered preview on the live page](#page-components-navigation-overlays-tooltip)

## Install

```bash
pnpm dlx shadcn@latest add @circle-ui/tooltip
```

## Use

```tsx
import { Tooltip, TooltipWrapper } from "@circle/ui";

<Tooltip content="Reviewed before sharing." side="right" />

<TooltipWrapper content="More context" side="top">
  <button type="button">Details</button>
</TooltipWrapper>
```

**Registry dependencies:** `@circle-ui/utils`

**Package dependencies:** `@radix-ui/react-tooltip`



[View the live page](https://registry.circle.health/components/navigation-overlays/tooltip)

---

<a id="page-components-layout-content-card"></a>

<!-- source: /components/layout-content/card -->

# Card

Surface, header, content, footer, and action slots.

> **Interactive preview: Card**
>
> [Open the rendered preview on the live page](#page-components-layout-content-card)

## Install

```bash
pnpm dlx shadcn@latest add @circle-ui/card
```

## Use

```tsx
import { Card } from "@circle/ui";
```

**Registry dependencies:** `@circle-ui/utils`



[View the live page](https://registry.circle.health/components/layout-content/card)

---

<a id="page-components-layout-content-container"></a>

<!-- source: /components/layout-content/container -->

# Container

Centered, width-constrained page container.

> **Interactive preview: Container**
>
> [Open the rendered preview on the live page](#page-components-layout-content-container)

## Install

```bash
pnpm dlx shadcn@latest add @circle-ui/container
```

## Use

```tsx
import { Container } from "@circle/ui";
```



[View the live page](https://registry.circle.health/components/layout-content/container)

---

<a id="page-components-layout-content-empty"></a>

<!-- source: /components/layout-content/empty -->

# Empty

Empty-state layouts with optional title and description.

> **Interactive preview: Empty**
>
> [Open the rendered preview on the live page](#page-components-layout-content-empty)

## Install

```bash
pnpm dlx shadcn@latest add @circle-ui/empty
```

## Use

```tsx
import { Empty } from "@circle/ui";
```

**Registry dependencies:** `@circle-ui/flex`, `@circle-ui/utils`



[View the live page](https://registry.circle.health/components/layout-content/empty)

---

<a id="page-components-layout-content-text"></a>

<!-- source: /components/layout-content/text -->

# Text

Product typography with semantic element options.

> **Interactive preview: Text**
>
> [Open the rendered preview on the live page](#page-components-layout-content-text)

## Install

```bash
pnpm dlx shadcn@latest add @circle-ui/text
```

## Use

```tsx
import { Text } from "@circle/ui";
```

**Registry dependencies:** `@circle-ui/utils`



[View the live page](https://registry.circle.health/components/layout-content/text)

---

<a id="page-components-layout-content-text-with-icon"></a>

<!-- source: /components/layout-content/text-with-icon -->

# Text With Icon

Inline label paired with an icon.

> **Interactive preview: Text With Icon**
>
> [Open the rendered preview on the live page](#page-components-layout-content-text-with-icon)

## Install

```bash
pnpm dlx shadcn@latest add @circle-ui/text-with-icon
```

## Use

```tsx
import { TextWithIcon } from "@circle/ui";
```

**Registry dependencies:** `@circle-ui/flex`



[View the live page](https://registry.circle.health/components/layout-content/text-with-icon)

---

<a id="page-components-layout-content-timeline"></a>

<!-- source: /components/layout-content/timeline -->

# Timeline

Vertical sequence of connected steps.

> **Interactive preview: Timeline**
>
> [Open the rendered preview on the live page](#page-components-layout-content-timeline)

## Install

```bash
pnpm dlx shadcn@latest add @circle-ui/timeline
```

## Use

```tsx
import { Timeline } from "@circle/ui";
```

**Registry dependencies:** `@circle-ui/flex`, `@circle-ui/utils`



[View the live page](https://registry.circle.health/components/layout-content/timeline)

---

<a id="page-blocks"></a>

<!-- source: /blocks -->

# Blocks

Product modules composed from components.

- **Flows** — Frame multi-step product journeys and their controls.
- **Commerce** — Product, pricing, quantity, cart, and discount patterns.
- **Booking Timeline** — Ordered booking steps with progress, notes, directions, and tasks.
- **Media & Support** — Present media, locations, addresses, and support.
- **Cookie Consent** — Collect, store, revisit, and broadcast category consent preferences.
- **Reporting** — Compose metrics and result visualizations.
- **Patient Results** — Edit, render, export, and bundle patient result reports.



[View the live page](https://registry.circle.health/blocks)

---

<a id="page-blocks-flows-flow"></a>

<!-- source: /blocks/flows/flow -->

# Flow

Shell, header, progress, choices, and actions for step-based journeys.

> **Interactive preview: Flow**
>
> [Open the rendered preview on the live page](#page-blocks-flows-flow)

## Install

```bash
pnpm dlx shadcn@latest add @blocks/flow
```

## Use

```tsx
import { FlowWrapper } from "@circle/ui";
```

**Registry dependencies:** `@circle-ui/button`, `@circle-ui/progress-bar`, `@circle-ui/utils`



[View the live page](https://registry.circle.health/blocks/flows/flow)

---

<a id="page-blocks-flows-floating-flow-actions"></a>

<!-- source: /blocks/flows/floating-flow-actions -->

# Floating Flow Actions

Pinned submit action with loading and disabled states.

> **Interactive preview: Floating Flow Actions**
>
> [Open the rendered preview on the live page](#page-blocks-flows-floating-flow-actions)

## Install

```bash
pnpm dlx shadcn@latest add @blocks/floating-flow-actions
```

## Use

```tsx
import { FloatingFlowActions } from "@circle/ui";
```

**Registry dependencies:** `@blocks/flow`, `@circle-ui/spinner`, `@circle-ui/utils`



[View the live page](https://registry.circle.health/blocks/flows/floating-flow-actions)

---

<a id="page-blocks-flows-top-bar"></a>

<!-- source: /blocks/flows/top-bar -->

# Top Bar

Back, close, and logo controls for flow headers.

> **Interactive preview: Top Bar**
>
> [Open the rendered preview on the live page](#page-blocks-flows-top-bar)

## Install

```bash
pnpm dlx shadcn@latest add @blocks/top-bar
```

## Use

```tsx
import { TopBar } from "@circle/ui";
```

**Registry dependencies:** `@blocks/flow`



[View the live page](https://registry.circle.health/blocks/flows/top-bar)

---

<a id="page-blocks-flows-fillout-flow"></a>

<!-- source: /blocks/flows/fillout-flow -->

# Fillout Flow

Schema-driven [Fillout](https://www.fillout.com/) flow renderer with components from the `circle-ui` namespace and compatible submission payloads.

## Install

```bash
pnpm dlx shadcn@latest add @blocks/fillout-flow
```

## Use

> **Caution:** The built-in mapper covers common choice, text, email, phone, number, and date fields. Use a kind override or custom renderer for other Fillout field types.

```tsx
import {
  FilloutFlow,
  createFilloutFlowDefinition,
  type FilloutFormMetadata,
} from "@circle/ui";

const flow = createFilloutFlowDefinition(form as FilloutFormMetadata, {
  steps: [
    { id: "goal", fields: ["kaRM"] },
    { id: "details", fields: ["h8ay", "goHS"] },
  ],
});

<FilloutFlow flow={flow} onSubmit={submitToFillout} />;
```

#### Setup

1. Fetch Fillout form metadata through a server or safe API route.
2. Group question ids into a flow definition.
3. Post the resulting payload back to Fillout from the submit callback.

#### Works with

- `flow` — Use the lower-level shell when Fillout is not the schema source.

**Registry dependencies:** `@blocks/fillout-flow-schema`, `@blocks/fillout-flow-submission`, `@blocks/use-fillout-flow`, `@circle-ui/container`, `@circle-ui/date-picker`, `@circle-ui/form`, `@circle-ui/text`, `@circle-ui/utils`



[View the live page](https://registry.circle.health/blocks/flows/fillout-flow)

---

<a id="page-blocks-commerce"></a>

<!-- source: /blocks/commerce -->

# Commerce

Product, pricing, quantity, cart, and discount patterns.

> **Interactive preview: Commerce**
>
> [Open the rendered preview on the live page](#page-blocks-commerce)

## Install

```bash
pnpm dlx shadcn@latest add @blocks/commerce
```

## Use

```tsx
import { ProductCard } from "@circle/ui";
```

**Registry dependencies:** `@circle-ui/badge`, `@circle-ui/button`, `@circle-ui/form`, `@circle-ui/utils`



[View the live page](https://registry.circle.health/blocks/commerce)

---

<a id="page-blocks-booking-timeline"></a>

<!-- source: /blocks/booking-timeline -->

# Booking Timeline

Ordered booking steps with progress, notes, directions, and tasks.

> **Interactive preview: Booking Timeline**
>
> [Open the rendered preview on the live page](#page-blocks-booking-timeline)

## Install

```bash
pnpm dlx shadcn@latest add @blocks/booking-timeline
```

## Use

```tsx
import { BookingTimeline } from "@circle/ui";
```

**Registry dependencies:** `@blocks/map`, `@circle-ui/button`, `@circle-ui/utils`



[View the live page](https://registry.circle.health/blocks/booking-timeline)

---

<a id="page-blocks-media-support-media"></a>

<!-- source: /blocks/media-support/media -->

# Media

Video, lightbox, gallery, carousel, and testimonial patterns.

> **Interactive preview: Media**
>
> [Open the rendered preview on the live page](#page-blocks-media-support-media)

## Install

```bash
pnpm dlx shadcn@latest add @blocks/media
```

## Use

```tsx
import { Video } from "@circle/ui";
```

**Registry dependencies:** `@circle-ui/overlay-action`, `@circle-ui/utils`



[View the live page](https://registry.circle.health/blocks/media-support/media)

---

<a id="page-blocks-media-support-map"></a>

<!-- source: /blocks/media-support/map -->

# Map

[Mapbox](https://www.mapbox.com/) location preview with coordinate fallback.

> **Interactive preview: Map**
>
> [Open the rendered preview on the live page](#page-blocks-media-support-map)

## Install

```bash
pnpm dlx shadcn@latest add @blocks/map
```

## Use

```tsx
import { Map } from "@circle/ui";
```



[View the live page](https://registry.circle.health/blocks/media-support/map)

---

<a id="page-blocks-media-support-address"></a>

<!-- source: /blocks/media-support/address -->

# Address

Address search, manual entry, and mode switching.

> **Interactive preview: Address**
>
> [Open the rendered preview on the live page](#page-blocks-media-support-address)

## Install

```bash
pnpm dlx shadcn@latest add @blocks/address
```

## Use

```tsx
import { AddressInput } from "@circle/ui";
```

**Registry dependencies:** `@circle-ui/form`

**Package dependencies:** `mapbox-gl`



[View the live page](https://registry.circle.health/blocks/media-support/address)

---

<a id="page-blocks-media-support-support"></a>

<!-- source: /blocks/media-support/support -->

# Support

Support contact card with an avatar and link.

> **Interactive preview: Support**
>
> [Open the rendered preview on the live page](#page-blocks-media-support-support)

## Install

```bash
pnpm dlx shadcn@latest add @blocks/support
```

## Use

```tsx
import { Support } from "@circle/ui";
```

**Registry dependencies:** `@circle-ui/link`, `@circle-ui/utils`



[View the live page](https://registry.circle.health/blocks/media-support/support)

---

<a id="page-blocks-cookie-consent"></a>

<!-- source: /blocks/cookie-consent -->

# Cookie Consent

`CookieConsent` combines a consent sheet with a controller for storage,
reopening preferences, browser events, subscriptions, and optional server
persistence.

> **Interactive preview: Cookie Consent**
>
> [Open the rendered preview on the live page](#page-blocks-cookie-consent)

> **WARNING**
>
> The block records a preference. It does not classify cookies, prevent scripts
> from loading, remove provider cookies, or make an application compliant by
> itself. The consuming application must gate each optional service from the
> stored preferences.

## Install

```bash
pnpm dlx shadcn@latest add @blocks/cookie-consent
```

## Basic use

```tsx
import {
  CookieConsent,
  createCookieConsentController,
  createCookieConsentCookieStorage,
} from "@/components/ui/cookie-consent";

const controller = createCookieConsentController({
  storage: createCookieConsentCookieStorage({
    days: 180,
    sameSite: "Lax",
    secure: process.env.NODE_ENV === "production",
  }),
});

export function CookieConsentProvider() {
  return <CookieConsent controller={controller} />;
}
```

**Registry dependencies:** `@circle-ui/button`, `@circle-ui/sheet`, `@circle-ui/switch`, `@circle-ui/utils`

Create one controller and pass the same instance to the sheet and to any code
that reads consent. Without an explicit controller, the component creates one
that uses `window.localStorage`.

## Lifecycle

1. On mount, the component reads the configured storage key.
2. When a valid record exists, it restores the choices and emits a `hydrate`
   change. It does not auto-open the sheet.
3. When no valid record exists, `autoOpen` opens the sheet by default.
4. Accepting, rejecting, or saving a selection forces required categories to
   `true`, writes the record, emits change events, calls the persistence hook,
   and notifies subscribers.
5. `openPreferences()` reopens the sheet on its settings screen with the latest
   stored choices.

Stored records are normalized against the current categories whenever they are
read. Removed category ids are dropped, new ids receive their configured
default, and required categories remain enabled.

## Production setup

Use the cookie adapter when the preference must be available across page loads
as a cookie or shared by subdomains. Guard the shared domain so local and
preview hosts keep a host-only cookie.

```tsx filename="cookie-consent-provider.tsx"
"use client";

import {
  CookieConsent,
  createCookieConsentController,
  createCookieConsentCookieStorage,
  type CookieConsentCategory,
} from "@/components/ui/cookie-consent";

const categories: CookieConsentCategory[] = [
  {
    id: "essential",
    label: "Essential",
    description: "Required to provide the requested service.",
    required: true,
  },
  {
    id: "analytics",
    label: "Analytics",
    description: "Helps us understand product usage.",
    defaultValue: false,
  },
  {
    id: "marketing",
    label: "Marketing",
    description: "Measures and personalizes campaigns.",
    defaultValue: false,
  },
];

function resolveCookieDomain(location: Location) {
  const hostname = location.hostname.toLowerCase();

  if (hostname === "circle.health" || hostname.endsWith(".circle.health")) {
    return ".circle.health";
  }

  return null;
}

export const cookieConsent = createCookieConsentController({
  categories,
  storage: createCookieConsentCookieStorage({
    days: 180,
    domain: resolveCookieDomain,
    sameSite: "Lax",
    secure: process.env.NODE_ENV === "production",
  }),
  storageKey: "ch_cc",
  version: "1",
});

export function CookieConsentProvider() {
  return (
    <CookieConsent
      controller={cookieConsent}
      policyLink={{
        href: "/privacy",
        label: "privacy and cookie policy",
      }}
    />
  );
}
```

Render the provider once near the application root. Keep category ids stable:
they are the keys consumed by analytics, marketing, and personalization
integrations.

### Cookie storage options

| Option     | Default | Behavior                                                                                         |
| ---------- | ------- | ------------------------------------------------------------------------------------------------ |
| `days`     | `365`   | Lifetime of the preference cookie.                                                               |
| `domain`   | —       | A domain string or a function of `window.location`. Returning `null` creates a host-only cookie. |
| `path`     | `/`     | Cookie path.                                                                                     |
| `sameSite` | —       | `Lax`, `Strict`, or `None`. `None` also enables `Secure`.                                        |
| `secure`   | `false` | Adds the `Secure` attribute. Enable it on HTTPS production hosts.                                |

The adapter is client-side and cannot create an `HttpOnly` cookie. When a
domain is configured, it also removes an older host-only cookie so that one
unambiguous record remains.

## Categories and stored preferences

### Category fields

| Field          | Required | Behavior                                                                                                              |
| -------------- | -------- | --------------------------------------------------------------------------------------------------------------------- |
| `id`           | Yes      | Stable key written to `consents` and included in events.                                                              |
| `label`        | Yes      | Label for an optional category switch.                                                                                |
| `description`  | No       | Metadata included with events; the current sheet does not render it.                                                  |
| `required`     | No       | Forces the category to `true`. Required categories are summarized by the Essential row instead of receiving switches. |
| `defaultValue` | No       | Initial value before a choice exists. Required categories always resolve to `true`.                                   |

The built-in categories are a starter configuration, not a policy. Define the
categories owned by the consuming application.

### Stored record

The default storage key is `ch_cc`. Both local storage and cookie storage use
the same JSON record:

```json
{
  "id": "8a9f9f61-cfdd-4556-9ed3-7537d7c7e580",
  "version": "1",
  "updatedAt": "2026-07-24T08:00:00.000Z",
  "consents": {
    "essential": true,
    "analytics": false,
    "marketing": false
  }
}
```

The `id` is retained when an existing preference is updated. `updatedAt`
records the latest user choice. Increment `version` when a policy or category
change requires a new choice; a stored record with a different version is
treated as absent.

## Gate optional services

Read the controller before loading an optional service, and react to later
changes. Mounting the sheet with an existing record dispatches the current
preferences with `source: "hydrate"`.

```tsx
const cookieConsent = createCookieConsentController({
  onPreferencesChange({ consents, source }) {
    if (consents.analytics) {
      startAnalytics();
    } else {
      stopAnalytics();
    }

    console.info("Consent applied", source);
  },
});
```

`onPreferencesChange` runs for a stored choice and for hydration. It is the
most direct integration point when the controller and service bootstrap live
in the same application.

### Browser events

| Event                        | Target     | Timing                                                                  |
| ---------------------------- | ---------- | ----------------------------------------------------------------------- |
| `cookies-chosen`             | `document` | Immediately after a choice or hydration.                                |
| `cookies-chosen-processed`   | `document` | One second later by default, for integrations that need a second phase. |
| `ch-open-cookie-preferences` | `window`   | Opens the mounted sheet on its settings screen.                         |

The two change events are `CustomEvent<CookieConsentEventDetail>` events. Their
detail contains `categories`, `consents`, the complete `preferences` record,
`source`, and `version`.

```tsx
import type { CookieConsentEventDetail } from "@/components/ui/cookie-consent";

document.addEventListener("cookies-chosen", (event) => {
  const { consents } = (event as CustomEvent<CookieConsentEventDetail>).detail;

  if (consents.analytics) startAnalytics();
});
```

Set `emitDataLayerEvents: true` to also push `{ event: eventName }` to
`window.dataLayer`, or set `dataLayerName` for another array. Data-layer entries
contain the event name only; use the callback or DOM event when the integration
needs the preference detail.

## Let people revisit their choice

Keep a cookie-settings action available after the sheet closes.

```tsx
export function CookieSettingsButton() {
  return (
    <button type="button" onClick={() => cookieConsent.openPreferences()}>
      Cookie settings
    </button>
  );
}
```

Code without the controller can dispatch the exported default event:

```tsx
import { DEFAULT_OPEN_PREFERENCES_EVENT } from "@/components/ui/cookie-consent";

window.dispatchEvent(new CustomEvent(DEFAULT_OPEN_PREFERENCES_EVENT));
```

The event only has an effect while a `CookieConsent` component is mounted.

### Optional window bridge

Legacy scripts and tag-manager code can use a global bridge instead of importing
the controller. Attach it from a client component and return the cleanup
function from the effect.

```tsx
import { useEffect } from "react";

import { attachCookieConsentBridge } from "@/components/ui/cookie-consent";

export function CookieConsentBridge() {
  useEffect(() => attachCookieConsentBridge(window, cookieConsent), []);

  return null;
}
```

The default global is `window.CH_CC`. It exposes `consents`, `get`, `set`,
`update`, `subscribe`, `delete`, `reset`, `openPreferences`, and
`dispatchCurrentPreferences`. Pass a third argument to change the global name.
Cleanup restores any value that existed there before the bridge was attached.

## Persist an audit record

`persist` receives the same event detail plus `storageKey` and optional
`persistText`. It runs only after a user choice, not during hydration.

```tsx
const cookieConsent = createCookieConsentController({
  persistText: "Cookie consent v3",
  async persist(payload) {
    await fetch("/api/consent", {
      method: "POST",
      headers: { "content-type": "application/json" },
      body: JSON.stringify(payload),
    });
  },
});
```

Configured client storage is written first. Persistence is fire-and-forget: a
rejected promise logs an error and does not roll back the local preference or
keep the sheet open. Handle authentication, retries, and server validation in
the application when the audit record is required.

## Controller reference

| Method                         | Behavior                                                                             |
| ------------------------------ | ------------------------------------------------------------------------------------ |
| `get()`                        | Returns the normalized preference record or `null`.                                  |
| `getConsents()`                | Returns only the consent map or `null`.                                              |
| `set(consents, source)`        | Replaces optional choices, writes the record, and emits a change.                    |
| `update(consents, source?)`    | Merges a partial consent map; the default source is `save-selection`.                |
| `dispatchCurrentPreferences()` | Re-emits the stored record with source `hydrate`.                                    |
| `openPreferences()`            | Dispatches the configured window event to open settings.                             |
| `subscribe(listener)`          | Observes writes, hydration, reset, and deletion. Returns an unsubscribe function.    |
| `reset()`                      | Removes the stored record and notifies subscribers with `null`.                      |
| `delete()`                     | Removes the stored record and notifies subscribers; currently the same as `reset()`. |

Neither `reset()` nor `delete()` emits a consent change event or removes cookies
owned by other services.

### Controller options

| Option                     | Default                      | Behavior                                                        |
| -------------------------- | ---------------------------- | --------------------------------------------------------------- |
| `categories`               | Built-in categories          | Categories normalized and owned by this controller.             |
| `storage`                  | `window.localStorage`        | Any synchronous `getItem`, `setItem`, and `removeItem` adapter. |
| `storageKey`               | `ch_cc`                      | Key passed to the storage adapter.                              |
| `version`                  | `1`                          | Record version used to accept or invalidate stored preferences. |
| `chosenEventName`          | `cookies-chosen`             | Immediate document event and optional data-layer event.         |
| `processedEventName`       | `cookies-chosen-processed`   | Delayed document event and optional data-layer event.           |
| `processedDelayMs`         | `1000`                       | Delay before the processed event.                               |
| `openPreferencesEventName` | `ch-open-cookie-preferences` | Window event observed by the sheet.                             |
| `onPreferencesChange`      | —                            | Synchronous callback for choices and hydration.                 |
| `persist`                  | —                            | Optional asynchronous audit hook for user choices.              |
| `persistText`              | —                            | Application-defined text copied into the persistence payload.   |
| `emitDataLayerEvents`      | `false`                      | Pushes the chosen and processed event names to a data layer.    |
| `dataLayerName`            | `dataLayer`                  | Property on `window` that receives data-layer events.           |

## Component props

| Prop            | Default             | Behavior                                                                         |
| --------------- | ------------------- | -------------------------------------------------------------------------------- |
| `controller`    | Internal controller | Preferred way to share preferences and configuration with the application.       |
| `categories`    | Built-in categories | Used only when the component creates its internal controller.                    |
| `autoOpen`      | `true`              | Opens on first mount when no valid preference exists.                            |
| `open`          | —                   | Controlled open state. The owner must update it from `onOpenChange`.             |
| `defaultOpen`   | `false`             | Initial open state when uncontrolled.                                            |
| `onOpenChange`  | —                   | Called for automatic, consent-action, dismissal, and open-preferences requests.  |
| `initialScreen` | `main`              | Screen restored after the sheet closes: `main` or `settings`.                    |
| `dismissible`   | `false`             | Whether the sheet can close without a consent action.                            |
| `title`         | English default     | Sheet heading.                                                                   |
| `description`   | English default     | Custom body copy. Takes precedence over the generated policy-link sentence.      |
| `policyLink`    | —                   | Link inserted into the default English description.                              |
| `labels`        | English defaults    | Partial overrides for buttons, Essential, Required, and the settings aria-label. |
| `className`     | —                   | Class names applied to the sheet's inner content wrapper.                        |

## Localization

The optional copy pack provides English and German dictionaries. It is data,
not an automatic locale provider; map the selected dictionary into categories,
labels, title, and description in the consuming application.

```bash
pnpm dlx shadcn@latest add @i18n/cookie-consent-copy
```

```tsx
import { cookieConsentCopy } from "@/registry/berlin/i18n/cookie-consent-copy";

const copy = cookieConsentCopy.de;

<CookieConsent
  controller={cookieConsent}
  title={copy.title}
  labels={{
    acceptAll: copy.buttons.acceptAll,
    back: copy.buttons.back,
    customize: copy.buttons.customize,
    essential: copy.settings.essential,
    rejectAll: copy.buttons.rejectAll,
    required: copy.settings.required,
    saveSelection: copy.buttons.saveSelection,
    settingsAriaLabel: copy.settings.ariaLabel,
  }}
/>;
```

The `{link}` token in `subtitle` is an application interpolation point. The
component does not replace it automatically.



[View the live page](https://registry.circle.health/blocks/cookie-consent)

---

<a id="page-blocks-reporting-metric-donut-card"></a>

<!-- source: /blocks/reporting/metric-donut-card -->

# Metric Donut Card

Circular metric card for compact progress and threshold summaries.

> **Interactive preview: Metric Donut Card**
>
> [Open the rendered preview on the live page](#page-blocks-reporting-metric-donut-card)

## Install

```bash
pnpm dlx shadcn@latest add @blocks/metric-donut-card
```

## Use

```tsx
import { MetricDonutCard } from "@circle/ui";
```

**Registry dependencies:** `@blocks/reporting-types`, `@circle-ui/card`, `@circle-ui/utils`



[View the live page](https://registry.circle.health/blocks/reporting/metric-donut-card)

---

<a id="page-blocks-reporting-metric-range-card"></a>

<!-- source: /blocks/reporting/metric-range-card -->

# Metric Range Card

Range-based metric card for score bands, ticks, and highlighted target ranges.

> **Interactive preview: Metric Range Card**
>
> [Open the rendered preview on the live page](#page-blocks-reporting-metric-range-card)

## Install

```bash
pnpm dlx shadcn@latest add @blocks/metric-range-card
```

## Use

```tsx
import { MetricRangeCard } from "@circle/ui";
```

**Registry dependencies:** `@blocks/metric-utils`, `@blocks/reporting-types`, `@circle-ui/card`, `@circle-ui/utils`



[View the live page](https://registry.circle.health/blocks/reporting/metric-range-card)

---

<a id="page-blocks-reporting-metric-split-card"></a>

<!-- source: /blocks/reporting/metric-split-card -->

# Metric Split Card

Two-sided comparison card for paired values or before-and-after measurements.

> **Interactive preview: Metric Split Card**
>
> [Open the rendered preview on the live page](#page-blocks-reporting-metric-split-card)

## Install

```bash
pnpm dlx shadcn@latest add @blocks/metric-split-card
```

## Use

```tsx
import { MetricSplitCard } from "@circle/ui";
```

**Registry dependencies:** `@blocks/metric-utils`, `@blocks/reporting-types`, `@circle-ui/card`, `@circle-ui/utils`



[View the live page](https://registry.circle.health/blocks/reporting/metric-split-card)

---

<a id="page-blocks-reporting-metric-stat-card"></a>

<!-- source: /blocks/reporting/metric-stat-card -->

# Metric Stat Card

Single-value metric card with optional status labelling.

> **Interactive preview: Metric Stat Card**
>
> [Open the rendered preview on the live page](#page-blocks-reporting-metric-stat-card)

## Install

```bash
pnpm dlx shadcn@latest add @blocks/metric-stat-card
```

## Use

```tsx
import { MetricStatCard } from "@circle/ui";
```

**Registry dependencies:** `@blocks/metric-utils`, `@blocks/reporting-types`, `@circle-ui/card`, `@circle-ui/utils`



[View the live page](https://registry.circle.health/blocks/reporting/metric-stat-card)

---

<a id="page-blocks-reporting-text-columns-card"></a>

<!-- source: /blocks/reporting/text-columns-card -->

# Text Columns Card

Neutral two-column text layout with independent headings and copy.

> **Interactive preview: Text Columns Card**
>
> [Open the rendered preview on the live page](#page-blocks-reporting-text-columns-card)

## Install

```bash
pnpm dlx shadcn@latest add @blocks/text-columns-card
```

## Use

```tsx
import { TextColumnsCard } from "@circle/ui";
```

**Registry dependencies:** `@blocks/reporting-types`, `@circle-ui/utils`



[View the live page](https://registry.circle.health/blocks/reporting/text-columns-card)

---

<a id="page-blocks-reporting-segment-muscle-analysis"></a>

<!-- source: /blocks/reporting/segment-muscle-analysis -->

# Segment Muscle Analysis

Composed muscle-distribution block with a figure image, hotspots, and surrounding metric cards.

> **Interactive preview: Segment Muscle Analysis**
>
> [Open the rendered preview on the live page](#page-blocks-reporting-segment-muscle-analysis)

## Install

```bash
pnpm dlx shadcn@latest add @blocks/segment-muscle-analysis
```

## Use

```tsx
import { SegmentMuscleAnalysis } from "@circle/ui";
```

**Registry dependencies:** `@blocks/metric-donut-card`, `@blocks/patient-results-types`, `@circle-ui/card`, `@circle-ui/utils`



[View the live page](https://registry.circle.health/blocks/reporting/segment-muscle-analysis)

---

<a id="page-blocks-patient-results-patient-results-editor"></a>

<!-- source: /blocks/patient-results/patient-results-editor -->

# Patient Results Editor

[BlockNote](https://www.blocknotejs.org/) editor with diagnostic blocks registered.

> **Interactive preview: Patient Results Editor**
>
> [Open the rendered preview on the live page](#page-blocks-patient-results-patient-results-editor)

## Install

```bash
pnpm dlx shadcn@latest add @blocks/patient-results-editor
```

## Use

```tsx
import { PatientResultsEditor } from "@circle/ui";
```

#### Document flow

1. Seed the editor with a preset or saved BlockNote document.
2. Persist the editor document as the source of truth.
3. Pass that document to the browser or PDF renderer.

#### Works with

- `patient-results-report` — Render the saved document in the browser.
- `patient-results-pdf` — Export the saved document.

**Registry dependencies:** `@blocks/diagnostic-interpretation-columns`, `@blocks/metric-donut-card`, `@blocks/metric-range-card`, `@blocks/metric-split-card`, `@blocks/metric-stat-card`, `@blocks/segment-muscle-analysis`, `@circle-ui/button`, `@circle-ui/utils`

**Package dependencies:** `@blocknote/core`, `@blocknote/shadcn`



[View the live page](https://registry.circle.health/blocks/patient-results/patient-results-editor)

---

<a id="page-blocks-patient-results-patient-results-report"></a>

<!-- source: /blocks/patient-results/patient-results-report -->

# Patient Results Report

Preset-aware browser renderer for patient-results documents.

> **Interactive preview: Patient Results Report**
>
> [Open the rendered preview on the live page](#page-blocks-patient-results-patient-results-report)

## Install

```bash
pnpm dlx shadcn@latest add @blocks/patient-results-report
```

## Use

```tsx
import { PatientResultsReport } from "@circle/ui";
```

#### Works with

- `patient-results-editor` — Produces the editable source document.
- `patient-results-pdf` — Exports the same content.

**Registry dependencies:** `@blocks/diagnostic-interpretation-columns`, `@blocks/diagnostic-report-intro`, `@blocks/diagnostic-report-page-shell`, `@blocks/metric-donut-card`, `@blocks/metric-range-card`, `@blocks/metric-split-card`, `@blocks/metric-stat-card`, `@blocks/patient-results-presets`, `@blocks/segment-muscle-analysis`, `@circle-ui/utils`



[View the live page](https://registry.circle.health/blocks/patient-results/patient-results-report)

---

<a id="page-blocks-patient-results-patient-results-pdf"></a>

<!-- source: /blocks/patient-results/patient-results-pdf -->

# Patient Results PDF

[React-pdf](https://react-pdf.org/) renderer for patient-results documents.

## Install

```bash
pnpm dlx shadcn@latest add @blocks/patient-results-pdf
```

## Use

> **Caution:** This is an export module, not an editing surface.

```tsx
import { pdf } from "@react-pdf/renderer";
import { PatientResultsPdfDocumentFromEditor } from "@/components/blocks/patient-results-pdf";

const blob = await pdf(
  <PatientResultsPdfDocumentFromEditor
    document={editor.document}
    presetId="vns-report"
  />,
).toBlob();
```

#### Works with

- `patient-results-editor` — Produces the editable source document.
- `patient-results-report` — Renders the browser-side equivalent.

**Registry dependencies:** `@blocks/patient-results-presets`, `@blocks/patient-results-types`



[View the live page](https://registry.circle.health/blocks/patient-results/patient-results-pdf)

---

<a id="page-blocks-patient-results-patient-results-blocks"></a>

<!-- source: /blocks/patient-results/patient-results-blocks -->

# Patient Results Suite

Umbrella install for the editor, browser report, PDF, and presets.

## Install

```bash
pnpm dlx shadcn@latest add @blocks/patient-results-blocks
```

**Registry dependencies:** `@blocks/diagnostic-report-footer`, `@blocks/diagnostic-report-header`, `@blocks/diagnostic-report-intro`, `@blocks/diagnostic-report-page-shell`, `@blocks/metric-donut-card`, `@blocks/metric-range-card`, `@blocks/metric-split-card`, `@blocks/metric-stat-card`, `@blocks/patient-results-assets`, `@blocks/patient-results-editor`, `@blocks/patient-results-pdf`, `@blocks/patient-results-presets`, `@blocks/patient-results-report`, `@blocks/patient-results-serialization`, `@blocks/patient-results-types`, `@blocks/segment-muscle-analysis`, `@blocks/text-columns-card`



[View the live page](https://registry.circle.health/blocks/patient-results/patient-results-blocks)

---

<a id="page-utilities"></a>

<!-- source: /utilities -->

# Utilities

Installable helpers and browser tools with no UI workflow attached.

- **Nextra Export** — Export a complete [Nextra](https://nextra.site/) site as rich Markdown or one component-faithful PDF.
- **Analytics** — Provider-neutral events and tag-manager bridge.
- **Circle Mixpanel Config** — Deferred [Google Tag Manager](https://marketingplatform.google.com/about/tag-manager/) and [Mixpanel](https://mixpanel.com/) initialization for Circle Health web apps.
- **CircleOS PII Mask** — Prepare safe screenshots, recordings, and demos.



[View the live page](https://registry.circle.health/utilities)

---

<a id="page-utilities-nextra-export"></a>

<!-- source: /utilities/nextra-export -->

# Nextra Export

Export a complete [Nextra](https://nextra.site/) site as one Markdown file or a
print-ready book. The PDF view renders the site's real MDX and React components.
Markdown components use a project-owned renderer registry.

## Install

```bash
pnpm dlx shadcn@latest add @utilities/nextra-export
```

The command writes the exporter below `src/lib/nextra-export`. It installs
`unified` and the Remark packages used for MDX normalization. The target site
must already use Nextra 4.

## Load the documentation tree

Create one project-specific loader:

```tsx filename="src/lib/docs-export.ts"
import {
  collectNextraExportPages,
  loadNextraPageSources,
} from "@/lib/nextra-export";
import { importPage } from "nextra/pages";
import { getPageMap } from "nextra/page-map";

export async function loadDocsExportPages() {
  const pages = collectNextraExportPages(await getPageMap());
  return loadNextraPageSources(pages, importPage);
}
```

Page order follows Nextra navigation. Set `export: false` in a page's
frontmatter to omit it.

## Add the PDF route

Reserve `/export/pdf` inside the existing Nextra catch-all. Next 16 can let the
root optional catch-all shadow a sibling page at the same URL.

```tsx filename="app/[[...mdxPath]]/page.tsx"
import { ExportBook } from "@/lib/nextra-export";
import { loadDocsExportPages } from "@/lib/docs-export";

async function PdfExportPage() {
  const pages = await loadDocsExportPages();

  return (
    <ExportBook
      title="My Docs"
      siteHref="https://docs.example.com"
      siteLabel="docs.example.com"
      pages={pages.map(({ Content, route, sectionPath, title }) => ({
        content: <Content />,
        route,
        sectionPath,
        title,
      }))}
    />
  );
}

export default async function Page({ params }) {
  const { mdxPath } = await params;
  if (mdxPath?.join("/") === "export/pdf") return <PdfExportPage />;

  // Keep the site's existing importPage(...) implementation here.
}
```

If the Nextra theme layout wraps the catch-all route, return `children` directly
from that layout for `export/pdf`. This keeps the navbar and sidebar out of the
book.

Add the button to the Nextra navbar:

```tsx
import { ExportPdfButton } from "@/lib/nextra-export";

<Navbar logo={logo}>
  <ExportPdfButton />
</Navbar>;
```

The button opens the complete book and starts the browser print flow after its
fonts and images settle. Choose **Save as PDF** to create one bundled file.
The table of contents uses internal PDF jumps. Links inside exported pages are
rewritten to canonical absolute URLs and printed in full after their labels, so
they remain usable in strict PDF viewers and on paper. `siteHref` sets that
canonical origin and adds the documentation link to the cover.

## Add the Markdown route

Create a route handler outside the Nextra catch-all:

```ts filename="app/api/export/markdown/route.ts"
import { exportMarkdownBundle } from "@/lib/nextra-export";
import { loadDocsExportPages } from "@/lib/docs-export";

export async function GET() {
  const markdown = await exportMarkdownBundle({
    title: "My Docs",
    pages: await loadDocsExportPages(),
    siteUrl: "https://docs.example.com",
  });

  return new Response(markdown, {
    headers: {
      "Content-Disposition": 'attachment; filename="my-docs.md"',
      "Content-Type": "text/markdown; charset=utf-8",
    },
  });
}
```

Internal page links become anchors in the bundled document. Frontmatter and MDX
module statements are removed.

## Render custom MDX components

Pass a renderer for each project-specific component:

```ts
import type { ComponentRendererRegistry } from "@/lib/nextra-export";

export const components: ComponentRendererRegistry = {
  ProductCard: ({ attributes }) =>
    `**${attributes.title}** — [Open product](${attributes.href})`,
  Diagram: async ({ attributes }) =>
    `![${attributes.alt}](${await renderDiagramImage(attributes)})`,
};
```

Renderers receive literal attributes, normalized Markdown children, and the
current page. They may return text, tables, code, links, or generated image
URLs. Unregistered components keep their children and produce a labeled
fallback instead of disappearing.

Pass the registry as `components` to `exportMarkdownBundle`.



[View the live page](https://registry.circle.health/utilities/nextra-export)

---

<a id="page-utilities-analytics"></a>

<!-- source: /utilities/analytics -->

# Analytics

Provider-neutral events and tag-manager bridge.

## Install

```bash
pnpm dlx shadcn@latest add @utilities/analytics
```

## Use

```tsx
import { trackEvent } from "@/lib/analytics";

trackEvent("booking_started", {
  serviceId,
  source: "service-page",
});
```



[View the live page](https://registry.circle.health/utilities/analytics)

---

<a id="page-utilities-circle-mixpanel-config"></a>

<!-- source: /utilities/circle-mixpanel-config -->

# Circle Mixpanel Config

Deferred [Google Tag Manager](https://marketingplatform.google.com/about/tag-manager/) and [Mixpanel](https://mixpanel.com/) initialization for Circle Health web apps.

## Install

```bash
pnpm dlx shadcn@latest add @utilities/circle-mixpanel-config
```

## Use

```tsx
import { CircleMixpanelConfig } from "@circle/ui";
```

**Registry dependencies:** `@utilities/analytics`, `@utilities/use-analytics`



[View the live page](https://registry.circle.health/utilities/circle-mixpanel-config)

---

<a id="page-utilities-pii-mask"></a>

<!-- source: /utilities/pii-mask -->

# CircleOS PII Mask

The script masks tagged names, emails, phone numbers, birth dates, addresses, and avatars. Paste it into DevTools before screenshots or recordings. It installs `window.__circlePiiMask`, applies masking immediately, and watches for remounts.

### Mark sensitive elements

```html
<div data-pii="name">Jane Doe</div>
<div data-pii="email">jane@example.com</div>
<div data-pii="phone">+49 1512 1234567</div>
<div data-pii="dob">23.09.1988 (37)</div>
<div data-pii="address">Rosenthaler Str. 16\n10119 Berlin</div>
<img data-pii="avatar" src="/avatar.jpg" alt="" />
```

### Runtime API

```js
window.__circlePiiMask.mask();
window.__circlePiiMask.unmask();
window.__circlePiiMask.toggle();
window.__circlePiiMask.destroy();
```

### Full script

```js
(() => {
  if (window.__circlePiiMask?.destroy) {
    window.__circlePiiMask.destroy();
  }

  const CONFIG = {
    selector: "[data-pii]",
    avatarMode: "blur", // "blur" | "hide"
  };

  const NAME_SEEDS = [
    "Anna Schmidt",
    "Lukas Müller",
    "Sophie Fischer",
    "Paul Weber",
    "Leonie Wagner",
    "Jonas Becker",
    "Marie Hoffmann",
    "Felix Schäfer",
    "Clara Koch",
    "Noah Bauer",
    "Emma Richter",
    "Ben Klein",
    "Mila Wolf",
    "Leon Neumann",
    "Hannah Schwarz",
    "Finn Zimmermann",
    "Laura Braun",
    "Elias Krüger",
    "Johanna Hartmann",
    "Mats Lange",
    "Nele Werner",
    "David Schmitz",
    "Ida Krause",
    "Tom Meier",
    "Lina Lehmann",
    "Moritz Schulz",
    "Greta Maier",
    "Oskar Kraus",
    "Pia Keller",
    "Anton Herrmann",
    "Theresa König",
    "Julian Walter",
    "Mira Mayer",
    "Vincent Huber",
    "Paula Kaiser",
    "Emil Fuchs",
    "Amelie Peters",
    "Nico Lang",
    "Frieda Scholz",
    "Samuel Möller",
    "Lotta Weiß",
    "Maximilian Jung",
    "Carla Pohl",
    "Theo Simon",
    "Elisa Franke",
    "Jakob Albrecht",
    "Nina Vogt",
    "Henry Winter",
    "Jule Seidel",
    "Marlon Graf",
  ];

  const STREET_SEEDS = [
    "Lindenstrasse 12",
    "Bergweg 8",
    "Gartenallee 27",
    "Sonnenweg 14",
    "Feldstrasse 33",
    "Am Park 5",
    "Wiesenweg 19",
    "Birkenallee 42",
    "Bahnhofstrasse 11",
    "Mozartstrasse 24",
  ];

  const CITY_SEEDS = [
    ["10115", "Berlin"],
    ["20095", "Hamburg"],
    ["80331", "Muenchen"],
    ["50667", "Koeln"],
    ["60311", "Frankfurt am Main"],
    ["01067", "Dresden"],
    ["70173", "Stuttgart"],
    ["04109", "Leipzig"],
    ["28195", "Bremen"],
    ["30159", "Hannover"],
  ];

  const textStore = new WeakMap();
  const avatarStore = new WeakMap();

  let observer = null;
  let masked = false;
  let applying = false;
  let scheduled = false;

  const transliterate = (value) =>
    String(value)
      .normalize("NFKD")
      .replace(/[\u0300-\u036f]/g, "")
      .replace(/ä/g, "ae")
      .replace(/ö/g, "oe")
      .replace(/ü/g, "ue")
      .replace(/Ä/g, "Ae")
      .replace(/Ö/g, "Oe")
      .replace(/Ü/g, "Ue")
      .replace(/ß/g, "ss")
      .replace(/[^a-zA-Z0-9]+/g, "-")
      .replace(/^-+|-+$/g, "")
      .toLowerCase();

  const hashString = (value) => {
    let hash = 2166136261;
    for (let index = 0; index < value.length; index += 1) {
      hash ^= value.charCodeAt(index);
      hash = Math.imul(hash, 16777619);
    }
    return hash >>> 0;
  };

  const pickSeedName = (source) => {
    const index = hashString(source || "person") % NAME_SEEDS.length;
    return NAME_SEEDS[index];
  };

  const fakeEmail = (source) => {
    const [firstName, ...rest] = pickSeedName(source).split(" ");
    const lastName = rest.join(" ");
    const suffix = String((hashString(`email:${source}`) % 90) + 10);
    return `${transliterate(firstName)}.${transliterate(lastName)}${suffix}@beispiel.invalid`;
  };

  const fakePhone = (source) => {
    const prefixes = [
      "1512",
      "1523",
      "1550",
      "1577",
      "1590",
      "1602",
      "1704",
      "1718",
      "1726",
      "1763",
    ];
    const hash = hashString(`phone:${source}`);
    const prefix = prefixes[hash % prefixes.length];
    const tail = String(1000000 + (hash % 9000000));
    return `+49 ${prefix.slice(0, 3)} ${prefix.slice(3)} ${tail.slice(0, 3)} ${tail.slice(3)}`;
  };

  const fakeDob = (source) => {
    const day = String((hashString(`dob-day:${source}`) % 28) + 1).padStart(
      2,
      "0",
    );
    const month = String((hashString(`dob-month:${source}`) % 12) + 1).padStart(
      2,
      "0",
    );
    const year = String(1960 + (hashString(`dob-year:${source}`) % 40));
    const age = new Date().getFullYear() - Number(year);
    return `${day}.${month}.${year} (${age})`;
  };

  const fakeAddress = (source) => {
    const street =
      STREET_SEEDS[hashString(`street:${source}`) % STREET_SEEDS.length];
    const [postalCode, city] =
      CITY_SEEDS[hashString(`city:${source}`) % CITY_SEEDS.length];
    return `${street}\n${postalCode}, ${city}, DE`;
  };

  const getText = (element) => {
    if (
      element instanceof HTMLInputElement ||
      element instanceof HTMLTextAreaElement
    ) {
      return element.value;
    }
    return element.textContent || "";
  };

  const setText = (element, value) => {
    if (
      element instanceof HTMLInputElement ||
      element instanceof HTMLTextAreaElement
    ) {
      element.value = value;
      return;
    }
    element.textContent = value;
  };

  const maskTextValue = (kind, original) => {
    const clean = String(original || "").trim();
    if (!clean) return clean;

    if (kind === "name") return pickSeedName(clean);
    if (kind === "email") return fakeEmail(clean);
    if (kind === "phone") return fakePhone(clean);
    if (kind === "dob") return fakeDob(clean);
    if (kind === "address") return fakeAddress(clean);
    return clean;
  };

  const rememberText = (element) => {
    if (!textStore.has(element)) {
      textStore.set(element, getText(element));
    }
    return textStore.get(element);
  };

  const rememberAvatar = (element) => {
    if (!avatarStore.has(element)) {
      avatarStore.set(element, {
        filter: element.style.filter,
        opacity: element.style.opacity,
        transform: element.style.transform,
        transition: element.style.transition,
        pointerEvents: element.style.pointerEvents,
      });
    }
    return avatarStore.get(element);
  };

  const maskAvatar = (element) => {
    rememberAvatar(element);
    element.style.transition =
      "filter 120ms ease, opacity 120ms ease, transform 120ms ease";
    element.style.pointerEvents = "none";

    if (CONFIG.avatarMode === "hide") {
      element.style.opacity = "0.08";
      element.style.filter = "grayscale(1) brightness(1.15)";
      element.style.transform = "scale(1.02)";
      return;
    }

    element.style.opacity = "1";
    element.style.filter =
      "blur(12px) grayscale(1) saturate(0) brightness(1.05)";
    element.style.transform = "scale(1.08)";
  };

  const unmaskAvatar = (element) => {
    const original = avatarStore.get(element);
    if (!original) return;
    element.style.filter = original.filter;
    element.style.opacity = original.opacity;
    element.style.transform = original.transform;
    element.style.transition = original.transition;
    element.style.pointerEvents = original.pointerEvents;
  };

  const maskElement = (element) => {
    const kind = element.getAttribute("data-pii");
    if (!kind) return;

    if (kind === "avatar") {
      maskAvatar(element);
      element.setAttribute("data-pii-masked", "true");
      return;
    }

    const original = rememberText(element);
    setText(element, maskTextValue(kind, original));
    if (kind === "address") {
      element.style.whiteSpace = "pre-line";
    }
    element.setAttribute("data-pii-masked", "true");
  };

  const unmaskElement = (element) => {
    const kind = element.getAttribute("data-pii");
    if (!kind) return;

    if (kind === "avatar") {
      unmaskAvatar(element);
      element.removeAttribute("data-pii-masked");
      return;
    }

    if (!textStore.has(element)) return;
    setText(element, textStore.get(element));
    if (kind === "address") {
      element.style.whiteSpace = "";
    }
    element.removeAttribute("data-pii-masked");
  };

  const applyMask = (root = document) => {
    applying = true;
    root.querySelectorAll(CONFIG.selector).forEach(maskElement);
    applying = false;
  };

  const applyUnmask = (root = document) => {
    applying = true;
    root
      .querySelectorAll(`${CONFIG.selector}[data-pii-masked="true"]`)
      .forEach(unmaskElement);
    applying = false;
  };

  const scheduleRefresh = () => {
    if (!masked || scheduled) return;
    scheduled = true;
    requestAnimationFrame(() => {
      scheduled = false;
      applyMask(document);
    });
  };

  const startObserver = () => {
    if (observer) return;

    observer = new MutationObserver(() => {
      if (!applying) scheduleRefresh();
    });

    observer.observe(document.body, {
      childList: true,
      subtree: true,
      characterData: true,
      attributes: true,
    });
  };

  const stopObserver = () => {
    if (!observer) return;
    observer.disconnect();
    observer = null;
  };

  const api = {
    config: CONFIG,
    mask() {
      masked = true;
      applyMask(document);
      startObserver();
      console.info("PII masking enabled");
    },
    unmask() {
      masked = false;
      stopObserver();
      applyUnmask(document);
      console.info("PII masking disabled");
    },
    toggle() {
      if (masked) this.unmask();
      else this.mask();
    },
    destroy() {
      this.unmask();
      delete window.__circlePiiMask;
    },
  };

  window.__circlePiiMask = api;
  api.mask();
})();
```



[View the live page](https://registry.circle.health/utilities/pii-mask)

---

<a id="page-sample-data"></a>

<!-- source: /sample-data -->

# Lab Reports

All patient data is synthetic. Regenerate the PDFs with
`pnpm sample-data:generate`; edit `scripts/sample-data/config.mjs` to change the
generated mix.

## MVZ Labormedizin Mitte

### 01 · Sofia Rossi

**Normal** · German · A4 · Accession `MVZ-2026-80109`

- Collected: 2026-05-22
- Reported: 2026-05-26
- Panels: Diabetes Monitoring (HbA1c), Vitamin & Iron Status

[Download the sample PDF](/sample-data/lab-reports/01-mvz-labormedizin-mitte-sofia-rossi-hba1c-vitd.pdf)

### 02 · Robert Becker

**Critical** · German · A4 · Accession `MVZ-2026-98064`

- Collected: 2026-05-18
- Reported: 2026-05-21
- Panels: Lipid Panel, Inflammation Markers

[Download the sample PDF](/sample-data/lab-reports/02-mvz-labormedizin-mitte-robert-becker-lipid-inflam.pdf)

### 10 · Lukas Müller

**Normal** · German · A4 · Accession `MVZ-2026-68120`

- Collected: 2026-03-09
- Reported: 2026-03-12
- Panels: Vitamin & Iron Status

[Download the sample PDF](/sample-data/lab-reports/10-mvz-labormedizin-mitte-lukas-muller-vitd.pdf)

### 22 · Anna Schmidt

**Abnormal** · German · A4 · Accession `MVZ-2026-40817`

- Collected: 2026-04-11
- Reported: 2026-04-15
- Panels: Vitamin & Iron Status

[Download the sample PDF](/sample-data/lab-reports/22-mvz-labormedizin-mitte-anna-schmidt-vitd.pdf)

## Labor Berlin Diagnostics

### 03 · Sofia Rossi

**Normal** · German · A4 · Accession `BLN-2026-97142`

- Collected: 2026-05-08
- Reported: 2026-05-10
- Panels: Vitamin & Iron Status

[Download the sample PDF](/sample-data/lab-reports/03-labor-berlin-diagnostics-sofia-rossi-vitd.pdf)

### 08 · Lukas Müller

**Abnormal** · German · A4 · Accession `BLN-2026-84587`

- Collected: 2026-04-05
- Reported: 2026-04-08
- Panels: Coagulation Screen, Vitamin & Iron Status

[Download the sample PDF](/sample-data/lab-reports/08-labor-berlin-diagnostics-lukas-muller-coag-vitd.pdf)

### 12 · Lukas Müller

**Abnormal** · German · A4 · Accession `BLN-2026-62015`

- Collected: 2026-05-28
- Reported: 2026-06-01
- Panels: Thyroid Function

[Download the sample PDF](/sample-data/lab-reports/12-labor-berlin-diagnostics-lukas-muller-thyr.pdf)

## Northbridge Clinical Laboratory

### 04 · Lukas Müller

**Abnormal** · English · A4 · Accession `NHS-2026-40629`

- Collected: 2026-02-22
- Reported: 2026-02-26
- Panels: Lipid Panel, Complete Blood Count (CBC)

[Download the sample PDF](/sample-data/lab-reports/04-northbridge-clinical-laboratory-lukas-muller-lipid-cbc.pdf)

### 13 · Robert Becker

**Abnormal** · English · A4 · Accession `NHS-2026-40857`

- Collected: 2026-02-22
- Reported: 2026-02-24
- Panels: Inflammation Markers

[Download the sample PDF](/sample-data/lab-reports/13-northbridge-clinical-laboratory-robert-becker-inflam.pdf)

### 19 · Robert Becker

**Abnormal** · English · A4 · Accession `NHS-2026-49618`

- Collected: 2026-05-08
- Reported: 2026-05-12
- Panels: Inflammation Markers

[Download the sample PDF](/sample-data/lab-reports/19-northbridge-clinical-laboratory-robert-becker-inflam.pdf)

## QuestPoint Diagnostics

### 05 · Anna Schmidt

**Abnormal** · English · US Letter · Accession `QST-2026-89814`

- Collected: 2026-06-01
- Reported: 2026-06-03
- Panels: Complete Blood Count (CBC), Vitamin & Iron Status

[Download the sample PDF](/sample-data/lab-reports/05-questpoint-diagnostics-anna-schmidt-cbc-vitd.pdf)

### 07 · Lukas Müller

**Normal** · English · US Letter · Accession `QST-2026-39665`

- Collected: 2026-04-24
- Reported: 2026-04-29
- Panels: Vitamin & Iron Status

[Download the sample PDF](/sample-data/lab-reports/07-questpoint-diagnostics-lukas-muller-vitd.pdf)

### 15 · James Okafor

**Abnormal** · English · US Letter · Accession `QST-2026-29730`

- Collected: 2026-05-07
- Reported: 2026-05-10
- Panels: Comprehensive Metabolic Panel

[Download the sample PDF](/sample-data/lab-reports/15-questpoint-diagnostics-james-okafor-metab.pdf)

### 17 · Fatima El-Amin

**Normal** · English · US Letter · Accession `QST-2026-35196`

- Collected: 2026-05-01
- Reported: 2026-05-04
- Panels: Coagulation Screen

[Download the sample PDF](/sample-data/lab-reports/17-questpoint-diagnostics-fatima-el-amin-coag.pdf)

### 21 · Robert Becker

**Abnormal** · English · US Letter · Accession `QST-2026-29430`

- Collected: 2026-03-14
- Reported: 2026-03-16
- Panels: Inflammation Markers, Comprehensive Metabolic Panel

[Download the sample PDF](/sample-data/lab-reports/21-questpoint-diagnostics-robert-becker-inflam-metab.pdf)

## Synevo Labs

### 06 · Noah Andersson

**Critical** · English · A4 · Accession `SYN-2026-19468`

- Collected: 2026-03-25
- Reported: 2026-03-29
- Panels: Coagulation Screen, Vitamin & Iron Status

[Download the sample PDF](/sample-data/lab-reports/06-synevo-labs-noah-andersson-coag-vitd.pdf)

### 09 · Noah Andersson

**Abnormal** · English · A4 · Accession `SYN-2026-37784`

- Collected: 2026-02-28
- Reported: 2026-03-04
- Panels: Inflammation Markers

[Download the sample PDF](/sample-data/lab-reports/09-synevo-labs-noah-andersson-inflam.pdf)

### 11 · Sofia Rossi

**Abnormal** · English · A4 · Accession `SYN-2026-60192`

- Collected: 2026-05-15
- Reported: 2026-05-20
- Panels: Lipid Panel, Inflammation Markers, Comprehensive Metabolic Panel

[Download the sample PDF](/sample-data/lab-reports/11-synevo-labs-sofia-rossi-lipid-inflam-metab.pdf)

### 14 · Mei Lin Tan

**Critical** · English · A4 · Accession `SYN-2026-52760`

- Collected: 2026-05-02
- Reported: 2026-05-04
- Panels: Vitamin & Iron Status, Complete Blood Count (CBC)

[Download the sample PDF](/sample-data/lab-reports/14-synevo-labs-mei-lin-tan-vitd-cbc.pdf)

### 20 · Fatima El-Amin

**Abnormal** · English · A4 · Accession `SYN-2026-76246`

- Collected: 2026-05-04
- Reported: 2026-05-09
- Panels: Lipid Panel, Inflammation Markers, Coagulation Screen

[Download the sample PDF](/sample-data/lab-reports/20-synevo-labs-fatima-el-amin-lipid-inflam-coag.pdf)

### 23 · Fatima El-Amin

**Abnormal** · English · A4 · Accession `SYN-2026-63482`

- Collected: 2026-02-21
- Reported: 2026-02-23
- Panels: Thyroid Function, Coagulation Screen, Vitamin & Iron Status

[Download the sample PDF](/sample-data/lab-reports/23-synevo-labs-fatima-el-amin-thyr-coag-vitd.pdf)

## Amedes Genetics & Lab

### 16 · James Okafor

**Normal** · German · A4 · Accession `AMD-2026-38168`

- Collected: 2026-03-21
- Reported: 2026-03-25
- Panels: Vitamin & Iron Status, Complete Blood Count (CBC), Inflammation Markers

[Download the sample PDF](/sample-data/lab-reports/16-amedes-genetics-lab-james-okafor-vitd-cbc-inflam.pdf)

### 18 · Mei Lin Tan

**Abnormal** · German · A4 · Accession `AMD-2026-15382`

- Collected: 2026-05-19
- Reported: 2026-05-23
- Panels: Inflammation Markers, Coagulation Screen, Lipid Panel

[Download the sample PDF](/sample-data/lab-reports/18-amedes-genetics-lab-mei-lin-tan-inflam-coag-lipid.pdf)

### 24 · James Okafor

**Normal** · German · A4 · Accession `AMD-2026-15674`

- Collected: 2026-02-27
- Reported: 2026-03-03
- Panels: Vitamin & Iron Status, Coagulation Screen, Inflammation Markers

[Download the sample PDF](/sample-data/lab-reports/24-amedes-genetics-lab-james-okafor-vitd-coag-inflam.pdf)



[View the live page](https://registry.circle.health/sample-data)

---

<a id="page-sample-data-intake-forms"></a>

<!-- source: /sample-data/intake-forms -->

# Intake Forms

All patient data is synthetic. Regenerate the PDFs with
`pnpm sample-data:generate`.

## Praxis Kessler — Initial Naturopathy Intake

### Anna Schmidt

**Ready** · German · Naturopathy Consultation

- Patient ID: `PAT-100482`
- Appointment: 90 min initial consultation
- Submitted: 2026-06-04

[Download the sample PDF](/sample-data/intake-forms/praxis-kessler-initial-naturopathy-intake-anna-schmidt.pdf)

### Blank template

**Blank** · German · Naturopathy Consultation

- Appointment: 90 min initial consultation

[Download the sample PDF](/sample-data/intake-forms/praxis-kessler-initial-naturopathy-intake-blank.pdf)

## Decades — Preventive Diagnostics Intake

### Robert Becker

**Signed** · English · Full Body Diagnostic

- Patient ID: `PAT-100958`
- Appointment: One-day diagnostics program
- Submitted: 2026-06-07

[Download the sample PDF](/sample-data/intake-forms/decades-preventive-diagnostics-intake-robert-becker.pdf)

### Blank template

**Blank** · English · Full Body Diagnostic

- Appointment: One-day diagnostics program

[Download the sample PDF](/sample-data/intake-forms/decades-preventive-diagnostics-intake-blank.pdf)

## Soma Studio — Movement and Rehab Intake

### Sofia Rossi

**Ready** · English · Movement Analysis

- Patient ID: `PAT-100644`
- Appointment: In-person assessment plus home plan
- Submitted: 2026-06-05

[Download the sample PDF](/sample-data/intake-forms/soma-studio-movement-and-rehab-intake-sofia-rossi.pdf)

### Blank template

**Blank** · English · Movement Analysis

- Appointment: In-person assessment plus home plan

[Download the sample PDF](/sample-data/intake-forms/soma-studio-movement-and-rehab-intake-blank.pdf)



[View the live page](https://registry.circle.health/sample-data/intake-forms)

---

<a id="page-sample-data-brands-overview"></a>

<!-- source: /sample-data/brands/overview -->

# Brands overview

Illustrative customer brands for Circle Health product demos and marketing. Each
profile combines a visual identity, service model, staff, and representative
circleOS configuration.

- [**Praxis Kessler**](#page-sample-data-brands-praxis-kessler) — A one-woman naturopathic practice in Hamburg, built on long-term patient relationships.
- [**Decades**](#page-sample-data-brands-decades) — Preventive medicine clinics catching serious illness years before it becomes a problem.
- [**Soma Studio**](#page-sample-data-brands-soma-studio) — A boutique Munich studio treating physical health as structural, functional, and personal.

## API

The brands endpoint exposes the same synthetic profiles.

| Method and path           | Returns                                     |
| ------------------------- | ------------------------------------------- |
| `GET /api/brands`         | `{ count, brands }` for all demo brands.    |
| `GET /api/brands?id=<id>` | One brand, or `404` when the id is unknown. |

Valid ids are `praxis-kessler`, `decades`, and `soma-studio`.

```ts
const { brands } = await fetch(
  "https://registry.circle.health/api/brands",
).then((response) => response.json());

const decades = await fetch(
  "https://registry.circle.health/api/brands?id=decades",
).then((response) => response.json());
```

### Brand shape

```ts
type Brand = {
  id: string;
  name: string;
  type: string;
  shortDescription: string;
  description: string;
  website: string;
  offering: string;
  logo: string;
  initials: string;
  typeface: string;
  palette: {
    bg: string;
    primary: string;
    accent: string;
    ink: string;
    onPrimary: string;
  };
  locations: string[];
  services: Array<{
    name: string;
    description: string;
    icon: string;
    image: string;
  }>;
  staff: Array<{
    name: string;
    role: string;
    image: string;
  }>;
  personality: string;
  circleOS: {
    used: string[];
    notUsed: string[];
  };
};
```

Image fields are absolute URLs against the request origin.

### Sample-data manifest

`GET /sample-data/manifest.json` returns the generated PDF inventory used by the
lab-report and intake-form galleries.



[View the live page](https://registry.circle.health/sample-data/brands/overview)

---

<a id="page-sample-data-brands-praxis-kessler"></a>

<!-- source: /sample-data/brands/praxis-kessler -->

# Praxis Kessler

## Overview

**Praxis Kessler** — Naturopathy, acupuncture & herbal medicine

A one-woman naturopathic practice in Hamburg, built on long-term patient relationships.

## Profile

### About

A one-woman naturopathic practice run by Nora Kessler in Hamburg-Eimsbüttel, opened in 2017. Nora sees 12 to 15 patients per week, mostly long-term regulars seeking complements to conventional medicine. She is methodical, warm, and skeptical of tech but uses circleOS because it helps her manage every part of her business in one place.

### Personality

Traditional and relationship-first. She knows most of her patients by name and has been treating some for years. She is not interested in growth for its own sake — what matters is doing good work and running a calm, orderly practice.

### Locations

- Hamburg-Eimsbüttel

## Brand identity

### Logo

![Praxis Kessler logo](/brands/logos/kessler.png)

### Typography

**Typeface:** Jost / Inter

### Website

[praxis-kessler.de](https://praxis-kessler.de)

### Colour palette

- **bg:** `#F5F0EB`
- **primary:** `#9C8B7E`
- **accent:** `#6F5F52`
- **ink:** `#25201C`
- **onPrimary:** `#FFFFFF`

## Team

- **Nora Kessler:** Heilpraktikerin & Founder
- **Practice Admin:** Part-time administrator

## Services

- **Naturopathy Consultation:** 90 min initial deep-dive, 45 min follow-ups.
- **Acupuncture:** Pain, stress, and hormonal balance.
- **Manual Therapy:** Lymphatic drainage and gentle bodywork.
- **Nutritional Counselling:** Gut health, elimination diets, and metabolic balance.
- **Phytotherapy:** Plant-based prescriptions and herbal protocols.

## circleOS configuration

### In use

- Online booking for new and returning patients
- Intake forms sent via SMS before first appointments
- Appointment reminders by email and SMS
- Invoicing and billing for private patients
- Patient notes and long-term tracking per patient
- Plans for recurring treatment programs

### Not used

- Multi-location configuration (single location only)
- Staff roles and permissions (solo practitioner)
- Lab order management (no diagnostic services)

## API

### Fetch this brand

```bash
curl https://registry.circle.health/api/brands?id=praxis-kessler
```

### Complete profile

The complete profile below includes every value shown on this page. Copy it as
JSON for fixtures, prompts, or prototypes.

```json
{
  "id": "praxis-kessler",
  "name": "Praxis Kessler",
  "type": "Small solo Heilpraktiker",
  "shortDescription": "A one-woman naturopathic practice in Hamburg, built on long-term patient relationships.",
  "description": "A one-woman naturopathic practice run by Nora Kessler in Hamburg-Eimsbüttel, opened in 2017. Nora sees 12 to 15 patients per week, mostly long-term regulars seeking complements to conventional medicine. She is methodical, warm, and skeptical of tech but uses circleOS because it helps her manage every part of her business in one place.",
  "website": "praxis-kessler.de",
  "offering": "Naturopathy, acupuncture & herbal medicine",
  "logo": "/brands/logos/kessler.png",
  "initials": "NK",
  "typeface": "Jost / Inter",
  "palette": {
    "bg": "#F5F0EB",
    "primary": "#9C8B7E",
    "accent": "#6F5F52",
    "ink": "#25201C",
    "onPrimary": "#FFFFFF"
  },
  "locations": [
    "Hamburg-Eimsbüttel"
  ],
  "services": [
    {
      "name": "Naturopathy Consultation",
      "description": "90 min initial deep-dive, 45 min follow-ups.",
      "icon": "consultation",
      "image": "/brands/kessler/services/naturopathy.png"
    },
    {
      "name": "Acupuncture",
      "description": "Pain, stress, and hormonal balance.",
      "icon": "needle",
      "image": "/brands/kessler/services/acupuncture.png"
    },
    {
      "name": "Manual Therapy",
      "description": "Lymphatic drainage and gentle bodywork.",
      "icon": "hands",
      "image": "/brands/kessler/services/manual-therapy.png"
    },
    {
      "name": "Nutritional Counselling",
      "description": "Gut health, elimination diets, and metabolic balance.",
      "icon": "nutrition",
      "image": "/brands/kessler/services/nutrition.png"
    },
    {
      "name": "Phytotherapy",
      "description": "Plant-based prescriptions and herbal protocols.",
      "icon": "herb",
      "image": "/brands/kessler/services/phytotherapy.png"
    }
  ],
  "staff": [
    {
      "name": "Nora Kessler",
      "role": "Heilpraktikerin & Founder",
      "image": "/brands/kessler/staff/nora-kessler.png"
    },
    {
      "name": "Practice Admin",
      "role": "Part-time administrator",
      "image": "/brands/kessler/staff/admin.png"
    }
  ],
  "personality": "Traditional and relationship-first. She knows most of her patients by name and has been treating some for years. She is not interested in growth for its own sake — what matters is doing good work and running a calm, orderly practice.",
  "circleOS": {
    "used": [
      "Online booking for new and returning patients",
      "Intake forms sent via SMS before first appointments",
      "Appointment reminders by email and SMS",
      "Invoicing and billing for private patients",
      "Patient notes and long-term tracking per patient",
      "Plans for recurring treatment programs"
    ],
    "notUsed": [
      "Multi-location configuration (single location only)",
      "Staff roles and permissions (solo practitioner)",
      "Lab order management (no diagnostic services)"
    ]
  }
}
```



[View the live page](https://registry.circle.health/sample-data/brands/praxis-kessler)

---

<a id="page-sample-data-brands-decades"></a>

<!-- source: /sample-data/brands/decades -->

# Decades

## Overview

**Decades** — Whole-body diagnostics & longevity programs

Preventive medicine clinics catching serious illness years before it becomes a problem.

## Profile

### About

Founded in Berlin in 2019, Decades is a preventive medicine clinic built on the belief that most serious illness is detectable years before it becomes a problem. Named for the decade of life you can add by catching things early, the company offers comprehensive diagnostic programs combining imaging, lab analysis, and physician consultations. The brand is precise, authoritative, and understated.

### Personality

Clinical but not cold. Decades speaks to people who treat their health like an investment. The tone is direct, evidence-based, and confident. No wellness language, no motivational copy.

### Locations

- Berlin (HQ)
- Hamburg
- Munich
- Frankfurt
- Cologne

## Brand identity

### Logo

![Decades logo](/brands/logos/decades.png)

### Typography

**Typeface:** Tenor Sans / Inter

### Website

[decades.health](https://decades.health)

### Colour palette

- **bg:** `#F1F4F2`
- **primary:** `#1C453E`
- **accent:** `#5A7870`
- **ink:** `#1C2B27`
- **onPrimary:** `#FFFFFF`

## Team

- **Dr. Jonas Weiß:** Medical Director
- **Dr. Lena Brandt:** Lead Radiologist
- **Location Medical Leads:** One per city (5)
- **Coordination Teams:** Per-location admin & ops

## Services

- **Full Body Diagnostic:** Whole-body MRI, blood panel, and physician debrief in one day.
- **Cardiovascular Screening:** ECG, echocardiography, arterial stiffness, and lipid analysis.
- **Cancer Early Detection:** Multi-cancer blood test combined with targeted imaging.
- **Metabolic & Hormonal Analysis:** Comprehensive lab workup with dietary and lifestyle follow-up.
- **Annual Membership Program:** Yearly reassessment, result tracking, and priority scheduling.

## circleOS configuration

### In use

- Multi-location scheduling across 5 cities with capacity management
- Patient profiles with full diagnostic history and longitudinal tracking
- Lab order management and result documentation
- Standardized intake and consent forms before each appointment
- AI briefings before physician consultations
- Patient portal for results, follow-ups, and secure messaging
- Invoicing for private-pay diagnostic packages
- Staff roles and permissions per location
- Reporting analytics across locations

### Not used

- Single-practitioner workflows (operates as a team)
- SMS-based intake (all forms go through the patient portal)
- Supplement prescriptions

## API

### Fetch this brand

```bash
curl https://registry.circle.health/api/brands?id=decades
```

### Complete profile

The complete profile below includes every value shown on this page. Copy it as
JSON for fixtures, prompts, or prototypes.

```json
{
  "id": "decades",
  "name": "Decades",
  "type": "Multi-city premium preventive health company",
  "shortDescription": "Preventive medicine clinics catching serious illness years before it becomes a problem.",
  "description": "Founded in Berlin in 2019, Decades is a preventive medicine clinic built on the belief that most serious illness is detectable years before it becomes a problem. Named for the decade of life you can add by catching things early, the company offers comprehensive diagnostic programs combining imaging, lab analysis, and physician consultations. The brand is precise, authoritative, and understated.",
  "website": "decades.health",
  "offering": "Whole-body diagnostics & longevity programs",
  "logo": "/brands/logos/decades.png",
  "initials": "D",
  "typeface": "Tenor Sans / Inter",
  "palette": {
    "bg": "#F1F4F2",
    "primary": "#1C453E",
    "accent": "#5A7870",
    "ink": "#1C2B27",
    "onPrimary": "#FFFFFF"
  },
  "locations": [
    "Berlin (HQ)",
    "Hamburg",
    "Munich",
    "Frankfurt",
    "Cologne"
  ],
  "services": [
    {
      "name": "Full Body Diagnostic",
      "description": "Whole-body MRI, blood panel, and physician debrief in one day.",
      "icon": "scan",
      "image": "/brands/decades/services/full-body.png"
    },
    {
      "name": "Cardiovascular Screening",
      "description": "ECG, echocardiography, arterial stiffness, and lipid analysis.",
      "icon": "heart",
      "image": "/brands/decades/services/cardiovascular.png"
    },
    {
      "name": "Cancer Early Detection",
      "description": "Multi-cancer blood test combined with targeted imaging.",
      "icon": "dna",
      "image": "/brands/decades/services/cancer.png"
    },
    {
      "name": "Metabolic & Hormonal Analysis",
      "description": "Comprehensive lab workup with dietary and lifestyle follow-up.",
      "icon": "lab",
      "image": "/brands/decades/services/metabolic.png"
    },
    {
      "name": "Annual Membership Program",
      "description": "Yearly reassessment, result tracking, and priority scheduling.",
      "icon": "membership",
      "image": "/brands/decades/services/membership.png"
    }
  ],
  "staff": [
    {
      "name": "Dr. Jonas Weiß",
      "role": "Medical Director",
      "image": "/brands/decades/staff/jonas-weiss.png"
    },
    {
      "name": "Dr. Lena Brandt",
      "role": "Lead Radiologist",
      "image": "/brands/decades/staff/lena-brandt.png"
    },
    {
      "name": "Location Medical Leads",
      "role": "One per city (5)"
    },
    {
      "name": "Coordination Teams",
      "role": "Per-location admin & ops"
    }
  ],
  "personality": "Clinical but not cold. Decades speaks to people who treat their health like an investment. The tone is direct, evidence-based, and confident. No wellness language, no motivational copy.",
  "circleOS": {
    "used": [
      "Multi-location scheduling across 5 cities with capacity management",
      "Patient profiles with full diagnostic history and longitudinal tracking",
      "Lab order management and result documentation",
      "Standardized intake and consent forms before each appointment",
      "AI briefings before physician consultations",
      "Patient portal for results, follow-ups, and secure messaging",
      "Invoicing for private-pay diagnostic packages",
      "Staff roles and permissions per location",
      "Reporting analytics across locations"
    ],
    "notUsed": [
      "Single-practitioner workflows (operates as a team)",
      "SMS-based intake (all forms go through the patient portal)",
      "Supplement prescriptions"
    ]
  }
}
```



[View the live page](https://registry.circle.health/sample-data/brands/decades)

---

<a id="page-sample-data-brands-soma-studio"></a>

<!-- source: /sample-data/brands/soma-studio -->

# Soma Studio

## Overview

**Soma Studio** — Osteopathy, sports medicine & rehab

A boutique Munich studio treating physical health as structural, functional, and personal.

## Profile

### About

Co-founded in Munich in 2022 by osteopath Clara Reuter and sports physician Dr. Felix Naumann, Soma Studio is a small practice built around the idea that physical health is structural, functional, and personal. One location in Munich-Schwabing with a growing online program. The studio works with a mix of athletes, desk workers, and people recovering from chronic pain or injury. It is intentionally small.

### Personality

Grounded, precise, and quietly confident. Soma does not market aggressively — its reputation comes from referrals and a loyal patient base. The tone is knowledgeable without being clinical, warm without being soft.

### Locations

- Munich-Schwabing
- Online (Germany-wide video)

## Brand identity

### Logo

![Soma Studio logo](/brands/logos/soma.png)

### Typography

**Typeface:** Poppins / Inter

### Website

[soma-studio.de](https://soma-studio.de)

### Colour palette

- **bg:** `#F3F0EC`
- **primary:** `#0A0A0A`
- **accent:** `#3B6CB5`
- **ink:** `#1C1C1E`
- **onPrimary:** `#FFFFFF`

## Team

- **Clara Reuter:** Osteopath & Co-founder
- **Dr. Felix Naumann:** Sports Physician & Co-founder
- **Associate Physiotherapists:** 3 therapists
- **Studio Coordinator:** Front-of-house & scheduling

## Services

- **Osteopathic Treatment:** Structural assessment and hands-on treatment for pain, posture, and mobility.
- **Sports Medicine Consultation:** Injury assessment, return-to-sport planning, and performance health.
- **Movement Analysis:** Video-based gait and movement screening with exercise prescription.
- **Online Physiotherapy:** Guided video sessions with associate physiotherapists.
- **Quarterly Body MOT:** Combined osteopathy and sports medicine check-in for active patients.

## circleOS configuration

### In use

- Online booking for in-person and video sessions
- Therapist profiles with speciality and availability
- Intake and consent forms before first appointments
- Notes and session documentation per patient
- Movement analysis files attached to patient profiles
- Appointment reminders by email and SMS
- Plans for phased rehabilitation programs
- Patient portal for notes, resources, and follow-up booking
- Invoicing for self-pay and private insurance patients

### Not used

- Multi-location scheduling (one physical location)
- Lab order management (no in-house diagnostics)
- Staff permissions across teams (small flat team)

## API

### Fetch this brand

```bash
curl https://registry.circle.health/api/brands?id=soma-studio
```

### Complete profile

The complete profile below includes every value shown on this page. Copy it as
JSON for fixtures, prompts, or prototypes.

```json
{
  "id": "soma-studio",
  "name": "Soma Studio",
  "type": "Boutique integrative health studio",
  "shortDescription": "A boutique Munich studio treating physical health as structural, functional, and personal.",
  "description": "Co-founded in Munich in 2022 by osteopath Clara Reuter and sports physician Dr. Felix Naumann, Soma Studio is a small practice built around the idea that physical health is structural, functional, and personal. One location in Munich-Schwabing with a growing online program. The studio works with a mix of athletes, desk workers, and people recovering from chronic pain or injury. It is intentionally small.",
  "website": "soma-studio.de",
  "offering": "Osteopathy, sports medicine & rehab",
  "logo": "/brands/logos/soma.png",
  "initials": "S",
  "typeface": "Poppins / Inter",
  "palette": {
    "bg": "#F3F0EC",
    "primary": "#0A0A0A",
    "accent": "#3B6CB5",
    "ink": "#1C1C1E",
    "onPrimary": "#FFFFFF"
  },
  "locations": [
    "Munich-Schwabing",
    "Online (Germany-wide video)"
  ],
  "services": [
    {
      "name": "Osteopathic Treatment",
      "description": "Structural assessment and hands-on treatment for pain, posture, and mobility.",
      "icon": "hands",
      "image": "/brands/soma/services/osteopathy.png"
    },
    {
      "name": "Sports Medicine Consultation",
      "description": "Injury assessment, return-to-sport planning, and performance health.",
      "icon": "consultation",
      "image": "/brands/soma/services/sports-medicine.png"
    },
    {
      "name": "Movement Analysis",
      "description": "Video-based gait and movement screening with exercise prescription.",
      "icon": "movement",
      "image": "/brands/soma/services/movement.png"
    },
    {
      "name": "Online Physiotherapy",
      "description": "Guided video sessions with associate physiotherapists.",
      "icon": "video",
      "image": "/brands/soma/services/physiotherapy.png"
    },
    {
      "name": "Quarterly Body MOT",
      "description": "Combined osteopathy and sports medicine check-in for active patients.",
      "icon": "rehab"
    }
  ],
  "staff": [
    {
      "name": "Clara Reuter",
      "role": "Osteopath & Co-founder",
      "image": "/brands/soma/staff/clara-reuter.png"
    },
    {
      "name": "Dr. Felix Naumann",
      "role": "Sports Physician & Co-founder",
      "image": "/brands/soma/staff/felix-naumann.png"
    },
    {
      "name": "Associate Physiotherapists",
      "role": "3 therapists"
    },
    {
      "name": "Studio Coordinator",
      "role": "Front-of-house & scheduling",
      "image": "/brands/soma/staff/coordinator.png"
    }
  ],
  "personality": "Grounded, precise, and quietly confident. Soma does not market aggressively — its reputation comes from referrals and a loyal patient base. The tone is knowledgeable without being clinical, warm without being soft.",
  "circleOS": {
    "used": [
      "Online booking for in-person and video sessions",
      "Therapist profiles with speciality and availability",
      "Intake and consent forms before first appointments",
      "Notes and session documentation per patient",
      "Movement analysis files attached to patient profiles",
      "Appointment reminders by email and SMS",
      "Plans for phased rehabilitation programs",
      "Patient portal for notes, resources, and follow-up booking",
      "Invoicing for self-pay and private insurance patients"
    ],
    "notUsed": [
      "Multi-location scheduling (one physical location)",
      "Lab order management (no in-house diagnostics)",
      "Staff permissions across teams (small flat team)"
    ]
  }
}
```



[View the live page](https://registry.circle.health/sample-data/brands/soma-studio)