feat(ui): install shadcn Card; sweep card patterns site-wide

Add the shadcn-vue Card primitive (Card / CardHeader / CardTitle /
CardDescription / CardContent / CardFooter) at src/components/ui/card.

Replace ~20 hand-rolled "rounded-lg border border-border bg-card …"
patterns across nine views with <Card>:

- ConceptView (slow-farming pillars)
- VisionValuesView (philosophy + pillars + team)
- GalleryView (image figure cards)
- EventsView (program cards)
- SymposiumView (included items + apply steps)
- LongStaysView (path cards)
- OpportunitiesView (group cards + apply explainers)
- AccommodationView (rooms + cabins + exterior items)
- ReservationsView (kind cards + contact card)
- MarketplaceView (category cards)
- HomeView (featured events)

For image-bearing cards (events / rooms), use Card + CardContent so
the image stays flush at the top of the card and the inner padding
lives on the content slot. For clickable cards, the RouterLink wraps
the Card so the whole card is the link target.

Variants where the card sits on a tinted section (philosophy items on
bg-card section, cabins on bg-card section) override Card's default
bg-card with bg-background via the class prop.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Padreug 2026-06-09 17:01:51 +02:00
commit 4cb0fa14a2
18 changed files with 265 additions and 197 deletions

View file

@ -0,0 +1,21 @@
<script setup lang="ts">
import type { HTMLAttributes } from "vue"
import { cn } from "@/lib/utils"
const props = defineProps<{
class?: HTMLAttributes["class"]
}>()
</script>
<template>
<div
:class="
cn(
'rounded-lg border bg-card text-card-foreground shadow-sm',
props.class,
)
"
>
<slot />
</div>
</template>

View file

@ -0,0 +1,14 @@
<script setup lang="ts">
import type { HTMLAttributes } from "vue"
import { cn } from "@/lib/utils"
const props = defineProps<{
class?: HTMLAttributes["class"]
}>()
</script>
<template>
<div :class="cn('p-6 pt-0', props.class)">
<slot />
</div>
</template>

View file

@ -0,0 +1,14 @@
<script setup lang="ts">
import type { HTMLAttributes } from "vue"
import { cn } from "@/lib/utils"
const props = defineProps<{
class?: HTMLAttributes["class"]
}>()
</script>
<template>
<p :class="cn('text-sm text-muted-foreground', props.class)">
<slot />
</p>
</template>

View file

@ -0,0 +1,14 @@
<script setup lang="ts">
import type { HTMLAttributes } from "vue"
import { cn } from "@/lib/utils"
const props = defineProps<{
class?: HTMLAttributes["class"]
}>()
</script>
<template>
<div :class="cn('flex items-center p-6 pt-0', props.class)">
<slot />
</div>
</template>

View file

@ -0,0 +1,14 @@
<script setup lang="ts">
import type { HTMLAttributes } from "vue"
import { cn } from "@/lib/utils"
const props = defineProps<{
class?: HTMLAttributes["class"]
}>()
</script>
<template>
<div :class="cn('flex flex-col gap-y-1.5 p-6', props.class)">
<slot />
</div>
</template>

View file

@ -0,0 +1,18 @@
<script setup lang="ts">
import type { HTMLAttributes } from "vue"
import { cn } from "@/lib/utils"
const props = defineProps<{
class?: HTMLAttributes["class"]
}>()
</script>
<template>
<h3
:class="
cn('text-2xl font-semibold leading-none tracking-tight', props.class)
"
>
<slot />
</h3>
</template>

View file

@ -0,0 +1,6 @@
export { default as Card } from "./Card.vue"
export { default as CardContent } from "./CardContent.vue"
export { default as CardDescription } from "./CardDescription.vue"
export { default as CardFooter } from "./CardFooter.vue"
export { default as CardHeader } from "./CardHeader.vue"
export { default as CardTitle } from "./CardTitle.vue"

View file

