From 5ece51b56737e2e19e3d0010cf033bebd8f4f5e6 Mon Sep 17 00:00:00 2001 From: Felix Klement Date: Fri, 9 Oct 2026 12:17:32 +0200 Subject: [PATCH] asdf --- src/components/Footer.astro | 2 +- src/components/home/Method.astro | 21 ++- src/components/home/MethodDiagram.astro | 164 ++++++++++++------------ src/i18n/ui.ts | 33 +++-- src/styles/tokens.css | 1 + 5 files changed, 111 insertions(+), 110 deletions(-) diff --git a/src/components/Footer.astro b/src/components/Footer.astro index 76ecad9..9207fbf 100644 --- a/src/components/Footer.astro +++ b/src/components/Footer.astro @@ -15,7 +15,7 @@ const funding = lang === 'de' ? fundingDe : fundingEn; const columns = [ { title: ui.footer.capabilities, - links: ui.stages.items.map((item, i) => ({ href: `${home}#capability-${i + 1}`, label: item.title })), + links: ui.method.stages.map((s) => ({ href: `${home}#capability-${s.card}`, label: s.title })), }, { title: ui.footer.company, diff --git a/src/components/home/Method.astro b/src/components/home/Method.astro index 91171e3..1f5641e 100644 --- a/src/components/home/Method.astro +++ b/src/components/home/Method.astro @@ -2,7 +2,7 @@ import { Picture } from 'astro:assets'; import MethodDiagram from './MethodDiagram.astro'; import monitoringRoom from '../../assets/images/monitoring-room-glass.jpg'; -import { localePath, t, type Lang } from '../../i18n/ui'; +import { t, type Lang } from '../../i18n/ui'; interface Props { lang: Lang } const { lang } = Astro.props; const ui = t(lang); @@ -26,8 +26,7 @@ const ui = t(lang); {ui.method.lines.map((line, i) => 0 }]}>{line})}
-

{ui.method.body}

- {ui.method.cta} +

{ui.method.body.map((line) => {line})}

