Files
qrknit/landing.html
2026-03-06 10:17:59 -06:00

1062 lines
59 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<script>
(function(){var t=localStorage.getItem('qrknit-theme');if(t&&t!=='default')document.documentElement.setAttribute('data-theme',t);})();
</script>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>QRknit — URL Shortener & QR Code Generator with Analytics</title>
<meta name="description" content="QRknit is a fully managed URL shortener and QR code generator with deep click analytics, custom domains, team management, and a powerful API. Start free — no server required.">
<meta name="keywords" content="URL shortener, QR code generator, link analytics, custom short links, QR codes, link management, click tracking, branded links, short URL">
<meta name="author" content="QRknit">
<meta name="robots" content="index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1">
<link rel="canonical" href="https://qrknit.com/">
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website">
<meta property="og:url" content="https://qrknit.com/">
<meta property="og:site_name" content="QRknit">
<meta property="og:title" content="QRknit — URL Shortener & QR Code Generator with Analytics">
<meta property="og:description" content="Shorten links, generate QR codes, and track every click with deep analytics. Custom domains, team management, and a powerful API — all in one platform.">
<meta property="og:image" content="https://qrknit.com/static/og-image.png">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta property="og:image:alt" content="QRknit — URL Shortener and QR Code Generator">
<meta property="og:locale" content="en_US">
<!-- Twitter / X Card -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:site" content="@qrknit">
<meta name="twitter:creator" content="@qrknit">
<meta name="twitter:title" content="QRknit — URL Shortener & QR Code Generator with Analytics">
<meta name="twitter:description" content="Shorten links, generate QR codes, and track every click with deep analytics. Custom domains, team management, and a powerful API.">
<meta name="twitter:image" content="https://qrknit.com/static/og-image.png">
<meta name="twitter:image:alt" content="QRknit — URL Shortener and QR Code Generator">
<!-- Favicon -->
<link rel="icon" type="image/png" href="/static/qk-ico.png">
<link rel="apple-touch-icon" href="/static/qk-ico.png">
<!-- JSON-LD Structured Data -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@graph": [
{
"@type": "WebSite",
"@id": "https://qrknit.com/#website",
"url": "https://qrknit.com/",
"name": "QRknit",
"description": "Fully managed URL shortener and QR code generator with click analytics, custom domains, and team management.",
"publisher": { "@id": "https://qrknit.com/#organization" },
"potentialAction": {
"@type": "SearchAction",
"target": { "@type": "EntryPoint", "urlTemplate": "https://qrknit.com/app?q={search_term_string}" },
"query-input": "required name=search_term_string"
}
},
{
"@type": "Organization",
"@id": "https://qrknit.com/#organization",
"name": "QRknit",
"url": "https://qrknit.com/",
"logo": {
"@type": "ImageObject",
"url": "https://qrknit.com/static/qk-ico.png",
"width": 512,
"height": 512
},
"sameAs": []
},
{
"@type": "SoftwareApplication",
"@id": "https://qrknit.com/#software",
"name": "QRknit",
"url": "https://qrknit.com/",
"applicationCategory": "BusinessApplication",
"operatingSystem": "Web",
"description": "A fully managed URL shortener with QR code generation and deep click analytics. Features custom domains, team management, bulk operations, CSV export, and a REST API.",
"offers": [
{
"@type": "Offer",
"name": "Free Plan",
"price": "0",
"priceCurrency": "USD",
"description": "10 links, 1,000 clicks/month, 7-day analytics"
},
{
"@type": "Offer",
"name": "Starter Plan",
"price": "9",
"priceCurrency": "USD",
"description": "500 links, 50,000 clicks/month, 30-day analytics"
},
{
"@type": "Offer",
"name": "Pro Plan",
"price": "39",
"priceCurrency": "USD",
"description": "Unlimited links, 500,000 clicks/month, 90-day analytics"
},
{
"@type": "Offer",
"name": "Team Plan",
"price": "79",
"priceCurrency": "USD",
"description": "Unlimited links, unlimited clicks, 90-day analytics, team management"
}
],
"featureList": [
"URL shortening with custom codes",
"QR code generation with customization",
"Click analytics with device, browser, country breakdown",
"Custom short domains",
"Team and organization management",
"Bulk link operations",
"CSV and JSON export",
"REST API access",
"Link expiration and scheduling",
"Tag-based link organization"
],
"screenshot": "https://qrknit.com/static/og-image.png",
"publisher": { "@id": "https://qrknit.com/#organization" }
}
]
}
</script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&family=Share+Tech+Mono&family=Barlow:wght@300;400;500;600&display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--bg: #0a0a0f;
--surface: #111118;
--surface2: #181824;
--surface3: #1e1e2e;
--border: rgba(255,255,255,0.07);
--accent: #00b8d4;
--accent-rgb: 0,184,212;
--accent-grad: #1a52c8;
--accent-grad-rgb: 26,82,200;
--accent2: #ff5e62;
--accent3: #7c6af7;
--accent3-rgb: 124,106,247;
--accent3-light: #a89cf7;
--text: #f0f0f8;
--muted: #808098;
--radius: 12px;
--font-head: 'Orbitron', sans-serif;
--font-mono: 'Share Tech Mono', monospace;
--font-body: 'Barlow', sans-serif;
}
[data-theme="gold"] { --accent:#f5a623; --accent-rgb:245,166,35; --accent-grad:#c47f17; --accent-grad-rgb:196,127,23; --accent3:#d4a847; --accent3-rgb:212,168,71; --accent3-light:#e8c96a; }
[data-theme="green"] { --accent:#00c896; --accent-rgb:0,200,150; --accent-grad:#007a58; --accent-grad-rgb:0,122,88; --accent3:#4caf7a; --accent3-rgb:76,175,122; --accent3-light:#7fd9a4; }
[data-theme="red"] { --accent:#ff4757; --accent-rgb:255,71,87; --accent-grad:#c0392b; --accent-grad-rgb:192,57,43; --accent3:#ff6b7a; --accent3-rgb:255,107,122; --accent3-light:#ff9daa; }
html { scroll-behavior: smooth; }
body { background:var(--bg); color:var(--text); font-family:var(--font-body); font-size:15px; line-height:1.6; min-height:100vh; overflow-x:hidden; }
body::before {
content:''; position:fixed; inset:0; pointer-events:none; z-index:0;
background:
radial-gradient(ellipse 90% 60% at 15% -5%, rgba(var(--accent-rgb),.09) 0%, transparent 60%),
radial-gradient(ellipse 60% 50% at 90% 90%, rgba(var(--accent-grad-rgb),.08) 0%, transparent 55%),
radial-gradient(ellipse 50% 50% at 50% 50%, rgba(var(--accent-rgb),.03) 0%, transparent 60%);
}
/* ── HEADER ── */
header {
position:sticky; top:0; z-index:200;
display:flex; align-items:center; justify-content:space-between;
padding:0 40px; height:64px;
background:rgba(10,10,15,.88); backdrop-filter:blur(20px);
border-bottom:1px solid var(--border);
}
.logo { font-family:var(--font-head); font-size:18px; font-weight:700; letter-spacing:.04em; text-decoration:none; color:var(--text); display:inline-flex; align-items:center; gap:8px; }
.logo-dot-text { color:var(--accent); text-shadow:0 0 12px var(--accent); }
.logo-alwisp { text-transform:uppercase; }
.logo-icon-wrap { position:relative; display:inline-flex; align-items:center; flex-shrink:0; isolation:isolate; }
.logo-icon { height:26px; width:26px; object-fit:contain; border-radius:3px; display:block; }
.logo-icon-tint { position:absolute; inset:0; background:var(--accent); mix-blend-mode:hue; border-radius:3px; pointer-events:none; }
.header-right { display:flex; align-items:center; gap:12px; }
.theme-picker { display:flex; align-items:center; gap:6px; padding:0 4px; }
.theme-swatch { width:14px; height:14px; border-radius:50%; border:2px solid transparent; cursor:pointer; padding:0; transition:transform .2s,border-color .2s; }
.theme-swatch:hover { transform:scale(1.2); }
.theme-swatch.active { border-color:var(--text); transform:scale(1.15); }
.theme-swatch[data-theme="default"] { background:linear-gradient(135deg,#00b8d4,#1a52c8); }
.theme-swatch[data-theme="gold"] { background:linear-gradient(135deg,#f5a623,#c47f17); }
.theme-swatch[data-theme="green"] { background:linear-gradient(135deg,#00c896,#007a58); }
.theme-swatch[data-theme="red"] { background:linear-gradient(135deg,#ff4757,#c0392b); }
/* ── BUTTONS ── */
.btn-primary { background:linear-gradient(135deg,var(--accent),var(--accent-grad)); color:#fff; border:none; cursor:pointer; font-family:var(--font-head); font-weight:700; font-size:12px; letter-spacing:.04em; padding:12px 22px; border-radius:var(--radius); transition:all .2s; white-space:nowrap; text-decoration:none; display:inline-flex; align-items:center; gap:6px; }
.btn-primary:hover { transform:translateY(-1px); box-shadow:0 8px 24px rgba(var(--accent-rgb),.35); }
.btn-outline { background:transparent; border:1px solid var(--border); color:var(--text); font-family:var(--font-mono); font-size:12px; padding:11px 20px; border-radius:var(--radius); cursor:pointer; transition:all .2s; text-decoration:none; display:inline-flex; align-items:center; gap:6px; white-space:nowrap; }
.btn-outline:hover { border-color:rgba(var(--accent-rgb),.5); color:var(--accent); }
/* ── SECTIONS ── */
section { position:relative; z-index:1; }
.section-inner { max-width:1080px; margin:0 auto; padding:0 40px; }
/* ── HERO ── */
.hero { text-align:center; padding:110px 40px 80px; position:relative; z-index:1; overflow:hidden; }
.hero-eyebrow { font-family:var(--font-mono); font-size:11px; letter-spacing:.16em; color:var(--accent); text-transform:uppercase; margin-bottom:24px; }
.hero h1 {
font-family:var(--font-head); font-weight:900; line-height:1.05;
font-size:clamp(38px,7vw,82px); letter-spacing:.04em; margin-bottom:28px;
}
.hero-accent { background:linear-gradient(135deg,var(--accent) 0%,var(--accent-grad) 100%); -webkit-background-clip:text; -webkit-text-fill-color:transparent; background-clip:text; }
.hero-sub { font-size:clamp(15px,2vw,19px); color:var(--muted); max-width:600px; margin:0 auto 44px; font-weight:300; line-height:1.7; }
.hero-cta { display:flex; align-items:center; justify-content:center; gap:14px; flex-wrap:wrap; margin-bottom:70px; }
.hero-note { font-family:var(--font-mono); font-size:11px; color:var(--muted); letter-spacing:.06em; }
.hero-note span { color:var(--accent); }
/* hero glow ring */
.hero::after {
content:''; position:absolute; left:50%; top:60px; transform:translateX(-50%);
width:600px; height:600px; border-radius:50%;
background:radial-gradient(ellipse, rgba(var(--accent-rgb),.07) 0%, transparent 70%);
pointer-events:none; z-index:-1;
}
/* ── FEATURE GRID ── */
.feature-section { padding:80px 0 90px; }
.section-label { font-family:var(--font-mono); font-size:10px; letter-spacing:.18em; color:var(--accent); text-transform:uppercase; margin-bottom:12px; text-align:center; }
.section-title { font-family:var(--font-head); font-size:clamp(22px,3vw,32px); font-weight:700; text-align:center; margin-bottom:56px; }
.feature-grid { display:grid; grid-template-columns:repeat(3,1fr); gap:18px; }
.feature-card {
background:var(--surface); border:1px solid var(--border); border-radius:16px;
padding:28px; position:relative; overflow:hidden; transition:border-color .25s, transform .25s;
}
.feature-card::before {
content:''; position:absolute; top:0; left:0; right:0; height:2px;
background:linear-gradient(90deg,var(--accent),transparent);
}
.feature-card:hover { border-color:rgba(var(--accent-rgb),.25); transform:translateY(-3px); }
.feature-icon { width:40px; height:40px; border-radius:10px; background:rgba(var(--accent-rgb),.1); border:1px solid rgba(var(--accent-rgb),.2); display:flex; align-items:center; justify-content:center; margin-bottom:18px; flex-shrink:0; }
.feature-icon svg { color:var(--accent); }
.feature-card h3 { font-family:var(--font-head); font-size:13px; font-weight:700; letter-spacing:.04em; margin-bottom:10px; }
.feature-card p { font-size:13px; color:var(--muted); line-height:1.65; }
.feature-tag { display:inline-block; font-family:var(--font-mono); font-size:9px; letter-spacing:.08em; color:var(--accent3-light); background:rgba(var(--accent3-rgb),.1); border:1px solid rgba(var(--accent3-rgb),.2); border-radius:4px; padding:1px 7px; margin-top:14px; }
/* ── ANALYTICS SHOWCASE ── */
.analytics-section { padding:80px 0 90px; background:linear-gradient(180deg, transparent 0%, rgba(var(--accent-rgb),.03) 40%, transparent 100%); }
.analytics-split { display:grid; grid-template-columns:1fr 1fr; gap:60px; align-items:center; }
.analytics-copy .section-label { text-align:left; }
.analytics-copy .section-title { text-align:left; margin-bottom:20px; }
.analytics-copy p { color:var(--muted); font-size:14px; line-height:1.75; margin-bottom:16px; }
.analytics-list { list-style:none; margin:24px 0 32px; display:flex; flex-direction:column; gap:10px; }
.analytics-list li { font-family:var(--font-mono); font-size:12px; color:var(--text); display:flex; align-items:center; gap:10px; }
.analytics-list li::before { content:''; width:6px; height:6px; border-radius:50%; background:var(--accent); flex-shrink:0; }
/* mock analytics panel */
.mock-panel {
background:var(--surface); border:1px solid var(--border); border-radius:16px;
padding:20px; box-shadow:0 32px 80px rgba(0,0,0,.5);
}
.mock-panel-title { font-family:var(--font-mono); font-size:10px; color:var(--muted); letter-spacing:.1em; margin-bottom:14px; }
.mock-chart { display:flex; align-items:flex-end; gap:3px; height:70px; margin-bottom:16px; }
.mock-bar { border-radius:2px 2px 0 0; flex:1; min-width:4px; transition:opacity .3s; }
.mock-heatmap { display:grid; grid-template-columns:24px repeat(12,1fr); gap:2px; margin-bottom:14px; }
.mock-hm-label { font-family:var(--font-mono); font-size:7px; color:var(--muted); display:flex; align-items:center; }
.mock-hm-cell { height:10px; border-radius:2px; }
.mock-stats { display:grid; grid-template-columns:repeat(3,1fr); gap:8px; }
.mock-stat { background:var(--surface2); border:1px solid var(--border); border-radius:8px; padding:10px; }
.mock-stat-val { font-family:var(--font-head); font-size:16px; font-weight:700; color:var(--accent); line-height:1; margin-bottom:3px; }
.mock-stat-lbl { font-family:var(--font-mono); font-size:8px; color:var(--muted); letter-spacing:.06em; }
/* ── LINKS DEMO ── */
.demo-section { padding:80px 0 90px; background:linear-gradient(180deg, transparent 0%, rgba(var(--accent3-rgb),.03) 40%, transparent 100%); }
.demo-split { display:grid; grid-template-columns:1fr 1fr; gap:60px; align-items:center; }
.demo-copy .section-label { text-align:left; }
.demo-copy .section-title { text-align:left; margin-bottom:20px; }
.demo-copy p { color:var(--muted); font-size:14px; line-height:1.75; margin-bottom:16px; }
.demo-list { list-style:none; margin:24px 0 32px; display:flex; flex-direction:column; gap:10px; }
.demo-list li { font-family:var(--font-mono); font-size:12px; color:var(--text); display:flex; align-items:center; gap:10px; }
.demo-list li::before { content:''; width:6px; height:6px; border-radius:50%; background:var(--accent3-light); flex-shrink:0; }
/* mock links panel */
.mock-links { background:var(--surface); border:1px solid var(--border); border-radius:16px; padding:20px; box-shadow:0 32px 80px rgba(0,0,0,.5); }
.mock-links-toolbar { display:flex; gap:8px; margin-bottom:14px; align-items:center; }
.mock-links-search { flex:1; background:var(--surface2); border:1px solid var(--border); border-radius:7px; padding:7px 11px; font-family:var(--font-mono); font-size:10px; color:var(--muted); }
.mock-links-add { background:linear-gradient(135deg,var(--accent),var(--accent-grad)); border:none; border-radius:7px; padding:7px 13px; font-family:var(--font-mono); font-size:10px; color:#fff; white-space:nowrap; cursor:default; letter-spacing:.04em; }
.mock-link-row { display:grid; grid-template-columns:1fr auto; gap:10px; align-items:center; padding:9px 0; border-bottom:1px solid var(--border); }
.mock-link-row:last-of-type { border-bottom:none; }
.mock-link-code { font-family:var(--font-mono); font-size:11px; color:var(--accent); margin-bottom:2px; }
.mock-link-dest { font-size:11px; color:var(--muted); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; max-width:220px; }
.mock-link-meta { display:flex; gap:5px; align-items:center; }
.mock-link-clicks { font-family:var(--font-mono); font-size:9px; color:var(--muted); background:var(--surface2); border:1px solid var(--border); border-radius:4px; padding:2px 6px; white-space:nowrap; }
.mock-link-tag { font-family:var(--font-mono); font-size:9px; border-radius:4px; padding:2px 6px; white-space:nowrap; }
.mock-links-footer { margin-top:12px; font-family:var(--font-mono); font-size:9px; color:var(--muted); opacity:.6; display:flex; justify-content:space-between; }
/* ── MANAGED PILLARS ── */
.managed-section { padding:80px 0; }
.pillar-grid { display:grid; grid-template-columns:repeat(3,1fr); gap:20px; margin-top:56px; }
.pillar-card {
background:var(--surface2); border:1px solid var(--border); border-radius:14px;
padding:28px 24px; text-align:center;
}
.pillar-num { font-family:var(--font-head); font-size:36px; font-weight:900; line-height:1; background:linear-gradient(135deg,var(--accent),var(--accent-grad)); -webkit-background-clip:text; -webkit-text-fill-color:transparent; background-clip:text; margin-bottom:8px; }
.pillar-label{ font-family:var(--font-mono); font-size:10px; color:var(--muted); letter-spacing:.1em; text-transform:uppercase; margin-bottom:10px; }
.pillar-desc { font-size:13px; color:var(--muted); line-height:1.6; }
/* ── USE CASES ── */
.usecase-section { padding:80px 0 90px; background:linear-gradient(180deg, transparent 0%, rgba(var(--accent3-rgb),.025) 50%, transparent 100%); }
.usecase-grid { display:grid; grid-template-columns:repeat(3,1fr); gap:20px; margin-top:56px; }
.usecase-card {
background:var(--surface); border:1px solid var(--border); border-radius:16px;
padding:28px; transition:border-color .25s, transform .25s;
}
.usecase-card:hover { border-color:rgba(var(--accent3-rgb),.25); transform:translateY(-3px); }
.usecase-icon { width:44px; height:44px; border-radius:12px; background:rgba(var(--accent3-rgb),.1); border:1px solid rgba(var(--accent3-rgb),.2); display:flex; align-items:center; justify-content:center; margin-bottom:18px; }
.usecase-icon svg { color:var(--accent3-light); }
.usecase-card h3 { font-family:var(--font-head); font-size:12px; font-weight:700; letter-spacing:.05em; margin-bottom:10px; text-transform:uppercase; }
.usecase-card p { font-size:13px; color:var(--muted); line-height:1.65; }
.usecase-card ul { list-style:none; margin-top:14px; display:flex; flex-direction:column; gap:6px; }
.usecase-card ul li { font-family:var(--font-mono); font-size:11px; color:var(--muted); display:flex; align-items:center; gap:8px; }
.usecase-card ul li::before { content:'→'; color:var(--accent3-light); font-size:10px; }
/* ── PRICING ── */
.pricing-section { padding:80px 0 90px; }
.pricing-grid { display:grid; grid-template-columns:repeat(4,1fr); gap:16px; margin-top:56px; }
.pricing-card {
background:var(--surface); border:1px solid var(--border); border-radius:16px;
padding:32px 28px; display:flex; flex-direction:column; position:relative; overflow:hidden;
}
.pricing-card.featured {
border-color:rgba(var(--accent-rgb),.35);
background:linear-gradient(160deg, var(--surface2) 0%, var(--surface) 100%);
box-shadow:0 0 0 1px rgba(var(--accent-rgb),.15), 0 24px 60px rgba(0,0,0,.4);
}
.pricing-card.featured::before { content:''; position:absolute; top:0; left:0; right:0; height:2px; background:linear-gradient(90deg,var(--accent),var(--accent-grad)); }
.pricing-card-wrap { position:relative; padding-top:30px; }
.pricing-popular-tag { position:absolute; top:0; left:50%; transform:translateX(-50%); white-space:nowrap; font-family:var(--font-mono); font-size:9px; letter-spacing:.12em; text-transform:uppercase; background:linear-gradient(90deg,var(--accent),var(--accent-grad)); color:#fff; border-radius:20px; padding:4px 14px; font-weight:700; }
.pricing-badge { position:absolute; top:18px; right:18px; font-family:var(--font-mono); font-size:9px; letter-spacing:.1em; text-transform:uppercase; background:rgba(var(--accent-rgb),.15); border:1px solid rgba(var(--accent-rgb),.3); color:var(--accent); border-radius:4px; padding:2px 8px; }
.pricing-name { font-family:var(--font-head); font-size:13px; font-weight:700; letter-spacing:.06em; margin-bottom:6px; text-transform:uppercase; }
.pricing-price { font-family:var(--font-head); font-size:36px; font-weight:900; line-height:1; margin:14px 0 6px; }
.pricing-price span { font-size:14px; color:var(--muted); font-family:var(--font-body); font-weight:300; }
.pricing-desc { font-size:13px; color:var(--muted); margin-bottom:24px; line-height:1.6; }
.pricing-features { list-style:none; margin:0 0 28px; display:flex; flex-direction:column; gap:9px; flex:1; }
.pricing-features li { font-size:13px; color:var(--muted); display:flex; align-items:flex-start; gap:8px; }
.pricing-features li::before { content:'✓'; color:var(--accent); font-family:var(--font-mono); font-size:11px; flex-shrink:0; margin-top:2px; }
.pricing-features li.dim { opacity:.45; }
.pricing-features li.dim::before { content:'—'; color:var(--muted); }
.pricing-soon { background:var(--surface2); border:1px solid var(--border); color:var(--muted); font-family:var(--font-mono); font-size:11px; padding:10px; border-radius:8px; text-align:center; margin-top:auto; letter-spacing:.04em; cursor:pointer; transition:all .2s; }
.pricing-soon:hover { border-color:rgba(var(--accent-rgb),.5); color:var(--accent); }
.pricing-divider { border:none; border-top:1px solid var(--border); margin:0 0 20px; }
.pricing-note { font-family:var(--font-mono); font-size:10px; color:var(--muted); text-align:center; margin-top:28px; letter-spacing:.05em; }
/* ── FINAL CTA ── */
.cta-section { padding:80px 40px 100px; text-align:center; position:relative; z-index:1; }
.cta-section h2 { font-family:var(--font-head); font-size:clamp(24px,4vw,42px); font-weight:900; margin-bottom:16px; }
.cta-section p { font-size:15px; color:var(--muted); max-width:480px; margin:0 auto 36px; }
.cta-sub { font-family:var(--font-mono); font-size:11px; color:var(--muted); margin-top:20px; letter-spacing:.06em; }
/* ── FOOTER ── */
footer {
border-top:1px solid var(--border); padding:32px 40px;
display:flex; align-items:center; justify-content:space-between; flex-wrap:wrap; gap:16px;
position:relative; z-index:1;
}
.footer-logo { font-family:var(--font-head); font-size:14px; font-weight:700; color:var(--muted); letter-spacing:.04em; display:flex; align-items:center; gap:6px; }
.footer-logo span { color:var(--accent); }
.footer-links { display:flex; gap:20px; align-items:center; }
.footer-links a { font-family:var(--font-mono); font-size:11px; color:var(--muted); text-decoration:none; letter-spacing:.06em; transition:color .2s; }
.footer-links a:hover { color:var(--accent); }
.footer-note { font-family:var(--font-mono); font-size:10px; color:var(--muted); opacity:.5; letter-spacing:.05em; }
/* ── DIVIDER ── */
.section-divider { border:none; border-top:1px solid var(--border); margin:0; }
/* ── ERROR BANNER ── */
.error-banner { display:none; position:fixed; top:72px; left:50%; transform:translateX(-50%); z-index:300; background:var(--surface2); border:1px solid rgba(255,94,98,.3); border-radius:10px; padding:12px 20px; font-family:var(--font-mono); font-size:12px; color:#ff5e62; box-shadow:0 8px 24px rgba(0,0,0,.4); white-space:nowrap; }
.error-banner.show { display:block; animation:fadeDown .3s ease; }
@keyframes fadeDown { from{opacity:0;transform:translateX(-50%) translateY(-8px)} to{opacity:1;transform:translateX(-50%) translateY(0)} }
/* ── REVEAL ANIMATIONS ── */
.reveal { opacity:0; transform:translateY(24px); transition:opacity .55s ease, transform .55s ease; }
.reveal.visible { opacity:1; transform:none; }
.reveal-delay-1 { transition-delay:.1s; }
.reveal-delay-2 { transition-delay:.2s; }
.reveal-delay-3 { transition-delay:.3s; }
.reveal-delay-4 { transition-delay:.4s; }
.reveal-delay-5 { transition-delay:.5s; }
/* ── RESPONSIVE ── */
@media(max-width:900px) {
header { padding:0 20px; }
.section-inner { padding:0 20px; }
.hero { padding:80px 20px 60px; }
.feature-grid, .pillar-grid, .usecase-grid { grid-template-columns:1fr 1fr; }
.pricing-grid { grid-template-columns:1fr 1fr; }
.analytics-split, .demo-split { grid-template-columns:1fr; gap:36px; }
footer { padding:24px 20px; }
}
@media(max-width:600px) {
.feature-grid, .pillar-grid, .pricing-grid, .usecase-grid { grid-template-columns:1fr; }
.hero-cta { flex-direction:column; align-items:center; }
}
/* ── CONTACT MODAL ── */
.contact-backdrop { display:none; position:fixed; inset:0; z-index:600; background:rgba(0,0,0,.65); backdrop-filter:blur(4px); align-items:center; justify-content:center; padding:20px; }
.contact-backdrop.open { display:flex; }
.contact-modal { background:var(--surface); border:1px solid var(--border); border-radius:var(--radius); padding:36px; width:100%; max-width:480px; position:relative; box-shadow:0 24px 60px rgba(0,0,0,.5); }
.contact-modal h3 { font-family:var(--font-head); font-size:16px; font-weight:700; letter-spacing:.04em; margin-bottom:6px; }
.contact-modal .cm-sub { font-size:13px; color:var(--muted); margin-bottom:24px; line-height:1.5; }
.contact-modal label { display:block; font-family:var(--font-mono); font-size:11px; letter-spacing:.06em; color:var(--muted); margin-bottom:6px; }
.contact-modal input, .contact-modal textarea { width:100%; background:var(--surface2); border:1px solid var(--border); border-radius:8px; padding:10px 14px; color:var(--text); font-family:var(--font-body); font-size:14px; outline:none; transition:border-color .2s; }
.contact-modal input:focus, .contact-modal textarea:focus { border-color:rgba(var(--accent-rgb),.5); }
.contact-modal textarea { resize:vertical; min-height:80px; }
.contact-field { margin-bottom:16px; }
.cm-actions { display:flex; gap:12px; justify-content:flex-end; margin-top:24px; }
.cm-close { position:absolute; top:14px; right:16px; background:none; border:none; color:var(--muted); font-size:22px; cursor:pointer; line-height:1; padding:4px; }
.cm-close:hover { color:var(--text); }
.cm-success { display:none; text-align:center; padding:20px 0; }
.cm-success .cm-check { font-size:40px; color:var(--accent); margin-bottom:12px; }
.cm-success p { color:var(--muted); font-size:14px; margin-top:8px; }
</style>
</head>
<body>
<div class="error-banner" id="error-banner"></div>
<!-- ── HEADER ── -->
<header>
<a class="logo" href="/">
<span class="logo-icon-wrap"><img class="logo-icon" src="/static/qk-ico.png" alt=""><span class="logo-icon-tint"></span></span>QR<span class="logo-dot-text">knit</span>
</a>
<div class="header-right">
<div class="theme-picker">
<button class="theme-swatch" data-theme="default" title="Default" aria-label="Default blue theme"></button>
<button class="theme-swatch" data-theme="gold" title="Gold" aria-label="Gold theme"></button>
<button class="theme-swatch" data-theme="green" title="Green" aria-label="Green theme"></button>
<button class="theme-swatch" data-theme="red" title="Red" aria-label="Red theme"></button>
</div>
<button class="btn-outline" onclick="openContact('early-access')" style="font-size:11px;padding:9px 16px">Join waitlist</button>
<a class="btn-primary" href="/app">Open App →</a>
</div>
</header>
<!-- ── HERO ── -->
<section class="hero">
<div class="hero-eyebrow reveal">Managed · Cloud-native · Always on</div>
<h1 class="reveal reveal-delay-1">Shorten. Track.<br><span class="hero-accent">Convert.</span></h1>
<p class="hero-sub reveal reveal-delay-2">
A fully managed link shortening and QR code platform built for marketers, agencies, and teams. No servers. No ops. Just powerful analytics and branded short links — up and running in minutes.
</p>
<div class="hero-cta reveal reveal-delay-3">
<button class="btn-primary" onclick="openContact('early-access')" style="font-size:13px;padding:14px 28px">→ Get Early Access</button>
<button class="btn-outline" onclick="openContact('contact-sales')">Contact Sales</button>
</div>
<div class="hero-note reveal reveal-delay-4">
<span>Cloud hosting launching soon</span> · No credit card required to join the waitlist
</div>
</section>
<!-- ── FEATURES ── -->
<section class="feature-section">
<div class="section-inner">
<div class="section-label reveal">Platform features</div>
<div class="section-title reveal reveal-delay-1">Everything your team needs, nothing you don't</div>
<div class="feature-grid">
<div class="feature-card reveal reveal-delay-1">
<div class="feature-icon">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg>
</div>
<h3>Smart Link Shortening</h3>
<p>Custom codes, automatic expiry, tag organisation, and bulk CSV import and export. Search and filter your entire link library in milliseconds from any device.</p>
<span class="feature-tag">Custom codes · Tags · Expiry · Bulk CSV</span>
</div>
<div class="feature-card reveal reveal-delay-2">
<div class="feature-icon" style="background:rgba(var(--accent3-rgb),.1);border-color:rgba(var(--accent3-rgb),.2)">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="color:var(--accent3-light)"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M7 7h.01M17 7h.01M7 17h.01M11 7h2M7 11v2M15 11h2M11 17h2M17 13v2"/></svg>
</div>
<h3>QR Code Generator</h3>
<p>Five dot styles, full colour control, and logo overlay support. Generate branded QR codes for any URL — campaigns, menus, print media — and download instantly.</p>
<span class="feature-tag" style="color:var(--accent3-light);background:rgba(var(--accent3-rgb),.1);border-color:rgba(var(--accent3-rgb),.2)">5 styles · Logo overlay · Custom colours</span>
</div>
<div class="feature-card reveal reveal-delay-3">
<div class="feature-icon" style="background:rgba(245,166,35,.1);border-color:rgba(245,166,35,.2)">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="color:#f5a623"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/></svg>
</div>
<h3>Deep Click Analytics</h3>
<p>Daily bar charts, hourly 7×24 activity heatmap, geographic country breakdown, and referrer and device tracking — per link, with raw CSV export for your own analysis.</p>
<span class="feature-tag" style="color:#e8c96a;background:rgba(245,166,35,.1);border-color:rgba(245,166,35,.2)">Heatmap · Geo · Referrers · CSV export</span>
</div>
<div class="feature-card reveal reveal-delay-1">
<div class="feature-icon">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>
</div>
<h3>Team Management</h3>
<p>Invite team members, set roles, and keep each user's links isolated. Admins get a full overview with owner attribution and one-click user-scoped filtering.</p>
<span class="feature-tag">Role-based access · User isolation · Admin panel</span>
</div>
<div class="feature-card reveal reveal-delay-2">
<div class="feature-icon" style="background:rgba(0,200,150,.1);border-color:rgba(0,200,150,.2)">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="color:#00c896"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>
</div>
<h3>Custom Domains</h3>
<p>Point your own branded short domain at QRknit and serve all your links from it. Consistent branding across every click, every QR scan, every campaign.</p>
<span class="feature-tag" style="color:#7fd9a4;background:rgba(0,200,150,.1);border-color:rgba(0,200,150,.2)">Branded domains · HTTPS · Auto-renew SSL</span>
</div>
<div class="feature-card reveal reveal-delay-3">
<div class="feature-icon" style="background:rgba(255,71,87,.1);border-color:rgba(255,71,87,.2)">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="color:#ff4757"><rect x="2" y="3" width="20" height="14" rx="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></svg>
</div>
<h3>REST API Access</h3>
<p>Automate link creation, pull analytics data, and integrate QRknit into your own tools with a clean REST API. API key authentication and per-key rate limiting on Team plans.</p>
<span class="feature-tag" style="color:#ff9daa;background:rgba(255,71,87,.1);border-color:rgba(255,71,87,.2)">REST API · API keys · Webhooks (coming)</span>
</div>
</div>
</div>
</section>
<hr class="section-divider">
<!-- ── ANALYTICS SHOWCASE ── -->
<section class="analytics-section">
<div class="section-inner">
<div class="analytics-split">
<div class="analytics-copy">
<div class="section-label reveal">Analytics deep-dive</div>
<h2 class="section-title reveal reveal-delay-1">Know exactly who clicked, when, and from where</h2>
<p class="reveal reveal-delay-2">Most link shorteners give you a click count. QRknit gives you the full picture — every event captured and broken down so you can make real decisions.</p>
<ul class="analytics-list reveal reveal-delay-3">
<li>Daily click bar chart per link (7d · 30d · 90d windows)</li>
<li>Hourly 7×24 activity heatmap — spot your peak times at a glance</li>
<li>Geographic country breakdown via Cloudflare headers</li>
<li>Referrer, device type, and browser breakdown</li>
<li>30-day dashboard-wide click chart across all links</li>
<li>Raw click-event CSV export — every timestamp, referrer, country</li>
</ul>
<button class="btn-primary reveal reveal-delay-4" onclick="openContact('early-access')">Get early access →</button>
</div>
<!-- Mock analytics panel -->
<div class="mock-panel reveal reveal-delay-2" id="mock-panel">
<div class="mock-panel-title">CLICK ANALYTICS — LAST 30 DAYS</div>
<div class="mock-chart" id="mock-chart"></div>
<div class="mock-panel-title" style="margin-top:4px">HOURLY ACTIVITY</div>
<div class="mock-heatmap" id="mock-heatmap"></div>
<div class="mock-stats">
<div class="mock-stat"><div class="mock-stat-val" id="mock-total"></div><div class="mock-stat-lbl">TOTAL CLICKS</div></div>
<div class="mock-stat"><div class="mock-stat-val" id="mock-peak"></div><div class="mock-stat-lbl">PEAK DAY</div></div>
<div class="mock-stat"><div class="mock-stat-val" id="mock-countries"></div><div class="mock-stat-lbl">COUNTRIES</div></div>
</div>
</div>
</div>
</div>
</section>
<hr class="section-divider">
<!-- ── LINKS DEMO ── -->
<section class="demo-section">
<div class="section-inner">
<div class="demo-split">
<!-- Mock links panel -->
<div class="mock-links reveal reveal-delay-1">
<div class="mock-panel-title">LINK DASHBOARD</div>
<div class="mock-links-toolbar">
<div class="mock-links-search">&nbsp;Search links, tags, or URLs…</div>
<div class="mock-links-add">+ New Link</div>
</div>
<div class="mock-link-row">
<div>
<div class="mock-link-code">go.acme.co/launch</div>
<div class="mock-link-dest">acme.com/products/launch-2025</div>
</div>
<div class="mock-link-meta">
<span class="mock-link-clicks">1,204 clicks</span>
<span class="mock-link-tag" style="color:var(--accent);background:rgba(var(--accent-rgb),.1);border:1px solid rgba(var(--accent-rgb),.2)">campaign</span>
</div>
</div>
<div class="mock-link-row">
<div>
<div class="mock-link-code">go.acme.co/promo</div>
<div class="mock-link-dest">acme.com/summer-sale?utm_source=qr</div>
</div>
<div class="mock-link-meta">
<span class="mock-link-clicks">876 clicks</span>
<span class="mock-link-tag" style="color:#e8c96a;background:rgba(245,166,35,.1);border:1px solid rgba(245,166,35,.2)">sale</span>
</div>
</div>
<div class="mock-link-row">
<div>
<div class="mock-link-code">go.acme.co/docs</div>
<div class="mock-link-dest">docs.acme.com/getting-started</div>
</div>
<div class="mock-link-meta">
<span class="mock-link-clicks">342 clicks</span>
<span class="mock-link-tag" style="color:var(--accent3-light);background:rgba(var(--accent3-rgb),.1);border:1px solid rgba(var(--accent3-rgb),.2)">support</span>
</div>
</div>
<div class="mock-link-row">
<div>
<div class="mock-link-code">go.acme.co/ig</div>
<div class="mock-link-dest">instagram.com/acmehq</div>
</div>
<div class="mock-link-meta">
<span class="mock-link-clicks">518 clicks</span>
<span class="mock-link-tag" style="color:#7fd9a4;background:rgba(0,200,150,.1);border:1px solid rgba(0,200,150,.2)">social</span>
</div>
</div>
<div class="mock-link-row">
<div>
<div class="mock-link-code">go.acme.co/demo</div>
<div class="mock-link-dest">acme.com/book-a-demo</div>
</div>
<div class="mock-link-meta">
<span class="mock-link-clicks">691 clicks</span>
<span class="mock-link-tag" style="color:#ff9daa;background:rgba(255,71,87,.1);border:1px solid rgba(255,71,87,.2)">sales</span>
</div>
</div>
<div class="mock-links-footer">
<span>Showing 5 of 248 links</span>
<span>← 1 2 3 … 50 →</span>
</div>
</div>
<div class="demo-copy">
<div class="section-label reveal">Link management</div>
<h2 class="section-title reveal reveal-delay-1">Your entire link library. Organised and instant.</h2>
<p class="reveal reveal-delay-2">Everything lives in one clean dashboard. Create, edit, tag, and search across all your links — no page reloads, no friction.</p>
<ul class="demo-list reveal reveal-delay-3">
<li>Custom short codes or auto-generated slugs</li>
<li>Tag-based organisation with one-click filtering</li>
<li>Inline search across codes, destinations, and tags</li>
<li>Pin your most important links to the top</li>
<li>Bulk tag, expire, or delete in seconds</li>
<li>CSV import to migrate an existing link library</li>
</ul>
<button class="btn-primary reveal reveal-delay-4" onclick="openContact('early-access')">Get early access →</button>
</div>
</div>
</div>
</section>
<hr class="section-divider">
<!-- ── MANAGED PILLARS ── -->
<section class="managed-section">
<div class="section-inner">
<div class="section-label reveal">Why managed?</div>
<div class="section-title reveal reveal-delay-1">We handle the infrastructure. You handle the marketing.</div>
<div class="pillar-grid">
<div class="pillar-card reveal reveal-delay-1">
<div class="pillar-num">99.9%</div>
<div class="pillar-label">Uptime SLA</div>
<p class="pillar-desc">Your short links and QR codes are mission-critical. We operate redundant infrastructure with continuous health monitoring and guaranteed availability.</p>
</div>
<div class="pillar-card reveal reveal-delay-2">
<div class="pillar-num">0</div>
<div class="pillar-label">Ops overhead</div>
<p class="pillar-desc">No servers to provision, no Docker images to build, no SSL to renew. We handle every update, patch, and backup — so you never have to think about it.</p>
</div>
<div class="pillar-card reveal reveal-delay-3">
<div class="pillar-num"></div>
<div class="pillar-label">Scale on demand</div>
<p class="pillar-desc">Traffic spike from a viral campaign? No problem. The platform scales automatically to handle your peak click volume without throttling or degraded performance.</p>
</div>
</div>
</div>
</section>
<hr class="section-divider">
<!-- ── USE CASES ── -->
<section class="usecase-section">
<div class="section-inner">
<div class="section-label reveal">Use cases</div>
<div class="section-title reveal reveal-delay-1">Built for teams that move fast</div>
<div class="usecase-grid">
<div class="usecase-card reveal reveal-delay-1">
<div class="usecase-icon">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 12h-4l-3 9L9 3l-3 9H2"/></svg>
</div>
<h3>Marketing Teams</h3>
<p>Run multi-channel campaigns with trackable short links and branded QR codes. See which channels convert and when your audience is most active.</p>
<ul>
<li>UTM parameter builder (coming)</li>
<li>Per-campaign analytics dashboards</li>
<li>Branded short domains for every campaign</li>
<li>Bulk link creation via CSV import</li>
</ul>
</div>
<div class="usecase-card reveal reveal-delay-2">
<div class="usecase-icon">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="7" width="20" height="14" rx="2"/><path d="M16 21V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v16"/></svg>
</div>
<h3>Agencies</h3>
<p>Manage links and QR codes for multiple clients from a single workspace. Per-user isolation keeps client data separate, while admin tools give you full visibility.</p>
<ul>
<li>Multi-user with per-client isolation</li>
<li>Admin oversight across all accounts</li>
<li>White-label custom domains per client</li>
<li>Raw CSV export for client reporting</li>
</ul>
</div>
<div class="usecase-card reveal reveal-delay-3">
<div class="usecase-icon">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></svg>
</div>
<h3>Developers</h3>
<p>Integrate QRknit into your own products and workflows via REST API. Automate link creation, pull analytics programmatically, and build on top of the platform.</p>
<ul>
<li>Full REST API with API key auth</li>
<li>Programmatic link creation &amp; management</li>
<li>Analytics data via API endpoints</li>
<li>Webhook support (coming)</li>
</ul>
</div>
</div>
</div>
</section>
<hr class="section-divider">
<!-- ── PRICING ── -->
<section class="pricing-section">
<div class="section-inner">
<div class="section-label reveal">Pricing</div>
<div class="section-title reveal reveal-delay-1">Simple, transparent pricing. No surprises.</div>
<div class="pricing-grid">
<div class="pricing-card reveal reveal-delay-1">
<div class="pricing-badge" style="background:rgba(var(--accent3-rgb),.12);border-color:rgba(var(--accent3-rgb),.3);color:var(--accent3-light)">Coming soon</div>
<div class="pricing-name">Free</div>
<div class="pricing-price">$0 <span>/ month</span></div>
<p class="pricing-desc">Try it out. No credit card, no commitment.</p>
<hr class="pricing-divider">
<ul class="pricing-features">
<li>10 active links</li>
<li>1,000 tracked clicks / month</li>
<li>Basic QR code generation</li>
<li>7-day click analytics</li>
<li>Basic support</li>
</ul>
<button class="pricing-soon" onclick="openContact('notify-me-free')">Notify me when available</button>
</div>
<div class="pricing-card reveal reveal-delay-2">
<div class="pricing-badge" style="background:rgba(var(--accent3-rgb),.12);border-color:rgba(var(--accent3-rgb),.3);color:var(--accent3-light)">Coming soon</div>
<div class="pricing-name">Starter</div>
<div class="pricing-price">$9 <span>/ month</span></div>
<p class="pricing-desc">Perfect for individuals and small teams getting started with managed link shortening.</p>
<hr class="pricing-divider">
<ul class="pricing-features">
<li>Up to 500 active links</li>
<li>50,000 tracked clicks / month</li>
<li>QR code generation (all styles)</li>
<li>30-day click analytics</li>
<li>Referrer &amp; device breakdown</li>
<li>CSV export</li>
<li>Email support</li>
</ul>
<button class="pricing-soon" onclick="openContact('notify-me-starter')">Notify me when available</button>
</div>
<div class="pricing-card-wrap reveal reveal-delay-3">
<div class="pricing-popular-tag">Most Requested</div>
<div class="pricing-card featured">
<div class="pricing-badge" style="background:rgba(var(--accent3-rgb),.12);border-color:rgba(var(--accent3-rgb),.3);color:var(--accent3-light)">Coming soon</div>
<div class="pricing-name">Pro</div>
<div class="pricing-price">$39 <span>/ month</span></div>
<p class="pricing-desc">For power users and growing teams who need full analytics and branded domains.</p>
<hr class="pricing-divider">
<ul class="pricing-features">
<li>Unlimited active links</li>
<li>500,000 tracked clicks / month</li>
<li>Everything in Starter</li>
<li>90-day click analytics</li>
<li>Geographic country breakdown</li>
<li>Hourly 7×24 heatmap</li>
<li>1 custom short domain</li>
<li>Private instance</li>
<li>Up to 5 team members</li>
<li>Priority email support</li>
</ul>
<button class="pricing-soon" onclick="openContact('notify-me-pro')">Notify me when available</button>
</div>
</div>
<div class="pricing-card reveal reveal-delay-4">
<div class="pricing-badge" style="background:rgba(var(--accent3-rgb),.12);border-color:rgba(var(--accent3-rgb),.3);color:var(--accent3-light)">Coming soon</div>
<div class="pricing-name">Team</div>
<div class="pricing-price">$79 <span>/ month</span></div>
<p class="pricing-desc">Advanced features and full API access for teams that need more insight and control.</p>
<hr class="pricing-divider">
<ul class="pricing-features">
<li>Unlimited links &amp; clicks</li>
<li>Everything in Pro</li>
<li>Up to 3 custom short domains</li>
<li>Private instance</li>
<li>Unlimited team members</li>
<li>Full REST API + API key auth</li>
<li>UTM parameter builder</li>
<li>Password-protected links</li>
<li>Custom 404 &amp; expired pages</li>
<li>Dedicated account support</li>
</ul>
<button class="pricing-soon" onclick="openContact('notify-me-team')" style="border-color:rgba(var(--accent3-rgb),.2)">Notify me when available</button>
</div>
</div>
<p class="pricing-note reveal reveal-delay-4">All prices in USD · Billed monthly · Cancel any time · Annual plans available at launch</p>
</div>
</section>
<hr class="section-divider">
<!-- ── FINAL CTA ── -->
<section class="cta-section">
<div class="section-label reveal">Early access</div>
<h2 class="reveal reveal-delay-1">Be first in line.</h2>
<p class="reveal reveal-delay-2">Cloud hosting is launching soon. Join the waitlist now and we'll reach out as soon as spots open up.</p>
<div class="reveal reveal-delay-3">
<button class="btn-primary" onclick="openContact('early-access')" style="font-size:13px;padding:14px 32px;display:inline-flex">→ Join the Waitlist</button>
</div>
<div class="cta-sub reveal reveal-delay-4">No credit card · No commitment · Priority account creation for waitlist members</div>
</section>
<!-- ── FOOTER ── -->
<footer>
<div class="footer-logo">
<span class="logo-icon-wrap" style="isolation:isolate;position:relative;display:inline-flex"><img style="height:20px;width:20px;object-fit:contain;border-radius:2px;display:block" src="/static/qk-ico.png" alt=""><span style="position:absolute;inset:0;background:var(--accent);mix-blend-mode:hue;border-radius:2px;pointer-events:none"></span></span>
QR<span>knit</span>
</div>
<div class="footer-links">
<a href="#" onclick="openContact('contact-sales');return false">Contact Sales</a>
<a href="/api-docs">API Docs</a>
<a href="/app">App</a>
</div>
<div class="footer-note">© 2025 QRknit · All rights reserved · <a href="/notices" style="color:inherit;text-decoration:none;opacity:.7">Third-party notices</a></div>
</footer>
<!-- ── Contact modal ── -->
<div class="contact-backdrop" id="contact-backdrop" onclick="contactBackdropClick(event)">
<div class="contact-modal">
<button class="cm-close" onclick="closeContact()" aria-label="Close">&times;</button>
<div id="cm-form-content">
<h3>Get in Touch</h3>
<p class="cm-sub" id="cm-subtitle">Send us a message and we'll get back to you.</p>
<div class="contact-field">
<label for="cm-name">NAME</label>
<input type="text" id="cm-name" placeholder="Your name" autocomplete="name">
</div>
<div class="contact-field">
<label for="cm-email">EMAIL</label>
<input type="email" id="cm-email" placeholder="you@example.com" autocomplete="email">
</div>
<div class="contact-field">
<label for="cm-body">MESSAGE <span style="font-size:10px;opacity:.6">(optional)</span></label>
<textarea id="cm-body" placeholder="Anything you'd like to add…"></textarea>
</div>
<input type="hidden" id="cm-subject">
<div class="cm-actions">
<button class="btn-outline" type="button" onclick="closeContact()">Cancel</button>
<button class="btn-primary" type="button" onclick="submitContact()" id="cm-submit">Send message</button>
</div>
</div>
<div class="cm-success" id="cm-success">
<div class="cm-check"></div>
<h3 style="margin-bottom:8px">You're on the list!</h3>
<p>Thanks for your interest. We'll be in touch as soon as spots open up.</p>
</div>
</div>
</div>
<script>
// ── Theme picker ──────────────────────────────────────────────────────────
function applyTheme(name) {
if (name && name !== 'default') {
document.documentElement.setAttribute('data-theme', name);
} else {
document.documentElement.removeAttribute('data-theme');
}
localStorage.setItem('qrknit-theme', name || 'default');
document.querySelectorAll('.theme-swatch').forEach(s => {
s.classList.toggle('active', s.dataset.theme === (name || 'default'));
});
}
document.querySelectorAll('.theme-swatch').forEach(s => {
s.addEventListener('click', () => applyTheme(s.dataset.theme));
});
(function(){
const t = localStorage.getItem('qrknit-theme') || 'default';
document.querySelectorAll('.theme-swatch').forEach(s => {
s.classList.toggle('active', s.dataset.theme === t);
});
})();
// ── Error banner ──────────────────────────────────────────────────────────
(function(){
const p = new URLSearchParams(location.search);
const err = p.get('error');
const banner = document.getElementById('error-banner');
const msgs = { not_found: 'That short link does not exist.', expired: 'That short link has expired.' };
if (err && msgs[err]) {
banner.textContent = msgs[err];
banner.classList.add('show');
setTimeout(() => banner.classList.remove('show'), 5000);
}
})();
// ── Scroll reveal ─────────────────────────────────────────────────────────
(function(){
const obs = new IntersectionObserver((entries) => {
entries.forEach(e => { if (e.isIntersecting) { e.target.classList.add('visible'); obs.unobserve(e.target); } });
}, { threshold: 0.12 });
document.querySelectorAll('.reveal').forEach(el => obs.observe(el));
document.querySelectorAll('.hero .reveal').forEach(el => el.classList.add('visible'));
})();
// ── Mock analytics panel ──────────────────────────────────────────────────
(function(){
const accentRgb = () => getComputedStyle(document.documentElement).getPropertyValue('--accent-rgb').trim() || '0,184,212';
const days = 30;
const data = Array.from({length: days}, (_, i) => {
const base = 20 + Math.sin(i / 4) * 12 + Math.random() * 18;
return Math.max(0, Math.round(base));
});
data[7] = 58; data[18] = 71; data[23] = 44;
const total = data.reduce((s, v) => s + v, 0);
const peak = Math.max(...data);
const hmap = Array.from({length: 7}, () =>
Array.from({length: 12}, (_, h) => {
const peak_h = (h >= 4 && h <= 8) ? 1.8 : (h >= 1 && h <= 3) ? 0.4 : 1;
return Math.round(Math.random() * 8 * peak_h);
})
);
const hmMax = Math.max(...hmap.flat(), 1);
function render() {
const rgb = accentRgb();
const chartEl = document.getElementById('mock-chart');
if (!chartEl) return;
const maxV = Math.max(...data, 1);
chartEl.innerHTML = data.map(v => {
const pct = (v / maxV * 100).toFixed(0);
const op = v > 0 ? (0.2 + (v / maxV) * 0.7).toFixed(2) : '0.08';
return `<div class="mock-bar" style="height:${pct}%;background:rgba(${rgb},${op})"></div>`;
}).join('');
const heatEl = document.getElementById('mock-heatmap');
if (!heatEl) return;
const days_lbl = ['M','T','W','T','F','S','S'];
let html = '';
hmap.forEach((row, di) => {
html += `<div class="mock-hm-label">${days_lbl[di]}</div>`;
row.forEach(v => {
const op = v > 0 ? (0.12 + (v / hmMax) * 0.82).toFixed(2) : '0.05';
html += `<div class="mock-hm-cell" style="background:rgba(${rgb},${op})"></div>`;
});
});
heatEl.innerHTML = html;
document.getElementById('mock-total').textContent = total.toLocaleString();
document.getElementById('mock-peak').textContent = peak;
document.getElementById('mock-countries').textContent = '12';
}
render();
document.querySelectorAll('.theme-swatch').forEach(s => {
s.addEventListener('click', () => setTimeout(render, 50));
});
})();
// ── Contact form ───────────────────────────────────────────────────────────
const CM_SUBTITLES = {
'early-access': "Join the waitlist and we'll reach out as soon as spots open up.",
'contact-sales': "Tell us about your team and we'll get back to you within one business day.",
'notify-me-free': "We'll let you know as soon as the Free plan is available.",
'notify-me-starter': "We'll let you know as soon as the Starter plan is available.",
'notify-me-pro': "We'll let you know as soon as the Pro plan is available.",
'notify-me-team': "We'll let you know when the Team plan launches.",
};
function openContact(subject) {
document.getElementById('cm-subject').value = subject;
const sub = CM_SUBTITLES[subject] || "Send us a message and we'll get back to you.";
document.getElementById('cm-subtitle').textContent = sub;
document.getElementById('cm-form-content').style.display = '';
document.getElementById('cm-success').style.display = 'none';
document.getElementById('cm-name').value = '';
document.getElementById('cm-email').value = '';
document.getElementById('cm-body').value = '';
const btn = document.getElementById('cm-submit');
btn.disabled = false;
btn.textContent = 'Send message';
document.getElementById('contact-backdrop').classList.add('open');
setTimeout(() => document.getElementById('cm-name').focus(), 80);
}
function closeContact() {
document.getElementById('contact-backdrop').classList.remove('open');
}
function contactBackdropClick(e) {
if (e.target === document.getElementById('contact-backdrop')) closeContact();
}
async function submitContact() {
const name = document.getElementById('cm-name').value.trim();
const email = document.getElementById('cm-email').value.trim();
const subject = document.getElementById('cm-subject').value;
const body = document.getElementById('cm-body').value.trim();
if (!name) { document.getElementById('cm-name').focus(); return; }
if (!email) { document.getElementById('cm-email').focus(); return; }
const btn = document.getElementById('cm-submit');
btn.disabled = true;
btn.textContent = 'Sending…';
try {
const resp = await fetch('/api/contact', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({ name, email, subject, body })
});
if (resp.ok) {
document.getElementById('cm-form-content').style.display = 'none';
document.getElementById('cm-success').style.display = 'block';
} else {
btn.disabled = false;
btn.textContent = 'Send message';
alert('Something went wrong. Please try again.');
}
} catch {
btn.disabled = false;
btn.textContent = 'Send message';
alert('Network error. Please try again.');
}
}
document.addEventListener('keydown', e => {
if (e.key === 'Escape') closeContact();
});
</script>
</body>
</html>