// Premium Companies Don't Discount, main app
const { useState, useEffect, useRef, useMemo } = React;

const CHANNELS = [
  { id: "social", label: "Social" },
  { id: "email", label: "Email" },
  { id: "seo", label: "SEO" },
  { id: "paid", label: "Paid Ads" },
  { id: "referral", label: "Referral" },
  { id: "other", label: "Other" },
];

const REVENUE_TIERS = [
  { id: "0-10k", label: "< $10K" },
  { id: "10-50k", label: "$10–50K" },
  { id: "50-250k", label: "$50–250K" },
  { id: "250k-1m", label: "$250K–1M" },
  { id: "1m+", label: "$1M+" },
];

const CATEGORIES = [
  { key: "positioning", num: "01", label: "Strategy", title: "Positioning" },
  { key: "premium_offerings", num: "02", label: "Product", title: "The hero offer" },
  { key: "retention", num: "03", label: "Loyalty", title: "What keeps them" },
];

const LOADING_BEATS = [
  "Auditing your positioning",
  "Finding your hero offer",
  "Drafting what keeps them",
];

// ============================================
// Attio integration  (server-side — no key in this file)
// ============================================
// The Attio key is NEVER in this front-end code. Submissions are POSTed to
// the serverless function at api/submit.js, which reads the key from the
// Vercel env var ATTIO_API_KEY and writes a person, deal, and note to Attio.
// That function is also where the bot-abuse protections live (origin
// allowlist, honeypot, validation). See saveSubmission() below and README.md.

// ============================================
// Contact, where "talk it through" leads
// ============================================
// Two follow-up CTAs sit at the bottom of the results page. Fill in whichever
// you want active, leave the other blank to hide it. The email is opened with
// a prefilled subject and body referencing the visitor's business name and
// summary so the conversation starts in context.
const CONTACT = {
  // Reply-to address for the "Email us" button. Blank = hide the button.
  email:       "",                    // e.g. "hello@yourstudio.co"
  // Public booking URL (Cal.com, Savvycal, Calendly, etc). Blank = hide.
  bookingUrl:  "",                    // e.g. "https://cal.com/yourname/30min"
  // Optional, shown as a small "Avg. response time" line under the CTAs.
  responseHint: "Usually within a working day.",
};

// Submissions are saved through the protected Vercel function at
// /api/submit. That function holds the Attio key server-side
// (Vercel env var ATTIO_API_KEY) and guards against bot abuse
// with an origin allowlist, a honeypot, and validation. The browser NEVER
// sees the key — that's the whole point, and what prevents the key being
// scraped out of the page and rate-limited.
async function saveSubmission(form, summary) {
  const channelLabels = (form.channels || [])
    .map((c) => CHANNELS.find((cc) => cc.id === c)?.label)
    .filter(Boolean)
    .join(", ");
  const revLabel = REVENUE_TIERS.find((r) => r.id === form.revenue)?.label || form.revenue || "";

  try {
    const res = await fetch("/api/submit", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        businessName:    form.business || "",
        offer:           form.sells || "",
        customer:        form.customer || "",
        channels:        channelLabels,
        scale:           revLabel,
        friction:        form.challenge || "",
        firstName:       form.firstName || "",
        email:           form.email || "",
        consent:         !!form.followUp,
        // Honeypot: real users never fill this. Bots usually do, and the
        // function silently drops anything where it's non-empty.
        company_website: form.company_website || "",
        // AI summary text, stored in the Attio note.
        summary:         summary || "",
      }),
    });
    if (!res.ok) {
      const text = await res.text().catch(() => "");
      console.warn("Submit failed:", res.status, text);
      return { ok: false, status: res.status, error: text };
    }
    return { ok: true };
  } catch (e) {
    console.warn("Submit network error:", e);
    return { ok: false, error: e.message || "network" };
  }
}

// ============================================
// Discount Sticker, vintage hand-painted price graphic
// ============================================
// Real photographed price-tag stickers used as the editorial motif on a
// "premium companies don't discount" page. Two variants ship with the
// project, pick whichever reads better in context.
const STICKER_SRC = {
  yellow:    "assets/sticker-yellow-melon.png",
  music:     "assets/sticker-music.png",
  square:    "assets/sticker-square.png",
  price:     "assets/sticker-price289.png",
  records:   "assets/sticker-records.png",
  price436:  "assets/sticker-price436.png",
  special:   "assets/sticker-special-sale.png",
  hmeluns:   "assets/sticker-hmeluns-black.png",
  blacklbl:  "assets/sticker-black-label-pink.png",
  faded:     "assets/sticker-special-faded.png",
};
const STICKER_ALT = {
  yellow:    "Hand-painted yellow price sticker reading 'Special $5.99 Hmeluns'",
  music:     "Hand-painted price tag reading 'HMLNS MUSIC $6.49'",
  square:    "Kraft-paper price sticker reading 'Specially Priced $15.99'",
  price:     "Round hand-painted price label reading 'PRICE 2.89$ LABEL'",
  records:   "Pinking-sheared cream sticker reading 'HMELUNS RECORDS $1.89'",
  price436:  "Square aged sticker reading 'PRICE $4.36' with scribbles",
  special:   "Torn red-and-cream sticker reading 'SPECIAL SALE $1.67'",
  hmeluns:   "Stacked green-and-yellow sticker reading 'HMELUNS BLACK $1.99 LABEL TEMP.'",
  blacklbl:  "Soft-pink rectangular sticker reading 'black label €1.99'",
  faded:     "Layered faded-cream tape stickers reading 'SPECIAL SALE $1.67'",
};

function Sticker({
  variant = "yellow",
  rotate = -4,
  size = "md",
  className = "",
  style = {},
}) {
  return (
    <span
      className={`sticker-wrap sticker-${size} sticker-${variant} ${className}`}
      style={{ ...style, "--rot": `${rotate}deg` }}
    >
      <img src={STICKER_SRC[variant]} alt={STICKER_ALT[variant]} draggable="false" />
    </span>
  );
}

