{
  "version": 3,
  "sources": ["deliverable-model.js"],
  "sourcesContent": ["/* One reading of a crawl payload, shared by every surface that draws it.\n *\n * The crawl hub, the four specialist viewers and the aggregated report all take\n * the same `/api/audit` response and each had grown its OWN copy of \"what this\n * data means\" — three different persona joins, three severity sorts, three\n * gap-diagnosis extractors. They drifted: the query explorer keyed personas by\n * id while the hub still fell back to a role string, so the same crawl showed\n * different group counts on two pages.\n *\n * This module owns the MODEL, not the markup. Surfaces legitimately look\n * different (an interactive dark card vs. a row on printed paper), so they keep\n * their own renderers — but they must agree on how many buyers there are, which\n * finding is most severe, and which action answers which question.\n *\n * Everything here is defensive by design: payloads vary between rich and legacy\n * deliveries, and gated fields arrive as null rather than absent.\n */\n\nexport const arr = (v) => (Array.isArray(v) ? v : []);\n\n// The crawlers that actually feed an AI answer engine — the only ones whose\n// blocking can cost a citation. Matched on the user-agent token, lowercased, so\n// a payload writing \"GPTBot\" or \"gptbot\" lands the same. Deliberately a\n// allow-list: an unrecognised crawler is treated as NOT answer-engine, so a new\n// name shows in the table without silently raising the report's urgency.\nconst ANSWER_ENGINE_CRAWLERS = new Set([\n  \"gptbot\", \"chatgpt-user\", \"oai-searchbot\",              // OpenAI\n  \"claudebot\", \"claude-user\", \"claude-searchbot\",         // Anthropic\n  \"perplexitybot\", \"perplexity-user\",                     // Perplexity\n  \"google-extended\",                                      // Google's AI control\n  \"applebot-extended\",                                    // Apple Intelligence\n]);\nexport const isAnswerEngineCrawler = (name) =>\n  ANSWER_ENGINE_CRAWLERS.has(String(name || \"\").trim().toLowerCase());\nexport const num = (v) => (v == null || isNaN(Number(v)) ? null : Math.round(Number(v)));\nexport const nf = (v) => (v == null ? \"—\" : Number(v).toLocaleString());\nexport const slug = (s) => String(s ?? \"\").toLowerCase().replace(/[^a-z0-9]+/g, \"-\").replace(/^-|-$/g, \"\").slice(0, 48) || \"x\";\n\n/* ── presence: named, cited, both, neither ───────────────────────────────────\n *\n * A question sits in exactly one of four states. The answer says your name\n * (named), an engine used one of your pages as a source (cited), both, or\n * neither. These three helpers are the ONE derivation of that split.\n *\n * They live here because they were written twice. The reader grew them inline\n * on 2026-09-08 while Schematic's report was being audited; the aggregated\n * report and the crawl hub, which draw the same numbers through\n * mountVisibility, never got them — so for a day the same crawl read\n * \"139 absent\" on one page and \"150 minus visible\" on another, and only one of\n * the two said which rule had scored it. A surface may still choose how to draw\n * this. It does not get to choose what it counts.\n */\n\n/* The payload carries `presence` once a crawl has been aggregated under the\n   named-vs-cited rule. Older crawls get it counted from the per-question\n   verdicts when the caller has them, so nothing draws a blank where a number\n   belongs — pass an array of { named, cited }. See scoredV2Of for why a count\n   made here is a floor rather than a correction. */\nexport function presenceOf(headline, verdicts) {\n  const p = headline?.presence;\n  if (p && p.total) return p;\n  const vs = arr(verdicts);\n  if (!vs.length) return {};\n  const out = { named_and_cited: 0, named_only: 0, cited_only: 0, absent: 0, total: vs.length };\n  for (const v of vs) {\n    const named = !!v?.named, cited = !!v?.cited;\n    out[named && cited ? \"named_and_cited\" : named ? \"named_only\" : cited ? \"cited_only\" : \"absent\"]++;\n  }\n  out.cited_total = out.named_and_cited + out.cited_only;\n  return out;\n}\n\n/* WHICH RULE SCORED THIS CRAWL.\n   The payload carries `presence` only when the numbers were produced under the\n   named-vs-cited rule. Without it a surface can still split the verdicts four\n   ways — and should, because that catches the questions the old scorer called\n   absent while an engine cited them — but it cannot recover the opposite\n   error, the ones the old scorer called VISIBLE on a citation alone. So on\n   those crawls the split is a floor and the headline is a ceiling, and every\n   surface has to SAY so rather than drawing a corrected shape over an\n   uncorrected number. Defaults to the new rule: a caller that omits the flag\n   is never silently downgraded. */\nexport const scoredV2Of = (headline) => !!(headline?.presence && headline.presence.total);\n\n/* ABSENT MEANS NEITHER NAMED NOR CITED.\n   map_numbers computes headline.invisible as total minus visible, so under the\n   named-vs-cited rule it silently folds the cited-but-not-named questions in\n   with the absences. Every tile that prints this number is labelled \"absent\"\n   and defined as \"no engine names you in, and no engine cites you for\", and\n   links to a list filtered to exactly that. Prefer the presence count, which\n   means what the label says; fall back only where there is no presence block. */\nexport const absentOf = (presence, headline) =>\n  (presence?.absent != null ? presence.absent : (headline?.invisible ?? null));\n\n/* ── the client's host, from whichever field carries it ──────────────────────\n *\n * A registrable hostname, or \"\". Four fields can hold it and no single one is\n * always populated, so every surface that wants the client's domain walks the\n * same chain here rather than picking a favourite.\n *\n * The reader used to take `query_explorer_url || title` and cut the title at\n * its first space. That is fine while query_explorer_url is set and silently\n * wrong when it is not: \"Charlotte Corporate Counsel\" became the host\n * \"charlotte\", /api/brand-icon rejects a name with no dot, and the client's\n * logo fell back to a monogram on the reader while the report — which reads\n * foundation.client_url — drew it correctly on the same crawl. The preview\n * harness fabricates query_explorer_url, so nothing local ever reproduced it.\n *\n * isBrandHost is the gate before any icon lookup: no dot, no request.\n */\nexport const isBrandHost = (h) => /^[a-z0-9-]+(\\.[a-z0-9-]+)+$/.test(String(h || \"\").trim());\n\nexport function hostFromAudit(a, override) {\n  const f = a?.foundation || null;\n  for (const raw of [override, a?.query_explorer_url, f?.client_url, a?.client_url]) {\n    const h = String(raw || \"\").trim()\n      .replace(/^https?:\\/\\//i, \"\").replace(/^www\\./i, \"\")\n      .split(\"/\")[0].replace(/\\s.*$/, \"\").toLowerCase();\n    if (isBrandHost(h)) return h;\n  }\n  /* Last resort only: a title is a display string, not a URL. Accept it solely\n     when it already parses as a hostname (\"acme.com Visibility Audit\"), never\n     by chopping prose at a space. */\n  const t = String(a?.title || \"\").trim().split(/\\s+/)[0].replace(/^https?:\\/\\//i, \"\").replace(/^www\\./i, \"\").toLowerCase();\n  return isBrandHost(t) ? t : \"\";\n}\n\n/* gap_diagnosis is a string on some audits and a structured object on others.\n   Deliberately falls back to \"the first string-valued key\" rather than dropping\n   the field: a diagnosis we can't name is still worth showing. */\nexport const gapText = (g) => (!g ? \"\" : typeof g === \"string\" ? g\n  : (g.summary || g.gap || g.problem || g.text || Object.values(g).find((v) => typeof v === \"string\") || \"\"));\nexport const layerKey = (it) => String(it?.layer || \"\").toUpperCase().replace(/_.*$/, \"\");\nexport const layerLabel = (it) => String(it?.layer || \"\").toUpperCase().replace(\"_\", \"/\");\nexport const bulletText = (b) => (typeof b === \"string\" ? b\n  : (b && (b.action || b.title || b.recommendation || b.text || b.summary)) || \"\");\nexport const nameOf = (v) => (typeof v === \"string\" ? v : (v?.name || v?.feature || v?.title || \"\"));\n\nexport const SEV_ORDER = { critical: 0, high: 1, medium: 2, low: 3 };\nexport const sevOf = (x) => String(x?.severity || \"low\").toLowerCase();\n\nexport function severityCounts(findings) {\n  const by = { critical: 0, high: 0, medium: 0, low: 0 };\n  for (const f of arr(findings)) { const s = sevOf(f); if (s in by) by[s]++; }\n  by.total = arr(findings).length;\n  by.hot = by.critical + by.high;\n  return by;\n}\n\n/* Anchor ids. Every surface links with these, so they're defined once — a\n   \"copy link to this finding\" on the hub has to land on the same element the\n   report renders. */\nexport const anchors = {\n  section: { foundations: \"s1\", questions: \"s2\", visibility: \"s3\", actions: \"s4\", method: \"s5\" },\n  finding: (i) => `f-${i}`,\n  persona: (key) => `p-${slug(key)}`,\n  group: (key) => `qp-${slug(key)}`,\n  action: (itemId) => `a-${slug(itemId)}`,\n  competitor: (name) => `c-${slug(name)}`,\n  feature: (name) => `ft-${slug(name)}`,\n  objection: (name) => `ob-${slug(name)}`,\n  trigger: (name) => `tg-${slug(name)}`,\n};\n\n/* Anchors that shipped before the aggregated report existed — in emails, in\n   copied links, in the hub's own markup. Cheap to honour, and a dead link into\n   your own report is the kind of thing customers remember. */\nexport const LEGACY_ANCHORS = {\n  \"chapter-findings\": \"s1-findings\",\n  \"chapter-overview\": \"s1\",\n  \"chapter-ai-crawler-access\": \"s1-crawlers\",\n  \"chapter-content-quality\": \"s1-gauges\",\n  visibility: \"s3\",\n  competitive: \"s3-sov\",\n  citations: \"s3-citations\",\n  queries: \"s2\",\n  plan: \"s4\",\n  summary: \"cover\",\n};\n\n/* Buyer personas, resolved once.\n *\n * Grouping is on persona_id (the KG's own key) with the role string as a\n * fallback: two personas that happen to share a role must not merge, and one\n * whose role was reworded mid-series must not split. ATDC ships two profiles\n * that both render as \"Founder & CEO\" — grouping by id keeps their counts\n * honest, and `dupe` flags the collision rather than papering over it (the real\n * fix belongs upstream in the knowledge graph).\n */\nexport function personaGroups({ queries, foundationPersonas, queryPersonas }) {\n  const qs = arr(queries);\n  const groups = new Map();\n  for (const q of qs) {\n    const k = personaKeyOf(q);\n    if (!groups.has(k)) groups.set(k, []);\n    groups.get(k).push(q);\n  }\n  const byKey = new Map();\n  const index = (p) => { for (const k of [p?.id, p?.name, p?.role]) if (k && String(k).trim() && !byKey.has(String(k).trim().toLowerCase())) byKey.set(String(k).trim().toLowerCase(), p); };\n  arr(foundationPersonas).forEach(index);\n  arr(queryPersonas).forEach(index);\n\n  const rows = [...groups.entries()].map(([key, list]) => {\n    const p = byKey.get(String(key).trim().toLowerCase());\n    return {\n      key,\n      persona: p || null,\n      label: p?.role || p?.name || key,\n      sub: [p?.department, p?.seniority].filter(Boolean).join(\" · \"),\n      n: list.length,\n      queries: list,\n      anchor: anchors.group(key),\n    };\n  }).sort((x, y) => y.n - x.n);\n\n  const seen = new Map();\n  rows.forEach((r) => seen.set(r.label, (seen.get(r.label) || 0) + 1));\n  rows.forEach((r) => { r.dupe = seen.get(r.label) > 1; });\n  return rows;\n}\nexport const personaKeyOf = (q) => String(q?.persona_id || q?.persona || \"Buyer\").trim() || \"Buyer\";\nexport const queryText = (q) => String(q?.text || q?.query || q?.question || \"\");\n\n/* The whole crawl as one object. Every surface reads from this; nothing reads\n   the raw payload twice. */\nexport function buildModel(res, { name, host, ordinal } = {}) {\n  const a = res?.audit || {};\n  const f = a.foundation || null;\n  const qx = a.query_explorer || null;\n  const nRaw = a.numbers || null;\n  const H = nRaw ? (nRaw.headline || nRaw) : null;\n  const unlocked = !!res?.unlocked;\n\n  const cycles = arr(res?.series_cycles);\n  const cur = cycles.find((c) => c.is_current) || cycles[cycles.length - 1] || null;\n  const ord = ordinal || cur?.ordinal || 1;\n  /* The cover's eyebrow already reads \"AI Visibility Report\", and audits.title\n     has always carried the product name too (\"acme.com Visibility Audit\"), so\n     the cover printed its own category twice — and the download came out\n     \"…-visibility-audit-visibility-report-….pdf\".\n     Stripped HERE, at display, rather than backfilled: every historical row\n     says \"Audit\", every new one says \"Report\", and both render as just the\n     client. If the wording changes again, old titles still land clean. */\n  const stripProduct = (s) => String(s || \"\")\n    .replace(/\\s*[-–—·|]?\\s*(?:AI\\s+)?Visibility\\s+(?:Report|Audit)\\s*$/i, \"\").trim();\n  /* THE BUSINESS NAME BEATS THE ROW TITLE.\n     `audits.title` is composed from whatever URL was typed into the order form,\n     so a client who entered \"http://rovstudios.com\" got that string set as the\n     cover's 3rem headline, repeated in every running head, in the PDF's title\n     metadata and in the download filename — while the foundation payload\n     carried \"Range of View Studios\" the whole time, because the analysis\n     resolves the real name from the site. The foundation is the better source\n     and it is already in hand everywhere this model is built.\n     Still second to `name`: an operator who passes ?n= is overriding on\n     purpose, and that has to keep winning. */\n  const tidyName = (s) => String(s || \"\").trim()\n    .replace(/^https?:\\/\\//i, \"\").replace(/\\/+$/, \"\");\n  const title = tidyName(name || f?.client_name || stripProduct(a.title) || a.title) || \"Your crawl\";\n  /* Protocol and `www.` go; the PATH stays. Stripping it assumed every audit is\n     scoped to a homepage, which is where the denominator lives — but a\n     product-scoped cycle is scoped to a product page, and dropping the path\n     renamed Tonic Textual's review after the whole of tonic.ai on the cover and\n     in section 1, while section 4 and the running head both carried the real\n     URL. Same document, three different answers to \"what did you audit\". Hosts\n     without a path are untouched, so this only shows up where one exists. */\n  /* The PATH is kept here (see below), so this cannot go through hostFromAudit,\n     which returns a bare host. The chain is the same one, in the same order. */\n  const site = (host || a.query_explorer_url || f?.client_url || \"\")\n    .replace(/^https?:\\/\\//, \"\").replace(/^www\\./, \"\").replace(/\\/+$/, \"\");\n\n  /* The bare host, for the one claim that is about the ORIGIN rather than the\n     audited path. robots.txt lives at the root and nowhere else, so a\n     path-scoped cycle that says \"orkin.com/commercial's robots.txt\" names a\n     file that cannot exist — and contradicts section 4, which links the real\n     www.orkin.com/robots.txt two pages later. Everything else on the cover and\n     in section 1 wants the path, which is why `site` keeps it. */\n  /* The bare host for icon lookups and root-scoped claims. hostFromAudit walks\n     every field that can carry it, so this is populated on audits where\n     query_explorer_url is null — which is where the logo used to go missing. */\n  const originHost = hostFromAudit(a, host) || site.replace(/\\/.*$/, \"\");\n\n  const queries = arr(qx?.queries);\n  const engines = arr(nRaw?.engines).length ? arr(nRaw.engines) : arr(qx?.engines);\n  const qCount = queries.length || num(H?.query_count ?? nRaw?.query_count ?? nRaw?.queries);\n  const answers = qCount != null && engines.length ? qCount * engines.length : null;\n\n  const findings = arr(f?.findings).slice()\n    .sort((x, y) => (SEV_ORDER[sevOf(x)] ?? 9) - (SEV_ORDER[sevOf(y)] ?? 9))\n    .map((x, i) => ({ ...x, _i: i, _sev: sevOf(x), anchor: anchors.finding(i) }));\n\n  const groups = personaGroups({ queries, foundationPersonas: f?.personas, queryPersonas: qx?.personas });\n\n  /* THE PRINTED NUMBER IS THE POSITION, NOT `rank`.\n     `rank` is a priority score the analysis assigns to a RANKING, and one\n     ranking expands into several action items (a NIO blueprint becomes an item\n     per action), so every item from that ranking carries the same rank. On\n     jdjune.com that put two items at 37 and none at 41: the plan printed \"37.\"\n     twice, stopped at 40, and said \"41 of 41\" underneath. The question section's\n     cross-links pointed at \"action #37\" without saying which one.\n\n     Two consequences, both fixed here:\n       · The ORDER was unstable. The API sorts server-side by rank, and a tie has\n         no defined order in SQL, so the two items could swap between loads —\n         and a deep link to \"#37\" would land on a different action.\n       · The NUMBER was not an ordinal, so it duplicated and skipped.\n\n     So: sort by (rank, item_id) — item_id is unique, which makes the order total\n     and identical on every load — then number by position. `n` is 1..N by\n     construction and is the only number anything renders. `rank` is kept as the\n     upstream score, for provenance; nothing displays it. */\n  const items = arr(res?.action_items).slice()\n    .sort((x, y) => (x.rank || 0) - (y.rank || 0)\n      || String(x.item_id || \"\").localeCompare(String(y.item_id || \"\")))\n    .map((it, i) => ({\n      ...it,\n      id: it.item_id,\n      n: i + 1,\n      rank: it.rank ?? i + 1,\n      gap: gapText(it.gap_diagnosis),\n      desc: String(it.description || \"\"),\n      layerK: layerKey(it),\n      layerL: layerLabel(it),\n      features: arr(it.affected_features).map(nameOf).filter(Boolean),\n      urls: arr(it.target_urls).filter(Boolean),\n      queryIds: arr(it.query_ids).filter(Boolean),\n      bullets: arr(it.brief_json?.recommended_actions).map(bulletText).filter(Boolean),\n      anchor: anchors.action(it.item_id),\n    }));\n\n  const wr = H?.win_rate_v1 || {};\n  /* Payload-only: buildModel has no per-question verdicts to recount from. A\n     surface that does have them (the reader) calls presenceOf itself with the\n     same function, so the two can't diverge. */\n  const pres = presenceOf(H);\n  const cycleLbl = ord === 1 ? \"Base Cycle\" : `Cycle ${ord}`;\n\n  const model = {\n    id: a.id || null,\n    audit: a,\n    name: title,\n    host: site,\n    originHost,\n    ordinal: ord,\n    cycleLabel: cycleLbl,\n    idCode: `${slug(title).replace(/-/g, \"\").slice(0, 24) || \"crawl\"}-${ord === 1 ? \"base\" : \"c\" + ord}`,\n    when: a.numbers_published_at || a.query_explorer_published_at || a.foundation_published_at || null,\n    /* A finished PDF, rendered at delivery. When it exists the customer gets a\n       file — bookmarks, page numbers, no browser print dialog and none of the\n       date/URL furniture Chrome staples onto a printed page. When it doesn't,\n       the portal falls back to browser print, which is what they have today. */\n    pdfUrl: a.report_pdf_url || nRaw?.report_pdf_url || null,\n    /* The SECOND document a crawl produces: every question no engine put you in,\n       with what it recommended instead. Signed per request by /api/audit behind\n       the query_results gate, so its presence here already means \"released and\n       this reader may have it\". */\n    answersPdfUrl: a.answers_pdf_url || null,\n    cycles,\n    unlocked,\n    isOperator: res?.is_operator === true,\n    team: res?.team || {},\n\n    ready: {\n      foundations: !!a.foundation_published_at && !!f,\n      questions: !!a.query_explorer_published_at && !!qx,\n      visibility: !!a.numbers_published_at && !!nRaw,\n      actions: !!a.numbers_published_at && unlocked && items.length > 0,\n    },\n\n    foundation: f && {\n      raw: f,\n      personas: arr(f.personas),\n      competitors: arr(f.competitors),\n      features: arr(f.features),\n      painPoints: arr(f.pain_points),\n      // New collections in schema 1.3. Absent from older payloads, so every consumer\n      // must treat them as optional — arr() already returns [] for undefined.\n      objections: arr(f.objections),\n      buyingTriggers: arr(f.buying_triggers),\n      findings,\n      gauges: arr(f.metrics?.gauges),\n      crawlers: arr(f.crawlers),\n      blocked: arr(f.crawlers).filter((c) => c.status === \"blocked\"),\n      // NOT EVERY CRAWLER IN THE LIST FEEDS AN AI ANSWER ENGINE, and the report\n      // used to escalate on the raw blocked count regardless. Bytespider is\n      // ByteDance's (it feeds Doubao and TikTok search); Googlebot is general\n      // search — Google-Extended is Google's AI control. Blocking either is a\n      // choice plenty of firms make deliberately, and calling it \"the first\n      // thing to fix\" contradicted the same document's own cover (\"nothing is\n      // critical or high severity\") and the finding's own MEDIUM rating.\n      // Urgency keys off answerBlocked; the table still shows everything.\n      answerEngine: arr(f.crawlers).filter((c) => isAnswerEngineCrawler(c.name)),\n      answerBlocked: arr(f.crawlers).filter(\n        (c) => c.status === \"blocked\" && isAnswerEngineCrawler(c.name)),\n      otherBlocked: arr(f.crawlers).filter(\n        (c) => c.status === \"blocked\" && !isAnswerEngineCrawler(c.name)),\n      /* The day the site was actually read (layer1 `analysis_date`, a bare\n         YYYY-MM-DD). Every other date on the audit row stamps a DELIVERY —\n         `when` above is built from published_at columns — so this is the only\n         field that dates the data itself. The document renderers must anchor\n         the foundation to this date, never to a publish stamp. */\n      analysisDate: f.analysis_date || null,\n      pagesAnalyzed: f.metrics?.pages_analyzed ?? null,\n      // Crawl coverage denominator ships with v1.3 crawls; absent from every\n      // payload delivered before it existed, so consumers must degrade.\n      coverage: f.metrics?.coverage ?? null,\n      pagesDiscoverable: f.metrics?.coverage?.discoverable_pages ?? null,\n      sev: severityCounts(findings),\n    },\n\n    questions: {\n      raw: qx,\n      queries,\n      groups,\n      count: qCount,\n      engines,\n      answers,\n    },\n\n    visibility: nRaw && {\n      raw: nRaw,\n      headline: H || {},\n      /* NOT num(): that is Math.round, and it made the report and the PDF print\n         a different number from the reader for the same crawl — JustinGPT read\n         6.7% in the reader and 7% on the report, Charlotte 7.3% and 7%. The\n         pipeline delivers one decimal; every surface prints what was delivered.\n         A percentage is not a count and does not get the count's rounding. */\n      vis: H?.visibility_pct == null || isNaN(Number(H.visibility_pct)) ? null : Number(H.visibility_pct),\n      winRate: num(wr.rate_pct ?? H?.win_rate_pct),\n      ciLo: num(wr.ci_low_pct),\n      ciHi: num(wr.ci_high_pct),\n      sovRank: H?.sov_rank ?? null,\n      invisible: H?.invisible ?? null,\n      visible: H?.visible ?? null,\n      /* The four presence states, and which rule produced them. A surface with\n         per-question verdicts in hand (the reader has them; the report does\n         not) can recount the split by calling presenceOf(headline, verdicts)\n         itself — same function, same counting. */\n      presence: pres,\n      scoredV2: scoredV2Of(H),\n      /* Not H.invisible: that is total minus visible and folds the\n         cited-but-not-named questions in with the absences. See absentOf. */\n      absent: absentOf(pres, H),\n      citedOnly: pres.cited_only ?? null,\n      /* Top picks as a COUNT. A rate against the questions you happen to appear\n         in rises as visibility falls, so no surface publishes one. */\n      wonUnique: wr.won_unique ?? null,\n      queries: num(H?.query_count ?? nRaw.query_count) ?? qCount,\n      sov: arr(nRaw.share_of_voice),\n      h2h: nRaw.head_to_head || null,\n      citations: nRaw.citations || null,\n      invisibleWinners: nRaw.invisible_winners || null,\n      tldr: arr(nRaw.tldr),\n      tldrInsights: arr(nRaw.tldr_insights),\n      execSummary: nRaw.exec_summary || \"\",\n      coreInsight: nRaw.core_insight || null,\n      // The narrative layer the standalone report has always shown and the\n      // portal never did: a per-section `data` + `synthesis` pair, plus the\n      // primary dichotomy — the one-sentence tension the whole report turns on.\n      narratives: nRaw.section_narratives || {},\n      dichotomy: nRaw.dichotomy || null,\n      reportTitle: nRaw.report_title || \"\",\n\n      /* Segment breakdowns. Each row is\n         { name, vis_pct, win_pct, visible, total, wins, high_intent, role_type,\n           vis_tone, win_tone, small_sample }\n         — note vis_tone/win_tone: the PIPELINE already decided whether a number\n         is good/warn/bad, so nothing here has to invent a threshold. */\n      byPersona: arr(nRaw.by_persona),\n      byFeature: arr(nRaw.by_feature),\n      byPainPoint: arr(nRaw.by_pain_point),\n      byBuyingJob: arr(nRaw.by_buying_job),\n      // Per-engine visibility, attached by /api/audit from the query results'\n      // platform_stats: { ChatGPT: { visible, total, wins } }. Absent on crawls\n      // whose answers are not released.\n      engineStats: nRaw.engine_stats || null,\n      roleSplit: nRaw.role_type_split || null,\n      highIntent: nRaw.high_intent || null,\n      funnel: nRaw.funnel || null,\n      // Competitors nobody expected to be in this category — a finding in its\n      // own right, and the portal was dropping it on the floor.\n      surprise: arr(nRaw.surprise_competitors),\n      recCounts: nRaw.rec_counts || {},\n      engines,\n    },\n\n    /* The full plan when unlocked: summary, owner counts and the NIO clusters\n       (dimensions moved, per-engine receptivity) the action plates draw from. */\n    plan: res?.plan || null,\n    actions: {\n      items,\n      counts: res?.action_counts || { total: items.length, l1: 0, l2: 0, l3: 0 },\n      layers: [...new Set(items.map((it) => it.layerK).filter(Boolean))].sort(),\n      unlocked,\n    },\n  };\n\n  model.links = crossLinks(model);\n  return model;\n}\n\n/* WHAT AN ITEM SAYS — one composition, rendered by every surface.\n *\n * Sharing the model stopped the two surfaces disagreeing about the DATA; it\n * didn't stop them showing different SUBSETS of it. The crawl hub's action rows\n * carried the diagnosis and the moves but silently dropped \"questions this\n * answers\", so the same action looked thinner on the page a customer sees first\n * — and nothing told them the fuller version existed somewhere else.\n *\n * These return an ordered list of labelled blocks. Each surface renders them\n * with its own classes; none of them decides WHICH blocks exist, or what a\n * block is called. Add a block here and it appears everywhere at once.\n */\nconst titleCase = (s) => String(s || \"\").replace(/_/g, \" \").replace(/\\b\\w/g, (c) => c.toUpperCase());\nconst strList = (v) => arr(v).map((x) => (typeof x === \"string\" ? x : bulletText(x))).filter(Boolean);\n\n/* The gap diagnosis is a plain string in some audits and a structured object\n   { mechanism, root_cause, why_this_item } in others. Both are rendered — an\n   object expanded into its parts, so a richer payload shows MORE, not less. */\nfunction gapBlocks(g) {\n  if (!g) return [];\n  if (typeof g === \"string\") return [{ k: \"Why this matters\", type: \"text\", v: g }];\n  const out = [];\n  if (g.mechanism || g.summary) out.push({ k: \"Why this matters\", type: \"text\", v: g.mechanism || g.summary });\n  if (g.root_cause) out.push({ k: \"Root cause\", type: \"text\", v: g.root_cause });\n  if (g.why_this_item) out.push({ k: \"Why this item\", type: \"text\", v: g.why_this_item });\n  return out.length ? out : [{ k: \"Why this matters\", type: \"text\", v: gapText(g) }];\n}\n\n/* THE FULL BRIEF — every field the standalone plan viewer renders.\n *\n * This used to be the reason an action row ended with \"Open the full brief ↗\",\n * which pushed the reader out of the document into a different viewer with a\n * different look. The brief is the substance of the deliverable; it belongs in\n * the same reader as everything else.\n *\n * genAI ships three brief shapes and the difference is not cosmetic, so each is\n * handled on its own terms rather than flattened into a lowest common set.\n */\nfunction briefBlocks(it) {\n  const b = it.brief_json;\n  if (!b || typeof b !== \"object\") return [];\n  const out = [];\n  const push = (k, type, v, extra = {}) => { if (v && (!Array.isArray(v) || v.length)) out.push({ k, type, v, ...extra }); };\n\n  // L1 — a technical fix: what we found, why, the steps, how to check it worked.\n  if (b.brief_type === \"l1_technical\" || it.layerK === \"L1\") {\n    push(\"What we found\", \"text\", b.what_we_found);\n    push(\"Why it matters\", \"text\", b.why_it_matters);\n    push(\"Step by step\", \"list\", strList(b.implementation_steps));\n    push(\"How to verify it worked\", \"list\", strList(b.verification_criteria));\n    push(\"What this unlocks\", \"list\", strList(b.downstream_unlocks));\n    if (out.length) return out;\n  }\n\n  // L2 remediation — the flat shape the published report uses.\n  push(\"What's missing\", \"list\", strList(b.issues));\n  push(\"Why competitors win here\", \"text\", b.structural_comparison);\n  push(\"The moves\", \"list\", strList(b.recommended_actions));\n  push(\"Why this matters commercially\", \"text\", b.commercial_weight_rationale);\n\n  // L2 / L3 content brief — numbered keys, from the knowledge graph.\n  const persona = b[\"2_buyer_persona\"];\n  if (persona) {\n    const who = [persona.role || persona.name, persona.seniority, persona.journey_stage && titleCase(persona.journey_stage)]\n      .filter(Boolean).join(\" · \");\n    push(\"Who's asking\", \"text\", who);\n  }\n  const plat = b[\"3_platform_priority\"];\n  if (plat) {\n    const order = [\"primary\", \"secondary\", \"tertiary\"].map((t) => plat[t]).filter(Boolean).map(titleCase);\n    push(\"Platform priority\", \"chips\", order.map((label) => ({ label })));\n  }\n  const comp = b[\"4_competitive_context\"];\n  if (comp) {\n    const lines = Object.entries(comp).filter(([, v]) => typeof v === \"string\" && v)\n      .map(([k, v]) => `${titleCase(k)}: ${v}`);\n    push(\"Why competitors win here\", \"list\", lines);\n  }\n  push(\"Claims your page must make\", \"list\", strList(b[\"5_required_claims\"]));\n  const fmt = b[\"6_format_specification\"];\n  if (fmt) {\n    push(\"How to format it\", \"chips\", strList(fmt.atomic_formats).map((x) => ({ label: titleCase(x) })));\n    const rest = Object.entries(fmt).filter(([k, v]) => k !== \"atomic_formats\" && typeof v === \"string\" && v)\n      .map(([k, v]) => `${titleCase(k)}: ${v}`);\n    push(\"Format notes\", \"list\", rest);\n  }\n  const impl = b[\"7_implementation\"];\n  if (impl) {\n    push(\"On your site\", \"list\", strList(impl.on_domain_actions));\n    push(\"Off-site\", \"list\", strList(impl.off_domain_actions));\n    push(\"Issues to fix\", \"list\", strList(impl.issues));\n    if (!b.recommended_actions) push(\"The moves\", \"list\", strList(impl.recommended_actions));\n    push(\"Note\", \"text\", impl.defensive_note);\n  }\n  const notes = b.platform_notes;\n  if (notes && typeof notes === \"object\") {\n    const lines = Object.entries(notes).filter(([, v]) => typeof v === \"string\" && v)\n      .map(([k, v]) => `${titleCase(k)}: ${v}`);\n    push(\"Platform receptivity\", \"list\", lines);\n  }\n  return out;\n}\n\nexport function actionBlocks(model, it) {\n  const qs = model?.links?.queriesForAction ? model.links.queriesForAction(it) : [];\n  const out = [...gapBlocks(it.gap_diagnosis)];\n  if (it.desc && it.desc !== it.gap) out.push({ k: \"What to do\", type: \"text\", v: it.desc });\n  out.push(...briefBlocks(it));\n  // The moves may arrive from `bullets` (the summary path) or from the brief.\n  if (!out.some((b) => b.k === \"The moves\") && it.bullets.length) {\n    out.push({ k: \"The moves\", type: \"list\", v: it.bullets, cap: 8 });\n  }\n  /* Count what will actually be SHOWN, not what came back.\n     `query_ids` can resolve to a query with no text on some deliveries, and can\n     repeat the same id — both counted toward the total while rendering an empty\n     or duplicate bullet, so a list of 8 visible questions claimed 14 with \"+2\n     more\". Dedupe and drop the blanks, then the number and the list agree. */\n  const qTexts = [...new Set(qs.map(queryText).map((t) => t.trim()).filter(Boolean))];\n  if (qTexts.length) out.push({ k: \"Questions this answers\", type: \"list\", v: qTexts, cap: 8 });\n  if (it.features.length) {\n    out.push({ k: \"Features it moves\", type: \"chips\",\n      v: it.features.map((f) => ({ label: f, anchor: anchors.feature(f) })) });\n  }\n  if (it.urls.length) out.push({ k: \"Pages it touches\", type: \"urls\", v: it.urls });\n  if (!out.length) out.push({ k: \"\", type: \"empty\", v: \"No brief was delivered for this action.\" });\n  return dedupeBlocks(out);\n}\n\n/* THE SAME PARAGRAPH, UNDER TWO LABELS.\n   `gap_diagnosis` and `brief_json.what_we_found` are written by separate\n   passes and, on a quarter of the actions we have delivered, they come back\n   byte-identical — so the plan printed one six-line paragraph twice, once as\n   \"Why this matters\" and again as \"What we found\", with nothing between them.\n   Counted on three published crawls: 11 of ATDC's 50 actions, 13 of\n   elicheesecake's 30, 6 of jdjune's 41.\n\n   The rule this enforces already existed one line up, where \"What to do\" is\n   suppressed when it equals the gap — this generalises that guard to every\n   block instead of the one pair somebody happened to hit first.\n\n   IT IS A GUARD, NOT A FIX. The duplication is upstream in the analysis, and\n   the right repair is for the two passes to stop writing the same sentence.\n   What this can honestly do is refuse to print it twice: the FIRST label wins,\n   because the blocks are pushed in reading order and the earlier one is the one\n   the reader has already been given a heading for. Only text blocks are\n   compared — two lists that legitimately share an item must not collapse. */\nfunction dedupeBlocks(blocks) {\n  const seen = new Set();\n  return blocks.filter((b) => {\n    if (b.type !== \"text\") return true;\n    const key = String(b.v || \"\").replace(/\\s+/g, \" \").trim().toLowerCase();\n    if (!key) return true;\n    if (seen.has(key)) return false;\n    seen.add(key);\n    return true;\n  });\n}\n\nexport function findingBlocks(x) {\n  const out = [];\n  const chips = [x.category, x.owner ? `Owner · ${x.owner}` : \"\", x.effort ? `Effort · ${x.effort}` : \"\"].filter(Boolean);\n  if (chips.length) out.push({ k: \"\", type: \"chips\", v: chips.map((label) => ({ label })) });\n  if (x.what_we_found) out.push({ k: \"What we found\", type: \"text\", v: x.what_we_found });\n  if (x.why_it_matters) out.push({ k: \"Why it matters\", type: \"text\", v: x.why_it_matters });\n  const fix = x.recommended_fix || x.fix;\n  if (fix) out.push({ k: \"Recommended fix\", type: \"text\", v: fix });\n  if (x.affected_scope) out.push({ k: \"\", type: \"note\", v: `Scope: ${x.affected_scope}` });\n  if (!out.some((b) => b.type === \"text\")) {\n    out.push({ k: \"\", type: \"empty\", v: \"The full write-up for this finding is in your foundation document.\" });\n  }\n  return out;\n}\n\n/* What connects to what.\n *\n * This is the payoff of one document: a reader who has just been told they're\n * invisible on 61 questions can see WHICH action covers them. Only links the\n * data actually carries are built — nothing is inferred from a title match,\n * because a plausible-but-wrong link is worse than no link.\n *\n * Not built, deliberately: finding → action. Nothing upstream ties a technical\n * finding to the plan item that resolves it; that link belongs in the pipeline,\n * not in a guess made in the browser.\n */\nfunction crossLinks(m) {\n  const queryById = new Map();\n  for (const q of m.questions.queries) if (q?.id) queryById.set(String(q.id), q);\n\n  const actionsForQuery = new Map();   // query id  → actions targeting it\n  const actionsForFeature = new Map(); // feature   → actions moving it\n  for (const it of m.actions.items) {\n    for (const qid of it.queryIds) {\n      const k = String(qid);\n      if (!actionsForQuery.has(k)) actionsForQuery.set(k, []);\n      actionsForQuery.get(k).push(it);\n    }\n    for (const ft of it.features) {\n      const k = ft.toLowerCase();\n      if (!actionsForFeature.has(k)) actionsForFeature.set(k, []);\n      actionsForFeature.get(k).push(it);\n    }\n  }\n\n  // Competitor → their share-of-voice row. Same vendor named in two places by\n  // two different upstream steps, matched on a normalised name.\n  const sovForCompetitor = new Map();\n  const norm = (s) => String(s || \"\").toLowerCase().replace(/[^a-z0-9]+/g, \"\");\n  for (const s of (m.visibility?.sov || [])) if (s?.vendor) sovForCompetitor.set(norm(s.vendor), s);\n\n  return {\n    queryById,\n    actionsForQuery,\n    actionsForFeature,\n    sovForCompetitor,\n    queriesForAction: (it) => it.queryIds.map((q) => queryById.get(String(q))).filter(Boolean),\n    sovFor: (vendorName) => sovForCompetitor.get(norm(vendorName)) || null,\n    actionsFor: (featureName) => actionsForFeature.get(String(featureName || \"\").toLowerCase()) || [],\n  };\n}\n"],
  "mappings": "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",
  "names": ["v", "ANSWER_ENGINE_CRAWLERS", "name", "s", "headline", "verdicts", "p", "vs", "out", "named", "cited", "presence", "h", "a", "override", "f", "raw", "t", "g", "it", "b", "x", "findings", "by", "i", "key", "itemId", "queries", "foundationPersonas", "queryPersonas", "qs", "groups", "q", "k", "byKey", "index", "rows", "list", "y", "seen", "r", "res", "host", "ordinal", "qx", "nRaw", "H", "unlocked", "cycles", "cur", "c", "ord", "stripProduct", "title", "site", "originHost", "engines", "qCount", "answers", "items", "wr", "pres", "cycleLbl", "model", "crossLinks", "titleCase", "strList", "gapBlocks", "briefBlocks", "push", "type", "extra", "persona", "who", "plat", "order", "label", "comp", "lines", "fmt", "rest", "impl", "notes", "qTexts", "dedupeBlocks", "blocks", "chips", "fix", "m", "queryById", "actionsForQuery", "actionsForFeature", "qid", "ft", "sovForCompetitor", "norm", "vendorName", "featureName"]
}
