diff --git a/.env.example b/.env.example index 51ad3c6..52b7b6c 100644 --- a/.env.example +++ b/.env.example @@ -1,7 +1,9 @@ PUBLIC_SITE_URL=https://www.bluecapstrategies.com PUBLIC_CONTACT_FORM_ENDPOINT= -# Analytics — side-by-side trial (ADR-0005). Leave blank to disable each. +# Analytics — leave blank to disable each. Production values come from CI (Dockerfile ARGs). +# Google Analytics 4: +PUBLIC_GA_MEASUREMENT_ID= # PostHog (Cloud free tier recommended for the trial): PUBLIC_POSTHOG_KEY= PUBLIC_POSTHOG_HOST=https://us.i.posthog.com diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 45a839e..63ebee0 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -98,9 +98,13 @@ jobs: # deploy time. CONTACT_FORM_ENDPOINT is the Formspree URL; set it as a repo Actions # secret. If it is empty the build still succeeds and the contact form degrades to a # mailto draft, so a missing secret is survivable but should be caught in review. + # GA_MEASUREMENT_ID / UMAMI_WEBSITE_ID are non-secret repo Actions *variables* + # (Settings → Actions → Variables); empty means that tracker is simply left out. docker build \ --build-arg PUBLIC_CONTACT_FORM_ENDPOINT="${{ secrets.CONTACT_FORM_ENDPOINT }}" \ --build-arg PUBLIC_SITE_URL="https://www.bluecapstrategies.com" \ + --build-arg PUBLIC_GA_MEASUREMENT_ID="${{ vars.GA_MEASUREMENT_ID }}" \ + --build-arg PUBLIC_UMAMI_WEBSITE_ID="${{ vars.UMAMI_WEBSITE_ID }}" \ -t "$PUSH_IMAGE:${{ steps.tag.outputs.sha }}" -t "$PUSH_IMAGE:latest" . # Registry pushes can time out on a slow blob; docker push is resumable, so retry. push_retry() { diff --git a/Dockerfile b/Dockerfile index 5f20126..b0b7feb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,8 +12,14 @@ WORKDIR /app # .gitea/workflows/ci.yml. ARG PUBLIC_CONTACT_FORM_ENDPOINT="" ARG PUBLIC_SITE_URL="https://www.bluecapstrategies.com" +ARG PUBLIC_GA_MEASUREMENT_ID="" +ARG PUBLIC_UMAMI_SRC="https://stats.bluecapstrategies.com/script.js" +ARG PUBLIC_UMAMI_WEBSITE_ID="" ENV PUBLIC_CONTACT_FORM_ENDPOINT=$PUBLIC_CONTACT_FORM_ENDPOINT \ - PUBLIC_SITE_URL=$PUBLIC_SITE_URL + PUBLIC_SITE_URL=$PUBLIC_SITE_URL \ + PUBLIC_GA_MEASUREMENT_ID=$PUBLIC_GA_MEASUREMENT_ID \ + PUBLIC_UMAMI_SRC=$PUBLIC_UMAMI_SRC \ + PUBLIC_UMAMI_WEBSITE_ID=$PUBLIC_UMAMI_WEBSITE_ID COPY --from=deps /app/node_modules ./node_modules COPY . . RUN npm run build diff --git a/src/components/Analytics.astro b/src/components/Analytics.astro index 0273582..c006079 100644 --- a/src/components/Analytics.astro +++ b/src/components/Analytics.astro @@ -1,15 +1,19 @@ --- /** - * Side-by-side visitor analytics (ADR-0005): PostHog + Umami, each gated by env vars so this is a - * no-op until configured. Set the PUBLIC_* vars at build time to enable either or both. + * Side-by-side visitor analytics, each gated by env vars so this is a no-op until configured. + * Set the PUBLIC_* vars at build time (Dockerfile ARGs, fed by CI) to enable any of them. + * - Umami: PUBLIC_UMAMI_SRC (script URL), PUBLIC_UMAMI_WEBSITE_ID — self-hosted, cookieless + * - GA4: PUBLIC_GA_MEASUREMENT_ID (G-XXXXXXXXXX) * - PostHog: PUBLIC_POSTHOG_KEY, PUBLIC_POSTHOG_HOST (default https://us.i.posthog.com) - * - Umami: PUBLIC_UMAMI_SRC (script URL), PUBLIC_UMAMI_WEBSITE_ID - * Track the same event set in both to compare (see docs/seo-aeo-optimization.md §6b). + * Umami and GA4 only fire on the production host, so staging/LAN/local visits are not counted. + * Anything enabled here must be disclosed on /privacy/. */ const posthogKey = import.meta.env.PUBLIC_POSTHOG_KEY; const posthogHost = import.meta.env.PUBLIC_POSTHOG_HOST ?? "https://us.i.posthog.com"; const umamiSrc = import.meta.env.PUBLIC_UMAMI_SRC; const umamiWebsiteId = import.meta.env.PUBLIC_UMAMI_WEBSITE_ID; +const gaId = import.meta.env.PUBLIC_GA_MEASUREMENT_ID; +const prodHost = new URL(import.meta.env.PUBLIC_SITE_URL ?? "https://www.bluecapstrategies.com").hostname; --- { @@ -23,6 +27,23 @@ const umamiWebsiteId = import.meta.env.PUBLIC_UMAMI_WEBSITE_ID; { umamiSrc && umamiWebsiteId && ( - ) } diff --git a/src/components/Footer.astro b/src/components/Footer.astro index 89c135d..dae57ef 100644 --- a/src/components/Footer.astro +++ b/src/components/Footer.astro @@ -28,6 +28,6 @@ import { nav, serviceNav, site } from "@data/site"; diff --git a/src/pages/privacy.astro b/src/pages/privacy.astro new file mode 100644 index 0000000..b271dec --- /dev/null +++ b/src/pages/privacy.astro @@ -0,0 +1,108 @@ +--- +import BaseLayout from "@layouts/BaseLayout.astro"; +import { site } from "@data/site"; +import { pageSchema } from "@utils/schema"; + +const description = + "How BlueCap Strategies collects, uses, and protects information when you visit this website or contact us."; +const effectiveDate = "October 23, 2026"; +--- + + +
+
+

