Skip to content

Commit 71ac9bf

Browse files
ofershapcursoragent
andcommitted
feat: add Free Version badge with upgrade modal + global cursor:pointer fix
Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 7b9ab7e commit 71ac9bf

3 files changed

Lines changed: 165 additions & 1 deletion

File tree

src/app/globals.css

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,17 @@
1616
color-scheme: dark;
1717
}
1818

19+
button,
20+
[role="button"],
21+
a,
22+
select,
23+
summary,
24+
[type="checkbox"],
25+
[type="radio"],
26+
label[for] {
27+
cursor: pointer;
28+
}
29+
1930
body {
2031
background: var(--background);
2132
color: var(--foreground);

src/app/layout.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { Metadata } from "next";
22
import Image from "next/image";
33
import Link from "next/link";
44
import { NavLinks } from "@/components/nav-links";
5+
import { UpgradeBanner } from "@/components/upgrade-banner";
56
import "./globals.css";
67

78
export const metadata: Metadata = {
@@ -24,7 +25,7 @@ export default function RootLayout({ children }: { children: React.ReactNode })
2425
</Link>
2526
<NavLinks />
2627
</div>
27-
<div className="text-xs text-zinc-500">cursor-usage-tracker</div>
28+
<UpgradeBanner />
2829
</div>
2930
</div>
3031
</nav>

src/components/upgrade-banner.tsx

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
"use client";
2+
3+
import { useState, useEffect } from "react";
4+
import { createPortal } from "react-dom";
5+
6+
const UPGRADE_URL = "https://cursor-usage-tracker.sticklight.app/";
7+
8+
const FEATURES = [
9+
{ title: "Server Deployment", desc: "Production-ready hosting on your infrastructure" },
10+
{ title: "Remote Database", desc: "PostgreSQL, MySQL - connect any remote DB" },
11+
{ title: "Authentication & RBAC", desc: "SSO, role-based access, team permissions" },
12+
{ title: "Onboarding & Support", desc: "Hands-on setup, migration help, priority support" },
13+
{ title: "White-Label Branding", desc: "Your logo, colors, and custom domain" },
14+
];
15+
16+
function Check() {
17+
return (
18+
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" className="mt-0.5 shrink-0">
19+
<circle cx="8" cy="8" r="8" fill="rgb(16 185 129 / 0.15)" />
20+
<path
21+
d="M5 8l2 2 4-4"
22+
stroke="rgb(52 211 153)"
23+
strokeWidth="1.5"
24+
strokeLinecap="round"
25+
strokeLinejoin="round"
26+
/>
27+
</svg>
28+
);
29+
}
30+
31+
function Modal({ onClose }: { onClose: () => void }) {
32+
useEffect(() => {
33+
const handler = (e: KeyboardEvent) => e.key === "Escape" && onClose();
34+
window.addEventListener("keydown", handler);
35+
return () => window.removeEventListener("keydown", handler);
36+
}, [onClose]);
37+
38+
return (
39+
<div
40+
className="fixed inset-0 z-[100] flex items-center justify-center bg-black/70 backdrop-blur-sm animate-[fadeIn_150ms_ease-out]"
41+
onClick={(e) => e.target === e.currentTarget && onClose()}
42+
>
43+
<div className="relative w-full max-w-[420px] mx-4 rounded-2xl border border-zinc-800 bg-zinc-950 shadow-2xl overflow-hidden animate-[scaleIn_200ms_ease-out]">
44+
<div className="h-1 w-full bg-gradient-to-r from-emerald-500 via-emerald-400 to-teal-500" />
45+
46+
<button
47+
onClick={onClose}
48+
className="absolute top-4 right-4 text-zinc-600 hover:text-zinc-300 transition-colors"
49+
aria-label="Close"
50+
>
51+
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
52+
<path
53+
d="M4 4l8 8M12 4l-8 8"
54+
stroke="currentColor"
55+
strokeWidth="1.5"
56+
strokeLinecap="round"
57+
/>
58+
</svg>
59+
</button>
60+
61+
<div className="px-6 pt-6 pb-5">
62+
<div className="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full bg-emerald-500/10 border border-emerald-500/20 mb-4">
63+
<svg width="12" height="12" viewBox="0 0 12 12" fill="none">
64+
<path
65+
d="M6 1l1.5 3.1L11 4.5 8.5 7l.6 3.5L6 8.8 2.9 10.5l.6-3.5L1 4.5l3.5-.4L6 1z"
66+
fill="rgb(52 211 153)"
67+
/>
68+
</svg>
69+
<span className="text-[11px] font-semibold text-emerald-400 tracking-wide uppercase">
70+
Pro
71+
</span>
72+
</div>
73+
74+
<h2 className="text-xl font-bold text-white mb-1.5">Unlock the Full Experience</h2>
75+
<p className="text-sm text-zinc-400 leading-relaxed">
76+
Everything you need to run Cursor Tracker in production
77+
<br />
78+
Deployed, secured, and supported.
79+
</p>
80+
</div>
81+
82+
<div className="px-6 pb-5">
83+
<ul className="space-y-3">
84+
{FEATURES.map((f) => (
85+
<li key={f.title} className="flex items-start gap-3">
86+
<Check />
87+
<div className="flex flex-col">
88+
<span className="text-[13px] font-medium text-zinc-100">{f.title}</span>
89+
<span className="text-[12px] text-zinc-500">{f.desc}</span>
90+
</div>
91+
</li>
92+
))}
93+
</ul>
94+
</div>
95+
96+
<div className="px-6 pb-6 pt-1 space-y-3">
97+
<a
98+
href={UPGRADE_URL}
99+
target="_blank"
100+
rel="noopener noreferrer"
101+
className="group relative block w-full text-center rounded-xl py-3 text-sm font-semibold text-white transition-all overflow-hidden"
102+
>
103+
<span className="absolute inset-0 bg-gradient-to-r from-emerald-600 to-teal-600 group-hover:from-emerald-500 group-hover:to-teal-500 transition-all" />
104+
<span className="absolute inset-0 opacity-0 group-hover:opacity-100 transition-opacity bg-[radial-gradient(circle_at_50%_50%,rgba(255,255,255,0.1),transparent_70%)]" />
105+
<span className="relative flex items-center justify-center gap-2">
106+
Get Started
107+
<svg
108+
width="14"
109+
height="14"
110+
viewBox="0 0 14 14"
111+
fill="none"
112+
className="group-hover:translate-x-0.5 transition-transform"
113+
>
114+
<path
115+
d="M5 3l4 4-4 4"
116+
stroke="currentColor"
117+
strokeWidth="1.5"
118+
strokeLinecap="round"
119+
strokeLinejoin="round"
120+
/>
121+
</svg>
122+
</span>
123+
</a>
124+
<p className="text-center text-[11px] text-zinc-600">
125+
No commitment required · Free consultation included
126+
</p>
127+
</div>
128+
</div>
129+
130+
<style>{`
131+
@keyframes fadeIn { from { opacity: 0 } to { opacity: 1 } }
132+
@keyframes scaleIn { from { opacity: 0; transform: scale(0.95) translateY(8px) } to { opacity: 1; transform: scale(1) translateY(0) } }
133+
`}</style>
134+
</div>
135+
);
136+
}
137+
138+
export function UpgradeBanner() {
139+
const [open, setOpen] = useState(false);
140+
141+
return (
142+
<>
143+
<button
144+
onClick={() => setOpen(true)}
145+
className="px-2 py-0.5 text-[10px] font-medium rounded-full border border-zinc-700 text-zinc-400 hover:text-zinc-200 hover:border-zinc-500 transition-colors"
146+
>
147+
Free Version
148+
</button>
149+
{open && createPortal(<Modal onClose={() => setOpen(false)} />, document.body)}
150+
</>
151+
);
152+
}

0 commit comments

Comments
 (0)