// spndly — App composition + Tweaks

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "accent": "#6a4cf0",
  "bgTone": "#ffffff",
  "density": "regular",
  "monoWeight": 400,
  "showTerminal": true,
  "showCases": true,
  "headline": "busywork"
}/*EDITMODE-END*/;

const ACCENT_OPTIONS = ['#6a4cf0', '#5a3ee0', '#7c3aed', '#4f46e5', '#8b5cf6'];
const BG_OPTIONS = ['#ffffff', '#fafaf9', '#f5f5f4'];

function App() {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const [dark, setDark] = React.useState(() => {
    try { return localStorage.getItem('spndly-theme') === 'dark'; } catch (e) { return false; }
  });

  React.useEffect(() => {
    try { localStorage.setItem('spndly-theme', dark ? 'dark' : 'light'); } catch (e) {}
    document.documentElement.classList.toggle('dark', dark);
  }, [dark]);

  // Scroll-reveal: fade + rise elements in as they enter the viewport, staggered
  // per row. Below the fold only — the hero animates on its own. Once, then rest.
  React.useEffect(() => {
    const reduce = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
    if (reduce || !('IntersectionObserver' in window)) return;
    const sel = '.sec-head, .stat-cell, .step, .service, .vc, .faq-item, .book-side, .book-form, .foot-col';
    const els = Array.from(document.querySelectorAll(sel))
      .filter(el => !el.closest('.hero') && !el.closest('.cs-reader'));
    els.forEach(el => el.classList.add('rv'));
    els.forEach(el => {
      const sibs = Array.from(el.parentElement.children).filter(c => c.classList.contains('rv'));
      const i = sibs.indexOf(el);
      if (i > 0) el.style.transitionDelay = Math.min(i, 6) * 70 + 'ms';
    });
    const io = new IntersectionObserver((entries) => {
      entries.forEach(e => {
        if (e.isIntersecting) { 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();
  }, []);

  // apply tweak vars to :root
  React.useEffect(() => {
    const r = document.documentElement.style;
    r.setProperty('--accent', t.accent);
    if (dark) {
      // clear inline light overrides so the .dark stylesheet tokens apply
      ['--bg','--bg-2','--bg-3','--fg','--fg-2','--fg-3','--fg-4','--fg-5','--border','--border-2','--accent-soft','--accent-text']
        .forEach(k => r.removeProperty(k));
    } else {
      r.setProperty('--bg', t.bgTone);
      r.setProperty('--bg-2', '#fafaf9');
      r.setProperty('--bg-3', '#f5f5f4');
      r.setProperty('--fg', '#0a0a0a');
      r.setProperty('--fg-2', '#404040');
      r.setProperty('--fg-3', '#737373');
      r.setProperty('--fg-4', '#a3a3a3');
      r.setProperty('--fg-5', '#d4d4d4');
      r.setProperty('--border', '#e7e5e4');
      r.setProperty('--border-2', '#d6d3d1');
      r.setProperty('--accent-soft', '#efedfe');
      r.setProperty('--accent-text', '#5b3fd6');
    }
    document.body.style.fontWeight = String(t.monoWeight);
    const pad = t.density === 'compact' ? 72 : t.density === 'comfy' ? 144 : 112;
    document.querySelectorAll('.section').forEach(s => { s.style.paddingTop = pad + 'px'; s.style.paddingBottom = pad + 'px'; });
  }, [t, dark]);

  return (
    <>
      <Nav dark={dark} onToggle={() => setDark(d => !d)} />
      <main>
        <Hero key={t.headline + (t.showTerminal ? '1' : '0')} headline={t.headline} showDemo={t.showTerminal} />
        <Problem />
        <HowItWorks />
        <Services />
        {t.showCases && <CaseStudies />}
        <FAQ />
        <Book />
      </main>
      <Footer />

      <TweaksPanel>
        <TweakSection label="theme" />
        <TweakColor label="accent" value={t.accent} options={ACCENT_OPTIONS}
                    onChange={(v) => setTweak('accent', v)} />
        <TweakColor label="background" value={t.bgTone} options={BG_OPTIONS}
                    onChange={(v) => setTweak('bgTone', v)} />
        <TweakRadio label="density" value={t.density} options={['compact', 'regular', 'comfy']}
                    onChange={(v) => setTweak('density', v)} />
        <TweakSlider label="mono weight" value={t.monoWeight} min={300} max={600} step={100}
                     onChange={(v) => setTweak('monoWeight', v)} />

        <TweakSection label="hero" />
        <TweakRadio label="headline" value={t.headline}
                    options={['busywork', 'paperwork', 'the pile']}
                    onChange={(v) => setTweak('headline', v)} />
        <TweakToggle label="animated demo" value={t.showTerminal}
                     onChange={(v) => setTweak('showTerminal', v)} />

        <TweakSection label="sections" />
        <TweakToggle label="show body of work" value={t.showCases}
                     onChange={(v) => setTweak('showCases', v)} />
      </TweaksPanel>
    </>
  );
}

// Types the purple accent line in on load, one real glyph at a time, then
// retires the caret. Honors reduced-motion (shows the full line, no caret).
function TypedAccent({ text }) {
  const reduce = typeof window !== 'undefined' && window.matchMedia
    && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
  const [n, setN] = React.useState(reduce ? text.length : 0);
  const [caretGone, setCaretGone] = React.useState(reduce);

  React.useEffect(() => {
    if (reduce) return;
    let i = 0, timer;
    const tick = () => {
      i += 1;
      setN(i);
      timer = (i < text.length)
        ? setTimeout(tick, 52)                        // ~50ms per character
        : setTimeout(() => setCaretGone(true), 1150); // hold, then retire caret
    };
    timer = setTimeout(tick, 460);                    // let the static lines land first
    return () => clearTimeout(timer);
  }, [text, reduce]);

  return (
    <span className="accent" aria-label={text}>
      <span aria-hidden="true">{text.slice(0, n)}</span>
      {!caretGone && <span className="type-caret" aria-hidden="true" />}
    </span>
  );
}

// re-render Hero based on headline tweak
const __OriginalHero = window.Hero;
window.Hero = function HeroWrap({ headline, showDemo }) {
  const H = {
    'busywork': {
      l1: 'The busywork that',
      l2: 'runs your week,',
      accent: 'running itself.',
    },
    'paperwork': {
      l1: 'Documents, claims,',
      l2: 'reports —',
      accent: 'moving on their own.',
    },
    'the pile': {
      l1: 'The workflow everyone',
      l2: 'quietly works around,',
      accent: 'made to run.',
    },
  }[headline] || { l1: '', l2: '', accent: '' };
  return (
    <section className="hero" id="top" data-screen-label="01 hero">
      <div className="container hero-grid">
        <div className="hero-copy">
          <div className="hero-eyebrow"><span className="dot" /> Operations engineering · healthcare &amp; insurance</div>
          <h1 className="h1" style={{ margin: '0 0 28px' }}>
            {H.l1}<br />{H.l2}<br /><TypedAccent key={H.accent} text={H.accent} />
          </h1>
          <p className="lead">
            Spndly is an operations engineering company for healthcare and insurance. We build the systems behind intake, claims, reports, and follow-up — engineered to scale and to run on the infrastructure you already have, with people kept where judgment matters.
          </p>
          <div className="cta-row">
            <a href="#book" className="btn btn-primary">Map a workflow →</a>
            <a href="#cases" className="btn btn-secondary">See selected work</a>
          </div>
          <ProofBar />
        </div>
        <div className="hero-visual">
          <WorkflowDeck animate={showDemo} />
        </div>
      </div>
    </section>
  );
};

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