kalei/docs/technical/kalei-user-journey-technica...

28 KiB

Kalei — User Journey Technical Map

Version 1.0 — February 2026 Maps every user-facing flow to backend API endpoints, database operations, frontend components, and AI calls


Architecture Summary

Backend: Fastify 5.x (Node.js 22 LTS), Drizzle ORM, PostgreSQL 16, Redis 7 Frontend: React Native + Expo SDK 54+, Expo Router, TanStack Query v5, Zustand, MMKV v4 AI: Claude Haiku 4.5 (primary), Groq/Qwen fallback, provider-agnostic AI Gateway Auth: JWT with refresh token rotation, Apple/Google SSO Billing: Direct App Store / Google Play webhook integration


1. Authentication & Onboarding

1.1 Account Registration

User Action: Enters email + password or taps Apple/Google SSO

Layer Detail
API POST /auth/register — body: { email, password, provider? }
Validation Zod: email format, password 8+ chars, provider enum
DB Write INSERT INTO users (id, email, password_hash, created_at)
DB Write INSERT INTO profiles (user_id, display_name, coaching_style, onboarding_complete)
DB Write INSERT INTO subscriptions (user_id, plan, status, started_at) — defaults to free
Redis Set rate:auth:{ip} with TTL for brute-force protection
Response { access_token, refresh_token, user: { id, email, profile } }
Frontend AuthStore.setTokens() → MMKV encrypted storage → navigate to onboarding

1.2 Token Refresh

Layer Detail
API POST /auth/refresh — body: { refresh_token }
DB Read SELECT FROM refresh_tokens WHERE token = $1 AND revoked = false
DB Write Revoke old token, issue new pair (rotation)
Redis Invalidate old session cache

1.3 Onboarding Completion

User Action: Completes screens 2-9 (style selection, first Turn)

Layer Detail
API PATCH /me/profile — body: { coaching_style, notification_time, onboarding_complete: true }
DB Write UPDATE profiles SET coaching_style = $1, notification_time = $2, onboarding_complete = true
Push Schedule first notification via push service at chosen time
Frontend OnboardingStore.complete() → navigate to main tab navigator

2. The Turn (Reframing)

2.1 Submit a Turn

User Action: Types thought → taps "Turn it"

Layer Detail
Frontend Validate non-empty input, show Turn animation (1.5s kaleidoscope rotation)
API POST /turns — body: { input_text, coaching_style? }
Rate Check Redis: INCR rate:turns:{user_id}:{date} → reject if > 3 (free) or > 100 (prism)
Entitlement SELECT plan FROM subscriptions WHERE user_id = $1 → gate check
Safety Deterministic keyword scan → if flagged: INSERT INTO safety_events, return crisis response
AI Call AI Gateway → Claude Haiku 4.5: system prompt (coaching style + reframe instructions) + user input
AI Response JSON: { perspectives: [{ style, text, emotion_before, emotion_after }], micro_action: { if_clause, then_clause }, fragments_detected: [{ type, phrase, confidence }] }
DB Write INSERT INTO turns (id, user_id, input_text, perspectives, micro_action, fragments, emotion_vector, created_at)
DB Write INSERT INTO ai_usage_events (user_id, feature, model, input_tokens, output_tokens, latency_ms, cost_usd)
Redis Increment daily counter, update streak cache
Response { turn_id, perspectives, micro_action, fragments, pattern_seed }
Frontend Dismiss animation → render 3 perspective cards + micro-action card

2.2 Save a Turn / Keepsake

User Action: Taps save on a perspective card

Layer Detail
API POST /turns/{id}/save — body: { perspective_index, save_type: "keepsake" }
DB Write UPDATE turns SET saved = true, saved_perspective_index = $1
DB Write INSERT INTO evidence_wall_tiles (user_id, tile_type, source_feature, source_id, color_accent, created_at) — type: saved_keepsake
Response { saved: true, gallery_id, evidence_tile_id }
Frontend Success toast "Turn saved" → update Gallery cache via TanStack Query invalidation

2.3 Get Turn History

Layer Detail
API GET /turns?limit=20&offset=0&date=2026-02-21
DB Read SELECT id, input_text, perspectives, fragments, created_at FROM turns WHERE user_id = $1 ORDER BY created_at DESC LIMIT $2 OFFSET $3
Cache Redis: cache recent 20 turns per user, 5 min TTL

