/* Live Luxury — AI Suite Survey
   Single-file React app rendered into #root.
   Brand-strict: Prata + Jost, foil-gold gradient, alabaster ground, hairline depth.
*/

const { useState, useEffect, useRef, useMemo, useCallback, useLayoutEffect } = React;
const { motion, AnimatePresence, LayoutGroup } = window.framerMotion || window["framer-motion"] || window.Motion || {};

/* ---------------- Mock data ---------------- */
const MOCK_AGENT = {
  id: "abc123def456",
  name: "Holli Clem",
  email: "holli@holliclem.com",
  market_center: "South Forsyth",
  headshot: "https://liveluxuryglobal.com/wp-content/uploads/2023/08/Holli_Headshot_Black_Sleeveless-300x300.jpg"
};
const MARKET_CENTERS = [
"Atlanta North", "Atlanta Partners", "Buckhead", "Cherokee", "Chattahoochee North", "East Cobb", "Emerald Coast", "First Atlanta",
"Golden Isles", "Intown", "Intown Atlanta", "Lanier", "Midtown", "North Atlanta", "North Forsyth", "North Fulton", "North Gwinnett",
"Peachtree City", "Peachtree Road", "Perimeter", "Roswell", "Sandy Springs", "Savannah", "Smyrna/Vinings", "South Forsyth",
"Spring Hill/Columbia", "Sugarloaf", "West Atlanta", "West Cobb"];

const MOCK_RESPONDENT_COUNT = 47;

const CATEGORIES = [
{ id: "website", label: "Personal / agent website", vendors: ["Real Geeks", "Sierra Interactive", "BoomTown", "Placester", "Wix", "Squarespace", "Custom-built", "Other"] },
{ id: "cma", label: "CMA / Comparable market analysis", vendors: ["Cloud CMA", "RPR", "ToolkitCMA", "FMLS Stats", "MoxiPresent", "Other"] },
{ id: "design", label: "Design tool", vendors: ["Canva Pro", "Adobe Creative Cloud", "Visme", "Other"] },
{ id: "crm", label: "Email marketing / CRM", vendors: ["Cloze", "Follow Up Boss", "kvCORE", "Mailchimp", "Constant Contact", "BombBomb", "Other"] },
{ id: "video", label: "Video creation / editing", vendors: ["BombBomb", "Loom", "Animoto", "CapCut Pro", "Adobe Premiere", "Other"] },
{ id: "staging", label: "Virtual staging", vendors: ["BoxBrownie", "VirtualStagingAI", "Apply Design", "Other"] },
{ id: "photo", label: "Photo editing", vendors: ["BoxBrownie", "PhotoUp", "Pixlr", "Lightroom", "Other"] },
{ id: "transactions", label: "Transaction management", vendors: ["dotloop", "SkySlope", "Brokermint", "Other"] },
{ id: "presentation", label: "Listing presentation builder", vendors: ["Cloud CMA Live", "MoxiPresent", "Highnote", "Custom Canva", "Other"] },
{ id: "social", label: "Social media scheduler", vendors: ["Later", "Buffer", "Hootsuite", "Sprout Social", "Other"] },
{ id: "leads", label: "AI Services", vendors: ["ChatGPT Plus / Pro", "Claude", "Gemini", "Perplexity", "Jasper", "Copy.ai", "Other"] }];


const COSTS = ["<$25", "$25–50", "$50–100", "$100–200", "$200–500", "$500+"];
const SWITCH = ["Yes definitely", "Probably", "Maybe", "No"];

// Per-category preset dollar amounts. Keys must match CATEGORIES[].id.
// ("leads" maps to the spec's "ai" because that's how AI services are keyed in this codebase.)
const PRICE_PRESETS = {
  website: [50, 150, 300, 500],
  cma: [25, 50, 100, 150],
  design: [15, 30, 50, 100],
  crm: [30, 75, 150, 300],
  video: [15, 30, 75, 150],
  staging: [15, 50, 100, 250],
  photo: [25, 50, 100, 200],
  transactions: [30, 50, 100, 200],
  presentation: [25, 50, 100, 200],
  social: [15, 30, 50, 100],
  leads: [20, 40, 100, 200]
};

const FEATURES = [
{ id: "staging", name: "Multi-Angle Virtual Staging", valueProp: "Vacant rooms staged with consistent furniture across every angle of the listing. Returned in minutes, not days.", icon: "staging" },
{ id: "website", name: "AI Listing Website", valueProp: "A custom-designed property website auto-built the moment your listing goes live. Link in your inbox before the photos are uploaded to MLS.", icon: "website" },
{ id: "content", name: "Studio-Quality Content of You", valueProp: "Photos and reels with you as the subject \u2014 anywhere, any setting, any time. Same hair, same face, same brand. No photoshoots.", icon: "content" },
{ id: "proposal", name: "AI Listing Proposal Generator", valueProp: "A fully-designed branded listing proposal for any address in 90 seconds. Comps, pricing strategy, marketing plan \u2014 done before the doorbell rings.", icon: "proposal" },
{ id: "cma", name: "AI CMA / Comp Finder", valueProp: "Comps pulled from MLS, Zillow, Redfin, and Realtor.com \u2014 scored, ranked, and presented as a branded report. 60 seconds, not 6 hours.", icon: "cma" },
{ id: "description", name: "AI Listing Description Writer", valueProp: "Voice-matched, MLS-compliant descriptions trained on your brand. Drop in MLS data, get a description that sounds like you wrote it.", icon: "description" },
{ id: "brochure", name: "AI Brochure & Flyer Designer", valueProp: "Branded flyers and just-listed graphics for any address, generated instantly. Print-ready, social-ready, on-brand.", icon: "brochure" },
{ id: "inspection", name: "AI Inspection Negotiator", valueProp: "Drop in any inspection report. Get a buyer-ready negotiation strategy with credit estimates and a one-page summary for your client.", icon: "inspection" }];


const STEPS = [
{ id: "welcome", label: "Welcome" },
{ id: "about", label: "Step 01 — About You" },
{ id: "stack", label: "Step 02 — Your current tools" },
{ id: "features", label: "Step 03 — What To Build First" },
{ id: "pricing", label: "Step 04 — How Do We Win You Over" },
{ id: "bonus", label: "Step 05 — Almost Done" }];


/* ---------------- Helpers ---------------- */
const haptic = (ms = 15) => {try {navigator.vibrate && navigator.vibrate(ms);} catch (e) {}};
const clamp = (n, a, b) => Math.max(a, Math.min(b, n));

