/* Terracheck Marketing Landing Page
Vanilla React + Babel — no build step.
All components (Btn, Chip, Panel, Metric, Ind, LayerRow, Logo, Input, ThemeToggle)
come from components.jsx via window globals.
*/
const { useState, useEffect, useRef } = React;
/* ─── color constants (hex for alpha math; CSS vars for plain references) ─── */
const TEAL = '#14b8a6';
const GREEN = '#10b981';
const AMBER = '#eab308';
const BLUE = '#3b82f6';
const PURPLE = '#a78bfa';
/* Animation text colors — CSS vars work as style.color strings */
const _t = 'var(--tx)';
const _t3 = 'var(--tx-3)';
const _tl = 'var(--teal)';
const _gn = 'var(--layer-unit)';
const _am = 'var(--layer-sec)';
const _bl = 'var(--layer-integ)';
/* ─── FadeIn ─────────────────────────────────────────────────────────────── */
function FadeIn({ children, delay = 0 }) {
const [visible, setVisible] = useState(false);
const ref = useRef(null);
useEffect(() => {
const obs = new IntersectionObserver(
([entry]) => { if (entry.isIntersecting) setVisible(true); },
{ threshold: 0.05 }
);
if (ref.current) obs.observe(ref.current);
return () => obs.disconnect();
}, []);
return (
{children}
);
}
/* ─── WorkflowAnimation ──────────────────────────────────────────────────── */
function mkL(at, icon, ic, segs) {
return { at, type: 'item', item: { kind: 'line', icon, ic, segs } };
}
function mkD(at, label) {
return { at, type: 'item', item: { kind: 'div', label } };
}
function mkLR(at, layer, count, duration) {
return { at, type: 'item', item: { kind: 'layer', layer, status: 'pass', count, duration } };
}
function mkDt(at, dots) {
return { at, type: 'dots', dots };
}
const ANIM_EVENTS = [
mkD(0, '⬡ detect'),
mkL(300, '▸', _t3, [{ t: 'polling jfrog registry...', c: _t3 }]),
mkL(1400, '◆', _tl, [{ t: 'new version detected: ', c: _tl }, { t: 'aks-cluster v3.2.0', c: _t }]),
mkD(2000, '⬡ analyze'),
mkL(2300, '▸', _t3, [{ t: 'analyzing module complexity...', c: _t3 }]),
mkL(3100, '◆', _am, [{ t: 'complexity score: ', c: _t }, { t: '67/100', c: _am }, { t: ' (medium)', c: _t3 }]),
mkL(3700, '◆', _bl, [{ t: 'ai strategy: ', c: _t }, { t: 'claude sonnet 4.6', c: _bl }]),
mkD(4500, '⬡ generate'),
mkL(4800, '▸', _t3, [{ t: 'generating test suite...', c: _t3 }]),
mkL(5300, '◆', _gn, [{ t: 'unit_test.go', c: _gn }, { t: ' 847 lines', c: _t3 }]),
mkL(5700, '◆', _gn, [{ t: 'security_test.go', c: _gn }, { t: ' 312 lines', c: _t3 }]),
mkL(6100, '◆', _gn, [{ t: 'integration_test.go', c: _gn }, { t: ' 534 lines', c: _t3 }]),
mkL(6500, '◆', _gn, [{ t: 'e2e_test.go', c: _gn }, { t: ' 289 lines', c: _t3 }]),
mkL(6900, '✓', _tl, [{ t: '4 test files generated', c: _tl }]),
mkD(7600, '⬡ validate'),
mkL(7900, '▸', _t3, [{ t: 'validating syntax...', c: _t3 }]),
mkL(8700, '✓', _gn, [{ t: 'terraform validate', c: _t }, { t: ' ✓', c: _gn }]),
mkL(9200, '✓', _gn, [{ t: 'go build ./...', c: _t }, { t: ' ✓', c: _gn }]),
mkD(9900, '⬡ fast tests (k8s)'),
mkDt(9900, { unit: 'active' }),
mkL(10100, '▸', _t3, [{ t: 'dispatching k8s jobs...', c: _t3 }]),
mkLR(10900, 'unit', '18/18', '4.2s'),
mkDt(10900, { unit: 'done', sec: 'active' }),
mkLR(12000, 'sec', '31/31', '12.8s'),
mkDt(12000, { sec: 'done' }),
mkL(12500, '◆', _tl, [{ t: 'fast tests passed ✓', c: _tl }]),
mkD(13200, '⬡ gate'),
mkL(13500, '▸', _t3, [{ t: 'checking cloud gate...', c: _t3 }]),
mkL(14100, '✓', _gn, [{ t: 'fast tests', c: _gn }, { t: ' passed ✓', c: _t3 }]),
mkL(14600, '✓', _gn, [{ t: 'azure credentials', c: _gn }, { t: ' configured ✓', c: _t3 }]),
mkL(15100, '◆', _tl, [{ t: 'gate passed — scheduling cloud tests', c: _tl }]),
mkD(15800, '⬡ cloud tests (azure)'),
mkDt(15800, { integ: 'active' }),
mkL(16100, '▸', _t3, [{ t: 'running cloud tests in azure...', c: _t3 }]),
mkLR(17200, 'integ', '6/6', '8m 32s'),
mkDt(17200, { integ: 'done', e2e: 'active' }),
mkLR(18400, 'e2e', '3/3', '22m 10s'),
mkDt(18400, { e2e: 'done' }),
mkD(19100, '⬡ promote'),
mkL(19500, '★', _tl, [{ t: 'aks-cluster v3.2.0', c: _tl }, { t: ' → PROMOTED', c: _gn }]),
mkL(20000, '★', _gn, [{ t: 'badge: validated ✓', c: _gn }]),
mkL(20400, '★', _gn, [{ t: 'release repo: updated', c: _gn }]),
{ at: 20400, type: 'done' },
];
const ANIM_LOOP_MS = 24400;
function dsToInd(ds) {
if (ds === 'done') return 'pass';
if (ds === 'active') return 'run';
return 'pend';
}
function WorkflowAnimation() {
const [items, setItems] = useState([]);
const [dots, setDots] = useState({ unit: 'idle', sec: 'idle', integ: 'idle', e2e: 'idle' });
const [isDone, setIsDone] = useState(false);
const termRef = useRef(null);
useEffect(() => {
let alive = true;
const ids = [];
function run() {
if (!alive) return;
setItems([]);
setDots({ unit: 'idle', sec: 'idle', integ: 'idle', e2e: 'idle' });
setIsDone(false);
for (const evt of ANIM_EVENTS) {
ids.push(setTimeout(() => {
if (!alive) return;
if (evt.type === 'item') setItems(p => [...p, evt.item]);
else if (evt.type === 'dots') setDots(p => ({ ...p, ...evt.dots }));
else if (evt.type === 'done') setIsDone(true);
}, evt.at));
}
ids.push(setTimeout(run, ANIM_LOOP_MS));
}
run();
return () => { alive = false; ids.forEach(clearTimeout); };
}, []);
useEffect(() => {
const el = termRef.current;
if (el) el.scrollTop = el.scrollHeight;
}, [items]);
return (
{/* title bar */}
terracheck — live workflow
{/* terminal body */}
{items.map((item, i) => {
if (item.kind === 'div') {
return (
0 ? '1px solid var(--line)' : undefined,
marginTop: i > 0 ? 4 : 0,
}}>{item.label}
);
}
if (item.kind === 'layer') {
return (
);
}
return (
{item.icon}
{item.segs.map((seg, j) => (
{seg.t}
))}
);
})}
{items.length > 0 && !isDone && (
▸
)}
{/* status bar */}
aks-cluster v3.2.0
{isDone ? 'PROMOTED ✓' : 'testing...'}
);
}
/* ─── WaitlistForm ───────────────────────────────────────────────────────── */
const PLANS = [
{ id: 'free', label: 'free · $0' },
{ id: 'starter', label: 'starter · $49/mo' },
{ id: 'pro', label: 'pro · $199/mo' },
{ id: 'team', label: 'team · $499/mo' },
];
function WaitlistForm() {
const [plan, setPlan] = useState('pro');
const [email, setEmail] = useState('');
const [status, setStatus] = useState('idle');
const [error, setError] = useState('');
async function submit() {
if (!email.includes('@')) {
setError('enter a valid email address');
return;
}
setError('');
setStatus('loading');
try {
const { error } = await window.__sb
.from('waitlist')
.insert({ email: email.trim(), plan });
if (error) throw error;
setStatus('done');
} catch (err) {
console.error(err);
setStatus('idle');
setError('something went wrong. please try again.');
}
}
if (status === 'done') {
return (
you're on the list — we'll be in touch
);
}
return (
{PLANS.map(p => (
setPlan(p.id)}
>
{p.label}
))}
);
}
/* ─── Nav ────────────────────────────────────────────────────────────────── */
function Nav() {
const [scrolled, setScrolled] = useState(false);
useEffect(() => {
const h = () => setScrolled(window.scrollY > 30);
window.addEventListener('scroll', h, { passive: true });
return () => window.removeEventListener('scroll', h);
}, []);
function scrollTo(id) {
document.getElementById(id)?.scrollIntoView({ behavior: 'smooth' });
}
return (
{[
['features', 'features'],
['pricing', 'pricing'],
['integrations', 'integrations'],
['roadmap', 'roadmap'],
].map(([id, label]) => (
scrollTo(id)}>
{label}
))}
scrollTo('cta')}>
join waitlist
);
}
/* ─── Hero ───────────────────────────────────────────────────────────────── */
function Hero() {
return (
early access — join the waitlist
terraform modules
tested automatically
terracheck detects new module versions in your registry, generates ai test suites using claude, and runs 4-layer testing against real cloud infrastructure — fully automated, no ci changes needed.
);
}
/* ─── SocialProof ────────────────────────────────────────────────────────── */
function SocialProof() {
const metrics = [
{ label: 'modules tested', value: '2,400+', sub: 'across all clouds', color: 'var(--teal)' },
{ label: 'test files generated', value: '18k+', sub: 'by claude ai', color: 'var(--layer-unit)' },
{ label: 'findings blocked', value: '3,200+', sub: 'before production', color: 'var(--layer-sec)' },
{ label: 'avg test runtime', value: '~26m', sub: 'unit to e2e', color: 'var(--layer-integ)' },
];
return (
{metrics.map((m, i) => (
))}
);
}
/* ─── Features ───────────────────────────────────────────────────────────── */
const FEATURES = [
{
icon: '◉',
title: 'auto-detection',
desc: 'polls your registry every 5 minutes. the moment a new module version lands, testing kicks off automatically — no webhooks, no manual triggers.',
accent: TEAL,
},
{
icon: '◈',
title: 'ai test generation',
desc: 'claude reads your .tf files and generates unit, security, integration, and e2e tests. understands your variables, resources, and outputs.',
accent: BLUE,
},
{
icon: '▤',
title: '4-layer testing',
desc: 'unit (seconds) → security scan (seconds) → integration on real infra (minutes) → e2e workflow (minutes). every layer must pass before promotion.',
accent: GREEN,
},
{
icon: '◎',
title: 'cis compliance',
desc: 'built-in checks for azure cis 1.4, aws cis 1.5, and gcp cis 1.3. compliance failures block promotion. every module ships policy-compliant.',
accent: AMBER,
},
{
icon: '⬡',
title: 'multi-cloud',
desc: 'azure, aws, gcp, and kubernetes. test modules against real cloud credentials. catches provider-specific bugs before they hit production.',
accent: PURPLE,
},
{
icon: '★',
title: 'auto-promotion',
desc: 'validated modules get promoted to your release registry with a certified badge. your team only consumes modules that have passed all 4 layers.',
accent: TEAL,
},
];
function Features() {
return (
capabilities
everything your modules need
from first commit to certified release — fully automated.
{FEATURES.map((f, i) => (
{f.icon}
{f.title}
{f.desc}
))}
);
}
/* ─── HowItWorks ─────────────────────────────────────────────────────────── */
const STEPS = [
{
n: '01', title: 'connect your registry',
desc: 'point terracheck at your jfrog artifactory, terraform registry, gitlab, or github packages. one api key, done.',
accent: TEAL,
},
{
n: '02', title: 'version detected',
desc: 'terracheck polls every 5 minutes. when a new version appears, it queues a full pipeline run automatically.',
accent: BLUE,
},
{
n: '03', title: 'ai generates tests',
desc: 'claude reads your .tf source and generates unit, security, integration, and e2e test files tailored to your module.',
accent: BLUE,
},
{
n: '04', title: 'fast tests on k8s',
desc: 'unit and security tests run in parallel kubernetes jobs — usually done in under 30 seconds.',
accent: GREEN,
},
{
n: '05', title: 'cloud tests on real infra',
desc: 'integration and e2e tests provision actual azure, aws, or gcp resources, verify behavior, and tear down cleanly.',
accent: AMBER,
},
{
n: '06', title: 'promote or block',
desc: 'all layers pass → module gets a validated badge and lands in your release registry. any failure → blocked with a full report.',
accent: GREEN,
},
];
function HowItWorks() {
return (
how it works
six steps to certified
{STEPS.map((s, i) => (
{s.n}
{s.title}
{s.desc}
))}
);
}
/* ─── Integrations ───────────────────────────────────────────────────────── */
const INTEG_ITEMS = [
{ name: 'jfrog artifactory', cat: 'registries', icon: '📦', color: TEAL, desc: 'poll artifactory for new module versions' },
{ name: 'terraform registry', cat: 'registries', icon: '⬡', color: TEAL, desc: 'official hashicorp terraform registry' },
{ name: 'gitlab packages', cat: 'registries', icon: '🦊', color: TEAL, desc: 'gitlab package registry support' },
{ name: 'github packages', cat: 'registries', icon: '◉', color: TEAL, desc: 'github container & package registry' },
{ name: 'azure devops', cat: 'ci/cd', icon: '⚙', color: BLUE, desc: 'trigger pipelines on promotion' },
{ name: 'github actions', cat: 'ci/cd', icon: '▶', color: BLUE, desc: 'post-promotion workflow dispatch' },
{ name: 'gitlab ci', cat: 'ci/cd', icon: '⚡', color: BLUE, desc: 'pipeline triggers via gitlab api' },
{ name: 'azure', cat: 'environment', icon: '☁', color: PURPLE, desc: 'real azure infra for cloud tests' },
{ name: 'aws', cat: 'environment', icon: '◈', color: AMBER, desc: 'aws ec2/vpc/iam integration tests' },
{ name: 'gcp', cat: 'environment', icon: '◎', color: GREEN, desc: 'google cloud compute & gke tests' },
{ name: 'kubernetes', cat: 'environment', icon: '⬢', color: BLUE, desc: 'k8s jobs for unit & security layers' },
];
const INTEG_FILTERS = [
{ id: 'all', label: 'all', color: 'var(--tx-2)' },
{ id: 'registries', label: 'registries', color: TEAL },
{ id: 'ci/cd', label: 'ci / cd', color: BLUE },
{ id: 'environment', label: 'environment', color: PURPLE },
];
function IntegCard({ item, visible }) {
const [hovered, setHovered] = useState(false);
return (
setHovered(true)}
onMouseLeave={() => setHovered(false)}
style={{
background: hovered ? `${item.color}0f` : 'var(--bg-panel)',
border: `1px solid ${hovered ? `${item.color}55` : 'var(--line)'}`,
borderRadius: 'var(--r-md)',
padding: '1.25rem',
cursor: 'default',
transition: 'all 200ms',
transform: hovered ? 'translateY(-2px)' : 'none',
boxShadow: hovered ? `0 8px 32px ${item.color}22` : 'none',
opacity: visible ? 1 : 0,
pointerEvents: visible ? 'auto' : 'none',
}}
>
{item.desc}
);
}
function Integrations() {
const [filter, setFilter] = useState('all');
return (
integrations
works with your stack
{/* Filter tabs */}
{INTEG_FILTERS.map(f => (
setFilter(f.id)}
style={{
padding: '5px 14px',
borderRadius: 'var(--r-sm)',
border: `1px solid ${filter === f.id ? f.color : 'var(--line)'}`,
background: filter === f.id ? `${f.color}18` : 'transparent',
color: filter === f.id ? f.color : 'var(--tx-3)',
fontFamily: 'var(--fn-mono)', fontSize: 12, fontWeight: 600,
cursor: 'pointer', transition: 'all 150ms',
textTransform: 'lowercase',
}}
>{f.label}
))}
{/* Cards grid */}
{INTEG_ITEMS.map(item => (
))}
{/* Pipeline flow */}
{[
{ label: 'registry', icon: '📦', color: TEAL },
{ label: 'detect', icon: '◉', color: TEAL, arrow: true },
{ label: 'generate', icon: '◈', color: BLUE, arrow: true },
{ label: 'k8s tests', icon: '⬢', color: GREEN, arrow: true },
{ label: 'cloud', icon: '☁', color: PURPLE, arrow: true },
{ label: 'promote', icon: '★', color: TEAL, arrow: true },
].map((step) => (
{step.arrow && (
)}
))}
);
}
function FlowStep({ step }) {
const [h, setH] = useState(false);
return (
setH(true)}
onMouseLeave={() => setH(false)}
style={{
display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 6,
padding: '10px 14px', borderRadius: 'var(--r-sm)', cursor: 'default',
background: h ? `${step.color}12` : 'transparent',
transition: 'background 150ms',
}}
>
{step.icon}
{step.label}
);
}
/* ─── Pricing ────────────────────────────────────────────────────────────── */
const TIERS = [
{
id: 'free', name: 'free', price: '$0',
tagline: 'get started today',
features: ['3 modules', 'unit tests only', '10 ai test generations/mo', 'terraform registry', 'community support'],
},
{
id: 'starter', name: 'starter', price: '$49', period: '/mo',
tagline: 'growing teams',
features: ['15 modules', 'unit + security tests', '100 ai test generations/mo', 'weekly continuous testing', 'all registries', 'email support'],
},
{
id: 'pro', name: 'pro', price: '$199', period: '/mo',
tagline: 'production-grade',
popular: true,
features: ['50 modules', 'all 4 test layers', '500 ai test generations/mo', 'nightly continuous testing', 'custom opa policies', 'cis compliance reports', 'priority support'],
},
{
id: 'team', name: 'team', price: '$499', period: '/mo',
tagline: 'enterprise-ready',
features: ['200 modules', 'all 4 test layers', '2000 ai test generations/mo', 'custom schedule', 'self-hosted option', 'sso / saml', 'sla + dedicated support'],
},
];
function PricingCard({ tier }) {
function scrollToCTA() {
document.getElementById('cta')?.scrollIntoView({ behavior: 'smooth' });
}
return (
{tier.popular && (
most popular
)}
{tier.name}
{tier.tagline}
{tier.price}
{tier.period && (
{tier.period}
)}
{tier.features.map(f => (
✓
{f}
))}
join waitlist
);
}
function Pricing() {
return (
pricing
simple, module-based pricing
start free. scale as your registry grows.
{TIERS.map((tier, i) => (
))}
need more?{' '}
enterprise plans
{' '}with unlimited modules, sso, soc2, and dedicated infra —{' '}
document.getElementById('cta')?.scrollIntoView({ behavior: 'smooth' })}
style={{
background: 'none', border: 'none', color: 'var(--teal)',
fontFamily: 'var(--fn-mono)', fontSize: 13, cursor: 'pointer', padding: 0,
}}
>
talk to us ↗
);
}
/* ─── Roadmap ─────────────────────────────────────────────────────────────── */
const STATUS_META = {
shipped: { icon: '✅', color: GREEN },
building: { icon: '🔨', color: TEAL },
planned: { icon: '📋', color: BLUE },
exploring: { icon: '💡', color: AMBER },
future: { icon: '🔮', color: PURPLE },
};
const ROADMAP_PHASES = [
{
id: 'foundation', period: 'jun — aug 2026', label: 'foundation',
status: 'building', accentColor: TEAL, active: true,
desc: 'core platform. automated detect → test → promote loop for first customers.',
cta: 'early access opens august 2026',
features: [
{ text: 'auto-detection of new versions', status: 'planned' },
{ text: 'ai test generation (claude sonnet 4.6)', status: 'planned' },
{ text: 'unit tests (.tftest.hcl)', status: 'planned' },
{ text: 'security scans (tfsec + checkov)', status: 'planned' },
{ text: 'k8s job execution (~20s)', status: 'planned' },
{ text: 'jfrog + terraform registry', status: 'planned' },
{ text: 'dashboard — pipeline view', status: 'planned' },
{ text: 'early access — first 50 users', status: 'planned' },
],
},
{
id: 'expand', period: 'sep — nov 2026', label: 'expand',
status: 'planned', accentColor: BLUE,
desc: 'multi-cloud testing, more registries, ci/cd integrations. production-ready.',
features: [
{ text: 'integration tests (real infra)', status: 'planned' },
{ text: 'e2e tests (workflow validation)', status: 'planned' },
{ text: 'azure devops + github actions', status: 'planned' },
{ text: 'azure + aws cloud support', status: 'planned' },
{ text: 'cost-saving gate (fast → cloud)', status: 'planned' },
{ text: 'auto-promotion to release registry', status: 'planned' },
{ text: 'module validation badge (embeddable)', status: 'planned' },
{ text: 'continuous testing (nightly)', status: 'planned' },
],
},
{
id: 'scale', period: 'dec 2026 — feb 2027', label: 'scale',
status: 'planned', accentColor: PURPLE,
desc: 'team features, more ai models, advanced policies. mid-market ready.',
features: [
{ text: 'gitlab + github packages', status: 'planned' },
{ text: 'gcp + kubernetes provider', status: 'planned' },
{ text: 'custom opa/rego policies', status: 'planned' },
{ text: 'ai model selection per module', status: 'planned' },
{ text: 'aws bedrock + google vertex', status: 'planned' },
{ text: 'team management (roles, rbac)', status: 'planned' },
{ text: 'slack / teams notifications', status: 'planned' },
{ text: 'webhook api', status: 'planned' },
],
},
{
id: 'enterprise', period: 'mar — may 2027', label: 'enterprise',
status: 'exploring', accentColor: AMBER,
desc: 'self-hosted option, compliance, sso. ready for enterprise adoption.',
features: [
{ text: 'self-hosted (helm chart)', status: 'exploring' },
{ text: 'sso / saml (azure ad, okta)', status: 'exploring' },
{ text: 'soc2 type i certification', status: 'exploring' },
{ text: 'audit log', status: 'exploring' },
{ text: 'azure marketplace listing', status: 'exploring' },
{ text: 'private registry support', status: 'exploring' },
{ text: 'dedicated support + sla', status: 'exploring' },
],
},
{
id: 'platform', period: 'beyond', label: 'platform',
status: 'future', accentColor: PURPLE,
desc: 'long-term platform vision. where we\'re heading.',
features: [
{ text: 'ai-powered fix suggestions', status: 'future' },
{ text: 'opentofu + pulumi support', status: 'future' },
{ text: 'drift detection', status: 'future' },
{ text: 'module quality leaderboard', status: 'future' },
{ text: '"terracheck certified" program', status: 'future' },
{ text: 'module marketplace', status: 'future' },
{ text: 'vs code extension', status: 'future' },
],
},
];
function RoadmapFeature({ feature }) {
const s = STATUS_META[feature.status];
return (
{s.icon}
{feature.text}
);
}
function RoadmapPhaseCard({ phase, index }) {
const sm = STATUS_META[phase.status];
const isFuture = phase.status === 'future';
const isExploring = phase.status === 'exploring';
return (
{/* Period */}
{phase.period}
{/* Label + status */}
{phase.label}
{sm.icon}
{phase.status}
{/* Description */}
{phase.desc}
{/* Feature list */}
{phase.features.map((f, i) => (
))}
{/* CTA strip */}
{phase.cta && (
⚡ {phase.cta}
)}
);
}
function Roadmap() {
return (
/// roadmap
where we're going
public roadmap. early access opens august 2026 — join the waitlist to shape what gets built first.
{/* Timeline connector strip */}
{ROADMAP_PHASES.map((phase, i) => {
const sm = STATUS_META[phase.status];
const isLast = i === ROADMAP_PHASES.length - 1;
return (
{phase.active ? '●' : sm.icon}
{phase.label}
{!isLast && (
)}
);
})}
{ROADMAP_PHASES.map((phase, i) => (
))}
{/* Legend */}
{Object.entries(STATUS_META).map(([key, val]) => (
{val.icon}
{key}
))}
);
}
/* ─── CTA ────────────────────────────────────────────────────────────────── */
function CTA() {
return (
get early access
stop shipping untested modules
join the waitlist and be first to automate your terraform module testing pipeline.
);
}
/* ─── Footer ─────────────────────────────────────────────────────────────── */
function Footer() {
return (
);
}
/* ─── App ────────────────────────────────────────────────────────────────── */
function App() {
useEffect(() => {
try {
const saved = localStorage.getItem('ttt-theme');
if (saved) document.documentElement.setAttribute('data-theme', saved);
} catch {}
}, []);
return (
);
}
ReactDOM.createRoot(document.getElementById('root')).render( );