#!/usr/bin/env python3
"""
IterFact Template Vault - Wave 2
13 new engines targeting the gaps identified across 100+ market verticals.
"""

import os, json

ENGINES_DIR = "engines"

def write_template(folder, filename, content, meta):
    path = os.path.join(ENGINES_DIR, folder)
    os.makedirs(path, exist_ok=True)
    with open(os.path.join(path, filename), 'w') as f:
        f.write(content)
    with open(os.path.join(path, "meta.json"), 'w') as f:
        json.dump(meta, f, indent=2)


# ============================================================
# ENGINE 18: PARALLAX SCROLL STORY
# Needed by: Annual reports, brand stories, case studies,
# nonprofit impact, product launches, longform marketing
# ============================================================
write_template("18-parallax-story", "template.html", '''<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- ENGINE_META: {"id":"parallax-story","name":"Parallax Scroll Story","category":"narrative","tags":["parallax","scroll","storytelling","longform","immersive","annual-report","case-study"],"compatible_verticals":["annual-report","case-study","brand-story","product-launch","nonprofit-impact","marketing-campaign","about-us"]} -->
<!-- AI_USAGE: Immersive scroll-driven storytelling engine with parallax backgrounds, text reveals, and big hero sections.
     Modify CHAPTERS array: {title, subtitle, body, stat{value,label}, bgGradient, textAlign}.
     Each chapter fills the viewport. Text fades in on scroll. Stats animate when visible. -->
<title><!-- CUSTOMIZABLE: story_title -->Our Story<!-- /CUSTOMIZABLE --></title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Libre+Baskerville:ital,wght@0,400;0,700;1,400&family=Karla:wght@400;500;700&display=swap');
:root{--text:#fafafa;--muted:rgba(255,255,255,0.6);--font-display:'Libre Baskerville',serif;--font-body:'Karla',sans-serif}
*{margin:0;padding:0;box-sizing:border-box}
body{background:#0a0a0a;color:var(--text);font-family:var(--font-body);overflow-x:hidden}
.chapter{min-height:100vh;display:flex;flex-direction:column;justify-content:center;padding:4rem 8%;position:relative;overflow:hidden}
.chapter .bg{position:absolute;top:-20%;left:0;width:100%;height:140%;z-index:0;opacity:0.4}
.chapter .content{position:relative;z-index:1;max-width:700px;opacity:0;transform:translateY(40px);transition:all 1s ease}
.chapter .content.visible{opacity:1;transform:translateY(0)}
.chapter.center .content{margin:0 auto;text-align:center}
.chapter.right .content{margin-left:auto;text-align:right}
.chapter h2{font-family:var(--font-display);font-size:clamp(2rem,5vw,3.5rem);line-height:1.2;margin-bottom:1rem;font-weight:400}
.chapter .subtitle{font-size:clamp(1rem,2vw,1.3rem);color:var(--muted);margin-bottom:2rem;font-style:italic;font-family:var(--font-display)}
.chapter p{font-size:1.05rem;line-height:1.9;color:var(--muted);margin-bottom:1.5rem}
.chapter .stat-block{display:flex;gap:3rem;margin-top:2rem;flex-wrap:wrap}
.chapter.center .stat-block{justify-content:center}
.chapter.right .stat-block{justify-content:flex-end}
.stat-item .num{font-family:var(--font-display);font-size:clamp(2.5rem,6vw,4rem);font-weight:700;line-height:1}
.stat-item .lbl{font-size:0.8rem;color:var(--muted);text-transform:uppercase;letter-spacing:0.15em;margin-top:0.5rem}
.divider{width:60px;height:1px;background:rgba(255,255,255,0.2);margin:2rem 0}
.chapter.center .divider{margin:2rem auto}
.chapter.right .divider{margin-left:auto}
.progress-dots{position:fixed;right:2rem;top:50%;transform:translateY(-50%);z-index:100;display:flex;flex-direction:column;gap:0.75rem}
.progress-dots .dot{width:8px;height:8px;border-radius:50%;background:rgba(255,255,255,0.2);transition:all 0.3s;cursor:pointer}
.progress-dots .dot.active{background:white;transform:scale(1.4)}
@media(max-width:600px){.chapter{padding:3rem 6%} .chapter .stat-block{gap:2rem}}
</style>
</head>
<body>
<div class="progress-dots" id="dots"></div>
<script>
// CUSTOMIZABLE: CHAPTERS array - each chapter is a full-viewport section
// AI_HINT: bgGradient is CSS gradient string. textAlign: 'left'|'center'|'right'.
// stats is optional array of {value,label}. body can contain HTML.
const CHAPTERS = [
  {
    title: "It started with a question",
    subtitle: "What if we could do this differently?",
    body: "In 2019, we looked at an industry that hadn't changed in decades and asked a simple question. The answer would change everything.",
    bgGradient: "linear-gradient(135deg, #1a1a2e 0%, #16213e 100%)",
    textAlign: "left"
  },
  {
    title: "The first year",
    subtitle: "2020 — Building in silence",
    body: "While the world paused, we built. Every line of code, every conversation with early users, every late night — they all pointed toward something real.",
    stats: [{value:"12",label:"Team Members"},{value:"847",label:"Beta Users"},{value:"94%",label:"Retention"}],
    bgGradient: "linear-gradient(135deg, #0f3460 0%, #16213e 100%)",
    textAlign: "center"
  },
  {
    title: "Traction",
    subtitle: "2021 — The numbers spoke",
    body: "What started as an experiment became undeniable. Customers weren't just using the product — they were building their businesses around it.",
    stats: [{value:"$2.4M",label:"ARR"},{value:"12K",label:"Active Users"},{value:"340%",label:"YoY Growth"}],
    bgGradient: "linear-gradient(135deg, #1a1a2e 0%, #533483 100%)",
    textAlign: "right"
  },
  {
    title: "What's next",
    subtitle: "The best is ahead",
    body: "We're just getting started. The next chapter isn't written yet — but we know who's writing it. Our team, our customers, and the community that believed before anyone else.",
    bgGradient: "linear-gradient(135deg, #0a0a0a 0%, #1a1a2e 100%)",
    textAlign: "center"
  }
];

const dots = document.getElementById('dots');

CHAPTERS.forEach((ch, i) => {
  // Create chapter
  const sec = document.createElement('div');
  sec.className = `chapter ${ch.textAlign}`;
  sec.style.background = ch.bgGradient;
  
  let html = '<div class="content">';
  html += `<h2>${ch.title}</h2>`;
  if (ch.subtitle) html += `<div class="subtitle">${ch.subtitle}</div>`;
  html += `<div class="divider"></div>`;
  html += `<p>${ch.body}</p>`;
  if (ch.stats) {
    html += '<div class="stat-block">';
    ch.stats.forEach(s => html += `<div class="stat-item"><div class="num">${s.value}</div><div class="lbl">${s.label}</div></div>`);
    html += '</div>';
  }
  html += '</div>';
  sec.innerHTML = html;
  document.body.appendChild(sec);

  // Create dot
  const dot = document.createElement('div');
  dot.className = 'dot';
  dot.onclick = () => sec.scrollIntoView({behavior:'smooth'});
  dots.appendChild(dot);
});

// Scroll observer
const observer = new IntersectionObserver((entries) => {
  entries.forEach(e => {
    if (e.isIntersecting) {
      e.target.querySelector('.content').classList.add('visible');
      const idx = [...document.querySelectorAll('.chapter')].indexOf(e.target);
      document.querySelectorAll('.dot').forEach((d,i) => d.classList.toggle('active', i===idx));
    }
  });
}, {threshold: 0.4});
document.querySelectorAll('.chapter').forEach(ch => observer.observe(ch));
</script>
</body>
</html>''', {
    "id": "parallax-story", "name": "Parallax Scroll Story", "category": "narrative",
    "tags": ["parallax","scroll","storytelling","longform","immersive","annual-report","case-study"],
    "compatible_verticals": ["annual-report","case-study","brand-story","product-launch","nonprofit-impact","marketing-campaign"],
    "core_libs": [], "complexity": "medium"
})


# ============================================================
# ENGINE 19: ORG CHART / TEAM DIRECTORY
# ============================================================
write_template("19-org-chart", "template.html", '''<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- ENGINE_META: {"id":"org-chart","name":"Org Chart / Team Directory","category":"data","tags":["org-chart","team","directory","hierarchy","people","company"],"compatible_verticals":["company-profile","team-page","franchise-structure","department-overview","board-of-directors","staff-directory"]} -->
<!-- AI_USAGE: Interactive org chart with clickable person cards. Modify PEOPLE array: {id,name,title,dept,reports_to(id|null),avatar(emoji),bio,email}.
     Root node (reports_to:null) is CEO/top. Supports filtering by department. Click card for detail panel. -->
<title>Team</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&display=swap');
:root{--bg:#fafafa;--card:#fff;--border:#e5e7eb;--text:#111827;--muted:#6b7280;--accent:#6366f1;--hover:#f3f4f6}
*{margin:0;padding:0;box-sizing:border-box}
body{background:var(--bg);color:var(--text);font-family:'Outfit',sans-serif;padding:2rem;min-height:100vh}
h1{font-size:1.8rem;font-weight:600;text-align:center;margin-bottom:0.5rem}
.subtitle{text-align:center;color:var(--muted);margin-bottom:1rem;font-size:0.95rem}
.filters{display:flex;justify-content:center;gap:0.5rem;margin-bottom:2rem;flex-wrap:wrap}
.filter-btn{padding:0.4rem 1rem;border:1px solid var(--border);background:var(--card);border-radius:20px;cursor:pointer;font-family:inherit;font-size:0.8rem;color:var(--muted);transition:all 0.2s}
.filter-btn.active,.filter-btn:hover{border-color:var(--accent);color:var(--accent);background:rgba(99,102,241,0.05)}
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:1rem;max-width:1100px;margin:0 auto}
.person-card{background:var(--card);border:1px solid var(--border);border-radius:12px;padding:1.5rem;cursor:pointer;transition:all 0.2s;text-align:center}
.person-card:hover{box-shadow:0 8px 24px rgba(0,0,0,0.08);transform:translateY(-2px)}
.person-card .avatar{font-size:3rem;margin-bottom:0.75rem;display:block}
.person-card .name{font-weight:600;font-size:1rem;margin-bottom:0.25rem}
.person-card .title{font-size:0.8rem;color:var(--accent);margin-bottom:0.25rem}
.person-card .dept{font-size:0.75rem;color:var(--muted);padding:0.2rem 0.6rem;background:var(--hover);border-radius:10px;display:inline-block}
.person-card.hidden{display:none}
/* Detail panel */
.overlay{display:none;position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.4);z-index:100;align-items:center;justify-content:center}
.overlay.show{display:flex}
.detail{background:var(--card);border-radius:16px;padding:2.5rem;max-width:440px;width:90%;position:relative;box-shadow:0 24px 48px rgba(0,0,0,0.15)}
.detail .close{position:absolute;top:1rem;right:1.5rem;background:none;border:none;font-size:1.5rem;cursor:pointer;color:var(--muted)}
.detail .avatar{font-size:4rem;display:block;margin-bottom:1rem}
.detail .name{font-size:1.4rem;font-weight:700;margin-bottom:0.25rem}
.detail .title{color:var(--accent);font-size:0.95rem;margin-bottom:0.5rem}
.detail .bio{color:var(--muted);line-height:1.7;font-size:0.9rem;margin:1rem 0}
.detail .meta{font-size:0.8rem;color:var(--muted)}
.detail .reports{margin-top:1rem;font-size:0.8rem;color:var(--muted)}
.detail .reports strong{color:var(--text)}
</style></head><body>
<h1><!-- CUSTOMIZABLE: team_title -->Our Team<!-- /CUSTOMIZABLE --></h1>
<p class="subtitle"><!-- CUSTOMIZABLE: team_subtitle -->The people building the future<!-- /CUSTOMIZABLE --></p>
<div class="filters" id="filters"></div>
<div class="grid" id="grid"></div>
<div class="overlay" id="overlay" onclick="if(event.target===this)closeDetail()">
  <div class="detail" id="detail"></div>
</div>
<script>
// CUSTOMIZABLE: PEOPLE array
const PEOPLE = [
  {id:'ceo',name:'Alex Chen',title:'CEO & Co-Founder',dept:'Leadership',reports_to:null,avatar:'👤',bio:'15 years in fintech. Previously VP at Stripe. Stanford MBA.',email:'alex@company.com'},
  {id:'cto',name:'Sam Rivera',title:'CTO & Co-Founder',dept:'Engineering',reports_to:'ceo',avatar:'👤',bio:'Former Google engineer. Built distributed systems at scale.',email:'sam@company.com'},
  {id:'coo',name:'Jordan Lee',title:'COO',dept:'Operations',reports_to:'ceo',avatar:'👤',bio:'Ex-McKinsey. Scaled operations at two unicorns.',email:'jordan@company.com'},
  {id:'vps',name:'Morgan Taylor',title:'VP Sales',dept:'Sales',reports_to:'ceo',avatar:'👤',bio:'Closed $50M+ in enterprise deals. Salesforce alumni.',email:'morgan@company.com'},
  {id:'eng1',name:'Casey Kim',title:'Senior Engineer',dept:'Engineering',reports_to:'cto',avatar:'👤',bio:'Full-stack. Rust enthusiast. Open source contributor.',email:'casey@company.com'},
  {id:'eng2',name:'Riley Park',title:'Senior Engineer',dept:'Engineering',reports_to:'cto',avatar:'👤',bio:'ML/AI specialist. Former research scientist at DeepMind.',email:'riley@company.com'},
  {id:'des1',name:'Drew Santos',title:'Head of Design',dept:'Design',reports_to:'coo',avatar:'👤',bio:'Led design at Figma. Passionate about design systems.',email:'drew@company.com'},
  {id:'mkt1',name:'Avery Brooks',title:'Marketing Lead',dept:'Marketing',reports_to:'coo',avatar:'👤',bio:'Growth marketing expert. Built content engines from 0 to 1M.',email:'avery@company.com'}
];

const grid = document.getElementById('grid');
const filters = document.getElementById('filters');
const overlay = document.getElementById('overlay');
const detail = document.getElementById('detail');
const depts = ['All', ...new Set(PEOPLE.map(p=>p.dept))];
const peopleMap = Object.fromEntries(PEOPLE.map(p=>[p.id,p]));

// Filters
depts.forEach((d,i) => {
  const btn = document.createElement('button');
  btn.className = `filter-btn ${i===0?'active':''}`;
  btn.textContent = d;
  btn.onclick = () => {
    document.querySelectorAll('.filter-btn').forEach(b=>b.classList.remove('active'));
    btn.classList.add('active');
    document.querySelectorAll('.person-card').forEach(c => {
      c.classList.toggle('hidden', d!=='All' && c.dataset.dept!==d);
    });
  };
  filters.appendChild(btn);
});

// Cards
PEOPLE.forEach(p => {
  const card = document.createElement('div');
  card.className = 'person-card';
  card.dataset.dept = p.dept;
  card.innerHTML = `<span class="avatar">${p.avatar}</span><div class="name">${p.name}</div><div class="title">${p.title}</div><span class="dept">${p.dept}</span>`;
  card.onclick = () => showDetail(p);
  grid.appendChild(card);
});

function showDetail(p) {
  const manager = p.reports_to ? peopleMap[p.reports_to] : null;
  const reports = PEOPLE.filter(x=>x.reports_to===p.id);
  detail.innerHTML = `<button class="close" onclick="closeDetail()">&times;</button>
    <span class="avatar">${p.avatar}</span><div class="name">${p.name}</div><div class="title">${p.title}</div>
    <span class="dept" style="display:inline-block;margin-top:0.5rem">${p.dept}</span>
    <div class="bio">${p.bio}</div>
    <div class="meta">📧 ${p.email}</div>
    ${manager ? `<div class="reports">Reports to: <strong>${manager.name}</strong></div>` : ''}
    ${reports.length ? `<div class="reports">Direct reports: <strong>${reports.map(r=>r.name).join(', ')}</strong></div>` : ''}`;
  overlay.classList.add('show');
}
function closeDetail(){overlay.classList.remove('show');}
document.addEventListener('keydown',e=>{if(e.key==='Escape')closeDetail();});
</script></body></html>''', {
    "id": "org-chart", "name": "Org Chart / Team Directory", "category": "data",
    "tags": ["org-chart","team","directory","hierarchy","people","company","staff"],
    "compatible_verticals": ["company-profile","team-page","franchise-structure","department-overview","board-of-directors"],
    "core_libs": [], "complexity": "low"
})


