/* global React, ReactDOM, HexGlobe, AggregationDiagram, HexShape, TweaksPanel, TweakSection, TweakSlider, TweakToggle, TweakRadio, TweakColor, TweakText, useTweaks */

const Hex = window.HexShape;

/* ============================================================
   Oblivions.Garden — landing
   Build your Garden of Eden. One hex of real Earth at a time.
   Powered by the TerraForge engine (the codename stays internal —
   public marketing surfaces Oblivions.Garden, engine credit lives
   in the hero attribution + footer).
   ============================================================ */

// Portal — escape ancestor containing blocks for modals. The Nav has
// backdrop-filter: blur, which (per CSS spec) creates a containing block
// for fixed-position descendants — so any `position: fixed` modal rendered
// inside the Nav (e.g., SignInModal hung off SignInControl) resolves its
// `inset: 0` relative to the Nav's bounding box, not the viewport.
// Routing modal content through a body-level portal node sidesteps the
// issue without removing the nav's blur effect.
function Portal({ children }) {
  const [mountNode] = React.useState(() => {
    const el = document.createElement('div');
    el.setAttribute('data-portal-root', '');
    return el;
  });
  React.useEffect(() => {
    document.body.appendChild(mountNode);
    return () => { document.body.removeChild(mountNode); };
  }, [mountNode]);
  return ReactDOM.createPortal(children, mountNode);
}

// useFirebaseAuthUser subscribes to onIdTokenChanged and returns the
// current Firebase User (or null when signed-out / Firebase not yet
// initialized). Single source of truth for "is the user signed in?"
// across all UI components. onIdTokenChanged covers sign-in/out PLUS
// every token refresh (~55min cadence) so cached state stays fresh.
function useFirebaseAuthUser() {
  const [user, setUser] = React.useState(null);
  React.useEffect(() => {
    const fb = (typeof window !== 'undefined') ? window.__firebase : null;
    if (!fb || !fb.onIdTokenChanged || !fb.auth) return;
    const unsub = fb.onIdTokenChanged(fb.auth, (u) => setUser(u || null));
    return () => unsub();
  }, []);
  return user;
}

function humanizeAuthError(code, message) {
  const map = {
    'auth/invalid-email': 'That email address looks invalid.',
    'auth/user-disabled': 'This account is disabled. Contact support if you think this is wrong.',
    'auth/user-not-found': 'No account found for that email. Try Sign up instead.',
    'auth/wrong-password': 'Wrong password — try again or click Reset password.',
    'auth/invalid-credential': 'Email or password is incorrect.',
    'auth/email-already-in-use': 'An account with that email already exists. Try Sign in instead.',
    'auth/weak-password': 'Password must be at least 6 characters.',
    'auth/popup-closed-by-user': 'Sign-in was cancelled.',
    'auth/popup-blocked': 'Your browser blocked the sign-in popup. Allow popups for this site and try again.',
    'auth/unauthorized-domain': 'This domain is not authorized for sign-in. The operator needs to add it in Firebase Console.',
    'auth/too-many-requests': 'Too many failed attempts. Try again in a few minutes or reset your password.',
    'auth/network-request-failed': 'Network error. Check your connection and try again.',
    'auth/operation-not-allowed': 'This sign-in method is not enabled in Firebase Console.',
  };
  if (code && map[code]) return map[code];
  if (message) return message;
  return 'Sign-in failed. Please try again.';
}

// SignInModal — email/password + Google sign-in popup. Firebase's
// authorized-domains list (Firebase Console → Auth → Settings) must
// include the serving hostname or Google rejects with
// auth/unauthorized-domain. See docs/ops/firebase-setup.md §5. Markup
// uses og-* classes/tokens from the Oblivions.Garden design system.
function SignInModal({ open, onClose }) {
  const [mode, setMode] = React.useState('signin');
  const [email, setEmail] = React.useState('');
  const [password, setPassword] = React.useState('');
  const [busy, setBusy] = React.useState(false);
  const [err, setErr] = React.useState('');

  React.useEffect(() => {
    if (open) { setEmail(''); setPassword(''); setErr(''); setBusy(false); }
  }, [open]);

  if (!open) return null;
  const fb = window.__firebase;
  if (!fb) {
    return (
      <Portal>
        <div className="bm-overlay" onClick={onClose}>
          <div className="bm-popup" onClick={e => e.stopPropagation()}>
            <div className="og-panel-header">
              <span className="og-panel-title">SIGN IN</span>
              <button className="bm-close" onClick={onClose} aria-label="Close">×</button>
            </div>
            <div style={{ padding: 24 }}>
              <p className="dim">Firebase did not initialize. Check your network connection and reload the page.</p>
            </div>
          </div>
        </div>
      </Portal>
    );
  }

  const handleEmailSubmit = async (e) => {
    e.preventDefault();
    setBusy(true); setErr('');
    try {
      const fn = mode === 'signin' ? fb.signInWithEmailAndPassword : fb.createUserWithEmailAndPassword;
      await fn(fb.auth, email, password);
      onClose();
    } catch (ex) {
      setErr(humanizeAuthError(ex && ex.code, ex && ex.message));
    } finally {
      setBusy(false);
    }
  };

  const handleGoogle = async () => {
    setBusy(true); setErr('');
    try {
      const provider = new fb.GoogleAuthProvider();
      await fb.signInWithPopup(fb.auth, provider);
      onClose();
    } catch (ex) {
      setErr(humanizeAuthError(ex && ex.code, ex && ex.message));
    } finally {
      setBusy(false);
    }
  };

  const handleReset = async () => {
    if (!email) { setErr('Enter your email first, then click Reset password.'); return; }
    setBusy(true); setErr('');
    try {
      await fb.sendPasswordResetEmail(fb.auth, email);
      setErr('Password reset link sent. Check your inbox.');
    } catch (ex) {
      setErr(humanizeAuthError(ex && ex.code, ex && ex.message));
    } finally {
      setBusy(false);
    }
  };

  const isSignIn = mode === 'signin';
  return (
    <Portal>
      <div className="bm-overlay" onClick={onClose}>
        <div className="bm-popup" onClick={e => e.stopPropagation()}>
          <div className="og-panel-header">
            <span className="og-panel-title">{isSignIn ? 'SIGN IN' : 'CREATE ACCOUNT'}</span>
            <button className="bm-close" onClick={onClose} aria-label="Close">×</button>
          </div>
          <div style={{ padding: 24 }}>
            <button
              type="button"
              className="og-btn"
              onClick={handleGoogle}
              disabled={busy}
              style={{ width: '100%', justifyContent: 'center', padding: '0.7rem' }}
            >
              <svg width="14" height="14" viewBox="0 0 18 18" style={{ marginRight: 8, verticalAlign: 'middle' }}>
                <path fill="#4285F4" d="M17.64 9.2c0-.64-.06-1.25-.16-1.84H9v3.48h4.84a4.13 4.13 0 0 1-1.79 2.71v2.26h2.91c1.7-1.57 2.68-3.88 2.68-6.61z"/>
                <path fill="#34A853" d="M9 18c2.43 0 4.47-.8 5.96-2.18l-2.91-2.26c-.81.54-1.84.86-3.05.86-2.34 0-4.32-1.58-5.03-3.71H.96v2.34A8.99 8.99 0 0 0 9 18z"/>
                <path fill="#FBBC05" d="M3.97 10.71A5.41 5.41 0 0 1 3.68 9c0-.59.1-1.17.29-1.71V4.95H.96A8.97 8.97 0 0 0 0 9c0 1.45.35 2.82.96 4.05l3.01-2.34z"/>
                <path fill="#EA4335" d="M9 3.58c1.32 0 2.5.45 3.44 1.35l2.58-2.58A8.97 8.97 0 0 0 9 0 8.99 8.99 0 0 0 .96 4.95l3.01 2.34C4.68 5.16 6.66 3.58 9 3.58z"/>
              </svg>
              <span>Continue with Google</span>
            </button>

            <div className="mono" style={{ display: 'flex', alignItems: 'center', gap: 12, margin: '20px 0 16px', color: 'var(--og-bone-mute)', fontSize: 11, letterSpacing: '0.1em', textTransform: 'uppercase' }}>
              <div style={{ flex: 1, height: 1, background: 'var(--og-line-strong)' }} />
              <span>or with email</span>
              <div style={{ flex: 1, height: 1, background: 'var(--og-line-strong)' }} />
            </div>

            <form onSubmit={handleEmailSubmit}>
              <div className="mono" style={{ fontSize: 11, letterSpacing: '0.1em', color: 'var(--og-bone-mute)', textTransform: 'uppercase', marginBottom: 6 }}>Email</div>
              <input
                className="wl-input"
                type="email"
                required
                autoFocus
                placeholder="commander@earth-1.og"
                value={email}
                onChange={e => setEmail(e.target.value)}
                style={{ width: '100%', marginBottom: 12 }}
              />
              <div className="mono" style={{ fontSize: 11, letterSpacing: '0.1em', color: 'var(--og-bone-mute)', textTransform: 'uppercase', marginBottom: 6 }}>Password</div>
              <input
                className="wl-input"
                type="password"
                required
                minLength={6}
                placeholder="••••••••"
                value={password}
                onChange={e => setPassword(e.target.value)}
                style={{ width: '100%', marginBottom: 16 }}
              />
              {err && (
                <div className="mono" style={{ fontSize: 12, color: 'var(--og-ember-300)', marginBottom: 12, lineHeight: 1.5 }}>
                  {err}
                </div>
              )}
              <button
                type="submit"
                className="og-btn og-btn-primary"
                disabled={busy}
                style={{ width: '100%', justifyContent: 'center', padding: '0.8rem' }}
              >
                {busy ? '…' : (isSignIn ? 'Sign in →' : 'Create account →')}
              </button>
            </form>

            <div style={{ display: 'flex', justifyContent: 'space-between', marginTop: 16, fontSize: 12 }}>
              <button
                type="button"
                className="dim"
                onClick={() => { setMode(isSignIn ? 'signup' : 'signin'); setErr(''); }}
                style={{ background: 'transparent', border: 'none', cursor: 'pointer', textDecoration: 'underline', padding: 0 }}
              >
                {isSignIn ? 'Need an account? Sign up' : 'Have an account? Sign in'}
              </button>
              {isSignIn && (
                <button
                  type="button"
                  className="dim"
                  onClick={handleReset}
                  style={{ background: 'transparent', border: 'none', cursor: 'pointer', textDecoration: 'underline', padding: 0 }}
                >
                  Reset password
                </button>
              )}
            </div>
          </div>
        </div>
      </div>
    </Portal>
  );
}

