{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "careers-job-board",
  "title": "Careers Job Board",
  "type": "registry:block",
  "description": "Careers page and job openings board with perks overview, live department & location filtering, keyword search, grouped department openings, and general application CTA.",
  "categories": [
    "marketing"
  ],
  "files": [
    {
      "path": "packages/elements-lit/src/blocks/careers-job-board/careers-job-board.ts",
      "content": "import { LitElement, css, html, nothing } from 'lit'\nimport {\n  ArrowRight,\n  Briefcase,\n  Coins,\n  Globe,\n  GraduationCap,\n  HeartHandshake,\n  MapPin,\n  Search,\n  SearchX,\n  TrendingUp,\n  type IconNode,\n} from 'lucide'\nimport { cn } from '../../lib/utils'\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/badge/badge'\nimport '../../components/button/button'\nimport '../../components/input/input'\nimport type { UipInput } from '../../components/input/input'\nimport '../../components/select/select'\nimport type { UipSelect } from '../../components/select/select'\n\ninterface JobOpening {\n  id: string\n  title: string\n  department: 'Engineering' | 'Design' | 'Product' | 'Marketing'\n  location: 'Remote' | 'San Francisco, CA' | 'London, UK'\n  type: string\n  level: string\n  salary: string\n  description: string\n}\n\ninterface Perk {\n  icon: IconNode\n  iconName: string\n  title: string\n  description: string\n}\n\nconst PERKS: Perk[] = [\n  {\n    icon: Globe,\n    iconName: 'globe',\n    title: 'Remote-first',\n    description: 'Work from anywhere in the world with flexible hours.',\n  },\n  {\n    icon: GraduationCap,\n    iconName: 'graduation-cap',\n    title: '$5k learning budget',\n    description: 'Annual stipend for courses, books, tools, and conferences.',\n  },\n  {\n    icon: HeartHandshake,\n    iconName: 'heart-handshake',\n    title: '100% health coverage',\n    description: 'Comprehensive medical, dental, and vision for you and dependents.',\n  },\n  {\n    icon: TrendingUp,\n    iconName: 'trending-up',\n    title: 'Equity packages',\n    description: 'Meaningful ownership stake with transparent vesting schedules.',\n  },\n]\n\nconst DEPARTMENTS = [\n  { id: 'all', label: 'All', count: 12 },\n  { id: 'Engineering', label: 'Engineering', count: 6 },\n  { id: 'Design', label: 'Design', count: 2 },\n  { id: 'Product', label: 'Product', count: 2 },\n  { id: 'Marketing', label: 'Marketing', count: 2 },\n] as const\n\nconst LOCATIONS = [\n  { id: 'all', label: 'All Locations' },\n  { id: 'Remote', label: 'Remote' },\n  { id: 'San Francisco, CA', label: 'San Francisco, CA' },\n  { id: 'London, UK', label: 'London, UK' },\n] as const\n\nconst JOBS: JobOpening[] = [\n  {\n    id: 'eng-1',\n    title: 'Staff Frontend Engineer',\n    department: 'Engineering',\n    location: 'Remote',\n    type: 'Full-time',\n    level: 'Staff',\n    salary: '$190k - $240k · 0.3% - 0.6%',\n    description:\n      'Lead the architecture of our high-performance UI components, compiler toolchains, and multi-framework design systems.',\n  },\n  {\n    id: 'eng-2',\n    title: 'Senior Full-Stack Engineer',\n    department: 'Engineering',\n    location: 'San Francisco, CA',\n    type: 'Full-time',\n    level: 'Senior',\n    salary: '$160k - $210k · 0.25% - 0.5%',\n    description:\n      'Build end-to-end features spanning serverless edge runtimes, component registries, and developer tooling.',\n  },\n  {\n    id: 'eng-3',\n    title: 'Systems & Performance Engineer',\n    department: 'Engineering',\n    location: 'Remote',\n    type: 'Full-time',\n    level: 'Senior',\n    salary: '$170k - $220k · 0.25% - 0.5%',\n    description:\n      'Optimize bundling pipelines, AST transformers, and static site generation latency across large workspaces.',\n  },\n  {\n    id: 'eng-4',\n    title: 'Design Systems Engineer',\n    department: 'Engineering',\n    location: 'London, UK',\n    type: 'Full-time',\n    level: 'Mid-Senior',\n    salary: '£110k - £140k · 0.2% - 0.4%',\n    description:\n      'Bridge design and code by maintaining tokens, accessible headless primitives, and fluid interaction standards.',\n  },\n  {\n    id: 'eng-5',\n    title: 'Developer Experience Engineer',\n    department: 'Engineering',\n    location: 'Remote',\n    type: 'Full-time',\n    level: 'Mid-Senior',\n    salary: '$150k - $190k · 0.2% - 0.4%',\n    description:\n      'Craft exceptional CLI workflows, interactive documentation playgrounds, and starter template engines.',\n  },\n  {\n    id: 'eng-6',\n    title: 'QA & Automation Engineer',\n    department: 'Engineering',\n    location: 'London, UK',\n    type: 'Full-time',\n    level: 'Mid-Senior',\n    salary: '£95k - £125k · 0.15% - 0.3%',\n    description:\n      'Expand cross-browser visual regression testing suites, accessibility audits, and automated release checks.',\n  },\n  {\n    id: 'des-1',\n    title: 'Senior Product Designer',\n    department: 'Design',\n    location: 'San Francisco, CA',\n    type: 'Full-time',\n    level: 'Senior',\n    salary: '$155k - $195k · 0.25% - 0.45%',\n    description:\n      'Define micro-interactions, responsive dashboard workflows, and layout ergonomics across our block catalog.',\n  },\n  {\n    id: 'des-2',\n    title: 'Brand & Motion Designer',\n    department: 'Design',\n    location: 'Remote',\n    type: 'Full-time',\n    level: 'Mid-Senior',\n    salary: '$140k - $180k · 0.2% - 0.35%',\n    description:\n      'Create fluid motion principles, brand identity assets, and polished launch visuals for major releases.',\n  },\n  {\n    id: 'prd-1',\n    title: 'Staff Technical Product Manager',\n    department: 'Product',\n    location: 'Remote',\n    type: 'Full-time',\n    level: 'Staff',\n    salary: '$180k - $230k · 0.3% - 0.5%',\n    description:\n      'Drive product roadmap for developer workflows, unbundled registry distribution, and monorepo orchestration.',\n  },\n  {\n    id: 'prd-2',\n    title: 'Product Operations Lead',\n    department: 'Product',\n    location: 'San Francisco, CA',\n    type: 'Full-time',\n    level: 'Senior',\n    salary: '$145k - $185k · 0.2% - 0.35%',\n    description: 'Manage release triage, community feature requests, and cross-team execution cadences.',\n  },\n  {\n    id: 'mkt-1',\n    title: 'Developer Advocate / Technical Writer',\n    department: 'Marketing',\n    location: 'Remote',\n    type: 'Full-time',\n    level: 'Mid-Senior',\n    salary: '$140k - $180k · 0.2% - 0.4%',\n    description:\n      'Author technical deep-dives, create interactive video tutorials, and engage with the open-source community.',\n  },\n  {\n    id: 'mkt-2',\n    title: 'Growth Marketing Lead',\n    department: 'Marketing',\n    location: 'London, UK',\n    type: 'Full-time',\n    level: 'Senior',\n    salary: '£105k - £135k · 0.2% - 0.4%',\n    description:\n      'Lead developer-focused growth experiments, SEO architecture, launch campaigns, and analytics instrumentation.',\n  },\n]\n\n/**\n * <uip-careers-job-board> — the registry CareersJobBoard block as a web component.\n *\n * Class strings are React's CareersJobBoard verbatim. `heading` is React's\n * `title` (`title` is taken by HTMLElement). React state (useState) becomes\n * @state fields. The search icon moves from React's absolute overlay into\n * <uip-input>'s prefix slot (same visual result, no overlay positioning to\n * maintain), so the input keeps `text-sm` and drops the overlay's `pl-9`.\n * The location Select is a <uip-select> reading light-DOM <option> children.\n */\nexport class UipCareersJobBoard 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    heading: {},\n    subtitle: {},\n    selectedDepartment: { state: true },\n    selectedLocation: { state: true },\n    searchQuery: { state: true },\n  }\n\n  heading = 'Join our team'\n  subtitle = 'Help us build the open-source UI infrastructure of tomorrow'\n  private selectedDepartment = 'all'\n  private selectedLocation = 'all'\n  private searchQuery = ''\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', 'careers-job-board')\n  }\n\n  private get filteredJobs() {\n    const q = this.searchQuery.trim().toLowerCase()\n    return JOBS.filter((job) => {\n      const matchDept = this.selectedDepartment === 'all' || job.department === this.selectedDepartment\n      const matchLoc = this.selectedLocation === 'all' || job.location === this.selectedLocation\n      if (!matchDept || !matchLoc) return false\n      if (!q) return true\n      return (\n        job.title.toLowerCase().includes(q) ||\n        job.description.toLowerCase().includes(q) ||\n        job.department.toLowerCase().includes(q) ||\n        job.level.toLowerCase().includes(q) ||\n        job.location.toLowerCase().includes(q) ||\n        job.type.toLowerCase().includes(q) ||\n        job.salary.toLowerCase().includes(q)\n      )\n    })\n  }\n\n  private get departmentGroups() {\n    const allDepts = ['Engineering', 'Design', 'Product', 'Marketing'] as const\n    const targetDepartments =\n      this.selectedDepartment === 'all' ? allDepts : allDepts.filter((dept) => dept === this.selectedDepartment)\n    return targetDepartments\n      .map((dept) => ({\n        name: dept,\n        jobs: this.filteredJobs.filter((job) => job.department === dept),\n      }))\n      .filter((group) => group.jobs.length > 0)\n  }\n\n  private get isFiltered() {\n    return this.selectedDepartment !== 'all' || this.selectedLocation !== 'all' || this.searchQuery.trim() !== ''\n  }\n\n  private resetFilters() {\n    this.selectedDepartment = 'all'\n    this.selectedLocation = 'all'\n    this.searchQuery = ''\n  }\n\n  render() {\n    const groups = this.departmentGroups\n    return html`\n      <section data-slot=\"careers-job-board\" class=${cn('w-full space-y-12 py-6')}>\n        <div class=\"mx-auto max-w-3xl space-y-4 text-center\">\n          <uip-badge variant=\"secondary\" class=\"gap-1.5 px-3 py-1 text-xs font-medium\">\n            ${icon(Briefcase, 'briefcase', 'text-primary size-3.5')} We're hiring\n          </uip-badge>\n          <h1 class=\"text-foreground text-3xl font-bold tracking-tight sm:text-4xl lg:text-5xl\">${this.heading}</h1>\n          <p class=\"text-muted-foreground text-base leading-relaxed sm:text-lg\">${this.subtitle}</p>\n        </div>\n\n        <div class=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n          ${PERKS.map(\n            (perk) => html`<div class=\"bg-card border-border flex flex-col gap-3 rounded-xl border p-4 shadow-xs\">\n              <div class=\"bg-primary/10 text-primary flex size-9 items-center justify-center rounded-lg\">\n                ${icon(perk.icon, perk.iconName, 'size-4.5')}\n              </div>\n              <div class=\"space-y-1\">\n                <p class=\"text-foreground text-sm font-semibold\">${perk.title}</p>\n                <p class=\"text-muted-foreground text-xs leading-relaxed\">${perk.description}</p>\n              </div>\n            </div>`,\n          )}\n        </div>\n\n        <div class=\"bg-card border-border space-y-4 rounded-xl border p-4 shadow-xs\">\n          <div class=\"flex flex-wrap items-center gap-1.5\">\n            ${DEPARTMENTS.map(\n              (dept) => html`<uip-button\n                variant=${this.selectedDepartment === dept.id ? 'default' : 'outline'}\n                size=\"sm\"\n                class=\"h-8 rounded-lg text-xs\"\n                @click=${() => {\n                  this.selectedDepartment = dept.id\n                }}\n              >\n                ${dept.label}\n                <span\n                  class=${cn(\n                    'ml-1.5 rounded-full px-1.5 py-0.5 text-xs font-medium tabular-nums',\n                    this.selectedDepartment === dept.id\n                      ? 'bg-primary-foreground/20 text-primary-foreground'\n                      : 'bg-muted text-muted-foreground',\n                  )}\n                >\n                  ${dept.count}\n                </span>\n              </uip-button>`,\n            )}\n          </div>\n\n          <div class=\"flex flex-col gap-3 sm:flex-row sm:items-center\">\n            <uip-input\n              class=\"flex-1 text-sm\"\n              .value=${this.searchQuery}\n              placeholder=\"Search roles, technologies, or keywords...\"\n              aria-label=\"Search open positions\"\n              @input=${(e: Event) => {\n                this.searchQuery = (e.target as UipInput).value\n              }}\n            >\n              <span slot=\"prefix\">${icon(Search, 'search', 'size-4')}</span>\n            </uip-input>\n\n            <div class=\"w-full shrink-0 sm:w-56\">\n              <uip-select\n                class=\"w-full text-xs sm:text-sm\"\n                placeholder=\"All Locations\"\n                aria-label=\"Filter by location\"\n                .value=${this.selectedLocation}\n                @change=${(e: Event) => {\n                  this.selectedLocation = (e.target as UipSelect).value\n                }}\n              >\n                ${LOCATIONS.map((loc) => html`<option value=${loc.id}>${loc.label}</option>`)}\n              </uip-select>\n            </div>\n\n            ${this.isFiltered\n              ? html`<uip-button\n                  variant=\"ghost\"\n                  size=\"sm\"\n                  class=\"text-muted-foreground hover:text-foreground text-xs\"\n                  @click=${this.resetFilters}\n                >\n                  Reset filters\n                </uip-button>`\n              : nothing}\n          </div>\n        </div>\n\n        ${groups.length > 0\n          ? html`<div class=\"space-y-10\">\n              ${groups.map(\n                (group) => html`<div class=\"space-y-4\">\n                  <div class=\"border-border flex items-center justify-between border-b pb-3\">\n                    <div class=\"flex items-center gap-2.5\">\n                      <h2 class=\"text-foreground text-lg font-semibold tracking-tight\">${group.name}</h2>\n                      <uip-badge variant=\"secondary\" class=\"text-xs font-normal\">\n                        ${group.jobs.length} ${group.jobs.length === 1 ? 'opening' : 'openings'}\n                      </uip-badge>\n                    </div>\n                  </div>\n\n                  <div class=\"space-y-3\">\n                    ${group.jobs.map(\n                      (job) => html`<article\n                        class=\"group bg-card border-border hover:border-primary/40 relative flex flex-col justify-between gap-4 rounded-xl border p-5 shadow-xs transition-colors duration-150 hover:shadow-sm md:flex-row md:items-center\"\n                      >\n                        <div class=\"min-w-0 flex-1 space-y-2\">\n                          <div class=\"flex flex-wrap items-center gap-2\">\n                            <h3\n                              class=\"group-hover:text-primary text-foreground text-base font-semibold tracking-tight transition-colors sm:text-lg\"\n                            >\n                              ${job.title}\n                            </h3>\n                            <uip-badge variant=\"outline\" class=\"text-xs\">${job.level}</uip-badge>\n                          </div>\n                          <p class=\"text-muted-foreground line-clamp-2 text-sm leading-relaxed\">${job.description}</p>\n                          <div class=\"text-muted-foreground flex flex-wrap items-center gap-x-4 gap-y-2 pt-1 text-xs\">\n                            <span class=\"inline-flex items-center gap-1.5\">\n                              ${icon(MapPin, 'map-pin', 'text-muted-foreground size-3.5 shrink-0')} ${job.location}\n                            </span>\n                            <span class=\"inline-flex items-center gap-1.5\">\n                              ${icon(Briefcase, 'briefcase', 'text-muted-foreground size-3.5 shrink-0')} ${job.type}\n                            </span>\n                            <span class=\"text-foreground inline-flex items-center gap-1.5 font-medium\">\n                              ${icon(Coins, 'coins', 'text-muted-foreground size-3.5 shrink-0')} ${job.salary}\n                            </span>\n                          </div>\n                        </div>\n                        <div class=\"shrink-0 pt-2 md:pt-0\">\n                          <uip-button class=\"w-full gap-1.5 md:w-auto\" size=\"sm\">\n                            Apply now ${icon(ArrowRight, 'arrow-right', 'size-3.5 transition-transform group-hover:translate-x-0.5')}\n                          </uip-button>\n                        </div>\n                      </article>`,\n                    )}\n                  </div>\n                </div>`,\n              )}\n            </div>`\n          : html`<div\n              class=\"border-border flex flex-col items-center justify-center rounded-xl border border-dashed p-12 text-center\"\n            >\n              <div class=\"bg-muted text-muted-foreground mb-4 flex size-12 items-center justify-center rounded-full\">\n                ${icon(SearchX, 'search-x', 'size-6')}\n              </div>\n              <h3 class=\"text-foreground text-base font-semibold\">No matching positions found</h3>\n              <p class=\"text-muted-foreground mt-1 mb-4 max-w-sm text-sm leading-normal\">\n                We could not find any roles matching your current search or filters. Try adjusting your criteria.\n              </p>\n              <uip-button variant=\"outline\" size=\"sm\" @click=${this.resetFilters}>Clear all filters</uip-button>\n            </div>`}\n\n        <div\n          class=\"bg-card border-border flex flex-col items-start justify-between gap-6 rounded-2xl border p-6 shadow-xs sm:flex-row sm:items-center sm:p-8\"\n        >\n          <div class=\"max-w-xl space-y-1.5\">\n            <h3 class=\"text-foreground text-lg font-semibold tracking-tight\">Don't see your role?</h3>\n            <p class=\"text-muted-foreground text-sm leading-relaxed\">\n              We are always looking for curious, high-craft engineers, designers, and thinkers. Send us an open\n              application and tell us what you'd love to build with us.\n            </p>\n          </div>\n          <uip-button variant=\"outline\" class=\"shrink-0 gap-2\">\n            Send open application ${icon(ArrowRight, 'arrow-right', 'size-4')}\n          </uip-button>\n        </div>\n      </section>\n    `\n  }\n}\n\ncustomElements.get('uip-careers-job-board') || customElements.define('uip-careers-job-board', UipCareersJobBoard)\n\ndeclare global {\n  interface HTMLElementTagNameMap {\n    'uip-careers-job-board': UipCareersJobBoard\n  }\n}\n",
      "type": "registry:file",
      "target": "src/uip/blocks/careers-job-board/careers-job-board.ts"
    }
  ],
  "dependencies": [
    "lit",
    "lucide"
  ],
  "registryDependencies": [
    "https://uipkge.dev/r/lit/badge.json",
    "https://uipkge.dev/r/lit/button.json",
    "https://uipkge.dev/r/lit/input.json",
    "https://uipkge.dev/r/lit/lit-core.json",
    "https://uipkge.dev/r/lit/select.json"
  ]
}
