consent-engine 0.3.2__tar.gz → 0.3.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {consent_engine-0.3.2 → consent_engine-0.3.3}/PKG-INFO +1 -1
- {consent_engine-0.3.2 → consent_engine-0.3.3}/pyproject.toml +1 -1
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/__init__.py +1 -1
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/templates/audit_report.html.j2 +15 -8
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/tools/tool_08_report_generator.py +113 -37
- {consent_engine-0.3.2 → consent_engine-0.3.3}/.gitignore +0 -0
- {consent_engine-0.3.2 → consent_engine-0.3.3}/LICENSE +0 -0
- {consent_engine-0.3.2 → consent_engine-0.3.3}/README.md +0 -0
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/api.py +0 -0
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/audit.py +0 -0
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/cli.py +0 -0
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/config.py +0 -0
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/data/vendor_library/open-cookie-database.csv +0 -0
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/data/vendor_library/vendors.json +0 -0
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/data/wiki/CLAUDE.md +0 -0
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/data/wiki/concepts/cipa-vppa.md +0 -0
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/data/wiki/concepts/cmp-failures.md +0 -0
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/data/wiki/concepts/consent-mode-v2.md +0 -0
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/data/wiki/concepts/dark-patterns.md +0 -0
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/data/wiki/concepts/gpc-signal.md +0 -0
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/data/wiki/concepts/ssgtm-risk.md +0 -0
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/data/wiki/enforcement/emerging-trends.md +0 -0
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/data/wiki/enforcement/gdpr-fines.md +0 -0
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/data/wiki/enforcement/lawsuit-surge.md +0 -0
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/data/wiki/enforcement/live-fines-db.md +0 -0
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/data/wiki/enforcement/us-class-actions.md +0 -0
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/data/wiki/enforcement/us-enforcement.md +0 -0
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/data/wiki/index.md +0 -0
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/data/wiki/log.md +0 -0
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/data/wiki/regulations/ccpa.md +0 -0
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/data/wiki/regulations/gdpr.md +0 -0
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/data/wiki/regulations/quebec-law25.md +0 -0
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/data/wiki/regulations/tcf.md +0 -0
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/data/wiki/regulations/us-state-laws.md +0 -0
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/data/wiki/technical/cmp-profiles.md +0 -0
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/data/wiki/technical/consent-mode-impact.md +0 -0
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/data/wiki/technical/google-tag-gateway.md +0 -0
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/data/wiki/technical/scanner-methodology.md +0 -0
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/llm/__init__.py +0 -0
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/llm/client.py +0 -0
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/mcp_server.py +0 -0
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/models/__init__.py +0 -0
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/models/audit_request.py +0 -0
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/models/audit_result.py +0 -0
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/models/scan_result.py +0 -0
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/models/vendor.py +0 -0
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/templates/audit_deck.marp.md.j2 +0 -0
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/tools/__init__.py +0 -0
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/tools/cmp_clicker.py +0 -0
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/tools/cmp_detector.py +0 -0
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/tools/cmp_injector.py +0 -0
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/tools/jurisdiction_detector.py +0 -0
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/tools/tool_01_gtm_parser.py +0 -0
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/tools/tool_02_violation_classifier.py +0 -0
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/tools/tool_03_browser_scanner.py +0 -0
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/tools/tool_04_har_analyzer.py +0 -0
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/tools/tool_05_vendor_library.py +0 -0
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/tools/tool_06_ssgtm_detector.py +0 -0
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/tools/tool_06b_pixel_detector.py +0 -0
- {consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/tools/tool_07_rag_retriever.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: consent-engine
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.3
|
|
4
4
|
Summary: Forensic consent-compliance audit engine. Deterministic by design.
|
|
5
5
|
Project-URL: Homepage, https://github.com/kb223/consent-engine
|
|
6
6
|
Project-URL: Repository, https://github.com/kb223/consent-engine
|
{consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/templates/audit_report.html.j2
RENAMED
|
@@ -692,8 +692,8 @@
|
|
|
692
692
|
<!-- REVENUE RECOVERY ESTIMATE (signal variant only) -->
|
|
693
693
|
<div class="recovery-panel">
|
|
694
694
|
<div class="recovery-head">
|
|
695
|
-
<span class="recovery-tag">Recoverable
|
|
696
|
-
<h3 class="recovery-title">
|
|
695
|
+
<span class="recovery-tag">Recoverable Conversion Value</span>
|
|
696
|
+
<h3 class="recovery-title">Monthly conversion value recoverable by fixing this stack</h3>
|
|
697
697
|
</div>
|
|
698
698
|
<div class="recovery-figure">
|
|
699
699
|
<div class="recovery-range">
|
|
@@ -701,19 +701,24 @@
|
|
|
701
701
|
<span class="recovery-unit">/ month</span>
|
|
702
702
|
</div>
|
|
703
703
|
<div class="recovery-annual">
|
|
704
|
-
≈ ${{ "{:,}".format(recovery.annual_recoverable_usd) }} / year
|
|
704
|
+
≈ ${{ "{:,}".format(recovery.annual_recoverable_usd) }} / year · brand tier: <strong>{{ recovery.brand_tier_label }}</strong>
|
|
705
705
|
</div>
|
|
706
706
|
</div>
|
|
707
707
|
<div class="recovery-grid">
|
|
708
708
|
<div class="recovery-cell">
|
|
709
709
|
<div class="recovery-cell-label">Monthly ad spend</div>
|
|
710
710
|
<div class="recovery-cell-value">${{ "{:,}".format(recovery.monthly_ad_spend_usd) }}</div>
|
|
711
|
-
<div class="recovery-cell-sub">{{ 'self-reported' if recovery.ad_spend_user_provided else '
|
|
711
|
+
<div class="recovery-cell-sub">{{ 'self-reported' if recovery.ad_spend_user_provided else 'auto-estimated from brand tier — override with --monthly-ad-spend' }}</div>
|
|
712
|
+
</div>
|
|
713
|
+
<div class="recovery-cell">
|
|
714
|
+
<div class="recovery-cell-label">Ad-attributable revenue</div>
|
|
715
|
+
<div class="recovery-cell-value">${{ "{:,}".format(recovery.ad_attributable_revenue_usd) }}</div>
|
|
716
|
+
<div class="recovery-cell-sub">monthly, at {{ recovery.roas_multiplier }}× ROAS</div>
|
|
712
717
|
</div>
|
|
713
718
|
<div class="recovery-cell">
|
|
714
719
|
<div class="recovery-cell-label">Signal gap</div>
|
|
715
720
|
<div class="recovery-cell-value">{{ recovery.signal_gap_pct }}%</div>
|
|
716
|
-
<div class="recovery-cell-sub">of
|
|
721
|
+
<div class="recovery-cell-sub">of opt-out traffic leaking</div>
|
|
717
722
|
</div>
|
|
718
723
|
<div class="recovery-cell">
|
|
719
724
|
<div class="recovery-cell-label">Recovery uplift</div>
|
|
@@ -722,9 +727,11 @@
|
|
|
722
727
|
</div>
|
|
723
728
|
</div>
|
|
724
729
|
<p class="recovery-footnote">
|
|
725
|
-
Formula: monthly ad spend × {{ recovery.consent_loss_rate_pct }}%
|
|
726
|
-
|
|
727
|
-
|
|
730
|
+
Formula: monthly ad spend × {{ recovery.roas_multiplier }}× ROAS × {{ recovery.consent_loss_rate_pct }}% US opt-out market × {{ recovery.signal_gap_pct }}% signal gap × {{ recovery.recovery_uplift_pct }}% recapture =
|
|
731
|
+
<strong>${{ "{:,}".format(recovery.monthly_recoverable_usd) }}/month recovered conversion value</strong>.
|
|
732
|
+
Brand tier ({{ recovery.brand_tier_label }}) is auto-derived from scan signals — vendor breadth, sGTM presence, CMP sophistication — so the math defaults to the
|
|
733
|
+
scale of the audited site rather than a generic mid-market anchor. Re-run with <code>--monthly-ad-spend</code> for a sharper estimate.
|
|
734
|
+
Recovery is realized by routing post-consent signal through server-side GTM → Meta CAPI, Google Enhanced Conversions, and TikTok Events API.
|
|
728
735
|
</p>
|
|
729
736
|
</div>
|
|
730
737
|
{% endif %}
|
{consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/tools/tool_08_report_generator.py
RENAMED
|
@@ -22,14 +22,63 @@ from consent_engine.tools.tool_07_rag_retriever import WikiPage
|
|
|
22
22
|
|
|
23
23
|
ReportVariant = Literal["signal", "compliance"]
|
|
24
24
|
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
_CA_OPTOUT_RATE: float = 0.25
|
|
25
|
+
# Share of US population in CCPA-like opt-out-protected states (CA, CO, CT, VA,
|
|
26
|
+
# UT, TX, OR, MT, IA, DE, NJ, NH, TN, NE, MN, MD, RI plus pending — ~35% of US
|
|
27
|
+
# population is in a state with a binding opt-out right as of 2026).
|
|
28
|
+
_US_OPTOUT_MARKET_RATE: float = 0.35
|
|
30
29
|
# Share of consent-denied traffic that can be recovered via proper sGTM + CAPI + ACM.
|
|
31
30
|
_RECOVERY_UPLIFT: float = 0.50
|
|
32
31
|
|
|
32
|
+
# Brand-tier auto-estimation: scan signals → (label, default monthly ad spend,
|
|
33
|
+
# typical ROAS multiplier). Used when the buyer hasn't self-reported
|
|
34
|
+
# --monthly-ad-spend, so the dollar math defaults to numbers that match the
|
|
35
|
+
# actual scale of the company being audited (not a generic mid-market anchor).
|
|
36
|
+
_BRAND_TIERS: list[tuple[str, int, float]] = [
|
|
37
|
+
# (tier_label, monthly_ad_spend_usd, roas_multiplier)
|
|
38
|
+
("Global Enterprise", 10_000_000, 7.0), # multi-domain, sGTM, 15+ vendors
|
|
39
|
+
("National Enterprise", 2_000_000, 6.0), # single-domain, sophisticated CMP, 8+ vendors
|
|
40
|
+
("Mid-Large / Multi-Channel", 500_000, 5.5), # OneTrust/Truyo/Cookiebot, 5-7 vendors
|
|
41
|
+
("Mid-Market", 100_000, 5.0), # established CMP, 3-5 vendors
|
|
42
|
+
("SMB", 20_000, 4.0), # basic stack, <3 vendors
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _estimate_brand_tier(audit_result: AuditResult) -> tuple[str, int, float]:
|
|
47
|
+
"""Return ``(tier_label, monthly_ad_spend_usd, roas_multiplier)`` from the scan.
|
|
48
|
+
|
|
49
|
+
Used when the buyer doesn't self-report ``--monthly-ad-spend``. Scores brand
|
|
50
|
+
sophistication from observable signals (vendor count, sGTM presence,
|
|
51
|
+
enterprise CMP) and maps to the matching tier in ``_BRAND_TIERS``. Bigger
|
|
52
|
+
brands get bigger defaults so the recoverable-revenue math doesn't default
|
|
53
|
+
to peanut numbers when the audit is run against a national enterprise site.
|
|
54
|
+
"""
|
|
55
|
+
vendor_count = len(audit_result.findings)
|
|
56
|
+
pixel_count = len(audit_result.pixel_firings)
|
|
57
|
+
has_ssgtm = audit_result.ssgtm_detected
|
|
58
|
+
# Enterprise-grade CMPs — running these signals significant procurement +
|
|
59
|
+
# legal involvement, which correlates with enterprise ad budgets.
|
|
60
|
+
enterprise_cmp = {
|
|
61
|
+
"OneTrust", "Truyo", "TrustArc", "Usercentrics", "Sourcepoint",
|
|
62
|
+
"Didomi", "Cookiebot", "Ketch",
|
|
63
|
+
}
|
|
64
|
+
has_enterprise_cmp = audit_result.detected_cmp in enterprise_cmp
|
|
65
|
+
|
|
66
|
+
score = 0
|
|
67
|
+
score += min(vendor_count, 10) # up to 10 from vendor breadth
|
|
68
|
+
score += min(pixel_count, 6) # up to 6 from pixel endpoint diversity
|
|
69
|
+
score += 6 if has_ssgtm else 0 # sGTM = enterprise infrastructure
|
|
70
|
+
score += 3 if has_enterprise_cmp else 0
|
|
71
|
+
|
|
72
|
+
if score >= 18:
|
|
73
|
+
return _BRAND_TIERS[0] # Global Enterprise
|
|
74
|
+
if score >= 13:
|
|
75
|
+
return _BRAND_TIERS[1] # National Enterprise
|
|
76
|
+
if score >= 8:
|
|
77
|
+
return _BRAND_TIERS[2] # Mid-Large
|
|
78
|
+
if score >= 4:
|
|
79
|
+
return _BRAND_TIERS[3] # Mid-Market
|
|
80
|
+
return _BRAND_TIERS[4] # SMB
|
|
81
|
+
|
|
33
82
|
_TEMPLATES_DIR = Path(__file__).parent.parent / "templates"
|
|
34
83
|
|
|
35
84
|
# KJB logo — read from bundled PNG so it renders in Playwright file:// PDF context
|
|
@@ -101,22 +150,36 @@ def estimate_recoverable_revenue(
|
|
|
101
150
|
audit_result: AuditResult,
|
|
102
151
|
monthly_ad_spend_usd: int | None = None,
|
|
103
152
|
) -> dict[str, int | float | str | bool]:
|
|
104
|
-
"""Estimate the monthly
|
|
153
|
+
"""Estimate the monthly **conversion value** recoverable by fixing the stack.
|
|
154
|
+
|
|
155
|
+
Formula:
|
|
156
|
+
ad_revenue = monthly_ad_spend × ROAS
|
|
157
|
+
monthly_recovered = ad_revenue × US_opt_out_rate × signal_gap × recovery_uplift
|
|
105
158
|
|
|
106
|
-
|
|
159
|
+
The output is *recovered conversion value*, not "ad spend not wasted" — that
|
|
160
|
+
distinction matters at enterprise scale, where a 4% signal gap on $5M/mo of
|
|
161
|
+
ad-attributable revenue ($30M+ with ROAS) is six figures, not four.
|
|
107
162
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
(a fully broken stack losing all consent-denied traffic back to ad AI).
|
|
163
|
+
Auto-tiers brand size from scan signals when the buyer hasn't self-reported
|
|
164
|
+
``--monthly-ad-spend``. Without auto-tiering, the formula defaulted to a
|
|
165
|
+
mid-market anchor that understates enterprise impact by 50-100×.
|
|
112
166
|
|
|
113
|
-
|
|
114
|
-
|
|
167
|
+
``signal_gap`` scales 0.0 → 1.0 based on observable defects in the scan:
|
|
168
|
+
each confirmed cookie violation, post-denial pixel firing, sGTM presence,
|
|
169
|
+
and partial/broken Consent Mode state adds to the gap. Capped at 1.0.
|
|
170
|
+
|
|
171
|
+
Kept defensible in a sales conversation: every input is either user-reported
|
|
172
|
+
or derived from the same AuditResult the rest of the report is built on.
|
|
115
173
|
"""
|
|
174
|
+
# Brand-tier auto-estimation provides defaults when the buyer hasn't
|
|
175
|
+
# self-reported spend. ROAS is always derived from the tier — even with a
|
|
176
|
+
# user-reported spend, we want the recovery framing in revenue terms.
|
|
177
|
+
tier_label, tier_default_spend, roas = _estimate_brand_tier(audit_result)
|
|
178
|
+
|
|
116
179
|
spend = (
|
|
117
180
|
monthly_ad_spend_usd
|
|
118
181
|
if monthly_ad_spend_usd and monthly_ad_spend_usd > 0
|
|
119
|
-
else
|
|
182
|
+
else tier_default_spend
|
|
120
183
|
)
|
|
121
184
|
|
|
122
185
|
violations = [f for f in audit_result.findings if f.status == ViolationStatus.CONFIRMED]
|
|
@@ -151,7 +214,12 @@ def estimate_recoverable_revenue(
|
|
|
151
214
|
gap += 0.05 # no CMP detected = no consent plumbing
|
|
152
215
|
gap = min(gap, 1.0)
|
|
153
216
|
|
|
154
|
-
|
|
217
|
+
# Ad-attributable monthly revenue = spend × ROAS (e.g. $2M × 6x = $12M).
|
|
218
|
+
ad_attributable_revenue = int(spend * roas)
|
|
219
|
+
# Monthly recovered conversion value at full signal gap on opt-out traffic.
|
|
220
|
+
monthly_recoverable = int(
|
|
221
|
+
ad_attributable_revenue * _US_OPTOUT_MARKET_RATE * gap * _RECOVERY_UPLIFT
|
|
222
|
+
)
|
|
155
223
|
monthly_recoverable_low = int(monthly_recoverable * 0.6)
|
|
156
224
|
monthly_recoverable_high = int(monthly_recoverable * 1.4)
|
|
157
225
|
annual_recoverable = monthly_recoverable * 12
|
|
@@ -159,8 +227,11 @@ def estimate_recoverable_revenue(
|
|
|
159
227
|
return {
|
|
160
228
|
"monthly_ad_spend_usd": spend,
|
|
161
229
|
"ad_spend_user_provided": bool(monthly_ad_spend_usd and monthly_ad_spend_usd > 0),
|
|
230
|
+
"brand_tier_label": tier_label,
|
|
231
|
+
"roas_multiplier": roas,
|
|
232
|
+
"ad_attributable_revenue_usd": ad_attributable_revenue,
|
|
162
233
|
"signal_gap_pct": round(gap * 100, 1),
|
|
163
|
-
"consent_loss_rate_pct": int(
|
|
234
|
+
"consent_loss_rate_pct": int(_US_OPTOUT_MARKET_RATE * 100),
|
|
164
235
|
"recovery_uplift_pct": int(_RECOVERY_UPLIFT * 100),
|
|
165
236
|
"monthly_recoverable_usd": monthly_recoverable,
|
|
166
237
|
"monthly_recoverable_low_usd": monthly_recoverable_low,
|
|
@@ -1060,14 +1131,17 @@ def generate_marp_slides(
|
|
|
1060
1131
|
)
|
|
1061
1132
|
|
|
1062
1133
|
def _metric_card(label: str, value: str, sub: str, accent: str) -> str:
|
|
1134
|
+
# Top-row card. Sized to align visually with the bottom-row scenario
|
|
1135
|
+
# cards on the financial-exposure slide — matching padding + flex base
|
|
1136
|
+
# so the two rows render at equal heights regardless of value width.
|
|
1063
1137
|
return (
|
|
1064
|
-
f'<div style="flex:1;background:#ffffff;border-radius:10px;
|
|
1065
|
-
f'border-top:3px solid {accent};">'
|
|
1066
|
-
f'<div style="color:#4b5563;font-size:0.
|
|
1067
|
-
f"letter-spacing:0.
|
|
1068
|
-
f"<div style=\"font-family:'Inter';font-weight:800;font-size:
|
|
1069
|
-
f'line-height:1;margin-bottom:4px;">{value}</div>'
|
|
1070
|
-
f'<div style="color:#6b7280;font-size:0.
|
|
1138
|
+
f'<div style="flex:1 1 0;min-width:0;background:#ffffff;border-radius:10px;'
|
|
1139
|
+
f'padding:12px 14px;border-top:3px solid {accent};">'
|
|
1140
|
+
f'<div style="color:#4b5563;font-size:0.55em;text-transform:uppercase;'
|
|
1141
|
+
f"letter-spacing:0.14em;margin-bottom:4px;font-family:'Inter';font-weight:600;\">{label}</div>"
|
|
1142
|
+
f"<div style=\"font-family:'Inter';font-weight:800;font-size:1.7em;color:{accent};"
|
|
1143
|
+
f'line-height:1.1;margin-bottom:4px;letter-spacing:-0.01em;">{value}</div>'
|
|
1144
|
+
f'<div style="color:#6b7280;font-size:0.55em;font-weight:300;line-height:1.45;">{sub}</div>'
|
|
1071
1145
|
f"</div>"
|
|
1072
1146
|
)
|
|
1073
1147
|
|
|
@@ -1319,8 +1393,8 @@ def generate_marp_slides(
|
|
|
1319
1393
|
if violations or pixel_violations:
|
|
1320
1394
|
v_count = len(violations)
|
|
1321
1395
|
p_count = len(pixel_violations)
|
|
1322
|
-
# Statutory rates row
|
|
1323
|
-
_exposure_html = '<div style="display:flex;gap:8px;margin-top:14px;">'
|
|
1396
|
+
# Statutory rates row — top of slide
|
|
1397
|
+
_exposure_html = '<div style="display:flex;gap:8px;margin-top:14px;align-items:stretch;">'
|
|
1324
1398
|
_exposure_html += _metric_card(
|
|
1325
1399
|
"CCPA / CPRA", "$7,500", "per intentional violation · per consumer", "#3d6abb"
|
|
1326
1400
|
)
|
|
@@ -1356,20 +1430,22 @@ def generate_marp_slides(
|
|
|
1356
1430
|
return f"${n/1_000_000:.1f}M"
|
|
1357
1431
|
return f"${n/1_000:.0f}K"
|
|
1358
1432
|
|
|
1359
|
-
# Scenario cards —
|
|
1360
|
-
# so
|
|
1361
|
-
|
|
1433
|
+
# Scenario cards — bottom row. Equal width via flex:1 1 0, min-width:0
|
|
1434
|
+
# so wide ranges can shrink inside their column instead of pushing the
|
|
1435
|
+
# row past the slide bounds. Smaller value font (1.05em) matches the
|
|
1436
|
+
# top-row card hierarchy and stays on one line at any tier.
|
|
1437
|
+
_exposure_html += '<div style="display:flex;gap:8px;margin-top:8px;align-items:stretch;">'
|
|
1362
1438
|
for label, optouts, statutory, s_low, s_high in _scenarios:
|
|
1363
1439
|
_exposure_html += (
|
|
1364
|
-
f'<div style="flex:1;background:#ffffff;border-radius:10px;
|
|
1365
|
-
f'border-top:3px solid #ef4444;">'
|
|
1366
|
-
f'<div style="color:#4b5563;font-size:0.
|
|
1367
|
-
f"letter-spacing:0.
|
|
1368
|
-
f"<div style=\"font-family:'Inter';font-weight:800;font-size:1.
|
|
1369
|
-
f'line-height:1.
|
|
1440
|
+
f'<div style="flex:1 1 0;min-width:0;background:#ffffff;border-radius:10px;'
|
|
1441
|
+
f'padding:12px 14px;border-top:3px solid #ef4444;">'
|
|
1442
|
+
f'<div style="color:#4b5563;font-size:0.55em;text-transform:uppercase;'
|
|
1443
|
+
f"letter-spacing:0.14em;margin-bottom:4px;font-family:'Inter';font-weight:600;\">{label}</div>"
|
|
1444
|
+
f"<div style=\"font-family:'Inter';font-weight:800;font-size:1.05em;color:#ef4444;"
|
|
1445
|
+
f'line-height:1.15;margin-bottom:4px;letter-spacing:-0.01em;">'
|
|
1370
1446
|
f"{_fmt(s_low)}–{_fmt(s_high)}</div>"
|
|
1371
|
-
f'<div style="color:#6b7280;font-size:0.
|
|
1372
|
-
f"{optouts:,} CA opt-outs/mo
|
|
1447
|
+
f'<div style="color:#6b7280;font-size:0.55em;font-weight:300;line-height:1.45;">'
|
|
1448
|
+
f"{optouts:,} CA opt-outs/mo<br>max {_fmt(statutory)}/yr</div>"
|
|
1373
1449
|
f"</div>"
|
|
1374
1450
|
)
|
|
1375
1451
|
_exposure_html += "</div>"
|
|
@@ -1809,7 +1885,7 @@ style: |
|
|
|
1809
1885
|
|
|
1810
1886
|
{"---" + chr(10) + chr(10) + _pixel_marp_section if _pixel_marp_section else ""}
|
|
1811
1887
|
|
|
1812
|
-
{"---" + chr(10) + chr(10) + "### RISK QUANTIFICATION" + chr(10) + chr(10) + "# Financial Exposure Estimate" + chr(10) + chr(10) + _exposure_html if (violations or pixel_violations) else ""}
|
|
1888
|
+
{"---" + chr(10) + chr(10) + "<!-- _class: compact -->" + chr(10) + chr(10) + "### RISK QUANTIFICATION" + chr(10) + chr(10) + "# Financial Exposure Estimate" + chr(10) + chr(10) + _exposure_html if (violations or pixel_violations) else ""}
|
|
1813
1889
|
|
|
1814
1890
|
{_gpc_slide_md}
|
|
1815
1891
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/data/vendor_library/vendors.json
RENAMED
|
File without changes
|
|
File without changes
|
{consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/data/wiki/concepts/cipa-vppa.md
RENAMED
|
File without changes
|
{consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/data/wiki/concepts/cmp-failures.md
RENAMED
|
File without changes
|
|
File without changes
|
{consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/data/wiki/concepts/dark-patterns.md
RENAMED
|
File without changes
|
{consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/data/wiki/concepts/gpc-signal.md
RENAMED
|
File without changes
|
{consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/data/wiki/concepts/ssgtm-risk.md
RENAMED
|
File without changes
|
|
File without changes
|
{consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/data/wiki/enforcement/gdpr-fines.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/data/wiki/regulations/ccpa.md
RENAMED
|
File without changes
|
{consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/data/wiki/regulations/gdpr.md
RENAMED
|
File without changes
|
|
File without changes
|
{consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/data/wiki/regulations/tcf.md
RENAMED
|
File without changes
|
|
File without changes
|
{consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/data/wiki/technical/cmp-profiles.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/templates/audit_deck.marp.md.j2
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/tools/jurisdiction_detector.py
RENAMED
|
File without changes
|
{consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/tools/tool_01_gtm_parser.py
RENAMED
|
File without changes
|
|
File without changes
|
{consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/tools/tool_03_browser_scanner.py
RENAMED
|
File without changes
|
{consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/tools/tool_04_har_analyzer.py
RENAMED
|
File without changes
|
{consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/tools/tool_05_vendor_library.py
RENAMED
|
File without changes
|
{consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/tools/tool_06_ssgtm_detector.py
RENAMED
|
File without changes
|
{consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/tools/tool_06b_pixel_detector.py
RENAMED
|
File without changes
|
{consent_engine-0.3.2 → consent_engine-0.3.3}/src/consent_engine/tools/tool_07_rag_retriever.py
RENAMED
|
File without changes
|