diff --git a/src/components/DesktopWelcomePage.tsx b/src/components/DesktopWelcomePage.tsx index 966651b..6d2a2df 100644 --- a/src/components/DesktopWelcomePage.tsx +++ b/src/components/DesktopWelcomePage.tsx @@ -1,578 +1,96 @@ 'use client'; -import React, { useState, useEffect, useCallback } from 'react'; +import { useEffect, useState } from 'react'; import Link from 'next/link'; -import { - ShieldCheck, - Scale, - Users, - Trophy, - Activity, - ArrowRight, - ChevronDown, - Sparkles, - Check, - Sun, - Moon, - Monitor, - ExternalLink, -} from 'lucide-react'; -import { - HERO_CONTENT, - DOMAIN_CONCEPTS, - MANIFESTO, - FAQ_ITEMS, -} from '@/lib/onboarding-content'; -import { ParitySimulator } from '@/components/ParitySimulator'; -import { OnboardingFlow, DuelConfiguration } from '@/components/OnboardingFlow'; -import { MembershipOffer } from '@/components/MembershipOffer'; -import { DoLogo } from '@/components/DoLogo'; +import { ArrowUpRight, Moon, Sun } from 'lucide-react'; +import { useStore } from '@/lib/store'; +import { MembershipOffer } from './MembershipOffer'; export function DesktopWelcomePage() { - const [showOnboardingModal, setShowOnboardingModal] = useState(false); - const [openFaqIndex, setOpenFaqIndex] = useState(0); - const [selectedConceptTab, setSelectedConceptTab] = useState('duel'); - const [currentTheme, setCurrentTheme] = useState<'light' | 'dark'>('light'); + const { themePreference, effectiveTheme, toggleTheme } = useStore(); + const [mounted, setMounted] = useState(false); - // Sync theme with document class and localStorage useEffect(() => { - if (typeof window === 'undefined') return; - const isDark = document.documentElement.classList.contains('dark'); - setCurrentTheme(isDark ? 'dark' : 'light'); + setMounted(true); }, []); - const toggleTheme = useCallback(() => { - if (typeof window === 'undefined') return; - const isDark = document.documentElement.classList.contains('dark'); - const next = isDark ? 'light' : 'dark'; - if (next === 'dark') { - document.documentElement.classList.add('dark'); - document.documentElement.classList.remove('light'); - localStorage.setItem('theme_preference', 'dark'); - setCurrentTheme('dark'); - } else { - document.documentElement.classList.remove('dark'); - document.documentElement.classList.add('light'); - localStorage.setItem('theme_preference', 'light'); - setCurrentTheme('light'); - } - }, []); - - // Keyboard shortcut listener ('O' for Onboard, 'Esc' to close modal) - useEffect(() => { - const handleKeyDown = (e: KeyboardEvent) => { - if (['INPUT', 'TEXTAREA'].includes((e.target as HTMLElement)?.tagName)) return; - if (e.key === 'o' || e.key === 'O') { - setShowOnboardingModal((prev) => !prev); - } - if (e.key === 'Escape' && showOnboardingModal) { - setShowOnboardingModal(false); - } - }; - window.addEventListener('keydown', handleKeyDown); - return () => window.removeEventListener('keydown', handleKeyDown); - }, [showOnboardingModal]); - return ( -
- {/* Subtle top hairline gradient */} -
- - {/* TOP STICKY DESKTOP BAR */} -
-
-
- - -
- - do. - - - Habit Parity Engine - -
- - - -
- -
- {/* Theme Toggle Button */} - - - - Sign In → - - - -
-
+
+
+ + do it. + +
- - {/* HERO SECTION - WIDE DESKTOP FOCUS */} -
-
-
- {/* Kicker badge */} -
- - Multi-Tenant Two-Player Habit Parity Engine -
- -

- The intimate two-player accountability engine. -

- -

- Solo habit trackers fail because nobody notices when you quit. Social feeds fail because public performativity dilutes discipline. - Do It binds two people in an uncheatable weekly habit duel, calibrated to mathematically balanced 240 Daily Par. +

+
+
+

+ Small habits. Shared commitment.

- - {/* CTAs */} -
- - - - Test Live Simulator Below - - - - - Membership & Stripe → - -
- - {/* Value Pillars */} -
- {HERO_CONTENT.valuePillars.map((pillar) => ( -
-
- {pillar.metric} -
-
{pillar.label}
-
{pillar.context}
-
- ))} -
-
-
-
- - {/* SECTION 1: HOW THE TWO-PLAYER HABIT PARITY ENGINE WORKS */} -
-
-
-
- - 01 • Calibrated Equivalence - -

- How two different lives compete on equal ground. -

-
-

- You run marathons; your partner studies medicine. Forcing identical routines destroys consistency. - Do It standardizes daily difficulty instead. +

+ Better habits.
Someone in
your corner. +

+

+ Make a commitment with a friend or partner. Choose your habits, check in every day, and give each other a reason to keep going.

-
- - {/* 3 Core Architecture Pillars */} -
- {/* Card 1: 240 Par */} -
-
- -
-

The 240 Daily Par Standard

-

- Every habit carries an agreed point weight. Both players customize asymmetric commitments that total exactly 240 points daily. Neither player can stack easy micro-habits to inflate scores. -

-
- 1,680 Weekly Par potential • Zero handicaps -
-
- - {/* Card 2: Telemetry & Photo Verification */} -
-
- +
+ -

Uncheatable Proof Engine

-

- Checkbox honor systems collapse when fatigue sets in. Do It connects directly to Garmin, Apple Health, and Oura for automatic telemetry verification, alongside instant client-compressed photo proofs. +

+ A private Duel for two.
+ Your routines. Your person.

-
- Cryptographic timestamping • Client-side privacy -
- - {/* Card 3: Sunday Midnight Settlement */} -
-
- -
-

Sunday Stakes & Champions Ledger

-

- Every Sunday at 23:59:59 local time, the Leaderboard resets. The player trailing in weekly score settles the agreed mutual stake (e.g. Sunday dinner date or chore week), forever engraved into the Hall of Champions. +

+

+ Start in three steps

-
- Permanent Karma score • Tangible stakes -
+
    +
  1. + 01Membership +
  2. +
  3. + 02Your account +
  4. +
  5. + 03Your Duel +
  6. +
-
-
- - {/* SECTION 2: INTERACTIVE PARITY SIMULATOR */} -
-
-
- - 02 • Interactive Browser Demonstration - -

- Experience the Parity Engine live right now. -

-

- Test how two players with vastly different commitments balance out to 240 Par each day. - Toggle habit check-ins, switch between preset archetypes, and view the live Sunday stake forecast. -

+
+
- - {/* Embedded Parity Simulator */} -
- -
-
-
- - {/* SECTION 3: MEMBERSHIP & STRIPE SUBSCRIPTION */} -
-
-
-
- - 03 • Membership & Pricing - -

- One membership. Both players. -

-

- We reject freemium models that sell your data or gate basic accountability behind ad spam. - One player subscribes, and both participants unlock full access to the Duel. -

- -
-
-
- -
- Your partner or rival joins completely free under your seat. -
-
-
- -
- Direct telemetry sync for Garmin, Apple Health, and Oura. -
-
-
- -
- Zero ads, zero data tracking, zero streak-freeze microtransactions. -
-
- -
- - - Manage existing billing → - -
-
- - {/* Embedded Live Membership Offer with Stripe Checkout */} -
- -
-
-
-
- - {/* SECTION 4: THE ANTI-FREEMIUM MANIFESTO */} -
-
-
- - 04 • Architectural Philosophy - -

- Why Do It is a paid subscription — never freemium. -

-

- The modern software ecosystem has normalized deceptive freemium habit apps: invasive ad networks, - dark-pattern push notifications, streak-freeze microtransactions, and user tracking. - We believe serious discipline requires aligned incentives. -

-
- -
- {MANIFESTO.principles.map((p) => ( -
-
- {p.index} - - Principle - -
-

{p.title}

-
-
- The Freemium Pathology: - {p.critique} -
-
- The Do It Architecture: - {p.counterPosition} -
-
-
- ))} -
-
-
- - {/* SECTION 5: DOMAIN GLOSSARY & UBIQUITOUS LANGUAGE */} -
-
-
- - 05 • Ubiquitous Language - -

- Every word is intentional. -

-

- In Do It, terminology reflects bounded cryptographic reality. We do not have 'Rooms' or 'Tasks'. - Explore the domain entities that govern your duel. -

-
- - {/* Domain tabs */} -
- {DOMAIN_CONCEPTS.map((concept) => ( - - ))} -
- - {/* Active Concept Display */} - {(() => { - const activeConcept = DOMAIN_CONCEPTS.find((c) => c.id === selectedConceptTab) || DOMAIN_CONCEPTS[0]; - return ( -
-
-
-

{activeConcept.term}

-
{activeConcept.summary}
-
- - Category: {activeConcept.category} - -
- -
- {activeConcept.definition} -
- -
-
- - Prohibited / Ambiguous Terms: - -
- {activeConcept.prohibitedTerms.map((term) => ( - - × {term} - - ))} -
-
- -
- - Architectural Rationale: - -

- {activeConcept.architecturalRationale} -

-
-
-
- ); - })()} -
-
- - {/* SECTION 6: FAQ ACCORDION */} -
-
-
- - 06 • Answers to Crucial Inquiries - -

- Frequently Asked Questions -

-
- -
- {FAQ_ITEMS.map((item, idx) => { - const isOpen = openFaqIndex === idx; - return ( -
- - - {isOpen && ( -
- {item.answer} -
- )} -
- ); - })} -
-
-
- - {/* FOOTER */} -
-
-
- -
- do. - • Two-Player Habit Parity Engine -
-
- -
- Architecture - Simulator - Membership - Open App -
- -
- Crafted for desktop browsers • Zero freemium ad spam -
-
+ + +
+

Do It. Show up for yourself. And each other.

+ + Membership & billing +
- - {/* ONBOARDING MODAL OVERLAY */} - {showOnboardingModal && ( -
-
- setShowOnboardingModal(false)} - onComplete={(config) => { - setShowOnboardingModal(false); - window.location.href = `/?invite=${config.inviteCode}&tab=today`; - }} - /> -
-
- )}
); } - -export default DesktopWelcomePage; diff --git a/src/components/MembershipOffer.tsx b/src/components/MembershipOffer.tsx index edc23c0..b89a61a 100644 --- a/src/components/MembershipOffer.tsx +++ b/src/components/MembershipOffer.tsx @@ -43,36 +43,36 @@ export function MembershipOffer() { } return ( -
+
-

Do It membership

- For two people +

Do It membership

+ For two people
-

One commitment. Both of you.

+

One commitment. Both of you.

- $15 - USD / month + $15 + USD / month
-

One monthly subscription covers both players.

-
+

One monthly subscription covers both players.

+
    {included.map(([title, description]) =>
  • - -

    {title}

    {description}

    + +

    {title}

    {description}

  • )}
- {guest ? View your shared membership : - } - {error &&

{error}

} -

Billed monthly. Cancel through your billing portal.

-

Secure checkout with stripe

+ {error &&

{error}

} +

Billed monthly. Cancel through your billing portal.

+

Secure checkout with stripe

-
-

Pay first. Then set up your account and invite your person.

-

$15 charged today. No free trial.

+
+

Pay first. Then set up your account and invite your person.

+

$15 charged today. No free trial.

); diff --git a/src/components/OnboardingFlow.tsx b/src/components/OnboardingFlow.tsx deleted file mode 100644 index 11147d4..0000000 --- a/src/components/OnboardingFlow.tsx +++ /dev/null @@ -1,632 +0,0 @@ -'use client'; - -import React, { useState } from 'react'; -import { - Users, - Scale, - Trophy, - CreditCard, - Rocket, - Check, - Copy, - ChevronRight, - ChevronLeft, - Sparkles, - ShieldCheck, - AlertCircle, - Plus, - Trash2, -} from 'lucide-react'; -import { BillingPanel } from './BillingPanel'; -import { useRouter } from 'next/navigation'; -import { useMultiplayer } from '@/lib/multiplayer'; -import { DoLogo } from './DoLogo'; - -export interface OnboardingFlowProps { - onComplete?: (duelConfig: DuelConfiguration) => void; - onCancel?: () => void; - className?: string; -} - -export interface HabitItem { - id: string; - title: string; - points: number; - category: string; -} - -export interface DuelConfiguration { - playerOneName: string; - playerTwoName: string; - inviteCode: string; - playerOneHabits: HabitItem[]; - playerTwoHabits: HabitItem[]; - weeklyStake: string; -} - -const DEFAULT_HABITS_P1: HabitItem[] = [ - { id: 'p1_1', title: 'Early Rise (06:30 AM)', points: 40, category: 'foundation' }, - { id: 'p1_2', title: 'Strength Training / Gym', points: 70, category: 'physical' }, - { id: 'p1_3', title: 'Deep Work Sprint (90m)', points: 80, category: 'deep_work' }, - { id: 'p1_4', title: 'Read 20 Pages', points: 50, category: 'intellect' }, -]; - -const DEFAULT_HABITS_P2: HabitItem[] = [ - { id: 'p2_1', title: 'Morning Mobility & Yoga', points: 40, category: 'physical' }, - { id: 'p2_2', title: 'Outdoor 5km Run', points: 70, category: 'cardio' }, - { id: 'p2_3', title: 'Deep Focus & Creative Studio', points: 80, category: 'deep_work' }, - { id: 'p2_4', title: 'Language Study & Journaling', points: 50, category: 'intellect' }, -]; - -const PRESET_STAKES = [ - 'Sunday Dinner at a favorite restaurant (Loser pays & reserves)', - 'Cook dinner every night for a full week', - 'Sunday breakfast in bed with pour-over coffee', - 'Take over all weekend house chores & cleaning', - 'Surprise specialty coffee bag & pastry run', -]; - -export function OnboardingFlow({ onComplete, onCancel, className = '' }: OnboardingFlowProps) { - const router = useRouter(); - const { duel } = useMultiplayer(); - const [currentStep, setCurrentStep] = useState<1 | 2 | 3 | 4 | 5>(1); - - // Step 1: Players - const [playerOneName, setPlayerOneName] = useState('Maciek'); - const [playerTwoName, setPlayerTwoName] = useState('Myrna'); - const inviteCode = duel?.invite_code ?? ''; - const [copiedLink, setCopiedLink] = useState(false); - - // Step 2: Habits - const [p1Habits, setP1Habits] = useState(DEFAULT_HABITS_P1); - const [p2Habits, setP2Habits] = useState(DEFAULT_HABITS_P2); - const [newP1Title, setNewP1Title] = useState(''); - const [newP1Points, setNewP1Points] = useState(40); - const [newP2Title, setNewP2Title] = useState(''); - const [newP2Points, setNewP2Points] = useState(40); - - // Step 3: Stake - const [selectedStake, setSelectedStake] = useState(PRESET_STAKES[0]); - const [customStake, setCustomStake] = useState(''); - - // Step 4: Subscription - - // Computed par totals - const p1TotalPoints = p1Habits.reduce((acc, h) => acc + h.points, 0); - const p2TotalPoints = p2Habits.reduce((acc, h) => acc + h.points, 0); - const isParBalanced = p1TotalPoints === 240 && p2TotalPoints === 240; - - const effectiveStake = customStake.trim() || selectedStake; - const inviteUrl = !inviteCode ? '' : typeof window !== 'undefined' - ? `${window.location.origin}/?invite=${inviteCode}` - : `https://do-it.app/?invite=${inviteCode}`; - - const copyInviteToClipboard = async () => { - try { - if (navigator?.clipboard) { - await navigator.clipboard.writeText(inviteUrl); - setCopiedLink(true); - setTimeout(() => setCopiedLink(false), 2500); - } - } catch { - // fallback - } - }; - - const handleNext = () => { - if (currentStep < 5) { - setCurrentStep(currentStep === 1 ? 2 : currentStep === 2 ? 3 : currentStep === 3 ? 4 : 5); - } - }; - - const handleBack = () => { - if (currentStep > 1) { - setCurrentStep(currentStep === 5 ? 4 : currentStep === 4 ? 3 : currentStep === 3 ? 2 : 1); - } - }; - - const handleFinish = () => { - const config: DuelConfiguration = { - playerOneName, - playerTwoName, - inviteCode, - playerOneHabits: p1Habits, - playerTwoHabits: p2Habits, - weeklyStake: effectiveStake, - }; - if (onComplete) { - onComplete(config); - } else { - router.push('/billing'); - } - }; - - const stepLabels = [ - { num: 1, title: 'Roster', icon: Users }, - { num: 2, title: 'Habit Parity', icon: Scale }, - { num: 3, title: 'The Stake', icon: Trophy }, - { num: 4, title: 'Membership', icon: CreditCard }, - { num: 5, title: 'Launch', icon: Rocket }, - ] satisfies { num: 1 | 2 | 3 | 4 | 5; title: string; icon: typeof Users }[]; - - return ( -
- {/* Top Header & Step Progress Bar */} -
-
- -
-
- Duel Genesis Wizard • Step {currentStep} of 5 -
-

- {currentStep === 1 && 'Establish the Two-Player Tenancy'} - {currentStep === 2 && 'Calibrate 240 Daily Par'} - {currentStep === 3 && 'Declare the Sunday Wager'} - {currentStep === 4 && 'Duo-Seat Membership Pass'} - {currentStep === 5 && 'Confirm & Launch the Duel'} -

-
-
- - {/* Step Indicator Pills */} -
- {stepLabels.map((s) => { - const Icon = s.icon; - const isActive = currentStep === s.num; - const isCompleted = currentStep > s.num; - return ( - - ); - })} -
-
- - {/* Step Contents */} -
- {/* STEP 1: THE TWO PLAYERS */} - {currentStep === 1 && ( -
-
-

- Do It is strictly a two-player arena. No solo trackers, no public feeds, and no social clutter. - You are setting up an isolated Duel bounded between you and your rival/partner. -

-
- -
- {/* Player 1 Card */} -
-
-
-
- 1 -
-
-
Player 1 (You)
-
Owner Slot
-
-
- - Host - -
- -
- - setPlayerOneName(e.target.value)} - placeholder="Your name" - className="w-full px-4 py-2.5 rounded-xl bg-white dark:bg-zinc-950 border border-zinc-300 dark:border-white/10 text-zinc-900 dark:text-white font-medium focus:outline-none focus:border-blue-500 text-sm" - /> -
-
- - {/* Player 2 Card */} -
-
-
-
- 2 -
-
-
Player 2 (Partner)
-
Guest Slot
-
-
- - Opponent - -
- -
- - setPlayerTwoName(e.target.value)} - placeholder="Partner name" - className="w-full px-4 py-2.5 rounded-xl bg-white dark:bg-zinc-950 border border-zinc-300 dark:border-white/10 text-zinc-900 dark:text-white font-medium focus:outline-none focus:border-purple-500 text-sm" - /> -
-
-
- - {/* Cryptographic Invite Link Card */} -
-
- - - One-Use Cryptographic Pair Link - - Code: {inviteCode} -
-
-
- {inviteUrl || 'Create your Duel after signing in to get a real invitation link.'} -
- -
-

- Send this link to your partner. When they open it on their browser, their seat in this Duel is instantaneously bound. -

-
-
- )} - - {/* STEP 2: HABIT PARITY CALIBRATION */} - {currentStep === 2 && ( -
-
-
-

- Both players do NOT need the same lifestyle. Player 1 can run 10k while Player 2 practices piano. - The rule of the engine: Each player must calibrate exactly 240 Daily Par. -

-
-
- {isParBalanced ? ( - <> - Par Balanced (240 / 240) - - ) : ( - <> - Adjust to 240 Par each - - )} -
-
- -
- {/* Player 1 Habits */} -
-
-
-

{playerOneName}'s Daily Routine

-
Target: 240 pts
-
-
- {p1TotalPoints} / 240 -
-
- -
- {p1Habits.map((habit) => ( -
- {habit.title} -
- {habit.points} pts - -
-
- ))} -
- - {/* Add Habit */} -
- setNewP1Title(e.target.value)} - placeholder="Add custom habit..." - className="flex-1 px-3 py-2 rounded-xl bg-white dark:bg-zinc-950 border border-zinc-300 dark:border-white/10 text-xs text-zinc-900 dark:text-white" - /> - setNewP1Points(Number(e.target.value))} - step={5} - min={5} - max={120} - className="w-16 px-2 py-2 rounded-xl bg-white dark:bg-zinc-950 border border-zinc-300 dark:border-white/10 text-xs text-zinc-900 dark:text-white font-mono text-center" - /> - -
-
- - {/* Player 2 Habits */} -
-
-
-

