/* ============================================================ AGROTHECA — Landing app ============================================================ */ const { useState: uS, useEffect: uE, useRef: uR } = React; const SECTIONS = window.AGRO_SECTIONS; const EXPLORE = SECTIONS.map((s) => ({ icon: s.icon, label: s.name })); const NAV = [ { label: 'Secciones', href: '#biblioteca' }, { label: 'El Lab', href: '#lab' }, { label: 'Servicios', href: '#servicios' }, { label: 'Acerca', href: '#acerca' }, { label: 'Contacto', href: '#contacto' }, ]; /* Servicios — consultoría & capacitación (your specialty) */ const SERVICES = [ { name: 'Consultoría en innovación AgTech & IA', icon: 'terminal', desc: 'Acompañamiento en proyectos de innovación para aplicar herramientas digitales e inteligencia artificial a tus procesos agrícolas.' }, { name: 'Análisis de datos', icon: 'database', desc: 'Convierte los datos de tu finca o negocio en información clara para decidir mejor: rendimiento, clima, suelo y más.' }, { name: 'Tu página web agro', icon: 'globe', desc: 'Creación de tu propia página web para tu negocio agrícola — a la medida, moderna y lista para crecer.' }, { name: 'Capacitación a equipos', icon: 'sprout', desc: 'Formaciones prácticas para que tu equipo adopte tecnología y datos en el día a día del campo.' }, ]; /* El Lab — tools/utilities. live: tool (modal) or link (page); rest "Próximamente" */ const LAB_TOOLS = [ { name: 'Simulador de Invernadero', kicker: 'Simulador 3D', icon: 'globe', link: 'simulador-invernadero.html', desc: 'Explora en 3D cómo el VPD afecta a un cultivo de tomate en un invernadero de alta tecnología.' }, { name: 'Simulador El Niño / La Niña', kicker: 'Simulador 3D', icon: 'cloud', link: 'simulador-enso.html', desc: 'Mueve el Pacífico en un globo interactivo y observa cómo El Niño y La Niña alteran lluvias y temperaturas.' }, { name: 'Calculadora de Siembra', kicker: 'Calculadora', icon: 'sprout', tool: 'siembra', desc: 'Densidad, distancia entre plantas y semilla necesaria por hectárea.' }, { name: 'Dosis de Fertilizante', kicker: 'Calculadora', icon: 'leaf', tool: 'fertilizante', desc: 'Cuánto aplicar según cultivo, área y análisis de suelo.' }, { name: 'Conversor de Unidades', kicker: 'Conversor', icon: 'database', tool: 'conversor', desc: 'Superficie, masa, rendimiento, temperatura y volumen del agro, al instante.' }, ]; const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{ "heroStyle": "cinematico", "accent": "#C27D38", "greenTheme": "Bosque", "headingFont": "serif", "autoplay": true } /*EDITMODE-END*/; /* Green characters live in js/theme.js (window.GREEN_THEMES) so they apply site-wide — landing + article — and persist across navigation. */ const GREEN_THEMES = window.GREEN_THEMES || { Bosque: { deep: '#1E3A2D' }, Oliva: { deep: '#2C3B22' }, Esmeralda: { deep: '#123F2D' }, Pino: { deep: '#163530' }, Salvia: { deep: '#38463A' } }; /* ---------------- HERO ---------------- */ function Hero({ t, showBg, menuOpen, setMenuOpen }) { const style = t.heroStyle; const titleRef = useParallax(14); const ADJ = ['oculta', 'digital', 'implacable']; const headline = style === 'editorial' ? La ciencia del agro. :

La ciencia del agro.

; const Intro =
01 El archivo {headline} AGROTECHA nació para entender el agro de una manera diferente, respondiendo las preguntas más curiosas de este mundo. Explorar Archivos
; const Theo = null; return (
{style === 'cinematico' && } {style === 'dividido' &&
}
{menuOpen &&
{NAV.map((n) => setMenuOpen(false)}>{n.label})}
}
Ciencia agrícola para mentes curiosas — un archivo vivo de biología, datos y campo.
{Intro} {style === 'editorial' && Metraje · germinación (ejemplo) }
Desliza para explorar
); } /* ---------------- SECTION 2 — Explore ---------------- */ function Explore({ t }) { const serif = t.headingFont === 'serif'; return (
[ 02 ] El Ecosistema {serif ? <>La ciencia invisible detrás de cada cosecha. : <>La ciencia invisible detrás de cada cosecha.} Cinco secciones temáticas. Dentro de cada una, artículos increíbles para los curiosos del agro. {EXPLORE.map((e, i) => {e.label} )}
); } /* ---------------- SECTION 3 — Library (dark) ---------------- */ const countLabel = (n) => n === 0 ? 'Próximamente' : n === 1 ? '1 artículo' : `${n} artículos`; function Library({ t, active, setActive }) { const [leafRef, leafIn] = useInView({ threshold: 0.01, rootMargin: '0px 0px -10% 0px' }); const leafParallax = useParallax(22); const setLeaf = React.useCallback((node) => { leafRef.current = node; leafParallax.current = node; }, []); const serif = t.headingFont === 'serif'; const s = SECTIONS[active]; return (
Sección 03 · Has llegado a la biblioteca La{' '} {'AGROTECHA'.split('').map((ch, i) => {ch} )} El corazón de todo. Aquí es donde puedes explorar nuestros artículos, notas e historias del agro, ordenados por temas. Cada publicación parte de información real y verificada, contada de forma sencilla, visual y fácil de disfrutar — para que aprendas sin que nadie te abrume con tecnicismos. Veraz · Información real y verificada Visual · Que se ve y se siente Curiosa · Preguntas antes que respuestas
{/* LEFT — the active SECTION and its articles */}
{countLabel(s.articles.length)}
Sección {String(active + 1).padStart(2, '0')} · {s.kicker}

{s.name}

{s.blurb}

{s.articles.length === 0 ? (
Próximamente

Estamos preparando los artículos de esta sección. Vuelve pronto.

) : (<> {s.articles.map((a, i) => {String(i + 1).padStart(2, '0')} {a.title} {a.read} )} Ver la sección completa )}
{/* RIGHT — the section index */}
Elige una sección {String(SECTIONS.length).padStart(2, '0')} secciones
{SECTIONS.map((c, i) => setActive(i)}> {String(i + 1).padStart(2, '0')} {c.name} {countLabel(c.articles.length)} )}
Agricultural science for curious minds AGROTECHA · MMXXVI
); } /* ---------------- SECTION 4 — El Lab (light) ---------------- */ function Lab({ t, onOpen }) { const serif = t.headingFont === 'serif'; return (
[ 04 ] El Lab Herramientas para llevar
la ciencia al campo.
Calculadoras, conversores y utilidades prácticas para tus decisiones diarias. Algunas ya están listas — el resto, muy pronto.
{LAB_TOOLS.map((tool, i) => { const isLink = !!tool.link; const isModal = !!tool.tool; const live = isLink || isModal; const as = isLink ? 'a' : (isModal ? 'button' : 'article'); const extra = isLink ? { href: tool.link } : (isModal ? { type: 'button', onClick: () => onOpen(tool.tool) } : {}); return (
{live ? 'Disponible' : 'Próximamente'}
{tool.kicker}

{tool.name}

{tool.desc}

{String(i + 1).padStart(2, '0')} {isLink ? 'Abrir' : (isModal ? 'Usar' : 'Avísame')}
); })}

¿Tienes una idea?

Cuéntanos qué herramienta te gustaría ver en El Lab.

Sugerir herramienta
); } /* ---------------- Tool modal + calculators ---------------- */ function TkField({ label, suffix, value, onChange, step, min, hint, placeholder }) { return ( ); } function TkResult({ k, v, unit, big }) { return (
{k} {v}{unit}
); } const fmt = (n, d = 0) => (isFinite(n) && n >= 0) ? n.toLocaleString('es', { maximumFractionDigits: d, minimumFractionDigits: 0 }) : '—'; function SiembraCalc() { const [area, setArea] = uS('1'); const [rowS, setRowS] = uS('75'); const [plantS, setPlantS] = uS('20'); const [seeds, setSeeds] = uS('1'); const [germ, setGerm] = uS('90'); const [pms, setPms] = uS('300'); const A = +area || 0, R = (+rowS || 0) / 100, P = (+plantS || 0) / 100; const S = +seeds || 1, G = (+germ || 100) / 100, PMS = +pms || 0; const plantasHa = (R > 0 && P > 0) ? (10000 / (R * P)) * S : 0; const poblacion = plantasHa * A; const semillasHa = G > 0 ? plantasHa / G : 0; const kgHa = semillasHa * PMS / 1e6; const kgTotal = kgHa * A; return ( <>

Estimación orientativa. La densidad real depende de la calidad del lote, el método de siembra y las condiciones del suelo.

); } function FertilizanteCalc() { const [nutr, setNutr] = uS('120'); const [conc, setConc] = uS('46'); const [area, setArea] = uS('1'); const [bulto, setBulto] = uS('50'); const N = +nutr || 0, C = (+conc || 0) / 100, A = +area || 0, B = +bulto || 0; const dosisHa = C > 0 ? N / C : 0; const total = dosisHa * A; const bultos = B > 0 ? total / B : 0; return ( <>

Cálculo del fertilizante para aportar el nutriente indicado. Ajusta según tu análisis de suelo y la recomendación agronómica del cultivo.

); } const CONV_CATS = { Superficie: { unit: 'ha', factors: { 'ha': 1, 'm²': 10000, 'acre': 2.471053, 'manzana': 1.43, 'fanega': 1.59 } }, Masa: { unit: 'kg', factors: { 'kg': 1, 't (ton)': 0.001, 'lb': 2.204623, 'qq (quintal)': 0.01, 'arroba': 0.08 } }, Rendimiento: { unit: 'kg/ha', factors: { 'kg/ha': 1, 't/ha': 0.001, 'lb/acre': 0.892179, 'qq/ha': 0.01 } }, Volumen: { unit: 'L', factors: { 'L': 1, 'm³': 0.001, 'galón US': 0.264172, 'galón UK': 0.219969 } }, }; function ConversorCalc() { const [cat, setCat] = uS('Superficie'); const [amount, setAmount] = uS('1'); const [from, setFrom] = uS('ha'); // temperatura is special-cased const isTemp = cat === 'Temperatura'; const C = CONV_CATS[cat]; uE(() => { if (isTemp) { setFrom('°C'); return; } setFrom(Object.keys(CONV_CATS[cat].factors)[0]); }, [cat]); const A = parseFloat(amount); let rows = []; if (isTemp) { const c = from === '°C' ? A : from === '°F' ? (A - 32) * 5 / 9 : A - 273.15; rows = [['°C', c], ['°F', c * 9 / 5 + 32], ['K', c + 273.15]]; } else { const base = isFinite(A) ? A / C.factors[from] : NaN; // to base unit rows = Object.keys(C.factors).map((u) => [u, base * C.factors[u]]); } const cats = [...Object.keys(CONV_CATS), 'Temperatura']; const units = isTemp ? ['°C', '°F', 'K'] : Object.keys(C.factors); return ( <>
{cats.map((k) => ( ))}
{rows.filter(([u]) => u !== from).map(([u, v]) => (
{u} {fmt(v, 3)}
))}

Conversiones aproximadas de uso común en el agro. La «manzana» y la «fanega» varían por país: aquí se usan valores promedio de referencia.

); } const TOOLS_META = { siembra: { title: 'Calculadora de Siembra', icon: 'sprout', Comp: SiembraCalc }, fertilizante: { title: 'Dosis de Fertilizante', icon: 'leaf', Comp: FertilizanteCalc }, conversor: { title: 'Conversor de Unidades', icon: 'database', Comp: ConversorCalc }, }; function ToolModal({ slug, onClose }) { uE(() => { if (!slug) return; const onKey = (e) => { if (e.key === 'Escape') onClose(); }; document.addEventListener('keydown', onKey); document.body.style.overflow = 'hidden'; return () => { document.removeEventListener('keydown', onKey); document.body.style.overflow = ''; }; }, [slug]); if (!slug) return null; const meta = TOOLS_META[slug]; const Comp = meta.Comp; return (
e.stopPropagation()} role="dialog" aria-modal="true">
El Lab · Calculadora

{meta.title}

); } /* ---------------- FEATURED — Último artículo (after Hero) ---------------- */ function Featured({ t }) { const serif = t.headingFont === 'serif'; // pick the most-recently-added real article (last section that has one with a link) const FEATURED = { sec: SECTIONS.find((s) => s.slug === 'expedientes-agricolas'), desc: 'De un sotobosque africano a fundar bolsas de valores y mover ferrocarriles: el expediente de la pequeña cereza que despertó a la humanidad.', }; const sec = FEATURED.sec; const art = sec.articles[0]; const href = art.link || `seccion.html?s=${sec.slug}`; return (
[ ★ ] Último artículo
Recién publicado
{sec.name} · {art.kicker} {art.title} {FEATURED.desc} Leer el artículo {art.read} de lectura
); } /* ---------------- ACERCA DE (brand + author) ---------------- */ function About({ t }) { const serif = t.headingFont === 'serif'; return (
[ 06 ] Acerca de Entender el agro de otra manera.
AGROTECHA nació para responder las preguntas más curiosas del mundo agrícola y contarlas de forma clara, visual y disfrutable. Reunimos biología, datos y campo en un archivo vivo de artículos, notas e historias — siempre a partir de información real y verificada. Creemos que la ciencia detrás de cada cosecha merece contarse bien: sin tecnicismos innecesarios, con rigor y con respeto por la curiosidad de quien lee. Ing. Agrónomo Alberth Ugalde Solano Fundador · AgTech & IA para la agricultura
); } /* ---------------- NEWSLETTER + CONTACTO (final) ---------------- */ function Newsletter({ t }) { const serif = t.headingFont === 'serif'; const [sent, setSent] = uS(false); const onSubmit = (e) => { e.preventDefault(); setSent(true); }; return (
[ Mantente cerca ]

No te pierdas el próximo expediente.

Recibe cada nuevo artículo de AGROTECHA en tu correo. Sin spam, solo ciencia del agro.

{sent ? (
¡Gracias! Te avisaremos cuando publiquemos algo nuevo.
) : (
)}
o escríbenos directo
Agricultural science for curious minds AGROTECHA · MMXXVI
); } /* ---------------- SECTION 5 — Servicios (dark, your specialty) ---------------- */ function Services({ t }) { const serif = t.headingFont === 'serif'; return (
[ 05 ] Servicios ¿Quieres llevar la IA y lo digital a tu campo? Más allá de los artículos, AGROTECHA ofrece consultoría y capacitación en la aplicación de herramientas digitales e inteligencia artificial para la agricultura. Te ayudamos a tomar mejores decisiones con tecnología y datos. Conversemos
{SERVICES.map((sv, i) => (

{sv.name}

{sv.desc}

))}
); } /* ---------------- Green theme picker (visible swatches) ---------------- */ function GreenPicker({ value, onChange }) { return (
VERDE
{Object.keys(GREEN_THEMES).map((name) => { const on = value === name; return ( ); })}
); } /* ---------------- Tweaks ---------------- */ function Panel({ t, setTweak }) { return ( setTweak('heroStyle', v)} /> setTweak('headingFont', v)} /> setTweak('accent', v)} /> setTweak('greenTheme', v)} /> setTweak('autoplay', v)} /> ); } /* ---------------- App ---------------- */ function App() { const defaults = { ...TWEAK_DEFAULTS, greenTheme: window.getGreenTheme && window.getGreenTheme() || TWEAK_DEFAULTS.greenTheme }; const [t, setTweak] = useTweaks(defaults); const [showBg, setShowBg] = uS(false); const [menuOpen, setMenuOpen] = uS(false); const [active, setActive] = uS(2); const [toolOpen, setToolOpen] = uS(null); uE(() => {const id = setTimeout(() => setShowBg(true), 1200);return () => clearTimeout(id);}, []); uE(() => {document.documentElement.style.setProperty('--gold', t.accent);}, [t.accent]); uE(() => { // apply + persist site-wide (article page reads the same localStorage key) if (window.setGreenTheme) window.setGreenTheme(t.greenTheme); }, [t.greenTheme]); uE(() => { if (!t.autoplay) return; const id = setInterval(() => setActive((p) => (p + 1) % SECTIONS.length), 4500); return () => clearInterval(id); }, [t.autoplay]); // Section-level entrance transitions (fade + rise as each scrolls into view). uE(() => { const secs = Array.from(document.querySelectorAll('main > section.sec, #root > section.sec, section.sec')); const targets = secs.filter((s) => !s.classList.contains('sec-featured') || true); targets.forEach((s) => s.classList.add('sec-anim')); const reveal = (s) => s.classList.add('sec-shown'); let io = null; const check = () => { const vh = window.innerHeight || document.documentElement.clientHeight; targets.forEach((s) => { if (s.classList.contains('sec-shown')) return; const r = s.getBoundingClientRect(); if (r.top < vh * 0.85 && r.bottom > 0) reveal(s); }); }; if (typeof IntersectionObserver !== 'undefined') { io = new IntersectionObserver((entries) => { entries.forEach((e) => { if (e.isIntersecting) reveal(e.target); }); }, { threshold: 0.12, rootMargin: '0px 0px -10% 0px' }); targets.forEach((s) => io.observe(s)); } window.addEventListener('scroll', check, { passive: true }); window.addEventListener('resize', check); requestAnimationFrame(check); const t1 = setTimeout(check, 400); return () => { if (io) io.disconnect(); window.removeEventListener('scroll', check); window.removeEventListener('resize', check); clearTimeout(t1); }; }, []); return ( <> setToolOpen(null)} /> ); } ReactDOM.createRoot(document.getElementById('root')).render();