{"id":1199,"date":"2025-01-08T10:03:16","date_gmt":"2025-01-08T10:03:16","guid":{"rendered":"http:\/\/brownsvillecic.com\/?page_id=1199"},"modified":"2026-08-24T18:48:26","modified_gmt":"2026-08-24T18:48:26","slug":"encuentra-tu-programa","status":"publish","type":"page","link":"https:\/\/brownsvillecic.com\/es\/find-your-program\/","title":{"rendered":"Encuentra tu Programa"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"1199\" class=\"elementor elementor-1199\" data-elementor-post-type=\"page\">\n\t\t\t\t<div class=\"elementor-element elementor-element-978559c e-flex e-con-boxed e-con e-parent\" data-id=\"978559c\" data-element_type=\"container\" data-e-type=\"container\" data-settings=\"{&quot;background_background&quot;:&quot;classic&quot;}\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t<div class=\"elementor-element elementor-element-c2c5090 e-con-full e-flex e-con e-child\" data-id=\"c2c5090\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-9500958 elementor-widget elementor-widget-heading\" data-id=\"9500958\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h1 class=\"elementor-heading-title elementor-size-default\">Discover the Perfect Program for Your Business<\/h1>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-3558c0c e-con-full e-flex e-con e-child\" data-id=\"3558c0c\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-a8cedc1 elementor-widget elementor-widget-text-editor\" data-id=\"a8cedc1\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>BCIC works to enhance quality of life in Brownsville through innovation and equitable economic development. In practice, that\u2019s programs for funding, building improvements, classes, mentorship, and workspace. Who qualifies is set per program \u2014 some are open to anyone, some only to businesses in Brownsville or Cameron County, and one is for nonprofits and community organizations. Tell the tool below what you\u2019re trying to do and it\u2019ll show you what fits.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-09f0e67 e-flex e-con-boxed e-con e-parent\" data-id=\"09f0e67\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-0f7d568 e-flex e-con-boxed e-con e-parent\" data-id=\"0f7d568\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-96f46c0 elementor-widget elementor-widget-html\" data-id=\"96f46c0\" 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<!-- ============================================================\n  BCIC \"Find Your Program\" widget\n  Paste this entire block into an HTML widget (Elementor: HTML\n  element) or any page section. No external libraries, no paid\n  services. Program data loads from a Google Sheet published as\n  CSV; if the sheet can't be reached, the widget falls back to\n  the data snapshot embedded below, so it always works.\n\n  TO CUSTOMIZE: edit the CONFIG block and the CSS variables at\n  the top of the <style> tag. Everything else can stay as is.\n============================================================ -->\n<!-- translate=\"no\" \/ notranslate \/ data-no-translation all say the same thing\n     to different tools (Google Translate, TranslatePress, Weglot). The widget\n     ships its own Spanish; a second translator on top of it produces a mixed\n     screen where some strings are translated and some are not, which is what\n     was happening on \/es\/ before v10. -->\n<div id=\"bcic-fyp\" data-fyp-version=\"26\" translate=\"no\" class=\"notranslate\" data-no-translation><\/div>\n\n<script>\nwindow.BCIC_FYP_CONFIG = {\n  \/* Published CSV URL of the Google Sheet (\"BCIC Find Your Program - Programs\").\n     Either: File > Share > Publish to web > CSV, and paste that link here,\n     or set the sheet to \"Anyone with the link: Viewer\" and keep this gviz link. *\/\n  sheetCsvUrl: \"https:\/\/docs.google.com\/spreadsheets\/d\/17_WUGDfLsXmb7yhhcyEfmSmfSnXU4OixCZw9rMDIJ70\/gviz\/tq?tqx=out:csv&gid=302161726\",\n\n  \/* Same idea, but for the \"Spanish\" tab of that same sheet (staff edit Spanish\n     program copy there \u2014 no code\/HTML knowledge needed). The &gid= at the end\n     is that tab's sheet ID; if the tab is ever recreated, update it here. *\/\n  sheetCsvUrlEs: \"https:\/\/docs.google.com\/spreadsheets\/d\/17_WUGDfLsXmb7yhhcyEfmSmfSnXU4OixCZw9rMDIJ70\/gviz\/tq?tqx=out:csv&gid=1763849195\",\n\n  \/* How long to wait for the sheet before using the built-in snapshot (ms) *\/\n  fetchTimeoutMs: 6000,\n\n  \/* Office hours, used by the advisor call-to-action on the results screen.\n     During these hours the results screen invites the visitor to CALL; outside\n     them it shows the published opening hours and a link to the contact page,\n     so nobody is sent to a phone nobody is answering. Note these are the hours\n     the phone is reliably staffed, which are narrower than the hours the office\n     is open -- see hoursLabel below.\n\n     timeZone is evaluated in Brownsville local time (America\/Chicago) via\n     Intl, NOT the visitor's own clock -- someone browsing from California at\n     3pm their time is looking at 5pm here, and the office is closed. Using an\n     IANA zone also means daylight saving is handled automatically; there is no\n     offset to update twice a year.\n\n     openDays uses JS weekday numbering (0 = Sunday). closeHour is exclusive:\n     16 means the call CTA stops the moment the clock hits 4:00 PM. *\/\n  office: {\n    phone: \"(956) 747-0100\",\n    phoneHref: \"tel:+19567470100\",\n    timeZone: \"America\/Chicago\",\n    openDays: [1, 2, 3, 4, 5],\n    openHour: 9,\n    closeHour: 16,\n    \/* Displayed opening hours. Deliberately separate from openHour\/closeHour:\n       those govern when we push the phone (someone is reliably at the desk),\n       these are the hours the office is actually open. Stated as a fact, never\n       as a claim about right now, so it can't be wrong. *\/\n    hoursLabel: \"8 AM to 5 PM CT\",\n    hoursLabelEs: \"8 AM a 5 PM CT\"\n  },\n\n  \/* Where the email-consent line links. Empty string renders the sentence as\n     plain text with no dead link. NOTE: the page below exists as an unpublished\n     DRAFT, so this URL 404s until someone publishes it. Blank this out again if\n     the policy page goes away. *\/\n  policy: {\n    url: \"https:\/\/brownsvillecic.com\/privacy-policy\/\"\n  },\n\n  \/* How the widget decides which language to render, and whether it shows a\n     switcher of its own.\n\n       mode: \"page\"  -- follow the page. THE DEFAULT, and the right answer for\n                        brownsvillecic.com, which runs TranslatePress: the site\n                        already has a language switcher and serves Spanish at\n                        \/es\/... with <html lang=\"es-MX\">. Two switchers on one\n                        page is how you get a Spanish page showing an English\n                        widget whose own control claims \"Ingl\u00e9s\" is active.\n             \"en\" \/ \"es\" -- pin the widget regardless of the page.\n             \"user\"  -- the pre-v10 behaviour: the widget owns the choice and\n                        renders its own switcher.\n\n       showSwitcher: \"auto\" shows it only in \"user\" mode. true\/false force it.\n\n     In \"page\" mode the site's switcher does the switching, which is a full\n     page load, so the widget simply boots in the right language. That removes\n     in-page switching entirely -- and with it every bug that lived there. *\/\n  language: {\n    mode: \"page\",\n    fallback: \"en\",\n    showSwitcher: \"auto\"\n  },\n\n  \/* Which service receives a captured email address.\n\n       \"hubspot\"   -- the live path. The HubSpot form submission is what fires\n                      the results-email workflow, so any other setting means\n                      the visitor never actually receives their matches.\n       \"mailchimp\" -- the old path. Still fully working, kept as a fallback.\n       \"auto\"      -- whichever is configured, HubSpot first.\n\n     HubSpot and Mailchimp are two-way synced on email, first name and program\n     interest, so the contact lands in both lists either way. The only thing\n     this setting changes is which one triggers the email. *\/\n  emailProvider: \"hubspot\",\n\n  \/* Optional newsletter signup shown under the results.\n     Set formGuid to a HubSpot form GUID to turn it on; leave \"\" to hide it.\n     Which of hubspot\/mailchimp is actually used is decided by emailProvider\n     above, not by which one happens to be filled in. *\/\n  hubspot: { portalId: \"243610053\", formGuid: \"70c02bd7-2b9c-4f19-9e81-358a6e7d720b\" },\n\n  \/* Optional newsletter signup via Mailchimp, shown under the results. When\n     filled in, this takes priority over the HubSpot box above, and the\n     confirmation message includes a link to that program's resource page\n     (see resourceLinks below). To fill this in:\n       1. In Mailchimp: Audience > Signup forms > Embedded forms. Copy the\n          form's \"action\" URL and the hidden \"u\" and \"id\" values from the\n          generated HTML.\n       2. In Mailchimp: Audience > Settings > Audience fields and *|MERGE|*\n          tags. Add a text field (e.g. \"Program interest\") and note its\n          merge tag (e.g. PROGRAM) \u2014 that's what programMergeField should be.\n     Leave formAction blank to keep this box hidden and fall back to HubSpot\n     (or to nothing, if HubSpot's formGuid is also blank).\n\n     Implementation notes (found the hard way \u2014 see submitMailchimp below):\n       - Mailchimp's subscribe\/post endpoint rejects plain fetch() calls\n         (returns HTTP 503) because it expects a real form navigation, not\n         an XHR\/fetch. The widget submits via a hidden <form>\/<iframe> pair\n         instead, same as Mailchimp's own official embed snippet.\n       - The \"BCIC Master Contact List\" audience requires First Name\n         (FNAME) on every signup \u2014 a bare EMAIL-only submission is silently\n         rejected (Mailchimp just redisplays its full hosted form with \"There\n         are errors below\"). So this box always asks for a first name too,\n         not just email. If a future Mailchimp audience doesn't require\n         FNAME, sending it anyway is harmless. *\/\n  mailchimp: {\n    formAction: \"https:\/\/brownsvilleedc.us13.list-manage.com\/subscribe\/post\",\n    u: \"31ac9f29b8a3a53cb0662e650\", \/\/ hidden \"u\" field from the embedded form code\n    id: \"37d0f2029c\",               \/\/ hidden \"id\" field (BCIC Master Contact List audience ID)\n    programMergeField: \"PROGRAM\"    \/\/ merge tag for the \"Program Interest\" field\n  },\n\n  \/* Trackable bit.ly resource links (one per program), shown after a\n     successful newsletter\/Mailchimp signup as \"here's a resource to get\n     started.\" Safe to repoint at different pages any time \u2014 the widget\n     always looks these up by program id, never hardcodes a URL. *\/\n  resourceLinks: {\n    \"ebridge-center\": \"https:\/\/bcic.us\/ebridge-center\",\n    \"blueprint-lab\": \"https:\/\/bcic.us\/blueprint-lab\",\n    \"ebridge-business-academy\": \"https:\/\/bcic.us\/business-academy\",\n    \"startup-texas\": \"https:\/\/bcic.us\/startup-texas\",\n    \"ebridge-fund\": \"https:\/\/bcic.us\/fyp-ebridge-fund\",\n    \"big-program\": \"https:\/\/bcic.us\/big-program\",\n    \"big-lift\": \"https:\/\/bcic.us\/big-lift\",\n    \"quality-of-life-grants\": \"https:\/\/bcic.us\/quality-of-life-grants\",\n    \"event-sponsorship\": \"https:\/\/bcic.us\/fyp-event-sponsorship\",\n    \"rgv-startup-week\": \"https:\/\/bcic.us\/fyp-rgv-startup-week\",\n    \"border-and-sea\": \"https:\/\/bcic.us\/fyp-border-and-sea\",\n    \"property-finder\": \"https:\/\/bcic.us\/fyp-property-finder\",\n    \"space-accelerator\": \"https:\/\/bcic.us\/fyp-space-accelerator\",\n    \"satop\": \"https:\/\/bcic.us\/fyp-satop\",\n    \"contact-bcic\": \"https:\/\/bcic.us\/fyp-contact-bcic\"\n  },\n\n  \/* Copy overrides. Any key here replaces the default text. *\/\n  strings: {\n    \/\/ headline: \"Find your program\",\n    \/\/ intro: \"Tell us what you're trying to do. We'll point you to the right BCIC program.\",\n    \/\/ searchPlaceholder: \"What are you trying to do? Try \\\"loan\\\" or \\\"start a business\\\"\"\n  }\n};\n<\/script>\n\n<style>\n#bcic-fyp {\n  \/* ---- Theme: pulled live from brownsvillecic.com on 2026-08-18 ----\n     Navy and green are the actual computed colors from the site's\n     headings and \"Subscribe\" button; Inter is the site's heading\/\n     button font, with its own body-text stack as the fallback. *\/\n  --fyp-accent: #1DC16A;         \/* BCIC green: chips, highlights, focus, CTA buttons (matches site \"Subscribe\" button) *\/\n  --fyp-primary: #00416B;        \/* BCIC navy: headings, body links (matches site H1\/H2 color) *\/\n  --fyp-primary-hover: #003253;\n  --fyp-focus: #00416B;          \/* focus ring: navy, 10.67:1 on white. The green accent\n                                    is only 2.36:1 and fails WCAG 1.4.11 as an indicator. *\/\n  --fyp-text: #00416B;\n  --fyp-muted: #4a5462;\n  --fyp-bg: #ffffff;\n  --fyp-card-bg: #f6f8fa;\n  --fyp-border: #d6dde5;\n  --fyp-radius: 15px;            \/* matches the site's rounded button corners *\/\n  --fyp-font: 'Inter', -apple-system, \"system-ui\", \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif;\n  --fyp-max-width: 720px;\n\n  font-family: var(--fyp-font);\n  color: var(--fyp-text);\n  background: var(--fyp-bg);\n  max-width: var(--fyp-max-width);\n  margin: 0 auto;\n  padding: 8px 4px;\n  line-height: 1.5;\n  box-sizing: border-box;\n}\n#bcic-fyp *, #bcic-fyp *::before, #bcic-fyp *::after { box-sizing: border-box; }\n\n#bcic-fyp .fyp-sr-only {\n  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;\n  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;\n}\n\n\/* The site's theme forces text-transform: uppercase on buttons\/links\n   site-wide. This widget's copy is written and cased on purpose\n   (sentence case), so every clickable control inside #bcic-fyp opts\n   back out of that inherited transform. *\/\n#bcic-fyp .fyp-btn,\n#bcic-fyp .fyp-option,\n#bcic-fyp .fyp-chip,\n#bcic-fyp .fyp-suggestion {\n  text-transform: none;\n}\n\n#bcic-fyp h2.fyp-headline {\n  font-size: 1.7rem; margin: 0 0 4px; color: var(--fyp-primary); line-height: 1.25;\n}\n#bcic-fyp .fyp-intro { margin: 0 0 18px; color: var(--fyp-muted); font-size: 1rem; }\n\n#bcic-fyp h3.fyp-step-title {\n  font-size: 1.25rem; margin: 0 0 14px; color: var(--fyp-primary); outline: none;\n}\n\n\/* Search *\/\n#bcic-fyp .fyp-searchwrap { position: relative; }\n#bcic-fyp .fyp-search {\n  width: 100%; font: inherit; font-size: 1.05rem; color: var(--fyp-text);\n  padding: 14px 16px; border: 2px solid var(--fyp-border);\n  border-radius: var(--fyp-radius); background: var(--fyp-bg);\n}\n#bcic-fyp .fyp-search:focus {\n  outline: 3px solid var(--fyp-focus); box-shadow: 0 0 0 2px var(--fyp-bg); outline-offset: 1px; border-color: var(--fyp-accent);\n}\n#bcic-fyp .fyp-suggestions {\n  list-style: none; margin: 6px 0 0; padding: 6px; position: absolute;\n  left: 0; right: 0; z-index: 50; background: var(--fyp-bg);\n  border: 1px solid var(--fyp-border); border-radius: var(--fyp-radius);\n  box-shadow: 0 8px 24px rgba(16,32,64,.14); max-height: 320px; overflow-y: auto;\n}\n#bcic-fyp .fyp-suggestions[hidden] { display: none; }\n#bcic-fyp .fyp-suggestion {\n  display: block; width: 100%; text-align: left; font: inherit; cursor: pointer;\n  background: none; border: 0; border-radius: 8px; padding: 10px 12px; color: var(--fyp-text);\n  white-space: normal;\n}\n#bcic-fyp .fyp-suggestion .fyp-sug-cat { display: block; font-size: .8rem; color: var(--fyp-muted); }\n#bcic-fyp .fyp-suggestion .fyp-sug-hint { display: block; font-size: .85rem; color: var(--fyp-text); margin-top: 2px; }\n#bcic-fyp .fyp-suggestion:hover,\n#bcic-fyp .fyp-suggestion[aria-selected=\"true\"] {\n  background: #eaf3e6; outline: 2px solid var(--fyp-focus); outline-offset: -2px;\n}\n\n\/* Chips *\/\n#bcic-fyp .fyp-chips { display: flex; flex-wrap: wrap; gap: 8px; margin: 14px 0 4px; padding: 0; list-style: none; }\n#bcic-fyp .fyp-chip {\n  font: inherit; font-size: .92rem; cursor: pointer; color: var(--fyp-primary);\n  background: var(--fyp-card-bg); border: 1.5px solid var(--fyp-border);\n  border-radius: 999px; padding: 10px 14px; min-height: 44px;\n}\n#bcic-fyp .fyp-chip:hover { border-color: var(--fyp-accent); background: #eaf3e6; }\n#bcic-fyp .fyp-chip:focus-visible { outline: 3px solid var(--fyp-focus); box-shadow: 0 0 0 2px var(--fyp-bg); outline-offset: 1px; }\n\n\/* Buttons \u2014 navy, matching the site's H1\/H2 color. NOTE: the site's own green\n   \"Subscribe\" button (white text on #1DC16A) measures 2.36:1 contrast, well\n   under the 4.5:1 WCAG AA minimum for text \u2014 so green is used here only for\n   accents\/borders\/focus rings, never as a background under white text, to\n   keep this widget ADA-compliant even where the source site isn't. *\/\n#bcic-fyp .fyp-btn {\n  font: inherit; font-size: 1rem; font-weight: 700; cursor: pointer; display: inline-block;\n  background: var(--fyp-primary); color: #fff; border: 2px solid var(--fyp-primary);\n  border-radius: var(--fyp-radius); padding: 12px 20px; min-height: 44px;\n  text-decoration: none; text-align: center; white-space: normal;\n}\n#bcic-fyp .fyp-btn:hover { background: var(--fyp-primary-hover); border-color: var(--fyp-primary-hover); }\n#bcic-fyp .fyp-btn:focus-visible { outline: 3px solid var(--fyp-focus); box-shadow: 0 0 0 2px var(--fyp-bg); outline-offset: 2px; }\n#bcic-fyp .fyp-btn--ghost { background: transparent; color: var(--fyp-primary); }\n#bcic-fyp .fyp-btn--ghost:hover { background: var(--fyp-card-bg); color: var(--fyp-primary); }\n#bcic-fyp .fyp-btn--back { display: inline-flex; align-items: center; justify-content: center; padding: 10px 14px; }\n#bcic-fyp .fyp-btn--back svg { display: block; }\n\n\/* Answer options *\/\n#bcic-fyp .fyp-options { display: grid; gap: 10px; margin: 0; padding: 0; list-style: none; }\n#bcic-fyp .fyp-option {\n  font: inherit; font-size: 1rem; cursor: pointer; text-align: left; width: 100%;\n  background: var(--fyp-card-bg); color: var(--fyp-text);\n  border: 2px solid var(--fyp-border); border-radius: var(--fyp-radius);\n  padding: 14px 16px; min-height: 48px; white-space: normal;\n}\n#bcic-fyp .fyp-option:hover { border-color: var(--fyp-accent); background: #eaf3e6; }\n#bcic-fyp .fyp-option:focus-visible { outline: 3px solid var(--fyp-focus); box-shadow: 0 0 0 2px var(--fyp-bg); outline-offset: 1px; }\n\n#bcic-fyp .fyp-meta { color: var(--fyp-muted); font-size: .9rem; margin: 0 0 6px; }\n#bcic-fyp .fyp-nav { margin-top: 16px; display: flex; gap: 10px; flex-wrap: wrap; }\n\n\/* Language switch \u2014 shown on every screen, top-right. Each language name is\n   written in that language on purpose (a Spanish speaker needs to recognize\n   \"Espa\u00f1ol\" before any other text on the page has translated). *\/\n#bcic-fyp .fyp-langswitch { display: flex; justify-content: flex-end; gap: 4px; margin: 0 0 10px; }\n#bcic-fyp .fyp-lang-btn {\n  font: inherit; font-size: .85rem; font-weight: 700; cursor: pointer;\n  background: none; border: 0; padding: 8px 10px; min-height: 40px;\n  color: var(--fyp-muted); border-radius: 8px; text-transform: none;\n}\n#bcic-fyp .fyp-lang-btn:hover { color: var(--fyp-primary); background: var(--fyp-card-bg); }\n#bcic-fyp .fyp-lang-btn:focus-visible { outline: 3px solid var(--fyp-focus); box-shadow: 0 0 0 2px var(--fyp-bg); outline-offset: 1px; }\n#bcic-fyp .fyp-lang-btn[aria-pressed=\"true\"] { color: var(--fyp-primary); text-decoration: underline; }\n\n\/* Name step (first question) *\/\n#bcic-fyp .fyp-namerow { display: flex; gap: 8px; flex-wrap: wrap; margin: 4px 0 0; }\n#bcic-fyp .fyp-nameinput {\n  flex: 1 1 220px; font: inherit; font-size: 1.05rem; color: var(--fyp-text);\n  padding: 13px 15px; border: 2px solid var(--fyp-border);\n  border-radius: var(--fyp-radius); background: var(--fyp-bg);\n}\n#bcic-fyp .fyp-nameinput:focus {\n  outline: 3px solid var(--fyp-focus); box-shadow: 0 0 0 2px var(--fyp-bg); outline-offset: 1px; border-color: var(--fyp-accent);\n}\n\n\/* \"Because you said ...\" line above the results *\/\n#bcic-fyp .fyp-reason { margin: 0 0 16px; color: var(--fyp-muted); font-size: .97rem; line-height: 1.55; }\n#bcic-fyp .fyp-reason .fyp-reason-chip {\n  display: inline-block; background: var(--fyp-card-bg); border: 1px solid var(--fyp-border);\n  border-radius: 999px; padding: 1px 9px; color: var(--fyp-text); font-weight: 500;\n}\n\n\/* Advisor call-to-action, shown between the results and the email box.\n   Swaps between \"call us\" and \"email my results\" on Brownsville office hours. *\/\n#bcic-fyp .fyp-advisor {\n  margin: 16px 0 0; padding: 14px 16px; border: 1.5px dashed var(--fyp-border);\n  border-radius: var(--fyp-radius); background: var(--fyp-card-bg);\n  display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap;\n}\n#bcic-fyp .fyp-advisor p { margin: 0; font-weight: 500; color: var(--fyp-text); font-size: .97rem; flex: 1 1 240px; }\n#bcic-fyp .fyp-advisor--open { border-style: solid; border-color: var(--fyp-accent); }\n#bcic-fyp .fyp-advisor .fyp-btn { flex: 0 0 auto; }\n#bcic-fyp .fyp-advisor-hours { display: block; font-weight: 400; color: var(--fyp-muted); font-size: .87rem; margin-top: 2px; }\n#bcic-fyp .fyp-advisor-tel { color: var(--fyp-primary); font-weight: 600; text-decoration: underline; white-space: nowrap; }\n\n\/* Divider between the three blocks on the results screen (results \/ talk to\n   us \/ email). Keeps them from reading as one undifferentiated stack. *\/\n#bcic-fyp .fyp-sep {\n  border: 0; border-top: 1px solid var(--fyp-border);\n  margin: 22px 0 18px; height: 0;\n}\n\n\/* Icon inside a button or a block heading. currentColor so it inherits. *\/\n#bcic-fyp .fyp-ico { flex: 0 0 auto; width: 18px; height: 18px; display: block; }\n#bcic-fyp .fyp-btn--ico { display: inline-flex; align-items: center; gap: 9px; }\n#bcic-fyp .fyp-news-label-row { display: flex; align-items: center; gap: 8px; }\n#bcic-fyp .fyp-news-label-row .fyp-ico { color: var(--fyp-primary); }\n\n\/* First-name \/ email step (single text input + primary action) *\/\n#bcic-fyp .fyp-fieldrow { display: flex; gap: 8px; flex-wrap: wrap; margin: 4px 0 0; }\n#bcic-fyp .fyp-textin {\n  flex: 1 1 220px; font: inherit; font-size: 1.05rem; color: var(--fyp-text);\n  padding: 13px 15px; border: 2px solid var(--fyp-border);\n  border-radius: var(--fyp-radius); background: var(--fyp-bg);\n}\n#bcic-fyp .fyp-textin:focus {\n  outline: 3px solid var(--fyp-focus); box-shadow: 0 0 0 2px var(--fyp-bg); outline-offset: 1px; border-color: var(--fyp-accent);\n}\n#bcic-fyp .fyp-stack { display: grid; gap: 8px; margin: 4px 0 0; }\n\n\/* \"Finding your matches\" interstitial *\/\n#bcic-fyp .fyp-calc { padding: 26px 0 10px; }\n#bcic-fyp .fyp-calc-bar {\n  height: 6px; border-radius: 999px; background: var(--fyp-card-bg);\n  overflow: hidden; margin: 0 0 16px;\n}\n#bcic-fyp .fyp-calc-bar > span {\n  display: block; height: 100%; width: 0%; border-radius: 999px;\n  background: var(--fyp-accent); transition: width .35s ease;\n}\n#bcic-fyp .fyp-calc-line {\n  margin: 0; color: var(--fyp-muted); font-size: .97rem; min-height: 1.5em;\n}\n#bcic-fyp .fyp-calc-dot {\n  display: inline-block; width: 7px; height: 7px; border-radius: 50%;\n  background: var(--fyp-accent); margin-right: 8px; vertical-align: 1px;\n}\n@media (prefers-reduced-motion: no-preference) {\n  #bcic-fyp .fyp-calc-dot { animation: fyppulse 1s ease-in-out infinite; }\n  @keyframes fyppulse { 0%,100% { opacity: .35; } 50% { opacity: 1; } }\n}\n\n\/* \"Sent to you\" confirmation shown instead of the email form when the address\n   was already captured before the results were calculated. *\/\n#bcic-fyp .fyp-sent {\n  margin-top: 4px; padding: 15px 16px; border: 1.5px solid var(--fyp-accent);\n  border-radius: var(--fyp-radius); background: var(--fyp-card-bg);\n  display: flex; align-items: flex-start; gap: 11px;\n}\n#bcic-fyp .fyp-sent .fyp-ico { color: var(--fyp-primary); margin-top: 2px; }\n#bcic-fyp .fyp-sent strong { display: block; color: var(--fyp-primary); }\n#bcic-fyp .fyp-sent span.fyp-sent-hint { display: block; color: var(--fyp-muted); font-size: .9rem; margin-top: 2px; }\n#bcic-fyp .fyp-sent--err { border-color: #d9b3b3; }\n#bcic-fyp .fyp-consent--sent { margin-top: 6px; font-size: .78rem; }\n\n\/* Result cards *\/\n#bcic-fyp .fyp-results { display: grid; gap: 14px; margin: 0; padding: 0; list-style: none; }\n#bcic-fyp .fyp-card {\n  background: var(--fyp-card-bg); border: 1.5px solid var(--fyp-border);\n  border-radius: var(--fyp-radius); padding: 18px;\n}\n#bcic-fyp .fyp-card--best { position: relative; border-color: var(--fyp-accent); border-width: 2px; }\n#bcic-fyp .fyp-card-badge {\n  position: absolute; top: -10px; left: 14px;\n  background: var(--fyp-accent); color: #fff; font-size: .72rem; font-weight: 600;\n  letter-spacing: .02em; padding: 3px 10px; border-radius: 999px;\n}\n#bcic-fyp .fyp-card-cat {\n  display: inline-block; font-size: .78rem; letter-spacing: .02em;\n  color: #2d5722; background: #e2efdb; border-radius: 999px; padding: 3px 10px; margin-bottom: 8px;\n}\n#bcic-fyp .fyp-card h4 { margin: 0 0 6px; font-size: 1.15rem; color: var(--fyp-primary); }\n#bcic-fyp .fyp-card p { margin: 0 0 12px; color: var(--fyp-text); }\n#bcic-fyp .fyp-note {\n  background: #fdf6e3; border: 1px solid #ecd9a0; border-radius: 10px;\n  padding: 10px 14px; margin: 0 0 14px; color: #5c4a12; font-size: .95rem;\n}\n#bcic-fyp .fyp-group-title { font-size: 1.05rem; color: var(--fyp-primary); margin: 18px 0 8px; }\n\n\/* Newsletter *\/\n#bcic-fyp .fyp-news { margin-top: 20px; padding: 16px; border: 1.5px dashed var(--fyp-border); border-radius: var(--fyp-radius); }\n#bcic-fyp .fyp-news label { display: block; font-weight: 600; margin-bottom: 8px; color: var(--fyp-primary); }\n#bcic-fyp .fyp-news-hint { margin: 0 0 10px; color: var(--fyp-muted); font-size: .92rem; }\n#bcic-fyp .fyp-consent { margin: 10px 0 0; color: var(--fyp-muted); font-size: .8rem; line-height: 1.45; }\n#bcic-fyp .fyp-consent a { color: inherit; text-decoration: underline; }\n#bcic-fyp .fyp-news-row { display: flex; gap: 8px; flex-wrap: wrap; }\n#bcic-fyp .fyp-news input[type=\"email\"], #bcic-fyp .fyp-news input[type=\"text\"] {\n  flex: 1 1 160px; font: inherit; padding: 11px 14px; min-height: 44px;\n  border: 2px solid var(--fyp-border); border-radius: 10px;\n}\n#bcic-fyp .fyp-news input[type=\"email\"] { flex-basis: 220px; }\n#bcic-fyp .fyp-news input[type=\"email\"]:focus, #bcic-fyp .fyp-news input[type=\"text\"]:focus { outline: 3px solid var(--fyp-focus); box-shadow: 0 0 0 2px var(--fyp-bg); outline-offset: 1px; }\n#bcic-fyp .fyp-news-msg { margin: 8px 0 0; font-size: .95rem; }\n#bcic-fyp .fyp-news-msg.fyp-ok { color: #2d5722; }\n#bcic-fyp .fyp-news-msg.fyp-err { color: #8a2a2a; }\n\n#bcic-fyp .fyp-footlink { margin-top: 16px; font-size: .95rem; }\n#bcic-fyp .fyp-footlink a, #bcic-fyp .fyp-news-msg a { text-decoration: underline; }\n#bcic-fyp a { color: var(--fyp-primary); }\n#bcic-fyp a:focus-visible { outline: 3px solid var(--fyp-focus); box-shadow: 0 0 0 2px var(--fyp-bg); outline-offset: 2px; }\n\n@media (max-width: 480px) {\n  #bcic-fyp h2.fyp-headline { font-size: 1.4rem; }\n  #bcic-fyp .fyp-btn:not(.fyp-btn--back) { width: 100%; }\n}\n@media (prefers-reduced-motion: no-preference) {\n  #bcic-fyp .fyp-view { animation: fypfade .24s ease-out; }\n  @keyframes fypfade { from { opacity: 0; transform: translateY(6px) scale(.99); } to { opacity: 1; transform: none; } }\n\n  \/* v18: playful micro-interactions -- nudge shake on the email box, staggered\n     match-card entrance, and a pop-in checkmark on the sent confirmation.\n     All skipped for anyone with prefers-reduced-motion set (this whole block). *\/\n  #bcic-fyp .fyp-news, #bcic-fyp .fyp-stack { animation: fypshake 6s ease-in-out infinite; }\n  #bcic-fyp .fyp-news:hover, #bcic-fyp .fyp-news:focus-within,\n  #bcic-fyp .fyp-stack:hover, #bcic-fyp .fyp-stack:focus-within { animation-play-state: paused; }\n  @keyframes fypshake {\n    0%, 8%, 100% { transform: translateX(0) rotate(0); }\n    1%, 5% { transform: translateX(-4px) rotate(-1deg); }\n    2%, 6% { transform: translateX(4px) rotate(1deg); }\n    3%, 7% { transform: translateX(-2px) rotate(-.5deg); }\n    4% { transform: translateX(2px) rotate(.5deg); }\n  }\n\n  #bcic-fyp .fyp-results .fyp-card { animation: fypcardin .32s ease both; }\n  #bcic-fyp .fyp-results .fyp-card:nth-child(1) { animation-delay: .02s; }\n  #bcic-fyp .fyp-results .fyp-card:nth-child(2) { animation-delay: .10s; }\n  #bcic-fyp .fyp-results .fyp-card:nth-child(3) { animation-delay: .18s; }\n  #bcic-fyp .fyp-results .fyp-card:nth-child(4) { animation-delay: .26s; }\n  #bcic-fyp .fyp-results .fyp-card:nth-child(5) { animation-delay: .34s; }\n  #bcic-fyp .fyp-results .fyp-card:nth-child(n+6) { animation-delay: .40s; }\n  @keyframes fypcardin { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }\n\n  #bcic-fyp .fyp-chips li { animation: fypcardin .32s ease both; }\n  #bcic-fyp .fyp-chips li:nth-child(1) { animation-delay: .02s; }\n  #bcic-fyp .fyp-chips li:nth-child(2) { animation-delay: .08s; }\n  #bcic-fyp .fyp-chips li:nth-child(3) { animation-delay: .14s; }\n  #bcic-fyp .fyp-chips li:nth-child(4) { animation-delay: .20s; }\n  #bcic-fyp .fyp-chips li:nth-child(5) { animation-delay: .26s; }\n  #bcic-fyp .fyp-chips li:nth-child(n+6) { animation-delay: .30s; }\n\n  #bcic-fyp .fyp-option-picked { animation: fypoptionpick .18s ease both; }\n  @keyframes fypoptionpick { from { transform: scale(.97); } to { transform: scale(1); } }\n\n  #bcic-fyp .fyp-card-badge { animation: fypbadgepop .4s cubic-bezier(.34,1.56,.64,1) .3s both; }\n  @keyframes fypbadgepop { from { opacity: 0; transform: scale(.5); } to { opacity: 1; transform: scale(1); } }\n\n  #bcic-fyp .fyp-check-pop { animation: fypcheckpop .4s cubic-bezier(.34,1.56,.64,1) both; }\n  @keyframes fypcheckpop { from { opacity: 0; transform: scale(.3); } to { opacity: 1; transform: scale(1); } }\n}\n\n#bcic-fyp .fyp-check-pop { display: inline-flex; vertical-align: -3px; margin-right: 4px; }\n#bcic-fyp .fyp-btn,\n#bcic-fyp .fyp-option,\n#bcic-fyp .fyp-chip { transition: transform .1s ease; }\n#bcic-fyp .fyp-btn:active,\n#bcic-fyp .fyp-option:active,\n#bcic-fyp .fyp-chip:active { transform: scale(.96); }\n#bcic-fyp .fyp-option-picked { border-color: var(--fyp-accent); background: #eaf3e6; }\n#bcic-fyp .fyp-option:disabled { cursor: default; }\n<\/style>\n\n<script>\n(function () {\n  \"use strict\";\n  var root = document.getElementById(\"bcic-fyp\");\n  if (!root) return;\n  var CFG = window.BCIC_FYP_CONFIG || {};\n\n  \/* ---------------- Copy (override via CONFIG.strings, English only) ----------------\n     STRINGS.en \/ STRINGS.es are the two UI-copy sets; applyLang() below picks\n     one into the live `S` object that all rendering code reads from. *\/\n  var STRINGS = {\n    en: {\n      headline: \"Find your program\",\n      intro: \"Tell us what you're trying to do. We'll point you to the program BCIC has for you.\",\n      searchLabel: \"Search programs\",\n      searchPlaceholder: \"What do you need? Try \u201cloan\u201d or \u201cstart\u201d\",\n      searchPlaceholderShort: \"What do you need?\",\n      searchExamples: [\"What do you need? Try \u201cloan\u201d or \u201cstart\u201d\", \"Try \u201cgrant\u201d\", \"Try \u201cmentor\u201d\", \"Try \u201cspace\u201d\", \"Try \u201csponsorship\u201d\"],\n      searchExamplesShort: [\"What do you need?\", \"Try \u201cgrant\u201d\", \"Try \u201cmentor\u201d\", \"Try \u201cspace\u201d\", \"Try \u201csponsorship\u201d\"],\n      guideBtn: \"Help me choose \u2014 answer a few questions\",\n      chipsLabel: \"Popular searches\",\n      bestMatchLabel: \"Best match\",\n      back: \"Back\",\n      startOver: \"Start over\",\n      resultsTitle: \"Here\u2019s what we recommend\",\n      resultsOneTitle: \"This looks like a good fit\",\n      \/\/ Same three titles, used when the visitor gave us a first name.\n      resultsTitleNamed: \"{name}, here's what we recommend\",\n      resultsOneTitleNamed: \"{name}, this looks like a good fit\",\n      noMatchTitleNamed: \"{name}, let's figure it out together\",\n      \/\/ First-name step (asked before anything else in the quiz)\n      nameTitle: \"How should we call you?\",\n      nameHint: \"Just a first name, so the results feel like yours. You can skip this.\",\n      namePlaceholder: \"First name\",\n      nameContinue: \"Continue\",\n      nameSkip: \"Skip this\",\n      \/\/ \"Because you said ...\" line above the results\n      reasonBoth: \"Because you said {stage} and want {need}:\",\n      reasonStage: \"Because you said {stage}:\",\n      reasonNeed: \"Because you\u2019re looking for {need}:\",\n      \/\/ Advisor CTA -- open \/ closed \/ unknown (if the clock can't be read)\n      advisorOpenLead: \"Rather talk it through with someone? The office is open right now.\",\n      advisorOpenBtn: \"Call {phone}\",\n      advisorClosedLead: \"Rather talk it through with someone?\",\n      \/* {hours} comes from CFG.office.hoursLabel, {phone} from CFG.office.phone,\n         so neither is written twice. The number is shown as plain text here and\n         not as a tel: link on purpose -- the sentence says to try again during\n         office hours, so inviting a tap now would contradict it. *\/\n      advisorClosedHours: \"Contact us or try again during our operating hours. We\u2019re open weekdays, {hours}. | {phone}\",\n      advisorClosedBtn: \"Contact us\",\n      advisorUnknownLead: \"Rather talk it through with someone?\",\n      advisorUnknownBtn: \"Talk to the BCIC team\",\n      allTitle: \"Everything BCIC offers\",\n      noMatchTitle: \"Let\u2019s figure it out together\",\n      geoNote: \"Some BCIC funding programs are only for businesses inside Brownsville or Cameron County, so we left those out.\",\n      stageNote: \"That kind of funding is reserved for businesses that are already up and running. Since you're still getting started, here's what can help you get there first:\",\n      footPrompt: \"Not seeing the right fit?\",\n      footLink: \"Talk to the BCIC team\",\n      newsLabel: \"Want these matches emailed to you?\",\n      newsHint: \"We\u2019ll send the programs you just matched with, plus how to take the next step.\",\n      newsBtn: \"Email my results\",\n      newsOk: \"On the way -- check your inbox.\",\n      newsErr: \"That didn\u2019t go through. Please check the email address and try again.\",\n      newsInvalid: \"Please enter a valid email address.\",\n      newsNamePlaceholder: \"First name\",\n      newsNameInvalid: \"Please enter your first name.\",\n      newsResourceIntro: \"Here's a resource to get you started:\",\n      newsResourceLinkText: \"{program} \u2192\",\n      questionCount: \"Question {n}\",\n      \/\/ Pre-results email ask\n      emailStepTitle: \"Want your matches emailed to you?\",\n      emailStepHint: \"We\\u2019ll send the programs you match with so you still have them later. Skip if you\\u2019d rather just see them.\",\n      emailStepNamePlaceholder: \"First name\",\n      emailStepPlaceholder: \"you@example.com\",\n      emailStepGo: \"Yes, email them to me\",\n      emailStepSkip: \"No thanks, just show me\",\n      \/\/ Consent line under the email field. {link} is replaced by a link to\n      \/\/ CFG.policy.url, or by plain text when no policy page exists yet.\n      emailStepConsent: \"By entering your email, you agree to BCIC\u2019s {link}.\",\n      emailStepConsentLink: \"email policy\",\n      \/\/ Repeated, smaller, under the \"on its way\" confirmation. Different\n      \/\/ sentence from the consent line: by this point they already agreed, so\n      \/\/ what's useful is how to get back out.\n      sentPolicy: \"You can unsubscribe any time. See our {link}.\",\n      \/\/ \"Finding your matches\" interstitial\n      calcTitle: \"Finding your matches\\u2026\",\n      calcStep1: \"Checking every active program\\u2026\",\n      calcStep2: \"Matching your stage and what you need\\u2026\",\n      calcStep3: \"Sorting by best fit\\u2026\",\n      \/\/ Confirmation shown when the address was captured before the results\n      sentTitle: \"On its way to {email}\",\n      sentHint: \"If it\\u2019s not there in a few minutes, check your spam folder.\",\n      sentErrTitle: \"We couldn\\u2019t send that just now\",\n      sentErrHint: \"Nothing lost \\u2014 the links above still work, and you can try again below.\"\n    },\n    es: {\n      headline: \"Encuentra tu programa\",\n      intro: \"Cu\u00e9ntanos qu\u00e9 necesitas hacer. Te dirigimos al programa que BCIC tiene para ti.\",\n      searchLabel: \"Buscar programas\",\n      searchPlaceholder: \"\u00bfQu\u00e9 necesitas? Prueba \u201cpr\u00e9stamo\u201d o \u201ciniciar\u201d\",\n      searchPlaceholderShort: \"\u00bfQu\u00e9 necesitas?\",\n      searchExamples: [\"\u00bfQu\u00e9 necesitas? Prueba \u201cpr\u00e9stamo\u201d o \u201ciniciar\u201d\", \"Prueba \u201capoyo\u201d\", \"Prueba \u201cmentor\u201d\", \"Prueba \u201cespacio\u201d\", \"Prueba \u201cpatrocinio\u201d\"],\n      searchExamplesShort: [\"\u00bfQu\u00e9 necesitas?\", \"Prueba \u201capoyo\u201d\", \"Prueba \u201cmentor\u201d\", \"Prueba \u201cespacio\u201d\", \"Prueba \u201cpatrocinio\u201d\"],\n      guideBtn: \"Ay\u00fadame a elegir \u2014 responde algunas preguntas\",\n      chipsLabel: \"B\u00fasquedas populares\",\n      bestMatchLabel: \"Mejor opci\u00f3n\",\n      back: \"Atr\u00e1s\",\n      startOver: \"Empezar de nuevo\",\n      resultsTitle: \"Esto es lo que recomendamos\",\n      resultsTitleNamed: \"{name}, esto es lo que recomendamos\",\n      resultsOneTitleNamed: \"{name}, esto parece una buena opci\u00f3n\",\n      noMatchTitleNamed: \"{name}, vamos a resolverlo juntos\",\n      nameTitle: \"\u00bfC\u00f3mo te llamas?\",\n      nameHint: \"Solo tu nombre, para personalizar los resultados. Puedes omitir este paso.\",\n      namePlaceholder: \"Nombre\",\n      nameContinue: \"Continuar\",\n      nameSkip: \"Omitir\",\n      reasonBoth: \"Como nos dijiste que {stage} y buscas {need}:\",\n      reasonStage: \"Como nos dijiste que {stage}:\",\n      reasonNeed: \"Como buscas {need}:\",\n      advisorOpenLead: \"\u00bfPrefieres hablar con alguien? La oficina est\u00e1 abierta ahora.\",\n      advisorOpenBtn: \"Llama al {phone}\",\n      advisorClosedLead: \"\u00bfPrefieres hablar con alguien?\",\n      advisorClosedHours: \"Cont\u00e1ctanos o vuelve a intentarlo durante nuestro horario de oficina. Atendemos de lunes a viernes, de {hours}. | {phone}\",\n      advisorClosedBtn: \"Cont\u00e1ctanos\",\n      advisorUnknownLead: \"\u00bfPrefieres hablar con alguien?\",\n      advisorUnknownBtn: \"Habla con el equipo de BCIC\",\n      resultsOneTitle: \"Esto parece una buena opci\u00f3n\",\n      allTitle: \"Todo lo que ofrece BCIC\",\n      noMatchTitle: \"Vamos a resolverlo juntos\",\n      geoNote: \"Algunos programas de financiamiento de BCIC son solo para negocios dentro de Brownsville o el condado de Cameron, as\u00ed que no los incluimos.\",\n      stageNote: \"Ese tipo de financiamiento est\u00e1 reservado para negocios que ya est\u00e1n en marcha. Como todav\u00eda est\u00e1s comenzando, esto puede ayudarte a llegar ah\u00ed primero:\",\n      footPrompt: \"\u00bfNo encuentras lo que buscas?\",\n      footLink: \"Habla con el equipo de BCIC\",\n      newsLabel: \"\u00bfQuieres que te enviemos estos resultados?\",\n      newsHint: \"Te enviamos los programas que coincidieron contigo y c\u00f3mo dar el siguiente paso.\",\n      newsBtn: \"Enviarme mis resultados\",\n      newsOk: \"En camino: revisa tu correo.\",\n      newsErr: \"Eso no se pudo enviar. Verifica el correo electr\u00f3nico e intenta de nuevo.\",\n      newsInvalid: \"Por favor ingresa un correo electr\u00f3nico v\u00e1lido.\",\n      newsNamePlaceholder: \"Nombre\",\n      newsNameInvalid: \"Por favor ingresa tu nombre.\",\n      newsResourceIntro: \"Aqu\u00ed tienes un recurso para comenzar:\",\n      newsResourceLinkText: \"{program} \u2192\",\n      questionCount: \"Pregunta {n}\",\n      emailStepTitle: \"\\u00bfQuieres que te enviemos tus resultados por correo?\",\n      emailStepHint: \"Te enviamos los programas que coincidan contigo para que los tengas despu\\u00e9s. Puedes omitir este paso si prefieres solo verlos.\",\n      emailStepNamePlaceholder: \"Nombre\",\n      emailStepPlaceholder: \"tu@ejemplo.com\",\n      emailStepGo: \"S\\u00ed, env\\u00edamelos\",\n      emailStepSkip: \"No, gracias: solo mu\\u00e9stramelos\",\n      emailStepConsent: \"Al ingresar tu correo, aceptas la {link} de BCIC.\",\n      emailStepConsentLink: \"pol\\u00edtica de correo electr\\u00f3nico\",\n      sentPolicy: \"Puedes darte de baja en cualquier momento. Consulta nuestra {link}.\",\n      calcTitle: \"Buscando tus opciones\\u2026\",\n      calcStep1: \"Revisando todos los programas activos\\u2026\",\n      calcStep2: \"Comparando tu etapa y lo que necesitas\\u2026\",\n      calcStep3: \"Ordenando por mejor coincidencia\\u2026\",\n      sentTitle: \"En camino a {email}\",\n      sentHint: \"Si no llega en unos minutos, revisa tu carpeta de spam.\",\n      sentErrTitle: \"No pudimos enviarlo en este momento\",\n      sentErrHint: \"No se perdi\\u00f3 nada: los enlaces de arriba siguen funcionando y puedes intentar de nuevo abajo.\"\n    }\n  };\n  var S; \/\/ set by applyLang()\n\n  \/* ---------------- Built-in data snapshot (fallback) ----------------\n     Kept in sync with the Google Sheet. The sheet wins when reachable.\n     Refreshed 2026-08-21 against the live published CSV \u2014 the sheet had\n     drifted (URL path renames, a couple of cta_label\/name edits, greatly\n     expanded keyword lists, broadened `stages`, and space-accelerator\n     paused) since this snapshot was last hand-updated. *\/\n  var FALLBACK = [\n    [\"ebridge-center\",\"true\",\"eBridge Center\",\"Learning & mentorship\",\"A home base for entrepreneurs in downtown Brownsville. Coworking space, mentors, workshops, and a community of people building businesses like yours.\",\"https:\/\/brownsvillecic.com\/rgv-projects\/ebridge-center\/\",\"Visit the eBridge Center\",\"business\",\"idea|existing|expanding\",\"learn|mentorship|space\",\"any\",\"incubator|coworking|co-working|mentor|mentorship|office|shared office|workspace|shared space|community|advice|guidance|help starting|accelerator|entrepreneur|entrepreneurship|network|networking|hub|resource center|business center|meet founders|meet entrepreneurs|desk|meeting room|conference room|downtown brownsville|new business owner|dont know where to start|need help starting a business\",\"2\"],\n    [\"blueprint-lab\",\"true\",\"Blueprint Prototyping Lab\",\"Learning & mentorship\",\"A prototyping and research space with large-format and desktop 3D printers, CNC tools, and vinyl-cutting equipment. Built with UTRGV for entrepreneurs, students, and startups turning ideas into physical products.\",\"https:\/\/brownsvillecic.com\/innovation-entrepreneurship\/blueprintlab\/\",\"Learn about Blueprint Lab\",\"business\",\"idea|existing|expanding\",\"learn\",\"any\",\"3d printing|3d printer|prototyping|prototype|cnc|cnc router|vinyl cutter|maker space|makerspace|fab lab|fabrication|product design|rapid prototyping|small batch manufacturing|build a prototype|physical product|hardware startup|hardware product|electronics workstation|research and development|turn my idea into a product|need to build something|need to prototype|utrgv|ebridge center\",\"3\"],\n    [\"ebridge-business-academy\",\"true\",\"eBridge Business Academy\",\"Learning & mentorship\",\"Short, practical classes for business owners: marketing, money management, permits, and more. Learn what you need, when you need it.\",\"https:\/\/brownsvillecic.com\/rgv-projects\/ebridge-business-academy\/\",\"Apply to the next cohort\",\"business\",\"idea|existing|expanding\",\"learn\",\"any\",\"classes|workshops|training|marketing class|bookkeeping|academy|courses|permits|licenses|licensing|accounting|money management|finance class|small business class|marketing help|how to market my business|social media marketing|advertising|taxes|payroll|hr|legal basics|free classes|business seminar|business skills|budget|bookkeeper|kauffman|blueprint business program\",\"1\"],\n    [\"startup-texas\",\"true\",\"Startup Texas\",\"Learning & mentorship\",\"An intensive accelerator for businesses ready to grow, run with UTRGV and Texas A&M partners. It wraps up with a pitch competition.\",\"https:\/\/brownsvillecic.com\/rgv-projects\/startup-texas\/\",\"Learn about Startup Texas\",\"business\",\"existing|expanding\",\"learn|mentorship\",\"any\",\"accelerator|pitch|pitch competition|pitch deck|grow|scale|scaling|competition|traction|startup|investor|investors|growth|utrgv|texas a&m|university partnership|demo day|cohort|ready to grow\",\"4\"],\n    [\"ebridge-fund\",\"true\",\"eBridge Fund\",\"Funding\",\"Small business loans at competitive rates for businesses in Cameron County, built for owners who can't get (or don't want) a traditional bank loan.\",\"https:\/\/brownsvillecic.com\/finance-programs\/ebridge-fund\/\",\"See if you're eligible for a loan\",\"business\",\"idea|existing|expanding\",\"funding-capital|funding-building\",\"cameron\",\"loan|money|funding|capital|finance|credit|working capital|equipment|building|construction|rehab|renovate|buy a building|buy land\",\"1\"],\n    [\"big-program\",\"true\",\"BIG Program\",\"Funding\",\"Grants for improving your building in Downtown Brownsville: facade work, interior systems, and rent reinvestment for tenants.\",\"https:\/\/brownsvillecic.com\/community-projects\/big-program\/\",\"Learn about BIG grants\",\"business\",\"existing|expanding\",\"funding-building\",\"brownsville\",\"building|renovation|remodel|rent|downtown|facade|storefront|exterior|construction|grant|paint|signage|sign|awning|fix up my building|improve my building|commercial building|rehab|repair|upgrade|30000|$30,000|property tax|matching grant|downtown improvement|beautification\",\"1\"],\n    [\"big-lift\",\"true\",\"BIG Lift\",\"Funding\",\"BIG Lift is BCIC's citywide storefront improvement grant. It reimburses 80% of exterior upgrades like facades, signage, and lighting, up to $30,000, opening to one district at a time on a rolling schedule.\",\"https:\/\/brownsvillecic.com\/community-development\/biglift\/\",\"Check if you qualify\",\"business\",\"existing|expanding\",\"funding-building|funding-capital\",\"brownsville\",\"grant|citywide|storefront|storefront grant|facade|facade grant|exterior improvement|signage|lighting|electoral district|my district|reimbursement|does my business qualify\",\"2\"],\n    [\"quality-of-life-grants\",\"true\",\"Quality of Life Grants\",\"Community & non-profits\",\"Grants for non-profits with projects that make Brownsville a better place to live: parks, public spaces, culture, and community events.\",\"https:\/\/brownsvillecic.com\/community-projects\/quality-of-life-grants\/\",\"Learn about the grants\",\"nonprofit\",\"idea|existing|expanding\",\"np-grant\",\"brownsville\",\"non-profit|nonprofit|grant|community|quality of life|park|public space|501c3|501(c)(3)|charity|charitable|community project|public art|mural|playground|greenspace|beautification|community event grant|nonprofit funding|need funding for my nonprofit|civic project\",\"1\"],\n    [\"event-sponsorship\",\"true\",\"Event sponsorship & marketing support\",\"Community & non-profits\",\"BCIC sponsors and helps promote events and initiatives that bring people together in Brownsville. Tell us about your event or project.\",\"https:\/\/bcic.submittable.com\/submit\/346995\/sponsorship-request-form\",\"Request sponsorship\",\"any\",\"idea|existing|expanding\",\"np-sponsorship\",\"brownsville\",\"sponsor|sponsorship|event|festival|marketing support|promote|sponsor my event|need a sponsor|festival sponsorship|community event|fundraiser|promote my event|advertise my event|partnership|collaborate|host an event|need help promoting\",\"2\"],\n    [\"rgv-startup-week\",\"true\",\"RGV Startup Week\",\"Events & community\",\"The Rio Grande Valley's biggest celebration of entrepreneurship: a week of free workshops, panels, and networking.\",\"https:\/\/brownsvillecic.com\/rgv-projects\/rgv-startup-week\/\",\"See what's on\",\"any\",\"idea|existing|expanding\",\"learn\",\"any\",\"event|networking|startup week|conference|workshops|meet people|rgv startup week|entrepreneur week|free workshops|panel|panels|meet founders|meet entrepreneurs|whats happening|calendar|schedule|upcoming events|things to do|business events\",\"5\"],\n    [\"border-and-sea\",\"true\",\"Border & Sea\",\"Marketing & promotion\",\"A platform that promotes Brownsville's local shops, restaurants, and experiences to visitors. If that's your business, you can be featured too.\",\"https:\/\/brownsvillecic.com\/rgv-projects\/border-sea\/\",\"Get featured\",\"business\",\"existing|expanding\",\"market\",\"brownsville\",\"tourism|restaurant|retail|shop|promotion|marketing|visitors|feature|get listed|list my business|be featured|local shop|local restaurant|boutique|visitor guide|things to do brownsville|promote my restaurant|promote my shop|directory|advertise my restaurant|advertise my shop|get more customers|attract visitors|attract tourists\",\"4\"],\n    [\"property-finder\",\"true\",\"Property Finder\",\"Locations & buildings\",\"Browse available buildings and land in Brownsville to find the right spot for your next location or project.\",\"https:\/\/brownsvillecic.com\/featured-properties\/\",\"Explore the map\",\"any\",\"idea|existing|expanding\",\"space\",\"any\",\"building|land|property|location|real estate|site|lease|buy|relocate|expand|find a building|find a space|find a location|available properties|vacant building|vacant land|storefront for rent|office space for rent|warehouse|commercial real estate|where can i open|looking for a location|need space|move my business|new location|open a second location\",\"3\"],\n    [\"space-accelerator\",\"false\",\"Space Accelerator\",\"Learning & mentorship\",\"A program for startups building space and aerospace technology, with mentorship and industry connections.\",\"https:\/\/brownsvillecic.com\/rgv-projects\/space-accelerator\/\",\"Learn more\",\"business\",\"idea|existing|expanding\",\"learn|mentorship\",\"any\",\"space|aerospace|rocket|satellite|spacex|technology|space industry|space tech|space company|spaceport|launch site|space startup|aerospace startup|space business|boca chica|starbase\",\"8\"],\n    [\"satop\",\"true\",\"SATOP\",\"Learning & mentorship\",\"Free engineering help for small businesses from space industry professionals: up to 40 hours of technical assistance for your product or process.\",\"https:\/\/brownsvillecic.com\/rgv-projects\/space-alliance-technology-outreach-program\/\",\"Request assistance\",\"business\",\"existing|expanding\",\"learn\",\"any\",\"engineering|technical|prototype|product|manufacturing|nasa|free engineering help|engineer|technical assistance|technical help|product design|product development|manufacturing help|process improvement|quality control|testing|research and development|r&d|space alliance|40 hours free\",\"8\"],\n    [\"frontera-leadership\",\"false\",\"Frontera Leadership Institute\",\"Learning & mentorship\",\"Leadership and product-management training for young people. Not currently offered.\",\"https:\/\/brownsvillecic.com\/rgv-projects\/frontera-leadership-institute\/\",\"Learn more\",\"business\",\"idea|existing|expanding\",\"learn\",\"any\",\"youth|leadership|young|student|teen|teenager|college student|young entrepreneur|youth program|leadership training\",\"9\"],\n    [\"contact-bcic\",\"true\",\"Talk to the BCIC team\",\"Get help\",\"Not sure where to start? Tell us what you're working on and we'll point you to the right program.\",\"https:\/\/brownsvillecic.com\/contact-bcic\/\",\"Contact us\",\"any\",\"idea|existing|expanding\",\"other\",\"any\",\"help|contact|question|talk|person|call|not sure|dont know|talk to someone|speak to someone|human|real person|general question|something else|other|assistance|guidance|where do i start|not sure what i need|none of the above\",\"99\"]\n  ];\n\n  \/* Spanish embedded snapshot \u2014 the Spanish widget always uses this (no live\n     fetch for Spanish; see project doc for why). Same 13-column shape as\n     FALLBACK; id\/active\/url\/audience\/stages\/needs\/geo\/priority match the\n     English rows above exactly. Official program brand names are kept in\n     English on purpose; only category, short_description, cta_label, and\n     keywords are translated (plus name for the two generic, non-branded\n     rows: event-sponsorship and contact-bcic). *\/\n  var FALLBACK_ES = [\n    [\"ebridge-center\",\"true\",\"eBridge Center\",\"Aprendizaje y mentor\u00eda\",\"Un lugar base para emprendedores en el centro de Brownsville. Espacio de coworking, mentores, talleres y una comunidad de personas construyendo negocios como el tuyo.\",\"https:\/\/brownsvillecic.com\/rgv-projects\/ebridge-center\/\",\"Visita el eBridge Center\",\"business\",\"idea|existing|expanding\",\"learn|mentorship|space\",\"any\",\"incubadora|coworking|espacio compartido|mentor|mentor\u00eda|oficina|oficina compartida|espacio de trabajo|comunidad|consejo|orientaci\u00f3n|ayuda para empezar|acelerador|emprendedor|emprendimiento|red de contactos|networking|centro de recursos|centro de negocios|conocer fundadores|conocer emprendedores|escritorio|sala de reuniones|sala de conferencias|centro de brownsville|nuevo due\u00f1o de negocio|no s\u00e9 por d\u00f3nde empezar|necesito ayuda para iniciar un negocio\",\"2\"],\n    [\"blueprint-lab\",\"true\",\"Blueprint Prototyping Lab\",\"Aprendizaje y mentor\u00eda\",\"Un espacio de prototipado e investigaci\u00f3n con impresoras 3D de gran formato y de escritorio, herramientas CNC y equipo de corte de vinil. Creado con UTRGV para emprendedores, estudiantes y startups que convierten ideas en productos f\u00edsicos.\",\"https:\/\/brownsvillecic.com\/innovation-entrepreneurship\/blueprintlab\/\",\"Conoce Blueprint Lab\",\"business\",\"idea|existing|expanding\",\"learn\",\"any\",\"impresi\u00f3n 3d|impresora 3d|prototipado|prototipo|cnc|router cnc|cortadora de vinil|espacio de fabricaci\u00f3n|makerspace|fab lab|fabricaci\u00f3n|dise\u00f1o de producto|prototipado r\u00e1pido|fabricaci\u00f3n a peque\u00f1a escala|construir un prototipo|producto f\u00edsico|startup de hardware|producto de hardware|estaci\u00f3n de electr\u00f3nica|investigaci\u00f3n y desarrollo|convertir mi idea en un producto|necesito construir algo|necesito prototipar|utrgv|eBridge Center\",\"3\"],\n    [\"ebridge-business-academy\",\"true\",\"eBridge Business Academy\",\"Aprendizaje y mentor\u00eda\",\"Clases cortas y pr\u00e1cticas para due\u00f1os de negocios: mercadotecnia, manejo del dinero, permisos y m\u00e1s. Aprende lo que necesitas, cuando lo necesitas.\",\"https:\/\/brownsvillecic.com\/rgv-projects\/ebridge-business-academy\/\",\"Aplica al pr\u00f3ximo grupo\",\"business\",\"idea|existing|expanding\",\"learn\",\"any\",\"clases|talleres|capacitaci\u00f3n|clase de mercadotecnia|contabilidad|academia|cursos|permisos|licencias|manejo del dinero|clase de finanzas|clase para peque\u00f1os negocios|ayuda de mercadotecnia|mercadotecnia en redes sociales|publicidad|impuestos|n\u00f3mina|recursos humanos|conceptos legales b\u00e1sicos|clases gratuitas|seminario de negocios|habilidades de negocio|presupuesto|contador|kauffman|blueprint business program\",\"1\"],\n    [\"startup-texas\",\"true\",\"Startup Texas\",\"Aprendizaje y mentor\u00eda\",\"Un acelerador intensivo para negocios listos para crecer, en colaboraci\u00f3n con UTRGV y Texas A&M. Termina con una competencia de presentaciones.\",\"https:\/\/brownsvillecic.com\/rgv-projects\/startup-texas\/\",\"Conoce Startup Texas\",\"business\",\"existing|expanding\",\"learn|mentorship\",\"any\",\"acelerador|presentaci\u00f3n|competencia de presentaciones|crecer|escalar|competencia|tracci\u00f3n|startup|inversionista|inversionistas|crecimiento|utrgv|texas a&m|colaboraci\u00f3n universitaria|d\u00eda de demostraci\u00f3n|cohorte|listo para crecer\",\"4\"],\n    [\"ebridge-fund\",\"true\",\"eBridge Fund\",\"Financiamiento\",\"Pr\u00e9stamos para peque\u00f1os negocios a tasas competitivas para negocios en el condado de Cameron, pensados para due\u00f1os que no pueden (o no quieren) un pr\u00e9stamo bancario tradicional.\",\"https:\/\/brownsvillecic.com\/finance-programs\/ebridge-fund\/\",\"Verifica si eres elegible para un pr\u00e9stamo\",\"business\",\"idea|existing|expanding\",\"funding-capital|funding-building\",\"cameron\",\"pr\u00e9stamo|dinero|financiamiento|capital|finanzas|cr\u00e9dito|capital de trabajo|equipo|edificio|construcci\u00f3n|remodelaci\u00f3n|renovar|comprar un edificio|comprar terreno\",\"1\"],\n    [\"big-program\",\"true\",\"BIG Program\",\"Financiamiento\",\"Apoyos para mejorar tu edificio en el centro de Brownsville: fachada, sistemas interiores y reinversi\u00f3n de renta para inquilinos.\",\"https:\/\/brownsvillecic.com\/community-projects\/big-program\/\",\"Conoce los apoyos BIG\",\"business\",\"existing|expanding\",\"funding-building\",\"brownsville\",\"edificio|renovaci\u00f3n|remodelaci\u00f3n|renta|centro|fachada|escaparate|exterior|construcci\u00f3n|apoyo|pintura|rotulaci\u00f3n|letrero|toldo|arreglar mi edificio|mejorar mi edificio|edificio comercial|rehabilitaci\u00f3n|reparaci\u00f3n|mejora|30000|impuesto sobre la propiedad|apoyo de igualaci\u00f3n|mejora del centro|embellecimiento\",\"1\"],\n    [\"big-lift\",\"true\",\"BIG Lift\",\"Financiamiento\",\"BIG Lift es el apoyo de BCIC para mejorar fachadas en toda la ciudad. Reembolsa el 80% del costo de mejoras exteriores, como fachadas, letreros e iluminaci\u00f3n, hasta $30,000, y se abre distrito por distrito seg\u00fan un calendario rotativo.\",\"https:\/\/brownsvillecic.com\/community-development\/biglift\/\",\"Verifica si calificas\",\"business\",\"existing|expanding\",\"funding-building|funding-capital\",\"brownsville\",\"apoyo|apoyo para escaparates|toda la ciudad|ciudad|fachada|apoyo de fachadas|mejora exterior|letreros|iluminaci\u00f3n|distrito electoral|mi distrito|reembolso|mi negocio califica\",\"2\"],\n    [\"quality-of-life-grants\",\"true\",\"Quality of Life Grants\",\"Comunidad y organizaciones sin fines de lucro\",\"Apoyos para organizaciones sin fines de lucro con proyectos que hacen de Brownsville un mejor lugar para vivir: parques, espacios p\u00fablicos, cultura y eventos comunitarios.\",\"https:\/\/brownsvillecic.com\/community-projects\/quality-of-life-grants\/\",\"Conoce los apoyos\",\"nonprofit\",\"idea|existing|expanding\",\"np-grant\",\"brownsville\",\"sin fines de lucro|organizaci\u00f3n sin fines de lucro|apoyo|comunidad|calidad de vida|parque|espacio p\u00fablico|501c3|caridad|caritativo|proyecto comunitario|arte p\u00fablico|mural|parque infantil|\u00e1rea verde|embellecimiento|apoyo para eventos comunitarios|financiamiento para organizaciones sin fines de lucro|necesito financiamiento para mi organizaci\u00f3n|proyecto c\u00edvico\",\"1\"],\n    [\"event-sponsorship\",\"true\",\"Patrocinio de eventos y apoyo de mercadotecnia\",\"Comunidad y organizaciones sin fines de lucro\",\"BCIC patrocina y ayuda a promover eventos e iniciativas que re\u00fanen a la comunidad de Brownsville. Cu\u00e9ntanos sobre tu evento o proyecto.\",\"https:\/\/bcic.submittable.com\/submit\/346995\/sponsorship-request-form\",\"Solicita patrocinio\",\"any\",\"idea|existing|expanding\",\"np-sponsorship\",\"brownsville\",\"patrocinador|patrocinio|evento|festival|apoyo de mercadotecnia|promover|patrocinar mi evento|necesito un patrocinador|patrocinio de festival|evento comunitario|recaudaci\u00f3n de fondos|promover mi evento|anunciar mi evento|asociaci\u00f3n|colaborar|organizar un evento|necesito ayuda para promover\",\"2\"],\n    [\"rgv-startup-week\",\"true\",\"RGV Startup Week\",\"Eventos y comunidad\",\"La mayor celebraci\u00f3n de emprendimiento del Valle de R\u00edo Grande: una semana de talleres gratuitos, paneles y networking.\",\"https:\/\/brownsvillecic.com\/rgv-projects\/rgv-startup-week\/\",\"Ver qu\u00e9 hay programado\",\"any\",\"idea|existing|expanding\",\"learn\",\"any\",\"evento|networking|semana de startups|conferencia|talleres|conocer gente|semana del emprendedor|talleres gratuitos|panel|paneles|conocer fundadores|conocer emprendedores|qu\u00e9 est\u00e1 pasando|calendario|horario|pr\u00f3ximos eventos|cosas que hacer|eventos de negocios\",\"5\"],\n    [\"border-and-sea\",\"true\",\"Border & Sea\",\"Mercadotecnia y promoci\u00f3n\",\"Una plataforma que promueve los negocios, restaurantes y experiencias locales de Brownsville entre los visitantes. Si tu negocio aplica, tambi\u00e9n puedes aparecer.\",\"https:\/\/brownsvillecic.com\/rgv-projects\/border-sea\/\",\"Aparece en la plataforma\",\"business\",\"existing|expanding\",\"market\",\"brownsville\",\"turismo|restaurante|tienda|comercio|promoci\u00f3n|mercadotecnia|visitantes|destacar|listar mi negocio|ser destacado|tienda local|restaurante local|boutique|gu\u00eda para visitantes|cosas que hacer en brownsville|promover mi restaurante|promover mi tienda|directorio|anunciar mi restaurante|anunciar mi tienda|conseguir m\u00e1s clientes|atraer visitantes|atraer turistas\",\"4\"],\n    [\"property-finder\",\"true\",\"Property Finder\",\"Ubicaciones y edificios\",\"Explora edificios y terrenos disponibles en Brownsville para encontrar el lugar correcto para tu pr\u00f3ximo local o proyecto.\",\"https:\/\/brownsvillecic.com\/featured-properties\/\",\"Explora el mapa\",\"any\",\"idea|existing|expanding\",\"space\",\"any\",\"edificio|terreno|propiedad|ubicaci\u00f3n|bienes ra\u00edces|local|alquilar|comprar|reubicar|expandir|buscar un edificio|buscar un espacio|buscar una ubicaci\u00f3n|propiedades disponibles|edificio vacante|terreno vacante|local en renta|oficina en renta|bodega|almac\u00e9n|bienes ra\u00edces comerciales|d\u00f3nde puedo abrir|buscando una ubicaci\u00f3n|necesito espacio|mudar mi negocio|nueva ubicaci\u00f3n|abrir una segunda ubicaci\u00f3n\",\"3\"],\n    [\"space-accelerator\",\"false\",\"Space Accelerator\",\"Aprendizaje y mentor\u00eda\",\"Un programa para startups que desarrollan tecnolog\u00eda espacial y aeroespacial, con mentor\u00eda y contactos en la industria.\",\"https:\/\/brownsvillecic.com\/rgv-projects\/space-accelerator\/\",\"Conoce m\u00e1s\",\"business\",\"idea|existing|expanding\",\"learn|mentorship\",\"any\",\"espacio|aeroespacial|cohete|sat\u00e9lite|spacex|tecnolog\u00eda|industria espacial|tecnolog\u00eda espacial|empresa espacial|puerto espacial|sitio de lanzamiento|startup espacial|startup aeroespacial|negocio espacial|boca chica|starbase\",\"8\"],\n    [\"satop\",\"true\",\"SATOP\",\"Aprendizaje y mentor\u00eda\",\"Ayuda de ingenier\u00eda gratuita para peque\u00f1os negocios por parte de profesionales de la industria espacial: hasta 40 horas de asistencia t\u00e9cnica para tu producto o proceso.\",\"https:\/\/brownsvillecic.com\/rgv-projects\/space-alliance-technology-outreach-program\/\",\"Solicita asistencia\",\"business\",\"existing|expanding\",\"learn\",\"any\",\"ingenier\u00eda|t\u00e9cnico|prototipo|producto|manufactura|nasa|ayuda de ingenier\u00eda gratuita|ingeniero|asistencia t\u00e9cnica|ayuda t\u00e9cnica|dise\u00f1o de producto|desarrollo de producto|ayuda de manufactura|mejora de procesos|control de calidad|pruebas|investigaci\u00f3n y desarrollo|alianza espacial|40 horas gratis\",\"8\"],\n    [\"frontera-leadership\",\"false\",\"Frontera Leadership Institute\",\"Aprendizaje y mentor\u00eda\",\"Capacitaci\u00f3n en liderazgo y gesti\u00f3n de productos para j\u00f3venes. No se ofrece actualmente.\",\"https:\/\/brownsvillecic.com\/rgv-projects\/frontera-leadership-institute\/\",\"Conoce m\u00e1s\",\"business\",\"idea|existing|expanding\",\"learn\",\"any\",\"juventud|liderazgo|joven|estudiante|adolescente|estudiante universitario|joven emprendedor|programa juvenil|capacitaci\u00f3n en liderazgo\",\"9\"],\n    [\"contact-bcic\",\"true\",\"Habla con el equipo de BCIC\",\"Obtener ayuda\",\"\u00bfNo sabes por d\u00f3nde empezar? Cu\u00e9ntanos en qu\u00e9 est\u00e1s trabajando y te dirigimos al programa correcto.\",\"https:\/\/brownsvillecic.com\/contact-bcic\/\",\"Cont\u00e1ctanos\",\"any\",\"idea|existing|expanding\",\"other\",\"any\",\"ayuda|contacto|pregunta|hablar|persona|llamar|no estoy seguro|no s\u00e9|hablar con alguien|persona real|pregunta general|otra cosa|otro|asistencia|orientaci\u00f3n|d\u00f3nde empiezo|no s\u00e9 qu\u00e9 necesito|ninguna de las anteriores\",\"99\"]\n  ];\n\n  var COLS = [\"id\",\"active\",\"name\",\"category\",\"short_description\",\"url\",\"cta_label\",\"audience\",\"stages\",\"needs\",\"geo\",\"keywords\",\"priority\"];\n\n  \/* ---------------- Utilities ---------------- *\/\n  \/* A stable, language-independent identifier for a quiz option or chip.\n\n     Display labels are translated and staff-editable, so logging them means\n     the same answer arrives as two different values in EN and ES, and any copy\n     edit silently breaks continuity with historical data. The `set` object is\n     the actual semantic content of an option -- {stage:\"running\"} -- so its\n     sorted key=value form is stable across both languages and all rewording. *\/\n  function answerToken(opt) {\n    var set = (opt && opt.set) || {};\n    var keys = [];\n    for (var k in set) keys.push(k);\n    if (!keys.length) return opt && opt.id ? opt.id : \"unknown\";\n    keys.sort();\n    return keys.map(function (k) { return k + \"=\" + set[k]; }).join(\",\");\n  }\n\n  function chipToken(c) {\n    if (c && c.id) return c.id;\n    var p = (c && c.preset) || {};\n    var keys = [];\n    for (var k in p) keys.push(k);\n    keys.sort();\n    return keys.length ? keys.map(function (k) { return k + \"=\" + p[k]; }).join(\",\") : \"unknown\";\n  }\n\n  function track(name, data) {\n    var payload = data || {};\n    try {\n      if (Array.isArray(window.dataLayer)) {\n        var dl = { event: \"fyp_\" + name };\n        for (var k in payload) dl[k] = payload[k];\n        window.dataLayer.push(dl);\n      }\n      if (typeof window.gtag === \"function\") window.gtag(\"event\", \"fyp_\" + name, payload);\n      \/\/ Also log program-interest clicks as a HubSpot custom behavioral event\n      \/\/ (\"Find Your Program - Result Click\", created 2026-08-20) so a\n      \/\/ visitor's interest in a specific program shows up on their HubSpot\n      \/\/ contact timeline once they're later identified, not just in GA4.\n      if (name === \"result_click\" && CFG.hubspot && CFG.hubspot.portalId) {\n        var _hsq = window._hsq = window._hsq || [];\n        _hsq.push([\"trackCustomBehavioralEvent\", {\n          name: \"pe\" + CFG.hubspot.portalId + \"_find_your_program___result_click\",\n          properties: {\n            program_id: payload.program || \"\",\n            program_name: payload.program_name || \"\"\n          }\n        }]);\n      }\n      document.dispatchEvent(new CustomEvent(\"bcic-fyp\", { detail: { event: name, data: payload } }));\n    } catch (e) { \/* analytics must never break the widget *\/ }\n  }\n\n  function parseCSV(text) {\n    var rows = [], row = [], cur = \"\", inQ = false;\n    for (var i = 0; i < text.length; i++) {\n      var c = text[i];\n      if (inQ) {\n        if (c === '\"') {\n          if (text[i + 1] === '\"') { cur += '\"'; i++; } else { inQ = false; }\n        } else cur += c;\n      } else if (c === '\"') inQ = true;\n      else if (c === \",\") { row.push(cur); cur = \"\"; }\n      else if (c === \"\\n\" || c === \"\\r\") {\n        if (c === \"\\r\" && text[i + 1] === \"\\n\") i++;\n        row.push(cur); cur = \"\";\n        if (row.length > 1 || row[0] !== \"\") rows.push(row);\n        row = [];\n      } else cur += c;\n    }\n    if (cur !== \"\" || row.length) { row.push(cur); rows.push(row); }\n    return rows;\n  }\n\n  function toProgram(values, header) {\n    var o = {};\n    for (var i = 0; i < COLS.length; i++) {\n      var idx = header ? header.indexOf(COLS[i]) : i;\n      o[COLS[i]] = idx >= 0 && values[idx] != null ? String(values[idx]).trim() : \"\";\n    }\n    if (!o.id || !o.name || !\/^https?:\\\/\\\/\/i.test(o.url)) return null; \/\/ skip broken rows\n    o.active = !\/^(false|no|0)$\/i.test(o.active || \"true\");\n    o.audience = (o.audience || \"any\").toLowerCase();\n    o.stages = (o.stages || \"any\").toLowerCase().split(\"|\").map(function (s) { return s.trim(); }).filter(Boolean);\n    o.needs = (o.needs || \"\").toLowerCase().split(\"|\").map(function (s) { return s.trim(); }).filter(Boolean);\n    o.geo = (o.geo || \"any\").toLowerCase();\n    o.keywords = (o.keywords || \"\").toLowerCase().split(\"|\").map(function (s) { return s.trim(); }).filter(Boolean);\n    o.priority = parseInt(o.priority, 10); if (isNaN(o.priority)) o.priority = 50;\n    if (!o.cta_label) o.cta_label = \"Learn more\";\n    return o;\n  }\n\n  \/\/ Generic loader used for both languages: fetches `url` as CSV, and falls\n  \/\/ back to `fallbackRows` (a FALLBACK\/FALLBACK_ES-shaped array) if the sheet\n  \/\/ is missing, unreachable, empty, or slower than fetchTimeoutMs. Each\n  \/\/ language gets its own independent fallback, so one sheet having a\n  \/\/ problem never takes the other language down with it.\n  function loadProgramsFrom(url, fallbackRows, done) {\n    var fallback = fallbackRows.map(function (r) { return toProgram(r, null); }).filter(Boolean);\n    if (!url) { done(fallback, \"embedded\"); return; }\n    var settled = false;\n    var timer = setTimeout(function () { finish(null); }, CFG.fetchTimeoutMs || 6000);\n    function finish(list) {\n      if (settled) return; settled = true; clearTimeout(timer);\n      if (list && list.length) done(list, \"sheet\");\n      else { if (window.console) console.warn(\"BCIC FYP: using embedded program snapshot (sheet unreachable or empty) for \" + url); done(fallback, \"embedded\"); }\n    }\n    try {\n      fetch(url, { method: \"GET\", mode: \"cors\" })\n        .then(function (res) { if (!res.ok) throw new Error(\"HTTP \" + res.status); return res.text(); })\n        .then(function (text) {\n          var rows = parseCSV(text);\n          if (!rows.length) return finish(null);\n          var header = rows[0].map(function (h) { return h.replace(\/^\"|\"$\/g, \"\").trim().toLowerCase(); });\n          if (header.indexOf(\"id\") === -1) return finish(null); \/\/ not our sheet\n          var list = rows.slice(1).map(function (r) { return toProgram(r, header); }).filter(Boolean);\n          finish(list);\n        })\n        .catch(function () { finish(null); });\n    } catch (e) { finish(null); }\n  }\n\n  function loadPrograms(done) { loadProgramsFrom(CFG.sheetCsvUrl, FALLBACK, done); }\n  function loadProgramsEs(done) { loadProgramsFrom(CFG.sheetCsvUrlEs, FALLBACK_ES, done); }\n\n  \/* ---------------- Recommendation engine ---------------- *\/\n  \/\/ state: { audience, stage, need, location, npNeed, showAll, query }\n  var QUESTION_SETS = {\n    en: {\n      audience: {\n        id: \"audience\",\n        title: \"Who is this for?\",\n        options: [\n          { label: \"My business or business idea\", set: { audience: \"business\" } },\n          { label: \"My non-profit organization\", set: { audience: \"nonprofit\" } },\n          { label: \"Something else\", set: { audience: \"other\" } }\n        ]\n      },\n      stage: {\n        id: \"stage\",\n        title: \"Which of these sounds most like you?\",\n        options: [\n          { label: \"I have an idea and want to figure out how to start\", set: { stage: \"idea\" } },\n          { label: \"I already run a business\", set: { stage: \"existing\" } },\n          { label: \"I\u2019m expanding or moving to Brownsville\", set: { stage: \"expanding\" } },\n          { label: \"Just show me everything\", set: { showAll: true } }\n        ]\n      },\n      need: {\n        id: \"need\",\n        title: \"What would help you most right now?\",\n        options: [\n          { label: \"Learning how to start or run a business\", set: { need: \"learn\" } },\n          { label: \"Money for my business\", set: { need: \"funding\" } },\n          { label: \"Mentors and a community of business owners\", set: { need: \"mentorship\" } },\n          { label: \"A building, office, or workspace\", set: { need: \"space\" } },\n          { label: \"Help getting customers or visibility\", set: { need: \"market\" } },\n          { label: \"Something else\", set: { need: \"other\" } }\n        ]\n      },\n      fundingType: {\n        id: \"fundingType\",\n        title: \"What\u2019s the money for?\",\n        options: [\n          { label: \"Day-to-day costs, equipment, or growth\", set: { need: \"funding-capital\" } },\n          { label: \"Buying or fixing up a building\", set: { need: \"funding-building\" } }\n        ]\n      },\n      location: {\n        id: \"location\",\n        title: \"Where is your business located (or planned)?\",\n        options: [\n          { label: \"Inside Brownsville city limits\", set: { location: \"brownsville\" } },\n          { label: \"Elsewhere in Cameron County\", set: { location: \"cameron\" } },\n          { label: \"Outside Cameron County\", set: { location: \"outside\" } }\n        ]\n      },\n      npNeed: {\n        id: \"npNeed\",\n        title: \"What would help your organization most?\",\n        options: [\n          { label: \"Funding for a community project\", set: { need: \"np-grant\" } },\n          { label: \"A sponsor for an event or initiative\", set: { need: \"np-sponsorship\" } },\n          { label: \"Something else\", set: { need: \"other\" } }\n        ]\n      }\n    },\n    es: {\n      audience: {\n        id: \"audience\",\n        title: \"\u00bfPara qui\u00e9n es esto?\",\n        options: [\n          { label: \"Mi negocio o idea de negocio\", set: { audience: \"business\" } },\n          { label: \"Mi organizaci\u00f3n sin fines de lucro\", set: { audience: \"nonprofit\" } },\n          { label: \"Otra cosa\", set: { audience: \"other\" } }\n        ]\n      },\n      stage: {\n        id: \"stage\",\n        title: \"\u00bfCu\u00e1l de estas opciones se parece m\u00e1s a tu situaci\u00f3n?\",\n        options: [\n          { label: \"Tengo una idea y quiero saber c\u00f3mo empezar\", set: { stage: \"idea\" } },\n          { label: \"Ya tengo un negocio en marcha\", set: { stage: \"existing\" } },\n          { label: \"Estoy expandi\u00e9ndome o mud\u00e1ndome a Brownsville\", set: { stage: \"expanding\" } },\n          { label: \"Mu\u00e9strame todo\", set: { showAll: true } }\n        ]\n      },\n      need: {\n        id: \"need\",\n        title: \"\u00bfQu\u00e9 te ayudar\u00eda m\u00e1s en este momento?\",\n        options: [\n          { label: \"Aprender a iniciar o manejar un negocio\", set: { need: \"learn\" } },\n          { label: \"Dinero para mi negocio\", set: { need: \"funding\" } },\n          { label: \"Mentores y una comunidad de due\u00f1os de negocios\", set: { need: \"mentorship\" } },\n          { label: \"Un edificio, oficina o espacio de trabajo\", set: { need: \"space\" } },\n          { label: \"Ayuda para conseguir clientes o visibilidad\", set: { need: \"market\" } },\n          { label: \"Otra cosa\", set: { need: \"other\" } }\n        ]\n      },\n      fundingType: {\n        id: \"fundingType\",\n        title: \"\u00bfPara qu\u00e9 es el dinero?\",\n        options: [\n          { label: \"Gastos del d\u00eda a d\u00eda, equipo o crecimiento\", set: { need: \"funding-capital\" } },\n          { label: \"Comprar o arreglar un edificio\", set: { need: \"funding-building\" } }\n        ]\n      },\n      location: {\n        id: \"location\",\n        title: \"\u00bfD\u00f3nde est\u00e1 ubicado tu negocio (o d\u00f3nde planeas ubicarlo)?\",\n        options: [\n          { label: \"Dentro de los l\u00edmites de la ciudad de Brownsville\", set: { location: \"brownsville\" } },\n          { label: \"En otra parte del condado de Cameron\", set: { location: \"cameron\" } },\n          { label: \"Fuera del condado de Cameron\", set: { location: \"outside\" } }\n        ]\n      },\n      npNeed: {\n        id: \"npNeed\",\n        title: \"\u00bfQu\u00e9 ayudar\u00eda m\u00e1s a tu organizaci\u00f3n?\",\n        options: [\n          { label: \"Financiamiento para un proyecto comunitario\", set: { need: \"np-grant\" } },\n          { label: \"Un patrocinador para un evento o iniciativa\", set: { need: \"np-sponsorship\" } },\n          { label: \"Otra cosa\", set: { need: \"other\" } }\n        ]\n      }\n    }\n  };\n  var QUESTIONS; \/\/ set by applyLang()\n\n  \/* Short, human phrasings of the visitor's own answers, used to build the\n     \"Because you said ...\" line above the results. Deliberately separate from\n     the quiz option labels: those are full sentences written to be clicked\n     (\"I already run a business\"), these are fragments written to be read back\n     inside another sentence (\"you're already running a business\").\n\n     stage phrases are clauses that follow \"you said\" \/ \"nos dijiste que\".\n     need phrases are noun phrases that follow \"want\" \/ \"buscas\". *\/\n  var REASON_SETS = {\n    en: {\n      stage: {\n        idea: \"you\u2019re just getting started\",\n        existing: \"you\u2019re already running a business\",\n        expanding: \"you\u2019re expanding or moving to Brownsville\"\n      },\n      need: {\n        learn: \"help learning the ropes\",\n        mentorship: \"mentors and a community of business owners\",\n        space: \"a building, office, or workspace\",\n        market: \"help getting customers or visibility\",\n        events: \"event support\",\n        funding: \"money for your business\",\n        \"funding-capital\": \"money for day-to-day costs, equipment, or growth\",\n        \"funding-building\": \"money to buy or fix up a building\",\n        \"np-grant\": \"funding for a community project\",\n        \"np-sponsorship\": \"a sponsor for an event or initiative\"\n      }\n    },\n    es: {\n      stage: {\n        idea: \"est\u00e1s empezando\",\n        existing: \"ya tienes un negocio en marcha\",\n        expanding: \"te est\u00e1s expandiendo o mudando a Brownsville\"\n      },\n      need: {\n        learn: \"aprender a iniciar o manejar un negocio\",\n        mentorship: \"mentores y una comunidad de due\u00f1os de negocios\",\n        space: \"un edificio, oficina o espacio de trabajo\",\n        market: \"ayuda para conseguir clientes o visibilidad\",\n        events: \"apoyo para eventos\",\n        funding: \"dinero para tu negocio\",\n        \"funding-capital\": \"dinero para gastos del d\u00eda a d\u00eda, equipo o crecimiento\",\n        \"funding-building\": \"dinero para comprar o arreglar un edificio\",\n        \"np-grant\": \"financiamiento para un proyecto comunitario\",\n        \"np-sponsorship\": \"un patrocinador para un evento o iniciativa\"\n      }\n    }\n  };\n  var REASONS; \/\/ set by applyLang()\n\n  function geoOk(program, location) {\n    if (program.geo === \"any\" || !location) return true;\n    if (location === \"brownsville\") return true;                    \/\/ satisfies brownsville + cameron\n    if (location === \"cameron\") return program.geo === \"cameron\";\n    return false;                                                    \/\/ outside Cameron County\n  }\n\n  function candidates(programs, state, opts) {\n    opts = opts || {};\n    return programs.filter(function (p) {\n      if (!p.active) return false;\n      if (p.id === \"contact-bcic\") return false; \/\/ fallback card handled separately\n      if (state.audience && p.audience !== \"any\" && p.audience !== state.audience) return false;\n      if (!opts.ignoreStage && state.audience === \"business\" && state.stage && p.stages.indexOf(\"any\") === -1 && p.stages.indexOf(state.stage) === -1) return false;\n      if (state.need && state.need !== \"other\" && p.needs.indexOf(state.need) === -1) return false;\n      if (!opts.ignoreGeo && !geoOk(p, state.location)) return false;\n      return true;\n    });\n  }\n\n  function nextQuestion(state) {\n    \/\/ First name comes before everything else. `undefined` means \"not asked\n    \/\/ yet\"; an empty string means \"asked and skipped\", so it is never re-asked.\n    if (state.name === undefined) return { id: \"name\", kind: \"text\" };\n    if (!state.audience) return QUESTIONS.audience;\n    if (state.audience === \"other\") return null;\n    if (state.audience === \"nonprofit\") return state.need ? null : QUESTIONS.npNeed;\n    if (state.showAll) return null;\n    if (!state.stage) return QUESTIONS.stage;\n    if (!state.need) return QUESTIONS.need;\n    if (state.need === \"funding\") return QUESTIONS.fundingType;\n    if (!state.location) {\n      var pool = candidates(PROGRAMS, state, { ignoreGeo: true });\n      var restricted = pool.some(function (p) { return p.geo !== \"any\"; });\n      if (restricted) return QUESTIONS.location;\n    }\n    return null;\n  }\n\n  function recommend(state) {\n    var geoExcluded = false;\n    var pool = candidates(PROGRAMS, state, {});\n    if (state.location) {\n      geoExcluded = candidates(PROGRAMS, state, { ignoreGeo: true }).length > pool.length;\n    }\n    \/\/ If nothing matched, figure out whether the visitor's business stage was\n    \/\/ the reason (e.g. an idea-stage visitor asking about a grant that's\n    \/\/ reserved for already-operating businesses) so we can explain why and\n    \/\/ point them somewhere useful, instead of a bare \"talk to us\" dead end.\n    var stageExcluded = false;\n    if (pool.length === 0 && state.audience === \"business\" && state.stage) {\n      stageExcluded = candidates(PROGRAMS, state, { ignoreGeo: true, ignoreStage: true }).length > 0;\n    }\n    \/\/ Boost a program the visitor searched for before starting the quiz\n    var q = (state.query || \"\").toLowerCase();\n    pool.sort(function (a, b) {\n      var qa = q && (a.name.toLowerCase().indexOf(q) > -1 || a.keywords.some(function (kw) { return q.indexOf(kw) > -1; })) ? 1 : 0;\n      var qb = q && (b.name.toLowerCase().indexOf(q) > -1 || b.keywords.some(function (kw) { return q.indexOf(kw) > -1; })) ? 1 : 0;\n      if (qa !== qb) return qb - qa;\n      return a.priority - b.priority;\n    });\n    return { list: pool.slice(0, 3), geoExcluded: geoExcluded, stageExcluded: stageExcluded };\n  }\n\n  function searchPrograms(query) {\n    var q = query.toLowerCase().trim();\n    if (!q) return [];\n    var terms = q.split(\/\\s+\/).filter(function (t) { return t.length > 1; });\n    if (!terms.length) return [];\n    return PROGRAMS\n      .filter(function (p) { return p.active; })\n      .map(function (p) {\n        var hay = { name: p.name.toLowerCase(), kw: p.keywords.join(\" \"), cat: p.category.toLowerCase(), desc: p.short_description.toLowerCase() };\n        var score = 0;\n        terms.forEach(function (t) {\n          if (hay.name.indexOf(t) > -1) score += 5;\n          if (hay.kw.indexOf(t) > -1) score += 3;\n          if (hay.cat.indexOf(t) > -1) score += 2;\n          if (hay.desc.indexOf(t) > -1) score += 1;\n        });\n        return { p: p, score: score };\n      })\n      .filter(function (x) { return x.score > 0; })\n      .sort(function (a, b) { return b.score - a.score || a.p.priority - b.p.priority; })\n      .slice(0, 5)\n      .map(function (x) { return x.p; });\n  }\n\n  \/* ---------------- Rendering ---------------- *\/\n  var PROGRAMS_EN = [], PROGRAMS_ES = [], PROGRAMS = [];\n  var LANG = \"en\";\n  var CHIP_SETS = {\n    en: [\n      { label: \"Start a business\", emoji: \"\ud83d\ude80\", preset: { audience: \"business\", stage: \"idea\" } },\n      { label: \"Get funding\", emoji: \"\ud83d\udcb0\", preset: { audience: \"business\", need: \"funding\" } },\n      { label: \"Fix up my building\", emoji: \"\ud83d\udc77\", preset: { audience: \"business\", need: \"funding-building\" } },\n      { label: \"Learn the basics\", emoji: \"\ud83d\udcda\", preset: { audience: \"business\", need: \"learn\" } },\n      { label: \"Find mentors\", emoji: \"\ud83d\udc65\", preset: { audience: \"business\", need: \"mentorship\" } },\n      { label: \"Non-profit grants\", emoji: \"\ud83e\udd1d\", preset: { audience: \"nonprofit\", need: \"np-grant\" } },\n      { label: \"Find a space\", emoji: \"\ud83d\udd0d\", preset: { audience: \"business\", need: \"space\" } }\n    ],\n    es: [\n      { label: \"Iniciar un negocio\", emoji: \"\ud83d\ude80\", preset: { audience: \"business\", stage: \"idea\" } },\n      { label: \"Obtener financiamiento\", emoji: \"\ud83d\udcb0\", preset: { audience: \"business\", need: \"funding\" } },\n      { label: \"Arreglar mi edificio\", emoji: \"\ud83d\udc77\", preset: { audience: \"business\", need: \"funding-building\" } },\n      { label: \"Aprender lo b\u00e1sico\", emoji: \"\ud83d\udcda\", preset: { audience: \"business\", need: \"learn\" } },\n      { label: \"Encontrar mentores\", emoji: \"\ud83d\udc65\", preset: { audience: \"business\", need: \"mentorship\" } },\n      { label: \"Apoyos para sin fines de lucro\", emoji: \"\ud83e\udd1d\", preset: { audience: \"nonprofit\", need: \"np-grant\" } },\n      { label: \"Encontrar un espacio\", emoji: \"\ud83d\udd0d\", preset: { audience: \"business\", need: \"space\" } }\n    ]\n  };\n  var CHIPS; \/\/ set by applyLang()\n\n  \/\/ Fallback-of-fallback contactProgram() object, only used if even the\n  \/\/ active FALLBACK\/FALLBACK_ES data is somehow missing a contact-bcic row.\n  var CONTACT_FALLBACK = {\n    en: { id: \"contact-bcic\", name: \"Talk to the BCIC team\", category: \"Get help\", short_description: \"Tell us what you\u2019re working on and we\u2019ll point you to the right program.\", url: \"https:\/\/brownsvillecic.com\/contact-bcic\/\", cta_label: \"Contact us\" },\n    es: { id: \"contact-bcic\", name: \"Habla con el equipo de BCIC\", category: \"Obtener ayuda\", short_description: \"\u00bfNo sabes por d\u00f3nde empezar? Cu\u00e9ntanos en qu\u00e9 est\u00e1s trabajando y te dirigimos al programa correcto.\", url: \"https:\/\/brownsvillecic.com\/contact-bcic\/\", cta_label: \"Cont\u00e1ctanos\" }\n  };\n\n  \/\/ Sets LANG plus everything derived from it: the live S\/QUESTIONS\/CHIPS\/\n  \/\/ PROGRAMS pointers every render function reads, and the widget's own\n  \/\/ lang attribute (for screen readers \u2014 the surrounding page stays\n  \/\/ lang=\"en\"; only this widget's container switches).\n  \/* What language is this page in?\n\n     Checked in order of how explicit the signal is. Returns null when nothing\n     says anything, so the caller can fall back rather than guess wrong.\n\n       1. <html lang> -- what TranslatePress sets (\"es-MX\"), what every other\n          WP translation plugin sets, and what screen readers already trust.\n       2. \/es\/ path prefix -- TranslatePress's URL scheme here.\n       3. the translatepress-es_MX body class -- last resort, plugin-specific.\n\n     The ?lang= override deliberately lives in resolveLang(), NOT here. If it\n     lived here, detectPageLang() would report the override back to itself and\n     the mismatch check below could never fire -- the widget would be unable to\n     notice it was rendering in a different language from its own page. *\/\n  function detectPageLang() {\n    var hl = String(document.documentElement.getAttribute(\"lang\") || \"\").toLowerCase();\n    if (hl.indexOf(\"es\") === 0) return \"es\";\n    if (hl.indexOf(\"en\") === 0) return \"en\";\n\n    try {\n      if (\/^\\\/es(\\\/|$)\/i.test(location.pathname)) return \"es\";\n      if (\/^\\\/en(\\\/|$)\/i.test(location.pathname)) return \"en\";\n    } catch (e) { \/* non-fatal *\/ }\n\n    var bc = String((document.body && document.body.className) || \"\");\n    if (\/translatepress-es\/i.test(bc)) return \"es\";\n    if (\/translatepress-en\/i.test(bc)) return \"en\";\n\n    return null;\n  }\n\n  \/\/ An explicit ?lang=en|es in the URL. Beats everything, including the page.\n  function urlLangOverride() {\n    try {\n      var q = new URL(location.href).searchParams.get(\"lang\");\n      if (q === \"es\" || q === \"en\") return q;\n    } catch (e) { \/* URL API missing or blocked *\/ }\n    return null;\n  }\n\n  \/\/ The language the widget should boot in.\n  function resolveLang() {\n    var cfg = CFG.language || {};\n    var mode = String(cfg.mode || \"page\").toLowerCase();\n    var fallback = (cfg.fallback === \"es\") ? \"es\" : \"en\";\n    var override = urlLangOverride();\n    if (override) return override;\n    if (mode === \"es\" || mode === \"en\") return mode;\n    return detectPageLang() || fallback;   \/\/ \"page\" and \"user\"\n  }\n\n  function switcherVisible() {\n    var cfg = CFG.language || {};\n    if (cfg.showSwitcher === true) return true;\n    if (cfg.showSwitcher === false) return false;\n    return String(cfg.mode || \"page\").toLowerCase() === \"user\";   \/\/ \"auto\"\n  }\n\n  function applyLang(lang) {\n    LANG = (lang === \"es\") ? \"es\" : \"en\";\n    S = {};\n    for (var k in STRINGS.en) S[k] = (STRINGS[LANG] && STRINGS[LANG][k]) || STRINGS.en[k];\n    if (LANG === \"en\" && CFG.strings) { for (var k2 in CFG.strings) { if (CFG.strings[k2]) S[k2] = CFG.strings[k2]; } }\n    QUESTIONS = QUESTION_SETS[LANG] || QUESTION_SETS.en;\n    CHIPS = CHIP_SETS[LANG] || CHIP_SETS.en;\n    REASONS = REASON_SETS[LANG] || REASON_SETS.en;\n    PROGRAMS = (LANG === \"es\") ? PROGRAMS_ES : PROGRAMS_EN;\n    root.setAttribute(\"lang\", LANG);\n  }\n\n  \/\/ Each language's own name is shown untranslated on purpose \u2014 a Spanish\n  \/\/ speaker needs to recognize \"Espa\u00f1ol\" before anything else on the page\n  \/\/ has switched language.\n  function langSwitcher() {\n    var group = el(\"div\", { \"class\": \"fyp-langswitch\", role: \"group\", \"aria-label\": \"Language \/ Idioma\" });\n    [[\"en\", \"English\"], [\"es\", \"Espa\u00f1ol\"]].forEach(function (pair) {\n      group.appendChild(el(\"button\", {\n        type: \"button\", \"class\": \"fyp-lang-btn\", \"aria-pressed\": String(LANG === pair[0]), text: pair[1],\n        onclick: function () { switchLang(pair[0]); }\n      }));\n    });\n    return group;\n  }\n\n  function switchLang(lang) {\n    if (lang === LANG) return;\n    applyLang(lang);\n    track(\"lang_switch\", { lang: LANG });\n    try {\n      var u = new URL(location.href);\n      u.searchParams.set(\"lang\", LANG);\n      window.history.replaceState(null, \"\", u.toString());\n    } catch (e) { \/* non-fatal: URL API missing or blocked *\/ }\n    \/\/ Repaint where they are, not the home screen. See SCREEN above.\n    if (SCREEN.rerender) { try { SCREEN.rerender(); return; } catch (e) { \/* fall through *\/ } }\n    renderHome(true);\n  }\n\n  var state = {};\n  \/\/ NOTE: this used to be called `history`, which shadowed window.history for\n  \/\/ the whole IIFE and silently broke the ?lang= URL update in switchLang()\n  \/\/ (the call threw and was swallowed by its own try\/catch). Renamed.\n  var trail = [];\n  var qNum = 0;\n\n  function el(tag, attrs, children) {\n    var node = document.createElement(tag);\n    if (attrs) for (var a in attrs) {\n      if (a === \"text\") node.textContent = attrs[a];\n      else if (a === \"html\") node.innerHTML = attrs[a];\n      else if (a.indexOf(\"on\") === 0) node.addEventListener(a.slice(2), attrs[a]);\n      else if (attrs[a] === false || attrs[a] == null) { \/* skip *\/ }\n      else node.setAttribute(a, attrs[a]);\n    }\n    (children || []).forEach(function (c) { if (c) node.appendChild(c); });\n    return node;\n  }\n\n  \/* The current screen, so two things can work that otherwise can't.\n\n     cancel: every screen that sets a timer registers a canceller here, and\n     shell() runs it before painting the next screen. Before v9, calculating()\n     left its 1.45s chain running; switching language mid-interstitial fired a\n     results render over the top of a wiped state and the visitor got three\n     arbitrary programs presented as their matches, with the promised email\n     silently never sent. Killing stray timers centrally means no future screen\n     can reintroduce that class of bug by forgetting to clean up.\n\n     rerender: repaints THIS screen from the current state. Switching language\n     used to call renderHome(), which wipes state, trail and qNum -- so a\n     Spanish speaker who got three questions in and then noticed the language\n     button lost everything. Every quiz answer is a language-independent token\n     (audience\/stage\/need\/location), so the same state renders correctly in\n     either language; only the labels change. *\/\n  var SCREEN = { cancel: null, rerender: null };\n\n  \/* Addresses already submitted this session. Deliberately module-level and\n     never copied into `trail`, so no back-navigation can resurrect a\n     pre-send snapshot. See sentBox(). *\/\n  var SENT = {};\n\n  var live; \/\/ aria-live region\n  function announce(msg) { if (live) { live.textContent = \"\"; setTimeout(function () { live.textContent = msg; }, 30); } }\n\n  function shell() {\n    if (SCREEN.cancel) { try { SCREEN.cancel(); } catch (e) { \/* non-fatal *\/ } }\n    SCREEN.cancel = null;\n    root.innerHTML = \"\";\n    if (switcherVisible()) root.appendChild(langSwitcher());\n    live = el(\"div\", { \"aria-live\": \"polite\", \"class\": \"fyp-sr-only\" });\n    root.appendChild(live);\n    var view = el(\"div\", { \"class\": \"fyp-view\" });\n    root.appendChild(view);\n    return view;\n  }\n\n  function focusTitle(view) {\n    var t = view.querySelector(\".fyp-step-title, .fyp-headline\");\n    if (t) { t.setAttribute(\"tabindex\", \"-1\"); t.focus({ preventScroll: false }); }\n  }\n\n  \/* ----- Home: search bar + chips ----- *\/\n  function renderHome(focus) {\n    state = {}; trail = []; qNum = 0;\n    var view = shell();\n    SCREEN.rerender = function () { renderHome(false); };\n    view.appendChild(el(\"h2\", { \"class\": \"fyp-headline\", text: S.headline }));\n    view.appendChild(el(\"p\", { \"class\": \"fyp-intro\", text: S.intro }));\n\n    var input, listbox, activeIdx = -1, current = [];\n    var listboxId = \"fyp-sug-list\";\n\n    function closeList() { listbox.hidden = true; input.setAttribute(\"aria-expanded\", \"false\"); activeIdx = -1; }\n    function openList() { if (current.length) { listbox.hidden = false; input.setAttribute(\"aria-expanded\", \"true\"); } }\n\n    function suggestionHint(p) {\n      return (p.search_hint || p.short_description || \"\").trim();\n    }\n\n    function renderList() {\n      listbox.innerHTML = \"\";\n      current.forEach(function (p, i) {\n        var btn = el(\"button\", {\n          \"class\": \"fyp-suggestion\", type: \"button\", role: \"option\",\n          id: \"fyp-sug-\" + i, \"aria-selected\": String(i === activeIdx),\n          onclick: function () { track(\"suggestion_click\", { program: p.id, query: input.value }); showResults({ list: [p], geoExcluded: false }, \"search\"); }\n        });\n        btn.appendChild(el(\"span\", { text: p.name }));\n        btn.appendChild(el(\"span\", { \"class\": \"fyp-sug-cat\", text: p.category }));\n        var hint = suggestionHint(p);\n        if (hint) btn.appendChild(el(\"span\", { \"class\": \"fyp-sug-hint\", text: hint }));\n        listbox.appendChild(el(\"li\", null, [btn]));\n      });\n      if (current.length) openList(); else closeList();\n    }\n\n    var wrap = el(\"div\", { \"class\": \"fyp-searchwrap\" });\n    input = el(\"input\", {\n      \"class\": \"fyp-search\", type: \"text\", id: \"fyp-search-input\",\n      placeholder: (function () { try { return (window.matchMedia(\"(max-width: 400px)\").matches && S.searchPlaceholderShort) || S.searchPlaceholder; } catch (e) { return S.searchPlaceholder; } })(), autocomplete: \"off\",\n      role: \"combobox\", \"aria-expanded\": \"false\", \"aria-controls\": listboxId, \"aria-autocomplete\": \"list\",\n      oninput: function () {\n        current = searchPrograms(input.value);\n        activeIdx = -1; input.removeAttribute(\"aria-activedescendant\");\n        renderList();\n      },\n      onkeydown: function (e) {\n        if (e.key === \"ArrowDown\" || e.key === \"ArrowUp\") {\n          if (!current.length) return;\n          e.preventDefault();\n          activeIdx = e.key === \"ArrowDown\" ? (activeIdx + 1) % current.length : (activeIdx - 1 + current.length) % current.length;\n          input.setAttribute(\"aria-activedescendant\", \"fyp-sug-\" + activeIdx);\n          renderList();\n        } else if (e.key === \"Enter\") {\n          e.preventDefault();\n          if (activeIdx >= 0 && current[activeIdx]) {\n            var p = current[activeIdx];\n            track(\"suggestion_click\", { program: p.id, query: input.value });\n            showResults({ list: [p], geoExcluded: false }, \"search\");\n          } else {\n            track(\"search\", { query: input.value });\n            startQuiz(input.value);\n          }\n        } else if (e.key === \"Escape\") { closeList(); }\n      },\n      onblur: function () { setTimeout(closeList, 150); }\n    });\n    var label = el(\"label\", { \"class\": \"fyp-sr-only\", \"for\": \"fyp-search-input\", text: S.searchLabel });\n    listbox = el(\"ul\", { \"class\": \"fyp-suggestions\", id: listboxId, role: \"listbox\", hidden: \"hidden\" });\n    listbox.hidden = true;\n    wrap.appendChild(label); wrap.appendChild(input); wrap.appendChild(listbox);\n    view.appendChild(wrap);\n\n    if (S.searchExamples && S.searchExamples.length > 1) {\n      (function () {\n        var reduced = false;\n        try { reduced = window.matchMedia && window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches; } catch (e) { \/* treat as not reduced *\/ }\n        var narrow = false;\n        try { narrow = window.matchMedia(\"(max-width: 400px)\").matches; } catch (e) { \/* treat as not narrow *\/ }\n        var EX = (narrow && S.searchExamplesShort) || S.searchExamples;\n        var idx = 0;\n        var timer = null;\n        function active() { return document.body.contains(input); }\n        function busy() { return document.activeElement === input || input.value; }\n        if (reduced) {\n          timer = setInterval(function () {\n            if (!active()) { clearInterval(timer); return; }\n            if (busy()) return;\n            idx = (idx + 1) % EX.length;\n            input.setAttribute(\"placeholder\", EX[idx]);\n          }, 3200);\n          return;\n        }\n        function wait(ms, fn) { timer = setTimeout(function () { if (active()) fn(); }, ms); }\n        function typeChar(text, pos) {\n          if (busy()) { wait(400, function () { typeChar(text, pos); }); return; }\n          input.setAttribute(\"placeholder\", text.slice(0, pos));\n          if (pos < text.length) wait(40, function () { typeChar(text, pos + 1); });\n          else wait(1500, function () { deleteChar(text, text.length); });\n        }\n        function deleteChar(text, pos) {\n          if (busy()) { wait(400, function () { deleteChar(text, pos); }); return; }\n          input.setAttribute(\"placeholder\", text.slice(0, pos));\n          if (pos > 0) wait(22, function () { deleteChar(text, pos - 1); });\n          else {\n            idx = (idx + 1) % EX.length;\n            wait(300, function () { typeChar(EX[idx], 0); });\n          }\n        }\n        wait(1200, function () { typeChar(EX[idx], 0); });\n      })();\n    }\n\n    var chips = CHIPS;\n    var chipList = el(\"ul\", { \"class\": \"fyp-chips\", \"aria-label\": S.chipsLabel });\n    chips.forEach(function (c) {\n      chipList.appendChild(el(\"li\", null, [el(\"button\", {\n        \"class\": \"fyp-chip\", type: \"button\",\n        onclick: function () { track(\"chip_click\", { chip: chipToken(c) }); startQuiz(\"\", c.preset); }\n      }, [\n        el(\"span\", { \"aria-hidden\": \"true\", \"class\": \"fyp-chip-emoji\", text: c.emoji }),\n        document.createTextNode(\" \" + c.label)\n      ])]));\n    });\n    view.appendChild(chipList);\n\n    view.appendChild(el(\"div\", { \"class\": \"fyp-nav\" }, [\n      el(\"button\", { \"class\": \"fyp-btn\", type: \"button\", text: S.guideBtn, onclick: function () { track(\"quiz_start\", { from: \"button\" }); startQuiz(\"\"); } })\n    ]));\n    if (focus) focusTitle(view);\n  }\n\n  \/* ----- Office hours ----------------------------------------------------\n     Returns true (open), false (closed), or null (couldn't tell). null is a\n     real outcome, not an error: if Intl or the time zone database is missing\n     we must not guess, because guessing \"open\" sends someone to a phone that\n     nobody answers. Callers treat null as \"show the neutral CTA\".\n\n     Evaluated in the office's own time zone, never the visitor's -- see the\n     note on CFG.office above for why that matters. *\/\n  function officeOpenNow(now) {\n    var o = CFG.office || {};\n    var tz = o.timeZone || \"America\/Chicago\";\n    var days = o.openDays || [1, 2, 3, 4, 5];\n    var openHour = (o.openHour == null) ? 9 : o.openHour;\n    var closeHour = (o.closeHour == null) ? 16 : o.closeHour;\n    try {\n      var parts = new Intl.DateTimeFormat(\"en-US\", {\n        timeZone: tz, weekday: \"short\", hour: \"numeric\", hour12: false\n      }).formatToParts(now || new Date());\n      var wdName = null, hour = null;\n      for (var i = 0; i < parts.length; i++) {\n        if (parts[i].type === \"weekday\") wdName = parts[i].value;\n        if (parts[i].type === \"hour\") hour = parseInt(parts[i].value, 10);\n      }\n      var WD = { Sun: 0, Mon: 1, Tue: 2, Wed: 3, Thu: 4, Fri: 5, Sat: 6 };\n      var wd = WD[wdName];\n      \/\/ Some engines render midnight as \"24\" under hour12:false.\n      if (hour === 24) hour = 0;\n      if (wd == null || hour == null || isNaN(hour)) return null;\n      if (days.indexOf(wd) === -1) return false;\n      return hour >= openHour && hour < closeHour;\n    } catch (e) {\n      return null;\n    }\n  }\n\n  \/* Tidy a typed first name for display and for pre-filling the email form.\n\n     This is an all-or-nothing check rather than a scrub. Scrubbing junk out of\n     hostile input just yields different junk: pasting a script tag and then\n     removing the disallowed characters leaves \"Scriptalertscript ana\", which\n     is worse than showing nothing. So: if the input contains any character\n     that never appears in a real first name (digits, angle brackets, slashes,\n     @, punctuation beyond an apostrophe\/hyphen\/period), the whole value is\n     discarded and the visitor is treated as having skipped the step -- they\n     still get results, just the unnamed copy.\n\n     Latin-1 letters are allowed through so Spanish names (Jos\u00e9, Mu\u00f1oz) and\n     hyphenated or apostrophed names (Mar\u00eda-Jos\u00e9, O'Brien) survive intact.\n\n     Note this is about presentation, not XSS: names are always written with\n     textContent or .value, never innerHTML, so there is no injection path\n     either way. *\/\n  var NAME_OK = \/^[A-Za-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u00FF][A-Za-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u00FF' .\\-]*$\/;\n\n  function cleanName(v) {\n    var out = String(v == null ? \"\" : v).replace(\/\\s+\/g, \" \").trim().slice(0, 40).trim();\n    if (!out) return \"\";\n    if (!NAME_OK.test(out)) return \"\";           \/\/ not a name -- treat as skipped\n    \/\/ Capitalize the start of each segment so \"maria-jose\" and \"o'brien\" come\n    \/\/ back as \"Maria-Jose\" and \"O'Brien\". Only ever upper-cases a letter, never\n    \/\/ lower-cases one, so someone who types \"MARIA\" keeps it.\n    return out.replace(\/(^|[\\s'\\-])([a-z\\u00E0-\\u00F6\\u00F8-\\u00FF])\/g, function (m, sep, ch) {\n      return sep + ch.toUpperCase();\n    });\n  }\n\n  \/* ----- Quiz ----- *\/\n  function startQuiz(query, preset) {\n    state = { query: query || \"\" };\n    if (preset) for (var k in preset) state[k] = preset[k];\n    trail = []; qNum = 0;\n    step();\n  }\n\n  \/* Inline icons. Stroke-based, currentColor, so they inherit button colour and\n     need no image requests. aria-hidden because the adjacent text already says\n     what the control does. *\/\n  var ICONS = {\n    phone: '<svg class=\"fyp-ico\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6A19.79 19.79 0 0 1 2.12 4.18 2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72c.13.96.36 1.9.7 2.81a2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45c.9.34 1.85.57 2.81.7A2 2 0 0 1 22 16.92z\"\/><\/svg>',\n    mail: '<svg class=\"fyp-ico\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect x=\"2\" y=\"4\" width=\"20\" height=\"16\" rx=\"2\"\/><path d=\"m2 7 10 6 10-6\"\/><\/svg>',\n    check: '<svg class=\"fyp-ico\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M20 6 9 17l-5-5\"\/><\/svg>',\n    chat: '<svg class=\"fyp-ico\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z\"\/><\/svg>',\n    inbox: '<svg class=\"fyp-ico\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><polyline points=\"22 12 16 12 14 15 10 15 8 12 2 12\"\/><path d=\"M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z\"\/><\/svg>',\n    check: '<svg class=\"fyp-ico\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M20 6 9 17l-5-5\"\/><\/svg>'\n  };\n\n  \/\/ Build a button\/link whose label sits next to an icon.\n  function icoBtn(tag, attrs, iconKey, label) {\n    attrs[\"class\"] = (attrs[\"class\"] || \"fyp-btn\") + \" fyp-btn--ico\";\n    var node = el(tag, attrs);\n    var holder = document.createElement(\"span\");\n    holder.innerHTML = ICONS[iconKey];          \/\/ static, authored above -- no user input\n    var svg = holder.firstChild;\n    if (svg) node.appendChild(svg);\n    node.appendChild(document.createTextNode(label));\n    return node;\n  }\n\n  function sepInto(view) {\n    view.appendChild(el(\"hr\", { \"class\": \"fyp-sep\" }));\n  }\n\n  var BACK_ICON = '<svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M15 18l-6-6 6-6\"\/><\/svg>';\n\n  \/\/ Shared by the in-quiz Back button and the Back control on the results\n  \/\/ screens: pop the last answer (so its question re-asks) or, if there's\n  \/\/ nothing to pop (e.g. arrived via search), just go home.\n  function goBack() {\n    if (trail.length) { var prev = trail.pop(); state = prev.state; qNum = prev.qNum - 1; step(); }\n    else renderHome(true);\n  }\n\n  function backButton() {\n    return el(\"button\", {\n      \"class\": \"fyp-btn fyp-btn--ghost fyp-btn--back\", type: \"button\",\n      \"aria-label\": S.back, title: S.back,\n      html: BACK_ICON,\n      onclick: goBack\n    });\n  }\n\n  function step() {\n    var q = nextQuestion(state);\n    if (!q) { finishQuiz(); return; }\n    if (q.kind === \"text\") { nameStep(q); return; }\n    qNum++;\n    renderQuestion(q);\n  }\n\n  \/* Split out of step() so a language switch can repaint the same question\n     without advancing qNum. nextQuestion() is a pure function of state, so\n     re-deriving it after applyLang() yields the same question with translated\n     labels. *\/\n  function personalizeTitle(title) {\n    if (!state.name) return title;\n    var m = title.match(\/[A-Za-z\\u00C0-\\u024F]\/);\n    if (!m) return state.name + \", \" + title;\n    var i = m.index;\n    return state.name + \", \" + title.slice(0, i) + title.charAt(i).toLowerCase() + title.slice(i + 1);\n  }\n  function renderQuestion(q) {\n    var view = shell();\n    SCREEN.rerender = function () {\n      var again = nextQuestion(state);\n      if (!again) { finishQuiz(); return; }\n      if (again.kind === \"text\") { nameStep(again); return; }\n      renderQuestion(again);\n    };\n    view.appendChild(el(\"p\", { \"class\": \"fyp-meta\", text: S.questionCount.replace(\"{n}\", qNum) }));\n    view.appendChild(el(\"h3\", { \"class\": \"fyp-step-title\", text: personalizeTitle(q.title) }));\n    var opts = el(\"ul\", { \"class\": \"fyp-options\" });\n    q.options.forEach(function (opt) {\n      opts.appendChild(el(\"li\", null, [el(\"button\", {\n        \"class\": \"fyp-option\", type: \"button\", text: opt.label,\n        onclick: function () {\n          var btn = this;\n          Array.prototype.forEach.call(opts.querySelectorAll(\".fyp-option\"), function (b) { b.disabled = true; });\n          btn.classList.add(\"fyp-option-picked\");\n          trail.push({ state: JSON.parse(JSON.stringify(state)), qNum: qNum });\n          for (var k in opt.set) state[k] = opt.set[k];\n          \/\/ Log the stable token, not the translated label -- otherwise the\n          \/\/ same answer lands as two different values in EN and ES and no\n          \/\/ report can compare them, and any copy edit breaks history.\n          track(\"answer\", { question: q.id, answer: answerToken(opt) });\n          setTimeout(step, 160);\n        }\n      })]));\n    });\n    view.appendChild(opts);\n    var nav = el(\"div\", { \"class\": \"fyp-nav\" });\n    nav.appendChild(backButton());\n    view.appendChild(nav);\n    announce(q.title);\n    focusTitle(view);\n  }\n\n  \/* The one free-text step. Kept deliberately skippable: the top of this\n     funnel is the part that already works (most visitors who land here do\n     start the quiz), so a hard gate on a name field is the one change most\n     likely to cost more visitors than the personalization wins back. Skipping\n     stores \"\" and every downstream string falls back to its unnamed form. *\/\n  function nameStep(q, prefill) {\n    if (prefill === undefined) qNum++;\n    var view = shell();\n    view.appendChild(el(\"p\", { \"class\": \"fyp-meta\", text: S.questionCount.replace(\"{n}\", qNum) }));\n    view.appendChild(el(\"h3\", { \"class\": \"fyp-step-title\", text: S.nameTitle }));\n    view.appendChild(el(\"p\", { \"class\": \"fyp-news-hint\", text: S.nameHint }));\n\n    var input = el(\"input\", {\n      \"class\": \"fyp-nameinput\", type: \"text\", id: \"fyp-name-input\",\n      autocomplete: \"given-name\", placeholder: S.namePlaceholder,\n      \"aria-label\": S.namePlaceholder, maxlength: \"40\"\n    });\n\n    function commit(value) {\n      trail.push({ state: JSON.parse(JSON.stringify(state)), qNum: qNum });\n      state.name = value;\n      track(\"answer\", { question: \"name\", answer: value ? \"provided\" : \"skipped\" });\n      step();\n    }\n\n    input.addEventListener(\"keydown\", function (e) {\n      if (e.key === \"Enter\" || e.keyCode === 13) { e.preventDefault(); commit(cleanName(input.value)); }\n    });\n\n    SCREEN.rerender = function () {\n      var typed = input.value;\n      var again = nextQuestion(state);\n      if (again && again.kind === \"text\") { nameStep(again, typed); }\n      else { step(); }\n    };\n\n    if (prefill !== undefined) input.value = prefill;\n    view.appendChild(el(\"div\", { \"class\": \"fyp-namerow\" }, [\n      input,\n      el(\"button\", {\n        \"class\": \"fyp-btn\", type: \"button\", text: S.nameContinue,\n        onclick: function () { commit(cleanName(input.value)); }\n      })\n    ]));\n\n    view.appendChild(el(\"div\", { \"class\": \"fyp-nav\" }, [\n      backButton(),\n      el(\"button\", {\n        \"class\": \"fyp-btn fyp-btn--ghost\", type: \"button\", text: S.nameSkip,\n        onclick: function () { commit(\"\"); }\n      })\n    ]));\n\n    announce(S.nameTitle);\n    \/\/ Focus the heading, not the input: it keeps the screen-reader flow\n    \/\/ identical to every other step and avoids yanking up the mobile keyboard\n    \/\/ before the visitor has read the question.\n    focusTitle(view);\n  }\n\n  \/* End of the quiz. Two things now sit between the last answer and the\n     results: the optional email ask, then a short \"finding your matches\"\n     beat. Both are skipped for the \"show me everything\" branch, which isn't a\n     recommendation and shouldn't pretend to compute one. *\/\n  function finishQuiz() {\n    if (state.showAll) { showAll(); return; }\n    if (state.email === undefined) { emailStep(); return; }\n    calculating(renderFinalResults);\n  }\n\n  function renderFinalResults() {\n    if (state.audience === \"other\" || state.need === \"other\") {\n      showResults({ list: [], geoExcluded: false }, \"quiz\"); \/\/ contact card\n      return;\n    }\n    showResults(recommend(state), \"quiz\");\n  }\n\n  \/* Optional email capture, asked BEFORE the results are shown. Rationale: at\n     this point the visitor has answered everything and is waiting on an\n     answer, which is the moment they're most willing to trade an address --\n     and it means the results email can be on its way before they've finished\n     reading the screen.\n\n     Skipping stores \"\" and the results screen falls back to showing the normal\n     email box, so nobody is cornered. A first-name field appears here only if\n     the name step was skipped, because Mailchimp's audience rejects a signup\n     with no first name (see submitMailchimp). *\/\n  function emailStep(keep) {\n    if (keep === undefined) qNum++;\n    keep = keep || {};\n    var view = shell();\n    var needName = !state.name;\n    view.appendChild(el(\"p\", { \"class\": \"fyp-meta\", text: S.questionCount.replace(\"{n}\", qNum) }));\n    view.appendChild(el(\"h3\", { \"class\": \"fyp-step-title\", text: personalizeTitle(S.emailStepTitle) }));\n    view.appendChild(el(\"p\", { \"class\": \"fyp-news-hint\", text: S.emailStepHint }));\n\n    var msg = el(\"p\", { \"class\": \"fyp-news-msg\", \"aria-live\": \"polite\" });\n    var nameIn = needName ? el(\"input\", {\n      \"class\": \"fyp-textin\", type: \"text\", id: \"fyp-step-name\",\n      autocomplete: \"given-name\", placeholder: S.emailStepNamePlaceholder,\n      \"aria-label\": S.emailStepNamePlaceholder, maxlength: \"40\"\n    }) : null;\n    var emailIn = el(\"input\", {\n      \"class\": \"fyp-textin\", type: \"email\", id: \"fyp-step-email\",\n      autocomplete: \"email\", placeholder: S.emailStepPlaceholder,\n      \"aria-label\": S.emailStepPlaceholder\n    });\n\n    function go() {\n      var addr = emailIn.value.trim();\n      if (!\/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$\/.test(addr)) {\n        msg.className = \"fyp-news-msg fyp-err\"; msg.textContent = S.newsInvalid;\n        try { emailIn.focus(); } catch (e) { \/* non-fatal *\/ }\n        return;\n      }\n      if (nameIn) {\n        var nm = cleanName(nameIn.value);\n        if (!nm) {\n          msg.className = \"fyp-news-msg fyp-err\"; msg.textContent = S.newsNameInvalid;\n          try { nameIn.focus(); } catch (e) { \/* non-fatal *\/ }\n          return;\n        }\n        state.name = nm;\n      }\n      commit(addr);\n    }\n\n    function commit(addr) {\n      trail.push({ state: JSON.parse(JSON.stringify(state)), qNum: qNum });\n      state.email = addr;\n      track(\"email_step\", { provided: !!addr });\n      calculating(renderFinalResults);\n    }\n\n    var stack = el(\"div\", { \"class\": \"fyp-stack\" });\n    if (nameIn) {\n      nameIn.addEventListener(\"keydown\", function (e) {\n        if (e.key === \"Enter\" || e.keyCode === 13) { e.preventDefault(); try { emailIn.focus(); } catch (er) {} }\n      });\n      stack.appendChild(nameIn);\n    }\n    emailIn.addEventListener(\"keydown\", function (e) {\n      if (e.key === \"Enter\" || e.keyCode === 13) { e.preventDefault(); go(); }\n    });\n    stack.appendChild(emailIn);\n    view.appendChild(stack);\n    view.appendChild(msg);\n    view.appendChild(consentLine());\n\n    var goBtn = icoBtn(\"button\", { type: \"button\" }, \"mail\", S.emailStepGo);\n    goBtn.addEventListener(\"click\", go);\n    var skipBtn = el(\"button\", {\n      \"class\": \"fyp-btn fyp-btn--ghost\", type: \"button\", text: S.emailStepSkip,\n      onclick: function () { commit(\"\"); }\n    });\n    view.appendChild(el(\"div\", { \"class\": \"fyp-nav\" }, [backButton(), goBtn, skipBtn]));\n\n    announce(S.emailStepTitle);\n    focusTitle(view);\n  }\n\n  \/* A one-line sentence with the policy link embedded in it. Split on the\n     {link} token so the linked words sit inside the sentence rather than\n     trailing it. With no policy URL configured the same sentence renders as\n     plain text -- a link to a page that doesn't exist is worse than no link,\n     and brownsvillecic.com's policy page is still an unpublished draft.\n\n     Used in two places with two different sentences: asking for consent before\n     the address is taken, and pointing at the policy after it's been used. *\/\n  function policyLine(sentence, extraClass) {\n    var p = el(\"p\", { \"class\": \"fyp-consent\" + (extraClass ? \" \" + extraClass : \"\") });\n    var url = (CFG.policy && CFG.policy.url) || \"\";\n    var parts = String(sentence || \"\").split(\"{link}\");\n    var label = S.emailStepConsentLink || \"\";\n    p.appendChild(document.createTextNode(parts[0] || \"\"));\n    if (url) {\n      p.appendChild(el(\"a\", { href: url, target: \"_blank\", rel: \"noopener\", text: label }));\n    } else {\n      p.appendChild(document.createTextNode(label));\n    }\n    p.appendChild(document.createTextNode(parts.length > 1 ? parts[1] : \"\"));\n    return p;\n  }\n\n  function consentLine() { return policyLine(S.emailStepConsent); }\n\n  \/* Short \"we're working on it\" beat before the results land. Deliberately\n     brief and honest: the matching itself is instant, so this exists to make\n     the hand-off feel considered rather than abrupt. Visitors who asked for\n     reduced motion get the same delay with no animation and a single line. *\/\n  function calculating(done) {\n    var view = shell();\n    view.appendChild(el(\"h3\", { \"class\": \"fyp-step-title\", text: S.calcTitle }));\n    var bar = el(\"div\", { \"class\": \"fyp-calc-bar\" });\n    var fill = document.createElement(\"span\");\n    bar.appendChild(fill);\n    var line = el(\"p\", { \"class\": \"fyp-calc-line\", \"aria-live\": \"polite\" });\n    function setLine(txt) {\n      line.innerHTML = \"\";\n      line.appendChild(el(\"span\", { \"class\": \"fyp-calc-dot\", \"aria-hidden\": \"true\" }));\n      line.appendChild(document.createTextNode(txt));\n    }\n    setLine(S.calcStep1);\n    var wrap = el(\"div\", { \"class\": \"fyp-calc\" }, [bar, line]);\n    view.appendChild(wrap);\n    announce(S.calcTitle);\n    focusTitle(view);\n\n    var reduced = false;\n    try {\n      reduced = window.matchMedia && window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n    } catch (e) { \/* treat as not reduced *\/ }\n\n    var timers = [];\n    function at(ms, fn) { timers.push(setTimeout(fn, ms)); }\n    SCREEN.cancel = function () { timers.forEach(function (t) { clearTimeout(t); }); timers.length = 0; };\n    SCREEN.rerender = function () { calculating(done); };\n\n    if (reduced) {\n      fill.style.width = \"100%\";\n      at(700, done);\n    } else {\n      at(30,   function () { fill.style.width = \"34%\"; });\n      at(520,  function () { fill.style.width = \"68%\"; setLine(S.calcStep2); });\n      at(1000, function () { fill.style.width = \"100%\"; setLine(S.calcStep3); });\n      at(1450, done);\n    }\n  }\n\n  \/* ----- Results ----- *\/\n  function programCard(p, best) {\n    var card = el(\"li\", { \"class\": \"fyp-card\" + (best ? \" fyp-card--best\" : \"\") });\n    if (best) card.appendChild(el(\"span\", { \"class\": \"fyp-card-badge\", text: S.bestMatchLabel }));\n    card.appendChild(el(\"span\", { \"class\": \"fyp-card-cat\", text: p.category }));\n    card.appendChild(el(\"h4\", { text: p.name }));\n    card.appendChild(el(\"p\", { text: p.short_description }));\n    card.appendChild(el(\"a\", {\n      \"class\": \"fyp-btn\", href: resourceUrl(p), text: p.cta_label,\n      onclick: function () { track(\"result_click\", { program: p.id, program_name: p.name }); }\n    }));\n    return card;\n  }\n\n  function programById(id) {\n    return PROGRAMS.filter(function (p) { return p.id === id; })[0];\n  }\n\n  \/\/ Prefer the tracked bit.ly (bcic.us) link from CONFIG.resourceLinks so\n  \/\/ clicks from the widget show up in Bitly's analytics; fall back to the\n  \/\/ plain sheet URL for any program that doesn't have a tracked link yet.\n  function resourceUrl(p) {\n    return (CFG.resourceLinks && CFG.resourceLinks[p.id]) || p.url;\n  }\n\n  function contactProgram() {\n    return programById(\"contact-bcic\") || CONTACT_FALLBACK[LANG] || CONTACT_FALLBACK.en;\n  }\n\n  \/\/ Shown when a visitor's business stage is the reason nothing matched\n  \/\/ (see stageExcluded in recommend()) \u2014 a bridge from \"just starting out\"\n  \/\/ to programs that can actually help right now.\n  function bridgePrograms() {\n    return [\"ebridge-center\", \"ebridge-business-academy\"]\n      .map(programById)\n      .filter(function (p) { return p && p.active; });\n  }\n\n  \/* Results heading. Uses the {name} variants only when the visitor actually\n     gave us one, so a skipped name reads exactly like the current live copy. *\/\n  function resultsTitle(count) {\n    var named = !!state.name;\n    if (count === 0) return named ? S.noMatchTitleNamed.replace(\"{name}\", state.name) : S.noMatchTitle;\n    if (count === 1) return named ? S.resultsOneTitleNamed.replace(\"{name}\", state.name) : S.resultsOneTitle;\n    return named ? S.resultsTitleNamed.replace(\"{name}\", state.name) : S.resultsTitle;\n  }\n\n  \/* \"Because you said <you're already running a business> and want <help\n     learning the ropes>:\" -- built entirely from answers already sitting in\n     `state`, so it costs no extra questions. Returns null when there is\n     nothing worth reading back (e.g. arrived straight from search).\n\n     Built as real nodes rather than innerHTML so the visitor's own answers\n     are never parsed as markup. *\/\n  function reasonLine() {\n    if (!REASONS) return null;\n    var stageText = (state.audience === \"business\" && state.stage) ? REASONS.stage[state.stage] : null;\n    var needText = state.need ? REASONS.need[state.need] : null;\n    var tpl;\n    if (stageText && needText) tpl = S.reasonBoth;\n    else if (stageText) tpl = S.reasonStage;\n    else if (needText) tpl = S.reasonNeed;\n    else return null;\n\n    var p = el(\"p\", { \"class\": \"fyp-reason\" });\n    \/\/ Walk the template and swap {stage}\/{need} for chip spans, keeping the\n    \/\/ surrounding words as plain text so each language controls its own\n    \/\/ word order.\n    var re = \/\\{(stage|need)\\}\/g, last = 0, m;\n    while ((m = re.exec(tpl)) !== null) {\n      if (m.index > last) p.appendChild(document.createTextNode(tpl.slice(last, m.index)));\n      p.appendChild(el(\"span\", { \"class\": \"fyp-reason-chip\", text: m[1] === \"stage\" ? stageText : needText }));\n      last = m.index + m[0].length;\n    }\n    if (last < tpl.length) p.appendChild(document.createTextNode(tpl.slice(last)));\n    return p;\n  }\n\n  \/\/ The displayed opening hours, in the current language.\n  function hoursLabel() {\n    var o = CFG.office || {};\n    var v = (LANG === \"es\" && o.hoursLabelEs) ? o.hoursLabelEs : o.hoursLabel;\n    return v || \"\";\n  }\n\n  \/* The hours sub-line shown outside phone hours. Built piece by piece rather\n     than as one text node so the number can be a real tel: link -- on a phone\n     an un-tappable number is friction, and someone who reads \"try again during\n     our operating hours\" and taps anyway has made an informed choice.\n\n     The tap is tracked separately from the in-hours call button, so the share\n     of people calling outside 9-4 is answerable rather than guessed at. *\/\n  function hoursLine(hrs, phone, href) {\n    var span = el(\"span\", { \"class\": \"fyp-advisor-hours\" });\n    var parts = String(S.advisorClosedHours || \"\").replace(\"{hours}\", hrs).split(\"{phone}\");\n    span.appendChild(document.createTextNode(parts[0] || \"\"));\n    if (parts.length > 1) {\n      if (href) {\n        var a = el(\"a\", { \"class\": \"fyp-advisor-tel\", href: href, text: phone });\n        a.addEventListener(\"click\", function () {\n          track(\"advisor_click\", { mode: \"phone_after_hours\" });\n        });\n        span.appendChild(a);\n      } else {\n        span.appendChild(document.createTextNode(phone));\n      }\n      span.appendChild(document.createTextNode(parts[1] || \"\"));\n    }\n    return span;\n  }\n\n  \/* The advisor call-to-action. Two rendered shapes:\n\n       phone   -> only while the office clock says someone is at the desk,\n                  so the tel: link always reaches a person\n       contact -> everything else: closed, or the clock couldn't be read\n\n     The contact shape states the opening hours as a fact and never asserts\n     anything about right now, which is what \"the office is closed\" got wrong:\n     it fired outside the 9-4 phone window even when the office was open, and\n     it contradicted the published hours. It also links to the contact page\n     rather than back to the email box on this same screen -- re-offering\n     email to someone who already gave us their address made no sense. *\/\n  \/\/ True when the results screen will offer the phone rather than the contact\n  \/\/ page. showResults needs to know this too, to decide whether the \"not\n  \/\/ seeing the right fit?\" footer line is a duplicate.\n  function phoneShape(openNow) {\n    var o = CFG.office || {};\n    var href = o.phoneHref || (o.phone ? \"tel:\" + o.phone.replace(\/[^0-9+]\/g, \"\") : \"\");\n    return openNow === true && !!href;\n  }\n\n  function advisorCta(view, openNow) {\n    var o = CFG.office || {};\n    var phone = o.phone || \"\";\n    var href = o.phoneHref || (phone ? \"tel:\" + phone.replace(\/[^0-9+]\/g, \"\") : \"\");\n    var onPhone = phoneShape(openNow);\n    var wrap = el(\"div\", { \"class\": \"fyp-advisor\" + (onPhone ? \" fyp-advisor--open\" : \"\") });\n    var copy = el(\"p\");\n    var btn;\n\n    if (onPhone) {\n      copy.appendChild(document.createTextNode(S.advisorOpenLead));\n      btn = icoBtn(\"a\", { href: href }, \"phone\", S.advisorOpenBtn.replace(\"{phone}\", phone));\n      btn.addEventListener(\"click\", function () { track(\"advisor_click\", { mode: \"phone\" }); });\n    } else {\n      var hrs = hoursLabel();\n      \/* Outside phone hours this block absorbs the \"not seeing the right fit?\"\n         line from the footer instead of saying its own near-identical thing.\n         showResults drops the footer line in the same branch, so the question\n         is asked exactly once on the screen. *\/\n      copy.appendChild(document.createTextNode(S.footPrompt));\n      if (hrs) copy.appendChild(hoursLine(hrs, phone, href));\n      btn = icoBtn(\"a\", { href: resourceUrl(contactProgram()) }, \"inbox\",\n        hrs ? S.advisorClosedBtn : S.advisorUnknownBtn);\n      btn.addEventListener(\"click\", function () {\n        track(\"advisor_click\", { mode: \"contact\", office: (openNow === false ? \"closed\" : \"unknown\") });\n        track(\"result_click\", { program: \"contact-bcic\", program_name: contactProgram().name });\n      });\n    }\n\n    wrap.appendChild(copy);\n    wrap.appendChild(btn);\n    view.appendChild(wrap);\n  }\n\n  function showResults(rec, source) {\n    var view = shell();\n    SCREEN.rerender = function () { showResults(rec, source); };\n    var list = rec.list || [];\n    var bridges = (list.length === 0 && rec.stageExcluded) ? bridgePrograms() : [];\n    var title = resultsTitle(list.length);\n    view.appendChild(el(\"h3\", { \"class\": \"fyp-step-title\", text: title }));\n    var reason = reasonLine();\n    if (reason) view.appendChild(reason);\n    if (rec.geoExcluded) view.appendChild(el(\"p\", { \"class\": \"fyp-note\", text: S.geoNote }));\n    if (bridges.length) view.appendChild(el(\"p\", { \"class\": \"fyp-note\", text: S.stageNote }));\n    var ul = el(\"ul\", { \"class\": \"fyp-results\" });\n    if (list.length === 0) {\n      bridges.forEach(function (p, i) { ul.appendChild(programCard(p, i === 0)); });\n      ul.appendChild(programCard(contactProgram(), bridges.length === 0));\n    } else {\n      list.forEach(function (p, i) { ul.appendChild(programCard(p, i === 0)); });\n    }\n    view.appendChild(ul);\n\n    var openNow = officeOpenNow();\n    \/* The footer line and the advisor block both ask \"want to talk to a\n       person?\". When the phone is on offer they're saying different things --\n       one is about program fit, the other about calling right now -- so both\n       earn their place. Outside phone hours they collapse into the same\n       sentence, so the footer line goes and the advisor block takes the\n       wording (see advisorCta). *\/\n    if (list.length > 0 && phoneShape(openNow)) {\n      var foot = el(\"p\", { \"class\": \"fyp-footlink\" });\n      foot.appendChild(document.createTextNode(S.footPrompt + \" \"));\n      foot.appendChild(el(\"a\", { href: resourceUrl(contactProgram()), text: S.footLink, onclick: function () { track(\"result_click\", { program: \"contact-bcic\", program_name: contactProgram().name }); } }));\n      foot.appendChild(document.createTextNode(\".\"));\n      view.appendChild(foot);\n    }\n\n    var matched = list.length ? list[0] : (bridges[0] || contactProgram());\n    sepInto(view);\n    \/\/ If the address was already captured before the results were calculated,\n    \/\/ send it now and show a confirmation instead of asking a second time.\n    if (state.email) sentBox(view, matched);\n    else renderNewsletter(view, matched);\n    sepInto(view);\n    advisorCta(view, openNow);\n\n    view.appendChild(el(\"div\", { \"class\": \"fyp-nav\" }, [\n      backButton(),\n      el(\"button\", { \"class\": \"fyp-btn fyp-btn--ghost\", type: \"button\", text: S.startOver, onclick: function () { track(\"restart\", {}); renderHome(true); } })\n    ]));\n\n    track(\"results\", {\n      source: source, count: list.length, stageExcluded: !!rec.stageExcluded,\n      named: !!state.name,\n      office: (openNow === true ? \"open\" : (openNow === false ? \"closed\" : \"unknown\")),\n      programs: list.map(function (p) { return p.id; }).join(\",\")\n    });\n    announce(title);\n    focusTitle(view);\n  }\n\n  \/* Shown in place of the email form when the visitor already gave an address\n     during the quiz. Fires the actual submission here -- this is the first\n     moment the matched program is known, so it's the earliest we can send\n     something worth sending. Submits once per results render; `emailSent`\n     guards against a re-render (e.g. Back then forward) sending twice. *\/\n  function sentBox(view, matched) {\n    var box = el(\"div\", { \"class\": \"fyp-sent\" });\n    var holder = document.createElement(\"span\");\n    holder.innerHTML = ICONS.check;\n    if (holder.firstChild) box.appendChild(holder.firstChild);\n    var body = el(\"div\");\n    var title = el(\"strong\", { text: S.sentTitle.replace(\"{email}\", state.email) });\n    var hint = el(\"span\", { \"class\": \"fyp-sent-hint\", text: S.sentHint });\n    body.appendChild(title); body.appendChild(hint);\n    body.appendChild(policyLine(S.sentPolicy, \"fyp-consent--sent\"));\n    box.appendChild(body);\n    view.appendChild(box);\n\n    \/* Send at most once per address, ever.\n\n       This used to guard on state.emailSent, which does not survive Back:\n       trail stores a deep clone of state taken BEFORE the flag is set, so\n       Back-then-forward restored a state with emailSent undefined and sent a\n       second time -- two form submissions, two workflow enrolments, two\n       emails, and both screens saying \"On its way\". Keying on the address in a\n       module-level map fixes it properly: the map is never cloned, so no\n       navigation can rewind it, and a visitor who genuinely changes their\n       address still gets their results. *\/\n    var key = String(state.email || \"\").trim().toLowerCase();\n    if (!key || SENT[key]) return;\n    SENT[key] = true;\n    track(\"email_submit\", { source: \"pre_results\", channel: emailProvider(), program: matched && matched.id });\n    submitEmail(state.email, state.name || \"\", matched, function (ok) {\n      if (ok) return;\n      delete SENT[key];   \/\/ a real failure should be retryable\n      track(\"email_submit\", { source: \"pre_results\", ok: false, channel: emailProvider() });\n      \/\/ Be straight about it rather than showing a false confirmation.\n      box.className = \"fyp-sent fyp-sent--err\";\n      box.setAttribute(\"role\", \"alert\");\n      title.textContent = S.sentErrTitle;\n      hint.textContent = S.sentErrHint;\n      \/* Put the retry form directly under the failure message. It used to be\n         appended to the view after showResults had already added the nav, so\n         it landed below the Back button at the very bottom of the page. *\/\n      renderNewsletter(box.parentNode === view ? view : view, matched);\n      var retry = view.querySelector(\".fyp-news\");\n      if (retry && box.nextSibling) view.insertBefore(retry, box.nextSibling);\n      try { box.setAttribute(\"tabindex\", \"-1\"); box.focus({ preventScroll: false }); } catch (e) { \/* non-fatal *\/ }\n    });\n  }\n\n  function showAll() {\n    var view = shell();\n    SCREEN.rerender = function () { showAll(); };\n    view.appendChild(el(\"h3\", { \"class\": \"fyp-step-title\", text: S.allTitle }));\n    var groups = {};\n    PROGRAMS.filter(function (p) { return p.active && p.id !== \"contact-bcic\"; })\n      .sort(function (a, b) { return a.priority - b.priority; })\n      .forEach(function (p) { (groups[p.category] = groups[p.category] || []).push(p); });\n    Object.keys(groups).forEach(function (cat) {\n      view.appendChild(el(\"h4\", { \"class\": \"fyp-group-title\", text: cat }));\n      var ul = el(\"ul\", { \"class\": \"fyp-results\" });\n      groups[cat].forEach(function (p) { ul.appendChild(programCard(p, false)); });\n      view.appendChild(ul);\n    });\n    renderNewsletter(view, null);\n    view.appendChild(el(\"div\", { \"class\": \"fyp-nav\" }, [\n      backButton(),\n      el(\"button\", { \"class\": \"fyp-btn fyp-btn--ghost\", type: \"button\", text: S.startOver, onclick: function () { track(\"restart\", {}); renderHome(true); } })\n    ]));\n    track(\"results\", { source: \"show_all\", count: -1 });\n    announce(S.allTitle);\n    focusTitle(view);\n  }\n\n  \/* ----- Optional newsletter (Mailchimp, preferred; HubSpot as fallback) -----\n     matchedProgram: the program to tag\/link as \"of interest\" (top result, or\n     the contact card on no-match). Pass null on the \"show everything\" view,\n     where there's no single program to attribute the signup to.\n\n     Mailchimp's classic subscribe\/post endpoint actively rejects fetch()\/XHR\n     submissions (returns HTTP 503) \u2014 it only accepts a genuine <form>\n     navigation. So this submits via a hidden, temporary <iframe> + <form>\n     pair targeting it, exactly like Mailchimp's own official embed snippet.\n     We can't read the iframe's (cross-origin) response, so this is\n     optimistic: any submission that doesn't throw synchronously is treated\n     as success after a short delay, same fire-and-forget spirit as the old\n     fetch attempt, just using a transport Mailchimp actually accepts. *\/\n  function submitMailchimp(email, name, matchedProgram, done) {\n    var mc = CFG.mailchimp || {};\n    try {\n      var action = mc.formAction + (mc.formAction.indexOf(\"?\") > -1 ? \"&\" : \"?\") +\n        \"u=\" + encodeURIComponent(mc.u) + \"&id=\" + encodeURIComponent(mc.id);\n      var frameName = \"fyp-mc-frame-\" + Math.random().toString(36).slice(2);\n      var iframe = el(\"iframe\", { name: frameName, \"aria-hidden\": \"true\", tabindex: \"-1\" });\n      iframe.style.cssText = \"position:absolute; width:1px; height:1px; opacity:0; pointer-events:none; left:-9999px;\";\n      document.body.appendChild(iframe);\n\n      var form = document.createElement(\"form\");\n      form.action = action;\n      form.method = \"POST\";\n      form.target = frameName;\n      form.style.display = \"none\";\n\n      function addField(fieldName, value) {\n        var inp = document.createElement(\"input\");\n        inp.type = \"hidden\"; inp.name = fieldName; inp.value = value;\n        form.appendChild(inp);\n      }\n      addField(\"EMAIL\", email);\n      addField(\"FNAME\", name);\n      addField(\"b_\" + mc.u + \"_\" + mc.id, \"\"); \/\/ honeypot field Mailchimp expects; must stay empty\n      addField(\"subscribe\", \"Subscribe\");       \/\/ Mailchimp's own submit-button name\/value pair\n      if (mc.programMergeField && matchedProgram) addField(mc.programMergeField, matchedProgram.name);\n      if (mc.programIdMergeField && matchedProgram) addField(mc.programIdMergeField, matchedProgram.id);\n      if (mc.languageMergeField) addField(mc.languageMergeField, LANG);\n\n      document.body.appendChild(form);\n      form.submit();\n\n      setTimeout(function () {\n        form.remove();\n        iframe.remove();\n        done(true);\n      }, 1200);\n    } catch (e) { done(false); }\n  }\n\n  \/* Which service the captured address actually goes to.\n\n     Both submit paths -- the pre-results auto-send and the results-screen box\n     -- resolve through here, so they can never disagree about the\n     destination. Before v8 the auto-send path called submitMailchimp directly\n     and ignored the config entirely, which meant that clearing the Mailchimp\n     block to move to HubSpot silently broke the main path while the\n     results-screen box kept working. One resolver, one answer.\n\n     A configured-but-unavailable choice falls through to the other service\n     rather than dropping the address on the floor: losing a lead to a config\n     typo is worse than sending it to the wrong list, since the two are\n     synced anyway. Returns \"\" only when neither is configured. *\/\n  function emailProvider() {\n    var mc = CFG.mailchimp || {};\n    var hs = CFG.hubspot || {};\n    var mcReady = !!(mc.formAction && mc.u && mc.id);\n    var hsReady = !!(hs.portalId && hs.formGuid);\n    var want = String(CFG.emailProvider || \"auto\").toLowerCase();\n    if (want === \"mailchimp\") return mcReady ? \"mailchimp\" : (hsReady ? \"hubspot\" : \"\");\n    return hsReady ? \"hubspot\" : (mcReady ? \"mailchimp\" : \"\");\n  }\n\n  \/\/ Send the captured address wherever emailProvider() points. done(ok).\n  function submitEmail(email, name, matchedProgram, done) {\n    var p = emailProvider();\n    if (p === \"mailchimp\") return submitMailchimp(email, name, matchedProgram, done);\n    if (p === \"hubspot\") return submitHubspot(email, name, matchedProgram, done);\n    done(false);\n  }\n\n  \/* HubSpot's forms API accepts a plain JSON fetch -- no auth, no cookies, no\n     hidden-iframe theatre, unlike Mailchimp. It also answers honestly, so\n     unlike the Mailchimp path this reports real success rather than assuming\n     it after a timeout. *\/\n  function submitHubspot(email, name, matchedProgram, done) {\n    var hs = CFG.hubspot || {};\n    try {\n      fetch(\"https:\/\/api.hsforms.com\/submissions\/v3\/integration\/submit\/\" + hs.portalId + \"\/\" + hs.formGuid, {\n        method: \"POST\",\n        headers: { \"Content-Type\": \"application\/json\" },\n        body: hubspotPayload(email, name, matchedProgram)\n      }).then(function (res) { done(!!res.ok); })\n        .catch(function () { done(false); });\n    } catch (e) { done(false); }\n  }\n\n  \/* The body for a HubSpot form submission. Carries the same three values the\n     Mailchimp path does, plus hutk: without the hubspotutk cookie HubSpot files\n     the contact as source OFFLINE and the real traffic source is lost. Missing\n     cookie is fine -- the field is simply omitted. *\/\n  \/* ---------------- Data integrity ----------------\n\n     Staff edit the programs sheet directly, and until v9 every mistake failed\n     silently: a comma instead of a pipe in `needs`, a blank `needs` cell, a\n     url without https:\/\/, a geo value outside the vocabulary, a program added\n     to the English tab but not the Spanish one. In every case the program\n     still looked present -- it rendered on the \"everything\" screen and in\n     search -- while being quietly unreachable from the quiz, which is the path\n     almost everyone takes. Nobody could see it, so nobody fixed it.\n\n     This does not repair anything. It names what is wrong, in one tracked\n     event, so the failure has a surface. The sheet itself carries the same\n     checks in a column staff can see while they type; this is the backstop for\n     the ones a spreadsheet formula cannot express -- notably \"reachable by\n     some combination of quiz answers\" and EN\/ES parity. *\/\n  var GEO_VOCAB = { any: 1, brownsville: 1, cameron: 1 };\n  var AUDIENCE_VOCAB = { any: 1, business: 1, nonprofit: 1 };\n\n  \/* Returns { issues, notes }.\n\n     issues  = something is actually broken for a visitor right now.\n     notes   = dead or suspicious config that is not currently hurting anyone.\n\n     The split matters. A validator that reports everything at one severity\n     gets ignored within a week, and then it is worse than nothing because it\n     looks like coverage. Only `issues` raises the tracked warning. *\/\n  function validatePrograms(en, es) {\n    var issues = [];\n    var notes = [];\n    var seen = {};\n\n    \/\/ Every `needs`\/`stages` token the quiz can actually produce.\n    var reachableNeeds = {}, reachableStages = {};\n    var qs = QUESTION_SETS.en || {};\n    for (var qk in qs) {\n      ((qs[qk] || {}).options || []).forEach(function (o) {\n        if (o.set && o.set.need) reachableNeeds[o.set.need] = 1;\n        if (o.set && o.set.stage) reachableStages[o.set.stage] = 1;\n      });\n    }\n    \/\/ Chips jump straight into the quiz with a preset, so they are real paths too.\n    (CHIP_SETS.en || []).forEach(function (c) {\n      var pset = c.preset || {};\n      if (pset.need) reachableNeeds[pset.need] = 1;\n      if (pset.stage) reachableStages[pset.stage] = 1;\n    });\n\n    en.forEach(function (pr) {\n      if (seen[pr.id]) issues.push(pr.id + \": duplicate id\");\n      seen[pr.id] = 1;\n      if (!pr.needs.length) issues.push(pr.id + \": needs is empty, so the quiz can never reach it\");\n      if (!GEO_VOCAB[pr.geo]) issues.push(pr.id + \": geo \\\"\" + pr.geo + \"\\\" is not any\/brownsville\/cameron\");\n      if (!AUDIENCE_VOCAB[pr.audience]) issues.push(pr.id + \": audience \\\"\" + pr.audience + \"\\\" is not any\/business\/nonprofit\");\n      var liveNeeds = 0;\n      pr.needs.forEach(function (n) {\n        if (n.indexOf(\",\") > -1) { issues.push(pr.id + \": needs looks comma-separated, use |\"); return; }\n        if (n === \"other\" || reachableNeeds[n]) { liveNeeds++; return; }\n        notes.push(pr.id + \": need \\\"\" + n + \"\\\" is a dead token -- no quiz answer or chip sets it\");\n      });\n      \/\/ The one that actually matters: not a single quiz path can reach it.\n      if (pr.needs.length && !liveNeeds) {\n        issues.push(pr.id + \": no quiz path can reach it -- none of its needs (\" + pr.needs.join(\"|\") + \") match any answer\");\n      }\n      pr.stages.forEach(function (st) {\n        if (st.indexOf(\",\") > -1) issues.push(pr.id + \": stages looks comma-separated, use |\");\n        else if (st !== \"any\" && !reachableStages[st]) notes.push(pr.id + \": stage \\\"\" + st + \"\\\" is a dead token\");\n      });\n    });\n\n    \/\/ EN\/ES parity. Ids and routing columns must match exactly; only the\n    \/\/ human-readable columns are allowed to differ between the two tabs.\n    var esById = {};\n    es.forEach(function (pr) { esById[pr.id] = pr; });\n    en.forEach(function (pr) {\n      var t = esById[pr.id];\n      if (!t) { issues.push(pr.id + \": missing from the Spanish sheet\"); return; }\n      [\"active\", \"geo\", \"audience\"].forEach(function (k) {\n        if (String(pr[k]) !== String(t[k])) issues.push(pr.id + \": \" + k + \" differs EN\/ES (\" + pr[k] + \" vs \" + t[k] + \")\");\n      });\n      if (pr.needs.join(\"|\") !== t.needs.join(\"|\")) issues.push(pr.id + \": needs differs EN\/ES -- the Spanish quiz will not match it\");\n      if (pr.stages.join(\"|\") !== t.stages.join(\"|\")) issues.push(pr.id + \": stages differs EN\/ES\");\n    });\n    es.forEach(function (pr) {\n      if (!en.some(function (x) { return x.id === pr.id; })) issues.push(pr.id + \": in the Spanish sheet but not the English one\");\n    });\n\n    \/\/ The hardcoded ids the widget depends on. Renaming any of these in the\n    \/\/ sheet removes a whole behaviour with no other symptom.\n    [\"contact-bcic\"].forEach(function (id) {\n      if (!en.some(function (x) { return x.id === id; })) issues.push(id + \": required id is missing -- the no-match contact card falls back to hardcoded copy\");\n    });\n\n    return { issues: issues, notes: notes };\n  }\n\n  function hubspotPayload(email, name, matchedProgram) {\n    var fields = [{ objectTypeId: \"0-1\", name: \"email\", value: email }];\n    if (name) fields.push({ objectTypeId: \"0-1\", name: \"firstname\", value: name });\n    if (matchedProgram && matchedProgram.name) {\n      \/\/ Display name. Translated, and staff-editable in the sheet -- fine for\n      \/\/ showing a human a program name in an email, useless for routing.\n      fields.push({ objectTypeId: \"0-1\", name: \"program_interest\", value: matchedProgram.name });\n    }\n    \/* The stable slug. THIS is what automation must branch on.\n\n       program_interest carries whatever the sheet's `name` cell says today, in\n       whatever language the visitor used -- two programs are genuinely\n       translated, so a Spanish lead arrives as \"Habla con el equipo de BCIC\".\n       A workflow filtering on a name string silently enrols nobody the moment\n       someone renames a program or a Spanish visitor shows up, and looks\n       perfectly healthy while doing it. Ids are never edited and never\n       translated. *\/\n    if (matchedProgram && matchedProgram.id) {\n      fields.push({ objectTypeId: \"0-1\", name: \"program_id\", value: matchedProgram.id });\n    }\n    \/* Which language they actually used. hs_language is HubSpot's own\n       \"Preferred language\" property, so email language variations and any\n       future ES workflow branch key off it natively rather than off a custom\n       field. Without this every Spanish visitor gets an English email. *\/\n    fields.push({ objectTypeId: \"0-1\", name: \"hs_language\", value: LANG === \"es\" ? \"es\" : \"en\" });\n    var ctx = { pageUri: location.href, pageName: document.title };\n    var m = \/(?:^|;\\s*)hubspotutk=([^;]+)\/.exec(document.cookie || \"\");\n    if (m) ctx.hutk = m[1];\n    return JSON.stringify({ fields: fields, context: ctx });\n  }\n\n  function renderNewsletter(view, matchedProgram) {\n    var provider = emailProvider();\n    if (!provider) return;\n    var useMailchimp = provider === \"mailchimp\";\n\n    var box = el(\"div\", { \"class\": \"fyp-news\" });\n    var labelRow = el(\"div\", { \"class\": \"fyp-news-label-row\" });\n    var lholder = document.createElement(\"span\");\n    lholder.innerHTML = ICONS.mail;\n    if (lholder.firstChild) labelRow.appendChild(lholder.firstChild);\n    labelRow.appendChild(el(\"label\", { \"for\": \"fyp-news-email\", text: S.newsLabel }));\n    box.appendChild(labelRow);\n    box.appendChild(el(\"p\", { \"class\": \"fyp-news-hint\", text: S.newsHint }));\n    var msg = el(\"p\", { \"class\": \"fyp-news-msg\", \"aria-live\": \"polite\" });\n    \/\/ Ask for a first name whenever we do not already have one -- a visitor\n    \/\/ who skipped straight to a program (search pick, direct link) never\n    \/\/ answered the quiz's name question, so state.name is empty here.\n    \/\/ This box is the only remaining chance to collect it before the\n    \/\/ record reaches HubSpot or Mailchimp -- previously the field only\n    \/\/ showed on the Mailchimp path, so a HubSpot signup from this screen\n    \/\/ could go out with no first name at all. It now always shows, on\n    \/\/ both paths.\n    var nameInput = el(\"input\", { type: \"text\", id: \"fyp-news-name\", autocomplete: \"given-name\", placeholder: S.newsNamePlaceholder });\n    \/\/ Already asked for it at the start of the quiz -- don't make them type it\n    \/\/ twice. Still editable, and still validated below, so a skipped name\n    \/\/ behaves exactly as it does today.\n    if (nameInput && state.name) nameInput.value = state.name;\n    var input = el(\"input\", { type: \"email\", id: \"fyp-news-email\", autocomplete: \"email\", placeholder: \"you@example.com\" });\n\n    function showSuccess() {\n      msg.className = \"fyp-news-msg fyp-ok\";\n      msg.innerHTML = \"\";\n      var checkHolder = document.createElement(\"span\");\n      checkHolder.className = \"fyp-check-pop\";\n      checkHolder.setAttribute(\"aria-hidden\", \"true\");\n      checkHolder.innerHTML = ICONS.check;\n      msg.appendChild(checkHolder);\n      msg.appendChild(document.createTextNode(S.newsOk));\n      var link = matchedProgram && CFG.resourceLinks ? CFG.resourceLinks[matchedProgram.id] : null;\n      if (link) {\n        msg.appendChild(document.createElement(\"br\"));\n        msg.appendChild(document.createTextNode(S.newsResourceIntro + \" \"));\n        var a = el(\"a\", {\n          href: link, target: \"_blank\", rel: \"noopener\",\n          text: S.newsResourceLinkText.replace(\"{program}\", matchedProgram.name),\n          onclick: function () { track(\"resource_link_click\", { program: matchedProgram.id }); }\n        });\n        msg.appendChild(a);\n      }\n      input.value = \"\";\n      if (nameInput) nameInput.value = \"\";\n    }\n\n    var btn = icoBtn(\"button\", { type: \"button\" }, \"mail\", S.newsBtn);\n    btn.addEventListener(\"click\", function () {\n        var email = input.value.trim();\n        if (!\/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$\/.test(email)) { msg.className = \"fyp-news-msg fyp-err\"; msg.textContent = S.newsInvalid; return; }\n        \/\/ A first name is required on both paths now -- nameInput always exists,\n        \/\/ see the note above its declaration.\n        var name = nameInput.value.trim();\n        if (!name) { msg.className = \"fyp-news-msg fyp-err\"; msg.textContent = S.newsNameInvalid; return; }\n        btn.disabled = true;\n        submitEmail(email, name, matchedProgram, function (ok) {\n          btn.disabled = false;\n          if (ok) {\n            showSuccess();\n            track(\"email_submit\", { ok: true, channel: provider, program: matchedProgram ? matchedProgram.id : null });\n          } else {\n            msg.className = \"fyp-news-msg fyp-err\";\n            msg.textContent = S.newsErr;\n            track(\"email_submit\", { ok: false, channel: provider });\n          }\n        });\n    });\n    var rowChildren = nameInput ? [nameInput, input, btn] : [input, btn];\n    var row = el(\"div\", { \"class\": \"fyp-news-row\" }, rowChildren);\n    box.appendChild(row); box.appendChild(msg);\n    \/\/ Same disclaimer as the pre-results ask. Which path the visitor took\n    \/\/ shouldn't change whether they were told what they're agreeing to.\n    box.appendChild(consentLine());\n    view.appendChild(box);\n  }\n\n  \/* ---------------- Boot ---------------- *\/\n  \/\/ English and Spanish load from their own sheets in parallel \u2014 neither\n  \/\/ waits on the other, and each has its own independent embedded fallback\n  \/\/ (see loadProgramsFrom above), so a problem with one sheet\/language never\n  \/\/ blocks or breaks the other. Rendering waits for both to settle so the\n  \/\/ language switcher always has real data on both sides from the start.\n  (function boot() {\n    var sourceEn = null, sourceEs = null, settledCount = 0;\n    function maybeFinish() {\n      if (++settledCount < 2) return;\n      applyLang(resolveLang());\n      root.setAttribute(\"data-fyp-source\", sourceEn);\n      root.setAttribute(\"data-fyp-source-es\", sourceEs);\n      renderHome(false);\n      root.setAttribute(\"data-fyp-lang\", LANG);\n      root.setAttribute(\"data-fyp-lang-mode\", String((CFG.language || {}).mode || \"page\"));\n      track(\"load\", {\n        source: sourceEn, source_es: sourceEs,\n        programs: PROGRAMS_EN.length, programs_es: PROGRAMS_ES.length,\n        lang: LANG,\n        lang_mode: String((CFG.language || {}).mode || \"page\"),\n        page_lang: detectPageLang() || \"unknown\"\n      });\n      \/* A widget rendering in a different language from its page is the exact\n         failure this mode exists to prevent, so say so rather than letting it\n         look intentional. *\/\n      var pl = detectPageLang();\n      if (pl && pl !== LANG) {\n        track(\"lang_mismatch\", { widget: LANG, page: pl });\n        if (window.console && console.warn) {\n          console.warn(\"BCIC FYP: rendering in \" + LANG + \" on a \" + pl + \" page. Check CFG.language.mode.\");\n        }\n      }\n      \/* Report bad data loudly enough to be findable. Fires only when\n         something is actually wrong, so a non-empty fyp_data_warning in GA4\n         always means a real problem in the sheet. *\/\n      try {\n        var v = validatePrograms(PROGRAMS_EN, PROGRAMS_ES);\n        if (v.issues.length) {\n          track(\"data_warning\", { count: v.issues.length, issues: v.issues.join(\" ;; \").slice(0, 900) });\n          root.setAttribute(\"data-fyp-issues\", String(v.issues.length));\n        }\n        if (window.console && console.warn && (v.issues.length || v.notes.length)) {\n          console.warn(\"BCIC FYP programs sheet: \" + v.issues.length + \" problem(s), \" + v.notes.length + \" note(s)\"\n            + (v.issues.length ? \"\\n  PROBLEM: \" + v.issues.join(\"\\n  PROBLEM: \") : \"\")\n            + (v.notes.length ? \"\\n  note: \" + v.notes.join(\"\\n  note: \") : \"\"));\n        }\n      } catch (e) {\n        \/\/ Never break the widget over a validation bug -- but never hide one\n        \/\/ either. A silent validator is worse than no validator.\n        if (window.console && console.warn) console.warn(\"BCIC FYP: data validation failed to run:\", e && e.message);\n        track(\"data_warning\", { count: -1, issues: \"validator error: \" + ((e && e.message) || \"unknown\") });\n      }\n    }\n    loadPrograms(function (list, source) { PROGRAMS_EN = list; sourceEn = source; maybeFinish(); });\n    loadProgramsEs(function (list, source) { PROGRAMS_ES = list; sourceEs = source; maybeFinish(); });\n  })();\n})();\n<\/script>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-857cfb9 e-flex e-con-boxed e-con e-parent\" data-id=\"857cfb9\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-052baed elementor-widget elementor-widget-text-editor\" data-id=\"052baed\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>BCIC\u2019s programs cover funding, building improvements, classes and mentorship, workspace, and community projects run by non-profits. Amounts, deadlines, and who qualifies are set per program and change over time, so check that program\u2019s own page for the current details rather than relying on a summary.<\/p><p>Answer the questions above to get matched with a program. If nothing fits, call <a href=\"tel:+19567470100\">(956) 747-0100<\/a> weekdays from 8 AM to 5 PM CT, or <a href=\"https:\/\/brownsvillecic.com\/contact-bcic\/\">contact the BCIC team<\/a> \u2014 we\u2019d rather point you somewhere useful than have you guess.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>Discover the Perfect Program for Your Business BCIC works to enhance quality of life in Brownsville through innovation and equitable economic development. In practice, that\u2019s programs for funding, building improvements, classes, mentorship, and workspace. Who qualifies is set per program \u2014 some are open to anyone, some only to businesses in Brownsville or Cameron County, [&hellip;]<\/p>\n","protected":false},"author":4,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"content-type":"","footnotes":""},"class_list":["post-1199","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/brownsvillecic.com\/es\/wp-json\/wp\/v2\/pages\/1199","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/brownsvillecic.com\/es\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/brownsvillecic.com\/es\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/brownsvillecic.com\/es\/wp-json\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/brownsvillecic.com\/es\/wp-json\/wp\/v2\/comments?post=1199"}],"version-history":[{"count":72,"href":"https:\/\/brownsvillecic.com\/es\/wp-json\/wp\/v2\/pages\/1199\/revisions"}],"predecessor-version":[{"id":4723,"href":"https:\/\/brownsvillecic.com\/es\/wp-json\/wp\/v2\/pages\/1199\/revisions\/4723"}],"wp:attachment":[{"href":"https:\/\/brownsvillecic.com\/es\/wp-json\/wp\/v2\/media?parent=1199"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}