refactor(live-atc): extract setup steps to components, apply navy/radar skin

Moves the four non-monitor screens out of live-atc.vue into
FlightSourceStep, FlightSelectStep, ScenarioPickerStep and
SessionCompleteStep, and replaces the bg-white/5 glassmorphism cards with
the navy/glow treatment from the design doc.

The card skin lives in one shared SetupCard so the four steps cannot
drift apart and the light-theme overrides stay in one place; it renders
as a real <button> when interactive, which the flight-plan list needs.
RadarBackdrop is mounted once around the setup container rather than per
screen. The scenario picker keeps its phase-chain structure verbatim —
only the card treatment changed — and its .pm-flow styles move with it.

Instrument readouts (callsign, route, flight level, squawk) now render in
font-mono per the design doc's convention.

Verified all four screens render in light and dark on a throwaway route,
since the real screens sit behind an auth guard and MongoDB is not
running locally.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
itsrubberduck
2026-07-10 15:58:01 +02:00
parent 1d6490d4d9
commit 8159bff0a5
6 changed files with 486 additions and 280 deletions

View File

@@ -0,0 +1,69 @@
<script setup lang="ts">
import SetupCard from '~/components/live-atc/SetupCard.vue'
defineProps<{
flightPlans: any[]
loading: boolean
vatsimId: string
}>()
defineEmits<{
(e: 'select-plan', plan: any): void
(e: 'back'): void
}>()
</script>
<template>
<section class="space-y-6">
<div class="flex items-center justify-between">
<v-btn icon class="text-cyan-300" @click="$emit('back')">
<v-icon>mdi-arrow-left</v-icon>
</v-btn>
<h2 class="text-lg font-semibold">Available flight plans</h2>
<v-chip color="cyan" variant="outlined" size="small">{{ vatsimId }}</v-chip>
</div>
<div v-if="loading" class="text-center py-8">
<v-progress-circular indeterminate color="cyan" class="mb-4" />
<p class="text-sm text-[var(--t3)]">Loading flight plans from VATSIM</p>
</div>
<div v-else-if="flightPlans.length === 0" class="text-center py-8">
<v-icon size="48" class="text-[var(--t4)] mb-4">mdi-airplane-off</v-icon>
<p class="text-[var(--t3)]">No flight plans found</p>
<v-btn color="cyan" variant="outlined" class="mt-4" @click="$emit('back')">
Back
</v-btn>
</div>
<div v-else class="space-y-3">
<SetupCard
v-for="plan in flightPlans"
:key="plan.id"
interactive
@click="$emit('select-plan', plan)"
>
<div class="space-y-2">
<div class="flex items-baseline justify-between">
<h3 class="text-xl font-semibold tracking-tight font-mono">{{ plan.callsign }}</h3>
<span class="text-xs uppercase text-[var(--t4)]">{{ plan.aircraft?.split('/')[0] }}</span>
</div>
<div class="flex flex-col gap-1 text-sm text-[var(--t3)]">
<div class="flex items-center gap-2">
<v-icon icon="mdi-map-marker" size="16" class="text-cyan-300" />
<span class="font-mono">{{ plan.dep }} {{ plan.arr }}</span>
</div>
<div v-if="plan.altitude" class="flex items-center gap-2">
<v-icon icon="mdi-airplane-takeoff" size="16" class="text-cyan-300" />
<span class="font-mono">FL{{ Math.floor(parseInt(plan.altitude) / 100) }}</span>
</div>
<div v-if="plan.assignedsquawk" class="flex items-start gap-2">
<v-icon icon="mdi-radar" size="16" class="text-cyan-300" />
<span class="text-xs text-[var(--t3)]">Squawk: <span class="font-mono">{{ plan.assignedsquawk }}</span></span>
</div>
</div>
</div>
</SetupCard>
</div>
</section>
</template>

View File