// ============================================
// StickerBand, overlapping collage row of stickers, anchored to a bottom edge.
// Reads like a torn strip of price tags stuck on a felt board, with light scratch
// marks across them. Used as a section-edge motif on the manifesto / playbook.
// ============================================
function StickerBand({ className = "", align = "bottom" }) {
  // Hand-tuned layout, left%, rotation, variant, vertical-bias, scale.
  // Order matters: later items render on top.
  const tiles = [
    { v: "music",  l: -4,  rot: -8,  y: 12,  s: 1.05, z: 1 },
    { v: "square", l: 9,   rot:  6,  y: 6,   s: 1.10, z: 2 },
    { v: "price",  l: 22,  rot: -4,  y: 22,  s: 1.00, z: 3 },
    { v: "yellow", l: 36,  rot: -3,  y: 14,  s: 1.00, z: 4 },
    { v: "music",  l: 50,  rot:  9,  y: 2,   s: 0.95, z: 5 },
    { v: "price",  l: 63,  rot:  7,  y: 18,  s: 0.95, z: 6 },
    { v: "square", l: 76,  rot: -5,  y: 12,  s: 1.05, z: 7 },
    { v: "yellow", l: 90,  rot:  4,  y: 8,   s: 1.00, z: 8 },
  ];
  return (
    <div className={`sticker-band sticker-band-${align} ${className}`} aria-hidden="true">
      <div className="sticker-band-inner">
        {tiles.map((t, i) => (
          <span
            key={i}
            className={`band-tile band-tile-${t.v}`}
            style={{
              left: `${t.l}%`,
              bottom: `${t.y}%`,
              transform: `translate(-50%, 0) rotate(${t.rot}deg) scale(${t.s})`,
              zIndex: t.z,
            }}
          >
            <img src={STICKER_SRC[t.v]} alt="" draggable="false" />
          </span>
        ))}
        {/* tape / scratch streaks across the band */}
        <span className="band-streak band-streak-1"></span>
        <span className="band-streak band-streak-2"></span>
        <span className="band-streak band-streak-3"></span>
      </div>
    </div>
  );
}

// ============================================
// CornerStickers, decorative price-tags pinned to top corners of the page
// ============================================
// Editorial motif: crowded clusters of hand-painted price stickers slapped
// into the upper-left and upper-right of the viewport like a kiosk window
// that's been re-marked-down a dozen times. Each tile has its own offset,
// rotation, and scale so the cluster reads as overlapping, not aligned.
// Purely decorative, pointer-events disabled so they never block clicks.
function CornerStickers() {
  // [variant, top%, side%, rotation, width(px), z]
  // side% = distance from the corner edge of the cluster (left for TL, right for TR)
  // Corner clusters use the four NEW stickers (records, price436, special, hmeluns).
  // The original four (music, square, price, yellow) are reserved for the StickerBand
  // beneath the hero so the two motifs don't echo each other.
  // Each corner uses exactly four distinct stickers (no repeats within a corner).
  // Left and right corners use different rotations/sizes/orderings so the two
  // clusters don't mirror each other. The StickerBand beneath the hero handles
  // the original four variants so the corner motif stays distinct.
  // Six distinct corner stickers, distributed so no sticker repeats within
  // a corner and each side has its own subset. Left/right share some types
  // but in different sizes, rotations and stack orders so the two clusters
  // don't read as mirrors of each other.
  const left = [
    { v: "blacklbl", t: -28, s: -34, r: -16, w: 200, z: 1 },
    { v: "records",  t:  -8, s:  78, r:   9, w: 230, z: 2 },
    { v: "hmeluns",  t:  68, s: -22, r:  -8, w: 220, z: 3 },
    { v: "price436", t:  50, s:  56, r:  -3, w: 190, z: 4 },
    { v: "faded",    t: 122, s:  22, r:  11, w: 210, z: 5 },
  ];
  const right = [
    { v: "special",  t: -14, s: -28, r:   9, w: 240, z: 1 },
    { v: "faded",    t:  22, s:  74, r:  -8, w: 220, z: 2 },
    { v: "records",  t:  66, s: -20, r:  -6, w: 200, z: 3 },
    { v: "hmeluns",  t:  46, s:  50, r:  13, w: 210, z: 4 },
    { v: "blacklbl", t: 130, s:  18, r:  -4, w: 190, z: 5 },
  ];
  return (
    <div className="corner-stickers" aria-hidden="true">
      <div className="corner-cluster corner-cluster-tl">
        {left.map((t, i) => (
          <span
            key={i}
            className="cluster-tile"
            style={{
              top: `${t.t}px`,
              left: `${t.s}px`,
              width: `${t.w}px`,
              transform: `rotate(${t.r}deg)`,
              zIndex: t.z,
            }}
          >
            <img src={STICKER_SRC[t.v]} alt="" draggable="false" />
          </span>
        ))}
      </div>
      <div className="corner-cluster corner-cluster-tr">
        {right.map((t, i) => (
          <span
            key={i}
            className="cluster-tile"
            style={{
              top: `${t.t}px`,
              right: `${t.s}px`,
              width: `${t.w}px`,
              transform: `rotate(${t.r}deg)`,
              zIndex: t.z,
            }}
          >
            <img src={STICKER_SRC[t.v]} alt="" draggable="false" />
          </span>
        ))}
      </div>
    </div>
  );
}

// ============================================
// Header
// ============================================
function Header({ onHome, onScrollTo }) {
  return (
    <header className="header">
      <div className="wordmark" onClick={onHome}>
        <div className="mark">Premium<span className="em">.</span></div>
        <div className="tag">A diagnostic for founders</div>
      </div>
      <nav className="header-nav">
        <a onClick={() => onScrollTo("manifesto")}>Manifesto</a>
        <a onClick={() => onScrollTo("intake")}>Diagnostic</a>
        <a onClick={() => onScrollTo("playbook")}>Playbook</a>
      </nav>
    </header>
  );
}

