Internal reference

Website Design System

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.

Viewport 1280px — showing the desktop curve values

Composition — surfaces, seams and spines

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.

The seams, rendered

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.

R1 · straddles → bandclients → firstEvidence

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.

Trusted by teams measuring immersive work at scale

Specimen copy. This line is one of the three things the original overhang estimate forgot, and it is worth about 67px on a phone.

Adding Cognitive3D to an existing build

Engines and devices
Specimen fact with a figure: 40+
Specimen fact, second row
Specimen fact, third row
Getting data out
Specimen fact, first row
Specimen fact, second row
Specimen fact, third row
Governance
Specimen fact, first row
Specimen fact, second row
Specimen fact, third row
R1 · straddles → bandtestimonial → workedExample

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 Name

Specimen quote. Length matters here, because on desktop the overhang is half of whatever height this card ends up being.

Specimen Name

SPECIMEN TITLE

One session, end to end

Specimen intro line. The four steps are the pillar names in order, and the rail runs their colours.

  1. Tracked. Specimen step copy.

  2. Explored. Specimen step copy.

  3. Analyzed. Specimen step copy.

  4. Reported. Specimen step copy.

R2 · domed → tucksmapExperience → platformSupport

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.

Specimen heading for the three pillars

Track

Specimen copy.

Learn More

Explore

Specimen copy.

Learn More

Analyze

Specimen copy.

Learn More

Specimen closing row

Specimen card

Specimen description.

Learn More

Specimen card

Specimen description.

Learn More
no seamoutcomes — the shape to copy

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.

What this audience has to prove

Specimen intro line, roughly the length the real ones run to.

  • Specimen point

    Specimen body copy, one or two lines.

  • Specimen point

    Specimen body copy, one or two lines.

  • Specimen point

    Specimen body copy, one or two lines.

  • Specimen point

    Specimen body copy, one or two lines.

The contract, and its audit

