// Olipop-themed preview components rendered inside tiles + fullscreen focus.
// All inline-SVG/HTML — no external assets.

// ============== HTMLPreview — renders a real HTML file in an iframe ==============
// `mode="thumb"` scales the full page down to fit a tile (non-interactive snapshot).
// `mode="full"` shows the page at real width inside browser chrome, scrollable.
function HTMLPreview({ src, mode = 'thumb', designWidth = 1280, designHeight = 2400, url = 'olipop.com/quiz', chrome = 'browser', frame = 'desktop' }) {
  const wrapRef = React.useRef(null);
  const iframeRef = React.useRef(null);
  const [scale, setScale] = React.useState(mode === 'thumb' ? 0.3 : 1);
  // When the iframe loads, probe its real content height and use that as the
  // scaling basis so short generated pages (e.g. a quiz with only 800px of
  // actual content) don't show huge empty space below.
  const [measuredH, setMeasuredH] = React.useState(null);
  const effectiveH = measuredH || designHeight;

  React.useLayoutEffect(() => {
    if (mode !== 'thumb') { setScale(1); return; }
    const el = wrapRef.current;
    if (!el) return;
    const compute = () => {
      const rect = el.getBoundingClientRect();
      const chromeH = chrome === 'browser' ? 34 : chrome === 'phone' ? 28 : 0;
      const sW = rect.width / designWidth;
      const sH = (rect.height - chromeH) / effectiveH;
      setScale(Math.max(0.05, Math.min(sW, sH)));
    };
    compute();
    const ro = new ResizeObserver(compute);
    ro.observe(el);
    return () => ro.disconnect();
  }, [mode, designWidth, effectiveH, chrome]);

  // Reset any prior measurement when the src changes (e.g. regenerate).
  React.useEffect(() => { setMeasuredH(null); }, [src]);

  const handleLoad = React.useCallback(() => {
    try {
      const ifr = iframeRef.current;
      if (!ifr || !ifr.contentDocument) return;
      const b = ifr.contentDocument.body;
      const h = ifr.contentDocument.documentElement;
      const real = Math.max(
        b ? b.scrollHeight : 0,
        h ? h.scrollHeight : 0,
      );
      if (real && real >= 400) {
        // Clamp to a sane window: never shorter than 800 (gives a bit of context),
        // never taller than designHeight (so we don't scale up).
        setMeasuredH(Math.min(Math.max(real + 40, 800), designHeight));
      }
    } catch (e) {
      // Cross-origin iframe — leave measuredH null and fall back to designHeight.
    }
  }, [designHeight]);

  const BrowserChrome = () => (
    <div style={{height:34,background:'linear-gradient(180deg,#f0e9da,#e7dec9)',borderBottom:'1px solid rgba(74,25,66,.12)',display:'flex',alignItems:'center',padding:'0 14px',gap:10,flexShrink:0}}>
      <div style={{display:'flex',gap:6}}>
        <div style={{width:10,height:10,borderRadius:50,background:'#E85C2B'}}/>
        <div style={{width:10,height:10,borderRadius:50,background:'#F5B301'}}/>
        <div style={{width:10,height:10,borderRadius:50,background:'#5AE6D2'}}/>
      </div>
      <div style={{flex:1,background:'rgba(255,255,255,.7)',height:20,borderRadius:6,display:'flex',alignItems:'center',padding:'0 10px',fontSize:11,color:'#4A1942',fontFamily:'Inter,sans-serif',letterSpacing:0}}>
        <span style={{opacity:.5,marginRight:4}}>🔒</span>{url}
      </div>
      <div style={{fontSize:11,color:'rgba(74,25,66,.45)'}}>⋯</div>
    </div>
  );

  if (mode === 'thumb') {
    return (
      <div ref={wrapRef} style={{position:'absolute',inset:0,background:'#ffffff',borderRadius:'inherit',overflow:'hidden',display:'flex',flexDirection:'column'}}>
        {chrome === 'browser' && <BrowserChrome/>}
        <div style={{flex:1,position:'relative',overflow:'hidden'}}>
          <div style={{position:'absolute',top:0,left:'50%',transform:`translateX(-50%) scale(${scale})`,transformOrigin:'top center',width:designWidth,height:effectiveH,pointerEvents:'none'}}>
            <iframe ref={iframeRef} src={src} title={url} onLoad={handleLoad}
                    style={{width:designWidth,height:effectiveH,border:0,background:'#ffffff',display:'block',pointerEvents:'none'}}
                    loading="lazy" scrolling="no"/>
          </div>
        </div>
      </div>
    );
  }

  // FULL mode — fills its container; ONE scrollbar (the iframe's own).
  // Parent must give us a defined height (flex:1 in a flex column, or fixed h).
  return (
    <div style={{width:'100%',height:'100%',background:'#1a1b22',borderRadius:12,overflow:'hidden',boxShadow:'0 30px 80px rgba(0,0,0,.6), 0 0 0 1px rgba(255,255,255,.06)',display:'flex',flexDirection:'column'}}>
      {chrome === 'browser' && <BrowserChrome/>}
      <iframe src={src} title={url} style={{width:'100%',flex:1,border:0,background:'#F4E9D8',display:'block'}}/>
    </div>
  );
}

