{"id":2013,"date":"2026-11-13T06:11:52","date_gmt":"2026-11-13T11:11:52","guid":{"rendered":"https:\/\/dicatone.com\/?page_id=2013"},"modified":"2026-11-13T06:11:57","modified_gmt":"2026-11-13T11:11:57","slug":"cotizador","status":"publish","type":"page","link":"https:\/\/dicatone.com\/index.php\/cotizador\/","title":{"rendered":"Cotizador"},"content":{"rendered":"\n<!DOCTYPE html>\n<html lang=\"es\">\n<head>\n  <meta charset=\"UTF-8\" \/>\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" \/>\n  <title>CNC WoodQuote Pro | Cotizador Interno de Taller<\/title>\n  <!-- Tailwind CSS -->\n  <script src=\"https:\/\/cdn.tailwindcss.com\"><\/script>\n  <!-- SheetJS (xlsx) para importar\/exportar configuraci\u00f3n en Excel -->\n  <script src=\"https:\/\/cdn.sheetjs.com\/xlsx-0.20.1\/package\/dist\/xlsx.full.min.js\"><\/script>\n  <!-- Google Fonts: Inter & JetBrains Mono para datos t\u00e9cnicos -->\n  <link rel=\"preconnect\" href=\"https:\/\/fonts.googleapis.com\">\n  <link rel=\"preconnect\" href=\"https:\/\/fonts.gstatic.com\" crossorigin>\n  <link href=\"https:\/\/fonts.googleapis.com\/css2?family=Inter:wght@400;500;600;700;800&#038;family=JetBrains+Mono:wght@400;600;700&#038;display=swap\" rel=\"stylesheet\">\n\n  <script>\n    tailwind.config = {\n      theme: {\n        extend: {\n          fontFamily: {\n            sans: ['Inter', 'sans-serif'],\n            mono: ['JetBrains Mono', 'monospace'],\n          },\n          colors: {\n            slate: {\n              850: '#172033',\n              900: '#0f172a',\n              950: '#090d16',\n            },\n            steel: {\n              400: '#60a5fa',\n              500: '#3b82f6',\n              600: '#2563eb',\n              700: '#1d4ed8',\n              800: '#1e3a8a',\n            },\n            ambertech: {\n              400: '#fbbf24',\n              500: '#f59e0b',\n              600: '#d97706',\n            }\n          }\n        }\n      }\n    }\n  <\/script>\n\n  <style>\n    \/* Ajustes personalizados de barra de desplazamiento y controles industriales *\/\n    body {\n      background-color: #0b1120;\n      color: #e2e8f0;\n      -webkit-tap-highlight-color: transparent;\n    }\n    input[type=\"number\"]::-webkit-inner-spin-button,\n    input[type=\"number\"]::-webkit-outer-spin-button {\n      opacity: 1;\n    }\n    .glass-panel {\n      background: linear-gradient(145deg, rgba(30, 41, 59, 0.85), rgba(15, 23, 42, 0.95));\n      border: 1px solid rgba(71, 85, 105, 0.45);\n      box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.4);\n    }\n    .input-industrial {\n      background-color: #0f172a;\n      border: 1px solid #334155;\n      color: #f8fafc;\n      transition: all 0.15s ease-in-out;\n    }\n    .input-industrial:focus {\n      outline: none;\n      border-color: #38bdf8;\n      box-shadow: 0 0 0 2px rgba(56, 189, 248, 0.25);\n    }\n    \/* Notificaci\u00f3n Toast *\/\n    #toast-container {\n      position: fixed;\n      bottom: 1.25rem;\n      right: 1.25rem;\n      z-index: 50;\n      display: flex;\n      flex-direction: column;\n      gap: 0.5rem;\n    }\n  <\/style>\n<\/head>\n<body class=\"min-h-screen flex flex-col antialiased selection:bg-sky-500 selection:text-white\">\n\n  <!-- Barra Superior Industrial -->\n  <header class=\"sticky top-0 z-40 bg-slate-950\/95 backdrop-blur-md border-b border-slate-800 shadow-lg\">\n    <div class=\"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8\">\n      <div class=\"flex flex-col sm:flex-row sm:items-center sm:justify-between py-3 gap-3\">\n        <!-- Logo y T\u00edtulo -->\n        <div class=\"flex items-center justify-between\">\n          <div class=\"flex items-center gap-3\">\n            <div class=\"w-10 h-10 rounded-xl bg-gradient-to-br from-sky-500 to-blue-700 flex items-center justify-center shadow-md shadow-sky-900\/40 border border-sky-400\/30\">\n              <!-- Icono Router CNC SVG -->\n              <svg class=\"w-6 h-6 text-white\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" viewBox=\"0 0 24 24\">\n                <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M4 5h16M7 5v4m10-4v4M9 9h6l-1.5 6h-3L9 9zm3 6v4m-7 0h14\" \/>\n              <\/svg>\n            <\/div>\n            <div>\n              <div class=\"flex items-center gap-2\">\n                <h1 class=\"text-lg sm:text-xl font-extrabold tracking-tight text-white\">CNC WOODQUOTE<\/h1>\n                <span class=\"px-2 py-0.5 text-[10px] font-mono font-bold uppercase tracking-wider bg-sky-500\/20 text-sky-300 border border-sky-500\/30 rounded-md\">Taller Interno<\/span>\n              <\/div>\n              <p class=\"text-xs text-slate-400\">C\u00e1lculo de Materia Prima, Tiempos de Mecanizado &#038; Costos (USD)<\/p>\n            <\/div>\n          <\/div>\n        <\/div>\n\n        <!-- Navegaci\u00f3n por Pesta\u00f1as -->\n        <nav class=\"flex bg-slate-900 p-1 rounded-xl border border-slate-800 w-full sm:w-auto\">\n          <button id=\"tab-btn-cotizador\" onclick=\"switchTab('cotizador')\" class=\"flex-1 sm:flex-none flex items-center justify-center gap-2 px-4 py-2 rounded-lg text-sm font-semibold transition-all bg-sky-600 text-white shadow\">\n            <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" viewBox=\"0 0 24 24\">\n              <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 7h6m0 10v-3m-3 3h.01M9 17h.01M9 14h.01M12 14h.01M15 11h.01M12 11h.01M9 11h.01M7 21h10a2 2 0 002-2V5a2 2 0 00-2-2H7a2 2 0 00-2 2v14a2 2 0 002 2z\" \/>\n            <\/svg>\n            Cotizador\n          <\/button>\n          <button id=\"tab-btn-config\" onclick=\"switchTab('config')\" class=\"flex-1 sm:flex-none flex items-center justify-center gap-2 px-4 py-2 rounded-lg text-sm font-semibold transition-all text-slate-400 hover:text-white\">\n            <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" viewBox=\"0 0 24 24\">\n              <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z\" \/>\n              <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M15 12a3 3 0 11-6 0 3 3 0 016 0z\" \/>\n            <\/svg>\n            Configuraci\u00f3n de Precios\n          <\/button>\n        <\/nav>\n      <\/div>\n    <\/div>\n  <\/header>\n\n  <!-- CONTENIDO PRINCIPAL -->\n  <main class=\"flex-1 max-w-7xl w-full mx-auto px-4 sm:px-6 lg:px-8 py-6\">\n\n    <!-- ========================================================= -->\n    <!-- PESTA\u00d1A 1: COTIZADOR CNC                                  -->\n    <!-- ========================================================= -->\n    <section id=\"view-cotizador\" class=\"grid grid-cols-1 lg:grid-cols-12 gap-6 items-start\">\n\n      <!-- COLUMNA IZQUIERDA: FORMULARIO DE PAR\u00c1METROS (7 cols en desktop) -->\n      <div class=\"lg:col-span-7 space-y-5\">\n\n        <!-- 1. Tipo de Producto y Cantidad -->\n        <div class=\"glass-panel rounded-2xl p-5\">\n          <div class=\"flex items-center gap-2 mb-4 border-b border-slate-800 pb-3\">\n            <span class=\"w-6 h-6 rounded-md bg-sky-500\/20 text-sky-400 font-mono text-xs font-bold flex items-center justify-center border border-sky-500\/30\">01<\/span>\n            <h2 class=\"text-base font-bold text-white uppercase tracking-wide\">Modalidad y Volumen de Lote<\/h2>\n          <\/div>\n\n          <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-4\">\n            <!-- Tipo de Producto -->\n            <div>\n              <label class=\"block text-xs font-semibold uppercase tracking-wider text-slate-400 mb-2\">Tipo de Producto<\/label>\n              <div class=\"grid grid-cols-2 gap-2\">\n                <button type=\"button\" id=\"btn-tipo-general\" onclick=\"setProductType('generalista')\" class=\"p-3 rounded-xl border text-left transition-all border-sky-500 bg-sky-500\/15 text-white\">\n                  <div class=\"text-xs font-bold\">Generalista<\/div>\n                  <div class=\"text-[11px] text-sky-300 font-mono mt-0.5\">Setup: $0.00<\/div>\n                <\/button>\n                <button type=\"button\" id=\"btn-tipo-custom\" onclick=\"setProductType('personalizado')\" class=\"p-3 rounded-xl border text-left transition-all border-slate-700 bg-slate-900\/70 text-slate-400 hover:border-slate-600\">\n                  <div class=\"text-xs font-bold\">Personalizado<\/div>\n                  <div id=\"label-setup-btn\" class=\"text-[11px] text-slate-400 font-mono mt-0.5\">Setup: +$10.00<\/div>\n                <\/button>\n              <\/div>\n            <\/div>\n\n            <!-- Cantidad de Unidades -->\n            <div>\n              <div class=\"flex items-center justify-between mb-2\">\n                <label for=\"input-cantidad\" class=\"block text-xs font-semibold uppercase tracking-wider text-slate-400\">Cantidad (Unidades)<\/label>\n                <span id=\"badge-descuento\" class=\"text-[11px] font-mono px-2 py-0.5 rounded bg-slate-800 text-slate-300 border border-slate-700\">Rango 1-2 (0%)<\/span>\n              <\/div>\n              <div class=\"flex items-center gap-2\">\n                <button type=\"button\" onclick=\"adjustQty(-1)\" class=\"w-11 h-11 rounded-xl bg-slate-800 hover:bg-slate-700 border border-slate-700 text-white font-bold text-lg flex items-center justify-center active:scale-95\">&#8211;<\/button>\n                <input type=\"number\" id=\"input-cantidad\" min=\"1\" step=\"1\" value=\"1\" oninput=\"calculateQuote()\" class=\"input-industrial w-full h-11 rounded-xl px-3 text-center font-mono font-bold text-lg\" \/>\n                <button type=\"button\" onclick=\"adjustQty(1)\" class=\"w-11 h-11 rounded-xl bg-slate-800 hover:bg-slate-700 border border-slate-700 text-white font-bold text-lg flex items-center justify-center active:scale-95\">+<\/button>\n              <\/div>\n              <!-- Accesos r\u00e1pidos de lote -->\n              <div class=\"flex gap-1.5 mt-2\">\n                <button type=\"button\" onclick=\"setQty(1)\" class=\"flex-1 py-1 text-[11px] font-mono rounded-lg bg-slate-800\/80 hover:bg-slate-700 text-slate-300 border border-slate-700\/70\">1 ud<\/button>\n                <button type=\"button\" onclick=\"setQty(6)\" class=\"flex-1 py-1 text-[11px] font-mono rounded-lg bg-slate-800\/80 hover:bg-slate-700 text-slate-300 border border-slate-700\/70\">6 uds<\/button>\n                <button type=\"button\" onclick=\"setQty(12)\" class=\"flex-1 py-1 text-[11px] font-mono rounded-lg bg-slate-800\/80 hover:bg-slate-700 text-slate-300 border border-slate-700\/70\">12 uds<\/button>\n                <button type=\"button\" onclick=\"setQty(50)\" class=\"flex-1 py-1 text-[11px] font-mono rounded-lg bg-slate-800\/80 hover:bg-slate-700 text-slate-300 border border-slate-700\/70\">50+ uds<\/button>\n              <\/div>\n            <\/div>\n          <\/div>\n        <\/div>\n\n        <!-- 2. Materia Prima y Dimensiones -->\n        <div class=\"glass-panel rounded-2xl p-5\">\n          <div class=\"flex items-center justify-between mb-4 border-b border-slate-800 pb-3\">\n            <div class=\"flex items-center gap-2\">\n              <span class=\"w-6 h-6 rounded-md bg-sky-500\/20 text-sky-400 font-mono text-xs font-bold flex items-center justify-center border border-sky-500\/30\">02<\/span>\n              <h2 class=\"text-base font-bold text-white uppercase tracking-wide\">Materia Prima y Geometr\u00eda<\/h2>\n            <\/div>\n            <span id=\"material-type-tag\" class=\"text-[11px] font-mono px-2.5 py-0.5 rounded-full bg-amber-500\/15 text-amber-300 border border-amber-500\/30\">Madera S\u00f3lida<\/span>\n          <\/div>\n\n          <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-4 mb-4\">\n            <!-- Selecci\u00f3n de Madera \/ Tablero -->\n            <div>\n              <label for=\"select-material\" class=\"block text-xs font-semibold uppercase tracking-wider text-slate-400 mb-1.5\">Material<\/label>\n              <select id=\"select-material\" onchange=\"onMaterialChange()\" class=\"input-industrial w-full h-11 rounded-xl px-3 text-sm font-medium\"><\/select>\n            <\/div>\n\n            <!-- Selecci\u00f3n de Formato \/ Espesor -->\n            <div>\n              <label for=\"select-formato\" class=\"block text-xs font-semibold uppercase tracking-wider text-slate-400 mb-1.5\">Formato \/ Espesor Materia Prima<\/label>\n              <select id=\"select-formato\" onchange=\"onFormatChange()\" class=\"input-industrial w-full h-11 rounded-xl px-3 text-sm font-medium\"><\/select>\n            <\/div>\n          <\/div>\n\n          <!-- Dimensiones de la Pieza -->\n          <div class=\"grid grid-cols-1 sm:grid-cols-3 gap-4\">\n            <div>\n              <label for=\"input-largo\" class=\"block text-xs font-semibold uppercase tracking-wider text-slate-400 mb-1.5\">Largo Pieza (mm)<\/label>\n              <div class=\"relative\">\n                <input type=\"number\" id=\"input-largo\" min=\"10\" max=\"2400\" step=\"1\" value=\"300\" oninput=\"calculateQuote()\" class=\"input-industrial w-full h-11 rounded-xl pl-3 pr-10 font-mono font-bold text-base\" \/>\n                <span class=\"absolute right-3 top-3 text-xs font-mono text-slate-500\">mm<\/span>\n              <\/div>\n            <\/div>\n\n            <div>\n              <label for=\"input-ancho\" class=\"block text-xs font-semibold uppercase tracking-wider text-slate-400 mb-1.5\">Ancho Pieza (mm)<\/label>\n              <div class=\"relative\">\n                <input type=\"number\" id=\"input-ancho\" min=\"10\" max=\"2400\" step=\"1\" value=\"180\" oninput=\"calculateQuote()\" class=\"input-industrial w-full h-11 rounded-xl pl-3 pr-10 font-mono font-bold text-base\" \/>\n                <span class=\"absolute right-3 top-3 text-xs font-mono text-slate-500\">mm<\/span>\n              <\/div>\n            <\/div>\n\n            <div>\n              <label for=\"input-espesor-corte\" class=\"block text-xs font-semibold uppercase tracking-wider text-slate-400 mb-1.5\">Espesor a Cortar (mm)<\/label>\n              <div class=\"relative\">\n                <input type=\"number\" id=\"input-espesor-corte\" min=\"1\" max=\"100\" step=\"0.5\" value=\"18\" oninput=\"calculateQuote()\" class=\"input-industrial w-full h-11 rounded-xl pl-3 pr-10 font-mono font-bold text-base\" \/>\n                <span class=\"absolute right-3 top-3 text-xs font-mono text-slate-500\">mm<\/span>\n              <\/div>\n            <\/div>\n          <\/div>\n\n          <!-- Alerta de Validaci\u00f3n Dimensional -->\n          <div id=\"dimension-alert\" class=\"hidden mt-4 p-3 rounded-xl bg-red-500\/15 border border-red-500\/40 text-red-200 text-xs flex items-start gap-2.5\">\n            <svg class=\"w-5 h-5 text-red-400 shrink-0 mt-0.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" viewBox=\"0 0 24 24\">\n              <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\" \/>\n            <\/svg>\n            <span id=\"dimension-alert-text\"><\/span>\n          <\/div>\n\n          <!-- Indicador Visual de Aprovechamiento -->\n          <div id=\"nesting-info-box\" class=\"mt-4 p-3 rounded-xl bg-slate-900\/90 border border-slate-800 flex flex-wrap items-center justify-between gap-2 text-xs\">\n            <div class=\"flex items-center gap-2 text-slate-300\">\n              <svg class=\"w-4 h-4 text-sky-400\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" viewBox=\"0 0 24 24\">\n                <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2V6zM14 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V6zM4 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2v-2zM14 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z\" \/>\n              <\/svg>\n              <span id=\"nesting-summary-text\">Calculando aprovechamiento&#8230;<\/span>\n            <\/div>\n            <span id=\"feedrate-badge\" class=\"font-mono text-[11px] px-2 py-0.5 rounded bg-slate-800 text-sky-300 border border-slate-700\">Avance: 4000 mm\/min<\/span>\n          <\/div>\n        <\/div>\n\n        <!-- 3. Operaciones CNC: Cajeras y Grabado -->\n        <div class=\"glass-panel rounded-2xl p-5\">\n          <div class=\"flex items-center gap-2 mb-4 border-b border-slate-800 pb-3\">\n            <span class=\"w-6 h-6 rounded-md bg-sky-500\/20 text-sky-400 font-mono text-xs font-bold flex items-center justify-center border border-sky-500\/30\">03<\/span>\n            <h2 class=\"text-base font-bold text-white uppercase tracking-wide\">Operaciones CNC (Cajeras y Grabado)<\/h2>\n          <\/div>\n\n          <!-- Subsecci\u00f3n Cajeras -->\n          <div class=\"mb-5\">\n            <div class=\"flex items-center justify-between mb-2\">\n              <span class=\"text-xs font-bold uppercase tracking-wider text-slate-300\">A. Mecanizado de Cajeras (Vaciado)<\/span>\n              <span id=\"pocket-vol-preview\" class=\"text-[11px] font-mono text-slate-400\">Volumen: 0 cm\u00b3<\/span>\n            <\/div>\n            <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-3\">\n              <div>\n                <label for=\"select-cajera-area\" class=\"block text-xs text-slate-400 mb-1\">\u00c1rea ocupada por cajeras<\/label>\n                <select id=\"select-cajera-area\" onchange=\"calculateQuote()\" class=\"input-industrial w-full h-11 rounded-xl px-3 text-sm\">\n                  <option value=\"0\">Sin cajeras (0%)<\/option>\n                  <option value=\"0.25\">Menos de la mitad (25%)<\/option>\n                  <option value=\"0.50\">Alrededor de la mitad (50%)<\/option>\n                  <option value=\"0.75\">M\u00e1s de la mitad (75%)<\/option>\n                <\/select>\n              <\/div>\n              <div>\n                <label for=\"select-cajera-prof\" class=\"block text-xs text-slate-400 mb-1\">Profundidad respecto al espesor<\/label>\n                <select id=\"select-cajera-prof\" onchange=\"calculateQuote()\" class=\"input-industrial w-full h-11 rounded-xl px-3 text-sm\">\n                  <option value=\"0\">Sin profundidad (0%)<\/option>\n                  <option value=\"0.25\">Menos de la mitad (25%)<\/option>\n                  <option value=\"0.50\">Alrededor de la mitad (50%)<\/option>\n                  <option value=\"0.75\">M\u00e1s de la mitad (75%)<\/option>\n                <\/select>\n              <\/div>\n            <\/div>\n          <\/div>\n\n          <!-- Subsecci\u00f3n Grabado -->\n          <div class=\"pt-4 border-t border-slate-800\/80\">\n            <div class=\"flex items-center justify-between mb-2\">\n              <span class=\"text-xs font-bold uppercase tracking-wider text-slate-300\">B. Grabado Superficial<\/span>\n              <span id=\"engrave-rate-preview\" class=\"text-[11px] font-mono text-slate-400\">Tarifa: $0.05 \/ cm\u00b2<\/span>\n            <\/div>\n            <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-3\">\n              <div>\n                <label for=\"input-grabado-area\" class=\"block text-xs text-slate-400 mb-1\">\u00c1rea de grabado (cm\u00b2)<\/label>\n                <div class=\"relative\">\n                  <input type=\"number\" id=\"input-grabado-area\" min=\"0\" step=\"1\" value=\"0\" oninput=\"calculateQuote()\" class=\"input-industrial w-full h-11 rounded-xl pl-3 pr-12 font-mono font-bold text-base\" \/>\n                  <span class=\"absolute right-3 top-3 text-xs font-mono text-slate-500\">cm\u00b2<\/span>\n                <\/div>\n              <\/div>\n              <div>\n                <label for=\"select-grabado-nivel\" class=\"block text-xs text-slate-400 mb-1\">Nivel de complejidad<\/label>\n                <select id=\"select-grabado-nivel\" onchange=\"calculateQuote()\" class=\"input-industrial w-full h-11 rounded-xl px-3 text-sm\">\n                  <option value=\"bajo\">Bajo (L\u00edneas \/ Vectores simples)<\/option>\n                  <option value=\"medio\">Medio (Texto detallado \/ V-Carve)<\/option>\n                  <option value=\"alto\">Alto (Relieve 3D \/ Trama densa)<\/option>\n                <\/select>\n              <\/div>\n            <\/div>\n          <\/div>\n        <\/div>\n\n        <!-- 4. Acabado Superficial -->\n        <div class=\"glass-panel rounded-2xl p-5\">\n          <div class=\"flex items-center gap-2 mb-4 border-b border-slate-800 pb-3\">\n            <span class=\"w-6 h-6 rounded-md bg-sky-500\/20 text-sky-400 font-mono text-xs font-bold flex items-center justify-center border border-sky-500\/30\">04<\/span>\n            <h2 class=\"text-base font-bold text-white uppercase tracking-wide\">Acabado Superficial<\/h2>\n          <\/div>\n\n          <div class=\"grid grid-cols-1 sm:grid-cols-3 gap-2.5\">\n            <button type=\"button\" id=\"btn-finish-ninguno\" onclick=\"setFinish('ninguno')\" class=\"p-3 rounded-xl border text-left transition-all border-sky-500 bg-sky-500\/15 text-white\">\n              <div class=\"text-xs font-bold\">Sin Acabados<\/div>\n              <div id=\"label-finish-ninguno\" class=\"text-[11px] font-mono text-sky-300 mt-0.5\">+0% (Crudo)<\/div>\n            <\/button>\n            <button type=\"button\" id=\"btn-finish-estandar\" onclick=\"setFinish('estandar')\" class=\"p-3 rounded-xl border text-left transition-all border-slate-700 bg-slate-900\/70 text-slate-400 hover:border-slate-600\">\n              <div class=\"text-xs font-bold\">Acabado Est\u00e1ndar<\/div>\n              <div id=\"label-finish-estandar\" class=\"text-[11px] font-mono text-slate-400 mt-0.5\">+20% (Lijado\/Aceite)<\/div>\n            <\/button>\n            <button type=\"button\" id=\"btn-finish-premium\" onclick=\"setFinish('premium')\" class=\"p-3 rounded-xl border text-left transition-all border-slate-700 bg-slate-900\/70 text-slate-400 hover:border-slate-600\">\n              <div class=\"text-xs font-bold\">Acabado Premium<\/div>\n              <div id=\"label-finish-premium\" class=\"text-[11px] font-mono text-slate-400 mt-0.5\">+40% (Lacado\/Barniz)<\/div>\n            <\/button>\n          <\/div>\n        <\/div>\n      <\/div>\n\n      <!-- COLUMNA DERECHA: RESUMEN T\u00c9CNICO Y COTIZACI\u00d3N FINAL (5 cols en desktop) -->\n      <div class=\"lg:col-span-5 lg:sticky lg:top-24 space-y-5\">\n        <div class=\"glass-panel rounded-2xl overflow-hidden border-sky-500\/30\">\n          <!-- Cabecera del Ticket -->\n          <div class=\"bg-gradient-to-r from-slate-900 via-slate-800 to-slate-900 px-5 py-4 border-b border-slate-700\/80 flex items-center justify-between\">\n            <div>\n              <span class=\"text-[11px] font-mono uppercase tracking-widest text-sky-400 font-bold\">Orden de C\u00e1lculo<\/span>\n              <h3 class=\"text-lg font-extrabold text-white\">Resumen de Cotizaci\u00f3n<\/h3>\n            <\/div>\n            <button type=\"button\" onclick=\"copyQuoteSummary()\" class=\"flex items-center gap-1.5 px-3 py-2 rounded-xl bg-sky-600 hover:bg-sky-500 active:scale-95 text-white text-xs font-bold shadow-lg shadow-sky-900\/40 transition-all\">\n              <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" viewBox=\"0 0 24 24\">\n                <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3\" \/>\n              <\/svg>\n              Copiar Resumen\n            <\/button>\n          <\/div>\n\n          <!-- Precios Principales Destacados -->\n          <div class=\"p-5 bg-slate-950\/60 border-b border-slate-800\">\n            <div class=\"grid grid-cols-2 gap-4\">\n              <div class=\"p-3.5 rounded-xl bg-slate-900\/90 border border-slate-800\">\n                <div class=\"text-[11px] uppercase tracking-wider text-slate-400 font-semibold\">Precio Unitario Final<\/div>\n                <div id=\"res-unit-price\" class=\"text-2xl sm:text-3xl font-mono font-extrabold text-white mt-1\">$0.00<\/div>\n                <div id=\"res-unit-subnote\" class=\"text-[11px] text-slate-400 mt-0.5\">Incluye prorrateo setup<\/div>\n              <\/div>\n              <div class=\"p-3.5 rounded-xl bg-gradient-to-br from-sky-950\/80 to-blue-950\/60 border border-sky-500\/40\">\n                <div class=\"text-[11px] uppercase tracking-wider text-sky-300 font-semibold\">Total Lote (<span id=\"res-qty-label\">1<\/span> ud)<\/div>\n                <div id=\"res-total-price\" class=\"text-2xl sm:text-3xl font-mono font-extrabold text-sky-400 mt-1\">$0.00<\/div>\n                <div id=\"res-discount-note\" class=\"text-[11px] text-emerald-400 font-mono mt-0.5\">Sin descuento<\/div>\n              <\/div>\n            <\/div>\n          <\/div>\n\n          <!-- Desglose T\u00e9cnico de Tiempos y Aprovechamiento -->\n          <div class=\"p-5 space-y-4\">\n            <div>\n              <h4 class=\"text-xs font-bold uppercase tracking-wider text-slate-400 mb-2.5 flex items-center gap-1.5\">\n                <svg class=\"w-3.5 h-3.5 text-sky-400\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" viewBox=\"0 0 24 24\">\n                  <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z\" \/>\n                <\/svg>\n                Par\u00e1metros T\u00e9cnicos de Mecanizado\n              <\/h4>\n              <div class=\"grid grid-cols-2 gap-2 text-xs font-mono bg-slate-900\/80 p-3 rounded-xl border border-slate-800\/90\">\n                <div class=\"text-slate-400\">Pasadas Corte (\u00d86mm):<\/div>\n                <div id=\"tech-passes\" class=\"text-right text-slate-200 font-semibold\">0 pasadas<\/div>\n\n                <div class=\"text-slate-400\">Tiempo Corte Perimetral:<\/div>\n                <div id=\"tech-cut-time\" class=\"text-right text-slate-200 font-semibold\">0.00 min<\/div>\n\n                <div class=\"text-slate-400\">Tiempo Cajeras (Vaciado):<\/div>\n                <div id=\"tech-pocket-time\" class=\"text-right text-slate-200 font-semibold\">0.00 min<\/div>\n\n                <div class=\"text-slate-400 border-t border-slate-800 pt-1.5 font-bold text-slate-300\">Tiempo Total CNC \/ ud:<\/div>\n                <div id=\"tech-total-time\" class=\"text-right border-t border-slate-800 pt-1.5 text-sky-400 font-bold\">0.00 min<\/div>\n\n                <div class=\"text-slate-400\">Tiempo Total Lote:<\/div>\n                <div id=\"tech-batch-time\" class=\"text-right text-amber-300 font-semibold\">0.00 min<\/div>\n              <\/div>\n            <\/div>\n\n            <!-- Desglose Econ\u00f3mico Unitario -->\n            <div>\n              <h4 class=\"text-xs font-bold uppercase tracking-wider text-slate-400 mb-2.5 flex items-center gap-1.5\">\n                <svg class=\"w-3.5 h-3.5 text-sky-400\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" viewBox=\"0 0 24 24\">\n                  <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 14l6-6m-5.5.5h.01m4.99 5h.01M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16l3.5-2 3.5 2 3.5-2 3.5 2z\" \/>\n                <\/svg>\n                Desglose de Costos (Por Unidad)\n              <\/h4>\n              <div class=\"space-y-2 text-xs font-mono\">\n                <div class=\"flex justify-between items-center py-1 border-b border-slate-800\/60\">\n                  <div>\n                    <span class=\"text-slate-300\">Materia Prima (+20% merma)<\/span>\n                    <div id=\"cost-mat-detail\" class=\"text-[10px] text-slate-500\">Detalle aprovechamiento<\/div>\n                  <\/div>\n                  <span id=\"cost-mat\" class=\"text-slate-200 font-semibold\">$0.00<\/span>\n                <\/div>\n\n                <div class=\"flex justify-between items-center py-1 border-b border-slate-800\/60\">\n                  <div>\n                    <span class=\"text-slate-300\">Mecanizado CNC (Corte + Cajeras)<\/span>\n                    <div id=\"cost-cnc-detail\" class=\"text-[10px] text-slate-500\">Tarifa hora m\u00e1quina<\/div>\n                  <\/div>\n                  <span id=\"cost-cnc\" class=\"text-slate-200 font-semibold\">$0.00<\/span>\n                <\/div>\n\n                <div class=\"flex justify-between items-center py-1 border-b border-slate-800\/60\">\n                  <div>\n                    <span class=\"text-slate-300\">Grabado Superficial<\/span>\n                    <div id=\"cost-engrave-detail\" class=\"text-[10px] text-slate-500\">0 cm\u00b2<\/div>\n                  <\/div>\n                  <span id=\"cost-engrave\" class=\"text-slate-200 font-semibold\">$0.00<\/span>\n                <\/div>\n\n                <div class=\"flex justify-between items-center py-1 border-b border-slate-800\/60\">\n                  <span class=\"text-slate-400\">Subtotal Fabricaci\u00f3n Base<\/span>\n                  <span id=\"cost-base-sub\" class=\"text-slate-200 font-bold\">$0.00<\/span>\n                <\/div>\n\n                <div class=\"flex justify-between items-center py-1 border-b border-slate-800\/60\">\n                  <span id=\"cost-finish-label\" class=\"text-slate-300\">Acabado Superficial (0%)<\/span>\n                  <span id=\"cost-finish\" class=\"text-slate-200 font-semibold\">+$0.00<\/span>\n                <\/div>\n\n                <div class=\"flex justify-between items-center py-1 border-b border-slate-800\/60\">\n                  <span id=\"cost-discount-label\" class=\"text-emerald-400\">Descuento por Volumen (0%)<\/span>\n                  <span id=\"cost-discount\" class=\"text-emerald-400 font-semibold\">-$0.00<\/span>\n                <\/div>\n\n                <div class=\"flex justify-between items-center py-1\">\n                  <div>\n                    <span class=\"text-slate-300\">Dise\u00f1o \/ Programaci\u00f3n CAM (Setup)<\/span>\n                    <div id=\"cost-setup-detail\" class=\"text-[10px] text-slate-500\">Costo \u00fanico dividido en lote<\/div>\n                  <\/div>\n                  <span id=\"cost-setup-unit\" class=\"text-amber-300 font-semibold\">+$0.00<\/span>\n                <\/div>\n              <\/div>\n            <\/div>\n          <\/div>\n        <\/div>\n      <\/div>\n    <\/section>\n\n    <!-- ========================================================= -->\n    <!-- PESTA\u00d1A 2: CONFIGURACI\u00d3N DE PRECIOS Y PAR\u00c1METROS          -->\n    <!-- ========================================================= -->\n    <section id=\"view-config\" class=\"hidden space-y-6\">\n\n      <!-- Barra de Herramientas Excel \/ Respaldo -->\n      <div class=\"glass-panel rounded-2xl p-5 flex flex-col md:flex-row md:items-center justify-between gap-4 border-sky-500\/30\">\n        <div>\n          <h2 class=\"text-lg font-extrabold text-white\">Archivo de Configuraci\u00f3n &#038; Tarifas<\/h2>\n          <p class=\"text-xs text-slate-400 mt-0.5\">Edita los valores directamente aqu\u00ed (se guardan autom\u00e1ticamente en este dispositivo) o administra tus tarifas desde Excel (.xlsx).<\/p>\n        <\/div>\n        <div class=\"flex flex-wrap items-center gap-2.5\">\n          <button type=\"button\" onclick=\"exportConfigToExcel()\" class=\"flex items-center gap-2 px-4 py-2.5 rounded-xl bg-emerald-600 hover:bg-emerald-500 text-white text-xs font-bold shadow transition-all\">\n            <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" viewBox=\"0 0 24 24\">\n              <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4\" \/>\n            <\/svg>\n            Descargar Excel (.xlsx)\n          <\/button>\n\n          <label class=\"flex items-center gap-2 px-4 py-2.5 rounded-xl bg-sky-600 hover:bg-sky-500 text-white text-xs font-bold shadow cursor-pointer transition-all\">\n            <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" viewBox=\"0 0 24 24\">\n              <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12\" \/>\n            <\/svg>\n            Cargar Excel (.xlsx \/ .csv)\n            <input type=\"file\" id=\"excel-upload-input\" accept=\".xlsx,.xls,.csv\" onchange=\"importConfigFromExcel(event)\" class=\"hidden\" \/>\n          <\/label>\n\n          <button type=\"button\" onclick=\"resetDefaultConfig()\" class=\"flex items-center gap-1.5 px-3.5 py-2.5 rounded-xl bg-slate-800 hover:bg-red-900\/60 text-slate-300 hover:text-red-200 border border-slate-700 text-xs font-semibold transition-all\">\n            Restaurar F\u00e1brica\n          <\/button>\n        <\/div>\n      <\/div>\n\n      <div class=\"grid grid-cols-1 lg:grid-cols-2 gap-6\">\n\n        <!-- A. Precios de Madera S\u00f3lida (Tablas 200mm x 2400mm) -->\n        <div class=\"glass-panel rounded-2xl p-5\">\n          <div class=\"flex items-center justify-between mb-4 border-b border-slate-800 pb-3\">\n            <div>\n              <h3 class=\"text-sm font-bold uppercase tracking-wider text-white\">1. Maderas S\u00f3lidas (Formato 200 \u00d7 2400 mm)<\/h3>\n              <p class=\"text-[11px] text-slate-400\">Precio por unidad de Tabla (18mm) y Tabl\u00f3n (40mm) en USD<\/p>\n            <\/div>\n            <button type=\"button\" onclick=\"addSolidWoodRow()\" class=\"px-3 py-1.5 rounded-lg bg-slate-800 hover:bg-slate-700 text-sky-400 border border-slate-700 text-xs font-bold\">+ Agregar Madera<\/button>\n          <\/div>\n          <div id=\"config-solid-woods\" class=\"space-y-3\"><\/div>\n        <\/div>\n\n        <!-- B. Precios de Tableros (MDF, Plywood por cm2) -->\n        <div class=\"glass-panel rounded-2xl p-5\">\n          <div class=\"flex items-center justify-between mb-4 border-b border-slate-800 pb-3\">\n            <div>\n              <h3 class=\"text-sm font-bold uppercase tracking-wider text-white\">2. Tableros (MDF, Plywood &#8211; Costo por cm\u00b2)<\/h3>\n              <p class=\"text-[11px] text-slate-400\">Ingresa el precio por cm\u00b2 (o calcula desde plancha en Excel)<\/p>\n            <\/div>\n            <button type=\"button\" onclick=\"addSheetMaterialRow()\" class=\"px-3 py-1.5 rounded-lg bg-slate-800 hover:bg-slate-700 text-sky-400 border border-slate-700 text-xs font-bold\">+ Agregar Tablero<\/button>\n          <\/div>\n          <!-- Calculadora r\u00e1pida auxiliar de plancha a cm2 -->\n          <div class=\"mb-4 p-3 rounded-xl bg-slate-900\/90 border border-slate-800 text-xs\">\n            <div class=\"font-semibold text-sky-300 mb-1.5\">Auxiliar: Convertir precio de plancha (2440 \u00d7 1220 mm = 29,768 cm\u00b2) a $\/cm\u00b2<\/div>\n            <div class=\"flex items-center gap-2\">\n              <input type=\"number\" id=\"helper-sheet-price\" placeholder=\"Precio plancha ($)\" step=\"0.5\" oninput=\"updateSheetHelper()\" class=\"input-industrial h-9 rounded-lg px-2.5 text-xs font-mono w-40\" \/>\n              <span class=\"text-slate-400\">\u2192<\/span>\n              <span id=\"helper-sheet-cm2\" class=\"font-mono font-bold text-emerald-400\">$0.00000 \/ cm\u00b2<\/span>\n            <\/div>\n          <\/div>\n          <div id=\"config-sheet-materials\" class=\"space-y-3 max-h-[380px] overflow-y-auto pr-1\"><\/div>\n        <\/div>\n\n        <!-- C. Par\u00e1metros de M\u00e1quina CNC, Fresa y Grabado -->\n        <div class=\"glass-panel rounded-2xl p-5\">\n          <h3 class=\"text-sm font-bold uppercase tracking-wider text-white mb-4 border-b border-slate-800 pb-3\">3. Tarifas CNC, Fresa y Grabados (USD)<\/h3>\n          <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-4 text-xs\">\n            <div>\n              <label class=\"block text-slate-400 mb-1\">Tarifa Hora M\u00e1quina CNC ($\/hora)<\/label>\n              <input type=\"number\" id=\"cfg-cnc-hour-rate\" step=\"0.5\" onchange=\"saveConfigFromUI()\" class=\"input-industrial w-full h-10 rounded-xl px-3 font-mono font-bold\" \/>\n            <\/div>\n            <div>\n              <label class=\"block text-slate-400 mb-1\">Costo Setup Producto Personalizado ($)<\/label>\n              <input type=\"number\" id=\"cfg-setup-fee\" step=\"0.5\" onchange=\"saveConfigFromUI()\" class=\"input-industrial w-full h-10 rounded-xl px-3 font-mono font-bold\" \/>\n            <\/div>\n            <div>\n              <label class=\"block text-slate-400 mb-1\">Di\u00e1metro Fresa Est\u00e1ndar (mm)<\/label>\n              <input type=\"number\" id=\"cfg-tool-diameter\" step=\"0.5\" onchange=\"saveConfigFromUI()\" class=\"input-industrial w-full h-10 rounded-xl px-3 font-mono font-bold\" \/>\n            <\/div>\n            <div>\n              <label class=\"block text-slate-400 mb-1\">Ancho de Paso Cajera (mm)<\/label>\n              <input type=\"number\" id=\"cfg-pocket-stepover\" step=\"0.1\" onchange=\"saveConfigFromUI()\" class=\"input-industrial w-full h-10 rounded-xl px-3 font-mono font-bold\" \/>\n            <\/div>\n            <div>\n              <label class=\"block text-slate-400 mb-1\">Avance Maderas Duras (mm\/min)<\/label>\n              <input type=\"number\" id=\"cfg-feed-hard\" step=\"100\" onchange=\"saveConfigFromUI()\" class=\"input-industrial w-full h-10 rounded-xl px-3 font-mono font-bold\" \/>\n            <\/div>\n            <div>\n              <label class=\"block text-slate-400 mb-1\">Avance Maderas Suaves \/ Tableros (mm\/min)<\/label>\n              <input type=\"number\" id=\"cfg-feed-soft\" step=\"100\" onchange=\"saveConfigFromUI()\" class=\"input-industrial w-full h-10 rounded-xl px-3 font-mono font-bold\" \/>\n            <\/div>\n            <div>\n              <label class=\"block text-slate-400 mb-1\">Avance Desbaste Cajeras (mm\/min)<\/label>\n              <input type=\"number\" id=\"cfg-feed-pocket\" step=\"100\" onchange=\"saveConfigFromUI()\" class=\"input-industrial w-full h-10 rounded-xl px-3 font-mono font-bold\" \/>\n            <\/div>\n            <div>\n              <label class=\"block text-slate-400 mb-1\">Porcentaje Desperdicio Material (%)<\/label>\n              <input type=\"number\" id=\"cfg-waste-pct\" step=\"1\" onchange=\"saveConfigFromUI()\" class=\"input-industrial w-full h-10 rounded-xl px-3 font-mono font-bold\" \/>\n            <\/div>\n          <\/div>\n\n          <div class=\"mt-5 pt-4 border-t border-slate-800\">\n            <h4 class=\"text-xs font-bold uppercase text-sky-400 mb-3\">Tarifas de Grabado por cm\u00b2 ($\/cm\u00b2)<\/h4>\n            <div class=\"grid grid-cols-3 gap-3 text-xs\">\n              <div>\n                <label class=\"block text-slate-400 mb-1\">Nivel Bajo ($\/cm\u00b2)<\/label>\n                <input type=\"number\" id=\"cfg-engrave-low\" step=\"0.005\" onchange=\"saveConfigFromUI()\" class=\"input-industrial w-full h-10 rounded-xl px-3 font-mono font-bold\" \/>\n              <\/div>\n              <div>\n                <label class=\"block text-slate-400 mb-1\">Nivel Medio ($\/cm\u00b2)<\/label>\n                <input type=\"number\" id=\"cfg-engrave-med\" step=\"0.005\" onchange=\"saveConfigFromUI()\" class=\"input-industrial w-full h-10 rounded-xl px-3 font-mono font-bold\" \/>\n              <\/div>\n              <div>\n                <label class=\"block text-slate-400 mb-1\">Nivel Alto ($\/cm\u00b2)<\/label>\n                <input type=\"number\" id=\"cfg-engrave-high\" step=\"0.005\" onchange=\"saveConfigFromUI()\" class=\"input-industrial w-full h-10 rounded-xl px-3 font-mono font-bold\" \/>\n              <\/div>\n            <\/div>\n          <\/div>\n        <\/div>\n\n        <!-- D. Acabados y Escala de Descuentos por Volumen -->\n        <div class=\"glass-panel rounded-2xl p-5\">\n          <h3 class=\"text-sm font-bold uppercase tracking-wider text-white mb-4 border-b border-slate-800 pb-3\">4. Acabados Superficiales y Descuentos por Cantidad<\/h3>\n\n          <div class=\"mb-5\">\n            <h4 class=\"text-xs font-bold uppercase text-sky-400 mb-3\">Recargo Porcentual por Acabado (%)<\/h4>\n            <div class=\"grid grid-cols-2 gap-4 text-xs\">\n              <div>\n                <label class=\"block text-slate-400 mb-1\">Acabado Est\u00e1ndar (%)<\/label>\n                <input type=\"number\" id=\"cfg-finish-standard\" step=\"1\" onchange=\"saveConfigFromUI()\" class=\"input-industrial w-full h-10 rounded-xl px-3 font-mono font-bold\" \/>\n              <\/div>\n              <div>\n                <label class=\"block text-slate-400 mb-1\">Acabado Premium (%)<\/label>\n                <input type=\"number\" id=\"cfg-finish-premium\" step=\"1\" onchange=\"saveConfigFromUI()\" class=\"input-industrial w-full h-10 rounded-xl px-3 font-mono font-bold\" \/>\n              <\/div>\n            <\/div>\n          <\/div>\n\n          <div class=\"pt-4 border-t border-slate-800\">\n            <h4 class=\"text-xs font-bold uppercase text-emerald-400 mb-3\">Escala de Descuentos por Volumen (%)<\/h4>\n            <div class=\"grid grid-cols-2 sm:grid-cols-4 gap-3 text-xs\">\n              <div>\n                <label class=\"block text-slate-400 mb-1\">1 a 2 uds (%)<\/label>\n                <input type=\"number\" id=\"cfg-disc-1-2\" step=\"1\" min=\"0\" max=\"90\" onchange=\"saveConfigFromUI()\" class=\"input-industrial w-full h-10 rounded-xl px-3 font-mono font-bold\" \/>\n              <\/div>\n              <div>\n                <label class=\"block text-slate-400 mb-1\">3 a 11 uds (%)<\/label>\n                <input type=\"number\" id=\"cfg-disc-3-11\" step=\"1\" min=\"0\" max=\"90\" onchange=\"saveConfigFromUI()\" class=\"input-industrial w-full h-10 rounded-xl px-3 font-mono font-bold\" \/>\n              <\/div>\n              <div>\n                <label class=\"block text-slate-400 mb-1\">12 a 49 uds (%)<\/label>\n                <input type=\"number\" id=\"cfg-disc-12-49\" step=\"1\" min=\"0\" max=\"90\" onchange=\"saveConfigFromUI()\" class=\"input-industrial w-full h-10 rounded-xl px-3 font-mono font-bold\" \/>\n              <\/div>\n              <div>\n                <label class=\"block text-slate-400 mb-1\">50+ uds (%)<\/label>\n                <input type=\"number\" id=\"cfg-disc-50-plus\" step=\"1\" min=\"0\" max=\"90\" onchange=\"saveConfigFromUI()\" class=\"input-industrial w-full h-10 rounded-xl px-3 font-mono font-bold\" \/>\n              <\/div>\n            <\/div>\n          <\/div>\n        <\/div>\n\n      <\/div>\n    <\/section>\n  <\/main>\n\n  <!-- Contenedor de Notificaciones Toast -->\n  <div id=\"toast-container\"><\/div>\n\n  <!-- ========================================================= -->\n  <!-- MOTOR DE C\u00c1LCULO Y L\u00d3GICA DE APLICACI\u00d3N                   -->\n  <!-- ========================================================= -->\n  <script>\n    const STORAGE_KEY = 'cnc_woodquote_config_v1';\n\n    \/\/ Configuraci\u00f3n por defecto editable\n    const DEFAULT_CONFIG = {\n      solidWoods: [\n        { id: 'pino', name: 'Pino', hardness: 'soft', priceTabla18: 12.00, priceTablon40: 24.00 },\n        { id: 'teca', name: 'Teca', hardness: 'hard', priceTabla18: 28.00, priceTablon40: 55.00 }\n      ],\n      sheetMaterials: [\n        { id: 'mdf_6', name: 'MDF 6mm', thickness: 6, hardness: 'soft', pricePerCm2: 0.00065 },\n        { id: 'mdf_12', name: 'MDF 12mm', thickness: 12, hardness: 'soft', pricePerCm2: 0.00115 },\n        { id: 'mdf_18', name: 'MDF 18mm', thickness: 18, hardness: 'soft', pricePerCm2: 0.00160 },\n        { id: 'ply_12', name: 'Plywood 12mm', thickness: 12, hardness: 'soft', pricePerCm2: 0.00145 },\n        { id: 'ply_18', name: 'Plywood 18mm', thickness: 18, hardness: 'soft', pricePerCm2: 0.00210 }\n      ],\n      cncHourRate: 24.00,        \/\/ USD por hora de m\u00e1quina CNC\n      setupFee: 10.00,           \/\/ USD cargo \u00fanico para producto personalizado\n      toolDiameter: 6.0,         \/\/ mm (profundidad m\u00e1x pasada = 1.25 * 6 = 7.5mm)\n      pocketStepover: 3.6,       \/\/ mm ancho de desbaste en cajeras\n      feedHard: 2500,            \/\/ mm\/min maderas duras (Teca)\n      feedSoft: 4000,            \/\/ mm\/min maderas suaves (Pino, MDF, Plywood)\n      feedPocket: 2500,          \/\/ mm\/min avance en cajeras\n      wastePct: 20,              \/\/ % excedente por desperdicios\n      engraveRates: {\n        bajo: 0.04,              \/\/ $\/cm2\n        medio: 0.08,             \/\/ $\/cm2\n        alto: 0.15               \/\/ $\/cm2\n      },\n      finishes: {\n        estandar: 20,            \/\/ % adicional sobre costo base\n        premium: 40              \/\/ % adicional sobre costo base\n      },\n      discounts: {\n        r1_2: 0,                 \/\/ % descuento 1-2 uds\n        r3_11: 10,               \/\/ % descuento 3-11 uds\n        r12_49: 18,              \/\/ % descuento 12-49 uds\n        r50_plus: 25             \/\/ % descuento 50+ uds\n      }\n    };\n\n    \/\/ Estado global de la app\n    let appConfig = loadConfig();\n    let appState = {\n      productType: 'generalista', \/\/ 'generalista' | 'personalizado'\n      finish: 'ninguno',          \/\/ 'ninguno' | 'estandar' | 'premium'\n      lastSummaryText: ''\n    };\n\n    \/\/ Cargar configuraci\u00f3n desde localStorage o usar por defecto\n    function loadConfig() {\n      try {\n        const saved = localStorage.getItem(STORAGE_KEY);\n        if (saved) {\n          const parsed = JSON.parse(saved);\n          return { ...DEFAULT_CONFIG, ...parsed };\n        }\n      } catch (e) {\n        console.error('Error leyendo localStorage:', e);\n      }\n      return JSON.parse(JSON.stringify(DEFAULT_CONFIG));\n    }\n\n    function persistConfig() {\n      try {\n        localStorage.setItem(STORAGE_KEY, JSON.stringify(appConfig));\n      } catch (e) {\n        console.error('Error guardando configuraci\u00f3n:', e);\n      }\n    }\n\n    \/\/ Notificaci\u00f3n visual no intrusiva\n    function showToast(message, type = 'info') {\n      const container = document.getElementById('toast-container');\n      const toast = document.createElement('div');\n      const colors = {\n        info: 'bg-slate-800 border-sky-500 text-white',\n        success: 'bg-emerald-950 border-emerald-500 text-emerald-100',\n        error: 'bg-red-950 border-red-500 text-red-100'\n      };\n      toast.className = `px-4 py-3 rounded-xl border shadow-xl text-xs font-semibold flex items-center gap-2 transition-all transform translate-y-0 ${colors[type] || colors.info}`;\n      toast.textContent = message;\n      container.appendChild(toast);\n      setTimeout(() => {\n        toast.style.opacity = '0';\n        setTimeout(() => toast.remove(), 300);\n      }, 3200);\n    }\n\n    \/\/ Cambio de pesta\u00f1as\n    function switchTab(tabName) {\n      const viewCot = document.getElementById('view-cotizador');\n      const viewCfg = document.getElementById('view-config');\n      const btnCot = document.getElementById('tab-btn-cotizador');\n      const btnCfg = document.getElementById('tab-btn-config');\n\n      if (tabName === 'cotizador') {\n        viewCot.classList.remove('hidden');\n        viewCfg.classList.add('hidden');\n        btnCot.className = 'flex-1 sm:flex-none flex items-center justify-center gap-2 px-4 py-2 rounded-lg text-sm font-semibold transition-all bg-sky-600 text-white shadow';\n        btnCfg.className = 'flex-1 sm:flex-none flex items-center justify-center gap-2 px-4 py-2 rounded-lg text-sm font-semibold transition-all text-slate-400 hover:text-white';\n        populateMaterialDropdowns(true);\n        calculateQuote();\n      } else {\n        viewCot.classList.add('hidden');\n        viewCfg.classList.remove('hidden');\n        btnCfg.className = 'flex-1 sm:flex-none flex items-center justify-center gap-2 px-4 py-2 rounded-lg text-sm font-semibold transition-all bg-sky-600 text-white shadow';\n        btnCot.className = 'flex-1 sm:flex-none flex items-center justify-center gap-2 px-4 py-2 rounded-lg text-sm font-semibold transition-all text-slate-400 hover:text-white';\n        renderConfigUI();\n      }\n    }\n\n    \/\/ Poblar selector de materiales en Cotizador\n    function populateMaterialDropdowns(preserveSelection = false) {\n      const matSelect = document.getElementById('select-material');\n      const prevVal = matSelect.value;\n      matSelect.innerHTML = '';\n\n      \/\/ Grupo Madera S\u00f3lida\n      const optGroupSolid = document.createElement('optgroup');\n      optGroupSolid.label = 'Madera S\u00f3lida (Tablas 200\u00d72400mm)';\n      appConfig.solidWoods.forEach(w => {\n        const opt = document.createElement('option');\n        opt.value = `solid:${w.id}`;\n        opt.textContent = `${w.name} (${w.hardness === 'hard' ? 'Dura' : 'Suave'})`;\n        optGroupSolid.appendChild(opt);\n      });\n      matSelect.appendChild(optGroupSolid);\n\n      \/\/ Grupo Tableros\n      const optGroupSheet = document.createElement('optgroup');\n      optGroupSheet.label = 'Tableros (C\u00e1lculo por cm\u00b2)';\n      appConfig.sheetMaterials.forEach(s => {\n        const opt = document.createElement('option');\n        opt.value = `sheet:${s.id}`;\n        opt.textContent = `${s.name} ($${Number(s.pricePerCm2).toFixed(5)}\/cm\u00b2)`;\n        optGroupSheet.appendChild(opt);\n      });\n      matSelect.appendChild(optGroupSheet);\n\n      if (preserveSelection && prevVal && [...matSelect.options].some(o => o.value === prevVal)) {\n        matSelect.value = prevVal;\n      }\n      onMaterialChange(preserveSelection);\n    }\n\n    function onMaterialChange(preserveFormat = false) {\n      const val = document.getElementById('select-material').value;\n      const fmtSelect = document.getElementById('select-formato');\n      const prevFmt = fmtSelect.value;\n      const tag = document.getElementById('material-type-tag');\n\n      fmtSelect.innerHTML = '';\n\n      if (val.startsWith('solid:')) {\n        const id = val.split(':')[1];\n        const wood = appConfig.solidWoods.find(w => w.id === id) || appConfig.solidWoods[0];\n        tag.textContent = `Madera S\u00f3lida (${wood.hardness === 'hard' ? 'Dura' : 'Suave'})`;\n        tag.className = 'text-[11px] font-mono px-2.5 py-0.5 rounded-full bg-amber-500\/15 text-amber-300 border border-amber-500\/30';\n\n        const opt18 = document.createElement('option');\n        opt18.value = 'tabla_18';\n        opt18.textContent = `Tabla 200 \u00d7 18 mm (Largo 2.40m) \u2014 $${Number(wood.priceTabla18).toFixed(2)}`;\n\n        const opt40 = document.createElement('option');\n        opt40.value = 'tablon_40';\n        opt40.textContent = `Tabl\u00f3n 200 \u00d7 40 mm (Largo 2.40m) \u2014 $${Number(wood.priceTablon40).toFixed(2)}`;\n\n        fmtSelect.appendChild(opt18);\n        fmtSelect.appendChild(opt40);\n\n        if (preserveFormat && (prevFmt === 'tabla_18' || prevFmt === 'tablon_40')) {\n          fmtSelect.value = prevFmt;\n        }\n      } else {\n        const id = val.split(':')[1];\n        const sheet = appConfig.sheetMaterials.find(s => s.id === id) || appConfig.sheetMaterials[0];\n        tag.textContent = 'Tablero Derivado (cm\u00b2)';\n        tag.className = 'text-[11px] font-mono px-2.5 py-0.5 rounded-full bg-sky-500\/15 text-sky-300 border border-sky-500\/30';\n\n        const optSheet = document.createElement('option');\n        optSheet.value = `sheet_${sheet.thickness}`;\n        optSheet.textContent = `Plancha Espesor ${sheet.thickness} mm \u2014 $${Number(sheet.pricePerCm2).toFixed(5)}\/cm\u00b2`;\n        fmtSelect.appendChild(optSheet);\n      }\n\n      onFormatChange();\n    }\n\n    function onFormatChange() {\n      const matVal = document.getElementById('select-material').value;\n      const fmtVal = document.getElementById('select-formato').value;\n      const espesorInput = document.getElementById('input-espesor-corte');\n\n      if (matVal.startsWith('solid:')) {\n        espesorInput.value = fmtVal === 'tabla_18' ? 18 : 40;\n      } else {\n        const id = matVal.split(':')[1];\n        const sheet = appConfig.sheetMaterials.find(s => s.id === id);\n        if (sheet) espesorInput.value = sheet.thickness;\n      }\n      calculateQuote();\n    }\n\n    \/\/ Controles interactivos de modalidad, acabado y cantidad\n    function setProductType(type) {\n      appState.productType = type;\n      const btnGen = document.getElementById('btn-tipo-general');\n      const btnCus = document.getElementById('btn-tipo-custom');\n\n      if (type === 'generalista') {\n        btnGen.className = 'p-3 rounded-xl border text-left transition-all border-sky-500 bg-sky-500\/15 text-white';\n        btnGen.querySelector('div:last-child').className = 'text-[11px] text-sky-300 font-mono mt-0.5';\n        btnCus.className = 'p-3 rounded-xl border text-left transition-all border-slate-700 bg-slate-900\/70 text-slate-400 hover:border-slate-600';\n        btnCus.querySelector('div:last-child').className = 'text-[11px] text-slate-400 font-mono mt-0.5';\n      } else {\n        btnCus.className = 'p-3 rounded-xl border text-left transition-all border-amber-500 bg-amber-500\/15 text-white';\n        btnCus.querySelector('div:last-child').className = 'text-[11px] text-amber-300 font-mono mt-0.5';\n        btnGen.className = 'p-3 rounded-xl border text-left transition-all border-slate-700 bg-slate-900\/70 text-slate-400 hover:border-slate-600';\n        btnGen.querySelector('div:last-child').className = 'text-[11px] text-slate-400 font-mono mt-0.5';\n      }\n      calculateQuote();\n    }\n\n    function setFinish(finishKey) {\n      appState.finish = finishKey;\n      ['ninguno', 'estandar', 'premium'].forEach(k => {\n        const btn = document.getElementById(`btn-finish-${k}`);\n        const sub = document.getElementById(`label-finish-${k}`);\n        if (k === finishKey) {\n          btn.className = 'p-3 rounded-xl border text-left transition-all border-sky-500 bg-sky-500\/15 text-white';\n          sub.className = 'text-[11px] font-mono text-sky-300 mt-0.5';\n        } else {\n          btn.className = 'p-3 rounded-xl border text-left transition-all border-slate-700 bg-slate-900\/70 text-slate-400 hover:border-slate-600';\n          sub.className = 'text-[11px] font-mono text-slate-400 mt-0.5';\n        }\n      });\n      calculateQuote();\n    }\n\n    function adjustQty(delta) {\n      const input = document.getElementById('input-cantidad');\n      const current = parseInt(input.value, 10) || 1;\n      input.value = Math.max(1, current + delta);\n      calculateQuote();\n    }\n\n    function setQty(val) {\n      document.getElementById('input-cantidad').value = val;\n      calculateQuote();\n    }\n\n    function updateDynamicLabels() {\n      document.getElementById('label-setup-btn').textContent = `Setup: +$${Number(appConfig.setupFee).toFixed(2)}`;\n      document.getElementById('label-finish-estandar').textContent = `+${appConfig.finishes.estandar}% (Lijado\/Aceite)`;\n      document.getElementById('label-finish-premium').textContent = `+${appConfig.finishes.premium}% (Lacado\/Barniz)`;\n    }\n\n    \/\/ =========================================================\n    \/\/ MOTOR MATEM\u00c1TICO PRINCIPAL DE COTIZACI\u00d3N\n    \/\/ =========================================================\n    function calculateQuote() {\n      updateDynamicLabels();\n\n      const matVal = document.getElementById('select-material').value;\n      const fmtVal = document.getElementById('select-formato').value;\n      const largoMm = Math.max(1, parseFloat(document.getElementById('input-largo').value) || 0);\n      const anchoMm = Math.max(1, parseFloat(document.getElementById('input-ancho').value) || 0);\n      const espesorMm = Math.max(0.5, parseFloat(document.getElementById('input-espesor-corte').value) || 1);\n      const qty = Math.max(1, parseInt(document.getElementById('input-cantidad').value, 10) || 1);\n\n      const cajeraAreaPct = parseFloat(document.getElementById('select-cajera-area').value) || 0;\n      const cajeraProfPct = parseFloat(document.getElementById('select-cajera-prof').value) || 0;\n\n      const grabadoAreaCm2 = Math.max(0, parseFloat(document.getElementById('input-grabado-area').value) || 0);\n      const grabadoNivel = document.getElementById('select-grabado-nivel').value;\n\n      const wasteFactor = 1 + (Number(appConfig.wastePct) \/ 100);\n      const alertBox = document.getElementById('dimension-alert');\n      const alertText = document.getElementById('dimension-alert-text');\n      alertBox.classList.add('hidden');\n\n      let materialCostUnit = 0;\n      let materialDetailStr = '';\n      let nestingSummaryStr = '';\n      let isHardwood = false;\n      let materialNameDisplay = '';\n\n      \/\/ 1. C\u00c1LCULO DE MATERIA PRIMA\n      if (matVal.startsWith('solid:')) {\n        const id = matVal.split(':')[1];\n        const wood = appConfig.solidWoods.find(w => w.id === id) || appConfig.solidWoods[0];\n        isHardwood = wood.hardness === 'hard';\n        const isTabla18 = fmtVal === 'tabla_18';\n        const boardPrice = isTabla18 ? Number(wood.priceTabla18) : Number(wood.priceTablon40);\n        const maxBoardThickness = isTabla18 ? 18 : 40;\n        materialNameDisplay = `${wood.name} (${isTabla18 ? 'Tabla 200\u00d718mm' : 'Tabl\u00f3n 200\u00d740mm'})`;\n\n        if (espesorMm > maxBoardThickness) {\n          alertBox.classList.remove('hidden');\n          alertText.textContent = `El espesor a cortar (${espesorMm} mm) supera el espesor del formato seleccionado (${maxBoardThickness} mm).`;\n        }\n\n        \/\/ Evaluaci\u00f3n de orientaci\u00f3n \u00f3ptima en tabla de 200mm ancho \u00d7 2400mm largo con margen de 10mm (1cm) por pieza\n        const BOARD_WIDTH = 200;\n        const BOARD_LENGTH = 2400;\n        const KERF_MARGIN = 10; \/\/ 1cm entre\/por pieza para refilar\n\n        \/\/ Orientaci\u00f3n A: Ancho de pieza va a lo ancho de la tabla (200mm), Largo va longitudinal\n        const fitA = Math.floor(BOARD_WIDTH \/ (anchoMm + KERF_MARGIN));\n        \/\/ Orientaci\u00f3n B (rotada 90\u00b0): Largo de pieza va a lo ancho de la tabla (200mm), Ancho va longitudinal\n        const fitB = Math.floor(BOARD_WIDTH \/ (largoMm + KERF_MARGIN));\n\n        let piecesAcross = 0;\n        let longitudinalDim = 0;\n        let transversalDim = 0;\n        let rotatedNote = '';\n\n        if (fitA <= 0 &#038;&#038; fitB <= 0) {\n          \/\/ Ninguna dimensi\u00f3n cabe en 200mm con margen de 10mm\n          alertBox.classList.remove('hidden');\n          alertText.textContent = `Atenci\u00f3n: Ni el largo (${largoMm} mm) ni el ancho (${anchoMm} mm) caben en el ancho de 200 mm de la tabla (considerando 10 mm de margen de refilado). Se calcula como fracci\u00f3n proporcional de ancho.`;\n          piecesAcross = BOARD_WIDTH \/ (Math.min(largoMm, anchoMm) + KERF_MARGIN);\n          longitudinalDim = Math.max(largoMm, anchoMm);\n          transversalDim = Math.min(largoMm, anchoMm);\n          rotatedNote = 'Excede ancho 200mm';\n        } else {\n          \/\/ Elegir la orientaci\u00f3n que consuma menos largo efectivo por pieza\n          const effLenA = fitA > 0 ? (largoMm * wasteFactor) \/ fitA : Infinity;\n          const effLenB = fitB > 0 ? (anchoMm * wasteFactor) \/ fitB : Infinity;\n\n          if (effLenA <= effLenB) {\n            piecesAcross = fitA;\n            longitudinalDim = largoMm;\n            transversalDim = anchoMm;\n            rotatedNote = 'Normal';\n          } else {\n            piecesAcross = fitB;\n            longitudinalDim = anchoMm;\n            transversalDim = largoMm;\n            rotatedNote = 'Rotada 90\u00b0';\n          }\n        }\n\n        const effectiveLengthPerPiece = (longitudinalDim * wasteFactor) \/ piecesAcross;\n        const boardFraction = effectiveLengthPerPiece \/ BOARD_LENGTH;\n        materialCostUnit = boardFraction * boardPrice;\n\n        nestingSummaryStr = `${piecesAcross >= 1 ? piecesAcross + ' pza(s) en ancho 200mm' : 'Excede 200mm'} (${rotatedNote}) \u00b7 Largo equiv: ${effectiveLengthPerPiece.toFixed(1)} mm (${(boardFraction * 100).toFixed(1)}% de tabla 2.40m)`;\n        materialDetailStr = `${(boardFraction * 100).toFixed(1)}% de ${isTabla18 ? 'Tabla' : 'Tabl\u00f3n'} ($${boardPrice.toFixed(2)})`;\n      } else {\n        \/\/ Tableros (MDF, Plywood) -> \u00c1rea en cm2 + 20% desperdicio\n        const id = val.split(':')[1];\n        const sheet = appConfig.sheetMaterials.find(s => s.id === id) || appConfig.sheetMaterials[0];\n        isHardwood = sheet.hardness === 'hard';\n        materialNameDisplay = `${sheet.name}`;\n\n        const areaCm2 = (largoMm \/ 10) * (anchoMm \/ 10);\n        const areaWithWasteCm2 = areaCm2 * wasteFactor;\n        materialCostUnit = areaWithWasteCm2 * Number(sheet.pricePerCm2);\n\n        nestingSummaryStr = `\u00c1rea neta: ${areaCm2.toFixed(1)} cm\u00b2 \u00b7 Con +${appConfig.wastePct}% merma: ${areaWithWasteCm2.toFixed(1)} cm\u00b2`;\n        materialDetailStr = `${areaWithWasteCm2.toFixed(1)} cm\u00b2 \u00d7 $${Number(sheet.pricePerCm2).toFixed(5)}\/cm\u00b2`;\n      }\n\n      document.getElementById('nesting-summary-text').textContent = nestingSummaryStr;\n\n      \/\/ 2. TIEMPO Y COSTO DE CORTE PERIMETRAL\n      const perimeterMm = 2 * (largoMm + anchoMm);\n      const toolDia = Number(appConfig.toolDiameter) || 6.0;\n      const maxDepthPerPass = 1.25 * toolDia; \/\/ 1.25 * 6mm = 7.5mm\n      const numPasses = Math.ceil(espesorMm \/ maxDepthPerPass) + 1; \/\/ +1 pasada extra por entrada en rampa\n      const totalCutDistanceMm = perimeterMm * numPasses;\n\n      const feedRate = isHardwood ? Number(appConfig.feedHard) : Number(appConfig.feedSoft);\n      document.getElementById('feedrate-badge').textContent = `Avance: ${feedRate} mm\/min`;\n\n      const perimeterTimeMin = totalCutDistanceMm \/ feedRate;\n\n      \/\/ 3. TIEMPO Y COSTO DE CAJERAS (VACIADO)\n      const pieceVolumeMm3 = largoMm * anchoMm * espesorMm;\n      const pocketVolumeMm3 = pieceVolumeMm3 * cajeraAreaPct * cajeraProfPct;\n      const pocketVolumeCm3 = pocketVolumeMm3 \/ 1000;\n      document.getElementById('pocket-vol-preview').textContent = `Volumen: ${pocketVolumeCm3.toFixed(1)} cm\u00b3`;\n\n      \/\/ Tasa de remoci\u00f3n de material (MRR) = ancho_paso (3.6mm) * prof_pasada (7.5mm) * avance (2500mm\/min)\n      const stepoverMm = Number(appConfig.pocketStepover) || 3.6;\n      const pocketFeed = Number(appConfig.feedPocket) || 2500;\n      const mrrMm3PerMin = stepoverMm * maxDepthPerPass * pocketFeed;\n\n      const pocketTimeMin = pocketVolumeMm3 > 0 ? (pocketVolumeMm3 \/ mrrMm3PerMin) : 0;\n\n      \/\/ 4. TIEMPO TOTAL CNC Y COSTO DE MECANIZADO\n      const totalMachineTimeMin = perimeterTimeMin + pocketTimeMin;\n      const cncHourRate = Number(appConfig.cncHourRate) || 0;\n      const machineCostUnit = (totalMachineTimeMin \/ 60) * cncHourRate;\n\n      \/\/ 5. COSTO DE GRABADO\n      const engraveRate = Number(appConfig.engraveRates[grabadoNivel]) || 0;\n      document.getElementById('engrave-rate-preview').textContent = `Tarifa: $${engraveRate.toFixed(3)} \/ cm\u00b2`;\n      const engraveCostUnit = grabadoAreaCm2 * engraveRate;\n\n      \/\/ 6. SUBTOTAL BASE Y ACABADO SUPERFICIAL\n      const baseFabricationUnit = materialCostUnit + machineCostUnit + engraveCostUnit;\n\n      let finishPct = 0;\n      let finishLabelName = 'Sin Acabados';\n      if (appState.finish === 'estandar') {\n        finishPct = Number(appConfig.finishes.estandar) || 0;\n        finishLabelName = 'Acabado Est\u00e1ndar';\n      } else if (appState.finish === 'premium') {\n        finishPct = Number(appConfig.finishes.premium) || 0;\n        finishLabelName = 'Acabado Premium';\n      }\n      const finishCostUnit = baseFabricationUnit * (finishPct \/ 100);\n      const unitBeforeDiscount = baseFabricationUnit + finishCostUnit;\n\n      \/\/ 7. DESCUENTO POR VOLUMEN\n      let discountPct = 0;\n      let rangeLabel = '1-2';\n      if (qty >= 50) {\n        discountPct = Number(appConfig.discounts.r50_plus) || 0;\n        rangeLabel = '50+';\n      } else if (qty >= 12) {\n        discountPct = Number(appConfig.discounts.r12_49) || 0;\n        rangeLabel = '12-49';\n      } else if (qty >= 3) {\n        discountPct = Number(appConfig.discounts.r3_11) || 0;\n        rangeLabel = '3-11';\n      } else {\n        discountPct = Number(appConfig.discounts.r1_2) || 0;\n        rangeLabel = '1-2';\n      }\n\n      const badgeDesc = document.getElementById('badge-descuento');\n      badgeDesc.textContent = `Rango ${rangeLabel} (-${discountPct}%)`;\n      badgeDesc.className = discountPct > 0\n        ? 'text-[11px] font-mono px-2 py-0.5 rounded bg-emerald-500\/20 text-emerald-300 border border-emerald-500\/30'\n        : 'text-[11px] font-mono px-2 py-0.5 rounded bg-slate-800 text-slate-300 border border-slate-700';\n\n      const discountAmountUnit = unitBeforeDiscount * (discountPct \/ 100);\n      const discountedUnit = unitBeforeDiscount - discountAmountUnit;\n\n      \/\/ 8. COSTO FIJO DE SETUP (DISE\u00d1O \/ CAM)\n      const totalSetupCost = appState.productType === 'personalizado' ? (Number(appConfig.setupFee) || 0) : 0;\n      const setupPerUnit = totalSetupCost \/ qty;\n\n      \/\/ 9. TOTALES FINALES\n      const finalUnitPrice = discountedUnit + setupPerUnit;\n      const finalBatchTotal = (discountedUnit * qty) + totalSetupCost;\n\n      \/\/ ACTUALIZAR INTERFAZ DE RESULTADOS\n      document.getElementById('res-unit-price').textContent = `$${finalUnitPrice.toFixed(2)}`;\n      document.getElementById('res-unit-subnote').textContent = totalSetupCost > 0\n        ? `Incluye $${setupPerUnit.toFixed(2)}\/ud de setup`\n        : 'Producto generalista (sin setup)';\n\n      document.getElementById('res-qty-label').textContent = qty;\n      document.getElementById('res-total-price').textContent = `$${finalBatchTotal.toFixed(2)}`;\n      document.getElementById('res-discount-note').textContent = discountPct > 0\n        ? `Ahorro lote: -$${(discountAmountUnit * qty).toFixed(2)} (${discountPct}%)`\n        : 'Tarifa base sin descuento';\n\n      \/\/ Par\u00e1metros t\u00e9cnicos\n      document.getElementById('tech-passes').textContent = `${numPasses} pasadas (${maxDepthPerPass}mm\/pas)`;\n      document.getElementById('tech-cut-time').textContent = `${perimeterTimeMin.toFixed(2)} min`;\n      document.getElementById('tech-pocket-time').textContent = `${pocketTimeMin.toFixed(2)} min`;\n      document.getElementById('tech-total-time').textContent = `${totalMachineTimeMin.toFixed(2)} min`;\n      document.getElementById('tech-batch-time').textContent = `${(totalMachineTimeMin * qty).toFixed(1)} min (${((totalMachineTimeMin * qty) \/ 60).toFixed(2)} hrs)`;\n\n      \/\/ Desglose de costos unitarios\n      document.getElementById('cost-mat').textContent = `$${materialCostUnit.toFixed(2)}`;\n      document.getElementById('cost-mat-detail').textContent = materialDetailStr;\n\n      document.getElementById('cost-cnc').textContent = `$${machineCostUnit.toFixed(2)}`;\n      document.getElementById('cost-cnc-detail').textContent = `${totalMachineTimeMin.toFixed(2)} min @ $${cncHourRate.toFixed(2)}\/h`;\n\n      document.getElementById('cost-engrave').textContent = `$${engraveCostUnit.toFixed(2)}`;\n      document.getElementById('cost-engrave-detail').textContent = `${grabadoAreaCm2} cm\u00b2 (Nivel ${grabadoNivel})`;\n\n      document.getElementById('cost-base-sub').textContent = `$${baseFabricationUnit.toFixed(2)}`;\n\n      document.getElementById('cost-finish-label').textContent = `${finishLabelName} (+${finishPct}%)`;\n      document.getElementById('cost-finish').textContent = `+$${finishCostUnit.toFixed(2)}`;\n\n      document.getElementById('cost-discount-label').textContent = `Descuento Volumen ${rangeLabel} (-${discountPct}%)`;\n      document.getElementById('cost-discount').textContent = `-$${discountAmountUnit.toFixed(2)}`;\n\n      document.getElementById('cost-setup-unit').textContent = `+$${setupPerUnit.toFixed(2)}`;\n      document.getElementById('cost-setup-detail').textContent = totalSetupCost > 0\n        ? `$${totalSetupCost.toFixed(2)} \u00fanico \u00f7 ${qty} uds`\n        : 'No aplica ($0.00)';\n\n      \/\/ Construir texto plano estructurado para copiar\n      appState.lastSummaryText = [\n        `\ud83d\udccb *COTIZACI\u00d3N INTERNA ROUTER CNC*`,\n        `\u2022 Modalidad: ${appState.productType === 'personalizado' ? 'Producto Personalizado' : 'Pieza Generalista'}`,\n        `\u2022 Material: ${materialNameDisplay}`,\n        `\u2022 Dimensiones: ${largoMm} \u00d7 ${anchoMm} \u00d7 ${espesorMm} mm`,\n        `\u2022 Cantidad: ${qty} unidad(es) [Rango ${rangeLabel}]`,\n        `\u2022 Operaciones: Corte (${numPasses} pasadas) | Cajeras (${cajeraAreaPct * 100}% \u00e1rea, ${cajeraProfPct * 100}% prof) | Grabado (${grabadoAreaCm2} cm\u00b2 - ${grabadoNivel})`,\n        `\u2022 Acabado: ${finishLabelName} (+${finishPct}%)`,\n        `\u2022 Tiempo CNC estimado: ${totalMachineTimeMin.toFixed(2)} min\/ud (Total lote: ${(totalMachineTimeMin * qty).toFixed(1)} min)`,\n        `------------------------------------`,\n        `\u2022 Material\/ud: $${materialCostUnit.toFixed(2)} | CNC\/ud: $${machineCostUnit.toFixed(2)} | Grabado\/ud: $${engraveCostUnit.toFixed(2)}`,\n        `\u2022 Setup CAM: $${totalSetupCost.toFixed(2)} | Descuento: -${discountPct}%`,\n        `\u2705 *PRECIO UNITARIO FINAL: $${finalUnitPrice.toFixed(2)} USD*`,\n        `\ud83d\udcb0 *TOTAL LOTE (${qty} uds): $${finalBatchTotal.toFixed(2)} USD*`\n      ].join('\\n');\n    }\n\n    \/\/ Copiar resumen al portapapeles (compatible con iframes)\n    function copyQuoteSummary() {\n      const textArea = document.createElement('textarea');\n      textArea.value = appState.lastSummaryText;\n      textArea.style.position = 'fixed';\n      textArea.style.left = '-9999px';\n      document.body.appendChild(textArea);\n      textArea.select();\n      try {\n        document.execCommand('copy');\n        showToast('Resumen de cotizaci\u00f3n copiado al portapapeles', 'success');\n      } catch (err) {\n        showToast('No se pudo copiar autom\u00e1ticamente', 'error');\n      }\n      document.body.removeChild(textArea);\n    }\n\n    \/\/ =========================================================\n    \/\/ PESTA\u00d1A DE CONFIGURACI\u00d3N (UI + EXCEL IMPORT\/EXPORT)\n    \/\/ =========================================================\n    function renderConfigUI() {\n      \/\/ 1. Maderas S\u00f3lidas\n      const solidContainer = document.getElementById('config-solid-woods');\n      solidContainer.innerHTML = '';\n      appConfig.solidWoods.forEach((wood, idx) => {\n        const row = document.createElement('div');\n        row.className = 'p-3 rounded-xl bg-slate-900\/90 border border-slate-800 grid grid-cols-1 sm:grid-cols-12 gap-2.5 items-end';\n        row.innerHTML = `\n          <div class=\"sm:col-span-4\">\n            <label class=\"block text-[10px] uppercase text-slate-400 mb-1\">Nombre Madera<\/label>\n            <input type=\"text\" value=\"${wood.name}\" onchange=\"updateSolidWood(${idx}, 'name', this.value)\" class=\"input-industrial w-full h-9 rounded-lg px-2.5 text-xs font-semibold\" \/>\n          <\/div>\n          <div class=\"sm:col-span-2\">\n            <label class=\"block text-[10px] uppercase text-slate-400 mb-1\">Dureza<\/label>\n            <select onchange=\"updateSolidWood(${idx}, 'hardness', this.value)\" class=\"input-industrial w-full h-9 rounded-lg px-2 text-xs\">\n              <option value=\"soft\" ${wood.hardness === 'soft' ? 'selected' : ''}>Suave<\/option>\n              <option value=\"hard\" ${wood.hardness === 'hard' ? 'selected' : ''}>Dura<\/option>\n            <\/select>\n          <\/div>\n          <div class=\"sm:col-span-2\">\n            <label class=\"block text-[10px] uppercase text-slate-400 mb-1\">Tabla 18mm ($)<\/label>\n            <input type=\"number\" step=\"0.5\" value=\"${wood.priceTabla18}\" onchange=\"updateSolidWood(${idx}, 'priceTabla18', parseFloat(this.value)||0)\" class=\"input-industrial w-full h-9 rounded-lg px-2 text-xs font-mono font-bold\" \/>\n          <\/div>\n          <div class=\"sm:col-span-3\">\n            <label class=\"block text-[10px] uppercase text-slate-400 mb-1\">Tabl\u00f3n 40mm ($)<\/label>\n            <input type=\"number\" step=\"0.5\" value=\"${wood.priceTablon40}\" onchange=\"updateSolidWood(${idx}, 'priceTablon40', parseFloat(this.value)||0)\" class=\"input-industrial w-full h-9 rounded-lg px-2 text-xs font-mono font-bold\" \/>\n          <\/div>\n          <div class=\"sm:col-span-1 flex justify-end\">\n            ${appConfig.solidWoods.length > 1 ? `<button type=\"button\" onclick=\"removeSolidWood(${idx})\" class=\"h-9 px-2.5 rounded-lg bg-red-950\/60 hover:bg-red-800 text-red-300 text-xs font-bold\">\u2715<\/button>` : ''}\n          <\/div>\n        `;\n        solidContainer.appendChild(row);\n      });\n\n      \/\/ 2. Tableros\n      const sheetContainer = document.getElementById('config-sheet-materials');\n      sheetContainer.innerHTML = '';\n      appConfig.sheetMaterials.forEach((sheet, idx) => {\n        const row = document.createElement('div');\n        row.className = 'p-3 rounded-xl bg-slate-900\/90 border border-slate-800 grid grid-cols-1 sm:grid-cols-12 gap-2.5 items-end';\n        row.innerHTML = `\n          <div class=\"sm:col-span-5\">\n            <label class=\"block text-[10px] uppercase text-slate-400 mb-1\">Nombre \/ Espesor<\/label>\n            <input type=\"text\" value=\"${sheet.name}\" onchange=\"updateSheetMat(${idx}, 'name', this.value)\" class=\"input-industrial w-full h-9 rounded-lg px-2.5 text-xs font-semibold\" \/>\n          <\/div>\n          <div class=\"sm:col-span-2\">\n            <label class=\"block text-[10px] uppercase text-slate-400 mb-1\">Esp. (mm)<\/label>\n            <input type=\"number\" step=\"1\" value=\"${sheet.thickness}\" onchange=\"updateSheetMat(${idx}, 'thickness', parseFloat(this.value)||3)\" class=\"input-industrial w-full h-9 rounded-lg px-2 text-xs font-mono\" \/>\n          <\/div>\n          <div class=\"sm:col-span-4\">\n            <label class=\"block text-[10px] uppercase text-slate-400 mb-1\">Precio ($ \/ cm\u00b2)<\/label>\n            <input type=\"number\" step=\"0.00005\" value=\"${sheet.pricePerCm2}\" onchange=\"updateSheetMat(${idx}, 'pricePerCm2', parseFloat(this.value)||0)\" class=\"input-industrial w-full h-9 rounded-lg px-2 text-xs font-mono font-bold\" \/>\n          <\/div>\n          <div class=\"sm:col-span-1 flex justify-end\">\n            ${appConfig.sheetMaterials.length > 1 ? `<button type=\"button\" onclick=\"removeSheetMat(${idx})\" class=\"h-9 px-2.5 rounded-lg bg-red-950\/60 hover:bg-red-800 text-red-300 text-xs font-bold\">\u2715<\/button>` : ''}\n          <\/div>\n        `;\n        sheetContainer.appendChild(row);\n      });\n\n      \/\/ 3. Campos num\u00e9ricos generales\n      document.getElementById('cfg-cnc-hour-rate').value = appConfig.cncHourRate;\n      document.getElementById('cfg-setup-fee').value = appConfig.setupFee;\n      document.getElementById('cfg-tool-diameter').value = appConfig.toolDiameter;\n      document.getElementById('cfg-pocket-stepover').value = appConfig.pocketStepover;\n      document.getElementById('cfg-feed-hard').value = appConfig.feedHard;\n      document.getElementById('cfg-feed-soft').value = appConfig.feedSoft;\n      document.getElementById('cfg-feed-pocket').value = appConfig.feedPocket;\n      document.getElementById('cfg-waste-pct').value = appConfig.wastePct;\n\n      document.getElementById('cfg-engrave-low').value = appConfig.engraveRates.bajo;\n      document.getElementById('cfg-engrave-med').value = appConfig.engraveRates.medio;\n      document.getElementById('cfg-engrave-high').value = appConfig.engraveRates.alto;\n\n      document.getElementById('cfg-finish-standard').value = appConfig.finishes.estandar;\n      document.getElementById('cfg-finish-premium').value = appConfig.finishes.premium;\n\n      document.getElementById('cfg-disc-1-2').value = appConfig.discounts.r1_2;\n      document.getElementById('cfg-disc-3-11').value = appConfig.discounts.r3_11;\n      document.getElementById('cfg-disc-12-49').value = appConfig.discounts.r12_49;\n      document.getElementById('cfg-disc-50-plus').value = appConfig.discounts.r50_plus;\n    }\n\n    function updateSolidWood(idx, field, value) {\n      appConfig.solidWoods[idx][field] = value;\n      persistConfig();\n      showToast('Tarifa de madera actualizada', 'success');\n    }\n\n    function addSolidWoodRow() {\n      appConfig.solidWoods.push({\n        id: 'wood_' + Date.now(),\n        name: 'Nueva Madera',\n        hardness: 'soft',\n        priceTabla18: 15.00,\n        priceTablon40: 30.00\n      });\n      persistConfig();\n      renderConfigUI();\n    }\n\n    function removeSolidWood(idx) {\n      appConfig.solidWoods.splice(idx, 1);\n      persistConfig();\n      renderConfigUI();\n    }\n\n    function updateSheetMat(idx, field, value) {\n      appConfig.sheetMaterials[idx][field] = value;\n      persistConfig();\n      showToast('Tarifa de tablero actualizada', 'success');\n    }\n\n    function addSheetMaterialRow() {\n      appConfig.sheetMaterials.push({\n        id: 'sheet_' + Date.now(),\n        name: 'Nuevo Tablero 15mm',\n        thickness: 15,\n        hardness: 'soft',\n        pricePerCm2: 0.00140\n      });\n      persistConfig();\n      renderConfigUI();\n    }\n\n    function removeSheetMat(idx) {\n      appConfig.sheetMaterials.splice(idx, 1);\n      persistConfig();\n      renderConfigUI();\n    }\n\n    function updateSheetHelper() {\n      const sheetPrice = parseFloat(document.getElementById('helper-sheet-price').value) || 0;\n      const areaStandardCm2 = 244 * 122; \/\/ 29,768 cm2\n      const perCm2 = sheetPrice \/ areaStandardCm2;\n      document.getElementById('helper-sheet-cm2').textContent = `$${perCm2.toFixed(5)} \/ cm\u00b2`;\n    }\n\n    function saveConfigFromUI() {\n      appConfig.cncHourRate = parseFloat(document.getElementById('cfg-cnc-hour-rate').value) || 0;\n      appConfig.setupFee = parseFloat(document.getElementById('cfg-setup-fee').value) || 0;\n      appConfig.toolDiameter = parseFloat(document.getElementById('cfg-tool-diameter').value) || 6;\n      appConfig.pocketStepover = parseFloat(document.getElementById('cfg-pocket-stepover').value) || 3.6;\n      appConfig.feedHard = parseFloat(document.getElementById('cfg-feed-hard').value) || 2500;\n      appConfig.feedSoft = parseFloat(document.getElementById('cfg-feed-soft').value) || 4000;\n      appConfig.feedPocket = parseFloat(document.getElementById('cfg-feed-pocket').value) || 2500;\n      appConfig.wastePct = parseFloat(document.getElementById('cfg-waste-pct').value) || 20;\n\n      appConfig.engraveRates.bajo = parseFloat(document.getElementById('cfg-engrave-low').value) || 0;\n      appConfig.engraveRates.medio = parseFloat(document.getElementById('cfg-engrave-med').value) || 0;\n      appConfig.engraveRates.alto = parseFloat(document.getElementById('cfg-engrave-high').value) || 0;\n\n      appConfig.finishes.estandar = parseFloat(document.getElementById('cfg-finish-standard').value) || 0;\n      appConfig.finishes.premium = parseFloat(document.getElementById('cfg-finish-premium').value) || 0;\n\n      appConfig.discounts.r1_2 = parseFloat(document.getElementById('cfg-disc-1-2').value) || 0;\n      appConfig.discounts.r3_11 = parseFloat(document.getElementById('cfg-disc-3-11').value) || 0;\n      appConfig.discounts.r12_49 = parseFloat(document.getElementById('cfg-disc-12-49').value) || 0;\n      appConfig.discounts.r50_plus = parseFloat(document.getElementById('cfg-disc-50-plus').value) || 0;\n\n      persistConfig();\n      showToast('Par\u00e1metros guardados correctamente', 'success');\n    }\n\n    function resetDefaultConfig() {\n      appConfig = JSON.parse(JSON.stringify(DEFAULT_CONFIG));\n      persistConfig();\n      renderConfigUI();\n      populateMaterialDropdowns(false);\n      calculateQuote();\n      showToast('Configuraci\u00f3n restaurada a valores iniciales', 'info');\n    }\n\n    \/\/ =========================================================\n    \/\/ EXPORTAR E IMPORTAR ARCHIVO EXCEL (.xlsx)\n    \/\/ =========================================================\n    function exportConfigToExcel() {\n      if (typeof XLSX === 'undefined') {\n        showToast('Librer\u00eda Excel no disponible sin conexi\u00f3n', 'error');\n        return;\n      }\n\n      const wb = XLSX.utils.book_new();\n\n      \/\/ Hoja 1: Maderas S\u00f3lidas\n      const solidRows = appConfig.solidWoods.map(w => ({\n        ID: w.id,\n        Madera: w.name,\n        Dureza_hard_o_soft: w.hardness,\n        Precio_Tabla_200x18mm_USD: w.priceTabla18,\n        Precio_Tablon_200x40mm_USD: w.priceTablon40\n      }));\n      const wsSolid = XLSX.utils.json_to_sheet(solidRows);\n      XLSX.utils.book_append_sheet(wb, wsSolid, 'Maderas_Solidas');\n\n      \/\/ Hoja 2: Tableros\n      const sheetRows = appConfig.sheetMaterials.map(s => ({\n        ID: s.id,\n        Tablero: s.name,\n        Espesor_mm: s.thickness,\n        Precio_por_cm2_USD: s.pricePerCm2\n      }));\n      const wsSheets = XLSX.utils.json_to_sheet(sheetRows);\n      XLSX.utils.book_append_sheet(wb, wsSheets, 'Tableros_cm2');\n\n      \/\/ Hoja 3: Tarifas CNC, Grabados, Acabados y Descuentos\n      const paramsRows = [\n        { Parametro: 'cncHourRate', Descripcion: 'Tarifa Hora Maquina CNC (USD)', Valor: appConfig.cncHourRate },\n        { Parametro: 'setupFee', Descripcion: 'Costo Setup Producto Personalizado (USD)', Valor: appConfig.setupFee },\n        { Parametro: 'toolDiameter', Descripcion: 'Diametro Fresa Estandar (mm)', Valor: appConfig.toolDiameter },\n        { Parametro: 'pocketStepover', Descripcion: 'Ancho Paso Desbaste Cajeras (mm)', Valor: appConfig.pocketStepover },\n        { Parametro: 'feedHard', Descripcion: 'Avance Maderas Duras (mm\/min)', Valor: appConfig.feedHard },\n        { Parametro: 'feedSoft', Descripcion: 'Avance Maderas Suaves y Tableros (mm\/min)', Valor: appConfig.feedSoft },\n        { Parametro: 'feedPocket', Descripcion: 'Avance Desbaste Cajeras (mm\/min)', Valor: appConfig.feedPocket },\n        { Parametro: 'wastePct', Descripcion: 'Porcentaje Desperdicio Material (%)', Valor: appConfig.wastePct },\n        { Parametro: 'engrave_bajo', Descripcion: 'Tarifa Grabado Nivel Bajo (USD\/cm2)', Valor: appConfig.engraveRates.bajo },\n        { Parametro: 'engrave_medio', Descripcion: 'Tarifa Grabado Nivel Medio (USD\/cm2)', Valor: appConfig.engraveRates.medio },\n        { Parametro: 'engrave_alto', Descripcion: 'Tarifa Grabado Nivel Alto (USD\/cm2)', Valor: appConfig.engraveRates.alto },\n        { Parametro: 'finish_estandar', Descripcion: 'Recargo Acabado Estandar (%)', Valor: appConfig.finishes.estandar },\n        { Parametro: 'finish_premium', Descripcion: 'Recargo Acabado Premium (%)', Valor: appConfig.finishes.premium },\n        { Parametro: 'disc_1_2', Descripcion: 'Descuento Cantidad 1 a 2 uds (%)', Valor: appConfig.discounts.r1_2 },\n        { Parametro: 'disc_3_11', Descripcion: 'Descuento Cantidad 3 a 11 uds (%)', Valor: appConfig.discounts.r3_11 },\n        { Parametro: 'disc_12_49', Descripcion: 'Descuento Cantidad 12 a 49 uds (%)', Valor: appConfig.discounts.r12_49 },\n        { Parametro: 'disc_50_plus', Descripcion: 'Descuento Cantidad 50+ uds (%)', Valor: appConfig.discounts.r50_plus }\n      ];\n      const wsParams = XLSX.utils.json_to_sheet(paramsRows);\n      XLSX.utils.book_append_sheet(wb, wsParams, 'Tarifas_y_Parametros');\n\n      XLSX.writeFile(wb, 'Configuracion_Costos_CNC.xlsx');\n      showToast('Plantilla Excel descargada: Configuracion_Costos_CNC.xlsx', 'success');\n    }\n\n    function importConfigFromExcel(event) {\n      const file = event.target.files[0];\n      if (!file) return;\n\n      const reader = new FileReader();\n      reader.onload = function (e) {\n        try {\n          const data = new Uint8Array(e.target.result);\n          const workbook = XLSX.read(data, { type: 'array' });\n\n          \/\/ Leer Maderas S\u00f3lidas\n          if (workbook.Sheets['Maderas_Solidas']) {\n            const rows = XLSX.utils.sheet_to_json(workbook.Sheets['Maderas_Solidas']);\n            if (rows.length > 0) {\n              appConfig.solidWoods = rows.map((r, i) => ({\n                id: r.ID || `wood_${i}`,\n                name: r.Madera || 'Madera',\n                hardness: (r.Dureza_hard_o_soft === 'hard') ? 'hard' : 'soft',\n                priceTabla18: parseFloat(r.Precio_Tabla_200x18mm_USD) || 0,\n                priceTablon40: parseFloat(r.Precio_Tablon_200x40mm_USD) || 0\n              }));\n            }\n          }\n\n          \/\/ Leer Tableros\n          if (workbook.Sheets['Tableros_cm2']) {\n            const rows = XLSX.utils.sheet_to_json(workbook.Sheets['Tableros_cm2']);\n            if (rows.length > 0) {\n              appConfig.sheetMaterials = rows.map((r, i) => ({\n                id: r.ID || `sheet_${i}`,\n                name: r.Tablero || 'Tablero',\n                thickness: parseFloat(r.Espesor_mm) || 12,\n                hardness: 'soft',\n                pricePerCm2: parseFloat(r.Precio_por_cm2_USD) || 0\n              }));\n            }\n          }\n\n          \/\/ Leer Tarifas y Par\u00e1metros\n          if (workbook.Sheets['Tarifas_y_Parametros']) {\n            const rows = XLSX.utils.sheet_to_json(workbook.Sheets['Tarifas_y_Parametros']);\n            rows.forEach(r => {\n              const key = r.Parametro;\n              const val = parseFloat(r.Valor);\n              if (isNaN(val)) return;\n\n              switch (key) {\n                case 'cncHourRate': appConfig.cncHourRate = val; break;\n                case 'setupFee': appConfig.setupFee = val; break;\n                case 'toolDiameter': appConfig.toolDiameter = val; break;\n                case 'pocketStepover': appConfig.pocketStepover = val; break;\n                case 'feedHard': appConfig.feedHard = val; break;\n                case 'feedSoft': appConfig.feedSoft = val; break;\n                case 'feedPocket': appConfig.feedPocket = val; break;\n                case 'wastePct': appConfig.wastePct = val; break;\n                case 'engrave_bajo': appConfig.engraveRates.bajo = val; break;\n                case 'engrave_medio': appConfig.engraveRates.medio = val; break;\n                case 'engrave_alto': appConfig.engraveRates.alto = val; break;\n                case 'finish_estandar': appConfig.finishes.estandar = val; break;\n                case 'finish_premium': appConfig.finishes.premium = val; break;\n                case 'disc_1_2': appConfig.discounts.r1_2 = val; break;\n                case 'disc_3_11': appConfig.discounts.r3_11 = val; break;\n                case 'disc_12_49': appConfig.discounts.r12_49 = val; break;\n                case 'disc_50_plus': appConfig.discounts.r50_plus = val; break;\n              }\n            });\n          }\n\n          persistConfig();\n          renderConfigUI();\n          populateMaterialDropdowns(false);\n          calculateQuote();\n          showToast('Precios y tarifas importados desde Excel correctamente', 'success');\n        } catch (err) {\n          console.error(err);\n          showToast('Error al leer el archivo Excel. Verifica el formato.', 'error');\n        }\n        event.target.value = '';\n      };\n      reader.readAsArrayBuffer(file);\n    }\n\n    \/\/ Inicializaci\u00f3n al cargar la p\u00e1gina\n    window.addEventListener('DOMContentLoaded', () => {\n      populateMaterialDropdowns(false);\n      calculateQuote();\n    });\n  <\/script>\n<\/body>\n<\/html>\n","protected":false},"excerpt":{"rendered":"<p>CNC WoodQuote Pro | Cotizador Interno de Taller CNC WOODQUOTE Taller Interno C\u00e1lculo de Materia Prima, Tiempos de Mecanizado &#038; Costos (USD) Cotizador Configuraci\u00f3n de Precios 01 Modalidad y Volumen de Lote Tipo de Producto Generalista Setup: $0.00 Personalizado Setup: +$10.00 Cantidad (Unidades) Rango 1-2 (0%) &#8211; + 1 ud 6 uds 12 uds 50+ [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_uag_custom_page_level_css":"","site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"default","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"set","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"class_list":["post-2013","page","type-page","status-publish","hentry"],"uagb_featured_image_src":{"full":false,"thumbnail":false,"medium":false,"medium_large":false,"large":false,"1536x1536":false,"2048x2048":false,"woocommerce_thumbnail":false,"woocommerce_single":false,"woocommerce_gallery_thumbnail":false},"uagb_author_info":{"display_name":"dicatone.com","author_link":"https:\/\/dicatone.com\/index.php\/author\/dicatone-com\/"},"uagb_comment_info":0,"uagb_excerpt":"CNC WoodQuote Pro | Cotizador Interno de Taller CNC WOODQUOTE Taller Interno C\u00e1lculo de Materia Prima, Tiempos de Mecanizado &#038; Costos (USD) Cotizador Configuraci\u00f3n de Precios 01 Modalidad y Volumen de Lote Tipo de Producto Generalista Setup: $0.00 Personalizado Setup: +$10.00 Cantidad (Unidades) Rango 1-2 (0%) &#8211; + 1 ud 6 uds 12 uds 50+&hellip;","_links":{"self":[{"href":"https:\/\/dicatone.com\/index.php\/wp-json\/wp\/v2\/pages\/2013","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/dicatone.com\/index.php\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/dicatone.com\/index.php\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/dicatone.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/dicatone.com\/index.php\/wp-json\/wp\/v2\/comments?post=2013"}],"version-history":[{"count":1,"href":"https:\/\/dicatone.com\/index.php\/wp-json\/wp\/v2\/pages\/2013\/revisions"}],"predecessor-version":[{"id":2015,"href":"https:\/\/dicatone.com\/index.php\/wp-json\/wp\/v2\/pages\/2013\/revisions\/2015"}],"wp:attachment":[{"href":"https:\/\/dicatone.com\/index.php\/wp-json\/wp\/v2\/media?parent=2013"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}