// app.jsx — top-level App with route state + Tweaks

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "accent": "pine",
  "backgroundTone": "warm",
  "headingTypeface": "geist",
  "wordmarkMd": "accent-light",
  "fontScale": 100
}/*EDITMODE-END*/;

const ACCENTS = {
  pine:   { name: 'Pine',          accent: '#1F5C3D', ink: '#143C28', tint: '#EEF3EE', tint2: '#DDE7DE' },
  slate:  { name: 'Slate teal',    accent: '#2C5A66', ink: '#1A3B45', tint: '#ECF1F2', tint2: '#D6E1E4' },
  navy:   { name: 'Clinical navy', accent: '#1F3D6E', ink: '#142B4D', tint: '#EDF0F6', tint2: '#D6DDEA' },
  ink:    { name: 'Warm ink',      accent: '#3A332A', ink: '#211C17', tint: '#F1EDE7', tint2: '#E1DACE' },
};

// Background tones. `line` / `line2` are the two hairline weights: `line` for
// internal dividers inside a surface, `line2` for a surface's own outer edge —
// deliberately near-invisible, since elevation (--shadow-card) does the
// separating. These are written as inline custom properties on <html>, which
// outrank :root in styles.css, so THIS is the authoritative source for them.
const BG_TONES = {
  warm: { bg: '#FAFAF7', paper2: '#F4F4EF', line: '#E8E8E2', line2: '#F1F1EC' },
  cool: { bg: '#F8F9FB', paper2: '#F1F3F6', line: '#E6E9EE', line2: '#F0F2F6' },
  bone: { bg: '#F4F1EA', paper2: '#EBE7DE', line: '#DFDACE', line2: '#E9E4D8' },
};

const HEADING_FONTS = {
  geist:   { stack: "'Geist', ui-sans-serif, system-ui, sans-serif", label: 'Geist' },
  jakarta: { stack: "'Plus Jakarta Sans', ui-sans-serif, system-ui, sans-serif", label: 'Plus Jakarta' },
  ibm:     { stack: "'IBM Plex Sans', ui-sans-serif, system-ui, sans-serif", label: 'IBM Plex' },
  fraunces:{ stack: "'Fraunces', Georgia, serif", label: 'Fraunces (serif)' },
};