// ============================================
// Hero / Manifesto
// ============================================
function Hero({ onStart }) {
  return (
    <section id="manifesto" className="hero-section" style={{ borderBottom: "1px solid var(--hairline)" }}>
      <div className="shell hero">
        <h1>
          Premium<br/>
          companies<br/>
          <span className="serif-italic">don't </span>
          <span className="accent">discount.</span>
        </h1>
        <div className="hero-side">
          <p>
            They charge what the work is worth. They
            keep customers because the alternative is unimaginable, not because the next
            email has 20% off.
          </p>
          <p style={{ fontSize: 17 }}>
            Tell us about your business below. We'll return your next three moves.
          </p>
        </div>
        <div className="hero-meta">
          <div>
            <span className="num">03</span>
            Moves
          </div>
          <div>
            <span className="num">~9</span>
            Tactical plays
          </div>
          <div>
            <span className="num">3m</span>
            Avg. fill time
          </div>
        </div>
      </div>
      <StickerBand align="bottom" />
    </section>
  );
}

// ============================================
// Intake form
// ============================================
function IntakeForm({ onSubmit }) {
  const [form, setForm] = useState({
    business: "",
    sells: "",
    customer: "",
    channels: [],
    revenue: "",
    challenge: "",
    firstName: "",
    email: "",
    followUp: false,
  });
  const [touched, setTouched] = useState(false);

  const update = (k, v) => setForm((f) => ({ ...f, [k]: v }));
  const toggleChannel = (id) =>
    setForm((f) => ({
      ...f,
      channels: f.channels.includes(id)
        ? f.channels.filter((c) => c !== id)
        : [...f.channels, id],
    }));

  const emailOk = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(form.email.trim());
  const valid =
    form.business.trim().length > 1 &&
    form.sells.trim().length > 3 &&
    form.customer.trim().length > 3 &&
    form.channels.length > 0 &&
    form.revenue &&
    form.challenge.trim().length > 5 &&
    emailOk;

  const handleSubmit = (e) => {
    e.preventDefault();
    setTouched(true);
    if (!valid) return;
    onSubmit(form);
  };

  return (
    <section id="intake" className="form-section">
      <div className="shell">
        <div className="form-head">
          <div className="eyebrow"><span className="dot"></span>Step 01 · Diagnostic</div>
          <h2>The <span className="em">intake</span>.</h2>
          <p className="lede">
            A handful of questions. Answer like you'd answer a strategist you trust, short,
            specific, and a little opinionated. Vague in, vague out.
          </p>
        </div>

        <form className="intake" onSubmit={handleSubmit}>
          {/* 1. Business name */}
          <div className="field span-6">
            <div className="field-label">
              <span className="num">01 / NAME</span>
              <span className="lbl">What's the business called?</span>
              <span className="hint">Your company or brand name.</span>
            </div>
            <div className="field-input">
              <input
                type="text"
                value={form.business}
                onChange={(e) => update("business", e.target.value)}
                placeholder="e.g. Foundry &amp; Wick"
                autoComplete="off"
              />
            </div>
          </div>

          {/* 2. What they sell */}
          <div className="field span-6">
            <div className="field-label">
              <span className="num">02 / OFFER</span>
              <span className="lbl">What do you sell?</span>
              <span className="hint">A sentence. Product or service, in plain language.</span>
            </div>
            <div className="field-input">
              <input
                type="text"
                value={form.sells}
                onChange={(e) => update("sells", e.target.value)}
                placeholder="e.g. small-batch ceramic tableware"
                autoComplete="off"
              />
            </div>
          </div>

          {/* 3. Target customer */}
          <div className="field">
            <div className="field-label">
              <span className="num">03 / WHO</span>
              <span className="lbl">Who is the customer, ideally?</span>
              <span className="hint">Specifics beat demographics. A few sentences is fine.</span>
            </div>
            <div className="field-input">
              <textarea
                rows={2}
                value={form.customer}
                onChange={(e) => update("customer", e.target.value)}
                placeholder="e.g. design-literate hosts, 32–50, who care about how their kitchen photographs and have given up on big-box dinnerware"
              />
            </div>
          </div>

          {/* 4. Channels */}
          <div className="field">
            <div className="field-label">
              <span className="num">04 / CHANNELS</span>
              <span className="lbl">Where are you marketing today?</span>
              <span className="hint">Pick everything you actively run.</span>
            </div>
            <div className="field-input">
              <div className="channels">
                {CHANNELS.map((c) => (
                  <button
                    type="button"
                    key={c.id}
                    className={"channel" + (form.channels.includes(c.id) ? " on" : "")}
                    onClick={() => toggleChannel(c.id)}
                  >
                    <span className="check"></span>
                    {c.label}
                  </button>
                ))}
              </div>
            </div>
          </div>

          {/* 5. Revenue */}
          <div className="field">
            <div className="field-label">
              <span className="num">05 / SCALE</span>
              <span className="lbl">Monthly revenue, roughly.</span>
              <span className="hint">Helps calibrate which plays to prioritize.</span>
            </div>
            <div className="field-input">
              <div className="revenue">
                {REVENUE_TIERS.map((r) => (
                  <button
                    type="button"
                    key={r.id}
                    className={"rev-opt" + (form.revenue === r.id ? " on" : "")}
                    onClick={() => update("revenue", r.id)}
                  >
                    {r.label}
                  </button>
                ))}
              </div>
            </div>
          </div>

          {/* 6. Challenge */}
          <div className="field">
            <div className="field-label">
              <span className="num">06 / FRICTION</span>
              <span className="lbl">Biggest growth challenge right now?</span>
              <span className="hint">The thing keeping you up. One paragraph, max.</span>
            </div>
            <div className="field-input">
              <textarea
                rows={3}
                value={form.challenge}
                onChange={(e) => update("challenge", e.target.value)}
                placeholder="e.g. acquisition costs keep climbing and the team wants to run a 25% sale to clear inventory; I think it'll cheapen the brand"
              />
            </div>
          </div>

          {/* 7. First name (optional) */}
          <div className="field span-6">
            <div className="field-label">
              <span className="num">07 / NAME</span>
              <span className="lbl">First name <span className="opt">— optional</span></span>
              <span className="hint">So we know what to call you.</span>
            </div>
            <div className="field-input">
              <input
                type="text"
                value={form.firstName}
                onChange={(e) => update("firstName", e.target.value)}
                placeholder="e.g. Mira"
                autoComplete="given-name"
              />
            </div>
          </div>

          {/* 8. Email (required) */}
          <div className="field span-6">
            <div className="field-label">
              <span className="num">08 / EMAIL</span>
              <span className="lbl">Where should we send it?</span>
              <span className="hint">Your three moves arrive here. Required.</span>
            </div>
            <div className="field-input">
              <input
                type="email"
                value={form.email}
                onChange={(e) => update("email", e.target.value)}
                placeholder="you@brand.co"
                autoComplete="email"
                inputMode="email"
              />
            </div>
          </div>

          {/* 9. Follow-up consent (optional) */}
          <div className="field">
            <div className="field-label">
              <span className="num">09 / CONSENT</span>
              <span className="lbl">Stay in touch? <span className="opt">— optional</span></span>
              <span className="hint">No newsletter spam, the occasional sharp note.</span>
            </div>
            <div className="field-input">
              <label className="consent">
                <input
                  type="checkbox"
                  checked={form.followUp}
                  onChange={(e) => update("followUp", e.target.checked)}
                />
                <span className="box" aria-hidden="true"></span>
                <span className="consent-copy">
                  Okay if we follow up?
                  <span className="consent-sub">Once or twice a quarter, only if we have something worth sending.</span>
                </span>
              </label>
            </div>
          </div>

          <div className="field" style={{ display: "block", padding: 0, gridTemplateColumns: "1fr" }}>
            <div className="submit-row" style={{ marginTop: 0, padding: "28px 32px", borderTop: "none" }}>
              <p className="submit-note">
                {touched && !valid
                  ? "A couple of fields still need attention, fill them in and we'll generate your three moves."
                  : "We'll generate your three moves in about 20 seconds. Your answers stay between us."}
              </p>
              <button className="btn" type="submit" disabled={!valid && touched}>
                Generate my three moves
                <span className="arrow">→</span>
              </button>
            </div>
          </div>
        </form>
      </div>
    </section>
  );
}