@ -2,6 +2,7 @@
import { useI18n } from 'vue-i18n'
import { RouterLink } from 'vue-router'
import { Button } from '@/components/ui/button'
import { Card, CardContent } from '@/components/ui/card'
const { t, tm, rt } = useI18n()
@ -82,41 +83,39 @@ const exteriorItems = tm('accommodation.exterior.items') as string[]
<p class="mt-3 text-muted-foreground">{{ t('accommodation.rooms.subtitle') }}</p>
</div>
<ul class="mt-8 grid gap-6 md:grid-cols-2 lg:grid-cols-3">
<li
v-for="room in rooms"
:key="room.key"
class="overflow-hidden rounded-lg border border-border bg-card"
>
<img
:src="room.image"
alt=""
class="aspect-[4/3] w-full object-cover"
loading="lazy"
/>
<div class="p-5">
<div class="flex items-baseline justify-between gap-3">
<h3 class="font-serif text-xl font-semibold">
{{ t(`accommodation.rooms.${room.key}.name`) }}
</h3>
<span
class="rounded-full px-2 py-0.5 text-[10px] uppercase tracking-wider"
:class="
room.open
? 'bg-primary/10 text-primary'
: 'border border-border text-muted-foreground'
"
>
{{
room.open
? t('accommodation.statusOpen')
: t('accommodation.statusComingSoon')
}}
</span>
</div>
<p class="mt-3 text-sm leading-relaxed text-foreground/85">
{{ t(`accommodation.rooms.${room.key}.summary`) }}
</p>
</div>
<li v-for="room in rooms" :key="room.key">
<Card class="overflow-hidden">
<img
:src="room.image"
alt=""
class="aspect-[4/3] w-full object-cover"
loading="lazy"
/>
<CardContent class="p-5 pt-5">
<div class="flex items-baseline justify-between gap-3">
<h3 class="font-serif text-xl font-semibold">
{{ t(`accommodation.rooms.${room.key}.name`) }}
</h3>
<span
class="rounded-full px-2 py-0.5 text-[10px] uppercase tracking-wider"
:class="
room.open
? 'bg-primary/10 text-primary'
: 'border border-border text-muted-foreground'
"
>
{{
room.open
? t('accommodation.statusOpen')
: t('accommodation.statusComingSoon')
}}
</span>
</div>
<p class="mt-3 text-sm leading-relaxed text-foreground/85">
{{ t(`accommodation.rooms.${room.key}.summary`) }}
</p>
</CardContent>
</Card>
</li>
</ul>
</section>
@ -131,27 +130,25 @@ const exteriorItems = tm('accommodation.exterior.items') as string[]
<p class="mt-3 text-muted-foreground">{{ t('accommodation.cabins.subtitle') }}</p>
</div>
<ul class="mt-8 grid gap-6 md:grid-cols-3">
<li
v-for="cabin in cabins"
:key="cabin.key"
class="overflow-hidden rounded-lg border border-border bg-background"
>
<img
:src="cabin.image"
alt=""
class="aspect-[4/3] w-full object-cover"
loading="lazy"
/>
<div class="flex items-baseline justify-between gap-3 p-5">
<h3 class="font-serif text-xl font-semibold">
{{ t(`accommodation.cabins.${cabin.key}`) }}
</h3>
<span
class="rounded-full border border-border px-2 py-0.5 text-[10px] uppercase tracking-wider text-muted-foreground"
>
{{ t('accommodation.statusComingSoon') }}
</span>
</div>
<li v-for="cabin in cabins" :key="cabin.key">
<Card class="overflow-hidden bg-background">
<img
:src="cabin.image"
alt=""
class="aspect-[4/3] w-full object-cover"
loading="lazy"
/>
<div class="flex items-baseline justify-between gap-3 p-5">
<h3 class="font-serif text-xl font-semibold">
{{ t(`accommodation.cabins.${cabin.key}`) }}
</h3>
<span
class="rounded-full border border-border px-2 py-0.5 text-[10px] uppercase tracking-wider text-muted-foreground"
>
{{ t('accommodation.statusComingSoon') }}
</span>
</div>
</Card>
</li>
</ul>
</div>
@ -166,12 +163,10 @@ const exteriorItems = tm('accommodation.exterior.items') as string[]
<p class="mt-3 text-muted-foreground">{{ t('accommodation.exterior.subtitle') }}</p>
</div>
<ul class="mt-8 grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
<li
v-for="(item, i) in exteriorItems"
:key="i"
class="rounded-lg border border-dashed border-border bg-secondary/20 p-5 text-sm text-foreground/85"
>
{{ rt(item) }}
<li v-for="(item, i) in exteriorItems" :key="i">
<Card class="border-dashed bg-secondary/20 p-5 text-sm text-foreground/85">
{{ rt(item) }}
</Card>
</li>
</ul>
</section>