function useInView(ids) {
  const [active, setActive] = useState(ids[0]);
  useEffect(() => {
    const els = ids.map((id) => document.getElementById(id)).filter(Boolean);
    if (!els.length) return;
    const onScroll = () => {
      const cy = window.innerHeight * 0.32;
      let best = ids[0],bestDist = Infinity;
      for (const el of els) {
        const r = el.getBoundingClientRect();
        const dist = Math.abs(r.top - cy);
        if (r.top - cy < 80 && dist < bestDist) {bestDist = dist;best = el.id;}
      }
      setActive(best);
    };
    onScroll();
    window.addEventListener("scroll", onScroll, { passive: true });
    window.addEventListener("resize", onScroll);
    return () => {
      window.removeEventListener("scroll", onScroll);
      window.removeEventListener("resize", onScroll);
    };
  }, [ids.join("|")]);
  return active;
}

/* ---------------- Atoms ---------------- */
function Eyebrow({ children }) {
  return (
    <div className="scene-eyebrow">
      <span className="rule" />
      <span className="eyebrow" style={{ color: "rgb(0, 0, 0)" }}>{children}</span>
    </div>);

}

function Ornament() {
  return (
    <div className="ornament">
      <span className="rule" />
      <span className="diamond" />
      <span className="rule" />
    </div>);

}

function CTA({ children, onClick, disabled, kind = "gold", large = false, type = "button" }) {
  if (kind === "ghost") {
    return (
      <button className={"cta ghost" + (large ? " large" : "")} onClick={onClick} disabled={disabled} type={type}>
        {children}
      </button>);

  }
  return (
    <button className={"cta" + (large ? " large" : "")} onClick={onClick} disabled={disabled} type={type}>
      {children}
    </button>);

}

function Opt({ pressed, onClick, children }) {
  return (
    <button
      className="opt"
      aria-pressed={pressed ? "true" : "false"}
      onClick={() => {onClick();haptic();}}
      type="button">
      
      <span>{children}</span>
      <span className="opt-dot" />
    </button>);

}

function OptGroup({ value, options, onChange, columns = 1 }) {
  const cls = "opt-grid" + (columns >= 5 ? " col-5" : columns >= 3 ? " col-3" : columns === 2 ? " col-2" : "");
  return (
    <div className={cls}>
      {options.map((o) =>
      <Opt key={o} pressed={value === o} onClick={() => onChange(o)}>{o}</Opt>
      )}
    </div>);

}

/* Stars */
function Stars({ value, onChange }) {
  return (
    <div className="stars" role="radiogroup" aria-label="Satisfaction rating">
      {[1, 2, 3, 4, 5].map((n) => {
        const on = (value || 0) >= n;
        return (
          <button
            key={n}
            className={"star" + (on ? " on" : "")}
            onClick={() => {onChange(n);haptic();}}
            aria-label={`${n} star${n > 1 ? 's' : ''}`}
            type="button">
            
            {on ?
            <span className="star-fill" aria-hidden="true" /> :

            <svg viewBox="0 0 24 24" width="44" height="44" aria-hidden="true" fill="none" stroke="currentColor" strokeWidth="1.2">
                <polygon points="12,2 14.7,8.6 21.8,9.2 16.4,13.9 18,20.9 12,17.2 6,20.9 7.6,13.9 2.2,9.2 9.3,8.6" />
              </svg>
            }
          </button>);

      })}
      {/* Shared gold gradient definition */}
      <svg width="0" height="0" style={{ position: 'absolute' }} aria-hidden="true">
        <defs>
          <linearGradient id="ll-star-grad" x1="0" y1="0" x2="1" y2="1">
            <stop offset="0%" stopColor="#BF953F" />
            <stop offset="25%" stopColor="#FCF6BA" />
            <stop offset="50%" stopColor="#B38728" />
            <stop offset="75%" stopColor="#FBF5B7" />
            <stop offset="100%" stopColor="#AA771C" />
          </linearGradient>
        </defs>
      </svg>
    </div>);

}

/* Pencil icon */
function Pencil() {
  return (
    <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
      <path d="M16.5 3.5l4 4L8 20H4v-4z" />
      <path d="M14 6l4 4" />
    </svg>);

}

/* Identity row — single field with inline editing inside the identity card */
function IdentRow({ label, value, onChange, type = "text", options, valClass = "" }) {
  const [editing, setEditing] = useState(false);
  const inputRef = useRef(null);
  useEffect(() => {if (editing && inputRef.current) inputRef.current.focus();}, [editing]);
  return (
    <div className="ident-row" onClick={() => setEditing(true)}>
      <div>
        <div className="lbl">{label}</div>
        {options ?
        <select
          ref={inputRef}
          className={"val " + valClass}
          value={value}
          onChange={(e) => onChange(e.target.value)}
          onBlur={() => setEditing(false)}>
          
            {options.map((o) => <option key={o} value={o}>{o}</option>)}
          </select> :

        <input
          ref={inputRef}
          className={"val " + valClass}
          type={type}
          value={value}
          onChange={(e) => onChange(e.target.value)}
          onBlur={() => setEditing(false)} />

        }
      </div>
      <button
        className="pencil"
        onClick={(e) => {e.stopPropagation();setEditing(true);}}
        aria-label={`Edit ${label}`}
        type="button">
        <Pencil /></button>
    </div>);

}

/* Inline editable card (legacy - kept for compatibility) */
function EditCard({ label, value, onChange, type = "text", options }) {
  const [editing, setEditing] = useState(false);
  const inputRef = useRef(null);
  useEffect(() => {if (editing && inputRef.current) inputRef.current.focus();}, [editing]);

  return (
    <div className="edit-card">
      <span className="lbl" style={{ gridColumn: '1 / span 2' }}>{label}</span>
      <button
        className="pencil"
        onClick={() => setEditing(true)}
        aria-label={`Edit ${label}`}
        type="button">
        <Pencil /></button>

      {options ?
      <select
        ref={inputRef}
        className="val"
        value={value}
        onChange={(e) => onChange(e.target.value)}
        onBlur={() => setEditing(false)}
        onFocus={() => setEditing(true)}>
        
          {options.map((o) => <option key={o} value={o}>{o}</option>)}
        </select> :

      <input
        ref={inputRef}
        className="val"
        type={type}
        value={value}
        onChange={(e) => onChange(e.target.value)}
        onBlur={() => setEditing(false)}
        onFocus={() => setEditing(true)} />

      }
    </div>);

}

/* ---------------- Welcome ---------------- */
function SceneWelcome({ onContinue }) {
  return (
    <section id="welcome" className="scene">
      <div className="hero">
        <div className="wm"><img src="assets/liveluxury-kw-logo.png" alt="Live Luxury · Keller Williams" /></div>
        <Eyebrow>Exclusive to Live Luxury Members</Eyebrow>
        <h1 className="display balance">
          Live Luxury is investing in tools to <span className="gold-text">multiply your business.</span>
        </h1>

        {MOCK_RESPONDENT_COUNT > 0 &&
        <div className="live-counter">
            <span className="dot" />
            <span className="txt"><b>{MOCK_RESPONDENT_COUNT}</b> of your peers have weighed in.</span>
          </div>
        }

        <div style={{ height: 36 }} />
        <CTA large onClick={onContinue}>
          Let's begin <span className="arrow">→</span>
        </CTA>

        <div className="signature">
           <span className="who"></span> 
        </div>
      </div>
    </section>);

}

