comming soon
This commit is contained in:
1 parent
c72d45740e
commit
2b509386eb
9 files changed
+437
-6
No files matched your search
@@ -26,6 +26,7 @@ npm run check # Astro + TypeScript check
|
|||||||
| `src/components/home/GridObject.astro` | The wireframe objects on the stage and product cards (sphere, cube, plane, wave, steps, nested, stack). Geometry is computed at build time; single stroke in `--ink`. Each drawing is laid out by hand, so the `viewBox` is derived from the points as they are emitted, not assumed: a square centred on the drawing, padded for the stroke. A new kind can therefore never be clipped, and the card gives the drawing the flexible row so it sits centred between title and body. Each drawing is emitted in two groups, the lines that carry its idea and the grid around them, which is what the hover and the reveal act on. |
|
| `src/components/home/GridObject.astro` | The wireframe objects on the stage and product cards (sphere, cube, plane, wave, steps, nested, stack). Geometry is computed at build time; single stroke in `--ink`. Each drawing is laid out by hand, so the `viewBox` is derived from the points as they are emitted, not assumed: a square centred on the drawing, padded for the stroke. A new kind can therefore never be clipped, and the card gives the drawing the flexible row so it sits centred between title and body. Each drawing is emitted in two groups, the lines that carry its idea and the grid around them, which is what the hover and the reveal act on. |
|
||||||
| `src/components/home/MethodDiagram.astro` | The four-stage loop, wide and tall compositions. Labels come from `ui.method.stages`. |
|
| `src/components/home/MethodDiagram.astro` | The four-stage loop, wide and tall compositions. Labels come from `ui.method.stages`. |
|
||||||
| `src/components/verifier/*` | The CipherVerifier product page, in page order: Hero (line burst), Intro (image band), HowItWorks (five group cards, one schematic each; on wide screens the heading is sticky and the cards stack under it as you scroll), Cards, Facts, Outro. |
|
| `src/components/verifier/*` | The CipherVerifier product page, in page order: Hero (line burst), Intro (image band), HowItWorks (five group cards, one schematic each; on wide screens the heading is sticky and the cards stack under it as you scroll), Cards, Facts, Outro. |
|
||||||
|
| `src/components/verifier/SoonHero.astro`, `ComingSoon.astro` | The coming-soon version of `/cipherverifier`, shown while `verifierComingSoon` in `src/data/flags.ts` is true: headline with the alpha sign-up beside it, a status strip along the foot of the hero, release path, spec sheet and the first schematic. Copy in `ui.verifier.soon`. |
|
||||||
| `src/components/verifier/diagrams.ts` | The five schematics as data: a hand-placed wide layout and an auto-generated tall spine per group. `Schematic.astro` renders either. Labels come from `ui.verifier.how.groups`; the tuple shape is in `types.ts`. Every element carries the `group` of the node it belongs to, which drives the hover focus and the draw-in order. |
|
| `src/components/verifier/diagrams.ts` | The five schematics as data: a hand-placed wide layout and an auto-generated tall spine per group. `Schematic.astro` renders either. Labels come from `ui.verifier.how.groups`; the tuple shape is in `types.ts`. Every element carries the `group` of the node it belongs to, which drives the hover focus and the draw-in order. |
|
||||||
| `scripts/*.mjs` | Review tooling: `shot.mjs` (full page), `scroll-shots.mjs` (viewport by viewport), `tabs-test.mjs` (keyboard test for the sectors tabs), `contact-sheet.mjs`. Need `npx astro preview --port 4322`. |
|
| `scripts/*.mjs` | Review tooling: `shot.mjs` (full page), `scroll-shots.mjs` (viewport by viewport), `tabs-test.mjs` (keyboard test for the sectors tabs), `contact-sheet.mjs`. Need `npx astro preview --port 4322`. |
|
||||||
| `src/pages/_*.astro` | Shared page bodies. `index.astro` and `de/index.astro` render `_Home.astro` with a `lang` prop, and so on. |
|
| `src/pages/_*.astro` | Shared page bodies. `index.astro` and `de/index.astro` render `_Home.astro` with a `lang` prop, and so on. |
|
||||||
@@ -43,6 +44,13 @@ npm run check # Astro + TypeScript check
|
|||||||
- **Contact form.** The form posts `first_name`, `last_name`, `email`, `message` and a honeypot
|
- **Contact form.** The form posts `first_name`, `last_name`, `email`, `message` and a honeypot
|
||||||
field `website` to `POST /contact`, as the current site does. The handler is server-side and
|
field `website` to `POST /contact`, as the current site does. The handler is server-side and
|
||||||
not part of this static build.
|
not part of this static build.
|
||||||
|
- **Alpha sign-up.** While `verifierComingSoon` in `src/data/flags.ts` is true, `/cipherverifier`
|
||||||
|
shows a coming-soon page whose form posts `email`, `consent=yes`, `lang`,
|
||||||
|
`list=cipherverifier-alpha` and the honeypot `website` to `POST /notify`. The handler is
|
||||||
|
server-side and not part of this static build. It should store the address with the time of
|
||||||
|
consent, confirm it by double opt-in, and redirect back with a thank-you. The privacy page
|
||||||
|
promises deletion after the alpha mail is sent. Set the flag to false to bring back the full
|
||||||
|
product page.
|
||||||
- **Matomo.** The privacy page links to the existing opt-out on `cipher.uber.space`. The tracking
|
- **Matomo.** The privacy page links to the existing opt-out on `cipher.uber.space`. The tracking
|
||||||
snippet itself is not included; add it in `src/layouts/Base.astro` if analytics should continue.
|
snippet itself is not included; add it in `src/layouts/Base.astro` if analytics should continue.
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,147 @@
|
|||||||
|
---
|
||||||
|
import Schematic from './Schematic.astro';
|
||||||
|
import { overview } from './diagrams';
|
||||||
|
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;
|
||||||
|
---
|
||||||
|
<section class="section track" aria-labelledby="track-title">
|
||||||
|
<div class="container grid">
|
||||||
|
<div class="track__head">
|
||||||
|
<h2 id="track-title" class="h2">{s.track.title}</h2>
|
||||||
|
<p class="muted">{s.track.body}</p>
|
||||||
|
</div>
|
||||||
|
<ol class="track__steps" role="list">
|
||||||
|
{s.track.steps.map((step, i) => (
|
||||||
|
<li class="track__step" data-state={i < now ? 'past' : i === now ? 'now' : 'next'} aria-current={i === now ? 'step' : undefined}>
|
||||||
|
<span class="track__node" aria-hidden="true" />
|
||||||
|
<p class="track__state">{step.state}</p>
|
||||||
|
<h3 class="h3 track__name">{step.name}</h3>
|
||||||
|
<p class="track__body muted">{step.body}</p>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ol>
|
||||||
|
</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>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* Release path: one hairline, one node per phase. Numbering is earned here: it is a sequence. */
|
||||||
|
.track__head { grid-column: 1 / -1; display: grid; gap: var(--space-5); align-content: start; }
|
||||||
|
.track__head p { max-width: 24em; }
|
||||||
|
.track__steps { grid-column: 1 / -1; display: grid; gap: var(--space-7); counter-reset: step; }
|
||||||
|
.track__step {
|
||||||
|
position: relative;
|
||||||
|
display: grid;
|
||||||
|
gap: var(--space-2);
|
||||||
|
align-content: start;
|
||||||
|
padding-left: var(--space-7);
|
||||||
|
counter-increment: step;
|
||||||
|
}
|
||||||
|
/* Vertical rail on small screens. */
|
||||||
|
.track__step::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
left: calc(0.5rem - var(--hairline) / 2);
|
||||||
|
top: 1.25rem;
|
||||||
|
bottom: calc(-1 * var(--space-7));
|
||||||
|
border-left: var(--hairline) solid var(--ink-muted);
|
||||||
|
}
|
||||||
|
.track__step:last-child::before { bottom: 0; border-left-style: dashed; }
|
||||||
|
.track__step[data-state='next']::before { border-left-style: dashed; border-color: var(--line); }
|
||||||
|
.track__node {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0.2rem;
|
||||||
|
width: 1rem;
|
||||||
|
height: 1rem;
|
||||||
|
border: var(--hairline) solid var(--ink);
|
||||||
|
border-radius: 50%;
|
||||||
|
background: var(--paper);
|
||||||
|
}
|
||||||
|
.track__step[data-state='now'] .track__node { outline: var(--hairline) solid var(--ink-muted); outline-offset: 0.35rem; }
|
||||||
|
.track__step[data-state='now'] .track__node::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
inset: 0.2rem;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: var(--signal);
|
||||||
|
outline: var(--hairline) solid var(--ink);
|
||||||
|
}
|
||||||
|
.track__step[data-state='next'] .track__node { border-style: dashed; border-color: var(--ink-muted); }
|
||||||
|
.track__state { font-size: var(--step-small); line-height: var(--leading-small); color: var(--ink-muted); }
|
||||||
|
.track__state::before { content: counter(step, decimal-leading-zero) ' '; white-space: pre; color: var(--ink); }
|
||||||
|
.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) {
|
||||||
|
.track__steps { grid-column: 5 / span 8; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--gap); }
|
||||||
|
.track__step { padding-left: 0; padding-top: var(--space-7); padding-right: var(--space-4); }
|
||||||
|
.track__step::before {
|
||||||
|
left: 1.25rem;
|
||||||
|
right: calc(-1 * var(--gap) - 0rem);
|
||||||
|
top: calc(0.5rem - var(--hairline) / 2);
|
||||||
|
bottom: auto;
|
||||||
|
border-left: 0;
|
||||||
|
border-top: var(--hairline) solid var(--ink-muted);
|
||||||
|
}
|
||||||
|
.track__step:last-child::before { right: 0; border-top-style: dashed; }
|
||||||
|
.track__step[data-state='next']::before { border-top-style: dashed; border-color: var(--ink-muted); }
|
||||||
|
.track__node { top: 0; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,159 @@
|
|||||||
|
---
|
||||||
|
import { localePath, t, type Lang } from '../../i18n/ui';
|
||||||
|
interface Props { lang: Lang }
|
||||||
|
const { lang } = Astro.props;
|
||||||
|
const ui = t(lang);
|
||||||
|
const s = ui.verifier.soon;
|
||||||
|
const n = s.notify;
|
||||||
|
---
|
||||||
|
<section class="shero night" aria-labelledby="shero-title">
|
||||||
|
<div class="container shero__inner">
|
||||||
|
<div class="shero__text">
|
||||||
|
<p class="eyebrow">{s.eyebrow}</p>
|
||||||
|
<h1 id="shero-title" class="display shero__title">
|
||||||
|
{s.title.map((line, i) => <span class:list={['line', { 'line--muted': i > 0 }]}>{line}</span>)}
|
||||||
|
</h1>
|
||||||
|
<p class="shero__lead">{s.lead}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form class="signup hud-frame" id="notify" action="/notify" method="post" lang={lang} aria-labelledby="notify-title">
|
||||||
|
<div class="signup__head">
|
||||||
|
<h2 id="notify-title" class="signup__title">{n.title}</h2>
|
||||||
|
<p class="signup__body">{n.body}</p>
|
||||||
|
</div>
|
||||||
|
<input type="hidden" name="lang" value={lang} />
|
||||||
|
<input type="hidden" name="list" value="cipherverifier-alpha" />
|
||||||
|
<p class="visually-hidden" aria-hidden="true">
|
||||||
|
<label for="notify-website">{ui.contact.form.honeypot}</label>
|
||||||
|
<input type="text" id="notify-website" name="website" tabindex="-1" autocomplete="off" />
|
||||||
|
</p>
|
||||||
|
<div class="signup__field">
|
||||||
|
<label class="signup__label" for="notify-email">{n.email}</label>
|
||||||
|
<input class="signup__input" id="notify-email" name="email" type="email" autocomplete="email" placeholder={n.placeholder} required />
|
||||||
|
</div>
|
||||||
|
<div class="signup__consent">
|
||||||
|
<input id="notify-consent" name="consent" type="checkbox" value="yes" required />
|
||||||
|
<label for="notify-consent">{n.consent} <a href={localePath(lang, '/privacy')}>{n.privacy}</a></label>
|
||||||
|
</div>
|
||||||
|
<button class="button signup__submit" type="submit">{n.submit}</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<!-- Status strip: the menu bar's HUD language, set along the foot of the hero. -->
|
||||||
|
<dl class="readout hud-frame" aria-label={s.status.label}>
|
||||||
|
<div class="readout__cell readout__cell--head">{s.status.label}</div>
|
||||||
|
{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>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.line { display: block; }
|
||||||
|
.line--muted { color: var(--ink-muted); }
|
||||||
|
.shero__inner {
|
||||||
|
display: grid;
|
||||||
|
gap: var(--space-8);
|
||||||
|
padding-block: var(--space-9) var(--space-7);
|
||||||
|
min-height: calc(100svh - var(--header-height));
|
||||||
|
align-content: end;
|
||||||
|
}
|
||||||
|
.shero__text { display: grid; gap: var(--space-5); justify-items: start; }
|
||||||
|
.shero__title { text-wrap: balance; }
|
||||||
|
.shero__lead { max-width: 26em; color: var(--ink-muted); }
|
||||||
|
|
||||||
|
/* Fade the headline in line by line: the page's one load moment. */
|
||||||
|
.shero__title .line, .shero__lead, .signup, .readout {
|
||||||
|
opacity: 0;
|
||||||
|
animation: shero-in var(--dur-slow) var(--ease) forwards;
|
||||||
|
}
|
||||||
|
.shero__title .line:nth-child(1) { animation-delay: 120ms; }
|
||||||
|
.shero__title .line:nth-child(2) { animation-delay: 280ms; }
|
||||||
|
.shero__lead { animation-delay: 440ms; }
|
||||||
|
.signup, .readout { animation-delay: 600ms; }
|
||||||
|
@keyframes shero-in { to { opacity: 1; } }
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.shero__title .line, .shero__lead, .signup, .readout { animation: none; opacity: 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sign-up: a framed instrument panel. */
|
||||||
|
.signup {
|
||||||
|
--hud-line: var(--hud-line-on-night);
|
||||||
|
display: grid;
|
||||||
|
gap: var(--space-5);
|
||||||
|
padding: var(--space-6);
|
||||||
|
align-content: start;
|
||||||
|
justify-items: start;
|
||||||
|
}
|
||||||
|
.signup__head { display: grid; gap: var(--space-2); }
|
||||||
|
.signup__title { font-size: var(--step-h3); letter-spacing: var(--track-h3); line-height: var(--leading-h3); font-weight: var(--weight-medium); }
|
||||||
|
.signup__body { color: var(--ink-muted); }
|
||||||
|
.signup__field { display: grid; gap: var(--space-2); width: 100%; }
|
||||||
|
.signup__label { font-size: var(--step-small); line-height: var(--leading-small); color: var(--ink-muted); }
|
||||||
|
.signup__input {
|
||||||
|
width: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
background: transparent;
|
||||||
|
border: 0;
|
||||||
|
border-bottom: var(--hairline) solid var(--line);
|
||||||
|
border-radius: 0;
|
||||||
|
padding: var(--space-3) 0;
|
||||||
|
transition: border-color var(--dur-fast) var(--ease);
|
||||||
|
}
|
||||||
|
.signup__input::placeholder { color: var(--ink-muted); opacity: 0.7; }
|
||||||
|
.signup__input:focus-visible { border-bottom-color: currentColor; }
|
||||||
|
.signup__consent {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto 1fr;
|
||||||
|
gap: var(--space-3);
|
||||||
|
align-items: start;
|
||||||
|
font-size: var(--step-small);
|
||||||
|
line-height: var(--leading-small);
|
||||||
|
color: var(--ink-muted);
|
||||||
|
}
|
||||||
|
.signup__consent input { width: 1.1em; height: 1.1em; margin: 0.15em 0 0; accent-color: var(--paper); }
|
||||||
|
.signup__consent a { color: var(--ink); text-decoration: underline; text-decoration-thickness: var(--hairline); text-underline-offset: 0.2em; }
|
||||||
|
|
||||||
|
/* Readout: stacked on small screens, one strip of cells on wide ones. */
|
||||||
|
.readout {
|
||||||
|
--hud-line: var(--hud-line-on-night);
|
||||||
|
display: grid;
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: var(--step-micro);
|
||||||
|
letter-spacing: var(--track-caps);
|
||||||
|
text-transform: uppercase;
|
||||||
|
line-height: var(--leading-small);
|
||||||
|
}
|
||||||
|
.readout__cell {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 8rem 1fr;
|
||||||
|
gap: var(--space-4);
|
||||||
|
padding: var(--space-3) var(--space-4);
|
||||||
|
}
|
||||||
|
.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 { width: 0.5rem; height: 0.5rem; border-radius: 50%; background: var(--signal); flex: none; }
|
||||||
|
|
||||||
|
@media (max-width: 30rem) {
|
||||||
|
.signup { padding: var(--space-5); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wide: headline across seven columns, sign-up across four beside it, the strip along the foot. */
|
||||||
|
@media (min-width: 64rem) {
|
||||||
|
.shero__inner {
|
||||||
|
grid-template-columns: repeat(12, minmax(0, 1fr));
|
||||||
|
column-gap: var(--gap);
|
||||||
|
row-gap: var(--space-9);
|
||||||
|
align-items: end;
|
||||||
|
}
|
||||||
|
.shero__text { grid-column: 1 / span 7; }
|
||||||
|
.signup { grid-column: 9 / span 4; }
|
||||||
|
.readout { grid-column: 1 / -1; grid-template-columns: 1fr 2fr 2fr 2fr; }
|
||||||
|
.readout__cell { grid-template-columns: 1fr; gap: var(--space-1); padding: var(--space-4) var(--space-5); }
|
||||||
|
.readout__cell + .readout__cell { border-top: 0; border-left: var(--hairline) solid var(--line-on-night); }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
/* Site-wide switches. Flip a flag and rebuild; nothing else needs to change. */
|
||||||
|
|
||||||
|
/* While true, /cipherverifier shows the coming-soon page with the alpha sign-up instead of the
|
||||||
|
full product page. The full page (src/components/verifier/*) is kept as it is. */
|
||||||
|
export const verifierComingSoon = true;
|
||||||
@@ -215,6 +215,51 @@ const en = {
|
|||||||
body: 'Bring a repository you know well, and we will show you what CipherVerifier makes of it: what it finds, what it rules out, and why.',
|
body: 'Bring a repository you know well, and we will show you what CipherVerifier makes of it: what it finds, what it rules out, and why.',
|
||||||
cta: 'Talk to us',
|
cta: 'Talk to us',
|
||||||
},
|
},
|
||||||
|
soon: {
|
||||||
|
eyebrow: 'Product',
|
||||||
|
title: ['CipherVerifier.', 'Coming soon.'],
|
||||||
|
lead: 'A self-hosted verification pipeline for software. Currently in a closed demonstrator phase.',
|
||||||
|
status: {
|
||||||
|
label: 'System status',
|
||||||
|
rows: [
|
||||||
|
{ key: 'Phase', value: 'Closed demonstrator', live: true },
|
||||||
|
{ key: 'Next', value: 'Alpha release' },
|
||||||
|
{ key: 'Deployment', value: 'Self-hosted' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
notify: {
|
||||||
|
title: 'Register for the alpha.',
|
||||||
|
body: 'One email when the first alpha is released. Nothing else.',
|
||||||
|
email: 'Work email',
|
||||||
|
placeholder: 'name@company.com',
|
||||||
|
consent: 'CipherCell may store my email address to notify me about the CipherVerifier alpha. I can withdraw this at any time.',
|
||||||
|
privacy: 'Privacy policy',
|
||||||
|
submit: 'Register',
|
||||||
|
},
|
||||||
|
track: {
|
||||||
|
title: 'Release path.',
|
||||||
|
body: 'Access opens in stages. Registered addresses hear first when it moves.',
|
||||||
|
steps: [
|
||||||
|
{ state: 'Now', name: 'Closed demonstrator', body: 'Running in a closed setting, by invitation only.' },
|
||||||
|
{ state: 'Next', name: 'Alpha', body: 'The first release outside the demonstrator. Registered addresses are told first.' },
|
||||||
|
{ 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' },
|
||||||
|
],
|
||||||
|
figure: 'Figure 1. One run, five stages.',
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
team: {
|
team: {
|
||||||
title: ['Researchers,', 'engineers,', 'architects.'],
|
title: ['Researchers,', 'engineers,', 'architects.'],
|
||||||
@@ -451,6 +496,51 @@ const de: typeof en = {
|
|||||||
body: 'Bringen Sie ein Repository mit, das Sie gut kennen, und wir zeigen Ihnen, was CipherVerifier daraus macht: was es findet, was es ausschließt, und warum.',
|
body: 'Bringen Sie ein Repository mit, das Sie gut kennen, und wir zeigen Ihnen, was CipherVerifier daraus macht: was es findet, was es ausschließt, und warum.',
|
||||||
cta: 'Sprechen Sie mit uns',
|
cta: 'Sprechen Sie mit uns',
|
||||||
},
|
},
|
||||||
|
soon: {
|
||||||
|
eyebrow: 'Produkt',
|
||||||
|
title: ['CipherVerifier.', 'Demnächst.'],
|
||||||
|
lead: 'Eine selbst gehostete Verifikations-Pipeline für Software. Derzeit in einer geschlossenen Demonstratorphase.',
|
||||||
|
status: {
|
||||||
|
label: 'Systemstatus',
|
||||||
|
rows: [
|
||||||
|
{ key: 'Phase', value: 'Geschlossener Demonstrator', live: true },
|
||||||
|
{ key: 'Als Nächstes', value: 'Alpha-Release' },
|
||||||
|
{ key: 'Betrieb', value: 'Selbst gehostet' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
notify: {
|
||||||
|
title: 'Für die Alpha registrieren.',
|
||||||
|
body: 'Eine E-Mail, sobald die erste Alpha erscheint. Sonst nichts.',
|
||||||
|
email: 'Geschäftliche E-Mail',
|
||||||
|
placeholder: 'name@firma.de',
|
||||||
|
consent: 'CipherCell darf meine E-Mail-Adresse speichern, um mich über die Alpha von CipherVerifier zu benachrichtigen. Ich kann dies jederzeit widerrufen.',
|
||||||
|
privacy: 'Datenschutzerklärung',
|
||||||
|
submit: 'Registrieren',
|
||||||
|
},
|
||||||
|
track: {
|
||||||
|
title: 'Weg zum Release.',
|
||||||
|
body: 'Der Zugang öffnet sich in Stufen. Registrierte Adressen erfahren zuerst, wenn es weitergeht.',
|
||||||
|
steps: [
|
||||||
|
{ state: 'Jetzt', name: 'Geschlossener Demonstrator', body: 'Läuft in einem geschlossenen Rahmen, nur auf Einladung.' },
|
||||||
|
{ state: 'Als Nächstes', name: 'Alpha', body: 'Das erste Release außerhalb des Demonstrators. Registrierte Adressen erfahren es zuerst.' },
|
||||||
|
{ 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' },
|
||||||
|
],
|
||||||
|
figure: 'Abbildung 1. Ein Lauf, fünf Stufen.',
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
team: {
|
team: {
|
||||||
title: ['Forschende,', 'Ingenieure,', 'Architekten.'],
|
title: ['Forschende,', 'Ingenieure,', 'Architekten.'],
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ import HowItWorks from '../components/verifier/HowItWorks.astro';
|
|||||||
import Cards from '../components/verifier/Cards.astro';
|
import Cards from '../components/verifier/Cards.astro';
|
||||||
import Facts from '../components/verifier/Facts.astro';
|
import Facts from '../components/verifier/Facts.astro';
|
||||||
import Outro from '../components/verifier/Outro.astro';
|
import Outro from '../components/verifier/Outro.astro';
|
||||||
|
import SoonHero from '../components/verifier/SoonHero.astro';
|
||||||
|
import ComingSoon from '../components/verifier/ComingSoon.astro';
|
||||||
|
import { verifierComingSoon } from '../data/flags';
|
||||||
import { t, type Lang } from '../i18n/ui';
|
import { t, type Lang } from '../i18n/ui';
|
||||||
|
|
||||||
interface Props { lang: Lang }
|
interface Props { lang: Lang }
|
||||||
@@ -13,10 +16,19 @@ const { lang } = Astro.props;
|
|||||||
const ui = t(lang);
|
const ui = t(lang);
|
||||||
---
|
---
|
||||||
<Base lang={lang} title={ui.meta.verifier.title} description={ui.meta.verifier.description}>
|
<Base lang={lang} title={ui.meta.verifier.title} description={ui.meta.verifier.description}>
|
||||||
<Hero lang={lang} />
|
{verifierComingSoon ? (
|
||||||
<Intro lang={lang} />
|
<>
|
||||||
<HowItWorks lang={lang} />
|
<SoonHero lang={lang} />
|
||||||
<Cards lang={lang} />
|
<ComingSoon lang={lang} />
|
||||||
<Facts lang={lang} />
|
</>
|
||||||
<Outro lang={lang} />
|
) : (
|
||||||
|
<>
|
||||||
|
<Hero lang={lang} />
|
||||||
|
<Intro lang={lang} />
|
||||||
|
<HowItWorks lang={lang} />
|
||||||
|
<Cards lang={lang} />
|
||||||
|
<Facts lang={lang} />
|
||||||
|
<Outro lang={lang} />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</Base>
|
</Base>
|
||||||
@@ -17,6 +17,9 @@ const ui = t('de');
|
|||||||
<h2>Kontaktaufnahme</h2>
|
<h2>Kontaktaufnahme</h2>
|
||||||
<p>Wenn Sie uns per E-Mail oder auf anderem Wege kontaktieren, speichern wir Ihre übermittelten personenbezogenen Daten (z. B. Name, E-Mail-Adresse, Inhalt der Nachricht), um Ihre Anfrage zu bearbeiten. Diese Daten werden ausschließlich für die Kommunikation mit Ihnen verwendet und nicht an Dritte weitergegeben. Rechtsgrundlage für die Verarbeitung der Daten ist Art. 6 Abs. 1 lit. b DSGVO (Anbahnung eines Vertragsverhältnisses) oder Art. 6 Abs. 1 lit. f DSGVO (berechtigtes Interesse an der Bearbeitung der Anfrage).</p>
|
<p>Wenn Sie uns per E-Mail oder auf anderem Wege kontaktieren, speichern wir Ihre übermittelten personenbezogenen Daten (z. B. Name, E-Mail-Adresse, Inhalt der Nachricht), um Ihre Anfrage zu bearbeiten. Diese Daten werden ausschließlich für die Kommunikation mit Ihnen verwendet und nicht an Dritte weitergegeben. Rechtsgrundlage für die Verarbeitung der Daten ist Art. 6 Abs. 1 lit. b DSGVO (Anbahnung eines Vertragsverhältnisses) oder Art. 6 Abs. 1 lit. f DSGVO (berechtigtes Interesse an der Bearbeitung der Anfrage).</p>
|
||||||
|
|
||||||
|
<h2>Benachrichtigung zur CipherVerifier-Alpha</h2>
|
||||||
|
<p>Wenn Sie sich für die Benachrichtigung zur Alpha von CipherVerifier eintragen, speichern wir Ihre E-Mail-Adresse und den Zeitpunkt Ihrer Einwilligung, um Ihnen mitzuteilen, wann die erste Alpha veröffentlicht ist. Wir nutzen sie für nichts anderes und geben sie nicht an Dritte weiter. Rechtsgrundlage ist Ihre Einwilligung nach Art. 6 Abs. 1 lit. a DSGVO. Sie können Ihre Einwilligung jederzeit per E-Mail an <a href="mailto:office@ciphercell.de">office@ciphercell.de</a> widerrufen; wir löschen Ihre Adresse dann. Nach dem Versand der Benachrichtigung löschen wir sie ebenfalls.</p>
|
||||||
|
|
||||||
<h2>Recht auf Auskunft, Berichtigung, Sperrung oder Löschung</h2>
|
<h2>Recht auf Auskunft, Berichtigung, Sperrung oder Löschung</h2>
|
||||||
<p>Nach dem anwendbaren Datenschutzrecht haben Sie ein Recht auf unentgeltliche Auskunft über Ihre gespeicherten Daten sowie ggf. ein Recht auf Berichtigung, Sperrung oder Löschung dieser Daten. Wenden Sie sich bei weitergehenden Fragen zum Datenschutz und zur Verarbeitung Ihrer personenbezogenen Daten, bei Auskünften oder zur Ausübung Ihres Rechts auf Berichtigung, Sperrung oder Löschung oder für den Fall des Widerrufs einer Einwilligung bitte per E-Mail an <a href="mailto:office@ciphercell.de">office@ciphercell.de</a>. Selbstverständlich können Sie auch die im Impressum angegebenen Kontaktdaten oder das Kontaktformular auf der Website nutzen. CipherCell verarbeitet und nutzt die von Ihnen mitgeteilten Informationen ausschließlich für die Bearbeitung Ihres Anliegens.</p>
|
<p>Nach dem anwendbaren Datenschutzrecht haben Sie ein Recht auf unentgeltliche Auskunft über Ihre gespeicherten Daten sowie ggf. ein Recht auf Berichtigung, Sperrung oder Löschung dieser Daten. Wenden Sie sich bei weitergehenden Fragen zum Datenschutz und zur Verarbeitung Ihrer personenbezogenen Daten, bei Auskünften oder zur Ausübung Ihres Rechts auf Berichtigung, Sperrung oder Löschung oder für den Fall des Widerrufs einer Einwilligung bitte per E-Mail an <a href="mailto:office@ciphercell.de">office@ciphercell.de</a>. Selbstverständlich können Sie auch die im Impressum angegebenen Kontaktdaten oder das Kontaktformular auf der Website nutzen. CipherCell verarbeitet und nutzt die von Ihnen mitgeteilten Informationen ausschließlich für die Bearbeitung Ihres Anliegens.</p>
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ const ui = t('en');
|
|||||||
<h2>Contact</h2>
|
<h2>Contact</h2>
|
||||||
<p>If you contact us by email or other means, we will store the personal data you provide (e.g. name, email address, content of the message) in order to process your request. This data will be used exclusively for communication with you and will not be passed on to third parties. The legal basis for processing the data is Art. 6 para. 1 lit. b GDPR (initiation of a contractual relationship) or Art. 6 para. 1 lit. f GDPR (legitimate interest in processing the request).</p>
|
<p>If you contact us by email or other means, we will store the personal data you provide (e.g. name, email address, content of the message) in order to process your request. This data will be used exclusively for communication with you and will not be passed on to third parties. The legal basis for processing the data is Art. 6 para. 1 lit. b GDPR (initiation of a contractual relationship) or Art. 6 para. 1 lit. f GDPR (legitimate interest in processing the request).</p>
|
||||||
|
|
||||||
|
<h2>CipherVerifier alpha notification</h2>
|
||||||
|
<p>If you register for the CipherVerifier alpha notification, we store your email address and the time of your consent in order to tell you when the first alpha is released. We use it for nothing else and do not pass it on to third parties. The legal basis is your consent under Art. 6 para. 1 lit. a GDPR. You can withdraw your consent at any time by email to <a href="mailto:office@ciphercell.de">office@ciphercell.de</a>; we then delete your address. We also delete it once the notification has been sent.</p>
|
||||||
|
|
||||||
<h2>Right to information, correction, blocking, or deletion</h2>
|
<h2>Right to information, correction, blocking, or deletion</h2>
|
||||||
<p>Under applicable data protection law, you have the right to obtain information about your stored data free of charge and, if necessary, the right to correct, block, or delete this data. If you have any further questions about data protection and the processing of your personal data, if you require information, or if you wish to exercise your right to correction, blocking, or deletion, or in the event of revocation of consent, please send an email to <a href="mailto:office@ciphercell.de">office@ciphercell.de</a>. You can also use the contact details provided in the legal notice or the contact form on the website. CipherCell processes and uses the information you provide exclusively for the purpose of processing your request.</p>
|
<p>Under applicable data protection law, you have the right to obtain information about your stored data free of charge and, if necessary, the right to correct, block, or delete this data. If you have any further questions about data protection and the processing of your personal data, if you require information, or if you wish to exercise your right to correction, blocking, or deletion, or in the event of revocation of consent, please send an email to <a href="mailto:office@ciphercell.de">office@ciphercell.de</a>. You can also use the contact details provided in the legal notice or the contact form on the website. CipherCell processes and uses the information you provide exclusively for the purpose of processing your request.</p>
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,10 @@
|
|||||||
--field-slate: #3A4862;
|
--field-slate: #3A4862;
|
||||||
--field-plum: #66508E;
|
--field-plum: #66508E;
|
||||||
|
|
||||||
|
/* Signal: the one status tone the brief allows. Used only for a live state (the coming-soon
|
||||||
|
page's current phase), at most twice per page. Never for text, links or decoration. */
|
||||||
|
--signal: #58C8D8;
|
||||||
|
|
||||||
/* Immutable copies for resetting inside a .night or overlay context */
|
/* Immutable copies for resetting inside a .night or overlay context */
|
||||||
--ink-base: #1A1A18;
|
--ink-base: #1A1A18;
|
||||||
--ink-muted-base: #57564F;
|
--ink-muted-base: #57564F;
|
||||||
|
|||||||
Reference in new issue
Block a user