/* Oh Apps brochure site - main app */

const { useState, useEffect, useRef } = React;

// ---------- Reveal-on-scroll hook ----------
function useReveal() {
  const ref = useRef(null);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const io = new IntersectionObserver(
      (entries) => {
        entries.forEach((e) => {
          if (e.isIntersecting) {
            e.target.classList.add('in');
            io.unobserve(e.target);
          }
        });
      },
      { threshold: 0.12 }
    );
    io.observe(el);
    return () => io.disconnect();
  }, []);
  return ref;
}

function Reveal({ children, delay = 0, as: As = 'div', className = '', ...rest }) {
  const ref = useReveal();
  return (
    <As
      ref={ref}
      className={`reveal ${className}`}
      style={{ transitionDelay: `${delay}ms` }}
      {...rest}>
      
      {children}
    </As>);

}

// ---------- Nav ----------
function Nav() {
  const [scrolled, setScrolled] = useState(false);
  useEffect(() => {
    const onScroll = () => setScrolled(window.scrollY > 8);
    onScroll();
    window.addEventListener('scroll', onScroll);
    return () => window.removeEventListener('scroll', onScroll);
  }, []);
  return (
    <nav className={`nav ${scrolled ? 'scrolled' : ''}`}>
      <div className="container nav-inner">
        <a href="#top" className="nav-logo" aria-label="Oh Apps home">
          <img src="assets/ohapps-logo-cyan.png" alt="Oh Apps" />
        </a>
        <div className="nav-links">
          <a href="#services">Services</a>
          <a href="#about">About</a>
          <a href="#contact">Contact</a>
        </div>
        <a href="#contact" className="btn btn-sm">
          Start a project <Icon.Arrow size={14} />
        </a>
      </div>
    </nav>);

}

// ---------- Hero ----------
function Hero() {
  return (
    <section className="hero" id="top">
      <div className="container hero-grid">
        <div>
          <Reveal>
            <span className="eyebrow">
              <span className="eyebrow-dot"></span>
              Open for new projects · Autumn 2026
            </span>
          </Reveal>
          <Reveal delay={80}>
            <h1 className="headline">
              We <em>design</em>, <em>build</em> &amp; <em>market</em> apps
              people go <span className="headline-quote">"Oh!"</span> over.
            </h1>
          </Reveal>
          <Reveal delay={160}>
            <p className="lede">A small, senior team of designers, engineers and growth folk taking products from a sketch on a napkin all the way to the top of the App Store charts.



            </p>
          </Reveal>
          <Reveal delay={240}>
            <div className="hero-ctas">
              <a href="#contact" className="btn">
                Start a project <Icon.Arrow size={16} />
              </a>
              <a href="#about" className="btn btn-ghost">How we work</a>
            </div>
          </Reveal>
        </div>
        <Reveal delay={120}>
          <div className="hero-art">
            <div className="hero-card hero-card-main">
              <img src="assets/ohapps-logo-white.png" alt="" />
            </div>
            <div className="hero-chip chip-design float">
              <span className="hero-chip-icon"><Icon.Design /></span>
              Design
            </div>
            <div className="hero-chip chip-build float-2">
              <span className="hero-chip-icon"><Icon.Build /></span>
              Build
            </div>
            <div className="hero-chip chip-market float-3">
              <span className="hero-chip-icon"><Icon.Market /></span>
              Market
            </div>
          </div>
        </Reveal>
      </div>
    </section>);

}

// ---------- Services ----------
const SERVICES = [
{
  num: '01',
  name: 'Design',
  icon: 'design',
  desc: 'We sweat every pixel so your product feels effortless to use and impossible to put down.',
  items: ['Brand & identity', 'Product & UX design', 'User research', 'Motion & prototyping'],
  iconCmp: Icon.Design
},
{
  num: '02',
  name: 'Build',
  icon: 'build',
  desc: 'Full-stack engineering teams who ship fast, write tests, and build software that lasts.',
  items: ['iOS, Android & web', 'AI features & infra', 'API & backend', 'QA & launch support'],
  iconCmp: Icon.Build
},
{
  num: '03',
  name: 'Market',
  icon: 'market',
  desc: 'We get your product in front of the people who will love it — and keep them coming back.',
  items: ['Launch strategy', 'ASO & paid acquisition', 'Content & lifecycle', 'Analytics & retention'],
  iconCmp: Icon.Market
}];