// ============== META AD (phone/instagram) ==============
// 10 distinct variants across 2 batches:
//   Batch A (cold):  a1 callout · a2 advertorial · a3 native · a4 ugc · a5 science
//   Batch B (warm):  b1 price · b2 urgency · b3 comparison · b4 flavor · b5 social-proof
function AdPhone({ variant = 'a1', scale = 1 }) {
  const palette = {
    coral:'#E85C2B', plum:'#4A1942', cream:'#F4E9D8', berry:'#9B2D5E', vintage:'#D97046', paper:'#FFE4D1',
  };
  const canWall = (size=34, count=3) => (
    <div style={{ display:'flex', gap: size*.12 }}>
      {[0,1,2,3].slice(0,count).map(i => (
        <div key={i} style={{width:size, height:size*2.3, borderRadius:'4px 4px 10px 10px', background:
          i===0 ? 'linear-gradient(180deg,#E85C2B,#9B2D5E)' :
          i===1 ? 'linear-gradient(180deg,#C83B5E,#7A1A36)' :
          i===2 ? 'linear-gradient(180deg,#D97046,#6B2818)' : 'linear-gradient(180deg,#6B3D8A,#4A1942)',
          boxShadow:'inset 0 2px 0 rgba(255,255,255,.3), 0 8px 16px rgba(0,0,0,.2)', position:'relative'}}>
          <div style={{position:'absolute',top:'30%',left:2,right:2,height:size*.53,background:'rgba(255,255,255,.92)',borderRadius:2,display:'flex',alignItems:'center',justifyContent:'center',fontFamily:'Instrument Serif,serif',fontSize:size*.18,color:'#E85C2B'}}>OLIPOP</div>
        </div>
      ))}
    </div>
  );
  const Footer = ({ brand='olipop.com', cta='SHOP NOW ›', color='#4A1942' }) => (
    <div style={{ position:'absolute', bottom:14, left:14, right:14, display:'flex', justifyContent:'space-between', fontSize:9, color, fontWeight:600, letterSpacing:.2 }}>
      <span>{brand}</span><span>{cta}</span>
    </div>
  );

  const Body = () => {
    // ---------------- BATCH A ----------------
    if (variant === 'a1') {
      // Problem-aware callout
      return (
        <div style={{ flex:1, background:'linear-gradient(160deg,#FFE4D1 0%,#F4E9D8 60%,#E8CDAB 100%)', position:'relative', overflow:'hidden' }}>
          <div style={{ position:'absolute', top:16, left:14, right:14, fontFamily:'Instrument Serif,serif', fontSize:22, lineHeight:1.05, color:palette.plum, letterSpacing:'-.02em' }}>
            The <i style={{color:palette.coral}}>only</i> soda my<br/>gut thanks me for.
          </div>
          <div style={{ position:'absolute', bottom:54, left:'50%', transform:'translateX(-50%)' }}>{canWall(34,3)}</div>
          <Footer/>
        </div>
      );
    }
    if (variant === 'a2') {
      // Advertorial hook
      return (
        <div style={{ flex:1, background:'#fff', padding:'10px 12px', overflow:'hidden' }}>
          <div style={{ fontFamily:'Georgia,serif', fontSize:8, color:'#888', letterSpacing:'.1em', textTransform:'uppercase'}}>BY LEA WHITNEY · 5 MIN READ</div>
          <div style={{ fontFamily:'Instrument Serif,serif', fontSize:17, lineHeight:1.08, color:'#1a1a1a', margin:'6px 0 8px', letterSpacing:'-.015em'}}>A gut doctor weighs in on the "soda that isn't soda"</div>
          <div style={{ height:60, borderRadius:4, background:'linear-gradient(160deg,#FFE4D1,#E85C2B 60%,#9B2D5E)', marginBottom:8, position:'relative', overflow:'hidden'}}>
            <div style={{position:'absolute',inset:0,background:'radial-gradient(circle at 70% 50%, rgba(255,255,255,.2), transparent)'}}/>
          </div>
          <div style={{fontSize:8.5, color:'#333', lineHeight:1.5}}>After 14 years in gastroenterology, Dr. Mary Pardee tested 41 "functional sodas." Only one made her short list…</div>
          <div style={{marginTop:8,fontSize:8,color:'#0066cc'}}>Continue reading →</div>
        </div>
      );
    }
    if (variant === 'a3') {
      // Dark native/meme energy
      return (
        <div style={{ flex:1, background:'#0B1115', padding:12, color:'#fff', display:'flex', flexDirection:'column', gap:6 }}>
          <div style={{fontFamily:'Instrument Serif,serif',fontSize:20,lineHeight:1,color:palette.cream,letterSpacing:'-.02em'}}>Bloated after lunch?</div>
          <div style={{fontSize:9,color:'rgba(255,255,255,.6)',lineHeight:1.4}}>You might want to read this before your next Diet Coke.</div>
          <div style={{flex:1,marginTop:6,background:'linear-gradient(180deg,#E85C2B,#4A1942)',borderRadius:4,position:'relative',overflow:'hidden',display:'flex',alignItems:'center',justifyContent:'center'}}>
            <div style={{fontFamily:'Instrument Serif,serif',fontSize:14,color:palette.cream,textAlign:'center',lineHeight:1.1,padding:'0 10px'}}><i>9g of fiber.<br/>Zero regrets.</i></div>
          </div>
          <div style={{fontSize:8,color:palette.coral,fontWeight:600,letterSpacing:'.1em'}}>OLIPOP.COM · LEARN MORE</div>
        </div>
      );
    }
    if (variant === 'a4') {
      // UGC testimonial · portrait-style with quote
      return (
        <div style={{ flex:1, background:'linear-gradient(180deg,#3a2230,#1a0f18)', position:'relative', overflow:'hidden', color:'#fff' }}>
          {/* Portrait placeholder */}
          <div style={{position:'absolute',inset:0,background:'radial-gradient(ellipse at 50% 30%, rgba(232,92,43,.35), transparent 60%), radial-gradient(ellipse at 20% 90%, rgba(155,45,94,.45), transparent 50%)'}}/>
          <div style={{position:'absolute',top:'28%',left:'50%',transform:'translate(-50%,-50%)',width:80,height:80,borderRadius:'50%',background:'linear-gradient(135deg,#E85C2B 0%,#C83B5E 50%,#4A1942 100%)',border:'3px solid rgba(255,255,255,.15)'}}/>
          <div style={{position:'absolute',top:98,left:14,right:14,fontSize:10,letterSpacing:'.2em',textTransform:'uppercase',color:palette.coral,fontWeight:700}}>VERIFIED · LAUREN M.</div>
          <div style={{position:'absolute',top:118,left:14,right:14,fontFamily:'Instrument Serif,serif',fontSize:17,lineHeight:1.1,color:palette.cream,letterSpacing:'-.015em',fontStyle:'italic'}}>"2 Diet Cokes a day. Gone. The crash? Gone too."</div>
          <div style={{position:'absolute',bottom:42,left:14,right:14,display:'flex',gap:3,color:palette.coral,fontSize:12}}>★★★★★<span style={{color:'rgba(255,255,255,.5)',fontSize:8,marginLeft:4,fontWeight:400,letterSpacing:'.06em'}}>5/5 · AUSTIN,&nbsp;TX</span></div>
          <Footer brand="olipop.com" cta="READ HER STORY ›" color={palette.cream}/>
        </div>
      );
    }
    if (variant === 'a5') {
      // Science/stat · the math
      return (
        <div style={{ flex:1, background:palette.cream, position:'relative', overflow:'hidden', padding:'14px 12px', display:'flex', flexDirection:'column', gap:8 }}>
          <div style={{fontSize:9,letterSpacing:'.22em',textTransform:'uppercase',color:palette.coral,fontWeight:700}}>THE GUT MATH</div>
          <div style={{fontFamily:'Instrument Serif,serif',fontSize:18,lineHeight:1.02,color:palette.plum,letterSpacing:'-.015em'}}>The numbers <i style={{color:palette.coral}}>compound</i>.</div>
          <div style={{display:'flex',flexDirection:'column',gap:5,marginTop:4}}>
            {[['9g','prebiotic fiber · more than a banana'],['2–5g','sugar · 80% less than Coke'],['45','calories · max'],['0','artificial anything']].map(([n,t],i)=>(
              <div key={i} style={{display:'grid',gridTemplateColumns:'40px 1fr',gap:6,alignItems:'baseline',borderBottom:'1px solid rgba(74,25,66,.08)',padding:'4px 0'}}>
                <div style={{fontFamily:'Instrument Serif,serif',fontSize:18,color:palette.coral,lineHeight:.9,letterSpacing:'-.02em'}}>{n}</div>
                <div style={{fontSize:8,color:palette.plum,lineHeight:1.3}}>{t}</div>
              </div>
            ))}
          </div>
          <div style={{marginTop:'auto',display:'flex',alignItems:'center',gap:6}}>{canWall(22,2)}<div style={{fontFamily:'Instrument Serif,serif',fontSize:11,color:palette.plum,lineHeight:1.1}}>one can.<br/>one decision.</div></div>
          <Footer/>
        </div>
      );
    }

    // ---------------- BATCH B ----------------
    if (variant === 'b1') {
      // Price anchor $19
      return (
        <div style={{ flex:1, background:`linear-gradient(160deg,${palette.coral} 0%,${palette.berry} 65%,${palette.plum} 100%)`, position:'relative', overflow:'hidden', color:palette.cream }}>
          <div style={{position:'absolute',top:14,left:14,fontSize:9,letterSpacing:'.22em',textTransform:'uppercase',fontWeight:700,opacity:.85}}>NEW · STARTER PACK</div>
          <div style={{position:'absolute',top:40,left:14,right:14,fontFamily:'Instrument Serif,serif',fontSize:24,lineHeight:.96,letterSpacing:'-.025em'}}>Your first <i style={{color:palette.cream,textDecoration:'line-through',opacity:.5,fontSize:18}}>$29</i> <span style={{color:palette.cream,fontSize:46,lineHeight:.8}}>$19</span></div>
          <div style={{position:'absolute',top:112,left:14,right:14,fontFamily:'Instrument Serif,serif',fontSize:15,lineHeight:1.1,letterSpacing:'-.01em'}}>8 cans. 4 flavors. <i>One decision.</i></div>
          <div style={{position:'absolute',bottom:48,left:'50%',transform:'translateX(-50%) rotate(-6deg)'}}>{canWall(30,4)}</div>
          <div style={{position:'absolute',top:14,right:14,background:palette.cream,color:palette.coral,fontSize:8,padding:'4px 8px',borderRadius:100,fontWeight:700,letterSpacing:'.16em'}}>SAVE 35%</div>
          <Footer brand="olipop.com" cta="CLAIM MY $19 PACK ›" color={palette.cream}/>
        </div>
      );
    }
    if (variant === 'b2') {
      // Urgency/countdown
      return (
        <div style={{ flex:1, background:palette.plum, position:'relative', overflow:'hidden', color:palette.cream, padding:'14px 12px', display:'flex', flexDirection:'column', gap:8 }}>
          <div style={{fontSize:9,letterSpacing:'.22em',textTransform:'uppercase',fontWeight:700,color:palette.coral,display:'flex',alignItems:'center',gap:6}}><span style={{display:'inline-block',width:6,height:6,borderRadius:50,background:palette.coral,boxShadow:`0 0 8px ${palette.coral}`}}/>FLASH · 4 HRS LEFT</div>
          <div style={{fontFamily:'Instrument Serif,serif',fontSize:22,lineHeight:1,color:palette.cream,letterSpacing:'-.02em'}}>35% off <i style={{color:palette.coral}}>ends soon</i>.</div>
          <div style={{background:'rgba(232,92,43,.18)',border:`1px solid ${palette.coral}`,borderRadius:6,padding:'8px 10px',display:'flex',gap:8,fontFamily:'JetBrains Mono,monospace',fontSize:20,color:palette.coral,letterSpacing:'.08em',justifyContent:'center'}}>04<span style={{color:'rgba(232,92,43,.5)'}}>:</span>12<span style={{color:'rgba(232,92,43,.5)'}}>:</span>47</div>
          <div style={{display:'grid',gridTemplateColumns:'1fr 1fr 1fr',gap:4,fontSize:7,color:'rgba(244,233,216,.6)',textAlign:'center',letterSpacing:'.16em',textTransform:'uppercase'}}><span>HRS</span><span>MIN</span><span>SEC</span></div>
          <div style={{flex:1,display:'flex',alignItems:'center',justifyContent:'center'}}>{canWall(28,4)}</div>
          <div style={{background:palette.coral,color:palette.cream,padding:'8px',borderRadius:6,textAlign:'center',fontSize:11,fontWeight:600}}>Claim 35% off →</div>
        </div>
      );
    }
    if (variant === 'b3') {
      // Comparison — Coke vs Poppi vs Olipop
      return (
        <div style={{ flex:1, background:'#fff', position:'relative', overflow:'hidden', padding:'12px 12px', color:'#1a1a1a', display:'flex', flexDirection:'column', gap:6 }}>
          <div style={{fontSize:9,letterSpacing:'.22em',textTransform:'uppercase',color:palette.coral,fontWeight:700}}>THE LINE-UP · PER CAN</div>
          <div style={{fontFamily:'Instrument Serif,serif',fontSize:16,lineHeight:1.02,color:palette.plum,letterSpacing:'-.015em'}}>The only one that <i style={{color:palette.coral}}>earns</i> its shelf.</div>
          <div style={{display:'grid',gridTemplateColumns:'1fr 42px 42px 42px',gap:2,marginTop:4,fontSize:7,color:'#666',letterSpacing:'.08em',textTransform:'uppercase',alignItems:'end'}}>
            <span/><span style={{textAlign:'center'}}>Coke</span><span style={{textAlign:'center'}}>Poppi</span><span style={{textAlign:'center',background:palette.coral,color:'#fff',padding:'2px',borderRadius:3,fontFamily:'Instrument Serif,serif',fontSize:9,letterSpacing:0,textTransform:'none'}}>Olipop</span>
          </div>
          {[['Fiber','0g','2g','9g'],['Sugar','39g','5g','2–5g'],['Artificial','—','✓','None'],['Clinical','—','—','✓']].map(([k,a,b,c],i)=>(
            <div key={i} style={{display:'grid',gridTemplateColumns:'1fr 42px 42px 42px',gap:2,padding:'4px 0',borderBottom:'1px solid rgba(0,0,0,.06)',fontSize:9,alignItems:'center'}}>
              <span style={{color:palette.plum,fontWeight:500}}>{k}</span>
              <span style={{textAlign:'center',color:'#c0425a'}}>{a}</span>
              <span style={{textAlign:'center',color:'#666'}}>{b}</span>
              <span style={{textAlign:'center',color:palette.coral,fontWeight:700,background:'rgba(232,92,43,.08)',padding:'2px 0',borderRadius:3}}>{c}</span>
            </div>
          ))}
          <div style={{marginTop:'auto',display:'flex',alignItems:'center',gap:4}}>{canWall(22,1)}<div style={{fontSize:8,color:palette.plum,fontWeight:600}}>The math doesn't lie.</div></div>
          <Footer/>
        </div>
      );
    }
    if (variant === 'b4') {
      // Flavor-forward lifestyle
      return (
        <div style={{ flex:1, background:`linear-gradient(155deg,#FFE4D1 0%,#E85C2B 55%,#C83B5E 100%)`, position:'relative', overflow:'hidden' }}>
          <div style={{position:'absolute',top:-20,left:-20,width:120,height:120,borderRadius:'50%',background:'radial-gradient(circle,rgba(255,255,255,.35),transparent 70%)'}}/>
          <div style={{position:'absolute',top:14,left:14,right:14,fontSize:9,letterSpacing:'.22em',textTransform:'uppercase',color:palette.plum,fontWeight:700}}>SUMMER ’26 · NEW BATCH</div>
          <div style={{position:'absolute',top:36,left:14,right:14,fontFamily:'Instrument Serif,serif',fontSize:32,lineHeight:.95,letterSpacing:'-.03em',color:palette.cream,textShadow:'0 2px 8px rgba(74,25,66,.3)'}}>Lime <i style={{color:palette.plum}}>Basil.</i></div>
          <div style={{position:'absolute',top:92,left:14,right:14,fontFamily:'Instrument Serif,serif',fontSize:11,fontStyle:'italic',lineHeight:1.2,color:palette.cream,opacity:.95,letterSpacing:'-.005em'}}>Summer in a can. Tastes like 4pm on a porch.</div>
          <div style={{position:'absolute',bottom:42,left:'50%',transform:'translateX(-50%)'}}>
            <div style={{width:58,height:130,borderRadius:'6px 6px 12px 12px',background:'linear-gradient(180deg,#5AB36E,#2A7A2A)',boxShadow:'inset 0 3px 0 rgba(255,255,255,.35), 0 20px 30px rgba(0,0,0,.25)',position:'relative'}}>
              <div style={{position:'absolute',top:'34%',left:4,right:4,background:'rgba(255,255,255,.95)',padding:'3px 2px',textAlign:'center',fontFamily:'Instrument Serif,serif',fontSize:8,color:'#2A7A2A',borderRadius:2}}>OLIPOP</div>
              <div style={{position:'absolute',top:'52%',left:4,right:4,fontSize:5,color:'#2A7A2A',textAlign:'center',letterSpacing:'.1em',fontWeight:600}}>LIME BASIL</div>
            </div>
          </div>
          <Footer brand="olipop.com" cta="TRY LIME BASIL ›" color={palette.plum}/>
        </div>
      );
    }
    if (variant === 'b5') {
      // Social proof · review-wall
      return (
        <div style={{ flex:1, background:palette.cream, position:'relative', overflow:'hidden', padding:'14px 12px', display:'flex', flexDirection:'column', gap:7 }}>
          <div style={{display:'flex',alignItems:'baseline',gap:6}}>
            <div style={{fontFamily:'Instrument Serif,serif',fontSize:28,lineHeight:.85,color:palette.coral,letterSpacing:'-.03em'}}>14,482</div>
            <div style={{fontSize:8,color:palette.plum,letterSpacing:'.14em',textTransform:'uppercase',fontWeight:600}}>five-star reviews</div>
          </div>
          <div style={{fontFamily:'Instrument Serif,serif',fontSize:14,lineHeight:1.02,color:palette.plum,letterSpacing:'-.015em'}}>They can't <i style={{color:palette.coral}}>all</i> be wrong.</div>
          <div style={{display:'flex',flexDirection:'column',gap:4,marginTop:2}}>
            {[
              ['L','★★★★★','"Diet Coke crash — gone."'],
              ['J','★★★★★','"Strawberry Vanilla is criminal."'],
              ['S','★★★★★','"Bloat fixed in a week."'],
            ].map(([i,s,q],k)=>(
              <div key={k} style={{display:'grid',gridTemplateColumns:'16px 1fr',gap:6,padding:'4px 6px',background:'rgba(255,255,255,.6)',borderRadius:4,alignItems:'center'}}>
                <div style={{width:16,height:16,borderRadius:50,background:`linear-gradient(135deg,${palette.coral},${palette.berry})`,color:'#fff',fontSize:8,display:'flex',alignItems:'center',justifyContent:'center',fontFamily:'Instrument Serif,serif'}}>{i}</div>
                <div>
                  <div style={{fontSize:6,color:palette.coral,letterSpacing:'-.05em'}}>{s}</div>
                  <div style={{fontSize:8.5,color:palette.plum,lineHeight:1.2,fontFamily:'Instrument Serif,serif',letterSpacing:'-.005em'}}>{q}</div>
                </div>
              </div>
            ))}
          </div>
          <Footer/>
        </div>
      );
    }

    // Fallback to a1
    return null;
  };
  return (
    <div style={{ display:'flex', alignItems:'center', justifyContent:'center', width:'100%', height:'100%' }}>
      <div className="phone-frame" style={{transform:`scale(${scale})`}}>
        <div className="phone-notch"/>
        <div className="phone-screen">
          <div className="ig-bar">
            <div className="ig-avatar"/>
            <div className="ig-user">drinkolipop<div className="ig-sponsored">Sponsored</div></div>
            <div className="ig-more">···</div>
          </div>
          <Body/>
          <div style={{height:40,background:'#fff',borderTop:'.5px solid rgba(0,0,0,.08)',display:'flex',alignItems:'center',padding:'0 10px',gap:14,fontSize:12,color:'#333'}}>
            <span>♡</span><span>💬</span><span>↗</span><span style={{marginLeft:'auto'}}>⊞</span>
          </div>
        </div>
      </div>
    </div>
  );
}

