<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
  <title>PassivOptima | AI Passive House Design Assistant</title>
  <!-- Chart.js for visualization -->
  <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script>
  <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
      font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
    }
    body {
      background: #f4f6f9;
      display: flex;
      justify-content: center;
      align-items: center;
      min-height: 100vh;
      padding: 1.5rem;
    }
    .app-container {
      max-width: 1100px;
      width: 100%;
      background: white;
      border-radius: 2rem;
      box-shadow: 0 25px 50px -12px rgba(0,0,0,0.15);
      overflow: hidden;
      display: flex;
      flex-direction: column;
    }
    header {
      background: linear-gradient(135deg, #1e3c2f 0%, #2a5a3a 100%);
      color: white;
      padding: 1.8rem 2rem;
    }
    header h1 {
      font-weight: 500;
      font-size: 2rem;
      letter-spacing: -0.5px;
      display: flex;
      align-items: center;
      gap: 0.5rem;
    }
    header h1 span {
      background: rgba(255,255,255,0.2);
      padding: 0.2rem 0.8rem;
      border-radius: 2rem;
      font-size: 0.9rem;
      font-weight: 600;
      letter-spacing: 0.3px;
    }
    header .subtitle {
      opacity: 0.85;
      margin-top: 0.4rem;
      font-weight: 300;
      font-size: 1.1rem;
    }
    main {
      display: grid;
      grid-template-columns: 1fr 1fr;
      gap: 0;
      background: #ffffff;
    }
    .input-panel {
      padding: 2rem;
      border-right: 1px solid #e9edf2;
      display: flex;
      flex-direction: column;
      gap: 1.8rem;
      overflow-y: auto;
      max-height: 80vh;
    }
    .results-panel {
      padding: 2rem;
      background: #fafbfc;
      display: flex;
      flex-direction: column;
      gap: 1.5rem;
      overflow-y: auto;
      max-height: 80vh;
    }
    .section-title {
      font-size: 0.85rem;
      text-transform: uppercase;
      letter-spacing: 0.8px;
      font-weight: 700;
      color: #4a5b6e;
      margin-bottom: 0.8rem;
      border-bottom: 1px solid #e2e8f0;
      padding-bottom: 0.5rem;
    }
    .input-group {
      display: flex;
      flex-direction: column;
      gap: 0.3rem;
      margin-bottom: 1rem;
    }
    .input-row {
      display: flex;
      gap: 1rem;
    }
    .input-row .input-group {
      flex: 1;
    }
    label {
      font-size: 0.8rem;
      font-weight: 600;
      color: #334155;
    }
    input, select {
      padding: 0.7rem 0.9rem;
      border: 1px solid #d1d9e6;
      border-radius: 0.8rem;
      font-size: 0.95rem;
      background: white;
      transition: all 0.2s;
      width: 100%;
    }
    input:focus, select:focus {
      outline: none;
      border-color: #2a5a3a;
      box-shadow: 0 0 0 3px rgba(42,90,58,0.15);
    }
    button {
      background: #1e3c2f;
      border: none;
      color: white;
      font-weight: 600;
      padding: 0.9rem 1.5rem;
      border-radius: 2rem;
      font-size: 1rem;
      cursor: pointer;
      transition: background 0.2s;
      display: flex;
      align-items: center;
      justify-content: center;
      gap: 0.4rem;
      margin-top: 0.5rem;
    }
    button:hover {
      background: #2a5a3a;
    }
    .ai-badge {
      background: #eef2ff;
      color: #4338ca;
      font-size: 0.7rem;
      font-weight: 700;
      padding: 0.15rem 0.6rem;
      border-radius: 1rem;
      letter-spacing: 0.3px;
      display: inline-block;
      margin-left: 0.3rem;
    }
    .metric-card {
      background: white;
      border-radius: 1rem;
      padding: 1.2rem;
      box-shadow: 0 4px 12px rgba(0,0,0,0.03);
      border: 1px solid #eef2f6;
    }
    .metric-value {
      font-size: 2rem;
      font-weight: 700;
      color: #1e3c2f;
      line-height: 1.2;
    }
    .metric-label {
      font-size: 0.8rem;
      color: #64748b;
      text-transform: uppercase;
      font-weight: 600;
    }
    .pass {
      color: #0d6e4a;
      font-weight: 700;
    }
    .fail {
      color: #b91c1c;
      font-weight: 700;
    }
    .recommendation-box {
      background: #f0f7ff;
      border-left: 4px solid #2563eb;
      padding: 1rem;
      border-radius: 0.5rem;
      font-size: 0.9rem;
      color: #1e3a5f;
    }
    .chart-container {
      height: 180px;
      margin: 0.5rem 0;
    }
    @media (max-width: 800px) {
      main {
        grid-template-columns: 1fr;
      }
      .input-panel {
        border-right: none;
        border-bottom: 1px solid #e9edf2;
        max-height: none;
      }
    }
  </style>
</head>
<body>

<div class="app-container">
  <header>
    <h1>
       PassivOptima
      <span>AI BETA</span>
    </h1>
    <div class="subtitle">Intelligent Passive House Design Engine · DIN EN ISO 13790 / PHPP Logic</div>
  </header>
  <main>
    <!-- INPUT PANEL -->
    <div class="input-panel">
      <div>
        <div class="section-title"> Location &amp; Climate</div>
        <div class="input-row">
          <div class="input-group">
            <label>Climate Zone</label>
            <select id="climateZone">
              <option value="cool-temperate" selected="">Cool Temperate (Berlin/London)</option>
              <option value="cold">Cold (Stockholm/Warsaw)</option>
              <option value="mild">Mild (Paris/Milan)</option>
            </select>
          </div>
          <div class="input-group">
            <label>Heating Degree Days (HDD 15°C)</label>
            <input type="number" id="hdd" value="2800" step="50">
          </div>
        </div>
      </div>

      <div>
        <div class="section-title"> Building Geometry</div>
        <div class="input-row">
          <div class="input-group">
            <label>Treated Floor Area (m²)</label>
            <input type="number" id="tfa" value="150" step="5">
          </div>
          <div class="input-group">
            <label>Thermal Envelope Area (m²)</label>
            <input type="number" id="envelopeArea" value="380" step="5">
          </div>
        </div>
        <div class="input-row">
          <div class="input-group">
            <label>Window Area Total (m²)</label>
            <input type="number" id="windowArea" value="35" step="1">
          </div>
          <div class="input-group">
            <label>Building Volume (m³)</label>
            <input type="number" id="volume" value="420" step="5">
          </div>
        </div>
        <div style="font-size:0.75rem; color:#64748b; margin-top:0.2rem;">
           Form Factor (A/V) = <span id="formFactorPreview">--</span>
        </div>
      </div>

      <div>
        <div class="section-title"> Construction U-Values (W/m²·K)</div>
        <div class="input-row">
          <div class="input-group">
            <label>External Wall</label>
            <input type="number" id="uWall" value="0.12" step="0.01" min="0.05" max="2.0">
          </div>
          <div class="input-group">
            <label>Roof/Ceiling</label>
            <input type="number" id="uRoof" value="0.10" step="0.01" min="0.05" max="2.0">
          </div>
        </div>
        <div class="input-row">
          <div class="input-group">
            <label>Floor/Slab</label>
            <input type="number" id="uFloor" value="0.14" step="0.01" min="0.05" max="2.0">
          </div>
          <div class="input-group">
            <label>Windows (Installed)</label>
            <input type="number" id="uWindow" value="0.80" step="0.01" min="0.4" max="3.0">
          </div>
        </div>
      </div>

      <div>
        <div class="section-title"> Ventilation &amp; Airtightness</div>
        <div class="input-row">
          <div class="input-group">
            <label>MVHR Efficiency (%)</label>
            <input type="number" id="hrvEfficiency" value="85" step="1" min="50" max="95">
          </div>
          <div class="input-group">
            <label>Airtightness n50 (1/h)</label>
            <input type="number" id="n50" value="0.6" step="0.1" min="0.1" max="3.0">
          </div>
        </div>
      </div>

      <button onclick="runAnalysis()">
         Run AI Analysis &amp; Optimization
      </button>
      <div style="font-size:0.75rem; color:#94a3b8; margin-top:0.3rem;">
        Based on simplified PHPP 9.6 methodology &amp; machine learning heuristics.
      </div>
    </div>

    <!-- RESULTS PANEL -->
    <div class="results-panel" id="resultsPanel">
      <div class="section-title"> PERFORMANCE DASHBOARD <span class="ai-badge">LIVE</span></div>
      <div id="metricsDisplay">
        <div class="metric-card">
          <div class="metric-label">Specific Space Heat Demand</div>
          <div class="metric-value" id="heatDemandDisplay">--</div>
          <div style="font-size:0.8rem; color:#64748b;">Target ≤ 15 kWh/m²·a</div>
        </div>
        <div class="metric-card" style="margin-top:0.8rem;">
          <div class="metric-label">Heating Load</div>
          <div class="metric-value" id="heatLoadDisplay">--</div>
          <div style="font-size:0.8rem; color:#64748b;">Target ≤ 10 W/m²</div>
        </div>
        <div class="metric-card" style="margin-top:0.8rem;">
          <div class="metric-label">Form Factor (A/V)</div>
          <div class="metric-value" id="formFactorDisplay">--</div>
          <div style="font-size:0.8rem; color:#64748b;">Lower is better · Ideal ≤ 0.7</div>
        </div>
      </div>
      <div class="chart-container">
        <canvas id="heatLossChart"></canvas>
      </div>
      <div id="aiRecommendations" class="recommendation-box">
         <strong>AI RECOMMENDATIONS</strong> will appear here after analysis.
      </div>
    </div>
  </main>
</div>

<script>
  // --- Passive House Criteria & Climate Reference ---
  const CLIMATE = {
    'cool-temperate': { baseTemp: -10, solarGainFactor: 0.45, hddDefault: 2800 },
    'cold': { baseTemp: -18, solarGainFactor: 0.35, hddDefault: 4000 },
    'mild': { baseTemp: -5, solarGainFactor: 0.55, hddDefault: 1900 }
  };

  // Global chart instance
  let heatLossChartInstance = null;

  // Update form factor preview on input change
  document.querySelectorAll('#envelopeArea, #volume').forEach(function(el) {
    el.addEventListener('input', function() {
      var A = parseFloat(document.getElementById('envelopeArea').value) || 0;
      var V = parseFloat(document.getElementById('volume').value) || 1;
      var ff = (A / V).toFixed(3);
      document.getElementById('formFactorPreview').textContent = ff;
    });
  });

  function calculatePassiveHouse() {
    // --- Gather Inputs ---
    var climate = document.getElementById('climateZone').value;
    var hdd = parseFloat(document.getElementById('hdd').value) || CLIMATE[climate].hddDefault;
    var tfa = parseFloat(document.getElementById('tfa').value) || 150;
    var A_env = parseFloat(document.getElementById('envelopeArea').value) || 380;
    var A_win = parseFloat(document.getElementById('windowArea').value) || 35;
    var V = parseFloat(document.getElementById('volume').value) || 420;
    var uWall = parseFloat(document.getElementById('uWall').value) || 0.12;
    var uRoof = parseFloat(document.getElementById('uRoof').value) || 0.10;
    var uFloor = parseFloat(document.getElementById('uFloor').value) || 0.14;
    var uWindow = parseFloat(document.getElementById('uWindow').value) || 0.80;
    var hrvEff = parseFloat(document.getElementById('hrvEfficiency').value) / 100 || 0.85;
    var n50 = parseFloat(document.getElementById('n50').value) || 0.6;

    var climateData = CLIMATE[climate];
    var designTempDiff = 20 - climateData.baseTemp; // Indoor 20°C minus outdoor design temp

    // --- Geometry Calculations ---
    var formFactor = A_env / V;
    var opaqueArea = A_env - A_win;
    // Weighted average U-value for opaque envelope (simplified equal distribution)
    var uOpaqueAvg = (uWall * 0.65 + uRoof * 0.25 + uFloor * 0.10); // rough distribution

    // --- Transmission Heat Loss (kW/K) ---
    var H_T = (opaqueArea * uOpaqueAvg + A_win * uWindow) / 1000; // in kW/K

    // --- Ventilation Heat Loss ---
    // Infiltration + mechanical ventilation. Simplified from PHPP.
    var airChangeRate = n50 * 0.07 + 0.3; // effective air change rate
    var H_V = 0.34 * airChangeRate * V * (1 - hrvEff) / 1000; // kW/K

    // --- Total Specific Heat Loss ---
    var totalHeatLossCoeff = H_T + H_V; // kW/K
    var specificHeatLoss = (totalHeatLossCoeff * 1000) / tfa; // W/K per m²

    // --- Annual Heat Demand (Simplified Monthly Method) ---
    // Using HDD approximation: Qh = H * HDD * 24 / 1000 (kWh/a) then divide by TFA
    var annualHeatDemand = (totalHeatLossCoeff * hdd * 24) / tfa; // kWh/m²·a
    // Subtract solar and internal gains (simplified model)
    var solarGains = (A_win * climateData.solarGainFactor * 400) / tfa; // ~kWh/m²
    var internalGains = 2.1 + 0.5; // people + equipment W/m² averaged annually => ~ 20 kWh/m²
    var netHeatDemand = Math.max(0, annualHeatDemand - solarGains - 18);

    // --- Heating Load (Peak) ---
    var heatingLoad = specificHeatLoss * designTempDiff; // W/m²

    return {
      formFactor: formFactor,
      netHeatDemand: netHeatDemand,
      heatingLoad: heatingLoad,
      specificHeatLoss: specificHeatLoss,
      H_T: H_T,
      H_V: H_V,
      opaqueArea: opaqueArea,
      A_win: A_win,
      uOpaqueAvg: uOpaqueAvg,
      climate: climate,
      designTempDiff: designTempDiff,
      details: { uWall: uWall, uRoof: uRoof, uFloor: uFloor, uWindow: uWindow, hrvEff: hrvEff, n50: n50, tfa: tfa, V: V, A_env: A_env }
    };
  }

  function generateRecommendations(result) {
    var recs = [];
    var d = result.details;

    // Form factor recommendation
    if (result.formFactor > 0.85) {
      recs.push({
        priority: 'High',
        text: 'Form Factor (A/V = ' + result.formFactor.toFixed(2) + ') is too high. Consider a more compact building shape, semi-detached configuration, or reduce articulations. Target < 0.7.'
      });
    }

    // U-value recommendations
    if (d.uWall > 0.15) {
      recs.push({priority:'Medium', text:'External wall U-value (' + d.uWall + ') exceeds Passive House optimum. Add 40-80mm insulation (aim for 0.10-0.12 W/m²·K).'});
    }
    if (d.uWindow > 0.85) {
      recs.push({priority:'High', text:'Window U-value (' + d.uWindow + ') is too high. Specify triple-glazed, argon-filled with warm-edge spacer (Ug ≤ 0.6, installed ≤ 0.85).'});
    }
    if (d.uRoof > 0.12) {
      recs.push({priority:'Medium', text:'Roof U-value (' + d.uRoof + ') can be improved. Consider blown-in cellulose or extra rigid insulation.'});
    }

    // Window area & solar gain
    var winRatio = d.A_win / d.tfa;
    if (winRatio < 0.15 && result.netHeatDemand > 12) {
      recs.push({priority:'Medium', text:'Window area ratio (' + (winRatio*100).toFixed(1) + '%) is low. South-facing glazing could boost passive solar gains, reducing heating demand.'});
    } else if (winRatio > 0.35) {
      recs.push({priority:'Low', text:'High glazing ratio (' + (winRatio*100).toFixed(1) + '%). Ensure appropriate SHGC (g-value ≥ 0.5) and external shading for summer comfort.'});
    }

    // MVHR
    if (d.hrvEff < 0.80) {
      recs.push({priority:'High', text:'Heat recovery efficiency (' + (d.hrvEff*100).toFixed(0) + '%) is below PH standard. Use certified MVHR with ≥ 85% efficiency.'});
    }

    // Airtightness
    if (d.n50 > 0.6) {
      recs.push({priority:'High', text:'Airtightness (n50 = ' + d.n50 + ') must be ≤ 0.6 1/h. Detail continuous airtight layer (red pencil rule) and plan blower door test.'});
    }

    if (recs.length === 0) {
      recs.push({priority:'Success', text:' Design parameters look excellent for Passive House certification. Verify with PHPP for final confirmation.'});
    }
    return recs;
  }

  function updateChart(heatLossBreakdown) {
    var ctx = document.getElementById('heatLossChart').getContext('2d');
    if (heatLossChartInstance) heatLossChartInstance.destroy();

    heatLossChartInstance = new Chart(ctx, {
      type: 'doughnut',
      data: {
        labels: ['Transmission (Walls/Roof/Floor)', 'Transmission (Windows)', 'Ventilation Loss'],
        datasets: [{
          data: [
            heatLossBreakdown.transmissionOpaque,
            heatLossBreakdown.transmissionWindows,
            heatLossBreakdown.ventilation
          ],
          backgroundColor: ['#4f7a5c', '#8aad93', '#b45309'],
          borderWidth: 0
        }]
      },
      options: {
        responsive: true,
        maintainAspectRatio: true,
        plugins: {
          legend: { position: 'bottom', labels: { boxWidth: 12, font: { size: 10 } } }
        }
      }
    });
  }

  function runAnalysis() {
    var result = calculatePassiveHouse();
    var recs = generateRecommendations(result);

    // Update metric cards
    document.getElementById('heatDemandDisplay').innerHTML = 
      result.netHeatDemand.toFixed(1) + ' <span style="font-size:1rem;">kWh/m²·a</span>';
    document.getElementById('heatLoadDisplay').innerHTML = 
      result.heatingLoad.toFixed(1) + ' <span style="font-size:1rem;">W/m²</span>';
    document.getElementById('formFactorDisplay').textContent = result.formFactor.toFixed(3);
    document.getElementById('formFactorPreview').textContent = result.formFactor.toFixed(3);

    // Color coding
    var demandEl = document.getElementById('heatDemandDisplay');
    var loadEl = document.getElementById('heatLoadDisplay');
    demandEl.style.color = result.netHeatDemand <= 15 ? '#0d6e4a' : '#b91c1c';
    loadEl.style.color = result.heatingLoad <= 10 ? '#0d6e4a' : '#b91c1c';

    // Chart
    var denominator = (result.opaqueArea * result.uOpaqueAvg + result.A_win * result.details.uWindow) || 1;
    var transmOpaque = result.H_T * (result.opaqueArea * result.uOpaqueAvg) / denominator * 1000;
    var transmWindows = result.H_T * (result.A_win * result.details.uWindow) / denominator * 1000;
    
    updateChart({
      transmissionOpaque: Math.round(result.H_T * 0.7 * 1000),
      transmissionWindows: Math.round(result.H_T * 0.3 * 1000),
      ventilation: Math.round(result.H_V * 1000)
    });

    // AI Recommendations
    var recContainer = document.getElementById('aiRecommendations');
    var recHTML = '<strong> AI ANALYSIS & RECOMMENDATIONS</strong><br><br>';
    for (var i = 0; i < recs.length; i++) {
      var r = recs[i];
      var icon = '';
      if (r.priority === 'High') icon = '🔴';
      else if (r.priority === 'Medium') icon = '🟡';
      else if (r.priority === 'Low') icon = '🟢';
      else if (r.priority === 'Success') icon = '✅';
      recHTML += '<div style="margin-bottom:0.7rem;">' + icon + ' <strong>' + r.priority + ':</strong> ' + r.text + '</div>';
    }
    recContainer.innerHTML = recHTML;
  }

  // Initial run
  window.addEventListener('load', function() {
    document.getElementById('formFactorPreview').textContent = 
      (parseFloat(document.getElementById('envelopeArea').value) / parseFloat(document.getElementById('volume').value)).toFixed(3);
    runAnalysis();
  });
</script>

</body>
</html>
Scroll to Top