function Services() {
  return (
    <section className="section section-soft" id="services">
      <div className="container">
        <div className="section-head">
          <Reveal className="section-head-stack">
            <div className="section-tag">What we do</div>
            <h2 className="section-title">
              One team, end&#8209;to&#8209;end. From the first sketch to a No.&nbsp;1 launch.
            </h2>
          </Reveal>
          <Reveal delay={80}>
            <p className="section-lede">
              Most agencies hand you off three times before you ship. We don't.
              The people who designed it also built it, and they're still around
              when it's time to market it.
            </p>
          </Reveal>
        </div>
        <div className="services-grid">
          {SERVICES.map((s, i) =>
          <Reveal key={s.name} delay={i * 90} className="service">
              <div className="service-num">{s.num}</div>
              <div className={`service-icon ${s.icon}`}><s.iconCmp /></div>
              <h3 className="service-name">{s.name}</h3>
              <p className="service-desc">{s.desc}</p>
              <ul className="service-list">
                {s.items.map((it) => <li key={it}>{it}</li>)}
              </ul>
            </Reveal>
          )}
        </div>
      </div>
    </section>);

}

// ---------- Work (removed) ----------
/* eslint-disable */const _UNUSED_WORK = [
{
  title: 'Mealtime',
  blurb: 'A recipe app for families who actually have to cook tonight.',
  tags: ['iOS', 'Brand', 'Launch'],
  tint: '#FFE9D9',
  accent: '#E8753C',
  size: 'wide',
  mock: 'recipes'
},
{
  title: 'Routely',
  blurb: 'Last-mile logistics rewritten for couriers, not dispatchers.',
  tags: ['Android', 'Web', 'Maps'],
  tint: '#E7EEFF',
  accent: '#5A7BFF',
  size: 'tall',
  mock: 'map'
},
{
  title: 'Pocketnote',
  blurb: 'A second-brain notebook that actually fits in your pocket.',
  tags: ['iOS', 'AI', 'Subs'],
  tint: '#EEEAFF',
  accent: '#7A60E8',
  size: 'tall',
  mock: 'note'
},
{
  title: 'Ferment',
  blurb: 'A coffee subscription marketplace for indie roasters.',
  tags: ['Web', 'Shopify', 'Brand'],
  tint: '#E9F7EF',
  accent: '#2EBE85',
  size: 'wide',
  mock: 'shop'
}];


function PhoneMock({ kind, accent }) {
  return (
    <div className="work-thumb-inner" style={{ background: '#fff' }}>
      <div style={{ position: 'absolute', top: 14, left: '50%', transform: 'translateX(-50%)', width: 60, height: 6, background: '#e6e6e6', borderRadius: 4 }} />
      <div style={{ padding: '36px 16px 16px', display: 'flex', flexDirection: 'column', gap: 10 }}>
        <div style={{ height: 10, width: '40%', background: accent, opacity: .9, borderRadius: 4 }} />
        <div style={{ height: 6, width: '70%', background: '#eaeaea', borderRadius: 3 }} />
        <div style={{ height: 6, width: '55%', background: '#eaeaea', borderRadius: 3 }} />
        {kind === 'recipes' &&
        <div style={{ marginTop: 10, display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8 }}>
            {[0, 1, 2, 3].map((i) =>
          <div key={i} style={{ aspectRatio: '1/1', background: accent, opacity: 0.18 + i * 0.06, borderRadius: 10 }} />
          )}
          </div>
        }
        {kind === 'map' &&
        <div style={{ marginTop: 8, aspectRatio: '4/5', background: accent, opacity: .12, borderRadius: 10, position: 'relative', overflow: 'hidden' }}>
            <div style={{ position: 'absolute', inset: 0, backgroundImage: 'repeating-linear-gradient(45deg, rgba(255,255,255,.6) 0 4px, transparent 4px 12px)' }} />
            <div style={{ position: 'absolute', top: '40%', left: '45%', width: 10, height: 10, background: accent, borderRadius: '50%' }} />
          </div>
        }
        {kind === 'note' &&
        <div style={{ marginTop: 10, display: 'flex', flexDirection: 'column', gap: 7 }}>
            {[100, 84, 92, 78, 88, 72].map((w, i) =>
          <div key={i} style={{ height: 5, width: `${w}%`, background: i === 0 ? accent : '#eaeaea', borderRadius: 3 }} />
          )}
          </div>
        }
        {kind === 'shop' &&
        <div style={{ marginTop: 10, display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8 }}>
            {[0, 1].map((i) =>
          <div key={i} style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
                <div style={{ aspectRatio: '1/1', background: accent, opacity: .2 + i * 0.1, borderRadius: 8 }} />
                <div style={{ height: 4, width: '70%', background: '#eaeaea', borderRadius: 2 }} />
                <div style={{ height: 4, width: '40%', background: accent, opacity: .7, borderRadius: 2 }} />
              </div>
          )}
          </div>
        }
      </div>
    </div>);

}

