́<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Chiffrage BTP — Déboursé Sec</title>
<link href="https://fonts.googleapis.com/css2?family=DM+Mono:wght@400;500&family=Syne:wght@400;600;700;800&display=swap" rel="stylesheet">
<style>
  :root {
    --bg: #0f0f0f;
    --surface: #1a1a1a;
    --surface2: #222222;
    --border: #2e2e2e;
    --accent: #f0c040;
    --accent2: #e87040;
    --text: #f0ece4;
    --muted: #888;
    --green: #5dbb7a;
    --red: #e05555;
  }
  * { box-sizing: border-box; margin: 0; padding: 0; }
  body {
    background: var(--bg);
    color: var(--text);
    font-family: 'DM Mono', monospace;
    min-height: 100vh;
    padding: 0 0 80px 0;
  }

  header {
    background: var(--surface);
    border-bottom: 2px solid var(--accent);
    padding: 20px 28px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    position: sticky;
    top: 0;
    z-index: 100;
  }
  header h1 {
    font-family: 'Syne', sans-serif;
    font-weight: 800;
    font-size: 1.3rem;
    letter-spacing: 0.05em;
    color: var(--accent);
  }
  header span {
    font-size: 0.75rem;
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: 0.1em;
  }

  .container { max-width: 960px; margin: 0 auto; padding: 24px 16px; }

  /* Corps d'état selector */
  .tabs {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
    margin-bottom: 24px;
  }
  .tab {
    padding: 7px 14px;
    border-radius: 4px;
    border: 1px solid var(--border);
    background: var(--surface);
    color: var(--muted);
    font-family: 'DM Mono', monospace;
    font-size: 0.78rem;
    cursor: pointer;
    transition: all 0.15s;
    text-transform: uppercase;
    letter-spacing: 0.06em;
  }
  .tab:hover { border-color: var(--accent); color: var(--accent); }
  .tab.active { background: var(--accent); color: #0f0f0f; border-color: var(--accent); font-weight: 600; }

  /* Section */
  .section-label {
    font-family: 'Syne', sans-serif;
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.15em;
    color: var(--muted);
    margin-bottom: 10px;
  }

  /* Table chiffrage */
  .chiffrage-table {
    width: 100%;
    border-collapse: collapse;
    background: var(--surface);
    border-radius: 8px;
    overflow: hidden;
    margin-bottom: 20px;
  }
  .chiffrage-table thead tr {
    background: var(--surface2);
    border-bottom: 2px solid var(--accent);
  }
  .chiffrage-table th {
    padding: 10px 12px;
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--muted);
    text-align: left;
    font-weight: 500;
  }
  .chiffrage-table th:last-child { text-align: right; }
  .chiffrage-table td {
    padding: 8px 12px;
    border-bottom: 1px solid var(--border);
    font-size: 0.82rem;
    vertical-align: middle;
  }
  .chiffrage-table tr:last-child td { border-bottom: none; }
  .chiffrage-table tr:hover td { background: #1e1e1e; }

  input[type="number"], input[type="text"] {
    background: var(--bg);
    border: 1px solid var(--border);
    color: var(--text);
    font-family: 'DM Mono', monospace;
    font-size: 0.82rem;
    padding: 5px 8px;
    border-radius: 4px;
    width: 100%;
    transition: border-color 0.15s;
  }
  input[type="number"]:focus, input[type="text"]:focus {
    outline: none;
    border-color: var(--accent);
  }
  input[type="number"] { text-align: right; }

  .col-designation { width: 28%; }
  .col-unite { width: 7%; }
  .col-qte { width: 9%; }
  .col-mo { width: 11%; }
  .col-fourniture { width: 11%; }
  .col-taux { width: 10%; }
  .col-ds { width: 11%; text-align: right; }
  .col-total { width: 13%; text-align: right; }

  .ds-val { color: var(--accent2); font-weight: 500; }
  .total-val { color: var(--accent); font-weight: 600; }

  /* Add row button */
  .btn-add {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 16px;
    background: transparent;
    border: 1px dashed var(--border);
    color: var(--muted);
    font-family: 'DM Mono', monospace;
    font-size: 0.78rem;
    border-radius: 4px;
    cursor: pointer;
    transition: all 0.15s;
    margin-bottom: 28px;
  }
  .btn-add:hover { border-color: var(--accent); color: var(--accent); }

  .btn-del {
    background: none;
    border: none;
    color: var(--red);
    cursor: pointer;
    font-size: 1rem;
    padding: 2px 6px;
    border-radius: 3px;
    opacity: 0.5;
    transition: opacity 0.15s;
  }
  .btn-del:hover { opacity: 1; }

  /* Récap */
  .recap {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 20px 24px;
    margin-top: 16px;
  }
  .recap-title {
    font-family: 'Syne', sans-serif;
    font-weight: 700;
    font-size: 1rem;
    color: var(--accent);
    margin-bottom: 16px;
    letter-spacing: 0.05em;
  }
  .recap-rows { display: flex; flex-direction: column; gap: 6px; }
  .recap-row {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 8px 12px;
    border-radius: 4px;
    background: var(--surface2);
    font-size: 0.83rem;
  }
  .recap-row .corps { color: var(--text); }
  .recap-row .montant { color: var(--accent); font-weight: 600; }
  .recap-total {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 12px 14px;
    margin-top: 10px;
    background: var(--accent);
    border-radius: 4px;
    font-family: 'Syne', sans-serif;
    font-weight: 800;
    font-size: 1.05rem;
    color: #0f0f0f;
  }

  /* Coefficients */
  .coeff-bar {
    display: flex;
    gap: 12px;
    flex-wrap: wrap;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 14px 20px;
    margin-bottom: 24px;
    align-items: center;
  }
  .coeff-item { display: flex; align-items: center; gap: 8px; font-size: 0.8rem; }
  .coeff-item label { color: var(--muted); white-space: nowrap; }
  .coeff-item input { width: 70px; }
  .coeff-sep { color: var(--border); font-size: 1.2rem; }

  .taux-ho {
    margin-left: auto;
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 0.8rem;
    color: var(--muted);
  }
  .taux-ho input { width: 80px; }

  /* Export */
  .btn-export {
    margin-top: 20px;
    padding: 10px 24px;
    background: var(--accent);
    color: #0f0f0f;
    font-family: 'Syne', sans-serif;
    font-weight: 700;
    font-size: 0.85rem;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    letter-spacing: 0.05em;
    transition: opacity 0.15s;
  }
  .btn-export:hover { opacity: 0.85; }

  @media (max-width: 600px) {
    .col-mo, .col-fourniture, .col-taux { display: none; }
    .chiffrage-table th.col-mo,
    .chiffrage-table th.col-fourniture,
    .chiffrage-table th.col-taux { display: none; }
  }
</style>
</head>
<body>

<header>
  <h1>⬛ CHIFFRAGE BTP</h1>
  <span>Déboursé sec — Tous corps d'état</span>
</header>

<div class="container">

  <!-- Coefficients globaux -->
  <div class="section-label">Paramètres globaux</div>
  <div class="coeff-bar">
    <div class="coeff-item">
      <label>Taux horaire MO (€/h)</label>
      <input type="number" id="tauxMO" value="45" min="0" step="0.5" oninput="recalcAll()">
    </div>
    <div class="coeff-sep">|</div>
    <div class="coeff-item">
      <label>Coeff. frais généraux</label>
      <input type="number" id="coeffFG" value="1.15" min="1" step="0.01" oninput="recalcAll()">
    </div>
    <div class="coeff-item">
      <label>Coeff. bénéfice</label>
      <input type="number" id="coeffBen" value="1.10" min="1" step="0.01" oninput="recalcAll()">
    </div>
    <div class="coeff-sep">|</div>
    <div class="coeff-item" style="color:var(--accent); font-weight:600;">
      <label style="color:var(--accent)">Coeff. vente total :</label>
      <span id="coeffTotal" style="font-size:0.95rem;">×1.265</span>
    </div>
  </div>

  <!-- Tabs corps d'état -->
  <div class="section-label">Corps d'état</div>
  <div class="tabs" id="tabs"></div>

  <!-- Table -->
  <div id="tableContainer"></div>

  <!-- Récap -->
  <div class="recap">
    <div class="recap-title">RÉCAPITULATIF GÉNÉRAL</div>
    <div class="recap-rows" id="recapRows"></div>
    <div class="recap-total">
      <span>TOTAL DÉBOURSÉ SEC</span>
      <span id="recapTotalDS">0,00 €</span>
    </div>
    <div class="recap-total" style="background:var(--accent2); margin-top:8px;">
      <span>TOTAL PRIX DE VENTE HT</span>
      <span id="recapTotalPV">0,00 €</span>
    </div>
  </div>

  <button class="btn-export" onclick="exportCSV()">↓ Exporter en CSV</button>
</div>

<script>
const CORPS = [
  { id: 'GO', label: 'Gros Œuvre', color: '#c0783a' },
  { id: 'ET', label: 'Étanchéité', color: '#5a8fc0' },
  { id: 'CH', label: 'Charpente', color: '#8b6fa0' },
  { id: 'CO', label: 'Couverture', color: '#5dbb7a' },
  { id: 'ME', label: 'Menuiserie', color: '#d4a020' },
  { id: 'PL', label: 'Plâtrerie', color: '#a0a0a0' },
  { id: 'CA', label: 'Carrelage', color: '#60b0a0' },
  { id: 'PE', label: 'Peinture', color: '#e07070' },
  { id: 'EL', label: 'Électricité', color: '#f0c040' },
  { id: 'PB', label: 'Plomberie', color: '#4090d0' },
  { id: 'CVC', label: 'CVC', color: '#f08040' },
  { id: 'VRD', label: 'VRD', color: '#80a060' },
];

// Data: { corpsId: [ {designation, unite, qte, tmo, pfourniture}, ... ] }
let data = {};
let activeCorps = 'GO';

// Default rows per corps
const defaultRows = () => [
  { designation: '', unite: 'm²', qte: 0, tmo: 0, pfourniture: 0 },
];

CORPS.forEach(c => { data[c.id] = defaultRows(); });

// Add some example rows
data['GO'] = [
  { designation: 'Maçonnerie parpaings 20cm', unite: 'm²', qte: 50, tmo: 1.5, pfourniture: 18 },
  { designation: 'Dalle béton e=12cm', unite: 'm²', qte: 80, tmo: 0.8, pfourniture: 22 },
];
data['PE'] = [
  { designation: 'Peinture murs (2 couches)', unite: 'm²', qte: 120, tmo: 0.3, pfourniture: 4.5 },
  { designation: 'Enduit plâtre', unite: 'm²', qte: 80, tmo: 0.6, pfourniture: 3 },
];
data['EL'] = [
  { designation: 'Tableau électrique 3x18', unite: 'ens.', qte: 1, tmo: 8, pfourniture: 320 },
  { designation: 'Prise de courant 2P+T', unite: 'u', qte: 20, tmo: 0.5, pfourniture: 8 },
];

function getTauxMO() { return parseFloat(document.getElementById('tauxMO').value) || 0; }
function getCoeffFG() { return parseFloat(document.getElementById('coeffFG').value) || 1; }
function getCoeffBen() { return parseFloat(document.getElementById('coeffBen').value) || 1; }
function getCoeffTotal() { return getCoeffFG() * getCoeffBen(); }

function fmt(n) {
  return n.toLocaleString('fr-FR', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) + ' €';
}

function calcDS(row) {
  const mo = (row.tmo || 0) * getTauxMO();
  const fourniture = parseFloat(row.pfourniture) || 0;
  return mo + fourniture;
}

function calcTotal(row) {
  return calcDS(row) * parseFloat(row.qte || 0);
}

function renderTabs() {
  const el = document.getElementById('tabs');
  el.innerHTML = CORPS.map(c => `
    <button class="tab ${c.id === activeCorps ? 'active' : ''}" onclick="setCorps('${c.id}')">${c.label}</button>
  `).join('');
}

function setCorps(id) {
  activeCorps = id;
  renderTabs();
  renderTable();
}

function renderTable() {
  const rows = data[activeCorps];
  const corps = CORPS.find(c => c.id === activeCorps);
  const container = document.getElementById('tableContainer');

  container.innerHTML = `
    <div class="section-label" style="margin-bottom:8px;">
      <span style="color:${corps.color}">■</span> ${corps.label}
    </div>
    <table class="chiffrage-table">
      <thead>
        <tr>
          <th class="col-designation">Désignation</th>
          <th class="col-unite">Unité</th>
          <th class="col-qte">Qté</th>
          <th class="col-mo">T.MO (h/u)</th>
          <th class="col-fourniture">Fourniture (€/u)</th>
          <th class="col-ds">DS (€/u)</th>
          <th class="col-total">Total DS (€)</th>
          <th style="width:40px;"></th>
        </tr>
      </thead>
      <tbody id="tbody">
        ${rows.map((r, i) => renderRow(r, i)).join('')}
      </tbody>
    </table>
    <button class="btn-add" onclick="addRow()">+ Ajouter une ligne</button>
  `;
}

function renderRow(r, i) {
  const ds = calcDS(r);
  const total = calcTotal(r);
  return `
    <tr>
      <td class="col-designation">
        <input type="text" value="${r.designation}" 
          oninput="updateRow(${i}, 'designation', this.value)" placeholder="Désignation…">
      </td>
      <td class="col-unite">
        <input type="text" value="${r.unite}" 
          oninput="updateRow(${i}, 'unite', this.value)" style="text-align:center;">
      </td>
      <td class="col-qte">
        <input type="number" value="${r.qte}" min="0" step="0.1"
          oninput="updateRow(${i}, 'qte', this.value)">
      </td>
      <td class="col-mo">
        <input type="number" value="${r.tmo}" min="0" step="0.05"
          oninput="updateRow(${i}, 'tmo', this.value)">
      </td>
      <td class="col-fourniture">
        <input type="number" value="${r.pfourniture}" min="0" step="0.1"
          oninput="updateRow(${i}, 'pfourniture', this.value)">
      </td>
      <td class="col-ds"><span class="ds-val">${fmt(ds)}</span></td>
      <td class="col-total"><span class="total-val">${fmt(total)}</span></td>
      <td><button class="btn-del" onclick="deleteRow(${i})">✕</button></td>
    </tr>
  `;
}

function updateRow(i, field, value) {
  data[activeCorps][i][field] = field === 'designation' || field === 'unite' ? value : parseFloat(value) || 0;
  refreshRowCalcs(i);
  updateRecap();
}

function refreshRowCalcs(i) {
  const row = data[activeCorps][i];
  const ds = calcDS(row);
  const total = calcTotal(row);
  const tbody = document.getElementById('tbody');
  if (!tbody) return;
  const tr = tbody.children[i];
  if (!tr) return;
  tr.querySelector('.ds-val').textContent = fmt(ds);
  tr.querySelector('.total-val').textContent = fmt(total);
}

function addRow() {
  data[activeCorps].push({ designation: '', unite: 'm²', qte: 0, tmo: 0, pfourniture: 0 });
  renderTable();
  updateRecap();
}

function deleteRow(i) {
  data[activeCorps].splice(i, 1);
  if (data[activeCorps].length === 0) data[activeCorps] = defaultRows();
  renderTable();
  updateRecap();
}

function recalcAll() {
  // Update coeff display
  document.getElementById('coeffTotal').textContent = '×' + getCoeffTotal().toFixed(3);
  renderTable();
  updateRecap();
}

function getCorpsTotal(id) {
  return (data[id] || []).reduce((sum, r) => sum + calcTotal(r), 0);
}

function updateRecap() {
  const recapRows = document.getElementById('recapRows');
  let totalDS = 0;
  recapRows.innerHTML = CORPS.map(c => {
    const t = getCorpsTotal(c.id);
    totalDS += t;
    if (t === 0) return '';
    return `<div class="recap-row">
      <span class="corps"><span style="color:${c.color}">■</span> ${c.label}</span>
      <span class="montant">${fmt(t)}</span>
    </div>`;
  }).join('');

  document.getElementById('recapTotalDS').textContent = fmt(totalDS);
  document.getElementById('recapTotalPV').textContent = fmt(totalDS * getCoeffTotal());
}

function exportCSV() {
  let csv = 'Corps d\'état;Désignation;Unité;Quantité;T.MO (h/u);Fourniture (€/u);DS unitaire (€);Total DS (€)\n';
  CORPS.forEach(c => {
    (data[c.id] || []).forEach(r => {
      if (!r.designation && r.qte === 0) return;
      const ds = calcDS(r);
      const total = calcTotal(r);
      csv += `${c.label};${r.designation};${r.unite};${r.qte};${r.tmo};${r.pfourniture};${ds.toFixed(2)};${total.toFixed(2)}\n`;
    });
  });
  const blob = new Blob(['\uFEFF' + csv], { type: 'text/csv;charset=utf-8;' });
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = 'chiffrage_btp.csv';
  a.click();
}

// Init
renderTabs();
renderTable();
recalcAll();
</script>
</body>
</html>