<!DOCTYPE html><html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>CanvasX Python Pipeline — AI-Powered HTML Generation</title>
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
    <style>:root {
  --bg: #000000;
  --surface: #0a0a0a;
  --surface-2: #111111;
  --surface-3: #1a1a1a;
  --border: #222222;
  --border-2: #333333;
  --text: #f0f0f0;
  --text-muted: #888888;
  --text-dim: #555555;
  --violet: #7B2FFF;
  --violet-dim: rgba(123,47,255,0.12);
  --cyan: #00F5FF;
  --cyan-dim: rgba(0,245,255,0.10);
  --green: #00E57A;
  --red: #FF3B5C;
  --yellow: #FFD60A;
  --gradient: linear-gradient(135deg, #7B2FFF 0%, #00F5FF 100%);
  --font: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --font-mono: 'JetBrains Mono', 'Fira Code', 'Cascadia Code', monospace;
  --radius: 8px;
  --radius-lg: 16px;
  --radius-full: 9999px;
  --shadow: 0 4px 24px rgba(0,0,0,0.6);
  --shadow-lg: 0 8px 48px rgba(0,0,0,0.8);
  --glow-v: 0 0 40px rgba(123,47,255,0.3);
  --glow-c: 0 0 40px rgba(0,245,255,0.3);
}
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0 }
html { scroll-behavior: smooth }
body { background: var(--bg); color: var(--text); font-family: var(--font); line-height: 1.6; -webkit-font-smoothing: antialiased }
a { color: inherit; text-decoration: none }
img { max-width: 100%; display: block }
.container { max-width: 1200px; margin: 0 auto; padding: 0 2rem }
.nav { position: sticky; top: 0; z-index: 100; background: transparent; border-bottom: 1px solid transparent; transition: background 0.3s ease, border-color 0.3s ease; }
.nav.scrolled { background: rgba(0,0,0,0.85); backdrop-filter: blur(20px); border-bottom: 1px solid var(--border); }
.nav .container { display: flex; justify-content: space-between; align-items: center; padding: 1rem 2rem; }
.nav-logo { background: var(--gradient); -webkit-background-clip: text; background-clip: text; color: transparent; font-weight: 700; font-size: 1.25rem; }
.nav-links { display: flex; gap: 2rem; align-items: center; list-style: none; }
.nav-links a { color: var(--text-muted); transition: color 0.2s; }
.nav-links a:hover { color: var(--text); }
.nav-cta { background: var(--gradient); padding: 0.5rem 1.25rem; border-radius: var(--radius-full); font-weight: 600; transition: transform 0.2s, box-shadow 0.2s; display: inline-block; }
.nav-cta:hover { transform: translateY(-2px); box-shadow: var(--glow-v); }
.skip-link { position: absolute; top: -100px; left: 0; background: var(--violet); color: #fff; padding: 0.75rem 1rem; z-index: 1000; transition: top 0.3s; }
.skip-link:focus { top: 0; outline: 2px solid var(--cyan); }
.gradient-text { background: var(--gradient); -webkit-background-clip: text; background-clip: text; color: transparent; }
.fx-slide { opacity: 0; transform: translateY(30px); transition: opacity 0.6s, transform 0.6s; }
.fx-slide.visible { opacity: 1; transform: translateY(0); }
.section-eyebrow { font-size: 0.8rem; font-weight: 600; letter-spacing: 0.12em; text-transform: uppercase; color: var(--violet); margin-bottom: 0.5rem; }
.section-title { font-size: clamp(2rem, 4vw, 3rem); font-weight: 800; line-height: 1.2; margin-bottom: 1rem; position: relative; display: inline-block; }
.section-title::after { content: ''; display: block; width: 60px; height: 3px; background: var(--gradient); margin-top: 0.5rem; border-radius: 2px; }
.tag { display: inline-block; font-size: 0.75rem; padding: 0.25rem 0.75rem; border-radius: var(--radius-full); border: 1px solid var(--border); color: var(--text-muted); }
.btn-primary { background: var(--gradient); padding: 0.75rem 1.75rem; border-radius: var(--radius-full); font-weight: 600; border: none; color: #fff; cursor: pointer; transition: transform 0.2s, box-shadow 0.2s; }
.btn-primary:hover { transform: translateY(-2px); box-shadow: var(--glow-v); }
.btn-secondary { background: transparent; border: 1px solid var(--border); padding: 0.75rem 1.75rem; border-radius: var(--radius-full); font-weight: 600; color: var(--text); cursor: pointer; transition: border-color 0.2s, color 0.2s; }
.btn-secondary:hover { border-color: var(--violet); color: var(--violet); }</style>
    <style>
main#main {
  max-width: 1440px;
  margin: 0 auto;
}

.hero {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 4rem 0;
  position: relative;
  overflow: hidden;
}
.hero::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse 80% 50% at 50% -20%, var(--violet-dim), transparent 60%);
  z-index: 0;
  pointer-events: none;
}
.hero::after {
  content: "";
  position: absolute;
  inset: 0;
  background-image: repeating-linear-gradient(var(--border) 0 1px, transparent 1px 40px),
                    repeating-linear-gradient(90deg, var(--border) 0 1px, transparent 1px 40px);
  background-size: 40px 40px;
  opacity: 0.15;
  z-index: 0;
  mask-image: linear-gradient(to bottom, black 40%, transparent 100%);
}
.hero .container {
  display: flex;
  align-items: center;
  gap: 4rem;
  position: relative;
  z-index: 1;
}
.hero-content {
  flex: 1;
  max-width: 600px;
}
.hero-title {
  font-size: clamp(3rem, 7vw, 6rem);
  font-weight: 800;
  line-height: 1.05;
  margin-bottom: 1.5rem;
  color: var(--text);
}
.hero-subtitle {
  color: var(--text-muted);
  font-size: 1.2rem;
  max-width: 600px;
  margin-bottom: 2.5rem;
  line-height: 1.6;
}
.hero-actions {
  display: flex;
  gap: 1rem;
  flex-wrap: wrap;
}
.hero-code-card {
  flex-shrink: 0;
  max-width: 380px;
  background: rgba(10, 10, 10, 0.8);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  backdrop-filter: blur(20px);
  padding: 1.5rem;
  font-family: var(--font-mono);
  font-size: 0.85rem;
  box-shadow: var(--shadow-lg);
}
.hero-code-card pre {
  margin: 0;
  padding: 0;
  background: transparent;
  overflow: hidden;
}
.hero-code-card code {
  color: var(--text-muted);
}

