// app.jsx — reviewpilot.work landing
// Single-page, dark-mode, founder-to-founder.
const { useState, useEffect, useRef, useMemo } = React;

// ─────────────────────────────────────────────────────────────────
// Defaults (Tweaks-editable)
// ─────────────────────────────────────────────────────────────────
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "accent": "#f5b94b",
  "founderPrice": 19,
  "listPrice": 39,
  "spots": 5,
  "spotsTaken": 0,
  "showPain": true,
  "showHow": true
}/*EDITMODE-END*/;

const STRIPE_URL = "https://buy.stripe.com/REPLACE_ME";

// ─────────────────────────────────────────────────────────────────
// Sample reviews that cycle in the chat mock
// ─────────────────────────────────────────────────────────────────
const REVIEWS = [
  {
    stars: 2,
    sentiment: -0.62,
    sentimentLabel: "Frustrated · trial billing",
    tone: "neg",
    app: "Quick Bundle Pro",
    who: "Lisa K.",
    country: "United States",
    when: "2m ago",
    body: "Confused why I'm being charged after the trial. Couldn't find a way to cancel and the bundles broke my checkout for two days. Support replied 18 hours later.",
    topics: ["billing", "checkout-bug", "support-latency"],
    drafts: [
      "Lisa — sorry about this. I refunded the charge and pushed a fix to make cancel easy to find. Want me to verify your checkout now? — Quick Bundle team",
      "You're right to be upset. Refund is already processed, and the cancel flow is fixed on the settings page. I can help confirm your store is stable. — Quick Bundle team",
      "Thanks for flagging this. We fixed the cancel path and issued your refund today. If you reply here, I'll personally check your setup. — Quick Bundle team"
    ]
  },
  {
    stars: 5,
    sentiment: 0.91,
    sentimentLabel: "Delighted · install flow",
    tone: "pos",
    app: "Quick Bundle Pro",
    who: "Marcus T.",
    country: "Australia",
    when: "Just now",
    body: "Installed yesterday and had a bundle live in 4 minutes. Did the math and we made back the monthly fee on the second day. The variant-aware pricing was the missing piece.",
    topics: ["install-flow", "pricing-config", "ROI"],
    drafts: [
      "Marcus — this made our day. Variant-aware pricing took months, so hearing this is huge. Thanks for sharing. — Quick Bundle team",
      "Really appreciate this, Marcus. Glad setup was fast and ROI showed up quickly. Reach out anytime if anything breaks. — Quick Bundle team",
      "Love this feedback. We built this for exactly your use case, so this means a lot. Thanks for the review. — Quick Bundle team"
    ]
  },
  {
    stars: 3,
    sentiment: 0.05,
    sentimentLabel: "Neutral · feature request",
    tone: "neu",
    app: "Quick Bundle Pro",
    who: "Priya R.",
    country: "United Kingdom",
    when: "8m ago",
    body: "Works fine for fixed bundles but I really need tiered discounts (buy 3, save 10%; buy 5, save 20%). Will switch once that exists.",
    topics: ["feature-request", "tiered-discounts"],
    drafts: [
      "Totally fair, Priya. Tiered discounts are in progress, and I'll ping you the day it ships so you can test first. — Quick Bundle team",
      "Great request. Buy-more-save-more tiers are on our near roadmap; I'll follow up as soon as they're live. — Quick Bundle team",
      "Thanks for the clear details. Tiered discounts are coming soon, and we'll notify you right when it's available. — Quick Bundle team"
    ]
  }
];

function trackEvent(name, props = {}){
  if (typeof window === "undefined") return;
  window.dataLayer = window.dataLayer || [];
  window.dataLayer.push({ event: name, ...props, ts: Date.now() });
  if (typeof window.gtag === "function") {
    window.gtag("event", name, props);
  }
}

// ─────────────────────────────────────────────────────────────────
// Helpers
// ─────────────────────────────────────────────────────────────────
function Stars({ n }){
  return (
    <span className="stars" aria-label={`${n} of 5 stars`}>
      {[0,1,2,3,4].map(i => <span key={i} className={i < n ? "on" : "off"}>★</span>)}
    </span>
  );
}