View file

@ -1,6 +1,7 @@
<script setup lang="ts">
import { useI18n } from 'vue-i18n'
import { RouterLink } from 'vue-router'
import { Card } from '@/components/ui/card'
const { t } = useI18n()
@ -96,18 +97,14 @@ const pillars = [
<p class="mt-3 text-muted-foreground">{{ t('concept.slowFarming.body') }}</p>
</div>
<div class="mt-8 grid gap-6 md:grid-cols-3">
<article
v-for="p in pillars"
:key="p.key"
class="rounded-lg border border-border bg-card p-6"
>
<Card v-for="p in pillars" :key="p.key" class="p-6">
<h3 class="font-serif text-xl font-semibold">
{{ t(`concept.slowFarming.${p.key}Title`) }}
</h3>
<p class="mt-3 text-sm leading-relaxed text-foreground/85">
{{ t(`concept.slowFarming.${p.key}Body`) }}
</p>
</article>
</Card>
</div>
</div>
</section>

View file

@ -2,6 +2,7 @@
import { useI18n } from 'vue-i18n'
import { RouterLink } from 'vue-router'
import { Button } from '@/components/ui/button'
import { Card, CardContent } from '@/components/ui/card'
const { t } = useI18n()
@ -64,28 +65,30 @@ const events = [
<component
:is="e.to ? 'RouterLink' : 'article'"
v-bind="e.to ? { to: e.to } : {}"
class="group block h-full overflow-hidden rounded-lg border border-border bg-card transition hover:shadow-md"
class="group block h-full"
>
<img
:src="e.image"
alt=""
class="aspect-[4/3] w-full object-cover transition group-hover:scale-[1.02]"
loading="lazy"
/>
<div class="p-5">
<p class="text-xs uppercase tracking-wider text-accent">
{{ t(`events.${e.key}.date`) }}
</p>
<h2 class="mt-1 font-serif text-xl font-semibold">
{{ t(`events.${e.key}.title`) }}
</h2>
<p class="mt-1 text-xs text-muted-foreground">
{{ t(`events.${e.key}.location`) }}
</p>
<p class="mt-3 text-sm leading-relaxed text-foreground/85">
{{ t(`events.${e.key}.description`) }}
</p>
</div>
<Card class="h-full overflow-hidden transition hover:shadow-md">
<img
:src="e.image"
alt=""
class="aspect-[4/3] w-full object-cover transition group-hover:scale-[1.02]"
loading="lazy"
/>
<CardContent class="p-5 pt-5">
<p class="text-xs uppercase tracking-wider text-accent">
{{ t(`events.${e.key}.date`) }}
</p>
<h2 class="mt-1 font-serif text-xl font-semibold">
{{ t(`events.${e.key}.title`) }}
</h2>
<p class="mt-1 text-xs text-muted-foreground">
{{ t(`events.${e.key}.location`) }}
</p>
<p class="mt-3 text-sm leading-relaxed text-foreground/85">
{{ t(`events.${e.key}.description`) }}
</p>
</CardContent>
</Card>
</component>
</li>
</ul>

View file

@ -1,5 +1,6 @@
<script setup lang="ts">
import { useI18n } from 'vue-i18n'
import { Card } from '@/components/ui/card'
const { t } = useI18n()
@ -47,24 +48,22 @@ const items = [
<section class="mx-auto max-w-7xl px-4 py-16 lg:px-6">
<ul class="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
<li
v-for="item in items"
:key="item.key"
class="group overflow-hidden rounded-lg border border-border bg-card"
>
<figure>
<img
:src="item.src"
:alt="t(`gallery.captions.${item.key}`)"
class="aspect-square w-full object-cover transition group-hover:scale-[1.02]"
loading="lazy"
/>
<figcaption
class="border-t border-border px-4 py-3 font-serif text-sm text-foreground/85"
>
{{ t(`gallery.captions.${item.key}`) }}
</figcaption>
</figure>
<li v-for="item in items" :key="item.key" class="group">
<Card class="overflow-hidden">
<figure>
<img
:src="item.src"
:alt="t(`gallery.captions.${item.key}`)"
class="aspect-square w-full object-cover transition group-hover:scale-[1.02]"
loading="lazy"
/>
<figcaption
class="border-t border-border px-4 py-3 font-serif text-sm text-foreground/85"
>
{{ t(`gallery.captions.${item.key}`) }}
</figcaption>
</figure>
</Card>
</li>
</ul>
</section>

View file

@ -2,6 +2,7 @@
import { useI18n } from 'vue-i18n'
import { RouterLink } from 'vue-router'
import { Button } from '@/components/ui/button'
import { Card, CardContent } from '@/components/ui/card'
import cosmicStag from '@/assets/cosmic-stag.avif'
import heroLandscape from '@/assets/hero-landscape.webp'
import sectionTile from '@/assets/section-tile.webp'
@ -192,25 +193,27 @@ const featuredEvents = [
v-for="e in featuredEvents"
:key="e.key"
:to="e.to"
class="group overflow-hidden rounded-lg border border-border bg-card transition hover:shadow-md"
class="group block"
>
<img
:src="e.image"
alt=""
class="aspect-[4/3] w-full object-cover transition group-hover:scale-[1.02]"
loading="lazy"
/>
<div class="p-5">
<p class="text-xs uppercase tracking-wider text-accent">
{{ t(`events.${e.key}.date`) }}
</p>
<h3 class="mt-2 font-display text-lg uppercase tracking-wider">
{{ t(`events.${e.key}.title`) }}
</h3>
<p class="mt-1 text-xs text-foreground/70">
{{ t(`events.${e.key}.location`) }}
</p>
</div>
<Card class="overflow-hidden transition hover:shadow-md">
<img
:src="e.image"
alt=""
class="aspect-[4/3] w-full object-cover transition group-hover:scale-[1.02]"
loading="lazy"
/>
<CardContent class="p-5 pt-5">
<p class="text-xs uppercase tracking-wider text-accent">
{{ t(`events.${e.key}.date`) }}
</p>
<h3 class="mt-2 font-display text-lg uppercase tracking-wider">
{{ t(`events.${e.key}.title`) }}
</h3>
<p class="mt-1 text-xs text-foreground/70">
{{ t(`events.${e.key}.location`) }}
</p>
</CardContent>
</Card>
</RouterLink>
</div>
<div class="mt-10 text-center">

View file

@ -2,6 +2,7 @@
import { useI18n } from 'vue-i18n'
import { RouterLink } from 'vue-router'
import { Button } from '@/components/ui/button'
import { Card } from '@/components/ui/card'
const { t } = useI18n()
@ -31,18 +32,14 @@ const paths = ['exchange', 'rental', 'partial', 'funded'] as const
{{ t('longStays.pathsTitle') }}
</h2>
<div class="mt-8 grid gap-6 md:grid-cols-2">
<article
v-for="key in paths"
:key="key"
class="rounded-lg border border-border bg-card p-6"
>
<Card v-for="key in paths" :key="key" class="p-6">
<h3 class="font-serif text-xl font-semibold">
{{ t(`longStays.paths.${key}Title`) }}
</h3>
<p class="mt-3 text-base leading-relaxed text-foreground/85">
{{ t(`longStays.paths.${key}Body`) }}
</p>
</article>
</Card>
</div>
</section>

View file

@ -1,6 +1,7 @@
<script setup lang="ts">
import { useI18n } from 'vue-i18n'
import { Button } from '@/components/ui/button'
import { Card } from '@/components/ui/card'
const { t } = useI18n()
@ -28,18 +29,14 @@ const categories = ['fresh', 'pantry', 'craft'] as const
{{ t('marketplace.categoriesTitle') }}
</h2>
<div class="mt-8 grid gap-6 md:grid-cols-3">
<article
v-for="key in categories"
:key="key"
class="rounded-lg border border-dashed border-border bg-card p-6"
>
<Card v-for="key in categories" :key="key" class="border-dashed p-6">
<h3 class="font-serif text-xl font-semibold">
{{ t(`marketplace.categories.${key}Title`) }}
</h3>
<p class="mt-3 text-base leading-relaxed text-foreground/85">
{{ t(`marketplace.categories.${key}Body`) }}
</p>
</article>
</Card>
</div>
</section>

View file

@ -2,6 +2,7 @@
import { useI18n } from 'vue-i18n'
import { RouterLink } from 'vue-router'
import { Button } from '@/components/ui/button'
import { Card } from '@/components/ui/card'
const { t } = useI18n()
@ -43,17 +44,15 @@ const applyKeys = ['model', 'window', 'open'] as const
<p class="mt-3 text-muted-foreground">{{ t('opportunities.groupsSubtitle') }}</p>
</div>
<ul class="mt-8 grid gap-6 md:grid-cols-2 lg:grid-cols-3">
<li
v-for="key in groups"
:key="key"
class="rounded-lg border border-border bg-card p-6"
>
<h3 class="font-serif text-xl font-semibold">
{{ t(`opportunities.groups.${key}Title`) }}
</h3>
<p class="mt-3 text-sm leading-relaxed text-foreground/85">
{{ t(`opportunities.groups.${key}Positions`) }}
</p>
<li v-for="key in groups" :key="key">
<Card class="p-6">
<h3 class="font-serif text-xl font-semibold">
{{ t(`opportunities.groups.${key}Title`) }}
</h3>
<p class="mt-3 text-sm leading-relaxed text-foreground/85">
{{ t(`opportunities.groups.${key}Positions`) }}
</p>
</Card>
</li>
</ul>
</section>
@ -65,18 +64,14 @@ const applyKeys = ['model', 'window', 'open'] as const
{{ t('opportunities.applyTitle') }}
</h2>
<div class="mt-8 grid gap-6 md:grid-cols-3">
<article
v-for="key in applyKeys"
:key="key"
class="rounded-lg border border-border bg-card p-6"
>
<Card v-for="key in applyKeys" :key="key" class="p-6">
<h3 class="font-serif text-lg font-semibold">
{{ t(`opportunities.apply.${key}Title`) }}
</h3>
<p class="mt-3 text-sm leading-relaxed text-foreground/85">
{{ t(`opportunities.apply.${key}Body`) }}
</p>
</article>
</Card>
</div>
<div class="mt-10 flex flex-wrap items-center gap-3">
<Button as-child>

View file

@ -2,6 +2,7 @@
import { useI18n } from 'vue-i18n'
import { RouterLink } from 'vue-router'
import { Button } from '@/components/ui/button'
import { Card } from '@/components/ui/card'
const { t } = useI18n()
@ -31,18 +32,14 @@ const kinds = ['weekend', 'retreat', 'gathering', 'residency'] as const
{{ t('reservations.kindsTitle') }}
</h2>
<div class="mt-8 grid gap-6 sm:grid-cols-2 lg:grid-cols-4">
<article
v-for="key in kinds"
:key="key"
class="rounded-lg border border-border bg-card p-6"
>
<Card v-for="key in kinds" :key="key" class="p-6">
<h3 class="font-serif text-xl font-semibold">
{{ t(`reservations.kinds.${key}Title`) }}
</h3>
<p class="mt-3 text-sm leading-relaxed text-foreground/85">
{{ t(`reservations.kinds.${key}Body`) }}
</p>
</article>
</Card>
</div>
</section>
@ -87,7 +84,7 @@ const kinds = ['weekend', 'retreat', 'gathering', 'residency'] as const
</div>
</div>
<aside class="rounded-lg border border-border bg-card p-6 lg:col-span-2">
<Card class="p-6 lg:col-span-2">
<h3 class="font-serif text-xl font-semibold">
{{ t('reservations.contactCard.title') }}
</h3>
@ -118,7 +115,7 @@ const kinds = ['weekend', 'retreat', 'gathering', 'residency'] as const
<dd class="mt-1">{{ t('reservations.contactCard.openingValue') }}</dd>
</div>
</dl>
</aside>
</Card>
</div>
</section>
</div>

View file

@ -2,6 +2,7 @@
import { useI18n } from 'vue-i18n'
import { RouterLink } from 'vue-router'
import { Button } from '@/components/ui/button'
import { Card } from '@/components/ui/card'
const { t } = useI18n()
@ -56,18 +57,14 @@ const applySteps = ['stepOne', 'stepTwo', 'stepThree'] as const
{{ t('symposium.includedTitle') }}
</h2>
<div class="mt-8 grid gap-6 sm:grid-cols-2 lg:grid-cols-4">
<article
v-for="key in included"
:key="key"
class="rounded-lg border border-border bg-background p-6"
>
<Card v-for="key in included" :key="key" class="bg-background p-6">
<h3 class="font-serif text-lg font-semibold">
{{ t(`symposium.included.${key}Title`) }}
</h3>
<p class="mt-3 text-sm leading-relaxed text-foreground/85">
{{ t(`symposium.included.${key}Body`) }}
</p>
</article>
</Card>
</div>
</div>
</section>
@ -89,17 +86,15 @@ const applySteps = ['stepOne', 'stepTwo', 'stepThree'] as const
{{ t('symposium.applyTitle') }}
</h2>
<ol class="mt-8 grid gap-6 md:grid-cols-3">
<li
v-for="step in applySteps"
:key="step"
class="rounded-lg border border-border bg-card p-6"
>
<h3 class="font-serif text-lg font-semibold">
{{ t(`symposium.apply.${step}Title`) }}
</h3>
<p class="mt-3 text-sm leading-relaxed text-foreground/85">
{{ t(`symposium.apply.${step}Body`) }}
</p>
<li v-for="step in applySteps" :key="step">
<Card class="p-6">
<h3 class="font-serif text-lg font-semibold">
{{ t(`symposium.apply.${step}Title`) }}
</h3>
<p class="mt-3 text-sm leading-relaxed text-foreground/85">
{{ t(`symposium.apply.${step}Body`) }}
</p>
</Card>
</li>
</ol>
<div class="mt-10 flex flex-wrap items-center gap-3">

View file

@ -1,5 +1,6 @@
<script setup lang="ts">
import { useI18n } from 'vue-i18n'
import { Card } from '@/components/ui/card'
const { t } = useI18n()
@ -55,11 +56,7 @@ const team = ['patrick', 'coco', 'charlie'] as const
<p class="mt-3 text-muted-foreground">{{ t('vision.philosophySubtitle') }}</p>
</div>
<div class="mt-8 grid gap-6 sm:grid-cols-2 lg:grid-cols-5">
<article
v-for="(p, i) in philosophy"
:key="p"
class="rounded-lg border border-border bg-background p-5"
>
<Card v-for="(p, i) in philosophy" :key="p" class="bg-background p-5">
<div class="font-serif text-2xl text-accent">{{ i + 1 }}</div>
<h3 class="mt-2 font-serif text-lg font-semibold">
{{ t(`vision.philosophy.${p}Title`) }}
@ -67,7 +64,7 @@ const team = ['patrick', 'coco', 'charlie'] as const
<p class="mt-2 text-sm leading-relaxed text-foreground/85">
{{ t(`vision.philosophy.${p}Body`) }}
</p>
</article>
</Card>
</div>
</div>
</section>
@ -81,16 +78,12 @@ const team = ['patrick', 'coco', 'charlie'] as const
<p class="mt-3 text-muted-foreground">{{ t('vision.pillarsSubtitle') }}</p>
</div>
<div class="mt-8 grid gap-6 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
<article
v-for="p in pillars"
:key="p"
class="rounded-lg border border-border bg-card p-6"
>
<Card v-for="p in pillars" :key="p" class="p-6">
<h3 class="font-serif text-xl font-semibold">{{ t(`vision.pillars.${p}Title`) }}</h3>
<p class="mt-3 text-sm leading-relaxed text-foreground/85">
{{ t(`vision.pillars.${p}Body`) }}
</p>
</article>
</Card>
</div>
</section>
@ -101,11 +94,7 @@ const team = ['patrick', 'coco', 'charlie'] as const
{{ t('vision.teamTitle') }}
</h2>
<div class="mt-8 grid gap-6 md:grid-cols-3">
<article
v-for="m in team"
:key="m"
class="rounded-lg border border-border bg-card p-6"
>
<Card v-for="m in team" :key="m" class="p-6">
<h3 class="font-serif text-xl font-semibold">{{ t(`vision.team.${m}Name`) }}</h3>
<p class="mt-1 text-xs uppercase tracking-wider text-accent">
{{ t(`vision.team.${m}Role`) }}
@ -113,7 +102,7 @@ const team = ['patrick', 'coco', 'charlie'] as const
<p class="mt-3 text-sm leading-relaxed text-foreground/85">
{{ t(`vision.team.${m}Body`) }}
</p>
</article>
</Card>
</div>
</div>
</section>