From afd1ae1528fc8772a10bb45892d8e066d33ad144 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Morais?= Date: Thu, 19 Feb 2026 15:53:32 -0800 Subject: [PATCH 01/10] overhaul phase 1 --- website/app/features/page.tsx | 134 ++++--- website/app/globals.css | 28 +- website/app/hero/hero.tsx | 668 +++++++++++++++++++++------------- website/app/layout.tsx | 4 +- website/app/pricing/page.tsx | 296 ++++++++++----- website/next-env.d.ts | 2 +- 6 files changed, 714 insertions(+), 418 deletions(-) diff --git a/website/app/features/page.tsx b/website/app/features/page.tsx index 3eb6bb7c..4574e899 100644 --- a/website/app/features/page.tsx +++ b/website/app/features/page.tsx @@ -1,6 +1,7 @@ "use client"; import React from "react"; import Image from "next/image"; +import { useWhatsAppWidget } from "../contexts/WhatsAppWidgetContext"; // Channel icons as SVG components const ChannelIcons = { @@ -193,18 +194,19 @@ function ChannelBadge({ status }: { status: string }) { } export default function FeaturesPage() { + const { openWidget } = useWhatsAppWidget(); + return (
{/* Header */}
-

- Built for capturing your journey +

+ Everything under the hood.

- Voice logging, multi-channel chat, AI-powered insights, and a dashboard that turns - your daily conversations into a visual story of your life. + Channels, integrations, and features that make LogLife work. No app needed.