@@ -51,11 +50,21 @@ const ui = t(lang); align-items: start; } .method__title { grid-column: 1 / -1; } - .method__body { grid-column: 1 / -1; max-width: 30em; display: grid; gap: var(--space-5); justify-items: start; } + .method__body { grid-column: 1 / -1; max-width: 30em; text-wrap: balance; } .method__figure { padding-bottom: var(--section); } - .method__figure :global(.diagram) { max-width: 78rem; margin-inline: auto; } + .method__figure :global(.diagram) { margin-inline: auto; } + .method__figure :global(.diagram--wide) { max-width: 78rem; } @media (min-width: 48rem) { .method__title { grid-column: 1 / span 6; } - .method__body { grid-column: 7 / span 5; padding-top: var(--space-2); } + /* Short text: it sits on the headline's last line rather than hanging from its top, set flush + right against the grid's edge, with a bar in the text colour along its full height. */ + .method__body { + grid-column: 7 / -1; + justify-self: end; + align-self: end; + text-align: right; + padding-right: var(--space-4); + border-right: var(--bar) solid currentColor; + } } diff --git a/src/components/home/MethodDiagram.astro b/src/components/home/MethodDiagram.astro index fdf1699..d30aa50 100644 --- a/src/components/home/MethodDiagram.astro +++ b/src/components/home/MethodDiagram.astro @@ -1,8 +1,11 @@ --- /** - * The method as a closed loop of four stages, drawn as a spec-sheet diagram. - * Main nodes sit on the corners of the loop with their labels inside; - * each stage's instruments fan outwards. Wide and tall compositions of the same drawing. + * The method as a triangle of three pillars, drawn as a spec-sheet diagram. + * Every pillar is joined to the other two; each edge carries arrows both ways, since the pillars feed each other. + * Each pillar's instruments fan outwards. Below 64rem a compact version shows the triangle alone, + * without the instruments, which would scale too small to read. + * + * Hovering a pillar brings its instruments forward: lines, dots and labels take the brighter tone. */ interface Stage { title: string; items: string[] } interface Props { title: string; desc: string; stages: Stage[] } @@ -10,98 +13,77 @@ const { title, desc, stages } = Astro.props; const stroke = 1.25; const ring = 36; +type Point = { x: number; y: number }; -/* Wide composition: 1400 x 560. Nodes on the corners of a loop. */ -const W = { w: 1400, h: 560, x1: 420, x2: 980, y1: 140, y2: 420, sat: 175, dy: 62 }; -const wideNodes = [ - { x: W.x1, y: W.y1, side: -1, up: -1 }, // top-left: threat analysis - { x: W.x2, y: W.y1, side: 1, up: -1 }, // top-right: engineering - { x: W.x2, y: W.y2, side: 1, up: 1 }, // bottom-right: verification - { x: W.x1, y: W.y2, side: -1, up: 1 }, // bottom-left: monitoring -]; - -/* Tall composition: 400 x 780. Nodes stacked on a spine, the loop closes on the left. - The title sits right of the node, its instruments hang below the title. */ -const T = { w: 400, h: 780, x: 60, loopX: 20, y0: 60, step: 200, ring: 28, labelX: 40, satX: 44, satY: 36, dy: 28 }; -const tallNodes = stages.map((_, i) => ({ x: T.x, y: T.y0 + i * T.step })); - -/* Direction tick: a small chevron at the midpoint of an edge, pointing along it. */ -const tick = (x: number, y: number, dx: number, dy: number, s = 7) => { +/* Direction tick: a small chevron near the midpoint of an edge, pointing along it, set `gap` past the midpoint. */ +const tick = (a: Point, b: Point, s = 7, gap = 0) => { + const dx = b.x - a.x, dy = b.y - a.y; const len = Math.hypot(dx, dy) || 1; const ux = dx / len, uy = dy / len; + const x = (a.x + b.x) / 2 + ux * gap, y = (a.y + b.y) / 2 + uy * gap; const px = -uy, py = ux; return `${x - ux * s + px * s},${y - uy * s + py * s} ${x},${y} ${x - ux * s - px * s},${y - uy * s - py * s}`; }; +/* Both ways: a pair of chevrons either side of the midpoint, pointing away from each other. */ +const ticks = (a: Point, b: Point, s = 7, gap = 20) => [tick(a, b, s, gap), tick(b, a, s, gap)]; +const pts = (list: Point[]) => list.map((p) => `${p.x},${p.y}`).join(' '); + +/* Wide composition: 1400 x 560. Security on the apex, engineering bottom right, research bottom left. + Labels sit outside the triangle: above the apex, below the base corners. */ +const W = { w: 1400, h: 560, sat: 160, dy: 62 }; +const wideNodes = [ + { x: 680, y: 110, side: -1, up: -1 }, + { x: 950, y: 440, side: 1, up: 1 }, + { x: 410, y: 440, side: -1, up: 1 }, +]; +const wideEdges = wideNodes.map((n, i) => [n, wideNodes[(i + 1) % wideNodes.length]]); + +/* Compact composition: 600 x 480. The same triangle with the pillar titles only; the labels are set + larger so they still read at phone width. */ +const C = { w: 600, h: 480, label: 22 }; +const compactNodes = [ + { x: 300, y: 80, up: -1 }, + { x: 520, y: 390, up: 1 }, + { x: 80, y: 390, up: 1 }, +]; +const compactEdges = compactNodes.map((n, i) => [n, compactNodes[(i + 1) % compactNodes.length]]); --- {title} {desc} - - - - - - {wideNodes.map((n, i) => ( - - - {stages[i].items.map((_, k) => { - const sx = n.x + n.side * W.sat; - const sy = n.y + (k - 1) * W.dy; - return ; - })} - - ))} - - - {wideNodes.map((n, i) => ( - - - {stages[i].items.map((_, k) => )} - - ))} - - - {wideNodes.map((n, i) => ( - {stages[i].title} - ))} - - - {wideNodes.map((n, i) => stages[i].items.map((item, k) => ( - {item} - )))} + + {wideEdges.flatMap(([a, b]) => ticks(a, b)).map((t) => )} + {wideNodes.map((n, i) => ( + + {stages[i].items.map((item, k) => ( + + + + {item} + + ))} + + + + {stages[i].title} + + ))} - - {title} - {desc} + + {title} + {desc} - - - {tallNodes.slice(0, 3).map((n) => )} - - {tallNodes.map((n, i) => ( - - - {stages[i].items.map((_, k) => )} - - ))} + + {compactEdges.flatMap(([a, b]) => ticks(a, b, 8, 28)).map((t) => )} + {compactNodes.map((n) => )} - {tallNodes.map((n, i) => ( - - - {stages[i].items.map((_, k) => )} - - ))} + {compactNodes.map((n) => )} - - {tallNodes.map((n, i) => {stages[i].title})} - - - {tallNodes.map((n, i) => stages[i].items.map((item, k) => ( - {item} - )))} + + {compactNodes.map((n, i) => {stages[i].title})} @@ -112,14 +94,26 @@ const tick = (x: number, y: number, dx: number, dy: number, s = 7) => { color: var(--ink); font-family: var(--font-sans); } - .diagram__labels { fill: var(--ink); } - .diagram__meta { fill: var(--ink-muted); } - .diagram__branch { stroke: var(--line); } - .diagram__dot { fill: var(--ink-muted); } - .diagram--tall { display: none; } - /* Below 64rem the wide drawing would scale its labels under 11px; the tall one takes over. */ + .diagram__label { fill: var(--ink); } + .diagram__meta { fill: var(--ink-muted); transition: fill var(--dur-slow) var(--ease); } + .diagram__branch { stroke: var(--line); transition: stroke var(--dur-slow) var(--ease); } + .diagram__dot { fill: var(--ink-muted); transition: fill var(--dur-slow) var(--ease); } + .diagram__hit { fill: transparent; pointer-events: all; } + + /* Hovered pillar: its instruments come forward. */ + .diagram__pillar:hover .diagram__branch { stroke: var(--ink-muted); } + .diagram__pillar:hover .diagram__dot, + .diagram__pillar:hover .diagram__meta { fill: var(--ink); } + + @media (prefers-reduced-motion: reduce) { + .diagram__meta, .diagram__branch, .diagram__dot { transition: none; } + } + + .diagram--compact { display: none; } + /* Below 64rem the wide drawing would scale its labels under 11px; the compact one takes over. */ @media (max-width: 63.99rem) { .diagram--wide { display: none; } - .diagram--tall { display: block; max-width: 26rem; } + .diagram--compact { display: block; max-width: 26rem; } } + diff --git a/src/i18n/ui.ts b/src/i18n/ui.ts index 9cd09c6..8888262 100644 --- a/src/i18n/ui.ts +++ b/src/i18n/ui.ts @@ -30,7 +30,7 @@ export function formatDate(date: Date, lang: Lang): string { const en = { meta: { siteName: 'CipherCell', - home: { title: 'CipherCell. Security engineering for software.', description: 'CipherCell is a security engineering company in Passau. Threat analysis, security engineering, verification and monitoring for the software behind private 5G, critical network infrastructure and cloud-native systems.' }, + home: { title: 'CipherCell. Security engineering for software.', description: 'CipherCell is a security engineering company in Passau. Security, engineering and research for the software behind private 5G, critical network infrastructure and cloud-native systems.' }, team: { title: 'Team', description: 'The security researchers, engineers and architects behind CipherCell.' }, verifier: { title: 'CipherVerifier', description: 'CipherVerifier is a self-hosted verification pipeline for software: seventeen static analysers and two SBOM generators, one vocabulary, a two-stage review of every finding, and a ranked list an engineer can act on.' }, insights: { title: 'Insights', description: 'Notes on software security in private 5G, critical networks and the systems that run them.' }, @@ -58,17 +58,16 @@ const en = { cta: 'About CipherCell', }, method: { - lines: ['Four stages.', 'One method.'], - body: 'Threat analysis, engineering, verification and monitoring, applied to your software as one continuous method rather than four separate projects. It runs alongside your development, modular and integrable.', + lines: ['Three pillars.', 'One team.'], + body: ['We analyse, we build, we research.', 'Three disciplines, one expert team, working hand in hand.'], imageAlt: 'An empty monitoring room seen through glass, walls of screens and indicator lights blurred behind the panes.', - cta: 'Explore CipherVerifier', diagramTitle: 'The CipherCell method', - diagramDesc: 'A closed loop of four stages: threat analysis, engineering, verification and monitoring. Each stage carries its own instruments: threat model, attack paths and CI/CD integration; zero trust, key management and secure APIs; independent review, hardening and testing; intrusion detection, security logging and resilience checks. Monitoring feeds back into threat analysis.', + diagramDesc: 'A triangle of three pillars, each joined to the other two: security, engineering and research. Each pillar carries its own instruments: threat modelling, automated threat analysis and security testing; secure architecture, FPGA and hardware design, and embedded and systems development; Open RAN and 6G security, hardware roots of trust, and vehicle and IoT security. Research feeds back into security.', + /* Pillars of the diagram and the footer column. `card` is the What-we-do card the footer links to. */ stages: [ - { title: 'Threat analysis', items: ['Threat model', 'Attack paths', 'CI/CD integration'] }, - { title: 'Engineering', items: ['Zero trust', 'Key management', 'Secure APIs'] }, - { title: 'Verification', items: ['Independent review', 'Hardening', 'Testing'] }, - { title: 'Monitoring', items: ['Intrusion detection', 'Security logging', 'Resilience checks'] }, + { title: 'Security', card: 1, items: ['Threat modelling', 'Automated threat analysis', 'Security testing'] }, + { title: 'Engineering', card: 2, items: ['Secure architecture', 'FPGA and hardware design', 'Embedded and systems development'] }, + { title: 'Research', card: 4, items: ['Open RAN and 6G security', 'Hardware roots of trust', 'Vehicle and IoT security'] }, ], }, stages: { @@ -371,7 +370,7 @@ const en = { const de: typeof en = { meta: { siteName: 'CipherCell', - home: { title: 'CipherCell. Security Engineering für Software.', description: 'CipherCell ist ein Security-Engineering-Unternehmen in Passau. Bedrohungsanalyse, Security Engineering, Verifikation und Monitoring für die Software hinter privaten 5G-Netzen, kritischer Netzinfrastruktur und Cloud-nativen Systemen.' }, + home: { title: 'CipherCell. Security Engineering für Software.', description: 'CipherCell ist ein Security-Engineering-Unternehmen in Passau. Sicherheit, Engineering und Forschung für die Software hinter privaten 5G-Netzen, kritischer Netzinfrastruktur und Cloud-nativen Systemen.' }, team: { title: 'Team', description: 'Die Sicherheitsforschenden, Ingenieurinnen und Architekten hinter CipherCell.' }, verifier: { title: 'CipherVerifier', description: 'CipherVerifier ist eine selbst gehostete Verifikations-Pipeline für Software: siebzehn statische Analysewerkzeuge und zwei SBOM-Generatoren, ein Vokabular, ein zweistufiges Urteil zu jedem Fund und eine Rangliste, die ein Engineer abarbeiten kann.' }, insights: { title: 'Einblicke', description: 'Notizen zur Softwaresicherheit in privaten 5G-Netzen, kritischen Netzen und den Systemen, die sie betreiben.' }, @@ -399,17 +398,15 @@ const de: typeof en = { cta: 'Über CipherCell', }, method: { - lines: ['Vier Stufen.', 'Eine Methode.'], - body: 'Bedrohungsanalyse, Engineering, Verifikation und Monitoring, auf Ihre Software angewandt als eine durchgehende Methode statt vier getrennter Projekte. Sie läuft neben Ihrer Entwicklung, modular und integrierbar.', + lines: ['Drei Säulen.', 'Ein Team.'], + body: ['Wir analysieren, wir entwickeln, wir forschen.', 'Drei Disziplinen, ein Expertenteam, das Hand in Hand arbeitet.'], imageAlt: 'Ein leerer Monitoring-Raum hinter Glas, Bildschirmwände und Statuslichter verschwimmen hinter den Scheiben.', - cta: 'CipherVerifier ansehen', diagramTitle: 'Die CipherCell-Methode', - diagramDesc: 'Ein geschlossener Kreislauf aus vier Stufen: Bedrohungsanalyse, Engineering, Verifikation und Monitoring. Jede Stufe hat ihre Instrumente: Bedrohungsmodell, Angriffspfade und CI/CD-Integration; Zero Trust, Schlüsselmanagement und sichere APIs; unabhängige Prüfung, Härtung und Tests; Angriffserkennung, Security-Logging und Resilienzprüfung. Das Monitoring fließt zurück in die Bedrohungsanalyse.', + diagramDesc: 'Ein Dreieck aus drei Säulen, jede mit den beiden anderen verbunden: Sicherheit, Engineering und Forschung. Jede Säule hat ihre Instrumente: Bedrohungsmodellierung, automatisierte Bedrohungsanalyse und Sicherheitstests; sichere Architektur, FPGA- und Hardware-Design sowie Embedded- und Systementwicklung; Open-RAN- und 6G-Sicherheit, Hardware-Vertrauensanker sowie Fahrzeug- und IoT-Sicherheit. Die Forschung fließt zurück in die Sicherheit.', stages: [ - { title: 'Bedrohungsanalyse', items: ['Bedrohungsmodell', 'Angriffspfade', 'CI/CD-Integration'] }, - { title: 'Engineering', items: ['Zero Trust', 'Schlüsselmanagement', 'Sichere APIs'] }, - { title: 'Verifikation', items: ['Unabhängige Prüfung', 'Härtung', 'Tests'] }, - { title: 'Monitoring', items: ['Angriffserkennung', 'Security-Logging', 'Resilienzprüfung'] }, + { title: 'Sicherheit', card: 1, items: ['Bedrohungsmodellierung', 'Automatisierte Bedrohungsanalyse', 'Sicherheitstests'] }, + { title: 'Engineering', card: 2, items: ['Sichere Architektur', 'FPGA- und Hardware-Design', 'Embedded- und Systementwicklung'] }, + { title: 'Forschung', card: 4, items: ['Open-RAN- und 6G-Sicherheit', 'Hardware-Vertrauensanker', 'Fahrzeug- und IoT-Sicherheit'] }, ], }, stages: { diff --git a/src/styles/tokens.css b/src/styles/tokens.css index e2e498d..edc6e60 100644 --- a/src/styles/tokens.css +++ b/src/styles/tokens.css @@ -118,6 +118,7 @@ /* Shape */ --hairline: 1px; + --bar: 2px; /* a marker rule beside a text block, heavier than a hairline */ --radius: 0; --radius-sm: 2px;