diff --git a/app/home-poc/_components/Atmosphere.tsx b/app/home-poc/_components/Atmosphere.tsx new file mode 100644 index 00000000..c6d5081f --- /dev/null +++ b/app/home-poc/_components/Atmosphere.tsx @@ -0,0 +1,17 @@ +'use client'; + +import { motion, useScroll, useSpring } from 'framer-motion'; + +/** Filmic grain + scanlines for depth, and a red scroll-progress line. + * Texture, not decoration — kills the flat dead-black. */ +export function Atmosphere() { + const { scrollYProgress } = useScroll(); + const scaleX = useSpring(scrollYProgress, { stiffness: 120, damping: 30, mass: 0.3 }); + return ( + <> + +
+
+ + ); +} diff --git a/app/home-poc/_components/CountUp.tsx b/app/home-poc/_components/CountUp.tsx new file mode 100644 index 00000000..a4db5477 --- /dev/null +++ b/app/home-poc/_components/CountUp.tsx @@ -0,0 +1,31 @@ +'use client'; + +import { useEffect, useRef, useState } from 'react'; +import { useInView, useReducedMotion } from 'framer-motion'; + +/** Counts from 0 to `value` once it scrolls into view. */ +export function CountUp({ value, duration = 1100 }: { value: number; duration?: number }) { + const ref = useRef(null); + const inView = useInView(ref, { once: true, amount: 0.6 }); + const reduce = useReducedMotion(); + const [v, setV] = useState(reduce ? value : 0); + + useEffect(() => { + if (!inView || reduce) { + if (reduce) setV(value); + return; + } + let raf = 0; + let start: number | null = null; + const tick = (t: number) => { + if (start === null) start = t; + const p = Math.min((t - start) / duration, 1); + setV(Math.round((1 - Math.pow(1 - p, 3)) * value)); + if (p < 1) raf = requestAnimationFrame(tick); + }; + raf = requestAnimationFrame(tick); + return () => cancelAnimationFrame(raf); + }, [inView, reduce, value, duration]); + + return {v}; +} diff --git a/app/home-poc/_components/DashboardPreview.tsx b/app/home-poc/_components/DashboardPreview.tsx new file mode 100644 index 00000000..283a1d7f --- /dev/null +++ b/app/home-poc/_components/DashboardPreview.tsx @@ -0,0 +1,213 @@ +'use client'; + +import { motion, useInView, useReducedMotion } from 'framer-motion'; +import { useRef } from 'react'; +import { CountUp } from './CountUp'; + +/** + * A faithful preview of the FormaOS in-app dashboard (/app/compliance/health + * + command-center): browser chrome, categorised sidebar with RAG dots, org + * top bar, the overdue/due-soon/completed status strip, KPI tiles, the overall + * compliance-health card with an animated posture gauge + status tiles + + * sparkline, per-framework health with animated bars, and the top outstanding + * controls list. Rendered in the page's monochrome+red palette. + */ + +const NAV = [ + ['Dashboard', null, true], + ['Obligations', 'red', false], + ['Policies', 'dim', false], + ['Evidence Vault', 'ok', false], + ['Incidents', 'red', false], + ['Staff Compliance', 'dim', false], + ['Reports', null, false], + ['Executive View', null, false], +] as const; + +const KPIS = [ + ['Open obligations', 12, 'awaiting owner'], + ['Overdue', 0, 'nothing past SLA'], + ['Due this week', 5, 'on cadence'], + ['Readiness', 94, '% · buyer-ready'], +] as const; + +const FRAMEWORKS = [ + ['SOC 2 Type II', 94], + ['ISO 27001', 88], + ['NDIS Practice', 96], + ['HIPAA', 96], +] as const; + +const OUTSTANDING = [ + ['AC-6', 'Least privilege', 'SOC 2', 'FAIL'], + ['A.8.24', 'Use of cryptography', 'ISO 27001', 'PARTIAL'], + ['1.7', 'Restrictive practices review', 'NDIS', 'PARTIAL'], +] as const; + +function ragColor(r: string | null) { + if (r === 'red') return 'var(--red)'; + if (r === 'ok') return 'var(--ok)'; + if (r === 'dim') return 'var(--ink-faint)'; + return 'transparent'; +} + +export function DashboardPreview() { + const ref = useRef(null); + const inView = useInView(ref, { once: true, amount: 0.3 }); + const reduce = useReducedMotion(); + + const r = 46; + const target = 0.82; // matches the 82% overall-health readout + + return ( +
+ {/* browser chrome */} +
+ + app.formaos.com.au/app/compliance/health + NM +
+ +
+ {/* sidebar */} + + + {/* main */} +
+
+ ORGANIZATION · Brightside Care + NDIS PROVIDER +
+
+ 0 OVERDUE + 5 DUE SOON + 38 COMPLETED + + LIVE + +
+ +
+ {/* KPI tiles */} +
+ {KPIS.map(([l, v, c]) => ( +
+
{l}
+
+ {l === 'Readiness' ? '%' : ''} +
+
{l === 'Readiness' ? 'buyer-ready' : c}
+
+ ))} +
+ +
+ {/* overall health */} +
+
+ Overall compliance health + WATCH +
+
+ + + + +
+
+ % +
+
+ 5 FRAMEWORKS · 252 CONTROLS +
+
+
+
+ {[['47', 'Pass'], ['8', 'Partial'], ['3', 'Fail'], ['12', 'Manual']].map(([v, l]) => ( +
+
{v}
+
{l}
+
+ ))} +
+ {/* sparkline */} +
+
+ 12-WEEK TREND · +6% +
+ + + +
+
+ + {/* per-framework + outstanding */} +
+
+
Per-frameworkSCORE
+ {FRAMEWORKS.map(([name, score], i) => ( +
+ {name} + + + + + {score}% + +
+ ))} +
+
+
Top outstanding controls
+ {OUTSTANDING.map(([k, t, fw, st]) => ( +
+ {k} + {t} + {fw} + {st} +
+ ))} +
+
+
+
+
+
+
+ ); +} diff --git a/app/home-poc/_components/Faq.tsx b/app/home-poc/_components/Faq.tsx new file mode 100644 index 00000000..54f27dcb --- /dev/null +++ b/app/home-poc/_components/Faq.tsx @@ -0,0 +1,40 @@ +/** + * FAQ — native
accordion (no JS, robust). Real answers sourced from + * the trust/audit-chain/objection content. + */ + +const QA = [ + ['Which frameworks ship today?', 'Eight framework packs ship in the template and policy library now — NDIS Practice Standards, Aged Care Quality Standards, NSQHS, SOC 2, ISO 27001, HIPAA, Essential Eight and more — cross-mapped so one evidence item can satisfy multiple frameworks.'], + ['How is the audit log tamper-evident?', 'Each row is HMAC-SHA256 chained to the previous one and the chain top is anchored daily to Sigstore Rekor (an RFC 6962 transparency log). A BEFORE UPDATE OR DELETE trigger plus restrictive RLS reject any mutation — even a service-role admin is stopped by the database, not application code.'], + ['Where is our data stored?', 'AU-hosted by default (Sydney region). Additional residency requirements are reviewed during procurement, with a Data Processing Agreement available for legal review.'], + ['Can we export our data if we leave?', 'Yes. Evidence, controls, audit trails, and framework mappings export in standard formats (PDF, CSV, JSON). Full data portability is guaranteed.'], + ['Do you support SSO and MFA?', 'SAML 2.0 SSO (Okta, Azure AD, Google) and MFA enforcement are available on all plans, with role-based access across four roles and session timeout / IP controls.'], + ['How long does audit preparation take?', 'On demand. An auditor bundle — framework summary, SHA-256 evidence references, automation log, score history, and the Rekor-anchored chain top — exports as a single ZIP, rather than days of reconstruction.'], +] as const; + +export function Faq() { + return ( +
+
+
+ Questions +

+ Answered before
you ask. +

+
+
+ +
+ {QA.map(([q, a]) => ( +
+ + {q} + + + +

{a}

+
+ ))} +
+
+ ); +} diff --git a/app/home-poc/_components/HeroIntro.tsx b/app/home-poc/_components/HeroIntro.tsx new file mode 100644 index 00000000..fac846c6 --- /dev/null +++ b/app/home-poc/_components/HeroIntro.tsx @@ -0,0 +1,141 @@ +'use client'; + +import { useRef } from 'react'; +import { motion, useReducedMotion, type Variants } from 'framer-motion'; +import { Magnetic } from './Magnetic'; + +/** + * Hero with a mechanical staggered entrance (on mount, so the LCP headline + * always resolves visible) + a pointer-reactive technical HUD: a red crosshair + * with live coordinates tracks the cursor and the exposed grid parallaxes + * slightly. Engineered, not glowy. + */ + +const container: Variants = { + hidden: {}, + show: { transition: { staggerChildren: 0.09, delayChildren: 0.08 } }, +}; +const item: Variants = { + hidden: { opacity: 0, y: 26 }, + show: { opacity: 1, y: 0, transition: { duration: 0.6, ease: [0.22, 1, 0.36, 1] } }, +}; + +const pad = (n: number) => String(Math.round(n)).padStart(4, '0'); + +export function HeroIntro() { + const reduce = useReducedMotion(); + const mv = reduce ? undefined : item; + const secRef = useRef(null); + const vRef = useRef(null); + const hRef = useRef(null); + const rRef = useRef(null); + const hudRef = useRef(null); + const gridRef = useRef(null); + + const onMove = (e: React.MouseEvent) => { + if (reduce || !secRef.current) return; + const rect = secRef.current.getBoundingClientRect(); + const x = e.clientX - rect.left; + const y = e.clientY - rect.top; + if (vRef.current) vRef.current.style.left = `${x}px`; + if (hRef.current) hRef.current.style.top = `${y}px`; + if (rRef.current) { + rRef.current.style.left = `${x}px`; + rRef.current.style.top = `${y}px`; + rRef.current.textContent = `X ${pad(x)} Y ${pad(y)}`; + } + if (gridRef.current) { + const dx = (x / rect.width - 0.5) * -14; + const dy = (y / rect.height - 0.5) * -10; + gridRef.current.style.transform = `translate(${dx}px, ${dy}px)`; + } + }; + const setHud = (on: boolean) => hudRef.current?.classList.toggle('is-on', on); + + return ( +
setHud(true)} + onMouseLeave={() => { + setHud(false); + if (gridRef.current) gridRef.current.style.transform = ''; + }} + > +
+ {Array.from({ length: 12 }).map((_, i) => )} +
+ + {/* pointer HUD */} +
+ + + X 0000 Y 0000 +
+ + + + COMPLIANCE OS FOR NDIS · AGED CARE · HEALTHCARE + + + + Audit-ready + + + every day. + + + Not the week before the Commission visits. + + +
+ + +

+ GUIDED ASSESSMENT · AU-HOSTED BY DEFAULT · EVIDENCE-BACKED WORKFLOWS +

+
+ +

+ FormaOS turns NDIS Practice Standards, Aged Care Quality Standards, and the + rest of your obligations into enforced workflows — named owners, blocked + failure paths, and an immutable evidence trail that passes review the first time. +

+
+
+ + + {[ + ['8', 'Framework packs'], + ['252', 'Controls mapped'], + ['100%', 'Audit coverage'], + ['05:30', 'UTC Rekor anchor'], + ].map(([n, l]) => ( +
+
{n}
+
{l}
+
+ ))} +
+
+
+ ); +} diff --git a/app/home-poc/_components/HowItWorks.tsx b/app/home-poc/_components/HowItWorks.tsx new file mode 100644 index 00000000..6bad191d --- /dev/null +++ b/app/home-poc/_components/HowItWorks.tsx @@ -0,0 +1,49 @@ +import { Reveal } from './Reveal'; + +/** + * The enforced operating loop — verbatim from the production HowItWorks. + * Five steps as a heavy numbered ledger; step 03 (enforcement) carries the + * red "ENFORCING" badge because that's the load-bearing idea. + */ + +const STEPS = [ + ['01', 'Define compliance workflow', 'Map the operational process — owners, due dates, evidence, and review points.', false], + ['02', 'Assign rules', 'Set what must be present before work can move forward.', false], + ['03', 'System enforces execution', 'FormaOS runs checks continuously and blocks incomplete paths.', true], + ['04', 'Evidence generated automatically', 'Actions, approvals, timestamps, and context become audit evidence.', false], + ['05', 'Audit-ready anytime', 'Export the evidence chain instead of rebuilding it under pressure.', false], +] as const; + +export function HowItWorks() { + return ( +
+
+
+ How it works +

+ From obligation to
enforced evidence chain. +

+
+

+ Compliance as a continuous operating loop — not a document clean-up project + before an audit. +

+
+ + +
+ {STEPS.map(([n, t, d, enforce]) => ( +
+ {n} +
+
{t}
+

{d}

+
+ {enforce ? ● Enforcing : } +
+ ))} +
+
+
+ ); +} diff --git a/app/home-poc/_components/Ledger.tsx b/app/home-poc/_components/Ledger.tsx new file mode 100644 index 00000000..f0d7655f --- /dev/null +++ b/app/home-poc/_components/Ledger.tsx @@ -0,0 +1,126 @@ +'use client'; + +import { useEffect, useRef, useState } from 'react'; +import { useInView, useReducedMotion } from 'framer-motion'; + +/** + * Terminal-style framework ledger: a monospaced data table that reads like + * a compliance trading desk. The aggregate score counts up on view; each + * row carries a coverage bar and a live tag. Replaces the usual "trust + * strip + posture card" with something data-forward and brutal. + */ + +type Row = { + framework: string; + controls: string; + coverage: number; // % + score: number; // % +}; + +// Real control counts from the framework registry; scores illustrative, +// mirroring how /app/compliance/health renders live posture. +const ROWS: Row[] = [ + { framework: 'SOC 2 Type II', controls: '61 TSC controls', coverage: 94, score: 94 }, + { framework: 'ISO 27001', controls: '93 controls', coverage: 88, score: 88 }, + { framework: 'NDIS Practice', controls: '25 evaluators', coverage: 96, score: 96 }, + { framework: 'HIPAA', controls: '10 safeguards', coverage: 96, score: 96 }, + { framework: 'Essential Eight', controls: '8 mitigations', coverage: 93, score: 93 }, +]; + +function useCountUp(target: number, run: boolean) { + const reduce = useReducedMotion(); + const [v, setV] = useState(reduce ? target : 0); + useEffect(() => { + if (!run || reduce) { + if (reduce) setV(target); + return; + } + let raf = 0; + let start: number | null = null; + const dur = 1100; + const tick = (t: number) => { + if (start === null) start = t; + const p = Math.min((t - start) / dur, 1); + setV(Math.round((1 - Math.pow(1 - p, 3)) * target)); + if (p < 1) raf = requestAnimationFrame(tick); + }; + raf = requestAnimationFrame(tick); + return () => cancelAnimationFrame(raf); + }, [run, reduce, target]); + return v; +} + +export function Ledger() { + const ref = useRef(null); + const inView = useInView(ref, { once: true, amount: 0.4 }); + const agg = useCountUp(94, inView); + + return ( +
+ {/* aggregate readout */} +
+
+ + {agg} + % + + + COMPOSITE +
+ POSTURE +
+
+
+ + + REFRESHED NIGHTLY · 06:00 UTC + + + ILLUSTRATIVE — NOT A CUSTOMER CLAIM + +
+
+ + + + + + + + + + + + + {ROWS.map((r) => ( + + + + + + + + ))} + +
FrameworkControlsCoverageScoreStatus
{r.framework}{r.controls} + + + + {r.score}% + TRACKED +
+
+ ); +} diff --git a/app/home-poc/_components/Magnetic.tsx b/app/home-poc/_components/Magnetic.tsx new file mode 100644 index 00000000..83afb903 --- /dev/null +++ b/app/home-poc/_components/Magnetic.tsx @@ -0,0 +1,33 @@ +'use client'; + +import { motion, useMotionValue, useReducedMotion, useSpring } from 'framer-motion'; +import type { ReactNode } from 'react'; + +/** Magnetic hover: the child is gently pulled toward the cursor and springs + * back on leave. Micro-delight on primary actions. */ +export function Magnetic({ children, strength = 0.35 }: { children: ReactNode; strength?: number }) { + const reduce = useReducedMotion(); + const x = useMotionValue(0); + const y = useMotionValue(0); + const sx = useSpring(x, { stiffness: 220, damping: 14 }); + const sy = useSpring(y, { stiffness: 220, damping: 14 }); + + if (reduce) return {children}; + + return ( + { + const r = e.currentTarget.getBoundingClientRect(); + x.set((e.clientX - (r.left + r.width / 2)) * strength); + y.set((e.clientY - (r.top + r.height / 2)) * strength); + }} + onMouseLeave={() => { + x.set(0); + y.set(0); + }} + > + {children} + + ); +} diff --git a/app/home-poc/_components/ObjectionHandling.tsx b/app/home-poc/_components/ObjectionHandling.tsx new file mode 100644 index 00000000..277cb76d --- /dev/null +++ b/app/home-poc/_components/ObjectionHandling.tsx @@ -0,0 +1,74 @@ +import { Reveal } from './Reveal'; + +/** Enterprise procurement: the four objections + the evaluation path + the + * buyer-facing artifacts that ship on day one. Verbatim from production. */ + +const OBJ = [ + ['How do we complete security review before sign-off?', 'A security review packet — architecture overview, DPA, and vendor questionnaire material — so your team starts immediately.', 'Security packet included'], + ['Where is our data stored?', 'AU-hosted by default. Additional residency is reviewed during procurement, with a DPA available for legal review.', 'Data sovereignty controls'], + ['Can we get our data out if we leave?', 'Evidence, controls, audit trails, and framework mappings export in standard formats. Full portability, guaranteed.', 'Full data portability'], + ['Does it work across multiple sites?', 'Multi-entity and multi-site management is core, with centralized oversight and local accountability per site.', 'Multi-entity by design'], +] as const; + +const PATH = [ + ['01', 'Start buyer review', 'Bring security, compliance, procurement, and operations into a guided evaluation from day one.'], + ['02', 'Run security review in parallel', 'Use the security packet and trust-center artifacts while teams validate implementation fit.'], + ['03', 'Close with defensible proof', 'Present ownership trails, evidence chains, and readiness posture for approval without rework.'], +] as const; + +const ARTIFACTS = [ + 'Security review packet', 'Trust center documents', 'Framework mapping overview', + 'DPA & data residency docs', 'Access & identity model', 'Enterprise service terms', +]; + +export function ObjectionHandling() { + return ( +
+
+
+ Enterprise ready +

+ From evaluation to
procurement, no blockers. +

+
+

+ Ships with the trust artifacts, security documentation, and buyer-facing proof + procurement teams need on day one. +

+
+ + +
+ {OBJ.map(([q, a, proof]) => ( +
+

{q}

+

{a}

+

↳ {proof}

+
+ ))} +
+
+ +
+ + Typical evaluation path + + +
+ {PATH.map(([n, t, d], i) => ( +
+ {n} +
{t}
+

{d}

+ {i < PATH.length - 1 && } +
+ ))} +
+
+
+ {ARTIFACTS.map((a) => {a})} +
+
+
+ ); +} diff --git a/app/home-poc/_components/PostureSimulator.tsx b/app/home-poc/_components/PostureSimulator.tsx new file mode 100644 index 00000000..f68f00a7 --- /dev/null +++ b/app/home-poc/_components/PostureSimulator.tsx @@ -0,0 +1,151 @@ +'use client'; + +import { useMemo, useState } from 'react'; +import { motion, useReducedMotion } from 'framer-motion'; + +/** + * Playable compliance posture. Toggle controls on/off and the gauge, status + * counts, and per-framework bars recompute live — the page's interactive + * centerpiece. Demonstrates the product idea instead of describing it. + */ + +type Ctrl = { id: string; label: string; group: string }; + +const CONTROLS: Ctrl[] = [ + { id: 'ac2', label: 'AC-2 Account review', group: 'Access' }, + { id: 'ac6', label: 'AC-6 Least privilege', group: 'Access' }, + { id: 'ac17', label: 'AC-17 Remote access', group: 'Access' }, + { id: 'rest', label: 'Data at rest · AES-256', group: 'Encryption' }, + { id: 'tls', label: 'In transit · TLS 1.3', group: 'Encryption' }, + { id: 'kms', label: 'Key management · KMS', group: 'Encryption' }, + { id: 'eatt', label: 'Evidence attached', group: 'Evidence' }, + { id: 'eanc', label: 'Chain anchored · Rekor', group: 'Evidence' }, + { id: 'sirs', label: 'SIRS clock encoded', group: 'Incident' }, + { id: 'iown', label: 'Incident owner named', group: 'Incident' }, + { id: 'pack', label: 'Policy acknowledged', group: 'Workforce' }, + { id: 'scr', label: 'Worker screening', group: 'Workforce' }, +]; + +const FRAMEWORKS: { name: string; ids: string[] }[] = [ + { name: 'SOC 2 Type II', ids: ['ac2', 'ac6', 'ac17', 'rest', 'tls', 'kms', 'eatt', 'eanc'] }, + { name: 'ISO 27001', ids: ['ac6', 'rest', 'tls', 'kms', 'eatt', 'pack'] }, + { name: 'NDIS Practice', ids: ['ac2', 'sirs', 'iown', 'pack', 'scr'] }, + { name: 'HIPAA', ids: ['ac17', 'rest', 'tls', 'kms', 'eatt'] }, +]; + +const GROUPS = ['Access', 'Encryption', 'Evidence', 'Incident', 'Workforce']; +const START = new Set(['ac2', 'ac6', 'ac17', 'rest', 'tls', 'kms']); + +export function PostureSimulator() { + const reduce = useReducedMotion(); + const [on, setOn] = useState>(new Set(START)); + + const toggle = (id: string) => + setOn((prev) => { + const next = new Set(prev); + next.has(id) ? next.delete(id) : next.add(id); + return next; + }); + + const score = Math.round((on.size / CONTROLS.length) * 100); + const r = 46; + + const fwScores = useMemo( + () => + FRAMEWORKS.map((f) => { + const met = f.ids.filter((id) => on.has(id)).length; + return { name: f.name, pct: Math.round((met / f.ids.length) * 100) }; + }), + [on], + ); + + return ( +
+ {/* controls */} +
+ {GROUPS.map((g) => ( +
+
{g}
+
+ {CONTROLS.filter((c) => c.group === g).map((c) => { + const active = on.has(c.id); + return ( + + ); + })} +
+
+ ))} +
+ + + +
+
+ + {/* live readout */} +
+
+ + + + +
+
+ {score}% +
+
+ COMPOSITE POSTURE +
+
+
+ +
+
+
{on.size}
+
Met
+
+
+
0 ? 'var(--red)' : 'var(--ink)' }}> + {CONTROLS.length - on.size} +
+
Gaps
+
+
+
{CONTROLS.length}
+
Mapped
+
+
+ +
+ {fwScores.map((f) => ( +
+ {f.name} + + + {f.pct}% + +
+ ))} +
+ +
↳ Toggle controls — watch posture move in real time.
+
+
+ ); +} diff --git a/app/home-poc/_components/Reveal.tsx b/app/home-poc/_components/Reveal.tsx new file mode 100644 index 00000000..39d045c5 --- /dev/null +++ b/app/home-poc/_components/Reveal.tsx @@ -0,0 +1,44 @@ +'use client'; + +import { motion, useReducedMotion } from 'framer-motion'; +import type { CSSProperties, ReactNode } from 'react'; + +/** + * Brutalist reveal: a short, hard-eased rise. Snappy and mechanical, not a + * soft float. Uses opacity+translate (robust on above-the-fold mount, unlike + * clip-path). Reduced-motion renders static. + */ +export function Reveal({ + children, + delay = 0, + y = 22, + className, + style, +}: { + children: ReactNode; + delay?: number; + y?: number; + className?: string; + style?: CSSProperties; +}) { + const reduce = useReducedMotion(); + if (reduce) { + return ( +
+ {children} +
+ ); + } + return ( + + {children} + + ); +} diff --git a/app/home-poc/_components/Schematic.tsx b/app/home-poc/_components/Schematic.tsx new file mode 100644 index 00000000..dbbaa420 --- /dev/null +++ b/app/home-poc/_components/Schematic.tsx @@ -0,0 +1,230 @@ +'use client'; + +import { motion, useReducedMotion } from 'framer-motion'; + +/** + * The compliance graph as a full engineering figure: title block, coordinate + * ruler, boxed nodes with crop ticks, orthogonal bus wiring with junction + * dots, dimension lines, and a legend. Wires draw on scroll; the evidence run + * carries the red signal. Reads like a patent drawing, not a floaty diagram. + */ + +type Box = { + id: string; + x: number; + y: number; + w: number; + h: number; + n: string; + label: string; + sub: string; + hub?: boolean; + accent?: boolean; +}; + +const BOX: Record = { + obl: { id: 'obl', x: 40, y: 232, w: 236, h: 92, n: '1.0', label: 'OBLIGATION', sub: 'Framework reqs mapped' }, + ctl: { id: 'ctl', x: 486, y: 196, w: 300, h: 164, n: '2.0', label: 'CONTROL', sub: 'Owner + review cadence assigned', hub: true }, + fwk: { id: 'fwk', x: 996, y: 150, w: 252, h: 80, n: '3.0', label: 'TASK', sub: 'Routed to the owner' }, + own: { id: 'own', x: 996, y: 248, w: 252, h: 80, n: '4.0', label: 'OWNER', sub: 'Named · accountable' }, + evd: { id: 'evd', x: 996, y: 346, w: 252, h: 80, n: '5.0', label: 'EVIDENCE', sub: 'Linked & sealed to control', accent: true }, + aud: { id: 'aud', x: 486, y: 432, w: 300, h: 78, n: '6.0', label: 'AUDIT', sub: 'Complete, exportable trail' }, +}; + +const ORDER = ['obl', 'ctl', 'fwk', 'own', 'evd', 'aud']; + +function rightTo(a: Box, b: Box) { + const ax = a.x + a.w; + const ay = a.y + a.h / 2; + const bx = b.x; + const by = b.y + b.h / 2; + const midx = ax + (bx - ax) / 2; + return { d: `M ${ax} ${ay} H ${midx} V ${by} H ${bx}`, jx: midx, jy: ay }; +} +function drop(a: Box, b: Box) { + const ax = a.x + a.w / 2; + const ay = a.y + a.h; + const by = b.y; + return { d: `M ${ax} ${ay} V ${by}`, jx: ax, jy: ay }; +} + +const W_OBL = rightTo(BOX.obl, BOX.ctl); +const W_FWK = rightTo(BOX.ctl, BOX.fwk); +const W_OWN = rightTo(BOX.ctl, BOX.own); +const W_EVD = rightTo(BOX.ctl, BOX.evd); +const W_AUD = drop(BOX.ctl, BOX.aud); +const WIRES = [ + { ...W_OBL, accent: false }, + { ...W_FWK, accent: false }, + { ...W_OWN, accent: false }, + { ...W_EVD, accent: true }, + { ...W_AUD, accent: false }, +]; + +export function Schematic() { + const reduce = useReducedMotion(); + + return ( + + {/* outer frame + registration marks */} + + {[[8, 8], [1272, 8], [8, 552], [1272, 552]].map(([cx, cy], i) => ( + + + + + ))} + + {/* coordinate ruler ticks */} + {Array.from({ length: 12 }).map((_, i) => ( + + ))} + + {/* engineering title block */} + + + + + FIG.01 — COMPLIANCE GRAPH + PROJECT + FORMAOS + REV. + A / 2026 + + + {/* dimension line under the obligation→control span */} + + + + + MAPPED 1:1 + + + {/* wiring */} + {WIRES.map((w, i) => ( + + + {/* junction dot */} + + + ))} + + {/* continuous signal pulses travelling the bus */} + {!reduce && ( + <> + {[ + { d: W_OBL.d, begin: '0s', accent: false }, + { d: W_FWK.d, begin: '0.6s', accent: false }, + { d: W_OWN.d, begin: '1.1s', accent: false }, + { d: W_EVD.d, begin: '0.3s', accent: true }, + { d: W_AUD.d, begin: '0.9s', accent: false }, + ].map((p, i) => ( + + + + ))} + {/* scan sweep */} + + + + + + )} + + {/* live readout panel (top-right, balances the title block) */} + + + + LIVE READOUT + + {!reduce && } + + POSTURE + 82% + AUTO-EVALUATED + 102 / 252 + + + {/* boxes */} + {ORDER.map((id, i) => { + const b = BOX[id]; + const tall = b.h > 100; + return ( + + {[ + [b.x, b.y, 1, 1], + [b.x + b.w, b.y, -1, 1], + [b.x, b.y + b.h, 1, -1], + [b.x + b.w, b.y + b.h, -1, -1], + ].map(([cx, cy, dx, dy], k) => ( + + ))} + + FIG.01.{b.n} + + {b.label} + + {b.sub} + + ); + })} + + {/* legend */} + + + ACTION WIRE + + EVIDENCE / SIGNAL + + NODE + + JUNCTION + + + ); +} diff --git a/app/home-poc/_components/SecurityGrid.tsx b/app/home-poc/_components/SecurityGrid.tsx new file mode 100644 index 00000000..1475f2c0 --- /dev/null +++ b/app/home-poc/_components/SecurityGrid.tsx @@ -0,0 +1,94 @@ +import { Reveal } from './Reveal'; + +/** + * Security built into the platform layer — encryption, identity, tamper-evident + * audit, control mapping, AU-hosting — plus a sample of what the audit trail + * actually captures. Verbatim from the production SecuritySection. + */ + +const CARDS = [ + { + h: 'Encryption', + rows: [ + ['Data at rest', 'AES-256'], + ['Data in transit', 'TLS 1.3'], + ['Key management', 'AWS KMS'], + ['Backup encryption', 'AES-256'], + ], + }, + { + h: 'Identity & access', + rows: [ + ['SAML 2.0 SSO', 'Okta · Azure · Google'], + ['MFA enforcement', 'All plans'], + ['Role-based access', '4 roles'], + ['Session control', 'Timeout · IP lock'], + ], + }, + { + h: 'Control mapping', + rows: [ + ['SOC 2 TSC', '61 / 61'], + ['ISO 27001', '93 / 93'], + ['NDIS Practice', '25 / 25'], + ['Audit coverage', '100%'], + ], + }, +]; + +const CAPTURE = [ + ['Evidence approved', 'CC6.1 · Logical access', 'Control owner', 'VERIFIED'], + ['Control drift detected', 'A1.2 · Availability', 'System', 'ALERT'], + ['Audit packet exported', 'SOC 2 Type II · Full', 'Workspace admin', 'VERIFIED'], + ['Policy acknowledged', 'ISO 27001 · A.5.1', 'Policy reviewer', 'VERIFIED'], + ['Worker credential updated', 'NDIS · Screening', 'Workforce coord.', 'VERIFIED'], +]; + +export function SecurityGrid() { + return ( +
+
+
+ Security & trust +

+ Security built into
the platform layer. +

+
+

+ Controls are enforced, not just documented. Encryption, identity governance, and + tamper-evident logs are infrastructure — not add-ons. +

+
+ + +
+ {CARDS.map((c) => ( +
+
{c.h}
+ {c.rows.map(([k, v]) => ( +
+ {k} + {v} +
+ ))} +
+ ))} +
+
+ + +
+
What the audit trail captures · hash-chained, append-only
+ {CAPTURE.map(([ev, ctl, who, st]) => ( +
+ {ev} + {ctl} + {who} + {st} +
+ ))} +
+
+
+ ); +} diff --git a/app/home-poc/_components/SiteFooter.tsx b/app/home-poc/_components/SiteFooter.tsx new file mode 100644 index 00000000..ce9cb0c8 --- /dev/null +++ b/app/home-poc/_components/SiteFooter.tsx @@ -0,0 +1,47 @@ +/** Full editorial footer — link columns + colophon. */ + +const COLS = [ + ['Product', [['Platform', '/product'], ['Capabilities', '/features'], ['Pricing', '/pricing'], ['Integrations', '/integrations'], ['Changelog', '/changelog']]], + ['Frameworks', [['All frameworks', '/frameworks'], ['SOC 2', '/soc2-compliance-automation'], ['ISO 27001', '/iso-compliance-software'], ['NDIS', '/ndis-providers'], ['HIPAA', '/healthcare-compliance']]], + ['Industries', [['Healthcare', '/healthcare-compliance'], ['NDIS providers', '/ndis-providers'], ['Mental health', '/mental-health-compliance'], ['Financial', '/financial-services-compliance'], ['Government', '/use-cases/government-public-sector']]], + ['Company', [['Our story', '/our-story'], ['Trust center', '/trust'], ['Security', '/security'], ['Contact', '/contact'], ['Status', '/status']]], +] as const; + +export function SiteFooter() { + return ( +
+
+
+
+ FORMAOS +

+ The compliance operating system for regulated industries. Enforced workflows, + immutable evidence, audit-ready every day. +

+ +
+ {COLS.map(([title, links]) => ( +
+

{title}

+ {links.map(([label, href]) => ( + {label} + ))} +
+ ))} +
+
+ + © 2026 FORMAOS · COMPLIANCE OPERATING SYSTEM · ADELAIDE, SOUTH AUSTRALIA + + + {[['Privacy', '/privacy'], ['Terms', '/terms'], ['Trust', '/trust']].map(([l, h]) => ( + {l.toUpperCase()} + ))} + +
+
+
+ ); +} diff --git a/app/home-poc/_components/StickyCTA.tsx b/app/home-poc/_components/StickyCTA.tsx new file mode 100644 index 00000000..7b99cece --- /dev/null +++ b/app/home-poc/_components/StickyCTA.tsx @@ -0,0 +1,25 @@ +'use client'; + +import { useEffect, useState } from 'react'; + +/** Mobile-only sticky action bar: split red/ghost, appears past the hero. */ +export function StickyCTA() { + const [show, setShow] = useState(false); + useEffect(() => { + const onScroll = () => setShow(window.scrollY > 600); + onScroll(); + window.addEventListener('scroll', onScroll, { passive: true }); + return () => window.removeEventListener('scroll', onScroll); + }, []); + if (!show) return null; + return ( + + ); +} diff --git a/app/home-poc/_components/Ticker.tsx b/app/home-poc/_components/Ticker.tsx new file mode 100644 index 00000000..b11b5d2a --- /dev/null +++ b/app/home-poc/_components/Ticker.tsx @@ -0,0 +1,66 @@ +/** + * Bloomberg-style kinetic ticker. Two strips scrolling opposite directions; + * the top runs live framework posture, the bottom runs the operating creed. + * Pure CSS animation (robust, no JS), pauses on reduced-motion via CSS. + */ + +// Real framework packs shipping today (from FrameworkTrustStrip). +const POSTURE = [ + 'NDIS PRACTICE STANDARDS', + 'AGED CARE QUALITY STANDARDS', + 'NSQHS STANDARDS', + 'AHPRA', + 'ASIC s912A', + 'APRA CPS 230', + 'AUSTRAC AML/CTF', + 'ISO 27001', + 'SOC 2', + 'HIPAA', + 'GDPR', + 'NIST CSF', + 'ESSENTIAL EIGHT', +]; + +const CREED = [ + 'NAMED OWNERS', + 'IMMUTABLE EVIDENCE', + 'CONTINUOUS POSTURE', + 'AU-HOSTED BY DEFAULT', + 'BLOCKED FAILURE PATHS', + 'NO SPREADSHEETS', +]; + +export function Ticker() { + return ( +
+
+
+ {[0, 1].map((dup) => ( +
+ {POSTURE.map((f) => ( + + + {f} + + ))} +
+ ))} +
+
+
+
+ {[0, 1].map((dup) => ( +
+ {CREED.map((c) => ( + + {c} + / + + ))} +
+ ))} +
+
+
+ ); +} diff --git a/app/home-poc/_components/TrustWall.tsx b/app/home-poc/_components/TrustWall.tsx new file mode 100644 index 00000000..1a666ba5 --- /dev/null +++ b/app/home-poc/_components/TrustWall.tsx @@ -0,0 +1,50 @@ +import { Reveal } from './Reveal'; + +/** + * Framework coverage wall + the production infrastructure FormaOS is built on. + * Static, structured proof (the ticker is the kinetic counterpart). + */ + +const FRAMEWORKS = [ + 'NDIS Practice Standards', 'Aged Care Quality', 'NSQHS Standards', 'AHPRA', + 'ASIC s912A', 'APRA CPS 230', 'AUSTRAC AML/CTF', 'ACECQA NQF', + 'WHS Act', 'ISO 27001', 'SOC 2', 'GDPR', + 'NIST CSF', 'PCI DSS', 'HIPAA', 'CIS Controls', + 'ISO 9001', 'Essential Eight', +]; + +const BUILT_ON = ['Vercel', 'Supabase', 'Stripe', 'Sentry', 'Resend']; + +export function TrustWall() { + return ( +
+
+
+ Framework coverage +

+ Eighteen packs.
One evidence model. +

+
+

+ Australian regulatory coverage and international standards, shipping today in the + template and policy library — cross-mapped so one evidence item satisfies many. +

+
+ + +
+ {FRAMEWORKS.map((f) => ( +
{f}
+ ))} +
+
+ +
+ Built on production infrastructure + {BUILT_ON.map((b) => ( + {b} + ))} +
+
+ ); +} diff --git a/app/home-poc/_components/UseCases.tsx b/app/home-poc/_components/UseCases.tsx new file mode 100644 index 00000000..3043378d --- /dev/null +++ b/app/home-poc/_components/UseCases.tsx @@ -0,0 +1,119 @@ +'use client'; + +import { useState } from 'react'; +import { AnimatePresence, motion, useReducedMotion } from 'framer-motion'; + +/** + * Interactive use-case scenarios — anonymized, verbatim from production. + * Tab list on the left, animated detail on the right. + */ + +const CASES = [ + { + org: 'NDIS Provider', + framework: 'NDIS Practice Standards · all 8 modules', + challenge: 'Reportable incidents tracked in spreadsheets; Commission audits required days of reconstruction across multiple sites.', + outcomes: [ + 'Reportable-incident response inside the 24h immediate / 5 business-day detailed timelines', + 'Audit preparation time measured in hours, not weeks', + 'Named control owner at every Practice Standard module', + ], + }, + { + org: 'Healthcare Operator', + framework: 'NSQHS Standards · AHPRA · RACGP', + challenge: 'Clinical governance controls existed on paper, but proof was inconsistent across sites; practitioner registration tracked manually.', + outcomes: [ + 'AHPRA registration expiry alerts at 90 / 60 / 30 days', + 'Control-to-evidence mapping with NSQHS Standards linkage', + 'Live executive posture view across sites', + ], + }, + { + org: 'Aged Care Provider', + framework: 'Aged Care Quality Standards · 8 standards', + challenge: 'Policy changes were hard to roll out uniformly; periodic reviews slipped without reliable triggers; Standard 8 governance reporting consumed executive time.', + outcomes: [ + 'Policy review cadence enforced with automated task triggers', + 'Evidence renewal and expiry tracking across all facilities', + 'Standard 8 governance reporting compressed from weeks to days', + ], + }, + { + org: 'Financial Services', + framework: 'ISO 27001 · APRA CPS 234 · AML/CTF', + challenge: 'Third-party risk grew with fintech partnerships, but control ownership and evidence collection remained manual; ASIC breach reporting relied on email threads.', + outcomes: [ + 'APRA CPS 234 control mapping with named owners and evidence trails', + 'ASIC reportable-situation response inside the statutory window', + 'Board governance packs generated from live data, not reconstructed', + ], + }, +]; + +export function UseCases() { + const [active, setActive] = useState(0); + const reduce = useReducedMotion(); + const c = CASES[active]; + + return ( +
+
+
+ Use-case scenarios +

+ How regulated teams
operate with FormaOS. +

+
+

+ Anonymized scenarios from regulated organizations. Outcomes reflect conditions at + the time of deployment. +

+
+ +
+
+ {CASES.map((cs, i) => ( + + ))} +
+
+ + + + The challenge + +

{c.challenge}

+ + What changed + +
+ {c.outcomes.map((o) => ( +
{o}
+ ))} +
+
+
+
+
+

+ ANONYMIZED · WE CAN WALK THROUGH FULL DEPLOYMENTS DURING EVALUATION +

+
+ ); +} diff --git a/app/home-poc/layout.tsx b/app/home-poc/layout.tsx new file mode 100644 index 00000000..839ebaad --- /dev/null +++ b/app/home-poc/layout.tsx @@ -0,0 +1,47 @@ +import type { Metadata } from 'next'; +import { Archivo, Spline_Sans_Mono } from 'next/font/google'; +import './poc.css'; + +/** + * Brutalist-editorial type system. + * — Archivo (variable, incl. width axis) carries the whole grotesque voice: + * pushed to Black + Expanded it gives monumental poster headlines without + * the Inter/Sora/Fraunces "AI default" tell. + * — Spline Sans Mono is the technical metadata voice (mastheads, ledgers, + * coordinates, callouts). + * + * PRODUCTION UPGRADE: swap --bru-sans for a licensed grotesque (GT America / + * Söhne / ABC Diatype) and the display face for Druk Wide — one var change in + * poc.css. Stand-ins here are the closest self-hostable equivalents. + */ +const archivo = Archivo({ + subsets: ['latin'], + display: 'swap', + variable: '--bru-sans', + weight: 'variable', + axes: ['wdth'], +}); + +const mono = Spline_Sans_Mono({ + subsets: ['latin'], + display: 'swap', + variable: '--bru-mono', + weight: ['400', '500', '700'], +}); + +export const metadata: Metadata = { + title: 'FormaOS — Homepage POC (Brutalist-Editorial)', + robots: { index: false, follow: false }, +}; + +export default function HomePocLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( +
+ {children} +
+ ); +} diff --git a/app/home-poc/page.tsx b/app/home-poc/page.tsx new file mode 100644 index 00000000..a90d7444 --- /dev/null +++ b/app/home-poc/page.tsx @@ -0,0 +1,489 @@ +import { Reveal } from './_components/Reveal'; +import { Atmosphere } from './_components/Atmosphere'; +import { PostureSimulator } from './_components/PostureSimulator'; +import { Schematic } from './_components/Schematic'; +import { DashboardPreview } from './_components/DashboardPreview'; +import { Ticker } from './_components/Ticker'; +import { CountUp } from './_components/CountUp'; +import { HeroIntro } from './_components/HeroIntro'; +import { TrustWall } from './_components/TrustWall'; +import { HowItWorks } from './_components/HowItWorks'; +import { SecurityGrid } from './_components/SecurityGrid'; +import { UseCases } from './_components/UseCases'; +import { ObjectionHandling } from './_components/ObjectionHandling'; +import { Faq } from './_components/Faq'; +import { SiteFooter } from './_components/SiteFooter'; +import { StickyCTA } from './_components/StickyCTA'; + +export const dynamic = 'force-static'; + +const CONVICTION = [ + { + step: 'For operators', + title: 'Controls run as workflows, not documents', + body: 'Named tasks, approval gates, and evidence chains execute inside daily operations — not in a separate compliance layer.', + cta: 'See how it works', + href: '/product', + }, + { + step: 'For enterprise buyers', + title: 'One flow from security review to rollout', + body: 'Identity controls, audit exports, hosting posture, and procurement artifacts stay in a single narrative buyers can verify.', + cta: 'See enterprise path', + href: '/enterprise', + }, + { + step: 'For security reviewers', + title: 'Trust evidence is visible before the first call', + body: 'Trust documentation, evidence defensibility, and review-ready context surface early so reviewers can verify substance upfront.', + cta: 'Visit trust center', + href: '/trust', + }, +]; + +const ENGINE = [ + ['Obligation', 'Framework requirements mapped to controls'], + ['Control', 'Ownership and review cadence assigned'], + ['Task', 'Work routed to the accountable owner'], + ['Evidence', 'Artifacts linked and sealed to the control'], + ['Audit', 'Complete, exportable compliance trail'], +]; + +const CAPS = [ + ['Automation Engine', 'Triggers for evidence, tasks, policies, and certifications with auto-task generation and escalation.'], + ['Evidence Vault', 'Every upload, review, and approval tracked with full audit-trail context and chain of custody.'], + ['8 Framework Packs', 'SOC 2, ISO 27001, GDPR, HIPAA, PCI-DSS, NIST CSF, CIS, NDIS Practice Standards & Essential Eight — pre-built.'], + ['Compliance Gates', 'Block non-compliant actions before they happen with real-time validation and enforcement.'], + ['Executive Dashboard', 'C-level visibility into posture, framework health, risk trends, and control ownership.'], + ['Multi-Site Operations', 'Each entity keeps its own controls and evidence with cross-site rollup for executive governance.'], + ['REST API + Webhooks', 'API v1 for compliance data, evidence uploads, and task management. Webhooks for SIEM and tooling.'], + ['AI Compliance Assistant', 'Context-aware AI drafts policies, runs gap analysis, and gives steps — powered by your live org data.'], +]; + +const PILLARS = [ + { + tag: 'Tamper-evident by construction', + h: 'HMAC-chained rows', + body: 'Each row carries a sequence number and an HMAC-SHA256 signature linking it to the previous row. A nightly cron re-walks the chain; any drift surfaces as a chain-integrity break before the next audit.', + }, + { + tag: 'Verifiable without trusting us', + h: 'External anchor at 05:30 UTC', + body: 'Daily, each org’s chain top is submitted to Sigstore Rekor as an RFC 6962 Merkle entry. An auditor can verify the timestamp of any event through Linux Foundation infrastructure — not ours.', + }, + { + tag: 'Immutable, even to platform admins', + h: 'Append-only at the database', + body: 'A BEFORE UPDATE OR DELETE trigger rejects any mutation of audit rows, backed by restrictive RLS deny policies. Even a service-role admin that bypasses RLS is stopped by the trigger. Enforced by Postgres, not app code.', + }, +]; + +const FACTS = [ + ['HMAC-SHA256', 'Row signature'], + ['RFC 6962', 'Merkle proof'], + ['05:30 UTC', 'Daily anchor'], + ['Append-only', 'DB trigger + RLS'], + ['Sigstore Rekor', 'External log'], +]; + +const INDUSTRIES: Array<{ name: string; tags: string[]; href: string }> = [ + { name: 'Healthcare', tags: ['HIPAA', 'RACGP', 'AHPRA', 'NSQHS'], href: '/healthcare-compliance' }, + { name: 'NDIS Providers', tags: ['Practice Standards', 'Q&S Commission'], href: '/ndis-providers' }, + { name: 'Mental Health', tags: ['NSMHS', 'Restrictive Practices'], href: '/mental-health-compliance' }, + { name: 'Financial Services', tags: ['SOC 2', 'ISO 27001', 'ASIC', 'APRA CPS 230'], href: '/financial-services-compliance' }, + { name: 'Education', tags: ['TEQSA', 'ASQA', 'RTO', 'VRQA'], href: '/industries' }, + { name: 'Government', tags: ['ISM', 'PSPF', 'Essential Eight', 'FOI'], href: '/use-cases/government-public-sector' }, +]; + +const BA = [ + { + impact: 'Auditor bundle, on demand', + before: 'Evidence scattered across email threads, shared drives, and spreadsheets. Days lost reconstructing trails.', + after: 'On-demand ZIP export: framework summary, evidence references with SHA-256 hashes, automation log, score history, chain top anchored to Sigstore Rekor.', + tag: 'Hash-chained', + }, + { + impact: 'Statutory clock, automated', + before: 'Email threads, ad-hoc severity tagging, statutory timelines tracked by memory.', + after: 'org_incidents writes carry severity, named owner, and the NDIS SIRS 24h-immediate / 5-business-day-detailed clock encoded in the predicate.', + tag: '24h / 5bd', + }, + { + impact: 'Refreshed nightly', + before: 'Manual status reconciliation. The board gets a stale quarterly snapshot. Drift surfaces too late.', + after: 'Nightly cron at 06:00 UTC writes org_control_evaluations; /app/compliance/health renders live posture with a 4-week sparkline.', + tag: 'Cron-driven', + }, +]; + +const STATS = [ + ['8', 'Framework packs'], + ['252', 'Controls mapped'], + ['102', 'Auto-evaluated'], + ['150', 'Manual attestations'], + ['16', 'Production crons'], +]; + +export default function HomePocPage() { + return ( +
+ + + {/* ========== MASTHEAD ========== */} +
+
+
+
+ FORMAOS +
+
COMPLIANCE OPERATING SYSTEM
+
ADELAIDE · AU
+
+ +
+ + OPERATIONAL +
+
+
+
+ + {/* ========== HERO ========== */} + + + {/* ========== TICKER ========== */} + + + {/* ========== WHY BUYERS STAY ========== */} +
+
+
+ Why buyers stay +

+ Three paths to conviction,
visible before the first call. +

+
+
+
+ {CONVICTION.map((c, i) => ( + + + + ))} +
+
+ +
+ + {/* ========== TRUST WALL ========== */} + + +
+ + {/* ========== LIVE SYSTEM / INTERACTIVE SIMULATOR ========== */} +
+
+
+ Operating system architecture +

+ Not a repository.
A live system. +

+
+

+ Other tools store documents. FormaOS enforces the program — so posture moves + the moment a control does. Try it: toggle controls and watch it recompute. +

+
+ + + +
+ +
+ + {/* ========== COMPLIANCE ENGINE: DASHBOARD + SCHEMATIC ========== */} +
+
+
+ Compliance engine +

+ One connected lifecycle. +

+
+

+ Obligations become controls, controls generate tasks, tasks produce evidence — + and every step stays audit-ready, on one live screen. +

+
+ + {/* The live product */} + + + +

+ /APP/COMPLIANCE/HEALTH · LIVE POSTURE FROM org_control_evaluations · ILLUSTRATIVE ORG DATA +

+ + {/* The model behind the screen */} +
+
+
+ The model behind the screen +

+ Wired, not filed. +

+
+
+ +
+ +
+
+
+ {ENGINE.map(([t, d], i) => ( +
+
{t}
+

{d}

+ {i < ENGINE.length - 1 && } +
+ ))} +
+
+
+ +
+ + {/* ========== HOW IT WORKS ========== */} + + +
+ + {/* ========== CAPABILITIES ========== */} +
+
+
+ Platform capabilities +

+ Everything you need.
Nothing you don’t. +

+
+
+ +
+ {CAPS.map(([t, d]) => ( +
+ +
{t}
+

{d}

+
+ ))} +
+
+
+ +
+ + {/* ========== CRYPTOGRAPHIC AUDIT CHAIN ========== */} +
+
+
+ Cryptographic audit chain +

+ Verifiable, not just
“we have logs.” +

+
+

+ Every org’s audit log is hash-chained, RLS-locked against mutation, and anchored + daily to Sigstore Rekor — the same transparency log the Linux Foundation runs for + signed open-source releases. +

+
+ +
+ {PILLARS.map((p) => ( +
+ {p.tag} +

{p.h}

+

{p.body}

+
+ ))} +
+
+ +
+ {FACTS.map(([k, v]) => ( +
+
{k}
+
{v}
+
+ ))} +
+
+
+ +
+ + {/* ========== SECURITY ========== */} + + +
+ + {/* ========== INDUSTRIES INDEX ========== */} +
+
+
+ Regulated industries +

+ Built for high-accountability industries. +

+
+
+ + + +
+ +
+ + {/* ========== USE CASES ========== */} + + +
+ + {/* ========== OUTCOME PROOF ========== */} +
+
+
+ What ships, what runs +

+ Operational mechanics,
not customer claims. +

+
+

+ Every number below comes from the framework registry checked into the codebase + or the cron schedule running in production. +

+
+ + +
+ {BA.map((b) => ( +
+
{b.impact}
+
+ Status quo +

{b.before}

+
+
+ With FormaOS · {b.tag} +

{b.after}

+
+
+ ))} +
+
+ + +
+ {STATS.map(([n, l]) => ( +
+
+
{l}
+
+ ))} +
+
+
+ +
+ + {/* ========== OBJECTION HANDLING ========== */} + + +
+ + {/* ========== FAQ ========== */} + + + {/* ========== RED STATEMENT BAND ========== */} +
+
+

+ Stop preparing
for audits. +

+

+ Start being audit-ready. +

+
+
+ + {/* ========== CTA + COLOPHON ========== */} +
+
+
+ +

+ GUIDED ASSESSMENT · SECURITY REVIEW PACKET · AU-HOSTED BY DEFAULT +

+
+ {[ + ['SOC 2-ALIGNED', 'Trust framework'], + ['AU-HOSTED', 'Data sovereignty'], + ['ENTERPRISE SSO', 'SAML 2.0 + MFA'], + ].map(([k, v]) => ( +
+
{k}
+
{v}
+
+ ))} +
+
+
+
+ {[ + ['HOSTING', 'Australia (Sydney region)'], + ['HQ', 'Adelaide, South Australia'], + ['FRAMEWORKS', '8 packs shipping today'], + ['CONTACT', 'support@formaos.com.au'], + ].map(([k, v]) => ( +
+ {k} + {v} +
+ ))} +
+
+
+
+ + + + +
+
+ ); +} diff --git a/app/home-poc/poc.css b/app/home-poc/poc.css new file mode 100644 index 00000000..4abf5278 --- /dev/null +++ b/app/home-poc/poc.css @@ -0,0 +1,1678 @@ +/* ============================================================ + FormaOS — Brutalist-Editorial POC (dark near-black + 1 accent) + Scoped under .bru-root. Reference world: Stripe Press / Pitch / + Bloomberg terminal. Exposed structural grid, monumental expanded- + black caps, a terminal data ledger, an engineering-schematic graph, + a numbered manifesto. One loud signal-red accent. + ============================================================ */ + +.bru-root { + /* Canvas */ + --bg: #0b0b0c; /* near-black, NOT cyan-space */ + --bg-panel: #fff; /* inverted panels punch against the dark */ + --bg-raise: #141416; + --ink: #eceae3; /* warm off-white */ + --ink-dim: #8c8c86; + --ink-faint: #5a5a54; + --line: rgba(236, 234, 227, 0.15); + --line-2: rgba(236, 234, 227, 0.32); + --red: #ff3a2d; /* the one and only accent */ + --red-deep: #d62c20; + /* "confirmed/positive" reads as neutral off-white — strictly one accent */ + --ok: #ece9e0; + + --sans: var(--bru-sans), 'Arial Narrow', system-ui, sans-serif; + --mono: var(--bru-mono), ui-monospace, 'SF Mono', monospace; + + /* The grid: a 12-col rhythm with a fixed gutter */ + --gutter: clamp(1rem, 3.2vw, 3rem); + --maxw: 1320px; + + background: var(--bg); + color: var(--ink); + font-family: var(--sans); + -webkit-font-smoothing: antialiased; + min-height: 100vh; + overflow-x: hidden; +} +.bru-root *, +.bru-root *::before, +.bru-root *::after { + box-sizing: border-box; +} +.bru-root ::selection { + background: var(--red); + color: #fff; +} + +/* ---------- Type primitives ---------- */ +.bru-mono { + font-family: var(--mono); + font-weight: 400; + letter-spacing: 0.02em; +} + +/* Monumental poster display — Archivo pushed to Black + Expanded */ +.bru-display { + font-family: var(--sans); + font-weight: 900; + font-variation-settings: 'wght' 900, 'wdth' 112; + text-transform: uppercase; + line-height: 0.84; + letter-spacing: -0.016em; + color: var(--ink); + margin: 0; +} + +.bru-h2 { + font-family: var(--sans); + font-weight: 800; + font-variation-settings: 'wght' 820, 'wdth' 118; + text-transform: uppercase; + line-height: 0.9; + letter-spacing: -0.008em; + margin: 0; +} + +/* Mono kicker / section tag */ +.bru-kicker { + font-family: var(--mono); + font-size: 12px; + font-weight: 500; + letter-spacing: 0.14em; + text-transform: uppercase; + color: var(--ink-dim); +} +.bru-kicker b { + color: var(--red); + font-weight: 500; +} + +.bru-deck { + font-family: var(--sans); + font-weight: 400; + font-size: clamp(1rem, 0.5vw + 0.95rem, 1.2rem); + line-height: 1.45; + color: var(--ink); +} +.bru-body { + font-family: var(--sans); + font-weight: 400; + font-size: 0.98rem; + line-height: 1.55; + color: var(--ink-dim); +} + +/* ---------- Frame / grid scaffolding ---------- */ +.bru-frame { + width: 100%; + max-width: var(--maxw); + margin: 0 auto; + padding-inline: var(--gutter); +} + +/* Exposed vertical column rules behind a section */ +.bru-cols { + position: absolute; + inset: 0; + max-width: var(--maxw); + margin: 0 auto; + padding-inline: var(--gutter); + pointer-events: none; + display: grid; + grid-template-columns: repeat(12, 1fr); +} +.bru-cols span { + border-left: 1px solid var(--line); +} +.bru-cols span:last-child { + border-right: 1px solid var(--line); +} +@media (max-width: 900px) { + .bru-cols { + grid-template-columns: repeat(4, 1fr); + } +} + +.bru-rule { + height: 0; + border: 0; + border-top: 1px solid var(--line); + margin: 0; +} +.bru-rule-strong { + border-top: 1.5px solid var(--line-2); +} +.bru-rule-red { + border-top: 2px solid var(--red); +} + +/* ---------- Masthead ---------- */ +.bru-masthead { + border-bottom: 1px solid var(--line-2); + position: sticky; + top: 0; + z-index: 50; + background: color-mix(in srgb, var(--bg) 88%, transparent); + backdrop-filter: blur(6px); +} +.bru-masthead-row { + display: flex; + align-items: stretch; +} +.bru-mh-cell { + display: flex; + align-items: center; + gap: 8px; + padding: 11px 16px; + border-left: 1px solid var(--line); + font-family: var(--mono); + font-size: 11.5px; + letter-spacing: 0.06em; + text-transform: uppercase; + color: var(--ink-dim); + white-space: nowrap; +} +.bru-mh-cell:first-child { + border-left: 0; + padding-left: 0; +} +.bru-wordmark { + font-family: var(--sans); + font-weight: 900; + font-variation-settings: 'wght' 900, 'wdth' 120; + letter-spacing: 0.02em; + font-size: 17px; + color: var(--ink); + text-transform: uppercase; +} +.bru-live-dot { + width: 7px; + height: 7px; + background: var(--red); + border-radius: 1px; + animation: bru-blink 1.8s steps(1) infinite; +} +@keyframes bru-blink { + 50% { + opacity: 0.25; + } +} + +/* ---------- Buttons ---------- */ +.bru-btn { + display: inline-flex; + align-items: center; + gap: 0.6rem; + font-family: var(--mono); + font-size: 13px; + font-weight: 500; + letter-spacing: 0.06em; + text-transform: uppercase; + padding: 0.85rem 1.3rem; + text-decoration: none; + border: 1.5px solid var(--ink); + background: transparent; + color: var(--ink); + border-radius: 0; + transition: + background 0.18s ease, + color 0.18s ease, + transform 0.18s ease; +} +.bru-btn:hover { + background: var(--ink); + color: var(--bg); +} +.bru-btn-red { + background: var(--red); + border-color: var(--red); + color: #fff; +} +.bru-btn-red:hover { + background: var(--red-deep); + border-color: var(--red-deep); + color: #fff; +} +.bru-btn .bru-arrow { + transition: transform 0.18s ease; +} +.bru-btn:hover .bru-arrow { + transform: translateX(4px); +} + +/* ---------- Terminal data ledger ---------- */ +.bru-ledger { + width: 100%; + border-collapse: collapse; + font-family: var(--mono); +} +.bru-ledger th { + text-align: right; + font-weight: 500; + font-size: 11px; + letter-spacing: 0.1em; + text-transform: uppercase; + color: var(--ink-faint); + padding: 10px 14px; + border-bottom: 1px solid var(--line-2); +} +.bru-ledger th:first-child, +.bru-ledger td:first-child { + text-align: left; +} +.bru-ledger td { + text-align: right; + padding: 14px; + border-bottom: 1px solid var(--line); + font-size: 13.5px; + color: var(--ink); +} +.bru-ledger tr:last-child td { + border-bottom: 0; +} +.bru-ledger .bru-fw { + font-family: var(--sans); + font-weight: 700; + font-variation-settings: 'wght' 700, 'wdth' 110; + text-transform: uppercase; + letter-spacing: 0.01em; + font-size: 15px; +} +.bru-score { + color: var(--ok); + font-weight: 700; +} +.bru-tag { + font-size: 10.5px; + letter-spacing: 0.08em; + padding: 2px 7px; + border: 1px solid var(--line-2); + text-transform: uppercase; +} +.bru-tag-live { + color: var(--ok); + border-color: color-mix(in srgb, var(--ok) 45%, transparent); +} +.bru-bar { + display: inline-block; + height: 6px; + background: var(--ok); + vertical-align: middle; +} +.bru-bar-track { + display: inline-block; + width: 90px; + height: 6px; + background: var(--line); + vertical-align: middle; +} + +/* ---------- Inverted console panel (product vignette) ---------- */ +.bru-panel { + background: var(--bg-raise); + border: 1px solid var(--line-2); +} +.bru-panel-bar { + display: flex; + align-items: center; + justify-content: space-between; + padding: 9px 14px; + border-bottom: 1px solid var(--line); + font-family: var(--mono); + font-size: 11px; + letter-spacing: 0.06em; + text-transform: uppercase; + color: var(--ink-dim); +} +.bru-prow { + display: flex; + align-items: center; + justify-content: space-between; + padding: 12px 14px; + border-bottom: 1px solid var(--line); + font-family: var(--mono); + font-size: 12.5px; +} +.bru-prow:last-child { + border-bottom: 0; +} +.bru-chip { + font-family: var(--mono); + font-size: 10px; + letter-spacing: 0.06em; + padding: 2px 6px; + border: 1px solid var(--line-2); + text-transform: uppercase; +} +.bru-chip-met { + color: var(--ok); + border-color: color-mix(in srgb, var(--ok) 45%, transparent); +} +.bru-chip-gap { + color: var(--red); + border-color: color-mix(in srgb, var(--red) 55%, transparent); +} + +/* ---------- Giant index numeral (manifesto) ---------- */ +.bru-index { + font-family: var(--sans); + font-weight: 900; + font-variation-settings: 'wght' 900, 'wdth' 125; + font-size: clamp(5rem, 14vw, 11rem); + font-variation-settings: 'wght' 900, 'wdth' 112; + line-height: 0.78; + color: transparent; + -webkit-text-stroke: 1.5px var(--line-2); + letter-spacing: -0.02em; +} +.bru-index-red { + -webkit-text-stroke-color: var(--red); + color: transparent; +} + +/* ---------- Big statement band ---------- */ +.bru-band { + background: var(--red); + color: #fff; +} +.bru-band .bru-display { + color: #fff; +} + +/* ---------- Schematic ---------- */ +.bru-schem-label { + font-family: var(--mono); + font-size: 11px; + letter-spacing: 0.04em; + fill: var(--ink); + text-transform: uppercase; +} +.bru-schem-sub { + font-family: var(--mono); + font-size: 9.5px; + fill: var(--ink-dim); +} + +/* ---------- Mobile sticky CTA ---------- */ +.bru-sticky { + position: fixed; + left: 0; + right: 0; + bottom: 0; + z-index: 60; + display: flex; + align-items: stretch; + border-top: 1.5px solid var(--line-2); + background: var(--bg); +} +.bru-sticky a { + flex: 1; + display: flex; + align-items: center; + justify-content: center; + font-family: var(--mono); + font-size: 13px; + letter-spacing: 0.06em; + text-transform: uppercase; + text-decoration: none; + padding: 16px 12px; + padding-bottom: calc(16px + env(safe-area-inset-bottom)); +} + +/* ---------- Kinetic ticker ---------- */ +.bru-ticker { + border-top: 1.5px solid var(--line-2); + border-bottom: 1.5px solid var(--line-2); + overflow: hidden; +} +.bru-ticker-row { + overflow: hidden; + white-space: nowrap; +} +.bru-ticker-row--alt { + background: var(--red); + border-top: 1px solid var(--line-2); +} +.bru-ticker-track { + display: inline-flex; + width: max-content; + animation: bru-marquee 38s linear infinite; +} +.bru-ticker-track--rev { + animation-direction: reverse; + animation-duration: 30s; +} +.bru-ticker-group { + display: inline-flex; + flex: 0 0 auto; +} +.bru-ticker-item { + display: inline-flex; + align-items: center; + gap: 12px; + padding: 14px 30px; + font-family: var(--sans); + font-weight: 800; + font-variation-settings: 'wght' 800, 'wdth' 110; + text-transform: uppercase; + font-size: clamp(1rem, 1.6vw, 1.5rem); + letter-spacing: 0.01em; + color: var(--ink); +} +.bru-ticker-val { + font-family: var(--mono); + font-style: normal; + font-weight: 500; + font-size: 0.7em; + color: var(--ok); +} +.bru-ticker-dot { + width: 8px; + height: 8px; + background: var(--red); +} +.bru-ticker-item--creed { + color: #fff; + font-size: clamp(0.85rem, 1.3vw, 1.2rem); + gap: 22px; +} +.bru-ticker-slash { + color: rgba(255, 255, 255, 0.55); + font-weight: 400; +} +@keyframes bru-marquee { + from { + transform: translateX(0); + } + to { + transform: translateX(-50%); + } +} + +/* ---------- Ledger row interactivity ---------- */ +.bru-ledger tbody tr { + transition: background 0.15s ease; +} +.bru-ledger tbody tr:hover td { + background: var(--bg-raise); +} +.bru-ledger tbody tr:hover td:first-child { + box-shadow: inset 3px 0 0 var(--red); +} + +/* ---------- Industries index ---------- */ +.bru-idx-row { + display: grid; + grid-template-columns: minmax(0, 1fr) auto auto; + align-items: center; + gap: clamp(12px, 2vw, 28px); + padding: clamp(1.1rem, 2.4vw, 1.8rem) 4px; + border-top: 1px solid var(--line); + text-decoration: none; + color: var(--ink); + transition: + padding-left 0.2s ease, + color 0.2s ease; +} +.bru-idx-tags { + display: flex; + gap: 8px; + flex-wrap: wrap; + justify-content: flex-end; +} +.bru-idx-tag { + font-family: var(--mono); + font-size: 11px; + letter-spacing: 0.03em; + color: var(--ink-dim); + border: 1px solid var(--line-2); + padding: 3px 9px; + text-transform: uppercase; + white-space: nowrap; + transition: border-color 0.2s ease, color 0.2s ease; +} +.bru-idx-row:hover .bru-idx-tag { + border-color: color-mix(in srgb, var(--red) 45%, transparent); + color: var(--ink); +} +.bru-idx-row:last-child { + border-bottom: 1px solid var(--line); +} +.bru-idx-row:hover { + padding-left: 18px; +} +.bru-idx-num { + font-family: var(--mono); + font-size: 12px; + color: var(--ink-faint); + letter-spacing: 0.06em; +} +.bru-idx-name { + font-family: var(--sans); + font-weight: 800; + font-variation-settings: 'wght' 800, 'wdth' 112; + text-transform: uppercase; + font-size: clamp(1.4rem, 3.4vw, 2.6rem); + line-height: 1; + letter-spacing: -0.01em; +} +.bru-idx-row:hover .bru-idx-name { + color: var(--red); +} +.bru-idx-meta { + font-family: var(--mono); + font-size: 11.5px; + color: var(--ink-dim); + text-align: right; + letter-spacing: 0.04em; +} +.bru-idx-arrow { + font-family: var(--mono); + color: var(--ink-faint); + transition: transform 0.2s ease, color 0.2s ease; +} +.bru-idx-row:hover .bru-idx-arrow { + color: var(--red); + transform: translateX(5px); +} + +/* ---------- Section rhythm (generous, confident spacing) ---------- */ +.bru-section { + padding-block: clamp(4.5rem, 9vw, 8.5rem); +} +.bru-section-sm { + padding-block: clamp(3.5rem, 6vw, 5.5rem); +} +.bru-head { + display: flex; + justify-content: space-between; + align-items: baseline; + gap: 16px; + flex-wrap: wrap; + margin-bottom: clamp(2rem, 4vw, 3.5rem); +} +.bru-eyebrow { + font-family: var(--mono); + font-size: 12px; + font-weight: 500; + letter-spacing: 0.16em; + text-transform: uppercase; + color: var(--red); +} + +/* ---------- Cards (conviction paths / generic) ---------- */ +.bru-card-b { + border: 1px solid var(--line-2); + background: var(--bg-raise); + padding: clamp(1.5rem, 2.4vw, 2.25rem); + display: flex; + flex-direction: column; + height: 100%; + transition: border-color 0.2s ease; +} +.bru-card-b:hover { + border-color: var(--ink-dim); +} +.bru-card-step { + font-family: var(--mono); + font-size: 12px; + color: var(--ink-faint); + letter-spacing: 0.08em; + margin-bottom: 18px; +} +.bru-card-title { + font-family: var(--sans); + font-weight: 800; + font-variation-settings: 'wght' 760, 'wdth' 110; + text-transform: uppercase; + font-size: clamp(1.1rem, 1.6vw, 1.4rem); + line-height: 1.04; + letter-spacing: -0.005em; +} +.bru-card-link { + margin-top: auto; + padding-top: 22px; + font-family: var(--mono); + font-size: 12px; + letter-spacing: 0.06em; + text-transform: uppercase; + color: var(--ink); + text-decoration: none; + display: inline-flex; + align-items: center; + gap: 8px; +} +.bru-card-link:hover { + color: var(--red); +} + +/* ---------- Capabilities grid ---------- */ +.bru-cap-grid { + display: grid; + grid-template-columns: repeat(4, 1fr); + border-top: 1px solid var(--line-2); + border-left: 1px solid var(--line-2); +} +.bru-cap { + position: relative; + border-right: 1px solid var(--line); + border-bottom: 1px solid var(--line); + padding: clamp(1.4rem, 2vw, 2rem); + min-height: 196px; + display: flex; + flex-direction: column; + transition: background 0.18s ease, box-shadow 0.18s ease; +} +.bru-cap:hover { + background: var(--bg-raise); + box-shadow: inset 0 3px 0 var(--red); +} +.bru-cap-tick { + position: absolute; + top: clamp(1.4rem, 2vw, 2rem); + right: clamp(1.4rem, 2vw, 2rem); + width: 8px; + height: 8px; + background: var(--line-2); + transition: background 0.18s ease, transform 0.18s ease; +} +.bru-cap:hover .bru-cap-tick { + background: var(--red); + transform: scale(1.35); +} +.bru-cap-t { + font-family: var(--sans); + font-weight: 800; + font-variation-settings: 'wght' 740, 'wdth' 108; + text-transform: uppercase; + font-size: 1.05rem; + line-height: 1.05; + padding-right: 18px; +} +.bru-cap-d { + font-size: 0.86rem; + line-height: 1.5; + color: var(--ink-dim); + margin-top: 12px; +} +@media (max-width: 900px) { + .bru-cap-grid { + grid-template-columns: repeat(2, 1fr); + } +} +@media (max-width: 520px) { + .bru-cap-grid { + grid-template-columns: 1fr; + } +} + +/* ---------- Audit-chain pillars + facts bar ---------- */ +.bru-pillars { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 0; + border-top: 1px solid var(--line-2); +} +.bru-pillar { + padding: clamp(1.5rem, 2.4vw, 2.25rem) clamp(1.25rem, 2vw, 2rem); + border-right: 1px solid var(--line); +} +.bru-pillar:last-child { + border-right: 0; +} +.bru-pillar-tag { + font-family: var(--mono); + font-size: 11px; + letter-spacing: 0.06em; + text-transform: uppercase; + color: var(--red); +} +.bru-pillar-h { + font-family: var(--sans); + font-weight: 800; + font-variation-settings: 'wght' 760, 'wdth' 110; + text-transform: uppercase; + font-size: 1.15rem; + line-height: 1.05; + margin: 14px 0 14px; +} +.bru-facts { + display: grid; + grid-template-columns: repeat(5, 1fr); + border: 1px solid var(--line-2); + border-top: 0; +} +.bru-fact { + padding: 16px; + border-right: 1px solid var(--line); +} +.bru-fact:last-child { + border-right: 0; +} +.bru-fact-k { + font-family: var(--mono); + font-weight: 500; + font-size: 13px; + color: var(--ink); +} +.bru-fact-v { + font-family: var(--mono); + font-size: 11px; + color: var(--ink-dim); + margin-top: 4px; +} +@media (max-width: 900px) { + .bru-pillars { + grid-template-columns: 1fr; + } + .bru-pillar { + border-right: 0; + border-bottom: 1px solid var(--line); + } + .bru-facts { + grid-template-columns: repeat(2, 1fr); + } + .bru-fact { + border-bottom: 1px solid var(--line); + } +} + +/* engine flow arrow */ +.bru-flow-arrow { + position: absolute; + right: -9px; + top: 50%; + transform: translateY(-50%); + z-index: 3; + font-family: var(--mono); + font-size: 15px; + color: var(--red); + background: var(--bg); + padding: 2px 0; +} +@media (max-width: 640px) { + .bru-flow-arrow { + display: none; + } +} + +/* ---------- Before / after proof ---------- */ +.bru-ba { + border-top: 1px solid var(--line); + padding: clamp(1.5rem, 2.6vw, 2.25rem) 0; + display: grid; + grid-template-columns: 240px 1fr 1fr; + gap: clamp(1rem, 3vw, 2.5rem); + align-items: start; +} +.bru-ba:last-child { + border-bottom: 1px solid var(--line); +} +.bru-ba-impact { + font-family: var(--sans); + font-weight: 800; + font-variation-settings: 'wght' 760, 'wdth' 112; + text-transform: uppercase; + font-size: clamp(1.1rem, 1.7vw, 1.5rem); + line-height: 1.02; +} +.bru-ba-col .bru-ba-label { + font-family: var(--mono); + font-size: 11px; + letter-spacing: 0.08em; + text-transform: uppercase; + color: var(--ink-faint); + display: block; + margin-bottom: 10px; +} +.bru-ba-before { + color: var(--ink-faint); + font-size: 0.92rem; + line-height: 1.5; +} +.bru-ba-after { + color: var(--ink); + font-size: 0.92rem; + line-height: 1.5; +} +@media (max-width: 760px) { + .bru-ba { + grid-template-columns: 1fr; + gap: 1rem; + } +} + +/* ---------- Big stat ledger ---------- */ +.bru-stats { + display: grid; + grid-template-columns: repeat(5, 1fr); + border: 1px solid var(--line-2); +} +.bru-stat-cell { + padding: clamp(1.25rem, 2vw, 1.75rem); + border-right: 1px solid var(--line); +} +.bru-stat-cell:last-child { + border-right: 0; +} +.bru-stat-n { + font-family: var(--sans); + font-weight: 900; + font-variation-settings: 'wght' 900, 'wdth' 112; + font-size: clamp(2.4rem, 4vw, 3.4rem); + line-height: 0.9; + letter-spacing: -0.02em; +} +.bru-stat-l { + font-family: var(--mono); + font-size: 11px; + letter-spacing: 0.04em; + text-transform: uppercase; + color: var(--ink-dim); + margin-top: 10px; +} +@media (max-width: 760px) { + .bru-stats { + grid-template-columns: repeat(2, 1fr); + } + .bru-stat-cell { + border-bottom: 1px solid var(--line); + } +} + +/* ============================================================ + Product dashboard preview — faithful to /app/compliance/health + + command-center, rendered in the brutalist monochrome+red palette. + ============================================================ */ +.dash-win { + border: 1px solid var(--line-2); + background: var(--bg); + box-shadow: 0 50px 90px -50px rgba(0, 0, 0, 0.85); +} +.dash-chrome { + display: flex; + align-items: center; + gap: 12px; + padding: 9px 14px; + border-bottom: 1px solid var(--line); +} +.dash-tl { + display: flex; + gap: 7px; +} +.dash-tl i { + width: 10px; + height: 10px; + border-radius: 50%; + border: 1px solid var(--line-2); + display: block; +} +.dash-url { + font-family: var(--mono); + font-size: 11px; + color: var(--ink-dim); + border: 1px solid var(--line); + padding: 4px 12px; + flex: 1; + max-width: 440px; +} +.dash-body { + display: grid; + grid-template-columns: 192px 1fr; +} +.dash-side { + border-right: 1px solid var(--line); + padding: 14px 0; + display: flex; + flex-direction: column; + gap: 2px; +} +.dash-brand { + display: flex; + align-items: center; + gap: 9px; + padding: 0 14px 14px; +} +.dash-mark { + width: 24px; + height: 24px; + background: var(--red); + color: #fff; + font-family: var(--sans); + font-weight: 900; + font-size: 11px; + display: flex; + align-items: center; + justify-content: center; +} +.dash-ctx { + font-family: var(--mono); + font-size: 9.5px; + letter-spacing: 0.12em; + text-transform: uppercase; + color: var(--ink-faint); + padding: 0 14px 8px; +} +.dash-nav-item { + display: flex; + align-items: center; + justify-content: space-between; + padding: 8px 14px; + font-size: 12.5px; + color: var(--ink-dim); + border-left: 2px solid transparent; +} +.dash-nav-item.is-active { + color: var(--ink); + border-left-color: var(--red); + background: var(--bg-raise); +} +.dash-nav-item .dash-rag { + width: 6px; + height: 6px; + border-radius: 50%; +} +.dash-side-foot { + margin-top: auto; + padding: 12px 14px 0; + border-top: 1px solid var(--line); + display: flex; + align-items: center; + gap: 8px; +} +.dash-ava { + width: 22px; + height: 22px; + border: 1px solid var(--line-2); + display: flex; + align-items: center; + justify-content: center; + font-family: var(--mono); + font-size: 10px; + color: var(--ink-dim); +} +.dash-main { + display: flex; + flex-direction: column; + min-width: 0; +} +.dash-topbar, +.dash-strip { + display: flex; + align-items: center; + gap: 18px; + padding: 10px 16px; + border-bottom: 1px solid var(--line); + font-family: var(--mono); + font-size: 11px; + color: var(--ink-dim); +} +.dash-strip { + font-size: 10.5px; + letter-spacing: 0.04em; +} +.dash-sd { + display: inline-flex; + align-items: center; + gap: 7px; +} +.dash-sd i { + width: 7px; + height: 7px; + border-radius: 50%; + display: block; +} +.dash-pad { + padding: 16px; +} +.dash-kpis { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 1px; + background: var(--line); + border: 1px solid var(--line); +} +.dash-kpi { + background: var(--bg); + padding: 14px 15px; +} +.dash-kpi-l { + font-family: var(--mono); + font-size: 10px; + letter-spacing: 0.06em; + text-transform: uppercase; + color: var(--ink-faint); +} +.dash-kpi-v { + font-family: var(--sans); + font-weight: 900; + font-variation-settings: 'wght' 900, 'wdth' 108; + font-size: 30px; + line-height: 1; + margin-top: 10px; +} +.dash-kpi-c { + font-family: var(--mono); + font-size: 10px; + color: var(--ink-dim); + margin-top: 7px; +} +.dash-grid2 { + display: grid; + grid-template-columns: 1.25fr 1fr; + gap: 14px; + margin-top: 14px; +} +.dash-card { + border: 1px solid var(--line); + padding: 15px; +} +.dash-card-h { + font-family: var(--mono); + font-size: 10px; + letter-spacing: 0.06em; + text-transform: uppercase; + color: var(--ink-faint); + margin-bottom: 16px; + display: flex; + justify-content: space-between; + align-items: center; +} +.dash-tiles { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 8px; + margin-top: 16px; +} +.dash-tile { + border: 1px solid var(--line); + padding: 10px 8px; +} +.dash-tile-v { + font-family: var(--sans); + font-weight: 800; + font-variation-settings: 'wght' 800, 'wdth' 108; + font-size: 20px; + line-height: 1; +} +.dash-tile-l { + font-family: var(--mono); + font-size: 9px; + letter-spacing: 0.06em; + text-transform: uppercase; + color: var(--ink-faint); + margin-top: 6px; +} +.dash-fw { + display: flex; + align-items: center; + justify-content: space-between; + gap: 10px; + padding: 10px 0; + border-bottom: 1px solid var(--line); + font-size: 12.5px; +} +.dash-fw:last-child { + border-bottom: 0; +} +.dash-bar2 { + width: 78px; + height: 5px; + background: var(--line); + flex-shrink: 0; +} +.dash-bar2 span { + display: block; + height: 100%; + background: var(--ok); +} +.dash-oc { + display: flex; + align-items: center; + gap: 10px; + padding: 9px 0; + border-bottom: 1px solid var(--line); + font-family: var(--mono); + font-size: 11.5px; +} +.dash-oc:last-child { + border-bottom: 0; +} +.dash-pill { + font-family: var(--mono); + font-size: 9.5px; + letter-spacing: 0.04em; + padding: 2px 7px; + border: 1px solid var(--line-2); + text-transform: uppercase; + white-space: nowrap; +} +.dash-pill-fail { + color: var(--red); + border-color: color-mix(in srgb, var(--red) 50%, transparent); +} +@media (max-width: 920px) { + .dash-side { + display: none; + } + .dash-body { + grid-template-columns: 1fr; + } + .dash-grid2 { + grid-template-columns: 1fr; + } +} +@media (max-width: 560px) { + .dash-kpis { + grid-template-columns: repeat(2, 1fr); + } + .dash-tiles { + grid-template-columns: repeat(2, 1fr); + } +} + +@media (prefers-reduced-motion: reduce) { + .bru-ticker-track { + animation: none !important; + } +} + +/* Reveal: brutalist = hard clip wipe, not soft fade */ +@media (prefers-reduced-motion: reduce) { + .bru-root *, + .bru-root *::before, + .bru-root *::after { + animation-duration: 0.001ms !important; + transition-duration: 0.001ms !important; + } +} + +/* ============================================================ + TOP-TO-BOTTOM EXPANSION — additional sections + ============================================================ */ + +/* ---------- Hero stat row ---------- */ +.bru-herostats { + display: grid; + grid-template-columns: repeat(4, 1fr); + border: 1px solid var(--line-2); + margin-top: clamp(2.5rem, 4vw, 3.5rem); +} +.bru-herostat { + padding: 18px 20px; + border-right: 1px solid var(--line); +} +.bru-herostat:last-child { border-right: 0; } +.bru-herostat-n { + font-family: var(--sans); + font-weight: 900; + font-variation-settings: 'wght' 900, 'wdth' 110; + font-size: clamp(1.8rem, 3vw, 2.6rem); + line-height: 0.9; +} +.bru-herostat-l { + font-family: var(--mono); + font-size: 10.5px; + letter-spacing: 0.05em; + text-transform: uppercase; + color: var(--ink-dim); + margin-top: 10px; +} +@media (max-width: 760px) { + .bru-herostats { grid-template-columns: repeat(2, 1fr); } + .bru-herostat:nth-child(2) { border-right: 0; } + .bru-herostat:nth-child(1), .bru-herostat:nth-child(2) { border-bottom: 1px solid var(--line); } +} + +/* ---------- Trust wall ---------- */ +.bru-wall { + display: grid; + grid-template-columns: repeat(6, 1fr); + border-top: 1px solid var(--line-2); + border-left: 1px solid var(--line-2); +} +.bru-wall-cell { + border-right: 1px solid var(--line); + border-bottom: 1px solid var(--line); + padding: 22px 16px; + min-height: 92px; + display: flex; + align-items: center; + justify-content: center; + text-align: center; + font-family: var(--sans); + font-weight: 700; + font-variation-settings: 'wght' 680, 'wdth' 105; + font-size: 13px; + letter-spacing: 0.01em; + color: var(--ink-dim); + text-transform: uppercase; + transition: color 0.18s ease, background 0.18s ease; +} +.bru-wall-cell:hover { color: var(--ink); background: var(--bg-raise); } +@media (max-width: 900px) { .bru-wall { grid-template-columns: repeat(3, 1fr); } } +@media (max-width: 520px) { .bru-wall { grid-template-columns: repeat(2, 1fr); } } + +.bru-built { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: clamp(1.5rem, 4vw, 3.5rem); + margin-top: clamp(2rem, 3vw, 2.5rem); +} +.bru-built-label { + font-family: var(--mono); + font-size: 11px; + letter-spacing: 0.1em; + text-transform: uppercase; + color: var(--ink-faint); +} +.bru-built-name { + font-family: var(--sans); + font-weight: 700; + font-variation-settings: 'wght' 680, 'wdth' 108; + font-size: clamp(1.05rem, 1.6vw, 1.35rem); + color: var(--ink-dim); + text-transform: uppercase; + transition: color 0.18s ease; +} +.bru-built-name:hover { color: var(--ink); } + +/* ---------- How it works (vertical enforced loop) ---------- */ +.bru-steps { border-top: 1px solid var(--line-2); } +.bru-step { + display: grid; + grid-template-columns: 92px 1fr auto; + gap: clamp(1rem, 3vw, 2.5rem); + align-items: start; + padding: clamp(1.75rem, 3vw, 2.75rem) 0; + border-bottom: 1px solid var(--line); + position: relative; + transition: padding-left 0.2s ease; +} +.bru-step:hover { padding-left: 12px; } +.bru-step-n { + font-family: var(--sans); + font-weight: 900; + font-variation-settings: 'wght' 900, 'wdth' 118; + font-size: clamp(2.2rem, 4vw, 3.4rem); + line-height: 0.8; + color: transparent; + -webkit-text-stroke: 1.3px var(--line-2); + transition: -webkit-text-stroke-color 0.2s ease, color 0.2s ease; +} +.bru-step:hover .bru-step-n { -webkit-text-stroke-color: var(--red); } +.bru-step-t { + font-family: var(--sans); + font-weight: 800; + font-variation-settings: 'wght' 760, 'wdth' 110; + text-transform: uppercase; + font-size: clamp(1.2rem, 2.2vw, 1.8rem); + line-height: 1.02; +} +.bru-step-enforce { + font-family: var(--mono); + font-size: 10px; + letter-spacing: 0.1em; + text-transform: uppercase; + color: #fff; + background: var(--red); + padding: 4px 9px; + white-space: nowrap; + align-self: center; +} + +/* ---------- Security grid ---------- */ +.bru-sec { + display: grid; + grid-template-columns: repeat(3, 1fr); + border-top: 1px solid var(--line-2); + border-left: 1px solid var(--line-2); +} +.bru-sec-card { + border-right: 1px solid var(--line); + border-bottom: 1px solid var(--line); + padding: clamp(1.5rem, 2.2vw, 2rem); +} +.bru-sec-h { + font-family: var(--sans); + font-weight: 800; + font-variation-settings: 'wght' 760, 'wdth' 110; + text-transform: uppercase; + font-size: 1.05rem; + margin-bottom: 18px; +} +.bru-sec-row { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + padding: 9px 0; + border-top: 1px solid var(--line); + font-size: 13px; +} +.bru-sec-k { color: var(--ink-dim); } +.bru-sec-v { font-family: var(--mono); font-size: 11.5px; color: var(--ink); text-align: right; } +@media (max-width: 900px) { .bru-sec { grid-template-columns: 1fr; } } + +.bru-capture { + border: 1px solid var(--line-2); + margin-top: 1px; +} +.bru-capture-h { + font-family: var(--mono); + font-size: 10.5px; + letter-spacing: 0.08em; + text-transform: uppercase; + color: var(--ink-faint); + padding: 12px 16px; + border-bottom: 1px solid var(--line); +} +.bru-capture-row { + display: grid; + grid-template-columns: 1.4fr 1.4fr 1fr auto; + gap: 14px; + padding: 12px 16px; + border-bottom: 1px solid var(--line); + font-family: var(--mono); + font-size: 11.5px; + align-items: center; +} +.bru-capture-row:last-child { border-bottom: 0; } +@media (max-width: 760px) { + .bru-capture-row { grid-template-columns: 1fr auto; } + .bru-capture-row .bru-cap-hide { display: none; } +} + +/* ---------- Use cases (interactive) ---------- */ +.bru-uc { display: grid; grid-template-columns: 320px 1fr; border: 1px solid var(--line-2); } +.bru-uc-tabs { border-right: 1px solid var(--line); } +.bru-uc-tab { + display: block; + width: 100%; + text-align: left; + padding: clamp(1.1rem, 2vw, 1.6rem) clamp(1.1rem, 2vw, 1.5rem); + border-bottom: 1px solid var(--line); + background: transparent; + cursor: pointer; + border-left: 2px solid transparent; + transition: background 0.18s ease, border-color 0.18s ease; +} +.bru-uc-tab:last-child { border-bottom: 0; } +.bru-uc-tab.is-active { background: var(--bg-raise); border-left-color: var(--red); } +.bru-uc-tab-t { + font-family: var(--sans); + font-weight: 800; + font-variation-settings: 'wght' 740, 'wdth' 108; + text-transform: uppercase; + font-size: 1.05rem; + color: var(--ink); +} +.bru-uc-tab-s { font-family: var(--mono); font-size: 11px; color: var(--ink-dim); margin-top: 7px; } +.bru-uc-detail { padding: clamp(1.75rem, 3vw, 2.75rem); } +.bru-uc-outcome { + display: flex; + gap: 14px; + padding: 14px 0; + border-top: 1px solid var(--line); + font-size: 0.95rem; + line-height: 1.5; + color: var(--ink); +} +.bru-uc-outcome::before { + content: ''; + flex-shrink: 0; + width: 8px; + height: 8px; + margin-top: 7px; + background: var(--red); +} +@media (max-width: 820px) { .bru-uc { grid-template-columns: 1fr; } .bru-uc-tabs { border-right: 0; border-bottom: 1px solid var(--line); display: grid; grid-template-columns: 1fr 1fr; } .bru-uc-tab { border-right: 1px solid var(--line); } } + +/* ---------- Objection handling ---------- */ +.bru-obj { display: grid; grid-template-columns: repeat(2, 1fr); border-top: 1px solid var(--line-2); border-left: 1px solid var(--line-2); } +.bru-obj-card { border-right: 1px solid var(--line); border-bottom: 1px solid var(--line); padding: clamp(1.5rem, 2.4vw, 2.25rem); } +.bru-obj-q { + font-family: var(--sans); + font-weight: 700; + font-variation-settings: 'wght' 680, 'wdth' 106; + font-size: clamp(1.05rem, 1.7vw, 1.35rem); + line-height: 1.15; + color: var(--ink); +} +.bru-obj-q::before { content: '“'; color: var(--red); } +.bru-obj-q::after { content: '”'; color: var(--red); } +.bru-obj-proof { font-family: var(--mono); font-size: 11px; letter-spacing: 0.05em; text-transform: uppercase; color: var(--red); margin-top: 18px; } +@media (max-width: 760px) { .bru-obj { grid-template-columns: 1fr; } } + +.bru-artifacts { display: flex; flex-wrap: wrap; gap: 10px; margin-top: clamp(2rem, 3vw, 2.5rem); } +.bru-artifact { + font-family: var(--mono); + font-size: 11.5px; + letter-spacing: 0.03em; + text-transform: uppercase; + color: var(--ink-dim); + border: 1px solid var(--line-2); + padding: 8px 13px; + transition: border-color 0.18s ease, color 0.18s ease; +} +.bru-artifact:hover { border-color: color-mix(in srgb, var(--red) 45%, transparent); color: var(--ink); } + +/* ---------- FAQ ---------- */ +.bru-faq { border-top: 1px solid var(--line-2); } +.bru-faq details { border-bottom: 1px solid var(--line); } +.bru-faq summary { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + padding: clamp(1.25rem, 2.4vw, 1.85rem) 4px; + cursor: pointer; + list-style: none; + font-family: var(--sans); + font-weight: 700; + font-variation-settings: 'wght' 660, 'wdth' 106; + font-size: clamp(1.05rem, 1.8vw, 1.4rem); + color: var(--ink); + transition: padding-left 0.2s ease; +} +.bru-faq summary:hover { padding-left: 12px; } +.bru-faq summary::-webkit-details-marker { display: none; } +.bru-faq summary .bru-faq-mk { font-family: var(--mono); color: var(--red); font-size: 1.4rem; line-height: 1; transition: transform 0.2s ease; } +.bru-faq details[open] summary .bru-faq-mk { transform: rotate(45deg); } +.bru-faq-a { padding: 0 4px clamp(1.5rem, 2.4vw, 2rem); max-width: 70ch; color: var(--ink-dim); line-height: 1.6; font-size: 1rem; } + +/* ---------- Full footer ---------- */ +.bru-footer { border-top: 1.5px solid var(--line-2); } +.bru-footer-top { + display: grid; + grid-template-columns: 1.4fr repeat(4, 1fr); + gap: clamp(1.5rem, 3vw, 3rem); + padding-block: clamp(3rem, 5vw, 4.5rem); +} +.bru-footer-col h4 { + font-family: var(--mono); + font-size: 11px; + letter-spacing: 0.1em; + text-transform: uppercase; + color: var(--ink-faint); + margin-bottom: 16px; +} +.bru-footer-col a { + display: block; + font-size: 13.5px; + color: var(--ink-dim); + text-decoration: none; + padding: 6px 0; + transition: color 0.18s ease; +} +.bru-footer-col a:hover { color: var(--red); } +.bru-footer-brand .bru-wordmark { font-size: 22px; } +.bru-footer-bottom { + display: flex; + align-items: center; + justify-content: space-between; + flex-wrap: wrap; + gap: 12px; + padding-block: 22px; + border-top: 1px solid var(--line); +} +@media (max-width: 900px) { + .bru-footer-top { grid-template-columns: 1fr 1fr; } + .bru-footer-brand { grid-column: 1 / -1; } +} + +/* shared section divider eyebrow line */ +.bru-eyebrow-line { + display: inline-flex; + align-items: center; + gap: 12px; +} +.bru-eyebrow-line::before { + content: ''; + width: 28px; + height: 2px; + background: var(--red); +} + +/* ============================================================ + INTERACTION + TEXTURE LAYER — make it feel alive, not just informative + ============================================================ */ + +/* ---------- Filmic grain + scanline depth (fixed, subtle) ---------- */ +.bru-grain { + position: fixed; + inset: 0; + z-index: 80; + pointer-events: none; + opacity: 0.05; + mix-blend-mode: overlay; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E"); + animation: bru-grain-shift 0.6s steps(2) infinite; +} +@keyframes bru-grain-shift { + 0% { transform: translate(0, 0); } + 50% { transform: translate(-3%, 2%); } + 100% { transform: translate(2%, -2%); } +} +.bru-scan { + position: fixed; + inset: 0; + z-index: 79; + pointer-events: none; + opacity: 0.4; + background: repeating-linear-gradient( + to bottom, + transparent 0, + transparent 2px, + rgba(0, 0, 0, 0.18) 3px, + transparent 4px + ); +} + +/* ---------- Scroll progress ---------- */ +.bru-progress { + position: fixed; + top: 0; + left: 0; + height: 2px; + background: var(--red); + z-index: 100; + transform-origin: left; + will-change: transform; +} + +/* ---------- Hero pointer HUD (crosshair + coordinates) ---------- */ +.bru-hud { + position: absolute; + inset: 0; + pointer-events: none; + z-index: 2; + opacity: 0; + transition: opacity 0.3s ease; +} +.bru-hud.is-on { opacity: 1; } +.bru-hud-vline, +.bru-hud-hline { + position: absolute; + background: color-mix(in srgb, var(--red) 55%, transparent); +} +.bru-hud-vline { top: 0; bottom: 0; width: 1px; } +.bru-hud-hline { left: 0; right: 0; height: 1px; } +.bru-hud-read { + position: absolute; + font-family: var(--mono); + font-size: 10px; + letter-spacing: 0.05em; + color: var(--red); + transform: translate(12px, 12px); + white-space: nowrap; +} + +/* ---------- Posture simulator (the interactive toy) ---------- */ +.sim { + display: grid; + grid-template-columns: 1fr 0.85fr; + border: 1px solid var(--line-2); +} +.sim-controls { border-right: 1px solid var(--line); padding: clamp(1.25rem, 2.4vw, 2rem); } +.sim-grouphead { + font-family: var(--mono); + font-size: 10px; + letter-spacing: 0.1em; + text-transform: uppercase; + color: var(--ink-faint); + margin: 18px 0 10px; +} +.sim-grouphead:first-child { margin-top: 0; } +.sim-chips { display: flex; flex-wrap: wrap; gap: 8px; } +.sim-chip { + font-family: var(--mono); + font-size: 12px; + letter-spacing: 0.02em; + padding: 8px 12px; + border: 1px solid var(--line-2); + background: transparent; + color: var(--ink-dim); + cursor: pointer; + display: inline-flex; + align-items: center; + gap: 8px; + transition: background 0.16s ease, color 0.16s ease, border-color 0.16s ease, transform 0.08s ease; + user-select: none; +} +.sim-chip:active { transform: scale(0.96); } +.sim-chip .sim-box { + width: 9px; height: 9px; border: 1px solid var(--line-2); flex-shrink: 0; + transition: background 0.16s ease, border-color 0.16s ease; +} +.sim-chip.is-on { color: var(--ink); border-color: color-mix(in srgb, var(--ink) 45%, transparent); background: var(--bg-raise); } +.sim-chip.is-on .sim-box { background: var(--ok); border-color: var(--ok); } +.sim-chip.is-off .sim-box { border-color: color-mix(in srgb, var(--red) 55%, transparent); } +.sim-actions { display: flex; gap: 10px; margin-top: 22px; } +.sim-act { + font-family: var(--mono); font-size: 11px; letter-spacing: 0.06em; text-transform: uppercase; + padding: 8px 12px; border: 1px solid var(--line-2); background: transparent; color: var(--ink-dim); cursor: pointer; + transition: color 0.16s ease, border-color 0.16s ease; +} +.sim-act:hover { color: var(--ink); border-color: var(--ink-dim); } + +.sim-readout { padding: clamp(1.25rem, 2.4vw, 2rem); display: flex; flex-direction: column; } +.sim-gaugewrap { display: flex; align-items: center; gap: 18px; } +.sim-score { + font-family: var(--sans); font-weight: 900; font-variation-settings: 'wght' 900, 'wdth' 108; + font-size: clamp(2.6rem, 5vw, 4rem); line-height: 0.85; letter-spacing: -0.02em; +} +.sim-statline { display: flex; gap: 10px; margin-top: 20px; } +.sim-stat { flex: 1; border: 1px solid var(--line); padding: 12px; } +.sim-stat-v { font-family: var(--sans); font-weight: 800; font-size: 22px; } +.sim-stat-l { font-family: var(--mono); font-size: 9.5px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-faint); margin-top: 6px; } +.sim-fw { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 9px 0; border-top: 1px solid var(--line); font-size: 12.5px; } +.sim-fwbar { width: 80px; height: 5px; background: var(--line); flex-shrink: 0; } +.sim-fwbar span { display: block; height: 100%; background: var(--ok); transition: width 0.5s cubic-bezier(0.22,1,0.36,1); } +.sim-hint { font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.05em; color: var(--ink-faint); margin-top: auto; padding-top: 18px; } +@media (max-width: 820px) { + .sim { grid-template-columns: 1fr; } + .sim-controls { border-right: 0; border-bottom: 1px solid var(--line); } +}