12 KiB
CipherCell homepage relaunch
Static site for www.ciphercell.de, built with Astro. Design foundation: CLAUDE.md
(European deep-tech minimalism: warm paper, one neo-grotesque, hairlines, no accent colour).
Run
npm install
npm run dev # http://localhost:4321
npm run build # static output in dist/
npm run preview # serve dist/
npm run check # Astro + TypeScript check
Where things live
| Path | Purpose |
|---|---|
src/styles/tokens.css |
Every colour, type step, spacing, radius and duration. Components use only these. |
src/styles/base.css |
Font faces, reset, focus ring, primitives (.container, .grid, .section, .prose, .link, .button). |
src/i18n/ui.ts |
All interface copy, English and German, one object per language with the same shape. |
src/data/team.ts |
Team members, roles, portraits, links. |
src/content/insights/*.md |
Insights posts (content collection). Front matter: title, date, lang, category, readingTime, author, cover. |
src/components/home/* |
One file per home section, in page order: Hero, Manifesto, Method (with MethodDiagram), Stages (with GridObject), Sectors, Proof, Quote, Insights, Contact. |
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/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: hero on a build-time SVG relief of ridge lines in the logo blues, under the floating header, "Coming soon." with the alpha sign-up beside it, a status strip, and the product name set across the full width and cut by the hero's lower edge, 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. |
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/assets/images/ |
Source images. Astro emits AVIF with a WebP fallback and explicit dimensions. |
public/fonts/ |
Self-hosted Inter Tight variable (latin, latin-ext). |
public/video/ |
The home hero's background loop, MP4 and WebM. Shipped as-is; Astro does not process video. |
Routes
/, /cipherverifier, /team, /insights, /insights/<slug>, /imprint, /privacy, /404 and the same under /de.
hreflang alternates and a sitemap are generated. The language switch sits in the footer.
Things the host has to provide
- Contact form. The form posts
first_name,last_name,email,messageand a honeypot fieldwebsitetoPOST /contact, as the current site does. The handler is server-side and not part of this static build. - Alpha sign-up. While
verifierComingSooninsrc/data/flags.tsis true,/cipherverifiershows a coming-soon page whose form postsemail,consent=yes,lang,list=cipherverifier-alphaand the honeypotwebsitetoPOST /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 snippet itself is not included; add it insrc/layouts/Base.astroif analytics should continue.
Adding a post
Create src/content/insights/<slug>.md with the front matter used in xg-aloe-user-forum.md.
Put the cover image in src/assets/images/ and reference it with a relative path.
The three newest posts appear on the home page automatically.
The card drawings
The objects on the stage cards ("What we do.") and the product cards do not animate on arrival;
they are simply drawn. Hovering a card sets --object-grid, which the drawing answers by letting
its grid recede to 26% while the lines that carry the idea stay: the cube's own edges, the tile
lifted out of the plane, the wave's profiles, the inner cube inside its shell, the column to fix
first. The card also sets --object-scale. Both are custom properties the card owns and the
drawing responds to, so the interaction lives with the card and the response with the drawing.
It is behind @media (hover: hover) so touch never sticks in it, and it is the same language as
the hover focus on the product page schematics.
The hero video
The home hero plays public/video/hero-plant.mp4 / .webm behind the headline, over the
plant-at-dusk.jpg still. The still is the video's own first frame, so it carries the hero until
the video is ready and stays put for anyone who never gets one.
The video is only fetched when it will be seen: not under prefers-reduced-motion, not on
saveData, and not below 48rem, where a 21:9 frame would be cropped to a sliver for the most
expensive bytes. The sources are attached by script rather than in the markup, so nothing is
requested otherwise. A blocked autoplay also just leaves the still in place.
The source clip was generated with Magnific (Kling 2.5, 10 s, from the hero still as its start
frame, https://www.magnific.com/app/creation/5jPaI3qKxe). scripts/encode-hero-video.sh turns a
clip into the looping file: it plays forward, then backward, so the loop point cannot be seen, and
encodes 1920-wide H.264 and VP9. A 20-second loop lands at 862 KB and 472 KB. To replace it:
FFMPEG=/path/to/ffmpeg ./scripts/encode-hero-video.sh source.mp4 public/video/hero-plant 1920
Keep the first frame of any replacement equal to plant-at-dusk.jpg, or replace both together.
Imagery
The mast at dusk (mast-at-dusk.jpg) is the one documentary photograph from the old site; it
remains the cover of the first post. The five other scene images were generated with Magnific
(Seedream 5 Pro) on 18 September 2026 in a desaturated documentary style, because no real
photography existed:
| File | Used in | Magnific creation |
|---|---|---|
plant-at-dusk.jpg |
Hero | https://www.magnific.com/app/creation/DoQZmiipcl |
engineer-glass.jpg |
Method section | https://www.magnific.com/app/creation/3zdScUIREY |
sector-5g.jpg |
Sectors, tab 1 | https://www.magnific.com/app/creation/s7ZQP52l8e |
sector-substation.jpg |
Sectors, tab 2 | https://www.magnific.com/app/creation/yi0r27vPW9 |
sector-datacentre.jpg |
Sectors, tab 3 | https://www.magnific.com/app/creation/eIHAvetdqL |
They are placeholders in the sense of the brief: swap them for the company's own documentary
photography when it exists, keeping the same file names and the img-doc treatment. The grid
objects on the stage cards were prototyped as a Magnific sheet
(https://www.magnific.com/app/creation/aFsD5crfSh) and then rebuilt as procedural SVG so the
stroke matches the rest of the site.
Product page
/cipherverifier describes the pipeline in product_repo/monocipher (mounted for reference, ignored
by git). Everything on the page was read from that repository on 18 September 2026:
- The five stages and their order come from
orchestrator/pipeline_plan.py. - The seventeen analysers are rayfusion's built-in plugins plus converters; the fourteen languages
are the
Technologyenum inrayfusion/plugins/base.py. The security profile is described indocs/rayfusion-rule-policy.md. - The two-stage classification, escalation rules, budgets and replay-validated cache are from
docs/polygraph-classification-pipeline.md. - 970 is the number of entries in
sarifcombiner/cwe_information.json. - SBOM merging (Syft and cdxgen, package-URL deduplication, CycloneDX 1.6) is
spectograph/backend, Dependency-Track upload and the ephemeral container aredeptrace/commands. - Git providers, archive upload, CI webhook, scoped settings, live pipeline, PDF report and artefacts
are the
cipherverifierDjango app; the installer iscvm.
When the product changes, update ui.verifier (copy and diagram labels) and, if a stage or tool is
added, the layouts in diagrams.ts. All five schematics are on the page; scroll-shots.mjs renders
them for review.
Interaction and motion
Three islands of script: the mobile navigation toggle in Header.astro, the sectors tabs in
Sectors.astro (src/scripts/tabs.ts: ARIA tablist, arrow keys, Home and End), and the reveal
observer used by the product page's drawings.
Motion on the product page, all of it on wide screens: the hero's seventeen lines draw in on load
and meet in one point (off under prefers-reduced-motion); hovering a node in a schematic dims
everything that does not belong to it. The drawings themselves do not animate.
"How CipherVerifier works" follows the pattern of arcos.eu/technology. On wide screens the title and a bordered panel hold still while the page scrolls; the step list under the title advances, one step open at a time, and the panel crossfades to that step's drawing. The section is as tall as one pitch per step plus one panel height, so the panel stays pinned until the last step has been read. Which step is open is decided by invisible triggers tiling that height: whichever crosses the middle of the viewport wins, and the triggers start half a viewport down so the first step turns over exactly when the panel pins. A step's title is a button that scrolls to it.
The panel is tied to the text, not to the pinned frame: it is positioned against
.how__text-inner, so it starts at the title and its width is stated as 200% of the text column
(4 of 12 against 8 of 12). That block is held at one height, measured once by script as the
collapsed list plus the tallest step's text, and centred in the pinned frame. So the title, the
list and the panel all sit in the middle of the dark area and none of them move as the steps
change, while each step's text still takes only the room it needs. Without script the first step stays open, the panel takes the text's own
height, and the section reads as a plain sequence. Phones get that plain sequence in any case:
every step open, the upright drawing under its text. This goes
beyond the home page's single load moment on purpose, at the client's request; the home page is
unchanged. Without JavaScript every sector panel
and its image is shown in full; the html.js class set in Base.astro switches to one at a time.