# ============================================================
# ENGINE 20: INTERACTIVE MAP
# ============================================================
write_template("20-interactive-map", "template.html", '''<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- ENGINE_META: {"id":"interactive-map","name":"Interactive Map","category":"spatial","tags":["map","locations","geography","pins","store-locator","territory"],"compatible_verticals":["store-locator","real-estate","territory-mapping","office-locations","event-venues","delivery-zones","travel-itinerary"]} -->
<!-- AI_USAGE: SVG-based interactive map with clickable location pins and a sidebar detail panel.
     Modify LOCATIONS array: {id,name,x,y,category,description,details{}}. x/y are percentage positions on the map container.
     Supports category filtering. For real maps, replace the SVG background with a static map image. -->
<title>Locations</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;700&display=swap');
:root{--bg:#f8fafc;--card:#fff;--border:#e2e8f0;--text:#0f172a;--muted:#64748b;--accent:#0ea5e9;--pin:#ef4444}
*{margin:0;padding:0;box-sizing:border-box}
body{background:var(--bg);color:var(--text);font-family:'DM Sans',sans-serif;height:100vh;display:flex}
.map-container{flex:1;position:relative;background:#e2e8f0;overflow:hidden;min-height:400px}
.map-bg{width:100%;height:100%;object-fit:cover;opacity:0.3}
/* AI_HINT: Replace this SVG placeholder with a real map image background-image for production use */
.map-container::before{content:'';position:absolute;top:0;left:0;right:0;bottom:0;
  background:linear-gradient(135deg,#dbeafe 0%,#e0e7ff 50%,#ddd6fe 100%);z-index:0}
.pin{position:absolute;z-index:2;cursor:pointer;transform:translate(-50%,-100%);transition:transform 0.2s}
.pin:hover{transform:translate(-50%,-100%) scale(1.2)}
.pin svg{width:32px;height:40px;filter:drop-shadow(0 2px 4px rgba(0,0,0,0.2))}
.pin.active svg path{fill:var(--accent)}
.sidebar{width:360px;background:var(--card);border-left:1px solid var(--border);overflow-y:auto;display:flex;flex-direction:column}
.sidebar-header{padding:1.5rem;border-bottom:1px solid var(--border)}
.sidebar-header h2{font-size:1.2rem;margin-bottom:0.5rem}
.sidebar-header .count{font-size:0.85rem;color:var(--muted)}
.filters{display:flex;gap:0.5rem;padding:1rem 1.5rem;flex-wrap:wrap;border-bottom:1px solid var(--border)}
.filter-btn{padding:0.3rem 0.8rem;border:1px solid var(--border);background:var(--card);border-radius:15px;cursor:pointer;font-family:inherit;font-size:0.75rem;transition:all 0.2s}
.filter-btn.active{border-color:var(--accent);color:var(--accent);background:rgba(14,165,233,0.05)}
.location-list{flex:1;overflow-y:auto}
.location-item{padding:1rem 1.5rem;border-bottom:1px solid var(--border);cursor:pointer;transition:background 0.15s}
.location-item:hover,.location-item.active{background:rgba(14,165,233,0.04)}
.location-item .name{font-weight:600;font-size:0.9rem;margin-bottom:0.25rem}
.location-item .cat{font-size:0.7rem;color:var(--accent);text-transform:uppercase;letter-spacing:0.08em}
.location-item .desc{font-size:0.8rem;color:var(--muted);margin-top:0.25rem;line-height:1.5}
.detail-panel{display:none;padding:1.5rem}
.detail-panel.show{display:block}
.detail-panel h3{font-size:1.1rem;margin-bottom:0.5rem}
.detail-panel .meta{font-size:0.85rem;color:var(--muted);line-height:1.7}
.detail-panel .back{background:none;border:none;color:var(--accent);cursor:pointer;font-family:inherit;font-size:0.85rem;margin-bottom:1rem;padding:0}
@media(max-width:768px){body{flex-direction:column} .sidebar{width:100%;max-height:50vh}}
</style></head><body>
<div class="map-container" id="map"></div>
<div class="sidebar">
  <div class="sidebar-header"><h2><!-- CUSTOMIZABLE: map_title -->Our Locations<!-- /CUSTOMIZABLE --></h2><div class="count" id="count"></div></div>
  <div class="filters" id="filters"></div>
  <div class="location-list" id="list"></div>
  <div class="detail-panel" id="detailPanel"></div>
</div>
<script>
// CUSTOMIZABLE: LOCATIONS array - x,y are % positions on map container
const LOCATIONS = [
  {id:'nyc',name:'New York HQ',x:78,y:35,category:'Office',description:'Our flagship headquarters in Manhattan.',details:{address:'350 5th Ave, New York, NY 10118',phone:'(212) 555-0100',hours:'Mon-Fri 9am-6pm',team:'45 employees'}},
  {id:'sf',name:'San Francisco',x:12,y:38,category:'Office',description:'West coast engineering hub.',details:{address:'525 Market St, San Francisco, CA 94105',phone:'(415) 555-0200',hours:'Mon-Fri 9am-6pm',team:'32 employees'}},
  {id:'chi',name:'Chicago',x:60,y:30,category:'Office',description:'Midwest operations center.',details:{address:'233 S Wacker Dr, Chicago, IL 60606',phone:'(312) 555-0300',hours:'Mon-Fri 8am-5pm',team:'18 employees'}},
  {id:'atl',name:'Atlanta',x:70,y:55,category:'Partner',description:'Strategic partner location.',details:{address:'191 Peachtree St, Atlanta, GA 30303',phone:'(404) 555-0400'}},
  {id:'den',name:'Denver',x:32,y:35,category:'Partner',description:'Mountain region partner office.',details:{address:'1144 15th St, Denver, CO 80202',phone:'(303) 555-0500'}},
  {id:'mia',name:'Miami',x:75,y:72,category:'Sales',description:'Latin America sales office.',details:{address:'801 Brickell Ave, Miami, FL 33131',phone:'(305) 555-0600',hours:'Mon-Fri 9am-5pm'}},
];

const map = document.getElementById('map');
const list = document.getElementById('list');
const filtersEl = document.getElementById('filters');
const countEl = document.getElementById('count');
const detailPanel = document.getElementById('detailPanel');
const categories = ['All', ...new Set(LOCATIONS.map(l=>l.category))];
let activeFilter = 'All';

// Pin SVG template
const pinSVG = (color='#ef4444') => `<svg viewBox="0 0 24 36" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M12 0C5.37 0 0 5.37 0 12c0 9 12 24 12 24s12-15 12-24C24 5.37 18.63 0 12 0z" fill="${color}"/><circle cx="12" cy="12" r="5" fill="white"/></svg>`;

function render() {
  // Clear
  map.querySelectorAll('.pin').forEach(p=>p.remove());
  list.innerHTML = '';
  const filtered = activeFilter==='All' ? LOCATIONS : LOCATIONS.filter(l=>l.category===activeFilter);
  countEl.textContent = `${filtered.length} locations`;
  
  filtered.forEach(loc => {
    // Map pin
    const pin = document.createElement('div');
    pin.className = 'pin';
    pin.id = 'pin-'+loc.id;
    pin.style.left = loc.x+'%';
    pin.style.top = loc.y+'%';
    pin.innerHTML = pinSVG();
    pin.onclick = () => showDetail(loc);
    map.appendChild(pin);
    
    // List item
    const item = document.createElement('div');
    item.className = 'location-item';
    item.id = 'item-'+loc.id;
    item.innerHTML = `<div class="cat">${loc.category}</div><div class="name">${loc.name}</div><div class="desc">${loc.description}</div>`;
    item.onclick = () => showDetail(loc);
    list.appendChild(item);
  });
}

function showDetail(loc) {
  document.querySelectorAll('.pin').forEach(p=>p.classList.remove('active'));
  document.querySelectorAll('.location-item').forEach(i=>i.classList.remove('active'));
  const pin = document.getElementById('pin-'+loc.id);
  const item = document.getElementById('item-'+loc.id);
  if(pin) pin.classList.add('active');
  if(item) item.classList.add('active');
  
  let html = `<button class="back" onclick="hideDetail()">← Back to list</button>`;
  html += `<h3>${loc.name}</h3><div class="cat" style="font-size:0.7rem;color:var(--accent);text-transform:uppercase;letter-spacing:0.08em;margin-bottom:0.75rem">${loc.category}</div>`;
  html += `<p style="color:var(--muted);font-size:0.9rem;line-height:1.6;margin-bottom:1rem">${loc.description}</p>`;
  if(loc.details){
    html += '<div class="meta">';
    Object.entries(loc.details).forEach(([k,v]) => {
      html += `<div><strong style="color:var(--text)">${k.charAt(0).toUpperCase()+k.slice(1)}:</strong> ${v}</div>`;
    });
    html += '</div>';
  }
  detailPanel.innerHTML = html;
  list.style.display = 'none';
  detailPanel.classList.add('show');
}
function hideDetail(){list.style.display='block';detailPanel.classList.remove('show');}

// Filters
categories.forEach((c,i) => {
  const btn = document.createElement('button');
  btn.className = `filter-btn ${i===0?'active':''}`;
  btn.textContent = c;
  btn.onclick = () => {
    activeFilter = c;
    document.querySelectorAll('.filter-btn').forEach(b=>b.classList.remove('active'));
    btn.classList.add('active');
    hideDetail();
    render();
  };
  filtersEl.appendChild(btn);
});

render();
</script></body></html>''', {
    "id": "interactive-map", "name": "Interactive Map", "category": "spatial",
    "tags": ["map","locations","geography","pins","store-locator","territory","offices"],
    "compatible_verticals": ["store-locator","real-estate","territory-mapping","office-locations","event-venues","delivery-zones","travel-itinerary"],
    "core_libs": [], "complexity": "medium"
})


