asdf
This commit is contained in:
1 parent
3cb5952e6d
commit
ab6495ad10
9 files changed
+63
-72
No files matched your search
Binary file not shown.
|
After Width: | Height: | Size: 263 KiB |
@@ -7,7 +7,6 @@ const { lang, tone = 'paper' } = Astro.props;
|
||||
const ui = t(lang);
|
||||
const home = localePath(lang, '/');
|
||||
const items = [
|
||||
{ href: `${home}#capabilities`, label: ui.nav.capabilities },
|
||||
{ href: localePath(lang, '/cipherverifier'), label: ui.nav.product },
|
||||
{ href: localePath(lang, '/insights'), label: ui.nav.insights },
|
||||
{ href: localePath(lang, '/team'), label: ui.nav.team },
|
||||
|
||||
@@ -11,28 +11,18 @@ const ui = t(lang);
|
||||
|
||||
/* The partner logos as on ciphercell.de (see PartnerLogo.astro for the two states). `h` balances
|
||||
optical size: the incubator wordmark is long and thin, the other two carry a mark. */
|
||||
const roles = ui.proof.partnerRoles;
|
||||
const roles = ui.partners.roles;
|
||||
const logos = await Promise.all([
|
||||
{ name: 'xG-Incubator', role: roles.incubator, src: incubator, h: 'var(--space-5)' },
|
||||
{ name: 'xG-ALOE', role: roles.aloe, src: aloe, h: 'var(--space-7)' },
|
||||
{ name: lang === 'de' ? 'Universität Passau' : 'University of Passau', role: roles.passau, src: passau, h: 'var(--space-7)' },
|
||||
].map(async (l) => ({ ...l, url: (await getImage({ src: l.src, format: 'webp' })).src })));
|
||||
---
|
||||
<section class="section proof" aria-labelledby="proof-title">
|
||||
<section class="section section--tight partners" aria-labelledby="partners-title">
|
||||
<div class="container grid">
|
||||
<h2 id="proof-title" class="h2 proof__title">{ui.proof.title}</h2>
|
||||
<ul class="proof__list" role="list">
|
||||
{ui.proof.items.map((p) => (
|
||||
<li class="proof__item">
|
||||
<p class="proof__name">{p.name}</p>
|
||||
<p class="muted proof__body">{p.body}</p>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
<div class="partners">
|
||||
<p class="small muted" id="partners-label">{ui.proof.partners}</p>
|
||||
<ul class="partners__list" role="list" aria-labelledby="partners-label">
|
||||
<div class="partners__block">
|
||||
<h2 class="small muted partners__title" id="partners-title">{ui.partners.title}</h2>
|
||||
<ul class="partners__list" role="list">
|
||||
{logos.map((l) => (
|
||||
<li class="partners__item" data-partner>
|
||||
<PartnerLogo name={l.name} src={l.src} url={l.url} h={l.h} />
|
||||
@@ -48,18 +38,8 @@ const logos = await Promise.all([
|
||||
</section>
|
||||
|
||||
<style>
|
||||
.proof { border-top: var(--hairline) solid var(--line); }
|
||||
.proof__title { grid-column: 1 / -1; }
|
||||
.proof__list { grid-column: 1 / -1; }
|
||||
.proof__item {
|
||||
display: grid;
|
||||
gap: var(--space-2);
|
||||
padding-block: var(--space-5);
|
||||
border-top: var(--hairline) solid var(--line);
|
||||
}
|
||||
.proof__item:last-child { border-bottom: var(--hairline) solid var(--line); }
|
||||
.proof__name { font-weight: var(--weight-medium); }
|
||||
.proof__body { max-width: 30em; }
|
||||
.partners__title { font-weight: var(--weight-regular); }
|
||||
|
||||
|
||||
/* Hover answers the pointer with the logo's own ground: night, original colours. */
|
||||
[data-partner] { transition: background-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease); }
|
||||
@@ -68,9 +48,8 @@ const logos = await Promise.all([
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) { [data-partner] { transition: none; } }
|
||||
|
||||
.partners {
|
||||
.partners__block {
|
||||
grid-column: 1 / -1;
|
||||
margin-top: var(--space-9);
|
||||
display: grid;
|
||||
gap: var(--space-5);
|
||||
}
|
||||
@@ -115,9 +94,6 @@ const logos = await Promise.all([
|
||||
}
|
||||
|
||||
@media (min-width: 48rem) {
|
||||
.proof__title { grid-column: 1 / span 6; }
|
||||
.proof__list { grid-column: 7 / span 6; }
|
||||
.proof__item { grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr); column-gap: var(--gap); }
|
||||
.partners__list { grid-template-columns: repeat(3, minmax(0, 1fr)); }
|
||||
.partners__item + .partners__item { border-top: 0; border-left: var(--hairline) solid var(--line); }
|
||||
}
|
||||
@@ -223,7 +223,7 @@ const quoted = (text: string) => (lang === 'de' ? `„${text}“` : `“${text}
|
||||
<script>
|
||||
/**
|
||||
* Rotates the founders' statements. Plays on its own only without reduced motion; holds while
|
||||
* the pointer or focus is inside or the section is off screen; picking a statement stops it.
|
||||
* keyboard focus is inside or the section is off screen; picking a statement stops it.
|
||||
*/
|
||||
const root = document.querySelector<HTMLElement>('[data-quotes]');
|
||||
if (root) {
|
||||
@@ -259,10 +259,8 @@ const quoted = (text: string) => (lang === 'de' ? `„${text}“` : `“${text}
|
||||
picks.forEach((pick, i) => pick.addEventListener('click', () => { setPlaying(false); show(i); }));
|
||||
|
||||
const hold = (on: boolean) => root.toggleAttribute('data-held', on);
|
||||
root.addEventListener('pointerenter', () => hold(true));
|
||||
root.addEventListener('pointerleave', () => hold(root.contains(document.activeElement)));
|
||||
root.addEventListener('focusin', () => hold(true));
|
||||
root.addEventListener('focusout', (e) => { if (!root.contains(e.relatedTarget as Node)) hold(root.matches(':hover')); });
|
||||
root.addEventListener('focusout', (e) => { if (!root.contains(e.relatedTarget as Node)) hold(false); });
|
||||
|
||||
// Off screen, the clock stops, so a reader arriving from above starts on a fresh statement.
|
||||
new IntersectionObserver(([entry]) => root.toggleAttribute('data-away', !entry.isIntersecting), { threshold: 0.4 }).observe(root);
|
||||
|
||||
@@ -3,13 +3,16 @@ import { Picture } from 'astro:assets';
|
||||
import fiveG from '../../assets/images/sector-5g.jpg';
|
||||
import substation from '../../assets/images/sector-substation.jpg';
|
||||
import controlRoom from '../../assets/images/sector-ot.jpg';
|
||||
import products from '../../assets/images/sector-rd-engineer.jpg';
|
||||
import datacentre from '../../assets/images/sector-datacentre.jpg';
|
||||
import { t, type Lang } from '../../i18n/ui';
|
||||
interface Props { lang: Lang }
|
||||
const { lang } = Astro.props;
|
||||
const ui = t(lang);
|
||||
const images = [fiveG, substation, controlRoom, datacentre];
|
||||
const items = ui.sectors.items.map((item, i) => ({ ...item, image: images[i], id: `sector-${i + 1}` }));
|
||||
const images = [fiveG, substation, controlRoom, products, datacentre];
|
||||
/* Where each picture keeps its subject when cropped; the R&D engineer sits on the left. */
|
||||
const focus = ['center', 'center', 'center', '30% center', 'center'];
|
||||
const items = ui.sectors.items.map((item, i) => ({ ...item, image: images[i], focus: focus[i], id: `sector-${i + 1}` }));
|
||||
---
|
||||
<section class="section sectors" id="sectors" aria-labelledby="sectors-title">
|
||||
<div class="container sectors__grid" data-sectors>
|
||||
@@ -42,6 +45,7 @@ const items = ui.sectors.items.map((item, i) => ({ ...item, image: images[i], id
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
class="sectors__img img-doc"
|
||||
style={`object-position: ${item.focus}`}
|
||||
pictureAttributes={{ class: 'sectors__picture' }}
|
||||
/>
|
||||
</div>
|
||||
@@ -62,6 +66,7 @@ const items = ui.sectors.items.map((item, i) => ({ ...item, image: images[i], id
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
class="sectors__img img-doc"
|
||||
style={`object-position: ${item.focus}`}
|
||||
pictureAttributes={{ class: 'sectors__picture sectors__picture--stage', 'data-stage-item': '', 'data-active': i === 0 ? '' : undefined }}
|
||||
/>
|
||||
))}
|
||||
@@ -70,6 +75,7 @@ const items = ui.sectors.items.map((item, i) => ({ ...item, image: images[i], id
|
||||
</section>
|
||||
|
||||
<style>
|
||||
.sectors { border-top: var(--hairline) solid var(--line); }
|
||||
.sectors__grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(12, minmax(0, 1fr));
|
||||
@@ -144,7 +150,16 @@ const items = ui.sectors.items.map((item, i) => ({ ...item, image: images[i], id
|
||||
}
|
||||
:global(html.js) .sectors__panel-inner { min-height: 0; }
|
||||
:global(html.js) .sectors__panel[data-open] { grid-template-rows: 1fr; }
|
||||
:global(html.js) .sectors__panel:not([data-open]) { margin-bottom: calc(var(--space-5) - var(--space-3)); }
|
||||
/* A closed panel is hidden, not only collapsed, so it does not take focus while invisible.
|
||||
Visibility switches at the end of the collapse and at the start of the expand. */
|
||||
:global(html.js) .sectors__panel:not([data-open]) {
|
||||
margin-bottom: calc(var(--space-5) - var(--space-3));
|
||||
visibility: hidden;
|
||||
transition:
|
||||
grid-template-rows var(--dur-slow) var(--ease),
|
||||
margin var(--dur-slow) var(--ease),
|
||||
visibility 0s linear var(--dur-slow);
|
||||
}
|
||||
|
||||
@media (min-width: 64rem) {
|
||||
.sectors__text { grid-column: 1 / span 5; }
|
||||
@@ -152,15 +167,21 @@ const items = ui.sectors.items.map((item, i) => ({ ...item, image: images[i], id
|
||||
half of whatever the window is wider than the container. The section clips, so a classic
|
||||
scrollbar (counted in 100vw) cannot cause a horizontal scroll. */
|
||||
.sectors { overflow-x: clip; }
|
||||
/* The image box is at least 5:4 (never taller than 45rem), and stretches with the row when
|
||||
the list beside it is taller, so its top and bottom always meet the heading and the last rule. */
|
||||
.sectors__media {
|
||||
grid-column: 7 / span 6;
|
||||
display: grid;
|
||||
display: block;
|
||||
position: relative;
|
||||
margin-right: calc(-1 * (max(var(--gutter), var(--safe-right)) + max(0px, (100vw - var(--container)) / 2)));
|
||||
}
|
||||
/* Near square, but never taller than a screenful on very wide windows. */
|
||||
.sectors__media :global(.sectors__img) { aspect-ratio: 5 / 4; max-height: 45rem; }
|
||||
/* The minimum: a spacer as tall as 5:4 of the width, capped at 45rem. A grid item stretches
|
||||
to its row, so a taller list beside it makes the box taller; the pictures fill the box. */
|
||||
.sectors__media::before { content: ''; display: block; padding-top: min(80%, 45rem); }
|
||||
.sectors__media :global(.sectors__img) { height: 100%; aspect-ratio: auto; }
|
||||
.sectors__media :global(.sectors__picture--stage) {
|
||||
grid-area: 1 / 1;
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
opacity: 0;
|
||||
transition: opacity var(--dur-slow) var(--ease);
|
||||
}
|
||||
@@ -170,7 +191,11 @@ const items = ui.sectors.items.map((item, i) => ({ ...item, image: images[i], id
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.sectors__panel, .sectors__tab::before, .sectors__media :global(.sectors__picture--stage) { transition: none; }
|
||||
.sectors__panel,
|
||||
:global(html.js) .sectors__panel,
|
||||
:global(html.js) .sectors__panel:not([data-open]),
|
||||
.sectors__tab::before,
|
||||
.sectors__media :global(.sectors__picture--stage) { transition: none; }
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
+2
-2
@@ -8,7 +8,7 @@ import philipp from '../assets/images/people/philipp.png';
|
||||
import karin from '../assets/images/people/karin.png';
|
||||
import valentin from '../assets/images/people/valentin.png';
|
||||
|
||||
export type RoleKey = 'ceo' | 'cto' | 'engineer' | 'bizdev' | 'studentF' | 'studentM';
|
||||
export type RoleKey = 'ceo' | 'cto' | 'engineer' | 'bizdev' | 'studentF' | 'studentM' | 'networkengineer';
|
||||
|
||||
export interface Person {
|
||||
name: string;
|
||||
@@ -20,7 +20,7 @@ export const team: Person[] = [
|
||||
{ name: 'Dr. Felix Klement', role: 'ceo', portrait: felix },
|
||||
{ name: 'Dr. Florian Frank', role: 'cto', portrait: florian },
|
||||
{ name: 'Alexander Braml', role: 'engineer', portrait: alexander },
|
||||
{ name: 'Erik Foris', role: 'engineer', portrait: erik },
|
||||
{ name: 'Erik Foris', role: 'networkengineer', portrait: erik },
|
||||
{ name: 'Marc Schlotzhauer', role: 'bizdev', portrait: marc },
|
||||
{ name: 'Philipp Mayr', role: 'engineer', portrait: philipp },
|
||||
{ name: 'Karin Kaufmann', role: 'studentF', portrait: karin },
|
||||
|
||||
+12
-22
@@ -39,7 +39,6 @@ const en = {
|
||||
},
|
||||
nav: {
|
||||
skip: 'Skip to content',
|
||||
capabilities: 'Capabilities',
|
||||
product: 'Product',
|
||||
sectors: 'Sectors',
|
||||
insights: 'Insights',
|
||||
@@ -54,7 +53,7 @@ const en = {
|
||||
},
|
||||
manifesto: {
|
||||
lines: ['Security built in,', 'not bolted on.'],
|
||||
p1: 'Infrastructure is software now. Private 5G networks, industrial control systems and cloud platforms are assembled from the code and components of many vendors, and every interface between them is an attack surface that off-the-shelf tools were never designed to cover.',
|
||||
p1: 'Infrastructure is software now. Private 5G networks, industrial control systems, digital products and cloud platforms are assembled from the code and components of many vendors, and every interface between them is an attack surface that off-the-shelf tools were never designed to cover.',
|
||||
p2: 'CipherCell is a security engineering company built by specialists in networks and software. We take on the hard problems, whatever the vertical, and solve them with security at the core of every decision.',
|
||||
cta: 'About CipherCell',
|
||||
},
|
||||
@@ -87,18 +86,13 @@ const en = {
|
||||
{ title: 'Private 5G and campus networks', body: 'Software-defined networks with open interfaces and a multi-vendor supply chain. Our origin, and still the hardest case.', imageAlt: 'A private 5G antenna and radio cabinet on the parapet of a factory roof, cranes in the distance.' },
|
||||
{ title: 'Critical infrastructure', body: 'Control and network software where a breach stops production, damages equipment or endangers people.', imageAlt: 'An electrical substation seen straight from above: transformers, switchgear and a service road.' },
|
||||
{ title: 'Operational technology (OT)', body: 'Controllers, SCADA and the engineering software that configures them. Systems built to run for decades, often on protocols without authentication, and rarely patched.', imageAlt: 'Three operators in hard hats at their consoles in a plant control room, facing a wall-sized diagram of the process.' },
|
||||
{ title: 'Products with digital elements', body: 'Devices, firmware and installable software sold in the EU. Under the Cyber Resilience Act, the manufacturer answers for their security for the whole support period.', imageAlt: 'An engineer at two monitors in a glass-walled development lab, engineering software on screen and a machined test part on the desk.' },
|
||||
{ title: 'Cloud-native systems', body: 'Containers, service meshes and CI/CD pipelines that carry infrastructure workloads. Security that runs as code, alongside yours.', imageAlt: 'A cold aisle between server racks in a data centre, a technician far in the distance.' },
|
||||
],
|
||||
},
|
||||
proof: {
|
||||
title: 'Where we come from.',
|
||||
items: [
|
||||
{ name: 'University of Passau', body: 'Our method grew out of academic security research. The university remains our research partner.' },
|
||||
{ name: 'xG-ALOE alliance and xG-Incubator', body: 'Partners in the alliance for open enterprise networks, from lab to deployment.' },
|
||||
{ name: 'Federal Ministry of Research, Technology and Space', body: 'Our work is publicly funded.' },
|
||||
],
|
||||
partners: 'Trusted by and working with',
|
||||
partnerRoles: { incubator: 'Incubator', aloe: 'Alliance for open enterprise networks', passau: 'Research partner' },
|
||||
partners: {
|
||||
title: 'Trusted by and working with',
|
||||
roles: { incubator: 'Incubator', aloe: 'Alliance for open enterprise networks', passau: 'Research partner' },
|
||||
},
|
||||
quote: {
|
||||
label: 'Statements from the founders',
|
||||
@@ -334,6 +328,7 @@ const en = {
|
||||
ceo: 'Founder and CEO',
|
||||
cto: 'Founder and CTO',
|
||||
engineer: 'Software Engineer',
|
||||
networkengineer: 'Network & Infrastructure Engineer',
|
||||
bizdev: 'Business Development',
|
||||
studentF: 'Mathematical Engineer',
|
||||
studentM: 'Process Optimization & Automation',
|
||||
@@ -385,7 +380,6 @@ const de: typeof en = {
|
||||
},
|
||||
nav: {
|
||||
skip: 'Zum Inhalt springen',
|
||||
capabilities: 'Leistungen',
|
||||
product: 'Produkt',
|
||||
sectors: 'Einsatzfelder',
|
||||
insights: 'Einblicke',
|
||||
@@ -400,7 +394,7 @@ const de: typeof en = {
|
||||
},
|
||||
manifesto: {
|
||||
lines: ['Sicherheit eingebaut,', 'nicht nachträglich angefügt.'],
|
||||
p1: 'Infrastruktur ist heute Software. Private 5G-Netze, industrielle Steuerungssysteme und Cloud-Plattformen setzen sich aus Code und Komponenten vieler Hersteller zusammen und jede Schnittstelle dazwischen ist eine Angriffsfläche, für die Lösungen von der Stange nie gedacht waren.',
|
||||
p1: 'Infrastruktur ist heute Software. Private 5G-Netze, industrielle Steuerungssysteme, digitale Produkte und Cloud-Plattformen setzen sich aus Code und Komponenten vieler Hersteller zusammen und jede Schnittstelle dazwischen ist eine Angriffsfläche, für die Lösungen von der Stange nie gedacht waren.',
|
||||
p2: 'CipherCell ist ein Security-Engineering-Unternehmen von Spezialisten für Netzwerke und Software. Wir packen die schwierigen Probleme an, ganz gleich in welcher Branche, und machen Sicherheit zum Kern jeder Entscheidung.',
|
||||
cta: 'Über CipherCell',
|
||||
},
|
||||
@@ -433,18 +427,13 @@ const de: typeof en = {
|
||||
{ title: 'Private 5G- und Campusnetze', body: 'Softwaredefinierte Netze mit offenen Schnittstellen und einer Lieferkette aus vielen Herstellern. Unser Ursprung, und noch immer der schwerste Fall.', imageAlt: 'Eine private 5G-Antenne mit Funkschrank auf der Attika eines Fabrikdachs, Kräne im Hintergrund.' },
|
||||
{ title: 'Kritische Infrastruktur', body: 'Steuerungs- und Netzsoftware, bei der ein Einbruch die Produktion stoppt, Anlagen beschädigt oder Menschen gefährdet.', imageAlt: 'Ein Umspannwerk senkrecht von oben: Transformatoren, Schaltanlagen und eine Zufahrt.' },
|
||||
{ title: 'Operational Technology (OT)', body: 'Steuerungen, SCADA und die Engineering-Software, mit der sie konfiguriert werden. Anlagen, die Jahrzehnte laufen, oft über Protokolle ohne Authentisierung, und selten Updates erhalten.', imageAlt: 'Drei Bediener mit Schutzhelm an ihren Pulten in der Leitwarte eines Werks, vor einem wandgroßen Schema der Anlage.' },
|
||||
{ title: 'Produkte mit digitalen Elementen', body: 'Geräte, Firmware und installierbare Software, die in der EU verkauft werden. Nach dem Cyber Resilience Act steht der Hersteller über den gesamten Supportzeitraum für ihre Sicherheit ein.', imageAlt: 'Ein Ingenieur an zwei Monitoren in einem verglasten Entwicklungslabor, Engineering-Software auf dem Bildschirm und ein gefrästes Testteil auf dem Tisch.' },
|
||||
{ title: 'Cloud-native Systeme', body: 'Container, Service Meshes und CI/CD-Pipelines, die Infrastruktur tragen. Sicherheit als Code, neben Ihrem.', imageAlt: 'Ein Kaltgang zwischen Serverschränken in einem Rechenzentrum, ein Techniker weit hinten.' },
|
||||
],
|
||||
},
|
||||
proof: {
|
||||
title: 'Woher wir kommen.',
|
||||
items: [
|
||||
{ name: 'Universität Passau', body: 'Unsere Methode ist aus akademischer Sicherheitsforschung entstanden. Die Universität bleibt unser Forschungspartner.' },
|
||||
{ name: 'xG-ALOE-Allianz und xG-Incubator', body: 'Partner in der Allianz für offene Unternehmensnetze, vom Labor bis zum Betrieb.' },
|
||||
{ name: 'Bundesministerium für Forschung, Technologie und Raumfahrt', body: 'Unsere Arbeit wird öffentlich gefördert.' },
|
||||
],
|
||||
partners: 'Vertrauen und Zusammenarbeit',
|
||||
partnerRoles: { incubator: 'Inkubator', aloe: 'Allianz für offene Unternehmensnetze', passau: 'Forschungspartner' },
|
||||
partners: {
|
||||
title: 'Vertrauen und Zusammenarbeit',
|
||||
roles: { incubator: 'Inkubator', aloe: 'Allianz für offene Unternehmensnetze', passau: 'Forschungspartner' },
|
||||
},
|
||||
quote: {
|
||||
label: 'Aussagen der Gründer',
|
||||
@@ -680,6 +669,7 @@ const de: typeof en = {
|
||||
ceo: 'Gründer und CEO',
|
||||
cto: 'Gründer und CTO',
|
||||
engineer: 'Software Engineer',
|
||||
networkengineer: 'Network & Infrastructure Engineer',
|
||||
bizdev: 'Business Development',
|
||||
studentF: 'Mathematical Engineer',
|
||||
studentM: 'Process Optimization & Automation',
|
||||
|
||||
@@ -5,7 +5,7 @@ import Manifesto from '../components/home/Manifesto.astro';
|
||||
import Method from '../components/home/Method.astro';
|
||||
import Stages from '../components/home/Stages.astro';
|
||||
import Sectors from '../components/home/Sectors.astro';
|
||||
import Proof from '../components/home/Proof.astro';
|
||||
import Partners from '../components/home/Partners.astro';
|
||||
import Quote from '../components/home/Quote.astro';
|
||||
import Insights from '../components/home/Insights.astro';
|
||||
import Contact from '../components/home/Contact.astro';
|
||||
@@ -21,7 +21,7 @@ const ui = t(lang);
|
||||
<Method lang={lang} />
|
||||
<Stages lang={lang} />
|
||||
<Sectors lang={lang} />
|
||||
<Proof lang={lang} />
|
||||
<Partners lang={lang} />
|
||||
<Quote lang={lang} />
|
||||
<Insights lang={lang} />
|
||||
<Contact lang={lang} />
|
||||
|
||||
Reference in new issue
Block a user