Wait Landing
Style direction: Reference landing study
$ npx cactus add wait
Copied!
Aa
InterBody
Recreate Spec (Exact Site)
Use this file as the single source of truth to recreate the current site exactly.
1) Design Variables (Set First)
Define these in index.css before building components.
:root {
--background: #0d0d0d;
--foreground: #ffffff;
--card: #141414;
--card-foreground: #ffffff;
--popover: #141414;
--popover-foreground: #ffffff;
--primary: #dfff1a;
--primary-foreground: #121212;
--secondary: #212121;
--secondary-foreground: #ffffff;
--muted: #141414;
--muted-foreground: #7e7e7e;
--accent: #9a9a9a;
--accent-foreground: #ffffff;
--destructive: #d41c1c;
--destructive-foreground: #ffffff;
--border: rgba(255, 255, 255, 0.1);
--input: rgba(255, 255, 255, 0.05);
--ring: rgba(223, 255, 26, 0.65);
--chart-1: #dfff1a;
--chart-2: #9a9a9a;
--chart-3: #7e7e7e;
--chart-4: #5b5b5b;
--chart-5: #494949;
--radius: 12px;
--sidebar: #0d0d0d;
--sidebar-foreground: #ffffff;
--sidebar-primary: #dfff1a;
--sidebar-primary-foreground: #121212;
--sidebar-accent: #141414;
--sidebar-accent-foreground: #ffffff;
--sidebar-border: #5b5b5b;
--sidebar-ring: rgba(223, 255, 26, 0.65);
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
--color-card-foreground: var(--card-foreground);
--color-popover: var(--popover);
--color-popover-foreground: var(--popover-foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-secondary: var(--secondary);
--color-secondary-foreground: var(--secondary-foreground);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
--color-accent: var(--accent);
--color-accent-foreground: var(--accent-foreground);
--color-destructive: var(--destructive);
--color-destructive-foreground: var(--destructive-foreground);
--color-border: var(--border);
--color-input: var(--input);
--color-ring: var(--ring);
--color-chart-1: var(--chart-1);
--color-chart-2: var(--chart-2);
--color-chart-3: var(--chart-3);
--color-chart-4: var(--chart-4);
--color-chart-5: var(--chart-5);
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
--color-sidebar: var(--sidebar);
--color-sidebar-foreground: var(--sidebar-foreground);
--color-sidebar-primary: var(--sidebar-primary);
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
--color-sidebar-accent: var(--sidebar-accent);
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-ring: var(--sidebar-ring);
--pattern-line: rgba(75, 85, 99, 0.08);
--pattern-dot: rgba(55, 65, 81, 0.12);
--mask-solid: #000000;
--color-border-soft: rgba(255, 255, 255, 0.07);
--color-primary-glow: rgba(223, 255, 26, 0.5);
--overlay-top: rgba(255, 255, 255, 0.08);
--overlay-bottom: rgba(13, 13, 13, 0.35);
}
2) Rebuild Workflow
- Analyze existing codebase and reuse exact content/copy/spacing.
- Create files and components exactly as below.
- Keep black background theme and token-based colors.
- Keep FAQ behavior: single-open accordion (only one open at a time).
- If any content is missing, ask user for title/description/CTA/link values before guessing.
3) Global Styles
Section Title (UI): Global Theme + Tokens
File: index.css
@import "tailwindcss";
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
:root {
font-family: "Inter", sans-serif;
/* Design Tokens: provide or override these values to theme the page */
--background: #0d0d0d;
--foreground: #ffffff;
--card: #141414;
--card-foreground: #ffffff;
--popover: #141414;
--popover-foreground: #ffffff;
--primary: #dfff1a;
--primary-foreground: #121212;
--secondary: #212121;
--secondary-foreground: #ffffff;
--muted: #141414;
--muted-foreground: #7e7e7e;
--accent: #9a9a9a;
--accent-foreground: #ffffff;
--destructive: #d41c1c;
--destructive-foreground: #ffffff;
--border: rgba(255, 255, 255, 0.1);
--input: rgba(255, 255, 255, 0.05);
--ring: rgba(223, 255, 26, 0.65);
--chart-1: #dfff1a;
--chart-2: #9a9a9a;
--chart-3: #7e7e7e;
--chart-4: #5b5b5b;
--chart-5: #494949;
--radius: 12px;
--sidebar: #0d0d0d;
--sidebar-foreground: #ffffff;
--sidebar-primary: #dfff1a;
--sidebar-primary-foreground: #121212;
--sidebar-accent: #141414;
--sidebar-accent-foreground: #ffffff;
--sidebar-border: #5b5b5b;
--sidebar-ring: rgba(223, 255, 26, 0.65);
/* Derived aliases */
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
--color-card-foreground: var(--card-foreground);
--color-popover: var(--popover);
--color-popover-foreground: var(--popover-foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-secondary: var(--secondary);
--color-secondary-foreground: var(--secondary-foreground);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
--color-accent: var(--accent);
--color-accent-foreground: var(--accent-foreground);
--color-destructive: var(--destructive);
--color-destructive-foreground: var(--destructive-foreground);
--color-border: var(--border);
--color-input: var(--input);
--color-ring: var(--ring);
--color-chart-1: var(--chart-1);
--color-chart-2: var(--chart-2);
--color-chart-3: var(--chart-3);
--color-chart-4: var(--chart-4);
--color-chart-5: var(--chart-5);
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
--color-sidebar: var(--sidebar);
--color-sidebar-foreground: var(--sidebar-foreground);
--color-sidebar-primary: var(--sidebar-primary);
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
--color-sidebar-accent: var(--sidebar-accent);
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-ring: var(--sidebar-ring);
/* Additional UI tokens used by this project */
--pattern-line: rgba(75, 85, 99, 0.08);
--pattern-dot: rgba(55, 65, 81, 0.12);
--mask-solid: #000000;
--color-border-soft: rgba(255, 255, 255, 0.07);
--color-primary-glow: rgba(223, 255, 26, 0.5);
--overlay-top: rgba(255, 255, 255, 0.08);
--overlay-bottom: rgba(13, 13, 13, 0.35);
}
* {
box-sizing: border-box;
}
body {
margin: 0;
min-height: 100vh;
background: var(--color-background);
}
a {
color: inherit;
text-decoration: none;
}
4) Page Shell
Section Title (UI): Full Page Layout + Background Pattern
File: App.tsx
import { FaqSection } from './components/FaqSection'
import { FooterSection } from './components/FooterSection'
import { HeroSection } from './components/HeroSection'
import { VideoSection } from './components/VideoSection'
function App() {
return (
<div className="relative min-h-screen w-full overflow-x-hidden bg-[var(--color-background)] text-[var(--color-foreground)]">
<div
className="pointer-events-none absolute inset-0 z-0"
style={{
backgroundImage: `
repeating-linear-gradient(0deg, transparent, transparent 19px, var(--pattern-line) 19px, var(--pattern-line) 20px, transparent 20px, transparent 39px, var(--pattern-line) 39px, var(--pattern-line) 40px),
repeating-linear-gradient(90deg, transparent, transparent 19px, var(--pattern-line) 19px, var(--pattern-line) 20px, transparent 20px, transparent 39px, var(--pattern-line) 39px, var(--pattern-line) 40px),
radial-gradient(circle at 20px 20px, var(--pattern-dot) 2px, transparent 2px),
radial-gradient(circle at 40px 40px, var(--pattern-dot) 2px, transparent 2px)
`,
backgroundSize: '40px 40px, 40px 40px, 40px 40px, 40px 40px',
WebkitMaskImage:
'linear-gradient(to right, var(--mask-solid) 0%, var(--mask-solid) 50%, transparent 20%, transparent 100%)',
maskImage:
'linear-gradient(to right, var(--mask-solid) 0%, var(--mask-solid) 50%, transparent 20%, transparent 100%)',
}}
/>
<main className="relative z-10 mx-auto flex w-full max-w-[1440px] flex-col items-center px-4 pt-14 min-[810px]:px-0 min-[810px]:pt-20">
<HeroSection />
<VideoSection />
<FaqSection />
<FooterSection />
</main>
</div>
)
}
export default App
5) Hero Section
Section Title (UI): Get early access
File: components/HeroSection.tsx
import avatar1 from '../assets/avatars/a1.png'
import avatar2 from '../assets/avatars/a2.png'
import avatar3 from '../assets/avatars/a3.png'
import avatar4 from '../assets/avatars/a4.png'
import avatar5 from '../assets/avatars/a5.jpg'
const avatars = [avatar1, avatar2, avatar3, avatar4, avatar5]
const countdown = [
{ value: '92', label: 'Days' },
{ value: '20', label: 'Hours' },
{ value: '37', label: 'Minutes' },
{ value: '31', label: 'Seconds', mobileValue: '28' },
]
export function HeroSection() {
return (
<section className="h-[545px] w-full max-w-[448px] min-[810px]:h-[464px]">
<div className="flex flex-col items-center gap-6">
<div className="relative h-12 w-12 rounded-[13px] bg-[var(--color-primary)]">
<svg
aria-hidden="true"
className="absolute left-3 top-3 h-6 w-6 text-[var(--color-primary-foreground)]"
fill="none"
viewBox="0 0 40 40"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M17.343 2.65705L20 0L40 20L20 40L17.3429 37.3429L34.6859 20L17.343 2.65705Z" fill="var(--color-primary-foreground)" />
<path d="M13.8744 6.12564L16.5314 3.46859L33.0628 20L16.5314 36.5314L13.8744 33.8744L27.7487 20L13.8744 6.12564Z" fill="var(--color-primary-foreground)" />
<path d="M0 20L13.0628 6.93718L26.1256 20L13.0628 33.0628L10.4058 30.4058L20.8115 20L13.0628 12.2513L2.65705 22.657L0 20Z" fill="var(--color-primary-foreground)" />
<path d="M13.0628 13.8744L10.4058 16.5314L13.8744 20L6.93718 26.9372L9.59422 29.5942L19.1885 20L13.0628 13.8744Z" fill="var(--color-primary-foreground)" />
<path d="M6.12564 26.1256L3.46859 23.4686L9.56643 17.3708L12.2235 20.0278L6.12564 26.1256Z" fill="var(--color-primary-foreground)" />
</svg>
</div>
<div className="inline-flex h-6 items-center rounded-full bg-[var(--color-card)] pl-[2px] pr-[10px] shadow-[0_0_0_1px_var(--color-border-soft)]">
<span className="mr-[7px] size-[20px] rounded-full bg-[var(--color-primary)] opacity-80 shadow-[0_0_14px_var(--color-ring)]" />
<p className="text-[12px] font-medium uppercase leading-6 tracking-[0.08em] text-[var(--color-foreground)]">
Available in Early 2025
</p>
</div>
</div>
<div className="mt-6">
<h1 className="text-center text-[32px] font-medium leading-[42px] tracking-[-0.02em] text-[var(--color-foreground)] min-[810px]:text-[40px] min-[810px]:leading-[48px]">
Get early access
</h1>
<p className="mt-1 text-center text-[15px] leading-6 text-[var(--color-muted-foreground)]">
Be amongst the first to experience Wait and launch a viral waitlist.
Sign up to be notified when we launch!
</p>
</div>
<form className="mt-5 min-[810px]:mt-8" onSubmit={(event) => event.preventDefault()}>
<div className="rounded-[12px] bg-[var(--color-card)] p-1 shadow-[inset_0_0_0_1px_var(--color-input)] min-[810px]:p-[6px]">
<div className="flex flex-col gap-1 min-[810px]:h-9 min-[810px]:flex-row min-[810px]:items-center min-[810px]:gap-[10px]">
<input
className="h-10 w-full rounded-lg border-none bg-transparent px-[10px] text-[15px] leading-6 text-[var(--color-foreground)] outline-none placeholder:text-[var(--color-muted-foreground)] min-[810px]:h-full min-[810px]:flex-1 min-[810px]:rounded-none min-[810px]:px-[6px]"
placeholder="Email"
type="email"
/>
<button
className="h-9 w-full rounded-lg bg-[var(--color-primary)] text-[15px] font-medium leading-6 text-[var(--color-primary-foreground)] min-[810px]:h-[35px] min-[810px]:w-[111px]"
type="submit"
>
Join waitlist
</button>
</div>
</div>
</form>
<div className="mt-6 flex flex-col items-center gap-1 min-[810px]:h-7 min-[810px]:flex-row min-[810px]:justify-center min-[810px]:gap-2">
<div className="flex -space-x-[3px]">
{avatars.map((src, index) => (
<img
alt=""
className="size-7 rounded-full object-cover"
key={src}
loading={index < 2 ? 'eager' : 'lazy'}
src={src}
/>
))}
</div>
<p className="text-[14px] leading-6 text-[var(--color-muted-foreground)]">
Join{' '}
<span className="hidden text-[var(--color-accent)] min-[810px]:inline">
12,500 +
</span>
<span className="inline text-[var(--color-accent)] min-[810px]:hidden">
17,871 +
</span>{' '}
others on the waitlist
</p>
</div>
<div className="mt-6 flex items-start justify-center">
{countdown.map((item, index) => (
<div className="flex items-start" key={item.label}>
<div className="w-16 text-center">
<p className="text-[18px] font-normal leading-[18px] text-[var(--color-foreground)]">
{item.mobileValue ? (
<>
<span className="hidden min-[810px]:inline">
{item.value}
</span>
<span className="min-[810px]:hidden">
{item.mobileValue}
</span>
</>
) : (
item.value
)}
</p>
<p className="mt-[5px] text-[10px] font-normal uppercase tracking-[0.12em] text-[var(--color-muted-foreground)]">
{item.label}
</p>
</div>
{index < countdown.length - 1 ? (
<span className="px-[15.5px] text-[14px] leading-[18px] text-[var(--color-chart-5)]">
·
</span>
) : null}
</div>
))}
</div>
<div className="mt-6 flex items-center justify-center gap-2">
<svg
aria-hidden="true"
className="h-4 w-[17px] text-[var(--color-muted-foreground)]"
fill="none"
viewBox="0 0 17 16"
>
<path
d="M3 6.5h11M5.667 3.167V1.833m5.666 1.334V1.833m-7 11.667h8.334c.736 0 1.333-.597 1.333-1.333V4.5c0-.736-.597-1.333-1.333-1.333H4.333C3.597 3.167 3 3.764 3 4.5v7.667c0 .736.597 1.333 1.333 1.333Z"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="1.5"
/>
</svg>
<p className="text-[12px] font-medium uppercase leading-6 tracking-[0.08em] text-[var(--color-foreground)]">
Left until full release
</p>
</div>
</section>
)
}
6) Video Section
Section Title (UI): See how Wait works (3m)
File: components/VideoSection.tsx
const demoVideoUrl =
'https://videos.pexels.com/video-files/4990245/4990245-hd_1920_1080_30fps.mp4'
export function VideoSection() {
return (
<section className="mt-8 flex h-[480px] w-full max-w-[720px] flex-col overflow-hidden rounded-[10px] border-t border-[var(--color-border)] bg-[var(--color-primary-foreground)] px-1 pb-1 pt-2 min-[810px]:mt-10 min-[810px]:h-[430px]">
<div className="ml-1 flex h-2 items-center gap-1">
<span className="size-2 rounded-full bg-[var(--color-secondary)]" />
<span className="size-2 rounded-full bg-[var(--color-secondary)]" />
<span className="size-2 rounded-full bg-[var(--color-secondary)]" />
</div>
<div className="relative mt-2 flex-1 overflow-hidden rounded-[8px]">
<video
autoPlay
className="absolute inset-0 h-full w-full object-cover opacity-40 [mix-blend-mode:luminosity]"
loop
muted
playsInline
preload="auto"
src={demoVideoUrl}
/>
<div className="absolute left-1/2 top-1/2 flex w-[172px] -translate-x-1/2 -translate-y-1/2 flex-col items-center">
<div className="relative grid size-16 place-items-center rounded-full border border-[var(--color-border)]">
<span className="absolute inset-[-17.7px] rounded-full bg-[var(--color-primary-glow)] blur-[8px]" />
<span className="absolute inset-[-16.6px] rounded-full bg-[var(--color-primary-glow)] blur-[6px]" />
<span className="absolute inset-[-11.6px] rounded-full bg-[var(--color-primary-glow)] blur-[4px]" />
<span className="absolute inset-0 rounded-full bg-[var(--color-primary)]" />
<svg
aria-hidden="true"
className="relative size-8 text-[var(--color-background)]"
fill="currentColor"
viewBox="0 0 32 32"
>
<path
clipRule="evenodd"
d="M6 7.537c0-1.901 2.039-3.106 3.705-2.19l15.387 8.464c1.727.949 1.727 3.43 0 4.38L9.707 26.655C8.04 27.57 6 26.365 6 24.464V7.537Z"
fillRule="evenodd"
/>
</svg>
</div>
<p className="mt-5 text-center text-[14px] leading-6 text-[var(--color-muted-foreground)]">
See how Wait works (3m)
</p>
</div>
</div>
</section>
)
}
7) FAQ Section
Section Title (UI): Frequently asked questions
File: components/FaqSection.tsx
import { useState } from 'react'
const faqItems = [
{
question: 'What is Wait?',
answer:
'Wait is a premium template for building and managing a launch waitlist. It helps you capture interest and grow your audience before release.',
},
{
question: "What's included in this template?",
answer:
'You get 15+ components, useful code overrides, and complete support pages so you can launch quickly with a polished waitlist experience.',
},
{
question: 'How do I customize this template?',
answer:
'You can edit every section directly in your project, update colors and typography, and replace media with your own brand assets in minutes.',
},
{
question: 'Is there support available?',
answer:
'Yes. Support is available for setup and customization questions so you can ship without getting blocked on implementation details.',
},
{
question: 'How much will this cost?',
answer:
'Pricing depends on the final package and scope. The template is designed to stay affordable while covering launch-ready essentials.',
},
]
export function FaqSection() {
const [openIndex, setOpenIndex] = useState(0)
return (
<section className="mt-8 w-full max-w-[448px] pt-6 min-[810px]:mt-10">
<div>
<h2 className="whitespace-nowrap text-center text-[28px] font-medium leading-[39.2px] text-[var(--color-foreground)] min-[810px]:text-left">
Frequently asked questions
</h2>
<p className="mt-1 text-center text-[15px] leading-6 text-[var(--color-muted-foreground)] min-[810px]:text-left">
Everything you need to know about the Wait template. Find answers to
the most common questions below.
</p>
</div>
<div className="mt-8 space-y-2">
{faqItems.map((item, index) => {
const isOpen = openIndex === index
return (
<div className="overflow-hidden rounded-[12px] bg-[var(--color-card)]" key={item.question}>
<button
aria-controls={`faq-panel-${index}`}
aria-expanded={isOpen}
className="flex h-12 w-full items-center justify-between px-4 text-left"
onClick={() => setOpenIndex(index)}
type="button"
>
<span className="text-[15px] font-normal leading-6 text-[var(--color-foreground)]">
{item.question}
</span>
<svg
aria-hidden="true"
className={`h-5 w-5 text-[var(--color-muted-foreground)] transition-transform duration-300 ${isOpen ? 'rotate-45' : 'rotate-0'}`}
fill="currentColor"
viewBox="0 0 20 20"
>
<path d="M10 2.5C10.46 2.5 10.833 2.873 10.833 3.333V9.167H16.667C17.127 9.167 17.5 9.54 17.5 10C17.5 10.46 17.127 10.833 16.667 10.833H10.833V16.667C10.833 17.127 10.46 17.5 10 17.5C9.54 17.5 9.167 17.127 9.167 16.667V10.833H3.333C2.873 10.833 2.5 10.46 2.5 10C2.5 9.54 2.873 9.167 3.333 9.167H9.167V3.333C9.167 2.873 9.54 2.5 10 2.5Z" />
</svg>
</button>
<div
className={`grid transition-all duration-300 ease-out ${isOpen ? 'grid-rows-[1fr] opacity-100' : 'grid-rows-[0fr] opacity-0'}`}
id={`faq-panel-${index}`}
>
<div className="overflow-hidden px-4 pb-4">
<p className="text-[15px] leading-6 text-[var(--color-muted-foreground)]">
{item.answer}
</p>
</div>
</div>
</div>
)
})}
</div>
</section>
)
}
8) Footer Section
Section Title (UI): Built using / © 2026 Wait by CMD Supply ⌘
File: components/FooterSection.tsx
export function FooterSection() {
return (
<footer className="mt-[88px] flex flex-col items-center pb-[52px] min-[810px]:mt-16 min-[810px]:pb-[64px]">
<div className="flex flex-col items-center text-[15px] leading-6 text-[var(--color-muted-foreground)] min-[810px]:flex-row">
<span>Built using</span>
<span className="mx-2 hidden text-[var(--color-chart-4)] min-[810px]:inline">·</span>
<a
className="text-[var(--color-muted-foreground)]"
href="https://jusfar.lemonsqueezy.com/buy/d7f24a7a-3eb7-4f43-8945-c855d5c5e7ed"
rel="noreferrer"
target="_blank"
>
Get this template
</a>
<span className="mx-2 hidden text-[var(--color-chart-4)] min-[810px]:inline">·</span>
<a
className="text-[var(--color-muted-foreground)]"
href="https://jusfar.lemonsqueezy.com/affiliates"
rel="noreferrer"
target="_blank"
>
Become an affiliate
</a>
</div>
<p className="mt-4 flex flex-wrap items-center justify-center gap-1 text-[15px] leading-6 text-[var(--color-muted-foreground)]">
<span>© 2026 Wait by</span>
<a
className="text-[var(--color-foreground)]"
href="https://www.cmd.supply/"
rel="noreferrer"
target="_blank"
>
CMD Supply ⌘
</a>
</p>
</footer>
)
}
9) Assets Required
assets/avatars/a1.pngassets/avatars/a2.pngassets/avatars/a3.pngassets/avatars/a4.pngassets/avatars/a5.jpg
10) Run
Use port 3005:
bun install
bun run dev -- --port 3005