// ============== QUIZ LANDING (desktop) ==============
function QuizPage({ compact = false }) {
  return (
    <div className="browser" style={{ maxWidth: compact ? 440 : 780, width:'100%'}}>
      <div className="browser-bar">
        <div className="dots"><span/><span/><span/></div>
        <div className="url">🔒 olipop.com/quiz</div>
      </div>
      <div style={{background:'linear-gradient(180deg,#FFE4D1 0%, #F4E9D8 100%)', padding: compact?16:32, minHeight: compact?220:460, color:'#4A1942'}}>
        <div style={{display:'flex',justifyContent:'space-between',alignItems:'center',marginBottom:compact?10:22,fontSize:compact?9:12}}>
          <div style={{fontFamily:'Instrument Serif,serif',fontSize:compact?16:26,color:'#E85C2B',letterSpacing:'-.02em'}}>Olipop</div>
          <div style={{fontFamily:'JetBrains Mono,monospace',letterSpacing:'.14em',fontSize:compact?8:10,color:'#4A1942'}}>QUESTION 3 / 6</div>
        </div>
        <div style={{fontFamily:'Instrument Serif,serif',fontSize:compact?22:44,lineHeight:.98,color:'#4A1942',letterSpacing:'-.025em',marginBottom:compact?6:14,textWrap:'balance'}}>
          What does your gut <i style={{color:'#E85C2B'}}>actually</i> need?
        </div>
        <div style={{fontSize:compact?10:14,color:'rgba(74,25,66,.7)',marginBottom:compact?14:28,maxWidth: 520}}>Answer 6 quick questions. We'll match you with the flavor that fits.</div>
        <div style={{display:'grid',gridTemplateColumns:compact?'1fr 1fr':'1fr 1fr',gap:compact?8:12}}>
          {[
            { t:'More fiber', s:'Hit 9g in one can', c:'#E85C2B' },
            { t:'Less sugar', s:'≤5g per serve', c:'#9B2D5E' },
            { t:'Prebiotic boost', s:'Feed good bacteria', c:'#4A1942' },
            { t:'Caffeine-free', s:'Evening-friendly', c:'#D97046' },
          ].map((o,i) => (
            <div key={i} style={{border:`1.5px solid ${o.c}40`,background:'rgba(255,255,255,.55)',borderRadius:compact?8:14,padding:compact?'10px 12px':'18px 20px',display:'flex',gap:compact?8:12,alignItems:'center'}}>
              <div style={{width:compact?20:32,height:compact?20:32,borderRadius:'50%',background:o.c,display:'flex',alignItems:'center',justifyContent:'center',color:'#fff',fontSize:compact?9:13,fontFamily:'Instrument Serif,serif'}}>{String.fromCharCode(65+i)}</div>
              <div>
                <div style={{fontSize:compact?11:15,fontWeight:500,color:'#2a0d26'}}>{o.t}</div>
                <div style={{fontSize:compact?8.5:11,color:'rgba(74,25,66,.65)',marginTop:1}}>{o.s}</div>
              </div>
            </div>
          ))}
        </div>
        <div style={{marginTop:compact?12:22,display:'flex',alignItems:'center',gap:8}}>
          <div style={{flex:1,height:compact?3:5,background:'rgba(74,25,66,.12)',borderRadius:4,overflow:'hidden'}}>
            <div style={{width:'50%',height:'100%',background:'#E85C2B'}}/>
          </div>
          <div style={{fontFamily:'JetBrains Mono,monospace',fontSize:compact?8:10,color:'#4A1942'}}>50%</div>
        </div>
      </div>
    </div>
  );
}