# ============================================================
# ENGINE 21: GUIDED TOUR / PRODUCT WALKTHROUGH  (Navattic killer)
# ============================================================
write_template("21-guided-tour", "template.html", '''<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- ENGINE_META: {"id":"guided-tour","name":"Guided Product Tour","category":"presentation","tags":["tour","walkthrough","demo","product","onboarding","step-by-step"],"compatible_verticals":["product-demo","software-onboarding","feature-tour","sales-demo","help-documentation","training-guide"]} -->
<!-- AI_USAGE: Step-by-step guided tour with hotspot highlights, tooltips, and branching paths.
     Modify STEPS array: {title, description, highlight{x,y,w,h} (% of viewport), position('top'|'bottom'|'left'|'right'), action_label}.
     Supports branching via 'branches' property: [{label,goto_step}]. Screenshot/mockup areas are placeholder divs. -->
<title>Product Tour</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
:root{--bg:#f9fafb;--card:#fff;--accent:#6366f1;--text:#111827;--muted:#6b7280;--overlay:rgba(0,0,0,0.5)}
*{margin:0;padding:0;box-sizing:border-box}
body{background:var(--bg);font-family:'Inter',sans-serif;color:var(--text);height:100vh;overflow:hidden}
.mockup-area{position:absolute;top:0;left:0;right:0;bottom:0;background:#e5e7eb;display:flex;align-items:center;justify-content:center}
.mockup-area .placeholder{color:var(--muted);font-size:0.9rem;text-align:center;padding:2rem}
/* AI_HINT: Replace mockup-area with actual screenshot or product UI representation */
.overlay-mask{position:fixed;top:0;left:0;right:0;bottom:0;z-index:50;pointer-events:none}
.highlight-ring{position:absolute;border:3px solid var(--accent);border-radius:8px;box-shadow:0 0 0 9999px var(--overlay);z-index:51;transition:all 0.5s ease;pointer-events:none}
.tooltip{position:absolute;z-index:52;background:var(--card);border-radius:12px;padding:1.5rem;max-width:340px;box-shadow:0 20px 40px rgba(0,0,0,0.2);pointer-events:all}
.tooltip h3{font-size:1rem;margin-bottom:0.5rem}
.tooltip p{font-size:0.85rem;color:var(--muted);line-height:1.6;margin-bottom:1rem}
.tooltip .step-indicator{font-size:0.7rem;color:var(--muted);text-transform:uppercase;letter-spacing:0.1em;margin-bottom:0.75rem}
.tooltip .actions{display:flex;gap:0.5rem;flex-wrap:wrap}
.tooltip .btn{padding:0.5rem 1rem;border-radius:8px;font-family:inherit;font-size:0.85rem;font-weight:500;cursor:pointer;transition:all 0.15s;border:none}
.tooltip .btn-primary{background:var(--accent);color:white}
.tooltip .btn-primary:hover{opacity:0.9}
.tooltip .btn-secondary{background:transparent;border:1px solid var(--muted);color:var(--muted)}
.tooltip .btn-secondary:hover{border-color:var(--accent);color:var(--accent)}
.tooltip .btn-branch{background:rgba(99,102,241,0.1);color:var(--accent);border:1px solid rgba(99,102,241,0.2)}
.progress-bar{position:fixed;bottom:0;left:0;height:4px;background:var(--accent);z-index:60;transition:width 0.4s}
</style></head><body>
<div class="mockup-area"><div class="placeholder">
  <!-- AI_HINT: This area represents the product UI. Replace with a screenshot, iframe, or custom HTML mockup. -->
  <div style="font-size:2rem;margin-bottom:1rem">🖥️</div>
  <div>Product Interface Area</div>
  <div style="font-size:0.8rem;margin-top:0.5rem">The guided tour highlights regions of this area</div>
</div></div>
<div class="overlay-mask"><div class="highlight-ring" id="ring"></div></div>
<div class="tooltip" id="tooltip"></div>
<div class="progress-bar" id="progress"></div>
<script>
// CUSTOMIZABLE: STEPS array - guided tour steps
// AI_HINT: highlight is {x,y,w,h} in viewport %. position is where tooltip appears relative to highlight.
// branches is optional [{label,goto_step(index)}] for non-linear tours.
const STEPS = [
  {
    title: "Welcome to the Dashboard",
    description: "This is your command center. Everything you need is accessible from here. Let's take a quick tour of the key features.",
    highlight: {x:0,y:0,w:100,h:8},
    position: "bottom",
    action_label: "Let's go →"
  },
  {
    title: "Navigation Menu",
    description: "Access all major sections from the sidebar. Your most-used items appear at the top based on your activity.",
    highlight: {x:0,y:8,w:18,h:92},
    position: "right",
    action_label: "Next"
  },
  {
    title: "What would you like to explore?",
    description: "We can show you different areas of the product based on your role.",
    highlight: {x:18,y:8,w:82,h:92},
    position: "left",
    branches: [
      {label: "📊 Analytics", goto_step: 3},
      {label: "👥 Team Management", goto_step: 4},
      {label: "⚙️ Settings", goto_step: 5}
    ]
  },
  {
    title: "Analytics Dashboard",
    description: "Real-time metrics, custom reports, and data exports. Everything updates live — no refresh needed.",
    highlight: {x:18,y:8,w:55,h:60},
    position: "right",
    action_label: "Continue"
  },
  {
    title: "Team Management",
    description: "Invite members, set roles and permissions, and manage your team's access from one place.",
    highlight: {x:18,y:8,w:55,h:60},
    position: "right",
    action_label: "Continue"
  },
  {
    title: "You're all set!",
    description: "That covers the basics. You can replay this tour anytime from the Help menu. Ready to dive in?",
    highlight: {x:20,y:20,w:60,h:60},
    position: "bottom",
    action_label: "Start Using →"
  }
];

let current = 0;
const ring = document.getElementById('ring');
const tooltip = document.getElementById('tooltip');
const progress = document.getElementById('progress');

function showStep(idx) {
  current = idx;
  if (current >= STEPS.length) { finish(); return; }
  const step = STEPS[current];
  const h = step.highlight;
  
  // Position highlight ring
  ring.style.left = h.x + '%';
  ring.style.top = h.y + '%';
  ring.style.width = h.w + '%';
  ring.style.height = h.h + '%';
  
  // Build tooltip
  let html = `<div class="step-indicator">Step ${current+1} of ${STEPS.length}</div>`;
  html += `<h3>${step.title}</h3><p>${step.description}</p><div class="actions">`;
  
  if (current > 0) html += `<button class="btn btn-secondary" onclick="showStep(${current-1})">← Back</button>`;
  
  if (step.branches) {
    step.branches.forEach(b => {
      html += `<button class="btn btn-branch" onclick="showStep(${b.goto_step})">${b.label}</button>`;
    });
  } else {
    html += `<button class="btn btn-primary" onclick="showStep(${current+1})">${step.action_label||'Next →'}</button>`;
  }
  html += '</div>';
  tooltip.innerHTML = html;
  
  // Position tooltip relative to highlight
  const pos = step.position || 'bottom';
  tooltip.style.top = pos==='bottom' ? `calc(${h.y+h.h}% + 16px)` : pos==='top' ? `calc(${h.y}% - 16px)` : `calc(${h.y + h.h/2}% - 80px)`;
  tooltip.style.left = pos==='right' ? `calc(${h.x+h.w}% + 16px)` : pos==='left' ? `calc(${h.x}% - 356px)` : `calc(${h.x + h.w/2}% - 170px)`;
  tooltip.style.transform = pos==='top' ? 'translateY(-100%)' : 'none';
  
  // Progress
  progress.style.width = ((current+1)/STEPS.length*100)+'%';
}

function finish() {
  ring.style.opacity = '0';
  tooltip.innerHTML = '<h3>🎉 Tour Complete!</h3><p style="color:var(--muted)">You\'re ready to go. Have questions? We\'re here to help.</p>';
  tooltip.style.top = '50%';
  tooltip.style.left = '50%';
  tooltip.style.transform = 'translate(-50%,-50%)';
  progress.style.width = '100%';
}

showStep(0);
</script></body></html>''', {
    "id": "guided-tour", "name": "Guided Product Tour", "category": "presentation",
    "tags": ["tour","walkthrough","demo","product","onboarding","step-by-step","interactive-demo"],
    "compatible_verticals": ["product-demo","software-onboarding","feature-tour","sales-demo","help-documentation","training-guide"],
    "core_libs": [], "complexity": "medium"
})


# ============================================================
# ENGINE 22: ACCORDION FAQ / KNOWLEDGE BASE
# ============================================================
write_template("22-accordion-faq", "template.html", '''<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- ENGINE_META: {"id":"accordion-faq","name":"Accordion FAQ / Knowledge Base","category":"tool","tags":["faq","accordion","knowledge-base","help","support","questions","policy"],"compatible_verticals":["customer-support","legal-policy","compliance","help-center","product-faq","employee-handbook","terms-of-service"]} -->
<!-- AI_USAGE: Searchable accordion FAQ with categories. Modify CATEGORIES array: {name, icon, items[{q,a}]}.
     Supports real-time search filtering. Accordion items expand/collapse with smooth animation. -->
<title>FAQ</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Nunito+Sans:wght@400;600;700&display=swap');
:root{--bg:#fff;--surface:#f9fafb;--border:#e5e7eb;--text:#111827;--muted:#6b7280;--accent:#2563eb}
*{margin:0;padding:0;box-sizing:border-box}
body{background:var(--bg);color:var(--text);font-family:'Nunito Sans',sans-serif;padding:3rem 2rem;min-height:100vh}
.container{max-width:760px;margin:0 auto}
h1{text-align:center;font-size:clamp(1.6rem,3vw,2.2rem);margin-bottom:0.5rem}
.subtitle{text-align:center;color:var(--muted);margin-bottom:2rem}
.search-wrap{margin-bottom:2rem}
.search-wrap input{width:100%;padding:0.85rem 1.25rem;border:1.5px solid var(--border);border-radius:12px;font-family:inherit;font-size:1rem;outline:none;transition:border-color 0.2s;background:var(--surface)}
.search-wrap input:focus{border-color:var(--accent)}
.search-wrap input::placeholder{color:#adb5bd}
.category{margin-bottom:2rem}
.category-title{display:flex;align-items:center;gap:0.5rem;font-size:0.85rem;font-weight:700;text-transform:uppercase;letter-spacing:0.08em;color:var(--accent);margin-bottom:0.75rem;padding-bottom:0.5rem;border-bottom:1px solid var(--border)}
.faq-item{border:1px solid var(--border);border-radius:10px;margin-bottom:0.5rem;overflow:hidden;transition:box-shadow 0.2s}
.faq-item:hover{box-shadow:0 2px 8px rgba(0,0,0,0.04)}
.faq-q{padding:1rem 1.25rem;cursor:pointer;display:flex;justify-content:space-between;align-items:center;font-weight:600;font-size:0.95rem;background:var(--bg);user-select:none}
.faq-q .arrow{transition:transform 0.3s;font-size:0.8rem;color:var(--muted)}
.faq-item.open .faq-q .arrow{transform:rotate(180deg)}
.faq-a{max-height:0;overflow:hidden;transition:max-height 0.4s ease,padding 0.3s;padding:0 1.25rem;color:var(--muted);font-size:0.9rem;line-height:1.8}
.faq-item.open .faq-a{max-height:500px;padding:0 1.25rem 1rem}
.no-results{text-align:center;color:var(--muted);padding:3rem;font-size:0.9rem}
.count{text-align:center;color:var(--muted);font-size:0.8rem;margin-bottom:1.5rem}
</style></head><body>
<div class="container">
  <h1><!-- CUSTOMIZABLE: faq_title -->Frequently Asked Questions<!-- /CUSTOMIZABLE --></h1>
  <p class="subtitle"><!-- CUSTOMIZABLE: faq_subtitle -->Find answers to common questions<!-- /CUSTOMIZABLE --></p>
  <div class="search-wrap"><input type="text" id="search" placeholder="Search questions..."></div>
  <div class="count" id="count"></div>
  <div id="content"></div>
</div>
<script>
// CUSTOMIZABLE: CATEGORIES with FAQ items
const CATEGORIES = [
  { name:"Getting Started", icon:"🚀", items:[
    {q:"How do I create an account?",a:"Click the Sign Up button on the homepage, enter your email and create a password. You'll receive a confirmation email within minutes."},
    {q:"Is there a free trial?",a:"Yes! We offer a 14-day free trial with full access to all features. No credit card required."},
    {q:"What browsers are supported?",a:"We support the latest versions of Chrome, Firefox, Safari, and Edge. Mobile browsers are fully supported on iOS and Android."}
  ]},
  { name:"Billing & Pricing", icon:"💳", items:[
    {q:"How much does it cost?",a:"Plans start at $29/month for individuals. Team plans are $49/user/month. Enterprise pricing is custom — contact our sales team."},
    {q:"Can I cancel anytime?",a:"Absolutely. There are no contracts or cancellation fees. You can cancel from your account settings at any time."},
    {q:"Do you offer refunds?",a:"We offer a full refund within the first 30 days if you're not satisfied. After 30 days, we pro-rate for the remaining period."}
  ]},
  { name:"Features & Usage", icon:"⚡", items:[
    {q:"Can I export my data?",a:"Yes, you can export all your data in CSV, JSON, or PDF format at any time from the Settings page."},
    {q:"Is there an API?",a:"Yes, we offer a full REST API with comprehensive documentation. API access is included in Team and Enterprise plans."},
    {q:"Do you integrate with other tools?",a:"We integrate with Slack, Zapier, HubSpot, Salesforce, Google Workspace, and 50+ other tools."}
  ]},
  { name:"Security & Privacy", icon:"🔒", items:[
    {q:"Is my data secure?",a:"We use AES-256 encryption at rest and TLS 1.3 in transit. Our infrastructure is SOC 2 Type II certified and GDPR compliant."},
    {q:"Where is my data stored?",a:"Data is stored in AWS data centers in the US (us-east-1) and EU (eu-west-1). Enterprise customers can choose their data region."}
  ]}
];

const content = document.getElementById('content');
const search = document.getElementById('search');
const countEl = document.getElementById('count');
let totalItems = 0;

function render(filter='') {
  content.innerHTML = '';
  let visibleCount = 0;
  const f = filter.toLowerCase();
  
  CATEGORIES.forEach(cat => {
    const filtered = cat.items.filter(item => !f || item.q.toLowerCase().includes(f) || item.a.toLowerCase().includes(f));
    if (filtered.length === 0) return;
    
    const div = document.createElement('div');
    div.className = 'category';
    div.innerHTML = `<div class="category-title">${cat.icon} ${cat.name}</div>`;
    
    filtered.forEach(item => {
      visibleCount++;
      const faq = document.createElement('div');
      faq.className = 'faq-item';
      faq.innerHTML = `<div class="faq-q">${item.q}<span class="arrow">▼</span></div><div class="faq-a">${item.a}</div>`;
      faq.querySelector('.faq-q').onclick = () => faq.classList.toggle('open');
      div.appendChild(faq);
    });
    content.appendChild(div);
  });
  
  if (visibleCount === 0) content.innerHTML = '<div class="no-results">No questions match your search. Try different keywords.</div>';
  countEl.textContent = `${visibleCount} question${visibleCount!==1?'s':''}`;
}

search.addEventListener('input', e => render(e.target.value));
render();
</script></body></html>''', {
    "id": "accordion-faq", "name": "Accordion FAQ / Knowledge Base", "category": "tool",
    "tags": ["faq","accordion","knowledge-base","help","support","questions","policy"],
    "compatible_verticals": ["customer-support","legal-policy","compliance","help-center","product-faq","employee-handbook"],
    "core_libs": [], "complexity": "low"
})