// SignInControl — nav-bar button. "Sign in" when signed-out; pill +
// dropdown when signed-in. Sits next to the Wishlist-on-Steam CTA;
// marketing CTA is primary, sign-in is secondary for returning players.
function SignInControl() {
  const user = useFirebaseAuthUser();
  const [modalOpen, setModalOpen] = React.useState(false);
  const [menuOpen, setMenuOpen] = React.useState(false);

  if (!user) {
    return (
      <>
        <button
          type="button"
          className="og-btn"
          onClick={() => setModalOpen(true)}
          style={{ marginRight: 8 }}
        >
          Sign in
        </button>
        <SignInModal open={modalOpen} onClose={() => setModalOpen(false)} />
      </>
    );
  }

  const handleSignOut = async () => {
    setMenuOpen(false);
    try { await window.__firebase.signOut(window.__firebase.auth); } catch (_) { /* swallow */ }
  };

  const label = user.displayName || (user.email ? user.email.split('@')[0] : 'Signed in');
  return (
    <div style={{ position: 'relative', marginRight: 8 }}>
      <button
        type="button"
        className="og-pill og-pill-clickable"
        onClick={() => setMenuOpen(o => !o)}
        style={{ cursor: 'pointer', background: 'transparent' }}
        aria-haspopup="menu"
        aria-expanded={menuOpen}
      >
        <span className="og-dot og-dot-live" />
        <span className="mono" style={{ fontSize: 11, letterSpacing: '0.1em', color: 'var(--og-bone)' }}>
          {label}
        </span>
        <span className="mono dim" style={{ fontSize: 10 }}>▾</span>
      </button>
      {menuOpen && (
        <div
          role="menu"
          style={{
            position: 'absolute', right: 0, top: 'calc(100% + 8px)',
            background: 'var(--og-soil)', border: '1px solid var(--og-line-strong)',
            borderRadius: 6, minWidth: 220,
            boxShadow: '0 12px 32px rgba(0,0,0,0.5)', zIndex: 150,
          }}
        >
          {user.email && (
            <div style={{ padding: '10px 14px', borderBottom: '1px solid var(--og-line)', fontSize: 11, color: 'var(--og-bone-mute)' }}>
              <div className="mono" style={{ letterSpacing: '0.08em', textTransform: 'uppercase', marginBottom: 2, fontSize: 9 }}>signed in as</div>
              <div className="bright" style={{ fontSize: 12, wordBreak: 'break-all' }}>{user.email}</div>
            </div>
          )}
          <button
            type="button"
            className="mono"
            onClick={handleSignOut}
            role="menuitem"
            style={{
              display: 'block', width: '100%', textAlign: 'left',
              padding: '10px 14px', background: 'transparent', border: 'none',
              color: 'var(--og-bone)', fontSize: 12, letterSpacing: '0.08em',
              textTransform: 'uppercase', cursor: 'pointer',
            }}
          >
            Sign out
          </button>
        </div>
      )}
    </div>
  );
}

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "lead": "ember",
  "headlineMode": "grow",
  "headlineCycle": false,
  "headlineCycleSec": 5,
  "grain": 3,
  "showSettlements": true,
  "settlementStage": "city",
  "shardLabel": "SHARD · EARTH-1"
}/*EDITMODE-END*/;

// Earth-builder framing: build your own Earth, your own Eden.
const HEADLINES = {
  build:  { lead: 'Build your ',         accent: 'Eden',     tail: '.',          sub: 'On a 1:1 reconstruction of real Earth.' },
  grow:   { lead: 'A city that ',        accent: 'grows',    tail: ' with you.',  sub: 'From a single hex to a continental economy.' },
  shape:  { lead: 'Shape ',              accent: 'real',     tail: ' Earth.',     sub: 'Real terrain. Real cities. Real geography.' },
  yours:  { lead: 'Your own ',           accent: 'planet',   tail: '.',           sub: 'Build it the way you always wanted.' },
};

function NavGlyph({ name }) {
  const c = { width: 14, height: 14, viewBox: '0 0 14 14', fill: 'none', stroke: 'currentColor', strokeWidth: 1.2, strokeLinecap: 'round', strokeLinejoin: 'round' };
  const g = {
    pillars:    (<svg {...c}><polygon points="7,1.5 11.5,4 11.5,10 7,12.5 2.5,10 2.5,4" /><circle cx="7" cy="7" r="1.4" fill="currentColor" stroke="none" /></svg>),
    mechanics:  (<svg {...c}><circle cx="7" cy="7" r="3" /><path d="M7 1.5v1.6M7 10.9v1.6M1.5 7h1.6M10.9 7h1.6" /></svg>),
    economy:    (<svg {...c}><path d="M2 12V8M5.5 12V5M9 12V9M12.5 12V3" /><path d="M2 12h11" /></svg>),
    world:      (<svg {...c}><circle cx="7" cy="7" r="5.2" /><path d="M2 7h10" /><ellipse cx="7" cy="7" rx="2.4" ry="5.2" /></svg>),
    pricing:    (<svg {...c}><circle cx="7" cy="7" r="5.2" /><path d="M5 5.5h3.2c.7 0 1.3.6 1.3 1.3S8.9 8.1 8.2 8.1H5L9.5 8.1" /></svg>),

    roadmap:    (<svg {...c}><path d="M1.5 7h11" /><circle cx="3" cy="7" r="1.1" fill="currentColor" /><circle cx="7" cy="7" r="1.1" fill="currentColor" /><circle cx="11" cy="7" r="1.1" fill="currentColor" /></svg>),
  };
  return g[name] || null;
}

function Logo({ size = 26 }) {
  const r = size / 2 - 1;
  return (
    <svg width={size} height={size} viewBox={`-${size/2} -${size/2} ${size} ${size}`}>
      <defs>
        <radialGradient id={`bloom-${size}`} cx="0" cy="0" r={r * 0.9} gradientUnits="userSpaceOnUse">
          <stop offset="0%" stopColor="#6cd49a" stopOpacity="0.6" />
          <stop offset="100%" stopColor="#6cd49a" stopOpacity="0" />
        </radialGradient>
        <linearGradient id={`hexFill-${size}`} x1="0" y1={-r} x2="0" y2={r}>
          <stop offset="0%" stopColor="#243029" />
          <stop offset="100%" stopColor="#0a0f0d" />
        </linearGradient>
      </defs>
      <circle cx="0" cy="0" r={r} fill={`url(#bloom-${size})`} />
      <Hex cx={0} cy={0} r={r} fill={`url(#hexFill-${size})`} stroke="#2bb673" sw={1.1} />
      <Hex cx={0} cy={0} r={r * 0.5} fill="none" stroke="#6cd49a" sw={1.1} />
      <circle cx="0" cy="0" r={r * 0.1} fill="#ecc781" />
    </svg>
  );
}

