Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
62 changes: 62 additions & 0 deletions apps/web/src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -6433,6 +6433,68 @@ body {
}
}

/* Tricorder page — the hero verdict loop (TricorderHeroVerdict). Dashes flow
along each input stream toward the package, and a scan line passes over the
package once per stage (the element is re-keyed per stage, so it restarts).
The loop itself is JS-driven and stops under reduced motion; these are off
there too. */
@keyframes cs-tri-stream {
to {
stroke-dashoffset: -20;
}
}
@keyframes cs-tri-scanline {
from {
transform: translateY(0);
opacity: 0;
}
15% {
opacity: 1;
}
85% {
opacity: 1;
}
to {
transform: translateY(132px);
opacity: 0;
}
}
@keyframes cs-tri-orbit {
to {
transform: rotate(360deg);
}
}
@keyframes cs-tri-intel-pulse {
from {
transform: scale(1);
opacity: 0.9;
}
to {
transform: scale(2.6);
opacity: 0;
}
}
.cs-tri-stream {
animation: cs-tri-stream 0.8s linear infinite;
}
.cs-tri-orbit {
animation: cs-tri-orbit 9s linear infinite;
}
.cs-tri-intel-pulse {
animation: cs-tri-intel-pulse 0.9s cubic-bezier(0.16, 1, 0.3, 1) both;
}
.cs-tri-scanline {
animation: cs-tri-scanline 0.85s ease-in-out both;
}
@media (prefers-reduced-motion: reduce) {
.cs-tri-stream,
.cs-tri-scanline,
.cs-tri-orbit,
.cs-tri-intel-pulse {
animation: none;
}
}

/* Vulnerability Remediation hero: "remediate at the source" panorama
(VulnHeroScene.tsx). CVE chips ride `--dx`/`--dy` into the gate and a
`cs-vuln-flash` with the same duration and delay fires as each one lands.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
"use client";

import { LazyMotion, domAnimation, m, useReducedMotion } from "motion/react";
import { LazyMotion, domAnimation, m } from "motion/react";
import { useCallback, useEffect, useRef, useState } from "react";
import { EASE_OUT } from "@/lib/motion";
import { useHydratedReducedMotion } from "@/lib/use-hydrated-reduced-motion";

const AUTOPLAY_MS = 6500;
const SWIPE_THRESHOLD = 48;
Expand Down Expand Up @@ -38,7 +39,7 @@ export function UpcomingEventCarousel({
// +1 = advancing forward (slide in from the right), -1 = going back.
const [direction, setDirection] = useState(1);
const [paused, setPaused] = useState(false);
const reduceMotion = useReducedMotion();
const reduceMotion = useHydratedReducedMotion();

const go = useCallback(
(next: number, dir: number) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

import type React from 'react';
import { useEffect, useRef, useState } from 'react';
import { useReducedMotion } from 'motion/react';
import { Reveal, RevealStagger, RevealItem } from '@/components/ui/Reveal';
import { useHydratedReducedMotion } from '@/lib/use-hydrated-reduced-motion';

/*
* "Risk Enters Long Before Production" — a continuous supply-chain scan.
Expand Down Expand Up @@ -1520,7 +1520,7 @@ function ScanRail({
/* -------------------------------------------------------------------------- */

