Consent Mode Architecture
Default-denied, explicitly-granted, re-evaluated on every page load and every consent change.
- Default state: all four consent types denied (ad_storage, analytics_storage, ad_user_data, ad_personalization), set via a script in BaseLayout.astro that runs before the GTM snippet loads.
- Persistence: localStorage key da-privacy-choice — value ‘granted’ or the key is entirely absent (never ‘denied’ as a stored value — binary state, absent means denied).
- Cross-page persistence fix: because this site uses native HTML form POST (full server-side redirects, not client-side routing), a restore script runs on every single page load, checking localStorage and immediately calling gtag(‘consent’,‘update’, ...) if previously granted — necessary because consent state doesn’t naturally persist across full page reloads without it.
- GTM Consent Overview: enabled at the container level (Admin → Container Settings → Additional Settings).
Google tags (GA4, Google Ads)
- Rely on Built-in Consent Checks only — "Advanced Consent Mode" behavior. Fire regardless of consent state, sending cookieless pings when denied.
Non-Google tags (Clarity, LinkedIn, Meta)
- Explicitly configured with Additional Consent Checks → "Require additional consent for tag to fire."
- Clarity requires analytics_storage specifically (not ad_storage — Clarity is session-analytics, not an ad platform).
- LinkedIn and Meta require ad_storage + ad_user_data — full blocking until consent is granted.
- Confirmed live in the current GTM export: every base/page-load tag in this category (Clarity, LinkedIn Insight Tag, Meta Pixel Base) pairs its Additional Consent Check with a Consent Update retry-trigger. Every event-driven tag in this category (LinkedIn Conversion, Meta Lead/Contact tags) correctly carries the consent requirement WITHOUT the retry-trigger — see Debugging Journal #2, #3, and #13 for why that distinction matters and what breaks if it’s not respected.