function Work() {
  return (
    <section className="section" id="work">
      <div className="container">
        <div className="section-head">
          <Reveal className="section-head-stack">
            <div className="section-tag">Selected work</div>
            <h2 className="section-title">A few products we've helped people fall in love with.</h2>
          </Reveal>
          <Reveal delay={80}>
            <a href="#contact" className="btn btn-ghost">Got an idea? Tell us <Icon.Arrow size={14} /></a>
          </Reveal>
        </div>
        <div className="work-grid">
          {WORK.map((w, i) =>
          <Reveal key={w.title} delay={i * 80} className={`work-card ${w.size}`}>
              <div className="work-thumb" style={{ background: w.tint }}>
                <div className="work-thumb-stripe" />
                <PhoneMock kind={w.mock} accent={w.accent} />
              </div>
              <div className="work-meta">
                <div className="work-tags">{w.tags.map((t) => <span key={t}>{t}</span>)}</div>
                <h3 className="work-title">{w.title}</h3>
                <p className="work-blurb">{w.blurb}</p>
              </div>
            </Reveal>
          )}
        </div>
      </div>
    </section>);

}

// ---------- Process ----------
const PROCESS = [
{ num: '01', name: 'Discover', desc: 'A short, sharp engagement to find the real problem worth solving.' },
{ num: '02', name: 'Design', desc: 'Brand, product and prototype — tested with real users every week.' },
{ num: '03', name: 'Build', desc: 'Senior engineers ship in two-week increments. You can watch it grow.' },
{ num: '04', name: 'Launch', desc: 'Coordinated marketing, ASO, and PR. We celebrate when you do.' }];


function Process() {
  return (
    <section className="section section-ink">
      <div className="container">
        <div className="section-head">
          <Reveal className="section-head-stack">
            <div className="section-tag">How we work</div>
            <h2 className="section-title" style={{ color: '#fff' }}>
              A clear, four-step rhythm. No surprises, no theatre.
            </h2>
          </Reveal>
        </div>
        <Reveal className="process">
          {PROCESS.map((p) =>
          <div key={p.name} className="process-step">
              <div className="process-step-num">{p.num}</div>
              <h3 className="process-step-name">{p.name}</h3>
              <p className="process-step-desc">{p.desc}</p>
            </div>
          )}
        </Reveal>
      </div>
    </section>);

}

// ---------- About ----------
function About() {
  return (
    <section className="section section-soft" id="about">
      <div className="container">
        <Reveal className="about-copy about-single">
          <div className="section-tag">Who we are</div>
          <h2 className="section-title">A small studio that takes <em style={{ color: 'var(--cyan)', fontStyle: 'normal' }}>"Oh!"</em> as a KPI.</h2>
          <p style={{ marginTop: 24 }}>
            Oh Apps is a senior team of designers, engineers and marketers who
            got tired of bouncing client work between three agencies. So we
            built one.
          </p>
          <p>
            We work with founders and product teams who'd rather have one
            senior team on speed-dial than a glossy pitch deck and a junior
            account manager.
          </p>
        </Reveal>
      </div>
    </section>);

}

// ---------- Contact ----------
const BUDGETS = ['< £25k', '£25–75k', '£75–150k', '£150k+'];
const SERVICES_OPTIONS = ['Design', 'Build', 'Market', 'Not sure yet'];

const FORMSPREE_URL = 'https://formspree.io/f/xwvzbayz';

