chore: bump version to 0.1.3 and update @ciphera-net/ui dependency to 0.0.46; refine feature descriptions for clarity and consistency
This commit is contained in:
@@ -4,8 +4,9 @@
|
||||
* @file Features / Product Tour page.
|
||||
*
|
||||
* Comprehensive showcase of everything Pulse offers — designed to convert
|
||||
* visitors who land here from search or internal navigation. Each feature
|
||||
* section uses consistent glass-morphism cards with staggered animations.
|
||||
* visitors who land here from search or internal navigation. Uses mixed
|
||||
* layout styles (cards, inline lists, numbered steps, split sections) to
|
||||
* avoid visual monotony.
|
||||
*/
|
||||
|
||||
import Link from 'next/link'
|
||||
@@ -18,115 +19,89 @@ import {
|
||||
ZapIcon,
|
||||
GlobeIcon,
|
||||
Share2Icon,
|
||||
EyeIcon,
|
||||
ArrowRightIcon,
|
||||
} from '@ciphera-net/ui'
|
||||
|
||||
// * Feature definitions grouped by section
|
||||
const heroFeatures = [
|
||||
// * Pillar features (top 3 cards — matches landing page)
|
||||
const pillars = [
|
||||
{
|
||||
icon: LockIcon,
|
||||
title: 'Privacy First',
|
||||
description:
|
||||
'No cookies, no IP tracking, no fingerprinting. Fully GDPR, CCPA, and PECR compliant out of the box — no cookie banner required.',
|
||||
'No cookies, no IP tracking, no fingerprinting. Fully GDPR, CCPA, and PECR compliant — no cookie banner required.',
|
||||
},
|
||||
{
|
||||
icon: BarChartIcon,
|
||||
title: 'Simple Dashboard',
|
||||
description:
|
||||
'One clear dashboard with everything you need. Page views, unique visitors, referral sources, and top pages — no learning curve.',
|
||||
'One clear dashboard with everything you need. Page views, visitors, referral sources, and top pages — no learning curve.',
|
||||
},
|
||||
{
|
||||
icon: ZapIcon,
|
||||
title: 'Lightweight Script',
|
||||
description:
|
||||
'Less than 1 KB. Our tracking script loads in milliseconds and has zero impact on your Lighthouse score or Core Web Vitals.',
|
||||
'Less than 1 KB. Loads in milliseconds with zero impact on your Lighthouse score or Core Web Vitals.',
|
||||
},
|
||||
]
|
||||
|
||||
const coreCapabilities = [
|
||||
// * Core capabilities — rendered as an icon + text list, NOT cards
|
||||
const capabilities = [
|
||||
{
|
||||
icon: (
|
||||
<svg className="w-6 h-6" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
|
||||
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M3.75 3v11.25A2.25 2.25 0 0 0 6 16.5h2.25M3.75 3h-1.5m1.5 0h16.5m0 0h1.5m-1.5 0v11.25A2.25 2.25 0 0 1 18 16.5h-2.25m-7.5 0h7.5m-7.5 0-1 3m8.5-3 1 3m0 0 .5 1.5m-.5-1.5h-9.5m0 0-.5 1.5m.75-9 3-3 2.148 2.148A12.061 12.061 0 0 1 16.5 7.605" />
|
||||
</svg>
|
||||
),
|
||||
title: 'Real-Time Analytics',
|
||||
description: 'Watch visitors arrive on your site in real time. See active pages, live referrers, and current visitor counts — no delay.',
|
||||
description: 'Watch visitors arrive live. See active pages, referrers, and current visitor counts with zero delay.',
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
<svg className="w-6 h-6" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
|
||||
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M12 3c2.755 0 5.455.232 8.083.678.533.09.917.556.917 1.096v1.044a2.25 2.25 0 0 1-.659 1.591l-5.432 5.432a2.25 2.25 0 0 0-.659 1.591v2.927a2.25 2.25 0 0 1-1.244 2.013L9.75 21v-6.568a2.25 2.25 0 0 0-.659-1.591L3.659 7.409A2.25 2.25 0 0 1 3 5.818V4.774c0-.54.384-1.006.917-1.096A48.32 48.32 0 0 1 12 3Z" />
|
||||
</svg>
|
||||
),
|
||||
title: 'Conversion Funnels',
|
||||
description: 'Define multi-step funnels to see where visitors drop off. Track sign-ups, purchases, or any custom flow with precise conversion rates.',
|
||||
description: 'Define multi-step funnels and see exactly where visitors drop off in your sign-up or checkout flow.',
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
<svg className="w-6 h-6" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
|
||||
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M3 3v1.5M3 21v-6m0 0 2.77-.693a9 9 0 0 1 6.208.682l.108.054a9 9 0 0 0 6.086.71l3.114-.732a48.524 48.524 0 0 1-.005-10.499l-3.11.732a9 9 0 0 1-6.085-.711l-.108-.054a9 9 0 0 0-6.208-.682L3 4.5M3 15V4.5" />
|
||||
</svg>
|
||||
),
|
||||
title: 'Goals & Events',
|
||||
description: 'Set goals for key pages or custom events. Track completions, conversion rates, and revenue attribution — all without cookies.',
|
||||
description: 'Track custom goals, completions, and conversion rates — all without cookies or complex setup.',
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
<svg className="w-6 h-6" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
|
||||
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M13.19 8.688a4.5 4.5 0 0 1 1.242 7.244l-4.5 4.5a4.5 4.5 0 0 1-6.364-6.364l1.757-1.757m13.35-.622 1.757-1.757a4.5 4.5 0 0 0-6.364-6.364l-4.5 4.5a4.5 4.5 0 0 0 1.242 7.244" />
|
||||
</svg>
|
||||
),
|
||||
title: 'UTM Campaign Tracking',
|
||||
description: 'Automatically parse UTM parameters to attribute traffic to campaigns, sources, and mediums. Built-in UTM link builder included.',
|
||||
description: 'Automatically parse UTM parameters. Built-in link builder for campaigns, sources, and mediums.',
|
||||
},
|
||||
{
|
||||
icon: Share2Icon,
|
||||
title: 'Shared Dashboards',
|
||||
description: 'Generate a public link to share your analytics with clients, teammates, or the world. No login required for viewers.',
|
||||
description: 'Generate a public link to share analytics with clients or teammates — no login required.',
|
||||
},
|
||||
{
|
||||
icon: GlobeIcon,
|
||||
title: 'Geographic Insights',
|
||||
description: 'See where your visitors are from — country, region, and city level. All derived at request time; IP addresses are never stored.',
|
||||
description: 'Country, region, and city-level breakdowns. IPs are never stored — derived at request time only.',
|
||||
},
|
||||
]
|
||||
|
||||
const technicalFeatures = [
|
||||
{
|
||||
title: 'One-Line Installation',
|
||||
description: 'Add a single <script> tag to your site. Works with any framework — React, Vue, Angular, WordPress, Shopify, and 70+ more.',
|
||||
link: { href: '/integrations', label: 'View all integrations' },
|
||||
},
|
||||
{
|
||||
title: 'Open Source',
|
||||
description: 'Our frontend is fully open source on GitHub. Inspect the code, contribute, or self-host. Transparency is a feature.',
|
||||
link: { href: 'https://github.com/ciphera-net/pulse', label: 'View on GitHub', external: true },
|
||||
},
|
||||
{
|
||||
title: 'Swiss Infrastructure',
|
||||
description: 'All data is processed and stored in Switzerland, one of the strongest privacy jurisdictions in the world.',
|
||||
},
|
||||
{
|
||||
title: 'No Cookie Banners',
|
||||
description: 'Since we don\'t use cookies, you can skip the annoying consent popups entirely. Better UX for your visitors.',
|
||||
},
|
||||
{
|
||||
title: '100% Data Ownership',
|
||||
description: 'Your analytics data belongs to you, not us. We never sell, share, or mine your data for advertising.',
|
||||
},
|
||||
{
|
||||
title: 'Bot & Spam Filtering',
|
||||
description: 'Automatic exclusion of bots, crawlers, and data-center traffic. Your metrics reflect real human visitors.',
|
||||
},
|
||||
]
|
||||
|
||||
const deviceInsights = [
|
||||
{ label: 'Browsers', description: 'Chrome, Firefox, Safari, Edge, and more' },
|
||||
{ label: 'Operating Systems', description: 'Windows, macOS, Linux, iOS, Android' },
|
||||
{ label: 'Device Types', description: 'Desktop, mobile, and tablet breakdowns' },
|
||||
{ label: 'Screen Resolutions', description: 'Responsive design intelligence' },
|
||||
// * Trust signals — rendered as compact inline items, NOT cards
|
||||
const trustSignals = [
|
||||
{ label: 'Open Source', detail: 'Frontend fully on GitHub' },
|
||||
{ label: 'Swiss Infrastructure', detail: 'Data processed in Switzerland' },
|
||||
{ label: 'No Cookie Banners', detail: 'Skip consent popups entirely' },
|
||||
{ label: '100% Data Ownership', detail: 'We never sell or share your data' },
|
||||
{ label: 'Bot & Spam Filtering', detail: 'Automatic exclusion of non-human traffic' },
|
||||
{ label: '75+ Integrations', detail: 'React, Vue, WordPress, Shopify & more' },
|
||||
]
|
||||
|
||||
export default function FeaturesPage() {
|
||||
@@ -163,9 +138,9 @@ export default function FeaturesPage() {
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
{/* * ─── PILLAR CARDS (Privacy, Dashboard, Lightweight) ─── */}
|
||||
<div className="grid md:grid-cols-3 gap-6 text-left mb-24">
|
||||
{heroFeatures.map((feature, i) => (
|
||||
{/* * ─── PILLAR CARDS (3 glass cards — matches landing page) ─── */}
|
||||
<div className="grid md:grid-cols-3 gap-6 text-left mb-28">
|
||||
{pillars.map((feature, i) => (
|
||||
<motion.div
|
||||
key={feature.title}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
@@ -187,89 +162,56 @@ export default function FeaturesPage() {
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* * ─── CORE CAPABILITIES ─── */}
|
||||
{/* * ─── CORE CAPABILITIES (icon list — NO cards) ─── */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5 }}
|
||||
className="text-center mb-12"
|
||||
className="mb-28"
|
||||
>
|
||||
<h2 className="text-3xl md:text-4xl font-bold text-neutral-900 dark:text-white mb-4">
|
||||
Powerful analytics, <span className="gradient-text">simplified</span>
|
||||
</h2>
|
||||
<p className="text-lg text-neutral-600 dark:text-neutral-400 max-w-2xl mx-auto">
|
||||
Everything from real-time dashboards to conversion funnels — without the bloat.
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6 mb-24">
|
||||
{coreCapabilities.map((cap, i) => (
|
||||
<motion.div
|
||||
key={cap.title}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: i * 0.08 }}
|
||||
className="group relative p-8 bg-white/50 dark:bg-neutral-900/50 backdrop-blur-sm border border-neutral-200 dark:border-neutral-800 rounded-2xl hover:border-brand-orange/50 dark:hover:border-brand-orange/50 transition-all duration-300 hover:-translate-y-1 hover:shadow-xl"
|
||||
>
|
||||
<div className="w-12 h-12 rounded-xl bg-brand-orange/10 flex items-center justify-center mb-6 text-brand-orange group-hover:scale-110 transition-transform duration-300">
|
||||
{typeof cap.icon === 'object' ? cap.icon : <cap.icon className="w-6 h-6" />}
|
||||
</div>
|
||||
<h3 className="text-lg font-bold text-neutral-900 dark:text-white mb-2">
|
||||
{cap.title}
|
||||
</h3>
|
||||
<p className="text-neutral-600 dark:text-neutral-400 text-sm leading-relaxed">
|
||||
{cap.description}
|
||||
</p>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* * ─── DEVICE & TECH INSIGHTS ─── */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5 }}
|
||||
className="mb-24"
|
||||
>
|
||||
<div className="text-center mb-12">
|
||||
<div className="text-center mb-14">
|
||||
<h2 className="text-3xl md:text-4xl font-bold text-neutral-900 dark:text-white mb-4">
|
||||
Know your audience
|
||||
Powerful analytics, <span className="gradient-text">simplified</span>
|
||||
</h2>
|
||||
<p className="text-lg text-neutral-600 dark:text-neutral-400 max-w-2xl mx-auto">
|
||||
Understand which devices, browsers, and screen sizes your visitors use — so you can build for what matters.
|
||||
Everything from real-time dashboards to conversion funnels — without the bloat.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid sm:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
{deviceInsights.map((item, i) => (
|
||||
<div className="grid md:grid-cols-2 gap-x-16 gap-y-10 max-w-4xl mx-auto">
|
||||
{capabilities.map((cap, i) => (
|
||||
<motion.div
|
||||
key={item.label}
|
||||
initial={{ opacity: 0, y: 15 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
key={cap.title}
|
||||
initial={{ opacity: 0, x: i % 2 === 0 ? -15 : 15 }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.4, delay: i * 0.08 }}
|
||||
className="p-6 bg-white/50 dark:bg-neutral-900/50 backdrop-blur-sm border border-neutral-200 dark:border-neutral-800 rounded-xl text-center"
|
||||
className="flex gap-4"
|
||||
>
|
||||
<div className="w-10 h-10 rounded-lg bg-brand-orange/10 flex items-center justify-center mx-auto mb-4 text-brand-orange">
|
||||
<EyeIcon className="w-5 h-5" />
|
||||
<div className="w-10 h-10 rounded-lg bg-brand-orange/10 flex items-center justify-center shrink-0 text-brand-orange mt-0.5">
|
||||
{typeof cap.icon === 'object' ? cap.icon : <cap.icon className="w-5 h-5" />}
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-bold text-neutral-900 dark:text-white mb-1">
|
||||
{cap.title}
|
||||
</h3>
|
||||
<p className="text-sm text-neutral-600 dark:text-neutral-400 leading-relaxed">
|
||||
{cap.description}
|
||||
</p>
|
||||
</div>
|
||||
<h3 className="font-bold text-neutral-900 dark:text-white mb-1">{item.label}</h3>
|
||||
<p className="text-sm text-neutral-500 dark:text-neutral-400">{item.description}</p>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* * ─── CONTENT ANALYTICS HIGHLIGHT ─── */}
|
||||
{/* * ─── CONTENT ANALYTICS (split layout — visual variety) ─── */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5 }}
|
||||
className="mb-24 p-10 md:p-14 bg-white/50 dark:bg-neutral-900/50 backdrop-blur-sm border border-neutral-200 dark:border-neutral-800 rounded-2xl"
|
||||
className="mb-28 p-10 md:p-14 bg-white/50 dark:bg-neutral-900/50 backdrop-blur-sm border border-neutral-200 dark:border-neutral-800 rounded-2xl"
|
||||
>
|
||||
<div className="grid md:grid-cols-2 gap-10 items-center">
|
||||
<div>
|
||||
@@ -280,7 +222,13 @@ export default function FeaturesPage() {
|
||||
See which pages drive the most traffic, where visitors enter your site, and where they leave. Use data to double down on what works.
|
||||
</p>
|
||||
<ul className="space-y-3">
|
||||
{['Top pages by views & unique visitors', 'Entry pages — first impressions that work', 'Exit pages — where you lose attention', 'Referral sources — where traffic comes from'].map((item) => (
|
||||
{[
|
||||
'Top pages by views & unique visitors',
|
||||
'Entry pages — first impressions that work',
|
||||
'Exit pages — where you lose attention',
|
||||
'Referral sources — where traffic comes from',
|
||||
'Browser, OS & device breakdowns',
|
||||
].map((item) => (
|
||||
<li key={item} className="flex items-start gap-3 text-sm text-neutral-600 dark:text-neutral-400">
|
||||
<svg className="w-5 h-5 text-brand-orange shrink-0 mt-0.5" fill="none" viewBox="0 0 24 24" strokeWidth={2} stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="m4.5 12.75 6 6 9-13.5" />
|
||||
@@ -303,7 +251,7 @@ export default function FeaturesPage() {
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.4, delay: i * 0.1 }}
|
||||
className="p-4 bg-neutral-50 dark:bg-neutral-800/50 rounded-xl border border-neutral-200 dark:border-neutral-700"
|
||||
className="p-4 bg-neutral-50 dark:bg-neutral-800/50 rounded-xl"
|
||||
>
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<span className="text-sm font-medium text-neutral-900 dark:text-white truncate mr-4">
|
||||
@@ -328,72 +276,71 @@ export default function FeaturesPage() {
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* * ─── TECHNICAL & TRUST FEATURES ─── */}
|
||||
{/* * ─── TRUST SIGNALS (compact grid — NO card borders) ─── */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5 }}
|
||||
className="text-center mb-12"
|
||||
>
|
||||
<h2 className="text-3xl md:text-4xl font-bold text-neutral-900 dark:text-white mb-4">
|
||||
Built for trust
|
||||
</h2>
|
||||
<p className="text-lg text-neutral-600 dark:text-neutral-400 max-w-2xl mx-auto">
|
||||
Open source, Swiss hosted, and designed to keep your visitors' data where it belongs — with them.
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6 mb-24">
|
||||
{technicalFeatures.map((feat, i) => (
|
||||
<motion.div
|
||||
key={feat.title}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: i * 0.08 }}
|
||||
className="p-8 bg-white/50 dark:bg-neutral-900/50 backdrop-blur-sm border border-neutral-200 dark:border-neutral-800 rounded-2xl"
|
||||
>
|
||||
<h3 className="text-lg font-bold text-neutral-900 dark:text-white mb-2">
|
||||
{feat.title}
|
||||
</h3>
|
||||
<p className="text-neutral-600 dark:text-neutral-400 text-sm leading-relaxed mb-4">
|
||||
{feat.description}
|
||||
</p>
|
||||
{feat.link && (
|
||||
feat.link.external ? (
|
||||
<a
|
||||
href={feat.link.href}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center gap-1 text-sm font-medium text-brand-orange hover:underline"
|
||||
>
|
||||
{feat.link.label}
|
||||
<ArrowRightIcon className="w-4 h-4" />
|
||||
</a>
|
||||
) : (
|
||||
<Link
|
||||
href={feat.link.href}
|
||||
className="inline-flex items-center gap-1 text-sm font-medium text-brand-orange hover:underline"
|
||||
>
|
||||
{feat.link.label}
|
||||
<ArrowRightIcon className="w-4 h-4" />
|
||||
</Link>
|
||||
)
|
||||
)}
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* * ─── HOW IT WORKS ─── */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5 }}
|
||||
className="mb-24"
|
||||
className="mb-28"
|
||||
>
|
||||
<div className="text-center mb-12">
|
||||
<h2 className="text-3xl md:text-4xl font-bold text-neutral-900 dark:text-white mb-4">
|
||||
Built for trust
|
||||
</h2>
|
||||
<p className="text-lg text-neutral-600 dark:text-neutral-400 max-w-2xl mx-auto">
|
||||
Open source, Swiss hosted, and designed to keep your visitors' data where it belongs.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid sm:grid-cols-2 lg:grid-cols-3 gap-x-12 gap-y-6 max-w-4xl mx-auto">
|
||||
{trustSignals.map((signal, i) => (
|
||||
<motion.div
|
||||
key={signal.label}
|
||||
initial={{ opacity: 0 }}
|
||||
whileInView={{ opacity: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.3, delay: i * 0.06 }}
|
||||
className="flex items-start gap-3 py-2"
|
||||
>
|
||||
<svg className="w-5 h-5 text-brand-orange shrink-0 mt-0.5" fill="none" viewBox="0 0 24 24" strokeWidth={2} stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="m4.5 12.75 6 6 9-13.5" />
|
||||
</svg>
|
||||
<div>
|
||||
<span className="font-semibold text-neutral-900 dark:text-white text-sm">{signal.label}</span>
|
||||
<p className="text-xs text-neutral-500 dark:text-neutral-400 mt-0.5">{signal.detail}</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="flex justify-center gap-6 mt-8">
|
||||
<Link
|
||||
href="/integrations"
|
||||
className="inline-flex items-center gap-1 text-sm font-medium text-brand-orange hover:underline"
|
||||
>
|
||||
View all integrations <ArrowRightIcon className="w-4 h-4" />
|
||||
</Link>
|
||||
<a
|
||||
href="https://github.com/ciphera-net/pulse"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center gap-1 text-sm font-medium text-brand-orange hover:underline"
|
||||
>
|
||||
View on GitHub <ArrowRightIcon className="w-4 h-4" />
|
||||
</a>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* * ─── HOW IT WORKS (numbered steps — inline, NO card borders) ─── */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5 }}
|
||||
className="mb-28"
|
||||
>
|
||||
<div className="text-center mb-14">
|
||||
<h2 className="text-3xl md:text-4xl font-bold text-neutral-900 dark:text-white mb-4">
|
||||
Up and running in <span className="gradient-text">3 minutes</span>
|
||||
</h2>
|
||||
@@ -402,41 +349,34 @@ export default function FeaturesPage() {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid md:grid-cols-3 gap-6">
|
||||
<div className="flex flex-col md:flex-row items-start md:items-center justify-center gap-8 md:gap-6 max-w-3xl mx-auto">
|
||||
{[
|
||||
{
|
||||
step: '1',
|
||||
title: 'Create your site',
|
||||
description: 'Sign up and add your domain. We verify ownership automatically.',
|
||||
},
|
||||
{
|
||||
step: '2',
|
||||
title: 'Add the script',
|
||||
description: 'Paste one <script> tag before your closing </head>. That\'s it.',
|
||||
},
|
||||
{
|
||||
step: '3',
|
||||
title: 'Watch the data flow',
|
||||
description: 'Visit your dashboard to see real-time analytics. No waiting period.',
|
||||
},
|
||||
{ step: '1', title: 'Create your site', desc: 'Sign up and add your domain.' },
|
||||
{ step: '2', title: 'Add the script', desc: 'Paste one <script> tag.' },
|
||||
{ step: '3', title: 'Watch the data flow', desc: 'Real-time analytics, instantly.' },
|
||||
].map((s, i) => (
|
||||
<motion.div
|
||||
key={s.step}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
initial={{ opacity: 0, y: 15 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: i * 0.15 }}
|
||||
className="relative p-8 bg-white/50 dark:bg-neutral-900/50 backdrop-blur-sm border border-neutral-200 dark:border-neutral-800 rounded-2xl text-center"
|
||||
transition={{ duration: 0.4, delay: i * 0.15 }}
|
||||
className="flex items-center gap-4 flex-1"
|
||||
>
|
||||
<div className="w-10 h-10 rounded-full bg-brand-orange text-white font-bold text-lg flex items-center justify-center mx-auto mb-6">
|
||||
<div className="w-10 h-10 rounded-full bg-brand-orange text-white font-bold text-lg flex items-center justify-center shrink-0">
|
||||
{s.step}
|
||||
</div>
|
||||
<h3 className="text-lg font-bold text-neutral-900 dark:text-white mb-2">
|
||||
{s.title}
|
||||
</h3>
|
||||
<p className="text-neutral-600 dark:text-neutral-400 text-sm leading-relaxed">
|
||||
{s.description}
|
||||
</p>
|
||||
<div>
|
||||
<h3 className="font-bold text-neutral-900 dark:text-white text-sm">
|
||||
{s.title}
|
||||
</h3>
|
||||
<p className="text-xs text-neutral-500 dark:text-neutral-400">
|
||||
{s.desc}
|
||||
</p>
|
||||
</div>
|
||||
{i < 2 && (
|
||||
<ArrowRightIcon className="w-5 h-5 text-neutral-300 dark:text-neutral-600 shrink-0 hidden md:block" />
|
||||
)}
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
12
package-lock.json
generated
12
package-lock.json
generated
@@ -1,14 +1,14 @@
|
||||
{
|
||||
"name": "pulse-frontend",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.3",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "pulse-frontend",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.3",
|
||||
"dependencies": {
|
||||
"@ciphera-net/ui": "^0.0.45",
|
||||
"@ciphera-net/ui": "^0.0.46",
|
||||
"@ducanh2912/next-pwa": "^10.2.9",
|
||||
"axios": "^1.13.2",
|
||||
"country-flag-icons": "^1.6.4",
|
||||
@@ -1467,9 +1467,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@ciphera-net/ui": {
|
||||
"version": "0.0.45",
|
||||
"resolved": "https://npm.pkg.github.com/download/@ciphera-net/ui/0.0.45/de60e0da8e1c78ea906d49fdc85cd7d7dd163348",
|
||||
"integrity": "sha512-KvrNKb9NzLMztB75h94opaaUp9RG43QW7GCRcVX+xGT8EFmrXi/N2h2kpjHZV652H/Cz1EXfcDA0hzoq/+wJXA==",
|
||||
"version": "0.0.46",
|
||||
"resolved": "https://npm.pkg.github.com/download/@ciphera-net/ui/0.0.46/852ce8d0289505c3d3f625a0f076bfb3fb03ef9f",
|
||||
"integrity": "sha512-ZYu1u07B1ROYFYFznWQk2sShSqhFpPKNPCBUjqqElTP5hMYX0jVAnVIzqXUugdLl7E8luwvL6Lx+dOaN4oPORg==",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-icons": "^1.3.0",
|
||||
"clsx": "^2.1.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "pulse-frontend",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.3",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
@@ -10,7 +10,7 @@
|
||||
"type-check": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ciphera-net/ui": "^0.0.45",
|
||||
"@ciphera-net/ui": "^0.0.46",
|
||||
"@ducanh2912/next-pwa": "^10.2.9",
|
||||
"axios": "^1.13.2",
|
||||
"country-flag-icons": "^1.6.4",
|
||||
|
||||
Reference in New Issue
Block a user