// app.jsx — ARMB portfolio
// Single full-bleed prototype: scroll-driven 3D parallax intro → hero → about → services → contact

const { useState, useEffect, useRef, useCallback, useMemo } = React;

// ── Helpers ─────────────────────────────────────────────────────────────────
const clamp = (v, lo, hi) => Math.max(lo, Math.min(hi, v));
const lerp = (a, b, t) => a + (b - a) * t;
const easeOutCubic = (t) => 1 - Math.pow(1 - t, 3);
const easeInOutCubic = (t) => t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;

// Hex to rgba helper for accent tints
function hexToRgba(hex, a = 1) {
  const h = hex.replace('#', '');
  const n = parseInt(h.length === 3 ? h.split('').map(c => c + c).join('') : h, 16);
  const r = (n >> 16) & 255, g = (n >> 8) & 255, b = n & 255;
  return `rgba(${r},${g},${b},${a})`;
}

// ── Scroll progress hook ────────────────────────────────────────────────────
// Returns scrollY in px and a normalized "intro" progress 0..1 over the first
// `introHeight` pixels of scroll.
function useScrollProgress(introHeight) {
  const [scrollY, setScrollY] = useState(0);
  useEffect(() => {
    let raf = 0;
    const onScroll = () => {
      if (raf) return;
      raf = requestAnimationFrame(() => {
        setScrollY(window.scrollY || window.pageYOffset || 0);
        raf = 0;
      });
    };
    window.addEventListener('scroll', onScroll, { passive: true });
    onScroll();
    return () => window.removeEventListener('scroll', onScroll);
  }, []);
  const intro = clamp(scrollY / introHeight, 0, 1);
  return { scrollY, intro };
}

// ── Reveal-on-scroll hook ───────────────────────────────────────────────────
function useReveal() {
  const ref = useRef(null);
  const [shown, setShown] = useState(false);
  useEffect(() => {
    if (!ref.current) return;
    const io = new IntersectionObserver(
      ([e]) => { if (e.isIntersecting) { setShown(true); io.disconnect(); } },
      { threshold: 0.18, rootMargin: '0px 0px -8% 0px' }
    );
    io.observe(ref.current);
    return () => io.disconnect();
  }, []);
  return [ref, shown];
}