// Per-route metadata — unique <title> + meta description (≤160 chars each).
const ROUTE_META = {
  home: { title: 'CriterionMD · Built for clinical care, by physicians', desc: "Insurify reads the clinical note as it's written and shows what the payer requires before the prior authorization goes out. Free for NPI-verified physicians and APPs." },
  insurify: { title: 'Insurify™ · CriterionMD', desc: 'Know what the payer requires before you submit. Insurify maps your note against medical-necessity criteria and flags gaps in the visit.' },
  access: { title: 'Get access · CriterionMD', desc: 'Get Insurify free with NPI verification, or request a practice demo of the CriterionMD platform.' },
  demo: { title: 'Request a practice demo · CriterionMD', desc: 'Request a demo of the CriterionMD platform, scoped to your practice, payer mix, and specialty.' },
  solutions: { title: 'Solutions · CriterionMD', desc: 'One platform from clinical note to clean payment: Insurify today, with practice and enterprise products in development.' },
  about: { title: 'Executive Leadership · CriterionMD', desc: 'The physician-founded team building CriterionMD.' },
  'physician-board': { title: 'Physician Advisory Board · CriterionMD', desc: 'The practicing physicians whose clinical experience shapes every CriterionMD product.' },
  'general-board': { title: 'Advisory Board · CriterionMD', desc: 'The advisors guiding CriterionMD across healthcare, operations, and technology.' },
  press: { title: 'Press · CriterionMD', desc: 'News, press releases, and company updates from CriterionMD.' },
  'press-insurify-launch': { title: 'CriterionMD Launches Insurify at ASPN 2026 · Press Release', desc: 'CriterionMD announces the public launch of Insurify at ASPN 2026 — free prior-authorization intelligence for NPI-verified physicians and APPs.' },
  'press-advisors': { title: 'Physician Advisory Board Additions · CriterionMD Press', desc: 'CriterionMD welcomes two practicing interventional pain physicians to its Physician Advisory Board.' },
  'press-pilot': { title: 'First Michigan Pilot · CriterionMD Press', desc: 'CriterionMD launches its first pilot across Michigan clinical and surgical sites.' },
  contact: { title: 'Contact · CriterionMD', desc: 'Reach the CriterionMD team: demos and pilots, media, investors, product development, and careers.' },
  'contact-demos': { title: 'Demos & Pilots · CriterionMD', desc: 'Talk to the founding team about a demo or pilot scoped to your practice.' },
  'contact-media': { title: 'Media Inquiries · CriterionMD', desc: 'Press contact for interviews, founder bios, logos, and product imagery.' },
  'contact-investors': { title: 'Investor Relations · CriterionMD', desc: 'Investor inquiries and updates from CriterionMD.' },
  'contact-development': { title: 'Product Development · CriterionMD', desc: 'Co-develop a workflow with the CriterionMD team.' },
  'contact-careers': { title: 'Careers · CriterionMD', desc: 'Join a small, physician-led team building payer-aware software for physicians.' },
  terms: { title: 'Terms of Use · CriterionMD', desc: 'Terms of Use for the CriterionMD website and products.' },
  privacy: { title: 'Privacy Policy · CriterionMD', desc: 'What CriterionMD collects, how it is used, and how to reach us with privacy requests.' },
  security: { title: 'Security Overview · CriterionMD', desc: 'CriterionMD security posture: HIPAA safeguards, encryption in transit and at rest, and BAAs.' },
  'not-found': { title: 'Page not found · CriterionMD', desc: 'That page isn\u2019t here. Everything current is reachable from the CriterionMD homepage.' },
};

function NotFoundPage({ go }) {
  return (
    <section className="section" data-screen-label="99 Not Found" style={{ minHeight: '55vh', display: 'flex', alignItems: 'center' }}>
      <div className="cmd-container" style={{ textAlign: 'center', maxWidth: 560 }}>
        <div className="mono" style={{ fontSize: 13, letterSpacing: '.18em', color: 'var(--ink-3)', marginBottom: 16 }}>404 — PAGE NOT FOUND</div>
        <h1 className="h-1" style={{ margin: 0 }}>That page{' '}<span style={{ color: 'var(--accent)' }}>isn't here.</span></h1>
        <p className="lede" style={{ margin: '16px auto 28px' }}>The link may be old or mistyped. Everything current is reachable from the homepage.</p>
        <div style={{ display: 'flex', gap: 12, justifyContent: 'center', flexWrap: 'wrap' }}>
          <button className="btn btn-primary" onClick={() => go('home')}>Back to home</button>
          <button className="btn btn-outline" onClick={() => go('access')}>Get Insurify free</button>
        </div>
      </div>
    </section>
  );
}

