calculadora


Calculadora de pintura de Andrés Merino

<!doctype html>
<html lang="es">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title>Calculadora de pintura de Andrés Merino</title>

  <style>
    :root{
      --brand:#046BD2;
      --brand-2:#1E88FF;   /* botón y acentos (armoniza con el azul principal) */
      --brand-3:#0E5FB8;   /* hover */
      --card:#ffffff;
      --text:#0b0f18;
      --muted:#556070;
      --border:rgba(11,15,24,.14);
      --shadow:0 18px 45px rgba(0,0,0,.18);
      --radius:18px;
    }

    *{box-sizing:border-box}

    body{
      margin:0;
      font-family: system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif;
      background:var(--brand);
      color:#fff;
      padding:34px 14px 56px;
    }

    .wrap{max-width:980px; margin:0 auto;}

    header{
      display:flex;
      flex-direction:column;
      align-items:center;
      gap:10px;
      margin-bottom:18px;
      text-align:center;
    }

    h1{
      margin:0;
      font-size:28px;
      letter-spacing:.2px;
      font-weight:800;
    }

    .subtitle{
      margin:0;
      opacity:.9;
      font-size:14px;
    }

    .card{
      background:var(--card);
      color:var(--text);
      border-radius:var(--radius);
      box-shadow:var(--shadow);
      padding:22px;
      border:1px solid rgba(255,255,255,.12);
    }

    /* GRID PROLIJO: evita que algunas columnas “empujen” y desalineen */
    .grid{
      display:grid;
      grid-template-columns: repeat(2, minmax(0, 1fr));
      gap:14px 16px;
      margin-top:16px;
      align-items:start;
    }

    /* Cada campo tiene wrapper para mantener alturas y márgenes coherentes */
    .field{display:flex; flex-direction:column; min-width:0;}

    .span2{grid-column:1 / -1;}

    label{
      display:block;
      font-size:13px;
      color:var(--muted);
      margin-bottom:6px;
      font-weight:700;
    }

    input, select{
      width:100%;
      padding:12px 12px;
      border-radius:12px;
      border:1px solid var(--border);
      font-size:15px;
      outline:none;
      background:#fff;
      height:44px;          /* fuerza misma altura en todos */
    }

    input:focus, select:focus{
      border-color: rgba(30,136,255,.7);
      box-shadow: 0 0 0 3px rgba(30,136,255,.18);
    }

    .hint{
      margin-top:6px;
      font-size:12px;
      color:var(--muted);
      opacity:.9;
      line-height:1.25;
    }

    .btn{
      margin-top:16px;
      width:100%;
      padding:14px 16px;
      border:none;
      border-radius:14px;
      background: var(--brand-2);
      color:#fff;
      font-size:16px;
      font-weight:800;
      cursor:pointer;
      transition: transform .06s ease, filter .15s ease, background .15s ease;
      height:48px;
    }
    .btn:hover{background:var(--brand-3);}
    .btn:active{transform: translateY(1px);}

    .result{
      margin-top:16px;
      background:#fff;
      color:var(--text);
      border-radius:var(--radius);
      box-shadow: var(--shadow);
      padding:20px;
      border:1px solid rgba(255,255,255,.12);
    }

    .result h2{
      margin:0 0 12px;
      font-size:16px;
      color:#111;
    }

    .rows{
      display:grid;
      grid-template-columns: 1fr auto;
      gap:10px 14px;
      align-items:center;
      font-size:15px;
    }
    .rows .k{color:var(--muted)}
    .rows .v{font-weight:900}

    .note{
      margin-top:10px;
      font-size:12px;
      color:var(--muted);
      line-height:1.35;
    }

    .error{
      margin-top:12px;
      padding:12px 12px;
      border-radius:12px;
      background: rgba(255, 76, 76, .10);
      border:1px solid rgba(255, 76, 76, .28);
      color:#8a1111;
      font-size:13px;
      display:none;
    }

    @media (max-width: 760px){
      .grid{grid-template-columns:1fr}
      h1{font-size:24px}
    }
  </style>