function Nav({ tweaks, setTweak }) {
  const links = [
    { id: 'manifesto', label: 'The loop',     glyph: 'pillars' },
    { id: 'mechanics', label: 'Mechanics',    glyph: 'mechanics' },
    { id: 'economy',   label: 'Economy',      glyph: 'economy' },
    { id: 'world',     label: 'Living world', glyph: 'world' },
    { id: 'roadmap',   label: 'Roadmap',      glyph: 'roadmap' },
    { id: 'pricing',   label: 'Pricing',      glyph: 'pricing' },
  ];
  return (
    <nav className="nav">
      <div className="nav-inner">
        <a href="#top" className="nav-logo">
          <Logo size={26} />
          <span>Oblivions<span className="dot">.</span>Garden</span>
        </a>
        <div className="nav-links">
          {links.map(l => (
            <a key={l.id} href={`#${l.id}`} className="nav-link">
              <NavGlyph name={l.glyph} />
              <span>{l.label}</span>
            </a>
          ))}
        </div>
        <div style={{ display: 'flex', alignItems: 'center' }}>
          <SignInControl />
          <a href="#wishlist" className="og-btn og-btn-primary" data-accent={tweaks.lead}>Wishlist on Steam →</a>
        </div>
      </div>
    </nav>
  );
}

function Hero({ tweaks }) {
  // Dynamic BUILD pill — pulls from /version.json via build-info.jsx so the
  // hero stays in lock-step with deploys. Falls back to "v… · loading" pre-fetch.
  const buildInfo = (typeof window !== 'undefined' && window.useBuildInfo) ? window.useBuildInfo() : null;
  const buildLabel = buildInfo?.major ? `BUILD v${buildInfo.major} · shipped` : 'BUILD v… · loading';

  const order = ['build', 'grow', 'shape', 'yours'];
  const baseIdx = Math.max(0, order.indexOf(tweaks.headlineMode));
  const [cycleIdx, setCycleIdx] = React.useState(baseIdx);
  const [fadeKey, setFadeKey] = React.useState(0);

  React.useEffect(() => { setCycleIdx(baseIdx); setFadeKey(k => k + 1); }, [baseIdx]);
  React.useEffect(() => {
    if (!tweaks.headlineCycle) return;
    const ms = Math.max(1500, (tweaks.headlineCycleSec || 5) * 1000);
    const id = setInterval(() => {
      setCycleIdx(i => (i + 1) % order.length);
      setFadeKey(k => k + 1);
    }, ms);
    return () => clearInterval(id);
  }, [tweaks.headlineCycle, tweaks.headlineCycleSec]);

  const activeKey = tweaks.headlineCycle ? order[cycleIdx] : tweaks.headlineMode;
  const h = HEADLINES[activeKey] || HEADLINES.build;
  const accentClass = tweaks.lead === 'bloom' ? 'serif-bloom' : 'serif-ember';

  return (
    <section className="hero" id="top">
      <div className="hero-bg" />
      <div className="wrap" style={{ position: 'relative' }}>
        <div className="hero-grid">
          <div>
            <div className="hero-status">
              <span className="og-pill">
                <span className="og-dot og-dot-live" />
                <span>{tweaks.shardLabel} · Alpha Q4 2027</span>
              </span>
              <span className="og-pill" style={{ borderColor: 'var(--og-line-ember)' }}>
                <span className="og-dot og-dot-ember" />
                <span style={{ color: 'var(--og-ember-300)' }}>{buildLabel}</span>
              </span>
            </div>

            <h1 className="display text-6xl mt-32" style={{ position: 'relative' }}>
              <span className="headline-bg-glow" aria-hidden="true" />
              {h.lead}<span key={fadeKey} className={`serif headline-verb ${accentClass}`}>{h.accent}</span>{h.tail}<br />
              <span style={{ color: 'var(--og-bone-mute)' }}>{h.sub}</span>
            </h1>

            <div className="verb-rhythm mt-24" aria-label="Build. Grow. Shape. Yours.">
              {['Build', 'Grow', 'Shape', 'Yours'].map((v, i) => {
                const liveKey = ['build', 'grow', 'shape', 'yours'][i];
                const live = liveKey === activeKey;
                return (
                  <React.Fragment key={v}>
                    <span className={`verb-rhythm-item serif ${accentClass} ${live ? 'is-live' : ''}`}>{v}{i === 3 ? '.' : ''}</span>
                    {i < 3 && <span className="verb-rhythm-sep" aria-hidden="true">·</span>}
                  </React.Fragment>
                );
              })}
            </div>

            <p className="text-xl mt-32" style={{ maxWidth: 540, color: 'var(--og-bone-dim)' }}>
              A persistent multi-shard city-builder on a 1:1 reconstruction of <span className="bright">real Earth</span>. Claim hex regions anywhere on the globe. Build a hierarchical economy from a single parcel up to a continent. Plant your <span className="bloom">Garden of Eden</span> — and watch it grow into a planet.
            </p>

            <div className="flex gap-12 mt-32" style={{ flexWrap: 'wrap' }}>
              <a href="#wishlist" className="og-btn og-btn-primary og-btn-lg" data-accent={tweaks.lead}>Wishlist on Steam →</a>
              <a href="#mechanics" className="og-btn og-btn-lg">See how it plays</a>
            </div>

            <div className="hero-stats mt-48">
              <div className="hero-stat"><div className="num">32,194</div><div className="lbl">Hexes / shard</div></div>
              <div className="hero-stat"><div className="num">1:1</div><div className="lbl">Real-Earth scale</div></div>
              <div className="hero-stat"><div className="num">24/7</div><div className="lbl">Server tick</div></div>
            </div>
          </div>

          <div className="hud">
            <div className="hud-c1"></div><div className="hud-c2"></div>
            <div className="og-panel-header" style={{ margin: '-24px -24px 18px' }}>
              <div className="og-panel-title">SHARD VIEWER · LIVE FEED</div>
              <span className="og-panel-badge"><span className="og-dot og-dot-live" />ONLINE</span>
            </div>
            <div style={{ aspectRatio: '1' }}>
              <HexGlobe size={420} accent={tweaks.lead} />
            </div>
            <div className="og-meta-row" style={{ marginTop: 18 }}>
              <span className="og-meta-key">Active hexes</span><span className="og-meta-val ember">8 · player</span>
            </div>
            <div className="og-meta-row">
              <span className="og-meta-key">AI-evolving</span><span className="og-meta-val bloom">26 · sage</span>
            </div>
            <div className="og-meta-row">
              <span className="og-meta-key">Trade routes</span><span className="og-meta-val cyan">5 active</span>
            </div>

            <a href="#trailer" className="hero-trailer-cta" onClick={(e) => { e.preventDefault(); }}>
              <span className="hero-trailer-play" aria-hidden="true">
                <svg width="14" height="14" viewBox="0 0 14 14" fill="none"><polygon points="4,2.5 4,11.5 11,7" fill="currentColor" /></svg>
              </span>
              <span className="hero-trailer-text">
                <span className="mono hero-trailer-eyebrow">REVEAL TRAILER</span>
                <span className="hero-trailer-when">Drops Q3 2027 — notify me</span>
              </span>
            </a>

            <div className="hero-engine-attribution">
              <span className="og-dot" style={{ background: 'var(--og-bloom-300)', boxShadow: '0 0 6px var(--og-bloom-glow)' }} />
              <div>
                <div className="mono" style={{ fontSize: 9, letterSpacing: '0.18em', color: 'var(--og-bone-mute)' }}>POWERED BY</div>
                <div className="mono" style={{ fontSize: 11, letterSpacing: '0.14em', color: 'var(--og-bone)', marginTop: 2 }}>TerraForge · World Server Core</div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </section>
  );
}

