/* =====================================================================
   Contact v2 — "004 / Start a project"
   Chip-driven intake: agent asks one question at a time, form fills live.
   ===================================================================== */
const { useEffect: useEfCt, useRef: useRefCt, useState: useStCt } = React;

const FIELDS = [
  { key:'name',        label:'Name',              type:'text',     placeholder:'Your full name' },
  { key:'business',    label:'Business name',     type:'text',     placeholder:'Company / trade' },
  { key:'email',       label:'Email',             type:'email',    placeholder:'you@example.com' },
  { key:'phone',       label:'Phone',             type:'tel',      placeholder:'(405) 555-0100', optional:true },
  { key:'projectType', label:'Type of site',      type:'select',   options:['Simple one-pager','Full business website','Online store','Listings (cars, real estate, etc.)','Google presence & SEO only','AI helper / chatbot only','Custom / not sure yet'] },
  { key:'budget',      label:'Budget range',      type:'select',   options:['Under $3k','$3–8k','$8–25k','$25k+','Not sure yet'] },
  { key:'timeline',    label:'Timeline',          type:'select',   options:['ASAP (this month)','1–3 months','3–6 months','Flexible'] },
  { key:'description', label:'What you need',     type:'textarea', placeholder:'Tell us about your business and what you need' },
];

function getPublicContact() {
  const site = window.MWT_SITE || {};
  const contact = site.contact || {};
  return {
    email: contact.email || 'hello@midwest.tech',
    phone: contact.phone || '+14055550142',
    phoneHref: contact.phoneHref || 'tel:+14055550142',
  };
}

function formatPhoneLabel(phone) {
  if (!phone) return '+1 (405) 555-0142';
  if (phone === '+14055550142') return '+1 (405) 555-0142';
  return phone;
}

const GREETINGS = [
  "Hey! Let's get your project started. What's your name?",
  "Hi there — happy to help. First, what should we call you?",
  "Welcome. Let's put together a free quote. What's your name?",
];

function getPrompt(field, values) {
  const prompts = {
    name:        () => GREETINGS[Math.floor(Math.random() * GREETINGS.length)],
    business:    (v) => `Nice to meet you, ${v.name}! What's the name of your business?`,
    email:       (v) => `Great. What's the best email for ${v.business || 'your project'}?`,
    phone:       ()  => `Phone number? (Optional — skip if you prefer email only.)`,
    projectType: (v) => `What kind of site does ${v.business || 'your business'} need?`,
    budget:      ()  => `What's your rough budget? No wrong answer — helps us scope it right.`,
    timeline:    ()  => `What's your timeline looking like?`,
    description: (v) => `Almost done! Tell us a bit more about ${v.business||'your project'} and what you need.`,
  };
  return (prompts[field.key] || (() => `What's your ${field.label.toLowerCase()}?`))(values);
}

function hasValue(value) {
  return typeof value === 'string' && value.trim().length > 0;
}

function canSkipField(field, values) {
  if (field.key === 'name') return hasValue(values.business);
  if (field.key === 'business') return hasValue(values.name);
  if (field.key === 'email') return hasValue(values.phone);
  if (field.key === 'phone') return hasValue(values.email);
  return Boolean(field.optional);
}

function getChips(field, values) {
  if (field.options) {
    return [...field.options, ...(canSkipField(field, values) ? ['Skip for now'] : [])];
  }
  return canSkipField(field, values) ? ['Skip for now'] : [];
}

function makeSessionId() {
  if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
    return crypto.randomUUID();
  }

  const bytes = new Uint8Array(16);
  if (typeof crypto !== 'undefined' && typeof crypto.getRandomValues === 'function') {
    crypto.getRandomValues(bytes);
  } else {
    for (let i = 0; i < bytes.length; i += 1) {
      bytes[i] = Math.floor(Math.random() * 256);
    }
  }

  bytes[6] = (bytes[6] & 0x0f) | 0x40;
  bytes[8] = (bytes[8] & 0x3f) | 0x80;

  const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, '0'));
  return `${hex.slice(0, 4).join('')}-${hex.slice(4, 6).join('')}-${hex.slice(6, 8).join('')}-${hex.slice(8, 10).join('')}-${hex.slice(10, 16).join('')}`;
}

function getNextFieldKey(values, skippedSet) {
  return FIELDS.find((field) => !hasValue(values[field.key]) && !skippedSet.has(field.key))?.key ?? null;
}

function normalizeServerReply(data) {
  if (!data || typeof data !== 'object') return null;

  const candidate = data;
  if (
    typeof candidate.reply !== 'string' ||
    !candidate.reply.trim() ||
    !Array.isArray(candidate.suggestions) ||
    (candidate.nextField !== null && typeof candidate.nextField !== 'string') ||
    typeof candidate.done !== 'boolean' ||
    !candidate.extracted ||
    typeof candidate.extracted !== 'object'
  ) {
    return null;
  }

  const suggestions = candidate.suggestions
    .filter((value) => typeof value === 'string')
    .slice(0, 6);

  const extracted = Object.fromEntries(
    Object.entries(candidate.extracted).filter(
      ([key, value]) => typeof key === 'string' && typeof value === 'string' && value.trim().length > 0,
    ),
  );

  return {
    reply: candidate.reply.trim(),
    suggestions,
    nextField: candidate.nextField,
    extracted,
    done: candidate.done,
  };
}

const LOCAL_DONE_REPLY =
  "Perfect — we have everything we need! Expect a reply within one business day. We'll put together a detailed quote just for you.";

const CHAT_SCHEMA = FIELDS.map(({ key, label, type, options, optional, placeholder }) => ({
  key,
  label,
  type,
  options,
  optional,
  placeholder,
}));

const FIELD_BY_KEY = Object.fromEntries(CHAT_SCHEMA.map((field) => [field.key, field]));

function isSkipText(value) {
  const normalized = value.trim().toLowerCase();
  return (
    normalized === 'skip' ||
    normalized === 'skip for now' ||
    normalized === 'skip it' ||
    normalized === '—'
  );
}

