/* global React, Icon, Origin, SyncChip, Meter, CB, RoleChip, JobChip, Portrait, Tag, Empty, Seg, Input, useRoute */
// ============================================================
// Screens 1-2: Landing + Character binding
// ============================================================
const { useState: useS12, useEffect: useE12 } = React;
// -------------------- 1. Landing / login --------------------
function ScreenLanding() {
const { go } = useRoute();
return (
Now in open beta
One home for your characters, grinds, static, and FC.
A companion app + Discord bot that ties together what's currently scattered across
spreadsheets and single-purpose sites. We sync the public data; you and your group
track the rest — painlessly.
go('binding')}>
Continue with Discord
We never read your game client — only public data (Lodestone, FFLogs, community item APIs).
Track your grinds
Relics, mounts, achievements — checkable, zone-grouped, satisfying. Auto when we can, manual when we must.
Run your static
Roster, schedule, loot, and prog in one place. Paste an FFLogs link — attendance fills itself.
Bring your FC together
100-row roster, FC-wide stats, and a Discord bot that lives where your group already does.
);
}
// -------------------- 2. Character binding --------------------
function ScreenBinding() {
const { go } = useRoute();
const [step, setStep] = useS12(1); // 1 = find, 2 = verify, 3 = done
const [selected, setSelected] = useS12(null);
const [verifyState, setVerifyState] = useS12('idle'); // idle | checking | error | ok
const [query, setQuery] = useS12('');
const RESULTS = [
{ name: "Aerith Nightsong", world: 'Sargatanas', dc: 'Aether', race: 'Au Ra' },
{ name: 'Aerith Lightwhisper', world: 'Faerie', dc: 'Aether', race: 'Miqo\'te' },
{ name: 'Aerith of the Veil', world: 'Adamantoise', dc: 'Aether', race: 'Viera' },
];
return (
Welcome, jakob
Bind your character
We use the Lodestone to prove ownership — no game client access, no addons.
{['Find character', 'Verify ownership', 'Done'].map((label, i) => {
const n = i + 1;
const state = step > n ? 'done' : step === n ? 'active' : 'idle';
return (
{state === 'done' ? : {n} }
{label}
{n < 3 && n ? 'done' : ''}`}>
}
);
})}
{step === 1 && (
Find your character
Search the Lodestone by character name. World or data center is optional.
{RESULTS.map((r, i) => (
setSelected(i)}>
{r.name}
{r.world} · {r.dc} · {r.race}
{selected === i ? : null}
))}
setStep(2)} style={selected == null ? { opacity: 0.4, cursor: 'not-allowed' } : {}}>
Continue
)}
{step === 2 && (
Verify ownership
Paste the token below into your Lodestone character profile bio, save, then click verify.
You can remove it afterward.
One-time verification token
aether-verify-7Q2F
Copy
Open your Lodestone profile in a new tab.
Click Edit profile and paste the token into your character profile / bio.
Save your profile, then come back and click Verify .
{verifyState === 'checking' && (
Looking for your token on the Lodestone… this can take up to 30 seconds.
)}
{verifyState === 'error' && (
We couldn't find the token yet
Make sure your profile is saved and visible. The Lodestone caches for a few minutes — try again shortly.
)}
setStep(1)}> Back
{verifyState === 'idle' && (
{
setVerifyState('checking');
setTimeout(() => setVerifyState('error'), 2200);
}}>Verify
)}
{verifyState === 'checking' && (
Checking…
)}
{verifyState === 'error' && (
<>
setVerifyState('idle')}>Try again
{ setVerifyState('ok'); setTimeout(() => setStep(3), 200); }}>
Verified
>
)}
)}
{step === 3 && (
You're bound
{RESULTS[selected || 0].name} · {RESULTS[selected || 0].world} is now linked to your account.
You can remove the token from your Lodestone bio whenever you like.
{RESULTS[selected || 0].name}
{RESULTS[selected || 0].world} · {RESULTS[selected || 0].dc} · Lv 100
Initial sync complete
go('dashboard')}>
Go to dashboard
)}
We only read your public Lodestone page. We never touch the game client and never ask for your Square Enix password.
);
}
Object.assign(window, { ScreenLanding, ScreenBinding });