# ============================================================
# ENGINE 23: IMAGE GALLERY / PORTFOLIO
# ============================================================
write_template("23-image-gallery", "template.html", '''<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- ENGINE_META: {"id":"image-gallery","name":"Image Gallery / Portfolio","category":"visual","tags":["gallery","portfolio","photos","lightbox","showcase","grid","masonry"],"compatible_verticals":["photography","real-estate-photos","product-showcase","renovation-portfolio","art-gallery","design-portfolio","event-photos"]} -->
<!-- AI_USAGE: Filterable image gallery with lightbox zoom. Modify IMAGES array: {src(placeholder color),title,category,description}.
     For production: replace color placeholders with actual image URLs via background-image.
     Supports category filtering and lightbox with prev/next navigation. -->
<title>Gallery</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;600&family=Source+Sans+3:wght@400;600&display=swap');
:root{--bg:#111;--card:#1a1a1a;--border:#333;--text:#f4f4f5;--muted:#a1a1aa;--accent:#f59e0b}
*{margin:0;padding:0;box-sizing:border-box}
body{background:var(--bg);color:var(--text);font-family:'Source Sans 3',sans-serif;padding:2rem;min-height:100vh}
h1{font-family:'Playfair Display',serif;font-size:clamp(1.8rem,4vw,2.5rem);text-align:center;margin-bottom:0.5rem;font-weight:400}
.subtitle{text-align:center;color:var(--muted);margin-bottom:2rem;font-size:0.95rem}
.filters{display:flex;justify-content:center;gap:0.5rem;margin-bottom:2rem;flex-wrap:wrap}
.filter-btn{padding:0.4rem 1.2rem;background:transparent;border:1px solid var(--border);color:var(--muted);border-radius:20px;cursor:pointer;font-family:inherit;font-size:0.8rem;transition:all 0.2s}
.filter-btn.active,.filter-btn:hover{border-color:var(--accent);color:var(--accent)}
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:0.75rem;max-width:1200px;margin:0 auto}
.grid-item{position:relative;aspect-ratio:4/3;border-radius:8px;overflow:hidden;cursor:pointer;transition:transform 0.3s}
.grid-item:hover{transform:scale(1.02)}
.grid-item .thumb{width:100%;height:100%;display:flex;align-items:center;justify-content:center;font-size:3rem}
.grid-item .info{position:absolute;bottom:0;left:0;right:0;padding:1rem;background:linear-gradient(transparent,rgba(0,0,0,0.8));opacity:0;transition:opacity 0.3s}
.grid-item:hover .info{opacity:1}
.grid-item .info .title{font-family:'Playfair Display',serif;font-size:1rem}
.grid-item .info .cat{font-size:0.75rem;color:var(--accent);margin-top:0.25rem}
.grid-item.hidden{display:none}
/* Lightbox */
.lightbox{display:none;position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.95);z-index:100;align-items:center;justify-content:center;flex-direction:column}
.lightbox.show{display:flex}
.lightbox .lb-image{width:80vw;max-height:70vh;display:flex;align-items:center;justify-content:center;font-size:6rem;border-radius:12px;margin-bottom:1.5rem}
.lightbox .lb-title{font-family:'Playfair Display',serif;font-size:1.4rem;margin-bottom:0.5rem}
.lightbox .lb-desc{color:var(--muted);font-size:0.9rem;max-width:600px;text-align:center;line-height:1.6}
.lightbox .lb-close{position:absolute;top:1.5rem;right:2rem;background:none;border:none;color:white;font-size:2rem;cursor:pointer}
.lightbox .lb-nav{position:absolute;top:50%;transform:translateY(-50%);background:rgba(255,255,255,0.1);border:none;color:white;font-size:1.5rem;padding:1rem;cursor:pointer;border-radius:50%;transition:background 0.2s}
.lightbox .lb-nav:hover{background:rgba(255,255,255,0.2)}
.lightbox .lb-prev{left:2rem}
.lightbox .lb-next{right:2rem}
.lightbox .lb-counter{position:absolute;bottom:2rem;color:var(--muted);font-size:0.8rem}
</style></head><body>
<h1><!-- CUSTOMIZABLE: gallery_title -->Portfolio<!-- /CUSTOMIZABLE --></h1>
<p class="subtitle"><!-- CUSTOMIZABLE: gallery_subtitle -->A selection of our finest work<!-- /CUSTOMIZABLE --></p>
<div class="filters" id="filters"></div>
<div class="grid" id="grid"></div>
<div class="lightbox" id="lightbox">
  <button class="lb-close" onclick="closeLB()">&times;</button>
  <button class="lb-nav lb-prev" onclick="navLB(-1)">‹</button>
  <button class="lb-nav lb-next" onclick="navLB(1)">›</button>
  <div class="lb-image" id="lbImage"></div>
  <div class="lb-title" id="lbTitle"></div>
  <div class="lb-desc" id="lbDesc"></div>
  <div class="lb-counter" id="lbCounter"></div>
</div>
<script>
// CUSTOMIZABLE: IMAGES array
// AI_HINT: src is a placeholder (CSS color/gradient). For production, use background-image:url(...)
const IMAGES = [
  {src:'linear-gradient(135deg,#667eea,#764ba2)',title:'Mountain Retreat',category:'Landscape',description:'Captured at dawn in the Swiss Alps. The interplay of light and shadow creates a dramatic composition.'},
  {src:'linear-gradient(135deg,#f093fb,#f5576c)',title:'Urban Geometry',category:'Architecture',description:'Modern architecture in downtown Tokyo. Clean lines and reflective surfaces define the cityscape.'},
  {src:'linear-gradient(135deg,#4facfe,#00f2fe)',title:'Ocean Serenity',category:'Landscape',description:'The Pacific coast at sunset. Long exposure smooths the waves into silk.'},
  {src:'linear-gradient(135deg,#43e97b,#38f9d7)',title:'Glass Tower',category:'Architecture',description:'A study in transparency and reflection. Shot from below to emphasize the soaring height.'},
  {src:'linear-gradient(135deg,#fa709a,#fee140)',title:'Golden Hour',category:'Portrait',description:'Natural light portrait during the magic hour. Warm tones and soft shadows.'},
  {src:'linear-gradient(135deg,#a18cd1,#fbc2eb)',title:'Lavender Fields',category:'Landscape',description:'Endless rows of lavender stretching to the horizon in Provence, France.'},
  {src:'linear-gradient(135deg,#fccb90,#d57eeb)',title:'Concrete Jungle',category:'Architecture',description:'Brutalist architecture meets modern design in this Berlin landmark.'},
  {src:'linear-gradient(135deg,#e0c3fc,#8ec5fc)',title:'Studio Light',category:'Portrait',description:'Controlled studio lighting creates dramatic contrast and mood.'},
  {src:'linear-gradient(135deg,#f5576c,#ff6b6b)',title:'Desert Bloom',category:'Landscape',description:'Rare desert wildflowers after spring rains in the Mojave.'}
];

const grid=document.getElementById('grid');const filtersEl=document.getElementById('filters');
const lightbox=document.getElementById('lightbox');
const categories=['All',...new Set(IMAGES.map(i=>i.category))];
let filteredImages=[...IMAGES];let lbIdx=0;

categories.forEach((c,i)=>{
  const btn=document.createElement('button');btn.className=`filter-btn ${i===0?'active':''}`;btn.textContent=c;
  btn.onclick=()=>{
    document.querySelectorAll('.filter-btn').forEach(b=>b.classList.remove('active'));btn.classList.add('active');
    filteredImages=c==='All'?[...IMAGES]:IMAGES.filter(img=>img.category===c);renderGrid();
  };filtersEl.appendChild(btn);
});

function renderGrid(){
  grid.innerHTML='';
  filteredImages.forEach((img,i)=>{
    const item=document.createElement('div');item.className='grid-item';
    item.innerHTML=`<div class="thumb" style="background:${img.src}"></div><div class="info"><div class="title">${img.title}</div><div class="cat">${img.category}</div></div>`;
    item.onclick=()=>openLB(i);grid.appendChild(item);
  });
}

function openLB(idx){lbIdx=idx;updateLB();lightbox.classList.add('show');}
function closeLB(){lightbox.classList.remove('show');}
function navLB(dir){lbIdx=(lbIdx+dir+filteredImages.length)%filteredImages.length;updateLB();}
function updateLB(){
  const img=filteredImages[lbIdx];
  document.getElementById('lbImage').style.background=img.src;
  document.getElementById('lbImage').style.aspectRatio='16/10';
  document.getElementById('lbTitle').textContent=img.title;
  document.getElementById('lbDesc').textContent=img.description;
  document.getElementById('lbCounter').textContent=`${lbIdx+1} / ${filteredImages.length}`;
}
document.addEventListener('keydown',e=>{if(e.key==='Escape')closeLB();if(e.key==='ArrowRight')navLB(1);if(e.key==='ArrowLeft')navLB(-1);});
renderGrid();
</script></body></html>''', {
    "id": "image-gallery", "name": "Image Gallery / Portfolio", "category": "visual",
    "tags": ["gallery","portfolio","photos","lightbox","showcase","grid"],
    "compatible_verticals": ["photography","real-estate-photos","product-showcase","renovation-portfolio","art-gallery","design-portfolio"],
    "core_libs": [], "complexity": "low"
})