function SocialProof() {
  const channels = [
    { name: 'Discord',    value: '4,820',  unit: 'devs in chat',     glyph: 'discord',  href: '#' },
    { name: 'Newsletter', value: '8,200',  unit: 'on the list',      glyph: 'mail',     href: '#wishlist' },
    { name: 'Bluesky',    value: '12.4k',  unit: 'following devlogs', glyph: 'bluesky', href: '#' },
    { name: 'GitHub',     value: 'public', unit: 'design spec + RFCs', glyph: 'github', href: '#' },
  ];
  const press = [
    { tier: 'COVERAGE', name: 'PCGamer',           note: '"The most ambitious city-builder in years"' },
    { tier: 'COVERAGE', name: 'Rock Paper Shotgun', note: '"SimCity meets the actual planet"' },
    { tier: 'FEATURED', name: 'IndieDB',           note: 'Game of the week · May 2026' },
  ];
  const Glyph = ({ name }) => {
    const c = { width: 16, height: 16, viewBox: '0 0 16 16', fill: 'currentColor' };
    if (name === 'discord')  return <svg {...c}><path d="M13.5 3.2A11.4 11.4 0 0 0 10.7 2.4l-.13.27a10.6 10.6 0 0 0-3.14 0L7.3 2.4A11.4 11.4 0 0 0 4.5 3.2C2.4 6.4 1.85 9.5 2.13 12.55c1.18.88 2.32 1.42 3.45 1.78l.7-1.06c-.6-.22-1.18-.5-1.74-.84.14-.1.28-.21.42-.32 3.32 1.55 6.92 1.55 10.2 0 .14.11.28.22.42.32-.56.34-1.14.62-1.74.84l.7 1.06c1.13-.36 2.27-.9 3.45-1.78.33-3.5-.56-6.6-2.39-9.35ZM6.6 10.62c-.79 0-1.43-.74-1.43-1.65 0-.92.63-1.65 1.43-1.65.8 0 1.44.74 1.43 1.65 0 .91-.63 1.65-1.43 1.65Zm4.8 0c-.79 0-1.43-.74-1.43-1.65 0-.92.63-1.65 1.43-1.65.8 0 1.44.74 1.43 1.65 0 .91-.63 1.65-1.43 1.65Z"/></svg>;
    if (name === 'mail')     return <svg {...c} fill="none" stroke="currentColor" strokeWidth="1.4"><rect x="2" y="3.5" width="12" height="9" rx="1"/><path d="M2.5 4.5l5.5 4 5.5-4"/></svg>;
    if (name === 'bluesky')  return <svg {...c}><path d="M8 6.4C7.07 4.4 4.6 1.6 3.2 1.4 1.7 1.18 1.5 2.7 1.5 3.6c0 .9.43 4.45.7 4.9.3.5.85.6 1.78.5-2 .27-3.7.83-1 3.5 2.7 2.65 3.7-.66 4.02-1.6.32.94 1.32 4.25 4.02 1.6 2.7-2.67 1-3.23-1-3.5.93.1 1.48 0 1.78-.5.27-.45.7-4 .7-4.9 0-.9-.2-2.42-1.7-2.2-1.4.2-3.87 3-4.8 5Z"/></svg>;
    if (name === 'github')   return <svg {...c}><path d="M8 1.5a6.5 6.5 0 0 0-2.05 12.66c.32.06.44-.14.44-.31v-1.13c-1.8.4-2.18-.86-2.18-.86-.3-.74-.72-.94-.72-.94-.6-.4.04-.4.04-.4.65.05 1 .67 1 .67.58 1 1.52.7 1.9.54.06-.42.23-.7.41-.86-1.43-.16-2.94-.71-2.94-3.18 0-.7.25-1.28.66-1.73-.07-.16-.29-.81.06-1.7 0 0 .54-.17 1.78.66a6.18 6.18 0 0 1 3.24 0c1.24-.83 1.78-.66 1.78-.66.36.89.13 1.54.07 1.7.4.45.66 1.03.66 1.73 0 2.48-1.51 3.02-2.95 3.18.23.2.44.59.44 1.2v1.78c0 .17.12.38.45.31A6.5 6.5 0 0 0 8 1.5Z"/></svg>;
    return null;
  };
  return (
    <section className="social-proof">
      <div className="wrap">
        <div className="social-proof-grid">
          <div className="social-proof-channels">
            <div className="eyebrow" style={{ marginBottom: 18 }}>The community</div>
            <div className="social-channel-row">
              {channels.map((c) => (
                <a key={c.name} href={c.href} onClick={(e) => c.href === '#' && e.preventDefault()} className="social-channel">
                  <span className="social-channel-glyph"><Glyph name={c.glyph} /></span>
                  <div className="social-channel-text">
                    <div className="social-channel-value mono">{c.value}</div>
                    <div className="social-channel-name">{c.name} <span className="dim">— {c.unit}</span></div>
                  </div>
                </a>
              ))}
            </div>
          </div>
          <div className="social-press">
            <div className="eyebrow" style={{ marginBottom: 18 }}>Press</div>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
              {press.map((p, i) => (
                <div key={i} className="social-press-row">
                  <span className="social-press-tier mono">{p.tier}</span>
                  <span className="social-press-name">{p.name}</span>
                  <span className="social-press-note dim">{p.note}</span>
                </div>
              ))}
            </div>
          </div>
        </div>
      </div>
    </section>
  );
}

function FloatingCTA({ tweaks }) {
  const [show, setShow] = React.useState(false);
  React.useEffect(() => {
    const onScroll = () => {
      const hero = document.getElementById('top');
      if (!hero) return;
      setShow(window.scrollY > hero.offsetHeight - 80);
    };
    window.addEventListener('scroll', onScroll, { passive: true });
    onScroll();
    return () => window.removeEventListener('scroll', onScroll);
  }, []);
  return (
    <a
      href="#wishlist"
      className={`floating-cta og-btn og-btn-primary ${show ? 'is-visible' : ''}`}
      data-accent={tweaks.lead}
      aria-hidden={!show}
    >
      <span className="floating-cta-star" aria-hidden="true">★</span>
      <span>Wishlist on Steam</span>
      <span className="floating-cta-arrow">→</span>
    </a>
  );
}

function Manifesto({ tweaks }) {
  const accentClass = tweaks.lead === 'bloom' ? 'serif-bloom' : 'serif-ember';
  const beats = [
    { verb: 'Build',  body: 'Place a single building on real lat/long. Footprints come from OpenStreetMap. The hex is your canvas — the world is the reference.', meta: ['LAT/LONG', 'OSM FOOTPRINTS'] },
    { verb: 'Grow',   body: 'Watch a hex become a district. A district becomes a city. Eight aggregation layers roll up while you play — and keep rolling when you don\'t.', meta: ['8 LAYERS', 'AGGREGATING'] },
    { verb: 'Shape',  body: 'Carve roads, ports, rail across real geography. Distance × infrastructure sets the cost. The Andes will charge you for it.', meta: ['REAL TERRAIN', 'TRADE-ROUTES'] },
    { verb: 'Yours',  body: 'Buy once, play solo forever. Mods, self-hosted shards, local saves. The subscription is opt-in — only if you want the world to keep ticking.', meta: ['ONE-TIME', 'OPT-IN MMO'] },
  ];
  return (
    <section id="manifesto" className="section" style={{ borderTop: '1px solid var(--og-line)', borderBottom: '1px solid var(--og-line)' }}>
      <div className="wrap">
        <div className="eyebrow">The four beats</div>
        <h2 className="display text-4xl mt-16" style={{ maxWidth: 720 }}>
          Four verbs. <span className="dim">One loop you'll keep coming back to.</span>
        </h2>

        <div className="manifesto-grid mt-64">
          {beats.map((b, i) => (
            <div key={b.verb} className="manifesto-beat">
              <div className="manifesto-num mono">0{i + 1}</div>
              <div className={`manifesto-verb display serif ${accentClass}`}>{b.verb}.</div>
              <p className="manifesto-body text-md">{b.body}</p>
              <div className="manifesto-meta mono">
                {b.meta.map((m, j) => (
                  <React.Fragment key={m}>
                    <span>{m}</span>
                    {j < b.meta.length - 1 && <span className="manifesto-meta-sep">·</span>}
                  </React.Fragment>
                ))}
              </div>
            </div>
          ))}
        </div>
      </div>
    </section>
  );
}

function Pitch() {
  return (
    <section id="pillars" className="section section-line" style={{ background: 'rgba(10,15,13,0.5)', borderBottom: '1px solid var(--og-line)' }}>
      <div className="wrap">
        <div style={{ maxWidth: 880 }}>
          <div className="eyebrow">The pitch</div>
          <h2 className="display text-4xl mt-16">
            <span className="dim">Other city-builders give you a blank tile.</span><br />
            We give you the <span className="serif serif-accent">whole Earth</span> to plant on.
          </h2>
          <p className="text-lg mt-32 dim" style={{ maxWidth: 720 }}>
            Real terrain. Real geography. Real building footprints. Pick any hex on the planet and start your own Eden — fishing village, megacity, or anything in between.
          </p>
        </div>

        <div className="grid mt-64" style={{ gridTemplateColumns: 'repeat(4, 1fr)', gap: 16 }}>
          {[
            { color: 'var(--og-ember-300)',   tag: 'PILLAR 01', title: 'Hierarchical economy', body: 'Eight aggregation layers — building → block → district → city → metro → region → country → continent. Local production rolls up; price signals propagate down.' },
            { color: 'var(--og-rite-violet)', tag: 'PILLAR 02', title: 'The hex empire loop', body: 'Claim → tend → link → trade. Extend a road across the boundary; a TradeRoute opens with the neighbor. Free placement on real terrain.' },
            { color: 'var(--og-bloom-300)',   tag: 'PILLAR 03', title: 'Real Earth canvas',   body: 'Cesium terrain, OpenStreetMap building footprints loaded per hex, real geography. NYC is ~22 hexes. The world you already know.' },
            { color: 'var(--og-rite-cyan)',   tag: 'PILLAR 04', title: 'Living world server', body: 'A separate AI worker grows unoccupied hexes in game-time. Fishing villages become port towns while you sleep. Log in to a world that moved on.' },
          ].map((p, i) => (
            <div key={i} className="pillar">
              <div className="pillar-head" style={{ borderBottomColor: `${p.color}30` }}>
                <span style={{ fontFamily: 'var(--og-font-mono)', fontSize: 10, letterSpacing: '0.16em', color: p.color, fontWeight: 600 }}>{p.tag}</span>
                <span className="og-dot" style={{ background: p.color, boxShadow: `0 0 6px ${p.color}` }} />
              </div>
              <div style={{ padding: 24 }}>
                <div className="display text-xl">{p.title}</div>
                <p className="text-sm dim mt-16" style={{ lineHeight: 1.65 }}>{p.body}</p>
              </div>
            </div>
          ))}
        </div>
      </div>
    </section>
  );
}

