Files
property-management-network/components/marketing/how-it-works.tsx
Leon SerfatyandClaude Opus 4.8 857b9a7811 Initial import: property management SaaS + security hardening + admin dashboard
Property Management Network — Next.js 16 (App Router), Better Auth,
Drizzle ORM over PostgreSQL, Stripe, OpenAI, Resend.

Includes:
- Security hardening: access-control/IDOR fixes, TLS-by-default DB layer,
  constant-time cron auth, strict security headers, atomic AI quota gating,
  HTML/email output encoding, demo-backdoor disabled in production.
- Superadmin dashboard at /admin (overview/MRR, server-paginated users with
  ban/impersonate/plan/delete, billing, platform activity + admin audit log,
  AI usage, system health) via the Better Auth admin plugin.
- Seed/migration utility scripts under scripts/.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 20:36:07 -04:00

98 lines
3.8 KiB
TypeScript

"use client"
import { motion } from "framer-motion"
import { Building2, Users, TrendingUp } from "lucide-react"
const STEPS = [
{
step: "01",
icon: Building2,
title: "Add your properties",
body: "Create properties, add units with rent amounts, and upload documents. Takes 3 minutes per property.",
color: "text-indigo-400",
bg: "bg-indigo-500/10 border-indigo-500/30",
shadow: "shadow-indigo-500/20",
},
{
step: "02",
icon: Users,
title: "Invite your tenants",
body: "Add tenant profiles, assign them to units, create leases. Each tenant automatically gets a private portal link.",
color: "text-violet-400",
bg: "bg-violet-500/10 border-violet-500/30",
shadow: "shadow-violet-500/20",
},
{
step: "03",
icon: TrendingUp,
title: "Run on autopilot",
body: "Rent reminders go out automatically. Maintenance gets logged. Lease expiries alert you 60 days early.",
color: "text-emerald-400",
bg: "bg-emerald-500/10 border-emerald-500/30",
shadow: "shadow-emerald-500/20",
},
]
export function HowItWorks() {
return (
<section id="how-it-works" className="relative mx-auto max-w-7xl px-4 sm:px-6 py-16 sm:py-24">
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
className="text-center mb-16"
>
<p className="text-xs font-semibold uppercase tracking-widest text-indigo-400 mb-3">How it works</p>
<h2 className="text-3xl font-bold text-white sm:text-4xl">Up and running in under 10 minutes</h2>
<p className="mt-4 text-white/50 max-w-xl mx-auto">
No training, no onboarding call. Sign up, add your properties, and you're managing.
</p>
</motion.div>
<div className="relative grid grid-cols-1 md:grid-cols-3 gap-8">
{/* Connector line */}
<div className="absolute top-10 left-[calc(16.66%+2rem)] right-[calc(16.66%+2rem)] hidden md:block h-px bg-gradient-to-r from-indigo-500/40 via-violet-500/40 to-emerald-500/40" />
{STEPS.map((step, i) => (
<motion.div
key={step.step}
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5, delay: i * 0.15 }}
className="flex flex-col items-center text-center"
>
<motion.div
whileHover={{ scale: 1.08 }}
transition={{ type: "spring", stiffness: 300 }}
className={`relative z-10 mb-6 flex h-20 w-20 items-center justify-center rounded-2xl border shadow-2xl ${step.bg} ${step.shadow}`}
>
<step.icon className={`h-8 w-8 ${step.color}`} />
<div className="absolute -top-2.5 -right-2.5 flex h-6 w-6 items-center justify-center rounded-full bg-[#09090b] border border-white/10 text-[10px] font-bold text-white/50">
{step.step}
</div>
</motion.div>
<h3 className="text-lg font-semibold text-white mb-3">{step.title}</h3>
<p className="text-sm text-white/50 leading-relaxed max-w-xs">{step.body}</p>
</motion.div>
))}
</div>
<motion.div
initial={{ opacity: 0, y: 16 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ delay: 0.5 }}
className="mt-14 text-center"
>
<a
href="/signup"
className="inline-flex items-center gap-2 rounded-xl bg-indigo-600 px-6 py-3 text-sm font-semibold text-white hover:bg-indigo-500 transition-all hover:shadow-lg hover:shadow-indigo-500/30 hover:-translate-y-0.5"
>
Start for free takes 3 minutes
</a>
</motion.div>
</section>
)
}