Sarolanda commited on
Commit
9686560
Β·
1 Parent(s): 4c640e2

adds lotties

Browse files
Files changed (2) hide show
  1. index.html +596 -613
  2. static/style.css +15 -16
index.html CHANGED
@@ -1,613 +1,596 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="utf-8"/>
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
6
- <title>PawMap</title>
7
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.min.css"/>
8
- <link rel="preconnect" href="https://fonts.googleapis.com"/>
9
- <link href="https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,400;0,14..32,500;0,14..32,600;0,14..32,700;1,14..32,400&display=swap" rel="stylesheet"/>
10
- <link rel="stylesheet" href="/static/style.css"/>
11
- <script src="https://cdn.jsdelivr.net/npm/lucide@0.454.0/dist/umd/lucide.min.js"></script>
12
- <script src="https://unpkg.com/@lottiefiles/lottie-player@2.0.8/dist/lottie-player.js"></script>
13
- </head>
14
- <body>
15
- <div id="desktop-layout">
16
-
17
- <!-- ══ LEFT SIDEBAR (desktop only) ══ -->
18
- <aside id="sidebar-left">
19
- <div class="sidebar-logo">
20
- <span class="sidebar-paw">🐾</span>
21
- <span class="sidebar-brand">PawMap</span>
22
- </div>
23
- <p class="sidebar-tagline">Collaborative stray animal mapping, powered by AI.</p>
24
-
25
- <div class="sidebar-divider"></div>
26
-
27
- <div class="sidebar-section">
28
- <h3>The problem</h3>
29
- <p>Independent rescuers in BrasΓ­lia, DF keep track of entire stray animal colonies in their heads β€” which ones were neutered, which one got hurt last winter, which one hasn't shown up this week. That knowledge lives in WhatsApp groups and fades when people move on.</p>
30
- <p>PawMap turns those scattered sightings into a shared, persistent map β€” one photo at a time.</p>
31
- </div>
32
-
33
- <div class="sidebar-divider"></div>
34
-
35
- <div class="sidebar-section">
36
- <h3>How it works</h3>
37
- <div class="how-steps">
38
- <div class="how-step">
39
- <div class="how-num"><i data-lucide="camera"></i></div>
40
- <div class="how-content">
41
- <strong>Snap a photo</strong>
42
- <span>Point your camera at any stray dog or cat.</span>
43
- </div>
44
- </div>
45
- <div class="how-connector"></div>
46
- <div class="how-step">
47
- <div class="how-num"><i data-lucide="scan-line"></i></div>
48
- <div class="how-content">
49
- <strong>AI identifies</strong>
50
- <span>Species, breed, color and condition β€” automatically.</span>
51
- </div>
52
- </div>
53
- <div class="how-connector"></div>
54
- <div class="how-step">
55
- <div class="how-num"><i data-lucide="git-merge"></i></div>
56
- <div class="how-content">
57
- <strong>Smart matching</strong>
58
- <span>Checks if this animal has been seen before.</span>
59
- </div>
60
- </div>
61
- <div class="how-connector"></div>
62
- <div class="how-step">
63
- <div class="how-num"><i data-lucide="map-pin"></i></div>
64
- <div class="how-content">
65
- <strong>Map updates</strong>
66
- <span>Sighting linked to the animal's profile β€” or a new one is created.</span>
67
- </div>
68
- </div>
69
- </div>
70
- </div>
71
-
72
- <div class="sidebar-divider"></div>
73
-
74
- <div class="sidebar-section sidebar-map-legend">
75
- <h3>Map legend</h3>
76
- <div class="legend-row"><span class="legend-dot green"></span> Dog β€” seen recently</div>
77
- <div class="legend-row"><span class="legend-dot orange"></span> Cat β€” seen recently</div>
78
- <div class="legend-row"><span class="legend-dot red"></span> Not seen in +30 days</div>
79
- </div>
80
- </aside>
81
-
82
- <div class="phone-wrap">
83
- <div class="better-on-mobile"><i data-lucide="smartphone"></i> Better on mobile</div>
84
- <div class="phone-frame">
85
- <div class="phone-btn phone-btn-vol-up"></div>
86
- <div class="phone-btn phone-btn-vol-down"></div>
87
- <div class="phone-btn phone-btn-power"></div>
88
- <div id="app">
89
-
90
- <!-- ══ ONBOARDING ══ -->
91
- <div id="screen-splash">
92
- <button id="ob-skip">Skip</button>
93
-
94
- <!-- Steps -->
95
- <div id="ob-track">
96
-
97
- <!-- Step 1 -->
98
- <div class="ob-step">
99
- <div class="ob-lottie-wrap">
100
- <!-- Replace src with your Lottie JSON when ready -->
101
- <lottie-player
102
- src="/static/lottie/step1.json"
103
- background="transparent" speed="1" loop autoplay
104
- class="ob-lottie">
105
- </lottie-player>
106
- <div class="ob-lottie-fallback"><i data-lucide="camera"></i></div>
107
- </div>
108
- <div class="ob-text">
109
- <div class="ob-step-label">Step 1 of 4</div>
110
- <h2>Help a stray find its way</h2>
111
- <p>Every street dog and cat deserves to be seen. PawMap lets your community map them together β€” one photo at a time.</p>
112
- </div>
113
- </div>
114
-
115
- <!-- Step 2 -->
116
- <div class="ob-step">
117
- <div class="ob-lottie-wrap">
118
- <lottie-player
119
- src="/static/lottie/step2.json"
120
- background="transparent" speed="1" loop autoplay
121
- class="ob-lottie">
122
- </lottie-player>
123
- <div class="ob-lottie-fallback"><i data-lucide="scan-line"></i></div>
124
- </div>
125
- <div class="ob-text">
126
- <div class="ob-step-label">Step 2 of 4</div>
127
- <h2>Snap a photo β€” AI does the rest</h2>
128
- <p>Point your camera and tap. Our AI instantly identifies species, breed, color, and condition. No forms to fill.</p>
129
- </div>
130
- </div>
131
-
132
- <!-- Step 3 -->
133
- <div class="ob-step">
134
- <div class="ob-lottie-wrap">
135
- <lottie-player
136
- src="/static/lottie/step3.json"
137
- background="transparent" speed="1" loop autoplay
138
- class="ob-lottie">
139
- </lottie-player>
140
- <div class="ob-lottie-fallback"><i data-lucide="refresh-cw"></i></div>
141
- </div>
142
- <div class="ob-text">
143
- <div class="ob-step-label">Step 3 of 4</div>
144
- <h2>AI matches the same animal</h2>
145
- <p>Seen this dog before? The AI compares your photo with previous sightings and links them automatically β€” building each animal's story.</p>
146
- </div>
147
- </div>
148
-
149
- <!-- Step 4 -->
150
- <div class="ob-step">
151
- <div class="ob-lottie-wrap">
152
- <lottie-player
153
- src="/static/lottie/step4.json"
154
- background="transparent" speed="1" loop autoplay
155
- class="ob-lottie">
156
- </lottie-player>
157
- <div class="ob-lottie-fallback"><i data-lucide="home"></i></div>
158
- </div>
159
- <div class="ob-text">
160
- <div class="ob-step-label">Step 4 of 4</div>
161
- <h2>Every map pin is a chance</h2>
162
- <p>Mapping a dog brings more chances to find a forever family β€” or get help from the community when it's needed most.</p>
163
- </div>
164
- </div>
165
-
166
- </div><!-- /ob-track -->
167
-
168
- <!-- Dots -->
169
- <div id="ob-dots">
170
- <span class="ob-dot active"></span>
171
- <span class="ob-dot"></span>
172
- <span class="ob-dot"></span>
173
- <span class="ob-dot"></span>
174
- </div>
175
-
176
- <!-- Actions -->
177
- <div id="ob-actions">
178
- <button id="ob-next">Next β†’</button>
179
- </div>
180
-
181
- </div><!-- /screen-splash -->
182
-
183
- <!-- ══ HEADER ══ -->
184
- <header id="header">
185
- <button class="icon-btn" aria-label="Menu">
186
- <svg viewBox="0 0 24 24"><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/></svg>
187
- </button>
188
- <span class="logo">PawMap</span>
189
- <button class="icon-btn" aria-label="Options">
190
- <svg viewBox="0 0 24 24"><circle cx="12" cy="5" r="1.5" fill="#fff" stroke="none"/><circle cx="12" cy="12" r="1.5" fill="#fff" stroke="none"/><circle cx="12" cy="19" r="1.5" fill="#fff" stroke="none"/></svg>
191
- </button>
192
- </header>
193
-
194
- <!-- ══ FILTER ROW ══ -->
195
- <div id="filter-row">
196
- <button class="chip active" data-species="all">All</button>
197
- <button class="chip" data-species="dog"><i data-lucide="dog"></i> Dogs</button>
198
- <button class="chip" data-species="cat"><i data-lucide="cat"></i> Cats</button>
199
- <button class="chip" data-timeframe="today">Today</button>
200
- <button class="chip" data-timeframe="week">This Week</button>
201
- </div>
202
-
203
- <!-- ══ SCREEN: MAP ══ -->
204
- <div id="screen-map" class="screen active">
205
- <div id="map"></div>
206
- <div id="map-empty">No sightings yet</div>
207
- <div id="sighting-card" class="hidden">
208
- <div class="card-photo" id="card-photo"><i data-lucide="paw-print"></i></div>
209
- <div class="card-info">
210
- <div class="card-top"><span class="badge" id="card-badge">Dog</span><span class="card-time" id="card-time"></span></div>
211
- <div class="card-location" id="card-location"></div>
212
- <div class="card-sub" id="card-sub"></div>
213
- </div>
214
- <button class="btn-ficha" id="card-btn">View profile</button>
215
- </div>
216
- </div>
217
-
218
- <!-- ══ SCREEN: REGISTER ══ -->
219
- <div id="screen-register" class="screen">
220
- <input type="file" id="photo-input" accept="image/*" capture="environment"/>
221
- <div id="viewfinder">
222
- <div id="gps-pill">
223
- <svg viewBox="0 0 24 24"><path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7z"/><circle cx="12" cy="9" r="2.5"/></svg>
224
- <span id="gps-text">Tap to detect location</span>
225
- </div>
226
- <img id="photo-preview" src="" alt="photo"/>
227
- <div id="camera-placeholder">
228
- <svg viewBox="0 0 24 24"><path d="M23 19a2 2 0 01-2 2H3a2 2 0 01-2-2V8a2 2 0 012-2h4l2-3h6l2 3h4a2 2 0 012 2z"/><circle cx="12" cy="13" r="4"/></svg>
229
- <p>Tap the button to take a photo</p>
230
- </div>
231
- <button id="shutter-btn" aria-label="Take photo">
232
- <svg viewBox="0 0 24 24"><path d="M23 19a2 2 0 01-2 2H3a2 2 0 01-2-2V8a2 2 0 012-2h4l2-3h6l2 3h4a2 2 0 012 2z"/><circle cx="12" cy="13" r="4"/></svg>
233
- </button>
234
- </div>
235
- <div id="reg-sheet">
236
- <div class="drag-handle"></div>
237
- <div id="notes-row">
238
- <svg viewBox="0 0 24 24"><path d="M11 4H4a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 013 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>
239
- <textarea id="notes-input" rows="1" placeholder="Add a note (optional)"></textarea>
240
- </div>
241
- <button class="btn-secondary" id="gps-btn">
242
- <svg viewBox="0 0 24 24"><path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7z"/><circle cx="12" cy="9" r="2.5"/></svg>
243
- Add Location
244
- </button>
245
- <button class="btn-primary" id="submit-reg-btn" disabled>
246
- <svg viewBox="0 0 24 24"><path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7z"/><circle cx="12" cy="9" r="2.5"/></svg>
247
- Report Sighting
248
- </button>
249
- </div>
250
- </div>
251
-
252
- <!-- ══ SCREEN: ANALYSIS ══ -->
253
- <div id="screen-analysis" class="screen">
254
- <div class="flow-header">
255
- <button class="back-btn" id="analysis-back"><svg viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"/></svg></button>
256
- <h2>Analyzing Sighting</h2>
257
- </div>
258
- <div id="analysis-photo-wrap">
259
- <img id="analysis-photo" src="" alt="photo"/>
260
- <div id="ai-badge"><span class="dot"></span><span id="ai-badge-text">AI Analyzing...</span></div>
261
- <div id="animal-result-badge"><svg viewBox="0 0 24 24"><path d="M20 6L9 17l-5-5"/></svg><span id="result-badge-text">Caramel Dog</span></div>
262
- </div>
263
- <div class="analysis-card">
264
- <h3>Identified Details <button id="edit-btn"><i data-lucide="pencil"></i></button></h3>
265
- <p class="subtitle">AI filled in these details. Correct if needed.</p>
266
- <div class="dropdowns-grid">
267
- <div class="dropdown-field"><label>Species</label><select id="sel-species"><option value="dog">Dog</option><option value="cat">Cat</option></select></div>
268
- <div class="dropdown-field"><label>Breed (Estimated)</label><select id="sel-breed"></select></div>
269
- <div class="dropdown-field"><label>Primary Color</label><select id="sel-color"><option value="Caramelo">Caramel</option><option value="Preto">Black</option><option value="Branco">White</option><option value="Cinza">Gray</option><option value="Marrom">Brown</option><option value="Mesclado">Mixed</option></select></div>
270
- <div class="dropdown-field"><label>Size</label><select id="sel-size"><option value="Pequeno">Small</option><option value="MΓ©dio">Medium</option><option value="Grande">Large</option></select></div>
271
- </div>
272
- <div class="condition-label">Condition (Optional)</div>
273
- <div class="condition-chips">
274
- <button class="cond-chip" data-val="Injured"><span class="check"><i data-lucide="check"></i></span> Injured</button>
275
- <button class="cond-chip" data-val="Looks Healthy"><span class="check"><i data-lucide="check"></i></span> Looks Healthy</button>
276
- <button class="cond-chip" data-val="Has Collar"><span class="check"><i data-lucide="check"></i></span> Has Collar</button>
277
- </div>
278
- </div>
279
- <div class="analysis-card" id="similar-section">
280
- <h3>Similar animals nearby</h3>
281
- <p class="subtitle">Found by AI for context β€” recognition is automatic.</p>
282
- <div class="similar-scroll" id="similar-scroll"></div>
283
- </div>
284
- <div class="analysis-card">
285
- <div class="dropdown-field">
286
- <label>Name <span class="optional-tag">optional</span></label>
287
- <input type="text" id="animal-name-input" placeholder="e.g. Caputino, Bolinha, Luna…" maxlength="40" autocomplete="off"/>
288
- </div>
289
- </div>
290
- <div id="analysis-actions">
291
- <button class="btn-primary" id="confirm-btn">Confirm & Report β†’</button>
292
- <button class="btn-outline" id="discard-btn">Discard Photo</button>
293
- </div>
294
- </div>
295
-
296
- <!-- ══ SCREEN: CONFIRM ══ -->
297
- <div id="screen-confirm" class="screen">
298
- <div id="confirm-inner">
299
- <div id="confirm-photo-wrap">
300
- <div id="confirm-photo"><i data-lucide="paw-print"></i></div>
301
- <div id="confirm-check"><svg viewBox="0 0 24 24"><polyline points="20 6 9 20 4 14"/></svg></div>
302
- </div>
303
- <div id="confirm-title">Sighting<br>Reported!</div>
304
- <div id="confirm-card">
305
- <div class="confirm-row">
306
- <span class="confirm-row-label"><svg viewBox="0 0 24 24"><path d="M20 21v-2a4 4 0 00-4-4H8a4 4 0 00-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>Animal:</span>
307
- <span class="confirm-row-value" id="confirm-animal">β€”</span>
308
- </div>
309
- <div class="confirm-row">
310
- <span class="confirm-row-label"><svg viewBox="0 0 24 24"><path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7z"/><circle cx="12" cy="9" r="2.5"/></svg>Location:</span>
311
- <span class="confirm-row-value" id="confirm-local">β€”</span>
312
- </div>
313
- <div class="confirm-row">
314
- <span class="confirm-row-label"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>Time:</span>
315
- <span class="confirm-row-value" id="confirm-hora">β€”</span>
316
- </div>
317
- </div>
318
- <div id="confirm-id-grid" class="confirm-id-grid"></div>
319
- <div class="confirm-btns">
320
- <button class="btn-primary" id="register-another-btn"><svg viewBox="0 0 24 24"><path d="M23 19a2 2 0 01-2 2H3a2 2 0 01-2-2V8a2 2 0 012-2h4l2-3h6l2 3h4a2 2 0 012 2z"/><circle cx="12" cy="13" r="4"/></svg>Report Another</button>
321
- <button class="btn-secondary" id="go-map-btn"><svg viewBox="0 0 24 24"><polygon points="3 6 9 3 15 6 21 3 21 18 15 21 9 18 3 21"/></svg>View on Map</button>
322
- </div>
323
- </div>
324
- </div>
325
-
326
- <!-- ══ SCREEN: SIGHTINGS LIST ══ -->
327
- <div id="screen-sightings" class="screen">
328
- <div id="sightings-header">
329
- <span id="sightings-count">Loading...</span>
330
- <button id="refresh-btn" title="Refresh"><i data-lucide="refresh-cw"></i></button>
331
- </div>
332
- <div id="animals-list"></div>
333
- </div>
334
-
335
- <!-- ══ SCREEN: PROFILE / FICHA ══ -->
336
- <div id="screen-profile" class="screen">
337
- <div class="profile-scroll">
338
- <!-- Hero -->
339
- <div class="profile-hero">
340
- <img class="profile-hero-img" id="profile-hero-img" src="" alt=""/>
341
- <div class="hero-gradient"></div>
342
- <div class="hero-top-btns">
343
- <button class="hero-btn" id="profile-back-btn"><svg viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"/></svg></button>
344
- <button class="hero-btn" id="profile-share-btn"><svg viewBox="0 0 24 24"><circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/><line x1="8.59" y1="13.51" x2="15.42" y2="17.49"/><line x1="15.41" y1="6.51" x2="8.59" y2="10.49"/></svg></button>
345
- </div>
346
- <div id="profile-sightings-badge">
347
- <svg viewBox="0 0 24 24"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
348
- <span id="profile-badge-text">0 sightings</span>
349
- </div>
350
- </div>
351
-
352
- <!-- Content -->
353
- <div class="profile-content">
354
- <h1 id="profile-title">...</h1>
355
- <div id="profile-status"><span class="status-dot"></span><span id="profile-status-text">...</span></div>
356
- <div id="profile-helped-banner" style="display:none">
357
- <i data-lucide="heart"></i>
358
- <span>This animal has been helped by the community</span>
359
- </div>
360
- <div id="profile-ai-desc" class="ai-desc-callout" style="display:none">
361
- <span class="ai-label">AI</span>
362
- <p id="profile-ai-text"></p>
363
- </div>
364
-
365
- <!-- Identification -->
366
- <div class="profile-section">
367
- <div class="section-title">Identification</div>
368
- <div class="id-grid">
369
- <div class="id-cell"><div class="id-cell-label">Species</div><div class="id-cell-value" id="prof-species">β€”</div></div>
370
- <div class="id-cell"><div class="id-cell-label">Breed</div><div class="id-cell-value" id="prof-breed">β€”</div></div>
371
- <div class="id-cell"><div class="id-cell-label">Size</div><div class="id-cell-value" id="prof-size">β€”</div></div>
372
- <div class="id-cell"><div class="id-cell-label">Color</div><div class="id-cell-value" id="prof-color">β€”</div></div>
373
- </div>
374
- </div>
375
-
376
- <!-- Gallery -->
377
- <div class="profile-section">
378
- <div class="gallery-header">
379
- <div class="section-title" style="margin-bottom:0">Sightings Gallery</div>
380
- <button class="gallery-see-all">See all</button>
381
- </div>
382
- <div class="gallery-scroll" id="profile-gallery"></div>
383
- </div>
384
-
385
- <!-- Community Help -->
386
- <div class="profile-section" id="profile-help-section" style="display:none">
387
- <div class="section-title help-section-title">
388
- <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="var(--green)" stroke-width="2"><path d="M20.84 4.61a5.5 5.5 0 00-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 00-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 000-7.78z"/></svg>
389
- Community Help
390
- </div>
391
- <div id="profile-help-list"></div>
392
- </div>
393
-
394
- <!-- Trajectory -->
395
- <div class="profile-section">
396
- <div class="section-title">Recent Path</div>
397
- <div id="trajectory-map"></div>
398
- </div>
399
- </div>
400
- </div>
401
-
402
- <!-- Footer -->
403
- <div class="profile-footer">
404
- <button class="btn-adopt" id="btn-adopt"><i data-lucide="heart"></i> I Want to Adopt / Help</button>
405
- </div>
406
- </div>
407
-
408
- <!-- ══ HELP BOTTOM SHEET ══ -->
409
- <div id="help-overlay" class="hidden"></div>
410
- <div id="help-sheet" class="help-sheet hidden">
411
- <div class="drag-handle"></div>
412
- <div id="help-animal-row">
413
- <div id="help-animal-photo"></div>
414
- <div id="help-animal-info">
415
- <div id="help-animal-name">β€”</div>
416
- <div id="help-animal-sub">β€”</div>
417
- </div>
418
- </div>
419
- <div class="help-actions">
420
- <button class="help-action-btn" id="help-share">
421
- <span class="help-action-icon"><i data-lucide="share-2"></i></span>
422
- <span class="help-action-text">
423
- <strong>Share this animal</strong>
424
- <small>Send via WhatsApp or copy link</small>
425
- </span>
426
- <svg class="help-chevron" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg>
427
- </button>
428
- <button class="help-action-btn" id="help-directions">
429
- <span class="help-action-icon"><i data-lucide="map-pin"></i></span>
430
- <span class="help-action-text">
431
- <strong>Get directions</strong>
432
- <small>Navigate to last known location</small>
433
- </span>
434
- <svg class="help-chevron" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg>
435
- </button>
436
- <button class="help-action-btn" id="help-helped">
437
- <span class="help-action-icon"><i data-lucide="check-circle"></i></span>
438
- <span class="help-action-text">
439
- <strong>I helped this animal</strong>
440
- <small>Log that you fed, treated, or fostered them</small>
441
- </span>
442
- <svg class="help-chevron" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg>
443
- </button>
444
- </div>
445
- <button class="help-cancel" id="help-cancel">Cancel</button>
446
- </div>
447
-
448
- <!-- ══ HELPED CONFIRM ══ -->
449
- <div id="helped-confirm" class="helped-confirm hidden">
450
- <div class="helped-inner">
451
- <div class="helped-icon-wrap"><i data-lucide="heart"></i></div>
452
- <h3>Thank you</h3>
453
- <p>Your help has been recorded.<br>Every act of care makes a difference.</p>
454
- <button id="helped-ok">Done</button>
455
- </div>
456
- </div>
457
-
458
- <!-- ══ SCREEN: HELP PROOF ══ -->
459
- <div id="screen-help-proof" class="screen">
460
- <div class="flow-header">
461
- <button class="back-btn" id="help-proof-back"><svg viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"/></svg></button>
462
- <h2>I Helped This Animal</h2>
463
- </div>
464
- <div id="help-proof-animal-row">
465
- <div id="help-proof-animal-photo" class="help-proof-thumb"></div>
466
- <div>
467
- <div id="help-proof-animal-name" class="help-proof-animal-name">β€”</div>
468
- <div id="help-proof-animal-sub" class="help-proof-animal-sub">β€”</div>
469
- </div>
470
- </div>
471
- <div class="help-proof-section">
472
- <div class="help-proof-label">What did you do?</div>
473
- <div class="help-type-chips" id="help-type-chips">
474
- <button class="help-type-chip active" data-type="fed"><i data-lucide="utensils"></i> Fed</button>
475
- <button class="help-type-chip" data-type="vet"><i data-lucide="stethoscope"></i> Took to vet</button>
476
- <button class="help-type-chip" data-type="adopted"><i data-lucide="home"></i> Adopted</button>
477
- <button class="help-type-chip" data-type="rescued"><i data-lucide="shield-check"></i> Rescued</button>
478
- <button class="help-type-chip" data-type="other"><i data-lucide="heart"></i> Other</button>
479
- </div>
480
- </div>
481
- <div class="help-proof-section">
482
- <div class="help-proof-label">Add a photo <span class="optional-tag">optional</span></div>
483
- <div id="help-proof-viewfinder" class="help-proof-viewfinder">
484
- <input type="file" id="help-proof-input" accept="image/*" capture="environment"/>
485
- <img id="help-proof-preview" src="" alt="" class="hidden"/>
486
- <div id="help-proof-placeholder">
487
- <svg viewBox="0 0 24 24"><path d="M23 19a2 2 0 01-2 2H3a2 2 0 01-2-2V8a2 2 0 012-2h4l2-3h6l2 3h4a2 2 0 012 2z"/><circle cx="12" cy="13" r="4"/></svg>
488
- <span>Tap to add photo proof</span>
489
- </div>
490
- <div id="help-proof-ai-badge" class="help-proof-ai-badge hidden">
491
- <span class="dot"></span><span id="help-proof-ai-text">Verifying with AI…</span>
492
- </div>
493
- <div id="help-proof-verified-badge" class="help-proof-verified-badge hidden">
494
- <svg viewBox="0 0 24 24"><path d="M20 6L9 17l-5-5"/></svg>
495
- <span id="help-proof-verified-text">Same animal confirmed</span>
496
- </div>
497
- </div>
498
- </div>
499
- <div class="help-proof-section">
500
- <div class="help-proof-label">Tell the story <span class="optional-tag">optional</span></div>
501
- <textarea id="help-proof-notes" rows="3" placeholder="What happened? Where did you take them?"></textarea>
502
- </div>
503
- <button class="btn-primary help-proof-submit" id="help-proof-submit">
504
- <svg viewBox="0 0 24 24"><path d="M20.84 4.61a5.5 5.5 0 00-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 00-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 000-7.78z"/></svg>
505
- Register Help
506
- </button>
507
- </div>
508
-
509
- <!-- ══ PHOTO LIGHTBOX ══ -->
510
- <div id="photo-lightbox" class="photo-lightbox hidden" role="dialog">
511
- <div class="lightbox-backdrop"></div>
512
- <div class="lightbox-inner">
513
- <img id="lightbox-img" src="" alt="photo" />
514
- <button id="lightbox-close" aria-label="Close"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg></button>
515
- </div>
516
- </div>
517
-
518
- <!-- ══ PHOTO CONTEXT MENU ══ -->
519
- <div id="photo-menu" class="photo-menu hidden">
520
- <button id="photo-menu-download">
521
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
522
- Download photo
523
- </button>
524
- </div>
525
-
526
- <!-- ══ BOTTOM NAV ══ -->
527
- <nav id="bottom-nav">
528
- <button class="nav-btn active" data-screen="map"><span class="nav-icon"><i data-lucide="map"></i></span><span>Map</span></button>
529
- <button class="nav-btn" data-screen="register"><span class="nav-icon"><i data-lucide="camera"></i></span><span>Report</span></button>
530
- <button class="nav-btn" data-screen="sightings"><span class="nav-icon"><i data-lucide="eye"></i></span><span>Sightings</span></button>
531
- </nav>
532
- </div><!-- /#app -->
533
- </div><!-- /.phone-frame -->
534
- </div><!-- /.phone-wrap -->
535
-
536
- <!-- ══ RIGHT SIDEBAR (desktop only) ══ -->
537
- <aside id="sidebar-right">
538
- <div class="sidebar-section">
539
- <div class="sidebar-badge-title">Built for</div>
540
- <a class="hackathon-badge" href="https://huggingface.co/build-small-hackathon" target="_blank" rel="noopener">
541
- <span class="hb-icon">🏑</span>
542
- <span class="hb-text">
543
- <strong>Build Small Hackathon 2026</strong>
544
- <small>Backyard AI track Β· Hugging Face</small>
545
- </span>
546
- </a>
547
- </div>
548
-
549
- <div class="sidebar-divider"></div>
550
-
551
- <div class="sidebar-section">
552
- <h3>Tech stack</h3>
553
- <div class="stack-list">
554
- <div class="stack-row">
555
- <span class="stack-label">Vision AI</span>
556
- <span class="stack-value">Nemotron Omni 30B</span>
557
- </div>
558
- <div class="stack-row">
559
- <span class="stack-label">Embeddings</span>
560
- <span class="stack-value">all-MiniLM-L6-v2</span>
561
- </div>
562
- <div class="stack-row">
563
- <span class="stack-label">Matching</span>
564
- <span class="stack-value">Cosine similarity</span>
565
- </div>
566
- <div class="stack-row">
567
- <span class="stack-label">Map</span>
568
- <span class="stack-value">Leaflet.js + OSM</span>
569
- </div>
570
- <div class="stack-row">
571
- <span class="stack-label">Frontend</span>
572
- <span class="stack-value">gr.Server SPA</span>
573
- </div>
574
- <div class="stack-row">
575
- <span class="stack-label">Storage</span>
576
- <span class="stack-value">SQLite</span>
577
- </div>
578
- </div>
579
- </div>
580
-
581
- <div class="sidebar-divider"></div>
582
-
583
- <div class="sidebar-section">
584
- <h3>Help an animal today</h3>
585
- <p>Open a profile from the map, tap <strong>I Want to Help</strong>, and log what you did β€” fed, treated, fostered. A photo proves it; the AI verifies it's the same animal.</p>
586
- </div>
587
-
588
- <div class="sidebar-divider"></div>
589
-
590
- <div class="sidebar-section sidebar-links">
591
- <a href="https://huggingface.co/spaces/build-small-hackathon/viralata-mapper" target="_blank" rel="noopener">
592
- <svg viewBox="0 0 24 24"><path d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"/></svg>
593
- View on Hugging Face
594
- </a>
595
- <a href="https://huggingface.co/blog/build-small-hackathon/pawmap" target="_blank" rel="noopener">
596
- <svg viewBox="0 0 24 24"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>
597
- Field Notes
598
- </a>
599
- </div>
600
-
601
- <div class="sidebar-footer">
602
- Made in BrasΓ­lia, DF πŸ‡§πŸ‡·<br>
603
- <span>for every stray that deserves to be seen</span>
604
- </div>
605
- </aside>
606
-
607
- </div><!-- /desktop-layout -->
608
-
609
- <script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.min.js"></script>
610
- <script src="/static/app.js"></script>
611
- <script src="/static/help-proof.js"></script>
612
- </body>
613
- </html>
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8"/>
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
6
+ <title>PawMap</title>
7
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.min.css"/>
8
+ <link rel="preconnect" href="https://fonts.googleapis.com"/>
9
+ <link href="https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,400;0,14..32,500;0,14..32,600;0,14..32,700;1,14..32,400&display=swap" rel="stylesheet"/>
10
+ <link rel="stylesheet" href="/static/style.css"/>
11
+ <script src="https://cdn.jsdelivr.net/npm/lucide@0.454.0/dist/umd/lucide.min.js"></script>
12
+ <script src="https://unpkg.com/@lottiefiles/lottie-player@2.0.8/dist/lottie-player.js"></script>
13
+ </head>
14
+ <body>
15
+ <div id="desktop-layout">
16
+
17
+ <!-- ══ LEFT SIDEBAR (desktop only) ══ -->
18
+ <aside id="sidebar-left">
19
+ <div class="sidebar-logo">
20
+ <span class="sidebar-paw">🐾</span>
21
+ <span class="sidebar-brand">PawMap</span>
22
+ </div>
23
+ <p class="sidebar-tagline">Collaborative stray animal mapping, powered by AI.</p>
24
+
25
+ <div class="sidebar-divider"></div>
26
+
27
+ <div class="sidebar-section">
28
+ <h3>The problem</h3>
29
+ <p>Independent rescuers in BrasΓ­lia, DF keep track of entire stray animal colonies in their heads β€” which ones were neutered, which one got hurt last winter, which one hasn't shown up this week. That knowledge lives in WhatsApp groups and fades when people move on.</p>
30
+ <p>PawMap turns those scattered sightings into a shared, persistent map β€” one photo at a time.</p>
31
+ </div>
32
+
33
+ <div class="sidebar-divider"></div>
34
+
35
+ <div class="sidebar-section">
36
+ <h3>How it works</h3>
37
+ <div class="how-steps">
38
+ <div class="how-step">
39
+ <div class="how-num"><i data-lucide="camera"></i></div>
40
+ <div class="how-content">
41
+ <strong>Snap a photo</strong>
42
+ <span>Point your camera at any stray dog or cat.</span>
43
+ </div>
44
+ </div>
45
+ <div class="how-connector"></div>
46
+ <div class="how-step">
47
+ <div class="how-num"><i data-lucide="scan-line"></i></div>
48
+ <div class="how-content">
49
+ <strong>AI identifies</strong>
50
+ <span>Species, breed, color and condition β€” automatically.</span>
51
+ </div>
52
+ </div>
53
+ <div class="how-connector"></div>
54
+ <div class="how-step">
55
+ <div class="how-num"><i data-lucide="git-merge"></i></div>
56
+ <div class="how-content">
57
+ <strong>Smart matching</strong>
58
+ <span>Checks if this animal has been seen before.</span>
59
+ </div>
60
+ </div>
61
+ <div class="how-connector"></div>
62
+ <div class="how-step">
63
+ <div class="how-num"><i data-lucide="map-pin"></i></div>
64
+ <div class="how-content">
65
+ <strong>Map updates</strong>
66
+ <span>Sighting linked to the animal's profile β€” or a new one is created.</span>
67
+ </div>
68
+ </div>
69
+ </div>
70
+ </div>
71
+
72
+ <div class="sidebar-divider"></div>
73
+
74
+ <div class="sidebar-section sidebar-map-legend">
75
+ <h3>Map legend</h3>
76
+ <div class="legend-row"><span class="legend-dot green"></span> Dog β€” seen recently</div>
77
+ <div class="legend-row"><span class="legend-dot orange"></span> Cat β€” seen recently</div>
78
+ <div class="legend-row"><span class="legend-dot red"></span> Not seen in +30 days</div>
79
+ </div>
80
+ </aside>
81
+
82
+ <div class="phone-wrap">
83
+ <div class="better-on-mobile"><i data-lucide="smartphone"></i> Better on mobile</div>
84
+ <div class="phone-frame">
85
+ <div class="phone-btn phone-btn-vol-up"></div>
86
+ <div class="phone-btn phone-btn-vol-down"></div>
87
+ <div class="phone-btn phone-btn-power"></div>
88
+ <div id="app">
89
+
90
+ <!-- ══ ONBOARDING ══ -->
91
+ <div id="screen-splash">
92
+ <button id="ob-skip">Skip</button>
93
+
94
+ <!-- Steps -->
95
+ <div id="ob-track">
96
+
97
+ <!-- Step 1 -->
98
+ <div class="ob-step">
99
+ <div class="ob-lottie-wrap">
100
+ <lottie-player src="/static/lottie/step1.json" background="transparent" speed="1" loop autoplay class="ob-lottie"></lottie-player>
101
+ <div class="ob-lottie-fallback"><i data-lucide="camera"></i></div>
102
+ </div>
103
+ <div class="ob-text">
104
+ <div class="ob-step-label">Step 1 of 4</div>
105
+ <h2>Help a stray find its way</h2>
106
+ <p>Every street dog and cat deserves to be seen. PawMap lets your community map them together β€” one photo at a time.</p>
107
+ </div>
108
+ </div>
109
+
110
+ <!-- Step 2 -->
111
+ <div class="ob-step">
112
+ <div class="ob-lottie-wrap">
113
+ <lottie-player src="/static/lottie/step2.json" background="transparent" speed="1" loop autoplay class="ob-lottie"></lottie-player>
114
+ <div class="ob-lottie-fallback"><i data-lucide="scan-line"></i></div>
115
+ </div>
116
+ <div class="ob-text">
117
+ <div class="ob-step-label">Step 2 of 4</div>
118
+ <h2>Snap a photo β€” AI does the rest</h2>
119
+ <p>Point your camera and tap. Our AI instantly identifies species, breed, color, and condition. No forms to fill.</p>
120
+ </div>
121
+ </div>
122
+
123
+ <!-- Step 3 -->
124
+ <div class="ob-step">
125
+ <div class="ob-lottie-wrap">
126
+ <lottie-player src="/static/lottie/step3.json" background="transparent" speed="1" loop autoplay class="ob-lottie"></lottie-player>
127
+ <div class="ob-lottie-fallback"><i data-lucide="refresh-cw"></i></div>
128
+ </div>
129
+ <div class="ob-text">
130
+ <div class="ob-step-label">Step 3 of 4</div>
131
+ <h2>AI matches the same animal</h2>
132
+ <p>Seen this dog before? The AI compares your photo with previous sightings and links them automatically β€” building each animal's story.</p>
133
+ </div>
134
+ </div>
135
+
136
+ <!-- Step 4 -->
137
+ <div class="ob-step">
138
+ <div class="ob-lottie-wrap">
139
+ <lottie-player src="/static/lottie/step4.json" background="transparent" speed="1" loop autoplay class="ob-lottie"></lottie-player>
140
+ <div class="ob-lottie-fallback"><i data-lucide="home"></i></div>
141
+ </div>
142
+ <div class="ob-text">
143
+ <div class="ob-step-label">Step 4 of 4</div>
144
+ <h2>Every map pin is a chance</h2>
145
+ <p>Mapping a dog brings more chances to find a forever family β€” or get help from the community when it's needed most.</p>
146
+ </div>
147
+ </div>
148
+
149
+ </div><!-- /ob-track -->
150
+
151
+ <!-- Dots -->
152
+ <div id="ob-dots">
153
+ <span class="ob-dot active"></span>
154
+ <span class="ob-dot"></span>
155
+ <span class="ob-dot"></span>
156
+ <span class="ob-dot"></span>
157
+ </div>
158
+
159
+ <!-- Actions -->
160
+ <div id="ob-actions">
161
+ <button id="ob-next">Next β†’</button>
162
+ </div>
163
+
164
+ </div><!-- /screen-splash -->
165
+
166
+ <!-- ══ HEADER ══ -->
167
+ <header id="header">
168
+ <button class="icon-btn" aria-label="Menu">
169
+ <svg viewBox="0 0 24 24"><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/></svg>
170
+ </button>
171
+ <span class="logo">PawMap</span>
172
+ <button class="icon-btn" aria-label="Options">
173
+ <svg viewBox="0 0 24 24"><circle cx="12" cy="5" r="1.5" fill="#fff" stroke="none"/><circle cx="12" cy="12" r="1.5" fill="#fff" stroke="none"/><circle cx="12" cy="19" r="1.5" fill="#fff" stroke="none"/></svg>
174
+ </button>
175
+ </header>
176
+
177
+ <!-- ══ FILTER ROW ══ -->
178
+ <div id="filter-row">
179
+ <button class="chip active" data-species="all">All</button>
180
+ <button class="chip" data-species="dog"><i data-lucide="dog"></i> Dogs</button>
181
+ <button class="chip" data-species="cat"><i data-lucide="cat"></i> Cats</button>
182
+ <button class="chip" data-timeframe="today">Today</button>
183
+ <button class="chip" data-timeframe="week">This Week</button>
184
+ </div>
185
+
186
+ <!-- ══ SCREEN: MAP ══ -->
187
+ <div id="screen-map" class="screen active">
188
+ <div id="map"></div>
189
+ <div id="map-empty">No sightings yet</div>
190
+ <div id="sighting-card" class="hidden">
191
+ <div class="card-photo" id="card-photo"><i data-lucide="paw-print"></i></div>
192
+ <div class="card-info">
193
+ <div class="card-top"><span class="badge" id="card-badge">Dog</span><span class="card-time" id="card-time"></span></div>
194
+ <div class="card-location" id="card-location"></div>
195
+ <div class="card-sub" id="card-sub"></div>
196
+ </div>
197
+ <button class="btn-ficha" id="card-btn">View profile</button>
198
+ </div>
199
+ </div>
200
+
201
+ <!-- ══ SCREEN: REGISTER ══ -->
202
+ <div id="screen-register" class="screen">
203
+ <input type="file" id="photo-input" accept="image/*" capture="environment"/>
204
+ <div id="viewfinder">
205
+ <div id="gps-pill">
206
+ <svg viewBox="0 0 24 24"><path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7z"/><circle cx="12" cy="9" r="2.5"/></svg>
207
+ <span id="gps-text">Tap to detect location</span>
208
+ </div>
209
+ <img id="photo-preview" src="" alt="photo"/>
210
+ <div id="camera-placeholder">
211
+ <svg viewBox="0 0 24 24"><path d="M23 19a2 2 0 01-2 2H3a2 2 0 01-2-2V8a2 2 0 012-2h4l2-3h6l2 3h4a2 2 0 012 2z"/><circle cx="12" cy="13" r="4"/></svg>
212
+ <p>Tap the button to take a photo</p>
213
+ </div>
214
+ <button id="shutter-btn" aria-label="Take photo">
215
+ <svg viewBox="0 0 24 24"><path d="M23 19a2 2 0 01-2 2H3a2 2 0 01-2-2V8a2 2 0 012-2h4l2-3h6l2 3h4a2 2 0 012 2z"/><circle cx="12" cy="13" r="4"/></svg>
216
+ </button>
217
+ </div>
218
+ <div id="reg-sheet">
219
+ <div class="drag-handle"></div>
220
+ <div id="notes-row">
221
+ <svg viewBox="0 0 24 24"><path d="M11 4H4a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 013 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>
222
+ <textarea id="notes-input" rows="1" placeholder="Add a note (optional)"></textarea>
223
+ </div>
224
+ <button class="btn-secondary" id="gps-btn">
225
+ <svg viewBox="0 0 24 24"><path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7z"/><circle cx="12" cy="9" r="2.5"/></svg>
226
+ Add Location
227
+ </button>
228
+ <button class="btn-primary" id="submit-reg-btn" disabled>
229
+ <svg viewBox="0 0 24 24"><path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7z"/><circle cx="12" cy="9" r="2.5"/></svg>
230
+ Report Sighting
231
+ </button>
232
+ </div>
233
+ </div>
234
+
235
+ <!-- ══ SCREEN: ANALYSIS ══ -->
236
+ <div id="screen-analysis" class="screen">
237
+ <div class="flow-header">
238
+ <button class="back-btn" id="analysis-back"><svg viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"/></svg></button>
239
+ <h2>Analyzing Sighting</h2>
240
+ </div>
241
+ <div id="analysis-photo-wrap">
242
+ <img id="analysis-photo" src="" alt="photo"/>
243
+ <div id="ai-badge"><span class="dot"></span><span id="ai-badge-text">AI Analyzing...</span></div>
244
+ <div id="animal-result-badge"><svg viewBox="0 0 24 24"><path d="M20 6L9 17l-5-5"/></svg><span id="result-badge-text">Caramel Dog</span></div>
245
+ </div>
246
+ <div class="analysis-card">
247
+ <h3>Identified Details <button id="edit-btn"><i data-lucide="pencil"></i></button></h3>
248
+ <p class="subtitle">AI filled in these details. Correct if needed.</p>
249
+ <div class="dropdowns-grid">
250
+ <div class="dropdown-field"><label>Species</label><select id="sel-species"><option value="dog">Dog</option><option value="cat">Cat</option></select></div>
251
+ <div class="dropdown-field"><label>Breed (Estimated)</label><select id="sel-breed"></select></div>
252
+ <div class="dropdown-field"><label>Primary Color</label><select id="sel-color"><option value="Caramelo">Caramel</option><option value="Preto">Black</option><option value="Branco">White</option><option value="Cinza">Gray</option><option value="Marrom">Brown</option><option value="Mesclado">Mixed</option></select></div>
253
+ <div class="dropdown-field"><label>Size</label><select id="sel-size"><option value="Pequeno">Small</option><option value="MΓ©dio">Medium</option><option value="Grande">Large</option></select></div>
254
+ </div>
255
+ <div class="condition-label">Condition (Optional)</div>
256
+ <div class="condition-chips">
257
+ <button class="cond-chip" data-val="Injured"><span class="check"><i data-lucide="check"></i></span> Injured</button>
258
+ <button class="cond-chip" data-val="Looks Healthy"><span class="check"><i data-lucide="check"></i></span> Looks Healthy</button>
259
+ <button class="cond-chip" data-val="Has Collar"><span class="check"><i data-lucide="check"></i></span> Has Collar</button>
260
+ </div>
261
+ </div>
262
+ <div class="analysis-card" id="similar-section">
263
+ <h3>Similar animals nearby</h3>
264
+ <p class="subtitle">Found by AI for context β€” recognition is automatic.</p>
265
+ <div class="similar-scroll" id="similar-scroll"></div>
266
+ </div>
267
+ <div class="analysis-card">
268
+ <div class="dropdown-field">
269
+ <label>Name <span class="optional-tag">optional</span></label>
270
+ <input type="text" id="animal-name-input" placeholder="e.g. Caputino, Bolinha, Luna…" maxlength="40" autocomplete="off"/>
271
+ </div>
272
+ </div>
273
+ <div id="analysis-actions">
274
+ <button class="btn-primary" id="confirm-btn">Confirm & Report β†’</button>
275
+ <button class="btn-outline" id="discard-btn">Discard Photo</button>
276
+ </div>
277
+ </div>
278
+
279
+ <!-- ══ SCREEN: CONFIRM ══ -->
280
+ <div id="screen-confirm" class="screen">
281
+ <div id="confirm-inner">
282
+ <div id="confirm-photo-wrap">
283
+ <div id="confirm-photo"><i data-lucide="paw-print"></i></div>
284
+ <div id="confirm-check"><svg viewBox="0 0 24 24"><polyline points="20 6 9 20 4 14"/></svg></div>
285
+ </div>
286
+ <div id="confirm-title">Sighting<br>Reported!</div>
287
+ <div id="confirm-card">
288
+ <div class="confirm-row">
289
+ <span class="confirm-row-label"><svg viewBox="0 0 24 24"><path d="M20 21v-2a4 4 0 00-4-4H8a4 4 0 00-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>Animal:</span>
290
+ <span class="confirm-row-value" id="confirm-animal">β€”</span>
291
+ </div>
292
+ <div class="confirm-row">
293
+ <span class="confirm-row-label"><svg viewBox="0 0 24 24"><path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7z"/><circle cx="12" cy="9" r="2.5"/></svg>Location:</span>
294
+ <span class="confirm-row-value" id="confirm-local">β€”</span>
295
+ </div>
296
+ <div class="confirm-row">
297
+ <span class="confirm-row-label"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>Time:</span>
298
+ <span class="confirm-row-value" id="confirm-hora">β€”</span>
299
+ </div>
300
+ </div>
301
+ <div id="confirm-id-grid" class="confirm-id-grid"></div>
302
+ <div class="confirm-btns">
303
+ <button class="btn-primary" id="register-another-btn"><svg viewBox="0 0 24 24"><path d="M23 19a2 2 0 01-2 2H3a2 2 0 01-2-2V8a2 2 0 012-2h4l2-3h6l2 3h4a2 2 0 012 2z"/><circle cx="12" cy="13" r="4"/></svg>Report Another</button>
304
+ <button class="btn-secondary" id="go-map-btn"><svg viewBox="0 0 24 24"><polygon points="3 6 9 3 15 6 21 3 21 18 15 21 9 18 3 21"/></svg>View on Map</button>
305
+ </div>
306
+ </div>
307
+ </div>
308
+
309
+ <!-- ══ SCREEN: SIGHTINGS LIST ══ -->
310
+ <div id="screen-sightings" class="screen">
311
+ <div id="sightings-header">
312
+ <span id="sightings-count">Loading...</span>
313
+ <button id="refresh-btn" title="Refresh"><i data-lucide="refresh-cw"></i></button>
314
+ </div>
315
+ <div id="animals-list"></div>
316
+ </div>
317
+
318
+ <!-- ══ SCREEN: PROFILE / FICHA ══ -->
319
+ <div id="screen-profile" class="screen">
320
+ <div class="profile-scroll">
321
+ <!-- Hero -->
322
+ <div class="profile-hero">
323
+ <img class="profile-hero-img" id="profile-hero-img" src="" alt=""/>
324
+ <div class="hero-gradient"></div>
325
+ <div class="hero-top-btns">
326
+ <button class="hero-btn" id="profile-back-btn"><svg viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"/></svg></button>
327
+ <button class="hero-btn" id="profile-share-btn"><svg viewBox="0 0 24 24"><circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/><line x1="8.59" y1="13.51" x2="15.42" y2="17.49"/><line x1="15.41" y1="6.51" x2="8.59" y2="10.49"/></svg></button>
328
+ </div>
329
+ <div id="profile-sightings-badge">
330
+ <svg viewBox="0 0 24 24"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
331
+ <span id="profile-badge-text">0 sightings</span>
332
+ </div>
333
+ </div>
334
+
335
+ <!-- Content -->
336
+ <div class="profile-content">
337
+ <h1 id="profile-title">...</h1>
338
+ <div id="profile-status"><span class="status-dot"></span><span id="profile-status-text">...</span></div>
339
+ <div id="profile-helped-banner" style="display:none">
340
+ <i data-lucide="heart"></i>
341
+ <span>This animal has been helped by the community</span>
342
+ </div>
343
+ <div id="profile-ai-desc" class="ai-desc-callout" style="display:none">
344
+ <span class="ai-label">AI</span>
345
+ <p id="profile-ai-text"></p>
346
+ </div>
347
+
348
+ <!-- Identification -->
349
+ <div class="profile-section">
350
+ <div class="section-title">Identification</div>
351
+ <div class="id-grid">
352
+ <div class="id-cell"><div class="id-cell-label">Species</div><div class="id-cell-value" id="prof-species">β€”</div></div>
353
+ <div class="id-cell"><div class="id-cell-label">Breed</div><div class="id-cell-value" id="prof-breed">β€”</div></div>
354
+ <div class="id-cell"><div class="id-cell-label">Size</div><div class="id-cell-value" id="prof-size">β€”</div></div>
355
+ <div class="id-cell"><div class="id-cell-label">Color</div><div class="id-cell-value" id="prof-color">β€”</div></div>
356
+ </div>
357
+ </div>
358
+
359
+ <!-- Gallery -->
360
+ <div class="profile-section">
361
+ <div class="gallery-header">
362
+ <div class="section-title" style="margin-bottom:0">Sightings Gallery</div>
363
+ <button class="gallery-see-all">See all</button>
364
+ </div>
365
+ <div class="gallery-scroll" id="profile-gallery"></div>
366
+ </div>
367
+
368
+ <!-- Community Help -->
369
+ <div class="profile-section" id="profile-help-section" style="display:none">
370
+ <div class="section-title help-section-title">
371
+ <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="var(--green)" stroke-width="2"><path d="M20.84 4.61a5.5 5.5 0 00-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 00-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 000-7.78z"/></svg>
372
+ Community Help
373
+ </div>
374
+ <div id="profile-help-list"></div>
375
+ </div>
376
+
377
+ <!-- Trajectory -->
378
+ <div class="profile-section">
379
+ <div class="section-title">Recent Path</div>
380
+ <div id="trajectory-map"></div>
381
+ </div>
382
+ </div>
383
+ </div>
384
+
385
+ <!-- Footer -->
386
+ <div class="profile-footer">
387
+ <button class="btn-adopt" id="btn-adopt"><i data-lucide="heart"></i> I Want to Adopt / Help</button>
388
+ </div>
389
+ </div>
390
+
391
+ <!-- ══ HELP BOTTOM SHEET ══ -->
392
+ <div id="help-overlay" class="hidden"></div>
393
+ <div id="help-sheet" class="help-sheet hidden">
394
+ <div class="drag-handle"></div>
395
+ <div id="help-animal-row">
396
+ <div id="help-animal-photo"></div>
397
+ <div id="help-animal-info">
398
+ <div id="help-animal-name">β€”</div>
399
+ <div id="help-animal-sub">β€”</div>
400
+ </div>
401
+ </div>
402
+ <div class="help-actions">
403
+ <button class="help-action-btn" id="help-share">
404
+ <span class="help-action-icon"><i data-lucide="share-2"></i></span>
405
+ <span class="help-action-text">
406
+ <strong>Share this animal</strong>
407
+ <small>Send via WhatsApp or copy link</small>
408
+ </span>
409
+ <svg class="help-chevron" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg>
410
+ </button>
411
+ <button class="help-action-btn" id="help-directions">
412
+ <span class="help-action-icon"><i data-lucide="map-pin"></i></span>
413
+ <span class="help-action-text">
414
+ <strong>Get directions</strong>
415
+ <small>Navigate to last known location</small>
416
+ </span>
417
+ <svg class="help-chevron" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg>
418
+ </button>
419
+ <button class="help-action-btn" id="help-helped">
420
+ <span class="help-action-icon"><i data-lucide="check-circle"></i></span>
421
+ <span class="help-action-text">
422
+ <strong>I helped this animal</strong>
423
+ <small>Log that you fed, treated, or fostered them</small>
424
+ </span>
425
+ <svg class="help-chevron" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg>
426
+ </button>
427
+ </div>
428
+ <button class="help-cancel" id="help-cancel">Cancel</button>
429
+ </div>
430
+
431
+ <!-- ══ HELPED CONFIRM ══ -->
432
+ <div id="helped-confirm" class="helped-confirm hidden">
433
+ <div class="helped-inner">
434
+ <div class="helped-icon-wrap"><i data-lucide="heart"></i></div>
435
+ <h3>Thank you</h3>
436
+ <p>Your help has been recorded.<br>Every act of care makes a difference.</p>
437
+ <button id="helped-ok">Done</button>
438
+ </div>
439
+ </div>
440
+
441
+ <!-- ══ SCREEN: HELP PROOF ══ -->
442
+ <div id="screen-help-proof" class="screen">
443
+ <div class="flow-header">
444
+ <button class="back-btn" id="help-proof-back"><svg viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"/></svg></button>
445
+ <h2>I Helped This Animal</h2>
446
+ </div>
447
+ <div id="help-proof-animal-row">
448
+ <div id="help-proof-animal-photo" class="help-proof-thumb"></div>
449
+ <div>
450
+ <div id="help-proof-animal-name" class="help-proof-animal-name">β€”</div>
451
+ <div id="help-proof-animal-sub" class="help-proof-animal-sub">β€”</div>
452
+ </div>
453
+ </div>
454
+ <div class="help-proof-section">
455
+ <div class="help-proof-label">What did you do?</div>
456
+ <div class="help-type-chips" id="help-type-chips">
457
+ <button class="help-type-chip active" data-type="fed"><i data-lucide="utensils"></i> Fed</button>
458
+ <button class="help-type-chip" data-type="vet"><i data-lucide="stethoscope"></i> Took to vet</button>
459
+ <button class="help-type-chip" data-type="adopted"><i data-lucide="home"></i> Adopted</button>
460
+ <button class="help-type-chip" data-type="rescued"><i data-lucide="shield-check"></i> Rescued</button>
461
+ <button class="help-type-chip" data-type="other"><i data-lucide="heart"></i> Other</button>
462
+ </div>
463
+ </div>
464
+ <div class="help-proof-section">
465
+ <div class="help-proof-label">Add a photo <span class="optional-tag">optional</span></div>
466
+ <div id="help-proof-viewfinder" class="help-proof-viewfinder">
467
+ <input type="file" id="help-proof-input" accept="image/*" capture="environment"/>
468
+ <img id="help-proof-preview" src="" alt="" class="hidden"/>
469
+ <div id="help-proof-placeholder">
470
+ <svg viewBox="0 0 24 24"><path d="M23 19a2 2 0 01-2 2H3a2 2 0 01-2-2V8a2 2 0 012-2h4l2-3h6l2 3h4a2 2 0 012 2z"/><circle cx="12" cy="13" r="4"/></svg>
471
+ <span>Tap to add photo proof</span>
472
+ </div>
473
+ <div id="help-proof-ai-badge" class="help-proof-ai-badge hidden">
474
+ <span class="dot"></span><span id="help-proof-ai-text">Verifying with AI…</span>
475
+ </div>
476
+ <div id="help-proof-verified-badge" class="help-proof-verified-badge hidden">
477
+ <svg viewBox="0 0 24 24"><path d="M20 6L9 17l-5-5"/></svg>
478
+ <span id="help-proof-verified-text">Same animal confirmed</span>
479
+ </div>
480
+ </div>
481
+ </div>
482
+ <div class="help-proof-section">
483
+ <div class="help-proof-label">Tell the story <span class="optional-tag">optional</span></div>
484
+ <textarea id="help-proof-notes" rows="3" placeholder="What happened? Where did you take them?"></textarea>
485
+ </div>
486
+ <button class="btn-primary help-proof-submit" id="help-proof-submit">
487
+ <svg viewBox="0 0 24 24"><path d="M20.84 4.61a5.5 5.5 0 00-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 00-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 000-7.78z"/></svg>
488
+ Register Help
489
+ </button>
490
+ </div>
491
+
492
+ <!-- ══ PHOTO LIGHTBOX ══ -->
493
+ <div id="photo-lightbox" class="photo-lightbox hidden" role="dialog">
494
+ <div class="lightbox-backdrop"></div>
495
+ <div class="lightbox-inner">
496
+ <img id="lightbox-img" src="" alt="photo" />
497
+ <button id="lightbox-close" aria-label="Close"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg></button>
498
+ </div>
499
+ </div>
500
+
501
+ <!-- ══ PHOTO CONTEXT MENU ══ -->
502
+ <div id="photo-menu" class="photo-menu hidden">
503
+ <button id="photo-menu-download">
504
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
505
+ Download photo
506
+ </button>
507
+ </div>
508
+
509
+ <!-- ══ BOTTOM NAV ══ -->
510
+ <nav id="bottom-nav">
511
+ <button class="nav-btn active" data-screen="map"><span class="nav-icon"><i data-lucide="map"></i></span><span>Map</span></button>
512
+ <button class="nav-btn" data-screen="register"><span class="nav-icon"><i data-lucide="camera"></i></span><span>Report</span></button>
513
+ <button class="nav-btn" data-screen="sightings"><span class="nav-icon"><i data-lucide="eye"></i></span><span>Sightings</span></button>
514
+ </nav>
515
+ </div><!-- /#app -->
516
+ </div><!-- /.phone-frame -->
517
+ </div><!-- /.phone-wrap -->
518
+
519
+ <!-- ══ RIGHT SIDEBAR (desktop only) ══ -->
520
+ <aside id="sidebar-right">
521
+ <div class="sidebar-section">
522
+ <div class="sidebar-badge-title">Built for</div>
523
+ <a class="hackathon-badge" href="https://huggingface.co/build-small-hackathon" target="_blank" rel="noopener">
524
+ <span class="hb-icon">🏑</span>
525
+ <span class="hb-text">
526
+ <strong>Build Small Hackathon 2026</strong>
527
+ <small>Backyard AI track Β· Hugging Face</small>
528
+ </span>
529
+ </a>
530
+ </div>
531
+
532
+ <div class="sidebar-divider"></div>
533
+
534
+ <div class="sidebar-section">
535
+ <h3>Tech stack</h3>
536
+ <div class="stack-list">
537
+ <div class="stack-row">
538
+ <span class="stack-label">Vision AI</span>
539
+ <span class="stack-value">Nemotron Omni 30B</span>
540
+ </div>
541
+ <div class="stack-row">
542
+ <span class="stack-label">Embeddings</span>
543
+ <span class="stack-value">all-MiniLM-L6-v2</span>
544
+ </div>
545
+ <div class="stack-row">
546
+ <span class="stack-label">Matching</span>
547
+ <span class="stack-value">Cosine similarity</span>
548
+ </div>
549
+ <div class="stack-row">
550
+ <span class="stack-label">Map</span>
551
+ <span class="stack-value">Leaflet.js + OSM</span>
552
+ </div>
553
+ <div class="stack-row">
554
+ <span class="stack-label">Frontend</span>
555
+ <span class="stack-value">gr.Server SPA</span>
556
+ </div>
557
+ <div class="stack-row">
558
+ <span class="stack-label">Storage</span>
559
+ <span class="stack-value">SQLite</span>
560
+ </div>
561
+ </div>
562
+ </div>
563
+
564
+ <div class="sidebar-divider"></div>
565
+
566
+ <div class="sidebar-section">
567
+ <h3>Help an animal today</h3>
568
+ <p>Open a profile from the map, tap <strong>I Want to Help</strong>, and log what you did β€” fed, treated, fostered. A photo proves it; the AI verifies it's the same animal.</p>
569
+ </div>
570
+
571
+ <div class="sidebar-divider"></div>
572
+
573
+ <div class="sidebar-section sidebar-links">
574
+ <a href="https://huggingface.co/spaces/build-small-hackathon/viralata-mapper" target="_blank" rel="noopener">
575
+ <svg viewBox="0 0 24 24"><path d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"/></svg>
576
+ View on Hugging Face
577
+ </a>
578
+ <a href="https://huggingface.co/blog/build-small-hackathon/pawmap" target="_blank" rel="noopener">
579
+ <svg viewBox="0 0 24 24"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>
580
+ Field Notes
581
+ </a>
582
+ </div>
583
+
584
+ <div class="sidebar-footer">
585
+ Made in BrasΓ­lia, DF πŸ‡§πŸ‡·<br>
586
+ <span>for every stray that deserves to be seen</span>
587
+ </div>
588
+ </aside>
589
+
590
+ </div><!-- /desktop-layout -->
591
+
592
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.min.js"></script>
593
+ <script src="/static/app.js"></script>
594
+ <script src="/static/help-proof.js"></script>
595
+ </body>
596
+ </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
static/style.css CHANGED
@@ -21,37 +21,40 @@
21
  #app { display:flex; flex-direction:column; height:100dvh; max-width:480px; margin:0 auto; background:var(--white); box-shadow:0 0 40px rgba(0,0,0,.15); overflow:hidden; position:relative; }