function Contact() {
  const [form, setForm] = useState({ name: '', email: '', service: 'Design', budget: '£25–75k', message: '' });
  const [errors, setErrors] = useState({});
  const [sent, setSent] = useState(false);
  const [submitting, setSubmitting] = useState(false);
  const [submitError, setSubmitError] = useState(null);

  const update = (k) => (e) => setForm({ ...form, [k]: e.target.value });

  const submit = async (e) => {
    e.preventDefault();
    const errs = {};
    if (!form.name.trim()) errs.name = 'Please tell us your name.';
    if (!form.email.match(/^[^\s@]+@[^\s@]+\.[^\s@]+$/)) errs.email = 'A valid email helps us reply.';
    if (form.message.trim().length < 10) errs.message = 'A sentence or two helps a lot.';
    setErrors(errs);
    if (Object.keys(errs).length !== 0) return;

    setSubmitting(true);
    setSubmitError(null);
    try {
      const res = await fetch(FORMSPREE_URL, {
        method: 'POST',
        headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' },
        body: JSON.stringify({
          name: form.name,
          email: form.email,
          service: form.service,
          budget: form.budget,
          message: form.message,
          _subject: `New enquiry from ${form.name} — ${form.service}`
        })
      });
      if (res.ok) {
        setSent(true);
      } else {
        const data = await res.json().catch(() => ({}));
        setSubmitError(data?.errors?.[0]?.message || 'Something went wrong sending that. Please try again, or email hello@ohapps.io.');
      }
    } catch (err) {
      setSubmitError('Couldn\u2019t reach the server. Please check your connection and try again.');
    } finally {
      setSubmitting(false);
    }
  };

  return (
    <section className="section section-cyan" id="contact">
      <div className="container">
        <div className="section-head" style={{ marginBottom: 40 }}>
          <Reveal className="section-head-stack">
            <div className="section-tag" style={{ color: 'rgba(255,255,255,.8)' }}>Get in touch</div>
            <h2 className="section-title" style={{ color: '#fff' }}>
              Have something on a napkin? Send it over.
            </h2>
          </Reveal>
        </div>
        <Reveal className="contact-card">
          <div className="contact-aside">
            <h3>Let's talk.</h3>
            <p>Tell us a bit about your project and we'll come back within one working day with a real human reply — usually with questions, sometimes with sketches.</p>
            <div className="contact-line">
              <span className="contact-line-label">Email</span>
              <Icon.Mail />
              <span className="contact-line-value">hello@ohapps.io</span>
            </div>
            <div className="contact-line">
              <span className="contact-line-label">Phone</span>
              <Icon.Phone />
              <span className="contact-line-value">+44 20 4538 1190</span>
            </div>
            <div className="contact-line">
              <span className="contact-line-label">Studio</span>
              <Icon.Pin />
              <span className="contact-line-value">London &amp; Lisbon</span>
            </div>
          </div>
          {sent ?
          <div className="sent-card">
              <div className="sent-emoji"><Icon.Check /></div>
              <h4>Thanks, {form.name.split(' ')[0]}!</h4>
              <p>We've got your note. Expect a reply within one working day.</p>
            </div> :

          <form className="contact-form" onSubmit={submit} noValidate>
              <div className="contact-row">
                <div className={`field ${errors.name ? 'field-error' : ''}`}>
                  <label htmlFor="name">Your name</label>
                  <input id="name" value={form.name} onChange={update('name')} placeholder="Jamie Walker" />
                  {errors.name && <div className="field-error-msg">{errors.name}</div>}
                </div>
                <div className={`field ${errors.email ? 'field-error' : ''}`}>
                  <label htmlFor="email">Email</label>
                  <input id="email" type="email" value={form.email} onChange={update('email')} placeholder="jamie@company.com" />
                  {errors.email && <div className="field-error-msg">{errors.email}</div>}
                </div>
              </div>
              <div className="field">
                <label htmlFor="service">What do you need help with?</label>
                <select id="service" value={form.service} onChange={update('service')}>
                  {SERVICES_OPTIONS.map((s) => <option key={s}>{s}</option>)}
                </select>
              </div>
              <div className="field">
                <label>Budget range</label>
                <div className="budget-options">
                  {BUDGETS.map((b) =>
                <button
                  type="button"
                  key={b}
                  className={form.budget === b ? 'active' : ''}
                  onClick={() => setForm({ ...form, budget: b })}>
                  {b}</button>
                )}
                </div>
              </div>
              <div className={`field ${errors.message ? 'field-error' : ''}`}>
                <label htmlFor="message">A sentence or two about it</label>
                <textarea id="message" rows="4" value={form.message} onChange={update('message')} placeholder="We're building a..." />
                {errors.message && <div className="field-error-msg">{errors.message}</div>}
              </div>
              {submitError && <div className="field-error-msg" role="alert" style={{ marginTop: 4 }}>{submitError}</div>}
              <div className="submit-row">
                <span className="submit-note">Replies within 1 working day.</span>
                <button type="submit" className="btn" disabled={submitting} style={submitting ? { opacity: 0.7, cursor: 'wait' } : null}>
                  {submitting ? 'Sending\u2026' : <>Send it over <Icon.Arrow size={16} /></>}
                </button>
              </div>
            </form>
          }
        </Reveal>
      </div>
    </section>);

}