.features {
  padding: 5rem 2rem;
  max-width: 1200px;
  margin: 0 auto;
}
.features-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 1.5rem;
}
.feature-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 2rem;
  transition: 0.3s ease;
}
.feature-card:hover {
  border-color: var(--violet);
  transform: translateY(-4px);
  box-shadow: var(--glow-v);
}
.feature-icon {
  width: 48px;
  height: 48px;
  border-radius: var(--radius);
  background: var(--gradient);
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 1.25rem;
  color: #fff;
}
.feature-title {
  font-weight: 600;
  margin-bottom: 0.5rem;
  font-size: 1.1rem;
  color: var(--text);
}
.feature-desc {
  color: var(--text-muted);
  font-size: 0.95rem;
  line-height: 1.6;
}

.pipeline-viz {
  padding: 5rem 0;
  background: var(--bg);
}
.pipeline-viz .container {
  max-width: 1200px;
  margin: 0 auto;
  padding: 0 2rem;
}
.pipeline-layout {
  display: flex;
  gap: 3rem;
  align-items: flex-start;
  flex-wrap: wrap;
  margin-top: 3rem;
}
.wave-list {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 2rem;
  min-width: 300px;
}
.wave-row {
  display: flex;
  align-items: center;
  gap: 1rem;
  flex-wrap: wrap;
}
.wave-label {
  font-size: 0.75rem;
  color: var(--text-dim);
  width: 60px;
  flex-shrink: 0;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  font-weight: 600;
}
.wave-part {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 1rem;
  border-radius: var(--radius-full);
  border: 1px solid var(--border);
  background: var(--surface);
  font-size: 0.85rem;
  transition: 0.4s ease;
  color: var(--text);
}
.wave-part.animate {
  border-color: var(--violet);
  background: var(--violet-dim);
  box-shadow: var(--glow-v);
}
.wave-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--text-dim);
  transition: 0.4s ease;
}
.wave-part.animate .wave-dot {
  background: var(--green);
}
.wave-connector {
  flex: 1;
  height: 2px;
  background: repeating-linear-gradient(90deg, var(--border) 0, var(--border) 6px, transparent 6px, transparent 12px);
  min-width: 20px;
}