function Mechanics() {
  const loop = [
    { n: '01', title: 'Pick your spot.',     body: 'Pan a real Earth globe and drop in anywhere — a Pacific island, the Sahel, Tokyo Bay. Real terrain is already there. Real OSM footprints are already there. You arrive somewhere, not nowhere.' },
    { n: '02', title: 'Place your first building.', body: 'Buildings sit on real lat/long, not a snap grid. A district forms as you fill the hex; service coverage and traffic emerge from what you actually built, not what you zoned.' },
    { n: '03', title: 'Watch the numbers add up.', body: 'Buildings roll into districts. Districts roll into a city. Your city joins a metro market and your metro joins a region. Eight layers, all live, all visible on the same globe.' },
    { n: '04', title: 'Extend a road. Trade opens.', body: 'Push a road segment across the hex boundary and a trade route exists with your neighbor. Distance and infrastructure set the price. The Andes will charge you for it.' },
    { n: '05', title: 'Log out. World keeps ticking.', body: 'One real minute is one game day, by default. Your city accrues. AI civilizations on neighboring hexes grow on a slow tick. Come back tomorrow to a corridor that didn\'t exist last night.' },
  ];

  const mechs = [
    { title: 'Build where you want',  body: 'Drop your warehouse on the harbor. Sprawl your factory across two hexes. The grid is for the simulation — your buildings sit on real lat/long, the way they would on a map.', meta: ['real-world coords', 'OSM footprints', 'no snap-to-grid'] },
    { title: 'Real geography matters', body: 'Oil in Texas. Ore in the Andes. Lithium in Atacama. What you can produce is what the actual continent under your hex can give you. Trade the rest.', meta: ['~30 resources at launch', '3–4 chain depth', 'modder-extendable'] },
    { title: 'A planet, not a tile',   body: 'Active hexes simulate every building. Distant hexes summarize — population, wealth, output. Walk a citizen near and the detail loads in. The whole Earth fits because most of it is sleeping.', meta: ['~32k hexes / shard', '100–1k active', 'lazy backfill'] },
    { title: 'Solo, co-op, MMO — same world', body: 'Single-player runs locally. A friend joins your shard. Hosted shards run NA / EU. Same save format. Same engine. Move your city between shards in V2.', meta: ['NA / EU shards', 'self-host first-class', 'fresh-start servers'] },
  ];

  return (
    <section id="mechanics" className="section">
      <div className="wrap">
        <div className="eyebrow">How it plays</div>
        <h2 className="display text-4xl mt-16" style={{ maxWidth: 880 }}>
          The core loop: <span className="serif serif-accent">claim, build, link, trade.</span><br />
          <span className="dim">Then watch it grow without you.</span>
        </h2>

        <div className="grid mt-64" style={{ gridTemplateColumns: 'minmax(0, 1.05fr) minmax(0, 1fr)', gap: 56 }}>
          <div>
            <div className="og-panel-header" style={{ borderRadius: '6px 6px 0 0', border: '1px solid var(--og-line)' }}>
              <span className="og-panel-title">Core loop · 5 steps</span>
              <span className="og-panel-badge">PER GAME-DAY</span>
            </div>
            <div style={{ background: 'var(--og-soil)', border: '1px solid var(--og-line)', borderTop: 0, borderRadius: '0 0 6px 6px', padding: '0 28px' }}>
              {loop.map((s, i) => (
                <div key={i} className="loop-step">
                  <div className="loop-num">{s.n}</div>
                  <div>
                    <div style={{ fontSize: 17, fontWeight: 500, color: 'var(--og-bone)' }}>{s.title}</div>
                    <p className="text-sm mt-8 dim" style={{ lineHeight: 1.65 }}>{s.body}</p>
                  </div>
                </div>
              ))}
            </div>
          </div>

          <div style={{ display: 'grid', gridTemplateColumns: '1fr', gap: 16 }}>
            {mechs.map((m, i) => (
              <div key={i} className="mech">
                <div className="mech-num">MECH {String(i + 1).padStart(2, '0')}</div>
                <div className="display text-lg" style={{ paddingRight: 100 }}>{m.title}</div>
                <p className="text-sm dim mt-12" style={{ lineHeight: 1.65 }}>{m.body}</p>
                <div className="flex gap-8 mt-16" style={{ flexWrap: 'wrap' }}>
                  {m.meta.map((t, j) => (
                    <span key={j} className="og-tag">{t}</span>
                  ))}
                </div>
              </div>
            ))}
          </div>
        </div>
      </div>
    </section>
  );
}

function EconomySection() {
  return (
    <section id="economy" className="section section-line" style={{ background: 'rgba(10,15,13,0.5)', borderBottom: '1px solid var(--og-line)' }}>
      <div className="wrap">
        <div style={{ maxWidth: 760 }}>
          <div className="eyebrow">The economy</div>
          <h2 className="display text-4xl mt-16">
            Eight layers. <span className="serif serif-accent">One ledger.</span><br />
            <span className="dim">From a single building to a continent — every transaction rolls up.</span>
          </h2>
          <p className="text-lg dim mt-32" style={{ maxWidth: 680 }}>
            Hover a layer to inspect it. Aggregation runs every 10 ticks; downward price signals propagate within 1–N ticks. Player-active hexes simulate full detail; dormant hexes tick statistically. That's what makes a 1:1 Earth tractable on a single server.
          </p>
        </div>
        <div className="mt-64">
          <AggregationDiagram />
        </div>
      </div>
    </section>
  );
}

function SettlementEvolution({ stage, onStage }) {
  const stages = [
    { id: 'town',       label: 'Town',       res: 'RES 7',  pop: '1.4k',  credits: '18k',  body: 'Fishing village or farming hamlet. Single market, two production chains, a handful of road segments. Lat/long-anchored buildings on real OSM footprint.' },
    { id: 'city',       label: 'City',       res: 'RES 6',  pop: '92.3k', credits: '2.8M', body: 'The player-claim unit. Downtown, mid-rise belts, port, rail-side industrial. Aggregates 7 child Res-7 districts; rolls up into a Res-5 metro market.' },
    { id: 'metropolis', label: 'Metropolis', res: 'RES 5',  pop: '640k',  credits: '19M',  body: 'Aggregate of seven cities. CBD towers, ring-highways, deepwater port, intl. airport. Statistical at the edges; full-detail at the player\'s active core.' },
  ];
  const cur = stages.find(s => s.id === stage) || stages[1];
  return (
    <section className="section section-line">
      <div className="wrap">
        <div className="eyebrow">Settlement evolution</div>
        <h2 className="display text-4xl mt-16" style={{ maxWidth: 760 }}>
          From frontier hex to <span className="serif serif-ember">capital metropolis</span>.<br />
          <span className="dim">Same backend. More density. More routes.</span>
        </h2>

        <div className="flex gap-8 mt-32" style={{ flexWrap: 'wrap' }}>
          {stages.map(s => (
            <button
              key={s.id}
              onClick={() => onStage(s.id)}
              className="og-btn"
              style={{
                fontFamily: 'var(--og-font-mono)',
                fontSize: 11, letterSpacing: '0.14em', textTransform: 'uppercase',
                padding: '0.55rem 0.95rem',
                color: stage === s.id ? 'var(--og-bone)' : 'var(--og-bone-mute)',
                borderColor: stage === s.id ? 'var(--og-accent)' : 'var(--og-line-strong)',
                background: stage === s.id ? 'var(--og-accent-tint)' : 'transparent',
              }}
            >
              {s.res} · {s.label}
            </button>
          ))}
        </div>

        <div className="og-card mt-32" style={{ overflow: 'hidden' }}>
          <div className="og-panel-header">
            <span className="og-panel-title">{cur.res} · {cur.label} · SHARD VIEWER</span>
            <span className="og-panel-badge"><span className="og-dot og-dot-live" />RENDER</span>
          </div>
          <div style={{ display: 'grid', gridTemplateColumns: 'minmax(0, 2.2fr) minmax(0, 1fr)', gap: 0 }}>
            <div style={{ background: 'var(--og-void)', borderRight: '1px solid var(--og-line)', position: 'relative', minHeight: 480 }}>
              {window.__SETTLEMENT_SVGS__ ? (
                <div style={{ display: 'block', width: '100%' }}
                     dangerouslySetInnerHTML={{ __html: window.__SETTLEMENT_SVGS__[cur.id] }} />
              ) : (
                <img src={`assets/${cur.id}.svg`} alt={`${cur.label} settlement`} style={{ display: 'block', width: '100%', height: 'auto' }} />
              )}
            </div>
            <div style={{ padding: 28, display: 'flex', flexDirection: 'column', gap: 18 }}>
              <div>
                <div style={{ fontFamily: 'var(--og-font-mono)', fontSize: 10, letterSpacing: '0.18em', color: 'var(--og-ember-300)', textTransform: 'uppercase', fontWeight: 600 }}>{cur.res}</div>
                <div className="display text-3xl mt-8">{cur.label}</div>
              </div>
              <p className="text-md dim" style={{ lineHeight: 1.65 }}>{cur.body}</p>
              <div>
                <div className="og-meta-row"><span className="og-meta-key">Population</span><span className="og-meta-val">{cur.pop}</span></div>
                <div className="og-meta-row"><span className="og-meta-key">Credits / tick</span><span className="og-meta-val ember">¢ {cur.credits}</span></div>
                <div className="og-meta-row"><span className="og-meta-key">Sim mode</span><span className="og-meta-val">{cur.id === 'metropolis' ? 'mixed' : 'full detail'}</span></div>
                <div className="og-meta-row"><span className="og-meta-key">Aggregates to</span><span className="og-meta-val">{cur.id === 'town' ? 'res 6 city' : cur.id === 'city' ? 'res 5 metro' : 'res 4 region'}</span></div>
              </div>
              <div style={{ fontFamily: 'var(--og-font-mono)', fontSize: 10, color: 'var(--og-bone-soft)', letterSpacing: '0.12em', textTransform: 'uppercase' }}>
                ↳ same engine. just more buildings.
              </div>
            </div>
          </div>
        </div>
      </div>
    </section>
  );
}

