feat(layout): shared tiled-panel page pattern across secondary pages

Calibrate all non-home pages to one layout language matching the Wix
design: the celtic-knot tile becomes the page background (shown in the
gutters), content floats in a single near-black rounded panel, and cards
inside are plain cards.

- Add PageShell (floating bg-panel card), PageSection (plain content
  block), and PageHero (centered hero; left grid w/ aside image) under
  src/components/layout/.
- Tile the body background and add a --panel token; the home page's
  fixed landscape sits above this layer, so home is unchanged.
- Re-skin the 11 secondary views through these primitives and unify
  headings to the font-display uppercase idiom.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Padreug 2026-06-14 16:43:27 +02:00
commit fe2a6912ce
15 changed files with 459 additions and 408 deletions

View file

@ -0,0 +1,58 @@
<script setup lang="ts">
// Top-of-page hero, the first block inside PageShell's panel. Centered to
// match the Wix layout; falls back to a left-aligned two-column grid when an
// `aside` slot (e.g. a hero image) is supplied. Title uses the Aboreto
// display face in the home page's uppercase / wide-tracking idiom.
import { useSlots } from 'vue'
withDefaults(
defineProps<{
kicker?: string
title: string
lede?: string
}>(),
{},
)
const slots = useSlots()
const hasAside = () => !!slots.aside
</script>
<template>
<section class="px-6 pt-16 pb-12 sm:px-10 lg:px-16 lg:pt-24 lg:pb-16">
<div
v-if="hasAside()"
class="grid items-center gap-10 lg:grid-cols-2 lg:gap-14"
>
<div class="max-w-3xl">
<p v-if="kicker" class="font-display text-xs uppercase tracking-[0.3em] text-accent">
{{ kicker }}
</p>
<h1 class="mt-3 font-display text-4xl uppercase leading-tight tracking-wider md:text-5xl">
{{ title }}
</h1>
<p v-if="lede" class="mt-6 max-w-prose text-lg leading-relaxed text-foreground/90">
{{ lede }}
</p>
<slot />
</div>
<slot name="aside" />
</div>
<div v-else class="mx-auto max-w-3xl text-center">
<h1 class="font-display text-4xl uppercase leading-tight tracking-wider md:text-6xl">
{{ title }}
</h1>
<p
v-if="kicker"
class="mt-6 font-display text-2xl uppercase tracking-wider text-accent md:text-3xl"
>
{{ kicker }}
</p>
<p v-if="lede" class="mx-auto mt-6 max-w-2xl text-base leading-relaxed text-foreground/90 md:text-lg">
{{ lede }}
</p>
<slot />
</div>
</section>
</template>

View file

@ -0,0 +1,33 @@
<script setup lang="ts">
// A content block inside PageShell's panel. No background of its own the
// near-black panel shows through; cards/images provide the contrast. Just
// constrains width and supplies the shared vertical rhythm and gutter.
withDefaults(
defineProps<{
/** Inner content max-width. `wide` fills the panel. */
width?: 'prose' | 'narrow' | 'wide'
/** Vertical rhythm. */
padding?: 'default' | 'compact'
}>(),
{ width: 'wide', padding: 'default' },
)
const widthClass = {
prose: 'max-w-3xl',
narrow: 'max-w-4xl',
wide: 'max-w-none',
} as const
const padClass = {
default: 'py-16 lg:py-20',
compact: 'py-12 lg:py-14',
} as const
</script>
<template>
<section class="px-6 sm:px-10 lg:px-16" :class="padClass[padding]">
<div :class="[widthClass[width], width !== 'wide' ? 'mx-auto' : '']">
<slot />
</div>
</section>
</template>

View file

@ -0,0 +1,14 @@
<script setup lang="ts">
// The floating content panel: a near-black rounded card that sits on the
// tiled celtic-knot page background, leaving a gutter of tile visible around
// it. Wrap every secondary page's content in this so they share the home
// page / Wix layout language. Sections go inside as plain blocks.
</script>
<template>
<div class="px-3 pt-3 pb-6 sm:px-4 sm:pt-4 lg:px-6">
<div class="mx-auto max-w-6xl overflow-hidden rounded-3xl bg-panel shadow-2xl">
<slot />
</div>
</div>
</template>

View file

@ -25,6 +25,8 @@
--border: hsl(151 50% 18%);
--input: hsl(151 50% 18%);
--ring: hsl(43 64% 50%);
/* Near-black green of the content panel that floats on the tiled bg. */
--panel: hsl(151 55% 4%);
--radius: 0.5rem;
}
@ -48,6 +50,7 @@
--border: hsl(151 50% 15%);
--input: hsl(151 50% 15%);
--ring: hsl(43 64% 52%);
--panel: hsl(151 55% 3%);
}
@theme inline {
@ -70,6 +73,7 @@
--color-border: var(--border);
--color-input: var(--input);
--color-ring: var(--ring);
--color-panel: var(--panel);
--radius-lg: var(--radius);
--radius-md: calc(var(--radius) - 2px);
--radius-sm: calc(var(--radius) - 4px);
@ -85,6 +89,12 @@
}
body {
@apply bg-background text-foreground;
/* Celtic-knot tile is the page background; it shows in the gutters
around the floating content panel (see PageShell). The home page's
fixed landscape image sits above this layer, so home is unaffected. */
background-image: url('./assets/section-tile.webp');
background-repeat: repeat;
background-position: center top;
font-family: var(--font-sans);
font-weight: 300;
}

View file

