/*
Stylized SVG visuals of a Milky Way enterprise map.
Three flavors:
- HeroMap: medium-density, animated, with customer-journey arm
- CompactMap: tighter, for the "what is the map" section
- PerspectiveMap: switches highlight set based on selected lens
*/
const cap = (x, y, w, h, label, tools, opts = {}) => ({ x, y, w, h, label, tools, ...opts });
// Hero map nodes — abstracted, cleaner than the original but recognizable
const HERO_NODES = [
cap(70, 150, 130, 56, "Marketing", ["EXCEL", "FILE-SYS"]),
cap(230, 110, 150, 56, "Campaign & Sales", ["EMAIL", "QLIK"]),
cap(410, 130, 130, 56, "Search & Find", ["GOOGLE"]),
cap(570, 110, 130, 56, "Digital Booking", ["ADMINWEB"]),
cap(720, 130, 130, 56, "Payments", ["VISMA"]),
cap(870, 100, 150, 56, "Customer Support", ["CRM", "COURSEWEB"]),
cap(50, 280, 140, 56, "Cost calc.", ["FINANCE"]),
cap(210, 300, 140, 56, "Price calc.", ["EXCEL"]),
cap(380, 280, 160, 56, "Content prod.", ["COURSEWEB"]),
cap(70, 430, 150, 56, "Staff selection", ["E-LEARN"]),
cap(240, 450, 150, 56, "Contract mgmt.", ["FILE-SYS"]),
cap(420, 430, 150, 56, "Course design", ["POWERPT"], { partner: true }),
cap(720, 280, 160, 56, "Course delivery", ["COURSEWEB"], { partner: true }),
cap(900, 310, 140, 56, "Evaluation", ["WEBEVAL"]),
cap(720, 430, 160, 56, "Sales analysis", ["QLIK"]),
cap(900, 450, 140, 56, "Invoicing", ["DIGI-INV"]),
];
const HUB = { cx: 555, cy: 295, r: 64 };
const STICKIES = [
{ x: HUB.cx - 30, y: HUB.cy - 130, label: "Marketing", color: "#f4a261" },
{ x: HUB.cx + 80, y: HUB.cy - 60, label: "Deliver", color: "#7ee787" },
{ x: HUB.cx + 50, y: HUB.cy + 80, label: "Follow up", color: "#a78bfa" },
{ x: HUB.cx - 110, y: HUB.cy + 60, label: "Plan", color: "#f472b6" },
{ x: HUB.cx - 130, y: HUB.cy - 40, label: "Productify", color: "#facc15" },
];
function CapBox({ node, dim, highlight }) {
const cls = highlight ? "cap-box hl" : dim ? "cap-box dim" : "cap-box";
return (
{node.label}
{node.tools && (
{node.tools.map((t, i) => (
{t}
))}
)}
);
}
function CustomerJourney({ animated = true }) {
// Curving "galaxy arm" that wraps the top of the map
const path = "M 20 60 C 200 -40, 500 30, 760 40 S 1050 60, 1100 180 L 1100 360 C 1080 480, 980 540, 800 540 S 240 520, 100 540";
return (
{/* Touch points */}
{[0.05, 0.18, 0.32, 0.48, 0.62, 0.78, 0.92].map((t, i) => {
// Approximate positions along the path
const positions = [
[30, 50], [200, 8], [400, 22], [620, 42], [900, 70], [1095, 280], [950, 535]
];
const [x, y] = positions[i] || [0, 0];
return (
);
})}
);
}
function Hub({ stickies = STICKIES, label = "UniBiz" }) {
return (
{/* outer ring */}
{/* hub disc */}
{label}
{/* stickies */}
{stickies.map((s, i) => (
{s.label}
))}
);
}
// Connections from hub to selected capabilities
function HubLinks({ targets, color = "#7ee787", dashed = false }) {
return (
{targets.map((t, i) => {
const tx = t.x + t.w / 2;
const ty = t.y + t.h / 2;
return (
);
})}
);
}
// Inter-capability connection arrows
function Flows({ pairs, color = "#3a3a52" }) {
return (
{pairs.map(([a, b], i) => {
const x1 = a.x + a.w; const y1 = a.y + a.h / 2;
const x2 = b.x; const y2 = b.y + b.h / 2;
const mx = (x1 + x2) / 2;
return (
);
})}
);
}
function MapDefs() {
return (
);
}
function HeroMap({ animated = true }) {
const flows = [
[HERO_NODES[0], HERO_NODES[1]],
[HERO_NODES[1], HERO_NODES[2]],
[HERO_NODES[2], HERO_NODES[3]],
[HERO_NODES[3], HERO_NODES[4]],
[HERO_NODES[4], HERO_NODES[5]],
[HERO_NODES[6], HERO_NODES[7]],
[HERO_NODES[7], HERO_NODES[8]],
[HERO_NODES[9], HERO_NODES[10]],
[HERO_NODES[10], HERO_NODES[11]],
[HERO_NODES[12], HERO_NODES[13]],
[HERO_NODES[14], HERO_NODES[15]],
];
return (
);
}
// --- Compact map (used in "what is" section) ---
const COMPACT_NODES = [
cap(60, 60, 120, 50, "Acquire", ["CRM"]),
cap(210, 80, 120, 50, "Onboard", ["FORM"]),
cap(360, 60, 120, 50, "Activate", ["APP"]),
cap(510, 80, 120, 50, "Bill", ["STRIPE"]),
cap(60, 220, 120, 50, "Support", ["DESK"]),
cap(210, 240, 120, 50, "Retain", ["EMAIL"]),
cap(510, 240, 120, 50, "Expand", ["DATA"]),
];
function CompactMap() {
const hubCx = 410, hubCy = 165, hubR = 40;
return (
);
}
// --- Perspective map: same map, different lens ---
const PERSP_HIGHLIGHTS = {
operations: [1, 2, 3, 4, 5, 12, 13], // customer-facing flow
tactics: [0, 6, 7, 8, 9, 10, 14], // capabilities + planning
strategy: [11, 12, 13, 14, 15], // delivery + analysis
};
function PerspectiveMap({ lens = "operations" }) {
const highlights = new Set(PERSP_HIGHLIGHTS[lens] || []);
const color = lens === "operations" ? "#7ee787" : lens === "tactics" ? "#a78bfa" : "#facc15";
return (
);
}
Object.assign(window, { HeroMap, CompactMap, PerspectiveMap });