// ── Entry animation: 3D parallax stack ──────────────────────────────────────
// Layered planes recede on scroll while the wordmark consolidates from
// shattered tracking → tight kerning. Camera dollies from z=-600 → 0.
function EntryStage({ progress, accent, speed, done }) {
  // Speed warps the curve so animSpeed=2 finishes in half the scroll distance
  const p = clamp(progress * speed, 0, 1);

  // Camera dolly
  const camZ = lerp(-700, 0, easeOutCubic(p));
  const camY = lerp(40, 0, easeInOutCubic(p));

  // Wordmark letters: start scattered in z, spread tracking, blurred → assemble
  const letters = ['A', 'R', 'M', 'B'];
  const letterStyle = (i) => {
    const t = clamp((p - i * 0.04) / 0.7, 0, 1);
    const e = easeOutCubic(t);
    const offsetZ = lerp([-260, 180, -120, 320][i], 0, e);
    const offsetY = lerp([60, -40, 50, -30][i], 0, e);
    const offsetX = lerp([-30, 40, -20, 30][i], 0, e);
    const rotY = lerp([35, -28, 22, -34][i], 0, e);
    const tracking = lerp(0.6, -0.04, e); // em
    const blur = lerp(14, 0, e);
    const opacity = lerp(0, 1, clamp(t * 1.6, 0, 1));
    return {
      transform: `translate3d(${offsetX}px, ${offsetY}px, ${offsetZ}px) rotateY(${rotY}deg)`,
      letterSpacing: `${tracking}em`,
      filter: `blur(${blur}px)`,
      opacity,
    };
  };

  // Layered planes: grid, frame, lines, dot
  const layerStyle = (z, baseOpacity = 1) => {
    const recede = lerp(0, -400, easeInOutCubic(p));
    const fade = lerp(baseOpacity, 0, clamp((p - 0.55) / 0.4, 0, 1));
    return {
      transform: `translate3d(-50%, -50%, ${z + recede}px)`,
      opacity: fade,
    };
  };

  // Final assembled wordmark fades in at the very end
  const assembledOpacity = clamp((p - 0.85) / 0.15, 0, 1);

  // Stage opacity for the very end (right before content takes over)
  const stageOpacity = lerp(1, 0, clamp((p - 0.92) / 0.08, 0, 1));

  return (
    <div
      className="entry-stage"
      style={{
        opacity: done ? 0 : stageOpacity,
        pointerEvents: done || p >= 1 ? 'none' : 'auto',
        visibility: done ? 'hidden' : 'visible',
      }}
    >
      <div
        className="entry-camera"
        style={{ transform: `translate3d(0, ${camY}px, ${camZ}px)` }}
      >
        {/* Far grid */}
        <div className="layer layer-grid" style={layerStyle(-600, 0.5)}>
          <div className="grid-inner" />
        </div>
        {/* Mid frame */}
        <div className="layer layer-frame" style={layerStyle(-300, 0.7)}>
          <div className="frame-inner" style={{ borderColor: hexToRgba(accent, 0.35) }}>
            <span className="frame-tag" style={{ color: hexToRgba(accent, 0.9) }}>
              ARMBCO / 2026
            </span>
            <span className="frame-tag frame-tag-r">REACT&nbsp;NATIVE</span>
            <span className="frame-tag frame-tag-bl">iOS&nbsp;·&nbsp;ANDROID</span>
            <span className="frame-tag frame-tag-br">v.001</span>
          </div>
        </div>
        {/* Near rules */}
        <div className="layer layer-rules" style={layerStyle(-100, 0.9)}>
          <div className="rule-h" />
          <div className="rule-v" />
        </div>

        {/* Letters scatter plane (the centerpiece) */}
        <div className="layer layer-letters" style={{ transform: 'translate3d(-50%, -50%, 0)' }}>
          {letters.map((ch, i) => (
            <span key={i} className="entry-letter" style={letterStyle(i)}>
              {ch}
            </span>
          ))}
        </div>
      </div>

      {/* HUD */}
      <div className="entry-hud">
        <div className="hud-l">
          <span className="hud-dot" style={{ background: accent }} />
          <span>SCROLL TO ENTER</span>
        </div>
        <div className="hud-r">
          <span className="hud-num">{String(Math.round(p * 100)).padStart(3, '0')}</span>
          <span className="hud-sub">/ 100</span>
        </div>
      </div>

      {/* Progress bar */}
      <div className="entry-progress">
        <div className="entry-progress-fill" style={{ width: `${p * 100}%`, background: accent }} />
      </div>

      {/* Final assembled mark cross-fades in */}
      <div className="entry-final" style={{ opacity: assembledOpacity }}>
        <span className="entry-final-mark">ARMB</span>
        <span className="entry-final-sub">armbco</span>
      </div>
    </div>
  );
}

// ── Top nav ─────────────────────────────────────────────────────────────────
function Nav({ accent, scrolled }) {
  return (
    <nav className={`nav ${scrolled ? 'nav-scrolled' : ''}`}>
      <a href="#top" className="nav-logo">
        ARMB<span style={{ color: accent }}>.</span>
      </a>
      <div className="nav-links">
        <a href="#about">About</a>
        <a href="#services">Services</a>
        <a href="#contact">Contact</a>
      </div>
      <a href="#contact" className="nav-cta" style={{ borderColor: hexToRgba(accent, 0.4) }}>
        <span style={{ color: accent }}>●</span> Available
      </a>
    </nav>
  );
}

