{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "waitlist",
  "title": "Waitlist",
  "type": "registry:page",
  "description": "Pre-launch signup page. Split layout with product pitch, logo mark, three benefit bullets on the left and an email capture card on the right that flips to a queue-position confirmation with referral hint after submit.",
  "categories": [
    "marketing"
  ],
  "files": [
    {
      "path": "packages/elements-lit/src/blocks/waitlist/waitlist.ts",
      "content": "import { LitElement, css, html } from 'lit'\nimport { Check, Layers, Link2, ShieldCheck, Zap } from 'lucide'\nimport { tailwind } from '../../lib/styles'\nimport { ThemeController } from '../../lib/theme'\nimport { icon } from '../../lib/icon'\n// Side-effect imports: register the composed elements for standalone use\n// (consumers who install only this block still get working primitives).\nimport '../../components/button/button'\nimport '../../components/card/card'\nimport '../../components/input/input'\nimport type { UipInput } from '../../components/input/input'\n\nconst benefits = [\n  { iconName: 'zap', title: 'Ships in minutes', copy: 'Drop-in blocks and primitives, not boilerplate to rewrite.' },\n  {\n    iconName: 'shield-check',\n    title: 'Own the code',\n    copy: 'Every file is copied into your repo. Edit it freely, no lock-in.',\n  },\n  {\n    iconName: 'layers',\n    title: 'Dual framework',\n    copy: 'Vue and React mirrors stay in lockstep across every component.',\n  },\n] as const\n\n/**\n * <uip-waitlist> — the registry Waitlist block as a web component.\n *\n * Class strings are React's Waitlist verbatim. React state (useState) becomes\n * @state fields. React has no submit callback; the form dispatches a\n * `subscribe` CustomEvent (detail: { email }, bubbles, composed). The benefit\n * icons resolve by name (React renders component references; lucide nodes\n * don't carry their kebab-case name, which icon() needs).\n */\nexport class UipWaitlist extends LitElement {\n  static shadowRootOptions = { ...LitElement.shadowRootOptions, delegatesFocus: true }\n  // :host display can't be a utility class (the host has no template of its own).\n  static styles = [tailwind, css`:host { display: block; }`]\n\n  static properties = {\n    email: { state: true },\n    submitted: { state: true },\n    position: { state: true },\n  }\n\n  private email = ''\n  private submitted = false\n  private position = 214\n\n  constructor() {\n    super()\n    new ThemeController(this)\n  }\n\n  connectedCallback() {\n    super.connectedCallback()\n    this.setAttribute('data-uipkge', '')\n    this.setAttribute('data-slot', 'waitlist')\n  }\n\n  private benefitIcon(name: string, className: string) {\n    // Mirrors React's <benefit.icon>: Zap / ShieldCheck / Layers in row order.\n    if (name === 'shield-check') return icon(ShieldCheck, 'shield-check', className)\n    if (name === 'layers') return icon(Layers, 'layers', className)\n    return icon(Zap, 'zap', className)\n  }\n\n  private handleSubmit(e: Event) {\n    e.preventDefault()\n    if (!this.email.includes('@')) return\n    this.submitted = true\n    this.dispatchEvent(new CustomEvent('subscribe', { detail: { email: this.email }, bubbles: true, composed: true }))\n  }\n\n  render() {\n    return html`\n      <section data-slot=\"waitlist\" class=\"bg-background grid min-h-screen lg:grid-cols-2\">\n        <div class=\"bg-muted/40 relative flex items-center px-6 py-16 lg:border-r lg:px-16\">\n          <div class=\"max-w-lg\">\n            <div class=\"flex items-center gap-3\">\n              <div class=\"bg-primary text-primary-foreground grid size-10 place-items-center rounded-lg shadow-sm\">\n                ${icon(Layers, 'layers', 'size-5')}\n              </div>\n              <span class=\"text-sm font-semibold tracking-tight\">Northwind</span>\n            </div>\n\n            <h1 class=\"mt-8 text-3xl font-semibold tracking-tight sm:text-4xl\">Be first in line when we launch</h1>\n            <p class=\"text-muted-foreground mt-4 text-base sm:text-lg\">\n              We&rsquo;re onboarding a small cohort of teams each week. Join the waitlist and we&rsquo;ll save your\n              spot.\n            </p>\n\n            <ul class=\"mt-10 space-y-6\">\n              ${benefits.map(\n                (benefit) => html`\n                  <li class=\"flex gap-4\">\n                    <div class=\"bg-primary/10 text-primary grid size-9 shrink-0 place-items-center rounded-lg\">\n                      ${this.benefitIcon(benefit.iconName, 'size-4.5')}\n                    </div>\n                    <div>\n                      <p class=\"text-sm font-medium\">${benefit.title}</p>\n                      <p class=\"text-muted-foreground mt-1 text-sm\">${benefit.copy}</p>\n                    </div>\n                  </li>\n                `,\n              )}\n            </ul>\n          </div>\n        </div>\n\n        <div class=\"flex items-center justify-center px-6 py-16\">\n          <uip-card class=\"w-full max-w-md\">\n            ${!this.submitted\n              ? html`\n                  <uip-card-header>\n                    <uip-card-title>Join the waitlist</uip-card-title>\n                    <uip-card-description>\n                      Early access rolls out weekly. No spam &mdash; one email when it&rsquo;s your turn.\n                    </uip-card-description>\n                  </uip-card-header>\n                  <uip-card-content>\n                    <form class=\"space-y-3\" @submit=${this.handleSubmit}>\n                      <uip-input\n                        type=\"email\"\n                        placeholder=\"you@company.com\"\n                        .value=${this.email}\n                        required\n                        @input=${(e: Event) => {\n                          this.email = (e.target as UipInput).value\n                        }}\n                      ></uip-input>\n                      <uip-button type=\"submit\" class=\"w-full\">Request access</uip-button>\n                    </form>\n                    <p class=\"text-muted-foreground mt-4 text-xs\">2,300+ teams already in line</p>\n                  </uip-card-content>\n                `\n              : html`\n                  <uip-card-content class=\"pt-6 text-center\">\n                    <div class=\"bg-primary/10 text-primary mx-auto grid size-12 place-items-center rounded-full\">\n                      ${icon(Check, 'check', 'size-6')}\n                    </div>\n                    <h2 class=\"mt-5 text-2xl font-semibold tracking-tight\">You&rsquo;re #${this.position} in line</h2>\n                    <p class=\"text-muted-foreground mt-2 text-sm\">\n                      We&rsquo;ll email ${this.email} as soon as your spot opens up.\n                    </p>\n                    <div class=\"bg-muted/50 mt-6 flex items-start gap-3 rounded-lg border p-4 text-left\">\n                      ${icon(Link2, 'link-2', 'text-muted-foreground mt-0.5 size-4 shrink-0')}\n                      <p class=\"text-muted-foreground text-xs\">\n                        Want to skip the queue? Share your invite link after launch &mdash; every friend who signs up\n                        moves you up 5 spots.\n                      </p>\n                    </div>\n                  </uip-card-content>\n                `}\n          </uip-card>\n        </div>\n      </section>\n    `\n  }\n}\n\ncustomElements.get('uip-waitlist') || customElements.define('uip-waitlist', UipWaitlist)\n\ndeclare global {\n  interface HTMLElementTagNameMap {\n    'uip-waitlist': UipWaitlist\n  }\n}\n",
      "type": "registry:file",
      "target": "src/uip/blocks/waitlist/waitlist.ts"
    }
  ],
  "dependencies": [
    "lit",
    "lucide"
  ],
  "registryDependencies": [
    "https://uipkge.dev/r/lit/button.json",
    "https://uipkge.dev/r/lit/card.json",
    "https://uipkge.dev/r/lit/input.json",
    "https://uipkge.dev/r/lit/lit-core.json"
  ]
}