function getSkipRefusal(fieldKey, values) {
  if ((fieldKey === 'name' || fieldKey === 'business') && !hasValue(values.name) && !hasValue(values.business)) {
    return 'I need at least a name or business name so I know how to address you.';
  }
  if ((fieldKey === 'email' || fieldKey === 'phone') && !hasValue(values.email) && !hasValue(values.phone)) {
    return 'I need at least an email or phone number so I have a way to follow up.';
  }
  return 'Thanks. Keep going and I’ll fill the form as we go.';
}

function getConversationFloorState(values) {
  const hasIdentity = hasValue(values.name) || hasValue(values.business);
  const hasContact = hasValue(values.email) || hasValue(values.phone);
  return hasIdentity && hasContact;
}

function getTranscriptMessages(messages) {
  return messages.map((message) => ({
    role: message.role === 'bot' ? 'assistant' : 'user',
    content: message.text,
  }));
}

function uniqueStrings(values) {
  return Array.from(new Set(values.filter((value) => typeof value === 'string' && value.trim().length > 0)));
}

function getLocalTurnOutcome({ field, value, values, skipped }) {
  const wantsSkip = isSkipText(value);
  const skippedSet = new Set(skipped);
  const nextValues = { ...values };

  if (wantsSkip) {
    if (!canSkipField(field, values)) {
      return {
        reply: getSkipRefusal(field.key, values),
        chips: getChips(field, values),
        values,
        skipped,
        done: false,
        nextFieldKey: field.key,
      };
    }

    skippedSet.add(field.key);
  } else {
    nextValues[field.key] = value.trim();
  }

  const nextFieldKey = getNextFieldKey(nextValues, skippedSet);
  const allAccountedFor = CHAT_SCHEMA.every(
    (schemaField) => hasValue(nextValues[schemaField.key]) || skippedSet.has(schemaField.key),
  );
  const done = !nextFieldKey && allAccountedFor && getConversationFloorState(nextValues);

  if (done) {
    return {
      reply: LOCAL_DONE_REPLY,
      chips: [],
      values: nextValues,
      skipped: Array.from(skippedSet),
      done: true,
      nextFieldKey: null,
    };
  }

  const nextField = nextFieldKey ? FIELD_BY_KEY[nextFieldKey] : field;
  return {
    reply: getPrompt(nextField, nextValues),
    chips: getChips(nextField, nextValues),
    values: nextValues,
    skipped: Array.from(skippedSet),
    done: false,
    nextFieldKey,
  };
}

function getServerRequestBody({ messages, sessionId, values, skipped }) {
  return {
    sessionId,
    messages: getTranscriptMessages(messages),
    values,
    skipped,
    tone: 'neutral',
    schema: CHAT_SCHEMA,
  };
}

/* ---- Chat bubble ---- */
function Bubble({ role, text, animate }) {
  return (
    <div style={{ display:'flex', justifyContent:role==='bot'?'flex-start':'flex-end',
      animation:animate?'mwFadeIn .35s':'none' }}>
      <div style={{ maxWidth:'82%', padding:'10px 14px',
        background:role==='bot'?'var(--bg-elev-2)':'color-mix(in oklab,var(--accent) 12%,transparent)',
        border:`1px solid ${role==='bot'?'var(--border-strong)':'color-mix(in oklab,var(--accent) 35%,transparent)'}`,
        fontFamily:'var(--font-body)', fontSize:14, lineHeight:1.5,
        color:role==='bot'?'var(--fg-1)':'var(--fg-1)',
        ...(role==='bot' ? { borderRadius:'0 8px 8px 8px' } : { borderRadius:'8px 0 8px 8px' }) }}>
        {text}
      </div>
    </div>
  );
}

/* ---- Form preview panel ---- */
function FormPreview({ values, fields, activeKey, skipped = [] }) {
  const skippedSet = new Set(skipped);
  return (
    <div style={{ border:'1px solid var(--border-strong)', background:'var(--bg-elev-1)', overflow:'hidden', height:'100%' }}>
      <div style={{ padding:'12px 16px', borderBottom:'1px solid var(--border)',
        fontFamily:'var(--font-mono)', fontSize:9, letterSpacing:'0.18em', color:'var(--fg-4)',
        display:'flex', justifyContent:'space-between' }}>
        <span>— PROJECT BRIEF</span>
        <span style={{color:'var(--accent)'}}>FILLING LIVE</span>
      </div>
      <div style={{ padding:16, display:'flex', flexDirection:'column', gap:0 }}>
        {fields.map(f => {
          const val = values[f.key];
          const isActive = f.key === activeKey;
          const isSkipped = skippedSet.has(f.key);
          const filled = hasValue(val);
          return (
            <div key={f.key} style={{ padding:'10px 0', borderBottom:'1px solid var(--border)',
              opacity: filled || isSkipped || isActive ? 1 : 0.4, transition:'opacity .3s' }}>
              <div style={{ fontFamily:'var(--font-mono)', fontSize:8, letterSpacing:'0.16em',
                textTransform:'uppercase',
                color: isActive ? 'var(--accent)' : filled ? 'var(--fg-3)' : isSkipped ? 'var(--fg-4)' : 'var(--fg-4)',
                marginBottom:4, transition:'color .3s' }}>
                {isActive && <span style={{marginRight:6}}>▶</span>}{f.label}
                {isSkipped && !isActive && <span style={{marginLeft:6, color:'var(--fg-4)'}}>SKIPPED</span>}
              </div>
              <div style={{ fontFamily:'var(--font-body)', fontSize:13, color:'var(--fg-1)',
                minHeight:16, fontStyle:filled?'normal':'italic',
                color:filled ? 'var(--fg-1)' : isSkipped ? 'var(--fg-4)' : 'var(--fg-4)' }}>
                {filled || (isSkipped ? 'Skipped' : isActive ? <span style={{animation:'mwBreathe 1s infinite',display:'inline-block',width:2,height:12,background:'var(--accent)',verticalAlign:'middle'}}/>  : '—')}
              </div>
            </div>
          );
        })}
      </div>
      <div style={{ padding:'14px 16px', borderTop:'1px solid var(--border)',
        fontFamily:'var(--font-mono)', fontSize:9, color:'var(--fg-4)', letterSpacing:'0.12em' }}>
        MIDWEST TECHNOLOGIES · FREE QUOTE · NO COMMITMENT
      </div>
    </div>
  );
}

