// app.jsx — root shell, router, nav, footer, tweaks panel

const VERSION = '1.0.3';
const RELEASE_DATE = 'Sep 2, 2026'; // VI display: '02/09/2026'
const BUY_URL = 'https://snapovo.lemonsqueezy.com/checkout/buy/6f4a9261-9400-4131-b67a-9ca990af8a4c';
const BUY_URL_1MAC = BUY_URL + '?enabled=2082989';
const BUY_URL_2MAC = 'https://snapovo.lemonsqueezy.com/checkout/buy/0678cef0-f3d3-460b-99eb-1bd66997abfa?enabled=2082990';
const SUPPORT_EMAIL = 'snapovo.support.app@gmail.com';

// Legal facts, confirmed by the publisher.
const LEGAL = {
  entity: null, // not shown publicly
  address: null, // no public postal address: invoices carry Lemon Squeezy's address
  refundWindow: 14, // days
  refundConditions: {
    en: 'Snapovo approves any refund request made within 14 days of purchase — no reason needed. Refunds are processed by Lemon Squeezy, the merchant of record for your purchase, and may be refused where there is evidence of fraud or refund abuse. A free 7-day trial is available so you can evaluate Snapovo fully before buying.',
    vi: 'Snapovo chấp thuận mọi yêu cầu hoàn tiền trong vòng 14 ngày kể từ khi mua — không cần lý do. Việc hoàn tiền do Lemon Squeezy, đơn vị đứng tên bán hàng cho giao dịch của bạn, thực hiện; yêu cầu có thể bị từ chối nếu có dấu hiệu gian lận hoặc lạm dụng hoàn tiền. Bản dùng thử miễn phí 7 ngày cho phép bạn đánh giá đầy đủ Snapovo trước khi mua.',
  },
  governingLaw: 'Vietnam',
  jurisdiction: 'Vietnam',
};
window.SN_LEGAL = LEGAL;

// ─────────────────────────────────────────────────────────────────────────────
// TRIAL DOWNLOAD LINK — paste your direct .dmg URL between the quotes below and
// every "Download" button on the site (Download page + Changelog) goes live.
// Recommended host: GitHub Releases → free, permanent direct link, no bandwidth
// limits, and it plays nicely with Sparkle auto-updates.
// While this stays empty, those buttons show a calm "coming soon" state instead
// of a broken link — the rest of the site (Buy, docs, etc.) stays fully live.
//   e.g. const DMG_URL = 'https://github.com/you/snapovo/releases/download/v1.0/Snapovo-1.0.dmg';
// 1.0.3 release is live on GitHub Releases.
const DMG_URL = 'https://github.com/hosiyen/snapovo-releases/releases/download/v1.0.3/Snapovo-1.0.3.dmg';
// ─────────────────────────────────────────────────────────────────────────────

window.SN_VERSION = VERSION;
window.SN_RELEASE_DATE = RELEASE_DATE;
window.SN_BUY_URL = BUY_URL_2MAC;
window.SN_BUY_URL_1MAC = BUY_URL_1MAC;
window.SN_BUY_URL_2MAC = BUY_URL_2MAC;
window.SN_DMG_URL = DMG_URL;
window.SN_SUPPORT_EMAIL = SUPPORT_EMAIL;
window.SN_BUY_URL_BOTH = BUY_URL;
window.SN_GO_PRICING = (e) => {
  if (e) e.preventDefault();
  if (location.hash !== '#/' && location.hash !== '') location.hash = '#/';
  requestAnimationFrame(() => setTimeout(() => {
    const el = document.getElementById('pricing');
    if (el) window.scrollTo({ top: el.getBoundingClientRect().top + window.pageYOffset - 72, behavior: 'smooth' });
  }, 60));
};

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "accent": "#6D5DFC",
  "theme": "dark",
  "density": "regular",
  "lang": "en"
}/*EDITMODE-END*/;

const ACCENT_DEEP = {
  '#6D5DFC': '#4F3FE0',
  '#7C3AED': '#5B21B6',
  '#5B5BD6': '#3D3DB0',
  '#9333EA': '#6B21A8',
};
const ACCENT_SOFT = {
  '#6D5DFC': '#B8B5FF',
  '#7C3AED': '#A78BFA',
  '#5B5BD6': '#8B8BFF',
  '#9333EA': '#C4B5FD',
};