.code-demo {
  padding: 5rem 2rem;
  max-width: 1000px;
  margin: 0 auto;
}
.tab-bar {
  display: flex;
  gap: 0.5rem;
  margin-bottom: 1.5rem;
  border-bottom: 1px solid var(--border);
  padding-bottom: 0;
}
.tab {
  background: none;
  border: none;
  color: var(--text-muted);
  padding: 0.75rem 1.25rem;
  cursor: pointer;
  font-size: 0.9rem;
  transition: color 0.2s;
  position: relative;
  font-family: var(--font);
}
.tab:hover {
  color: var(--text);
}
.tab.active {
  color: var(--text);
}
.tab.active::after {
  content: "";
  position: absolute;
  bottom: -1px;
  left: 0;
  right: 0;
  height: 2px;
  background: var(--gradient);
}
.tab-panel {
  display: none;
}
.tab-panel.active {
  display: block;
}
.code-block {
  position: relative;
  background: var(--surface-3);
  border-radius: var(--radius-lg);
  border: 1px solid var(--border);
  overflow: hidden;
}
.copy-btn {
  position: absolute;
  top: 0.75rem;
  right: 0.75rem;
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text-muted);
  padding: 0.25rem 0.75rem;
  border-radius: var(--radius);
  font-size: 0.75rem;
  cursor: pointer;
  transition: all 0.2s;
  font-family: var(--font);
}
.copy-btn:hover {
  color: var(--text);
  border-color: var(--text-muted);
}
.copy-btn.copied {
  color: var(--green);
  border-color: var(--green);
}
.code-block pre {
  padding: 1.5rem;
  overflow-x: auto;
  margin: 0;
}
.code-block code {
  font-family: var(--font-mono);
  font-size: 0.875rem;
  line-height: 1.7;
  color: var(--text);
}
.kw { color: var(--violet); font-weight: 500; }
.fn { color: var(--cyan); }
.str { color: var(--green); }
.cm { color: var(--text-dim); font-style: italic; }
.num { color: var(--yellow); }
.var { color: var(--text); }
.op { color: var(--text-muted); }

.stats {
  padding: 5rem 2rem;
  max-width: 1200px;
  margin: 0 auto;
}
.stats-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 1.5rem;
}
.stat-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 2.5rem 2rem;
  text-align: center;
  transition: 0.3s ease;
}
.stat-card:hover {
  border-color: var(--cyan);
  box-shadow: var(--glow-c);
}
.stat-number {
  font-size: clamp(2.5rem, 5vw, 4rem);
  font-weight: 800;
  background: var(--gradient);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  line-height: 1;
}
.stat-label {
  color: var(--text-muted);
  font-size: 0.9rem;
  margin-top: 0.5rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}
.stats table {
  margin-top: 3rem;
  width: 100%;
  border-collapse: collapse;
  border-radius: var(--radius-lg);
  overflow: hidden;
  border: 1px solid var(--border);
  background: var(--surface);
}
.stats thead th {
  text-align: left;
  padding: 0.75rem 1rem;
  background: var(--surface-2);
  color: var(--text-muted);
  font-size: 0.85rem;
  font-weight: 600;
  border-bottom: 2px solid var(--border);
}
.stats tbody td {
  padding: 0.75rem 1rem;
  border-bottom: 1px solid var(--border);
  font-size: 0.9rem;
  color: var(--text);
}
.stats tbody tr:nth-child(odd) {
  background: var(--surface);
}
.stats tbody tr:nth-child(even) {
  background: var(--surface-2);
}
.stats tbody tr:last-child td {
  border-bottom: none;
}
.stats tbody td:first-child {
  color: var(--text-muted);
  font-weight: 500;
}
.stats tbody td:last-child {
  color: var(--text-dim);
  font-family: var(--font-mono);
}

