diff --git a/app/pages/pm.vue b/app/pages/pm.vue
index 5f66a49..b8f3864 100644
--- a/app/pages/pm.vue
+++ b/app/pages/pm.vue
@@ -140,48 +140,57 @@
{{ error }}
-
-
-
- Complete scenarios
+
+
+
+ {{ grp.category }}
-
-
-
-
- {{ s.name }}
- {{ s.subtitle }}
-
-
-
+
+
+
+
+
+
+
+
+
{{ chain.complete.name }}
+
{{ chain.complete.subtitle }}
+
+
+
+ Fly full
+
+
+
+
+
+
+
+
+ mdi-arrow-right
+
+
+
+
+
-
-
-
- Practice a single phase
-
-
-
-
-
+
+ Tap a step to practise just that phase · “Fly full” runs the whole chain.
+
@@ -1922,8 +1931,35 @@ const SCENARIOS: Scenario[] = [
},
]
-const completeScenarios = SCENARIOS.filter(s => s.isComplete)
-const individualScenarios = SCENARIOS.filter(s => !s.isComplete)
+// ---------------------------------------------------------------------------
+// Chooser layout: each complete chain is shown as a flow of its phases, so it's
+// clear which single-phase practice maps to which segment of which journey.
+// ---------------------------------------------------------------------------
+interface ChainDef {
+ category: 'Departure' | 'Arrival'
+ completeId: string // the isComplete scenario that flies the whole chain
+ segmentIds: string[] // individual-phase scenario ids, in order
+}
+
+const CHAIN_DEFS: ChainDef[] = [
+ { category: 'Departure', completeId: 'ifr-departure', segmentIds: ['clearance', 'taxi', 'tower', 'departure'] },
+ { category: 'Arrival', completeId: 'ifr-arrival', segmentIds: ['ifr-enroute', 'ifr-approach', 'ifr-landing', 'taxi-in'] },
+ { category: 'Arrival', completeId: 'vfr-arrival', segmentIds: ['vfr-approach', 'circuit-landing', 'taxi-in'] },
+]
+
+const chainGroups = computed(() => {
+ const byCategory = new Map
>()
+ for (const def of CHAIN_DEFS) {
+ const complete = SCENARIOS.find(s => s.id === def.completeId)
+ if (!complete) continue
+ const segments = def.segmentIds
+ .map(id => SCENARIOS.find(s => s.id === id))
+ .filter((s): s is Scenario => !!s)
+ if (!byCategory.has(def.category)) byCategory.set(def.category, [])
+ byCategory.get(def.category)!.push({ id: def.completeId, complete, segments })
+ }
+ return Array.from(byCategory, ([category, chains]) => ({ category, chains }))
+})
/** The scenario the user just finished (used on the completion screen). */
const completedScenario = ref(null)
@@ -4778,6 +4814,35 @@ onUnmounted(() => {
.pm-readback-expected { color: #fff; font-weight: 600; }
.pm-readback-forms { color: rgba(255, 255, 255, 0.4); }
+/* 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: rgba(255, 255, 255, 0.05);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ font-size: 11.5px;
+ line-height: 1.2;
+ color: rgba(255, 255, 255, 0.8);
+ 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: #fff;
+}
+.pm-flow-arrow {
+ color: rgba(255, 255, 255, 0.25);
+}
+
.pm-bottomnav {
flex: 0 0 auto;
display: flex;