const NAV = [
  { id: 'home', label_en: 'Overview', label_vi: 'Tổng quan', path: '/' },
  { id: 'features', label_en: 'Features', label_vi: 'Tính năng', path: '/features' },
  { id: 'download', label_en: 'Download', label_vi: 'Tải về', path: '/download' },
  { id: 'docs', label_en: 'Docs', label_vi: 'Tài liệu', path: '/docs' },
  { id: 'changelog', label_en: 'Changelog', label_vi: 'Cập nhật', path: '/changelog' },
  { id: 'privacy', label_en: 'Privacy', label_vi: 'Riêng tư', path: '/privacy' },
  { id: 'contact', label_en: 'Contact', label_vi: 'Liên hệ', path: '/contact', hidden: true },
  { id: 'terms', label_en: 'Terms', label_vi: 'Điều khoản', path: '/terms', hidden: true },
];

// Hash-based router: #/features, #/docs, etc.
const useRoute = () => {
  const parse = () => {
    const h = (window.location.hash || '#/').replace(/^#/, '');
    const seg = h.split('/').filter(Boolean);
    const route = seg[0] || 'home';
    return route;
  };
  const [route, setRoute] = React.useState(parse);
  React.useEffect(() => {
    const onHash = () => { setRoute(parse()); window.scrollTo({ top: 0, behavior: 'instant' }); };
    window.addEventListener('hashchange', onHash);
    return () => window.removeEventListener('hashchange', onHash);
  }, []);
  const nav = (id) => {
    if (id === 'home') window.location.hash = '#/';
    else window.location.hash = `#/${id}`;
  };
  return [route, nav];
};

const Header = ({ route, onNav, lang, setLang, theme, setTheme }) => {
  const [open, setOpen] = React.useState(false);
  React.useEffect(() => {
    if (!open) return;
    const onKey = (e) => { if (e.key === 'Escape') setOpen(false); };
    const onResize = () => { if (window.innerWidth > 640) setOpen(false); };
    window.addEventListener('keydown', onKey);
    window.addEventListener('resize', onResize);
    return () => { window.removeEventListener('keydown', onKey); window.removeEventListener('resize', onResize); };
  }, [open]);
  const go = (id) => { setOpen(false); onNav(id); };
  return (
  <header className="hdr">
    <div className="container hdr-inner">
      <a className="hdr-brand" href="#/" onClick={(e) => { e.preventDefault(); onNav('home'); }}>
        <AppIcon size={26} />
        <span>Snapovo</span>
      </a>

      <nav className="hdr-nav" aria-label="Primary">
        {NAV.slice(1).filter((n) => !n.hidden).map((n) => (
          <a key={n.id}
             href={`#/${n.id}`}
             className={route === n.id ? 'is-active' : ''}
             aria-current={route === n.id ? 'page' : undefined}
             onClick={(e) => { e.preventDefault(); onNav(n.id); }}>
            {lang === 'vi' ? n.label_vi : n.label_en}
          </a>
        ))}
      </nav>

      <div className="hdr-meta">
        <div className="lang-toggle" role="group" aria-label="Language">
          <button onClick={() => setLang('en')} aria-pressed={lang === 'en'} className={lang === 'en' ? 'is-on' : ''}>EN</button>
          <button onClick={() => setLang('vi')} aria-pressed={lang === 'vi'} className={lang === 'vi' ? 'is-on' : ''}>VI</button>
        </div>
        <button className={`icon-btn`}
                title={theme === 'dark' ? 'Switch to light' : 'Switch to dark'}
                onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}>
          <span className="icon-swap" key={theme}>
            {theme === 'dark' ? <Icon.Sun size={14} /> : <Icon.Moon size={14} />}
          </span>
        </button>
        <a className="btn btn--primary hdr-buy"
           href={BUY_URL} target="_blank" rel="noopener noreferrer">
          {lang === 'vi' ? 'Mua Pro' : 'Buy Pro'}
        </a>
        <button className={`icon-btn hdr-menu-btn ${open ? 'is-open' : ''}`}
                aria-expanded={open} aria-controls="mobile-menu"
                aria-label={lang === 'vi' ? (open ? 'Đóng menu' : 'Mở menu') : (open ? 'Close menu' : 'Open menu')}
                onClick={() => setOpen(!open)}>
          <span className="burger" aria-hidden><i /><i /><i /></span>
        </button>
      </div>
    </div>
    {ReactDOM.createPortal(<div className={`hdr-sheet-veil ${open ? 'is-open' : ''}`} onClick={() => setOpen(false)} aria-hidden="true" />, document.body)}
    <nav id="mobile-menu" className={`hdr-sheet ${open ? 'is-open' : ''}`} aria-label="Mobile" aria-hidden={!open}>
      {NAV.filter((n) => !n.hidden).map((n) => (
        <a key={n.id} href={n.id === 'home' ? '#/' : `#/${n.id}`}
           className={route === n.id ? 'is-active' : ''}
           aria-current={route === n.id ? 'page' : undefined}
           tabIndex={open ? 0 : -1}
           onClick={(e) => { e.preventDefault(); go(n.id); }}>
          {lang === 'vi' ? n.label_vi : n.label_en}
          {route === n.id && <span className="hdr-sheet-dot" aria-hidden />}
        </a>
      ))}
      <a className="btn btn--primary" style={{ margin: '10px 16px 4px', justifyContent: 'center' }}
         href={BUY_URL} target="_blank" rel="noopener noreferrer" tabIndex={open ? 0 : -1}>
        {lang === 'vi' ? 'Mua Pro — từ $9.99' : 'Buy Pro — from $9.99'}
      </a>
      <a className="btn btn--ghost" style={{ margin: '8px 16px 4px', justifyContent: 'center' }}
         href="#/download" tabIndex={open ? 0 : -1}
         onClick={(e) => { e.preventDefault(); go('download'); }}>
        <Icon.Download size={15} /> {lang === 'vi' ? 'Tải bản dùng thử' : 'Download free trial'}
      </a>
    </nav>
    <div className="hdr-progress" aria-hidden="true"><i /></div>
  </header>
  );
};