.footer {
  border-top: 1px solid var(--border);
  background: var(--surface);
  padding: 4rem 0 2rem;
  margin-top: 4rem;
}
.footer a {
  color: var(--text-muted);
  transition: color 0.2s;
  text-decoration: none;
}
.footer a:hover {
  color: var(--text);
}
.footer-inner {
  max-width: 1200px;
  margin: 0 auto;
  padding: 0 2rem;
  display: flex;
  flex-direction: column;
  gap: 3rem;
}
.footer-links {
  display: flex;
  gap: 4rem;
  flex-wrap: wrap;
}
.footer-group-title {
  font-size: 0.75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--text-dim);
  margin-bottom: 1rem;
}
.footer-links ul {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
.footer-links li a {
  color: var(--text-muted);
  font-size: 0.9rem;
}
.footer-links li a:hover {
  color: var(--text);
}
.footer-bottom {
  border-top: 1px solid var(--border);
  padding-top: 2rem;
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.footer-copyright {
  color: var(--text-dim);
  font-size: 0.85rem;
}

/* Nav (referenced in media queries) */
@media (max-width: 1024px) {
  .features-grid {
    grid-template-columns: repeat(2, 1fr);
  }
  .stats-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (max-width: 768px) {
  .hero-title {
    font-size: clamp(2.5rem, 6vw, 4rem);
  }
  .features-grid {
    grid-template-columns: 1fr;
  }
  .stats-grid {
    grid-template-columns: repeat(2, 1fr);
  }
  .nav {
    flex-wrap: wrap;
  }
  .nav-links {
    gap: 1rem;
  }
  .hero .container {
    flex-direction: column;
    text-align: center;
    gap: 2rem;
  }
  .hero-content {
    max-width: 100%;
  }
  .hero-subtitle {
    max-width: 100%;
    margin-left: auto;
    margin-right: auto;
  }
  .hero-actions {
    justify-content: center;
  }
  .hero-code-card {
    max-width: 100%;
    width: 100%;
    text-align: left;
  }
}

@media (max-width: 480px) {
  .stats-grid {
    grid-template-columns: 1fr;
  }
  .hero {
    padding: 3rem 1rem;
  }
  .features, .code-demo, .stats, .footer-inner, .pipeline-viz .container {
    padding-left: 1rem;
    padding-right: 1rem;
  }
  .footer-links {
    flex-direction: column;
    gap: 2rem;
  }
}
</style>
</head>
<body>
    <a href="#main" class="skip-link">Skip to content</a>
    <nav class="nav" id="nav">
        <div class="container">
            <a class="nav-logo" href="#">CanvasX</a>
            <ul class="nav-links">
                <li><a href="#features">Features</a></li>
                <li><a href="#pipeline">Pipeline</a></li>
                <li><a href="#code">Code</a></li>
                <li><a href="#stats">Stats</a></li>
                <li><a href="#" class="nav-cta">GitHub</a></li>
            </ul>
        </div>
    </nav>
    <main id="main">
        <section id="hero" class="hero">
    <div class="container">
        <div class="hero-content">
            <p class="section-eyebrow">Python-Powered AI Pipeline</p>
            <h1 class="hero-title">
                <span class="gradient-text">Generate premium UI</span><br>
                at machine speed
            </h1>
            <p class="hero-subtitle">CanvasX orchestrates multi-part HTML generation with wave-based parallelism, built-in QA checks, intelligent stitching, and AI-powered audit — all from Python.</p>
            <div class="hero-actions">
                <a href="#" class="btn-primary">
                    <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
                        <path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
                    </svg>
                    View on GitHub
                </a>
                <a href="#" class="btn-secondary">Read the Docs</a>
            </div>
        </div>
        <div class="hero-code-card">
            <button class="copy-btn">Copy</button>
            <pre><code><span class="kw">from</span> pipeline <span class="kw">import</span> Pipeline
<span class="kw">import</span> asyncio

<span class="kw">async def</span> <span class="fn">main</span>():
    p = <span class="fn">Pipeline</span>(<span class="str">name="Dashboard"</span>, <span class="str">output_file="out.html"</span>)
    p.<span class="fn">shell</span>(<span class="str">"shell"</span>, <span class="str">prompt="..."</span>)
    p.<span class="fn">part</span>(<span class="str">"css"</span>, <span class="str">placeholder="/* PART:css */"</span>, <span class="str">context=["shell"]</span>)
    p.<span class="fn">part</span>(<span class="str">"hero"</span>, <span class="str">placeholder="&lt;!-- PART:hero --&gt;"</span>, <span class="str">context=["css"]</span>)
    p.<span class="fn">static</span>(<span class="str">"js"</span>, <span class="str">placeholder="/* PART:js */"</span>, <span class="str">content=</span><span class="var">MY_JS</span>)
    <span class="kw">await</span> p.<span class="fn">run</span>(<span class="str">parallel=True</span>, <span class="str">ai_dedup_pass=True</span>)

<span class="fn">asyncio</span>.<span class="fn">run</span>(<span class="fn">main</span>())</code></pre>
        </div>
    </div>
</section>
        <section id="features" class="features">
  <div class="container">
    <p class="section-eyebrow fx-slide">Python Modules</p>
    <h2 class="section-title fx-slide">Four modules. One pipeline.</h2>
    <div class="features-grid">
      <!-- Feature 1: PLAN TRACKING -->
      <div class="feature-card fx-slide">
        <div class="feature-icon">
          <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
            <line x1="8" y1="6" x2="21" y2="6"></line>
            <line x1="8" y1="12" x2="21" y2="12"></line>
            <line x1="8" y1="18" x2="21" y2="18"></line>
            <line x1="3" y1="6" x2="3.01" y2="6"></line>
            <line x1="3" y1="12" x2="3.01" y2="12"></line>
            <line x1="3" y1="18" x2="3.01" y2="18"></line>
            <polyline points="16 11 18 13 22 9"></polyline>
          </svg>
        </div>
        <h3 class="feature-title">Instruction Memory</h3>
        <p class="feature-desc">PartDef and Plan classes track every part's status, cache path, and output. Wave dependencies, placeholder rules, and generation config are all serialised to plan.json — giving you full resumability and an audit trail for every run.</p>
        <div style="display: flex; gap: 0.5rem; flex-wrap: wrap; margin-top: 1rem;">
          <span class="tag">plan.py</span>
          <span class="tag">PartDef</span>
          <span class="tag">Plan</span>
          <span class="tag">JSON</span>
        </div>
      </div>

      <!-- Feature 2: QA ENGINE -->
      <div class="feature-card fx-slide">
        <div class="feature-icon">
          <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
            <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path>
            <path d="M9 12l2 2 4-4"></path>
          </svg>
        </div>
        <h3 class="feature-title">Zero-Tolerance QA</h3>
        <p class="feature-desc">25+ pre-compiled regex patterns strip think blocks, prose lines, and leaked markdown fences from every part. Deep checks validate CSS variables, JS DOM references, tag balance, duplicate IDs, and accessibility — all before a single byte hits disk.</p>
        <div style="display: flex; gap: 0.5rem; flex-wrap: wrap; margin-top: 1rem;">
          <span class="tag">quality.py</span>
          <span class="tag">25+ checks</span>
          <span class="tag">Auto-fix</span>
          <span class="tag">Accessibility</span>
        </div>
      </div>

      <!-- Feature 3: INTELLIGENT STITCHING -->
      <div class="feature-card fx-slide">
        <div class="feature-icon">
          <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
            <circle cx="18" cy="18" r="3"></circle>
            <circle cx="6" cy="6" r="3"></circle>
            <path d="M6 21V9a9 9 0 0 0 9 9"></path>
            <line x1="6" y1="9" x2="6" y2="4"></line>
            <line x1="10" y1="4" x2="10" y2="2"></line>
            <line x1="14" y1="4" x2="14" y2="2"></line>
            <line x1="18" y1="4" x2="18" y2="2"></line>
          </svg>
        </div>
        <h3 class="feature-title">Three-Mode Stitching</h3>
        <p class="feature-desc">Fast stitch replaces placeholders in microseconds with pre/code block awareness. AI dedup sends each CSS block to Nemotron individually — no context limits, no wasted tokens. AI stitch uses full Nemotron reasoning to resolve conflicts, wire JS to correct IDs, and deduplicate across all parts simultaneously.</p>
        <div style="display: flex; gap: 0.5rem; flex-wrap: wrap; margin-top: 1rem;">
          <span class="tag">stitch.py</span>
          <span class="tag">Fast</span>
          <span class="tag">AI Dedup</span>
          <span class="tag">Nemotron</span>
        </div>
      </div>

      <!-- Feature 4: AI AUDIT -->
      <div class="feature-card fx-slide">
        <div class="feature-icon">
          <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
            <circle cx="11" cy="11" r="8"></circle>
            <line x1="21" y1="21" x2="16.65" y2="16.65"></line>
            <line x1="8" y1="11" x2="14" y2="11"></line>
          </svg>
        </div>
        <h3 class="feature-title">Structural Audit</h3>
        <p class="feature-desc">build_audit_payload() extracts a compact structural manifest from the stitched document — CSS vars, JS refs, section sizes, duplicate resources — and sends it to Nemotron for severity-ranked findings. CRITICAL → HIGH → MEDIUM → INFO, with exact fix recommendations.</p>
        <div style="display: flex; gap: 0.5rem; flex-wrap: wrap; margin-top: 1rem;">
          <span class="tag">audit.py</span>
          <span class="tag">Nemotron</span>
          <span class="tag">CRITICAL/HIGH/MEDIUM</span>
        </div>
      </div>
    </div>
  </div>
</section>
        <section id="pipeline" class="pipeline-viz">
  <div class="container">
    <p class="section-eyebrow fx-slide">Wave Execution</p>
    <h2 class="section-title fx-slide">Parallel by design.</h2>
    <p class="feature-desc fx-slide">Parts whose dependencies are satisfied run concurrently. The shell runs first, then independent parts execute in parallel waves — dramatically reducing total generation time.</p>

    <div class="pipeline-layout">
      <div class="wave-list">
        <!-- Wave 1 -->
        <div class="wave-row fx-slide">
          <span class="wave-label">Wave 1</span>
          <div class="wave-part animate" data-wave-delay="0ms">
            <span class="wave-dot"></span>
            <b>structure_guide</b>
            <span class="tag">static</span>
          </div>
          <div class="wave-part animate" data-wave-delay="100ms">
            <span class="wave-dot"></span>
            <b>shell</b>
            <span class="tag">shell</span>
          </div>
          <div class="wave-part animate" data-wave-delay="200ms">
            <span class="wave-dot"></span>
            <b>js</b>
            <span class="tag">static</span>
          </div>
        </div>

        <div class="wave-connector"></div>

        <!-- Wave 2 -->
        <div class="wave-row fx-slide">
          <span class="wave-label">Wave 2</span>
          <div class="wave-part animate" data-wave-delay="400ms">
            <span class="wave-dot"></span>
            <b>css_base</b>
            <span class="tag">generated</span>
          </div>
          <div class="wave-part animate" data-wave-delay="500ms">
            <span class="wave-dot"></span>
            <b>css</b>
            <span class="tag">generated</span>
          </div>
        </div>

        <div class="wave-connector"></div>

        <!-- Wave 3 -->
        <div class="wave-row fx-slide">
          <span class="wave-label">Wave 3</span>
          <div class="wave-part animate" data-wave-delay="700ms">
            <span class="wave-dot"></span>
            <b>hero</b>
            <span class="tag">generated</span>
          </div>
          <div class="wave-part animate" data-wave-delay="800ms">
            <span class="wave-dot"></span>
            <b>features</b>
            <span class="tag">generated</span>
          </div>
          <div class="wave-part animate" data-wave-delay="900ms">
            <span class="wave-dot"></span>
            <b>pipeline_viz</b>
            <span class="tag">generated</span>
          </div>
          <div class="wave-part animate" data-wave-delay="1000ms">
            <span class="wave-dot"></span>
            <b>code_demo</b>
            <span class="tag">generated</span>
          </div>
          <div class="wave-part animate" data-wave-delay="1100ms">
            <span class="wave-dot"></span>
            <b>stats</b>
            <span class="tag">generated</span>
          </div>
        </div>
      </div>

      <div class="stat-card fx-slide">
        <div class="stat-number">10</div>
        <div class="stat-label">parts</div>
        <div class="stat-number">3</div>
        <div class="stat-label">waves</div>
      </div>
    </div>
  </div>
</section>
        <section id="code" class="code-demo">
  <div class="container">
    <p class="section-eyebrow fx-slide">Code Examples</p>
    <h2 class="section-title fx-slide">Clean Python API.</h2>
    <div class="tab-wrapper fx-slide" data-tab-group="code-tabs">
      <div class="tab-bar">
        <button class="tab active" data-tab="basic">Basic Pipeline</button>
        <button class="tab" data-tab="qa">QA Checks</button>
        <button class="tab" data-tab="audit">AI Audit</button>
      </div>
      <div class="tab-panels">
        <div class="tab-panel active" data-tab-panel="basic">
          <div class="code-block">
            <button class="copy-btn">Copy</button>
            <pre><code><span class="kw">import</span> asyncio
<span class="kw">from</span> pipeline <span class="kw">import</span> Pipeline

<span class="kw">async def</span> <span class="fn">main</span>():
    p = <span class="fn">Pipeline</span>(
        name=<span class="str">"My Dashboard"</span>,
        output_file=<span class="str">"dashboard.html"</span>,
        cache_dir=<span class="str">".pipeline-cache/dashboard"</span>,
    )
    p.shell(<span class="str">"shell"</span>, max_tokens=<span class="num">4000</span>, prompt=<span class="str">""" Generate a dark dashboard skeleton. Include: /* PART:css */ &lt;!-- PART:content --&gt; /* PART:js */ """</span>)
    p.part(<span class="str">"css"</span>, max_tokens=<span class="num">12000</span>, placeholder=<span class="str">"/* PART:css */"</span>, context=[<span class="str">"shell"</span>])
    p.part(<span class="str">"content"</span>, max_tokens=<span class="num">10000</span>, placeholder=<span class="str">"&lt;!-- PART:content --&gt;"</span>, context=[<span class="str">"css"</span>])
    p.static(<span class="str">"js"</span>, placeholder=<span class="str">"/* PART:js */"</span>, content=MY_JS)
    html = <span class="kw">await</span> p.<span class="fn">run</span>(parallel=<span class="kw">True</span>, ai_dedup_pass=<span class="kw">True</span>)
    <span class="fn">print</span>(<span class="str">f"Generated {len(html) // 1024} KB"</span>)

asyncio.<span class="fn">run</span>(main())</code></pre>
          </div>
        </div>
        <div class="tab-panel" data-tab-panel="qa">
          <div class="code-block">
            <button class="copy-btn">Copy</button>
            <pre><code><span class="kw">from</span> pipeline.quality <span class="kw">import</span> ( qualify_part, fix_final_doc, check_html_deep, strip_think_blocks, fix_duplicate_ids, )

<span class="cm"># Per-part quality gate (fast, no API)</span>
result = <span class="fn">qualify_part</span>(raw_content, label=<span class="str">"css"</span>, is_shell=<span class="kw">False</span>)
<span class="cm"># result = {"content": fixed_str, "issues": [...], "fixed": True}</span>

<span class="cm"># Deep-validate the final stitched document</span>
result = <span class="fn">fix_final_doc</span>(html, parts)
<span class="cm"># Runs: leak cleanup → prose strip → dedup imports → orphan removal → dup IDs → missing alt → deep checks</span>

<span class="cm"># Run all checks manually</span>
issues = <span class="fn">check_html_deep</span>(html)
<span class="kw">for</span> issue <span class="kw">in</span> issues:
    severity = issue[<span class="str">"severity"</span>] <span class="cm"># "error" | "warning"</span>
    message = issue[<span class="str">"message"</span>]
    <span class="fn">print</span>(<span class="str">f"[{severity.upper()}] {message}"</span>)</code></pre>
          </div>
        </div>
        <div class="tab-panel" data-tab-panel="audit">
          <div class="code-block">
            <button class="copy-btn">Copy</button>
            <pre><code><span class="kw">import</span> asyncio
<span class="kw">from</span> pipeline.audit <span class="kw">import</span> audit_with_ai, quick_audit, validate_part_with_ai

<span class="kw">async def</span> <span class="fn">run_audit</span>(html: str) -> <span class="kw">None</span>:
    <span class="cm"># Instant heuristic check (no API call)</span>
    summary = <span class="fn">quick_audit</span>(html) <span class="cm"># {"criticals": [...], "highs": [...], "mediums": [...]}</span>

    <span class="cm"># Per-part AI validation via Nemotron</span>
    result = <span class="kw">await</span> <span class="fn">validate_part_with_ai</span>(
        content, label=<span class="str">"hero"</span>, expected_type=<span class="str">"html-fragment"</span>, min_kb=<span class="num">3.0</span>,
    )
    <span class="cm"># result["verdict"] → "PASS" | "WARN" | "FAIL"</span>
    <span class="cm"># result["issues"] → ["[HIGH] JS targeting missing element #nav-toggle"]</span>

    <span class="cm"># Full document audit (Nemotron reasoning)</span>
    report = <span class="kw">await</span> <span class="fn">audit_with_ai</span>(html, context=<span class="str">"Dark dashboard, 8 parts"</span>)
    <span class="fn">print</span>(report[<span class="str">"report"</span>]) <span class="cm"># structured findings</span>
    <span class="fn">print</span>(report[<span class="str">"criticals"</span>]) <span class="cm"># list of CRITICAL finding titles</span>

asyncio.<span class="fn">run</span>(run_audit(html))</code></pre>
          </div>
        </div>
      </div>
    </div>
  </div>
</section>
        <section id="stats" class="stats">
  <div class="container">
    <p class="section-eyebrow fx-slide">By the numbers</p>
    <h2 class="section-title fx-slide">Built for serious generation.</h2>
    
    <div class="stats-grid">
      <div class="stat-card fx-slide">
        <div class="stat-number" data-target="32000">32 000</div>
        <div class="stat-label">Max tokens per part</div>
      </div>
      <div class="stat-card fx-slide">
        <div class="stat-number" data-target="25">25</div>
        <div class="stat-label">QA checks per part</div>
      </div>
      <div class="stat-card fx-slide">
        <div class="stat-number" data-target="3">3</div>
        <div class="stat-label">AI models supported</div>
      </div>
      <div class="stat-card fx-slide">
        <div class="stat-number" data-target="100">100</div>
        <div class="stat-label">KB+ generated per pipeline</div>
      </div>
    </div>

    <table class="fx-slide">
      <thead>
        <tr>
          <th>Feature</th>
          <th>CanvasX Python Pipeline</th>
          <th>Manual HTML</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>Wave parallelism</td>
          <td>Yes — asyncio.gather()</td>
          <td>No</td>
        </tr>
        <tr>
          <td>Auto QA + auto-fix</td>
          <td>25+ checks, auto-repair</td>
          <td>Manual review</td>
        </tr>
        <tr>
          <td>AI stitching</td>
          <td>Nemotron reasoning</td>
          <td>Manual integration</td>
        </tr>
        <tr>
          <td>Cache & resume</td>
          <td>Part-level caching</td>
          <td>Start over</td>
        </tr>
        <tr>
          <td>AI audit</td>
          <td>Structural manifest</td>
          <td>None</td>
        </tr>
        <tr>
          <td>Syntax repair</td>
          <td>Nemotron + retry</td>
          <td>None</td>
        </tr>
      </tbody>
    </table>
  </div>
</section>
    </main>
    <footer class="footer">
        <div class="container">
            <div class="footer-inner">
                <div class="footer-links">
                    <!-- 2-3 groups of links: Product, Docs, Community -->
                </div>
                <div class="footer-bottom">
                    <p class="footer-copyright">© 2025 CanvasX. All rights reserved.</p>
                </div>
            </div>
        </div>
    </footer>
<script>(function () {
  'use strict';

  /* ── Scroll shadow on nav ─────────────────────────────────────────── */
  const nav = document.getElementById('nav');
  if (nav) {
    const onScroll = () => {
      nav.classList.toggle('scrolled', window.scrollY > 20);
    };
    window.addEventListener('scroll', onScroll, { passive: true });
  }

  /* ── Intersection observer: fx-slide-up ──────────────────────────── */
  const io = new IntersectionObserver(
    (entries) => {
      entries.forEach(e => {
        if (e.isIntersecting) {
          e.target.classList.add('visible');
          io.unobserve(e.target);
        }
      });
    },
    { threshold: 0.12 }
  );
  document.querySelectorAll('.fx-slide').forEach(el => io.observe(el));

  /* ── Tab switching ───────────────────────────────────────────────── */
  document.addEventListener('click', e => {
    const tab = e.target.closest('[data-tab]');
    if (!tab) return;
    const group = tab.closest('[data-tab-group]');
    if (!group) return;
    const target = tab.dataset.tab;

    group.querySelectorAll('[data-tab]').forEach(t => t.classList.toggle('active', t === tab));
    group.querySelectorAll('[data-tab-panel]').forEach(p => {
      p.classList.toggle('active', p.dataset.tabPanel === target);
    });
  });

  /* ── Copy code buttons ───────────────────────────────────────────── */
  document.addEventListener('click', e => {
    const btn = e.target.closest('.copy-btn');
    if (!btn) return;
    const pre = btn.closest('.code-block')?.querySelector('pre');
    if (!pre) return;
    navigator.clipboard.writeText(pre.textContent).then(() => {
      btn.textContent = 'Copied!';
      btn.classList.add('copied');
      setTimeout(() => {
        btn.textContent = 'Copy';
        btn.classList.remove('copied');
      }, 1800);
    });
  });

  /* ── Animated counters ───────────────────────────────────────────── */
  function animateCounter(el) {
    const target = parseInt(el.dataset.target, 10);
    const duration = 1600;
    const start = performance.now();
    const easeOut = t => 1 - Math.pow(1 - t, 3);
    const tick = (now) => {
      const progress = Math.min((now - start) / duration, 1);
      el.textContent = Math.round(easeOut(progress) * target).toLocaleString();
      if (progress < 1) requestAnimationFrame(tick);
    };
    requestAnimationFrame(tick);
  }

  const counterObs = new IntersectionObserver((entries) => {
    entries.forEach(e => {
      if (e.isIntersecting) {
        animateCounter(e.target);
        counterObs.unobserve(e.target);
      }
    });
  }, { threshold: 0.5 });
  document.querySelectorAll('[data-target]').forEach(el => counterObs.observe(el));

  /* ── Pipeline wave animation ─────────────────────────────────────── */
  const waveItems = document.querySelectorAll('.wave-part');
  if (waveItems.length) {
    let delay = 0;
    waveItems.forEach(item => {
      const waveDelay = parseInt(item.dataset.waveDelay || '0', 10);
      setTimeout(() => item.classList.add('animate'), waveDelay);
    });
  }

})();</script>
</body>
</html>