feat: Add packages/site — landing vitrine (Vue 3 + Tailwind 4)

- Scaffold packages/site with 11 Vue components: hero (animated terminal), features, showcase, work groups, how-it-works, security, FAQ accordion, footer
- i18n EN/FR via vue-i18n with 82+ keys, autodetect navigator.language, toggle without reload
- Animated timeline (terminal, dialog, toast) ported from design, scroll-reveal, copy buttons (2 instances)
- Self-hosted JetBrains Mono fonts (@fontsource), optimized assets (mark 28KB, og-cover 1200×630)
- SEO: canonical, og:image, theme-color, robots.txt, sitemap.xml for git-arboretum.com
- Monorepo integration (decoupled from npm release): build:site / dev:site / preview:site scripts
- CI: add build:site step to ci.yml for early detection; create .gitea/workflows/prod.yml for FTPS deploy (lftp mirror) to git-arboretum.com
- Validated builds ✓ and UI rendering (EN/FR headless test) ✓
This commit is contained in:
2026-06-19 10:02:22 +02:00
parent 8d3a47bd91
commit 3b251c8174
41 changed files with 1923 additions and 1 deletions

View File

@@ -0,0 +1,170 @@
<script setup lang="ts">
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
</script>
<template>
<section id="showcase" class="mx-auto flex max-w-[1200px] scroll-mt-[84px] flex-col gap-[92px] px-6">
<!-- a) dashboard + needs attention -->
<div v-reveal class="flex flex-wrap items-center gap-12">
<div class="min-w-[280px] flex-[1_1_380px]">
<div class="mb-3 font-mono text-xs uppercase tracking-[0.12em] text-emerald-400">{{ t('scAKicker') }}</div>
<h2 class="m-0 mb-4 text-[clamp(26px,3vw,34px)] font-semibold leading-[1.15] tracking-[-0.025em] text-zinc-50">
{{ t('scATitle') }}
</h2>
<p class="m-0 text-[16.5px] leading-[1.6] text-zinc-400">{{ t('scABody') }}</p>
</div>
<div class="min-w-[280px] flex-[1_1_420px]">
<div class="overflow-hidden rounded-xl border border-zinc-800 bg-[#0c0c0e] shadow-card">
<div class="flex items-center gap-[9px] border-b border-[#1c1c1f] bg-[rgba(69,26,3,0.18)] px-[15px] py-3">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="#fcd34d" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z" /><path d="M12 9v4" /><path d="M12 17h.01" /></svg>
<span class="text-sm font-semibold text-zinc-50">{{ t('mAttn') }}</span>
<span class="ml-auto rounded-full bg-amber-300/[0.12] px-[9px] py-0.5 font-mono text-[11px] text-amber-300">2</span>
</div>
<div class="flex flex-col gap-2 p-2.5">
<div class="flex items-center gap-3 rounded-[9px] border border-[rgba(120,53,15,0.55)] bg-[rgba(69,26,3,0.3)] px-[13px] py-[11px]">
<span class="h-2 w-2 flex-none animate-pulse-amber rounded-full bg-amber-300"></span>
<div class="min-w-0 flex-1">
<div class="font-mono text-[13px] text-zinc-200">api · feat/auth</div>
<div class="text-xs text-[#d4a55a]">Approve change to login.ts?</div>
</div>
<button type="button" class="flex-none rounded-[7px] border border-amber-300/30 bg-amber-300/[0.12] px-3 py-[5px] text-xs font-semibold text-amber-300">Answer</button>
</div>
<div class="flex items-center gap-3 rounded-[9px] border border-[rgba(120,53,15,0.55)] bg-[rgba(69,26,3,0.3)] px-[13px] py-[11px]">
<span class="h-2 w-2 flex-none animate-pulse-amber rounded-full bg-amber-300"></span>
<div class="min-w-0 flex-1">
<div class="font-mono text-[13px] text-zinc-200">web · checkout</div>
<div class="text-xs text-[#d4a55a]">Run the failing test again?</div>
</div>
<button type="button" class="flex-none rounded-[7px] border border-amber-300/30 bg-amber-300/[0.12] px-3 py-[5px] text-xs font-semibold text-amber-300">Answer</button>
</div>
<div class="mx-0.5 my-[3px] h-px bg-[#1c1c1f]"></div>
<div class="flex items-center gap-3 rounded-[9px] px-[13px] py-[9px]">
<span class="h-2 w-2 flex-none animate-pulse-sky rounded-full bg-sky-300"></span>
<div class="min-w-0 flex-1 font-mono text-[13px] text-zinc-400">db · refactor</div>
<span class="flex-none font-mono text-[11px] text-sky-300">{{ t('busy') }}</span>
</div>
<div class="flex items-center gap-3 rounded-[9px] px-[13px] py-[9px]">
<span class="h-2 w-2 flex-none rounded-full bg-emerald-400"></span>
<div class="min-w-0 flex-1 font-mono text-[13px] text-zinc-400">docs · main</div>
<span class="flex-none font-mono text-[11px] text-emerald-400">{{ t('idle') }}</span>
</div>
</div>
</div>
</div>
</div>
<!-- b) multi-terminal grid -->
<div v-reveal class="flex flex-wrap-reverse items-center gap-12">
<div class="min-w-[280px] flex-[1_1_420px]">
<div class="grid grid-cols-2 gap-2.5 overflow-hidden rounded-xl border border-zinc-800 bg-[#0c0c0e] p-3 shadow-card">
<div class="overflow-hidden rounded-[9px] border border-zinc-800 bg-zinc-950">
<div class="flex items-center justify-between border-b border-[#1c1c1f] px-[11px] py-2">
<span class="font-mono text-[11px] text-zinc-400">api · feat/auth</span>
<span class="h-1.5 w-1.5 animate-pulse-sky rounded-full bg-sky-300"></span>
</div>
<div class="p-[11px] font-mono text-[11px] leading-[1.75] text-zinc-300">
<div class="text-emerald-400"> Edit session.ts</div>
<div class="text-emerald-400">+ const token = sign(...)</div>
<div class="text-red-400">- legacy cookie auth</div>
<div class="text-zinc-500">refactoring guards</div>
</div>
</div>
<div class="overflow-hidden rounded-[9px] border border-zinc-800 bg-zinc-950">
<div class="flex items-center justify-between border-b border-[#1c1c1f] px-[11px] py-2">
<span class="font-mono text-[11px] text-zinc-400">web · fix/cart</span>
<span class="h-1.5 w-1.5 rounded-full bg-emerald-400"></span>
</div>
<div class="p-[11px] font-mono text-[11px] leading-[1.75] text-zinc-300">
<div class="text-emerald-400"> all tests passing</div>
<div class="text-zinc-500">24 passed, 0 failed</div>
<div>
<span class="text-emerald-400">$</span>
<span class="ml-1 inline-block h-[11px] w-1.5 animate-blink align-middle bg-zinc-200"></span>
</div>
</div>
</div>
<div class="overflow-hidden rounded-[9px] border border-zinc-800 bg-zinc-950">
<div class="flex items-center justify-between border-b border-[#1c1c1f] px-[11px] py-2">
<span class="font-mono text-[11px] text-zinc-400">db · refactor</span>
<span class="h-1.5 w-1.5 animate-pulse-sky rounded-full bg-sky-300"></span>
</div>
<div class="p-[11px] font-mono text-[11px] leading-[1.75] text-zinc-300">
<div class="text-emerald-400"> Write migration</div>
<div class="text-zinc-500">0003_add_index.sql</div>
<div class="text-zinc-500">applying to schema</div>
</div>
</div>
<div class="overflow-hidden rounded-[9px] border border-zinc-800 bg-zinc-950">
<div class="flex items-center justify-between border-b border-[#1c1c1f] px-[11px] py-2">
<span class="font-mono text-[11px] text-zinc-400">docs · api</span>
<span class="h-1.5 w-1.5 animate-pulse-sky rounded-full bg-sky-300"></span>
</div>
<div class="p-[11px] font-mono text-[11px] leading-[1.75] text-zinc-300">
<div class="text-emerald-400"> Read README.md</div>
<div class="text-zinc-500">## Quick start</div>
<div class="text-zinc-500">writing examples</div>
</div>
</div>
</div>
</div>
<div class="min-w-[280px] flex-[1_1_380px]">
<div class="mb-3 font-mono text-xs uppercase tracking-[0.12em] text-emerald-400">{{ t('scBKicker') }}</div>
<h2 class="m-0 mb-4 text-[clamp(26px,3vw,34px)] font-semibold leading-[1.15] tracking-[-0.025em] text-zinc-50">
{{ t('scBTitle') }}
</h2>
<p class="m-0 text-[16.5px] leading-[1.6] text-zinc-400">{{ t('scBBody') }}</p>
</div>
</div>
<!-- c) mobile -->
<div v-reveal class="flex flex-wrap items-center gap-12">
<div class="min-w-[280px] flex-[1_1_380px]">
<div class="mb-3 font-mono text-xs uppercase tracking-[0.12em] text-emerald-400">{{ t('scCKicker') }}</div>
<h2 class="m-0 mb-4 text-[clamp(26px,3vw,34px)] font-semibold leading-[1.15] tracking-[-0.025em] text-zinc-50">
{{ t('scCTitle') }}
</h2>
<p class="m-0 text-[16.5px] leading-[1.6] text-zinc-400">{{ t('scCBody') }}</p>
</div>
<div class="flex min-w-[280px] flex-[1_1_320px] justify-center">
<div class="relative w-[288px] overflow-hidden rounded-[40px] border-[9px] border-zinc-900 bg-zinc-950 shadow-phone">
<div class="flex h-[30px] items-center justify-center">
<div class="h-1.5 w-[90px] rounded-full bg-zinc-800"></div>
</div>
<div class="px-[13px] pb-[22px]">
<div class="flex items-start gap-[11px] rounded-[15px] border border-[rgba(120,53,15,0.5)] bg-[#0c0c0e] p-[13px] shadow-toast-sm">
<span class="inline-flex h-[30px] w-[30px] flex-none items-center justify-center rounded-lg bg-emerald-500/[0.14]">
<img src="/assets/arboretum-mark.png" alt="" width="18" height="18" class="h-[18px] w-[18px] object-contain" />
</span>
<div class="min-w-0 flex-1">
<div class="flex items-center justify-between">
<span class="font-mono text-[11px] text-emerald-400">Arboretum</span>
<span class="text-[10px] text-zinc-600">now</span>
</div>
<div class="mt-[3px] text-[13.5px] font-semibold text-zinc-50">{{ t('toastTitle') }}</div>
<div class="text-xs leading-[1.45] text-zinc-400">{{ t('toastBody') }}</div>
</div>
</div>
<div class="mt-[14px] overflow-hidden rounded-[15px] border border-zinc-800 bg-zinc-950">
<div class="flex items-center gap-2 border-b border-[#1c1c1f] px-[13px] py-[11px]">
<span class="h-[7px] w-[7px] animate-pulse-amber rounded-full bg-amber-300"></span>
<span class="font-mono text-[11px] text-zinc-300">feat/auth</span>
</div>
<div class="p-[13px]">
<div class="text-[13px] leading-[1.5] text-zinc-300">{{ t('dlgQ') }}</div>
<div class="mt-2 rounded-lg border border-[#1c1c1f] bg-[#0c0c0e] p-[9px] font-mono text-[11px] leading-[1.6] text-zinc-500">
<div class="text-emerald-400">+ verifyToken(req)</div>
<div class="text-red-400">- skipAuth()</div>
</div>
<div class="mt-[13px] flex gap-2">
<button type="button" class="flex-1 rounded-lg bg-emerald-600 py-2.5 text-[13px] font-semibold text-white">{{ t('approve') }}</button>
<button type="button" class="flex-1 rounded-lg border border-zinc-800 bg-transparent py-2.5 text-[13px] font-medium text-zinc-300">{{ t('reject') }}</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
</template>