Skip to content
31 changes: 31 additions & 0 deletions app/home-poc/_components/CountUp.tsx
Original file line number Diff line number Diff line change
@@ -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<HTMLSpanElement>(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 <span ref={ref}>{v}</span>;
}
213 changes: 213 additions & 0 deletions app/home-poc/_components/DashboardPreview.tsx
Original file line number Diff line number Diff line change
@@ -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<HTMLDivElement>(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 (
<div className="dash-win" ref={ref}>
{/* browser chrome */}
<div className="dash-chrome">
<span className="dash-tl"><i /><i /><i /></span>
<span className="dash-url">app.formaos.com.au/app/compliance/health</span>
<span className="dash-ava" aria-hidden>NM</span>
</div>

<div className="dash-body">
{/* sidebar */}
<aside className="dash-side">
<div className="dash-brand">
<span className="dash-mark">FO</span>
<span style={{ fontFamily: 'var(--sans)', fontWeight: 800, fontSize: 13, letterSpacing: '0.02em' }}>FORMAOS</span>
</div>
<div className="dash-ctx">Compliance</div>
{NAV.map(([label, rag, active]) => (
<div key={label} className={`dash-nav-item ${active ? 'is-active' : ''}`}>
<span>{label}</span>
{rag && <span className="dash-rag" style={{ background: ragColor(rag) }} />}
</div>
))}
<div className="dash-side-foot">
<span className="dash-ava">NM</span>
<div>
<div style={{ fontSize: 11, color: 'var(--ink)' }}>Nancy M.</div>
<div className="bru-mono" style={{ fontSize: 9, color: 'var(--ink-faint)', letterSpacing: '0.08em' }}>ADMIN</div>
</div>
</div>
</aside>

{/* main */}
<div className="dash-main">
<div className="dash-topbar">
<span style={{ color: 'var(--ink)' }}>ORGANIZATION · Brightside Care</span>
<span style={{ marginLeft: 'auto' }} className="hidden sm:inline">NDIS PROVIDER</span>
</div>
<div className="dash-strip">
<span className="dash-sd"><i style={{ background: 'var(--red)' }} />0 OVERDUE</span>
<span className="dash-sd"><i style={{ background: 'var(--ink-faint)' }} />5 DUE SOON</span>
<span className="dash-sd"><i style={{ background: 'var(--ok)' }} />38 COMPLETED</span>
<span className="dash-sd" style={{ marginLeft: 'auto', color: 'var(--red)' }}>
<span className="bru-live-dot" />LIVE
</span>
</div>

<div className="dash-pad">
{/* KPI tiles */}
<div className="dash-kpis">
{KPIS.map(([l, v, c]) => (
<div className="dash-kpi" key={l}>
<div className="dash-kpi-l">{l}</div>
<div className="dash-kpi-v" style={l === 'Overdue' ? undefined : undefined}>
<CountUp value={v as number} />{l === 'Readiness' ? '%' : ''}
</div>
<div className="dash-kpi-c">{l === 'Readiness' ? 'buyer-ready' : c}</div>
</div>
))}
</div>

<div className="dash-grid2">
{/* overall health */}
<div className="dash-card">
<div className="dash-card-h">
<span>Overall compliance health</span>
<span className="dash-pill" style={{ color: 'var(--ink)' }}>WATCH</span>
</div>
<div style={{ display: 'flex', alignItems: 'center', gap: 18 }}>
<svg width="118" height="118" viewBox="0 0 118 118" aria-hidden style={{ flexShrink: 0 }}>
<circle cx="59" cy="59" r={r} fill="none" stroke="var(--line)" strokeWidth="7" />
<motion.circle
cx="59" cy="59" r={r} fill="none" stroke="var(--ok)" strokeWidth="7" strokeLinecap="round"
transform="rotate(-90 59 59)"
initial={reduce ? false : { pathLength: 0 }}
animate={inView || reduce ? { pathLength: target } : { pathLength: 0 }}
transition={{ duration: 1.3, ease: [0.22, 1, 0.36, 1] }}
/>
</svg>
<div>
<div className="dash-kpi-v" style={{ fontSize: 38 }}>
<CountUp value={82} /><span style={{ color: 'var(--red)' }}>%</span>
</div>
<div className="bru-mono" style={{ fontSize: 10.5, color: 'var(--ink-dim)', marginTop: 6 }}>
5 FRAMEWORKS · 252 CONTROLS
</div>
</div>
</div>
<div className="dash-tiles">
{[['47', 'Pass'], ['8', 'Partial'], ['3', 'Fail'], ['12', 'Manual']].map(([v, l]) => (
<div className="dash-tile" key={l}>
<div className="dash-tile-v" style={{ color: l === 'Fail' ? 'var(--red)' : 'var(--ink)' }}>{v}</div>
<div className="dash-tile-l">{l}</div>
</div>
))}
</div>
{/* sparkline */}
<div style={{ marginTop: 16 }}>
<div className="bru-mono" style={{ fontSize: 9.5, color: 'var(--ink-faint)', letterSpacing: '0.06em', marginBottom: 8 }}>
12-WEEK TREND · +6%
</div>
<svg width="100%" height="44" viewBox="0 0 460 44" preserveAspectRatio="none" aria-hidden>
<motion.polyline
fill="none" stroke="var(--ok)" strokeWidth="1.5"
points="0,34 42,32 84,33 126,28 168,29 210,24 252,25 294,18 336,20 378,13 420,12 460,8"
initial={reduce ? false : { pathLength: 0 }}
animate={inView || reduce ? { pathLength: 1 } : { pathLength: 0 }}
transition={{ duration: 1.4, ease: 'easeInOut', delay: 0.2 }}
/>
</svg>
</div>
</div>

{/* per-framework + outstanding */}
<div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
<div className="dash-card">
<div className="dash-card-h"><span>Per-framework</span><span>SCORE</span></div>
{FRAMEWORKS.map(([name, score], i) => (
<div className="dash-fw" key={name}>
<span style={{ color: 'var(--ink)' }}>{name}</span>
<span style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
<span className="dash-bar2">
<motion.span
initial={reduce ? false : { width: 0 }}
animate={inView || reduce ? { width: `${score}%` } : { width: 0 }}
transition={{ duration: 1, ease: [0.22, 1, 0.36, 1], delay: 0.15 + i * 0.1 }}
/>
</span>
<span className="bru-mono" style={{ fontSize: 12, width: 32, textAlign: 'right' }}>{score}%</span>
</span>
</div>
))}
</div>
<div className="dash-card">
<div className="dash-card-h"><span>Top outstanding controls</span></div>
{OUTSTANDING.map(([k, t, fw, st]) => (
<div className="dash-oc" key={k}>
<span style={{ color: 'var(--ink)', width: 48 }}>{k}</span>
<span style={{ color: 'var(--ink-dim)', flex: 1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{t}</span>
<span style={{ color: 'var(--ink-faint)' }} className="hidden sm:inline">{fw}</span>
<span className={`dash-pill ${st === 'FAIL' ? 'dash-pill-fail' : ''}`}>{st}</span>
</div>
))}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
);
}
75 changes: 75 additions & 0 deletions app/home-poc/_components/HeroIntro.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
'use client';

import { motion, useReducedMotion, type Variants } from 'framer-motion';

/**
* Hero with a mechanical, staggered entrance that plays ON MOUNT (not on
* scroll) so the LCP headline always resolves visible. Each line rises and
* un-blurs in sequence; the red period punches in last.
*/

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] } },
};