/* ---------------- About You ---------------- */
function SceneAbout({ state, set, onContinue }) {
  const ready = state.role;
  return (
    <section id="about" className="scene">
      <Eyebrow>Step 01 — About You</Eyebrow>
      <h2 className="headline balance">First, confirm we have you right.</h2>
      <p className="body" style={{ marginBottom: 28 }}>Quick check on what we already know. Adjust anything that's wrong.</p>

      <div className="ident-card">
        <div className="ident-photo">
          {state.headshot ?
          <img src={state.headshot} alt={state.name} referrerPolicy="no-referrer" onError={(e) => {e.target.style.display = 'none';}} /> :
          null}
          <span className="badge"><i /> Live Luxury Member</span>
        </div>
        <div className="ident-rows">
          <IdentRow label="Full name" value={state.name} onChange={(v) => set({ name: v })} />
          <IdentRow label="Email" value={state.email} onChange={(v) => set({ email: v })} type="email" valClass="email" />
          <IdentRow label="Market center" value={state.market_center} onChange={(v) => set({ market_center: v })} options={MARKET_CENTERS} />
        </div>
      </div>

      <div className="divider" />

      <div>
        <div className="eyebrow" style={{ marginBottom: 12 }}>Role</div>
        <OptGroup
          value={state.role}
          onChange={(v) => set({ role: v })}
          options={["Solo agent", "Team member"]}
          columns={2} />
        
      </div>

      <AnimatePresence>
        {ready &&
        <motion.div
          initial={{ opacity: 0, y: 8 }}
          animate={{ opacity: 1, y: 0 }}
          exit={{ opacity: 0 }}
          transition={{ duration: 0.25 }}
          style={{ marginTop: 36 }}>
          
            <CTA onClick={onContinue}>Continue <span className="arrow">→</span></CTA>
          </motion.div>
        }
      </AnimatePresence>
    </section>);

}

/* ---------------- Cost field (presets + numeric input) ---------------- */
function CostField({ presets, value, source, onChange }) {
  // value is the canonical numeric (or null). source tells us how the user got there:
  //   "preset:<n>"  → a preset is highlighted
  //   "custom"      → Custom button was tapped (input focused)
  //   "typed"       → user typed directly into the input
  //   undefined     → no answer yet
  const inputRef = useRef(null);

  const sanitize = (raw) => {
    const cleaned = String(raw).replace(/[^\d.]/g, '');
    const parts = cleaned.split('.');
    return parts[0] + (parts.length > 1 ? '.' + parts.slice(1).join('') : '');
  };

  const display = value === null || value === undefined ? "" : String(value);

  const tapPreset = (n) => {
    haptic();
    onChange(n, `preset:${n}`);
  };
  const tapCustom = () => {
    haptic();
    // Don't clobber existing typed value; just mark intent + focus
    onChange(value, "custom");
    setTimeout(() => {inputRef.current && inputRef.current.focus();}, 30);
  };
  const handleType = (e) => {
    const safe = sanitize(e.target.value);
    if (safe === "") {
      onChange(null, "typed");
    } else {
      // Coerce to number where possible; preserve trailing dot mid-typing
      const num = safe.endsWith(".") ? safe : Number(safe);
      onChange(num, "typed");
    }
  };

  const isPresetSelected = (n) => source === `preset:${n}`;
  const isCustomSelected = source === "custom" || source === "typed";

  return (
    <div className="cost-field">
      <div className="cost-presets">
        {presets.map((n) =>
        <button
          key={n}
          type="button"
          className={"cost-preset" + (isPresetSelected(n) ? " selected" : "")}
          aria-pressed={isPresetSelected(n) ? "true" : "false"}
          onClick={() => tapPreset(n)}>
          ${n}</button>
        )}
        <button
          type="button"
          className={"cost-preset cost-custom" + (isCustomSelected ? " selected" : "")}
          aria-pressed={isCustomSelected ? "true" : "false"}
          onClick={tapCustom}>
          Custom</button>
      </div>
      <div className="cost-input-wrap">
        <span className="cost-prefix" aria-hidden="true">$</span>
        <input
          ref={inputRef}
          className="cost-input"
          type="text"
          inputMode="decimal"
          pattern="[0-9]*\.?[0-9]*"
          placeholder="Enter amount"
          value={display}
          onChange={handleType}
          aria-label="Monthly cost in dollars" />
        
      </div>
    </div>);

}

/* ---------------- Spend row ---------------- */
function SpendRow({ cat, value, onChange }) {
  const expanded = value.paid === "Yes, paid";
  const isFree = value.paid === "Free tool";
  const dimmed = value.paid === "No / I do without";
  const [otherVendor, setOtherVendor] = useState("");
  return (
    <div className={"spend-row" + (expanded ? " expanded" : "") + (isFree ? " free" : "") + (dimmed ? " dimmed" : "")}>
      <div className="spend-head" style={{ opacity: "1" }}>
        <div className="cat">{cat.label}</div>
        <div className="yesnos">
          {[
          { k: "Yes, paid", lbl: "Yes" },
          { k: "Free tool", lbl: "Free" },
          { k: "No / I do without", lbl: "No" }].
          map((o) =>
          <button
            key={o.k}
            className="yn"
            aria-pressed={value.paid === o.k ? "true" : "false"}
            onClick={() => {onChange({ ...value, paid: o.k });haptic();}}
            type="button">
            {o.lbl}</button>
          )}
        </div>
      </div>

      {/* Free tool: ask which one + how good is it (smooth height/opacity expand) */}
      <AnimatePresence initial={false}>
        {isFree &&
        <motion.div
          initial={{ height: 0, opacity: 0 }}
          animate={{ height: "auto", opacity: 1 }}
          exit={{ height: 0, opacity: 0 }}
          transition={{ duration: 0.28, ease: [0.2, 0.7, 0.2, 1] }}
          style={{ overflow: 'hidden' }}>
            <div className="spend-detail">
              <motion.div className="field" initial={{ opacity: 0, y: 6 }} animate={{ opacity: 1, y: 0 }} transition={{ delay: 0.05 }}>
                <span className="field-lbl">Which one?</span>
                <input
                  className="vendor-other"
                  style={{ marginTop: 0 }}
                  placeholder="Tell us which free tool you use"
                  value={value.freeToolName || ""}
                  onChange={(e) => onChange({ ...value, freeToolName: e.target.value })} />
              </motion.div>

              <motion.div className="field" initial={{ opacity: 0, y: 6 }} animate={{ opacity: 1, y: 0 }} transition={{ delay: 0.10 }}>
                <span className="field-lbl">Satisfaction</span>
                <Stars value={value.satisfaction} onChange={(n) => onChange({ ...value, satisfaction: n })} />
              </motion.div>
            </div>
          </motion.div>
        }
      </AnimatePresence>

      <AnimatePresence initial={false}>
        {expanded &&
        <motion.div
          initial={{ height: 0, opacity: 0 }}
          animate={{ height: "auto", opacity: 1 }}
          exit={{ height: 0, opacity: 0 }}
          transition={{ duration: 0.28, ease: [0.2, 0.7, 0.2, 1] }}
          style={{ overflow: 'hidden' }}>
          
            <div className="spend-detail">
              <motion.div className="field" initial={{ opacity: 0, y: 6 }} animate={{ opacity: 1, y: 0 }} transition={{ delay: 0.05 }}>
                <span className="field-lbl">Vendor</span>
                <select
                className="vendor-select"
                value={value.vendor || ""}
                onChange={(e) => onChange({ ...value, vendor: e.target.value })}>
                
                  <option value="" disabled>Select a vendor…</option>
                  {cat.vendors.map((v) => <option key={v} value={v}>{v}</option>)}
                </select>
                {value.vendor === "Other" &&
              <input
                className="vendor-other"
                placeholder="Tell us which one"
                value={otherVendor}
                onChange={(e) => setOtherVendor(e.target.value)} />

              }
              </motion.div>

              <motion.div className="field" initial={{ opacity: 0, y: 6 }} animate={{ opacity: 1, y: 0 }} transition={{ delay: 0.10 }}>
                <span className="field-lbl">Monthly cost</span>
                <CostField
                presets={PRICE_PRESETS[cat.id] || [25, 50, 100, 200]}
                value={value.cost}
                source={value.costSource}
                onChange={(amount, source) => onChange({ ...value, cost: amount, costSource: source })} />
              
              </motion.div>

              <motion.div className="field" initial={{ opacity: 0, y: 6 }} animate={{ opacity: 1, y: 0 }} transition={{ delay: 0.15 }}>
                <span className="field-lbl">Satisfaction</span>
                <Stars value={value.satisfaction} onChange={(n) => onChange({ ...value, satisfaction: n })} />
              </motion.div>
            </div>
          </motion.div>
        }
      </AnimatePresence>
    </div>);

}

