{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "newsletter",
  "title": "Newsletter",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-angular/blocks/newsletter/newsletter-inline-band.component.ts",
      "content": "// Slim inline signup band (quartet member): inline field, submit, and a status row reserved\n// from first paint so confirming never shifts the page. Port of the React block 1:1.\nimport { ChangeDetectionStrategy, Component, Input, signal } from '@angular/core'\nimport { Check, LucideAngularModule, Mail } from 'lucide-angular'\nimport { UiBadgeComponent } from '@/ui/badge/badge.component'\nimport { UiButtonComponent } from '@/ui/button/button.component'\nimport { UiInputComponent } from '@/ui/input/input.component'\n\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-newsletter-inline-band, [ui-newsletter-inline-band]',\n  standalone: true,\n  // The block renders its own root <section>: keep the host out of layout.\n  host: { '[attr.class]': '\"contents\"' },\n  imports: [LucideAngularModule, UiBadgeComponent, UiButtonComponent, UiInputComponent],\n  template: `\n    <section data-slot=\"newsletter-inline-band\" class=\"border-border bg-muted/40 border-y\">\n      <div class=\"mx-auto flex max-w-6xl flex-col gap-6 px-6 py-10 lg:flex-row lg:items-center lg:gap-12\">\n        <div class=\"min-w-0 lg:flex-1\">\n          <div class=\"flex items-center gap-2\">\n            <lucide-icon [img]=\"Mail\" class=\"text-muted-foreground size-4\" aria-hidden=\"true\" />\n            <span ui-badge variant=\"secondary\">Monthly</span>\n          </div>\n          <p class=\"mt-2.5 text-lg leading-snug font-medium text-balance\">\n            Engineering notes on metric modelling, access control, and warehouse cost.\n          </p>\n          <p class=\"text-muted-foreground mt-1.5 text-sm\">\n            Read by 8,400 data and finance engineers. One email a month, no product announcements.\n          </p>\n        </div>\n\n        <form class=\"lg:w-[26rem] lg:shrink-0\" (submit)=\"subscribe($event)\">\n          <div class=\"flex gap-2\">\n            <ui-input\n              [value]=\"email()\"\n              (valueChange)=\"email.set($event)\"\n              type=\"email\"\n              required\n              placeholder=\"you@company.com\"\n              autoComplete=\"email\"\n              aria-label=\"Email address\"\n            />\n            <button ui-button type=\"submit\" class=\"shrink-0\">Subscribe</button>\n          </div>\n          <!-- Status row is reserved from first paint, so confirming does not\n               nudge the band's height and shift the page under the cursor. -->\n          <p class=\"mt-2 min-h-5 text-xs\" aria-live=\"polite\">\n            @if (submitted()) {\n              <span class=\"text-success inline-flex items-center gap-1.5\">\n                <lucide-icon [img]=\"Check\" class=\"size-3\" aria-hidden=\"true\" />\n                Check your inbox to confirm.\n              </span>\n            } @else {\n              <span class=\"text-muted-foreground\">Unsubscribe in one click. We never share the list.</span>\n            }\n          </p>\n        </form>\n      </div>\n    </section>\n  `,\n})\nexport class UiNewsletterInlineBandComponent {\n  protected readonly Check = Check\n  protected readonly Mail = Mail\n\n  @Input('class') className?: string\n\n  readonly email = signal('')\n  readonly submitted = signal(false)\n\n  subscribe(event: Event): void {\n    event.preventDefault()\n    if (!this.email().trim()) return\n    this.submitted.set(true)\n  }\n}\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/newsletter/newsletter-inline-band.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/newsletter/newsletter-issue-archive.component.ts",
      "content": "// Substack/Beehiiv style publication archive: hero subscribe form, featured issue\n// spotlight, category pills with counts, live search, edition cards, and an RSS footer.\n// Port of the React block 1:1.\nimport { ChangeDetectionStrategy, Component, Input, computed, signal } from '@angular/core'\nimport {\n  ArrowRight,\n  Bookmark,\n  Calendar,\n  Check,\n  Clock,\n  Heart,\n  LucideAngularModule,\n  MessageSquare,\n  Newspaper,\n  Rss,\n  Search,\n  Users,\n  X,\n  Zap,\n} from 'lucide-angular'\nimport { cn } from '@/lib/utils'\nimport { UiBadgeComponent } from '@/ui/badge/badge.component'\nimport { UiButtonComponent } from '@/ui/button/button.component'\nimport {\n  UiCardComponent,\n  UiCardContentComponent,\n  UiCardDescriptionComponent,\n  UiCardFooterComponent,\n  UiCardHeaderComponent,\n  UiCardTitleComponent,\n} from '@/ui/card/card.component'\nimport { UiInputComponent } from '@/ui/input/input.component'\n\nexport interface NewsletterIssue {\n  id: string\n  issueNumber: number\n  volumeDate: string\n  title: string\n  excerpt: string\n  category: 'Architecture' | 'Design Tokens' | 'Performance' | 'Interviews'\n  tags: string[]\n  readTime: string\n  date: string\n  commentsCount: number\n  likesCount: number\n  featured?: boolean\n}\n\nconst categories = ['All Issues', 'Architecture', 'Design Tokens', 'Performance', 'Interviews'] as const\n\nconst featuredIssue: NewsletterIssue = {\n  id: 'issue-43',\n  issueNumber: 43,\n  volumeDate: 'Aug 2026',\n  title: 'The Death of npm Component Libraries: Why Unbundled Registry Architecture Won',\n  excerpt:\n    'How unbundled UI registries replaced monolithic node_modules packages, eliminated dependency hell, and gave full code ownership back to frontend engineering teams across the industry.',\n  category: 'Architecture',\n  tags: ['#architecture', '#registries', '#future-of-web', '#tooling'],\n  readTime: '9 min read',\n  date: 'Aug 25, 2026',\n  commentsCount: 128,\n  likesCount: 1420,\n  featured: true,\n}\n\nconst archiveIssues: NewsletterIssue[] = [\n  {\n    id: 'issue-42',\n    issueNumber: 42,\n    volumeDate: 'Aug 2026',\n    title: 'Why Zero-Dependency Registries Are Winning',\n    excerpt:\n      'A deep dive into why enterprise engineering teams are abandoning monolithic UI packages in favor of composable own-your-code registry models that eliminate breaking upgrade cascades.',\n    category: 'Architecture',\n    tags: ['#architecture', '#web-performance', '#dx'],\n    readTime: '6 min read',\n    date: 'Aug 18, 2026',\n    commentsCount: 84,\n    likesCount: 642,\n  },\n  {\n    id: 'issue-41',\n    issueNumber: 41,\n    volumeDate: 'Aug 2026',\n    title: 'Mastering Tailwind v4: OKLCH Colors & Dynamic Themes',\n    excerpt:\n      'How modern CSS color spaces and inline theme definitions unlock mathematically perceptually uniform light/dark transitions without CSS variables explosion.',\n    category: 'Design Tokens',\n    tags: ['#design-tokens', '#css', '#theming'],\n    readTime: '8 min read',\n    date: 'Aug 11, 2026',\n    commentsCount: 56,\n    likesCount: 519,\n  },\n  {\n    id: 'issue-40',\n    issueNumber: 40,\n    volumeDate: 'Aug 2026',\n    title: 'Zero-CLS Island Hydration in Modern Web Frameworks',\n    excerpt:\n      'Eliminating Cumulative Layout Shift when server-rendered islands hydrate in Astro and Nuxt. Practical patterns distilled from serving 1M+ monthly pageviews.',\n    category: 'Performance',\n    tags: ['#performance', '#core-web-vitals', '#astro'],\n    readTime: '5 min read',\n    date: 'Aug 04, 2026',\n    commentsCount: 92,\n    likesCount: 730,\n  },\n  {\n    id: 'issue-39',\n    issueNumber: 39,\n    volumeDate: 'Jul 2026',\n    title: 'Interview: Building High-Craft Interfaces with Paco Coursey',\n    excerpt:\n      'The creator of cmdk and sonner breaks down spring physics, popover placement math, and why 60fps micro-interactions define software brand trust.',\n    category: 'Interviews',\n    tags: ['#interviews', '#craft', '#animation'],\n    readTime: '11 min read',\n    date: 'Jul 28, 2026',\n    commentsCount: 147,\n    likesCount: 1205,\n  },\n  {\n    id: 'issue-38',\n    issueNumber: 38,\n    volumeDate: 'Jul 2026',\n    title: 'Building Resilient Component APIs with Polymorphic Slots',\n    excerpt:\n      'Why the asChild composition pattern beat standard prop drilling for accessible primitives, and how Reka UI implements headless polymorphism without DOM overhead.',\n    category: 'Architecture',\n    tags: ['#architecture', '#vue', '#reka-ui'],\n    readTime: '7 min read',\n    date: 'Jul 21, 2026',\n    commentsCount: 63,\n    likesCount: 488,\n  },\n  {\n    id: 'issue-37',\n    issueNumber: 37,\n    volumeDate: 'Jul 2026',\n    title: 'Designing for Multi-Tenant White-Labeling at Scale',\n    excerpt:\n      'Architecting a headless token graph that dynamically maps corporate brand identities and custom color palettes across 500+ enterprise subdomains in real time.',\n    category: 'Design Tokens',\n    tags: ['#design-tokens', '#architecture', '#enterprise'],\n    readTime: '9 min read',\n    date: 'Jul 14, 2026',\n    commentsCount: 71,\n    likesCount: 610,\n  },\n]\n\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-newsletter-issue-archive, [ui-newsletter-issue-archive]',\n  standalone: true,\n  // The block renders its own root <div>: keep the host out of layout.\n  host: { '[attr.class]': '\"contents\"' },\n  imports: [\n    LucideAngularModule,\n    UiBadgeComponent,\n    UiButtonComponent,\n    UiCardComponent,\n    UiCardContentComponent,\n    UiCardDescriptionComponent,\n    UiCardFooterComponent,\n    UiCardHeaderComponent,\n    UiCardTitleComponent,\n    UiInputComponent,\n  ],\n  template: `\n    <ng-template #searchIcon><lucide-icon [img]=\"Search\" class=\"size-4\" aria-hidden=\"true\" /></ng-template>\n    <div data-slot=\"newsletter-issue-archive\" [class]=\"rootClass\">\n      <!-- Newsletter Header Hero -->\n      <header class=\"mx-auto max-w-3xl space-y-4 text-center\">\n        <div\n          class=\"border-primary/20 bg-primary/5 text-primary inline-flex items-center gap-1.5 rounded-full border px-3.5 py-1 text-xs font-medium shadow-xs\"\n        >\n          <lucide-icon [img]=\"Users\" class=\"size-3.5 shrink-0\" aria-hidden=\"true\" />\n          <span>{{ subscriberCount }}</span>\n        </div>\n\n        <h1 class=\"text-foreground text-3xl font-bold tracking-tight sm:text-4xl lg:text-5xl\">{{ publicationName }}</h1>\n\n        <p class=\"text-muted-foreground mx-auto max-w-2xl text-base leading-relaxed sm:text-lg\">{{ subtitle }}</p>\n\n        <!-- Subscribe Form -->\n        <div class=\"mx-auto w-full max-w-md pt-2\">\n          @if (!isSubscribed()) {\n            <div>\n              <form class=\"flex flex-col gap-2 sm:flex-row sm:gap-0\" (submit)=\"handleSubscribe($event)\">\n                <ui-input\n                  id=\"newsletter-archive-email-angular\"\n                  [value]=\"email()\"\n                  (valueChange)=\"email.set($event)\"\n                  type=\"email\"\n                  placeholder=\"Enter your work email...\"\n                  required\n                  class=\"bg-card h-11 text-sm shadow-xs sm:rounded-r-none\"\n                  aria-label=\"Work email address\"\n                />\n                <button ui-button type=\"submit\" class=\"h-11 shrink-0 px-6 font-medium sm:rounded-l-none\">\n                  Subscribe for Free\n                </button>\n              </form>\n              <p\n                class=\"text-muted-foreground mt-2.5 flex flex-wrap items-center justify-center gap-x-2 gap-y-1 text-xs\"\n              >\n                <span>Free weekly issues</span>\n                <span>Zero spam</span>\n                <span>1-click unsubscribe</span>\n              </p>\n            </div>\n          } @else {\n            <div\n              class=\"border-success/30 bg-success/10 text-success inline-flex items-center gap-2.5 rounded-lg border px-4 py-3 text-sm font-medium shadow-xs\"\n            >\n              <lucide-icon [img]=\"Check\" class=\"size-4 shrink-0\" aria-hidden=\"true\" />\n              <span>You&rsquo;re subscribed! Check your inbox for confirmation.</span>\n            </div>\n          }\n        </div>\n      </header>\n\n      <!-- Featured Issue Spotlight Card -->\n      <section aria-labelledby=\"featured-issue-heading-angular\" class=\"space-y-3\">\n        <div class=\"flex items-center justify-between px-1\">\n          <h2\n            id=\"featured-issue-heading-angular\"\n            class=\"text-muted-foreground text-xs font-semibold tracking-wider uppercase\"\n          >\n            Pinned Spotlight Edition\n          </h2>\n          <span ui-badge variant=\"outline\" class=\"text-primary border-primary/30 gap-1 text-xs\">\n            <lucide-icon [img]=\"Zap\" class=\"size-3\" aria-hidden=\"true\" /> Latest Release\n          </span>\n        </div>\n\n        <div\n          ui-card\n          class=\"border-border/80 bg-card hover:border-primary/40 overflow-hidden shadow-xs transition-colors\"\n        >\n          <div class=\"grid grid-cols-1 gap-6 p-6 sm:p-8 lg:grid-cols-12\">\n            <!-- Text Content -->\n            <div class=\"flex flex-col justify-between space-y-4 lg:col-span-7\">\n              <div class=\"space-y-3\">\n                <div class=\"flex flex-wrap items-center gap-2\">\n                  <span ui-badge variant=\"default\" class=\"font-mono text-xs\">\n                    Issue #{{ featured().issueNumber }} &middot; {{ featured().volumeDate }}\n                  </span>\n                  <span ui-badge variant=\"secondary\" class=\"text-xs\">{{ featured().category }}</span>\n                  <span class=\"text-muted-foreground ml-auto inline-flex items-center gap-1 text-xs\">\n                    <lucide-icon [img]=\"Clock\" class=\"size-3\" aria-hidden=\"true\" />\n                    {{ featured().readTime }}\n                  </span>\n                </div>\n\n                <h3\n                  class=\"text-foreground hover:text-primary cursor-pointer text-xl font-bold tracking-tight transition-colors sm:text-2xl\"\n                >\n                  {{ featured().title }}\n                </h3>\n\n                <p class=\"text-muted-foreground text-sm leading-relaxed sm:text-base\">{{ featured().excerpt }}</p>\n              </div>\n\n              <div class=\"space-y-4 pt-2\">\n                <div class=\"flex flex-wrap gap-1.5\">\n                  @for (tag of featured().tags; track tag) {\n                    <span ui-badge variant=\"outline\" class=\"text-muted-foreground font-mono text-xs font-normal\">\n                      {{ tag }}\n                    </span>\n                  }\n                </div>\n\n                <div class=\"border-border/60 flex flex-wrap items-center justify-between gap-4 border-t pt-3\">\n                  <div class=\"text-muted-foreground flex items-center gap-4 text-xs\">\n                    <span class=\"inline-flex items-center gap-1.5\">\n                      <lucide-icon [img]=\"Calendar\" class=\"size-3.5\" aria-hidden=\"true\" />\n                      {{ featured().date }}\n                    </span>\n                    <span class=\"inline-flex items-center gap-1.5\">\n                      <lucide-icon [img]=\"MessageSquare\" class=\"size-3.5\" aria-hidden=\"true\" />\n                      {{ featured().commentsCount }} comments\n                    </span>\n                    <span class=\"inline-flex items-center gap-1.5\">\n                      <lucide-icon [img]=\"Heart\" class=\"size-3.5\" aria-hidden=\"true\" />\n                      {{ featured().likesCount }}\n                    </span>\n                  </div>\n\n                  <button ui-button variant=\"default\" size=\"sm\" class=\"group gap-1.5 font-medium\">\n                    Read Issue\n                    <lucide-icon\n                      [img]=\"ArrowRight\"\n                      class=\"size-3.5 transition-transform group-hover:translate-x-0.5\"\n                      aria-hidden=\"true\"\n                    />\n                  </button>\n                </div>\n              </div>\n            </div>\n\n            <!-- Schematic Cover Illustration -->\n            <div class=\"flex items-center lg:col-span-5\">\n              <div class=\"border-border/80 bg-muted/40 w-full rounded-lg border p-4 font-mono text-xs sm:p-5\">\n                <div class=\"border-border/60 mb-3 flex items-center justify-between border-b pb-2.5\">\n                  <div class=\"flex items-center gap-1.5\">\n                    <span class=\"bg-destructive/60 inline-block size-2.5 rounded-full\"></span>\n                    <span class=\"bg-warning/60 inline-block size-2.5 rounded-full\"></span>\n                    <span class=\"bg-success/60 inline-block size-2.5 rounded-full\"></span>\n                  </div>\n                  <span class=\"text-muted-foreground text-xs\">registry-teardown.ts</span>\n                </div>\n                <div class=\"text-muted-foreground space-y-2\">\n                  <div class=\"text-primary text-xs font-medium\">// Own your component code</div>\n                  <div><span class=\"text-foreground\">$</span> npx shadcn&#64;latest add &#64;uipkge/card</div>\n                  <div class=\"text-success\">&#10003; Fetched raw TypeScript TSX</div>\n                  <div class=\"text-success\">&#10003; Merged local Tailwind v4 tokens</div>\n                  <div class=\"text-muted-foreground/80 pt-1\">// Zero runtime package lock-in</div>\n                </div>\n                <div class=\"border-border/40 mt-4 flex flex-wrap gap-1.5 border-t pt-3\">\n                  <span class=\"bg-background text-foreground border-border rounded border px-2 py-0.5 text-xs\">\n                    Zero npm deps\n                  </span>\n                  <span class=\"bg-background text-foreground border-border rounded border px-2 py-0.5 text-xs\">\n                    Tailwind v4\n                  </span>\n                  <span class=\"bg-background text-foreground border-border rounded border px-2 py-0.5 text-xs\"\n                    >OKLCH</span\n                  >\n                </div>\n              </div>\n            </div>\n          </div>\n        </div>\n      </section>\n\n      <!-- Search and Filter Bar -->\n      <section aria-label=\"Search and filter archive\" class=\"space-y-4\">\n        <div class=\"flex flex-col items-stretch justify-between gap-4 md:flex-row md:items-center\">\n          <!-- Search Input -->\n          <div class=\"w-full md:max-w-md\">\n            <ui-input\n              [value]=\"searchQuery()\"\n              (valueChange)=\"searchQuery.set($event)\"\n              [prefixIcon]=\"searchIcon\"\n              allowClear\n              type=\"search\"\n              placeholder=\"Search issues by title, topic, or tag...\"\n              class=\"bg-card shadow-xs\"\n              aria-label=\"Search archive issues\"\n            />\n          </div>\n\n          <!-- Result Counter -->\n          <div class=\"text-muted-foreground flex items-center justify-between gap-3 text-xs md:justify-end\">\n            <span\n              >Showing {{ filteredIssues().length }} {{ filteredIssues().length === 1 ? 'edition' : 'editions' }}</span\n            >\n            @if (searchQuery() || selectedCategory() !== 'All Issues') {\n              <button\n                type=\"button\"\n                class=\"text-primary focus-visible:ring-ring inline-flex items-center gap-1 rounded font-medium hover:underline focus-visible:ring-1 focus-visible:outline-none\"\n                (click)=\"clearFilters()\"\n              >\n                Reset filters\n                <lucide-icon [img]=\"X\" class=\"size-3\" aria-hidden=\"true\" />\n              </button>\n            }\n          </div>\n        </div>\n\n        <!-- Category Filter Pills -->\n        <div class=\"flex flex-wrap items-center gap-1.5\" role=\"tablist\" aria-label=\"Filter by category\">\n          @for (cat of categories; track cat) {\n            <button\n              type=\"button\"\n              role=\"tab\"\n              [attr.aria-selected]=\"selectedCategory() === cat\"\n              [class]=\"pillClass(cat)\"\n              (click)=\"selectedCategory.set(cat)\"\n            >\n              <span>{{ cat }}</span>\n              <span [class]=\"pillCountClass(cat)\">{{ categoryCount(cat) }}</span>\n            </button>\n          }\n        </div>\n      </section>\n\n      <!-- Issue Archive List -->\n      <section aria-label=\"Archive issue list\">\n        @if (filteredIssues().length > 0) {\n          <div class=\"grid grid-cols-1 gap-5 md:grid-cols-2\">\n            @for (issue of filteredIssues(); track issue.id) {\n              <div\n                ui-card\n                class=\"group border-border/70 bg-card hover:border-primary/40 flex flex-col justify-between transition-colors hover:shadow-xs\"\n              >\n                <div ui-card-header class=\"pb-3\">\n                  <div class=\"flex items-center justify-between gap-2\">\n                    <span ui-badge variant=\"outline\" class=\"font-mono text-xs\">\n                      Issue #{{ issue.issueNumber }} &middot; {{ issue.volumeDate }}\n                    </span>\n                    <span class=\"text-muted-foreground inline-flex items-center gap-1 text-xs\">\n                      <lucide-icon [img]=\"Clock\" class=\"size-3\" aria-hidden=\"true\" />\n                      {{ issue.readTime }}\n                    </span>\n                  </div>\n\n                  <h3\n                    ui-card-title\n                    class=\"group-hover:text-primary cursor-pointer pt-2 text-base leading-snug font-semibold tracking-tight transition-colors sm:text-lg\"\n                  >\n                    {{ issue.title }}\n                  </h3>\n\n                  <p ui-card-description class=\"text-muted-foreground mt-1.5 line-clamp-3 text-sm leading-relaxed\">\n                    {{ issue.excerpt }}\n                  </p>\n                </div>\n\n                <div ui-card-content class=\"py-0\">\n                  <div class=\"flex flex-wrap gap-1.5 pt-1\">\n                    @for (tag of issue.tags; track tag) {\n                      <span ui-badge variant=\"secondary\" class=\"font-mono text-xs font-normal\">{{ tag }}</span>\n                    }\n                  </div>\n                </div>\n\n                <div\n                  ui-card-footer\n                  class=\"border-border/60 text-muted-foreground mt-4 flex items-center justify-between border-t pt-4 text-xs\"\n                >\n                  <div class=\"flex items-center gap-3\">\n                    <span class=\"inline-flex items-center gap-1\">\n                      <lucide-icon [img]=\"Calendar\" class=\"size-3.5\" aria-hidden=\"true\" />\n                      {{ issue.date }}\n                    </span>\n                    <span class=\"inline-flex items-center gap-1\">\n                      <lucide-icon [img]=\"MessageSquare\" class=\"size-3.5\" aria-hidden=\"true\" />\n                      {{ issue.commentsCount }}\n                    </span>\n                    <span class=\"inline-flex items-center gap-1\">\n                      <lucide-icon [img]=\"Heart\" class=\"size-3.5\" aria-hidden=\"true\" />\n                      {{ issue.likesCount }}\n                    </span>\n                  </div>\n\n                  <a\n                    href=\"#read\"\n                    class=\"text-foreground group-hover:text-primary inline-flex items-center gap-1 font-medium transition-colors focus-visible:underline focus-visible:outline-none\"\n                  >\n                    <span>Read Issue</span>\n                    <lucide-icon\n                      [img]=\"ArrowRight\"\n                      class=\"size-3.5 transition-transform group-hover:translate-x-0.5\"\n                      aria-hidden=\"true\"\n                    />\n                  </a>\n                </div>\n              </div>\n            }\n          </div>\n        } @else {\n          <!-- Empty State -->\n          <div class=\"border-border bg-card/50 space-y-3 rounded-xl border border-dashed p-10 text-center\">\n            <lucide-icon [img]=\"Newspaper\" class=\"text-muted-foreground/60 mx-auto size-8\" aria-hidden=\"true\" />\n            <h3 class=\"text-foreground text-base font-semibold\">No issues found</h3>\n            <p class=\"text-muted-foreground mx-auto max-w-sm text-sm\">\n              No newsletter issues matched &ldquo;{{ searchQuery() }}&rdquo; in {{ selectedCategory() }}.\n            </p>\n            <div class=\"pt-2\">\n              <button ui-button variant=\"outline\" size=\"sm\" (click)=\"clearFilters()\">Clear search &amp; filters</button>\n            </div>\n          </div>\n        }\n      </section>\n\n      <!-- Publication Cadence & RSS Bar -->\n      <footer\n        class=\"border-border/80 bg-muted/30 flex flex-col items-center justify-between gap-4 rounded-xl border p-6 text-center shadow-xs sm:flex-row sm:text-left\"\n      >\n        <div>\n          <h3 class=\"text-foreground text-sm font-semibold\">Never miss a teardown</h3>\n          <p class=\"text-muted-foreground mt-0.5 text-xs\">\n            Published every Tuesday morning. Zero sponsor promotions, purely deep technical architecture.\n          </p>\n        </div>\n\n        <div class=\"flex shrink-0 items-center gap-2.5\">\n          <button ui-button variant=\"outline\" size=\"sm\" class=\"gap-1.5 text-xs\">\n            <lucide-icon [img]=\"Rss\" class=\"size-3.5\" aria-hidden=\"true\" />\n            RSS Feed\n          </button>\n          <button ui-button variant=\"default\" size=\"sm\" class=\"gap-1.5 text-xs\" (click)=\"focusEmail()\">\n            <lucide-icon [img]=\"Bookmark\" class=\"size-3.5\" aria-hidden=\"true\" />\n            Join Newsletter\n          </button>\n        </div>\n      </footer>\n    </div>\n  `,\n})\nexport class UiNewsletterIssueArchiveComponent {\n  protected readonly ArrowRight = ArrowRight\n  protected readonly Bookmark = Bookmark\n  protected readonly Calendar = Calendar\n  protected readonly Check = Check\n  protected readonly Clock = Clock\n  protected readonly Heart = Heart\n  protected readonly MessageSquare = MessageSquare\n  protected readonly Newspaper = Newspaper\n  protected readonly Rss = Rss\n  protected readonly Search = Search\n  protected readonly Users = Users\n  protected readonly X = X\n  protected readonly Zap = Zap\n\n  protected readonly categories = categories\n  protected readonly featured = signal(featuredIssue)\n\n  @Input() publicationName = 'The Unbundled Engineer'\n  @Input() subtitle =\n    'Weekly architectural teardowns of modern design systems, web performance, and component registries.'\n  @Input() subscriberCount = '42,500+ Subscribers · Top 1% on Substack'\n  @Input('class') className?: string\n\n  readonly email = signal('')\n  readonly isSubscribed = signal(false)\n  readonly searchQuery = signal('')\n  readonly selectedCategory = signal<string>('All Issues')\n\n  readonly filteredIssues = computed(() => {\n    const query = this.searchQuery().trim().toLowerCase()\n    const selected = this.selectedCategory()\n    return archiveIssues.filter((issue) => {\n      const matchesCategory = selected === 'All Issues' || issue.category === selected\n      const matchesSearch =\n        !query ||\n        issue.title.toLowerCase().includes(query) ||\n        issue.excerpt.toLowerCase().includes(query) ||\n        issue.tags.some((tag) => tag.toLowerCase().includes(query)) ||\n        `issue #${issue.issueNumber}`.toLowerCase().includes(query)\n      return matchesCategory && matchesSearch\n    })\n  })\n\n  get rootClass(): string {\n    return cn('mx-auto w-full max-w-5xl space-y-12 px-4 py-8 sm:px-6 sm:py-12', this.className)\n  }\n\n  pillClass(cat: string): string {\n    const isSelected = this.selectedCategory() === cat\n    return cn(\n      'focus-visible:ring-ring inline-flex items-center gap-1.5 rounded-full px-3.5 py-1.5 text-xs transition-colors focus-visible:ring-2 focus-visible:outline-none',\n      isSelected\n        ? 'bg-primary text-primary-foreground font-medium shadow-xs'\n        : 'bg-muted/70 text-muted-foreground hover:bg-muted hover:text-foreground',\n    )\n  }\n\n  pillCountClass(cat: string): string {\n    const isSelected = this.selectedCategory() === cat\n    return cn(\n      'py-0.2 rounded-full px-1.5 text-xs',\n      isSelected ? 'bg-primary-foreground/20 text-primary-foreground' : 'bg-background/80 text-muted-foreground',\n    )\n  }\n\n  categoryCount(cat: string): number {\n    if (cat === 'All Issues') return archiveIssues.length\n    return archiveIssues.filter((issue) => issue.category === cat).length\n  }\n\n  clearFilters(): void {\n    this.searchQuery.set('')\n    this.selectedCategory.set('All Issues')\n  }\n\n  handleSubscribe(event: Event): void {\n    event.preventDefault()\n    if (!this.email() || !this.email().includes('@')) return\n    this.isSubscribed.set(true)\n  }\n\n  focusEmail(): void {\n    document.getElementById('newsletter-archive-email-angular')?.focus()\n  }\n}\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/newsletter/newsletter-issue-archive.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/newsletter/newsletter-signup.component.ts",
      "content": "// Engineering dispatch signup: channel radio cards, live email form with a submitting\n// beat, a success view, and an archive sampler with clickable issues. Port of the React block 1:1.\nimport { ChangeDetectionStrategy, Component, Input, signal } from '@angular/core'\nimport { ArrowRight, BellRing, BookOpen, Check, LucideAngularModule, Mail, Send } from 'lucide-angular'\nimport { cn } from '@/lib/utils'\nimport { UiBadgeComponent } from '@/ui/badge/badge.component'\nimport { UiButtonComponent } from '@/ui/button/button.component'\nimport {\n  UiCardComponent,\n  UiCardContentComponent,\n  UiCardDescriptionComponent,\n  UiCardHeaderComponent,\n  UiCardTitleComponent,\n} from '@/ui/card/card.component'\nimport { UiInputComponent } from '@/ui/input/input.component'\nimport { UiSeparatorComponent } from '@/ui/separator/separator.component'\n\nexport type NewsletterSignupVariant = 'centered' | 'split'\n\ninterface ChannelOption {\n  id: string\n  title: string\n  frequency: string\n  description: string\n  badge: string\n}\n\ninterface RecentIssue {\n  id: number\n  date: string\n  title: string\n  reads: string\n  tags: string[]\n  snippet: string\n}\n\nconst channels: ChannelOption[] = [\n  {\n    id: 'architecture',\n    title: 'Architecture & Token Deep Dives',\n    frequency: 'Bi-Weekly',\n    description: 'Technical breakdowns on OKLCH color science, headless AST transformers, and zero-lockin paradigms.',\n    badge: 'Flagship',\n  },\n  {\n    id: 'releases',\n    title: 'Registry Ship Notes & RFCs',\n    frequency: 'Monthly',\n    description: 'Direct changelogs, new workbench releases, breaking primitive refactors, and roadmap discussions.',\n    badge: 'Changelog',\n  },\n  {\n    id: 'security',\n    title: 'Security & Dependency Bulletins',\n    frequency: 'As-Needed',\n    description: 'Zero-day vulnerability alerts, upstream headless primitive fixes, and patch notifications.',\n    badge: 'Critical',\n  },\n]\n\nconst recentIssues: RecentIssue[] = [\n  {\n    id: 48,\n    date: 'Aug 2026',\n    title: 'The Death of Monolithic npm UI Packages',\n    reads: '4 min read',\n    tags: ['Architecture', 'Registry', 'AST'],\n    snippet:\n      'Why the next decade of frontend engineering belongs to unbundled registries where the consumer owns the source code.',\n  },\n  {\n    id: 47,\n    date: 'Jul 2026',\n    title: 'OKLCH Theming in Tailwind CSS v4',\n    reads: '6 min read',\n    tags: ['Tailwind v4', 'Color Science', 'Tokens'],\n    snippet:\n      'Mastering perceptual lightness, chroma gamut mapping, and dynamic theme switching without runtime CSS bloat.',\n  },\n]\n\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-newsletter-signup, [ui-newsletter-signup]',\n  standalone: true,\n  // The block renders its own root <section>: keep the host out of layout.\n  host: { '[attr.class]': '\"contents\"' },\n  imports: [\n    LucideAngularModule,\n    UiBadgeComponent,\n    UiButtonComponent,\n    UiCardComponent,\n    UiCardContentComponent,\n    UiCardDescriptionComponent,\n    UiCardHeaderComponent,\n    UiCardTitleComponent,\n    UiInputComponent,\n    UiSeparatorComponent,\n  ],\n  template: `\n    <section data-slot=\"newsletter-signup\" class=\"bg-background relative overflow-hidden px-4 py-16 sm:px-6 lg:px-8\">\n      <div class=\"mx-auto max-w-6xl space-y-10\">\n        <!-- Section Header -->\n        <div class=\"max-w-3xl space-y-3\">\n          <span ui-badge variant=\"secondary\" class=\"gap-1.5 px-3 py-1 font-mono text-xs shadow-xs\">\n            <lucide-icon [img]=\"Mail\" class=\"text-primary size-3.5\" />\n            Engineering Dispatch & Architecture Radar\n          </span>\n          <h2 class=\"text-foreground text-3xl font-bold tracking-tight sm:text-4xl\">\n            Ship notes, not marketing fluff.\n          </h2>\n          <p class=\"text-muted-foreground text-sm leading-relaxed sm:text-base\">\n            Join 18,400+ frontend architects, design engineers, and systems builders. Plain-text insights into component\n            architecture, token systems, and dual-framework engineering.\n          </p>\n        </div>\n\n        <!-- Main Layout -->\n        <div class=\"grid grid-cols-1 items-start gap-8 lg:grid-cols-12\">\n          <!-- Left Column: Channel Selector & Live Dispatch Form (7 Cols) -->\n          <div class=\"space-y-6 lg:col-span-7\">\n            <div ui-card class=\"border-border bg-card overflow-hidden shadow-sm\">\n              <div ui-card-header class=\"bg-muted/20 border-border/60 border-b pb-4\">\n                <div class=\"flex items-center justify-between\">\n                  <h3 ui-card-title class=\"flex items-center gap-2 text-base font-semibold\">\n                    <lucide-icon [img]=\"BellRing\" class=\"text-primary size-4\" />\n                    Select Dispatch Track\n                  </h3>\n                  <span class=\"text-muted-foreground font-mono text-xs\">Zero Spam Guarantee</span>\n                </div>\n                <p ui-card-description class=\"text-xs\">\n                  Choose the telemetry streams you want delivered to your inbox.\n                </p>\n              </div>\n\n              <div ui-card-content class=\"space-y-5 p-6\">\n                <!-- Channel Radio Cards -->\n                <div class=\"space-y-2.5\">\n                  @for (ch of channels; track ch.id) {\n                    <div\n                      role=\"radio\"\n                      tabindex=\"0\"\n                      [attr.aria-checked]=\"selectedChannel() === ch.id\"\n                      [class]=\"channelClass(ch.id)\"\n                      (click)=\"selectedChannel.set(ch.id)\"\n                      (keydown.enter)=\"selectedChannel.set(ch.id)\"\n                      (keydown.space)=\"selectedChannel.set(ch.id); $event.preventDefault()\"\n                    >\n                      <div class=\"flex items-center justify-between gap-2\">\n                        <div class=\"flex items-center gap-2\">\n                          <div [class]=\"radioClass(ch.id)\">\n                            @if (selectedChannel() === ch.id) {\n                              <div class=\"bg-primary-foreground size-1.5 rounded-full\"></div>\n                            }\n                          </div>\n                          <span class=\"text-foreground text-xs font-semibold\">{{ ch.title }}</span>\n                        </div>\n                        <span ui-badge variant=\"outline\" class=\"font-mono text-xs\">{{ ch.frequency }}</span>\n                      </div>\n                      <p class=\"text-muted-foreground mt-1.5 pl-5.5 text-xs leading-relaxed\">{{ ch.description }}</p>\n                    </div>\n                  }\n                </div>\n\n                <ui-separator />\n\n                <!-- Dispatch Subscription Input -->\n                @if (!submitted()) {\n                  <form class=\"space-y-3\" (submit)=\"handleSubmit($event)\">\n                    <div class=\"flex flex-col gap-2 sm:flex-row\">\n                      <ui-input\n                        type=\"email\"\n                        placeholder=\"architect@company.com\"\n                        [value]=\"email()\"\n                        (valueChange)=\"email.set($event)\"\n                        required\n                        class=\"h-10 flex-1 font-mono text-xs\"\n                        aria-label=\"Email address for dispatch\"\n                      />\n                      <button\n                        ui-button\n                        type=\"submit\"\n                        [disabled]=\"isSubmitting()\"\n                        class=\"h-10 gap-1.5 px-5 text-xs font-semibold\"\n                      >\n                        <span>Subscribe to Stream</span>\n                        <lucide-icon [img]=\"Send\" class=\"size-3.5\" />\n                      </button>\n                    </div>\n                    <div class=\"text-muted-foreground flex items-center justify-between pt-1 font-mono text-xs\">\n                      <span>Plain text &bull; 1-click unsubscribe</span>\n                      <span class=\"text-success font-semibold\">18,412 Active Subscribers</span>\n                    </div>\n                  </form>\n                } @else {\n                  <div class=\"border-success/20 bg-success/10 space-y-2 rounded-xl border p-5 text-center\">\n                    <div\n                      class=\"bg-success/20 text-success mx-auto flex size-9 items-center justify-center rounded-full\"\n                    >\n                      <lucide-icon [img]=\"Check\" class=\"size-5\" />\n                    </div>\n                    <p class=\"text-foreground text-sm font-semibold\">Dispatches Activated!</p>\n                    <p class=\"text-muted-foreground text-xs\">\n                      Verification sent to <strong class=\"text-foreground font-mono\">{{ email() }}</strong\n                      >. Check your inbox to confirm delivery.\n                    </p>\n                  </div>\n                }\n              </div>\n            </div>\n          </div>\n\n          <!-- Right Column: Recent Issue Sampler & Archives (5 Cols) -->\n          <div class=\"space-y-4 lg:col-span-5\">\n            <div class=\"flex items-center justify-between\">\n              <p class=\"text-muted-foreground flex items-center gap-1.5 font-mono text-xs tracking-wider uppercase\">\n                <lucide-icon [img]=\"BookOpen\" class=\"text-primary size-3.5\" /> Archive Sampler\n              </p>\n              <span class=\"text-muted-foreground font-mono text-xs\">Issue #{{ activeIssue().id }}</span>\n            </div>\n\n            <!-- Interactive Issue Cards -->\n            <div class=\"space-y-3\">\n              @for (issue of recentIssues; track issue.id; let idx = $index) {\n                <div [class]=\"issueClass(idx)\" (click)=\"activePreviewIssue.set(idx)\">\n                  <div class=\"text-muted-foreground mb-1.5 flex items-center justify-between font-mono text-xs\">\n                    <span>{{ issue.date }} &bull; Issue #{{ issue.id }}</span>\n                    <span class=\"text-foreground font-medium\">{{ issue.reads }}</span>\n                  </div>\n                  <h3 class=\"text-foreground mb-1.5 text-sm leading-snug font-bold\">{{ issue.title }}</h3>\n                  <p class=\"text-muted-foreground text-xs leading-relaxed\">{{ issue.snippet }}</p>\n\n                  <div class=\"mt-3 flex flex-wrap gap-1.5\">\n                    @for (tag of issue.tags; track tag) {\n                      <span ui-badge variant=\"secondary\" class=\"px-1.5 py-0 font-mono text-xs\">#{{ tag }}</span>\n                    }\n                  </div>\n                </div>\n              }\n            </div>\n\n            <!-- RSS note -->\n            <div\n              class=\"border-border bg-muted/20 text-muted-foreground flex items-center justify-between rounded-lg border p-3.5 text-xs\"\n            >\n              <span class=\"font-mono\">Prefer RSS feeds?</span>\n              <a\n                href=\"https://uipkge.dev/rss.xml\"\n                target=\"_blank\"\n                rel=\"noreferrer\"\n                class=\"text-primary inline-flex items-center gap-1 font-mono hover:underline\"\n              >\n                uipkge.dev/rss.xml <lucide-icon [img]=\"ArrowRight\" class=\"size-3\" />\n              </a>\n            </div>\n          </div>\n        </div>\n      </div>\n    </section>\n  `,\n})\nexport class UiNewsletterSignupComponent {\n  protected readonly ArrowRight = ArrowRight\n  protected readonly BellRing = BellRing\n  protected readonly BookOpen = BookOpen\n  protected readonly Check = Check\n  protected readonly Mail = Mail\n  protected readonly Send = Send\n\n  protected readonly channels = channels\n  protected readonly recentIssues = recentIssues\n\n  @Input() variant: NewsletterSignupVariant = 'split'\n  @Input('class') className?: string\n\n  readonly email = signal('')\n  readonly selectedChannel = signal('architecture')\n  readonly submitted = signal(false)\n  readonly activePreviewIssue = signal(0)\n  readonly isSubmitting = signal(false)\n\n  activeIssue(): RecentIssue {\n    return this.recentIssues[this.activePreviewIssue()] ?? this.recentIssues[0]!\n  }\n\n  channelClass(id: string): string {\n    return cn(\n      'group cursor-pointer rounded-xl border p-3.5 transition-colors',\n      this.selectedChannel() === id\n        ? 'border-primary bg-primary/5 ring-primary/20 shadow-xs ring-1'\n        : 'border-border bg-background hover:bg-muted/40',\n    )\n  }\n\n  radioClass(id: string): string {\n    return cn(\n      'flex size-3.5 items-center justify-center rounded-full border transition-colors',\n      this.selectedChannel() === id ? 'border-primary bg-primary' : 'border-muted-foreground/40',\n    )\n  }\n\n  issueClass(idx: number): string {\n    return cn(\n      'bg-card cursor-pointer rounded-xl border p-4 transition-colors',\n      this.activePreviewIssue() === idx ? 'border-primary/50 shadow-xs' : 'border-border hover:border-border/80',\n    )\n  }\n\n  handleSubmit(event: Event): void {\n    event.preventDefault()\n    if (!this.email() || !this.email().includes('@')) return\n    this.isSubmitting.set(true)\n    setTimeout(() => {\n      this.isSubmitting.set(false)\n      this.submitted.set(true)\n    }, 400)\n  }\n}\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/newsletter/newsletter-signup.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/newsletter/newsletter-split-preview.component.ts",
      "content": "// Split pitch + last-issue preview: the offer is legible before anyone types an address.\n// Showing the last issue is the point — nobody should have to subscribe to find out\n// what they are subscribing to. Port of the React block 1:1.\nimport { ChangeDetectionStrategy, Component, Input, signal } from '@angular/core'\nimport { Check, LucideAngularModule, Mail } from 'lucide-angular'\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 { UiSeparatorComponent } from '@/ui/separator/separator.component'\n\nconst lastIssue = {\n  subject: 'Why we version metric definitions instead of dashboards',\n  date: 'Feb 18, 2026',\n  readTime: '9 min',\n  opening:\n    'Dashboards are the output. The definition is the thing teams argue about, so that is what belongs under review — with a diff, an owner, and a revert path. This issue walks through what changed when we moved ours into the repo.',\n}\n\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-newsletter-split-preview, [ui-newsletter-split-preview]',\n  standalone: true,\n  // The block renders its own root <section>: keep the host out of layout.\n  host: { '[attr.class]': '\"contents\"' },\n  imports: [\n    LucideAngularModule,\n    UiBadgeComponent,\n    UiButtonComponent,\n    UiCardComponent,\n    UiCardContentComponent,\n    UiInputComponent,\n    UiSeparatorComponent,\n  ],\n  template: `\n    <section data-slot=\"newsletter-split-preview\" class=\"bg-background\">\n      <div class=\"mx-auto max-w-6xl px-6 py-20 lg:py-28\">\n        <div class=\"grid items-center gap-10 lg:grid-cols-2 lg:gap-16\">\n          <div>\n            <span ui-badge variant=\"secondary\" class=\"gap-1.5\">\n              <lucide-icon [img]=\"Mail\" class=\"size-3\" aria-hidden=\"true\" />\n              Monthly\n            </span>\n            <h2 class=\"mt-4 text-3xl font-semibold tracking-tight text-balance sm:text-4xl\">\n              Notes from the people who build it\n            </h2>\n            <p class=\"text-muted-foreground mt-3 text-lg\">\n              One email a month on metric modelling, access control, and warehouse cost. No product announcements.\n            </p>\n\n            <form class=\"mt-6 max-w-md\" (submit)=\"subscribe($event)\">\n              <div class=\"flex gap-2\">\n                <ui-input\n                  [value]=\"email()\"\n                  (valueChange)=\"email.set($event)\"\n                  type=\"email\"\n                  required\n                  placeholder=\"you@company.com\"\n                  autoComplete=\"email\"\n                  aria-label=\"Email address\"\n                />\n                <button ui-button type=\"submit\" class=\"shrink-0\">Subscribe</button>\n              </div>\n              <p class=\"mt-2 min-h-5 text-xs\" aria-live=\"polite\">\n                @if (submitted()) {\n                  <span class=\"text-success inline-flex items-center gap-1.5\">\n                    <lucide-icon [img]=\"Check\" class=\"size-3\" aria-hidden=\"true\" />\n                    Check your inbox to confirm.\n                  </span>\n                } @else {\n                  <span class=\"text-muted-foreground\">8,400 subscribers &middot; unsubscribe in one click.</span>\n                }\n              </p>\n            </form>\n          </div>\n\n          <!-- Showing the last issue is the point: nobody should have to subscribe\n               to find out what they are subscribing to. -->\n          <div ui-card>\n            <div ui-card-content class=\"p-6\">\n              <div class=\"flex flex-wrap items-center gap-3\">\n                <span ui-badge variant=\"outline\">Last issue</span>\n                <span class=\"text-muted-foreground text-xs\"\n                  >{{ lastIssue.date }} &middot; {{ lastIssue.readTime }}</span\n                >\n              </div>\n\n              <h3 class=\"mt-4 text-lg leading-snug font-semibold tracking-tight text-balance\">\n                {{ lastIssue.subject }}\n              </h3>\n\n              <ui-separator class=\"my-4\" />\n\n              <p class=\"text-muted-foreground text-sm leading-relaxed\">{{ lastIssue.opening }}</p>\n\n              <button ui-button variant=\"link\" class=\"mt-4 h-auto p-0 text-sm\">Read the full issue</button>\n            </div>\n          </div>\n        </div>\n      </div>\n    </section>\n  `,\n})\nexport class UiNewsletterSplitPreviewComponent {\n  protected readonly Check = Check\n  protected readonly Mail = Mail\n\n  protected readonly lastIssue = lastIssue\n\n  @Input('class') className?: string\n\n  readonly email = signal('')\n  readonly submitted = signal(false)\n\n  subscribe(event: Event): void {\n    event.preventDefault()\n    if (!this.email().trim()) return\n    this.submitted.set(true)\n  }\n}\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/newsletter/newsletter-split-preview.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/newsletter/index.ts",
      "content": "export { UiNewsletterInlineBandComponent } from './newsletter-inline-band.component'\nexport { UiNewsletterIssueArchiveComponent, type NewsletterIssue } from './newsletter-issue-archive.component'\nexport { UiNewsletterSignupComponent, type NewsletterSignupVariant } from './newsletter-signup.component'\nexport { UiNewsletterSplitPreviewComponent } from './newsletter-split-preview.component'\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/newsletter/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/separator.json"
  ],
  "description": "Newsletter growth quartet: a slim inline signup band, a filterable issue archive with featured spotlight, centered and split signup forms, and a split section pairing the pitch with a preview of the last issue.",
  "categories": [
    "marketing",
    "media",
    "app"
  ]
}