22
 
23
  /* ── Onboarding ── */
24
- #screen-splash { position:absolute; inset:0; z-index:999; background:linear-gradient(170deg,#2d7249 0%,#388C59 60%,#4aa86e 100%); display:flex; flex-direction:column; overflow:hidden; transition:opacity .35s ease, transform .35s ease; }
25
  #screen-splash.splash-out { opacity:0; transform:scale(1.03); pointer-events:none; }
26
 
27
- #ob-skip { position:absolute; top:16px; right:16px; background:rgba(255,255,255,.18); border:none; color:#fff; font-size:13px; font-weight:600; padding:6px 14px; border-radius:20px; cursor:pointer; z-index:10; }
28
 
29
  /* Sliding track */
30
  #ob-track { display:flex; flex:1; transition:transform .38s cubic-bezier(.4,0,.2,1); min-height:0; }
31
  .ob-step { min-width:100%; display:flex; flex-direction:column; align-items:center; padding:48px 28px 0; }
32
 
33
- /* Lottie area */
34
  .ob-lottie-wrap { width:220px; height:220px; position:relative; display:flex; align-items:center; justify-content:center; flex-shrink:0; }
35
- .ob-lottie { width:220px; height:220px; }
36
  .ob-lottie-fallback { position:absolute; inset:0; display:flex; align-items:center; justify-content:center; font-size:96px; line-height:1; }
