// Maple Pavers Explorer — QUOTE TOOL ENTRY, 5 variants (teasers; link to the real Maple AI tool).
(function () {
  const { Button, Badge } = window.MaplePaversDesignSystem_8de4d8;
  const { Icon, Overline, PHOTO, ASSET, startQuote } = window.MP;
  const STEPS = ['Address', 'Project', 'Render', 'Quote'];

  function BrandRow() {
    return (
      <div style={{ display: 'flex', alignItems: 'center', gap: 11, marginBottom: 16 }}>
        <span style={{ width: 38, height: 38, borderRadius: 'var(--radius)', background: 'var(--terracotta)', display: 'inline-flex', alignItems: 'center', justifyContent: 'center' }}>
          <img src={ASSET + 'mark-solid.png'} alt="" style={{ width: 22, filter: 'brightness(0) invert(1)' }} />
        </span>
        <div style={{ flex: 1 }}>
          <div style={{ fontFamily: 'var(--font-display)', fontWeight: 600, fontSize: 17, color: 'var(--text-strong)', lineHeight: 1 }}>Maple AI</div>
          <div style={{ fontFamily: 'var(--font-body)', fontSize: 11.5, color: 'var(--text-muted)', marginTop: 3 }}>Instant render &amp; ballpark quote</div>
        </div>
        <Badge variant="soft" size="sm">Free</Badge>
      </div>
    );
  }

  // V1 — Stepper preview card
  function QuoteStepper() {
    return (
      <section style={{ background: 'var(--beige-300)', padding: '44px 22px' }}>
        <div style={{ textAlign: 'center', marginBottom: 22 }}>
          <Overline>The Maple AI Tool</Overline>
          <h2 style={{ fontFamily: 'var(--font-display)', fontWeight: 600, fontSize: 28, lineHeight: 1.12, color: 'var(--text-strong)', margin: '10px 0 0' }}>Four taps to your price.</h2>
        </div>
        <div style={{ background: 'var(--color-surface)', border: '1px solid var(--border-subtle)', borderRadius: 'var(--radius-lg)', boxShadow: 'var(--shadow)', padding: '22px 20px' }}>
          <BrandRow />
          <div style={{ display: 'flex', flexDirection: 'column', gap: 0 }}>
            {STEPS.map((s, i) => (
              <div key={s} style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '9px 0' }}>
                <span style={{ width: 28, height: 28, borderRadius: 'var(--radius-full)', background: i === 0 ? 'var(--terracotta)' : 'var(--beige-300)', color: i === 0 ? '#fff' : 'var(--text-muted)', fontFamily: 'var(--font-body)', fontSize: 12, fontWeight: 700, display: 'flex', alignItems: 'center', justifyContent: 'center', flex: 'none' }}>{i + 1}</span>
                <span style={{ fontFamily: 'var(--font-body)', fontSize: 14, fontWeight: 600, color: 'var(--text-strong)' }}>{s}</span>
                {i < STEPS.length - 1 && <span style={{ marginLeft: 'auto' }}><Icon name="chevron-right" size={16} color="var(--taupe)" /></span>}
              </div>
            ))}
          </div>
          <div style={{ marginTop: 18 }}>
            <Button variant="primary" size="lg" withArrow onClick={startQuote} style={{ width: '100%', justifyContent: 'center' }}>Start — It's Free</Button>
          </div>
        </div>
      </section>
    );
  }

  // V2 — Chat teaser (self-playing animated sequence)
  const CHAT_SCRIPT = [
    { from: 'ai', text: "Hi — I'm Maple AI. Tell me your address and I'll redesign your driveway, free.", think: 1000 },
    { from: 'user', text: '42 Forest Hill Rd, Toronto', think: 750 },
    { from: 'ai', text: 'Love that street. What are we building?', think: 1050 },
    { from: 'user', text: 'New interlock driveway', think: 700 },
    { from: 'ai', text: 'On it — generating your render…', think: 1150, render: true },
  ];

  function ChatBubble({ m }) {
    const ai = m.from === 'ai';
    return (
      <div className="mp-msg-in" style={{ display: 'flex', gap: 9, justifyContent: ai ? 'flex-start' : 'flex-end' }}>
        {ai && (
          <span style={{ width: 28, height: 28, borderRadius: 'var(--radius)', background: 'var(--terracotta)', display: 'flex', alignItems: 'center', justifyContent: 'center', flex: 'none', alignSelf: 'flex-end' }}>
            <img src={ASSET + 'mark-solid.png'} alt="" style={{ width: 16, filter: 'brightness(0) invert(1)' }} />
          </span>
        )}
        <div style={{ maxWidth: '78%' }}>
          <div style={{
            background: ai ? 'rgba(248,240,229,0.09)' : 'var(--clay)',
            color: ai ? 'var(--beige-150)' : '#fff',
            borderRadius: ai ? '4px 15px 15px 15px' : '15px 4px 15px 15px',
            padding: '11px 14px', fontFamily: 'var(--font-body)', fontSize: 13.5, lineHeight: '20px',
          }}>{m.text}</div>
          {m.render && (
            <div className="mp-msg-in" style={{ marginTop: 8, borderRadius: 'var(--radius)', overflow: 'hidden', position: 'relative', height: 96 }}>
              <img src={PHOTO + 'house-render.png'} alt="render" style={{ width: '100%', height: '100%', objectFit: 'cover' }} />
              <span style={{ position: 'absolute', bottom: 7, left: 7 }}><Badge variant="solid" size="sm">Your render</Badge></span>
            </div>
          )}
        </div>
      </div>
    );
  }

  function QuoteChat() {
    const [shown, setShown] = React.useState([]);
    const [typing, setTyping] = React.useState(false);
    const bodyRef = React.useRef(null);

    React.useEffect(() => {
      let cancelled = false;
      const timers = [];
      const wait = (ms) => new Promise((res) => { timers.push(setTimeout(res, ms)); });
      (async function run() {
        while (!cancelled) {
          setShown([]); setTyping(false);
          await wait(700);
          for (let i = 0; i < CHAT_SCRIPT.length; i++) {
            const m = CHAT_SCRIPT[i];
            if (cancelled) return;
            if (m.from === 'ai') { setTyping(true); await wait(m.think); if (cancelled) return; setTyping(false); }
            else { await wait(m.think); }
            if (cancelled) return;
            setShown((s) => [...s, m]);
            await wait(380);
          }
          await wait(3000);
        }
      })();
      return () => { cancelled = true; timers.forEach(clearTimeout); };
    }, []);

    React.useEffect(() => {
      const el = bodyRef.current;
      if (el) el.scrollTop = el.scrollHeight;
    }, [shown, typing]);

    return (
      <section style={{ background: 'var(--graphite)', padding: '44px 22px' }}>
        <div style={{ textAlign: 'center', marginBottom: 20 }}>
          <Overline onDark>Just Ask Maple AI</Overline>
          <h2 style={{ fontFamily: 'var(--font-display)', fontWeight: 600, fontSize: 28, lineHeight: 1.12, color: 'var(--beige-150)', margin: '10px 0 0' }}>Like texting a contractor who never sleeps.</h2>
        </div>
        <div style={{ background: 'var(--graphite-800)', border: '1px solid var(--border-on-dark)', borderRadius: 'var(--radius-lg)', overflow: 'hidden' }}>
          {/* chat header */}
          <div style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '12px 16px', borderBottom: '1px solid rgba(255,255,255,0.08)' }}>
            <span style={{ width: 30, height: 30, borderRadius: 'var(--radius)', background: 'var(--terracotta)', display: 'flex', alignItems: 'center', justifyContent: 'center', flex: 'none' }}>
              <img src={ASSET + 'mark-solid.png'} alt="" style={{ width: 17, filter: 'brightness(0) invert(1)' }} />
            </span>
            <div style={{ flex: 1 }}>
              <div style={{ fontFamily: 'var(--font-display)', fontWeight: 600, fontSize: 15, color: 'var(--beige-150)', lineHeight: 1 }}>Maple AI</div>
              <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginTop: 4 }}>
                <span className="mp-online" />
                <span style={{ fontFamily: 'var(--font-body)', fontSize: 11, color: 'var(--text-on-dark-muted)' }}>Online</span>
              </div>
            </div>
          </div>
          {/* chat body */}          <div ref={bodyRef} style={{ height: 296, overflow: 'hidden', padding: '16px', display: 'flex', flexDirection: 'column', gap: 10 }}>
            <div style={{ flex: 1 }} />
            {shown.map((m, i) => <ChatBubble key={i} m={m} />)}
            {typing && (
              <div className="mp-msg-in" style={{ display: 'flex', gap: 9 }}>
                <span style={{ width: 28, height: 28, borderRadius: 'var(--radius)', background: 'var(--terracotta)', display: 'flex', alignItems: 'center', justifyContent: 'center', flex: 'none', alignSelf: 'flex-end' }}>
                  <img src={ASSET + 'mark-solid.png'} alt="" style={{ width: 16, filter: 'brightness(0) invert(1)' }} />
                </span>
                <div className="mp-typing" style={{ background: 'rgba(248,240,229,0.09)', color: 'var(--text-on-dark-muted)', borderRadius: '4px 15px 15px 15px', padding: '13px 14px' }}>
                  <i /><i /><i />
                </div>
              </div>
            )}
          </div>
          {/* input */}
          <div style={{ padding: '0 14px 14px' }}>
            <button onClick={startQuote} className="mp-press" style={{ width: '100%', display: 'flex', alignItems: 'center', gap: 8, border: '1px solid var(--border-on-dark)', borderRadius: 'var(--radius)', padding: '6px 6px 6px 14px', background: 'rgba(248,240,229,0.05)', cursor: 'pointer' }}>
              <span style={{ flex: 1, textAlign: 'left', fontFamily: 'var(--font-body)', fontSize: 14, color: 'var(--text-on-dark-muted)' }}>Type your address to start…</span>
              <span aria-label="Send" style={{ width: 36, height: 36, borderRadius: 'var(--radius-sm)', background: 'var(--terracotta)', color: '#fff', display: 'flex', alignItems: 'center', justifyContent: 'center', flex: 'none' }}><Icon name="arrow" size={17} /></span>
            </button>
          </div>
        </div>
        <div style={{ marginTop: 16 }}>
          <Button variant="primary" size="lg" withArrow onClick={startQuote} style={{ width: '100%', justifyContent: 'center' }}>Design My Driveway</Button>
        </div>
      </section>
    );
  }

  // V3 — Before/after render teaser (static handle)
  function QuoteRender() {
    return (
      <section style={{ background: 'var(--color-bg)', padding: '44px 22px' }}>
        <div style={{ textAlign: 'center', marginBottom: 20 }}>
          <Overline>See It First</Overline>
          <h2 style={{ fontFamily: 'var(--font-display)', fontWeight: 600, fontSize: 28, lineHeight: 1.12, color: 'var(--text-strong)', margin: '10px 0 0' }}>Your home, re-paved by AI.</h2>
        </div>
        <div style={{ position: 'relative', borderRadius: 'var(--radius-lg)', overflow: 'hidden', boxShadow: 'var(--shadow)', height: 220 }}>
          <img src={PHOTO + 'house-render.png'} alt="render" style={{ width: '100%', height: '100%', objectFit: 'cover' }} />
          <div style={{ position: 'absolute', top: 0, bottom: 0, left: '54%', width: 2, background: 'var(--beige-150)' }} />
          <div style={{ position: 'absolute', top: '50%', left: '54%', transform: 'translate(-50%,-50%)', width: 40, height: 40, borderRadius: '50%', background: 'var(--beige-150)', display: 'flex', alignItems: 'center', justifyContent: 'center', boxShadow: '0 4px 14px rgba(0,0,0,0.35)', color: 'var(--graphite)' }}>
            <Icon name="move-h" size={20} />
          </div>
          <span style={{ position: 'absolute', bottom: 12, left: 12 }}><Badge variant="on-dark" size="sm">Before</Badge></span>
          <span style={{ position: 'absolute', bottom: 12, right: 12 }}><Badge variant="solid" size="sm">After</Badge></span>
        </div>
        <p style={{ fontFamily: 'var(--font-body)', fontSize: 14, lineHeight: '22px', color: 'var(--text-muted)', textAlign: 'center', margin: '16px 0 16px' }}>
          Photorealistic. Free. Yours in 90 seconds — no one calls you unless you ask.
        </p>
        <Button variant="primary" size="lg" withArrow onClick={startQuote} style={{ width: '100%', justifyContent: 'center' }}>Render My Home</Button>
      </section>
    );
  }

  // V4 — Address-first big field
  function QuoteAddress() {
    const [addr, setAddr] = React.useState('');
    return (
      <section style={{ background: 'var(--terracotta)', padding: '46px 22px' }}>
        <h2 style={{ fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: 32, lineHeight: 1.08, color: '#fff', margin: 0, textAlign: 'center' }}>What's your address?</h2>
        <p style={{ fontFamily: 'var(--font-body)', fontSize: 14.5, lineHeight: '22px', color: 'rgba(255,255,255,0.88)', textAlign: 'center', margin: '12px 0 22px' }}>
          That's all we need to start your free render and ballpark quote.
        </p>
        <div style={{ background: 'var(--white)', borderRadius: 'var(--radius-lg)', boxShadow: 'var(--shadow-md)', padding: 8, display: 'flex', alignItems: 'center', gap: 8 }}>
          <span style={{ paddingLeft: 8 }}><Icon name="map-pin" size={20} color="var(--terracotta)" /></span>
          <input value={addr} onChange={(e) => setAddr(e.target.value)} placeholder="Start typing your address…" style={{ flex: 1, border: 0, outline: 'none', fontFamily: 'var(--font-body)', fontSize: 16, background: 'transparent', color: 'var(--text-body)', minWidth: 0 }} />
        </div>
        <div style={{ marginTop: 12 }}>
          <button onClick={startQuote} className="mp-press" style={{ width: '100%', border: 0, cursor: 'pointer', background: 'var(--graphite)', color: 'var(--beige-150)', borderRadius: 'var(--radius)', padding: '15px', fontFamily: 'var(--font-body)', fontWeight: 700, fontSize: 14, letterSpacing: '0.08em', textTransform: 'uppercase', display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 10 }}>
            <Icon name="sparkles" size={17} color="var(--clay)" /> Generate My Render
          </button>
        </div>
        <p style={{ fontFamily: 'var(--font-body)', fontSize: 12, color: 'rgba(255,255,255,0.8)', textAlign: 'center', margin: '14px 0 0' }}>
          No obligation. We'll only use it to send your render.
        </p>
      </section>
    );
  }

  // V5 — Stat banner (dark, punchy)
  function QuoteBanner() {
    const stats = [['90s', 'Render time'], ['$0', 'To try'], ['0', 'Salespeople']];
    return (
      <section style={{ background: 'var(--graphite)', padding: '44px 22px' }}>
        <div style={{ display: 'flex', gap: 0, marginBottom: 24 }}>
          {stats.map(([v, l], i) => (
            <div key={l} style={{ flex: 1, textAlign: 'center', borderLeft: i ? '1px solid rgba(255,255,255,0.12)' : 'none' }}>
              <div style={{ fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: 36, color: 'var(--clay)', lineHeight: 1 }}>{v}</div>
              <div style={{ fontFamily: 'var(--font-body)', fontSize: 10.5, fontWeight: 700, letterSpacing: '0.1em', textTransform: 'uppercase', color: 'var(--text-on-dark-muted)', marginTop: 7 }}>{l}</div>
            </div>
          ))}
        </div>
        <h2 style={{ fontFamily: 'var(--font-display)', fontWeight: 600, fontSize: 27, lineHeight: 1.15, color: 'var(--beige-150)', margin: 0, textAlign: 'center' }}>
          The only paver site that shows you the answer before the call.
        </h2>
        <div style={{ marginTop: 22 }}>
          <Button variant="primary" size="lg" withArrow onClick={startQuote} style={{ width: '100%', justifyContent: 'center' }}>Design My Driveway</Button>
        </div>
      </section>
    );
  }

  // V6 — Phone hero animation (ported from QuoteBox HeroPhone, Maple-skinned)
  // 7-chapter chat sequence: greet → Street View → analysis → render + materials
  // → instant estimate → booking → "meanwhile at the shop" lead notification.
  // Autoplays on intersection, loops with a 3.5s pause between cycles.
  const QPHONE_CSS = `
.mp-qphone-wrap { padding: 26px 18px 48px; background: var(--graphite); }
.mp-qphone-head { text-align: center; max-width: 360px; margin: 0 auto 16px; }
.mp-qphone-head h2 { font-family: var(--font-display); font-weight: 600; font-size: 26px; line-height: 1.12; color: var(--beige-150); margin: 8px 0 6px; }
.mp-qphone-head p { font-family: var(--font-body); font-size: 14px; line-height: 1.55; color: var(--text-on-dark-muted); margin: 0; }
.mp-qphone-stage { display: flex; justify-content: center; padding: 0 4px; }
.mp-qphone .phone {
  width: min(316px, 92%);
  height: 600px;
  background: var(--color-bg);
  border-radius: 44px;
  position: relative;
  box-shadow: 0 0 0 9px #1b1d22, 0 0 0 11px #2b2e36, 0 30px 60px -18px rgba(0,0,0,0.55);
  overflow: hidden; display: flex; flex-direction: column; margin: 0 auto;
}
.mp-qphone .notch { position: absolute; top: 11px; left: 50%; transform: translateX(-50%); width: 96px; height: 25px; background: #0f1115; border-radius: 99px; z-index: 8; }
.mp-qphone .pstatus { height: 42px; display: flex; align-items: flex-end; justify-content: space-between; padding: 0 22px 6px; font-family: var(--font-body); font-size: 12px; font-weight: 700; color: var(--text-strong); z-index: 7; }
.mp-qphone .pstatus .r { display: flex; gap: 5px; align-items: center; }
.mp-qphone .pstatus .r b { width: 16px; height: 9px; border: 1.5px solid currentColor; border-radius: 2px; position: relative; }
.mp-qphone .pstatus .r b::before { content: ''; position: absolute; right: -3px; top: 50%; transform: translateY(-50%); width: 2px; height: 4px; background: currentColor; border-radius: 0 1px 1px 0; }
.mp-qphone .pstatus .r b::after { content: ''; position: absolute; inset: 1px 4px 1px 1px; background: currentColor; border-radius: 1px; }
.mp-qphone .pstatus .r u { width: 13px; height: 9px; background: linear-gradient(45deg, currentColor 0 33%, transparent 33% 66%, currentColor 66%); border-radius: 1px; text-decoration: none; }
.mp-qphone .pstatus .r s { width: 13px; height: 9px; -webkit-mask: radial-gradient(circle at 50% 100%, currentColor 30%, transparent 31%) bottom/100% 100% no-repeat; mask: radial-gradient(circle at 50% 100%, currentColor 30%, transparent 31%) bottom/100% 100% no-repeat; background: currentColor; text-decoration: none; }
.mp-qphone .phead { display: flex; align-items: center; gap: 10px; padding: 8px 15px 11px; border-bottom: 1px solid rgba(0,0,0,0.06); background: var(--color-surface); flex: none; }
.mp-qphone .phead .lg { width: 34px; height: 34px; border-radius: 9px; background: var(--terracotta); display: flex; align-items: center; justify-content: center; flex: none; }
.mp-qphone .phead .lg img { width: 19px; filter: brightness(0) invert(1); }
.mp-qphone .phead .nm { font-family: var(--font-display); font-weight: 600; font-size: 14.5px; color: var(--text-strong); line-height: 1; }
.mp-qphone .phead .st { font-family: var(--font-body); font-size: 11px; color: var(--success); display: flex; align-items: center; gap: 5px; margin-top: 3px; }
.mp-qphone .phead .st .d { width: 7px; height: 7px; border-radius: 50%; background: var(--success); animation: mpqPulse 1.9s infinite; flex: none; }
@keyframes mpqPulse { 0% { box-shadow: 0 0 0 0 rgba(79,107,58,0.55); } 70% { box-shadow: 0 0 0 6px rgba(79,107,58,0); } 100% { box-shadow: 0 0 0 0 rgba(79,107,58,0); } }
.mp-qphone .chat { flex: 1; min-height: 0; overflow-y: auto; padding: 14px 11px 18px; display: flex; flex-direction: column; gap: 10px; scroll-behavior: smooth; background: var(--color-bg); scrollbar-width: none; }
.mp-qphone .chat::-webkit-scrollbar { width: 0; }
.mp-qphone .chat > * { opacity: 0; transform: translateY(10px); transition: opacity .45s ease, transform .45s ease; }
.mp-qphone .chat > *.show { opacity: 1; transform: none; }
.mp-qphone .ai { align-self: flex-start; display: flex; gap: 7px; max-width: 90%; }
.mp-qphone .ai .av { width: 26px; height: 26px; border-radius: 8px; background: var(--terracotta); display: flex; align-items: center; justify-content: center; flex: none; align-self: flex-end; }
.mp-qphone .ai .av img { width: 14px; filter: brightness(0) invert(1); }
.mp-qphone .ai .bx { background: var(--color-surface); border: 1px solid rgba(0,0,0,0.06); border-radius: 4px 14px 14px 14px; padding: 10px 13px; font-family: var(--font-body); font-size: 13px; line-height: 1.45; color: var(--text-strong); box-shadow: 0 1px 2px rgba(0,0,0,0.04); }
.mp-qphone .me { align-self: flex-end; max-width: 82%; }
.mp-qphone .me .bx { background: var(--terracotta); color: #fff; border-radius: 14px 4px 14px 14px; padding: 10px 13px; font-family: var(--font-body); font-size: 13px; line-height: 1.4; }
.mp-qphone .me .bx em { font-style: normal; opacity: 0.85; margin-right: 4px; }
.mp-qphone .typing { display: flex; gap: 4px; padding: 12px 13px; }
.mp-qphone .typing i { width: 6px; height: 6px; border-radius: 50%; background: var(--text-muted); animation: mpqtp 1.2s infinite; }
.mp-qphone .typing i:nth-child(2) { animation-delay: 0.18s; }
.mp-qphone .typing i:nth-child(3) { animation-delay: 0.36s; }
@keyframes mpqtp { 0%,60%,100% { opacity: 0.3; transform: translateY(0); } 30% { opacity: 1; transform: translateY(-3px); } }
.mp-qphone .mediaB { align-self: flex-start; width: 88%; }
.mp-qphone .media { border-radius: 14px; overflow: hidden; position: relative; height: 160px; background: linear-gradient(135deg, #cfd6df, #aeb6c0); display: flex; align-items: center; justify-content: center; transition: background 0.5s; }
.mp-qphone .media .cap { position: absolute; left: 10px; bottom: 9px; color: #fff; font-family: var(--font-body); font-size: 11px; font-weight: 700; background: rgba(0,0,0,0.55); backdrop-filter: blur(4px); padding: 4px 9px; border-radius: 7px; z-index: 2; }
.mp-qphone .media .scrim { position: absolute; inset: 0; background: linear-gradient(to top, rgba(0,0,0,0.55), transparent 50%); z-index: 1; }
.mp-qphone .media img.real { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.mp-qphone .shimmer::after { content: ""; position: absolute; inset: 0; background: linear-gradient(110deg, transparent 30%, rgba(255,255,255,0.4) 50%, transparent 70%); transform: translateX(-100%); animation: mpqsh 1.1s infinite; z-index: 3; }
@keyframes mpqsh { to { transform: translateX(100%); } }
.mp-qphone .genlabel { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 9px; color: #fff; z-index: 2; font-family: var(--font-body); }
.mp-qphone .genlabel .spin { width: 22px; height: 22px; border: 2px solid rgba(255,255,255,0.3); border-top-color: #fff; border-radius: 50%; animation: mpqspin 1s linear infinite; }
@keyframes mpqspin { to { transform: rotate(360deg); } }
.mp-qphone .genlabel span { font-size: 12px; font-weight: 600; }
.mp-qphone .analysis { align-self: flex-start; width: 90%; background: var(--color-surface); border: 1px solid rgba(0,0,0,0.06); border-radius: 14px; padding: 12px 14px; box-shadow: 0 1px 3px rgba(0,0,0,0.04); }
.mp-qphone .analysis .h { font-family: var(--font-body); font-weight: 700; font-size: 10.5px; display: flex; align-items: center; gap: 7px; margin-bottom: 9px; color: var(--text-strong); text-transform: uppercase; letter-spacing: .08em; }
.mp-qphone .analysis .h .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--terracotta); flex: none; }
.mp-qphone .arow { display: flex; align-items: center; gap: 8px; padding: 4px 0; font-family: var(--font-body); font-size: 12.5px; opacity: 0; transform: translateX(-6px); transition: 0.3s; color: var(--text-body); }
.mp-qphone .arow.show { opacity: 1; transform: none; }
.mp-qphone .arow .ck { width: 15px; height: 15px; border-radius: 50%; background: var(--success); color: #fff; display: inline-flex; align-items: center; justify-content: center; flex: none; font-size: 9px; font-weight: 900; line-height: 1; }
.mp-qphone .arow .k { color: var(--text-muted); }
.mp-qphone .arow .v { margin-left: auto; font-weight: 700; color: var(--text-strong); }
.mp-qphone .swrow { align-self: flex-start; width: 92%; display: flex; gap: 6px; flex-wrap: wrap; }
.mp-qphone .sw { display: flex; align-items: center; gap: 7px; border: 1.5px solid rgba(0,0,0,0.12); background: var(--color-surface); border-radius: 99px; padding: 5px 10px 5px 6px; font-family: var(--font-body); font-size: 11.5px; font-weight: 600; color: var(--text-muted); transition: 0.2s; }
.mp-qphone .sw .c { width: 14px; height: 14px; border-radius: 50%; border: 1px solid rgba(0,0,0,0.1); }
.mp-qphone .sw.on { border-color: var(--terracotta); background: var(--terracotta-050); color: var(--terracotta); }
.mp-qphone .quoteC { align-self: flex-start; width: 92%; background: var(--color-surface); border: 1px solid rgba(0,0,0,0.06); border-radius: 15px; overflow: hidden; box-shadow: 0 6px 16px rgba(0,0,0,0.08); }
.mp-qphone .quoteC .top { padding: 13px 15px; border-bottom: 1px solid rgba(0,0,0,0.05); }
.mp-qphone .quoteC .lbl { font-family: var(--font-body); font-size: 10px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-muted); }
.mp-qphone .quoteC .pr { font-family: var(--font-display); font-weight: 600; letter-spacing: -0.01em; color: var(--terracotta); font-size: 24px; margin-top: 4px; line-height: 1.1; }
.mp-qphone .quoteC .pr .s { font-family: var(--font-body); font-size: 11px; color: var(--text-muted); font-weight: 500; display: block; margin-top: 3px; letter-spacing: 0; text-transform: none; }
.mp-qphone .quoteC .incs { padding: 9px 15px 12px; }
.mp-qphone .qinc { display: flex; align-items: center; gap: 9px; padding: 4px 0; font-family: var(--font-body); font-size: 12.5px; color: var(--text-body); }
.mp-qphone .qinc .ic { width: 18px; height: 18px; border-radius: 50%; background: var(--terracotta-050); color: var(--terracotta); display: inline-flex; align-items: center; justify-content: center; font-size: 11px; flex: none; font-weight: 700; }
.mp-qphone .bookC { align-self: flex-start; width: 92%; background: var(--color-surface); border: 1px solid rgba(0,0,0,0.06); border-radius: 15px; padding: 13px 14px; box-shadow: 0 1px 3px rgba(0,0,0,0.04); }
.mp-qphone .bookC .h { font-family: var(--font-body); font-weight: 700; font-size: 12.5px; margin-bottom: 10px; color: var(--text-strong); }
.mp-qphone .slots { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 11px; }
.mp-qphone .slot { border: 1.5px solid rgba(0,0,0,0.12); border-radius: 9px; padding: 6px 10px; font-family: var(--font-body); font-size: 11.5px; font-weight: 600; color: var(--text-strong); transition: 0.2s; }
.mp-qphone .slot.on { border-color: var(--terracotta); background: var(--terracotta); color: #fff; }
.mp-qphone .bookC .cbtn { width: 100%; text-align: center; background: var(--terracotta); color: #fff; border-radius: 10px; padding: 10px; font-family: var(--font-body); font-weight: 700; font-size: 12.5px; letter-spacing: .04em; }
.mp-qphone .confirm { align-self: center; text-align: center; background: var(--success-100); border: 1px solid rgba(79,107,58,0.4); border-radius: 14px; padding: 14px 18px; width: 84%; font-family: var(--font-body); }
.mp-qphone .confirm .ic { width: 30px; height: 30px; border-radius: 50%; background: var(--success); color: #fff; display: flex; align-items: center; justify-content: center; margin: 0 auto 6px; font-size: 16px; font-weight: 900; }
.mp-qphone .confirm b { display: block; font-size: 13px; color: var(--success); font-weight: 700; }
.mp-qphone .confirm span { display: block; font-size: 11.5px; color: var(--text-muted); margin-top: 3px; }
.mp-qphone .meta { align-self: center; font-family: var(--font-body); font-size: 10px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; color: var(--text-muted); margin: 6px 0 0; }
.mp-qphone .notify { align-self: stretch; display: flex; flex-direction: column; gap: 9px; padding: 0 2px; }
.mp-qphone .toast { display: flex; gap: 11px; align-items: flex-start; background: var(--graphite); color: #fff; border-radius: 14px; padding: 12px 14px; }
.mp-qphone .toast .ic { width: 30px; height: 30px; border-radius: 8px; background: rgba(176,79,36,0.25); color: var(--terracotta); display: flex; align-items: center; justify-content: center; flex: none; font-size: 15px; font-weight: 700; }
.mp-qphone .toast .tt { font-family: var(--font-body); font-weight: 700; font-size: 12.5px; }
.mp-qphone .toast .ts { font-family: var(--font-body); font-size: 11.5px; color: rgba(255,255,255,0.7); margin-top: 2px; line-height: 1.4; }
.mp-qphone .leadcard { background: var(--color-surface); border: 1px solid rgba(0,0,0,0.06); border-radius: 14px; padding: 11px 12px; display: flex; align-items: center; gap: 11px; box-shadow: 0 1px 3px rgba(0,0,0,0.04); }
.mp-qphone .leadcard .av { width: 38px; height: 38px; border-radius: 9px; background: var(--beige-300); display: flex; align-items: center; justify-content: center; color: var(--terracotta); flex: none; font-family: var(--font-display); font-weight: 700; font-size: 14px; }
.mp-qphone .leadcard .nm { font-family: var(--font-body); font-weight: 700; font-size: 13px; color: var(--text-strong); line-height: 1.2; }
.mp-qphone .leadcard .mt { font-family: var(--font-body); font-size: 11px; color: var(--text-muted); margin-top: 2px; }
.mp-qphone .leadcard .pr { margin-left: auto; font-family: var(--font-display); font-weight: 700; color: var(--terracotta); font-size: 13px; text-align: right; white-space: nowrap; }
.mp-qphone-cta { max-width: 360px; margin: 22px auto 0; }
`;

  function ensureQphoneStyles() {
    if (document.getElementById('mp-qphone-style')) return;
    const s = document.createElement('style');
    s.id = 'mp-qphone-style';
    s.textContent = QPHONE_CSS;
    document.head.appendChild(s);
  }

  function QuotePhone() {
    const rootRef = React.useRef(null);

    React.useEffect(() => {
      ensureQphoneStyles();
      const root = rootRef.current;
      if (!root) return;

      const MARK = ASSET + 'mark-solid.png';
      // Four matched shots of the same brick home at sunset — Street View
      // baseline + three material treatments. Lets the phone animation show
      // the actual driveway swap as each material is tapped, instead of
      // gradient placeholders.
      const STREETVIEW = PHOTO + 'quote-phone-before.jpg';
      const RENDER_STAMPED = PHOTO + 'quote-phone-stamped.jpg';
      const RENDER_CONCRETE = PHOTO + 'quote-phone-concrete.jpg';
      const RENDER_PAVERS = PHOTO + 'quote-phone-pavers.jpg';

      const T = {
        who: 'Maple Pavers',
        greet: '👋 Hi! I’m Maple Pavers’ quote assistant. Want to see your new driveway before you spend a dollar? Just drop in your address.',
        addr: '88 Forest Hill Rd, Toronto',
        found: 'Found it. Here’s your place from the street.',
        analyze: 'Give me a sec to size it up…',
        rows: [
          ['Current surface', 'Aging asphalt'],
          ['Driveway size', '~820 sq ft'],
          ['Best upgrade', 'Premium pavers'],
        ],
        renderMsg: 'Here’s your home with fresh new pavers ✨',
        materials: [
          { n: 'Stamped Concrete', img: RENDER_STAMPED, c1: '#C3B8AB', c2: '#A89C8C', p: '$6,200 – $7,800' },
          { n: 'Poured Concrete', img: RENDER_CONCRETE, c1: '#cfd3d8', c2: '#9aa0a8', p: '$7,200 – $9,400' },
          { n: 'Charcoal Pavers', img: RENDER_PAVERS, c1: '#4A505A', c2: '#2C313A', p: '$8,450 – $10,250' },
        ],
        switchTo: 2,
        incs: [
          ['◆', 'Premium charcoal pavers'],
          ['≡', 'Base prep + compaction'],
          ['◷', '~2-week install'],
          ['✓', '10-year workmanship warranty'],
        ],
        lead: { nm: 'Sarah Johnson', mt: 'Charcoal Pavers driveway · Toronto, ON', pr: '$8,450–$10,250', initial: 'SJ' },
      };

      let token = 0;
      const chat = root.querySelector('.chat');

      function el(html) {
        const d = document.createElement('div');
        d.innerHTML = html.trim();
        return d.firstChild;
      }
      function scrollDown() { chat.scrollTop = chat.scrollHeight; }
      function show(node, instant) {
        chat.appendChild(node); scrollDown();
        if (instant) node.classList.add('show');
        else requestAnimationFrame(() => requestAnimationFrame(() => { node.classList.add('show'); scrollDown(); }));
        return node;
      }
      function gate(ms, my) {
        return new Promise((res, rej) => {
          let rem = ms;
          const iv = setInterval(() => {
            if (my !== token) { clearInterval(iv); return rej('x'); }
            rem -= 50; if (rem <= 0) { clearInterval(iv); res(); }
          }, 50);
        });
      }
      async function ai(text, ctx) {
        if (!ctx.instant) {
          const t = show(el('<div class="ai"><div class="av"><img src="' + MARK + '" alt=""></div><div class="bx"><div class="typing"><i></i><i></i><i></i></div></div></div>'), false);
          await gate(820, ctx.my); t.remove();
        }
        show(el('<div class="ai"><div class="av"><img src="' + MARK + '" alt=""></div><div class="bx">' + text + '</div></div>'), ctx.instant);
        if (!ctx.instant) await gate(420, ctx.my);
      }
      async function userMsg(text, ctx) {
        show(el('<div class="me"><div class="bx">' + text + '</div></div>'), ctx.instant);
        if (!ctx.instant) await gate(520, ctx.my);
      }
      async function media(opts, ctx) {
        const node = el('<div class="mediaB"><div class="media"></div></div>');
        const m = node.firstChild;
        function paint(loading) {
          if (loading) {
            m.style.background = opts.gen ? 'linear-gradient(135deg,#3a3f47,#23262e)' : 'linear-gradient(135deg,#cfd6df,#aeb6c0)';
            m.innerHTML = opts.gen
              ? '<div class="genlabel"><div class="spin"></div><span>Generating your render…</span></div>'
              : '';
            m.classList.add('shimmer');
          } else {
            m.classList.remove('shimmer');
            if (opts.img) {
              m.style.background = 'none';
              m.innerHTML = '<img src="' + opts.img + '" class="real" alt=""><div class="scrim"></div><div class="cap">' + opts.label + '</div>';
            } else {
              m.style.background = 'linear-gradient(135deg,' + opts.c1 + ',' + opts.c2 + ')';
              m.innerHTML = '<div class="scrim"></div><div class="cap">' + opts.label + '</div>';
            }
          }
        }
        if (ctx.instant) { paint(false); show(node, true); return m; }
        paint(true); show(node, false);
        await gate(opts.gen ? 1700 : 1100, ctx.my);
        paint(false);
        return m;
      }
      async function analysis(rows, ctx) {
        const node = el('<div class="analysis"><div class="h"><span class="dot"></span>Analyzing project…</div></div>');
        rows.forEach((r) => node.appendChild(el('<div class="arow"><span class="ck">✓</span><span class="k">' + r[0] + '</span><span class="v">' + r[1] + '</span></div>')));
        show(node, ctx.instant);
        const arows = node.querySelectorAll('.arow');
        for (let i = 0; i < arows.length; i++) {
          if (ctx.instant) { arows[i].classList.add('show'); continue; }
          await gate(520, ctx.my); arows[i].classList.add('show'); scrollDown();
        }
        if (!ctx.instant) await gate(300, ctx.my);
      }

      const CH = [
        async (ctx) => {
          await ai(T.greet, ctx);
          await userMsg('<em>\u{1F4CD}</em>' + T.addr, ctx);
        },
        async (ctx) => {
          await ai(T.found, ctx);
          await media({ gen: false, img: STREETVIEW, label: 'Your home · Street View' }, ctx);
        },
        async (ctx) => {
          await ai(T.analyze, ctx);
          await analysis(T.rows, ctx);
        },
        async (ctx) => {
          await ai(T.renderMsg, ctx);
          const m0 = T.materials[0];
          const mediaEl = await media({ gen: true, img: m0.img, c1: m0.c1, c2: m0.c2, label: m0.n + ' · ' + m0.p }, ctx);
          const sw = el('<div class="swrow"></div>');
          T.materials.forEach((mt, i) => {
            sw.appendChild(el('<div class="sw' + (i === 0 ? ' on' : '') + '"><span class="c" style="background:linear-gradient(135deg,' + mt.c1 + ',' + mt.c2 + ')"></span>' + mt.n + '</div>'));
          });
          show(sw, ctx.instant);
          const chips = sw.querySelectorAll('.sw');
          const to = T.switchTo, mt = T.materials[to];
          function applySwitch(instant) {
            chips.forEach((c, i) => c.classList.toggle('on', i === to));
            mediaEl.classList.add('shimmer');
            const cap = mediaEl.querySelector('.cap'); if (cap) cap.textContent = mt.n + ' · ' + mt.p;
            if (mt.img) {
              mediaEl.style.background = 'none';
              const existing = mediaEl.querySelector('img.real');
              if (existing) { existing.src = mt.img; }
              else {
                const i2 = document.createElement('img');
                i2.className = 'real'; i2.src = mt.img; i2.alt = '';
                mediaEl.insertBefore(i2, mediaEl.firstChild);
              }
            } else {
              const existing = mediaEl.querySelector('img.real'); if (existing) existing.remove();
              mediaEl.style.background = 'linear-gradient(135deg,' + mt.c1 + ',' + mt.c2 + ')';
            }
            if (instant) mediaEl.classList.remove('shimmer');
          }
          if (ctx.instant) { applySwitch(true); return; }
          await gate(1200, ctx.my);
          await ai('Try a few looks — tap any material.', ctx);
          await gate(500, ctx.my);
          applySwitch(false);
          await gate(900, ctx.my); mediaEl.classList.remove('shimmer');
        },
        async (ctx) => {
          const mat = T.materials[T.switchTo];
          // Stop fighting the scroll. Clear the chat instead — the estimate
          // is the answer, not just another message in a queue. With only
          // an AI bubble + the lockup in the chat, the card is the bottom
          // element AND the only thing competing for viewport space.
          chat.innerHTML = '';
          await ai('Here is your instant estimate:', ctx);
          // Lockup B — photo on top, dark panel below.
          const card = document.createElement('div');
          card.className = 'show';
          card.setAttribute('style', [
            'align-self:flex-start',
            'width:92%',
            'background:#1a1a1a',
            'border-radius:14px',
            'overflow:hidden',
            'box-shadow:0 6px 18px rgba(0,0,0,0.25)',
            'opacity:1',
            'transform:none',
            'box-sizing:border-box',
          ].join(';'));
          card.innerHTML =
            '<div style="height:170px;background-image:url(' + PHOTO + 'quote-phone-pavers.jpg);background-size:cover;background-position:center;background-color:#0d0d0d"></div>' +
            '<div style="padding:16px 16px 18px">' +
              '<div style="font-family:Inter,system-ui,sans-serif;font-size:10px;font-weight:700;letter-spacing:0.12em;text-transform:uppercase;color:#c47a5a">Instant estimate</div>' +
              '<div style="font-family:Cormorant Garamond,Georgia,serif;font-weight:600;font-size:28px;line-height:1.05;color:#f8f0e5;margin-top:4px">' + mat.p + '</div>' +
              '<div style="font-family:Inter,system-ui,sans-serif;font-size:11.5px;color:rgba(248,240,229,0.78);margin-top:4px">' + mat.n + ' &middot; all-in</div>' +
              '<div style="font-family:Inter,system-ui,sans-serif;font-size:9.5px;color:rgba(248,240,229,0.55);margin-top:10px;padding-top:10px;border-top:1px solid rgba(255,255,255,0.12);line-height:1.4;letter-spacing:0.02em">~820 sq ft &middot; excludes permits &middot; final scope confirmed on a free site visit</div>' +
            '</div>';
          chat.appendChild(card);
          chat.scrollTop = chat.scrollHeight;
          if (!ctx.instant) await gate(2200, ctx.my);
        },
      ];

      // Play through all chapters, hold on the estimate for a long beat, then
      // restart. The estimate-chapter already wipes the chat itself, so the
      // visitor sees the lockup linger before the greeting kicks off again.
      async function playLoop() {
        token++; const my = token;
        chat.innerHTML = '';
        try {
          for (let j = 0; j < CH.length; j++) {
            if (my !== token) return;
            await CH[j]({ instant: false, my });
            if (my !== token) return;
            if (j < CH.length - 1) await gate(1300, my);
          }
          // Hold on the estimate frame before restarting.
          await gate(5000, my);
          if (my !== token) return;
          playLoop();
        } catch { /* cancelled */ }
      }

      let started = false;
      const io = new IntersectionObserver(
        (entries) => {
          if (entries[0].isIntersecting && !started) {
            started = true;
            playLoop();
          }
        },
        { threshold: 0.25 }
      );
      io.observe(root);
      return () => { token++; io.disconnect(); };
    }, []);

    return (
      <section className="mp-qphone-wrap">
        <div className="mp-qphone-head">
          <Overline onDark>Your Free Driveway Preview</Overline>
          <h2>Real renders. Real prices. Zero pressure.</h2>
        </div>
        <div className="mp-qphone-stage mp-qphone" ref={rootRef}>
          <div className="phone">
            <div className="notch" />
            <div className="pstatus">
              <span>9:41</span>
              <span className="r"><u /><s /><b /></span>
            </div>
            <div className="phead">
              <div className="lg">
                <img src={ASSET + 'mark-solid.png'} alt="" />
              </div>
              <div style={{ flex: 1 }}>
                <div className="nm">Maple Pavers</div>
                <div className="st"><span className="d" />Quote assistant · Online</div>
              </div>
            </div>
            <div className="chat" />
          </div>
        </div>
        <div className="mp-qphone-cta">
          <Button variant="primary" size="lg" withArrow onClick={startQuote} style={{ width: '100%', justifyContent: 'center' }}>Start My Driveway Design</Button>
        </div>
      </section>
    );
  }

  window.MP.register('quote', {
    label: 'Quote tool',
    options: [
      { id: 'phone', name: '6 · Phone animation', Render: QuotePhone },
      { id: 'stepper', name: '1 · Stepper preview', Render: QuoteStepper },
      { id: 'chat', name: '2 · Chat teaser', Render: QuoteChat },
      { id: 'render', name: '3 · Before/after', Render: QuoteRender },
      { id: 'address', name: '4 · Address-first', Render: QuoteAddress },
      { id: 'banner', name: '5 · Stat banner', Render: QuoteBanner },
    ],
  });
})();