# ============================================================
# ENGINE 24: EVENT / COUNTDOWN PAGE
# ============================================================
write_template("24-event-countdown", "template.html", '''<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- ENGINE_META: {"id":"event-countdown","name":"Event / Countdown Page","category":"engagement","tags":["event","countdown","launch","wedding","conference","invitation","timer"],"compatible_verticals":["wedding-invitation","product-launch","conference","fundraiser-gala","grand-opening","webinar","sale-promotion"]} -->
<!-- AI_USAGE: Event page with countdown timer, schedule, and RSVP form. 
     Modify EVENT config: {name,tagline,date(ISO),location,schedule[{time,title,description}]}.
     Countdown updates every second. RSVP form collects name/email/attendance. -->
<title>Event</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Cormorant+Garamond:wght@300;400;600&family=Manrope:wght@400;500;600&display=swap');
:root{--bg:#0a0a0a;--surface:#141414;--border:#262626;--text:#fafafa;--muted:#a1a1aa;--accent:#d4af37}
*{margin:0;padding:0;box-sizing:border-box}
body{background:var(--bg);color:var(--text);font-family:'Manrope',sans-serif;min-height:100vh}
.hero{height:100vh;display:flex;flex-direction:column;justify-content:center;align-items:center;text-align:center;padding:2rem;position:relative}
.hero::after{content:'';position:absolute;bottom:0;left:0;right:0;height:200px;background:linear-gradient(transparent,var(--bg))}
.hero .eyebrow{font-size:0.75rem;text-transform:uppercase;letter-spacing:0.3em;color:var(--accent);margin-bottom:1.5rem}
.hero h1{font-family:'Cormorant Garamond',serif;font-size:clamp(2.5rem,7vw,5rem);font-weight:300;line-height:1.1;margin-bottom:1rem}
.hero .tagline{font-size:clamp(1rem,2vw,1.2rem);color:var(--muted);margin-bottom:3rem;max-width:500px}
.countdown{display:flex;gap:1.5rem;margin-bottom:2rem}
.cd-unit{text-align:center}
.cd-unit .num{font-family:'Cormorant Garamond',serif;font-size:clamp(2.5rem,5vw,4rem);font-weight:300;color:var(--accent);line-height:1}
.cd-unit .lbl{font-size:0.65rem;text-transform:uppercase;letter-spacing:0.2em;color:var(--muted);margin-top:0.5rem}
.details{padding:4rem 2rem;max-width:700px;margin:0 auto;text-align:center}
.details .location{font-family:'Cormorant Garamond',serif;font-size:1.5rem;margin-bottom:0.5rem;color:var(--accent)}
.details .date-str{color:var(--muted);margin-bottom:3rem}
.schedule{text-align:left;margin:3rem 0}
.schedule h2{font-family:'Cormorant Garamond',serif;font-size:1.8rem;font-weight:300;text-align:center;margin-bottom:2rem}
.sched-item{display:flex;gap:1.5rem;padding:1.25rem 0;border-bottom:1px solid var(--border)}
.sched-item .time{min-width:80px;font-size:0.85rem;color:var(--accent);font-weight:600}
.sched-item .info .stitle{font-weight:600;margin-bottom:0.25rem}
.sched-item .info .sdesc{font-size:0.85rem;color:var(--muted)}
.rsvp{padding:4rem 2rem;max-width:480px;margin:0 auto;text-align:center}
.rsvp h2{font-family:'Cormorant Garamond',serif;font-size:1.8rem;font-weight:300;margin-bottom:2rem}
.rsvp input,.rsvp select{width:100%;padding:0.85rem 1rem;background:var(--surface);border:1px solid var(--border);border-radius:8px;color:var(--text);font-family:inherit;font-size:1rem;margin-bottom:1rem;outline:none}
.rsvp input:focus,.rsvp select:focus{border-color:var(--accent)}
.rsvp button{width:100%;padding:0.85rem;background:var(--accent);color:var(--bg);border:none;border-radius:8px;font-family:inherit;font-size:1rem;font-weight:600;cursor:pointer;transition:opacity 0.2s}
.rsvp button:hover{opacity:0.9}
.rsvp .success{display:none;color:var(--accent);font-family:'Cormorant Garamond',serif;font-size:1.3rem}
</style></head><body>
<div class="hero">
  <div class="eyebrow" id="eyebrow"></div>
  <h1 id="eventName"></h1>
  <div class="tagline" id="tagline"></div>
  <div class="countdown" id="countdown"></div>
</div>
<div class="details">
  <div class="location" id="location"></div>
  <div class="date-str" id="dateStr"></div>
  <div class="schedule" id="schedule"></div>
</div>
<div class="rsvp">
  <h2>RSVP</h2>
  <div id="rsvpForm">
    <input type="text" placeholder="Your Name" id="rName" required>
    <input type="email" placeholder="Email Address" id="rEmail" required>
    <select id="rAttend"><option value="">Will you attend?</option><option value="yes">Yes, I'll be there!</option><option value="maybe">Maybe</option><option value="no">Unfortunately, no</option></select>
    <button onclick="submitRSVP()">Send RSVP</button>
  </div>
  <div class="success" id="success">Thank you! We'll see you there. ✨</div>
</div>
<script>
// CUSTOMIZABLE: EVENT configuration
const EVENT = {
  name: "The Annual Gala",
  eyebrow: "You're Invited",
  tagline: "An evening of celebration, connection, and looking ahead to what's next.",
  date: "2026-06-15T18:00:00",
  location: "The Grand Ballroom, New York",
  schedule: [
    {time:"6:00 PM",title:"Cocktail Reception",description:"Welcome drinks and networking on the terrace"},
    {time:"7:00 PM",title:"Dinner & Keynote",description:"Three-course dinner with our annual address"},
    {time:"8:30 PM",title:"Awards Ceremony",description:"Celebrating outstanding achievements"},
    {time:"9:30 PM",title:"Live Entertainment",description:"Dancing and music until midnight"}
  ]
};

document.getElementById('eyebrow').textContent = EVENT.eyebrow;
document.getElementById('eventName').textContent = EVENT.name;
document.getElementById('tagline').textContent = EVENT.tagline;
document.getElementById('location').textContent = EVENT.location;
document.getElementById('dateStr').textContent = new Date(EVENT.date).toLocaleDateString('en-US',{weekday:'long',year:'numeric',month:'long',day:'numeric'});

// Schedule
const schedEl = document.getElementById('schedule');
schedEl.innerHTML = '<h2>Schedule</h2>' + EVENT.schedule.map(s=>`<div class="sched-item"><div class="time">${s.time}</div><div class="info"><div class="stitle">${s.title}</div><div class="sdesc">${s.description}</div></div></div>`).join('');

// Countdown
const cdEl = document.getElementById('countdown');
function updateCountdown(){
  const diff = new Date(EVENT.date)-new Date();
  if(diff<=0){cdEl.innerHTML='<div style="color:var(--accent);font-family:Cormorant Garamond,serif;font-size:2rem">The event has begun!</div>';return;}
  const d=Math.floor(diff/86400000),h=Math.floor((diff%86400000)/3600000),m=Math.floor((diff%3600000)/60000),s=Math.floor((diff%60000)/1000);
  cdEl.innerHTML=[{n:d,l:'Days'},{n:h,l:'Hours'},{n:m,l:'Minutes'},{n:s,l:'Seconds'}].map(u=>`<div class="cd-unit"><div class="num">${String(u.n).padStart(2,'0')}</div><div class="lbl">${u.l}</div></div>`).join('');
}
updateCountdown();setInterval(updateCountdown,1000);

function submitRSVP(){
  document.getElementById('rsvpForm').style.display='none';
  document.getElementById('success').style.display='block';
}
</script></body></html>''', {
    "id": "event-countdown", "name": "Event / Countdown Page", "category": "engagement",
    "tags": ["event","countdown","launch","wedding","conference","invitation","timer","RSVP"],
    "compatible_verticals": ["wedding-invitation","product-launch","conference","fundraiser-gala","grand-opening","webinar"],
    "core_libs": [], "complexity": "low"
})


# ============================================================
# ENGINE 25: E-SIGN / CONTRACT ACCEPTANCE
# ============================================================
write_template("25-esign-contract", "template.html", '''<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- ENGINE_META: {"id":"esign-contract","name":"E-Sign / Contract Acceptance","category":"tool","tags":["esign","signature","contract","agreement","acceptance","legal","terms"],"compatible_verticals":["contract-signing","proposal-acceptance","terms-agreement","NDA","partnership-agreement","freelance-contract","consent-form"]} -->
<!-- AI_USAGE: Document with scrollable terms, checkbox acknowledgments, and signature capture pad.
     Modify DOCUMENT: {title, parties[], date, sections[{heading,content}], checkboxes[]}.
     Canvas-based signature pad captures drawn signature. Generates timestamp on sign. -->
<title>Agreement</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Crimson+Pro:wght@400;500;600&family=IBM+Plex+Sans:wght@400;500;600&display=swap');
:root{--bg:#fafafa;--card:#fff;--border:#e2e8f0;--text:#1e293b;--muted:#64748b;--accent:#1e40af;--green:#16a34a}
*{margin:0;padding:0;box-sizing:border-box}
body{background:var(--bg);color:var(--text);font-family:'IBM Plex Sans',sans-serif;padding:2rem;min-height:100vh}
.doc-container{max-width:760px;margin:0 auto}
.doc-header{text-align:center;padding:2rem 0;border-bottom:2px solid var(--text);margin-bottom:2rem}
.doc-header h1{font-family:'Crimson Pro',serif;font-size:1.8rem;margin-bottom:0.5rem}
.doc-header .parties{font-size:0.9rem;color:var(--muted)}
.doc-header .date{font-size:0.85rem;color:var(--muted);margin-top:0.5rem}
.section{margin-bottom:2rem}
.section h2{font-family:'Crimson Pro',serif;font-size:1.2rem;margin-bottom:0.75rem;color:var(--accent)}
.section p{font-size:0.9rem;line-height:1.8;color:var(--text)}
.checkboxes{background:var(--card);border:1px solid var(--border);border-radius:12px;padding:1.5rem;margin:2rem 0}
.checkboxes h3{font-size:0.9rem;font-weight:600;margin-bottom:1rem}
.cb-item{display:flex;align-items:flex-start;gap:0.75rem;padding:0.75rem 0;border-bottom:1px solid var(--border)}
.cb-item:last-child{border:none}
.cb-item input[type=checkbox]{margin-top:0.2rem;width:18px;height:18px;accent-color:var(--accent)}
.cb-item label{font-size:0.85rem;line-height:1.5;cursor:pointer}
.sig-section{background:var(--card);border:1px solid var(--border);border-radius:12px;padding:1.5rem;margin:2rem 0}
.sig-section h3{font-size:0.9rem;font-weight:600;margin-bottom:1rem}
.sig-pad-wrap{position:relative}
canvas#sigPad{width:100%;height:150px;border:1.5px dashed var(--border);border-radius:8px;cursor:crosshair;display:block}
.sig-actions{display:flex;gap:0.5rem;margin-top:0.75rem}
.sig-actions button{padding:0.4rem 1rem;font-family:inherit;font-size:0.8rem;border-radius:6px;cursor:pointer;border:1px solid var(--border);background:var(--bg);color:var(--muted);transition:all 0.2s}
.sig-actions button:hover{border-color:var(--accent);color:var(--accent)}
.sign-btn{width:100%;padding:1rem;background:var(--accent);color:white;border:none;border-radius:10px;font-family:inherit;font-size:1rem;font-weight:600;cursor:pointer;transition:all 0.2s;margin-top:1.5rem;opacity:0.4;pointer-events:none}
.sign-btn.ready{opacity:1;pointer-events:all}
.sign-btn.ready:hover{background:#1e3a8a}
.signed{display:none;text-align:center;padding:3rem;background:var(--card);border-radius:12px;border:2px solid var(--green);margin-top:2rem}
.signed .check{font-size:3rem;margin-bottom:1rem}
.signed h3{color:var(--green);margin-bottom:0.5rem}
.signed .timestamp{font-size:0.8rem;color:var(--muted);margin-top:1rem;font-family:monospace}
</style></head><body>
<div class="doc-container">
  <div class="doc-header">
    <h1 id="docTitle"></h1>
    <div class="parties" id="parties"></div>
    <div class="date" id="date"></div>
  </div>
  <div id="sections"></div>
  <div class="checkboxes" id="checkboxes"><h3>Acknowledgments</h3></div>
  <div class="sig-section" id="sigSection">
    <h3>Signature</h3>
    <div class="sig-pad-wrap"><canvas id="sigPad"></canvas></div>
    <div class="sig-actions"><button onclick="clearSig()">Clear</button></div>
  </div>
  <button class="sign-btn" id="signBtn" onclick="signDocument()">Sign & Accept Agreement</button>
  <div class="signed" id="signed">
    <div class="check">✅</div>
    <h3>Agreement Signed</h3>
    <p style="color:var(--muted)">This document has been signed and accepted.</p>
    <div class="timestamp" id="timestamp"></div>
  </div>
</div>
<script>
// CUSTOMIZABLE: DOCUMENT configuration
const DOCUMENT = {
  title: "Partnership Agreement",
  parties: ["IterFact, Inc.", "Partner Company, LLC"],
  date: "March 15, 2026",
  sections: [
    {heading:"1. Purpose",content:"This Agreement establishes the terms and conditions under which the parties agree to collaborate on the development and distribution of interactive document technology."},
    {heading:"2. Term",content:"This Agreement shall commence on the date of execution and continue for a period of twelve (12) months, unless earlier terminated by either party with thirty (30) days written notice."},
    {heading:"3. Responsibilities",content:"Each party shall designate a primary contact for all communications related to this Agreement. Both parties agree to act in good faith and maintain confidentiality of all shared information."},
    {heading:"4. Compensation",content:"Revenue sharing shall be calculated on a quarterly basis according to the schedule outlined in Exhibit A. Payments shall be made within thirty (30) days of quarter end."}
  ],
  checkboxes: [
    "I have read and understood all terms of this agreement",
    "I am authorized to sign on behalf of my organization",
    "I agree to the confidentiality provisions outlined in Section 3",
    "I acknowledge the compensation structure in Section 4"
  ]
};

// Render document
document.getElementById('docTitle').textContent = DOCUMENT.title;
document.getElementById('parties').textContent = `Between: ${DOCUMENT.parties.join(' and ')}`;
document.getElementById('date').textContent = `Effective Date: ${DOCUMENT.date}`;

const sectionsEl = document.getElementById('sections');
DOCUMENT.sections.forEach(s => {
  const div = document.createElement('div');
  div.className = 'section';
  div.innerHTML = `<h2>${s.heading}</h2><p>${s.content}</p>`;
  sectionsEl.appendChild(div);
});

const cbContainer = document.getElementById('checkboxes');
DOCUMENT.checkboxes.forEach((text, i) => {
  const item = document.createElement('div');
  item.className = 'cb-item';
  item.innerHTML = `<input type="checkbox" id="cb${i}" onchange="checkReady()"><label for="cb${i}">${text}</label>`;
  cbContainer.appendChild(item);
});

// Signature pad
const canvas = document.getElementById('sigPad');
const ctx = canvas.getContext('2d');
let drawing = false, hasSig = false;
canvas.width = canvas.offsetWidth * 2;
canvas.height = 300;
ctx.scale(2, 2);
ctx.strokeStyle = '#1e293b';
ctx.lineWidth = 2;
ctx.lineCap = 'round';

canvas.addEventListener('mousedown', e => { drawing = true; ctx.beginPath(); ctx.moveTo(e.offsetX, e.offsetY); });
canvas.addEventListener('mousemove', e => { if (!drawing) return; ctx.lineTo(e.offsetX, e.offsetY); ctx.stroke(); hasSig = true; checkReady(); });
canvas.addEventListener('mouseup', () => drawing = false);
canvas.addEventListener('mouseleave', () => drawing = false);
// Touch
canvas.addEventListener('touchstart', e => { e.preventDefault(); const t = e.touches[0]; const r = canvas.getBoundingClientRect(); drawing = true; ctx.beginPath(); ctx.moveTo(t.clientX - r.left, t.clientY - r.top); });
canvas.addEventListener('touchmove', e => { e.preventDefault(); if (!drawing) return; const t = e.touches[0]; const r = canvas.getBoundingClientRect(); ctx.lineTo(t.clientX - r.left, t.clientY - r.top); ctx.stroke(); hasSig = true; checkReady(); });
canvas.addEventListener('touchend', () => drawing = false);

function clearSig() { ctx.clearRect(0, 0, canvas.width, canvas.height); hasSig = false; checkReady(); }

function checkReady() {
  const allChecked = [...document.querySelectorAll('.cb-item input')].every(cb => cb.checked);
  document.getElementById('signBtn').classList.toggle('ready', allChecked && hasSig);
}

function signDocument() {
  const now = new Date();
  document.getElementById('sigSection').style.display = 'none';
  document.getElementById('checkboxes').style.opacity = '0.5';
  document.getElementById('checkboxes').style.pointerEvents = 'none';
  document.getElementById('signBtn').style.display = 'none';
  document.getElementById('signed').style.display = 'block';
  document.getElementById('timestamp').textContent = `Signed: ${now.toISOString()} | IP: [client-side only] | Hash: ${Math.random().toString(36).substr(2,12)}`;
}
</script></body></html>''', {
    "id": "esign-contract", "name": "E-Sign / Contract Acceptance", "category": "tool",
    "tags": ["esign","signature","contract","agreement","acceptance","legal","terms","ESIGN-Act"],
    "compatible_verticals": ["contract-signing","proposal-acceptance","terms-agreement","NDA","partnership-agreement","freelance-contract"],
    "core_libs": [], "complexity": "medium"
})