// ---------- Footer ----------
function Footer() {
  return (
    <footer className="footer">
      <div className="container">
        <div className="footer-inner">
          <img className="footer-logo" src="assets/ohapps-logo-cyan.png" alt="Oh Apps" />
          <div className="footer-meta">
            <a href="#services">Services</a>
            <a href="#about">About</a>
            <a href="#contact">Contact</a>
          </div>
        </div>
        <div className="footer-fine">© 2026 Oh Apps Ltd · Builders of "OhWesome" apps · Made with care in London & Manchester.</div>
      </div>
    </footer>);

}

// ---------- Tweaks ----------
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "accent": "#1BA8C5",
  "display": "Fredoka",
  "bg": "#FBFAF6"
} /*EDITMODE-END*/;

const ACCENTS = ['#1BA8C5', '#E8753C', '#7A60E8', '#2EBE85', '#14272F'];
const DISPLAYS = ['Fredoka', 'Quicksand', 'Caveat Brush', 'Bricolage Grotesque', 'DM Serif Display'];
const BGS = ['#FBFAF6', '#FFFFFF', '#F4F1EA', '#101820'];

function applyTweaks(t) {
  const root = document.documentElement;
  root.style.setProperty('--cyan', t.accent);
  // Derive deep + soft
  root.style.setProperty('--cyan-deep', shade(t.accent, -0.18));
  root.style.setProperty('--cyan-soft', mix(t.accent, '#ffffff', 0.86));
  root.style.setProperty('--cyan-50', mix(t.accent, '#ffffff', 0.94));
  root.style.setProperty('--display', `"${t.display}", system-ui, sans-serif`);
  root.style.setProperty('--paper', t.bg);
  // Dark mode-ish if bg is dark
  const dark = isDark(t.bg);
  root.style.setProperty('--ink', dark ? '#F5F6F4' : '#14272F');
  root.style.setProperty('--ink-soft', dark ? 'rgba(245,246,244,.72)' : '#4A5D66');
  root.style.setProperty('--ink-mute', dark ? 'rgba(245,246,244,.5)' : '#7C8A91');
  root.style.setProperty('--line', dark ? 'rgba(255,255,255,.12)' : '#E5EBED');
  root.style.setProperty('--white', dark ? '#1a2730' : '#ffffff');
}

function hexToRgb(h) {
  const m = h.replace('#', '');
  const n = parseInt(m, 16);
  return [n >> 16 & 255, n >> 8 & 255, n & 255];
}
function rgbToHex(r, g, b) {
  return '#' + [r, g, b].map((x) => Math.max(0, Math.min(255, Math.round(x))).toString(16).padStart(2, '0')).join('');
}
function mix(a, b, t) {
  const [ar, ag, ab] = hexToRgb(a),[br, bg, bb] = hexToRgb(b);
  return rgbToHex(ar * (1 - t) + br * t, ag * (1 - t) + bg * t, ab * (1 - t) + bb * t);
}
function shade(c, amt) {
  return mix(c, amt < 0 ? '#000000' : '#ffffff', Math.abs(amt));
}
function isDark(hex) {
  const [r, g, b] = hexToRgb(hex);
  return 0.299 * r + 0.587 * g + 0.114 * b < 110;
}

function TweaksUI() {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  useEffect(() => {applyTweaks(t);}, [t]);

  return (
    <TweaksPanel title="Tweaks">
      <TweakSection title="Accent colour">
        <TweakColor
          value={t.accent}
          onChange={(v) => setTweak('accent', v)}
          options={ACCENTS} />
        
      </TweakSection>
      <TweakSection title="Display font">
        <TweakSelect
          value={t.display}
          onChange={(v) => setTweak('display', v)}
          options={DISPLAYS} />
        
      </TweakSection>
      <TweakSection title="Background">
        <TweakColor
          value={t.bg}
          onChange={(v) => setTweak('bg', v)}
          options={BGS} />
        
      </TweakSection>
    </TweaksPanel>);

}

// ---------- App ----------
function App() {
  // Apply tweak defaults on mount so colour-derivations exist even without panel open
  useEffect(() => {applyTweaks(TWEAK_DEFAULTS);}, []);
  return (
    <>
      <Nav />
      <Hero />
      <Services />
      <Process />
      <About />
      <Contact />
      <Footer />
      <TweaksUI />
    </>);

}

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