Compare commits

...
2 Commits
Author SHA1 Message Date
Felix Klement 5ece51b567 asdf 2026-10-09 12:17:32 +02:00
Felix Klement bf27642bf6 asdf 2026-10-09 09:28:44 +02:00
11 changed files with 290 additions and 256 deletions

No files matched your search

+4 -1
View File
@@ -187,11 +187,14 @@ photography existed:
| File | Used in | Magnific creation |
|---|---|---|
| `plant-at-dusk.jpg` | Hero | https://www.magnific.com/app/creation/DoQZmiipcl |
| `engineer-glass.jpg` | Method section | https://www.magnific.com/app/creation/3zdScUIREY |
| `monitoring-room-glass.jpg` | Method section | https://www.magnific.com/app/creation/bx34g4D5Y2 |
| `sector-5g.jpg` | Sectors, tab 1 | https://www.magnific.com/app/creation/s7ZQP52l8e |
| `sector-substation.jpg` | Sectors, tab 2 | https://www.magnific.com/app/creation/yi0r27vPW9 |
| `sector-datacentre.jpg` | Sectors, tab 5 | https://www.magnific.com/app/creation/eIHAvetdqL |
`monitoring-room-glass.jpg` was regenerated on 8 October 2026 from the same prompt without a person,
with the original engineer image as the style reference; it replaced `engineer-glass.jpg`.
`sector-ot.jpg` (Sectors, tab 3, operational technology) is a real photograph from the Freepik
stock catalogue via Magnific, "Workers in control room" (free licence, Canon EOS 5D Mark III, 2017),
downloaded on 7 October 2026 and scaled to 2304 px.
+172 -124
View File
@@ -1160,9 +1160,9 @@
}
},
"node_modules/@img/sharp-darwin-arm64": {
"version": "0.35.4",
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.4.tgz",
"integrity": "sha512-Uhfl4V4lhP2nbUVF9+hyH1+luj86f1gUFeo8ALYxFoULoU+G87D43BfeMP8XHsk9boxAnCY/bf2EHwhA7MuGsA==",
"version": "0.35.5",
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.5.tgz",
"integrity": "sha512-QRUlFQ0WxvdWyqqG/WtI3iupfD5rBzmCHXSdPsY91sAtVtTo7Q4cb6zOccZ3gqEqkr0f1As1ehLqmEpDsRf+lg==",
"cpu": [
"arm64"
],
@@ -1178,13 +1178,13 @@
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
"@img/sharp-libvips-darwin-arm64": "1.3.3"
"@img/sharp-libvips-darwin-arm64": "1.3.4"
}
},
"node_modules/@img/sharp-darwin-x64": {
"version": "0.35.4",
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.4.tgz",
"integrity": "sha512-hWniXY3bG5qKpkKrAwPe4y+VTPmf086YQAnkxWh7uA1YrlRouWGa0M0Mxj3ZjnXFkv7/TD1bTy9lGUK26vRvWw==",
"version": "0.35.5",
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.5.tgz",
"integrity": "sha512-+BR255RhDlpygUpOc/Jdt1nT6DQ3XG/ERo5wbcdOf5Q320dKtPCKPLR1LJs9VGXRaMa8l1uUa0tkCNOXiAxZUw==",
"cpu": [
"x64"
],
@@ -1200,20 +1200,20 @@
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
"@img/sharp-libvips-darwin-x64": "1.3.3"
"@img/sharp-libvips-darwin-x64": "1.3.4"
}
},
"node_modules/@img/sharp-freebsd-wasm32": {
"version": "0.35.4",
"resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.4.tgz",
"integrity": "sha512-lIsKw/BU+kjB4eZjxrYrZmwOJYi3Ajrv66iAlBmUPyKc3HpnloevB1g3wxGD9P/5BbQ1brBGl65VRRrCvQDEqA==",
"version": "0.35.5",
"resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.5.tgz",
"integrity": "sha512-Y/z91nEZ4uIBX5X3nfTovjU9lHNKFYbL2lpHCLVNmXQK03VIZvXBBt0KxbPGp2SdGSF+2mQU4e+hQaWOt86iAw==",
"license": "Apache-2.0",
"optional": true,
"os": [
"freebsd"
],
"dependencies": {
"@img/sharp-wasm32": "0.35.4"
"@img/sharp-wasm32": "0.35.5"
},
"engines": {
"node": ">=20.9.0"
@@ -1223,9 +1223,9 @@
}
},
"node_modules/@img/sharp-libvips-darwin-arm64": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.3.tgz",
"integrity": "sha512-suTBPTDGrI9WodccaDdwZItTSaBYASlBk1NSfElSHrUfzu3szG6lvIF58+WiFvnfzuK8ZBFS5zE00PxqxnRiPg==",
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.4.tgz",
"integrity": "sha512-5R89nBYiRdUlSWJxPhO+GVtaXzXSxKnRu/xqMn3KTA3L9EB9Oy/P+Nn2f2vlhPuUdy/Zusb2DarbyTpGCfEDuw==",
"cpu": [
"arm64"
],
@@ -1239,9 +1239,9 @@
}
},
"node_modules/@img/sharp-libvips-darwin-x64": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.3.tgz",
"integrity": "sha512-FVJZ5mITMobmXIz/hPDTw0EintTW5H3WfrxwLqEqjiIihlu+hVRyGrFQ60xl0Lxn7Bt3zdpevPaQi0HEzqz9fw==",
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.4.tgz",
"integrity": "sha512-iR2OKH80yi0U+dUplyh3/xdpFvps6YkCwsXenIJxqxR1v9o+xtKTGbS9H7cps+2Vxjc8B1j96p75NmTGjIhtpQ==",
"cpu": [
"x64"
],
@@ -1255,12 +1255,15 @@
}
},
"node_modules/@img/sharp-libvips-linux-arm": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.3.tgz",
"integrity": "sha512-3rbU4vqXXc3hY/OiXdl52xZvT0F1yEngWfvqudtPJg/KkyiaQw2DRsFrNzpmLvfavbwOq3qXn36GP8obHRULQA==",
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.4.tgz",
"integrity": "sha512-LmRtTsOHuvM2+wlO2Db37dx5MiZhB0FvSunciw48YjdOkZz9KAiRbm8ujeMOA1INqmei5NapFxYEK1D1ZSidmw==",
"cpu": [
"arm"
],
"libc": [
"glibc"
],
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -1271,12 +1274,15 @@
}
},
"node_modules/@img/sharp-libvips-linux-arm64": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.3.tgz",
"integrity": "sha512-0DaL0A6Xu6sQSQFwe4iVCrKWU2cCTItnRsYsCdxAMm9NF6twAA9BKnoqy4hqz4+azQ0JHuA26qiUKsf1XJ/v5A==",
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.4.tgz",
"integrity": "sha512-Y3dgX/6lE2QhQb+Gxy0WZxfg9MEm/JBjamZpS2IklP7xIQoKN4hzAm7KcMVGtaVDt3neE9OKBC7vAfonA/Lr1A==",
"cpu": [
"arm64"
],
"libc": [
"glibc"
],
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -1287,12 +1293,15 @@
}
},
"node_modules/@img/sharp-libvips-linux-ppc64": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.3.tgz",
"integrity": "sha512-cdn1OvUBwsXhbC0zSzJnNzf5MZ/mTrobawDvNXBTxe8VtqKAm0sRuEY2Evzovb/w9JMk4TvRxqt1mekSuJz64w==",
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.4.tgz",
"integrity": "sha512-Le6boB8Tai0Nis+gIxIpKx68UDVVIqdR8Tin5Yf1z2LJJQLDJvCDRqRu+jC2qCoD+eIomonmOwB4smBRxfVpYQ==",
"cpu": [
"ppc64"
],
"libc": [
"glibc"
],
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -1303,12 +1312,15 @@
}
},
"node_modules/@img/sharp-libvips-linux-riscv64": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.3.tgz",
"integrity": "sha512-HjPVx7yKz+0lqdhDlTw1tt90wamBoxhiXpvl1XZpJLiHH4RCJ5yDTqH+VlYPv2fwFs89JFw4c1IexYOcQUi4IQ==",
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.4.tgz",
"integrity": "sha512-aHkkIEHPRdQEegJN20MLmGtxYD9R2wQr3Cwpddnu5+YKMt6Uzax7S9h5gpZTo8wyrGuZSlfQ63OevL5mTyOC7Q==",
"cpu": [
"riscv64"
],
"libc": [
"glibc"
],
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -1319,12 +1331,15 @@
}
},
"node_modules/@img/sharp-libvips-linux-s390x": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.3.tgz",
"integrity": "sha512-neWLh+3yCNThxnfy3c4BbVBeGgt9aftno+XbT56iK28RgeDs3UOFWviLWlUu0bArYVYJaFDK+RRohbicUNCm8Q==",
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.4.tgz",
"integrity": "sha512-ra/mB6MikESDUO7Yg+Mi95bFBb9GsObURuhnOv3OqknjGe9sZrG8tCe9q0xSIGrtLgvgw0gKnFWcK4blSgQOuQ==",
"cpu": [
"s390x"
],
"libc": [
"glibc"
],
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -1335,12 +1350,15 @@
}
},
"node_modules/@img/sharp-libvips-linux-x64": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.3.tgz",
"integrity": "sha512-4vKmvAst9nrowcqquKFAyZJUDolUaIp8uRiN0mWFguJ1IplC9/pitXtlnnlU4aa/eJw3J7i67V+pwUL+wZGdsA==",
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.4.tgz",
"integrity": "sha512-GJ//SSXbnwSDes02umB3nDJLFcQzw8a18V8fyhqr6tV515tOEMdImjjxj1AoafMRz56F3PHgftnj1QEKSU1zkw==",
"cpu": [
"x64"
],
"libc": [
"glibc"
],
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -1351,12 +1369,15 @@
}
},
"node_modules/@img/sharp-libvips-linuxmusl-arm64": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.3.tgz",
"integrity": "sha512-Y9kQaLMuNoB0bPYOOdcZMaseNrFpPodIWWMrx+CZyydf2xn68j9WYc6sWWRrDwNkzCQjKYfc68L7jKjGlHMibw==",
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.4.tgz",
"integrity": "sha512-hvulFwtjUcagsis6BBxHwGFwWoNZjgYmULGVrZcyfNbjA8hKILbRxGg15/7w5HDyXHXUos/j6baAWqnCyQ2DWA==",
"cpu": [
"arm64"
],
"libc": [
"musl"
],
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -1367,12 +1388,15 @@
}
},
"node_modules/@img/sharp-libvips-linuxmusl-x64": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.3.tgz",
"integrity": "sha512-fj8Mv0HHfD1Rr+4I68+3agJynxDWtBFgicTbSOb9Bke6pIwzGcJ+RX/yHjmiEGFMCavY/dxvem7MyNaJF+wDiw==",
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.4.tgz",
"integrity": "sha512-6zXKeE/p39I1AmA3cJG35eyBGNqNddLnUXjhwBnsGjFPWqf5VKkDBEqaEkPDoTEtkxwi2vv8Tcr2mDyP4So7Fg==",
"cpu": [
"x64"
],
"libc": [
"musl"
],
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -1383,12 +1407,15 @@
}
},
"node_modules/@img/sharp-linux-arm": {
"version": "0.35.4",
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.4.tgz",
"integrity": "sha512-7OAS8gI0EReKGVN2HssHlM6umJgxF5VI3xN0p9FA91p/YO+ou5hiNghLdZ5BEHztwaaK5+bLKRf8x/o2L2nk9A==",
"version": "0.35.5",
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.5.tgz",
"integrity": "sha512-LEaXK2WdXVK5ykcw0buWyPMsmLLL2vpHLD6yrNSW+JGEL3BZPA4tpKN6iaMc4AxTTAoaX/sU1rOL51lcIz48ZQ==",
"cpu": [
"arm"
],
"libc": [
"glibc"
],
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -1401,16 +1428,19 @@
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
"@img/sharp-libvips-linux-arm": "1.3.3"
"@img/sharp-libvips-linux-arm": "1.3.4"
}
},
"node_modules/@img/sharp-linux-arm64": {
"version": "0.35.4",
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.4.tgz",
"integrity": "sha512-De4jpEnAU8Hd5oT0j1G3uL4ZvTuipVMn7YC6vPaJhy6/7EwEae0SVAoBrUMYQbkLGDm85taVWwuPc1a44LTzCQ==",
"version": "0.35.5",
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.5.tgz",
"integrity": "sha512-LYVx5JTsOM2CBzmxreh+nl64/3H6Xb09iSLknqH47z2T2DFFxDeFLP5y4dJwe6H7uGQlHPyEEtIqyo3DYsRwdQ==",
"cpu": [
"arm64"
],
"libc": [
"glibc"
],
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -1423,16 +1453,19 @@
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
"@img/sharp-libvips-linux-arm64": "1.3.3"
"@img/sharp-libvips-linux-arm64": "1.3.4"
}
},
"node_modules/@img/sharp-linux-ppc64": {
"version": "0.35.4",
"resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.4.tgz",
"integrity": "sha512-2oYZJeIl4kCcMGk4ouZVjnkCtFrpQFlNEtJ6GbxzhHQchwH0NH/qEb9ykmOl29dqwMq+JhFdZn+1ak2FKhI9fQ==",
"version": "0.35.5",
"resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.5.tgz",
"integrity": "sha512-QVxAAq8evVRI9ia2vqgwrmWucn5Dfv+JdWzj75pD8omHLPSP7f8p20O8jxzjCcuCEQEOtYOZUmX1hkiZ0kdevA==",
"cpu": [
"ppc64"
],
"libc": [
"glibc"
],
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -1445,16 +1478,19 @@
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
"@img/sharp-libvips-linux-ppc64": "1.3.3"
"@img/sharp-libvips-linux-ppc64": "1.3.4"
}
},
"node_modules/@img/sharp-linux-riscv64": {
"version": "0.35.4",
"resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.4.tgz",
"integrity": "sha512-cPbNChoRURAWdebDIHSenxRpgEdy7JkPydSnUxRm9VvKD7m0/xVaR/8Fzlu81pk5nHEvHH87UZUA7cTtwnbJSA==",
"version": "0.35.5",
"resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.5.tgz",
"integrity": "sha512-LtdreXguaavKODPIfzJ4kffx7UNt1omwtK0rch4EBbbSTXPnxWmYSayXdLJw0fJzQ97kHt1gL/yh4tvU+nCyRQ==",
"cpu": [
"riscv64"
],
"libc": [
"glibc"
],
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -1467,16 +1503,19 @@
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
"@img/sharp-libvips-linux-riscv64": "1.3.3"
"@img/sharp-libvips-linux-riscv64": "1.3.4"
}
},
"node_modules/@img/sharp-linux-s390x": {
"version": "0.35.4",
"resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.4.tgz",
"integrity": "sha512-RY0JFY8Fd6RonCBtHz+DvadaPkXDSI1AUn6yWL9TipqkZ1vY8w8evqdgyDFnkm4/K1ve1TvZiaePP5oSd4+WVQ==",
"version": "0.35.5",
"resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.5.tgz",
"integrity": "sha512-UZasTOFiYzotTsGOCu42BfUzP6Tu6Do/947iRm1RsLKvlllxwGcn4RN27LibGWceix4Y+Pmw3jsnTcCQIgWjqA==",
"cpu": [
"s390x"
],
"libc": [
"glibc"
],
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -1489,16 +1528,19 @@
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
"@img/sharp-libvips-linux-s390x": "1.3.3"
"@img/sharp-libvips-linux-s390x": "1.3.4"
}
},
"node_modules/@img/sharp-linux-x64": {
"version": "0.35.4",
"resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.4.tgz",
"integrity": "sha512-9qvvEAuk8k89TfWUoX2htWjbAMX8p+NxCppjpcg5k6xMsjhBQPTsoIh36h9Qde4WRuGpJeYnOjdosDn/cnv+OA==",
"version": "0.35.5",
"resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.5.tgz",
"integrity": "sha512-SxFtLTeJInhAA9Q836kux2vZNeOBQEx658qvbboZScr0wIARym3IcGmW7KpVD5sbVg0Ojy+udFQdayYIZyoNog==",
"cpu": [
"x64"
],
"libc": [
"glibc"
],
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -1511,16 +1553,19 @@
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
"@img/sharp-libvips-linux-x64": "1.3.3"
"@img/sharp-libvips-linux-x64": "1.3.4"
}
},
"node_modules/@img/sharp-linuxmusl-arm64": {
"version": "0.35.4",
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.4.tgz",
"integrity": "sha512-KB5jxpfWQTr0nc3xdHtWChdbifHrBGsd2SM62Eyxrl8afikm+f5qGBU75SJIZBT/S1MC8XyacdlXBMSWq6OURA==",
"version": "0.35.5",
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.5.tgz",
"integrity": "sha512-9HbMclmI1zlNkFRs3z9/eBtDjfD0sGlrX1z6b1qwmiFY5ElDLh4BC0LPBdVp7z1DXFiKlIcznf+ZlsuZzLxQqg==",
"cpu": [
"arm64"
],
"libc": [
"musl"
],
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -1533,16 +1578,19 @@
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
"@img/sharp-libvips-linuxmusl-arm64": "1.3.3"
"@img/sharp-libvips-linuxmusl-arm64": "1.3.4"
}
},
"node_modules/@img/sharp-linuxmusl-x64": {
"version": "0.35.4",
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.4.tgz",
"integrity": "sha512-f+eZJZIQNEEd26RPSW+76chwOf1XtA2Y/O+5ocVyLliHkeih3e+jhLVBdNTd2rS3IbNXK8+ug93Vf5ZXtF5Lxg==",
"version": "0.35.5",
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.5.tgz",
"integrity": "sha512-4KOphqB035HrVdqLZfCgMzzERrQkkzOwRhl4OAkRO1YCldbaFjySXMaK534Mo0V+LndnlJk+sbUyLeU0ULyD1A==",
"cpu": [
"x64"
],
"libc": [
"musl"
],
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -1555,13 +1603,13 @@
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
"@img/sharp-libvips-linuxmusl-x64": "1.3.3"
"@img/sharp-libvips-linuxmusl-x64": "1.3.4"
}
},
"node_modules/@img/sharp-wasm32": {
"version": "0.35.4",
"resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.4.tgz",
"integrity": "sha512-zQnl4Kwp7Q6NHsENtU2T/00Zi+w3AQNwz3+UaTyVBy2FpXrzXzGjndpK61onhZjRtRpQXxCTeqw19bVyXOh7jA==",
"version": "0.35.5",
"resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.5.tgz",
"integrity": "sha512-Ptsga1su4tQx+LLF1ECS9U6nz5kmrXKo6XVbtR48Ke3ZRxxgaWBu7IDtEe1quo8hiupwm6WFqxVlXaSf7IINGQ==",
"license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
"optional": true,
"dependencies": {
@@ -1575,16 +1623,16 @@
}
},
"node_modules/@img/sharp-webcontainers-wasm32": {
"version": "0.35.4",
"resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.4.tgz",
"integrity": "sha512-ESfNkywmCfPNyaZjxooddJQiQ+l/nTpGEOGthxiLnIHXC/CmcBixnfwUleX9mCz9ovrUUvKMap/pm8RYbzfwaA==",
"version": "0.35.5",
"resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.5.tgz",
"integrity": "sha512-hfhF/FmoQyTUkA0bIKFOtw536BQSeBMe6BF6QyWlrPxT754+TFLaZ7sKKTfvvM0yJgKgaYTwnFCIZ/GuDw5SUA==",
"cpu": [
"wasm32"
],
"license": "Apache-2.0",
"optional": true,
"dependencies": {
"@img/sharp-wasm32": "0.35.4"
"@img/sharp-wasm32": "0.35.5"
},
"engines": {
"node": ">=20.9.0"
@@ -1594,9 +1642,9 @@
}
},
"node_modules/@img/sharp-win32-arm64": {
"version": "0.35.4",
"resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.4.tgz",
"integrity": "sha512-iNdlBX9gLVvqe2I3uIJSIKTq6wckP/DYxZtcqxm09x5Gi24DnFBmPAWZmr60ZyYMG0xlzo6goG3670ar+RXvRw==",
"version": "0.35.5",
"resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.5.tgz",
"integrity": "sha512-X4t7g+7ZA5DKblCBEXGjUqqemj4vczING/5viFwAL8h4N3qYeyjwdCvRLHi4EdOUI+2Z7UFlp1VM+p/AuEtm6Q==",
"cpu": [
"arm64"
],
@@ -1613,9 +1661,9 @@
}
},
"node_modules/@img/sharp-win32-ia32": {
"version": "0.35.4",
"resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.4.tgz",
"integrity": "sha512-kqRsbaa5CS6KHlpxnN7WhE6vAAugXyZButpRdvDWetlv6Qv4N9WTcrWzF7tXfB9T7MsoadqdI8hmwLq6UlLvtw==",
"version": "0.35.5",
"resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.5.tgz",
"integrity": "sha512-5Zm82LoBc43nhwNybZlG7Y1KO//Zhsn306fQl29ZOuStHLGTo3BWL83q3cznX0poxSAMuYL1On/BHBxkBeKr6A==",
"cpu": [
"ia32"
],
@@ -1632,9 +1680,9 @@
}
},
"node_modules/@img/sharp-win32-x64": {
"version": "0.35.4",
"resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.4.tgz",
"integrity": "sha512-XtmnYhBcrORsJ4XJngyzr/EWP0hRZLAZRFaApdKuviyqF78+ylxh2y06ZmtULAMOnObJ3ucpN0AcwSWnMowTRg==",
"version": "0.35.5",
"resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.5.tgz",
"integrity": "sha512-x76eH0vEiHlcMQu8Y8IenntaACtddpT6W0wmXtWrnKcnKI7ME5DdgqhAD6SEWOEl1v2zDvkZDhFA9KnURwpfqg==",
"cpu": [
"x64"
],
@@ -4165,9 +4213,9 @@
}
},
"node_modules/sharp": {
"version": "0.35.4",
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.4.tgz",
"integrity": "sha512-n++8XWcj+jCOr2IOl7h8LbKnGBDY4aPbmprMONBNFdn0ImXqpGVv5zliDs0V9HbmbCQLpbuo2ej9rAoOQTvMDA==",
"version": "0.35.5",
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.5.tgz",
"integrity": "sha512-Ywn4OnzGukp7CDMrp08RQ50YKmuwG47brZgIVPTvBaaAfQlRlygrRqSrxdCiL9M+LlzLBiJ68IR1QqvzHyjC7g==",
"license": "Apache-2.0",
"dependencies": {
"@img/colour": "^1.1.0",
@@ -4181,31 +4229,31 @@
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
"@img/sharp-darwin-arm64": "0.35.4",
"@img/sharp-darwin-x64": "0.35.4",
"@img/sharp-freebsd-wasm32": "0.35.4",
"@img/sharp-libvips-darwin-arm64": "1.3.3",
"@img/sharp-libvips-darwin-x64": "1.3.3",
"@img/sharp-libvips-linux-arm": "1.3.3",
"@img/sharp-libvips-linux-arm64": "1.3.3",
"@img/sharp-libvips-linux-ppc64": "1.3.3",
"@img/sharp-libvips-linux-riscv64": "1.3.3",
"@img/sharp-libvips-linux-s390x": "1.3.3",
"@img/sharp-libvips-linux-x64": "1.3.3",
"@img/sharp-libvips-linuxmusl-arm64": "1.3.3",
"@img/sharp-libvips-linuxmusl-x64": "1.3.3",
"@img/sharp-linux-arm": "0.35.4",
"@img/sharp-linux-arm64": "0.35.4",
"@img/sharp-linux-ppc64": "0.35.4",
"@img/sharp-linux-riscv64": "0.35.4",
"@img/sharp-linux-s390x": "0.35.4",
"@img/sharp-linux-x64": "0.35.4",
"@img/sharp-linuxmusl-arm64": "0.35.4",
"@img/sharp-linuxmusl-x64": "0.35.4",
"@img/sharp-webcontainers-wasm32": "0.35.4",
"@img/sharp-win32-arm64": "0.35.4",
"@img/sharp-win32-ia32": "0.35.4",
"@img/sharp-win32-x64": "0.35.4"
"@img/sharp-darwin-arm64": "0.35.5",
"@img/sharp-darwin-x64": "0.35.5",
"@img/sharp-freebsd-wasm32": "0.35.5",
"@img/sharp-libvips-darwin-arm64": "1.3.4",
"@img/sharp-libvips-darwin-x64": "1.3.4",
"@img/sharp-libvips-linux-arm": "1.3.4",
"@img/sharp-libvips-linux-arm64": "1.3.4",
"@img/sharp-libvips-linux-ppc64": "1.3.4",
"@img/sharp-libvips-linux-riscv64": "1.3.4",
"@img/sharp-libvips-linux-s390x": "1.3.4",
"@img/sharp-libvips-linux-x64": "1.3.4",
"@img/sharp-libvips-linuxmusl-arm64": "1.3.4",
"@img/sharp-libvips-linuxmusl-x64": "1.3.4",
"@img/sharp-linux-arm": "0.35.5",
"@img/sharp-linux-arm64": "0.35.5",
"@img/sharp-linux-ppc64": "0.35.5",
"@img/sharp-linux-riscv64": "0.35.5",
"@img/sharp-linux-s390x": "0.35.5",
"@img/sharp-linux-x64": "0.35.5",
"@img/sharp-linuxmusl-arm64": "0.35.5",
"@img/sharp-linuxmusl-x64": "0.35.5",
"@img/sharp-webcontainers-wasm32": "0.35.5",
"@img/sharp-win32-arm64": "0.35.5",
"@img/sharp-win32-ia32": "0.35.5",
"@img/sharp-win32-x64": "0.35.5"
},
"peerDependenciesMeta": {
"@types/node": {
@@ -4259,9 +4307,9 @@
}
},
"node_modules/smol-toml": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.8.0.tgz",
"integrity": "sha512-kCZr2V3ch9i00x8zXRhjUNVcjG9ijES5dDudkXvUVCT5QlJNQWElSJdZqyPemffHoLNUYwOcou0Fy+ojN0uHSQ==",
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.9.0.tgz",
"integrity": "sha512-hpd+HLON7HdZXqYchMM/+LaTTbdK0AU3NngIJ4KVyWbY9bfQqdL9cD+4yf6dUoU2Ap4VsU0JkQi6FxAI1B2mXQ==",
"license": "BSD-3-Clause",
"engines": {
"node": ">= 18"
@@ -4271,9 +4319,9 @@
}
},
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz",
"integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==",
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
Binary file not shown.

