/* app-sections-top.jsx — Nav, faux Jira product window, Hero, Trust, Pillars */
const { useState, useEffect, useRef } = React;
const TINT = {
P: ['var(--P-surface)', 'var(--P-text)'],
B: ['var(--B50)', '#0747A6'],
R: ['var(--R-surface)', 'var(--R-text)'],
T: ['var(--T-surface)', 'var(--T-text)'],
G: ['var(--G-surface)', 'var(--G-text)'],
Y: ['var(--Y-surface)', '#8a6d00']
};
function Ico({ name, ...rest }) {
const C = window.Icons[name];
return C ? : null;
}
/* ---------------- NAV ---------------- */
function Nav({ darkHero, onDemo }) {
const [scrolled, setScrolled] = useState(false);
useEffect(() => {
const onScroll = () => setScrolled(window.scrollY > 24);
onScroll();
window.addEventListener('scroll', onScroll, { passive: true });
return () => window.removeEventListener('scroll', onScroll);
}, []);
const onDark = darkHero && !scrolled;
const logo = asset(onDark ? 'assets/qity-logo-dark.png' : 'assets/qity-logo-light.png');
return (
{window.NAV_LINKS.map((l) =>
{l.label}
)}
Contact us
);
}
/* ---------------- FAUX JIRA WINDOW ---------------- */
function Donut({ pct, color }) {
return (
{pct}%
);
}
function JiraWindow() {
const nav = [
{ icon: 'FileCheck', label: 'Document Control', active: true },
{ icon: 'Ruler', label: 'Design Control' },
{ icon: 'AlertTriangle', label: 'Risk Management' },
{ icon: 'ClipboardCheck', label: 'Audit Management' },
{ icon: 'Cap', label: 'Training' }];
return (
{Array.from({ length: 9 }).map((_, i) => )}
Qity
Search quality records
iQMS
{nav.map((n) =>
{n.label}
)}
My quality dashboard
Wednesday, 19 Jun 2025 · Evidence is up to date
DC-123, DC-125 and 2 others are waiting for your review.
Review now
Document Control
8 documents pending your approval 55 in a long review cycle
Use-related risks
3 risks need mitigation 24 controlled this quarter
Design Control — traceability
WI-231 Firmware OTA update requirement Released
DOC-55 Risk control verification report QA approval
CC-15 Change request — sensor calibration Doc approval
);
}
/* ---------------- PRODUCT SCREENSHOT (real app) ---------------- */
function ProductShot({ src, alt }) {
return (
);
}
/* ---------------- HERO ---------------- */
function Hero({ tweaks, onDemo }) {
const layout = tweaks.heroLayout; // 'right' | 'below'
const motion = 'tilt'; // settled hero scroll motion
const visualRef = useRef(null);
useEffect(() => {
const wrap = visualRef.current;if (!wrap) return;
const win = wrap.querySelector('.window');if (!win) return;
const cards = [
{ el: wrap.querySelector('.float-bl'), at: 0.07 },
{ el: wrap.querySelector('.float-tr'), at: 0.18 }];
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
cards.forEach((c) => c.el && c.el.classList.add('popped'));
return;
}
let raf = 0;
let baseTop = null;
const upd = () => {
raf = 0;
const r = wrap.getBoundingClientRect();
const vh = window.innerHeight || 800;
const prog = Math.max(0, Math.min(1.2, (vh - r.top) / (vh * 0.85)));
const p1 = Math.min(prog, 1);
if (motion === 'tilt') {
// leaning forward at the top, standing flat as you scroll down
win.style.setProperty('--win-origin', 'bottom center');
win.style.setProperty('--win-tiltX', (Math.max(0, 1 - prog) * 26).toFixed(2) + 'deg');
win.style.setProperty('--win-lift', (-((prog - 0.5) * 14)).toFixed(1) + 'px');
win.style.setProperty('--win-scale', (0.95 + p1 * 0.05).toFixed(3));
} else if (motion === 'rise') {
// rises up from below and scales into place
win.style.setProperty('--win-origin', 'center center');
win.style.setProperty('--win-tiltX', '0deg');
win.style.setProperty('--win-lift', ((1 - p1) * 70 - 10).toFixed(1) + 'px');
win.style.setProperty('--win-scale', (0.9 + p1 * 0.1).toFixed(3));
} else {
// lift (default): subtle settle
win.style.setProperty('--win-origin', 'center center');
win.style.setProperty('--win-tiltX', (Math.max(0, 1 - prog) * 5).toFixed(2) + 'deg');
win.style.setProperty('--win-lift', (-((prog - 0.4) * 34)).toFixed(1) + 'px');
win.style.setProperty('--win-scale', (0.965 + p1 * 0.035).toFixed(3));
}
if (baseTop === null) baseTop = r.top;
const scrolled = baseTop - r.top; // how far the hero has scrolled up from its initial spot
cards.forEach((c) => c.el && c.el.classList.toggle('popped', scrolled > vh * c.at));
};
const onScroll = () => {if (!raf) raf = requestAnimationFrame(upd);};
upd();
window.addEventListener('scroll', onScroll, { passive: true });
window.addEventListener('resize', onScroll);
return () => {window.removeEventListener('scroll', onScroll);window.removeEventListener('resize', onScroll);cancelAnimationFrame(raf);};
}, [layout, tweaks.heroSpot, motion]);
return (
Quality built-in , not bolted on.
Qity's apps for Jira & Confluence turn your team’s everyday work into compliance evidence and documentation. Integrated with the tools you already use, built for regulated industries.
Coming soon to
Document Review Agent
DC-332 Design & Development SOP reviewed against ISO 13485 clauses
CI/CD pipeline
Release build #1284 passed — detailed design and test descriptions updated in Jira
);
}
/* ---------------- TRUST ---------------- */
function Trust() {
const logos = [
{ name: 'Wyss Zurich', file: 'wyss', h: 26 },
{ name: 'FBGS', file: 'fbgs-mark', h: 28 },
{ name: 'Trince', file: 'trince', h: 26 },
{ name: 'Agricells', file: 'agricells', h: 34 },
{ name: 'Mantyx', file: 'mantyx', h: 32 },
{ name: 'Samplid', file: 'samplid', h: 28 },
{ name: 'Robovision', file: 'robovision', h: 40 },
{ name: 'NHa Normandy Hadrontherapy', file: 'nha', h: 32 }];
const trackRef = useRef(null);
const [copies, setCopies] = useState(2);
const [ready, setReady] = useState(false);
useEffect(() => {
const track = trackRef.current;if (!track) return;
const marquee = track.parentElement;if (!marquee) return;
let cancelled = false;
const measure = () => {
if (cancelled) return;
const kids = [...track.children].slice(0, logos.length);
if (kids.length < logos.length) return;
let setW = 0;
kids.forEach((k) => {setW += k.getBoundingClientRect().width + (parseFloat(getComputedStyle(k).marginRight) || 0);});
if (!setW) return;
// enough copies that (copies-1) sets always exceed the viewport → never a gap
const need = Math.ceil((marquee.clientWidth + setW) / setW) + 1;
setCopies((c) => Math.max(2, need) === c ? c : Math.max(2, need));
track.style.setProperty('--set-w', setW + 'px');
track.style.setProperty('--marq-dur', (setW / 40).toFixed(1) + 's'); // ~40px/s
setReady(true);
};
const imgs = [...track.querySelectorAll('img')];
let pending = imgs.filter((im) => !im.complete).length;
const onOne = () => {if (--pending <= 0) measure();};
if (pending === 0) measure();else
imgs.forEach((im) => {if (!im.complete) {im.addEventListener('load', onOne);im.addEventListener('error', onOne);}});
const ro = new ResizeObserver(() => measure());
ro.observe(marquee);
return () => {cancelled = true;ro.disconnect();};
}, []);
const sets = Array.from({ length: copies }, () => logos).flat();
return (
TRUSTED BY
{sets.map((l, i) =>
= logos.length} style={{ height: l.h }} />
)}
);
}
function ComplianceStrip() {
return (
Built for regulated industries
{window.STANDARDS.map((s) =>
{s.img ? : }
{s.label}
)}
);
}
/* ---------------- PILLARS ---------------- */
function PillarIllo({ i }) {
const illos = [
// Jira items flowing into a document
,
// Pipeline of dots + robot producing documentation
,
// AI icon — sparkle
];
return illos[i % illos.length];
}
function Pillars({ tweaks }) {
const variant = 'tiles'; // settled on icon tiles
const illo = true; // settled on flat illustrations
return (
Quality evidence that builds itself
Stop managing your quality in disconnected Word and Excel files.
Your documentation gets generated in the system where the work happens, with the help of automation and agentic AI.
{window.PILLARS.map((p, i) =>
{variant === 'rows' && {p.n} }
{illo ?
:
}
{variant === 'rows' ?
<>
{p.title}
{p.body}
> :
<>
{p.title}
{p.body}
>
}
)}
);
}
Object.assign(window, { Nav, Hero, JiraWindow, ProductShot, Trust, ComplianceStrip, Pillars, Ico, TINT });