feat: sign interlude, window plate and a two-button control bay

The sign closes in over the road on a gantry truss, arcs into a wider
window and hands off to a flat 2D plate that mirrors its face; content
steps aside for the interlude and rises after. The bay keeps sound and
settings; volume and Pause motion move into the settings popover.
This commit is contained in:
2026-10-10 23:30:58 +03:00
parent 5757dd968f
commit bacc035c43
12 changed files with 823 additions and 287 deletions

View File

@@ -1,7 +1,7 @@
<script setup lang="ts">
import { computed, onMounted, onUnmounted, shallowRef } from 'vue';
import { computed } from 'vue';
import type { JourneySnapshot } from '@/journey/types';
import { bucharestClock } from '@/lib/operations';
import { SIGN_ACCENTS } from '@/journey/sign-routes';
const props = defineProps<{
journey: JourneySnapshot;
@@ -13,24 +13,27 @@ const props = defineProps<{
/** Decorative next-exit line (CSS generated content keeps SSR text stable). */
const next = computed(() =>
props.nextLabel === undefined ? 'END OF ROUTE' : `NEXT ${props.nextExit ?? ''} \u2192 ${props.nextLabel}`.toUpperCase(),
props.nextLabel === undefined ? 'END OF ROUTE' : `NEXT ${props.nextExit ?? ''} → ${props.nextLabel}`.toUpperCase(),
);
const routePercent = computed(() => `${Math.round(Math.min(1, Math.max(0, props.journey.routeProgress)) * 100)}%`);
// The dash clock is decorative and CSS-generated, so SSR and hydrated text stay identical.
const clock = shallowRef<string | undefined>(undefined);
let timer = 0;
onMounted(() => {
const tick = (): void => { clock.value = bucharestClock(new Date()); };
tick();
timer = window.setInterval(tick, 30_000);
});
onUnmounted(() => window.clearInterval(timer));
/* The window's flat exit plate: exit tab, chapter, a next-exit chevron, and
a route rule that fills along its lower edge. */
const route = computed(() => Math.min(1, Math.max(0, props.journey.routeProgress)));
const routePercent = computed(() => `${Math.round(route.value * 100)}%`);
</script>
<template>
<aside class="journey-hud" aria-hidden="true" :data-exit="exit" :data-clock="clock">
<aside
class="journey-hud"
aria-hidden="true"
:data-exit="exit"
:data-arrived="journey.arrived"
:style="{
'--route': route,
'--sign-accent': SIGN_ACCENTS[journey.activeSectionId],
'--label-chars': sectionLabel.length,
}"
>
<span class="journey-hud__route" :data-next="next"><span>ROUTE</span> <strong>{{ routePercent }}</strong></span>
<span class="journey-hud__section">{{ sectionLabel }}</span>
<strong v-if="journey.arrived" class="journey-hud__arrival">ARRIVED</strong>