# ============================================================
# ENGINE 26: DATA TABLE EXPLORER
# ============================================================
write_template("26-data-table", "template.html", '''<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- ENGINE_META: {"id":"data-table","name":"Data Table Explorer","category":"data","tags":["table","data","sortable","filterable","search","spreadsheet","directory"],"compatible_verticals":["product-catalog","employee-directory","inventory-list","financial-data","vendor-list","leaderboard","comparison-database"]} -->
<!-- AI_USAGE: Interactive data table with sorting, searching, filtering, and pagination.
     Modify COLUMNS: [{key,label,type('text'|'number'|'currency'|'tag'),sortable,filterable}].
     Modify DATA: array of row objects matching column keys. Supports 1000+ rows efficiently. -->
<title>Data Explorer</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500&family=Outfit:wght@400;500;600&display=swap');
:root{--bg:#f8fafc;--card:#fff;--border:#e2e8f0;--text:#0f172a;--muted:#64748b;--accent:#6366f1;--hover:#f1f5f9;--green:#22c55e;--red:#ef4444}
*{margin:0;padding:0;box-sizing:border-box}
body{background:var(--bg);color:var(--text);font-family:'Outfit',sans-serif;padding:2rem;min-height:100vh}
.container{max-width:1100px;margin:0 auto}
h1{font-size:1.4rem;font-weight:600;margin-bottom:0.25rem}
.subtitle{color:var(--muted);font-size:0.85rem;margin-bottom:1.5rem}
.controls{display:flex;gap:1rem;margin-bottom:1rem;flex-wrap:wrap;align-items:center}
.controls input{flex:1;min-width:200px;padding:0.6rem 1rem;border:1px solid var(--border);border-radius:8px;font-family:inherit;font-size:0.85rem;outline:none;background:var(--card)}
.controls input:focus{border-color:var(--accent)}
.controls .count{font-size:0.8rem;color:var(--muted);white-space:nowrap}
.table-wrap{overflow-x:auto;background:var(--card);border:1px solid var(--border);border-radius:12px}
table{width:100%;border-collapse:collapse;font-size:0.85rem}
thead th{padding:0.75rem 1rem;text-align:left;font-weight:600;font-size:0.75rem;text-transform:uppercase;letter-spacing:0.05em;color:var(--muted);border-bottom:2px solid var(--border);cursor:pointer;user-select:none;white-space:nowrap}
thead th:hover{color:var(--accent)}
thead th .sort-arrow{margin-left:0.25rem;opacity:0.3}
thead th.sorted .sort-arrow{opacity:1;color:var(--accent)}
tbody td{padding:0.65rem 1rem;border-bottom:1px solid var(--border)}
tbody tr:hover{background:var(--hover)}
tbody tr:last-child td{border:none}
.tag{display:inline-block;padding:0.15rem 0.5rem;border-radius:10px;font-size:0.7rem;font-weight:500}
.tag-green{background:rgba(34,197,94,0.1);color:var(--green)}
.tag-red{background:rgba(239,68,68,0.1);color:var(--red)}
.tag-blue{background:rgba(99,102,241,0.1);color:var(--accent)}
.tag-gray{background:rgba(100,116,139,0.1);color:var(--muted)}
.currency{font-family:'JetBrains Mono',monospace;font-size:0.8rem}
.pagination{display:flex;justify-content:center;gap:0.25rem;margin-top:1rem}
.pagination button{padding:0.4rem 0.75rem;border:1px solid var(--border);background:var(--card);border-radius:6px;cursor:pointer;font-family:inherit;font-size:0.8rem;transition:all 0.15s}
.pagination button.active{background:var(--accent);color:white;border-color:var(--accent)}
.pagination button:hover:not(.active){border-color:var(--accent);color:var(--accent)}
</style></head><body>
<div class="container">
  <h1><!-- CUSTOMIZABLE: table_title -->Data Explorer<!-- /CUSTOMIZABLE --></h1>
  <p class="subtitle"><!-- CUSTOMIZABLE: table_subtitle -->Click headers to sort. Use search to filter.<!-- /CUSTOMIZABLE --></p>
  <div class="controls"><input type="text" id="search" placeholder="Search..."><span class="count" id="count"></span></div>
  <div class="table-wrap"><table><thead id="thead"></thead><tbody id="tbody"></tbody></table></div>
  <div class="pagination" id="pagination"></div>
</div>
<script>
// CUSTOMIZABLE: COLUMNS definition
const COLUMNS = [
  {key:'name',label:'Name',type:'text',sortable:true},
  {key:'role',label:'Role',type:'text',sortable:true},
  {key:'department',label:'Department',type:'tag',sortable:true},
  {key:'revenue',label:'Revenue',type:'currency',sortable:true},
  {key:'status',label:'Status',type:'tag',sortable:true},
  {key:'score',label:'Score',type:'number',sortable:true}
];

// CUSTOMIZABLE: DATA rows
const DATA = [
  {name:'Alice Johnson',role:'VP Sales',department:'Sales',revenue:1240000,status:'Active',score:94},
  {name:'Bob Smith',role:'Engineer',department:'Engineering',revenue:0,status:'Active',score:87},
  {name:'Carol Williams',role:'Designer',department:'Design',revenue:0,status:'Active',score:91},
  {name:'David Brown',role:'Account Exec',department:'Sales',revenue:890000,status:'Active',score:82},
  {name:'Eva Martinez',role:'PM',department:'Product',revenue:0,status:'On Leave',score:88},
  {name:'Frank Lee',role:'Engineer',department:'Engineering',revenue:0,status:'Active',score:95},
  {name:'Grace Chen',role:'Marketing Lead',department:'Marketing',revenue:450000,status:'Active',score:79},
  {name:'Henry Wilson',role:'Account Exec',department:'Sales',revenue:670000,status:'Active',score:85},
  {name:'Iris Patel',role:'Data Analyst',department:'Engineering',revenue:0,status:'Active',score:92},
  {name:'Jack Thompson',role:'Designer',department:'Design',revenue:0,status:'Inactive',score:76},
  {name:'Karen Davis',role:'HR Manager',department:'Operations',revenue:0,status:'Active',score:88},
  {name:'Liam Garcia',role:'SDR',department:'Sales',revenue:320000,status:'Active',score:81}
];

const PAGE_SIZE = 10;
let sortKey = null, sortDir = 1, searchTerm = '', currentPage = 0;

const tagColors = {Sales:'blue',Engineering:'green',Design:'blue',Product:'gray',Marketing:'blue',Operations:'gray',Active:'green',Inactive:'red','On Leave':'gray'};

function formatCell(val, type) {
  if (type==='currency') return val ? `<span class="currency">$${val.toLocaleString()}</span>` : '<span style="color:var(--muted)">—</span>';
  if (type==='tag') return `<span class="tag tag-${tagColors[val]||'gray'}">${val}</span>`;
  return val;
}

function getFiltered() {
  let rows = [...DATA];
  if (searchTerm) {
    const s = searchTerm.toLowerCase();
    rows = rows.filter(r => Object.values(r).some(v => String(v).toLowerCase().includes(s)));
  }
  if (sortKey) rows.sort((a,b) => { const av=a[sortKey],bv=b[sortKey]; return (av<bv?-1:av>bv?1:0)*sortDir; });
  return rows;
}

function render() {
  const filtered = getFiltered();
  const paged = filtered.slice(currentPage*PAGE_SIZE, (currentPage+1)*PAGE_SIZE);
  const totalPages = Math.ceil(filtered.length/PAGE_SIZE);
  
  document.getElementById('count').textContent = `${filtered.length} results`;
  
  // Header
  document.getElementById('thead').innerHTML = '<tr>' + COLUMNS.map(c => 
    `<th class="${sortKey===c.key?'sorted':''}" onclick="sort('${c.key}')">${c.label}<span class="sort-arrow">${sortKey===c.key?(sortDir===1?'↑':'↓'):'↕'}</span></th>`
  ).join('') + '</tr>';
  
  // Body
  document.getElementById('tbody').innerHTML = paged.map(row => 
    '<tr>' + COLUMNS.map(c => `<td>${formatCell(row[c.key], c.type)}</td>`).join('') + '</tr>'
  ).join('');
  
  // Pagination
  const pagEl = document.getElementById('pagination');
  pagEl.innerHTML = '';
  for(let i=0;i<totalPages;i++){
    const btn = document.createElement('button');
    btn.textContent = i+1;
    btn.className = i===currentPage?'active':'';
    btn.onclick = () => {currentPage=i;render();};
    pagEl.appendChild(btn);
  }
}

function sort(key){if(sortKey===key)sortDir*=-1;else{sortKey=key;sortDir=1;}currentPage=0;render();}
document.getElementById('search').addEventListener('input',e=>{searchTerm=e.target.value;currentPage=0;render();});
render();
</script></body></html>''', {
    "id": "data-table", "name": "Data Table Explorer", "category": "data",
    "tags": ["table","data","sortable","filterable","search","spreadsheet","directory","catalog"],
    "compatible_verticals": ["product-catalog","employee-directory","inventory-list","financial-data","vendor-list","leaderboard"],
    "core_libs": [], "complexity": "medium"
})