export function FinanceRiskChain(): React.ReactElement {
const reduceMotion = useReducedMotion();
const reduceMotion = useHydratedReducedMotion();
const animated = !reduceMotion;
const { beamRef, scan } = useScanClock(animated);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
"use client";

import { Fragment, useEffect, useRef, useState } from "react";
import { useInView, useReducedMotion } from "motion/react";
import { useInView } from "motion/react";

import { cn } from "@/lib/cn";
import { useHydratedReducedMotion } from "@/lib/use-hydrated-reduced-motion";
import { FlowArrow } from "@/components/ui/FlowArrow";
import { ShieldCheck } from "./ShieldCheck";
import { VerifyBeams } from "./VerifyBeams";
Expand Down Expand Up @@ -174,7 +175,7 @@ function GuaranteeCard({

export function SecurityDiagram(): React.ReactElement {
const ref = useRef<HTMLDivElement>(null);
const reduce = useReducedMotion();
const reduce = useHydratedReducedMotion();
const inView = useInView(ref, { margin: "0px 0px -80px 0px", amount: 0.05 });
const [started, setStarted] = useState(false);
const [hovered, setHovered] = useState<number | null>(null);
Expand Down
13 changes: 8 additions & 5 deletions apps/web/src/components/sections/tricorder/TricorderContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@ function RelationshipsContent({ accent }: { accent: string }): React.ReactElemen
}}
/>
) : null}
<div className="flex w-[92px] flex-col items-center gap-3">
<div className="flex w-[80px] flex-col items-center gap-3">
<ItemTile accent={accent} glyph={item.glyph} />
<ItemLabel>{item.label}</ItemLabel>
</div>
Expand Down Expand Up @@ -466,11 +466,14 @@ function VerdictCard(): React.ReactElement {
/* ── Desktop scene: fixed 1320 × 538 canvas, scaled to fit ───────────────── */

const VB = { w: 1320, h: 538 } as const;
/** Panel geometry. Widths differ because the doc's three panels carry different loads. */
/**
* Panel geometry: equal thirds, so the Behavior panel is centred on CARD_X and
* its current drops straight through the junction into the component.
*/
const PANELS = {
history: { x: 0, w: 360 },
behavior: { x: 386, w: 440 },
relationships: { x: 852, w: 468 },
history: { x: 0, w: 424 },
behavior: { x: 448, w: 424 },
relationships: { x: 896, w: 424 },
} as const;
const PANEL_Y = 0;
const PANEL_H = 200;
Expand Down
53 changes: 11 additions & 42 deletions apps/web/src/components/sections/tricorder/TricorderHero.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import Image from 'next/image';
import Link from 'next/link';

import { HeroReveal } from '@/components/ui/Reveal';
import { TricorderHeroVerdict } from './TricorderHeroVerdict';

/**
* Tricorder hero, on the solution-page hero shell (FinanceHero / SaasHero):
* bg-cs-hero mesh, gridline overlay, left-aligned copy with the gradient
* accent, a 3D artifact on the right and a fade into the white section below.
* accent and an artifact on the right. No bottom fade: the hero ends cleanly
* on its gradient.
*
* The artifact is the copy doc's own hero concept: the intelligence brain in a
* glass cube on a pedestal. The render was produced on black and matted to a
* real alpha channel, so it composites straight onto the gradient.
* The artifact is the verdict loop (TricorderHeroVerdict), built in code: one
* package examined by the four stages and judged before it is trusted. It is
* decorative, so phones drop it rather than stack it under the headline, as
* the other solution heroes do.
*/
export function TricorderHero(): React.ReactElement {
return (
Expand All @@ -27,20 +29,9 @@ export function TricorderHero(): React.ReactElement {
/>

<div
aria-hidden
className="pointer-events-none absolute inset-x-0 bottom-0 select-none"
style={{
height: '200px',
background:
'linear-gradient(180deg, rgba(255,255,255,0) 0%, rgba(255,255,255,0.45) 55%, rgba(255,255,255,0.92) 88%, #ffffff 100%)',
}}
/>

<div
className="relative mx-auto grid max-w-[var(--container-default)] items-center gap-8 px-6 sm:px-10 lg:grid-cols-[minmax(0,1fr)_minmax(0,1fr)] lg:gap-10"
className="relative mx-auto grid max-w-[var(--container-default)] items-center gap-8 px-6 pb-[72px] sm:px-10 md:pb-[clamp(72px,7vw,112px)] lg:grid-cols-[minmax(0,1fr)_minmax(0,1fr)] lg:gap-10"
style={{
paddingTop: 'calc(clamp(104px, 10vw, 150px) + var(--cs-header-extra))',
paddingBottom: 'clamp(96px, 9vw, 150px)',
}}
>
<div className="flex flex-col items-center text-center lg:items-start lg:text-left">
Expand All @@ -57,7 +48,7 @@ export function TricorderHero(): React.ReactElement {
}}
>
The Intelligence Layer{' '}
<span className="cs-text-gradient-impact">for Software Trust</span>
<span className="cs-text-gradient-impact inline-block">for Software Trust</span>
</h1>
</HeroReveal>

Expand Down Expand Up @@ -101,31 +92,9 @@ export function TricorderHero(): React.ReactElement {
y={40}
delay={0.2}
duration={1.0}
className="mx-auto w-full max-w-[560px] lg:max-w-[640px]"
className="mx-auto hidden w-full max-w-[440px] md:block lg:max-w-[520px]"
>
<div className="relative w-full" style={{ aspectRatio: '1300 / 975' }}>
{/* Halo, so the render sits in light rather than on flat navy. */}
<div
aria-hidden
className="pointer-events-none absolute inset-[6%] rounded-full select-none"
style={{
background:
'radial-gradient(closest-side, rgba(90,110,255,0.38) 0%, rgba(90,110,255,0) 72%)',
filter: 'blur(24px)',
}}
/>
<div className="cs-tri-float absolute inset-0">
<Image
src="/images/tricorder/hero-intelligence-cube.webp"
alt="A glowing glass cube holding a circuit-trace brain on a glass pedestal, surrounded by floating security panels"
fill
priority
sizes="(min-width: 1440px) 640px, (min-width: 1024px) 45vw, 560px"
className="select-none object-contain"
draggable={false}
/>
</div>
</div>
<TricorderHeroVerdict />
</HeroReveal>
</div>
</section>
Expand Down
Loading
Loading