Purple
Track — record app activity
#ede6f8- Accent
#a443f0- Card title
#b877f3- Learn-more
#c5a3ff- Card border
rgba(164,67,240,0.38)
Internal reference
The approved values and components for the Cognitive3D marketing site, rendered live rather than described. Specimens are the real components wherever possible — if one disagrees with a page, one of them is a bug.
Which sections exist, what surface each paints, and what each owes its neighbours. This is the section that would have caught the use-case overlap: the trust card declared a straddle, the band below it declared it could receive one, and nothing checked that the numbers agreed. Everything here is rendered from src/util/section-contract.ts and page-spines.ts, and the edge verdicts are probed against the shipped stylesheet on mount — so a wrong declaration shows up as a MISMATCH rather than as a wrong page.
Real components, real class names, real CSS, at real viewport width, in the adjacent PAIRS the contract governs. A seam is a relationship between two sections, so a specimen of one section on its own cannot show whether the seam works — which is exactly how the use-case overlap shipped past a design-system page that already had specimens on it. Media queries read the window, not these stages: to QA the mobile straddle use responsive mode at 375px and at 1024px.
The join that broke. The trust card is displaced past its own bottom edge and the band underneath has to clear it. On desktop the displacement is half the card's own height, applied as a transform, which layout cannot see. Below 1024px the straddle is dropped entirely, because the card measures 217–285px across the four pages and no single clearance serves a 68px spread. Check the heading below the card is never touched.
Specimen copy. This line is one of the three things the original overhang estimate forgot, and it is worth about 67px on a phone.
The same rule, the other straddle. This one displaces by a fixed -240px section margin below 1024px rather than a transform, which is why it is height-independent and has held: clearance is 330 − 240 = 90px whatever the quote length. It is the mechanic to copy. Note the card must float ON TOP of the band (z-index 16 against 2) while the section beneath stays under it — that has broken twice.
Specimen quote. Length matters here, because on desktop the overhang is half of whatever height this card ends up being.
Specimen intro line. The four steps are the pillar names in order, and the rail runs their colours.
Tracked. Specimen step copy.
Explored. Specimen step copy.
Analyzed. Specimen step copy.
Reported. Specimen step copy.
A tuck, not a straddle. The section above presents a curve; the section below carries a negative margin-top and pulls itself up UNDER it, so the transparent part of the arc reveals this section's surface instead of body white. Put a flat-bottomed section above a tucking one and a white crescent opens — that is rule R2. Look at the arc: no white should appear at either end of it.
The only section in the system with both edges flat: no negative margin, no mask, no displaced child, no z-index above 2. It needs nothing from its neighbours and owes them nothing, so it can be moved anywhere in a spine without arithmetic. Copy this shape for anything new unless there is a real reason not to — twelve of the fifteen archetypes carry a seam obligation, and every seam is a thing that can be got wrong.
Specimen intro line, roughly the length the real ones run to.
Specimen body copy, one or two lines.
Specimen body copy, one or two lines.
Specimen body copy, one or two lines.
Specimen body copy, one or two lines.
| Archetype | Surface | Top edge | Bottom edge | Component |
|---|---|---|---|---|
productHeroOpens a pillar page. Dome via clip-path: ellipse(), not a mask. | hero | flat | domed | ProductHero |
useCaseHeroOpens a use-case page. Its padding-bottom is JS-measured from the hero image height into --image-height, so the dome clears the artwork. | hero | flat | domed | UseCaseHero |
mapExperienceThe three pillar cards. Clips BOTH edges and carries margin-bottom -250px, so whatever follows it must tuck. | darkflat | flat | domed | UseCaseMapExperience |
outcomesWhat this audience has to prove. The only seam-free section in the system — copy it as the shape for new archetypes. | white | flat | flat | UseCaseOutcomes |
featureSectionOne pillar told as alternating image/text blocks. margin-top -120px, so it must follow a domed section. | wash | tucks | flat | HomeFeatureSection |
clientsTrust band: a logo card straddling the band below. Desktop straddles by translateY(50%); at <=1024px by a fixed -140px section margin. | wash | flat | straddles | UseCaseClients |
testimonialOne quote card straddling the band below. Overhang is half the card height on desktop, a fixed -240px at <=1024px. | wash | flat | straddles | HomeTestimonial |
ctaBandThe ask. Mask, grain, constellation, one or two buttons. | band | receives | domed | HomeCTASection |
ctaBandClosingThe last band on a page. No mask, flat bottom, meets the footer with no seam. Exactly one per page, and it must be the last band. | band | receives | flat | HomeCTASection (--straight-bottom) |
firstEvidenceAdding Cognitive3D: a spec sheet for an evaluator, carrying the mid-page ask. Renders AS a band because it must receive a straddle. | band | receives | domed | UseCaseFirstEvidence |
workedExampleOne session end to end, as the four pillar steps. Renders AS a band for the same reason as firstEvidence. | band | receives | domed | UseCaseWorkedExample |
platformSupportKeep-reading cards. Ships a light tint that all three pillar pages repaint #0c0b26 — treat dark as the real default. | darkflat | tucks | flat | PlatformSupportSection |
featureComparisonPlan comparison table. Bound to pricing tiers, not generic. | white | flat | flat | FeatureComparison |
useCaseIntroLegacy tick-list intro. Unity/Unreal only; superseded by outcomes. | white | flat | flat | UseCaseIntro |
aboutHeroOpens the About page. Artwork straddles the dome as on the use-case heroes, so the section reserves half the image height as padding and a sibling wrapper pulls the other half up. | hero | flat | domed | about.tsx §1 (bespoke; UseCaseHero forces two CTAs) |
aboutOriginWhy the company exists, as three beats alternating image right/left/right. Tucks 120px under the hero dome. | wash | tucks | flat | about.tsx §2 (bespoke, three feature-blocks) |
aboutTodayWhat the platform does, as the three pillar cards — DARK cards on a light wash. Was darkflat with an ellipse top and a band arc until both went: a curve between two surfaces of the same wash is invisible. | wash | flat | flat | about.tsx §3 (home-map-experience cards; swap to UseCaseMapExperience) |
aboutHowWeWorkHow the team works, with the Whistler photo. No tuck: the section above lost its arc when the pillar cards went light, so there is nothing to tuck under. | wash | flat | flat | about.tsx §5 (bespoke two-column) |
aboutUseCasesThe four audiences as a 2x2 of white cards on the wash. The outcomes component with a wash surface under it instead of white. | wash | flat | flat | UseCaseOutcomes (as .about-usecases) |
aboutBrandsFor over a decade: the 1,400 claim and the customer marquee that evidences it, directly after the origin story per the deck. Wash, not white — a white surface here made two flat seams that could not be curved, because #fff against #ede6f8 has no arc worth drawing. | wash | flat | flat | HomeLogos1 in frontpage-brands__section |
aboutTrustDataDeployment, security and support as a 3-column spec sheet. The only band on the page, which is why the quote above it has no other legal position. | band | receives | domed | about.tsx §8 (bespoke; swap to UseCaseFirstEvidence) |
aboutFounderThe founder, portrait left. Exists so Tony is not a 96px circle in the leadership row; his card is removed from aboutTeam because of it. | wash | flat | flat | about.tsx §6 (bespoke portrait + blurb) |
aboutTeamWho builds it, grouped with how-we-work as the two sections about the company rather than the product. No tuck: its neighbour above is flat-bottomed, so the 120px it used to pull moved to aboutBacked. | wash | flat | flat | about.tsx §6 (bespoke 4/2/1 grid) |
aboutBackedWho funds it. LIGHT — was dark with a curved top until that arc moved down to aboutClose. Tucks 120px into the trust band above, a job it inherited from aboutTeam when the team moved up to slot 6. | wash | tucks | flat | about.tsx §10 (bespoke investor row) |
aboutCloseThe ask, and the only dark section at the foot of the page. Curved top via clip-path with a -64px pull; flat into the footer. | band | self-covers | flat | about.tsx §11 (bespoke; swap to HomeCTASection --straight-bottom) |
Drawn from USE_CASE_SPINE in page-spines.ts, so it cannot disagree with what tsc checks. Reorder the spine and this diagram moves with it.
| From | To | Join |
|---|---|---|
useCaseHero | outcomes | plain join |
outcomes | mapExperience | plain join |
mapExperience | featureSection | featureSection tucks under mapExperience — R2 |
featureSection | clients | plain join |
clients | firstEvidence | straddles into firstEvidence — R1 |
firstEvidence | featureSection | featureSection tucks under firstEvidence — R2 |
featureSection | testimonial | plain join |
testimonial | workedExample | straddles into workedExample — R1 |
workedExample | featureSection | featureSection tucks under workedExample — R2 |
featureSection | testimonial | plain join |
testimonial | ctaBandClosing | straddles into ctaBandClosing — R1 |
Drawn from ABOUT_SPINE in page-spines.ts, so it cannot disagree with what tsc checks. Reorder the spine and this diagram moves with it.
| From | To | Join |
|---|---|---|
aboutHero | aboutOrigin | aboutOrigin tucks under aboutHero — R2 |
aboutOrigin | aboutToday | plain join |
aboutToday | aboutBrands | plain join |
aboutBrands | aboutHowWeWork | plain join |
aboutHowWeWork | aboutFounder | plain join |
aboutFounder | aboutTeam | plain join |
aboutTeam | aboutUseCases | plain join |
aboutUseCases | testimonial | plain join |
testimonial | aboutTrustData | straddles into aboutTrustData — R1 |
aboutTrustData | aboutBacked | aboutBacked tucks under aboutTrustData — R2 |
aboutBacked | aboutClose | aboutClose covers its own clipped top — no rule |
Pages join one at a time, each on its own branch. This is the order that works — steps 1 and 2 are where every past mistake happened.
bodyWrapperClass="frontpage <pagename>". Every rule in _front-page.scss is a descendant selector, so the page picks up the whole system for free. Hand-copying those rules into a page partial produced ~860 lines that had to be deleted again.var(--c3d-purple-wash), not #ede6f8. A page joining the system gets this for free; there is no separate migration step.usePageReveal with the page's selectors and bodyClass: 'fp-enhanced', plus the fp-scrolled toggle. Copy from track.tsx.home-cta-section--straight-bottom so it runs flat into whatever follows.Pages that predate the tokens — homepage, the three pillars, pricing, the demo funnel — still carry literals. That is deliberate. They get migrated when next touched for another reason, never as a bulk sweep; there is no upside to editing an approved page just to move where its numbers come from.
The single most confusing thing about this system: some of the July-2026 tightening was pushed into the shared base and applies to EVERY page including ones nobody has touched. The rest only arrives when a page opts in. Knowing which is which saves an afternoon.
Pushed into _base.scss, _navigation.scss and _footer.scss. An untouched page like /about or /partners is already using these, which is why those pages look tighter than they did.
type scale headings + bodythe "sharpened" sizes, weights and letter-spacingheader / nav redesignedincl. dropdown panel and mobile menufooter redesignedfocus rings site-wide--c3d-* tokens declared on :rootavailable everywhere, consumed only where writtenfrontpage classScoped to .frontpage in _front-page.scss. A page without that class gets none of it, and the nav even behaves differently — the solid dark nav rule is .app-shell:not(.frontpage), so opting in is what makes the nav transparent over a dark hero.
hero dome + glow curves, aurora, horizonCTA bands mask, grain, constellationtestimonial straddle + white logo inversionsection rhythm tucks and overlapsthe negative margins — see Rhythmreveal choreography body.fp-enhancedkicker icons hidden v13, intentionalSo an untouched page has the new type and chrome but the old spacing and structure. That mismatch is expected mid-rollout — it is not a bug to chase. When you bring a page onto the system, the spacing arrives with the frontpage class; you do not hand-set margins to imitate it.
Every hero spotlight, CTA glow and section wash must match its page's pillar. Mixing them is the most common drift.
Track — record app activity
#ede6f8#a443f0#b877f3#c5a3ffrgba(164,67,240,0.38)Explore — individual behaviour
#e3edf9#0087f6#51b8f5#8ecdfbrgba(0,135,246,0.38)Analyze — aggregate insights
#ebf8fa#09bab8#6fd0c6#8fe6dfrgba(9,186,184,0.38)The dark-mode ladder from nav to footer. The footer tone doubles as the seam target — a CTA fading to anything else shows a visible line.
#0f0e2f#241a5c#25116a#1c1758#0c0b26#002942#40394b#dcc6ffOnest throughout. The two numbers are a fluid floor and ceiling via the fluid-type mixin, not breakpoint steps.
Understand everything
Understand everything
Understand everything
Understand everything
Understand everything
Understand everything
$lg 75em / 1200px$md 64em / 1024pxthe mobile/desktop curve boundary$sm 48em / 768px$xs 0$container-width 1512pxpadding 0 80px desktopeats into max-width — account for ittarget 1280pxmost users; must look perfect hereA CTA band's bottom arc is transparent below the curve. If the next section starts flush, that transparency reveals the white page body as a pale sliver. So the section below is pulled under the band and pads its content back down.
Read this before changing any curve. Heroes and CTA bands use different mechanisms, so changing one does nothing to the other — and they must be matched in pixels of sag, not percentages.
Heroes curve via clip-path: ellipse(Rx% 100% at 50% 0%) on ::before. CTA bands curve via an SVG mask path — the clip-path has no effect there.
Three layers carry the band mask and all must move together: ::before (fill), .home-cta-section__field (constellation), and ::after (grain).
hero sag = height × (1 − √(1 − (0.5 ÷ Rx)²)) — width cancels, so sag tracks height only
band sag = height × 0.75 × (controlY − edgeY) ÷ 100 — a cubic reaches 0.75 of its control offset
ellipse(160% 100% at 50% 0%)sag 51.9px on a 1280×1036 hero — true proportionsM0 0L100 0L100 91C66 98 34 98 0 91Zsag 39.2px on a 1280×747 band — true proportions| Breakpoint | Hero ellipse | Band edge Y | Band control Y | Hero sag | Band sag |
|---|---|---|---|---|---|
| desktop ← you are here | 160% | 91 | 98 | 51.9px | 39.2px |
| mobile | 340% | 91 | 93 | 11.3px | 11.2px |
Desktop hero and band are deliberately not matched — the deep dome is the approved look there. On mobile they are matched to ~11px, which is why the mobile control point is 93 and not 98. The mobile menu keeps its own 320% and is not part of this group.
The spatial data-field canvas behind every hero and CTA band. Four tints, live below — the tint must match its section's pillar or the band reads as the wrong colour.
| Node count | clamp(44, round(width ÷ 16), 78)scales with width, never sparse or busy |
| Link distance | 152pxalpha = (1 − d ÷ 152) × 0.17 — fades with distance |
| Packet count | max(3, round(width ÷ 260))slow dots drifting straight down |
| Plain node | r = 0.9 + depth × 2alpha 0.32 + depth × 0.46 |
| Captured node | r = 1.8 + depth × 2.6alpha 0.55 + depth × 0.4, plus a pulsing halo |
| Perspective | 2.2 ÷ (2.7 + z)yaw follows the pointer; slow autorotate |
| DPR cap | min(devicePixelRatio, 2)stops 3× retina from tanking framerate |
| Canvas opacity | 0.55v47 — was 0.42 |
tint | 'purple' | 'blue' | 'teal' | 'multi'must match the section pillar |
wide | booleanwider spread for full-bleed bands |
speed | number (default 1)global motion multiplier — demo pages use 0.06 |
packets | boolean (default true)the drifting dots; off on the demo funnel |
Motion is scaled in three places — the time step, the pointer easing, and the autorotate. A speed change that only touches one of them leaves the field still drifting at full pace, which is why the demo page felt jarring twice.
Hero fields are masked transparent 0 → 8% → #000 34% so the strip directly under the nav stays clean. Reduced-motion and low-power devices get a single still frame, not a paused animation.
Measured from the live homepage. This is invisible until it breaks, and it broke twice — the testimonial card must float on top of the band it straddles, and the tucked section must stay beneath.
| Layer | z-index | Why |
|---|---|---|
| Header / nav | 100003 | must clear the mobile menu overlay |
| Testimonial box | 16 | floats ON TOP of the CTA band it straddles |
| Testimonial section | 15 | above the band, below its own card |
| CTA band | 2 | draws over the tucked section below it |
| Map section | 2 | overlaps the feature section beneath |
| Feature section | 1 | tucked under the CTA/map above |
| Constellation field | 0 | inside the band, behind the copy |
| Hero dome ::before | −1 | behind hero content, above the section background |
Measured on the live homepage at 1535px. The negative margins are load-bearing — they pull a section under the masked arc above it so the transparent part of that arc reveals a pillar wash instead of white.
| Section | Spacing | Note |
|---|---|---|
| Hero | padding 176px / 150px | min-height 960px, content vertically centred |
| Brands strip | padding 8px / 4px | sits tight under the hero curve |
| Map section | padding 120px, margin-bottom −250px | overlaps the feature section below |
| Feature section | padding-top 230px, margin-top −120px | 110px visual + 120px hidden |
| Testimonial | wash #ede6f8, no padding | card is translated, not padded |
| Testimonial card | translateY(50% of its own height) | 134px at 269px tall — always exactly half |
| CTA band | padding 232px / 200px | 232px top clears the straddling card |
| Post-CTA section | padding-top 310px, margin-top −120px | 190px visual + 120px hidden |
| Footer | padding-top 132px | flat dark top, flush with the last CTA |
| Container | max 1512px, padding 0 80px | padding eats into the max-width |
The testimonial straddle is expressed as 50% of the card's own height, never a pixel value — the card's height changes with quote length, and a fixed offset stops landing on the band's flat top.
The small things that make the dark surfaces read as lit volumes rather than flat fills.
An SVG feTurbulence fractal noise tile on every CTA band's ::after, masked to the same band shape.
baseFrequency 0.82, 2 octavesbackground-size 220pxopacity 0.05mix-blend-mode overlayde-bands the gradient; must stay barely-thereDark surfaces are never a single flat colour. Two off-centre radials — purple upper-left, teal lower-right — sit over a deep navy base so the surface reads as a lit volume.
Testimonial cards use the same treatment at a whisper (0.2 and 0.14 alpha instead of 0.5 and 0.32).
Keyboard focus is never removed. Every interactive element gets a visible ring via :focus-visible — tab through this page to check. Pricing buttons use 2px rgba(164,67,240,0.85) with a 3px offset.
Everything decorative is wrapped in prefers-reduced-motion. Reveals collapse to their final state, the constellation renders one still frame, and the horizon glow stops drifting.
Crucially, opacity stays at 1 throughout — copy is never gated on JS or motion, so nothing can ship blank.
/demo, /demo/calendar and /demo/confirm use .demo-dark, not .frontpage. Values measured live. The legacy .demo-hero block must stay: seven call/* pages depend on it.
page gradient #0f0e2f → #150f3d 46% → #0b0a24spotlight radial, 2533 × 1197pxblur(120px), rises from the bottom edgeanimation hue-rotate 0 → −80deg, 22s alternaterotate the hue — gradient stops do not interpolateconstellation speed 0.06, packets offnear-still; full speed reads as jarring herecard white, 448pxtri-colour gradient rim + box-shadow underglowfield bg #faf9fefield border 0.8px #e5e1f0field radius 10px, height 50pxlabels placeholder-onlylabels + placeholders made it too tall; order is fixedThe card background is plain white — no gradient. A faint wash on the card was tried and rejected; only the fields carry any tint.
Shine sweep plus a 2px lift. No hover glows anywhere — that was removed deliberately. Hover each specimen to check the sweep direction and that nothing flips to a dark fill.
ProductCtaButtons — the pair used in every product hero.
Real class names inside a .frontpage wrapper, so the themed gradients and hover sweeps are the genuine CSS. Rendered here without the surrounding band so you can compare the three side by side.
Primary is the purple shine with a tracking arrow. Secondary and contact are the light counterpart — a neutral grey sweep on white. At rest the sweep band sits fully outside the button; if you see a purple smudge on the left edge, the band position has regressed.
Section headers and statistics, live, in all three pillar themes. Kicker icons are hidden under .frontpage on purpose.
Track what happens inside your XR application. Cognitive3D captures scenes, sessions, objects, goals, and feedback without added complexity.
Track what happens inside your XR application. Cognitive3D captures scenes, sessions, objects, goals, and feedback without added complexity.
Track what happens inside your XR application. Cognitive3D captures scenes, sessions, objects, goals, and feedback without added complexity.
The card family. Note that the recommended pricing card's rim and glow are two separate elements — a single masked element cannot do both without a visible wash.
For teams validating a first XR build
For teams shipping continuously
The homepage journey cards and the 'Continue Exploring' cards share this treatment: dark fill, pillar-tinted border, and a 5px accent bar on the top edge.
Capture what happens, replay it in 3D, and compare across sessions.
Learn more →Capture what happens, replay it in 3D, and compare across sessions.
Learn more →Capture what happens, replay it in 3D, and compare across sessions.
Learn more →HomeTestimonial needs IGatsbyImageData, so this is markup-only. The card fill, border and quote glyph are the real CSS. On a live page the company logo is inverted to white via filter: brightness(0) invert(1) — a colour logo here means that filter was lost.
“If you're developing for Meta Quest and not using spatial analytics like Cognitive3D, it's borderline negligent.”
The real component, all three pillars plus the straight-bottom variant. These are tall, so they are collapsed by default. Each carries the band mask on three layers and the constellation canvas at 0.55 opacity.
Start capturing real user behaviour, app activity, and device inputs in full 3D.
Start capturing real user behaviour, app activity, and device inputs in full 3D.
Start capturing real user behaviour, app activity, and device inputs in full 3D.
This variant has no mask and no curve. It is deliberately flat where it meets the dark block and footer below.
Live FormRow in every input type. The demo form is placeholder-only by design — labels plus placeholders made it too tall and cluttered. Field order on the live form must not change.
All motion is subtle and slow. Every effect below is wrapped in prefers-reduced-motion on the live site — if you have reduced motion enabled, these will be still, which is correct.
reveal 0.7–0.8scubic-bezier(0.22, 0.61, 0.36, 1)shine sweep 0.5s background-positionlift translateY(−2px), 0.3shorizon drift 18s alternatedemo spotlight hue-rotate 0 → −80degrotate the hue; gradient stops do not interpolateElements start offset and settle to transform: none when .is-revealed is added by usePageReveal. Opacity stays at 1 throughout — copy is never gated on JS, so nothing can ship blank.
section-header translateY(24px)journey cards translateY(28px), staggered 0.12sfeature imagery translateX(±28px) from its own sidetestimonial scale(0.97)Each of these was shipped once and had to be undone. They are settled — don't reintroduce them.
!important; re-adding a box-shadow reverses a deliberate decision..frontpage rules into a page partial. Add the frontpage class to the wrapper and inherit. Hand-copying produced ~860 lines that had to be deleted again.style backgrounds on testimonials or heroes — they beat every stylesheet rule and break the wash.z-index: -1 for glows. It paints above the parent's background when the parent is a stacking context, and below in-flow ancestor backgrounds otherwise. Use box-shadow.mask-composite: exclude only cancels at alpha 0 or 1. Two partial alphas leave a visible wash. Split into two elements.justify-content: justify-between did nothing for a while — check computed styles, not the source.


