feat: finish portfolio title, navigation, and destination effects

Add the approved name animation, rounded docking navigation, localized keyboard focus cue, unified builds card, and destination copy hierarchy.

Introduce lightweight one-shot meteors and a sun ring that repeats every three seconds at Contact. Open the email action in a separate browsing context. Preserve reduced-motion behavior and keep private handoff and visual-check artifacts out of version control.

Validation: 649 tests passed; all 20 failures in world-models and SceneCanvas reproduce on baseline 103427f. Typecheck, artifact verification, and transfer budgets passed. Client/SSR preview build passed before this commit. Existing baseline test failures remain unchanged.
This commit is contained in:
2026-09-05 22:16:42 +03:00
parent 103427f95b
commit f46131723b
31 changed files with 948 additions and 51 deletions

View File

@@ -219,6 +219,7 @@ onUnmounted(() => removePageListeners());
<SiteNavigation
:items="canonicalNavigation"
:journey="journey"
:allow-motion="motion.policy.value.allowSceneRequest"
:on-section-link-click="navigation.onSectionLinkClick"
/>
</header>
@@ -226,7 +227,7 @@ onUnmounted(() => removePageListeners());
<div class="reading-stage">
<div class="reading-viewport">
<main id="portfolio-content" class="portfolio-main" tabindex="-1">
<HeroJourney :identity="content.identity" @explore="navigation.onSectionLinkClick" @contact="navigation.onSectionLinkClick" />
<HeroJourney :identity="content.identity" :allow-motion="motion.policy.value.allowSceneRequest && pageLifecycle === 'active'" @explore="navigation.onSectionLinkClick" @contact="navigation.onSectionLinkClick" />
<PortfolioSection id="profile" :layout="sectionLayouts.profile" :heading="sectionLabel('profile')">
<p>{{ content.profile.introduction }}</p>
@@ -247,12 +248,20 @@ onUnmounted(() => removePageListeners());
</PortfolioSection>
<PortfolioSection id="selected-builds" :layout="sectionLayouts['selected-builds']" eyebrow="Selected work" :heading="sectionLabel('selected-builds')">
<template #lead>
<ProjectCaseStudy
v-if="content.projects.selected[0]"
:project="content.projects.selected[0]"
priority="primary"
:allow-automatic-video="motion.policy.value.allowAutomaticVideo"
/>
</template>
<div class="project-stage__projects">
<ProjectCaseStudy
v-for="(project, index) in content.projects.selected"
v-for="(project, index) in content.projects.selected.slice(1)"
:key="project.id"
:project="project"
:priority="index < 2 ? 'primary' : 'supporting'"
:priority="index === 0 ? 'primary' : 'supporting'"
:allow-automatic-video="motion.policy.value.allowAutomaticVideo"
/>
</div>
@@ -281,6 +290,7 @@ onUnmounted(() => removePageListeners());
</main>
</div>
</div>
<span class="content-focus-cue" aria-hidden="true">Portfolio content</span>
<DriveControls
:policy="displayedPolicy"
:maximum-quality="controlsReady ? motion.maximumQuality.value : 'high'"