function hexToOklch(){ /* keep accent as-is; CSS uses it directly */ }

// ─────────────────────────────────────────────────────────────────
// Chat notification mock
// ─────────────────────────────────────────────────────────────────
function ChatMock(){
  const [idx, setIdx] = useState(0);
  const [paused, setPaused] = useState(false);
  const [draftIdxByReview, setDraftIdxByReview] = useState(() => REVIEWS.map(() => 0));
  const [sentAtByReview, setSentAtByReview] = useState(() => REVIEWS.map(() => null));
  const [toast, setToast] = useState("");
  const [lastChangeAtByReview, setLastChangeAtByReview] = useState(() => REVIEWS.map(() => null));
  const r = REVIEWS[idx];
  const currentDraftIdx = draftIdxByReview[idx] || 0;
  const currentDraft = r.drafts[currentDraftIdx];
  const isSent = sentAtByReview[idx] !== null;

  useEffect(() => {
    if (paused) return;
    const t = setTimeout(() => setIdx(i => (i + 1) % REVIEWS.length), 6500);
    return () => clearTimeout(t);
  }, [idx, paused]);

  useEffect(() => {
    if (!toast) return;
    const t = setTimeout(() => setToast(""), 1200);
    return () => clearTimeout(t);
  }, [toast]);

  function rotateDraft(step){
    setDraftIdxByReview(prev => {
      const next = [...prev];
      const len = r.drafts.length;
      next[idx] = (next[idx] + step + len) % len;
      return next;
    });
    setSentAtByReview(prev => {
      const next = [...prev];
      next[idx] = null;
      return next;
    });
    setLastChangeAtByReview(prev => {
      const next = [...prev];
      next[idx] = Date.now();
      return next;
    });
    setToast(step > 0 ? "Regenerated" : "Draft changed");
    trackEvent(step > 0 ? "demo_regenerate_click" : "demo_edit_click", { review_idx: idx });
  }

  function sendReply(){
    setSentAtByReview(prev => {
      const next = [...prev];
      next[idx] = Date.now();
      return next;
    });
    setToast("Marked as sent");
    trackEvent("demo_send_click", { review_idx: idx });
  }

  const lastChangeAt = lastChangeAtByReview[idx];
  const actionMeta = lastChangeAt ? `updated ${new Date(lastChangeAt).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })}` : "";

  return (
    <div className="chat" onMouseEnter={() => setPaused(true)} onMouseLeave={() => setPaused(false)}>
      <div className="chat-hd">
        <div className="ch">
          <span className="hash">#</span>
          <span>app-reviews</span>
        </div>
        <div className="meta">
          <span>connected</span>
          <span className="traffic" aria-hidden><i></i><i></i><i></i></span>
        </div>
      </div>

      <div className="msg">
        <div className="av">RP</div>
        <div>
          <div className="who">
            <b>ReviewPilot</b>
            <span className="app-tag">app</span>
            <time>{r.when}</time>
          </div>
          <div className="what">
            New review on <b style={{color:"var(--fg)"}}>{r.app}</b> · <span className="mono">{r.country}</span>
          </div>
        </div>
      </div>

      <div className={`card ${r.tone}`} key={idx}>
        <div className="hd">
          <Stars n={r.stars} />
          <span className="app mono">{r.app}</span>
        </div>
        <div className="body">"{r.body}"</div>
        <div className="who">
          <span>{r.who}</span>
          <span>{r.country}</span>
          <span>{r.when}</span>
        </div>
        <div className="chips">
          <span className={`chip sentiment ${r.tone}`}>
            sentiment {r.sentiment > 0 ? "+" : ""}{r.sentiment.toFixed(2)} · {r.sentimentLabel}
          </span>
          {r.topics.map(t => <span key={t} className="chip">#{t}</span>)}
        </div>

        <div className="draft">
          <div className="lbl"><span className="pulse"></span> draft reply · in your voice</div>
          <div className="body"><span className="swap" key={`${idx}-${currentDraftIdx}`}>{currentDraft}</span><span className="cursor" aria-hidden></span></div>
        </div>

        <div className="actions">
          <button className="send" type="button" onClick={sendReply}>{isSent ? "Sent" : "Send reply"}</button>
          <button type="button" onClick={() => rotateDraft(-1)}>Edit</button>
          <button type="button" onClick={() => rotateDraft(1)}>Regenerate</button>
          <span className="spacer"></span>
          <button className="link" type="button">Open in Partner Dashboard ↗</button>
        </div>
        <div className="action-meta mono">{toast || actionMeta}</div>
      </div>

    </div>
  );
}

// ─────────────────────────────────────────────────────────────────
// Sections
// ─────────────────────────────────────────────────────────────────
function Nav({ price }){
  return (
    <nav className="top">
      <div className="wrap">
        <a className="brand" href="#top">
          <span className="mark">R</span>
          <span>reviewpilot</span>
          <span className="domain">.work</span>
        </a>
        <a className="nav-cta" href="#pricing">
          <span className="price">${price}/mo</span> · founding member →
        </a>
      </div>
    </nav>
  );
}

function Hero({ price, list, spots }){
  return (
    <section className="hero" id="top">
      <div className="wrap">
        <div className="grid">
          <div className="col">
            <span className="eyebrow">pre-launch · {spots} founding spots</span>
            <h1 style={{marginTop:18}}>
              Know about every new review in <span className="em">~60 seconds.</span>
              <span className="stop">Then send a better reply, faster.</span>
            </h1>
            <p className="lead">
              ReviewPilot pings Slack or Discord when a Shopify App Store review lands,
              scores sentiment, and drafts a reply in your voice before it costs you installs.
            </p>
            <div className="proof-strip" role="status" aria-label="Recent detection sample">
              <span className="mono">last detected review</span>
              <span className="mono">2m ago</span>
              <span className="mono">sentiment -0.62</span>
              <span className="mono">draft ready</span>
            </div>
            <div className="cta-row">
              <a className="btn primary" href={STRIPE_URL} onClick={() => trackEvent("hero_cta_click")}>
                <span>{`Claim a founding spot — $${price}/mo`}</span>
                <span className="arrow">→</span>
              </a>
              <a className="btn ghost" href="#how"><span>How it works</span></a>
            </div>
            <div className="micro">
              <span><span className="dot"></span> ~60s after a review hits</span>
              <span>·</span>
              <span>Slack &amp; Discord webhooks</span>
              <span>·</span>
              <span>60-day refund, no questions</span>
            </div>
            <div className="hero-compare" role="note" aria-label="Before and after using ReviewPilot">
              <div className="item">
                <span className="k">without</span>
                <span className="v">manual dashboard checks, delayed response</span>
              </div>
              <div className="item">
                <span className="k">with reviewpilot</span>
                <span className="v">instant ping, scored sentiment, ready-to-edit draft</span>
              </div>
            </div>
          </div>
          <div className="col">
            <ChatMock />
          </div>
        </div>
      </div>
    </section>
  );
}

function MidCta({ price }){
  return (
    <section>
      <div className="wrap">
        <div className="mid-cta">
          <p className="mono">If a 1-star landed right now, you'd know in under a minute.</p>
          <a className="btn primary" href={STRIPE_URL} onClick={() => trackEvent("mid_cta_click")}>
            <span>{`Get founding access — $${price}/mo`}</span>
            <span className="arrow">→</span>
          </a>
        </div>
      </div>
    </section>
  );
}

function Pain(){
  return (
    <section>
      <div className="wrap">
        <span className="eyebrow">the problem</span>
        <h2 style={{marginTop:14, maxWidth:"22ch"}}>
          The Partner Dashboard isn't a real-time tool.
        </h2>
        <p className="lead" style={{marginTop:18}}>
          There's no push notification. No API for reviews. No email when a 1-star lands at 2am
          while a competitor is watching. So you do what every Shopify app maker does — open the
          tab, hit F5, close it, repeat. Forever.
        </p>

        <div className="pain-grid" style={{marginTop:36}}>
          <div className="cell">
            <span className="num mono">5–12×</span>
            <span className="lbl">times a day you check for new reviews, by hand.</span>
          </div>
          <div className="cell">
            <span className="num mono">6–18h</span>
            <span className="lbl">typical delay before you even see a negative review.</span>
          </div>
          <div className="cell">
            <span className="num mono">~3min</span>
            <span className="lbl">how long after a 1-star it shapes a stranger's first impression.</span>
          </div>
        </div>
      </div>
    </section>
  );
}

function Features(){
  const items = [
    {
      ico: "⌁",
      h: "Pings within ~60 seconds.",
      p: "We poll the public listing aggressively. The moment a review appears, the message hits your channel. No browser extension. No Partner API hacks.",
      demo: "POST /webhook · 18:42:07 · status: delivered"
    },
    {
      ico: "≈",
      h: "Sentiment + topics, not just stars.",
      p: "A 3-star can be a love letter or a quiet 'I'm switching apps'. Every review gets a sentiment score from −1.0 to +1.0 and 2–4 topic tags so you triage in a glance.",
      demo: "sentiment: −0.62  ·  #billing  #checkout-bug"
    },
    {
      ico: "✎",
      h: "Reply drafted in your voice.",
      p: "ReviewPilot reads your last ~30 replies (paste them in once) and drafts in your tone. Specific, contrite where it should be, never the corporate-PR mush. You always approve before sending.",
      demo: "draft ready · 178 chars · matches your last 12 replies"
    },
    {
      ico: "⌥",
      h: "All your apps, one channel.",
      p: "Ship two apps? Six? Route everything to #app-reviews, or split per-app, or per-rating. Webhook in, JSON out — bend it however you want.",
      demo: "apps tracked: 4  ·  channels: 3"
    }
  ];
  return (
    <section id="features">
      <div className="wrap">
        <span className="eyebrow">what you actually get</span>
        <h2 style={{marginTop:14, maxWidth:"24ch"}}>
          Four things. Done well.
        </h2>
        <div className="feature-grid" style={{marginTop:36}}>
          {items.map((it, i) => (
            <div className="feature" key={i}>
              <div className="ico" aria-hidden>{it.ico}</div>
              <h3>{it.h}</h3>
              <p>{it.p}</p>
              <div className="demo mono">{it.demo}</div>
            </div>
          ))}
        </div>
      </div>
    </section>
  );
}

function How(){
  return (
    <section id="how" className="anchor">
      <div className="wrap">
        <span className="eyebrow">how it works</span>
        <h2 style={{marginTop:14, maxWidth:"22ch"}}>
          Three steps. ~4 minutes total.
        </h2>

        <div className="steps" style={{marginTop:40}}>
          <div className="step">
            <span className="n">01</span>
            <h3>Drop in a webhook.</h3>
            <p>
              Paste a Slack <span className="mono">incoming-webhook</span> URL or a Discord one. Nothing
              else to install — no OAuth, no workspace app, no permissions audit with your CTO.
            </p>
          </div>
          <div className="step">
            <span className="n">02</span>
            <h3>Tell us your apps.</h3>
            <p>
              Paste the App Store listing URLs for the apps you want watched. One or fifty.
              Optionally drop in ~30 of your past replies so the AI matches your voice.
            </p>
          </div>
          <div className="step">
            <span className="n">03</span>
            <h3>Close the tab.</h3>
            <p>
              That's it. Next review that lands, you'll know in your channel within ~60s, with
              sentiment scored and a reply drafted. Approve, edit, or ignore.
            </p>
          </div>
        </div>
      </div>
    </section>
  );
}

function Pricing({ price, list, spots, spotsTaken }){
  const remaining = Math.max(0, spots - spotsTaken);
  return (
    <section id="pricing" className="anchor">
      <div className="wrap">
        <span className="eyebrow">founding members</span>
        <h2 style={{marginTop:14, maxWidth:"24ch"}}>
          {spots} spots. Forever-locked price.
        </h2>
        <p className="lead" style={{marginTop:18}}>
          I'm taking on {spots} founding members before public launch. You get a price
          that never goes up, direct access to me, and meaningful say on what ships in v1.
        </p>

        <div className="pricing-card" style={{marginTop:36}}>
          <span className="tag">Founding · locked forever</span>

          <div className="price-row">
            <span className="price">
              ${price}<span className="slash">/</span><span style={{fontSize:".4em", fontWeight:500, color:"var(--muted)"}}>mo</span>
            </span>
            <span className="list-price mono">at launch ${list}/mo</span>
          </div>

          <div className="what-row">
            <div className="offer-list">
              <h3>What you get</h3>
              <ul>
                <li>Every founding feature, forever — no "founders tier got deprecated" emails.</li>
                <li>Track unlimited Shopify apps from one workspace.</li>
                <li>Slack &amp; Discord. Webhook out for anything else.</li>
                <li>AI-drafted replies tuned on your past 30 responses.</li>
                <li>Direct line to the founder for feature requests. They actually ship.</li>
              </ul>
            </div>
            <div className="offer-list no-list">
              <h3>What you don't deal with</h3>
              <ul>
                <li className="no">No fake testimonials. No fake user counts. No "join 10,000 founders".</li>
                <li className="no">No annual lock-in, no setup fee, no Calendly hoop to jump through.</li>
              </ul>
            </div>

            <div className="cta-block">
              <div>
                <div className="mono" style={{fontSize:12, color:"var(--muted)", marginBottom:10, letterSpacing:".06em", textTransform:"uppercase"}}>
                  spots remaining
                </div>
                <div className="spots" role="img" aria-label={`${remaining} of ${spots} spots open`}>
                  {Array.from({length: spots}).map((_, i) => (
                    <span key={i} className={`seat ${i < spotsTaken ? "taken" : "open"}`}></span>
                  ))}
                  <span className="count mono">{remaining}/{spots}</span>
                </div>
              </div>

              <a className="btn primary" href={STRIPE_URL} onClick={() => trackEvent("pricing_cta_click")}>
                <span>{`Claim a spot — $${price}/mo`}</span>
                <span className="arrow">→</span>
              </a>

              <p className="fineprint">
                Refundable within 60 days, no questions, no email reply chain.<br />
                Stripe handles the billing. Cancel any time, keep what you got.
              </p>
              <div className="checkout-flow" aria-label="What happens after payment">
                <span className="flow-title mono">after payment</span>
                <ol>
                  <li>Instant Stripe receipt</li>
                  <li>Webhook setup link in under 24h</li>
                  <li>First live review ping to your channel</li>
                </ol>
              </div>
              <p className="promise mono">From payment to first ping in under 24 hours, or I refund you.</p>
            </div>
          </div>
        </div>
      </div>
    </section>
  );
}

function Fit(){
  return (
    <section>
      <div className="wrap">
        <span className="eyebrow">fit check</span>
        <h2 style={{marginTop:14, maxWidth:"24ch"}}>Who this is for (and who should skip it).</h2>
        <div className="fit-grid" style={{marginTop:30}}>
          <div className="fit-card">
            <h3>For you if...</h3>
            <ul>
              <li>You check Shopify reviews daily by hand.</li>
              <li>You care about fast public responses to low ratings.</li>
              <li>You want Slack or Discord alerts, not another dashboard.</li>
            </ul>
          </div>
          <div className="fit-card no-fit">
            <h3>Skip if...</h3>
            <ul>
              <li>You want a free forever plan.</li>
              <li>You only need quarterly review summaries.</li>
              <li>You expect auto-sending replies without approval.</li>
            </ul>
          </div>
        </div>
      </div>
    </section>
  );
}

function Founder(){
  return (
    <section>
      <div className="wrap">
        <span className="eyebrow">who's building this</span>
        <h2 style={{marginTop:14, maxWidth:"22ch"}}>
          One person. Solo.
        </h2>

        <div className="founder" style={{marginTop:36}}>
          <div className="copy" style={{gridColumn:"1 / -1"}}>
            <p>
              Built by a Shopify app developer who spent the first six months after launch
              refreshing the Partner Dashboard like a maniac, terrified of missing a 1-star
              long enough for it to do real damage. The Partner API doesn't expose reviews.
              The dashboard doesn't push. Nothing on the App Store helps.
            </p>
            <p>
              ReviewPilot started as a personal tool. It's been pinging in Slack for four
              months. It changed the job from "anxiously refresh" to "respond well, then
              go back to building". A handful of other indie app makers might want the same thing.
            </p>
            <p>
              It's pre-launch. No other customers yet — you'd be among the first. That's
              the deal: cheap price, locked forever, real input on v1, and a founder who
              replies to email personally. Identity shared on signup.
            </p>
          </div>
        </div>
      </div>
    </section>
  );
}

function Faq({ price, list, spots }){
  const items = [
    {
      q: `Why $${price}? Why a founding tier at all?`,
      a: <>
        <p className="ans">
          Because you're betting on a pre-launch product made by one person. The price reflects
          that risk. After launch it goes to ${list}/mo — you'll never pay that. Lock the rate
          now and keep it as long as you stay subscribed, even if I ship enterprise tiers later.
        </p>
      </>
    },
    {
      q: "Why no free trial?",
      a: <p className="ans">
        Free trials filter for people who want free things. Founding members filter for people
        who want this thing. The 60-day refund is the safety net — if it doesn't earn its
        keep, you ask for it back, and I send it back. No support ticket gauntlet.
      </p>
    },
    {
      q: "What if I cancel?",
      a: <p className="ans">
        Cancel any time in the Stripe portal — no email back-and-forth. If you come back later,
        the founding price comes back with you (one re-entry, so I'm not running a revolving door).
      </p>
    },
    {
      q: "Does it auto-send replies?",
      a: <p className="ans">
        No. Every reply is drafted, never sent. You approve, edit, or ignore. Auto-sending into
        an angry customer's thread is exactly how indie apps acquire a reputation problem.
      </p>
    },
    {
      q: "How fast is the ping, really?",
      a: <p className="ans">
        Typically 30–90 seconds after the review appears on the public listing. We poll the
        listing on a tight schedule. If Shopify ever exposes reviews via the Partner API, we
        flip to push instantly and the speed gets even better.
      </p>
    },
    {
      q: "What about Shopify Theme reviews / Apple App Store / Google Play?",
      a: <p className="ans">
        Shopify App Store only for v1. Theme reviews are on the list for v1.1. Apple/Google
        only if a founding member is shipping there and asks — I'd rather do one store
        excellently than five poorly.
      </p>
    },
    {
      q: "Where's my data living?",
      a: <p className="ans">
        Reviews are public. Your webhook URL and the last ~30 replies you optionally paste in
        are stored encrypted in a single managed Postgres in EU-Central. No third-party
        analytics, no Segment, no behavior tracking. Delete-on-cancel by default.
      </p>
    },
    {
      q: "When does it launch?",
      a: <p className="ans">
        The core (pings + sentiment + drafts for Shopify) works today — founding members get
        access within 24 hours of signing up. Public launch is targeting late 2026 Q2. Founding
        members shape what gets in before then.
      </p>
    }
  ];

  return (
    <section id="faq" className="anchor">
      <div className="wrap">
        <span className="eyebrow">questions</span>
        <h2 style={{marginTop:14, maxWidth:"22ch"}}>
          Things you'll probably want to ask.
        </h2>
        <div className="faq" style={{marginTop:28}}>
          {items.map((it, i) => (
            <details key={i} onToggle={(e) => e.currentTarget.open && trackEvent("faq_open", { index: i })}>
              <summary>
                <span>{it.q}</span>
                <span className="toggle" aria-hidden>＋</span>
              </summary>
              {it.a}
            </details>
          ))}
        </div>
      </div>
    </section>
  );
}

function FinalCta({ price }){
  return (
    <section>
      <div className="wrap">
        <div className="strip">
          <div>
            <h2>Stop F5-ing. Start building.</h2>
            <p style={{marginTop:8, color:"var(--muted)", fontFamily:"var(--font-mono)", fontSize:13}}>
              Founding-member access · 60-day refund · cancel any time
            </p>
          </div>
          <div className="right">
            <a className="btn primary" href={STRIPE_URL} onClick={() => trackEvent("final_cta_click")}>
              <span>{`Claim a spot — $${price}/mo`}</span>
              <span className="arrow">→</span>
            </a>
            <span className="mono dim" style={{fontSize:11}}>secure checkout via Stripe</span>
          </div>
        </div>
      </div>
    </section>
  );
}

function Footer(){
  return (
    <footer className="foot">
      <div className="wrap">
        <span>© 2026 reviewpilot.work · built by one person.</span>
        <span>
          <a href="#top">back to top ↑</a>
        </span>
      </div>
    </footer>
  );
}

function MobileStickyCta({ price }){
  return (
    <div className="mobile-cta" role="region" aria-label="Sticky mobile call to action">
      <div className="mobile-cta-inner">
        <div>
          <div className="label mono">Founding member</div>
          <div className="price">${price}/mo · 60-day refund</div>
        </div>
        <a className="btn primary" href={STRIPE_URL} onClick={() => trackEvent("sticky_cta_click")}>
          <span>Claim spot</span>
          <span className="arrow">→</span>
        </a>
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────────
// App
// ─────────────────────────────────────────────────────────────────
function App(){
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);

  // Apply accent color live
  useEffect(() => {
    const root = document.documentElement;
    root.style.setProperty("--accent", t.accent);
  }, [t.accent]);

  return (
    <>
      <Nav price={t.founderPrice} />
      <Hero price={t.founderPrice} list={t.listPrice} spots={t.spots} />
      {t.showPain && <Pain />}
      <Features />
      <MidCta price={t.founderPrice} />
      {t.showHow && <How />}
      <Fit />
      <Pricing price={t.founderPrice} list={t.listPrice} spots={t.spots} spotsTaken={t.spotsTaken} />
      <Founder />
      <Faq price={t.founderPrice} list={t.listPrice} spots={t.spots} />
      <FinalCta price={t.founderPrice} />
      <Footer />
      <MobileStickyCta price={t.founderPrice} />

      <TweaksPanel title="Tweaks">
        <TweakSection label="Accent" />
        <TweakColor
          label="Color"
          value={t.accent}
          options={["#f5b94b","#7dd3a3","#86a8e7","#e87a7a","#c8a6ff"]}
          onChange={v => setTweak("accent", v)}
        />
        <TweakSection label="Offer" />
        <TweakNumber
          label="Founding price ($/mo)"
          value={t.founderPrice}
          min={9} max={99} step={1}
          onChange={v => setTweak("founderPrice", v)}
        />
        <TweakNumber
          label="List price ($/mo)"
          value={t.listPrice}
          min={t.founderPrice + 1} max={199} step={1}
          onChange={v => setTweak("listPrice", v)}
        />
        <TweakSlider
          label="Founding spots"
          value={t.spots}
          min={3} max={20} step={1}
          onChange={v => setTweak("spots", v)}
        />
        <TweakSlider
          label="Spots taken"
          value={t.spotsTaken}
          min={0} max={t.spots} step={1}
          onChange={v => setTweak("spotsTaken", v)}
        />
        <TweakSection label="Sections" />
        <TweakToggle
          label="Show pain stats"
          value={t.showPain}
          onChange={v => setTweak("showPain", v)}
        />
        <TweakToggle
          label="Show how-it-works"
          value={t.showHow}
          onChange={v => setTweak("showHow", v)}
        />
      </TweaksPanel>
    </>
  );
}

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