/* ---------------- Stack ---------------- */
function SceneStack({ state, set, onContinue }) {
  const answered = CATEGORIES.filter((c) => state.spend[c.id]?.paid).length;
  const pct = answered / CATEGORIES.length * 100;
  return (
    <section id="stack" className="scene">
      <Eyebrow>Step 02 — Your current tools</Eyebrow>
      <h2 className="headline balance">What are you paying for today?</h2>
      <p className="body">Quick run-through. Tap <b>Yes</b> if you pay for one and a tiny detail row will expand. Tap <b>No</b> or <b>Free tool</b> to skip. About 90 seconds.</p>

      <div className="micro-prog">
        <span className="num"><b style={{ color: "rgb(0, 0, 0)" }}>{answered}</b> <span className="muted" style={{ color: "rgb(0, 0, 0)" }}>/ {CATEGORIES.length} answered</span></span>
        <span className="bar"><i style={{ width: pct + "%" }} /></span>
      </div>

      <div style={{ display: 'grid', gap: 10 }}>
        {CATEGORIES.map((cat) =>
        <SpendRow
          key={cat.id}
          cat={cat}
          value={state.spend[cat.id] || {}}
          onChange={(next) => set({ spend: { ...state.spend, [cat.id]: next } })} />

        )}
      </div>

      <div style={{ marginTop: 36 }}>
        <div className="eyebrow" style={{ marginBottom: 12, color: "rgb(0, 0, 0)" }}>Anything else you're paying for?</div>
        <textarea
          className="editorial"
          value={state.stack_other_text || ""}
          onChange={(e) => set({ stack_other_text: e.target.value })}
          placeholder="Other tools, subscriptions, or services that didn't fit the categories above." style={{ opacity: "0.7" }} />
        
      </div>

      <AnimatePresence>
        {answered >= 1 &&
        <motion.div
          initial={{ opacity: 0, y: 8 }} animate={{ opacity: 1, y: 0 }} exit={{ opacity: 0 }}
          transition={{ duration: 0.25 }}
          style={{ marginTop: 36 }}>
          
            <CTA onClick={onContinue}>Continue <span className="arrow">→</span></CTA>
          </motion.div>
        }
      </AnimatePresence>
    </section>);

}

/* ---------------- Features (the big one) ---------------- */
function GoldParticles({ trigger }) {
  // 12 particles emitted from bucket bar
  const [seed, setSeed] = useState(0);
  useEffect(() => {setSeed((s) => s + 1);}, [trigger]);
  if (!trigger) return null;
  return (
    <div style={{ position: 'absolute', inset: 0, pointerEvents: 'none', overflow: 'visible' }}>
      {Array.from({ length: 14 }).map((_, i) => {
        const left = 20 + Math.random() * 60;
        const dx = (Math.random() - 0.5) * 60;
        const dy = -40 - Math.random() * 80;
        const dur = 0.7 + Math.random() * 0.4;
        const size = 4 + Math.random() * 4;
        return (
          <motion.div
            key={`p-${seed}-${i}`}
            initial={{ opacity: 0.95, x: 0, y: 0, scale: 0.6 }}
            animate={{ opacity: 0, x: dx, y: dy, scale: 1 }}
            transition={{ duration: dur, ease: [0.2, 0.7, 0.2, 1] }}
            style={{
              position: 'absolute',
              left: `${left}%`,
              bottom: 4,
              width: size, height: size,
              background: 'var(--ll-gold)',
              transform: 'rotate(45deg)'
            }} />);


      })}
    </div>);

}

