/* Shared figures. Every one is drawn from window.SX_DATA — nothing is typed twice.
Chrome (title, legend, note) comes from ChartFrame wherever a figure is a chart. */
const { Tag: FTag, Card: FCard, Button: FButton, ChartFrame, Plot, DataTable: FTable, GlobalDelivery: FGlobe } = window.SoftVerxDesignSystem_6fd95b;
const D = window.SX_DATA;
/* ── the six stages, as a chain ────────────────────────────────────
Circular nodes on one rail with the flow drawn between them. Each stage owns
a hue and lights on hover; the icons are hairline schematics, not pictograms. */
const STAGE_ICON = {
Sense: (<>>),
Stream: (<>>),
Store: (<>>),
Learn: (<>>),
Predict: (<>>),
Act: (<>>),
};
function StageIcon({ title, size }) {
return ;
}
const STAGE_HUE = ["var(--sx-accent)", "color-mix(in oklab, var(--sx-accent) 55%, var(--sx-field))", "var(--sx-field)", "var(--sx-signal)", "var(--sx-amber)", "var(--sx-accent)"];
/* The chain is drawn on an arc rather than a rule: six nodes sampled off one
cubic, solid arrowheads set on the tangent, labels on a common baseline. */
const bez = (t, p0, c1, c2, p3) => {
const u = 1 - t;
return [u * u * u * p0[0] + 3 * u * u * t * c1[0] + 3 * u * t * t * c2[0] + t * t * t * p3[0],
u * u * u * p0[1] + 3 * u * u * t * c1[1] + 3 * u * t * t * c2[1] + t * t * t * p3[1]];
};
function PipelineFigure({ mobile }) {
const s = D.pipeline;
const wrap = React.useRef(null);
const [w, setW] = React.useState(1120);
React.useEffect(() => {
const el = wrap.current;
if (!el) return;
const ro = new ResizeObserver(() => setW(el.clientWidth || 1120));
ro.observe(el);
setW(el.clientWidth || 1120);
return () => ro.disconnect();
}, []);
if (mobile) {
return (
{s.map((st, i) => (
{i > 0 && }
{st.n}
{st.title}
{st.detail}
))}
);
}
const R = 50, pad = R + 34, H = 232;
const P0 = [pad, H - 46], C1 = [w * 0.28, 14], C2 = [w * 0.72, 14], P3 = [w - pad, H - 46];
const pts = s.map((_, i) => bez(i / (s.length - 1), P0, C1, C2, P3));
const mids = pts.slice(0, -1).map((p, i) => {
const t = (i + 0.5) / (s.length - 1);
const a = bez(t - 0.012, P0, C1, C2, P3), b = bez(t + 0.012, P0, C1, C2, P3);
return { m: bez(t, P0, C1, C2, P3), deg: (Math.atan2(b[1] - a[1], b[0] - a[0]) * 180) / Math.PI };
});
const boxH = Math.max(...pts.map((p) => p[1])) + R + 148;
/* The label can never be wider than its own slot on the arc, or apex neighbours collide. */
const slot = Math.max(96, (w - pad * 2) / (s.length - 1) - 14);
return (
);
};
if (wide && !mobile) {
/* Fourteen sector names cannot sit horizontally at this column width, so the
heads run at 45° — full names, ending at the column they label. */
return (
{r.name}
{Array.from({ length: max }).map((_, i) => (
))}
{r.n}
))}
{rows.length < proven.length && (
{proven.length - rows.length} further solutions carry more than one deployment — the full list is on the solutions page.
)}
);
}
/* ── honest comparison ──────────────────────────────────────────────────── */
function ComparisonMatrix({ mobile }) {
const { columns, rows } = D.comparison;
if (mobile) {
return (
{rows.map((r) => (
{r.criterion}
{r.detail}
{columns.map((c, i) => (
{c.toLowerCase()}
{r.cells[i]}
))}
))}
);
}
const col = "minmax(0,1.5fr) repeat(4, minmax(0,0.85fr))";
return (
what you are weighing
{columns.map((c, i) => (
{c.toLowerCase()}
))}
{rows.map((r, ri) => (
{r.criterion}
{r.detail}
{r.cells.map((cell, i) => (
{cell}
))}
))}
);
}
/* ── engagement ladder ──────────────────────────────────────────────────── */
function EngagementLadder({ mobile }) {
const [picked, setPicked] = React.useState(1);
return (
{D.engagements.map((e, i) => (
))}
);
}
/* ── four tiers we build, as a table view companion ─────────────────────── */
/* A pipeline is a sequence, so the colour is a journey, not a set of labels:
accent ("detected") resolving into field ("measured, good") step by step. */
const PROC_STEPS = 6;
const PROC_HUE = Array.from({ length: PROC_STEPS }, (_, i) =>
`color-mix(in oklab, var(--sx-accent) ${100 - Math.round((i * 100) / (PROC_STEPS - 1))}%, var(--sx-field))`);
/* The engagement as a track, not a grid: six nodes on one rail, a pulse running
the length of it, and each stage hanging off its own node. */
function ProcessSteps({ mobile }) {
const steps = D.process;
if (mobile) {
return (
{steps.map((p, i) => (
{p.step}
{p.title}
{p.description}
))}
);
}
return (
{steps.map((p, i) => (
{p.step}
{p.title}
{p.description}
))}
);
}
Object.assign(window, { PipelineFigure, CapabilityTimeline, SectorLayerHeatmap, SolutionReuse, ComparisonMatrix, EngagementLadder, ProcessSteps, PRACTICE_LAYER });
/* ── the globe, without its ranked bar list ─────────────────────────────── */
function GlobeOnly({ mobile, height }) {
const ref = React.useRef(null);
React.useEffect(() => {
const root = ref.current?.firstElementChild;
if (!root) return;
const hide = () => {
const ul = root.querySelector("ul");
if (!ul) return;
let el = ul;
while (el.parentElement && el.parentElement !== root) el = el.parentElement;
el.style.display = "none";
};
hide();
const t = setTimeout(hide, 500);
return () => clearTimeout(t);
}, []);
return (
{D.countries.map((c) => (
{c.flag && }
{c.name}{c.total}
))}
);
}
Object.assign(window, { GlobeOnly });
/* ── the stack as a core sample ─────────────────────────────────────────── */
const LAYER_TECH = {
agents: ["LangChain", "LangGraph", "LangSmith", "LlamaIndex", "vLLM", "Ollama", "Model Context Protocol", "Claude API", "OpenAI API", "Tesseract", "PaddleOCR", "Azure Document Intelligence", "pgvector"],
ml: ["PyTorch", "TensorFlow", "scikit-learn", "Hugging Face Transformers", "Ultralytics YOLO", "OpenCV", "ONNX Runtime", "OpenVINO", "XGBoost", "pandas", "NumPy"],
edge: ["ESP32", "STM32", "Raspberry Pi (through Pi 5)", "ESP-IDF", "FreeRTOS", "MQTT", "Modbus RTU/TCP", "BLE", "LoRa", "ThingsBoard", "AWS IoT Core"],
apps: ["React", "Next.js", "Tailwind CSS", "TypeScript", "TanStack Query", "Recharts", "React Native", "Flutter", "Expo"],
data: ["PostgreSQL", "Oracle", "SQL Server", "MySQL", "MongoDB", "DynamoDB", "Redis", "Kafka", "RabbitMQ", "Celery"],
core: ["Python", "Java", "C#", "C", "C++", "FastAPI", "Django", "Spring Boot", "ASP.NET Core", "Node.js", "REST", "gRPC"],
};
/* Layers inherit the three family hues, so a layer is the same colour here as
it is in the practice dome and the solutions register. */
const LAYER_HUE = { core: "var(--sx-layer-core)", data: "var(--sx-layer-core)", apps: "var(--sx-layer-connected)", edge: "var(--sx-layer-connected)", ml: "var(--sx-layer-intelligence)", agents: "var(--sx-layer-intelligence)" };
const LAYER_ORDER = ["core", "data", "apps", "edge", "ml", "agents"];
/* Six layers as a numbered chain, oldest first: each one is built on the one
before it, which is the argument the figure has to make. The technologies
belong to the open step — six lists at once is a wall, not a diagram. */
function StrataFigure({ mobile }) {
const byId = {};
D.layers.forEach((l) => { byId[l.id] = l; });
const steps = LAYER_ORDER.map((id) => byId[id]).filter(Boolean);
const [open, setOpen] = React.useState(steps[steps.length - 1].id);
const g = steps.find((s) => s.id === open) || steps[0];
const hue = LAYER_HUE[g.id];
return (
{steps.map((l, i) => {
const on = open === l.id;
const h = LAYER_HUE[l.id];
return (
{!mobile && i < steps.length - 1 && (
)}
);
})}
{g.label}
{(LAYER_TECH[g.id] || []).map((it) => (
{it}
))}
Read left to right: 01 is the layer everything else writes to, 06 is the newest work. Pick a step to see what we run in it.