{"id":13341,"date":"2026-06-01T10:09:35","date_gmt":"2026-06-01T08:09:35","guid":{"rendered":"https:\/\/geopard.tech\/?page_id=13341"},"modified":"2026-06-01T19:39:18","modified_gmt":"2026-06-01T17:39:18","slug":"explorador-agroclimatico","status":"publish","type":"page","link":"https:\/\/geopard.tech\/es\/agroclimate-explorer\/","title":{"rendered":"Explorador del futuro agroclim\u00e1tico"},"content":{"rendered":"\n<!--\n  Tool-specific hero for \/agroclimate-explorer\/ \u2014 pasted INLINE on page 13341\n  (not a shared reusable block, since block 13098 was written for field-data-\n  explorer and its copy doesn't match this tool).\n\n  Pattern mirrors tools\/guidance-lines\/hero.html: green-cream radial-gradient\n  backdrop, eyebrow pill, headline with orange\u2192green accent, 5 feature chips\n  with the last one being the \"data stays in your browser\" trust chip.\n\n  If page content gets reset and you need to rebuild it: paste this in a\n  Custom HTML block as the FIRST block, build\/embed_only.html as the SECOND,\n  bottom.html as the THIRD.\n-->\n<div class=\"gp-lead\">\n<style>\n\/* Rule \u00a77 \u2014 hide the geopard child theme's bare <h1> page title so the\n   tool's custom hero is the first thing visible on the page. *\/\n#simple_page > .container > h1{display:none !important}\n.gp-lead{font-family:var(--wp--preset--font-family--nunito,\"Nunito\",system-ui,-apple-system,Segoe UI,sans-serif);color:#212121;max-width:1700px;margin:0 auto;padding:0 8px}\n.gp-lead h1{font-family:var(--wp--preset--font-family--poppins,\"Poppins\",system-ui,sans-serif);font-weight:800;color:#0e3a1c;line-height:1.15;letter-spacing:-0.018em;margin:0 auto 12px;font-size:1.95rem;max-width:920px}\n.gp-lead h1 .accent{background:linear-gradient(120deg,#f76a0c 0%,#15701e 100%);-webkit-background-clip:text;background-clip:text;color:transparent}\n.gp-lead .gpl-hero{position:relative;background:radial-gradient(900px 360px at 0% -10%,rgba(74,222,128,0.18),transparent 60%),radial-gradient(700px 320px at 105% 110%,rgba(247,106,12,0.10),transparent 60%),linear-gradient(180deg,#fbfcf6 0%,#f1f6e8 100%);border:1px solid #d6e2c5;border-radius:20px;padding:28px 36px 24px;margin:6px 0 24px;overflow:hidden;box-shadow:0 12px 32px rgba(20,83,40,0.06),0 0 0 1px rgba(255,255,255,0.55) inset;text-align:center}\n.gp-lead .gpl-hero-eyebrow{display:inline-flex;align-items:center;gap:8px;font-family:var(--wp--preset--font-family--poppins,\"Poppins\",sans-serif);font-size:.74rem;font-weight:700;letter-spacing:.14em;text-transform:uppercase;color:#15701e;background:#fff;border:1px solid #cfe4d4;border-radius:999px;padding:6px 12px;margin:0 0 14px;box-shadow:0 1px 2px rgba(20,83,40,0.05)}\n.gp-lead .gpl-hero-eyebrow::before{content:\"\";width:7px;height:7px;border-radius:50%;background:#22c55e;box-shadow:0 0 0 3px rgba(34,197,94,0.22)}\n.gp-lead .gpl-hero .lede{font-size:1.02rem;line-height:1.55;color:#243024;margin:0 auto;max-width:820px;font-weight:400}\n.gp-lead .gpl-hero .lede strong{color:#0e3a1c;font-weight:600}\n.gp-lead .gpl-hero-features{display:flex;flex-wrap:wrap;gap:8px;margin:18px 0 0;justify-content:center}\n.gp-lead .gpl-hero-feat{display:inline-flex;align-items:center;gap:8px;background:#fff;border:1px solid #d8e6d8;color:#1c4a2a;font-size:.86rem;font-weight:600;padding:6px 12px;border-radius:999px;font-family:var(--wp--preset--font-family--poppins,\"Poppins\",sans-serif);box-shadow:0 1px 2px rgba(20,83,40,0.04)}\n.gp-lead .gpl-hero-feat::before{content:\"\\2713\";width:16px;height:16px;flex:0 0 16px;background:#22c55e;color:#fff;border-radius:50%;font-size:10px;font-weight:800;line-height:16px;text-align:center}\n.gp-lead .gpl-hero-feat.is-trust{background:transparent;border-color:transparent;color:#3a4a3a;padding:6px 4px;box-shadow:none}\n.gp-lead .gpl-hero-feat.is-trust::before{content:\"\\1F512\";width:auto;height:auto;background:transparent;color:#15701e;font-size:13px;line-height:1}\n@media (max-width:760px){\n  .gp-lead .gpl-hero{padding:20px 18px;margin:4px 0 16px;border-radius:16px}\n  .gp-lead .gpl-hero-eyebrow{font-size:.66rem;letter-spacing:.12em;padding:5px 10px;margin-bottom:10px}\n  .gp-lead h1{font-size:1.35rem;line-height:1.2;margin-bottom:8px}\n  .gp-lead .gpl-hero .lede{font-size:.95rem;line-height:1.5}\n  .gp-lead .gpl-hero-features{gap:6px;margin-top:12px}\n  .gp-lead .gpl-hero-feat{font-size:.78rem;padding:5px 10px}\n}\n<\/style>\n<section class=\"gpl-hero\">\n  <div class=\"gpl-hero-eyebrow\">Agroclimate Future Explorer \u00b7 Free \u00b7 No signup<\/div>\n  <h1>Where your farmland is going \u2014 by <span class=\"accent\">2030, 2050, 2060<\/span>.<\/h1>\n  <p class=\"lede\">Pick a country and agroclimatic zone. Scrub the decade. Test trade scenarios. See <strong>which crops gain suitability, which lose it<\/strong>, how <strong>logistics, input costs, land price, and investment access<\/strong> reshape margin, and which zones land funds and operators should be watching for the next 20 years.<\/p>\n  <div class=\"gpl-hero-features\">\n    <span class=\"gpl-hero-feat\">62 zones across 36 countries<\/span>\n    <span class=\"gpl-hero-feat\">Decade snapshots 2020-2060<\/span>\n    <span class=\"gpl-hero-feat\">3 trade scenarios<\/span>\n    <span class=\"gpl-hero-feat\">Land price + yield gap + investment access<\/span>\n    <span class=\"gpl-hero-feat\">PDF export<\/span>\n  <\/div>\n<\/section>\n<\/div>\n\n\n\n<div class=\"gpy-wrap\" id=\"gpy-acx-root\" data-theme=\"light\">\n<style>\n\/* GeoPard brand alignment:\n   - Primary green #15701e (links, accents)  \u00b7 dark green #145328 (headings)\n   - Accent orange #f76a0c (CTAs, active states)\n   - Cream #fafbf4 (page background)  \u00b7 text #212121 \/ dim #4c6066\n   - Borders #e8ebe2 \/ softer #cfe4d4 (matches hero+bottom)\n   - Fonts: Nunito body, Poppins heads, DM Mono numerics \u2014 pulled from\n     WordPress preset tokens so the embed inherits site typography. *\/\n.gpy-wrap{all:initial;display:block;font-family:var(--wp--preset--font-family--nunito,\"Nunito\",\"Segoe UI\",system-ui,-apple-system,sans-serif);font-size:13px;line-height:1.5;border-radius:14px;overflow:hidden;max-width:100%;color:#212121}\n.gpy-wrap *,.gpy-wrap *::before,.gpy-wrap *::after{box-sizing:border-box}\n\n.gpy-wrap[data-theme=\"light\"]{\n  --bg:#f4f7f4;--panel:#ffffff;--panel2:#eef3ee;--line:#e2e8e2;--line2:#cfe4d4;\n  --ink:#212121;--ink-dim:#4c6066;\n  --accent:#15701e;--accent-h:#1b7a2a;--accent-soft:#bfe2c8;\n  --accent-dark:#145328;--accent-pale:#eef6e8;\n  --orange:#f76a0c;--orange-h:#ff7d24;\n  --warn:#c2410c;--warn-soft:#fed7aa;--warn-bg:#fff7ed;\n  --win:#15701e;--lose:#c8232c;\n  --cta-bg:linear-gradient(145deg,#eef6e8 0%,#dde9d4 100%);\n  --cta-border:#bfe2c8;--cta-title:#145328;--cta-text:#1c4a2a;\n  --shadow:0 6px 22px rgba(20,83,40,.07);--shadow-sm:0 1px 3px rgba(20,83,40,.05);\n  color:var(--ink);background:var(--bg);border:1px solid var(--line);box-shadow:var(--shadow);\n}\n.gpy-wrap[data-theme=\"dark\"]{\n  --bg:#0e1612;--panel:#152019;--panel2:#1c2a23;--line:#293b32;--line2:#3a4f43;\n  --ink:#e7edd6;--ink-dim:#93a08a;\n  --accent:#22c55e;--accent-h:#34d169;--accent-soft:#86c9a8;\n  --accent-dark:#15701e;--accent-pale:#1c2a23;\n  --orange:#f76a0c;--orange-h:#ff7d24;\n  --warn:#fb923c;--warn-soft:#7c2d12;--warn-bg:#1c1410;\n  --win:#22c55e;--lose:#f87171;\n  --cta-bg:linear-gradient(145deg,#0f2a1f 0%,#112e25 100%);\n  --cta-border:#1e5e3d;--cta-title:#86efac;--cta-text:#d1fae5;\n  --shadow:0 10px 40px rgba(0,0,0,.4);--shadow-sm:0 2px 6px rgba(0,0,0,.25);\n  color:var(--ink);background:var(--bg);border:1px solid var(--line);box-shadow:var(--shadow);\n}\n\n.gpy-wrap .gpy-card{background:var(--panel);border:1px solid var(--line);border-radius:10px;padding:12px 14px;box-shadow:var(--shadow-sm)}\n.gpy-wrap .gpy-card h3{font-size:10px;text-transform:uppercase;letter-spacing:.13em;color:var(--accent-dark);margin:0 0 9px 0;font-weight:700;font-family:var(--wp--preset--font-family--poppins,\"Poppins\",system-ui,sans-serif)}\n.gpy-wrap .acx-card-scope{color:var(--orange);font-weight:800;text-transform:none;letter-spacing:.02em}\n\n\/* Filter bar \u2014 global controls condensed into clickable pills with popovers.\n   Sticky at top so the user can change region\/decade\/scenario while scrolling. *\/\n.gpy-wrap .acx-filterbar{position:sticky;top:0;z-index:50;display:flex;align-items:center;gap:6px;padding:8px 14px;margin:0 0 6px;background:rgba(244,247,244,0.96);backdrop-filter:saturate(140%) blur(8px);border-bottom:1px solid var(--line);flex-wrap:wrap}\n.gpy-wrap .acx-filterbar-label{display:inline-flex;align-items:center;gap:6px;font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",monospace);font-size:9.5px;text-transform:uppercase;letter-spacing:.14em;color:var(--ink-dim);font-weight:700;margin-right:2px}\n.gpy-wrap .acx-filter-pill{display:inline-flex;align-items:center;gap:6px;background:#fff;border:1px solid var(--line);color:var(--ink);padding:5px 10px;border-radius:7px;font-family:var(--wp--preset--font-family--nunito,\"Nunito\",sans-serif);font-size:11.5px;cursor:pointer;transition:all .12s;white-space:nowrap;position:relative}\n.gpy-wrap .acx-filter-pill:hover{border-color:var(--accent);background:var(--bg)}\n.gpy-wrap .acx-filter-pill[aria-expanded=\"true\"]{border-color:var(--accent-dark);background:var(--accent-pale);box-shadow:0 0 0 2px rgba(20,83,40,0.08)}\n.gpy-wrap .acx-filter-pill .pill-label{font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",monospace);font-size:9.5px;text-transform:uppercase;letter-spacing:.1em;color:var(--ink-dim);font-weight:700}\n.gpy-wrap .acx-filter-pill .pill-value{font-weight:700;color:var(--ink);letter-spacing:-.01em}\n.gpy-wrap .acx-filter-pill .pill-chev{color:var(--ink-dim);font-size:9px;margin-left:2px}\n.gpy-wrap .acx-filter-pill.compact .pill-value{font-size:11px}\n\n\/* Filter popovers *\/\n.gpy-wrap .acx-filter-popup{position:absolute;top:calc(100% + 6px);background:#fff;border:1px solid var(--line);border-radius:9px;box-shadow:0 12px 32px rgba(20,83,40,0.16);padding:8px;z-index:60;min-width:200px;max-width:380px}\n.gpy-wrap .acx-filter-popup[hidden]{display:none}\n.gpy-wrap .acx-filter-popup[data-popup=\"region\"]{min-width:320px;max-width:380px;max-height:420px;overflow:hidden;display:flex;flex-direction:column}\n.gpy-wrap .acx-popup-search{width:100%;padding:7px 10px;border:1px solid var(--line);border-radius:6px;font-family:inherit;font-size:12px;color:var(--ink);outline:none;margin-bottom:6px;background:var(--bg)}\n.gpy-wrap .acx-popup-search:focus{border-color:var(--accent)}\n.gpy-wrap .acx-popup-list{overflow-y:auto;max-height:340px}\n.gpy-wrap .acx-popup-region-group{margin-bottom:8px}\n.gpy-wrap .acx-popup-region-group-h{font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",monospace);font-size:9px;text-transform:uppercase;letter-spacing:.14em;color:var(--ink-dim);font-weight:700;padding:5px 8px 3px;border-top:1px solid var(--line)}\n.gpy-wrap .acx-popup-region-group:first-child .acx-popup-region-group-h{border-top:0}\n.gpy-wrap .acx-popup-region-item{display:block;width:100%;text-align:left;background:transparent;border:0;padding:6px 10px;font-family:inherit;font-size:12px;color:var(--ink);border-radius:5px;cursor:pointer;transition:background .1s}\n.gpy-wrap .acx-popup-region-item:hover{background:var(--bg);color:var(--accent-dark)}\n.gpy-wrap .acx-popup-region-item.on{background:var(--accent-dark);color:#fff;font-weight:700}\n.gpy-wrap .acx-popup-region-item .cc{display:inline-block;width:24px;color:var(--ink-dim);font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",monospace);font-size:10px;font-weight:700}\n.gpy-wrap .acx-popup-region-item.on .cc{color:rgba(255,255,255,0.7)}\n.gpy-wrap .acx-popup-grid{display:grid;grid-template-columns:1fr 1fr;gap:4px}\n.gpy-wrap .acx-popup-opt{background:var(--bg);border:1px solid var(--line);color:var(--ink);padding:8px 10px;font-family:inherit;font-size:13px;font-weight:700;border-radius:6px;cursor:pointer;transition:all .12s;font-variant-numeric:tabular-nums}\n.gpy-wrap .acx-popup-opt:hover{border-color:var(--accent);background:#fff}\n.gpy-wrap .acx-popup-opt[aria-selected=\"true\"]{background:var(--accent-dark);color:#fff;border-color:var(--accent-dark)}\n.gpy-wrap .acx-popup-row{display:flex;flex-direction:column;width:100%;text-align:left;background:transparent;border:0;padding:8px 10px;border-radius:6px;cursor:pointer;gap:2px;transition:background .1s;border:1px solid transparent;font-family:inherit}\n.gpy-wrap .acx-popup-row:hover{background:var(--bg)}\n.gpy-wrap .acx-popup-row[aria-selected=\"true\"]{background:var(--accent-pale);border-color:var(--accent-soft)}\n.gpy-wrap .acx-popup-row .row-title{font-weight:700;color:var(--ink);font-size:13px;letter-spacing:-.01em}\n.gpy-wrap .acx-popup-row .row-hint{font-size:11px;color:var(--ink-dim);font-weight:500;line-height:1.35}\n@media (max-width:820px){\n  .gpy-wrap .acx-filterbar{padding:7px 12px;gap:5px}\n  .gpy-wrap .acx-filter-pill{padding:7px 10px;min-height:32px}\n  .gpy-wrap .acx-filter-popup{left:0 !important;right:0 !important;max-width:none;width:auto;margin:0 8px}\n}\n\n\/* Scope legend \u2014 single italic line under the locale banner. No box, no\n   callout. Modern, editorial. Colored tokens carry the scope vocabulary\n   (global \/ map-only \/ crop-focus) that's reinforced below. *\/\n.gpy-wrap .acx-scope-legend{margin:8px 18px 0;padding:0;font-size:11.5px;line-height:1.6;color:var(--ink-dim);font-family:var(--wp--preset--font-family--nunito,\"Nunito\",system-ui,sans-serif);font-style:italic}\n.gpy-wrap .acx-scope-legend .lg-tok{font-style:normal;font-weight:700;color:var(--ink);padding:1px 6px;border-radius:3px;letter-spacing:.01em}\n.gpy-wrap .acx-scope-legend .lg-tok.global{background:var(--accent-dark);color:#fff}\n.gpy-wrap .acx-scope-legend .lg-tok.local{background:var(--line);color:var(--accent-dark)}\n.gpy-wrap .acx-scope-legend .lg-tok.focus{background:var(--orange);color:#fff}\n\n\/* Inline scope dots \u2014 used on individual control headings. Just a tiny\n   colored marker + uppercase label, no pill, no border. Much lighter\n   than the previous chip pattern. *\/\n.gpy-wrap .acx-scope-dot{display:inline-flex;align-items:center;gap:5px;font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",\"SF Mono\",ui-monospace,monospace);font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:.12em;margin-right:8px;vertical-align:baseline}\n.gpy-wrap .acx-scope-dot::before{content:\"\";width:5px;height:5px;border-radius:50%;flex:none}\n.gpy-wrap .acx-scope-dot.global{color:var(--accent-dark)}\n.gpy-wrap .acx-scope-dot.global::before{background:var(--accent-dark)}\n.gpy-wrap .acx-scope-dot.local{color:var(--ink-dim)}\n.gpy-wrap .acx-scope-dot.local::before{background:var(--ink-dim)}\n.gpy-wrap .acx-scope-dot.focus{color:var(--orange)}\n.gpy-wrap .acx-scope-dot.focus::before{background:var(--orange)}\n\n\/* Footer secondary actions \u2014 PDF export + share *\/\n.gpy-wrap .acx-foot-actions{display:flex;justify-content:center;gap:10px;align-items:center;flex-wrap:wrap;margin:4px 0 2px}\n.gpy-wrap .acx-foot-btn{background:transparent;border:1px solid var(--line);color:var(--ink);padding:6px 14px;border-radius:999px;font-family:var(--wp--preset--font-family--poppins,\"Poppins\",system-ui,sans-serif);font-size:12px;font-weight:600;cursor:pointer;transition:all .15s;display:inline-flex;align-items:center;gap:6px;text-decoration:none}\n.gpy-wrap .acx-foot-btn:hover{border-color:var(--accent);color:var(--accent);background:var(--bg)}\n\n\/* Internal-link directory \u2014 discreet, editorial. Two rows (country \/ crop). *\/\n.gpy-wrap .acx-seo-dir{margin:6px 0 10px;padding:10px 14px;background:var(--panel);border:1px solid var(--line);border-radius:9px;display:flex;flex-direction:column;gap:6px}\n.gpy-wrap .acx-seo-dir-row{display:flex;flex-wrap:wrap;align-items:baseline;gap:6px 10px;line-height:1.55}\n.gpy-wrap .acx-seo-dir-h{font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",\"SF Mono\",ui-monospace,monospace);font-size:9.5px;text-transform:uppercase;letter-spacing:.14em;color:var(--ink-dim);font-weight:700;flex:0 0 130px;padding-top:1px}\n.gpy-wrap .acx-seo-dir a{font-family:var(--wp--preset--font-family--nunito,\"Nunito\",sans-serif);font-size:12.5px;font-weight:600;color:var(--accent-dark);text-decoration:none;padding:2px 4px;border-radius:4px;transition:background .12s,color .12s}\n.gpy-wrap .acx-seo-dir a:hover{background:var(--accent-pale);color:var(--accent)}\n@media (max-width:820px){\n  .gpy-wrap .acx-seo-dir{padding:9px 12px}\n  .gpy-wrap .acx-seo-dir-h{flex:0 0 100%;padding-top:0;margin-bottom:2px}\n  .gpy-wrap .acx-seo-dir a{font-size:13.5px;padding:6px 8px;background:var(--bg);border:1px solid var(--line)}\n}\n\n\/* Always-visible copyright line *\/\n.gpy-wrap .acx-copyright{font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",\"SF Mono\",ui-monospace,monospace);font-size:10px;color:var(--ink-dim);text-align:center;letter-spacing:.04em;padding:8px 0 2px;border-top:1px dashed var(--line2);margin-top:4px;line-height:1.55}\n.gpy-wrap .acx-copyright a{color:var(--accent);text-decoration:none}\n.gpy-wrap .acx-copyright a:hover{text-decoration:underline}\n\n\/* Print-only elements \u2014 hidden in screen mode *\/\n.gpy-wrap .acx-print-only{display:none}\n.gpy-wrap .acx-print-watermark{display:none}\n\n\/* PRINT STYLES \u2014 clean, paginated, GeoPard-branded report *\/\n@media print {\n  @page { size: A4; margin: 12mm 12mm 18mm 12mm; }\n  body { background: #fff !important; }\n  \/* Hide interactive controls + chrome *\/\n  .gpy-wrap .gpy-acx-globals,\n  .gpy-wrap .acx-mapcard,\n  .gpy-wrap .acx-top-picks,\n  .gpy-wrap .acx-chart-tip,\n  .gpy-wrap .gpy-acx-cta,\n  .gpy-wrap .acx-foot-actions,\n  .gpy-wrap .acx-seo-dir,\n  .gpy-wrap .gpy-acx-xlink,\n  .gpy-wrap .gpy-acx-play,\n  .leaflet-control,\n  .acx-card-scope::after { display: none !important; }\n  \/* Show print-only elements *\/\n  .gpy-wrap .acx-print-only { display: block !important; }\n  .gpy-wrap .acx-print-watermark {\n    display: block !important;\n    position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%) rotate(-32deg);\n    font-family: var(--wp--preset--font-family--poppins,\"Poppins\",sans-serif);\n    font-size: 110px; font-weight: 900; color: rgba(20, 83, 40, 0.06);\n    letter-spacing: -0.04em; pointer-events: none; z-index: 0; white-space: nowrap;\n  }\n  .gpy-wrap .acx-print-header {\n    display: flex !important; justify-content: space-between; align-items: baseline;\n    padding: 0 0 8px; margin: 0 0 14px; border-bottom: 2px solid #145328;\n    font-family: var(--wp--preset--font-family--poppins,\"Poppins\",sans-serif);\n  }\n  .gpy-wrap .acx-print-header .ph-brand { display: inline-flex; align-items: center; gap: 8px; }\n  .gpy-wrap .acx-print-header .ph-logo { width: 26px; height: 26px; flex: none; }\n  .gpy-wrap .acx-print-header .ph-word { font-weight: 800; font-size: 18px; color: #145328; letter-spacing: -0.015em; }\n  .gpy-wrap .acx-print-header .ph-tool { font-weight: 500; color: #4c6066; font-size: 11px; text-transform: uppercase; letter-spacing: 0.1em; }\n  .gpy-wrap .acx-print-meta {\n    display: flex !important; justify-content: space-between; font-size: 11px;\n    font-family: var(--wp--preset--font-family--dm-mono,\"DM Mono\",monospace);\n    color: #4c6066; padding: 0 0 12px; margin: 0 0 14px; border-bottom: 1px solid #e8ebe2;\n  }\n  .gpy-wrap .acx-print-meta strong { color: #212121; font-weight: 700; }\n  .gpy-wrap .acx-print-footer {\n    display: block !important;\n    position: running(footer);\n    font-family: var(--wp--preset--font-family--dm-mono,\"DM Mono\",monospace);\n    font-size: 9px; color: #4c6066; text-align: center;\n    padding-top: 6px; border-top: 1px dashed #cfe4d4; margin-top: 16px; letter-spacing: 0.04em;\n  }\n  .gpy-wrap .acx-print-footer strong { color: #145328; }\n  \/* Loosen body grid for print so cards stack vertically *\/\n  .gpy-wrap .gpy-acx-body { grid-template-columns: 1fr !important; gap: 10px !important; padding: 0 !important; }\n  .gpy-wrap .gpy-card { break-inside: avoid; box-shadow: none !important; border-radius: 6px; padding: 12px 14px; }\n  .gpy-wrap .acx-hero-stats { grid-template-columns: 1fr 1fr 1fr !important; }\n  .gpy-wrap .acx-hero-stat { padding: 8px 10px; }\n  .gpy-wrap .gpy-acx-locale { padding: 0 !important; margin: 0 !important; }\n  \/* Hide WP theme chrome (header, nav, footer, sidebars) but keep the\n     embed visible. We CANNOT use `body > *:not(.gp-lead)` because the WP\n     theme nests the embed many levels deep; that selector hides the whole\n     tree. Instead target known chrome by class\/tag. *\/\n  body > header, body > footer, body > nav,\n  .site-header, .site-footer, .site-navigation,\n  #masthead, #colophon, #site-navigation,\n  .wp-block-template-part, .wp-site-blocks > header, .wp-site-blocks > footer,\n  .menu-toggle, .main-navigation, .secondary, .widget-area,\n  #wpadminbar { display: none !important; }\n  \/* HubSpot leadin chat widget, Intercom, Drift \u2014 hide all chat overlays *\/\n  #hubspot-messages-iframe-container,\n  iframe[id^=\"hs-chat-\"], iframe[id^=\"hubspot-\"],\n  .hs-chat-frame, [data-test-id=\"chat-widget\"],\n  #intercom-container, .intercom-launcher,\n  .drift-widget-controller-frame { display: none !important; }\n  \/* WordPress site-wide pre-footer (\"Ready? Service without extra costs \/ Get\n     Demo \/ Choose Your Plan\") sits on every page below the embed. Hide it in\n     PDF so the export stops cleanly after the FAQ. *\/\n  #pre_footer, #pre_footer-content,\n  .pre_footer__title { display: none !important; }\n  \/* Hide the agroclimate hero (top wrapper). Keep .gpl-bottom (which carries\n     the CTA + FAQ) so the FAQ ships in the PDF \u2014 but suppress the in-bottom\n     green CTA \"Country-zone resolution...\" so it doesn't duplicate the tool's\n     own CTA. *\/\n  .gp-lead .gpl-hero,\n  .gpl-bottom .gpb-cta { display: none !important; }\n  \/* FAQ tabs can't be operated on paper \u2014 hide the tablist + tabs and show\n     every panel inline so the saved PDF carries the full text. *\/\n  .gpl-bottom .gpb-tablist { display: none !important; }\n  .gpl-bottom .gpb-tabs { border: 0 !important; box-shadow: none !important; background: transparent !important }\n  .gpl-bottom .gpb-panel,\n  .gpl-bottom .gpb-panel[hidden] { display: block !important; padding: 6px 0 !important; border-top: 1px dashed #cfe4d4 !important }\n  .gpl-bottom .gpb-panel:first-of-type { border-top: 0 !important }\n  \/* Embed-internal print-only CTA + footer still hidden (replaced by the\n     real FAQ that now ships through). Also hide the screen-mode copyright\n     line \u2014 user explicitly asked for no footer in PDF. *\/\n  .gpy-wrap .acx-print-cta,\n  .gpy-wrap .acx-print-footer,\n  .gpy-wrap .acx-copyright { display: none !important; }\n}\n\n.gpy-wrap .gpy-acx{background:var(--panel)}\n.gpy-wrap .gpy-acx-head{padding:14px 18px 8px;display:flex;justify-content:space-between;align-items:flex-start;gap:14px;flex-wrap:wrap}\n.gpy-wrap .gpy-acx-title{font-family:var(--wp--preset--font-family--poppins,\"Poppins\",system-ui,sans-serif);font-size:18px;font-weight:800;margin:0;color:var(--accent-dark);letter-spacing:-.018em;line-height:1.15}\n.gpy-wrap .gpy-acx-sub{color:var(--ink-dim);font-size:12px;margin-top:4px;max-width:620px;line-height:1.5}\n\n.gpy-wrap .gpy-acx-locale{display:flex;align-items:center;gap:8px;padding:0 18px;margin:4px 0 0;font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",\"SF Mono\",ui-monospace,Menlo,monospace);font-size:10.5px;color:var(--ink-dim);text-transform:uppercase;letter-spacing:.1em}\n.gpy-wrap .gpy-acx-locale .acx-loc-icon{flex:none;font-size:18px;line-height:1}\n.gpy-wrap .gpy-acx-locale .acx-loc-text{flex:1;min-width:0;line-height:1.5}\n.gpy-wrap .gpy-acx-locale .acx-loc-country{font-weight:700;color:var(--ink)}\n.gpy-wrap .gpy-acx-locale .acx-loc-zone{color:var(--ink);font-weight:500}\n.gpy-wrap .gpy-acx-locale .acx-loc-edit{background:transparent;border:1px solid var(--line);color:var(--ink-dim);padding:6px 12px;border-radius:6px;cursor:pointer;font-size:11px;font-family:inherit;font-weight:600;flex:none;transition:all .15s}\n.gpy-wrap .gpy-acx-locale .acx-loc-edit:hover{color:var(--ink);background:var(--bg);border-color:var(--ink-dim)}\n.gpy-wrap .gpy-acx-locale-picker{display:none;flex-direction:column;padding:12px 16px;margin:6px 18px 0;background:var(--panel2);border:1px solid var(--line);border-radius:8px;gap:10px}\n.gpy-wrap .gpy-acx-locale-picker.show{display:flex}\n.gpy-wrap .gpy-acx-locale-picker .acx-picker-tabs{display:flex;gap:4px;background:var(--bg);padding:3px;border-radius:7px;border:1px solid var(--line);align-self:flex-start}\n.gpy-wrap .gpy-acx-locale-picker .acx-picker-tab{background:transparent;border:0;color:var(--ink-dim);padding:6px 13px;font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",\"SF Mono\",ui-monospace,Menlo,monospace);font-size:11px;border-radius:5px;cursor:pointer;font-weight:600;transition:all .12s}\n.gpy-wrap .gpy-acx-locale-picker .acx-picker-tab.on{background:var(--panel);color:var(--ink);box-shadow:var(--shadow-sm)}\n.gpy-wrap .gpy-acx-locale-picker .acx-picker-list{display:flex;flex-direction:column;gap:10px}\n.gpy-wrap .gpy-acx-locale-picker .acx-picker-grp{display:flex;align-items:flex-start;gap:12px;padding:6px 0;border-top:1px solid var(--line);flex-wrap:wrap}\n.gpy-wrap .gpy-acx-locale-picker .acx-picker-grp:first-child{border-top:0;padding-top:2px}\n.gpy-wrap .gpy-acx-locale-picker .acx-picker-grp-h{flex:none;min-width:118px;font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",\"SF Mono\",ui-monospace,Menlo,monospace);font-size:10px;text-transform:uppercase;letter-spacing:.12em;color:var(--ink-dim);font-weight:700;padding-top:7px}\n.gpy-wrap .gpy-acx-locale-picker .acx-picker-grp-row{display:flex;flex-wrap:wrap;gap:6px;flex:1;min-width:0}\n.gpy-wrap .gpy-acx-locale-picker .acx-picker-list button{background:var(--bg);border:1px solid var(--line);color:var(--ink);padding:6px 11px;border-radius:5px;font-size:11px;font-family:'SF Mono',ui-monospace,monospace;cursor:pointer;font-weight:600;transition:all .12s}\n.gpy-wrap .gpy-acx-locale-picker .acx-picker-list button:hover{border-color:var(--accent);color:var(--accent)}\n.gpy-wrap .gpy-acx-locale-picker .acx-picker-list button.on{background:var(--accent);color:#fff;border-color:var(--accent)}\n.gpy-wrap .gpy-acx-globe{width:100%;max-width:760px;background:linear-gradient(180deg,#e0f2fe 0%,#bae6fd 100%);border-radius:10px;border:1px solid var(--line);overflow:hidden;display:block}\n.gpy-wrap[data-theme=\"dark\"] .gpy-acx-globe{background:linear-gradient(180deg,#0f1c2e 0%,#0a1422 100%)}\n.gpy-wrap .gpy-acx-globe .acx-graticule{stroke:rgba(0,0,0,0.08);stroke-width:1;stroke-dasharray:3 4;fill:none}\n.gpy-wrap[data-theme=\"dark\"] .gpy-acx-globe .acx-graticule{stroke:rgba(255,255,255,0.10)}\n.gpy-wrap .gpy-acx-globe .acx-country-dot{cursor:pointer;transition:all .15s ease}\n.gpy-wrap .gpy-acx-globe .acx-country-dot circle{fill:#22c55e;stroke:#fff;stroke-width:2;transition:r .15s ease}\n.gpy-wrap[data-theme=\"dark\"] .gpy-acx-globe .acx-country-dot circle{stroke:#0a1422}\n.gpy-wrap .gpy-acx-globe .acx-country-dot text{font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",\"SF Mono\",ui-monospace,Menlo,monospace);font-size:10px;font-weight:700;fill:#0e3a1c;text-anchor:middle;pointer-events:none;user-select:none}\n.gpy-wrap[data-theme=\"dark\"] .gpy-acx-globe .acx-country-dot text{fill:#e7edf5}\n.gpy-wrap .gpy-acx-globe .acx-country-dot:hover circle{r:11;fill:#15701e}\n.gpy-wrap .gpy-acx-globe .acx-country-dot.on circle{r:12;fill:#f76a0c;stroke-width:3}\n.gpy-wrap .gpy-acx-globe .acx-country-dot.on text{fill:#fff}\n.gpy-wrap .gpy-acx-globe-caption{font-size:11px;color:var(--ink-dim);text-align:center;margin-top:4px;font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",\"SF Mono\",ui-monospace,Menlo,monospace)}\n\n.gpy-wrap .gpy-acx-globals{margin:8px 18px 0;padding:9px 14px 12px;border:1px solid var(--line);border-radius:9px;background:var(--panel)}\n.gpy-wrap .gpy-acx-globals-label{font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",\"SF Mono\",ui-monospace,monospace);font-size:9.5px;text-transform:uppercase;letter-spacing:.14em;color:var(--accent-dark);font-weight:700;margin-bottom:4px}\n.gpy-wrap .gpy-acx-globals-label .gpy-acx-globals-sub{color:var(--ink-dim);font-weight:600;letter-spacing:.08em}\n.gpy-wrap .gpy-acx-timeline{display:flex;align-items:center;gap:10px;padding:6px 0 4px;flex-wrap:wrap}\n.gpy-wrap .gpy-acx-timeline-label{font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",\"SF Mono\",ui-monospace,Menlo,monospace);font-size:10px;text-transform:uppercase;letter-spacing:.12em;color:var(--ink-dim);font-weight:700;flex:none}\n.gpy-wrap .gpy-acx-decades{display:flex;gap:4px;background:var(--bg);padding:4px;border-radius:8px;border:1px solid var(--line);flex:1;min-width:240px}\n.gpy-wrap .gpy-acx-decades button{flex:1;background:transparent;border:0;color:var(--ink-dim);padding:9px 6px;font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",\"SF Mono\",ui-monospace,Menlo,monospace);font-size:12px;border-radius:5px;cursor:pointer;transition:all .15s;font-weight:700;letter-spacing:.02em}\n.gpy-wrap .gpy-acx-decades button.on{background:var(--accent-dark);color:#fff;box-shadow:var(--shadow-sm)}\n.gpy-wrap .gpy-acx-decades button:hover:not(.on){color:var(--ink);background:rgba(45,106,79,0.08)}\n.gpy-wrap .gpy-acx-play{flex:none;width:30px;height:30px;border-radius:7px;border:1px solid var(--line);background:var(--bg);color:var(--ink);cursor:pointer;font-size:12px;display:flex;align-items:center;justify-content:center;transition:all .15s;margin-left:auto}\n.gpy-wrap .gpy-acx-play:hover{border-color:var(--accent);color:var(--accent)}\n.gpy-wrap .gpy-acx-play.playing{background:var(--accent);color:#fff;border-color:var(--accent)}\n\n.gpy-wrap .gpy-acx-scen{display:flex;gap:4px;padding:0;margin-top:6px;flex-wrap:wrap}\n.gpy-wrap .gpy-acx-scen button{flex:1;min-width:150px;background:var(--bg);border:1px solid var(--line);color:var(--ink-dim);padding:8px 11px;font-family:inherit;font-size:12px;border-radius:7px;cursor:pointer;transition:all .15s;font-weight:600;text-align:left;line-height:1.3}\n.gpy-wrap .gpy-acx-scen button:hover:not(.on){border-color:var(--accent-soft);color:var(--ink)}\n.gpy-wrap .gpy-acx-scen button.on{background:var(--accent-pale);border-color:var(--accent);color:var(--ink)}\n.gpy-wrap .gpy-acx-scen button .scen-title{font-size:13px;font-weight:700;color:var(--ink);display:block;margin-bottom:3px;letter-spacing:-.01em}\n.gpy-wrap .gpy-acx-scen button .scen-hint{font-size:11px;color:var(--ink-dim);font-weight:500;display:block}\n.gpy-wrap .gpy-acx-scen button{position:relative}\n.gpy-wrap .gpy-acx-scen button.on::after{content:\"\";position:absolute;top:0;left:13px;right:13px;height:2px;background:var(--accent-dark);border-radius:0 0 2px 2px}\n\n.gpy-wrap .acx-hero-stats{display:grid;grid-template-columns:1fr 1fr 1fr;gap:0;padding:0;border-top:1px solid var(--line);border-bottom:1px solid var(--line);margin-top:0}\n.gpy-wrap .acx-hero-stat{padding:8px 14px;background:var(--panel);border-right:1px solid var(--line);display:flex;flex-direction:column;gap:1px;position:relative;min-height:auto;border-radius:0}\n.gpy-wrap .acx-hero-stat:last-child{border-right:0}\n.gpy-wrap .acx-hero-stat::after{display:none}\n.gpy-wrap .acx-hero-stat.win,.gpy-wrap .acx-hero-stat.lose,.gpy-wrap .acx-hero-stat.warn{border-color:var(--line);background:var(--panel)}\n.gpy-wrap .acx-hero-stat .hs-h{font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",\"SF Mono\",ui-monospace,Menlo,monospace);font-size:9px;text-transform:uppercase;letter-spacing:.12em;color:var(--ink-dim);font-weight:700;display:flex;align-items:center;gap:6px}\n.gpy-wrap .acx-hero-stat .hs-h .pip{width:6px;height:6px;border-radius:50%;display:inline-block;flex:none}\n.gpy-wrap .acx-hero-stat.win .hs-h .pip{background:var(--win)}\n.gpy-wrap .acx-hero-stat.lose .hs-h .pip{background:var(--lose)}\n.gpy-wrap .acx-hero-stat.warn .hs-h .pip{background:var(--warn)}\n.gpy-wrap .acx-hero-stat .hs-name{font-size:13px;font-weight:700;color:var(--ink);text-transform:capitalize;letter-spacing:-.01em;line-height:1.15;margin-top:1px}\n.gpy-wrap .acx-hero-stat .hs-delta{font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",\"SF Mono\",ui-monospace,Menlo,monospace);font-size:16px;font-weight:700;letter-spacing:-.02em;display:block;margin-top:0;font-variant-numeric:tabular-nums;line-height:1}\n.gpy-wrap .acx-hero-stat.win .hs-delta{color:var(--win)}\n.gpy-wrap .acx-hero-stat.lose .hs-delta{color:var(--lose)}\n.gpy-wrap .acx-hero-stat.warn .hs-delta{color:var(--warn)}\n.gpy-wrap .acx-hero-stat .hs-sub{font-size:10px;color:var(--ink-dim);font-weight:600;font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",\"SF Mono\",ui-monospace,Menlo,monospace);letter-spacing:.02em;margin-top:1px}\n.gpy-wrap .acx-hero-stat.flat .hs-delta{color:var(--ink-dim)}\n\n\/* Always-visible world map card *\/\n.gpy-wrap .acx-mapcard{margin:6px 14px 0;padding:10px 12px;background:var(--panel2);border:1px solid var(--line);border-radius:10px}\n.gpy-wrap .acx-mapcard-head{display:flex;justify-content:space-between;align-items:center;gap:10px;margin-bottom:8px;flex-wrap:wrap}\n.gpy-wrap .acx-mapcard-head h3{margin:0;font-size:11px;text-transform:uppercase;letter-spacing:.12em;color:var(--ink);font-weight:700;font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",\"SF Mono\",ui-monospace,Menlo,monospace)}\n.gpy-wrap .acx-mapcard-head h3 .head-decade{color:var(--accent)}\n.gpy-wrap .acx-map-metric{display:flex;gap:2px;background:var(--bg);padding:3px;border-radius:7px;border:1px solid var(--line)}\n.gpy-wrap .acx-map-metric button{background:transparent;border:0;color:var(--ink-dim);padding:6px 12px;font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",\"SF Mono\",ui-monospace,Menlo,monospace);font-size:10px;border-radius:5px;cursor:pointer;font-weight:700;text-transform:uppercase;letter-spacing:.04em;transition:all .12s}\n.gpy-wrap .acx-map-metric button:hover:not(.on){color:var(--ink)}\n.gpy-wrap .acx-map-metric button.on{background:var(--accent-dark);color:#fff;box-shadow:var(--shadow-sm)}\n\/* Leaflet map host \u2014 real interactive basemap (Leaflet 1.9.4 + OSM tiles).\n   No API key. Free, attribution-only. *\/\n.gpy-wrap .acx-mapcard-globe{border-radius:8px;border:1px solid var(--line);overflow:hidden;display:block;position:relative;height:280px;background:#eef3ee}\n.gpy-wrap[data-theme=\"dark\"] .acx-mapcard-globe{background:#0f1c2e}\n@media (max-width:820px){.gpy-wrap .acx-mapcard-globe{height:240px}}\n@media (min-width:1440px){.gpy-wrap .acx-mapcard-globe{height:340px}}\n.gpy-wrap .acx-mapcard-globe.loading::after{content:\"Loading map\u2026\";position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",\"SF Mono\",ui-monospace,Menlo,monospace);font-size:11px;color:var(--ink-dim);background:var(--bg);z-index:5}\n.gpy-wrap .acx-mapcard-globe.failed::after{content:\"Map unavailable \u2014 pick a country from the list below\";position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",\"SF Mono\",ui-monospace,Menlo,monospace);font-size:11px;color:var(--warn);background:var(--bg);z-index:5;padding:0 16px;text-align:center}\n\/* Wheel-zoom hint \u2014 small pill in the top-left, fades out after first click *\/\n.gpy-wrap .acx-mapcard-globe::before{content:\"Click map to enable scroll zoom\";position:absolute;top:10px;left:50%;transform:translateX(-50%);z-index:600;background:rgba(20,83,40,0.92);color:#fff;font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",\"SF Mono\",ui-monospace,monospace);font-size:10px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;padding:5px 12px;border-radius:999px;pointer-events:none;transition:opacity .3s ease;backdrop-filter:blur(6px)}\n.gpy-wrap .acx-mapcard-globe.wheel-on::before{opacity:0}\n.gpy-wrap .acx-mapcard-globe.loading::before,.gpy-wrap .acx-mapcard-globe.failed::before{display:none}\n.gpy-wrap .acx-mapcard-caption{font-size:10.5px;color:var(--ink-dim);text-align:center;margin:8px 0 0;font-family:var(--wp--preset--font-family--nunito,\"Nunito\",sans-serif);font-style:italic}\n.gpy-wrap .acx-mapcard-caption-top{margin:0 0 8px;text-transform:none;letter-spacing:0;font-weight:400;font-size:10.5px;text-align:left;padding:0 2px;font-style:italic;color:var(--ink-dim)}\n.gpy-wrap .acx-top-picks{display:none;margin-top:10px;padding:10px 12px;background:var(--panel);border:1px solid var(--line);border-radius:8px}\n.gpy-wrap .acx-top-picks[hidden]{display:none}\n.gpy-wrap .acx-top-picks.show{display:block}\n.gpy-wrap .acx-top-picks-head{font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",\"SF Mono\",ui-monospace,monospace);font-size:10px;text-transform:uppercase;letter-spacing:.14em;color:var(--accent-dark);font-weight:700;margin-bottom:8px}\n.gpy-wrap .acx-top-picks-row{display:flex;align-items:flex-start;gap:10px;padding:5px 0}\n.gpy-wrap .acx-top-picks-row + .acx-top-picks-row{border-top:1px dashed var(--line2);padding-top:8px;margin-top:2px}\n.gpy-wrap .acx-top-picks-row-label{flex:0 0 130px;font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",\"SF Mono\",ui-monospace,monospace);font-size:10px;text-transform:uppercase;letter-spacing:.08em;font-weight:700;padding-top:5px;line-height:1.3}\n.gpy-wrap .acx-top-picks-row.win .acx-top-picks-row-label{color:var(--win)}\n.gpy-wrap .acx-top-picks-row.lose .acx-top-picks-row-label{color:var(--lose)}\n.gpy-wrap .acx-top-picks-list{display:flex;flex-wrap:wrap;gap:6px;flex:1;min-width:0}\n.gpy-wrap .acx-top-pick{display:inline-flex;align-items:center;gap:6px;padding:5px 10px;background:var(--bg);border:1px solid var(--line);border-radius:999px;font-family:var(--wp--preset--font-family--nunito,\"Nunito\",sans-serif);font-size:11.5px;color:var(--ink);cursor:pointer;transition:all .12s}\n.gpy-wrap .acx-top-pick:hover{border-color:var(--accent);background:#fff}\n.gpy-wrap .acx-top-pick.win{border-color:var(--accent-soft)}\n.gpy-wrap .acx-top-pick.lose{border-color:#fda4af}\n.gpy-wrap .acx-top-pick .pick-score{font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",\"SF Mono\",ui-monospace,monospace);font-weight:700;font-size:11px}\n.gpy-wrap .acx-top-pick.win .pick-score{color:var(--win)}\n.gpy-wrap .acx-top-pick.lose .pick-score{color:var(--lose)}\n.gpy-wrap .acx-top-pick .pick-rank{display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;border-radius:50%;color:#fff;font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",\"SF Mono\",ui-monospace,monospace);font-size:10px;font-weight:700;flex:none}\n.gpy-wrap .acx-top-pick.win .pick-rank{background:var(--win)}\n.gpy-wrap .acx-top-pick.lose .pick-rank{background:var(--lose)}\n@media (max-width:820px){\n  .gpy-wrap .acx-top-picks-row{flex-direction:column;gap:5px}\n  .gpy-wrap .acx-top-picks-row-label{flex:none;padding-top:0}\n}\n\n\n\/* Tooltip styling for Leaflet-bound country tooltips. Targeted by the className\n   we pass to bindTooltip so we keep our own typography + scale chips. Leaflet\n   inserts tooltips into a global pane so we can't anchor under .gpy-wrap. *\/\n\/* Global chart tooltip \u2014 per rule \u00a717, every chart uses this single floating\n   element. Content swapped on mouseover, position follows cursor. Touch:\n   tap to show, tap elsewhere to hide. *\/\n.gpy-wrap .acx-chart-tip{position:fixed;z-index:9999;background:#ffffff;border:1px solid var(--accent);border-radius:8px;padding:9px 12px;box-shadow:0 10px 28px rgba(20,83,40,0.18);font-family:var(--wp--preset--font-family--nunito,\"Nunito\",system-ui,sans-serif);color:#212121;min-width:160px;max-width:280px;font-size:12px;line-height:1.45;pointer-events:none;opacity:0;transition:opacity .12s ease;display:none}\n.gpy-wrap .acx-chart-tip.show{display:block;opacity:1}\n.gpy-wrap .acx-chart-tip .tip-h{font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",\"SF Mono\",ui-monospace,monospace);font-size:10px;text-transform:uppercase;letter-spacing:.1em;color:var(--accent-dark);font-weight:700;margin-bottom:4px}\n.gpy-wrap .acx-chart-tip .tip-v{font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",\"SF Mono\",ui-monospace,monospace);font-size:13px;font-weight:700;color:var(--ink);font-variant-numeric:tabular-nums;letter-spacing:-.01em}\n.gpy-wrap .acx-chart-tip .tip-meta{font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",\"SF Mono\",ui-monospace,monospace);font-size:10.5px;color:var(--ink-dim);margin-top:4px;font-weight:600}\n.gpy-wrap .acx-chart-tip .tip-meta .up{color:var(--win)}\n.gpy-wrap .acx-chart-tip .tip-meta .down{color:var(--lose)}\n.gpy-wrap .acx-chart-tip .tip-meta .warn{color:var(--warn)}\n.gpy-wrap .acx-heat-tbl tbody td:not(:first-child){cursor:help}\n\n.leaflet-tooltip.acx-leaf-tip{background:#ffffff;border:1px solid #2d6a4f;border-radius:8px;padding:9px 12px;box-shadow:0 8px 24px rgba(0,0,0,0.18);font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",sans-serif;color:#1a1a1a;min-width:180px;max-width:260px;white-space:normal}\n.leaflet-tooltip.acx-leaf-tip::before{border-top-color:#2d6a4f}\n.leaflet-tooltip.acx-leaf-tip .tip-name{font-size:13px;font-weight:700;line-height:1.2;letter-spacing:-.01em}\n.leaflet-tooltip.acx-leaf-tip .tip-zone{font-size:10px;color:#6b6b6b;font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",\"SF Mono\",ui-monospace,Menlo,monospace);margin-top:2px;margin-bottom:6px;padding-bottom:6px;border-bottom:1px solid #e8e4d8}\n.leaflet-tooltip.acx-leaf-tip .tip-stat{display:flex;justify-content:space-between;gap:10px;font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",\"SF Mono\",ui-monospace,Menlo,monospace);font-size:11px;margin-top:3px;align-items:baseline}\n.leaflet-tooltip.acx-leaf-tip .tip-stat .tip-h{color:#6b6b6b;font-weight:600}\n.leaflet-tooltip.acx-leaf-tip .tip-stat .tip-v{color:#1a1a1a;font-weight:700;font-variant-numeric:tabular-nums;text-transform:capitalize}\n.leaflet-tooltip.acx-leaf-tip .tip-stat .tip-v.win{color:#16a34a}\n.leaflet-tooltip.acx-leaf-tip .tip-stat .tip-v.lose{color:#dc2626}\n.leaflet-tooltip.acx-leaf-tip .tip-stat .tip-v.warn{color:#c2410c}\n.leaflet-container .acx-leaf-marker{cursor:pointer}\n.leaflet-container .acx-leaf-marker.on{filter:drop-shadow(0 0 6px #f76a0c)}\n\n\/* Reset-zoom + metric controls grouped *\/\n.gpy-wrap .acx-map-reset{background:var(--bg);border:1px solid var(--line);color:var(--ink-dim);padding:6px 11px;font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",\"SF Mono\",ui-monospace,Menlo,monospace);font-size:10px;border-radius:7px;cursor:pointer;font-weight:700;text-transform:uppercase;letter-spacing:.04em;transition:all .12s;display:flex;align-items:center;gap:5px}\n.gpy-wrap .acx-map-reset:hover{border-color:var(--accent);color:var(--accent)}\n.gpy-wrap .acx-map-reset.hidden{opacity:0;pointer-events:none}\n.gpy-wrap .acx-map-controls{display:flex;gap:6px;align-items:center}\n.gpy-wrap .acx-mapcard-scale{display:flex;align-items:center;gap:10px;justify-content:center;margin-top:8px;font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",\"SF Mono\",ui-monospace,Menlo,monospace);font-size:10px;color:var(--ink-dim);flex-wrap:wrap}\n.gpy-wrap .acx-mapcard-scale .scale-label{font-weight:700;letter-spacing:.05em}\n.gpy-wrap .acx-mapcard-scale .scale-grad{display:inline-block;width:160px;height:10px;border-radius:3px}\n.gpy-wrap .acx-mapcard-scale .scale-end{font-weight:700;color:var(--ink)}\n@media (max-width:820px){\n  .gpy-wrap .acx-mapcard{margin:8px 14px 0;padding:12px 14px}\n  .gpy-wrap .acx-map-metric button{padding:8px 10px;font-size:10px;min-height:34px}\n  .gpy-wrap .acx-mapcard-globe .acx-country-dot text{font-size:11px}\n}\n@media (min-width:1200px){\n  .gpy-wrap .acx-mapcard{margin:12px 22px 0;padding:18px 22px}\n  .gpy-wrap .acx-mapcard-head h3{font-size:12px}\n}\n\n.gpy-wrap .gpy-acx-body{display:grid;grid-template-columns:1fr 1fr;gap:10px;padding:0 14px 14px;align-items:start}\n.gpy-wrap .gpy-acx-col{display:flex;flex-direction:column;gap:10px;min-width:0}\n\n.gpy-wrap .acx-climate-grid{display:grid;grid-template-columns:1fr 1fr;gap:8px}\n.gpy-wrap .acx-climate-tile{background:var(--bg);border:1px solid var(--line);border-radius:7px;padding:9px 11px;display:flex;flex-direction:column;gap:2px;position:relative;overflow:hidden}\n.gpy-wrap .acx-climate-tile .tile-h{font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",\"SF Mono\",ui-monospace,Menlo,monospace);font-size:9px;text-transform:uppercase;letter-spacing:.1em;color:var(--ink-dim);font-weight:700}\n.gpy-wrap .acx-climate-tile .tile-val{font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",\"SF Mono\",ui-monospace,Menlo,monospace);font-size:19px;color:var(--ink);font-weight:700;line-height:1.05;font-variant-numeric:tabular-nums;margin-top:2px;letter-spacing:-.01em}\n.gpy-wrap .acx-climate-tile .tile-delta{font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",\"SF Mono\",ui-monospace,Menlo,monospace);font-size:10px;color:var(--ink-dim);font-weight:700;margin-top:2px;letter-spacing:.02em}\n.gpy-wrap .acx-climate-tile .tile-delta.up{color:var(--win)}\n.gpy-wrap .acx-climate-tile .tile-delta.down{color:var(--lose)}\n.gpy-wrap .acx-climate-tile .tile-delta.warn{color:var(--warn)}\n.gpy-wrap .acx-climate-tile .tile-spark{width:100%;height:30px;margin-top:6px;display:block}\n.gpy-wrap .acx-climate-tile .tile-spark .spark-line{stroke:var(--ink-dim);stroke-width:1.4;fill:none;opacity:.7}\n.gpy-wrap .acx-climate-tile .tile-spark .spark-fill{fill:var(--accent);opacity:.10}\n.gpy-wrap .acx-climate-tile.warn .tile-spark .spark-fill{fill:var(--warn);opacity:.10}\n.gpy-wrap .acx-climate-tile .tile-spark .spark-dot{fill:var(--ink-dim);opacity:.5}\n.gpy-wrap .acx-climate-tile .tile-spark .spark-dot.now{fill:var(--accent);opacity:1;stroke:var(--bg);stroke-width:2;r:4}\n.gpy-wrap .acx-climate-tile.warn .tile-spark .spark-dot.now{fill:var(--warn)}\n.gpy-wrap .acx-climate-tile .tile-spark .spark-base{stroke:var(--line2);stroke-width:1;stroke-dasharray:2 3;fill:none}\n.gpy-wrap .acx-climate-zone{display:flex;align-items:center;gap:8px;margin-top:10px;padding:8px 10px;background:var(--bg);border:1px dashed var(--line2);border-radius:7px;font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",\"SF Mono\",ui-monospace,Menlo,monospace);font-size:11px;color:var(--ink);flex-wrap:wrap}\n.gpy-wrap .acx-climate-zone .acx-zone-from{color:var(--ink-dim)}\n.gpy-wrap .acx-climate-zone .acx-zone-to{color:var(--ink);font-weight:700}\n.gpy-wrap .acx-climate-zone.shifting{border-color:var(--warn);background:var(--warn-bg)}\n.gpy-wrap .acx-climate-zone.shifting .acx-zone-to{color:var(--warn)}\n\n.gpy-wrap .acx-wl{display:grid;grid-template-columns:1fr 1fr;gap:10px}\n.gpy-wrap .acx-wl-col h4{font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",\"SF Mono\",ui-monospace,Menlo,monospace);font-size:10px;text-transform:uppercase;letter-spacing:.1em;color:var(--ink-dim);font-weight:700;margin:0 0 8px;display:flex;align-items:center;gap:6px}\n.gpy-wrap .acx-wl-col.win h4 .pip{width:6px;height:6px;border-radius:50%;background:var(--win);display:inline-block}\n.gpy-wrap .acx-wl-col.lose h4 .pip{width:6px;height:6px;border-radius:50%;background:var(--lose);display:inline-block}\n.gpy-wrap .acx-wl-row{display:flex;flex-direction:column;gap:6px;padding:10px 12px;background:var(--bg);border:1px solid var(--line);border-radius:8px;margin-bottom:6px}\n.gpy-wrap .acx-wl-row:last-child{margin-bottom:0}\n.gpy-wrap .acx-wl-head{display:flex;justify-content:space-between;align-items:baseline;gap:8px}\n.gpy-wrap .acx-wl-row .acx-wl-name{font-size:13px;font-weight:700;color:var(--ink);text-transform:capitalize;letter-spacing:-.01em}\n.gpy-wrap .acx-wl-row .acx-wl-delta{font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",\"SF Mono\",ui-monospace,Menlo,monospace);font-size:13px;font-weight:700;font-variant-numeric:tabular-nums}\n.gpy-wrap .acx-wl-col.win .acx-wl-row .acx-wl-delta{color:var(--win)}\n.gpy-wrap .acx-wl-col.lose .acx-wl-row .acx-wl-delta{color:var(--lose)}\n.gpy-wrap .acx-wl-bar{position:relative;height:9px;background:var(--panel2);border-radius:5px;overflow:hidden;border:1px solid var(--line)}\n.gpy-wrap .acx-wl-bar .bar-base{position:absolute;top:0;left:0;height:100%;background:var(--line2);opacity:.6}\n.gpy-wrap .acx-wl-bar .bar-now{position:absolute;top:0;left:0;height:100%;border-radius:0;transition:width .25s}\n.gpy-wrap .acx-wl-col.win .acx-wl-bar .bar-now{background:linear-gradient(90deg,var(--accent-soft),var(--win))}\n.gpy-wrap .acx-wl-col.lose .acx-wl-bar .bar-now{background:linear-gradient(90deg,#fb7185,var(--lose))}\n.gpy-wrap .acx-wl-bar .bar-axis{position:absolute;top:0;height:100%;width:1px;background:var(--ink);opacity:.18}\n.gpy-wrap .acx-wl-bar .bar-axis.x100{display:none}\n.gpy-wrap .acx-wl-meta{font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",\"SF Mono\",ui-monospace,Menlo,monospace);font-size:10px;color:var(--ink-dim);letter-spacing:.02em;display:flex;flex-wrap:wrap;gap:4px;align-items:center}\n.gpy-wrap .acx-wl-meta .chip{display:inline-block;padding:2px 7px;border-radius:4px;border:1px solid var(--line2);font-weight:700;background:var(--panel2)}\n.gpy-wrap .acx-wl-meta .chip.risk-hi{color:var(--lose);border-color:var(--lose);background:rgba(220,38,38,0.06)}\n.gpy-wrap .acx-wl-meta .chip.risk-med{color:var(--warn);border-color:var(--warn);background:rgba(194,65,12,0.06)}\n.gpy-wrap .acx-wl-meta .chip.risk-lo{color:var(--win);border-color:var(--win);background:rgba(22,163,74,0.06)}\n.gpy-wrap .acx-wl-meta .score-range{color:var(--ink-dim);font-weight:600}\n.gpy-wrap .acx-wl-meta .score-range b{color:var(--ink);font-weight:700}\n.gpy-wrap .acx-wl-empty{font-size:11px;color:var(--ink-dim);font-style:italic;padding:8px 4px}\n\n.gpy-wrap .acx-heat{margin-top:14px;overflow-x:auto}\n.gpy-wrap .acx-heat-cap{font-family:var(--wp--preset--font-family--nunito,\"Nunito\",sans-serif);font-size:10.5px;text-transform:none;letter-spacing:0;color:var(--ink-dim);font-weight:400;font-style:italic;margin:0 0 6px}\n.gpy-wrap .acx-heat-tbl{width:100%;border-collapse:separate;border-spacing:3px;font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",\"SF Mono\",ui-monospace,Menlo,monospace);font-size:11px;table-layout:fixed;min-width:420px}\n.gpy-wrap .acx-heat-tbl th{font-weight:700;color:var(--ink-dim);padding:5px 4px;text-align:center;background:var(--bg);border-radius:4px;letter-spacing:.04em;font-size:10px}\n.gpy-wrap .acx-heat-tbl th:first-child{text-align:left;padding-left:8px}\n.gpy-wrap .acx-heat-tbl td{padding:8px 4px;text-align:center;color:#fff;font-weight:700;border-radius:4px;font-variant-numeric:tabular-nums;transition:transform .15s;font-size:11px}\n.gpy-wrap .acx-heat-tbl td:first-child{background:var(--bg);color:var(--ink);text-align:left;padding-left:10px;text-transform:capitalize;font-weight:700;font-size:11px}\n.gpy-wrap .acx-heat-tbl td.heat-now{box-shadow:inset 0 0 0 2px var(--ink);transform:scale(1.04)}\n.gpy-wrap .acx-heat-tbl td[data-empty=\"1\"]{background:var(--bg) !important;color:var(--ink-dim);box-shadow:none;opacity:.5}\n.gpy-wrap .acx-heat-legend{display:flex;align-items:center;gap:10px;margin-top:10px;font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",\"SF Mono\",ui-monospace,Menlo,monospace);font-size:10px;color:var(--ink-dim);justify-content:center;flex-wrap:wrap}\n.gpy-wrap .acx-heat-legend .lg-label{font-weight:700;letter-spacing:.04em}\n.gpy-wrap .acx-heat-legend .lg-scale{display:flex;align-items:center;gap:1px;border-radius:4px;overflow:hidden}\n.gpy-wrap .acx-heat-legend .lg-step{width:30px;height:14px;display:inline-flex;align-items:center;justify-content:center;color:#fff;font-size:9px;font-weight:700}\n.gpy-wrap .acx-heat-legend .lg-step.empty{background:var(--bg);color:var(--ink-dim);border:1px dashed var(--line2)}\n.gpy-wrap .acx-heat-legend .lg-now{display:inline-flex;align-items:center;gap:5px;color:var(--ink-dim)}\n.gpy-wrap .acx-heat-legend .lg-now-ring{width:11px;height:11px;border:2px solid var(--accent-dark);border-radius:3px;display:inline-block;box-sizing:border-box;background:transparent}\n.gpy-wrap .acx-heat-legend .lg-hint{color:var(--ink-dim);font-weight:600;letter-spacing:.02em;padding-left:6px;border-left:1px solid var(--line)}\n.gpy-wrap .acx-heat-tbl tr{transition:background .12s}\n.gpy-wrap .acx-heat-tbl tbody tr{cursor:pointer}\n.gpy-wrap .acx-heat-tbl tbody tr:hover td:first-child{background:var(--panel2)}\n.gpy-wrap .acx-heat-tbl tbody tr.focus td:first-child{background:var(--ink);color:#fff}\n.gpy-wrap .acx-heat-tbl tbody tr.focus td:not(:first-child){box-shadow:inset 0 0 0 2px var(--ink)}\n\n\/* Crop focus card \u2014 appears when a heatmap row is clicked *\/\n.gpy-wrap .acx-cropfocus{margin-top:14px;background:var(--bg);border:1px solid var(--line);border-radius:10px;padding:14px 16px;display:none}\n.gpy-wrap .acx-cropfocus.show{display:block}\n.gpy-wrap .acx-cropfocus-head{display:flex;justify-content:space-between;align-items:baseline;gap:10px;margin-bottom:10px;border-bottom:1px solid var(--line);padding-bottom:10px}\n.gpy-wrap .acx-cropfocus-title{font-size:14px;font-weight:700;color:var(--ink);text-transform:capitalize;letter-spacing:-.01em}\n.gpy-wrap .acx-cropfocus-title .crop-name{color:var(--accent)}\n.gpy-wrap .acx-cropfocus-clear{background:transparent;border:1px solid var(--line);color:var(--ink-dim);padding:4px 10px;font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",\"SF Mono\",ui-monospace,Menlo,monospace);font-size:10px;border-radius:5px;cursor:pointer;font-weight:700;text-transform:uppercase;letter-spacing:.04em;flex:none}\n.gpy-wrap .acx-cropfocus-clear:hover{color:var(--ink);border-color:var(--ink-dim)}\n.gpy-wrap .acx-cropfocus-grid{display:grid;grid-template-columns:1.4fr 1fr;gap:14px;align-items:stretch}\n.gpy-wrap .acx-cropfocus-traj{background:var(--panel2);border:1px solid var(--line);border-radius:7px;padding:10px 12px;display:flex;flex-direction:column;gap:6px}\n.gpy-wrap .acx-cropfocus-traj .traj-h{font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",\"SF Mono\",ui-monospace,Menlo,monospace);font-size:9px;text-transform:uppercase;letter-spacing:.1em;color:var(--ink-dim);font-weight:700}\n.gpy-wrap .acx-cropfocus-traj .traj-vals{font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",\"SF Mono\",ui-monospace,Menlo,monospace);font-size:13px;color:var(--ink);font-weight:700;font-variant-numeric:tabular-nums;display:flex;gap:6px;align-items:baseline;flex-wrap:wrap}\n.gpy-wrap .acx-cropfocus-traj .traj-vals .from{color:var(--ink-dim);font-weight:600}\n.gpy-wrap .acx-cropfocus-traj .traj-vals .to{color:var(--ink)}\n.gpy-wrap .acx-cropfocus-traj .traj-vals .delta.up{color:var(--win)}\n.gpy-wrap .acx-cropfocus-traj .traj-vals .delta.down{color:var(--lose)}\n.gpy-wrap .acx-cropfocus-traj .traj-spark{width:100%;height:48px;display:block;margin-top:4px}\n.gpy-wrap .acx-cropfocus-traj .traj-spark .spark-line{stroke:var(--accent);stroke-width:1.6;fill:none}\n.gpy-wrap .acx-cropfocus-traj .traj-spark .spark-fill{fill:var(--accent);opacity:.12}\n.gpy-wrap .acx-cropfocus-traj .traj-spark .spark-dot{fill:var(--accent);stroke:var(--bg);stroke-width:2;r:3.5}\n.gpy-wrap .acx-cropfocus-traj .traj-spark .spark-dot.now{r:5;stroke-width:2}\n.gpy-wrap .acx-cropfocus-traj .traj-spark .spark-grid{stroke:var(--line2);stroke-width:1;stroke-dasharray:2 3;fill:none;opacity:.5}\n.gpy-wrap .acx-cropfocus-traj .traj-spark text.spark-yr{fill:var(--ink-dim);font-size:8.5px;font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",\"SF Mono\",ui-monospace,Menlo,monospace)}\n.gpy-wrap .acx-cropfocus-side{display:flex;flex-direction:column;gap:6px}\n.gpy-wrap .acx-cropfocus-row{display:flex;justify-content:space-between;gap:10px;padding:7px 10px;background:var(--panel2);border:1px solid var(--line);border-radius:6px;font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",\"SF Mono\",ui-monospace,Menlo,monospace);font-size:11px;align-items:baseline}\n.gpy-wrap .acx-cropfocus-row .cf-h{color:var(--ink-dim);font-weight:600;text-transform:uppercase;letter-spacing:.05em;font-size:9.5px}\n.gpy-wrap .acx-cropfocus-row .cf-v{color:var(--ink);font-weight:700;font-variant-numeric:tabular-nums;text-align:right}\n.gpy-wrap .acx-cropfocus-row .cf-v.up{color:var(--win)}\n.gpy-wrap .acx-cropfocus-row .cf-v.down{color:var(--lose)}\n.gpy-wrap .acx-cropfocus-row .cf-v.warn{color:var(--warn)}\n.gpy-wrap .acx-cropfocus-row .cf-v.muted{color:var(--ink-dim);font-weight:600}\n.gpy-wrap .acx-cropfocus-empty{font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",\"SF Mono\",ui-monospace,Menlo,monospace);font-size:11px;color:var(--ink-dim);padding:18px;text-align:center;font-style:italic}\n@media (max-width:760px){\n  .gpy-wrap .acx-cropfocus-grid{grid-template-columns:1fr}\n}\n\n\/* Editorial summary \u2014 quiet, newsprint-feel callout. No gradient, no\n   coloured side stripe, no shadow halo. Reads like the closing paragraph\n   of a brief, not a SaaS hint banner. *\/\n.gpy-wrap .acx-summary{background:transparent;border:0;border-top:2px solid var(--accent-dark);border-bottom:1px solid var(--line);border-radius:0;padding:14px 2px 16px;position:relative}\n.gpy-wrap .acx-summary h3{font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",\"SF Mono\",ui-monospace,monospace);font-size:9.5px;text-transform:uppercase;letter-spacing:.18em;color:var(--ink-dim);font-weight:700;margin:0 0 10px}\n.gpy-wrap .acx-summary p{margin:0 0 8px;font-size:14px;color:var(--ink);line-height:1.6;font-family:var(--wp--preset--font-family--nunito,\"Nunito\",sans-serif)}\n.gpy-wrap .acx-summary p:last-child{margin-bottom:0}\n.gpy-wrap .acx-summary p strong{font-weight:700;color:var(--ink)}\n.gpy-wrap .acx-summary .num{font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",\"SF Mono\",ui-monospace,Menlo,monospace);font-weight:700;color:var(--ink);font-size:14px;letter-spacing:-.01em}\n.gpy-wrap .acx-summary .num.up{color:var(--win)}\n.gpy-wrap .acx-summary .num.down{color:var(--lose)}\n.gpy-wrap .acx-summary .num.warn{color:var(--warn)}\n.gpy-wrap .acx-summary .summary-divider{height:0;border:0;margin:8px 0;border-top:1px dashed var(--line2)}\n\n.gpy-wrap .acx-zone-access{display:flex;flex-direction:column;gap:8px}\n.gpy-wrap .acx-za-row{display:grid;grid-template-columns:1fr auto;gap:6px;align-items:center}\n.gpy-wrap .acx-za-row .acx-za-head{font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",\"SF Mono\",ui-monospace,monospace);font-size:10px;text-transform:uppercase;letter-spacing:.1em;color:var(--ink-dim);font-weight:700}\n.gpy-wrap .acx-za-row .acx-za-val{font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",\"SF Mono\",ui-monospace,monospace);font-size:12px;font-weight:700;color:var(--ink);font-variant-numeric:tabular-nums}\n.gpy-wrap .acx-za-row .acx-za-val .tier{font-size:10px;color:var(--ink-dim);font-weight:600;margin-left:6px}\n.gpy-wrap .acx-za-row .acx-za-val.up{color:var(--win)}\n.gpy-wrap .acx-za-row .acx-za-val.warn{color:var(--warn)}\n.gpy-wrap .acx-za-row .acx-za-val.down{color:var(--lose)}\n.gpy-wrap .acx-za-bar{grid-column:1 \/ -1;height:6px;background:var(--panel2);border-radius:3px;overflow:hidden;border:1px solid var(--line)}\n.gpy-wrap .acx-za-bar .fill{height:100%;background:linear-gradient(90deg,var(--accent-soft),var(--accent));transition:width .25s}\n.gpy-wrap .acx-za-bar.cost .fill{background:linear-gradient(90deg,var(--accent-soft),var(--warn))}\n.gpy-wrap .acx-za-note{font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",\"SF Mono\",ui-monospace,monospace);font-size:10.5px;color:var(--ink-dim);line-height:1.5;padding-top:6px;border-top:1px dashed var(--line2);margin-top:4px}\n.gpy-wrap .acx-za-note strong{color:var(--ink)}\n.gpy-wrap .acx-za-note.warn strong{color:var(--warn)}\n\n.gpy-wrap .acx-trade{display:flex;flex-direction:column;gap:7px;margin-top:2px}\n.gpy-wrap .acx-trade-row{display:grid;grid-template-columns:1fr 80px auto;gap:10px;font-size:11px;padding:9px 12px;background:var(--bg);border:1px solid var(--line);border-radius:7px;align-items:center;font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",\"SF Mono\",ui-monospace,Menlo,monospace)}\n.gpy-wrap .acx-trade-label-grp{display:flex;flex-direction:column;gap:2px;min-width:0}\n.gpy-wrap .acx-trade-label-grp .crop{color:var(--ink);font-weight:700;text-transform:capitalize;font-size:12px}\n.gpy-wrap .acx-trade-label-grp .choke{color:var(--ink-dim);font-weight:600;font-size:10px;letter-spacing:.02em}\n.gpy-wrap .acx-trade-bar{position:relative;height:9px;background:var(--panel2);border-radius:5px;border:1px solid var(--line)}\n.gpy-wrap .acx-trade-bar .bar-mid{position:absolute;top:-2px;left:50%;width:1px;height:13px;background:var(--ink-dim);opacity:.4}\n.gpy-wrap .acx-trade-bar .bar-fill{position:absolute;top:0;height:100%;border-radius:0;transition:all .25s}\n.gpy-wrap .acx-trade-bar .bar-fill.pos{left:50%;background:var(--accent)}\n.gpy-wrap .acx-trade-bar .bar-fill.pos.med{background:var(--warn)}\n.gpy-wrap .acx-trade-bar .bar-fill.pos.hi{background:var(--lose)}\n.gpy-wrap .acx-trade-bar .bar-fill.neg{right:50%;background:var(--accent)}\n.gpy-wrap .acx-trade-vals{display:flex;flex-direction:column;gap:1px;text-align:right;min-width:74px}\n.gpy-wrap .acx-trade-vals .px{font-weight:700;font-size:11px;font-variant-numeric:tabular-nums;letter-spacing:.02em}\n.gpy-wrap .acx-trade-vals .px.hi{color:var(--lose)}\n.gpy-wrap .acx-trade-vals .px.med{color:var(--warn)}\n.gpy-wrap .acx-trade-vals .px.lo{color:var(--win)}\n.gpy-wrap .acx-trade-vals .conc{color:var(--ink-dim);font-size:10px;font-weight:600}\n\n.gpy-wrap .gpy-acx-foot{padding:6px 14px 14px;display:flex;flex-direction:column;gap:8px}\n.gpy-wrap .gpy-acx-note{font-size:11px;color:var(--ink-dim);line-height:1.55;padding:0 2px}\n.gpy-wrap .gpy-acx-note strong{color:var(--ink);font-weight:700}\n.gpy-wrap .gpy-acx-cta{display:inline-flex;align-items:center;justify-content:center;gap:6px;background:var(--orange);color:#fff;border:0;padding:9px 18px;border-radius:999px;font-family:var(--wp--preset--font-family--poppins,\"Poppins\",system-ui,sans-serif);font-weight:600;font-size:13px;cursor:pointer;text-decoration:none;align-self:center;transition:background .15s,box-shadow .15s,transform .15s;box-shadow:0 4px 12px rgba(247,106,12,.28)}\n.gpy-wrap .gpy-acx-cta:hover{background:var(--orange-h);transform:translateY(-1px);box-shadow:0 6px 18px rgba(247,106,12,.38)}\n.gpy-wrap .gpy-acx-cta .arr{transition:transform .15s}\n.gpy-wrap .gpy-acx-cta:hover .arr{transform:translateX(4px)}\n\n.gpy-wrap .gpy-acx-xlink{display:flex;align-items:center;gap:11px;background:var(--accent-pale);border:1px solid var(--cta-border);border-radius:10px;padding:12px 16px;color:var(--cta-text);text-decoration:none;transition:all .15s;font-family:inherit}\n.gpy-wrap .gpy-acx-xlink:hover{background:#fff;border-color:var(--accent);color:var(--ink)}\n.gpy-wrap .gpy-acx-xlink-icon{flex:none;font-size:20px;line-height:1}\n.gpy-wrap .gpy-acx-xlink-text{flex:1;min-width:0;display:flex;flex-direction:column;text-align:left;line-height:1.35}\n.gpy-wrap .gpy-acx-xlink-text strong{font-size:12px;color:var(--ink);font-weight:700}\n.gpy-wrap .gpy-acx-xlink-text span{font-size:11px;color:var(--ink-dim);font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",\"SF Mono\",ui-monospace,Menlo,monospace)}\n.gpy-wrap .gpy-acx-xlink .arr{flex:none;color:var(--accent);font-weight:700;transition:transform .15s}\n.gpy-wrap .gpy-acx-xlink:hover .arr{transform:translateX(3px)}\n\n@media (max-width:820px){\n  .gpy-wrap .acx-scope-legend{margin:8px 14px 0;padding:8px 12px;font-size:11px;line-height:1.55}\n  .gpy-wrap .acx-hero-stats{grid-template-columns:1fr;gap:7px;padding:12px 14px 4px}\n  .gpy-wrap .acx-hero-stat{padding:11px 14px;min-height:0}\n  .gpy-wrap .acx-hero-stat .hs-delta{font-size:20px}\n  .gpy-wrap .gpy-acx-head{padding:18px 16px 12px}\n  .gpy-wrap .gpy-acx-locale{margin:14px 14px 0;padding:11px 14px;gap:10px}\n  .gpy-wrap .gpy-acx-locale-picker{margin:6px 14px 0}\n  .gpy-wrap .gpy-acx-timeline{padding:14px 14px 8px;gap:10px}\n  .gpy-wrap .gpy-acx-decades button{padding:11px 4px;font-size:12px;min-height:44px}\n  .gpy-wrap .gpy-acx-play{width:44px;height:44px;min-height:44px;font-size:16px}\n  .gpy-wrap .gpy-acx-scen{padding:0 14px;flex-direction:column;gap:6px}\n  .gpy-wrap .gpy-acx-scen button{min-width:0;min-height:56px;padding:10px 12px}\n  .gpy-wrap .gpy-acx-body{grid-template-columns:1fr;gap:12px;padding:0 14px 14px}\n  .gpy-wrap .acx-wl{grid-template-columns:1fr;gap:8px}\n  .gpy-wrap .acx-heat-tbl{font-size:10px}\n  .gpy-wrap .acx-heat-tbl td:first-child{font-size:11px}\n  .gpy-wrap .gpy-acx-foot{padding:6px 14px 16px}\n  .gpy-wrap .gpy-acx-cta{padding:14px;min-height:48px}\n  .gpy-wrap .gpy-acx-locale .acx-loc-edit{padding:8px 12px;min-height:36px;font-size:12px}\n  .gpy-wrap .gpy-acx-locale-picker button{padding:10px 12px;min-height:40px;font-size:12px}\n  .gpy-wrap .gpy-acx-locale-picker .acx-picker-tab{padding:9px 14px;min-height:36px;font-size:12px}\n}\n@media (max-width:480px){\n  .gpy-wrap{font-size:14px;border-radius:0}\n  .gpy-wrap .gpy-acx-title{font-size:16px}\n  .gpy-wrap .acx-climate-grid{grid-template-columns:1fr 1fr;gap:6px}\n  .gpy-wrap .acx-climate-tile .tile-val{font-size:16px}\n  .gpy-wrap .acx-heat-tbl td:first-child{padding-left:6px}\n  .gpy-wrap .acx-summary p{font-size:13px}\n}\n\n@media (min-width:1200px){\n  .gpy-wrap .acx-hero-stats{padding:0;gap:0}\n  .gpy-wrap .acx-hero-stat{padding:13px 22px;min-height:auto}\n  .gpy-wrap .acx-hero-stat .hs-delta{font-size:22px}\n  .gpy-wrap .acx-hero-stat .hs-name{font-size:15px}\n  .gpy-wrap .gpy-acx-head{padding:18px 22px 10px}\n  .gpy-wrap .gpy-acx-title{font-size:20px}\n  .gpy-wrap .gpy-acx-sub{font-size:13px;max-width:780px}\n  .gpy-wrap .gpy-acx-locale{margin:6px 0 0;padding:0 22px;font-size:11px}\n  .gpy-wrap .gpy-acx-timeline{padding:12px 22px 6px}\n  .gpy-wrap .gpy-acx-scen{padding:0 22px}\n  .gpy-wrap .gpy-acx-body{grid-template-columns:1.1fr 1.4fr;gap:12px;padding:0 18px 16px}\n  .gpy-wrap .gpy-acx-foot{padding:8px 18px 18px}\n  .gpy-wrap .acx-climate-tile .tile-val{font-size:20px}\n  .gpy-wrap .acx-summary{padding:16px 18px}\n  .gpy-wrap .acx-summary p{font-size:13px}\n  .gpy-wrap .acx-heat-tbl{font-size:11px}\n}\n@media (min-width:1440px){\n  .gpy-wrap .gpy-acx-body{grid-template-columns:1fr 1.55fr}\n  .gpy-wrap .acx-climate-grid{grid-template-columns:1fr 1fr 1fr 1fr}\n  .gpy-wrap .acx-climate-tile .tile-val{font-size:22px}\n  .gpy-wrap .acx-wl{grid-template-columns:1fr 1fr}\n}\n<\/style>\n\n<div class=\"gpy-acx\">\n  <div class=\"acx-chart-tip\" id=\"acx-chart-tip\" role=\"status\" aria-live=\"polite\"><\/div>\n  <!-- Print-only header + watermark. Hidden on screen; appears when the\n       user saves the page as PDF via the export button. GeoPard mark is\n       an inline SVG so it renders crisp without an external image fetch. -->\n  <div class=\"acx-print-only acx-print-header\" aria-hidden=\"true\">\n    <span class=\"ph-brand\">\n      <svg class=\"ph-logo\" viewBox=\"0 0 36 36\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" aria-hidden=\"true\">\n        <circle cx=\"18\" cy=\"18\" r=\"16\" fill=\"#145328\"\/>\n        <path d=\"M 11 22 Q 14 12 18 14 Q 22 16 25 22 Q 22 18 18 19 Q 14 20 11 22 Z\" fill=\"#86efac\"\/>\n        <path d=\"M 18 14 L 18 26\" stroke=\"#86efac\" stroke-width=\"1.2\" stroke-linecap=\"round\"\/>\n      <\/svg>\n      <span class=\"ph-word\">GeoPard<\/span>\n    <\/span>\n    <span class=\"ph-tool\">Agroclimate Future Explorer<\/span>\n  <\/div>\n  <div class=\"acx-print-only acx-print-meta\" aria-hidden=\"true\">\n    <span><strong id=\"acx-print-zone\">Zone<\/strong><\/span>\n    <span>Decade \u00b7 <strong id=\"acx-print-decade\">2050<\/strong> \u00b7 Scenario \u00b7 <strong id=\"acx-print-scen\">Open trade<\/strong><\/span>\n  <\/div>\n  <div class=\"acx-print-watermark\" aria-hidden=\"true\">GeoPard<\/div>\n\n  <!-- Compact filter bar \u2014 the three global controls (Region \u00b7 Decade \u00b7\n       Scenario) live here as clickable pills with popovers. Map-metric\n       stays inside the map card (it's map-only). -->\n  <div class=\"acx-filterbar\" role=\"toolbar\" aria-label=\"Global filters\">\n    <span class=\"acx-filterbar-label\"><span class=\"acx-scope-dot global\">Global<\/span>filters<\/span>\n    <button class=\"acx-filter-pill\" data-filter=\"region\" type=\"button\" aria-expanded=\"false\" aria-haspopup=\"true\">\n      <span class=\"pill-label\">Region<\/span>\n      <span class=\"pill-value\" id=\"acx-filter-region\">\u2014<\/span>\n      <span class=\"pill-chev\" aria-hidden=\"true\">\u25be<\/span>\n    <\/button>\n    <button class=\"acx-filter-pill\" data-filter=\"decade\" type=\"button\" aria-expanded=\"false\" aria-haspopup=\"true\">\n      <span class=\"pill-label\">Decade<\/span>\n      <span class=\"pill-value\" id=\"acx-filter-decade\">2050<\/span>\n      <span class=\"pill-chev\" aria-hidden=\"true\">\u25be<\/span>\n    <\/button>\n    <button class=\"acx-filter-pill\" data-filter=\"scenario\" type=\"button\" aria-expanded=\"false\" aria-haspopup=\"true\">\n      <span class=\"pill-label\">Scenario<\/span>\n      <span class=\"pill-value\" id=\"acx-filter-scenario\">Open trade<\/span>\n      <span class=\"pill-chev\" aria-hidden=\"true\">\u25be<\/span>\n    <\/button>\n    <button class=\"acx-filter-pill compact\" data-filter=\"units\" type=\"button\" aria-expanded=\"false\" aria-haspopup=\"true\">\n      <span class=\"pill-label\">Units<\/span>\n      <span class=\"pill-value\" id=\"acx-filter-units\">metric \u00b7 t<\/span>\n      <span class=\"pill-chev\" aria-hidden=\"true\">\u25be<\/span>\n    <\/button>\n    <button class=\"acx-filter-pill compact\" data-filter=\"currency\" type=\"button\" aria-expanded=\"false\" aria-haspopup=\"true\">\n      <span class=\"pill-label\">Currency<\/span>\n      <span class=\"pill-value\" id=\"acx-filter-currency\">$ USD<\/span>\n      <span class=\"pill-chev\" aria-hidden=\"true\">\u25be<\/span>\n    <\/button>\n    <button class=\"gpy-acx-play\" id=\"acx-play\" type=\"button\" aria-label=\"Play decade animation\" title=\"Play \/ pause through decades\">\u25b6<\/button>\n\n    <!-- Popovers \u2014 one per pill -->\n    <div class=\"acx-filter-popup\" id=\"acx-popup-region\" data-popup=\"region\" hidden>\n      <input type=\"search\" class=\"acx-popup-search\" id=\"acx-popup-region-search\" placeholder=\"Search country or zone\u2026\" aria-label=\"Search regions\">\n      <div class=\"acx-popup-list\" id=\"acx-popup-region-list\" role=\"listbox\" aria-label=\"Regions\"><\/div>\n    <\/div>\n    <div class=\"acx-filter-popup\" id=\"acx-popup-decade\" data-popup=\"decade\" hidden>\n      <div class=\"acx-popup-grid\" role=\"listbox\" aria-label=\"Decade\">\n        <button class=\"acx-popup-opt\" data-d=\"2030\" role=\"option\" aria-selected=\"false\">2030<\/button>\n        <button class=\"acx-popup-opt\" data-d=\"2040\" role=\"option\" aria-selected=\"false\">2040<\/button>\n        <button class=\"acx-popup-opt\" data-d=\"2050\" role=\"option\" aria-selected=\"true\">2050<\/button>\n        <button class=\"acx-popup-opt\" data-d=\"2060\" role=\"option\" aria-selected=\"false\">2060<\/button>\n      <\/div>\n    <\/div>\n    <div class=\"acx-filter-popup\" id=\"acx-popup-scenario\" data-popup=\"scenario\" hidden>\n      <button class=\"acx-popup-row\" data-s=\"open\" role=\"option\" aria-selected=\"true\">\n        <span class=\"row-title\">Open trade<\/span>\n        <span class=\"row-hint\">Baseline. Free-flowing exports, no shocks.<\/span>\n      <\/button>\n      <button class=\"acx-popup-row\" data-s=\"fragmented\" role=\"option\" aria-selected=\"false\">\n        <span class=\"row-title\">Bloc fragmentation<\/span>\n        <span class=\"row-hint\">Tariff walls. Volatility up, margins compress.<\/span>\n      <\/button>\n      <button class=\"acx-popup-row\" data-s=\"shock\" role=\"option\" aria-selected=\"false\">\n        <span class=\"row-title\">Export ban shock<\/span>\n        <span class=\"row-hint\">A major exporter offline. Prices spike, inputs spike harder.<\/span>\n      <\/button>\n    <\/div>\n    <div class=\"acx-filter-popup\" id=\"acx-popup-units\" data-popup=\"units\" hidden>\n      <button class=\"acx-popup-row\" data-u=\"metric\" role=\"option\" aria-selected=\"true\">\n        <span class=\"row-title\">Metric<\/span>\n        <span class=\"row-hint\">Per tonne \u00b7 per hectare<\/span>\n      <\/button>\n      <button class=\"acx-popup-row\" data-u=\"imperial\" role=\"option\" aria-selected=\"false\">\n        <span class=\"row-title\">Imperial<\/span>\n        <span class=\"row-hint\">Per bushel \/ lb \/ cwt \u00b7 per acre<\/span>\n      <\/button>\n    <\/div>\n    <div class=\"acx-filter-popup\" id=\"acx-popup-currency\" data-popup=\"currency\" hidden>\n      <button class=\"acx-popup-row\" data-c=\"USD\" role=\"option\" aria-selected=\"true\">\n        <span class=\"row-title\">$ USD<\/span>\n        <span class=\"row-hint\">US dollar (baseline)<\/span>\n      <\/button>\n      <button class=\"acx-popup-row\" data-c=\"EUR\" role=\"option\" aria-selected=\"false\">\n        <span class=\"row-title\">\u20ac EUR<\/span>\n        <span class=\"row-hint\">Euro (\u00d70.92 ref. rate)<\/span>\n      <\/button>\n      <button class=\"acx-popup-row\" data-c=\"GBP\" role=\"option\" aria-selected=\"false\">\n        <span class=\"row-title\">\u00a3 GBP<\/span>\n        <span class=\"row-hint\">Pound sterling (\u00d70.79 ref. rate)<\/span>\n      <\/button>\n    <\/div>\n  <\/div>\n\n  <div class=\"acx-hero-stats\" id=\"acx-hero-stats\">\n    <div class=\"acx-hero-stat win\" id=\"acx-hero-win\">\n      <span class=\"hs-h\"><span class=\"pip\"><\/span> Biggest gainer \u00b7 <span id=\"acx-hero-decade-w\">by 2050<\/span><\/span>\n      <span class=\"hs-name\" id=\"acx-hero-win-crop\">\u2014<\/span>\n      <span class=\"hs-delta\" id=\"acx-hero-win-delta\">\u2014<\/span>\n      <span class=\"hs-sub\" id=\"acx-hero-win-sub\">crop fit \u00b7 this zone<\/span>\n    <\/div>\n    <div class=\"acx-hero-stat lose\" id=\"acx-hero-lose\">\n      <span class=\"hs-h\"><span class=\"pip\"><\/span> Biggest loser<\/span>\n      <span class=\"hs-name\" id=\"acx-hero-lose-crop\">\u2014<\/span>\n      <span class=\"hs-delta\" id=\"acx-hero-lose-delta\">\u2014<\/span>\n      <span class=\"hs-sub\" id=\"acx-hero-lose-sub\">crop fit \u00b7 this zone<\/span>\n    <\/div>\n    <div class=\"acx-hero-stat warn\" id=\"acx-hero-clim\">\n      <span class=\"hs-h\"><span class=\"pip\"><\/span> Climate signal<\/span>\n      <span class=\"hs-name\" id=\"acx-hero-clim-label\">heat stress days<\/span>\n      <span class=\"hs-delta\" id=\"acx-hero-clim-delta\">\u2014<\/span>\n      <span class=\"hs-sub\" id=\"acx-hero-clim-sub\">vs 2020 baseline<\/span>\n    <\/div>\n  <\/div>\n\n  <div class=\"acx-mapcard\" id=\"acx-mapcard\">\n    <div class=\"acx-mapcard-head\">\n      <h3><span class=\"acx-scope-dot local\">Global view<\/span>All 62 zones \u00b7 <span class=\"head-decade\" id=\"acx-map-decade-lbl\">by 2050<\/span><\/h3>\n      <div class=\"acx-map-controls\">\n        <button class=\"acx-map-reset hidden\" id=\"acx-map-reset\" type=\"button\" aria-label=\"Fit world view\">Reset view<\/button>\n        <div class=\"acx-map-metric\" id=\"acx-map-metric\" role=\"tablist\" aria-label=\"Map metric\">\n          <button data-m=\"crop\" class=\"on\" role=\"tab\" aria-selected=\"true\">Crop shift<\/button>\n          <button data-m=\"climate\" role=\"tab\" aria-selected=\"false\">Climate<\/button>\n          <button data-m=\"trade\" role=\"tab\" aria-selected=\"false\">Trade risk<\/button>\n          <button data-m=\"invest\" role=\"tab\" aria-selected=\"false\">Investment<\/button>\n        <\/div>\n      <\/div>\n    <\/div>\n    <div class=\"acx-mapcard-globe loading\" id=\"acx-mapcard-globe\"><\/div>\n    <div class=\"acx-mapcard-scale\" id=\"acx-mapcard-scale\">\n      <span class=\"scale-label\" id=\"acx-map-scale-lo\">least<\/span>\n      <span class=\"scale-grad\" id=\"acx-map-scale-grad\"><\/span>\n      <span class=\"scale-label\" id=\"acx-map-scale-hi\">most<\/span>\n    <\/div>\n    <div class=\"acx-top-picks\" id=\"acx-top-picks\" hidden><\/div>\n  <\/div>\n\n  <div class=\"gpy-acx-body\">\n    <div class=\"gpy-acx-col\">\n      <div class=\"gpy-card\">\n        <h3>Climate \u00b7 <span class=\"acx-card-scope\" id=\"acx-snap-zone\">this zone<\/span> \u00b7 <span id=\"acx-snap-decade\">2020<\/span><\/h3>\n        <div class=\"acx-climate-grid\">\n          <div class=\"acx-climate-tile\" id=\"acx-tile-gdd\">\n            <span class=\"tile-h\">Growing-degree days<\/span>\n            <span class=\"tile-val\" id=\"acx-gdd\">\u2014<\/span>\n            <span class=\"tile-delta\" id=\"acx-gdd-d\">base \u00b7 GDD\u2081\u2080<\/span>\n            <svg class=\"tile-spark\" id=\"acx-spark-gdd\" viewBox=\"0 0 120 30\" preserveAspectRatio=\"none\" aria-hidden=\"true\"><\/svg>\n          <\/div>\n          <div class=\"acx-climate-tile\" id=\"acx-tile-season\">\n            <span class=\"tile-h\">Season length<\/span>\n            <span class=\"tile-val\" id=\"acx-season\">\u2014<\/span>\n            <span class=\"tile-delta\" id=\"acx-season-d\">base \u00b7 frost-free days<\/span>\n            <svg class=\"tile-spark\" id=\"acx-spark-season\" viewBox=\"0 0 120 30\" preserveAspectRatio=\"none\" aria-hidden=\"true\"><\/svg>\n          <\/div>\n          <div class=\"acx-climate-tile\" id=\"acx-tile-precip\">\n            <span class=\"tile-h\">Annual rainfall<\/span>\n            <span class=\"tile-val\" id=\"acx-precip\">\u2014<\/span>\n            <span class=\"tile-delta\" id=\"acx-precip-d\">base \u00b7 mm<\/span>\n            <svg class=\"tile-spark\" id=\"acx-spark-precip\" viewBox=\"0 0 120 30\" preserveAspectRatio=\"none\" aria-hidden=\"true\"><\/svg>\n          <\/div>\n          <div class=\"acx-climate-tile\" id=\"acx-tile-heat\">\n            <span class=\"tile-h\">Heat stress days<\/span>\n            <span class=\"tile-val\" id=\"acx-heat\">\u2014<\/span>\n            <span class=\"tile-delta\" id=\"acx-heat-d\">base \u00b7 days &gt;35\u00b0C<\/span>\n            <svg class=\"tile-spark\" id=\"acx-spark-heat\" viewBox=\"0 0 120 30\" preserveAspectRatio=\"none\" aria-hidden=\"true\"><\/svg>\n          <\/div>\n        <\/div>\n        <div class=\"acx-climate-zone\" id=\"acx-zone\">\n          <span>Agroclimatic zone:<\/span>\n          <span class=\"acx-zone-from\" id=\"acx-zone-from\">\u2014<\/span>\n          <span aria-hidden=\"true\">\u2192<\/span>\n          <span class=\"acx-zone-to\" id=\"acx-zone-to\">\u2014<\/span>\n        <\/div>\n      <\/div>\n\n      <div class=\"gpy-card\">\n        <h3>Zone economics \u00b7 <span class=\"acx-card-scope\" id=\"acx-econ-zone\">this zone<\/span><\/h3>\n        <div class=\"acx-zone-access\" id=\"acx-zone-access\"><\/div>\n      <\/div>\n\n      <div class=\"gpy-card\">\n        <h3>Trade risk \u00b7 top crops in <span class=\"acx-card-scope\" id=\"acx-trade-zone\">this zone<\/span><\/h3>\n        <div class=\"acx-trade\" id=\"acx-trade\"><\/div>\n      <\/div>\n    <\/div>\n\n    <div class=\"gpy-acx-col\">\n      <div class=\"gpy-card\">\n        <h3>Crops in <span class=\"acx-card-scope\" id=\"acx-wl-zone\">this zone<\/span> \u00b7 winners &amp; losers vs 2020<\/h3>\n        <div class=\"acx-wl\">\n          <div class=\"acx-wl-col win\">\n            <h4><span class=\"pip\"><\/span> Gaining<\/h4>\n            <div id=\"acx-winners\"><\/div>\n          <\/div>\n          <div class=\"acx-wl-col lose\">\n            <h4><span class=\"pip\"><\/span> Losing<\/h4>\n            <div id=\"acx-losers\"><\/div>\n          <\/div>\n        <\/div>\n        <div class=\"acx-heat\">\n          <table class=\"acx-heat-tbl\" id=\"acx-heat-tbl\"><\/table>\n          <div class=\"acx-heat-legend\">\n            <span class=\"lg-label\">Crop fit<\/span>\n            <span class=\"lg-scale\">\n              <span class=\"lg-step empty\">\u2014<\/span>\n              <span class=\"lg-step\" style=\"background:#dc2626\">0<\/span>\n              <span class=\"lg-step\" style=\"background:#ea580c\">25<\/span>\n              <span class=\"lg-step\" style=\"background:#d4a017\">50<\/span>\n              <span class=\"lg-step\" style=\"background:#65a30d\">75<\/span>\n              <span class=\"lg-step\" style=\"background:#16a34a\">100<\/span>\n            <\/span>\n            <span class=\"lg-now\" aria-hidden=\"true\"><span class=\"lg-now-ring\"><\/span>current decade<\/span>\n            <span class=\"lg-hint\">Click a row to focus a crop<\/span>\n          <\/div>\n          <div class=\"acx-cropfocus\" id=\"acx-cropfocus\" aria-live=\"polite\"><\/div>\n        <\/div>\n      <\/div>\n\n      <div class=\"acx-summary\" id=\"acx-summary\">\n        <h3>In short<\/h3>\n        <p id=\"acx-summary-text\">Loading\u2026<\/p>\n        <div class=\"summary-divider\"><\/div>\n        <p id=\"acx-summary-scenario\">Loading\u2026<\/p>\n      <\/div>\n    <\/div>\n  <\/div>\n\n  <div class=\"gpy-acx-foot\">\n    <div class=\"gpy-acx-note\">\n      <strong>What this is.<\/strong> Decade snapshots built from leading climate-model ensembles, established crop-suitability frameworks, authoritative commodity-price indices, and international trade-flow statistics. Trade scenarios use conservative price-shock magnitudes from past export-disruption events. Country-level \u2014 not field-level.\n      <br><br>\n      <strong>What it isn&#8217;t.<\/strong> A forecast. A guarantee. A field-level recommendation. For the climate trajectory of your actual fields and the rotation that fits, see the next step.\n    <\/div>\n\n    <!-- Editorial directory \u2014 internal links to curated country + crop outlook\n         pages. Helps Google discover the SEO surface and gives users a reading\n         path next to the interactive tool. -->\n    <nav class=\"acx-seo-dir\" aria-label=\"Country and crop outlooks\">\n      <div class=\"acx-seo-dir-row\">\n        <span class=\"acx-seo-dir-h\">Country outlooks<\/span>\n        <a href=\"\/agroclimate-united-states\/\">United States<\/a>\n        <a href=\"\/agroclimate-brazil\/\">Brazil<\/a>\n        <a href=\"\/agroclimate-india\/\">India<\/a>\n        <a href=\"\/agroclimate-china\/\">China<\/a>\n        <a href=\"\/agroclimate-ukraine\/\">Ukraine<\/a>\n        <a href=\"\/agroclimate-france\/\">France<\/a>\n        <a href=\"\/agroclimate-australia\/\">Australia<\/a>\n        <a href=\"\/agroclimate-argentina\/\">Argentina<\/a>\n        <a href=\"\/agroclimate-vietnam\/\">Vietnam<\/a>\n        <a href=\"\/agroclimate-indonesia\/\">Indonesia<\/a>\n      <\/div>\n      <div class=\"acx-seo-dir-row\">\n        <span class=\"acx-seo-dir-h\">Crop outlooks<\/span>\n        <a href=\"\/agroclimate-wheat\/\">Wheat<\/a>\n        <a href=\"\/agroclimate-corn\/\">Corn<\/a>\n        <a href=\"\/agroclimate-soybean\/\">Soybean<\/a>\n        <a href=\"\/agroclimate-rice\/\">Rice<\/a>\n        <a href=\"\/agroclimate-cotton\/\">Cotton<\/a>\n        <a href=\"\/agroclimate-sugarcane\/\">Sugarcane<\/a>\n        <a href=\"\/agroclimate-coffee\/\">Coffee<\/a>\n        <a href=\"\/agroclimate-cocoa\/\">Cocoa<\/a>\n        <a href=\"\/agroclimate-palm\/\">Palm<\/a>\n        <a href=\"\/agroclimate-wine\/\">Wine<\/a>\n      <\/div>\n    <\/nav>\n\n    <a class=\"gpy-acx-cta\" href=\"https:\/\/app.geopard.tech\/signup?utm_source=agroclimate-explorer&amp;utm_medium=wp-embed&amp;utm_campaign=acx\" target=\"_blank\" rel=\"noopener\">\n      Apply to your fields<span class=\"arr\">\u2192<\/span>\n    <\/a>\n    <div class=\"acx-foot-actions\">\n      <button class=\"acx-foot-btn\" id=\"acx-print-btn\" type=\"button\" aria-label=\"Save current view as PDF\">Save as PDF<\/button>\n      <a class=\"acx-foot-btn\" href=\"https:\/\/geopard.tech\/\" target=\"_blank\" rel=\"noopener\">geopard.tech<\/a>\n    <\/div>\n    <div class=\"acx-copyright\">\n      \u00a9 2026 GeoPard Agriculture \u00b7 Licensed for non-commercial reference use \u00b7 <a href=\"https:\/\/geopard.tech\/\" rel=\"noopener\">geopard.tech<\/a>\n    <\/div>\n    <!-- Print-only CTA \u2014 single GeoPard call-to-action (replaces the\n         full FAQ + integration chips bottom from the screen view) -->\n    <div class=\"acx-print-only acx-print-cta\" aria-hidden=\"true\">\n      <span class=\"cta-h\">Run this on your real fields with GeoPard<\/span>\n      <span class=\"cta-url\">app.geopard.tech \/ agroclimate-explorer<\/span>\n    <\/div>\n    <!-- Print-only footer (carried into saved PDFs) -->\n    <div class=\"acx-print-only acx-print-footer\" aria-hidden=\"true\">\n      <strong>GeoPard Agroclimate Future Explorer<\/strong> \u00b7 \u00a9 2026 GeoPard Agriculture \u00b7 geopard.tech \u00b7 Data is directional, not a forecast\n    <\/div>\n  <\/div>\n<\/div>\n\n<script nowprocket data-no-optimize=\"1\" data-no-defer=\"1\" data-no-minify=\"1\">\n(function(){\n  var root = document.getElementById('gpy-acx-root');\n  if(!root ? true : root.dataset.acxInit) return;\n  root.dataset.acxInit = '1';\n  var $ = function(s){ return root.querySelector(s); };\n  \/\/ COUNTRIES carry the meta + a list of agroclimatic zones. Big-ag countries\n  \/\/ (US, UA, BR, CN, IN, AU) carry multiple zones with distinct climates and\n  \/\/ dominant crops; small-ag countries carry a single zone. Each zone is the\n  \/\/ unit users actually select on the map \u2014 `state.zone` is the active one.\n  var COUNTRIES = {\n    US: { name:'United States', continent:'North America', units:'imperial', zones:[\n      { id:'us-cornbelt',   label:'Corn Belt',          coord:[-91,  41] },\n      { id:'us-n-plains',   label:'Northern Plains',    coord:[-100, 47] },\n      { id:'us-lake',       label:'Lake States',        coord:[-89,  45] },\n      { id:'us-northeast',  label:'Northeast',          coord:[-75,  43] },\n      { id:'us-cotton',     label:'Cotton Belt',        coord:[-86,  33] },\n      { id:'us-delta',      label:'Mississippi Delta',  coord:[-91,  34] },\n      { id:'us-s-plains',   label:'Southern Plains',    coord:[-99,  36] },\n      { id:'us-pnw',        label:'Pacific Northwest',  coord:[-119, 46] },\n      { id:'us-california', label:'California Central', coord:[-120, 37] }\n    ]},\n    CA: { name:'Canada', continent:'North America', units:'imperial', zones:[\n      { id:'ca-prairies',   label:'Canadian Prairies',  coord:[-105, 52] },\n      { id:'ca-ontario',    label:'Ontario-Quebec',     coord:[-79,  45] },\n      { id:'ca-bc',         label:'BC Coastal',         coord:[-123, 49] },\n      { id:'ca-maritimes',  label:'Maritimes',          coord:[-64,  46] }\n    ]},\n    MX: { name:'Mexico', continent:'North America', units:'metric', zones:[\n      { id:'mx-bajio',      label:'Baj\u00edo Highlands',    coord:[-101, 21] }\n    ]},\n    BR: { name:'Brazil', continent:'South America', units:'metric', zones:[\n      { id:'br-cerrado',    label:'Cerrado',            coord:[-49, -13] },\n      { id:'br-sul',        label:'Sul (south)',        coord:[-53, -28] },\n      { id:'br-sertao',     label:'Sert\u00e3o (NE)',        coord:[-40, -10] },\n      { id:'br-bahia',      label:'Bahia Coast',        coord:[-39, -14] }\n    ]},\n    AR: { name:'Argentina', continent:'South America', units:'metric', zones:[\n      { id:'ar-pampas',     label:'Pampas',             coord:[-62, -34] }\n    ]},\n    GB: { name:'United Kingdom', continent:'Europe', units:'metric', zones:[\n      { id:'gb-atlantic',   label:'Atlantic Maritime',  coord:[ -2,  54] }\n    ]},\n    FR: { name:'France', continent:'Europe', units:'metric', zones:[\n      { id:'fr-atlantic',   label:'Atlantic Temperate', coord:[  2,  47] }\n    ]},\n    DE: { name:'Germany', continent:'Europe', units:'metric', zones:[\n      { id:'de-atl-cont',   label:'Atlantic \/ Continental', coord:[ 10,  51] }\n    ]},\n    ES: { name:'Spain', continent:'Europe', units:'metric', zones:[\n      { id:'es-med',        label:'Mediterranean',      coord:[ -4,  40] }\n    ]},\n    IT: { name:'Italy', continent:'Europe', units:'metric', zones:[\n      { id:'it-med',        label:'Mediterranean',      coord:[ 13,  42] }\n    ]},\n    PL: { name:'Poland', continent:'Europe', units:'metric', zones:[\n      { id:'pl-cee',        label:'Continental E. Europe', coord:[ 19, 52] }\n    ]},\n    UA: { name:'Ukraine', continent:'Europe', units:'metric', zones:[\n      { id:'ua-polissia',       label:'Polissia',         coord:[ 28,  51] },\n      { id:'ua-carpathian',     label:'Carpathian',       coord:[ 24,  48.5] },\n      { id:'ua-podillia',       label:'Podillia',         coord:[ 28,  49.3] },\n      { id:'ua-forest-steppe',  label:'Forest-Steppe',    coord:[ 32,  49.5] },\n      { id:'ua-steppe',         label:'Steppe',           coord:[ 35,  47] },\n      { id:'ua-steppe-south',   label:'Black Sea Coast',  coord:[ 32,  46.5] }\n    ]},\n    RO: { name:'Romania', continent:'Europe', units:'metric', zones:[\n      { id:'ro-cee',        label:'Continental E. Europe', coord:[ 25, 46] }\n    ]},\n    HU: { name:'Hungary', continent:'Europe', units:'metric', zones:[\n      { id:'hu-cee',        label:'Continental E. Europe', coord:[ 19, 47] }\n    ]},\n    KZ: { name:'Kazakhstan', continent:'Asia', units:'metric', zones:[\n      { id:'kz-steppe',     label:'Kazakh Steppe',      coord:[ 67,  50] }\n    ]},\n    IN: { name:'India', continent:'Asia', units:'metric', zones:[\n      { id:'in-indo-gangetic', label:'Indo-Gangetic Plain', coord:[ 78, 27] },\n      { id:'in-deccan',        label:'Deccan Plateau',      coord:[ 78, 18] }\n    ]},\n    CN: { name:'China', continent:'Asia', units:'metric', zones:[\n      { id:'cn-n-plain',    label:'North China Plain',  coord:[115,  35] },\n      { id:'cn-northeast',  label:'Northeast China',    coord:[126,  46] }\n    ]},\n    ZA: { name:'South Africa', continent:'Africa', units:'metric', zones:[\n      { id:'za-highveld',   label:'Highveld',           coord:[ 27, -27] },\n      { id:'za-cape',       label:'Western Cape',       coord:[ 19, -33] }\n    ]},\n    EG: { name:'Egypt', continent:'Africa', units:'metric', zones:[\n      { id:'eg-nile',       label:'Nile Delta',         coord:[ 31,  30.5] }\n    ]},\n    MA: { name:'Morocco', continent:'Africa', units:'metric', zones:[\n      { id:'ma-atlas',      label:'Atlas \/ Atlantic',   coord:[ -7,  32] }\n    ]},\n    ET: { name:'Ethiopia', continent:'Africa', units:'metric', zones:[\n      { id:'et-highlands',  label:'Highlands',          coord:[ 38,   9] }\n    ]},\n    KE: { name:'Kenya', continent:'Africa', units:'metric', zones:[\n      { id:'ke-highlands',  label:'Highlands',          coord:[ 37,  -0.5] }\n    ]},\n    NG: { name:'Nigeria', continent:'Africa', units:'metric', zones:[\n      { id:'ng-savanna',    label:'Savanna belt',       coord:[  8,  10] }\n    ]},\n    CI: { name:'C\u00f4te d\\'Ivoire', continent:'Africa', units:'metric', zones:[\n      { id:'ci-cocoa',      label:'Cocoa belt',         coord:[ -6,   7] }\n    ]},\n    ZM: { name:'Zambia', continent:'Africa', units:'metric', zones:[\n      { id:'zm-commercial', label:'Commercial belt',    coord:[ 28, -14] }\n    ]},\n    AU: { name:'Australia', continent:'Oceania', units:'metric', zones:[\n      { id:'au-wa-wheat',   label:'WA Wheatbelt',       coord:[117, -32] },\n      { id:'au-murray',     label:'Murray-Darling',     coord:[145, -34] },\n      { id:'au-qld',        label:'Queensland Coastal', coord:[148, -22] },\n      { id:'au-tasmania',   label:'Tasmania',           coord:[147, -42] },\n      { id:'au-north',      label:'Tropical North',     coord:[133, -15] }\n    ]},\n    NZ: { name:'New Zealand', continent:'Oceania', units:'metric', zones:[\n      { id:'nz-temperate',  label:'Temperate Oceanic',  coord:[174, -41] }\n    ]},\n    IL: { name:'Israel', continent:'Asia', units:'metric', zones:[\n      { id:'il-coastal',    label:'Mediterranean coast',coord:[ 35,  32] }\n    ]},\n    VN: { name:'Vietnam', continent:'Asia', units:'metric', zones:[\n      { id:'vn-central',    label:'Central Highlands + Mekong', coord:[108,  14] }\n    ]},\n    ID: { name:'Indonesia', continent:'Asia', units:'metric', zones:[\n      { id:'id-sumatra',    label:'Sumatra \/ palm belt',coord:[101,  -1] }\n    ]},\n    TR: { name:'Turkey', continent:'Asia', units:'metric', zones:[\n      { id:'tr-anatolia',   label:'Anatolian Plateau',  coord:[ 35,  39] }\n    ]},\n    TH: { name:'Thailand', continent:'Asia', units:'metric', zones:[\n      { id:'th-central',    label:'Central Plain',      coord:[101,  14] }\n    ]},\n    PK: { name:'Pakistan', continent:'Asia', units:'metric', zones:[\n      { id:'pk-indus',      label:'Indus Plain',        coord:[ 73,  30] }\n    ]},\n    CL: { name:'Chile', continent:'South America', units:'metric', zones:[\n      { id:'cl-central',    label:'Central Valley',     coord:[-71, -34] }\n    ]},\n    CO: { name:'Colombia', continent:'South America', units:'metric', zones:[\n      { id:'co-andean',     label:'Andean coffee belt', coord:[-75,   5] }\n    ]},\n    BD: { name:'Bangladesh', continent:'Asia', units:'metric', zones:[\n      { id:'bd-delta',      label:'Ganges Delta',       coord:[ 90,  23] }\n    ]}\n  };\n  var CONTINENT_ORDER = ['North America','South America','Europe','Asia','Africa','Oceania'];\n  \/\/ Derived: flat lookup zone id \u2192 { id, label, coord, country, countryCode, units, continent }\n  var ZONES = {};\n  (function(){\n    var cks = Object.keys(COUNTRIES);\n    for(var ci=0; ci<cks.length; ci++){\n      var cc = cks[ci];\n      var info = COUNTRIES[cc];\n      for(var zi=0; zi<info.zones.length; zi++){\n        var z = info.zones[zi];\n        ZONES[z.id] = {\n          id: z.id,\n          label: z.label,\n          coord: z.coord,\n          country: info.name,\n          countryCode: cc,\n          units: info.units,\n          continent: info.continent\n        };\n      }\n    }\n  })();\n  \/\/ Alias map for CLIMATE\/SUIT lookups \u2014 keeps the existing keys (which are\n  \/\/ human-readable zone names) backwards-compatible while new sub-zones get\n  \/\/ their own data. The helpers below resolve a zone id to its dataset.\n  var ZONE_ALIAS = {\n    'us-cornbelt':      'Continental Cornbelt',\n    'us-n-plains':      'Northern Plains \/ Prairies',\n    'ca-prairies':      'Northern Plains \/ Prairies',\n    'mx-bajio':         'Subtropical Highlands',\n    'br-cerrado':       'Subtropical Cerrado',\n    'ar-pampas':        'Pampas',\n    'gb-atlantic':      'Atlantic Maritime',\n    'fr-atlantic':      'Atlantic Temperate',\n    'de-atl-cont':      'Atlantic \/ Continental',\n    'es-med':           'Mediterranean',\n    'it-med':           'Mediterranean',\n    'pl-cee':           'Continental Eastern Europe',\n    'ro-cee':           'Continental Eastern Europe',\n    'hu-cee':           'Continental Eastern Europe',\n    'kz-steppe':        'Continental Steppe',\n    'ua-steppe':        'Continental Steppe',\n    'za-highveld':      'Subtropical Highveld',\n    'in-indo-gangetic': 'Monsoon Tropical',\n    'cn-n-plain':       'Continental \/ Monsoon',\n    'au-wa-wheat':      'Mediterranean \/ Semi-arid',\n    'nz-temperate':     'Temperate Oceanic'\n  };\n  function climateForZone(zoneId){\n    if(CLIMATE[zoneId]) return CLIMATE[zoneId];\n    var a = ZONE_ALIAS[zoneId];\n    return a ? CLIMATE[a] : null;\n  }\n  function suitForZone(zoneId){\n    if(SUIT[zoneId]) return SUIT[zoneId];\n    var a = ZONE_ALIAS[zoneId];\n    return a ? SUIT[a] : null;\n  }\n  var CLIMATE = {\n    'Continental Cornbelt': {\n      2020:{gdd:2800,season:185,precip:920,heat:8 ,zone:'Continental Cornbelt'},\n      2030:{gdd:2880,season:192,precip:925,heat:14,zone:'Continental Cornbelt'},\n      2040:{gdd:2960,season:198,precip:930,heat:22,zone:'Warm-Continental Cornbelt'},\n      2050:{gdd:3050,season:205,precip:935,heat:32,zone:'Warm-Continental Cornbelt'},\n      2060:{gdd:3140,season:212,precip:935,heat:44,zone:'Humid Subtropical (south) \/ Warm Cornbelt (north)'}\n    },\n    'Northern Plains \/ Prairies': {\n      2020:{gdd:1900,season:130,precip:430,heat:3 ,zone:'Northern Plains \/ Prairies'},\n      2030:{gdd:2010,season:138,precip:445,heat:5 ,zone:'Northern Plains \/ Prairies'},\n      2040:{gdd:2120,season:147,precip:460,heat:9 ,zone:'Northern Plains \/ Prairies'},\n      2050:{gdd:2240,season:156,precip:475,heat:14,zone:'Warming Prairies \u2192 Cornbelt analog'},\n      2060:{gdd:2360,season:165,precip:485,heat:20,zone:'Warming Prairies \u2192 Cornbelt analog'}\n    },\n    'Subtropical Highlands': {\n      2020:{gdd:3200,season:300,precip:680,heat:18,zone:'Subtropical Highlands'},\n      2030:{gdd:3280,season:305,precip:660,heat:26,zone:'Subtropical Highlands'},\n      2040:{gdd:3360,season:310,precip:640,heat:36,zone:'Drier Subtropical Highlands'},\n      2050:{gdd:3450,season:315,precip:620,heat:48,zone:'Drier Subtropical Highlands'},\n      2060:{gdd:3540,season:320,precip:600,heat:62,zone:'Semi-arid Highlands'}\n    },\n    'Subtropical Cerrado': {\n      2020:{gdd:3800,season:340,precip:1450,heat:45,zone:'Subtropical Cerrado'},\n      2030:{gdd:3870,season:342,precip:1430,heat:58,zone:'Subtropical Cerrado'},\n      2040:{gdd:3940,season:344,precip:1410,heat:74,zone:'Drier Cerrado'},\n      2050:{gdd:4020,season:345,precip:1390,heat:92,zone:'Drier Cerrado'},\n      2060:{gdd:4100,season:345,precip:1370,heat:112,zone:'Cerrado \u2192 Semi-arid transition'}\n    },\n    'Pampas': {\n      2020:{gdd:2400,season:240,precip:880,heat:18,zone:'Pampas'},\n      2030:{gdd:2460,season:245,precip:890,heat:24,zone:'Pampas'},\n      2040:{gdd:2520,season:250,precip:900,heat:32,zone:'Warm Pampas'},\n      2050:{gdd:2580,season:255,precip:910,heat:42,zone:'Warm Pampas'},\n      2060:{gdd:2640,season:260,precip:920,heat:54,zone:'Warm Pampas \u2192 Subtropical transition'}\n    },\n    'Atlantic Maritime': {\n      2020:{gdd:1500,season:230,precip:1100,heat:1 ,zone:'Atlantic Maritime'},\n      2030:{gdd:1560,season:236,precip:1110,heat:2 ,zone:'Atlantic Maritime'},\n      2040:{gdd:1620,season:243,precip:1115,heat:4 ,zone:'Warming Atlantic Maritime'},\n      2050:{gdd:1685,season:250,precip:1120,heat:7 ,zone:'Warming Atlantic Maritime'},\n      2060:{gdd:1750,season:257,precip:1125,heat:11,zone:'Atlantic Maritime \u2192 Atlantic Temperate'}\n    },\n    'Atlantic Temperate': {\n      2020:{gdd:2000,season:240,precip:780,heat:5 ,zone:'Atlantic Temperate'},\n      2030:{gdd:2060,season:246,precip:770,heat:9 ,zone:'Atlantic Temperate'},\n      2040:{gdd:2125,season:253,precip:760,heat:15,zone:'Warming Atlantic Temperate'},\n      2050:{gdd:2195,season:260,precip:750,heat:23,zone:'Warming Atlantic Temperate'},\n      2060:{gdd:2265,season:268,precip:740,heat:33,zone:'Atlantic Temperate \u2192 Mediterranean analog'}\n    },\n    'Atlantic \/ Continental': {\n      2020:{gdd:1900,season:225,precip:700,heat:4 ,zone:'Atlantic \/ Continental'},\n      2030:{gdd:1970,season:232,precip:695,heat:7 ,zone:'Atlantic \/ Continental'},\n      2040:{gdd:2045,season:240,precip:685,heat:13,zone:'Warming Atlantic \/ Continental'},\n      2050:{gdd:2125,season:248,precip:675,heat:21,zone:'Warming Atlantic \/ Continental'},\n      2060:{gdd:2205,season:256,precip:660,heat:30,zone:'Atlantic \/ Continental \u2192 Atlantic Temperate'}\n    },\n    'Mediterranean': {\n      2020:{gdd:2800,season:260,precip:570,heat:35,zone:'Mediterranean'},\n      2030:{gdd:2870,season:265,precip:545,heat:48,zone:'Mediterranean'},\n      2040:{gdd:2945,season:270,precip:520,heat:64,zone:'Drier Mediterranean'},\n      2050:{gdd:3020,season:275,precip:495,heat:82,zone:'Drier Mediterranean'},\n      2060:{gdd:3100,season:280,precip:470,heat:104,zone:'Mediterranean \u2192 Semi-arid'}\n    },\n    'Mediterranean \/ Semi-arid': {\n      2020:{gdd:3000,season:280,precip:420,heat:32,zone:'Mediterranean \/ Semi-arid'},\n      2030:{gdd:3075,season:285,precip:405,heat:44,zone:'Mediterranean \/ Semi-arid'},\n      2040:{gdd:3155,season:290,precip:390,heat:60,zone:'Drier Semi-arid'},\n      2050:{gdd:3230,season:295,precip:375,heat:78,zone:'Drier Semi-arid'},\n      2060:{gdd:3310,season:300,precip:360,heat:100,zone:'Semi-arid \u2192 Arid transition'}\n    },\n    'Continental Eastern Europe': {\n      2020:{gdd:2200,season:185,precip:600,heat:8 ,zone:'Continental Eastern Europe'},\n      2030:{gdd:2275,season:192,precip:595,heat:13,zone:'Continental Eastern Europe'},\n      2040:{gdd:2355,season:199,precip:585,heat:21,zone:'Warming Continental EE'},\n      2050:{gdd:2435,season:206,precip:575,heat:31,zone:'Warming Continental EE'},\n      2060:{gdd:2515,season:213,precip:560,heat:44,zone:'Continental EE \u2192 Atlantic Temperate analog'}\n    },\n    'Continental Steppe': {\n      2020:{gdd:2400,season:175,precip:480,heat:14,zone:'Continental Steppe'},\n      2030:{gdd:2485,season:182,precip:470,heat:21,zone:'Continental Steppe'},\n      2040:{gdd:2575,season:189,precip:455,heat:31,zone:'Drier Steppe'},\n      2050:{gdd:2665,season:196,precip:440,heat:43,zone:'Drier Steppe'},\n      2060:{gdd:2755,season:203,precip:420,heat:58,zone:'Steppe \u2192 Semi-arid transition'}\n    },\n    'Subtropical Highveld': {\n      2020:{gdd:2400,season:280,precip:680,heat:22,zone:'Subtropical Highveld'},\n      2030:{gdd:2465,season:283,precip:660,heat:30,zone:'Subtropical Highveld'},\n      2040:{gdd:2535,season:286,precip:640,heat:40,zone:'Drier Highveld'},\n      2050:{gdd:2605,season:289,precip:620,heat:52,zone:'Drier Highveld'},\n      2060:{gdd:2680,season:292,precip:600,heat:68,zone:'Highveld \u2192 Semi-arid transition'}\n    },\n    'Monsoon Tropical': {\n      2020:{gdd:4000,season:340,precip:1100,heat:55,zone:'Monsoon Tropical'},\n      2030:{gdd:4055,season:342,precip:1115,heat:70,zone:'Monsoon Tropical'},\n      2040:{gdd:4115,season:343,precip:1130,heat:88,zone:'Heat-stressed Monsoon'},\n      2050:{gdd:4175,season:344,precip:1140,heat:108,zone:'Heat-stressed Monsoon'},\n      2060:{gdd:4235,season:345,precip:1150,heat:132,zone:'Critical-heat Monsoon'}\n    },\n    'Continental \/ Monsoon': {\n      2020:{gdd:3200,season:230,precip:780,heat:28,zone:'Continental \/ Monsoon'},\n      2030:{gdd:3270,season:236,precip:790,heat:38,zone:'Continental \/ Monsoon'},\n      2040:{gdd:3345,season:242,precip:800,heat:52,zone:'Warming Continental \/ Monsoon'},\n      2050:{gdd:3420,season:248,precip:805,heat:68,zone:'Warming Continental \/ Monsoon'},\n      2060:{gdd:3495,season:254,precip:810,heat:88,zone:'Subtropical Monsoon analog'}\n    },\n    'Temperate Oceanic': {\n      2020:{gdd:1800,season:255,precip:1150,heat:2 ,zone:'Temperate Oceanic'},\n      2030:{gdd:1860,season:261,precip:1160,heat:3 ,zone:'Temperate Oceanic'},\n      2040:{gdd:1925,season:268,precip:1170,heat:5 ,zone:'Warming Temperate Oceanic'},\n      2050:{gdd:1990,season:275,precip:1180,heat:9 ,zone:'Warming Temperate Oceanic'},\n      2060:{gdd:2055,season:282,precip:1190,heat:14,zone:'Temperate Oceanic \u2192 Atlantic Temperate analog'}\n    },\n    'us-cotton': {\n      2020:{gdd:3400,season:230,precip:1300,heat:28,zone:'US Cotton Belt'},\n      2030:{gdd:3475,season:235,precip:1290,heat:38,zone:'US Cotton Belt'},\n      2040:{gdd:3555,season:240,precip:1280,heat:50,zone:'Hotter Cotton Belt'},\n      2050:{gdd:3635,season:245,precip:1270,heat:65,zone:'Hotter Cotton Belt'},\n      2060:{gdd:3720,season:250,precip:1260,heat:82,zone:'Cotton Belt \u2192 Humid Subtropical heat-stressed'}\n    },\n    'us-pnw': {\n      2020:{gdd:2100,season:200,precip:600,heat:5 ,zone:'US Pacific Northwest'},\n      2030:{gdd:2175,season:207,precip:595,heat:8 ,zone:'US Pacific Northwest'},\n      2040:{gdd:2255,season:214,precip:585,heat:14,zone:'Warming Pacific Northwest'},\n      2050:{gdd:2335,season:221,precip:575,heat:22,zone:'Warming Pacific Northwest'},\n      2060:{gdd:2420,season:228,precip:560,heat:32,zone:'PNW \u2192 Mediterranean analog'}\n    },\n    'us-california': {\n      2020:{gdd:3500,season:320,precip:350,heat:60,zone:'California Central Valley'},\n      2030:{gdd:3585,season:324,precip:340,heat:75,zone:'California Central Valley'},\n      2040:{gdd:3670,season:328,precip:325,heat:93,zone:'Drier Central Valley'},\n      2050:{gdd:3755,season:332,precip:310,heat:115,zone:'Drier Central Valley'},\n      2060:{gdd:3840,season:336,precip:295,heat:140,zone:'Central Valley \u2192 Semi-arid Mediterranean'}\n    },\n    'br-sul': {\n      2020:{gdd:3100,season:320,precip:1600,heat:22,zone:'Brazil Sul'},\n      2030:{gdd:3170,season:323,precip:1610,heat:30,zone:'Brazil Sul'},\n      2040:{gdd:3245,season:326,precip:1615,heat:42,zone:'Warm Brazil Sul'},\n      2050:{gdd:3320,season:329,precip:1620,heat:55,zone:'Warm Brazil Sul'},\n      2060:{gdd:3400,season:332,precip:1620,heat:70,zone:'Brazil Sul \u2192 Subtropical \/ Cerrado transition'}\n    },\n    'ua-polissia': {\n      2020:{gdd:1800,season:160,precip:600,heat:4 ,zone:'Ukrainian Polissia'},\n      2030:{gdd:1880,season:167,precip:605,heat:7 ,zone:'Ukrainian Polissia'},\n      2040:{gdd:1960,season:174,precip:605,heat:12,zone:'Warming Polissia'},\n      2050:{gdd:2045,season:181,precip:600,heat:19,zone:'Warming Polissia'},\n      2060:{gdd:2130,season:188,precip:590,heat:28,zone:'Polissia \u2192 Forest-Steppe analog'}\n    },\n    'ua-forest-steppe': {\n      2020:{gdd:2100,season:175,precip:550,heat:8 ,zone:'Ukrainian Forest-Steppe'},\n      2030:{gdd:2185,season:182,precip:545,heat:13,zone:'Ukrainian Forest-Steppe'},\n      2040:{gdd:2275,season:189,precip:535,heat:22,zone:'Warming Forest-Steppe'},\n      2050:{gdd:2365,season:196,precip:520,heat:33,zone:'Warming Forest-Steppe'},\n      2060:{gdd:2455,season:203,precip:500,heat:47,zone:'Forest-Steppe \u2192 Steppe transition'}\n    },\n    'cn-northeast': {\n      2020:{gdd:2100,season:165,precip:500,heat:6 ,zone:'Northeast China'},\n      2030:{gdd:2200,season:174,precip:510,heat:10,zone:'Northeast China'},\n      2040:{gdd:2305,season:183,precip:520,heat:17,zone:'Warming Northeast China'},\n      2050:{gdd:2410,season:192,precip:525,heat:26,zone:'Warming Northeast China'},\n      2060:{gdd:2515,season:201,precip:525,heat:38,zone:'Northeast China \u2192 North China Plain analog'}\n    },\n    'in-deccan': {\n      2020:{gdd:3800,season:280,precip:750,heat:65,zone:'Deccan Plateau'},\n      2030:{gdd:3870,season:283,precip:740,heat:82,zone:'Deccan Plateau'},\n      2040:{gdd:3945,season:286,precip:725,heat:103,zone:'Drier Deccan Plateau'},\n      2050:{gdd:4020,season:289,precip:710,heat:128,zone:'Drier Deccan Plateau'},\n      2060:{gdd:4095,season:292,precip:695,heat:156,zone:'Deccan \u2192 Semi-arid critical-heat'}\n    },\n    'au-murray': {\n      2020:{gdd:2700,season:280,precip:480,heat:25,zone:'Murray-Darling Basin'},\n      2030:{gdd:2790,season:286,precip:465,heat:36,zone:'Murray-Darling Basin'},\n      2040:{gdd:2880,season:292,precip:445,heat:52,zone:'Drier Murray-Darling'},\n      2050:{gdd:2970,season:298,precip:425,heat:70,zone:'Drier Murray-Darling'},\n      2060:{gdd:3060,season:304,precip:405,heat:92,zone:'Murray-Darling \u2192 Semi-arid transition'}\n    },\n    'us-delta': {\n      2020:{gdd:3500,season:240,precip:1400,heat:30,zone:'Mississippi Delta'},\n      2030:{gdd:3580,season:245,precip:1395,heat:40,zone:'Mississippi Delta'},\n      2040:{gdd:3660,season:250,precip:1385,heat:52,zone:'Hotter Delta'},\n      2050:{gdd:3745,season:255,precip:1375,heat:67,zone:'Hotter Delta'},\n      2060:{gdd:3835,season:260,precip:1365,heat:84,zone:'Critical-heat Delta'}\n    },\n    'us-s-plains': {\n      2020:{gdd:2900,season:220,precip:600,heat:25,zone:'Southern Plains'},\n      2030:{gdd:2985,season:225,precip:580,heat:35,zone:'Southern Plains'},\n      2040:{gdd:3075,season:230,precip:555,heat:48,zone:'Drier Southern Plains'},\n      2050:{gdd:3165,season:235,precip:530,heat:62,zone:'Drier Southern Plains'},\n      2060:{gdd:3255,season:240,precip:505,heat:78,zone:'Southern Plains \u2192 Semi-arid'}\n    },\n    'us-lake': {\n      2020:{gdd:2300,season:165,precip:800,heat:6 ,zone:'Lake States'},\n      2030:{gdd:2395,season:173,precip:800,heat:10,zone:'Lake States'},\n      2040:{gdd:2490,season:181,precip:795,heat:16,zone:'Warming Lake States'},\n      2050:{gdd:2590,season:189,precip:790,heat:24,zone:'Warming Lake States'},\n      2060:{gdd:2685,season:197,precip:780,heat:34,zone:'Lake States \u2192 Cornbelt analog'}\n    },\n    'us-northeast': {\n      2020:{gdd:2200,season:175,precip:1100,heat:5 ,zone:'Northeast'},\n      2030:{gdd:2290,season:182,precip:1110,heat:8 ,zone:'Northeast'},\n      2040:{gdd:2380,season:189,precip:1115,heat:14,zone:'Warming Northeast'},\n      2050:{gdd:2475,season:196,precip:1120,heat:22,zone:'Warming Northeast'},\n      2060:{gdd:2565,season:203,precip:1120,heat:31,zone:'Northeast \u2192 Mid-Atlantic analog'}\n    },\n    'ca-ontario': {\n      2020:{gdd:2150,season:165,precip:900,heat:4 ,zone:'Ontario-Quebec'},\n      2030:{gdd:2240,season:172,precip:905,heat:7 ,zone:'Ontario-Quebec'},\n      2040:{gdd:2335,season:179,precip:905,heat:12,zone:'Warming Ontario-Quebec'},\n      2050:{gdd:2430,season:186,precip:905,heat:19,zone:'Warming Ontario-Quebec'},\n      2060:{gdd:2520,season:193,precip:900,heat:27,zone:'Ontario \u2192 US Lake States analog'}\n    },\n    'ca-bc': {\n      2020:{gdd:1700,season:240,precip:1200,heat:1 ,zone:'BC Coastal'},\n      2030:{gdd:1755,season:246,precip:1210,heat:2 ,zone:'BC Coastal'},\n      2040:{gdd:1815,season:253,precip:1215,heat:4 ,zone:'Warming BC Coastal'},\n      2050:{gdd:1875,season:260,precip:1220,heat:7 ,zone:'Warming BC Coastal'},\n      2060:{gdd:1935,season:267,precip:1225,heat:11,zone:'BC Coastal \u2192 Willamette analog'}\n    },\n    'ca-maritimes': {\n      2020:{gdd:1600,season:165,precip:1100,heat:2 ,zone:'Maritimes'},\n      2030:{gdd:1665,season:172,precip:1110,heat:3 ,zone:'Maritimes'},\n      2040:{gdd:1735,season:179,precip:1115,heat:6 ,zone:'Warming Maritimes'},\n      2050:{gdd:1800,season:186,precip:1120,heat:9 ,zone:'Warming Maritimes'},\n      2060:{gdd:1870,season:193,precip:1120,heat:14,zone:'Maritimes \u2192 Northeast US analog'}\n    },\n    'br-sertao': {\n      2020:{gdd:4500,season:365,precip:600,heat:80,zone:'Sert\u00e3o (semi-arid)'},\n      2030:{gdd:4570,season:365,precip:580,heat:95,zone:'Sert\u00e3o (semi-arid)'},\n      2040:{gdd:4640,season:365,precip:555,heat:115,zone:'Drier Sert\u00e3o'},\n      2050:{gdd:4710,season:365,precip:530,heat:138,zone:'Drier Sert\u00e3o'},\n      2060:{gdd:4780,season:365,precip:505,heat:165,zone:'Sert\u00e3o \u2192 Critical-arid'}\n    },\n    'br-bahia': {\n      2020:{gdd:4100,season:350,precip:1200,heat:50,zone:'Bahia Coast'},\n      2030:{gdd:4170,season:352,precip:1200,heat:62,zone:'Bahia Coast'},\n      2040:{gdd:4240,season:354,precip:1190,heat:78,zone:'Warm Bahia Coast'},\n      2050:{gdd:4315,season:356,precip:1180,heat:96,zone:'Warm Bahia Coast'},\n      2060:{gdd:4390,season:358,precip:1170,heat:118,zone:'Bahia \u2192 Heat-stressed tropics'}\n    },\n    'ua-carpathian': {\n      2020:{gdd:1700,season:170,precip:1100,heat:3 ,zone:'Ukrainian Carpathian'},\n      2030:{gdd:1770,season:177,precip:1105,heat:5 ,zone:'Ukrainian Carpathian'},\n      2040:{gdd:1845,season:184,precip:1105,heat:9 ,zone:'Warming Carpathian'},\n      2050:{gdd:1920,season:191,precip:1100,heat:14,zone:'Warming Carpathian'},\n      2060:{gdd:1995,season:198,precip:1090,heat:21,zone:'Carpathian \u2192 Forest-Steppe analog'}\n    },\n    'ua-steppe-south': {\n      2020:{gdd:2700,season:200,precip:420,heat:22,zone:'Ukrainian Black Sea Coast'},\n      2030:{gdd:2785,season:207,precip:410,heat:30,zone:'Ukrainian Black Sea Coast'},\n      2040:{gdd:2875,season:214,precip:395,heat:41,zone:'Drier Black Sea Coast'},\n      2050:{gdd:2965,season:221,precip:380,heat:54,zone:'Drier Black Sea Coast'},\n      2060:{gdd:3055,season:228,precip:360,heat:70,zone:'Black Sea Coast \u2192 Mediterranean analog'}\n    },\n    'ua-podillia': {\n      2020:{gdd:2000,season:175,precip:600,heat:7 ,zone:'Ukrainian Podillia'},\n      2030:{gdd:2080,season:182,precip:595,heat:11,zone:'Ukrainian Podillia'},\n      2040:{gdd:2165,season:189,precip:585,heat:17,zone:'Warming Podillia'},\n      2050:{gdd:2250,season:196,precip:570,heat:26,zone:'Warming Podillia'},\n      2060:{gdd:2335,season:203,precip:550,heat:38,zone:'Podillia \u2192 Forest-Steppe South'}\n    },\n    'au-qld': {\n      2020:{gdd:3800,season:340,precip:1300,heat:35,zone:'Queensland Coastal'},\n      2030:{gdd:3875,season:343,precip:1305,heat:45,zone:'Queensland Coastal'},\n      2040:{gdd:3950,season:346,precip:1300,heat:58,zone:'Warm Queensland Coastal'},\n      2050:{gdd:4030,season:349,precip:1295,heat:73,zone:'Warm Queensland Coastal'},\n      2060:{gdd:4110,season:352,precip:1290,heat:91,zone:'Queensland \u2192 Heat-stressed subtropical'}\n    },\n    'au-tasmania': {\n      2020:{gdd:1700,season:240,precip:1100,heat:1 ,zone:'Tasmania'},\n      2030:{gdd:1755,season:246,precip:1110,heat:2 ,zone:'Tasmania'},\n      2040:{gdd:1815,season:253,precip:1115,heat:4 ,zone:'Warming Tasmania'},\n      2050:{gdd:1875,season:260,precip:1120,heat:7 ,zone:'Warming Tasmania'},\n      2060:{gdd:1935,season:267,precip:1125,heat:11,zone:'Tasmania \u2192 S Island NZ analog'}\n    },\n    'au-north': {\n      2020:{gdd:4500,season:365,precip:1000,heat:90,zone:'Australian Tropical North'},\n      2030:{gdd:4570,season:365,precip:990,heat:105,zone:'Australian Tropical North'},\n      2040:{gdd:4640,season:365,precip:975,heat:122,zone:'Hotter Tropical North'},\n      2050:{gdd:4710,season:365,precip:960,heat:143,zone:'Hotter Tropical North'},\n      2060:{gdd:4780,season:365,precip:945,heat:168,zone:'Tropical North \u2192 Critical-heat tropics'}\n    },\n    'za-cape': {\n      2020:{gdd:2400,season:280,precip:500,heat:22,zone:'Western Cape (Mediterranean)'},\n      2030:{gdd:2475,season:285,precip:485,heat:32,zone:'Western Cape (Mediterranean)'},\n      2040:{gdd:2555,season:290,precip:465,heat:45,zone:'Drier Western Cape'},\n      2050:{gdd:2635,season:295,precip:445,heat:60,zone:'Drier Western Cape'},\n      2060:{gdd:2715,season:300,precip:425,heat:78,zone:'Cape \u2192 Semi-arid Mediterranean'}\n    },\n    'eg-nile': {\n      2020:{gdd:3600,season:280,precip:100,heat:65,zone:'Nile Delta (irrigated arid)'},\n      2030:{gdd:3680,season:283,precip:95,heat:80,zone:'Nile Delta (irrigated arid)'},\n      2040:{gdd:3760,season:286,precip:90,heat:98,zone:'Hotter Nile Delta'},\n      2050:{gdd:3840,season:289,precip:85,heat:119,zone:'Hotter Nile Delta'},\n      2060:{gdd:3920,season:292,precip:80,heat:144,zone:'Nile Delta \u2192 Critical-heat arid'}\n    },\n    'ma-atlas': {\n      2020:{gdd:2800,season:250,precip:400,heat:35,zone:'Atlas Mediterranean'},\n      2030:{gdd:2880,season:254,precip:385,heat:47,zone:'Atlas Mediterranean'},\n      2040:{gdd:2965,season:258,precip:365,heat:62,zone:'Drier Atlas'},\n      2050:{gdd:3050,season:262,precip:345,heat:80,zone:'Drier Atlas'},\n      2060:{gdd:3135,season:266,precip:325,heat:100,zone:'Atlas \u2192 Semi-arid Mediterranean'}\n    },\n    'et-highlands': {\n      2020:{gdd:2800,season:220,precip:900,heat:8 ,zone:'Ethiopian Highlands (cool tropical)'},\n      2030:{gdd:2880,season:224,precip:895,heat:12,zone:'Ethiopian Highlands (cool tropical)'},\n      2040:{gdd:2965,season:228,precip:885,heat:19,zone:'Warming Ethiopian Highlands'},\n      2050:{gdd:3050,season:232,precip:870,heat:28,zone:'Warming Ethiopian Highlands'},\n      2060:{gdd:3135,season:236,precip:855,heat:40,zone:'Highlands \u2192 Subtropical analog'}\n    },\n    'ke-highlands': {\n      2020:{gdd:2600,season:240,precip:1100,heat:6 ,zone:'Kenyan Highlands (cool tropical)'},\n      2030:{gdd:2680,season:244,precip:1095,heat:9 ,zone:'Kenyan Highlands (cool tropical)'},\n      2040:{gdd:2760,season:248,precip:1085,heat:14,zone:'Warming Kenyan Highlands'},\n      2050:{gdd:2840,season:252,precip:1070,heat:21,zone:'Warming Kenyan Highlands'},\n      2060:{gdd:2925,season:256,precip:1055,heat:30,zone:'Highlands \u2192 Subtropical analog'}\n    },\n    'ng-savanna': {\n      2020:{gdd:4500,season:250,precip:900,heat:60,zone:'Nigerian Savanna (Sudan\/Sahel)'},\n      2030:{gdd:4570,season:250,precip:880,heat:75,zone:'Nigerian Savanna (Sudan\/Sahel)'},\n      2040:{gdd:4640,season:250,precip:855,heat:95,zone:'Drier Savanna'},\n      2050:{gdd:4710,season:250,precip:830,heat:118,zone:'Drier Savanna'},\n      2060:{gdd:4780,season:250,precip:805,heat:144,zone:'Savanna \u2192 Sahel transition'}\n    },\n    'ci-cocoa': {\n      2020:{gdd:4200,season:365,precip:1500,heat:65,zone:'W African Cocoa Belt (humid tropical)'},\n      2030:{gdd:4270,season:365,precip:1490,heat:80,zone:'W African Cocoa Belt (humid tropical)'},\n      2040:{gdd:4340,season:365,precip:1475,heat:100,zone:'Hotter Cocoa Belt'},\n      2050:{gdd:4415,season:365,precip:1460,heat:125,zone:'Hotter Cocoa Belt'},\n      2060:{gdd:4490,season:365,precip:1445,heat:152,zone:'Cocoa Belt \u2192 Critical-heat tropics'}\n    },\n    'zm-commercial': {\n      2020:{gdd:3400,season:200,precip:900,heat:20,zone:'Zambian Commercial Belt'},\n      2030:{gdd:3475,season:204,precip:880,heat:28,zone:'Zambian Commercial Belt'},\n      2040:{gdd:3550,season:208,precip:855,heat:39,zone:'Warmer Commercial Belt'},\n      2050:{gdd:3625,season:212,precip:830,heat:53,zone:'Warmer Commercial Belt'},\n      2060:{gdd:3700,season:216,precip:805,heat:70,zone:'Commercial Belt \u2192 Drier subtropical'}\n    },\n    'il-coastal': {\n      2020:{gdd:3000,season:280,precip:450,heat:35,zone:'Israeli Mediterranean Coast'},\n      2030:{gdd:3080,season:284,precip:435,heat:48,zone:'Israeli Mediterranean Coast'},\n      2040:{gdd:3165,season:288,precip:415,heat:65,zone:'Drier Israeli Coast'},\n      2050:{gdd:3250,season:292,precip:395,heat:84,zone:'Drier Israeli Coast'},\n      2060:{gdd:3335,season:296,precip:375,heat:106,zone:'Israeli Coast \u2192 Semi-arid Mediterranean'}\n    },\n    'vn-central': {\n      2020:{gdd:4000,season:365,precip:1800,heat:50,zone:'Vietnam Central \/ Mekong'},\n      2030:{gdd:4070,season:365,precip:1790,heat:64,zone:'Vietnam Central \/ Mekong'},\n      2040:{gdd:4140,season:365,precip:1780,heat:80,zone:'Hotter Vietnam Central'},\n      2050:{gdd:4215,season:365,precip:1765,heat:99,zone:'Hotter Vietnam Central'},\n      2060:{gdd:4290,season:365,precip:1750,heat:120,zone:'Vietnam \u2192 Critical-heat tropics'}\n    },\n    'id-sumatra': {\n      2020:{gdd:4200,season:365,precip:2500,heat:50,zone:'Sumatran palm belt (humid tropical)'},\n      2030:{gdd:4270,season:365,precip:2490,heat:62,zone:'Sumatran palm belt (humid tropical)'},\n      2040:{gdd:4340,season:365,precip:2475,heat:78,zone:'Hotter Sumatra'},\n      2050:{gdd:4415,season:365,precip:2460,heat:96,zone:'Hotter Sumatra'},\n      2060:{gdd:4490,season:365,precip:2445,heat:118,zone:'Sumatra \u2192 Heat-stressed humid tropical'}\n    },\n    'tr-anatolia': {\n      2020:{gdd:2900,season:230,precip:480,heat:30,zone:'Anatolian Plateau (continental Med)'},\n      2030:{gdd:2985,season:234,precip:465,heat:42,zone:'Anatolian Plateau (continental Med)'},\n      2040:{gdd:3075,season:238,precip:445,heat:58,zone:'Drier Anatolia'},\n      2050:{gdd:3165,season:242,precip:425,heat:76,zone:'Drier Anatolia'},\n      2060:{gdd:3255,season:246,precip:405,heat:98,zone:'Anatolia \u2192 Semi-arid Mediterranean'}\n    },\n    'th-central': {\n      2020:{gdd:4500,season:365,precip:1400,heat:65,zone:'Thai Central Plain (tropical monsoon)'},\n      2030:{gdd:4570,season:365,precip:1395,heat:80,zone:'Thai Central Plain (tropical monsoon)'},\n      2040:{gdd:4640,season:365,precip:1385,heat:99,zone:'Hotter Thai Central Plain'},\n      2050:{gdd:4715,season:365,precip:1370,heat:121,zone:'Hotter Thai Central Plain'},\n      2060:{gdd:4790,season:365,precip:1355,heat:146,zone:'Thai Plain \u2192 Critical-heat tropics'}\n    },\n    'pk-indus': {\n      2020:{gdd:4000,season:280,precip:350,heat:75,zone:'Pakistani Indus Plain (irrigated arid)'},\n      2030:{gdd:4080,season:283,precip:340,heat:91,zone:'Pakistani Indus Plain (irrigated arid)'},\n      2040:{gdd:4165,season:286,precip:325,heat:111,zone:'Drier Indus Plain'},\n      2050:{gdd:4250,season:289,precip:310,heat:134,zone:'Drier Indus Plain'},\n      2060:{gdd:4335,season:292,precip:295,heat:160,zone:'Indus \u2192 Critical-heat arid'}\n    },\n    'cl-central': {\n      2020:{gdd:2400,season:270,precip:400,heat:25,zone:'Chilean Central Valley (Mediterranean)'},\n      2030:{gdd:2475,season:274,precip:385,heat:35,zone:'Chilean Central Valley (Mediterranean)'},\n      2040:{gdd:2555,season:278,precip:365,heat:48,zone:'Drier Central Valley'},\n      2050:{gdd:2635,season:282,precip:345,heat:63,zone:'Drier Central Valley'},\n      2060:{gdd:2715,season:286,precip:325,heat:82,zone:'Central Valley \u2192 Semi-arid Mediterranean'}\n    },\n    'co-andean': {\n      2020:{gdd:3200,season:365,precip:1800,heat:12,zone:'Colombian Andean Coffee Belt (cool tropical)'},\n      2030:{gdd:3275,season:365,precip:1790,heat:17,zone:'Colombian Andean Coffee Belt (cool tropical)'},\n      2040:{gdd:3355,season:365,precip:1775,heat:25,zone:'Warming Andean Coffee Belt'},\n      2050:{gdd:3435,season:365,precip:1755,heat:36,zone:'Warming Andean Coffee Belt'},\n      2060:{gdd:3515,season:365,precip:1735,heat:50,zone:'Andean Belt \u2192 Coffee-marginal subtropical'}\n    },\n    'bd-delta': {\n      2020:{gdd:4200,season:350,precip:2000,heat:50,zone:'Bangladesh Ganges Delta'},\n      2030:{gdd:4275,season:352,precip:1990,heat:65,zone:'Bangladesh Ganges Delta'},\n      2040:{gdd:4350,season:354,precip:1975,heat:82,zone:'Hotter Ganges Delta'},\n      2050:{gdd:4425,season:356,precip:1960,heat:102,zone:'Hotter Ganges Delta'},\n      2060:{gdd:4500,season:358,precip:1945,heat:125,zone:'Ganges Delta \u2192 Critical-heat humid tropics'}\n    }\n  };\n  var SUIT = {\n    'Continental Cornbelt': {\n      2020:{corn:88,soybean:85,wheat:55,sunflower:50,rapeseed:35,sorghum:45,barley:40,cotton:25,potato:55},\n      2030:{corn:86,soybean:84,wheat:50,sunflower:54,rapeseed:34,sorghum:50,barley:36,cotton:30,potato:54},\n      2040:{corn:82,soybean:81,wheat:44,sunflower:58,rapeseed:30,sorghum:56,barley:32,cotton:38,potato:52},\n      2050:{corn:78,soybean:77,wheat:38,sunflower:60,rapeseed:25,sorghum:62,barley:26,cotton:46,potato:48},\n      2060:{corn:72,soybean:73,wheat:32,sunflower:60,rapeseed:20,sorghum:68,barley:22,cotton:54,potato:44}\n    },\n    'Northern Plains \/ Prairies': {\n      2020:{wheat:78,rapeseed:74,barley:70,corn:48,soybean:42,sunflower:45,potato:65,oats:62,lentils:55,peas:60,wine:28,hemp:80},\n      2030:{wheat:76,rapeseed:74,barley:68,corn:55,soybean:50,sunflower:52,potato:65,oats:60,lentils:54,peas:60,wine:34,hemp:80},\n      2040:{wheat:72,rapeseed:72,barley:64,corn:62,soybean:58,sunflower:58,potato:64,oats:56,lentils:52,peas:58,wine:40,hemp:78},\n      2050:{wheat:67,rapeseed:69,barley:58,corn:68,soybean:64,sunflower:62,potato:62,oats:50,lentils:50,peas:55,wine:46,hemp:75},\n      2060:{wheat:62,rapeseed:65,barley:52,corn:73,soybean:69,sunflower:65,potato:60,oats:44,lentils:48,peas:52,wine:52,hemp:70}\n    },\n    'Subtropical Highlands': {\n      2020:{corn:72,wheat:55,sorghum:65,coffee:60,sugarcane:55,potato:55,barley:48,beans:62,avocado:65,wine:50},\n      2030:{corn:70,wheat:50,sorghum:68,coffee:56,sugarcane:55,potato:53,barley:44,beans:60,avocado:65,wine:55},\n      2040:{corn:66,wheat:44,sorghum:72,coffee:50,sugarcane:53,potato:50,barley:38,beans:56,avocado:62,wine:58},\n      2050:{corn:62,wheat:38,sorghum:74,coffee:42,sugarcane:50,potato:46,barley:32,beans:52,avocado:58,wine:58},\n      2060:{corn:56,wheat:32,sorghum:76,coffee:34,sugarcane:48,potato:42,barley:26,beans:48,avocado:54,wine:55}\n    },\n    'Subtropical Cerrado': {\n      2020:{soybean:82,corn:75,cotton:78,sugarcane:80,sorghum:72,coffee:70,beans:55},\n      2030:{soybean:80,corn:72,cotton:78,sugarcane:78,sorghum:74,coffee:64,beans:52},\n      2040:{soybean:76,corn:68,cotton:76,sugarcane:74,sorghum:76,coffee:56,beans:48},\n      2050:{soybean:71,corn:62,cotton:73,sugarcane:70,sorghum:77,coffee:46,beans:44},\n      2060:{soybean:65,corn:55,cotton:69,sugarcane:65,sorghum:77,coffee:36,beans:40}\n    },\n    'Pampas': {\n      2020:{soybean:82,corn:78,wheat:70,sunflower:75,barley:62,sorghum:65,rapeseed:50,wine:55},\n      2030:{soybean:81,corn:78,wheat:67,sunflower:75,barley:58,sorghum:68,rapeseed:48,wine:55},\n      2040:{soybean:79,corn:76,wheat:62,sunflower:73,barley:54,sorghum:71,rapeseed:45,wine:53},\n      2050:{soybean:76,corn:74,wheat:56,sunflower:71,barley:48,sorghum:73,rapeseed:42,wine:48},\n      2060:{soybean:73,corn:71,wheat:50,sunflower:68,barley:42,sorghum:75,rapeseed:38,wine:42}\n    },\n    'Atlantic Maritime': {\n      2020:{wheat:78,barley:75,rapeseed:72,potato:78,oats:70,sugarbeet:65,corn:42,sunflower:25,grapes:35,wine:40},\n      2030:{wheat:77,barley:73,rapeseed:73,potato:77,oats:68,sugarbeet:66,corn:50,sunflower:32,grapes:42,wine:50},\n      2040:{wheat:75,barley:70,rapeseed:73,potato:75,oats:64,sugarbeet:66,corn:58,sunflower:40,grapes:50,wine:60},\n      2050:{wheat:72,barley:66,rapeseed:72,potato:72,oats:60,sugarbeet:65,corn:65,sunflower:48,grapes:58,wine:68},\n      2060:{wheat:68,barley:62,rapeseed:69,potato:68,oats:55,sugarbeet:63,corn:70,sunflower:55,grapes:65,wine:72}\n    },\n    'Atlantic Temperate': {\n      2020:{wheat:76,corn:70,rapeseed:72,sunflower:60,barley:68,sugarbeet:70,grapes:65,potato:72,soybean:42,wine:78,hemp:78},\n      2030:{wheat:74,corn:72,rapeseed:71,sunflower:62,barley:64,sugarbeet:70,grapes:68,potato:70,soybean:48,wine:76,hemp:77},\n      2040:{wheat:70,corn:73,rapeseed:68,sunflower:64,barley:58,sugarbeet:68,grapes:70,potato:67,soybean:54,wine:72,hemp:74},\n      2050:{wheat:65,corn:73,rapeseed:64,sunflower:65,barley:52,sugarbeet:65,grapes:72,potato:62,soybean:60,wine:66,hemp:70},\n      2060:{wheat:60,corn:72,rapeseed:58,sunflower:64,barley:46,sugarbeet:60,grapes:73,potato:56,soybean:64,wine:60,hemp:66}\n    },\n    'Atlantic \/ Continental': {\n      2020:{wheat:75,corn:65,rapeseed:74,barley:70,sugarbeet:68,potato:72,sunflower:50,soybean:38,rye:65,wine:65,hemp:75},\n      2030:{wheat:73,corn:68,rapeseed:73,barley:66,sugarbeet:67,potato:70,sunflower:54,soybean:45,rye:62,wine:70,hemp:75},\n      2040:{wheat:69,corn:70,rapeseed:70,barley:60,sugarbeet:64,potato:67,sunflower:58,soybean:52,rye:58,wine:72,hemp:72},\n      2050:{wheat:64,corn:71,rapeseed:65,barley:54,sugarbeet:60,potato:62,sunflower:60,soybean:58,rye:53,wine:73,hemp:68},\n      2060:{wheat:58,corn:71,rapeseed:60,barley:48,sugarbeet:55,potato:56,sunflower:60,soybean:62,rye:48,wine:72,hemp:63}\n    },\n    'Mediterranean': {\n      2020:{wheat:62,barley:60,sunflower:62,rapeseed:48,olives:80,grapes:75,almond:75,citrus:75,chickpea:55,durum:65,corn:50,wine:80},\n      2030:{wheat:55,barley:54,sunflower:64,rapeseed:42,olives:78,grapes:73,almond:73,citrus:72,chickpea:58,durum:60,corn:46,wine:74},\n      2040:{wheat:46,barley:46,sunflower:64,rapeseed:35,olives:75,grapes:70,almond:70,citrus:68,chickpea:60,durum:54,corn:40,wine:66},\n      2050:{wheat:38,barley:38,sunflower:62,rapeseed:28,olives:70,grapes:65,almond:65,citrus:62,chickpea:60,durum:46,corn:33,wine:55},\n      2060:{wheat:30,barley:30,sunflower:58,rapeseed:22,olives:64,grapes:58,almond:58,citrus:54,chickpea:58,durum:38,corn:26,wine:45}\n    },\n    'Mediterranean \/ Semi-arid': {\n      2020:{wheat:50,barley:55,sunflower:55,rapeseed:35,sorghum:60,chickpea:65,canola:30,cotton:55,almond:60,grapes:60,wine:70},\n      2030:{wheat:44,barley:50,sunflower:58,rapeseed:30,sorghum:64,chickpea:67,canola:26,cotton:56,almond:56,grapes:57,wine:65},\n      2040:{wheat:36,barley:42,sunflower:60,rapeseed:24,sorghum:68,chickpea:68,canola:20,cotton:56,almond:50,grapes:52,wine:58},\n      2050:{wheat:28,barley:34,sunflower:60,rapeseed:18,sorghum:70,chickpea:68,canola:14,cotton:54,almond:42,grapes:46,wine:50},\n      2060:{wheat:20,barley:26,sunflower:58,rapeseed:12,sorghum:70,chickpea:66,canola:8 ,cotton:50,almond:34,grapes:38,wine:42}\n    },\n    'Continental Eastern Europe': {\n      2020:{wheat:80,corn:72,sunflower:68,rapeseed:74,barley:72,soybean:50,sugarbeet:70,potato:68,rye:65,wine:55,hemp:70},\n      2030:{wheat:78,corn:74,sunflower:71,rapeseed:73,barley:69,soybean:56,sugarbeet:69,potato:65,rye:62,wine:60,hemp:70},\n      2040:{wheat:74,corn:76,sunflower:73,rapeseed:70,barley:65,soybean:62,sugarbeet:67,potato:62,rye:58,wine:65,hemp:67},\n      2050:{wheat:69,corn:76,sunflower:74,rapeseed:65,barley:60,soybean:66,sugarbeet:64,potato:58,rye:53,wine:67,hemp:62},\n      2060:{wheat:63,corn:75,sunflower:74,rapeseed:60,barley:54,soybean:69,sugarbeet:60,potato:53,rye:48,wine:66,hemp:57}\n    },\n    'Continental Steppe': {\n      2020:{wheat:75,sunflower:78,corn:60,rapeseed:62,barley:68,soybean:45,sorghum:55,chickpea:50,millet:55,wine:62},\n      2030:{wheat:72,sunflower:79,corn:64,rapeseed:60,barley:64,soybean:50,sorghum:60,chickpea:53,millet:58,wine:62},\n      2040:{wheat:67,sunflower:78,corn:67,rapeseed:55,barley:58,soybean:54,sorghum:65,chickpea:56,millet:62,wine:60},\n      2050:{wheat:61,sunflower:76,corn:68,rapeseed:48,barley:51,soybean:56,sorghum:68,chickpea:58,millet:65,wine:55},\n      2060:{wheat:54,sunflower:73,corn:67,rapeseed:40,barley:43,soybean:57,sorghum:71,chickpea:60,millet:67,wine:48}\n    },\n    'Subtropical Highveld': {\n      2020:{corn:75,wheat:60,sorghum:68,soybean:62,sunflower:60,sugarcane:55,potato:55,beans:55},\n      2030:{corn:72,wheat:54,sorghum:71,soybean:60,sunflower:62,sugarcane:54,potato:53,beans:53},\n      2040:{corn:67,wheat:47,sorghum:74,soybean:57,sunflower:63,sugarcane:52,potato:50,beans:50},\n      2050:{corn:62,wheat:40,sorghum:75,soybean:53,sunflower:63,sugarcane:50,potato:46,beans:47},\n      2060:{corn:55,wheat:33,sorghum:75,soybean:48,sunflower:62,sugarcane:47,potato:42,beans:43}\n    },\n    'Monsoon Tropical': {\n      2020:{rice:82,wheat:65,cotton:72,sugarcane:78,sorghum:60,chickpea:55,pulses:60,mustard:60,corn:55},\n      2030:{rice:79,wheat:58,cotton:71,sugarcane:76,sorghum:62,chickpea:55,pulses:60,mustard:58,corn:56},\n      2040:{rice:74,wheat:50,cotton:69,sugarcane:73,sorghum:64,chickpea:55,pulses:60,mustard:54,corn:57},\n      2050:{rice:68,wheat:42,cotton:65,sugarcane:69,sorghum:65,chickpea:54,pulses:60,mustard:48,corn:58},\n      2060:{rice:61,wheat:34,cotton:60,sugarcane:65,sorghum:65,chickpea:52,pulses:58,mustard:42,corn:58}\n    },\n    'Continental \/ Monsoon': {\n      2020:{corn:75,wheat:72,rice:78,soybean:60,cotton:70,sorghum:55,rapeseed:55,potato:60},\n      2030:{corn:76,wheat:68,rice:76,soybean:62,cotton:70,sorghum:58,rapeseed:52,potato:58},\n      2040:{corn:76,wheat:62,rice:73,soybean:63,cotton:69,sorghum:62,rapeseed:48,potato:55},\n      2050:{corn:74,wheat:55,rice:69,soybean:63,cotton:67,sorghum:66,rapeseed:43,potato:51},\n      2060:{corn:71,wheat:48,rice:64,soybean:62,cotton:64,sorghum:69,rapeseed:38,potato:46}\n    },\n    'Temperate Oceanic': {\n      2020:{wheat:75,barley:72,corn:65,rapeseed:68,potato:78,sugarbeet:62,oats:70,grapes:55,sunflower:35,wine:78},\n      2030:{wheat:74,barley:70,corn:68,rapeseed:67,potato:77,sugarbeet:62,oats:68,grapes:60,sunflower:42,wine:78},\n      2040:{wheat:71,barley:67,corn:71,rapeseed:64,potato:75,sugarbeet:61,oats:64,grapes:65,sunflower:50,wine:75},\n      2050:{wheat:68,barley:62,corn:73,rapeseed:60,potato:72,sugarbeet:58,oats:60,grapes:69,sunflower:56,wine:72},\n      2060:{wheat:64,barley:57,corn:73,rapeseed:55,potato:68,sugarbeet:54,oats:54,grapes:72,sunflower:60,wine:68}\n    },\n    'us-cotton': {\n      2020:{cotton:88,corn:72,soybean:75,sorghum:65,rice:55,peanut:78,wheat:50},\n      2030:{cotton:86,corn:68,soybean:72,sorghum:70,rice:54,peanut:75,wheat:44},\n      2040:{cotton:82,corn:62,soybean:67,sorghum:73,rice:52,peanut:71,wheat:36},\n      2050:{cotton:77,corn:54,soybean:60,sorghum:75,rice:48,peanut:65,wheat:28},\n      2060:{cotton:71,corn:46,soybean:52,sorghum:75,rice:43,peanut:58,wheat:22}\n    },\n    'us-pnw': {\n      2020:{wheat:82,barley:72,potato:78,rapeseed:65,hops:80,grapes:65,corn:48,sugarbeet:55,wine:75},\n      2030:{wheat:81,barley:70,potato:77,rapeseed:64,hops:78,grapes:68,corn:54,sugarbeet:54,wine:76},\n      2040:{wheat:78,barley:66,potato:74,rapeseed:60,hops:74,grapes:72,corn:60,sugarbeet:52,wine:73},\n      2050:{wheat:74,barley:60,potato:70,rapeseed:55,hops:69,grapes:73,corn:65,sugarbeet:48,wine:68},\n      2060:{wheat:69,barley:54,potato:65,rapeseed:48,hops:63,grapes:73,corn:68,sugarbeet:44,wine:62}\n    },\n    'us-california': {\n      2020:{almond:80,grapes:78,citrus:75,cotton:62,corn:58,rice:65,sugarbeet:55,wheat:50,wine:82},\n      2030:{almond:75,grapes:76,citrus:71,cotton:62,corn:55,rice:60,sugarbeet:53,wheat:46,wine:76},\n      2040:{almond:68,grapes:72,citrus:65,cotton:60,corn:50,rice:54,sugarbeet:48,wheat:40,wine:68},\n      2050:{almond:60,grapes:67,citrus:58,cotton:57,corn:44,rice:47,sugarbeet:42,wheat:34,wine:58},\n      2060:{almond:52,grapes:60,citrus:50,cotton:53,corn:38,rice:40,sugarbeet:36,wheat:28,wine:48}\n    },\n    'br-sul': {\n      2020:{soybean:85,corn:80,wheat:65,rice:75,tobacco:70,sugarcane:55,sorghum:50,wine:60},\n      2030:{soybean:84,corn:79,wheat:60,rice:73,tobacco:68,sugarcane:58,sorghum:55,wine:62},\n      2040:{soybean:81,corn:76,wheat:54,rice:70,tobacco:64,sugarcane:62,sorghum:62,wine:62},\n      2050:{soybean:77,corn:72,wheat:47,rice:65,tobacco:58,sugarcane:65,sorghum:68,wine:58},\n      2060:{soybean:72,corn:67,wheat:40,rice:60,tobacco:52,sugarcane:67,sorghum:72,wine:53}\n    },\n    'ua-polissia': {\n      2020:{potato:80,rye:75,oats:72,barley:68,wheat:62,rapeseed:55,corn:42,buckwheat:65,hops:55,wine:30,hemp:75},\n      2030:{potato:78,rye:72,oats:70,barley:65,wheat:62,rapeseed:55,corn:48,buckwheat:62,hops:55,wine:38,hemp:74},\n      2040:{potato:74,rye:67,oats:66,barley:60,wheat:60,rapeseed:53,corn:55,buckwheat:58,hops:54,wine:45,hemp:71},\n      2050:{potato:69,rye:60,oats:60,barley:54,wheat:57,rapeseed:50,corn:62,buckwheat:53,hops:52,wine:52,hemp:66},\n      2060:{potato:63,rye:53,oats:54,barley:48,wheat:53,rapeseed:46,corn:66,buckwheat:48,hops:48,wine:58,hemp:60}\n    },\n    'ua-forest-steppe': {\n      2020:{wheat:82,sugarbeet:78,corn:72,sunflower:68,rapeseed:74,soybean:55,barley:72,potato:65,peas:55,wine:50},\n      2030:{wheat:80,sugarbeet:76,corn:75,sunflower:71,rapeseed:73,soybean:60,barley:68,potato:62,peas:55,wine:58},\n      2040:{wheat:76,sugarbeet:72,corn:78,sunflower:74,rapeseed:70,soybean:66,barley:62,potato:58,peas:54,wine:66},\n      2050:{wheat:70,sugarbeet:67,corn:79,sunflower:75,rapeseed:65,soybean:71,barley:55,potato:53,peas:52,wine:72},\n      2060:{wheat:63,sugarbeet:61,corn:78,sunflower:74,rapeseed:58,soybean:74,barley:48,potato:47,peas:48,wine:75}\n    },\n    'cn-northeast': {\n      2020:{corn:82,soybean:78,rice:72,sorghum:60,wheat:58,potato:62,sugarbeet:55},\n      2030:{corn:83,soybean:79,rice:73,sorghum:62,wheat:55,potato:60,sugarbeet:54},\n      2040:{corn:83,soybean:78,rice:72,sorghum:66,wheat:50,potato:57,sugarbeet:51},\n      2050:{corn:81,soybean:76,rice:69,sorghum:69,wheat:44,potato:53,sugarbeet:47},\n      2060:{corn:78,soybean:72,rice:65,sorghum:71,wheat:38,potato:48,sugarbeet:43}\n    },\n    'in-deccan': {\n      2020:{sorghum:80,millet:78,cotton:75,pulses:72,oilseeds:65,sugarcane:55,rice:48,wheat:40},\n      2030:{sorghum:81,millet:78,cotton:74,pulses:71,oilseeds:65,sugarcane:52,rice:44,wheat:34},\n      2040:{sorghum:82,millet:77,cotton:71,pulses:69,oilseeds:63,sugarcane:48,rice:38,wheat:28},\n      2050:{sorghum:81,millet:75,cotton:67,pulses:65,oilseeds:60,sugarcane:43,rice:32,wheat:22},\n      2060:{sorghum:79,millet:72,cotton:62,pulses:60,oilseeds:56,sugarcane:38,rice:26,wheat:16}\n    },\n    'au-murray': {\n      2020:{wheat:72,cotton:68,barley:65,rapeseed:55,sorghum:50,oats:60,grapes:60,rice:55,wine:72},\n      2030:{wheat:68,cotton:70,barley:60,rapeseed:50,sorghum:55,oats:55,grapes:58,rice:48,wine:65},\n      2040:{wheat:62,cotton:71,barley:54,rapeseed:43,sorghum:60,oats:48,grapes:54,rice:40,wine:56},\n      2050:{wheat:55,cotton:70,barley:46,rapeseed:35,sorghum:64,oats:40,grapes:48,rice:32,wine:46},\n      2060:{wheat:47,cotton:67,barley:38,rapeseed:27,sorghum:66,oats:32,grapes:42,rice:24,wine:38}\n    },\n    'us-delta': {\n      2020:{rice:88,cotton:78,soybean:80,corn:62,sugarcane:55,sorghum:55,wheat:42},\n      2030:{rice:85,cotton:76,soybean:77,corn:58,sugarcane:58,sorghum:60,wheat:38},\n      2040:{rice:80,cotton:73,soybean:73,corn:53,sugarcane:60,sorghum:65,wheat:32},\n      2050:{rice:74,cotton:69,soybean:68,corn:46,sugarcane:60,sorghum:68,wheat:26},\n      2060:{rice:66,cotton:64,soybean:62,corn:38,sugarcane:58,sorghum:70,wheat:20}\n    },\n    'us-s-plains': {\n      2020:{wheat:78,sorghum:80,cotton:65,corn:55,sunflower:58,peanut:45,soybean:48},\n      2030:{wheat:73,sorghum:80,cotton:67,corn:52,sunflower:60,peanut:46,soybean:46},\n      2040:{wheat:67,sorghum:78,cotton:67,corn:46,sunflower:60,peanut:46,soybean:43},\n      2050:{wheat:60,sorghum:75,cotton:65,corn:40,sunflower:58,peanut:44,soybean:38},\n      2060:{wheat:52,sorghum:71,cotton:60,corn:33,sunflower:55,peanut:40,soybean:33}\n    },\n    'us-lake': {\n      2020:{corn:72,soybean:75,oats:78,wheat:62,barley:65,potato:75,sugarbeet:72,rye:60,hemp:72},\n      2030:{corn:74,soybean:76,oats:76,wheat:62,barley:62,potato:73,sugarbeet:71,rye:58,hemp:72},\n      2040:{corn:75,soybean:75,oats:72,wheat:60,barley:58,potato:69,sugarbeet:68,rye:54,hemp:70},\n      2050:{corn:74,soybean:73,oats:67,wheat:57,barley:52,potato:64,sugarbeet:64,rye:48,hemp:66},\n      2060:{corn:72,soybean:69,oats:62,wheat:53,barley:46,potato:58,sugarbeet:58,rye:42,hemp:60}\n    },\n    'us-northeast': {\n      2020:{corn:62,soybean:55,oats:72,wheat:55,potato:70,barley:62,rye:60,hemp:70},\n      2030:{corn:66,soybean:60,oats:69,wheat:55,potato:68,barley:58,rye:57,hemp:70},\n      2040:{corn:69,soybean:64,oats:65,wheat:53,potato:64,barley:53,rye:53,hemp:67},\n      2050:{corn:71,soybean:66,oats:60,wheat:50,potato:60,barley:47,rye:48,hemp:63},\n      2060:{corn:71,soybean:67,oats:55,wheat:46,potato:55,barley:41,rye:43,hemp:58}\n    },\n    'ca-ontario': {\n      2020:{corn:78,soybean:75,wheat:70,oats:72,barley:65,potato:75,rapeseed:55,rye:62},\n      2030:{corn:80,soybean:76,wheat:68,oats:70,barley:62,potato:73,rapeseed:55,rye:60},\n      2040:{corn:81,soybean:77,wheat:65,oats:66,barley:58,potato:70,rapeseed:54,rye:56},\n      2050:{corn:80,soybean:76,wheat:61,oats:62,barley:53,potato:65,rapeseed:51,rye:51},\n      2060:{corn:78,soybean:74,wheat:56,oats:57,barley:48,potato:60,rapeseed:48,rye:46}\n    },\n    'ca-bc': {\n      2020:{potato:78,oats:72,barley:65,wheat:60,hops:78,wine:62,grapes:65,rapeseed:55},\n      2030:{potato:78,oats:70,barley:62,wheat:60,hops:78,wine:68,grapes:70,rapeseed:55},\n      2040:{potato:75,oats:67,barley:58,wheat:58,hops:75,wine:72,grapes:73,rapeseed:53},\n      2050:{potato:72,oats:62,barley:53,wheat:55,hops:71,wine:73,grapes:74,rapeseed:50},\n      2060:{potato:68,oats:57,barley:48,wheat:51,hops:65,wine:72,grapes:73,rapeseed:46}\n    },\n    'ca-maritimes': {\n      2020:{potato:82,oats:70,barley:65,wheat:55,rye:60},\n      2030:{potato:80,oats:68,barley:62,wheat:55,rye:58},\n      2040:{potato:77,oats:64,barley:58,wheat:53,rye:54},\n      2050:{potato:73,oats:60,barley:53,wheat:50,rye:49},\n      2060:{potato:68,oats:54,barley:48,wheat:46,rye:43}\n    },\n    'br-sertao': {\n      2020:{sugarcane:55,sorghum:65,beans:48,cotton:50,corn:35,sunflower:30},\n      2030:{sugarcane:50,sorghum:67,beans:46,cotton:50,corn:32,sunflower:32},\n      2040:{sugarcane:44,sorghum:68,beans:42,cotton:48,corn:28,sunflower:32},\n      2050:{sugarcane:38,sorghum:68,beans:37,cotton:44,corn:23,sunflower:30},\n      2060:{sugarcane:32,sorghum:66,beans:31,cotton:40,corn:18,sunflower:27}\n    },\n    'br-bahia': {\n      2020:{cocoa:75,soybean:72,cotton:70,sugarcane:70,coffee:65,corn:60,beans:55},\n      2030:{cocoa:70,soybean:70,cotton:70,sugarcane:67,coffee:60,corn:57,beans:53},\n      2040:{cocoa:63,soybean:66,cotton:68,sugarcane:62,coffee:54,corn:52,beans:50},\n      2050:{cocoa:55,soybean:60,cotton:65,sugarcane:56,coffee:46,corn:46,beans:46},\n      2060:{cocoa:46,soybean:54,cotton:60,sugarcane:50,coffee:38,corn:40,beans:42}\n    },\n    'ua-carpathian': {\n      2020:{potato:80,oats:75,rye:72,barley:65,wheat:55,corn:42,buckwheat:62},\n      2030:{potato:78,oats:72,rye:68,barley:62,wheat:55,corn:48,buckwheat:60},\n      2040:{potato:75,oats:68,rye:63,barley:58,wheat:55,corn:55,buckwheat:56},\n      2050:{potato:71,oats:62,rye:57,barley:52,wheat:52,corn:62,buckwheat:51},\n      2060:{potato:65,oats:56,rye:50,barley:46,wheat:48,corn:67,buckwheat:46}\n    },\n    'ua-steppe-south': {\n      2020:{wine:78,sunflower:80,wheat:70,corn:55,rapeseed:55,barley:62,sorghum:50},\n      2030:{wine:78,sunflower:80,wheat:67,corn:60,rapeseed:53,barley:58,sorghum:55},\n      2040:{wine:75,sunflower:78,wheat:62,corn:62,rapeseed:50,barley:52,sorghum:60},\n      2050:{wine:70,sunflower:75,wheat:55,corn:62,rapeseed:45,barley:46,sorghum:64},\n      2060:{wine:63,sunflower:70,wheat:48,corn:60,rapeseed:38,barley:38,sorghum:67}\n    },\n    'ua-podillia': {\n      2020:{wheat:80,sunflower:65,corn:70,rapeseed:72,sugarbeet:75,soybean:55,barley:70,potato:67},\n      2030:{wheat:78,sunflower:68,corn:73,rapeseed:71,sugarbeet:73,soybean:60,barley:66,potato:65},\n      2040:{wheat:74,sunflower:72,corn:76,rapeseed:68,sugarbeet:70,soybean:65,barley:60,potato:60},\n      2050:{wheat:69,sunflower:73,corn:77,rapeseed:63,sugarbeet:65,soybean:70,barley:54,potato:54},\n      2060:{wheat:62,sunflower:72,corn:76,rapeseed:56,sugarbeet:60,soybean:73,barley:47,potato:48}\n    },\n    'au-qld': {\n      2020:{sugarcane:85,cotton:70,sorghum:75,soybean:60,corn:65,beans:55},\n      2030:{sugarcane:83,cotton:71,sorghum:76,soybean:58,corn:63,beans:53},\n      2040:{sugarcane:80,cotton:71,sorghum:75,soybean:54,corn:58,beans:50},\n      2050:{sugarcane:75,cotton:69,sorghum:73,soybean:48,corn:51,beans:45},\n      2060:{sugarcane:69,cotton:65,sorghum:70,soybean:42,corn:43,beans:40}\n    },\n    'au-tasmania': {\n      2020:{potato:82,oats:75,barley:68,wheat:62,rapeseed:60,wine:72,grapes:70,hops:78},\n      2030:{potato:80,oats:72,barley:65,wheat:60,rapeseed:60,wine:75,grapes:72,hops:78},\n      2040:{potato:78,oats:68,barley:60,wheat:57,rapeseed:58,wine:76,grapes:73,hops:75},\n      2050:{potato:74,oats:63,barley:55,wheat:53,rapeseed:55,wine:75,grapes:73,hops:71},\n      2060:{potato:70,oats:57,barley:49,wheat:48,rapeseed:51,wine:72,grapes:71,hops:65}\n    },\n    'au-north': {\n      2020:{sorghum:65,cotton:55,sugarcane:60,beans:45,corn:45,peanut:50},\n      2030:{sorghum:64,cotton:53,sugarcane:55,beans:42,corn:42,peanut:48},\n      2040:{sorghum:61,cotton:50,sugarcane:50,beans:38,corn:38,peanut:44},\n      2050:{sorghum:57,cotton:46,sugarcane:44,beans:33,corn:33,peanut:40},\n      2060:{sorghum:52,cotton:42,sugarcane:38,beans:28,corn:28,peanut:35}\n    },\n    'za-cape': {\n      2020:{wine:80,grapes:78,olives:72,citrus:68,wheat:60,almond:60},\n      2030:{wine:78,grapes:75,olives:68,citrus:64,wheat:55,almond:55},\n      2040:{wine:73,grapes:70,olives:62,citrus:58,wheat:48,almond:48},\n      2050:{wine:65,grapes:63,olives:55,citrus:51,wheat:40,almond:40},\n      2060:{wine:55,grapes:55,olives:47,citrus:43,wheat:32,almond:32}\n    },\n    'eg-nile': {\n      2020:{cotton:75,rice:75,wheat:65,sugarcane:70,corn:55,citrus:70,sorghum:55},\n      2030:{cotton:73,rice:72,wheat:60,sugarcane:67,corn:52,citrus:67,sorghum:58},\n      2040:{cotton:70,rice:67,wheat:53,sugarcane:62,corn:46,citrus:62,sorghum:61},\n      2050:{cotton:65,rice:60,wheat:46,sugarcane:55,corn:39,citrus:55,sorghum:62},\n      2060:{cotton:58,rice:53,wheat:38,sugarcane:48,corn:32,citrus:48,sorghum:62}\n    },\n    'ma-atlas': {\n      2020:{wheat:60,olives:75,citrus:70,almond:65,grapes:60,barley:55,durum:60,wine:62},\n      2030:{wheat:54,olives:72,citrus:65,almond:62,grapes:57,barley:50,durum:55,wine:60},\n      2040:{wheat:46,olives:68,citrus:58,almond:57,grapes:52,barley:43,durum:48,wine:54},\n      2050:{wheat:38,olives:62,citrus:50,almond:50,grapes:46,barley:35,durum:40,wine:46},\n      2060:{wheat:30,olives:55,citrus:42,almond:42,grapes:38,barley:27,durum:32,wine:38}\n    },\n    'et-highlands': {\n      2020:{coffee:80,wheat:65,barley:60,corn:58,pulses:70,beans:65,sorghum:60},\n      2030:{coffee:75,wheat:62,barley:58,corn:62,pulses:70,beans:62,sorghum:65},\n      2040:{coffee:68,wheat:57,barley:54,corn:64,pulses:67,beans:58,sorghum:68},\n      2050:{coffee:60,wheat:51,barley:48,corn:65,pulses:64,beans:53,sorghum:70},\n      2060:{coffee:52,wheat:44,barley:42,corn:64,pulses:60,beans:48,sorghum:70}\n    },\n    'ke-highlands': {\n      2020:{tea:82,coffee:75,corn:72,wheat:60,beans:65,sugarcane:55,avocado:70},\n      2030:{tea:78,coffee:71,corn:73,wheat:57,beans:63,sugarcane:53,avocado:67},\n      2040:{tea:73,coffee:65,corn:72,wheat:52,beans:59,sugarcane:50,avocado:62},\n      2050:{tea:66,coffee:58,corn:69,wheat:46,beans:54,sugarcane:46,avocado:56},\n      2060:{tea:58,coffee:50,corn:64,wheat:40,beans:48,sugarcane:42,avocado:49}\n    },\n    'ng-savanna': {\n      2020:{sorghum:78,millet:80,cassava:75,peanut:70,corn:55,cotton:55,rice:45},\n      2030:{sorghum:78,millet:79,cassava:73,peanut:68,corn:52,cotton:55,rice:42},\n      2040:{sorghum:76,millet:77,cassava:70,peanut:64,corn:47,cotton:53,rice:36},\n      2050:{sorghum:73,millet:73,cassava:65,peanut:58,corn:41,cotton:50,rice:30},\n      2060:{sorghum:68,millet:69,cassava:59,peanut:52,corn:35,cotton:46,rice:24}\n    },\n    'ci-cocoa': {\n      2020:{cocoa:85,palm:75,coffee:60,corn:55,cassava:75,sugarcane:60,cashew:65},\n      2030:{cocoa:80,palm:73,coffee:55,corn:52,cassava:72,sugarcane:57,cashew:65},\n      2040:{cocoa:72,palm:69,coffee:48,corn:48,cassava:68,sugarcane:53,cashew:62},\n      2050:{cocoa:62,palm:64,coffee:40,corn:43,cassava:63,sugarcane:48,cashew:57},\n      2060:{cocoa:52,palm:58,coffee:32,corn:38,cassava:56,sugarcane:43,cashew:50}\n    },\n    'zm-commercial': {\n      2020:{corn:78,soybean:65,wheat:55,sugarcane:70,cotton:60,sorghum:60,peanut:60,tobacco:65},\n      2030:{corn:76,soybean:64,wheat:52,sugarcane:67,cotton:60,sorghum:63,peanut:58,tobacco:62},\n      2040:{corn:72,soybean:61,wheat:48,sugarcane:62,cotton:58,sorghum:65,peanut:54,tobacco:57},\n      2050:{corn:67,soybean:57,wheat:42,sugarcane:56,cotton:55,sorghum:66,peanut:48,tobacco:50},\n      2060:{corn:60,soybean:51,wheat:35,sugarcane:50,cotton:50,sorghum:66,peanut:42,tobacco:43}\n    },\n    'il-coastal': {\n      2020:{citrus:75,olives:75,almond:65,grapes:70,wine:72,dates:80,cotton:55,wheat:55},\n      2030:{citrus:70,olives:73,almond:62,grapes:67,wine:70,dates:82,cotton:54,wheat:50},\n      2040:{citrus:63,olives:68,almond:57,grapes:62,wine:65,dates:82,cotton:52,wheat:43},\n      2050:{citrus:55,olives:62,almond:50,grapes:55,wine:58,dates:80,cotton:48,wheat:35},\n      2060:{citrus:46,olives:55,almond:42,grapes:47,wine:48,dates:75,cotton:43,wheat:28}\n    },\n    'vn-central': {\n      2020:{coffee:78,pepper:85,rice:80,cashew:75,sugarcane:75,cassava:70,mango:65,bananas:70},\n      2030:{coffee:72,pepper:84,rice:77,cashew:75,sugarcane:73,cassava:69,mango:65,bananas:68},\n      2040:{coffee:64,pepper:80,rice:72,cashew:72,sugarcane:69,cassava:66,mango:64,bananas:64},\n      2050:{coffee:55,pepper:74,rice:65,cashew:68,sugarcane:63,cassava:62,mango:60,bananas:58},\n      2060:{coffee:45,pepper:66,rice:58,cashew:63,sugarcane:56,cassava:57,mango:55,bananas:51}\n    },\n    'id-sumatra': {\n      2020:{palm:88,cocoa:78,coffee:72,rice:72,sugarcane:65,bananas:75,cassava:65,pepper:70},\n      2030:{palm:86,cocoa:73,coffee:67,rice:70,sugarcane:63,bananas:73,cassava:64,pepper:69},\n      2040:{palm:82,cocoa:66,coffee:60,rice:66,sugarcane:60,bananas:69,cassava:62,pepper:65},\n      2050:{palm:76,cocoa:58,coffee:52,rice:60,sugarcane:55,bananas:64,cassava:58,pepper:60},\n      2060:{palm:69,cocoa:48,coffee:43,rice:54,sugarcane:50,bananas:58,cassava:53,pepper:53}\n    },\n    'tr-anatolia': {\n      2020:{wheat:72,cotton:65,sunflower:70,durum:75,olives:72,citrus:62,pistachio:75,grapes:70,wine:72,hazelnut:85,almond:62,chickpea:65},\n      2030:{wheat:67,cotton:65,sunflower:71,durum:70,olives:70,citrus:58,pistachio:75,grapes:68,wine:70,hazelnut:82,almond:60,chickpea:67},\n      2040:{wheat:60,cotton:64,sunflower:71,durum:63,olives:67,citrus:52,pistachio:73,grapes:64,wine:65,hazelnut:77,almond:55,chickpea:67},\n      2050:{wheat:52,cotton:61,sunflower:69,durum:55,olives:62,citrus:46,pistachio:70,grapes:58,wine:58,hazelnut:70,almond:48,chickpea:65},\n      2060:{wheat:44,cotton:56,sunflower:65,durum:46,olives:55,citrus:38,pistachio:65,grapes:50,wine:48,hazelnut:62,almond:40,chickpea:62}\n    },\n    'th-central': {\n      2020:{rice:88,sugarcane:80,cassava:80,palm:65,corn:60,soybean:55,pepper:65,bananas:70,mango:72,pulses:58},\n      2030:{rice:86,sugarcane:78,cassava:78,palm:63,corn:57,soybean:52,pepper:65,bananas:68,mango:71,pulses:57},\n      2040:{rice:82,sugarcane:73,cassava:75,palm:59,corn:53,soybean:47,pepper:62,bananas:64,mango:68,pulses:54},\n      2050:{rice:76,sugarcane:67,cassava:71,palm:53,corn:46,soybean:41,pepper:57,bananas:59,mango:63,pulses:50},\n      2060:{rice:68,sugarcane:60,cassava:65,palm:46,corn:38,soybean:34,pepper:50,bananas:53,mango:57,pulses:45}\n    },\n    'pk-indus': {\n      2020:{wheat:65,rice:75,cotton:70,sugarcane:65,mango:75,citrus:55,pulses:60,sesame:60,sunflower:55},\n      2030:{wheat:60,rice:71,cotton:71,sugarcane:62,mango:73,citrus:50,pulses:58,sesame:62,sunflower:55},\n      2040:{wheat:53,rice:65,cotton:69,sugarcane:57,mango:69,citrus:43,pulses:55,sesame:62,sunflower:54},\n      2050:{wheat:45,rice:58,cotton:66,sugarcane:50,mango:63,citrus:36,pulses:50,sesame:60,sunflower:52},\n      2060:{wheat:37,cotton:62,rice:50,sugarcane:43,mango:56,citrus:28,pulses:44,sesame:57,sunflower:48}\n    },\n    'cl-central': {\n      2020:{wine:80,grapes:78,citrus:70,almond:65,olives:72,avocado:65,wheat:60,sugarbeet:55,wheat:60,quinoa:55},\n      2030:{wine:78,grapes:75,citrus:67,almond:62,olives:69,avocado:62,wheat:55,sugarbeet:53,quinoa:56},\n      2040:{wine:73,grapes:70,citrus:62,almond:57,olives:64,avocado:57,wheat:48,sugarbeet:50,quinoa:55},\n      2050:{wine:65,grapes:63,citrus:55,almond:50,olives:57,avocado:50,wheat:40,sugarbeet:45,quinoa:52},\n      2060:{wine:55,grapes:55,citrus:46,almond:42,olives:48,avocado:42,wheat:32,sugarbeet:40,quinoa:48}\n    },\n    'co-andean': {\n      2020:{coffee:85,sugarcane:78,palm:70,bananas:75,cocoa:65,corn:60,beans:62},\n      2030:{coffee:80,sugarcane:76,palm:68,bananas:73,cocoa:62,corn:58,beans:60},\n      2040:{coffee:73,sugarcane:72,palm:65,bananas:69,cocoa:57,corn:54,beans:56},\n      2050:{coffee:64,sugarcane:67,palm:60,bananas:64,cocoa:50,corn:48,beans:51},\n      2060:{coffee:54,sugarcane:61,palm:54,bananas:58,cocoa:42,corn:42,beans:46}\n    },\n    'bd-delta': {\n      2020:{rice:88,wheat:50,sugarcane:65,pulses:60,mango:70,jute:78,sweetpotato:65},\n      2030:{rice:86,wheat:46,sugarcane:62,pulses:58,mango:68,jute:75,sweetpotato:64},\n      2040:{rice:82,wheat:40,sugarcane:58,pulses:55,mango:64,jute:71,sweetpotato:61},\n      2050:{rice:76,wheat:34,sugarcane:53,pulses:51,mango:59,jute:65,sweetpotato:57},\n      2060:{rice:68,wheat:28,sugarcane:47,pulses:46,mango:53,jute:58,sweetpotato:52}\n    }\n  };\n  var COMMODITY = {\n    corn:      { price:200, vol:0.18, currency:'USD\/t' },\n    soybean:   { price:400, vol:0.22, currency:'USD\/t' },\n    wheat:     { price:250, vol:0.25, currency:'USD\/t' },\n    sunflower: { price:450, vol:0.30, currency:'USD\/t' },\n    rapeseed:  { price:500, vol:0.24, currency:'USD\/t' },\n    canola:    { price:500, vol:0.24, currency:'USD\/t' },\n    cotton:    { price:1500,vol:0.20, currency:'USD\/t' },\n    rice:      { price:480, vol:0.15, currency:'USD\/t' },\n    sorghum:   { price:220, vol:0.22, currency:'USD\/t' },\n    barley:    { price:230, vol:0.20, currency:'USD\/t' },\n    potato:    { price:250, vol:0.30, currency:'USD\/t' },\n    oats:      { price:280, vol:0.22, currency:'USD\/t' },\n    rye:       { price:230, vol:0.20, currency:'USD\/t' },\n    millet:    { price:290, vol:0.20, currency:'USD\/t' },\n    sugarbeet: { price:45,  vol:0.18, currency:'USD\/t' },\n    sugarcane: { price:30,  vol:0.20, currency:'USD\/t' },\n    coffee:    { price:3800,vol:0.32, currency:'USD\/t' },\n    olives:    { price:5500,vol:0.28, currency:'USD\/t' },\n    almond:    { price:6000,vol:0.30, currency:'USD\/t' },\n    avocado:   { price:2500,vol:0.32, currency:'USD\/t' },\n    citrus:    { price:600, vol:0.22, currency:'USD\/t' },\n    grapes:    { price:900, vol:0.22, currency:'USD\/t' },\n    chickpea:  { price:900, vol:0.28, currency:'USD\/t' },\n    beans:     { price:850, vol:0.28, currency:'USD\/t' },\n    lentils:   { price:1000,vol:0.28, currency:'USD\/t' },\n    peas:      { price:380, vol:0.24, currency:'USD\/t' },\n    pulses:    { price:850, vol:0.26, currency:'USD\/t' },\n    mustard:   { price:600, vol:0.26, currency:'USD\/t' },\n    durum:     { price:330, vol:0.26, currency:'USD\/t' },\n    peanut:    { price:1100,vol:0.22, currency:'USD\/t' },\n    hops:      { price:4000,vol:0.25, currency:'USD\/t' },\n    tobacco:   { price:3500,vol:0.18, currency:'USD\/t' },\n    buckwheat: { price:450, vol:0.28, currency:'USD\/t' },\n    oilseeds:  { price:500, vol:0.25, currency:'USD\/t' },\n    wine:      { price:1200,vol:0.22, currency:'USD\/t' },\n    cocoa:     { price:3000,vol:0.30, currency:'USD\/t' },\n    cassava:   { price:250, vol:0.18, currency:'USD\/t' },\n    palm:      { price:900, vol:0.20, currency:'USD\/t' },\n    cashew:    { price:1800,vol:0.25, currency:'USD\/t' },\n    tea:       { price:2800,vol:0.18, currency:'USD\/t' },\n    hemp:      { price:1500,vol:0.30, currency:'USD\/t' },\n    dates:     { price:2500,vol:0.22, currency:'USD\/t' },\n    bananas:   { price:400, vol:0.20, currency:'USD\/t' },\n    mango:     { price:1500,vol:0.25, currency:'USD\/t' },\n    alfalfa:   { price:350, vol:0.20, currency:'USD\/t' },\n    sesame:    { price:1500,vol:0.28, currency:'USD\/t' },\n    quinoa:    { price:2500,vol:0.30, currency:'USD\/t' },\n    sweetpotato:{price:250, vol:0.20, currency:'USD\/t' },\n    yam:       { price:400, vol:0.25, currency:'USD\/t' },\n    pistachio: { price:8000,vol:0.32, currency:'USD\/t' },\n    pepper:    { price:5000,vol:0.30, currency:'USD\/t' },\n    hazelnut:  { price:7000,vol:0.35, currency:'USD\/t' },\n    jute:      { price:600, vol:0.25, currency:'USD\/t' }\n  };\n  var TRADE = {\n    corn:      { top5:0.72, chokepoint:'US Gulf \/ Black Sea',     restricted:true  },\n    soybean:   { top5:0.92, chokepoint:'US Gulf \/ Brazil ports',  restricted:false },\n    wheat:     { top5:0.65, chokepoint:'Black Sea \/ Bosphorus',   restricted:true  },\n    sunflower: { top5:0.78, chokepoint:'Black Sea \/ Bosphorus',   restricted:true  },\n    rapeseed:  { top5:0.80, chokepoint:'St Lawrence \/ EU ports',  restricted:false },\n    canola:    { top5:0.80, chokepoint:'St Lawrence \/ EU ports',  restricted:false },\n    cotton:    { top5:0.65, chokepoint:'US Gulf \/ Mumbai',        restricted:true  },\n    rice:      { top5:0.78, chokepoint:'Bab-el-Mandeb \/ Suez',    restricted:true  },\n    sorghum:   { top5:0.95, chokepoint:'US Gulf \/ Australia',     restricted:false },\n    barley:    { top5:0.70, chokepoint:'Black Sea \/ Australia',   restricted:false },\n    potato:    { top5:0.50, chokepoint:'EU intra-trade',          restricted:false },\n    oats:      { top5:0.85, chokepoint:'Baltic \/ Canada',         restricted:false },\n    rye:       { top5:0.80, chokepoint:'Black Sea \/ Baltic',      restricted:false },\n    millet:    { top5:0.60, chokepoint:'South Asia \/ W Africa',   restricted:false },\n    sugarbeet: { top5:0.85, chokepoint:'EU intra-trade',          restricted:false },\n    sugarcane: { top5:0.85, chokepoint:'Brazil ports',            restricted:false },\n    coffee:    { top5:0.70, chokepoint:'Brazil \/ Vietnam',        restricted:false },\n    olives:    { top5:0.85, chokepoint:'Mediterranean',           restricted:false },\n    almond:    { top5:0.90, chokepoint:'US Pacific',              restricted:false },\n    avocado:   { top5:0.75, chokepoint:'Mexico ports',            restricted:false },\n    citrus:    { top5:0.55, chokepoint:'Mediterranean \/ Brazil',  restricted:false },\n    grapes:    { top5:0.55, chokepoint:'Chile \/ Mediterranean',   restricted:false },\n    chickpea:  { top5:0.75, chokepoint:'India \/ Australia',       restricted:true  },\n    beans:     { top5:0.55, chokepoint:'Brazil \/ India',          restricted:true  },\n    lentils:   { top5:0.85, chokepoint:'Canada \/ Australia',      restricted:false },\n    peas:      { top5:0.80, chokepoint:'Canada \/ Russia',         restricted:false },\n    pulses:    { top5:0.70, chokepoint:'India \/ Australia',       restricted:true  },\n    mustard:   { top5:0.85, chokepoint:'Canada \/ India',          restricted:false },\n    durum:     { top5:0.80, chokepoint:'Canada \/ Mediterranean',  restricted:false },\n    peanut:    { top5:0.62, chokepoint:'US \/ Argentina \/ India',  restricted:false },\n    hops:      { top5:0.92, chokepoint:'Germany \/ US',            restricted:false },\n    tobacco:   { top5:0.75, chokepoint:'Brazil \/ China \/ India',  restricted:false },\n    buckwheat: { top5:0.78, chokepoint:'Black Sea \/ China',       restricted:false },\n    oilseeds:  { top5:0.70, chokepoint:'Black Sea \/ S America',   restricted:true  },\n    wine:      { top5:0.55, chokepoint:'France \/ Italy \/ Spain',  restricted:false },\n    cocoa:     { top5:0.85, chokepoint:'C\u00f4te d\\'Ivoire \/ Ghana',  restricted:false },\n    cassava:   { top5:0.70, chokepoint:'Thailand \/ Nigeria',      restricted:false },\n    palm:      { top5:0.90, chokepoint:'Indonesia \/ Malaysia',    restricted:true  },\n    cashew:    { top5:0.78, chokepoint:'Vietnam \/ India \/ Africa',restricted:false },\n    tea:       { top5:0.70, chokepoint:'Kenya \/ Sri Lanka \/ India',restricted:false },\n    hemp:      { top5:0.65, chokepoint:'Canada \/ France \/ China', restricted:false },\n    dates:     { top5:0.75, chokepoint:'Egypt \/ Saudi \/ Iran',    restricted:false },\n    bananas:   { top5:0.92, chokepoint:'Ecuador \/ Philippines',   restricted:false },\n    mango:     { top5:0.65, chokepoint:'India \/ Thailand \/ Mexico',restricted:false},\n    alfalfa:   { top5:0.85, chokepoint:'US Pacific \/ Spain',      restricted:false },\n    sesame:    { top5:0.78, chokepoint:'Sudan \/ India \/ Tanzania',restricted:false },\n    quinoa:    { top5:0.95, chokepoint:'Bolivia \/ Peru',          restricted:false },\n    sweetpotato:{top5:0.92,chokepoint:'China \/ E Africa',         restricted:false },\n    yam:       { top5:0.95, chokepoint:'West Africa',             restricted:false },\n    pistachio: { top5:0.95, chokepoint:'Iran \/ US California',    restricted:true  },\n    pepper:    { top5:0.85, chokepoint:'Vietnam \/ India',         restricted:false },\n    hazelnut:  { top5:0.85, chokepoint:'Turkey \/ Italy',          restricted:false },\n    jute:      { top5:0.95, chokepoint:'Bangladesh \/ India',      restricted:false }\n  };\n  \/\/ Zone-level logistics access (0-100). Higher = better access to ports,\n  \/\/ rail, processing, storage. Calibrated from published trade-flow + port\n  \/\/ throughput + rail-density data. Drives the \"Zone access\" card + a\n  \/\/ caveat in the headline when \u2264 40 (long haul kills landed margin).\n  var LOGISTICS = {\n    'us-cornbelt':90,'us-n-plains':75,'us-lake':85,'us-northeast':85,\n    'us-cotton':80,'us-delta':92,'us-s-plains':70,'us-pnw':88,'us-california':88,\n    'ca-prairies':65,'ca-ontario':88,'ca-bc':85,'ca-maritimes':75,\n    'mx-bajio':65,\n    'br-cerrado':35,'br-sul':78,'br-sertao':45,'br-bahia':60,\n    'ar-pampas':80,\n    'gb-atlantic':85,'fr-atlantic':88,'de-atl-cont':90,\n    'es-med':80,'it-med':78,\n    'pl-cee':70,'ro-cee':65,'hu-cee':60,\n    'ua-polissia':50,'ua-carpathian':40,'ua-podillia':55,\n    'ua-forest-steppe':60,'ua-steppe':70,'ua-steppe-south':90,\n    'kz-steppe':30,\n    'in-indo-gangetic':55,'in-deccan':60,\n    'cn-n-plain':80,'cn-northeast':70,\n    'za-highveld':70,'za-cape':75,\n    'eg-nile':75,'ma-atlas':70,\n    'et-highlands':35,'ke-highlands':60,\n    'ng-savanna':45,'ci-cocoa':60,'zm-commercial':40,\n    'il-coastal':82,\n    'vn-central':70,'id-sumatra':60,'tr-anatolia':70,'th-central':75,\n    'pk-indus':55,'cl-central':78,'co-andean':60,'bd-delta':45,\n    'au-wa-wheat':90,'au-murray':75,'au-qld':78,'au-tasmania':60,'au-north':35,\n    'nz-temperate':82\n  };\n  \/\/ Zone-level input cost index (0-100). Higher = more expensive inputs\n  \/\/ (fertilizer, fuel, seed, labor, water) for the dominant farming system.\n  \/\/ EU zones high (regulations + N price), IN low (heavy subsidies),\n  \/\/ KZ low (extensive), CA California very high (water + labor).\n  \/\/ Drives the \"Zone access\" card + headline caveat when \u2265 75.\n  var INPUT_COST = {\n    'us-cornbelt':70,'us-n-plains':68,'us-lake':72,'us-northeast':78,\n    'us-cotton':75,'us-delta':72,'us-s-plains':65,'us-pnw':75,'us-california':90,\n    'ca-prairies':65,'ca-ontario':75,'ca-bc':82,'ca-maritimes':70,\n    'mx-bajio':55,\n    'br-cerrado':60,'br-sul':65,'br-sertao':50,'br-bahia':55,\n    'ar-pampas':75,\n    'gb-atlantic':85,'fr-atlantic':82,'de-atl-cont':82,\n    'es-med':75,'it-med':78,\n    'pl-cee':65,'ro-cee':60,'hu-cee':65,\n    'ua-polissia':45,'ua-carpathian':50,'ua-podillia':55,\n    'ua-forest-steppe':55,'ua-steppe':50,'ua-steppe-south':55,\n    'kz-steppe':35,\n    'in-indo-gangetic':30,'in-deccan':35,\n    'cn-n-plain':40,'cn-northeast':42,\n    'za-highveld':60,'za-cape':65,\n    'eg-nile':50,'ma-atlas':55,\n    'et-highlands':45,'ke-highlands':55,\n    'ng-savanna':35,'ci-cocoa':45,'zm-commercial':60,\n    'il-coastal':88,\n    'vn-central':45,'id-sumatra':50,'tr-anatolia':60,'th-central':45,\n    'pk-indus':35,'cl-central':70,'co-andean':50,'bd-delta':30,\n    'au-wa-wheat':72,'au-murray':85,'au-qld':75,'au-tasmania':80,'au-north':70,\n    'nz-temperate':80\n  };\n  \/\/ Land-price index (0-100, normalized to global cropland). 100 = US Iowa \/\n  \/\/ CA Central Valley premium prices, 0 = frontier \/ very cheap. Drives the\n  \/\/ \"cheap dirt\" axis of the investment opportunity composite. Calibrated\n  \/\/ from published per-hectare cropland transaction medians.\n  var LAND_PRICE_INDEX = {\n    'us-cornbelt':95,'us-n-plains':60,'us-lake':70,'us-northeast':80,\n    'us-cotton':60,'us-delta':70,'us-s-plains':55,'us-pnw':70,'us-california':100,\n    'ca-prairies':50,'ca-ontario':75,'ca-bc':80,'ca-maritimes':50,\n    'mx-bajio':35,\n    'br-cerrado':35,'br-sul':50,'br-sertao':15,'br-bahia':25,\n    'ar-pampas':50,\n    'gb-atlantic':85,'fr-atlantic':82,'de-atl-cont':88,\n    'es-med':75,'it-med':78,\n    'pl-cee':45,'ro-cee':35,'hu-cee':40,\n    'ua-polissia':12,'ua-carpathian':10,'ua-podillia':18,\n    'ua-forest-steppe':22,'ua-steppe':18,'ua-steppe-south':25,\n    'kz-steppe':8,\n    'in-indo-gangetic':30,'in-deccan':25,\n    'cn-n-plain':60,'cn-northeast':45,\n    'za-highveld':25,'za-cape':60,\n    'eg-nile':50,'ma-atlas':30,\n    'et-highlands':15,'ke-highlands':35,\n    'ng-savanna':12,'ci-cocoa':25,'zm-commercial':18,\n    'au-wa-wheat':35,'au-murray':50,'au-qld':45,'au-tasmania':70,'au-north':20,\n    'nz-temperate':80,\n    'il-coastal':80,\n    'vn-central':35,'id-sumatra':30,'tr-anatolia':35,'th-central':35,\n    'pk-indus':25,'cl-central':45,'co-andean':30,'bd-delta':35\n  };\n  \/\/ Yield gap (0-100). 0 = current yields \u2248 biophysical potential (mature\n  \/\/ markets where capital adds little). 100 = huge unrealized lift available\n  \/\/ from inputs, mechanisation, and modern agronomy. The \"uptake\" axis.\n  var YIELD_GAP = {\n    'us-cornbelt':10,'us-n-plains':20,'us-lake':20,'us-northeast':15,\n    'us-cotton':25,'us-delta':25,'us-s-plains':35,'us-pnw':20,'us-california':8,\n    'ca-prairies':25,'ca-ontario':20,'ca-bc':25,'ca-maritimes':30,\n    'mx-bajio':50,\n    'br-cerrado':30,'br-sul':25,'br-sertao':70,'br-bahia':55,\n    'ar-pampas':30,\n    'gb-atlantic':10,'fr-atlantic':12,'de-atl-cont':8,\n    'es-med':18,'it-med':18,\n    'pl-cee':25,'ro-cee':35,'hu-cee':30,\n    'ua-polissia':35,'ua-carpathian':35,'ua-podillia':30,\n    'ua-forest-steppe':30,'ua-steppe':30,'ua-steppe-south':25,\n    'kz-steppe':65,\n    'in-indo-gangetic':45,'in-deccan':60,\n    'cn-n-plain':25,'cn-northeast':35,\n    'za-highveld':50,'za-cape':25,\n    'eg-nile':35,'ma-atlas':55,\n    'et-highlands':75,'ke-highlands':65,\n    'ng-savanna':80,'ci-cocoa':50,'zm-commercial':50,\n    'au-wa-wheat':30,'au-murray':35,'au-qld':40,'au-tasmania':25,'au-north':60,\n    'nz-temperate':20,\n    'il-coastal':10,\n    'vn-central':40,'id-sumatra':45,'tr-anatolia':35,'th-central':35,\n    'pk-indus':55,'cl-central':25,'co-andean':40,'bd-delta':45\n  };\n  \/\/ Investment access (0-100). 100 = fully open to foreign capital, stable\n  \/\/ currency, deep markets, clear land titling. 0 = closed \/ war \/ capital\n  \/\/ controls \/ no foreign land ownership. Composite of foreign-ownership\n  \/\/ rules, political stability, currency risk, banking depth, title clarity.\n  var INVESTMENT_ACCESS = {\n    'us-cornbelt':95,'us-n-plains':95,'us-lake':95,'us-northeast':95,\n    'us-cotton':95,'us-delta':95,'us-s-plains':95,'us-pnw':95,'us-california':95,\n    'ca-prairies':95,'ca-ontario':95,'ca-bc':95,'ca-maritimes':95,\n    'mx-bajio':60,\n    'br-cerrado':62,'br-sul':62,'br-sertao':60,'br-bahia':62,\n    'ar-pampas':45,\n    'gb-atlantic':92,'fr-atlantic':85,'de-atl-cont':88,\n    'es-med':80,'it-med':78,\n    'pl-cee':75,'ro-cee':68,'hu-cee':68,\n    'ua-polissia':25,'ua-carpathian':25,'ua-podillia':25,\n    'ua-forest-steppe':25,'ua-steppe':25,'ua-steppe-south':25,\n    'kz-steppe':35,\n    'in-indo-gangetic':30,'in-deccan':30,\n    'cn-n-plain':25,'cn-northeast':25,\n    'za-highveld':65,'za-cape':65,\n    'eg-nile':40,'ma-atlas':55,\n    'et-highlands':30,'ke-highlands':55,\n    'ng-savanna':45,'ci-cocoa':50,'zm-commercial':70,\n    'au-wa-wheat':92,'au-murray':92,'au-qld':92,'au-tasmania':92,'au-north':92,\n    'nz-temperate':90,\n    'il-coastal':85,\n    'vn-central':55,'id-sumatra':50,'tr-anatolia':60,'th-central':65,\n    'pk-indus':35,'cl-central':80,'co-andean':55,'bd-delta':35\n  };\n  \/\/ Geopolitical risk (0-100). Higher = lower risk (lines up with the other\n  \/\/ scores). Captures EVENT-based risk that the Investment Access score does\n  \/\/ not: sovereign-credit standing (40%), political stability (30%), sanctions\n  \/\/ \/ proxy exposure (15%), expropriation + property-rights risk (15%).\n  \/\/ Sources (defensible, generic by design \u2014 see CLAUDE.md rule \u00a710):\n  \/\/   - sovereign ratings (Moody's \/ S+P \/ Fitch IG scale, normalized 0-100)\n  \/\/   - World Bank Worldwide Governance Indicators (Political Stability)\n  \/\/   - OFAC sanctions distance + secondary-sanctions risk\n  \/\/   - Heritage Foundation Property Rights index\n  \/\/ Distinct from INVESTMENT_ACCESS: that one is RULES-based (foreign-ownership\n  \/\/ law, banking depth, title clarity). Geopol captures CURRENT events.\n  var GEOPOLITICAL_RISK = {\n    'us-cornbelt':92,'us-n-plains':92,'us-lake':92,'us-northeast':92,\n    'us-cotton':92,'us-delta':92,'us-s-plains':92,'us-pnw':92,'us-california':92,\n    'ca-prairies':92,'ca-ontario':92,'ca-bc':92,'ca-maritimes':92,\n    'mx-bajio':50,\n    'br-cerrado':65,'br-sul':68,'br-sertao':62,'br-bahia':65,\n    'ar-pampas':35,\n    'gb-atlantic':88,'fr-atlantic':85,'de-atl-cont':90,\n    'es-med':80,'it-med':72,\n    'pl-cee':72,'ro-cee':65,'hu-cee':55,\n    'ua-polissia':25,'ua-carpathian':30,'ua-podillia':28,\n    'ua-forest-steppe':25,'ua-steppe':22,'ua-steppe-south':18,\n    'kz-steppe':45,\n    'in-indo-gangetic':60,'in-deccan':60,\n    'cn-n-plain':32,'cn-northeast':32,\n    'za-highveld':55,'za-cape':60,\n    'eg-nile':40,'ma-atlas':65,\n    'et-highlands':32,'ke-highlands':55,\n    'ng-savanna':38,'ci-cocoa':50,'zm-commercial':55,\n    'au-wa-wheat':92,'au-murray':92,'au-qld':92,'au-tasmania':92,'au-north':92,\n    'nz-temperate':95,\n    'il-coastal':60,\n    'vn-central':60,'id-sumatra':60,'tr-anatolia':45,'th-central':58,\n    'pk-indus':28,'cl-central':78,'co-andean':52,'bd-delta':38\n  };\n  function logisticsForZone(zid){ return LOGISTICS[zid] === undefined ? null : LOGISTICS[zid]; }\n  function inputCostForZone(zid){ return INPUT_COST[zid] === undefined ? null : INPUT_COST[zid]; }\n  function landPriceForZone(zid){ return LAND_PRICE_INDEX[zid] === undefined ? null : LAND_PRICE_INDEX[zid]; }\n  function yieldGapForZone(zid){ return YIELD_GAP[zid] === undefined ? null : YIELD_GAP[zid]; }\n  function investmentAccessForZone(zid){ return INVESTMENT_ACCESS[zid] === undefined ? null : INVESTMENT_ACCESS[zid]; }\n  function geopolRiskForZone(zid){ return GEOPOLITICAL_RISK[zid] === undefined ? null : GEOPOLITICAL_RISK[zid]; }\n  \/\/ Composite \"Investment opportunity\" score (0-100). Combines:\n  \/\/   - climate trajectory (best-crop crop-fit gain 2020\u2192heroDec, +pts)\n  \/\/   - cheap dirt (land price index inverted)\n  \/\/   - yield gap (room for capital + tech to lift yields)\n  \/\/   - logistics (port\/rail access \u2014 needed to monetize output)\n  \/\/   - investment access (can foreign capital actually deploy? rules-based)\n  \/\/   - geopolitical risk (events: sovereign + political stability + sanctions)\n  \/\/ Weights chosen so no single dimension dominates. Returns null if data\n  \/\/ missing.\n  function investmentOpportunityScore(zid){\n    var land = landPriceForZone(zid);\n    var gap  = yieldGapForZone(zid);\n    var logi = logisticsForZone(zid);\n    var inv  = investmentAccessForZone(zid);\n    var geo  = geopolRiskForZone(zid);\n    if(land === null ? true : (gap === null ? true : (logi === null ? true : (inv === null ? true : geo === null)))) return null;\n    \/\/ Climate trajectory: best-crop crop-fit gain 2020\u2192heroDec, capped 0-30 pts \u2192 0-100\n    var data = suitForZone(zid);\n    var heroDec = state.decade;\n    var climateScore = 50;\n    if(data ? data[2020] : false){\n      var base = data[2020], now = data[heroDec] ? data[heroDec] : base;\n      var best = -999;\n      var ck = Object.keys(base);\n      for(var i=0; i<ck.length; i++){\n        if(now[ck[i]] === undefined) continue;\n        var d = now[ck[i]] - base[ck[i]];\n        if(d > best) best = d;\n      }\n      if(best > -990) climateScore = Math.max(0, Math.min(100, 50 + best * 1.8));\n    }\n    var landInverted = 100 - land;\n    var score = 0.16 * climateScore + 0.20 * landInverted + 0.22 * gap + 0.16 * logi + 0.13 * inv + 0.13 * geo;\n    return Math.round(score);\n  }\n  function tierLabel(score, isLogistics){\n    if(score === null) return '\u2014';\n    if(isLogistics){\n      if(score >= 80) return 'excellent';\n      if(score >= 65) return 'good';\n      if(score >= 50) return 'moderate';\n      if(score >= 35) return 'weak';\n      return 'poor';\n    }\n    if(score >= 80) return 'very high';\n    if(score >= 65) return 'high';\n    if(score >= 50) return 'moderate';\n    if(score >= 35) return 'low';\n    return 'very low';\n  }\n  var SCENARIOS = {\n    open: {\n      label:'Open Trade', priceMult:1.00, volMult:1.00, inputMult:1.00,\n      topConcPenalty:0, chokeMult:1.0,\n      note:'Baseline. Free-flowing exports, no shocks. Commodity prices track demand and weather; volatility at 10-year norms.'\n    },\n    fragmented: {\n      label:'Bloc Fragmentation', priceMult:1.06, volMult:1.30, inputMult:1.10,\n      topConcPenalty:0.04, chokeMult:1.15,\n      note:'Trade walls between blocs. Importers pay 6-10% more, exporters lose 10-15% margin from lost market access. Volatility +30%. Input costs creep up.'\n    },\n    shock: {\n      label:'Export Ban Shock', priceMult:1.18, volMult:1.60, inputMult:1.25,\n      topConcPenalty:0.12, chokeMult:1.40,\n      note:'A major exporter goes offline (Black Sea closure, 2022-style). Prices spike most on high-concentration crops (sunflower, wheat, sorghum). Fertilizer N up 25-40% \u2014 net margin DOWN in importing countries even as crop prices rise.'\n    }\n  };\n  var TZ_COUNTRY = {\n    'America\/Chicago':'US','America\/Los_Angeles':'US','America\/New_York':'US','America\/Denver':'US',\n    'America\/Indiana\/Indianapolis':'US','America\/Detroit':'US','America\/Phoenix':'US','America\/Anchorage':'US',\n    'America\/Toronto':'CA','America\/Edmonton':'CA','America\/Winnipeg':'CA','America\/Vancouver':'CA',\n    'America\/Halifax':'CA','America\/Regina':'CA','America\/St_Johns':'CA','America\/Montreal':'CA',\n    'America\/Mexico_City':'MX','America\/Monterrey':'MX','America\/Cancun':'MX','America\/Tijuana':'MX',\n    'America\/Sao_Paulo':'BR','America\/Recife':'BR','America\/Bahia':'BR','America\/Fortaleza':'BR',\n    'America\/Argentina\/Buenos_Aires':'AR','America\/Argentina\/Cordoba':'AR','America\/Argentina\/Mendoza':'AR',\n    'America\/Montevideo':'AR','America\/Asuncion':'AR','America\/Santiago':'AR',\n    'Europe\/London':'GB','Europe\/Belfast':'GB','Europe\/Dublin':'GB',\n    'Europe\/Paris':'FR','Europe\/Brussels':'FR','Europe\/Luxembourg':'FR','Europe\/Amsterdam':'DE',\n    'Europe\/Berlin':'DE','Europe\/Vienna':'DE','Europe\/Zurich':'DE','Europe\/Copenhagen':'DE',\n    'Europe\/Madrid':'ES','Europe\/Lisbon':'ES','Atlantic\/Canary':'ES',\n    'Europe\/Rome':'IT','Europe\/Vatican':'IT','Europe\/Malta':'IT',\n    'Europe\/Warsaw':'PL','Europe\/Prague':'PL','Europe\/Bratislava':'PL','Europe\/Stockholm':'PL',\n    'Europe\/Oslo':'PL','Europe\/Helsinki':'PL',\n    'Europe\/Kiev':'UA','Europe\/Kyiv':'UA','Europe\/Uzhgorod':'UA','Europe\/Zaporozhye':'UA',\n    'Europe\/Bucharest':'RO','Europe\/Sofia':'RO','Europe\/Chisinau':'RO',\n    'Europe\/Budapest':'HU','Europe\/Belgrade':'HU','Europe\/Zagreb':'HU','Europe\/Ljubljana':'HU',\n    'Asia\/Almaty':'KZ','Asia\/Aqtobe':'KZ','Asia\/Aqtau':'KZ','Asia\/Tashkent':'KZ','Asia\/Bishkek':'KZ',\n    'Asia\/Yekaterinburg':'KZ','Asia\/Omsk':'KZ','Asia\/Novosibirsk':'KZ',\n    'Africa\/Johannesburg':'ZA','Africa\/Cairo':'ZA','Africa\/Nairobi':'ZA','Africa\/Lagos':'ZA',\n    'Australia\/Sydney':'AU','Australia\/Melbourne':'AU','Australia\/Brisbane':'AU',\n    'Australia\/Perth':'AU','Australia\/Adelaide':'AU','Australia\/Darwin':'AU',\n    'Pacific\/Auckland':'NZ','Pacific\/Chatham':'NZ',\n    'Asia\/Kolkata':'IN','Asia\/Calcutta':'IN','Asia\/Karachi':'IN','Asia\/Dhaka':'IN','Asia\/Colombo':'IN',\n    'Asia\/Shanghai':'CN','Asia\/Beijing':'CN','Asia\/Urumqi':'CN','Asia\/Hong_Kong':'CN',\n    'Asia\/Taipei':'CN','Asia\/Seoul':'CN','Asia\/Tokyo':'CN'\n  };\n  var detectSource = '';\n  function detectCountry(){\n    try {\n      var saved = localStorage.getItem('gpy-acx-country');\n      if(saved ? COUNTRIES[saved] : false){ detectSource = 'saved'; return saved; }\n    } catch(_){}\n    var langs = [];\n    try { if(navigator.languages ? navigator.languages.length : false) langs = Array.prototype.slice.call(navigator.languages); } catch(_){}\n    try { if(navigator.language) langs.push(navigator.language); } catch(_){}\n    for(var li=0; li<langs.length; li++){\n      var lng = (langs[li] || '').trim();\n      if(!lng) continue;\n      try {\n        var loc = new Intl.Locale(lng);\n        if(loc.region ? COUNTRIES[loc.region] : false){\n          detectSource = 'language ' + lng;\n          return loc.region;\n        }\n      } catch(_){}\n      var m = lng.match(\/[a-z]{2,3}[_-]([A-Z]{2})(?:[_-]|$)\/);\n      if(m ? COUNTRIES[m[1]] : false){\n        detectSource = 'language ' + lng;\n        return m[1];\n      }\n    }\n    try {\n      var tz = Intl.DateTimeFormat().resolvedOptions().timeZone || '';\n      if(TZ_COUNTRY[tz] ? COUNTRIES[TZ_COUNTRY[tz]] : false){\n        detectSource = 'timezone ' + tz;\n        return TZ_COUNTRY[tz];\n      }\n    } catch(_){}\n    detectSource = 'fallback';\n    return 'US';\n  }\n  \/\/ Initial country (browser-detected) \u2192 its primary zone (first in COUNTRIES.zones).\n  \/\/ User can override either by clicking another zone marker on the map.\n  var initialCountry = detectCountry();\n  var initialZone = (function(){\n    try {\n      var saved = localStorage.getItem('gpy-acx-zone');\n      if(saved ? ZONES[saved] : false) return saved;\n    } catch(_){}\n    var info = COUNTRIES[initialCountry];\n    return (info ? info.zones[0] : COUNTRIES.US.zones[0]).id;\n  })();\n  \/\/ Default units: imperial for US\/CA (where users think in bushels\/acres),\n  \/\/ metric elsewhere. User can override via the filter pill.\n  var initialUnits = (COUNTRIES[initialCountry] ? COUNTRIES[initialCountry].units : 'metric');\n  try {\n    var savedU = localStorage.getItem('gpy-acx-units');\n    if(savedU === 'metric' ? true : savedU === 'imperial') initialUnits = savedU;\n  } catch(_){}\n  var initialCurrency = 'USD';\n  try {\n    var savedC = localStorage.getItem('gpy-acx-currency');\n    if(savedC === 'USD' ? true : savedC === 'EUR' ? true : savedC === 'GBP') initialCurrency = savedC;\n  } catch(_){}\n  var state = {\n    country:   initialCountry,\n    zone:      initialZone,\n    decade:    2050,\n    scenario:  'open',\n    mapMetric: 'crop',\n    focusCrop: null,\n    units:     initialUnits,\n    currency:  initialCurrency\n  };\n  \/\/ Currency reference rates (early-2026 midpoints). Static \u2014 for an SEO\n  \/\/ lead-magnet a live FX feed would add a runtime fetch we don't want.\n  var FX_RATE = { USD:1, EUR:0.92, GBP:0.79 };\n  var CURRENCY_SYM = { USD:'$', EUR:'\u20ac', GBP:'\u00a3' };\n  \/\/ Per-crop imperial price unit. Grains in bu (most common), weight crops\n  \/\/ in lb, potato\/rice in cwt. Crops not listed default to \/t even in imperial.\n  var CROP_IMP_UNIT = {\n    corn:'bu', wheat:'bu', soybean:'bu', oats:'bu', barley:'bu', rye:'bu',\n    sorghum:'bu', rapeseed:'bu', canola:'bu', millet:'bu', durum:'bu', buckwheat:'bu',\n    cotton:'lb', sunflower:'lb', peanut:'lb', sesame:'lb',\n    potato:'cwt', sweetpotato:'cwt', rice:'cwt', sugarbeet:'t', sugarcane:'t',\n    almond:'lb', cashew:'lb', hazelnut:'lb', pistachio:'lb',\n    pepper:'lb', mango:'lb', bananas:'lb', dates:'lb',\n    coffee:'lb', cocoa:'lb', tea:'lb',\n    olives:'lb', citrus:'lb', grapes:'lb', avocado:'lb',\n    chickpea:'lb', beans:'lb', lentils:'lb', peas:'lb', pulses:'lb',\n    mustard:'lb', tobacco:'lb', oilseeds:'lb',\n    cassava:'lb', yam:'lb', quinoa:'lb', jute:'lb',\n    palm:'lb', hops:'lb', hemp:'lb', alfalfa:'t', wine:'t'\n  };\n  \/\/ tonne \u2192 imperial unit conversion factor for each crop's IMP unit\n  function tonneToImpFactor(crop){\n    var u = CROP_IMP_UNIT[crop] || 't';\n    if(u === 't')   return 1;\n    if(u === 'lb')  return 2204.62;\n    if(u === 'cwt') return 22.046;\n    \/\/ bu \u2014 varies by crop\n    var BU = {corn:39.37, wheat:36.74, soybean:36.74, oats:64.84, barley:45.93, rye:39.37, sorghum:39.37, rapeseed:44.09, canola:44.09, millet:48.50, durum:36.74, buckwheat:50.00};\n    return BU[crop] || 36.74;\n  }\n  \/\/ Convert USD\/t baseline \u2192 display string in current units + currency.\n  function formatPrice(usdPerTonne, crop){\n    var imp = state.units === 'imperial';\n    var u   = imp ? (CROP_IMP_UNIT[crop] || 't') : 't';\n    var factor = imp ? tonneToImpFactor(crop) : 1;\n    var pricePerUnit = usdPerTonne \/ factor;          \/\/ USD per chosen unit\n    var fx = FX_RATE[state.currency] || 1;\n    var val = pricePerUnit * fx;\n    var sym = CURRENCY_SYM[state.currency] || '$';\n    \/\/ Choose decimals: tonnes\/cwt \u2192 integer-ish; bushels\/lb \u2192 2 dp\n    var displayVal;\n    if(u === 't' ? true : u === 'cwt'){\n      displayVal = val >= 100 ? Math.round(val).toLocaleString('en-US') : (Math.round(val*10)\/10).toString();\n    } else {\n      displayVal = (Math.round(val*100)\/100).toFixed(2);\n    }\n    return sym + displayVal + ' \/ ' + u;\n  }\n  var playTimer = null;\n  \/\/ \u2500\u2500\u2500 COLOR HELPERS for the world-view map \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  function hexToRgb(h){\n    return [parseInt(h.slice(1,3),16), parseInt(h.slice(3,5),16), parseInt(h.slice(5,7),16)];\n  }\n  function interpHex(a, b, t){\n    var ra = hexToRgb(a), rb = hexToRgb(b);\n    var r = Math.round(ra[0] + (rb[0]-ra[0]) * t);\n    var g = Math.round(ra[1] + (rb[1]-ra[1]) * t);\n    var bl= Math.round(ra[2] + (rb[2]-ra[2]) * t);\n    return 'rgb(' + r + ',' + g + ',' + bl + ')';\n  }\n  \/\/ Picks a colour for a zone marker based on the active map metric. Returns\n  \/\/ rgb(...) string. Falls back to neutral gray if the zone has no dataset.\n  function colorForZone(zoneId, metric){\n    var zinfo = ZONES[zoneId];\n    if(!zinfo) return '#9ca3af';\n    var heroDec = state.decade;\n    if(metric === 'crop'){\n      var data = suitForZone(zoneId);\n      if(!data ? true : !data[2020]) return '#9ca3af';\n      var base = data[2020], now = data[heroDec] ? data[heroDec] : base;\n      var best = -999;\n      var ck = Object.keys(base);\n      for(var i=0; i<ck.length; i++){\n        if(now[ck[i]] === undefined) continue;\n        var d = now[ck[i]] - base[ck[i]];\n        if(d > best) best = d;\n      }\n      if(best <= -990) return '#9ca3af';\n      if(best >= 0){\n        var t = Math.min(1, best \/ 20);\n        return interpHex('#dcfce7', '#15803d', t);\n      } else {\n        var t2 = Math.min(1, -best \/ 20);\n        return interpHex('#fef9c3', '#b91c1c', t2);\n      }\n    }\n    if(metric === 'climate'){\n      var c = climateForZone(zoneId);\n      if(!c ? true : !c[2020]) return '#9ca3af';\n      var cn = c[heroDec] ? c[heroDec] : c[2020];\n      var dHeat = cn.heat - c[2020].heat;\n      var t3 = Math.min(1, Math.max(0, dHeat \/ 70));\n      return interpHex('#fef9c3', '#991b1b', t3);\n    }\n    if(metric === 'trade'){\n      var data2 = suitForZone(zoneId);\n      if(!data2) return '#9ca3af';\n      var nowS = data2[heroDec] ? data2[heroDec] : data2[2020];\n      if(!nowS) return '#9ca3af';\n      var top = null, topVal = -1;\n      var keys2 = Object.keys(nowS);\n      for(var j=0; j<keys2.length; j++){\n        if(nowS[keys2[j]] > topVal){ top = keys2[j]; topVal = nowS[keys2[j]]; }\n      }\n      var tr = TRADE[top];\n      if(!tr) return '#9ca3af';\n      var scen = SCENARIOS[state.scenario];\n      var shift = (scen.priceMult - 1) + (tr.top5 >= 0.75 ? scen.topConcPenalty : 0);\n      var t4 = Math.min(1, Math.max(0, shift \/ 0.25));\n      return interpHex('#dcfce7', '#991b1b', t4);\n    }\n    if(metric === 'invest'){\n      var s = investmentOpportunityScore(zoneId);\n      if(s === null) return '#9ca3af';\n      \/\/ 30 = uninteresting (gray), 60 = solid (lime), 80+ = top pick (deep green).\n      if(s < 30) return interpHex('#e5e7eb', '#fde68a', Math.max(0, s \/ 30));\n      if(s < 60) return interpHex('#fde68a', '#bef264', (s - 30) \/ 30);\n      return interpHex('#bef264', '#15803d', Math.min(1, (s - 60) \/ 40));\n    }\n    return '#9ca3af';\n  }\n  \/\/ Back-compat alias used by older call sites that pass a country code.\n  function colorForCountry(cc, metric){\n    var info = COUNTRIES[cc];\n    if(!info ? true : !info.zones.length) return '#9ca3af';\n    return colorForZone(info.zones[0].id, metric);\n  }\n  \/\/ Returns the gradient string for the scale strip below the map.\n  function gradientForMetric(metric){\n    if(metric === 'crop')    return 'linear-gradient(90deg,#b91c1c,#fef9c3,#15803d)';\n    if(metric === 'climate') return 'linear-gradient(90deg,#fef9c3,#991b1b)';\n    if(metric === 'trade')   return 'linear-gradient(90deg,#dcfce7,#991b1b)';\n    if(metric === 'invest')  return 'linear-gradient(90deg,#e5e7eb,#fde68a,#bef264,#15803d)';\n    return 'linear-gradient(90deg,#9ca3af,#9ca3af)';\n  }\n  function scaleLabelsForMetric(metric){\n    if(metric === 'crop')    return { lo:'biggest loss', hi:'biggest gain' };\n    if(metric === 'climate') return { lo:'modest heat',  hi:'extreme heat' };\n    if(metric === 'trade')   return { lo:'low impact',   hi:'high impact'  };\n    if(metric === 'invest')  return { lo:'low fit',      hi:'top opportunity' };\n    return { lo:'\u2014', hi:'\u2014' };\n  }\n  function projGlobe(lng, lat, W, H){\n    return { x: (lng + 180) \/ 360 * W, y: (90 - lat) \/ 180 * H };\n  }\n  \/\/ \u2500\u2500\u2500 Real interactive map (Leaflet 1.9.4 + OpenStreetMap tiles) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  \/\/ Lib + tiles are loaded from public CDNs on first paint. No API key.\n  \/\/ No uploaded user data leaves the browser \u2014 only tile XYZ requests for\n  \/\/ the basemap, which a basemap would make anyway. Markers + tooltip\n  \/\/ content + flyTo are driven by the same state (decade \/ scenario \/\n  \/\/ metric \/ country) as the rest of the tool, so the map stays in sync\n  \/\/ with every interaction.\n  var leafletMap = null;\n  var mapMarkers = {};\n  var mapBlobs   = {};   \/\/ soft semi-transparent circles suggesting zone extent\n  var leafletReady = false;\n  \/\/ Per-country approximate \"zone reach\" radius (km) for the soft blob.\n  \/\/ Honest about precision \u2014 these are not real polygon boundaries, just a\n  \/\/ visual cue that a zone covers a region not a single point.\n  var COUNTRY_RADIUS_KM = {\n    US:380, CA:400, BR:450, AR:350, AU:500,\n    CN:380, IN:330, KZ:450, RU:500,\n    UA:230, FR:230, DE:220, ES:230, IT:200, PL:230, RO:180, HU:160,\n    TR:280, ID:320, VN:250, TH:230, PK:290, CL:280, CO:250, BD:170,\n    EG:180, MA:240, ET:240, KE:180, NG:280, CI:200, ZM:290, ZA:280,\n    GB:160, NZ:180, IL:90, MX:280, JP:200\n  };\n  var leafletLoading = false;\n  var LEAFLET_CSS = 'https:\/\/unpkg.com\/leaflet@1.9.4\/dist\/leaflet.css';\n  var LEAFLET_JS  = 'https:\/\/unpkg.com\/leaflet@1.9.4\/dist\/leaflet.js';\n  function loadLeaflet(cb){\n    if(window.L){ cb(); return; }\n    if(leafletLoading){\n      var existingJs = document.querySelector('script[data-acx-leaflet]');\n      if(existingJs) existingJs.addEventListener('load', cb);\n      return;\n    }\n    leafletLoading = true;\n    if(!document.querySelector('link[data-acx-leaflet]')){\n      var css = document.createElement('link');\n      css.rel = 'stylesheet';\n      css.href = LEAFLET_CSS;\n      css.integrity = 'sha256-p4NxAoJBhIIN+hmNHrzRCf9tD\/miZyoHS5obTRR9BMY=';\n      css.crossOrigin = '';\n      css.setAttribute('data-acx-leaflet', '1');\n      document.head.appendChild(css);\n    }\n    var js = document.createElement('script');\n    js.src = LEAFLET_JS;\n    js.integrity = 'sha256-20nQCchB9co0qIjJZRGuk2\/Z9VM+kNiyxNV1lvTlZBo=';\n    js.crossOrigin = '';\n    js.async = true;\n    js.setAttribute('data-acx-leaflet', '1');\n    js.onload = function(){ cb(); };\n    js.onerror = function(){\n      var c = document.getElementById('acx-mapcard-globe');\n      if(c){ c.classList.remove('loading'); c.classList.add('failed'); }\n    };\n    document.head.appendChild(js);\n  }\n  function initLeafletMap(){\n    if(leafletReady) return;\n    var container = $('#acx-mapcard-globe');\n    if(!container ? true : !window.L) return;\n    container.classList.remove('loading');\n    \/\/ Zoom config:\n    \/\/   scrollWheelZoom        \u2014 disabled until the user clicks the map, so\n    \/\/                            scrolling the page over the map doesn't\n    \/\/                            hijack the wheel. After click it's live.\n    \/\/   touchZoom              \u2014 pinch-to-zoom on trackpads + mobile (default)\n    \/\/   wheelPxPerZoomLevel    \u2014 bigger = less twitchy on Mac trackpads\n    leafletMap = window.L.map(container, {\n      center: [30, 10],\n      zoom: 2,\n      minZoom: 1,\n      maxZoom: 6,\n      worldCopyJump: true,\n      scrollWheelZoom: false,\n      touchZoom: true,\n      wheelPxPerZoomLevel: 80,\n      wheelDebounceTime: 40,\n      zoomControl: true,\n      attributionControl: true\n    });\n    \/\/ Activate wheel zoom after first click\/tap (Google Maps embed pattern).\n    \/\/ Disables again on mouseleave so the next time the user scrolls past,\n    \/\/ the page scrolls normally.\n    function activateWheel(){\n      if(!leafletMap.scrollWheelZoom.enabled()) leafletMap.scrollWheelZoom.enable();\n      container.classList.add('wheel-on');\n    }\n    function deactivateWheel(){\n      if(leafletMap.scrollWheelZoom.enabled()) leafletMap.scrollWheelZoom.disable();\n      container.classList.remove('wheel-on');\n    }\n    leafletMap.on('click', activateWheel);\n    leafletMap.on('focus', activateWheel);\n    leafletMap.on('mouseout', function(ev){\n      if(ev.originalEvent ? ev.originalEvent.relatedTarget : false){\n        if(container.contains(ev.originalEvent.relatedTarget)) return;\n      }\n      deactivateWheel();\n    });\n    \/\/ Esri World Imagery (satellite). Free for low\/mid volume use, requires\n    \/\/ attribution. For heavy traffic, swap to a paid provider with a key.\n    window.L.tileLayer('https:\/\/server.arcgisonline.com\/ArcGIS\/rest\/services\/World_Imagery\/MapServer\/tile\/{z}\/{y}\/{x}', {\n      attribution: 'Tiles \u00a9 Esri \u2014 Source: Esri, USGS, NASA, NOAA, and the GIS User Community',\n      maxZoom: 6,\n      minZoom: 1\n    }).addTo(leafletMap);\n    \/\/ Reference overlay (country boundaries + city labels) \u2014 kept light so\n    \/\/ the satellite imagery still shows through.\n    window.L.tileLayer('https:\/\/server.arcgisonline.com\/ArcGIS\/rest\/services\/Reference\/World_Boundaries_and_Places\/MapServer\/tile\/{z}\/{y}\/{x}', {\n      maxZoom: 6,\n      minZoom: 1,\n      opacity: 0.85,\n      pane: 'overlayPane'\n    }).addTo(leafletMap);\n    \/\/ Soft \"blob\" circles drawn BEFORE the markers so they sit underneath.\n    \/\/ Each blob is a faint, semi-transparent disc roughly approximating\n    \/\/ the zone's geographic extent. interactive:false so clicks pass\n    \/\/ through to the marker on top.\n    var zoneIds = Object.keys(ZONES);\n    for(var bi=0; bi<zoneIds.length; bi++){\n      (function(zid){\n        var zinfo = ZONES[zid];\n        if(!zinfo ? true : !zinfo.coord) return;\n        var rkm = COUNTRY_RADIUS_KM[zinfo.countryCode] ? COUNTRY_RADIUS_KM[zinfo.countryCode] : 250;\n        var c0 = colorForZone(zid, state.mapMetric);\n        var blob = window.L.circle([zinfo.coord[1], zinfo.coord[0]], {\n          radius: rkm * 1000,\n          fillColor: c0,\n          fillOpacity: 0.16,\n          color: c0,\n          opacity: 0.30,\n          weight: 1,\n          interactive: false\n        }).addTo(leafletMap);\n        mapBlobs[zid] = blob;\n      })(zoneIds[bi]);\n    }\n    \/\/ One marker per agroclimatic zone (not per country). Big countries get\n    \/\/ multiple markers; each has its own zone dataset.\n    for(var k=0; k<zoneIds.length; k++){\n      (function(zid){\n        var zinfo = ZONES[zid];\n        if(!zinfo ? true : !zinfo.coord) return;\n        var coord = zinfo.coord;\n        var isOn = zid === state.zone;\n        var marker = window.L.circleMarker([coord[1], coord[0]], {\n          radius: isOn ? 11 : 8,\n          fillColor: colorForZone(zid, state.mapMetric),\n          color: isOn ? '#f76a0c' : '#ffffff',\n          weight: isOn ? 3 : 2,\n          fillOpacity: 0.92,\n          className: 'acx-leaf-marker' + (isOn ? ' on' : '')\n        }).addTo(leafletMap);\n        marker.bindTooltip(tooltipFor(zid), {\n          className: 'acx-leaf-tip',\n          direction: 'top',\n          offset: [0, -10],\n          sticky: false,\n          opacity: 1\n        });\n        marker.on('click', function(){\n          selectZone(zid);\n          flyToZone(zid);\n        });\n        \/\/ Bubble hover effect \u2014 grow + glow on enter, restore on leave.\n        \/\/ Recolor function repaints everything so we just bump the visual\n        \/\/ briefly here; selection state is preserved.\n        marker.on('mouseover', function(){\n          var isOn = zid === state.zone;\n          marker.setStyle({\n            radius: isOn ? 15 : 13,\n            weight: isOn ? 4 : 3,\n            color: '#f76a0c',\n            fillOpacity: 1\n          });\n          if(marker._path) marker._path.style.filter = 'drop-shadow(0 0 6px rgba(247,106,12,0.6))';\n        });\n        marker.on('mouseout', function(){\n          var isOn = zid === state.zone;\n          marker.setStyle({\n            radius: isOn ? 11 : 8,\n            weight: isOn ? 3 : 2,\n            color: isOn ? '#f76a0c' : '#ffffff',\n            fillOpacity: 0.92\n          });\n          if(marker._path) marker._path.style.filter = '';\n        });\n        mapMarkers[zid] = marker;\n      })(zoneIds[k]);\n    }\n    leafletReady = true;\n    recolorGlobeMap();\n  }\n  \/\/ Tooltip HTML \u2014 bound to each marker via Leaflet's bindTooltip. Content is\n  \/\/ metric-aware: top crop gainer\/loser, climate deltas, or scenario price shift.\n  function tooltipFor(zoneId){\n    var zinfo = ZONES[zoneId];\n    if(!zinfo) return '';\n    var heroDec = state.decade;\n    var html = '<div class=\"tip-name\">' + zinfo.country + ' \u2014 ' + zinfo.label + '<\/div>';\n    html += '<div class=\"tip-zone\">' + heroDec + '<\/div>';\n    if(state.mapMetric === 'crop'){\n      var data = suitForZone(zoneId);\n      if(data ? data[2020] : false){\n        var base = data[2020], now = data[heroDec] ? data[heroDec] : base;\n        var deltas = [];\n        var ck = Object.keys(base);\n        for(var i=0; i<ck.length; i++){\n          var cr = ck[i];\n          if(now[cr] === undefined) continue;\n          deltas.push({ c:cr, d:now[cr]-base[cr] });\n        }\n        deltas.sort(function(a, b){ return b.d - a.d; });\n        var win = deltas.length ? deltas[0] : null;\n        var lose = deltas.length ? deltas[deltas.length-1] : null;\n        if(win) html += '<div class=\"tip-stat\"><span class=\"tip-h\">Top gainer<\/span> <span class=\"tip-v win\">' + win.c + ' ' + (win.d>=0?'+':'') + win.d + ' pts<\/span><\/div>';\n        if(lose) html += '<div class=\"tip-stat\"><span class=\"tip-h\">Top loser<\/span> <span class=\"tip-v lose\">' + lose.c + ' ' + (lose.d>=0?'+':'') + lose.d + ' pts<\/span><\/div>';\n      }\n    } else if(state.mapMetric === 'climate'){\n      var c = climateForZone(zoneId);\n      if(c ? c[2020] : false){\n        var cb = c[2020], cn = c[heroDec] ? c[heroDec] : cb;\n        var dG = cn.gdd - cb.gdd;\n        var dH = cn.heat - cb.heat;\n        var dPpct = cb.precip ? (cn.precip - cb.precip) \/ cb.precip * 100 : 0;\n        html += '<div class=\"tip-stat\"><span class=\"tip-h\">GDD<\/span> <span class=\"tip-v\">' + (dG>=0?'+':'') + Math.round(dG) + '<\/span><\/div>';\n        html += '<div class=\"tip-stat\"><span class=\"tip-h\">Heat days<\/span> <span class=\"tip-v ' + (dH>=10?'warn':'') + '\">' + (dH>=0?'+':'') + Math.round(dH) + ' d<\/span><\/div>';\n        html += '<div class=\"tip-stat\"><span class=\"tip-h\">Rainfall<\/span> <span class=\"tip-v ' + (dPpct<=-5?'warn':'') + '\">' + (dPpct>=0?'+':'') + Math.round(dPpct) + '%<\/span><\/div>';\n      }\n    } else if(state.mapMetric === 'trade'){\n      var data2 = suitForZone(zoneId);\n      if(data2 ? data2[heroDec] : false){\n        var nowS = data2[heroDec];\n        var top = null, tv = -1;\n        var k2 = Object.keys(nowS);\n        for(var j=0; j<k2.length; j++){\n          if(nowS[k2[j]] > tv){ top = k2[j]; tv = nowS[k2[j]]; }\n        }\n        var tr = TRADE[top];\n        if(tr){\n          var scen = SCENARIOS[state.scenario];\n          var shift = (scen.priceMult - 1) + (tr.top5 >= 0.75 ? scen.topConcPenalty : 0);\n          var shiftCls = shift >= 0.10 ? 'lose' : (shift >= 0.04 ? 'warn' : 'win');\n          html += '<div class=\"tip-stat\"><span class=\"tip-h\">Top crop<\/span> <span class=\"tip-v\">' + top + '<\/span><\/div>';\n          html += '<div class=\"tip-stat\"><span class=\"tip-h\">Price shift<\/span> <span class=\"tip-v ' + shiftCls + '\">' + (shift>=0?'+':'') + Math.round(shift*100) + '%<\/span><\/div>';\n          html += '<div class=\"tip-stat\"><span class=\"tip-h\">Top-5 share<\/span> <span class=\"tip-v\">' + Math.round(tr.top5*100) + '%<\/span><\/div>';\n        }\n      }\n    } else if(state.mapMetric === 'invest'){\n      var op = investmentOpportunityScore(zoneId);\n      var land = landPriceForZone(zoneId);\n      var gap = yieldGapForZone(zoneId);\n      var inv = investmentAccessForZone(zoneId);\n      var geo = geopolRiskForZone(zoneId);\n      if(op !== null){\n        var opCls = op >= 65 ? 'win' : (op >= 45 ? 'warn' : 'lose');\n        html += '<div class=\"tip-stat\"><span class=\"tip-h\">Opportunity<\/span> <span class=\"tip-v ' + opCls + '\">' + op + ' \/ 100<\/span><\/div>';\n      }\n      if(land !== null) html += '<div class=\"tip-stat\"><span class=\"tip-h\">Land price<\/span> <span class=\"tip-v ' + (land<=30?'win':(land<=60?'warn':'')) + '\">' + land + ' \/ 100<\/span><\/div>';\n      if(gap !== null)  html += '<div class=\"tip-stat\"><span class=\"tip-h\">Yield gap<\/span> <span class=\"tip-v ' + (gap>=50?'win':(gap>=30?'warn':'')) + '\">' + gap + ' \/ 100<\/span><\/div>';\n      if(inv !== null)  html += '<div class=\"tip-stat\"><span class=\"tip-h\">Access<\/span> <span class=\"tip-v ' + (inv>=70?'win':(inv>=45?'warn':'lose')) + '\">' + inv + ' \/ 100<\/span><\/div>';\n      if(geo !== null)  html += '<div class=\"tip-stat\"><span class=\"tip-h\">Geopol risk<\/span> <span class=\"tip-v ' + (geo>=70?'win':(geo>=45?'warn':'lose')) + '\">' + geo + ' \/ 100<\/span><\/div>';\n    }\n    return html;\n  }\n  \/\/ Glide the map view to a country at ~regional zoom. Default view shows\n  \/\/ the whole crop band; focused view zooms to z=4 over the picked country.\n  function flyToZone(zoneId){\n    if(!leafletReady ? true : !leafletMap) return;\n    var z = ZONES[zoneId];\n    if(!z ? true : !z.coord) return;\n    leafletMap.flyTo([z.coord[1], z.coord[0]], 5, { duration: 0.85 });\n    var resetBtn = $('#acx-map-reset');\n    if(resetBtn) resetBtn.classList.remove('hidden');\n  }\n  function resetMap(){\n    if(!leafletReady ? true : !leafletMap) return;\n    leafletMap.flyTo([30, 10], 2, { duration: 0.7 });\n    var resetBtn = $('#acx-map-reset');\n    if(resetBtn) resetBtn.classList.add('hidden');\n  }\n  \/\/ Push current decade\/scenario\/metric\/zone into every marker (color,\n  \/\/ active-state ring, tooltip content) + the legend strip. Cheap to call.\n  function recolorGlobeMap(){\n    if(leafletReady ? leafletMap : false){\n      var keys = Object.keys(mapMarkers);\n      for(var i=0; i<keys.length; i++){\n        var zid = keys[i];\n        var m = mapMarkers[zid];\n        var isOn = zid === state.zone;\n        var col = colorForZone(zid, state.mapMetric);\n        m.setStyle({\n          fillColor: col,\n          radius: isOn ? 11 : 8,\n          weight: isOn ? 3 : 2,\n          color: isOn ? '#f76a0c' : '#ffffff'\n        });\n        m.setTooltipContent(tooltipFor(zid));\n        if(m._path){\n          m._path.classList.toggle('on', isOn);\n        }\n        \/\/ Keep the soft blob in sync \u2014 same metric color, slightly more\n        \/\/ opacity for the active zone so it reads as \"this is what you picked\".\n        if(mapBlobs[zid]){\n          mapBlobs[zid].setStyle({\n            fillColor: col,\n            color: col,\n            fillOpacity: isOn ? 0.28 : 0.16,\n            opacity:     isOn ? 0.55 : 0.30,\n            weight:      isOn ? 1.5  : 1\n          });\n        }\n      }\n    }\n    var heroDec = state.decade;\n    var decLbl = $('#acx-map-decade-lbl');\n    var decCap = $('#acx-map-decade-cap');\n    if(decLbl) decLbl.textContent = 'by ' + heroDec;\n    if(decCap) decCap.textContent = heroDec;\n    var grad = $('#acx-map-scale-grad');\n    if(grad) grad.style.background = gradientForMetric(state.mapMetric);\n    var labels = scaleLabelsForMetric(state.mapMetric);\n    var lo = $('#acx-map-scale-lo'), hi = $('#acx-map-scale-hi');\n    if(lo) lo.textContent = labels.lo;\n    if(hi) hi.textContent = labels.hi;\n    renderTopPicks();\n  }\n  \/\/ Per-zone ranking score for the active map metric. Always returns\n  \/\/ { rank, display, sign } where rank is the comparable number (higher =\n  \/\/ \"better\") and display is the human-readable string.\n  function rankingFor(zid, metric, heroDec){\n    if(metric === 'invest'){\n      var s = investmentOpportunityScore(zid);\n      if(s === null) return null;\n      return { rank: s, display: s + ' \/ 100' };\n    }\n    if(metric === 'crop'){\n      var data = suitForZone(zid);\n      if(!data ? true : !data[2020]) return null;\n      var base = data[2020], now = data[heroDec] ? data[heroDec] : base;\n      var best = -999;\n      var ck = Object.keys(base);\n      for(var i=0; i<ck.length; i++){\n        if(now[ck[i]] === undefined) continue;\n        var d = now[ck[i]] - base[ck[i]];\n        if(d > best) best = d;\n      }\n      if(best <= -990) return null;\n      return { rank: best, display: (best >= 0 ? '+' : '') + best + ' pts' };\n    }\n    if(metric === 'climate'){\n      var c = climateForZone(zid);\n      if(!c ? true : !c[2020]) return null;\n      var cn = c[heroDec] ? c[heroDec] : c[2020];\n      var dHeat = cn.heat - c[2020].heat;\n      \/\/ Mildest shift wins. Invert so smaller dHeat = higher rank.\n      return { rank: -dHeat, display: '+' + Math.round(dHeat) + ' heat d' };\n    }\n    if(metric === 'trade'){\n      var data2 = suitForZone(zid);\n      if(!data2 ? true : !data2[heroDec]) return null;\n      var nowS = data2[heroDec];\n      var top = null, tv = -1;\n      var keys2 = Object.keys(nowS);\n      for(var j=0; j<keys2.length; j++){\n        if(nowS[keys2[j]] > tv){ top = keys2[j]; tv = nowS[keys2[j]]; }\n      }\n      var tr = TRADE[top];\n      if(!tr) return null;\n      var scen = SCENARIOS[state.scenario];\n      var shift = (scen.priceMult - 1) + (tr.top5 >= 0.75 ? scen.topConcPenalty : 0);\n      \/\/ Lower shift = more resilient = higher rank. Invert.\n      return { rank: -shift, display: (shift>=0?'+':'') + Math.round(shift*100) + '% \u00b7 ' + top };\n    }\n    return null;\n  }\n  function rankingLabels(metric){\n    if(metric === 'crop')    return { win:'Biggest crop gainers',  lose:'Biggest crop losers',     headBase:'Global crop-fit shift' };\n    if(metric === 'climate') return { win:'Mildest climate shift', lose:'Harshest climate shift',  headBase:'Climate stress shift' };\n    if(metric === 'trade')   return { win:'Most shock-resilient',  lose:'Most shock-exposed',      headBase:'Trade price shift \u00b7 top crop' };\n    if(metric === 'invest')  return { win:'Top investment fit',    lose:'Weakest investment fit',  headBase:'Investment opportunity composite' };\n    return { win:'Top zones', lose:'Bottom zones', headBase:'Zones' };\n  }\n  \/\/ Renders a 2-row \"winners \/ losers\" strip under the map. Always shown\n  \/\/ (not only for Investment). Updates with every state change via\n  \/\/ recolorGlobeMap \u2192 renderTopPicks.\n  function renderTopPicks(){\n    var el = $('#acx-top-picks');\n    if(!el) return;\n    var heroDec = state.decade;\n    var ranked = [];\n    var zoneIds = Object.keys(ZONES);\n    for(var i=0; i<zoneIds.length; i++){\n      var r = rankingFor(zoneIds[i], state.mapMetric, heroDec);\n      if(r) ranked.push({ id:zoneIds[i], rank:r.rank, display:r.display });\n    }\n    if(ranked.length < 4){\n      el.classList.remove('show');\n      el.setAttribute('hidden', '');\n      return;\n    }\n    ranked.sort(function(a, b){ return b.rank - a.rank; });\n    var winners = ranked.slice(0, 3);\n    var losers  = ranked.slice(-3).reverse(); \/\/ reverse so worst-of-all is first\n    var labels = rankingLabels(state.mapMetric);\n    function rowHtml(items, kind){\n      var listHtml = '';\n      for(var k=0; k<items.length; k++){\n        var t = items[k];\n        var zi = ZONES[t.id];\n        listHtml += '<span class=\"acx-top-pick ' + kind + '\" data-cc=\"' + t.id + '\" role=\"button\" tabindex=\"0\">' +\n          '<span class=\"pick-rank\">' + (k+1) + '<\/span>' +\n          '<span>' + zi.country + ' \u00b7 ' + zi.label + '<\/span>' +\n          '<span class=\"pick-score\">' + t.display + '<\/span>' +\n        '<\/span>';\n      }\n      var sideLabel = kind === 'win' ? labels.win : labels.lose;\n      return '<div class=\"acx-top-picks-row ' + kind + '\">' +\n        '<span class=\"acx-top-picks-row-label\">' + sideLabel + '<\/span>' +\n        '<div class=\"acx-top-picks-list\">' + listHtml + '<\/div>' +\n      '<\/div>';\n    }\n    el.innerHTML =\n      '<div class=\"acx-top-picks-head\">' + labels.headBase + ' \u00b7 by ' + heroDec + '<\/div>' +\n      rowHtml(winners, 'win') +\n      rowHtml(losers,  'lose');\n    el.removeAttribute('hidden');\n    el.classList.add('show');\n    if(el.dataset.wired !== '1'){\n      el.addEventListener('click', function(ev){\n        var c = ev.target.closest ? ev.target.closest('.acx-top-pick') : null;\n        if(c ? c.dataset.cc : false){\n          selectZone(c.dataset.cc);\n          flyToZone(c.dataset.cc);\n        }\n      });\n      el.dataset.wired = '1';\n    }\n  }\n  \/\/ Kept as stubs so existing call sites don't break \u2014 Leaflet handles tooltips.\n  function hideTooltip(){}\n  \/\/ Select a zone (by id) \u2014 sets the parent country too. Persists in\n  \/\/ localStorage so the next visit lands on the same place.\n  function selectZone(zoneId){\n    var z = ZONES[zoneId];\n    if(!z) return;\n    state.zone = zoneId;\n    state.country = z.countryCode;\n    try {\n      localStorage.setItem('gpy-acx-zone', zoneId);\n      localStorage.setItem('gpy-acx-country', z.countryCode);\n    } catch(_){}\n    refreshFilterPills();\n    renderAll();\n  }\n  \/\/ Back-compat alias \u2014 country click translates to that country's primary zone.\n  function selectCountry(cc){\n    if(!COUNTRIES[cc] ? true : !COUNTRIES[cc].zones.length) return;\n    selectZone(COUNTRIES[cc].zones[0].id);\n  }\n  \/\/ Locale banner removed \u2014 region is now visible in the filter bar pill.\n  \/\/ Keeping the function as a no-op so legacy call sites stay happy.\n  function updateLocaleBanner(){}\n  \/\/ Universal chart tooltip \u2014 single floating element, content swapped on\n  \/\/ every mouseover. Per project rule \u00a717. Touch-friendly: tap shows, tap\n  \/\/ outside hides. Positioned in viewport coords (fixed), follows pointer.\n  function showChartTip(html, ev){\n    var tip = $('#acx-chart-tip');\n    if(!tip) return;\n    tip.innerHTML = html;\n    tip.classList.add('show');\n    var rect = tip.getBoundingClientRect();\n    var w = rect.width || 200, h = rect.height || 60;\n    var x = ev.clientX + 14, y = ev.clientY + 14;\n    if(x + w > window.innerWidth - 6) x = ev.clientX - w - 14;\n    if(y + h > window.innerHeight - 6) y = ev.clientY - h - 14;\n    if(x < 4) x = 4;\n    if(y < 4) y = 4;\n    tip.style.left = x + 'px';\n    tip.style.top  = y + 'px';\n  }\n  function hideChartTip(){\n    var tip = $('#acx-chart-tip');\n    if(tip) tip.classList.remove('show');\n  }\n  \/\/ Touch: any tap outside the active chart hides the tip.\n  document.addEventListener('touchstart', function(ev){\n    if(ev.target.closest ? !ev.target.closest('[data-acx-tip]') : true) hideChartTip();\n  }, { passive: true });\n  \/\/ Build tooltip HTML for a heatmap cell. Surfaces decade, current value,\n  \/\/ 2020 baseline, delta \u2014 the four numbers the user needs to read the cell.\n  function heatTipHTML(crop, decade, val, base){\n    var html = '<div class=\"tip-h\">' + crop + ' \u00b7 ' + decade + '<\/div>';\n    html += '<div class=\"tip-v\">Crop fit ' + val + ' \/ 100<\/div>';\n    if(decade !== 2020 ? base !== undefined : false){\n      var d = val - base;\n      var cls = d > 0 ? 'up' : (d < 0 ? 'down' : '');\n      var sign = d >= 0 ? '+' : '';\n      html += '<div class=\"tip-meta\">vs 2020 baseline ' + base + ' <span class=\"' + cls + '\">' + sign + d + ' pts<\/span><\/div>';\n    }\n    return html;\n  }\n  \/\/ Sparkline \/ bar tip \u2014 generic shape with title + value + optional meta.\n  function chartTipHTML(title, value, meta, metaCls){\n    var html = '<div class=\"tip-h\">' + title + '<\/div>';\n    html += '<div class=\"tip-v\">' + value + '<\/div>';\n    if(meta) html += '<div class=\"tip-meta ' + (metaCls || '') + '\">' + meta + '<\/div>';\n    return html;\n  }\n  function fmtDelta(n, suffix){\n    if(!isFinite(n)) return '\u2014';\n    var s = n >= 0 ? '+' : '';\n    var rounded = Math.abs(n) < 10 ? (Math.round(n*10)\/10) : Math.round(n);\n    return s + rounded + (suffix ? suffix : '');\n  }\n  \/\/ Draw a horizontal sparkline of a metric across all 5 decades into the given\n  \/\/ <svg>. Marks the current-decade point with a larger filled dot. Optionally\n  \/\/ tints the tile as 'warn' when the metric trend is unfavourable.\n  function drawSparkline(svgEl, traj, metric, metricLabel, unit){\n    if(!svgEl ? true : !traj) return;\n    var decs = [2020, 2030, 2040, 2050, 2060];\n    var vals = [];\n    for(var i=0; i<decs.length; i++){\n      var d = traj[decs[i]];\n      vals.push(d ? d[metric] : null);\n    }\n    var min = Infinity, max = -Infinity;\n    for(var j=0; j<vals.length; j++){\n      var v = vals[j];\n      if(v === null ? true : !isFinite(v)) continue;\n      if(v < min) min = v;\n      if(v > max) max = v;\n    }\n    if(!isFinite(min) ? true : !isFinite(max)) { svgEl.innerHTML = ''; return; }\n    if(min === max) { min = min - 1; max = max + 1; }\n    var W = 120, H = 30, padX = 4, padY = 5;\n    var range = max - min;\n    function xAt(idx){ return padX + idx * (W - 2*padX) \/ (decs.length - 1); }\n    function yAt(val){ return H - padY - (val - min) \/ range * (H - 2*padY); }\n    var pts = [];\n    var fillPts = ['M' + xAt(0).toFixed(1) + ',' + H];\n    for(var k=0; k<vals.length; k++){\n      if(vals[k] === null) continue;\n      var px = xAt(k);\n      var py = yAt(vals[k]);\n      pts.push((k === 0 ? 'M' : 'L') + px.toFixed(1) + ',' + py.toFixed(1));\n      fillPts.push('L' + px.toFixed(1) + ',' + py.toFixed(1));\n    }\n    fillPts.push('L' + xAt(vals.length - 1).toFixed(1) + ',' + H + 'Z');\n    \/\/ Invisible wider hit area on each decade column for hover tooltips\n    \/\/ (rule \u00a717). The visible dots are tiny so a transparent rect at the\n    \/\/ column gives a generous tap\/hover target.\n    var html = '';\n    html += '<path class=\"spark-fill\" d=\"' + fillPts.join('') + '\"\/>';\n    html += '<path class=\"spark-line\" d=\"' + pts.join('') + '\"\/>';\n    var curIdx = decs.indexOf(state.decade);\n    for(var d=0; d<vals.length; d++){\n      if(vals[d] === null) continue;\n      var cls = (d === curIdx) ? 'spark-dot now' : 'spark-dot';\n      var r = (d === curIdx) ? 4 : 2;\n      html += '<circle class=\"' + cls + '\" cx=\"' + xAt(d).toFixed(1) + '\" cy=\"' + yAt(vals[d]).toFixed(1) + '\" r=\"' + r + '\"\/>';\n    }\n    \/\/ Per-decade transparent hit rects for hover tooltips (rule \u00a717)\n    var colW = (W - 2*padX) \/ (decs.length - 1);\n    for(var hi=0; hi<decs.length; hi++){\n      if(vals[hi] === null) continue;\n      var rx = (xAt(hi) - colW\/2).toFixed(1);\n      html += '<rect x=\"' + rx + '\" y=\"0\" width=\"' + colW.toFixed(1) + '\" height=\"' + H + '\" fill=\"transparent\" data-acx-tip=\"spark\" data-dec=\"' + decs[hi] + '\" data-val=\"' + vals[hi] + '\" data-base=\"' + vals[0] + '\" data-metric-label=\"' + (metricLabel || metric) + '\" data-unit=\"' + (unit || '') + '\" style=\"cursor:help\"\/>';\n    }\n    svgEl.innerHTML = html;\n    if(svgEl.dataset.tipWired !== '1'){\n      svgEl.addEventListener('mousemove', function(ev){\n        var t = ev.target.closest ? ev.target.closest('[data-acx-tip=\"spark\"]') : null;\n        if(!t){ hideChartTip(); return; }\n        var dec = +t.dataset.dec, val = +t.dataset.val, base = +t.dataset.base;\n        var lbl = t.dataset.metricLabel, u = t.dataset.unit;\n        var meta = dec === 2020 ? '2020 baseline' : (val - base >= 0 ? '+' : '') + Math.round((val - base) * 10) \/ 10 + ' ' + u + ' vs 2020';\n        var cls = dec === 2020 ? '' : (val - base > 0 ? 'up' : 'down');\n        showChartTip(chartTipHTML(lbl + ' \u00b7 ' + dec, val + ' ' + u, meta, cls), ev);\n      });\n      svgEl.addEventListener('mouseleave', hideChartTip);\n      svgEl.dataset.tipWired = '1';\n    }\n  }\n  function renderClimate(){\n    var traj = climateForZone(state.zone);\n    if(!traj){\n      $('#acx-gdd').textContent = '\u2014';\n      return;\n    }\n    var base = traj[2020];\n    var now = traj[state.decade] ? traj[state.decade] : base;\n    $('#acx-snap-decade').textContent = state.decade;\n    var zinfoC = ZONES[state.zone];\n    var snapZ = $('#acx-snap-zone');\n    if(snapZ ? zinfoC : false) snapZ.textContent = zinfoC.country + ' \u2014 ' + zinfoC.label;\n    $('#acx-gdd').textContent = Math.round(now.gdd);\n    if(state.decade === 2020){\n      $('#acx-gdd-d').textContent = 'baseline \u00b7 GDD\u2081\u2080';\n      $('#acx-gdd-d').className = 'tile-delta';\n    } else {\n      var dG = now.gdd - base.gdd;\n      $('#acx-gdd-d').textContent = fmtDelta(dG, ' GDD vs 2020');\n      $('#acx-gdd-d').className = 'tile-delta ' + (dG >= 50 ? 'up' : (dG <= -50 ? 'down' : ''));\n    }\n    $('#acx-season').textContent = Math.round(now.season) + ' d';\n    if(state.decade === 2020){\n      $('#acx-season-d').textContent = 'baseline \u00b7 frost-free';\n      $('#acx-season-d').className = 'tile-delta';\n    } else {\n      var dS = now.season - base.season;\n      $('#acx-season-d').textContent = fmtDelta(dS, ' days vs 2020');\n      $('#acx-season-d').className = 'tile-delta ' + (dS >= 5 ? 'up' : (dS <= -5 ? 'down' : ''));\n    }\n    $('#acx-precip').textContent = Math.round(now.precip) + ' mm';\n    var precipWarn = false;\n    if(state.decade === 2020){\n      $('#acx-precip-d').textContent = 'baseline \u00b7 annual';\n      $('#acx-precip-d').className = 'tile-delta';\n    } else {\n      var dP = now.precip - base.precip;\n      var dPpct = base.precip ? (dP \/ base.precip * 100) : 0;\n      $('#acx-precip-d').textContent = fmtDelta(dPpct, '% vs 2020');\n      $('#acx-precip-d').className = 'tile-delta ' + (dPpct <= -5 ? 'warn' : (dPpct >= 5 ? 'up' : ''));\n      precipWarn = dPpct <= -5;\n    }\n    var precipTile = $('#acx-tile-precip');\n    if(precipTile) precipTile.classList.toggle('warn', precipWarn);\n    $('#acx-heat').textContent = Math.round(now.heat) + ' d';\n    var heatWarn = false;\n    if(state.decade === 2020){\n      $('#acx-heat-d').textContent = 'baseline \u00b7 days >35\u00b0C';\n      $('#acx-heat-d').className = 'tile-delta';\n    } else {\n      var dH = now.heat - base.heat;\n      $('#acx-heat-d').textContent = fmtDelta(dH, ' d vs 2020');\n      $('#acx-heat-d').className = 'tile-delta ' + (dH >= 10 ? 'warn' : (dH >= 3 ? 'down' : ''));\n      heatWarn = dH >= 10;\n    }\n    var heatTile = $('#acx-tile-heat');\n    if(heatTile) heatTile.classList.toggle('warn', heatWarn);\n    $('#acx-zone-from').textContent = base.zone;\n    $('#acx-zone-to').textContent = now.zone;\n    var shifting = base.zone !== now.zone;\n    var zoneEl = $('#acx-zone');\n    if(zoneEl) zoneEl.classList.toggle('shifting', shifting);\n    drawSparkline($('#acx-spark-gdd'),    traj, 'gdd',    'Growing-degree days', 'GDD');\n    drawSparkline($('#acx-spark-season'), traj, 'season', 'Season length',       'days');\n    drawSparkline($('#acx-spark-precip'), traj, 'precip', 'Annual rainfall',     'mm');\n    drawSparkline($('#acx-spark-heat'),   traj, 'heat',   'Heat-stress days',    'd');\n  }\n  \/\/ 5-band discrete palette matching the heatmap legend.\n  \/\/  0-19 = unsuitable (red), 20-39 = poor (orange), 40-59 = fair (amber),\n  \/\/  60-79 = good (olive-green), 80-100 = ideal (green).\n  function suitColor(s){\n    if(!isFinite(s)) return 'transparent';\n    var c = Math.max(0, Math.min(100, s));\n    if(c < 20) return '#dc2626';\n    if(c < 40) return '#ea580c';\n    if(c < 60) return '#d4a017';\n    if(c < 80) return '#65a30d';\n    return '#16a34a';\n  }\n  function renderHeatmap(){\n    var data = suitForZone(state.zone);\n    var tbl = $('#acx-heat-tbl');\n    if(!data){\n      tbl.innerHTML = '<tr><td colspan=\"6\" data-empty=\"1\" style=\"padding:14px;text-align:center\">No regional suitability data for this zone.<\/td><\/tr>';\n      return;\n    }\n    var decs = [2020, 2030, 2040, 2050, 2060];\n    var crops = {};\n    for(var di=0; di<decs.length; di++){\n      var d = data[decs[di]];\n      if(!d) continue;\n      var keys = Object.keys(d);\n      for(var ki=0; ki<keys.length; ki++) crops[keys[ki]] = true;\n    }\n    var cropList = Object.keys(crops);\n    var d2020 = data[2020] ? data[2020] : {};\n    var d2050 = data[2050] ? data[2050] : {};\n    cropList.sort(function(a, b){\n      var da = (d2050[a] !== undefined ? d2050[a] : 0) - (d2020[a] !== undefined ? d2020[a] : 0);\n      var db = (d2050[b] !== undefined ? d2050[b] : 0) - (d2020[b] !== undefined ? d2020[b] : 0);\n      return db - da;\n    });\n    var html = '<thead><tr><th style=\"text-align:left\">Crop<\/th>';\n    for(var hi=0; hi<decs.length; hi++) html += '<th>' + decs[hi] + '<\/th>';\n    html += '<\/tr><\/thead><tbody>';\n    for(var ri=0; ri<cropList.length; ri++){\n      var cr = cropList[ri];\n      var rowCls = (cr === state.focusCrop) ? ' class=\"focus\"' : '';\n      html += '<tr' + rowCls + ' data-crop=\"' + cr + '\"><td>' + cr + '<\/td>';\n      for(var ci=0; ci<decs.length; ci++){\n        var dd = decs[ci];\n        var v = data[dd] ? data[dd][cr] : null;\n        var nowCls = dd === state.decade ? ' heat-now' : '';\n        var base2020 = data[2020] ? data[2020][cr] : null;\n        if(v === undefined ? true : v === null){\n          html += '<td data-empty=\"1\" data-acx-tip=\"1\" data-crop=\"' + cr + '\" data-dec=\"' + dd + '\" class=\"' + (nowCls ? 'heat-now' : '') + '\">\u2014<\/td>';\n        } else {\n          var col = suitColor(v);\n          html += '<td style=\"background:' + col + '\" class=\"' + (nowCls ? 'heat-now' : '') + '\" data-acx-tip=\"1\" data-crop=\"' + cr + '\" data-dec=\"' + dd + '\" data-val=\"' + v + '\"' + (base2020 != null ? ' data-base=\"' + base2020 + '\"' : '') + ' aria-label=\"' + cr + ' ' + dd + ' suitability ' + v + '\">' + v + '<\/td>';\n        }\n      }\n      html += '<\/tr>';\n    }\n    html += '<\/tbody>';\n    tbl.innerHTML = html;\n  }\n  \/\/ Focused crop card \u2014 shows when state.focusCrop is set. Suitability\n  \/\/ trajectory (sparkline + delta) + price\/vol\/top5 under the active scenario.\n  function renderCropFocus(){\n    var panel = $('#acx-cropfocus');\n    if(!panel) return;\n    var cr = state.focusCrop;\n    var zinfo = ZONES[state.zone];\n    if(!cr ? true : !zinfo){\n      panel.classList.remove('show');\n      panel.innerHTML = '';\n      return;\n    }\n    var data = suitForZone(state.zone);\n    var decs = [2020, 2030, 2040, 2050, 2060];\n    var vals = [];\n    var hasAny = false;\n    for(var i=0; i<decs.length; i++){\n      var dd = decs[i];\n      var v = (data ? data[dd] : null) ? data[dd][cr] : null;\n      if(v === undefined) v = null;\n      if(v !== null) hasAny = true;\n      vals.push(v);\n    }\n    panel.classList.add('show');\n    if(!hasAny){\n      panel.innerHTML =\n        '<div class=\"acx-cropfocus-head\">' +\n          '<div class=\"acx-cropfocus-title\"><span class=\"acx-scope-dot focus\">Crop detail<\/span><span class=\"crop-name\">' + cr + '<\/span> <span style=\"color:var(--ink-dim);font-weight:400\">in<\/span> <span style=\"color:var(--orange);font-weight:700\">' + zinfo.country + ' \u2014 ' + zinfo.label + '<\/span><\/div>' +\n          '<button class=\"acx-cropfocus-clear\" type=\"button\" id=\"acx-cropfocus-clear\">Clear<\/button>' +\n        '<\/div>' +\n        '<div class=\"acx-cropfocus-empty\">This crop is not tracked in ' + zinfo.label + '. Switch to a zone where it is grown to see the trajectory.<\/div>';\n      bindClearBtn();\n      return;\n    }\n    var v2020 = vals[0], v2050 = vals[3], v2060 = vals[4];\n    var endpoint = (v2060 !== null) ? v2060 : v2050;\n    var deltaTot = (v2020 !== null ? endpoint !== null : false) ? (endpoint - v2020) : null;\n    var deltaCls = deltaTot === null ? 'muted' : (deltaTot >= 0 ? 'up' : 'down');\n    var deltaTxt = deltaTot === null ? '\u2014' : ((deltaTot >= 0 ? '+' : '') + deltaTot + ' pts');\n    \/\/ Sparkline SVG. Y axis is the suitability scale 0-100.\n    var W = 240, H = 48, padX = 6, padY = 6;\n    function xAt(idx){ return padX + idx * (W - 2*padX) \/ (decs.length - 1); }\n    function yAt(val){ return H - padY - (val \/ 100) * (H - 2*padY); }\n    var pathPts = [];\n    var fillPts = ['M' + padX + ',' + (H - padY)];\n    var firstSet = false;\n    for(var j=0; j<vals.length; j++){\n      if(vals[j] === null) continue;\n      var px = xAt(j), py = yAt(vals[j]);\n      pathPts.push((firstSet ? 'L' : 'M') + px.toFixed(1) + ',' + py.toFixed(1));\n      fillPts.push('L' + px.toFixed(1) + ',' + py.toFixed(1));\n      firstSet = true;\n    }\n    fillPts.push('L' + xAt(vals.length - 1).toFixed(1) + ',' + (H - padY) + 'Z');\n    var sparkHtml = '<svg class=\"traj-spark\" id=\"acx-cropfocus-spark\" viewBox=\"0 0 ' + W + ' ' + H + '\" preserveAspectRatio=\"none\">';\n    \/\/ Reference grid line at suitability=50\n    var midY = yAt(50);\n    sparkHtml += '<line class=\"spark-grid\" x1=\"' + padX + '\" y1=\"' + midY.toFixed(1) + '\" x2=\"' + (W-padX) + '\" y2=\"' + midY.toFixed(1) + '\"\/>';\n    sparkHtml += '<path class=\"spark-fill\" d=\"' + fillPts.join('') + '\"\/>';\n    sparkHtml += '<path class=\"spark-line\" d=\"' + pathPts.join('') + '\"\/>';\n    var curIdx = decs.indexOf(state.decade);\n    for(var k=0; k<vals.length; k++){\n      if(vals[k] === null) continue;\n      var dotCls = (k === curIdx) ? 'spark-dot now' : 'spark-dot';\n      var rr = (k === curIdx) ? 5 : 3.5;\n      sparkHtml += '<circle class=\"' + dotCls + '\" cx=\"' + xAt(k).toFixed(1) + '\" cy=\"' + yAt(vals[k]).toFixed(1) + '\" r=\"' + rr + '\"\/>';\n    }\n    \/\/ Hit rects per decade \u2014 provide a generous hover target for the\n    \/\/ rich tooltip on each decade column (rule \u00a717).\n    var colW = (W - 2*padX) \/ (decs.length - 1);\n    for(var hi=0; hi<decs.length; hi++){\n      if(vals[hi] === null) continue;\n      var rx = (xAt(hi) - colW\/2).toFixed(1);\n      sparkHtml += '<rect x=\"' + rx + '\" y=\"0\" width=\"' + colW.toFixed(1) + '\" height=\"' + H + '\" fill=\"transparent\" data-acx-tip=\"cropfocus\" data-dec=\"' + decs[hi] + '\" data-val=\"' + vals[hi] + '\" data-base=\"' + vals[0] + '\" data-crop=\"' + cr + '\" style=\"cursor:help\"\/>';\n    }\n    \/\/ Year labels at the corners\n    sparkHtml += '<text class=\"spark-yr\" x=\"' + (padX+1) + '\" y=\"' + (H-1) + '\">2020<\/text>';\n    sparkHtml += '<text class=\"spark-yr\" x=\"' + (W-padX-22) + '\" y=\"' + (H-1) + '\">2060<\/text>';\n    sparkHtml += '<\/svg>';\n    var commod = COMMODITY[cr];\n    var trade = TRADE[cr];\n    var scen = SCENARIOS[state.scenario];\n    var sideHtml = '';\n    if(commod){\n      var basePrice = commod.price * scen.priceMult;\n      var pricePct = Math.round((scen.priceMult - 1) * 100);\n      var priceShiftCls = pricePct >= 10 ? 'up' : (pricePct >= 4 ? 'warn' : 'muted');\n      var formatted = formatPrice(basePrice, cr);\n      sideHtml += '<div class=\"acx-cropfocus-row\"><span class=\"cf-h\">Price \u00b7 ' + scen.label.toLowerCase() + '<\/span><span class=\"cf-v\">' + formatted + ' <span class=\"' + (pricePct>=4?'up':'muted') + '\">' + (pricePct>=0?'+':'') + pricePct + '%<\/span><\/span><\/div>';\n      var vol = Math.round(commod.vol * scen.volMult * 100);\n      var volCls = vol >= 35 ? 'warn' : 'muted';\n      sideHtml += '<div class=\"acx-cropfocus-row\"><span class=\"cf-h\">Volatility<\/span><span class=\"cf-v ' + volCls + '\">' + vol + '% annualised<\/span><\/div>';\n    }\n    if(trade){\n      var conc = Math.round(trade.top5 * 100);\n      var concCls = conc >= 75 ? 'down' : (conc >= 60 ? 'warn' : 'up');\n      sideHtml += '<div class=\"acx-cropfocus-row\"><span class=\"cf-h\">Export concentration<\/span><span class=\"cf-v ' + concCls + '\">' + conc + '% top-5<\/span><\/div>';\n      sideHtml += '<div class=\"acx-cropfocus-row\"><span class=\"cf-h\">Trade chokepoint<\/span><span class=\"cf-v muted\">' + trade.chokepoint + '<\/span><\/div>';\n      if(trade.restricted){\n        sideHtml += '<div class=\"acx-cropfocus-row\"><span class=\"cf-h\">Export restriction<\/span><span class=\"cf-v warn\">active in last 3y<\/span><\/div>';\n      }\n    }\n    \/\/ Zone-level structural factors \u2014 applies to every crop in this zone.\n    var logi = logisticsForZone(state.zone);\n    var cost = inputCostForZone(state.zone);\n    if(logi !== null){\n      var lcls = logi >= 65 ? 'up' : (logi >= 50 ? 'muted' : 'down');\n      sideHtml += '<div class=\"acx-cropfocus-row\"><span class=\"cf-h\">Zone logistics<\/span><span class=\"cf-v ' + lcls + '\">' + logi + ' \/ 100 \u00b7 ' + tierLabel(logi, true) + '<\/span><\/div>';\n    }\n    if(cost !== null){\n      var ccls = cost <= 55 ? 'up' : (cost <= 70 ? 'muted' : 'warn');\n      sideHtml += '<div class=\"acx-cropfocus-row\"><span class=\"cf-h\">Input cost<\/span><span class=\"cf-v ' + ccls + '\">' + cost + ' \/ 100 \u00b7 ' + tierLabel(cost, false) + '<\/span><\/div>';\n    }\n    if(!sideHtml){\n      sideHtml = '<div class=\"acx-cropfocus-empty\">No commodity\/trade tracking for this crop yet.<\/div>';\n    }\n    panel.innerHTML =\n      '<div class=\"acx-cropfocus-head\">' +\n        '<div class=\"acx-cropfocus-title\"><span class=\"crop-name\">' + cr + '<\/span> \u00b7 <span style=\"color:var(--ink-dim);font-weight:500\">' + zinfo.country + ' \u2014 ' + zinfo.label + '<\/span><\/div>' +\n        '<button class=\"acx-cropfocus-clear\" type=\"button\" id=\"acx-cropfocus-clear\">Clear<\/button>' +\n      '<\/div>' +\n      '<div class=\"acx-cropfocus-grid\">' +\n        '<div class=\"acx-cropfocus-traj\">' +\n          '<div class=\"traj-h\">Crop fit \u00b7 2020 \u2192 2060<\/div>' +\n          '<div class=\"traj-vals\">' +\n            '<span class=\"from\">' + (v2020 !== null ? v2020 : '\u2014') + '<\/span>' +\n            '<span>\u2192<\/span>' +\n            '<span class=\"to\">' + (endpoint !== null ? endpoint : '\u2014') + '<\/span>' +\n            '<span class=\"delta ' + deltaCls + '\">' + deltaTxt + '<\/span>' +\n          '<\/div>' +\n          sparkHtml +\n        '<\/div>' +\n        '<div class=\"acx-cropfocus-side\">' + sideHtml + '<\/div>' +\n      '<\/div>';\n    bindClearBtn();\n  }\n  function bindClearBtn(){\n    var b = $('#acx-cropfocus-clear');\n    if(b) b.addEventListener('click', function(){ state.focusCrop = null; renderCropFocus(); renderHeatmap(); });\n    \/\/ Wire hover tooltips on the cropfocus sparkline hit rects (rule \u00a717)\n    var spark = $('#acx-cropfocus-spark');\n    if(spark){\n      spark.addEventListener('mousemove', function(ev){\n        var t = ev.target.closest ? ev.target.closest('[data-acx-tip=\"cropfocus\"]') : null;\n        if(!t){ hideChartTip(); return; }\n        var dec = +t.dataset.dec, val = +t.dataset.val, base = +t.dataset.base, cropName = t.dataset.crop;\n        var delta = val - base;\n        var deltaCls = delta > 0 ? 'up' : (delta < 0 ? 'down' : '');\n        var deltaTxt = dec === 2020 ? '2020 baseline' : (delta >= 0 ? '+' : '') + delta + ' pts vs 2020';\n        showChartTip(chartTipHTML(cropName + ' \u00b7 ' + dec, val + ' \/ 100 \u00b7 crop fit', deltaTxt, deltaCls), ev);\n      });\n      spark.addEventListener('mouseleave', hideChartTip);\n    }\n  }\n  function renderWLRow(x, isWin){\n    var scen = SCENARIOS[state.scenario];\n    var priceUsdT = x.commod ? (x.commod.price * scen.priceMult) : null;\n    var price    = priceUsdT === null ? null : formatPrice(priceUsdT, x.crop);\n    var vol      = x.commod ? Math.round(x.commod.vol * scen.volMult * 100) : null;\n    var riskCls = 'risk-lo';\n    if(x.trade){\n      var conc = x.trade.top5;\n      var restr = x.trade.restricted;\n      if(conc >= 0.75 ? true : restr){ riskCls = 'risk-hi'; }\n      else if(conc >= 0.6){ riskCls = 'risk-med'; }\n    }\n    var deltaTxt = (x.delta >= 0 ? '+' : '') + x.delta + ' pts';\n    \/\/ Suitability bar \u2014 gray \"base\" bar shows 2020 baseline, colored \"now\" bar\n    \/\/ overlays current decade. Width is % of 100-point scale.\n    var basePct = Math.max(0, Math.min(100, x.base));\n    var nowPct  = Math.max(0, Math.min(100, x.now));\n    var bar = '<div class=\"acx-wl-bar\" title=\"2020 ' + x.base + ' \u2192 ' + state.decade + ' ' + x.now + '\">' +\n              '<span class=\"bar-base\" style=\"width:' + basePct + '%\"><\/span>' +\n              '<span class=\"bar-now\"  style=\"width:' + nowPct + '%\"><\/span>' +\n              '<\/div>';\n    var meta = '';\n    meta += '<span class=\"score-range\">2020 <b>' + x.base + '<\/b> \u2192 ' + state.decade + ' <b>' + x.now + '<\/b><\/span>';\n    if(price !== null) meta += '<span class=\"chip\">' + price + '<\/span>';\n    if(vol !== null)   meta += '<span class=\"chip\">vol ' + vol + '%<\/span>';\n    if(x.trade) meta += '<span class=\"chip ' + riskCls + '\">' + Math.round(x.trade.top5 * 100) + '% top-5<\/span>';\n    return '<div class=\"acx-wl-row\">' +\n      '<div class=\"acx-wl-head\">' +\n        '<span class=\"acx-wl-name\">' + x.crop + '<\/span>' +\n        '<span class=\"acx-wl-delta\">' + deltaTxt + '<\/span>' +\n      '<\/div>' +\n      bar +\n      '<div class=\"acx-wl-meta\">' + meta + '<\/div>' +\n    '<\/div>';\n  }\n  function renderWinnersLosers(){\n    var data = suitForZone(state.zone);\n    var zinfoW = ZONES[state.zone];\n    var wlZ = $('#acx-wl-zone');\n    if(wlZ ? zinfoW : false) wlZ.textContent = zinfoW.country + ' \u2014 ' + zinfoW.label;\n    if(!data){\n      $('#acx-winners').innerHTML = '<div class=\"acx-wl-empty\">No regional data.<\/div>';\n      $('#acx-losers').innerHTML  = '<div class=\"acx-wl-empty\">No regional data.<\/div>';\n      return;\n    }\n    var base = data[2020] ? data[2020] : {};\n    var now  = data[state.decade] ? data[state.decade] : base;\n    var crops = {};\n    var k1 = Object.keys(base); for(var i=0;i<k1.length;i++) crops[k1[i]] = true;\n    var k2 = Object.keys(now);  for(var j=0;j<k2.length;j++) crops[k2[j]] = true;\n    var deltas = [];\n    var ckeys = Object.keys(crops);\n    for(var ci=0; ci<ckeys.length; ci++){\n      var cr = ckeys[ci];\n      var b = base[cr] !== undefined ? base[cr] : 0;\n      var n = now[cr] !== undefined ? now[cr] : 0;\n      var d = n - b;\n      deltas.push({ crop:cr, base:b, now:n, delta:d, trade:TRADE[cr], commod:COMMODITY[cr] });\n    }\n    deltas.sort(function(a, b){ return b.delta - a.delta; });\n    var winners = deltas.slice(0, 3).filter(function(x){ return x.delta > 0; });\n    var losers  = deltas.slice().reverse().slice(0, 3).filter(function(x){ return x.delta < 0; });\n    $('#acx-winners').innerHTML = winners.length ? winners.map(function(x){ return renderWLRow(x, true); }).join('') : '<div class=\"acx-wl-empty\">No clear gainers.<\/div>';\n    $('#acx-losers').innerHTML  = losers.length  ? losers.map(function(x){  return renderWLRow(x, false); }).join('') : '<div class=\"acx-wl-empty\">No clear losers.<\/div>';\n  }\n  \/\/ Zone-level access card \u2014 logistics + input cost. Surfaces a note when\n  \/\/ either score signals a structural margin headwind (long port haul,\n  \/\/ expensive inputs). Stable across decade\/scenario.\n  function renderZoneAccess(){\n    var el = $('#acx-zone-access');\n    if(!el) return;\n    var zinfoE = ZONES[state.zone];\n    var econZ = $('#acx-econ-zone');\n    if(econZ ? zinfoE : false) econZ.textContent = zinfoE.country + ' \u2014 ' + zinfoE.label;\n    var logi = logisticsForZone(state.zone);\n    var cost = inputCostForZone(state.zone);\n    var land = landPriceForZone(state.zone);\n    var gap  = yieldGapForZone(state.zone);\n    var inv  = investmentAccessForZone(state.zone);\n    var geo  = geopolRiskForZone(state.zone);\n    var op   = investmentOpportunityScore(state.zone);\n    if(logi === null ? (cost === null ? land === null : false) : false){\n      el.innerHTML = '<div class=\"acx-wl-empty\">No access data for this zone yet.<\/div>';\n      return;\n    }\n    \/\/ Helpers for tier classes\n    function clsLogi(v){ return v === null ? '' : (v >= 65 ? 'up' : (v >= 50 ? '' : (v >= 35 ? 'warn' : 'down'))); }\n    function clsCost(v){ return v === null ? '' : (v <= 50 ? 'up' : (v <= 65 ? '' : (v <= 80 ? 'warn' : 'down'))); }\n    function clsLand(v){ return v === null ? '' : (v <= 30 ? 'up' : (v <= 60 ? '' : 'warn')); }\n    function clsGap(v){  return v === null ? '' : (v >= 50 ? 'up' : (v >= 30 ? '' : 'warn')); }\n    function clsInv(v){  return v === null ? '' : (v >= 70 ? 'up' : (v >= 45 ? '' : 'down')); }\n    function clsGeo(v){  return v === null ? '' : (v >= 70 ? 'up' : (v >= 45 ? '' : 'down')); }\n    function landTier(v){ return v === null ? '\u2014' : (v <= 20 ? 'cheap' : (v <= 45 ? 'low' : (v <= 70 ? 'mid-market' : (v <= 85 ? 'premium' : 'top-of-market')))); }\n    function gapTier(v){  return v === null ? '\u2014' : (v >= 60 ? 'large' : (v >= 40 ? 'meaningful' : (v >= 20 ? 'modest' : 'mature'))); }\n    function invTier(v){  return v === null ? '\u2014' : (v >= 80 ? 'fully open' : (v >= 60 ? 'open with caveats' : (v >= 40 ? 'restricted' : (v >= 25 ? 'closed\/risky' : 'effectively closed')))); }\n    function geoTier(v){  return v === null ? '\u2014' : (v >= 85 ? 'very stable' : (v >= 70 ? 'stable' : (v >= 50 ? 'mixed' : (v >= 35 ? 'elevated risk' : 'high risk')))); }\n    var note = '';\n    if(op !== null ? op >= 70 : false){\n      note = '<div class=\"acx-za-note\"><strong>Top-tier opportunity (' + op + '\/100):<\/strong> climate + cheap dirt + yield gap + access all align here. The kind of zone land funds compete for early.<\/div>';\n    } else if(op !== null ? op >= 55 : false){\n      note = '<div class=\"acx-za-note\"><strong>Solid setup (' + op + '\/100):<\/strong> the composite is favourable. Verify the limiting factor before going further (usually logistics, access, or geopolitical exposure).<\/div>';\n    } else if(geo !== null ? geo <= 35 : false){\n      note = '<div class=\"acx-za-note warn\"><strong>\u26a0 Geopolitical overhead (' + geo + '\/100):<\/strong> sovereign-rating, sanctions, or active-conflict exposure adds insurance and discount-rate cost that the agronomy can\\'t offset. Investors typically demand a 200-400 bps risk premium here.<\/div>';\n    } else if(logi !== null ? logi <= 40 : false){\n      note = '<div class=\"acx-za-note warn\"><strong>Long-haul handicap:<\/strong> port distance + thin infrastructure typically add 8-15% to landed cost. Climate suitability alone overstates the opportunity.<\/div>';\n    } else if(inv !== null ? inv <= 35 : false){\n      note = '<div class=\"acx-za-note warn\"><strong>Capital can\\'t deploy easily:<\/strong> foreign-ownership restrictions, currency, or banking-depth issues make this zone hard to act on even if the agronomics look good.<\/div>';\n    } else if(cost !== null ? cost >= 78 : false){\n      note = '<div class=\"acx-za-note warn\"><strong>High input regime:<\/strong> fertilizer, water, or labor cost a large share of revenue. A strong crop-fit score doesn\\'t guarantee strong margin.<\/div>';\n    }\n    function row(headLabel, val, cls, tierStr, isCost){\n      var w = val === null ? 0 : val;\n      var displayed = val === null ? '\u2014' : (val + ' \/ 100');\n      return '<div class=\"acx-za-row\"><span class=\"acx-za-head\">' + headLabel + '<\/span>' +\n        '<span class=\"acx-za-val ' + cls + '\">' + displayed + ' <span class=\"tier\">' + tierStr + '<\/span><\/span>' +\n        '<div class=\"acx-za-bar' + (isCost ? ' cost' : '') + '\"><div class=\"fill\" style=\"width:' + w + '%\"><\/div><\/div>' +\n      '<\/div>';\n    }\n    var html = '';\n    if(op !== null){\n      var opCls = op >= 65 ? 'up' : (op >= 45 ? 'warn' : 'down');\n      html += '<div class=\"acx-za-row\" style=\"border-bottom:1px dashed var(--line2);padding-bottom:6px;margin-bottom:2px\">' +\n        '<span class=\"acx-za-head\" style=\"color:var(--accent-dark)\">Investment opportunity<\/span>' +\n        '<span class=\"acx-za-val ' + opCls + '\" style=\"font-size:14px\">' + op + ' \/ 100<\/span>' +\n        '<div class=\"acx-za-bar\"><div class=\"fill\" style=\"width:' + op + '%\"><\/div><\/div>' +\n      '<\/div>';\n    }\n    html += row('Logistics access', logi, clsLogi(logi), tierLabel(logi, true), false);\n    html += row('Input cost index', cost, clsCost(cost), tierLabel(cost, false), true);\n    html += row('Land price index', land, clsLand(land), landTier(land), true);\n    html += row('Yield gap',        gap,  clsGap(gap),   gapTier(gap),   false);\n    html += row('Investment access',inv,  clsInv(inv),   invTier(inv),   false);\n    html += row('Geopolitical risk',geo,  clsGeo(geo),   geoTier(geo),   false);\n    el.innerHTML = html + note;\n  }\n  function renderTradeRisk(){\n    var data = suitForZone(state.zone);\n    var trEl = $('#acx-trade');\n    var zinfoT = ZONES[state.zone];\n    var tradeZ = $('#acx-trade-zone');\n    if(tradeZ ? zinfoT : false) tradeZ.textContent = zinfoT.country + ' \u2014 ' + zinfoT.label;\n    if(!data){\n      trEl.innerHTML = '<div class=\"acx-wl-empty\">No regional data.<\/div>';\n      return;\n    }\n    var now = data[state.decade] ? data[state.decade] : data[2020];\n    var cropList = Object.keys(now).sort(function(a, b){ return (now[b] || 0) - (now[a] || 0); }).slice(0, 5);\n    var scen = SCENARIOS[state.scenario];\n    var html = '';\n    \/\/ Bar spans [-20%, +40%]; centre at 0. Each 1% = 1.667% of the bar width.\n    \/\/ Cap at \u00b120 so the most extreme shock still fits.\n    var maxAbs = 40;\n    function barWidthPct(shiftPct){\n      var capped = Math.max(-maxAbs\/2, Math.min(maxAbs, shiftPct));\n      return Math.min(50, Math.abs(capped) \/ maxAbs * 100);\n    }\n    for(var i=0; i<cropList.length; i++){\n      var cr = cropList[i];\n      var tr = TRADE[cr];\n      var co = COMMODITY[cr];\n      if(!tr ? true : !co){\n        html += '<div class=\"acx-trade-row\"><div class=\"acx-trade-label-grp\"><span class=\"crop\">' + cr + '<\/span><\/div><div><\/div><div class=\"acx-trade-vals\"><span class=\"px\">\u2014<\/span><\/div><\/div>';\n        continue;\n      }\n      var conc = tr.top5;\n      var priceShift = (scen.priceMult - 1) + (conc >= 0.75 ? scen.topConcPenalty : 0);\n      var pricePctTxt = (priceShift >= 0 ? '+' : '') + Math.round(priceShift * 100) + '%';\n      var cls = 'lo';\n      if(priceShift >= 0.10) cls = 'hi';\n      else if(priceShift >= 0.04) cls = 'med';\n      var w = barWidthPct(priceShift * 100).toFixed(1);\n      var fillDir = priceShift >= 0 ? 'pos' : 'neg';\n      var fillCls = 'bar-fill ' + fillDir + ' ' + cls;\n      html += '<div class=\"acx-trade-row\">' +\n        '<div class=\"acx-trade-label-grp\">' +\n          '<span class=\"crop\">' + cr + '<\/span>' +\n          '<span class=\"choke\">' + tr.chokepoint + '<\/span>' +\n        '<\/div>' +\n        '<div class=\"acx-trade-bar\" title=\"' + pricePctTxt + ' price shift under ' + scen.label + '\">' +\n          '<span class=\"bar-mid\"><\/span>' +\n          '<span class=\"' + fillCls + '\" style=\"width:' + w + '%\"><\/span>' +\n        '<\/div>' +\n        '<div class=\"acx-trade-vals\">' +\n          '<span class=\"px ' + cls + '\">' + pricePctTxt + '<\/span>' +\n          '<span class=\"conc\">' + Math.round(conc*100) + '% top-5' + (tr.restricted ? ' \u00b7 restricted' : '') + '<\/span>' +\n        '<\/div>' +\n      '<\/div>';\n    }\n    trEl.innerHTML = html;\n  }\n  function renderSummary(){\n    var zinfo = ZONES[state.zone];\n    var data = suitForZone(state.zone);\n    var climateTraj = climateForZone(state.zone);\n    if(!data ? true : !zinfo){\n      $('#acx-summary-text').innerHTML = 'No regional projection available for this zone yet.';\n      $('#acx-summary-scenario').innerHTML = '';\n      return;\n    }\n    var base = data[2020] ? data[2020] : {};\n    var nowDec = state.decade;\n    var nowS = data[nowDec] ? data[nowDec] : base;\n    var deltas = [];\n    var ck = Object.keys(base);\n    for(var i=0; i<ck.length; i++){\n      var cr = ck[i];\n      if(nowS[cr] === undefined) continue;\n      deltas.push({ crop:cr, d: nowS[cr] - base[cr] });\n    }\n    deltas.sort(function(a, b){ return b.d - a.d; });\n    var win = deltas.length ? deltas[0] : null;\n    var lose = deltas.length ? deltas[deltas.length - 1] : null;\n    var climateNow = climateTraj ? climateTraj[nowDec] : null;\n    var climateBase = climateTraj ? climateTraj[2020] : null;\n    var heatDelta = (climateNow ? climateBase : false) ? climateNow.heat - climateBase.heat : 0;\n    var precipDelta = (climateNow ? climateBase : false) ? (climateNow.precip - climateBase.precip) \/ climateBase.precip * 100 : 0;\n    var headline = '';\n    if(win ? lose : false){\n      var winDir  = win.d  >= 0 ? 'up' : 'down';\n      var loseDir = lose.d >= 0 ? 'up' : 'down';\n      headline = '<strong>' + zinfo.country + ' \u2014 ' + zinfo.label + '<\/strong> by <span class=\"num\">' + nowDec + '<\/span>: ' +\n        '<span class=\"num ' + winDir + '\">' + win.crop + ' ' + (win.d >= 0 ? '+' : '') + win.d + ' pts<\/span> crop fit, ' +\n        '<span class=\"num ' + loseDir + '\">' + lose.crop + ' ' + (lose.d >= 0 ? '+' : '') + lose.d + ' pts<\/span>. ' +\n        'Heat stress days <span class=\"num warn\">' + (heatDelta >= 0 ? '+' : '') + Math.round(heatDelta) + '<\/span>, rainfall <span class=\"num ' + (precipDelta <= -5 ? 'warn' : '') + '\">' + (precipDelta >= 0 ? '+' : '') + Math.round(precipDelta) + '%<\/span> vs 2020.';\n    }\n    $('#acx-summary-text').innerHTML = headline ? headline : '(no shift to report)';\n    var scen = SCENARIOS[state.scenario];\n    var scenText = '<strong>' + scen.label + ':<\/strong> ' + scen.note;\n    if(win ? state.scenario !== 'open' : false){\n      var commodWin = COMMODITY[win.crop];\n      if(commodWin){\n        var newPriceUsdT = commodWin.price * scen.priceMult;\n        var pct = Math.round((scen.priceMult - 1) * 100);\n        scenText += '<br><br>' + win.crop + ' price under this scenario: <span class=\"num\">' + formatPrice(newPriceUsdT, win.crop) + '<\/span> (' + (pct >= 0 ? '+' : '') + pct + '% vs baseline).';\n      }\n    }\n    $('#acx-summary-scenario').innerHTML = scenText;\n  }\n  function renderHero(){\n    var data = suitForZone(state.zone);\n    var climate = climateForZone(state.zone);\n    \/\/ Decade for the hero: if user is on 2020, look ahead to 2050; otherwise use selected.\n    var heroDec = state.decade;\n    var dwEl = $('#acx-hero-decade-w');\n    if(dwEl) dwEl.textContent = 'by ' + heroDec;\n    var winCard  = $('#acx-hero-win');\n    var loseCard = $('#acx-hero-lose');\n    var climCard = $('#acx-hero-clim');\n    if(!data ? true : !climate){\n      $('#acx-hero-win-crop').textContent  = 'No data';\n      $('#acx-hero-win-delta').textContent = '\u2014';\n      $('#acx-hero-lose-crop').textContent = 'No data';\n      $('#acx-hero-lose-delta').textContent= '\u2014';\n      $('#acx-hero-clim-delta').textContent= '\u2014';\n      return;\n    }\n    var base = data[2020] ? data[2020] : {};\n    var nowS = data[heroDec] ? data[heroDec] : base;\n    var deltas = [];\n    var ck = Object.keys(base);\n    for(var i=0; i<ck.length; i++){\n      var cr = ck[i];\n      if(nowS[cr] === undefined) continue;\n      deltas.push({ crop:cr, d: nowS[cr] - base[cr], now: nowS[cr] });\n    }\n    deltas.sort(function(a, b){ return b.d - a.d; });\n    var win  = deltas.length ? deltas[0] : null;\n    var lose = deltas.length ? deltas[deltas.length - 1] : null;\n    if(win ? win.d > 0 : false){\n      $('#acx-hero-win-crop').textContent = win.crop;\n      $('#acx-hero-win-delta').textContent = '+' + win.d + ' pts';\n      $('#acx-hero-win-sub').textContent = 'now ' + win.now + ' \/ 100 \u00b7 ' + (ZONES[state.zone] ? ZONES[state.zone].label : 'zone');\n      if(winCard) winCard.classList.remove('flat');\n    } else {\n      $('#acx-hero-win-crop').textContent = win ? win.crop : '\u2014';\n      $('#acx-hero-win-delta').textContent = win ? (win.d >= 0 ? '+' : '') + win.d + ' pts' : '\u2014';\n      $('#acx-hero-win-sub').textContent = 'no clear gainer at this horizon';\n      if(winCard) winCard.classList.add('flat');\n    }\n    if(lose ? lose.d < 0 : false){\n      $('#acx-hero-lose-crop').textContent = lose.crop;\n      $('#acx-hero-lose-delta').textContent = lose.d + ' pts';\n      $('#acx-hero-lose-sub').textContent = 'now ' + lose.now + ' \/ 100 \u00b7 ' + (ZONES[state.zone] ? ZONES[state.zone].label : 'zone');\n      if(loseCard) loseCard.classList.remove('flat');\n    } else {\n      $('#acx-hero-lose-crop').textContent = lose ? lose.crop : '\u2014';\n      $('#acx-hero-lose-delta').textContent = lose ? (lose.d >= 0 ? '+' : '') + lose.d + ' pts' : '\u2014';\n      $('#acx-hero-lose-sub').textContent = 'no clear loser at this horizon';\n      if(loseCard) loseCard.classList.add('flat');\n    }\n    \/\/ Climate signal: prefer heat-stress days delta if material, otherwise GDD delta.\n    var cBase = climate[2020];\n    var cNow  = climate[heroDec] ? climate[heroDec] : cBase;\n    var dHeat = cNow.heat - cBase.heat;\n    var dGdd  = cNow.gdd  - cBase.gdd;\n    var dPrec = cBase.precip ? (cNow.precip - cBase.precip) \/ cBase.precip * 100 : 0;\n    if(Math.abs(dHeat) >= 10){\n      $('#acx-hero-clim-label').textContent = 'heat stress days';\n      $('#acx-hero-clim-delta').textContent = (dHeat >= 0 ? '+' : '') + Math.round(dHeat) + ' d';\n      $('#acx-hero-clim-sub').textContent = 'days >35\u00b0C vs 2020';\n    } else if(dPrec <= -8){\n      $('#acx-hero-clim-label').textContent = 'rainfall';\n      $('#acx-hero-clim-delta').textContent = Math.round(dPrec) + '%';\n      $('#acx-hero-clim-sub').textContent = 'annual vs 2020';\n    } else {\n      $('#acx-hero-clim-label').textContent = 'growing-degree days';\n      $('#acx-hero-clim-delta').textContent = (dGdd >= 0 ? '+' : '') + Math.round(dGdd);\n      $('#acx-hero-clim-sub').textContent = 'GDD\u2081\u2080 vs 2020';\n    }\n  }\n  function renderAll(){\n    renderHero();\n    renderClimate();\n    renderZoneAccess();\n    renderHeatmap();\n    renderWinnersLosers();\n    renderTradeRisk();\n    renderSummary();\n    renderCropFocus();\n    recolorGlobeMap();\n  }\n  \/\/ \u2500\u2500\u2500 Filter-bar wiring \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  \/\/ Pills (Region \u00b7 Decade \u00b7 Scenario \u00b7 Units \u00b7 Currency) open popovers\n  \/\/ that capture selection and dispatch to state. Closing on any outside\n  \/\/ click, ESC, or selection.\n  function setDecade(d){\n    state.decade = +d;\n    hideTooltip();\n    refreshFilterPills();\n    renderAll();\n  }\n  function setScenario(s){\n    if(!SCENARIOS[s]) return;\n    state.scenario = s;\n    hideTooltip();\n    refreshFilterPills();\n    renderAll();\n  }\n  function setUnits(u){\n    if(u !== 'metric' ? u !== 'imperial' : false) return;\n    state.units = u;\n    try { localStorage.setItem('gpy-acx-units', u); } catch(_){}\n    refreshFilterPills();\n    renderAll();\n  }\n  function setCurrency(c){\n    if(!FX_RATE[c]) return;\n    state.currency = c;\n    try { localStorage.setItem('gpy-acx-currency', c); } catch(_){}\n    refreshFilterPills();\n    renderAll();\n  }\n  \/\/ Sync the pill VALUE text + the popup option highlights to current state.\n  function refreshFilterPills(){\n    var zinfo = ZONES[state.zone];\n    var rE = $('#acx-filter-region');    if(rE ? zinfo : false) rE.textContent = zinfo.country + ' \u2014 ' + zinfo.label;\n    var dE = $('#acx-filter-decade');    if(dE) dE.textContent = String(state.decade);\n    var sE = $('#acx-filter-scenario');  if(sE ? SCENARIOS[state.scenario] : false) sE.textContent = SCENARIOS[state.scenario].label;\n    var uE = $('#acx-filter-units');     if(uE) uE.textContent = state.units === 'imperial' ? 'imperial \u00b7 bu\/lb' : 'metric \u00b7 t';\n    var cE = $('#acx-filter-currency');  if(cE) cE.textContent = CURRENCY_SYM[state.currency] + ' ' + state.currency;\n    \/\/ Popup option highlights\n    var dOpts = root.querySelectorAll('#acx-popup-decade .acx-popup-opt');\n    for(var i=0; i<dOpts.length; i++) dOpts[i].setAttribute('aria-selected', +dOpts[i].dataset.d === state.decade ? 'true' : 'false');\n    var sOpts = root.querySelectorAll('#acx-popup-scenario .acx-popup-row');\n    for(var j=0; j<sOpts.length; j++) sOpts[j].setAttribute('aria-selected', sOpts[j].dataset.s === state.scenario ? 'true' : 'false');\n    var uOpts = root.querySelectorAll('#acx-popup-units .acx-popup-row');\n    for(var k=0; k<uOpts.length; k++) uOpts[k].setAttribute('aria-selected', uOpts[k].dataset.u === state.units ? 'true' : 'false');\n    var cOpts = root.querySelectorAll('#acx-popup-currency .acx-popup-row');\n    for(var n=0; n<cOpts.length; n++) cOpts[n].setAttribute('aria-selected', cOpts[n].dataset.c === state.currency ? 'true' : 'false');\n    \/\/ Region popup highlight\n    var rItems = root.querySelectorAll('#acx-popup-region-list .acx-popup-region-item');\n    for(var m=0; m<rItems.length; m++) rItems[m].classList.toggle('on', rItems[m].dataset.zid === state.zone);\n  }\n  \/\/ Build the region popup list (grouped by continent, country, zones)\n  function buildRegionPopup(){\n    var list = $('#acx-popup-region-list');\n    if(!list ? true : list.dataset.built === '1') return;\n    var groups = {};\n    var cks = Object.keys(COUNTRIES);\n    for(var i=0; i<cks.length; i++){\n      var cc = cks[i];\n      var info = COUNTRIES[cc];\n      var cont = info.continent || 'Other';\n      if(!groups[cont]) groups[cont] = [];\n      groups[cont].push(cc);\n    }\n    var html = '';\n    for(var ci=0; ci<CONTINENT_ORDER.length; ci++){\n      var cont2 = CONTINENT_ORDER[ci];\n      var members = groups[cont2];\n      if(!members ? true : !members.length) continue;\n      members.sort(function(a, b){ return COUNTRIES[a].name.localeCompare(COUNTRIES[b].name); });\n      html += '<div class=\"acx-popup-region-group\"><div class=\"acx-popup-region-group-h\">' + cont2 + '<\/div>';\n      for(var x=0; x<members.length; x++){\n        var ccx = members[x];\n        var c = COUNTRIES[ccx];\n        for(var z=0; z<c.zones.length; z++){\n          var zn = c.zones[z];\n          var label = c.name + (c.zones.length > 1 ? ' \u2014 ' + zn.label : '');\n          html += '<button class=\"acx-popup-region-item\" data-zid=\"' + zn.id + '\" data-search=\"' + (c.name + ' ' + zn.label).toLowerCase() + '\" role=\"option\">' +\n            '<span class=\"cc\">' + ccx + '<\/span>' + label +\n            '<\/button>';\n        }\n      }\n      html += '<\/div>';\n    }\n    list.innerHTML = html;\n    list.dataset.built = '1';\n  }\n  \/\/ Popover toggle wiring\n  var currentOpenPopup = null;\n  function closePopups(){\n    if(!currentOpenPopup) return;\n    currentOpenPopup.setAttribute('hidden', '');\n    var pill = root.querySelector('.acx-filter-pill[data-filter=\"' + currentOpenPopup.dataset.popup + '\"]');\n    if(pill) pill.setAttribute('aria-expanded', 'false');\n    currentOpenPopup = null;\n  }\n  function togglePopup(name, pillEl){\n    var popup = $('#acx-popup-' + name);\n    if(!popup) return;\n    if(currentOpenPopup === popup){ closePopups(); return; }\n    closePopups();\n    if(name === 'region') buildRegionPopup();\n    popup.removeAttribute('hidden');\n    if(pillEl) pillEl.setAttribute('aria-expanded', 'true');\n    currentOpenPopup = popup;\n    \/\/ Position popup under the pill\n    if(pillEl){\n      var pillRect = pillEl.getBoundingClientRect();\n      var barRect  = pillEl.parentElement.getBoundingClientRect();\n      var left = pillRect.left - barRect.left;\n      popup.style.left = left + 'px';\n    }\n    \/\/ Focus search if region\n    if(name === 'region'){\n      var s = $('#acx-popup-region-search');\n      if(s){ s.value = ''; s.focus(); list_filterRegions(''); }\n    }\n  }\n  function list_filterRegions(q){\n    var items = root.querySelectorAll('#acx-popup-region-list .acx-popup-region-item');\n    q = (q || '').toLowerCase().trim();\n    for(var i=0; i<items.length; i++){\n      var match = !q ? true : items[i].dataset.search.indexOf(q) >= 0;\n      items[i].style.display = match ? '' : 'none';\n    }\n    \/\/ Hide group headers whose children are all hidden\n    var groups = root.querySelectorAll('#acx-popup-region-list .acx-popup-region-group');\n    for(var g=0; g<groups.length; g++){\n      var any = groups[g].querySelectorAll('.acx-popup-region-item:not([style*=\"display: none\"])').length;\n      groups[g].style.display = any ? '' : 'none';\n    }\n  }\n  var pills = root.querySelectorAll('.acx-filter-pill');\n  for(var pi=0; pi<pills.length; pi++){\n    (function(pill){\n      pill.addEventListener('click', function(ev){\n        ev.stopPropagation();\n        togglePopup(pill.dataset.filter, pill);\n      });\n    })(pills[pi]);\n  }\n  \/\/ Click-outside-to-close\n  document.addEventListener('click', function(ev){\n    if(!currentOpenPopup) return;\n    if(currentOpenPopup.contains(ev.target)) return;\n    var anyPill = ev.target.closest ? ev.target.closest('.acx-filter-pill') : null;\n    if(anyPill) return;\n    closePopups();\n  });\n  \/\/ ESC closes\n  document.addEventListener('keydown', function(ev){\n    if(ev.key === 'Escape') closePopups();\n  });\n  \/\/ Wire selection inside each popup\n  var dPopOpts = root.querySelectorAll('#acx-popup-decade .acx-popup-opt');\n  for(var i=0; i<dPopOpts.length; i++){\n    (function(b){ b.addEventListener('click', function(){ setDecade(b.dataset.d); stopPlay(); closePopups(); }); })(dPopOpts[i]);\n  }\n  var sPopOpts = root.querySelectorAll('#acx-popup-scenario .acx-popup-row');\n  for(var j=0; j<sPopOpts.length; j++){\n    (function(b){ b.addEventListener('click', function(){ setScenario(b.dataset.s); closePopups(); }); })(sPopOpts[j]);\n  }\n  var uPopOpts = root.querySelectorAll('#acx-popup-units .acx-popup-row');\n  for(var k=0; k<uPopOpts.length; k++){\n    (function(b){ b.addEventListener('click', function(){ setUnits(b.dataset.u); closePopups(); }); })(uPopOpts[k]);\n  }\n  var cPopOpts = root.querySelectorAll('#acx-popup-currency .acx-popup-row');\n  for(var l=0; l<cPopOpts.length; l++){\n    (function(b){ b.addEventListener('click', function(){ setCurrency(b.dataset.c); closePopups(); }); })(cPopOpts[l]);\n  }\n  \/\/ Region popup \u2014 click on item + search filtering\n  var regionList = $('#acx-popup-region-list');\n  if(regionList){\n    regionList.addEventListener('click', function(ev){\n      var btn = ev.target.closest ? ev.target.closest('.acx-popup-region-item') : null;\n      if(btn ? btn.dataset.zid : false){ selectZone(btn.dataset.zid); flyToZone(btn.dataset.zid); closePopups(); }\n    });\n  }\n  var regionSearch = $('#acx-popup-region-search');\n  if(regionSearch){\n    regionSearch.addEventListener('input', function(){ list_filterRegions(regionSearch.value); });\n  }\n  \/\/ Play \/ pause control\n  var playBtn = $('#acx-play');\n  function stopPlay(){\n    if(playTimer){ clearInterval(playTimer); playTimer = null; }\n    if(playBtn){ playBtn.classList.remove('playing'); playBtn.textContent = '\u25b6'; }\n  }\n  function startPlay(){\n    if(playTimer) return;\n    if(playBtn){ playBtn.classList.add('playing'); playBtn.textContent = '\u275a\u275a'; }\n    playTimer = setInterval(function(){\n      var decs = [2030, 2040, 2050, 2060];\n      var idx = decs.indexOf(state.decade);\n      var next = decs[(idx + 1) % decs.length];\n      setDecade(next);\n    }, 1400);\n  }\n  if(playBtn){\n    playBtn.addEventListener('click', function(){\n      if(playTimer) stopPlay(); else startPlay();\n    });\n  }\n  \/\/ Metric selector \u2014 Crop shift \/ Climate \/ Trade risk\n  var mapMetricBtns = root.querySelectorAll('#acx-map-metric button');\n  for(var mi=0; mi<mapMetricBtns.length; mi++){\n    (function(btn){\n      btn.addEventListener('click', function(){\n        state.mapMetric = btn.dataset.m;\n        for(var i=0; i<mapMetricBtns.length; i++){\n          var on = mapMetricBtns[i].dataset.m === state.mapMetric;\n          mapMetricBtns[i].classList.toggle('on', on);\n          mapMetricBtns[i].setAttribute('aria-selected', on ? 'true' : 'false');\n        }\n        hideTooltip();\n        recolorGlobeMap();\n      });\n    })(mapMetricBtns[mi]);\n  }\n  \/\/ Fit-world reset button\n  var resetBtn = $('#acx-map-reset');\n  if(resetBtn){\n    resetBtn.addEventListener('click', function(){\n      resetMap();\n    });\n  }\n  \/\/ Click a row in the suitability heatmap to focus that crop. Click the\n  \/\/ same crop again to clear; the Clear button in the panel does the same.\n  var heatTbl = $('#acx-heat-tbl');\n  if(heatTbl){\n    heatTbl.addEventListener('click', function(ev){\n      var tr = ev.target.closest ? ev.target.closest('tr[data-crop]') : null;\n      if(!tr) return;\n      var cr = tr.dataset.crop;\n      if(!cr) return;\n      state.focusCrop = (state.focusCrop === cr) ? null : cr;\n      renderHeatmap();\n      renderCropFocus();\n    });\n    \/\/ Rich hover tooltip on every cell \u2014 per rule \u00a717.\n    heatTbl.addEventListener('mousemove', function(ev){\n      var td = ev.target.closest ? ev.target.closest('td[data-acx-tip]') : null;\n      if(!td){ hideChartTip(); return; }\n      var cr = td.dataset.crop, dd = +td.dataset.dec;\n      if(td.dataset.empty === '1'){\n        showChartTip('<div class=\"tip-h\">' + cr + ' \u00b7 ' + dd + '<\/div><div class=\"tip-v\">Not tracked here<\/div>', ev);\n        return;\n      }\n      var v = +td.dataset.val;\n      var base = td.dataset.base !== undefined ? +td.dataset.base : undefined;\n      showChartTip(heatTipHTML(cr, dd, v, base), ev);\n    });\n    heatTbl.addEventListener('mouseleave', hideChartTip);\n  }\n  \/\/ PDF export \u2014 populates the print-only header with the active state so\n  \/\/ the saved PDF carries the right zone\/decade\/scenario, then invokes\n  \/\/ window.print(). Works in every browser without external libs.\n  var printBtn = $('#acx-print-btn');\n  if(printBtn){\n    printBtn.addEventListener('click', function(){\n      var zinfo = ZONES[state.zone];\n      var zEl = $('#acx-print-zone');\n      var dEl = $('#acx-print-decade');\n      var sEl = $('#acx-print-scen');\n      var scen = SCENARIOS[state.scenario];\n      if(zEl ? zinfo : false) zEl.textContent = zinfo.country + ' \u2014 ' + zinfo.label;\n      if(dEl) dEl.textContent = String(state.decade);\n      if(sEl ? scen : false) sEl.textContent = scen.label;\n      window.print();\n    });\n  }\n  \/\/ Kick off Leaflet load on first paint. Async \u2014 when ready, init the map +\n  \/\/ markers and run a first recolor. Until then the container shows\n  \/\/ \"Loading map\u2026\". If the CDN fails, an inline error message replaces it.\n  loadLeaflet(initLeafletMap);\n  refreshFilterPills();\n  renderAll();\n})();\n<\/script>\n<\/div>\n\n\n\n<!--\n  Tool-specific bottom (CTA + FAQ) for \/agroclimate-explorer\/.\n  Self-contained CSS so it renders correctly WITHOUT depending on block\n  13099's .gpx-* styles (which only ship with the field-explorer hero).\n\n  Replaces the shared Reusable Block 13099 reference on page 13341.\n  Pasted INLINE as the last Custom HTML block.\n-->\n<div class=\"gpl-bottom\">\n<style>\n.gpl-bottom{font-family:var(--wp--preset--font-family--nunito,\"Nunito\",system-ui,-apple-system,Segoe UI,sans-serif);color:#212121;max-width:1700px;margin:0 auto;padding:0 8px}\n.gpl-bottom h1,.gpl-bottom h2,.gpl-bottom h3{font-family:var(--wp--preset--font-family--poppins,\"Poppins\",system-ui,-apple-system,sans-serif);font-weight:700;color:#145328;line-height:1.2;letter-spacing:-0.01em}\n.gpl-bottom h2{font-size:1.55rem;margin:28px 0 14px}\n.gpl-bottom h3{font-size:1.1rem;margin:0 0 10px;color:#145328}\n.gpl-bottom p{line-height:1.65;color:#2c3e2c;margin:0 0 14px}\n.gpl-bottom a{color:#15701e;text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:2px}\n.gpl-bottom a:hover{color:#145328}\n.gpl-bottom strong{color:#145328}\n.gpl-bottom .gpb-cta{background:linear-gradient(135deg,#145328 0%,#1b7a2a 65%,#1a7951 100%);border-radius:18px;padding:38px 40px;margin:36px 0 24px;color:#fafbf4;text-align:center;position:relative;overflow:hidden}\n.gpl-bottom .gpb-cta::before{content:\"\";position:absolute;inset:auto -40px -60px auto;width:200px;height:200px;background:radial-gradient(circle,rgba(247,106,12,0.28),transparent 70%);pointer-events:none}\n.gpl-bottom .gpb-cta h2{color:#fff;margin:0 0 12px;font-size:1.6rem}\n.gpl-bottom .gpb-cta p{color:rgba(255,255,255,0.95);font-size:1.05rem;max-width:780px;margin:0 auto 22px}\n.gpl-bottom .gpb-btn{display:inline-block;background:#f76a0c;color:#fff;font-family:var(--wp--preset--font-family--poppins,\"Poppins\",sans-serif);font-weight:600;font-size:1rem;padding:14px 30px;border-radius:999px;text-decoration:none;transition:transform .15s,box-shadow .15s,background .15s;box-shadow:0 6px 18px rgba(247,106,12,0.4)}\n.gpl-bottom .gpb-btn:hover{transform:translateY(-1px);background:#ff7d24;color:#fff;box-shadow:0 8px 24px rgba(247,106,12,0.5)}\n.gpl-bottom .gpb-integ{display:flex;flex-wrap:wrap;gap:8px;justify-content:center;margin:10px 0 20px}\n.gpl-bottom .gpb-integ-label{font-size:.86rem;color:rgba(255,255,255,0.86);margin:0 0 8px;font-weight:500;letter-spacing:.01em}\n.gpl-bottom .gpb-integ-chip{background:rgba(255,255,255,0.16);border:1px solid rgba(255,255,255,0.32);color:#fff;padding:6px 14px;border-radius:999px;font-size:.85rem;font-weight:600;letter-spacing:.02em}\n.gpl-bottom .gpb-faq{margin:36px 0 8px}\n.gpl-bottom .gpb-faq h2{text-align:center;margin:0 0 20px;font-size:1.6rem}\n.gpl-bottom .gpb-tabs{border:1px solid #e8ebe2;border-radius:16px;overflow:hidden;background:#fff;box-shadow:0 2px 14px rgba(20,83,40,0.06)}\n.gpl-bottom .gpb-tablist{display:flex;gap:0;background:#f6f7f1;border-bottom:1px solid #e8ebe2;overflow-x:auto;scrollbar-width:none}\n.gpl-bottom .gpb-tablist::-webkit-scrollbar{display:none}\n.gpl-bottom .gpb-tab{flex:1 1 auto;min-width:max-content;background:transparent;border:0;padding:16px 22px;font-family:var(--wp--preset--font-family--poppins,\"Poppins\",sans-serif);font-size:.95rem;font-weight:600;color:#4c6066;cursor:pointer;border-bottom:3px solid transparent;transition:color .15s,border-color .15s,background .15s;white-space:nowrap;letter-spacing:.01em}\n.gpl-bottom .gpb-tab:hover{color:#145328;background:rgba(123,220,181,0.14)}\n.gpl-bottom .gpb-tab[aria-selected=\"true\"]{color:#145328;border-bottom-color:#15701e;background:#fff}\n.gpl-bottom .gpb-tab:focus{outline:2px solid #15701e;outline-offset:-2px}\n.gpl-bottom .gpb-panel{padding:28px 32px;display:none;animation:gpbfade .25s ease}\n.gpl-bottom .gpb-panel.is-active{display:block}\n@keyframes gpbfade{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}\n.gpl-bottom .gpb-panel p{margin:0;line-height:1.7;color:#243024;font-size:1rem}\n.gpl-bottom .gpb-panel p+p{margin-top:10px}\n.gpl-bottom .gpb-panel kbd,.gpl-bottom .gpb-panel code{background:#f0f3ec;padding:2px 6px;border-radius:4px;font-family:var(--wp--preset--font-family--dm-mono,\"DM Mono\",ui-monospace,monospace);font-size:.88em;color:#145328}\n@media (max-width:760px){\n  .gpl-bottom h2{font-size:1.3rem}\n  .gpl-bottom .gpb-cta{padding:24px 22px}\n  .gpl-bottom .gpb-panel{padding:22px 20px}\n  .gpl-bottom .gpb-tab{padding:14px 16px;font-size:.9rem}\n}\n<\/style>\n<section class=\"gpb-cta\">\n  <h2>Country-zone resolution is the start. GeoPard runs this at the field.<\/h2>\n  <p>This page works at the agroclimatic-zone level. The full GeoPard platform combines your actual yield, soil tests, NDVI, and real elevation into per-field management zones, then generates variable-rate prescriptions that respect the local climate trajectory and rotation logic.<\/p>\n  <div class=\"gpb-integ-label\">Push prescriptions and pull as-applied data with:<\/div>\n  <div class=\"gpb-integ\">\n    <span class=\"gpb-integ-chip\">John Deere Operations Center<\/span>\n    <span class=\"gpb-integ-chip\">CNH FieldOps<\/span>\n    <span class=\"gpb-integ-chip\">AGCO \/ PTx FarmEngage<\/span>\n  <\/div>\n  <a class=\"gpb-btn\" href=\"https:\/\/app.geopard.tech\/sign-up?utm_source=geopard.tech&amp;utm_medium=lead-magnet&amp;utm_campaign=agroclimate-explorer&amp;utm_content=below-tool-cta\">Register free in GeoPard &rarr;<\/a>\n<\/section>\n\n<section class=\"gpb-faq\" aria-labelledby=\"gpb-faq-h\">\n  <h2 id=\"gpb-faq-h\">Frequently asked questions<\/h2>\n  <div class=\"gpb-tabs\">\n    <div class=\"gpb-tablist\" role=\"tablist\" aria-label=\"Agroclimate Explorer FAQ\">\n      <button class=\"gpb-tab\" role=\"tab\" id=\"gpb-tab-1\" aria-controls=\"gpb-panel-1\" aria-selected=\"true\" tabindex=\"0\">Zones<\/button>\n      <button class=\"gpb-tab\" role=\"tab\" id=\"gpb-tab-2\" aria-controls=\"gpb-panel-2\" aria-selected=\"false\" tabindex=\"-1\">Projections<\/button>\n      <button class=\"gpb-tab\" role=\"tab\" id=\"gpb-tab-3\" aria-controls=\"gpb-panel-3\" aria-selected=\"false\" tabindex=\"-1\">Suitability<\/button>\n      <button class=\"gpb-tab\" role=\"tab\" id=\"gpb-tab-4\" aria-controls=\"gpb-panel-4\" aria-selected=\"false\" tabindex=\"-1\">Trade scenarios<\/button>\n      <button class=\"gpb-tab\" role=\"tab\" id=\"gpb-tab-7\" aria-controls=\"gpb-panel-7\" aria-selected=\"false\" tabindex=\"-1\">Costs &amp; access<\/button>\n      <button class=\"gpb-tab\" role=\"tab\" id=\"gpb-tab-5\" aria-controls=\"gpb-panel-5\" aria-selected=\"false\" tabindex=\"-1\">Limitations<\/button>\n      <button class=\"gpb-tab\" role=\"tab\" id=\"gpb-tab-6\" aria-controls=\"gpb-panel-6\" aria-selected=\"false\" tabindex=\"-1\">Who uses it<\/button>\n    <\/div>\n    <div class=\"gpb-panel is-active\" role=\"tabpanel\" id=\"gpb-panel-1\" aria-labelledby=\"gpb-tab-1\">\n      <h3>Why split big countries into multiple agroclimatic zones?<\/h3>\n      <p>Big countries span fundamentally different climates. The US Corn Belt (Iowa, Illinois) and the US Cotton Belt (Mississippi, Alabama) sit in different agroclimatic conditions: different soils, growing seasons, dominant crops, and trade flows. A single national average hides the signal entirely.<\/p>\n      <p>The tool currently splits 36 countries into <strong>62 agroclimatic zones<\/strong>. The US has nine (Corn Belt, Cotton Belt, Mississippi Delta, Pacific Northwest, California Central, Northern Plains, Southern Plains, Lake States, Northeast). Ukraine has six (Polissia, Forest-Steppe, Steppe, Black Sea Coast, Carpathian, Podillia). Africa is covered through C\u00f4te d&#8217;Ivoire cocoa belt, Ethiopian + Kenyan highlands, Nigerian savanna, Egyptian Nile Delta, Moroccan Atlas, Zambian commercial belt, the South African Highveld and Western Cape. Asia-Pacific now covers India, China, Bangladesh, Pakistan, Vietnam, Indonesia, Thailand, Turkey, and Israel. Latin America adds Argentina, Chile, Colombia, and Mexico. Brazil, India, China, Australia, and Canada all carry multiple zones. Each map marker carries a dataset that matches what a farmer or planner in that specific zone actually faces.<\/p>\n    <\/div>\n    <div class=\"gpb-panel\" role=\"tabpanel\" id=\"gpb-panel-2\" aria-labelledby=\"gpb-tab-2\" hidden>\n      <h3>What data drives the projections?<\/h3>\n      <p>Decade snapshots are aggregated from <strong>leading climate-model ensembles<\/strong> on a moderate-emissions trajectory, decadal averages 2020-2060. Crop suitability comes from a <strong>recognized global crop-suitability framework<\/strong>, shifted by the projected climate. Commodity prices use <strong>authoritative global commodity-price indices<\/strong> plus 10-year annualized volatility. Trade-flow concentration uses <strong>international trade-flow statistics<\/strong> for the top-5 exporter share.<\/p>\n      <p>What this tool gives you is <strong>direction and magnitude<\/strong>: where the climate is heading, which crops follow that direction, which trade flows get squeezed. It is not a forecast. It is a planning framework for the next 5-30 years.<\/p>\n    <\/div>\n    <div class=\"gpb-panel\" role=\"tabpanel\" id=\"gpb-panel-3\" aria-labelledby=\"gpb-tab-3\" hidden>\n      <h3>How are suitability scores calculated?<\/h3>\n      <p>Each zone \u00d7 decade \u00d7 crop combination gets a <strong>0-100 score<\/strong>:<\/p>\n      <p><code>80-100<\/code> \u2014 ideal climate conditions, high yield potential.<br><code>60-80<\/code> \u2014 good fit, normal yields with standard management.<br><code>40-60<\/code> \u2014 fair, marginal yields, heat or drought stress reduces upside.<br><code>20-40<\/code> \u2014 poor, the crop survives but isn&#8217;t competitive against alternatives.<br><code>0-20<\/code> \u2014 unsuitable, the climate envelope no longer supports the crop.<\/p>\n      <p>Scores shift across decades as growing-degree days, heat-stress days, season length, and rainfall change. Use the heatmap to spot the trajectory: green tiles drift down (crop becoming less competitive) or up (crop expanding into a new zone). The winners\/losers panel surfaces the biggest moves.<\/p>\n    <\/div>\n    <div class=\"gpb-panel\" role=\"tabpanel\" id=\"gpb-panel-4\" aria-labelledby=\"gpb-tab-4\" hidden>\n      <h3>What do the three trade scenarios model?<\/h3>\n      <p><strong>Open trade<\/strong> is the baseline \u2014 free-flowing exports, volatility at 10-year norms, no shocks.<\/p>\n      <p><strong>Bloc fragmentation<\/strong> models tariff walls between trade blocs. Importers pay 6-10 % more, exporters lose 10-15 % margin from lost market access. Volatility rises ~30 %. Input costs (fertilizer, fuel) creep up.<\/p>\n      <p><strong>Export ban shock<\/strong> models a major exporter going offline (2022-style Black Sea closure). Prices spike most on high-concentration crops where the top-5 exporters control over 75 % of trade \u2014 sunflower, wheat, sorghum. Fertilizer N spikes 25-40 %, so <strong>net margin can fall even as crop prices rise<\/strong> in importing countries.<\/p>\n      <p>The magnitudes are conservative midpoints from past disruption events. They show you the shape of the risk, not the exact path.<\/p>\n    <\/div>\n    <div class=\"gpb-panel\" role=\"tabpanel\" id=\"gpb-panel-7\" aria-labelledby=\"gpb-tab-7\" hidden>\n      <h3>Costs, access, and the investment composite<\/h3>\n      <p>Climate suitability is necessary but not sufficient. The tool surfaces six structural zone-level scores so the &#8220;where do I plant \u2014 or buy land?&#8221; question answers honestly.<\/p>\n      <p><strong>Logistics access (0-100).<\/strong> Distance to ports, rail density, processing infrastructure. Brazilian Cerrado scores low because it&#8217;s 1500-2000 km of poor roads to Santos. Ukrainian Black Sea Coast scores high \u2014 Odesa-Mykolaiv-Kherson ports sit at the field edge. A low logistics score typically adds 8-15% to landed cost vs port-adjacent zones.<\/p>\n      <p><strong>Input cost index (0-100, higher = more expensive).<\/strong> Fertilizer subsidies, fuel, seed cost, labor, water. India scores low (subsidies). EU zones score high (regulation + N price). California scores very high (water + labor).<\/p>\n      <p><strong>Land price index (0-100, normalized).<\/strong> Iowa Cornbelt and California Central Valley anchor the top (premium prices). Kazakhstan, Ukrainian Polissia, Ethiopia Highlands anchor the bottom (cheap dirt). For land funds, low score = where capital deploys at a discount.<\/p>\n      <p><strong>Yield gap (0-100).<\/strong> Gap between current yields and biophysical potential. US Cornbelt: ~10 \u2014 mature, near ceiling, little room for capital to lift. Nigeria savanna: 80 \u2014 huge unrealized lift if you bring inputs + mechanisation. Ethiopia Highlands: 75. The &#8220;uptake&#8221; axis \u2014 large gap means the agronomic envelope hasn&#8217;t been monetized yet.<\/p>\n      <p><strong>Investment access (0-100).<\/strong> Foreign-ownership rules + currency stability + banking depth + title clarity. US\/CA\/AU\/NZ: 90+ (open, stable). EU: 75-90. Brazil: ~60. Ukraine: ~25 (war-suppressed). China + India: ~25-30 (foreign land ownership restricted). Tells you whether outside capital <em>can<\/em> deploy in this zone \u2014 a rules-based read.<\/p>\n      <p><strong>Geopolitical risk (0-100, higher = lower risk).<\/strong> Event-based exposure: sovereign credit standing (40%) + political stability (30%) + sanctions \/ proxy-sanctions distance (15%) + expropriation and property-rights risk (15%). Distinct from access: a country can be rules-open yet event-risky (Israel ~60 \u2014 high rule of law overlaid on active conflict) or rules-closed yet relatively stable (China ~32 \u2014 restricted to foreigners but no open war). Anchors: US\/CA\/AU\/NZ 90+ \u00b7 EU core 80-90 \u00b7 Chile 78 \u00b7 Brazil 65 \u00b7 Israel 60 \u00b7 India 60 \u00b7 Argentina 35 \u00b7 China 32 \u00b7 Ethiopia 32 \u00b7 Pakistan 28 \u00b7 Ukraine 18-30 (varies by oblast).<\/p>\n      <p><strong>Investment composite (0-100)<\/strong> blends climate trajectory + cheap dirt + yield gap + logistics + access + geopolitical risk. Switch the map to the <em>Investment<\/em> metric to see the world coloured by this single score \u2014 top zones light up deep green, mature markets stay yellow, closed\/war-affected zones stay grey. The strip under the map ranks the top five. A zone with strong climate + cheap dirt but weak geopol typically lands in the 40-55 band: the agronomy looks good on paper, the discount rate eats it.<\/p>\n    <\/div>\n    <div class=\"gpb-panel\" role=\"tabpanel\" id=\"gpb-panel-5\" aria-labelledby=\"gpb-tab-5\" hidden>\n      <h3>What this tool doesn&#8217;t model<\/h3>\n      <p><strong>Field-level variability.<\/strong> This is country-zone resolution. Soil texture, slope, drainage, microclimate, and local water-table effects can move suitability \u00b115-20 pts within a single zone. For field-level trajectories, register a GeoPard account and pull the same logic against your real boundaries and yield history.<\/p>\n      <p><strong>Breeding and varietal innovation.<\/strong> Suitability assumes today&#8217;s commercial cultivars. Heat-tolerant wheat, drought-tolerant maize, late-budding wine grape selections \u2014 none of these are factored in. Expect the actual envelope for any crop to widen 5-10 pts compared to what&#8217;s shown here as breeding catches up.<\/p>\n      <p><strong>Irrigation and infrastructure.<\/strong> Suitability is calibrated for the dominant water regime in each zone (mostly rainfed). California Central Valley and Murray-Darling Basin scores assume current irrigation access; if water allocations tighten, the real picture is worse than shown.<\/p>\n      <p><strong>Policy and subsidies.<\/strong> Trade scenarios capture price-shock magnitudes but not government responses \u2014 emergency subsidies, export quotas, strategic stockpiling, tariff retaliation. Real shocks usually trigger policy interventions that smooth the worst spikes.<\/p>\n      <p><strong>Yield vs suitability.<\/strong> Suitability scores reflect climate fit, not absolute yield. Two zones at suitability 70 can produce very different t\/ha depending on management intensity, input costs, and operator skill. Treat the scores as <strong>directional<\/strong>, not bookable.<\/p>\n    <\/div>\n    <div class=\"gpb-panel\" role=\"tabpanel\" id=\"gpb-panel-6\" aria-labelledby=\"gpb-tab-6\" hidden>\n      <h3>Who is this built for?<\/h3>\n      <p><strong>Agronomists and crop consultants<\/strong> building 5-10 year rotation plans for their clients.<\/p>\n      <p><strong>Procurement and risk teams<\/strong> at ag retailers, traders, and processors who need a fast read on where exposure is shifting.<\/p>\n      <p><strong>Cooperative directors<\/strong> planning storage, contract mix, and capital projects against changing crop mixes.<\/p>\n      <p><strong>Land funds and ag-investment groups<\/strong> scouting where dirt is cheap, yield gaps are large, climate is improving, and foreign capital can actually deploy. The Investment map metric is designed for them \u2014 switch the map and look for deep-green dots, ignore grey ones.<\/p>\n      <p><strong>Investors and analysts<\/strong> modeling acreage shifts, asset relocation, and supply-chain resilience.<\/p>\n      <p><strong>Universities and extension services<\/strong> using a clean visual to teach agroclimate dynamics and trade-shock pricing.<\/p>\n      <p>For field-level planning grounded in your actual farm data, that&#8217;s the paid GeoPard platform.<\/p>\n    <\/div>\n  <\/div>\n<\/section>\n<\/div>\n<script nowprocket data-no-optimize=\"1\" data-no-defer=\"1\" data-no-minify=\"1\">\n(function(){\n  var root = document.querySelector('.gpl-bottom .gpb-faq');\n  if(!root) return;\n  var tabs = root.querySelectorAll('.gpb-tab');\n  var panels = root.querySelectorAll('.gpb-panel');\n  function activate(idx){\n    tabs.forEach(function(t,i){\n      var on = i===idx;\n      t.setAttribute('aria-selected', on ? 'true' : 'false');\n      t.setAttribute('tabindex', on ? '0' : '-1');\n    });\n    panels.forEach(function(p,i){\n      var on = i===idx;\n      p.classList.toggle('is-active', on);\n      if(on){ p.removeAttribute('hidden'); } else { p.setAttribute('hidden',''); }\n    });\n  }\n  tabs.forEach(function(t,i){\n    t.addEventListener('click', function(){ activate(i); });\n    t.addEventListener('keydown', function(e){\n      var n=tabs.length, ni=i;\n      if(e.key==='ArrowRight'){ni=(i+1)%n;}\n      else if(e.key==='ArrowLeft'){ni=(i-1+n)%n;}\n      else if(e.key==='Home'){ni=0;}\n      else if(e.key==='End'){ni=n-1;}\n      else { return; }\n      e.preventDefault();\n      activate(ni);\n      tabs[ni].focus();\n    });\n  });\n})();\n<\/script>\n\n","protected":false},"excerpt":{"rendered":"<p>Explorador del Futuro Agroclim\u00e1tico \u00b7 Gratis \u00b7 Sin registro \u00bfHacia d\u00f3nde se dirigen sus tierras de cultivo en 2030, 2050 y 2060? Elija un pa\u00eds y una zona agroclim\u00e1tica. Deslice\u2026<\/p>","protected":false},"author":210157960,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_coblocks_attr":"","_coblocks_dimensions":"","_coblocks_responsive_height":"","_coblocks_accordion_ie_support":"","_eb_attr":"","content-type":"","footnotes":"","big_sky_generated":false},"class_list":["post-13341","page","type-page","status-publish","hentry"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.7 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Agroclimate Future Explorer - GeoPard Agriculture<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/geopard.tech\/es\/explorador-agroclimatico\/\" \/>\n<meta property=\"og:locale\" content=\"es_ES\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Agroclimate Future Explorer - GeoPard Agriculture\" \/>\n<meta property=\"og:description\" content=\"Agroclimate Future Explorer \u00b7 Free \u00b7 No signup Where your farmland is going \u2014 by 2030, 2050, 2060. Pick a country and agroclimatic zone. Scrub...\" \/>\n<meta property=\"og:url\" content=\"https:\/\/geopard.tech\/es\/explorador-agroclimatico\/\" \/>\n<meta property=\"og:site_name\" content=\"GeoPard - Precision agriculture Mapping software\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/geopardAgriculture\/\" \/>\n<meta property=\"article:modified_time\" content=\"2026-06-01T17:39:18+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/i0.wp.com\/geopard.tech\/wp-content\/uploads\/2026\/03\/GeoPard-Background-Precision-Ag-software-Do-more-with-your-data.png?fit=3116%2C1754&ssl=1\" \/>\n\t<meta property=\"og:image:width\" content=\"3116\" \/>\n\t<meta property=\"og:image:height\" content=\"1754\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:site\" content=\"@geopardagri\" \/>\n<meta name=\"twitter:label1\" content=\"Tiempo de lectura\" \/>\n\t<meta name=\"twitter:data1\" content=\"8 minutos\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/geopard.tech\\\/agroclimate-explorer\\\/\",\"url\":\"https:\\\/\\\/geopard.tech\\\/agroclimate-explorer\\\/\",\"name\":\"Agroclimate Future Explorer - GeoPard Agriculture\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/geopard.tech\\\/#website\"},\"datePublished\":\"2026-06-01T08:09:35+00:00\",\"dateModified\":\"2026-06-01T17:39:18+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/geopard.tech\\\/agroclimate-explorer\\\/#breadcrumb\"},\"inLanguage\":\"es\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/geopard.tech\\\/agroclimate-explorer\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/geopard.tech\\\/agroclimate-explorer\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/geopard.tech\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Agroclimate Future Explorer\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/geopard.tech\\\/#website\",\"url\":\"https:\\\/\\\/geopard.tech\\\/\",\"name\":\"GeoPard - Precision agriculture software\",\"description\":\"Precision agriculture Mapping software\",\"publisher\":{\"@id\":\"https:\\\/\\\/geopard.tech\\\/#organization\"},\"alternateName\":\"GeoPard\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/geopard.tech\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"es\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/geopard.tech\\\/#organization\",\"name\":\"GeoPard Agriculture\",\"alternateName\":\"GeoPard\",\"url\":\"https:\\\/\\\/geopard.tech\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"es\",\"@id\":\"https:\\\/\\\/geopard.tech\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/i0.wp.com\\\/geopard.tech\\\/wp-content\\\/uploads\\\/2022\\\/03\\\/favicon.png?fit=200%2C200&ssl=1\",\"contentUrl\":\"https:\\\/\\\/i0.wp.com\\\/geopard.tech\\\/wp-content\\\/uploads\\\/2022\\\/03\\\/favicon.png?fit=200%2C200&ssl=1\",\"width\":200,\"height\":200,\"caption\":\"GeoPard Agriculture\"},\"image\":{\"@id\":\"https:\\\/\\\/geopard.tech\\\/#\\\/schema\\\/logo\\\/image\\\/\"},\"sameAs\":[\"https:\\\/\\\/www.facebook.com\\\/geopardAgriculture\\\/\",\"https:\\\/\\\/x.com\\\/geopardagri\",\"https:\\\/\\\/www.linkedin.com\\\/company\\\/geopard-agriculture\\\/\",\"https:\\\/\\\/www.instagram.com\\\/geopardagriculture\\\/\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Explorador del futuro agroclim\u00e1tico - Agricultura GeoPard","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/geopard.tech\/es\/explorador-agroclimatico\/","og_locale":"es_ES","og_type":"article","og_title":"Agroclimate Future Explorer - GeoPard Agriculture","og_description":"Agroclimate Future Explorer \u00b7 Free \u00b7 No signup Where your farmland is going \u2014 by 2030, 2050, 2060. Pick a country and agroclimatic zone. Scrub...","og_url":"https:\/\/geopard.tech\/es\/explorador-agroclimatico\/","og_site_name":"GeoPard - Precision agriculture Mapping software","article_publisher":"https:\/\/www.facebook.com\/geopardAgriculture\/","article_modified_time":"2026-06-01T17:39:18+00:00","og_image":[{"width":3116,"height":1754,"url":"https:\/\/i0.wp.com\/geopard.tech\/wp-content\/uploads\/2026\/03\/GeoPard-Background-Precision-Ag-software-Do-more-with-your-data.png?fit=3116%2C1754&ssl=1","type":"image\/png"}],"twitter_card":"summary_large_image","twitter_site":"@geopardagri","twitter_misc":{"Tiempo de lectura":"8 minutos"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/geopard.tech\/agroclimate-explorer\/","url":"https:\/\/geopard.tech\/agroclimate-explorer\/","name":"Explorador del futuro agroclim\u00e1tico - Agricultura GeoPard","isPartOf":{"@id":"https:\/\/geopard.tech\/#website"},"datePublished":"2026-06-01T08:09:35+00:00","dateModified":"2026-06-01T17:39:18+00:00","breadcrumb":{"@id":"https:\/\/geopard.tech\/agroclimate-explorer\/#breadcrumb"},"inLanguage":"es","potentialAction":[{"@type":"ReadAction","target":["https:\/\/geopard.tech\/agroclimate-explorer\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/geopard.tech\/agroclimate-explorer\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/geopard.tech\/"},{"@type":"ListItem","position":2,"name":"Agroclimate Future Explorer"}]},{"@type":"WebSite","@id":"https:\/\/geopard.tech\/#website","url":"https:\/\/geopard.tech\/","name":"GeoPard - Software para agricultura de precisi\u00f3n","description":"Software de mapeo para agricultura de precisi\u00f3n","publisher":{"@id":"https:\/\/geopard.tech\/#organization"},"alternateName":"GeoPard","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/geopard.tech\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"es"},{"@type":"Organization","@id":"https:\/\/geopard.tech\/#organization","name":"GeoPard Agricultura","alternateName":"GeoPard","url":"https:\/\/geopard.tech\/","logo":{"@type":"ImageObject","inLanguage":"es","@id":"https:\/\/geopard.tech\/#\/schema\/logo\/image\/","url":"https:\/\/i0.wp.com\/geopard.tech\/wp-content\/uploads\/2022\/03\/favicon.png?fit=200%2C200&ssl=1","contentUrl":"https:\/\/i0.wp.com\/geopard.tech\/wp-content\/uploads\/2022\/03\/favicon.png?fit=200%2C200&ssl=1","width":200,"height":200,"caption":"GeoPard Agriculture"},"image":{"@id":"https:\/\/geopard.tech\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/geopardAgriculture\/","https:\/\/x.com\/geopardagri","https:\/\/www.linkedin.com\/company\/geopard-agriculture\/","https:\/\/www.instagram.com\/geopardagriculture\/"]}]}},"jetpack_likes_enabled":true,"jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/PdiCPa-3tb","_links":{"self":[{"href":"https:\/\/geopard.tech\/es\/wp-json\/wp\/v2\/pages\/13341","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/geopard.tech\/es\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/geopard.tech\/es\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/geopard.tech\/es\/wp-json\/wp\/v2\/users\/210157960"}],"replies":[{"embeddable":true,"href":"https:\/\/geopard.tech\/es\/wp-json\/wp\/v2\/comments?post=13341"}],"version-history":[{"count":0,"href":"https:\/\/geopard.tech\/es\/wp-json\/wp\/v2\/pages\/13341\/revisions"}],"wp:attachment":[{"href":"https:\/\/geopard.tech\/es\/wp-json\/wp\/v2\/media?parent=13341"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}