// ============================================
// Loading state
// ============================================
function Loading() {
  const [tick, setTick] = useState(0);
  useEffect(() => {
    const id = setInterval(() => setTick((t) => Math.min(t + 1, LOADING_BEATS.length - 1)), 1500);
    return () => clearInterval(id);
  }, []);

  return (
    <section className="loading" id="playbook">
      <div className="loading-inner">
        <div className="eyebrow"><span className="dot"></span>Generating · Step 02 of 02</div>
        <h2>
          Reading your inputs.<br/>
          Drafting your <span className="accent">three moves</span>.
        </h2>
        <p style={{ fontFamily: "var(--font-serif)", fontSize: 19, color: "var(--ink-soft)", margin: "0 auto", maxWidth: "44ch" }}>
          Premium businesses earn their margin in slow, patient moves.
          Hold tight, we're sequencing yours.
        </p>
        <div className="beats">
          {LOADING_BEATS.map((b, i) => (
            <div key={i} className={"beat " + (i < tick ? "done" : i === tick ? "active" : "")}>
              <span className="marker">{i < tick ? "" : i === tick ? "" : String(i + 1).padStart(2, "0")}</span>
              <span>{b}</span>
            </div>
          ))}
        </div>
      </div>
    </section>
  );
}

// ============================================
// PDF export, open a print-ready window
// ============================================
// window.print() is silently blocked in sandboxed artifact iframes. The
// workaround: open a new top-level window with a self-contained print stylesheet
// (white paper, black ink, all three cards expanded), then trigger print there.
// If popup is blocked, fall back to direct print on the current page.
function savePlaybookPdf({ form, recs, summary }) {
  const channelLabels = form.channels
    .map((c) => CHANNELS.find((cc) => cc.id === c)?.label)
    .filter(Boolean);
  const revLabel = REVENUE_TIERS.find((r) => r.id === form.revenue)?.label || form.revenue;

  const escape = (s = "") =>
    String(s).replace(/[&<>"']/g, (c) => ({
      "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;",
    }[c]));

  const cardHtml = CATEGORIES.map((cat) => {
    const data = recs[cat.key];
    if (!data) return "";
    const plays = (data.plays || [])
      .map(
        (p, i) => `
        <div class="play">
          <div class="play-num">P/${String(i + 1).padStart(2, "0")}</div>
          <div>
            <h4>${escape(p.name)}</h4>
            <p>${escape(p.detail)}</p>
          </div>
        </div>`
      )
      .join("");
    return `
      <section class="card">
        <header class="card-head">
          <div class="card-num">${cat.num}</div>
          <div class="card-title">
            <span class="label">${escape(cat.label)}</span>
            <h3>${escape(cat.title)}</h3>
          </div>
        </header>
        <p class="thesis">${escape(data.thesis)}</p>
        <div class="plays">${plays}</div>
      </section>
    `;
  }).join("");

  const pillsHtml = [
    `<span class="pill accent">${escape(revLabel)}/mo</span>`,
    `<span class="pill">${escape(form.sells)}</span>`,
    ...channelLabels.slice(0, 4).map((l) => `<span class="pill">${escape(l)}</span>`),
  ].join("");

  const greeting = form.firstName
    ? `Thanks, <em>${escape(form.firstName)}</em>.`
    : `Thanks for filling out the diagnostic.`;
  const followUpLine = form.followUp
    ? `Yes <span class="note">— a sharp note once or twice a quarter, never a discount.</span>`
    : `No <span class="note">— we'll leave you alone.</span>`;

  const doc = `<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Three moves for ${escape(form.business || "your brand")}</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Instrument+Sans:wght@400;500;600&family=Instrument+Serif:ital@0;1&display=swap" rel="stylesheet" />
<style>
  :root {
    --ink: #1a1a1a;
    --ink-soft: #555;
    --muted: #8a8a8a;
    --hairline: #d8d4cc;
    --hairline-strong: #b3aea5;
    --accent: #c84a18;
    --paper: #fbfaf6;
    --font-display: "Instrument Serif", Georgia, serif;
    --font-serif: "Instrument Serif", Georgia, serif;
    --font-sans: "Instrument Sans", -apple-system, "Segoe UI", sans-serif;
    --font-mono: "Instrument Sans", monospace;
  }
  * { box-sizing: border-box; }
  html, body { margin: 0; padding: 0; }
  body {
    background: var(--paper);
    color: var(--ink);
    font-family: var(--font-sans);
    font-size: 14px;
    line-height: 1.5;
    -webkit-font-smoothing: antialiased;
    padding: 56px 64px 80px;
    max-width: 8.5in;
    margin: 0 auto;
  }
  @page { size: letter; margin: 0.6in; }
  @media print {
    body { padding: 0; }
  }

  .wordmark {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    padding-bottom: 16px;
    border-bottom: 1px solid var(--hairline);
    margin-bottom: 28px;
  }
  .wordmark .mark {
    font-family: var(--font-display);
    font-size: 28px;
    letter-spacing: -0.02em;
  }
  .wordmark .mark em { color: var(--accent); font-style: normal; }
  .wordmark .meta {
    font-family: var(--font-mono);
    font-size: 11px;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--muted);
  }

  .receipt {
    display: grid;
    grid-template-columns: 140px 1fr;
    gap: 24px;
    padding: 18px 0 24px;
    border-top: 1px solid var(--accent);
    border-bottom: 1px solid var(--hairline);
    margin-bottom: 28px;
  }
  .receipt-num {
    font-family: var(--font-display);
    font-size: 36px;
    line-height: 1;
    color: var(--accent);
  }
  .receipt-label {
    font-family: var(--font-mono);
    font-size: 10px;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--ink-soft);
    margin-top: 4px;
  }
  .greet {
    font-family: var(--font-serif);
    font-size: 20px;
    line-height: 1.35;
    margin: 0 0 14px;
  }
  .greet em { color: var(--accent); }
  .meta-rows { list-style: none; margin: 0; padding: 12px 0 0; border-top: 1px dashed var(--hairline-strong); display: flex; flex-direction: column; gap: 6px; }
  .meta-rows li { display: grid; grid-template-columns: 120px 1fr; gap: 12px; font-size: 13px; }
  .meta-rows .k { font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.14em; text-transform: uppercase; color: var(--muted); }
  .meta-rows .note { font-style: italic; color: var(--ink-soft); margin-left: 4px; font-family: var(--font-serif); }

  h1 {
    font-family: var(--font-display);
    font-weight: 400;
    font-size: 52px;
    line-height: 0.95;
    letter-spacing: -0.025em;
    margin: 16px 0 14px;
  }
  h1 em { color: var(--accent); font-style: normal; }
  .summary {
    font-family: var(--font-serif);
    font-size: 17px;
    line-height: 1.45;
    color: var(--ink);
    max-width: 60ch;
    margin: 0 0 18px;
  }
  .pills { display: flex; flex-wrap: wrap; gap: 6px; font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink-soft); margin-bottom: 36px; }
  .pill { padding: 3px 10px; border: 1px solid var(--hairline-strong); border-radius: 999px; }
  .pill.accent { color: var(--accent); border-color: var(--accent); }

  .card {
    border-top: 1px solid var(--ink);
    padding: 22px 0 26px;
    page-break-inside: avoid;
    break-inside: avoid;
  }
  .card:last-of-type { border-bottom: 1px solid var(--ink); }
  .card-head { display: flex; align-items: flex-start; gap: 22px; margin-bottom: 12px; }
  .card-num {
    font-family: var(--font-display);
    font-size: 56px;
    line-height: 0.9;
    color: var(--accent);
    min-width: 90px;
  }
  .card-title .label {
    font-family: var(--font-mono);
    font-size: 10px;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--muted);
    display: block;
    margin-bottom: 4px;
    margin-top: 8px;
  }
  .card-title h3 {
    font-family: var(--font-display);
    font-weight: 400;
    font-size: 30px;
    letter-spacing: -0.015em;
    margin: 0;
    line-height: 1.05;
  }
  .thesis {
    font-family: var(--font-serif);
    font-size: 17px;
    line-height: 1.45;
    color: var(--ink);
    margin: 6px 0 18px;
    max-width: 64ch;
  }
  .plays { display: grid; grid-template-columns: 1fr; gap: 14px; }
  .play { display: grid; grid-template-columns: 60px 1fr; gap: 16px; padding: 12px 0; border-top: 1px dashed var(--hairline); }
  .play:first-child { border-top: 1px dashed var(--hairline-strong); }
  .play-num {
    font-family: var(--font-mono);
    font-size: 11px;
    letter-spacing: 0.12em;
    color: var(--muted);
  }
  .play h4 {
    font-family: var(--font-sans);
    font-size: 16px;
    font-weight: 600;
    margin: 0 0 4px;
    letter-spacing: -0.005em;
  }
  .play p {
    font-family: var(--font-sans);
    font-size: 14px;
    line-height: 1.5;
    color: var(--ink-soft);
    margin: 0;
    max-width: 64ch;
  }

  footer.foot {
    margin-top: 40px;
    padding-top: 16px;
    border-top: 1px solid var(--hairline);
    display: flex;
    justify-content: space-between;
    font-family: var(--font-mono);
    font-size: 10px;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--muted);
  }
</style>
</head>
<body>
  <div class="wordmark">
    <div class="mark">Premium<em>.</em></div>
    <div class="meta">${escape(new Date().toLocaleDateString(undefined, { year: "numeric", month: "long", day: "numeric" }))} · Diagnostic for ${escape(form.business || "your brand")}</div>
  </div>

  <div class="receipt">
    <div>
      <div class="receipt-num">→</div>
      <div class="receipt-label">Diagnostic received</div>
    </div>
    <div>
      <p class="greet">${greeting} Your three moves for <em>${escape(form.business || "your brand")}</em> are below.</p>
      <ul class="meta-rows">
        <li><span class="k">Copy sent to</span><span>${escape(form.email)}</span></li>
        <li><span class="k">Follow-up</span><span>${followUpLine}</span></li>
      </ul>
    </div>
  </div>

  <h1>Three moves<br/>for <em>${escape(form.business || "your brand")}</em>.</h1>
  <p class="summary">${escape(summary || "")}</p>
  <div class="pills">${pillsHtml}</div>

  ${cardHtml}

  <footer class="foot">
    <span>Premium · A diagnostic, not advice</span>
    <span>Charge what it's worth.</span>
  </footer>

  <script>
    window.addEventListener('load', () => {
      // Give fonts a beat, then open the print dialog.
      setTimeout(() => { try { window.focus(); window.print(); } catch (e) {} }, 400);
    });
  </script>
</body>
</html>`;

  const w = window.open("", "_blank", "width=900,height=1100");
  if (!w) {
    // popup blocked — fall back to in-place print
    try { window.print(); } catch (e) {}
    return;
  }
  w.document.open();
  w.document.write(doc);
  w.document.close();
}

