// Aerixlab.com — marketing site
// Periwinkle pastel · light mode · 4 sections (Hero, Features, Pricing, CTA+Footer)
const SITE = {
ink: '#1A1F3D',
inkSoft: '#3F4566',
paper: '#FFFFFF',
surface: '#F7F8FE',
tint: '#E8EBFC',
brand: '#7986F0',
brandHover: '#6470DE',
brandSoft: '#B7BFF6',
brandPale: '#DDE2FA',
mute: '#6B6F8E',
line: '#E5E7F4',
success: '#16A572',
successSoft: '#D7F2E5',
};
const APP_URL = 'https://app.aerixlab.com';
// ---------- RESPONSIVE ----------
// Two breakpoints: <= 640px (mobile), <= 1024px (tablet)
function useViewport() {
const get = () => {
const w = typeof window === 'undefined' ? 1200 : window.innerWidth;
return { w, isMobile: w <= 640, isTablet: w <= 1024 };
};
const [vp, setVp] = React.useState(get);
React.useEffect(() => {
const onR = () => setVp(get());
window.addEventListener('resize', onR);
return () => window.removeEventListener('resize', onR);
}, []);
return vp;
}
// ---------- TWEAK CONTEXT ----------
const TweakCtx = React.createContext({});
function useT() { return React.useContext(TweakCtx); }
// Resolves the announcement copy template, replacing {days} with the actual value.
function resolveCopy(template, days) {
return (template || '').replace(/\{days\}/g, days);
}
// CTA text — uses trial language when trialDays > 0
function primaryCtaLabel(days, baseFree = 'Start free') {
return days > 0 ? `Start ${days}-day free trial` : baseFree;
}
// ---------- LOGO MARK ----------
function Cyclone({ size = 36, color = SITE.brand, ink = SITE.ink, strokeW = 6, swirlW = 7 }) {
return (
);
}
function Logo({ size = 28 }) {
return (
aerixlab
);
}
// ---------- ANNOUNCEMENT BAR ----------
function AnnouncementBar() {
const t = useT();
const { isMobile } = useViewport();
if (!t.showAnnouncement || !t.trialDays) return null;
return (
{resolveCopy(t.announcementCopy, t.trialDays)}
Start now →
);
}
// ---------- NAV ----------
function Nav() {
const { isMobile, isTablet } = useViewport();
const [open, setOpen] = React.useState(false);
// close on resize back to desktop
React.useEffect(() => { if (!isMobile) setOpen(false); }, [isMobile]);
// lock body scroll while open
React.useEffect(() => {
if (open) {
const prev = document.body.style.overflow;
document.body.style.overflow = 'hidden';
return () => { document.body.style.overflow = prev; };
}
}, [open]);
const links = [
['Product', '#product'],
['Why Aerixlab', '#features'],
['Pricing', '#pricing'],
['Sign in', APP_URL],
];
return (
{!isMobile && (
{links.map(([l, h]) => (
{l} ))}
)}
{!isMobile && (
Get started →
)}
{isMobile && (
setOpen(o => !o)} style={{
background: 'transparent', border: `1px solid ${SITE.line}`,
width: 44, height: 44, borderRadius: 12, cursor: 'pointer',
display: 'flex', alignItems: 'center', justifyContent: 'center',
padding: 0,
}}>
{open ? (<> >)
: (<> >)}
)}
{isMobile && open && (
{links.map(([l, h]) => (
setOpen(false)} style={{
padding: '16px 8px', fontSize: 18, fontWeight: 500,
color: SITE.ink, textDecoration: 'none',
borderBottom: `1px solid ${SITE.line}`,
}}>{l}
))}
setOpen(false)} style={{
marginTop: 20,
background: SITE.ink, color: '#fff', textDecoration: 'none',
padding: '16px 22px', borderRadius: 999, fontSize: 15, fontWeight: 600,
textAlign: 'center',
}}>Get started →
)}
);
}
const navLinkStyle = {
color: SITE.inkSoft, fontSize: 14, fontWeight: 500,
textDecoration: 'none', letterSpacing: '-0.005em',
};
// ---------- 1) HERO ----------
function Hero() {
const t = useT();
const { isMobile, isTablet } = useViewport();
const days = t.trialDays;
const ctaLabel = primaryCtaLabel(days);
return (
{/* dotted pastel grid */}
ARX E-Invoice · for Malaysian SMEs
{renderHeadline(t.heroHeadline)}
{t.heroSub}
{days > 0 ? `${days}-day free trial` : 'Free to start'}
No credit card
Cancel anytime
{/* product mockup */}
);
}
function Check({ small }) {
const s = small ? 14 : 18;
return (
);
}
// Renders headline copy with *segments wrapped in asterisks* styled as italic-serif accent.
// e.g. "Send invoices. *Track money.* Done in minutes."
function renderHeadline(text) {
if (!text) return null;
const parts = text.split(/(\*[^*]+\*)/g);
return parts.map((p, i) => {
if (p.startsWith('*') && p.endsWith('*') && p.length > 2) {
return (
{p.slice(1, -1)}
);
}
// preserve newlines as
const lines = p.split('\n');
return lines.map((ln, j) => (
{ln}
{j < lines.length - 1 && }
));
});
}
function HeroProductMock() {
const { isMobile } = useViewport();
const invoices = [
{ id: 'INV-0234', name: 'Acme Trading Sdn Bhd', amount: 'RM 4,280.00', status: 'Paid', tone: 'success' },
{ id: 'INV-0233', name: 'Bumi Hijau Enterprise', amount: 'RM 1,950.00', status: 'Sent', tone: 'brand' },
{ id: 'INV-0232', name: 'Kedai Kopi Aman', amount: 'RM 620.00', status: 'Overdue', tone: 'warn' },
{ id: 'INV-0231', name: 'Lestari Logistics', amount: 'RM 12,400.00', status: 'Paid', tone: 'success' },
{ id: 'INV-0230', name: 'Saga Print Sdn Bhd', amount: 'RM 880.00', status: 'Draft', tone: 'mute' },
];
const toneMap = {
success: { bg: SITE.successSoft, fg: SITE.success },
brand: { bg: SITE.brandPale, fg: SITE.brand },
warn: { bg: '#FBE3DC', fg: '#C84A2B' },
mute: { bg: SITE.surface, fg: SITE.mute },
};
return (
{/* browser bar */}
{['#FF6058', '#FFBD2E', '#28C840'].map(c =>
)}
app.aerixlab.com/invoices
{/* app body */}
{/* sidebar — hidden on mobile to keep mock readable */}
{!isMobile && (
aerixlab
{[
['Dashboard', false],
['Invoices', true],
['Customers', false],
['Accounting', false],
['Reports', false],
['Settings', false],
].map(([label, active]) => (
{label}
))}
)}
{/* main */}
Invoices
32 this month · RM 48,290 outstanding
+ New invoice
{/* mini stats */}
{[
{ label: 'Paid', value: 'RM 22,140', tone: SITE.success },
{ label: 'Pending', value: 'RM 18,950', tone: SITE.brand },
{ label: 'Overdue', value: 'RM 7,200', tone: '#C84A2B' },
].map(s => (
))}
{/* list */}
{!isMobile && No. }
Customer
{!isMobile && Amount }
Status
{invoices.map((inv, i) => (
{!isMobile && {inv.id} }
{inv.name}
{isMobile && {inv.amount} }
{!isMobile && {inv.amount} }
{inv.status}
))}
);
}
// ---------- 2) FEATURES ----------
function Features() {
const features = [
{
title: '30-second invoices',
body: 'Create and send professional invoices faster than your kettle boils. Save customer details, line items, and send via email or WhatsApp.',
icon: 'send',
},
{
title: 'Money in, money out',
body: 'Simple accounting built in. Track every ringgit, reconcile payments automatically, and see your cash position at a glance.',
icon: 'wallet',
},
{
title: 'Made for Malaysia',
body: 'RM-first pricing, SST-ready, designed around how Malaysian SMEs actually run. From mamak shops to consultancies.',
icon: 'flag',
},
{
title: 'Cloud, any device',
body: 'Your data syncs across laptop, phone, and tablet. Send a quote from the kopitiam, mark it paid back at the office.',
icon: 'cloud',
},
];
return (
Why Aerixlab
Built around how Malaysian
SMEs actually work.
{features.map(f => (
{f.title}
{f.body}
))}
);
}
function FeatureIcon({ kind }) {
const wrap = {
width: 52, height: 52, borderRadius: 14,
background: SITE.brandPale, color: SITE.brand,
display: 'flex', alignItems: 'center', justifyContent: 'center',
};
const ICONS = {
send: (
),
wallet: (
),
flag: (
),
cloud: (
),
};
return {ICONS[kind]}
;
}
// ---------- 3) PRICING ----------
function Pricing() {
const t = useT();
const [yearly, setYearly] = React.useState(false);
const showPromo = t.yearlyPromo;
const days = t.trialDays;
return (
Pricing
Simple plans. {showPromo ? 'Promo prices.' : 'Fair prices.'}
{days > 0 ? `Every plan starts with a ${days}-day free trial. No credit card. Cancel anytime.` : `Start free, upgrade when you're ready. All plans include the full e-invoice engine and accounting basics.`}
{/* toggle */}
{[
{ label: 'Monthly', value: false },
{ label: 'Yearly · save up to 50%', value: true },
].map(opt => (
setYearly(opt.value)} style={{
background: yearly === opt.value ? SITE.ink : 'transparent',
color: yearly === opt.value ? '#fff' : SITE.inkSoft,
border: 'none', padding: '10px 22px', borderRadius: 999,
fontSize: 13, fontWeight: 600, cursor: 'pointer',
fontFamily: 'inherit', letterSpacing: '-0.005em',
transition: 'background 0.15s ease',
}}>{opt.label}
))}
0 ? `Start ${days}-day trial` : 'Start with Basic'}
highlighted={false}
/>
0 ? `Start ${days}-day Pro trial` : 'Get Pro'}
highlighted={true}
/>
);
}
function PriceCard({ tier, tagline, yearly, monthly, yearlyOriginal, yearlyPromo, showPromo = true, trialDays = 0, features, cta, highlighted }) {
return (
{highlighted && (
Best Value
)}
{yearly ? (
<>
{showPromo && (
RM {yearlyOriginal}
)}
RM {showPromo ? yearlyPromo : yearlyOriginal}
/year
{showPromo && (
Promo — save RM {yearlyOriginal - yearlyPromo}
)}
>
) : (
RM {monthly}
/month
)}
{trialDays > 0 && (
✨
{trialDays}-day free trial for new customers
)}
{features.map(f => (
{f}
))}
{cta} →
);
}
function CheckPlan({ inverted }) {
return (
);
}
// ---------- 4) CTA + FOOTER ----------
function CtaFooter() {
const t = useT();
const { isMobile, isTablet } = useViewport();
const days = t.trialDays;
return (
{/* big CTA */}
{/* glow */}
Start sending cleaner
invoices today.
{days > 0 ? `${days}-day free trial. No credit card. Cancel anytime. Your books will thank you.` : 'Free to start. No credit card. Cancel anytime. Your books will thank you.'}
{/* footer */}
);
}
function FooterCol({ title, links }) {
return (
{title}
{links.map(([label, href]) => (
{label}
))}
);
}
// ---------- ROOT ----------
function AerixSite() {
const defaults = window.TWEAK_DEFAULTS || {
trialDays: 14,
showAnnouncement: true,
announcementCopy: '\ud83c\udf89 New customers \u2014 get {days} days free. No credit card required.',
heroHeadline: 'Send invoices. *Track money.* \nDone in minutes.',
heroSub: 'ARX E-Invoice is the easiest way for Malaysian SMEs to bill customers, track payments, and keep simple accounting \u2014 all in one cloud system, without the spreadsheets.',
yearlyPromo: true,
};
const [t, setTweak] = useTweaks(defaults);
return (
setTweak('trialDays', v)} />
setTweak('showAnnouncement', v)} />
setTweak('announcementCopy', v)} />
setTweak('heroHeadline', v)} />
setTweak('heroSub', v)} />
setTweak('yearlyPromo', v)} />
);
}
window.AerixSite = AerixSite;