Before

Width:  |  Height:  |  Size: 328 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 KiB

+1 -1
View File
@@ -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,
+17 -8
View File
@@ -1,15 +1,15 @@
---
import { Picture } from 'astro:assets';
import MethodDiagram from './MethodDiagram.astro';
import engineer from '../../assets/images/engineer-glass.jpg';
import { localePath, t, type Lang } from '../../i18n/ui';
import monitoringRoom from '../../assets/images/monitoring-room-glass.jpg';
import { t, type Lang } from '../../i18n/ui';
interface Props { lang: Lang }
const { lang } = Astro.props;
const ui = t(lang);
---
<section class="night method" id="capabilities" aria-labelledby="method-title">
<Picture
src={engineer}
src={monitoringRoom}
alt={ui.method.imageAlt}
formats={['avif']}
fallbackFormat="webp"
@@ -26,8 +26,7 @@ const ui = t(lang);
{ui.method.lines.map((line, i) => <span class:list={['line', { 'line--muted': i > 0 }]}>{line}</span>)}
</h2>
<div class="method__body">
<p>{ui.method.body}</p>
<a class="button" href={localePath(lang, '/cipherverifier')}>{ui.method.cta}</a>
<p>{ui.method.body.map((line) => <span class="line">{line}</span>)}</p>
</div>
</div>
@@ -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;
}
}
</style>
+74 -80
View File
@@ -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]]);
---
<svg class="diagram diagram--wide" viewBox={`0 0 ${W.w} ${W.h}`} role="img" aria-labelledby="method-w-title method-w-desc" xmlns="http://www.w3.org/2000/svg">
<title id="method-w-title">{title}</title>
<desc id="method-w-desc">{desc}</desc>
<g fill="none" stroke="currentColor" stroke-width={stroke} stroke-linecap="square" vector-effect="non-scaling-stroke">
<rect x={W.x1} y={W.y1} width={W.x2 - W.x1} height={W.y2 - W.y1} />
<polyline points={tick((W.x1 + W.x2) / 2, W.y1, 1, 0)} />
<polyline points={tick(W.x2, (W.y1 + W.y2) / 2, 0, 1)} />
<polyline points={tick((W.x1 + W.x2) / 2, W.y2, -1, 0)} />
<polyline points={tick(W.x1, (W.y1 + W.y2) / 2, 0, -1)} />
<polygon points={pts(wideNodes)} />
{wideEdges.flatMap(([a, b]) => ticks(a, b)).map((t) => <polyline points={t} />)}
</g>
{wideNodes.map((n, i) => (
<g class="diagram__pillar">
{stages[i].items.map((item, k) => (
<g>
<circle cx={n.x} cy={n.y} r={ring} stroke-dasharray="2 5" />
{stages[i].items.map((_, k) => {
const sx = n.x + n.side * W.sat;
const sy = n.y + (k - 1) * W.dy;
return <line x1={n.x} y1={n.y} x2={sx} y2={sy} class="diagram__branch" />;
})}
<line x1={n.x} y1={n.y} x2={n.x + n.side * W.sat} y2={n.y + (k - 1) * W.dy} stroke-width={stroke} vector-effect="non-scaling-stroke" class="diagram__branch" />
<circle cx={n.x + n.side * W.sat} cy={n.y + (k - 1) * W.dy} r="3" class="diagram__dot" />
<text class="diagram__meta" x={n.x + n.side * (W.sat + 14)} y={n.y + (k - 1) * W.dy + 5} text-anchor={n.side < 0 ? 'end' : 'start'} font-size="15">{item}</text>
</g>
))}
</g>
<g fill="currentColor">
{wideNodes.map((n, i) => (
<g>
<circle cx={n.x} cy={n.y} r="5" />
{stages[i].items.map((_, k) => <circle cx={n.x + n.side * W.sat} cy={n.y + (k - 1) * W.dy} r="3" class="diagram__dot" />)}
<circle cx={n.x} cy={n.y} r={ring} fill="none" stroke="currentColor" stroke-width={stroke} vector-effect="non-scaling-stroke" stroke-linecap="square" stroke-dasharray="2 5" />
<circle cx={n.x} cy={n.y} r={ring + 8} class="diagram__hit" />
<circle cx={n.x} cy={n.y} r="5" fill="currentColor" />
<text class="diagram__label" x={n.x} y={n.up < 0 ? n.y - ring - 16 : n.y + ring + 28} text-anchor="middle" font-size="17" font-weight="500">{stages[i].title}</text>
</g>
))}
</g>
<g class="diagram__labels" font-size="17" font-weight="500">
{wideNodes.map((n, i) => (
<text x={n.x - n.side * (ring + 14)} y={n.y - n.up * 30 + 6} text-anchor={n.side < 0 ? 'start' : 'end'}>{stages[i].title}</text>
))}
</g>
<g class="diagram__meta" font-size="15">
{wideNodes.map((n, i) => stages[i].items.map((item, k) => (
<text x={n.x + n.side * (W.sat + 14)} y={n.y + (k - 1) * W.dy + 5} text-anchor={n.side < 0 ? 'end' : 'start'}>{item}</text>
)))}
</g>
</svg>
<svg class="diagram diagram--tall" viewBox={`0 0 ${T.w} ${T.h}`} role="img" aria-labelledby="method-t-title method-t-desc" xmlns="http://www.w3.org/2000/svg">
<title id="method-t-title">{title}</title>
<desc id="method-t-desc">{desc}</desc>
<svg class="diagram diagram--compact" viewBox={`0 0 ${C.w} ${C.h}`} role="img" aria-labelledby="method-c-title method-c-desc" xmlns="http://www.w3.org/2000/svg">
<title id="method-c-title">{title}</title>
<desc id="method-c-desc">{desc}</desc>
<g fill="none" stroke="currentColor" stroke-width={stroke} stroke-linecap="square" vector-effect="non-scaling-stroke">
<line x1={T.x} y1={tallNodes[0].y} x2={T.x} y2={tallNodes[3].y} />
<polyline points={`${T.x},${tallNodes[3].y} ${T.loopX},${tallNodes[3].y} ${T.loopX},${tallNodes[0].y} ${T.x},${tallNodes[0].y}`} />
{tallNodes.slice(0, 3).map((n) => <polyline points={tick(T.x, n.y + T.step / 2 + 20, 0, 1)} />)}
<polyline points={tick(T.loopX, (tallNodes[0].y + tallNodes[3].y) / 2, 0, -1)} />
{tallNodes.map((n, i) => (
<g>
<circle cx={n.x} cy={n.y} r={T.ring} stroke-dasharray="2 5" />
{stages[i].items.map((_, k) => <line x1={n.x} y1={n.y} x2={n.x + T.satX} y2={n.y + T.satY + k * T.dy} class="diagram__branch" />)}
</g>
))}
<polygon points={pts(compactNodes)} />
{compactEdges.flatMap(([a, b]) => ticks(a, b, 8, 28)).map((t) => <polyline points={t} />)}
{compactNodes.map((n) => <circle cx={n.x} cy={n.y} r={ring} stroke-dasharray="2 5" />)}
</g>
<g fill="currentColor">
{tallNodes.map((n, i) => (
<g>
<circle cx={n.x} cy={n.y} r="5" />
{stages[i].items.map((_, k) => <circle cx={n.x + T.satX} cy={n.y + T.satY + k * T.dy} r="3" class="diagram__dot" />)}
{compactNodes.map((n) => <circle cx={n.x} cy={n.y} r="6" />)}
</g>
))}
</g>
<g class="diagram__labels" font-size="16" font-weight="500">
{tallNodes.map((n, i) => <text x={n.x + T.labelX} y={n.y + 6}>{stages[i].title}</text>)}
</g>
<g class="diagram__meta" font-size="14">
{tallNodes.map((n, i) => stages[i].items.map((item, k) => (
<text x={n.x + T.satX + 12} y={n.y + T.satY + k * T.dy + 5}>{item}</text>
)))}
<g class="diagram__label" font-size={C.label} font-weight="500" text-anchor="middle">
{compactNodes.map((n, i) => <text x={n.x} y={n.up < 0 ? n.y - ring - 14 : n.y + ring + 30}>{stages[i].title}</text>)}
</g>
</svg>
@@ -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; }
}
</style>
+1 -19
View File
@@ -100,7 +100,7 @@ const ridges = Array.from({ length: rows }, (_, r) => {
{s.status.rows.map((r) => (
<div class="readout__cell">
<dt>{r.key}</dt>
<dd>{'live' in r && r.live && <span class="readout__live" aria-hidden="true" />}{r.value}</dd>
<dd>{r.value}</dd>
</div>
))}
</dl>
@@ -236,24 +236,6 @@ const ridges = Array.from({ length: rows }, (_, r) => {
}
.readout__cell + .readout__cell { border-top: var(--hairline) solid var(--line-on-night); }
.readout__cell--head, .readout dt { color: var(--ink-muted); }
.readout dd { display: flex; align-items: center; gap: var(--space-2); }
.readout__live { position: relative; width: 0.5rem; height: 0.5rem; border-radius: 50%; background: var(--signal); flex: none; }
/* The live phase pulses: a ring in the signal tone grows out of the dot and fades. */
.readout__live::after {
content: '';
position: absolute;
inset: 0;
border-radius: 50%;
background: var(--signal);
animation: readout-pulse calc(6 * var(--dur-slow)) var(--ease) infinite;
}
@keyframes readout-pulse {
from { transform: scale(1); opacity: 0.7; }
70%, to { transform: scale(3); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
.readout__live::after { animation: none; opacity: 0; }
}
@media (max-width: 30rem) {
.signup { padding: var(--space-5); }
+19 -22
View File
@@ -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.',
imageAlt: 'A security engineer seen through glass, the reflections of monitoring screens layered across her face.',
cta: 'Explore CipherVerifier',
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.',
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: {
@@ -252,7 +251,7 @@ const en = {
status: {
label: 'System status',
rows: [
{ key: 'Phase', value: 'Closed demonstrator', live: true },
{ key: 'Phase', value: 'Closed demonstrator' },
{ key: 'Next', value: 'Alpha release' },
{ key: 'Focus', value: 'Software security' },
],
@@ -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.',
imageAlt: 'Eine Sicherheitsingenieurin hinter Glas, die Spiegelungen von Monitoring-Bildschirmen liegen über ihrem Gesicht.',
cta: 'CipherVerifier ansehen',
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.',
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: {
@@ -593,7 +590,7 @@ const de: typeof en = {
status: {
label: 'Systemstatus',
rows: [
{ key: 'Phase', value: 'Geschlossener Demonstrator', live: true },
{ key: 'Phase', value: 'Geschlossener Demonstrator' },
{ key: 'Als Nächstes', value: 'Alpha-Release' },
{ key: 'Fokus', value: 'Software sicherheit' },
],
+1 -1
View File
@@ -19,7 +19,7 @@ const ui = t(lang);
<Hero lang={lang} />
<Manifesto lang={lang} />
<Method lang={lang} />
<Stages lang={lang} />
<!-- <Stages lang={lang} /> -->
<Sectors lang={lang} />
<Partners lang={lang} />
<Quote lang={lang} />
+1
View File
@@ -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;