// Main loboLab institutional site.
// Loads after copy.js, heroes.jsx, tweaks-panel.jsx.
const { useState, useEffect, useRef } = React;
const T = window.LOBO_COPY;
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
"lang": "pt",
"hero": "wolf",
"intensity": "med",
"accent": "#F08A2B"
} /*EDITMODE-END*/;
// ──────────────────────────────────────────────────────────────────
// Reveal-on-scroll wrapper
function Reveal({ children, delay = 0, as: Tag = "div", className = "", ...rest }) {
const ref = useRef(null);
const [shown, setShown] = useState(false);
useEffect(() => {
const el = ref.current;if (!el) return;
const io = new IntersectionObserver(
(entries) => entries.forEach((e) => {if (e.isIntersecting) {setShown(true);io.unobserve(el);}}),
{ threshold: 0.15, rootMargin: "0px 0px -60px 0px" }
);
io.observe(el);
return () => io.disconnect();
}, []);
return (
{children}
);
}
// ──────────────────────────────────────────────────────────────────
// Logo lockup — uses the wolf glyph (recreated in SVG so it inverts cleanly on dark)
function Logo({ size = 32 }) {
return (
LOBOLAB
);
}
// ──────────────────────────────────────────────────────────────────
// NAV
function Nav({ lang, setLang, t }) {
const [scrolled, setScrolled] = useState(false);
useEffect(() => {
const onS = () => setScrolled(window.scrollY > 12);
onS();window.addEventListener("scroll", onS, { passive: true });
return () => window.removeEventListener("scroll", onS);
}, []);
return (
);
}
// ──────────────────────────────────────────────────────────────────
// HERO
function Hero({ variant, intensity, t }) {
const HeroBg = variant === "wolf" ? WolfHowling : variant === "streams" ? DataStreams : NeuralMesh;
return (
{t.hero.tag}
{t.hero.title_a}
{t.hero.title_b}
{t.hero.title_c}
{t.hero.sub}
{[t.hero.stat_a, t.hero.stat_b, t.hero.stat_c].map((s, i) =>
)}
scroll
);
}
// ──────────────────────────────────────────────────────────────────
// MARQUEE — fake client logos as wordmarks (placeholders)
function Marquee() {
const items = ["BANCO·TIER1", "INSURE/CO", "RETAIL∞", "PHARMA-X", "LOGTECH", "ENERGIA·SA", "FINX", "HOSPITAL·R", "SEGUROS·ZL", "TELECO"];
const row = [...items, ...items];
return (
{row.map((it, i) => {it})}
);
}
// ──────────────────────────────────────────────────────────────────
// ABOUT
function About({ t }) {
return (
{t.about.kicker}
{t.about.title}
{t.about.body_a}
{t.about.body_b}
{t.about.pillars.map((p, i) =>
{p.k}
{p.t}
{p.d}
)}
);
}
// ──────────────────────────────────────────────────────────────────
// SERVICES
function Services({ t }) {
return (
{t.services.kicker}
{t.services.title}
{t.services.items.map((s, i) =>
{s.n}
{s.t}
{s.d}
{s.tags.map((tg) => {tg})}
)}
);
}
function ServiceGlyph({ idx }) {
// Tiny abstract SVG glyphs per service — geometric, monoline orange
const sw = 1.6;
const props = { width: 36, height: 36, viewBox: "0 0 36 36", fill: "none",
stroke: "currentColor", strokeWidth: sw, strokeLinecap: "round", strokeLinejoin: "round" };
if (idx === 0) return (// single agent node with branches
);
if (idx === 1) return (// workflow boxes
);
if (idx === 2) return (// strategy chart
);
return (// integration plug/cycle
);
}
// ──────────────────────────────────────────────────────────────────
// CASES
function Cases({ t }) {
return (
{t.cases.kicker}
{t.cases.title}
{t.cases.items.map((c, i) =>
0{i + 1}
{c.sector}
{c.t}
{c.body}
{c.metrics.map((m, j) =>
)}
)}
);
}
// ──────────────────────────────────────────────────────────────────
// TEAM
function Team({ t }) {
// Initial-only avatars on tinted plates (no photo placeholders to avoid slop)
const palette = ["#F08A2B", "#5B6B85", "#E97640", "#7B8AA3"];
return (
{t.team.kicker}
{t.team.title}
{t.team.sub}
{t.team.members.map((m, i) => {
const initials = m.name.split(" ").map((s) => s[0]).slice(0, 2).join("");
return (
{initials}
{m.name}
{m.role}
{m.bio}
);
})}
);
}
// ──────────────────────────────────────────────────────────────────
// CONTACT
function Contact({ t }) {
const [sent, setSent] = useState(false);
return (
);
}
// ──────────────────────────────────────────────────────────────────
// FOOTER
function Footer({ t }) {
return (
);
}
// ──────────────────────────────────────────────────────────────────
// TWEAKS
function LoboTweaks({ t, setTweak }) {
return (
setTweak("lang", v)} />
setTweak("hero", v)} />
setTweak("intensity", v)} />
setTweak("accent", v)} />
);
}
// ──────────────────────────────────────────────────────────────────
// APP
function App() {
const [tw, setTweak] = useTweaks(TWEAK_DEFAULTS);
const t = T[tw.lang] || T.pt;
// Apply accent color as CSS var
useEffect(() => {
document.documentElement.style.setProperty("--accent", tw.accent);
}, [tw.accent]);
return (
);
}
ReactDOM.createRoot(document.getElementById("root")).render();