3. The Mirror (Journaling + Fragment Detection)

3.1 Start Mirror Session

User Action: Opens Mirror tab → starts new session

Layer Detail
API POST /mirror/sessions — body: { prompt_id? }
Rate Check Redis: INCR rate:mirror:{user_id}:{week} → reject if > 2 (free)
DB Write INSERT INTO mirror_sessions (id, user_id, status, started_at) — status: active
Response { session_id, opening_prompt }
Frontend Navigate to session view, show opening prompt in AI bubble

3.2 Send Message in Mirror

User Action: Types and sends a message

Layer Detail
Frontend Append user message to local state, show AI thinking animation
API POST /mirror/sessions/{id}/messages — body: { content, message_type: "user" }
DB Write INSERT INTO mirror_messages (id, session_id, role, content, created_at)
AI Call #1 Fragment Detection: system prompt (10 distortion types + detection rules) + session context + new message
AI Response #1 { fragments: [{ type, phrase, start_index, end_index, confidence }] }
Entitlement Gate Free: filter to 3 types (catastrophizing, black_and_white, should_statements). Prism: all 10
DB Write INSERT INTO mirror_fragments (id, session_id, message_id, distortion_type, phrase, start_idx, end_idx, confidence)
AI Call #2 Reflective Response: system prompt (warm, non-directive, Mirror voice) + session history + detected fragments
AI Response #2 { response_text, suggested_prompts: [] }
DB Write INSERT INTO mirror_messages (id, session_id, role: "assistant", content)
Response { message_id, ai_response, fragments: [{ type, phrase, indices }] }
Frontend Render AI response bubble, apply amber highlight underlines to user's message at fragment positions

3.3 Tap Fragment Highlight → Inline Reframe

User Action: Taps highlighted text in their message

Layer Detail
Frontend Open half-sheet modal with distortion info (local data from fragment detection response)
API POST /mirror/fragments/{id}/reframe — body: { fragment_id }
Rate Check Free: 1 inline reframe per session
AI Call Quick reframe: system prompt + fragment context + distortion type
AI Response { reframes: [{ text, style }], can_turn: true }
Response { reframes, turn_prefill }turn_prefill is the fragment phrase ready for Turn
Frontend Render reframes in half-sheet. "Take to Turn" button navigates to Turn tab with input_text pre-filled

3.4 Close Mirror Session → Generate Reflection

User Action: Taps "End Session" or navigates away

Layer Detail
API POST /mirror/sessions/{id}/close
DB Read Fetch all messages + fragments for session
AI Call Reflection generation: full session transcript + all fragments → themes, insight, pattern seed
AI Response { themes: [], fragment_summary: { total, by_type }, insight: "string", pattern_seed: "hash" }
DB Write UPDATE mirror_sessions SET status = "closed", reflection = $1, pattern_seed = $2, closed_at = NOW()
DB Write INSERT INTO evidence_wall_tiles (user_id, tile_type: "mirror_reflection", source_id, ...)
Response { reflection, pattern_seed, fragment_summary }
Frontend Show reflection card with generated kaleidoscope pattern thumbnail → auto-saved to Gallery

4. The Lens (Goals + Actions)

4.1 Create Goal

Layer Detail
API POST /lens/goals — body: { title, description, target_date?, category? }
AI Call Goal refinement: make SMART, suggest metrics, generate initial visualization description
DB Write INSERT INTO lens_goals (id, user_id, title, description, target_date, visualization_text, status, created_at)
Response { goal_id, refined_title, visualization, suggested_actions }

4.2 Get Daily Actions + Affirmation

Layer Detail
API GET /lens/today
DB Read Active goals + incomplete actions + today's affirmation
AI Call (if no affirmation cached) Generate daily affirmation based on active goals + recent progress
Redis Cache today's affirmation, 24h TTL
Response { goals: [...], today_actions: [...], affirmation: { text, goal_id } }

4.3 Complete Action

Layer Detail
API POST /lens/actions/{id}/complete
DB Write UPDATE lens_actions SET completed = true, completed_at = NOW()
DB Write INSERT INTO evidence_wall_tiles (tile_type: "completed_action", source_id, color_accent: "emerald")
Redis Update streak counter
Response { completed: true, goal_progress_pct, streak_count, evidence_tile_id }