ArchetypeSurfaceTop edgeBottom edgeComponent
productHeroOpens a pillar page. Dome via clip-path: ellipse(), not a mask.heroflatdomedProductHero
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.heroflatdomedUseCaseHero
mapExperienceThe three pillar cards. Clips BOTH edges and carries margin-bottom -250px, so whatever follows it must tuck.darkflatflatdomedUseCaseMapExperience
outcomesWhat this audience has to prove. The only seam-free section in the system — copy it as the shape for new archetypes.whiteflatflatUseCaseOutcomes
featureSectionOne pillar told as alternating image/text blocks. margin-top -120px, so it must follow a domed section.washtucksflatHomeFeatureSection
clientsTrust band: a logo card straddling the band below. Desktop straddles by translateY(50%); at <=1024px by a fixed -140px section margin.washflatstraddlesUseCaseClients
testimonialOne quote card straddling the band below. Overhang is half the card height on desktop, a fixed -240px at <=1024px.washflatstraddlesHomeTestimonial
ctaBandThe ask. Mask, grain, constellation, one or two buttons.bandreceivesdomedHomeCTASection
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.bandreceivesflatHomeCTASection (--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.bandreceivesdomedUseCaseFirstEvidence
workedExampleOne session end to end, as the four pillar steps. Renders AS a band for the same reason as firstEvidence.bandreceivesdomedUseCaseWorkedExample
platformSupportKeep-reading cards. Ships a light tint that all three pillar pages repaint #0c0b26 — treat dark as the real default.darkflattucksflatPlatformSupportSection
featureComparisonPlan comparison table. Bound to pricing tiers, not generic.whiteflatflatFeatureComparison
useCaseIntroLegacy tick-list intro. Unity/Unreal only; superseded by outcomes.whiteflatflatUseCaseIntro
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.heroflatdomedabout.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.washtucksflatabout.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.washflatflatabout.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.washflatflatabout.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.washflatflatUseCaseOutcomes (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.washflatflatHomeLogos1 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.bandreceivesdomedabout.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.washflatflatabout.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.washflatflatabout.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.washtucksflatabout.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.bandself-coversflatabout.tsx §11 (bespoke; swap to HomeCTASection --straight-bottom)

Use-case spine — all four pages run this order

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.

  1. 1useCaseHerohero
    plain join
  2. 2outcomeswhite
    plain join
  3. 3mapExperiencedarkflat
    featureSection tucks under mapExperience — R2
  4. 4featureSectionwash
    plain join
  5. 5clientswash
    straddles into firstEvidence — R1
  6. 6firstEvidenceband
    featureSection tucks under firstEvidence — R2
  7. 7featureSectionwash
    plain join
  8. 8testimonialwash
    straddles into workedExample — R1
  9. 9workedExampleband
    featureSection tucks under workedExample — R2
  10. 10featureSectionwash
    plain join
  11. 11testimonialwash
    straddles into ctaBandClosing — R1
  12. 12ctaBandClosingband

Every seam in that spine

FromToJoin
useCaseHerooutcomesplain join
outcomesmapExperienceplain join
mapExperiencefeatureSectionfeatureSection tucks under mapExperience — R2
featureSectionclientsplain join
clientsfirstEvidencestraddles into firstEvidence — R1
firstEvidencefeatureSectionfeatureSection tucks under firstEvidence — R2
featureSectiontestimonialplain join
testimonialworkedExamplestraddles into workedExample — R1
workedExamplefeatureSectionfeatureSection tucks under workedExample — R2
featureSectiontestimonialplain join
testimonialctaBandClosingstraddles into ctaBandClosing — R1

About spine — /about/

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.

  1. 1aboutHerohero
    aboutOrigin tucks under aboutHero — R2
  2. 2aboutOriginwash
    plain join
  3. 3aboutTodaywash
    plain join
  4. 4aboutBrandswash
    plain join
  5. 5aboutHowWeWorkwash
    plain join
  6. 6aboutFounderwash
    plain join
  7. 7aboutTeamwash
    plain join
  8. 8aboutUseCaseswash
    plain join
  9. 9testimonialwash
    straddles into aboutTrustData — R1
  10. 10aboutTrustDataband
    aboutBacked tucks under aboutTrustData — R2
  11. 11aboutBackedwash
    aboutClose covers its own clipped top — no rule
  12. 12aboutCloseband

Every seam in that spine

FromToJoin
aboutHeroaboutOriginaboutOrigin tucks under aboutHero — R2
aboutOriginaboutTodayplain join
aboutTodayaboutBrandsplain join
aboutBrandsaboutHowWeWorkplain join
aboutHowWeWorkaboutFounderplain join
aboutFounderaboutTeamplain join
aboutTeamaboutUseCasesplain join
aboutUseCasestestimonialplain join
testimonialaboutTrustDatastraddles into aboutTrustData — R1
aboutTrustDataaboutBackedaboutBacked tucks under aboutTrustData — R2
aboutBackedaboutCloseaboutClose covers its own clipped top — no rule

Attaching a page to this system

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.

  1. Inherit, never copy. Give the wrapper both classes: 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.
  2. Delete the page's old hero and section CSS before writing new. Leftover rules out-specify the inherited ones and you'll spend a day chasing why a wash won't apply.
  3. Write the new partial against tokens, not literals — var(--c3d-purple-wash), not #ede6f8. A page joining the system gets this for free; there is no separate migration step.
  4. Wire the reveal hookusePageReveal with the page's selectors and bodyClass: 'fp-enhanced', plus the fp-scrolled toggle. Copy from track.tsx.
  5. Pick the pillar and use it everywhere — hero spotlight, CTA glows, section washes, card accents. A mismatched CTA glow is the most common drift.
  6. Mark the last CTA home-cta-section--straight-bottom so it runs flat into whatever follows.
  7. Check desktop and mobile before calling it done. The curve values differ across the ≤1024px boundary and the two mechanisms are independent — see Curves.

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.

What's global vs what's opt-in

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.

Global — every page already has this

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-spacing
  • header / nav redesignedincl. dropdown panel and mobile menu
  • footer redesigned
  • focus rings site-wide
  • --c3d-* tokens declared on :rootavailable everywhere, consumed only where written

Opt-in — only with the frontpage class

Scoped 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, horizon
  • CTA bands mask, grain, constellation
  • testimonial straddle + white logo inversion
  • section rhythm tucks and overlapsthe negative margins — see Rhythm
  • reveal choreography body.fp-enhanced
  • kicker icons hidden v13, intentional

So 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.

Pillar colour map

Every hero spotlight, CTA glow and section wash must match its page's pillar. Mixing them is the most common drift.

Purple

Track — record app activity

Section wash#ede6f8
Accent
#a443f0
Card title
#b877f3
Learn-more
#c5a3ff
Card border
rgba(164,67,240,0.38)

Blue

Explore — individual behaviour

Section wash#e3edf9
Accent
#0087f6
Card title
#51b8f5
Learn-more
#8ecdfb
Card border
rgba(0,135,246,0.38)

Teal

Analyze — aggregate insights

Section wash#ebf8fa
Accent
#09bab8
Card title
#6fd0c6
Learn-more
#8fe6df
Card border
rgba(9,186,184,0.38)

Core colours

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.

Dark purple#0f0e2f$dark-purplenav + hero top
Hero mid#241a5chero gradient 58%
Hero base#25116aproduct hero base
CTA band#1c1758dark band + cards
Footer#0c0b26footer + seam target
Deep blue#002942$deep-blueheadings on light
Body ink#40394bfeature paragraphs
Hero link#dcc6ffinline, NO underline

Type scale

Onest throughout. The two numbers are a fluid floor and ceiling via the fluid-type mixin, not breakpoint steps.

Hero H160 → 78px · 700 · lh 1.05 · ls -0.03em

Understand everything

Section H240 → 54px · 600 · lh 1.15 · ls -0.02em

Understand everything

Feature title20 → 26px · 600 · lh 1.3 · ls -0.015em

Understand everything

Hero subcopy18 → 20px · 400 · lh 1.55 · ls normal

Understand everything

Body16 → 18px · 400 · lh 1.6 · ls normal

Understand everything

Kicker14px · 700 · lh 1.2 · ls 0.08em

Understand everything

Layout & breakpoints

Breakpoints

  • $lg 75em / 1200px
  • $md 64em / 1024pxthe mobile/desktop curve boundary
  • $sm 48em / 768px
  • $xs 0

Container

  • $container-width 1512px
  • padding 0 80px desktopeats into max-width — account for it
  • target 1280pxmost users; must look perfect here

The post-CTA tuck (desktop) — why sections overlap

A 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.

CTA band · z-index 2 · masked bottom arc
margin-top: −120px (hidden under the band)
Next section · z-index 1 · padding-top: 310px= 190px visual clearance + 120px hidden

The curve system

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

Hero domeellipse(160% 100% at 50% 0%)sag 51.9px on a 1280×1036 hero — true proportions
CTA bandM0 0L100 0L100 91C66 98 34 98 0 91Zsag 39.2px on a 1280×747 band — true proportions
BreakpointHero ellipseBand edge YBand control YHero sagBand sag
desktop ← you are here160%919851.9px39.2px
mobile 340%919311.3px11.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.

Constellation field

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.

purplenodes 164,67,240 · 190,120,250 · 132,68,216links rgba(176,120,240)
bluenodes 0,135,246 · 86,178,250 · 40,110,220links rgba(110,170,245)
tealnodes 9,186,184 · 64,210,205 · 0,150,150links rgba(90,200,200)
multinodes 164,67,240 · 0,135,246 · 0,201,199links rgba(140,160,255)

Geometry & density

Node countclamp(44, round(width ÷ 16), 78)scales with width, never sparse or busy
Link distance152pxalpha = (1 − d ÷ 152) × 0.17 — fades with distance
Packet countmax(3, round(width ÷ 260))slow dots drifting straight down
Plain noder = 0.9 + depth × 2alpha 0.32 + depth × 0.46
Captured noder = 1.8 + depth × 2.6alpha 0.55 + depth × 0.4, plus a pulsing halo
Perspective2.2 ÷ (2.7 + z)yaw follows the pointer; slow autorotate
DPR capmin(devicePixelRatio, 2)stops 3× retina from tanking framerate
Canvas opacity0.55v47 — was 0.42

Props

tint'purple' | 'blue' | 'teal' | 'multi'must match the section pillar
widebooleanwider spread for full-bleed bands
speednumber (default 1)global motion multiplier — demo pages use 0.06
packetsboolean (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.

Z-index ladder

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.

Layerz-indexWhy
Header / nav100003must clear the mobile menu overlay
Testimonial box16floats ON TOP of the CTA band it straddles
Testimonial section15above the band, below its own card
CTA band2draws over the tucked section below it
Map section2overlaps the feature section beneath
Feature section1tucked under the CTA/map above
Constellation field0inside the band, behind the copy
Hero dome ::before−1behind hero content, above the section background

Vertical rhythm & overlaps

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.

SectionSpacingNote
Heropadding 176px / 150pxmin-height 960px, content vertically centred
Brands strippadding 8px / 4pxsits tight under the hero curve
Map sectionpadding 120px, margin-bottom −250pxoverlaps the feature section below
Feature sectionpadding-top 230px, margin-top −120px110px visual + 120px hidden
Testimonialwash #ede6f8, no paddingcard is translated, not padded
Testimonial cardtranslateY(50% of its own height)134px at 269px tall — always exactly half
CTA bandpadding 232px / 200px232px top clears the straddling card
Post-CTA sectionpadding-top 310px, margin-top −120px190px visual + 120px hidden
Footerpadding-top 132pxflat dark top, flush with the last CTA
Containermax 1512px, padding 0 80pxpadding 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.

Texture & effects

The small things that make the dark surfaces read as lit volumes rather than flat fills.

Grain overlay

An SVG feTurbulence fractal noise tile on every CTA band's ::after, masked to the same band shape.

  • baseFrequency 0.82, 2 octaves
  • background-size 220px
  • opacity 0.05
  • mix-blend-mode overlayde-bands the gradient; must stay barely-there

Lit-volume backgrounds

Dark 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).

Focus rings

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.

Tab to me

Reduced motion

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 funnel — a separate system

/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.

Surface & spotlight

  • page gradient #0f0e2f → #150f3d 46% → #0b0a24
  • spotlight radial, 2533 × 1197pxblur(120px), rises from the bottom edge
  • animation hue-rotate 0 → −80deg, 22s alternaterotate the hue — gradient stops do not interpolate
  • constellation speed 0.06, packets offnear-still; full speed reads as jarring here

Form card & fields

  • card white, 448pxtri-colour gradient rim + box-shadow underglow
  • field bg #faf9fe
  • field border 0.8px #e5e1f0
  • field radius 10px, height 50px
  • labels placeholder-onlylabels + placeholders made it too tall; order is fixed

The card background is plain white — no gradient. A faint wash on the card was tried and rejected; only the fields carry any tint.

Buttons

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.

Product hero CTAslive component

ProductCtaButtons — the pair used in every product hero.

CTA band buttons, all three pillarsclass-name replica

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.

Pricing card buttonsclass-name replica

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.

Content components

Section headers and statistics, live, in all three pillar themes. Kicker icons are hidden under .frontpage on purpose.

SectionHeader + Statistics — purplelive component
Purple pillar

Record What Happens in XR Apps

Track what happens inside your XR application. Cognitive3D captures scenes, sessions, objects, goals, and feedback without added complexity.

1,400+companies
290Mminutes analysed
SectionHeader + Statistics — bluelive component
Blue pillar

Record What Happens in XR Apps

Track what happens inside your XR application. Cognitive3D captures scenes, sessions, objects, goals, and feedback without added complexity.

1,400+companies
290Mminutes analysed
SectionHeader + Statistics — teallive component
Teal pillar

Record What Happens in XR Apps

Track what happens inside your XR application. Cognitive3D captures scenes, sessions, objects, goals, and feedback without added complexity.

1,400+companies
290Mminutes analysed

Surfaces & cards

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.

PricingCard — standard and recommendedlive component

Starter

For teams validating a first XR build

  • Sessions per month
  • Scene Explorer + replay
  • Objectives & goals
  • SSO and audit log
Dark pillar cardclass-name replica

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.

Purple pillar

Capture what happens, replay it in 3D, and compare across sessions.

Learn more →

Blue pillar

Capture what happens, replay it in 3D, and compare across sessions.

Learn more →

Teal pillar

Capture what happens, replay it in 3D, and compare across sessions.

Learn more →
Testimonial cardclass-name replica

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.”

James Senior

Product Manager Lead, Metaverse Apps

CTA bands

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.

HomeCTASection — purpletoggle

Ready To See Inside Your XR App?

Start capturing real user behaviour, app activity, and device inputs in full 3D.

HomeCTASection — bluetoggle

Ready To See Inside Your XR App?

Start capturing real user behaviour, app activity, and device inputs in full 3D.

HomeCTASection — tealtoggle

Ready To See Inside Your XR App?

Start capturing real user behaviour, app activity, and device inputs in full 3D.

HomeCTASection — straight-bottom (the last CTA on a page)toggle

Ready To See Inside Your XR App?

This variant has no mask and no curve. It is deliberately flat where it meets the dark block and footer below.

Forms

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.

FormRow — all input typeslive component
Country*

Motion

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.

Durations & easing

  • reveal 0.7–0.8scubic-bezier(0.22, 0.61, 0.36, 1)
  • shine sweep 0.5s background-position
  • lift translateY(−2px), 0.3s
  • horizon drift 18s alternate
  • demo spotlight hue-rotate 0 → −80degrotate the hue; gradient stops do not interpolate

Reveal states

Elements 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.12s
  • feature imagery translateX(±28px) from its own side
  • testimonial scale(0.97)

Anti-patterns

Each of these was shipped once and had to be undone. They are settled — don't reintroduce them.

  • No hover glows on CTA buttons. Shine sweep and a 2px lift only. Removed with !important; re-adding a box-shadow reverses a deliberate decision.
  • Constellation opacity is 0.55. Not 0.42 — that was an early iteration.
  • Never hand-copy .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.
  • No inline style backgrounds on testimonials or heroes — they beat every stylesheet rule and break the wash.
  • Avoid 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.
  • Match arcs in pixels, not percentages. Equal percentages leave the shorter element visibly flatter, because sag scales with element height.
  • Invalid CSS values fail silently. justify-content: justify-between did nothing for a while — check computed styles, not the source.