/* Gold-foil animated icons for feature cards */
const FEATURE_ICONS = {
  staging:
  <svg viewBox="0 0 32 32" width="100%" height="100%" fill="none" stroke="url(#ll-icon-grad)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
      <path d="M5 18 v-3 a3 3 0 0 1 3 -3 h16 a3 3 0 0 1 3 3 v3" />
      <path d="M3 18 h26 v6 h-3 v3 h-2 v-3 H8 v3 H6 v-3 H3 z" />
    </svg>,

  website:
  <svg viewBox="0 0 32 32" width="100%" height="100%" fill="none" stroke="url(#ll-icon-grad)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
      <rect x="3" y="5" width="26" height="17" rx="1.5" />
      <path d="M13 22 v3 M19 22 v3" />
      <path d="M9 28 h14" />
    </svg>,

  content:
  <svg viewBox="0 0 32 32" width="100%" height="100%" fill="none" stroke="url(#ll-icon-grad)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
      <circle cx="16" cy="11" r="5" />
      <path d="M5 28 c0 -6 5 -10 11 -10 s11 4 11 10" />
    </svg>,

  proposal:
  <svg viewBox="0 0 32 32" width="100%" height="100%" fill="none" stroke="url(#ll-icon-grad)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
      <path d="M7 4 h13 l5 5 v19 H7 z" />
      <path d="M20 4 v5 h5" />
      <path d="M11 16 h10 M11 20 h10 M11 24 h6" />
    </svg>,

  cma:
  <svg viewBox="0 0 32 32" width="100%" height="100%" fill="none" stroke="url(#ll-icon-grad)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
      <path d="M5 27 h22" />
      <rect x="7" y="19" width="4" height="7" />
      <rect x="14" y="13" width="4" height="13" />
      <rect x="21" y="7" width="4" height="19" />
    </svg>,

  description:
  <svg viewBox="0 0 32 32" width="100%" height="100%" fill="url(#ll-icon-grad)" stroke="none">
      <text x="3" y="24" fontFamily="Prata, serif" fontSize="18" fontWeight="400">Aa</text>
    </svg>,

  brochure:
  <svg viewBox="0 0 32 32" width="100%" height="100%" fill="none" stroke="url(#ll-icon-grad)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
      <path d="M4 8 h8 v18 H4 z" />
      <path d="M12 8 h8 v18 h-8 z" />
      <path d="M20 8 h8 v18 h-8 z" />
      <path d="M7 13 h2 M15 13 h2 M23 13 h2" />
    </svg>,

  inspection:
  <svg viewBox="0 0 32 32" width="100%" height="100%" fill="none" stroke="url(#ll-icon-grad)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
      <circle cx="13" cy="13" r="7" />
      <path d="M18 18 l8 8" />
      <path d="M10 13 l2 2 4 -4" />
    </svg>

};

function FeatureIcon({ name, delay, paused }) {
  return (
    <span
      className="feat-icon"
      style={{ animationDelay: `${delay.shimmer}s, ${delay.breath}s`, animationPlayState: paused ? 'paused, paused' : 'running, running' }}
      aria-hidden="true">
      
      {FEATURE_ICONS[name] || FEATURE_ICONS.proposal}
    </span>);

}

function FeatureCard({ feat, selected, onTap, cardRef, iconDelay }) {
  return (
    <div
      ref={cardRef}
      className={"feat-card text-only" + (selected ? " selected" : "")}
      onClick={() => onTap(feat)}
      role="button"
      tabIndex={0}
      onKeyDown={(e) => {if (e.key === "Enter" || e.key === " ") {e.preventDefault();onTap(feat);}}} style={{ opacity: "0.7" }}>
      
      {selected &&
      <span className="selected-tag"><span style={{ width: 6, height: 6, background: 'var(--ll-gold)', transform: 'rotate(45deg)', display: 'inline-block' }} /> Selected</span>
      }
      <FeatureIcon name={feat.icon || feat.id} delay={iconDelay} paused={selected} />
      <span className="plus-mark" aria-hidden="true">{selected ? "✓" : "+"}</span>
      <div className="body-wrap">
        <div className="feat-name">{feat.name}</div>
        <div className="feat-prop">{feat.valueProp}</div>
      </div>
    </div>);

}

function BucketSlot({ index, feature, onRemove, slotRef, animationOffset = 0 }) {
  const [showRipple, setShowRipple] = useState(false);
  const prevHadFeature = useRef(!!feature);
  useEffect(() => {
    const has = !!feature;
    if (has && !prevHadFeature.current) {
      setShowRipple(true);
      const t = setTimeout(() => setShowRipple(false), 460);
      return () => clearTimeout(t);
    }
    prevHadFeature.current = has;
  }, [feature]);

  if (!feature) {
    return (
      <div
        className="slot empty"
        ref={slotRef}
        role="region"
        aria-label={`Top pick slot ${index + 1}, empty`}>
        
        <span className="empty-ring" aria-hidden="true">
          <svg viewBox="0 0 100 100" fill="none">
            <defs>
              <linearGradient id={`ll-empty-ring-${index}`} x1="0%" y1="0%" x2="100%" y2="100%">
                <stop offset="0%" stopColor="#AA771C" />
                <stop offset="35%" stopColor="#FCF6BA" />
                <stop offset="60%" stopColor="#B8893B" />
                <stop offset="100%" stopColor="#7A5A1F" />
              </linearGradient>
            </defs>
            <circle
              cx="50" cy="50" r="48"
              stroke={`url(#ll-empty-ring-${index})`}
              strokeWidth="1.5"
              strokeDasharray="6 8"
              strokeLinecap="round" />
            
          </svg>
        </span>
        <span className="num-tag">{String(index + 1).padStart(2, '0')}</span>
      </div>);

  }
  return (
    <button
      className="slot filled"
      ref={slotRef}
      onClick={(e) => {e.stopPropagation();onRemove(feature.id);haptic(15);}}
      type="button"
      aria-label={`Top pick ${index + 1}: ${feature.name}. Tap to remove.`}
      style={{ animationDelay: `${animationOffset}s` }}>
      
      <span
        className="disc"
        aria-hidden="true"
        style={{ animationDelay: `${animationOffset - 0.3}s, ${animationOffset}s` }} />
      
      {showRipple && <span className="arrival-ripple" aria-hidden="true" />}
      <span className="slot-icon" aria-hidden="true">
        {FEATURE_ICONS[feature.icon || feature.id] || FEATURE_ICONS.proposal}
      </span>
      <span
        className="remove"
        aria-label={`Remove ${feature.name} from your top three`}>
        ×</span>
    </button>);

}