/* ---- Pre-start swank panel ----
   Two-column intake card. Left: a calm scripted demo — bot question, brief
   typing indicator, then user reply. Loops gently with a fade. No jumpy
   counters. Right: the real pitch + CTA. */
function PreStartPanel({ onStart }) {
  const contact = getPublicContact();
  // Rotating scripted demos — all featuring Mason. Each loop picks the next.
  const scripts = [
    [
      { role:'bot',  text:"Hey — Mason here at Midwest. What kind of site are you after?", typingBefore: 700 },
      { role:'user', text:"Body shop in Edmond. Need something real, not a Wix template.", typingBefore: 500 },
      { role:'bot',  text:"Got it. Photo gallery, online quote requests, Google reviews on the homepage?", typingBefore: 800 },
      { role:'user', text:"Yeah, all of that. Plus a way for customers to text me directly.", typingBefore: 600 },
      { role:'bot',  text:"Easy. We'll have a quote on your desk tomorrow.", typingBefore: 800 },
    ],
    [
      { role:'bot',  text:"Mason at Midwest — what's the business?", typingBefore: 600 },
      { role:'user', text:"Real estate. Need a listings site that doesn't look like Zillow's hand-me-down.", typingBefore: 600 },
      { role:'bot',  text:"How many active listings, and do you want IDX feed or manual?", typingBefore: 800 },
      { role:'user', text:"Around 80, manual is fine — I want full control of the photos.", typingBefore: 600 },
      { role:'bot',  text:"Perfect. Map view, saved searches, the works. Quote by tomorrow.", typingBefore: 800 },
    ],
    [
      { role:'bot',  text:"Mason here. Tell me about the project.", typingBefore: 600 },
      { role:'user', text:"Lawn care company in Norman. Just need leads to come in while I'm mowing.", typingBefore: 600 },
      { role:'bot',  text:"Got it — quote form, before/afters, and an AI helper that books estimates after hours?", typingBefore: 900 },
      { role:'user', text:"That's exactly what I need. How long?", typingBefore: 500 },
      { role:'bot',  text:"Three days for the site. AI helper a few days after. Sound good?", typingBefore: 800 },
    ],
    [
      { role:'bot',  text:"Mason — what're we building?", typingBefore: 500 },
      { role:'user', text:"Boutique in OKC. Want to actually sell online without paying Shopify forever.", typingBefore: 600 },
      { role:'bot',  text:"How many products to start, and do you ship or local pickup?", typingBefore: 800 },
      { role:'user', text:"~40 products. Both — pickup is bigger here.", typingBefore: 600 },
      { role:'bot',  text:"Done. Stripe, pickup scheduling, low-fee checkout. Quote by tomorrow.", typingBefore: 900 },
    ],
  ];

  const HOLD_AFTER = 1300;   // pause after bubble before next typing indicator
  const LOOP_PAUSE = 2000;   // pause at end of script before swapping
  const FADE = 360;          // crossfade duration between scripts
  const REVEAL_GAP = 90;     // tight handoff between dots → bubble

  // Crossfade architecture (fixes the "disappear then reappear" rough cut):
  //   The previous implementation owned a single visible transcript that
  //   was opacity-faded to 0, mutated in place (script swap + step reset),
  //   then opacity-faded back in. That produced a perceptible empty frame
  //   where the panel looked broken — every loop. We now keep TWO
  //   transcripts mounted in a position-stacked layer; the one that is
  //   currently advancing is fully opaque, the previous one is fading
  //   out behind it. Swap = bump the active layer index, reset state on
  //   the now-inactive layer. The user sees a continuous chat: outgoing
  //   transcript softens away while incoming transcript types in over it.
  const [activeLayer, setActiveLayer] = useStCt(0);              // 0 or 1
  const [scripts0, setScripts0] = useStCt(scripts[0 % scripts.length]);
  const [scripts1, setScripts1] = useStCt(scripts[1 % scripts.length]);
  const [scriptIdx, setScriptIdx] = useStCt(0);                  // logical script index
  const [step, setStep] = useStCt(0);                            // bubbles revealed in active layer
  const [typing, setTyping] = useStCt(true);                     // dots in active layer
  const [pulse, setPulse] = useStCt(false);

  // The active script is whichever buffer the active layer points at.
  const script = activeLayer === 0 ? scripts0 : scripts1;

  // Honor reduced-motion: skip the typing/loop animation entirely and
  // render the final state of the first script as a static transcript.
  const reducedMotion = (typeof window !== 'undefined' && window.matchMedia)
    ? window.matchMedia('(prefers-reduced-motion: reduce)').matches
    : false;

  useEfCt(() => {
    if (reducedMotion) return;
    if (step >= script.length) {
      // End of current script → preload the NEXT script into the
      // currently-inactive layer, switch active to it (which triggers
      // the crossfade), and reset typing on the new layer. The previous
      // layer fades out for `FADE` ms while the new one fades in.
      const t = setTimeout(() => {
        const nextIdx = (scriptIdx + 1) % scripts.length;
        if (activeLayer === 0) {
          setScripts1(scripts[nextIdx]);
          setActiveLayer(1);
        } else {
          setScripts0(scripts[nextIdx]);
          setActiveLayer(0);
        }
        setScriptIdx(nextIdx);
        setStep(0);
        setTyping(true);
      }, LOOP_PAUSE);
      return () => clearTimeout(t);
    }
    const item = script[step];
    if (typing) {
      const t = setTimeout(() => setTyping(false), item.typingBefore);
      return () => clearTimeout(t);
    } else {
      const t = setTimeout(() => {
        setStep(s => s + 1);
        // small gap so dots disappear, then immediately new dots appear
        setTimeout(() => setTyping(true), REVEAL_GAP);
      }, HOLD_AFTER);
      return () => clearTimeout(t);
    }
  }, [step, typing, activeLayer, scriptIdx, reducedMotion]);

  // `progress` drives the quiet progress bar in the chrome footer.
  // Active-layer step + half-typing half-bubble lerp; clamped to [0,1].
  const progress = reducedMotion ? 1 : Math.min(1, (step + (typing ? 0 : 0.5)) / script.length);

  const stats = [
    { n:'42+', l:'sites launched in OKC & nationwide' },
    { n:'14 days', l:'average from contract to live site' },
    { n:'100%', l:'client ownership — no lock-in' },
  ];
  const includes = [
    'Free, detailed quote inside 1 business day',
    'A real human reply (no chatbot, no form bots)',
    'Fixed price · no surprises · cancel anytime',
  ];

  return (
    <div style={{
      gap:0,
      border:'1px solid var(--border-strong)', background:'var(--bg-elev-1)',
      maxWidth:1100, margin:'0 auto', overflow:'hidden',
      boxShadow:'0 30px 80px -30px rgba(0,0,0,.6), 0 0 0 1px color-mix(in oklab, var(--accent) 8%, transparent)',
    }} className="mw-prestart">
      {/* LEFT — terminal preview. Fixed height so the cycling demo never
          changes the container size — that was bumping the right column's
          "Start your free quote" button further down with every new bubble
          on mobile (single-column collapse). The transcript inside is
          flex:1 with justify-content:flex-end + overflow:hidden so newest
          bubbles anchor to the bottom and earliest ones scroll off cleanly. */}
      <div className="mw-prestart-demo" style={{
        position:'relative', padding:'22px 24px 26px', background:'var(--bg)',
        borderRight:'1px solid var(--border)', height:'clamp(360px, 50vh, 460px)',
        display:'flex', flexDirection:'column', gap:14, overflow:'hidden',
        backgroundImage:`radial-gradient(circle at 100% 0%, color-mix(in oklab, var(--accent) 10%, transparent), transparent 55%)`,
      }}>
        {/* terminal chrome — status pill, no jumpy counter */}
        <div style={{ display:'flex', alignItems:'center', gap:10,
          paddingBottom:12, borderBottom:'1px solid var(--border)' }}>
          <span style={{width:8,height:8,borderRadius:'50%',background:'var(--accent)',boxShadow:'0 0 10px var(--accent)',animation:'mwBreathe 2.4s infinite'}}/>
          <span style={{fontFamily:'var(--font-mono)',fontSize:10,letterSpacing:'0.18em',color:'var(--fg-3)'}}>
            MIDWEST · INTAKE
          </span>
          <span style={{flex:1}}/>
          <span style={{fontFamily:'var(--font-mono)',fontSize:9,letterSpacing:'0.16em',color:'var(--fg-4)',
            display:'inline-flex',alignItems:'center',gap:6}}>
            <span style={{width:5,height:5,borderRadius:'50%',background:'var(--accent)'}}/>
            ONLINE
          </span>
        </div>

        {/* Live transcript — TWO stacked layers, crossfading. The active
            layer is fully opaque and types its bubbles in; the inactive
            layer keeps its final state and fades out underneath. There is
            no moment where the panel goes empty: the outgoing chat is
            still visible at >0 opacity while the incoming chat starts
            typing on top. Both layers stay mounted in absolute position
            so the column height never shifts. minHeight:0 lets flex:1
            shrink inside its fixed-height parent.

            Renders are keyed off `activeLayer` for the visual transitions;
            React reuses each layer's DOM nodes across script swaps so
            there's no mount/unmount flash. */}
        <div style={{position:'relative',flex:1,minHeight:0}}>
          {[0, 1].map((layerIdx) => {
            const isActive = activeLayer === layerIdx;
            const layerScript = layerIdx === 0 ? scripts0 : scripts1;
            // Active layer types live; inactive layer shows its final
            // state (full transcript, no typing dots) so it has something
            // to fade out FROM. On reduced-motion both layers are static.
            const layerVisible = isActive
              ? (reducedMotion ? layerScript : layerScript.slice(0, step))
              : layerScript;
            const layerTyping = isActive && !reducedMotion && typing && step < layerScript.length;
            return (
              <div key={layerIdx}
                aria-hidden={isActive ? undefined : 'true'}
                style={{
                  position:'absolute', inset:0,
                  display:'flex', flexDirection:'column', gap:10,
                  justifyContent:'flex-end',
                  opacity: isActive ? 1 : 0,
                  transform: isActive ? 'translateY(0)' : 'translateY(-4px)',
                  transition: reducedMotion ? 'none'
                    : `opacity ${FADE}ms cubic-bezier(.4,0,.2,1), transform ${FADE}ms cubic-bezier(.4,0,.2,1)`,
                  pointerEvents:'none',
                  willChange: isActive ? 'opacity, transform' : 'auto',
                }}>
                {layerVisible.map((l, i) => (
                  <div key={`${layerIdx}-${i}-${l.text.slice(0,8)}`}
                    style={{ animation: isActive && !reducedMotion ? 'mwFadeIn .4s var(--ease-out)' : 'none' }}>
                    {l.role === 'bot' && (
                      <div style={{display:'flex',justifyContent:'flex-start'}}>
                        <div style={{maxWidth:'88%',padding:'9px 13px',
                          background:'var(--bg-elev-2)',
                          border:'1px solid var(--border-strong)',
                          borderRadius:'0 8px 8px 8px',
                          fontFamily:'var(--font-body)',fontSize:13,lineHeight:1.45,color:'var(--fg-1)'}}>
                          {l.text}
                        </div>
                      </div>
                    )}
                    {l.role === 'user' && (
                      <div style={{display:'flex',justifyContent:'flex-end'}}>
                        <div style={{maxWidth:'88%',padding:'9px 13px',
                          background:'color-mix(in oklab,var(--accent) 14%,transparent)',
                          border:'1px solid color-mix(in oklab,var(--accent) 38%,transparent)',
                          borderRadius:'8px 0 8px 8px',
                          fontFamily:'var(--font-body)',fontSize:13,lineHeight:1.45,color:'var(--fg-1)'}}>
                          {l.text}
                        </div>
                      </div>
                    )}
                  </div>
                ))}
                {layerTyping && (
                  <div style={{display:'flex',
                    justifyContent: layerScript[step].role === 'bot' ? 'flex-start' : 'flex-end',
                    animation:'mwFadeIn .25s var(--ease-out)'}}>
                    <div style={{padding:'10px 14px',
                      background: layerScript[step].role === 'bot' ? 'var(--bg-elev-2)' : 'color-mix(in oklab,var(--accent) 14%,transparent)',
                      border:`1px solid ${layerScript[step].role === 'bot' ? 'var(--border-strong)' : 'color-mix(in oklab,var(--accent) 38%,transparent)'}`,
                      borderRadius: layerScript[step].role === 'bot' ? '0 8px 8px 8px' : '8px 0 8px 8px',
                      display:'flex',gap:4,alignItems:'center'}}>
                      <span style={{width:5,height:5,borderRadius:'50%',background:'var(--fg-3)',animation:'mwPulse 1s infinite'}}/>
                      <span style={{width:5,height:5,borderRadius:'50%',background:'var(--fg-3)',animation:'mwPulse 1s infinite .15s'}}/>
                      <span style={{width:5,height:5,borderRadius:'50%',background:'var(--fg-3)',animation:'mwPulse 1s infinite .3s'}}/>
                    </div>
                  </div>
                )}
              </div>
            );
          })}
        </div>

        {/* footer — quiet progress bar */}
        <div style={{display:'flex',alignItems:'center',gap:10,
          paddingTop:12,borderTop:'1px solid var(--border)'}}>
          <span style={{fontFamily:'var(--font-mono)',fontSize:9,letterSpacing:'0.16em',color:'var(--fg-4)'}}>
            DEMO · {scriptIdx + 1}/{scripts.length}
          </span>
          <span style={{flex:1,height:2,background:'var(--border)',position:'relative',overflow:'hidden'}}>
            <span style={{position:'absolute',left:0,top:0,bottom:0,
              width:`${progress*100}%`,background:'var(--accent)',
              transition:'width .6s var(--ease-out)'}}/>
          </span>
        </div>
      </div>

      {/* RIGHT — pitch column */}
      <div style={{
        padding:'28px 32px 30px',
        display:'flex',flexDirection:'column',gap:20,
        background:'linear-gradient(180deg, color-mix(in oklab, var(--accent) 4%, var(--bg-elev-1)) 0%, var(--bg-elev-1) 100%)'
      }}>
        <div style={{display:'flex',alignItems:'baseline',gap:10,flexWrap:'wrap'}}>
          <span style={{fontFamily:'var(--font-mono)',fontSize:10,letterSpacing:'0.2em',color:'var(--accent)'}}>● ACCEPTING WORK</span>
          <span style={{fontFamily:'var(--font-mono)',fontSize:10,letterSpacing:'0.16em',color:'var(--fg-4)'}}>
            · 3 SLOTS · MAY 2026
          </span>
        </div>

        <h3 style={{
          fontFamily:'var(--font-display)',
          fontSize:'clamp(30px,4vw,52px)',fontWeight:500,
          letterSpacing:'-0.035em',lineHeight:0.98,color:'var(--fg-1)',
          margin:0,overflowWrap:'break-word'
        }}>
          Tell us what you need.{' '}
          <em style={{fontFamily:'var(--font-editorial)',fontStyle:'italic',fontWeight:400,color:'var(--accent)'}}>
            We'll build it.
          </em>
        </h3>

        <p style={{fontFamily:'var(--font-body)',fontSize:15,lineHeight:1.55,color:'var(--fg-3)',margin:0,maxWidth:440}}>
          Two minutes of questions. One real human reply with a fixed-price quote inside one business day. That's it.
        </p>

        {/* what you get */}
        <ul style={{listStyle:'none',padding:0,margin:0,display:'flex',flexDirection:'column',gap:8}}>
          {includes.map((it, i) => (
            <li key={i} style={{display:'flex',alignItems:'flex-start',gap:10,
              fontFamily:'var(--font-body)',fontSize:13.5,lineHeight:1.5,color:'var(--fg-2)'}}>
              <span style={{flex:'0 0 auto',width:14,height:14,marginTop:3,
                border:'1px solid var(--accent)',
                background:'color-mix(in oklab,var(--accent) 18%,transparent)',
                display:'inline-flex',alignItems:'center',justifyContent:'center',
                fontFamily:'var(--font-mono)',fontSize:9,fontWeight:700,color:'var(--accent)'}}>✓</span>
              <span>{it}</span>
            </li>
          ))}
        </ul>

        {/* stats strip */}
        <div className="mw-prestart-stats" style={{display:'grid',gridTemplateColumns:`repeat(${stats.length},1fr)`,gap:0,
          borderTop:'1px solid var(--border)',borderBottom:'1px solid var(--border)',
          margin:'4px 0 2px'}}>
          {stats.map((s, i) => (
            <div key={i} style={{padding:'14px 12px 12px',
              borderRight:i<stats.length-1?'1px solid var(--border)':'none'}}>
              <div style={{fontFamily:'var(--font-display)',fontSize:28,fontWeight:600,
                letterSpacing:'-0.02em',color:'var(--fg-1)',lineHeight:1}}>{s.n}</div>
              <div style={{fontFamily:'var(--font-mono)',fontSize:9,letterSpacing:'0.14em',
                color:'var(--fg-4)',marginTop:6,textTransform:'uppercase'}}>{s.l}</div>
            </div>
          ))}
        </div>

        {/* CTA */}
        <div style={{display:'flex',flexDirection:'column',gap:10}}>
          <button onClick={() => {
              window['mwtTrack'] && window['mwtTrack']('select_content', {
                content_type: 'cta',
                content_id: 'contact_start_quote',
                cta_label: 'Start your free quote',
                page_section: 'contact_prestart',
                page_path: typeof window !== 'undefined' ? window.location.pathname : '',
              });
              window['mwtTrack'] && window['mwtTrack']('generate_lead', {
                method: 'form',
                lead_source: 'contact_prestart',
              });
              onStart();
            }}
            onMouseEnter={()=>setPulse(true)} onMouseLeave={()=>setPulse(false)}
            style={{
              position:'relative',display:'flex',alignItems:'center',justifyContent:'space-between',gap:14,
              padding:'18px 22px',background:'var(--accent)',color:'var(--accent-ink)',border:'none',
              fontFamily:'var(--font-display)',fontSize:18,fontWeight:600,letterSpacing:'-0.01em',
              cursor:'pointer',textAlign:'left',
              boxShadow: pulse ? '0 12px 32px -8px color-mix(in oklab, var(--accent) 70%, transparent)' : '0 4px 16px -4px color-mix(in oklab, var(--accent) 50%, transparent)',
              transform:pulse?'translateY(-1px)':'none',
              transition:'all .25s var(--ease-out)'
            }}>
            <span>Start your free quote</span>
            <span style={{display:'flex',alignItems:'center',gap:8,
              fontFamily:'var(--font-mono)',fontSize:11,letterSpacing:'0.16em'}}>
              <span style={{opacity:0.7}}>2 MIN</span>
              <span style={{fontSize:18,transform:pulse?'translateX(4px)':'none',transition:'transform .25s var(--ease-out)'}}>→</span>
            </span>
          </button>

          <div style={{display:'flex',gap:14,alignItems:'center',flexWrap:'wrap',
            fontFamily:'var(--font-mono)',fontSize:10,letterSpacing:'0.14em',color:'var(--fg-4)'}}>
            <a href={`mailto:${contact.email}`}
              onClick={() => window['mwtTrack'] && window['mwtTrack']('email_click', {
                email: contact.email,
                page_section: 'contact_prestart',
                page_path: typeof window !== 'undefined' ? window.location.pathname : '',
              })}
              style={{color:'var(--fg-3)',textDecoration:'none',borderBottom:'1px solid var(--border-strong)',paddingBottom:1}}>{contact.email.toUpperCase()}</a>
            <span style={{opacity:0.4}}>·</span>
            <a href={contact.phoneHref}
              onClick={() => window['mwtTrack'] && window['mwtTrack']('phone_call_click', {
                phone_number: contact.phone,
                page_section: 'contact_prestart',
                page_path: typeof window !== 'undefined' ? window.location.pathname : '',
              })}
              style={{color:'var(--fg-3)',textDecoration:'none',borderBottom:'1px solid var(--border-strong)',paddingBottom:1}}>{formatPhoneLabel(contact.phone)}</a>
            <span style={{opacity:0.4}}>·</span>
            <span>MON–FRI · 9–6 CT</span>
          </div>
        </div>
      </div>

    </div>
  );
}