{playerTwoName}'s Daily Routine

-
Target: 240 pts
-
-
- {p2TotalPoints} / 240 -
-
- -
- {p2Habits.map((habit) => ( -
- {habit.title} -
- {habit.points} pts - -
-
- ))} -
- - {/* Add Habit */} -
- setNewP2Title(e.target.value)} - placeholder="Add custom habit..." - className="flex-1 px-3 py-2 rounded-xl bg-white dark:bg-zinc-950 border border-zinc-300 dark:border-white/10 text-xs text-zinc-900 dark:text-white" - /> - setNewP2Points(Number(e.target.value))} - step={5} - min={5} - max={120} - className="w-16 px-2 py-2 rounded-xl bg-white dark:bg-zinc-950 border border-zinc-300 dark:border-white/10 text-xs text-zinc-900 dark:text-white font-mono text-center" - /> - -
-
-
-
- )} - - {/* STEP 3: THE SUNDAY STAKE */} - {currentStep === 3 && ( -
-
-

- Consistency needs skin in the game. Every Sunday at 23:59:59 local time, the Leaderboard locks. - Whoever trails in the weekly score must fulfill the agreed stake. -

-
- -
- {PRESET_STAKES.map((stake) => { - const isSelected = selectedStake === stake && !customStake; - return ( - - ); - })} -
- -
- - setCustomStake(e.target.value)} - placeholder="e.g. Loser washes both cars and cleans the garage..." - className="w-full px-4 py-3 rounded-2xl bg-white dark:bg-zinc-950 border border-zinc-300 dark:border-white/10 text-zinc-900 dark:text-white text-sm focus:outline-none focus:border-amber-500" - /> -
-
- )} - - {/* STEP 4: DUO-SEAT MEMBERSHIP PASS */} - {currentStep === 4 && } - - {currentStep === 5 && ( -
-
-
- -
-

Your Duel preview is ready

-

- These choices are a preview. Sign in to create your Duel and review billing before purchasing. -

-
- - {/* Summary Grid */} -
-
-
Tenants
-
{playerOneName} vs {playerTwoName}
-
2-Player Parity Tenancy
-
- -
-
Daily Par Calibration
-
240 Par Equal Target
-
{p1Habits.length} habits vs {p2Habits.length} habits
-
- -
-
Sunday Stake
-
{effectiveStake}
-
Settles Sunday 23:59
-
-
- - {/* Share link reminder */} -
-
- Partner Link: {inviteUrl || 'Create your Duel after signing in to get a real invitation link.'} -
- -
-
- )} -
- - {/* Bottom Navigation Buttons */} -
-
- {currentStep > 1 ? ( - - ) : ( - onCancel && ( - - ) - )} -
- -
- {currentStep < 5 ? ( - - ) : ( - - )} -
-
-
- ); -} - -export default OnboardingFlow; diff --git a/src/components/ParitySimulator.tsx b/src/components/ParitySimulator.tsx deleted file mode 100644 index de7dd13..0000000 --- a/src/components/ParitySimulator.tsx +++ /dev/null @@ -1,1479 +0,0 @@ -'use client'; - -import React, { useState, useMemo, useCallback } from 'react'; -import { - Activity, - Award, - BookOpen, - Camera, - Check, - ChevronRight, - Clock, - Dumbbell, - Flame, - HeartPulse, - Laptop, - Moon, - RefreshCw, - Scale, - ShieldCheck, - Sparkles, - Sunrise, - Trophy, - Users, - Utensils, - Watch, - X, - Zap, -} from 'lucide-react'; - -export const DAILY_PAR_CEILING = 240; - -export interface HabitProofInfo { - deviceOrSource: string; - metricPrimary: string; - timestamp: string; - verificationCode: string; - notes: string; - type: 'telemetry' | 'photo'; - sensorDetails?: { - avgHr?: string; - distance?: string; - cadenceOrPace?: string; - calories?: string; - exifModel?: string; - geoAccuracy?: string; - }; -} - -export interface SimulatorHabit { - id: string; - title: string; - points: number; - category: 'foundation' | 'physical' | 'deep_work' | 'mind' | 'intellect' | 'nutrition'; - iconType: 'sunrise' | 'dumbbell' | 'book' | 'laptop' | 'run' | 'nutrition' | 'yoga' | 'sleep' | 'steps' | 'breath'; - proof: HabitProofInfo; -} - -export interface SimulatorPlayer { - id: 'player_a' | 'player_b'; - name: string; - tagline: string; - avatarInitials: string; - accentHex: string; - habits: SimulatorHabit[]; -} - -export interface SimulatorPreset { - id: string; - name: string; - tagline: string; - description: string; - stakeTitle: string; - stakeWager: string; - playerA: SimulatorPlayer; - playerB: SimulatorPlayer; - defaultCheckedA: string[]; - defaultCheckedB: string[]; -} - -export const PRESET_SCENARIOS: SimulatorPreset[] = [ - { - id: 'morning_athletes', - name: 'Morning Athletes', - tagline: 'Strength & Cold Discipline vs. Endurance & Mobility', - description: 'Two athletes with completely contrasting training philosophies competing under an identical 240 Par daily quota.', - stakeTitle: 'Sunday Omakase & Wine at Osteria', - stakeWager: 'Lowest aggregate score on Sunday evening covers the tasting menu bill.', - playerA: { - id: 'player_a', - name: 'Alex', - tagline: 'Powerlifting & Cold Immersion', - avatarInitials: 'AL', - accentHex: '#38bdf8', // Sky 400 - habits: [ - { - id: 'ma-a-1', - title: 'Early Rise 06:00 AM', - points: 30, - category: 'foundation', - iconType: 'sunrise', - proof: { - type: 'photo', - deviceOrSource: 'iPhone 16 Pro • Camera API', - metricPrimary: 'EXIF 06:02:14 AM CEST', - timestamp: '06:02 AM', - verificationCode: 'EXIF-GEO-VERIFIED #9182', - notes: 'Geotagged morning balcony snapshot with natural sunlight validation.', - sensorDetails: { exifModel: 'iPhone 16 Pro (f/1.78, 24mm)', geoAccuracy: '±3m radius' }, - }, - }, - { - id: 'ma-a-2', - title: 'Heavy Barbell Strength (75m)', - points: 60, - category: 'physical', - iconType: 'dumbbell', - proof: { - type: 'telemetry', - deviceOrSource: 'Apple Watch Ultra 2', - metricPrimary: '144 bpm avg HR • 520 kcal', - timestamp: '07:35 AM', - verificationCode: 'HEALTHKIT-WORKOUT #8831', - notes: 'Functional strength training session streamed via Apple HealthKit.', - sensorDetails: { avgHr: '144 bpm (peak 172)', calories: '520 active kcal', cadenceOrPace: '75 min duration' }, - }, - }, - { - id: 'ma-a-3', - title: 'Cold Immersion 3 Min', - points: 20, - category: 'physical', - iconType: 'run', - proof: { - type: 'photo', - deviceOrSource: 'Leica Q3 • Photo Verification', - metricPrimary: 'Water Temp 4.8°C • 03:12', - timestamp: '08:50 AM', - verificationCode: 'COLD-TIMER #4902', - notes: 'Stopwatch timestamped plunge verified against session metadata.', - sensorDetails: { exifModel: 'Leica Q3 (28mm Summilux)', geoAccuracy: 'Verified' }, - }, - }, - { - id: 'ma-a-4', - title: 'Deep Work 3 Hours (Architecture)', - points: 80, - category: 'deep_work', - iconType: 'laptop', - proof: { - type: 'telemetry', - deviceOrSource: 'RescueTime & VS Code Telemetry', - metricPrimary: '184 min uninterrupted focus', - timestamp: '01:15 PM', - verificationCode: 'SHA-FOCUS #2290', - notes: 'Zero contextual switching across coding and schema documentation.', - sensorDetails: { cadenceOrPace: '98% productive score', calories: '0 distraction flags' }, - }, - }, - { - id: 'ma-a-5', - title: '20 Pages Non-Fiction', - points: 50, - category: 'intellect', - iconType: 'book', - proof: { - type: 'photo', - deviceOrSource: 'Do It In-App Camera', - metricPrimary: 'Pages 142 - 164 verified', - timestamp: '09:30 PM', - verificationCode: 'OCR-READING #7142', - notes: 'High-resolution capture of annotated margins and bookmark placement.', - sensorDetails: { exifModel: 'Do It Secure Camera Sandbox' }, - }, - }, - ], - }, - playerB: { - id: 'player_b', - name: 'Jordan', - tagline: 'Endurance & Mobility', - avatarInitials: 'JO', - accentHex: '#f43f5e', // Rose 500 - habits: [ - { - id: 'ma-b-1', - title: '10km Tempo Run (Sub-50m)', - points: 80, - category: 'physical', - iconType: 'run', - proof: { - type: 'telemetry', - deviceOrSource: 'Garmin Forerunner 965', - metricPrimary: '10.21 km • 4:48/km • 690 kcal', - timestamp: '06:45 AM', - verificationCode: 'GARMIN-FIT-SYNC #1049', - notes: 'Raw GPS track and biometric stream synced over official Garmin Connect API.', - sensorDetails: { avgHr: '158 bpm avg', distance: '10.21 km', cadenceOrPace: '178 spm • 4:48/km', calories: '690 kcal' }, - }, - }, - { - id: 'ma-b-2', - title: 'Mobility & Vinyasa Flow (40m)', - points: 40, - category: 'mind', - iconType: 'yoga', - proof: { - type: 'telemetry', - deviceOrSource: 'Whoop 4.0 Strap', - metricPrimary: '42 min • Strain 8.4', - timestamp: '08:15 AM', - verificationCode: 'WHOOP-RECOVERY #3391', - notes: 'Restorative hip and spine mobility session recorded via Whoop.', - sensorDetails: { avgHr: '102 bpm', calories: '185 active kcal' }, - }, - }, - { - id: 'ma-b-3', - title: 'Anti-Inflammatory Fuel (Whole Foods)', - points: 40, - category: 'nutrition', - iconType: 'nutrition', - proof: { - type: 'photo', - deviceOrSource: 'Mobile Verification Lens', - metricPrimary: '100% Organic • Zero Seed Oils', - timestamp: '01:30 PM', - verificationCode: 'NUTRITION-PHOTO #4410', - notes: 'Complete meal prep snapshot with timestamped nutrient breakdown.', - }, - }, - { - id: 'ma-b-4', - title: 'Language Immersion 30m', - points: 40, - category: 'intellect', - iconType: 'book', - proof: { - type: 'photo', - deviceOrSource: 'Babbel Live + Audio Timestamp', - metricPrimary: '35 min Italian B2 conversation', - timestamp: '06:00 PM', - verificationCode: 'LESSON-API #8012', - notes: 'Completed live conversational module certified with tutor signoff.', - }, - }, - { - id: 'ma-b-5', - title: 'Evening Unwind & Journal', - points: 40, - category: 'mind', - iconType: 'breath', - proof: { - type: 'photo', - deviceOrSource: 'Do It Camera Lens', - metricPrimary: 'Daily ledger entry stamped', - timestamp: '10:15 PM', - verificationCode: 'JOURNAL-EXIF #6109', - notes: 'Handwritten physical notebook entry captured prior to bedroom lock.', - }, - }, - ], - }, - defaultCheckedA: ['ma-a-1', 'ma-a-2', 'ma-a-3'], // 30 + 60 + 20 = 110 pts - defaultCheckedB: ['ma-b-1', 'ma-b-2'], // 80 + 40 = 120 pts - }, - { - id: 'founder_deep_work', - name: 'Founder & Co-founder', - tagline: 'Engineering Architecture vs. Product Discovery', - description: 'Technical co-founder and product lead driving aggressive startup milestones with zero room for slacking.', - stakeTitle: 'Espresso Bar Tabs & Saturday Croissant Run', - stakeWager: 'Partner trailing on Sunday covers all artisanal coffees and morning pastries for the entire team.', - playerA: { - id: 'player_a', - name: 'David', - tagline: 'Technical Architecture & Deep Code', - avatarInitials: 'DA', - accentHex: '#818cf8', // Indigo 400 - habits: [ - { - id: 'fd-a-1', - title: 'Core Architecture Deep Block (4h)', - points: 90, - category: 'deep_work', - iconType: 'laptop', - proof: { - type: 'telemetry', - deviceOrSource: 'GitHub Enterprise Audit', - metricPrimary: '4h 12m active IDE session', - timestamp: '01:45 PM', - verificationCode: 'GIT-SHA-256 #a8f4c1', - notes: '4 clean PRs submitted, zero context interruption or browser tab wander.', - sensorDetails: { cadenceOrPace: '240 min clocked', calories: '99% focus rating' }, - }, - }, - { - id: 'fd-a-2', - title: 'Code Review & Security Audit', - points: 40, - category: 'deep_work', - iconType: 'laptop', - proof: { - type: 'telemetry', - deviceOrSource: 'Linear & GitHub API', - metricPrimary: '8 PRs reviewed w/ test coverage', - timestamp: '03:30 PM', - verificationCode: 'PR-AUDIT #1904', - notes: 'Peer-reviewed critical payment gateway migrations.', - }, - }, - { - id: 'fd-a-3', - title: 'Intermittent Fasting (16:8 Protocol)', - points: 30, - category: 'nutrition', - iconType: 'nutrition', - proof: { - type: 'telemetry', - deviceOrSource: 'Zero Fasting API', - metricPrimary: '16h 40m completed fast', - timestamp: '12:00 PM', - verificationCode: 'ZERO-FAST #9921', - notes: 'Metabolic fast verified through connected tracker.', - }, - }, - { - id: 'fd-a-4', - title: 'Zone 2 Cardio Base (45m)', - points: 50, - category: 'physical', - iconType: 'run', - proof: { - type: 'telemetry', - deviceOrSource: 'Garmin Edge / HR Monitor', - metricPrimary: '132 bpm steady state • 440 kcal', - timestamp: '06:10 PM', - verificationCode: 'GARMIN-HR-Z2 #5821', - notes: 'Optimal mitochondrial aerobic baseline maintained in strict Zone 2.', - sensorDetails: { avgHr: '132 bpm', calories: '440 kcal' }, - }, - }, - { - id: 'fd-a-5', - title: 'Zero Inbox & Standup Clearance', - points: 30, - category: 'foundation', - iconType: 'sunrise', - proof: { - type: 'photo', - deviceOrSource: 'Superhuman Screen Capture', - metricPrimary: '0 unread threads @ 18:00', - timestamp: '06:00 PM', - verificationCode: 'ZERO-INBOX #3021', - notes: 'Clean slate inbox zero state confirmed with unread counter zero.', - }, - }, - ], - }, - playerB: { - id: 'player_b', - name: 'Elena', - tagline: 'Product Strategy & Customer Interviews', - avatarInitials: 'EL', - accentHex: '#34d399', // Emerald 400 - habits: [ - { - id: 'fd-b-1', - title: 'Product PRD Specs Drafting (3h)', - points: 80, - category: 'deep_work', - iconType: 'laptop', - proof: { - type: 'telemetry', - deviceOrSource: 'Notion Workspace Audit', - metricPrimary: '185 min focused spec authoring', - timestamp: '11:30 AM', - verificationCode: 'NOTION-TIME #4418', - notes: 'Complete Q4 roadmap specs finalized with interactive flow wireframes.', - }, - }, - { - id: 'fd-b-2', - title: '3 Customer Discovery Interviews', - points: 60, - category: 'intellect', - iconType: 'book', - proof: { - type: 'photo', - deviceOrSource: 'Grain AI / Zoom Telemetry', - metricPrimary: '3 enterprise interviews + synthesis', - timestamp: '03:15 PM', - verificationCode: 'GRAIN-INTERVIEW #9201', - notes: 'Recorded customer transcripts synthesized into prioritized linear issues.', - }, - }, - { - id: 'fd-b-3', - title: 'Strength & Functional Conditioning', - points: 50, - category: 'physical', - iconType: 'dumbbell', - proof: { - type: 'telemetry', - deviceOrSource: 'Apple Watch Series 10', - metricPrimary: '50 min • 380 active kcal', - timestamp: '05:30 PM', - verificationCode: 'HEALTH-STRENGTH #7741', - notes: 'Full body functional circuit verified via HealthKit.', - sensorDetails: { avgHr: '138 bpm', calories: '380 kcal' }, - }, - }, - { - id: 'fd-b-4', - title: 'Technical Whitepaper Reading 45m', - points: 25, - category: 'intellect', - iconType: 'book', - proof: { - type: 'photo', - deviceOrSource: 'Readwise Reader API', - metricPrimary: '24 highlights synced to Obsidian', - timestamp: '08:45 PM', - verificationCode: 'READWISE-SYNC #6122', - notes: 'Deep dive into distributed systems papers with synchronized highlights.', - }, - }, - { - id: 'fd-b-5', - title: 'Digital Sunset (Screens Off 21:30)', - points: 25, - category: 'foundation', - iconType: 'sleep', - proof: { - type: 'telemetry', - deviceOrSource: 'iOS Screen Time API', - metricPrimary: 'Zero display unlocks after 21:30', - timestamp: '09:30 PM', - verificationCode: 'DOWNTIME-LOCK #0042', - notes: 'Hardware lock enforced via iOS Downtime API.', - }, - }, - ], - }, - defaultCheckedA: ['fd-a-1', 'fd-a-3', 'fd-a-4'], // 90 + 30 + 50 = 170 pts - defaultCheckedB: ['fd-b-1', 'fd-b-2'], // 80 + 60 = 140 pts - }, - { - id: 'couples_wellness', - name: 'Discipline & Recovery', - tagline: 'Circadian Performance vs. Daily Balance & Mindset', - description: 'A couple synchronizing healthy habits while maintaining radically customized personal routines.', - stakeTitle: 'Michelin Star Dinner & Housekeeper Handover', - stakeWager: 'The partner with fewer points by Sunday night funds dinner and assumes full weekend cleanup.', - playerA: { - id: 'player_a', - name: 'Marcus', - tagline: 'Circadian Rhythm & High Output', - avatarInitials: 'MA', - accentHex: '#fbbf24', // Amber 400 - habits: [ - { - id: 'cw-a-1', - title: 'Wake 06:30 + Sunlight Protocol', - points: 30, - category: 'foundation', - iconType: 'sunrise', - proof: { - type: 'photo', - deviceOrSource: 'Do It Verification Camera', - metricPrimary: 'Lux meter 12,000+ lux @ 06:38 AM', - timestamp: '06:38 AM', - verificationCode: 'SOLAR-EXIF #1009', - notes: 'Direct optic sunlight protocol documented within 15 minutes of waking.', - sensorDetails: { exifModel: 'Do It Solar Sync Engine' }, - }, - }, - { - id: 'cw-a-2', - title: 'Kettlebell Complex & Rucking (50m)', - points: 60, - category: 'physical', - iconType: 'dumbbell', - proof: { - type: 'telemetry', - deviceOrSource: 'Garmin Tactix 7', - metricPrimary: '24kg ruck • 5.4 km • 490 kcal', - timestamp: '07:45 AM', - verificationCode: 'GARMIN-RUCK #7821', - notes: 'Weighted vest endurance session logged with elevation profile.', - sensorDetails: { avgHr: '141 bpm', distance: '5.4 km', calories: '490 kcal' }, - }, - }, - { - id: 'cw-a-3', - title: 'Sugar-Free Clean Macro Intake', - points: 40, - category: 'nutrition', - iconType: 'nutrition', - proof: { - type: 'photo', - deviceOrSource: 'Nutritional Macro Lens', - metricPrimary: '0g refined sugar • 180g protein', - timestamp: '07:00 PM', - verificationCode: 'CLEAN-MACRO #2841', - notes: 'Daily nutritional compliance log verified.', - }, - }, - { - id: 'cw-a-4', - title: 'Box Breathing & Meditation (20m)', - points: 30, - category: 'mind', - iconType: 'breath', - proof: { - type: 'telemetry', - deviceOrSource: 'Oura Ring Gen 3', - metricPrimary: '20 min • HRV drop + calm state', - timestamp: '08:30 PM', - verificationCode: 'OURA-MIND #6620', - notes: 'Parasympathetic tone activation tracked via skin temp and HRV delta.', - }, - }, - { - id: 'cw-a-5', - title: '8 Hours Circadian Sleep Parity', - points: 80, - category: 'foundation', - iconType: 'sleep', - proof: { - type: 'telemetry', - deviceOrSource: 'Eight Sleep Pod 4', - metricPrimary: '8h 14m sleep • 92 Sleep Fitness', - timestamp: '06:30 AM', - verificationCode: 'EIGHT-SLEEP #9011', - notes: 'Full overnight hypnogram: 2h 10m deep sleep, 1h 55m REM.', - sensorDetails: { cadenceOrPace: '8h 14m total', avgHr: '48 bpm resting HR' }, - }, - }, - ], - }, - playerB: { - id: 'player_b', - name: 'Sofia', - tagline: 'Vitality, Movement & Mindfulness', - avatarInitials: 'SO', - accentHex: '#a78bfa', // Violet 400 - habits: [ - { - id: 'cw-b-1', - title: '10,000 Verified Steps', - points: 50, - category: 'physical', - iconType: 'steps', - proof: { - type: 'telemetry', - deviceOrSource: 'Apple Health Pedometer', - metricPrimary: '10,480 steps • 7.8 km', - timestamp: '06:15 PM', - verificationCode: 'HEALTH-PEDO #3819', - notes: 'Continuous non-vehicular step count streamed via M-series coprocessor.', - sensorDetails: { distance: '7.8 km', cadenceOrPace: '10,480 total steps' }, - }, - }, - { - id: 'cw-b-2', - title: 'Reformer Pilates / Core Flow (50m)', - points: 60, - category: 'physical', - iconType: 'yoga', - proof: { - type: 'telemetry', - deviceOrSource: 'Whoop 4.0 Activity', - metricPrimary: '52 min • Strain 10.8', - timestamp: '10:00 AM', - verificationCode: 'WHOOP-PILATES #4928', - notes: 'Targeted core isometric resistance flow recorded with studio beacon.', - sensorDetails: { avgHr: '124 bpm', calories: '310 kcal' }, - }, - }, - { - id: 'cw-b-3', - title: '3 Liters Mineral Hydration', - points: 30, - category: 'nutrition', - iconType: 'nutrition', - proof: { - type: 'photo', - deviceOrSource: 'Do It Verification Camera', - metricPrimary: 'Full 3L carafe drained', - timestamp: '07:30 PM', - verificationCode: 'HYDRATION-LOG #1104', - notes: 'Electrolyte-infused water carafe emptied and logged.', - }, - }, - { - id: 'cw-b-4', - title: 'Paperback Reading & Reflection (45m)', - points: 40, - category: 'intellect', - iconType: 'book', - proof: { - type: 'photo', - deviceOrSource: 'Do It In-App Camera', - metricPrimary: 'Philosophy text + notes', - timestamp: '09:15 PM', - verificationCode: 'READ-PHOTO #5501', - notes: 'Tangible physical book reading session with handwritten margins.', - }, - }, - { - id: 'cw-b-5', - title: '8 Hours Restorative Recovery Sleep', - points: 60, - category: 'foundation', - iconType: 'sleep', - proof: { - type: 'telemetry', - deviceOrSource: 'Oura Ring Gen 3', - metricPrimary: '8h 05m • Oura Readiness 89', - timestamp: '06:45 AM', - verificationCode: 'OURA-SLEEP #7109', - notes: 'Sleep score 89 with lowest resting heart rate of 51 bpm at 03:20 AM.', - sensorDetails: { avgHr: '51 bpm resting HR', cadenceOrPace: '8h 05m duration' }, - }, - }, - ], - }, - defaultCheckedA: ['cw-a-1', 'cw-a-2', 'cw-a-5'], // 30 + 60 + 80 = 170 pts - defaultCheckedB: ['cw-b-1', 'cw-b-2', 'cw-b-5'], // 50 + 60 + 60 = 170 pts (Tie!) - }, -]; - -function renderHabitIcon(type: SimulatorHabit['iconType'], className = 'w-4 h-4') { - switch (type) { - case 'sunrise': - return ; - case 'dumbbell': - return ; - case 'book': - return ; - case 'laptop': - return ; - case 'run': - return ; - case 'nutrition': - return ; - case 'yoga': - return ; - case 'sleep': - return ; - case 'steps': - return ; - case 'breath': - return ; - default: - return ; - } -} - -export function ParitySimulator() { - const [selectedPresetId, setSelectedPresetId] = useState(PRESET_SCENARIOS[0].id); - const [activePreset, setActivePreset] = useState(PRESET_SCENARIOS[0]); - - // Checked habit IDs per player - const [checkedA, setCheckedA] = useState>(() => new Set(PRESET_SCENARIOS[0].defaultCheckedA)); - const [checkedB, setCheckedB] = useState>(() => new Set(PRESET_SCENARIOS[0].defaultCheckedB)); - - // Proof inspection modal - const [inspectingProof, setInspectingProof] = useState<{ - habit: SimulatorHabit; - player: SimulatorPlayer; - isChecked: boolean; - } | null>(null); - - // Master telemetry badges overlay toggle - const [showProofBadges, setShowProofBadges] = useState(true); - - // Switch preset - const handleSelectPreset = useCallback((presetId: string) => { - const next = PRESET_SCENARIOS.find((p) => p.id === presetId); - if (!next) return; - setSelectedPresetId(presetId); - setActivePreset(next); - setCheckedA(new Set(next.defaultCheckedA)); - setCheckedB(new Set(next.defaultCheckedB)); - }, []); - - // Toggle habit check-in for Player A - const toggleHabitA = useCallback((habitId: string) => { - setCheckedA((prev) => { - const next = new Set(prev); - if (next.has(habitId)) { - next.delete(habitId); - } else { - next.add(habitId); - } - return next; - }); - }, []); - - // Toggle habit check-in for Player B - const toggleHabitB = useCallback((habitId: string) => { - setCheckedB((prev) => { - const next = new Set(prev); - if (next.has(habitId)) { - next.delete(habitId); - } else { - next.add(habitId); - } - return next; - }); - }, []); - - // Quick actions - const handleReset = useCallback(() => { - setCheckedA(new Set()); - setCheckedB(new Set()); - }, []); - - const handleSimulateMaxPar = useCallback(() => { - setCheckedA(new Set(activePreset.playerA.habits.map((h) => h.id))); - setCheckedB(new Set(activePreset.playerB.habits.map((h) => h.id))); - }, [activePreset]); - - const handleSimulateDeadHeat = useCallback(() => { - // Check habits that sum closest to identical points - const halfA = activePreset.playerA.habits.slice(0, 3).map((h) => h.id); - const halfB = activePreset.playerB.habits.slice(0, 3).map((h) => h.id); - setCheckedA(new Set(halfA)); - setCheckedB(new Set(halfB)); - }, [activePreset]); - - // Scores - const scoreA = useMemo(() => { - return activePreset.playerA.habits.reduce((acc, h) => (checkedA.has(h.id) ? acc + h.points : acc), 0); - }, [activePreset, checkedA]); - - const scoreB = useMemo(() => { - return activePreset.playerB.habits.reduce((acc, h) => (checkedB.has(h.id) ? acc + h.points : acc), 0); - }, [activePreset, checkedB]); - - const totalPossibleA = useMemo(() => { - return activePreset.playerA.habits.reduce((acc, h) => acc + h.points, 0); - }, [activePreset]); - - const totalPossibleB = useMemo(() => { - return activePreset.playerB.habits.reduce((acc, h) => acc + h.points, 0); - }, [activePreset]); - - const delta = scoreA - scoreB; - const isParA = scoreA >= DAILY_PAR_CEILING; - const isParB = scoreB >= DAILY_PAR_CEILING; - - // Percentage calculations - const pctA = Math.min(100, Math.round((scoreA / DAILY_PAR_CEILING) * 100)); - const pctB = Math.min(100, Math.round((scoreB / DAILY_PAR_CEILING) * 100)); - - // Stake determination - const leaderPlayer = delta > 0 ? activePreset.playerA : delta < 0 ? activePreset.playerB : null; - const trailingPlayer = delta > 0 ? activePreset.playerB : delta < 0 ? activePreset.playerA : null; - - return ( -
- {/* Top Banner / Simulator Header */} -
- {/* Subtle decorative grid background */} - - - {/* Main Parity Gauge & Delta Card */} -
-
- {/* Player A Score Column */} -
-
-
-
- - {scoreA} - - - / {DAILY_PAR_CEILING} PTS - -
-
- - {isParA ? '✓ PAR ACHIEVED' : `${DAILY_PAR_CEILING - scoreA} pts to par`} - - - {pctA}% - -
-
- - {/* Center Tug-of-War Balance Meter & Delta */} -
-
- - {delta === 0 ? ( - DEAD HEAT • TIED AT {scoreA} PTS - ) : delta > 0 ? ( - - {activePreset.playerA.name} leads by{' '} - +{delta} pts - - ) : ( - - {activePreset.playerB.name} leads by{' '} - +{Math.abs(delta)} pts - - )} -
- - {/* Parity Balance Bar with Center Notch */} -
- {/* Dual progress bar */} -
- {/* Left side (Player A, fills toward center) */} -
-
-
- {/* Right side (Player B, fills toward right) */} -
-
-
-
- - {/* Ticks & Labels */} -
- 0 - {DAILY_PAR_CEILING} DAILY PAR TARGET - 0 -
-
- - {/* Micro Game Mechanic Callout */} -

- Both partners start with independent custom habit ladders calibrated to exactly{' '} - 240 points. -

-
- - {/* Player B Score Column */} -
-
- - P2 - - {activePreset.playerB.name} -
-
- - / {DAILY_PAR_CEILING} PTS - - - {scoreB} - -
-
- - {pctB}% - - - {isParB ? '✓ PAR ACHIEVED' : `${DAILY_PAR_CEILING - scoreB} pts to par`} - -
-
-
- - {/* Live Stake Preview Sub-panel */} -
-
-
- - - Active Weekly Stake - -
-

{activePreset.stakeTitle}

-

{activePreset.stakeWager}

-
- -
- - Current Sunday Liability - - {trailingPlayer ? ( -
- - {trailingPlayer.name} pays - - - (trailing by {Math.abs(delta)} pts) - -
- ) : ( -
- Equal Standings • No Deficit Wager -
- )} -
-
-
- - {/* Side-by-Side Two-Player Columns */} -
- {/* PLAYER A COLUMN */} - - setInspectingProof({ - habit, - player: activePreset.playerA, - isChecked: checkedA.has(habit.id), - }) - } - currentScore={scoreA} - totalPossible={totalPossibleA} - /> - - {/* PLAYER B COLUMN */} - - setInspectingProof({ - habit, - player: activePreset.playerB, - isChecked: checkedB.has(habit.id), - }) - } - currentScore={scoreB} - totalPossible={totalPossibleB} - /> -
- - {/* Educational Game-Mechanic Footer Callout */} -
-
-
- -
-
-

- Deterministic Two-Player Calibrated Parity -

-

- In conventional habit trackers, one partner completing three trivial check-ins outscores a partner completing a grueling marathon training session. Do It eliminates false parity: each habit ladder is calibrated to a hard 240 Daily Par ceiling. Whether you balance creative sprint blocks, heavy compound lifts, or foreign language immersion, difficulty is equalized at the architectural layer. -

-
-
-
- - {/* Proof Inspection Dialog Modal */} - {inspectingProof && ( - setInspectingProof(null)} - /> - )} -
- ); -} - -// ---------------------------------------------------------------------------- -// Sub-components: PlayerColumn, HabitCard, ProofModal -// ---------------------------------------------------------------------------- - -interface PlayerColumnProps { - player: SimulatorPlayer; - checkedHabits: Set; - onToggleHabit: (habitId: string) => void; - showProofBadges: boolean; - onInspectProof: (habit: SimulatorHabit) => void; - currentScore: number; - totalPossible: number; -} - -function PlayerColumn({ - player, - checkedHabits, - onToggleHabit, - showProofBadges, - onInspectProof, - currentScore, - totalPossible, -}: PlayerColumnProps) { - const isPar = currentScore >= DAILY_PAR_CEILING; - - return ( -
- {/* Column Header */} -
-
-
-
- {player.avatarInitials} -
-
-
-