37
- /* Hide fallback once lottie loads */
38
  .ob-lottie:not([error]) + .ob-lottie-fallback { animation: hideFallback 0s 1.2s forwards; }
39
  @keyframes hideFallback { to { opacity:0; pointer-events:none; } }
40
 
41
  /* Text */
42
  .ob-text { text-align:center; margin-top:20px; }
43
- .ob-step-label { font-size:11px; font-weight:700; letter-spacing:1.2px; text-transform:uppercase; color:rgba(255,255,255,.55); margin-bottom:10px; }
44
- .ob-text h2 { font-size:22px; font-weight:800; color:#fff; line-height:1.25; letter-spacing:-.3px; margin-bottom:12px; }
45
- .ob-text p { font-size:14px; color:rgba(255,255,255,.82); line-height:1.6; }
 
 
 
46
 
47
  /* Dots */
48
  #ob-dots { display:flex; justify-content:center; gap:8px; padding:20px 0 12px; flex-shrink:0; }
49
- .ob-dot { width:8px; height:8px; border-radius:4px; background:rgba(255,255,255,.3); transition:width .25s, background .25s; }
50
- .ob-dot.active { width:24px; background:#fff; }
51
 
52
  /* Actions */
53
  #ob-actions { padding:0 24px 32px; flex-shrink:0; }
54
- #ob-next { width:100%; padding:16px; background:#fff; color:var(--green-dark); border:none; border-radius:14px; font-size:16px; font-weight:700; cursor:pointer; letter-spacing:-.2px; transition:transform .1s, box-shadow .1s; box-shadow:0 4px 20px rgba(0,0,0,.2); }
55
  #ob-next:active { transform:scale(.98); }
56
 
57
  /* ── Header ── */
@@ -787,8 +790,4 @@
787
  .sidebar-section p strong { color:#1e3d28; }
788
 
789
  /* ── Footer ── */
790
- .sidebar-footer {
791
- margin-top:auto; padding-top:28px;
792
- font-size:12px; color:#9abda6; line-height:1.6;
793
- }
794
- }
 
21
  #app { display:flex; flex-direction:column; height:100dvh; max-width:480px; margin:0 auto; background:var(--white); box-shadow:0 0 40px rgba(0,0,0,.15); overflow:hidden; position:relative; }
22
 
23
  /* ── Onboarding ── */
24
+ #screen-splash { position:absolute; inset:0; z-index:999; background:#fff; display:flex; flex-direction:column; overflow:hidden; transition:opacity .35s ease, transform .35s ease; }
25
  #screen-splash.splash-out { opacity:0; transform:scale(1.03); pointer-events:none; }
26
 
27
+ #ob-skip { position:absolute; top:16px; right:16px; background:rgba(0,0,0,.06); border:none; color:#444; font-size:13px; font-weight:600; padding:6px 14px; border-radius:20px; cursor:pointer; z-index:10; }
28
 
29
  /* Sliding track */
30
  #ob-track { display:flex; flex:1; transition:transform .38s cubic-bezier(.4,0,.2,1); min-height:0; }
31
  .ob-step { min-width:100%; display:flex; flex-direction:column; align-items:center; padding:48px 28px 0; }
32
 
33
+ /* Video area */
34
  .ob-lottie-wrap { width:220px; height:220px; position:relative; display:flex; align-items:center; justify-content:center; flex-shrink:0; }
35
+ .ob-lottie { width:220px; height:220px; object-fit:contain; }
36
  .ob-lottie-fallback { position:absolute; inset:0; display:flex; align-items:center; justify-content:center; font-size:96px; line-height:1; }
37
+ /* Hide fallback once video loads */
38
  .ob-lottie:not([error]) + .ob-lottie-fallback { animation: hideFallback 0s 1.2s forwards; }
39
  @keyframes hideFallback { to { opacity:0; pointer-events:none; } }
40
 
41
  /* Text */
42
  .ob-text { text-align:center; margin-top:20px; }
43
+ /* #388C59 on white = 4.6:1 contrast β€” passes WCAG AA for normal text */
44
+ .ob-step-label { font-size:11px; font-weight:700; letter-spacing:1.2px; text-transform:uppercase; color:var(--green); margin-bottom:10px; }
45
+ /* #1a1a1a on white = 16:1 β€” AAA */
46
+ .ob-text h2 { font-size:22px; font-weight:800; color:#1a1a1a; line-height:1.25; letter-spacing:-.3px; margin-bottom:12px; }
47
+ /* #4a4a4a on white = 9.7:1 β€” AAA */
48
+ .ob-text p { font-size:14px; color:#4a4a4a; line-height:1.6; }
49
 
50
  /* Dots */
51
  #ob-dots { display:flex; justify-content:center; gap:8px; padding:20px 0 12px; flex-shrink:0; }
52
+ .ob-dot { width:8px; height:8px; border-radius:4px; background:rgba(0,0,0,.15); transition:width .25s, background .25s; }
53
+ .ob-dot.active { width:24px; background:var(--green); }
54
 
55
  /* Actions */
56
  #ob-actions { padding:0 24px 32px; flex-shrink:0; }
57
+ #ob-next { width:100%; padding:16px; background:var(--green-dark); color:#fff; border:none; border-radius:14px; font-size:16px; font-weight:700; cursor:pointer; letter-spacing:-.2px; transition:transform .1s, box-shadow .1s; box-shadow:0 4px 20px rgba(45,114,73,.35); }
58
  #ob-next:active { transform:scale(.98); }
59
 
60
  /* ── Header ── */
 
790
  .sidebar-section p strong { color:#1e3d28; }
791
 
792
  /* ── Footer ── */
793
+ .sidebar