inesbedar commited on
Commit
3c89bc3
·
verified ·
1 Parent(s): e6cc26b

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +1014 -0
app.py ADDED
@@ -0,0 +1,1014 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ─────────────────────────────────────────────
2
+ # RegMap — AI Regulation Compliance Platform
3
+ # Copyright (c) 2026 RegMap. All rights reserved.
4
+ # License: CC BY-NC 4.0
5
+ # https://creativecommons.org/licenses/by-nc/4.0/
6
+ # Commercial use prohibited without prior written consent.
7
+ # ─────────────────────────────────────────────
8
+
9
+ import streamlit as st
10
+
11
+ # ─────────────────────────────────────────────
12
+ # REGMAP — AI System ID Card
13
+ # ─────────────────────────────────────────────
14
+
15
+ st.set_page_config(
16
+ page_title="RegMap — AI System ID Card",
17
+ page_icon="◈",
18
+ layout="centered",
19
+ )
20
+
21
+ # ── Visual Identity ──
22
+ st.markdown("""
23
+ <style>
24
+ @import url('https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,100..1000;1,9..40,100..1000&display=swap');
25
+ @import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&display=swap');
26
+
27
+ html, body, [class*="css"] { font-family: 'DM Sans', sans-serif; }
28
+ .stApp { background-color: #F7F8FA; }
29
+
30
+ /* ── Dark text override for HF Spaces dark mode ──
31
+ Excludes .rmh (RegMap Header) elements */
32
+ .stApp p:not(.rmh-el), .stApp span:not(.rmh-el), .stApp label,
33
+ .stApp h1, .stApp h2, .stApp h3, .stApp h4,
34
+ .stApp li, .stApp strong:not(.rmh-el), .stApp em,
35
+ [data-testid="stMarkdownContainer"] p:not(.rmh-el),
36
+ [data-testid="stMarkdownContainer"] h4,
37
+ [data-testid="stMarkdownContainer"] strong:not(.rmh-el),
38
+ [data-testid="stMarkdownContainer"] li {
39
+ color: #1E293B !important;
40
+ }
41
+
42
+ /* ── Header ── */
43
+ .rmh {
44
+ background: linear-gradient(135deg, #0F2B46 0%, #143A5C 60%, #0F2B46 100%);
45
+ padding: 2rem 2.2rem 1.6rem 2.2rem;
46
+ border-radius: 14px;
47
+ margin-bottom: 1.8rem;
48
+ position: relative;
49
+ overflow: hidden;
50
+ border: 1px solid rgba(255,255,255,0.04);
51
+ }
52
+ .rmh::before {
53
+ content: '';
54
+ position: absolute;
55
+ top: -50%; right: -20%;
56
+ width: 60%; height: 200%;
57
+ background: radial-gradient(ellipse, rgba(13,148,136,0.15) 0%, transparent 70%);
58
+ pointer-events: none;
59
+ }
60
+ .rmh::after {
61
+ content: '';
62
+ position: absolute;
63
+ bottom: 0; left: 0;
64
+ width: 100%; height: 1px;
65
+ background: linear-gradient(90deg, transparent, rgba(45,212,191,0.3), transparent);
66
+ }
67
+ .rmh-logo {
68
+ font-family: 'Space Grotesk', sans-serif !important;
69
+ font-size: 1.7rem !important; font-weight: 700 !important;
70
+ letter-spacing: -0.02em !important;
71
+ color: #FFFFFF !important;
72
+ margin: 0 !important; position: relative; z-index: 1;
73
+ line-height: 1.2 !important;
74
+ }
75
+ .rmh-accent { color: #2DD4BF !important; }
76
+ .rmh-tagline {
77
+ font-family: 'DM Sans', sans-serif !important;
78
+ color: #8BA3BB !important;
79
+ font-size: 0.82rem !important;
80
+ font-weight: 400 !important;
81
+ letter-spacing: 0.03em !important;
82
+ margin: 0.35rem 0 0 0 !important;
83
+ position: relative; z-index: 1;
84
+ }
85
+ .rmh-badges {
86
+ display: flex; gap: 0.45rem;
87
+ margin-top: 0.9rem;
88
+ position: relative; z-index: 1;
89
+ }
90
+ .rmh-badge {
91
+ background: rgba(45,212,191,0.08) !important;
92
+ border: 1px solid rgba(45,212,191,0.2) !important;
93
+ border-radius: 5px !important;
94
+ padding: 0.18rem 0.55rem !important;
95
+ font-family: 'Space Grotesk', sans-serif !important;
96
+ font-size: 0.68rem !important;
97
+ font-weight: 600 !important;
98
+ color: #5EEAD4 !important;
99
+ letter-spacing: 0.06em !important;
100
+ text-transform: uppercase !important;
101
+ }
102
+
103
+ /* ── Progress ── */
104
+ .progress-container {
105
+ background: #E2E8F0; border-radius: 8px;
106
+ height: 5px; margin-bottom: 0.4rem;
107
+ }
108
+ .progress-fill {
109
+ background: linear-gradient(90deg, #0D9488, #2DD4BF);
110
+ height: 5px; border-radius: 8px;
111
+ transition: width 0.4s ease;
112
+ }
113
+ .progress-label {
114
+ color: #64748B !important; font-size: 0.78rem;
115
+ font-weight: 500; margin-bottom: 1.5rem;
116
+ }
117
+
118
+ /* ── Screen title ── */
119
+ .screen-title {
120
+ font-family: 'Space Grotesk', sans-serif;
121
+ color: #0F2B46 !important; font-size: 1.15rem;
122
+ font-weight: 700; letter-spacing: -0.01em;
123
+ margin-bottom: 0.2rem;
124
+ }
125
+ .screen-subtitle {
126
+ color: #64748B !important; font-size: 0.82rem;
127
+ font-weight: 400; margin-bottom: 1.5rem;
128
+ line-height: 1.4;
129
+ }
130
+
131
+ /* ── Buttons ── */
132
+ .stButton > button {
133
+ font-family: 'DM Sans', sans-serif;
134
+ font-weight: 600; font-size: 0.85rem;
135
+ border-radius: 8px; padding: 0.45rem 1.5rem;
136
+ color: #FFFFFF !important;
137
+ }
138
+
139
+ /* ── Summary cards ── */
140
+ .summary-section {
141
+ background: #F8FAFB;
142
+ border: 1px solid #E2E8F0;
143
+ border-left: 3px solid #0D9488;
144
+ border-radius: 8px;
145
+ padding: 1rem 1.3rem;
146
+ margin-bottom: 0.8rem;
147
+ }
148
+ .summary-section h4 {
149
+ color: #0F2B46 !important;
150
+ font-family: 'Space Grotesk', sans-serif;
151
+ font-size: 0.88rem; font-weight: 700;
152
+ margin: 0 0 0.5rem 0;
153
+ }
154
+ .summary-section p {
155
+ color: #334155 !important; font-size: 0.85rem;
156
+ margin: 0.15rem 0; line-height: 1.5;
157
+ }
158
+
159
+ /* ── Widget spacing ── */
160
+ .stSelectbox, .stMultiSelect, .stTextInput, .stTextArea { margin-bottom: 0.6rem; }
161
+
162
+ /* ── Widget labels (belt-and-suspenders) ── */
163
+ .stSelectbox label, .stMultiSelect label, .stTextInput label, .stTextArea label,
164
+ .stRadio label, .stCheckbox label, .stSlider label {
165
+ color: #1E293B !important;
166
+ }
167
+ .stSelectbox label p, .stMultiSelect label p, .stTextInput label p, .stTextArea label p,
168
+ .stRadio label p, .stCheckbox label p, .stSlider label p {
169
+ color: #1E293B !important;
170
+ }
171
+ .stRadio div[role="radiogroup"] label p,
172
+ .stRadio div[role="radiogroup"] label span,
173
+ .stRadio div[role="radiogroup"] label,
174
+ [data-testid="stRadio"] label p,
175
+ [data-testid="stRadio"] label span,
176
+ [data-testid="stRadio"] label,
177
+ [data-testid="stWidgetLabel"] p,
178
+ [data-testid="stWidgetLabel"] span {
179
+ color: #1E293B !important;
180
+ }
181
+ .stMarkdown h4, .stMarkdown p:not(.rmh-el), .stMarkdown strong:not(.rmh-el), .stMarkdown em,
182
+ .stMarkdown li, .stMarkdown span:not(.rmh-el),
183
+ [data-testid="stMarkdownContainer"] span:not(.rmh-el) {
184
+ color: #1E293B !important;
185
+ }
186
+ .stAlert p, .stAlert span,
187
+ [data-testid="stAlert"] p,
188
+ [data-testid="stAlert"] span {
189
+ color: #1E293B !important;
190
+ }
191
+ [data-testid="stCaptionContainer"] p,
192
+ [data-testid="stCaptionContainer"] span {
193
+ color: #64748B !important;
194
+ }
195
+
196
+ /* ── Beta disclaimer ── */
197
+ .beta-disclaimer {
198
+ background: #FEF3C7;
199
+ border: 1px solid #F59E0B;
200
+ border-radius: 8px;
201
+ padding: 0.8rem 1rem;
202
+ margin-bottom: 1rem;
203
+ color: #92400E !important;
204
+ font-size: 0.82rem;
205
+ line-height: 1.5;
206
+ }
207
+ .beta-disclaimer strong { color: #92400E !important; }
208
+
209
+ /* ── Regulation banner ── */
210
+ .reg-banner {
211
+ background: linear-gradient(135deg, #0F2B46 0%, #143A5C 100%);
212
+ border: 1px solid rgba(45,212,191,0.2);
213
+ border-radius: 12px;
214
+ padding: 1.5rem 1.8rem;
215
+ margin: 1.5rem 0 1rem 0;
216
+ }
217
+ .reg-banner-title {
218
+ font-family: 'Space Grotesk', sans-serif !important;
219
+ font-size: 1rem !important;
220
+ font-weight: 700 !important;
221
+ color: #FFFFFF !important;
222
+ margin: 0 0 0.8rem 0 !important;
223
+ }
224
+ .reg-banner-list {
225
+ list-style: none !important;
226
+ padding: 0 !important;
227
+ margin: 0 0 1rem 0 !important;
228
+ }
229
+ .reg-banner-list li {
230
+ color: #E2E8F0 !important;
231
+ font-size: 0.85rem !important;
232
+ padding: 0.35rem 0 !important;
233
+ border-bottom: 1px solid rgba(255,255,255,0.06) !important;
234
+ }
235
+ .reg-banner-list li:last-child { border-bottom: none !important; }
236
+ .reg-tag {
237
+ display: inline-block !important;
238
+ background: rgba(45,212,191,0.15) !important;
239
+ color: #5EEAD4 !important;
240
+ font-size: 0.65rem !important;
241
+ font-weight: 600 !important;
242
+ padding: 0.1rem 0.4rem !important;
243
+ border-radius: 4px !important;
244
+ margin-right: 0.5rem !important;
245
+ vertical-align: middle !important;
246
+ letter-spacing: 0.03em !important;
247
+ }
248
+ .reg-banner-cta {
249
+ color: #E2E8F0 !important;
250
+ font-size: 0.9rem !important;
251
+ font-weight: 700 !important;
252
+ margin: 0.5rem 0 0 0 !important;
253
+ }
254
+ .reg-section-label {
255
+ color: #5EEAD4 !important;
256
+ font-size: 0.7rem !important;
257
+ font-weight: 700 !important;
258
+ letter-spacing: 0.08em !important;
259
+ text-transform: uppercase !important;
260
+ margin: 0.8rem 0 0.3rem 0 !important;
261
+ padding-top: 0.6rem !important;
262
+ border-top: 1px solid rgba(255,255,255,0.08) !important;
263
+ }
264
+ .reg-section-label-first {
265
+ color: #5EEAD4 !important;
266
+ font-size: 0.7rem !important;
267
+ font-weight: 700 !important;
268
+ letter-spacing: 0.08em !important;
269
+ text-transform: uppercase !important;
270
+ margin: 0 0 0.3rem 0 !important;
271
+ padding-top: 0 !important;
272
+ border-top: none !important;
273
+ }
274
+
275
+ /* ── Hide branding ── */
276
+ #MainMenu {visibility: hidden;}
277
+ footer {visibility: hidden;}
278
+ header {visibility: hidden;}
279
+ </style>
280
+ """, unsafe_allow_html=True)
281
+
282
+
283
+ # ── Reference Data ──
284
+
285
+ INDUSTRY_SECTORS = [
286
+ "Agriculture & Food", "Automotive & Transportation",
287
+ "Banking & Financial Services", "Construction & Real Estate",
288
+ "Consulting & Professional Services", "Defence & Security",
289
+ "Education & Training", "Energy & Utilities",
290
+ "Entertainment & Media", "Environmental Services",
291
+ "Government & Public Administration", "Healthcare & Life Sciences",
292
+ "Hospitality & Tourism", "Human Resources & Recruitment",
293
+ "Insurance", "Legal Services", "Logistics & Supply Chain",
294
+ "Manufacturing", "Mining & Natural Resources", "Non-Profit & NGO",
295
+ "Pharmaceuticals", "Retail & E-commerce", "Telecommunications",
296
+ "Technology & Software", "Other",
297
+ ]
298
+
299
+ US_STATES = [
300
+ "Alabama", "Alaska", "Arizona", "Arkansas", "California", "Colorado",
301
+ "Connecticut", "Delaware", "Florida", "Georgia", "Hawaii", "Idaho",
302
+ "Illinois", "Indiana", "Iowa", "Kansas", "Kentucky", "Louisiana",
303
+ "Maine", "Maryland", "Massachusetts", "Michigan", "Minnesota",
304
+ "Mississippi", "Missouri", "Montana", "Nebraska", "Nevada",
305
+ "New Hampshire", "New Jersey", "New Mexico", "New York",
306
+ "North Carolina", "North Dakota", "Ohio", "Oklahoma", "Oregon",
307
+ "Pennsylvania", "Rhode Island", "South Carolina", "South Dakota",
308
+ "Tennessee", "Texas", "Utah", "Vermont", "Virginia", "Washington",
309
+ "West Virginia", "Wisconsin", "Wyoming", "District of Columbia",
310
+ ]
311
+
312
+ # States with specific AI regulation (for downstream analysis)
313
+ US_STATES_WITH_AI_REGULATION = [
314
+ "California", "Colorado", "Connecticut", "Illinois", "Texas", "Utah",
315
+ ]
316
+
317
+ EU_COUNTRIES = [
318
+ "Austria", "Belgium", "Bulgaria", "Croatia", "Cyprus", "Czechia",
319
+ "Denmark", "Estonia", "Finland", "France", "Germany", "Greece",
320
+ "Hungary", "Ireland", "Italy", "Latvia", "Lithuania", "Luxembourg",
321
+ "Malta", "Netherlands", "Poland", "Portugal", "Romania", "Slovakia",
322
+ "Slovenia", "Spain", "Sweden",
323
+ "Iceland (EEA)", "Liechtenstein (EEA)", "Norway (EEA)",
324
+ ]
325
+
326
+ UAE_EMIRATES = [
327
+ "Dubai", "Abu Dhabi", "Sharjah", "Ajman",
328
+ "Fujairah", "Ras Al Khaimah", "Umm Al Quwain",
329
+ ]
330
+
331
+ UAE_FREE_ZONES = {
332
+ "Dubai": ["DIFC", "DMCC", "JAFZA", "Dubai Internet City",
333
+ "Dubai Media City", "Dubai Silicon Oasis", "Dubai Healthcare City",
334
+ "Dubai Design District (d3)", "Dubai South", "Dubai Knowledge Park",
335
+ "DAFZA", "Dubai World Trade Centre", "Dubai Science Park",
336
+ "Dubai Textile City", "DUCAMZ", "Dubai Maritime City",
337
+ "Meydan Free Zone", "IFZA"],
338
+ "Abu Dhabi": ["ADGM", "KIZAD", "Masdar City", "ADAFZ",
339
+ "Khalifa Port Free Trade Zone", "Twofour54"],
340
+ "Sharjah": ["Sharjah Media City (Shams)", "SAIF Zone",
341
+ "Sharjah Publishing City", "Hamriyah Free Zone"],
342
+ "Ras Al Khaimah": ["RAKEZ", "RAK Maritime City", "RAK Media City"],
343
+ "Ajman": ["Ajman Free Zone"],
344
+ "Fujairah": ["Fujairah Free Zone", "Fujairah Creative City"],
345
+ "Umm Al Quwain": ["UAQ Free Trade Zone"],
346
+ }
347
+
348
+ ROLES = [
349
+ "Provider — You develop or commission the AI system",
350
+ "Deployer — You use an AI system in your operations (you did not build it)",
351
+ "Authorised Representative (EU) — You act on behalf of a non-EU provider to fulfill EU AI Act obligations",
352
+ "Importer (EU) — You place on the EU market an AI system from a non-EU provider",
353
+ "Distributor (EU) — You make an AI system available on the EU market (neither provider nor importer)",
354
+ ]
355
+
356
+ AI_TYPES = ["Machine Learning (ML)", "Generative AI (GenAI)", "Agentic AI", "Rule-based", "Hybrid"]
357
+
358
+ DATA_TYPE_OPTIONS = [
359
+ "Personal data (name, email, ID)",
360
+ "Sensitive/special category data (health, race, religion, political, sexual orientation)",
361
+ "Biometric data",
362
+ "Children's data (<18)",
363
+ "Financial/credit data",
364
+ "Employment/HR data",
365
+ "Copyrighted content (text, images, audio, video)",
366
+ "Publicly available data only",
367
+ "No personal data",
368
+ ]
369
+
370
+ TRAINING_SOURCES = ["Public web", "Licensed datasets", "Proprietary data", "User-generated content"]
371
+ INVOLVEMENT_LEVELS = ["Fully automated", "Human-assisted", "Human decides"]
372
+
373
+ ALL_COUNTRIES = [
374
+ "Afghanistan", "Albania", "Algeria", "Andorra", "Angola", "Argentina",
375
+ "Armenia", "Australia", "Austria", "Azerbaijan", "Bahrain", "Bangladesh",
376
+ "Belarus", "Belgium", "Bolivia", "Bosnia and Herzegovina", "Brazil",
377
+ "Brunei", "Bulgaria", "Cambodia", "Cameroon", "Canada", "Chile", "China",
378
+ "Colombia", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czechia",
379
+ "Denmark", "Dominican Republic", "Ecuador", "Egypt", "El Salvador",
380
+ "Estonia", "Ethiopia", "Finland", "France", "Georgia", "Germany",
381
+ "Ghana", "Greece", "Guatemala", "Honduras", "Hong Kong", "Hungary",
382
+ "Iceland", "India", "Indonesia", "Iran", "Iraq", "Ireland", "Israel",
383
+ "Italy", "Jamaica", "Japan", "Jordan", "Kazakhstan", "Kenya", "Kuwait",
384
+ "Latvia", "Lebanon", "Libya", "Liechtenstein", "Lithuania", "Luxembourg",
385
+ "Malaysia", "Malta", "Mexico", "Moldova", "Monaco", "Mongolia",
386
+ "Montenegro", "Morocco", "Mozambique", "Myanmar", "Nepal",
387
+ "Netherlands", "New Zealand", "Nicaragua", "Nigeria", "North Korea",
388
+ "North Macedonia", "Norway", "Oman", "Pakistan", "Panama",
389
+ "Paraguay", "Peru", "Philippines", "Poland", "Portugal", "Qatar",
390
+ "Romania", "Russia", "Rwanda", "Saudi Arabia", "Senegal", "Serbia",
391
+ "Singapore", "Slovakia", "Slovenia", "Somalia", "South Africa",
392
+ "South Korea", "Spain", "Sri Lanka", "Sudan", "Sweden", "Switzerland",
393
+ "Syria", "Taiwan", "Tanzania", "Thailand", "Tunisia", "Turkey",
394
+ "Uganda", "Ukraine", "United Arab Emirates", "United Kingdom",
395
+ "United States", "Uruguay", "Uzbekistan", "Venezuela", "Vietnam",
396
+ "Yemen", "Zambia", "Zimbabwe",
397
+ ]
398
+
399
+
400
+ # ── Session State ──
401
+
402
+ TOTAL_SCREENS = 7
403
+
404
+ if "screen" not in st.session_state:
405
+ st.session_state.screen = 1
406
+ if "data" not in st.session_state:
407
+ st.session_state.data = {}
408
+
409
+
410
+ def go_next():
411
+ if st.session_state.screen < TOTAL_SCREENS + 1:
412
+ st.session_state.screen += 1
413
+
414
+ def go_back():
415
+ if st.session_state.screen > 1:
416
+ st.session_state.screen -= 1
417
+
418
+ def go_to(n):
419
+ st.session_state.screen = n
420
+
421
+
422
+ # ── Header ──
423
+ st.markdown("""
424
+ <div class="rmh">
425
+ <p class="rmh-logo rmh-el">Reg<span class="rmh-accent rmh-el">Map</span></p>
426
+ <p class="rmh-tagline rmh-el">Navigate AI regulation across jurisdictions</p>
427
+ <div class="rmh-badges">
428
+ <span class="rmh-badge rmh-el">&#127466;&#127482; EU</span>
429
+ <span class="rmh-badge rmh-el">&#127482;&#127480; US</span>
430
+ <span class="rmh-badge rmh-el">&#127462;&#127466; UAE</span>
431
+ </div>
432
+ </div>
433
+ """, unsafe_allow_html=True)
434
+
435
+ # ── Progress ──
436
+ current = st.session_state.screen
437
+ progress_pct = min((current - 1) / TOTAL_SCREENS * 100, 100)
438
+ screen_labels = {
439
+ 1: "Screen 1/7 — AI System Identity",
440
+ 2: "Screen 2/7 — Application Domain",
441
+ 3: "Screen 3/7 — Company Location",
442
+ 4: "Screen 4/7 — Geographic Scope",
443
+ 5: "Screen 5/7 — Role in Value Chain",
444
+ 6: "Screen 6/7 — Technology Profile",
445
+ 7: "Screen 7/7 — Data Profile",
446
+ 8: "Summary",
447
+ }
448
+ label = screen_labels.get(current, "")
449
+
450
+ st.markdown(f"""
451
+ <div class="progress-container"><div class="progress-fill" style="width:{progress_pct}%"></div></div>
452
+ <div class="progress-label">{label}</div>
453
+ """, unsafe_allow_html=True)
454
+
455
+
456
+ # ═══════════════════════════════════════════════
457
+ # SCREEN 1: AI System Identity
458
+ # ═══════════════════════════════════════════════
459
+
460
+ if current == 1:
461
+ st.markdown('<p class="screen-title">AI System ID Card</p>', unsafe_allow_html=True)
462
+ st.markdown('<p class="screen-subtitle">Basic information about your AI system</p>', unsafe_allow_html=True)
463
+
464
+ st.markdown('<div class="beta-disclaimer"><strong>Beta version</strong> — This Space is public by default. We recommend not entering directly identifying data, especially in free text fields.</div>', unsafe_allow_html=True)
465
+
466
+ name = st.text_input(
467
+ "1.1 — AI system name",
468
+ value=st.session_state.data.get("name", ""),
469
+ placeholder="e.g. ResumeScreener, ChatAssist, FraudDetect",
470
+ )
471
+ description = st.text_area(
472
+ "1.2 — Brief description",
473
+ value=st.session_state.data.get("description", ""),
474
+ placeholder="Describe what your AI system does in 1-2 sentences",
475
+ height=100,
476
+ )
477
+
478
+ col1, col2 = st.columns([5, 1])
479
+ with col2:
480
+ if st.button("Next →", use_container_width=True, disabled=(not name.strip())):
481
+ st.session_state.data["name"] = name.strip()
482
+ st.session_state.data["description"] = description.strip()
483
+ go_next()
484
+ st.rerun()
485
+
486
+
487
+ # ═══════════════════════════════════════════════
488
+ # SCREEN 2: Application Domain
489
+ # ═══════════════════════════════════════════════
490
+
491
+ elif current == 2:
492
+ st.markdown('<p class="screen-title">Application Domain</p>', unsafe_allow_html=True)
493
+ st.markdown('<p class="screen-subtitle">Industry sectors where your AI system is used</p>', unsafe_allow_html=True)
494
+
495
+ prev_sector = st.session_state.data.get("sector", [])
496
+ sector = st.multiselect(
497
+ "2.1 — Industry sector(s) (select all that apply)",
498
+ INDUSTRY_SECTORS,
499
+ default=[s for s in prev_sector if s in INDUSTRY_SECTORS],
500
+ )
501
+
502
+ col1, col2, col3 = st.columns([1, 3, 1])
503
+ with col1:
504
+ if st.button("← Back", use_container_width=True):
505
+ go_back()
506
+ st.rerun()
507
+ with col3:
508
+ if st.button("Next →", use_container_width=True, disabled=(not sector)):
509
+ st.session_state.data["sector"] = sector
510
+ go_next()
511
+ st.rerun()
512
+
513
+
514
+ # ═══════════════════════════════════════════════
515
+ # SCREEN 3: Company Location
516
+ # ═══════════════════════════════════════════════
517
+
518
+ elif current == 3:
519
+ st.markdown('<p class="screen-title">Company Location</p>', unsafe_allow_html=True)
520
+ st.markdown('<p class="screen-subtitle">Legal establishment of your organization</p>', unsafe_allow_html=True)
521
+
522
+ prev_base = st.session_state.data.get("company_base", None)
523
+ base_idx = ALL_COUNTRIES.index(prev_base) if prev_base in ALL_COUNTRIES else 0
524
+ company_base = st.selectbox(
525
+ "3.1 — Country of legal establishment",
526
+ ALL_COUNTRIES,
527
+ index=base_idx,
528
+ key="q_company_base",
529
+ )
530
+
531
+ col1, col2, col3 = st.columns([1, 3, 1])
532
+ with col1:
533
+ if st.button("← Back", use_container_width=True):
534
+ go_back()
535
+ st.rerun()
536
+ with col3:
537
+ if st.button("Next →", use_container_width=True):
538
+ st.session_state.data["company_base"] = company_base
539
+ go_next()
540
+ st.rerun()
541
+
542
+
543
+ # ═══════════════════════════════════════════════
544
+ # SCREEN 4: Geographic Scope
545
+ # ═══════════════════════════════════════════════
546
+
547
+ elif current == 4:
548
+ st.markdown('<p class="screen-title">Geographic Scope</p>', unsafe_allow_html=True)
549
+ st.markdown('<p class="screen-subtitle">Countries where your AI system has a presence</p>', unsafe_allow_html=True)
550
+
551
+ prev_countries = st.session_state.data.get("operating_countries", [])
552
+ operating_countries = st.multiselect(
553
+ "4.1 — Countries where the AI system is deployed, distributed, or has end users (select all that apply)",
554
+ ALL_COUNTRIES,
555
+ default=[c for c in prev_countries if c in ALL_COUNTRIES],
556
+ key="q_operating_countries",
557
+ )
558
+
559
+ EU_COUNTRY_NAMES_CLEAN = [c.replace(" (EEA)", "") for c in EU_COUNTRIES]
560
+ selected_eu = [c for c in operating_countries if c in EU_COUNTRY_NAMES_CLEAN or c in ["Iceland", "Liechtenstein", "Norway"]]
561
+ has_us = "United States" in operating_countries
562
+ has_uae = "United Arab Emirates" in operating_countries
563
+
564
+ us_states = []
565
+ if has_us:
566
+ prev_states = st.session_state.data.get("us_states", [])
567
+ us_states = st.multiselect(
568
+ "4.2 — US states (select all that apply)",
569
+ US_STATES,
570
+ default=[s for s in prev_states if s in US_STATES],
571
+ key="q_us_states",
572
+ )
573
+
574
+ uae_emirates = []
575
+ if has_uae:
576
+ prev_emirates = st.session_state.data.get("uae_emirates", [])
577
+ uae_emirates = st.multiselect(
578
+ "4.3 — UAE emirates (select all that apply)",
579
+ UAE_EMIRATES,
580
+ default=[e for e in prev_emirates if e in UAE_EMIRATES],
581
+ key="q_uae_emirates",
582
+ )
583
+
584
+ # 4.4 — UAE free zones (if Dubai or Abu Dhabi selected)
585
+ uae_free_zones = []
586
+ emirates_with_fz = [e for e in uae_emirates if e in UAE_FREE_ZONES]
587
+ if emirates_with_fz:
588
+ available_fz = []
589
+ for em in emirates_with_fz:
590
+ available_fz.extend(UAE_FREE_ZONES[em])
591
+ prev_fz = st.session_state.data.get("uae_free_zones", [])
592
+ uae_free_zones = st.multiselect(
593
+ "4.4 — UAE free zones (select all that apply, or leave empty if none)",
594
+ available_fz,
595
+ default=[f for f in prev_fz if f in available_fz],
596
+ key="q_uae_free_zones",
597
+ )
598
+
599
+ can_proceed = len(operating_countries) > 0
600
+ if has_us and not us_states:
601
+ can_proceed = False
602
+ if has_uae and not uae_emirates:
603
+ can_proceed = False
604
+
605
+ col1, col2, col3 = st.columns([1, 3, 1])
606
+ with col1:
607
+ if st.button("← Back", use_container_width=True):
608
+ go_back()
609
+ st.rerun()
610
+ with col3:
611
+ if st.button("Next →", use_container_width=True, disabled=(not can_proceed)):
612
+ st.session_state.data["operating_countries"] = operating_countries
613
+ st.session_state.data["us_states"] = us_states if has_us else []
614
+ st.session_state.data["us_states_with_regulation"] = [s for s in us_states if s in US_STATES_WITH_AI_REGULATION] if has_us else []
615
+ st.session_state.data["eu_countries"] = selected_eu
616
+ st.session_state.data["uae_emirates"] = uae_emirates if has_uae else []
617
+ st.session_state.data["uae_free_zones"] = uae_free_zones if has_uae else []
618
+ regions = []
619
+ if has_us:
620
+ regions.append("United States")
621
+ if selected_eu:
622
+ regions.append("Europe")
623
+ if has_uae:
624
+ regions.append("UAE")
625
+ st.session_state.data["regions"] = regions
626
+ go_next()
627
+ st.rerun()
628
+
629
+
630
+ # ═══════════════════════════════════════════════
631
+ # SCREEN 5: Role in Value Chain
632
+ # ═══════════════════════════════════════════════
633
+
634
+ elif current == 5:
635
+ st.markdown('<p class="screen-title">Role in Value Chain</p>', unsafe_allow_html=True)
636
+ st.markdown('<p class="screen-subtitle">Your organization\'s position in the AI value chain</p>', unsafe_allow_html=True)
637
+
638
+ prev_roles = st.session_state.data.get("roles", [])
639
+ roles = st.multiselect(
640
+ "5.1 — Organization's role (select all that apply)",
641
+ ROLES,
642
+ default=[r for r in prev_roles if r in ROLES],
643
+ key="q_roles",
644
+ )
645
+
646
+ col1, col2, col3 = st.columns([1, 3, 1])
647
+ with col1:
648
+ if st.button("← Back", use_container_width=True):
649
+ go_back()
650
+ st.rerun()
651
+ with col3:
652
+ if st.button("Next →", use_container_width=True, disabled=(not roles)):
653
+ st.session_state.data["roles"] = roles
654
+ go_next()
655
+ st.rerun()
656
+
657
+
658
+ # ═══════════════════════════════════════════════
659
+ # SCREEN 6: Technology Profile
660
+ # ═══════════════════════════════════════════════
661
+
662
+ elif current == 6:
663
+ st.markdown('<p class="screen-title">Technology Profile</p>', unsafe_allow_html=True)
664
+ st.markdown('<p class="screen-subtitle">Technical characteristics of your AI system</p>', unsafe_allow_html=True)
665
+
666
+ # 6.1 AI type
667
+ prev_type = st.session_state.data.get("ai_type", AI_TYPES[0])
668
+ type_idx = AI_TYPES.index(prev_type) if prev_type in AI_TYPES else 0
669
+ ai_type = st.selectbox("6.1 — AI system type", AI_TYPES, index=type_idx, key="q_ai_type")
670
+
671
+ # 6.2 Model type
672
+ MODEL_TYPES = [
673
+ "Foundation model — general-purpose, trained on broad data (e.g. GPT, Claude, Llama)",
674
+ "Fine-tuned model — adapted from a foundation model for specific tasks",
675
+ "Task-specific model — built and trained for a single purpose",
676
+ ]
677
+ prev_model = st.session_state.data.get("model_type", MODEL_TYPES[2])
678
+ model_idx = MODEL_TYPES.index(prev_model) if prev_model in MODEL_TYPES else 2
679
+ model_type = st.selectbox("6.2 — Model type", MODEL_TYPES, index=model_idx, key="q_model_type")
680
+
681
+ # 6.3 Capabilities
682
+ CAPABILITY_OPTIONS = [
683
+ "Interacts directly with end users",
684
+ "Makes or supports decisions that affect individuals",
685
+ "Profiles individuals (builds user profiles based on behavior, preferences, or characteristics)",
686
+ "Generates synthetic content (text, images, audio, video, code)",
687
+ "Performs emotion recognition",
688
+ ]
689
+ prev_caps = st.session_state.data.get("capabilities", [])
690
+ capabilities = st.multiselect(
691
+ "6.3 — Capabilities (select all that apply)",
692
+ CAPABILITY_OPTIONS,
693
+ default=[c for c in prev_caps if c in CAPABILITY_OPTIONS],
694
+ key="q_capabilities",
695
+ )
696
+
697
+ # 6.4 Human involvement — always shown
698
+ prev_involvement = st.session_state.data.get("human_involvement", "N/A")
699
+ INVOLVEMENT_WITH_NA = ["N/A — no decisions affecting individuals", "Fully automated", "Human-assisted", "Human decides"]
700
+ if prev_involvement in INVOLVEMENT_LEVELS:
701
+ inv_idx = INVOLVEMENT_WITH_NA.index(prev_involvement)
702
+ else:
703
+ inv_idx = 0
704
+ human_involvement = st.selectbox(
705
+ "6.4 — Human involvement in decisions",
706
+ INVOLVEMENT_WITH_NA,
707
+ index=inv_idx,
708
+ key="q_involvement",
709
+ )
710
+
711
+ # 6.5 Synthetic content types — always shown
712
+ SYNTHETIC_TYPES = ["Text", "Images", "Audio", "Video", "Code"]
713
+ prev_content = st.session_state.data.get("synthetic_content", [])
714
+ synthetic_content = st.multiselect(
715
+ "6.5 — Synthetic content types (select all that apply)",
716
+ SYNTHETIC_TYPES,
717
+ default=[s for s in prev_content if s in SYNTHETIC_TYPES],
718
+ key="q_content",
719
+ )
720
+
721
+ col1, col2, col3 = st.columns([1, 3, 1])
722
+ with col1:
723
+ if st.button("← Back", use_container_width=True):
724
+ go_back()
725
+ st.rerun()
726
+ with col3:
727
+ if st.button("Next →", use_container_width=True):
728
+ st.session_state.data["ai_type"] = ai_type
729
+ st.session_state.data["model_type"] = model_type
730
+ st.session_state.data["gpai"] = model_type.startswith("Foundation model")
731
+ st.session_state.data["capabilities"] = capabilities
732
+ st.session_state.data["direct_interaction"] = "Interacts directly with end users" in capabilities
733
+ st.session_state.data["decision_making"] = "Makes or supports decisions that affect individuals" in capabilities
734
+ st.session_state.data["profiling"] = "Profiles individuals (builds user profiles based on behavior, preferences, or characteristics)" in capabilities
735
+ is_decision = "Makes or supports decisions that affect individuals" in capabilities
736
+ st.session_state.data["human_involvement"] = human_involvement if is_decision and not human_involvement.startswith("N/A") else "N/A"
737
+ is_synthetic = "Generates synthetic content (text, images, audio, video, code)" in capabilities
738
+ st.session_state.data["synthetic_content"] = synthetic_content if is_synthetic else []
739
+ st.session_state.data["emotion_recognition"] = "Performs emotion recognition" in capabilities
740
+ go_next()
741
+ st.rerun()
742
+
743
+
744
+ # ═══════════════════════════════════════════════
745
+ # SCREEN 7: Data Profile
746
+ # ═══════════════════════════════════════════════
747
+
748
+ elif current == 7:
749
+ st.markdown('<p class="screen-title">Data Profile</p>', unsafe_allow_html=True)
750
+ st.markdown('<p class="screen-subtitle">Data processed and training sources</p>', unsafe_allow_html=True)
751
+
752
+ prev_data_types = st.session_state.data.get("data_types", [])
753
+ selected_data_types = st.multiselect(
754
+ "7.1 — Data types processed (select all that apply)",
755
+ DATA_TYPE_OPTIONS,
756
+ default=[dt for dt in prev_data_types if dt in DATA_TYPE_OPTIONS],
757
+ )
758
+
759
+ st.divider()
760
+
761
+ prev_sources = st.session_state.data.get("training_sources", [])
762
+ training_sources = st.multiselect(
763
+ "7.2 — Training data sources (select all that apply)",
764
+ TRAINING_SOURCES,
765
+ default=[s for s in prev_sources if s in TRAINING_SOURCES],
766
+ )
767
+
768
+ col1, col2, col3 = st.columns([1, 3, 1])
769
+ with col1:
770
+ if st.button("← Back", use_container_width=True):
771
+ go_back()
772
+ st.rerun()
773
+ with col3:
774
+ if st.button("View Summary →", use_container_width=True, disabled=(not selected_data_types)):
775
+ st.session_state.data["data_types"] = selected_data_types
776
+ st.session_state.data["training_sources"] = training_sources
777
+ go_next()
778
+ st.rerun()
779
+
780
+
781
+ # ═══════════════════════════════════════════════
782
+ # SCREEN 8: Summary
783
+ # ═══════════════════════════════════════════════
784
+
785
+ elif current == 8:
786
+ d = st.session_state.data
787
+
788
+ st.markdown('<p class="screen-title">AI System ID Card — Summary</p>', unsafe_allow_html=True)
789
+ st.markdown(f'<p class="screen-subtitle">Review your inputs for <strong>{d.get("name", "")}</strong></p>', unsafe_allow_html=True)
790
+
791
+ # Identity
792
+ st.markdown(f'<div class="summary-section"><h4>AI System Identity</h4><p><strong>Name:</strong> {d.get("name", "—")}</p><p><strong>Description:</strong> {d.get("description", "—") or "—"}</p></div>', unsafe_allow_html=True)
793
+
794
+ # Domain
795
+ sectors = d.get("sector", [])
796
+ sectors_display = ", ".join(sectors) if sectors else "—"
797
+ st.markdown(f'<div class="summary-section"><h4>Application Domain</h4><p><strong>Sector(s):</strong> {sectors_display}</p></div>', unsafe_allow_html=True)
798
+
799
+ # Geography
800
+ geo_lines = f'<p><strong>Company base:</strong> {d.get("company_base", "—")}</p>'
801
+ op_countries = d.get("operating_countries", [])
802
+ if op_countries:
803
+ geo_lines += f'<p><strong>Countries:</strong> {", ".join(op_countries)}</p>'
804
+ if d.get("us_states"):
805
+ regulated = d.get("us_states_with_regulation", [])
806
+ non_regulated = [s for s in d["us_states"] if s not in regulated]
807
+ geo_lines += f'<p><strong>US states:</strong> {", ".join(d["us_states"])}</p>'
808
+ if regulated:
809
+ geo_lines += f'<p><strong>States with AI regulation:</strong> {", ".join(regulated)}</p>'
810
+ if non_regulated:
811
+ geo_lines += f'<p><strong>States without specific AI regulation:</strong> {", ".join(non_regulated)}</p>'
812
+ if d.get("eu_countries"):
813
+ geo_lines += f'<p><strong>EU/EEA countries detected:</strong> {", ".join(d["eu_countries"])}</p>'
814
+ if d.get("uae_emirates"):
815
+ geo_lines += f'<p><strong>UAE emirates:</strong> {", ".join(d["uae_emirates"])}</p>'
816
+ if d.get("uae_free_zones"):
817
+ geo_lines += f'<p><strong>UAE free zones:</strong> {", ".join(d["uae_free_zones"])}</p>'
818
+ st.markdown(f'<div class="summary-section"><h4>Geographic Scope</h4>{geo_lines}</div>', unsafe_allow_html=True)
819
+
820
+ # Roles
821
+ roles_display = ", ".join(d.get("roles", ["—"]))
822
+ st.markdown(f'<div class="summary-section"><h4>Role in Value Chain</h4><p>{roles_display}</p></div>', unsafe_allow_html=True)
823
+
824
+ # Technology
825
+ model_type_display = d.get("model_type", "—")
826
+ caps = d.get("capabilities", [])
827
+ caps_display = ", ".join(caps) if caps else "None selected"
828
+ synthetic = ", ".join(d.get("synthetic_content", [])) or "None"
829
+ involvement = d.get("human_involvement", "N/A")
830
+
831
+ tech_lines = f'<p><strong>AI type:</strong> {d.get("ai_type", "—")}</p>'
832
+ tech_lines += f'<p><strong>Model:</strong> {model_type_display}</p>'
833
+ tech_lines += f'<p><strong>Capabilities:</strong> {caps_display}</p>'
834
+ if d.get("decision_making"):
835
+ tech_lines += f'<p><strong>Human involvement in decisions:</strong> {involvement}</p>'
836
+ if d.get("synthetic_content"):
837
+ tech_lines += f'<p><strong>Synthetic content types:</strong> {synthetic}</p>'
838
+ st.markdown(f'<div class="summary-section"><h4>Technology Profile</h4>{tech_lines}</div>', unsafe_allow_html=True)
839
+
840
+ # Data
841
+ data_types_display = ", ".join(d.get("data_types", ["—"]))
842
+ sources_display = ", ".join(d.get("training_sources", ["—"]))
843
+ st.markdown(f'<div class="summary-section"><h4>Data Profile</h4><p><strong>Data types:</strong> {data_types_display}</p><p><strong>Training sources:</strong> {sources_display}</p></div>', unsafe_allow_html=True)
844
+
845
+ # ── Regulation detection ──
846
+ # Tuples: (region_tag, regulation_name, category)
847
+ # category: "ai" = AI-specific, "other" = related regulation
848
+ detected_regs = []
849
+
850
+ # Helper flags from ID card data
851
+ data_types = d.get("data_types", [])
852
+ sectors = d.get("sector", [])
853
+ roles = d.get("roles", [])
854
+ capabilities = d.get("capabilities", [])
855
+
856
+ personal_data_types = [
857
+ "Personal data (name, email, ID)",
858
+ "Sensitive/special category data (health, race, religion, political, sexual orientation)",
859
+ "Biometric data",
860
+ "Children's data (<18)",
861
+ "Financial/credit data",
862
+ "Employment/HR data",
863
+ ]
864
+ has_personal_data = any(dt in data_types for dt in personal_data_types)
865
+ has_copyrighted = "Copyrighted content (text, images, audio, video)" in data_types
866
+ has_biometric = "Biometric data" in data_types
867
+ has_financial_data = "Financial/credit data" in data_types
868
+ has_employment_data = "Employment/HR data" in data_types
869
+ has_children_data = "Children's data (<18)" in data_types
870
+ has_decision_making = d.get("decision_making", False)
871
+ has_direct_interaction = d.get("direct_interaction", False)
872
+ is_provider = any("Provider" in r for r in roles)
873
+ is_employment_sector = "Human Resources & Recruitment" in sectors
874
+ is_financial_sector = any(s in sectors for s in ["Banking & Financial Services", "Insurance"])
875
+ is_healthcare_sector = "Healthcare & Life Sciences" in sectors
876
+ is_education_sector = "Education & Training" in sectors
877
+ is_critical_sector = any(s in sectors for s in [
878
+ "Energy & Utilities", "Healthcare & Life Sciences", "Banking & Financial Services",
879
+ "Government & Public Administration", "Telecommunications", "Logistics & Supply Chain",
880
+ ])
881
+
882
+ # ── EU ──
883
+ company_base = d.get("company_base", "")
884
+ eu_countries = d.get("eu_countries", [])
885
+ EU_BASE_NAMES = [c.replace(" (EEA)", "") for c in EU_COUNTRIES]
886
+ has_eu = len(eu_countries) > 0 or company_base in EU_BASE_NAMES
887
+
888
+ if has_eu:
889
+ # AI-specific
890
+ detected_regs.append(("EU", "EU AI Act (Regulation 2024/1689)", "ai"))
891
+ if d.get("gpai", False):
892
+ detected_regs.append(("EU", "EU AI Act — GPAI Framework (Chapter V)", "ai"))
893
+ # Related
894
+ if has_personal_data:
895
+ detected_regs.append(("EU", "GDPR (Regulation 2016/679)", "other"))
896
+ if has_copyrighted:
897
+ detected_regs.append(("EU", "Copyright Directive (2019/790)", "other"))
898
+ if is_critical_sector:
899
+ detected_regs.append(("EU", "NIS2 Directive (2022/2555)", "other"))
900
+ if is_provider:
901
+ detected_regs.append(("EU", "Product Liability Directive (2024/2853)", "other"))
902
+ if has_decision_making:
903
+ detected_regs.append(("EU", "Equal Treatment Directives", "other"))
904
+ if has_direct_interaction:
905
+ detected_regs.append(("EU", "Consumer Rights Directive / GPSR", "other"))
906
+ detected_regs.append(("EU", "ePrivacy Directive (2002/58/EC)", "other"))
907
+ if is_healthcare_sector:
908
+ detected_regs.append(("EU", "Medical Device Regulation (MDR 2017/745)", "other"))
909
+
910
+ # ── US ──
911
+ has_us = "United States" in d.get("operating_countries", [])
912
+ us_regulated = d.get("us_states_with_regulation", [])
913
+
914
+ # Federal laws first
915
+ if has_us:
916
+ detected_regs.append(("US", "FTC Act Section 5 (Unfair/Deceptive Practices)", "other"))
917
+ if is_employment_sector or has_employment_data:
918
+ detected_regs.append(("US", "Title VII (Civil Rights Act)", "other"))
919
+ detected_regs.append(("US", "ADA (Americans with Disabilities Act)", "other"))
920
+ if is_financial_sector or has_financial_data:
921
+ detected_regs.append(("US", "ECOA (Equal Credit Opportunity Act)", "other"))
922
+ detected_regs.append(("US", "FCRA (Fair Credit Reporting Act)", "other"))
923
+ if "Construction & Real Estate" in sectors:
924
+ detected_regs.append(("US", "Fair Housing Act", "other"))
925
+ if is_healthcare_sector and has_personal_data:
926
+ detected_regs.append(("US", "HIPAA (Health Insurance Portability and Accountability Act)", "other"))
927
+ if has_children_data:
928
+ detected_regs.append(("US", "COPPA (Children's Online Privacy Protection Act)", "other"))
929
+ if is_education_sector and has_personal_data:
930
+ detected_regs.append(("US", "FERPA (Family Educational Rights and Privacy Act)", "other"))
931
+
932
+ # State-level laws
933
+ if has_personal_data and has_us:
934
+ detected_regs.append(("US", "State Data Protection Laws (CCPA, CTDPA, etc.)", "other"))
935
+ # AI-specific state laws
936
+ if "Colorado" in us_regulated:
937
+ detected_regs.append(("US", "Colorado AI Act (SB 24-205)", "ai"))
938
+ if "Connecticut" in us_regulated:
939
+ detected_regs.append(("US", "Connecticut AI Act (SB 2)", "ai"))
940
+ if "Texas" in us_regulated:
941
+ detected_regs.append(("US", "Texas TRAIGA (HB 1709)", "ai"))
942
+ if "Utah" in us_regulated:
943
+ detected_regs.append(("US", "Utah AI Policy Act (SB 149)", "ai"))
944
+ if "California" in us_regulated:
945
+ detected_regs.append(("US", "California CCPA / ADMT Regulations", "ai"))
946
+ if "Illinois" in us_regulated:
947
+ detected_regs.append(("US", "Illinois AI Video Interview Act (HB 3773)", "ai"))
948
+ if has_biometric:
949
+ detected_regs.append(("US", "Illinois BIPA (Biometric Information Privacy Act)", "other"))
950
+
951
+ # ── UAE ──
952
+ uae_emirates = d.get("uae_emirates", [])
953
+
954
+ if uae_emirates:
955
+ # Federal laws first
956
+ if has_personal_data:
957
+ detected_regs.append(("UAE", "UAE Federal PDPL (Decree-Law 45/2021)", "other"))
958
+ if has_copyrighted:
959
+ detected_regs.append(("UAE", "Copyright Law (Decree-Law 38/2021) — No TDM exception", "other"))
960
+ detected_regs.append(("UAE", "Cybercrime Law (Decree-Law 34/2021)", "other"))
961
+ detected_regs.append(("UAE", "Civil Transactions Law (Federal Law 5/1985)", "other"))
962
+ if has_direct_interaction:
963
+ detected_regs.append(("UAE", "Consumer Protection (Federal Law 15/2020)", "other"))
964
+ if has_decision_making or is_employment_sector or has_employment_data:
965
+ detected_regs.append(("UAE", "Anti-Discrimination (Decree-Law 34/2023)", "other"))
966
+ detected_regs.append(("UAE", "Labour Law (Decree-Law 33/2021)", "other"))
967
+
968
+ # Free zone level — only if specific free zone selected
969
+ uae_fz = d.get("uae_free_zones", [])
970
+ if "DIFC" in uae_fz and has_personal_data:
971
+ detected_regs.append(("UAE", "DIFC Data Protection Law + Regulation 10", "ai"))
972
+ if "ADGM" in uae_fz and has_personal_data:
973
+ detected_regs.append(("UAE", "ADGM Data Protection Regulations 2021", "other"))
974
+
975
+ # ── Build banner ──
976
+ ai_regs = [(t, n) for t, n, c in detected_regs if c == "ai"]
977
+ other_regs = [(t, n) for t, n, c in detected_regs if c == "other"]
978
+
979
+ if detected_regs:
980
+ banner_content = ""
981
+ if ai_regs:
982
+ banner_content += '<p class="reg-section-label-first rmh-el">AI-Specific Regulations</p>'
983
+ banner_content += '<ul class="reg-banner-list">'
984
+ for tag, name in ai_regs:
985
+ banner_content += f'<li><span class="reg-tag rmh-el">{tag}</span> {name}</li>'
986
+ banner_content += '</ul>'
987
+ if other_regs:
988
+ label_class = "reg-section-label" if ai_regs else "reg-section-label-first"
989
+ banner_content += f'<p class="{label_class} rmh-el">Related Regulations</p>'
990
+ banner_content += '<ul class="reg-banner-list">'
991
+ for tag, name in other_regs:
992
+ banner_content += f'<li><span class="reg-tag rmh-el">{tag}</span> {name}</li>'
993
+ banner_content += '</ul>'
994
+
995
+ banner_html = f"""
996
+ <div class="reg-banner">
997
+ <p class="reg-banner-title rmh-el">&#9878;&#65039; Based on your AI System ID Card, these regulations could apply:</p>
998
+ {banner_content}
999
+ <p class="reg-banner-cta rmh-el">Phase 2 will provide a detailed requirements checklist for each regulation.</p>
1000
+ </div>
1001
+ """
1002
+ st.markdown(banner_html, unsafe_allow_html=True)
1003
+ else:
1004
+ st.info("No specific AI regulations detected based on your inputs. This may change as more jurisdictions are added.")
1005
+
1006
+ # Navigation
1007
+ col1, col2, col3 = st.columns([1, 2, 2])
1008
+ with col1:
1009
+ if st.button("← Edit", use_container_width=True):
1010
+ go_to(1)
1011
+ st.rerun()
1012
+ with col3:
1013
+ if st.button("🤓 Deep dive into requirements — WIP", use_container_width=True, type="primary"):
1014
+ st.toast("Phase 2 — Requirements deep dive coming soon!", icon="🚀")