asdf
This commit is contained in:
1 parent
5af2d78328
commit
efc2ad7da4
7 files changed
+54
-68
No files matched your search
@@ -6,7 +6,7 @@ import { localePath, t, type Lang } from '../../i18n/ui';
|
||||
interface Props { lang: Lang }
|
||||
const { lang } = Astro.props;
|
||||
const ui = t(lang);
|
||||
const feed = (await getFeed()).slice(0, 2);
|
||||
const feed = (await getFeed()).slice(0, 3);
|
||||
const m = ui.insights.mention;
|
||||
---
|
||||
<section class="section insights" aria-labelledby="insights-title">
|
||||
@@ -44,13 +44,6 @@ const m = ui.insights.mention;
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
<li class="insights__item">
|
||||
<a class="insights__card insights__card--event" href={ui.insights.event.href} rel="noopener">
|
||||
<Field seed={ui.insights.event.href} class="insights__panel"><span>{ui.insights.event.when}</span></Field>
|
||||
<p class="small muted">{ui.insights.event.category}</p>
|
||||
<h3 class="h3">{ui.insights.event.text}</h3>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
@@ -78,7 +71,7 @@ const m = ui.insights.mention;
|
||||
.insights__card:hover h3 { text-decoration: underline; text-decoration-thickness: var(--hairline); text-underline-offset: 0.12em; }
|
||||
.insights__img, :global(.insights__img) { width: 100%; height: auto; aspect-ratio: 3 / 2; object-fit: cover; margin-bottom: var(--space-2); }
|
||||
.insights__card h3 { max-width: 18em; }
|
||||
/* No picture: a colour field carries the source (or the event date) in large type. */
|
||||
/* No picture: a colour field carries the source in large type. */
|
||||
:global(.insights__panel) {
|
||||
aspect-ratio: 3 / 2;
|
||||
margin-bottom: var(--space-2);
|
||||
|
||||
@@ -31,27 +31,14 @@ const logos = await Promise.all([
|
||||
</ul>
|
||||
|
||||
<div class="partners">
|
||||
<p class="small muted partners__label" id="partners-label">{ui.proof.partners}</p>
|
||||
<p class="small muted" id="partners-label">{ui.proof.partners}</p>
|
||||
<ul class="partners__list" role="list" aria-labelledby="partners-label">
|
||||
{logos.map((l) => (
|
||||
<li class="partners__item" data-partner>
|
||||
<PartnerLogo name={l.name} src={l.src} url={l.url} h={l.h} />
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Variant B, for comparison: one column per partner across the full width, with its role.
|
||||
Keep one of the two and delete the other. */}
|
||||
<div class="partners-b">
|
||||
<p class="small muted" id="partners-b-label">{ui.proof.partners}</p>
|
||||
<ul class="partners-b__list" role="list" aria-labelledby="partners-b-label">
|
||||
{logos.map((l) => (
|
||||
<li class="partners-b__item" data-partner>
|
||||
<PartnerLogo name={l.name} src={l.src} url={l.url} h={l.h} />
|
||||
<p class="partners-b__text">
|
||||
<span class="partners-b__name">{l.name}</span>
|
||||
<span class="small partners-b__role">{l.role}</span>
|
||||
<p class="partners__text">
|
||||
<span class="partners__name">{l.name}</span>
|
||||
<span class="small partners__role">{l.role}</span>
|
||||
</p>
|
||||
</li>
|
||||
))}
|
||||
@@ -74,23 +61,6 @@ const logos = await Promise.all([
|
||||
.proof__name { font-weight: var(--weight-medium); }
|
||||
.proof__body { max-width: 30em; }
|
||||
|
||||
.partners {
|
||||
grid-column: 1 / -1;
|
||||
margin-top: var(--space-9);
|
||||
display: grid;
|
||||
gap: var(--space-5);
|
||||
}
|
||||
.partners__list {
|
||||
display: grid;
|
||||
border-top: var(--hairline) solid var(--line);
|
||||
}
|
||||
.partners__item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-height: var(--space-9);
|
||||
padding: var(--space-5) var(--space-4);
|
||||
border-bottom: var(--hairline) solid var(--line);
|
||||
}
|
||||
/* 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); }
|
||||
@media (hover: hover) {
|
||||
@@ -98,15 +68,14 @@ const logos = await Promise.all([
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) { [data-partner] { transition: none; } }
|
||||
|
||||
/* Variant B */
|
||||
.partners-b {
|
||||
.partners {
|
||||
grid-column: 1 / -1;
|
||||
margin-top: var(--space-9);
|
||||
display: grid;
|
||||
gap: var(--space-5);
|
||||
}
|
||||
.partners-b__list { display: grid; border-top: var(--hairline) solid var(--line); }
|
||||
.partners-b__item {
|
||||
.partners__list { display: grid; border-top: var(--hairline) solid var(--line); }
|
||||
.partners__item {
|
||||
display: grid;
|
||||
align-content: space-between;
|
||||
gap: var(--space-7);
|
||||
@@ -114,22 +83,19 @@ const logos = await Promise.all([
|
||||
padding: var(--space-6) var(--space-5);
|
||||
border-bottom: var(--hairline) solid var(--line);
|
||||
}
|
||||
.partners-b__text { display: grid; gap: var(--space-1); }
|
||||
.partners-b__name { font-weight: var(--weight-medium); }
|
||||
.partners-b__role { color: var(--ink-muted); transition: color var(--dur-fast) var(--ease); }
|
||||
.partners__text { display: grid; gap: var(--space-1); }
|
||||
.partners__name { font-weight: var(--weight-medium); }
|
||||
.partners__role { color: var(--ink-muted); transition: color var(--dur-fast) var(--ease); }
|
||||
@media (hover: hover) {
|
||||
.partners-b__item:hover .partners-b__role { color: var(--ink-muted-on-night); }
|
||||
.partners__item:hover .partners__role { color: var(--ink-muted-on-night); }
|
||||
}
|
||||
|
||||
@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 { grid-template-columns: repeat(12, minmax(0, 1fr)); column-gap: var(--gap); align-items: start; }
|
||||
.partners__label { grid-column: 1 / span 6; padding-top: var(--space-5); }
|
||||
.partners__list { grid-column: 7 / span 6; grid-template-columns: repeat(3, minmax(0, 1fr)); column-gap: var(--gap); }
|
||||
.partners-b__list { grid-template-columns: repeat(3, minmax(0, 1fr)); border-bottom: var(--hairline) solid var(--line); }
|
||||
.partners-b__item { border-bottom: 0; }
|
||||
.partners-b__item + .partners-b__item { border-left: var(--hairline) solid var(--line); }
|
||||
.partners__list { grid-template-columns: repeat(3, minmax(0, 1fr)); border-bottom: var(--hairline) solid var(--line); }
|
||||
.partners__item { border-bottom: 0; }
|
||||
.partners__item + .partners__item { border-left: var(--hairline) solid var(--line); }
|
||||
}
|
||||
</style>
|
||||
+4
-6
@@ -53,7 +53,7 @@ const en = {
|
||||
lines: ['Security for the software', 'everything else runs on.'],
|
||||
},
|
||||
manifesto: {
|
||||
lines: ['Security is built in,', 'not bolted on.'],
|
||||
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. Every interface between them is an attack surface, and off-the-shelf tools were not built for that.',
|
||||
p2: 'CipherCell is a security engineering company for software. We model real adversaries, build zero trust into the architecture rather than around it, and prove that the code holds. From the first threat model to the running system.',
|
||||
cta: 'About CipherCell',
|
||||
@@ -85,7 +85,7 @@ const en = {
|
||||
title: 'Who we work with',
|
||||
items: [
|
||||
{ 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 network 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: '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: '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.' },
|
||||
],
|
||||
},
|
||||
@@ -112,7 +112,6 @@ const en = {
|
||||
all: 'All insights',
|
||||
intro: 'Notes on software security in private 5G, critical networks and the systems that run them.',
|
||||
readingTime: (min: number) => `${min} min read`,
|
||||
event: { category: 'Event', when: 'September 2026', text: 'Meet us at the xG‑ALOE Plugfest 2026.', link: 'Event details', href: 'https://xg-aloe.de/xg-aloe-plugfest-2026/' },
|
||||
back: 'All insights',
|
||||
by: 'By',
|
||||
inEnglish: 'This essay is available in English.',
|
||||
@@ -348,7 +347,7 @@ const de: typeof en = {
|
||||
lines: ['Sicherheit für die Software,', 'auf der alles andere läuft.'],
|
||||
},
|
||||
manifesto: {
|
||||
lines: ['Sicherheit wird eingebaut,', 'nicht angeschraubt.'],
|
||||
lines: ['Sicherheit eingebaut,', 'nicht nachträglich angefügt.'],
|
||||
p1: 'Infrastruktur ist heute Software. Private 5G-Netze, industrielle Steuerungssysteme und Cloud-Plattformen werden aus Code und Komponenten vieler Hersteller zusammengesetzt. Jede Schnittstelle dazwischen ist eine Angriffsfläche, und Standardwerkzeuge sind dafür nicht gebaut.',
|
||||
p2: 'CipherCell ist ein Security-Engineering-Unternehmen für Software. Wir modellieren reale Angreifer, verankern Zero Trust in der Architektur statt daneben und weisen nach, dass der Code standhält. Vom ersten Bedrohungsmodell bis zum laufenden System.',
|
||||
cta: 'Über CipherCell',
|
||||
@@ -380,7 +379,7 @@ const de: typeof en = {
|
||||
title: 'Für wen wir arbeiten',
|
||||
items: [
|
||||
{ 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 Netzinfrastruktur', 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: '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: '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.' },
|
||||
],
|
||||
},
|
||||
@@ -407,7 +406,6 @@ const de: typeof en = {
|
||||
all: 'Alle Einblicke',
|
||||
intro: 'Notizen zur Softwaresicherheit in privaten 5G-Netzen, kritischen Netzen und den Systemen, die sie betreiben.',
|
||||
readingTime: (min: number) => `${min} Min. Lesezeit`,
|
||||
event: { category: 'Veranstaltung', when: 'September 2026', text: 'Treffen Sie uns beim xG‑ALOE Plugfest 2026.', link: 'Zur Veranstaltung', href: 'https://xg-aloe.de/xg-aloe-plugfest-2026/' },
|
||||
back: 'Alle Einblicke',
|
||||
by: 'Von',
|
||||
inEnglish: 'Dieser Essay liegt auf Englisch vor.',
|
||||
|
||||
@@ -22,13 +22,13 @@ const filters = [
|
||||
<Base lang={lang} title={ui.meta.insights.title} description={ui.meta.insights.description}>
|
||||
<section class="section section--intro">
|
||||
<div class="container grid">
|
||||
<h1 class="display index__title">{ui.insights.title}</h1>
|
||||
<p class="index__lead">{ui.insights.intro}</p>
|
||||
<h1 class="display index__title load-rise" style="--i: 0">{ui.insights.title}</h1>
|
||||
<p class="index__lead load-rise" style="--i: 1">{ui.insights.intro}</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section--list" aria-label={ui.insights.title}>
|
||||
<div class="container">
|
||||
<div class="container load-rise" style="--i: 2">
|
||||
{filters.length > 2 && (
|
||||
<fieldset class="choice-group filters" data-filters>
|
||||
<legend class="choice-group__legend">{f.label}</legend>
|
||||
|
||||
+15
-2
@@ -14,9 +14,9 @@ const ui = t(lang);
|
||||
<section class="section section--intro team-intro">
|
||||
<div class="container grid">
|
||||
<h1 class="display team-intro__title">
|
||||
{ui.team.title.map((line) => <span class="line">{line}</span>)}
|
||||
{ui.team.title.map((line, i) => <span class="line load-rise" style={`--i: ${i}`}>{line}</span>)}
|
||||
</h1>
|
||||
<p class="team-intro__lead">{ui.team.intro}</p>
|
||||
<p class="team-intro__lead load-rise" style={`--i: ${ui.team.title.length}`}>{ui.team.intro}</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -66,6 +66,19 @@ const ui = t(lang);
|
||||
|
||||
<style>
|
||||
.line { display: block; }
|
||||
|
||||
/* Load: the headline rises line by line and the lead follows (.load-rise, base.css);
|
||||
the photo settles from a slight scale. Then stillness. */
|
||||
.team-photo { overflow: hidden; }
|
||||
.team-photo__img, :global(.team-photo__img) { animation: team-settle var(--dur-hero) var(--ease) both; }
|
||||
|
||||
@keyframes team-settle {
|
||||
from { transform: scale(1.045); }
|
||||
to { transform: none; }
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.team-photo__img, :global(.team-photo__img) { animation: none; transform: none; }
|
||||
}
|
||||
.team-intro__title { grid-column: 1 / -1; }
|
||||
.team-intro__lead { grid-column: 1 / -1; max-width: var(--measure); color: var(--ink-muted); }
|
||||
@media (min-width: 48rem) {
|
||||
|
||||
@@ -281,6 +281,18 @@ p { overflow-wrap: break-word; }
|
||||
white-space: nowrap; border: 0;
|
||||
}
|
||||
|
||||
/* Page load: an element rises in after a delay of --i steps (the index in its sequence).
|
||||
Used for the intro of inner pages, in the home hero's vocabulary. */
|
||||
.load-rise {
|
||||
opacity: 0;
|
||||
animation: load-rise var(--dur-rise) var(--ease) forwards;
|
||||
animation-delay: calc(var(--delay-rise) + var(--i, 0) * var(--stagger-rise));
|
||||
}
|
||||
@keyframes load-rise {
|
||||
from { opacity: 0; transform: translateY(0.18em); }
|
||||
to { opacity: 1; transform: none; }
|
||||
}
|
||||
|
||||
/* Motion off. No exceptions. */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
html { scroll-behavior: auto; }
|
||||
@@ -290,4 +302,5 @@ p { overflow-wrap: break-word; }
|
||||
transition-duration: 0.01ms !important;
|
||||
scroll-behavior: auto !important;
|
||||
}
|
||||
.load-rise { animation: none; opacity: 1; }
|
||||
}
|
||||
@@ -125,6 +125,9 @@
|
||||
--dur: 240ms;
|
||||
--dur-slow: 320ms;
|
||||
--dur-hero: 1400ms;
|
||||
--dur-rise: 700ms; /* a headline line rising in on load */
|
||||
--delay-rise: 240ms; /* before the first line */
|
||||
--stagger-rise: 160ms; /* between lines */
|
||||
|
||||
/* Chrome */
|
||||
--header-height: 4.5rem;
|
||||
|
||||
Reference in new issue
Block a user