function LivingWorld() {
  return (
    <section id="world" className="section section-line">
      <div className="wrap">
        <div style={{ display: 'grid', gridTemplateColumns: 'minmax(0, 1fr) minmax(0, 1fr)', gap: 64, alignItems: 'start' }}>
          <div>
            <div className="eyebrow">A living world</div>
            <h2 className="display text-4xl mt-16">
              The world should feel <span className="serif serif-accent">alive</span> <span className="dim">when you log in.</span>
            </h2>
            <p className="text-lg mt-32 dim">
              A separate AI worker iterates dormant hexes on a slow tick — fishing villages become port towns, frontier roads form trade networks, market towns form around your nearest port. All in game-time. Even when no player is in the region.
            </p>
            <div className="mt-32" style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14 }}>
              {[
                { glyph: '◢', name: 'Coastal + temperate', body: 'fishing village → port town → coastal city' },
                { glyph: '◤', name: 'Riverine + fertile',   body: 'farming hamlet → market town → ag city' },
                { glyph: '◣', name: 'Mountain + minerals',  body: 'mining outpost → industrial town' },
                { glyph: '◥', name: 'Arctic / tundra',      body: 'frontier outpost · low growth' },
              ].map((b, i) => (
                <div key={i} style={{ padding: 16, background: 'var(--og-soil)', border: '1px solid var(--og-line)', borderRadius: 6 }}>
                  <div style={{ fontFamily: 'var(--og-font-mono)', color: 'var(--og-ember-300)', fontSize: 18 }}>{b.glyph}</div>
                  <div className="bright mt-8" style={{ fontWeight: 500, fontSize: 14 }}>{b.name}</div>
                  <div className="dim mt-8" style={{ fontSize: 12, lineHeight: 1.5 }}>{b.body}</div>
                </div>
              ))}
            </div>
          </div>

          <div className="og-card og-card-glow">
            <div className="og-panel-header">
              <span className="og-panel-title">AI evolution worker · tick log</span>
              <span className="og-panel-badge"><span className="og-dot og-dot-live" />RUNNING</span>
            </div>
            <div style={{ padding: 20, fontFamily: 'var(--og-font-mono)', fontSize: 12, lineHeight: 1.95 }}>
              {[
                ['T+0042', 'spawn', 'fishing_village @ res6/8a2a1...d3f', 'ember'],
                ['T+0143', 'grow',  'pop 240 → 590 · port_town', 'cyan'],
                ['T+0309', 'trade', 'wheat ↔ tools route opened', 'bloom'],
                ['T+0411', 'price', 'iron +14% propagated res4 → res6', 'ember'],
                ['T+0578', 'spawn', 'mining_outpost @ res6/8b41c...91e', 'ember'],
                ['T+0612', 'event', 'drought · ag_yield -22% · 14 hexes', 'rose'],
                ['T+0744', 'grow',  'pop 12.4k · district promoted res7', 'cyan'],
                ['T+0901', 'route', 'rail link queued · 3 hexes', 'bloom'],
                ['T+1020', 'price', 'global iron +8% · res2 aggregate',  'ember'],
              ].map(([t, kind, msg, color], i) => (
                <div key={i} style={{ display: 'grid', gridTemplateColumns: '70px 64px 1fr', gap: 14, padding: '4px 0', borderBottom: '1px solid var(--og-line)' }}>
                  <span className="mute">{t}</span>
                  <span className={color} style={{ textTransform: 'uppercase', letterSpacing: '0.1em', fontSize: 10, fontWeight: 600 }}>{kind}</span>
                  <span className="bright" style={{ fontSize: 12 }}>{msg}</span>
                </div>
              ))}
              <div style={{ marginTop: 14, fontSize: 10, color: 'var(--og-bone-soft)', letterSpacing: '0.14em', textTransform: 'uppercase' }}>
                ↳ deterministic · replayable from event log · runs on CPU
              </div>
            </div>
          </div>
        </div>
      </div>
    </section>
  );
}

function RoadmapQuarterScale() {
  const buildLabel = 'v1.0';
  const quarters = [];
  for (let y = 2026; y <= 2029; y++) {
    for (let q = 1; q <= 4; q++) quarters.push({ year: y, q });
  }
  const hereIdx = 1; // 2026 Q2
  const milestones = {
    7:  { label: 'V0 · MVP',    color: 'var(--og-ember-300)' },
    11: { label: 'V2 · LAUNCH', color: 'var(--og-bloom-300)' },
  };

  return (
    <div className="rm-scale mt-64" aria-label="Quarter scale">
      <div className="rm-scale-header">
        <div className="rm-here-stack" style={{ left: `calc(${(hereIdx + 0.5) / quarters.length * 100}% )` }}>
          <div className="rm-here-build">BUILD <span className="v">{buildLabel}</span></div>
          <div className="rm-here-label">
            <svg width="10" height="10" viewBox="0 0 14 14" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round">
              <polygon points="7,1.5 11.5,4 11.5,10 7,12.5 2.5,10 2.5,4" />
              <circle cx="7" cy="7" r="1.4" fill="currentColor" stroke="none" />
            </svg>
            <span>We are here</span>
          </div>
          <div className="rm-here-arrow" aria-hidden="true">
            <svg width="14" height="22" viewBox="0 0 14 22" fill="none" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" strokeLinejoin="round">
              <path d="M7 1v18" />
              <path d="M2.5 14.5L7 19l4.5-4.5" />
            </svg>
          </div>
        </div>
      </div>

      <div className="rm-scale-track">
        {quarters.map((q, i) => {
          const isHere = i === hereIdx;
          const past = i < hereIdx;
          const ms = milestones[i];
          return (
            <div key={i} className={`rm-tick ${isHere ? 'is-here' : ''} ${past ? 'is-past' : ''} ${ms ? 'has-ms' : ''}`}>
              <div className="rm-tick-mark" style={ms ? { background: ms.color, boxShadow: `0 0 8px ${ms.color}` } : undefined} />
              {q.q === 1 && <div className="rm-tick-year mono">{q.year}</div>}
              <div className="rm-tick-q mono">Q{q.q}</div>
              {ms && <div className="rm-tick-ms mono" style={{ color: ms.color }}>{ms.label}</div>}
            </div>
          );
        })}
      </div>
    </div>
  );
}

