{"id":58134,"date":"2026-08-06T18:31:07","date_gmt":"2026-08-06T18:31:07","guid":{"rendered":"http:\/\/trueomni.com\/?p=58134"},"modified":"2026-08-06T18:31:50","modified_gmt":"2026-08-06T18:31:50","slug":"visitknoxville","status":"publish","type":"post","link":"https:\/\/trueomni.com\/french\/true-impact\/visitknoxville\/","title":{"rendered":"Visitez Knoxville"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-post\" data-elementor-id=\"58134\" class=\"elementor elementor-58134\" data-elementor-post-type=\"post\">\n\t\t\t\t<div class=\"elementor-element elementor-element-cec9d27 e-con e-atomic-element e-flexbox-base e-74bce89 \" data-id=\"cec9d27\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"cec9d27\" data-e-type=\"e-flexbox\" data-id=\"cec9d27\">\n    \t\t<div class=\"elementor-element elementor-element-27134db elementor-widget elementor-widget-html\" data-id=\"27134db\" 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 Visit Knoxville<\/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 opportunity scenario \u2014 a 20-kiosk + 5-screen Phase-1 network. No deployment exists yet; 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        Board 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 prepared for Visit Knoxville \u00b7 Scottsdale, AZ \u00b7 www.trueomni.com \u00b7 833-300-6664<\/div>\n  <div>OPPORTUNITY SCENARIO \u00b7 modeled pre-deployment projection \u00b7 20 kiosks + 5 digital signage screens \u00b7 figures in USD \u00b7 Visit Knoxville\u2019s own cited visitor-economy statistics are labeled as such; all network, engagement and advertising figures are modeled and 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 Visit Knoxville edition.\n   Single-tenant. Base inputs per deployment zone; every headline number\n   is DERIVED in deriveMetrics() so the math is transparent and can be\n   re-pointed at the live Kiolytics attribution feed once units are online.\n   Dollars-first, always. Currency: US dollars (USD).\n\n   PROVENANCE \u2014 read before quoting any figure:\n   * REAL \/ CITED  \u2014 the destination-context strip (~7.3M annual\n     visitors, visitor spending, tourism jobs, taxes and the other headline\n     stats) is Visit Knoxville's own published data, supplied 2026-08-06.\n     Tax rates are public statutory rates (see taxNote on the tax page).\n   * MODELED       \u2014 the 20-kiosk + 5-screen network, its zones, the phase\n     ramp, the per-zone exposure split, interaction rates, average spend,\n     and ALL advertising economics. There is no discovery call and no\n     deployment behind these structural inputs. This is an OPPORTUNITY\n     SCENARIO sized as a defensible Phase-1 pilot, not saturation.\n   * The attributed economic impact below is a small slice of the\n     destination's total visitor economy \u2014 never conflate the two.\n------------------------------------------------------------------ *\/\nconst FUNNEL = { highIntent:0.17, conversion:0.14 };  \/\/ TrueOmni platform benchmarks\nconst INFL_RATE = 0.85;   \/\/ influenced $ per $1 direct (Tourism Economics tail; 0.7\u20131.1)\nconst LODGING_SHARE = 0.34;        \/\/ of captured visitor spend that is lodging (TOT base)\nconst DINING_RETAIL_SHARE = 0.46;  \/\/ taxable sales base (dining + retail + attraction tickets)\nconst RENTAL_SHARE = 0.08;         \/\/ of captured spend attributable to car rental\nconst STATE_INCOME_RATE = 0.018;   \/\/ blended state general-fund capture on total impact\nconst RECIRC_MULT = 0.9;           \/\/ local re-spend of each public tax $ (induced, soft)\n\n\/* NETWORK BASE \u2014 the quoted opportunity: 20 interactive kiosks + 5 digital\n   signage screens. Kiosks transact; signage broadcasts. Never mixed. *\/\nconst NET_KIOSKS = 20;\nconst NET_SCREENS = 5;\n\n\/* DESTINATION CONTEXT \u2014 Visit Knoxville's own cited statistics. Surfaced first on the\n   overview so the reader sees the whole visitor economy before the slice\n   TrueOmni measurably influences. *\/\nconst CONTEXT_TITLE = 'Visit Knoxville \u2014 the visitor economy we are measuring against';\nconst CONTEXT = [\n  {label:'Annual visitors', v:'~7.3M', sub:'regional tourism data'},\n  {label:'Visitor spending', v:'~$1.6B', sub:'direct visitor spend'},\n  {label:'Economic impact', v:'~$2.1B', sub:'destination-wide'},\n  {label:'Tourism jobs', v:'~15,000', sub:'supported by tourism'},\n  {label:'University', v:'Univ. of Tennessee', sub:'Neyland Stadium'},\n  {label:'Convention', v:'Knoxville Convention Center', sub:'+ World\\'s Fair Park'},\n  {label:'Downtown core', v:'Market Square', sub:'100 Block \u00b7 Gay St'},\n  {label:'Gateway', v:'Great Smoky Mtns', sub:'most-visited US park'}\n];\n\n\/* Tennessee tax model (US variant). taxBreak keys are kept stable for the\n   render layer: hotelTOT = hotel \/ transient-occupancy tax (LOCAL \u2014 the line\n   that funds the destination), salesTax = sales & use on taxable visitor\n   spend, rentalTax = rental-vehicle excise, stateTax = state general-fund\n   capture. taxLocal = the three local lines; taxState = state capture. *\/\nconst TAX_NOTE = \"Rates: combined hotel occupancy tax carried at <b>9.00%<\/b> (City of Knoxville 4.00%, raised effective Jan 1 2025, plus Knox County 5.00%) and the <b>9.25%<\/b> combined Knoxville sales tax (7.00% Tennessee + 2.25% Knox County, confirmed). The county share and the 3.00% rental surcharge should be confirmed with the CVB before publication.\";\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\/\/ Partner segment metadata\nconst SEG = {\n  hotels:      {name:'Hotels & Lodging',    color:'var(--s-hotels)',      raw:'#1C9CD8'},\n  dining:      {name:'Restaurants & Dining', color:'var(--s-dining)',      raw:'#EB6834'},\n  attractions: {name:'Attractions & Tours',  color:'var(--s-attractions)', raw:'#008300'},\n  events:      {name:'Events & Venues',      color:'var(--s-events)',      raw:'#E87BA4'},\n};\n\n\/\/ seasonal shape (Jan..Dec) for this destination's archetype\nconst SEASON = {\n  knox:[0.66,0.7,0.92,1.1,1.18,1.26,1.3,1.16,1.14,1.34,0.92,0.72],\n};\nconst MONTHS=['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];\n\n\/* ==================================================================\n   DESTINATION REGISTRY \u2014 single tenant: Visit Knoxville.\n   Each \"location\" is a deployment zone with its own inputs, kiosk count,\n   signage screen count and deployment phase. x,y = map coords in 480x620.\n================================================================== *\/\nconst DESTINATIONS = {\n  knx:{ name:'Visit Knoxville', kind:'Destination Marketing Organization', scope:'Knoxville & Knox County', region:'Knoxville, TN',\n    outline:'M66,146 L236,98 L390,154 L418,266 L386,344 L408,444 L334,528 L180,542 L82,470 L60,316 Z',\n    lakeLabel:{x:238,y:592,t:'Tennessee River'},\n    locations:[\n      {id:'marketsq', name:\"Market Square & the 100 Block\", sites:\"Market Square \u00b7 Gay St \u00b7 Union Ave\", x:238,y:296, kiosks:5, screens:2, phase:1, exposures:720_000, interactionRate:0.46, avgSpend:168, tax:{tot:9.0,sales:9.25,rental:3.0}, season:'knox', mix:{hotels:0.24,dining:0.4,attractions:0.18,events:0.18}, note:\"The walkable downtown core \u2014 highest interaction rate in the network and the densest concentration of independent dining and retail partners. Phase-1 launch cluster.\", badge:'Phase 1'},\n      {id:'convention', name:\"Convention Center & World's Fair Park\", sites:\"Knoxville Convention Center \u00b7 Sunsphere\", x:214,y:330, kiosks:4, screens:1, phase:1, exposures:620_000, interactionRate:0.44, avgSpend:226, tax:{tot:9.0,sales:9.25,rental:3.0}, season:'knox', mix:{hotels:0.46,dining:0.24,attractions:0.14,events:0.16}, note:\"Captive delegate audiences and the adjacent hotel block \u2014 highest per-visitor spend and the fastest, cleanest proof of the program. Phase-1 launch cluster.\", badge:'Phase 1'},\n      {id:'ut', name:\"University of Tennessee\", sites:\"Neyland Stadium \u00b7 Cumberland Ave strip\", x:206,y:376, kiosks:4, screens:1, phase:2, exposures:540_000, interactionRate:0.38, avgSpend:142, tax:{tot:9.0,sales:9.25,rental:3.0}, season:'knox', mix:{hotels:0.22,dining:0.38,attractions:0.12,events:0.28}, note:\"Game-day surges and year-round campus visitation: the network's highest-volume, lowest-ticket zone and its most valuable date-targeted ad inventory.\"},\n      {id:'smokies', name:\"Smokies gateway corridor\", sites:\"I-40 visitor corridor \u00b7 Gateway Regional Center\", x:336,y:310, kiosks:3, screens:1, phase:2, exposures:420_000, interactionRate:0.4, avgSpend:198, tax:{tot:9.0,sales:9.25,rental:3.0}, season:'knox', mix:{hotels:0.38,dining:0.24,attractions:0.28,events:0.1}, note:\"Knoxville's position as the gateway to the most-visited national park in the country \u2014 the zone that converts pass-through Smokies traffic into Knoxville room nights.\"},\n      {id:'riverfront', name:\"Volunteer Landing waterfront\", sites:\"Tennessee River \u00b7 Volunteer Landing greenway\", x:250,y:406, kiosks:2, screens:0, phase:3, exposures:250_000, interactionRate:0.42, avgSpend:138, tax:{tot:9.0,sales:9.25,rental:3.0}, season:'knox', mix:{hotels:0.16,dining:0.28,attractions:0.44,events:0.12}, note:\"Weatherized riverfront units on the greenway; recreation-led intent with strong attractions and outfitter referrals.\"},\n      {id:'oldcity', name:\"Old City entertainment district\", sites:\"Jackson Ave \u00b7 Central St\", x:262,y:262, kiosks:2, screens:0, phase:3, exposures:230_000, interactionRate:0.43, avgSpend:162, tax:{tot:9.0,sales:9.25,rental:3.0}, season:'knox', mix:{hotels:0.18,dining:0.44,attractions:0.16,events:0.22}, note:\"Evening dining, music and events \u2014 the highest-converting zone for flash offers and late-day partner promotions.\"},\n    ]},\n};\n\nlet activeDest='knx';\nfunction ACT(){return DESTINATIONS[activeDest];}\nfunction LOCS(){return ACT().locations;}\nfunction findLoc(id){return LOCS().find(l=>l.id===id);}\n\n\/* deployment ramp phases *\/\nconst PHASES=[\n  {n:1, name:'Phase 1 \u00b7 Downtown + convention launch', window:'Weeks 0\u20134', ids:['marketsq','convention']},\n  {n:2, name:'Phase 2 \u00b7 Campus & Smokies gateway', window:'Weeks 5\u20139', ids:['marketsq','convention','ut','smokies']},\n  {n:3, name:'Phase 3 \u00b7 Full 20-kiosk network', window:'Weeks 10\u201316', ids:['marketsq','convention','ut','smokies','riverfront','oldcity']},\n];\n\n\/* ==================================================================\n   MEDIA NETWORK \u2014 kiosk advertising (Omni Media Network: native\n   direct-sold containers + Google Ad Manager programmatic).\n   MODELED on the TrueOmni platform benchmark of $500\u20131,500\/kiosk\/month\n   at mature fill. Replace with the customer's rate card when it lands.\n================================================================== *\/\nconst MEDIA={\n  perKioskMo:{floor:500, mid:1000, strong:1500},   \/\/ USD \/ kiosk \/ month at mature fill\n  imprPerEncounter:3.2,        \/\/ ad visualizations per kiosk encounter across containers\n  fillMature:0.70,             \/\/ sell-through at maturity\n  fillByPhase:{1:0.35, 2:0.55, 3:0.70},\n  streamSplit:{direct:0.62, programmatic:0.38},    \/\/ native direct-sold vs GAM programmatic\n  softwareYr:18000,            \/\/ kiosk platform software \/ yr (20 units)\n  capital:220000,              \/\/ one-time kiosk capital (20 units)\n  inventory:[\n    {id:'video',    name:'Full-screen video (kiosk attract loop)', share:.30},\n    {id:'banner',   name:'Home & footer banners',                  share:.24},\n    {id:'rail',     name:'Events \/ listings side-rail',            share:.18},\n    {id:'sponsored',name:'Sponsored listings & tiles',             share:.16},\n    {id:'qr',       name:'QR-conversion sponsorships',             share:.12},\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   never fed into interactions, attributed spend, economic impact or tax. *\/\nconst SIGNAGE={\n  enabled:true,\n  perScreenMo:{floor:200, mid:450, strong:800},   \/\/ ad revenue \/ screen \/ month at mature fill\n  loopSec:60, spotSec:15,       \/\/ playlist loop + spot length -> spots per loop -> share of voice\n  viewShare:0.55,               \/\/ share of a zone's foot traffic passing a screen's view zone\n  imprPerPasserby:1.4,          \/\/ screen impressions per person per pass\n  dwellSec:8,                   \/\/ avg attention seconds (the DOOH quality metric)\n  fillMature:0.65, fillByPhase:{1:0.30,2:0.50,3:0.65},\n  capitalPerScreen:4200, softwarePerScreenYr:600,\n  formats:[\n    {id:'wall',     name:'Video wall \/ lobby hero',        share:.34},\n    {id:'portrait', name:'Portrait posters (corridor)',     share:.28},\n    {id:'wayfind',  name:'Wayfinding & directory boards',   share:.20},\n    {id:'menu',     name:'Menu \/ retail promo boards',      share:.12},\n    {id:'outdoor',  name:'Outdoor \/ weatherized',           share:.06},\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}\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  const dwellHrs=impressions*SIGNAGE.dwellSec\/3600;\n  return {screens,reach,impressions,band,rev,cpm,spotsPerLoop,shareOfVoice,capital,softwareYr,byFormat,dwellHrs};\n}\nfunction totalCapital(){const sg=signageMetrics(1);return MEDIA.capital+(sg?sg.capital:0);}\nfunction totalSoftwareYr(){const sg=signageMetrics(1);return MEDIA.softwareYr+(sg?sg.softwareYr:0);}\nfunction totalAdRev(k=1, locs=LOCS()){const sg=signageMetrics(k,locs);return mediaMetrics(k,locs).rev+(sg?sg.rev:0);}\n\n\/* ---- derive all metrics for one location at a period scale ----\n   KIOSK-DERIVED ONLY. Signage impressions never enter this function. ---- *\/\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\n  const direct = directSpend;\n  const influenced = directSpend*INFL_RATE;\n  const total = direct+influenced;\n  \/\/ -------- locality tax engine (attributed spend flows into each tax base) --------\n  const r = L.tax;\n  const taxableSpend = directSpend + influenced*0.5;      \/\/ direct + half of influenced re-spend\n  const hotelTOT   = taxableSpend*LODGING_SHARE*(r.tot\/100);\n  const salesTax   = taxableSpend*DINING_RETAIL_SHARE*(r.sales\/100);\n  const rentalTax  = taxableSpend*RENTAL_SHARE*(r.rental\/100);\n  const localSub   = hotelTOT+salesTax+rentalTax;         \/\/ stays local \/ funds the destination\n  const stateTax   = total*STATE_INCOME_RATE;             \/\/ state general-fund capture\n  const tax        = localSub+stateTax;\n  const recirc     = tax*RECIRC_MULT;                     \/\/ SOFT: public $ re-spent in community\n  const taxLocal=localSub, taxState=stateTax;\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  return sh.map(v=>base*v);\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,screens: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(key==='screens'){acc.screens+=(L.screens||0);}\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:NET_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,screens: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=['knx'];\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 platform modules and destination\n   campaign concepts. Each item splits across the funnel and renormalizes\n   to the scope totals so it reconciles with every view.\n================================================================== *\/\nconst MODULES=[\n  {id:'map',    name:'OMNI Maps',                   tag:'attractions', share:.16, dwell:80,  intentRel:0.9, convRel:0.8},\n  {id:'itin',   name:'OMNI Itinerary',              tag:'attractions', share:.13, dwell:150, intentRel:1.9, convRel:1.6},\n  {id:'deals',  name:'Deals & Passes',              tag:'events',      share:.11, dwell:120, intentRel:2.1, convRel:1.9},\n  {id:'dine',   name:'Dining & Restaurants',        tag:'dining',      share:.12, dwell:98,  intentRel:1.3, convRel:1.2},\n  {id:'wayfind',name:'OMNI Wayfinding',             tag:'attractions', share:.10, dwell:70,  intentRel:0.7, convRel:0.6},\n  {id:'events', name:'Events Calendar',             tag:'events',      share:.10, dwell:86,  intentRel:1.1, convRel:1.0},\n  {id:'photo',  name:'OMNI PhotoBooth',             tag:'events',      share:.07, dwell:130, intentRel:1.2, convRel:1.1},\n  {id:'stay',   name:'Lodging & Hotels',            tag:'hotels',      share:.07, dwell:110, intentRel:1.7, convRel:1.6},\n  {id:'guest',  name:'OMNI GuestBook (lead capture)',tag:'hotels',     share:.05, dwell:44,  intentRel:2.3, convRel:2.2},\n  {id:'lang',   name:'Multi-language & ADA',        tag:'attractions', share:.05, dwell:40,  intentRel:0.5, convRel:0.4},\n  {id:'phone',  name:'Mobile Handoff (Send to Phone)',tag:'hotels',    share:.04, dwell:34,  intentRel:2.4, convRel:2.3},\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:'sponsored', name:'Sponsored Placements (ad partners)', type:'Paid media', share:0.17, intentRel:0.9, convRel:0.8, cpi:0.55},\n  {id:'smokies', name:'Smokies Gateway Trip Planner', type:'Content', share:0.16, intentRel:1.5, convRel:1.4, cpi:0.85},\n  {id:'citypass', name:'Knoxville City Pass', type:'Pass \/ loyalty', share:0.14, intentRel:1.7, convRel:1.6, cpi:0.95},\n  {id:'itinerary', name:'Plan-Your-Trip Push', type:'Itinerary \/ CRM', share:0.12, intentRel:1.8, convRel:1.6, cpi:1.4},\n  {id:'dineweek', name:'Knoxville Restaurant Week', type:'Partner co-op', share:0.12, intentRel:1.3, convRel:1.2, cpi:1.1},\n  {id:'gameday', name:'Tennessee Game-Day Co-op', type:'Group \/ MICE', share:0.11, intentRel:1.1, convRel:1.3, cpi:0.8},\n  {id:'winter', name:'Winter-Shoulder Getaway', type:'Automation', share:0.09, intentRel:1.3, convRel:1.4, cpi:0.6},\n  {id:'flash', name:'Flash Deals & Coupons', type:'Promotions', share:0.09, intentRel:1.4, convRel:1.7, cpi:0.95},\n];\nconst AB_TESTS=[\n  {id:'t1', camp:'citypass', name:'City Pass \u2014 hero creative', a:'Market Square hero', b:'Sunsphere hero', split:0.5, bLift:1.13, conf:0.94, status:'B winning'},\n  {id:'t2', camp:'smokies', name:'Smokies planner \u2014 CTA copy', a:'\u201cPlan the park\u201d', b:'\u201cStay in Knoxville, see the park\u201d', split:0.5, bLift:1.17, conf:0.95, status:'B winning'},\n  {id:'t3', camp:'sponsored', name:'Sponsored placement \u2014 layout', a:'Carousel', b:'Full-card', split:0.5, bLift:0.95, conf:0.88, status:'A winning'},\n  {id:'t4', camp:'gameday', name:'Game-Day Co-op \u2014 offer framing', a:'\u201cWhere to watch\u201d', b:'\u201cMake it a weekend\u201d', split:0.5, bLift:1.07, conf:0.73, 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 USD only, no toggle\nconst FX={USD:1};\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> in <b>${locs.length} Knoxville 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. <b>${best.name}<\/b> leads on impact; <b>${rate.name}<\/b> engages the highest share of visitors at <b>${pct(rate.interactionRate)}<\/b>. Figures are modeled against Visit Knoxville\u2019s cited visitation of ~7.3M annual visitors using 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.`;\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:'hotel \/ transient-occupancy tax',salesTax:'sales & use tax',rentalTax:'rental-vehicle excise',stateTax:'state general-fund capture'}[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 public tax this period \u2014 <b>${money(m.taxLocal)}<\/b> in local tax (hotel occupancy, sales &amp; use, rental excise) that stays in the community and funds the destination, and <b>${money(m.taxState)}<\/b> captured by the state general fund. The single largest line is <b>${nm}<\/b> at <span class=\"em\">${money(top[1])}<\/span>. Re-spent through public budgets, the local share drives an estimated <span class=\"em\">${money(m.recirc)}<\/span> of induced local 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 public tax (hotel occupancy ${r.tot}%, sales &amp; use ${r.sales}%). ${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> \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 ${m.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(contextStrip()); wrap.lastChild.style.marginBottom='16px';\n  wrap.appendChild(h(aiBanner('TrueImpact AI \u00b7 Board-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 ${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  \/\/ 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\">Set against Visit Knoxville\\u2019s cited <b>~7.3M annual visitors<\/b>, this 20-kiosk network is modeled to capture <b>${num(m.exposures)}<\/b> kiosk encounters a year \\u2014 about <b>${(m.exposures\/7300000*100).toFixed(0)}%<\/b> of that base, which is what a Phase-1 pilot in the highest-traffic zones can defensibly reach. Seasonality carries a summer peak from Smokies gateway traffic and a pronounced October spike where leaf season and Tennessee home football overlap. The winter trough is the clearest target for shoulder-season automation.<\/div>`)]);\n  wrap.appendChild(trend);\n  return wrap;\n}\n\/* DESTINATION CONTEXT \u2014 the customer's own cited statistics, shown first so the\n   attributed TrueImpact number reads as a slice of this larger visitor economy. *\/\nfunction contextStrip(){\n  const c=card(CONTEXT_TITLE,'the destination\\u2019s own published statistics \u00b7 REAL \/ CITED','',' ');\n  const row=h('<div class=\"stat-inline\" style=\"margin-top:6px;gap:22px\"><\/div>');\n  CONTEXT.forEach(x=>row.appendChild(h(`<div class=\"si\"><b>${x.v}<\/b><span>${x.label}<br>${x.sub}<\/span><\/div>`)));\n  c.appendChild(row);\n  c.appendChild(h('<div class=\"note\"><b style=\"color:var(--text)\">This strip is the only REAL data on the page.<\/b> Every figure below it \\u2014 the network, the zones, the ramp, the engagement mix and all advertising revenue \\u2014 is a <b>modeled opportunity scenario<\/b> for a 20-kiosk + 5-screen Phase-1 deployment. TrueImpact\\u2019s attributed economic impact is a small, defensible slice of the visitor spending shown above, never the whole of it.<\/div>'));\n  return c;\n}\nfunction rampCard(k){\n  const c=card(`Deployment ramp \u00b7 ${NET_KIOSKS} kiosks + ${NET_SCREENS} 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 \u00b7 ${m.screens} screens live${add?` (+${add} kiosks)`:''}<\/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\/NET_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 on Market Square and at the Convention Center \u2014 walkable footfall paired with captive delegate audiences. Campus, the Smokies gateway, the waterfront and the Old City follow. Figures scale with units online, not with 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:'Hotel \/ TOT',color:'var(--s-local)'},\n    {v:b.salesTax,name:'Sales & use',color:'#33abe0'},\n    {v:b.rentalTax,name:'Rental-vehicle excise',color:'#7cc9e8'},\n    {v:b.stateTax,name:'State capture',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 ${periodLabel()}<\/div><\/div><div class=\"meta\">Tax rates<br><b style=\"color:var(--text);font-size:13px\">TOT ${r.tot}% \u00b7 Sales ${r.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 ${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:`Hotel \/ TOT (${r.tot}%)`,parts:[{v:b.hotelTOT,color:'var(--s-local)',name:'Hotel occupancy tax'}],total:b.hotelTOT},\n    {label:`Sales & use (${r.sales}%)`,parts:[{v:b.salesTax,color:'#33abe0',name:'Sales & use tax'}],total:b.salesTax},\n    {label:`Rental-vehicle (${r.rental}%)`,parts:[{v:b.rentalTax,color:'#7cc9e8',name:'Rental-vehicle excise'}],total:b.rentalTax},\n    {label:'State capture',parts:[{v:b.stateTax,color:'var(--s-state)',name:'State general fund'}],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 ${money(m.taxState)}<\/div><\/div>`));\n  kpis.appendChild(kpiTile('Hotel \/ TOT',money(b.hotelTOT),'','transient-occupancy tax \u2014 funds tourism'));\n  kpis.appendChild(kpiTile('Sales & use',money(b.salesTax),'','on taxable visitor spend'));\n  kpis.appendChild(kpiTile('Rental-vehicle excise',money(b.rentalTax),'','vehicle surcharge'));\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:'Hotel \/ TOT'},\n      {v:bb.salesTax,color:'#33abe0',name:'Sales & use'},\n      {v:bb.rentalTax,color:'#7cc9e8',name:'Rental-vehicle excise'},\n      {v:bb.stateTax,color:'var(--s-state)',name:'State capture'},\n    ]};}).sort((a,b)=>b.total-a.total);\n  const c1=card('Estimated tax revenue by zone','hotel occupancy \u00b7 sales &amp; use \u00b7 rental excise \u00b7 state capture \u00b7 click a bar to drill','',' ');\n  c1.appendChild(rankedStacked(rows,{pl:120,w:680}));\n  c1.appendChild(legend([{name:'Hotel \/ TOT',color:'var(--s-local)'},{name:'Sales & use',color:'#33abe0'},{name:'Rental-vehicle excise',color:'#7cc9e8'},{name:'State capture',color:'var(--s-state)'}]));\n  c1.appendChild(h(`<div class=\"note\">${TAX_NOTE}<\/div>`));\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>TOT %<\/th><th>Hotel \/ TOT<\/th><th>Sales &amp; use<\/th><th>Rental excise<\/th><th>State capture<\/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 ${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);const tot=m.rev+(sg?sg.rev:0);\n  return `Across <b>${m.kiosks} kiosks<\/b> and <b>${sg?sg.screens:0} digital signage screens<\/b>, TrueImpact models <span class=\"em\">${money(tot)}<\/span> a year in Total Advertising Revenue at the mid scenario (range ${money(m.band.floor+(sg?sg.band.floor:0))}\u2013${money(m.band.strong+(sg?sg.band.strong:0))}) \u2014 <b>${money(m.rev)}<\/b> from kiosks and <b>${money(sg?sg.rev:0)}<\/b> from signage \u2014 against an estimated <b>${num(m.impressions+(sg?sg.impressions:0))}<\/b> screen impressions. Kiosk CPM lands 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 annual platform fee many times over and repays the full hardware investment in roughly <b>${(totalCapital()\/tot).toFixed(1)} years<\/b> \u2014 the self-funding engine that lets a destination run this program without adding a budget line. <b>All advertising figures on this page are modeled<\/b> from TrueOmni platform benchmarks, not a signed rate card.`;}\nfunction mediaMiniCard(k){const m=mediaMetrics(k), sg=signageMetrics(k), tot=m.rev+(sg?sg.rev:0);\n  const c=card('Media Network \u00b7 potential advertising revenue','the self-funding layer \u00b7 kiosks + digital signage','',' ');\n  c.appendChild(h(`<div class=\"stat-inline\" style=\"margin-top:2px\"><div class=\"si\"><b>${money(tot)}<\/b><span>Total Advertising Revenue \/ yr<\/span><\/div><div class=\"si\"><b>${num(m.impressions+(sg?sg.impressions:0))}<\/b><span>Screen Impressions \/ yr<\/span><\/div><div class=\"si\"><b>${pct(MEDIA.fillMature)}<\/b><span>Fill Rate at maturity<\/span><\/div><div class=\"si\"><b>${money(m.cpm)}<\/b><span>CPM (kiosk)<\/span><\/div><div class=\"si\"><b style=\"color:var(--good)\">${(totalCapital()\/tot).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); const sg=signageMetrics(k);\n  const tot=m.rev+(sg?sg.rev:0); const totImpr=m.impressions+(sg?sg.impressions: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\"><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 advertising revenue potential<\/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\">Fill Rate at maturity<br><b style=\"color:var(--text);font-size:14px\">${pct(MEDIA.fillMature)} kiosk \u00b7 ${sg?pct(SIGNAGE.fillMature):'\u2014'} signage<br>${m.kiosks} kiosks \u00b7 ${sg?sg.screens:0} screens<\/b><\/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\">Total Advertising Revenue<\/div><div class=\"k-val\"><span class=\"cur\">${curSym()}<\/span>${money(tot).replace(curSym(),'')}<\/div><div class=\"k-delta up\">\u25b2 scales with fill, kiosks + screens<\/div><div class=\"k-foot\">Kiosks ${money(m.rev)} + digital signage ${money(sg?sg.rev:0)} \u00b7 range ${money(m.band.floor+(sg?sg.band.floor:0))}\u2013${money(m.band.strong+(sg?sg.band.strong:0))}<\/div><\/div>`));\n  kpis.appendChild(kpiTile('Screen Impressions \/ yr',num(totImpr),'',`kiosk ${num(m.impressions)} + signage ${num(sg?sg.impressions:0)}`));\n  kpis.appendChild(kpiTile('Effective CPM',money(m.cpm),'','kiosk inventory \u00b7 in line with DOOH benchmarks'));\n  kpis.appendChild(kpiTile('Covers annual software',(tot\/totalSoftwareYr()).toFixed(0)+'\u00d7','',`vs ${money(totalSoftwareYr())}\/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('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(`Ad-revenue ramp \u00b7 ${NET_KIOSKS} kiosks + ${NET_SCREENS} screens`,'builds with units 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 sgp=signageMetrics(k,locs);const fill=MEDIA.fillByPhase[ph.n];const sfill=SIGNAGE.fillByPhase[ph.n];const rev=mm.rev*(fill\/MEDIA.fillMature)+(sgp?sgp.rev*(sfill\/SIGNAGE.fillMature):0);\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 ${sgp?sgp.screens:0} screens \u00b7 ${pct(fill)} kiosk fill \/ ${pct(sfill)} signage 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\/NET_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  const sf=card('The self-funding math','advertising vs. program cost','',' ');\n  sf.appendChild(h(`<div class=\"stat-inline\" style=\"margin-top:4px\"><div class=\"si\"><b>${money(tot)}<\/b><span>Total Advertising Revenue \/ yr (mid)<\/span><\/div><div class=\"si\"><b>${money(totalSoftwareYr())}<\/b><span>annual software (kiosk + signage)<\/span><\/div><div class=\"si\"><b>${money(totalCapital())}<\/b><span>one-time capital (kiosk + signage)<\/span><\/div><div class=\"si\"><b style=\"color:var(--good)\">${(totalCapital()\/tot).toFixed(1)} yr<\/b><span>capital payback (mid)<\/span><\/div><\/div>`));\n  sf.appendChild(h('<div class=\"note\"><b style=\"color:var(--text)\">MODELED.<\/b> At the mid scenario, advertising covers the annual software many times over and repays the full hardware investment in about a year \u2014 the self-funding engine that lets a destination run this program without a new line item. Built from the TrueOmni platform benchmark of $500\u20131,500 per kiosk per month and $200\u2013800 per screen per month at mature fill; refine with a real rate card, fill curve, and revenue split.<\/div>'));\n  wrap.appendChild(sf);\n\n  \/* ---- DIGITAL SIGNAGE \u2014 reach, not interactions. Never folded into\n     economic impact, attributed spend or tax. ---- *\/\n  if(sg){\n    const sgHead=card('Digital Signage \u00b7 the broadcast layer',`${sg.screens} non-interactive screens \u2014 video walls, portrait posters, wayfinding &amp; directory boards`,'',' ');\n    sgHead.appendChild(h(`<div class=\"stat-inline\" style=\"margin-top:6px;gap:22px\"><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>Reach (unique passers-by)<\/span><\/div><div class=\"si\"><b>${SIGNAGE.dwellSec}s<\/b><span>Avg Dwell<\/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<\/span><\/div><div class=\"si\"><b>${pct(SIGNAGE.fillMature)}<\/b><span>Signage Fill Rate<\/span><\/div><div class=\"si\"><b>${money(sg.rev\/sg.screens)}<\/b><span>Revenue per Screen \/ yr<\/span><\/div><\/div>`));\n    sgHead.appendChild(h('<div class=\"note\"><b style=\"color:var(--text)\">Signage broadcasts; kiosks transact.<\/b> These impressions are <b>reach<\/b> \u2014 they are deliberately excluded from interactions, attributed visitor spend, economic impact and tax. Signage contributes advertising revenue and audience scale only. Conflating the two is what makes an impact report indefensible.<\/div>'));\n    wrap.appendChild(sgHead); wrap.lastChild.style.marginBottom='16px';\n\n    const r3=h('<div class=\"grid row-2\" style=\"margin-bottom:16px\"><\/div>');\n    const fmtCard=card('Revenue by screen format','share of signage advertising revenue','',' ');\n    const fl=h('<div class=\"loclist\"><\/div>'); const fmax=Math.max(...sg.byFormat.map(x=>x.v));\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); r3.appendChild(fmtCard);\n\n    const mixCard=card('Signage vs kiosk revenue mix','where Total Advertising Revenue comes from','',' ');\n    const mixParts=[{v:m.rev,name:'Interactive kiosks',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\"><\/div>'); mrow.appendChild(donut(mixParts));\n    const ml=h('<div style=\"flex:1;min-width:180px\"><\/div>');\n    mixParts.forEach(pp=>ml.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:${pp.color}\"><\/span>${pp.name}<\/div><div class=\"lr-val\">${money(pp.v)}<div class=\"lr-sub\" style=\"font-weight:600\">${pct(pp.v\/tot)}<\/div><\/div><\/div>`)));\n    mrow.appendChild(ml); mixCard.appendChild(mrow);\n    mixCard.appendChild(h(`<div class=\"note\">Screens carry a lower unit price than kiosks but a far lower cost per unit \u2014 ${money(SIGNAGE.capitalPerScreen)} of capital each against ${money(MEDIA.capital\/NET_KIOSKS)} per kiosk. Expanding the screen count is the cheapest way to add both audience reach and sellable inventory.<\/div>`));\n    r3.appendChild(mixCard);\n    wrap.appendChild(r3);\n\n    const zc=card('Screens by zone','where the broadcast layer sits','',' ');\n    const zl=h('<div class=\"loclist\"><\/div>');\n    const withScreens=LOCS().filter(L=>(L.screens||0)>0);\n    const zmax=Math.max(...withScreens.map(L=>L.screens));\n    withScreens.slice().sort((a,b)=>b.screens-a.screens).forEach(L=>{\n      const zr=deriveMetrics(L,k); const zreach=zr.exposures*SIGNAGE.viewShare;\n      zl.appendChild(h(`<div class=\"locrow\" style=\"cursor:pointer\"><div><div class=\"lr-name\">${L.name}<\/div><div class=\"lr-sub\">${L.kiosks} kiosks \u00b7 ${L.screens} screens \u00b7 ${L.sites}<\/div><\/div><div class=\"lr-val\">${num(zreach*SIGNAGE.imprPerPasserby)}<div class=\"lr-sub\" style=\"font-weight:600\">screen impressions \/ yr<\/div><\/div><div class=\"lr-bar\"><i style=\"width:${(L.screens\/zmax*100).toFixed(0)}%\"><\/i><\/div><\/div>`));});\n    zc.appendChild(zl);\n    zc.appendChild(h(`<div class=\"note\"><b style=\"color:var(--text)\">MODELED placement.<\/b> The 5-screen base is deliberately conservative \u2014 it sits in the highest-footfall Phase-1 and Phase-2 zones only. Real deployments typically run 2\u20134 screens per kiosk indoors, so screen count (and signage revenue) is the most elastic number on this page.<\/div>`));\n    wrap.appendChild(zc);\n  }\n  return wrap;\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 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\/\/ 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 Tableau de bord ex\u00e9cutif \u00b7 Visitez Knoxville<\/p>","protected":false},"author":11,"featured_media":57099,"comment_status":"open","ping_status":"open","sticky":false,"template":"elementor_canvas","format":"standard","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_primary_category":0,"footnotes":""},"categories":[1171],"tags":[1172,1185],"class_list":["post-58134","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-true-impact","tag-trueimpact","tag-visit-knoxville"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v27.9 (Yoast SEO v28.4) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>VisitKnoxville - TrueOmni<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/trueomni.com\/french\/true-impact\/visitknoxville\/\" \/>\n<meta property=\"og:locale\" content=\"fr_FR\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"VisitKnoxville\" \/>\n<meta property=\"og:description\" content=\"TrueImpact \u2014 Executive Dashboard \u00b7 Visit Knoxville\" \/>\n<meta property=\"og:url\" content=\"https:\/\/trueomni.com\/french\/true-impact\/visitknoxville\/\" \/>\n<meta property=\"og:site_name\" content=\"TrueOmni\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/trueomni\/\" \/>\n<meta property=\"article:published_time\" content=\"2026-08-06T18:31:07+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-08-06T18:31:50+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=\"author\" content=\"Ruben Ramirez\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"\u00c9crit par\" \/>\n\t<meta name=\"twitter:data1\" content=\"Ruben Ramirez\" \/>\n\t<meta name=\"twitter:label2\" content=\"Dur\u00e9e de lecture estim\u00e9e\" \/>\n\t<meta name=\"twitter:data2\" content=\"1 minute\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/trueomni.com\\\/true-impact\\\/visitknoxville\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/trueomni.com\\\/true-impact\\\/visitknoxville\\\/\"},\"author\":{\"name\":\"Ruben Ramirez\",\"@id\":\"http:\\\/\\\/trueomni.com\\\/#\\\/schema\\\/person\\\/063bf88aea0a02d03a0115375148bf2b\"},\"headline\":\"VisitKnoxville\",\"datePublished\":\"2026-08-06T18:31:07+00:00\",\"dateModified\":\"2026-08-06T18:31:50+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/trueomni.com\\\/true-impact\\\/visitknoxville\\\/\"},\"wordCount\":6,\"commentCount\":0,\"publisher\":{\"@id\":\"http:\\\/\\\/trueomni.com\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/trueomni.com\\\/true-impact\\\/visitknoxville\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/trueomni.com\\\/wp-content\\\/uploads\\\/2026\\\/05\\\/Travel-TrueOmni-TNT-IVE-Kiosks-2026-Website-scaled.png\",\"keywords\":[\"TrueImpact\",\"Visit Knoxville\"],\"articleSection\":[\"True Impact\"],\"inLanguage\":\"fr-FR\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/trueomni.com\\\/true-impact\\\/visitknoxville\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/trueomni.com\\\/true-impact\\\/visitknoxville\\\/\",\"url\":\"https:\\\/\\\/trueomni.com\\\/true-impact\\\/visitknoxville\\\/\",\"name\":\"VisitKnoxville - TrueOmni\",\"isPartOf\":{\"@id\":\"http:\\\/\\\/trueomni.com\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/trueomni.com\\\/true-impact\\\/visitknoxville\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/trueomni.com\\\/true-impact\\\/visitknoxville\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/trueomni.com\\\/wp-content\\\/uploads\\\/2026\\\/05\\\/Travel-TrueOmni-TNT-IVE-Kiosks-2026-Website-scaled.png\",\"datePublished\":\"2026-08-06T18:31:07+00:00\",\"dateModified\":\"2026-08-06T18:31:50+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/trueomni.com\\\/true-impact\\\/visitknoxville\\\/#breadcrumb\"},\"inLanguage\":\"fr-FR\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/trueomni.com\\\/true-impact\\\/visitknoxville\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"fr-FR\",\"@id\":\"https:\\\/\\\/trueomni.com\\\/true-impact\\\/visitknoxville\\\/#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\\\/true-impact\\\/visitknoxville\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"http:\\\/\\\/trueomni.com\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"VisitKnoxville\"}]},{\"@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\":\"fr-FR\"},{\"@type\":\"Organization\",\"@id\":\"http:\\\/\\\/trueomni.com\\\/#organization\",\"name\":\"TRUE OMNI LLC\",\"alternateName\":\"TRUE OMNI LLC\",\"url\":\"http:\\\/\\\/trueomni.com\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"fr-FR\",\"@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\"},{\"@type\":\"Person\",\"@id\":\"http:\\\/\\\/trueomni.com\\\/#\\\/schema\\\/person\\\/063bf88aea0a02d03a0115375148bf2b\",\"name\":\"Ruben Ramirez\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"fr-FR\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/e5f68af99ae89c8a9e4ccd0d7e8d00309bb80f5b0cd9c21768a7dd9f6daea70f?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/e5f68af99ae89c8a9e4ccd0d7e8d00309bb80f5b0cd9c21768a7dd9f6daea70f?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/e5f68af99ae89c8a9e4ccd0d7e8d00309bb80f5b0cd9c21768a7dd9f6daea70f?s=96&d=mm&r=g\",\"caption\":\"Ruben Ramirez\"},\"url\":\"https:\\\/\\\/trueomni.com\\\/french\\\/author\\\/ruben\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"VisitKnoxville - TrueOmni","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\/french\/true-impact\/visitknoxville\/","og_locale":"fr_FR","og_type":"article","og_title":"VisitKnoxville","og_description":"TrueImpact \u2014 Executive Dashboard \u00b7 Visit Knoxville","og_url":"https:\/\/trueomni.com\/french\/true-impact\/visitknoxville\/","og_site_name":"TrueOmni","article_publisher":"https:\/\/www.facebook.com\/trueomni\/","article_published_time":"2026-08-06T18:31:07+00:00","article_modified_time":"2026-08-06T18:31:50+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"}],"author":"Ruben Ramirez","twitter_card":"summary_large_image","twitter_misc":{"\u00c9crit par":"Ruben Ramirez","Dur\u00e9e de lecture estim\u00e9e":"1 minute"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/trueomni.com\/true-impact\/visitknoxville\/#article","isPartOf":{"@id":"https:\/\/trueomni.com\/true-impact\/visitknoxville\/"},"author":{"name":"Ruben Ramirez","@id":"http:\/\/trueomni.com\/#\/schema\/person\/063bf88aea0a02d03a0115375148bf2b"},"headline":"VisitKnoxville","datePublished":"2026-08-06T18:31:07+00:00","dateModified":"2026-08-06T18:31:50+00:00","mainEntityOfPage":{"@id":"https:\/\/trueomni.com\/true-impact\/visitknoxville\/"},"wordCount":6,"commentCount":0,"publisher":{"@id":"http:\/\/trueomni.com\/#organization"},"image":{"@id":"https:\/\/trueomni.com\/true-impact\/visitknoxville\/#primaryimage"},"thumbnailUrl":"https:\/\/trueomni.com\/wp-content\/uploads\/2026\/05\/Travel-TrueOmni-TNT-IVE-Kiosks-2026-Website-scaled.png","keywords":["TrueImpact","Visit Knoxville"],"articleSection":["True Impact"],"inLanguage":"fr-FR","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/trueomni.com\/true-impact\/visitknoxville\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/trueomni.com\/true-impact\/visitknoxville\/","url":"https:\/\/trueomni.com\/true-impact\/visitknoxville\/","name":"VisitKnoxville - TrueOmni","isPartOf":{"@id":"http:\/\/trueomni.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/trueomni.com\/true-impact\/visitknoxville\/#primaryimage"},"image":{"@id":"https:\/\/trueomni.com\/true-impact\/visitknoxville\/#primaryimage"},"thumbnailUrl":"https:\/\/trueomni.com\/wp-content\/uploads\/2026\/05\/Travel-TrueOmni-TNT-IVE-Kiosks-2026-Website-scaled.png","datePublished":"2026-08-06T18:31:07+00:00","dateModified":"2026-08-06T18:31:50+00:00","breadcrumb":{"@id":"https:\/\/trueomni.com\/true-impact\/visitknoxville\/#breadcrumb"},"inLanguage":"fr-FR","potentialAction":[{"@type":"ReadAction","target":["https:\/\/trueomni.com\/true-impact\/visitknoxville\/"]}]},{"@type":"ImageObject","inLanguage":"fr-FR","@id":"https:\/\/trueomni.com\/true-impact\/visitknoxville\/#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\/true-impact\/visitknoxville\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"http:\/\/trueomni.com\/"},{"@type":"ListItem","position":2,"name":"VisitKnoxville"}]},{"@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":"fr-FR"},{"@type":"Organization","@id":"http:\/\/trueomni.com\/#organization","name":"TRUE OMNI LLC","alternateName":"TRUE OMNI LLC","url":"http:\/\/trueomni.com\/","logo":{"@type":"ImageObject","inLanguage":"fr-FR","@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"},{"@type":"Person","@id":"http:\/\/trueomni.com\/#\/schema\/person\/063bf88aea0a02d03a0115375148bf2b","name":"Ruben Ramirez","image":{"@type":"ImageObject","inLanguage":"fr-FR","@id":"https:\/\/secure.gravatar.com\/avatar\/e5f68af99ae89c8a9e4ccd0d7e8d00309bb80f5b0cd9c21768a7dd9f6daea70f?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/e5f68af99ae89c8a9e4ccd0d7e8d00309bb80f5b0cd9c21768a7dd9f6daea70f?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/e5f68af99ae89c8a9e4ccd0d7e8d00309bb80f5b0cd9c21768a7dd9f6daea70f?s=96&d=mm&r=g","caption":"Ruben Ramirez"},"url":"https:\/\/trueomni.com\/french\/author\/ruben\/"}]}},"_links":{"self":[{"href":"https:\/\/trueomni.com\/french\/wp-json\/wp\/v2\/posts\/58134","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/trueomni.com\/french\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/trueomni.com\/french\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/trueomni.com\/french\/wp-json\/wp\/v2\/users\/11"}],"replies":[{"embeddable":true,"href":"https:\/\/trueomni.com\/french\/wp-json\/wp\/v2\/comments?post=58134"}],"version-history":[{"count":4,"href":"https:\/\/trueomni.com\/french\/wp-json\/wp\/v2\/posts\/58134\/revisions"}],"predecessor-version":[{"id":58138,"href":"https:\/\/trueomni.com\/french\/wp-json\/wp\/v2\/posts\/58134\/revisions\/58138"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/trueomni.com\/french\/wp-json\/wp\/v2\/media\/57099"}],"wp:attachment":[{"href":"https:\/\/trueomni.com\/french\/wp-json\/wp\/v2\/media?parent=58134"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/trueomni.com\/french\/wp-json\/wp\/v2\/categories?post=58134"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/trueomni.com\/french\/wp-json\/wp\/v2\/tags?post=58134"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}