// ── Hero ────────────────────────────────────────────────────────────────────
function Hero({ accent }) {
  const [ref, shown] = useReveal();
  return (
    <section id="top" className={`hero ${shown ? 'in' : ''}`} ref={ref} data-screen-label="Hero">
      <div className="hero-meta">
        <span className="meta-line" />
        <span>SECTION / 01 / INDEX</span>
      </div>
      <h1 className="hero-h">
        <span className="hero-line">Turn your ideas</span>
        <span className="hero-line"><em style={{ color: accent }}>into revenue</em></span>
        <span className="hero-line">in weeks, not months.</span>
      </h1>
      <div className="hero-foot">
        <p className="hero-blurb">
          ARMB designs and ships React&nbsp;Native applications across iOS and
          Android. Built lean, instrumented for growth, handed off without drama.
        </p>
        <div className="hero-actions">
          <a href="#contact" className="btn btn-primary" style={{ background: accent, color: '#08090B' }}>
            Start a project →
          </a>
          <a href="#services" className="btn btn-ghost">What I do</a>
        </div>
      </div>
      <div className="hero-corners">
        <span>iOS</span><span>ANDROID</span><span>REACT&nbsp;NATIVE</span><span>SHIP&nbsp;FAST</span>
      </div>
    </section>
  );
}

// ── About ───────────────────────────────────────────────────────────────────
function About({ accent }) {
  const [ref, shown] = useReveal();
  return (
    <section id="about" className={`about ${shown ? 'in' : ''}`} ref={ref} data-screen-label="About">
      <header className="sect-head">
        <span className="sect-num">02</span>
        <span className="sect-name">About</span>
      </header>
      <div className="about-grid">
        <h2 className="about-h">
          A one-person studio for <em style={{ color: accent }}>mobile-first</em> products that need
          to ship and earn.
        </h2>
        <div className="about-body">
          <p>
            I'm an independent developer building applications and software solutions,
            primarily React Native, with deep work on both the iOS and Android side of the fence.
          </p>
          <p>
            The work I take on is small, sharp, and outcome-driven. No agency layers.
            No three-month discovery phase. Just a fast loop from idea → prototype → store-ready build.
          </p>
          <dl className="about-stats">
            <div><dt>06+</dt><dd>Years shipping mobile</dd></div>
            <div><dt>100%</dt><dd>Cross-platform RN</dd></div>
            <div><dt>2-6w</dt><dd>Typical engagement</dd></div>
          </dl>
        </div>
      </div>
    </section>
  );
}

// ── Services ────────────────────────────────────────────────────────────────
const SERVICES = [
  {
    n: '01',
    t: 'Mobile App Development',
    d: 'End-to-end React Native builds for iOS and Android, from blank repo to App Store and Play submission.',
    tags: ['React Native', 'iOS', 'Android', 'TypeScript'],
  },
  {
    n: '02',
    t: 'MVP & Prototype Sprints',
    d: 'A working prototype in your hands within two weeks. Built to validate, not to throw away. Production foundations from day one.',
    tags: ['MVP', 'Validation', '2-week sprint'],
  },
  {
    n: '03',
    t: 'Existing App Rescue',
    d: 'Stuck builds, slow releases, flaky CI, missing analytics. I come in, stabilize, and hand back a project you can ship from again.',
    tags: ['Refactor', 'CI/CD', 'Performance'],
  },
  {
    n: '04',
    t: 'Backend & Integrations',
    d: 'Auth, payments, push, analytics, third-party APIs. The unglamorous wiring that decides whether a product actually earns.',
    tags: ['Auth', 'Stripe', 'Push', 'APIs'],
  },
];

function Services({ accent }) {
  const [ref, shown] = useReveal();
  return (
    <section id="services" className={`services ${shown ? 'in' : ''}`} ref={ref} data-screen-label="Services">
      <header className="sect-head">
        <span className="sect-num">03</span>
        <span className="sect-name">Services</span>
      </header>
      <h2 className="services-h">
        Four ways I plug in.<br />
        <em style={{ color: accent }}>All of them ship.</em>
      </h2>
      <div className="services-list">
        {SERVICES.map((s, i) => (
          <ServiceRow key={s.n} s={s} accent={accent} delay={i * 80} />
        ))}
      </div>
    </section>
  );
}

