+ See how Tricorder powers software security across Images, Libraries,
+ and CleanSight.
+
+
+ Talk to an Expert
+
+
+
+
+ {/* Verdict ledger — desktop only; the slot is too short to stack it. */}
+
+
+
+
+ Verdicts
+
+ live
+
+
+ {LEDGER.map((row, i) => (
+
+
+ {row.name}
+
+
+
+ {row.verdict}
+
+
+ ))}
+
+
+
+
+ );
+}
diff --git a/apps/web/src/components/sections/tricorder/TricorderContext.tsx b/apps/web/src/components/sections/tricorder/TricorderContext.tsx
new file mode 100644
index 00000000..ed920967
--- /dev/null
+++ b/apps/web/src/components/sections/tricorder/TricorderContext.tsx
@@ -0,0 +1,486 @@
+import { Container, Section } from "@/components/layout";
+import { Reveal, RevealItem, RevealStagger } from "@/components/ui/Reveal";
+import { ScaleToFit } from "@/components/ui/ScaleToFit";
+import { GlassIcon } from "@/components/sections/_shared/GlassIcon";
+import { SIGNAL } from "./tricorder-palette";
+
+/**
+ * "Software Doesn't Exist in Isolation." — a component is understood through
+ * three lenses (history, behavior, relationships). Copy on the left; on the
+ * right a coded scene: the three lenses as glass tiles converging on the
+ * component, which resolves downward into a verdict. Beams carry travelling
+ * packets (the Clean Libraries `cs-lep-*` keyframes) and the whole scene is
+ * laid out on a fixed design canvas scaled to fit, like LibrariesPipeline.
+ * Dark section.
+ */
+
+type LensKey = "history" | "behavior" | "relationships";
+
+interface Lens {
+ key: LensKey;
+ title: string;
+ detail: string;
+ accent: string;
+ /** Facts shown inside the tile on the scene. */
+ facts: readonly [string, string, string];
+}
+
+const LENSES: Lens[] = [
+ {
+ key: "history",
+ title: "History",
+ detail: "Versions, changes, vulnerabilities.",
+ accent: SIGNAL.history,
+ facts: ["2.4.1 → 2.5.0", "maintainer changed", "0 CVEs on record"],
+ },
+ {
+ key: "behavior",
+ title: "Behavior",
+ detail: "Capabilities, purpose, reachability.",
+ accent: SIGNAL.behavior,
+ facts: ["network · shell", "file system", "reachable at runtime"],
+ },
+ {
+ key: "relationships",
+ title: "Relationships",
+ detail: "Dependencies, maintainers, infrastructure.",
+ accent: SIGNAL.relationships,
+ facts: ["41 dependencies", "1 shared host", "2 linked packages"],
+ },
+];
+
+const MONO = "var(--font-mono), ui-monospace, Menlo, Consolas, monospace";
+
+function LensGlyph({ lens, size }: { lens: LensKey; size: number }): React.ReactElement {
+ const common = {
+ width: size,
+ height: size,
+ viewBox: "0 0 24 24",
+ fill: "none",
+ stroke: "currentColor",
+ strokeWidth: 1.7,
+ strokeLinecap: "round" as const,
+ strokeLinejoin: "round" as const,
+ "aria-hidden": true,
+ };
+ switch (lens) {
+ case "history":
+ return (
+
+ );
+ case "behavior":
+ return (
+
+ );
+ case "relationships":
+ return (
+
+ );
+ }
+}
+
+/* ---- Scene geometry: 560×600 design canvas -------------------------------- */
+const VB = { w: 560, h: 600 } as const;
+const TILE = { w: 232, h: 132 } as const;
+/** Tile top-left corners: two on the left column, one on the right, staggered. */
+const TILE_POS: Record = {
+ history: { x: 0, y: 30 },
+ behavior: { x: 328, y: 96 },
+ relationships: { x: 0, y: 244 },
+};
+const CORE = { cx: 330, cy: 372, r: 64 } as const;
+const VERDICT = { cx: 330, cy: 546 } as const;
+const pct = (v: number, total: number): string => `${(v / total) * 100}%`;
+
+/** Curve from a tile's nearest edge midpoint to the core's rim, arriving radially. */
+function beamFrom(key: LensKey): string {
+ const p = TILE_POS[key];
+ const tileLeftOfCore = p.x + TILE.w / 2 < CORE.cx;
+ const sx = tileLeftOfCore ? p.x + TILE.w : p.x;
+ const sy = p.y + TILE.h / 2;
+ const dx = sx - CORE.cx;
+ const dy = sy - CORE.cy;
+ const len = Math.hypot(dx, dy);
+ const ux = dx / len;
+ const uy = dy / len;
+ const ex = CORE.cx + ux * (CORE.r + 4);
+ const ey = CORE.cy + uy * (CORE.r + 4);
+ const c1x = sx + (tileLeftOfCore ? 56 : -56);
+ const c2x = ex + ux * 70;
+ const c2y = ey + uy * 70;
+ return `M ${sx} ${sy} C ${c1x} ${sy}, ${c2x.toFixed(1)} ${c2y.toFixed(1)}, ${ex.toFixed(1)} ${ey.toFixed(1)}`;
+}
+/** Starts below the core's "Component" caption so the line never crosses the text. */
+const TRUNK = `M ${CORE.cx} ${CORE.cy + CORE.r + 40} L ${CORE.cx} ${VERDICT.cy - 26}`;
+
+function LensTile({ lens }: { lens: Lens }): React.ReactElement {
+ return (
+
+
+
+
+
+
+ {lens.title}
+
+
+
+ {lens.facts.map((f) => (
+
+
+ {f}
+
+ ))}
+
+
+ );
+}
+
+/** The component under analysis — a dark orb with a brand-gradient rim and the cube glyph. */
+function ComponentCore(): React.ReactElement {
+ const size = CORE.r * 2;
+ return (
+
+
+ {[0, 1.4].map((d) => (
+
+ ))}
+
+
+
+
+
+ Component
+
+
+ );
+}
+
+/** Verdict readout under the core — the three states, with the resolved one lit. */
+function VerdictReadout(): React.ReactElement {
+ const states = [
+ { label: "Malicious", color: "#f43f5e", lit: true },
+ { label: "Uncertain", color: "#f7a35c", lit: false },
+ { label: "Pass", color: "#2dd4bf", lit: false },
+ ];
+ return (
+
+ Understand every component through its history, behavior, and
+ relationships.
+
+
+
+
+ {LENSES.map((l, i) => (
+
+
+
+
+
+ {l.title}
+
+
+ {l.detail}
+
+
+
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/apps/web/src/components/sections/tricorder/TricorderHero.tsx b/apps/web/src/components/sections/tricorder/TricorderHero.tsx
new file mode 100644
index 00000000..313e474c
--- /dev/null
+++ b/apps/web/src/components/sections/tricorder/TricorderHero.tsx
@@ -0,0 +1,185 @@
+import Link from "next/link";
+
+import { HeroReveal } from "@/components/ui/Reveal";
+import { TricorderHeroConsole } from "./TricorderHeroConsole";
+
+/**
+ * Tricorder hero — the site's product-hero shell (CleanSight / Clean Libraries):
+ * the dark navy→purple gradient, left-aligned copy with a glass primary and a
+ * ghost secondary, and the artifact on the right. The artifact is the scan
+ * console (TricorderHeroConsole), drawn in code like the Clean Libraries
+ * constellation rather than rendered.
+ */
+export function TricorderHero(): React.ReactElement {
+ return (
+
+ {/* Gridline overlay — the shared hero decoration. */}
+ {/* eslint-disable-next-line @next/next/no-img-element */}
+
+
+ {/* Purple wash behind the console. */}
+
+ {/* Cyan counter-glow, low left, so the copy column isn't sitting on flat navy. */}
+
+
+
+
+
+
+
+
+ Tricorder by CleanStart
+
+
+
+
+
+ The Intelligence Layer for Software Trust
+
+
+
+
+
+ Understand Every Software Dependency Before Trusting It.
+
+
+
+
+
+
+ Talk to an Expert
+
+
+ How It Powers CleanStart
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/apps/web/src/components/sections/tricorder/TricorderHeroConsole.tsx b/apps/web/src/components/sections/tricorder/TricorderHeroConsole.tsx
new file mode 100644
index 00000000..a6a21cd4
--- /dev/null
+++ b/apps/web/src/components/sections/tricorder/TricorderHeroConsole.tsx
@@ -0,0 +1,288 @@
+import { SIGNAL, VERDICT } from "./tricorder-palette";
+
+/**
+ * The hero artifact: a Tricorder scan console drawn in code. A component is
+ * being analysed — its identity, its version chain, four signal meters filling
+ * in turn, and finally the verdict. The console sits on a slow radar sweep so
+ * the page opens on the act of scanning rather than on a static product shot.
+ *
+ * Everything here is CSS/SVG (no raster). The meters, the sweep and the verdict
+ * reveal are one-shot / ambient CSS animations (`cs-tri-*` in globals.css) and
+ * all of them are off under prefers-reduced-motion, where the console renders
+ * in its final state.
+ */
+
+interface SignalRow {
+ key: string;
+ label: string;
+ finding: string;
+ accent: string;
+ /** Meter fill, 0–1. */
+ level: number;
+}
+
+const ROWS: SignalRow[] = [
+ { key: "behavior", label: "Behavior", finding: "Opens outbound socket at install", accent: SIGNAL.behavior, level: 0.92 },
+ { key: "history", label: "History", finding: "Maintainer changed 3 days ago", accent: SIGNAL.history, level: 0.74 },
+ { key: "correlate", label: "Correlation", finding: "Shares infrastructure with 2 flagged packages", accent: SIGNAL.relationships, level: 0.83 },
+ { key: "enrich", label: "Enrichment", finding: "No CVE on record", accent: SIGNAL.intel, level: 0.18 },
+];
+
+const VERSIONS = ["2.4.0", "2.4.1", "2.5.0"] as const;
+
+const MONO = "var(--font-mono), ui-monospace, Menlo, Consolas, monospace";
+
+function CubeGlyph({ size }: { size: number }): React.ReactElement {
+ return (
+
+ );
+}
+
+export function TricorderHeroConsole(): React.ReactElement {
+ return (
+
+ {/* Radar sweep — two hairline rings and a conic wedge that rotates
+ behind the console. Wider than the card so it reads as a field. */}
+
+
+
+
+
+
+
+ {/* The console. */}
+
+ {/* Title bar. */}
+
+
+ Tricorder · Scan
+
+
+
+ analyzing
+
+
+
+ {/* Identity row. */}
+
+
+
+
+
+
+ strutil-core@2.5.0
+
+
+ npm · 41 transitive dependencies
+
+
+ {/* Version chain — the newest version is the one under scrutiny. */}
+
+ {VERSIONS.map((v, i) => {
+ const latest = i === VERSIONS.length - 1;
+ return (
+
+ {i > 0 ? (
+
+ ) : null}
+
+ {v}
+
+
+ );
+ })}
+
+
+
+ {/* Signal meters. */}
+
+ {ROWS.map((row, i) => (
+
+
+ {row.label}
+
+
+
+
+
+
+ {row.finding}
+
+
+
+ ))}
+
+
+ {/* Verdict. */}
+
+
+
+ Verdict
+
+
+ 3 of 4 signals · evidence attached
+
+
+
+
+ Malicious
+
+
+
+
+ );
+}
diff --git a/apps/web/src/components/sections/tricorder/TricorderPipeline.tsx b/apps/web/src/components/sections/tricorder/TricorderPipeline.tsx
new file mode 100644
index 00000000..c14c4bec
--- /dev/null
+++ b/apps/web/src/components/sections/tricorder/TricorderPipeline.tsx
@@ -0,0 +1,278 @@
+import { Container, Section } from "@/components/layout";
+import { Reveal, RevealItem, RevealStagger } from "@/components/ui/Reveal";
+import { GlassIcon } from "@/components/sections/_shared/GlassIcon";
+import { INK, INK_MUTED, SIGNAL, VERDICT } from "./tricorder-palette";
+
+/**
+ * "From Signals to Verdicts." — the four analysis stages as a left-to-right
+ * rail (Analyze → Compare → Correlate → Enrich) that terminates in a dark
+ * verdict terminal showing the three possible outcomes. A pulse travels the
+ * rail under the stages so the row reads as a pipeline, not a list of tiles.
+ * Light section. Stages stack 2×2 below lg and single-column below sm.
+ */
+
+type StageKey = "analyze" | "compare" | "correlate" | "enrich";
+
+interface Stage {
+ key: StageKey;
+ index: string;
+ title: string;
+ desc: string;
+ accent: string;
+}
+
+const STAGES: Stage[] = [
+ { key: "analyze", index: "01", title: "Analyze", desc: "Understand capabilities, purpose, and reachability.", accent: SIGNAL.behavior },
+ { key: "compare", index: "02", title: "Compare", desc: "Identify unexpected changes across versions.", accent: SIGNAL.history },
+ { key: "correlate", index: "03", title: "Correlate", desc: "Connect packages, maintainers, and infrastructure.", accent: SIGNAL.relationships },
+ { key: "enrich", index: "04", title: "Enrich", desc: "Add threat intelligence and vulnerability context.", accent: SIGNAL.intel },
+];
+
+const OUTCOMES = [
+ { label: "Malicious", color: VERDICT.malicious },
+ { label: "Uncertain", color: VERDICT.uncertain },
+ { label: "Pass", color: VERDICT.pass },
+] as const;
+
+const RAIL_CSS = `
+@keyframes cs-tri-rail{from{background-position:-40% 0}to{background-position:140% 0}}
+.cs-tri-rail-pulse{background:linear-gradient(90deg,transparent,rgba(255,255,255,0.9) 12%,transparent 24%);background-size:38% 100%;animation:cs-tri-rail 4.2s linear infinite}
+@media (prefers-reduced-motion:reduce){.cs-tri-rail-pulse{animation:none;opacity:0}}
+`;
+
+function StageGlyph({ stage, size }: { stage: StageKey; size: number }): React.ReactElement {
+ const common = {
+ width: size,
+ height: size,
+ viewBox: "0 0 24 24",
+ fill: "none",
+ stroke: "currentColor",
+ strokeWidth: 1.7,
+ strokeLinecap: "round" as const,
+ strokeLinejoin: "round" as const,
+ "aria-hidden": true,
+ };
+ switch (stage) {
+ case "analyze":
+ return (
+
+ );
+ case "compare":
+ return (
+
+ );
+ case "correlate":
+ return (
+
+ );
+ case "enrich":
+ return (
+
+ );
+ }
+}
+
+function StageCard({ stage }: { stage: Stage }): React.ReactElement {
+ return (
+
+
+
+
+
+
+ {stage.index}
+
+
+
+ {stage.title}
+
+
+ {stage.desc}
+
+
+ );
+}
+
+/** The terminal: a dark card carrying the three verdict states. */
+function VerdictTerminal(): React.ReactElement {
+ return (
+
+ The software supply chain changes faster than vulnerability
+ databases can document it.
+
+
+
+
+
+ {CARDS.map((card) => (
+
+
+
+ ))}
+
+
+
+ );
+}
diff --git a/apps/web/src/components/sections/tricorder/tricorder-palette.ts b/apps/web/src/components/sections/tricorder/tricorder-palette.ts
new file mode 100644
index 00000000..4dfb426b
--- /dev/null
+++ b/apps/web/src/components/sections/tricorder/tricorder-palette.ts
@@ -0,0 +1,22 @@
+/**
+ * Tricorder page palette — one place for the signal and verdict colours so the
+ * hero console, the context scene, the pipeline and the substrate all agree.
+ * Signal accents follow the site's per-card accent set (Clean Libraries uses
+ * the same blue / teal / purple / amber quartet); verdict colours are the
+ * three states the product returns.
+ */
+export const SIGNAL = {
+ history: "#5b9bff",
+ behavior: "#2dd4bf",
+ relationships: "#a974ff",
+ intel: "#f7a35c",
+} as const;
+
+export const VERDICT = {
+ malicious: "#f43f5e",
+ uncertain: "#f7a35c",
+ pass: "#2dd4bf",
+} as const;
+
+export const INK = "#111111";
+export const INK_MUTED = "#555555";
diff --git a/apps/web/src/lib/nav-config.ts b/apps/web/src/lib/nav-config.ts
index b71c8576..6e241e4d 100644
--- a/apps/web/src/lib/nav-config.ts
+++ b/apps/web/src/lib/nav-config.ts
@@ -89,7 +89,13 @@ export const NAV_TREE: NavItem[] = [
href: "/cleansight",
description: "Runtime visibility into vulnerabilities and drift.",
icon: "radar",
- }
+ },
+ {
+ label: "Tricorder",
+ href: "/tricorder",
+ description: "The intelligence layer behind every CleanStart verdict.",
+ icon: "lens",
+ },
],
},
],
diff --git a/docs/web/WEB-PAGES.md b/docs/web/WEB-PAGES.md
index a0c79703..4581dca1 100644
--- a/docs/web/WEB-PAGES.md
+++ b/docs/web/WEB-PAGES.md
@@ -86,6 +86,7 @@ page slugs, categories, types, and build status across the dev journey.
| 8 | CleanStart Images | `/cleanstart-images` | Static | ✅ | All 5 sections built (Hero, Browse, EasyStart, UVP, Environment) |
| 8b | CleanStart Platform | `/cleanstart-platform` | Static | ❌ removed | **Deleted 2026-09-02** — route, `cleanstart-platform` section components and image assets removed. The page was never finished: it shipped `noindex`, absent from `nav-config.ts` and de-listed from the sitemap, so nothing was de-ranked and no redirect was seeded. It did resolve publicly and was advertised in `public/llms.txt` (entry removed), so register a 301 in the CMS `redirects` collection if the bare URL is still being hit. `cta-cube-textured.webp` moved to `public/images/teams/` — the Teams CTA was the only other consumer. Recover the whole page from git history (last built state: commit before this deletion) when it is rebuilt. |
| 8c | Clean Libraries | `/clean-libraries` | Static | ✅ | Built 2026-06-17 from Figma 1512:988. 4 sections (Hero, Dependency-Risk cards, Invisible-Pipeline diagram, Built-Into-Workflow cards) + Govern-Every-Dependency CTA. Linked from Products nav (`folder` icon) and from the Pricing "Clean Libraries" offering. |
+| 8d | Tricorder | `/tricorder` | Static | ✅ | Built 2026-09-16 from the "The Intelligence Layer" copy doc (no Figma; every scene is drawn in SVG/CSS on the site's tokens). Sections: `TricorderHero` (scan-console artifact on a radar sweep) → `TricorderThreatGap` ("Not Every Threat Has a CVE", three evidence cards: version timeline / behaviour diff / relationship graph) → `TricorderContext` ("Software Doesn't Exist in Isolation", three lenses converging on the component and resolving to a verdict) → `TricorderPipeline` (Analyze → Compare → Correlate → Enrich → Verdict terminal) → `TricorderSubstrate` (Tricorder core fanning out to Clean Images / Clean Libraries / CleanSight; anchor `#one-intelligence-layer`) + `TricorderCTA` in the footer slot ("Talk to an Expert" → `/contact-us`). Emits BreadcrumbList + SoftwareApplication. Indexable and in `STATIC_ROUTES`; linked from Products nav (`lens` glyph), the footer Product column and `llms.txt`. **Add a `pageRegistry` row for `/tricorder` in the CMS** so the page emits a WebPage node. |
---
From be0aa2ad9ea72b2e005d7f988bb5296132c52072 Mon Sep 17 00:00:00 2001
From: Claude
Date: Wed, 16 Sep 2026 07:11:59 +0000
Subject: [PATCH 02/26] feat(web): rebuild the Tricorder "One Intelligence
Layer" scene
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Replace the orb-and-branches diagram with the layer drawn literally: the
three products stand on glass pedestals carrying the site's own hexagonal
product art (shared with the homepage factory), each dropping a flowing
current into a gridded floor that recedes in perspective, with a flare
where it lands. The Tricorder emblem — a bevelled hex in the product-art
palette with the lens mark — is set into the floor, with the four analysis
stages laid along it. Decision chips now carry lucide glyphs and align to
the card bottoms. Below lg the pedestals stack and the floor becomes the
homepage-style hatched layer panel.
Co-Authored-By: Claude Fable 5.1
Claude-Session: https://claude.ai/code/session_0198mhURhZhARAmAHoDNoCmQ
---
apps/web/src/app/globals.css | 36 +
.../sections/tricorder/TricorderSubstrate.tsx | 616 ++++++++++--------
docs/web/WEB-PAGES.md | 2 +-
3 files changed, 398 insertions(+), 256 deletions(-)
diff --git a/apps/web/src/app/globals.css b/apps/web/src/app/globals.css
index 3843b8f3..246460f2 100644
--- a/apps/web/src/app/globals.css
+++ b/apps/web/src/app/globals.css
@@ -6430,3 +6430,39 @@ body {
animation: none !important;
}
}
+
+/* Tricorder page — the intelligence-layer floor (TricorderSubstrate.tsx). A
+ scan band crosses the perspective plane; the emblem set into it breathes.
+ Transform / opacity only; both off under reduced-motion. */
+@keyframes cs-tri-scan {
+ from {
+ transform: translateX(-120%);
+ }
+ to {
+ transform: translateX(520%);
+ }
+}
+@keyframes cs-tri-emblem {
+ 0%,
+ 100% {
+ transform: translateY(0);
+ filter: drop-shadow(0 12px 22px rgba(154, 81, 255, 0.35));
+ }
+ 50% {
+ transform: translateY(-4px);
+ filter: drop-shadow(0 20px 30px rgba(154, 81, 255, 0.55));
+ }
+}
+.cs-tri-scan {
+ will-change: transform;
+ animation: cs-tri-scan 7s linear infinite;
+}
+.cs-tri-emblem {
+ animation: cs-tri-emblem 5.5s ease-in-out infinite;
+}
+@media (prefers-reduced-motion: reduce) {
+ .cs-tri-scan,
+ .cs-tri-emblem {
+ animation: none !important;
+ }
+}
diff --git a/apps/web/src/components/sections/tricorder/TricorderSubstrate.tsx b/apps/web/src/components/sections/tricorder/TricorderSubstrate.tsx
index a0456591..001d2636 100644
--- a/apps/web/src/components/sections/tricorder/TricorderSubstrate.tsx
+++ b/apps/web/src/components/sections/tricorder/TricorderSubstrate.tsx
@@ -1,30 +1,42 @@
+import Image from "next/image";
import Link from "next/link";
+import { Clock3, Code2, Radar } from "lucide-react";
import { Container, Section } from "@/components/layout";
+import { FlowBeam } from "@/components/ui/FlowBeam";
import { Reveal, RevealItem, RevealStagger } from "@/components/ui/Reveal";
-import { ScaleToFit } from "@/components/ui/ScaleToFit";
-import { GlassIcon } from "@/components/sections/_shared/GlassIcon";
/**
- * "One Intelligence Layer. Multiple Security Decisions." — Tricorder as the
- * substrate the three products draw on. A single glowing core at the top fans
- * out through three branches (packets flow downward) into the product cards,
- * each of which names the decision it makes and links to its page. The scene
- * is a fixed 1040px design canvas scaled to fit, like the other coded scenes;
- * below lg the branches become a vertical spine and the cards stack.
+ * "One Intelligence Layer. Multiple Security Decisions." — Tricorder rendered
+ * literally as the layer the products stand on. The three product badges (the
+ * site's own hexagonal product art, shared with the homepage factory) float on
+ * glass pedestals; a current runs down from each into a lit floor that recedes
+ * in perspective, and the Tricorder emblem is set into that floor with the
+ * four analysis stages laid along it. The homepage already draws Tricorder as
+ * the "Intelligence Center" bar the product cards plug into — this is the same
+ * idea, built as a scene.
+ *
+ * Motion: the beams flow (FlowBeam), a scan band crosses the floor, the emblem
+ * breathes. All CSS, all off under prefers-reduced-motion. Below lg the floor
+ * becomes a compact layer panel and the pedestals stack.
*
* Last section on the page: the bottom padding reserves the footer CTA's
* overlap zone (see Footer.tsx layout contract).
*/
+type DecisionIcon = "release" | "build" | "fleet";
+
interface Product {
key: string;
title: string;
desc: string;
decision: string;
+ icon: DecisionIcon;
href: string;
- accent: string;
- glyph: "cube" | "brackets" | "radar";
+ art: string;
+ artAlt: string;
+ /** Tint for the pedestal glow and the decision chip. */
+ tint: string;
}
const PRODUCTS: Product[] = [
@@ -33,314 +45,408 @@ const PRODUCTS: Product[] = [
title: "Clean Images",
desc: "Verify images before release.",
decision: "Release-time decisions",
+ icon: "release",
href: "/cleanstart-images",
- accent: "#5b9bff",
- glyph: "cube",
+ art: "/images/cleanstart-factory/clean-images-2.webp",
+ artAlt: "Clean Images badge: a stack of hardened image layers with a verified shield",
+ tint: "#5b9bff",
},
{
key: "libraries",
title: "Clean Libraries",
desc: "Trust dependencies before they enter your build.",
decision: "Build-time decisions",
+ icon: "build",
href: "/clean-libraries",
- accent: "#2dd4bf",
- glyph: "brackets",
+ art: "/images/cleanstart-factory/clean-libraries-2.webp",
+ artAlt: "Clean Libraries badge: verified library volumes with a package seal",
+ tint: "#34d399",
},
{
key: "cleansight",
title: "CleanSight",
desc: "Map risk across your container estate.",
decision: "Fleet-level intelligence",
+ icon: "fleet",
href: "/cleansight",
- accent: "#a974ff",
- glyph: "radar",
+ art: "/images/cleanstart-factory/cleansight-2.webp",
+ artAlt: "CleanSight badge: a dependency map under a magnifying lens",
+ tint: "#a974ff",
},
];
-function ProductGlyph({ glyph, size }: { glyph: Product["glyph"]; size: number }): React.ReactElement {
- const common = {
- width: size,
- height: size,
- viewBox: "0 0 24 24",
- fill: "none",
- stroke: "currentColor",
- strokeWidth: 1.7,
- strokeLinecap: "round" as const,
- strokeLinejoin: "round" as const,
- "aria-hidden": true,
- };
- switch (glyph) {
- case "cube":
- return (
-
- );
- case "brackets":
- return (
-
- );
- case "radar":
- return (
-
- );
- }
-}
+const STAGES = [
+ { label: "Analyze", color: "#2dd4bf" },
+ { label: "Compare", color: "#5b9bff" },
+ { label: "Correlate", color: "#a974ff" },
+ { label: "Enrich", color: "#f7a35c" },
+] as const;
-/* ---- Scene geometry: 1040×520 design canvas ------------------------------- */
-const VB = { w: 1040, h: 576 } as const;
-const CORE = { cx: 520, cy: 96, r: 62 } as const;
-const CARD = { w: 320, h: 256, y: 312 } as const;
-const CARD_X = [0, 360, 720] as const;
-const BUS_Y = 250;
-const pct = (v: number, total: number): string => `${(v / total) * 100}%`;
-
-function branchPath(i: number): string {
- const cx = (CARD_X[i] ?? 0) + CARD.w / 2;
- const top = CORE.cy + CORE.r + 58; // below the core's two-line caption
- if (cx === CORE.cx) return `M ${CORE.cx} ${top} L ${cx} ${CARD.y}`;
- const dir = cx < CORE.cx ? -1 : 1;
- return `M ${CORE.cx} ${top} L ${CORE.cx} ${BUS_Y - 18} Q ${CORE.cx} ${BUS_Y} ${CORE.cx + dir * 18} ${BUS_Y} L ${cx - dir * 18} ${BUS_Y} Q ${cx} ${BUS_Y} ${cx} ${BUS_Y + 18} L ${cx} ${CARD.y}`;
+function DecisionGlyph({ icon, size }: { icon: DecisionIcon; size: number }): React.ReactElement {
+ const props = { size, strokeWidth: 1.8, "aria-hidden": true } as const;
+ switch (icon) {
+ case "release":
+ return ;
+ case "build":
+ return ;
+ case "fleet":
+ return ;
+ }
}
-/** The Tricorder core: a dark glass orb with the brand rim, the wordmark beneath. */
-function TricorderCore(): React.ReactElement {
- const size = CORE.r * 2;
+/** A product standing on the layer: badge art over a lit pedestal, name, decision. */
+function Pedestal({ product, priority }: { product: Product; priority: boolean }): React.ReactElement {
return (
-
-
+ {/* Tinted hairline along the top edge — the product's colour, not a border. */}
+
+ {/* Light pool behind the badge. */}
+
+
+
- {[0, 1.4].map((d) => (
-
- ))}
-
-
- {/* The Tricorder mark: a lens with a check — the verdict glyph. */}
-
+
+
+
+
+
);
}
-function SceneDesktop(): React.ReactElement {
+/** The Tricorder mark set into the floor: a bevelled hex in the product-art palette, the lens inside. */
+function Emblem({ size }: { size: number }): React.ReactElement {
return (
-
-
-
+ );
+}
+
+/** The four stages, laid along the floor under the emblem. */
+function StageRail(): React.ReactElement {
+ return (
+
+ {STAGES.map((s, i) => (
+
+ ))}
+
+ );
+}
+/** The lit floor: a gridded plane receding in perspective, with a scan band crossing it. */
+function Floor(): React.ReactElement {
+ return (
+
+ {/* Far edge = element width (transform-origin is the top edge), so the plane
+ starts exactly under the brand line and only the near edge widens. */}
+
+ {/* Scan band travelling across the plane. */}
+
+ {/* The far edge: a crisp brand line with a bloom under it. */}
+
+
+
+ {/* Pedestals. */}
+
+ {PRODUCTS.map((p, i) => (
+
+
+
+ ))}
+
+ {/* Currents from each pedestal down into the layer, with a flare where they land. */}
+
);
diff --git a/docs/web/WEB-PAGES.md b/docs/web/WEB-PAGES.md
index 4581dca1..ee36eac2 100644
--- a/docs/web/WEB-PAGES.md
+++ b/docs/web/WEB-PAGES.md
@@ -86,7 +86,7 @@ page slugs, categories, types, and build status across the dev journey.
| 8 | CleanStart Images | `/cleanstart-images` | Static | ✅ | All 5 sections built (Hero, Browse, EasyStart, UVP, Environment) |
| 8b | CleanStart Platform | `/cleanstart-platform` | Static | ❌ removed | **Deleted 2026-09-02** — route, `cleanstart-platform` section components and image assets removed. The page was never finished: it shipped `noindex`, absent from `nav-config.ts` and de-listed from the sitemap, so nothing was de-ranked and no redirect was seeded. It did resolve publicly and was advertised in `public/llms.txt` (entry removed), so register a 301 in the CMS `redirects` collection if the bare URL is still being hit. `cta-cube-textured.webp` moved to `public/images/teams/` — the Teams CTA was the only other consumer. Recover the whole page from git history (last built state: commit before this deletion) when it is rebuilt. |
| 8c | Clean Libraries | `/clean-libraries` | Static | ✅ | Built 2026-06-17 from Figma 1512:988. 4 sections (Hero, Dependency-Risk cards, Invisible-Pipeline diagram, Built-Into-Workflow cards) + Govern-Every-Dependency CTA. Linked from Products nav (`folder` icon) and from the Pricing "Clean Libraries" offering. |
-| 8d | Tricorder | `/tricorder` | Static | ✅ | Built 2026-09-16 from the "The Intelligence Layer" copy doc (no Figma; every scene is drawn in SVG/CSS on the site's tokens). Sections: `TricorderHero` (scan-console artifact on a radar sweep) → `TricorderThreatGap` ("Not Every Threat Has a CVE", three evidence cards: version timeline / behaviour diff / relationship graph) → `TricorderContext` ("Software Doesn't Exist in Isolation", three lenses converging on the component and resolving to a verdict) → `TricorderPipeline` (Analyze → Compare → Correlate → Enrich → Verdict terminal) → `TricorderSubstrate` (Tricorder core fanning out to Clean Images / Clean Libraries / CleanSight; anchor `#one-intelligence-layer`) + `TricorderCTA` in the footer slot ("Talk to an Expert" → `/contact-us`). Emits BreadcrumbList + SoftwareApplication. Indexable and in `STATIC_ROUTES`; linked from Products nav (`lens` glyph), the footer Product column and `llms.txt`. **Add a `pageRegistry` row for `/tricorder` in the CMS** so the page emits a WebPage node. |
+| 8d | Tricorder | `/tricorder` | Static | ✅ | Built 2026-09-16 from the "The Intelligence Layer" copy doc (no Figma; every scene is drawn in SVG/CSS on the site's tokens). Sections: `TricorderHero` (scan-console artifact on a radar sweep) → `TricorderThreatGap` ("Not Every Threat Has a CVE", three evidence cards: version timeline / behaviour diff / relationship graph) → `TricorderContext` ("Software Doesn't Exist in Isolation", three lenses converging on the component and resolving to a verdict) → `TricorderPipeline` (Analyze → Compare → Correlate → Enrich → Verdict terminal) → `TricorderSubstrate` (the three products on glass pedestals with the shared hexagonal product art from `cleanstart-factory/`, currents dropping into a perspective "intelligence layer" floor that carries the Tricorder emblem and the four stages; anchor `#one-intelligence-layer`) + `TricorderCTA` in the footer slot ("Talk to an Expert" → `/contact-us`). Emits BreadcrumbList + SoftwareApplication. Indexable and in `STATIC_ROUTES`; linked from Products nav (`lens` glyph), the footer Product column and `llms.txt`. **Add a `pageRegistry` row for `/tricorder` in the CMS** so the page emits a WebPage node. |
---
From 56f6a28d01b6a70cc0e4bfcdf476ad49f95f65fc Mon Sep 17 00:00:00 2001
From: Claude
Date: Wed, 16 Sep 2026 08:14:45 +0000
Subject: [PATCH 03/26] feat(web): rebuild the Tricorder "isolation" section as
an argument
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The section diagrammed the claim instead of making it. Replace the floating
tiles and dotted beams with one panel holding the same package twice: a flat,
desaturated "seen in isolation" readout where every fact is reassuring
(signed, zero CVEs, license OK, nothing to flag), then a lit "seen in
context" half where three lenses each carry real evidence — a version
timeline with the fresh release flagged, a capability profile with network
and shell live, a relationship graph sharing one host — and their currents
converge on a Malicious verdict. The two halves are separated by tone rather
than a rule, so the eye travels from a dead readout into a live one.
The left column drops the gradient divider bar under the heading and becomes
a quiet accent-bar legend, since the panel opposite now carries the density.
Below sm the identity row stacks (no truncated package name), the connectors
are dropped and the verdict reads as an icon-led status banner.
Co-Authored-By: Claude Opus 5
Claude-Session: https://claude.ai/code/session_0198mhURhZhARAmAHoDNoCmQ
---
.../sections/tricorder/TricorderContext.tsx | 723 +++++++++---------
docs/web/WEB-PAGES.md | 2 +-
2 files changed, 378 insertions(+), 347 deletions(-)
diff --git a/apps/web/src/components/sections/tricorder/TricorderContext.tsx b/apps/web/src/components/sections/tricorder/TricorderContext.tsx
index ed920967..fe5fd673 100644
--- a/apps/web/src/components/sections/tricorder/TricorderContext.tsx
+++ b/apps/web/src/components/sections/tricorder/TricorderContext.tsx
@@ -1,401 +1,432 @@
import { Container, Section } from "@/components/layout";
import { Reveal, RevealItem, RevealStagger } from "@/components/ui/Reveal";
-import { ScaleToFit } from "@/components/ui/ScaleToFit";
-import { GlassIcon } from "@/components/sections/_shared/GlassIcon";
-import { SIGNAL } from "./tricorder-palette";
+import { SIGNAL, VERDICT } from "./tricorder-palette";
/**
- * "Software Doesn't Exist in Isolation." — a component is understood through
- * three lenses (history, behavior, relationships). Copy on the left; on the
- * right a coded scene: the three lenses as glass tiles converging on the
- * component, which resolves downward into a verdict. Beams carry travelling
- * packets (the Clean Libraries `cs-lep-*` keyframes) and the whole scene is
- * laid out on a fixed design canvas scaled to fit, like LibrariesPipeline.
- * Dark section.
+ * "Software Doesn't Exist in Isolation." — the section makes the argument
+ * instead of diagramming it. One panel holds the same package twice: at the
+ * top, seen alone, where every isolated fact is reassuring; below, seen through
+ * the three lenses, where the same package is plainly malicious. The two halves
+ * are separated by tone, not by a rule — the isolation half is desaturated and
+ * flat, the context half carries the brand light — so the eye travels from a
+ * dead readout into a live one.
+ *
+ * Each lens shows real evidence rather than an icon: a version timeline, a
+ * capability profile, and a relationship graph. Their three currents converge
+ * into the verdict bar at the foot of the panel.
+ *
+ * Everything is CSS/SVG. The only motion is the reused `cs-lep-beam-v` pulse on
+ * the three converging currents, already disabled under prefers-reduced-motion.
*/
-type LensKey = "history" | "behavior" | "relationships";
+const MONO = "var(--font-mono), ui-monospace, Menlo, Consolas, monospace";
interface Lens {
- key: LensKey;
+ key: "history" | "behavior" | "relationships";
+ index: string;
title: string;
detail: string;
+ /** The single damning fact this lens contributes, shown under its evidence. */
+ finding: string;
accent: string;
- /** Facts shown inside the tile on the scene. */
- facts: readonly [string, string, string];
}
const LENSES: Lens[] = [
{
key: "history",
+ index: "01",
title: "History",
detail: "Versions, changes, vulnerabilities.",
+ finding: "New maintainer, 3 days ago",
accent: SIGNAL.history,
- facts: ["2.4.1 → 2.5.0", "maintainer changed", "0 CVEs on record"],
},
{
key: "behavior",
+ index: "02",
title: "Behavior",
detail: "Capabilities, purpose, reachability.",
+ finding: "Network + shell at install",
accent: SIGNAL.behavior,
- facts: ["network · shell", "file system", "reachable at runtime"],
},
{
key: "relationships",
+ index: "03",
title: "Relationships",
detail: "Dependencies, maintainers, infrastructure.",
+ finding: "Shared host with 2 flagged packages",
accent: SIGNAL.relationships,
- facts: ["41 dependencies", "1 shared host", "2 linked packages"],
},
];
-const MONO = "var(--font-mono), ui-monospace, Menlo, Consolas, monospace";
+/* ── Evidence: version history ───────────────────────────────────────────── */
+
+const RELEASES = [
+ { version: "2.4.0", y: 14, flagged: false },
+ { version: "2.4.1", y: 46, flagged: false },
+ { version: "2.5.0", y: 78, flagged: true },
+] as const;
+
+function HistoryEvidence({ accent }: { accent: string }): React.ReactElement {
+ return (
+
+
+
+ {RELEASES.map((r) => (
+
+ {r.flagged ? : null}
+
+
+ {r.version}
+
+
+ ))}
+
+ published 12 min ago
+
+
+ );
+}
+
+/* ── Evidence: capability profile ────────────────────────────────────────── */
+
+const CAPABILITIES = [
+ { label: "network", live: true },
+ { label: "shell", live: true },
+ { label: "file system", live: true },
+ { label: "crypto", live: false },
+] as const;
+
+function BehaviorEvidence({ accent }: { accent: string }): React.ReactElement {
+ return (
+
+ );
}
-/** Starts below the core's "Component" caption so the line never crosses the text. */
-const TRUNK = `M ${CORE.cx} ${CORE.cy + CORE.r + 40} L ${CORE.cx} ${VERDICT.cy - 26}`;
-function LensTile({ lens }: { lens: Lens }): React.ReactElement {
+function LensPanel({ lens }: { lens: Lens }): React.ReactElement {
return (
-
-
-
-
+
{lens.title}
-
-
- {lens.facts.map((f) => (
-
-
- {f}
-
- ))}
-
-
- );
-}
-
-/** The component under analysis — a dark orb with a brand-gradient rim and the cube glyph. */
-function ComponentCore(): React.ReactElement {
- const size = CORE.r * 2;
- return (
-
+
+ {/* Three currents converging on the verdict. */}
+
+ {LENSES.map((lens, i) => (
+
+
+
))}
-
-
+
+ {/* Status banner, not a card: the glyph leads the line at every width, so
+ nothing wraps onto an orphan row on a phone. */}
-
- {LENSES.map((l) => (
-
-
-
- ))}
-
-
-
-
-
-
-
-
-
-
- );
-}
-
-/** Below md: tiles stack, feed the core through a vertical beam, and resolve into the verdict. */
-function SceneMobile(): React.ReactElement {
- return (
-
- {LENSES.map((l, i) => (
-
-
+ >
+
+
+
+
+
+
+
+
+ Malicious
+
+
+ no single lens proves it · all three together do
+
+ {/* Quiet on purpose: the panel opposite carries the density, so the
+ legend is an accent bar and two lines, not a third stack of cards. */}
+
+ {LENSES.map((lens) => (
+
+
+
+
+
+ {lens.title}
+
+
+ {lens.index}
+
+
- {l.detail}
+ {lens.detail}
@@ -475,9 +506,9 @@ export function TricorderContext(): React.ReactElement {
diff --git a/docs/web/WEB-PAGES.md b/docs/web/WEB-PAGES.md
index ee36eac2..5909094b 100644
--- a/docs/web/WEB-PAGES.md
+++ b/docs/web/WEB-PAGES.md
@@ -86,7 +86,7 @@ page slugs, categories, types, and build status across the dev journey.
| 8 | CleanStart Images | `/cleanstart-images` | Static | ✅ | All 5 sections built (Hero, Browse, EasyStart, UVP, Environment) |
| 8b | CleanStart Platform | `/cleanstart-platform` | Static | ❌ removed | **Deleted 2026-09-02** — route, `cleanstart-platform` section components and image assets removed. The page was never finished: it shipped `noindex`, absent from `nav-config.ts` and de-listed from the sitemap, so nothing was de-ranked and no redirect was seeded. It did resolve publicly and was advertised in `public/llms.txt` (entry removed), so register a 301 in the CMS `redirects` collection if the bare URL is still being hit. `cta-cube-textured.webp` moved to `public/images/teams/` — the Teams CTA was the only other consumer. Recover the whole page from git history (last built state: commit before this deletion) when it is rebuilt. |
| 8c | Clean Libraries | `/clean-libraries` | Static | ✅ | Built 2026-06-17 from Figma 1512:988. 4 sections (Hero, Dependency-Risk cards, Invisible-Pipeline diagram, Built-Into-Workflow cards) + Govern-Every-Dependency CTA. Linked from Products nav (`folder` icon) and from the Pricing "Clean Libraries" offering. |
-| 8d | Tricorder | `/tricorder` | Static | ✅ | Built 2026-09-16 from the "The Intelligence Layer" copy doc (no Figma; every scene is drawn in SVG/CSS on the site's tokens). Sections: `TricorderHero` (scan-console artifact on a radar sweep) → `TricorderThreatGap` ("Not Every Threat Has a CVE", three evidence cards: version timeline / behaviour diff / relationship graph) → `TricorderContext` ("Software Doesn't Exist in Isolation", three lenses converging on the component and resolving to a verdict) → `TricorderPipeline` (Analyze → Compare → Correlate → Enrich → Verdict terminal) → `TricorderSubstrate` (the three products on glass pedestals with the shared hexagonal product art from `cleanstart-factory/`, currents dropping into a perspective "intelligence layer" floor that carries the Tricorder emblem and the four stages; anchor `#one-intelligence-layer`) + `TricorderCTA` in the footer slot ("Talk to an Expert" → `/contact-us`). Emits BreadcrumbList + SoftwareApplication. Indexable and in `STATIC_ROUTES`; linked from Products nav (`lens` glyph), the footer Product column and `llms.txt`. **Add a `pageRegistry` row for `/tricorder` in the CMS** so the page emits a WebPage node. |
+| 8d | Tricorder | `/tricorder` | Static | ✅ | Built 2026-09-16 from the "The Intelligence Layer" copy doc (no Figma; every scene is drawn in SVG/CSS on the site's tokens). Sections: `TricorderHero` (scan-console artifact on a radar sweep) → `TricorderThreatGap` ("Not Every Threat Has a CVE", three evidence cards: version timeline / behaviour diff / relationship graph) → `TricorderContext` ("Software Doesn't Exist in Isolation" — one panel showing the same package twice: an inert, desaturated "seen in isolation" readout where every fact is reassuring, then a lit "seen in context" half whose three lenses each carry real evidence (version timeline / capability profile / relationship graph) and converge on a Malicious verdict) → `TricorderPipeline` (Analyze → Compare → Correlate → Enrich → Verdict terminal) → `TricorderSubstrate` (the three products on glass pedestals with the shared hexagonal product art from `cleanstart-factory/`, currents dropping into a perspective "intelligence layer" floor that carries the Tricorder emblem and the four stages; anchor `#one-intelligence-layer`) + `TricorderCTA` in the footer slot ("Talk to an Expert" → `/contact-us`). Emits BreadcrumbList + SoftwareApplication. Indexable and in `STATIC_ROUTES`; linked from Products nav (`lens` glyph), the footer Product column and `llms.txt`. **Add a `pageRegistry` row for `/tricorder` in the CMS** so the page emits a WebPage node. |
---
From 65ceb6a87c4ba158e43a59731d9d64d34723a397 Mon Sep 17 00:00:00 2001
From: Claude
Date: Wed, 16 Sep 2026 08:20:21 +0000
Subject: [PATCH 04/26] fix(web): restore the Tricorder page's copy fidelity to
the source doc
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
An audit diffing every rendered text run against the copy doc turned up
four gaps, all now closed:
- The doc sets the pipeline out as `Analyze → Compare → Correlate → Enrich
→ Verdict`. Only the last hop carried its arrow; every hop carries one
now, and the arrow columns collapse below lg where the cards stack.
- The hero console labelled two of its rows "Correlation" and "Enrichment",
near-misses of the doc's own "Correlate" and "Enrich". All four rows now
use the doc's stage names and the pipeline's accent colours, so the hero
shows the same sequence the page explains further down.
- The hero carried a second CTA, "How It Powers CleanStart", that appears
nowhere in the doc. Removed; "Talk to an Expert" is the doc's only call
to action and now the page's only one. The substrate anchor stays.
- The rail keyframes were injected through a
+
+
+
+
+
+
+
+ Patching More Doesn’t Mean{" "}
+
+ Being More Secure
+
+
+
+
+
+
+ Traditional remediation starts after vulnerable software enters your environment. By
+ then, the risk is already inherited.
+
+
+
+ {/* The ring, matching the doc's reference composition, at every
+ viewport. Canvas is sized (see CANVAS_W/CANVAS_H above) to fully
+ contain the labels hanging off the ring, not just the ring
+ itself, and a container-query scale brings the whole thing down
+ uniformly as the viewport narrows — same mechanism at 375px as
+ at 1920px, just a smaller scale factor. */}
+
+
+
+
+ {/* Brand cyan→violet gradient — same pair as the "Being More
+ Secure" heading above — spanning the full canvas corner to
+ corner so all five arc segments read as one continuous
+ gradient rather than five short repeats of 0%→100%. */}
+
+
+
+
+
+
+
+
+ {ARCS.map((arc, i) => (
+
+ ))}
+
+
+ {/* Pulse tracing the loop — decorative, reinforces "this repeats". */}
+
+
+
-
- {/* Grid: 1 column on mobile (no beam), 4 columns from md+ with a scaled beam between icon and card. */}
-
- {FEATURES.map((feat) => (
- // Mobile: horizontal row, icon left + card right. From md+: vertical column, icon above card with a beam between.
-
- {/* eslint-disable-next-line @next/next/no-img-element */}
-
-
- {/* Horizontal beam — mobile only — glass rod plus a flare PNG rotated 90° so the highlight runs along the rod. */}
-
-
- {/*
- * Card uses a gradient-border wrapper: the outer div holds the
- * gradient as its background and the inner div clips the content
- * with a 22px radius, so the card's own glow shines through the
- * border edge.
- */}
-
-
- {/* Top flare positioned above the card edge; overflow:hidden clips it to the lower glowing arc, and color-dodge brightens the purple base into a white/teal highlight. */}
-
-
-
- {feat.title}
-
-
-
- {feat.body}
-
-
-
-
- ))}
-
-
-
- );
-}
diff --git a/apps/web/src/components/sections/vulnerability-remediation/VulnVerificationTrust.tsx b/apps/web/src/components/sections/vulnerability-remediation/VulnVerificationTrust.tsx
new file mode 100644
index 00000000..fee6cd16
--- /dev/null
+++ b/apps/web/src/components/sections/vulnerability-remediation/VulnVerificationTrust.tsx
@@ -0,0 +1,229 @@
+import Image from "next/image";
+import type { ReactElement } from "react";
+import { Reveal, RevealItem, RevealStagger } from "@/components/ui/Reveal";
+
+/*
+ * "Verification You Can Trust": the six build attributes drawn as one chain of
+ * custody, source to signature, rather than a grid of equal cards. It answers
+ * the Lifecycle section above: that one is a red loop that never ends, this
+ * one is a straight verified line with a pulse travelling along it.
+ *
+ * Icons are the site's violet 3D family (compare + ASR sets), not line glyphs.
+ * Desktop runs the chain horizontally across six columns; below `lg` it turns
+ * into a vertical timeline with the rail on the left. The travelling pulse is
+ * transform-only and stops under reduced motion (see `cs-vr-rail-*` in
+ * globals.css).
+ */
+
+interface Attribute {
+ title: string;
+ body: string;
+ icon: string;
+}
+
+const ATTRIBUTES: readonly Attribute[] = [
+ {
+ title: "Source Built",
+ body: "Built from source to improve transparency and reduce inherited risk.",
+ icon: "/images/compare/icon-origin.webp",
+ },
+ {
+ title: "Hermetic Builds",
+ body: "Isolated builds ensure consistency, security, and repeatability.",
+ icon: "/images/attack-surface-reduction/approach-icon-secure.webp",
+ },
+ {
+ title: "Verified Provenance",
+ body: "Trace artifact origins, inputs, and build history with confidence.",
+ icon: "/images/compare/icon-provenance.webp",
+ },
+ {
+ title: "SBOM Visibility",
+ body: "Understand components, dependencies, and software relationships.",
+ icon: "/images/compare/icon-sbom.webp",
+ },
+ {
+ title: "Signed Artifacts",
+ body: "Verify integrity with cryptographic signatures before deployment.",
+ icon: "/images/compare/icon-signed-artifact.webp",
+ },
+ {
+ title: "Security Policies",
+ body: "Enforce controls for secure and compliant software delivery.",
+ icon: "/images/compare/icon-stig.webp",
+ },
+];
+
+const RAIL_GRADIENT = "linear-gradient(90deg, #9A51FF 0%, #6A7BFF 50%, #2CC1EB 100%)";
+const RAIL_GRADIENT_Y = "linear-gradient(180deg, #9A51FF 0%, #6A7BFF 50%, #2CC1EB 100%)";
+
+function Rail(): ReactElement {
+ return (
+ <>
+ {/* Horizontal rail through the disc centres, first column to last. */}
+
+
+
+
+
+
+
+ {/* Vertical rail below lg, through the 80px discs' centre line. */}
+
+ );
+}
+
+export function VulnVerificationTrust(): ReactElement {
+ return (
+
+ {/* Last section before } />, whose CTA card
+ overlaps upward by half its height. Footer.tsx's contract: reserve
+ `--spacing-section-cta` of bottom padding so the card lands on this
+ background, not on the last step. */}
+
+
+
+
+ Verification You Can Trust
+
+
+
+
+
+ Every layer of the build is verifiable, from source to signature.
+
+
+
+
+
+
+
+ {ATTRIBUTES.map((attr) => (
+
+
+
+ ))}
+
+
+
+
+ );
+}
diff --git a/apps/web/src/components/sections/vulnerability-remediation/VulnWhyEliminate.tsx b/apps/web/src/components/sections/vulnerability-remediation/VulnWhyEliminate.tsx
deleted file mode 100644
index 81bf45a2..00000000
--- a/apps/web/src/components/sections/vulnerability-remediation/VulnWhyEliminate.tsx
+++ /dev/null
@@ -1,298 +0,0 @@
-import Image from "next/image";
-import type { ReactElement } from "react";
-import { Reveal } from "@/components/ui/Reveal";
-
-/*
- * "Why Traditional Remediation Breaks Down" — diagnostic ledger.
- *
- * A vertical list of the four pain points of traditional remediation, threaded
- * onto a single gradient "spine" with a glowing node per row. The spine reads as
- * one system under stress (on-message for a problem section) and a soft scan
- * travels down it. No index numbers, no badges — spine node · glass icon chip ·
- * title + description. Hover/scan/reveal styling lives in globals.css under the
- * `cs-vuln-*` classes (with prefers-reduced-motion guards).
- */
-
-type Row = {
- title: string;
- body: string;
- Icon: () => ReactElement;
-};
-
-function IconRefresh(): ReactElement {
- return (
-
-
-
- );
-}
-
-function IconBell(): ReactElement {
- return (
-
-
-
- );
-}
-
-function IconStack(): ReactElement {
- return (
-
-
-
- );
-}
-
-function IconLink(): ReactElement {
- return (
-
-
-
- );
-}
-
-const ROWS = [
- { title: "Endless Patching", body: "Constant remediation slows delivery.", Icon: IconRefresh },
- { title: "Scanner Noise", body: "Too many alerts. Too little context.", Icon: IconBell },
- {
- title: "Backlog Overload",
- body: "Security queues grow faster than teams can respond.",
- Icon: IconStack,
- },
- {
- title: "Inherited Risk",
- body: "Most vulnerabilities arrive through dependencies.",
- Icon: IconLink,
- },
-] as const satisfies readonly Row[];
-
-const HAIRLINE =
- "linear-gradient(to right, transparent 0%, #d9d9d9 20%, #d9d9d9 80%, transparent 100%)";
-
-export function VulnWhyEliminate(): ReactElement {
- return (
-
- {/* Soft corner glows — kept from the prior design, subtle + on-brand. */}
-
-
-
-
-
-
- Why Traditional Remediation{" "}
-
- Breaks Down
-
-
+ Every extra component expands what you need to secure.
+
+
{/*
diff --git a/apps/web/src/components/sections/attack-surface-reduction/ASRCTA.tsx b/apps/web/src/components/sections/attack-surface-reduction/ASRCTA.tsx
index d17cead0..5f198388 100644
--- a/apps/web/src/components/sections/attack-surface-reduction/ASRCTA.tsx
+++ b/apps/web/src/components/sections/attack-surface-reduction/ASRCTA.tsx
@@ -131,7 +131,7 @@ export function ASRCTA(): React.ReactElement {
textWrap: "balance",
}}
>
- What’s Actually Inside Your Software?
+ Ready to Reduce Your Attack Surface?
@@ -148,12 +148,11 @@ export function ASRCTA(): React.ReactElement {
lineHeight: "var(--cta-card-desc-lh)",
}}
>
- Learn how leading teams reduce attack surface by removing unnecessary software
- components before deployment.
+ Build with fewer components and reduce the software you need to secure.
- Download the Guide
+ Talk to an Expert
- Reduce attack surface by eliminating unnecessary software components before they
- reach production.
+ Reduce your attack surface with a smaller software footprint.
@@ -113,7 +112,7 @@ export function ASRHero(): React.ReactElement {
{/* Inline style overrides are required because cs-btn-glass is
unlayered CSS and beats @layer utilities (Tailwind). */}
- Explore CleanStart Images
+ See How It Works
diff --git a/apps/web/src/components/sections/attack-surface-reduction/ASRModern.tsx b/apps/web/src/components/sections/attack-surface-reduction/ASRModern.tsx
index 810c1b45..561f14c4 100644
--- a/apps/web/src/components/sections/attack-surface-reduction/ASRModern.tsx
+++ b/apps/web/src/components/sections/attack-surface-reduction/ASRModern.tsx
@@ -3,18 +3,18 @@ import { Reveal } from '@/components/ui/Reveal';
const TARGETS = [
{
- title: 'Kubernetes Platforms',
- desc: 'Secure container foundations.',
+ title: 'Minimal Images',
+ desc: 'Fewer packages. Smaller footprint.',
icon: '/images/attack-surface-reduction/modern-icon-k8s.svg',
},
{
- title: 'Regulated Environments',
- desc: 'Built for compliance-heavy workloads.',
+ title: 'Distroless Runtime',
+ desc: 'Only what applications need.',
icon: '/images/attack-surface-reduction/modern-icon-regulated.svg',
},
{
- title: 'Security-Focused Teams',
- desc: 'Reduce software supply chain risk.',
+ title: 'Hardened Configurations',
+ desc: 'Secure defaults from the start.',
icon: '/images/attack-surface-reduction/modern-icon-security.svg',
},
] as const;
@@ -130,8 +130,8 @@ export function ASRModern(): React.ReactElement {
marginBottom: 'clamp(32px, 5vw, 64px)',
}}
>
- Built for Modern Production{' '}
- Environments
+ Built to{' '}
+ Reduce Attack Surface
@@ -199,7 +199,7 @@ export function ASRModern(): React.ReactElement {
margin: 0,
}}
>
- {'Built for Modern Production '}
+ {'Built to '}
- Environments
+ Reduce Attack Surface