<script setup lang="ts">
export type MemberLinkType = 'website' | 'email' | 'code' | 'chat'
export interface MemberLink {
type: MemberLinkType
href: string
}
export interface TeamMember {
name: string
role: string
bio: string
location?: string
links?: MemberLink[]
}
withDefaults(defineProps<{
eyebrow?: string
heading?: string
subheading?: string
members?: TeamMember[]
}>(), {
eyebrow: 'Our team',
heading: 'The people behind Orchard',
subheading: 'A small, remote team across five time zones. We care about calm software, clear writing and answering support tickets ourselves.',
members: () => [
{ name: 'Nadia Osei', role: 'Co-founder & CEO', bio: 'Ran operations at two logistics startups before deciding scheduling software could be kinder.', location: 'Lisbon', links: [{ type: 'website', href: '#nadia' }, { type: 'email', href: '#nadia-mail' }] },
{ name: 'Lukas Brenner', role: 'Co-founder & CTO', bio: 'Writes most of the sync engine and all of the terrible puns in our release notes.', location: 'Vienna', links: [{ type: 'code', href: '#lukas-code' }, { type: 'website', href: '#lukas' }] },
{ name: 'Hana Moriyama', role: 'Head of Design', bio: 'Obsessed with empty states, keyboard flows and the space between two buttons.', location: 'Osaka', links: [{ type: 'website', href: '#hana' }, { type: 'chat', href: '#hana-chat' }] },
{ name: 'Rafael Duarte', role: 'Senior Engineer', bio: 'Keeps the API fast and the on-call rotation boring. Former competitive cyclist.', location: 'São Paulo', links: [{ type: 'code', href: '#rafael-code' }] },
{ name: 'Grace Achterberg', role: 'Customer Success', bio: 'Answers most support threads within the hour and turns them into better docs.', location: 'Utrecht', links: [{ type: 'email', href: '#grace-mail' }, { type: 'chat', href: '#grace-chat' }] },
{ name: 'Theo Karlsson', role: 'Product Engineer', bio: 'Ships the small details customers mention in reviews. Bakes sourdough on Fridays.', location: 'Gothenburg', links: [{ type: 'code', href: '#theo-code' }, { type: 'website', href: '#theo' }] },
],
})
defineSlots<{
/** Full-width row after the members, e.g. a hiring call to action. */
after?: () => unknown
}>()
const linkMeta: Record<MemberLinkType, { label: string, path: string }> = {
website: { label: 'Website', path: 'M12 21a9 9 0 1 0 0-18 9 9 0 0 0 0 18Zm-9-9h18M12 3c2.4 2.5 3.5 5.5 3.5 9s-1.1 6.5-3.5 9c-2.4-2.5-3.5-5.5-3.5-9s1.1-6.5 3.5-9Z' },
email: { label: 'Email', path: 'M3.5 6.5h17v11h-17v-11Zm0 .5 8.5 6 8.5-6' },
code: { label: 'Code profile', path: 'm8.5 8-4 4 4 4m7-8 4 4-4 4M13.5 5l-3 14' },
chat: { label: 'Chat', path: 'M5 5h14v10h-7l-4 3.5V15H5V5Z' },
}
const initials = (name: string) => name.split(/\s+/).filter(Boolean).slice(0, 2).map(p => p[0]!.toUpperCase()).join('')
const gradients = [
'from-cyan-400 to-cyan-600',
'from-sky-400 to-indigo-500',
'from-teal-400 to-cyan-600',
'from-cyan-500 to-blue-600',
]
function gradient(name: string) {
let h = 0
for (const ch of name) h = (h * 31 + ch.charCodeAt(0)) >>> 0
return gradients[h % gradients.length]
}
</script>
<template>
<section class="px-4 py-20 sm:px-6 sm:py-24 lg:px-8">
<div class="mx-auto max-w-6xl">
<div class="max-w-2xl">
<p v-if="eyebrow" class="text-sm font-semibold text-cyan-700 dark:text-cyan-400">{{ eyebrow }}</p>
<h2 class="mt-2 text-3xl font-bold tracking-tight text-balance text-gray-900 sm:text-4xl dark:text-white">{{ heading }}</h2>
<p class="mt-4 text-lg text-pretty text-gray-600 dark:text-gray-400">{{ subheading }}</p>
</div>
<ul role="list" class="mt-14 grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
<li
v-for="member in members"
:key="member.name"
class="flex flex-col rounded-2xl bg-white p-6 shadow-sm ring-1 ring-gray-200 dark:bg-gray-900 dark:shadow-none dark:ring-white/10"
>
<div class="flex items-center gap-4">
<span
aria-hidden="true"
class="grid size-14 shrink-0 place-items-center rounded-2xl bg-gradient-to-br text-lg font-semibold text-white shadow-sm"
:class="gradient(member.name)"
>{{ initials(member.name) }}</span>
<div class="min-w-0">
<h3 class="truncate text-base font-semibold text-gray-900 dark:text-white">{{ member.name }}</h3>
<p class="text-sm font-medium text-cyan-700 dark:text-cyan-400">{{ member.role }}</p>
</div>
</div>
<p class="mt-4 flex-1 text-sm/6 text-gray-600 dark:text-gray-400">{{ member.bio }}</p>
<div class="mt-5 flex items-center justify-between gap-4 border-t border-gray-100 pt-4 dark:border-white/5">
<p v-if="member.location" class="inline-flex items-center gap-1.5 text-sm text-gray-500 dark:text-gray-400">
<svg viewBox="0 0 20 20" aria-hidden="true" class="size-4 fill-current">
<path fill-rule="evenodd" d="M10 18s6-5.2 6-10A6 6 0 0 0 4 8c0 4.8 6 10 6 10Zm0-7.5a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5Z" clip-rule="evenodd" />
</svg>
<span class="sr-only">Based in</span>
{{ member.location }}
</p>
<span v-else />
<ul v-if="member.links?.length" role="list" class="flex gap-1">
<li v-for="link in member.links" :key="link.href">
<a
:href="link.href"
class="grid size-8 place-items-center rounded-lg text-gray-500 transition hover:bg-cyan-50 hover:text-cyan-700 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-cyan-600 dark:text-gray-400 dark:hover:bg-cyan-400/10 dark:hover:text-cyan-300"
:aria-label="`${linkMeta[link.type].label} of ${member.name}`"
:title="linkMeta[link.type].label"
>
<svg viewBox="0 0 24 24" aria-hidden="true" class="size-4.5" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round">
<path :d="linkMeta[link.type].path" />
</svg>
</a>
</li>
</ul>
</div>
</li>
<li v-if="$slots.after" class="sm:col-span-2 lg:col-span-3">
<slot name="after" />
</li>
</ul>
</div>
</section>
</template>
<script setup lang="ts">
import TeamGrid from './Component.vue'
</script>
<template>
<main class="min-h-screen bg-gray-50 dark:bg-gray-950">
<TeamGrid>
<template #after>
<div class="flex flex-col gap-4 rounded-2xl border border-dashed border-cyan-300 bg-cyan-50/60 p-6 sm:flex-row sm:items-center sm:justify-between dark:border-cyan-400/30 dark:bg-cyan-400/5">
<div>
<p class="font-semibold text-gray-900 dark:text-white">We're hiring a Senior Frontend Engineer</p>
<p class="mt-1 text-sm text-gray-600 dark:text-gray-400">Fully remote, four-day weeks, and a team that reviews code kindly.</p>
</div>
<a
href="#careers"
class="inline-flex shrink-0 justify-center rounded-lg bg-cyan-700 px-4 py-2 text-sm font-semibold text-white hover:bg-cyan-600 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-cyan-600 dark:bg-cyan-400 dark:text-gray-950 dark:hover:bg-cyan-300"
>See open roles</a>
</div>
</template>
</TeamGrid>
</main>
</template>