asdf
This commit is contained in:
1 parent
cd45c5d98f
commit
2b7ac42d6b
3 files changed
+375
-74
No files matched your search
@@ -1,13 +1,10 @@
|
||||
---
|
||||
import Schematic from './Schematic.astro';
|
||||
import { overview } from './diagrams';
|
||||
import Verticals from './Verticals.astro';
|
||||
import { t, type Lang } from '../../i18n/ui';
|
||||
interface Props { lang: Lang }
|
||||
const { lang } = Astro.props;
|
||||
const ui = t(lang);
|
||||
const s = ui.verifier.soon;
|
||||
const g = ui.verifier.how.groups[0];
|
||||
const pair = overview(g);
|
||||
/* The step that is current; earlier ones read as passed, later ones as open. */
|
||||
const now = 0;
|
||||
---
|
||||
@@ -30,30 +27,7 @@ const now = 0;
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section spec" aria-labelledby="spec-title">
|
||||
<div class="container grid">
|
||||
<div class="spec__head">
|
||||
<h2 id="spec-title" class="h2">{s.spec.title}</h2>
|
||||
<p class="muted">{s.spec.body}</p>
|
||||
</div>
|
||||
<dl class="spec__table">
|
||||
{s.spec.rows.map((r) => (
|
||||
<div class="spec__row">
|
||||
<dt>{r.key}</dt>
|
||||
<dd>{r.value}</dd>
|
||||
</div>
|
||||
))}
|
||||
</dl>
|
||||
<figure class="spec__figure">
|
||||
<div class="spec__drawing hud-frame">
|
||||
<div class="spec__wide"><Schematic layout={pair.wide} title={g.title} desc={g.desc} variant="wide" id="soon-wide" /></div>
|
||||
<div class="spec__tall"><Schematic layout={pair.tall} title={g.title} desc={g.desc} variant="tall" id="soon-tall" /></div>
|
||||
</div>
|
||||
<figcaption class="small muted">{s.spec.figure}</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Verticals lang={lang} />
|
||||
<style>
|
||||
/* Release path: one hairline, one node per phase. */
|
||||
.track__head { grid-column: 1 / -1; display: grid; gap: var(--space-5); align-content: start; }
|
||||
@@ -100,30 +74,8 @@ const now = 0;
|
||||
.track__step[data-state='next'] .track__name { color: var(--ink-muted); }
|
||||
.track__body { max-width: 22em; }
|
||||
|
||||
/* Spec sheet: key and value on hairlines, like a data sheet. */
|
||||
.spec { border-top: var(--hairline) solid var(--line); }
|
||||
.spec__head { grid-column: 1 / -1; display: grid; gap: var(--space-5); align-content: start; }
|
||||
.spec__head p { max-width: 28em; }
|
||||
.spec__table { grid-column: 1 / -1; border-top: var(--hairline) solid var(--ink); }
|
||||
.spec__row {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(8rem, 1fr) 2fr;
|
||||
gap: var(--gap);
|
||||
padding-block: var(--space-4);
|
||||
border-bottom: var(--hairline) solid var(--line);
|
||||
}
|
||||
.spec__row dt { font-size: var(--step-small); line-height: var(--leading-small); color: var(--ink-muted); padding-top: 0.15em; }
|
||||
.spec__figure { grid-column: 1 / -1; display: grid; gap: var(--space-3); margin-top: var(--space-6); }
|
||||
.spec__drawing { padding: var(--space-6); }
|
||||
.spec__wide { display: none; }
|
||||
|
||||
@media (min-width: 48rem) {
|
||||
.track__head { grid-column: 1 / span 4; }
|
||||
.spec__head { grid-column: 1 / span 5; }
|
||||
.spec__table { grid-column: 7 / span 6; }
|
||||
.spec__wide { display: block; }
|
||||
.spec__tall { display: none; }
|
||||
.spec__drawing { padding: var(--space-7) var(--space-8); }
|
||||
}
|
||||
/* Wide: the rail turns horizontal and runs across all three phases. */
|
||||
@media (min-width: 64rem) {
|
||||
|
||||
@@ -0,0 +1,329 @@
|
||||
---
|
||||
import { localePath, t, type Lang } from '../../i18n/ui';
|
||||
interface Props { lang: Lang }
|
||||
const { lang } = Astro.props;
|
||||
const v = t(lang).verifier.soon.verticals;
|
||||
/* The first tile is the vertical that is integrated; the others are open slots. */
|
||||
const tiles = v.tiles.map((tile, i) => ({ ...tile, id: `vertical-${i + 1}`, kind: i === 0 ? 'telecom' : 'open' }));
|
||||
---
|
||||
<section class="section verticals" aria-labelledby="verticals-title">
|
||||
<div class="container grid">
|
||||
<div class="verticals__head">
|
||||
<h2 id="verticals-title" class="h2">{v.title}</h2>
|
||||
<p class="muted">{v.body}</p>
|
||||
</div>
|
||||
|
||||
<div class="verticals__system" data-verticals>
|
||||
<p class="verticals__platform small">{v.platform}</p>
|
||||
|
||||
<div class="verticals__tiles" role="tablist" aria-labelledby="verticals-title">
|
||||
{tiles.map((tile, i) => (
|
||||
<button
|
||||
class="verticals__tile hud-frame"
|
||||
type="button"
|
||||
role="tab"
|
||||
id={`${tile.id}-tab`}
|
||||
aria-controls={`${tile.id}-panel`}
|
||||
aria-selected={i === 0 ? 'true' : 'false'}
|
||||
tabindex={i === 0 ? undefined : -1}
|
||||
data-kind={tile.kind}
|
||||
>
|
||||
<span class="verticals__state small">
|
||||
<span class="verticals__mark" aria-hidden="true" />
|
||||
{tile.state}
|
||||
</span>
|
||||
{tile.kind === 'telecom' ? (
|
||||
/* A lattice mast with its antenna panels, radiating to both sides, two cabinets at the foot. */
|
||||
<svg class="verticals__drawing" viewBox="0 0 240 150" aria-hidden="true">
|
||||
<g class="verticals__idea">
|
||||
<path d="M104 132 L117 34 M136 132 L123 34 M117 34 L123 34 M120 34 L120 14" />
|
||||
<path d="M105 124 L134 112 L107 100 L132 88 L109 76 L130 64 L112 52 L128 42" />
|
||||
<path d="M110 40 h-5 v14 h5 z M130 40 h5 v14 h-5 z" />
|
||||
<path d="M96 28 A30 30 0 0 0 96 66 M86 18 A44 44 0 0 0 86 76 M76 8 A58 58 0 0 0 76 86" />
|
||||
<path d="M144 28 A30 30 0 0 1 144 66 M154 18 A44 44 0 0 1 154 76 M164 8 A58 58 0 0 1 164 86" />
|
||||
</g>
|
||||
<g class="verticals__ground">
|
||||
<path d="M40 132 H200" />
|
||||
<path d="M62 132 v-14 h18 v14 M160 132 v-14 h18 v14 M80 125 H104 M136 125 H160" />
|
||||
</g>
|
||||
</svg>
|
||||
) : (
|
||||
/* An empty module, drawn dashed: the slot the next vertical plugs into. */
|
||||
<svg class="verticals__drawing" viewBox="0 0 240 150" aria-hidden="true">
|
||||
<g class="verticals__idea">
|
||||
<path d="M120 14 V34" />
|
||||
<path class="verticals__dashed" d="M120 34 L162 56 L120 78 L78 56 Z M78 56 V104 L120 126 L162 104 V56 M120 78 V126" />
|
||||
<path d="M120 49 v14 M113 56 h14" />
|
||||
</g>
|
||||
<g class="verticals__ground">
|
||||
<path d="M40 132 H200" />
|
||||
<path d="M60 22 v-8 h8 M180 22 v-8 h-8 M60 118 v8 h8 M180 118 v8 h-8" />
|
||||
</g>
|
||||
</svg>
|
||||
)}
|
||||
<span class="verticals__name">{tile.name}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div class="verticals__panels">
|
||||
{tiles.map((tile, i) => (
|
||||
<div
|
||||
class="verticals__panel"
|
||||
role="tabpanel"
|
||||
id={`${tile.id}-panel`}
|
||||
aria-labelledby={`${tile.id}-tab`}
|
||||
tabindex="0"
|
||||
data-open={i === 0 ? '' : undefined}
|
||||
>
|
||||
<div class="verticals__copy">
|
||||
<h3 class="h3">{tile.title}</h3>
|
||||
<p class="muted">{tile.body}</p>
|
||||
</div>
|
||||
{'rows' in tile && tile.rows && (
|
||||
<dl class="verticals__rows">
|
||||
{tile.rows.map((r) => (
|
||||
<div class="verticals__row">
|
||||
<dt>{r.key}</dt>
|
||||
<dd>{r.value}</dd>
|
||||
</div>
|
||||
))}
|
||||
</dl>
|
||||
)}
|
||||
{'cta' in tile && tile.cta && (
|
||||
<p class="verticals__cta"><a class="button" href={`${localePath(lang, '/')}#contact`}>{tile.cta}</a></p>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<style>
|
||||
/* Verticals: the platform on top, a bus below it, one tile per vertical plugged into the bus.
|
||||
The selected tile turns night; its panel below says what the vertical covers. */
|
||||
.verticals { border-top: var(--hairline) solid var(--line); }
|
||||
.verticals__head { grid-column: 1 / -1; display: grid; gap: var(--space-5); align-content: start; }
|
||||
.verticals__head p { max-width: 24em; }
|
||||
.verticals__system { grid-column: 1 / -1; display: grid; }
|
||||
|
||||
/* Phones: the platform sits left, a spine runs down from it and each tile plugs in from the
|
||||
side. Wider: the platform is centred over a bus and each tile drops a stub from it. */
|
||||
.verticals__system { --stub: var(--space-5); --spine: var(--space-4); --indent: var(--space-7); }
|
||||
.verticals__platform {
|
||||
justify-self: start;
|
||||
position: relative;
|
||||
padding: var(--space-2) var(--space-4);
|
||||
border: var(--hairline) solid var(--ink);
|
||||
font-weight: var(--weight-medium);
|
||||
}
|
||||
.verticals__platform::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: var(--spine);
|
||||
height: var(--stub);
|
||||
border-left: var(--hairline) solid var(--ink);
|
||||
}
|
||||
|
||||
.verticals__tiles {
|
||||
position: relative;
|
||||
display: grid;
|
||||
grid-auto-rows: 1fr;
|
||||
gap: var(--gap);
|
||||
margin-top: var(--stub);
|
||||
padding-left: var(--indent);
|
||||
}
|
||||
|
||||
/* .hud-frame redefines --line as a width, so the frame colour comes from the HUD token. */
|
||||
.verticals__tile {
|
||||
--frame-color: var(--hud-line);
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) minmax(0, 0.8fr);
|
||||
grid-template-rows: auto 1fr;
|
||||
column-gap: var(--space-4);
|
||||
row-gap: var(--space-5);
|
||||
padding: var(--space-4);
|
||||
text-align: left;
|
||||
font: inherit;
|
||||
color: var(--ink);
|
||||
background: var(--paper);
|
||||
cursor: pointer;
|
||||
transition:
|
||||
background var(--dur) var(--ease),
|
||||
color var(--dur) var(--ease);
|
||||
}
|
||||
/* The plug: down the spine from the tile above (or the platform), then across into the tile.
|
||||
Rows share one height, so -50% of a tile reaches the middle of the one before it. */
|
||||
.verticals__tile::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: 100%;
|
||||
width: calc(var(--indent) - var(--spine));
|
||||
top: calc(-50% - var(--gap));
|
||||
bottom: 50%;
|
||||
border-left: var(--hairline) solid var(--ink);
|
||||
border-bottom: var(--hairline) solid var(--ink);
|
||||
}
|
||||
.verticals__tile:first-child::before { top: calc(-1 * var(--stub)); }
|
||||
/* An open slot is plugged in with a dashed line: reserved, not yet connected. */
|
||||
.verticals__tile[data-kind='open']::before { border-left-style: dashed; border-bottom-style: dashed; }
|
||||
|
||||
.verticals__tile[aria-selected='true'] {
|
||||
--frame-color: var(--ink-base);
|
||||
--ink-muted: var(--ink-muted-on-night);
|
||||
background: var(--night);
|
||||
color: var(--ink-on-night);
|
||||
cursor: default;
|
||||
}
|
||||
/* The ring sits outside the tile, on paper, so it keeps the paper ink even on the night tile. */
|
||||
.verticals__tile:focus-visible { outline-color: var(--ink-base); }
|
||||
/* Crosses only on the selected tile: it is the one under inspection. */
|
||||
.verticals__tile[aria-selected='false']::after { opacity: 0; }
|
||||
.verticals__tile::after { transition: opacity var(--dur) var(--ease); }
|
||||
|
||||
.verticals__state {
|
||||
grid-column: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-2);
|
||||
color: var(--ink-muted);
|
||||
}
|
||||
/* A square that fills when the tile is selected, like a selector on a panel. */
|
||||
.verticals__mark {
|
||||
width: 0.5rem;
|
||||
height: 0.5rem;
|
||||
border: var(--hairline) solid currentColor;
|
||||
flex: none;
|
||||
transition: background var(--dur) var(--ease);
|
||||
}
|
||||
.verticals__tile[aria-selected='true'] .verticals__mark { background: var(--ink-on-night); border-color: var(--ink-on-night); }
|
||||
|
||||
.verticals__drawing {
|
||||
grid-column: 2;
|
||||
grid-row: 1 / span 2;
|
||||
width: 100%;
|
||||
max-width: 22rem;
|
||||
justify-self: center;
|
||||
align-self: center;
|
||||
height: auto;
|
||||
overflow: visible;
|
||||
fill: none;
|
||||
stroke: currentColor;
|
||||
stroke-width: var(--hairline);
|
||||
stroke-linecap: square;
|
||||
vector-effect: non-scaling-stroke;
|
||||
transform-origin: 50% 60%;
|
||||
transition: transform var(--dur-slow) var(--ease);
|
||||
}
|
||||
.verticals__drawing path { vector-effect: non-scaling-stroke; }
|
||||
.verticals__ground { opacity: 0.4; transition: opacity var(--dur-slow) var(--ease); }
|
||||
.verticals__dashed { stroke-dasharray: 4 4; }
|
||||
|
||||
.verticals__name {
|
||||
grid-column: 1;
|
||||
align-self: end;
|
||||
font-size: var(--step-h3);
|
||||
font-weight: var(--weight-medium);
|
||||
letter-spacing: var(--track-h3);
|
||||
line-height: var(--leading-h3);
|
||||
}
|
||||
|
||||
/* Pointing at a tile brings its drawing forward, as on the home page cards. */
|
||||
@media (hover: hover) {
|
||||
.verticals__tile[aria-selected='false']:hover { background: color-mix(in srgb, var(--ink) var(--tint-hover), var(--paper)); }
|
||||
.verticals__tile:hover .verticals__drawing { transform: scale(1.03); }
|
||||
.verticals__tile:hover .verticals__ground { opacity: 0.26; }
|
||||
}
|
||||
|
||||
/* Panels share one cell, so switching crossfades without the section changing height. */
|
||||
.verticals__panels {
|
||||
display: grid;
|
||||
margin-top: var(--space-6);
|
||||
border-top: var(--hairline) solid var(--ink);
|
||||
}
|
||||
.verticals__panel {
|
||||
grid-area: 1 / 1;
|
||||
display: grid;
|
||||
gap: var(--space-5);
|
||||
align-content: start;
|
||||
padding-top: var(--space-5);
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition:
|
||||
opacity var(--dur) var(--ease),
|
||||
visibility 0s linear var(--dur);
|
||||
}
|
||||
.verticals__panel[data-open] {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
transition: opacity var(--dur-slow) var(--ease) var(--dur-fast), visibility 0s;
|
||||
}
|
||||
.verticals__copy { display: grid; gap: var(--space-3); }
|
||||
.verticals__copy p { max-width: var(--measure); }
|
||||
.verticals__rows { border-top: var(--hairline) solid var(--line); }
|
||||
.verticals__row {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(7rem, 1fr) 2fr;
|
||||
gap: var(--gap);
|
||||
padding-block: var(--space-3);
|
||||
border-bottom: var(--hairline) solid var(--line);
|
||||
}
|
||||
.verticals__row dt { font-size: var(--step-small); line-height: var(--leading-small); color: var(--ink-muted); padding-top: 0.15em; }
|
||||
|
||||
@media (min-width: 48rem) {
|
||||
.verticals__system { --stub: var(--space-6); }
|
||||
.verticals__platform { justify-self: center; }
|
||||
.verticals__platform::after { left: 50%; }
|
||||
.verticals__tiles {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
grid-auto-rows: auto;
|
||||
margin-top: calc(2 * var(--stub));
|
||||
padding-left: 0;
|
||||
}
|
||||
/* The bus runs between the centres of the two tiles. */
|
||||
.verticals__tiles::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: calc(-1 * var(--stub));
|
||||
left: calc(25% - var(--gap) / 4);
|
||||
right: calc(25% - var(--gap) / 4);
|
||||
border-top: var(--hairline) solid var(--ink);
|
||||
}
|
||||
.verticals__tile {
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
grid-template-rows: auto 1fr auto;
|
||||
row-gap: var(--space-4);
|
||||
padding: var(--space-5);
|
||||
}
|
||||
.verticals__tile::before,
|
||||
.verticals__tile:first-child::before {
|
||||
top: auto;
|
||||
bottom: 100%;
|
||||
left: 50%;
|
||||
right: auto;
|
||||
width: 0;
|
||||
height: var(--stub);
|
||||
border-bottom: 0;
|
||||
}
|
||||
.verticals__state, .verticals__drawing, .verticals__name { grid-column: 1; grid-row: auto; }
|
||||
.verticals__drawing { padding-block: var(--space-4); }
|
||||
.verticals__panel { grid-template-columns: minmax(0, 3fr) minmax(0, 2fr); column-gap: var(--gap); }
|
||||
.verticals__rows, .verticals__cta { align-self: start; margin-top: var(--space-1); }
|
||||
}
|
||||
@media (min-width: 64rem) {
|
||||
.verticals__head { grid-column: 1 / span 4; }
|
||||
.verticals__system { grid-column: 5 / span 8; }
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.verticals__tile, .verticals__tile::after, .verticals__mark, .verticals__drawing, .verticals__ground, .verticals__panel, .verticals__panel[data-open] { transition: none; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import { initTabs } from '../../scripts/tabs';
|
||||
const root = document.querySelector<HTMLElement>('[data-verticals]');
|
||||
if (root) initTabs(root);
|
||||
</script>
|
||||
+44
-24
@@ -304,19 +304,29 @@ const en = {
|
||||
{ state: 'Later', name: 'General availability', body: 'Date to follow.' },
|
||||
],
|
||||
},
|
||||
spec: {
|
||||
title: 'What is being built.',
|
||||
body: 'CipherVerifier takes a repository, runs seventeen static analysers and two bill-of-materials generators over it, reads each finding in its context and ranks what is real by security impact.',
|
||||
rows: [
|
||||
{ key: 'Static analysers', value: '17, each in its own container' },
|
||||
{ key: 'Languages', value: '14, plus Docker, Kubernetes, Helm, Terraform and CloudFormation' },
|
||||
{ key: 'Bill of materials', value: '2 generators, merged into one CycloneDX document' },
|
||||
{ key: 'Vocabulary', value: '970 CWE entries' },
|
||||
{ key: 'Pipeline', value: '5 stages, each switchable per repository' },
|
||||
{ key: 'Review', value: 'Two-stage verdict on every finding' },
|
||||
{ key: 'Deployment', value: 'Self-hosted' },
|
||||
verticals: {
|
||||
title: 'Verticals.',
|
||||
body: 'One pipeline underneath. Each vertical brings the knowledge of one domain to it.',
|
||||
platform: 'CipherVerifier platform',
|
||||
tiles: [
|
||||
{
|
||||
state: 'Integrated',
|
||||
name: 'Telecommunication security',
|
||||
title: 'Built from Open RAN security research.',
|
||||
body: 'The first vertical comes from where CipherCell started: the security of Open RAN and private 5G networks, worked on with the University of Passau and the xG-ALOE alliance.',
|
||||
rows: [
|
||||
{ key: 'Networks', value: 'Open RAN, private 5G' },
|
||||
{ key: 'Infrastructure', value: 'Critical network infrastructure' },
|
||||
],
|
||||
},
|
||||
{
|
||||
state: 'Open',
|
||||
name: 'Other',
|
||||
title: 'Your domain next.',
|
||||
body: 'The pipeline underneath is not tied to telecommunications. Further verticals are added where operators bring a domain to us.',
|
||||
cta: 'Propose a vertical',
|
||||
},
|
||||
],
|
||||
figure: 'Figure 1. One run, five stages.',
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -644,19 +654,29 @@ const de: typeof en = {
|
||||
{ state: 'Später', name: 'Allgemeine Verfügbarkeit', body: 'Termin folgt.' },
|
||||
],
|
||||
},
|
||||
spec: {
|
||||
title: 'Was entsteht.',
|
||||
body: 'CipherVerifier nimmt ein Repository, lässt siebzehn statische Analysewerkzeuge und zwei Stücklisten-Generatoren darüber laufen, liest jeden Fund in seinem Kontext und ordnet, was echt ist, nach Sicherheitsrelevanz.',
|
||||
rows: [
|
||||
{ key: 'Statische Analyse', value: '17 Werkzeuge, jedes im eigenen Container' },
|
||||
{ key: 'Sprachen', value: '14, dazu Docker, Kubernetes, Helm, Terraform und CloudFormation' },
|
||||
{ key: 'Stückliste', value: '2 Generatoren, zu einem CycloneDX-Dokument zusammengeführt' },
|
||||
{ key: 'Vokabular', value: '970 CWE-Einträge' },
|
||||
{ key: 'Pipeline', value: '5 Stufen, jede pro Repository abschaltbar' },
|
||||
{ key: 'Urteil', value: 'Zweistufig, für jeden Fund' },
|
||||
{ key: 'Betrieb', value: 'Selbst gehostet' },
|
||||
verticals: {
|
||||
title: 'Vertikalen.',
|
||||
body: 'Darunter eine Pipeline. Jede Vertikale bringt das Wissen einer Domäne mit.',
|
||||
platform: 'Plattform CipherVerifier',
|
||||
tiles: [
|
||||
{
|
||||
state: 'Integriert',
|
||||
name: 'Telekommunikations\u00ADsicherheit',
|
||||
title: 'Aus der Open-RAN-Sicherheitsforschung.',
|
||||
body: 'Die erste Vertikale kommt von dort, wo CipherCell begonnen hat: der Sicherheit von Open-RAN- und privaten 5G-Netzen, erarbeitet mit der Universität Passau und der xG-ALOE-Allianz.',
|
||||
rows: [
|
||||
{ key: 'Netze', value: 'Open RAN, private 5G-Netze' },
|
||||
{ key: 'Infrastruktur', value: 'Kritische Netzinfrastruktur' },
|
||||
],
|
||||
},
|
||||
{
|
||||
state: 'Offen',
|
||||
name: 'Weitere',
|
||||
title: 'Ihre Domäne als Nächstes.',
|
||||
body: 'Die Pipeline darunter ist nicht an die Telekommunikation gebunden. Weitere Vertikalen entstehen dort, wo Betreiber uns eine Domäne bringen.',
|
||||
cta: 'Vertikale vorschlagen',
|
||||
},
|
||||
],
|
||||
figure: 'Abbildung 1. Ein Lauf, fünf Stufen.',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user