4.4 Start Rehearsal Session

User Action: On goal detail → taps "Rehearse"

Layer Detail
API POST /lens/goals/{id}/rehearsal
Rate Check Free: 1/week, Prism: unlimited
DB Read Fetch goal details + user's recent Mirror/Turn data for personalization
AI Call Visualization script generation: first-person, multi-sensory, process-oriented, obstacle rehearsal
AI Response { script_segments: [{ text, duration_seconds, breathing_cue? }], total_duration }
DB Write INSERT INTO rehearsal_sessions (id, user_id, goal_id, script, duration, created_at)
Response { session_id, script_segments, total_duration }
Frontend Enter Rehearsal mode: timer ring, text card sequence with breathing animation pacing

4.5 Complete Rehearsal

Layer Detail
API POST /lens/rehearsals/{id}/complete
DB Write UPDATE rehearsal_sessions SET completed = true, completed_at = NOW()
DB Write INSERT INTO evidence_wall_tiles (tile_type: "rehearsal_complete", source_id, color_accent: "amethyst")
Response { completed: true, evidence_tile_id }
Frontend Success burst animation → navigate back to goal detail

5. The Ritual (Daily Habit Sequences)

5.1 Start Ritual

User Action: Taps "Start Ritual" on Turn tab or from notification

Layer Detail
API POST /rituals/start — body: `{ template: "morning"
Rate Check Free: only quick template allowed
DB Write INSERT INTO ritual_sessions (id, user_id, template, status, started_at)
DB Read Fetch user's active goals, recent fragments, streak data for personalization
AI Call Personalize ritual prompts based on user context
Response { session_id, steps: [{ type, prompt, duration_seconds }] }
Frontend Enter Ritual mode: fragment-shaped step progress bar, step-by-step flow

5.2 Ritual Step Completion

Each step may trigger its own API call:

Step API Calls
Mirror check-in POST /mirror/sessions + POST /mirror/sessions/{id}/messages (lightweight, 1-2 exchanges)
Turn POST /turns (with ritual context flag)
Lens review POST /lens/actions/{id}/complete (for each completed action)
Affirmation GET /lens/today (cached)
Gratitude POST /rituals/{id}/gratitude — body: { text }

5.3 Complete Ritual

Layer Detail
API POST /rituals/{id}/complete
DB Write UPDATE ritual_sessions SET status = "completed", completed_at = NOW()
DB Write INSERT INTO evidence_wall_tiles (tile_type: "ritual_complete", color_accent: "amber")
Redis Update ritual streak: INCR streak:ritual:{user_id}, check context consistency (same time ± 30 min)
DB Write UPDATE profiles SET ritual_streak = $1, ritual_consistency_score = $2
Response { completed: true, streak_count, consistency_score, evidence_tile_id }
Frontend Prismatic ring completion animation → streak view

6.1 Get All Patterns

Layer Detail
API GET /gallery?view=all&limit=20&offset=0
DB Read SELECT id, source_feature, pattern_seed, preview_text, created_at FROM gallery_items WHERE user_id = $1 ORDER BY created_at DESC
Cache Redis: cache first page, 2 min TTL

6.2 Get Pattern Detail

Layer Detail
API GET /gallery/{id}
DB Read Full gallery item with source content, fragments, pattern seed
Frontend Render hero kaleidoscope pattern (deterministic from seed), source content, metadata

6.3 Search / Filter

Layer Detail
API GET /gallery/search?q=text&feature=turn&distortion=catastrophizing&from=2026-01-01
DB Read Full-text search on gallery content + filter joins

6.4 Generate Pattern Card (Share)

Layer Detail
API POST /gallery/{id}/share
Backend Generate Pattern Card image: kaleidoscope pattern + reframe text overlay + Kalei watermark
Response { share_url, image_url }
Frontend Native share sheet with generated image

7. Evidence Wall

7.1 Get Evidence Wall

Layer Detail
API GET /evidence-wall?limit=50
DB Read SELECT * FROM evidence_wall_tiles WHERE user_id = $1 ORDER BY created_at DESC
Entitlement Free: 30-day window (WHERE created_at > NOW() - INTERVAL '30 days'). Prism: all history
Response `{ tiles: [...], connections: [...], stage: "empty"
Frontend Render mosaic view based on stage, assign tile shapes (diamond, hex, rect, pentagon, triangle) based on tile_type

7.2 Get Tile Detail

Layer Detail
API GET /evidence-wall/tiles/{id}
DB Read Tile + source data (join to turns/mirror_sessions/lens_actions/ritual_sessions/rehearsal_sessions)
Response { tile, source_content, source_feature, created_at }
Frontend Half-sheet with tile detail, source content, link to source feature

7.3 Contextual Evidence Surfacing

Trigger: AI detects low self-efficacy language in Mirror or Turn

Layer Detail
AI Detection During Mirror fragment detection or Turn processing, flag self-efficacy score < threshold
DB Read SELECT * FROM evidence_wall_tiles WHERE user_id = $1 ORDER BY relevance_score DESC LIMIT 2
Response Included in Mirror/Turn response: { evidence_nudge: { tiles: [...], message: "..." } }
Frontend Render gentle card below main content with 1-2 evidence tiles

8. Spectrum (Phase 2)

8.1 Weekly Aggregation (Background Job)

Layer Detail
Trigger Cron: Sunday 6pm UTC
DB Read All turns + mirror_sessions + lens_actions for user's week
AI Call Batch API (50% cost): analyze emotional vectors, fragment patterns, Turn impact
DB Write INSERT INTO spectrum_weekly (user_id, week_start, river_data, glass_data, impact_data, rhythm_data, growth_score, insight_text)
Push Notification: "Your weekly Spectrum insight is ready"

8.2 Get Spectrum Dashboard

Layer Detail
API GET /spectrum/dashboard
Entitlement Free: simplified { weekly_insight_text, basic_fragment_count }. Prism: full dashboard
DB Read Latest weekly + monthly aggregates
Response { river, glass, impact, rhythm, growth, weekly_insight, monthly_insight? }
Frontend Render 5 visualization components from spectrum-visualizations.svg data

8.3 Monthly Deep Dive (Background Job)

Layer Detail
Trigger Cron: 1st of month
DB Read All weekly aggregates for the month
AI Call Batch API: month-over-month narrative generation
DB Write INSERT INTO spectrum_monthly (user_id, month_start, narrative, growth_trajectory, milestone_events)

9. Billing & Entitlements

9.1 Entitlement Check (Middleware)

Every rate-limited endpoint runs this check:

1. Redis: GET entitlement:{user_id} → if cached, return
2. DB: SELECT plan, status FROM subscriptions WHERE user_id = $1 AND status = 'active'
3. Redis: SET entitlement:{user_id} = plan, EX 300 (5 min cache)
4. Return plan → middleware applies feature gates

9.2 App Store Webhook

Layer Detail
API POST /billing/webhooks/apple
Validation Verify Apple JWT signature
DB Write INSERT/UPDATE subscriptions SET plan = $1, status = $2, apple_transaction_id = $3
Redis Invalidate entitlement:{user_id} cache
DB Write INSERT INTO entitlement_snapshots (user_id, plan, event_type, timestamp)

9.3 Google Play Webhook

Same pattern as Apple, with Google-specific JWT validation and google_purchase_token.


10. Safety System

10.1 Crisis Detection Pipeline

Every AI-processed input runs through:

Stage 1: Deterministic keyword scan (regex, ~1ms)
  → If match: flag, skip AI, return crisis template

Stage 2: AI confirmation (during normal processing)
  → AI output includes safety_flag: boolean
  → If flagged: return hardcoded crisis response (never AI-generated)

Stage 3: Logging
  → INSERT INTO safety_events (user_id, input_hash, detection_stage, action_taken)
  → Alert: send to safety dashboard

10.2 Crisis Response

Layer Detail
Response Hardcoded template: empathetic acknowledgment + 988 Suicide & Crisis Lifeline + Crisis Text Line
UI Full-screen modal with prominent crisis resource links, "I'm OK" dismiss button
Logging All crisis events logged, never the content itself

Database Schema Summary

Core Tables

Table Key Columns Indexes
users id (uuid), email, password_hash, created_at email (unique)
profiles user_id (FK), display_name, coaching_style, notification_time, onboarding_complete, ritual_streak, ritual_consistency_score user_id (unique)
subscriptions user_id (FK), plan (enum), status, started_at, expires_at, apple_transaction_id?, google_purchase_token? user_id, status
refresh_tokens id, user_id (FK), token_hash, revoked, expires_at token_hash, user_id

Feature Tables

Table Key Columns Indexes
turns id, user_id, input_text (encrypted), perspectives (jsonb), micro_action (jsonb), fragments (jsonb), emotion_vector (jsonb), saved, pattern_seed, created_at user_id + created_at
mirror_sessions id, user_id, status, reflection (jsonb), pattern_seed, started_at, closed_at user_id + status
mirror_messages id, session_id (FK), role, content (encrypted), created_at session_id + created_at
mirror_fragments id, session_id (FK), message_id (FK), distortion_type (enum), phrase, start_idx, end_idx, confidence session_id, distortion_type
lens_goals id, user_id, title, description, target_date, visualization_text, status, created_at user_id + status
lens_actions id, goal_id (FK), text, if_clause, then_clause, completed, completed_at goal_id + completed
rehearsal_sessions id, user_id, goal_id (FK), script (jsonb), duration, completed, started_at, completed_at user_id + goal_id
ritual_sessions id, user_id, template (enum), status, steps_completed (jsonb), started_at, completed_at user_id + created_at
gallery_items id, user_id, source_feature (enum), source_id, content_preview, pattern_seed, distortion_types (text[]), created_at user_id + source_feature, full-text on content_preview
evidence_wall_tiles id, user_id, tile_type (enum), source_feature, source_id, color_accent, metadata (jsonb), created_at user_id + created_at, user_id + tile_type

Analytics Tables

Table Key Columns Indexes
spectrum_weekly id, user_id, week_start, river_data (jsonb), glass_data (jsonb), impact_data (jsonb), rhythm_data (jsonb), growth_score, insight_text user_id + week_start
spectrum_monthly id, user_id, month_start, narrative (text), growth_trajectory (jsonb), milestone_events (jsonb) user_id + month_start
ai_usage_events id, user_id, feature, model, input_tokens, output_tokens, latency_ms, cost_usd, created_at user_id + feature, created_at
safety_events id, user_id, input_hash, detection_stage, action_taken, created_at user_id, created_at

Enum Types

CREATE TYPE plan_type AS ENUM ('free', 'prism');
CREATE TYPE subscription_status AS ENUM ('active', 'expired', 'canceled', 'trial', 'grace_period');
CREATE TYPE distortion_type AS ENUM ('catastrophizing', 'black_and_white', 'mind_reading', 'fortune_telling', 'personalization', 'discounting_positives', 'emotional_reasoning', 'should_statements', 'labeling', 'overgeneralization');
CREATE TYPE tile_type AS ENUM ('saved_keepsake', 'mirror_reflection', 'completed_action', 'self_correction', 'streak_milestone', 'goal_completion', 'reframe_echo', 'rehearsal_complete', 'ritual_complete');
CREATE TYPE ritual_template AS ENUM ('morning', 'evening', 'quick');
CREATE TYPE source_feature AS ENUM ('turn', 'mirror', 'lens', 'rehearsal', 'ritual');

Redis Key Patterns

Key Type TTL Purpose
rate:turns:{user_id}:{date} counter 24h Daily Turn rate limit
rate:mirror:{user_id}:{week} counter 7d Weekly Mirror rate limit
rate:rehearsal:{user_id}:{week} counter 7d Weekly Rehearsal rate limit
entitlement:{user_id} string 5 min Cached subscription plan
streak:daily:{user_id} hash Current daily streak count + last active date
streak:ritual:{user_id} hash Ritual streak + consistency data
affirmation:{user_id}:{date} string 24h Today's cached affirmation
turns:recent:{user_id} list 5 min Cached recent turns
session:{session_id} hash 2h Active Mirror session context

AI Gateway Call Patterns

Cost Tiers

Feature Model Est. Cost/Call Caching
Turn (reframe) Claude Haiku 4.5 ~$0.003 System prompt cached (40% saving)
Mirror (fragment detection) Claude Haiku 4.5 ~$0.002 System prompt cached
Mirror (reflective response) Claude Haiku 4.5 ~$0.003 System prompt cached
Mirror (session reflection) Claude Haiku 4.5 ~$0.005
Lens (goal refinement) Claude Haiku 4.5 ~$0.004
Rehearsal (script gen) Claude Haiku 4.5 ~$0.008
Ritual (personalization) Claude Haiku 4.5 ~$0.003 System prompt cached
Spectrum (weekly) Claude Batch API ~$0.010 Batch (50% off)
Spectrum (monthly) Claude Batch API ~$0.015 Batch (50% off)
Safety (confirmation) Included in feature call $0 Part of existing call

Prompt Template Versioning

All prompts stored as versioned templates: prompts/{feature}/{version}.json

Prompt Current Version
turn_reframe v1.0
mirror_fragment_detect v1.0
mirror_reflect v1.0
mirror_session_close v1.0
lens_goal_refine v1.0
rehearsal_script v1.0
ritual_personalize v1.0
spectrum_weekly v1.0
spectrum_monthly v1.0
safety_check v1.0

Frontend Component Architecture

Navigation

AppNavigator (Expo Router)
├── (auth)
│   ├── login.tsx
│   ├── register.tsx
│   └── onboarding/
│       ├── welcome.tsx
│       ├── metaphor.tsx
│       ├── turn-demo.tsx
│       ├── style-select.tsx
│       ├── notifications.tsx
│       └── first-turn.tsx
├── (tabs)
│   ├── turn/
│   │   ├── index.tsx          — Turn home + input
│   │   ├── results.tsx        — Turn results display
│   │   ├── ritual-select.tsx  — Ritual template picker
│   │   └── ritual-flow.tsx    — Active ritual flow
│   ├── mirror/
│   │   ├── index.tsx          — Session list + new session
│   │   └── session.tsx        — Active Mirror session
│   ├── lens/
│   │   ├── index.tsx          — Goal dashboard
│   │   ├── create-goal.tsx    — 6-step goal creation
│   │   ├── goal-detail.tsx    — Goal detail + actions
│   │   └── rehearsal.tsx      — Rehearsal session
│   ├── gallery/
│   │   ├── index.tsx          — Pattern grid + filters
│   │   └── detail.tsx         — Pattern detail + share
│   └── you/
│       ├── index.tsx          — Profile + stats
│       ├── evidence-wall.tsx  — Evidence Wall mosaic
│       ├── spectrum.tsx       — Spectrum dashboard
│       ├── settings.tsx       — App settings
│       └── subscription.tsx   — Plan management
└── (modals)
    ├── fragment-detail.tsx    — Half-sheet for fragment info
    ├── upgrade.tsx            — Prism upgrade prompt
    ├── crisis.tsx             — Crisis response (safety)
    ├── share-card.tsx         — Pattern card share sheet
    └── rate-limit.tsx         — Rate limit notice

Shared Components

Component Usage SVG Asset
FragmentIcon Everywhere — the core ◇ fragment-icons.svg
TabBar Main navigation icons-tab-bar.svg
DistortionBadge Mirror highlights, Gallery filters icons-distortions.svg
ActionIcon Buttons, list items, settings icons-actions.svg
KaleidoscopePattern Gallery, Turn result, share card patterns-kaleidoscope.svg
ProgressRing Lens goals, Rehearsal timer progress-indicators.svg
StepDots Lens 6-step, Ritual flow progress-indicators.svg
StreakCalendar Ritual tracking, You stats progress-indicators.svg
EvidenceMosaic Evidence Wall evidence-wall.svg
LoadingSpinner All loading states loading-animations.svg
SkeletonShimmer Data loading loading-animations.svg
TurnAnimation Turn processing loading-animations.svg
AIThinkingBubble Mirror AI processing loading-animations.svg
SuccessBurst Completion celebrations loading-animations.svg
BreathingLogo Splash, idle states loading-animations.svg
StatusBar Device chrome device-chrome.svg
NavHeader All screens device-chrome.svg
TabBarFrame Tab bar container device-chrome.svg
Toast Success/error feedback device-chrome.svg
InputAccessory Mirror, Turn text input device-chrome.svg
ShardCluster Empty states, backgrounds decorative-shards.svg
PrismaticDivider Section separators decorative-shards.svg
CornerAccent Card decorations decorative-shards.svg
SpectrumRiver Spectrum dashboard spectrum-visualizations.svg
SpectrumGlass Spectrum dashboard spectrum-visualizations.svg
SpectrumImpact Spectrum dashboard spectrum-visualizations.svg
SpectrumRhythm Spectrum dashboard spectrum-visualizations.svg
SpectrumGrowth Spectrum dashboard spectrum-visualizations.svg