const Footer = ({ onNav, lang }) => (
  <footer className="ftr">
    <div className="container">
      <div className="ftr-grid">
        <div className="ftr-col">
          <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 16 }}>
            <AppIcon size={28} />
            <span style={{ fontFamily: 'var(--font-display)', fontWeight: 600, fontSize: 17 }}>Snapovo</span>
          </div>
          <p style={{ color: 'var(--text-muted)', fontSize: 13.5, maxWidth: '34ch' }}>
            {lang === 'vi'
              ? 'Trình quản lý không gian làm việc cho macOS. Yên lặng, riêng tư, tại chỗ.'
              : 'A workspace-layout manager for macOS. Quiet, private, local-first.'}
          </p>
        </div>

        <div className="ftr-col">
          <h5>{lang === 'vi' ? 'Sản phẩm' : 'Product'}</h5>
          <ul>
            <li><a href="#/features" onClick={(e) => { e.preventDefault(); onNav('features'); }}>{lang === 'vi' ? 'Tính năng' : 'Features'}</a></li>
            <li><a href="#/download" onClick={(e) => { e.preventDefault(); onNav('download'); }}>{lang === 'vi' ? 'Tải về' : 'Download'}</a></li>
            <li><a href="#/changelog" onClick={(e) => { e.preventDefault(); onNav('changelog'); }}>{lang === 'vi' ? 'Cập nhật' : 'Changelog'}</a></li>
            <li><a href="#/#pricing" onClick={(e) => { e.preventDefault(); onNav('home'); setTimeout(() => { const el = document.getElementById('pricing'); if (el) window.scrollTo({ top: el.getBoundingClientRect().top + window.pageYOffset - 80, behavior: 'smooth' }); }, 80); }}>{lang === 'vi' ? 'Giá' : 'Pricing'}</a></li>
          </ul>
        </div>

        <div className="ftr-col">
          <h5>{lang === 'vi' ? 'Hỗ trợ' : 'Support'}</h5>
          <ul>
            <li><a href="#/docs" onClick={(e) => { e.preventDefault(); onNav('docs'); }}>{lang === 'vi' ? 'Tài liệu' : 'Docs'}</a></li>
            <li><a href="#/contact" onClick={(e) => { e.preventDefault(); onNav('contact'); }}>{lang === 'vi' ? 'Liên hệ' : 'Contact'}</a></li>
            <li><a href="#/privacy" onClick={(e) => { e.preventDefault(); onNav('privacy'); }}>{lang === 'vi' ? 'Riêng tư' : 'Privacy'}</a></li>
            <li><a href="#/terms" onClick={(e) => { e.preventDefault(); onNav('terms'); }}>{lang === 'vi' ? 'Điều khoản' : 'Terms'}</a></li>
          </ul>
        </div>

      </div>

      <div className="ftr-bottom">
        <span>© 2026 Snapovo.</span>
        <span>v{VERSION} · macOS 13.5+</span>
      </div>
    </div>
  </footer>
);