@ -4,6 +4,9 @@ import { RouterLink } from 'vue-router'
import { Button } from '@/components/ui/button'
import { Card, CardContent } from '@/components/ui/card'
import { Badge } from '@/components/ui/badge'
import PageHero from '@/components/layout/PageHero.vue'
import PageSection from '@/components/layout/PageSection.vue'
import PageShell from '@/components/layout/PageShell.vue'
const { t, tm, rt } = useI18n()
@ -59,29 +62,21 @@ const exteriorItems = tm('accommodation.exterior.items') as string[]
</script>
<template>
<div>
<PageShell>
<!-- Hero -->
<section class="border-b border-border bg-secondary/40">
<div class="mx-auto max-w-4xl px-4 py-16 lg:px-6 lg:py-20">
<p class="text-xs uppercase tracking-[0.2em] text-accent">
{{ t('accommodation.hero.kicker') }}
</p>
<h1 class="mt-3 font-serif text-5xl font-semibold leading-tight tracking-tight md:text-6xl">
{{ t('accommodation.hero.title') }}
</h1>
<p class="mt-6 max-w-prose text-lg leading-relaxed text-foreground/90">
{{ t('accommodation.hero.lede') }}
</p>
</div>
</section>
<PageHero
:kicker="t('accommodation.hero.kicker')"
:title="t('accommodation.hero.title')"
:lede="t('accommodation.hero.lede')"
/>
<!-- Rooms -->
<section class="mx-auto max-w-7xl px-4 py-16 lg:px-6">
<PageSection>
<div class="max-w-3xl">
<h2 class="font-serif text-3xl font-semibold tracking-tight">
<h2 class="font-display text-2xl uppercase tracking-wider md:text-3xl">
{{ t('accommodation.rooms.title') }}
</h2>
<p class="mt-3 text-muted-foreground">{{ t('accommodation.rooms.subtitle') }}</p>
<p class="mt-3 text-foreground/80">{{ 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">
@ -94,7 +89,7 @@ const exteriorItems = tm('accommodation.exterior.items') as string[]
/>
<CardContent class="p-5 pt-5">
<div class="flex items-baseline justify-between gap-3">
<h3 class="font-serif text-xl font-semibold">
<h3 class="font-display text-lg uppercase tracking-wider">
{{ t(`accommodation.rooms.${room.key}.name`) }}
</h3>
<Badge
@ -115,50 +110,48 @@ const exteriorItems = tm('accommodation.exterior.items') as string[]
</Card>
</li>
</ul>
</section>
</PageSection>
<!-- Cabins -->
<section class="border-y border-border bg-card">
<div class="mx-auto max-w-7xl px-4 py-16 lg:px-6">
<div class="max-w-3xl">
<h2 class="font-serif text-3xl font-semibold tracking-tight">
{{ t('accommodation.cabins.title') }}
</h2>
<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">
<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>
<Badge
variant="outline"
class="text-[10px] uppercase tracking-wider text-muted-foreground"
>
{{ t('accommodation.statusComingSoon') }}
</Badge>
</div>
</Card>
</li>
</ul>
<PageSection>
<div class="max-w-3xl">
<h2 class="font-display text-2xl uppercase tracking-wider md:text-3xl">
{{ t('accommodation.cabins.title') }}
</h2>
<p class="mt-3 text-foreground/80">{{ t('accommodation.cabins.subtitle') }}</p>
</div>
</section>
<ul class="mt-8 grid gap-6 md:grid-cols-3">
<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-display text-lg uppercase tracking-wider">
{{ t(`accommodation.cabins.${cabin.key}`) }}
</h3>
<Badge
variant="outline"
class="text-[10px] uppercase tracking-wider text-muted-foreground"
>
{{ t('accommodation.statusComingSoon') }}
</Badge>
</div>
</Card>
</li>
</ul>
</PageSection>
<!-- Exterior, coming next -->
<section class="mx-auto max-w-7xl px-4 py-16 lg:px-6">
<PageSection>
<div class="max-w-3xl">
<h2 class="font-serif text-3xl font-semibold tracking-tight">
<h2 class="font-display text-2xl uppercase tracking-wider md:text-3xl">
{{ t('accommodation.exterior.title') }}
</h2>
<p class="mt-3 text-muted-foreground">{{ t('accommodation.exterior.subtitle') }}</p>
<p class="mt-3 text-foreground/80">{{ 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">
@ -167,11 +160,11 @@ const exteriorItems = tm('accommodation.exterior.items') as string[]
</Card>
</li>
</ul>
</section>
</PageSection>
<!-- CTAs -->
<section class="border-t border-border bg-secondary/30">
<div class="mx-auto flex max-w-7xl flex-wrap items-center gap-3 px-4 py-12 lg:px-6">
<PageSection padding="compact">
<div class="flex flex-wrap items-center gap-3">
<Button as-child>
<a href="mailto:chateaudufaune@ariege.io?subject=R%C3%A9servation%20%E2%80%94%20Ch%C3%A2teau%20du%20Faune">
{{ t('accommodation.ctaReserve') }}
@ -181,6 +174,6 @@ const exteriorItems = tm('accommodation.exterior.items') as string[]
<RouterLink to="/reservations"> {{ t('accommodation.ctaBack') }}</RouterLink>
</Button>
</div>
</section>
</div>
</PageSection>
</PageShell>
</template>

View file

@ -2,6 +2,9 @@
import { useI18n } from 'vue-i18n'
import { RouterLink } from 'vue-router'
import { Card } from '@/components/ui/card'
import PageHero from '@/components/layout/PageHero.vue'
import PageSection from '@/components/layout/PageSection.vue'
import PageShell from '@/components/layout/PageShell.vue'
const { t } = useI18n()
@ -13,35 +16,27 @@ const pillars = [
</script>
<template>
<div>
<PageShell>
<!-- Hero -->
<section class="border-b border-border bg-secondary/40">
<div class="mx-auto max-w-4xl px-4 py-16 lg:px-6 lg:py-24">
<p class="text-xs uppercase tracking-[0.2em] text-accent">
{{ t('concept.hero.kicker') }}
</p>
<h1 class="mt-3 font-serif text-5xl font-semibold leading-tight tracking-tight md:text-6xl">
{{ t('concept.hero.title') }}
</h1>
<p class="mt-6 max-w-prose text-lg leading-relaxed text-foreground/90">
{{ t('concept.hero.lede') }}
</p>
</div>
</section>
<PageHero
:kicker="t('concept.hero.kicker')"
:title="t('concept.hero.title')"
:lede="t('concept.hero.lede')"
/>
<!-- Mission -->
<section class="mx-auto max-w-4xl px-4 py-16 lg:px-6">
<h2 class="font-serif text-3xl font-semibold tracking-tight">
<PageSection width="narrow">
<h2 class="font-display text-2xl uppercase tracking-wider md:text-3xl">
{{ t('concept.mission.title') }}
</h2>
<p class="mt-5 max-w-prose text-base leading-relaxed">
{{ t('concept.mission.body') }}
</p>
</section>
</PageSection>
<!-- Land art & residencies -->
<section class="bg-card border-y border-border">
<div class="mx-auto grid max-w-7xl gap-10 px-4 py-16 lg:grid-cols-2 lg:gap-14 lg:px-6">
<PageSection>
<div class="grid items-center gap-10 lg:grid-cols-2 lg:gap-14">
<img
src="https://static.wixstatic.com/media/11062b_7c683da5bcbd44d7b0d2ddbaf4693625~mv2.jpg"
alt=""
@ -49,7 +44,7 @@ const pillars = [
loading="lazy"
/>
<div>
<h2 class="font-serif text-3xl font-semibold tracking-tight">
<h2 class="font-display text-2xl uppercase tracking-wider md:text-3xl">
{{ t('concept.landArt.title') }}
</h2>
<p class="mt-5 max-w-prose leading-relaxed">{{ t('concept.landArt.body') }}</p>
@ -61,13 +56,13 @@ const pillars = [
</RouterLink>
</div>
</div>
</section>
</PageSection>
<!-- Farmstand & boutique -->
<section class="mx-auto max-w-7xl px-4 py-16 lg:px-6">
<div class="grid gap-10 lg:grid-cols-2 lg:gap-14">
<PageSection>
<div class="grid items-center gap-10 lg:grid-cols-2 lg:gap-14">
<div class="order-2 lg:order-1">
<h2 class="font-serif text-3xl font-semibold tracking-tight">
<h2 class="font-display text-2xl uppercase tracking-wider md:text-3xl">
{{ t('concept.farmstand.title') }}
</h2>
<p class="mt-5 max-w-prose leading-relaxed">{{ t('concept.farmstand.body') }}</p>
@ -85,33 +80,31 @@ const pillars = [
loading="lazy"
/>
</div>
</section>
</PageSection>
<!-- Slow farming -->
<section class="bg-secondary/30 border-y border-border">
<div class="mx-auto max-w-7xl px-4 py-16 lg:px-6">
<div class="max-w-3xl">
<h2 class="font-serif text-3xl font-semibold tracking-tight">
{{ t('concept.slowFarming.title') }}
</h2>
<p class="mt-3 text-muted-foreground">{{ t('concept.slowFarming.body') }}</p>
</div>
<div class="mt-8 grid gap-6 md:grid-cols-3">
<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>
</Card>
</div>
<PageSection>
<div class="max-w-3xl">
<h2 class="font-display text-2xl uppercase tracking-wider md:text-3xl">
{{ t('concept.slowFarming.title') }}
</h2>
<p class="mt-3 text-foreground/80">{{ t('concept.slowFarming.body') }}</p>
</div>
</section>
<div class="mt-8 grid gap-6 md:grid-cols-3">
<Card v-for="p in pillars" :key="p.key" class="p-6">
<h3 class="font-display text-lg uppercase tracking-wider">
{{ 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>
</Card>
</div>
</PageSection>
<!-- Events in nature -->
<section class="mx-auto max-w-7xl px-4 py-16 lg:px-6">
<div class="grid gap-10 lg:grid-cols-2 lg:gap-14">
<PageSection>
<div class="grid items-center gap-10 lg:grid-cols-2 lg:gap-14">
<img
src="https://static.wixstatic.com/media/11062b_a56b905cae7c424c84cbaa1677d59ec5~mv2.jpg"
alt=""
@ -119,7 +112,7 @@ const pillars = [
loading="lazy"
/>
<div>
<h2 class="font-serif text-3xl font-semibold tracking-tight">
<h2 class="font-display text-2xl uppercase tracking-wider md:text-3xl">
{{ t('concept.events.title') }}
</h2>
<p class="mt-5 max-w-prose leading-relaxed">{{ t('concept.events.body') }}</p>
@ -131,6 +124,6 @@ const pillars = [
</RouterLink>
</div>
</div>
</section>
</div>
</PageSection>
</PageShell>
</template>

View file

@ -4,6 +4,9 @@ import { RouterLink } from 'vue-router'
import { Button } from '@/components/ui/button'
import { Card, CardContent } from '@/components/ui/card'
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'
import PageHero from '@/components/layout/PageHero.vue'
import PageSection from '@/components/layout/PageSection.vue'
import PageShell from '@/components/layout/PageShell.vue'
const { t } = useI18n()
@ -37,30 +40,23 @@ const events = [
</script>
<template>
<div>
<section class="border-b border-border bg-secondary/40">
<div class="mx-auto max-w-4xl px-4 py-16 lg:px-6 lg:py-20">
<p class="text-xs uppercase tracking-[0.2em] text-accent">
{{ t('events.page.kicker') }}
</p>
<h1 class="mt-3 font-serif text-5xl font-semibold leading-tight tracking-tight md:text-6xl">
{{ t('events.page.title') }}
</h1>
<p class="mt-6 max-w-prose text-lg leading-relaxed text-foreground/90">
{{ t('events.page.lede') }}
</p>
<Alert class="mt-8 max-w-prose bg-card text-foreground/85">
<AlertTitle class="text-xs uppercase tracking-wider text-accent">
{{ t('events.page.noteHeading') }}
</AlertTitle>
<AlertDescription>
{{ t('events.page.calendarNote') }}
</AlertDescription>
</Alert>
</div>
</section>
<PageShell>
<PageHero
:kicker="t('events.page.kicker')"
:title="t('events.page.title')"
:lede="t('events.page.lede')"
>
<Alert class="mt-8 max-w-prose bg-card text-foreground/85">
<AlertTitle class="text-xs uppercase tracking-wider text-accent">
{{ t('events.page.noteHeading') }}
</AlertTitle>
<AlertDescription>
{{ t('events.page.calendarNote') }}
</AlertDescription>
</Alert>
</PageHero>
<section class="mx-auto max-w-7xl px-4 py-16 lg:px-6">
<PageSection>
<ul class="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
<li v-for="e in events" :key="e.key">
<component
@ -79,7 +75,7 @@ const events = [
<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">
<h2 class="mt-1 font-display text-lg uppercase tracking-wider">
{{ t(`events.${e.key}.title`) }}
</h2>
<p class="mt-1 text-xs text-muted-foreground">
@ -99,6 +95,6 @@ const events = [
<RouterLink to="/symposium">{{ t('nav.symposium') }} </RouterLink>
</Button>
</div>
</section>
</div>
</PageSection>
</PageShell>
</template>

View file

@ -1,6 +1,9 @@
<script setup lang="ts">
import { useI18n } from 'vue-i18n'
import { Card } from '@/components/ui/card'
import PageHero from '@/components/layout/PageHero.vue'
import PageSection from '@/components/layout/PageSection.vue'
import PageShell from '@/components/layout/PageShell.vue'
const { t } = useI18n()
@ -33,20 +36,14 @@ const items = [
</script>
<template>
<div>
<section class="border-b border-border bg-secondary/40">
<div class="mx-auto max-w-4xl px-4 py-16 lg:px-6 lg:py-20">
<p class="text-xs uppercase tracking-[0.2em] text-accent">{{ t('gallery.kicker') }}</p>
<h1 class="mt-3 font-serif text-5xl font-semibold leading-tight tracking-tight md:text-6xl">
{{ t('gallery.title') }}
</h1>
<p class="mt-6 max-w-prose text-lg leading-relaxed text-foreground/90">
{{ t('gallery.lede') }}
</p>
</div>
</section>
<PageShell>
<PageHero
:kicker="t('gallery.kicker')"
:title="t('gallery.title')"
:lede="t('gallery.lede')"
/>
<section class="mx-auto max-w-7xl px-4 py-16 lg:px-6">
<PageSection>
<ul class="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
<li v-for="item in items" :key="item.key" class="group">
<Card class="overflow-hidden">
@ -58,7 +55,7 @@ const items = [
loading="lazy"
/>
<figcaption
class="border-t border-border px-4 py-3 font-serif text-sm text-foreground/85"
class="border-t border-border px-4 py-3 text-sm text-foreground/85"
>
{{ t(`gallery.captions.${item.key}`) }}
</figcaption>
@ -66,6 +63,6 @@ const items = [
</Card>
</li>
</ul>
</section>
</div>
</PageSection>
</PageShell>
</template>

View file

@ -3,6 +3,9 @@ import { useI18n } from 'vue-i18n'
import { RouterLink } from 'vue-router'
import { Button } from '@/components/ui/button'
import { Card } from '@/components/ui/card'
import PageHero from '@/components/layout/PageHero.vue'
import PageSection from '@/components/layout/PageSection.vue'
import PageShell from '@/components/layout/PageShell.vue'
const { t } = useI18n()
@ -10,30 +13,22 @@ const paths = ['exchange', 'rental', 'partial', 'funded'] as const
</script>
<template>
<div>
<PageShell>
<!-- Hero -->
<section class="border-b border-border bg-secondary/40">
<div class="mx-auto max-w-4xl px-4 py-16 lg:px-6 lg:py-20">
<p class="text-xs uppercase tracking-[0.2em] text-accent">
{{ t('longStays.hero.kicker') }}
</p>
<h1 class="mt-3 font-serif text-5xl font-semibold leading-tight tracking-tight md:text-6xl">
{{ t('longStays.hero.title') }}
</h1>
<p class="mt-6 max-w-prose text-lg leading-relaxed text-foreground/90">
{{ t('longStays.hero.lede') }}
</p>
</div>
</section>
<PageHero
:kicker="t('longStays.hero.kicker')"
:title="t('longStays.hero.title')"
:lede="t('longStays.hero.lede')"
/>
<!-- Paths -->
<section class="mx-auto max-w-7xl px-4 py-16 lg:px-6">
<h2 class="font-serif text-3xl font-semibold tracking-tight">
<PageSection>
<h2 class="font-display text-2xl uppercase tracking-wider md:text-3xl">
{{ t('longStays.pathsTitle') }}
</h2>
<div class="mt-8 grid gap-6 md:grid-cols-2">
<Card v-for="key in paths" :key="key" class="p-6">
<h3 class="font-serif text-xl font-semibold">
<h3 class="font-display text-lg uppercase tracking-wider">
{{ t(`longStays.paths.${key}Title`) }}
</h3>
<p class="mt-3 text-base leading-relaxed text-foreground/85">
@ -41,11 +36,11 @@ const paths = ['exchange', 'rental', 'partial', 'funded'] as const
</p>
</Card>
</div>
</section>
</PageSection>
<!-- Symposium teaser -->
<section class="border-t border-border bg-secondary/30">
<div class="mx-auto grid max-w-7xl items-center gap-10 px-4 py-16 lg:grid-cols-2 lg:gap-14 lg:px-6">
<PageSection>
<div class="grid items-center gap-10 lg:grid-cols-2 lg:gap-14">
<img
src="https://static.wixstatic.com/media/11062b_322db9c671074a6cb3d76de93ac013f0~mv2.jpg"
alt=""
@ -53,7 +48,7 @@ const paths = ['exchange', 'rental', 'partial', 'funded'] as const
loading="lazy"
/>
<div>
<h2 class="font-serif text-3xl font-semibold tracking-tight">
<h2 class="font-display text-2xl uppercase tracking-wider md:text-3xl">
{{ t('longStays.symposiumTeaser.title') }}
</h2>
<p class="mt-5 max-w-prose leading-relaxed">
@ -69,6 +64,6 @@ const paths = ['exchange', 'rental', 'partial', 'funded'] as const
</div>
</div>
</div>
</section>
</div>
</PageSection>
</PageShell>
</template>

View file

@ -2,6 +2,9 @@
import { useI18n } from 'vue-i18n'
import { Button } from '@/components/ui/button'
import { Card } from '@/components/ui/card'
import PageHero from '@/components/layout/PageHero.vue'
import PageSection from '@/components/layout/PageSection.vue'
import PageShell from '@/components/layout/PageShell.vue'
const { t } = useI18n()
@ -9,28 +12,20 @@ const categories = ['fresh', 'pantry', 'craft'] as const
</script>
<template>
<div>
<section class="border-b border-border bg-secondary/40">
<div class="mx-auto max-w-4xl px-4 py-16 lg:px-6 lg:py-20">
<p class="text-xs uppercase tracking-[0.2em] text-accent">
{{ t('marketplace.kicker') }}
</p>
<h1 class="mt-3 font-serif text-5xl font-semibold leading-tight tracking-tight md:text-6xl">
{{ t('marketplace.title') }}
</h1>
<p class="mt-6 max-w-prose text-lg leading-relaxed text-foreground/90">
{{ t('marketplace.lede') }}
</p>
</div>
</section>
<PageShell>
<PageHero
:kicker="t('marketplace.kicker')"
:title="t('marketplace.title')"
:lede="t('marketplace.lede')"
/>
<section class="mx-auto max-w-7xl px-4 py-16 lg:px-6">
<h2 class="font-serif text-3xl font-semibold tracking-tight">
<PageSection>
<h2 class="font-display text-2xl uppercase tracking-wider md:text-3xl">
{{ t('marketplace.categoriesTitle') }}
</h2>
<div class="mt-8 grid gap-6 md:grid-cols-3">
<Card v-for="key in categories" :key="key" class="border-dashed p-6">
<h3 class="font-serif text-xl font-semibold">
<h3 class="font-display text-lg uppercase tracking-wider">
{{ t(`marketplace.categories.${key}Title`) }}
</h3>
<p class="mt-3 text-base leading-relaxed text-foreground/85">
@ -38,11 +33,11 @@ const categories = ['fresh', 'pantry', 'craft'] as const
</p>
</Card>
</div>
</section>
</PageSection>
<section class="border-t border-border bg-secondary/30">
<div class="mx-auto max-w-4xl px-4 py-12 text-center lg:px-6">
<p class="font-serif text-lg italic text-foreground/85">
<PageSection width="narrow" padding="compact">
<div class="text-center">
<p class="text-lg italic text-foreground/85">
{{ t('marketplace.notice') }}
</p>
<Button as-child variant="outline" class="mt-6">
@ -51,6 +46,6 @@ const categories = ['fresh', 'pantry', 'craft'] as const
</a>
</Button>
</div>
</section>
</div>
</PageSection>
</PageShell>
</template>

View file

@ -4,7 +4,7 @@ import { RouterLink } from 'vue-router'
<template>
<article class="mx-auto max-w-3xl px-4 py-24 text-center">
<h1 class="font-serif text-5xl font-semibold tracking-tight">404</h1>
<h1 class="font-display text-6xl uppercase tracking-wider">404</h1>
<p class="mt-4 text-muted-foreground">Page introuvable / Page not found.</p>
<RouterLink to="/" class="mt-8 inline-block text-sm underline hover:text-primary">
Château du Faune

View file

@ -3,6 +3,9 @@ import { useI18n } from 'vue-i18n'
import { RouterLink } from 'vue-router'
import { Button } from '@/components/ui/button'
import { Card } from '@/components/ui/card'
import PageHero from '@/components/layout/PageHero.vue'
import PageSection from '@/components/layout/PageSection.vue'
import PageShell from '@/components/layout/PageShell.vue'
const { t } = useI18n()
@ -19,34 +22,26 @@ const applyKeys = ['model', 'window', 'open'] as const
</script>
<template>
<div>
<PageShell>
<!-- Hero -->
<section class="border-b border-border bg-secondary/40">
<div class="mx-auto max-w-4xl px-4 py-16 lg:px-6 lg:py-20">
<p class="text-xs uppercase tracking-[0.2em] text-accent">
{{ t('opportunities.hero.kicker') }}
</p>
<h1 class="mt-3 font-serif text-5xl font-semibold leading-tight tracking-tight md:text-6xl">
{{ t('opportunities.hero.title') }}
</h1>
<p class="mt-6 max-w-prose text-lg leading-relaxed text-foreground/90">
{{ t('opportunities.hero.lede') }}
</p>
</div>
</section>
<PageHero
:kicker="t('opportunities.hero.kicker')"
:title="t('opportunities.hero.title')"
:lede="t('opportunities.hero.lede')"
/>
<!-- Groups of positions -->
<section class="mx-auto max-w-7xl px-4 py-16 lg:px-6">
<PageSection>
<div class="max-w-3xl">
<h2 class="font-serif text-3xl font-semibold tracking-tight">
<h2 class="font-display text-2xl uppercase tracking-wider md:text-3xl">
{{ t('opportunities.groupsTitle') }}
</h2>
<p class="mt-3 text-muted-foreground">{{ t('opportunities.groupsSubtitle') }}</p>
<p class="mt-3 text-foreground/80">{{ 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">
<Card class="p-6">
<h3 class="font-serif text-xl font-semibold">
<h3 class="font-display text-lg uppercase tracking-wider">
{{ t(`opportunities.groups.${key}Title`) }}
</h3>
<p class="mt-3 text-sm leading-relaxed text-foreground/85">
@ -55,35 +50,33 @@ const applyKeys = ['model', 'window', 'open'] as const
</Card>
</li>
</ul>
</section>
</PageSection>
<!-- How we hire -->
<section class="border-t border-border bg-secondary/30">
<div class="mx-auto max-w-7xl px-4 py-16 lg:px-6">
<h2 class="font-serif text-3xl font-semibold tracking-tight">
{{ t('opportunities.applyTitle') }}
</h2>
<div class="mt-8 grid gap-6 md:grid-cols-3">
<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>
</Card>
</div>
<div class="mt-10 flex flex-wrap items-center gap-3">
<Button as-child>
<a href="mailto:chateaudufaune@ariege.io?subject=Application%20%E2%80%94%20Ch%C3%A2teau%20du%20Faune">
{{ t('opportunities.ctaApply') }}
</a>
</Button>
<Button as-child variant="outline">
<RouterLink to="/symposium">{{ t('opportunities.ctaSymposium') }}</RouterLink>
</Button>
</div>
<PageSection>
<h2 class="font-display text-2xl uppercase tracking-wider md:text-3xl">
{{ t('opportunities.applyTitle') }}
</h2>
<div class="mt-8 grid gap-6 md:grid-cols-3">
<Card v-for="key in applyKeys" :key="key" class="p-6">
<h3 class="font-display text-lg uppercase tracking-wider">
{{ t(`opportunities.apply.${key}Title`) }}
</h3>
<p class="mt-3 text-sm leading-relaxed text-foreground/85">
{{ t(`opportunities.apply.${key}Body`) }}
</p>
</Card>
</div>
</section>
</div>
<div class="mt-10 flex flex-wrap items-center gap-3">
<Button as-child>
<a href="mailto:chateaudufaune@ariege.io?subject=Application%20%E2%80%94%20Ch%C3%A2teau%20du%20Faune">
{{ t('opportunities.ctaApply') }}
</a>
</Button>
<Button as-child variant="outline">
<RouterLink to="/symposium">{{ t('opportunities.ctaSymposium') }}</RouterLink>
</Button>
</div>
</PageSection>
</PageShell>
</template>

View file

@ -3,6 +3,9 @@ import { useI18n } from 'vue-i18n'
import { RouterLink } from 'vue-router'
import { Button } from '@/components/ui/button'
import { Card } from '@/components/ui/card'
import PageHero from '@/components/layout/PageHero.vue'
import PageSection from '@/components/layout/PageSection.vue'
import PageShell from '@/components/layout/PageShell.vue'
const { t } = useI18n()
@ -10,30 +13,22 @@ const kinds = ['weekend', 'retreat', 'gathering', 'residency'] as const
</script>
<template>
<div>
<PageShell>
<!-- Hero -->
<section class="border-b border-border bg-secondary/40">
<div class="mx-auto max-w-4xl px-4 py-16 lg:px-6 lg:py-20">
<p class="text-xs uppercase tracking-[0.2em] text-accent">
{{ t('reservations.hero.kicker') }}
</p>
<h1 class="mt-3 font-serif text-5xl font-semibold leading-tight tracking-tight md:text-6xl">
{{ t('reservations.hero.title') }}
</h1>
<p class="mt-6 max-w-prose text-lg leading-relaxed text-foreground/90">
{{ t('reservations.hero.lede') }}
</p>
</div>
</section>
<PageHero
:kicker="t('reservations.hero.kicker')"
:title="t('reservations.hero.title')"
:lede="t('reservations.hero.lede')"
/>
<!-- Kinds of stays -->
<section class="mx-auto max-w-7xl px-4 py-16 lg:px-6">
<h2 class="font-serif text-3xl font-semibold tracking-tight">
<PageSection>
<h2 class="font-display text-2xl uppercase tracking-wider md:text-3xl">
{{ t('reservations.kindsTitle') }}
</h2>
<div class="mt-8 grid gap-6 sm:grid-cols-2 lg:grid-cols-4">
<Card v-for="key in kinds" :key="key" class="p-6">
<h3 class="font-serif text-xl font-semibold">
<h3 class="font-display text-lg uppercase tracking-wider">
{{ t(`reservations.kinds.${key}Title`) }}
</h3>
<p class="mt-3 text-sm leading-relaxed text-foreground/85">
@ -41,34 +36,32 @@ const kinds = ['weekend', 'retreat', 'gathering', 'residency'] as const
</p>
</Card>
</div>
</section>
</PageSection>
<!-- Values -->
<section class="border-y border-border bg-card">
<div class="mx-auto max-w-4xl px-4 py-16 lg:px-6">
<h2 class="font-serif text-3xl font-semibold tracking-tight">
{{ t('reservations.valuesTitle') }}
</h2>
<p class="mt-5 max-w-prose text-base leading-relaxed">
{{ t('reservations.valuesBody') }}
</p>
</div>
</section>
<PageSection width="narrow">
<h2 class="font-display text-2xl uppercase tracking-wider md:text-3xl">
{{ t('reservations.valuesTitle') }}
</h2>
<p class="mt-5 max-w-prose text-base leading-relaxed">
{{ t('reservations.valuesBody') }}
</p>
</PageSection>
<!-- Booking form placeholder + contact card -->
<section class="mx-auto max-w-7xl px-4 py-16 lg:px-6">
<PageSection>
<div class="grid gap-10 lg:grid-cols-5 lg:gap-14">
<div class="lg:col-span-3">
<h2 class="font-serif text-3xl font-semibold tracking-tight">
<h2 class="font-display text-2xl uppercase tracking-wider md:text-3xl">
{{ t('reservations.formTitle') }}
</h2>
<p class="mt-3 max-w-prose text-muted-foreground">
<p class="mt-3 max-w-prose text-foreground/80">
{{ t('reservations.formSubtitle') }}
</p>
<div
class="mt-6 flex aspect-[4/3] items-center justify-center rounded-lg border border-dashed border-border bg-secondary/30 p-6 text-center text-muted-foreground"
>
<p class="font-serif italic">{{ t('reservations.formPlaceholder') }}</p>
<p class="italic">{{ t('reservations.formPlaceholder') }}</p>
</div>
<div class="mt-6 flex flex-wrap items-center gap-3">
<Button as-child>
@ -85,7 +78,7 @@ const kinds = ['weekend', 'retreat', 'gathering', 'residency'] as const
</div>
<Card class="p-6 lg:col-span-2">
<h3 class="font-serif text-xl font-semibold">
<h3 class="font-display text-lg uppercase tracking-wider">
{{ t('reservations.contactCard.title') }}
</h3>
<dl class="mt-4 space-y-4 text-sm">
@ -117,6 +110,6 @@ const kinds = ['weekend', 'retreat', 'gathering', 'residency'] as const
</dl>
</Card>
</div>
</section>
</div>
</PageSection>
</PageShell>
</template>

View file

@ -3,6 +3,9 @@ import { useI18n } from 'vue-i18n'
import { RouterLink } from 'vue-router'
import { Button } from '@/components/ui/button'
import { Card } from '@/components/ui/card'
import PageHero from '@/components/layout/PageHero.vue'
import PageSection from '@/components/layout/PageSection.vue'
import PageShell from '@/components/layout/PageShell.vue'
const { t } = useI18n()
@ -11,101 +14,88 @@ const applySteps = ['stepOne', 'stepTwo', 'stepThree'] as const
</script>
<template>
<div>
<PageShell>
<!-- Hero -->
<section class="border-b border-border bg-secondary/40">
<div class="mx-auto grid max-w-7xl gap-10 px-4 py-16 lg:grid-cols-2 lg:gap-14 lg:px-6 lg:py-20">
<div>
<p class="text-xs uppercase tracking-[0.2em] text-accent">
{{ t('symposium.hero.kicker') }}
</p>
<h1
class="mt-3 font-serif text-5xl font-semibold leading-tight tracking-tight md:text-6xl"
>
{{ t('symposium.hero.title') }}
</h1>
<p class="mt-6 max-w-prose text-lg leading-relaxed text-foreground/90">
{{ t('symposium.hero.lede') }}
</p>
<p class="mt-6 inline-block rounded-md bg-card px-3 py-2 font-serif text-base">
{{ t('symposium.hero.dates') }}
</p>
</div>
<PageHero
:kicker="t('symposium.hero.kicker')"
:title="t('symposium.hero.title')"
:lede="t('symposium.hero.lede')"
>
<p class="mt-6 inline-block rounded-md bg-card px-3 py-2 text-base">
{{ t('symposium.hero.dates') }}
</p>
<template #aside>
<img
src="https://static.wixstatic.com/media/f1cbb8_72455a57197944e2b3cd2d895d459ba0~mv2.jpg"
alt=""
class="aspect-[4/3] w-full rounded-lg object-cover"
loading="lazy"
/>
</div>
</section>
</template>
</PageHero>
<!-- Overview -->
<section class="mx-auto max-w-4xl px-4 py-16 lg:px-6">
<h2 class="font-serif text-3xl font-semibold tracking-tight">
<PageSection width="narrow">
<h2 class="font-display text-2xl uppercase tracking-wider md:text-3xl">
{{ t('symposium.overview.title') }}
</h2>
<p class="mt-5 max-w-prose text-base leading-relaxed">
{{ t('symposium.overview.body') }}
</p>
</section>
</PageSection>
<!-- What's included -->
<section class="border-y border-border bg-card">
<div class="mx-auto max-w-7xl px-4 py-16 lg:px-6">
<h2 class="font-serif text-3xl font-semibold tracking-tight">
{{ t('symposium.includedTitle') }}
</h2>
<div class="mt-8 grid gap-6 sm:grid-cols-2 lg:grid-cols-4">
<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>
</Card>
</div>
<PageSection>
<h2 class="font-display text-2xl uppercase tracking-wider md:text-3xl">
{{ t('symposium.includedTitle') }}
</h2>
<div class="mt-8 grid gap-6 sm:grid-cols-2 lg:grid-cols-4">
<Card v-for="key in included" :key="key" class="bg-background p-6">
<h3 class="font-display text-lg uppercase tracking-wider">
{{ t(`symposium.included.${key}Title`) }}
</h3>
<p class="mt-3 text-sm leading-relaxed text-foreground/85">
{{ t(`symposium.included.${key}Body`) }}
</p>
</Card>
</div>
</section>
</PageSection>
<!-- Eligibility -->
<section class="mx-auto max-w-4xl px-4 py-16 lg:px-6">
<h2 class="font-serif text-3xl font-semibold tracking-tight">
<PageSection width="narrow">
<h2 class="font-display text-2xl uppercase tracking-wider md:text-3xl">
{{ t('symposium.eligibility.title') }}
</h2>
<p class="mt-5 max-w-prose text-base leading-relaxed">
{{ t('symposium.eligibility.body') }}
</p>
</section>
</PageSection>
<!-- How to apply -->
<section class="border-t border-border bg-secondary/30">
<div class="mx-auto max-w-7xl px-4 py-16 lg:px-6">
<h2 class="font-serif text-3xl font-semibold tracking-tight">
{{ t('symposium.applyTitle') }}
</h2>
<ol class="mt-8 grid gap-6 md:grid-cols-3">
<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">
<Button as-child>
<RouterLink to="/opportunities">{{ t('symposium.ctaApply') }}</RouterLink>
</Button>
<Button as-child variant="outline">
<a href="mailto:chateaudufaune@ariege.io">{{ t('symposium.ctaContact') }}</a>
</Button>
</div>
<PageSection>
<h2 class="font-display text-2xl uppercase tracking-wider md:text-3xl">
{{ t('symposium.applyTitle') }}
</h2>
<ol class="mt-8 grid gap-6 md:grid-cols-3">
<li v-for="step in applySteps" :key="step">
<Card class="p-6">
<h3 class="font-display text-lg uppercase tracking-wider">
{{ 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">
<Button as-child>
<RouterLink to="/opportunities">{{ t('symposium.ctaApply') }}</RouterLink>
</Button>
<Button as-child variant="outline">
<a href="mailto:chateaudufaune@ariege.io">{{ t('symposium.ctaContact') }}</a>
</Button>
</div>
</section>
</div>
</PageSection>
</PageShell>
</template>

View file

@ -1,6 +1,9 @@
<script setup lang="ts">
import { useI18n } from 'vue-i18n'
import { Card } from '@/components/ui/card'
import PageHero from '@/components/layout/PageHero.vue'
import PageSection from '@/components/layout/PageSection.vue'
import PageShell from '@/components/layout/PageShell.vue'
const { t } = useI18n()
@ -20,91 +23,79 @@ const team = ['patrick', 'coco', 'charlie'] as const
</script>
<template>
<div>
<PageShell>
<!-- Hero -->
<section class="border-b border-border bg-secondary/40">
<div class="mx-auto max-w-4xl px-4 py-16 lg:px-6 lg:py-24">
<p class="text-xs uppercase tracking-[0.2em] text-accent">
{{ t('vision.hero.kicker') }}
</p>
<h1 class="mt-3 font-serif text-5xl font-semibold leading-tight tracking-tight md:text-6xl">
{{ t('vision.hero.title') }}
</h1>
<p class="mt-6 max-w-prose text-lg leading-relaxed text-foreground/90">
{{ t('vision.hero.lede') }}
</p>
</div>
</section>
<PageHero
:kicker="t('vision.hero.kicker')"
:title="t('vision.hero.title')"
:lede="t('vision.hero.lede')"
/>
<!-- Mission -->
<section class="mx-auto max-w-4xl px-4 py-16 lg:px-6">
<h2 class="font-serif text-3xl font-semibold tracking-tight">
<PageSection width="narrow">
<h2 class="font-display text-2xl uppercase tracking-wider md:text-3xl">
{{ t('vision.mission.title') }}
</h2>
<p class="mt-5 max-w-prose text-base leading-relaxed">
{{ t('vision.mission.body') }}
</p>
</section>
</PageSection>
<!-- Philosophy -->
<section class="border-y border-border bg-card">
<div class="mx-auto max-w-7xl px-4 py-16 lg:px-6">
<div class="max-w-3xl">
<h2 class="font-serif text-3xl font-semibold tracking-tight">
{{ t('vision.philosophyTitle') }}
</h2>
<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">
<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`) }}
</h3>
<p class="mt-2 text-sm leading-relaxed text-foreground/85">
{{ t(`vision.philosophy.${p}Body`) }}
</p>
</Card>
</div>
<PageSection>
<div class="max-w-3xl">
<h2 class="font-display text-2xl uppercase tracking-wider md:text-3xl">
{{ t('vision.philosophyTitle') }}
</h2>
<p class="mt-3 text-foreground/80">{{ t('vision.philosophySubtitle') }}</p>
</div>
</section>
<div class="mt-8 grid gap-6 sm:grid-cols-2 lg:grid-cols-5">
<Card v-for="(p, i) in philosophy" :key="p" class="bg-background p-5">
<div class="font-display text-2xl text-accent">{{ i + 1 }}</div>
<h3 class="mt-2 font-display text-lg uppercase tracking-wider">
{{ t(`vision.philosophy.${p}Title`) }}
</h3>
<p class="mt-2 text-sm leading-relaxed text-foreground/85">
{{ t(`vision.philosophy.${p}Body`) }}
</p>
</Card>
</div>
</PageSection>
<!-- Operational pillars -->
<section class="mx-auto max-w-7xl px-4 py-16 lg:px-6">
<PageSection>
<div class="max-w-3xl">
<h2 class="font-serif text-3xl font-semibold tracking-tight">
<h2 class="font-display text-2xl uppercase tracking-wider md:text-3xl">
{{ t('vision.pillarsTitle') }}
</h2>
<p class="mt-3 text-muted-foreground">{{ t('vision.pillarsSubtitle') }}</p>
<p class="mt-3 text-foreground/80">{{ t('vision.pillarsSubtitle') }}</p>
</div>
<div class="mt-8 grid gap-6 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
<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>
<h3 class="font-display text-lg uppercase tracking-wider">{{ t(`vision.pillars.${p}Title`) }}</h3>
<p class="mt-3 text-sm leading-relaxed text-foreground/85">
{{ t(`vision.pillars.${p}Body`) }}
</p>
</Card>
</div>
</section>
</PageSection>
<!-- Team -->
<section class="border-t border-border bg-secondary/30">
<div class="mx-auto max-w-7xl px-4 py-16 lg:px-6">
<h2 class="font-serif text-3xl font-semibold tracking-tight">
{{ t('vision.teamTitle') }}
</h2>
<div class="mt-8 grid gap-6 md:grid-cols-3">
<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`) }}
</p>
<p class="mt-3 text-sm leading-relaxed text-foreground/85">
{{ t(`vision.team.${m}Body`) }}
</p>
</Card>
</div>
<PageSection>
<h2 class="font-display text-2xl uppercase tracking-wider md:text-3xl">
{{ t('vision.teamTitle') }}
</h2>
<div class="mt-8 grid gap-6 md:grid-cols-3">
<Card v-for="m in team" :key="m" class="p-6">
<h3 class="font-display text-lg uppercase tracking-wider">{{ t(`vision.team.${m}Name`) }}</h3>
<p class="mt-1 text-xs uppercase tracking-wider text-accent">
{{ t(`vision.team.${m}Role`) }}
</p>
<p class="mt-3 text-sm leading-relaxed text-foreground/85">
{{ t(`vision.team.${m}Body`) }}
</p>
</Card>
</div>
</section>
</div>
</PageSection>
</PageShell>
</template>