/* visdi-shared.jsx - VISDI by Minerva - Shared UI components
Exports: WheelMark · NavBar · Btn · Eyebrow · Arrow · CircuitSVG · PageHero · SiteFooter · STAGE */
const WORDMARK = "assets/logo-minerva-km-dark.svg";
const KM_HORIZONTAL = "assets/logo-minerva-km-horizontal.svg"; /* color-on-white — master bar */
const KM_WHITECLEAR = "assets/logo-minerva-km-white-clear.svg"; /* white, transparent — pinned sub-bar */
const MARK_SRC = "assets/logo-minerva-km.svg";
const VISDI_ICON = "assets/logo-visdi-wheels-white.svg";
const NAV_SPECTRUM = "linear-gradient(90deg,#D51C29,#E0542A,#E0A230,#F2D12E,#8FBF3F,#069848,#1AA89A,#3FB1E5,#1C4DA1,#4A3BA8,#8B2CA8,#D20F8C)";
/* Standalone-bundle resolver: maps an asset path to its inlined blob URL when
the page has been bundled (window.__resources present), else returns the path
unchanged. No-op on the normal multi-file site. */
if (!window.__asset) window.__asset = function (p) { return (window.__resources && window.__resources[p]) || p; };
/* Stage color system - Brief §2.1: Diagnose=gold · Connect=blue · Operate=green */
const STAGE = {
diagnose: { color:"#E0A230", bg:"rgba(224,162,48,0.08)", border:"rgba(224,162,48,0.22)", label:"Diagnose" },
connect: { color:"#3FB1E5", bg:"rgba(63,177,229,0.08)", border:"rgba(63,177,229,0.22)", label:"Connect" },
operate: { color:"#04975e", bg:"rgba(4,151,94,0.08)", border:"rgba(4,151,94,0.22)", label:"Operate" },
};
/* 2026 spectrum wheel - CSS-crop of logo-minerva-km.svg (61% height = wheel only) */
function WheelMark({ width = 360, style = {} }) {
const aspect = 180.5 / 160.9;
const fullH = Math.round(width / aspect);
const cropH = Math.round(fullH * 0.61);
return (
);
}
/* Global navigation */
const NAV_LINKS = [
{ id:"method", label:"The Method", href:"visdi-method.html" },
{ id:"products", label:"Products", href:"visdi-products.html"},
{ id:"mingi", label:"Mingi", href:"visdi-mingi.html" },
];
/* Persistent two-tier brand bar — Tier 1 master (firm, scrolls away) +
Tier 2 General Intelligence sub-bar (sticky, pins). See the handoff spec. */
function NavBar({ page = "home" }) {
const [pinned, setPinned] = React.useState(false);
const [menuOpen, setMenuOpen] = React.useState(false);
React.useEffect(() => {
const onScroll = () => setPinned(window.scrollY > 60);
onScroll();
window.addEventListener("scroll", onScroll, { passive: true });
return () => window.removeEventListener("scroll", onScroll);
}, []);
/* close the mobile menu if the viewport grows back to desktop */
React.useEffect(() => {
const onResize = () => { if (window.innerWidth > 1024) setMenuOpen(false); };
window.addEventListener("resize", onResize);
return () => window.removeEventListener("resize", onResize);
}, []);
/* lock body scroll while the overlay is open */
React.useEffect(() => {
document.body.style.overflow = menuOpen ? "hidden" : "";
return () => { document.body.style.overflow = ""; };
}, [menuOpen]);
const dim = "rgba(231,239,250,0.62)";
const SKY = "#3FB1E5";
/* master-bar division box — previews the destination site's theme.
block=true → full-width stacked variant for the mobile menu (equal lengths). */
const divBox = ({ href, label, kind, active, block = false }) => {
const box = kind === "gi"
? { background: active ? "#0A1426" : "#0E1A30", color: "#FFFFFF",
border: `1px solid ${active ? SKY : "rgba(63,177,229,0.45)"}`,
boxShadow: active ? "0 2px 14px rgba(63,177,229,0.30)" : "none", dot: SKY }
: { background: "#FFFFFF", color: active ? "#067A3E" : "var(--ink-800)",
border: `1px solid ${active ? "#069348" : "var(--ink-300)"}`,
boxShadow: active ? "0 2px 14px rgba(6,147,72,0.20)" : "0 1px 2px rgba(15,26,46,0.06)", dot: "#069348" };
return (
setMenuOpen(false) : undefined}
style={{ display: block ? "flex" : "inline-flex", alignItems:"center",
justifyContent: block ? "center" : "flex-start", gap:9, cursor:"pointer",
fontFamily:"var(--wix-text)", fontSize:15, fontWeight: active ? 700 : 600, letterSpacing:"-0.01em",
whiteSpace:"nowrap", padding: block ? "14px 18px" : "10px 18px", borderRadius:"var(--radius-md)",
width: block ? "100%" : "auto", boxSizing:"border-box",
background:box.background, color:box.color, border:box.border, boxShadow:box.boxShadow }}>
{active && }
{label}
);
};
/* hamburger / close toggle — hidden on desktop via .nav-burger CSS */
const burger = (color) => (
);
return (
{/* ── Tier 1 — Minerva master brand bar (white, frosted; scrolls away) ── */}
{/* ── Tier 2 — General Intelligence sub-bar (dark, sticky; pins) ── */}
{/* ── Mobile menu overlay (hamburger target; equal-width stacked actions) ── */}
Divisions
{divBox({ href:"/knowledge-governance/", label:"Knowledge Governance", kind:"kg", active:false, block:true })}
{divBox({ href:"/general-intelligence/", label:"General Intelligence", kind:"gi", active:true, block:true })}
setMenuOpen(false)}
style={{ display:"flex", justifyContent:"center", width:"100%", boxSizing:"border-box",
fontSize:16, fontWeight:700, color:"#0A1426", background:"var(--minerva-gold)",
fontFamily:"var(--wix-text)", padding:"14px 18px", borderRadius:"var(--radius-md)",
marginTop:4, boxShadow:"0 2px 10px rgba(224,162,48,0.30)" }}>Start a Diagnostic
General Intelligence
{NAV_LINKS.map(({ id, label, href }) => (
setMenuOpen(false)}
style={{ fontFamily:"var(--wix-text)", fontSize:17, fontWeight: page===id ? 700 : 500,
color: page===id ? "#fff" : "rgba(231,239,250,0.72)", padding:"12px 0",
borderBottom:"1px solid rgba(255,255,255,0.06)" }}>{label}
))}
);
}
/* Button */
function Btn({ children, href="#", variant="primary", accent, textColor, shadow, xstyle={} }) {
const base = { display:"inline-flex", alignItems:"center", gap:8, fontSize:17,
fontFamily:"var(--wix-text)", fontWeight:700, padding:"12px 22px",
borderRadius:"var(--radius-md)", letterSpacing:"-0.01em", cursor:"pointer", ...xstyle };
return variant==="primary"
? {children}
: {children};
}
/* Baroque ring-dot ornament - echoes Mingi's ring motifs (Arturo's approved A/B/C type system) */
function EyebrowMark({ color, flip=false }) {
return (
);
}
/* Art-nouveau wavy vine-scroll divider - sits beneath display headlines */
function HeadlineDivider({ color="var(--minerva-gold)", style={} }) {
return (
);
}
/* Eyebrow label */
function Eyebrow({ children, color="var(--minerva-gold)", center=false }) {
return (
{children}
{center && }
);
}
/* Inline Lucide-style icon set */
const ICONS = {
users: [,,,],
scale: [,,,,],
'alert-circle':[,,],
eye: [,],
'layout-grid':[,,,],
database: [,,],
'shield-check':[,],
zap: [],
package: [,,,],
'message-circle':[],
layers: [,,],
'git-merge': [,,],
'book-open': [,],
'check-circle':[,],
'badge': [,],
'clipboard-check':[,,],
activity: [],
network: [,,,,],
};
function Icon({ name, size=24, color="currentColor", strokeWidth=1.8 }) {
const paths = ICONS[name];
if (!paths) return null;
return (
);
}
/* Arrow icon */
function Arrow() {
return (
);
}
/* Medical circuit motif - life sciences visual language.
Gold rounded traces · IV drip bags · capsule pills · dot grid clusters. */
function CircuitSVG({ color="var(--minerva-gold)", opacity=0.40, style:ext={} }) {
const c = color;
const o = opacity;
const sw = 1.3;
return (
);
}
/* Decorative side-border ornament - line-art in the Dark Minerva "blue version"
vocabulary: crown rings, inward chevrons, rotated-square diamonds, tendril
curls. Stroke-only in sky-blue (no gold), muted, tiles seamlessly down any
height via an SVG pattern. Top/bottom mask-fade; sits behind content (z:0). */
function VineBorder({ side = "left", opacity = 0.5, color = "var(--minerva-sky)", mask }) {
const m = mask || "linear-gradient(to bottom, transparent 0%, #000 14%, #000 86%, transparent 100%)";
const pid = `vb-${side}`;
return (
);
}
/* Inner-page hero splash - 55vh min, matches main hero energy */
function PageHero({ eyebrow, title, body, accent="var(--minerva-sky)", eyebrowColor, rightSlot, circuit=true, bgImage, ghost, minHeight="55vh", pad="96px 0 80px", align="center" }) {
const ghostWord = ghost !== undefined ? ghost : (title ? title.split(' ').slice(0,3).join(' ') : '');
return (
{bgImage &&
}
{ghostWord}
{eyebrow}
{title}
{body &&
{body}
}
{rightSlot &&
{rightSlot}
}
);
}
/* Compact footer glossary - reachable from every page */
const GLOSSARY = [
["VISDI", "Visual-Digital Knowledge - the architecture everything else is built on"],
["Mingi", "Minerva General Intelligence - the result of compliant integration between humans and AI"],
["The Ledger", "Auditable, append-only governance & comms layer - nothing ever overwritten"],
["CLV", "Context Language Validation - the context-layer counterpart to CSV"],
["Knowledge Execution","The category - the compliant AI-ready knowledge layer"],
["Trust gate", "Human verification checkpoint - nothing becomes knowledge without it"],
["knowledge packet", "The atomic unit - small, self-contained, machine-legible"],
["GxP", "The regulated standard in life sciences"],
];
/* Site footer */
function SiteFooter() {
const dim="rgba(231,239,250,0.55)";
const cols = [
{ label:"VISDI", links:[["The Method","visdi-method.html"],["Products","visdi-products.html"],["Mingi","visdi-mingi.html"],["Who It's For","visdi-products.html"],["Why General Intelligence","general-intelligence.html"]] },
{ label:"Products", links:[["VISDI Diagnose","visdi-know.html"],["VISDI Connect","visdi-connect.html"],["VISDI Operate","visdi-operate.html"],["Start a Diagnostic","/knowledge-governance/Start%20Diagnostic.html"]] },
{ label:"Minerva", links:[["Services ↗","https://minervakrm.com"],["Quality Systems","https://minervakrm.com"],["Investigations","https://minervakrm.com"],["Visualizations","https://minervakrm.com"]] },
];
return (
);
}
Object.assign(window, { WheelMark, NavBar, Btn, Eyebrow, EyebrowMark, HeadlineDivider, Arrow, Icon, CircuitSVG, VineBorder, PageHero, SiteFooter, STAGE, WORDMARK, MARK_SRC, VISDI_ICON });
/* ── Scroll-triggered animations - injected once, active on all pages ──
IMPORTANT: this preview engine has a bug where a dynamically-injected
attribute-selector CSS rule (e.g. [data-animate]{opacity:0}) wrongly beats
an element's own inline style on React-rendered nodes - so a CSS hidden
base state can leave content stuck invisible. We therefore use NO opacity
CSS at all: the hidden state and the reveal are BOTH set as inline styles
(which behave correctly), and a MutationObserver hides nodes the instant
React inserts them so there is no first-paint flash. */
(function initAnim() {
if (window.__vaInit) return; window.__vaInit = true;
const SEL = '[data-animate],[data-stagger]';
const EASE = 'cubic-bezier(.2,.8,.2,1)';
const reduce = matchMedia('(prefers-reduced-motion:reduce)').matches;
const targets = el => el.hasAttribute('data-stagger') ? [...el.children] : [el];
const prep = el => {
if (el.__vaPrepped) return; el.__vaPrepped = true;
if (reduce) return; // reduced motion → never hide
const dur = el.hasAttribute('data-stagger') ? '.6s' : '.7s';
targets(el).forEach((t, i) => {
t.style.transition = `opacity ${dur} ${EASE}`;
if (el.hasAttribute('data-stagger')) t.style.transitionDelay = (i * 0.1) + 's';
t.style.opacity = '0';
});
};
const show = el => targets(el).forEach(t => { if (t.style.opacity !== '1') t.style.opacity = '1'; });
const inView = el => {
const r = el.getBoundingClientRect();
const h = window.innerHeight || document.documentElement.clientHeight;
return r.top < h * 0.92 && r.bottom > 0;
};
const scan = () => document.querySelectorAll(SEL).forEach(el => { prep(el); if (reduce || inView(el)) show(el); });
/* Hide tagged nodes the moment React inserts them (kills first-paint flash). */
const mo = new MutationObserver(scan);
const startMO = () => mo.observe(document.getElementById('root') || document.body, { childList: true, subtree: true });
startMO();
/* Poll ~5s to cover React's async render + reveal whatever is in view; the
scroll/resize listeners then reveal everything below the fold, forever. */
let tries = 0;
(function tick() { scan(); if (++tries < 50) setTimeout(tick, 100); })();
addEventListener('scroll', scan, { passive: true });
addEventListener('resize', scan, { passive: true });
/* Hard fallback - never leave content stuck invisible. */
setTimeout(() => document.querySelectorAll(SEL).forEach(show), 6000);
})();