const App = () => {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const [route, nav] = useRoute();
  const [anncHidden, setAnncHidden] = React.useState(() => {
    try { return localStorage.getItem('sn_annc_hide') === '1'; } catch (e) { return false; }
  });
  const [anncClosing, setAnncClosing] = React.useState(false);
  const hideAnnc = () => {
    if (anncClosing) return;
    if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
      setAnncHidden(true);
    } else {
      setAnncClosing(true);
      setTimeout(() => { setAnncHidden(true); setAnncClosing(false); }, 320);
    }
    try { localStorage.setItem('sn_annc_hide', '1'); } catch (e) {}
  };

  // Cross-fade colors when theme/accent change after first paint
  const mounted = React.useRef(false);
  const xfadeT = React.useRef(0);
  const themeXfade = () => {
    if (!mounted.current) return;
    const root = document.documentElement;
    root.classList.add('theme-xfade');
    clearTimeout(xfadeT.current);
    xfadeT.current = setTimeout(() => root.classList.remove('theme-xfade'), 500);
  };

  // Dissolve the boot splash once React has painted
  React.useEffect(() => {
    const s = document.getElementById('splash');
    if (s) {
      s.classList.add('is-done');
      setTimeout(() => s.remove(), 600);
    }
  }, []);

  // Document title follows route + language
  React.useEffect(() => {
    const item = NAV.find((n) => n.id === route);
    document.title = !item || route === 'home'
      ? "Snapovo — Save your Mac's apps, windows & tabs as layouts"
      : `${t.lang === 'vi' ? item.label_vi : item.label_en} · Snapovo`;
  }, [route, t.lang]);

  // Apply theme + density to <html>
  React.useEffect(() => {
    themeXfade();
    document.documentElement.setAttribute('data-theme', t.theme);
    document.documentElement.setAttribute('data-density', t.density);
    document.documentElement.setAttribute('lang', t.lang);
    const mc = document.querySelector('meta[name="theme-color"]');
    if (mc) mc.setAttribute('content', t.theme === 'dark' ? '#0A0A0F' : '#FAFAF7');
  }, [t.theme, t.density, t.lang]);

  // Header elevation on scroll
  React.useEffect(() => {
    const onScroll = () => {
      const hdr = document.querySelector('.hdr');
      if (hdr) {
        hdr.classList.toggle('is-scrolled', window.scrollY > 4);
        const max = document.documentElement.scrollHeight - window.innerHeight;
        hdr.style.setProperty('--scroll-p', max > 0 ? Math.min(1, window.scrollY / max).toFixed(4) : '0');
      }
      const tt = document.querySelector('.to-top');
      if (tt) tt.classList.toggle('is-show', window.scrollY > 900);
    };
    onScroll();
    window.addEventListener('scroll', onScroll, { passive: true });
    window.addEventListener('resize', onScroll);
    return () => { window.removeEventListener('scroll', onScroll); window.removeEventListener('resize', onScroll); };
  }, []);

  // Scroll-reveal: staggered fade-rise as blocks enter the viewport
  React.useLayoutEffect(() => {
    if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
    const sel = [
      '.s-head', '.pillar', '.preset-legend-row', '.preset-shot', '.insights-shot',
      '.privacy-stripe', '.price-card', '.buyflow-step', '.cmp', '.faq-item',
      '.final-cta', '.feat-row', '.feat-extra', '.dl-card', '.step',
      '.changelog-entry', '.ph-inner', '.pomo-card', '.hairline',
    ].join(', ');
    const els = Array.from(document.querySelectorAll(sel));
    els.forEach((el) => el.classList.add('rv'));
    const io = new IntersectionObserver((entries) => {
      entries.filter((e) => e.isIntersecting).forEach((e, i) => {
        e.target.style.setProperty('--rv-d', `${Math.min(i, 5) * 70}ms`);
        e.target.classList.add('rv-in');
        io.unobserve(e.target);
      });
    }, { rootMargin: '0px 0px -8% 0px', threshold: 0.08 });
    els.forEach((el) => io.observe(el));
    return () => io.disconnect();
  }, [route]);

  // Apply accent color
  React.useEffect(() => {
    themeXfade();
    const root = document.documentElement;
    root.style.setProperty('--accent', t.accent);
    root.style.setProperty('--accent-deep', ACCENT_DEEP[t.accent] || t.accent);
    root.style.setProperty('--accent-soft', ACCENT_SOFT[t.accent] || t.accent);
    const r = parseInt(t.accent.slice(1, 3), 16);
    const g = parseInt(t.accent.slice(3, 5), 16);
    const b = parseInt(t.accent.slice(5, 7), 16);
    root.style.setProperty('--accent-glow', `rgba(${r},${g},${b},0.18)`);
    root.style.setProperty('--accent-tint', `rgba(${r},${g},${b},0.10)`);
  }, [t.accent]);

  // Mark mounted (declared last so first-paint effects above skip the cross-fade)
  React.useEffect(() => { mounted.current = true; }, []);

  let page;
  if (route === 'features') page = <Features lang={t.lang} theme={t.theme} />;
  else if (route === 'download') page = <Download lang={t.lang} />;
  else if (route === 'docs') page = <Docs lang={t.lang} />;
  else if (route === 'changelog') page = <Changelog lang={t.lang} />;
  else if (route === 'privacy') page = <Privacy lang={t.lang} />;
  else if (route === 'contact') page = <Contact lang={t.lang} />;
  else if (route === 'terms') page = <Terms lang={t.lang} />;
  else page = <Home lang={t.lang} theme={t.theme} onNav={nav} />;

  return (
    <div className="shell">
      <a className="skip-link" href="#main-content" onClick={(e) => { e.preventDefault(); const el = document.getElementById('main-content'); if (el) { el.focus({ preventScroll: true }); window.scrollTo({ top: el.getBoundingClientRect().top + window.pageYOffset - 70, behavior: 'instant' }); } }}>
        {t.lang === 'vi' ? 'Bỏ qua tới nội dung' : 'Skip to content'}
      </a>
      {!anncHidden && (
      <div className={`annc ${anncClosing ? 'is-closing' : ''}`}>
        <div className="container annc-inner">
          <span className="annc-tag">Snapovo {VERSION}</span>
          <span>{t.lang === 'vi' ? 'Snapovo 1.0 đã phát hành — dùng thử miễn phí 7 ngày.' : 'Snapovo 1.0 is here — free 7-day trial.'}</span>
          <a href="#/changelog" onClick={(e) => { e.preventDefault(); nav('changelog'); }}>
            {t.lang === 'vi' ? 'Xem thay đổi' : 'See what shipped'} <span aria-hidden>→</span>
          </a>
          <button className="annc-close" aria-label={t.lang === 'vi' ? 'Ẩn thông báo' : 'Dismiss announcement'} onClick={hideAnnc}>×</button>
        </div>
      </div>
      )}
      <Header route={route} onNav={nav} lang={t.lang} setLang={(l) => setTweak('lang', l)}
              theme={t.theme} setTheme={(th) => setTweak('theme', th)} />
      <div id="main-content" tabIndex={-1}>{page}</div>
      <Footer onNav={nav} lang={t.lang} />
      <button className="to-top" aria-label={t.lang === 'vi' ? 'Lên đầu trang' : 'Back to top'} onClick={() => window.scrollTo({ top: 0, behavior: 'smooth' })}>
        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M12 19V5" /><polyline points="5 12 12 5 19 12" /></svg>
      </button>

      <TweaksPanel>
        <TweakSection label="Theme" />
        <TweakRadio label="Mode" value={t.theme} options={['dark', 'light']}
                    onChange={(v) => setTweak('theme', v)} />
        <TweakColor label="Accent" value={t.accent}
                    options={['#6D5DFC', '#7C3AED', '#5B5BD6', '#9333EA']}
                    onChange={(v) => setTweak('accent', v)} />
        <TweakSection label="Layout" />
        <TweakRadio label="Density" value={t.density} options={['compact', 'regular', 'comfy']}
                    onChange={(v) => setTweak('density', v)} />
        <TweakSection label="Locale" />
        <TweakRadio label="Language" value={t.lang} options={['en', 'vi']}
                    onChange={(v) => setTweak('lang', v)} />
      </TweaksPanel>
    </div>
  );
};

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