feat: complete TapTrack landing page — Phases 1-10

- Vite + Vue 3 + TypeScript + Tailwind CSS v4 scaffold
- lucide-vue-next icons, Inter font, design tokens
- AppNavbar: sticky, scroll shadow, mobile hamburger
- AppFooter: dark navy, 3 link columns, copyright bar
- HeroSection: pill badge, H1, CTAs, trust row, screenshot
- PrivacyBanner: dark, 3 stat icons (Server/Shield/WifiOff)
- SeeItLiveSection: gate kiosk screenshot showcase
- FeaturesSection: 6-card grid with Lucide icons
- ScreenshotsSection: Student Directory + Reports.jpg cards
- HowItWorksSection: 3 steps + billing card
- SupportSection: 5 support cards + quote card
- ContactSection: form with Formspree, validation, success state
- v-reveal directive: Intersection Observer scroll animations
- SEO meta tags + Open Graph + Twitter Card
- vercel.json SPA rewrite rule
- Build: 100KB JS / 19KB CSS, 0 errors
This commit is contained in:
kevin-asprec
2026-03-17 04:01:44 +08:00
parent 8c37e5bb45
commit 5376d1b1d5
34 changed files with 6619 additions and 0 deletions

View File

@@ -0,0 +1,118 @@
<script setup lang="ts">
import { Wrench, Zap, CircleCheck } from 'lucide-vue-next'
const steps = [
{
num: '1',
title: "We install TapTrack on your school's hardware",
description:
'Our team sets up the on-premises software and NFC reader. You pay a one-time implementation fee and you own the setup.',
},
{
num: '2',
title: 'We enrol your students and train your staff',
description:
'We import your student list, assign NFC ID cards, add parent numbers, and run a full walkthrough with your administrators.',
},
{
num: '3',
title: 'Top up SMS credits monthly as needed',
description:
'No recurring subscription. You purchase prepaid SMS credits when you need them. The system keeps sending alerts as long as your credits last.',
},
]
const billingItems = [
{
icon: Wrench,
iconColor: '#1D4ED8',
iconBg: '#EFF6FF',
title: 'One-Time Implementation Fee',
description: 'Covers installation, student enrolment, NFC reader setup, and staff training. Paid once.',
},
{
icon: Zap,
iconColor: '#059669',
iconBg: '#ECFDF5',
title: 'Prepaid SMS Credits',
description: 'Purchase credits when you need them. Each parent notification consumes one credit. Top up anytime.',
},
]
</script>
<template>
<section id="how-it-works" class="bg-[#F8FAFF]">
<div class="page-container flex flex-col lg:flex-row gap-16 xl:gap-20 py-24">
<!-- Left: Steps -->
<div v-reveal class="flex-1 flex flex-col gap-10 min-w-0">
<div class="flex flex-col gap-2.5">
<span class="text-[#1D4ED8] font-semibold text-[11px] tracking-[2px] uppercase">
How It Works
</span>
<h2 class="text-[#0F172A] font-bold text-[clamp(28px,3vw,38px)] leading-[1.15] max-w-[480px]">
Simple to set up.<br />Simple to run.
</h2>
<p class="text-[#64748B] text-[16px] leading-[1.6] max-w-[460px]">
Our team handles the full installation. Once you're live, the system runs on its own.
</p>
</div>
<div class="flex flex-col gap-8">
<div
v-for="(step, i) in steps"
:key="step.num"
v-reveal="{ delay: i * 100 }"
class="flex items-start gap-5"
>
<div class="w-9 h-9 rounded-full bg-[#1D4ED8] flex items-center justify-center shrink-0 mt-0.5">
<span class="text-white font-bold text-[15px] leading-none">{{ step.num }}</span>
</div>
<div class="flex flex-col gap-1.5 min-w-0">
<h3 class="text-[#0F172A] font-semibold text-[15px] leading-snug">{{ step.title }}</h3>
<p class="text-[#64748B] text-[13px] leading-[1.6]">{{ step.description }}</p>
</div>
</div>
</div>
</div>
<!-- Right: Billing card -->
<div v-reveal="{ delay: 150 }" class="w-full lg:w-[460px] shrink-0">
<div class="bg-white border border-[#E2E8F0] rounded-[20px] p-8 lg:p-10 flex flex-col gap-6">
<h3 class="text-[#0F172A] font-bold text-xl">How billing works</h3>
<p class="text-[#64748B] text-[14px] leading-[1.6]">
Transparent, no surprises. You only pay for what you actually use.
</p>
<div class="flex flex-col">
<div
v-for="(item, i) in billingItems"
:key="item.title"
class="flex items-start gap-3.5 py-4"
:class="i === 0 ? 'border-b border-[#F1F5F9]' : ''"
>
<div
class="w-9 h-9 rounded-lg flex items-center justify-center shrink-0"
:style="{ backgroundColor: item.iconBg }"
>
<component :is="item.icon" :size="18" :stroke-width="1.75" :style="{ color: item.iconColor }" />
</div>
<div class="flex flex-col gap-0.5 min-w-0">
<span class="text-[#0F172A] font-semibold text-[14px]">{{ item.title }}</span>
<span class="text-[#64748B] text-[13px] leading-[1.5]">{{ item.description }}</span>
</div>
</div>
</div>
<div class="flex items-start gap-2.5 bg-[#F0FDF4] rounded-lg px-3.5 py-3">
<CircleCheck :size="16" :stroke-width="2" class="text-[#16A34A] shrink-0 mt-0.5" />
<p class="text-[#15803D] text-[13px] leading-[1.5]">
No monthly subscription fee. No lock-in contract. Pricing shared upon request.
</p>
</div>
</div>
</div>
</div>
</section>
</template>