/* ---- Main Contact section ---- */
function Contact() {
  const ref = useRefCt(null); useReveal(ref);
  const chatRef = useRefCt(null);
  const sessionIdRef = useRefCt(null);
  const startedRef = useRefCt(false);
  const postingRef = useRefCt(false);
  const [messages, setMessages] = useStCt([]);
  const [values, setValues] = useStCt({});
  const [skipped, setSkipped] = useStCt([]);
  const [inputVal, setInputVal] = useStCt('');
  const [typing, setTyping] = useStCt(false);
  const [sending, setSending] = useStCt(false);
  const [done, setDone] = useStCt(false);
  const [started, setStarted] = useStCt(false);
  const [activeFieldKey, setActiveFieldKey] = useStCt(FIELDS[0].key);
  const [chips, setChips] = useStCt([]);

  const skippedSet = new Set(skipped);
  const currentField = done
    ? null
    : CHAT_SCHEMA.find((field) => field.key === activeFieldKey) ||
      CHAT_SCHEMA.find((field) => !hasValue(values[field.key]) && !skippedSet.has(field.key)) ||
      CHAT_SCHEMA[0] ||
      null;
  const currentFieldIndex = currentField
    ? CHAT_SCHEMA.findIndex((field) => field.key === currentField.key)
    : CHAT_SCHEMA.length;
  const showTextInput = !done && currentField && !currentField.options;
  const controlsDisabled = typing || sending;
  const submittedTurns = messages.filter((message) => message.role === 'user').length;

  function ensureSessionId() {
    if (!sessionIdRef.current) {
      sessionIdRef.current = makeSessionId();
    }
    return sessionIdRef.current;
  }

  function applyOutcome(outcome) {
    setMessages((prev) => [...prev, { role: 'bot', text: outcome.reply, animate: true }]);
    setValues(outcome.values);
    setSkipped(outcome.skipped);
    setActiveFieldKey(outcome.done ? '' : outcome.nextFieldKey || currentField?.key || FIELDS[0].key);
    setChips(outcome.done ? [] : outcome.chips);
    setDone(outcome.done);
    setTyping(false);
    setSending(false);
    postingRef.current = false;
  }

  function startChat() {
    if (startedRef.current) return;
    startedRef.current = true;
    ensureSessionId();
    setStarted(true);
    setTyping(true);
    setDone(false);
    setChips([]);
    setMessages([]);
    setValues({});
    setSkipped([]);
    setInputVal('');
    setActiveFieldKey(FIELDS[0].key);

    window['mwtTrack'] && window['mwtTrack']('ai_chat_start', {
      chat_session_id: sessionIdRef.current,
      tone: 'neutral',
    });

    setTimeout(() => {
      setMessages([{ role: 'bot', text: getPrompt(FIELDS[0], {}), animate: true }]);
      setTyping(false);
    }, 550);
  }

  async function submitAnswer(val) {
    const field = currentField;
    if (!field || done || controlsDisabled || postingRef.current) return;

    const trimmed = val.trim();
    const wantsSkip = isSkipText(val);
    if (!trimmed && !wantsSkip) return;

    const sessionId = ensureSessionId();
    const userText = wantsSkip ? 'Skip for now' : trimmed;
    const valuesSnapshot = values;
    const skippedSnapshot = skipped;
    const requestValues = wantsSkip ? valuesSnapshot : { ...valuesSnapshot, [field.key]: trimmed };
    const requestSkipped = wantsSkip && canSkipField(field, valuesSnapshot)
      ? uniqueStrings([...skippedSnapshot, field.key])
      : skippedSnapshot.filter((key) => key !== field.key);
    const transcript = [...messages, { role: 'user', text: userText, animate: true }];
    const turnIndex = submittedTurns;

    setMessages(transcript);
    setValues(requestValues);
    setSkipped(requestSkipped);
    setInputVal('');
    setTyping(true);
    setSending(true);
    postingRef.current = true;

    window['mwtTrack'] && window['mwtTrack']('ai_chat_message_sent', {
      chat_session_id: sessionId,
      turn_index: turnIndex,
      field_name: field.key,
      message_length: wantsSkip ? 0 : trimmed.length,
    });

    let outcome;
    try {
      const response = await fetch('/api/chat', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(getServerRequestBody({
          messages: transcript,
          sessionId,
          values: requestValues,
          skipped: requestSkipped,
        })),
      });

      if (!response.ok) {
        throw new Error(`chat request failed with ${response.status}`);
      }

      const serverData = normalizeServerReply(await response.json().catch(() => null));
      if (!serverData) {
        throw new Error('invalid chat response');
      }

      const nextValues = { ...requestValues, ...serverData.extracted };
      const nextSkipped = requestSkipped.filter(
        (key) => !Object.prototype.hasOwnProperty.call(serverData.extracted, key),
      );
      const localNextFieldKey = getNextFieldKey(nextValues, new Set(nextSkipped));
      const allAccountedFor = CHAT_SCHEMA.every(
        (schemaField) => hasValue(nextValues[schemaField.key]) || nextSkipped.includes(schemaField.key),
      );
      const resolvedDone =
        serverData.done || (!localNextFieldKey && allAccountedFor && getConversationFloorState(nextValues));
      const resolvedNextFieldKey = resolvedDone
        ? null
        : (serverData.nextField && FIELD_BY_KEY[serverData.nextField]
          ? serverData.nextField
          : localNextFieldKey || field.key);
      const resolvedNextField = resolvedNextFieldKey ? FIELD_BY_KEY[resolvedNextFieldKey] : null;
      const resolvedChips = serverData.suggestions.length
        ? serverData.suggestions
        : resolvedNextField
          ? getChips(resolvedNextField, nextValues)
          : [];

      outcome = {
        reply: resolvedDone && !serverData.done ? LOCAL_DONE_REPLY : serverData.reply,
        chips: resolvedDone ? [] : resolvedChips,
        values: nextValues,
        skipped: nextSkipped,
        nextFieldKey: resolvedNextFieldKey,
        done: resolvedDone,
      };
    } catch {
      outcome = getLocalTurnOutcome({ field, value: userText, values: valuesSnapshot, skipped: skippedSnapshot });
    } finally {
      postingRef.current = false;
    }

    if (outcome.done) {
      const fieldsCompleted = Object.values(outcome.values).filter(hasValue).length;
      if (window.mwtTrack) {
        window.mwtTrack('contact_form_submit', {
          form_id: 'intake_chat',
          fields_completed: fieldsCompleted,
          total_fields: CHAT_SCHEMA.length,
        });
        window.mwtTrack('generate_lead', {
          lead_source: 'intake_chat',
          project_type: outcome.values.projectType,
          budget: outcome.values.budget,
          timeline: outcome.values.timeline,
        });
      }
    }

    applyOutcome(outcome);
  }

  useEfCt(() => {
    if (chatRef.current) chatRef.current.scrollTop = chatRef.current.scrollHeight;
  }, [messages, typing, sending]);

  return (
    <section id="contact" ref={ref} className="mw-reveal" style={{
      padding:'clamp(64px,10vh,100px) clamp(20px,4vw,32px) 40px',
      maxWidth:1440, margin:'0 auto' }}>
      <SectionHead
        eyebrow="004 / Start a project"
        title={<>Let's build<br/><em style={{fontFamily:'var(--font-editorial)',fontStyle:'italic',fontWeight:400,color:'var(--accent)'}}>something real.</em></>}
        kicker="Tell us what you need — takes about 2 minutes. An Oklahoma City web studio replies with a detailed, fixed-price quote within one business day. No commitment required."
      />

      {!started ? (
        <PreStartPanel onStart={startChat}/>
      ) : (
        /* Active intake */
        <div className="mw-contact-split">
          {/* Chat column */}
          <div style={{ display:'flex', flexDirection:'column', gap:0, border:'1px solid var(--border-strong)', background:'var(--bg-elev-1)' }}>
            {/* Header */}
            <div style={{ padding:'12px 18px', borderBottom:'1px solid var(--border)', background:'var(--bg-elev-2)',
              display:'flex', alignItems:'center', justifyContent:'space-between' }}>
              <div style={{ display:'flex', alignItems:'center', gap:10 }}>
                <span style={{width:8,height:8,borderRadius:'50%',background:'var(--accent)',boxShadow:'0 0 10px var(--accent)',animation:'mwBreathe 2s infinite'}}/>
                <span style={{fontFamily:'var(--font-mono)',fontSize:10,letterSpacing:'0.14em',color:'var(--fg-2)'}}>
                  {done ? 'QUOTE REQUEST RECEIVED' : `STEP ${Math.min(currentFieldIndex + 1, CHAT_SCHEMA.length)} OF ${CHAT_SCHEMA.length}`}
                </span>
              </div>
              <div style={{ display:'flex', gap:3 }}>
                {CHAT_SCHEMA.map((field, i)=>(
                  <div key={field.key} style={{width:hasValue(values[field.key]) || skippedSet.has(field.key) || done ? 18 : i===currentFieldIndex ? 8 : 4,height:3,
                    background:hasValue(values[field.key]) || skippedSet.has(field.key) || done ? 'var(--accent)' : i===currentFieldIndex ? 'var(--fg-2)' : 'var(--border-strong)',
                    transition:'all .4s var(--ease-out)'}}/>
                ))}
              </div>
            </div>

            {/* Messages */}
            <div ref={chatRef} className="mw-chat-msgs" style={{ padding:'20px 18px', display:'flex', flexDirection:'column', gap:12,
              overflowY:'auto' }}>
              {messages.map((m,i) => <Bubble key={i} role={m.role} text={m.text} animate={m.animate}/>) }
              {controlsDisabled && (
                <div style={{display:'flex',justifyContent:'flex-start'}}>
                  <div style={{padding:'10px 14px',background:'var(--bg-elev-2)',border:'1px solid var(--border-strong)',borderRadius:'0 8px 8px 8px'}}>
                    <DotTrace color="var(--fg-3)" size={5}/>
                  </div>
                </div>
              )}
            </div>

            {/* Chips */}
            {!done && chips.length > 0 && !controlsDisabled && (
              <div style={{ padding:'12px 18px', borderTop:'1px solid var(--border)', display:'flex', gap:8, flexWrap:'wrap' }}>
                {chips.map(c => (
                  <button key={c} onClick={() => submitAnswer(c)} style={{
                    padding:'8px 14px', fontFamily:'var(--font-mono)', fontSize:11, letterSpacing:'0.1em',
                    background:'transparent', color:'var(--fg-2)',
                    border:'1px solid var(--border-strong)', cursor:'pointer',
                    transition:'all .2s', textAlign:'left', textTransform:'none' }}
                    onMouseEnter={e=>{e.currentTarget.style.borderColor='var(--accent)';e.currentTarget.style.color='var(--accent)';e.currentTarget.style.background='color-mix(in oklab,var(--accent) 6%,transparent)';}}
                    onMouseLeave={e=>{e.currentTarget.style.borderColor='var(--border-strong)';e.currentTarget.style.color='var(--fg-2)';e.currentTarget.style.background='transparent';}}>
                    {c}
                  </button>
                ))}
              </div>
            )}

            {/* Text input */}
            {showTextInput && !controlsDisabled && (
              <div style={{ padding:'12px 18px', borderTop:'1px solid var(--border)', display:'flex', gap:8 }}>
                {currentField.type === 'textarea' ? (
                  <textarea
                    placeholder={currentField.placeholder}
                    value={inputVal}
                    onChange={e => setInputVal(e.target.value)}
                    onKeyDown={e => {
                      if (e.key === 'Enter' && !e.shiftKey) {
                        e.preventDefault();
                        submitAnswer(inputVal);
                      }
                    }}
                    style={{ flex:1, minHeight:96, padding:'10px 14px', background:'var(--bg)',
                      border:'1px solid var(--border-strong)', color:'var(--fg-1)',
                      fontFamily:'var(--font-body)', fontSize:14, outline:'none', resize:'vertical',
                      transition:'border-color .2s' }}
                    onFocus={e=>e.target.style.borderColor='var(--accent)'}
                    onBlur={e=>e.target.style.borderColor='var(--border-strong)'}
                    autoFocus
                  />
                ) : (
                  <input
                    type={currentField.type || 'text'}
                    placeholder={currentField.placeholder}
                    value={inputVal}
                    onChange={e => setInputVal(e.target.value)}
                    onKeyDown={e => e.key === 'Enter' && submitAnswer(inputVal)}
                    style={{ flex:1, padding:'10px 14px', background:'var(--bg)',
                      border:'1px solid var(--border-strong)', color:'var(--fg-1)',
                      fontFamily:'var(--font-body)', fontSize:14, outline:'none',
                      transition:'border-color .2s' }}
                    onFocus={e=>e.target.style.borderColor='var(--accent)'}
                    onBlur={e=>e.target.style.borderColor='var(--border-strong)'}
                    autoFocus
                  />
                )}
                <button onClick={() => submitAnswer(inputVal)} style={{
                  padding:'10px 18px', background:'var(--accent)', color:'var(--accent-ink)',
                  border:'none', fontFamily:'var(--font-mono)', fontSize:11, letterSpacing:'0.12em',
                  cursor:'pointer', transition:'background .2s' }}
                  onMouseEnter={e=>e.currentTarget.style.background='var(--accent-strong)'}
                  onMouseLeave={e=>e.currentTarget.style.background='var(--accent)'}>
                  SEND →
                </button>
              </div>
            )}

            {done && (
              <div style={{ padding:'18px', borderTop:'1px solid var(--border)', background:'color-mix(in oklab,var(--accent) 6%,transparent)',
                fontFamily:'var(--font-mono)', fontSize:10, color:'var(--accent)', letterSpacing:'0.14em', textAlign:'center' }}>
                ◆ QUOTE REQUEST SENT · WE'LL BE IN TOUCH WITHIN 1 BUSINESS DAY
              </div>
            )}
          </div>

          {/* Form preview */}
          <FormPreview values={values} fields={CHAT_SCHEMA} activeKey={currentField?.key} skipped={skipped}/>
        </div>
      )}
    </section>
  );
}

Object.assign(window, { Contact });
