{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "waitlist-referral-position",
  "title": "Waitlist — Referral Position",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-angular/blocks/waitlist-referral-position/waitlist-referral-position.component.ts",
      "content": "// Waitlist capture that swaps on submit to show queue position, the referral link\n// that moves it, and a progress meter to the next tier of early access.\n// Port of the React block 1:1.\nimport { ChangeDetectionStrategy, Component, DestroyRef, Input, computed, inject, signal } from '@angular/core'\nimport { Check, Copy, LucideAngularModule, Users } from 'lucide-angular'\nimport { cn } from '@/lib/utils'\nimport { UiBadgeComponent } from '@/ui/badge/badge.component'\nimport { UiButtonComponent } from '@/ui/button/button.component'\nimport { UiCardComponent, UiCardContentComponent } from '@/ui/card/card.component'\nimport { UiInputComponent } from '@/ui/input/input.component'\nimport { UiProgressComponent } from '@/ui/progress/progress.component'\nimport { UiSeparatorComponent } from '@/ui/separator/separator.component'\n\nconst referralLink = 'https://northwind.dev/w/8f2a41'\n\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-waitlist-referral-position, [ui-waitlist-referral-position]',\n  standalone: true,\n  // React renders the root <section> itself: the host stays out of layout and `class` goes to the root.\n  host: { '[attr.class]': '\"contents\"' },\n  imports: [\n    LucideAngularModule,\n    UiBadgeComponent,\n    UiButtonComponent,\n    UiCardComponent,\n    UiCardContentComponent,\n    UiInputComponent,\n    UiProgressComponent,\n    UiSeparatorComponent,\n  ],\n  template: `\n    <section data-slot=\"waitlist-referral-position\" [class]=\"rootClass\">\n      <div class=\"mx-auto max-w-xl px-6 py-20 text-center lg:py-28\">\n        <span ui-badge variant=\"secondary\" class=\"gap-1.5\">\n          <lucide-icon [img]=\"Users\" class=\"size-3\" aria-hidden=\"true\" />\n          8,400 waiting\n        </span>\n\n        <h2 class=\"mt-5 text-3xl font-semibold tracking-tight text-balance sm:text-4xl\">\n          Early access opens a cohort at a time\n        </h2>\n        <p class=\"text-muted-foreground mt-3 text-lg\">\n          Join the list, then move up it. Every person who joins through your link moves you forward.\n        </p>\n\n        <!-- One card that swaps contents rather than two stacked states, so the\n             section height barely changes between before and after. -->\n        <div ui-card class=\"mt-8 text-left\">\n          <div ui-card-content class=\"p-6\">\n            @if (!joined()) {\n              <form (submit)=\"join($event)\">\n                <label for=\"waitlist-email\" class=\"text-sm font-medium\">Work email</label>\n                <div class=\"mt-2 flex gap-2\">\n                  <ui-input\n                    id=\"waitlist-email\"\n                    [value]=\"email()\"\n                    (valueChange)=\"email.set($event)\"\n                    type=\"email\"\n                    required\n                    placeholder=\"you@company.com\"\n                    autoComplete=\"email\"\n                  />\n                  <button ui-button type=\"submit\" class=\"shrink-0\">Join</button>\n                </div>\n                <p class=\"text-muted-foreground mt-2 text-xs\">\n                  No product emails while you wait — one message when your cohort opens.\n                </p>\n              </form>\n            } @else {\n              <div aria-live=\"polite\">\n                <div class=\"flex items-baseline justify-between gap-4\">\n                  <div>\n                    <p class=\"text-muted-foreground text-xs tracking-wide uppercase\">Your position</p>\n                    <p class=\"font-display mt-1 text-3xl font-bold tracking-tight\">#{{ position.toLocaleString() }}</p>\n                  </div>\n                  <p class=\"text-muted-foreground text-right text-xs\">\n                    Next cohort opens<br />at #{{ nextTierAt.toLocaleString() }}\n                  </p>\n                </div>\n\n                <div ui-progress [value]=\"progress()\" class=\"mt-4 h-1.5\" aria-label=\"Progress to the next cohort\"></div>\n\n                <ui-separator class=\"my-5\" />\n\n                <p class=\"text-sm font-medium\">Move up the list</p>\n                <p class=\"text-muted-foreground mt-1 text-xs\">\n                  Each signup through your link moves you forward 12 places.\n                </p>\n                <div class=\"border-border bg-muted/30 mt-3 flex items-stretch overflow-hidden rounded-md border\">\n                  <code class=\"min-w-0 flex-1 truncate px-3 py-2 font-mono text-xs\">{{ referralLink }}</code>\n                  <button\n                    ui-button\n                    variant=\"ghost\"\n                    class=\"border-border h-auto shrink-0 rounded-none border-l px-3\"\n                    aria-label=\"Copy referral link\"\n                    (click)=\"copyLink()\"\n                  >\n                    @if (copied()) {\n                      <lucide-icon [img]=\"Check\" class=\"text-success size-4\" aria-hidden=\"true\" />\n                    } @else {\n                      <lucide-icon [img]=\"Copy\" class=\"size-4\" aria-hidden=\"true\" />\n                    }\n                  </button>\n                </div>\n              </div>\n            }\n          </div>\n        </div>\n      </div>\n    </section>\n  `,\n})\nexport class UiWaitlistReferralPositionComponent {\n  protected readonly Check = Check\n  protected readonly Copy = Copy\n  protected readonly Users = Users\n\n  protected readonly referralLink = referralLink\n\n  @Input() set position(v: number) {\n    this.positionSig.set(v ?? 1284)\n  }\n  get position(): number {\n    return this.positionSig()\n  }\n  @Input() set nextTierAt(v: number) {\n    this.nextTierAtSig.set(v ?? 1000)\n  }\n  get nextTierAt(): number {\n    return this.nextTierAtSig()\n  }\n  @Input('class') className?: string\n\n  private readonly positionSig = signal(1284)\n  private readonly nextTierAtSig = signal(1000)\n\n  readonly email = signal('')\n  readonly joined = signal(false)\n  readonly copied = signal(false)\n  private copyTimer: ReturnType<typeof setTimeout> | undefined\n\n  constructor() {\n    inject(DestroyRef).onDestroy(() => clearTimeout(this.copyTimer))\n  }\n\n  // How far from the current position to the next access tier, as a percentage.\n  readonly progress = computed(() => {\n    const position = this.positionSig()\n    const span = position - this.nextTierAtSig()\n    if (span <= 0) return 100\n    return Math.round(((position - span) / position) * 100)\n  })\n\n  get rootClass(): string {\n    return cn('bg-background', this.className)\n  }\n\n  join(event: Event): void {\n    event.preventDefault()\n    if (!this.email().trim()) return\n    this.joined.set(true)\n  }\n\n  async copyLink(): Promise<void> {\n    try {\n      await navigator.clipboard.writeText(referralLink)\n      this.copied.set(true)\n      clearTimeout(this.copyTimer)\n      this.copyTimer = setTimeout(() => this.copied.set(false), 1600)\n    } catch {\n      // Clipboard blocked: the link stays visible and selectable.\n    }\n  }\n}\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/waitlist-referral-position/waitlist-referral-position.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/waitlist-referral-position/index.ts",
      "content": "export { UiWaitlistReferralPositionComponent } from './waitlist-referral-position.component'\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/waitlist-referral-position/index.ts"
    }
  ],
  "dependencies": [
    "lucide-angular"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/angular/badge.json",
    "https://uipkge.dev/r/angular/button.json",
    "https://uipkge.dev/r/angular/card.json",
    "https://uipkge.dev/r/angular/input.json",
    "https://uipkge.dev/r/angular/progress.json",
    "https://uipkge.dev/r/angular/separator.json"
  ],
  "description": "Waitlist capture that swaps on submit to show queue position, the referral link that moves it, and a progress meter to the next tier of early access.",
  "categories": [
    "marketing"
  ]
}