@@ -0,0 +1,74 @@
<script setup lang="ts">
import SetupCard from '~/components/live-atc/SetupCard.vue'
defineProps<{
loading: boolean
error: string
}>()
const vatsimId = defineModel<string>('vatsimId', { required: true })
defineEmits<{
(e: 'load-flight-plans'): void
(e: 'start-demo'): void
}>()
</script>
<template>
<section class="space-y-6">
<SetupCard>
<div class="space-y-4">
<div>
<h2 class="text-lg font-semibold">VATSIM Integration</h2>
<p class="text-sm text-[var(--t3)]">Enter your CID to load your filtered flight plans.</p>
</div>
<v-text-field
v-model="vatsimId"
label="VATSIM CID"
variant="outlined"
density="comfortable"
color="cyan"
prepend-inner-icon="mdi-account-circle"
hide-details
inputmode="numeric"
/>
<v-btn
block
color="cyan"
variant="flat"
:loading="loading"
@click="$emit('load-flight-plans')"
>
Load flight plans
</v-btn>
<v-alert
v-if="error"
type="warning"
density="compact"
variant="tonal"
class="bg-amber-500/10 text-amber-200"
>
{{ error }}
</v-alert>
</div>
</SetupCard>
<!-- Demo Mode -->
<SetupCard>
<div class="space-y-4">
<div>
<h2 class="text-lg font-semibold">Demo Mode</h2>
<p class="text-sm text-[var(--t3)]">Start with a sample flight plan for testing.</p>
</div>
<v-btn
block
color="orange"
variant="outlined"
@click="$emit('start-demo')"
>
Start demo (DLH39A EDDFEDDM)
</v-btn>
</div>
</SetupCard>
</section>
</template>

View File

@@ -0,0 +1,150 @@
<script setup lang="ts">
import SetupCard from '~/components/live-atc/SetupCard.vue'
import type { Scenario } from '../../../shared/constants/scenarios'
type ChainGroup = {
category: string
chains: Array<{ id: string; complete: Scenario; segments: Scenario[] }>
}
defineProps<{
chainGroups: ChainGroup[]
drillScenarios: Scenario[]
selectedPlan: any
error: string
}>()
defineEmits<{
(e: 'launch', scenario: Scenario): void
(e: 'back'): void
(e: 'dismiss-error'): void
}>()
</script>
<template>
<section class="space-y-6">
<!-- Header -->
<div class="flex items-center gap-3">
<v-btn icon variant="text" class="text-cyan-300" @click="$emit('back')">
<v-icon>mdi-arrow-left</v-icon>
</v-btn>
<div>
<h2 class="text-lg font-semibold">Choose your scenario</h2>
<p v-if="selectedPlan" class="text-sm text-[var(--t4)] font-mono">
{{ selectedPlan.callsign }} · {{ selectedPlan.dep }} {{ selectedPlan.arr }}
</p>
</div>
</div>
<v-alert
v-if="error"
type="warning"
density="compact"
variant="tonal"
closable
class="bg-amber-500/10 text-amber-200"
@click:close="$emit('dismiss-error')"
>
{{ error }}
</v-alert>
<!-- Scenarios grouped by journey, each chain shown as a flow -->
<div v-for="grp in chainGroups" :key="grp.category" class="space-y-3">
<p class="text-[11px] font-semibold uppercase tracking-widest text-[var(--t4)]">
{{ grp.category }}
</p>
<SetupCard v-for="chain in grp.chains" :key="chain.id">
<div class="space-y-3">
<!-- Chain header + run-the-whole-thing button -->
<div class="flex items-center justify-between gap-3">
<div class="flex items-center gap-2 min-w-0">
<v-icon :icon="chain.complete.icon" class="text-cyan-300 shrink-0" size="24" />
<div class="min-w-0">
<div class="font-semibold text-sm leading-tight">{{ chain.complete.name }}</div>
<div class="text-[11px] text-[var(--t4)] leading-snug truncate">{{ chain.complete.subtitle }}</div>
</div>
</div>
<v-btn size="small" color="cyan" variant="flat" class="shrink-0" @click="$emit('launch', chain.complete)">
Fly full
</v-btn>
</div>
<!-- Phase flow: tap a step to practise just that phase -->
<div class="pm-flow">
<template v-for="(seg, i) in chain.segments" :key="seg.id">
<button
type="button"
class="pm-flow-node"
:title="seg.subtitle"
@click="$emit('launch', seg)"
>
<v-icon :icon="seg.icon" size="13" />
<span>{{ seg.name }}</span>
</button>
<v-icon v-if="i < chain.segments.length - 1" size="13" class="pm-flow-arrow">
mdi-arrow-right
</v-icon>
</template>
</div>
</div>
</SetupCard>
</div>
<!-- Standalone drills -->
<div v-if="drillScenarios.length" class="space-y-3">
<p class="text-[11px] font-semibold uppercase tracking-widest text-[var(--t4)]">
Drills
</p>
<SetupCard v-for="drill in drillScenarios" :key="drill.id">
<div class="flex items-center justify-between gap-3">
<div class="flex items-center gap-2 min-w-0">
<v-icon :icon="drill.icon" class="text-cyan-300 shrink-0" size="24" />
<div class="min-w-0">
<div class="font-semibold text-sm leading-tight">{{ drill.name }}</div>
<div class="text-[11px] text-[var(--t4)] leading-snug truncate">{{ drill.subtitle }}</div>
</div>
</div>
<v-btn size="small" color="cyan" variant="flat" class="shrink-0" @click="$emit('launch', drill)">
Start
</v-btn>
</div>
</SetupCard>
</div>
<p class="text-[10px] text-[var(--t4)] text-center">
Tap a step to practise just that phase · Fly full runs the whole chain.
</p>
</section>
</template>
<style scoped>
/* Scenario chooser — phase flow with arrows */
.pm-flow {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 6px;
}
.pm-flow-node {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 4px 9px;
border-radius: 8px;
background: var(--pm-surface-1);
border: 1px solid var(--border);
font-size: 11.5px;
line-height: 1.2;
color: var(--t2);
transition: border-color .15s, background .15s, color .15s;
}
.pm-flow-node:hover {
border-color: rgba(34, 211, 238, 0.6);
background: rgba(34, 211, 238, 0.1);
color: var(--text);
}
.pm-flow-arrow {
color: var(--t4);
}
</style>