Effective {effectiveDate}

+

Privacy Policy

+

+ This policy explains what information BlueCap Strategies, LLC ("BlueCap Strategies," "we," + "us") collects through www.bluecapstrategies.com, how we use it, and the choices you have. +

+ +

Information you give us

+

+ When you use our contact form, we collect the details you enter: your name, email address, + phone number, organization, and message. The form is delivered to us by Formspree, a + form-processing service that receives the submission and forwards it to our inbox. We use + this information only to respond to your inquiry and to follow up about the work you asked + about. You can also reach us directly by email or phone instead of using the form. +

+ +

Website analytics

+

We use two analytics tools to understand how the site is used and to improve it.

+
    +
  • + Umami, which we host on our own infrastructure. It does not use cookies, + does not track you across other websites, and does not store your IP address. It records + aggregate information such as the pages viewed, the referring website, browser and device + type, and approximate country. +
  • +
  • + Google Analytics, provided by Google LLC. It uses cookies to distinguish + visitors and measure visits, and the data it collects is processed by Google under + Google's Privacy Policy. You can opt out + by installing the + Google Analytics opt-out browser + add-on or by blocking cookies in your browser settings. +
  • +
+ +

Server and security logs

+

+ Like most websites, our servers automatically record technical details of each request, + such as IP address, browser type, the page requested, and the time of the request. We use + these logs only to keep the site secure and running, for example to block abusive traffic, + and keep them for a limited time. +

+ +

What we do not do

+
    +
  • We do not sell or rent your personal information.
  • +
  • We do not use your information for advertising, and we do not show ads on this site.
  • +
  • We do not knowingly collect information from children under 13.
  • +
+ +

Sharing

+

+ We share information only with the service providers named above that help us operate + this site, or when required by law. +

+ +

Your choices

+

+ You may ask us to tell you what information we hold about you from a contact form + submission, or to correct or delete it, by emailing + {site.email}. +

+ +

Changes to this policy

+

+ If we change how we handle information, we will update this page and its effective date. +

+ +

Contact

+

+ Questions about this policy can be sent to {site.email} + or by phone at {site.phone}. +

+
+
+
+ + diff --git a/src/styles/global.css b/src/styles/global.css index 0702fef..00a8b27 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -1149,6 +1149,11 @@ textarea:focus { font-size: 0.92rem; } +.footer-bottom a { + color: inherit; + text-decoration: underline; +} + .insights-shell { padding: 4rem 0; display: grid;