function ServiceRow({ s, accent, delay }) {
  const [hover, setHover] = useState(false);
  return (
    <article
      className={`service ${hover ? 'service-hover' : ''}`}
      style={{ transitionDelay: `${delay}ms` }}
      onMouseEnter={() => setHover(true)}
      onMouseLeave={() => setHover(false)}
    >
      <div className="service-n" style={{ color: hover ? accent : undefined }}>{s.n}</div>
      <div className="service-body">
        <h3 className="service-t">{s.t}</h3>
        <p className="service-d">{s.d}</p>
      </div>
      <div className="service-tags">
        {s.tags.map((t) => <span key={t}>{t}</span>)}
      </div>
      <div className="service-arrow" style={{ color: hover ? accent : undefined }}>→</div>
      <span className="service-line" style={{ background: hover ? accent : undefined }} />
    </article>
  );
}

// ── Contact form ────────────────────────────────────────────────────────────
function Contact({ accent }) {
  const [ref, shown] = useReveal();
  const [form, setForm] = useState({ name: '', email: '', subject: 'New project', message: '', website: '' });
  const [errors, setErrors] = useState({});
  const [status, setStatus] = useState('idle'); // idle | sending | sent

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

  const validate = () => {
    const er = {};
    if (!form.name.trim()) er.name = 'Required';
    if (!form.email.trim()) er.email = 'Required';
    else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(form.email)) er.email = 'Invalid email';
    if (!form.message.trim()) er.message = 'Tell me a little about it';
    else if (form.message.trim().length < 10) er.message = 'A bit more detail, please';
    setErrors(er);
    return Object.keys(er).length === 0;
  };

  const onSubmit = async (e) => {
    e.preventDefault();
    if (!validate()) return;
    setStatus('sending');
    try {
      const r = await fetch('/api/contact', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(form),
      });
      if (r.ok) {
        setStatus('sent');
        return;
      }
      const data = await r.json().catch(() => ({}));
      const msg = data.error === 'rate_limited'
        ? `Too many tries — wait ${data.retry_after_s ?? 60}s and retry`
        : 'Could not send — try again in a moment';
      setErrors({ message: msg });
      setStatus('idle');
    } catch {
      setErrors({ message: 'Network error — try again' });
      setStatus('idle');
    }
  };

  const reset = () => {
    setForm({ name: '', email: '', subject: 'New project', message: '', website: '' });
    setErrors({});
    setStatus('idle');
  };

  return (
    <section id="contact" className={`contact ${shown ? 'in' : ''}`} ref={ref} data-screen-label="Contact">
      <header className="sect-head">
        <span className="sect-num">04</span>
        <span className="sect-name">Contact</span>
      </header>
      <div className="contact-grid">
        <div className="contact-l">
          <h2 className="contact-h">
            Got an idea?<br />
            <em style={{ color: accent }}>Let's talk.</em>
          </h2>
          <p className="contact-blurb">
            Tell me what you're building, the rough timeline, and where you're stuck.
            I read every message and reply within one business day.
          </p>
          <ul className="contact-meta">
            <li><span>Based</span><b>Remote, global</b></li>
            <li><span>Reply</span><b>&lt; 24 hours</b></li>
          </ul>
        </div>

        <form
          className={`contact-form ${status === 'sent' ? 'sent' : ''}`}
          onSubmit={onSubmit}
          noValidate
          style={{ '--accent': accent }}
        >
          {status === 'sent' ? (
            <div className="form-success">
              <div className="success-mark" style={{ borderColor: accent, color: accent }}>✓</div>
              <h3>Message received.</h3>
              <p>Thanks, {form.name.split(' ')[0] || 'friend'}. I'll be back at <b>{form.email}</b> within a day.</p>
              <button type="button" className="btn btn-ghost" onClick={reset}>Send another</button>
            </div>
          ) : (
            <>
              {/* Honeypot — bots fill it, humans never see it. Server silently
                  accepts and discards any submission with this field set. */}
              <input
                type="text"
                name="website"
                tabIndex={-1}
                autoComplete="off"
                value={form.website}
                onChange={set('website')}
                style={{ position: 'absolute', left: '-9999px', width: 1, height: 1, opacity: 0 }}
                aria-hidden="true"
              />
              <Field label="Your name" id="f-name" error={errors.name}>
                <input id="f-name" value={form.name} onChange={set('name')} placeholder="Jane Doe" autoComplete="name" />
              </Field>
              <Field label="Email" id="f-email" error={errors.email}>
                <input id="f-email" type="email" value={form.email} onChange={set('email')} placeholder="jane@example.com" autoComplete="email" />
              </Field>
              <Field label="Subject" id="f-subject">
                <select id="f-subject" value={form.subject} onChange={set('subject')}>
                  <option>New project</option>
                  <option>MVP / prototype</option>
                  <option>Existing app rescue</option>
                  <option>Backend / integrations</option>
                  <option>Just saying hi</option>
                </select>
              </Field>
              <Field label="Message" id="f-message" error={errors.message}>
                <textarea id="f-message" rows={5} value={form.message} onChange={set('message')}
                          placeholder="A few lines on what you're building, your timeline, and your budget if you have one." />
              </Field>
              <div className="form-foot">
                <span className="form-meta">
                  {form.message.length} chars · enter to fire
                </span>
                <button type="submit" className="btn btn-primary" disabled={status === 'sending'}
                        style={{ background: accent, color: '#08090B' }}>
                  {status === 'sending' ? 'Sending…' : 'Send message →'}
                </button>
              </div>
            </>
          )}
        </form>
      </div>
    </section>
  );
}