{player.name}

- - Ladder: {totalPossible} pts - -
-

{player.tagline}

-
-
- -
-
- {currentScore}{' '} - / {DAILY_PAR_CEILING} -
-
- {checkedHabits.size} of {player.habits.length} habits done -
-
-
- - {/* Small column progress bar */} -
-
-
-
- - {/* Habit List */} -
- {player.habits.map((habit) => { - const isChecked = checkedHabits.has(habit.id); - return ( -
-
- {/* Checkbox & Details */} - - - {/* Points Badge */} -
- - +{habit.points} PTS - -
-
- - {/* Simulated Proof Telemetry / Photo Badge */} - {showProofBadges && ( -
- - - - {isChecked ? 'Verified' : 'Pending Proof'} - -
- )} -
- ); - })} -
- - {/* Column Footer */} -
- Status - - {isPar ? '240 Par Completed' : `${DAILY_PAR_CEILING - currentScore} pts needed`} - -
-
- ); -} - - -// ---------------------------------------------------------------------------- -// Modal: Proof Detail Simulator -// ---------------------------------------------------------------------------- -interface ProofInspectionModalProps { - data: { - habit: SimulatorHabit; - player: SimulatorPlayer; - isChecked: boolean; - }; - onClose: () => void; -} - -function ProofInspectionModal({ data, onClose }: ProofInspectionModalProps) { - const { habit, player, isChecked } = data; - const { proof } = habit; - - return ( -
-
e.stopPropagation()} - > - {/* Header */} -
-
-
-
-