export function HeroIntro() {
const reduce = useReducedMotion();
const mv = reduce ? undefined : item;

return (
<section style={{ position: 'relative', overflow: 'hidden', borderBottom: '1.5px solid var(--line-2)' }}>
<div className="bru-cols" aria-hidden>
{Array.from({ length: 12 }).map((_, i) => <span key={i} />)}
</div>
<motion.div
className="bru-frame"
style={{ position: 'relative', paddingTop: 'clamp(3rem, 6vw, 5rem)', paddingBottom: 'clamp(2.5rem, 5vw, 4rem)' }}
variants={reduce ? undefined : container}
initial={reduce ? undefined : 'hidden'}
animate={reduce ? undefined : 'show'}
>
<motion.span variants={mv} className="bru-chip" style={{ display: 'inline-block', marginBottom: 'clamp(2rem, 5vw, 3.5rem)' }}>
COMPLIANCE OS FOR NDIS · AGED CARE · HEALTHCARE
</motion.span>

<motion.h1 variants={mv} className="bru-display" style={{ fontSize: 'clamp(2.6rem, 11vw, 9rem)' }}>
Audit-ready
</motion.h1>
<motion.h1 variants={mv} className="bru-display" style={{ fontSize: 'clamp(2.6rem, 11vw, 9rem)' }}>
every day<span style={{ color: 'var(--red)' }}>.</span>
</motion.h1>
<motion.p
variants={mv}
className="bru-h2"
style={{ fontSize: 'clamp(1.25rem, 2.6vw, 2.1rem)', marginTop: 'clamp(1.25rem, 2vw, 1.75rem)', fontVariationSettings: "'wght' 600, 'wdth' 105", color: 'var(--ink-dim)', textTransform: 'none', lineHeight: 1.1 }}
>
Not the week before the <span style={{ color: 'var(--red)' }}>Commission</span> visits.
</motion.p>

<div className="grid gap-x-8 gap-y-8 lg:grid-cols-12" style={{ marginTop: 'clamp(2.5rem, 4vw, 3.5rem)', alignItems: 'end' }}>
<motion.div variants={mv} className="lg:col-span-6">
<div style={{ display: 'flex', gap: 14, flexWrap: 'wrap' }}>
<a href="/contact?type=compliance-plan" className="bru-btn bru-btn-red">Get Compliance Plan <span className="bru-arrow">→</span></a>
<a href="/contact?type=demo" className="bru-btn">Book Demo</a>
</div>
<p className="bru-mono" style={{ fontSize: 11.5, color: 'var(--ink-faint)', marginTop: 18, letterSpacing: '0.04em' }}>
GUIDED ASSESSMENT · AU-HOSTED BY DEFAULT · EVIDENCE-BACKED WORKFLOWS
</p>
</motion.div>
<motion.div variants={mv} className="lg:col-span-5 lg:col-start-8">
<p className="bru-deck">
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.
</p>
</motion.div>
</div>
</motion.div>
</section>
);
}
Loading
Loading