function Field({ label, id, error, children }) {
  return (
    <label className={`field ${error ? 'field-err' : ''}`} htmlFor={id}>
      <span className="field-l">
        <span>{label}</span>
        {error && <span className="field-e">{error}</span>}
      </span>
      {children}
    </label>
  );
}

// ── Footer ──────────────────────────────────────────────────────────────────
function Footer({ accent }) {
  return (
    <footer className="footer">
      <div className="footer-mark">
        ARMB<span style={{ color: accent }}>.</span>
      </div>
      <div className="footer-cols">
        <div>
          <span className="footer-l">Domain</span>
          <span>armbco</span>
        </div>
        <div>
          <span className="footer-l">Year</span>
          <span>© 2026</span>
        </div>
        <div>
          <span className="footer-l">Legal</span>
          <a href="/app/pace/privacy" style={{ fontSize: 14 }}>Privacy</a>
          <a href="/app/pace/terms" style={{ fontSize: 14 }}>Terms</a>
        </div>
      </div>
      <div className="footer-tag">shipped fast, on purpose</div>
    </footer>
  );
}

// ── Main App ────────────────────────────────────────────────────────────────
function App() {
  const accent = '#5B8CFF';
  const speed = 1;

  // Intro takes ~1.6 viewport heights of scroll at speed=1.
  const introHeight = typeof window !== 'undefined' ? window.innerHeight * 1.6 : 1200;
  const { scrollY, intro } = useScrollProgress(introHeight);

  // Past-intro flag: lock the entry stage in its final state and let content take over
  const intoContent = scrollY > introHeight * 0.9;

  return (
    <>
      {/* Fixed entry stage — hides itself once intro is done */}
      <EntryStage progress={intro} accent={accent} speed={speed} done={intoContent} />

      {/* Spacer so the intro has scroll-room before content begins */}
      <div className="intro-spacer" style={{ height: `${introHeight}px` }} />

      {intoContent && <Nav accent={accent} scrolled={intoContent} />}
      <main className="content">
        <Hero accent={accent} />
        <About accent={accent} />
        <Services accent={accent} />
        <Contact accent={accent} />
      </main>
      <Footer accent={accent} />
    </>
  );
}

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