- {habit.title} -

-
- -
- - {/* Content Body */} -
- {/* Verification Badge Bar */} -
-
-
- -
-
-
- {isChecked ? 'Cryptographically Verified' : 'Simulated Verification Available'} -
-
- Source: {proof.deviceOrSource} -
-
-
- - {proof.verificationCode} - -
- - {/* Primary Metric Preview Card */} -
- - Primary Biometric / Telemetry Record - -
- {proof.metricPrimary} -
-

- {proof.notes} -

-
- - {/* Detailed Sensor Telemetry (if available) */} - {proof.sensorDetails && ( -
- {proof.sensorDetails.avgHr && ( -
-
Heart Rate
-
{proof.sensorDetails.avgHr}
-
- )} - {proof.sensorDetails.distance && ( -
-
GPS Distance
-
{proof.sensorDetails.distance}
-
- )} - {proof.sensorDetails.calories && ( -
-
Active Energy
-
{proof.sensorDetails.calories}
-
- )} - {proof.sensorDetails.cadenceOrPace && ( -
-
Pace / Duration
-
{proof.sensorDetails.cadenceOrPace}
-
- )} - {proof.sensorDetails.exifModel && ( -
-
Optical EXIF Hardware
-
{proof.sensorDetails.exifModel}
-
- )} -
- )} - - {/* Timestamp details */} -
- - - Registered: Today @ {proof.timestamp} - - Allocated Points: +{habit.points} PTS -
-
- - {/* Footer */} -
- -
-
-
- ); -} - -export default ParitySimulator; diff --git a/src/lib/onboarding-content.test.ts b/src/lib/onboarding-content.test.ts deleted file mode 100644 index de91aaf..0000000 --- a/src/lib/onboarding-content.test.ts +++ /dev/null @@ -1,132 +0,0 @@ -import { describe, it } from 'node:test'; -import assert from 'node:assert'; -import { - HERO_CONTENT, - DOMAIN_CONCEPTS, - MANIFESTO, - PARITY_ENGINE_EXPLANATION, - SIMULATOR_PRESETS, - VERIFICATION_FEATURES, - SETTLEMENT_SYSTEM, - KARMA_SYSTEM, - ONBOARDING_STEPS, - PRICING_SPECIFICATION, - FAQ_ITEMS, - ONBOARDING_CONTENT, - getPresetById, - getDomainConceptById, - getFaqItemsByCategory, -} from './onboarding-content'; - -describe('Onboarding Content & Domain Copy Integrity', () => { - it('hero content contains high-craft, unslopped positioning', () => { - assert.ok(HERO_CONTENT.headline.length > 0); - assert.ok(HERO_CONTENT.subheadline.includes('Do It is an intimate two-player accountability duel')); - assert.strictEqual(HERO_CONTENT.valuePillars.length, 3); - assert.strictEqual(HERO_CONTENT.valuePillars[0].metric, '240 Par'); - }); - - it('domain concepts include all core entities with architectural rationales and prohibited terms', () => { - const requiredConcepts = ['duel', 'player', 'habit', 'equivalent-habit', 'check-in', 'proof', 'stake', 'karma', 'rest-day']; - for (const conceptId of requiredConcepts) { - const concept = getDomainConceptById(conceptId); - assert.ok(concept, `Missing concept: ${conceptId}`); - assert.ok(concept.definition.length > 20); - assert.ok(concept.prohibitedTerms.length > 0); - assert.ok(concept.architecturalRationale.length > 20); - } - }); - - it('manifesto enforces the anti-freemium duo-subscription philosophy', () => { - assert.ok(MANIFESTO.theFreemiumTrap.mechanisms.length >= 4); - assert.ok(MANIFESTO.principles.length >= 5); - assert.strictEqual(MANIFESTO.principles[4].title, 'Duo-Billing: One Subscription, Two Players'); - assert.ok(MANIFESTO.pricingAlignment.description.includes('We do not sell data')); - }); - - it('simulator presets strictly enforce 240 Daily Par (1680 weekly potential) with mathematical parity', () => { - assert.ok(SIMULATOR_PRESETS.length >= 3); - - for (const preset of SIMULATOR_PRESETS) { - assert.strictEqual(preset.dailyPar, 240); - assert.strictEqual(preset.weeklyPotential, 1680); - assert.strictEqual(preset.playerA.weeklyTotal, 1680); - assert.strictEqual(preset.playerB.weeklyTotal, 1680); - assert.strictEqual(preset.parityProof.isEqual, true); - assert.strictEqual(preset.parityProof.pointDelta, 0); - - // Verify Player A math: sum of habit weeklyPoints equals 1680 - const calculatedPlayerA = preset.playerA.habits.reduce((acc, h) => acc + h.weeklyPoints, 0); - assert.strictEqual(calculatedPlayerA, 1680, `${preset.id} Player A weekly sum (${calculatedPlayerA}) != 1680`); - - // Verify Player B math: sum of habit weeklyPoints equals 1680 - const calculatedPlayerB = preset.playerB.habits.reduce((acc, h) => acc + h.weeklyPoints, 0); - assert.strictEqual(calculatedPlayerB, 1680, `${preset.id} Player B weekly sum (${calculatedPlayerB}) != 1680`); - } - }); - - it('verification features document photo compression, wearable telemetry, and database idempotence', () => { - const photo = VERIFICATION_FEATURES.find(f => f.category === 'camera'); - const wearable = VERIFICATION_FEATURES.find(f => f.category === 'wearable'); - const crypto = VERIFICATION_FEATURES.find(f => f.category === 'cryptography'); - - assert.ok(photo && photo.technicalDetail.includes('EXIF')); - assert.ok(wearable && wearable.supportedIntegrations && wearable.supportedIntegrations.length > 0); - assert.ok(crypto && crypto.technicalDetail.includes('composite constraint')); - }); - - it('settlement mechanism details the Sunday midnight Hall of Champions ceremony', () => { - assert.strictEqual(SETTLEMENT_SYSTEM.ceremonyName, 'The Hall of Champions'); - assert.ok(SETTLEMENT_SYSTEM.rules.length >= 4); - assert.ok(SETTLEMENT_SYSTEM.curatedStakes.length >= 4); - }); - - it('karma system decouples lifetime consistency from weekly resets', () => { - assert.ok(KARMA_SYSTEM.milestones.length >= 4); - assert.strictEqual(KARMA_SYSTEM.milestones[0].points, 1000); - assert.ok(KARMA_SYSTEM.contrastWithLeaderboard.length > 20); - }); - - it('onboarding steps guide the user through the 6-step initialization journey', () => { - assert.strictEqual(ONBOARDING_STEPS.length, 6); - assert.strictEqual(ONBOARDING_STEPS[0].slug, 'initialize-duel'); - assert.strictEqual(ONBOARDING_STEPS[1].slug, 'invite-counterpart'); - assert.strictEqual(ONBOARDING_STEPS[2].slug, 'assemble-habits'); - assert.strictEqual(ONBOARDING_STEPS[3].slug, 'calibrate-parity'); - assert.strictEqual(ONBOARDING_STEPS[4].slug, 'pledge-the-stake'); - assert.strictEqual(ONBOARDING_STEPS[5].slug, 'bind-telemetry'); - }); - - it('pricing copy leaves purchasable prices to Stripe and makes no trial promise', () => { - assert.deepStrictEqual(PRICING_SPECIFICATION.plans, []); - assert.ok(PRICING_SPECIFICATION.trialPolicy.includes('No free trial')); - }); - - it('faq contains comprehensive coverage across all categories', () => { - const philosophyFaq = getFaqItemsByCategory('philosophy'); - const parityFaq = getFaqItemsByCategory('parity'); - const hardwareFaq = getFaqItemsByCategory('hardware'); - const subscriptionFaq = getFaqItemsByCategory('subscription'); - const fairnessFaq = getFaqItemsByCategory('fairness'); - - assert.ok(philosophyFaq.length > 0); - assert.ok(parityFaq.length > 0); - assert.ok(hardwareFaq.length > 0); - assert.ok(subscriptionFaq.length > 0); - assert.ok(fairnessFaq.length > 0); - }); - - it('preset finder helper returns expected presets or undefined', () => { - assert.ok(getPresetById('athlete-vs-creator')); - assert.strictEqual(getPresetById('non-existent-preset'), undefined); - }); - - it('aggregate content package bundles all structured copy', () => { - assert.strictEqual(ONBOARDING_CONTENT.hero, HERO_CONTENT); - assert.strictEqual(ONBOARDING_CONTENT.domainConcepts, DOMAIN_CONCEPTS); - assert.strictEqual(ONBOARDING_CONTENT.manifesto, MANIFESTO); - assert.strictEqual(ONBOARDING_CONTENT.simulatorPresets, SIMULATOR_PRESETS); - assert.strictEqual(ONBOARDING_CONTENT.pricing, PRICING_SPECIFICATION); - assert.strictEqual(ONBOARDING_CONTENT.faq, FAQ_ITEMS); - }); -}); diff --git a/src/lib/onboarding-content.ts b/src/lib/onboarding-content.ts deleted file mode 100644 index 4f5becf..0000000 --- a/src/lib/onboarding-content.ts +++ /dev/null @@ -1,1506 +0,0 @@ -import type { HabitCategory } from './types'; - -// ============================================================================ -// DO IT — Multi-Tenant Two-Player Habit Parity & Accountability Engine -// Domain Copy, Manifesto, Simulator Presets, Onboarding & Pricing Specifications -// ============================================================================ - -export type VerificationType = 'photo' | 'wearable' | 'manual'; -export type DomainCategory = 'entity' | 'mechanic' | 'settlement' | 'integrity'; -export type FaqCategory = 'philosophy' | 'parity' | 'hardware' | 'subscription' | 'fairness'; - -export interface HeroContent { - kicker: string; - headline: string; - subheadline: string; - emphasisPhrase: string; - primaryCta: { - label: string; - sublabel: string; - href: string; - }; - secondaryCta: { - label: string; - sublabel: string; - href: string; - }; - valuePillars: Array<{ - label: string; - metric: string; - context: string; - }>; -} - -export interface DomainConcept { - id: string; - term: string; - category: DomainCategory; - summary: string; - definition: string; - prohibitedTerms: string[]; - architecturalRationale: string; -} - -export interface ManifestoPoint { - index: string; - title: string; - critique: string; - counterPosition: string; - architecturalEnforcement: string; -} - -export interface AntiFreemiumManifesto { - headline: string; - subheadline: string; - coreThesis: string; - theFreemiumTrap: { - title: string; - mechanisms: Array<{ - flaw: string; - industryStandard: string; - doItArchitecture: string; - }>; - }; - principles: ManifestoPoint[]; - pricingAlignment: { - headline: string; - description: string; - pairEconomics: string; - }; -} - -export interface SimulatorHabitItem { - title: string; - category: HabitCategory; - points: number; - frequency: 'daily' | 'weekly'; - weeklyTargetDays: number; - weeklyPoints: number; - verificationType: VerificationType; - iconName: string; - telemetryBinding?: string; - notes?: string; -} - -export interface SimulatorPlayerPlan { - role: 'owner' | 'guest'; - archetype: string; - personaTitle: string; - lifestyleContext: string; - habits: SimulatorHabitItem[]; - weeklyTotal: number; - dailyParEquivalent: number; -} - -export interface ParityPreset { - id: string; - title: string; - tagline: string; - description: string; - dailyPar: number; - weeklyPotential: number; - playerA: SimulatorPlayerPlan; - playerB: SimulatorPlayerPlan; - parityProof: { - isEqual: boolean; - pointDelta: number; - explanation: string; - }; -} - -export interface VerificationFeature { - id: string; - title: string; - badge: string; - category: 'camera' | 'wearable' | 'cryptography'; - summary: string; - technicalDetail: string; - supportedIntegrations?: string[]; -} - -export interface SettlementMechanism { - id: string; - name: string; - ceremonyName: string; - schedule: string; - triggerTime: string; - description: string; - rules: string[]; - curatedStakes: Array<{ - title: string; - category: 'ritual' | 'experience' | 'forfeit' | 'privilege'; - commitment: string; - }>; -} - -export interface KarmaMilestone { - points: number; - name: string; - description: string; - badgeId: string; -} - -export interface KarmaSystemContent { - headline: string; - subheadline: string; - definition: string; - contrastWithLeaderboard: string; - permanenceRule: string; - milestones: KarmaMilestone[]; -} - -export interface OnboardingStep { - stepNumber: number; - slug: string; - title: string; - shortTitle: string; - tagline: string; - instruction: string; - technicalMechanism: string; - actorRequirement: 'solo' | 'bilateral' | 'passive'; - estimatedSeconds: number; - tips: string[]; -} - -export interface PricingPlan { - id: 'monthly' | 'annual'; - name: string; - cadence: 'month' | 'year'; - amountUsd: number; - perMonthEquivalentUsd: number; - discountPercentage?: number; - badge?: string; - billStatement: string; - perPersonBreakdown: string; - features: string[]; - ctaLabel: string; -} - -export interface PricingSpecification { - headline: string; - subheadline: string; - pairModelClarification: string; - trialPolicy: string; - guaranteeNotice: string; - plans: PricingPlan[]; - commitments: string[]; -} - -export interface FaqItem { - id: string; - category: FaqCategory; - question: string; - answer: string; - footNote?: string; -} - -// ============================================================================ -// 1. HERO CONTENT -// ============================================================================ - -export const HERO_CONTENT: HeroContent = { - kicker: 'Two-Player Habit Parity Engine', - headline: 'Asymmetric lives. Equal stakes.', - subheadline: - 'Do It is an intimate two-player accountability duel. You choose your habits, your partner chooses theirs, and our engine calibrates both routines to an identical point ceiling. Sunday midnight settles the week.', - emphasisPhrase: 'No public feeds. No solo surrender. One shared wager.', - primaryCta: { - label: 'Initialize Duel', - sublabel: 'One subscription covers both players', - href: '/#start', - }, - secondaryCta: { - label: 'Explore Parity Math', - sublabel: 'How 240 Daily Par balances unlike lives', - href: '/#simulator', - }, - valuePillars: [ - { - label: 'Calibrated Parity', - metric: '240 Par', - context: 'Both players share identical weekly point ceilings regardless of habit choices', - }, - { - label: 'Verified Telemetry', - metric: '0 Cheats', - context: 'Direct Apple Health, Garmin, and client-compressed photo confirmation', - }, - { - label: 'Duo Economics', - metric: '$6 / mo', - context: 'One subscription covers both participants; see current billing plans', - }, - ], -}; - -// ============================================================================ -// 2. DOMAIN CONCEPTS & UBIQUITOUS LANGUAGE -// Strict alignment with CONTEXT.md & Domain-Driven Architecture -// ============================================================================ - -export const DOMAIN_CONCEPTS: DomainConcept[] = [ - { - id: 'duel', - term: 'Duel', - category: 'entity', - summary: 'An isolated two-player competitive tenancy.', - definition: - 'A private, cryptographically secured boundary connecting exactly two participants: an owner and a guest. Every habit, check-in, rest day, reaction, and stake exists strictly inside this single tenancy, protected by PostgreSQL Row-Level Security.', - prohibitedTerms: ['Room', 'Group', 'Team', 'Workspace', 'Organization', 'Channel'], - architecturalRationale: - 'Accountability collapses in groups. Group trackers allow social loafing and silent dropouts. A Duel is strictly dyadic: if one player slows down, the tension is immediate and visible.', - }, - { - id: 'player', - term: 'Player', - category: 'entity', - summary: 'One of the two authenticated participants in a Duel.', - definition: - 'An individual bound to either the owner or guest slot within a Duel. Each Player holds read visibility across the shared tenancy but holds write authority exclusively over their own habits, check-ins, and rest days.', - prohibitedTerms: ['User', 'Customer', 'Client', 'Member', 'Subscriber', 'Account'], - architecturalRationale: - 'Language shapes posture. Users consume; Players enter an arena with rules, agency, and counterpart responsibility.', - }, - { - id: 'habit', - term: 'Habit', - category: 'entity', - summary: 'A recurring behavioral commitment with explicit mathematical weight.', - definition: - 'A personal daily or weekly routine defined by a point value (5–100 pts), category classification, target cadence, and optional quantitative or automated hardware criteria. Habits are not checkboxes; they are calibrated commitments.', - prohibitedTerms: ['Task', 'Todo', 'Chore', 'Goal', 'Item', 'Reminder'], - architecturalRationale: - 'Todo items end when checked. Habits are permanent behavioral loops whose value compounds through consistent execution.', - }, - { - id: 'equivalent-habit', - term: 'Equivalent Habit', - category: 'mechanic', - summary: 'Asymmetric routines calibrated to an identical weekly point potential.', - definition: - 'The foundational innovation of Do It. A marathoner and an author cannot perform the same actions, but their routines can be mathematically balanced to 240 Daily Par (1,680 weekly potential). Neither player gets a handicap; both encounter equivalent discipline ceilings.', - prohibitedTerms: ['Handicap', 'Custom Task', 'Unbalanced Habit', 'Equalizer', 'Cheat Weight'], - architecturalRationale: - 'Enforcing identical routines between two people creates resentment or abandonment. Equivalent habits respect divergent biological and professional realities while preserving razor-sharp competitive balance.', - }, - { - id: 'check-in', - term: 'Check-in', - category: 'mechanic', - summary: 'An idempotent behavioral record stamped to a local civil date.', - definition: - 'The verifiable registration of habit completion. Enforced at the database layer by the composite uniqueness constraint (duel_id, habit_id, date). Replays are safe; duplicate double-counting is mathematically impossible.', - prohibitedTerms: ['Tick-off', 'Completion', 'Log', 'Entry', 'Click', 'Submission'], - architecturalRationale: - 'Distributed offline mobile usage requires mathematical idempotence. A Check-in represents an atomic truth about a calendar day.', - }, - { - id: 'proof', - term: 'Proof', - category: 'integrity', - summary: 'Visual confirmation compressed client-side or hardware telemetry.', - definition: - 'Client-side compressed photo verification (EXIF stripped for privacy) or automated biometric telemetry (Apple Health, Garmin, Strava). Proof is surfaced directly in the shared activity feed and duel gallery for peer inspection.', - prohibitedTerms: ['Attachment', 'Evidence', 'Upload', 'Screenshot', 'File'], - architecturalRationale: - 'Self-reported checkboxes invite rationalization and self-deception. Proof introduces frictionless verification without corporate surveillance.', - }, - { - id: 'stake', - term: 'Stake', - category: 'settlement', - summary: 'The mutual real-world reward or forfeit wagered on the leaderboard window.', - definition: - 'A tangible covenant agreed upon before the weekly or monthly competition cycle starts (e.g. Sunday dinner date, chore forfeit, or weekend excursion). Settled and permanently inscribed into the Hall of Champions ledger upon period close.', - prohibitedTerms: ['Prize', 'Bounty', 'Purchase', 'Shop Item', 'Bet', 'Gamble'], - architecturalRationale: - 'Digital points without physical consequence fade into apathy. A tangible stake grounds daily discipline into shared lived experience.', - }, - { - id: 'karma', - term: 'Karma', - category: 'mechanic', - summary: 'The permanent, non-spendable lifetime consistency score.', - definition: - 'The immutable sum of all historical check-in points earned by a Player since duel inception. Karma never resets, cannot be gambled, and cannot be spent. It is an unassailable record of lifetime discipline.', - prohibitedTerms: ['XP', 'Currency', 'Balance', 'Spendable Tokens', 'Credits', 'Coins'], - architecturalRationale: - 'Weekly competitions provide urgency; Karma provides decadal legacy. Decoupling lifetime consistency from the weekly reset prevents defeatism.', - }, - { - id: 'rest-day', - term: 'Rest Day', - category: 'mechanic', - summary: 'An explicit declared recovery date that safeguards streak continuity.', - definition: - 'A declared pause scoped to a specific Player within the Duel. Rest Days preserve streak longevity without generating artificial leaderboard points, eliminating the toxic incentive to train injured or burn out.', - prohibitedTerms: ['Cheat Day', 'Skip', 'Freeze Token', 'Freebie', 'Pass'], - architecturalRationale: - 'Recovery is a biological necessity. Honest recovery declared in advance differs fundamentally from covert failure or gamified freeze tokens.', - }, -]; - -// ============================================================================ -// 3. THE ANTI-FREEMIUM MANIFESTO -// Precision critique of extractive product models -// ============================================================================ - -export const MANIFESTO: AntiFreemiumManifesto = { - headline: 'Accountability requires skin in the game.', - subheadline: - 'Free apps monetize your distracted gaze. Do It is a paid mechanical engine designed for two people to conquer inertia.', - coreThesis: - 'When software is free, the product is your attention. To keep you returning, free apps rely on fake streaks, frictionless checkboxes, intrusive ad networks, and selling private health data. Real behavioral change cannot exist on a foundation of zero-cost promises. We charge a fair, transparent subscription for both players so our incentives remain uncompromised.', - theFreemiumTrap: { - title: 'The Anatomy of the Freemium Illusion', - mechanisms: [ - { - flaw: 'The Zero-Cost Surrender Trap', - industryStandard: - 'Free downloads with immediate abandonment after 4 days. Zero financial friction means zero emotional stake.', - doItArchitecture: - 'One subscription shared between two players supports the Duel.', - }, - { - flaw: 'Vanity Streaks & Fake Checkboxes', - industryStandard: - 'Easily gamed checkmarks without verification. Apps celebrate hollow streaks to boost daily active user (DAU) graphs.', - doItArchitecture: - 'Equivalent Habit point parities (240 Par) combined with required photo proof and direct wearable telemetry.', - }, - { - flaw: 'Surveillance Monetization', - industryStandard: - 'Ad tracking SDKs scraping sleep cycles, biometrics, and mental health logs to target behavioural ads.', - doItArchitecture: - 'Zero third-party advertising, zero tracking SDKs, client-side EXIF stripping, and strict tenant isolation.', - }, - { - flaw: 'Dark Pattern Dopamine Cycles', - industryStandard: - 'Artificial coin shops, energy bars, push notification cascades, and gambling mechanics designed for screen time.', - doItArchitecture: - 'Quiet, high-craft, tool-grade interface. Minimal haptics, purposeful sound design, and an exit back into real life.', - }, - ], - }, - principles: [ - { - index: '01', - title: 'Dyadic Tenancy Over Social Feeds', - critique: - 'Social habit apps build global feeds, follower graphs, and public vanity metrics where accountability diffuses into performative posting.', - counterPosition: - 'Meaningful accountability exists exclusively between two humans who look each other in the eye.', - architecturalEnforcement: - 'A Duel is capped at exactly two players. There are no public leaderboards, no follower lists, and no algorithmic discovery.', - }, - { - index: '02', - title: 'Equivalent Calibration Over Generic Checklists', - critique: - 'Generic trackers assume all humans have identical schedules and identical goals, or leave point weighting entirely arbitrary.', - counterPosition: - 'Different lives require different habits, but competitive fairness demands calibrated equivalence.', - architecturalEnforcement: - 'The 240 Daily Par engine solves a constrained knapsack problem to balance asymmetric weekly point potentials to the exact same total.', - }, - { - index: '03', - title: 'Verifiable Proof Over Honour-System Slop', - critique: - 'Tapping a digital button while lying in bed is not discipline. The honour system decays under fatigue.', - counterPosition: - 'Habits must be corroborated by physical evidence or biometric telemetry.', - architecturalEnforcement: - 'Local image downsampling with stripped EXIF and background synchronization with Apple Health, Garmin, and Strava.', - }, - { - index: '04', - title: 'Sunday Settlement Over Endless Rolling Streaks', - critique: - 'Endless streaks create irrational anxiety. Once an arbitrary 200-day streak breaks, users quit entirely out of demoralization.', - counterPosition: - 'Discipline thrives in recurring, bounded campaign windows with tangible real-world consequences.', - architecturalEnforcement: - 'Leaderboard scores reset every Sunday midnight. The winner claims the stake, the slate wipes clean, and lifetime Karma preserves long-term legacy.', - }, - { - index: '05', - title: 'Duo-Billing: One Subscription, Two Players', - critique: - 'SaaS companies nickel-and-dime each user with separate paywalls, referral traps, and bait-and-switch guest restrictions.', - counterPosition: - 'Accountability is a shared pact. One partner paying should invite the other without friction or secondary billing.', - architecturalEnforcement: - 'One flat subscription covers the owner and the guest for the entire life of the Duel.', - }, - ], - pricingAlignment: { - headline: 'Alignment of Purpose', - description: - 'We do not sell data. We do not run ads. We do not manipulate your dopamine. You pay us for an uncompromising instrument of personal discipline, and we build the fastest, most reliable engine on earth to deliver it.', - pairEconomics: - 'The Duel owner manages one subscription for both players. See current plans before purchasing.', - }, -}; - -// ============================================================================ -// 4. PARITY SIMULATOR PRESETS & ALGORITHM EXPLANATION -// ============================================================================ - -export const PARITY_ENGINE_EXPLANATION = { - headline: 'The Mathematics of 240 Daily Par', - subheadline: - 'How two humans with radically divergent lives compete on perfectly even terms.', - dailyParValue: 240, - weeklyPointCeiling: 1680, // 240 * 7 - corePhilosophy: - 'Competitive parity does not mean doing the same exercises or reading the same books. It means committing an equivalent proportion of human intentionality toward disciplined living. In Do It, every valid duel plan is calibrated to 1,680 weekly points (240 Daily Par).', - algorithmicSteps: [ - { - step: '1. Asymmetric Selection', - detail: - 'Player A selects strength training, hydration, and deep work blocks. Player B selects marathon endurance, language fluency, and meditation.', - }, - { - step: '2. Cadence & Unit Weighting', - detail: - 'Daily habits provide baseline foundations (10–25 pts). High-intensity sessions (workout 4x/week, cardio 3x/week) are assigned weighted session values.', - }, - { - step: '3. Knapsack Balancing', - detail: - 'The engine balances point distributions across both active plans so that both players reach the exact 1,680 weekly ceiling. Neither player has an unearned advantage.', - }, - ], -}; - -export const SIMULATOR_PRESETS: ParityPreset[] = [ - { - id: 'athlete-vs-creator', - title: 'The Athlete & The Creator', - tagline: 'Endurance physiology meets intellectual craftsmanship', - description: - 'One partner is training for an Ironman triathlon; the other is writing a manuscript and shipping software. Radically different physical exertion, calibrated to identical weekly output.', - dailyPar: 240, - weeklyPotential: 1680, - playerA: { - role: 'owner', - archetype: 'The Athlete', - personaTitle: 'Endurance & Physical Mastery', - lifestyleContext: 'Early morning cardio, high protein intake, progressive overload lifting', - weeklyTotal: 1680, - dailyParEquivalent: 240, - habits: [ - { - title: 'Sleep 8+ Hours', - category: 'foundation', - points: 25, - frequency: 'daily', - weeklyTargetDays: 7, - weeklyPoints: 175, - verificationType: 'wearable', - iconName: 'Moon', - telemetryBinding: 'Apple Health / Garmin Sleep Analysis', - }, - { - title: 'Strength Training', - category: 'physical', - points: 40, - frequency: 'weekly', - weeklyTargetDays: 4, - weeklyPoints: 160, - verificationType: 'wearable', - iconName: 'Dumbbell', - telemetryBinding: 'Garmin Strength Activity (45m+)', - }, - { - title: 'Zone 2 Running / Cycling', - category: 'cardio', - points: 45, - frequency: 'weekly', - weeklyTargetDays: 4, - weeklyPoints: 180, - verificationType: 'wearable', - iconName: 'Heart', - telemetryBinding: 'Strava / Garmin Cardio Telemetry', - }, - { - title: '12,000 Daily Steps', - category: 'physical', - points: 25, - frequency: 'daily', - weeklyTargetDays: 7, - weeklyPoints: 175, - verificationType: 'wearable', - iconName: 'Footprints', - telemetryBinding: 'HealthKit Step Rollup', - }, - { - title: 'Clean High-Protein Fueling', - category: 'nutrition', - points: 25, - frequency: 'daily', - weeklyTargetDays: 7, - weeklyPoints: 175, - verificationType: 'photo', - iconName: 'Apple', - notes: 'Meal photo verification', - }, - { - title: 'Cold Mobility & Stretch', - category: 'physical', - points: 20, - frequency: 'daily', - weeklyTargetDays: 7, - weeklyPoints: 140, - verificationType: 'photo', - iconName: 'Activity', - }, - { - title: 'Deep Sports Hydration (3.5L)', - category: 'foundation', - points: 20, - frequency: 'daily', - weeklyTargetDays: 7, - weeklyPoints: 140, - verificationType: 'manual', - iconName: 'Sparkles', - }, - { - title: 'Rest & Mental Stillness', - category: 'mind', - points: 20, - frequency: 'daily', - weeklyTargetDays: 7, - weeklyPoints: 140, - verificationType: 'manual', - iconName: 'Sparkles', - }, - { - title: 'Evening Athletic Recovery Log', - category: 'mind', - points: 56, - frequency: 'weekly', - weeklyTargetDays: 7, - weeklyPoints: 395, - verificationType: 'manual', - iconName: 'Target', - }, - ], - }, - playerB: { - role: 'guest', - archetype: 'The Creator', - personaTitle: 'Deep Cognitive Craft & Focus', - lifestyleContext: 'Desk-based intellectual output, writing, code architecture, language acquisition', - weeklyTotal: 1680, - dailyParEquivalent: 240, - habits: [ - { - title: 'Sleep 8+ Hours', - category: 'foundation', - points: 25, - frequency: 'daily', - weeklyTargetDays: 7, - weeklyPoints: 175, - verificationType: 'wearable', - iconName: 'Moon', - telemetryBinding: 'Oura Sleep Ring', - }, - { - title: 'Deep Work Block (90m Uninterrupted)', - category: 'deep_work', - points: 50, - frequency: 'daily', - weeklyTargetDays: 6, - weeklyPoints: 300, - verificationType: 'photo', - iconName: 'Briefcase', - notes: 'Photo of focused workspace without tabs', - }, - { - title: 'Book Reading (30 Pages)', - category: 'intellect', - points: 30, - frequency: 'daily', - weeklyTargetDays: 7, - weeklyPoints: 210, - verificationType: 'photo', - iconName: 'BookOpen', - notes: 'Annotated page capture', - }, - { - title: 'Swedish / Polish Language Study', - category: 'language', - points: 25, - frequency: 'daily', - weeklyTargetDays: 7, - weeklyPoints: 175, - verificationType: 'photo', - iconName: 'Globe', - notes: 'Completed session screenshot', - }, - { - title: 'Daily Movement Walk (8k Steps)', - category: 'physical', - points: 25, - frequency: 'daily', - weeklyTargetDays: 7, - weeklyPoints: 175, - verificationType: 'wearable', - iconName: 'Footprints', - telemetryBinding: 'Apple Health Steps', - }, - { - title: 'Writing & Synthesizing Notes', - category: 'skills', - points: 35, - frequency: 'weekly', - weeklyTargetDays: 5, - weeklyPoints: 175, - verificationType: 'manual', - iconName: 'Target', - }, - { - title: 'Physical Workspace Reset', - category: 'environment', - points: 20, - frequency: 'daily', - weeklyTargetDays: 7, - weeklyPoints: 140, - verificationType: 'photo', - iconName: 'CheckCircle2', - notes: 'Clean desk verification', - }, - { - title: 'Zero Screen Time 45m Before Bed', - category: 'foundation', - points: 25, - frequency: 'daily', - weeklyTargetDays: 7, - weeklyPoints: 175, - verificationType: 'manual', - iconName: 'Moon', - }, - { - title: 'Weekly Financial & Review Audit', - category: 'finance', - points: 155, - frequency: 'weekly', - weeklyTargetDays: 1, - weeklyPoints: 155, - verificationType: 'manual', - iconName: 'Briefcase', - }, - ], - }, - parityProof: { - isEqual: true, - pointDelta: 0, - explanation: - 'Both players accumulate exactly 1,680 points across seven days. Player A earns high points through athletic training volume; Player B earns high points through uninterrupted intellectual focus. The competition is strictly fair.', - }, - }, - { - id: 'founder-vs-resident', - title: 'The Startup Founder & The Medical Resident', - tagline: 'High-uncertainty builder meets rigorous clinical shifts', - description: - 'A founder navigating sprint deliverables competing with a junior doctor on grueling hospital rotations. Fixed weekly sessions preserve accountability amid erratic working hours.', - dailyPar: 240, - weeklyPotential: 1680, - playerA: { - role: 'owner', - archetype: 'The Founder', - personaTitle: 'Velocity & Company Building', - lifestyleContext: 'Product shipping, cold outreach, strength workouts, cognitive stability', - weeklyTotal: 1680, - dailyParEquivalent: 240, - habits: [ - { - title: 'Consistent Wakeup (06:30)', - category: 'foundation', - points: 25, - frequency: 'daily', - weeklyTargetDays: 7, - weeklyPoints: 175, - verificationType: 'wearable', - iconName: 'Moon', - }, - { - title: 'Morning Product Ship Block (2h)', - category: 'deep_work', - points: 45, - frequency: 'weekly', - weeklyTargetDays: 5, - weeklyPoints: 225, - verificationType: 'photo', - iconName: 'Briefcase', - }, - { - title: 'Strength Lifting Session', - category: 'physical', - points: 40, - frequency: 'weekly', - weeklyTargetDays: 4, - weeklyPoints: 160, - verificationType: 'wearable', - iconName: 'Dumbbell', - }, - { - title: 'Customer Discovery / Outreach', - category: 'skills', - points: 35, - frequency: 'weekly', - weeklyTargetDays: 5, - weeklyPoints: 175, - verificationType: 'manual', - iconName: 'Target', - }, - { - title: '10,000 Steps', - category: 'physical', - points: 25, - frequency: 'daily', - weeklyTargetDays: 7, - weeklyPoints: 175, - verificationType: 'wearable', - iconName: 'Footprints', - }, - { - title: 'Macro-Tracked Nutrition', - category: 'nutrition', - points: 25, - frequency: 'daily', - weeklyTargetDays: 7, - weeklyPoints: 175, - verificationType: 'photo', - iconName: 'Apple', - }, - { - title: 'Non-Fiction Reading (20m)', - category: 'intellect', - points: 25, - frequency: 'daily', - weeklyTargetDays: 7, - weeklyPoints: 175, - verificationType: 'manual', - iconName: 'BookOpen', - }, - { - title: 'Digital Shutdown by 22:30', - category: 'foundation', - points: 20, - frequency: 'daily', - weeklyTargetDays: 7, - weeklyPoints: 140, - verificationType: 'manual', - iconName: 'Moon', - }, - { - title: 'Weekly P&L and Metrics Sync', - category: 'finance', - points: 140, - frequency: 'weekly', - weeklyTargetDays: 2, - weeklyPoints: 280, - verificationType: 'manual', - iconName: 'Briefcase', - }, - ], - }, - playerB: { - role: 'guest', - archetype: 'The Medical Resident', - personaTitle: 'Clinical Endurance & Patient Care', - lifestyleContext: '12-hour hospital shifts, emergency triage, clinical literature recall', - weeklyTotal: 1680, - dailyParEquivalent: 240, - habits: [ - { - title: 'Clinical Hydration (2.5L Water Bottle)', - category: 'foundation', - points: 25, - frequency: 'daily', - weeklyTargetDays: 7, - weeklyPoints: 175, - verificationType: 'photo', - iconName: 'Sparkles', - }, - { - title: 'Daily Shift Steps (12,000 Steps)', - category: 'physical', - points: 30, - frequency: 'daily', - weeklyTargetDays: 7, - weeklyPoints: 210, - verificationType: 'wearable', - iconName: 'Footprints', - telemetryBinding: 'Apple Watch', - }, - { - title: 'Medical Journal Paper Review', - category: 'intellect', - points: 40, - frequency: 'weekly', - weeklyTargetDays: 4, - weeklyPoints: 160, - verificationType: 'photo', - iconName: 'BookOpen', - }, - { - title: 'High-Intensity Calisthenics / Run', - category: 'cardio', - points: 45, - frequency: 'weekly', - weeklyTargetDays: 3, - weeklyPoints: 135, - verificationType: 'wearable', - iconName: 'Heart', - }, - { - title: 'Shift Meal Prep (Non-Cafeteria)', - category: 'nutrition', - points: 30, - frequency: 'weekly', - weeklyTargetDays: 5, - weeklyPoints: 150, - verificationType: 'photo', - iconName: 'Apple', - }, - { - title: 'Post-Shift Mindful Decompression', - category: 'mind', - points: 25, - frequency: 'daily', - weeklyTargetDays: 7, - weeklyPoints: 175, - verificationType: 'manual', - iconName: 'Sparkles', - }, - { - title: 'Targeted Sleep Hygiene Protocol', - category: 'foundation', - points: 35, - frequency: 'daily', - weeklyTargetDays: 7, - weeklyPoints: 245, - verificationType: 'wearable', - iconName: 'Moon', - }, - { - title: 'Emergency Response Flashcard Drill', - category: 'skills', - points: 30, - frequency: 'weekly', - weeklyTargetDays: 5, - weeklyPoints: 150, - verificationType: 'manual', - iconName: 'Target', - }, - { - title: 'Weekly Clinical Review & Recovery', - category: 'environment', - points: 280, - frequency: 'weekly', - weeklyTargetDays: 1, - weeklyPoints: 280, - verificationType: 'manual', - iconName: 'CheckCircle2', - }, - ], - }, - parityProof: { - isEqual: true, - pointDelta: 0, - explanation: - 'A medical resident on shift cannot perform scheduled 2-hour coding blocks; a software founder cannot claim hospital triage steps. Calibrating both to 1,680 weekly points delivers parity without asking either to compromise their craft.', - }, - }, - { - id: 'mindfulness-vs-discipline', - title: 'The Stoic Scholar & The Kinetic Builder', - tagline: 'Quiet contemplative habits paired with explosive kinetic output', - description: - 'Demonstrating that mental disciplines (journaling, meditation, cold exposure, writing) carry the exact same mathematical weight as barbell squats, sprinting, and metabolic conditioning.', - dailyPar: 240, - weeklyPotential: 1680, - playerA: { - role: 'owner', - archetype: 'The Stoic Scholar', - personaTitle: 'Internal Equilibrium & Wisdom', - lifestyleContext: 'Meditation, physical stillness, classical literature, rigorous reflection', - weeklyTotal: 1680, - dailyParEquivalent: 240, - habits: [ - { - title: 'Morning Silence & Meditation (20m)', - category: 'mind', - points: 35, - frequency: 'daily', - weeklyTargetDays: 7, - weeklyPoints: 245, - verificationType: 'manual', - iconName: 'Sparkles', - }, - { - title: 'Daily Handwritten Reflection', - category: 'mind', - points: 30, - frequency: 'daily', - weeklyTargetDays: 7, - weeklyPoints: 210, - verificationType: 'photo', - iconName: 'BookOpen', - notes: 'Notebook photo', - }, - { - title: 'Classical Philosophy Reading (45m)', - category: 'intellect', - points: 35, - frequency: 'daily', - weeklyTargetDays: 7, - weeklyPoints: 245, - verificationType: 'manual', - iconName: 'BookOpen', - }, - { - title: 'Cold Exposure Shower / Plunge', - category: 'foundation', - points: 25, - frequency: 'daily', - weeklyTargetDays: 7, - weeklyPoints: 175, - verificationType: 'manual', - iconName: 'Sparkles', - }, - { - title: 'Mindful Walking (45m)', - category: 'physical', - points: 30, - frequency: 'daily', - weeklyTargetDays: 7, - weeklyPoints: 210, - verificationType: 'wearable', - iconName: 'Footprints', - }, - { - title: 'Intermittent Fasting (16h)', - category: 'nutrition', - points: 25, - frequency: 'daily', - weeklyTargetDays: 7, - weeklyPoints: 175, - verificationType: 'manual', - iconName: 'Apple', - }, - { - title: 'Living Space Minimalist Reset', - category: 'environment', - points: 25, - frequency: 'daily', - weeklyTargetDays: 7, - weeklyPoints: 175, - verificationType: 'photo', - iconName: 'CheckCircle2', - }, - { - title: 'Sleep 8 Hours', - category: 'foundation', - points: 25, - frequency: 'daily', - weeklyTargetDays: 7, - weeklyPoints: 175, - verificationType: 'wearable', - iconName: 'Moon', - }, - { - title: 'Weekly Digital Fast (4 Hours Offline)', - category: 'deep_work', - points: 70, - frequency: 'weekly', - weeklyTargetDays: 1, - weeklyPoints: 70, - verificationType: 'manual', - iconName: 'Target', - }, - ], - }, - playerB: { - role: 'guest', - archetype: 'The Kinetic Builder', - personaTitle: 'Mechanical Velocity & Physical Drive', - lifestyleContext: 'Heavy barbell compound lifts, high-output coding, cycling intervals', - weeklyTotal: 1680, - dailyParEquivalent: 240, - habits: [ - { - title: 'Heavy Barbell Compound Session', - category: 'physical', - points: 50, - frequency: 'weekly', - weeklyTargetDays: 4, - weeklyPoints: 200, - verificationType: 'wearable', - iconName: 'Dumbbell', - }, - { - title: 'HIIT / Sprint Intervals (25m)', - category: 'cardio', - points: 40, - frequency: 'weekly', - weeklyTargetDays: 3, - weeklyPoints: 120, - verificationType: 'wearable', - iconName: 'Heart', - }, - { - title: 'Deep Engineering Focus (90m)', - category: 'deep_work', - points: 45, - frequency: 'daily', - weeklyTargetDays: 5, - weeklyPoints: 225, - verificationType: 'photo', - iconName: 'Briefcase', - }, - { - title: '10,000 Daily Steps', - category: 'physical', - points: 25, - frequency: 'daily', - weeklyTargetDays: 7, - weeklyPoints: 175, - verificationType: 'wearable', - iconName: 'Footprints', - }, - { - title: 'Strict Whole-Food Protein Intake', - category: 'nutrition', - points: 25, - frequency: 'daily', - weeklyTargetDays: 7, - weeklyPoints: 175, - verificationType: 'photo', - iconName: 'Apple', - }, - { - title: 'Sleep 8 Hours', - category: 'foundation', - points: 25, - frequency: 'daily', - weeklyTargetDays: 7, - weeklyPoints: 175, - verificationType: 'wearable', - iconName: 'Moon', - }, - { - title: 'Technical Reading / Architecture', - category: 'skills', - points: 30, - frequency: 'daily', - weeklyTargetDays: 6, - weeklyPoints: 180, - verificationType: 'manual', - iconName: 'BookOpen', - }, - { - title: 'Evening Physical Recovery / Foam Roll', - category: 'physical', - points: 25, - frequency: 'daily', - weeklyTargetDays: 7, - weeklyPoints: 175, - verificationType: 'photo', - iconName: 'Activity', - }, - { - title: 'Weekly Hardware / Workshop Sprint', - category: 'deep_work', - points: 127, - frequency: 'weekly', - weeklyTargetDays: 2, - weeklyPoints: 255, - verificationType: 'manual', - iconName: 'Briefcase', - }, - ], - }, - parityProof: { - isEqual: true, - pointDelta: 0, - explanation: - 'A monk-like contemplative discipline and an explosive athletic discipline hit the exact same 1,680 weekly point ceiling. Intention is what is being measured and competed over.', - }, - }, -]; - -// ============================================================================ -// 5. UNCHEATABLE PROOF & HARDWARE INTEGRATION -// ============================================================================ - -export const VERIFICATION_FEATURES: VerificationFeature[] = [ - { - id: 'photo-proof', - title: 'Client-Compressed Photo Proof', - badge: 'Zero-Leak Privacy', - category: 'camera', - summary: 'Visual confirmation stripped of EXIF metadata before network egress.', - technicalDetail: - 'Photos are downsampled on-device to high-efficiency WebP/JPEG, stripped of EXIF metadata (GPS coordinates and camera serial numbers), and uploaded directly to encrypted tenant storage. Your partner sees your proof in the shared feed; third-party ad networks see nothing.', - }, - { - id: 'wearable-telemetry', - title: 'Biometric Wearable Telemetry', - badge: 'Zero-Click Automation', - category: 'wearable', - summary: 'Direct reconciliation with Apple Health, Garmin, Oura, and Strava.', - technicalDetail: - 'Habits bound to wearable automation evaluate actual physiological records (sleep minutes, active workout heart rate, daily step totals) via OAuth 2.0 and native HealthKit bridges. Manual check-ins maintain precedence while automated rollups enforce session minimums.', - supportedIntegrations: ['Apple Health / HealthKit', 'Garmin Connect', 'Oura Ring Gen 3', 'Strava', 'Google Health Connect'], - }, - { - id: 'idempotent-constraints', - title: 'Cryptographic & Database Idempotence', - badge: 'PostgreSQL RLS', - category: 'cryptography', - summary: 'Duplicate check-ins and retroactive tampering are mathematically impossible.', - technicalDetail: - 'Every check-in is bound to a strict composite constraint (duel_id, habit_id, civil_date). Offline check-ins reconcile deterministically using monotonic vector clocks. You cannot check in twice for the same day, and you cannot inject points into past weeks.', - }, -]; - -// ============================================================================ -// 6. STAKES & SUNDAY MIDNIGHT SETTLEMENT -// ============================================================================ - -export const SETTLEMENT_SYSTEM: SettlementMechanism = { - id: 'sunday-midnight', - name: 'Sunday Midnight Settlement', - ceremonyName: 'The Hall of Champions', - schedule: 'Weekly, Every Sunday at 23:59:59 Local Time', - triggerTime: 'Sunday 23:59:59', - description: - 'Accountability requires an inevitable conclusion. At Sunday midnight, the weekly leaderboard locks permanently, calculates the winner, records the outcome in the immutable Hall of Champions ledger, and resets active scores to zero for the new week.', - rules: [ - 'The Player with the higher weekly point total claims the agreed stake.', - 'In the event of an exact tie, the stake rolls over to double the stakes for the following week, or is declared a mutual draw.', - 'Settled stakes are permanently stamped into the Duel ledger with the final point spread and date.', - 'Leaderboard scores reset to zero at 00:00:00 Monday. Lifetime Karma remains untouched.', - ], - curatedStakes: [ - { - title: 'The Sunday Dinner Date', - category: 'ritual', - commitment: 'The loser reserves and pays for a full dinner at the winner’s chosen restaurant.', - }, - { - title: 'The Weekend Domestic Forfeit', - category: 'forfeit', - commitment: 'The loser assumes all kitchen and laundry duties for the entire upcoming weekend.', - }, - { - title: 'The Curated Book Exchange', - category: 'experience', - commitment: 'The winner picks a book of their choice; the loser purchases it and reads the first 100 pages.', - }, - { - title: 'The Hand-Written Letter of Concession', - category: 'ritual', - commitment: 'A handwritten letter detailing three specific qualities of discipline admired in the winner.', - }, - { - title: 'The Cold Plunge / Morning Coffee Run', - category: 'forfeit', - commitment: 'The loser delivers artisanal morning coffee and fresh pastry to the winner in bed for three consecutive mornings.', - }, - ], -}; - -// ============================================================================ -// 7. PERMANENT KARMA VS. EPHEMERAL LEADERBOARDS -// ============================================================================ - -export const KARMA_SYSTEM: KarmaSystemContent = { - headline: 'Karma is Forever. The Leaderboard Resets.', - subheadline: - 'Urgency demands weekly endings. Self-respect demands decadal permanence.', - definition: - 'Karma is the cumulative sum of all historical check-in points earned by a Player since the day their Duel began. It never resets, cannot be spent on virtual items, and cannot be wagered away.', - contrastWithLeaderboard: - 'The weekly leaderboard creates immediate tactical tension for the Sunday stake. Karma creates long-term strategic pride: a permanent monument to every hour of sleep, every barbell lifted, and every page read.', - permanenceRule: - 'Even if you lose three weekly duels in a row, your Karma continues to grow uninterrupted. Winning the week is about tactical discipline; accumulating Karma is about lifelong identity.', - milestones: [ - { - points: 1000, - name: 'Initiate of the Covenant', - description: 'First proof of endurance: four full weeks of calibrated daily execution.', - badgeId: 'karma_1000', - }, - { - points: 5000, - name: 'Iron Consistence', - description: 'Over 20 weeks of unbroken discipline across all categories.', - badgeId: 'karma_5000', - }, - { - points: 15000, - name: 'Century Pillar', - description: 'One full calendar year of unyielding two-player accountability.', - badgeId: 'karma_15000', - }, - { - points: 50000, - name: 'Unbreakable Vanguard', - description: 'Decade-scale identity transformation witnessed by your counterpart.', - badgeId: 'karma_50000', - }, - ], -}; - -// ============================================================================ -// 8. ONBOARDING WALKTHROUGH STEPS -// ============================================================================ - -export const ONBOARDING_STEPS: OnboardingStep[] = [ - { - stepNumber: 1, - slug: 'initialize-duel', - title: 'Initialize Your Duel Tenancy', - shortTitle: 'Tenancy', - tagline: 'Claim your isolated arena', - instruction: - 'Create your duel with an encrypted owner account and generate a single-use cryptographic invitation link.', - technicalMechanism: - 'Generates a 128-bit secure invite code stored in PostgreSQL with strict 1:1 foreign key relationships.', - actorRequirement: 'solo', - estimatedSeconds: 45, - tips: [ - 'Choose a display name your counterpart immediately recognizes.', - 'One-use invite codes expire automatically once paired.', - ], - }, - { - stepNumber: 2, - slug: 'invite-counterpart', - title: 'Bind Your Counterpart', - shortTitle: 'Pairing', - tagline: 'Accountability requires two', - instruction: - 'Send the secure invite link to your spouse, co-founder, training partner, or best friend. When they accept, your duel locks.', - technicalMechanism: - 'Atomic Supabase handshake binds the guest account to the duel slot, initializing mutual real-time presence.', - actorRequirement: 'bilateral', - estimatedSeconds: 60, - tips: [ - 'Do not invite a passive acquaintance. Invite someone you respect and refuse to disappoint.', - 'Remember: your subscription covers their seat entirely.', - ], - }, - { - stepNumber: 3, - slug: 'assemble-habits', - title: 'Assemble Asymmetric Habits', - shortTitle: 'Habit Selection', - tagline: 'Your lifestyle, your commitments', - instruction: - 'Select 4–8 habits that represent genuine personal growth for your life. Your counterpart selects theirs independently.', - technicalMechanism: - 'Curated catalog spanning Foundation, Physical, Cardio, Mind, Intellect, and Deep Work with custom habit support.', - actorRequirement: 'solo', - estimatedSeconds: 120, - tips: [ - 'Do not copy your partner’s list unless your lives are identical.', - 'Mix high-impact weekly habits (3–4x/week) with daily foundation anchors.', - ], - }, - { - stepNumber: 4, - slug: 'calibrate-parity', - title: 'Calibrate to 240 Daily Par', - shortTitle: 'Parity Calibration', - tagline: 'Mathematical fairness confirmed', - instruction: - 'Tap Balance Plan. Our algorithm weights point values across both players until both active plans reach identical 1,680 weekly potential.', - technicalMechanism: - 'Dynamic integer programming optimizer balances weights within allowable bounds (5–100 pts per session).', - actorRequirement: 'bilateral', - estimatedSeconds: 30, - tips: [ - 'Green indicator confirms exact parity alignment.', - 'If your partner has not finished their plan yet, you can calibrate against standard 240 Par.', - ], - }, - { - stepNumber: 5, - slug: 'pledge-the-stake', - title: 'Pledge the Sunday Stake', - shortTitle: 'Weekly Stake', - tagline: 'Put something tangible on the line', - instruction: - 'Agree on the wager for Sunday midnight. Dinner, chores, an artisanal coffee run, or an honest forfeit.', - technicalMechanism: - 'Inscribes an active Stake record scoped to the current ISO week key (e.g. 2026-W42).', - actorRequirement: 'bilateral', - estimatedSeconds: 60, - tips: [ - 'Choose a stake both players genuinely care about.', - 'Playful rivalry makes consistency delightful.', - ], - }, - { - stepNumber: 6, - slug: 'bind-telemetry', - title: 'Bind Wearables & Camera Permissions', - shortTitle: 'Proof Engine', - tagline: 'Automate verification', - instruction: - 'Connect Apple Health, Garmin, or Oura to automatically verify workouts and sleep, or enable quick photo verification.', - technicalMechanism: - 'Secure on-device permission grant with local-only photo compression and EXIF metadata stripping.', - actorRequirement: 'solo', - estimatedSeconds: 45, - tips: [ - 'Wearable workouts automatically check off without manual tapping.', - 'Photo proof takes less than 3 seconds per completion.', - ], - }, -]; - -// ============================================================================ -// 9. PRICING & SUBSCRIPTION SPECIFICATION -// Fair, honest, duo-seat architecture -// ============================================================================ - -export const PRICING_SPECIFICATION: PricingSpecification = { - headline: 'One subscription. Two players.', - subheadline: - 'No separate seat upgrades. No guest surcharges. No ads. Just two people committed to the work.', - pairModelClarification: - 'One player subscribes; both players receive full, uninhibited access to the Duel for as long as the subscription remains active.', - trialPolicy: - 'No free trial is currently offered. Stripe shows the charge before purchase.', - guaranteeNotice: - 'Manage your subscription and cancellation in the Stripe customer portal.', - // Purchasable plans come from /api/billing/plans, never proposed amounts. - plans: [], - commitments: [ - 'No paywalls between partners: the paying member covers the duel.', - 'No automatic price hikes or hidden checkout add-ons.', - 'Cancel anytime: your historical data and Karma remain exportable.', - 'Local-first privacy: your health data is never sold to brokers.', - ], -}; - -// ============================================================================ -// 10. COMPREHENSIVE FAQ -// Direct, intellectually honest, unslopped answers -// ============================================================================ - -export const FAQ_ITEMS: FaqItem[] = [ - { - id: 'why-two-players-only', - category: 'philosophy', - question: 'Why only two players? Can we add a third or fourth friend?', - answer: - 'No. The architecture is deliberately, permanently restricted to two players. In behavioural psychology, groups of three or more suffer from the Ringelmann effect (social loafing): responsibility diffuses, quiet dropouts go unnoticed, and competition becomes muddy. A two-player duel is intimate and unescapable. If your partner completes their habits and you do not, there is nowhere to hide.', - footNote: 'For teams or larger groups, traditional workplace productivity software is a better fit.', - }, - { - id: 'how-parity-balances-different-lives', - category: 'parity', - question: 'How can a marathon runner and a software engineer compete fairly?', - answer: - 'Through our Equivalent Habit Engine calibrated to 240 Daily Par (1,680 weekly potential). We do not measure raw physical outputs against each other; we measure the execution of disciplined commitments. If the marathoner commits to 4 runs per week and the software engineer commits to 4 deep-work sessions per week, our engine assigns both routines identical mathematical weight. Winning requires completing 100% of your chosen commitments, not matching your partner’s biometrics.', - }, - { - id: 'can-my-partner-cheat', - category: 'fairness', - question: 'What stops my partner from lying and checking off habits from bed?', - answer: - 'Two structural safeguards: Proof and Human Accountability. First, habits can require photo proof or automated wearable telemetry (Apple Health, Garmin, Oura). Photos are compressed and sent directly to the shared activity feed for your inspection. Second, Do It is designed for two people who know and respect each other. If someone cheats their closest friend for a fake leaderboard score, the problem is not software.', - }, - { - id: 'how-does-subscription-cover-both', - category: 'subscription', - question: 'Does my partner need to buy a separate subscription?', - answer: - 'No. The Duel owner manages one subscription for both players. Your existing habits and history remain accessible during the billing rollout.', - footNote: 'If the owner cancels, the duel remains active until the end of the paid billing period.', - }, - { - id: 'what-happens-when-someone-gets-sick', - category: 'fairness', - question: 'What happens if one of us gets injured, sick, or travels?', - answer: - 'Do It includes explicit Rest Days. A Rest Day preserves your active streak without generating artificial points on the leaderboard. If a major life event occurs, both players can agree to roll over the week’s stake into a mutual tie without penalty.', - }, - { - id: 'is-our-health-data-private', - category: 'hardware', - question: 'What happens to our photos and wearable telemetry data?', - answer: - 'Your privacy is guarded at the architecture level. Photos are downsampled on your device, stripped of EXIF metadata (GPS coordinates, timestamp details, camera model), and encrypted in transit. We do not sell health data, we do not train third-party machine learning models on your logs, and we run zero advertising trackers.', - }, - { - id: 'can-we-change-habits-mid-week', - category: 'parity', - question: 'Can we change our habits or adjust points in the middle of a week?', - answer: - 'You can edit habits at any time, but altering active point totals mid-week prompts a re-balancing warning. To prevent opportunistic point inflation during a close race, point modifications require mutual visibility in the activity feed, and previous days’ check-in values remain immutably anchored.', - }, - { - id: 'what-is-the-hall-of-champions', - category: 'philosophy', - question: 'What is the Hall of Champions?', - answer: - 'The Hall of Champions is the permanent historical ledger of your Duel. Every Sunday at midnight, the active week closes, records the final score spread, marks whether the stake was claimed or drawn, and files a permanent chapter in your joint archive. You can look back on months of rivalry and see exactly when each of you peaked.', - }, -]; - -// ============================================================================ -// 11. QUICK SUMMARY EXPORTS & HELPER UTILITIES -// ============================================================================ - -export interface OnboardingContentPackage { - hero: HeroContent; - domainConcepts: DomainConcept[]; - manifesto: AntiFreemiumManifesto; - parityExplanation: typeof PARITY_ENGINE_EXPLANATION; - simulatorPresets: ParityPreset[]; - verificationFeatures: VerificationFeature[]; - settlementSystem: SettlementMechanism; - karmaSystem: KarmaSystemContent; - onboardingSteps: OnboardingStep[]; - pricing: PricingSpecification; - faq: FaqItem[]; -} - -export const ONBOARDING_CONTENT: OnboardingContentPackage = { - hero: HERO_CONTENT, - domainConcepts: DOMAIN_CONCEPTS, - manifesto: MANIFESTO, - parityExplanation: PARITY_ENGINE_EXPLANATION, - simulatorPresets: SIMULATOR_PRESETS, - verificationFeatures: VERIFICATION_FEATURES, - settlementSystem: SETTLEMENT_SYSTEM, - karmaSystem: KARMA_SYSTEM, - onboardingSteps: ONBOARDING_STEPS, - pricing: PRICING_SPECIFICATION, - faq: FAQ_ITEMS, -}; - -export function getPresetById(presetId: string): ParityPreset | undefined { - return SIMULATOR_PRESETS.find(preset => preset.id === presetId); -} - -export function getDomainConceptById(conceptId: string): DomainConcept | undefined { - return DOMAIN_CONCEPTS.find(concept => concept.id === conceptId); -} - -export function getFaqItemsByCategory(category: FaqCategory): FaqItem[] { - return FAQ_ITEMS.filter(item => item.category === category); -}