function SceneFeatures({ state, set, onContinue }) {
  const cardRefs = useRef({});
  const slotRefs = useRef([useRef(null), useRef(null), useRef(null)]);
  const sectionRef = useRef(null);

  const [flying, setFlying] = useState([]); // {id, name, fromRect, toRect, dir, key}
  const [pulseSlot, setPulseSlot] = useState(-1);
  const [particleTick, setParticleTick] = useState(0);
  const [counterTick, setCounterTick] = useState(0);

  const selectedIds = state.selectedFeatures;
  const selectedFeatures = selectedIds.map((id) => FEATURES.find((f) => f.id === id));
  // Per-card randomized animation offsets, computed once on mount
  const iconDelays = useMemo(
    () => FEATURES.map(() => ({
      shimmer: -(Math.random() * 4),
      breath: -(Math.random() * 3.2)
    })),
    []
  );
  // Per-slot randomized loop offsets so no two slots are in sync (-5s..0s).
  const slotOffsets = useMemo(
    () => [0, 1, 2].map(() => -(Math.random() * 5)),
    []
  );

  const tapFeature = (feat) => {
    haptic();
    const isSel = selectedIds.includes(feat.id);
    if (isSel) {
      // Remove flow: fly back from slot to card
      const slotIdx = selectedIds.indexOf(feat.id);
      const slotEl = slotRefs.current[slotIdx]?.current;
      const cardEl = cardRefs.current[feat.id];
      if (slotEl && cardEl) {
        const fromRect = slotEl.getBoundingClientRect();
        const toRect = cardEl.getBoundingClientRect();
        flyOne({ id: feat.id, name: feat.name, icon: feat.icon || feat.id, fromRect, toRect, dir: 'back' });
      }
      const next = selectedIds.filter((id) => id !== feat.id);
      set({ selectedFeatures: next, topFeature: next.includes(state.topFeature) ? state.topFeature : null });
      setCounterTick((t) => t + 1);
      return;
    }

    if (selectedIds.length >= 3) {
      // Swap: oldest flies back, new flies in
      const oldestId = selectedIds[0];
      const oldestFeat = FEATURES.find((f) => f.id === oldestId);
      const oldestSlot = slotRefs.current[0].current;
      const oldestCard = cardRefs.current[oldestId];
      if (oldestSlot && oldestCard) {
        const fr = oldestSlot.getBoundingClientRect();
        const tr = oldestCard.getBoundingClientRect();
        flyOne({ id: oldestId, name: oldestFeat.name, icon: oldestFeat.icon || oldestFeat.id, fromRect: fr, toRect: tr, dir: 'back', key: 'swap-back-' + Date.now() });
      }
      // Build the new array: drop oldest, push new
      const nextSelected = [...selectedIds.slice(1), feat.id];
      // Compute target slot (last one)
      const cardEl = cardRefs.current[feat.id];
      const targetSlot = slotRefs.current[2].current;
      if (cardEl && targetSlot) {
        const fr = cardEl.getBoundingClientRect();
        const tr = targetSlot.getBoundingClientRect();
        flyOne({ id: feat.id, name: feat.name, icon: feat.icon || feat.id, fromRect: fr, toRect: tr, dir: 'in', key: 'swap-in-' + Date.now() });
      }
      // Commit after small delay to align with arrival
      setTimeout(() => {
        set({
          selectedFeatures: nextSelected,
          topFeature: nextSelected.includes(state.topFeature) ? state.topFeature : null
        });
        setPulseSlot(2);
        setCounterTick((t) => t + 1);
        haptic(25);
        setTimeout(() => setPulseSlot(-1), 280);
      }, 380);
      return;
    }

    // Normal add
    const targetIdx = selectedIds.length;
    const cardEl = cardRefs.current[feat.id];
    const targetSlot = slotRefs.current[targetIdx].current;
    if (cardEl && targetSlot) {
      const fromRect = cardEl.getBoundingClientRect();
      const toRect = targetSlot.getBoundingClientRect();
      flyOne({ id: feat.id, name: feat.name, icon: feat.icon || feat.id, fromRect, toRect, dir: 'in' });
    }
    setTimeout(() => {
      const next = [...selectedIds, feat.id];
      set({ selectedFeatures: next });
      setPulseSlot(targetIdx);
      setCounterTick((t) => t + 1);
      haptic(25);
      if (next.length === 3) {
        setParticleTick((t) => t + 1);
      }
      setTimeout(() => setPulseSlot(-1), 280);
    }, 380);
  };

  const flyOne = ({ id, name, icon, fromRect, toRect, dir, key }) => {
    const k = key || `${id}-${dir}-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
    setFlying((prev) => [...prev, { id, name, icon, fromRect, toRect, dir, key: k }]);
    setTimeout(() => {
      setFlying((prev) => prev.filter((f) => f.key !== k));
    }, 600);
  };

  const removeFeature = (id) => {
    const slotIdx = selectedIds.indexOf(id);
    const feat = FEATURES.find((f) => f.id === id);
    const slotEl = slotRefs.current[slotIdx]?.current;
    const cardEl = cardRefs.current[id];
    if (slotEl && cardEl) {
      const fromRect = slotEl.getBoundingClientRect();
      const toRect = cardEl.getBoundingClientRect();
      flyOne({ id, name: feat.name, icon: feat.icon || feat.id, fromRect, toRect, dir: 'back' });
    }
    const next = selectedIds.filter((x) => x !== id);
    set({ selectedFeatures: next, topFeature: next.includes(state.topFeature) ? state.topFeature : null });
    setCounterTick((t) => t + 1);
  };

  const ready = selectedIds.length === 3 && state.topFeature;

  return (
    <section id="features" className="scene" ref={sectionRef}>
      <Eyebrow>Step 03 — What To Build First</Eyebrow>
      <h2 className="headline balance">If we built an AI Suite for Live Luxury, which 3 of these would change your business most?</h2>
      <p className="body">Tap any feature to add it to your top three. Tap a slot to remove. Pick a fourth and the oldest swaps out — no rules to memorize.</p>

      {/* Sticky bucket */}
      <div className="bucket-bar" style={{ opacity: "0.7" }}>
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 10 }}>
          <span className="eyebrow">Your top three</span>
          <motion.span
            key={counterTick}
            initial={{ scale: 0.85, opacity: 0.6 }}
            animate={{ scale: 1, opacity: 1 }}
            transition={{ duration: 0.22, ease: [0.2, 0.7, 0.2, 1] }}
            className="num"
            style={{ fontSize: 12, color: 'var(--ll-mist)', letterSpacing: '0.06em' }}>
            <b style={{ color: 'var(--ll-gold-flat)', fontWeight: 600 }}>{selectedIds.length}</b> of 3</motion.span>
        </div>
        <div className="bucket-row" style={{ position: 'relative' }}>
          {[0, 1, 2].map((i) => {
            const feat = selectedFeatures[i];
            const pulse = pulseSlot === i;
            return (
              <motion.div
                key={i}
                animate={pulse ? { scale: [1, 1.04, 1] } : {}}
                transition={{ duration: 0.28 }}>
                
                <BucketSlot index={i} feature={feat} onRemove={removeFeature} slotRef={slotRefs.current[i]} animationOffset={slotOffsets[i]} />
              </motion.div>);

          })}
          <GoldParticles trigger={particleTick} />
        </div>
      </div>

      {/* Grid */}
      <div className="feature-grid">
        {FEATURES.map((f, i) =>
        <FeatureCard
          key={f.id}
          feat={f}
          selected={selectedIds.includes(f.id)}
          onTap={tapFeature}
          cardRef={(el) => {cardRefs.current[f.id] = el;}}
          iconDelay={iconDelays[i]} />

        )}
      </div>

      {/* Top-1 question */}
      <AnimatePresence>
        {selectedIds.length === 3 &&
        <motion.div
          initial={{ opacity: 0, y: 12 }}
          animate={{ opacity: 0.7, y: 0 }}
          exit={{ opacity: 0 }}
          transition={{ duration: 0.32, delay: 0.1 }}
          className="top1-q">
          
            <div className="eyebrow" style={{ marginBottom: 10 }}>Of those three…</div>
            <h3 className="prata" style={{ fontSize: 22, lineHeight: 1.2, margin: '0 0 16px' }}>Which is the <span className="gold-text">#1 thing</span> you'd want most?</h3>
            <div className="opt-grid">
              {selectedIds.map((id) => {
              const f = FEATURES.find((x) => x.id === id);
              return (
                <Opt
                  key={id}
                  pressed={state.topFeature === id}
                  onClick={() => set({ topFeature: id })}>
                  {f.name}</Opt>);

            })}
            </div>
          </motion.div>
        }
      </AnimatePresence>

      <AnimatePresence>
        {ready &&
        <motion.div
          initial={{ opacity: 0, y: 10 }}
          animate={{ opacity: 1, y: 0 }}
          exit={{ opacity: 0 }}
          transition={{ duration: 0.28 }}
          style={{ marginTop: 32 }}>
          
            <CTA onClick={onContinue}>Continue <span className="arrow">→</span></CTA>
          </motion.div>
        }
      </AnimatePresence>

      {/* Fly layer */}
      <FlyLayer flying={flying} />
    </section>);

}

function FlyLayer({ flying }) {
  if (!flying.length) return null;
  return ReactDOM.createPortal(
    <div className="fly-layer" aria-hidden="true">
      {flying.map((f) => {
        const { fromRect, toRect, name, dir, key, icon } = f;
        const dx = toRect.left - fromRect.left;
        const dy = toRect.top - fromRect.top;
        const targetW = Math.max(120, toRect.width);
        const fromW = fromRect.width;
        const scaleEnd = targetW / fromW;
        // Arc: midpoint lifted
        const midY = dy / 2 - (dir === 'in' ? 36 : 24);
        return (
          <motion.div
            key={key}
            className="fly-card"
            initial={{
              left: fromRect.left, top: fromRect.top,
              width: fromW,
              x: 0, y: 0,
              scale: 1, opacity: 1
            }}
            animate={{
              x: [0, dx * 0.5, dx],
              y: [0, midY, dy],
              scale: dir === 'in' ? [1.04, 0.85, scaleEnd] : [1, 1, scaleEnd],
              opacity: [1, 1, 0]
            }}
            transition={{
              duration: 0.45,
              ease: [0.2, 0.7, 0.2, 1],
              times: [0, 0.5, 1]
            }}
            style={{ position: 'absolute' }}>
            
            <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
              <span style={{ width: 28, height: 28, flex: '0 0 auto', position: 'relative', display: 'inline-block' }}>
                {FEATURE_ICONS[icon] || FEATURE_ICONS.proposal}
              </span>
              <div className="feat-name" style={{ fontSize: 14, lineHeight: 1.25 }}>{name}</div>
            </div>
            <div style={{ fontSize: 10, letterSpacing: '0.18em', textTransform: 'uppercase', color: 'var(--ll-gold-flat)', fontWeight: 600, marginTop: 8 }}>
              {dir === 'in' ? 'Adding…' : 'Returning…'}
            </div>
          </motion.div>);

      })}
    </div>,
    document.body
  );
}

/* ---------------- Pricing ---------------- */
function ScenePricing({ state, set, onContinue }) {
  const ready = state.would_switch;
  return (
    <section id="pricing" className="scene">
      <Eyebrow>Step 04 — How Do We Win You Over</Eyebrow>
      <h2 className="headline balance">How do we win you over?</h2>
      <p className="body">Tell us what it would take — honest answers help us build the right thing.</p>

      <div style={{ marginTop: 32 }}>
        <div className="eyebrow" style={{ marginBottom: 12, color: "rgb(0, 0, 0)" }}>If a Live Luxury suite replaced 3+ tools at lower total cost, would you switch?</div>
        <OptGroup
          value={state.would_switch}
          options={["Yes, definitely", "Probably", "Maybe", "No"]}
          onChange={(v) => set({ would_switch: v })}
          columns={2} />
        
      </div>

      <div style={{ marginTop: 32 }}>
        <div className="eyebrow" style={{ marginBottom: 12, color: "rgb(0, 0, 0)" }}>What would make this a no-brainer for you?</div>
        <textarea
          className="editorial"
          maxLength={280}
          value={state.no_brainer_text}
          onChange={(e) => set({ no_brainer_text: e.target.value })}
          placeholder="One sentence is plenty." />
        
        <div className="row-counter" style={{ textAlign: 'right', marginTop: 6 }}>
          <span className="num"><b>{state.no_brainer_text.length}</b>/280</span>
        </div>
      </div>

      <AnimatePresence>
        {ready &&
        <motion.div
          initial={{ opacity: 0, y: 8 }} animate={{ opacity: 1, y: 0 }} exit={{ opacity: 0 }}
          transition={{ duration: 0.25 }}
          style={{ marginTop: 36 }}>
          
            <CTA onClick={onContinue}>Continue <span className="arrow">→</span></CTA>
          </motion.div>
        }
      </AnimatePresence>
    </section>);

}

/* ---------------- Bonus ---------------- */
function SceneBonus({ state, set, onSubmit }) {
  return (
    <section id="bonus" className="scene">
      <Eyebrow>Step 05 — Almost Done</Eyebrow>
      <h2 className="headline balance">Two more — both optional.</h2>

      <div style={{ marginTop: 28 }}>
        <div className="eyebrow" style={{ marginBottom: 12, color: "rgb(0, 0, 0)" }}>What's missing? Anything we didn't ask about?</div>
        <textarea
          className="editorial"
          value={state.missing_features_text}
          onChange={(e) => set({ missing_features_text: e.target.value })}
          placeholder="Anything we didn't ask about that would change your business." />
        
      </div>

      <div style={{ marginTop: 32 }}>
        <div className="eyebrow" style={{ marginBottom: 12, color: "rgb(0, 0, 0)" }}>Open to a 15-minute call to share more detail?</div>
        <OptGroup
          value={state.open_to_call}
          options={["Yes", "No"]}
          onChange={(v) => set({ open_to_call: v })}
          columns={2} />
        
      </div>

      <div style={{ marginTop: 44 }}>
        <div className="cta-gold-wrap" style={{ display: 'block' }}>
          <button className="cta large" onClick={onSubmit} type="button" style={{ width: '100%' }}>
            Submit my survey <span className="arrow">→</span>
          </button>
        </div>
      </div>
    </section>);

}

/* ---------------- Thank You ---------------- */
function SceneThanks({ state }) {
  const num = state.respondent_number;
  const founding = num <= 50;
  // Confetti
  const [shower, setShower] = useState(0);
  useEffect(() => {setShower(1);}, []);
  return (
    <section id="thanks" className="scene">
      <div className="thanks">
        <Ornament />
        <div className="eyebrow" style={{ marginTop: 24 }}>Submitted</div>
        <h1 className="display gold balance" style={{ margin: '18px auto 22px', maxWidth: 560 }}>
          You're respondent <span className="num">#{num}</span>.
        </h1>
        <p className="body" style={{ maxWidth: 520, margin: '0 auto' }}>
          Thank you. We'll send your personal benchmark report within seven days, showing how your tool spend compares to your Live Luxury peers — anonymously.
        </p>

        {founding &&
        <div style={{ marginTop: 32, position: 'relative' }}>
            <div className="gold-frame" style={{ padding: '20px 22px', maxWidth: 520, margin: '0 auto' }}>
              <div className="eyebrow" style={{ color: 'var(--ll-gold-flat)', marginBottom: 8 }}>Founding-member access</div>
              <div className="prata" style={{ fontSize: 22, lineHeight: 1.25, color: 'var(--ll-ink)' }}>
                You've locked in founding-member pricing on the AI Suite — <span className="gold-text">half off, for life.</span>
              </div>
              <div style={{ marginTop: 10, fontStyle: 'italic', fontFamily: "'Josefin Slab', serif", fontSize: 13, color: 'var(--ll-mist)' }}>
                Pending Pat's confirmation. Treat as placeholder.
              </div>
            </div>
          </div>
        }

        <div className="signature" style={{ maxWidth: 520, margin: '48px auto 0' }}>
          From the desk of <span className="who">Courtney Gardner</span> — Live Luxury
        </div>

        {/* Gold particles */}
        <ConfettiShower trigger={shower} />
      </div>
    </section>);

}

function ConfettiShower({ trigger }) {
  const [seed, setSeed] = useState(0);
  useEffect(() => {setSeed((s) => s + 1);}, [trigger]);
  if (!trigger) return null;
  const N = 28;
  return (
    <div style={{ position: 'fixed', inset: 0, pointerEvents: 'none', zIndex: 5, overflow: 'hidden' }}>
      {Array.from({ length: N }).map((_, i) => {
        const left = Math.random() * 100;
        const dur = 2.4 + Math.random() * 1.4;
        const delay = Math.random() * 0.6;
        const size = 4 + Math.random() * 6;
        const dx = (Math.random() - 0.5) * 80;
        const rot = Math.random() * 360;
        return (
          <motion.div
            key={`c-${seed}-${i}`}
            initial={{ y: -40, x: 0, opacity: 0, rotate: 0 }}
            animate={{ y: window.innerHeight + 60, x: dx, opacity: [0, 1, 1, 0], rotate: rot }}
            transition={{ duration: dur, delay, ease: 'linear', times: [0, 0.1, 0.85, 1] }}
            style={{
              position: 'absolute', top: 0, left: `${left}%`,
              width: size, height: size * 0.4,
              background: 'var(--ll-gold)'
            }} />);


      })}
    </div>);

}

/* ---------------- Progress rail ---------------- */
function ProgressRail({ activeId, submitted }) {
  const idx = STEPS.findIndex((s) => s.id === activeId);
  const stepIdx = clamp(idx, 0, STEPS.length - 1);
  // Steps 1..5 are the real survey steps; step 0 is the Welcome intro and isn't counted.
  const totalSteps = STEPS.length - 1; // 5
  const onWelcome = stepIdx === 0 && !submitted;
  const stepNumber = submitted ?
  String(totalSteps).padStart(2, '0') :
  onWelcome ? "" : String(stepIdx).padStart(2, '0');
  const pct = submitted ?
  100 :
  onWelcome ? 0 : stepIdx / totalSteps * 100;
  const label = submitted ? "Complete" : STEPS[stepIdx].label;
  return (
    <div className="progress-rail" role="navigation" aria-label="Survey progress">
      <div className="progress-rail-inner">
        <div className="wordmark"><img src="assets/liveluxury-kw-logo.png" alt="Live Luxury · Keller Williams" /></div>
        <div style={{ display: 'grid', gap: 6 }}>
          <div className="progress-track">
            <div className="progress-fill" style={{ width: pct + "%" }} />
          </div>
          <div className="progress-step">
            {stepNumber && <b>{stepNumber} · </b>}
            {label.replace(/^Step \d+ — /, '')}
          </div>
        </div>
        <div className="num eyebrow" style={{ whiteSpace: 'nowrap' }}>
          {onWelcome ?
          <span style={{ color: 'var(--ll-mist)' }}>—</span> :

          <>
              {submitted ? totalSteps : stepIdx}
              /<span style={{ color: 'var(--ll-gold-flat)' }}>{totalSteps}</span>
            </>
          }
        </div>
      </div>
    </div>);

}

/* ---------------- Root ---------------- */
function App() {
  const [state, setState] = useState({
    headshot: MOCK_AGENT.headshot,
    name: MOCK_AGENT.name,
    email: MOCK_AGENT.email,
    market_center: MOCK_AGENT.market_center,
    years_in_re: null,
    role: null,
    transactions_2024: null,
    volume_2024: null,
    spend: Object.fromEntries(CATEGORIES.map((c) => [c.id, { paid: null, vendor: null, cost: null, costSource: null, satisfaction: null, switch: null, freeToolName: "" }])),
    selectedFeatures: [],
    topFeature: null,
    max_monthly_fee: null,
    stack_other_text: "",
    would_switch: null,
    no_brainer_text: "",
    missing_features_text: "",
    open_to_call: null,
    submitted: false,
    respondent_number: null
  });
  const set = useCallback((patch) => setState((s) => ({ ...s, ...patch })), []);
  const ids = useMemo(() => state.submitted ? ["thanks"] : STEPS.map((s) => s.id), [state.submitted]);
  const activeId = useInView(ids);

  const scrollTo = (id) => {
    const el = document.getElementById(id);
    if (!el) return;
    const top = el.getBoundingClientRect().top + window.scrollY - 64;
    window.scrollTo({ top, behavior: 'smooth' });
  };

  const onSubmit = () => {
    const num = Math.floor(Math.random() * 80) + 1;
    set({ submitted: true, respondent_number: num });
    setTimeout(() => scrollTo("thanks"), 60);
  };

  // Scroll to top on mount
  useEffect(() => {window.scrollTo(0, 0);}, []);

  return (
    <>
      <ProgressRail activeId={activeId} submitted={state.submitted} />
      <main className="stage">
        {!state.submitted ?
        <>
            <SceneWelcome onContinue={() => scrollTo("about")} />
            <SceneAbout state={state} set={set} onContinue={() => scrollTo("stack")} />
            <SceneStack state={state} set={set} onContinue={() => scrollTo("features")} />
            <SceneFeatures state={state} set={set} onContinue={() => scrollTo("pricing")} />
            <ScenePricing state={state} set={set} onContinue={() => scrollTo("bonus")} />
            <SceneBonus state={state} set={set} onSubmit={onSubmit} />
          </> :

        <SceneThanks state={state} />
        }
      </main>

      <button
        className="dev-pill"
        onClick={() => {console.log("Live Luxury survey state:", state);}}
        title="Log state">
        Log state</button>
    </>);

}

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