// ============================================
// Results
// ============================================
function ResultCard({ cat, data, open, onToggle }) {
  if (!data) return null;
  return (
    <div className={"card" + (open ? " open" : "")} onClick={onToggle}>
      <div className="card-head">
        <div className="card-num">{cat.num}</div>
        <div className="card-title">
          <span className="label">{cat.label}</span>
          <h3>{cat.title}</h3>
        </div>
        <button className="card-toggle" aria-label={open ? "Collapse" : "Expand"}>+</button>
      </div>
      <p className="card-thesis">
        {data.thesis}
      </p>
      <div className="card-body">
        <div className="plays">
          {(data.plays || []).map((p, i) => (
            <div key={i} className="play">
              <div className="play-num">P/{String(i + 1).padStart(2, "0")}</div>
              <div>
                <h4>{p.name}</h4>
                <p>{p.detail}</p>
              </div>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

// ============================================
// TalkThrough, post-playbook "let's discuss" block
// ============================================
// Two CTAs sit at the bottom of the results: email or book a call. The mailto
// link prefills a subject and body referencing the visitor's business name,
// summary, and the three play names so the conversation starts in context.
// If neither contact channel is configured, the whole block is hidden.
function TalkThrough({ form, summary, recs }) {
  const hasEmail = !!CONTACT.email;
  const hasBooking = !!CONTACT.bookingUrl;
  if (!hasEmail && !hasBooking) return null;

  const biz = form.business || "my brand";
  const subject = `Three moves for ${biz}`;
  const firstNameLine = form.firstName ? `${form.firstName} here.` : "Hi,";
  const playList = CATEGORIES
    .map((cat) => {
      const data = recs && recs[cat.key];
      if (!data) return "";
      const names = (data.plays || []).map((p) => `   • ${p.name}`).join("\n");
      return `${cat.num}. ${cat.title}\n   Thesis: ${data.thesis || ""}\n${names}`;
    })
    .filter(Boolean)
    .join("\n\n");
  const bodyLines = [
    firstNameLine,
    "",
    `I just ran the Premium diagnostic for ${biz} and wanted to talk through the three moves it surfaced.`,
    "",
    summary ? `Summary it gave me:\n${summary}` : "",
    "",
    playList ? `The plays:\n\n${playList}` : "",
    "",
    "A few things I'd want to pressure-test:",
    "  1. ",
    "  2. ",
    "",
    "When's a good time?",
    "",
    form.firstName || "",
  ].filter((line) => line !== null && line !== undefined).join("\n");
  const mailto = `mailto:${CONTACT.email}?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(bodyLines)}`;

  return (
    <section className="talkthrough">
      <div className="talkthrough-head">
        <div className="eyebrow"><span className="dot"></span>Talk it through</div>
        <h3>
          Three moves are a <span className="serif-italic">starting point</span>,
          <br/>not a plan<span className="em">.</span>
        </h3>
        <p>
          Want to pressure-test these against the market you're actually in?
          Pick a path, we'll meet you there.
        </p>
      </div>

      <div className={"talkthrough-tiles" + (hasEmail && hasBooking ? " two" : " one")}>
        {hasEmail && (
          <a className="tt-tile" href={mailto}>
            <div className="tt-num">→</div>
            <div className="tt-label">Option A · Async</div>
            <h4>Send us a note</h4>
            <p>
              Reply with your reaction, the move you'd run first, anything we missed.
              We'll write back, usually same day.
            </p>
            <span className="tt-cta">
              Email <span className="tt-cta-addr">{CONTACT.email}</span>
              <span className="arrow">→</span>
            </span>
          </a>
        )}
        {hasBooking && (
          <a className="tt-tile" href={CONTACT.bookingUrl} target="_blank" rel="noopener noreferrer">
            <div className="tt-num">→</div>
            <div className="tt-label">Option B · Live</div>
            <h4>Book a 30-minute call</h4>
            <p>
              Walk through the moves with us, debate the riskiest one, leave with
              a sharper version of the plan.
            </p>
            <span className="tt-cta">
              Pick a time
              <span className="arrow">→</span>
            </span>
          </a>
        )}
      </div>

      {CONTACT.responseHint && (
        <p className="talkthrough-hint">{CONTACT.responseHint}</p>
      )}
    </section>
  );
}

function Results({ form, recs, summary, saveStatus, onRestart, error, onRetry }) {
  const [openKey, setOpenKey] = useState("positioning");
  const channelLabels = useMemo(
    () => form.channels.map((c) => CHANNELS.find((cc) => cc.id === c)?.label).filter(Boolean),
    [form.channels]
  );
  const revLabel = REVENUE_TIERS.find((r) => r.id === form.revenue)?.label || form.revenue;

  return (
    <section id="playbook" style={{ paddingBottom: 40 }}>
      <div className="shell">
        {error && (
          <div className="error-banner">
            <span>Couldn't reach the strategy engine, showing a baseline playbook. {error}</span>
            <button onClick={onRetry}>Retry</button>
          </div>
        )}

        <div className="receipt">
          <div className="receipt-mark">
            <span className="receipt-num">→</span>
            <span className="receipt-label">Diagnostic received</span>
          </div>
          <div className="receipt-body">
            <p className="receipt-greet">
              {form.firstName ? <>Thanks, <span className="em">{form.firstName}</span>.</> : <>Thanks for filling that out.</>}
              {" "}Your three moves for <span className="em">{form.business || "your brand"}</span> are below.
            </p>
            <ul className="receipt-meta">
              <li>
                <span className="k">Follow-up</span>
                <span className="v">
                  {form.followUp
                    ? <>Yes <span className="receipt-note">— a sharp note once or twice a quarter, never a discount.</span></>
                    : <>No <span className="receipt-note">— we'll leave you alone. Come back any time.</span></>}
                </span>
              </li>
              <li>
                <span className="k">Submission</span>
                <span className="v">
                  {saveStatus === "pending" && <>Saving<span className="receipt-note">— a moment.</span></>}
                  {saveStatus === "saved"   && <>Saved <span className="receipt-note">— logged with your other answers.</span></>}
                  {saveStatus === "skipped" && <>Not stored <span className="receipt-note">— this is a preview; your inputs stayed on your device.</span></>}
                  {saveStatus === "failed"  && <>Couldn't save remotely <span className="receipt-note">— your three moves below are still yours to keep.</span></>}
                </span>
              </li>
            </ul>
          </div>
        </div>

        <div className="results-head">
          <div className="eyebrow"><span className="dot"></span>Your Diagnosis</div>
          <h2>Three moves<br/>for {form.business || "your brand"}.</h2>
          <p className="summary">{summary}</p>
          <div className="biz">
            <span className="pill accent">{revLabel}/mo</span>
            <span className="pill">{form.sells}</span>
            {channelLabels.slice(0, 4).map((l) => (
              <span key={l} className="pill">{l}</span>
            ))}
          </div>
        </div>

        <div className="cards">
          {CATEGORIES.map((cat) => (
            <ResultCard
              key={cat.key}
              cat={cat}
              data={recs[cat.key]}
              open={openKey === cat.key}
              onToggle={() => setOpenKey(openKey === cat.key ? null : cat.key)}
            />
          ))}
        </div>

        <TalkThrough form={form} summary={summary} recs={recs} />

        <div className="results-actions">
          <p className="results-cta-note">Three moves. Pick one. Run it this quarter.</p>
          <div className="right">
            <button className="btn ghost" onClick={() => savePlaybookPdf({ form, recs, summary })}>Save as PDF</button>
            <button className="btn" onClick={onRestart}>
              New diagnostic
              <span className="arrow">→</span>
            </button>
          </div>
        </div>
      </div>
    </section>
  );
}

// ============================================
// Footer
// ============================================
function Footer() {
  return (
    <footer className="footer">
      <div className="footer-inner">
        <div className="footer-credo">
          <Sticker
            className="footer-sticker"
            variant="yellow"
            rotate={-3}
            size="md"
          />
          <div className="credo">
            Charge what<br/>
            it's worth<span className="em">.</span>
          </div>
          <p className="small">
            A diagnostic for founders who'd rather build category gravity than run another sale.
            Free, opinionated, and built to be argued with.
          </p>
        </div>
        <div>
          <h4>Diagnostic</h4>
          <ul>
            <li>How it works</li>
            <li>Methodology</li>
            <li>FAQ</li>
            <li>Examples</li>
          </ul>
        </div>
        <div>
          <h4>Reading</h4>
          <ul>
            <li>The case against discounts</li>
            <li>Anatomy of a premium offer</li>
            <li>Retention &gt; acquisition</li>
            <li>Notes from the field</li>
          </ul>
        </div>
        <div>
          <h4>Connect</h4>
          <ul>
            <li>Newsletter</li>
            <li>Office hours</li>
            <li>Send a note</li>
            <li>Press</li>
          </ul>
        </div>
      </div>
      <div className="footer-bottom">
        <span>© 2026 Premium · A diagnostic, not advice</span>
        <span>Made for builders who hold the line</span>
      </div>
    </footer>
  );
}

// ============================================
// Strategy engine
// ============================================
// The diagnostic is generated by the serverless function at
// /api/playbook, which runs a CHEAP model (Haiku) with the
// Anthropic key kept server-side and the same bot-guards as the form. If the
// call fails for ANY reason (no key set, network, bad JSON, blocked bot) the
// page silently uses the built-in playbook below — so it never shows an error
// and never burns a paid call on junk traffic.
async function generatePlaybook(form) {
  const channelText = form.channels.map((c) => CHANNELS.find((cc) => cc.id === c)?.label).filter(Boolean).join(", ");
  const revLabel = REVENUE_TIERS.find((r) => r.id === form.revenue)?.label || form.revenue;

  try {
    const res = await fetch("/api/playbook", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        business:  form.business || "",
        sells:     form.sells || "",
        customer:  form.customer || "",
        channels:  channelText,
        revenue:   revLabel,
        challenge: form.challenge || "",
      }),
    });
    if (!res.ok) return fallbackPlaybook(form);
    const data = await res.json();
    // Guard against a malformed response — fall back rather than render junk.
    if (!data || !data.summary || !data.positioning) return fallbackPlaybook(form);
    return data;
  } catch (e) {
    return fallbackPlaybook(form);
  }
}

// Fallback playbook (used if API fails)
function fallbackPlaybook(form) {
  const biz = form.business || "your brand";
  return {
    summary: `Your real lever isn't a sale, it's making the next purchase from ${biz} feel inevitable. Stop pricing for traffic; start pricing for the customer you actually want.`,
    positioning: {
      thesis: `${biz} should compete on inevitability, not affordability, the customer you want will only show up if the brand earns their respect first.`,
      plays: [
        { name: "Write the manifesto", detail: "Draft a single page that explains exactly who this is for and who it isn't. Pin it above every brand decision for the next two quarters." },
        { name: "Cut the bottom 20% of customers", detail: "Identify the lowest-LTV, highest-friction segment and stop marketing to them. The savings fund a tier above your current ceiling." },
        { name: "Ladder your prices up, not down", detail: "Add a tier 30–50% above your current top SKU. Most won't buy it; the ones who do change the math, and the rest reframe everything below as a deal." },
      ],
    },
    premium_offerings: {
      thesis: "Add altitude before you add breadth, a small number of unmistakably premium offers do more for margin and brand than a long catalog ever will.",
      plays: [
        { name: "Build a hero offer", detail: "One product or package priced where it makes you slightly nervous to quote. Make it the most photographed, most explained thing you do." },
        { name: "Productize your taste", detail: "Bottle the judgment your customers actually pay for, a curation, a kit, a guided service, so it scales without losing voice." },
        { name: "Kill three SKUs this quarter", detail: "Discontinue the bottom-margin items that confuse positioning. Use the runway to ship one item that no competitor would dare make." },
      ],
    },
    retention: {
      thesis: "Premium businesses earn margin on the second purchase, not the first, your retention math is more powerful than any acquisition channel you're running.",
      plays: [
        { name: "Build a 90-day post-purchase arc", detail: "Sequence emails, packaging inserts, and a hand-written touch over the first three months. Treat the first repurchase like a recruiting event." },
        { name: "Open a quiet inner circle", detail: "Invite your top 50 customers into a low-volume, high-signal channel, early drops, opinions solicited, names known. No discounts; access instead." },
        { name: "Measure month-12 LTV, not CAC payback", detail: "Reorient the dashboard so the team makes decisions for the customer who's still here a year from now, not the one who clicked yesterday." },
      ],
    },
    partnerships: {
      thesis: "Borrow taste, not traffic, one thoughtful partnership with someone the customer already trusts beats six months of paid acquisition.",
      plays: [
        { name: "Co-sign with one peer brand", detail: "Identify a non-competing brand serving the same customer and ship a collaboration this quarter. Co-sign credibility, share the audience, split nothing else." },
        { name: "Recruit three creator-operators", detail: "Find founders or makers (not influencers) who already use your category. Offer access, equity-style attention, and a real say in the next product." },
        { name: "Plant in three rooms", detail: "Get the product into three physical contexts where your customer already feels good, a hotel, a studio, a private club. Distribution as endorsement." },
      ],
    },
    content: {
      thesis: "The brand needs an editorial spine, a body of work that makes the customer smarter, not a feed that asks them to buy.",
      plays: [
        { name: "Publish a serial point of view", detail: "Commit to a single recurring format, letter, dispatch, field note, published on a fixed cadence for a year. Earn authority through consistency." },
        { name: "Document the work", detail: "Show the unglamorous craft behind the offer: sourcing, prototyping, deciding what not to do. Process is the most defensible content you'll ever publish." },
        { name: "Hold a strong opinion in public", detail: "Pick one belief about your category that competitors won't say. Repeat it until customers cite it back to you. That's positioning that scales." },
      ],
    },
    distribution: {
      thesis: "Trade reach for resonance, fewer placements, higher trust, longer compounding. Premium is built where the right rooms already are, not where the cheapest impressions live.",
      plays: [
        { name: "Go narrow on one channel", detail: "Pick the single channel with the highest-trust audience and over-invest there for two quarters. Default for premium brands: long-form, owned, or word-of-mouth." },
        { name: "Stand up an event ritual", detail: "Host one small, recurring in-person moment, dinner, workshop, salon, for the customer you most want. The room becomes the marketing." },
        { name: "Earn one editorial mention", detail: "Identify three publications your customer actually reads and pitch a story angle, not a product. One placement here outvalues a quarter of paid spend." },
      ],
    },
  };
}

// ============================================
// App
// ============================================
function App() {
  const [stage, setStage] = useState("form"); // form | loading | results
  const [form, setForm] = useState(null);
  const [recs, setRecs] = useState(null);
  const [summary, setSummary] = useState("");
  const [error, setError] = useState(null);
  // saveStatus: null | "pending" | "saved" | "skipped" | "failed"
  const [saveStatus, setSaveStatus] = useState(null);

  const scrollTo = (id) => {
    const el = document.getElementById(id);
    if (el) el.scrollIntoView({ behavior: "smooth", block: "start" });
  };

  const handleSubmit = async (data) => {
    setForm(data);
    setStage("loading");
    setError(null);
    setSaveStatus("pending");
    // ensure scroll to top of loading
    setTimeout(() => window.scrollTo({ top: document.getElementById("playbook")?.offsetTop || 0, behavior: "smooth" }), 100);

    // Race: minimum 4s loading for narrative effect
    const minDelay = new Promise((r) => setTimeout(r, 4000));

    let resultSummary = "";
    try {
      const result = await Promise.all([generatePlaybook(data), minDelay]).then(([r]) => r);
      resultSummary = result.summary || "";
      setRecs(result);
      setSummary(resultSummary);
      setStage("results");
    } catch (e) {
      console.error("Playbook generation failed:", e);
      const fb = fallbackPlaybook(data);
      resultSummary = fb.summary;
      setRecs(fb);
      setSummary(fb.summary);
      setError(e.message || "unknown");
      setStage("results");
    }

    // Fire-and-forget Attio save once we have the final summary.
    // Doesn't block the user; status flows into the receipt asynchronously.
    saveSubmission(data, resultSummary).then((r) => {
      if (r.skipped) setSaveStatus("skipped");
      else if (r.ok)  setSaveStatus("saved");
      else            setSaveStatus("failed");
    });
  };

  const handleRestart = () => {
    setStage("form");
    setRecs(null);
    setForm(null);
    setError(null);
    setTimeout(() => scrollTo("manifesto"), 50);
  };

  const handleRetry = async () => {
    if (!form) return;
    setStage("loading");
    setError(null);
    try {
      const result = await generatePlaybook(form);
      setRecs(result);
      setSummary(result.summary || "");
      setStage("results");
    } catch (e) {
      const fb = fallbackPlaybook(form);
      setRecs(fb);
      setSummary(fb.summary);
      setError(e.message || "unknown");
      setStage("results");
    }
  };

  return (
    <>
      <CornerStickers />
      {stage === "form" && (
        <>
          <Hero onStart={() => scrollTo("intake")} />
          <IntakeForm onSubmit={handleSubmit} />
        </>
      )}
      {stage === "loading" && <Loading />}
      {stage === "results" && recs && (
        <Results
          form={form}
          recs={recs}
          summary={summary}
          saveStatus={saveStatus}
          onRestart={handleRestart}
          error={error}
          onRetry={handleRetry}
        />
      )}
    </>
  );
}

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