function App() {
  // ── URL routing ──────────────────────────────────────────────────────────
  // Deployed (e.g. criterionmd.com): clean paths like /solutions via History API.
  // Local preview (URL ends in .html): hash fallback like #/solutions so the
  // preview host keeps serving the file.
  const VALID_ROUTES = ['home','solutions','insurify','access','demo','about',
    'physician-board','general-board','press','press-advisors','press-pilot',
    'press-insurify-launch',
    'contact-demos','contact-media','contact-investors','contact-development',
    'contact-careers','contact','terms','privacy','security','login',
    'portal','insurify-app','portal-billing','portal-team'];
  const usePaths = !window.location.pathname.endsWith('.html');

  // Friendly URL aliases → canonical internal routes
  const ROUTE_ALIASES = {
    'leadership': 'about',
    'physician-advisory-board': 'physician-board',
    'advisory-board': 'general-board',
    'careers': 'contact-careers',
    'press/insurify-launch': 'press-insurify-launch'
  };

  const routeFromLocation = () => {
    let seg = '';
    if (usePaths) {
      seg = window.location.pathname.replace(/^\/+|\/+$/g, '');
    } else if (window.location.hash.startsWith('#/')) {
      seg = window.location.hash.slice(2).replace(/\/+$/g, '');
    }
    seg = ROUTE_ALIASES[seg] || seg;
    return VALID_ROUTES.includes(seg) ? seg : (seg === '' ? 'home' : 'not-found');
  };

  const syncUrl = (r) => {
    const target = usePaths
      ? (r === 'home' ? '/' : '/' + r)
      : window.location.pathname + (r === 'home' ? '' : '#/' + r);
    const current = usePaths
      ? window.location.pathname
      : window.location.pathname + window.location.hash;
    if (current !== target) {
      // Stamp the departing entry with its scroll position so Back restores it
      window.history.replaceState(
        { ...(window.history.state || {}), scroll: window.scrollY }, '',
        usePaths ? window.location.pathname : window.location.pathname + window.location.hash);
      window.history.pushState({ route: r, scroll: 0 }, '', target);
    }
  };

  const [route, setRoute] = React.useState(routeFromLocation);
  const [authed, setAuthed] = React.useState(false);
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);

  // Entrance animations are an enhancement, never a dependency: after the
  // longest hero entrance would have finished, force every one-shot reveal to
  // its settled state. setTimeout keeps running in contexts where the document
  // animation timeline (and rAF) never advance, which would otherwise leave
  // opacity:0 entrance elements permanently invisible.
  React.useEffect(() => {
    const t = setTimeout(() => {
      document.documentElement.classList.add('motion-settled');
    }, 2600);
    return () => clearTimeout(t);
  }, []);

  // Back/forward buttons — restore the previous scroll position on back,
  // start at top on forward navigation. We manage scroll manually.
  React.useEffect(() => {
    if ('scrollRestoration' in window.history) window.history.scrollRestoration = 'manual';
    const onPop = (e) => {
      setRoute(routeFromLocation());
      const s = (e.state && e.state.scroll) || 0;
      requestAnimationFrame(() => requestAnimationFrame(() => window.scrollTo(0, s)));
    };
    window.addEventListener('popstate', onPop);
    return () => window.removeEventListener('popstate', onPop);
  }, []);

  // Per-route <title> + meta description
  React.useEffect(() => {
    const m = ROUTE_META[route] || ROUTE_META.home;
    document.title = m.title;
    let el = document.querySelector('meta[name="description"]');
    if (!el) {
      el = document.createElement('meta');
      el.setAttribute('name', 'description');
      document.head.appendChild(el);
    }
    el.setAttribute('content', m.desc);
    // Canonical URL per route
    let canon = document.querySelector('link[rel="canonical"]');
    if (!canon) {
      canon = document.createElement('link');
      canon.setAttribute('rel', 'canonical');
      document.head.appendChild(canon);
    }
    canon.setAttribute('href', 'https://criterionmd.com/' + (route === 'home' ? '' : route));
    if (typeof window.track === 'function') window.track('page_view_spa', { route: route });
  }, [route]);

  const go = React.useCallback((r) => {
    if (r === 'login') { setRoute('login'); syncUrl('login'); return; }
    if (r === 'logout') { setAuthed(false); setRoute('home'); syncUrl('home'); return; }
    if ((r === 'portal' || r === 'insurify-app' || r === 'portal-billing' || r === 'portal-team') && !authed) {
      setRoute('login');
      syncUrl('login');
      return;
    }
    setRoute(r);
    syncUrl(r);
    window.scrollTo({ top: 0, behavior: 'instant' });
  }, [authed]);

  const onLogin = () => { setAuthed(true); setRoute('portal'); syncUrl('portal'); };
  const onLogout = () => { setAuthed(false); setRoute('home'); syncUrl('home'); };

  // Apply tweaks to CSS vars
  React.useEffect(() => {
    const root = document.documentElement;
    const a = ACCENTS[t.accent] || ACCENTS.pine;
    root.style.setProperty('--accent', a.accent);
    root.style.setProperty('--accent-ink', a.ink);
    root.style.setProperty('--accent-tint', a.tint);
    root.style.setProperty('--accent-tint-2', a.tint2);
    const tone = BG_TONES[t.backgroundTone] || BG_TONES.warm;
    root.style.setProperty('--bg', tone.bg);
    root.style.setProperty('--paper-2', tone.paper2);
    root.style.setProperty('--line', tone.line);
    root.style.setProperty('--line-2', tone.line2);
    const font = HEADING_FONTS[t.headingTypeface] || HEADING_FONTS.geist;
    root.style.setProperty('--font-sans', font.stack);
    root.style.setProperty('font-size', `${(t.fontScale || 100)}%`);
  }, [t]);

  // Inject extra Google fonts when needed
  React.useEffect(() => {
    const needed = {
      jakarta: 'Plus+Jakarta+Sans:wght@400;500;600;700',
      ibm:     'IBM+Plex+Sans:wght@400;500;600',
      fraunces:'Fraunces:opsz,wght@9..144,400;9..144,500;9..144,600',
    }[t.headingTypeface];
    if (!needed) return;
    const id = 'gf-' + t.headingTypeface;
    if (document.getElementById(id)) return;
    const link = document.createElement('link');
    link.id = id;
    link.rel = 'stylesheet';
    link.href = `https://fonts.googleapis.com/css2?family=${needed}&display=swap`;
    document.head.appendChild(link);
  }, [t.headingTypeface]);

  // Render wordmark variants by overriding the .md css per tweak
  React.useEffect(() => {
    let css = '';
    if (t.wordmarkMd === 'accent-light') {
      css = `.cmd-wordmark .md { color: var(--accent); font-weight: 350; }`;
    } else if (t.wordmarkMd === 'accent-bold') {
      css = `.cmd-wordmark .md { color: var(--accent); font-weight: 600; }`;
    } else if (t.wordmarkMd === 'muted') {
      css = `.cmd-wordmark .md { color: var(--ink-3); font-weight: 400; }`;
    } else if (t.wordmarkMd === 'inline') {
      css = `.cmd-wordmark .md { color: inherit; font-weight: inherit; }`;
    }
    let el = document.getElementById('wordmark-override');
    if (!el) {
      el = document.createElement('style');
      el.id = 'wordmark-override';
      document.head.appendChild(el);
    }
    el.textContent = css;
  }, [t.wordmarkMd]);

  // Render current page
  let page;
  switch (route) {
    case 'home':           page = <HomePage go={go} />; break;
    case 'solutions':      page = <SolutionsPage go={go} />; break;
    case 'insurify':       page = <InsurifyPage go={go} />; break;
    case 'access':         page = <AccessPage go={go} />; break;
    case 'demo':           page = <AccessPage go={go} initialTab="practice" />; break;
    case 'about':          page = <AboutPage go={go} />; break;
    case 'physician-board': page = <PhysicianBoardPage go={go} />; break;
    case 'general-board':  page = <GeneralBoardPage go={go} />; break;
    case 'press':          page = <PressPage go={go} />; break;
    case 'press-advisors': page = <PressArticlePage go={go} />; break;
    case 'press-pilot':    page = <PressArticlePage go={go} release={MICHIGAN_RELEASE} />; break;
    case 'press-insurify-launch': page = <PressArticlePage go={go} release={INSURIFY_LAUNCH_RELEASE} />; break;
    case 'contact-demos':       page = <DemosPage go={go} />; break;
    case 'contact-media':       page = <MediaPage go={go} />; break;
    case 'contact-investors':   page = <InvestorsPage go={go} />; break;
    case 'contact-development': page = <DevelopmentPage go={go} />; break;
    case 'contact-careers':     page = <CareersPage go={go} />; break;
    case 'contact':        page = <ContactPage go={go} />; break;
    case 'terms':          page = <TermsPage go={go} />; break;
    case 'privacy':        page = <PrivacyPage go={go} />; break;
    case 'security':       page = <SecurityPage go={go} />; break;
    case 'login':          page = <LoginPage go={go} onLogin={onLogin} />; break;
    case 'portal':         page = <PortalPage go={go} />; break;
    case 'insurify-app':   page = <InsurifyAppPage go={go} />; break;
    case 'portal-billing': page = <BillingPage go={go} />; break;
    case 'portal-team':    page = <TeamPage go={go} />; break;
    case 'not-found':      page = <NotFoundPage go={go} />; break;
    default:               page = <HomePage go={go} />;
  }

  const hideChrome = route === 'login';
  const showFooter = !authed && !hideChrome;

  return (
    <div className="cmd-shell">
      {!hideChrome && <Header route={route} go={go} authed={authed} onLogout={onLogout} />}
      <main className="cmd-main">{page}</main>
      {showFooter && <Footer go={go} route={route} />}

      <TweaksPanel>
        <TweakSection label="Brand" />
        <TweakColor
          label="Accent"
          value={ACCENTS[t.accent]?.accent}
          options={Object.values(ACCENTS).map(a => a.accent)}
          onChange={(hex) => {
            const key = Object.keys(ACCENTS).find(k => ACCENTS[k].accent === hex) || 'pine';
            setTweak('accent', key);
          }}
        />
        <TweakRadio
          label="Wordmark"
          value={t.wordmarkMd}
          options={['accent-light', 'accent-bold', 'muted', 'inline']}
          onChange={(v) => setTweak('wordmarkMd', v)}
        />

        <TweakSection label="Surface" />
        <TweakRadio
          label="Background"
          value={t.backgroundTone}
          options={['warm', 'cool', 'bone']}
          onChange={(v) => setTweak('backgroundTone', v)}
        />

        <TweakSection label="Typography" />
        <TweakSelect
          label="Typeface"
          value={t.headingTypeface}
          options={Object.keys(HEADING_FONTS)}
          onChange={(v) => setTweak('headingTypeface', v)}
        />
        <TweakSlider
          label="Font scale"
          value={t.fontScale || 100}
          min={90} max={112} step={2} unit="%"
          onChange={(v) => setTweak('fontScale', v)}
        />

        <TweakSection label="Navigate" />
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 6 }}>
          {[
            ['home','Home'],['solutions','Solutions'],['insurify','Insurify'],
            ['access','Access'],['about','About'],
            ['contact','Contact'],['login','Login'],
          ].map(([id, label]) => (
            <button key={id} onClick={() => go(id)} className="twk-jump"
              style={{
                appearance: 'none', border: '1px solid rgba(41,38,27,0.15)',
                background: route === id ? 'rgba(41,38,27,0.1)' : 'transparent',
                padding: '6px 10px', borderRadius: 6, fontSize: 11,
                color: 'inherit', textAlign: 'left', fontFamily: 'inherit'
              }}>{label}</button>
          ))}
        </div>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 6, marginTop: 6 }}>
          {[
            ['portal','Portal'],
            ['insurify-app','Insurify App'],
            ['portal-billing','Billing'],
            ['portal-team','Team'],
          ].map(([id, label]) => (
            <button key={id} onClick={() => { setAuthed(true); setRoute(id); syncUrl(id); }}
              style={{
                appearance: 'none', border: '1px solid rgba(41,38,27,0.15)',
                background: route === id ? 'rgba(41,38,27,0.1)' : 'transparent',
                padding: '6px 10px', borderRadius: 6, fontSize: 11,
                color: 'inherit', textAlign: 'left', fontFamily: 'inherit'
              }}>{label}</button>
          ))}
        </div>
      </TweaksPanel>
    </div>
  );
}

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