// v4-components.jsx — Awesomic-skinned design system for 9mith Advisory // Cosmica type (weight-driven hierarchy), Mist/Snow/Aubergine surfaces, // maximum roundness (36px cards, pill buttons), flat surfaces — no drop shadows. const { useState, useEffect, useRef } = React; // Palette (Awesomic tokens) const C = { aubergine: '#421d24', aubergineDeep: '#4e242c', graphite: '#3f3f46', slate: '#52525b', steel: '#71717a', ash: '#a1a1aa', pebble: '#d4d4d8', fog: '#ececee', mist: '#f4f4f5', snow: '#ffffff', gold: '#c2a05a', // subtle gold — logo "g" only onDark: 'rgba(255,255,255,0.92)', onDarkMuted: 'rgba(255,255,255,0.66)', }; // ── Scroll visibility hook ──────────────────────────────────────────────────── function useVisible() { const ref = useRef(null); const [vis, setVis] = useState(false); useEffect(() => { const el = ref.current; if (!el) return; const timer = setTimeout(() => setVis(true), 800); const io = new IntersectionObserver(([e]) => { if (e.isIntersecting) { setVis(true); clearTimeout(timer); io.disconnect(); } }, { threshold: 0, rootMargin: '0px 0px 80px 0px' }); io.observe(el); return () => { clearTimeout(timer); io.disconnect(); }; }, []); return [ref, vis]; } // ── Fade-in wrapper (Awesomic overshoot spring) ─────────────────────────────── function FadeIn({ children, delay = 0, style: s, className }) { const [ref, vis] = useVisible(); return (
{children}
); } // ── CountUp ─────────────────────────────────────────────────────────────────── function CountUp({ to, startFrom = 0, suffix = '', prefix = '', duration = 1000, style: s, className }) { const [val, setVal] = useState(startFrom); const [triggered, setTriggered] = useState(false); const ref = useRef(null); useEffect(() => { const el = ref.current; if (!el) return; const io = new IntersectionObserver(([e]) => { if (e.isIntersecting) { setTriggered(true); io.disconnect(); } }, { threshold: 0.5 }); io.observe(el); return () => io.disconnect(); }, []); useEffect(() => { if (!triggered) return; const t0 = Date.now(); const tick = () => { const p = Math.min((Date.now() - t0) / duration, 1); const ease = 1 - Math.pow(1 - p, 3); setVal(Math.round(startFrom + (to - startFrom) * ease)); if (p < 1) requestAnimationFrame(tick); }; requestAnimationFrame(tick); }, [triggered]); return {prefix}{val.toLocaleString()}{suffix}; } // ── Logo ────────────────────────────────────────────────────────────────────── // "g" set in Giaza — subtle gold; "mith" in aubergine (light bg) / snow (dark bg). function Logo({ dark = false, compact = false, onClick }) { const gs = compact ? 30 : 46; const ms = compact ? 17 : 26; const mithColor = dark ? C.mist : C.aubergine; return ( ); } // ── Eyebrow label (quiet, sentence case, with short rule) ───────────────────── function Eyebrow({ label, dark = false }) { return (
{label}
); } // ── Typography ──────────────────────────────────────────────────────────────── // Display headline / pull quote. Use inside children for weight contrast. function PullQuote({ children, size = 36, dark = false, center = false, maxWidth = 780, muted = false, italic = false }) { const color = muted ? (dark ? C.onDarkMuted : C.steel) : (dark ? C.snow : C.aubergine); return (

30 ? 1.1 : 1.28, maxWidth, margin: center ? '0 auto' : 0, textAlign: center ? 'center' : 'left', textWrap: 'pretty', letterSpacing: '-0.01em' }}>{children}

); } function Body({ children, size = 16, color, dark = false, maxWidth, style: s, italic = false }) { const c = color || (dark ? C.onDarkMuted : C.slate); return

{children}

; } function Caption({ children, dark = false }) { return {children}; } function Hairline({ style: s, dark = false }) { return
; } // ── Stat (Awesomic bare-numeral style) ──────────────────────────────────────── function StatAnnotation({ number, suffix = '', prefix = '', label, dark = false, countFrom }) { const borderC = dark ? 'rgba(255,255,255,0.14)' : C.pebble; return (
{countFrom !== undefined ? : <>{prefix}{number}{suffix}}
{label}
); } // ── Primary CTA — aubergine pill, pressed-glass shadow ──────────────────────── function CTAButton({ children, onClick, variant = 'primary' }) { const [press, setPress] = useState(false); const primary = variant === 'primary'; return ( ); } // ── Text link with arrow (strong affordance via weight + arrow) ────────────── function GoldLink({ children, onClick, dark = false, style: s }) { const [hov, setHov] = useState(false); return ( ); } // ── Page section hero ───────────────────────────────────────────────────────── function SectionHero({ label, headline, sub, dark = true, size = 46 }) { return (
{dark &&
g
}
{label && } {headline} {sub &&

{sub}

}
); } // ── Sector card — Snow card, 36px radius, color dot marker ──────────────────── function SectorCard({ name, color, metric, count }) { const [hov, setHov] = useState(false); return (
setHov(true)} onMouseLeave={() => setHov(false)}>
{name}

{metric}

{count}

); } // ── Issue tree — rounded fog boxes, pebble connectors ───────────────────────── function IssueTree({ root, branches }) { return (

{root}

{branches.map((b, i) => (

{b.label}

{b.body}
))}
); } window.AdvC = C; Object.assign(window, { useVisible, FadeIn, CountUp, Logo, Eyebrow, PullQuote, Body, Caption, Hairline, StatAnnotation, CTAButton, GoldLink, SectionHero, SectorCard, IssueTree });