View File

@@ -0,0 +1,52 @@
<script setup lang="ts">
import type { Scenario } from '../../../shared/constants/scenarios'
defineProps<{
completedScenario: Scenario | null
selectedPlan: any
oppositeScenario: Scenario | null
}>()
defineEmits<{
(e: 'fly-again'): void
(e: 'try-opposite', scenario: Scenario): void
(e: 'back-to-scenarios'): void
}>()
</script>
<template>
<section class="space-y-6">
<div class="text-center space-y-3 pt-10 pb-6">
<v-icon size="72" class="text-green-400">mdi-check-circle-outline</v-icon>
<h2 class="text-2xl font-semibold">
{{ completedScenario?.name ?? 'Session' }} complete
</h2>
<p class="text-[var(--t4)] text-sm font-mono">
{{ selectedPlan?.callsign }} ·
{{ selectedPlan?.dep }} {{ selectedPlan?.arr }}
</p>
</div>
<div class="space-y-3">
<v-btn block color="cyan" variant="flat" @click="$emit('fly-again')">
<v-icon start>mdi-refresh</v-icon>
Fly again
</v-btn>
<v-btn
v-if="oppositeScenario"
block
color="white"
variant="outlined"
@click="$emit('try-opposite', oppositeScenario)"
>
<v-icon start>{{ oppositeScenario.icon }}</v-icon>
Try {{ oppositeScenario.name }}
</v-btn>
<v-btn block variant="text" class="text-[var(--t3)]" @click="$emit('back-to-scenarios')">
Back to scenarios
</v-btn>
</div>
</section>
</template>

View File

@@ -0,0 +1,90 @@
<script setup lang="ts">
/**
* The navy/glow panel the /live-atc setup screens are built from — replaces the
* old `bg-white/5 border-white/10` glassmorphism cards. Shared so the four step
* components can't drift apart, and so the light-theme overrides live in one place.
*/
withDefaults(defineProps<{
/** Renders as a <button> with hover/focus affordance instead of a plain panel. */
interactive?: boolean
}>(), {
interactive: false,
})
</script>
<template>
<component
:is="interactive ? 'button' : 'div'"
:type="interactive ? 'button' : undefined"
class="setup-card"
:class="{ 'setup-card--interactive': interactive }"
>
<div class="setup-card__body">
<slot />
</div>
</component>
</template>
<style scoped>
.setup-card {
position: relative;
display: block;
width: 100%;
border-radius: 16px;
border: 1px solid var(--border);
background:
linear-gradient(160deg,
color-mix(in srgb, var(--bg2) 88%, transparent),
color-mix(in srgb, var(--bg) 94%, transparent));
box-shadow:
inset 0 1px 0 color-mix(in srgb, var(--text) 6%, transparent),
0 18px 40px -24px rgba(0, 0, 0, 0.85);
overflow: hidden;
text-align: left;
}
/* Hairline accent along the top edge — the one cyan cue on an otherwise dark panel. */
.setup-card::before {
content: '';
position: absolute;
inset: 0 0 auto 0;
height: 1px;
background: linear-gradient(
90deg,
transparent,
color-mix(in srgb, var(--accent) 55%, transparent),
transparent
);
opacity: 0.7;
}
.setup-card__body {
position: relative;
padding: 1rem;
}
.setup-card--interactive {
cursor: pointer;
transition: border-color 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease;
}
.setup-card--interactive:hover,
.setup-card--interactive:focus-visible {
border-color: color-mix(in srgb, var(--accent) 55%, transparent);
box-shadow:
inset 0 1px 0 color-mix(in srgb, var(--text) 8%, transparent),
0 0 0 1px color-mix(in srgb, var(--accent) 22%, transparent),
0 20px 44px -22px color-mix(in srgb, var(--accent) 35%, transparent);
}
.setup-card--interactive:focus-visible {
outline: 2px solid color-mix(in srgb, var(--accent) 70%, transparent);
outline-offset: 2px;
}
@media (prefers-reduced-motion: reduce) {
.setup-card--interactive {
transition: none;
}
}
</style>