@@ -212,8 +214,8 @@ export default function FeaturesPage() {
-

Channels

-

Journal from anywhere

+ Channels +

Journal from anywhere

@@ -255,15 +257,8 @@ export default function FeaturesPage() { {/* Integrations Section (replaces Skills) */}
-
-

Integrations

- - - - - Life telemetry - -
+ Integrations +

Life telemetry

Transparently collect the info you want documented on your journey

@@ -290,8 +285,8 @@ export default function FeaturesPage() { {/* Platform Features */}
-

Platform Features

-

Everything you need to capture, reflect, and grow

+ Platform +

Everything you need to capture, reflect, and grow

@@ -308,57 +303,59 @@ export default function FeaturesPage() {
- {/* What LogLife Adds */} + {/* Category Comparison Table */}
-
-
-

What LogLife Adds

-

On top of your existing chat and voice

-
- -
- {[ - { title: "No App Needed", description: "Primary interface is chat-native. E2E encryption built-in. No app to download.", icon: "M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" }, - { title: "Privacy by Design", description: "Open-source + self-host/local-first by default. No access by design.", icon: "M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" }, - { title: "Deep Analytics", description: "D/W/M/Q/Y timeline + dashboard + memory graph as the product.", icon: "M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" }, - { title: "Non-Prescriptive", description: "A capture system, not a coach. Advice only when you ask.", icon: "M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z" }, - { title: "Auto-Cancel on Inactivity", description: "Not using it? We\u2019ll cancel your subscription. We only want you paying if you\u2019re getting value.", icon: "M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9" }, - ].map((item, index) => ( -
-
- - - -
-

{item.title}

-

{item.description}

-
- ))} -
- - +
+ Where LogLife Fits +

+ One tool to replace four. +

+
+ +
+ + + + + + + + + + + {[ + { name: "Habit trackers", context: "x", action: "partial", habit: "check", highlight: false }, + { name: "Health wearables", context: "x", action: "partial", habit: "partial", highlight: false }, + { name: "Digital journaling", context: "check", action: "partial", habit: "partial", highlight: false }, + { name: "AI coaching", context: "check", action: "check", habit: "x", highlight: false }, + { name: "LogLife", context: "check", action: "check", habit: "check", highlight: true }, + ].map((row, index) => ( + + + + + + + ))} + +
CategoryLife contextAction-orientedHabit record
{row.name}
{/* Competitor Comparison Table */}
-

- How LogLife compares + How We Compare +

+ LogLife vs. the leading AI journaling apps

-

- See how LogLife stacks up against the leading AI journaling apps. -

@@ -411,6 +408,23 @@ export default function FeaturesPage() {
+ {/* CTA */} +
+
+

Ready to try it?

+

Early access is free. No card needed.

+ +
+
+
); diff --git a/website/app/globals.css b/website/app/globals.css index d28112b7..24370d3f 100644 --- a/website/app/globals.css +++ b/website/app/globals.css @@ -44,14 +44,16 @@ body { font-family: Arial, Helvetica, sans-serif; } -/* Animation keyframes for hero section */ +/* Dot-grid background overlay */ +.dot-grid { + background-image: radial-gradient(circle, rgba(148, 163, 184, 0.12) 1px, transparent 1px); + background-size: 24px 24px; +} + +/* Animation keyframes */ @keyframes fade-in { - from { - opacity: 0; - } - to { - opacity: 1; - } + from { opacity: 0; } + to { opacity: 1; } } @keyframes slide-up { @@ -89,3 +91,15 @@ body { animation: fade-in-on-scroll 0.6s ease-out; animation-fill-mode: both; } + +/* Scroll-triggered reveal */ +.reveal { + opacity: 0; + transform: translateY(24px); + transition: opacity 0.7s ease-out, transform 0.7s ease-out; +} + +.reveal.visible { + opacity: 1; + transform: translateY(0); +} diff --git a/website/app/hero/hero.tsx b/website/app/hero/hero.tsx index a978d867..eb77d93e 100644 --- a/website/app/hero/hero.tsx +++ b/website/app/hero/hero.tsx @@ -1,93 +1,125 @@ "use client"; -import React from "react"; +import React, { useState, useEffect, useRef } from "react"; +import { useWhatsAppWidget } from "../contexts/WhatsAppWidgetContext"; + +function useInView(threshold = 0.15) { + const ref = useRef(null); + const [visible, setVisible] = useState(false); + useEffect(() => { + const el = ref.current; + if (!el) return; + const obs = new IntersectionObserver( + ([entry]) => { if (entry.isIntersecting) { setVisible(true); obs.unobserve(el); } }, + { threshold } + ); + obs.observe(el); + return () => obs.disconnect(); + }, [threshold]); + return { ref, visible }; +} + +function SectionLabel({ children }: { children: React.ReactNode }) { + return ( + + {children} + + ); +} -// Modern Hero Section with Dark Theme function Hero() { + const { openWidget } = useWhatsAppWidget(); + return ( -
- +
- {/* Left Column - Text Content */}
-

- Your Life,{" "} - Captured. +

+ Effortless tracking,{" "} + + in chat. +

- +

- Nobody can tell you what they did last Tuesday. It's not a memory issue—it's a capture issue. LogLife turns your daily conversations into a timeline of your life. + Turns your chat into a life log with habit tracking and wearable + integration—AI listens, remembers, and surfaces patterns, + without coaching you.

- - - - {/* Platform badges */} -
+ +

+ Free. No card needed. +

+ +
+ {[ + "Built for people who want to remember their life.", + "Built for people who don\u2019t have time for journaling apps.", + "Built for people who already talk to their phone.", + ].map((line) => ( +

{line}

+ ))}
- - {/* Right Column - Illustration */} + + {/* Chat mock */}
-
- {/* AI Assistant visualization */} -
- - {/* Floating cards */} -
-
- - - - Voice logging -
-
- -
-

"Ran 5K this morning"

-
- -
-
Weekly Highlights
-
3 of 5
+
+
+
+ LogLife + 8:15 PM
- -
-
-
- - - + +
+ {/* User message */} +
+
+
+ + + + Voice note +
+

+ I have a lot of work and I feel like eating some chips. +

- Patterns found
-
- - {/* Central AI icon */} -
-
- - - + + {/* LogLife response */} +
+
+

+ Let's slow down. You slept less last night, ate 4 hours + ago, and you're tired. The pattern: when + you're off-schedule, the impulse is snacks + work that can + wait. If you get the chips and finish the slides—how + will you feel tomorrow morning? +

+
@@ -98,68 +130,50 @@ function Hero() { ); } -// Features Section with Cards -function Features() { - const features = [ +function TheProblem() { + const { ref, visible } = useInView(); + + const cards = [ { - icon: ( - - - - ), - title: "No app to download", - description: "LogLife lives where you already chat. WhatsApp, Telegram, or plain SMS. No new app, no new habit to build." + title: "Habits without context.", + description: "Habit trackers give you streaks and checkboxes, but no idea why you fell off. Rigid, binary, guilt-inducing.", + icon: "M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2", }, { - icon: ( - - - - ), - title: "Just talk", - description: "Send a voice note about your day. Or type a few words. AI handles the rest — transcription, tagging, organizing. Zero friction." + title: "Biometrics without meaning.", + description: "Your wearable says you slept 5 hours. But was it the late meeting, the coffee, or the argument? Numbers without context are just noise.", + icon: "M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z", }, { - icon: ( - - - - ), - title: "Your highlights, every week", - description: "Every day, week, month, quarter, and year, LogLife surfaces your 3–5 most important moments. A timeline of what actually mattered." + title: "Journals that gather dust.", + description: "You start strong, then life gets busy. Journaling apps feel like homework. By March, you\u2019ve stopped.", + icon: "M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253", }, - { - icon: ( - - - - ), - title: "Private by default", - description: "Open source. Self-hostable. We designed it so we can't see your data. Export everything, delete anytime, no questions asked." - } ]; return ( -
+
-

- A simpler way to know yourself + The Problem +

+ You're already tracking. It's just scattered everywhere.

-

Four ideas that make LogLife different

- -
- {features.map((feature, index) => ( -
+ {cards.map((card, i) => ( +
-
- {feature.icon} +
+ + +
-

{feature.title}

-

{feature.description}

+

{card.title}

+

{card.description}

))}
@@ -168,79 +182,180 @@ function Features() { ); } -// How It Works Section with Visual Steps +function Quote() { + const { ref, visible } = useInView(); + + return ( +
+
+

+ “You do not rise to the level of your goals. You fall to the level of your systems.” +

+

— James Clear

+

LogLife is the system.

+
+
+ ); +} + +function TheDifference() { + const { ref, visible } = useInView(); + const [activeTab, setActiveTab] = useState(0); + + const tabs = [ + { + label: "Habit Trackers", + left: { + title: "Habit Trackers", + text: "Good at streaks and records, but rigid, binary, guilt-inducing. No idea why you fell off or what conditions led to consistency.", + }, + right: { + title: "LogLife", + text: "Log habits naturally in chat. AI spots what leads to consistency vs drop-off\u2014no streak pressure. Easy and guilt-free.", + }, + }, + { + label: "Health Wearables", + left: { + title: "Health Wearables", + text: "Great at collecting biometrics\u2014sleep, activity, stress\u2014but missing what was actually happening in your life. Numbers without context.", + }, + right: { + title: "LogLife", + text: "Integrates wearable data and adds life context (meeting vs workout vs cooking) so those numbers become interpretable and useful.", + }, + }, + { + label: "Digital Journaling", + left: { + title: "Digital Journaling", + text: "Captures thoughts, but tends to be present-focused and disconnected from habits and health signals. Not very actionable.", + }, + right: { + title: "LogLife", + text: "Combines journaling with habit and wearable data. Summaries across past\u2013present\u2013future. Open-source, user-owned data.", + }, + }, + { + label: "AI Coaching", + left: { + title: "AI Coaching", + text: "Tries to guide your decisions and tell you what to do. Prescriptive by design.", + }, + right: { + title: "LogLife", + text: "A listener, not a coach. Low-friction capture, reliable memory, pattern recognition\u2014no unsolicited advice.", + }, + }, + ]; + + return ( +
+
+
+ The Difference +

+ One tool to replace four. +

+
+ + {/* Pill tabs */} +
+ {tabs.map((tab, i) => ( + + ))} +
+ + {/* Comparison card */} +
+
+
+ + + +

{tabs[activeTab].left.title}

+
+

{tabs[activeTab].left.text}

+
+ +
+
+ + + +

{tabs[activeTab].right.title}

+
+

{tabs[activeTab].right.text}

+
+
+
+
+ ); +} + function HowItWorks() { + const { ref, visible } = useInView(); + const steps = [ { number: "1", title: "Talk to LogLife", - description: "Send a voice note or text about your day through your favorite chat app. A short reflection at the end of the day is all you need.", - icon: ( - - - - ) + description: "Voice note or text. WhatsApp, Telegram, SMS, webchat. A short reflection is all you need.", + icon: "M19 11a7 7 0 01-7 7m0 0a7 7 0 01-7-7m7 7v4m0 0H8m4 0h4m-4-8a3 3 0 01-3-3V5a3 3 0 116 0v6a3 3 0 01-3 3z", }, { number: "2", - title: "AI captures everything", - description: "LogLife transcribes, tags, and organizes. No buttons, no mood ratings, no structured input. Just your words.", - icon: ( - - - - ) + title: "AI captures and organizes", + description: "Transcription, tagging, highlighting. Your 3\u20135 highlights emerge daily, weekly, monthly, yearly.", + icon: "M13 10V3L4 14h7v7l9-11h-7z", }, { number: "3", - title: "Your highlights emerge", - description: "AI surfaces the 3\u20135 most important things from your day, week, month, quarter, and year.", - icon: ( - - - - ) - }, - { - number: "4", title: "Explore your dashboard", - description: "Visualize patterns, track habits, and see your progress over time. Chat is the input; the dashboard is the output.", - icon: ( - - - - ) + description: "Patterns, habits, progress. Chat is the input; the dashboard is the output.", + icon: "M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z", }, { - number: "5", + number: "4", title: "Ask anything", - description: "Query your life data in plain language: \u2018How many on-time bedtimes last week?\u2019 \u2018What were my top 3 achievements this month?\u2019", - icon: ( - - - - ) - } + description: "\u2018How did I sleep this week?\u2019 Your life, queryable in plain language.", + icon: "M8 10h.01M12 10h.01M16 10h.01M9 16H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-5l-5 5v-5z", + }, ]; return ( -
+
-

How it works

-

Capture your life in five simple steps

+ How It Works +

+ Capture your life in four steps. +

- -
- {steps.map((step, index) => ( -
+ {steps.map((step, i) => ( +
- Step {step.number} -
{step.icon}
+ + {step.number} + + + +

{step.title}

{step.description}

@@ -252,106 +367,89 @@ function HowItWorks() { ); } -// Before/After Comparison Section -function BeforeAfter() { +function WhatYouGet() { + const { ref, visible } = useInView(); + + const cards = [ + { + title: "Chat-native", + description: "Lives where you already chat. No new app, no new habit to build.", + icon: "M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z", + }, + { + title: "Life context", + description: "The missing piece. Habits and biometrics mean something when connected to what was actually happening.", + icon: "M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1", + }, + { + title: "A listener, not a coach", + description: "Captures with low friction, remembers reliably, surfaces patterns\u2014without pushing advice.", + icon: "M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z", + }, + { + title: "Wearable integration", + description: "Oura, Garmin, and more. Your body data + your words = the full picture.", + icon: "M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z", + }, + { + title: "Open source", + description: "Your data, your rules. Export everything, delete anytime, self-host if you want.", + icon: "M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4", + }, + ]; + return ( -
+
-
-

- The old way vs. LogLife +
+ What You Get +

+ We don't coach you. We make you more self-aware.

-

Scattered notes and forgotten goals — or one place that actually gets you

+

+ This is not a therapy app. This is not a productivity coach. This is a listener that remembers everything. +

- -
- {/* Before */} -
-

- Without LogLife -

-
    - {[ - "Can't remember what you did last Tuesday", - "Resolutions that don't survive January", - "Thoughts in 10 different apps, none connected", - "Voice memos you never listen to again", - "No idea if you're actually improving", - "Journaling that feels like homework" - ].map((item, i) => ( -
  • - - - - {item} -
  • - ))} -
-
- - {/* After */} -
-

- With LogLife -

-
    - {[ - "Your whole week, reconstructed from one conversation", - "Weekly highlights that keep your goals alive", - "Everything in one place, linked by AI", - "Voice notes transcribed, tagged, searchable", - "Charts and patterns that show real progress", - "Just talk. That's the whole journal." - ].map((item, i) => ( -
  • - - - - {item} -
  • - ))} -
-
+ +
+ {cards.map((card, i) => ( +
+
+ + + +
+

{card.title}

+

{card.description}

+
+ ))}

); } -// Quotes Section -function Quotes() { - const quotes = [ - { - text: "You do not rise to the level of your goals. You fall to the level of your systems.", - author: "James Clear", - tagline: "LogLife is the system." - }, - { - text: "The unexamined life is not worth living.", - author: "Socrates", - tagline: "We think he'd have liked voice notes." - }, - { - text: "Every action you take is a vote for the type of person you wish to become.", - author: "James Clear", - tagline: "LogLife helps you count the votes." - } +function StatsBar() { + const { ref, visible } = useInView(); + + const stats = [ + { value: "10+", label: "Chat channels supported" }, + { value: "100%", label: "Open source" }, + { value: "0", label: "Unsolicited advice" }, + { value: "Early access", label: "Free, no card needed" }, ]; return ( -
+
-
- {quotes.map((quote, index) => ( -
-

- “{quote.text}” -

-

— {quote.author}

-

{quote.tagline}

+
+ {stats.map((stat, i) => ( +
+
{stat.value}
+
{stat.label}
))}
@@ -360,16 +458,84 @@ function Quotes() { ); } +function FinalCTA() { + const { ref, visible } = useInView(); + const { openWidget } = useWhatsAppWidget(); + + return ( +
+
+
+

+ You've never known yourself this well. +

+

+ Your words. Your data. Your patterns. AI that listens, remembers, and surfaces what matters. +

+ + {/* Provider pills */} +
+ + +
+ + +

Free. No card needed.

+ + +
+
+
+ ); +} -// Main export component export default function LogLifeHero() { return (
- + + + - - + + +
); } diff --git a/website/app/layout.tsx b/website/app/layout.tsx index fa4896fe..795a76c5 100644 --- a/website/app/layout.tsx +++ b/website/app/layout.tsx @@ -9,9 +9,9 @@ import WhatsAppWidget from "./components/WhatsAppWidget"; import { WebVitals } from "./components/WebVitals"; export const metadata: Metadata = { - title: "LogLife — Your Life, Captured", + title: "LogLife — Effortless Tracking, in Chat", description: - "Chat-native AI journaling powered by voice. Track your progress, find patterns, and build better habits through the chat apps you already use. No app to download. Open source. Your data stays yours.", + "Turns your chat into a life log with habit tracking and wearable integration. AI listens, remembers, and surfaces patterns — without coaching you. Open source. No app to download.", }; export default function RootLayout({ diff --git a/website/app/pricing/page.tsx b/website/app/pricing/page.tsx index 59765560..86183b97 100644 --- a/website/app/pricing/page.tsx +++ b/website/app/pricing/page.tsx @@ -1,5 +1,6 @@ "use client"; import React from "react"; +import { useWhatsAppWidget } from "../contexts/WhatsAppWidgetContext"; function CheckIcon({ className = "w-5 h-5" }: { className?: string }) { return ( @@ -10,28 +11,113 @@ function CheckIcon({ className = "w-5 h-5" }: { className?: string }) { } export default function PricingPage() { + const { openWidget } = useWhatsAppWidget(); + return (
- + {/* Header */}
-

- Pricing that adapts to you +

+ Start for free. Scale when you're ready.

- Start with our open-source self-hosted version, - or let LogLife handle the infrastructure for you. + Early access is free with no card needed. When we launch paid plans, self-hosting stays free forever.

- {/* 2-Column Pricing Grid */} -
+ {/* Time Comparison */} +
+
+ The Difference +

+ Same product. Two paths. +

+
+ +
+ {/* Self-Hosted */} +
+
+
+ + + +
+
+

Self-Hosted

+

Full control, you do the work

+
+
+ +
+ ~2–4 hours +

to get up and running

+
- {/* Column 1: Self-Hosted (Free) */} +
+ {[ + "Set up a VPS or local server", + "Configure 5 API keys (OpenAI, Anthropic, Whisper, etc.)", + "Deploy via Docker", + "Configure cloud storage", + "Handle all updates yourself", + ].map((item) => ( +
+ + + + {item} +
+ ))} +
+
+ + {/* Hosted */} +
+
+
+ + + +
+
+

Hosted

+

You just chat. We handle the rest.

+
+
+ +
+ ~5 minutes +

to start chatting

+
+ +
+ {[ + "All 5 API keys configured for you", + "Private cloud instance provisioned", + "Cloud storage and backups included", + "Automatic updates and monitoring", + "Email support", + ].map((item) => ( +
+ + {item} +
+ ))} +
+
+
+
+ + {/* Pricing Cards */} +
+ + {/* Self-Hosted (Free) */}
- +
@@ -62,8 +148,8 @@ export default function PricingPage() { ))}
-
- {/* Column 2: Hosted ($19/mo) -- MOST POPULAR */} + {/* Hosted */} - - {/* Billing Principles Section */} + + {/* What We Handle */}
-

- How we bill + What's Included +

+ We take care of the hard parts.

-

- Transparent, fair, and designed to keep you in control. -

- +
{[ { - icon: ( + title: "API Keys", + description: "5 provider keys (LLM, speech, embeddings, etc.) configured and managed. No BYO hassle unless you want it.", + icon: "M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z", + }, + { + title: "Infrastructure", + description: "Private cloud instance, storage, monitoring, alerts. Always running, always updated.", + icon: "M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2m-2-4h.01M17 16h.01", + }, + { + title: "Updates", + description: "New features, security patches, model upgrades\u2014deployed automatically. You never touch a terminal.", + icon: "M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12", + }, + { + title: "Support", + description: "Email support, onboarding help, and community access. We want you to succeed.", + icon: "M18.364 5.636l-3.536 3.536m0 5.656l3.536 3.536M9.172 9.172L5.636 5.636m3.536 9.192l-3.536 3.536M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-5 0a4 4 0 11-8 0 4 4 0 018 0z", + }, + ].map((item, index) => ( +
+
- + - ), +
+

{item.title}

+

{item.description}

+
+ ))} +
+
+ + {/* Billing Principles */} +
+
+ How We Bill +

+ Transparent, fair, and designed to keep you in control. +

+
+ +
+ {[ + { + icon: "M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z", title: "No API Markup", - description: "We don\u2019t resell API keys or add margin on model usage. You pay providers directly at their rates." + description: "We don\u2019t resell API keys or add margin on model usage. You pay providers directly at their rates.", }, { - icon: ( - - - - ), + icon: "M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2", title: "Itemized Usage", - description: "Monthly receipts show usage by provider\u2014tokens, minutes, calls\u2014plus our subscription fee. No surprises." + description: "Monthly receipts show usage by provider\u2014tokens, minutes, calls\u2014plus our subscription fee. No surprises.", }, { - icon: ( - - - - ), + icon: "M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z", title: "Keys & Data Control", - description: "BYO keys stay in your environment. For Hosted, keys can be yours or ours\u2014rotate anytime." + description: "BYO keys stay in your environment. For Hosted, keys can be yours or ours\u2014rotate anytime.", }, { - icon: ( - - - - ), + icon: "M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21", title: "No Content Logging", - description: "We don\u2019t store message content in logs by default. Opt in explicitly only if you need debugging." + description: "We don\u2019t store message content in logs by default. Opt in explicitly only if you need debugging.", }, { - icon: ( - - - - ), + icon: "M6 18L18 6M6 6l12 12", title: "Clear Cancellation", - description: "Cancel any time. No lock-in, no hidden fees. All data automatically deleted 30 days after cancellation." + description: "Cancel any time. No lock-in, no hidden fees. All data automatically deleted 30 days after cancellation.", }, { - icon: ( - - - - ), + icon: "M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9", title: "Inactivity Nudges", - description: "No usage for a while? We\u2019ll warn you, then auto-cancel the subscription if the service isn\u2019t being used. We only want you paying if you\u2019re getting value." + description: "No usage for a while? We\u2019ll warn you, then auto-cancel the subscription if the service isn\u2019t being used.", }, { - icon: ( - - - - ), + icon: "M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12", title: "Always Your Data", - description: "Host anywhere. Export or delete with one click. We can\u2019t see or change your message content." + description: "Host anywhere. Export or delete with one click. We can\u2019t see or change your message content.", }, { - icon: ( - - - - ), + icon: "M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4", title: "Subscription Covers", - description: "Web hosting, development, monitoring, alerts, audit logs, and support. All infrastructure costs included." + description: "Web hosting, development, monitoring, alerts, audit logs, and support. All infrastructure costs included.", }, ].map((item, index) => ( -
- {item.icon} + + +

{item.title}

{item.description}

@@ -233,16 +334,17 @@ export default function PricingPage() {
{/* Privacy Section */} -
+
-

- Your privacy, by design + Privacy +

+ Your data, your rules.

- Different deployment options, same commitment to your privacy. + Different deployment options, same commitment to transparency.

- +
{/* Private Deploy */}
@@ -293,7 +395,7 @@ export default function PricingPage() {

- We don't sell data. We design it so we don't have access to it by design. However, in theory, since we operate the server, we could access it. But we don't. We only care about usage metadata. + We don't sell data and we don't train models on your data. We design for minimal access—but since we operate the server, theoretical access exists. For zero-access guarantees, self-host.

@@ -306,33 +408,33 @@ export default function PricingPage() { - Goal: “no access by design” & no model training on user data + No access by design & no model training on user data
{/* CTA Footer */} -
+
-

Ready to start capturing your life?

-

We handle the infrastructure. You focus on what matters.

- Ready to try it? +

Early access is free. No card needed. Cancel anytime.

+
{/* Trust Indicators */}

- Powered by LogLife · Open Source · Your Data, Your Control + Powered by LogLife · Open Source · Your Data, Your Rules

diff --git a/website/next-env.d.ts b/website/next-env.d.ts index c4b7818f..9edff1c7 100644 --- a/website/next-env.d.ts +++ b/website/next-env.d.ts @@ -1,6 +1,6 @@ /// /// -import "./.next/dev/types/routes.d.ts"; +import "./.next/types/routes.d.ts"; // NOTE: This file should not be edited // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. From 973f4e044f828403606833eeb41d40e7ca8d51dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Morais?= Date: Thu, 19 Feb 2026 16:01:09 -0800 Subject: [PATCH 02/10] fix pricing page --- website/app/pricing/page.tsx | 368 +++++++++++++++++++---------------- 1 file changed, 203 insertions(+), 165 deletions(-) diff --git a/website/app/pricing/page.tsx b/website/app/pricing/page.tsx index 86183b97..09c77c07 100644 --- a/website/app/pricing/page.tsx +++ b/website/app/pricing/page.tsx @@ -1,5 +1,5 @@ "use client"; -import React from "react"; +import React, { useState, useEffect, useRef, useCallback } from "react"; import { useWhatsAppWidget } from "../contexts/WhatsAppWidgetContext"; function CheckIcon({ className = "w-5 h-5" }: { className?: string }) { @@ -10,109 +10,212 @@ function CheckIcon({ className = "w-5 h-5" }: { className?: string }) { ); } -export default function PricingPage() { - const { openWidget } = useWhatsAppWidget(); +const SETUP_STEPS = [ + "Configure API keys", + "Provision server", + "Set up storage", + "Deploy application", + "Verify & monitor", +]; + +function AnimatedComparison() { + const ref = useRef(null); + const [started, setStarted] = useState(false); + const [selfHostedCompleted, setSelfHostedCompleted] = useState(new Array(SETUP_STEPS.length).fill(false)); + const [hostedCompleted, setHostedCompleted] = useState(new Array(SETUP_STEPS.length).fill(false)); + const [selfHostedTime, setSelfHostedTime] = useState(0); + const [hostedTime, setHostedTime] = useState(0); + const [selfHostedDone, setSelfHostedDone] = useState(false); + const [hostedDone, setHostedDone] = useState(false); + + useEffect(() => { + const el = ref.current; + if (!el) return; + const obs = new IntersectionObserver( + ([entry]) => { if (entry.isIntersecting) { setStarted(true); obs.unobserve(el); } }, + { threshold: 0.3 } + ); + obs.observe(el); + return () => obs.disconnect(); + }, []); + + const selfHostedTarget = 9000; + const hostedTarget = 300; + + const formatTime = useCallback((ms: number, isSelfHosted: boolean) => { + if (isSelfHosted) { + const totalMinutes = Math.floor((ms / selfHostedTarget) * 150); + const hours = Math.floor(totalMinutes / 60); + const minutes = totalMinutes % 60; + return `${hours}h ${String(minutes).padStart(2, "0")}m`; + } + const totalSeconds = Math.floor((ms / hostedTarget) * 300); + const minutes = Math.floor(totalSeconds / 60); + const seconds = totalSeconds % 60; + return `${minutes}m ${String(seconds).padStart(2, "0")}s`; + }, []); + + useEffect(() => { + if (!started) return; + + const stepDelay = selfHostedTarget / SETUP_STEPS.length; + const timers: ReturnType[] = []; + + SETUP_STEPS.forEach((_, i) => { + timers.push(setTimeout(() => { + setSelfHostedCompleted(prev => { const next = [...prev]; next[i] = true; return next; }); + }, stepDelay * (i + 1))); + }); + + timers.push(setTimeout(() => setSelfHostedDone(true), selfHostedTarget)); + + return () => timers.forEach(clearTimeout); + }, [started, selfHostedTarget]); + + useEffect(() => { + if (!started) return; + + const timers: ReturnType[] = []; + SETUP_STEPS.forEach((_, i) => { + timers.push(setTimeout(() => { + setHostedCompleted(prev => { const next = [...prev]; next[i] = true; return next; }); + }, 80 * (i + 1))); + }); + + timers.push(setTimeout(() => setHostedDone(true), hostedTarget)); + + return () => timers.forEach(clearTimeout); + }, [started, hostedTarget]); + + useEffect(() => { + if (!started || selfHostedDone) return; + const interval = setInterval(() => { + setSelfHostedTime(prev => { + if (prev >= selfHostedTarget) { clearInterval(interval); return selfHostedTarget; } + return prev + 50; + }); + }, 50); + return () => clearInterval(interval); + }, [started, selfHostedDone, selfHostedTarget]); + + useEffect(() => { + if (!started || hostedDone) return; + const interval = setInterval(() => { + setHostedTime(prev => { + if (prev >= hostedTarget) { clearInterval(interval); return hostedTarget; } + return prev + 50; + }); + }, 50); + return () => clearInterval(interval); + }, [started, hostedDone, hostedTarget]); return ( -
-
+
+
+ The Difference +

+ Same product. Two paths. +

+
- {/* Header */} -
-

- Start for free. Scale when you're ready. -

-

- Early access is free with no card needed. When we launch paid plans, self-hosting stays free forever. -

-
+
+ {/* Self-Hosted panel */} +
+
+
+ + + +
+
+

Self-Hosted

+
+
- {/* Time Comparison */} -
-
- The Difference -

- Same product. Two paths. -

+
+ + {started ? formatTime(selfHostedTime, true) : "0h 00m"} +
-
- {/* Self-Hosted */} -
-
-
- - - -
-
-

Self-Hosted

-

Full control, you do the work

+
+ {SETUP_STEPS.map((step, i) => ( +
+
+ {selfHostedCompleted[i] && ( + + + + )}
+ {step}
+ ))} +
+
-
- ~2–4 hours -

to get up and running

-
- -
- {[ - "Set up a VPS or local server", - "Configure 5 API keys (OpenAI, Anthropic, Whisper, etc.)", - "Deploy via Docker", - "Configure cloud storage", - "Handle all updates yourself", - ].map((item) => ( -
- - - - {item} -
- ))} -
+ {/* Hosted panel */} +
+
+
+ + + +
+
+

Hosted

+
- {/* Hosted */} -
-
-
- - - -
-
-

Hosted

-

You just chat. We handle the rest.

+
+ + {started ? formatTime(hostedTime, false) : "0m 00s"} + +
+ +
+ {SETUP_STEPS.map((step, i) => ( +
+
+ {hostedCompleted[i] && ( + + + + )}
+ {step}
+ ))} +
+
+
-
- ~5 minutes -

to start chatting

-
+

+ Same product. Same features. Different setup time. +

+
+ ); +} -
- {[ - "All 5 API keys configured for you", - "Private cloud instance provisioned", - "Cloud storage and backups included", - "Automatic updates and monitoring", - "Email support", - ].map((item) => ( -
- - {item} -
- ))} -
-
-
+export default function PricingPage() { + const { openWidget } = useWhatsAppWidget(); + + return ( +
+
+ + {/* Header */} +
+

+ Start for free. Scale when you're ready. +

+

+ Early access is free with no card needed. When we launch paid plans, self-hosting stays free forever. +

{/* Pricing Cards */} -
+
{/* Self-Hosted (Free) */}
@@ -178,7 +281,7 @@ export default function PricingPage() {

For most users

Free - $19/mo + $29/mo
@@ -189,7 +292,7 @@ export default function PricingPage() { "No content logging", "API usage included (up to limit)", "Dashboard access", - "Life telemetry integrations", + "Health telemetry integrations", "Smart reminders", "AI highlights (D/W/M/Q/Y)", "Access controls & audit logs", @@ -217,56 +320,11 @@ export default function PricingPage() {
- {/* What We Handle */} -
-
- What's Included -

- We take care of the hard parts. -

-
- -
- {[ - { - title: "API Keys", - description: "5 provider keys (LLM, speech, embeddings, etc.) configured and managed. No BYO hassle unless you want it.", - icon: "M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z", - }, - { - title: "Infrastructure", - description: "Private cloud instance, storage, monitoring, alerts. Always running, always updated.", - icon: "M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2m-2-4h.01M17 16h.01", - }, - { - title: "Updates", - description: "New features, security patches, model upgrades\u2014deployed automatically. You never touch a terminal.", - icon: "M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12", - }, - { - title: "Support", - description: "Email support, onboarding help, and community access. We want you to succeed.", - icon: "M18.364 5.636l-3.536 3.536m0 5.656l3.536 3.536M9.172 9.172L5.636 5.636m3.536 9.192l-3.536 3.536M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-5 0a4 4 0 11-8 0 4 4 0 018 0z", - }, - ].map((item, index) => ( -
-
- - - -
-

{item.title}

-

{item.description}

-
- ))} -
-
+ {/* Animated Time Comparison */} + - {/* Billing Principles */} -
+ {/* How We Bill */} +
How We Bill

@@ -277,25 +335,15 @@ export default function PricingPage() {
{[ { - icon: "M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z", - title: "No API Markup", - description: "We don\u2019t resell API keys or add margin on model usage. You pay providers directly at their rates.", + icon: "M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4", + title: "Subscription Covers", + description: "Web hosting, development, monitoring, alerts, audit logs, and support. All infrastructure costs included.", }, { icon: "M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2", title: "Itemized Usage", description: "Monthly receipts show usage by provider\u2014tokens, minutes, calls\u2014plus our subscription fee. No surprises.", }, - { - icon: "M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z", - title: "Keys & Data Control", - description: "BYO keys stay in your environment. For Hosted, keys can be yours or ours\u2014rotate anytime.", - }, - { - icon: "M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21", - title: "No Content Logging", - description: "We don\u2019t store message content in logs by default. Opt in explicitly only if you need debugging.", - }, { icon: "M6 18L18 6M6 6l12 12", title: "Clear Cancellation", @@ -306,16 +354,6 @@ export default function PricingPage() { title: "Inactivity Nudges", description: "No usage for a while? We\u2019ll warn you, then auto-cancel the subscription if the service isn\u2019t being used.", }, - { - icon: "M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12", - title: "Always Your Data", - description: "Host anywhere. Export or delete with one click. We can\u2019t see or change your message content.", - }, - { - icon: "M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4", - title: "Subscription Covers", - description: "Web hosting, development, monitoring, alerts, audit logs, and support. All infrastructure costs included.", - }, ].map((item, index) => (
{/* Privacy Section */} -
+
Privacy

@@ -356,7 +394,7 @@ export default function PricingPage() {

Private Deploy

-

Self-hosted or your infrastructure

+

Self-hosted on your infrastructure

@@ -400,7 +438,7 @@ export default function PricingPage() {

- Users can export/delete/reset anytime. All data is automatically deleted 30 days after a subscription is canceled. It's possible to delete all data without deleting the account (called a brain reset). + Users have full control over their data. Export/delete/reset anytime. Automatic deletion 30 days after a subscription is canceled.

@@ -415,10 +453,10 @@ export default function PricingPage() {
{/* CTA Footer */} -
+

Ready to try it?

-

Early access is free. No card needed. Cancel anytime.

+

Early access is free. No card needed.

- Free. No card needed. + No card needed.

@@ -499,12 +499,12 @@ function FinalCTA() { onClick={openWidget} className="inline-flex items-center justify-center px-8 py-4 bg-emerald-600 hover:bg-emerald-500 text-white font-bold rounded-xl shadow-[0_0_24px_rgba(16,185,129,0.3)] transition-all transform hover:scale-105 cursor-pointer" > - Get Early Access + Free Early Access -

Free. No card needed.

+

No card needed.

(
@@ -289,15 +287,12 @@ export default function PricingPage() { {[ "Everything below, plus we handle the infrastructure:", "Hosted private instance", - "No content logging", "API usage included (up to limit)", "Dashboard access", "Health telemetry integrations", "Smart reminders", "AI highlights (D/W/M/Q/Y)", - "Access controls & audit logs", "Email support", - "Auto-cancel on inactivity", ].map((item, index) => (
@@ -310,12 +305,12 @@ export default function PricingPage() { onClick={openWidget} className="w-full inline-flex items-center justify-center px-4 py-3 bg-emerald-600 hover:bg-emerald-500 text-white text-sm font-bold rounded-xl shadow-[0_0_20px_rgba(16,185,129,0.25)] transition-all transform hover:scale-105 cursor-pointer" > - Get Early Access + Free Early Access -

Free. No card needed.

+

No card needed.

@@ -438,7 +433,7 @@ export default function PricingPage() {

- Users have full control over their data. Export/delete/reset anytime. Automatic deletion 30 days after a subscription is canceled. + Users can export/delete/reset data anytime. Automatic deletion 30 days after a subscription is canceled.

@@ -456,12 +451,12 @@ export default function PricingPage() {

Ready to try it?

-

Early access is free. No card needed.

+

No card needed.

); From f735d23c2ded92bdbc5f38e2a79eec31edff3fed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Morais?= Date: Thu, 19 Feb 2026 16:54:42 -0800 Subject: [PATCH 07/10] fix deploy --- .github/workflows/deploy.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 2b0b2016..15d0d4d3 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -44,6 +44,8 @@ jobs: cd "$DEPLOY_DIR" git fetch origin + git checkout -- . + git clean -fd git checkout "$BRANCH" git reset --hard "origin/$BRANCH" From b25ff895c25055605d2e284348ae3fd87e20cc29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Morais?= Date: Thu, 19 Feb 2026 17:01:59 -0800 Subject: [PATCH 08/10] - verbose --- website/app/hero/hero.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/app/hero/hero.tsx b/website/app/hero/hero.tsx index 631365ec..41bff4f7 100644 --- a/website/app/hero/hero.tsx +++ b/website/app/hero/hero.tsx @@ -211,7 +211,7 @@ function TheDifference() { }, right: { title: "LogLife", - text: "Log habits naturally in chat. AI spots what leads to consistency vs drop-off\u2014no streak pressure. Easy and guilt-free.", + text: "Log habits naturally in chat. AI spots what leads to consistency vs drop-off\u2014no streak pressure. Easy & guilt-free.", }, }, { From 5f229d173c0e4ff4804619cccf9b0fc3cef0c1b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Morais?= Date: Thu, 19 Feb 2026 17:14:32 -0800 Subject: [PATCH 09/10] hero --- website/app/hero/hero.tsx | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/website/app/hero/hero.tsx b/website/app/hero/hero.tsx index 41bff4f7..8ddcc1dc 100644 --- a/website/app/hero/hero.tsx +++ b/website/app/hero/hero.tsx @@ -113,11 +113,8 @@ function Hero() {

- Let's slow down. You slept less last night, ate 4 hours - ago, and you're tired. The pattern: when - you're off-schedule, the impulse is snacks + work that can - wait. If you get the chips and finish the slides—how - will you feel tomorrow morning? + I hear you. Let's slow it down. The facts: less sleep last night, you're hungry and tired. + If you get the chips—how will you feel after? Whatever you decide, log so we learn from it!

From 29866597824bc87a5383e51750ff4b6edc677c65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Morais?= Date: Thu, 19 Feb 2026 17:19:29 -0800 Subject: [PATCH 10/10] fix lints --- website/app/components/WhatsAppWidget.tsx | 14 +- website/app/hero/hero.tsx | 2 +- website/next-env.d.ts | 2 +- website/package.json | 1 + website/pnpm-lock.yaml | 308 +++++++++++++++++++++- 5 files changed, 312 insertions(+), 15 deletions(-) diff --git a/website/app/components/WhatsAppWidget.tsx b/website/app/components/WhatsAppWidget.tsx index 24acf54b..62ad5562 100644 --- a/website/app/components/WhatsAppWidget.tsx +++ b/website/app/components/WhatsAppWidget.tsx @@ -72,19 +72,21 @@ export default function WhatsAppWidget() { const link = `https://wa.me/${number}?text=${encodeURIComponent(message)}`; useEffect(() => { - if (isOpen) { - setIsAnimating(false); - } + if (!isOpen) return; + const id = requestAnimationFrame(() => setIsAnimating(false)); + return () => cancelAnimationFrame(id); }, [isOpen]); // Enter: after panel mounts, trigger transition from opacity-0 to opacity-100 useEffect(() => { if (!isChatPanelOpen || isPanelClosing) return; - setIsPanelEntered(false); const id = requestAnimationFrame(() => { requestAnimationFrame(() => setIsPanelEntered(true)); }); - return () => cancelAnimationFrame(id); + return () => { + cancelAnimationFrame(id); + setIsPanelEntered(false); + }; }, [isChatPanelOpen, isPanelClosing]); // Close: run exit animation then unmount @@ -101,7 +103,7 @@ export default function WhatsAppWidget() { return () => { if (closeTimeoutRef.current) clearTimeout(closeTimeoutRef.current); }; - }, [isPanelClosing]); + }, [isPanelClosing, closeWidget]); const handleToggle = () => { if (isOpen) { diff --git a/website/app/hero/hero.tsx b/website/app/hero/hero.tsx index 8ddcc1dc..bb063ec4 100644 --- a/website/app/hero/hero.tsx +++ b/website/app/hero/hero.tsx @@ -113,7 +113,7 @@ function Hero() {

- I hear you. Let's slow it down. The facts: less sleep last night, you're hungry and tired. + I hear you. Let's slow it down. The facts: less sleep last night, you're hungry and tired. If you get the chips—how will you feel after? Whatever you decide, log so we learn from it!

diff --git a/website/next-env.d.ts b/website/next-env.d.ts index 9edff1c7..c4b7818f 100644 --- a/website/next-env.d.ts +++ b/website/next-env.d.ts @@ -1,6 +1,6 @@ /// /// -import "./.next/types/routes.d.ts"; +import "./.next/dev/types/routes.d.ts"; // NOTE: This file should not be edited // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/website/package.json b/website/package.json index 8e66639b..d15adad6 100644 --- a/website/package.json +++ b/website/package.json @@ -27,6 +27,7 @@ "@types/react-dom": "^19", "eslint": "^9", "eslint-config-next": "15.5.4", + "eslint-plugin-react-hooks": "^7.0.1", "tailwindcss": "^4", "typescript": "^5" } diff --git a/website/pnpm-lock.yaml b/website/pnpm-lock.yaml index c5a30cb2..5fc9f167 100644 --- a/website/pnpm-lock.yaml +++ b/website/pnpm-lock.yaml @@ -10,7 +10,7 @@ importers: dependencies: '@clerk/nextjs': specifier: ^6.37.2 - version: 6.37.2(next@16.1.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 6.37.2(next@16.1.6(@babel/core@7.29.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@vapi-ai/client-sdk-react': specifier: ^0.1.1 version: 0.1.1(@types/react@19.2.11)(@vapi-ai/web@2.5.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -19,7 +19,7 @@ importers: version: 2.5.2 next: specifier: ^16.1.6 - version: 16.1.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 16.1.6(@babel/core@7.29.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: specifier: 19.1.0 version: 19.1.0 @@ -54,6 +54,9 @@ importers: eslint-config-next: specifier: 15.5.4 version: 15.5.4(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + eslint-plugin-react-hooks: + specifier: ^7.0.1 + version: 7.0.1(eslint@9.39.2(jiti@2.6.1)) tailwindcss: specifier: ^4 version: 4.1.18 @@ -67,10 +70,77 @@ packages: resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} engines: {node: '>=10'} + '@babel/code-frame@7.29.0': + resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.29.0': + resolution: {integrity: sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.29.0': + resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.29.1': + resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.28.6': + resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.28.6': + resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.28.6': + resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.28.6': + resolution: {integrity: sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.0': + resolution: {integrity: sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==} + engines: {node: '>=6.0.0'} + hasBin: true + '@babel/runtime@7.28.6': resolution: {integrity: sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==} engines: {node: '>=6.9.0'} + '@babel/template@7.28.6': + resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.29.0': + resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.29.0': + resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} + engines: {node: '>=6.9.0'} + '@clerk/backend@2.30.0': resolution: {integrity: sha512-4DJBisLmA6pAtvTLJb1EshWCVfQRpuIxSO+pIkhGOC2DvNc85EkMq2G3a/jPOzUFzZPMmIJp99sKo7JF9MoOcg==} engines: {node: '>=18.17.0'} @@ -875,6 +945,11 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} + browserslist@4.28.1: + resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} @@ -929,6 +1004,9 @@ packages: concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -1004,6 +1082,9 @@ packages: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} + electron-to-chromium@1.5.286: + resolution: {integrity: sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==} + emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} @@ -1043,6 +1124,10 @@ packages: resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} engines: {node: '>= 0.4'} + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} @@ -1115,6 +1200,12 @@ packages: peerDependencies: eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 + eslint-plugin-react-hooks@7.0.1: + resolution: {integrity: sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==} + engines: {node: '>=18'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 + eslint-plugin-react@7.37.5: resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==} engines: {node: '>=4'} @@ -1238,6 +1329,10 @@ packages: resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} engines: {node: '>= 0.4'} + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + get-intrinsic@1.3.0: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} @@ -1312,6 +1407,12 @@ packages: hast-util-whitespace@3.0.0: resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + hermes-estree@0.25.1: + resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==} + + hermes-parser@0.25.1: + resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==} + html-url-attributes@3.0.1: resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==} @@ -1482,6 +1583,11 @@ packages: resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} hasBin: true + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} @@ -1495,6 +1601,11 @@ packages: resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} hasBin: true + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + jsx-ast-utils@3.3.5: resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} engines: {node: '>=4.0'} @@ -1601,6 +1712,9 @@ packages: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} @@ -1750,6 +1864,9 @@ packages: sass: optional: true + node-releases@2.0.27: + resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} + object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} @@ -2156,6 +2273,12 @@ packages: unrs-resolver@1.11.1: resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==} + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} @@ -2198,10 +2321,22 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} + zod-validation-error@4.0.2: + resolution: {integrity: sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + zod: ^3.25.0 || ^4.0.0 + + zod@4.3.6: + resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} + zwitch@2.0.4: resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} @@ -2209,8 +2344,108 @@ snapshots: '@alloc/quick-lru@5.2.0': {} + '@babel/code-frame@7.29.0': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.29.0': {} + + '@babel/core@7.29.0': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helpers': 7.28.6 + '@babel/parser': 7.29.0 + '@babel/template': 7.28.6 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.29.1': + dependencies: + '@babel/parser': 7.29.0 + '@babel/types': 7.29.0 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.28.6': + dependencies: + '@babel/compat-data': 7.29.0 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.28.1 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-module-imports@7.28.6': + dependencies: + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-module-imports': 7.28.6 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.28.5': {} + + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helpers@7.28.6': + dependencies: + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + + '@babel/parser@7.29.0': + dependencies: + '@babel/types': 7.29.0 + '@babel/runtime@7.28.6': {} + '@babel/template@7.28.6': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/parser': 7.29.0 + '@babel/types': 7.29.0 + + '@babel/traverse@7.29.0': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.29.0 + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.29.0': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + '@clerk/backend@2.30.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@clerk/shared': 3.44.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -2228,13 +2463,13 @@ snapshots: react-dom: 19.1.0(react@19.1.0) tslib: 2.8.1 - '@clerk/nextjs@6.37.2(next@16.1.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@clerk/nextjs@6.37.2(next@16.1.6(@babel/core@7.29.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@clerk/backend': 2.30.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@clerk/clerk-react': 5.60.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@clerk/shared': 3.44.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@clerk/types': 4.101.14(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - next: 16.1.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + next: 16.1.6(@babel/core@7.29.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) server-only: 0.0.1 @@ -2958,6 +3193,14 @@ snapshots: dependencies: fill-range: 7.1.1 + browserslist@4.28.1: + dependencies: + baseline-browser-mapping: 2.9.19 + caniuse-lite: 1.0.30001767 + electron-to-chromium: 1.5.286 + node-releases: 2.0.27 + update-browserslist-db: 1.2.3(browserslist@4.28.1) + call-bind-apply-helpers@1.0.2: dependencies: es-errors: 1.3.0 @@ -3006,6 +3249,8 @@ snapshots: concat-map@0.0.1: {} + convert-source-map@2.0.0: {} + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -3080,6 +3325,8 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 + electron-to-chromium@1.5.286: {} + emoji-regex@9.2.2: {} enhanced-resolve@5.19.0: @@ -3188,6 +3435,8 @@ snapshots: is-date-object: 1.1.0 is-symbol: 1.1.1 + escalade@3.2.0: {} + escape-string-regexp@4.0.0: {} eslint-config-next@15.5.4(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3): @@ -3296,6 +3545,17 @@ snapshots: dependencies: eslint: 9.39.2(jiti@2.6.1) + eslint-plugin-react-hooks@7.0.1(eslint@9.39.2(jiti@2.6.1)): + dependencies: + '@babel/core': 7.29.0 + '@babel/parser': 7.29.0 + eslint: 9.39.2(jiti@2.6.1) + hermes-parser: 0.25.1 + zod: 4.3.6 + zod-validation-error: 4.0.2(zod@4.3.6) + transitivePeerDependencies: + - supports-color + eslint-plugin-react@7.37.5(eslint@9.39.2(jiti@2.6.1)): dependencies: array-includes: 3.1.9 @@ -3455,6 +3715,8 @@ snapshots: generator-function@2.0.1: {} + gensync@1.0.0-beta.2: {} + get-intrinsic@1.3.0: dependencies: call-bind-apply-helpers: 1.0.2 @@ -3550,6 +3812,12 @@ snapshots: dependencies: '@types/hast': 3.0.4 + hermes-estree@0.25.1: {} + + hermes-parser@0.25.1: + dependencies: + hermes-estree: 0.25.1 + html-url-attributes@3.0.1: {} ignore@5.3.2: {} @@ -3719,6 +3987,8 @@ snapshots: dependencies: argparse: 2.0.1 + jsesc@3.1.0: {} + json-buffer@3.0.1: {} json-schema-traverse@0.4.1: {} @@ -3729,6 +3999,8 @@ snapshots: dependencies: minimist: 1.2.8 + json5@2.2.3: {} + jsx-ast-utils@3.3.5: dependencies: array-includes: 3.1.9 @@ -3812,6 +4084,10 @@ snapshots: dependencies: js-tokens: 4.0.0 + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + magic-string@0.30.21: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -4065,7 +4341,7 @@ snapshots: natural-compare@1.4.0: {} - next@16.1.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + next@16.1.6(@babel/core@7.29.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: '@next/env': 16.1.6 '@swc/helpers': 0.5.15 @@ -4074,7 +4350,7 @@ snapshots: postcss: 8.4.31 react: 19.1.0 react-dom: 19.1.0(react@19.1.0) - styled-jsx: 5.1.6(react@19.1.0) + styled-jsx: 5.1.6(@babel/core@7.29.0)(react@19.1.0) optionalDependencies: '@next/swc-darwin-arm64': 16.1.6 '@next/swc-darwin-x64': 16.1.6 @@ -4089,6 +4365,8 @@ snapshots: - '@babel/core' - babel-plugin-macros + node-releases@2.0.27: {} + object-assign@4.1.1: {} object-inspect@1.13.4: {} @@ -4499,10 +4777,12 @@ snapshots: dependencies: inline-style-parser: 0.2.7 - styled-jsx@5.1.6(react@19.1.0): + styled-jsx@5.1.6(@babel/core@7.29.0)(react@19.1.0): dependencies: client-only: 0.0.1 react: 19.1.0 + optionalDependencies: + '@babel/core': 7.29.0 supports-color@7.2.0: dependencies: @@ -4651,6 +4931,12 @@ snapshots: '@unrs/resolver-binding-win32-ia32-msvc': 1.11.1 '@unrs/resolver-binding-win32-x64-msvc': 1.11.1 + update-browserslist-db@1.2.3(browserslist@4.28.1): + dependencies: + browserslist: 4.28.1 + escalade: 3.2.0 + picocolors: 1.1.1 + uri-js@4.4.1: dependencies: punycode: 2.3.1 @@ -4718,6 +5004,14 @@ snapshots: word-wrap@1.2.5: {} + yallist@3.1.1: {} + yocto-queue@0.1.0: {} + zod-validation-error@4.0.2(zod@4.3.6): + dependencies: + zod: 4.3.6 + + zod@4.3.6: {} + zwitch@2.0.4: {}