function Roadmap() {
  const stops = [
    { color: 'var(--og-ember-300)',   phase: 'V0 · MVP',    when: 'Q4 2027', title: 'Single-player on multiplayer-ready arch', desc: 'One shard. Real Earth, OSM building import, AI growing unoccupied hexes. Player claims 1–3 Res-6 hexes; backend complete.' },
    { color: 'var(--og-rite-violet)', phase: 'V1',          when: '2028',    title: 'Friends-only multiplayer + self-host',    desc: 'Invite up to ~10 to a private shard. Per-hex co-op. Self-hosting Docker image released. Modder shards begin.' },
    { color: 'var(--og-bloom-300)',   phase: 'V2 · LAUNCH', when: 'Q4 2028', title: 'Public hosted shards (NA / EU)',          desc: 'Hundreds of concurrent per shard. Real per-hex competition. City-transfer between shards. Mod API v1.' },
    { color: 'var(--og-rite-cyan)',   phase: 'V3',          when: '2029+',   title: 'Space expansion · political layer',       desc: 'Mars colonization. Res-3 country layer activated. AI asset generation production-quality.' },
  ];
  return (
    <section id="roadmap" className="section section-line" style={{ background: 'rgba(10,15,13,0.5)', borderBottom: '1px solid var(--og-line)' }}>
      <div className="wrap">
        <div className="eyebrow">Phasing roadmap</div>
        <h2 className="display text-4xl mt-16" style={{ maxWidth: 760 }}>
          Each phase reuses the same backend.<br />
          <span className="dim">Multiplayer is a toggle, not a rewrite.</span>
        </h2>

        <RoadmapQuarterScale />

        <div className="timeline" style={{ marginTop: 12 }}>
          {stops.map((s, i) => (
            <div key={i} className="tl-stop">
              <div className="tl-dot" style={{ background: s.color, color: s.color }} />
              <div style={{ fontFamily: 'var(--og-font-mono)', fontSize: 10, letterSpacing: '0.18em', color: s.color, textTransform: 'uppercase', fontWeight: 600 }}>{s.phase}</div>
              <div className="display" style={{ fontSize: 22, marginTop: 4 }}>{s.when}</div>
              <div className="bright mt-12" style={{ fontSize: 14, fontWeight: 500 }}>{s.title}</div>
              <div className="text-sm dim mt-12" style={{ lineHeight: 1.6 }}>{s.desc}</div>
            </div>
          ))}
        </div>

        <div className="mt-64" style={{ padding: 24, background: 'var(--og-soil)', border: '1px solid var(--og-line-bloom)', borderRadius: 8, display: 'grid', gridTemplateColumns: 'auto 1fr', gap: 24, alignItems: 'center' }}>
          <span className="og-panel-badge" style={{ background: 'rgba(108,212,154,0.08)', color: 'var(--og-bloom-300)' }}><span className="og-dot og-dot-live" />SHIPPED</span>
          <div className="text-md">
            <span className="bloom" style={{ fontWeight: 500 }}>v1 — World server core</span> is live. The persistent world ticks forward continuously — tracking every hex, every claim, every change — whether anyone is logged in or not. <span className="dim">All 22 tasks of the v1 plan complete (2026-04-26).</span>
          </div>
        </div>
      </div>
    </section>
  );
}

function Pricing({ tweaks }) {
  return (
    <section id="pricing" className="section section-line">
      <div className="wrap">
        <div className="eyebrow">What you get</div>
        <h2 className="display text-4xl mt-16" style={{ maxWidth: 820 }}>
          Buy once. Play solo forever.<br />
          <span className="dim">Subscribe when you want the </span>
          <span className="serif serif-bloom">living world</span>
          <span className="dim">.</span>
        </h2>
        <p className="text-lg dim mt-32" style={{ maxWidth: 720 }}>
          The base game is yours — full single-player simulation, your shard runs on your machine, no recurring fees. The subscription unlocks the parts that run on our infrastructure: shared persistent shards, the AI evolution worker that grows your world while you're offline, and cross-shard trade.
        </p>

        <div className="grid mt-64" style={{ gridTemplateColumns: 'repeat(2, 1fr)', gap: 20 }}>
          {/* Tier 1 */}
          <div className="og-card og-card-bloom">
            <div className="og-panel-header">
              <span className="og-panel-title">Tier 01 · base game</span>
              <span className="og-panel-badge">ONE-TIME</span>
            </div>
            <div style={{ padding: 32 }}>
              <div className="mono" style={{ fontSize: 11, letterSpacing: '0.16em', color: 'var(--og-bone-mute)', textTransform: 'uppercase' }}>Purchase · Steam / itch</div>
              <div className="display mt-12" style={{ fontSize: 56, lineHeight: 1, letterSpacing: '-0.03em' }}>
                $69
                <span className="mono" style={{ fontSize: 14, color: 'var(--og-bone-mute)', marginLeft: 8, letterSpacing: '0.08em' }}>USD</span>
              </div>
              <div className="text-md dim mt-12">Pay once. Yours forever. No DRM beyond Steam.</div>

              <ul style={{ listStyle: 'none', padding: 0, margin: '32px 0 0', display: 'flex', flexDirection: 'column', gap: 12 }}>
                {[
                  'Full single-player on real Earth — pick any hex',
                  'Complete simulation: aggregation, production chains, trade',
                  'Self-hosted shard — runs on your machine',
                  'Local AI worker grows your unoccupied hexes',
                  'Steam Workshop mods + first-class modding API',
                  'Free updates through V3',
                ].map((feat, i) => (
                  <li key={i} style={{ display: 'grid', gridTemplateColumns: '20px 1fr', gap: 10, alignItems: 'start' }}>
                    <span style={{ color: 'var(--og-ember-300)', fontFamily: 'var(--og-font-mono)', fontSize: 12, paddingTop: 2 }}>▸</span>
                    <span className="text-sm bright" style={{ lineHeight: 1.5 }}>{feat}</span>
                  </li>
                ))}
              </ul>

              <div className="mt-32" style={{ paddingTop: 20, borderTop: '1px solid var(--og-line)' }}>
                <div className="og-meta-row"><span className="og-meta-key">Players per shard</span><span className="og-meta-val">1 · solo</span></div>
                <div className="og-meta-row"><span className="og-meta-key">World persistence</span><span className="og-meta-val">local save</span></div>
                <div className="og-meta-row"><span className="og-meta-key">Cross-shard trade</span><span className="og-meta-val mute">—</span></div>
              </div>

              <a href="#wishlist" className="og-btn og-btn-lg" style={{ marginTop: 28, width: '100%', justifyContent: 'center' }}>Wishlist on Steam →</a>
            </div>
          </div>

          {/* Tier 2 */}
          <div className="og-card og-card-glow">
            <div className="og-panel-header" style={{ background: 'linear-gradient(90deg, rgba(108,212,154,0.10), transparent)', borderBottom: '1px solid var(--og-line-bloom)' }}>
              <span className="og-panel-title bloom">Tier 02 · living world</span>
              <span className="og-panel-badge"><span className="og-dot og-dot-live" />OPTIONAL</span>
            </div>
            <div style={{ padding: 32 }}>
              <div className="mono" style={{ fontSize: 11, letterSpacing: '0.16em', color: 'var(--og-bloom-300)', textTransform: 'uppercase' }}>Subscription · cancel anytime</div>
              <div className="display mt-12" style={{ fontSize: 56, lineHeight: 1, letterSpacing: '-0.03em' }}>
                <span className="bloom">$9.99</span>
                <span className="mono" style={{ fontSize: 14, color: 'var(--og-bone-mute)', marginLeft: 8, letterSpacing: '0.08em' }}>/ month</span>
              </div>
              <div className="text-md dim mt-12">Adds everything that needs a server tick when you're not playing.</div>

              <ul style={{ listStyle: 'none', padding: 0, margin: '32px 0 0', display: 'flex', flexDirection: 'column', gap: 12 }}>
                {[
                  'Multiplayer hosted shards (NA / EU)',
                  'Offline AI evolution — your city grows 24/7',
                  'Persistent world tick · 1 real-min = 1 game-day',
                  'Cross-shard trade routes + city-transfer (V2)',
                  'Up to 3 active shards per account',
                  'Priority on fresh-start launch shards',
                ].map((feat, i) => (
                  <li key={i} style={{ display: 'grid', gridTemplateColumns: '20px 1fr', gap: 10, alignItems: 'start' }}>
                    <span style={{ color: 'var(--og-bloom-300)', fontFamily: 'var(--og-font-mono)', fontSize: 12, paddingTop: 2 }}>▸</span>
                    <span className="text-sm bright" style={{ lineHeight: 1.5 }}>{feat}</span>
                  </li>
                ))}
              </ul>

              <div className="mt-32" style={{ paddingTop: 20, borderTop: '1px solid var(--og-line)' }}>
                <div className="og-meta-row"><span className="og-meta-key">Players per shard</span><span className="og-meta-val">100s · MMO</span></div>
                <div className="og-meta-row"><span className="og-meta-key">World persistence</span><span className="og-meta-val bloom">always-on</span></div>
                <div className="og-meta-row"><span className="og-meta-key">AI worker</span><span className="og-meta-val bloom">runs 24/7</span></div>
              </div>

              <a href="#wishlist" className="og-btn og-btn-primary og-btn-lg" data-accent={tweaks.lead} style={{ marginTop: 28, width: '100%', justifyContent: 'center' }}>Get notified at Alpha →</a>
            </div>
          </div>
        </div>

        <div className="mt-48" style={{ padding: 24, background: 'var(--og-void-2)', border: '1px solid var(--og-line)', borderRadius: 8, display: 'grid', gridTemplateColumns: 'auto 1fr', gap: 24, alignItems: 'start' }}>
          <span className="og-panel-badge">REASSURE</span>
          <div className="text-sm" style={{ color: 'var(--og-bone-dim)', lineHeight: 1.6 }}>
            <span className="bright">The subscription is opt-in.</span> If you cancel, your single-player saves keep working — they're local. The subscription pays for the world server, not the game. Self-hosting your own multiplayer shard is also free (Docker image, V1) — you only pay if you want us to host it for you.
          </div>
        </div>
      </div>
    </section>
  );
}