View File

@@ -11,263 +11,48 @@
<p class="text-sm text-white/80">{{ sessionStartingMessage }}</p>
</div>
<div v-if="currentScreen !== 'monitor'" class="mx-auto w-full max-w-[420px] px-4 pb-24 pt-6 sm:px-6">
<!-- Login/Flight Selection Screen -->
<section v-if="currentScreen === 'login'" class="space-y-6">
<v-card class="bg-white/5 backdrop-blur border border-white/10">
<v-card-text class="space-y-4">
<div>
<h2 class="text-lg font-semibold">VATSIM Integration</h2>
<p class="text-sm text-white/70">Enter your CID to load your filtered flight plans.</p>
</div>
<v-text-field
v-model="vatsimId"
label="VATSIM CID"
variant="outlined"
density="comfortable"
color="cyan"
prepend-inner-icon="mdi-account-circle"
hide-details
inputmode="numeric"
/>
<v-btn
block
color="cyan"
variant="flat"
:loading="loading"
@click="loadFlightPlans"
>
Load flight plans
</v-btn>
<v-alert
v-if="error"
type="warning"
density="compact"
variant="tonal"
class="bg-amber-500/10 text-amber-200"
>
{{ error }}
</v-alert>
</v-card-text>
</v-card>
<div v-if="currentScreen !== 'monitor'" class="pm-setup">
<RadarBackdrop />
<div class="pm-setup__inner mx-auto w-full max-w-[420px] px-4 pb-24 pt-6 sm:px-6">
<FlightSourceStep
v-if="currentScreen === 'login'"
v-model:vatsim-id="vatsimId"
:loading="loading"
:error="error"
@load-flight-plans="loadFlightPlans"
@start-demo="startDemoFlight"
/>
<!-- Demo Mode -->
<v-card class="bg-white/5 backdrop-blur border border-white/10">
<v-card-text class="space-y-4">
<div>
<h2 class="text-lg font-semibold">Demo Mode</h2>
<p class="text-sm text-white/70">Start with a sample flight plan for testing.</p>
</div>
<v-btn
block
color="orange"
variant="outlined"
@click="startDemoFlight"
>
Start demo (DLH39A EDDFEDDM)
</v-btn>
</v-card-text>
</v-card>
</section>
<FlightSelectStep
v-else-if="currentScreen === 'flightselect'"
:flight-plans="flightPlans"
:loading="loading"
:vatsim-id="vatsimId"
@select-plan="plan => { selectedPlan = plan; currentScreen = 'scenario' }"
@back="currentScreen = 'login'"
/>
<!-- Flight Selection Screen -->
<section v-else-if="currentScreen === 'flightselect'" class="space-y-6">
<div class="flex items-center justify-between">
<v-btn icon @click="currentScreen = 'login'" class="text-cyan-300">
<v-icon>mdi-arrow-left</v-icon>
</v-btn>
<h2 class="text-lg font-semibold">Available flight plans</h2>
<v-chip color="cyan" variant="outlined" size="small">{{ vatsimId }}</v-chip>
</div>
<div v-if="loading" class="text-center py-8">
<v-progress-circular indeterminate color="cyan" class="mb-4" />
<p class="text-sm text-white/70">Loading flight plans from VATSIM</p>
</div>
<div v-else-if="flightPlans.length === 0" class="text-center py-8">
<v-icon size="48" class="text-white/30 mb-4">mdi-airplane-off</v-icon>
<p class="text-white/70">No flight plans found</p>
<v-btn color="cyan" variant="outlined" class="mt-4" @click="currentScreen = 'login'">
Back
</v-btn>
</div>
<div v-else class="space-y-3">
<v-card
v-for="plan in flightPlans"
:key="plan.id"
class="bg-white/5 border border-white/10 backdrop-blur transition hover:border-cyan-400/60 cursor-pointer"
@click="selectedPlan = plan; currentScreen = 'scenario'"
>
<v-card-text class="space-y-2">
<div class="flex items-baseline justify-between">
<h3 class="text-xl font-semibold tracking-tight">{{ plan.callsign }}</h3>
<span class="text-xs uppercase text-white/50">{{ plan.aircraft?.split('/')[0] }}</span>
</div>
<div class="flex flex-col gap-1 text-sm text-white/70">
<div class="flex items-center gap-2">
<v-icon icon="mdi-map-marker" size="16" class="text-cyan-300" />
<span>{{ plan.dep }} {{ plan.arr }}</span>
</div>
<div v-if="plan.altitude" class="flex items-center gap-2">
<v-icon icon="mdi-airplane-takeoff" size="16" class="text-cyan-300" />
<span>FL{{ Math.floor(parseInt(plan.altitude) / 100) }}</span>
</div>
<div v-if="plan.assignedsquawk" class="flex items-start gap-2">
<v-icon icon="mdi-radar" size="16" class="text-cyan-300" />
<span class="text-xs text-white/60">Squawk: {{ plan.assignedsquawk }}</span>
</div>
</div>
</v-card-text>
</v-card>
</div>
</section>
<!-- Scenario Picker -->
<section v-else-if="currentScreen === 'scenario'" class="space-y-6">
<!-- Header -->
<div class="flex items-center gap-3">
<v-btn icon variant="text" @click="currentScreen = flightPlans.length > 0 ? 'flightselect' : 'login'" class="text-cyan-300">
<v-icon>mdi-arrow-left</v-icon>
</v-btn>
<div>
<h2 class="text-lg font-semibold">Choose your scenario</h2>
<p v-if="selectedPlan" class="text-sm text-white/50">
{{ selectedPlan.callsign }} · {{ selectedPlan.dep }} {{ selectedPlan.arr }}
</p>
</div>
</div>
<v-alert
v-if="error"
type="warning"
density="compact"
variant="tonal"
closable
class="bg-amber-500/10 text-amber-200"
@click:close="error = ''"
>
{{ error }}
</v-alert>
<!-- Scenarios grouped by journey, each chain shown as a flow -->
<div v-for="grp in CHAIN_GROUPS" :key="grp.category" class="space-y-3">
<p class="text-[11px] font-semibold uppercase tracking-widest text-white/35">
{{ grp.category }}
</p>
<v-card
v-for="chain in grp.chains"
:key="chain.id"
class="bg-white/5 border border-white/10 backdrop-blur"
rounded="lg"
>
<v-card-text class="p-4 space-y-3">
<!-- Chain header + run-the-whole-thing button -->
<div class="flex items-center justify-between gap-3">
<div class="flex items-center gap-2 min-w-0">
<v-icon :icon="chain.complete.icon" class="text-cyan-300 shrink-0" size="24" />
<div class="min-w-0">
<div class="font-semibold text-sm leading-tight">{{ chain.complete.name }}</div>
<div class="text-[11px] text-white/45 leading-snug truncate">{{ chain.complete.subtitle }}</div>
</div>
</div>
<v-btn size="small" color="cyan" variant="flat" class="shrink-0" @click="launchScenario(chain.complete)">
Fly full
</v-btn>
</div>
<!-- Phase flow: tap a step to practise just that phase -->
<div class="pm-flow">
<template v-for="(seg, i) in chain.segments" :key="seg.id">
<button
type="button"
class="pm-flow-node"
:title="seg.subtitle"
@click="launchScenario(seg)"
>
<v-icon :icon="seg.icon" size="13" />
<span>{{ seg.name }}</span>
</button>
<v-icon v-if="i < chain.segments.length - 1" size="13" class="pm-flow-arrow">
mdi-arrow-right
</v-icon>
</template>
</div>
</v-card-text>
</v-card>
</div>
<!-- Standalone drills -->
<div v-if="DRILL_SCENARIOS.length" class="space-y-3">
<p class="text-[11px] font-semibold uppercase tracking-widest text-white/35">
Drills
</p>
<v-card
v-for="drill in DRILL_SCENARIOS"
:key="drill.id"
class="bg-white/5 border border-white/10 backdrop-blur"
rounded="lg"
>
<v-card-text class="p-4">
<div class="flex items-center justify-between gap-3">
<div class="flex items-center gap-2 min-w-0">
<v-icon :icon="drill.icon" class="text-cyan-300 shrink-0" size="24" />
<div class="min-w-0">
<div class="font-semibold text-sm leading-tight">{{ drill.name }}</div>
<div class="text-[11px] text-white/45 leading-snug truncate">{{ drill.subtitle }}</div>
</div>
</div>
<v-btn size="small" color="cyan" variant="flat" class="shrink-0" @click="launchScenario(drill)">
Start
</v-btn>
</div>
</v-card-text>
</v-card>
</div>
<p class="text-[10px] text-white/30 text-center">
Tap a step to practise just that phase · Fly full runs the whole chain.
</p>
</section>
<!-- Completion Screen -->
<section v-else-if="currentScreen === 'complete'" class="space-y-6">
<div class="text-center space-y-3 pt-10 pb-6">
<v-icon size="72" class="text-green-400">mdi-check-circle-outline</v-icon>
<h2 class="text-2xl font-semibold">
{{ completedScenario?.name ?? 'Session' }} complete
</h2>
<p class="text-white/50 text-sm">
{{ selectedPlan?.callsign }} ·
{{ selectedPlan?.dep }} {{ selectedPlan?.arr }}
</p>
</div>
<div class="space-y-3">
<v-btn block color="cyan" variant="flat" @click="flyAgain">
<v-icon start>mdi-refresh</v-icon>
Fly again
</v-btn>
<v-btn
v-if="oppositeScenario"
block
color="white"
variant="outlined"
@click="launchScenario(oppositeScenario!)"
>
<v-icon start>{{ oppositeScenario.icon }}</v-icon>
Try {{ oppositeScenario.name }}
</v-btn>
<v-btn block variant="text" class="text-white/60" @click="currentScreen = 'scenario'">
Back to scenarios
</v-btn>
</div>
</section>
<ScenarioPickerStep
v-else-if="currentScreen === 'scenario'"
:chain-groups="CHAIN_GROUPS"
:drill-scenarios="DRILL_SCENARIOS"
:selected-plan="selectedPlan"
:error="error"
@launch="launchScenario"
@back="currentScreen = flightPlans.length > 0 ? 'flightselect' : 'login'"
@dismiss-error="error = ''"
/>
<SessionCompleteStep
v-else-if="currentScreen === 'complete'"
:completed-scenario="completedScenario"
:selected-plan="selectedPlan"
:opposite-scenario="oppositeScenario"
@fly-again="flyAgain"
@try-opposite="launchScenario"
@back-to-scenarios="currentScreen = 'scenario'"
/>
</div>
</div>
<!-- Main Monitoring Screen (full app shell) -->
@@ -1526,6 +1311,10 @@ import { useBugReport } from '~/composables/useBugReport'
import { useDebugSimulation } from '~/composables/useDebugSimulation'
import { useSimBridgeSync } from '~/composables/useSimBridgeSync'
import { CHAIN_GROUPS, DRILL_SCENARIOS } from '../../shared/constants/scenarios'
import FlightSourceStep from '~/components/live-atc/FlightSourceStep.vue'
import FlightSelectStep from '~/components/live-atc/FlightSelectStep.vue'
import ScenarioPickerStep from '~/components/live-atc/ScenarioPickerStep.vue'
import SessionCompleteStep from '~/components/live-atc/SessionCompleteStep.vue'
import { useLiveAtcSession } from '~/composables/useLiveAtcSession'
import { useSessionState } from '~/composables/useSessionState'
import { useSpeechInterrupt } from '~/composables/useSpeechInterrupt'
@@ -2566,33 +2355,15 @@ onUnmounted(() => {
.pm-readback-expected { color: var(--text); font-weight: 600; }
.pm-readback-forms { color: var(--t4); }
/* Scenario chooser — phase flow with arrows */
.pm-flow {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 6px;
/* Setup screens sit above the shared radar backdrop. */
.pm-setup {
position: relative;
min-height: 100vh;
isolation: isolate;
}
.pm-flow-node {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 4px 9px;
border-radius: 8px;
background: var(--pm-surface-1);
border: 1px solid var(--border);
font-size: 11.5px;
line-height: 1.2;
color: var(--t2);
transition: border-color .15s, background .15s, color .15s;
}
.pm-flow-node:hover {
border-color: rgba(34, 211, 238, 0.6);
background: rgba(34, 211, 238, 0.1);
color: var(--text);
}
.pm-flow-arrow {
color: var(--t4);
.pm-setup__inner {
position: relative;
z-index: 1;
}
.pm-bottomnav {