# ============================================================
# ENGINE 27: TESTIMONIAL / SOCIAL PROOF CAROUSEL
# ============================================================
write_template("27-testimonials", "template.html", '''<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- ENGINE_META: {"id":"testimonials","name":"Testimonial / Social Proof","category":"sales","tags":["testimonials","reviews","social-proof","quotes","case-studies","carousel"],"compatible_verticals":["landing-page","sales-page","product-page","case-study","portfolio","about-page"]} -->
<!-- AI_USAGE: Auto-rotating testimonial carousel with metrics bar. Modify TESTIMONIALS array: {quote,name,title,company,avatar(emoji),rating(1-5)}.
     Modify METRICS array: {value,label} for the social proof bar. Auto-advances every 5s, manual nav via dots. -->
<title>Testimonials</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Fraunces:ital,wght@0,400;0,700;1,400&family=DM+Sans:wght@400;500;600&display=swap');
:root{--bg:#0f172a;--card:#1e293b;--border:#334155;--text:#f1f5f9;--muted:#94a3b8;--accent:#f59e0b;--star:#f59e0b}
*{margin:0;padding:0;box-sizing:border-box}
body{background:var(--bg);color:var(--text);font-family:'DM Sans',sans-serif;min-height:100vh;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:3rem 2rem}
.metrics-bar{display:flex;gap:3rem;margin-bottom:3rem;flex-wrap:wrap;justify-content:center}
.metric{text-align:center}
.metric .val{font-family:'Fraunces',serif;font-size:2.5rem;font-weight:700;color:var(--accent)}
.metric .lbl{font-size:0.75rem;color:var(--muted);text-transform:uppercase;letter-spacing:0.12em;margin-top:0.25rem}
.carousel{max-width:650px;width:100%;position:relative}
.card{background:var(--card);border:1px solid var(--border);border-radius:16px;padding:2.5rem;text-align:center;opacity:0;transform:translateY(20px) scale(0.95);transition:all 0.6s ease;position:absolute;width:100%;pointer-events:none}
.card.active{opacity:1;transform:translateY(0) scale(1);position:relative;pointer-events:all}
.stars{color:var(--star);font-size:1.2rem;margin-bottom:1.5rem;letter-spacing:0.15em}
.quote{font-family:'Fraunces',serif;font-size:clamp(1rem,2vw,1.25rem);font-style:italic;line-height:1.8;color:var(--text);margin-bottom:1.5rem}
.avatar{font-size:2.5rem;margin-bottom:0.5rem}
.name{font-weight:600;font-size:0.95rem}
.title-co{font-size:0.8rem;color:var(--muted);margin-top:0.25rem}
.dots{display:flex;justify-content:center;gap:0.5rem;margin-top:2rem}
.dot{width:8px;height:8px;border-radius:50%;background:var(--border);cursor:pointer;transition:all 0.3s}
.dot.active{background:var(--accent);transform:scale(1.3)}
</style></head><body>
<div class="metrics-bar" id="metricsBar"></div>
<div class="carousel" id="carousel"></div>
<div class="dots" id="dots"></div>
<script>
// CUSTOMIZABLE: METRICS - social proof numbers
const METRICS = [
  {value:"10,000+",label:"Happy Customers"},
  {value:"4.9/5",label:"Average Rating"},
  {value:"98%",label:"Would Recommend"},
  {value:"50+",label:"Countries Served"}
];

// CUSTOMIZABLE: TESTIMONIALS
const TESTIMONIALS = [
  {quote:"This completely transformed how we present data to our board. Instead of a 40-page PDF nobody reads, we send an interactive experience that sparks real conversation.",name:"Sarah Mitchell",title:"CFO",company:"TechVentures Inc.",avatar:"👩‍💼",rating:5},
  {quote:"We closed a $2M deal because our proposal stood out. The prospect told us it was the first pitch deck they actually enjoyed going through.",name:"Marcus Chen",title:"VP Sales",company:"Cloudwise Solutions",avatar:"👨‍💼",rating:5},
  {quote:"The interactive annual report we created got shared by our donors on social media. That never happened with our PDF reports. Donations were up 34% that quarter.",name:"Dr. Amara Okafor",title:"Executive Director",company:"Global Impact Foundation",avatar:"👩‍⚕️",rating:5},
  {quote:"I replaced three different SaaS tools with this. My proposals, client reports, and pitch decks all come from one place now. And they look 10x better.",name:"Jake Morrison",title:"Independent Consultant",company:"Morrison Advisory",avatar:"👨‍💻",rating:5}
];

// Render metrics
document.getElementById('metricsBar').innerHTML = METRICS.map(m=>`<div class="metric"><div class="val">${m.value}</div><div class="lbl">${m.label}</div></div>`).join('');

// Render carousel
const carousel = document.getElementById('carousel');
const dotsEl = document.getElementById('dots');
let current = 0;

TESTIMONIALS.forEach((t,i)=>{
  const card = document.createElement('div');
  card.className = `card ${i===0?'active':''}`;
  card.innerHTML = `<div class="stars">${'★'.repeat(t.rating)}${'☆'.repeat(5-t.rating)}</div><div class="quote">"${t.quote}"</div><div class="avatar">${t.avatar}</div><div class="name">${t.name}</div><div class="title-co">${t.title}, ${t.company}</div>`;
  carousel.appendChild(card);
  
  const dot = document.createElement('div');
  dot.className = `dot ${i===0?'active':''}`;
  dot.onclick = () => goTo(i);
  dotsEl.appendChild(dot);
});

function goTo(idx){
  current = idx;
  carousel.querySelectorAll('.card').forEach((c,i)=>c.classList.toggle('active',i===idx));
  dotsEl.querySelectorAll('.dot').forEach((d,i)=>d.classList.toggle('active',i===idx));
}

setInterval(()=>goTo((current+1)%TESTIMONIALS.length), 5000);
</script></body></html>''', {
    "id": "testimonials", "name": "Testimonial / Social Proof", "category": "sales",
    "tags": ["testimonials","reviews","social-proof","quotes","case-studies","carousel"],
    "compatible_verticals": ["landing-page","sales-page","product-page","case-study","portfolio"],
    "core_libs": [], "complexity": "low"
})


# ============================================================
# ENGINE 28: CHECKLIST / PROGRESS TRACKER
# ============================================================
write_template("28-checklist", "template.html", '''<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- ENGINE_META: {"id":"checklist","name":"Checklist / Progress Tracker","category":"tool","tags":["checklist","progress","tracker","onboarding","tasks","compliance","steps"],"compatible_verticals":["onboarding-checklist","project-tracker","compliance-audit","launch-checklist","moving-checklist","wedding-planning","home-buying"]} -->
<!-- AI_USAGE: Interactive checklist with progress bar, categories, and completion tracking.
     Modify SECTIONS: [{title, items[{text, required(bool)}]}]. Progress auto-calculates.
     State persists in memory during session. Completion triggers celebration. -->
<title>Checklist</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Figtree:wght@400;500;600;700&display=swap');
:root{--bg:#fff;--surface:#f9fafb;--border:#e5e7eb;--text:#111827;--muted:#6b7280;--accent:#6366f1;--green:#22c55e;--red:#ef4444}
*{margin:0;padding:0;box-sizing:border-box}
body{background:var(--bg);color:var(--text);font-family:'Figtree',sans-serif;padding:2rem;min-height:100vh}
.container{max-width:640px;margin:0 auto}
h1{font-size:1.5rem;font-weight:700;margin-bottom:0.25rem}
.subtitle{color:var(--muted);font-size:0.9rem;margin-bottom:1.5rem}
.progress-wrap{margin-bottom:2rem}
.progress-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:0.5rem}
.progress-header .pct{font-size:1.5rem;font-weight:700;color:var(--accent)}
.progress-header .count{font-size:0.85rem;color:var(--muted)}
.progress-track{height:8px;background:var(--surface);border-radius:4px;overflow:hidden}
.progress-fill{height:100%;background:var(--accent);border-radius:4px;transition:width 0.4s ease}
.section{margin-bottom:2rem}
.section-title{font-size:0.8rem;font-weight:700;text-transform:uppercase;letter-spacing:0.08em;color:var(--accent);margin-bottom:0.75rem;padding-bottom:0.5rem;border-bottom:1px solid var(--border)}
.item{display:flex;align-items:flex-start;gap:0.75rem;padding:0.75rem 0;border-bottom:1px solid var(--border);transition:opacity 0.3s}
.item:last-child{border:none}
.item.done{opacity:0.5}
.item.done .text{text-decoration:line-through}
.item input[type=checkbox]{margin-top:0.15rem;width:20px;height:20px;accent-color:var(--accent);cursor:pointer;flex-shrink:0}
.item .text{font-size:0.9rem;line-height:1.5;cursor:pointer;flex:1}
.item .required{font-size:0.65rem;color:var(--red);font-weight:600;text-transform:uppercase;letter-spacing:0.05em;margin-left:0.5rem}
.celebration{display:none;text-align:center;padding:3rem;background:var(--surface);border-radius:16px;margin-top:2rem}
.celebration .emoji{font-size:4rem;margin-bottom:1rem}
.celebration h2{font-size:1.3rem;margin-bottom:0.5rem}
.celebration p{color:var(--muted)}
</style></head><body>
<div class="container">
  <h1><!-- CUSTOMIZABLE: checklist_title -->Launch Checklist<!-- /CUSTOMIZABLE --></h1>
  <p class="subtitle"><!-- CUSTOMIZABLE: checklist_subtitle -->Track your progress to launch day<!-- /CUSTOMIZABLE --></p>
  <div class="progress-wrap">
    <div class="progress-header"><span class="pct" id="pct">0%</span><span class="count" id="countText"></span></div>
    <div class="progress-track"><div class="progress-fill" id="progressFill"></div></div>
  </div>
  <div id="sections"></div>
  <div class="celebration" id="celebration"><div class="emoji">🎉</div><h2>All Complete!</h2><p>Every item has been checked off. You're ready!</p></div>
</div>
<script>
// CUSTOMIZABLE: SECTIONS with checklist items
const SECTIONS = [
  {title:"Pre-Launch", items:[
    {text:"Finalize product features and freeze scope",required:true},
    {text:"Complete QA testing on all platforms",required:true},
    {text:"Set up analytics and tracking",required:true},
    {text:"Prepare customer support documentation",required:false},
    {text:"Create onboarding email sequence",required:false}
  ]},
  {title:"Marketing", items:[
    {text:"Write and schedule launch announcement",required:true},
    {text:"Prepare social media assets and schedule posts",required:false},
    {text:"Brief PR contacts and send press kit",required:false},
    {text:"Set up launch landing page with email capture",required:true},
    {text:"Record product demo video",required:false}
  ]},
  {title:"Technical", items:[
    {text:"Deploy to production environment",required:true},
    {text:"Verify SSL certificates and security",required:true},
    {text:"Test payment processing end-to-end",required:true},
    {text:"Set up monitoring and alerting",required:true},
    {text:"Prepare rollback plan",required:false}
  ]}
];

const sectionsEl = document.getElementById('sections');
let totalItems = 0, checkedItems = 0;

SECTIONS.forEach(sec => {
  const div = document.createElement('div');
  div.className = 'section';
  div.innerHTML = `<div class="section-title">${sec.title}</div>`;
  sec.items.forEach(item => {
    totalItems++;
    const row = document.createElement('div');
    row.className = 'item';
    row.innerHTML = `<input type="checkbox" onchange="toggle(this)"><span class="text" onclick="this.previousElementSibling.click()">${item.text}</span>${item.required?'<span class="required">Required</span>':''}`;
    div.appendChild(row);
  });
  sectionsEl.appendChild(div);
});

function toggle(cb) {
  cb.parentElement.classList.toggle('done', cb.checked);
  checkedItems = document.querySelectorAll('.item input:checked').length;
  const pct = Math.round(checkedItems/totalItems*100);
  document.getElementById('pct').textContent = pct+'%';
  document.getElementById('countText').textContent = `${checkedItems} of ${totalItems} complete`;
  document.getElementById('progressFill').style.width = pct+'%';
  document.getElementById('celebration').style.display = pct===100?'block':'none';
}
document.getElementById('countText').textContent = `0 of ${totalItems} complete`;
</script></body></html>''', {
    "id": "checklist", "name": "Checklist / Progress Tracker", "category": "tool",
    "tags": ["checklist","progress","tracker","onboarding","tasks","compliance","steps","launch"],
    "compatible_verticals": ["onboarding-checklist","project-tracker","compliance-audit","launch-checklist","moving-checklist","wedding-planning"],
    "core_libs": [], "complexity": "low"
})


