feat(marketing): public homepage replacing / → /login redirect
- Add (marketing) route group: /, /pricing, /privacy, /terms - Add (app) route group: moves all authenticated pages, app home → /home - Root / is now a static marketing page (zero DB imports, zero auth) - NavAuthButton client component: shows "Open Tia →" if logged in, else "Continue with Google" - Plausible analytics hook in marketing layout - Auto-generated OG image via opengraph-image.tsx - Middleware updated to allowlist marketing routes - All /-redirects updated to /home (login, onboarding, invite, circle join) - BottomNav home tab updated: / → /home Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
f1d4374609
commit
2a09c027fa
38 changed files with 911 additions and 41 deletions
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import { useState, useEffect } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useFamily } from "../FamilyProvider";
|
||||
import { useFamily } from "@/app/FamilyProvider";
|
||||
import { getGuideline, getAgeInMonths } from "@/lib/guidelines";
|
||||
import { api } from "@/lib/api";
|
||||
import { CalendarView } from "@/components/CalendarView";
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
"use client";
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import { useFamily } from "../FamilyProvider";
|
||||
import { useFamily } from "@/app/FamilyProvider";
|
||||
import { Button, Input, ConfirmDialog } from "@/components/ui";
|
||||
import type { AIChat, ChatSession } from "@/types";
|
||||
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import { useState, useEffect, useCallback, use } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useFamily } from "../../FamilyProvider";
|
||||
import { useFamily } from "@/app/FamilyProvider";
|
||||
import type { CirclePost, CircleComment, Circle } from "@/types";
|
||||
|
||||
const REACTIONS = ["❤️", "😂", "👍", "🙏", "😮"];
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import { useState, useEffect, use } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useFamily } from "../../../FamilyProvider";
|
||||
import { useFamily } from "@/app/FamilyProvider";
|
||||
|
||||
type State = "loading" | "preview" | "joining" | "success" | "error";
|
||||
|
||||
|
|
@ -89,7 +89,7 @@ export default function JoinCirclePage({ params }: { params: Promise<{ token: st
|
|||
>
|
||||
{familyId ? `Join ${circleName}` : "Log in to Join"}
|
||||
</button>
|
||||
<button onClick={() => router.push("/")} className="mt-3 text-xs text-gray-400">
|
||||
<button onClick={() => router.push("/home")} className="mt-3 text-xs text-gray-400">
|
||||
Not now
|
||||
</button>
|
||||
</>
|
||||
|
|
@ -115,7 +115,7 @@ export default function JoinCirclePage({ params }: { params: Promise<{ token: st
|
|||
<div className="text-5xl mb-4">😕</div>
|
||||
<h1 className="text-lg font-bold mb-2 text-gray-800 dark:text-white">Invite issue</h1>
|
||||
<p className="text-gray-500 text-sm mb-5">{errorMsg}</p>
|
||||
<button onClick={() => router.push("/")} className="w-full py-3 bg-gray-100 dark:bg-gray-700 rounded-xl text-sm">Go to Home</button>
|
||||
<button onClick={() => router.push("/home")} className="w-full py-3 bg-gray-100 dark:bg-gray-700 rounded-xl text-sm">Go to Home</button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
import { useState, useEffect } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import Link from "next/link";
|
||||
import { useFamily } from "../FamilyProvider";
|
||||
import { useFamily } from "@/app/FamilyProvider";
|
||||
import type { Circle } from "@/types";
|
||||
|
||||
type PendingInvite = {
|
||||
|
|
@ -6,7 +6,7 @@ import {
|
|||
EmptyState, LoadingShimmer, ConfirmDialog, WashiTape,
|
||||
Badge, Avatar, Tabs, TabPanel,
|
||||
} from "@/components/ui";
|
||||
import { useTheme } from "../../ThemeProvider";
|
||||
import { useTheme } from "@/app/ThemeProvider";
|
||||
|
||||
export default function DevComponentsPage() {
|
||||
const { theme, toggle } = useTheme();
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import { useState, useEffect } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useFamily } from "../FamilyProvider";
|
||||
import { useFamily } from "@/app/FamilyProvider";
|
||||
|
||||
interface Child {
|
||||
id: string;
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import { useState, useEffect } from "react";
|
||||
import Link from "next/link";
|
||||
import { useFamily } from "../FamilyProvider";
|
||||
import { useFamily } from "@/app/FamilyProvider";
|
||||
import { Button, Card, Input, ConfirmDialog } from "@/components/ui";
|
||||
import { WHO_BOY_WEIGHT, WHO_GIRL_WEIGHT, getAgeInMonthsFromBirth, getPercentile } from "@/lib/growth-standards";
|
||||
import { formatAge } from "@/lib/formatting";
|
||||
|
|
@ -19,7 +19,7 @@ import {
|
|||
Filler,
|
||||
} from "chart.js";
|
||||
import { Line } from "react-chartjs-2";
|
||||
import { useTheme } from "../ThemeProvider";
|
||||
import { useTheme } from "@/app/ThemeProvider";
|
||||
|
||||
ChartJS.register(CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend, Filler);
|
||||
|
||||
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
import Link from "next/link";
|
||||
import { useTheme } from "./ThemeProvider";
|
||||
import { useFamily } from "./FamilyProvider";
|
||||
import { useTheme } from "@/app/ThemeProvider";
|
||||
import { useFamily } from "@/app/FamilyProvider";
|
||||
import { useStageCheck, type BabyStage } from "@/hooks/useStageCheck";
|
||||
import { LogModal, type LogType } from "@/components/LogModal";
|
||||
import { getOfflineQueue, processOfflineQueue } from "@/lib/offline-queue";
|
||||
19
src/app/(app)/layout.tsx
Normal file
19
src/app/(app)/layout.tsx
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import { ThemeProvider } from "@/app/ThemeProvider";
|
||||
import { FamilyProvider } from "@/app/FamilyProvider";
|
||||
import { PageTransition } from "@/components/PageTransition";
|
||||
import { BottomNav } from "@/components/BottomNav";
|
||||
|
||||
export default function AppLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<ThemeProvider>
|
||||
<FamilyProvider>
|
||||
<PageTransition>{children}</PageTransition>
|
||||
<BottomNav />
|
||||
</FamilyProvider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { useFamily } from "../FamilyProvider";
|
||||
import { useFamily } from "@/app/FamilyProvider";
|
||||
import { PageHeader } from "@/components/PageHeader";
|
||||
import { TabBar } from "@/components/TabBar";
|
||||
import { VaccineTab } from "@/components/medical/VaccineTab";
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import { useState, useEffect, useRef, useCallback } from "react";
|
||||
import Link from "next/link";
|
||||
import { useFamily } from "../FamilyProvider";
|
||||
import { useFamily } from "@/app/FamilyProvider";
|
||||
import { Button, ConfirmDialog, Modal } from "@/components/ui";
|
||||
|
||||
const PRESET_FOLDERS = [
|
||||
|
|
@ -68,7 +68,7 @@ export default function OnboardingPage() {
|
|||
if (!data.authenticated) {
|
||||
router.push("/login");
|
||||
} else if (data.familyId) {
|
||||
router.push("/");
|
||||
router.push("/home");
|
||||
}
|
||||
setCheckingAuth(false);
|
||||
}
|
||||
|
|
@ -127,7 +127,7 @@ export default function OnboardingPage() {
|
|||
console.error(e);
|
||||
}
|
||||
setSaving(false);
|
||||
router.push("/");
|
||||
router.push("/home");
|
||||
};
|
||||
|
||||
if (checkingAuth) {
|
||||
|
|
@ -268,7 +268,7 @@ export default function OnboardingPage() {
|
|||
)}
|
||||
|
||||
<div className="flex gap-2 pt-2">
|
||||
<Button variant="ghost" fullWidth onClick={() => router.push("/")}>Skip for now</Button>
|
||||
<Button variant="ghost" fullWidth onClick={() => router.push("/home")}>Skip for now</Button>
|
||||
<Button fullWidth loading={saving} onClick={handleVaccSave}>Save & Go Home</Button>
|
||||
</div>
|
||||
</>
|
||||
|
|
@ -3,8 +3,8 @@
|
|||
import { useState, useEffect } from "react";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useTheme } from "../ThemeProvider";
|
||||
import { useFamily } from "../FamilyProvider";
|
||||
import { useTheme } from "@/app/ThemeProvider";
|
||||
import { useFamily } from "@/app/FamilyProvider";
|
||||
import { Button, Card, Input, Select, Badge } from "@/components/ui";
|
||||
|
||||
interface Member {
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
import { useRef, useState } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import Image from "next/image";
|
||||
import { useFamily } from "../../FamilyProvider";
|
||||
import { useFamily } from "@/app/FamilyProvider";
|
||||
import { Button } from "@/components/ui";
|
||||
import { GARMENT_CATEGORIES, GARMENT_SIZE_ORDER } from "@/db/schema/wardrobe";
|
||||
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import { useState, useEffect } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useFamily } from "../../FamilyProvider";
|
||||
import { useFamily } from "@/app/FamilyProvider";
|
||||
import { Button } from "@/components/ui";
|
||||
|
||||
interface OutfitItem {
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import { useState } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useFamily } from "../../FamilyProvider";
|
||||
import { useFamily } from "@/app/FamilyProvider";
|
||||
import { Button } from "@/components/ui";
|
||||
|
||||
interface PackingItem {
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
import { useState, useEffect, useCallback } from "react";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useFamily } from "../FamilyProvider";
|
||||
import { useFamily } from "@/app/FamilyProvider";
|
||||
import { GARMENT_CATEGORIES, GARMENT_SIZE_ORDER } from "@/db/schema/wardrobe";
|
||||
|
||||
interface Garment {
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
import { useState, useEffect } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import Link from "next/link";
|
||||
import { useFamily } from "../../FamilyProvider";
|
||||
import { useFamily } from "@/app/FamilyProvider";
|
||||
|
||||
interface SavedOutfit {
|
||||
id: string;
|
||||
95
src/app/(marketing)/layout.tsx
Normal file
95
src/app/(marketing)/layout.tsx
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
import type { Metadata } from "next";
|
||||
import Link from "next/link";
|
||||
import { NavAuthButton } from "@/components/marketing/NavAuthButton";
|
||||
import Script from "next/script";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: {
|
||||
default: "Tia — Your baby's digital heirloom",
|
||||
template: "%s | Tia",
|
||||
},
|
||||
description:
|
||||
"Tia is a digital heirloom for your baby — not just a tracker. Log daily moments, track the IAP vaccination schedule, and build a living archive your child will one day inherit.",
|
||||
openGraph: {
|
||||
type: "website",
|
||||
siteName: "Tia",
|
||||
title: "Tia — Your baby's digital heirloom",
|
||||
description:
|
||||
"Log every feed, milestone, and memory. Built for Indian families. Privacy-first. Free during early access.",
|
||||
},
|
||||
twitter: {
|
||||
card: "summary_large_image",
|
||||
title: "Tia — Your baby's digital heirloom",
|
||||
description:
|
||||
"Log every feed, milestone, and memory. Built for Indian families. Privacy-first. Free during early access.",
|
||||
},
|
||||
};
|
||||
|
||||
export default function MarketingLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
{/* Privacy-respecting analytics — no cookies, no tracking */}
|
||||
<Script
|
||||
defer
|
||||
data-domain={process.env.NEXT_PUBLIC_APP_URL?.replace("https://", "").replace("http://", "")}
|
||||
src="https://plausible.io/js/script.js"
|
||||
strategy="afterInteractive"
|
||||
/>
|
||||
|
||||
{/* Top nav */}
|
||||
<header className="sticky top-0 z-50 bg-white/90 backdrop-blur-sm border-b border-rose-100">
|
||||
<div className="max-w-5xl mx-auto px-4 py-3 flex items-center justify-between">
|
||||
<Link href="/" className="flex items-center gap-2">
|
||||
<span className="text-2xl">🌸</span>
|
||||
<span className="text-lg font-bold text-gray-900" style={{ fontFamily: "var(--font-caveat)" }}>
|
||||
Tia
|
||||
</span>
|
||||
</Link>
|
||||
|
||||
<nav className="hidden sm:flex items-center gap-6 text-sm text-gray-600">
|
||||
<Link href="/pricing" className="hover:text-rose-600 transition-colors">Pricing</Link>
|
||||
<Link href="/privacy" className="hover:text-rose-600 transition-colors">Privacy</Link>
|
||||
</nav>
|
||||
|
||||
<NavAuthButton />
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main>{children}</main>
|
||||
|
||||
{/* Footer */}
|
||||
<footer className="bg-gray-50 border-t border-gray-100 mt-20">
|
||||
<div className="max-w-5xl mx-auto px-4 py-12">
|
||||
<div className="flex flex-col sm:flex-row justify-between items-start gap-8">
|
||||
<div>
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<span className="text-xl">🌸</span>
|
||||
<span className="text-base font-bold text-gray-900" style={{ fontFamily: "var(--font-caveat)" }}>
|
||||
Tia
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-sm text-gray-500 max-w-xs">
|
||||
A digital heirloom for your baby. Every moment, preserved.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-2 text-sm">
|
||||
<p className="font-semibold text-gray-700 mb-1">Pages</p>
|
||||
<Link href="/pricing" className="text-gray-500 hover:text-rose-600 transition-colors">Pricing</Link>
|
||||
<Link href="/privacy" className="text-gray-500 hover:text-rose-600 transition-colors">Privacy Policy</Link>
|
||||
<Link href="/terms" className="text-gray-500 hover:text-rose-600 transition-colors">Terms of Service</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-10 pt-6 border-t border-gray-200 text-xs text-gray-400">
|
||||
© {new Date().getFullYear()} Tia. Built with love in India. We don't sell your data — we preserve it.
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</>
|
||||
);
|
||||
}
|
||||
66
src/app/(marketing)/opengraph-image.tsx
Normal file
66
src/app/(marketing)/opengraph-image.tsx
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
import { ImageResponse } from "next/og";
|
||||
|
||||
export const alt = "Tia — Your baby's digital heirloom";
|
||||
export const size = { width: 1200, height: 630 };
|
||||
export const contentType = "image/png";
|
||||
|
||||
export default function OgImage() {
|
||||
return new ImageResponse(
|
||||
(
|
||||
<div
|
||||
style={{
|
||||
background: "linear-gradient(135deg, #fdf2f2 0%, #fef3c7 50%, #fdf2f2 100%)",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
fontFamily: "sans-serif",
|
||||
padding: 80,
|
||||
}}
|
||||
>
|
||||
<div style={{ fontSize: 72, marginBottom: 24 }}>🌸</div>
|
||||
|
||||
<div
|
||||
style={{
|
||||
fontSize: 60,
|
||||
fontWeight: 800,
|
||||
color: "#111827",
|
||||
textAlign: "center",
|
||||
lineHeight: 1.2,
|
||||
marginBottom: 20,
|
||||
}}
|
||||
>
|
||||
Tia
|
||||
</div>
|
||||
|
||||
<div
|
||||
style={{
|
||||
fontSize: 28,
|
||||
color: "#f43f5e",
|
||||
fontWeight: 600,
|
||||
textAlign: "center",
|
||||
marginBottom: 24,
|
||||
}}
|
||||
>
|
||||
Your baby's digital heirloom
|
||||
</div>
|
||||
|
||||
<div
|
||||
style={{
|
||||
fontSize: 22,
|
||||
color: "#6b7280",
|
||||
textAlign: "center",
|
||||
maxWidth: 800,
|
||||
lineHeight: 1.5,
|
||||
}}
|
||||
>
|
||||
Log every feed, milestone, and memory.
|
||||
Built for Indian families. Privacy-first. Free during early access.
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
{ ...size }
|
||||
);
|
||||
}
|
||||
410
src/app/(marketing)/page.tsx
Normal file
410
src/app/(marketing)/page.tsx
Normal file
|
|
@ -0,0 +1,410 @@
|
|||
import type { Metadata } from "next";
|
||||
import Link from "next/link";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Tia — Your baby's digital heirloom",
|
||||
description:
|
||||
"Tia is a digital heirloom for your baby — not just a tracker. Log daily moments, track the IAP vaccination schedule with Telegram alerts, and build a living archive your child will one day inherit.",
|
||||
};
|
||||
|
||||
// ── Section: Hero ───────────────────────────────────────────────
|
||||
function Hero() {
|
||||
return (
|
||||
<section className="relative overflow-hidden bg-gradient-to-br from-rose-50 via-amber-50 to-rose-50 pt-16 pb-24 px-4">
|
||||
<div className="max-w-2xl mx-auto text-center">
|
||||
<div className="inline-flex items-center gap-2 bg-rose-100 text-rose-700 text-xs font-semibold px-3 py-1.5 rounded-full mb-6">
|
||||
<span>✨</span> Free during early access
|
||||
</div>
|
||||
|
||||
<h1 className="text-4xl sm:text-5xl font-bold text-gray-900 leading-tight mb-5">
|
||||
Your baby's story,{" "}
|
||||
<span className="text-rose-500" style={{ fontFamily: "var(--font-caveat)", fontSize: "1.1em" }}>
|
||||
preserved for a lifetime.
|
||||
</span>
|
||||
</h1>
|
||||
|
||||
<p className="text-lg text-gray-600 leading-relaxed mb-8 max-w-xl mx-auto">
|
||||
Tia is a digital heirloom — not just a tracker. Every feed, every first
|
||||
word, every vaccination, archived in one private place your child will
|
||||
one day look back on.
|
||||
</p>
|
||||
|
||||
<Link
|
||||
href="/login"
|
||||
className="inline-flex items-center gap-2 bg-rose-500 hover:bg-rose-600 text-white font-semibold px-7 py-3.5 rounded-full text-base transition-colors shadow-md shadow-rose-200"
|
||||
>
|
||||
<svg className="w-5 h-5" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
||||
<path d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z" fill="#fff" fillOpacity=".9"/>
|
||||
<path d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" fill="#fff" fillOpacity=".9"/>
|
||||
<path d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l3.66-2.84z" fill="#fff" fillOpacity=".9"/>
|
||||
<path d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" fill="#fff" fillOpacity=".9"/>
|
||||
</svg>
|
||||
Continue with Google
|
||||
</Link>
|
||||
|
||||
<p className="mt-4 text-xs text-gray-400">No credit card. No setup fee. Your data is yours.</p>
|
||||
</div>
|
||||
|
||||
{/* Decorative blobs */}
|
||||
<div className="absolute -top-20 -right-20 w-64 h-64 bg-rose-100 rounded-full opacity-40 blur-3xl pointer-events-none" />
|
||||
<div className="absolute -bottom-16 -left-16 w-48 h-48 bg-amber-100 rounded-full opacity-50 blur-2xl pointer-events-none" />
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
// ── Section: The Problem ────────────────────────────────────────
|
||||
function TheProblem() {
|
||||
return (
|
||||
<section className="py-20 px-4 bg-white">
|
||||
<div className="max-w-2xl mx-auto">
|
||||
<p className="text-xs font-semibold text-rose-500 uppercase tracking-widest mb-4">The 3am reality</p>
|
||||
|
||||
<h2 className="text-3xl font-bold text-gray-900 mb-6 leading-tight">
|
||||
You're awake at 3am.<br />
|
||||
When did she last feed?
|
||||
</h2>
|
||||
|
||||
<div className="space-y-4 text-gray-600 leading-relaxed">
|
||||
<p>
|
||||
You scroll back through WhatsApp messages to your mother-in-law. You check a
|
||||
sticky note on the refrigerator. You open a notes app you downloaded last week
|
||||
and another one you downloaded the week before that.
|
||||
</p>
|
||||
<p>
|
||||
Every precious detail — the exact weight at the six-week checkup, the tiny smile
|
||||
at eleven weeks, the first solid feed — is scattered across six apps, two
|
||||
notebooks, and a dozen photographs that may or may not be backed up.
|
||||
</p>
|
||||
<p>
|
||||
And somewhere underneath the exhaustion, a quieter fear: <em>I'm going to forget this.</em>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="mt-8 grid grid-cols-1 sm:grid-cols-3 gap-4">
|
||||
{[
|
||||
{ icon: "📱", text: "Notes scattered across 4+ apps" },
|
||||
{ icon: "😰", text: "Fear of losing precious moments" },
|
||||
{ icon: "🌙", text: "No answers at 3am" },
|
||||
].map(item => (
|
||||
<div key={item.text} className="flex items-start gap-3 bg-rose-50 rounded-xl p-4">
|
||||
<span className="text-2xl flex-shrink-0">{item.icon}</span>
|
||||
<p className="text-sm text-gray-700 font-medium">{item.text}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
// ── Section: Features ──────────────────────────────────────────
|
||||
const FEATURES = [
|
||||
{
|
||||
icon: "🍼",
|
||||
title: "Log in 5 seconds",
|
||||
body: "Feed, sleep, diaper — one tap. Tia timestamps everything automatically. At 3am, you should be sleeping, not typing.",
|
||||
example: "\"Fed 90ml at 2:47am\" → logged and archived forever.",
|
||||
},
|
||||
{
|
||||
icon: "💉",
|
||||
title: "IAP vaccination schedule",
|
||||
body: "Tia tracks your baby's vaccination schedule against the Indian Academy of Pediatrics (IAP) recommended schedule. Reminders arrive via Telegram — the one app you actually check.",
|
||||
example: "BCG, OPV, Hepatitis B — all tracked. Telegram alert: \"Pentavalent 2 due in 3 days.\"",
|
||||
},
|
||||
{
|
||||
icon: "🔮",
|
||||
title: "Ask Tia",
|
||||
body: "Ask Tia parenting logistics questions — feeding windows, sleep patterns, when to introduce solids. For anything that sounds medical, Tia will always refer you to your pediatrician. That restraint is intentional. It's a trust feature, not a limitation.",
|
||||
example: "\"Is 90ml normal at 6 weeks?\" → Tia gives context, then: \"Your pediatrician can confirm this for your baby specifically.\"",
|
||||
},
|
||||
{
|
||||
icon: "📚",
|
||||
title: "The heirloom archive",
|
||||
body: "Every log, photo, milestone, and memory becomes part of a permanent, private archive. Not a feed. Not a highlights reel. A complete record of your child's earliest years — searchable, exportable, and theirs to keep.",
|
||||
example: "\"Show me everything from her first month\" → every feed, every photo, every note.",
|
||||
},
|
||||
{
|
||||
icon: "👨👩👧",
|
||||
title: "Family circle",
|
||||
body: "Role-based access so everyone in your baby's life can be as involved as they should be. Grandparents get view-only access to milestones. The nanny gets caregiver access to log feeds. You stay the admin.",
|
||||
example: "Nani in Jaipur sees today's photos in real time. The daai logs the afternoon feed.",
|
||||
},
|
||||
{
|
||||
icon: "📈",
|
||||
title: "Growth tracking",
|
||||
body: "Weight, length, and head circumference plotted on clear charts. See how your baby is growing over time and carry the full history into every pediatric visit.",
|
||||
example: "\"She's been in the 60th percentile for weight since month two.\"",
|
||||
},
|
||||
];
|
||||
|
||||
function Features() {
|
||||
return (
|
||||
<section className="py-20 px-4 bg-gray-50">
|
||||
<div className="max-w-2xl mx-auto">
|
||||
<p className="text-xs font-semibold text-rose-500 uppercase tracking-widest mb-4 text-center">What Tia does</p>
|
||||
<h2 className="text-3xl font-bold text-gray-900 text-center mb-12">
|
||||
Everything in one private place.
|
||||
</h2>
|
||||
|
||||
<div className="space-y-4">
|
||||
{FEATURES.map(f => (
|
||||
<div key={f.title} className="bg-white rounded-2xl p-6 shadow-sm border border-gray-100">
|
||||
<div className="flex items-start gap-4">
|
||||
<span className="text-3xl flex-shrink-0">{f.icon}</span>
|
||||
<div>
|
||||
<h3 className="font-bold text-gray-900 text-lg mb-2">{f.title}</h3>
|
||||
<p className="text-gray-600 text-sm leading-relaxed mb-3" dangerouslySetInnerHTML={{ __html: f.body }} />
|
||||
<div className="bg-rose-50 rounded-lg px-3 py-2 text-xs text-rose-700 italic">
|
||||
{f.example}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
// ── Section: Founder Story ─────────────────────────────────────
|
||||
function FounderStory() {
|
||||
return (
|
||||
<section className="py-20 px-4 bg-white">
|
||||
<div className="max-w-2xl mx-auto">
|
||||
<p className="text-xs font-semibold text-rose-500 uppercase tracking-widest mb-4">Why Tia exists</p>
|
||||
|
||||
<div className="bg-amber-50 border border-amber-200 rounded-2xl p-6 sm:p-8">
|
||||
{/*
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ PLACEHOLDER — Manohar, this section is yours to write. │
|
||||
│ │
|
||||
│ Tell the story of your daughter. The specific 3am moment in │
|
||||
│ Gurugram that made you start building. The fear that she'd │
|
||||
│ grow up and you'd have nothing but blurry photos. The reason │
|
||||
│ you want her to be able to read her own first chapter one day. │
|
||||
│ │
|
||||
│ Replace everything between these comment tags with your own │
|
||||
│ words. This section is the moat — it has to be in your voice. │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
*/}
|
||||
<div className="flex items-center gap-3 mb-5">
|
||||
<div className="w-10 h-10 rounded-full bg-rose-200 flex items-center justify-center text-lg">👨💻</div>
|
||||
<div>
|
||||
<p className="font-semibold text-gray-900">Manohar Gupta</p>
|
||||
<p className="text-xs text-gray-500">Founder, Tia · Gurugram</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<blockquote className="text-gray-700 leading-relaxed space-y-4 italic text-sm sm:text-base">
|
||||
<p>
|
||||
[PLACEHOLDER] My daughter was born and within two weeks I realised I was already
|
||||
forgetting things. Not forgetting them completely — just losing the texture. The
|
||||
exact weight on day five. The time of the first real smile. The way she sounded
|
||||
when she figured out her own hands.
|
||||
</p>
|
||||
<p>
|
||||
[PLACEHOLDER] I wanted to build something that would let her read her own first
|
||||
chapter one day. Not a social media highlight reel. A real archive — private,
|
||||
complete, and hers to keep. That's Tia.
|
||||
</p>
|
||||
<p>
|
||||
[PLACEHOLDER] — Replace these paragraphs with your story in your own words.
|
||||
The specific 3am moment. The specific fear. Why Gurugram, why this daughter, why now.
|
||||
</p>
|
||||
</blockquote>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
// ── Section: The Heirloom Vision ───────────────────────────────
|
||||
function HeirloomVision() {
|
||||
return (
|
||||
<section className="py-20 px-4 bg-gradient-to-br from-rose-50 to-amber-50">
|
||||
<div className="max-w-2xl mx-auto text-center">
|
||||
<p className="text-xs font-semibold text-rose-500 uppercase tracking-widest mb-4">The heirloom vision</p>
|
||||
|
||||
<h2 className="text-3xl font-bold text-gray-900 mb-6 leading-tight">
|
||||
One day, your child will be able to{" "}
|
||||
<span className="text-rose-500" style={{ fontFamily: "var(--font-caveat)", fontSize: "1.1em" }}>
|
||||
read their own story.
|
||||
</span>
|
||||
</h2>
|
||||
|
||||
<p className="text-gray-600 leading-relaxed mb-8 max-w-xl mx-auto">
|
||||
Everything you log today is a letter to your future child. The 2:47am feed.
|
||||
The first solid. The doctor visit you worried about for a week. The photo from
|
||||
the moment you realised she could recognise your voice.
|
||||
</p>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4 text-left">
|
||||
{[
|
||||
{
|
||||
icon: "📖",
|
||||
title: "A complete record",
|
||||
desc: "Not highlights. Everything — because you can't know yet which moment will matter most.",
|
||||
},
|
||||
{
|
||||
icon: "🔒",
|
||||
title: "Private and permanent",
|
||||
desc: "No public feed. No algorithm. Your family's archive, locked to your family.",
|
||||
},
|
||||
{
|
||||
icon: "💾",
|
||||
title: "Fully exportable",
|
||||
desc: "Your data is yours. Export everything at any time. The heirloom is portable.",
|
||||
},
|
||||
].map(item => (
|
||||
<div key={item.title} className="bg-white/80 rounded-2xl p-5 border border-rose-100">
|
||||
<span className="text-2xl mb-3 block">{item.icon}</span>
|
||||
<h3 className="font-bold text-gray-900 mb-1">{item.title}</h3>
|
||||
<p className="text-sm text-gray-600 leading-relaxed" dangerouslySetInnerHTML={{ __html: item.desc }} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
// ── Section: Privacy & Trust ───────────────────────────────────
|
||||
function Privacy() {
|
||||
return (
|
||||
<section className="py-20 px-4 bg-white">
|
||||
<div className="max-w-2xl mx-auto">
|
||||
<p className="text-xs font-semibold text-rose-500 uppercase tracking-widest mb-4">Privacy & trust</p>
|
||||
|
||||
<h2 className="text-3xl font-bold text-gray-900 mb-4">
|
||||
We don't sell your data —<br />
|
||||
<span className="text-rose-500">we preserve it.</span>
|
||||
</h2>
|
||||
|
||||
<p className="text-gray-600 leading-relaxed mb-8">
|
||||
Tia is a baby-tracking app. Your child's records are not the product.
|
||||
They are the point.
|
||||
</p>
|
||||
|
||||
<div className="space-y-3">
|
||||
{[
|
||||
{
|
||||
icon: "🏛️",
|
||||
title: "Row-Level Security",
|
||||
desc: "Your family's data is isolated at the database level. No other user — no other family — can reach your records.",
|
||||
},
|
||||
{
|
||||
icon: "🚫",
|
||||
title: "No ads. No data resale.",
|
||||
desc: "We do not sell, share, or monetise your data with third parties. Ever.",
|
||||
},
|
||||
{
|
||||
icon: "📤",
|
||||
title: "Full export at any time",
|
||||
desc: "Export everything: logs, photos, milestones, vaccinations. Your data leaves with you whenever you want.",
|
||||
},
|
||||
{
|
||||
icon: "🔐",
|
||||
title: "Google Sign-In only",
|
||||
desc: "We don't store passwords. Authentication is handled by Google — the same account you already trust for everything else.",
|
||||
},
|
||||
].map(item => (
|
||||
<div key={item.title} className="flex items-start gap-4 p-4 bg-gray-50 rounded-xl">
|
||||
<span className="text-xl flex-shrink-0 mt-0.5">{item.icon}</span>
|
||||
<div>
|
||||
<p className="font-semibold text-gray-900 text-sm">{item.title}</p>
|
||||
<p className="text-sm text-gray-600 leading-relaxed mt-0.5" dangerouslySetInnerHTML={{ __html: item.desc }} />
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
// ── Section: Early Access ──────────────────────────────────────
|
||||
function EarlyAccess() {
|
||||
return (
|
||||
<section className="py-16 px-4 bg-gray-50 border-y border-gray-100">
|
||||
<div className="max-w-2xl mx-auto">
|
||||
<p className="text-xs font-semibold text-rose-500 uppercase tracking-widest mb-4 text-center">Private early access</p>
|
||||
|
||||
<h2 className="text-2xl font-bold text-gray-900 text-center mb-6">
|
||||
Built by a parent, being tested by parents.
|
||||
</h2>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
{[
|
||||
{
|
||||
icon: "🌱",
|
||||
title: "Free during early access",
|
||||
desc: "Core features — activity logging and the heirloom archive — are free while Tia is in early access. Early families keep their terms for life.",
|
||||
},
|
||||
{
|
||||
icon: "🇮🇳",
|
||||
title: "India-native from day one",
|
||||
desc: "IAP vaccination schedule. Telegram alerts. Indian naming conventions. Built for how Indian families actually live.",
|
||||
},
|
||||
].map(item => (
|
||||
<div key={item.title} className="bg-white rounded-2xl p-5 border border-gray-100 shadow-sm">
|
||||
<span className="text-2xl mb-3 block">{item.icon}</span>
|
||||
<h3 className="font-bold text-gray-900 mb-2">{item.title}</h3>
|
||||
<p className="text-sm text-gray-600 leading-relaxed">{item.desc}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
// ── Section: Final CTA ─────────────────────────────────────────
|
||||
function FinalCTA() {
|
||||
return (
|
||||
<section className="py-24 px-4 bg-gradient-to-br from-rose-500 to-rose-600 text-white text-center">
|
||||
<div className="max-w-xl mx-auto">
|
||||
<h2 className="text-3xl sm:text-4xl font-bold mb-4 leading-tight">
|
||||
Start preserving your<br />child's story today.
|
||||
</h2>
|
||||
|
||||
<p className="text-rose-100 mb-8 leading-relaxed">
|
||||
Free during early access. No credit card. Your data is yours — always.
|
||||
</p>
|
||||
|
||||
<Link
|
||||
href="/login"
|
||||
className="inline-flex items-center gap-2 bg-white text-rose-600 font-bold px-8 py-4 rounded-full text-base hover:bg-rose-50 transition-colors shadow-lg"
|
||||
>
|
||||
<svg className="w-5 h-5" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
||||
<path d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z" fill="#4285F4"/>
|
||||
<path d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" fill="#34A853"/>
|
||||
<path d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l3.66-2.84z" fill="#FBBC05"/>
|
||||
<path d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" fill="#EA4335"/>
|
||||
</svg>
|
||||
Continue with Google
|
||||
</Link>
|
||||
|
||||
<p className="mt-4 text-xs text-rose-200">
|
||||
Invite-only early access — join from a shared link or reach out directly.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
// ── Page ───────────────────────────────────────────────────────
|
||||
export default function MarketingHomePage() {
|
||||
return (
|
||||
<>
|
||||
<Hero />
|
||||
<TheProblem />
|
||||
<Features />
|
||||
<FounderStory />
|
||||
<HeirloomVision />
|
||||
<Privacy />
|
||||
<EarlyAccess />
|
||||
<FinalCTA />
|
||||
</>
|
||||
);
|
||||
}
|
||||
81
src/app/(marketing)/pricing/page.tsx
Normal file
81
src/app/(marketing)/pricing/page.tsx
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
import type { Metadata } from "next";
|
||||
import Link from "next/link";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Pricing",
|
||||
description: "Founder pricing — early families keep their terms for life.",
|
||||
};
|
||||
|
||||
export default function PricingPage() {
|
||||
return (
|
||||
<div className="max-w-2xl mx-auto px-4 py-20">
|
||||
<p className="text-xs font-semibold text-rose-500 uppercase tracking-widest mb-4 text-center">Pricing</p>
|
||||
|
||||
<h1 className="text-3xl sm:text-4xl font-bold text-gray-900 text-center mb-4">
|
||||
Founder pricing.
|
||||
</h1>
|
||||
<p className="text-gray-500 text-center mb-12 max-w-md mx-auto">
|
||||
Early families keep their terms for life.
|
||||
</p>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div className="bg-white rounded-2xl border border-gray-100 shadow-sm p-6">
|
||||
<div className="flex items-start justify-between mb-4">
|
||||
<div>
|
||||
<h2 className="text-lg font-bold text-gray-900">Free — always</h2>
|
||||
<p className="text-sm text-gray-500 mt-1">The core heirloom experience</p>
|
||||
</div>
|
||||
<span className="bg-green-100 text-green-700 text-xs font-semibold px-3 py-1 rounded-full">Included</span>
|
||||
</div>
|
||||
<ul className="space-y-2 text-sm text-gray-700">
|
||||
{[
|
||||
"Activity logging — feed, sleep, diaper, and more",
|
||||
"The heirloom archive — every log, searchable forever",
|
||||
"Milestone tracking",
|
||||
"IAP vaccination schedule",
|
||||
"Telegram alerts for upcoming vaccinations",
|
||||
"Ask Tia (parenting logistics AI)",
|
||||
"Family circle with role-based access",
|
||||
"Growth tracking",
|
||||
"Full data export — the archive is yours",
|
||||
].map(item => (
|
||||
<li key={item} className="flex items-start gap-2">
|
||||
<span className="text-rose-400 flex-shrink-0 mt-0.5">✓</span>
|
||||
{item}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-2xl border border-gray-100 shadow-sm p-6">
|
||||
<div className="flex items-start justify-between mb-4">
|
||||
<div>
|
||||
<h2 className="text-lg font-bold text-gray-900">Media storage</h2>
|
||||
<p className="text-sm text-gray-500 mt-1">Photos and video in the archive</p>
|
||||
</div>
|
||||
<span className="bg-amber-100 text-amber-700 text-xs font-semibold px-3 py-1 rounded-full">Paid / metered</span>
|
||||
</div>
|
||||
<p className="text-sm text-gray-600 leading-relaxed">
|
||||
Storing photos and video at scale has a real infrastructure cost. Media storage is
|
||||
the one thing we charge for — metered, so you only pay for what you use.
|
||||
Pricing details will be shared before the early-access period ends.
|
||||
</p>
|
||||
<p className="text-xs text-gray-400 mt-3">
|
||||
Founder families joining during early access will be the first to know — and will
|
||||
keep whatever terms we agree on, for life.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-12 text-center">
|
||||
<Link
|
||||
href="/login"
|
||||
className="inline-flex items-center gap-2 bg-rose-500 hover:bg-rose-600 text-white font-semibold px-7 py-3.5 rounded-full text-base transition-colors"
|
||||
>
|
||||
Continue with Google — it's free
|
||||
</Link>
|
||||
<p className="text-xs text-gray-400 mt-3">No credit card required.</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
80
src/app/(marketing)/privacy/page.tsx
Normal file
80
src/app/(marketing)/privacy/page.tsx
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
import type { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Privacy Policy",
|
||||
description: "How Tia handles your family's data. We don't sell it — we preserve it.",
|
||||
};
|
||||
|
||||
export default function PrivacyPage() {
|
||||
return (
|
||||
<div className="max-w-2xl mx-auto px-4 py-20">
|
||||
<h1 className="text-3xl font-bold text-gray-900 mb-2">Privacy Policy</h1>
|
||||
<p className="text-sm text-gray-400 mb-10">Last updated: May 2026</p>
|
||||
|
||||
<div className="prose prose-gray max-w-none space-y-8 text-gray-700 leading-relaxed">
|
||||
<section>
|
||||
<h2 className="text-xl font-bold text-gray-900 mb-3">The short version</h2>
|
||||
<p>
|
||||
We don't sell your data. We don't show you ads. We don't share your records
|
||||
with third parties. Your family's data exists in Tia for one purpose: to
|
||||
be preserved for you and your child.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="text-xl font-bold text-gray-900 mb-3">What we collect</h2>
|
||||
<ul className="list-disc pl-5 space-y-2 text-sm">
|
||||
<li>Your name and email address, via Google Sign-In.</li>
|
||||
<li>Activity logs you enter (feeds, sleep, diapers, medical records, milestones).</li>
|
||||
<li>Photos and media you upload to the heirloom archive.</li>
|
||||
<li>Device and session information for security purposes.</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="text-xl font-bold text-gray-900 mb-3">How we protect it</h2>
|
||||
<p>
|
||||
Your family's data is isolated at the database level using Row-Level Security (RLS).
|
||||
No other user — no other family — can access your records. We use HTTPS everywhere.
|
||||
Sessions are managed with secure, httpOnly cookies.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="text-xl font-bold text-gray-900 mb-3">Who we share it with</h2>
|
||||
<p>
|
||||
We use the following third-party services to operate Tia. We do not sell your data
|
||||
to any third party, ever.
|
||||
</p>
|
||||
<ul className="list-disc pl-5 space-y-2 text-sm mt-3">
|
||||
<li><strong>Google</strong> — authentication only.</li>
|
||||
<li><strong>Cloudflare R2</strong> — media storage (photos, video).</li>
|
||||
<li><strong>Telegram</strong> — vaccination alerts, if you opt in.</li>
|
||||
<li><strong>Resend</strong> — transactional email (invites, verification).</li>
|
||||
<li><strong>Plausible Analytics</strong> — privacy-preserving, cookie-free analytics on our marketing pages only. No tracking inside the app.</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="text-xl font-bold text-gray-900 mb-3">Your rights</h2>
|
||||
<ul className="list-disc pl-5 space-y-2 text-sm">
|
||||
<li><strong>Export:</strong> You can export all your data at any time from Settings. The heirloom is portable and yours.</li>
|
||||
<li><strong>Deletion:</strong> You can delete your account and all associated data. Contact us at <a href="mailto:tia@manohargupta.com" className="text-rose-500 hover:underline">tia@manohargupta.com</a>.</li>
|
||||
<li><strong>Correction:</strong> You can edit or delete any log or record within the app.</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="text-xl font-bold text-gray-900 mb-3">Contact</h2>
|
||||
<p>
|
||||
Questions about this policy? Email us at{" "}
|
||||
<a href="mailto:tia@manohargupta.com" className="text-rose-500 hover:underline">
|
||||
tia@manohargupta.com
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
87
src/app/(marketing)/terms/page.tsx
Normal file
87
src/app/(marketing)/terms/page.tsx
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
import type { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Terms of Service",
|
||||
description: "Terms of service for Tia.",
|
||||
};
|
||||
|
||||
export default function TermsPage() {
|
||||
return (
|
||||
<div className="max-w-2xl mx-auto px-4 py-20">
|
||||
<h1 className="text-3xl font-bold text-gray-900 mb-2">Terms of Service</h1>
|
||||
<p className="text-sm text-gray-400 mb-10">Last updated: May 2026</p>
|
||||
|
||||
<div className="space-y-8 text-gray-700 leading-relaxed">
|
||||
<section>
|
||||
<h2 className="text-xl font-bold text-gray-900 mb-3">Acceptance</h2>
|
||||
<p>
|
||||
By using Tia, you agree to these terms. If you don't agree, please don't use
|
||||
the service.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="text-xl font-bold text-gray-900 mb-3">What Tia is</h2>
|
||||
<p>
|
||||
Tia is a baby-tracking and heirloom-archiving application for families. It is
|
||||
not a medical service. Tia does not provide medical advice, diagnosis, or
|
||||
treatment. For any health concerns about your child, consult your pediatrician.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="text-xl font-bold text-gray-900 mb-3">Your account</h2>
|
||||
<p>
|
||||
You are responsible for maintaining the security of your account. You must
|
||||
be at least 18 years old to create an account. Family members you invite
|
||||
are governed by these same terms.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="text-xl font-bold text-gray-900 mb-3">Your data</h2>
|
||||
<p>
|
||||
You own your data. We do not claim any ownership over the records, photos,
|
||||
or content you create in Tia. You can export and delete your data at any time.
|
||||
See our <a href="/privacy" className="text-rose-500 hover:underline">Privacy Policy</a> for details.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="text-xl font-bold text-gray-900 mb-3">Acceptable use</h2>
|
||||
<p>
|
||||
You agree not to use Tia to store illegal content, harm others, or interfere
|
||||
with the service. We may terminate accounts that violate these terms.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="text-xl font-bold text-gray-900 mb-3">Service availability</h2>
|
||||
<p>
|
||||
Tia is provided as-is during early access. We make no guarantees of uptime
|
||||
or availability, though we work hard to keep the service running reliably.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="text-xl font-bold text-gray-900 mb-3">Changes to these terms</h2>
|
||||
<p>
|
||||
We may update these terms. We will notify you by email before any material
|
||||
changes take effect.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="text-xl font-bold text-gray-900 mb-3">Contact</h2>
|
||||
<p>
|
||||
Questions? Email{" "}
|
||||
<a href="mailto:tia@manohargupta.com" className="text-rose-500 hover:underline">
|
||||
tia@manohargupta.com
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -24,7 +24,7 @@ export default function InvitePage({ params }: { params: { token: string } }) {
|
|||
const data = await res.json();
|
||||
|
||||
if (data.success) {
|
||||
router.push("/");
|
||||
router.push("/home");
|
||||
} else {
|
||||
setError(data.error || "Invalid invite");
|
||||
}
|
||||
|
|
@ -55,7 +55,7 @@ export default function InvitePage({ params }: { params: { token: string } }) {
|
|||
<div className="text-center p-8">
|
||||
<div className="text-4xl mb-4">❌</div>
|
||||
<p className="text-red-500">{error}</p>
|
||||
<button onClick={() => router.push("/")} className="mt-4 px-4 py-2 bg-rose-400 text-white rounded-lg">
|
||||
<button onClick={() => router.push("/home")} className="mt-4 px-4 py-2 bg-rose-400 text-white rounded-lg">
|
||||
Go Home
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,5 @@
|
|||
import type { Metadata } from "next";
|
||||
import { Geist, Geist_Mono, Caveat } from "next/font/google";
|
||||
import { ThemeProvider } from "./ThemeProvider";
|
||||
import { FamilyProvider } from "./FamilyProvider";
|
||||
import { PageTransition } from "@/components/PageTransition";
|
||||
import { BottomNav } from "@/components/BottomNav";
|
||||
import "./globals.css";
|
||||
|
||||
const geistSans = Geist({
|
||||
|
|
@ -39,12 +35,7 @@ export default function RootLayout({
|
|||
return (
|
||||
<html lang="en" suppressHydrationWarning>
|
||||
<body className={`${geistSans.variable} ${geistMono.variable} ${caveat.variable} min-h-full antialiased`}>
|
||||
<ThemeProvider>
|
||||
<FamilyProvider>
|
||||
<PageTransition>{children}</PageTransition>
|
||||
<BottomNav />
|
||||
</FamilyProvider>
|
||||
</ThemeProvider>
|
||||
{children}
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ export default function LoginPage() {
|
|||
}
|
||||
|
||||
// Sign-in success.
|
||||
router.push(data.familyId ? "/" : "/onboarding");
|
||||
router.push(data.familyId ? "/home" : "/onboarding");
|
||||
} catch {
|
||||
setError("Something went wrong");
|
||||
} finally {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import Link from "next/link";
|
|||
import { usePathname } from "next/navigation";
|
||||
|
||||
const TABS = [
|
||||
{ href: "/", icon: "🏡", label: "Home" },
|
||||
{ href: "/home", icon: "🏡", label: "Home" },
|
||||
{ href: "/activity", icon: "📝", label: "Activity" },
|
||||
{ href: "/ai", icon: "🔮", label: "Ask AI" },
|
||||
{ href: "/menu", icon: "☰", label: "Menu" },
|
||||
|
|
@ -20,7 +20,7 @@ export function BottomNav() {
|
|||
return (
|
||||
<nav className="fixed bottom-0 inset-x-0 bg-white dark:bg-gray-900 border-t border-gray-100 dark:border-gray-800 flex justify-around items-center py-2 z-40">
|
||||
{TABS.map(tab => {
|
||||
const isActive = tab.href === "/" ? pathname === "/" : pathname?.startsWith(tab.href);
|
||||
const isActive = pathname?.startsWith(tab.href);
|
||||
return (
|
||||
<Link
|
||||
key={tab.href}
|
||||
|
|
|
|||
34
src/components/marketing/NavAuthButton.tsx
Normal file
34
src/components/marketing/NavAuthButton.tsx
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import Link from "next/link";
|
||||
|
||||
export function NavAuthButton() {
|
||||
const [isAuth, setIsAuth] = useState<boolean | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
fetch("/api/auth/profile")
|
||||
.then(r => setIsAuth(r.ok))
|
||||
.catch(() => setIsAuth(false));
|
||||
}, []);
|
||||
|
||||
if (isAuth === true) {
|
||||
return (
|
||||
<Link
|
||||
href="/home"
|
||||
className="px-4 py-2 bg-rose-500 text-white rounded-full text-sm font-semibold hover:bg-rose-600 transition-colors"
|
||||
>
|
||||
Open Tia →
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Link
|
||||
href="/login"
|
||||
className="px-4 py-2 bg-rose-500 text-white rounded-full text-sm font-semibold hover:bg-rose-600 transition-colors"
|
||||
>
|
||||
Continue with Google
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
|
@ -4,11 +4,18 @@ import type { NextRequest } from "next/server";
|
|||
// Public routes that don't require authentication
|
||||
const publicRoutes = [
|
||||
"/",
|
||||
"/pricing",
|
||||
"/privacy",
|
||||
"/terms",
|
||||
"/login",
|
||||
"/admin-login",
|
||||
"/m",
|
||||
"/invite",
|
||||
"/verify",
|
||||
"/api/auth/signin",
|
||||
"/api/admin/auth",
|
||||
"/api/onboarding",
|
||||
"/api/profile",
|
||||
];
|
||||
|
||||
// Protected API routes that need authentication
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue