{"id":4470,"date":"2026-07-28T12:34:56","date_gmt":"2026-07-28T12:34:56","guid":{"rendered":"https:\/\/www.innra.cl\/?page_id=4470"},"modified":"2026-07-28T12:58:11","modified_gmt":"2026-07-28T12:58:11","slug":"cotizacion","status":"publish","type":"page","link":"https:\/\/www.innra.cl\/en\/cotizacion\/","title":{"rendered":"COTIZACI\u00d3N"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"4470\" class=\"elementor elementor-4470\" data-elementor-settings=\"{&quot;ha_cmc_init_switcher&quot;:&quot;no&quot;}\" data-elementor-post-type=\"page\">\n\t\t\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-4ee57a5 elementor-section-full_width elementor-section-height-default elementor-section-height-default\" data-id=\"4ee57a5\" data-element_type=\"section\" data-e-type=\"section\" data-settings=\"{&quot;_ha_eqh_enable&quot;:false}\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-no\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-e4eb5fd\" data-id=\"e4eb5fd\" data-element_type=\"column\" data-e-type=\"column\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t<div class=\"elementor-element elementor-element-710d6c6 de_scroll_animation_no elementor-widget elementor-widget-html\" data-id=\"710d6c6\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<!DOCTYPE html>\r\n<!--\r\n================================================================================\r\n  INNRA \u00b7 CONFIGURADOR IDR\r\n  Aplicaci\u00f3n web de configuraci\u00f3n y cotizaci\u00f3n de equipos de perforaci\u00f3n IDR.\r\n================================================================================\r\n  ARCHIVO \u00daNICO PARA PRUEBAS.\r\n  Para dividir el proyecto, extraer cada bloque marcado con el encabezado\r\n  \"==== FILE: nombre ====\" a su propio archivo, en este orden de carga:\r\n\r\n      index.html\r\n      styles.css\r\n      translations.js\r\n      data.js\r\n      components.js\r\n      cart.js\r\n      app.js\r\n\r\n  COMPATIBILIDAD WORDPRESS \/ ELEMENTOR\r\n  ------------------------------------\r\n  \u00b7 Pegar desde <div id=\"idr-app\"> hasta el \u00faltimo <\/script> dentro de un\r\n    Widget HTML de Elementor.\r\n  \u00b7 Todo el CSS est\u00e1 scopeado bajo #idr-app (ninguna regla global).\r\n  \u00b7 Todo el JS vive dentro de una IIFE y expone un \u00fanico global: window.IDR.\r\n  \u00b7 Sin jQuery. S\u00f3lo HTML5 + CSS3 + JavaScript ES6.\r\n  \u00b7 Sin dependencias externas salvo las webfonts (degradan a stack de sistema).\r\n================================================================================\r\n-->\r\n<html lang=\"es\">\r\n<head>\r\n<meta charset=\"UTF-8\">\r\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\r\n<title>INNRA \u00b7 Configurador IDR<\/title>\r\n\r\n<!-- ============================================================ -->\r\n<!-- ==== FILE: styles.css ====================================== -->\r\n<!-- ============================================================ -->\r\n<style>\r\n@import url('https:\/\/fonts.googleapis.com\/css2?family=Audiowide&family=Barlow+Condensed:wght@500;600;700&family=IBM+Plex+Mono:wght@400;500&family=Inter:wght@400;500;600;700&display=swap');\r\n\r\n\/* ------------------------------------------------------------------\r\n   1. TOKENS \u00b7 PALETA CORPORATIVA INNRA\r\n   Colores de marca:\r\n     #15559c  azul del logotipo   -> acci\u00f3n principal\r\n     #123F6D  azul profundo       -> estados presionados y capas densas\r\n     #0f1c2a  azul casi negro     -> tinta, superficies oscuras\r\n     #7a8691  gris azulado        -> texto secundario y l\u00edneas\r\n     #edeff0  base clara          -> lienzo de la aplicaci\u00f3n (ya existente)\r\n   Las variantes -050\/-100\/-300 son tintes del azul de marca, no colores\r\n   nuevos. El \u00e1mbar sobrevive \u00fanicamente con valor sem\u00e1ntico de\r\n   advertencia (--idr-warn); nunca como color decorativo.\r\n------------------------------------------------------------------ *\/\r\n#idr-app{\r\n  \/* Tinta y superficies *\/\r\n  --idr-ink:#0f1c2a;\r\n  --idr-ink-2:#1d3348;\r\n  --idr-muted:#7a8691;\r\n  --idr-line:#d6dade;\r\n  --idr-line-soft:#e8eaec;\r\n  --idr-canvas:#edeff0;\r\n  --idr-surface:#ffffff;\r\n  --idr-surface-2:#f5f6f8;\r\n\r\n  \/* Acci\u00f3n *\/\r\n  --idr-primary:#15559c;\r\n  --idr-primary-600:#123F6D;\r\n  --idr-primary-050:#e9eff7;\r\n  --idr-primary-100:#c4d6ea;\r\n  --idr-primary-300:#6f9fd4;   \/* tinte para fondos oscuros *\/\r\n\r\n  \/* Estado *\/\r\n  --idr-signal:#15559c;\r\n  --idr-signal-soft:#e9eff7;\r\n  --idr-warn:#c8871f;\r\n  --idr-warn-soft:#f9f1e2;\r\n  --idr-danger:#a93520;\r\n  --idr-danger-soft:#fbeae6;\r\n  --idr-ok:#2c7a58;\r\n\r\n  \/* Estratos del registro de sondaje (elemento firma):\r\n     la paleta de marca apilada, de la tinta al gris azulado. *\/\r\n  --idr-strata-1:#0f1c2a;\r\n  --idr-strata-2:#123F6D;\r\n  --idr-strata-3:#15559c;\r\n  --idr-strata-4:#4a7bb0;\r\n  --idr-strata-5:#7a8691;\r\n\r\n  \/* Tipograf\u00eda *\/\r\n  --idr-brand:'Audiowide','Barlow Condensed',system-ui,sans-serif;\r\n  --idr-display:'Barlow Condensed','Oswald','Arial Narrow',system-ui,sans-serif;\r\n  --idr-body:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;\r\n  --idr-mono:'IBM Plex Mono','SFMono-Regular',Consolas,monospace;\r\n\r\n  \/* M\u00e9trica *\/\r\n  --idr-r-s:8px;\r\n  --idr-r-m:12px;\r\n  --idr-r-l:18px;\r\n  --idr-sh-1:0 1px 2px rgba(15,28,42,.06),0 1px 1px rgba(15,28,42,.04);\r\n  --idr-sh-2:0 6px 18px rgba(15,28,42,.08),0 2px 6px rgba(15,28,42,.05);\r\n  --idr-sh-3:0 24px 60px rgba(15,28,42,.18),0 8px 20px rgba(15,28,42,.10);\r\n  --idr-ease:cubic-bezier(.22,.61,.36,1);\r\n\r\n  \/* Posici\u00f3n del carrito (ajustable si la barra de admin de WP desplaza el layout) *\/\r\n  --idr-cart-top:98px;      \/* deja libre la barra superior *\/\r\n  --idr-cart-right:24px;\r\n  --idr-cart-w:388px;\r\n}\r\n\r\n\/* ------------------------------------------------------------------\r\n   2. RESET ENCAPSULADO\r\n------------------------------------------------------------------ *\/\r\n#idr-app,#idr-app *,#idr-app *::before,#idr-app *::after{box-sizing:border-box;}\r\n#idr-app *{margin:0;padding:0;}\r\n#idr-app{\r\n  font-family:var(--idr-body);\r\n  color:var(--idr-ink);\r\n  background:var(--idr-canvas);\r\n  font-size:16px;\r\n  line-height:1.55;\r\n  -webkit-font-smoothing:antialiased;\r\n  text-rendering:optimizeLegibility;\r\n  scroll-behavior:smooth;\r\n  position:relative;\r\n  isolation:isolate;\r\n}\r\n#idr-app img{max-width:100%;display:block;}\r\n#idr-app button,#idr-app input,#idr-app select,#idr-app textarea{\r\n  font:inherit;color:inherit;background:none;border:none;outline:none;\r\n}\r\n#idr-app button{cursor:pointer;}\r\n#idr-app ul,#idr-app ol{list-style:none;}\r\n#idr-app a{color:inherit;text-decoration:none;}\r\n#idr-app :focus-visible{\r\n  outline:2px solid var(--idr-primary-300);\r\n  outline-offset:2px;\r\n  border-radius:4px;\r\n}\r\n\r\n\/* ------------------------------------------------------------------\r\n   3. UTILIDADES TIPOGR\u00c1FICAS\r\n------------------------------------------------------------------ *\/\r\n#idr-app .idr-eyebrow{\r\n  font-family:var(--idr-mono);\r\n  font-size:11px;\r\n  letter-spacing:.16em;\r\n  text-transform:uppercase;\r\n  color:var(--idr-muted);\r\n}\r\n#idr-app .idr-h1{\r\n  font-family:var(--idr-display);\r\n  font-weight:700;\r\n  font-size:clamp(38px,6.2vw,74px);\r\n  line-height:.96;\r\n  letter-spacing:-.01em;\r\n  text-transform:uppercase;\r\n}\r\n#idr-app .idr-h2{\r\n  font-family:var(--idr-display);\r\n  font-weight:600;\r\n  font-size:clamp(26px,3.4vw,38px);\r\n  line-height:1.05;\r\n  letter-spacing:.005em;\r\n  text-transform:uppercase;\r\n}\r\n#idr-app .idr-h3{\r\n  font-family:var(--idr-display);\r\n  font-weight:600;\r\n  font-size:22px;\r\n  line-height:1.15;\r\n  letter-spacing:.01em;\r\n  text-transform:uppercase;\r\n}\r\n#idr-app .idr-lead{font-size:clamp(15px,1.5vw,19px);color:var(--idr-muted);max-width:56ch;}\r\n#idr-app .idr-sub{font-size:14px;color:var(--idr-muted);}\r\n#idr-app .idr-mono{font-family:var(--idr-mono);font-size:12px;letter-spacing:.06em;}\r\n#idr-app .idr-sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;}\r\n\r\n\/* ------------------------------------------------------------------\r\n   4. LAYOUT\r\n------------------------------------------------------------------ *\/\r\n#idr-app .idr-wrap{width:100%;max-width:1280px;margin:0 auto;padding:0 28px;}\r\n#idr-app .idr-section{padding:clamp(40px,5vw,72px) 0;}\r\n\r\n\/* -- Barra superior ------------------------------------------------ *\/\r\n#idr-app .idr-topbar{\r\n  position:sticky;top:0;z-index:40;\r\n  background:rgba(237,239,240,.86);\r\n  backdrop-filter:saturate(160%) blur(12px);\r\n  -webkit-backdrop-filter:saturate(160%) blur(12px);\r\n  border-bottom:1px solid var(--idr-line);\r\n}\r\n#idr-app .idr-topbar__in{\r\n  display:flex;align-items:center;justify-content:space-between;gap:20px;\r\n  height:74px;\r\n}\r\n#idr-app .idr-brand{display:flex;align-items:baseline;gap:14px;min-width:0;}\r\n#idr-app .idr-brand__mark{\r\n  font-family:var(--idr-brand);        \/* Audiowide *\/\r\n  font-weight:400;font-size:22px;letter-spacing:.04em;\r\n  text-transform:uppercase;line-height:1;\r\n  color:var(--idr-primary);\r\n}\r\n#idr-app .idr-brand__rule{width:1px;height:22px;background:var(--idr-line);align-self:center;}\r\n#idr-app .idr-brand__sub{\r\n  font-family:var(--idr-mono);font-size:11px;letter-spacing:.14em;\r\n  text-transform:uppercase;color:var(--idr-muted);white-space:nowrap;\r\n}\r\n\r\n\/* -- Selector de idioma -------------------------------------------- *\/\r\n#idr-app .idr-lang{\r\n  display:flex;align-items:center;gap:2px;\r\n  border:1px solid var(--idr-line);border-radius:999px;\r\n  padding:3px;background:var(--idr-surface);\r\n}\r\n#idr-app .idr-lang__btn{\r\n  font-family:var(--idr-mono);font-size:11px;letter-spacing:.1em;\r\n  text-transform:uppercase;color:var(--idr-muted);\r\n  padding:6px 12px;border-radius:999px;transition:.2s var(--idr-ease);\r\n}\r\n#idr-app .idr-lang__btn:hover{color:var(--idr-ink);}\r\n#idr-app .idr-lang__btn.is-on{background:var(--idr-ink);color:#fff;}\r\n\r\n\/* ------------------------------------------------------------------\r\n   5. HERO\r\n   Tesis visual: la boca del pozo. Anillos conc\u00e9ntricos + marcas de\r\n   profundidad sobre grafito.\r\n------------------------------------------------------------------ *\/\r\n#idr-app .idr-hero{\r\n  position:relative;overflow:hidden;\r\n  background:var(--idr-ink);\r\n  color:#fff;\r\n}\r\n#idr-app .idr-hero__bg{\r\n  position:absolute;inset:0;opacity:.5;pointer-events:none;\r\n}\r\n#idr-app .idr-hero__in{\r\n  position:relative;z-index:2;\r\n  padding:clamp(64px,9vw,124px) 0 clamp(56px,7vw,96px);\r\n  display:grid;grid-template-columns:minmax(0,1fr) auto;gap:48px;align-items:end;\r\n}\r\n#idr-app .idr-hero .idr-eyebrow{color:rgba(255,255,255,.55);}\r\n#idr-app .idr-hero__title{margin:18px 0 20px;color:#fff;max-width:16ch;}\r\n#idr-app .idr-hero__title em{font-style:normal;color:var(--idr-primary-300);}\r\n#idr-app .idr-hero__lead{color:rgba(255,255,255,.68);max-width:44ch;margin-bottom:34px;}\r\n#idr-app .idr-hero__meta{\r\n  display:flex;flex-direction:column;gap:14px;align-items:flex-end;\r\n  border-right:1px solid rgba(255,255,255,.14);padding-right:22px;\r\n}\r\n#idr-app .idr-hero__metaItem{text-align:right;}\r\n#idr-app .idr-hero__metaK{\r\n  font-family:var(--idr-mono);font-size:10px;letter-spacing:.16em;\r\n  text-transform:uppercase;color:rgba(255,255,255,.45);\r\n}\r\n#idr-app .idr-hero__metaV{\r\n  font-family:var(--idr-display);font-size:23px;font-weight:600;\r\n  text-transform:uppercase;letter-spacing:.03em;\r\n}\r\n\r\n\/* ------------------------------------------------------------------\r\n   6. REGISTRO DE SONDAJE (elemento firma)\r\n   Columna de estratos que se rellena a medida que avanza la config.\r\n------------------------------------------------------------------ *\/\r\n#idr-app .idr-stage{\r\n  display:grid;\r\n  grid-template-columns:214px minmax(0,1fr);\r\n  gap:clamp(28px,3.5vw,56px);\r\n  align-items:start;\r\n}\r\n#idr-app .idr-log{position:sticky;top:98px;}\r\n#idr-app .idr-log__head{\r\n  font-family:var(--idr-mono);font-size:10px;letter-spacing:.16em;\r\n  text-transform:uppercase;color:var(--idr-muted);\r\n  padding-bottom:10px;border-bottom:1px solid var(--idr-line);margin-bottom:14px;\r\n  display:flex;justify-content:space-between;\r\n}\r\n#idr-app .idr-log__col{display:flex;flex-direction:column;}\r\n#idr-app .idr-log__step{\r\n  display:grid;grid-template-columns:44px minmax(0,1fr);gap:14px;align-items:stretch;\r\n  width:100%;text-align:left;padding:0;background:none;\r\n}\r\n#idr-app .idr-log__core{\r\n  position:relative;height:62px;border-radius:3px;\r\n  background:var(--idr-line-soft);\r\n  border:1px solid var(--idr-line);\r\n  overflow:hidden;transition:.45s var(--idr-ease);\r\n}\r\n#idr-app .idr-log__step .idr-log__core{border-radius:0;}\r\n#idr-app .idr-log__step:first-child .idr-log__core{border-radius:3px 3px 0 0;}\r\n#idr-app .idr-log__step:last-child  .idr-log__core{border-radius:0 0 3px 3px;}\r\n#idr-app .idr-log__step + .idr-log__step .idr-log__core{border-top:none;}\r\n#idr-app .idr-log__core::after{ \/* marcas de profundidad *\/\r\n  content:\"\";position:absolute;inset:0;\r\n  background-image:repeating-linear-gradient(180deg,rgba(255,255,255,.22) 0 1px,transparent 1px 12px);\r\n  opacity:0;transition:.4s var(--idr-ease);\r\n}\r\n#idr-app .idr-log__step.is-done .idr-log__core::after,\r\n#idr-app .idr-log__step.is-active .idr-log__core::after{opacity:1;}\r\n#idr-app .idr-log__step.is-done .idr-log__core,\r\n#idr-app .idr-log__step.is-active .idr-log__core{border-color:transparent;}\r\n#idr-app .idr-log__step[data-tone=\"1\"].is-done .idr-log__core,#idr-app .idr-log__step[data-tone=\"1\"].is-active .idr-log__core{background:var(--idr-strata-1);}\r\n#idr-app .idr-log__step[data-tone=\"2\"].is-done .idr-log__core,#idr-app .idr-log__step[data-tone=\"2\"].is-active .idr-log__core{background:var(--idr-strata-2);}\r\n#idr-app .idr-log__step[data-tone=\"3\"].is-done .idr-log__core,#idr-app .idr-log__step[data-tone=\"3\"].is-active .idr-log__core{background:var(--idr-strata-3);}\r\n#idr-app .idr-log__step[data-tone=\"4\"].is-done .idr-log__core,#idr-app .idr-log__step[data-tone=\"4\"].is-active .idr-log__core{background:var(--idr-strata-4);}\r\n#idr-app .idr-log__step[data-tone=\"5\"].is-done .idr-log__core,#idr-app .idr-log__step[data-tone=\"5\"].is-active .idr-log__core{background:var(--idr-strata-5);}\r\n#idr-app .idr-log__step.is-active .idr-log__core{box-shadow:inset 3px 0 0 #ffffff;}\r\n#idr-app .idr-log__body{display:flex;flex-direction:column;justify-content:center;min-width:0;}\r\n#idr-app .idr-log__n{\r\n  font-family:var(--idr-mono);font-size:10px;letter-spacing:.16em;color:var(--idr-muted);\r\n}\r\n#idr-app .idr-log__label{\r\n  font-family:var(--idr-display);font-size:16px;font-weight:600;\r\n  text-transform:uppercase;letter-spacing:.04em;color:var(--idr-muted);\r\n  transition:.25s var(--idr-ease);\r\n}\r\n#idr-app .idr-log__step.is-done .idr-log__label,\r\n#idr-app .idr-log__step.is-active .idr-log__label{color:var(--idr-ink);}\r\n#idr-app .idr-log__step.is-active .idr-log__n{color:var(--idr-signal);}\r\n#idr-app .idr-log__value{\r\n  font-size:12px;color:var(--idr-muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;\r\n}\r\n#idr-app .idr-log__step:disabled{cursor:not-allowed;opacity:.55;}\r\n#idr-app .idr-log__step:not(:disabled):hover .idr-log__label{color:var(--idr-primary);}\r\n\r\n\/* ------------------------------------------------------------------\r\n   7. PANELES Y TARJETAS\r\n------------------------------------------------------------------ *\/\r\n#idr-app .idr-panel{\r\n  background:var(--idr-surface);\r\n  border:1px solid var(--idr-line);\r\n  border-radius:var(--idr-r-l);\r\n  box-shadow:var(--idr-sh-1);\r\n  padding:clamp(22px,3vw,38px);\r\n}\r\n#idr-app .idr-panel + .idr-panel{margin-top:22px;}\r\n#idr-app .idr-panel__head{margin-bottom:24px;}\r\n#idr-app .idr-panel__head .idr-eyebrow{display:block;margin-bottom:8px;}\r\n#idr-app .idr-panel__head p{margin-top:8px;}\r\n\r\n#idr-app .idr-block{padding:24px 0;border-top:1px solid var(--idr-line-soft);}\r\n#idr-app .idr-block:first-of-type{border-top:none;padding-top:0;}\r\n#idr-app .idr-block__head{\r\n  display:flex;align-items:baseline;justify-content:space-between;gap:16px;\r\n  margin-bottom:16px;flex-wrap:wrap;\r\n}\r\n#idr-app .idr-block__title{\r\n  font-family:var(--idr-display);font-size:19px;font-weight:600;\r\n  text-transform:uppercase;letter-spacing:.03em;\r\n}\r\n\r\n\/* -- Rejillas ------------------------------------------------------ *\/\r\n#idr-app .idr-grid{display:grid;gap:14px;}\r\n#idr-app .idr-grid--2{grid-template-columns:repeat(auto-fit,minmax(248px,1fr));}\r\n#idr-app .idr-grid--3{grid-template-columns:repeat(auto-fit,minmax(232px,1fr));}\r\n#idr-app .idr-grid--wide{grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:18px;}\r\n\r\n\/* -- Opci\u00f3n seleccionable ------------------------------------------ *\/\r\n#idr-app .idr-opt{\r\n  position:relative;display:flex;flex-direction:column;gap:6px;\r\n  text-align:left;width:100%;\r\n  padding:18px 46px 18px 18px;\r\n  background:var(--idr-surface);\r\n  border-radius:var(--idr-r-m);\r\n  box-shadow:inset 0 0 0 1px var(--idr-line);\r\n  transition:.22s var(--idr-ease);\r\n}\r\n#idr-app .idr-opt:hover{box-shadow:inset 0 0 0 1px var(--idr-primary-100);background:var(--idr-surface-2);}\r\n#idr-app .idr-opt.is-on{\r\n  box-shadow:inset 0 0 0 2px var(--idr-primary);\r\n  background:var(--idr-primary-050);\r\n}\r\n#idr-app .idr-opt__title{\r\n  font-family:var(--idr-display);font-size:18px;font-weight:600;\r\n  text-transform:uppercase;letter-spacing:.03em;line-height:1.1;\r\n}\r\n#idr-app .idr-opt__desc{font-size:13px;color:var(--idr-muted);}\r\n#idr-app .idr-opt__tick{\r\n  position:absolute;top:16px;right:16px;width:20px;height:20px;border-radius:50%;\r\n  border:1px solid var(--idr-line);display:grid;place-items:center;transition:.2s var(--idr-ease);\r\n}\r\n#idr-app .idr-opt.is-on .idr-opt__tick{background:var(--idr-primary);border-color:var(--idr-primary);}\r\n#idr-app .idr-opt__tick svg{width:11px;height:11px;stroke:#fff;opacity:0;transition:.2s var(--idr-ease);}\r\n#idr-app .idr-opt.is-on .idr-opt__tick svg{opacity:1;}\r\n#idr-app .idr-opt--check .idr-opt__tick{border-radius:5px;}\r\n\r\n\/* -- Tarjeta de m\u00f3dulo (equipos) ----------------------------------- *\/\r\n#idr-app .idr-modcard{\r\n  display:flex;flex-direction:column;text-align:left;width:100%;\r\n  background:var(--idr-surface);border-radius:var(--idr-r-l);\r\n  box-shadow:inset 0 0 0 1px var(--idr-line),var(--idr-sh-1);\r\n  overflow:hidden;transition:.28s var(--idr-ease);\r\n}\r\n#idr-app .idr-modcard:hover{transform:translateY(-3px);box-shadow:inset 0 0 0 1px var(--idr-primary-100),var(--idr-sh-2);}\r\n#idr-app .idr-modcard__art{\r\n  height:132px;background:var(--idr-ink);position:relative;overflow:hidden;\r\n  display:grid;place-items:center;\r\n}\r\n#idr-app .idr-modcard__art svg{width:100%;height:100%;}\r\n#idr-app .idr-modcard__code{\r\n  position:absolute;top:12px;left:14px;\r\n  font-family:var(--idr-mono);font-size:10px;letter-spacing:.16em;\r\n  color:var(--idr-primary-300);\r\n}\r\n#idr-app .idr-modcard__body{padding:20px 20px 22px;display:flex;flex-direction:column;flex:1;gap:8px;}\r\n#idr-app .idr-modcard__title{\r\n  font-family:var(--idr-display);font-size:20px;font-weight:600;\r\n  text-transform:uppercase;letter-spacing:.03em;line-height:1.08;\r\n}\r\n#idr-app .idr-modcard__desc{font-size:13.5px;color:var(--idr-muted);flex:1;}\r\n#idr-app .idr-modcard__foot{\r\n  display:flex;align-items:center;justify-content:space-between;gap:12px;\r\n  margin-top:14px;padding-top:14px;border-top:1px solid var(--idr-line-soft);\r\n}\r\n#idr-app .idr-modcard__cta{\r\n  font-family:var(--idr-mono);font-size:11px;letter-spacing:.12em;\r\n  text-transform:uppercase;color:var(--idr-primary);\r\n  display:inline-flex;align-items:center;gap:8px;\r\n}\r\n#idr-app .idr-modcard:hover .idr-modcard__cta svg{transform:translateX(4px);}\r\n#idr-app .idr-modcard__cta svg{width:14px;height:14px;transition:.25s var(--idr-ease);}\r\n#idr-app .idr-modcard.is-configured{box-shadow:inset 0 0 0 2px var(--idr-ok),var(--idr-sh-1);}\r\n\r\n\/* -- Etiquetas ------------------------------------------------------ *\/\r\n#idr-app .idr-tag{\r\n  display:inline-flex;align-items:center;gap:6px;\r\n  font-family:var(--idr-mono);font-size:10px;letter-spacing:.12em;text-transform:uppercase;\r\n  padding:4px 9px;border-radius:999px;border:1px solid var(--idr-line);color:var(--idr-muted);\r\n}\r\n#idr-app .idr-tag--ok{border-color:rgba(44,122,88,.35);color:var(--idr-ok);background:rgba(44,122,88,.07);}\r\n#idr-app .idr-tag--warn{border-color:rgba(200,135,31,.42);color:#8a5b06;background:var(--idr-warn-soft);}\r\n\r\n\/* -- Chips (barras) -------------------------------------------------- *\/\r\n#idr-app .idr-chips{display:flex;flex-wrap:wrap;gap:10px;}\r\n#idr-app .idr-chip{\r\n  display:inline-flex;align-items:center;gap:9px;\r\n  padding:11px 18px;border-radius:999px;\r\n  box-shadow:inset 0 0 0 1px var(--idr-line);\r\n  font-family:var(--idr-display);font-size:17px;font-weight:600;\r\n  text-transform:uppercase;letter-spacing:.06em;\r\n  background:var(--idr-surface);transition:.2s var(--idr-ease);\r\n}\r\n#idr-app .idr-chip:hover{box-shadow:inset 0 0 0 1px var(--idr-primary-100);}\r\n#idr-app .idr-chip.is-on{background:var(--idr-ink);color:#fff;box-shadow:inset 0 0 0 1px var(--idr-ink);}\r\n#idr-app .idr-chip--static{\r\n  cursor:default;background:var(--idr-surface-2);\r\n  box-shadow:inset 0 0 0 1px var(--idr-line);\r\n}\r\n#idr-app .idr-chip--static:hover{box-shadow:inset 0 0 0 1px var(--idr-line);}\r\n#idr-app .idr-chip--static .idr-chip__dot{background:var(--idr-signal);}\r\n#idr-app .idr-chip__dot{width:7px;height:7px;border-radius:50%;background:var(--idr-line);transition:.2s var(--idr-ease);}\r\n#idr-app .idr-chip.is-on .idr-chip__dot{background:var(--idr-primary-300);}\r\n\r\n\/* -- Campos --------------------------------------------------------- *\/\r\n#idr-app .idr-field{display:flex;flex-direction:column;gap:7px;}\r\n#idr-app .idr-label{\r\n  font-family:var(--idr-mono);font-size:10px;letter-spacing:.14em;\r\n  text-transform:uppercase;color:var(--idr-muted);\r\n}\r\n#idr-app .idr-input,#idr-app .idr-textarea{\r\n  width:100%;padding:12px 14px;border-radius:var(--idr-r-s);\r\n  background:var(--idr-surface);\r\n  box-shadow:inset 0 0 0 1px var(--idr-line);\r\n  font-size:15px;transition:.2s var(--idr-ease);\r\n}\r\n#idr-app .idr-input:focus,#idr-app .idr-textarea:focus{box-shadow:inset 0 0 0 2px var(--idr-primary);}\r\n#idr-app .idr-input::placeholder,#idr-app .idr-textarea::placeholder{color:#a4aeb8;}\r\n#idr-app .idr-textarea{min-height:96px;resize:vertical;line-height:1.5;}\r\n\r\n\/* -- Contador de cantidad ------------------------------------------- *\/\r\n#idr-app .idr-qty{\r\n  display:inline-flex;align-items:center;\r\n  border-radius:999px;box-shadow:inset 0 0 0 1px var(--idr-line);\r\n  background:var(--idr-surface);overflow:hidden;\r\n}\r\n#idr-app .idr-qty__btn{\r\n  width:34px;height:34px;display:grid;place-items:center;\r\n  color:var(--idr-muted);transition:.18s var(--idr-ease);\r\n}\r\n#idr-app .idr-qty__btn:hover{background:var(--idr-primary-050);color:var(--idr-primary);}\r\n#idr-app .idr-qty__btn:disabled{opacity:.3;cursor:not-allowed;background:none;}\r\n#idr-app .idr-qty__btn svg{width:13px;height:13px;}\r\n#idr-app .idr-qty__val{\r\n  width:44px;text-align:center;font-family:var(--idr-mono);font-size:14px;font-weight:500;\r\n  -moz-appearance:textfield;\r\n}\r\n#idr-app .idr-qty__val::-webkit-outer-spin-button,\r\n#idr-app .idr-qty__val::-webkit-inner-spin-button{-webkit-appearance:none;margin:0;}\r\n#idr-app .idr-qty--sm .idr-qty__btn{width:29px;height:29px;}\r\n#idr-app .idr-qty--sm .idr-qty__val{width:36px;font-size:13px;}\r\n\r\n\/* -- Fila con cantidad (extracci\u00f3n de testigos, accesorios) ---------- *\/\r\n#idr-app .idr-row{\r\n  display:flex;align-items:center;justify-content:space-between;gap:16px;\r\n  padding:14px 16px;border-radius:var(--idr-r-m);\r\n  box-shadow:inset 0 0 0 1px var(--idr-line);\r\n  background:var(--idr-surface);transition:.2s var(--idr-ease);\r\n}\r\n#idr-app .idr-row.is-on{box-shadow:inset 0 0 0 2px var(--idr-primary);background:var(--idr-primary-050);}\r\n#idr-app .idr-row__main{display:flex;align-items:center;gap:13px;min-width:0;flex:1;text-align:left;}\r\n#idr-app .idr-row__box{\r\n  width:20px;height:20px;border-radius:5px;flex:none;\r\n  border:1px solid var(--idr-line);background:var(--idr-surface);\r\n  display:grid;place-items:center;transition:.2s var(--idr-ease);\r\n}\r\n#idr-app .idr-row.is-on .idr-row__box{background:var(--idr-primary);border-color:var(--idr-primary);}\r\n#idr-app .idr-row__box svg{width:11px;height:11px;stroke:#fff;opacity:0;transition:.2s var(--idr-ease);}\r\n#idr-app .idr-row.is-on .idr-row__box svg{opacity:1;}\r\n#idr-app .idr-row__name{font-weight:500;font-size:15px;line-height:1.3;}\r\n#idr-app .idr-row__desc{font-size:12.5px;color:var(--idr-muted);}\r\n#idr-app .idr-rows{display:flex;flex-direction:column;gap:10px;}\r\n#idr-app .idr-row.is-locked{opacity:.5;background:var(--idr-surface-2);}\r\n#idr-app .idr-row.is-locked .idr-row__main{cursor:not-allowed;}\r\n#idr-app .idr-row__req{\r\n  display:inline-flex;align-items:center;gap:6px;margin-left:8px;\r\n  font-family:var(--idr-mono);font-size:9.5px;letter-spacing:.1em;text-transform:uppercase;\r\n  color:var(--idr-muted);border:1px solid var(--idr-line);border-radius:999px;padding:2px 7px;\r\n  vertical-align:middle;\r\n}\r\n#idr-app .idr-row__code{\r\n  font-family:var(--idr-mono);font-size:10px;letter-spacing:.12em;\r\n  color:var(--idr-primary);margin-left:8px;\r\n}\r\n\r\n\/* -- Interruptor S\u00ed\/No ---------------------------------------------- *\/\r\n#idr-app .idr-seg{display:inline-flex;padding:3px;border-radius:999px;background:var(--idr-line-soft);}\r\n#idr-app .idr-seg__btn{\r\n  padding:7px 20px;border-radius:999px;\r\n  font-family:var(--idr-mono);font-size:11px;letter-spacing:.1em;text-transform:uppercase;\r\n  color:var(--idr-muted);transition:.2s var(--idr-ease);\r\n}\r\n#idr-app .idr-seg__btn.is-on{background:var(--idr-surface);color:var(--idr-ink);box-shadow:var(--idr-sh-1);}\r\n\r\n\/* -- Avisos ---------------------------------------------------------- *\/\r\n#idr-app .idr-note{\r\n  display:flex;gap:12px;padding:14px 16px;border-radius:var(--idr-r-m);\r\n  background:var(--idr-warn-soft);border-left:3px solid var(--idr-warn);\r\n  font-size:13.5px;color:#6b4a08;margin-top:14px;\r\n}\r\n#idr-app .idr-note svg{width:17px;height:17px;flex:none;margin-top:2px;stroke:var(--idr-warn);}\r\n#idr-app .idr-info{\r\n  padding:20px 22px;border-radius:var(--idr-r-m);\r\n  background:var(--idr-primary-050);border-left:3px solid var(--idr-primary);\r\n  font-size:14px;color:#123F6D;\r\n}\r\n#idr-app .idr-info strong{color:var(--idr-primary);}\r\n#idr-app .idr-info__list{\r\n  display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));\r\n  gap:8px 20px;margin-top:14px;\r\n}\r\n#idr-app .idr-info__list li{\r\n  display:flex;align-items:center;gap:9px;font-size:13px;\r\n  font-family:var(--idr-mono);letter-spacing:.02em;\r\n}\r\n#idr-app .idr-info__list li::before{\r\n  content:\"\";width:5px;height:5px;border-radius:50%;background:var(--idr-primary);flex:none;\r\n}\r\n\r\n\/* -- Botones ---------------------------------------------------------- *\/\r\n#idr-app .idr-btn{\r\n  display:inline-flex;align-items:center;justify-content:center;gap:10px;\r\n  padding:14px 26px;border-radius:999px;\r\n  font-family:var(--idr-mono);font-size:12px;letter-spacing:.12em;text-transform:uppercase;\r\n  background:var(--idr-ink);color:#fff;transition:.24s var(--idr-ease);white-space:nowrap;\r\n}\r\n#idr-app .idr-btn:hover{background:var(--idr-primary);transform:translateY(-1px);}\r\n#idr-app .idr-btn:active{transform:translateY(0);}\r\n#idr-app .idr-btn:disabled{opacity:.38;cursor:not-allowed;transform:none;background:var(--idr-ink);}\r\n#idr-app .idr-btn svg{width:15px;height:15px;}\r\n#idr-app .idr-btn--lg{padding:18px 34px;font-size:13px;}\r\n#idr-app .idr-btn--ghost{background:transparent;color:var(--idr-ink);box-shadow:inset 0 0 0 1px var(--idr-line);}\r\n#idr-app .idr-btn--ghost:hover{background:var(--idr-surface);color:var(--idr-primary);box-shadow:inset 0 0 0 1px var(--idr-primary);}\r\n#idr-app .idr-btn--accent{background:var(--idr-primary);}\r\n#idr-app .idr-btn--accent:hover{background:var(--idr-primary-600);}\r\n#idr-app .idr-btn--danger{background:transparent;color:var(--idr-danger);box-shadow:inset 0 0 0 1px rgba(169,53,32,.3);}\r\n#idr-app .idr-btn--danger:hover{background:var(--idr-danger-soft);color:var(--idr-danger);}\r\n#idr-app .idr-btn--block{width:100%;}\r\n#idr-app .idr-btn--onDark{background:#ffffff;color:var(--idr-ink);}\r\n#idr-app .idr-btn--onDark:hover{background:var(--idr-primary);color:#ffffff;}\r\n#idr-app .idr-linkbtn{\r\n  font-family:var(--idr-mono);font-size:11px;letter-spacing:.1em;text-transform:uppercase;\r\n  color:var(--idr-muted);display:inline-flex;align-items:center;gap:8px;transition:.2s var(--idr-ease);\r\n}\r\n#idr-app .idr-linkbtn:hover{color:var(--idr-primary);}\r\n#idr-app .idr-linkbtn svg{width:14px;height:14px;}\r\n#idr-app .idr-actions{\r\n  display:flex;align-items:center;justify-content:space-between;gap:16px;\r\n  margin-top:30px;padding-top:24px;border-top:1px solid var(--idr-line-soft);flex-wrap:wrap;\r\n}\r\n#idr-app .idr-actions__r{display:flex;align-items:center;gap:12px;flex-wrap:wrap;}\r\n\r\n\/* ------------------------------------------------------------------\r\n   8. CARRITO FIJO\r\n------------------------------------------------------------------ *\/\r\n#idr-app .idr-cart{\r\n  position:fixed;top:var(--idr-cart-top);right:var(--idr-cart-right);\r\n  width:var(--idr-cart-w);max-width:calc(100vw - 32px);\r\n  z-index:60;\r\n  background:var(--idr-surface);\r\n  border-radius:var(--idr-r-l);\r\n  box-shadow:var(--idr-sh-3);\r\n  display:flex;flex-direction:column;\r\n  max-height:calc(100vh - var(--idr-cart-top) - 28px);\r\n  overflow:hidden;\r\n  transition:.32s var(--idr-ease);\r\n}\r\n#idr-app .idr-cart.is-collapsed{width:auto;}\r\n#idr-app .idr-cart__head{\r\n  display:flex;align-items:center;gap:12px;\r\n  padding:15px 18px;background:var(--idr-ink);color:#fff;\r\n  flex:none;\r\n}\r\n#idr-app .idr-cart__icon{position:relative;display:grid;place-items:center;}\r\n#idr-app .idr-cart__icon svg{width:19px;height:19px;stroke:#fff;}\r\n#idr-app .idr-cart__badge{\r\n  position:absolute;top:-7px;right:-9px;min-width:17px;height:17px;padding:0 4px;\r\n  border-radius:999px;background:var(--idr-primary-300);color:var(--idr-ink);\r\n  font-family:var(--idr-mono);font-size:10px;font-weight:500;\r\n  display:grid;place-items:center;\r\n}\r\n#idr-app .idr-cart__t{\r\n  font-family:var(--idr-display);font-size:17px;font-weight:600;\r\n  text-transform:uppercase;letter-spacing:.06em;flex:1;white-space:nowrap;\r\n}\r\n#idr-app .idr-cart.is-collapsed .idr-cart__t{display:none;}\r\n#idr-app .idr-cart__toggle{\r\n  width:28px;height:28px;display:grid;place-items:center;border-radius:6px;\r\n  transition:.2s var(--idr-ease);\r\n}\r\n#idr-app .idr-cart__toggle:hover{background:rgba(255,255,255,.12);}\r\n#idr-app .idr-cart__toggle svg{width:15px;height:15px;stroke:#fff;transition:.3s var(--idr-ease);}\r\n#idr-app .idr-cart.is-collapsed .idr-cart__toggle svg{transform:rotate(180deg);}\r\n#idr-app .idr-cart__body{overflow-y:auto;flex:1;padding:6px 0;}\r\n#idr-app .idr-cart.is-collapsed .idr-cart__body,\r\n#idr-app .idr-cart.is-collapsed .idr-cart__foot{display:none;}\r\n#idr-app .idr-cart__empty{padding:34px 22px;text-align:center;}\r\n#idr-app .idr-cart__empty svg{width:34px;height:34px;stroke:var(--idr-line);margin:0 auto 12px;}\r\n#idr-app .idr-cart__empty p{font-size:13.5px;color:var(--idr-muted);}\r\n#idr-app .idr-cart__group{padding:12px 18px 4px;}\r\n#idr-app .idr-cart__groupT{\r\n  font-family:var(--idr-mono);font-size:9.5px;letter-spacing:.16em;text-transform:uppercase;\r\n  color:var(--idr-muted);padding-bottom:7px;border-bottom:1px solid var(--idr-line-soft);\r\n}\r\n#idr-app .idr-cart__item{\r\n  display:flex;align-items:flex-start;gap:10px;\r\n  padding:11px 18px;transition:.18s var(--idr-ease);\r\n}\r\n#idr-app .idr-cart__item:hover{background:var(--idr-surface-2);}\r\n#idr-app .idr-cart__itemMain{flex:1;min-width:0;}\r\n#idr-app .idr-cart__itemName{font-size:14px;font-weight:500;line-height:1.35;}\r\n#idr-app .idr-cart__itemMeta{font-size:12px;color:var(--idr-muted);margin-top:2px;}\r\n#idr-app .idr-cart__qty{\r\n  font-family:var(--idr-mono);font-size:11.5px;color:var(--idr-primary);\r\n  background:var(--idr-primary-050);padding:2px 7px;border-radius:5px;flex:none;margin-top:1px;\r\n}\r\n#idr-app .idr-cart__qty--info{\r\n  background:var(--idr-line-soft);color:var(--idr-muted);letter-spacing:.1em;\r\n  text-transform:uppercase;font-size:9.5px;\r\n}\r\n#idr-app .idr-cart__tools{display:flex;gap:2px;flex:none;opacity:.45;transition:.2s var(--idr-ease);}\r\n#idr-app .idr-cart__item:hover .idr-cart__tools{opacity:1;}\r\n#idr-app .idr-cart__tool{\r\n  width:26px;height:26px;display:grid;place-items:center;border-radius:6px;transition:.18s var(--idr-ease);\r\n}\r\n#idr-app .idr-cart__tool svg{width:13px;height:13px;stroke:var(--idr-muted);}\r\n#idr-app .idr-cart__tool:hover{background:var(--idr-line-soft);}\r\n#idr-app .idr-cart__tool:hover svg{stroke:var(--idr-primary);}\r\n#idr-app .idr-cart__tool--del:hover{background:var(--idr-danger-soft);}\r\n#idr-app .idr-cart__tool--del:hover svg{stroke:var(--idr-danger);}\r\n#idr-app .idr-cart__foot{\r\n  padding:16px 18px;border-top:1px solid var(--idr-line);\r\n  background:var(--idr-surface-2);flex:none;\r\n}\r\n#idr-app .idr-cart__total{\r\n  display:flex;align-items:baseline;justify-content:space-between;margin-bottom:12px;\r\n}\r\n#idr-app .idr-cart__totalK{font-family:var(--idr-mono);font-size:10px;letter-spacing:.14em;text-transform:uppercase;color:var(--idr-muted);}\r\n#idr-app .idr-cart__totalV{font-family:var(--idr-display);font-size:20px;font-weight:600;}\r\n#idr-app .idr-cart__reset{display:block;margin:11px auto 0;}\r\n\r\n\/* ------------------------------------------------------------------\r\n   9. MODALES\r\n------------------------------------------------------------------ *\/\r\n#idr-app .idr-overlay{\r\n  position:fixed;inset:0;z-index:90;\r\n  background:rgba(15,28,42,.62);\r\n  backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);\r\n  display:grid;place-items:center;padding:20px;\r\n  opacity:0;visibility:hidden;transition:.28s var(--idr-ease);\r\n}\r\n#idr-app .idr-overlay.is-open{opacity:1;visibility:visible;}\r\n#idr-app .idr-modal{\r\n  width:100%;max-width:560px;max-height:88vh;\r\n  background:var(--idr-surface);border-radius:var(--idr-r-l);\r\n  box-shadow:var(--idr-sh-3);display:flex;flex-direction:column;overflow:hidden;\r\n  transform:translateY(14px) scale(.985);transition:.28s var(--idr-ease);\r\n}\r\n#idr-app .idr-overlay.is-open .idr-modal{transform:none;}\r\n#idr-app .idr-modal--wide{max-width:860px;}\r\n#idr-app .idr-modal__head{\r\n  padding:24px 28px 18px;border-bottom:1px solid var(--idr-line-soft);flex:none;\r\n  display:flex;align-items:flex-start;gap:16px;\r\n}\r\n#idr-app .idr-modal__head .idr-eyebrow{display:block;margin-bottom:6px;}\r\n#idr-app .idr-modal__x{\r\n  width:32px;height:32px;display:grid;place-items:center;border-radius:8px;flex:none;\r\n  transition:.18s var(--idr-ease);\r\n}\r\n#idr-app .idr-modal__x:hover{background:var(--idr-line-soft);}\r\n#idr-app .idr-modal__x svg{width:15px;height:15px;stroke:var(--idr-muted);}\r\n#idr-app .idr-modal__body{padding:24px 28px;overflow-y:auto;flex:1;}\r\n#idr-app .idr-modal__foot{\r\n  padding:18px 28px;border-top:1px solid var(--idr-line);background:var(--idr-surface-2);\r\n  display:flex;gap:12px;justify-content:flex-end;flex:none;flex-wrap:wrap;\r\n}\r\n#idr-app .idr-modal__icon{\r\n  width:44px;height:44px;border-radius:50%;display:grid;place-items:center;flex:none;\r\n  background:var(--idr-warn-soft);\r\n}\r\n#idr-app .idr-modal__icon svg{width:21px;height:21px;stroke:var(--idr-warn);}\r\n\r\n\/* -- Resumen de cotizaci\u00f3n ------------------------------------------ *\/\r\n#idr-app .idr-sum__sec{margin-bottom:26px;}\r\n#idr-app .idr-sum__secT{\r\n  font-family:var(--idr-mono);font-size:10px;letter-spacing:.16em;text-transform:uppercase;\r\n  color:var(--idr-muted);padding-bottom:8px;border-bottom:1px solid var(--idr-line);margin-bottom:12px;\r\n}\r\n#idr-app .idr-sum__line{\r\n  display:flex;align-items:baseline;justify-content:space-between;gap:20px;\r\n  padding:8px 0;border-bottom:1px dashed var(--idr-line-soft);\r\n}\r\n#idr-app .idr-sum__line:last-child{border-bottom:none;}\r\n#idr-app .idr-sum__k{font-size:14px;}\r\n#idr-app .idr-sum__k small{display:block;color:var(--idr-muted);font-size:12px;}\r\n#idr-app .idr-sum__v{font-family:var(--idr-mono);font-size:13px;color:var(--idr-muted);white-space:nowrap;}\r\n#idr-app .idr-form{display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));gap:14px;}\r\n#idr-app .idr-form .idr-field--full{grid-column:1\/-1;}\r\n\r\n\/* -- Confirmaci\u00f3n de env\u00edo ------------------------------------------ *\/\r\n#idr-app .idr-done{text-align:center;padding:26px 10px;}\r\n#idr-app .idr-done__ring{\r\n  width:62px;height:62px;border-radius:50%;margin:0 auto 18px;\r\n  display:grid;place-items:center;background:rgba(44,122,88,.1);\r\n}\r\n#idr-app .idr-done__ring svg{width:27px;height:27px;stroke:var(--idr-ok);}\r\n#idr-app .idr-done p{color:var(--idr-muted);margin-top:8px;font-size:14.5px;}\r\n#idr-app .idr-code{\r\n  font-family:var(--idr-mono);font-size:13px;letter-spacing:.1em;\r\n  background:var(--idr-surface-2);padding:9px 14px;border-radius:8px;\r\n  display:inline-block;margin-top:16px;border:1px solid var(--idr-line);\r\n}\r\n\r\n\/* ------------------------------------------------------------------\r\n   10. TOAST\r\n------------------------------------------------------------------ *\/\r\n#idr-app .idr-toast{\r\n  position:fixed;left:50%;bottom:28px;transform:translate(-50%,18px);\r\n  z-index:95;padding:13px 22px;border-radius:999px;\r\n  background:var(--idr-ink);color:#fff;\r\n  font-family:var(--idr-mono);font-size:11.5px;letter-spacing:.06em;\r\n  box-shadow:var(--idr-sh-3);opacity:0;visibility:hidden;\r\n  transition:.3s var(--idr-ease);max-width:calc(100vw - 40px);text-align:center;\r\n}\r\n#idr-app .idr-toast.is-on{opacity:1;visibility:visible;transform:translate(-50%,0);}\r\n\r\n\/* ------------------------------------------------------------------\r\n   11. ANIMACI\u00d3N DE ENTRADA DE VISTA\r\n------------------------------------------------------------------ *\/\r\n@keyframes idrRise{from{opacity:0;transform:translateY(12px);}to{opacity:1;transform:none;}}\r\n#idr-app .idr-anim{animation:idrRise .42s var(--idr-ease) both;}\r\n#idr-app .idr-anim-1{animation-delay:.04s;}\r\n#idr-app .idr-anim-2{animation-delay:.10s;}\r\n#idr-app .idr-anim-3{animation-delay:.16s;}\r\n\r\n\/* ------------------------------------------------------------------\r\n   12. RESPONSIVE\r\n------------------------------------------------------------------ *\/\r\n@media (max-width:1180px){\r\n  #idr-app{--idr-cart-w:340px;}\r\n  #idr-app .idr-stage{grid-template-columns:184px minmax(0,1fr);}\r\n}\r\n@media (max-width:1024px){\r\n  #idr-app .idr-hero__in{grid-template-columns:1fr;align-items:start;}\r\n  #idr-app .idr-hero__meta{\r\n    flex-direction:row;align-items:flex-start;justify-content:flex-start;gap:38px;\r\n    border-right:none;border-top:1px solid rgba(255,255,255,.14);padding:20px 0 0;\r\n  }\r\n  #idr-app .idr-hero__metaItem{text-align:left;}\r\n  #idr-app .idr-stage{grid-template-columns:1fr;gap:22px;}\r\n\r\n  \/* El registro de sondaje pasa a lectura horizontal *\/\r\n  #idr-app .idr-log{position:sticky;top:74px;z-index:30;background:var(--idr-canvas);padding:12px 0;}\r\n  #idr-app .idr-log__head{display:none;}\r\n  #idr-app .idr-log__col{flex-direction:row;gap:6px;}\r\n  #idr-app .idr-log__step{\r\n    grid-template-columns:1fr;gap:6px;flex:1;min-width:0;\r\n  }\r\n  #idr-app .idr-log__core{height:7px;border-radius:3px !important;}\r\n  #idr-app .idr-log__step.is-active .idr-log__core{box-shadow:inset 0 3px 0 #ffffff;}\r\n  #idr-app .idr-log__label{font-size:11px;letter-spacing:.02em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}\r\n  #idr-app .idr-log__n,#idr-app .idr-log__value{display:none;}\r\n}\r\n@media (max-width:860px){\r\n  #idr-app .idr-wrap{padding:0 18px;}\r\n  \/* Carrito como hoja inferior *\/\r\n  #idr-app .idr-cart{\r\n    top:auto;bottom:0;right:0;left:0;width:100%;max-width:100%;\r\n    border-radius:var(--idr-r-l) var(--idr-r-l) 0 0;\r\n    max-height:76vh;\r\n  }\r\n  #idr-app .idr-cart.is-collapsed{width:100%;}\r\n  #idr-app .idr-cart.is-collapsed .idr-cart__t{display:block;}\r\n  #idr-app .idr-toast{bottom:92px;}\r\n  #idr-app .idr-section{padding-bottom:130px;}\r\n  #idr-app .idr-actions{flex-direction:column;align-items:stretch;}\r\n  #idr-app .idr-actions__r{flex-direction:column-reverse;align-items:stretch;}\r\n  #idr-app .idr-actions .idr-btn{width:100%;}\r\n  #idr-app .idr-modal__foot{flex-direction:column-reverse;}\r\n  #idr-app .idr-modal__foot .idr-btn{width:100%;}\r\n}\r\n@media (max-width:520px){\r\n  #idr-app .idr-brand__rule,#idr-app .idr-brand__sub{display:none;}\r\n  #idr-app .idr-topbar__in{height:64px;}\r\n  #idr-app .idr-row{flex-wrap:wrap;}\r\n}\r\n\r\n\/* ------------------------------------------------------------------\r\n   13. PREFERENCIAS DE MOVIMIENTO \/ IMPRESI\u00d3N\r\n------------------------------------------------------------------ *\/\r\n@media (prefers-reduced-motion:reduce){\r\n  #idr-app *,#idr-app *::before,#idr-app *::after{\r\n    animation-duration:.01ms !important;animation-iteration-count:1 !important;\r\n    transition-duration:.01ms !important;scroll-behavior:auto !important;\r\n  }\r\n}\r\n@media print{\r\n  #idr-app .idr-cart,#idr-app .idr-topbar,#idr-app .idr-hero,#idr-app .idr-log{display:none !important;}\r\n}\r\n<\/style>\r\n<\/head>\r\n<body>\r\n\r\n<!-- ============================================================ -->\r\n<!-- ==== FILE: index.html ====================================== -->\r\n<!-- Marcado m\u00ednimo: la aplicaci\u00f3n inyecta las vistas en           -->\r\n<!-- [data-idr-view], el carrito en [data-idr-cart] y los modales  -->\r\n<!-- en [data-idr-modals]. Copiar desde aqu\u00ed hasta el \u00faltimo       -->\r\n<!-- <\/script> para pegar en el Widget HTML de Elementor.          -->\r\n<!-- ============================================================ -->\r\n<div id=\"idr-app\" data-idr-root>\r\n\r\n  <!-- ---------- Barra superior: marca + idioma ---------- -->\r\n  <header class=\"idr-topbar\">\r\n    <div class=\"idr-wrap idr-topbar__in\">\r\n      <div class=\"idr-brand\">\r\n        <span class=\"idr-brand__mark\">INNRA<\/span>\r\n        <span class=\"idr-brand__rule\" aria-hidden=\"true\"><\/span>\r\n        <span class=\"idr-brand__sub\" data-idr-t=\"brand.tagline\">Expertos en Ingenier\u00eda<\/span>\r\n      <\/div>\r\n      <nav class=\"idr-lang\" data-idr-lang aria-label=\"Idioma \/ Language\"><\/nav>\r\n    <\/div>\r\n  <\/header>\r\n\r\n  <!-- ---------- Hero ---------- -->\r\n  <section class=\"idr-hero\" data-idr-hero>\r\n    <!-- Motivo de fondo: boca de pozo + marcas de profundidad -->\r\n    <svg class=\"idr-hero__bg\" viewBox=\"0 0 1440 620\" preserveAspectRatio=\"xMidYMid slice\" aria-hidden=\"true\">\r\n      <defs>\r\n        <radialGradient id=\"idrBore\" cx=\"78%\" cy=\"52%\" r=\"58%\">\r\n          <stop offset=\"0%\" stop-color=\"#15559c\" stop-opacity=\".34\"\/>\r\n          <stop offset=\"55%\" stop-color=\"#123F6D\" stop-opacity=\".22\"\/>\r\n          <stop offset=\"100%\" stop-color=\"#0f1c2a\" stop-opacity=\"0\"\/>\r\n        <\/radialGradient>\r\n      <\/defs>\r\n      <rect width=\"1440\" height=\"620\" fill=\"url(#idrBore)\"\/>\r\n      <g stroke=\"#ffffff\" stroke-opacity=\".10\" fill=\"none\">\r\n        <circle cx=\"1122\" cy=\"322\" r=\"66\"\/><circle cx=\"1122\" cy=\"322\" r=\"126\"\/>\r\n        <circle cx=\"1122\" cy=\"322\" r=\"192\"\/><circle cx=\"1122\" cy=\"322\" r=\"266\"\/>\r\n        <circle cx=\"1122\" cy=\"322\" r=\"348\"\/>\r\n      <\/g>\r\n      <circle cx=\"1122\" cy=\"322\" r=\"66\" fill=\"#ffffff\" fill-opacity=\".03\"\/>\r\n      <g stroke=\"#ffffff\" stroke-opacity=\".14\">\r\n        <line x1=\"0\" y1=\"322\" x2=\"1440\" y2=\"322\" stroke-dasharray=\"2 10\"\/>\r\n        <line x1=\"1122\" y1=\"0\" x2=\"1122\" y2=\"620\" stroke-dasharray=\"2 10\"\/>\r\n      <\/g>\r\n      <g stroke=\"#ffffff\" stroke-opacity=\".08\">\r\n        <!-- Escala de profundidad -->\r\n        <line x1=\"60\" y1=\"0\" x2=\"60\" y2=\"620\"\/>\r\n        <line x1=\"46\" y1=\"80\"  x2=\"60\" y2=\"80\"\/><line x1=\"52\" y1=\"140\" x2=\"60\" y2=\"140\"\/>\r\n        <line x1=\"46\" y1=\"200\" x2=\"60\" y2=\"200\"\/><line x1=\"52\" y1=\"260\" x2=\"60\" y2=\"260\"\/>\r\n        <line x1=\"46\" y1=\"320\" x2=\"60\" y2=\"320\"\/><line x1=\"52\" y1=\"380\" x2=\"60\" y2=\"380\"\/>\r\n        <line x1=\"46\" y1=\"440\" x2=\"60\" y2=\"440\"\/><line x1=\"52\" y1=\"500\" x2=\"60\" y2=\"500\"\/>\r\n      <\/g>\r\n    <\/svg>\r\n\r\n    <div class=\"idr-wrap idr-hero__in\">\r\n      <div>\r\n        <span class=\"idr-eyebrow\" data-idr-t=\"hero.eyebrow\"><\/span>\r\n        <h1 class=\"idr-h1 idr-hero__title\" data-idr-html=\"hero.title\"><\/h1>\r\n        <p class=\"idr-lead idr-hero__lead\" data-idr-t=\"hero.subtitle\"><\/p>\r\n        <button class=\"idr-btn idr-btn--lg idr-btn--onDark\" data-idr-action=\"start\">\r\n          <span data-idr-t=\"hero.cta\"><\/span>\r\n          <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M5 12h14M13 6l6 6-6 6\"\/><\/svg>\r\n        <\/button>\r\n      <\/div>\r\n      <div class=\"idr-hero__meta\">\r\n        <div class=\"idr-hero__metaItem\">\r\n          <div class=\"idr-hero__metaK\" data-idr-t=\"hero.meta1k\"><\/div>\r\n          <div class=\"idr-hero__metaV\" data-idr-t=\"hero.meta1v\"><\/div>\r\n        <\/div>\r\n        <div class=\"idr-hero__metaItem\">\r\n          <div class=\"idr-hero__metaK\" data-idr-t=\"hero.meta2k\"><\/div>\r\n          <div class=\"idr-hero__metaV\" data-idr-t=\"hero.meta2v\"><\/div>\r\n        <\/div>\r\n        <div class=\"idr-hero__metaItem\">\r\n          <div class=\"idr-hero__metaK\" data-idr-t=\"hero.meta3k\"><\/div>\r\n          <div class=\"idr-hero__metaV\" data-idr-t=\"hero.meta3v\"><\/div>\r\n        <\/div>\r\n      <\/div>\r\n    <\/div>\r\n  <\/section>\r\n\r\n  <!-- ---------- Escenario del configurador ---------- -->\r\n  <main class=\"idr-section\" data-idr-stage hidden>\r\n    <div class=\"idr-wrap idr-stage\">\r\n      <!-- Registro de sondaje (indicador de progreso) -->\r\n      <aside class=\"idr-log\" data-idr-log aria-label=\"Progreso\"><\/aside>\r\n      <!-- Vista activa -->\r\n      <div data-idr-view><\/div>\r\n    <\/div>\r\n  <\/main>\r\n\r\n  <!-- ---------- Carrito fijo ---------- -->\r\n  <aside data-idr-cart aria-live=\"polite\"><\/aside>\r\n\r\n  <!-- ---------- Contenedor de modales ---------- -->\r\n  <div data-idr-modals><\/div>\r\n\r\n  <!-- ---------- Toast ---------- -->\r\n  <div class=\"idr-toast\" data-idr-toast role=\"status\"><\/div>\r\n\r\n<\/div>\r\n\r\n<!-- ============================================================ -->\r\n<!-- ==== FILE: translations.js ================================= -->\r\n<!-- Diccionario \u00fanico de textos de interfaz.                      -->\r\n<!-- Para a\u00f1adir un idioma: agregar una clave de 2-5 letras con la -->\r\n<!-- misma estructura y sumarla a IDR_LANGS.                       -->\r\n<!-- ============================================================ -->\r\n<script>\r\n\/* Idiomas disponibles en el selector. `code` es la clave del diccionario. *\/\r\nconst IDR_LANGS = [\r\n  { code: 'es', short: 'ES', label: 'Espa\u00f1ol' },\r\n  { code: 'en', short: 'EN', label: 'English' },\r\n  { code: 'pt', short: 'PT', label: 'Portugu\u00eas (BR)' }\r\n];\r\n\r\nconst IDR_I18N = {\r\n\r\n  \/* ------------------------------------------------------------ *\/\r\n  \/* ESPA\u00d1OL                                                       *\/\r\n  \/* ------------------------------------------------------------ *\/\r\n  es: {\r\n    'brand.tagline': 'Expertos en Ingenier\u00eda',\r\n\r\n    'hero.eyebrow': 'Configurador de equipos IDR',\r\n    'hero.title': 'Cotiza nuestros equipos <em>IDR<\/em>',\r\n    'hero.subtitle': 'Arma la configuraci\u00f3n a tus necesidades y requerimientos.',\r\n    'hero.cta': 'Comenzar Configuraci\u00f3n',\r\n    'hero.meta1k': 'Operaci\u00f3n',\r\n    'hero.meta1v': 'Superficie y subterr\u00e1nea',\r\n    'hero.meta2k': 'Sistemas',\r\n    'hero.meta2v': 'Manipulaci\u00f3n \u00b7 Telemetr\u00eda \u00b7 Muestras',\r\n    'hero.meta3k': 'Respuesta',\r\n    'hero.meta3v': '48 horas h\u00e1biles',\r\n\r\n    'common.back': 'Volver',\r\n    'common.continue': 'Continuar',\r\n    'common.save': 'Guardar y volver',\r\n    'common.cancel': 'Cancelar',\r\n    'common.yes': 'S\u00ed',\r\n    'common.no': 'No',\r\n    'common.qty': 'Cantidad',\r\n    'common.units': 'unidades',\r\n    'common.unit': 'unidad',\r\n    'common.edit': 'Editar',\r\n    'common.remove': 'Eliminar',\r\n    'common.other': 'Otra',\r\n    'common.close': 'Cerrar',\r\n    'common.configured': 'Configurado',\r\n    'common.pending': 'Sin configurar',\r\n    'common.selectToContinue': 'Selecciona una opci\u00f3n para continuar',\r\n\r\n    'steps.operation': 'Operaci\u00f3n',\r\n    'steps.rig': 'Perforadora',\r\n    'steps.rods': 'Barras',\r\n    'steps.equipment': 'Equipos',\r\n    'steps.quote': 'Cotizaci\u00f3n',\r\n    'log.title': 'Registro',\r\n    'log.depth': 'Avance',\r\n\r\n    'op.eyebrow': 'Paso 1 de 5',\r\n    'op.title': 'Tipo de operaci\u00f3n',\r\n    'op.desc': 'La operaci\u00f3n define los modelos de perforadora, los di\u00e1metros de barra y los sistemas IDR disponibles.',\r\n    'op.surface': 'Superficie',\r\n    'op.surfaceDesc': 'Sondaje diamantino desde plataforma en superficie.',\r\n    'op.underground': 'Subterr\u00e1nea',\r\n    'op.undergroundDesc': 'Sondaje en galer\u00eda o c\u00e1mara de perforaci\u00f3n.',\r\n\r\n    'rig.eyebrow': 'Paso 2 de 5',\r\n    'rig.title': 'Perforadora',\r\n    'rig.desc': 'Indica el modelo con el que operas y cu\u00e1ntas unidades quieres equipar.',\r\n    'rig.model': 'Modelo de perforadora',\r\n    'rig.otherLabel': 'Escribe el modelo',\r\n    'rig.otherPlaceholder': 'Ej.: Versadrill V-2000',\r\n    'rig.feasibility': 'Este modelo queda sujeto a factibilidad t\u00e9cnica.',\r\n    'rig.quantity': 'Cantidad de perforadoras',\r\n    'rig.quantityHint': 'Se cotizar\u00e1 un set de equipos por cada perforadora indicada.',\r\n\r\n    'rods.eyebrow': 'Paso 3 de 5',\r\n    'rods.title': 'Barras',\r\n    'rods.desc': 'Estos son los di\u00e1metros de barra con los que operan los equipos IDR en esta configuraci\u00f3n.',\r\n    'rods.spec': 'Di\u00e1metros compatibles',\r\n    'rods.hint': 'Informaci\u00f3n de referencia.',\r\n    'rods.infoNote': 'Las barras no se seleccionan ni se cotizan: se muestran s\u00f3lo para recordar con qu\u00e9 di\u00e1metros trabajan los equipos.',\r\n\r\n    'equip.eyebrow': 'Paso 4 de 5',\r\n    'equip.title': 'Equipos disponibles',\r\n    'equip.desc': 'Abre cada sistema para configurarlo. Tu selecci\u00f3n se conserva al entrar y salir de cada vista.',\r\n    'equip.open': 'Configurar',\r\n    'equip.review': 'Revisar',\r\n\r\n    'hd.eyebrow': 'Sistema de Manipulaci\u00f3n de Barras',\r\n    'hd.title': 'Manipulaci\u00f3n de barras',\r\n    'hd.desc': 'Configura el equipo principal, el almacenamiento y la extracci\u00f3n de testigos.',\r\n    'hd.mainEquip': 'Equipo principal',\r\n    'hd.etDesc': 'Brazo manipulador de barras para plataforma de superficie.',\r\n    'hd.storage': 'Tipo de almacenamiento',\r\n    'hd.storageDesc': 'Define c\u00f3mo se acopian las barras en plataforma.',\r\n    'hd.addons': 'Complementos de la cama',\r\n    'hd.ampliroll': 'Ampliroll',\r\n    'hd.amplirollDesc': 'Puede cotizarse por s\u00ed solo.',\r\n    'hd.conveyor': 'Agregar Transportador',\r\n    'hd.conveyorDesc': 'Requiere Ampliroll. Disponible s\u00f3lo con Cama 350H.',\r\n    'hd.conveyorAuto': 'Se agreg\u00f3 el Ampliroll: el transportador no puede cotizarse sin \u00e9l.',\r\n    'hd.cutting': 'Sistema de Corte',\r\n    'hd.cuttingDesc': 'Cada equipo habilita al siguiente de la l\u00ednea.',\r\n    'hd.cuttingNote': 'El Centralizador requiere el Conjunto de Prensas, y \u00e9ste la Unidad de Corte. Las configuraciones fuera de esta secuencia las eval\u00faa ingenier\u00eda caso a caso.',\r\n    'hd.requires': 'Requiere',\r\n    'hd.core': 'Manejo de Muestras',\r\n    'hd.coreDesc': 'Indica cantidad por cada elemento requerido.',\r\n\r\n    'etwarn.title': 'Advertencia',\r\n    'etwarn.body': 'Est\u00e1s agregando un equipo sin elegir el brazo manipulador ET. \u00bfA\u00fan deseas continuar?',\r\n    'etfinal.title': 'Advertencia',\r\n    'etfinal.body': 'Tu configuraci\u00f3n no incluye el brazo manipulador ET. \u00bfDeseas enviar la solicitud de todas formas?',\r\n    'etfinal.yes': 'S\u00ed, continuar',\r\n    'warn.title': 'Revisa esta configuraci\u00f3n',\r\n    'warn.body': 'Est\u00e1 seleccionando \u00fanicamente una cama manual sin el equipo ET. Esta configuraci\u00f3n podr\u00eda no corresponder a una soluci\u00f3n completa. \u00bfDesea continuar?',\r\n\r\n    'sub.eyebrow': 'Equipo para operaci\u00f3n subterr\u00e1nea',\r\n    'sub.title': 'Brazo Manipulador ET SUB',\r\n    'sub.desc': 'Manipulaci\u00f3n de barras para perforaci\u00f3n subterr\u00e1nea.',\r\n    'sub.armDesc': 'Brazo manipulador dise\u00f1ado para galer\u00edas y c\u00e1maras de perforaci\u00f3n.',\r\n\r\n    'tel.eyebrow': 'Sistema de Telemetr\u00eda y Telesupervisi\u00f3n',\r\n    'tel.title': 'Telemetr\u00eda',\r\n    'tel.desc': 'Monitoreo remoto de la operaci\u00f3n de sondaje.',\r\n    'tel.q1': '\u00bfQu\u00e9 desea cotizar?',\r\n    'tel.probe': 'Telemetr\u00eda para Sonda',\r\n    'tel.probeDesc': 'Instrumentaci\u00f3n embarcada en la perforadora.',\r\n    'tel.cart': 'Carro de Telesupervisi\u00f3n IDR-CS',\r\n    'tel.cartDesc': 'Puesto m\u00f3vil de telesupervisi\u00f3n en faena.',\r\n    'tel.surfaceOnly': 'S\u00f3lo disponible para operaci\u00f3n de superficie.',\r\n    'tel.q2': '\u00bfQu\u00e9 desea monitorear?',\r\n    'tel.video': 'Solo Video',\r\n    'tel.videoDesc': 'Captura y transmisi\u00f3n de imagen en faena.',\r\n    'tel.data': 'Solo Datos',\r\n    'tel.dataDesc': 'Variables operacionales de la perforadora.',\r\n    'tel.dataVideo': 'Datos + Video',\r\n    'tel.dataVideoDesc': 'Cobertura completa de la operaci\u00f3n.',\r\n    'tel.benefit': 'Clientes con telemetr\u00eda IDR-TEL han documentado hasta un <strong>15% de ahorro<\/strong> frente a malas pr\u00e1cticas y un <strong>50% m\u00e1s capacidad de respuesta<\/strong> ante eventos operacionales.',\r\n    'tel.varsTitle': 'Variables de telemetr\u00eda incluidas',\r\n    'tel.qtyLabel': 'Cantidad de sondas a instrumentar',\r\n\r\n    'cs.title': 'Carro de Telesupervisi\u00f3n IDR-CS',\r\n    'cs.q': 'Modalidad',\r\n    'cs.purchase': 'Compra',\r\n    'cs.purchaseDesc': 'Adquisici\u00f3n del carro en propiedad.',\r\n    'cs.rental': 'Arriendo',\r\n    'cs.rentalDesc': 'Arriendo mensual con soporte incluido.',\r\n    'cs.qtyLabel': 'Cantidad de carros',\r\n    'cs.addons': 'Accesorios',\r\n    'cs.message': 'El carro puede utilizarse \u00fanicamente para telesupervisi\u00f3n o, agregando el HUB, como puente de comunicaciones hacia la perforadora.',\r\n\r\n    'sm.eyebrow': 'Sistema de Extracci\u00f3n de Muestras',\r\n    'sm.title': 'Extracci\u00f3n de muestras',\r\n    'sm.desc': 'Equipos de extracci\u00f3n y corte de testigos en plataforma.',\r\n    'sm.items': 'Equipos disponibles',\r\n\r\n    'notes.title': 'Observaciones',\r\n    'notes.desc': 'Condiciones de faena, plazos, requisitos especiales o cualquier detalle que debamos considerar.',\r\n    'notes.placeholder': 'Ej.: faena a 4.200 msnm, sin acceso a red celular, entrega requerida para marzo.',\r\n\r\n    'cart.title': 'Tu configuraci\u00f3n',\r\n    'cart.empty': 'A\u00fan no hay selecciones',\r\n    'cart.emptyHint': 'Cada elemento que elijas se guardar\u00e1 aqu\u00ed autom\u00e1ticamente.',\r\n    'cart.operation': 'Operaci\u00f3n',\r\n    'cart.rig': 'Perforadora',\r\n    'cart.rods': 'Barras',\r\n    'cart.rodsInfo': 'Referencia t\u00e9cnica \u00b7 no se cotiza',\r\n    'quote.rodsRef': 'Barras compatibles',\r\n    'cart.infoTag': 'Info',\r\n    'cart.handling': 'Manipulaci\u00f3n de barras',\r\n    'cart.subhandling': 'Manipulaci\u00f3n subterr\u00e1nea',\r\n    'cart.telemetry': 'Telemetr\u00eda',\r\n    'cart.sampling': 'Extracci\u00f3n de muestras',\r\n    'cart.accessories': 'Accesorios',\r\n    'cart.notes': 'Observaciones',\r\n    'cart.request': 'Solicitar Cotizaci\u00f3n',\r\n    'cart.reset': 'Vaciar configuraci\u00f3n',\r\n    'cart.resetConfirm': '\u00bfVaciar toda la configuraci\u00f3n? Se perder\u00e1n todas las selecciones.',\r\n    'cart.total': 'Total estimado',\r\n    'cart.priceOnRequest': 'A cotizar',\r\n    'cart.lines': '\u00edtems',\r\n\r\n    'quote.eyebrow': 'Paso 5 de 5',\r\n    'quote.title': 'Resumen de tu solicitud',\r\n    'quote.desc': 'Revisa la configuraci\u00f3n antes de enviarla. Nuestro equipo responde en un plazo de 48 horas h\u00e1biles.',\r\n    'quote.edit': 'Editar Configuraci\u00f3n',\r\n    'quote.send': 'Enviar Solicitud',\r\n    'quote.contact': 'Datos de contacto',\r\n    'quote.name': 'Nombre y apellido',\r\n    'quote.company': 'Empresa o faena',\r\n    'quote.email': 'Correo electr\u00f3nico',\r\n    'quote.phone': 'Tel\u00e9fono',\r\n    'quote.country': 'Pa\u00eds',\r\n    'quote.channel': 'Canal de respuesta',\r\n    'quote.sending': 'Enviando\u2026',\r\n    'quote.doneTitle': 'Solicitud enviada',\r\n    'quote.doneBody': 'Recibimos tu configuraci\u00f3n. Un especialista IDR te contactar\u00e1 dentro de 48 horas h\u00e1biles.',\r\n    'quote.ref': 'Folio',\r\n    'quote.needContact': 'Completa nombre y correo electr\u00f3nico para enviar la solicitud.',\r\n    'quote.needConfig': 'Agrega al menos un equipo antes de solicitar la cotizaci\u00f3n.',\r\n\r\n    'toast.removed': 'Elemento eliminado de la configuraci\u00f3n',\r\n    'toast.reset': 'Configuraci\u00f3n vaciada',\r\n    'toast.saved': 'Configuraci\u00f3n guardada'\r\n  },\r\n\r\n  \/* ------------------------------------------------------------ *\/\r\n  \/* ENGLISH                                                       *\/\r\n  \/* ------------------------------------------------------------ *\/\r\n  en: {\r\n    'brand.tagline': 'Engineering Experts',\r\n\r\n    'hero.eyebrow': 'IDR equipment configurator',\r\n    'hero.title': 'Quote our <em>IDR<\/em> equipment',\r\n    'hero.subtitle': 'Build the configuration that matches your needs and requirements.',\r\n    'hero.cta': 'Start Configuration',\r\n    'hero.meta1k': 'Operation',\r\n    'hero.meta1v': 'Surface and underground',\r\n    'hero.meta2k': 'Systems',\r\n    'hero.meta2v': 'Handling \u00b7 Telemetry \u00b7 Sampling',\r\n    'hero.meta3k': 'Response',\r\n    'hero.meta3v': '48 business hours',\r\n\r\n    'common.back': 'Back',\r\n    'common.continue': 'Continue',\r\n    'common.save': 'Save and go back',\r\n    'common.cancel': 'Cancel',\r\n    'common.yes': 'Yes',\r\n    'common.no': 'No',\r\n    'common.qty': 'Quantity',\r\n    'common.units': 'units',\r\n    'common.unit': 'unit',\r\n    'common.edit': 'Edit',\r\n    'common.remove': 'Remove',\r\n    'common.other': 'Other',\r\n    'common.close': 'Close',\r\n    'common.configured': 'Configured',\r\n    'common.pending': 'Not configured',\r\n    'common.selectToContinue': 'Select an option to continue',\r\n\r\n    'steps.operation': 'Operation',\r\n    'steps.rig': 'Drill rig',\r\n    'steps.rods': 'Rods',\r\n    'steps.equipment': 'Equipment',\r\n    'steps.quote': 'Quote',\r\n    'log.title': 'Log',\r\n    'log.depth': 'Progress',\r\n\r\n    'op.eyebrow': 'Step 1 of 5',\r\n    'op.title': 'Type of operation',\r\n    'op.desc': 'The operation determines the drill rig models, rod sizes and IDR systems available to you.',\r\n    'op.surface': 'Surface',\r\n    'op.surfaceDesc': 'Diamond drilling from a surface platform.',\r\n    'op.underground': 'Underground',\r\n    'op.undergroundDesc': 'Drilling from a drift or underground chamber.',\r\n\r\n    'rig.eyebrow': 'Step 2 of 5',\r\n    'rig.title': 'Drill rig',\r\n    'rig.desc': 'Tell us which model you run and how many units you want to equip.',\r\n    'rig.model': 'Drill rig model',\r\n    'rig.otherLabel': 'Type the model',\r\n    'rig.otherPlaceholder': 'e.g. Versadrill V-2000',\r\n    'rig.feasibility': 'This model is subject to technical feasibility.',\r\n    'rig.quantity': 'Number of drill rigs',\r\n    'rig.quantityHint': 'One equipment set will be quoted per drill rig.',\r\n\r\n    'rods.eyebrow': 'Step 3 of 5',\r\n    'rods.title': 'Rods',\r\n    'rods.desc': 'These are the rod sizes the IDR equipment runs with in this configuration.',\r\n    'rods.spec': 'Compatible sizes',\r\n    'rods.hint': 'Reference information.',\r\n    'rods.infoNote': 'Rods are neither selected nor quoted: they are shown only as a reminder of the sizes the equipment works with.',\r\n\r\n    'equip.eyebrow': 'Step 4 of 5',\r\n    'equip.title': 'Available equipment',\r\n    'equip.desc': 'Open each system to configure it. Your selections are kept when you enter and leave a view.',\r\n    'equip.open': 'Configure',\r\n    'equip.review': 'Review',\r\n\r\n    'hd.eyebrow': 'Rod Handling System',\r\n    'hd.title': 'Rod handling',\r\n    'hd.desc': 'Configure the main equipment, rod storage and core extraction.',\r\n    'hd.mainEquip': 'Main equipment',\r\n    'hd.etDesc': 'Rod handling arm for surface platforms.',\r\n    'hd.storage': 'Storage type',\r\n    'hd.storageDesc': 'Defines how rods are stacked on the platform.',\r\n    'hd.addons': 'Bed add-ons',\r\n    'hd.ampliroll': 'Ampliroll',\r\n    'hd.amplirollDesc': 'Can be quoted on its own.',\r\n    'hd.conveyor': 'Add Conveyor',\r\n    'hd.conveyorDesc': 'Requires Ampliroll. Available with the 350H Bed only.',\r\n    'hd.conveyorAuto': 'Ampliroll added: the conveyor cannot be quoted without it.',\r\n    'hd.cutting': 'Cutting System',\r\n    'hd.cuttingDesc': 'Each unit unlocks the next one in the line.',\r\n    'hd.cuttingNote': 'The Centralizer requires the Clamp Assembly, which in turn requires the Cutting Unit. Configurations outside this sequence are reviewed by engineering case by case.',\r\n    'hd.requires': 'Requires',\r\n    'hd.core': 'Sample Handling',\r\n    'hd.coreDesc': 'Set the quantity for each item you need.',\r\n\r\n    'etwarn.title': 'Warning',\r\n    'etwarn.body': 'You are adding equipment without selecting the ET handling arm. Do you still want to continue?',\r\n    'etfinal.title': 'Warning',\r\n    'etfinal.body': 'Your configuration does not include the ET handling arm. Do you want to send the request anyway?',\r\n    'etfinal.yes': 'Yes, continue',\r\n    'warn.title': 'Please review this configuration',\r\n    'warn.body': 'You are selecting a manual bed only, without the ET equipment. This configuration may not be a complete solution. Do you want to continue?',\r\n\r\n    'sub.eyebrow': 'Equipment for underground operation',\r\n    'sub.title': 'ET SUB Handling Arm',\r\n    'sub.desc': 'Rod handling for underground drilling.',\r\n    'sub.armDesc': 'Handling arm designed for drifts and underground drilling chambers.',\r\n\r\n    'tel.eyebrow': 'Telemetry and Remote Supervision System',\r\n    'tel.title': 'Telemetry',\r\n    'tel.desc': 'Remote monitoring of the drilling operation.',\r\n    'tel.q1': 'What would you like to quote?',\r\n    'tel.probe': 'Telemetry for the Rig',\r\n    'tel.probeDesc': 'Instrumentation installed on the drill rig.',\r\n    'tel.cart': 'IDR-CS Remote Supervision Cart',\r\n    'tel.cartDesc': 'Mobile remote supervision station on site.',\r\n    'tel.surfaceOnly': 'Available for surface operation only.',\r\n    'tel.q2': 'What would you like to monitor?',\r\n    'tel.video': 'Video only',\r\n    'tel.videoDesc': 'Image capture and streaming on site.',\r\n    'tel.data': 'Data only',\r\n    'tel.dataDesc': 'Operational variables from the drill rig.',\r\n    'tel.dataVideo': 'Data + Video',\r\n    'tel.dataVideoDesc': 'Full coverage of the operation.',\r\n    'tel.benefit': 'Customers running IDR-TEL telemetry have documented up to <strong>15% savings<\/strong> against poor practices and <strong>50% more response capacity<\/strong> during operational events.',\r\n    'tel.varsTitle': 'Telemetry variables included',\r\n    'tel.qtyLabel': 'Number of rigs to instrument',\r\n\r\n    'cs.title': 'IDR-CS Remote Supervision Cart',\r\n    'cs.q': 'Commercial model',\r\n    'cs.purchase': 'Purchase',\r\n    'cs.purchaseDesc': 'Buy the cart outright.',\r\n    'cs.rental': 'Rental',\r\n    'cs.rentalDesc': 'Monthly rental with support included.',\r\n    'cs.qtyLabel': 'Number of carts',\r\n    'cs.addons': 'Add-ons',\r\n    'cs.message': 'The cart can be used for remote supervision only or, by adding the HUB, as a communications bridge to the drill rig.',\r\n\r\n    'sm.eyebrow': 'Sample Extraction System',\r\n    'sm.title': 'Sample extraction',\r\n    'sm.desc': 'Core extraction and cutting equipment for the platform.',\r\n    'sm.items': 'Available equipment',\r\n\r\n    'notes.title': 'Notes',\r\n    'notes.desc': 'Site conditions, deadlines, special requirements or anything else we should take into account.',\r\n    'notes.placeholder': 'e.g. site at 4,200 m.a.s.l., no cellular coverage, delivery required by March.',\r\n\r\n    'cart.title': 'Your configuration',\r\n    'cart.empty': 'Nothing selected yet',\r\n    'cart.emptyHint': 'Everything you choose is saved here automatically.',\r\n    'cart.operation': 'Operation',\r\n    'cart.rig': 'Drill rig',\r\n    'cart.rods': 'Rods',\r\n    'cart.rodsInfo': 'Technical reference \u00b7 not quoted',\r\n    'quote.rodsRef': 'Compatible rods',\r\n    'cart.infoTag': 'Info',\r\n    'cart.handling': 'Rod handling',\r\n    'cart.subhandling': 'Underground handling',\r\n    'cart.telemetry': 'Telemetry',\r\n    'cart.sampling': 'Sample extraction',\r\n    'cart.accessories': 'Add-ons',\r\n    'cart.notes': 'Notes',\r\n    'cart.request': 'Request Quote',\r\n    'cart.reset': 'Clear configuration',\r\n    'cart.resetConfirm': 'Clear the whole configuration? Every selection will be lost.',\r\n    'cart.total': 'Estimated total',\r\n    'cart.priceOnRequest': 'On request',\r\n    'cart.lines': 'items',\r\n\r\n    'quote.eyebrow': 'Step 5 of 5',\r\n    'quote.title': 'Summary of your request',\r\n    'quote.desc': 'Review the configuration before sending it. Our team replies within 48 business hours.',\r\n    'quote.edit': 'Edit Configuration',\r\n    'quote.send': 'Send Request',\r\n    'quote.contact': 'Contact details',\r\n    'quote.name': 'Full name',\r\n    'quote.company': 'Company or site',\r\n    'quote.email': 'Email',\r\n    'quote.phone': 'Phone',\r\n    'quote.country': 'Country',\r\n    'quote.channel': 'Reply channel',\r\n    'quote.sending': 'Sending\u2026',\r\n    'quote.doneTitle': 'Request sent',\r\n    'quote.doneBody': 'We received your configuration. An IDR specialist will contact you within 48 business hours.',\r\n    'quote.ref': 'Reference',\r\n    'quote.needContact': 'Add your name and email to send the request.',\r\n    'quote.needConfig': 'Add at least one piece of equipment before requesting a quote.',\r\n\r\n    'toast.removed': 'Item removed from the configuration',\r\n    'toast.reset': 'Configuration cleared',\r\n    'toast.saved': 'Configuration saved'\r\n  },\r\n\r\n  \/* ------------------------------------------------------------ *\/\r\n  \/* PORTUGU\u00caS (BRASIL)                                            *\/\r\n  \/* ------------------------------------------------------------ *\/\r\n  pt: {\r\n    'brand.tagline': 'Especialistas em Engenharia',\r\n\r\n    'hero.eyebrow': 'Configurador de equipamentos IDR',\r\n    'hero.title': 'Cote nossos equipamentos <em>IDR<\/em>',\r\n    'hero.subtitle': 'Monte a configura\u00e7\u00e3o conforme suas necessidades e requisitos.',\r\n    'hero.cta': 'Iniciar Configura\u00e7\u00e3o',\r\n    'hero.meta1k': 'Opera\u00e7\u00e3o',\r\n    'hero.meta1v': 'Superf\u00edcie e subterr\u00e2nea',\r\n    'hero.meta2k': 'Sistemas',\r\n    'hero.meta2v': 'Manipula\u00e7\u00e3o \u00b7 Telemetria \u00b7 Amostras',\r\n    'hero.meta3k': 'Resposta',\r\n    'hero.meta3v': '48 horas \u00fateis',\r\n\r\n    'common.back': 'Voltar',\r\n    'common.continue': 'Continuar',\r\n    'common.save': 'Salvar e voltar',\r\n    'common.cancel': 'Cancelar',\r\n    'common.yes': 'Sim',\r\n    'common.no': 'N\u00e3o',\r\n    'common.qty': 'Quantidade',\r\n    'common.units': 'unidades',\r\n    'common.unit': 'unidade',\r\n    'common.edit': 'Editar',\r\n    'common.remove': 'Remover',\r\n    'common.other': 'Outra',\r\n    'common.close': 'Fechar',\r\n    'common.configured': 'Configurado',\r\n    'common.pending': 'N\u00e3o configurado',\r\n    'common.selectToContinue': 'Selecione uma op\u00e7\u00e3o para continuar',\r\n\r\n    'steps.operation': 'Opera\u00e7\u00e3o',\r\n    'steps.rig': 'Perfuratriz',\r\n    'steps.rods': 'Hastes',\r\n    'steps.equipment': 'Equipamentos',\r\n    'steps.quote': 'Cota\u00e7\u00e3o',\r\n    'log.title': 'Registro',\r\n    'log.depth': 'Avan\u00e7o',\r\n\r\n    'op.eyebrow': 'Etapa 1 de 5',\r\n    'op.title': 'Tipo de opera\u00e7\u00e3o',\r\n    'op.desc': 'A opera\u00e7\u00e3o define os modelos de perfuratriz, os di\u00e2metros de haste e os sistemas IDR dispon\u00edveis.',\r\n    'op.surface': 'Superf\u00edcie',\r\n    'op.surfaceDesc': 'Sondagem diamantada a partir de plataforma em superf\u00edcie.',\r\n    'op.underground': 'Subterr\u00e2nea',\r\n    'op.undergroundDesc': 'Sondagem em galeria ou c\u00e2mara de perfura\u00e7\u00e3o.',\r\n\r\n    'rig.eyebrow': 'Etapa 2 de 5',\r\n    'rig.title': 'Perfuratriz',\r\n    'rig.desc': 'Informe o modelo que voc\u00ea opera e quantas unidades deseja equipar.',\r\n    'rig.model': 'Modelo de perfuratriz',\r\n    'rig.otherLabel': 'Escreva o modelo',\r\n    'rig.otherPlaceholder': 'Ex.: Versadrill V-2000',\r\n    'rig.feasibility': 'Este modelo fica sujeito \u00e0 viabilidade t\u00e9cnica.',\r\n    'rig.quantity': 'Quantidade de perfuratrizes',\r\n    'rig.quantityHint': 'Ser\u00e1 cotado um conjunto de equipamentos por perfuratriz informada.',\r\n\r\n    'rods.eyebrow': 'Etapa 3 de 5',\r\n    'rods.title': 'Hastes',\r\n    'rods.desc': 'Estes s\u00e3o os di\u00e2metros de haste com que os equipamentos IDR operam nesta configura\u00e7\u00e3o.',\r\n    'rods.spec': 'Di\u00e2metros compat\u00edveis',\r\n    'rods.hint': 'Informa\u00e7\u00e3o de refer\u00eancia.',\r\n    'rods.infoNote': 'As hastes n\u00e3o s\u00e3o selecionadas nem cotadas: aparecem apenas para lembrar com quais di\u00e2metros os equipamentos trabalham.',\r\n\r\n    'equip.eyebrow': 'Etapa 4 de 5',\r\n    'equip.title': 'Equipamentos dispon\u00edveis',\r\n    'equip.desc': 'Abra cada sistema para configur\u00e1-lo. Sua sele\u00e7\u00e3o \u00e9 mantida ao entrar e sair de cada tela.',\r\n    'equip.open': 'Configurar',\r\n    'equip.review': 'Revisar',\r\n\r\n    'hd.eyebrow': 'Sistema de Manipula\u00e7\u00e3o de Hastes',\r\n    'hd.title': 'Manipula\u00e7\u00e3o de hastes',\r\n    'hd.desc': 'Configure o equipamento principal, o armazenamento e a extra\u00e7\u00e3o de testemunhos.',\r\n    'hd.mainEquip': 'Equipamento principal',\r\n    'hd.etDesc': 'Bra\u00e7o manipulador de hastes para plataforma de superf\u00edcie.',\r\n    'hd.storage': 'Tipo de armazenamento',\r\n    'hd.storageDesc': 'Define como as hastes s\u00e3o acomodadas na plataforma.',\r\n    'hd.addons': 'Complementos da cama',\r\n    'hd.ampliroll': 'Ampliroll',\r\n    'hd.amplirollDesc': 'Pode ser cotado sozinho.',\r\n    'hd.conveyor': 'Adicionar Transportador',\r\n    'hd.conveyorDesc': 'Requer Ampliroll. Dispon\u00edvel somente com a Cama 350H.',\r\n    'hd.conveyorAuto': 'Ampliroll adicionado: o transportador n\u00e3o pode ser cotado sem ele.',\r\n    'hd.cutting': 'Sistema de Corte',\r\n    'hd.cuttingDesc': 'Cada equipamento habilita o seguinte da linha.',\r\n    'hd.cuttingNote': 'O Centralizador requer o Conjunto de Prensas, que por sua vez requer a Unidade de Corte. Configura\u00e7\u00f5es fora desta sequ\u00eancia s\u00e3o avaliadas pela engenharia caso a caso.',\r\n    'hd.requires': 'Requer',\r\n    'hd.core': 'Manejo de Amostras',\r\n    'hd.coreDesc': 'Informe a quantidade de cada item necess\u00e1rio.',\r\n\r\n    'etwarn.title': 'Aten\u00e7\u00e3o',\r\n    'etwarn.body': 'Voc\u00ea est\u00e1 adicionando um equipamento sem escolher o bra\u00e7o manipulador ET. Ainda deseja continuar?',\r\n    'etfinal.title': 'Aten\u00e7\u00e3o',\r\n    'etfinal.body': 'Sua configura\u00e7\u00e3o n\u00e3o inclui o bra\u00e7o manipulador ET. Deseja enviar a solicita\u00e7\u00e3o mesmo assim?',\r\n    'etfinal.yes': 'Sim, continuar',\r\n    'warn.title': 'Revise esta configura\u00e7\u00e3o',\r\n    'warn.body': 'Voc\u00ea est\u00e1 selecionando apenas uma cama manual, sem o equipamento ET. Esta configura\u00e7\u00e3o pode n\u00e3o corresponder a uma solu\u00e7\u00e3o completa. Deseja continuar?',\r\n\r\n    'sub.eyebrow': 'Equipamento para opera\u00e7\u00e3o subterr\u00e2nea',\r\n    'sub.title': 'Bra\u00e7o Manipulador ET SUB',\r\n    'sub.desc': 'Manipula\u00e7\u00e3o de hastes para perfura\u00e7\u00e3o subterr\u00e2nea.',\r\n    'sub.armDesc': 'Bra\u00e7o manipulador projetado para galerias e c\u00e2maras de perfura\u00e7\u00e3o.',\r\n\r\n    'tel.eyebrow': 'Sistema de Telemetria e Telessupervis\u00e3o',\r\n    'tel.title': 'Telemetria',\r\n    'tel.desc': 'Monitoramento remoto da opera\u00e7\u00e3o de sondagem.',\r\n    'tel.q1': 'O que voc\u00ea deseja cotar?',\r\n    'tel.probe': 'Telemetria para a Sonda',\r\n    'tel.probeDesc': 'Instrumenta\u00e7\u00e3o embarcada na perfuratriz.',\r\n    'tel.cart': 'Carro de Telessupervis\u00e3o IDR-CS',\r\n    'tel.cartDesc': 'Posto m\u00f3vel de telessupervis\u00e3o na obra.',\r\n    'tel.surfaceOnly': 'Dispon\u00edvel apenas para opera\u00e7\u00e3o de superf\u00edcie.',\r\n    'tel.q2': 'O que voc\u00ea deseja monitorar?',\r\n    'tel.video': 'Somente V\u00eddeo',\r\n    'tel.videoDesc': 'Captura e transmiss\u00e3o de imagem na obra.',\r\n    'tel.data': 'Somente Dados',\r\n    'tel.dataDesc': 'Vari\u00e1veis operacionais da perfuratriz.',\r\n    'tel.dataVideo': 'Dados + V\u00eddeo',\r\n    'tel.dataVideoDesc': 'Cobertura completa da opera\u00e7\u00e3o.',\r\n    'tel.benefit': 'Clientes com telemetria IDR-TEL documentaram at\u00e9 <strong>15% de economia<\/strong> frente a m\u00e1s pr\u00e1ticas e <strong>50% mais capacidade de resposta<\/strong> diante de eventos operacionais.',\r\n    'tel.varsTitle': 'Vari\u00e1veis de telemetria inclu\u00eddas',\r\n    'tel.qtyLabel': 'Quantidade de sondas a instrumentar',\r\n\r\n    'cs.title': 'Carro de Telessupervis\u00e3o IDR-CS',\r\n    'cs.q': 'Modalidade',\r\n    'cs.purchase': 'Compra',\r\n    'cs.purchaseDesc': 'Aquisi\u00e7\u00e3o definitiva do carro.',\r\n    'cs.rental': 'Loca\u00e7\u00e3o',\r\n    'cs.rentalDesc': 'Loca\u00e7\u00e3o mensal com suporte inclu\u00eddo.',\r\n    'cs.qtyLabel': 'Quantidade de carros',\r\n    'cs.addons': 'Acess\u00f3rios',\r\n    'cs.message': 'O carro pode ser usado apenas para telessupervis\u00e3o ou, adicionando o HUB, como ponte de comunica\u00e7\u00e3o com a perfuratriz.',\r\n\r\n    'sm.eyebrow': 'Sistema de Extra\u00e7\u00e3o de Amostras',\r\n    'sm.title': 'Extra\u00e7\u00e3o de amostras',\r\n    'sm.desc': 'Equipamentos de extra\u00e7\u00e3o e corte de testemunhos na plataforma.',\r\n    'sm.items': 'Equipamentos dispon\u00edveis',\r\n\r\n    'notes.title': 'Observa\u00e7\u00f5es',\r\n    'notes.desc': 'Condi\u00e7\u00f5es da obra, prazos, requisitos especiais ou qualquer detalhe que devamos considerar.',\r\n    'notes.placeholder': 'Ex.: obra a 4.200 m de altitude, sem cobertura celular, entrega necess\u00e1ria em mar\u00e7o.',\r\n\r\n    'cart.title': 'Sua configura\u00e7\u00e3o',\r\n    'cart.empty': 'Ainda n\u00e3o h\u00e1 sele\u00e7\u00f5es',\r\n    'cart.emptyHint': 'Cada item escolhido \u00e9 salvo aqui automaticamente.',\r\n    'cart.operation': 'Opera\u00e7\u00e3o',\r\n    'cart.rig': 'Perfuratriz',\r\n    'cart.rods': 'Hastes',\r\n    'cart.rodsInfo': 'Refer\u00eancia t\u00e9cnica \u00b7 n\u00e3o \u00e9 cotado',\r\n    'quote.rodsRef': 'Hastes compat\u00edveis',\r\n    'cart.infoTag': 'Info',\r\n    'cart.handling': 'Manipula\u00e7\u00e3o de hastes',\r\n    'cart.subhandling': 'Manipula\u00e7\u00e3o subterr\u00e2nea',\r\n    'cart.telemetry': 'Telemetria',\r\n    'cart.sampling': 'Extra\u00e7\u00e3o de amostras',\r\n    'cart.accessories': 'Acess\u00f3rios',\r\n    'cart.notes': 'Observa\u00e7\u00f5es',\r\n    'cart.request': 'Solicitar Cota\u00e7\u00e3o',\r\n    'cart.reset': 'Limpar configura\u00e7\u00e3o',\r\n    'cart.resetConfirm': 'Limpar toda a configura\u00e7\u00e3o? Todas as sele\u00e7\u00f5es ser\u00e3o perdidas.',\r\n    'cart.total': 'Total estimado',\r\n    'cart.priceOnRequest': 'Sob cota\u00e7\u00e3o',\r\n    'cart.lines': 'itens',\r\n\r\n    'quote.eyebrow': 'Etapa 5 de 5',\r\n    'quote.title': 'Resumo da sua solicita\u00e7\u00e3o',\r\n    'quote.desc': 'Revise a configura\u00e7\u00e3o antes de enviar. Nossa equipe responde em at\u00e9 48 horas \u00fateis.',\r\n    'quote.edit': 'Editar Configura\u00e7\u00e3o',\r\n    'quote.send': 'Enviar Solicita\u00e7\u00e3o',\r\n    'quote.contact': 'Dados de contato',\r\n    'quote.name': 'Nome completo',\r\n    'quote.company': 'Empresa ou obra',\r\n    'quote.email': 'E-mail',\r\n    'quote.phone': 'Telefone',\r\n    'quote.country': 'Pa\u00eds',\r\n    'quote.channel': 'Canal de resposta',\r\n    'quote.sending': 'Enviando\u2026',\r\n    'quote.doneTitle': 'Solicita\u00e7\u00e3o enviada',\r\n    'quote.doneBody': 'Recebemos sua configura\u00e7\u00e3o. Um especialista IDR entrar\u00e1 em contato em at\u00e9 48 horas \u00fateis.',\r\n    'quote.ref': 'Protocolo',\r\n    'quote.needContact': 'Preencha nome e e-mail para enviar a solicita\u00e7\u00e3o.',\r\n    'quote.needConfig': 'Adicione pelo menos um equipamento antes de solicitar a cota\u00e7\u00e3o.',\r\n\r\n    'toast.removed': 'Item removido da configura\u00e7\u00e3o',\r\n    'toast.reset': 'Configura\u00e7\u00e3o limpa',\r\n    'toast.saved': 'Configura\u00e7\u00e3o salva'\r\n  }\r\n};\r\n<\/script>\r\n\r\n<!-- ============================================================ -->\r\n<!-- ==== FILE: data.js ========================================= -->\r\n<!-- Cat\u00e1logo y configuraci\u00f3n. Todo el negocio vive aqu\u00ed:          -->\r\n<!-- agregar o quitar modelos, barras, equipos y accesorios NO     -->\r\n<!-- requiere tocar la l\u00f3gica de la aplicaci\u00f3n.                    -->\r\n<!--                                                               -->\r\n<!-- Convenciones:                                                 -->\r\n<!--   name  : string (nombre propio) u objeto {es,en,pt}          -->\r\n<!--   descK : clave del diccionario de traducciones               -->\r\n<!--   price : null = \"a cotizar\". Al cargar precios y poner       -->\r\n<!--           IDR_CONFIG.pricing.enabled = true el carrito        -->\r\n<!--           calcula y muestra totales autom\u00e1ticamente.          -->\r\n<!-- ============================================================ -->\r\n<script>\r\n\/* ---------------------------------------------------------------- *\/\r\n\/* Configuraci\u00f3n general                                             *\/\r\n\/* ---------------------------------------------------------------- *\/\r\nconst IDR_CONFIG = {\r\n  defaultLang: 'es',\r\n\r\n  \/* Motor de precios (desactivado hasta cargar tarifas) *\/\r\n  pricing: {\r\n    enabled: false,        \/\/ true -> el carrito muestra subtotales y total\r\n    currency: 'USD',\r\n    locale: 'es-CL'\r\n  },\r\n\r\n  \/* Persistencia de sesi\u00f3n: si el navegador la bloquea se degrada a memoria *\/\r\n  persistSession: true,\r\n  storageKey: 'innra_idr_configurator_v1',\r\n\r\n  \/* Integraciones de salida. Se activan una a una sin tocar la UI. *\/\r\n  integrations: {\r\n    email:    { enabled: false, endpoint: '', to: 'ventas@innra.cl' },\r\n    whatsapp: { enabled: false, phone: '56900000000' },   \/\/ formato internacional sin \"+\"\r\n    crm:      { enabled: false, endpoint: '', apiKey: '' }\r\n  },\r\n\r\n  \/* L\u00edmites de los contadores de cantidad *\/\r\n  qty: { min: 1, max: 99 }\r\n};\r\n\r\n\/* ---------------------------------------------------------------- *\/\r\n\/* Cat\u00e1logo                                                          *\/\r\n\/* ---------------------------------------------------------------- *\/\r\nconst IDR_DATA = {\r\n\r\n  \/* -- Paso 1: tipo de operaci\u00f3n ---------------------------------- *\/\r\n  operations: [\r\n    { id: 'surface',     nameK: 'op.surface',     descK: 'op.surfaceDesc' },\r\n    { id: 'underground', nameK: 'op.underground', descK: 'op.undergroundDesc' }\r\n  ],\r\n\r\n  \/* -- Paso 2: modelos de perforadora por operaci\u00f3n ---------------- *\/\r\n  rigs: {\r\n    surface: [\r\n      { id: 'sandvik-de710', name: 'Sandvik DE710' },\r\n      { id: 'sandvik-712',   name: 'Sandvik 712' },\r\n      { id: 'epiroc-c8',     name: 'Epiroc C8' },\r\n      { id: 'epiroc-ct20',   name: 'Epiroc CT20' },\r\n      { id: 'bl-lf90',       name: 'Boart Longyear LF90' },\r\n      { id: 'bl-lx6',        name: 'Boart Longyear LX6' },\r\n      { id: 'sondeq-ssh10',  name: 'SONDEQ SSH10' },\r\n      { id: 'other',         nameK: 'common.other', custom: true }\r\n    ],\r\n    underground: [\r\n      { id: 'diamec-252',      name: 'Diamec 252' },\r\n      { id: 'diamec-u4-smart', name: 'Diamec U4 Smart' },\r\n      { id: 'diamec-u6-smart', name: 'Diamec U6 Smart' },\r\n      { id: 'other',           nameK: 'common.other', custom: true }\r\n    ]\r\n  },\r\n\r\n  \/* -- Paso 3: di\u00e1metros de barra por operaci\u00f3n --------------------\r\n     S\u00d3LO INFORMATIVO. No es una elecci\u00f3n del usuario ni un \u00edtem\r\n     cotizable: se muestra como ficha t\u00e9cnica para recordar con qu\u00e9\r\n     barras trabajan los equipos IDR de cada operaci\u00f3n. *\/\r\n  rods: {\r\n    surface: [\r\n      { id: 'nq',  name: 'NQ' },\r\n      { id: 'hq',  name: 'HQ' },\r\n      { id: 'pq',  name: 'PQ' },\r\n      { id: 'pw',  name: 'Casing PW' },\r\n      { id: 'spt', name: 'SPT' }\r\n    ],\r\n    underground: [\r\n      { id: 'bq', name: 'BQ' },\r\n      { id: 'nq', name: 'NQ' },\r\n      { id: 'hq', name: 'HQ' },\r\n      { id: 'pq', name: 'PQ' }\r\n    ]\r\n  },\r\n\r\n  \/* -- Paso 4: m\u00f3dulos disponibles por operaci\u00f3n ------------------- *\/\r\n  \/* El orden de este arreglo es el orden de las tarjetas en pantalla *\/\r\n  modules: {\r\n    surface:     ['handling', 'telemetry', 'sampling'],\r\n    underground: ['subhandling', 'telemetry']\r\n  },\r\n\r\n  \/* Metadatos de cada m\u00f3dulo (tarjeta + cabecera de su vista) *\/\r\n  moduleMeta: {\r\n    handling:    { code: 'IDR-ET',  art: 'arm',       titleK: 'hd.eyebrow',  descK: 'hd.desc',   cartK: 'cart.handling' },\r\n    subhandling: { code: 'ET SUB',  art: 'arm',       titleK: 'sub.title',   descK: 'sub.desc',  cartK: 'cart.subhandling' },\r\n    telemetry:   { code: 'IDR-TEL', art: 'telemetry', titleK: 'tel.eyebrow', descK: 'tel.desc',  cartK: 'cart.telemetry' },\r\n    sampling:    { code: 'IDR-SM',  art: 'core',      titleK: 'sm.eyebrow',  descK: 'sm.desc',   cartK: 'cart.sampling' }\r\n  },\r\n\r\n  \/* -- Sistema de Manipulaci\u00f3n de Barras (superficie) -------------- *\/\r\n  handling: {\r\n    main: { id: 'idr-et', name: 'IDR-ET', descK: 'hd.etDesc', price: null },\r\n\r\n    \/* Tipo de almacenamiento (selecci\u00f3n \u00fanica).\r\n       allowAddons: la cama admite Ampliroll y Transportador. *\/\r\n    storage: [\r\n      { id: 'bed-350ju',  name: 'Cama 350JU', price: null },\r\n      { id: 'bed-350h',   name: 'Cama 350H',  price: null, allowAddons: true },\r\n      { id: 'bed-manual', name: { es: 'Cama Manual', en: 'Manual Bed', pt: 'Cama Manual' }, price: null, manual: true }\r\n    ],\r\n\r\n    \/* Complementos de la cama.\r\n       REGLA: el Ampliroll puede ir solo; el Transportador NO puede\r\n       cotizarse sin Ampliroll (requires). Marcar Transportador activa\r\n       el Ampliroll y desmarcar Ampliroll arrastra al Transportador. *\/\r\n    ampliroll: { id: 'ampliroll', name: 'Ampliroll', price: null },\r\n    conveyor:  {\r\n      id: 'conveyor',\r\n      name: { es: 'Transportador', en: 'Conveyor', pt: 'Transportador' },\r\n      requires: 'ampliroll',\r\n      price: null\r\n    },\r\n\r\n    \/* Sistema de Corte.\r\n       Cadena de dependencia declarada en `requires`:\r\n         IDR-LP  (base, puede pedirse solo)\r\n           -> IDR-BOU  (necesita IDR-LP)\r\n              -> IDR-CEN (necesita IDR-BOU)\r\n       Quien no tenga BOU no puede pedir CEN: esos casos los eval\u00faa\r\n       ingenier\u00eda. Para cambiar la cadena basta con editar `requires`. *\/\r\n    cutting: [\r\n      {\r\n        id: 'idr-lp', code: 'IDR-LP', requires: null, price: null,\r\n        name: { es: 'Unidad de Corte', en: 'Cutting Unit', pt: 'Unidade de Corte' }\r\n      },\r\n      {\r\n        id: 'idr-bou', code: 'IDR-BOU', requires: 'idr-lp', price: null,\r\n        name: { es: 'Conjunto de Prensas', en: 'Clamp Assembly', pt: 'Conjunto de Prensas' }\r\n      },\r\n      {\r\n        id: 'idr-cen', code: 'IDR-CEN', requires: 'idr-bou', price: null,\r\n        name: { es: 'Centralizador', en: 'Centralizer', pt: 'Centralizador' }\r\n      }\r\n    ]\r\n  },\r\n\r\n  \/* -- Manejo de muestras ------------------------------------------ *\/\r\n  \/* NOTA: esta misma lista alimenta el bloque \"Manejo de Muestras\"   *\/\r\n  \/* del sistema de manipulaci\u00f3n y la vista \"Sistema de              *\/\r\n  \/* Extracci\u00f3n de Muestras\". Comparten estado (state.sampling) para  *\/\r\n  \/* evitar duplicar cantidades en el carrito. Si en el futuro deben  *\/\r\n  \/* cotizarse por separado, basta con crear un segundo arreglo y un  *\/\r\n  \/* segundo store en createState().                                  *\/\r\n  sampling: {\r\n    items: [\r\n      { id: 'pushcore-auto', name: { es: 'PushCore Aut\u00f3nomo', en: 'Autonomous PushCore', pt: 'PushCore Aut\u00f4nomo' }, price: null },\r\n      { id: 'rail-pushcore', name: 'Rail PushCore', price: null },\r\n      { id: 'atril-corte',   name: { es: 'Atril de Corte', en: 'Cutting Stand', pt: 'Cavalete de Corte' }, price: null }\r\n      \/* La Unidad de Corte se traslad\u00f3 al Sistema de Corte (IDR-LP). *\/\r\n    ]\r\n  },\r\n\r\n  \/* -- Manipulaci\u00f3n subterr\u00e1nea ------------------------------------ *\/\r\n  subhandling: {\r\n    main: {\r\n      id: 'et-sub',\r\n      name: { es: 'Brazo Manipulador ET SUB', en: 'ET SUB Handling Arm', pt: 'Bra\u00e7o Manipulador ET SUB' },\r\n      descK: 'sub.armDesc',\r\n      price: null\r\n    }\r\n  },\r\n\r\n  \/* -- Telemetr\u00eda y telesupervisi\u00f3n -------------------------------- *\/\r\n  telemetry: {\r\n    \/* Qu\u00e9 desea cotizar *\/\r\n    targets: [\r\n      { id: 'probe',   nameK: 'tel.probe', descK: 'tel.probeDesc' },\r\n      { id: 'cs-cart', nameK: 'tel.cart',  descK: 'tel.cartDesc', operations: ['surface'] }\r\n    ],\r\n\r\n    \/* Qu\u00e9 desea monitorear (telemetr\u00eda para sonda) *\/\r\n    monitoring: [\r\n      { id: 'video',      nameK: 'tel.video',     descK: 'tel.videoDesc',     price: null },\r\n      { id: 'data',       nameK: 'tel.data',      descK: 'tel.dataDesc',      price: null },\r\n      { id: 'data-video', nameK: 'tel.dataVideo', descK: 'tel.dataVideoDesc', price: null }\r\n    ],\r\n\r\n    \/* Variables incluidas en el paquete IDR-TEL *\/\r\n    variables: [\r\n      { id: 'mud-flow',     name: { es: 'Caudal de lodos',    en: 'Mud flow',        pt: 'Vaz\u00e3o de lama' } },\r\n      { id: 'mud-pressure', name: { es: 'Presi\u00f3n de lodos',   en: 'Mud pressure',    pt: 'Press\u00e3o de lama' } },\r\n      { id: 'torque',       name: { es: 'Presi\u00f3n de torque',  en: 'Torque pressure', pt: 'Press\u00e3o de torque' } },\r\n      { id: 'vibration',    name: { es: 'Vibraci\u00f3n',          en: 'Vibration',       pt: 'Vibra\u00e7\u00e3o' } },\r\n      { id: 'position',     name: { es: 'Posici\u00f3n',           en: 'Position',        pt: 'Posi\u00e7\u00e3o' } },\r\n      { id: 'engine',       name: { es: 'Monitoreo de motor', en: 'Engine monitoring', pt: 'Monitoramento de motor' } },\r\n      { id: 'video',        name: { es: 'Captura de video',   en: 'Video capture',   pt: 'Captura de v\u00eddeo' } }\r\n    ],\r\n\r\n    \/* Carro de telesupervisi\u00f3n IDR-CS *\/\r\n    cs: {\r\n      id: 'idr-cs',\r\n      name: 'IDR-CS',\r\n      modes: [\r\n        { id: 'purchase', nameK: 'cs.purchase', descK: 'cs.purchaseDesc', price: null },\r\n        { id: 'rental',   nameK: 'cs.rental',   descK: 'cs.rentalDesc',   price: null }\r\n      ],\r\n      accessories: [\r\n        {\r\n          id: 'comm-hub',\r\n          name: { es: 'HUB de Comunicaciones', en: 'Communications HUB', pt: 'HUB de Comunica\u00e7\u00f5es' },\r\n          desc: {\r\n            es: 'Enlace de datos para la sonda. Recomendado en faenas remotas sin acceso a Internet.',\r\n            en: 'Data link for the rig. Recommended on remote sites without internet access.',\r\n            pt: 'Enlace de dados para a sonda. Recomendado em obras remotas sem acesso \u00e0 internet.'\r\n          },\r\n          price: null\r\n        }\r\n      ]\r\n    }\r\n  },\r\n\r\n  \/* -- Pasos del registro de sondaje (indicador de progreso) -------- *\/\r\n  steps: [\r\n    { id: 'operation', labelK: 'steps.operation', tone: 1 },\r\n    { id: 'rig',       labelK: 'steps.rig',       tone: 2 },\r\n    \/* optional: informativo, no bloquea el avance de los pasos siguientes *\/\r\n    { id: 'rods',      labelK: 'steps.rods',      tone: 3, optional: true },\r\n    { id: 'equipment', labelK: 'steps.equipment', tone: 4 },\r\n    { id: 'quote',     labelK: 'steps.quote',     tone: 5 }\r\n  ]\r\n};\r\n<\/script>\r\n\r\n<!-- ============================================================ -->\r\n<!-- ==== FILE: components.js =================================== -->\r\n<!-- Componentes de presentaci\u00f3n puros: reciben datos y devuelven  -->\r\n<!-- HTML. No leen ni escriben estado.                             -->\r\n<!-- ============================================================ -->\r\n<script>\r\nconst IDR_C = (function () {\r\n  'use strict';\r\n\r\n  \/* Acceso perezoso al traductor (app.js se carga despu\u00e9s). *\/\r\n  const t = (k) => (window.IDR ? window.IDR.t(k) : k);\r\n\r\n  \/* -------------------------------------------------------------- *\/\r\n  \/* Utilidades                                                      *\/\r\n  \/* -------------------------------------------------------------- *\/\r\n\r\n  \/** Escapa texto para inyecci\u00f3n segura en HTML. *\/\r\n  function esc(value) {\r\n    return String(value == null ? '' : value)\r\n      .replace(\/&\/g, '&amp;').replace(\/<\/g, '&lt;').replace(\/>\/g, '&gt;')\r\n      .replace(\/\"\/g, '&quot;').replace(\/'\/g, '&#39;');\r\n  }\r\n\r\n  \/** Une clases descartando valores vac\u00edos. *\/\r\n  function cx() {\r\n    return Array.prototype.filter.call(arguments, Boolean).join(' ');\r\n  }\r\n\r\n  \/** Serializa un objeto de atributos data-*. *\/\r\n  function dataAttrs(map) {\r\n    if (!map) return '';\r\n    return Object.keys(map)\r\n      .filter((k) => map[k] !== undefined && map[k] !== null)\r\n      .map((k) => ` data-${k}=\"${esc(map[k])}\"`)\r\n      .join('');\r\n  }\r\n\r\n  \/* -------------------------------------------------------------- *\/\r\n  \/* Iconograf\u00eda (trazo 1.7, 24x24, sin dependencias)                *\/\r\n  \/* -------------------------------------------------------------- *\/\r\n  const PATHS = {\r\n    check:    '<path d=\"M20 6 9 17l-5-5\"\/>',\r\n    arrowR:   '<path d=\"M5 12h14M13 6l6 6-6 6\"\/>',\r\n    arrowL:   '<path d=\"M19 12H5M11 18l-6-6 6-6\"\/>',\r\n    plus:     '<path d=\"M12 5v14M5 12h14\"\/>',\r\n    minus:    '<path d=\"M5 12h14\"\/>',\r\n    cart:     '<circle cx=\"9\" cy=\"20\" r=\"1.4\"\/><circle cx=\"18\" cy=\"20\" r=\"1.4\"\/><path d=\"M2 3h2.2l2.5 12.4a2 2 0 0 0 2 1.6h8.6a2 2 0 0 0 2-1.6L21 7H5.4\"\/>',\r\n    chevron:  '<path d=\"m6 9 6 6 6-6\"\/>',\r\n    x:        '<path d=\"M18 6 6 18M6 6l12 12\"\/>',\r\n    pencil:   '<path d=\"M12 20h9\"\/><path d=\"M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4Z\"\/>',\r\n    trash:    '<path d=\"M3 6h18M8 6V4h8v2M19 6l-1 14H6L5 6\"\/>',\r\n    alert:    '<path d=\"M12 9v4M12 17h.01\"\/><path d=\"M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0Z\"\/>',\r\n    send:     '<path d=\"M22 2 11 13M22 2l-7 20-4-9-9-4Z\"\/>',\r\n    box:      '<path d=\"M21 8v8a2 2 0 0 1-1 1.7l-7 4a2 2 0 0 1-2 0l-7-4A2 2 0 0 1 3 16V8a2 2 0 0 1 1-1.7l7-4a2 2 0 0 1 2 0l7 4A2 2 0 0 1 21 8Z\"\/><path d=\"m3.3 7 8.7 5 8.7-5M12 22V12\"\/>',\r\n    reset:    '<path d=\"M3 12a9 9 0 1 0 3-6.7L3 8\"\/><path d=\"M3 3v5h5\"\/>'\r\n  };\r\n\r\n  \/** Devuelve un <svg> inline por nombre. *\/\r\n  function icon(name, cls) {\r\n    return '<svg class=\"' + esc(cls || '') + '\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" ' +\r\n           'stroke-width=\"1.7\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">' +\r\n           (PATHS[name] || '') + '<\/svg>';\r\n  }\r\n\r\n  \/* -------------------------------------------------------------- *\/\r\n  \/* Ilustraci\u00f3n de cabecera de las tarjetas de m\u00f3dulo               *\/\r\n  \/* Trazado t\u00e9cnico sobre grafito: brazo, antena y testigo.         *\/\r\n  \/* -------------------------------------------------------------- *\/\r\n  const ART = {\r\n    arm: '<svg viewBox=\"0 0 300 132\" preserveAspectRatio=\"xMidYMid slice\">' +\r\n      '<g stroke=\"#1d3348\" stroke-width=\"1\"><path d=\"M0 104h300M0 74h300M0 44h300\"\/><\/g>' +\r\n      '<g stroke=\"#7a8691\" stroke-width=\"2\" fill=\"none\" stroke-linecap=\"round\">' +\r\n      '<path d=\"M52 112V64\"\/><path d=\"M52 64l58-26\"\/><path d=\"M110 38l52 22\"\/>' +\r\n      '<path d=\"M162 60v30\"\/><\/g>' +\r\n      '<g fill=\"#6f9fd4\"><circle cx=\"52\" cy=\"64\" r=\"5\"\/><circle cx=\"110\" cy=\"38\" r=\"5\"\/><circle cx=\"162\" cy=\"60\" r=\"5\"\/><\/g>' +\r\n      '<g stroke=\"#6f9fd4\" stroke-width=\"2\"><path d=\"M150 90h24\"\/><\/g>' +\r\n      '<g stroke=\"#123F6D\" stroke-width=\"7\" stroke-linecap=\"round\">' +\r\n      '<path d=\"M212 108V52\"\/><path d=\"M232 108V44\"\/><path d=\"M252 108V60\"\/><path d=\"M272 108V38\"\/><\/g>' +\r\n      '<\/svg>',\r\n    telemetry: '<svg viewBox=\"0 0 300 132\" preserveAspectRatio=\"xMidYMid slice\">' +\r\n      '<g stroke=\"#1d3348\" stroke-width=\"1\"><path d=\"M0 104h300M0 74h300M0 44h300\"\/><\/g>' +\r\n      '<g stroke=\"#7a8691\" stroke-width=\"2\" fill=\"none\"><path d=\"M70 108V46\"\/>' +\r\n      '<path d=\"M56 46h28l-14-18Z\"\/><\/g>' +\r\n      '<g stroke=\"#6f9fd4\" stroke-width=\"1.8\" fill=\"none\" opacity=\".9\">' +\r\n      '<path d=\"M92 34a34 34 0 0 1 0 40\"\/><path d=\"M104 24a52 52 0 0 1 0 60\"\/>' +\r\n      '<path d=\"M116 14a70 70 0 0 1 0 80\"\/><\/g>' +\r\n      '<g stroke=\"#7a8691\" stroke-width=\"1.6\" fill=\"none\">' +\r\n      '<path d=\"M180 92l16-22 14 14 18-34 16 24 14-18\"\/><\/g>' +\r\n      '<g fill=\"#6f9fd4\"><circle cx=\"196\" cy=\"70\" r=\"3\"\/><circle cx=\"228\" cy=\"50\" r=\"3\"\/><circle cx=\"258\" cy=\"56\" r=\"3\"\/><\/g>' +\r\n      '<\/svg>',\r\n    core: '<svg viewBox=\"0 0 300 132\" preserveAspectRatio=\"xMidYMid slice\">' +\r\n      '<g stroke=\"#1d3348\" stroke-width=\"1\"><path d=\"M0 104h300M0 74h300M0 44h300\"\/><\/g>' +\r\n      '<g><rect x=\"44\" y=\"24\" width=\"212\" height=\"26\" rx=\"4\" fill=\"#123F6D\"\/>' +\r\n      '<rect x=\"44\" y=\"56\" width=\"212\" height=\"26\" rx=\"4\" fill=\"#15559c\"\/>' +\r\n      '<rect x=\"44\" y=\"88\" width=\"212\" height=\"26\" rx=\"4\" fill=\"#4a7bb0\"\/><\/g>' +\r\n      '<g stroke=\"#0f1c2a\" stroke-width=\"1.4\" opacity=\".55\">' +\r\n      '<path d=\"M92 24v26M140 24v26M188 24v26M228 24v26\"\/>' +\r\n      '<path d=\"M78 56v26M126 56v26M174 56v26M222 56v26\"\/>' +\r\n      '<path d=\"M104 88v26M152 88v26M200 88v26\"\/><\/g>' +\r\n      '<g stroke=\"#6f9fd4\" stroke-width=\"2\"><path d=\"M44 18h212\"\/><path d=\"M44 14v8M256 14v8\"\/><\/g>' +\r\n      '<\/svg>'\r\n  };\r\n\r\n  function moduleArt(name) { return ART[name] || ART.arm; }\r\n\r\n  \/* -------------------------------------------------------------- *\/\r\n  \/* Cabecera de panel                                               *\/\r\n  \/* -------------------------------------------------------------- *\/\r\n  function panelHead(eyebrow, title, desc) {\r\n    return '<header class=\"idr-panel__head\">' +\r\n      (eyebrow ? '<span class=\"idr-eyebrow\">' + esc(eyebrow) + '<\/span>' : '') +\r\n      '<h2 class=\"idr-h2\">' + esc(title) + '<\/h2>' +\r\n      (desc ? '<p class=\"idr-lead\">' + esc(desc) + '<\/p>' : '') +\r\n      '<\/header>';\r\n  }\r\n\r\n  \/* Cabecera de bloque interno *\/\r\n  function blockHead(title, desc) {\r\n    return '<div class=\"idr-block__head\"><div>' +\r\n      '<h3 class=\"idr-block__title\">' + esc(title) + '<\/h3>' +\r\n      (desc ? '<p class=\"idr-sub\">' + esc(desc) + '<\/p>' : '') +\r\n      '<\/div><\/div>';\r\n  }\r\n\r\n  \/* -------------------------------------------------------------- *\/\r\n  \/* Opci\u00f3n seleccionable (radio o checkbox visual)                  *\/\r\n  \/* -------------------------------------------------------------- *\/\r\n  function option(o) {\r\n    const isCheck = o.type === 'check';\r\n    return '<button type=\"button\" role=\"' + (isCheck ? 'checkbox' : 'radio') + '\" ' +\r\n      'aria-checked=\"' + (o.on ? 'true' : 'false') + '\" ' +\r\n      'class=\"' + cx('idr-opt', isCheck && 'idr-opt--check', o.on && 'is-on') + '\"' +\r\n      dataAttrs(o.data) + '>' +\r\n      '<span class=\"idr-opt__title\">' + esc(o.title) + '<\/span>' +\r\n      (o.desc ? '<span class=\"idr-opt__desc\">' + esc(o.desc) + '<\/span>' : '') +\r\n      '<span class=\"idr-opt__tick\">' + icon('check') + '<\/span>' +\r\n      '<\/button>';\r\n  }\r\n\r\n  \/* -------------------------------------------------------------- *\/\r\n  \/* Chip (di\u00e1metros de barra, multiselecci\u00f3n)                       *\/\r\n  \/* -------------------------------------------------------------- *\/\r\n  function chip(o) {\r\n    return '<button type=\"button\" role=\"checkbox\" aria-checked=\"' + (o.on ? 'true' : 'false') + '\" ' +\r\n      'class=\"' + cx('idr-chip', o.on && 'is-on') + '\"' + dataAttrs(o.data) + '>' +\r\n      '<span class=\"idr-chip__dot\"><\/span>' + esc(o.label) + '<\/button>';\r\n  }\r\n\r\n  \/* -------------------------------------------------------------- *\/\r\n  \/* Contador de cantidad                                            *\/\r\n  \/* -------------------------------------------------------------- *\/\r\n  function qty(o) {\r\n    const min = o.min != null ? o.min : IDR_CONFIG.qty.min;\r\n    const max = o.max != null ? o.max : IDR_CONFIG.qty.max;\r\n    const base = { 'idr-qty-target': o.target, 'idr-qty-key': o.key };\r\n    return '<div class=\"' + cx('idr-qty', o.size === 'sm' && 'idr-qty--sm') + '\">' +\r\n      '<button type=\"button\" class=\"idr-qty__btn\" aria-label=\"-\" ' +\r\n        'data-idr-action=\"qty-dec\"' + dataAttrs(base) + (o.value <= min ? ' disabled' : '') + '>' +\r\n        icon('minus') + '<\/button>' +\r\n      '<input class=\"idr-qty__val\" type=\"number\" inputmode=\"numeric\" ' +\r\n        'value=\"' + esc(o.value) + '\" min=\"' + min + '\" max=\"' + max + '\" ' +\r\n        'aria-label=\"' + esc(t('common.qty')) + '\" ' +\r\n        'data-idr-input=\"qty\"' + dataAttrs(base) + '>' +\r\n      '<button type=\"button\" class=\"idr-qty__btn\" aria-label=\"+\" ' +\r\n        'data-idr-action=\"qty-inc\"' + dataAttrs(base) + (o.value >= max ? ' disabled' : '') + '>' +\r\n        icon('plus') + '<\/button>' +\r\n      '<\/div>';\r\n  }\r\n\r\n  \/* -------------------------------------------------------------- *\/\r\n  \/* Fila seleccionable con cantidad                                 *\/\r\n  \/* -------------------------------------------------------------- *\/\r\n  function row(o) {\r\n    return '<div class=\"' + cx('idr-row', o.on && 'is-on', o.locked && 'is-locked') + '\">' +\r\n      '<button type=\"button\" class=\"idr-row__main\" role=\"checkbox\" ' +\r\n        'aria-checked=\"' + (o.on ? 'true' : 'false') + '\"' +\r\n        (o.locked ? ' disabled aria-disabled=\"true\"' : '') +\r\n        dataAttrs(o.data) + '>' +\r\n        '<span class=\"idr-row__box\">' + icon('check') + '<\/span>' +\r\n        '<span><span class=\"idr-row__name\">' + esc(o.name) +\r\n          (o.code ? '<span class=\"idr-row__code\">' + esc(o.code) + '<\/span>' : '') +\r\n          (o.requires ? '<span class=\"idr-row__req\">' + esc(o.requires) + '<\/span>' : '') +\r\n        '<\/span>' +\r\n        (o.desc ? '<span class=\"idr-row__desc\">' + esc(o.desc) + '<\/span>' : '') + '<\/span>' +\r\n      '<\/button>' +\r\n      (o.on && o.qty !== false\r\n        ? qty({ value: o.qtyValue, target: o.qtyTarget, key: o.qtyKey, size: 'sm' })\r\n        : '') +\r\n      '<\/div>';\r\n  }\r\n\r\n  \/* -------------------------------------------------------------- *\/\r\n  \/* Interruptor segmentado S\u00ed \/ No                                  *\/\r\n  \/* -------------------------------------------------------------- *\/\r\n  function segYesNo(o) {\r\n    return '<div class=\"idr-seg\" role=\"radiogroup\">' +\r\n      '<button type=\"button\" role=\"radio\" aria-checked=\"' + (o.value ? 'true' : 'false') + '\" ' +\r\n        'class=\"' + cx('idr-seg__btn', o.value && 'is-on') + '\"' +\r\n        dataAttrs(Object.assign({}, o.data, { 'idr-value': '1' })) + '>' + esc(t('common.yes')) + '<\/button>' +\r\n      '<button type=\"button\" role=\"radio\" aria-checked=\"' + (!o.value ? 'true' : 'false') + '\" ' +\r\n        'class=\"' + cx('idr-seg__btn', !o.value && 'is-on') + '\"' +\r\n        dataAttrs(Object.assign({}, o.data, { 'idr-value': '0' })) + '>' + esc(t('common.no')) + '<\/button>' +\r\n      '<\/div>';\r\n  }\r\n\r\n  \/* -------------------------------------------------------------- *\/\r\n  \/* Avisos                                                          *\/\r\n  \/* -------------------------------------------------------------- *\/\r\n  function note(text) {\r\n    return '<div class=\"idr-note\">' + icon('alert') + '<span>' + esc(text) + '<\/span><\/div>';\r\n  }\r\n  function info(html, listItems, listTitle) {\r\n    return '<div class=\"idr-info\"><p>' + html + '<\/p>' +\r\n      (listItems && listItems.length\r\n        ? '<p class=\"idr-eyebrow\" style=\"margin-top:16px;display:block\">' + esc(listTitle || '') + '<\/p>' +\r\n          '<ul class=\"idr-info__list\">' + listItems.map((i) => '<li>' + esc(i) + '<\/li>').join('') + '<\/ul>'\r\n        : '') +\r\n      '<\/div>';\r\n  }\r\n\r\n  \/* -------------------------------------------------------------- *\/\r\n  \/* Campo de formulario                                             *\/\r\n  \/* -------------------------------------------------------------- *\/\r\n  function field(label, inputHtml, full) {\r\n    return '<label class=\"' + cx('idr-field', full && 'idr-field--full') + '\">' +\r\n      '<span class=\"idr-label\">' + esc(label) + '<\/span>' + inputHtml + '<\/label>';\r\n  }\r\n  function input(o) {\r\n    return '<input class=\"idr-input\" type=\"' + esc(o.type || 'text') + '\" ' +\r\n      'value=\"' + esc(o.value || '') + '\" placeholder=\"' + esc(o.placeholder || '') + '\" ' +\r\n      'data-idr-input=\"' + esc(o.name) + '\"' + (o.autofocus ? ' autofocus' : '') + '>';\r\n  }\r\n  function textarea(o) {\r\n    return '<textarea class=\"idr-textarea\" placeholder=\"' + esc(o.placeholder || '') + '\" ' +\r\n      'data-idr-input=\"' + esc(o.name) + '\">' + esc(o.value || '') + '<\/textarea>';\r\n  }\r\n\r\n  \/* -------------------------------------------------------------- *\/\r\n  \/* Barra de acciones inferior de cada vista                        *\/\r\n  \/* -------------------------------------------------------------- *\/\r\n  function actions(left, right) {\r\n    return '<div class=\"idr-actions\"><div>' + (left || '') + '<\/div>' +\r\n      '<div class=\"idr-actions__r\">' + (right || '') + '<\/div><\/div>';\r\n  }\r\n  function backLink(label, action) {\r\n    return '<button type=\"button\" class=\"idr-linkbtn\" data-idr-action=\"' + esc(action) + '\">' +\r\n      icon('arrowL') + esc(label) + '<\/button>';\r\n  }\r\n  function nextBtn(label, action, disabled) {\r\n    return '<button type=\"button\" class=\"idr-btn\" data-idr-action=\"' + esc(action) + '\"' +\r\n      (disabled ? ' disabled' : '') + '>' + esc(label) + icon('arrowR') + '<\/button>';\r\n  }\r\n\r\n  \/* -------------------------------------------------------------- *\/\r\n  \/* Tarjeta de m\u00f3dulo (paso 4)                                      *\/\r\n  \/* -------------------------------------------------------------- *\/\r\n  function moduleCard(o) {\r\n    return '<button type=\"button\" class=\"' + cx('idr-modcard', o.configured && 'is-configured') + '\" ' +\r\n      'data-idr-action=\"open-module\" data-idr-module=\"' + esc(o.id) + '\">' +\r\n      '<span class=\"idr-modcard__art\">' + moduleArt(o.art) +\r\n        '<span class=\"idr-modcard__code\">' + esc(o.code) + '<\/span><\/span>' +\r\n      '<span class=\"idr-modcard__body\">' +\r\n        '<span class=\"idr-modcard__title\">' + esc(o.title) + '<\/span>' +\r\n        '<span class=\"idr-modcard__desc\">' + esc(o.desc) + '<\/span>' +\r\n        '<span class=\"idr-modcard__foot\">' +\r\n          '<span class=\"' + cx('idr-tag', o.configured && 'idr-tag--ok') + '\">' +\r\n            esc(o.configured ? t('common.configured') : t('common.pending')) + '<\/span>' +\r\n          '<span class=\"idr-modcard__cta\">' +\r\n            esc(o.configured ? t('equip.review') : t('equip.open')) + icon('arrowR') + '<\/span>' +\r\n        '<\/span>' +\r\n      '<\/span><\/button>';\r\n  }\r\n\r\n  \/* -------------------------------------------------------------- *\/\r\n  \/* Modal gen\u00e9rico                                                  *\/\r\n  \/* -------------------------------------------------------------- *\/\r\n  function modal(o) {\r\n    return '<div class=\"idr-overlay\" data-idr-overlay=\"' + esc(o.id) + '\" role=\"dialog\" aria-modal=\"true\">' +\r\n      '<div class=\"' + cx('idr-modal', o.wide && 'idr-modal--wide') + '\" role=\"document\">' +\r\n        '<header class=\"idr-modal__head\">' +\r\n          (o.icon ? '<span class=\"idr-modal__icon\">' + icon(o.icon) + '<\/span>' : '') +\r\n          '<div style=\"flex:1\">' +\r\n            (o.eyebrow ? '<span class=\"idr-eyebrow\">' + esc(o.eyebrow) + '<\/span>' : '') +\r\n            '<h2 class=\"idr-h3\">' + esc(o.title) + '<\/h2>' +\r\n          '<\/div>' +\r\n          '<button type=\"button\" class=\"idr-modal__x\" data-idr-action=\"close-modal\" ' +\r\n            'aria-label=\"' + esc(t('common.close')) + '\">' + icon('x') + '<\/button>' +\r\n        '<\/header>' +\r\n        '<div class=\"idr-modal__body\">' + o.body + '<\/div>' +\r\n        (o.foot ? '<footer class=\"idr-modal__foot\">' + o.foot + '<\/footer>' : '') +\r\n      '<\/div><\/div>';\r\n  }\r\n\r\n  \/* -------------------------------------------------------------- *\/\r\n  return {\r\n    esc, cx, icon, moduleArt, panelHead, blockHead, option, chip, qty, row,\r\n    segYesNo, note, info, field, input, textarea, actions, backLink, nextBtn,\r\n    moduleCard, modal\r\n  };\r\n})();\r\n<\/script>\r\n\r\n<!-- ============================================================ -->\r\n<!-- ==== FILE: cart.js ========================================= -->\r\n<!-- El carrito es una PROYECCI\u00d3N del estado, no una lista aparte:  -->\r\n<!-- cada selecci\u00f3n aparece autom\u00e1ticamente y cada l\u00ednea sabe a qu\u00e9 -->\r\n<!-- vista lleva \"Editar\" y qu\u00e9 llave borra \"Eliminar\".             -->\r\n<!-- ============================================================ -->\r\n<script>\r\nconst IDR_CART = (function () {\r\n  'use strict';\r\n\r\n  const C = IDR_C;\r\n  const t = (k) => window.IDR.t(k);\r\n  const L = (v) => window.IDR.L(v);\r\n\r\n  \/* -------------------------------------------------------------- *\/\r\n  \/* Construcci\u00f3n de l\u00edneas                                          *\/\r\n  \/* line = { group, name, meta, qty, price, edit, key }             *\/\r\n  \/*   group : clave de traducci\u00f3n del encabezado                    *\/\r\n  \/*   edit  : destino de navegaci\u00f3n al pulsar \"Editar\"              *\/\r\n  \/*   key   : identificador que consume removeLine() en app.js      *\/\r\n  \/* -------------------------------------------------------------- *\/\r\n  function lines(s) {\r\n    const out = [];\r\n    const op = s.operation;\r\n    if (!op) return out;\r\n\r\n    const push = (o) => out.push(o);\r\n    const find = (arr, id) => arr.find((x) => x.id === id);\r\n\r\n    \/* 1. Operaci\u00f3n ------------------------------------------------- *\/\r\n    const opDef = find(IDR_DATA.operations, op);\r\n    push({\r\n      group: 'cart.operation',\r\n      name: t(opDef.nameK),\r\n      edit: { view: 'operation' },\r\n      key: 'operation'\r\n    });\r\n\r\n    \/* 2. Perforadora ----------------------------------------------- *\/\r\n    if (s.rig.model) {\r\n      const rigDef = find(IDR_DATA.rigs[op], s.rig.model);\r\n      const isCustom = !!(rigDef && rigDef.custom);\r\n      const label = isCustom\r\n        ? (s.rig.customModel.trim() || t('common.other'))\r\n        : L(rigDef.name);\r\n      push({\r\n        group: 'cart.rig',\r\n        name: label,\r\n        meta: isCustom ? t('rig.feasibility') : null,\r\n        qty: s.rig.quantity,\r\n        edit: { view: 'rig' },\r\n        key: 'rig'\r\n      });\r\n    }\r\n\r\n    \/* 3. Barras -----------------------------------------------------\r\n       NO figuran en el carrito: son ficha t\u00e9cnica, no una elecci\u00f3n.\r\n       Se adjuntan al payload dentro de `information.compatibleRods`. *\/\r\n\r\n    \/* 4. Manipulaci\u00f3n de barras (superficie) ------------------------ *\/\r\n    if (op === 'surface') {\r\n      const h = s.handling;\r\n      if (h.et) {\r\n        push({\r\n          group: 'cart.handling',\r\n          name: IDR_DATA.handling.main.name,\r\n          qty: h.etQty,\r\n          price: IDR_DATA.handling.main.price,\r\n          edit: { module: 'handling' },\r\n          key: 'handling.et'\r\n        });\r\n      }\r\n      if (h.storage) {\r\n        const st = find(IDR_DATA.handling.storage, h.storage);\r\n        push({\r\n          group: 'cart.handling',\r\n          name: L(st.name),\r\n          qty: h.storageQty,\r\n          price: st.price,\r\n          edit: { module: 'handling' },\r\n          key: 'handling.storage'\r\n        });\r\n        if (st.allowAddons && h.ampliroll) {\r\n          push({\r\n            group: 'cart.handling',\r\n            name: L(IDR_DATA.handling.ampliroll.name),\r\n            qty: h.amplirollQty,\r\n            price: IDR_DATA.handling.ampliroll.price,\r\n            edit: { module: 'handling' },\r\n            key: 'handling.ampliroll'\r\n          });\r\n        }\r\n        if (st.allowAddons && h.conveyor) {\r\n          push({\r\n            group: 'cart.handling',\r\n            name: L(IDR_DATA.handling.conveyor.name),\r\n            qty: h.conveyorQty,\r\n            price: IDR_DATA.handling.conveyor.price,\r\n            edit: { module: 'handling' },\r\n            key: 'handling.conveyor'\r\n          });\r\n        }\r\n      }\r\n      \/* Sistema de Corte: se listan en el orden del cat\u00e1logo, que es\r\n         tambi\u00e9n el orden de la cadena de dependencias. *\/\r\n      IDR_DATA.handling.cutting.forEach((item) => {\r\n        const q = h.cutting[item.id];\r\n        if (!q) return;\r\n        push({\r\n          group: 'cart.handling',\r\n          name: L(item.name),\r\n          meta: item.code,\r\n          qty: q,\r\n          price: item.price,\r\n          edit: { module: 'handling' },\r\n          key: 'handling.cutting.' + item.id\r\n        });\r\n      });\r\n    }\r\n\r\n    \/* 5. Manipulaci\u00f3n subterr\u00e1nea ----------------------------------- *\/\r\n    if (op === 'underground' && s.subhandling.arm) {\r\n      push({\r\n        group: 'cart.subhandling',\r\n        name: L(IDR_DATA.subhandling.main.name),\r\n        qty: s.subhandling.armQty,\r\n        price: IDR_DATA.subhandling.main.price,\r\n        edit: { module: 'subhandling' },\r\n        key: 'sub.arm'\r\n      });\r\n    }\r\n\r\n    \/* 6. Extracci\u00f3n de muestras \/ testigos --------------------------- *\/\r\n    IDR_DATA.sampling.items.forEach((item) => {\r\n      const qty = s.sampling[item.id];\r\n      if (!qty) return;\r\n      push({\r\n        group: 'cart.sampling',\r\n        name: L(item.name),\r\n        qty: qty,\r\n        price: item.price,\r\n        edit: { module: op === 'surface' ? 'sampling' : 'handling' },\r\n        key: 'sampling.' + item.id\r\n      });\r\n    });\r\n\r\n    \/* 7. Telemetr\u00eda --------------------------------------------------- *\/\r\n    const tel = s.telemetry;\r\n    if (tel.target === 'probe' && tel.monitoring) {\r\n      const m = find(IDR_DATA.telemetry.monitoring, tel.monitoring);\r\n      push({\r\n        group: 'cart.telemetry',\r\n        name: t('tel.probe'),\r\n        meta: t(m.nameK),\r\n        qty: tel.probeQty,\r\n        price: m.price,\r\n        edit: { module: 'telemetry' },\r\n        key: 'tel.probe'\r\n      });\r\n    }\r\n    if (tel.target === 'cs-cart' && tel.csMode) {\r\n      const mode = find(IDR_DATA.telemetry.cs.modes, tel.csMode);\r\n      push({\r\n        group: 'cart.telemetry',\r\n        name: IDR_DATA.telemetry.cs.name,\r\n        meta: t(mode.nameK),\r\n        qty: tel.csQty,\r\n        price: mode.price,\r\n        edit: { module: 'telemetry' },\r\n        key: 'tel.cs'\r\n      });\r\n      IDR_DATA.telemetry.cs.accessories.forEach((acc) => {\r\n        const q = tel.accessories[acc.id];\r\n        if (!q) return;\r\n        push({\r\n          group: 'cart.accessories',\r\n          name: L(acc.name),\r\n          qty: q,\r\n          price: acc.price,\r\n          edit: { module: 'telemetry' },\r\n          key: 'tel.acc.' + acc.id\r\n        });\r\n      });\r\n    }\r\n\r\n    \/* 8. Observaciones ------------------------------------------------ *\/\r\n    if (s.notes.trim()) {\r\n      push({\r\n        group: 'cart.notes',\r\n        name: s.notes.trim(),\r\n        edit: { view: 'quote' },\r\n        key: 'notes'\r\n      });\r\n    }\r\n\r\n    return out;\r\n  }\r\n\r\n  \/** Di\u00e1metros de barra que aplican a la operaci\u00f3n elegida (informativo). *\/\r\n  function compatibleRods(s) {\r\n    if (!s.operation) return [];\r\n    return (IDR_DATA.rods[s.operation] || []).map((r) => L(r.name));\r\n  }\r\n\r\n  \/* N\u00ba de l\u00edneas cotizables: excluye operaci\u00f3n, observaciones y\r\n     cualquier l\u00ednea marcada como informativa (barras). *\/\r\n  function count(s) {\r\n    return lines(s).filter((l) =>\r\n      !l.info && l.group !== 'cart.operation' && l.group !== 'cart.notes'\r\n    ).length;\r\n  }\r\n\r\n  \/* \u00bfHay al menos un equipo seleccionado? *\/\r\n  function hasEquipment(s) {\r\n    const eq = ['cart.handling', 'cart.subhandling', 'cart.sampling', 'cart.telemetry', 'cart.accessories'];\r\n    return lines(s).some((l) => eq.indexOf(l.group) !== -1);\r\n  }\r\n\r\n  \/* Total: s\u00f3lo se calcula si el motor de precios est\u00e1 activo. *\/\r\n  function total(s) {\r\n    if (!IDR_CONFIG.pricing.enabled) return null;\r\n    return lines(s).reduce((sum, l) => sum + (l.info ? 0 : (l.price || 0) * (l.qty || 1)), 0);\r\n  }\r\n\r\n  function money(n) {\r\n    try {\r\n      return new Intl.NumberFormat(IDR_CONFIG.pricing.locale, {\r\n        style: 'currency', currency: IDR_CONFIG.pricing.currency, maximumFractionDigits: 0\r\n      }).format(n);\r\n    } catch (e) {\r\n      return IDR_CONFIG.pricing.currency + ' ' + n;\r\n    }\r\n  }\r\n\r\n  \/* -------------------------------------------------------------- *\/\r\n  \/* Render                                                          *\/\r\n  \/* -------------------------------------------------------------- *\/\r\n  function render(s) {\r\n    const items = lines(s);\r\n    const n = count(s);\r\n    const grand = total(s);\r\n\r\n    \/* Destino del bot\u00f3n \"Editar\" de cada l\u00ednea. *\/\r\n    const editAttr = (l) => l.edit.module\r\n      ? ' data-idr-action=\"open-module\" data-idr-module=\"' + C.esc(l.edit.module) + '\"'\r\n      : ' data-idr-action=\"goto\" data-idr-view=\"' + C.esc(l.edit.view) + '\"';\r\n\r\n    let body;\r\n    if (!items.length) {\r\n      body = '<div class=\"idr-cart__empty\">' + C.icon('box') +\r\n        '<p><strong>' + C.esc(t('cart.empty')) + '<\/strong><\/p>' +\r\n        '<p>' + C.esc(t('cart.emptyHint')) + '<\/p><\/div>';\r\n    } else {\r\n      \/* Agrupaci\u00f3n conservando el orden de aparici\u00f3n *\/\r\n      const order = [];\r\n      const groups = {};\r\n      items.forEach((l) => {\r\n        if (!groups[l.group]) { groups[l.group] = []; order.push(l.group); }\r\n        groups[l.group].push(l);\r\n      });\r\n\r\n      body = order.map((g) =>\r\n        '<section class=\"idr-cart__group\"><h4 class=\"idr-cart__groupT\">' + C.esc(t(g)) + '<\/h4><\/section>' +\r\n        groups[g].map(renderLine).join('')\r\n      ).join('');\r\n    }\r\n\r\n    function renderLine(l) {\r\n      return '<article class=\"idr-cart__item\">' +\r\n        '<div class=\"idr-cart__itemMain\">' +\r\n          '<div class=\"idr-cart__itemName\">' + C.esc(l.name) + '<\/div>' +\r\n          (l.meta ? '<div class=\"idr-cart__itemMeta\">' + C.esc(l.meta) + '<\/div>' : '') +\r\n          (IDR_CONFIG.pricing.enabled && l.price != null\r\n            ? '<div class=\"idr-cart__itemMeta\">' + C.esc(money(l.price * (l.qty || 1))) + '<\/div>'\r\n            : '') +\r\n        '<\/div>' +\r\n        (l.info\r\n          ? '<span class=\"idr-cart__qty idr-cart__qty--info\">' + C.esc(t('cart.infoTag')) + '<\/span>'\r\n          : (l.qty ? '<span class=\"idr-cart__qty\">x' + C.esc(l.qty) + '<\/span>' : '')) +\r\n        '<div class=\"idr-cart__tools\">' +\r\n          '<button type=\"button\" class=\"idr-cart__tool\" title=\"' + C.esc(t('common.edit')) + '\" ' +\r\n            'aria-label=\"' + C.esc(t('common.edit')) + '\"' + editAttr(l) + '>' + C.icon('pencil') + '<\/button>' +\r\n          '<button type=\"button\" class=\"idr-cart__tool idr-cart__tool--del\" ' +\r\n            'title=\"' + C.esc(t('common.remove')) + '\" aria-label=\"' + C.esc(t('common.remove')) + '\" ' +\r\n            'data-idr-action=\"remove-line\" data-idr-key=\"' + C.esc(l.key) + '\">' + C.icon('trash') + '<\/button>' +\r\n        '<\/div><\/article>';\r\n    }\r\n\r\n    return '<div class=\"' + C.cx('idr-cart', s.ui.cartCollapsed && 'is-collapsed') + '\">' +\r\n      '<header class=\"idr-cart__head\">' +\r\n        '<span class=\"idr-cart__icon\">' + C.icon('cart') +\r\n          (n ? '<span class=\"idr-cart__badge\">' + n + '<\/span>' : '') + '<\/span>' +\r\n        '<h3 class=\"idr-cart__t\">' + C.esc(t('cart.title')) + '<\/h3>' +\r\n        '<button type=\"button\" class=\"idr-cart__toggle\" data-idr-action=\"toggle-cart\" ' +\r\n          'aria-expanded=\"' + (!s.ui.cartCollapsed) + '\" ' +\r\n          'aria-label=\"' + C.esc(t('cart.title')) + '\">' + C.icon('chevron') + '<\/button>' +\r\n      '<\/header>' +\r\n      '<div class=\"idr-cart__body\">' + body + '<\/div>' +\r\n      '<footer class=\"idr-cart__foot\">' +\r\n        '<div class=\"idr-cart__total\">' +\r\n          '<span class=\"idr-cart__totalK\">' + C.esc(t('cart.total')) + '<\/span>' +\r\n          '<span class=\"idr-cart__totalV\">' +\r\n            C.esc(grand != null ? money(grand) : t('cart.priceOnRequest')) + '<\/span>' +\r\n        '<\/div>' +\r\n        '<button type=\"button\" class=\"idr-btn idr-btn--accent idr-btn--block\" ' +\r\n          'data-idr-action=\"open-quote\"' + (n ? '' : ' disabled') + '>' +\r\n          C.esc(t('cart.request')) + C.icon('arrowR') + '<\/button>' +\r\n        (items.length\r\n          ? '<button type=\"button\" class=\"idr-linkbtn idr-cart__reset\" data-idr-action=\"reset\">' +\r\n            C.icon('reset') + C.esc(t('cart.reset')) + '<\/button>'\r\n          : '') +\r\n      '<\/footer><\/div>';\r\n  }\r\n\r\n  \/* -------------------------------------------------------------- *\/\r\n  \/* Payload de salida (correo \/ WhatsApp \/ CRM)                     *\/\r\n  \/* -------------------------------------------------------------- *\/\r\n  function payload(s) {\r\n    return {\r\n      reference: s.ui.reference,\r\n      createdAt: new Date().toISOString(),\r\n      language: s.lang,\r\n      contact: Object.assign({}, s.contact),\r\n      configuration: {\r\n        operation: s.operation,\r\n        rig: {\r\n          model: s.rig.model,\r\n          customModel: s.rig.customModel,\r\n          quantity: s.rig.quantity\r\n        },\r\n        handling: Object.assign({}, s.handling),\r\n        subhandling: Object.assign({}, s.subhandling),\r\n        sampling: Object.assign({}, s.sampling),\r\n        telemetry: Object.assign({}, s.telemetry, { accessories: Object.assign({}, s.telemetry.accessories) }),\r\n        notes: s.notes\r\n      },\r\n      \/* S\u00f3lo \u00edtems cotizables. Las barras viajan en `information`. *\/\r\n      items: lines(s).filter((l) => !l.info).map((l) => ({\r\n        group: t(l.group), name: l.name, detail: l.meta || null,\r\n        quantity: l.qty || 1, unitPrice: l.price != null ? l.price : null\r\n      })),\r\n      information: {\r\n        compatibleRods: compatibleRods(s)\r\n      },\r\n      total: total(s),\r\n      currency: IDR_CONFIG.pricing.currency\r\n    };\r\n  }\r\n\r\n  \/* Versi\u00f3n en texto plano, \u00fatil para WhatsApp y para el cuerpo del correo. *\/\r\n  function asText(s) {\r\n    const p = payload(s);\r\n    const rows = p.items.map((i) =>\r\n      '\u00b7 ' + i.group + ': ' + i.name + (i.detail ? ' (' + i.detail + ')' : '') +\r\n      (i.quantity > 1 ? ' x' + i.quantity : '')\r\n    );\r\n    return [\r\n      'INNRA \u00b7 ' + t('cart.request'),\r\n      t('quote.ref') + ': ' + p.reference,\r\n      '',\r\n      t('quote.contact') + ': ' + [p.contact.name, p.contact.company, p.contact.email, p.contact.phone, p.contact.country].filter(Boolean).join(' \u00b7 '),\r\n      p.information.compatibleRods.length\r\n        ? t('quote.rodsRef') + ': ' + p.information.compatibleRods.join(' \u00b7 ')\r\n        : '',\r\n      '',\r\n      rows.join('\\n')\r\n    ].filter((l, i, a) => !(l === '' && a[i - 1] === '')).join('\\n');\r\n  }\r\n\r\n  return { lines, count, hasEquipment, total, money, render, payload, asText, compatibleRods };\r\n})();\r\n<\/script>\r\n\r\n<!-- ============================================================ -->\r\n<!-- ==== FILE: app.js ========================================== -->\r\n<!-- Estado, enrutado de vistas, render y eventos.                 -->\r\n<!-- Toda la aplicaci\u00f3n vive dentro de una IIFE y expone un \u00fanico  -->\r\n<!-- objeto global: window.IDR                                     -->\r\n<!-- ============================================================ -->\r\n<script>\r\n(function () {\r\n  'use strict';\r\n\r\n  const C = IDR_C;\r\n  const ROOT = document.querySelector('[data-idr-root]');\r\n  if (!ROOT) return;\r\n\r\n  const $  = (sel, ctx) => (ctx || ROOT).querySelector(sel);\r\n  const els = {\r\n    lang:   $('[data-idr-lang]'),\r\n    stage:  $('[data-idr-stage]'),\r\n    log:    $('[data-idr-log]'),\r\n    view:   $('[data-idr-view]'),\r\n    cart:   $('[data-idr-cart]'),\r\n    modals: $('[data-idr-modals]'),\r\n    toast:  $('[data-idr-toast]')\r\n  };\r\n\r\n  \/* ================================================================\r\n     1. PERSISTENCIA\r\n     Adaptador tolerante: si el navegador bloquea el almacenamiento\r\n     (modo privado, pol\u00edticas de WordPress) se degrada a memoria.\r\n  ================================================================ *\/\r\n  const store = (function () {\r\n    let memory = null;\r\n    let available = false;\r\n    try {\r\n      const probe = '__idr_probe__';\r\n      window.localStorage.setItem(probe, '1');\r\n      window.localStorage.removeItem(probe);\r\n      available = true;\r\n    } catch (e) { available = false; }\r\n\r\n    return {\r\n      read() {\r\n        if (!IDR_CONFIG.persistSession) return null;\r\n        try {\r\n          const raw = available ? window.localStorage.getItem(IDR_CONFIG.storageKey) : memory;\r\n          return raw ? JSON.parse(raw) : null;\r\n        } catch (e) { return null; }\r\n      },\r\n      write(data) {\r\n        if (!IDR_CONFIG.persistSession) return;\r\n        try {\r\n          const raw = JSON.stringify(data);\r\n          if (available) window.localStorage.setItem(IDR_CONFIG.storageKey, raw);\r\n          else memory = raw;\r\n        } catch (e) { \/* silencioso: la app funciona igual sin persistencia *\/ }\r\n      },\r\n      clear() {\r\n        try {\r\n          if (available) window.localStorage.removeItem(IDR_CONFIG.storageKey);\r\n          memory = null;\r\n        } catch (e) { \/* noop *\/ }\r\n      }\r\n    };\r\n  })();\r\n\r\n  \/* ================================================================\r\n     2. ESTADO\r\n  ================================================================ *\/\r\n  function makeReference() {\r\n    const d = new Date();\r\n    const pad = (n) => String(n).padStart(2, '0');\r\n    const rnd = Math.random().toString(36).slice(2, 6).toUpperCase();\r\n    return 'IDR-' + d.getFullYear() + pad(d.getMonth() + 1) + pad(d.getDate()) + '-' + rnd;\r\n  }\r\n\r\n  function createState() {\r\n    return {\r\n      lang: IDR_CONFIG.defaultLang,\r\n      view: 'operation',\r\n      module: null,\r\n\r\n      operation: null,\r\n      rig: { model: null, customModel: '', quantity: 1 },\r\n\r\n      handling: {\r\n        et: false, etQty: 1,\r\n        storage: null, storageQty: 1,\r\n        ampliroll: false, amplirollQty: 1,\r\n        conveyor: false, conveyorQty: 1,\r\n        cutting: {},                 \/* id del equipo de corte -> cantidad *\/\r\n        manualWarningAck: false,\r\n        etWarningAck: false          \/* el usuario ya acept\u00f3 configurar sin IDR-ET *\/\r\n      },\r\n      subhandling: { arm: false, armQty: 1 },\r\n      sampling: {},                        \/* itemId -> cantidad *\/\r\n      telemetry: {\r\n        target: null,                      \/* 'probe' | 'cs-cart' *\/\r\n        monitoring: null,                  \/* 'video' | 'data' | 'data-video' *\/\r\n        probeQty: 1,\r\n        csMode: null,                      \/* 'purchase' | 'rental' *\/\r\n        csQty: 1,\r\n        accessories: {}                    \/* accId -> cantidad *\/\r\n      },\r\n      notes: '',\r\n      contact: { name: '', company: '', email: '', phone: '', country: '' },\r\n\r\n      ui: {\r\n        started: false,\r\n        cartCollapsed: false,\r\n        modal: null,\r\n        pending: null,               \/* acci\u00f3n en espera de confirmaci\u00f3n del usuario *\/\r\n        reference: makeReference(),\r\n        sending: false,\r\n        sent: false\r\n      }\r\n    };\r\n  }\r\n\r\n  let state = createState();\r\n\r\n  \/* Rehidrataci\u00f3n desde sesi\u00f3n previa (s\u00f3lo datos de configuraci\u00f3n). *\/\r\n  (function hydrate() {\r\n    const saved = store.read();\r\n    if (!saved) return;\r\n    try {\r\n      ['lang', 'operation', 'notes'].forEach((k) => { if (saved[k] != null) state[k] = saved[k]; });\r\n      ['rig', 'handling', 'subhandling', 'telemetry', 'contact'].forEach((k) => {\r\n        if (saved[k]) Object.assign(state[k], saved[k]);\r\n      });\r\n      if (saved.sampling) state.sampling = Object.assign({}, saved.sampling);\r\n      if (saved.handling && saved.handling.cutting) {\r\n        state.handling.cutting = Object.assign({}, saved.handling.cutting);\r\n      }\r\n      if (saved.telemetry && saved.telemetry.accessories) {\r\n        state.telemetry.accessories = Object.assign({}, saved.telemetry.accessories);\r\n      }\r\n      if (saved.reference) state.ui.reference = saved.reference;\r\n      if (state.operation) { state.ui.started = true; state.view = 'equipment'; }\r\n    } catch (e) { state = createState(); }\r\n  })();\r\n\r\n  function persist() {\r\n    store.write({\r\n      lang: state.lang,\r\n      operation: state.operation,\r\n      rig: state.rig,\r\n      handling: state.handling,\r\n      subhandling: state.subhandling,\r\n      sampling: state.sampling,\r\n      telemetry: state.telemetry,\r\n      contact: state.contact,\r\n      notes: state.notes,\r\n      reference: state.ui.reference\r\n    });\r\n  }\r\n\r\n  \/* ================================================================\r\n     3. TRADUCCI\u00d3N\r\n  ================================================================ *\/\r\n  function t(key) {\r\n    const dict = IDR_I18N[state.lang] || IDR_I18N[IDR_CONFIG.defaultLang];\r\n    if (dict && dict[key] != null) return dict[key];\r\n    const fb = IDR_I18N[IDR_CONFIG.defaultLang];\r\n    return (fb && fb[key] != null) ? fb[key] : key;\r\n  }\r\n\r\n  \/** Localiza un valor de cat\u00e1logo: string literal u objeto {es,en,pt}. *\/\r\n  function L(value) {\r\n    if (value == null) return '';\r\n    if (typeof value === 'string') return value;\r\n    return value[state.lang] || value[IDR_CONFIG.defaultLang] || Object.values(value)[0] || '';\r\n  }\r\n\r\n  \/** Nombre visible de una entrada de cat\u00e1logo (name literal o nameK traducible). *\/\r\n  function nameOf(def) {\r\n    if (!def) return '';\r\n    return def.nameK ? t(def.nameK) : L(def.name);\r\n  }\r\n\r\n  \/** Aplica traducciones a los nodos est\u00e1ticos del marcado. *\/\r\n  function applyStaticText() {\r\n    ROOT.querySelectorAll('[data-idr-t]').forEach((el) => { el.textContent = t(el.dataset.idrT); });\r\n    ROOT.querySelectorAll('[data-idr-html]').forEach((el) => { el.innerHTML = t(el.dataset.idrHtml); });\r\n    document.documentElement.setAttribute('lang', state.lang === 'pt' ? 'pt-BR' : state.lang);\r\n  }\r\n\r\n  \/* ================================================================\r\n     4. REGLAS DE NEGOCIO \/ DERIVADOS\r\n  ================================================================ *\/\r\n  const byId = (arr, id) => arr.find((x) => x.id === id);\r\n\r\n  function rigDef()  { return state.operation ? byId(IDR_DATA.rigs[state.operation], state.rig.model) : null; }\r\n  function rigIsCustom() { const d = rigDef(); return !!(d && d.custom); }\r\n\r\n  function availableModules() {\r\n    return state.operation ? IDR_DATA.modules[state.operation] : [];\r\n  }\r\n\r\n  function moduleIsConfigured(id) {\r\n    const s = state;\r\n    switch (id) {\r\n      case 'handling':\r\n        return !!(s.handling.et || s.handling.storage ||\r\n                  Object.keys(s.handling.cutting).some((k) => s.handling.cutting[k] > 0));\r\n      case 'subhandling': return !!s.subhandling.arm;\r\n      case 'sampling':    return Object.keys(s.sampling).some((k) => s.sampling[k] > 0);\r\n      case 'telemetry':\r\n        return !!((s.telemetry.target === 'probe' && s.telemetry.monitoring) ||\r\n                  (s.telemetry.target === 'cs-cart' && s.telemetry.csMode));\r\n      default: return false;\r\n    }\r\n  }\r\n\r\n  function moduleTitle(id) {\r\n    switch (id) {\r\n      case 'handling':    return t('hd.title');\r\n      case 'subhandling': return t('sub.title');\r\n      case 'telemetry':   return t('tel.title');\r\n      case 'sampling':    return t('sm.title');\r\n      default: return '';\r\n    }\r\n  }\r\n\r\n  \/* Estado de cada paso del registro de sondaje. *\/\r\n  function stepDone(id) {\r\n    switch (id) {\r\n      case 'operation': return !!state.operation;\r\n      case 'rig':       return !!state.rig.model && (!rigIsCustom() || !!state.rig.customModel.trim());\r\n      \/* Paso informativo: se considera cubierto en cuanto hay operaci\u00f3n,\r\n         porque los di\u00e1metros los define el cat\u00e1logo, no el usuario. *\/\r\n      case 'rods':      return !!state.operation;\r\n      case 'equipment': return IDR_CART.hasEquipment(state);\r\n      case 'quote':     return state.ui.sent;\r\n      default: return false;\r\n    }\r\n  }\r\n  function stepReachable(id) {\r\n    const steps = IDR_DATA.steps;\r\n    const i = steps.findIndex((x) => x.id === id);\r\n    if (i <= 0) return true;\r\n    \/* Los pasos marcados como `optional` (barras: informaci\u00f3n de\r\n       referencia) nunca bloquean el avance. *\/\r\n    for (let k = 0; k < i; k++) {\r\n      if (!steps[k].optional && !stepDone(steps[k].id)) return false;\r\n    }\r\n    return true;\r\n  }\r\n  function currentStep() {\r\n    if (state.view === 'module') {\r\n      return 'equipment';\r\n    }\r\n    return ['operation', 'rig', 'rods', 'equipment', 'quote'].indexOf(state.view) !== -1\r\n      ? state.view : 'operation';\r\n  }\r\n\r\n  \/* Resumen corto por paso, mostrado en el registro. *\/\r\n  function stepValue(id) {\r\n    const s = state;\r\n    switch (id) {\r\n      case 'operation':\r\n        return s.operation ? nameOf(byId(IDR_DATA.operations, s.operation)) : '';\r\n      case 'rig': {\r\n        if (!s.rig.model) return '';\r\n        const label = rigIsCustom() ? (s.rig.customModel.trim() || t('common.other')) : nameOf(rigDef());\r\n        return label + (s.rig.quantity > 1 ? ' \u00d7' + s.rig.quantity : '');\r\n      }\r\n      case 'rods':\r\n        return s.operation ? IDR_CART.compatibleRods(s).join(' \u00b7 ') : '';\r\n      case 'equipment': {\r\n        const n = IDR_CART.count(s);\r\n        return n ? n + ' ' + t('cart.lines') : '';\r\n      }\r\n      case 'quote':\r\n        return s.ui.sent ? s.ui.reference : '';\r\n      default: return '';\r\n    }\r\n  }\r\n\r\n  \/* ================================================================\r\n     5. MUTACIONES DE ESTADO\r\n  ================================================================ *\/\r\n\r\n  \/** Cambiar de operaci\u00f3n reinicia todo lo que depende del cat\u00e1logo. *\/\r\n  function setOperation(id) {\r\n    if (state.operation === id) return;\r\n    const keep = { lang: state.lang, ui: state.ui, contact: state.contact, notes: state.notes };\r\n    state = Object.assign(createState(), {\r\n      lang: keep.lang, ui: keep.ui, contact: keep.contact, notes: keep.notes,\r\n      operation: id, view: state.view\r\n    });\r\n  }\r\n\r\n  \/* ---------------------------------------------------------------\r\n     Sistema de Corte: cadena IDR-LP -> IDR-BOU -> IDR-CEN.\r\n     Un equipo s\u00f3lo puede elegirse si su prerrequisito ya est\u00e1 en la\r\n     configuraci\u00f3n; al quitarlo se arrastran sus dependientes.\r\n  --------------------------------------------------------------- *\/\r\n  function cuttingUnlocked(itemId) {\r\n    const item = byId(IDR_DATA.handling.cutting, itemId);\r\n    if (!item || !item.requires) return true;\r\n    return (state.handling.cutting[item.requires] || 0) > 0;\r\n  }\r\n\r\n  \/** Nombre del prerrequisito, para mostrarlo en la fila bloqueada. *\/\r\n  function cuttingRequirement(item) {\r\n    if (!item.requires) return null;\r\n    const req = byId(IDR_DATA.handling.cutting, item.requires);\r\n    return req ? (req.code || L(req.name)) : null;\r\n  }\r\n\r\n  function toggleCutting(id) {\r\n    const c = state.handling.cutting;\r\n    if (c[id]) {\r\n      delete c[id];\r\n      \/* Cascada: cae todo lo que depend\u00eda de \u00e9l. *\/\r\n      let changed = true;\r\n      while (changed) {\r\n        changed = false;\r\n        IDR_DATA.handling.cutting.forEach((it) => {\r\n          if (c[it.id] && it.requires && !c[it.requires]) { delete c[it.id]; changed = true; }\r\n        });\r\n      }\r\n    } else if (cuttingUnlocked(id)) {\r\n      c[id] = 1;\r\n    }\r\n  }\r\n\r\n  \/* Lectura y escritura gen\u00e9rica de cantidades. target\/key vienen del DOM. *\/\r\n  function getQty(target, key) {\r\n    const s = state;\r\n    switch (target) {\r\n      case 'rig':      return s.rig.quantity;\r\n      case 'handling': return s.handling[key];\r\n      case 'cutting':  return s.handling.cutting[key] || 0;\r\n      case 'sub':      return s.subhandling[key];\r\n      case 'sampling': return s.sampling[key] || 0;\r\n      case 'tel':      return s.telemetry[key];\r\n      case 'telacc':   return s.telemetry.accessories[key] || 0;\r\n      default: return 1;\r\n    }\r\n  }\r\n  function setQty(target, key, value) {\r\n    const min = IDR_CONFIG.qty.min;\r\n    const max = IDR_CONFIG.qty.max;\r\n    const v = Math.min(max, Math.max(min, parseInt(value, 10) || min));\r\n    const s = state;\r\n    switch (target) {\r\n      case 'rig':      s.rig.quantity = v; break;\r\n      case 'handling': s.handling[key] = v; break;\r\n      case 'cutting':  s.handling.cutting[key] = v; break;\r\n      case 'sub':      s.subhandling[key] = v; break;\r\n      case 'sampling': s.sampling[key] = v; break;\r\n      case 'tel':      s.telemetry[key] = v; break;\r\n      case 'telacc':   s.telemetry.accessories[key] = v; break;\r\n    }\r\n  }\r\n\r\n  \/** Elimina una l\u00ednea del carrito a partir de su llave. *\/\r\n  function removeLine(key) {\r\n    const s = state;\r\n    const [head, tail] = [key.split('.')[0], key.split('.').slice(1).join('.')];\r\n\r\n    if (key === 'operation') { resetAll(false); return; }\r\n    if (key === 'rig') { s.rig.model = null; s.rig.customModel = ''; s.rig.quantity = 1; return; }\r\n\r\n    if (head === 'handling') {\r\n      if (tail === 'et') { s.handling.et = false; s.handling.etQty = 1; }\r\n      if (tail === 'storage') {\r\n        s.handling.storage = null;\r\n        s.handling.ampliroll = false;\r\n        s.handling.conveyor = false;\r\n        s.handling.manualWarningAck = false;\r\n      }\r\n      \/* Quitar el Ampliroll arrastra al Transportador. *\/\r\n      if (tail === 'ampliroll') { s.handling.ampliroll = false; s.handling.conveyor = false; }\r\n      if (tail === 'conveyor')  { s.handling.conveyor = false; }\r\n      if (tail.indexOf('cutting.') === 0) { toggleCutting(tail.slice(8)); }\r\n      return;\r\n    }\r\n    if (head === 'sub')      { s.subhandling.arm = false; s.subhandling.armQty = 1; return; }\r\n    if (head === 'sampling') { delete s.sampling[tail]; return; }\r\n\r\n    if (head === 'tel') {\r\n      if (tail === 'probe') { s.telemetry.monitoring = null; s.telemetry.target = null; }\r\n      if (tail === 'cs')    { s.telemetry.csMode = null; s.telemetry.target = null; s.telemetry.accessories = {}; }\r\n      if (tail.indexOf('acc.') === 0) { delete s.telemetry.accessories[tail.slice(4)]; }\r\n      return;\r\n    }\r\n    if (key === 'notes') { s.notes = ''; }\r\n  }\r\n\r\n  function resetAll(full) {\r\n    const lang = state.lang;\r\n    const started = state.ui.started;\r\n    state = createState();\r\n    state.lang = lang;\r\n    state.ui.started = started;\r\n    state.view = 'operation';\r\n    if (full) store.clear();\r\n  }\r\n\r\n  \/* ================================================================\r\n     6. VISTAS\r\n  ================================================================ *\/\r\n\r\n  \/* -- Paso 1: operaci\u00f3n ------------------------------------------ *\/\r\n  function viewOperation() {\r\n    const opts = IDR_DATA.operations.map((o) => C.option({\r\n      title: nameOf(o),\r\n      desc: t(o.descK),\r\n      on: state.operation === o.id,\r\n      data: { 'idr-action': 'select-operation', 'idr-id': o.id }\r\n    })).join('');\r\n\r\n    return '<section class=\"idr-panel idr-anim\">' +\r\n      C.panelHead(t('op.eyebrow'), t('op.title'), t('op.desc')) +\r\n      '<div class=\"idr-grid idr-grid--2\">' + opts + '<\/div>' +\r\n      C.actions('', C.nextBtn(t('common.continue'), 'next', !stepDone('operation'))) +\r\n      '<\/section>';\r\n  }\r\n\r\n  \/* -- Paso 2: perforadora ---------------------------------------- *\/\r\n  function viewRig() {\r\n    const list = IDR_DATA.rigs[state.operation] || [];\r\n    const opts = list.map((r) => C.option({\r\n      title: nameOf(r),\r\n      on: state.rig.model === r.id,\r\n      data: { 'idr-action': 'select-rig', 'idr-id': r.id }\r\n    })).join('');\r\n\r\n    const customBlock = rigIsCustom()\r\n      ? '<div style=\"margin-top:14px;max-width:420px\">' +\r\n          C.field(t('rig.otherLabel'), C.input({\r\n            name: 'rig.customModel', value: state.rig.customModel,\r\n            placeholder: t('rig.otherPlaceholder'), autofocus: true\r\n          })) +\r\n        '<\/div>' + C.note(t('rig.feasibility'))\r\n      : '';\r\n\r\n    return '<section class=\"idr-panel idr-anim\">' +\r\n      C.panelHead(t('rig.eyebrow'), t('rig.title'), t('rig.desc')) +\r\n      '<div class=\"idr-block\">' +\r\n        C.blockHead(t('rig.model')) +\r\n        '<div class=\"idr-grid idr-grid--3\">' + opts + '<\/div>' +\r\n        customBlock +\r\n      '<\/div>' +\r\n      '<div class=\"idr-block\">' +\r\n        C.blockHead(t('rig.quantity'), t('rig.quantityHint')) +\r\n        C.qty({ value: state.rig.quantity, target: 'rig', key: 'quantity' }) +\r\n      '<\/div>' +\r\n      C.actions(\r\n        C.backLink(t('common.back'), 'back'),\r\n        C.nextBtn(t('common.continue'), 'next', !stepDone('rig'))\r\n      ) + '<\/section>';\r\n  }\r\n\r\n  \/* -- Paso 3: barras ---------------------------------------------- *\/\r\n  \/* -- Paso 3: barras (ficha t\u00e9cnica, no seleccionable) ------------- *\/\r\n  function viewRods() {\r\n    const list = IDR_DATA.rods[state.operation] || [];\r\n    const chips = list.map((r) =>\r\n      '<span class=\"idr-chip idr-chip--static\"><span class=\"idr-chip__dot\"><\/span>' +\r\n      C.esc(nameOf(r)) + '<\/span>'\r\n    ).join('');\r\n\r\n    return '<section class=\"idr-panel idr-anim\">' +\r\n      C.panelHead(t('rods.eyebrow'), t('rods.title'), t('rods.desc')) +\r\n      '<div class=\"idr-block\">' +\r\n        C.blockHead(t('rods.spec'), t('rods.hint')) +\r\n        '<div class=\"idr-chips\">' + chips + '<\/div>' +\r\n        C.note(t('rods.infoNote')) +\r\n      '<\/div>' +\r\n      C.actions(\r\n        C.backLink(t('common.back'), 'back'),\r\n        \/* Paso informativo: nunca bloquea el avance. *\/\r\n        C.nextBtn(t('common.continue'), 'next', false)\r\n      ) + '<\/section>';\r\n  }\r\n\r\n  \/* -- Paso 4: equipos disponibles --------------------------------- *\/\r\n  function viewEquipment() {\r\n    const cards = availableModules().map((id) => {\r\n      const meta = IDR_DATA.moduleMeta[id];\r\n      return C.moduleCard({\r\n        id: id,\r\n        code: meta.code,\r\n        art: meta.art,\r\n        title: moduleTitle(id),\r\n        desc: t(meta.descK),\r\n        configured: moduleIsConfigured(id)\r\n      });\r\n    }).join('');\r\n\r\n    return '<section class=\"idr-panel idr-anim\">' +\r\n      C.panelHead(t('equip.eyebrow'), t('equip.title'), t('equip.desc')) +\r\n      '<div class=\"idr-grid idr-grid--wide\">' + cards + '<\/div>' +\r\n      C.actions(\r\n        C.backLink(t('common.back'), 'back'),\r\n        C.nextBtn(t('common.continue'), 'next', !stepDone('equipment'))\r\n      ) + '<\/section>';\r\n  }\r\n\r\n  \/* -- M\u00f3dulo: Sistema de Manipulaci\u00f3n de Barras ------------------- *\/\r\n  function viewHandling() {\r\n    const h = state.handling;\r\n    const D = IDR_DATA.handling;\r\n\r\n    \/* Equipo principal IDR-ET *\/\r\n    const etRow = C.row({\r\n      name: D.main.name,\r\n      desc: t(D.main.descK),\r\n      on: h.et,\r\n      data: { 'idr-action': 'toggle-et' },\r\n      qtyValue: h.etQty, qtyTarget: 'handling', qtyKey: 'etQty'\r\n    });\r\n\r\n    \/* Tipo de almacenamiento *\/\r\n    const storageOpts = D.storage.map((st) => C.option({\r\n      title: L(st.name),\r\n      on: h.storage === st.id,\r\n      data: { 'idr-action': 'select-storage', 'idr-id': st.id }\r\n    })).join('');\r\n\r\n    const selectedStorage = h.storage ? byId(D.storage, h.storage) : null;\r\n\r\n    const storageQtyRow = selectedStorage\r\n      ? '<div class=\"idr-row is-on\" style=\"margin-top:14px\">' +\r\n          '<span class=\"idr-row__main\"><span><span class=\"idr-row__name\">' +\r\n            C.esc(L(selectedStorage.name)) + '<\/span>' +\r\n            '<span class=\"idr-row__desc\">' + C.esc(t('common.qty')) + '<\/span><\/span><\/span>' +\r\n          C.qty({ value: h.storageQty, target: 'handling', key: 'storageQty', size: 'sm' }) +\r\n        '<\/div>'\r\n      : '';\r\n\r\n    \/* Complementos de la cama: s\u00f3lo en camas con allowAddons (350H).\r\n       El Ampliroll puede ir solo; el Transportador exige Ampliroll. *\/\r\n    const addonRow = (o) =>\r\n      '<div class=\"idr-row' + (o.on ? ' is-on' : '') + '\">' +\r\n        '<span class=\"idr-row__main\"><span>' +\r\n          '<span class=\"idr-row__name\">' + C.esc(o.name) + '<\/span>' +\r\n          '<span class=\"idr-row__desc\">' + C.esc(o.desc) + '<\/span><\/span><\/span>' +\r\n        '<div style=\"display:flex;align-items:center;gap:10px\">' +\r\n          (o.on ? C.qty({ value: o.qtyValue, target: 'handling', key: o.qtyKey, size: 'sm' }) : '') +\r\n          C.segYesNo({ value: o.on, data: { 'idr-action': o.action } }) +\r\n        '<\/div>' +\r\n      '<\/div>';\r\n\r\n    const addonsBlock = (selectedStorage && selectedStorage.allowAddons)\r\n      ? '<p class=\"idr-label\" style=\"margin:22px 0 10px\">' + C.esc(t('hd.addons')) + '<\/p>' +\r\n        '<div class=\"idr-rows\">' +\r\n          addonRow({\r\n            name: t('hd.ampliroll'), desc: t('hd.amplirollDesc'),\r\n            on: h.ampliroll, qtyValue: h.amplirollQty, qtyKey: 'amplirollQty',\r\n            action: 'set-ampliroll'\r\n          }) +\r\n          addonRow({\r\n            name: t('hd.conveyor'), desc: t('hd.conveyorDesc'),\r\n            on: h.conveyor, qtyValue: h.conveyorQty, qtyKey: 'conveyorQty',\r\n            action: 'set-conveyor'\r\n          }) +\r\n        '<\/div>'\r\n      : '';\r\n\r\n    \/* Sistema de Corte: cadena IDR-LP -> IDR-BOU -> IDR-CEN *\/\r\n    const cuttingRows = D.cutting.map((item) => {\r\n      const on = (h.cutting[item.id] || 0) > 0;\r\n      const unlocked = cuttingUnlocked(item.id);\r\n      return C.row({\r\n        name: L(item.name),\r\n        code: item.code,\r\n        requires: (!unlocked && !on) ? t('hd.requires') + ' ' + cuttingRequirement(item) : null,\r\n        locked: !unlocked && !on,\r\n        on: on,\r\n        data: { 'idr-action': 'toggle-cutting', 'idr-id': item.id },\r\n        qtyValue: h.cutting[item.id] || 1,\r\n        qtyTarget: 'cutting', qtyKey: item.id\r\n      });\r\n    }).join('');\r\n\r\n    return '<section class=\"idr-panel idr-anim\">' +\r\n      C.panelHead(t('hd.eyebrow'), t('hd.title'), t('hd.desc')) +\r\n\r\n      '<div class=\"idr-block\">' + C.blockHead(t('hd.mainEquip')) +\r\n        '<div class=\"idr-rows\">' + etRow + '<\/div>' +\r\n      '<\/div>' +\r\n\r\n      '<div class=\"idr-block\">' + C.blockHead(t('hd.storage'), t('hd.storageDesc')) +\r\n        '<div class=\"idr-grid idr-grid--3\">' + storageOpts + '<\/div>' +\r\n        storageQtyRow + addonsBlock +\r\n      '<\/div>' +\r\n\r\n      '<div class=\"idr-block\">' + C.blockHead(t('hd.cutting'), t('hd.cuttingDesc')) +\r\n        '<div class=\"idr-rows\">' + cuttingRows + '<\/div>' +\r\n        C.note(t('hd.cuttingNote')) +\r\n      '<\/div>' +\r\n\r\n      '<div class=\"idr-block\">' + C.blockHead(t('hd.core'), t('hd.coreDesc')) +\r\n        samplingRows() +\r\n      '<\/div>' +\r\n\r\n      C.actions(\r\n        C.backLink(t('common.back'), 'close-module'),\r\n        '<button type=\"button\" class=\"idr-btn\" data-idr-action=\"close-module\">' +\r\n          C.esc(t('common.save')) + C.icon('check') + '<\/button>'\r\n      ) + '<\/section>';\r\n  }\r\n\r\n  \/* Filas de extracci\u00f3n de testigos \/ muestras (estado compartido). *\/\r\n  function samplingRows() {\r\n    return '<div class=\"idr-rows\">' + IDR_DATA.sampling.items.map((item) => C.row({\r\n      name: L(item.name),\r\n      on: (state.sampling[item.id] || 0) > 0,\r\n      data: { 'idr-action': 'toggle-sampling', 'idr-id': item.id },\r\n      qtyValue: state.sampling[item.id] || 1,\r\n      qtyTarget: 'sampling', qtyKey: item.id\r\n    })).join('') + '<\/div>';\r\n  }\r\n\r\n  \/* -- M\u00f3dulo: Sistema de Extracci\u00f3n de Muestras ------------------- *\/\r\n  function viewSampling() {\r\n    return '<section class=\"idr-panel idr-anim\">' +\r\n      C.panelHead(t('sm.eyebrow'), t('sm.title'), t('sm.desc')) +\r\n      '<div class=\"idr-block\">' + C.blockHead(t('sm.items'), t('hd.coreDesc')) +\r\n        samplingRows() +\r\n      '<\/div>' +\r\n      C.actions(\r\n        C.backLink(t('common.back'), 'close-module'),\r\n        '<button type=\"button\" class=\"idr-btn\" data-idr-action=\"close-module\">' +\r\n          C.esc(t('common.save')) + C.icon('check') + '<\/button>'\r\n      ) + '<\/section>';\r\n  }\r\n\r\n  \/* -- M\u00f3dulo: Brazo Manipulador ET SUB ---------------------------- *\/\r\n  function viewSubHandling() {\r\n    const m = IDR_DATA.subhandling.main;\r\n    return '<section class=\"idr-panel idr-anim\">' +\r\n      C.panelHead(t('sub.eyebrow'), t('sub.title'), t('sub.desc')) +\r\n      '<div class=\"idr-rows\">' +\r\n        C.row({\r\n          name: L(m.name),\r\n          desc: t(m.descK),\r\n          on: state.subhandling.arm,\r\n          data: { 'idr-action': 'toggle-arm' },\r\n          qtyValue: state.subhandling.armQty, qtyTarget: 'sub', qtyKey: 'armQty'\r\n        }) +\r\n      '<\/div>' +\r\n      C.actions(\r\n        C.backLink(t('common.back'), 'close-module'),\r\n        '<button type=\"button\" class=\"idr-btn\" data-idr-action=\"close-module\">' +\r\n          C.esc(t('common.save')) + C.icon('check') + '<\/button>'\r\n      ) + '<\/section>';\r\n  }\r\n\r\n  \/* -- M\u00f3dulo: Telemetr\u00eda y Telesupervisi\u00f3n ------------------------ *\/\r\n  function viewTelemetry() {\r\n    const tel = state.telemetry;\r\n    const D = IDR_DATA.telemetry;\r\n\r\n    \/* Objetivos disponibles seg\u00fan la operaci\u00f3n *\/\r\n    const targets = D.targets.filter((x) => !x.operations || x.operations.indexOf(state.operation) !== -1);\r\n    const targetOpts = targets.map((x) => C.option({\r\n      title: t(x.nameK),\r\n      desc: t(x.descK) + (x.operations ? ' ' + t('tel.surfaceOnly') : ''),\r\n      on: tel.target === x.id,\r\n      data: { 'idr-action': 'select-tel-target', 'idr-id': x.id }\r\n    })).join('');\r\n\r\n    let detail = '';\r\n\r\n    \/* A. Telemetr\u00eda para sonda *\/\r\n    if (tel.target === 'probe') {\r\n      const modes = D.monitoring.map((m) => C.option({\r\n        title: t(m.nameK),\r\n        desc: t(m.descK),\r\n        on: tel.monitoring === m.id,\r\n        data: { 'idr-action': 'select-monitoring', 'idr-id': m.id }\r\n      })).join('');\r\n\r\n      detail =\r\n        '<div class=\"idr-block\">' + C.blockHead(t('tel.q2')) +\r\n          '<div class=\"idr-grid idr-grid--3\">' + modes + '<\/div>' +\r\n          (tel.monitoring\r\n            ? '<div style=\"margin-top:16px;display:flex;align-items:center;gap:14px;flex-wrap:wrap\">' +\r\n                '<span class=\"idr-label\">' + C.esc(t('tel.qtyLabel')) + '<\/span>' +\r\n                C.qty({ value: tel.probeQty, target: 'tel', key: 'probeQty', size: 'sm' }) +\r\n              '<\/div>'\r\n            : '') +\r\n          '<div style=\"margin-top:20px\">' +\r\n            C.info(t('tel.benefit'), D.variables.map((v) => L(v.name)), t('tel.varsTitle')) +\r\n          '<\/div>' +\r\n        '<\/div>';\r\n    }\r\n\r\n    \/* B. Carro de telesupervisi\u00f3n IDR-CS *\/\r\n    if (tel.target === 'cs-cart') {\r\n      const modes = D.cs.modes.map((m) => C.option({\r\n        title: t(m.nameK),\r\n        desc: t(m.descK),\r\n        on: tel.csMode === m.id,\r\n        data: { 'idr-action': 'select-cs-mode', 'idr-id': m.id }\r\n      })).join('');\r\n\r\n      const accessories = D.cs.accessories.map((acc) => C.row({\r\n        name: L(acc.name),\r\n        desc: L(acc.desc),\r\n        on: (tel.accessories[acc.id] || 0) > 0,\r\n        data: { 'idr-action': 'toggle-accessory', 'idr-id': acc.id },\r\n        qtyValue: tel.accessories[acc.id] || 1,\r\n        qtyTarget: 'telacc', qtyKey: acc.id\r\n      })).join('');\r\n\r\n      detail =\r\n        '<div class=\"idr-block\">' + C.blockHead(t('cs.title')) +\r\n          '<p class=\"idr-label\" style=\"margin-bottom:12px\">' + C.esc(t('cs.q')) + '<\/p>' +\r\n          '<div class=\"idr-grid idr-grid--2\">' + modes + '<\/div>' +\r\n          (tel.csMode\r\n            ? '<div style=\"margin-top:16px;display:flex;align-items:center;gap:14px;flex-wrap:wrap\">' +\r\n                '<span class=\"idr-label\">' + C.esc(t('cs.qtyLabel')) + '<\/span>' +\r\n                C.qty({ value: tel.csQty, target: 'tel', key: 'csQty', size: 'sm' }) +\r\n              '<\/div>'\r\n            : '') +\r\n        '<\/div>' +\r\n        '<div class=\"idr-block\">' + C.blockHead(t('cs.addons')) +\r\n          '<div class=\"idr-rows\">' + accessories + '<\/div>' +\r\n          C.note(t('cs.message')) +\r\n        '<\/div>';\r\n    }\r\n\r\n    return '<section class=\"idr-panel idr-anim\">' +\r\n      C.panelHead(t('tel.eyebrow'), t('tel.title'), t('tel.desc')) +\r\n      '<div class=\"idr-block\">' + C.blockHead(t('tel.q1')) +\r\n        '<div class=\"idr-grid idr-grid--2\">' + targetOpts + '<\/div>' +\r\n      '<\/div>' +\r\n      detail +\r\n      C.actions(\r\n        C.backLink(t('common.back'), 'close-module'),\r\n        '<button type=\"button\" class=\"idr-btn\" data-idr-action=\"close-module\">' +\r\n          C.esc(t('common.save')) + C.icon('check') + '<\/button>'\r\n      ) + '<\/section>';\r\n  }\r\n\r\n  \/* -- Paso 5: revisi\u00f3n y observaciones ---------------------------- *\/\r\n  function viewQuote() {\r\n    return '<section class=\"idr-panel idr-anim\">' +\r\n      C.panelHead(t('quote.eyebrow'), t('quote.title'), t('quote.desc')) +\r\n      '<div class=\"idr-block\">' + summaryHTML() + '<\/div>' +\r\n      '<div class=\"idr-block\">' + C.blockHead(t('notes.title'), t('notes.desc')) +\r\n        C.textarea({ name: 'notes', value: state.notes, placeholder: t('notes.placeholder') }) +\r\n      '<\/div>' +\r\n      C.actions(\r\n        C.backLink(t('common.back'), 'back'),\r\n        '<button type=\"button\" class=\"idr-btn idr-btn--accent idr-btn--lg\" data-idr-action=\"open-quote\"' +\r\n          (IDR_CART.count(state) ? '' : ' disabled') + '>' +\r\n          C.esc(t('cart.request')) + C.icon('arrowR') + '<\/button>'\r\n      ) + '<\/section>';\r\n  }\r\n\r\n  \/* Resumen agrupado, reutilizado en la vista 5 y en el modal. *\/\r\n  function summaryHTML() {\r\n    const items = IDR_CART.lines(state);\r\n    if (!items.length) return '<p class=\"idr-sub\">' + C.esc(t('cart.empty')) + '<\/p>';\r\n\r\n    const order = [];\r\n    const groups = {};\r\n    items.forEach((l) => {\r\n      if (!groups[l.group]) { groups[l.group] = []; order.push(l.group); }\r\n      groups[l.group].push(l);\r\n    });\r\n\r\n    \/* Bloque final de referencia t\u00e9cnica (no cotizable). *\/\r\n    const rods = IDR_CART.compatibleRods(state);\r\n    const rodsBlock = rods.length\r\n      ? '<div class=\"idr-sum__sec\">' +\r\n          '<h4 class=\"idr-sum__secT\">' + C.esc(t('quote.rodsRef')) + '<\/h4>' +\r\n          '<div class=\"idr-sum__line\">' +\r\n            '<span class=\"idr-sum__k\">' + C.esc(rods.join(' \u00b7 ')) +\r\n              '<small>' + C.esc(t('cart.rodsInfo')) + '<\/small><\/span>' +\r\n          '<\/div>' +\r\n        '<\/div>'\r\n      : '';\r\n\r\n    return order.map((g) =>\r\n      '<div class=\"idr-sum__sec\">' +\r\n        '<h4 class=\"idr-sum__secT\">' + C.esc(t(g)) + '<\/h4>' +\r\n        groups[g].map((l) =>\r\n          '<div class=\"idr-sum__line\">' +\r\n            '<span class=\"idr-sum__k\">' + C.esc(l.name) +\r\n              (l.meta ? '<small>' + C.esc(l.meta) + '<\/small>' : '') + '<\/span>' +\r\n            '<span class=\"idr-sum__v\">' +\r\n              (l.qty ? '\u00d7' + C.esc(l.qty) : '') +\r\n              (IDR_CONFIG.pricing.enabled && l.price != null\r\n                ? ' \u00b7 ' + C.esc(IDR_CART.money(l.price * (l.qty || 1)))\r\n                : '') +\r\n            '<\/span>' +\r\n          '<\/div>'\r\n        ).join('') +\r\n      '<\/div>'\r\n    ).join('') + rodsBlock;\r\n  }\r\n\r\n  \/* Mapa de vistas *\/\r\n  const VIEWS = {\r\n    operation: viewOperation,\r\n    rig: viewRig,\r\n    rods: viewRods,\r\n    equipment: viewEquipment,\r\n    quote: viewQuote\r\n  };\r\n  const MODULE_VIEWS = {\r\n    handling: viewHandling,\r\n    subhandling: viewSubHandling,\r\n    telemetry: viewTelemetry,\r\n    sampling: viewSampling\r\n  };\r\n\r\n  \/* ================================================================\r\n     7. REGISTRO DE SONDAJE (progreso)\r\n  ================================================================ *\/\r\n  function renderLog() {\r\n    const active = currentStep();\r\n    const head = '<div class=\"idr-log__head\"><span>' + C.esc(t('log.title')) + '<\/span>' +\r\n                 '<span>' + C.esc(t('log.depth')) + '<\/span><\/div>';\r\n\r\n    const steps = IDR_DATA.steps.map((s, i) => {\r\n      const done = stepDone(s.id);\r\n      const isActive = s.id === active;\r\n      const reachable = stepReachable(s.id) || done;\r\n      const value = stepValue(s.id);\r\n      return '<button type=\"button\" data-tone=\"' + s.tone + '\" ' +\r\n        'class=\"' + C.cx('idr-log__step', done && 'is-done', isActive && 'is-active') + '\" ' +\r\n        'data-idr-action=\"goto-step\" data-idr-step=\"' + s.id + '\"' +\r\n        (reachable ? '' : ' disabled') +\r\n        (isActive ? ' aria-current=\"step\"' : '') + '>' +\r\n        '<span class=\"idr-log__core\"><\/span>' +\r\n        '<span class=\"idr-log__body\">' +\r\n          '<span class=\"idr-log__n\">' + String(i + 1).padStart(2, '0') + '<\/span>' +\r\n          '<span class=\"idr-log__label\">' + C.esc(t(s.labelK)) + '<\/span>' +\r\n          (value ? '<span class=\"idr-log__value\">' + C.esc(value) + '<\/span>' : '') +\r\n        '<\/span><\/button>';\r\n    }).join('');\r\n\r\n    return head + '<div class=\"idr-log__col\">' + steps + '<\/div>';\r\n  }\r\n\r\n  \/* ================================================================\r\n     8. MODALES\r\n  ================================================================ *\/\r\n  function renderModals() {\r\n    const which = state.ui.modal;\r\n    if (!which) { els.modals.innerHTML = ''; return; }\r\n\r\n    let html = '';\r\n\r\n    \/* Advertencia final: se solicita la cotizaci\u00f3n sin el brazo IDR-ET *\/\r\n    if (which === 'et-final') {\r\n      html = C.modal({\r\n        id: 'et-final',\r\n        icon: 'alert',\r\n        title: t('etfinal.title'),\r\n        body: '<p style=\"font-size:15.5px;line-height:1.6\">' + C.esc(t('etfinal.body')) + '<\/p>',\r\n        foot:\r\n          '<button type=\"button\" class=\"idr-btn idr-btn--ghost\" data-idr-action=\"close-modal\">' +\r\n            C.esc(t('common.no')) + '<\/button>' +\r\n          '<button type=\"button\" class=\"idr-btn\" data-idr-action=\"et-final-yes\">' +\r\n            C.esc(t('etfinal.yes')) + C.icon('arrowR') + '<\/button>'\r\n      });\r\n    }\r\n\r\n    \/* Advertencia: se agrega equipo sin el brazo manipulador IDR-ET *\/\r\n    if (which === 'et-warning') {\r\n      html = C.modal({\r\n        id: 'et-warning',\r\n        icon: 'alert',\r\n        title: t('etwarn.title'),\r\n        body: '<p style=\"font-size:15.5px;line-height:1.6\">' + C.esc(t('etwarn.body')) + '<\/p>',\r\n        foot:\r\n          '<button type=\"button\" class=\"idr-btn idr-btn--ghost\" data-idr-action=\"close-modal\">' +\r\n            C.esc(t('common.no')) + '<\/button>' +\r\n          '<button type=\"button\" class=\"idr-btn\" data-idr-action=\"et-warning-yes\">' +\r\n            C.esc(t('common.yes')) + C.icon('arrowR') + '<\/button>'\r\n      });\r\n    }\r\n\r\n    \/* Advertencia: cama manual sin equipo ET *\/\r\n    if (which === 'manual-warning') {\r\n      html = C.modal({\r\n        id: 'manual-warning',\r\n        icon: 'alert',\r\n        title: t('warn.title'),\r\n        body: '<p style=\"font-size:15.5px;line-height:1.6\">' + C.esc(t('warn.body')) + '<\/p>',\r\n        foot:\r\n          '<button type=\"button\" class=\"idr-btn idr-btn--ghost\" data-idr-action=\"close-modal\">' +\r\n            C.esc(t('common.cancel')) + '<\/button>' +\r\n          '<button type=\"button\" class=\"idr-btn\" data-idr-action=\"confirm-manual\">' +\r\n            C.esc(t('common.continue')) + C.icon('arrowR') + '<\/button>'\r\n      });\r\n    }\r\n\r\n    \/* Confirmaci\u00f3n de vaciado *\/\r\n    if (which === 'reset') {\r\n      html = C.modal({\r\n        id: 'reset',\r\n        icon: 'alert',\r\n        title: t('cart.reset'),\r\n        body: '<p style=\"font-size:15.5px;line-height:1.6\">' + C.esc(t('cart.resetConfirm')) + '<\/p>',\r\n        foot:\r\n          '<button type=\"button\" class=\"idr-btn idr-btn--ghost\" data-idr-action=\"close-modal\">' +\r\n            C.esc(t('common.cancel')) + '<\/button>' +\r\n          '<button type=\"button\" class=\"idr-btn idr-btn--danger\" data-idr-action=\"confirm-reset\">' +\r\n            C.esc(t('cart.reset')) + '<\/button>'\r\n      });\r\n    }\r\n\r\n    \/* Solicitud de cotizaci\u00f3n *\/\r\n    if (which === 'quote') {\r\n      if (state.ui.sent) {\r\n        html = C.modal({\r\n          id: 'quote',\r\n          wide: true,\r\n          title: t('quote.doneTitle'),\r\n          body:\r\n            '<div class=\"idr-done\">' +\r\n              '<div class=\"idr-done__ring\">' + C.icon('check') + '<\/div>' +\r\n              '<h3 class=\"idr-h3\">' + C.esc(t('quote.doneTitle')) + '<\/h3>' +\r\n              '<p>' + C.esc(t('quote.doneBody')) + '<\/p>' +\r\n              '<div class=\"idr-code\">' + C.esc(t('quote.ref')) + ' \u00b7 ' + C.esc(state.ui.reference) + '<\/div>' +\r\n            '<\/div>',\r\n          foot:\r\n            '<button type=\"button\" class=\"idr-btn\" data-idr-action=\"close-modal\">' +\r\n              C.esc(t('common.close')) + '<\/button>'\r\n        });\r\n      } else {\r\n        const f = state.contact;\r\n        html = C.modal({\r\n          id: 'quote',\r\n          wide: true,\r\n          eyebrow: t('quote.eyebrow'),\r\n          title: t('quote.title'),\r\n          body:\r\n            '<p class=\"idr-sub\" style=\"margin-bottom:22px\">' + C.esc(t('quote.desc')) + '<\/p>' +\r\n            summaryHTML() +\r\n            '<div class=\"idr-sum__sec\">' +\r\n              '<h4 class=\"idr-sum__secT\">' + C.esc(t('quote.contact')) + '<\/h4>' +\r\n              '<div class=\"idr-form\">' +\r\n                C.field(t('quote.name'),    C.input({ name: 'contact.name', value: f.name })) +\r\n                C.field(t('quote.company'), C.input({ name: 'contact.company', value: f.company })) +\r\n                C.field(t('quote.email'),   C.input({ name: 'contact.email', value: f.email, type: 'email' })) +\r\n                C.field(t('quote.phone'),   C.input({ name: 'contact.phone', value: f.phone, type: 'tel' })) +\r\n                C.field(t('quote.country'), C.input({ name: 'contact.country', value: f.country })) +\r\n              '<\/div>' +\r\n            '<\/div>',\r\n          foot:\r\n            '<button type=\"button\" class=\"idr-btn idr-btn--ghost\" data-idr-action=\"edit-config\">' +\r\n              C.esc(t('quote.edit')) + '<\/button>' +\r\n            '<button type=\"button\" class=\"idr-btn idr-btn--accent\" data-idr-action=\"send-quote\"' +\r\n              (state.ui.sending ? ' disabled' : '') + '>' +\r\n              C.esc(state.ui.sending ? t('quote.sending') : t('quote.send')) + C.icon('send') + '<\/button>'\r\n        });\r\n      }\r\n    }\r\n\r\n    els.modals.innerHTML = html;\r\n    \/* La clase is-open se aplica en el siguiente frame para animar la entrada. *\/\r\n    requestAnimationFrame(() => {\r\n      const overlay = els.modals.querySelector('.idr-overlay');\r\n      if (!overlay) return;\r\n      overlay.classList.add('is-open');\r\n      const focusable = overlay.querySelector('input, button:not([data-idr-action=\"close-modal\"])');\r\n      if (focusable) focusable.focus({ preventScroll: true });\r\n    });\r\n  }\r\n\r\n  function openModal(id) { state.ui.modal = id; renderModals(); }\r\n  function closeModal()  {\r\n    state.ui.modal = null;\r\n    state.ui.pending = null;   \/* responder \"No\" descarta la selecci\u00f3n *\/\r\n    renderModals();\r\n  }\r\n\r\n  \/* ================================================================\r\n     9. RENDER GENERAL\r\n  ================================================================ *\/\r\n  function renderLangSelector() {\r\n    els.lang.innerHTML = IDR_LANGS.map((l) =>\r\n      '<button type=\"button\" class=\"' + C.cx('idr-lang__btn', state.lang === l.code && 'is-on') + '\" ' +\r\n      'lang=\"' + l.code + '\" title=\"' + C.esc(l.label) + '\" ' +\r\n      'aria-pressed=\"' + (state.lang === l.code) + '\" ' +\r\n      'data-idr-action=\"set-lang\" data-idr-value=\"' + l.code + '\">' + C.esc(l.short) + '<\/button>'\r\n    ).join('');\r\n  }\r\n\r\n  function renderView() {\r\n    const fn = state.view === 'module'\r\n      ? (MODULE_VIEWS[state.module] || viewEquipment)\r\n      : (VIEWS[state.view] || viewOperation);\r\n    els.view.innerHTML = fn();\r\n  }\r\n\r\n  function renderCart() { els.cart.innerHTML = IDR_CART.render(state); }\r\n\r\n  function render() {\r\n    applyStaticText();\r\n    renderLangSelector();\r\n    els.stage.hidden = !state.ui.started;\r\n    els.log.innerHTML = renderLog();\r\n    renderView();\r\n    renderCart();\r\n    persist();\r\n  }\r\n\r\n  \/* ================================================================\r\n     10. NAVEGACI\u00d3N\r\n  ================================================================ *\/\r\n  const FLOW = ['operation', 'rig', 'rods', 'equipment', 'quote'];\r\n\r\n  function goto(view, opts) {\r\n    state.view = view;\r\n    state.module = null;\r\n    render();\r\n    if (!opts || opts.scroll !== false) scrollToStage();\r\n  }\r\n\r\n  function next() {\r\n    const i = FLOW.indexOf(state.view);\r\n    if (i > -1 && i < FLOW.length - 1) goto(FLOW[i + 1]);\r\n  }\r\n\r\n  function back() {\r\n    if (state.view === 'module') { closeModule(); return; }\r\n    const i = FLOW.indexOf(state.view);\r\n    if (i > 0) goto(FLOW[i - 1]);\r\n  }\r\n\r\n  function openModule(id) {\r\n    if (availableModules().indexOf(id) === -1) { goto('equipment'); return; }\r\n    state.ui.started = true;\r\n    state.view = 'module';\r\n    state.module = id;\r\n    render();\r\n    scrollToStage();\r\n  }\r\n\r\n  \/* ----------------------------------------------------------------\r\n     Regla de negocio: no se puede agregar ning\u00fan equipo de\r\n     manipulaci\u00f3n sin haber elegido antes el brazo IDR-ET sin que el\r\n     usuario lo confirme. La acci\u00f3n queda \"en espera\" (state.ui.pending)\r\n     hasta que responda S\u00ed o No en la ventana de advertencia.\r\n  ---------------------------------------------------------------- *\/\r\n\r\n  \/** Aplica una selecci\u00f3n del sistema de manipulaci\u00f3n \/ muestras. *\/\r\n  function applyHandlingAction(action, id, value) {\r\n    const s = state;\r\n    switch (action) {\r\n      case 'select-storage': {\r\n        s.handling.storage = (s.handling.storage === id) ? null : id;\r\n        const st = s.handling.storage ? byId(IDR_DATA.handling.storage, s.handling.storage) : null;\r\n        if (!st || !st.allowAddons) { s.handling.ampliroll = false; s.handling.conveyor = false; }\r\n        s.handling.manualWarningAck = false;\r\n        break;\r\n      }\r\n      \/* El transportador no existe sin Ampliroll: al activarlo se\r\n         agrega el Ampliroll autom\u00e1ticamente y se avisa al usuario. *\/\r\n      case 'set-conveyor':\r\n        if (value === '1') {\r\n          s.handling.conveyor = true;\r\n          if (!s.handling.ampliroll) { s.handling.ampliroll = true; toast(t('hd.conveyorAuto')); }\r\n        } else {\r\n          s.handling.conveyor = false;\r\n        }\r\n        break;\r\n      \/* Quitar el Ampliroll arrastra al Transportador. *\/\r\n      case 'set-ampliroll':\r\n        if (value === '1') {\r\n          s.handling.ampliroll = true;\r\n        } else {\r\n          s.handling.ampliroll = false;\r\n          s.handling.conveyor = false;\r\n        }\r\n        break;\r\n      case 'toggle-cutting':\r\n        toggleCutting(id);\r\n        break;\r\n      case 'toggle-sampling':\r\n        if (s.sampling[id]) delete s.sampling[id];\r\n        else s.sampling[id] = 1;\r\n        break;\r\n    }\r\n  }\r\n\r\n  \/** \u00bfLa acci\u00f3n AGREGA equipo? Quitar nunca dispara la advertencia. *\/\r\n  function isAddingEquipment(action, id, value) {\r\n    const s = state;\r\n    switch (action) {\r\n      case 'select-storage':  return s.handling.storage !== id;\r\n      case 'set-conveyor':    return value === '1' && !s.handling.conveyor;\r\n      case 'set-ampliroll':   return value === '1' && !s.handling.ampliroll;\r\n      case 'toggle-cutting':  return !s.handling.cutting[id];\r\n      case 'toggle-sampling': return !s.sampling[id];\r\n      default: return false;\r\n    }\r\n  }\r\n\r\n  \/** Intercepta la acci\u00f3n y pide confirmaci\u00f3n si falta el IDR-ET. *\/\r\n  function guardEt(action, id, value) {\r\n    const s = state;\r\n    const needsWarning = s.operation === 'surface' &&\r\n                         !s.handling.et &&\r\n                         !s.handling.etWarningAck &&\r\n                         isAddingEquipment(action, id, value);\r\n    if (needsWarning) {\r\n      s.ui.pending = { action: action, id: id, value: value };\r\n      openModal('et-warning');\r\n      return;\r\n    }\r\n    applyHandlingAction(action, id, value);\r\n    renderKeepFocus();\r\n  }\r\n\r\n  \/** Antes de enviar: recordar que la configuraci\u00f3n no incluye el IDR-ET.\r\n      Se muestra SIEMPRE que falte el brazo, aunque ya se haya aceptado\r\n      la advertencia durante la configuraci\u00f3n. *\/\r\n  function needsFinalEtWarning() {\r\n    return state.operation === 'surface' && !state.handling.et && IDR_CART.count(state) > 0;\r\n  }\r\n\r\n  \/* Regla de negocio: cama manual sin IDR-ET requiere confirmaci\u00f3n. *\/\r\n  function needsManualWarning() {\r\n    const h = state.handling;\r\n    return state.operation === 'surface' && h.storage === 'bed-manual' &&\r\n           !h.et && !h.manualWarningAck && !h.etWarningAck;\r\n  }\r\n\r\n  function closeModule() {\r\n    if (state.module === 'handling' && needsManualWarning()) { openModal('manual-warning'); return; }\r\n    goto('equipment');\r\n  }\r\n\r\n  function scrollToStage() {\r\n    if (!els.stage || els.stage.hidden) return;\r\n    const top = els.stage.getBoundingClientRect().top + window.pageYOffset - 88;\r\n    try { window.scrollTo({ top: top, behavior: 'smooth' }); }\r\n    catch (e) { window.scrollTo(0, top); }\r\n  }\r\n\r\n  \/* ================================================================\r\n     11. TOAST\r\n  ================================================================ *\/\r\n  let toastTimer = null;\r\n  function toast(message) {\r\n    els.toast.textContent = message;\r\n    els.toast.classList.add('is-on');\r\n    clearTimeout(toastTimer);\r\n    toastTimer = setTimeout(() => els.toast.classList.remove('is-on'), 2600);\r\n  }\r\n\r\n  \/* ================================================================\r\n     12. INTEGRACIONES DE SALIDA\r\n     Cada canal se activa desde IDR_CONFIG.integrations sin tocar la UI.\r\n  ================================================================ *\/\r\n  const integrations = {\r\n    async submit(payload, text) {\r\n      const cfg = IDR_CONFIG.integrations;\r\n      const jobs = [];\r\n\r\n      \/* Correo electr\u00f3nico: endpoint propio (por ejemplo admin-ajax.php\r\n         o una REST route de WordPress que env\u00ede con wp_mail). *\/\r\n      if (cfg.email.enabled && cfg.email.endpoint) {\r\n        jobs.push(fetch(cfg.email.endpoint, {\r\n          method: 'POST',\r\n          headers: { 'Content-Type': 'application\/json' },\r\n          body: JSON.stringify({ to: cfg.email.to, subject: 'INNRA \u00b7 ' + payload.reference, text, payload })\r\n        }));\r\n      }\r\n\r\n      \/* CRM: endpoint REST con clave de API. *\/\r\n      if (cfg.crm.enabled && cfg.crm.endpoint) {\r\n        jobs.push(fetch(cfg.crm.endpoint, {\r\n          method: 'POST',\r\n          headers: {\r\n            'Content-Type': 'application\/json',\r\n            'Authorization': 'Bearer ' + cfg.crm.apiKey\r\n          },\r\n          body: JSON.stringify(payload)\r\n        }));\r\n      }\r\n\r\n      if (jobs.length) {\r\n        try { await Promise.allSettled(jobs); }\r\n        catch (e) { \/* la confirmaci\u00f3n se muestra igualmente: el folio queda registrado *\/ }\r\n      } else {\r\n        \/* Sin canales activos: el payload queda disponible en consola\r\n           y en window.IDR.lastPayload para pruebas de integraci\u00f3n. *\/\r\n        console.info('[IDR] Solicitud generada', payload);\r\n      }\r\n\r\n      \/* WhatsApp abre una pesta\u00f1a con el resumen precargado. *\/\r\n      if (cfg.whatsapp.enabled && cfg.whatsapp.phone) {\r\n        window.open('https:\/\/wa.me\/' + cfg.whatsapp.phone + '?text=' + encodeURIComponent(text), '_blank', 'noopener');\r\n      }\r\n\r\n      return true;\r\n    }\r\n  };\r\n\r\n  async function sendQuote() {\r\n    if (!IDR_CART.hasEquipment(state)) { toast(t('quote.needConfig')); return; }\r\n    const c = state.contact;\r\n    if (!c.name.trim() || !\/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$\/.test(c.email.trim())) {\r\n      toast(t('quote.needContact'));\r\n      return;\r\n    }\r\n\r\n    state.ui.sending = true;\r\n    renderModals();\r\n\r\n    const payload = IDR_CART.payload(state);\r\n    const text = IDR_CART.asText(state);\r\n    window.IDR.lastPayload = payload;\r\n\r\n    await integrations.submit(payload, text);\r\n\r\n    state.ui.sending = false;\r\n    state.ui.sent = true;\r\n    renderModals();\r\n    render();\r\n  }\r\n\r\n  \/* ================================================================\r\n     13. FOCO ESTABLE ENTRE RENDERS\r\n     Re-renderizamos por innerHTML: reponemos el foco en el control\r\n     equivalente para no romper la navegaci\u00f3n por teclado.\r\n  ================================================================ *\/\r\n  const FOCUS_KEYS = ['idrId', 'idrQtyTarget', 'idrQtyKey', 'idrValue', 'idrModule', 'idrStep'];\r\n\r\n  function focusSignature(el) {\r\n    if (!el || !el.dataset || !el.dataset.idrAction) return null;\r\n    const parts = ['[data-idr-action=\"' + el.dataset.idrAction + '\"]'];\r\n    FOCUS_KEYS.forEach((k) => {\r\n      if (el.dataset[k] != null) {\r\n        parts.push('[data-' + k.replace(\/([A-Z])\/g, '-$1').toLowerCase() + '=\"' + el.dataset[k] + '\"]');\r\n      }\r\n    });\r\n    return parts.join('');\r\n  }\r\n\r\n  function renderKeepFocus() {\r\n    const sel = focusSignature(document.activeElement);\r\n    render();\r\n    if (!sel) return;\r\n    const el = els.view.querySelector(sel) || els.cart.querySelector(sel);\r\n    if (el && !el.disabled) el.focus({ preventScroll: true });\r\n  }\r\n\r\n  \/* ================================================================\r\n     14. EVENTOS\r\n  ================================================================ *\/\r\n  ROOT.addEventListener('click', (event) => {\r\n    \/* Cierre del modal al pulsar el fondo *\/\r\n    if (event.target.hasAttribute && event.target.hasAttribute('data-idr-overlay')) {\r\n      closeModal();\r\n      return;\r\n    }\r\n\r\n    const el = event.target.closest('[data-idr-action]');\r\n    if (!el || !ROOT.contains(el)) return;\r\n\r\n    const action = el.dataset.idrAction;\r\n    const id     = el.dataset.idrId;\r\n    const value  = el.dataset.idrValue;\r\n    const s      = state;\r\n\r\n    switch (action) {\r\n\r\n      \/* -- Arranque y navegaci\u00f3n --------------------------------- *\/\r\n      case 'start':\r\n        s.ui.started = true;\r\n        if (!s.operation) s.view = 'operation';\r\n        render();\r\n        scrollToStage();\r\n        break;\r\n\r\n      case 'next': next(); break;\r\n      case 'back': back(); break;\r\n\r\n      case 'goto':\r\n        s.ui.started = true;\r\n        goto(el.dataset.idrView);\r\n        break;\r\n\r\n      case 'goto-step':\r\n        s.ui.started = true;\r\n        goto(el.dataset.idrStep);\r\n        break;\r\n\r\n      \/* -- Idioma -------------------------------------------------- *\/\r\n      case 'set-lang':\r\n        if (s.lang === value) break;\r\n        s.lang = value;\r\n        render();\r\n        renderModals();\r\n        break;\r\n\r\n      \/* -- Paso 1 --------------------------------------------------- *\/\r\n      case 'select-operation':\r\n        setOperation(id);\r\n        renderKeepFocus();\r\n        break;\r\n\r\n      \/* -- Paso 2 --------------------------------------------------- *\/\r\n      case 'select-rig': {\r\n        s.rig.model = (s.rig.model === id) ? null : id;\r\n        const def = s.rig.model ? byId(IDR_DATA.rigs[s.operation], s.rig.model) : null;\r\n        if (!def || !def.custom) s.rig.customModel = '';\r\n        render();\r\n        \/* El campo de modelo libre recibe el foco autom\u00e1ticamente *\/\r\n        const custom = els.view.querySelector('[data-idr-input=\"rig.customModel\"]');\r\n        if (custom) custom.focus({ preventScroll: true });\r\n        break;\r\n      }\r\n\r\n      \/* -- Cantidades ------------------------------------------------ *\/\r\n      case 'qty-inc':\r\n      case 'qty-dec': {\r\n        const target = el.dataset.idrQtyTarget;\r\n        const key = el.dataset.idrQtyKey;\r\n        setQty(target, key, getQty(target, key) + (action === 'qty-inc' ? 1 : -1));\r\n        renderKeepFocus();\r\n        break;\r\n      }\r\n\r\n      \/* -- Paso 4: m\u00f3dulos -------------------------------------------- *\/\r\n      case 'open-module': openModule(el.dataset.idrModule); break;\r\n      case 'close-module': closeModule(); break;\r\n\r\n      \/* -- Manipulaci\u00f3n de barras -------------------------------------- *\/\r\n      case 'toggle-et':\r\n        s.handling.et = !s.handling.et;\r\n        s.handling.manualWarningAck = false;\r\n        \/* Al quitar el brazo volvemos a advertir en la pr\u00f3xima selecci\u00f3n. *\/\r\n        if (!s.handling.et) s.handling.etWarningAck = false;\r\n        renderKeepFocus();\r\n        break;\r\n\r\n      \/* Estas acciones se validan contra la presencia del IDR-ET *\/\r\n      case 'select-storage':\r\n      case 'set-conveyor':\r\n      case 'set-ampliroll':\r\n      case 'toggle-cutting':\r\n      case 'toggle-sampling':\r\n        guardEt(action, id, value);\r\n        break;\r\n\r\n      \/* -- Manipulaci\u00f3n subterr\u00e1nea -------------------------------------- *\/\r\n      case 'toggle-arm':\r\n        s.subhandling.arm = !s.subhandling.arm;\r\n        renderKeepFocus();\r\n        break;\r\n\r\n      \/* -- Telemetr\u00eda ------------------------------------------------------ *\/\r\n      case 'select-tel-target':\r\n        if (s.telemetry.target === id) {\r\n          s.telemetry.target = null;\r\n        } else {\r\n          s.telemetry.target = id;\r\n          \/* Cambiar de objetivo limpia la rama anterior para no arrastrar\r\n             selecciones ocultas al carrito. *\/\r\n          if (id === 'probe') { s.telemetry.csMode = null; s.telemetry.accessories = {}; }\r\n          if (id === 'cs-cart') { s.telemetry.monitoring = null; }\r\n        }\r\n        renderKeepFocus();\r\n        break;\r\n\r\n      case 'select-monitoring':\r\n        s.telemetry.monitoring = (s.telemetry.monitoring === id) ? null : id;\r\n        renderKeepFocus();\r\n        break;\r\n\r\n      case 'select-cs-mode':\r\n        s.telemetry.csMode = (s.telemetry.csMode === id) ? null : id;\r\n        renderKeepFocus();\r\n        break;\r\n\r\n      case 'toggle-accessory':\r\n        if (s.telemetry.accessories[id]) delete s.telemetry.accessories[id];\r\n        else s.telemetry.accessories[id] = 1;\r\n        renderKeepFocus();\r\n        break;\r\n\r\n      \/* -- Carrito ---------------------------------------------------------- *\/\r\n      case 'toggle-cart':\r\n        s.ui.cartCollapsed = !s.ui.cartCollapsed;\r\n        renderCart();\r\n        break;\r\n\r\n      case 'remove-line':\r\n        removeLine(el.dataset.idrKey);\r\n        render();\r\n        toast(t('toast.removed'));\r\n        break;\r\n\r\n      case 'reset': openModal('reset'); break;\r\n\r\n      case 'confirm-reset':\r\n        resetAll(true);\r\n        closeModal();\r\n        render();\r\n        toast(t('toast.reset'));\r\n        break;\r\n\r\n      \/* -- Modales ----------------------------------------------------------- *\/\r\n      case 'et-warning-yes': {\r\n        const pending = s.ui.pending;\r\n        s.handling.etWarningAck = true;\r\n        s.ui.pending = null;\r\n        s.ui.modal = null;\r\n        if (pending) applyHandlingAction(pending.action, pending.id, pending.value);\r\n        renderModals();\r\n        render();\r\n        break;\r\n      }\r\n\r\n      case 'confirm-manual':\r\n        s.handling.manualWarningAck = true;\r\n        closeModal();\r\n        goto('equipment');\r\n        break;\r\n\r\n      case 'close-modal': closeModal(); break;\r\n\r\n      case 'open-quote':\r\n        if (!IDR_CART.count(s)) { toast(t('quote.needConfig')); break; }\r\n        if (s.ui.sent) { s.ui.sent = false; s.ui.reference = makeReference(); }\r\n        openModal(needsFinalEtWarning() ? 'et-final' : 'quote');\r\n        break;\r\n\r\n      case 'et-final-yes':\r\n        s.ui.modal = 'quote';\r\n        renderModals();\r\n        break;\r\n\r\n      case 'edit-config':\r\n        closeModal();\r\n        goto('equipment');\r\n        break;\r\n\r\n      case 'send-quote': sendQuote(); break;\r\n    }\r\n  });\r\n\r\n  \/* -- Entrada de texto y cantidades escritas ------------------------------- *\/\r\n  ROOT.addEventListener('input', (event) => {\r\n    const el = event.target.closest && event.target.closest('[data-idr-input]');\r\n    if (!el) return;\r\n    const name = el.dataset.idrInput;\r\n\r\n    if (name === 'qty') {\r\n      setQty(el.dataset.idrQtyTarget, el.dataset.idrQtyKey, el.value);\r\n    } else if (name === 'rig.customModel') {\r\n      state.rig.customModel = el.value;\r\n    } else if (name === 'notes') {\r\n      state.notes = el.value;\r\n    } else if (name.indexOf('contact.') === 0) {\r\n      state.contact[name.slice(8)] = el.value;\r\n    }\r\n\r\n    \/* Actualizaciones parciales: nunca re-renderizamos la vista mientras\r\n       el usuario escribe, para no perder el foco ni el cursor. *\/\r\n    renderCart();\r\n    els.log.innerHTML = renderLog();\r\n    const nextButton = els.view.querySelector('[data-idr-action=\"next\"]');\r\n    if (nextButton) nextButton.disabled = !stepDone(state.view);\r\n    persist();\r\n  });\r\n\r\n  \/* Al salir del campo de cantidad normalizamos el valor en pantalla. *\/\r\n  ROOT.addEventListener('change', (event) => {\r\n    const el = event.target.closest && event.target.closest('[data-idr-input=\"qty\"]');\r\n    if (!el) return;\r\n    render();\r\n  });\r\n\r\n  \/* -- Teclado --------------------------------------------------------------- *\/\r\n  document.addEventListener('keydown', (event) => {\r\n    if (event.key === 'Escape' && state.ui.modal) closeModal();\r\n  });\r\n\r\n  \/* ================================================================\r\n     15. API P\u00daBLICA\r\n     Expuesta para integraciones externas y depuraci\u00f3n desde consola.\r\n  ================================================================ *\/\r\n  window.IDR = {\r\n    version: '1.0.0',\r\n    t: t,\r\n    L: L,\r\n    config: IDR_CONFIG,\r\n    data: IDR_DATA,\r\n    get state() { return state; },\r\n    getPayload() { return IDR_CART.payload(state); },\r\n    setLanguage(code) { if (IDR_I18N[code]) { state.lang = code; render(); renderModals(); } },\r\n    goTo(view) { goto(view); },\r\n    reset() { resetAll(true); render(); },\r\n    render: render,\r\n    lastPayload: null\r\n  };\r\n\r\n  \/* ================================================================\r\n     16. ARRANQUE\r\n  ================================================================ *\/\r\n  render();\r\n})();\r\n<\/script>\r\n<\/body>\r\n<\/html>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p class=\"excerpt\">INNRA \u00b7 Configurador IDR INNRA Expertos en Ingenier\u00eda<\/p>","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"template-blank.php","meta":{"_seopress_robots_primary_cat":"","_seopress_titles_title":"","_seopress_titles_desc":"","_seopress_robots_index":"","footnotes":""},"class_list":["post-4470","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/www.innra.cl\/en\/wp-json\/wp\/v2\/pages\/4470","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.innra.cl\/en\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.innra.cl\/en\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.innra.cl\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.innra.cl\/en\/wp-json\/wp\/v2\/comments?post=4470"}],"version-history":[{"count":13,"href":"https:\/\/www.innra.cl\/en\/wp-json\/wp\/v2\/pages\/4470\/revisions"}],"predecessor-version":[{"id":4487,"href":"https:\/\/www.innra.cl\/en\/wp-json\/wp\/v2\/pages\/4470\/revisions\/4487"}],"wp:attachment":[{"href":"https:\/\/www.innra.cl\/en\/wp-json\/wp\/v2\/media?parent=4470"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}