</head>

<body>
  <div class="wrap">
    <header>
      <h1>Calculadora de pintura</h1>
      <p class="subtitle">de Andrés Merino</p>
    </header>

    <section class="card" aria-label="Formulario calculadora">
      <div class="grid">

        <div class="field span2">
          <label for="tipo">¿Qué vas a pintar?</label>
          <select id="tipo">
            <option value="paredes" selected>Paredes</option>
            <option value="techo">Techo</option>
            <option value="fachada">Fachada</option>
          </select>
        </div>

        <div class="field">
          <label for="alto">Alto (m)</label>
          <input id="alto" type="number" inputmode="decimal" step="0.01" value="2.50" min="0" />
        </div>

        <div class="field">
          <label for="manos">N° de manos</label>
          <input id="manos" type="number" inputmode="numeric" step="1" value="3" min="1" />
        </div>

        <div class="field" id="field-perimetro">
          <label for="perimetro">Perímetro total (m)</label>
          <input id="perimetro" type="number" inputmode="decimal" step="0.01" value="12" min="0" />
        </div>

        <div class="field" id="field-aberturas">
          <label for="aberturas">Aberturas (m²)</label>
          <input id="aberturas" type="number" inputmode="decimal" step="0.01" value="2" min="0" />
        </div>

        <div class="field" id="field-superficie-techo" style="display:none;">
          <label for="superficieTecho">Superficie techo (m²)</label>
          <input id="superficieTecho" type="number" inputmode="decimal" step="0.01" value="20" min="0" />
        </div>

        <div class="field">
          <label for="rendimiento">Rendimiento (m² por litro)</label>
          <input id="rendimiento" type="number" inputmode="decimal" step="0.1" value="10" min="0.1" />
        </div>

        <div class="field">
          <label for="merma">Extra por merma (%)</label>
          <input id="merma" type="number" inputmode="decimal" step="1" value="10" min="0" />
        </div>

        <div class="field span2">
          <label for="presentaciones">Presentaciones disponibles (litros)</label>
          <input id="presentaciones" type="text" value="1,4,10,20" />
          <div class="hint">Separadas por coma. Ej: 1,4,10,20</div>
        </div>

        <div class="span2">
          <button class="btn" id="btnCalcular" type="button">Calcular pintura necesaria</button>
          <div class="error" id="errorBox"></div>
        </div>

      </div>
    </section>

    <section class="result" id="resultBox" style="display:none;" aria-label="Resultado">
      <h2>Resultado</h2>
      <div class="rows">
        <div class="k">Superficie base</div><div class="v" id="outSuperficie">—</div>
        <div class="k">Litros estimados</div><div class="v" id="outLitros">—</div>
        <div class="k">Sugerencia de compra</div><div class="v" id="outSugerencia">—</div>
      </div>
      <div class="note" id="outNota"></div>
    </section>
  </div>

  <script>
    const $ = (id) => document.getElementById(id);

    function parsePresentaciones(raw){
      const arr = raw
        .split(',')
        .map(s => s.trim().replace(',', '.'))
        .filter(Boolean)
        .map(Number)
        .filter(n => Number.isFinite(n) && n > 0);

      // unique + orden asc
      return [...new Set(arr)].sort((a,b)=>a-b);
    }

    function formatM2(n){ return `${n.toFixed(2)} m²`; }
    function formatL(n){ return `${n.toFixed(2)} L`; }

    // Sugerencia simple: greedy desde la presentación más grande
    function sugerirCompra(litrosNecesarios, presentaciones){
      let restante = litrosNecesarios;
      const picks = [];
      const sizes = [...presentaciones].sort((a,b)=>b-a);

      for (const s of sizes){
        if (s <= 0) continue;
        const qty = Math.floor(restante / s);
        if (qty > 0){
          picks.push([qty, s]);
          restante -= qty * s;
        }
      }

      // Si falta algo, completar con la más chica para cubrir
      if (restante > 1e-9){
        const smallest = Math.min(...presentaciones);
        const qty = Math.ceil(restante / smallest);
        picks.push([qty, smallest]);
        restante = 0;
      }

      const total = picks.reduce((acc,[q,s]) => acc + q*s, 0);
      const text = picks.map(([q,s]) => `${q} x ${s}L`).join(' + ');
      return { text: `${text} (Total: ${total.toFixed(1)} L)`, total };
    }

    function updateFieldsByTipo(){
      const tipo = $('tipo').value;
      const isTecho = (tipo === 'techo');

      $('field-perimetro').style.display = isTecho ? 'none' : 'flex';
      $('field-aberturas').style.display = isTecho ? 'none' : 'flex';
      $('field-superficie-techo').style.display = isTecho ? 'flex' : 'none';
    }

    function showError(msg){
      const box = $('errorBox');
      box.textContent = msg;
      box.style.display = 'block';
      $('resultBox').style.display = 'none';
    }

    function clearError(){
      const box = $('errorBox');
      box.textContent = '';
      box.style.display = 'none';
    }

    function calcular(){
      clearError();

      const tipo = $('tipo').value;
      const manos = Number($('manos').value);
      const rendimiento = Number($('rendimiento').value);
      const mermaPct = Number($('merma').value);
      const pres = parsePresentaciones($('presentaciones').value);

      if (!Number.isFinite(manos) || manos < 1) return showError('Ingresá un número de manos válido (mínimo 1).');
      if (!Number.isFinite(rendimiento) || rendimiento <= 0) return showError('Ingresá un rendimiento válido (mayor a 0).');
      if (!Number.isFinite(mermaPct) || mermaPct < 0) return showError('Ingresá una merma válida (0 o más).');
      if (pres.length === 0) return showError('Ingresá al menos una presentación válida, separada por comas (ej: 1,4,10,20).');

      let superficieBase = 0;

      if (tipo === 'techo'){
        const supTecho = Number($('superficieTecho').value);
        if (!Number.isFinite(supTecho) || supTecho <= 0) return showError('Ingresá una superficie de techo válida (mayor a 0).');
        superficieBase = supTecho;
      } else {
        const alto = Number($('alto').value);
        const perimetro = Number($('perimetro').value);
        const aberturas = Number($('aberturas').value);

        if (!Number.isFinite(alto) || alto <= 0) return showError('Ingresá un alto válido (mayor a 0).');
        if (!Number.isFinite(perimetro) || perimetro <= 0) return showError('Ingresá un perímetro válido (mayor a 0).');
        if (!Number.isFinite(aberturas) || aberturas < 0) return showError('Ingresá aberturas válidas (0 o más).');

        superficieBase = (alto * perimetro) - aberturas;
        if (superficieBase <= 0) return showError('La superficie base quedó en 0 o menos. Revisá perímetro, alto y aberturas.');
      }

      const superficieTotal = superficieBase * manos;
      const litrosSinMerma = superficieTotal / rendimiento;
      const litrosFinal = litrosSinMerma * (1 + mermaPct/100);

      const sug = sugerirCompra(litrosFinal, pres);

      $('outSuperficie').textContent = formatM2(superficieBase);
      $('outLitros').textContent = formatL(litrosFinal);
      $('outSugerencia').textContent = sug.text;

      $('outNota').textContent =
        `Cálculo con ${manos} mano(s), rendimiento ${rendimiento} m²/L y ${mermaPct}% de merma.`;

      $('resultBox').style.display = 'block';
    }

    // Eventos
    $('tipo').addEventListener('change', updateFieldsByTipo);
    $('btnCalcular').addEventListener('click', calcular);

    // Init
    updateFieldsByTipo();
  </script>
</body>
</html>
<iframe src="/calc/" style="width:100%; height:900px; border:0; border-radius:12px;" loading="lazy"></iframe>

Scroll al inicio