/* eslint-disable */ /* Screens 0–3. Scene area is 1380 × 900. */ /* ============================================================ SCREEN 0 — Call to action / idle loop ============================================================ */ function Screen0({ onStart }) { return ( {/* Centered hero */}
Software-defined Train
The future of the train.
A short tagline introducing the SDT concept.
Discover the SDT concept
); } /* ============================================================ SCREEN 1 — The Evolution ============================================================ */ function Screen1({ onNext, onBack, sliderValue, setSliderValue }) { // sliderValue = position of the split (0 = far left, 1 = far right) // LEFT of split = Train today. RIGHT of split = Train tomorrow. // → divider all the way right (1) → Train today fully revealed // → divider all the way left (0) → Train tomorrow fully revealed const showToday = sliderValue >= 0.95; const showTomorrow = sliderValue <= 0.05; // Drag the split anywhere on the compare area const compareRef = useRef(null); const draggingRef = useRef(false); const setFromEvent = (e) => { const el = compareRef.current; if (!el) return; const r = el.getBoundingClientRect(); const clientX = e.touches ? e.touches[0].clientX : e.clientX; const v = Math.max(0, Math.min(1, (clientX - r.left) / r.width)); setSliderValue(v); }; const onCompareDown = (e) => { draggingRef.current = true; setFromEvent(e); e.preventDefault(); }; useEffect(() => { const move = (e) => { if (draggingRef.current) setFromEvent(e); }; const up = () => { draggingRef.current = false; }; window.addEventListener("mousemove", move); window.addEventListener("mouseup", up); window.addEventListener("touchmove", move, { passive: false }); window.addEventListener("touchend", up); return () => { window.removeEventListener("mousemove", move); window.removeEventListener("mouseup", up); window.removeEventListener("touchmove", move); window.removeEventListener("touchend", up); }; }); const linePct = `${sliderValue * 100}%`; return ( {/* Title */}
The Evolution
Drag the split to reveal today's train or tomorrow's SDT.
{/* Split-screen compare area */}
{/* Train today fills the LEFT of the split */}
{/* Train tomorrow fills the RIGHT of the split */}
{/* Vertical split line */}
{/* Split handle (visual; drag works on the whole area) */}
{/* Corner labels */}
Train today
Train tomorrow
{/* Shortcut row */}
Drag the split — or use these shortcuts
{/* Popups appear once the split fully reveals one of the two trains */} {showToday && ( setSliderValue(0.5)} >
Short explanation of today's train architecture.
)} {showTomorrow && ( setSliderValue(0.5)} >
Short explanation of the SDT and how it changes the train.
)}
); } /* ============================================================ SCREEN 2 — SDT Levels (always 3 layers; compact stacks them, exploded separates them and makes them clickable) ============================================================ */ function Screen2({ onNext, onBack, vSliderValue, setVSliderValue, activeLayer, setActiveLayer }) { // vSliderValue: 0 = exploded (slider at top), 1 = compact (slider at bottom) const isExploded = vSliderValue < 0.5; // Order shown top → bottom on screen const layers = [ { id: "application", label: "Application layer", num: "03", desc: "Apps and services running on the SDT" }, { id: "abstraction", label: "Abstraction layer", num: "02", desc: "API / orchestration / digital continuity" }, { id: "physical", label: "Physical layer", num: "01", desc: "Hardware and on-board sub-systems" }, ]; // smooth gap between 0 and ~28px depending on slider position const expandFactor = Math.max(0, Math.min(1, 1 - vSliderValue * 2)); // 0 at slider≥0.5, 1 at slider=0 const gap = expandFactor * 28; const cardOpacity = isExploded ? 1 : 0.85; return ( {/* Title */}
SDT Levels
From the compact view to the three separable layers.
{/* Global text zone — sits above the stack to introduce the SDT Layer (compact only) */} {!isExploded && (
Short global text introducing what the SDT Layer is — to read before exploding the three layers.
)} {/* Layered stack — always 3 layers */}
{layers.map((L) => { const isActive = isExploded && activeLayer === L.id; return (
setActiveLayer(L.id) : undefined} style={{ border: "1px solid var(--ink)", background: isActive ? "var(--accent-soft)" : "var(--paper)", borderColor: isActive ? "var(--accent)" : "var(--ink)", padding: "18px 24px", cursor: isExploded ? "pointer" : "default", opacity: cardOpacity, transition: "background 0.2s, border-color 0.2s, opacity 0.3s", display: "flex", flexDirection: "column", gap: 6, position: "relative", }} >
{L.label}
{isExploded && Click to inspect}
{isExploded &&
{L.desc}
}
); })}
{/* Vertical slider */}
{/* Layer info popup */} {activeLayer && isExploded && ( setActiveLayer(null)} >
Short description of the selected layer.
)}
); } /* ============================================================ SCREEN 3 — Key Characteristics (3 buttons stay at the bottom so the bg loop is fully visible; info reveals top-right, no inline visual.) ============================================================ */ function Screen3({ onNext, onBack, activeChar, setActiveChar }) { const characteristics = [ { id: "data", label: "Data-driven design", num: "01", x: 290, y: 720 }, { id: "connectivity", label: "Connectivity", num: "02", x: 690, y: 720 }, { id: "evolvability", label: "Evolvability", num: "03", x: 1090, y: 720 }, ]; const info = { data: { title: "Data-driven design", text: "The SDT relies on continuous data collection and exploitation across the train." }, connectivity: { title: "Connectivity", text: "Digital continuity between on-board systems, ground and cloud." }, evolvability: { title: "Evolvability", text: "The train evolves over time through software updates." }, }; return ( {/* Title */}
Key characteristics
Three connected pillars defining the SDT — pick one to highlight it on the background loop.
{/* Dashed connecting line between the 3 buttons */} {/* 3 buttons at the bottom */} {characteristics.map((c) => { const isActive = activeChar === c.id; return ( ); })} {/* Info reveal — top-right, no inline visual (the bg loop already plays it) */} {activeChar && ( setActiveChar(null)} >
{info[activeChar].text}
Strong benefit
)}
); } Object.assign(window, { Screen0, Screen1, Screen2, Screen3 });