# ============================================================
# ENGINE 29: WATERFALL / BRIDGE CHART
# ============================================================
write_template("29-waterfall-chart", "template.html", '''<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- ENGINE_META: {"id":"waterfall-chart","name":"Waterfall / Bridge Chart","category":"data","tags":["waterfall","bridge","financial","revenue","cost","variance","P&L"],"compatible_verticals":["financial-analysis","revenue-bridge","cost-analysis","budget-variance","M&A-analysis","earnings-walkthrough","board-presentation"]} -->
<!-- AI_USAGE: Animated waterfall/bridge chart showing how a starting value changes through additions and subtractions.
     Modify ITEMS: [{label, value, type('start'|'add'|'subtract'|'total')}]. Chart auto-calculates running totals.
     Bars animate on scroll. Positive=green, negative=red, totals=blue. Click bar for detail tooltip. -->
<title>Waterfall Chart</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;600&family=Sora:wght@300;400;600&display=swap');
:root{--bg:#0f172a;--surface:#1e293b;--border:#334155;--text:#f1f5f9;--muted:#94a3b8;--green:#22c55e;--red:#ef4444;--blue:#6366f1;--line:#475569}
*{margin:0;padding:0;box-sizing:border-box}
body{background:var(--bg);color:var(--text);font-family:'Sora',sans-serif;padding:3rem 2rem;min-height:100vh}
.container{max-width:900px;margin:0 auto}
h1{font-size:1.4rem;font-weight:600;margin-bottom:0.25rem}
.subtitle{color:var(--muted);font-size:0.85rem;margin-bottom:2rem}
.chart{position:relative;padding:2rem 0}
.bar-group{display:flex;align-items:flex-end;gap:2px;height:400px;padding-bottom:2.5rem;position:relative}
.bar-col{flex:1;display:flex;flex-direction:column;align-items:center;position:relative;height:100%}
.bar{position:absolute;width:70%;border-radius:4px 4px 0 0;transition:height 1s ease,bottom 1s ease;cursor:pointer;min-height:2px}
.bar:hover{filter:brightness(1.2)}
.bar.green{background:var(--green)}.bar.red{background:var(--red)}.bar.blue{background:var(--blue)}
.bar-label{position:absolute;bottom:0;font-size:0.65rem;color:var(--muted);text-align:center;width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;padding:0 2px}
.bar-value{position:absolute;font-family:'JetBrains Mono',monospace;font-size:0.7rem;font-weight:600;text-align:center;width:100%;white-space:nowrap}
.connector{position:absolute;border-top:1px dashed var(--line);z-index:1}
.tooltip-box{display:none;position:fixed;background:var(--surface);border:1px solid var(--border);border-radius:8px;padding:1rem;z-index:100;font-size:0.85rem;box-shadow:0 8px 24px rgba(0,0,0,0.3);pointer-events:none}
.tooltip-box .t-label{font-weight:600;margin-bottom:0.25rem}
.tooltip-box .t-value{font-family:'JetBrains Mono',monospace;font-size:1.1rem}
.tooltip-box .t-running{font-size:0.75rem;color:var(--muted);margin-top:0.25rem}
</style></head><body>
<div class="container">
  <h1><!-- CUSTOMIZABLE: chart_title -->Revenue Bridge<!-- /CUSTOMIZABLE --></h1>
  <p class="subtitle"><!-- CUSTOMIZABLE: chart_subtitle -->Q3 to Q4 Revenue Walkthrough<!-- /CUSTOMIZABLE --></p>
  <div class="chart"><div class="bar-group" id="chart"></div></div>
</div>
<div class="tooltip-box" id="tooltip"></div>
<script>
// CUSTOMIZABLE: ITEMS for waterfall chart
// AI_HINT: type 'start' = opening balance, 'add' = positive change, 'subtract' = negative change, 'total' = closing balance
const ITEMS = [
  {label:"Q3 Revenue",value:4200000,type:"start"},
  {label:"New Customers",value:680000,type:"add"},
  {label:"Expansion",value:420000,type:"add"},
  {label:"Price Increase",value:150000,type:"add"},
  {label:"Churn",value:-310000,type:"subtract"},
  {label:"Downgrades",value:-120000,type:"subtract"},
  {label:"Seasonal Dip",value:-85000,type:"subtract"},
  {label:"Q4 Revenue",value:4935000,type:"total"}
];

const chart = document.getElementById('chart');
const tooltip = document.getElementById('tooltip');

// Calculate running totals
let running = 0;
const processed = ITEMS.map(item => {
  if (item.type==='start') { running = item.value; return {...item, bottom:0, height:item.value, running}; }
  if (item.type==='total') { return {...item, bottom:0, height:running, running}; }
  const prevRunning = running;
  running += item.value;
  const absVal = Math.abs(item.value);
  const bottom = item.value > 0 ? prevRunning : running;
  return {...item, bottom, height:absVal, running};
});

const maxVal = Math.max(...processed.map(p=>p.bottom+p.height));
const scale = v => (v/maxVal)*100;
const fmt = v => (v>=1000000?'$'+(v/1000000).toFixed(1)+'M':v>=1000?'$'+(v/1000).toFixed(0)+'K':'$'+v);

processed.forEach((item,i) => {
  const col = document.createElement('div');
  col.className = 'bar-col';
  
  const bar = document.createElement('div');
  const color = item.type==='start'||item.type==='total' ? 'blue' : item.value>0 ? 'green' : 'red';
  bar.className = `bar ${color}`;
  bar.style.bottom = scale(item.bottom)+'%';
  bar.style.height = '0%';
  
  // Animate on load
  setTimeout(()=>{bar.style.height = scale(item.height)+'%';}, 200+i*100);
  
  const valEl = document.createElement('div');
  valEl.className = 'bar-value';
  valEl.style.bottom = `calc(${scale(item.bottom+item.height)}% + 4px)`;
  valEl.style.color = color==='green'?'var(--green)':color==='red'?'var(--red)':'var(--blue)';
  valEl.textContent = (item.type!=='start'&&item.type!=='total'&&item.value>0?'+':'')+fmt(item.value);
  
  const label = document.createElement('div');
  label.className = 'bar-label';
  label.textContent = item.label;
  
  // Tooltip
  bar.addEventListener('mouseenter',e=>{
    tooltip.innerHTML = `<div class="t-label">${item.label}</div><div class="t-value" style="color:${color==='green'?'var(--green)':color==='red'?'var(--red)':'var(--blue)'}">${(item.value>0?'+':'')+fmt(item.value)}</div><div class="t-running">Running total: ${fmt(item.running)}</div>`;
    tooltip.style.display = 'block';
  });
  bar.addEventListener('mousemove',e=>{tooltip.style.left=e.clientX+16+'px';tooltip.style.top=e.clientY-16+'px';});
  bar.addEventListener('mouseleave',()=>{tooltip.style.display='none';});
  
  col.appendChild(bar);
  col.appendChild(valEl);
  col.appendChild(label);
  chart.appendChild(col);
});
</script></body></html>''', {
    "id": "waterfall-chart", "name": "Waterfall / Bridge Chart", "category": "data",
    "tags": ["waterfall","bridge","financial","revenue","cost","variance","P&L"],
    "compatible_verticals": ["financial-analysis","revenue-bridge","cost-analysis","budget-variance","M&A-analysis","earnings-walkthrough"],
    "core_libs": [], "complexity": "medium"
})


# ============================================================
# ENGINE 30: INTERACTIVE CODE PLAYGROUND
# ============================================================
write_template("30-code-playground", "template.html", '''<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- ENGINE_META: {"id":"code-playground","name":"Interactive Code Playground","category":"education","tags":["code","playground","editor","tutorial","programming","sandbox","live-preview"],"compatible_verticals":["coding-tutorial","API-documentation","developer-onboarding","technical-training","code-examples","SDK-guide"]} -->
<!-- AI_USAGE: Live code editor with instant preview. Modify EXAMPLES array: {title, description, code, language}.
     Editor uses contenteditable with syntax highlighting. Output panel shows live HTML/JS results.
     Supports HTML/CSS/JS editing with real-time preview in sandboxed iframe. -->
<title>Code Playground</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;500&family=Outfit:wght@400;500;600&display=swap');
:root{--bg:#1e1e2e;--editor:#181825;--border:#313244;--text:#cdd6f4;--muted:#6c7086;--accent:#89b4fa;--green:#a6e3a1;--red:#f38ba8;--yellow:#f9e2af;--surface:#313244}
*{margin:0;padding:0;box-sizing:border-box}
body{background:var(--bg);color:var(--text);font-family:'Outfit',sans-serif;height:100vh;display:flex;flex-direction:column}
.toolbar{display:flex;align-items:center;gap:1rem;padding:0.75rem 1.5rem;border-bottom:1px solid var(--border);background:var(--editor)}
.toolbar h1{font-size:0.95rem;font-weight:600;flex:1}
.toolbar select{background:var(--surface);border:1px solid var(--border);color:var(--text);padding:0.4rem 0.75rem;border-radius:6px;font-family:inherit;font-size:0.8rem}
.toolbar button{padding:0.4rem 1rem;background:var(--accent);color:var(--bg);border:none;border-radius:6px;font-family:inherit;font-size:0.8rem;font-weight:600;cursor:pointer;transition:opacity 0.15s}
.toolbar button:hover{opacity:0.9}
.workspace{flex:1;display:flex;overflow:hidden}
.editor-panel{flex:1;display:flex;flex-direction:column;border-right:1px solid var(--border)}
.editor-header{padding:0.5rem 1rem;background:var(--surface);font-size:0.75rem;color:var(--muted);display:flex;justify-content:space-between}
.editor{flex:1;overflow:auto;padding:1rem;font-family:'Fira Code',monospace;font-size:0.85rem;line-height:1.7;background:var(--editor);color:var(--text);white-space:pre-wrap;outline:none;tab-size:2;-moz-tab-size:2}
.preview-panel{flex:1;display:flex;flex-direction:column;background:#fff}
.preview-header{padding:0.5rem 1rem;background:var(--surface);font-size:0.75rem;color:var(--muted);border-bottom:1px solid var(--border)}
.preview-frame{flex:1;border:none;background:#fff}
.examples-desc{padding:0.5rem 1rem;font-size:0.8rem;color:var(--muted);background:var(--editor);border-bottom:1px solid var(--border)}
@media(max-width:768px){.workspace{flex-direction:column} .editor-panel{border-right:none;border-bottom:1px solid var(--border);max-height:50%}}
</style></head><body>
<div class="toolbar">
  <h1><!-- CUSTOMIZABLE: playground_title -->Code Playground<!-- /CUSTOMIZABLE --></h1>
  <select id="exampleSelect" onchange="loadExample(this.value)"></select>
  <button onclick="runCode()">▶ Run</button>
</div>
<div class="examples-desc" id="desc"></div>
<div class="workspace">
  <div class="editor-panel">
    <div class="editor-header"><span>Editor</span><span id="charCount"></span></div>
    <div class="editor" id="editor" contenteditable="true" spellcheck="false"></div>
  </div>
  <div class="preview-panel">
    <div class="preview-header">Preview</div>
    <iframe class="preview-frame" id="preview" sandbox="allow-scripts"></iframe>
  </div>
</div>
<script>
// CUSTOMIZABLE: EXAMPLES array
const EXAMPLES = [
  {
    title:"Hello World",
    description:"A simple HTML page with styled heading and animation.",
    code:`<!DOCTYPE html>
<html>
<head>
<style>
  body { font-family: system-ui; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background: #0f172a; }
  h1 { color: #6366f1; font-size: 3rem; animation: pulse 2s ease-in-out infinite; }
  @keyframes pulse { 0%,100%{transform:scale(1)} 50%{transform:scale(1.1)} }
</style>
</head>
<body>
  <h1>Hello, World!</h1>
</body>
</html>`
  },
  {
    title:"Interactive Counter",
    description:"A button that counts clicks with animated number display.",
    code:`<!DOCTYPE html>
<html>
<head>
<style>
  body { font-family: system-ui; display: flex; flex-direction: column; justify-content: center; align-items: center; height: 100vh; margin: 0; background: #fafafa; }
  .count { font-size: 5rem; font-weight: 800; color: #6366f1; transition: transform 0.15s; }
  .count.bump { transform: scale(1.2); }
  button { margin-top: 1rem; padding: 0.75rem 2rem; font-size: 1.1rem; background: #6366f1; color: white; border: none; border-radius: 8px; cursor: pointer; }
</style>
</head>
<body>
  <div class="count" id="n">0</div>
  <button onclick="inc()">Click Me!</button>
  <script>
    let count = 0;
    function inc() {
      count++;
      const el = document.getElementById('n');
      el.textContent = count;
      el.classList.add('bump');
      setTimeout(() => el.classList.remove('bump'), 150);
    }
  <\\/script>
</body>
</html>`
  },
  {
    title:"API Fetch Example",
    description:"Fetch data from a public API and display it.",
    code:`<!DOCTYPE html>
<html>
<head>
<style>
  body { font-family: system-ui; padding: 2rem; background: #fafafa; }
  .card { background: white; padding: 1.5rem; border-radius: 12px; box-shadow: 0 2px 8px rgba(0,0,0,0.08); margin-bottom: 1rem; }
  h3 { color: #6366f1; margin-bottom: 0.5rem; }
  p { color: #64748b; line-height: 1.6; }
</style>
</head>
<body>
  <h2>Random Users</h2>
  <div id="users">Loading...</div>
  <script>
    fetch('https://jsonplaceholder.typicode.com/users')
      .then(r=>r.json())
      .then(users=>{
        document.getElementById('users').innerHTML = users.slice(0,3).map(u=>
          '<div class="card"><h3>'+u.name+'</h3><p>'+u.email+' · '+u.company.name+'</p></div>'
        ).join('');
      });
  <\\/script>
</body>
</html>`
  }
];

const editor = document.getElementById('editor');
const preview = document.getElementById('preview');
const select = document.getElementById('exampleSelect');
const desc = document.getElementById('desc');

EXAMPLES.forEach((ex,i) => {
  const opt = document.createElement('option');
  opt.value = i; opt.textContent = ex.title;
  select.appendChild(opt);
});

function loadExample(idx) {
  const ex = EXAMPLES[idx];
  editor.textContent = ex.code;
  desc.textContent = ex.description;
  document.getElementById('charCount').textContent = ex.code.length + ' chars';
  runCode();
}

function runCode() {
  const code = editor.textContent;
  const blob = new Blob([code], {type:'text/html'});
  preview.src = URL.createObjectURL(blob);
  document.getElementById('charCount').textContent = code.length + ' chars';
}

editor.addEventListener('input', () => {
  document.getElementById('charCount').textContent = editor.textContent.length + ' chars';
});
editor.addEventListener('keydown', e => {
  if (e.key==='Tab') { e.preventDefault(); document.execCommand('insertText',false,'  '); }
  if ((e.metaKey||e.ctrlKey) && e.key==='Enter') { e.preventDefault(); runCode(); }
});

loadExample(0);
</script></body></html>''', {
    "id": "code-playground", "name": "Interactive Code Playground", "category": "education",
    "tags": ["code","playground","editor","tutorial","programming","sandbox","live-preview"],
    "compatible_verticals": ["coding-tutorial","API-documentation","developer-onboarding","technical-training","code-examples"],
    "core_libs": [], "complexity": "medium"
})


# ============================================================
# UPDATE MANIFEST
# ============================================================
import glob

manifest = {
    "name": "IterFact Template Vault",
    "version": "2.0.0",
    "description": "30 core interactive HTML engines for AI-assisted assembly. Each template is a self-contained, fully functional HTML file with AI-readable IFX annotations.",
    "protocol": "IFX (IterFact Exchange) v1.0",
    "annotation_spec": {
        "ENGINE_META": "JSON metadata for AI matching — id, tags, compatible_verticals",
        "AI_USAGE": "Plain-English guide for AI on how to customize this template",
        "CUSTOMIZABLE": "Marks injectable content sections with named identifiers",
        "AI_HINT": "Inline explanations of code behavior and modification guidance"
    },
    "engines": []
}

for meta_path in sorted(glob.glob(os.path.join(ENGINES_DIR, "*/meta.json"))):
    folder = os.path.basename(os.path.dirname(meta_path))
    with open(meta_path) as f:
        meta = json.load(f)
    manifest["engines"].append({
        "folder": folder,
        "path": f"engines/{folder}/template.html",
        **meta
    })

with open("manifest.json", "w") as f:
    json.dump(manifest, f, indent=2)

print(f"✅ Total engines in vault: {len(manifest['engines'])}")
for e in manifest["engines"]:
    print(f"   {e['folder']}: {e.get('name','?')} ({e.get('category','?')})")