function Wishlist({ tweaks }) {
  const [email, setEmail] = React.useState('');
  const [sent, setSent] = React.useState(false);
  return (
    <section id="wishlist" className="section">
      <div className="wrap">
        <div className="wl-card">
          <div style={{ display: 'grid', gridTemplateColumns: 'minmax(0, 1.1fr) minmax(0, 1fr)', gap: 56, alignItems: 'center' }}>
            <div>
              <div className="eyebrow">Be there for Alpha</div>
              <h2 className="display text-4xl mt-16">Forge your first hex<br />in <span className="serif serif-accent">Q4 2027</span>.</h2>
              <p className="text-lg dim mt-24" style={{ maxWidth: 480 }}>
                Wishlist on Steam to be notified at Alpha. Or drop your email for the dev newsletter — early access invites, devlogs from solo-dev to launch, behind-the-scenes from the studio.
              </p>
              <div className="flex gap-12 mt-32" style={{ flexWrap: 'wrap' }}>
                <a href="#" className="og-btn og-btn-primary og-btn-lg" data-accent={tweaks.lead} onClick={(e) => { e.preventDefault(); }}>Wishlist on Steam →</a>
                <a href="#" className="og-btn og-btn-lg" onClick={(e) => e.preventDefault()}>Read the spec on GitHub</a>
              </div>
            </div>

            <form onSubmit={(e) => { e.preventDefault(); if (email) setSent(true); }}
                  style={{ background: 'var(--og-void-2)', border: '1px solid var(--og-line-strong)', borderRadius: 10, overflow: 'hidden' }}>
              <div className="og-panel-header">
                <span className="og-panel-title">Dev newsletter · enroll</span>
                <span className="og-panel-badge">~MONTHLY</span>
              </div>
              <div style={{ padding: 28 }}>
                {!sent ? (
                  <>
                    <div className="mono" style={{ fontSize: 11, letterSpacing: '0.14em', color: 'var(--og-bone-mute)', textTransform: 'uppercase', marginBottom: 8 }}>Your email</div>
                    <input className="wl-input" type="email" required placeholder="you@earth-1.og" value={email} onChange={e => setEmail(e.target.value)} />
                    <button type="submit" className="og-btn og-btn-primary" data-accent={tweaks.lead} style={{ marginTop: 16, width: '100%', justifyContent: 'center', padding: '0.9rem' }}>Subscribe →</button>
                    <div className="og-meta-row mt-16"><span className="og-meta-key">Cadence</span><span className="og-meta-val">~1× / month</span></div>
                    <div className="og-meta-row"><span className="og-meta-key">Audience</span><span className="og-meta-val">8,200+ early</span></div>
                    <div className="og-meta-row"><span className="og-meta-key">Studio</span><span className="og-meta-val">Oblivion Studio</span></div>
                  </>
                ) : (
                  <div style={{ padding: '32px 0', textAlign: 'center' }}>
                    <div className="og-dot og-dot-live" style={{ width: 10, height: 10, margin: '0 auto 16px' }} />
                    <div className="display text-xl">You're on the list.</div>
                    <div className="text-sm dim mt-12">First devlog lands soon — check your inbox.</div>
                  </div>
                )}
              </div>
            </form>
          </div>
        </div>
      </div>
    </section>
  );
}

function Footer() {
  return (
    <footer className="foot">
      <div className="wrap">
        <div className="flex between center" style={{ flexWrap: 'wrap', gap: 24 }}>
          <div className="flex center gap-12">
            <Logo size={22} />
            <span className="bright" style={{ fontWeight: 500 }}>Oblivions<span className="accent">.</span>Garden</span>
            <span className="soft" style={{ marginLeft: 16 }}>by Oblivion Studio · est. 2026</span>
          </div>
          <div className="mono" style={{ fontSize: 11, letterSpacing: '0.14em', textTransform: 'uppercase', color: 'var(--og-bone-soft)' }}>
            v1 shipped · Alpha Q4 2027 · Launch Q4 2028
          </div>
        </div>
      </div>
    </footer>
  );
}

function App() {
  const [tweaks, setTweak] = useTweaks(TWEAK_DEFAULTS);

  const rootRef = React.useRef(null);
  React.useEffect(() => {
    const el = rootRef.current;
    if (!el) return;
    if (tweaks.lead === 'bloom') {
      el.style.setProperty('--og-accent',      'var(--og-bloom-300)');
      el.style.setProperty('--og-accent-soft', 'var(--og-bloom-100)');
      el.style.setProperty('--og-accent-deep', 'var(--og-bloom-500)');
      el.style.setProperty('--og-accent-shade','var(--og-bloom-700)');
      el.style.setProperty('--og-accent-glow', 'var(--og-bloom-glow)');
      el.style.setProperty('--og-accent-line', 'var(--og-line-bloom)');
      el.style.setProperty('--og-accent-tint', 'rgba(108,212,154,0.10)');
      el.style.setProperty('--og-accent-tint-2','rgba(108,212,154,0.04)');
    } else {
      el.style.removeProperty('--og-accent');
      el.style.removeProperty('--og-accent-soft');
      el.style.removeProperty('--og-accent-deep');
      el.style.removeProperty('--og-accent-shade');
      el.style.removeProperty('--og-accent-glow');
      el.style.removeProperty('--og-accent-line');
      el.style.removeProperty('--og-accent-tint');
      el.style.removeProperty('--og-accent-tint-2');
    }
  }, [tweaks.lead]);

  return (
    <div id="top" ref={rootRef}>
      <div className="og-grain" style={{ opacity: tweaks.grain / 100 }} />
      <Nav tweaks={tweaks} setTweak={setTweak} />
      <Hero tweaks={tweaks} />
      <Manifesto tweaks={tweaks} />
      <SocialProof />
      <Mechanics />
      <EconomySection />
      {tweaks.showSettlements && (
        <SettlementEvolution
          stage={tweaks.settlementStage}
          onStage={(v) => setTweak('settlementStage', v)}
        />
      )}
      <LivingWorld />
      <Roadmap />
      <Pricing tweaks={tweaks} />
      <Wishlist tweaks={tweaks} />
      <Footer />

      <FloatingCTA tweaks={tweaks} />

      <TweaksPanel>
        <TweakSection label="Atmosphere" />
        <TweakRadio label="Lead palette" value={tweaks.lead} options={['bloom', 'ember']} onChange={(v) => setTweak('lead', v)} />
        <TweakSlider label="Film grain" value={tweaks.grain} min={0} max={20} step={1} unit="%" onChange={(v) => setTweak('grain', v)} />

        <TweakSection label="Headline" />
        <TweakToggle label="Auto-cycle" value={tweaks.headlineCycle} onChange={(v) => setTweak('headlineCycle', v)} />
        <TweakSlider label="Cycle interval" value={tweaks.headlineCycleSec} min={2} max={10} step={1} unit="s" onChange={(v) => setTweak('headlineCycleSec', v)} />
        <TweakRadio label="Verb" value={tweaks.headlineMode} options={['build', 'grow', 'shape', 'yours']} onChange={(v) => setTweak('headlineMode', v)} />
        <TweakText label="Shard label" value={tweaks.shardLabel} onChange={(v) => setTweak('shardLabel', v)} />

        <TweakSection label="Sections" />
        <TweakToggle label="Show settlement evolution" value={tweaks.showSettlements} onChange={(v) => setTweak('showSettlements', v)} />
        <TweakRadio label="Default stage" value={tweaks.settlementStage} options={['town', 'city', 'metropolis']} onChange={(v) => setTweak('settlementStage', v)} />
      </TweaksPanel>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById('root')).render(<App />);
