{"id":58305,"date":"2026-08-20T15:45:13","date_gmt":"2026-08-20T15:45:13","guid":{"rendered":"http:\/\/trueomni.com\/?page_id=58305"},"modified":"2026-08-20T15:46:02","modified_gmt":"2026-08-20T15:46:02","slug":"aspen-snowmass","status":"publish","type":"page","link":"https:\/\/trueomni.com\/spanish\/aspen-snowmass\/","title":{"rendered":"AspenSnowmass"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"58305\" class=\"elementor elementor-58305\" data-elementor-post-type=\"page\">\n\t\t\t\t<div class=\"elementor-element elementor-element-bf761dd e-con e-atomic-element e-flexbox-base e-default-div e-eaaadfb \" data-id=\"bf761dd\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"bf761dd\" data-e-type=\"e-flexbox\" data-id=\"bf761dd\">\n    \t\t<div class=\"elementor-element elementor-element-5627ff1 elementor-widget elementor-widget-html\" data-id=\"5627ff1\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<!DOCTYPE html>\n<html lang=\"en\" data-theme=\"light\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<link rel=\"preconnect\" href=\"https:\/\/fonts.googleapis.com\"><link rel=\"preconnect\" href=\"https:\/\/fonts.gstatic.com\" crossorigin>\n<link href=\"https:\/\/fonts.googleapis.com\/css2?family=Inter:wght@400;500;600;700;800;900&display=swap\" rel=\"stylesheet\">\n<title>TrueImpact \u2014 Executive Dashboard \u00b7 Aspen Snowmass<\/title>\n<!--\n  TrueImpact Executive Dashboard  \u00b7  TrueOmni  \u00b7  Destination Performance Intelligence\n  ---------------------------------------------------------------------------------\n  Single-file, dependency-free reference build. All data lives in the DATA object\n  (see <script id=\"model\">). Metrics are DERIVED from base inputs in deriveMetrics()\n  so the math is transparent and re-wireable to a live attribution API.\n  Illustrative\/sample data for demonstration. Brand + palette per TrueOmni house style.\n-->\n<style>\n:root{\n  \/* ---- TrueOmni Design System tokens (blue #0EA1DC \u00b7 ink #0A0A0A \u00b7 bg #F5F7FA \u00b7 Inter) ---- *\/\n  --navy:#0D1A2D; --navy-2:#0A1420; --azure:#0EA1DC; --azure-d:#0B8AC4;\n  --deep:#0B8AC4; --ink:#0A0A0A; --slate:#6B7280; --amber:#C2641F;\n  \/* ---- validated chart palette (dataviz skill; passes CVD + normal-vision gates) ---- *\/\n  --s-hotels:#0EA1DC; --s-dining:#EB6834; --s-attractions:#008300; --s-events:#E87BA4;\n  --s-direct:#0EA1DC; --s-influenced:#0B8AC4; --s-local:#0EA1DC; --s-state:#1BAF7A;\n  --good:#0ca30c; --warn:#C2641F;\n  \/* ---- surfaces \/ ink (light) ---- *\/\n  --bg:#F5F7FA; --surface:#ffffff; --surface-2:#F1F5F9; --line:#E5E7EB;\n  --text:#0A0A0A; --text-2:#6B7280; --muted:#9CA3AF; --grid:#E5E7EB;\n  --tint:#E0F2FE;\n  --shadow:0 1px 2px rgba(13,26,45,.06),0 8px 24px rgba(13,26,45,.06);\n  --shadow-lg:0 24px 60px rgba(13,26,45,.16);\n  --radius:16px; --radius-sm:11px;\n  --font:'Inter','Segoe UI',system-ui,-apple-system,Roboto,Helvetica,Arial,sans-serif;\n}\nhtml[data-theme=\"dark\"]{\n  --navy:#0D1A2D; --azure:#3BB8ED; --deep:#0B8AC4;\n  --s-hotels:#38b6ec; --s-dining:#f0854f; --s-attractions:#37a637; --s-events:#e493b5;\n  --s-direct:#38b6ec; --s-influenced:#2f77c4; --s-local:#38b6ec; --s-state:#22c48a;\n  --bg:#0a1420; --surface:#101d2e; --surface-2:#0c1826; --line:#1e2f45;\n  --text:#eaf1f8; --text-2:#a9b7c8; --muted:#6f8098; --grid:#182636;\n  --shadow:0 1px 2px rgba(0,0,0,.4),0 10px 30px rgba(0,0,0,.35);\n  --shadow-lg:0 24px 60px rgba(0,0,0,.55);\n}\n*{box-sizing:border-box;margin:0;padding:0}\nhtml,body{background:var(--bg);color:var(--text);font-family:var(--font);-webkit-font-smoothing:antialiased;scroll-behavior:smooth}\nbody{font-size:15px;line-height:1.5}\na{color:inherit;text-decoration:none}\nbutton{font-family:inherit;cursor:pointer;border:0;background:none;color:inherit}\n.tnum{font-variant-numeric:tabular-nums}\n\n\/* ---------- Top bar ---------- *\/\n.topbar{position:sticky;top:0;z-index:50;background:linear-gradient(100deg,var(--navy) 0%,var(--navy-2) 100%);color:#fff;box-shadow:0 6px 20px rgba(10,26,49,.28)}\n.topbar-inner{max-width:1320px;margin:0 auto;padding:12px 24px;display:flex;align-items:center;gap:18px}\n.brand{display:flex;align-items:center;gap:11px;font-weight:700;letter-spacing:.2px}\n.brand .mark{width:30px;height:30px;border-radius:8px;background:linear-gradient(135deg,var(--azure),#0f6fa0);display:grid;place-items:center;box-shadow:0 4px 14px rgba(28,156,216,.45)}\n.brand .mark svg{width:17px;height:17px}\n.brand .name{font-size:16px}.brand .name b{color:var(--azure)}\n.brand .sub{font-size:10px;font-weight:600;letter-spacing:1.6px;color:#9fc7de;text-transform:uppercase;margin-top:1px;white-space:nowrap}\n.brand{flex:none}\n.segset button{white-space:nowrap}\n@media (max-width:1280px){.brand .sub{display:none}.live{display:none}}\n.destpick{display:flex;align-items:center;gap:7px;background:rgba(255,255,255,.11);border:1px solid rgba(255,255,255,.14);border-radius:9px;padding:6px 10px;color:#dceaf5}\n.destpick svg{width:15px;height:15px;flex:none;color:var(--azure)}\n.destpick select{background:transparent;border:0;color:#fff;font-family:inherit;font-size:13px;font-weight:700;outline:none;cursor:pointer;max-width:190px}\n.destpick select option{color:#12202f}\n.sample{font-size:10px;font-weight:800;letter-spacing:1.2px;color:#ffd79a;background:rgba(194,100,31,.28);border:1px solid rgba(255,183,120,.4);border-radius:6px;padding:4px 8px;white-space:nowrap}\n@media (max-width:1180px){.sample{display:none}}\n.live{display:flex;align-items:center;gap:7px;font-size:12px;color:#bfe0f0;font-weight:600;margin-left:2px}\n.dot{width:8px;height:8px;border-radius:50%;background:#3ee08a;box-shadow:0 0 0 0 rgba(62,224,138,.7);animation:pulse 2s infinite}\n@keyframes pulse{0%{box-shadow:0 0 0 0 rgba(62,224,138,.55)}70%{box-shadow:0 0 0 9px rgba(62,224,138,0)}100%{box-shadow:0 0 0 0 rgba(62,224,138,0)}}\n.spacer{flex:1}\n.controls{display:flex;align-items:center;gap:9px}\n.segset{display:flex;background:rgba(255,255,255,.09);border-radius:10px;padding:3px}\n.segset button{font-size:12.5px;font-weight:600;color:#c5d6e6;padding:6px 12px;border-radius:7px;transition:.15s}\n.segset button.on{background:var(--azure);color:#04141f;box-shadow:0 2px 8px rgba(28,156,216,.4)}\n.icon-btn{width:36px;height:36px;border-radius:9px;background:rgba(255,255,255,.1);display:grid;place-items:center;color:#dceaf5;transition:.15s}\n.icon-btn:hover{background:rgba(255,255,255,.2)}\n.icon-btn svg{width:18px;height:18px}\n.cta{background:var(--azure);color:#04141f;font-weight:700;font-size:13px;padding:9px 15px;border-radius:9px;display:flex;align-items:center;gap:7px;transition:.15s}\n.cta:hover{background:#33abe0;transform:translateY(-1px)}\n.cta svg{width:15px;height:15px}\n\n\/* ---------- Breadcrumb \/ subnav ---------- *\/\n.wrap{max-width:1320px;margin:0 auto;padding:22px 24px 60px}\n.crumbs{display:flex;align-items:center;gap:8px;font-size:13px;color:var(--text-2);margin-bottom:16px;flex-wrap:wrap}\n.crumbs a{color:var(--azure);font-weight:600}\n.crumbs .sep{color:var(--muted)}\n.pagehead{display:flex;align-items:flex-end;justify-content:space-between;gap:16px;margin-bottom:6px;flex-wrap:wrap}\n.pagehead h1{font-size:27px;letter-spacing:-.4px;line-height:1.15}\n.pagehead .eyebrow{font-size:11.5px;font-weight:700;letter-spacing:2px;text-transform:uppercase;color:var(--azure);margin-bottom:5px}\n.pagehead .meta{font-size:12.5px;color:var(--muted);text-align:right}\n\n\/* ---------- Cards \/ grid ---------- *\/\n.grid{display:grid;gap:16px}\n.card{background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow);padding:18px 18px 16px}\n.card.pad0{padding:0;overflow:hidden}\n.card h3{font-size:13px;font-weight:700;letter-spacing:.2px;display:flex;align-items:center;gap:8px}\n.card h3 .hint{font-size:11px;color:var(--muted);font-weight:600}\n.card .ch-sub{font-size:12px;color:var(--text-2);margin-top:2px}\n.card-h{display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:14px}\n\n\/* KPI hero band *\/\n.kpis{display:grid;grid-template-columns:1.5fr 1fr 1fr 1fr;gap:16px}\n.kpi{position:relative;overflow:hidden}\n.kpi.hero{background:linear-gradient(140deg,var(--navy),#123056 90%);color:#fff;border:0}\n.kpi .k-label{font-size:12px;color:var(--text-2);font-weight:600;display:flex;align-items:center;gap:6px}\n.kpi.hero .k-label{color:#a8c6db}\n.kpi .k-val{font-size:34px;font-weight:700;letter-spacing:-1px;margin-top:7px;line-height:1}\n.kpi.hero .k-val{font-size:44px}\n.kpi .k-val .cur{font-size:.62em;opacity:.7;font-weight:600;vertical-align:.14em;margin-right:1px}\n.kpi .k-delta{font-size:12.5px;font-weight:700;margin-top:9px;display:inline-flex;align-items:center;gap:5px}\n.k-delta.up{color:var(--good)} .kpi.hero .k-delta.up{color:#5ef0a5}\n.kpi .k-foot{font-size:11.5px;color:var(--muted);margin-top:8px}\n.kpi.hero .k-foot{color:#8fb2ca}\n.spark{position:absolute;right:14px;bottom:12px;opacity:.9}\n.kpi.hero .glow{position:absolute;right:-40px;top:-40px;width:180px;height:180px;background:radial-gradient(circle,rgba(28,156,216,.5),transparent 70%);pointer-events:none}\n\n.row-2{grid-template-columns:1.35fr 1fr}\n.row-3{grid-template-columns:repeat(3,1fr)}\n.row-map{grid-template-columns:1.15fr .85fr}\n\n\/* Map *\/\n.mapwrap{position:relative;padding:8px}\n.mapwrap svg{width:100%;height:auto;display:block}\n.mk{cursor:pointer;transition:.15s}\n.mk circle.hit{fill:transparent}\n.mk .pin{transition:.15s;stroke:var(--surface);stroke-width:2}\n.mk:hover .pin,.mk.active .pin{filter:brightness(1.08)}\n.mk:hover .ring,.mk.active .ring{opacity:1}\n.mk .ring{fill:none;stroke:var(--azure);stroke-width:2;opacity:0;transition:.15s}\n.mk text{font-size:9.5px;font-weight:700;fill:var(--text);paint-order:stroke;stroke:var(--surface);stroke-width:3px;stroke-linejoin:round}\n.ca-outline{fill:var(--surface-2);stroke:var(--line);stroke-width:1.5}\nhtml[data-theme=\"dark\"] .ca-outline{fill:#0c1c2e;stroke:#20344c}\n\n\/* Location list *\/\n.loclist{display:flex;flex-direction:column;gap:2px}\n.locrow{display:grid;grid-template-columns:1fr auto auto;align-items:center;gap:12px;padding:11px 12px;border-radius:11px;cursor:pointer;transition:.13s;border:1px solid transparent}\n.locrow:hover{background:var(--surface-2);border-color:var(--line);transform:translateX(2px)}\n.locrow .lr-name{font-weight:700;font-size:13.5px}\n.locrow .lr-sub{font-size:11.5px;color:var(--muted);margin-top:1px}\n.locrow .lr-val{font-weight:700;font-size:14px;text-align:right}\n.locrow .lr-bar{grid-column:1\/-1;height:5px;border-radius:3px;background:var(--grid);overflow:hidden;margin-top:2px}\n.locrow .lr-bar i{display:block;height:100%;border-radius:3px;background:linear-gradient(90deg,var(--azure),#0f6fa0)}\n.locrow .chev{color:var(--muted)}\n\n\/* Funnel *\/\n.funnel{display:flex;flex-direction:column;gap:9px;margin-top:4px}\n.fstage{position:relative}\n.fstage .fbar{height:38px;border-radius:9px;display:flex;align-items:center;padding:0 13px;color:#fff;font-weight:700;font-size:13px;transition:width .6s cubic-bezier(.2,.8,.2,1);box-shadow:0 2px 8px rgba(14,34,64,.12)}\n.fstage .fmeta{position:absolute;right:0;top:50%;transform:translateY(-50%);display:flex;gap:12px;align-items:center;font-size:12px;color:var(--text-2);padding-right:4px}\n.fstage .fmeta b{color:var(--text);font-size:13px}\n.fstage .frate{font-size:11px;color:var(--muted);font-weight:600}\n\n\/* Legend *\/\n.legend{display:flex;flex-wrap:wrap;gap:14px;margin-top:12px;font-size:12px;color:var(--text-2)}\n.legend .li{display:flex;align-items:center;gap:6px;font-weight:600}\n.legend .sw{width:11px;height:11px;border-radius:3px}\n.legend .sw.line{height:3px;width:16px;border-radius:2px}\n\n\/* partner segment cards *\/\n.seg-cards{display:grid;grid-template-columns:repeat(4,1fr);gap:12px}\n.seg-card{border:1px solid var(--line);border-radius:var(--radius-sm);padding:14px;cursor:pointer;transition:.15s;background:var(--surface)}\n.seg-card:hover{transform:translateY(-3px);box-shadow:var(--shadow)}\n.seg-card .sc-top{display:flex;align-items:center;gap:8px;font-size:12.5px;font-weight:700}\n.seg-card .sc-ic{width:26px;height:26px;border-radius:7px;display:grid;place-items:center}\n.seg-card .sc-ic svg{width:15px;height:15px;color:#fff}\n.seg-card .sc-val{font-size:23px;font-weight:700;letter-spacing:-.5px;margin-top:11px}\n.seg-card .sc-sub{font-size:11.5px;color:var(--muted);margin-top:3px}\n.seg-card .sc-count{font-size:11.5px;color:var(--text-2);font-weight:600;margin-top:9px;display:flex;justify-content:space-between;border-top:1px solid var(--line);padding-top:8px}\n\n\/* table *\/\ntable{width:100%;border-collapse:collapse;font-size:13px}\nth,td{text-align:right;padding:10px 10px;border-bottom:1px solid var(--line)}\nth{font-size:11px;text-transform:uppercase;letter-spacing:.6px;color:var(--muted);font-weight:700}\nth:first-child,td:first-child{text-align:left}\ntbody tr{cursor:pointer;transition:.12s}\ntbody tr:hover{background:var(--surface-2)}\ntd .pill{display:inline-block;font-size:11px;font-weight:700;padding:2px 8px;border-radius:20px}\n.tag-hi{background:rgba(12,163,12,.13);color:var(--good)}\n.tag-mid{background:rgba(28,156,216,.14);color:var(--azure-d)}\n\n\/* AI insight banner *\/\n.ai-banner{display:flex;gap:14px;align-items:flex-start;background:linear-gradient(100deg,rgba(28,156,216,.08),rgba(20,80,140,.05));border:1px solid var(--line);border-left:3px solid var(--azure);border-radius:var(--radius);padding:15px 18px;margin-bottom:16px}\n.ai-banner .ai-ic{flex:none;width:34px;height:34px;border-radius:9px;background:linear-gradient(135deg,var(--azure),var(--deep));display:grid;place-items:center;box-shadow:0 4px 12px rgba(28,156,216,.35)}\n.ai-banner .ai-ic svg{width:18px;height:18px;color:#fff}\n.ai-banner .ai-txt{font-size:13.5px;color:var(--text);line-height:1.55}\n.ai-banner .ai-txt b{color:var(--text)}\n.ai-banner .ai-txt .em{color:var(--azure-d);font-weight:700}\nhtml[data-theme=\"dark\"] .ai-banner .ai-txt .em{color:var(--azure)}\n.ai-banner .ai-tag{font-size:10.5px;font-weight:700;letter-spacing:1.4px;text-transform:uppercase;color:var(--azure-d);margin-bottom:3px;display:block}\n\n\/* tooltip *\/\n.tt{position:fixed;z-index:99;pointer-events:none;background:var(--navy);color:#fff;font-size:12px;padding:9px 12px;border-radius:9px;box-shadow:var(--shadow-lg);opacity:0;transition:opacity .1s;max-width:230px;line-height:1.45}\n.tt .tt-t{font-weight:700;margin-bottom:3px;font-size:12.5px}\n.tt .tt-r{display:flex;justify-content:space-between;gap:16px;color:#bcd4e6}\n.tt .tt-r b{color:#fff}\nhtml[data-theme=\"dark\"] .tt{background:#020a14;border:1px solid #1e3350}\n\n\/* misc *\/\n.chip{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:600;padding:5px 11px;border-radius:20px;background:var(--surface-2);border:1px solid var(--line);color:var(--text-2)}\n.chip .sw{width:9px;height:9px;border-radius:50%}\n.stat-inline{display:flex;gap:26px;flex-wrap:wrap;margin-top:2px}\n.stat-inline .si b{display:block;font-size:20px;font-weight:700;letter-spacing:-.4px}\n.stat-inline .si span{font-size:11.5px;color:var(--muted);font-weight:600}\n.backbtn{display:inline-flex;align-items:center;gap:6px;font-size:13px;font-weight:600;color:var(--azure);margin-bottom:12px}\n.backbtn svg{width:15px;height:15px}\n.gauge-track{height:9px;border-radius:5px;background:var(--grid);overflow:hidden;margin-top:8px}\n.gauge-track i{display:block;height:100%;border-radius:5px;background:linear-gradient(90deg,var(--s-state),#0f9c6d)}\n.note{font-size:11px;color:var(--muted);margin-top:12px;line-height:1.5}\n\/* engagement filters *\/\n.filters{display:flex;flex-wrap:wrap;gap:10px 18px;align-items:center;background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow);padding:12px 16px;margin-bottom:16px}\n.fgroup{display:flex;align-items:center;gap:8px}\n.fgroup>.flab{font-size:11px;font-weight:700;letter-spacing:.5px;text-transform:uppercase;color:var(--muted)}\n.fseg{display:flex;background:var(--surface-2);border:1px solid var(--line);border-radius:9px;padding:3px;gap:2px}\n.fseg button{font-size:12.5px;font-weight:600;color:var(--text-2);padding:6px 12px;border-radius:6px;transition:.14s;white-space:nowrap}\n.fseg button.on{background:var(--azure);color:#04141f}\n.fsel{font-family:inherit;font-size:13px;font-weight:600;color:var(--text);background:var(--surface-2);border:1px solid var(--line);border-radius:8px;padding:7px 11px;cursor:pointer;outline:none}\n.fspace{flex:1}\n\/* A\/B test card *\/\n.ab-card{border:1px solid var(--line);border-radius:var(--radius-sm);padding:15px 16px;background:var(--surface)}\n.ab-head{display:flex;justify-content:space-between;align-items:flex-start;gap:10px;margin-bottom:12px}\n.ab-head .abt{font-size:13.5px;font-weight:700}\n.ab-head .abs{font-size:11px;color:var(--muted);margin-top:2px}\n.ab-badge{font-size:11px;font-weight:800;padding:4px 9px;border-radius:20px;white-space:nowrap}\n.ab-win{background:rgba(12,163,12,.14);color:var(--good)}\n.ab-none{background:rgba(139,150,165,.16);color:var(--text-2)}\n.ab-var{display:grid;grid-template-columns:auto 1fr auto;gap:10px;align-items:center;padding:9px 0;border-top:1px solid var(--line)}\n.ab-tag{width:22px;height:22px;border-radius:6px;display:grid;place-items:center;font-size:12px;font-weight:800;color:#fff}\n.ab-nm{font-size:12.5px;font-weight:600}\n.ab-sub{font-size:11px;color:var(--muted)}\n.ab-conv{text-align:right}\n.ab-conv b{font-size:19px;font-weight:700;letter-spacing:-.5px}\n.ab-conv span{font-size:10.5px;color:var(--muted);display:block}\n.ab-bar{grid-column:1\/-1;height:6px;border-radius:3px;background:var(--grid);overflow:hidden;margin-top:-2px}\n.ab-bar i{display:block;height:100%;border-radius:3px}\n.ab-foot{display:flex;justify-content:space-between;font-size:11.5px;color:var(--text-2);margin-top:11px;border-top:1px solid var(--line);padding-top:10px}\n.ab-foot b{color:var(--text)}\n.conv-pill{font-size:11px;font-weight:700;padding:2px 8px;border-radius:20px;background:rgba(28,156,216,.14);color:var(--azure-d)}\nhtml[data-theme=\"dark\"] .conv-pill{color:var(--azure)}\n.toggle-tbl{font-size:11.5px;color:var(--azure-d);font-weight:600;cursor:pointer}\nhtml[data-theme=\"dark\"] .toggle-tbl{color:var(--azure)}\n.hidden{display:none!important}\n.fade{animation:fade .35s ease}\n@keyframes fade{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}\n.footer{max-width:1320px;margin:0 auto;padding:20px 24px 40px;color:var(--muted);font-size:11.5px;display:flex;justify-content:space-between;gap:16px;flex-wrap:wrap;border-top:1px solid var(--line);margin-top:20px}\n\n@media (max-width:1080px){\n  .kpis{grid-template-columns:1fr 1fr}.row-2,.row-map,.row-3{grid-template-columns:1fr}.seg-cards{grid-template-columns:1fr 1fr}\n}\n@media print{\n  .topbar,.cta,.icon-btn,.segset,.toggle-tbl{display:none!important}\n  body{background:#fff}.card{box-shadow:none;border:1px solid #ccc;break-inside:avoid}\n}\n\n\/* ===================== TrueOmni Design System overrides ===================== *\/\n.eyebrow{color:var(--azure)!important;text-transform:uppercase;letter-spacing:.12em;font-weight:600}\n.eyebrow::before{content:'\\2500\\2500  ';color:var(--azure);font-weight:700;letter-spacing:0}\n.card{border-top:3px solid var(--azure)}\n.pagehead h1{letter-spacing:-.01em}\n.pagehead h1::after{content:'\\2022';color:var(--azure);margin-left:3px;font-weight:900}\n.crumbs a{color:var(--azure)}\n<\/style>\n<\/head>\n<body>\n\n<!-- =================== TOP BAR =================== -->\n<header class=\"topbar\">\n  <div class=\"topbar-inner\">\n    <div class=\"brand\">\n      <span class=\"mark\"><svg viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M3 20h18M6 20V10m4 10V5m4 15v-7m4 7V8\" stroke=\"#fff\" stroke-width=\"2.4\" stroke-linecap=\"round\"\/><\/svg><\/span>\n      <div>\n        <div class=\"name\">True<b>Impact<\/b><\/div>\n        <div class=\"sub\">Destination Performance Intelligence<\/div>\n      <\/div>\n    <\/div>\n    <label class=\"destpick\" style=\"display:none\"><svg viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M12 21s-7-6.2-7-11a7 7 0 1114 0c0 4.8-7 11-7 11z\" stroke=\"currentColor\" stroke-width=\"1.8\"\/><circle cx=\"12\" cy=\"10\" r=\"2.5\" stroke=\"currentColor\" stroke-width=\"1.8\"\/><\/svg><select id=\"destSel\"><\/select><\/label>\n    <span class=\"sample\" title=\"Modeled pre-deployment opportunity scenario \u2014 10 kiosks + 5 digital signage screens. Illustrative, not audited results.\">MODELED<\/span>\n    <div class=\"live\"><span class=\"dot\"><\/span> PROJECTION \u00b7 pre-deployment<\/div>\n    <div class=\"spacer\"><\/div>\n    <div class=\"controls\">\n      <div class=\"segset\" id=\"periodSel\">\n        <button data-p=\"q\" >Quarter<\/button>\n        <button data-p=\"ytd\">YTD<\/button>\n        <button data-p=\"t12\" class=\"on\">Trailing 12 mo<\/button>\n      <\/div>\n      <button class=\"icon-btn\" id=\"themeBtn\" title=\"Toggle theme\">\n        <svg viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M21 12.8A9 9 0 1111.2 3a7 7 0 009.8 9.8z\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/><\/svg>\n      <\/button>\n      <button class=\"cta\" onclick=\"window.print()\">\n        <svg viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M6 9V3h12v6M6 18H4v-6h16v6h-2M8 14h8v7H8z\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/><\/svg>\n        Council Briefing\n      <\/button>\n    <\/div>\n  <\/div>\n<\/header>\n\n<div class=\"wrap\">\n  <div id=\"app\"><\/div>\n<\/div>\n\n<footer class=\"footer\">\n  <div>\u00a9 TrueOmni 2026 \u00b7 TrueImpact Executive Dashboard \u00b7 Scottsdale, AZ \u00b7 www.trueomni.com \u00b7 833-300-6664<\/div>\n  <div>Modeled pre-deployment opportunity scenario \u00b7 figures in USD \u00b7 10 kiosks + 5 digital signage screens \u00b7 Aspen visitor-economy anchors are customer-supplied and unverified against a public source \u00b7 derived &amp; auditable via the TrueOmni attribution stack. Cite under NDA.<\/div>\n<\/footer>\n\n<div class=\"tt\" id=\"tt\"><\/div>\n\n<!-- =================== DATA MODEL =================== -->\n<script id=\"model\">\n\/* ------------------------------------------------------------------\n   TrueImpact data model \u2014 ASPEN SNOWMASS.\n   Single-tenant, covering the City of Aspen and the Town of Snowmass\n   Village as deployment zones under one executive view.\n   Currency: US dollars (USD).\n\n   THE THING THAT MAKES THIS DESTINATION DIFFERENT: Aspen is a HIGH-VALUE,\n   LOW-VOLUME market. It draws roughly a tenth of Kissimmee's visitors but\n   each one spends more than twice as much. So the hero metric is NOT\n   interaction volume \u2014 it is VALUE PER INTERACTION and VISITOR LIFETIME\n   VALUE. The engine is unchanged; the story it tells inverts.\n\n   CUSTOMER-SUPPLIED ANCHORS (Aspen visitor economy \u2014 provided by Doug,\n   attributed to Aspen visitor research; NOT independently verified\n   against a public source, see the provenance doc):\n     1.1M+ Aspen visitors \u00b7 $1.7B direct visitor spending\n     $2.0B Aspen economic impact \u00b7 15,000+ visitor-industry jobs\n     12.9 average lifetime trips per visitor (2025 research)\n   Derived from those: $1,545 direct visitor spend per visitor,\n   $19,936 lifetime visitor value.\n\n   CITED PUBLIC ANCHORS (Snowmass context):\n     ~$80M Aspen Snowmass summer-2025 on-mountain investment\n     95% slopeside lodging \u00b7 30+ restaurants \u00b7 Base Village\n     New Elk Camp 6-pack lift, Ullrhof rebuild, expanded snowmaking\n\n   MODELED: the 10-kiosk \/ 5-screen network, its zone placement, exposure\n   split, engagement rates, ad economics and the deployment ramp. This is\n   a PRE-DEPLOYMENT OPPORTUNITY SCENARIO, not audited results.\n------------------------------------------------------------------ *\/\nconst FUNNEL = { highIntent:0.17, conversion:0.14 };  \/\/ TrueOmni platform benchmarks\nconst INFL_RATE = 0.85;   \/\/ influenced $ per $1 direct (0.7\u20131.1 range)\nconst LODGING_SHARE = 0.42;       \/\/ of captured spend that is lodging \u2014 HIGH in a ski resort\nconst DINING_RETAIL_SHARE = 0.32; \/\/ dining + retail taxable base\nconst ATTR_SHARE = 0.23;          \/\/ lift tickets, ski school, tours, activities\nconst RENTAL_SHARE = 0.03;        \/\/ ground transport \/ rental \u2014 low, most guests shuttle\nconst RECIRC_MULT = 0.9;          \/\/ local re-spend of each public tax $ (soft)\n\n\/* Colorado tax model. Four flows leadership cares about:\n   1. Local lodging tax \u2014 City of Aspen 2.0%, Town of Snowmass Village 2.4%.\n      This is the money that funds destination marketing locally.\n   2. Local sales tax \u2014 Aspen: city 2.4% + Pitkin County 3.6% + RTA 0.4%\n      = 6.4%. Snowmass Village: town 3.5% + Pitkin 3.6% + RTA 0.4% = 7.5%.\n   3. Ground transport \/ rental \u2014 small in this market.\n   4. Colorado state sales tax, 2.9%.\n   CONFIRM ALL RATES. Aspen ballot measures passed in November 2025\n   raised the city sales-tax rate; published aggregates for Aspen range\n   from 10.35% to 11.15% depending on source and date. The rates below\n   use the explicitly itemized 2025 components and should be re-checked\n   against the current City of Aspen and Pitkin County schedules before\n   any external use.\n   Rates live on each zone as tax:{tot:lodging%, sales:local sales%,\n   rental:ground transport%}. *\/\nconst CO_STATE_SALES = 2.9;       \/\/ Colorado state sales tax %\n\n\/\/ period scaling (of full-deployment trailing-12 baseline)\nconst PERIODS = { t12:{label:'Trailing 12 months (annualized, full deployment)',k:1}, ytd:{label:'Year to date (2026)',k:0.545}, q:{label:'This quarter (Q2 2026)',k:0.262} };\n\n\/* ==================================================================\n   DESTINATION CONTEXT \u2014 the customer's own scale, shown first so every\n   modeled number below is read against a real baseline.\n   These are CUSTOMER-SUPPLIED, not TrueOmni estimates.\n================================================================== *\/\nconst DEST_CONTEXT={\n  visitors:1_100_000,\n  visitorSpend:1_700_000_000,     \/\/ direct visitor spending\n  economicImpact:2_000_000_000,\n  jobs:15_000,\n  lifetimeTrips:12.9,\n  note:'Aspen visitor economy \u2014 customer-supplied figures.',\n  snowmass:[\n    {v:'95%',    l:'Slopeside lodging',        d:'Snowmass Village \u2014 the highest slopeside share of any major US resort.'},\n    {v:'$80M+',  l:'2025 mountain investment', d:'Aspen Snowmass summer-2025 projects: new Elk Camp 6-pack lift, Ullrhof rebuild, expanded snowmaking.'},\n    {v:'30+',    l:'Restaurants',              d:'Across Base Village, the Mall and on-mountain.'},\n    {v:'Year-round', l:'Event calendar',       d:'Major summer and winter programming plus continuing Base Village development.'},\n  ],\n};\n\n\/\/ Partner segment metadata\nconst SEG = {\n  hotels:      {name:'Lodging & Slopeside Stays', color:'var(--s-hotels)',      raw:'#1C9CD8'},\n  dining:      {name:'Restaurants & Dining',      color:'var(--s-dining)',      raw:'#EB6834'},\n  attractions: {name:'Mountain, Lift & Activities',color:'var(--s-attractions)', raw:'#008300'},\n  events:      {name:'Events & Venues',           color:'var(--s-events)',      raw:'#E87BA4'},\n};\n\n\/\/ seasonal shape (Jan..Dec) \u2014 a two-peak alpine resort: deep Dec\u2013Mar ski\n\/\/ peak, a strong Jul\u2013Aug summer peak, and the brutal Apr\u2013May and Oct\n\/\/ \"mud season\" troughs. Those troughs are the single largest commercial\n\/\/ opportunity in the whole model.\nconst SEASON = {\n  aspen:[1.42,1.44,1.48,.52,.42,.86,1.24,1.20,.92,.54,.66,1.30],\n};\nconst MONTHS=['Jul','Aug','Sep','Oct','Nov','Dec','Jan','Feb','Mar','Apr','May','Jun'];\n\n\/* ==================================================================\n   DESTINATION REGISTRY \u2014 single tenant: Aspen Snowmass.\n   Each \"location\" is a deployment zone with kiosk count, SCREEN count\n   and deployment phase. x,y = map coords in the 480\u00d7620 space.\n   tax = {tot:local lodging%, sales:local sales%, rental:%}.\n   NOTE the avgSpend values: they are 2\u20134\u00d7 a mass-market destination's,\n   because Aspen's per-visitor direct spend is ~$1,545 vs ~$673 in a\n   high-volume Florida market. Fewer interactions, far more value each.\n================================================================== *\/\nconst DESTINATIONS = {\n  asp:{ name:'Aspen Snowmass', kind:'Destination Marketing Organization', scope:'Aspen & Snowmass Village', region:'Pitkin County, CO',\n    outline:'M60,150 L200,110 L300,150 L360,230 L392,320 L372,400 L330,470 L250,520 L150,500 L80,430 L56,320 Z',\n    lake:'M392,320 L474,300 L474,560 L250,560 L250,520 L330,470 L372,400 Z',\n    lakeLabel:{x:400,y:470,t:'Elk Mountains'},\n    locations:[\n      {id:'downtown',     name:'Downtown Aspen Core',            sites:'Visitor Center \u00b7 Hyman & Cooper malls \u00b7 galleries', x:325,y:280, kiosks:3, screens:1, phase:1, exposures:110_000, interactionRate:.50, avgSpend:520, tax:{tot:2.0,sales:6.4,rental:4.0}, season:'aspen', mix:{hotels:.26,dining:.36,attractions:.18,events:.20}, note:'The pedestrian core \u2014 highest dwell, highest discovery intent, and the natural home for the AI concierge. Anchors on the ACRA visitor centre. Phase-1 launch cluster.', badge:'Phase 1'},\n      {id:'aspenmtn',     name:'Aspen Mountain Base \u00b7 Gondola Plaza', sites:'Silver Queen Gondola \u00b7 Little Nell \u00b7 Ajax base', x:330,y:330, kiosks:2, screens:1, phase:1, exposures:95_000, interactionRate:.52, avgSpend:640, tax:{tot:2.0,sales:6.4,rental:4.0}, season:'aspen', mix:{hotels:.34,dining:.22,attractions:.34,events:.10}, note:'Captive lift-queue audience with the highest interaction rate in the network, adjacent to the destination\\'s highest-value lodging. Phase-1 launch cluster.', badge:'Phase 1'},\n      {id:'basevillage', name:'Snowmass Base Village',            sites:'Base Village plaza \u00b7 Limelight \u00b7 Viceroy', x:130,y:250, kiosks:2, screens:2, phase:2, exposures:90_000, interactionRate:.54, avgSpend:680, tax:{tot:2.4,sales:7.5,rental:4.0}, season:'aspen', mix:{hotels:.46,dining:.24,attractions:.20,events:.10}, note:'95% slopeside lodging and continuing Base Village development \u2014 the highest attributed spend per converted visitor anywhere in the network, and the strongest video-wall placement.'},\n      {id:'snowmassmall', name:'Snowmass Village Mall',           sites:'Village Mall \u00b7 Elk Camp gondola \u00b7 dining', x:110,y:200, kiosks:1, screens:1, phase:2, exposures:60_000, interactionRate:.48, avgSpend:470, tax:{tot:2.4,sales:7.5,rental:4.0}, season:'aspen', mix:{hotels:.28,dining:.40,attractions:.22,events:.10}, note:'The village\\'s dining and retail spine \u2014 30+ restaurants across the resort make this the highest-yield zone for restaurant discovery and offers.'},\n      {id:'highlands',    name:'Aspen Highlands Village',         sites:'Highlands base village \u00b7 Highland Bowl access', x:250,y:365, kiosks:1, screens:0, phase:3, exposures:45_000, interactionRate:.46, avgSpend:495, tax:{tot:2.0,sales:6.4,rental:4.0}, season:'aspen', mix:{hotels:.30,dining:.26,attractions:.34,events:.10}, note:'Smaller, more expert-skewed base village \u2014 lower volume, high intent, and a natural test bed for conditions and terrain content.'},\n      {id:'buttermilk',   name:'Buttermilk & ASE Airport Corridor', sites:'Buttermilk base \u00b7 Aspen\/Pitkin County Airport', x:215,y:300, kiosks:1, screens:0, phase:3, exposures:30_000, interactionRate:.42, avgSpend:405, tax:{tot:2.0,sales:6.4,rental:4.0}, season:'aspen', mix:{hotels:.36,dining:.20,attractions:.24,events:.20}, note:'The arrivals gateway and event venue (X Games host) \u2014 the first and last touchpoint of the trip, and the best place to capture a returning-visitor relationship.'},\n    ]},\n};\n\nlet activeDest='asp';\nfunction ACT(){return DESTINATIONS[activeDest];}\nfunction LOCS(){return ACT().locations;}\nfunction findLoc(id){return LOCS().find(l=>l.id===id);}\nfunction SCREENS(locs=LOCS()){return locs.reduce((s,L)=>s+(L.screens||0),0);}\nconst NETWORK_KIOSKS=10, NETWORK_SCREENS=5;\n\n\/* deployment ramp phases (5 \u2192 8 \u2192 10 kiosks; 2 \u2192 5 screens) *\/\nconst PHASES=[\n  {n:1, name:'Phase 1 \u00b7 Aspen core & gondola launch', window:'Weeks 0\u20134', ids:['downtown','aspenmtn']},\n  {n:2, name:'Phase 2 \u00b7 Snowmass Village',            window:'Weeks 5\u20138', ids:['downtown','aspenmtn','basevillage','snowmassmall']},\n  {n:3, name:'Phase 3 \u00b7 Full network',                window:'Weeks 9\u201316', ids:['downtown','aspenmtn','basevillage','snowmassmall','highlands','buttermilk']},\n];\n\n\/* ==================================================================\n   AI CONCIERGE ENGAGEMENT \u2014 the metrics that describe what visitors\n   actually DO at a TrueOmni surface, in the customer's own vocabulary:\n   People Reached \u2192 Impressions \u2192 Interactions \u2192 AI Conversations \u2192\n   Itineraries Created \u2192 Attractions\/Restaurants Discovered \u2192\n   Offers\/Tickets Viewed \u2192 Conversions.\n   All rates are TrueOmni platform benchmarks \u2014 MODELED.\n================================================================== *\/\nconst ENGAGE={\n  aiConvRate:0.22,             \/\/ share of interactions that become an AI concierge conversation\n  avgConvTurns:4.2,            \/\/ conversational turns per AI conversation\n  itinPerAiConv:0.38,          \/\/ itineraries created per AI conversation\n  discoverPerInteraction:2.6,  \/\/ attraction & restaurant detail views per interaction\n  offersPerInteraction:1.15,   \/\/ offers \/ tickets \/ passes viewed per interaction\n  handoffRate:0.19,            \/\/ share of interactions handed off to the visitor's phone\n  imprPerInteraction:3.2,      \/\/ on-screen impressions per interaction\n};\nfunction engageMetrics(k=1, locs=LOCS()){\n  const m=rollup(k,locs);\n  const interactions=m.interactions;\n  const aiConversations=interactions*ENGAGE.aiConvRate;\n  const convTurns=aiConversations*ENGAGE.avgConvTurns;\n  const itineraries=aiConversations*ENGAGE.itinPerAiConv;\n  const discoveries=interactions*ENGAGE.discoverPerInteraction;\n  const offersViewed=interactions*ENGAGE.offersPerInteraction;\n  const handoffs=interactions*ENGAGE.handoffRate;\n  const impressions=interactions*ENGAGE.imprPerInteraction;\n  return {reached:m.exposures, impressions, interactions, aiConversations, convTurns,\n          itineraries, discoveries, offersViewed, handoffs, conversions:m.conversions,\n          valuePerInteraction:m.total\/interactions, valuePerAiConv:m.total\/aiConversations};\n}\n\n\/* ==================================================================\n   VISITOR LIFETIME VALUE \u2014 the strategic argument for an AI concierge.\n   Aspen research puts the average visitor at 12.9 lifetime trips. The\n   programme is therefore not competing for one trip's spend; it is\n   competing for a RELATIONSHIP. We deliberately do NOT claim credit for\n   all 12.9 trips \u2014 we model the value of a marginal additional trip.\n================================================================== *\/\nconst LTV={\n  lifetimeTrips:DEST_CONTEXT.lifetimeTrips,                                  \/\/ 12.9 \u2014 customer research\n  spendPerTrip:DEST_CONTEXT.visitorSpend\/DEST_CONTEXT.visitors,              \/\/ ~$1,545 \u2014 derived\n  scenarios:[                                                                 \/\/ marginal trips added per influenced visitor\n    {id:'cons', name:'Conservative', trips:0.25, note:'A quarter-trip lift across influenced visitors.'},\n    {id:'base', name:'Base',         trips:0.50, note:'Half a trip \u2014 one extra visit for every two influenced visitors.'},\n    {id:'strong',name:'Strong',      trips:1.00, note:'One additional lifetime trip per influenced visitor.'},\n  ],\n};\nfunction ltvMetrics(k=1, locs=LOCS()){\n  const m=rollup(k,locs);\n  const lifetimeValue=LTV.lifetimeTrips*LTV.spendPerTrip;   \/\/ ~$19,936 per visitor relationship\n  const influenced=m.conversions;                            \/\/ attributed converted visitors \/ yr\n  const relationshipValue=influenced*lifetimeValue;          \/\/ total lifetime value under management\n  const scen=LTV.scenarios.map(s=>({...s,\n    futureSpend:influenced*s.trips*LTV.spendPerTrip,\n    pctLift:s.trips\/LTV.lifetimeTrips}));\n  return {lifetimeValue, influenced, relationshipValue, scen,\n          spendPerTrip:LTV.spendPerTrip, lifetimeTrips:LTV.lifetimeTrips};\n}\n\n\/* ==================================================================\n   TOP-DOWN OPPORTUNITY BAND \u2014 the executive sanity check. Rather than\n   only building impact bottom-up through the funnel, express it as a\n   share of the destination's own reported direct visitor spending. If\n   the bottom-up number lands inside this band, both methods agree and\n   the model is credible. If it lands outside, the model is wrong.\n================================================================== *\/\nconst OPPORTUNITY={\n  base:DEST_CONTEXT.visitorSpend,   \/\/ $1.7B direct visitor spending\n  rates:[0.0010, 0.0025, 0.0050, 0.0100],\n  label:'Illustrative economic opportunity based on Aspen\\'s reported direct visitor spending. Actual impact will be measured through TrueOmni engagement and conversion data after deployment.',\n};\nfunction opportunityRows(){return OPPORTUNITY.rates.map(r=>({rate:r, v:OPPORTUNITY.base*r}));}\n\n\/* ==================================================================\n   MEDIA NETWORK (kiosk advertising revenue). Aspen is a PREMIUM ad\n   market \u2014 luxury real estate, galleries, private aviation, ski retail\n   and hospitality all buy against this audience \u2014 so the per-kiosk rate\n   is set materially above the platform benchmark. MODELED until a rate\n   card lands.\n================================================================== *\/\nconst MEDIA={\n  perKioskMo:{floor:700, mid:1400, strong:2400},   \/\/ USD \/ kiosk \/ month at mature fill\n  imprPerEncounter:3.2,\n  fillMature:0.72,\n  fillByPhase:{1:0.36, 2:0.58, 3:0.72},\n  streamSplit:{direct:0.74, programmatic:0.26},    \/\/ heavily direct-sold \u2014 a luxury local advertiser base\n  softwareYr:12000,            \/\/ annual TrueOmni platform software, 10 kiosks (MODELED)\n  capital:95000,               \/\/ one-time kiosk capital, 10 units @ ~$9.5K, alpine-rated (MODELED)\n  inventory:[\n    {id:'video',    name:'Full-screen video (kiosk attract loop)', share:.30},\n    {id:'sponsored',name:'Sponsored listings & tiles',             share:.22},\n    {id:'banner',   name:'Home & footer banners',                  share:.20},\n    {id:'rail',     name:'Events \/ listings side-rail',            share:.16},\n    {id:'qr',       name:'QR-conversion sponsorships',             share:.12},\n  ],\n};\nfunction mediaMetrics(k=1, locs=LOCS()){\n  const r=rollup(k,locs);\n  const kiosks=r.kiosks;\n  const impressions=r.exposures*MEDIA.imprPerEncounter;\n  const band={}; for(const s in MEDIA.perKioskMo) band[s]=kiosks*MEDIA.perKioskMo[s]*12*k;\n  const rev=band.mid;\n  const cpm=impressions? rev\/(impressions\/1000):0;\n  const byStream={direct:rev*MEDIA.streamSplit.direct, programmatic:rev*MEDIA.streamSplit.programmatic};\n  const byInv=MEDIA.inventory.map(it=>({...it, v:rev*it.share}));\n  return {kiosks,impressions,band,rev,cpm,byStream,byInv};\n}\n\n\/* ==================================================================\n   DIGITAL SIGNAGE \u2014 non-interactive broadcast screens (OMNI Signage).\n   Screens deliver REACH and ad inventory; they do NOT transact and are\n   excluded from attributed spend, economic impact and tax.\n================================================================== *\/\nconst SIGNAGE={\n  enabled:true,\n  perScreenMo:{floor:300, mid:650, strong:1100}, \/\/ premium alpine \/ luxury market\n  loopSec:60, spotSec:15,\n  viewShare:0.62,                  \/\/ village plazas funnel foot traffic past screens\n  imprPerPasserby:1.6,\n  dwellSec:9,\n  fillMature:0.68, fillByPhase:{1:0.32,2:0.52,3:0.68},\n  capitalPerScreen:5200,           \/\/ weatherized \/ alpine-rated enclosures\n  softwarePerScreenYr:600,\n  formats:[\n    {id:'wall',     name:'Video wall \/ base-village hero',   share:.36},\n    {id:'portrait', name:'Portrait posters (plaza & lobby)', share:.26},\n    {id:'wayfind',  name:'Wayfinding & mountain conditions', share:.22},\n    {id:'menu',     name:'Menu \/ retail promo boards',       share:.10},\n    {id:'outdoor',  name:'Outdoor \/ weatherized',            share:.06},\n  ],\n};\nfunction signageMetrics(k=1, locs=LOCS()){\n  if(!SIGNAGE.enabled) return null;\n  const screens=locs.reduce((s,L)=>s+(L.screens||0),0);\n  const reach=locs.reduce((s,L)=>s+L.exposures,0)*k*SIGNAGE.viewShare;\n  const impressions=reach*SIGNAGE.imprPerPasserby;\n  const band={}; for(const s in SIGNAGE.perScreenMo) band[s]=screens*SIGNAGE.perScreenMo[s]*12*k;\n  const rev=band.mid;\n  const cpm=impressions? rev\/(impressions\/1000):0;\n  const spotsPerLoop=Math.round(SIGNAGE.loopSec\/SIGNAGE.spotSec);\n  const shareOfVoice=1\/spotsPerLoop;\n  const capital=screens*SIGNAGE.capitalPerScreen, softwareYr=screens*SIGNAGE.softwarePerScreenYr;\n  const byFormat=SIGNAGE.formats.map(f=>({...f, v:rev*f.share}));\n  return {screens,reach,impressions,band,rev,cpm,spotsPerLoop,shareOfVoice,capital,softwareYr,byFormat};\n}\n\n\/* ---- derive all metrics for one location at a period scale ---- *\/\nfunction deriveMetrics(L, k=1){\n  const exposures = L.exposures*k;\n  const interactions = exposures*L.interactionRate;\n  const highIntent = interactions*FUNNEL.highIntent;\n  const conversions = highIntent*FUNNEL.conversion;\n  const directSpend = conversions*L.avgSpend;             \/\/ visitor $ captured & attributed (USD)\n  const direct = directSpend;\n  const influenced = directSpend*INFL_RATE;\n  const total = direct+influenced;\n  \/\/ -------- Colorado tax engine (attributed spend flows into each base) --------\n  const r = L.tax;                                        \/\/ {tot:lodging%, sales:local sales%, rental:%}\n  const taxableSpend = directSpend + influenced*0.5;      \/\/ direct + half of influenced re-spend\n  const lodgingBase  = taxableSpend*LODGING_SHARE;\n  const diningBase   = taxableSpend*DINING_RETAIL_SHARE;\n  const attrBase     = taxableSpend*ATTR_SHARE;\n  const rentalBase   = taxableSpend*RENTAL_SHARE;\n  \/\/ taxBreak keys kept stable for the render layer, mapped to Colorado lines:\n  const hotelTOT   = lodgingBase*(r.tot\/100);                                   \/\/ local lodging tax\n  const salesTax   = (lodgingBase+diningBase+attrBase)*(r.sales\/100);           \/\/ city\/town + county + RTA\n  const rentalTax  = rentalBase*(r.rental\/100);                                 \/\/ ground transport \/ rental\n  const stateTax   = (lodgingBase+diningBase+attrBase+rentalBase)*(CO_STATE_SALES\/100); \/\/ CO state sales\n  const taxLocal   = hotelTOT+salesTax+rentalTax;         \/\/ stays in Aspen \/ Snowmass Village \/ Pitkin\n  const taxState   = stateTax;                            \/\/ State of Colorado\n  const tax        = taxLocal+taxState;\n  const recirc     = tax*RECIRC_MULT;                     \/\/ SOFT: public $ re-spent in community\n  const taxBreak = {hotelTOT,salesTax,rentalTax,stateTax};\n  const partners = {};\n  for(const s in L.mix){ partners[s] = directSpend*L.mix[s]*1.0 + influenced*L.mix[s]*0.6; }\n  const revAssist = L.kiosks*1500 + (L.kiosks>5?L.kiosks*1100:L.kiosks*700);\n  return {exposures,interactions,highIntent,conversions,directSpend,direct,influenced,total,taxLocal,taxState,tax,taxBreak,recirc,partners,revAssist};\n}\nfunction seriesFor(L,k=1){\n  const m=deriveMetrics(L,k); const sh=SEASON[L.season];\n  const base=m.total\/sh.reduce((a,b)=>a+b,0);\n  const order=[6,7,8,9,10,11,0,1,2,3,4,5];\n  return order.map(i=>base*sh[i]);\n}\nfunction rollup(k=1, locs=LOCS()){\n  const acc={exposures:0,interactions:0,highIntent:0,conversions:0,directSpend:0,direct:0,influenced:0,total:0,taxLocal:0,taxState:0,tax:0,recirc:0,revAssist:0,kiosks:0,\n    partners:{hotels:0,dining:0,attractions:0,events:0}, taxBreak:{hotelTOT:0,salesTax:0,rentalTax:0,stateTax:0}};\n  locs.forEach(L=>{const m=deriveMetrics(L,k);for(const key in acc){\n    if(key==='partners'){for(const s in acc.partners)acc.partners[s]+=m.partners[s];}\n    else if(key==='taxBreak'){for(const s in acc.taxBreak)acc.taxBreak[s]+=m.taxBreak[s];}\n    else if(key==='kiosks'){acc.kiosks+=L.kiosks;}\n    else if(m[key]!=null)acc[key]+=m[key];}});\n  return acc;\n}\nfunction rollupSeries(k=1, locs=LOCS()){\n  const t=new Array(12).fill(0);\n  locs.forEach(L=>{const s=seriesFor(L,k);s.forEach((v,i)=>t[i]+=v);});\n  return t;\n}\n\n\/* ---------- (network roll-up retained but unused in single-tenant) ---------- *\/\nconst NETWORK={ destinations:1, kiosks:NETWORK_KIOSKS, targetTotal:0, targetTax:0, targetActions:0 };\nfunction mergeRollups(list){\n  const acc={exposures:0,interactions:0,highIntent:0,conversions:0,directSpend:0,direct:0,influenced:0,total:0,taxLocal:0,taxState:0,tax:0,recirc:0,revAssist:0,kiosks:0,\n    partners:{hotels:0,dining:0,attractions:0,events:0}, taxBreak:{hotelTOT:0,salesTax:0,rentalTax:0,stateTax:0}};\n  list.forEach(m=>{for(const key in acc){\n    if(key==='partners'){for(const s in acc.partners)acc.partners[s]+=m.partners[s];}\n    else if(key==='taxBreak'){for(const s in acc.taxBreak)acc.taxBreak[s]+=m.taxBreak[s];}\n    else if(m[key]!=null)acc[key]+=m[key];}});\n  return acc;\n}\nfunction scaleRollup(m,f){const o=JSON.parse(JSON.stringify(m));for(const key in o){ if(key==='partners'||key==='taxBreak'){for(const s in o[key])o[key][s]*=f;} else o[key]*=f; }return o;}\nconst NET_IDS=['asp'];\nfunction destRollup(id,k){return rollup(k, DESTINATIONS[id].locations);}\nfunction networkAgg(k=1){const parts=NET_IDS.map(id=>destRollup(id,k));const real=mergeRollups(parts);const net=scaleRollup(real,1);net.kiosks=real.kiosks;net.destinations=1;return {real,net,others:scaleRollup(real,0),scale:1,parts};}\nfunction networkSeries(k=1){return rollupSeries(k, LOCS());}\n\n\/* ==================================================================\n   ENGAGEMENT DRILL-THROUGH \u2014 TrueOmni modules weighted for an AI\n   concierge deployment, and Aspen Snowmass campaigns.\n================================================================== *\/\nconst MODULES=[\n  {id:'ai',     name:'OMNI AI Concierge',              tag:'attractions', share:.18, dwell:210, intentRel:2.2, convRel:2.0},\n  {id:'itin',   name:'OMNI Itinerary',                 tag:'attractions', share:.13, dwell:165, intentRel:1.9, convRel:1.6},\n  {id:'dine',   name:'Dining & Restaurants',           tag:'dining',      share:.13, dwell:104, intentRel:1.3, convRel:1.2},\n  {id:'tickets',name:'Lift Tickets, Passes & Lessons', tag:'attractions', share:.11, dwell:126, intentRel:2.1, convRel:1.9},\n  {id:'map',    name:'OMNI Maps \u00b7 Trails & Conditions',tag:'attractions', share:.11, dwell:88,  intentRel:0.9, convRel:0.8},\n  {id:'stay',   name:'Lodging & Slopeside Stays',      tag:'hotels',      share:.09, dwell:118, intentRel:1.7, convRel:1.6},\n  {id:'events', name:'Events Calendar',                tag:'events',      share:.08, dwell:86,  intentRel:1.1, convRel:1.0},\n  {id:'phone',  name:'Mobile Handoff (Send to Phone)', tag:'hotels',      share:.06, dwell:34,  intentRel:2.4, convRel:2.3},\n  {id:'wayfind',name:'OMNI Wayfinding',                tag:'attractions', share:.05, dwell:70,  intentRel:0.7, convRel:0.6},\n  {id:'guest',  name:'OMNI GuestBook (lead capture)',  tag:'hotels',      share:.03, dwell:44,  intentRel:2.3, convRel:2.2},\n  {id:'lang',   name:'Multi-language & ADA',           tag:'attractions', share:.03, dwell:40,  intentRel:0.5, convRel:0.4},\n];\nconst CAMP_TYPE={ 'Pass \/ loyalty':'#1C9CD8','Automation':'#1BAF7A','Partner co-op':'#EB6834','Group \/ MICE':'#14508C','Paid media':'#E87BA4','Content':'#008300','Itinerary \/ CRM':'#6B4FB0','Promotions':'#EDA100' };\nconst CAMPAIGNS=[\n  {id:'concierge', name:'AI Concierge \u00b7 Plan My Day',        type:'Itinerary \/ CRM',share:.19, intentRel:2.0, convRel:1.8, cpi:1.30},\n  {id:'sponsored', name:'Sponsored Placements (ad partners)',type:'Paid media',     share:.16, intentRel:0.9, convRel:0.8, cpi:0.55},\n  {id:'pass',      name:'Lift Pass & Lesson Upsell',         type:'Pass \/ loyalty', share:.15, intentRel:1.7, convRel:1.6, cpi:0.95},\n  {id:'shoulder',  name:'Shoulder-Season Recovery (Apr\u2013May, Oct)', type:'Automation', share:.14, intentRel:1.4, convRel:1.5, cpi:0.60},\n  {id:'dine',      name:'Restaurant Discovery & Reservations',type:'Partner co-op', share:.13, intentRel:1.3, convRel:1.2, cpi:1.05},\n  {id:'summer',    name:'Summer Aspen Snowmass',             type:'Content',        share:.11, intentRel:1.0, convRel:0.9, cpi:0.70},\n  {id:'events',    name:'Events & Group Travel',             type:'Group \/ MICE',   share:.07, intentRel:1.1, convRel:1.3, cpi:0.85},\n  {id:'return',    name:'Return-Visitor Relationship Build', type:'Promotions',     share:.05, intentRel:1.5, convRel:1.4, cpi:0.90},\n];\nconst AB_TESTS=[\n  {id:'t1', camp:'concierge', name:'AI Concierge \u2014 opening prompt',   a:'\u201cAsk me anything\u201d', b:'\u201cWhat should I do today?\u201d', split:.5, bLift:1.21, conf:0.97, status:'B winning'},\n  {id:'t2', camp:'pass',      name:'Lift pass upsell \u2014 framing',      a:'\u201cBuy a day pass\u201d', b:'\u201cAdd tomorrow for less\u201d',    split:.5, bLift:1.14, conf:0.94, status:'B winning'},\n  {id:'t3', camp:'sponsored', name:'Sponsored placement \u2014 layout',    a:'Carousel', b:'Full-card',                          split:.5, bLift:0.94, conf:0.88, status:'A winning'},\n  {id:'t4', camp:'shoulder',  name:'Shoulder-season \u2014 offer framing', a:'\u201cSave 30%\u201d', b:'\u201cHave the mountain to yourself\u201d',  split:.5, bLift:1.08, conf:0.74, status:'No winner yet'},\n];\nfunction splitFunnel(m, items, segShares){\n  const sSum=items.reduce((a,b)=>a+b.share,0);\n  const rows=items.map(it=>{\n    let w=it.share\/sSum;\n    if(segShares && it.tag){ w*=Math.max(0.35, 1+0.7*((segShares[it.tag]||.25)-0.25)); }\n    return {it, w};\n  });\n  const wSum=rows.reduce((a,b)=>a+b.w,0);\n  rows.forEach(r=>r.w\/=wSum);\n  const hiRaw=rows.map(r=>r.w*r.it.intentRel), hiS=hiRaw.reduce((a,b)=>a+b,0);\n  const cvRaw=rows.map((r,i)=>hiRaw[i]*r.it.convRel), cvS=cvRaw.reduce((a,b)=>a+b,0);\n  return rows.map((r,i)=>{\n    const interactions=m.interactions*r.w;\n    const highIntent=m.highIntent*hiRaw[i]\/hiS;\n    const conversions=m.conversions*cvRaw[i]\/cvS;\n    const exposures=m.exposures*r.w;\n    const impact=m.total*cvRaw[i]\/cvS;\n    const timeHrs=r.it.dwell? interactions*r.it.dwell\/3600 : null;\n    const spend=r.it.cpi!=null? interactions*r.it.cpi : null;\n    return {...r.it, exposures, interactions, highIntent, conversions, impact, timeHrs, spend,\n      convRate:conversions\/interactions, roas:spend?impact\/spend:null, valPerK:impact\/(interactions\/1000)};\n  });\n}\nfunction segSharesOf(m){const t=Object.values(m.partners).reduce((a,b)=>a+b,0)||1;const o={};for(const s in m.partners)o[s]=m.partners[s]\/t;return o;}\n<\/script>\n\n<!-- =================== FORMAT + CHART HELPERS =================== -->\n<script id=\"helpers\">\nconst $=(s,r=document)=>r.querySelector(s);\nconst NS='http:\/\/www.w3.org\/2000\/svg';\nfunction el(t,a={},kids=[]){const e=document.createElementNS(NS,t);for(const k in a)e.setAttribute(k,a[k]);(Array.isArray(kids)?kids:[kids]).forEach(c=>c!=null&&e.appendChild(typeof c==='string'?document.createTextNode(c):c));return e;}\nlet CUR='USD';                 \/\/ US destination \u2014 single currency, no toggle\nconst FX={USD:1};              \/\/ data stored and displayed in USD\nfunction curSym(){return '$';}\nfunction money(v){v=v*FX[CUR];const s=v<0?'-':'';v=Math.abs(v);const c=curSym();\n  if(v>=1e9)return s+c+(v\/1e9).toFixed(v>=1e10?0:2).replace(\/\\.0+$\/,'')+'B';\n  if(v>=1e6)return s+c+(v\/1e6).toFixed(v>=1e7?1:2).replace(\/\\.?0+$\/,'')+'M';\n  if(v>=1e3)return s+c+(v\/1e3).toFixed(0)+'K';\n  return s+c+Math.round(v);}\nfunction money2(v){return curSym()+Math.round(v*FX[CUR]).toLocaleString();}\nfunction num(v){if(v>=1e6)return (v\/1e6).toFixed(v>=1e7?1:2).replace(\/\\.?0+$\/,'')+'M';if(v>=1e3)return (v\/1e3).toFixed(v>=1e4?0:1).replace(\/\\.0$\/,'')+'K';return Math.round(v).toLocaleString();}\nfunction pct(v,d=0){return (v*100).toFixed(d)+'%';}\n\nconst TT=$('#tt');\nfunction showTT(html,e){TT.innerHTML=html;TT.style.opacity=1;moveTT(e);}\nfunction moveTT(e){const p=12;let x=e.clientX+p,y=e.clientY+p;const r=TT.getBoundingClientRect();if(x+r.width>innerWidth-8)x=e.clientX-r.width-p;if(y+r.height>innerHeight-8)y=e.clientY-r.height-p;TT.style.left=x+'px';TT.style.top=y+'px';}\nfunction hideTT(){TT.style.opacity=0;}\n\n\/* ---- Area\/line trend chart with crosshair+tooltip ---- *\/\nfunction areaChart(values,labels,opt={}){\n  const w=opt.w||660,h=opt.h||210,pl=42,pr=16,pt=14,pb=26;\n  const iw=w-pl-pr,ih=h-pt-pb;\n  const max=Math.max(...values)*1.12,min=0;\n  const X=i=>pl+iw*(i\/(values.length-1));\n  const Y=v=>pt+ih*(1-(v-min)\/(max-min));\n  const svg=el('svg',{viewBox:`0 0 ${w} ${h}`,width:'100%',role:'img'});\n  \/\/ gridlines + y ticks\n  const ticks=4;\n  for(let t=0;t<=ticks;t++){const val=max*t\/ticks;const y=Y(val);\n    svg.appendChild(el('line',{x1:pl,x2:w-pr,y1:y,y2:y,stroke:'var(--grid)','stroke-width':1}));\n    svg.appendChild(el('text',{x:pl-8,y:y+3.5,'text-anchor':'end','font-size':10,fill:'var(--muted)'},money(val)));\n  }\n  \/\/ area path\n  let d='M'+X(0)+' '+Y(values[0]);values.forEach((v,i)=>{if(i)d+=' L'+X(i)+' '+Y(v);});\n  let area=d+` L${X(values.length-1)} ${Y(0)} L${X(0)} ${Y(0)} Z`;\n  const gid='g'+Math.random().toString(36).slice(2,7);\n  const defs=el('defs');const lg=el('linearGradient',{id:gid,x1:0,y1:0,x2:0,y2:1});\n  lg.appendChild(el('stop',{offset:'0%','stop-color':'var(--azure)','stop-opacity':.28}));\n  lg.appendChild(el('stop',{offset:'100%','stop-color':'var(--azure)','stop-opacity':.02}));\n  defs.appendChild(lg);svg.appendChild(defs);\n  svg.appendChild(el('path',{d:area,fill:`url(#${gid})`}));\n  svg.appendChild(el('path',{d,fill:'none',stroke:'var(--azure)','stroke-width':2.4,'stroke-linejoin':'round','stroke-linecap':'round'}));\n  \/\/ x labels (every other)\n  labels.forEach((lb,i)=>{if(i%2===0||i===labels.length-1)svg.appendChild(el('text',{x:X(i),y:h-8,'text-anchor':'middle','font-size':10,fill:'var(--muted)'},lb));});\n  \/\/ end dot\n  svg.appendChild(el('circle',{cx:X(values.length-1),cy:Y(values.at(-1)),r:4.5,fill:'var(--azure)',stroke:'var(--surface)','stroke-width':2}));\n  \/\/ crosshair layer\n  const cx=el('line',{x1:0,x2:0,y1:pt,y2:pt+ih,stroke:'var(--azure)','stroke-width':1,'stroke-dasharray':'3 3',opacity:0});\n  const cdot=el('circle',{r:5,fill:'var(--azure)',stroke:'var(--surface)','stroke-width':2,opacity:0});\n  svg.appendChild(cx);svg.appendChild(cdot);\n  const hit=el('rect',{x:pl,y:pt,width:iw,height:ih,fill:'transparent'});\n  hit.addEventListener('mousemove',ev=>{\n    const rect=svg.getBoundingClientRect();const rel=(ev.clientX-rect.left)\/rect.width*w;\n    let i=Math.round((rel-pl)\/iw*(values.length-1));i=Math.max(0,Math.min(values.length-1,i));\n    cx.setAttribute('x1',X(i));cx.setAttribute('x2',X(i));cx.setAttribute('opacity',.6);\n    cdot.setAttribute('cx',X(i));cdot.setAttribute('cy',Y(values[i]));cdot.setAttribute('opacity',1);\n    showTT(`<div class=\"tt-t\">${labels[i]} 2026<\/div><div class=\"tt-r\">Economic impact <b>${money(values[i])}<\/b><\/div>`,ev);\n  });\n  hit.addEventListener('mouseleave',()=>{cx.setAttribute('opacity',0);cdot.setAttribute('opacity',0);hideTT();});\n  svg.appendChild(hit);\n  return svg;\n}\n\n\/* ---- horizontal ranked stacked bars (tax local+state, per location) ---- *\/\nfunction rankedStacked(rows,opt={}){\n  \/\/ rows: [{label, parts:[{v,color,name}], total, sub, onClick}]\n  const fmt=opt.fmt||money, tfmt=opt.tfmt||money2;\n  const w=opt.w||620,rowH=34,gap=10,pl=opt.pl||120,pr=opt.pr||64,pt=6;\n  const h=pt+rows.length*(rowH+gap);\n  const max=Math.max(...rows.map(r=>r.total))*1.02;\n  const iw=w-pl-pr;\n  const svg=el('svg',{viewBox:`0 0 ${w} ${h}`,width:'100%'});\n  rows.forEach((r,ri)=>{\n    const y=pt+ri*(rowH+gap);\n    svg.appendChild(el('text',{x:pl-10,y:y+rowH\/2+4,'text-anchor':'end','font-size':12,'font-weight':600,fill:'var(--text)'},r.label));\n    let x=pl;\n    r.parts.forEach((p,pi)=>{\n      const bw=iw*(p.v\/max);\n      const g=el('g',{class:'barseg',style:'cursor:pointer'});\n      const rc=el('rect',{x:x,y:y,width:Math.max(0,bw-2),height:rowH,rx:(pi===r.parts.length-1?6:0),fill:p.color});\n      g.appendChild(rc);\n      g.addEventListener('mousemove',ev=>showTT(`<div class=\"tt-t\">${r.label}<\/div><div class=\"tt-r\">${p.name} <b>${tfmt(p.v)}<\/b><\/div>`,ev));\n      g.addEventListener('mouseleave',hideTT);\n      if(r.onClick)g.addEventListener('click',r.onClick);\n      svg.appendChild(g);x+=bw;\n    });\n    svg.appendChild(el('text',{x:x+8,y:y+rowH\/2+4,'font-size':12.5,'font-weight':700,fill:'var(--text)'},fmt(r.total)));\n  });\n  return svg;\n}\n\n\/* ---- donut for partner mix ---- *\/\nfunction donut(parts,opt={}){ \/\/ parts:[{v,color,name}]\n  const size=opt.size||190,r=size\/2,rin=r*0.62,cx=r,cy=r;\n  const tot=parts.reduce((a,p)=>a+p.v,0);let a0=-Math.PI\/2;\n  const svg=el('svg',{viewBox:`0 0 ${size} ${size}`,width:size,height:size});\n  parts.forEach(p=>{\n    const a1=a0+ (p.v\/tot)*Math.PI*2;\n    const x0=cx+r*Math.cos(a0),y0=cy+r*Math.sin(a0),x1=cx+r*Math.cos(a1),y1=cy+r*Math.sin(a1);\n    const xi0=cx+rin*Math.cos(a0),yi0=cy+rin*Math.sin(a0),xi1=cx+rin*Math.cos(a1),yi1=cy+rin*Math.sin(a1);\n    const large=(a1-a0)>Math.PI?1:0;\n    const d=`M${x0} ${y0} A${r} ${r} 0 ${large} 1 ${x1} ${y1} L${xi1} ${yi1} A${rin} ${rin} 0 ${large} 0 ${xi0} ${yi0} Z`;\n    const path=el('path',{d,fill:p.color,stroke:'var(--surface)','stroke-width':2,style:'cursor:pointer;transition:.15s'});\n    path.addEventListener('mousemove',ev=>showTT(`<div class=\"tt-t\">${p.name}<\/div><div class=\"tt-r\">Impact <b>${money(p.v)}<\/b><\/div><div class=\"tt-r\">Share <b>${pct(p.v\/tot,0)}<\/b><\/div>`,ev));\n    path.addEventListener('mouseleave',hideTT);\n    if(p.onClick)path.addEventListener('click',p.onClick);\n    svg.appendChild(path);a0=a1;\n  });\n  svg.appendChild(el('text',{x:cx,y:cy-3,'text-anchor':'middle','font-size':20,'font-weight':700,fill:'var(--text)'},money(tot)));\n  svg.appendChild(el('text',{x:cx,y:cy+14,'text-anchor':'middle','font-size':10.5,fill:'var(--muted)'},'partner impact'));\n  return svg;\n}\n\n\/* ---- stacked column: direct vs influenced by location ---- *\/\nfunction stackedCols(rows,opt={}){ \/\/ rows:[{label,direct,influenced,onClick}]\n  const w=opt.w||620,h=opt.h||230,pl=42,pr=10,pt=12,pb=34;\n  const iw=w-pl-pr,ih=h-pt-pb,n=rows.length;\n  const max=Math.max(...rows.map(r=>r.direct+r.influenced))*1.14;\n  const bw=Math.min(30,iw\/n*0.52),slot=iw\/n;\n  const Y=v=>pt+ih*(1-v\/max);\n  const svg=el('svg',{viewBox:`0 0 ${w} ${h}`,width:'100%'});\n  for(let t=0;t<=4;t++){const val=max*t\/4,y=Y(val);svg.appendChild(el('line',{x1:pl,x2:w-pr,y1:y,y2:y,stroke:'var(--grid)','stroke-width':1}));svg.appendChild(el('text',{x:pl-8,y:y+3.5,'text-anchor':'end','font-size':10,fill:'var(--muted)'},money(val)));}\n  rows.forEach((r,i)=>{\n    const cx=pl+slot*i+slot\/2, x=cx-bw\/2;\n    const hD=ih*r.direct\/max, hI=ih*r.influenced\/max;\n    const yD=Y(r.direct), yI=Y(r.direct+r.influenced);\n    const gI=el('rect',{x,y:yI,width:bw,height:Math.max(0,hI-1),rx:5,fill:'var(--s-influenced)',style:'cursor:pointer'});\n    const gD=el('rect',{x,y:yD,width:bw,height:hD,rx:0,fill:'var(--s-direct)',style:'cursor:pointer'});\n    [ ['Direct',r.direct,gD],['Influenced',r.influenced,gI] ].forEach(([nm,v,g])=>{\n      g.addEventListener('mousemove',ev=>showTT(`<div class=\"tt-t\">${r.label}<\/div><div class=\"tt-r\">${nm} <b>${money(v)}<\/b><\/div><div class=\"tt-r\">Total <b>${money(r.direct+r.influenced)}<\/b><\/div>`,ev));\n      g.addEventListener('mouseleave',hideTT);if(r.onClick)g.addEventListener('click',r.onClick);\n    });\n    svg.appendChild(gI);svg.appendChild(gD);\n    svg.appendChild(el('text',{x:cx,y:h-19,'text-anchor':'middle','font-size':10,'font-weight':600,fill:'var(--text-2)'},r.label));\n    svg.appendChild(el('text',{x:cx,y:h-8,'text-anchor':'middle','font-size':9.5,fill:'var(--muted)'},money(r.direct+r.influenced)));\n  });\n  return svg;\n}\n<\/script>\n\n<!-- =================== AI NARRATIVE ENGINE =================== -->\n<script id=\"ai\">\n\/* Lightweight on-device narrative generator. Reads the derived model and writes\n   the dollars-first, council-ready sentence a director takes into a budget meeting.\n   (In production this is the TrueImpact AI summarizer over the live attribution feed.) *\/\nfunction aiOverview(k){\n  const m=rollup(k), locs=LOCS();\n  const best=[...locs].sort((a,b)=>deriveMetrics(b,k).total-deriveMetrics(a,k).total)[0];\n  const rate=[...locs].sort((a,b)=>b.interactionRate-a.interactionRate)[0];\n  return `Across <b>${m.kiosks} kiosks<\/b> and <b>${SCREENS(locs)} digital signage screens<\/b> in <b>${locs.length} Aspen Snowmass deployment zones<\/b>, TrueImpact projects <span class=\"em\">${money(m.total)}<\/span> in visitor-driven economic impact and <span class=\"em\">${money(m.tax)}<\/span> in estimated public tax this period &mdash; from just <b>${num(m.interactions)}<\/b> interactions, or <b>${money(m.total\/m.interactions)}<\/b> of impact per interaction. <b>${best.name}<\/b> leads on impact; <b>${rate.name}<\/b> engages the highest share of visitors at <b>${pct(rate.interactionRate)}<\/b>. That value density is the point: this is a high-value, low-volume destination, so depth of engagement matters more than footfall. Figures are MODELED from customer-supplied Aspen visitor-economy anchors and TrueOmni platform benchmarks; at go-live every dollar is ledgered to a live kiosk event. An estimated <span class=\"em\">${money(m.recirc)}<\/span> re-circulates back through the local economy. Signage impressions are reach and are excluded from these figures.`;\n}\nfunction aiTax(k){\n  const m=rollup(k),b=m.taxBreak,locs=LOCS();\n  const top=Object.entries(b).sort((x,y)=>y[1]-x[1])[0];\n  const nm={hotelTOT:'the local lodging tax',salesTax:'city, county &amp; RTA sales tax',rentalTax:'the ground-transport surcharge',stateTax:'Colorado state sales tax'}[top[0]];\n  const hi=[...locs].sort((a,b)=>b.tax.tot-a.tax.tot)[0], lo=[...locs].sort((a,b)=>a.tax.tot-b.tax.tot)[0];\n  const cmp = hi.tax.tot!==lo.tax.tot ? ` Because rates are set locally, the same visitor is worth more in <b>${hi.name} (${hi.tax.tot}% TOT)<\/b> than in <b>${lo.name} (${lo.tax.tot}%)<\/b>.` : '';\n  return `Attributed visitor spending is projected to generate an estimated <span class=\"em\">${money(m.tax)}<\/span> in tax this period \u2014 <b>${money(m.taxLocal)}<\/b> in local revenue (lodging tax plus city, county and RTA sales tax) that stays in Aspen, Snowmass Village and Pitkin County, and <b>${money(m.taxState)}<\/b> in Colorado state sales tax. The single largest line is <b>${nm}<\/b> at <span class=\"em\">${money(top[1])}<\/span>. Note the shape of this: Aspen's lodging-tax <i>rate<\/i> is low by resort standards, but the <i>spend<\/i> it applies to is not, so local sales tax on high-value visitor spending carries most of the return. Re-spent through public budgets, local tax drives an estimated <span class=\"em\">${money(m.recirc)}<\/span> of induced community activity \u2014 a soft benefit on top of the hard capture.`;\n}\nfunction aiLocation(L,k){\n  const m=deriveMetrics(L,k),r=L.tax;\n  return `<b>${L.name}<\/b> is projected to turn <b>${num(m.interactions)}<\/b> kiosk interactions into <span class=\"em\">${money(m.total)}<\/span> of economic impact and <span class=\"em\">${money(m.tax)}<\/span> of tax (lodging ${r.tot}%, local sales ${r.sales}%, Colorado state 2.9%) \u2014 <b>${money(m.total\/m.interactions)}<\/b> of impact per interaction. ${L.note} Partners captured <b>${money(Object.values(m.partners).reduce((a,b)=>a+b,0))}<\/b>, led by ${SEG[Object.entries(m.partners).sort((a,b)=>b[1]-a[1])[0][0]].name.toLowerCase()}.`;\n}\nfunction aiNetwork(k){\n  const {net}=networkAgg(k);\n  return `Across the full TrueOmni network \u2014 <b>${net.destinations} destinations<\/b> and <b>${num(net.kiosks)} kiosks<\/b> in North America and Japan \u2014 the platform attributed <span class=\"em\">${money(net.total)}<\/span> of visitor-driven economic impact and <span class=\"em\">${money(net.tax)}<\/span> in estimated public tax revenue this period. This is the first time the destination industry has an aggregate impact number built from <b>ledgered first-party behavioral data<\/b> rather than modeled surveys. Every destination below drills down to its own kiosks.`;\n}\nfunction aiEngagement(rows, mode, scopeName){\n  const attn=[...rows].sort((a,b)=>b.interactions-a.interactions)[0];\n  const val=[...rows].sort((a,b)=>b.impact-a.impact)[0];\n  const invest= mode==='campaigns'\n      ? [...rows].filter(r=>r.roas).sort((a,b)=>b.roas-a.roas)[0]\n      : [...rows].sort((a,b)=>b.valPerK-a.valPerK)[0];\n  const gap = attn.id!==val.id\n      ? ` The gap is the story: <b>${attn.name}<\/b> draws the most attention, but <b>${val.name}<\/b> converts it into the most attributed dollars \u2014 so raw attention isn't the same as value.`\n      : ` <b>${attn.name}<\/b> both draws the most attention and drives the most dollars.`;\n  const investLine = mode==='campaigns'\n      ? ` For spend, <b>${invest.name}<\/b> returns the best ROI at <span class=\"em\">${invest.roas.toFixed(1)}\u00d7<\/span> \u2014 invest here first.`\n      : ` <b>${invest.name}<\/b> punches above its attention (${money(invest.valPerK)} attributed per 1K interactions) \u2014 the efficient place to feature and cross-promote.`;\n  return `In <b>${scopeName}<\/b>, the ${mode==='campaigns'?'campaigns':'kiosk modules'} split the funnel from exposure to attributed conversion.${gap}${investLine} Rank by any funnel stage using the metric toggle; filter by market to see how the mix shifts location to location.`;\n}\nfunction aiPartner(k){\n  const m=rollup(k),tot=Object.values(m.partners).reduce((a,b)=>a+b,0);\n  const top=Object.entries(m.partners).sort((a,b)=>b[1]-a[1])[0];\n  return `TrueImpact attributed <span class=\"em\">${money(tot)}<\/span> of visitor spending to partner businesses \u2014 hotels, restaurants, attractions and event venues \u2014 every dollar tied to a kiosk referral, QR scan, pass pull or partner-POS event. <b>${SEG[top[0]].name}<\/b> captured the most at <span class=\"em\">${money(top[1])}<\/span>. This is the number a partner sees in their own portal, and the reason ${'>'}30% upgrade to premium membership within 18 months.`;\n}\nfunction aiBanner(tag,html){\n  return `<div class=\"ai-banner fade\"><div class=\"ai-ic\"><svg viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M12 3l1.9 4.6L18 9l-4.1 1.4L12 15l-1.9-4.6L6 9l4.1-1.4zM19 14l.9 2.2L22 17l-2.1.8L19 20l-.9-2.2L16 17l2.1-.8z\" fill=\"#fff\"\/><\/svg><\/div><div><span class=\"ai-tag\">${tag}<\/span><div class=\"ai-txt\">${html}<\/div><\/div><\/div>`;\n}\n<\/script>\n\n<!-- =================== APP \/ ROUTER \/ PAGES =================== -->\n<script id=\"app-logic\">\nlet PERIOD='t12';\nlet engMode='modules', engLoc='all', engMetric='interactions';   \/\/ engagement drill-through filters\nconst app=$('#app');\nfunction K(){return PERIODS[PERIOD].k;}\nfunction periodLabel(){return PERIODS[PERIOD].label;}\n\n\/* ---------- helpers to build DOM cards ---------- *\/\nfunction h(html){const t=document.createElement('template');t.innerHTML=html.trim();return t.content.firstChild;}\nfunction card(title,hint,bodyNodes,cls=''){\n  const c=h(`<div class=\"card ${cls}\"><div class=\"card-h\"><div><h3>${title}${hint?` <span class=\"hint\">${hint}<\/span>`:''}<\/h3><\/div><\/div><\/div>`);\n  (Array.isArray(bodyNodes)?bodyNodes:[bodyNodes]).forEach(n=>n&&c.appendChild(typeof n==='string'?h(n):n));\n  return c;\n}\nfunction legend(items){const l=h('<div class=\"legend\"><\/div>');items.forEach(it=>l.appendChild(h(`<span class=\"li\"><span class=\"sw ${it.line?'line':''}\" style=\"background:${it.color}\"><\/span>${it.name}<\/span>`)));return l;}\nfunction shortName(n){return n.replace(' Lake','').replace(' Valley','').replace(' Bay','').replace('Fort ','Ft ').replace(' Springs','').replace('San Francisco','SF').replace('Los Angeles','LA').replace('Magnificent Mile','Mag Mile').replace('McCormick Place','McCormick').replace('Museum Campus','Museum').split(' (')[0];}\nfunction engShort(n){return n.replace(\/\\s*\\(.*\\)\/,'').replace('Interactive Map & Wayfinding','Map & Wayfinding').replace(' Placements','').replace('Local ','').replace('-Season Getaway','-Season').replace(' & Coupons','').replace(' Push','').replace(' Welcome','').replace(' & Restaurants','').replace(' to Do & Attractions',' to Do').replace(' Builder','').replace(' Calendar','').replace(' & Parking','');}\nfunction setDest(id){activeDest=id;engLoc='all';location.hash='#\/overview';render();}\nfunction engMiniCard(k){\n  const sc=engScope(k); const rows=splitFunnel(sc.m, MODULES, sc.seg).sort((a,b)=>b.interactions-a.interactions).slice(0,5);\n  const max=rows[0].interactions;\n  const c=card('Module &amp; campaign engagement','what draws attention \u2014 drill the funnel','',' ');\n  const list=h('<div class=\"loclist\"><\/div>');\n  rows.forEach(r=>{const row=h(`<div class=\"locrow\" style=\"grid-template-columns:1fr auto\"><div><div class=\"lr-name\" style=\"display:flex;align-items:center;gap:7px\"><span class=\"sw\" style=\"width:11px;height:11px;border-radius:3px;background:${SEG[r.tag].color}\"><\/span>${r.name}<\/div><\/div><div class=\"lr-val\">${num(r.interactions)}<div class=\"lr-sub\" style=\"font-weight:600\">${money(r.impact)}<\/div><\/div><div class=\"lr-bar\"><i style=\"width:${(r.interactions\/max*100).toFixed(0)}%\"><\/i><\/div><\/div>`);\n    row.addEventListener('click',()=>go('#\/modules'));list.appendChild(row);});\n  c.appendChild(list);\n  const btns=h('<div style=\"display:flex;gap:8px;margin-top:12px\"><\/div>');\n  btns.appendChild(h(`<button class=\"chip\" style=\"cursor:pointer\" onclick=\"go('#\/modules')\">Modules \u203a<\/button>`));\n  btns.appendChild(h(`<button class=\"chip\" style=\"cursor:pointer\" onclick=\"go('#\/campaigns')\">Campaigns &amp; A\/B tests \u203a<\/button>`));\n  c.appendChild(btns);\n  return c;\n}\n\n\/* ================= OVERVIEW ================= *\/\nfunction pageOverview(){\n  const k=K(),m=rollup(k);\n  const wrap=h('<div class=\"fade\"><\/div>');\n  wrap.appendChild(h(`<div class=\"pagehead\"><div><div class=\"eyebrow\">${ACT().kind} \u00b7 Executive Dashboard<\/div><h1>${ACT().name} \u2014 ${ACT().scope} Impact<\/h1><\/div><div class=\"meta\">Reporting period<br><b style=\"color:var(--text);font-size:14px\">${periodLabel()}<\/b><br>${m.kiosks} kiosks \u00b7 ${SCREENS()} screens \u00b7 ${LOCS().length} zones<\/div><\/div>`));\n  wrap.appendChild(h(`<div class=\"crumbs\"><span>${ACT().name}<\/span><span class=\"sep\">\u203a<\/span><a onclick=\"go('#\/overview')\">${ACT().region}<\/a><span class=\"sep\">\u203a<\/span><span>All zones<\/span><\/div>`));\n  wrap.appendChild(destContextCard()); wrap.lastChild.style.marginBottom='16px';\n  wrap.appendChild(h(aiBanner('TrueImpact AI \u00b7 Council-ready summary',aiOverview(k))));\n  wrap.appendChild(rampCard(k)); wrap.lastChild.style.marginBottom='16px';\n\n  \/\/ KPI band\n  const kpis=h('<div class=\"grid kpis\" style=\"margin-bottom:16px\"><\/div>');\n  kpis.appendChild(h(`<div class=\"card kpi hero\"><div class=\"glow\"><\/div><div class=\"k-label\">Total economic impact<\/div><div class=\"k-val\"><span class=\"cur\">${curSym()}<\/span>${money(m.total).replace(curSym(),'')}<\/div><div class=\"k-delta up\">\u25b2 18.4% vs prior period<\/div><div class=\"k-foot\">Direct ${money(m.direct)} + Influenced ${money(m.influenced)} \u00b7 dollars-first, ledger-level<\/div><\/div>`));\n  kpis.appendChild(kpiTile('Estimated tax revenue',money(m.tax),'\u25b2 16.1%',`Local ${money(m.taxLocal)} \u00b7 State of Colorado ${money(m.taxState)}`,()=>go('#\/tax')));\n  kpis.appendChild(kpiTile('Partner-captured spend',money(Object.values(m.partners).reduce((a,b)=>a+b,0)),'\u25b2 21.0%','Hotels \u00b7 dining \u00b7 attractions \u00b7 events',()=>go('#\/partners')));\n  kpis.appendChild(kpiTile('Trackable visitor actions',num(m.interactions),'\u25b2 12.7%',`${num(m.conversions)} attributed conversions`));\n  wrap.appendChild(kpis);\n\n  \/\/ Map + location list\n  const mapRow=h('<div class=\"grid row-map\" style=\"margin-bottom:16px\"><\/div>');\n  mapRow.appendChild(card('Impact by kiosk location','click a market to drill in',[buildMap(k),legend([{name:'Bubble size = economic impact',color:'var(--azure)'}])],'pad0'+' '));\n  const mapCard=mapRow.lastChild;mapCard.style.padding='18px';\n  mapRow.appendChild(card('Zones ranked','by economic impact',[buildLocList(k)]));\n  wrap.appendChild(mapRow);\n\n  \/\/ Funnel + Direct\/influenced by location\n  const r2=h('<div class=\"grid row-2\" style=\"margin-bottom:16px\"><\/div>');\n  r2.appendChild(card('The visitor funnel',ACT().scope.toLowerCase(),[buildFunnel(m),h('<div class=\"note\">Exposure \u2192 interaction \u2192 high-intent action \u2192 attributed conversion. Rates are TrueOmni platform benchmarks; volumes are ledgered kiosk events.<\/div>')]));\n  const dcols=stackedCols(LOCS().map(L=>{const mm=deriveMetrics(L,k);return{label:shortName(L.name),direct:mm.direct,influenced:mm.influenced,onClick:()=>go('#\/location\/'+L.id)};}));\n  r2.appendChild(card('Direct vs influenced impact','by market \u00b7 click to drill',[dcols,legend([{name:'Direct (attributed spend)',color:'var(--s-direct)'},{name:'Influenced (supply-chain + wage tail)',color:'var(--s-influenced)'}])]));\n  wrap.appendChild(r2);\n\n  \/\/ Partner mix + tax preview\n  const r3=h('<div class=\"grid row-2\" style=\"margin-bottom:16px\"><\/div>');\n  r3.appendChild(buildPartnerCard(m,k));\n  r3.appendChild(buildTaxPreview(m,k));\n  wrap.appendChild(r3);\n\n  \/\/ Engagement entry\n  wrap.appendChild(engMiniCard(k));\n  wrap.lastChild.style.marginBottom='16px';\n\n  \/\/ Visitor value entry\n  wrap.appendChild(valueMiniCard(k));\n  wrap.lastChild.style.marginBottom='16px';\n\n  \/\/ Media Network entry\n  wrap.appendChild(mediaMiniCard(k));\n  wrap.lastChild.style.marginBottom='16px';\n\n  \/\/ Trend\n  const trend=card('Economic impact trend','monthly \u00b7 '+periodLabel(),[areaChart(rollupSeries(k),MONTHS,{h:220}),h('<div class=\"note\">Aspen draws <b>1.1M+ visitors<\/b> spending <b>$1.7B<\/b> directly &mdash; about <b>$1,545 per visitor<\/b>, roughly 2.3&times; a high-volume destination. Against that base this 10-kiosk network is modeled to encounter ~430,000 visitors a year, about 39% of visitation, concentrated in the town core and the base villages where nearly every guest passes. <b>Look at the shape of this curve, not its height.<\/b> The Dec&ndash;Mar ski peak and the Jul&ndash;Aug summer peak are separated by two brutal troughs &mdash; April&ndash;May and October run at roughly a third of peak. Those troughs are the single largest commercial opportunity in the model: shoulder-season automation and an always-on AI concierge are worth an estimated 8&ndash;12% during off-peak weeks, and they cost nothing extra to run.<\/div>')]);\n  wrap.appendChild(trend);\n  return wrap;\n}\nfunction rampCard(k){\n  const c=card('Deployment ramp \u00b7 5 \u2192 10 kiosks + 5 screens','projected run-rate as zones come online (90\u2013120 day rollout)','',' ');\n  const list=h('<div class=\"loclist\"><\/div>');\n  let prevK=0;\n  PHASES.forEach(ph=>{\n    const locs=LOCS().filter(L=>ph.ids.includes(L.id));\n    const m=rollup(k,locs); const add=m.kiosks-prevK; prevK=m.kiosks;\n    const row=h(`<div class=\"locrow\"><div><div class=\"lr-name\">${ph.name}<\/div><div class=\"lr-sub\">${ph.window} \u00b7 ${m.kiosks} kiosks${add?` (+${add})`:''} \u00b7 ${SCREENS(locs)} screens live<\/div><\/div><div class=\"lr-val\">${money(m.total)}<div class=\"lr-sub\" style=\"font-weight:600\">${money(m.tax)} tax \u00b7 ${money(m.taxLocal)} local<\/div><\/div><div class=\"lr-bar\"><i style=\"width:${(m.kiosks\/NETWORK_KIOSKS*100).toFixed(0)}%\"><\/i><\/div><\/div>`);\n    list.appendChild(row);\n  });\n  c.appendChild(list);\n  c.appendChild(h('<div class=\"note\">Cumulative annualized run-rate at each phase. Phase 1 launches in the downtown Aspen core and at the Silver Queen gondola plaza (highest dwell and a captive lift-queue audience, fastest proof); Snowmass Base Village and the Village Mall follow; Highlands and the Buttermilk\/ASE arrivals corridor complete the network. Figures scale with units online \\u2014 they are not a forecast of new demand.<\/div>'));\n  return c;\n}\nfunction kpiTile(label,val,delta,foot,onClick){\n  const c=h(`<div class=\"card kpi\"${onClick?' style=\"cursor:pointer\"':''}><div class=\"k-label\">${label}<\/div><div class=\"k-val\">${val}<\/div><div class=\"k-delta up\">\u25b2 ${delta.replace('\u25b2','').trim()}<\/div><div class=\"k-foot\">${foot}<\/div><\/div>`);\n  if(onClick)c.addEventListener('click',onClick);\n  return c;\n}\n\n\/* ---- Destination map (pixel-scale viewBox so marks\/labels stay true size).\n   Reads the active destination's outline + each location's x,y. Works for any tenant. ---- *\/\nfunction buildMap(k){\n  const D=ACT(), locs=LOCS();\n  const svg=el('svg',{viewBox:'0 0 480 620',role:'img','aria-label':D.region+' kiosk map'});\n  if(D.lake) svg.appendChild(el('path',{d:D.lake,fill:'var(--s-hotels)','fill-opacity':.12,stroke:'none'}));\n  svg.appendChild(el('path',{d:D.outline,class:'ca-outline'}));\n  if(D.lakeLabel) svg.appendChild(el('text',{x:D.lakeLabel.x,y:D.lakeLabel.y,'text-anchor':'middle','font-size':12,'font-style':'italic',fill:'var(--muted)',stroke:'none'},D.lakeLabel.t));\n  const max=Math.max(...locs.map(L=>deriveMetrics(L,k).total));\n  locs.forEach(L=>{\n    const mm=deriveMetrics(L,k);const rr=9+18*Math.sqrt(mm.total\/max);\n    const px=L.x,py=L.y;\n    const g=el('g',{class:'mk'+(state.loc===L.id?' active':''),transform:`translate(${px},${py})`});\n    g.appendChild(el('circle',{class:'ring',r:rr+5}));\n    g.appendChild(el('circle',{class:'pin',r:rr,fill:'var(--azure)','fill-opacity':.85}));\n    g.appendChild(el('circle',{class:'hit',r:Math.max(rr+7,16)}));\n    if(rr>16)g.appendChild(el('text',{y:4,'text-anchor':'middle','font-size':11,fill:'#fff',stroke:'none'},money(mm.total)));\n    g.appendChild(el('text',{y:rr+15,'text-anchor':'middle','font-size':11.5,'font-weight':700},L.name));\n    g.addEventListener('mousemove',ev=>showTT(`<div class=\"tt-t\">${L.name}<\/div><div class=\"tt-r\">Impact <b>${money(mm.total)}<\/b><\/div><div class=\"tt-r\">Tax <b>${money(mm.tax)}<\/b><\/div><div class=\"tt-r\">Interaction <b>${pct(L.interactionRate)}<\/b><\/div>`,ev));\n    g.addEventListener('mouseleave',hideTT);\n    g.addEventListener('click',()=>go('#\/location\/'+L.id));\n    svg.appendChild(g);\n  });\n  const box=h('<div class=\"mapwrap\"><\/div>');box.appendChild(svg);return box;\n}\nfunction buildLocList(k){\n  const rows=[...LOCS()].map(L=>({L,m:deriveMetrics(L,k)})).sort((a,b)=>b.m.total-a.m.total);\n  const max=rows[0].m.total;\n  const box=h('<div class=\"loclist\"><\/div>');\n  rows.forEach(({L,m})=>{\n    const row=h(`<div class=\"locrow\"><div><div class=\"lr-name\">${L.name} ${L.badge?`<span class=\"pill tag-hi\">${L.badge}<\/span>`:''}<\/div><div class=\"lr-sub\">${L.sites} \u00b7 ${L.kiosks} kiosks<\/div><\/div><div class=\"lr-val\">${money(m.total)}<div class=\"lr-sub\" style=\"font-weight:600\">${money(m.tax)} tax<\/div><\/div><span class=\"chev\">\u203a<\/span><div class=\"lr-bar\"><i style=\"width:${(m.total\/max*100).toFixed(0)}%\"><\/i><\/div><\/div>`);\n    row.addEventListener('click',()=>go('#\/location\/'+L.id));\n    box.appendChild(row);\n  });\n  return box;\n}\nfunction buildFunnel(m){\n  const stages=[\n    {n:'Kiosk exposures',v:m.exposures,rate:''},\n    {n:'Interactions',v:m.interactions,rate:'of exposed'},\n    {n:'High-intent actions',v:m.highIntent,rate:pct(FUNNEL.highIntent)+' of interactions'},\n    {n:'Attributed conversions',v:m.conversions,rate:pct(FUNNEL.conversion)+' of high-intent'},\n  ];\n  const max=stages[0].v;const cols=['#0E2240','#14508C','#1C9CD8','#33abe0'];\n  const box=h('<div class=\"funnel\"><\/div>');\n  stages.forEach((s,i)=>{\n    const w=26+40*(s.v\/max);\n    const st=h(`<div class=\"fstage\"><div class=\"fbar\" style=\"width:${w}%;background:${cols[i]}\">${s.n}<\/div><div class=\"fmeta\"><b>${num(s.v)}<\/b> <span class=\"frate\">${s.rate}<\/span><\/div><\/div>`);\n    box.appendChild(st);\n  });\n  return box;\n}\nfunction buildPartnerCard(m,k){\n  const parts=Object.entries(m.partners).map(([id,v])=>({v,name:SEG[id].name,color:SEG[id].color,onClick:()=>go('#\/partners')}));\n  const c=card('Partner economic impact','hotels \u00b7 dining \u00b7 attractions \u00b7 events','',' ');\n  const inner=h('<div style=\"display:flex;gap:20px;align-items:center;flex-wrap:wrap\"><\/div>');\n  inner.appendChild(donut(parts));\n  const list=h('<div style=\"flex:1;min-width:200px\"><\/div>');\n  parts.sort((a,b)=>b.v-a.v).forEach(p=>{\n    const row=h(`<div class=\"locrow\" style=\"grid-template-columns:1fr auto\"><div><div class=\"lr-name\" style=\"display:flex;align-items:center;gap:7px\"><span class=\"sw\" style=\"width:11px;height:11px;border-radius:3px;background:${p.color}\"><\/span>${p.name}<\/div><\/div><div class=\"lr-val\">${money(p.v)}<\/div><\/div>`);\n    row.addEventListener('click',()=>go('#\/partners'));list.appendChild(row);\n  });\n  inner.appendChild(list);\n  c.appendChild(inner);\n  c.appendChild(h('<div class=\"note\">This is the figure a partner sees in their own portal \u2014 every dollar tied to a kiosk referral, QR scan, pass pull, or partner-POS event. <span class=\"toggle-tbl\" onclick=\"go(\\'#\/partners\\')\">Open partner detail \u203a<\/span><\/div>'));\n  return c;\n}\nfunction buildTaxPreview(m,k){\n  const b=m.taxBreak;\n  const parts=[\n    {v:b.hotelTOT,name:'Local lodging tax',color:'var(--s-local)'},\n    {v:b.salesTax,name:'City, county & RTA sales tax',color:'#33abe0'},\n    {v:b.rentalTax,name:'Ground transport',color:'#7cc9e8'},\n    {v:b.stateTax,name:'Colorado state sales tax',color:'var(--s-state)'},\n  ];\n  const c=card('Estimated tax revenue','locality-based \u00b7 click to drill','',' ');\n  const rows=parts.map(p=>({label:p.name,parts:[{v:p.v,color:p.color,name:p.name}],total:p.v,onClick:()=>go('#\/tax')}));\n  c.appendChild(rankedStacked(rows,{pl:130,w:560}));\n  c.appendChild(h(`<div class=\"gauge-track\"><i style=\"width:${(m.recirc\/(m.recirc+m.tax)*100).toFixed(0)}%\"><\/i><\/div>`));\n  c.appendChild(h(`<div class=\"note\"><b style=\"color:var(--text)\">${money(m.recirc)}<\/b> soft benefit \u2014 estimated local re-circulation of these public tax dollars through community budgets & payroll. <span class=\"toggle-tbl\" onclick=\"go('#\/tax')\">Full tax breakdown \u203a<\/span><\/div>`));\n  return c;\n}\n\n\/* ================= LOCATION DETAIL ================= *\/\nfunction pageLocation(id){\n  const L=findLoc(id);if(!L)return pageOverview();\n  const k=K(),m=deriveMetrics(L,k),r=L.tax;\n  const wrap=h('<div class=\"fade\"><\/div>');\n  wrap.appendChild(h(`<a class=\"backbtn\" onclick=\"go('#\/overview')\"><svg viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M15 18l-6-6 6-6\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/><\/svg> All ${ACT().region} markets<\/a>`));\n  wrap.appendChild(h(`<div class=\"crumbs\"><span>${ACT().name}<\/span><span class=\"sep\">\u203a<\/span><a onclick=\"go('#\/overview')\">${ACT().region}<\/a><span class=\"sep\">\u203a<\/span><span>${L.name}<\/span><\/div>`));\n  wrap.appendChild(h(`<div class=\"pagehead\"><div><div class=\"eyebrow\">Kiosk market${L.badge?` \u00b7 <span style=\"color:var(--good)\">${L.badge}<\/span>`:''}<\/div><h1>${L.name}<\/h1><div class=\"ch-sub\" style=\"margin-top:4px;color:var(--text-2)\">${L.sites} \u00b7 ${L.kiosks} kiosks \u00b7 ${L.screens||0} screens \u00b7 ${periodLabel()}<\/div><\/div><div class=\"meta\">Tax rates<br><b style=\"color:var(--text);font-size:13px\">Lodging ${r.tot}% \u00b7 local sales ${r.sales}% \u00b7 CO ${CO_STATE_SALES}%<\/b><\/div><\/div>`));\n  wrap.appendChild(h(aiBanner('TrueImpact AI \u00b7 Market read',aiLocation(L,k))));\n\n  const kpis=h('<div class=\"grid kpis\" style=\"margin-bottom:16px\"><\/div>');\n  kpis.appendChild(h(`<div class=\"card kpi hero\"><div class=\"glow\"><\/div><div class=\"k-label\">Economic impact<\/div><div class=\"k-val\"><span class=\"cur\">${curSym()}<\/span>${money(m.total).replace(curSym(),'')}<\/div><div class=\"k-foot\">Direct ${money(m.direct)} + Influenced ${money(m.influenced)}<\/div><\/div>`));\n  kpis.appendChild(kpiTile('Estimated tax revenue',money(m.tax),'',`Local ${money(m.taxLocal)} \u00b7 State of Colorado ${money(m.taxState)}`,()=>go('#\/tax')));\n  kpis.appendChild(kpiTile('Partner-captured spend',money(Object.values(m.partners).reduce((a,b)=>a+b,0)),'','across 4 segments',()=>go('#\/partners')));\n  kpis.appendChild(kpiTile('Interaction rate',pct(L.interactionRate),'',`${num(m.interactions)} interactions`));\n  wrap.appendChild(kpis);\n\n  const r2=h('<div class=\"grid row-2\" style=\"margin-bottom:16px\"><\/div>');\n  r2.appendChild(card('Visitor funnel','this market',[buildFunnel(m)]));\n  r2.appendChild(buildLocTaxCard(m,L));\n  wrap.appendChild(r2);\n\n  const r3=h('<div class=\"grid row-2\" style=\"margin-bottom:16px\"><\/div>');\n  const parts=Object.entries(m.partners).map(([pid,v])=>({v,name:SEG[pid].name,color:SEG[pid].color}));\n  const pc=card('Partner impact \u2014 '+L.name,'who captured the visitor dollar','',' ');\n  const pin=h('<div style=\"display:flex;gap:20px;align-items:center;flex-wrap:wrap\"><\/div>');\n  pin.appendChild(donut(parts));\n  const pl=h('<div style=\"flex:1;min-width:200px\"><\/div>');\n  parts.sort((a,b)=>b.v-a.v).forEach(p=>pl.appendChild(h(`<div class=\"locrow\" style=\"grid-template-columns:1fr auto\"><div class=\"lr-name\" style=\"display:flex;align-items:center;gap:7px\"><span class=\"sw\" style=\"width:11px;height:11px;border-radius:3px;background:${p.color}\"><\/span>${p.name}<\/div><div class=\"lr-val\">${money(p.v)}<\/div><\/div>`)));\n  pin.appendChild(pl);pc.appendChild(pin);r3.appendChild(pc);\n  r3.appendChild(card('Monthly impact','seasonality',[areaChart(seriesFor(L,k),MONTHS,{h:210})]));\n  wrap.appendChild(r3);\n\n  \/\/ sample named partners\n  wrap.appendChild(buildSamplePartners(L,m));\n  return wrap;\n}\nfunction buildLocTaxCard(m,L){\n  const b=m.taxBreak,r=L.tax;\n  const rows=[\n    {label:`Local lodging tax (${r.tot}%)`,parts:[{v:b.hotelTOT,color:'var(--s-local)',name:'Local lodging tax \u2014 funds the destination'}],total:b.hotelTOT},\n    {label:`City, county &amp; RTA sales tax (${r.sales}%)`,parts:[{v:b.salesTax,color:'#33abe0',name:'Local sales tax'}],total:b.salesTax},\n    {label:`Ground transport (${r.rental}%)`,parts:[{v:b.rentalTax,color:'#7cc9e8',name:'Ground-transport surcharge'}],total:b.rentalTax},\n    {label:`Colorado state sales tax (${CO_STATE_SALES}%)`,parts:[{v:b.stateTax,color:'var(--s-state)',name:'Colorado state sales tax'}],total:b.stateTax},\n  ];\n  const c=card('Estimated tax revenue','locality-based','',' ');\n  c.appendChild(rankedStacked(rows,{pl:150,w:560}));\n  c.appendChild(h(`<div class=\"note\">Rates are set by this jurisdiction \u2014 which is why a visitor here is worth a different tax yield than in any other market. <b style=\"color:var(--text)\">${money(m.recirc)}<\/b> estimated re-circulation (soft benefit).<\/div>`));\n  return c;\n}\nfunction buildSamplePartners(L,m){\n  const SAMPLES={\n    la:['JW Marriott L.A. LIVE','Grand Central Market','Griffith Observatory','Crypto.com Arena'],\n    sf:['Hotel Nikko','Ferry Building Marketplace','Alcatraz Cruises','Moscone Center'],\n    sd:['Hotel del Coronado','Little Italy Food Hall','San Diego Zoo','Petco Park'],\n    ana:['Anaheim Marriott','Downtown Disney Dining','Disneyland Resort','Angel Stadium'],\n    bb:['The Lodge at Big Bear','Peppercorn Grille','Big Bear Mountain Resort','Village Events'],\n    napa:['Carneros Resort','The French Laundry','Castello di Amorosa','Oxbow Public Market'],\n    ps:['Parker Palm Springs','Workshop Kitchen','Palm Springs Tram','Coachella \/ Stagecoach'],\n    sac:['Citizen Hotel','Old Sacramento Waterfront','California State Railroad Museum','Golden 1 Center'],\n    ord:['Hilton O\u2019Hare','Tortas Frontera','Terminal 5 Retail','United Club'],\n    mag:['The Peninsula Chicago','Purple Pig','360 CHICAGO','Water Tower Place'],\n    navy:['Sable at Navy Pier','Giordano\u2019s','Centennial Wheel','Navy Pier Events'],\n    mcc:['Marriott Marquis Chicago','Wintrust Cafe','Field Museum','McCormick Expo Halls'],\n    loop:['JW Marriott Chicago','The Berghoff','Skydeck Willis Tower','Chicago Theatre'],\n    mus:['Hilton Museum Campus','Shedd Cafe','Shedd Aquarium','Adler After Dark'],\n    wrig:['Hotel Zachary','Gallagher Way Dining','Wrigley Field Tours','Cubs Home Games'],\n    orl:['Hilton Bonnet Creek','Disney Springs Dining','Universal Orlando','OCCC Events'],\n    mia:['Fontainebleau Miami','Joe\u2019s Stone Crab','Vizcaya Museum','Ultra \/ Art Basel'],\n    tpa:['Tampa Marriott Water St','Columbia Restaurant','Busch Gardens','Amalie Arena'],\n    ftl:['W Fort Lauderdale','Casa Sensei','Riverwalk Cruises','Broward Convention'],\n    jax:['Marriott Downtown JAX','Cowford Chophouse','MOSH Museum','VyStar Arena'],\n    nap:['Ritz-Carlton Naples','Campiello','Naples Botanical Garden','5th Ave Events'],\n    key:['Casa Marina Resort','Blue Heaven','Mallory Square Sunset','Fantasy Fest'],\n  };\n  const names=SAMPLES[L.id]||['Hotel','Restaurant','Attraction','Venue'];\n  const segIds=['hotels','dining','attractions','events'];\n  const c=card('Sample attributed partners','illustrative \u2014 top referral recipients','',' ');\n  const tbl=h('<table><thead><tr><th>Partner<\/th><th>Segment<\/th><th>Attributed spend<\/th><th>Referrals<\/th><th>Trend<\/th><\/tr><\/thead><tbody><\/tbody><\/table>');\n  const tb=tbl.querySelector('tbody');\n  segIds.forEach((sid,i)=>{\n    const v=m.partners[sid]*(0.34-i*0.03);\n    const refs=Math.round(m.highIntent*L.mix[sid]*0.4);\n    tb.appendChild(h(`<tr><td><b>${names[i]}<\/b><\/td><td><span class=\"pill tag-mid\">${SEG[sid].name}<\/span><\/td><td class=\"tnum\">${money2(v)}<\/td><td class=\"tnum\">${refs.toLocaleString()}<\/td><td><span class=\"pill tag-hi\">\u25b2 ${(8+i*3)}%<\/span><\/td><\/tr>`));\n  });\n  c.appendChild(tbl);\n  return c;\n}\n\n\/* ================= PARTNER DETAIL ================= *\/\nfunction pagePartners(){\n  const k=K(),m=rollup(k);const tot=Object.values(m.partners).reduce((a,b)=>a+b,0);\n  const wrap=h('<div class=\"fade\"><\/div>');\n  wrap.appendChild(h(`<a class=\"backbtn\" onclick=\"go('#\/overview')\"><svg viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M15 18l-6-6 6-6\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/><\/svg> ${ACT().region} overview<\/a>`));\n  wrap.appendChild(h(`<div class=\"crumbs\"><a onclick=\"go('#\/overview')\">${ACT().region}<\/a><span class=\"sep\">\u203a<\/span><span>Partner economic impact<\/span><\/div>`));\n  wrap.appendChild(h(`<div class=\"pagehead\"><div><div class=\"eyebrow\">${ACT().region} \u00b7 Partner economic impact<\/div><h1>What the visitor dollar did for partners<\/h1><\/div><div class=\"meta\">${periodLabel()}<br><b style=\"color:var(--text)\">${money(tot)}<\/b> attributed<\/div><\/div>`));\n  wrap.appendChild(h(aiBanner('TrueImpact AI \u00b7 Partner read',aiPartner(k))));\n\n  \/\/ segment cards\n  const icons={hotels:'M3 21V9l9-6 9 6v12M9 21v-6h6v6',dining:'M6 3v7m0 0a3 3 0 006 0V3M6 10v11M18 3c-2 0-3 2-3 5s1 4 3 4v9',attractions:'M3 21l9-18 9 18M8 21v-5h8v5',events:'M3 8h18M3 8l2-4h14l2 4M5 8v12h14V8'};\n  const cards=h('<div class=\"seg-cards\" style=\"margin-bottom:16px\"><\/div>');\n  Object.entries(m.partners).sort((a,b)=>b[1]-a[1]).forEach(([id,v])=>{\n    const c=h(`<div class=\"seg-card\"><div class=\"sc-top\"><span class=\"sc-ic\" style=\"background:${SEG[id].color}\"><svg viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"${icons[id]}\" stroke=\"#fff\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/><\/svg><\/span>${SEG[id].name}<\/div><div class=\"sc-val\">${money(v)}<\/div><div class=\"sc-sub\">${pct(v\/tot,0)} of partner impact<\/div><div class=\"sc-count\"><span>Est. partners<\/span><span>${Math.round(400*SEG_SHARE(id))}<\/span><\/div><\/div>`);\n    cards.appendChild(c);\n  });\n  wrap.appendChild(cards);\n\n  \/\/ impact by segment across locations (stacked ranked)\n  const rows=[...LOCS()].map(L=>{const mm=deriveMetrics(L,k);const p=mm.partners;const t=Object.values(p).reduce((a,b)=>a+b,0);\n    return {label:L.name,total:t,onClick:()=>go('#\/location\/'+L.id),parts:['hotels','dining','attractions','events'].map(s=>({v:p[s],color:SEG[s].color,name:SEG[s].name}))};\n  }).sort((a,b)=>b.total-a.total);\n  const c1=card('Partner impact by market','stacked by segment \u00b7 click a bar to open the market','',' ');\n  c1.appendChild(rankedStacked(rows,{pl:120,w:660}));\n  c1.appendChild(legend(Object.keys(SEG).map(id=>({name:SEG[id].name,color:SEG[id].color}))));\n  wrap.appendChild(c1);\n\n  \/\/ why it matters\n  wrap.appendChild(card('Why partners upgrade','the portal effect',[h(`<div class=\"stat-inline\" style=\"margin-top:6px\"><div class=\"si\"><b>+15\u201330%<\/b><span>itinerary inclusion lift<\/span><\/div><div class=\"si\"><b>+20\u201340%<\/b><span>listing exposure<\/span><\/div><div class=\"si\"><b>+10\u201325%<\/b><span>pass participation<\/span><\/div><div class=\"si\"><b>&gt;30%<\/b><span>upgrade to premium in 18 mo<\/span><\/div><\/div>`),h('<div class=\"note\">The Partner ROI view is the single most-requested feature in the 2026 customer survey. When a restaurant or hotel sees its own attributed dollars, flat-dues membership becomes performance-aligned membership.<\/div>')]));\n  return wrap;\n}\nfunction SEG_SHARE(id){return {hotels:.34,dining:.30,attractions:.22,events:.14}[id];}\n\n\/* ================= TAX DETAIL ================= *\/\nfunction pageTax(){\n  const k=K(),m=rollup(k),b=m.taxBreak;\n  const wrap=h('<div class=\"fade\"><\/div>');\n  wrap.appendChild(h(`<a class=\"backbtn\" onclick=\"go('#\/overview')\"><svg viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M15 18l-6-6 6-6\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/><\/svg> ${ACT().region} overview<\/a>`));\n  wrap.appendChild(h(`<div class=\"crumbs\"><a onclick=\"go('#\/overview')\">${ACT().region}<\/a><span class=\"sep\">\u203a<\/span><span>Estimated tax revenue<\/span><\/div>`));\n  wrap.appendChild(h(`<div class=\"pagehead\"><div><div class=\"eyebrow\">${ACT().region} \u00b7 Public revenue impact<\/div><h1>Estimated tax revenue by zone<\/h1><\/div><div class=\"meta\">${periodLabel()}<br><b style=\"color:var(--text)\">${money(m.tax)}<\/b> total est.<\/div><\/div>`));\n  wrap.appendChild(h(aiBanner('TrueImpact AI \u00b7 Tax read',aiTax(k))));\n\n  \/\/ KPI tiles by tax type\n  const kpis=h('<div class=\"grid kpis\" style=\"margin-bottom:16px\"><\/div>');\n  kpis.appendChild(h(`<div class=\"card kpi hero\"><div class=\"glow\"><\/div><div class=\"k-label\">Total estimated tax revenue<\/div><div class=\"k-val\"><span class=\"cur\">${curSym()}<\/span>${money(m.tax).replace(curSym(),'')}<\/div><div class=\"k-foot\">Local ${money(m.taxLocal)} + State of Colorado ${money(m.taxState)}<\/div><\/div>`));\n  kpis.appendChild(kpiTile('Local lodging tax',money(b.hotelTOT),'','Aspen 2.0% \u00b7 Snowmass Village 2.4%'));\n  kpis.appendChild(kpiTile('Local sales tax',money(b.salesTax),'','city\/town + Pitkin County + RTA'));\n  kpis.appendChild(kpiTile('Colorado state sales tax',money(b.stateTax),'','2.9% state general fund'));\n  wrap.appendChild(kpis);\n\n  \/\/ stacked tax by location (local components + state)\n  const rows=[...LOCS()].map(L=>{const mm=deriveMetrics(L,k),bb=mm.taxBreak;\n    return {label:L.name,total:mm.tax,onClick:()=>go('#\/location\/'+L.id),parts:[\n      {v:bb.hotelTOT,color:'var(--s-local)',name:'Local lodging tax'},\n      {v:bb.salesTax,color:'#33abe0',name:'City, county & RTA sales tax'},\n      {v:bb.rentalTax,color:'#7cc9e8',name:'Ground transport'},\n      {v:bb.stateTax,color:'var(--s-state)',name:'Colorado state sales tax'},\n    ]};}).sort((a,b)=>b.total-a.total);\n  const c1=card('Estimated tax revenue by zone','lodging tax \u00b7 local sales tax \u00b7 ground transport \u00b7 Colorado state sales tax \u00b7 click a bar to drill','',' ');\n  c1.appendChild(rankedStacked(rows,{pl:120,w:680}));\n  c1.appendChild(legend([{name:'Local lodging tax',color:'var(--s-local)'},{name:'City, county & RTA sales tax',color:'#33abe0'},{name:'Ground transport',color:'#7cc9e8'},{name:'Colorado state sales tax',color:'var(--s-state)'}]));\n  wrap.appendChild(c1);\n\n  \/\/ recirculation soft benefit\n  const recircCard=card('Recirculation \u2014 the soft benefit','induced community activity from public tax dollars','',' ');\n  recircCard.appendChild(h(`<div class=\"stat-inline\" style=\"margin-top:4px\"><div class=\"si\"><b>${money(m.tax)}<\/b><span>hard tax captured<\/span><\/div><div class=\"si\"><b style=\"color:var(--good)\">${money(m.recirc)}<\/b><span>est. local re-circulation<\/span><\/div><div class=\"si\"><b>${money(m.tax+m.recirc)}<\/b><span>combined public benefit<\/span><\/div><\/div>`));\n  recircCard.appendChild(h(`<div class=\"gauge-track\" style=\"margin-top:14px\"><i style=\"width:${(m.recirc\/(m.tax+m.recirc)*100).toFixed(0)}%\"><\/i><\/div>`));\n  recircCard.appendChild(h('<div class=\"note\">Public revenue does not leave the community \u2014 it funds payroll, services, and local procurement that are re-spent through the regional economy. Modeled conservatively at 0.9\u00d7 of captured tax (induced\/soft). This is the number that turns \"we captured tax\" into \"we grew the local economy.\"<\/div>'));\n  wrap.appendChild(recircCard);\n\n  \/\/ full data table\n  const c2=card('Full tax detail','auditable line items \u00b7 '+periodLabel(),'',' ');\n  const tbl=h('<table><thead><tr><th>Zone<\/th><th>Lodging %<\/th><th>Lodging tax<\/th><th>Local sales<\/th><th>Ground transp.<\/th><th>CO state<\/th><th>Total tax<\/th><th>Recirc (soft)<\/th><\/tr><\/thead><tbody><\/tbody><\/table>');\n  const tb=tbl.querySelector('tbody');\n  rows.forEach(r=>{const L=LOCS().find(x=>x.name===r.label);const mm=deriveMetrics(L,k),bb=mm.taxBreak,rt=L.tax;\n    const tr=h(`<tr><td><b>${L.name}<\/b><\/td><td class=\"tnum\">${rt.tot}%<\/td><td class=\"tnum\">${money2(bb.hotelTOT)}<\/td><td class=\"tnum\">${money2(bb.salesTax)}<\/td><td class=\"tnum\">${money2(bb.rentalTax)}<\/td><td class=\"tnum\">${money2(bb.stateTax)}<\/td><td class=\"tnum\"><b>${money2(mm.tax)}<\/b><\/td><td class=\"tnum\" style=\"color:var(--good)\">${money2(mm.recirc)}<\/td><\/tr>`);\n    tr.addEventListener('click',()=>go('#\/location\/'+L.id));tb.appendChild(tr);\n  });\n  c2.appendChild(tbl);\n  wrap.appendChild(c2);\n  return wrap;\n}\n\n\/* ================= NETWORK ROLL-UP (all destinations) ================= *\/\nfunction pageNetwork(){\n  const k=K(),{net,parts}=networkAgg(k);\n  const wrap=h('<div class=\"fade\"><\/div>');\n  wrap.appendChild(h(`<div class=\"pagehead\"><div><div class=\"eyebrow\">TrueOmni Network \u00b7 All destinations<\/div><h1>Total economic impact \u2014 every kiosk, every destination<\/h1><\/div><div class=\"meta\">${periodLabel()}<br><b style=\"color:var(--text);font-size:14px\">${net.destinations} destinations \u00b7 ${num(net.kiosks)} kiosks<\/b><\/div><\/div>`));\n  wrap.appendChild(h(`<div class=\"crumbs\"><span>TrueOmni Network<\/span><span class=\"sep\">\u203a<\/span><span>All destinations<\/span><\/div>`));\n  wrap.appendChild(h(aiBanner('TrueImpact AI \u00b7 Network roll-up',aiNetwork(k))));\n\n  const kpis=h('<div class=\"grid kpis\" style=\"margin-bottom:16px\"><\/div>');\n  kpis.appendChild(h(`<div class=\"card kpi hero\"><div class=\"glow\"><\/div><div class=\"k-label\">Network economic impact<\/div><div class=\"k-val\"><span class=\"cur\">${curSym()}<\/span>${money(net.total).replace(curSym(),'')}<\/div><div class=\"k-delta up\">\u25b2 22.6% YoY<\/div><div class=\"k-foot\">Direct ${money(net.direct)} + Influenced ${money(net.influenced)} \u00b7 ${net.destinations} destinations, ledger-level<\/div><\/div>`));\n  kpis.appendChild(kpiTile('Public tax revenue',money(net.tax),'\u25b2 19.4%',`Local ${money(net.taxLocal)} \u00b7 State of Colorado ${money(net.taxState)}`));\n  kpis.appendChild(kpiTile('Partner-captured spend',money(Object.values(net.partners).reduce((a,b)=>a+b,0)),'\u25b2 24.1%','across all destinations'));\n  kpis.appendChild(kpiTile('Trackable visitor actions',num(net.interactions),'\u25b2 15.2%',`${num(net.conversions)} attributed conversions`));\n  wrap.appendChild(kpis);\n\n  \/\/ destinations ranked (real 3 + modeled long-tail)\n  const destRows=NET_IDS.map((id,i)=>({id,name:DESTINATIONS[id].name,region:DESTINATIONS[id].region,kind:DESTINATIONS[id].kind,m:parts[i]}))\n    .sort((a,b)=>b.m.total-a.m.total);\n  const maxD=destRows[0].m.total;\n  const dl=h('<div class=\"loclist\"><\/div>');\n  destRows.forEach(d=>{\n    const row=h(`<div class=\"locrow\"><div><div class=\"lr-name\">${d.name}<\/div><div class=\"lr-sub\">${d.kind} \u00b7 ${d.m.kiosks} kiosks \u00b7 ${d.region}<\/div><\/div><div class=\"lr-val\">${money(d.m.total)}<div class=\"lr-sub\" style=\"font-weight:600\">${money(d.m.tax)} tax<\/div><\/div><span class=\"chev\">\u203a<\/span><div class=\"lr-bar\"><i style=\"width:${(d.m.total\/maxD*100).toFixed(0)}%\"><\/i><\/div><\/div>`);\n    row.addEventListener('click',()=>setDest(d.id));dl.appendChild(row);\n  });\n  const realTot=parts.reduce((a,b)=>a+b.total,0), othersTot=net.total-realTot, othersTax=net.tax-parts.reduce((a,b)=>a+b.tax,0), othersK=net.kiosks-parts.reduce((a,b)=>a+b.kiosks,0);\n  dl.appendChild(h(`<div class=\"locrow\" style=\"opacity:.85;cursor:default\"><div><div class=\"lr-name\">Other ${net.destinations-3} destinations<\/div><div class=\"lr-sub\">North America + Japan \u00b7 ~${num(othersK)} kiosks \u00b7 modeled long-tail<\/div><\/div><div class=\"lr-val\">${money(othersTot)}<div class=\"lr-sub\" style=\"font-weight:600\">${money(othersTax)} tax<\/div><\/div><span class=\"chev\" style=\"opacity:0\">\u203a<\/span><div class=\"lr-bar\"><i style=\"width:${(othersTot\/maxD*100).toFixed(0)}%;background:linear-gradient(90deg,#8b96a5,#5B6675)\"><\/i><\/div><\/div>`));\n\n  const r1=h('<div class=\"grid row-2\" style=\"margin-bottom:16px\"><\/div>');\n  r1.appendChild(card('Destinations ranked','click a live destination to drill into its kiosks',[dl,h('<div class=\"note\">California, Chicago and Florida are live TrueImpact tenants shown from their own kiosk data. The long-tail bucket models the remaining network to the published footprint.<\/div>')]));\n  \/\/ network partner mix\n  const parts2=Object.entries(net.partners).map(([id,v])=>({v,name:SEG[id].name,color:SEG[id].color}));\n  const pc=card('Network partner impact','hotels \u00b7 dining \u00b7 attractions \u00b7 events','',' ');\n  const pin=h('<div style=\"display:flex;gap:20px;align-items:center;flex-wrap:wrap\"><\/div>');\n  pin.appendChild(donut(parts2));\n  const plist=h('<div style=\"flex:1;min-width:190px\"><\/div>');\n  parts2.sort((a,b)=>b.v-a.v).forEach(p=>plist.appendChild(h(`<div class=\"locrow\" style=\"grid-template-columns:1fr auto\"><div class=\"lr-name\" style=\"display:flex;align-items:center;gap:7px\"><span class=\"sw\" style=\"width:11px;height:11px;border-radius:3px;background:${p.color}\"><\/span>${p.name}<\/div><div class=\"lr-val\">${money(p.v)}<\/div><\/div>`)));\n  pin.appendChild(plist);pc.appendChild(pin);\n  r1.appendChild(pc);\n  wrap.appendChild(r1);\n\n  \/\/ engagement entry (network scope)\n  wrap.appendChild(engMiniCard(k));\n  wrap.lastChild.style.marginBottom='16px';\n\n  \/\/ network trend\n  wrap.appendChild(card('Network economic impact trend','monthly \u00b7 all destinations',[areaChart(networkSeries(k),MONTHS,{h:220}),h('<div class=\"note\">The category\\'s first aggregate impact curve built from first-party behavioral data across the network. Each destination re-circulates public tax dollars into its own community \u2014 an estimated '+money(net.recirc)+' network-wide (soft benefit).<\/div>')]));\n  return wrap;\n}\n\n\/* ================= ENGAGEMENT DRILL-THROUGH ================= *\/\nfunction engScope(k){\n  if(activeDest==='network'){const m=networkAgg(k).net;return {m,name:'the TrueOmni Network',seg:segSharesOf(m),locs:[]};}\n  if(engLoc!=='all'){const L=findLoc(engLoc);if(L){const m=deriveMetrics(L,k);return {m,name:L.name,seg:segSharesOf(m),locs:LOCS()};}}\n  const m=rollup(k);return {m,name:ACT().region,seg:segSharesOf(m),locs:LOCS()};\n}\nfunction engMetrics(mode){\n  const base={\n    interactions:{label:'Attention',   get:r=>r.interactions, fmt:num,   tfmt:v=>num(v)+' sessions'},\n    highIntent:  {label:'High-intent',  get:r=>r.highIntent,   fmt:num,   tfmt:v=>num(v)+' actions'},\n    conversions: {label:'Conversions',  get:r=>r.conversions,  fmt:num,   tfmt:v=>num(v)+' conv.'},\n    impact:      {label:'Attributed $', get:r=>r.impact,       fmt:money, tfmt:money2},\n  };\n  if(mode==='modules') base.time={label:'Time spent',get:r=>r.timeHrs,fmt:v=>num(v)+' h',tfmt:v=>num(v)+' hours'};\n  else base.roas={label:'ROI (ROAS)',get:r=>r.roas,fmt:v=>v.toFixed(1)+'\u00d7',tfmt:v=>v.toFixed(2)+'\u00d7 return'};\n  return base;\n}\nfunction setEng(key,val){ if(key==='mode'){engMode=val;engMetric='interactions';} else if(key==='loc'){engLoc=val;} else {engMetric=val;}\n  location.hash = '#\/'+(engMode==='modules'?'modules':'campaigns'); render(); }\nfunction pageEngagement(){\n  const k=K(), sc=engScope(k);\n  const items = engMode==='modules'?MODULES:CAMPAIGNS;\n  const rows = splitFunnel(sc.m, items, sc.seg);\n  const M = engMetrics(engMode);\n  if(!M[engMetric]) engMetric='interactions';\n  const met=M[engMetric];\n  rows.sort((a,b)=>(met.get(b)||0)-(met.get(a)||0));\n  const colorOf=r=> engMode==='modules'?SEG[r.tag].color:CAMP_TYPE[r.type];\n\n  const wrap=h('<div class=\"fade\"><\/div>');\n  wrap.appendChild(h(`<a class=\"backbtn\" onclick=\"go('#\/overview')\"><svg viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M15 18l-6-6 6-6\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/><\/svg> ${activeDest==='network'?'Network':ACT().region} overview<\/a>`));\n  wrap.appendChild(h(`<div class=\"crumbs\"><a onclick=\"go('#\/overview')\">${activeDest==='network'?'TrueOmni Network':ACT().region}<\/a><span class=\"sep\">\u203a<\/span><span>Engagement drill-through<\/span><\/div>`));\n  wrap.appendChild(h(`<div class=\"pagehead\"><div><div class=\"eyebrow\">Marketing \u00b7 Engagement drill-through<\/div><h1>What draws attention \u2014 and what to invest in<\/h1><\/div><div class=\"meta\">${periodLabel()}<br><b style=\"color:var(--text)\">${sc.name}<\/b><\/div><\/div>`));\n  wrap.appendChild(h(aiBanner('TrueImpact AI \u00b7 Investment read',aiEngagement(rows,engMode,sc.name))));\n\n  \/\/ filters\n  const f=h('<div class=\"filters\"><\/div>');\n  f.appendChild(h(`<div class=\"fgroup\"><span class=\"flab\">Dimension<\/span><div class=\"fseg\">\n    <button class=\"${engMode==='modules'?'on':''}\" onclick=\"setEng('mode','modules')\">Modules<\/button>\n    <button class=\"${engMode==='campaigns'?'on':''}\" onclick=\"setEng('mode','campaigns')\">Campaigns<\/button><\/div><\/div>`));\n  if(sc.locs.length){\n    const opts=['<option value=\"all\"'+(engLoc==='all'?' selected':'')+'>All markets<\/option>']\n      .concat(sc.locs.map(L=>`<option value=\"${L.id}\"${engLoc===L.id?' selected':''}>${L.name}<\/option>`)).join('');\n    f.appendChild(h(`<div class=\"fgroup\"><span class=\"flab\">Location<\/span><select class=\"fsel\" onchange=\"setEng('loc',this.value)\">${opts}<\/select><\/div>`));\n  }\n  const mkeys=Object.keys(M);\n  f.appendChild(h(`<div class=\"fgroup\"><span class=\"flab\">Rank by<\/span><div class=\"fseg\">${mkeys.map(mk=>`<button class=\"${mk===engMetric?'on':''}\" onclick=\"setEng('metric','${mk}')\">${M[mk].label}<\/button>`).join('')}<\/div><\/div>`));\n  wrap.appendChild(f);\n\n  \/\/ ranked bar + top funnel\n  const r1=h('<div class=\"grid row-2\" style=\"margin-bottom:16px\"><\/div>');\n  const barRows=rows.map(r=>({label:engShort(r.name),parts:[{v:met.get(r)||0,color:colorOf(r),name:met.label}],total:met.get(r)||0}));\n  const rk=card(`${engMode==='modules'?'Modules':'Campaigns'} ranked by ${met.label.toLowerCase()}`,'stack ranking','',' ');\n  rk.appendChild(rankedStacked(barRows,{pl:150,w:640,pr:74,fmt:met.fmt,tfmt:met.tfmt}));\n  r1.appendChild(rk);\n  \/\/ top item funnel drill-through\n  const top=rows[0];\n  const fc=card('Funnel drill-through','top '+(engMode==='modules'?'module':'campaign')+': '+top.name,[buildEngFunnel(top),h(`<div class=\"note\">Exposures \u2192 interactions \u2192 high-intent \u2192 attributed conversions for <b>${top.name}<\/b>. Conversion rate <b>${pct(top.convRate,1)}<\/b>${top.roas?` \u00b7 ROAS <b>${top.roas.toFixed(1)}\u00d7<\/b>`:''}${top.timeHrs?` \u00b7 <b>${num(top.timeHrs)}<\/b> engaged hours`:''}.<\/div>`)]);\n  r1.appendChild(fc);\n  wrap.appendChild(r1);\n\n  \/\/ full table\n  const tc=card(`${engMode==='modules'?'Module':'Campaign'} funnel \u2014 full breakdown`,'every stage \u00b7 '+periodLabel(),'',' ');\n  tc.appendChild(buildEngTable(rows,engMode,colorOf));\n  wrap.appendChild(tc);\n\n  \/\/ A\/B tests (campaigns only)\n  if(engMode==='campaigns'){\n    const tests=AB_TESTS.map(t=>({t,row:rows.find(r=>r.id===t.camp)})).filter(x=>x.row);\n    const abWrap=card('A\/B campaign tests','conversion rate by variant \u2014 compare & contrast',[h('<div class=\"grid row-2\" id=\"abgrid\" style=\"margin-top:2px\"><\/div>')]);\n    const grid=abWrap.querySelector('#abgrid');\n    tests.forEach(({t,row})=>grid.appendChild(buildABcard(t,row)));\n    wrap.appendChild(abWrap);\n  }\n  return wrap;\n}\nfunction buildEngFunnel(r){\n  const stages=[['Exposures',r.exposures,''],['Interactions',r.interactions,pct(r.interactions\/r.exposures,0)+' of exposed'],\n    ['High-intent',r.highIntent,pct(r.highIntent\/r.interactions,0)+' of interactions'],['Conversions',r.conversions,pct(r.conversions\/r.highIntent,0)+' of high-intent']];\n  const max=stages[0][1],cols=['#0E2240','#14508C','#1C9CD8','#33abe0'];\n  const box=h('<div class=\"funnel\"><\/div>');\n  stages.forEach(([n,v,rate],i)=>box.appendChild(h(`<div class=\"fstage\"><div class=\"fbar\" style=\"width:${26+40*(v\/max)}%;background:${cols[i]}\">${n}<\/div><div class=\"fmeta\"><b>${num(v)}<\/b> <span class=\"frate\">${rate}<\/span><\/div><\/div>`)));\n  return box;\n}\nfunction buildEngTable(rows,mode,colorOf){\n  const head = mode==='modules'\n    ? '<tr><th>Module<\/th><th>Exposures<\/th><th>Interactions<\/th><th>High-intent<\/th><th>Conversions<\/th><th>Conv %<\/th><th>Time<\/th><th>Attributed $<\/th><th>$\/1K int<\/th><\/tr>'\n    : '<tr><th>Campaign<\/th><th>Interactions<\/th><th>High-intent<\/th><th>Conversions<\/th><th>Conv %<\/th><th>Spend<\/th><th>Attributed $<\/th><th>ROAS<\/th><\/tr>';\n  const tbl=h(`<table><thead>${head}<\/thead><tbody><\/tbody><\/table>`);\n  const tb=tbl.querySelector('tbody');\n  rows.forEach(r=>{\n    const dot=`<span class=\"sw\" style=\"display:inline-block;width:10px;height:10px;border-radius:3px;background:${colorOf(r)};margin-right:7px;vertical-align:-1px\"><\/span>`;\n    const conv=`<span class=\"conv-pill\">${pct(r.convRate,1)}<\/span>`;\n    if(mode==='modules'){\n      tb.appendChild(h(`<tr><td>${dot}<b>${r.name}<\/b><\/td><td class=\"tnum\">${num(r.exposures)}<\/td><td class=\"tnum\">${num(r.interactions)}<\/td><td class=\"tnum\">${num(r.highIntent)}<\/td><td class=\"tnum\">${num(r.conversions)}<\/td><td>${conv}<\/td><td class=\"tnum\">${num(r.timeHrs)} h<\/td><td class=\"tnum\"><b>${money(r.impact)}<\/b><\/td><td class=\"tnum\">${money(r.valPerK)}<\/td><\/tr>`));\n    } else {\n      tb.appendChild(h(`<tr><td>${dot}<b>${r.name}<\/b><div class=\"ab-sub\" style=\"margin-left:17px\">${r.type}<\/div><\/td><td class=\"tnum\">${num(r.interactions)}<\/td><td class=\"tnum\">${num(r.highIntent)}<\/td><td class=\"tnum\">${num(r.conversions)}<\/td><td>${conv}<\/td><td class=\"tnum\">${money(r.spend)}<\/td><td class=\"tnum\"><b>${money(r.impact)}<\/b><\/td><td class=\"tnum\"><span class=\"pill ${r.roas>=4?'tag-hi':'tag-mid'}\">${r.roas.toFixed(1)}\u00d7<\/span><\/td><\/tr>`));\n    }\n  });\n  return tbl;\n}\nfunction buildABcard(t,row){\n  \/\/ split the campaign row's traffic; blended conv rate must equal row.convRate\n  const base=row.convRate, rA=base\/(t.split + (1-t.split)*t.bLift), rB=rA*t.bLift;\n  const intA=row.interactions*t.split, intB=row.interactions*(1-t.split);\n  const cvA=intA*rA, cvB=intB*rB;\n  const impA=row.impact*(cvA\/(cvA+cvB)), impB=row.impact*(cvB\/(cvA+cvB));\n  const winnerB=t.bLift>1, decisive=t.conf>=0.9;\n  const maxR=Math.max(rA,rB);\n  const badge = t.status==='No winner yet' ? `<span class=\"ab-badge ab-none\">Running \u00b7 ${pct(t.conf,0)} conf.<\/span>`\n    : `<span class=\"ab-badge ab-win\">${t.status} \u00b7 ${pct(t.conf,0)} conf.<\/span>`;\n  const lift=(rB\/rA-1);\n  const c=h(`<div class=\"ab-card\"><\/div>`);\n  c.appendChild(h(`<div class=\"ab-head\"><div><div class=\"abt\">${t.name}<\/div><div class=\"abs\">${row.name} \u00b7 ${num(row.interactions)} sessions split ${Math.round(t.split*100)}\/${Math.round((1-t.split)*100)}<\/div><\/div>${badge}<\/div>`));\n  const variant=(tag,label,r,cv,imp,isWin)=>`\n    <div class=\"ab-var\">\n      <div class=\"ab-tag\" style=\"background:${isWin?'var(--good)':'var(--slate)'}\">${tag}<\/div>\n      <div><div class=\"ab-nm\">${label} ${isWin?'<span class=\"conv-pill\" style=\"background:rgba(12,163,12,.14);color:var(--good)\">winner<\/span>':''}<\/div><div class=\"ab-sub\">${num(cv)} conversions \u00b7 ${money(imp)} attributed<\/div><\/div>\n      <div class=\"ab-conv\"><b>${pct(r,2)}<\/b><span>conv. rate<\/span><\/div>\n      <div class=\"ab-bar\"><i style=\"width:${(r\/maxR*100).toFixed(0)}%;background:${isWin?'var(--good)':'var(--slate)'}\"><\/i><\/div>\n    <\/div>`;\n  c.appendChild(h(variant('A',t.a,rA,cvA,impA,!winnerB)));\n  c.appendChild(h(variant('B',t.b,rB,cvB,impB,winnerB)));\n  c.appendChild(h(`<div class=\"ab-foot\"><span>Variant B vs A<\/span><span><b style=\"color:${lift>=0?'var(--good)':'var(--warn)'}\">${lift>=0?'+':''}${pct(lift,1)}<\/b> conversion-rate lift<\/span><\/div>`));\n  return c;\n}\n\n\/* ================= MEDIA NETWORK ================= *\/\nfunction aiMedia(k){const m=mediaMetrics(k); const sg=signageMetrics(k);\n  return `At the planned <b>${m.kiosks}-kiosk + ${sg?sg.screens:0}-screen<\/b> network, TrueImpact projects <span class=\"em\">${money(m.rev)}<\/span> a year in advertising revenue at the mid scenario (range ${money(m.band.floor)}\u2013${money(m.band.strong)}), from an estimated <b>${num(m.impressions)}<\/b> ad impressions \u2014 an effective CPM near <b>${money(m.cpm)}<\/b>, in line with digital out-of-home. About <b>${pct(MEDIA.streamSplit.direct)}<\/b> is direct\/native local sales, the rest filled programmatically through Google Ad Manager. That covers the platform's annual fee many times over and repays the hardware in roughly <b>${((MEDIA.capital+(sg?sg.capital:0))\/(m.rev+(sg?sg.rev:0))).toFixed(1)} years<\/b> once digital signage is included \u2014 the self-funding growth loop. Signage adds <b>${sg?money(sg.rev):'$0'}<\/b> and <b>${sg?num(sg.impressions):0}<\/b> broadcast impressions on top, taking total advertising revenue to <span class=\"em\">${money(m.rev+(sg?sg.rev:0))}<\/span>. Screen impressions are reach, not interactions, and are excluded from the economic-impact and tax figures.`;}\nfunction mediaMiniCard(k){const m=mediaMetrics(k); const sg=signageMetrics(k);\n  const c=card('Media Network \u00b7 advertising revenue','kiosk media + digital signage \u2014 the self-funding layer','',' ');\n  const tR=m.rev+(sg?sg.rev:0), tC=MEDIA.capital+(sg?sg.capital:0);\n  c.appendChild(h(`<div class=\"stat-inline\" style=\"margin-top:2px\"><div class=\"si\"><b>${money(tR)}<\/b><span>total ad revenue \/ yr (mid)<\/span><\/div><div class=\"si\"><b>${money(m.rev)}<\/b><span>kiosk media<\/span><\/div><div class=\"si\"><b>${money(sg?sg.rev:0)}<\/b><span>digital signage (${sg?sg.screens:0} screens)<\/span><\/div><div class=\"si\"><b>${num(m.impressions+(sg?sg.impressions:0))}<\/b><span>combined impressions \/ yr<\/span><\/div><div class=\"si\"><b style=\"color:var(--good)\">${(tC\/tR).toFixed(1)} yr<\/b><span>capital payback<\/span><\/div><\/div>`));\n  c.appendChild(h(`<div style=\"display:flex;gap:8px;margin-top:12px\"><button class=\"chip\" style=\"cursor:pointer\" onclick=\"go('#\/media')\">Open Media Network \u203a<\/button><\/div>`));\n  return c;\n}\nfunction pageMedia(){\n  const k=K(); const m=mediaMetrics(k);\n  const wrap=h('<div class=\"fade\"><\/div>');\n  wrap.appendChild(h(`<a class=\"backbtn\" onclick=\"go('#\/overview')\"><svg viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M15 18l-6-6 6-6\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/><\/svg> ${ACT().region} overview<\/a>`));\n  wrap.appendChild(h(`<div class=\"crumbs\"><span>${ACT().name}<\/span><span class=\"sep\">\u203a<\/span><a onclick=\"go('#\/overview')\">${ACT().region}<\/a><span class=\"sep\">\u203a<\/span><span>Media Network<\/span><\/div>`));\n  wrap.appendChild(h(`<div class=\"pagehead\"><div><div class=\"eyebrow\">Advertising \u00b7 Media Network<\/div><h1>Media Network \u2014 kiosk + signage advertising revenue<\/h1><div class=\"ch-sub\" style=\"margin-top:4px;color:var(--text-2)\">Native direct-sold + Google Ad Manager programmatic \u00b7 ${periodLabel()}<\/div><\/div><div class=\"meta\">Network<br><b style=\"color:var(--text);font-size:14px\">${m.kiosks} kiosks \u00b7 ${SCREENS()} screens<\/b><br>${pct(MEDIA.fillMature)} kiosk fill at maturity<\/div><\/div>`));\n  wrap.appendChild(h(aiBanner('TrueImpact AI \u00b7 Media read', aiMedia(k))));\n\n  const kpis=h('<div class=\"grid kpis\" style=\"margin-bottom:16px\"><\/div>');\n  kpis.appendChild(h(`<div class=\"card kpi hero\"><div class=\"glow\"><\/div><div class=\"k-label\">Annual ad-revenue potential<\/div><div class=\"k-val\"><span class=\"cur\">${curSym()}<\/span>${money(m.rev).replace(curSym(),'')}<\/div><div class=\"k-delta up\">\u25b2 scales with fill + kiosks<\/div><div class=\"k-foot\">Range ${money(m.band.floor)}\u2013${money(m.band.strong)} \u00b7 $700\u20132,400 \/ kiosk \/ mo \u00b7 premium luxury market<\/div><\/div>`));\n  kpis.appendChild(kpiTile('Ad impressions \/ yr',num(m.impressions),'','kiosk visualizations across containers'));\n  kpis.appendChild(kpiTile('Effective CPM',money(m.cpm),'','in line with DOOH benchmarks'));\n  kpis.appendChild(kpiTile('Covers annual software',(m.rev\/MEDIA.softwareYr).toFixed(0)+'\u00d7','',`vs ${money(MEDIA.softwareYr)}\/yr platform fee`));\n  wrap.appendChild(kpis);\n\n  const r2=h('<div class=\"grid row-2\" style=\"margin-bottom:16px\"><\/div>');\n  const invCard=card('Kiosk revenue by inventory type','container-level potential (Omni Media Network)','',' ');\n  const list=h('<div class=\"loclist\"><\/div>'); const maxv=Math.max(...m.byInv.map(x=>x.v));\n  m.byInv.slice().sort((a,b)=>b.v-a.v).forEach(x=>{list.appendChild(h(`<div class=\"locrow\" style=\"grid-template-columns:1fr auto\"><div class=\"lr-name\">${x.name}<\/div><div class=\"lr-val\">${money(x.v)}<div class=\"lr-sub\" style=\"font-weight:600\">${pct(x.share)}<\/div><\/div><div class=\"lr-bar\"><i style=\"width:${(x.v\/maxv*100).toFixed(0)}%\"><\/i><\/div><\/div>`));});\n  invCard.appendChild(list); r2.appendChild(invCard);\n  const streamCard=card('Direct vs programmatic','native sales + Google Ad Manager','',' ');\n  const parts=[{v:m.byStream.direct,name:'Direct \/ native sold',color:'var(--s-hotels)'},{v:m.byStream.programmatic,name:'Programmatic (GAM \/ DTN)',color:'var(--s-events)'}];\n  const pin=h('<div style=\"display:flex;gap:20px;align-items:center;flex-wrap:wrap\"><\/div>'); pin.appendChild(donut(parts));\n  const pl=h('<div style=\"flex:1;min-width:180px\"><\/div>'); parts.forEach(p=>pl.appendChild(h(`<div class=\"locrow\" style=\"grid-template-columns:1fr auto\"><div class=\"lr-name\" style=\"display:flex;align-items:center;gap:7px\"><span class=\"sw\" style=\"width:11px;height:11px;border-radius:3px;background:${p.color}\"><\/span>${p.name}<\/div><div class=\"lr-val\">${money(p.v)}<\/div><\/div>`))); pin.appendChild(pl);\n  streamCard.appendChild(pin); r2.appendChild(streamCard);\n  wrap.appendChild(r2);\n\n  const rampC=card('Kiosk ad-revenue ramp \u00b7 5 \u2192 10 kiosks','builds with kiosks online and sell-through','',' ');\n  const rl=h('<div class=\"loclist\"><\/div>');\n  PHASES.forEach(ph=>{const locs=LOCS().filter(L=>ph.ids.includes(L.id));const mm=mediaMetrics(k,locs);const fill=MEDIA.fillByPhase[ph.n];const rev=mm.rev*(fill\/MEDIA.fillMature);\n    rl.appendChild(h(`<div class=\"locrow\"><div><div class=\"lr-name\">${ph.name}<\/div><div class=\"lr-sub\">${ph.window} \u00b7 ${mm.kiosks} kiosks \u00b7 ${pct(fill)} fill<\/div><\/div><div class=\"lr-val\">${money(rev)}<div class=\"lr-sub\" style=\"font-weight:600\">\/yr run-rate<\/div><\/div><div class=\"lr-bar\"><i style=\"width:${(mm.kiosks\/NETWORK_KIOSKS*100).toFixed(0)}%\"><\/i><\/div><\/div>`));});\n  rampC.appendChild(rl);\n  rampC.appendChild(h('<div class=\"note\">Revenue scales with kiosks online and with sell-through as impressions grow. Local partners anchor direct sales from day one; programmatic (Google Ad Manager) fills remnant inventory once impression volume attracts national demand.<\/div>'));\n  wrap.appendChild(rampC);\n\n\n  \/\/ ---------- DIGITAL SIGNAGE (broadcast screens) ----------\n  const sg=signageMetrics(k);\n  if(sg && sg.screens){\n    const sHead=card('Digital Signage \u00b7 broadcast screen network','OMNI Signage \u2014 reach and ad inventory; screens broadcast, they do not transact','',' ');\n    sHead.appendChild(h(`<div class=\"stat-inline\" style=\"margin-top:4px\"><div class=\"si\"><b>${sg.screens}<\/b><span>screens deployed<\/span><\/div><div class=\"si\"><b>${num(sg.impressions)}<\/b><span>screen impressions \/ yr<\/span><\/div><div class=\"si\"><b>${num(sg.reach)}<\/b><span>annual reach (passers-by)<\/span><\/div><div class=\"si\"><b>${SIGNAGE.dwellSec}s<\/b><span>avg dwell \/ attention<\/span><\/div><div class=\"si\"><b>${sg.spotsPerLoop}<\/b><span>spots per ${SIGNAGE.loopSec}s loop<\/span><\/div><div class=\"si\"><b>${pct(sg.shareOfVoice)}<\/b><span>share of voice \/ advertiser<\/span><\/div><div class=\"si\"><b>${money(sg.rev)}<\/b><span>signage ad revenue \/ yr<\/span><\/div><div class=\"si\"><b>${money(sg.cpm)}<\/b><span>effective CPM<\/span><\/div><\/div>`));\n    sHead.appendChild(h(`<div class=\"note\"><b>Signage impressions are reach, not interactions.<\/b> They are deliberately excluded from attributed visitor spend, economic impact and tax \u2014 those stay kiosk-derived. Signage fill ramps ${pct(SIGNAGE.fillByPhase[1])} \u2192 ${pct(SIGNAGE.fillMature)} as inventory matures. Revenue modeled at ${money2(SIGNAGE.perScreenMo.mid)}\/screen\/month at mature fill (range ${money2(SIGNAGE.perScreenMo.floor)}\u2013${money2(SIGNAGE.perScreenMo.strong)}) \u2014 set above the platform benchmark to reflect a luxury advertiser base. MODELED.<\/div>`));\n    wrap.appendChild(sHead); wrap.lastChild.style.marginBottom='16px';\n\n    const s2=h('<div class=\"grid row-2\" style=\"margin-bottom:16px\"><\/div>');\n    const fmtCard=card('Signage revenue by format','base-village hero \u00b7 portrait \u00b7 conditions \u00b7 menu \u00b7 outdoor','',' ');\n    const fl=h('<div class=\"loclist\"><\/div>'); const fmax=Math.max(...sg.byFormat.map(x=>x.v))||1;\n    sg.byFormat.slice().sort((a,b)=>b.v-a.v).forEach(x=>{fl.appendChild(h(`<div class=\"locrow\" style=\"grid-template-columns:1fr auto\"><div class=\"lr-name\">${x.name}<\/div><div class=\"lr-val\">${money(x.v)}<div class=\"lr-sub\" style=\"font-weight:600\">${pct(x.share)}<\/div><\/div><div class=\"lr-bar\"><i style=\"width:${(x.v\/fmax*100).toFixed(0)}%\"><\/i><\/div><\/div>`));});\n    fmtCard.appendChild(fl); s2.appendChild(fmtCard);\n\n    const zCard=card('Screens by zone','where the broadcast inventory sits','',' ');\n    const zl=h('<div class=\"loclist\"><\/div>'); const zmax=Math.max(...LOCS().map(L=>L.screens||0))||1;\n    LOCS().slice().sort((a,b)=>(b.screens||0)-(a.screens||0)).forEach(L=>{zl.appendChild(h(`<div class=\"locrow\"><div><div class=\"lr-name\">${L.name}<\/div><div class=\"lr-sub\">${L.kiosks} kiosks \u00b7 ${L.screens||0} screens \u00b7 Phase ${L.phase}<\/div><\/div><div class=\"lr-val\">${L.screens||0}<div class=\"lr-sub\" style=\"font-weight:600\">screens<\/div><\/div><div class=\"lr-bar\"><i style=\"width:${((L.screens||0)\/zmax*100).toFixed(0)}%\"><\/i><\/div><\/div>`));});\n    zCard.appendChild(zl); s2.appendChild(zCard);\n    wrap.appendChild(s2);\n\n    const totAd=m.rev+sg.rev;\n    const mixCard=card('Total Advertising Revenue','kiosk media + digital signage','',' ');\n    mixCard.appendChild(h(`<div class=\"stat-inline\" style=\"margin-top:4px\"><div class=\"si\"><b style=\"color:var(--good)\">${money(totAd)}<\/b><span>total advertising revenue \/ yr<\/span><\/div><div class=\"si\"><b>${money(m.rev)}<\/b><span>kiosk media (${pct(m.rev\/totAd)})<\/span><\/div><div class=\"si\"><b>${money(sg.rev)}<\/b><span>digital signage (${pct(sg.rev\/totAd)})<\/span><\/div><div class=\"si\"><b>${num(m.impressions+sg.impressions)}<\/b><span>combined impressions \/ yr<\/span><\/div><\/div>`));\n    const mixParts=[{v:m.rev,name:'Kiosk media',color:'var(--s-hotels)'},{v:sg.rev,name:'Digital signage',color:'var(--s-attractions)'}];\n    const mrow=h('<div style=\"display:flex;gap:20px;align-items:center;flex-wrap:wrap;margin-top:10px\"><\/div>');\n    mrow.appendChild(donut(mixParts));\n    const mleg=h('<div style=\"flex:1;min-width:180px\"><\/div>');\n    mixParts.forEach(p=>mleg.appendChild(h(`<div class=\"locrow\" style=\"grid-template-columns:1fr auto\"><div class=\"lr-name\" style=\"display:flex;align-items:center;gap:7px\"><span class=\"sw\" style=\"width:11px;height:11px;border-radius:3px;background:${p.color}\"><\/span>${p.name}<\/div><div class=\"lr-val\">${money(p.v)}<\/div><\/div>`)));\n    mrow.appendChild(mleg); mixCard.appendChild(mrow);\n    wrap.appendChild(mixCard); wrap.lastChild.style.marginBottom='16px';\n  }\n\n  const sf=card('The self-funding math','total advertising vs. total program cost','',' ');\n  const totRev=m.rev+(sg?sg.rev:0), totCap=MEDIA.capital+(sg?sg.capital:0), totSw=MEDIA.softwareYr+(sg?sg.softwareYr:0);\n  sf.appendChild(h(`<div class=\"stat-inline\" style=\"margin-top:4px\"><div class=\"si\"><b>${money(totRev)}<\/b><span>total ad revenue \/ yr (mid)<\/span><\/div><div class=\"si\"><b>${money(totSw)}<\/b><span>annual software (kiosks + screens)<\/span><\/div><div class=\"si\"><b>${money(totCap)}<\/b><span>one-time capital (kiosks + screens)<\/span><\/div><div class=\"si\"><b style=\"color:var(--good)\">${(totCap\/totRev).toFixed(1)} yr<\/b><span>capital payback (mid)<\/span><\/div><\/div>`));\n  sf.appendChild(h(`<div class=\"note\">MODELED. Aspen is a premium advertising market \\u2014 luxury real estate, galleries, private aviation, ski retail and hospitality all buy against this audience \\u2014 so the per-unit rates are set materially above the platform benchmark and the direct-sold share is high at ${pct(MEDIA.streamSplit.direct)}. At the mid scenario, combined kiosk and signage advertising covers the annual platform fee about ${(totRev\/totSw).toFixed(0)}\\u00d7 over and repays the full hardware investment in roughly ${(totCap\/totRev).toFixed(1)} years. Anchor with a real rate card before quoting externally.<\/div>`));\n  wrap.appendChild(sf);\n  return wrap;\n}\n\n\n\/* ================= VISITOR VALUE (LTV + AI ENGAGEMENT + OPPORTUNITY) =================\n   New page for a HIGH-VALUE, LOW-VOLUME destination. Aspen draws roughly a\n   tenth of a mass-market destination's visitors but each is worth far more,\n   and research puts the average visitor at 12.9 lifetime trips. So the\n   argument is not \"how many people touched a kiosk\" \u2014 it is what each\n   interaction is worth, and whether the relationship brings them back. *\/\nfunction aiValue(k){\n  const lv=ltvMetrics(k), en=engageMetrics(k), m=rollup(k);\n  const base=lv.scen.find(s=>s.id==='base');\n  return `Aspen visitors average <b>${lv.lifetimeTrips} lifetime trips<\/b> at roughly <b>${money(lv.spendPerTrip)}<\/b> of direct spend each \u2014 a visitor relationship worth <span class=\"em\">${money(lv.lifetimeValue)}<\/span>. The modeled network influences <b>${num(lv.influenced)}<\/b> converted visitors a year, which puts <span class=\"em\">${money(lv.relationshipValue)}<\/span> of lifetime visitor value under management. Each kiosk interaction carries <b>${money(en.valuePerInteraction)}<\/b> of attributed impact and each AI concierge conversation <b>${money(en.valuePerAiConv)}<\/b> \u2014 multiples of what a high-volume destination returns per touch. At the base scenario, adding <b>${base.trips}<\/b> of a trip across influenced visitors is worth <span class=\"em\">${money(base.futureSpend)}<\/span> of future visitor spending \u2014 arriving over a decade, on top of this year's attributed impact.`;\n}\nfunction valueMiniCard(k){\n  const lv=ltvMetrics(k), en=engageMetrics(k);\n  const c=card('Visitor value &amp; AI concierge engagement','high-value visitors, measured \u2014 the relationship, not just the trip','',' ');\n  c.appendChild(h(`<div class=\"stat-inline\" style=\"margin-top:2px\"><div class=\"si\"><b>${money(lv.lifetimeValue)}<\/b><span>visitor lifetime value<\/span><\/div><div class=\"si\"><b>${lv.lifetimeTrips}<\/b><span>avg lifetime trips<\/span><\/div><div class=\"si\"><b>${money(en.valuePerInteraction)}<\/b><span>impact per interaction<\/span><\/div><div class=\"si\"><b>${num(en.aiConversations)}<\/b><span>AI conversations \/ yr<\/span><\/div><div class=\"si\"><b>${num(en.itineraries)}<\/b><span>itineraries created \/ yr<\/span><\/div><\/div>`));\n  c.appendChild(h(`<div style=\"display:flex;gap:8px;margin-top:12px\"><button class=\"chip\" style=\"cursor:pointer\" onclick=\"go('#\/value')\">Open Visitor Value \u203a<\/button><\/div>`));\n  return c;\n}\nfunction pageValue(){\n  const k=K(); const lv=ltvMetrics(k), en=engageMetrics(k), m=rollup(k);\n  const wrap=h('<div class=\"fade\"><\/div>');\n  wrap.appendChild(h(`<a class=\"backbtn\" onclick=\"go('#\/overview')\"><svg viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M15 18l-6-6 6-6\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/><\/svg> ${ACT().region} overview<\/a>`));\n  wrap.appendChild(h(`<div class=\"crumbs\"><span>${ACT().name}<\/span><span class=\"sep\">\u203a<\/span><a onclick=\"go('#\/overview')\">${ACT().region}<\/a><span class=\"sep\">\u203a<\/span><span>Visitor Value<\/span><\/div>`));\n  wrap.appendChild(h(`<div class=\"pagehead\"><div><div class=\"eyebrow\">High-value visitors \u00b7 AI concierge<\/div><h1>Visitor value \u2014 the relationship, not just the trip<\/h1><div class=\"ch-sub\" style=\"margin-top:4px;color:var(--text-2)\">Lifetime value \u00b7 engagement taxonomy \u00b7 opportunity band \u00b7 ${periodLabel()}<\/div><\/div><div class=\"meta\">Avg lifetime trips<br><b style=\"color:var(--text);font-size:18px\">${lv.lifetimeTrips}<\/b><\/div><\/div>`));\n  wrap.appendChild(h(aiBanner('TrueImpact AI \u00b7 Visitor value read', aiValue(k))));\n\n  \/\/ ---- KPI band\n  const kpis=h('<div class=\"grid kpis\" style=\"margin-bottom:16px\"><\/div>');\n  kpis.appendChild(h(`<div class=\"card kpi hero\"><div class=\"glow\"><\/div><div class=\"k-label\">Visitor lifetime value<\/div><div class=\"k-val\"><span class=\"cur\">${curSym()}<\/span>${money(lv.lifetimeValue).replace(curSym(),'')}<\/div><div class=\"k-delta up\">\u25b2 ${lv.lifetimeTrips} trips \u00d7 ${money(lv.spendPerTrip)}<\/div><div class=\"k-foot\">What one visitor relationship is worth across its life \u2014 not one trip<\/div><\/div>`));\n  kpis.appendChild(kpiTile('Lifetime value under management',money(lv.relationshipValue),'',`${num(lv.influenced)} influenced visitors \/ yr`));\n  kpis.appendChild(kpiTile('Impact per interaction',money(en.valuePerInteraction),'','the high-value destination advantage'));\n  kpis.appendChild(kpiTile('Impact per AI conversation',money(en.valuePerAiConv),'',`${num(en.aiConversations)} conversations \/ yr`));\n  wrap.appendChild(kpis);\n\n  \/\/ ---- why high-value changes the model\n  const why=card('Why a high-value destination reads differently','fewer interactions, far more value in each','',' ');\n  why.appendChild(h(`<div class=\"stat-inline\" style=\"margin-top:4px\"><div class=\"si\"><b>${num(DEST_CONTEXT.visitors)}<\/b><span>annual visitors<\/span><\/div><div class=\"si\"><b>${money(DEST_CONTEXT.visitorSpend\/DEST_CONTEXT.visitors)}<\/b><span>direct spend per visitor<\/span><\/div><div class=\"si\"><b>${num(m.interactions)}<\/b><span>modeled interactions \/ yr<\/span><\/div><div class=\"si\"><b style=\"color:var(--good)\">${money(en.valuePerInteraction)}<\/b><span>attributed impact per interaction<\/span><\/div><\/div>`));\n  why.appendChild(h(`<div class=\"note\">On identical funnel assumptions, the same 10-kiosk network modeled for a high-volume Florida destination returns about <b>$9.52<\/b> of attributed impact per interaction. This one is modeled at <b>${money2(en.valuePerInteraction)}<\/b> \u2014 roughly <b>${(en.valuePerInteraction\/9.52).toFixed(1)}\u00d7<\/b> \u2014 because Aspen's direct spend per visitor is about <b>${money(DEST_CONTEXT.visitorSpend\/DEST_CONTEXT.visitors)}<\/b> against roughly <b>$673<\/b> in a mass market. The practical consequence: <b>the goal is not more touches, it is better ones.<\/b> Depth of engagement \u2014 an AI conversation, a built itinerary, a saved offer, a mobile handoff \u2014 matters far more here than raw footfall, and the network is sized and placed accordingly. It also means a 10-unit network is the right size for Aspen; scaling to 30 would add cost without adding proportionate value.<\/div>`));\n  wrap.appendChild(why); wrap.lastChild.style.marginBottom='16px';\n\n  \/\/ ---- the relationship scenarios\n  const rel=card('The relationship is the asset','what one more lifetime trip is worth across influenced visitors','',' ');\n  const rl=h('<div class=\"loclist\"><\/div>');\n  const rmax=Math.max(...lv.scen.map(s=>s.futureSpend))||1;\n  lv.scen.forEach(s=>{rl.appendChild(h(`<div class=\"locrow\"><div><div class=\"lr-name\">${s.name} \u00b7 +${s.trips} lifetime trips<\/div><div class=\"lr-sub\">${s.note} A ${pct(s.pctLift,1)} lift on the ${lv.lifetimeTrips}-trip average.<\/div><\/div><div class=\"lr-val\">${money(s.futureSpend)}<div class=\"lr-sub\" style=\"font-weight:600\">future visitor spending<\/div><\/div><div class=\"lr-bar\"><i style=\"width:${(s.futureSpend\/rmax*100).toFixed(0)}%\"><\/i><\/div><\/div>`));});\n  rel.appendChild(rl);\n  rel.appendChild(h(`<div class=\"note\"><b>MODELED.<\/b> We deliberately do <i>not<\/i> claim credit for all ${lv.lifetimeTrips} trips \u2014 only for a marginal lift. This is the strategic case for an AI concierge, an itinerary builder and a mobile handoff: they are relationship instruments, not transaction instruments. Their return arrives over a decade of repeat visitation, alongside the ${money(m.total)} of impact attributed to the current year. Measured properly after go-live through returning-visitor identification in OMNI GuestBook.<\/div>`));\n  wrap.appendChild(rel); wrap.lastChild.style.marginBottom='16px';\n\n  \/\/ ---- AI concierge engagement taxonomy\n  const eng=card('TrueOmni engagement \u2014 what visitors actually do','people reached \u2192 impressions \u2192 interactions \u2192 AI conversations \u2192 itineraries \u2192 discovery \u2192 offers \u2192 conversions','',' ');\n  const grid=h('<div class=\"loclist\"><\/div>');\n  const rows=[\n    {n:'People reached',                 v:num(en.reached),        s:'annual visitors encountering a TrueOmni surface'},\n    {n:'Impressions',                    v:num(en.impressions),    s:`${ENGAGE.imprPerInteraction} on-screen impressions per interaction`},\n    {n:'Interactions',                   v:num(en.interactions),   s:'a visitor actively engaged the surface'},\n    {n:'AI Conversations',               v:num(en.aiConversations),s:`${pct(ENGAGE.aiConvRate)} of interactions \u00b7 ${ENGAGE.avgConvTurns} turns each`},\n    {n:'Itineraries Created',            v:num(en.itineraries),    s:`${pct(ENGAGE.itinPerAiConv)} of AI conversations produce a plan`},\n    {n:'Attractions & Restaurants Discovered', v:num(en.discoveries), s:`${ENGAGE.discoverPerInteraction} detail views per interaction`},\n    {n:'Offers & Tickets Viewed',        v:num(en.offersViewed),   s:`${ENGAGE.offersPerInteraction} per interaction`},\n    {n:'Mobile Handoffs (Send to Phone)',v:num(en.handoffs),       s:`${pct(ENGAGE.handoffRate)} of interactions continue on the visitor's phone`},\n    {n:'Attributed Conversions',         v:num(en.conversions),    s:'the visitors whose spend the model attributes'},\n  ];\n  const emax=en.discoveries;\n  rows.forEach((r,i)=>{const raw=[en.reached,en.impressions,en.interactions,en.aiConversations,en.itineraries,en.discoveries,en.offersViewed,en.handoffs,en.conversions][i];\n    grid.appendChild(h(`<div class=\"locrow\"><div><div class=\"lr-name\">${r.n}<\/div><div class=\"lr-sub\">${r.s}<\/div><\/div><div class=\"lr-val\">${r.v}<\/div><div class=\"lr-bar\"><i style=\"width:${(Math.min(raw,emax)\/emax*100).toFixed(0)}%\"><\/i><\/div><\/div>`));});\n  eng.appendChild(grid);\n  eng.appendChild(h('<div class=\"note\">Every rate on this page is a TrueOmni platform benchmark and is <b>MODELED<\/b>. After go-live these become ledgered counts from the live attribution feed \u2014 this is exactly the layer a standard economic-impact report cannot produce, because a brochure rack and a static sign generate no data at all.<\/div>'));\n  wrap.appendChild(eng); wrap.lastChild.style.marginBottom='16px';\n\n  \/\/ ---- top-down opportunity band + cross-check\n  const opp=card('Modeled opportunity \u2014 top-down cross-check',`share of Aspen's ${money(OPPORTUNITY.base)} reported direct visitor spending`,'',' ');\n  const ol=h('<div class=\"loclist\"><\/div>');\n  const orows=opportunityRows(); const omax=orows[orows.length-1].v;\n  orows.forEach(r=>{const hit=(m.direct>=r.v);\n    ol.appendChild(h(`<div class=\"locrow\"><div><div class=\"lr-name\">${pct(r.rate,2)} of direct visitor spending<\/div><div class=\"lr-sub\">engagement influence scenario<\/div><\/div><div class=\"lr-val\">${money(r.v)}<div class=\"lr-sub\" style=\"font-weight:600\">${hit?'below the modeled result':'above the modeled result'}<\/div><\/div><div class=\"lr-bar\"><i style=\"width:${(r.v\/omax*100).toFixed(0)}%\"><\/i><\/div><\/div>`));});\n  opp.appendChild(ol);\n  opp.appendChild(h(`<div class=\"stat-inline\" style=\"margin-top:14px\"><div class=\"si\"><b style=\"color:var(--good)\">${money(m.direct)}<\/b><span>bottom-up direct attributed spend<\/span><\/div><div class=\"si\"><b>${pct(m.direct\/OPPORTUNITY.base,3)}<\/b><span>of Aspen's direct visitor spending<\/span><\/div><div class=\"si\"><b>${money(m.total)}<\/b><span>total impact incl. influenced<\/span><\/div><\/div>`));\n  opp.appendChild(h(`<div class=\"note\"><b>The cross-check that matters.<\/b> Built bottom-up through the funnel \u2014 exposure, interaction, high-intent, conversion \u2014 the network lands at <b>${money(m.direct)}<\/b> of direct attributed spend, or <b>${pct(m.direct\/OPPORTUNITY.base,3)}<\/b> of Aspen's reported direct visitor spending. That sits at the conservative end of the illustrative band above, which means the two independent methods agree. If the funnel had produced a number above the 1.00% row, the model would be wrong and we would say so.<br><br><i>${OPPORTUNITY.label}<\/i><\/div>`));\n  wrap.appendChild(opp);\n  return wrap;\n}\n\nfunction destContextCard(){\n  const d=DEST_CONTEXT;\n  const c=card('Destination scale','Aspen visitor economy \u2014 the customer\\'s own reported figures','',' ');\n  c.appendChild(h(`<div class=\"stat-inline\" style=\"margin-top:4px\"><div class=\"si\"><b>${num(d.visitors)}+<\/b><span>Aspen visitors<\/span><\/div><div class=\"si\"><b>${money(d.visitorSpend)}<\/b><span>direct visitor spending<\/span><\/div><div class=\"si\"><b>${money(d.economicImpact)}<\/b><span>Aspen economic impact<\/span><\/div><div class=\"si\"><b>${num(d.jobs)}+<\/b><span>visitor-industry jobs<\/span><\/div><div class=\"si\"><b>${d.lifetimeTrips}<\/b><span>avg lifetime trips per visitor<\/span><\/div><\/div>`));\n  const sn=h('<div class=\"loclist\" style=\"margin-top:6px\"><\/div>');\n  d.snowmass.forEach(s=>sn.appendChild(h(`<div class=\"locrow\" style=\"grid-template-columns:auto 1fr;cursor:default\"><div class=\"lr-val\" style=\"min-width:96px\">${s.v}<div class=\"lr-sub\" style=\"font-weight:600\">${s.l}<\/div><\/div><div class=\"lr-sub\" style=\"text-align:left\">${s.d}<\/div><\/div>`)));\n  c.appendChild(h('<div class=\"note\" style=\"margin-bottom:2px\"><b>Regional \/ Snowmass context<\/b><\/div>'));\n  c.appendChild(sn);\n  c.appendChild(h('<div class=\"note\">Aspen figures are <b>customer-supplied<\/b> and were not independently verified against a public source. The Snowmass investment, lift and lodging figures are from Aspen Snowmass\\' published summer-2025 announcements. Everything modeled by TrueOmni is labelled as such throughout.<\/div>'));\n  return c;\n}\n\n\/* ================= ROUTER ================= *\/\nconst state={loc:null};\nfunction go(hash){location.hash=hash;}\nfunction render(){\n  buildDestSel();\n  const hash=location.hash||'#\/overview';\n  const parts=hash.replace('#\/','').split('\/');\n  if(activeDest==='network'){\n    app.innerHTML='';\n    if(parts[0]==='modules'){engMode='modules';app.appendChild(pageEngagement());}\n    else if(parts[0]==='campaigns'){engMode='campaigns';app.appendChild(pageEngagement());}\n    else app.appendChild(pageNetwork());\n    window.scrollTo({top:0,behavior:'instant'});return;\n  }\n  state.loc = parts[0]==='location'?parts[1]:null;\n  app.innerHTML='';\n  let node;\n  if(parts[0]==='location') node=pageLocation(parts[1]);\n  else if(parts[0]==='partners') node=pagePartners();\n  else if(parts[0]==='tax') node=pageTax();\n  else if(parts[0]==='modules'){engMode='modules';node=pageEngagement();}\n  else if(parts[0]==='campaigns'){engMode='campaigns';node=pageEngagement();}\n  else if(parts[0]==='media') node=pageMedia();\n  else if(parts[0]==='value') node=pageValue();\n  else node=pageOverview();\n  app.appendChild(node);\n  window.scrollTo({top:0,behavior:'instant'});\n}\nwindow.addEventListener('hashchange',render);\n\n\/\/ destination selector (rebuilt each render to reflect active)\nfunction buildDestSel(){\n  const host=$('#destSel');if(!host)return;\n  const opts=[...Object.keys(DESTINATIONS).map(id=>[id,DESTINATIONS[id].name])];\n  host.innerHTML=opts.map(([id,label])=>`<option value=\"${id}\"${id===activeDest?' selected':''}>${label}<\/option>`).join('');\n}\n\n\/\/ period selector\n$('#periodSel').addEventListener('click',e=>{const b=e.target.closest('button');if(!b)return;\n  [...$('#periodSel').children].forEach(x=>x.classList.remove('on'));b.classList.add('on');PERIOD=b.dataset.p;render();});\n\/\/ single-currency dashboard (USD) \u2014 no currency selector on US destinations\n\/\/ destination selector\n$('#destSel').addEventListener('change',e=>setDest(e.target.value));\n\/\/ theme\n$('#themeBtn').addEventListener('click',()=>{const cur=document.documentElement.getAttribute('data-theme');document.documentElement.setAttribute('data-theme',cur==='dark'?'light':'dark');render();});\nwindow.addEventListener('mousemove',e=>{if(TT.style.opacity==1)moveTT(e);});\n\nrender();\n<\/script>\n<\/body>\n<\/html>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\n<\/div>\n\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>TrueImpact \u2014 Executive Dashboard \u00b7 Aspen Snowmass<\/p>","protected":false},"author":11,"featured_media":57099,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"elementor_canvas","meta":{"_acf_changed":false,"inline_featured_image":false,"postBodyCss":"","postBodyMargin":[],"postBodyPadding":[],"postBodyBackground":{"backgroundType":"classic","gradient":""},"_thinkrank_schema_form_data":"","_thinkrank_selected_schema_type":"","_thinkrank_additional_schemas":"","_thinkrank_canonical_url":"","_thinkrank_og_title":"","_thinkrank_og_description":"","_thinkrank_og_image":"","_thinkrank_twitter_title":"","_thinkrank_twitter_description":"","_thinkrank_twitter_image":"","_thinkrank_imported_from":"","_thinkrank_focus_keywords":[],"_thinkrank_focus_keyword":"","_thinkrank_robots_meta_enabled":0,"_thinkrank_robots_meta":"[]","_thinkrank_advanced_robots_meta":"[]","_thinkrank_exclude_from_search":0,"_thinkrank_exclude_from_archives":0,"_thinkrank_primary_category":0,"footnotes":""},"class_list":["post-58305","page","type-page","status-publish","has-post-thumbnail","hentry"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v27.9 (Yoast SEO v28.7) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>AspenSnowmass - TrueOmni<\/title>\n<meta name=\"description\" content=\"Aspen Snowmass\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/trueomni.com\/spanish\/aspen-snowmass\/\" \/>\n<meta property=\"og:locale\" content=\"es_MX\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"AspenSnowmass\" \/>\n<meta property=\"og:description\" content=\"Aspen Snowmass\" \/>\n<meta property=\"og:url\" content=\"https:\/\/trueomni.com\/spanish\/aspen-snowmass\/\" \/>\n<meta property=\"og:site_name\" content=\"TrueOmni\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/trueomni\/\" \/>\n<meta property=\"article:modified_time\" content=\"2026-08-20T15:46:02+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/trueomni.com\/wp-content\/uploads\/2026\/05\/Travel-TrueOmni-TNT-IVE-Kiosks-2026-Website-1024x753.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1024\" \/>\n\t<meta property=\"og:image:height\" content=\"753\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Tiempo de lectura\" \/>\n\t<meta name=\"twitter:data1\" content=\"1 minuto\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/trueomni.com\\\/aspen-snowmass\\\/\",\"url\":\"https:\\\/\\\/trueomni.com\\\/aspen-snowmass\\\/\",\"name\":\"AspenSnowmass - TrueOmni\",\"isPartOf\":{\"@id\":\"http:\\\/\\\/trueomni.com\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/trueomni.com\\\/aspen-snowmass\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/trueomni.com\\\/aspen-snowmass\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/trueomni.com\\\/wp-content\\\/uploads\\\/2026\\\/05\\\/Travel-TrueOmni-TNT-IVE-Kiosks-2026-Website-scaled.png\",\"datePublished\":\"2026-08-20T15:45:13+00:00\",\"dateModified\":\"2026-08-20T15:46:02+00:00\",\"description\":\"Aspen Snowmass\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/trueomni.com\\\/aspen-snowmass\\\/#breadcrumb\"},\"inLanguage\":\"es\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/trueomni.com\\\/aspen-snowmass\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"es\",\"@id\":\"https:\\\/\\\/trueomni.com\\\/aspen-snowmass\\\/#primaryimage\",\"url\":\"https:\\\/\\\/trueomni.com\\\/wp-content\\\/uploads\\\/2026\\\/05\\\/Travel-TrueOmni-TNT-IVE-Kiosks-2026-Website-scaled.png\",\"contentUrl\":\"https:\\\/\\\/trueomni.com\\\/wp-content\\\/uploads\\\/2026\\\/05\\\/Travel-TrueOmni-TNT-IVE-Kiosks-2026-Website-scaled.png\",\"width\":2560,\"height\":1883,\"caption\":\"TrueOmni interactive kiosks in a hotel lobby\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/trueomni.com\\\/aspen-snowmass\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"http:\\\/\\\/trueomni.com\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"AspenSnowmass\"}]},{\"@type\":\"WebSite\",\"@id\":\"http:\\\/\\\/trueomni.com\\\/#website\",\"url\":\"http:\\\/\\\/trueomni.com\\\/\",\"name\":\"TrueOmni\",\"description\":\"TrueOmni Self-Service Digital Customer Experience Company\",\"publisher\":{\"@id\":\"http:\\\/\\\/trueomni.com\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"http:\\\/\\\/trueomni.com\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"es\"},{\"@type\":\"Organization\",\"@id\":\"http:\\\/\\\/trueomni.com\\\/#organization\",\"name\":\"TRUE OMNI LLC\",\"alternateName\":\"TRUE OMNI LLC\",\"url\":\"http:\\\/\\\/trueomni.com\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"es\",\"@id\":\"http:\\\/\\\/trueomni.com\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"http:\\\/\\\/trueomni.com\\\/wp-content\\\/uploads\\\/2024\\\/06\\\/TrueOmni-Logo-2.png\",\"contentUrl\":\"http:\\\/\\\/trueomni.com\\\/wp-content\\\/uploads\\\/2024\\\/06\\\/TrueOmni-Logo-2.png\",\"width\":1000,\"height\":230,\"caption\":\"TRUE OMNI LLC\"},\"image\":{\"@id\":\"http:\\\/\\\/trueomni.com\\\/#\\\/schema\\\/logo\\\/image\\\/\"},\"sameAs\":[\"https:\\\/\\\/www.facebook.com\\\/trueomni\\\/\"],\"description\":\"TrueOmni is the leader in digital self-service solutions, offering innovative kiosks, digital signage, and mobile marketing platforms. Enhance customer engagement and streamline operations with our cutting-edge technology tailored for the hospitality, retail, and tourism industries.\",\"email\":\"info@trueomni.com\",\"telephone\":\"8333006664\",\"legalName\":\"True Omni LLC\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"AspenSnowmass - TrueOmni","description":"Aspen Snowmass","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:\/\/trueomni.com\/spanish\/aspen-snowmass\/","og_locale":"es_MX","og_type":"article","og_title":"AspenSnowmass","og_description":"Aspen Snowmass","og_url":"https:\/\/trueomni.com\/spanish\/aspen-snowmass\/","og_site_name":"TrueOmni","article_publisher":"https:\/\/www.facebook.com\/trueomni\/","article_modified_time":"2026-08-20T15:46:02+00:00","og_image":[{"width":1024,"height":753,"url":"https:\/\/trueomni.com\/wp-content\/uploads\/2026\/05\/Travel-TrueOmni-TNT-IVE-Kiosks-2026-Website-1024x753.png","type":"image\/png"}],"twitter_card":"summary_large_image","twitter_misc":{"Tiempo de lectura":"1 minuto"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/trueomni.com\/aspen-snowmass\/","url":"https:\/\/trueomni.com\/aspen-snowmass\/","name":"AspenSnowmass - TrueOmni","isPartOf":{"@id":"http:\/\/trueomni.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/trueomni.com\/aspen-snowmass\/#primaryimage"},"image":{"@id":"https:\/\/trueomni.com\/aspen-snowmass\/#primaryimage"},"thumbnailUrl":"https:\/\/trueomni.com\/wp-content\/uploads\/2026\/05\/Travel-TrueOmni-TNT-IVE-Kiosks-2026-Website-scaled.png","datePublished":"2026-08-20T15:45:13+00:00","dateModified":"2026-08-20T15:46:02+00:00","description":"Aspen Snowmass","breadcrumb":{"@id":"https:\/\/trueomni.com\/aspen-snowmass\/#breadcrumb"},"inLanguage":"es","potentialAction":[{"@type":"ReadAction","target":["https:\/\/trueomni.com\/aspen-snowmass\/"]}]},{"@type":"ImageObject","inLanguage":"es","@id":"https:\/\/trueomni.com\/aspen-snowmass\/#primaryimage","url":"https:\/\/trueomni.com\/wp-content\/uploads\/2026\/05\/Travel-TrueOmni-TNT-IVE-Kiosks-2026-Website-scaled.png","contentUrl":"https:\/\/trueomni.com\/wp-content\/uploads\/2026\/05\/Travel-TrueOmni-TNT-IVE-Kiosks-2026-Website-scaled.png","width":2560,"height":1883,"caption":"TrueOmni interactive kiosks in a hotel lobby"},{"@type":"BreadcrumbList","@id":"https:\/\/trueomni.com\/aspen-snowmass\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"http:\/\/trueomni.com\/"},{"@type":"ListItem","position":2,"name":"AspenSnowmass"}]},{"@type":"WebSite","@id":"http:\/\/trueomni.com\/#website","url":"http:\/\/trueomni.com\/","name":"VerdaderoOmni","description":"TrueOmni es una empresa de experiencia digital de autoservicio para el cliente.","publisher":{"@id":"http:\/\/trueomni.com\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"http:\/\/trueomni.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"es"},{"@type":"Organization","@id":"http:\/\/trueomni.com\/#organization","name":"TRUE OMNI LLC","alternateName":"TRUE OMNI LLC","url":"http:\/\/trueomni.com\/","logo":{"@type":"ImageObject","inLanguage":"es","@id":"http:\/\/trueomni.com\/#\/schema\/logo\/image\/","url":"http:\/\/trueomni.com\/wp-content\/uploads\/2024\/06\/TrueOmni-Logo-2.png","contentUrl":"http:\/\/trueomni.com\/wp-content\/uploads\/2024\/06\/TrueOmni-Logo-2.png","width":1000,"height":230,"caption":"TRUE OMNI LLC"},"image":{"@id":"http:\/\/trueomni.com\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/trueomni\/"],"description":"TrueOmni is the leader in digital self-service solutions, offering innovative kiosks, digital signage, and mobile marketing platforms. Enhance customer engagement and streamline operations with our cutting-edge technology tailored for the hospitality, retail, and tourism industries.","email":"info@trueomni.com","telephone":"8333006664","legalName":"True Omni LLC"}]}},"_links":{"self":[{"href":"https:\/\/trueomni.com\/spanish\/wp-json\/wp\/v2\/pages\/58305","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/trueomni.com\/spanish\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/trueomni.com\/spanish\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/trueomni.com\/spanish\/wp-json\/wp\/v2\/users\/11"}],"replies":[{"embeddable":true,"href":"https:\/\/trueomni.com\/spanish\/wp-json\/wp\/v2\/comments?post=58305"}],"version-history":[{"count":4,"href":"https:\/\/trueomni.com\/spanish\/wp-json\/wp\/v2\/pages\/58305\/revisions"}],"predecessor-version":[{"id":58309,"href":"https:\/\/trueomni.com\/spanish\/wp-json\/wp\/v2\/pages\/58305\/revisions\/58309"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/trueomni.com\/spanish\/wp-json\/wp\/v2\/media\/57099"}],"wp:attachment":[{"href":"https:\/\/trueomni.com\/spanish\/wp-json\/wp\/v2\/media?parent=58305"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}