// ============== OFFER LANDING (desktop) ==============
function OfferPage({ compact = false }) {
  return (
    <div className="browser" style={{maxWidth: compact?480:900}}>
      <div className="browser-bar">
        <div className="dots"><span/><span/><span/></div>
        <div className="url">🔒 olipop.com/starter-pack</div>
      </div>
      <div style={{background:'#F4E9D8',color:'#2a0d26'}}>
        <div style={{padding:compact?'14px 16px':'22px 36px',display:'flex',justifyContent:'space-between',alignItems:'center',borderBottom:'1px solid rgba(74,25,66,.12)'}}>
          <div style={{fontFamily:'Instrument Serif,serif',fontSize:compact?18:26,color:'#E85C2B'}}>Olipop</div>
          <div style={{fontFamily:'JetBrains Mono,monospace',fontSize:compact?8:11,color:'#9B2D5E',letterSpacing:'.1em'}}>🔥 ENDS IN 03:14:59</div>
        </div>
        <div style={{display:'grid',gridTemplateColumns:'1.1fr 1fr',gap:compact?14:30,padding:compact?'18px 16px':'40px 36px'}}>
          <div>
            <div style={{fontFamily:'JetBrains Mono,monospace',fontSize:compact?8:10,color:'#E85C2B',letterSpacing:'.18em',marginBottom:compact?6:12}}>★ 40,000+ 5-STAR REVIEWS</div>
            <div style={{fontFamily:'Instrument Serif,serif',fontSize:compact?24:58,lineHeight:.95,letterSpacing:'-.03em',color:'#4A1942',marginBottom:compact?8:16}}>
              Your first 12 cans. <i style={{color:'#E85C2B'}}>$19.</i>
            </div>
            <div style={{fontSize:compact?10:15,color:'rgba(74,25,66,.75)',lineHeight:1.5,marginBottom:compact?10:20}}>Build your own pack. Free shipping. Cancel anytime. Only available to first-time customers.</div>
            <ul style={{listStyle:'none',padding:0,margin:0,display:'flex',flexDirection:'column',gap:compact?4:8,fontSize:compact?9.5:13,color:'#2a0d26',marginBottom:compact?10:24}}>
              {['9g prebiotic fiber per can','Only 2–5g sugar','Made with real fruit','100% satisfaction or free'].map((f,i)=>(
                <li key={i} style={{display:'flex',gap:6,alignItems:'center'}}><span style={{color:'#E85C2B'}}>✓</span>{f}</li>
              ))}
            </ul>
            <div style={{background:'#E85C2B',color:'#fff',padding:compact?'10px 14px':'16px 22px',borderRadius:compact?8:14,fontSize:compact?11:15,fontWeight:500,textAlign:'center',boxShadow:'0 8px 24px -6px rgba(232,92,43,.5)'}}>Claim your $19 pack →</div>
          </div>
          <div style={{background:'linear-gradient(160deg,#FFE4D1,#E85C2B 80%)',borderRadius:compact?8:16,position:'relative',minHeight:compact?160:320,overflow:'hidden',display:'flex',alignItems:'center',justifyContent:'center'}}>
            <div style={{position:'absolute',top:10,right:10,background:'#4A1942',color:'#fff',fontSize:compact?8:10,padding:'3px 8px',borderRadius:100,fontWeight:600,letterSpacing:'.08em'}}>SAVE 65%</div>
            <div style={{display:'flex',gap:compact?4:8,transform:'rotate(-6deg)'}}>
              {['coral','cherry','vintage','grape'].map((c,i)=>(
                <div key={i} className={`brand-can ${c}`} style={{width:compact?28:48,height:compact?60:120,borderRadius:compact?'3px 3px 6px 6px':'5px 5px 10px 10px',position:'relative'}}>
                  <div style={{position:'absolute',top:'30%',left:compact?2:4,right:compact?2:4,background:'rgba(255,255,255,.9)',padding:compact?'2px':'4px 2px',textAlign:'center',fontFamily:'Instrument Serif,serif',fontSize:compact?5:9,color:'#E85C2B',borderRadius:1}}>OLIPOP</div>
                </div>
              ))}
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}

// ============== EMAIL PREVIEW ==============
function EmailPreview({ variant = 'cart', compact = false }) {
  const subjects = {
    cart: { sender:'Olipop', line:'Your cart\'s still warm 🧡', body:'Still thinking about that Strawberry Vanilla?' },
    winback: { sender:'Olipop', line:'We miss your gut.', body:'Here\'s 20% off to come back.' },
    browse: { sender:'Olipop', line:'You peeked. We noticed.', body:'The Classic Root Beer is calling.' },
  };
  const s = subjects[variant] || subjects.cart;
  if (compact) {
    return (
      <div className="mailbox">
        <div className="mailbox-head">📧 Primary · 3 unread</div>
        {[s, subjects.winback, subjects.browse].map((e,i)=>(
          <div key={i} className={`mail-row ${i===0?'unread':''}`}>
            <div className="mail-av">O</div>
            <div className="mail-body">
              <div className="mail-sender">{e.sender}</div>
              <div className="mail-subj"><b>{e.line}</b> — {e.body}</div>
            </div>
            <div className="mail-time">{i===0?'9:12 AM':i===1?'Yest':'Mon'}</div>
          </div>
        ))}
      </div>
    );
  }
  // full view
  return (
    <div style={{background:'#fff',borderRadius:12,overflow:'hidden',maxWidth:520,width:'100%',margin:'auto',boxShadow:'var(--shadow-lg)'}}>
      <div style={{padding:'14px 20px',borderBottom:'1px solid #eee',display:'flex',gap:10,fontSize:12,color:'#5f6368',alignItems:'center'}}>
        <div className="mail-av">O</div>
        <div>
          <div style={{color:'#202124',fontWeight:500}}>Olipop <span style={{color:'#5f6368',fontWeight:400}}>&lt;hello@drinkolipop.com&gt;</span></div>
          <div style={{fontSize:11}}>to you · 9:12 AM</div>
        </div>
      </div>
      <div style={{padding:'24px 28px',color:'#111'}}>
        <div style={{fontSize:22,fontFamily:'Instrument Serif,serif',color:'#4A1942',marginBottom:14,letterSpacing:'-.015em'}}>{s.line}</div>
        <div style={{height:160,borderRadius:8,background:'linear-gradient(160deg,#FFE4D1,#E85C2B 60%,#9B2D5E)',marginBottom:18,position:'relative',overflow:'hidden',display:'flex',alignItems:'center',justifyContent:'center'}}>
          <div style={{fontFamily:'Instrument Serif,serif',fontSize:42,color:'#F4E9D8',fontStyle:'italic'}}>come back.</div>
        </div>
        <div style={{fontSize:14,color:'#333',lineHeight:1.55,marginBottom:16}}>Hey friend — you had Strawberry Vanilla in your cart yesterday. It's still waiting. Here's 15% off if you want to finish what you started.</div>
        <div style={{display:'inline-block',background:'#E85C2B',color:'#fff',padding:'12px 22px',borderRadius:10,fontSize:14,fontWeight:500}}>Claim 15% → OLIPOP15</div>
        <div style={{marginTop:24,fontSize:11,color:'#888'}}>You're getting this because you love your gut. <u>Unsubscribe</u></div>
      </div>
    </div>
  );
}

// ============== ADVERTORIAL / LISTICLE (browser) ==============
function ArticlePage({ kind = 'advertorial', compact = false }) {
  if (kind === 'listicle') {
    return (
      <div className="browser" style={{maxWidth: compact?440:780}}>
        <div className="browser-bar"><div className="dots"><span/><span/><span/></div><div className="url">🔒 well-tribune.com/7-sodas-gut-2026</div></div>
        <div style={{background:'#fff',padding:compact?'12px 14px':'26px 36px',color:'#111'}}>
          <div style={{fontFamily:'Georgia,serif',fontSize:compact?8:10,color:'#888',letterSpacing:'.1em',textTransform:'uppercase'}}>WELL TRIBUNE · FEATURED</div>
          <h1 style={{fontFamily:'Instrument Serif,serif',fontSize:compact?20:38,lineHeight:1.05,margin:compact?'4px 0 8px':'8px 0 14px',letterSpacing:'-.02em'}}>7 "Sodas" that nutritionists <i style={{color:'#E85C2B'}}>actually</i> approve of</h1>
          <div style={{fontSize:compact?9:12,color:'#666',marginBottom:compact?10:18}}>By Sam Ortega · April 23, 2026 · 6 min read</div>
          <div style={{display:'flex',flexDirection:'column',gap:compact?6:12}}>
            {[
              {n:'#1',name:'Olipop',tag:'Best overall',color:'#E85C2B'},
              {n:'#2',name:'Poppi',tag:'Runner-up',color:'#F5B301'},
              {n:'#3',name:'Culture Pop',tag:'Honorable mention',color:'#5AB36E'},
            ].map((x,i)=>(
              <div key={i} style={{display:'flex',gap:compact?8:14,padding:compact?8:12,border:`1px solid ${x.color}30`,borderRadius:compact?6:10,background:i===0?`linear-gradient(90deg,${x.color}10,transparent)`:'#fafafa'}}>
                <div style={{fontFamily:'Instrument Serif,serif',fontSize:compact?20:34,color:x.color,lineHeight:1}}>{x.n}</div>
                <div style={{flex:1}}>
                  <div style={{fontSize:compact?11:15,fontWeight:600}}>{x.name} <span style={{fontWeight:400,color:'#888',fontSize:compact?8:11,marginLeft:6}}>{x.tag}</span></div>
                  <div style={{fontSize:compact?8.5:11,color:'#555',marginTop:2,lineHeight:1.4}}>{i===0?'9g fiber, 2-5g sugar, prebiotic. The only one our panel rebought.':'Good apple-cider-vinegar base. A little tart for some.'}</div>
                </div>
              </div>
            ))}
          </div>
        </div>
      </div>
    );
  }
  // advertorial
  return (
    <div className="browser" style={{maxWidth: compact?440:780}}>
      <div className="browser-bar"><div className="dots"><span/><span/><span/></div><div className="url">🔒 healthdesk-weekly.com/olipop-gut</div></div>
      <div style={{background:'#fff',padding:compact?'12px 14px':'26px 40px',color:'#111'}}>
        <div style={{fontFamily:'Georgia,serif',fontSize:compact?8:10,color:'#888',letterSpacing:'.1em',textTransform:'uppercase'}}>HEALTHDESK WEEKLY · ADVERTORIAL</div>
        <h1 style={{fontFamily:'Instrument Serif,serif',fontSize:compact?20:38,lineHeight:1.05,margin:compact?'4px 0 8px':'8px 0 14px',letterSpacing:'-.02em'}}>"I thought I'd never drink soda again." How one mom of three found a <i style={{color:'#E85C2B'}}>loophole</i></h1>
        <div style={{fontSize:compact?9:12,color:'#666',marginBottom:compact?10:18}}>By Lea Whitney · April 23, 2026 · 5 min read</div>
        <div style={{height:compact?90:180,borderRadius:compact?6:10,background:'linear-gradient(160deg,#FFE4D1,#E85C2B 60%,#9B2D5E)',marginBottom:compact?10:18,position:'relative'}}>
          <div style={{position:'absolute',bottom:compact?6:12,left:compact?8:14,fontSize:compact?8:10,color:'#fff',opacity:.8}}>Sarah K., 36 — "Honestly, I can't taste the difference."</div>
        </div>
        <div style={{fontSize:compact?10:14,color:'#333',lineHeight:1.55,marginBottom:compact?8:14}}>After 9 years of being told by her GI doctor to quit Diet Coke, Sarah tried every "healthy" alternative on the shelf. Most were awful. Then her neighbor handed her a can of <b style={{color:'#E85C2B'}}>Olipop</b> at a 4th of July barbecue...</div>
        <div style={{fontSize:compact?10:14,color:'#333',lineHeight:1.55}}>"I didn't believe it at first. It tastes like actual root beer. But it has 9 grams of fiber and barely any sugar. I haven't bought a Diet Coke since."</div>
      </div>
    </div>
  );
}

// ============== EBOOK ==============
function Ebook({ size = 'md' }) {
  const scale = size==='lg' ? 1.6 : 1;
  return (
    <div style={{ background: 'radial-gradient(ellipse at top, #3a1a38, #0d0b12)', display:'flex', alignItems:'center', justifyContent:'center', width:'100%', height:'100%', padding: 24 }}>
      <div style={{display:'flex', gap: 20, alignItems:'center', transform:`scale(${scale})`}}>
        <div className="book" style={{width:160,height:216}}>
          <div className="book-eyebrow">The Olipop Guide</div>
          <div className="book-title">Your Gut,<br/>Your <i>Rules.</i></div>
          <div className="book-foot">21 days to a happier belly</div>
        </div>
        {size==='lg' && (
          <div style={{color:'var(--ink-2)',maxWidth:260,fontSize:13,lineHeight:1.6}}>
            <div style={{fontFamily:'Instrument Serif,serif',fontSize:28,color:'var(--cream)',lineHeight:1.05,marginBottom:12,letterSpacing:'-.015em'}}>Branded digital product — delivered as a lead magnet.</div>
            <div>42 pages. Cover mirrors the brand. Chapters auto-written from their catalog + persona. PDF + email capture ready.</div>
          </div>
        )}
      </div>
    </div>
  );
}

// ============== Tile preview renderer ==============
// The Worker produces emails in order [upsell, cart, winback] but the UI
// labels them [cart, upsell, winback]. Match by `role` so each tile gets
// the email it actually advertises.
const EMAIL_ROLE_FOR_ID = {
  'email-1': 'cart_recovery',         // labeled "Abandoned cart" in ASSETS
  'email-2': 'post_purchase_upsell',  // labeled "Post-purchase upsell" in ASSETS
  'email-3': 'non_converter_winback', // labeled "180-day winback" in ASSETS
};
function findEmailByRole(emails, role) {
  if (!Array.isArray(emails)) return null;
  return emails.find((e) => e && e.role === role) || null;
}
function wrapEmailShell(e) {
  if (!e || !e.body_html) return null;
  return `<!doctype html><html><head><meta charset="utf-8"><title>${e.subject || ''}</title><style>body{margin:0;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif;background:#f6f8fc;color:#202124}.mail{max-width:680px;margin:0 auto;background:#fff;border:1px solid #dadce0;border-radius:8px;overflow:hidden}.mail-h{padding:20px 24px;border-bottom:1px solid #e8eaed}.mail-sub{font-size:22px;font-weight:500;line-height:1.3;margin:0 0 6px}.mail-f{font-size:13px;color:#5f6368}.mail-body{padding:0}</style></head><body><div class="mail"><div class="mail-h"><div class="mail-sub">${e.subject || '(no subject)'}</div><div class="mail-f">${e.from_name || 'Your Brand'} &lt;hi@yourbrand.com&gt;</div></div><div class="mail-body">${e.body_html}</div></div></body></html>`;
}
function emailHtml(emails, tileId) {
  const role = EMAIL_ROLE_FOR_ID[tileId];
  if (!role) return null;
  return wrapEmailShell(findEmailByRole(emails, role));
}

// Pull live HTML for a given tile-id out of the Worker payload, if present.
function livePayloadHtml(payload, id) {
  if (!payload || !payload.assets) return null;
  const a = payload.assets;
  if (id === 'offer') return a.offer_lp?.html || null;
  if (id === 'quiz') return a.quiz?.html || null;
  if (id === 'advertorial') return a.advertorial?.html || null;
  if (id === 'listicle') return a.listicle?.html || null;
  if (id === 'ebook') return a.digital_product?.html || null;
  if (id === 'email-1' || id === 'email-2' || id === 'email-3') return emailHtml(a.emails, id);
  return null;
}

// Pull live generated ad image for a given tile slot. Worker now generates
// 10 distinct GPT Image 2 creatives: ads[0..4] = Batch A (cold), ads[5..9] =
// Batch B (warm). Each UI tile maps 1:1 to its own index — no rotation.
function liveAdImage(payload, id) {
  const ads = (payload && payload.new_batch && payload.new_batch.ads) || [];
  const idxMap = {
    'ad-a1':0, 'ad-a2':1, 'ad-a3':2, 'ad-a4':3, 'ad-a5':4,
    'ad-b1':5, 'ad-b2':6, 'ad-b3':7, 'ad-b4':8, 'ad-b5':9,
  };
  const i = idxMap[id];
  if (i === undefined) return null;
  const ad = ads[i];
  if (!ad) return null;
  return ad.generated_image_url || ad.image_url || null;
}

// Cache blob URLs keyed by HTML-content hash so we don't thrash iframes.
const _tileBlobCache = new Map();
function htmlToStableBlobUrl(html) {
  if (!html) return null;
  const cached = _tileBlobCache.get(html);
  if (cached) return cached;
  try {
    const url = URL.createObjectURL(new Blob([html], { type: 'text/html' }));
    _tileBlobCache.set(html, url);
    // Soft cap: if the cache gets big (brand switches, regens), evict the
    // oldest. Keeps memory bounded for a long-lived session.
    if (_tileBlobCache.size > 60) {
      const firstKey = _tileBlobCache.keys().next().value;
      const firstUrl = _tileBlobCache.get(firstKey);
      _tileBlobCache.delete(firstKey);
      try { URL.revokeObjectURL(firstUrl); } catch {}
    }
    return url;
  } catch {
    return null;
  }
}

function TilePreview({ id, payload }) {
  // Ads render as clean 1:1 squares — no phone chrome, no username, no
  // like/comment bar. Just the live product-locked GPT Image 2 creative,
  // perfectly square. Real ads on Meta; we show them the way Meta will.
  if (/^ad-/.test(id)) {
    const adImg = liveAdImage(payload, id);
    return (
      <div className="preview p-square" style={{aspectRatio:'1 / 1',width:'100%',display:'flex',alignItems:'center',justifyContent:'center',background:'#0a0b12',borderRadius:16,overflow:'hidden',boxShadow:'0 20px 40px -16px rgba(0,0,0,.6)'}}>
        {adImg
          ? <img src={adImg} alt="Generated ad creative"
                 style={{width:'100%',height:'100%',objectFit:'cover',display:'block'}}
                 loading="lazy"/>
          : <div style={{color:'var(--ink-3)',fontSize:11,letterSpacing:'.14em',textTransform:'uppercase'}}>Generating…</div>}
      </div>
    );
  }

  // HTML assets: when the Worker has finished generating, render the actual
  // HTML in the thumbnail. Blob URLs are memoized so we don't tear down iframes.
  const liveHtml = livePayloadHtml(payload, id);
  const liveSrc = liveHtml ? htmlToStableBlobUrl(liveHtml) : null;
  const domain = (payload && payload.store && payload.store.url
    ? payload.store.url.replace(/^https?:\/\//i,'').replace(/\/$/,'')
    : 'your-store.com');

  switch (id) {
    // All ad-* ids are handled by the early-return above — we never reach here
    // for ads because every ad renders as a clean 1:1 square (live img or
    // "Generating…" placeholder).
    case 'quiz': return <HTMLPreview src={liveSrc || 'assets/quiz-landing.html'} mode="thumb" designWidth={720} designHeight={1400} url={`${domain}/quiz`}/>;
    case 'advertorial': return <HTMLPreview src={liveSrc || 'assets/advertorial.html'} mode="thumb" designWidth={900} designHeight={3600} url={`healthdesk-weekly.com/article`}/>;
    case 'listicle': return <HTMLPreview src={liveSrc || 'assets/listicle.html'} mode="thumb" designWidth={900} designHeight={4200} url={`theeatguide.com/best-in-category`}/>;
    case 'offer': return <HTMLPreview src={liveSrc || 'assets/offer-page.html'} mode="thumb" designWidth={1280} designHeight={3400} url={`${domain}/starter`}/>;
    case 'email-1': return <HTMLPreview src={liveSrc || 'assets/email-cart.html'} mode="thumb" designWidth={680} designHeight={1800} url="mail.google.com" chrome="browser"/>;
    case 'email-2': return <HTMLPreview src={liveSrc || 'assets/email-upsell.html'} mode="thumb" designWidth={680} designHeight={1800} url="mail.google.com" chrome="browser"/>;
    case 'email-3': return <HTMLPreview src={liveSrc || 'assets/email-winback.html'} mode="thumb" designWidth={680} designHeight={1800} url="mail.google.com" chrome="browser"/>;
    case 'ebook': return <HTMLPreview src={liveSrc || 'assets/ebook.html'} mode="thumb" designWidth={1200} designHeight={1600} url={`${domain}/ebook`} chrome="browser"/>;
    default: return null;
  }
}

// expose
Object.assign(window, { AdPhone, QuizPage, OfferPage, EmailPreview, ArticlePage, Ebook, TilePreview, HTMLPreview });
