Spaces:
Running
Running
Tom Claude Opus 4.5 commited on
Commit ·
979edc1
1
Parent(s): 696b25e
Add OSINT and scrolly-sveltekit skills
Browse files- OSINT: 150+ tools from Bellingcat, IDI Follow The Money, and DigitalDigging
for geolocation, social media analysis, corporate research, and more
- scrolly-sveltekit: Scroll-driven narrative components for SvelteKit 5
including image, video, map scrolly patterns
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- index.html +8 -0
- skills/osint/SKILL.md +161 -0
- skills/osint/references/ai-tools.md +303 -0
- skills/osint/references/country-resources.md +75 -0
- skills/osint/references/index.md +11 -0
- skills/osint/references/llms.md +222 -0
- skills/osint/references/maps-satellites.md +233 -0
- skills/scrolly-sveltekit/SKILL.md +937 -0
- skills/scrolly-sveltekit/references/HeroVisualization.svelte +57 -0
- skills/scrolly-sveltekit/references/MapScrolly.svelte +346 -0
- skills/scrolly-sveltekit/references/NoticeMosaic.svelte +229 -0
- skills/scrolly-sveltekit/references/ScrollyHelper.svelte +107 -0
- skills/scrolly-sveltekit/references/ScrollySection.svelte +278 -0
- skills/scrolly-sveltekit/references/ScrollyTextBox.svelte +260 -0
- skills/scrolly-sveltekit/references/VideoScrollyVisualization.svelte +109 -0
index.html
CHANGED
|
@@ -159,6 +159,14 @@
|
|
| 159 |
<td><a href="https://huggingface.co/spaces/fdaudens/ai-journalism-skills/tree/main/skills/fact-checker" target="_blank"><code>fact-checker</code></a></td>
|
| 160 |
<td>Verify claims using multiple sources</td>
|
| 161 |
</tr>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 162 |
</table>
|
| 163 |
|
| 164 |
<h2>Examples of what we could add</h2>
|
|
|
|
| 159 |
<td><a href="https://huggingface.co/spaces/fdaudens/ai-journalism-skills/tree/main/skills/fact-checker" target="_blank"><code>fact-checker</code></a></td>
|
| 160 |
<td>Verify claims using multiple sources</td>
|
| 161 |
</tr>
|
| 162 |
+
<tr>
|
| 163 |
+
<td><a href="https://huggingface.co/spaces/fdaudens/ai-journalism-skills/tree/main/skills/osint" target="_blank"><code>osint</code></a></td>
|
| 164 |
+
<td>150+ OSINT tools for investigations (Bellingcat, IDI, DigitalDigging)</td>
|
| 165 |
+
</tr>
|
| 166 |
+
<tr>
|
| 167 |
+
<td><a href="https://huggingface.co/spaces/fdaudens/ai-journalism-skills/tree/main/skills/scrolly-sveltekit" target="_blank"><code>scrolly-sveltekit</code></a></td>
|
| 168 |
+
<td>Scroll-driven narratives with SvelteKit 5</td>
|
| 169 |
+
</tr>
|
| 170 |
</table>
|
| 171 |
|
| 172 |
<h2>Examples of what we could add</h2>
|
skills/osint/SKILL.md
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
name: OSINT
|
| 3 |
+
description: Comprehensive OSINT (Open Source Intelligence) toolkit combining Bellingcat investigation tools, IDI Follow The Money resources, and country-specific OSINT databases. Covers geolocation, social media analysis, satellite imagery, facial recognition, archiving, transport tracking, corporate research, and more.
|
| 4 |
+
---
|
| 5 |
+
|
| 6 |
+
# OSINT Skill
|
| 7 |
+
|
| 8 |
+
Comprehensive assistance with Open Source Intelligence investigations, generated from Bellingcat's toolkit, Inclusive Development International's Follow The Money resources, and DigitalDigging country-specific databases.
|
| 9 |
+
|
| 10 |
+
## When to Use This Skill
|
| 11 |
+
|
| 12 |
+
This skill should be triggered when:
|
| 13 |
+
- Conducting open source investigations
|
| 14 |
+
- Geolocating images or videos
|
| 15 |
+
- Analyzing social media content (Facebook, Instagram, Telegram, TikTok, Twitter/X, YouTube)
|
| 16 |
+
- Verifying images or detecting manipulations
|
| 17 |
+
- Tracking aircraft, ships, or vehicles
|
| 18 |
+
- Researching companies, finances, or sanctions
|
| 19 |
+
- Investigating environmental or wildlife crimes
|
| 20 |
+
- Archiving web content for preservation
|
| 21 |
+
- Finding country-specific OSINT resources
|
| 22 |
+
- Following the money in development finance investigations
|
| 23 |
+
|
| 24 |
+
## Tool Categories
|
| 25 |
+
|
| 26 |
+
### AI-Powered Tools (NEW)
|
| 27 |
+
- **AI Geolocation**: GeoSpy, Picarta, GeoFinder, EarthKit, Planet
|
| 28 |
+
- **AI Face Search**: FaceSeek, Lenso.ai, ProFace Finder, Raugen
|
| 29 |
+
- **AI Infrastructure Search**: CensysGPT, ZoomEye GPT, ShodanGPT
|
| 30 |
+
- **Deepfake Detection**: Sensity.ai, DeepWare, AI Detector
|
| 31 |
+
- **AI Platforms**: Ubikron, Cyclect (475+ tools), Taranis AI
|
| 32 |
+
- **ChatGPT Assistants**: OSINT GPT, Intel Sourcing Agent, Analyst's Co-Pilot
|
| 33 |
+
|
| 34 |
+
### Maps & Geolocation
|
| 35 |
+
- **Maps**: Google Maps, Bing Maps, Apple Maps, Yandex Maps, Baidu Maps, OpenStreetMap
|
| 36 |
+
- **Satellite Imagery**: Planet Labs, Sentinel Hub, Google Earth Pro, NASA Worldview
|
| 37 |
+
- **Street View**: Google Street View, Mapillary, KartaView
|
| 38 |
+
- **Geolocation Helpers**: GeoHints, SunCalc, Shadow Finder, PeakVisor
|
| 39 |
+
|
| 40 |
+
### Image & Video Analysis
|
| 41 |
+
- **Reverse Image Search**: Google Lens, TinEye, Search by Image
|
| 42 |
+
- **Facial Recognition**: PimEyes, FaceCheck.ID, Search4Faces
|
| 43 |
+
- **Metadata**: ExifTool, xIFr, Forensically
|
| 44 |
+
- **Video Tools**: InVID, Azure AI Video Indexer
|
| 45 |
+
|
| 46 |
+
### Social Media
|
| 47 |
+
- **Facebook**: Who Posted What?, Meta Content Library
|
| 48 |
+
- **Instagram**: InstaLoader, Instagram Location Search
|
| 49 |
+
- **Telegram**: Telepathy, TGStat, Telemetrio, TelegramDB
|
| 50 |
+
- **TikTok**: Zeeschuimer
|
| 51 |
+
- **Twitter/X**: Advanced Search, Location Search
|
| 52 |
+
- **YouTube**: MW Geofind
|
| 53 |
+
- **Username Search**: Sherlock, Maigret, Blackbird, WhatsMyName
|
| 54 |
+
|
| 55 |
+
### People & Identity
|
| 56 |
+
- **Username Search**: Namechk, Blackbird, Sherlock
|
| 57 |
+
- **Data Breach Search**: Have I Been Pwned, DeHashed, Leak-Lookup
|
| 58 |
+
- **Google Accounts**: GHunt
|
| 59 |
+
|
| 60 |
+
### Companies & Finance
|
| 61 |
+
- **Corporate Registries**: OpenCorporates, Companies House, OCCRP Aleph
|
| 62 |
+
- **Sanctions**: OpenSanctions, EU Sanctions Map, SanctionsExplorer
|
| 63 |
+
- **US Politics**: OpenSecrets, EDGAR, 527 Explorer
|
| 64 |
+
- **Beneficial Ownership**: Open Ownership, ICIJ Offshore Leaks
|
| 65 |
+
|
| 66 |
+
### Transport Tracking
|
| 67 |
+
- **Aircraft**: Flightradar24, FlightAware, ADS-B Exchange
|
| 68 |
+
- **Ships**: MarineTraffic, VesselFinder, Global Fishing Watch
|
| 69 |
+
- **Vehicles**: License Plate Maps
|
| 70 |
+
|
| 71 |
+
### Conflict & Weapons
|
| 72 |
+
- **Conflict Data**: ACLED, Liveuamap
|
| 73 |
+
- **Weapons ID**: Bulletpicker, CAT UXO, Open Source Munitions Portal
|
| 74 |
+
- **Radar**: Radar Interference Tracker
|
| 75 |
+
|
| 76 |
+
### Environment & Wildlife
|
| 77 |
+
- **Deforestation**: Global Forest Watch
|
| 78 |
+
- **Wildlife Trade**: CITES Database, Wildlife Trade Portal, WildEye
|
| 79 |
+
- **Fishing**: Global Fishing Watch
|
| 80 |
+
|
| 81 |
+
### Archiving
|
| 82 |
+
- **Web Archives**: Wayback Machine, Archive.today, Web Archives extension
|
| 83 |
+
- **Auto Archiving**: Auto Archiver, Hunchly
|
| 84 |
+
|
| 85 |
+
### Data & Analysis
|
| 86 |
+
- **Visualization**: Gephi, Datawrapper, RAWGraphs
|
| 87 |
+
- **Knowledge Management**: Obsidian, Logseq, Zotero
|
| 88 |
+
- **Investigation Platforms**: Maltego, Atlos
|
| 89 |
+
|
| 90 |
+
## Reference Files
|
| 91 |
+
|
| 92 |
+
This skill includes comprehensive documentation in `references/`:
|
| 93 |
+
|
| 94 |
+
- **ai-tools.md** - AI-powered OSINT tools (geolocation, facial recognition, deepfakes, platforms)
|
| 95 |
+
- **llms.md** - Complete Bellingcat toolkit with 150+ tools and descriptions
|
| 96 |
+
- **maps-satellites.md** - Maps and satellite imagery tools
|
| 97 |
+
- **country-resources.md** - Country-specific OSINT databases and global resources
|
| 98 |
+
|
| 99 |
+
## Country-Specific Resources
|
| 100 |
+
|
| 101 |
+
For investigations in specific countries, see `references/country-resources.md` which includes:
|
| 102 |
+
|
| 103 |
+
- **Individual Countries**: Armenia, Australia, Azerbaijan, Belarus, Bulgaria, China, India, Iran, Israel, Kazakhstan, Kyrgyzstan, Netherlands, Poland, Russia, Syria, Taiwan, Tajikistan, UAE, UK, Uzbekistan
|
| 104 |
+
- **Regional Collections**: Middle East, Africa (BBC Africa Eye), UK/Europe, Asia/Pacific, North America
|
| 105 |
+
- **Global Resources**: OSINT Map, OhShINT Web Resources (80+ countries)
|
| 106 |
+
|
| 107 |
+
## Follow The Money Resources
|
| 108 |
+
|
| 109 |
+
For financial and development investigations (from IDI toolkit):
|
| 110 |
+
- Development Finance Institution databases
|
| 111 |
+
- Chinese financial actor identification
|
| 112 |
+
- Follow the Money investigative methods
|
| 113 |
+
|
| 114 |
+
Contact: annie@inclusivedevelopment.net or alexla@inclusivedevelopment.net
|
| 115 |
+
|
| 116 |
+
## Quick Reference
|
| 117 |
+
|
| 118 |
+
### Geolocation Workflow
|
| 119 |
+
1. Extract metadata with ExifTool
|
| 120 |
+
2. Reverse image search with Google Lens/TinEye
|
| 121 |
+
3. Analyze shadows with SunCalc
|
| 122 |
+
4. Cross-reference with street view (Google/Mapillary)
|
| 123 |
+
5. Verify with satellite imagery
|
| 124 |
+
|
| 125 |
+
### Social Media Archiving
|
| 126 |
+
1. Use Auto Archiver for automated preservation
|
| 127 |
+
2. Archive.today for quick snapshots
|
| 128 |
+
3. Wayback Machine for long-term storage
|
| 129 |
+
4. Download videos before deletion
|
| 130 |
+
|
| 131 |
+
### Username Investigation
|
| 132 |
+
1. Search with Sherlock/Maigret across 400+ sites
|
| 133 |
+
2. Check data breaches with Have I Been Pwned
|
| 134 |
+
3. Cross-reference with social media platforms
|
| 135 |
+
4. Build network graph with Maltego
|
| 136 |
+
|
| 137 |
+
### AI-Powered Geolocation (No Metadata)
|
| 138 |
+
1. Upload to GeoSpy for pixel-based location estimation
|
| 139 |
+
2. Cross-reference with Picarta for additional predictions
|
| 140 |
+
3. Verify candidates using Google Street View/Mapillary
|
| 141 |
+
4. Confirm with satellite imagery from Planet/Sentinel Hub
|
| 142 |
+
|
| 143 |
+
### Deepfake Detection
|
| 144 |
+
1. Use Sensity.ai for comprehensive multilayer analysis
|
| 145 |
+
2. Check for pixel-level manipulation artifacts
|
| 146 |
+
3. Analyze audio for voice cloning indicators
|
| 147 |
+
4. Generate forensic report with confidence scores
|
| 148 |
+
|
| 149 |
+
## External Resources
|
| 150 |
+
|
| 151 |
+
- **Bellingcat Toolkit**: https://bellingcat.gitbook.io/toolkit
|
| 152 |
+
- **Stay Safe Guide**: Online security and privacy best practices
|
| 153 |
+
- **Guides & Handbooks**: Training materials for OSINT research
|
| 154 |
+
|
| 155 |
+
## Notes
|
| 156 |
+
|
| 157 |
+
- This skill combines multiple authoritative OSINT sources
|
| 158 |
+
- Tools may require accounts or have usage limits
|
| 159 |
+
- Always verify findings through multiple sources
|
| 160 |
+
- Follow ethical guidelines for OSINT research
|
| 161 |
+
- Respect privacy laws and platform terms of service
|
skills/osint/references/ai-tools.md
ADDED
|
@@ -0,0 +1,303 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# AI-Powered OSINT Tools
|
| 2 |
+
|
| 3 |
+
Reference for AI-powered tools that enhance OSINT investigations. These tools leverage machine learning and large language models for geolocation, facial recognition, infrastructure discovery, deepfake detection, and investigation automation.
|
| 4 |
+
|
| 5 |
+
**Source:** https://github.com/ubikron/Awesome-AI-OSINT
|
| 6 |
+
|
| 7 |
+
---
|
| 8 |
+
|
| 9 |
+
## AI Geolocation (GEOINT)
|
| 10 |
+
|
| 11 |
+
### GeoSpy
|
| 12 |
+
**URL:** https://geospy.ai/
|
| 13 |
+
**Capabilities:**
|
| 14 |
+
- Geo-estimation from low-context images using only pixel data (no metadata required)
|
| 15 |
+
- Meter-level accuracy for enterprise/law enforcement
|
| 16 |
+
- Property targeting and precision location identification
|
| 17 |
+
- Used by law enforcement to locate suspects from photos
|
| 18 |
+
- Enterprise API for qualified government/enterprise organizations
|
| 19 |
+
|
| 20 |
+
### Picarta
|
| 21 |
+
**URL:** https://picarta.ai/
|
| 22 |
+
**Capabilities:**
|
| 23 |
+
- AI-powered GPS coordinate prediction from photos
|
| 24 |
+
- Supports ground-level and aerial imagery
|
| 25 |
+
- Country-specific or worldwide search options
|
| 26 |
+
- Focus search on specific geographic areas
|
| 27 |
+
- Extracts and displays EXIF metadata
|
| 28 |
+
|
| 29 |
+
### GeoFinder
|
| 30 |
+
**URL:** https://geofinderai.com/
|
| 31 |
+
**Capabilities:**
|
| 32 |
+
- Photo location identification without EXIF data
|
| 33 |
+
- Analyzes visual elements (architecture, vegetation, signage)
|
| 34 |
+
|
| 35 |
+
### EarthKit
|
| 36 |
+
**URL:** https://earthkit.app/
|
| 37 |
+
**Capabilities:**
|
| 38 |
+
- AI-powered geospatial analysis
|
| 39 |
+
- Satellite and aerial imagery interpretation
|
| 40 |
+
|
| 41 |
+
### FindLocation
|
| 42 |
+
**URL:** https://findpiclocation.com/
|
| 43 |
+
**Capabilities:**
|
| 44 |
+
- Find photo locations using environmental clue analysis
|
| 45 |
+
|
| 46 |
+
### Planet
|
| 47 |
+
**URL:** https://www.planet.com/products/
|
| 48 |
+
**Capabilities:**
|
| 49 |
+
- AI satellite object detection
|
| 50 |
+
- Daily global imagery capture
|
| 51 |
+
- Change detection and monitoring
|
| 52 |
+
- Maritime vessel tracking
|
| 53 |
+
|
| 54 |
+
---
|
| 55 |
+
|
| 56 |
+
## AI Image Search
|
| 57 |
+
|
| 58 |
+
### Lenso.ai
|
| 59 |
+
**URL:** https://lenso.ai/en
|
| 60 |
+
**Capabilities:**
|
| 61 |
+
- Multi-category AI search: faces, places, duplicates, similar images
|
| 62 |
+
- Facial recognition search across the web
|
| 63 |
+
- Copyright/duplicate detection to trace original sources
|
| 64 |
+
- Research Mode for deeper investigation
|
| 65 |
+
- Alerts for new matches on tracked images
|
| 66 |
+
- API access and Chrome extension
|
| 67 |
+
|
| 68 |
+
### Vehicle Identifier
|
| 69 |
+
**URL:** https://vehicle-ai.vercel.app/
|
| 70 |
+
**Capabilities:**
|
| 71 |
+
- Identify vehicle make, model, and year from photos
|
| 72 |
+
- Works with partial vehicle images
|
| 73 |
+
|
| 74 |
+
---
|
| 75 |
+
|
| 76 |
+
## AI Face Search & Analysis
|
| 77 |
+
|
| 78 |
+
### FaceSeek
|
| 79 |
+
**URL:** https://www.faceseek.online/
|
| 80 |
+
**Capabilities:**
|
| 81 |
+
- AI-powered reverse face search across the internet
|
| 82 |
+
- Verify dating profiles and detect catfishing
|
| 83 |
+
- Spot fake accounts using stolen photos
|
| 84 |
+
- Identity verification and online safety
|
| 85 |
+
|
| 86 |
+
### Lenso.ai Face Search
|
| 87 |
+
**URL:** https://lenso.ai/en/page/face-search
|
| 88 |
+
**Capabilities:**
|
| 89 |
+
- Find where photos of a specific face appear online
|
| 90 |
+
- High precision facial detail matching
|
| 91 |
+
|
| 92 |
+
### ProFace Finder
|
| 93 |
+
**URL:** https://profacefinder.com/face-comparison/
|
| 94 |
+
**Capabilities:**
|
| 95 |
+
- Compare two faces to determine if same person
|
| 96 |
+
- Similarity scoring
|
| 97 |
+
|
| 98 |
+
### Raugen Face Comparison
|
| 99 |
+
**URL:** https://raugen.com/ai-tools/face-comparison
|
| 100 |
+
**Capabilities:**
|
| 101 |
+
- Side-by-side face comparison with AI similarity analysis
|
| 102 |
+
|
| 103 |
+
### The Flux Train Face Score
|
| 104 |
+
**URL:** https://thefluxtrain.com/tools/ai-face-score
|
| 105 |
+
**Capabilities:**
|
| 106 |
+
- Calculate percentage similarity between faces
|
| 107 |
+
|
| 108 |
+
### Ethnicity Analysis Tools
|
| 109 |
+
- **Ethnicity Guesser GPT:** https://chatgpt.com/g/g-ckfCaNtJt-ethnicity-guesser
|
| 110 |
+
- **Ethnicity Analysis:** https://justbuildthings.com/ai-image-analysis/ethnicity-analysis
|
| 111 |
+
- **Galaxy AI Face Analyzer:** https://image.galaxy.ai/ai-face-analyzer
|
| 112 |
+
|
| 113 |
+
---
|
| 114 |
+
|
| 115 |
+
## IP/Infrastructure Search with AI
|
| 116 |
+
|
| 117 |
+
### CensysGPT
|
| 118 |
+
**URL:** https://gpt.censys.io/
|
| 119 |
+
**Capabilities:**
|
| 120 |
+
- Natural language queries to Censys search syntax
|
| 121 |
+
- Find hosts, services, and vulnerabilities
|
| 122 |
+
- Translate queries from Shodan, ZoomEye, BinaryEdge
|
| 123 |
+
- Proactive threat hunting and attack surface management
|
| 124 |
+
|
| 125 |
+
### ZoomEye GPT
|
| 126 |
+
**URL:** https://www.zoomeye.ai/gpt
|
| 127 |
+
**Capabilities:**
|
| 128 |
+
- Natural language queries for internet-connected devices
|
| 129 |
+
- Search IoT devices, servers, network infrastructure
|
| 130 |
+
- Historical data access
|
| 131 |
+
|
| 132 |
+
### ShodanGPT
|
| 133 |
+
**URL:** https://chatgpt.com/g/g-JwmZilzME-shodan-gpt
|
| 134 |
+
**Capabilities:**
|
| 135 |
+
- Generate Shodan queries from natural language
|
| 136 |
+
- Find exposed devices and services
|
| 137 |
+
|
| 138 |
+
---
|
| 139 |
+
|
| 140 |
+
## ChatGPT-Based OSINT Assistants
|
| 141 |
+
|
| 142 |
+
| Tool | URL | Purpose |
|
| 143 |
+
|------|-----|---------|
|
| 144 |
+
| OSINT GPT | https://chatgpt.com/g/g-ysjJG1VjM-osint-gpt | General OSINT guidance |
|
| 145 |
+
| AI OSINT by Cyclect | https://chatgpt.com/g/g-aZQ1x6vqB-ai-osint | Investigation assistance |
|
| 146 |
+
| OSINT AI | https://chatgpt.com/g/g-bTltYNKUJ-osint-ai | Research methodology |
|
| 147 |
+
| Intel Sourcing Agent | https://chatgpt.com/g/g-HcFHDwAdM-intel-sourcing-agent | Intelligence sourcing |
|
| 148 |
+
| Analyst's Co-Pilot | https://theee.ai/tools/analyst-s-co-pilot-2OToA2Irnq | Intelligence analysis |
|
| 149 |
+
|
| 150 |
+
---
|
| 151 |
+
|
| 152 |
+
## Google Dorks AI
|
| 153 |
+
|
| 154 |
+
### The Dorker
|
| 155 |
+
**URL:** https://www.yeschat.ai/ru/gpts-2OToO2th4j-The-Dorker
|
| 156 |
+
**Capabilities:**
|
| 157 |
+
- Generate advanced Google search queries (dorks)
|
| 158 |
+
- Find exposed files, directories, sensitive information
|
| 159 |
+
|
| 160 |
+
### Advanced Dorks Generator
|
| 161 |
+
**URL:** https://syntax.goldenowl.ai/
|
| 162 |
+
**Capabilities:**
|
| 163 |
+
- Create complex search engine queries
|
| 164 |
+
- Support for multiple search engines
|
| 165 |
+
|
| 166 |
+
---
|
| 167 |
+
|
| 168 |
+
## AI Contact/Lead Discovery
|
| 169 |
+
|
| 170 |
+
### Neuralead
|
| 171 |
+
**URL:** https://neuralead.ai/
|
| 172 |
+
**Capabilities:**
|
| 173 |
+
- Find business leads based on reference company profiles
|
| 174 |
+
- Discover contact info for decision makers
|
| 175 |
+
- Search millions of records across databases
|
| 176 |
+
- Chrome extension, web app, and API
|
| 177 |
+
- Integration with CRMs (Salesforce, HubSpot, Zoho)
|
| 178 |
+
|
| 179 |
+
### Prospectrin
|
| 180 |
+
**URL:** https://prospectrin.com/
|
| 181 |
+
**Capabilities:**
|
| 182 |
+
- Lead discovery and enrichment
|
| 183 |
+
- Business intelligence gathering
|
| 184 |
+
|
| 185 |
+
---
|
| 186 |
+
|
| 187 |
+
## Deepfake & AI Content Detection
|
| 188 |
+
|
| 189 |
+
### Sensity.ai
|
| 190 |
+
**URL:** https://sensity.ai/
|
| 191 |
+
**Capabilities:**
|
| 192 |
+
- Multilayer detection: pixel analysis, voice analysis, file forensics
|
| 193 |
+
- Detect deepfake videos, images, and audio
|
| 194 |
+
- Court-ready forensic reports with audit trails
|
| 195 |
+
- 98% accuracy on public datasets
|
| 196 |
+
- API, SDK, and web app access
|
| 197 |
+
- Heatmap visualization of manipulated regions
|
| 198 |
+
|
| 199 |
+
### AI Detector
|
| 200 |
+
**URL:** https://aidetector.com/
|
| 201 |
+
**Capabilities:**
|
| 202 |
+
- Detect AI-generated text and images
|
| 203 |
+
- Content authenticity verification
|
| 204 |
+
|
| 205 |
+
### DeepWare
|
| 206 |
+
**URL:** https://deepware.ai/
|
| 207 |
+
**Capabilities:**
|
| 208 |
+
- Analyze videos for deepfake manipulation
|
| 209 |
+
- Face swap and audio deepfake detection
|
| 210 |
+
|
| 211 |
+
---
|
| 212 |
+
|
| 213 |
+
## Multi-Tool AI Platforms
|
| 214 |
+
|
| 215 |
+
### Ubikron
|
| 216 |
+
**URL:** https://www.ubikron.com/
|
| 217 |
+
**Type:** Chrome extension OSINT workbench
|
| 218 |
+
**Capabilities:**
|
| 219 |
+
- Automatic capture of every visited page (screenshots, text, structure)
|
| 220 |
+
- Visual browser history timeline
|
| 221 |
+
- Clip, tag, and annotate web content
|
| 222 |
+
- AI assistant that summarizes and remembers research context
|
| 223 |
+
- RAG-powered Q&A about browsing history
|
| 224 |
+
- Full-text search across all captured content
|
| 225 |
+
- Entity extraction with enrichment lookups
|
| 226 |
+
|
| 227 |
+
### Cyclect
|
| 228 |
+
**URL:** https://cylect.io/
|
| 229 |
+
**Type:** OSINT search engine aggregator
|
| 230 |
+
**Capabilities:**
|
| 231 |
+
- 475+ integrated OSINT tools in one interface
|
| 232 |
+
- Categories: AI, username, domain, IP, email, people, face, maps, phone, vehicles, files, crypto, Tor
|
| 233 |
+
- Dark web search engines
|
| 234 |
+
- Blockchain explorers
|
| 235 |
+
- No query logging on server side
|
| 236 |
+
|
| 237 |
+
### Taranis AI
|
| 238 |
+
**URL:** https://github.com/taranis-ai/taranis-ai
|
| 239 |
+
**Capabilities:**
|
| 240 |
+
- Open source intelligence collection
|
| 241 |
+
- News aggregation and monitoring
|
| 242 |
+
- Automated report generation
|
| 243 |
+
- Self-hosted deployment
|
| 244 |
+
|
| 245 |
+
### Research Pilot v2.5
|
| 246 |
+
**URL:** https://digitaldigging.org/research/
|
| 247 |
+
**Capabilities:**
|
| 248 |
+
- AI-assisted research workflows
|
| 249 |
+
- Source discovery and organization
|
| 250 |
+
|
| 251 |
+
---
|
| 252 |
+
|
| 253 |
+
## Command Line / Self-Hosted Tools
|
| 254 |
+
|
| 255 |
+
| Tool | URL | Purpose |
|
| 256 |
+
|------|-----|---------|
|
| 257 |
+
| OSINTGPT | https://github.com/estebanpdl/osintgpt | GPT-based OSINT CLI |
|
| 258 |
+
| Robin | https://github.com/apurvsinghgautam/robin | Dark web OSINT |
|
| 259 |
+
| Perplexity Sonar OSINT | https://github.com/AXRoux/OSINT-Assistant | Perplexity-based assistant |
|
| 260 |
+
| DarkGPT | https://github.com/binaco/DarkGPT | Dark web intelligence |
|
| 261 |
+
| Maigret LLM | https://github.com/BurtTheCoder/mcp-maigret | Username OSINT via MCP |
|
| 262 |
+
| Sherlock Mail | https://github.com/Hamed233/Sherlock-Mail-AI-Powered-Email-Intelligence | Email intelligence |
|
| 263 |
+
| OSINT AI CLI | https://github.com/Mawgaming/osint-ai-cli | CLI with AI |
|
| 264 |
+
| AI Security Analyzer | https://github.com/Armaan29-09-2005/AI-OSINT-Security-Analyzer | Security analysis |
|
| 265 |
+
|
| 266 |
+
---
|
| 267 |
+
|
| 268 |
+
## Quick Reference by Use Case
|
| 269 |
+
|
| 270 |
+
### Geolocate a Photo (No Metadata)
|
| 271 |
+
1. **GeoSpy** - Most accurate, meter-level for law enforcement
|
| 272 |
+
2. **Picarta** - Good for aerial imagery
|
| 273 |
+
3. **GeoFinder** - Analyzes environmental clues
|
| 274 |
+
|
| 275 |
+
### Find Where a Face Appears Online
|
| 276 |
+
1. **FaceSeek** - Comprehensive face search
|
| 277 |
+
2. **Lenso.ai** - Multi-category including faces
|
| 278 |
+
3. **PimEyes** (see main toolkit) - Premium face search
|
| 279 |
+
|
| 280 |
+
### Discover Internet Infrastructure
|
| 281 |
+
1. **CensysGPT** - Natural language Censys queries
|
| 282 |
+
2. **ZoomEye GPT** - IoT and device discovery
|
| 283 |
+
3. **ShodanGPT** - Exposed services
|
| 284 |
+
|
| 285 |
+
### Detect Deepfakes
|
| 286 |
+
1. **Sensity.ai** - Enterprise-grade, court-ready
|
| 287 |
+
2. **DeepWare** - Video analysis
|
| 288 |
+
3. **AI Detector** - Text and image detection
|
| 289 |
+
|
| 290 |
+
### Comprehensive Investigation Platform
|
| 291 |
+
1. **Ubikron** - Browser-based capture and analysis
|
| 292 |
+
2. **Cyclect** - 475+ tools aggregated
|
| 293 |
+
3. **Taranis AI** - Self-hosted intelligence platform
|
| 294 |
+
|
| 295 |
+
---
|
| 296 |
+
|
| 297 |
+
## Notes
|
| 298 |
+
|
| 299 |
+
- Many tools have free tiers with limited searches
|
| 300 |
+
- Enterprise/law enforcement features often require verification
|
| 301 |
+
- Always cross-reference AI results with traditional methods
|
| 302 |
+
- AI geolocation works best with distinctive visual features
|
| 303 |
+
- Facial recognition may be restricted in some jurisdictions
|
skills/osint/references/country-resources.md
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Supplementary OSINT Resources
|
| 2 |
+
|
| 3 |
+
## IDI Follow The Money Toolkit
|
| 4 |
+
|
| 5 |
+
Inclusive Development International's publicly available online Follow the Money toolkit includes:
|
| 6 |
+
|
| 7 |
+
- **Open-source intelligence (OSINT) tools** for financial investigations
|
| 8 |
+
- **Development Finance Institution databases** for tracking project funding
|
| 9 |
+
- **Chinese financial actor identification databases** for tracing investments
|
| 10 |
+
- **Follow the Money investigative methods** for civil society organizations
|
| 11 |
+
|
| 12 |
+
Contact: annie@inclusivedevelopment.net or alexla@inclusivedevelopment.net
|
| 13 |
+
Website: https://www.inclusivedevelopment.net/
|
| 14 |
+
|
| 15 |
+
---
|
| 16 |
+
|
| 17 |
+
## Country-Specific OSINT Resources (DigitalDigging.org)
|
| 18 |
+
|
| 19 |
+
### Individual Country Resources
|
| 20 |
+
|
| 21 |
+
| Country | Repository |
|
| 22 |
+
|---------|------------|
|
| 23 |
+
| Armenia | https://github.com/paulpogoda/OSINT-Tools-Armenia/ |
|
| 24 |
+
| Australia | https://github.com/paulpogoda/OSINT-Tools-Australia/ |
|
| 25 |
+
| Azerbaijan | https://github.com/paulpogoda/OSINT-Tools-Azerbaijan/ |
|
| 26 |
+
| Belarus | https://github.com/int-sector/OSINT-Tools-Belarus/ |
|
| 27 |
+
| Bulgaria | https://github.com/paulpogoda/OSINT-Tools-Bulgaria/ |
|
| 28 |
+
| China | https://github.com/paulpogoda/OSINT-Tools-China |
|
| 29 |
+
| India | https://github.com/paulpogoda/OSINT-Tools-India |
|
| 30 |
+
| Iran | https://github.com/paulpogoda/OSINT-Tools-Iran |
|
| 31 |
+
| Israel | https://github.com/paulpogoda/OSINT-Tools-Israel |
|
| 32 |
+
| Kazakhstan | https://github.com/paulpogoda/OSINT-Tools-Kazakhstan |
|
| 33 |
+
| Kyrgyzstan | https://github.com/paulpogoda/OSINT-Tools-Kyrgyzstan |
|
| 34 |
+
| Netherlands | https://github.com/paulpogoda/OSINT-Tools-Netherlands/ |
|
| 35 |
+
| Poland | https://github.com/paulpogoda/OSINT-Tools-Poland |
|
| 36 |
+
| Russia | https://github.com/paulpogoda/OSINT-Tools-Russia |
|
| 37 |
+
| Syria | https://github.com/paulpogoda/OSINT-Tools-Syria |
|
| 38 |
+
| Taiwan | https://github.com/paulpogoda/OSINT-Tools-Taiwan |
|
| 39 |
+
| Tajikistan | https://github.com/paulpogoda/OSINT-Tools-Tajikistan |
|
| 40 |
+
| UAE | https://github.com/paulpogoda/OSINT-Tools-Emirates |
|
| 41 |
+
| United Kingdom | https://github.com/paulpogoda/OSINT-Tools-UK |
|
| 42 |
+
| Uzbekistan | https://github.com/paulpogoda/OSINT-Tools-Uzbekistan |
|
| 43 |
+
|
| 44 |
+
### Multi-Country & Global Resources
|
| 45 |
+
|
| 46 |
+
| Resource | Coverage | URL |
|
| 47 |
+
|----------|----------|-----|
|
| 48 |
+
| OSINT Map | Global | https://cybdetective.com/osintmap/ |
|
| 49 |
+
| OSINT International | 50+ Countries | https://start.me/p/7kDabv/osint-international |
|
| 50 |
+
| Middle East OSINT | Middle East | https://start.me/p/jj8Y9a/middle-east-osint |
|
| 51 |
+
| World (start.me) | Multiple Regions | https://start.me/p/lLaoXv/07-world |
|
| 52 |
+
| BBC Africa Eye | Africa | https://start.me/p/m6OJgv/the-bbc-africa-eye-forensics-dashboard |
|
| 53 |
+
| OhShINT Web Resources | 80+ Countries | https://github.com/OhShINT/ohshint.gitbook.io/blob/main/Lists_of_OSINT_Web_Resources/1-Complete-List-of-OSINT-Web-Resources.md |
|
| 54 |
+
| CNTY WORLD | 80+ Countries | https://start.me/p/kxNv55/cnty-world |
|
| 55 |
+
| OSINT by Country | 70+ Countries | https://start.me/p/kvAQBk/osint-resources-by-country |
|
| 56 |
+
| HBG UK/Europe | UK/Europe | https://start.me/p/VRMpYm/hbg-ukeurope-research-resources |
|
| 57 |
+
| HBG Asia/Pacific | Asia/Pacific | https://start.me/p/3KMwaw/hbg-asia-pacific-prospect-research-resources |
|
| 58 |
+
| HBG North America | North America | https://start.me/p/ZQdorV/hbg-north-america-prospect-research-links |
|
| 59 |
+
| HBG Middle East | Middle East | https://start.me/p/ZYaxaJ/hbg-middle-east-prospect-research-links |
|
| 60 |
+
|
| 61 |
+
### Country Resource Categories
|
| 62 |
+
|
| 63 |
+
Each country-specific repository typically includes:
|
| 64 |
+
|
| 65 |
+
- **Open databases and statistics** - Government data, census, public records
|
| 66 |
+
- **Company/legal/tax searches** - Business registries, court records
|
| 67 |
+
- **Maps and property records** - Cadastral maps, satellite imagery, property ownership
|
| 68 |
+
- **Vehicle information** - License plate lookups, registration databases
|
| 69 |
+
- **People search** - Names, social media, phone directories, public records
|
| 70 |
+
- **Government contracts** - Procurement databases, tender information
|
| 71 |
+
- **WHOIS lookups** - Domain registration, website ownership
|
| 72 |
+
|
| 73 |
+
### Template for New Countries
|
| 74 |
+
|
| 75 |
+
Create new country resources using: https://github.com/paulpogoda/OSINT-for-countries-V2.0/blob/main/Country%20Template
|
skills/osint/references/index.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# OSINT Documentation Index
|
| 2 |
+
|
| 3 |
+
## Categories
|
| 4 |
+
|
| 5 |
+
### AI-Tools
|
| 6 |
+
**File:** `ai-tools.md`
|
| 7 |
+
**Description:** AI-powered OSINT tools including geolocation (GeoSpy, Picarta), facial recognition (FaceSeek, Lenso.ai), infrastructure search (CensysGPT, ZoomEye GPT), deepfake detection (Sensity.ai), and multi-tool platforms (Ubikron, Cyclect).
|
| 8 |
+
|
| 9 |
+
### Maps-Satellites
|
| 10 |
+
**File:** `maps-satellites.md`
|
| 11 |
+
**Description:** Maps and satellite imagery tools
|
skills/osint/references/llms.md
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Bellingcat's Online Investigation Toolkit
|
| 2 |
+
|
| 3 |
+
## Production
|
| 4 |
+
|
| 5 |
+
- [Home](/toolkit/pages.md): A toolkit for open source researchers
|
| 6 |
+
- [New Tools](/toolkit/new-tools.md): These tools were added to the toolkit within the last four weeks.
|
| 7 |
+
- [Maps & Satellites](/toolkit/categories/maps-and-satellites.md)
|
| 8 |
+
- [Maps](/toolkit/categories/maps-and-satellites/maps.md)
|
| 9 |
+
- [Satellite Imagery](/toolkit/categories/maps-and-satellites/satellite-imagery.md)
|
| 10 |
+
- [Street View](/toolkit/categories/maps-and-satellites/street-view.md)
|
| 11 |
+
- [Geolocation](/toolkit/categories/geolocation.md)
|
| 12 |
+
- [Image/Video](/toolkit/categories/image-video.md)
|
| 13 |
+
- [Reverse Image Search](/toolkit/categories/image-video/reverse-image-search.md)
|
| 14 |
+
- [Facial Recognition](/toolkit/categories/image-video/facial-recognition.md)
|
| 15 |
+
- [Metadata](/toolkit/categories/image-video/metadata.md)
|
| 16 |
+
- [Misc](/toolkit/categories/image-video/image-misc.md)
|
| 17 |
+
- [Social Media](/toolkit/categories/social-media.md): Tools for one or more social media platforms
|
| 18 |
+
- [Facebook](/toolkit/categories/social-media/facebook.md)
|
| 19 |
+
- [Instagram](/toolkit/categories/social-media/instagram.md)
|
| 20 |
+
- [Telegram](/toolkit/categories/social-media/telegram.md)
|
| 21 |
+
- [Tiktok](/toolkit/categories/social-media/tiktok.md)
|
| 22 |
+
- [Twitter/X](/toolkit/categories/social-media/twitter.md)
|
| 23 |
+
- [Youtube](/toolkit/categories/social-media/youtube.md)
|
| 24 |
+
- [Other Platforms](/toolkit/categories/social-media/other-platforms.md): Tools for other platforms including Bluesky, Discord, Linkedin, Reddit, and Vkontakte.
|
| 25 |
+
- [Multiple Platforms](/toolkit/categories/social-media/multiple-platforms.md)
|
| 26 |
+
- [People](/toolkit/categories/people.md)
|
| 27 |
+
- [Websites](/toolkit/categories/websites.md)
|
| 28 |
+
- [Companies & Finance](/toolkit/categories/companies-and-finance.md)
|
| 29 |
+
- [Conflict](/toolkit/categories/conflict.md)
|
| 30 |
+
- [Transport](/toolkit/categories/transport.md)
|
| 31 |
+
- [Environment & Wildlife](/toolkit/categories/environment-and-wildlife.md)
|
| 32 |
+
- [Archiving](/toolkit/categories/archiving.md)
|
| 33 |
+
- [Data Organization & Analysis](/toolkit/categories/data.md)
|
| 34 |
+
- [Guides & Handbooks](/toolkit/resources/guides-and-handbooks.md)
|
| 35 |
+
- [Newsletters & Toolkits](/toolkit/resources/newsletters-and-toolkits.md)
|
| 36 |
+
- [Stay Safe](/toolkit/resources/stay-safe.md): Online security and privacy
|
| 37 |
+
- [All Tools](/toolkit/more/all-tools.md)
|
| 38 |
+
- [4plebs](/toolkit/more/all-tools/4plebs.md): Searchable archive of specific 4chan boards that makes it possible to read threads after they are purged from 4chan.
|
| 39 |
+
- [527 Explorer](/toolkit/more/all-tools/527-explorer.md): ProPublica's 527 Explorer is a database that allows users to examine the finances of organizations known as 527s in the United States, which can raise unlimited sums for political purposes.
|
| 40 |
+
- [About Maps and Satellites](/toolkit/more/all-tools/about-maps-and-satellites.md): A guide to using map and satellite tools.
|
| 41 |
+
- [ACLED](/toolkit/more/all-tools/acled.md): ACLED provides data and analysis on political violence and protest around the world, facilitating research, policy making, and journalistic reporting.
|
| 42 |
+
- [AllTrails](/toolkit/more/all-tools/alltrails.md): AllTrails.com is a tool for discovering hiking, biking, and running trails worldwide, providing detailed trail maps, user reviews, and navigation support for outdoor enthusiasts.
|
| 43 |
+
- [Apollo Mapping](/toolkit/more/all-tools/apollo-mapping.md): Image Hunter is a search engine for finding and purchasing commercial satellite imagery (including Planet, Airbus, Maxar, and many Chinese satellite companies) without a subscription.
|
| 44 |
+
- [Apple Maps](/toolkit/more/all-tools/apple-maps.md): Apple Maps is a digital mapping service with detailed, interactive maps, satellite imagery, and location-based information.
|
| 45 |
+
- [Archive.today](/toolkit/more/all-tools/archive.today.md): Archive any webpage, including Facebook and search for archived pages.
|
| 46 |
+
- [Atlos](/toolkit/more/all-tools/atlos.md): ATLOS is a platform for collaborative and large-scale open source investigations.
|
| 47 |
+
- [Auto Archiver](/toolkit/more/all-tools/auto-archiver.md): Bellingcat's tool to automatically archive social media posts, videos, and images. Free and Open-Source.
|
| 48 |
+
- [AutoStitch](/toolkit/more/all-tools/autostitch.md): Autostitch is a free tool for seamlessly combining multiple photos into a single panoramic image, making it ideal for creating wide-angle photography without needing specialized equipment.
|
| 49 |
+
- [Azure AI Video Indexer](/toolkit/more/all-tools/azure-ai-video-indexer.md): AI video tool for facial detection and other types of insights.
|
| 50 |
+
- [Baidu Maps](/toolkit/more/all-tools/baidu-maps.md): A mapping application provided by Chinese technology company Baidu Inc.
|
| 51 |
+
- [Bellingcat OpenStreetMap Search](/toolkit/more/all-tools/openstreetmap-search-tool.md): A user interface to search OpenStreetMap data for features in proximity to each other.
|
| 52 |
+
- [Bing Maps](/toolkit/more/all-tools/bing-maps.md): Bing Maps is a web mapping service provided by Microsoft that offers detailed geographical information and tools for route planning, location search, and satellite imagery.
|
| 53 |
+
- [Blackbird](/toolkit/more/all-tools/blackbird.md): Check usernames and email addresses on websites and social networks
|
| 54 |
+
- [Blender](/toolkit/more/all-tools/blender.md): Blender is an open-source 3D creation suite supporting the 3D pipeline—modeling, rigging, animation, simulation, rendering, compositing, and motion tracking, even video editing.
|
| 55 |
+
- [Bluesky Insights](/toolkit/more/all-tools/bluesky-insights.md): Bluesky Insights is a free web-based analytics tool for the Bluesky social network.
|
| 56 |
+
- [BskyFollowFinder/Bluesky network analyzer](/toolkit/more/all-tools/bluesky-network-analyzer.md): A tool that identifies which Bluesky accounts are followed by a profile’s contacts but not by that profile. Can be used for expanding networks and social graph analysis.
|
| 57 |
+
- [BskyThreadReader](/toolkit/more/all-tools/bskythreadreader.md): BskyThreadReader is a web-based Bluesky thread viewer that allows anyone to read and share Bluesky threads without logging in.
|
| 58 |
+
- [Bulletpicker.com](/toolkit/more/all-tools/bulletpicker.com.md): Bulletpicker.com is a collection of ammunition guidebooks and manuals from several different armed forces.
|
| 59 |
+
- [CAT UXO](/toolkit/more/all-tools/cat-uxo.md): A repository for professionals working in the explosive ordnance disposal (EOD) space.
|
| 60 |
+
- [China-related resources](/toolkit/more/all-tools/china-related-resources.md): Resources for research on companies in China.
|
| 61 |
+
- [Chronotrains](/toolkit/more/all-tools/chronotrains.md): Chronotrains is a free interactive map designed to explore the reach of Europe’s extensive rail network. Enter a starting point and travel time to see reachable destinations by train.
|
| 62 |
+
- [CITES Trade Database](/toolkit/more/all-tools/cites-trade-database.md): Around 23 million records of trade in wildlife since 1975.
|
| 63 |
+
- [Companies House](/toolkit/more/all-tools/companies-house.md): Search companies and individuals in the United Kingdom.
|
| 64 |
+
- [Convert Geographic Units](/toolkit/more/all-tools/convert-geographic-units.md): A tool that converts various geographic coordinates to support diverse mapping and spatial analysis needs.
|
| 65 |
+
- [Datawrapper](/toolkit/more/all-tools/datawrapper.md): A tool for creating interactive charts, maps, and tables from your data, offering a user-friendly interface for visualizing information.
|
| 66 |
+
- [DeHashed](/toolkit/more/all-tools/dehashed.md): A platform that maintains a database of compromised credentials, with a newly-launched web domain registration search tool.
|
| 67 |
+
- [Discord Chat Exporter](/toolkit/more/all-tools/discord-chat-exporter.md): A tool for exporting Discord chat logs in multiple formats.
|
| 68 |
+
- [DiscordLeaks](/toolkit/more/all-tools/discordleaks.md): Search hundreds of thousands of messages leaked from 290+ white-supremacist / nazi discord servers.
|
| 69 |
+
- [Distill.io](/toolkit/more/all-tools/distill.md): Distill.io is a website change monitoring tool that allows users to track changes on web pages.
|
| 70 |
+
- [DomainTools Whois Lookup](/toolkit/more/all-tools/domaintools-whois-lookup.md): DomainTools Whois provides detailed domain name registration information, and can be used to investigate details about domains or IP addresses.
|
| 71 |
+
- [Earth Explorer](/toolkit/more/all-tools/earth-explorer.md): "Query and order satellite images, aerial photographs, and cartographic products"
|
| 72 |
+
- [Earth Online](/toolkit/more/all-tools/earth-online.md): The ESA's Earth Online product offers a portal for accessing satellite imagery and environmental data, supporting a range of applications from climate monitoring to natural disaster assessment.
|
| 73 |
+
- [EDGAR Suite](/toolkit/more/all-tools/edgar-suite.md): Tool for the retrieval of corporate and financial data from SEC's EDGAR (Electronic Data Gathering, Analysis, and Retrieval) database.
|
| 74 |
+
- [EDGAR](/toolkit/more/all-tools/edgar.md): Database of corporate filings for the US
|
| 75 |
+
- [EIA Global Environmental Crime Tracker](/toolkit/more/all-tools/eia-global-environmental-crime-tracker.md): Map/tracking of environmental crimes including trade in ivory, rhino, big cats, and other exotic animals.
|
| 76 |
+
- [Environmental Justice Atlas](/toolkit/more/all-tools/environmental-justice-atlas.md): Maps environmental conflicts around the world to provide information about who is involved, their impact and other information.
|
| 77 |
+
- [Equasis](/toolkit/more/all-tools/equasis.md): Equasis provides vessel ownership and safety records, as well as shipping company fleet information.
|
| 78 |
+
- [Etherscan](/toolkit/more/all-tools/etherscan.md): An explorer that allows researchers to track wallets, transactions and more on the Ethereum blockchain.
|
| 79 |
+
- [EU consolidated corporate registers](/toolkit/more/all-tools/eu-consolidated-corporate-registers.md): Consolidated company registers covering most of the EU, Iceland, Liechtenstein and Norway.
|
| 80 |
+
- [EU Sanctions Map](/toolkit/more/all-tools/eu-sanctions-map.md): Database of sanctions imposed by the European Union
|
| 81 |
+
- [ExifTool](/toolkit/more/all-tools/exiftool.md): ExifTool is a command-line application for reading, writing, and editing meta information in files.
|
| 82 |
+
- [F4Map](/toolkit/more/all-tools/f4map.md): F4Map is an interactive 3D map visualization tool that provides detailed rendering of urban landscapes and geographical features.
|
| 83 |
+
- [Face Comparison by ToolPie](/toolkit/more/all-tools/face-comparison-toolpie.md): Compares two human face photos to determine similarity.
|
| 84 |
+
- [FaceCheck.ID](/toolkit/more/all-tools/facecheck.id.md): A facial recognition search engine that tries to find photos of people that look similar to a person of interest.
|
| 85 |
+
- [FlightAware](/toolkit/more/all-tools/flightaware.md): FlightAware is a global flight-tracking platform that provides real-time data on aircraft movements. It offers live tracking, historical data, and predictive analytics via its website and apps.
|
| 86 |
+
- [Flightradar24](/toolkit/more/all-tools/flightradar24.md): Flightradar24, a real-time flight tracking service, that provides comprehensive information about aircraft positions, flight numbers, routes, historical data, detailed aircraft specifications.
|
| 87 |
+
- [Forensically](/toolkit/more/all-tools/forensically.md): A collection of web-based image forensics tools. Can identify fake or doctored images.
|
| 88 |
+
- [Gaode Maps](/toolkit/more/all-tools/gaode-maps.md): Gaode Maps (also known as AMap) is a mapping application and technology from the Chinese company Alibaba.
|
| 89 |
+
- [Geo Data Tool](/toolkit/more/all-tools/geo-data-tool.md): IP geolocation service to identify the location and other technical information associated to IP addresses.
|
| 90 |
+
- [GeoHints](/toolkit/more/all-tools/geohints.md): GeoHints is a website that provides information about things like traffic lights, utility poles, bollards etc. for different regions of the world to help geolocate a location.
|
| 91 |
+
- [Gephi](/toolkit/more/all-tools/gephi.md): Open-source network analysis and visualization software
|
| 92 |
+
- [Ghunt](/toolkit/more/all-tools/ghunt.md): A command line tool for obtaining information about Google accounts.
|
| 93 |
+
- [Global Fishing Watch Map](/toolkit/more/all-tools/global-fishing-watch-map.md): The Global Fishing Watch Map is a digital platform for investigating fishing activities worldwide by utilising satellite and AIS data.
|
| 94 |
+
- [Global Forest Watch](/toolkit/more/all-tools/global-forest-watch.md): Explore tree cover loss and gain data, recent deforestation and fire alerts, land use designations, carbon emissions, biodiversity metrics and more.
|
| 95 |
+
- [Global Monitoring System - ECOSOLVE](/toolkit/more/all-tools/global-monitoring-system.md): Illicit online wildlife markets data from Brazil, South Africa and Thailand.
|
| 96 |
+
- [Global Suppliers Online](/toolkit/more/all-tools/global-suppliers-online.md): A site dedicated to connect suppliers and buyers of goods from all over the world.
|
| 97 |
+
- [Google Earth Engine](/toolkit/more/all-tools/google-earth-engine.md): Google Earth Engine is a platform for environmental monitoring and analysis through satellite imagery and geospatial data.
|
| 98 |
+
- [Google Earth Pro](/toolkit/more/all-tools/google-earth-pro.md): Google Earth is a geospatial tool that provides detailed, global satellite imagery, maps, 3D terrain models, and the ability to explore geographic data interactively.
|
| 99 |
+
- [Google Flood Hub](/toolkit/more/all-tools/google-flood-hub.md): A visual tool to monitor river levels and forecast floods based on AI models developed by Google Research.
|
| 100 |
+
- [Google Lens](/toolkit/more/all-tools/google-lens.md): Google Lens is an image recognition tool which can be used to identify locations or objects in photographs.
|
| 101 |
+
- [Google Maps](/toolkit/more/all-tools/google-maps.md): Google Maps provides mapping information, satellite imagery and Google Street View imagery including historical Street View images.
|
| 102 |
+
- [GovMap](/toolkit/more/all-tools/govmap.md): GovMap provides an interactive map of Israel, offering users a wide range of data including property boundaries, planning information, and infrastructure details.
|
| 103 |
+
- [GPSJam](/toolkit/more/all-tools/gpsjam.md): GPSJam.org is a daily map that visualizes the GPS/GNSS disruptions on aircraft worldwide. It collects and presents 24-hour data showing areas experiencing interference.
|
| 104 |
+
- [Have I Been Pwned](/toolkit/more/all-tools/have-i-been-pwned.md): Does an email or a phone number appear in data breaches?
|
| 105 |
+
- [Hitta.se](/toolkit/more/all-tools/hitta.se.md): Hitta.se is a comprehensive Swedish search engine and map that provides detailed information and contact details for businesses and individuals across Sweden.
|
| 106 |
+
- [Hoaxy](/toolkit/more/all-tools/hoaxy.md): Hoaxy is a web-based search and visualization tool. It helps visualize the spread of information on Bluesky and X (Twitter).
|
| 107 |
+
- [Hugin](/toolkit/more/all-tools/hugin.md): Hugin is a free and open-source panorama photo stitching and HDR (High Dynamic Range imaging) merging software that helps users create seamless panoramic images from multiple photographs.
|
| 108 |
+
- [Hunchly](/toolkit/more/all-tools/hunchly.md): An archiving tool that tracks online activities and preserves essential information about the web pages researchers visit.
|
| 109 |
+
- [ICANN Lookup](/toolkit/more/all-tools/icann-lookup.md): This tool allows you to search for the current registration data of internet domain names.
|
| 110 |
+
- [ICIJ Offshore Leaks Database](/toolkit/more/all-tools/icij-offshore-leaks-database.md): A database providing otherwise secret information about more than 810k offshore companies, foundations, and trusts based on leaks like the Panama Papers or the Paradise Papers.
|
| 111 |
+
- [IDN Checker](/toolkit/more/all-tools/idn-checker.md): IDN Checker detects visually similar versions of a domain.
|
| 112 |
+
- [ImportGenius](/toolkit/more/all-tools/importgenius.md): Commercial supplier of trade data for 23 countries. Paid service but journalists can ask for free access.
|
| 113 |
+
- [ImportYeti](/toolkit/more/all-tools/importyeti.md): Search US customs inbound sea shipment records, find company suppliers.
|
| 114 |
+
- [Index Database](/toolkit/more/all-tools/index-database.md): A database of remote sensing indices and satellite imaging sensors
|
| 115 |
+
- [Instagram Location Search](/toolkit/more/all-tools/instagram-location-search.md): A command line tool that allows users to find location tags near a specified latitude and longitude.
|
| 116 |
+
- [InstaLoader](/toolkit/more/all-tools/instaloader.md): Download pictures or videos (with metadata) from Instagram.
|
| 117 |
+
- [Instant Data Scraper](/toolkit/more/all-tools/instant-data-scraper.md): Browser extension for simple web scraping, with table output.
|
| 118 |
+
- [Intelx.io](/toolkit/more/all-tools/intelx.io.md): Find user details in data breaches
|
| 119 |
+
- [InVID](/toolkit/more/all-tools/invid.md): A toolkit that supports the verification of videos and images.
|
| 120 |
+
- [KartaView](/toolkit/more/all-tools/kartaview.md): KartaView is a crowdsourced platform for street view imagery.
|
| 121 |
+
- [Leak-Lookup](/toolkit/more/all-tools/leak-lookup.md): An online tool that allows you to search across public data breaches to surface credentials that may have been compromised.
|
| 122 |
+
- [License Plate Maps](/toolkit/more/all-tools/license-plate-maps.md): Collection of tools and maps for discerning license plates by country
|
| 123 |
+
- [LinkdTime](/toolkit/more/all-tools/linkdtime.md): Build a clean timeline of any LinkedIn activity from a single URL or a whole list of links.
|
| 124 |
+
- [LittleSis](/toolkit/more/all-tools/littlesis.md): Connects dots between influential / wealthy individuals in (mostly US) politics and business.
|
| 125 |
+
- [Liveuamap](/toolkit/more/all-tools/liveuamap.md): LiveUAMap is a mapping tool that provides up-to-date information on global geopolitical events, conflicts, and crises.
|
| 126 |
+
- [Locust Hub](/toolkit/more/all-tools/locust-hub.md): A repository for desert locust data with maps and other resources for tracking movements, early detection and planning locust control interventions.
|
| 127 |
+
- [Logseq](/toolkit/more/all-tools/logseq.md): Logseq is an open-source knowledge management tool that enables users to organize their notes, tasks, and projects.
|
| 128 |
+
- [Lumen](/toolkit/more/all-tools/lumen.md): A research project collecting and publishing legal takedown notices for online content transparency
|
| 129 |
+
- [Maigret](/toolkit/more/all-tools/maigret.md): Maigret is a Python script that retrieves user information by searching for usernames across various websites and social media platforms.
|
| 130 |
+
- [Maltego Graph](/toolkit/more/all-tools/maltego.md): Maltego Graph is an investigation platform that combines two things at once: (1) It acts as a search tool, and (2) It creates a graph establishing links between data you uncover from your search.
|
| 131 |
+
- [MapChecking](/toolkit/more/all-tools/mapchecking.md): This tool helps you estimate and fact-check the maximum number of people standing in a given area.
|
| 132 |
+
- [Mapillary](/toolkit/more/all-tools/mapillary.md): Mapillary is a crowdsourced street-level imagery platform.
|
| 133 |
+
- [MapSwitcher](/toolkit/more/all-tools/mapswitcher.md): Chrome extension switches between online map apps, maintaining (as far as possible) the map centre, zoom level, & directions of the source map.
|
| 134 |
+
- [MarineTraffic](/toolkit/more/all-tools/marinetraffic.md): An open, community-based project, providing (near) real-time information on the movements of ships and their locations in harbours and ports.
|
| 135 |
+
- [Merlin](/toolkit/more/all-tools/merlin.md): Identify birds (visually), through an app.
|
| 136 |
+
- [Meta Content Library](/toolkit/more/all-tools/meta-content-library.md): Meta Content Library is a controlled-access tool that lets approved academic and non-profit researchers search the full public archive of Facebook, Instagram, and Threads posts, in near-real-time.
|
| 137 |
+
- [MW Geofind](/toolkit/more/all-tools/mw-geofind.md): MW Geofind is a tool for finding geotagged YouTube videos.
|
| 138 |
+
- [Name Variant Search](/toolkit/more/all-tools/name-variant-search.md): Simple tool to help search for different ways of writing a name.
|
| 139 |
+
- [Namechk](/toolkit/more/all-tools/namechk.md): A username and domain search tool that checks on which platforms or domain a given username is registered.
|
| 140 |
+
- [NASA Firms](/toolkit/more/all-tools/nasa-firms.md): Displays a world map overlaid with infra-red data from one or more satellites, some, but not all of which may represent heat from fires and explosions.
|
| 141 |
+
- [NASA Worldview](/toolkit/more/all-tools/nasa-worldview.md): NASA Worldview is an online tool for visualizing and downloading near real-time satellite imagery and scientific data of Earth's atmosphere, land, and oceans.
|
| 142 |
+
- [Navtex](/toolkit/more/all-tools/navtex.md): A historical database providing vital navigational and meteorological warnings, forecasts, and urgent maritime safety information to ships.
|
| 143 |
+
- [NeutrOSINT](/toolkit/more/all-tools/neutrosint.md): A tool for investigating Proton Mail addresses.
|
| 144 |
+
- [North Data](/toolkit/more/all-tools/north-data.md): Search for people and companies in EU corporate and trade registers + visualize relationships
|
| 145 |
+
- [Obsidian](/toolkit/more/all-tools/obsidian.md): A knowledge management and note-taking app with extensive customization options.
|
| 146 |
+
- [OCCRP Aleph](/toolkit/more/all-tools/occrp-aleph.md): Aleph offers a way to research sanctions lists, corporate registries, leaks, and more
|
| 147 |
+
- [Open Measures](/toolkit/more/all-tools/open-measures.md): Open Measures helps open source researchers investigate harmful online activity such as extremism and disinformation.
|
| 148 |
+
- [Open Ownership](/toolkit/more/all-tools/open-ownership.md): Links to beneficial ownership registers.
|
| 149 |
+
- [Open Source Munitions Portal](/toolkit/more/all-tools/open-source-munitions-portal.md): A searchable library of verified images for researchers, journalists, and practitioners trying to learn more about munitions and their use and impact in conflict.
|
| 150 |
+
- [OpenCorporates](/toolkit/more/all-tools/opencorporates.md): Comprehensive repository of company registries around the world
|
| 151 |
+
- [OpenSanctions](/toolkit/more/all-tools/opensanctions.md): Open-source international database of sanctions data, persons of interest and politically exposed persons.
|
| 152 |
+
- [OpenSecrets](/toolkit/more/all-tools/opensecrets.md): Data on campaign finance, lobbying, and spending in U.S. politics
|
| 153 |
+
- [OrbTrack](/toolkit/more/all-tools/orbtrack.md): Predicts & describes the position & path of >10K satellites in Earth orbit, relative to points on the earth's surface input by the user, for 5 days ahead, + International Space Station video feed.
|
| 154 |
+
- [Osint Tools Map](/toolkit/more/all-tools/osint-tools-map.md): An interactive worldwide map, showcasing business registries, court records, and other publicly available information to aid OSINT investigations and research.
|
| 155 |
+
- [Overpass Turbo](/toolkit/more/all-tools/overpass-turbo.md): Overpass Turbo is a web-based tool for querying and visualizing OpenStreetMap crowd sourced data, aiding in extracting specific information like locations of amenities e.g.hospitals.
|
| 156 |
+
- [PeakVisor](/toolkit/more/all-tools/peakvisor.md): Dual window views for any global location: (1) a 2-D map & (2) a 3-D rendered terrain model, with photo fitting, shade/slope mapping, sun trails & weather data. In active development for OS research.
|
| 157 |
+
- [PimEyes](/toolkit/more/all-tools/pimeyes.md): An AI-powered facial recognition reverse image search tool.
|
| 158 |
+
- [Pinpoint](/toolkit/more/all-tools/pinpoint.md): A tool by Google to catalogue uploaded documents and files, providing OCR, indexing, and other services. Full access only granted to journalists, academic researchers and university students.
|
| 159 |
+
- [PixPlot](/toolkit/more/all-tools/pixplot.md): PixPlot is a tool that utilizes machine learning and WebGL to provide an interactive visualization of large image collections, allowing users to explore patterns and outliers within image datasets.
|
| 160 |
+
- [Planet Labs](/toolkit/more/all-tools/planet-labs.md): Planet Labs PBC is an American optical satellite imagery company that sells access to imagery.
|
| 161 |
+
- [Police Records Access Project](/toolkit/more/all-tools/police-records-access-project.md): A database providing searchable access to California law enforcement records including police use-of-force incidents, shootings, and misconduct cases.
|
| 162 |
+
- [PublicWWW](/toolkit/more/all-tools/publicwww.md): PublicWWW is a source code search engine that allows you to search for any alphanumeric snippet, signature, or keyword within the HTML, JavaScript, and CSS code of millions of web pages.
|
| 163 |
+
- [QGIS](/toolkit/more/all-tools/qgis.md): QGIS is a free Open Source Geographic Information System (GIS).
|
| 164 |
+
- [Quick geolocation search](/toolkit/more/all-tools/quick-geolocation-search.md): A tool that brings several maps into one place for easy location search.
|
| 165 |
+
- [Radar Interference Tracker](/toolkit/more/all-tools/radar-interference-tracker.md): Bellingcat's radar interference tracker can be used to locate and monitor active military radar systems.
|
| 166 |
+
- [RAMMB SLIDER](/toolkit/more/all-tools/rammb-slider.md): Real-time weather satellites of the entire globe
|
| 167 |
+
- [RAWGraphs](/toolkit/more/all-tools/rawgraphs.md): RAWGraphs is an open-source data visualization tool designed for non-technical users, enabling the creation of customizable, editable charts without coding skills.
|
| 168 |
+
- [RootAbout](/toolkit/more/all-tools/rootabout.md): RootAbout is a reverse image search tool that pulls indexed images from the Internet Archive.
|
| 169 |
+
- [RuPEP](/toolkit/more/all-tools/rupep.md): Online database of politically exposed persons in Russia, Belarus and Kazakhstan.
|
| 170 |
+
- [SanctionsExplorer](/toolkit/more/all-tools/sanctionsexplorer.md): A comprehensive database of current and historical OFAC/UN/EU sanctions
|
| 171 |
+
- [satellites.pro](/toolkit/more/all-tools/satellites.pro.md): Satellites.pro allows open source researchers to quickly switch between several free satellite imagery and mapping services.
|
| 172 |
+
- [Search by Image](/toolkit/more/all-tools/search-by-image.md): A browser extension to reverse search an image on multiple search engines.
|
| 173 |
+
- [Search4Faces](/toolkit/more/all-tools/search4faces.md): Upload the picture of a face and find pictures of similar looking people on VKontakte, Odnoklassniki, TikTok and Clubhouse.
|
| 174 |
+
- [Sentinel Hub Playground](/toolkit/more/all-tools/sentinal-hub-playground.md): A free web-based platform for viewing, analyzing, and downloading satellite imagery from the European Space Agency's Sentinel missions, with data updated every 5-10 days.
|
| 175 |
+
- [ShadeMap](/toolkit/more/all-tools/shademap.md): ShadeMap is a global simulation of mountain, building & tree shadows for a given date & time. Base data is free, but users can buy 30cm accurate data per sq km for areas of special focus.
|
| 176 |
+
- [Shadow Finder](/toolkit/more/all-tools/shadow-finder.md): To analyse shadows in source imagery, Shadow Finder maps all points on the earth where a shadow of given length could occur at a given date & time, IF the height of the object casting it is known.
|
| 177 |
+
- [ShadowMap](/toolkit/more/all-tools/shadowmap.md): Tilting global map of 3D buildlings and the shadows they cast at a specific time of the day, but date fixed at "today" in free version. Paid versions allow date change and offer 3D models.
|
| 178 |
+
- [Sherlock](/toolkit/more/all-tools/sherlock.md): Check usernames across more than 400 websites and social networks.
|
| 179 |
+
- [ShipFinder](/toolkit/more/all-tools/shipfinder.md): ShipFinder is an application designed to track vessels in near real-time across the globe.
|
| 180 |
+
- [Skopenow](/toolkit/more/all-tools/skopenow.md): Social Media Investigations - name, phone, email, username searches
|
| 181 |
+
- [SkyFi](/toolkit/more/all-tools/skyfi.md): SkyFi is used to purchase commercial satellite imagery and task (order the collection of images) satellites without a subscription.
|
| 182 |
+
- [Snap Map](/toolkit/more/all-tools/snap-map.md): Searchable map of geotagged snaps.
|
| 183 |
+
- [Species+](/toolkit/more/all-tools/species+.md): Centralized website with vulnerable species information.
|
| 184 |
+
- [Strava](/toolkit/more/all-tools/strava.md): Social media fitness app with exercise map based on users' GPS data.
|
| 185 |
+
- [Suncalc](/toolkit/more/all-tools/suncalc.md): Suncalc models the relationship between the date, time of day, the geographic location of a place, and the position of the sun in the sky, together with the length & direction of the shadows it casts.
|
| 186 |
+
- [Telegago](/toolkit/more/all-tools/telegago.md): Telegago is a Google Custom Search Engine tailored for searching public Telegram content for OSINT purposes.
|
| 187 |
+
- [Telegram Group Joiner](/toolkit/more/all-tools/telegram-group-joiner.md): Automate joining multiple Telegram groups and channels, ideal for researchers monitoring specific topics.
|
| 188 |
+
- [Telegram Phone Number Checker](/toolkit/more/all-tools/telegram-phone-number-checker.md): Command line tool for checking if phone numbers are connected to Telegram accounts and retrieving related information where available.
|
| 189 |
+
- [TelegramDB](/toolkit/more/all-tools/telegramdb.md): TelegramDB is a searchable database service that allows users to explore public Telegram groups and channels via a dedicated bot.
|
| 190 |
+
- [Telemetrio](/toolkit/more/all-tools/telemetrio.md): Telemetr.io offers a range of Telegram-related services based on a catalog of Telegram channels: country and category-specific rankings, curated collections, real-time event tracking, and an API.
|
| 191 |
+
- [Telemetry](/toolkit/more/all-tools/telemetry.md): An analytical search tool for Telegram groups and channels.
|
| 192 |
+
- [Telepathy](/toolkit/more/all-tools/telepathy.md): Telepathy is a versatile Telegram toolkit for OSINT analysts, enabling chat archiving, memberlist gathering, user location lookup, top poster analysis, message mapping, and more.
|
| 193 |
+
- [Tencent Maps](/toolkit/more/all-tools/tencent-maps.md): Tencent Maps is a mapping service provided by the Chinese technology company Tencent. Tencent Maps provides maps, satellite imagery, and street view.
|
| 194 |
+
- [TGStat](/toolkit/more/all-tools/tgstat.md): TGStat is a web-based analytics tool for Telegram that monitors active channels and provides profile analytics and statistics. It tracks channel subscribers’ growth rate, reach, and citation index.
|
| 195 |
+
- [The Information Laundromat](/toolkit/more/all-tools/the-information-laundromat.md): A tool for analyzing content replication and site architecture to detect information laundering.
|
| 196 |
+
- [TinEye](/toolkit/more/all-tools/tineye.md): TinEye is a search engine that allows the user to search using images (reverse image search).
|
| 197 |
+
- [TrueCaller](/toolkit/more/all-tools/truecaller.md): Truecaller is a caller ID app that identifies incoming calls, blocks unwanted numbers, and gathers phone numbers and names from contact lists. It also performs a reverse phone number search.
|
| 198 |
+
- [TruffleHog](/toolkit/more/all-tools/trufflehog.md): Find leaked credentials.
|
| 199 |
+
- [Twitter Advanced Search](/toolkit/more/all-tools/twitter-advanced-search.md): Twitter/X Advanced Search is X's own tool to help users find more precise information on the platform by filtering posts according to criteria such as location, user, date or popularity.
|
| 200 |
+
- [Twitter Location Search](/toolkit/more/all-tools/twitter-location-search.md): Discover real-time conversations and trends in any area with X's built-in location search. Search by latitude and longitude coordinates or distance for targeted local content.
|
| 201 |
+
- [Twitter Video Downloader](/toolkit/more/all-tools/twitter-video-downloader.md): Download videos from X (formerly Twitter) by converting tweet URLs into downloadable video links.
|
| 202 |
+
- [Umbra Space](/toolkit/more/all-tools/umbra-space.md): Umbra is an American synthetic aperture radar (SAR) satellite imaging company that sells on-demand taskings for satellite imagery.
|
| 203 |
+
- [UN Comtrade Database](/toolkit/more/all-tools/un-comtrade-database.md): United Nations free database of global trade.
|
| 204 |
+
- [UNOSAT Analyses](/toolkit/more/all-tools/unosat-analyses.md): UNOSAT Analyses is a tool that maps humanitarian emergencies across the globe utilising United Nations Satellite Centre data.
|
| 205 |
+
- [Uwazi](/toolkit/more/all-tools/uwazi.md): Uwazi is an open-source platform that simplifies the management of document collections, particularly for human rights documentation in the justice and advocacy fields.
|
| 206 |
+
- [VesselFinder](/toolkit/more/all-tools/vesselfinder.md): Live marine vessel tracker
|
| 207 |
+
- [Wayback Machine](/toolkit/more/all-tools/internet-archive.md): The Internet Archive's Wayback Machine lets users view and archive web pages, aiding in historical research and digital preservation.
|
| 208 |
+
- [Web Archives](/toolkit/more/all-tools/web-archives.md): A browser extension to view archived and cached versions of a website on multiple archiving sites.
|
| 209 |
+
- [What CMS](/toolkit/more/all-tools/what-cms.md): A tool that you can use to identify the technologies used to power a website.
|
| 210 |
+
- [what3words](/toolkit/more/all-tools/what3words.md): A proprietary geocode system which identifies any location on the surface of the earth to a resolution of 3 metres. The identifier is a unique combination of three words, available in 60 languagues.
|
| 211 |
+
- [WhatsMyName](/toolkit/more/all-tools/whats-my-name.md): Search for usernames on several hundred platforms
|
| 212 |
+
- [Who posted what?](/toolkit/more/all-tools/who-posted-what.md): A tool that allows a keyword search on Facebook on a specific date or within a specific time frame.
|
| 213 |
+
- [Whoxy](/toolkit/more/all-tools/whoxy.md): Whoxy is a domain search engine or "whois lookup" tool to find (the history of) registration information on a domain, such as the registrar, the status of the domain and the date of registration.
|
| 214 |
+
- [Wikimapia](/toolkit/more/all-tools/wikimapia.md): Wikimapia was a long-running collaborative mapping project that remains partially accessible, providing open source researchers with a unique database of historical, user-generated content.
|
| 215 |
+
- [Wikipedia list of registers](/toolkit/more/all-tools/wikipedia-list-of-registers.md): Wikipedia list of official business registers around the world.
|
| 216 |
+
- [WildEye](/toolkit/more/all-tools/wildeye.md): Tracking tool for data on environmental and wildlife crime cases, including court cases and convictions, across the globe.
|
| 217 |
+
- [Wildlife Trade Portal](/toolkit/more/all-tools/wildlife-trade-portal.md): An open-source tool to search wildlife seizure data worldwide.
|
| 218 |
+
- [World Database on Protected and Conserved Areas](/toolkit/more/all-tools/world-database-protected-areas.md): A comprehensive global database on terrestrial and marine protected areas.
|
| 219 |
+
- [xIFr](/toolkit/more/all-tools/xifr.md): A Firefox add-on for extracting EXIF metadata by right-clicking an image.
|
| 220 |
+
- [Yandex Maps](/toolkit/more/all-tools/yandex-maps.md): A platform offering detailed maps, satellite imagery, street views (static & sometimes dynamic imagery, including aerial views). Often the best available data on Russia & surrounding regions.
|
| 221 |
+
- [Zeeschuimer](/toolkit/more/all-tools/zeeschuimer.md): Zeeschuimer is a browser extension for collecting social media posts that are visible in your web browser, enabling systematic analysis of content from platforms that are hard to scrape via APIs.
|
| 222 |
+
- [Zotero](/toolkit/more/all-tools/zotero.md): A tool for collecting, organizing, annotating, citing, and sharing research sources.
|
skills/osint/references/maps-satellites.md
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Osint - Maps-Satellites
|
| 2 |
+
|
| 3 |
+
**Pages:** 1
|
| 4 |
+
|
| 5 |
+
---
|
| 6 |
+
|
| 7 |
+
## Bellingcat's Online Investigation Toolkit
|
| 8 |
+
|
| 9 |
+
**URL:** llms-txt#bellingcat's-online-investigation-toolkit
|
| 10 |
+
|
| 11 |
+
**Contents:**
|
| 12 |
+
- Production
|
| 13 |
+
|
| 14 |
+
- [Home](/toolkit/pages.md): A toolkit for open source researchers
|
| 15 |
+
- [New Tools](/toolkit/new-tools.md): These tools were added to the toolkit within the last four weeks.
|
| 16 |
+
- [Maps & Satellites](/toolkit/categories/maps-and-satellites.md)
|
| 17 |
+
- [Maps](/toolkit/categories/maps-and-satellites/maps.md)
|
| 18 |
+
- [Satellite Imagery](/toolkit/categories/maps-and-satellites/satellite-imagery.md)
|
| 19 |
+
- [Street View](/toolkit/categories/maps-and-satellites/street-view.md)
|
| 20 |
+
- [Geolocation](/toolkit/categories/geolocation.md)
|
| 21 |
+
- [Image/Video](/toolkit/categories/image-video.md)
|
| 22 |
+
- [Reverse Image Search](/toolkit/categories/image-video/reverse-image-search.md)
|
| 23 |
+
- [Facial Recognition](/toolkit/categories/image-video/facial-recognition.md)
|
| 24 |
+
- [Metadata](/toolkit/categories/image-video/metadata.md)
|
| 25 |
+
- [Misc](/toolkit/categories/image-video/image-misc.md)
|
| 26 |
+
- [Social Media](/toolkit/categories/social-media.md): Tools for one or more social media platforms
|
| 27 |
+
- [Facebook](/toolkit/categories/social-media/facebook.md)
|
| 28 |
+
- [Instagram](/toolkit/categories/social-media/instagram.md)
|
| 29 |
+
- [Telegram](/toolkit/categories/social-media/telegram.md)
|
| 30 |
+
- [Tiktok](/toolkit/categories/social-media/tiktok.md)
|
| 31 |
+
- [Twitter/X](/toolkit/categories/social-media/twitter.md)
|
| 32 |
+
- [Youtube](/toolkit/categories/social-media/youtube.md)
|
| 33 |
+
- [Other Platforms](/toolkit/categories/social-media/other-platforms.md): Tools for other platforms including Bluesky, Discord, Linkedin, Reddit, and Vkontakte.
|
| 34 |
+
- [Multiple Platforms](/toolkit/categories/social-media/multiple-platforms.md)
|
| 35 |
+
- [People](/toolkit/categories/people.md)
|
| 36 |
+
- [Websites](/toolkit/categories/websites.md)
|
| 37 |
+
- [Companies & Finance](/toolkit/categories/companies-and-finance.md)
|
| 38 |
+
- [Conflict](/toolkit/categories/conflict.md)
|
| 39 |
+
- [Transport](/toolkit/categories/transport.md)
|
| 40 |
+
- [Environment & Wildlife](/toolkit/categories/environment-and-wildlife.md)
|
| 41 |
+
- [Archiving](/toolkit/categories/archiving.md)
|
| 42 |
+
- [Data Organization & Analysis](/toolkit/categories/data.md)
|
| 43 |
+
- [Guides & Handbooks](/toolkit/resources/guides-and-handbooks.md)
|
| 44 |
+
- [Newsletters & Toolkits](/toolkit/resources/newsletters-and-toolkits.md)
|
| 45 |
+
- [Stay Safe](/toolkit/resources/stay-safe.md): Online security and privacy
|
| 46 |
+
- [All Tools](/toolkit/more/all-tools.md)
|
| 47 |
+
- [4plebs](/toolkit/more/all-tools/4plebs.md): Searchable archive of specific 4chan boards that makes it possible to read threads after they are purged from 4chan.
|
| 48 |
+
- [527 Explorer](/toolkit/more/all-tools/527-explorer.md): ProPublica's 527 Explorer is a database that allows users to examine the finances of organizations known as 527s in the United States, which can raise unlimited sums for political purposes.
|
| 49 |
+
- [About Maps and Satellites](/toolkit/more/all-tools/about-maps-and-satellites.md): A guide to using map and satellite tools.
|
| 50 |
+
- [ACLED](/toolkit/more/all-tools/acled.md): ACLED provides data and analysis on political violence and protest around the world, facilitating research, policy making, and journalistic reporting.
|
| 51 |
+
- [AllTrails](/toolkit/more/all-tools/alltrails.md): AllTrails.com is a tool for discovering hiking, biking, and running trails worldwide, providing detailed trail maps, user reviews, and navigation support for outdoor enthusiasts.
|
| 52 |
+
- [Apollo Mapping](/toolkit/more/all-tools/apollo-mapping.md): Image Hunter is a search engine for finding and purchasing commercial satellite imagery (including Planet, Airbus, Maxar, and many Chinese satellite companies) without a subscription.
|
| 53 |
+
- [Apple Maps](/toolkit/more/all-tools/apple-maps.md): Apple Maps is a digital mapping service with detailed, interactive maps, satellite imagery, and location-based information.
|
| 54 |
+
- [Archive.today](/toolkit/more/all-tools/archive.today.md): Archive any webpage, including Facebook and search for archived pages.
|
| 55 |
+
- [Atlos](/toolkit/more/all-tools/atlos.md): ATLOS is a platform for collaborative and large-scale open source investigations.
|
| 56 |
+
- [Auto Archiver](/toolkit/more/all-tools/auto-archiver.md): Bellingcat's tool to automatically archive social media posts, videos, and images. Free and Open-Source.
|
| 57 |
+
- [AutoStitch](/toolkit/more/all-tools/autostitch.md): Autostitch is a free tool for seamlessly combining multiple photos into a single panoramic image, making it ideal for creating wide-angle photography without needing specialized equipment.
|
| 58 |
+
- [Azure AI Video Indexer](/toolkit/more/all-tools/azure-ai-video-indexer.md): AI video tool for facial detection and other types of insights.
|
| 59 |
+
- [Baidu Maps](/toolkit/more/all-tools/baidu-maps.md): A mapping application provided by Chinese technology company Baidu Inc.
|
| 60 |
+
- [Bellingcat OpenStreetMap Search](/toolkit/more/all-tools/openstreetmap-search-tool.md): A user interface to search OpenStreetMap data for features in proximity to each other.
|
| 61 |
+
- [Bing Maps](/toolkit/more/all-tools/bing-maps.md): Bing Maps is a web mapping service provided by Microsoft that offers detailed geographical information and tools for route planning, location search, and satellite imagery.
|
| 62 |
+
- [Blackbird](/toolkit/more/all-tools/blackbird.md): Check usernames and email addresses on websites and social networks
|
| 63 |
+
- [Blender](/toolkit/more/all-tools/blender.md): Blender is an open-source 3D creation suite supporting the 3D pipeline—modeling, rigging, animation, simulation, rendering, compositing, and motion tracking, even video editing.
|
| 64 |
+
- [Bluesky Insights](/toolkit/more/all-tools/bluesky-insights.md): Bluesky Insights is a free web-based analytics tool for the Bluesky social network.
|
| 65 |
+
- [BskyFollowFinder/Bluesky network analyzer](/toolkit/more/all-tools/bluesky-network-analyzer.md): A tool that identifies which Bluesky accounts are followed by a profile’s contacts but not by that profile. Can be used for expanding networks and social graph analysis.
|
| 66 |
+
- [BskyThreadReader](/toolkit/more/all-tools/bskythreadreader.md): BskyThreadReader is a web-based Bluesky thread viewer that allows anyone to read and share Bluesky threads without logging in.
|
| 67 |
+
- [Bulletpicker.com](/toolkit/more/all-tools/bulletpicker.com.md): Bulletpicker.com is a collection of ammunition guidebooks and manuals from several different armed forces.
|
| 68 |
+
- [CAT UXO](/toolkit/more/all-tools/cat-uxo.md): A repository for professionals working in the explosive ordnance disposal (EOD) space.
|
| 69 |
+
- [China-related resources](/toolkit/more/all-tools/china-related-resources.md): Resources for research on companies in China.
|
| 70 |
+
- [Chronotrains](/toolkit/more/all-tools/chronotrains.md): Chronotrains is a free interactive map designed to explore the reach of Europe’s extensive rail network. Enter a starting point and travel time to see reachable destinations by train.
|
| 71 |
+
- [CITES Trade Database](/toolkit/more/all-tools/cites-trade-database.md): Around 23 million records of trade in wildlife since 1975.
|
| 72 |
+
- [Companies House](/toolkit/more/all-tools/companies-house.md): Search companies and individuals in the United Kingdom.
|
| 73 |
+
- [Convert Geographic Units](/toolkit/more/all-tools/convert-geographic-units.md): A tool that converts various geographic coordinates to support diverse mapping and spatial analysis needs.
|
| 74 |
+
- [Datawrapper](/toolkit/more/all-tools/datawrapper.md): A tool for creating interactive charts, maps, and tables from your data, offering a user-friendly interface for visualizing information.
|
| 75 |
+
- [DeHashed](/toolkit/more/all-tools/dehashed.md): A platform that maintains a database of compromised credentials, with a newly-launched web domain registration search tool.
|
| 76 |
+
- [Discord Chat Exporter](/toolkit/more/all-tools/discord-chat-exporter.md): A tool for exporting Discord chat logs in multiple formats.
|
| 77 |
+
- [DiscordLeaks](/toolkit/more/all-tools/discordleaks.md): Search hundreds of thousands of messages leaked from 290+ white-supremacist / nazi discord servers.
|
| 78 |
+
- [Distill.io](/toolkit/more/all-tools/distill.md): Distill.io is a website change monitoring tool that allows users to track changes on web pages.
|
| 79 |
+
- [DomainTools Whois Lookup](/toolkit/more/all-tools/domaintools-whois-lookup.md): DomainTools Whois provides detailed domain name registration information, and can be used to investigate details about domains or IP addresses.
|
| 80 |
+
- [Earth Explorer](/toolkit/more/all-tools/earth-explorer.md): "Query and order satellite images, aerial photographs, and cartographic products"
|
| 81 |
+
- [Earth Online](/toolkit/more/all-tools/earth-online.md): The ESA's Earth Online product offers a portal for accessing satellite imagery and environmental data, supporting a range of applications from climate monitoring to natural disaster assessment.
|
| 82 |
+
- [EDGAR Suite](/toolkit/more/all-tools/edgar-suite.md): Tool for the retrieval of corporate and financial data from SEC's EDGAR (Electronic Data Gathering, Analysis, and Retrieval) database.
|
| 83 |
+
- [EDGAR](/toolkit/more/all-tools/edgar.md): Database of corporate filings for the US
|
| 84 |
+
- [EIA Global Environmental Crime Tracker](/toolkit/more/all-tools/eia-global-environmental-crime-tracker.md): Map/tracking of environmental crimes including trade in ivory, rhino, big cats, and other exotic animals.
|
| 85 |
+
- [Environmental Justice Atlas](/toolkit/more/all-tools/environmental-justice-atlas.md): Maps environmental conflicts around the world to provide information about who is involved, their impact and other information.
|
| 86 |
+
- [Equasis](/toolkit/more/all-tools/equasis.md): Equasis provides vessel ownership and safety records, as well as shipping company fleet information.
|
| 87 |
+
- [Etherscan](/toolkit/more/all-tools/etherscan.md): An explorer that allows researchers to track wallets, transactions and more on the Ethereum blockchain.
|
| 88 |
+
- [EU consolidated corporate registers](/toolkit/more/all-tools/eu-consolidated-corporate-registers.md): Consolidated company registers covering most of the EU, Iceland, Liechtenstein and Norway.
|
| 89 |
+
- [EU Sanctions Map](/toolkit/more/all-tools/eu-sanctions-map.md): Database of sanctions imposed by the European Union
|
| 90 |
+
- [ExifTool](/toolkit/more/all-tools/exiftool.md): ExifTool is a command-line application for reading, writing, and editing meta information in files.
|
| 91 |
+
- [F4Map](/toolkit/more/all-tools/f4map.md): F4Map is an interactive 3D map visualization tool that provides detailed rendering of urban landscapes and geographical features.
|
| 92 |
+
- [Face Comparison by ToolPie](/toolkit/more/all-tools/face-comparison-toolpie.md): Compares two human face photos to determine similarity.
|
| 93 |
+
- [FaceCheck.ID](/toolkit/more/all-tools/facecheck.id.md): A facial recognition search engine that tries to find photos of people that look similar to a person of interest.
|
| 94 |
+
- [FlightAware](/toolkit/more/all-tools/flightaware.md): FlightAware is a global flight-tracking platform that provides real-time data on aircraft movements. It offers live tracking, historical data, and predictive analytics via its website and apps.
|
| 95 |
+
- [Flightradar24](/toolkit/more/all-tools/flightradar24.md): Flightradar24, a real-time flight tracking service, that provides comprehensive information about aircraft positions, flight numbers, routes, historical data, detailed aircraft specifications.
|
| 96 |
+
- [Forensically](/toolkit/more/all-tools/forensically.md): A collection of web-based image forensics tools. Can identify fake or doctored images.
|
| 97 |
+
- [Gaode Maps](/toolkit/more/all-tools/gaode-maps.md): Gaode Maps (also known as AMap) is a mapping application and technology from the Chinese company Alibaba.
|
| 98 |
+
- [Geo Data Tool](/toolkit/more/all-tools/geo-data-tool.md): IP geolocation service to identify the location and other technical information associated to IP addresses.
|
| 99 |
+
- [GeoHints](/toolkit/more/all-tools/geohints.md): GeoHints is a website that provides information about things like traffic lights, utility poles, bollards etc. for different regions of the world to help geolocate a location.
|
| 100 |
+
- [Gephi](/toolkit/more/all-tools/gephi.md): Open-source network analysis and visualization software
|
| 101 |
+
- [Ghunt](/toolkit/more/all-tools/ghunt.md): A command line tool for obtaining information about Google accounts.
|
| 102 |
+
- [Global Fishing Watch Map](/toolkit/more/all-tools/global-fishing-watch-map.md): The Global Fishing Watch Map is a digital platform for investigating fishing activities worldwide by utilising satellite and AIS data.
|
| 103 |
+
- [Global Forest Watch](/toolkit/more/all-tools/global-forest-watch.md): Explore tree cover loss and gain data, recent deforestation and fire alerts, land use designations, carbon emissions, biodiversity metrics and more.
|
| 104 |
+
- [Global Monitoring System - ECOSOLVE](/toolkit/more/all-tools/global-monitoring-system.md): Illicit online wildlife markets data from Brazil, South Africa and Thailand.
|
| 105 |
+
- [Global Suppliers Online](/toolkit/more/all-tools/global-suppliers-online.md): A site dedicated to connect suppliers and buyers of goods from all over the world.
|
| 106 |
+
- [Google Earth Engine](/toolkit/more/all-tools/google-earth-engine.md): Google Earth Engine is a platform for environmental monitoring and analysis through satellite imagery and geospatial data.
|
| 107 |
+
- [Google Earth Pro](/toolkit/more/all-tools/google-earth-pro.md): Google Earth is a geospatial tool that provides detailed, global satellite imagery, maps, 3D terrain models, and the ability to explore geographic data interactively.
|
| 108 |
+
- [Google Flood Hub](/toolkit/more/all-tools/google-flood-hub.md): A visual tool to monitor river levels and forecast floods based on AI models developed by Google Research.
|
| 109 |
+
- [Google Lens](/toolkit/more/all-tools/google-lens.md): Google Lens is an image recognition tool which can be used to identify locations or objects in photographs.
|
| 110 |
+
- [Google Maps](/toolkit/more/all-tools/google-maps.md): Google Maps provides mapping information, satellite imagery and Google Street View imagery including historical Street View images.
|
| 111 |
+
- [GovMap](/toolkit/more/all-tools/govmap.md): GovMap provides an interactive map of Israel, offering users a wide range of data including property boundaries, planning information, and infrastructure details.
|
| 112 |
+
- [GPSJam](/toolkit/more/all-tools/gpsjam.md): GPSJam.org is a daily map that visualizes the GPS/GNSS disruptions on aircraft worldwide. It collects and presents 24-hour data showing areas experiencing interference.
|
| 113 |
+
- [Have I Been Pwned](/toolkit/more/all-tools/have-i-been-pwned.md): Does an email or a phone number appear in data breaches?
|
| 114 |
+
- [Hitta.se](/toolkit/more/all-tools/hitta.se.md): Hitta.se is a comprehensive Swedish search engine and map that provides detailed information and contact details for businesses and individuals across Sweden.
|
| 115 |
+
- [Hoaxy](/toolkit/more/all-tools/hoaxy.md): Hoaxy is a web-based search and visualization tool. It helps visualize the spread of information on Bluesky and X (Twitter).
|
| 116 |
+
- [Hugin](/toolkit/more/all-tools/hugin.md): Hugin is a free and open-source panorama photo stitching and HDR (High Dynamic Range imaging) merging software that helps users create seamless panoramic images from multiple photographs.
|
| 117 |
+
- [Hunchly](/toolkit/more/all-tools/hunchly.md): An archiving tool that tracks online activities and preserves essential information about the web pages researchers visit.
|
| 118 |
+
- [ICANN Lookup](/toolkit/more/all-tools/icann-lookup.md): This tool allows you to search for the current registration data of internet domain names.
|
| 119 |
+
- [ICIJ Offshore Leaks Database](/toolkit/more/all-tools/icij-offshore-leaks-database.md): A database providing otherwise secret information about more than 810k offshore companies, foundations, and trusts based on leaks like the Panama Papers or the Paradise Papers.
|
| 120 |
+
- [IDN Checker](/toolkit/more/all-tools/idn-checker.md): IDN Checker detects visually similar versions of a domain.
|
| 121 |
+
- [ImportGenius](/toolkit/more/all-tools/importgenius.md): Commercial supplier of trade data for 23 countries. Paid service but journalists can ask for free access.
|
| 122 |
+
- [ImportYeti](/toolkit/more/all-tools/importyeti.md): Search US customs inbound sea shipment records, find company suppliers.
|
| 123 |
+
- [Index Database](/toolkit/more/all-tools/index-database.md): A database of remote sensing indices and satellite imaging sensors
|
| 124 |
+
- [Instagram Location Search](/toolkit/more/all-tools/instagram-location-search.md): A command line tool that allows users to find location tags near a specified latitude and longitude.
|
| 125 |
+
- [InstaLoader](/toolkit/more/all-tools/instaloader.md): Download pictures or videos (with metadata) from Instagram.
|
| 126 |
+
- [Instant Data Scraper](/toolkit/more/all-tools/instant-data-scraper.md): Browser extension for simple web scraping, with table output.
|
| 127 |
+
- [Intelx.io](/toolkit/more/all-tools/intelx.io.md): Find user details in data breaches
|
| 128 |
+
- [InVID](/toolkit/more/all-tools/invid.md): A toolkit that supports the verification of videos and images.
|
| 129 |
+
- [KartaView](/toolkit/more/all-tools/kartaview.md): KartaView is a crowdsourced platform for street view imagery.
|
| 130 |
+
- [Leak-Lookup](/toolkit/more/all-tools/leak-lookup.md): An online tool that allows you to search across public data breaches to surface credentials that may have been compromised.
|
| 131 |
+
- [License Plate Maps](/toolkit/more/all-tools/license-plate-maps.md): Collection of tools and maps for discerning license plates by country
|
| 132 |
+
- [LinkdTime](/toolkit/more/all-tools/linkdtime.md): Build a clean timeline of any LinkedIn activity from a single URL or a whole list of links.
|
| 133 |
+
- [LittleSis](/toolkit/more/all-tools/littlesis.md): Connects dots between influential / wealthy individuals in (mostly US) politics and business.
|
| 134 |
+
- [Liveuamap](/toolkit/more/all-tools/liveuamap.md): LiveUAMap is a mapping tool that provides up-to-date information on global geopolitical events, conflicts, and crises.
|
| 135 |
+
- [Locust Hub](/toolkit/more/all-tools/locust-hub.md): A repository for desert locust data with maps and other resources for tracking movements, early detection and planning locust control interventions.
|
| 136 |
+
- [Logseq](/toolkit/more/all-tools/logseq.md): Logseq is an open-source knowledge management tool that enables users to organize their notes, tasks, and projects.
|
| 137 |
+
- [Lumen](/toolkit/more/all-tools/lumen.md): A research project collecting and publishing legal takedown notices for online content transparency
|
| 138 |
+
- [Maigret](/toolkit/more/all-tools/maigret.md): Maigret is a Python script that retrieves user information by searching for usernames across various websites and social media platforms.
|
| 139 |
+
- [Maltego Graph](/toolkit/more/all-tools/maltego.md): Maltego Graph is an investigation platform that combines two things at once: (1) It acts as a search tool, and (2) It creates a graph establishing links between data you uncover from your search.
|
| 140 |
+
- [MapChecking](/toolkit/more/all-tools/mapchecking.md): This tool helps you estimate and fact-check the maximum number of people standing in a given area.
|
| 141 |
+
- [Mapillary](/toolkit/more/all-tools/mapillary.md): Mapillary is a crowdsourced street-level imagery platform.
|
| 142 |
+
- [MapSwitcher](/toolkit/more/all-tools/mapswitcher.md): Chrome extension switches between online map apps, maintaining (as far as possible) the map centre, zoom level, & directions of the source map.
|
| 143 |
+
- [MarineTraffic](/toolkit/more/all-tools/marinetraffic.md): An open, community-based project, providing (near) real-time information on the movements of ships and their locations in harbours and ports.
|
| 144 |
+
- [Merlin](/toolkit/more/all-tools/merlin.md): Identify birds (visually), through an app.
|
| 145 |
+
- [Meta Content Library](/toolkit/more/all-tools/meta-content-library.md): Meta Content Library is a controlled-access tool that lets approved academic and non-profit researchers search the full public archive of Facebook, Instagram, and Threads posts, in near-real-time.
|
| 146 |
+
- [MW Geofind](/toolkit/more/all-tools/mw-geofind.md): MW Geofind is a tool for finding geotagged YouTube videos.
|
| 147 |
+
- [Name Variant Search](/toolkit/more/all-tools/name-variant-search.md): Simple tool to help search for different ways of writing a name.
|
| 148 |
+
- [Namechk](/toolkit/more/all-tools/namechk.md): A username and domain search tool that checks on which platforms or domain a given username is registered.
|
| 149 |
+
- [NASA Firms](/toolkit/more/all-tools/nasa-firms.md): Displays a world map overlaid with infra-red data from one or more satellites, some, but not all of which may represent heat from fires and explosions.
|
| 150 |
+
- [NASA Worldview](/toolkit/more/all-tools/nasa-worldview.md): NASA Worldview is an online tool for visualizing and downloading near real-time satellite imagery and scientific data of Earth's atmosphere, land, and oceans.
|
| 151 |
+
- [Navtex](/toolkit/more/all-tools/navtex.md): A historical database providing vital navigational and meteorological warnings, forecasts, and urgent maritime safety information to ships.
|
| 152 |
+
- [NeutrOSINT](/toolkit/more/all-tools/neutrosint.md): A tool for investigating Proton Mail addresses.
|
| 153 |
+
- [North Data](/toolkit/more/all-tools/north-data.md): Search for people and companies in EU corporate and trade registers + visualize relationships
|
| 154 |
+
- [Obsidian](/toolkit/more/all-tools/obsidian.md): A knowledge management and note-taking app with extensive customization options.
|
| 155 |
+
- [OCCRP Aleph](/toolkit/more/all-tools/occrp-aleph.md): Aleph offers a way to research sanctions lists, corporate registries, leaks, and more
|
| 156 |
+
- [Open Measures](/toolkit/more/all-tools/open-measures.md): Open Measures helps open source researchers investigate harmful online activity such as extremism and disinformation.
|
| 157 |
+
- [Open Ownership](/toolkit/more/all-tools/open-ownership.md): Links to beneficial ownership registers.
|
| 158 |
+
- [Open Source Munitions Portal](/toolkit/more/all-tools/open-source-munitions-portal.md): A searchable library of verified images for researchers, journalists, and practitioners trying to learn more about munitions and their use and impact in conflict.
|
| 159 |
+
- [OpenCorporates](/toolkit/more/all-tools/opencorporates.md): Comprehensive repository of company registries around the world
|
| 160 |
+
- [OpenSanctions](/toolkit/more/all-tools/opensanctions.md): Open-source international database of sanctions data, persons of interest and politically exposed persons.
|
| 161 |
+
- [OpenSecrets](/toolkit/more/all-tools/opensecrets.md): Data on campaign finance, lobbying, and spending in U.S. politics
|
| 162 |
+
- [OrbTrack](/toolkit/more/all-tools/orbtrack.md): Predicts & describes the position & path of >10K satellites in Earth orbit, relative to points on the earth's surface input by the user, for 5 days ahead, + International Space Station video feed.
|
| 163 |
+
- [Osint Tools Map](/toolkit/more/all-tools/osint-tools-map.md): An interactive worldwide map, showcasing business registries, court records, and other publicly available information to aid OSINT investigations and research.
|
| 164 |
+
- [Overpass Turbo](/toolkit/more/all-tools/overpass-turbo.md): Overpass Turbo is a web-based tool for querying and visualizing OpenStreetMap crowd sourced data, aiding in extracting specific information like locations of amenities e.g.hospitals.
|
| 165 |
+
- [PeakVisor](/toolkit/more/all-tools/peakvisor.md): Dual window views for any global location: (1) a 2-D map & (2) a 3-D rendered terrain model, with photo fitting, shade/slope mapping, sun trails & weather data. In active development for OS research.
|
| 166 |
+
- [PimEyes](/toolkit/more/all-tools/pimeyes.md): An AI-powered facial recognition reverse image search tool.
|
| 167 |
+
- [Pinpoint](/toolkit/more/all-tools/pinpoint.md): A tool by Google to catalogue uploaded documents and files, providing OCR, indexing, and other services. Full access only granted to journalists, academic researchers and university students.
|
| 168 |
+
- [PixPlot](/toolkit/more/all-tools/pixplot.md): PixPlot is a tool that utilizes machine learning and WebGL to provide an interactive visualization of large image collections, allowing users to explore patterns and outliers within image datasets.
|
| 169 |
+
- [Planet Labs](/toolkit/more/all-tools/planet-labs.md): Planet Labs PBC is an American optical satellite imagery company that sells access to imagery.
|
| 170 |
+
- [Police Records Access Project](/toolkit/more/all-tools/police-records-access-project.md): A database providing searchable access to California law enforcement records including police use-of-force incidents, shootings, and misconduct cases.
|
| 171 |
+
- [PublicWWW](/toolkit/more/all-tools/publicwww.md): PublicWWW is a source code search engine that allows you to search for any alphanumeric snippet, signature, or keyword within the HTML, JavaScript, and CSS code of millions of web pages.
|
| 172 |
+
- [QGIS](/toolkit/more/all-tools/qgis.md): QGIS is a free Open Source Geographic Information System (GIS).
|
| 173 |
+
- [Quick geolocation search](/toolkit/more/all-tools/quick-geolocation-search.md): A tool that brings several maps into one place for easy location search.
|
| 174 |
+
- [Radar Interference Tracker](/toolkit/more/all-tools/radar-interference-tracker.md): Bellingcat's radar interference tracker can be used to locate and monitor active military radar systems.
|
| 175 |
+
- [RAMMB SLIDER](/toolkit/more/all-tools/rammb-slider.md): Real-time weather satellites of the entire globe
|
| 176 |
+
- [RAWGraphs](/toolkit/more/all-tools/rawgraphs.md): RAWGraphs is an open-source data visualization tool designed for non-technical users, enabling the creation of customizable, editable charts without coding skills.
|
| 177 |
+
- [RootAbout](/toolkit/more/all-tools/rootabout.md): RootAbout is a reverse image search tool that pulls indexed images from the Internet Archive.
|
| 178 |
+
- [RuPEP](/toolkit/more/all-tools/rupep.md): Online database of politically exposed persons in Russia, Belarus and Kazakhstan.
|
| 179 |
+
- [SanctionsExplorer](/toolkit/more/all-tools/sanctionsexplorer.md): A comprehensive database of current and historical OFAC/UN/EU sanctions
|
| 180 |
+
- [satellites.pro](/toolkit/more/all-tools/satellites.pro.md): Satellites.pro allows open source researchers to quickly switch between several free satellite imagery and mapping services.
|
| 181 |
+
- [Search by Image](/toolkit/more/all-tools/search-by-image.md): A browser extension to reverse search an image on multiple search engines.
|
| 182 |
+
- [Search4Faces](/toolkit/more/all-tools/search4faces.md): Upload the picture of a face and find pictures of similar looking people on VKontakte, Odnoklassniki, TikTok and Clubhouse.
|
| 183 |
+
- [Sentinel Hub Playground](/toolkit/more/all-tools/sentinal-hub-playground.md): A free web-based platform for viewing, analyzing, and downloading satellite imagery from the European Space Agency's Sentinel missions, with data updated every 5-10 days.
|
| 184 |
+
- [ShadeMap](/toolkit/more/all-tools/shademap.md): ShadeMap is a global simulation of mountain, building & tree shadows for a given date & time. Base data is free, but users can buy 30cm accurate data per sq km for areas of special focus.
|
| 185 |
+
- [Shadow Finder](/toolkit/more/all-tools/shadow-finder.md): To analyse shadows in source imagery, Shadow Finder maps all points on the earth where a shadow of given length could occur at a given date & time, IF the height of the object casting it is known.
|
| 186 |
+
- [ShadowMap](/toolkit/more/all-tools/shadowmap.md): Tilting global map of 3D buildlings and the shadows they cast at a specific time of the day, but date fixed at "today" in free version. Paid versions allow date change and offer 3D models.
|
| 187 |
+
- [Sherlock](/toolkit/more/all-tools/sherlock.md): Check usernames across more than 400 websites and social networks.
|
| 188 |
+
- [ShipFinder](/toolkit/more/all-tools/shipfinder.md): ShipFinder is an application designed to track vessels in near real-time across the globe.
|
| 189 |
+
- [Skopenow](/toolkit/more/all-tools/skopenow.md): Social Media Investigations - name, phone, email, username searches
|
| 190 |
+
- [SkyFi](/toolkit/more/all-tools/skyfi.md): SkyFi is used to purchase commercial satellite imagery and task (order the collection of images) satellites without a subscription.
|
| 191 |
+
- [Snap Map](/toolkit/more/all-tools/snap-map.md): Searchable map of geotagged snaps.
|
| 192 |
+
- [Species+](/toolkit/more/all-tools/species+.md): Centralized website with vulnerable species information.
|
| 193 |
+
- [Strava](/toolkit/more/all-tools/strava.md): Social media fitness app with exercise map based on users' GPS data.
|
| 194 |
+
- [Suncalc](/toolkit/more/all-tools/suncalc.md): Suncalc models the relationship between the date, time of day, the geographic location of a place, and the position of the sun in the sky, together with the length & direction of the shadows it casts.
|
| 195 |
+
- [Telegago](/toolkit/more/all-tools/telegago.md): Telegago is a Google Custom Search Engine tailored for searching public Telegram content for OSINT purposes.
|
| 196 |
+
- [Telegram Group Joiner](/toolkit/more/all-tools/telegram-group-joiner.md): Automate joining multiple Telegram groups and channels, ideal for researchers monitoring specific topics.
|
| 197 |
+
- [Telegram Phone Number Checker](/toolkit/more/all-tools/telegram-phone-number-checker.md): Command line tool for checking if phone numbers are connected to Telegram accounts and retrieving related information where available.
|
| 198 |
+
- [TelegramDB](/toolkit/more/all-tools/telegramdb.md): TelegramDB is a searchable database service that allows users to explore public Telegram groups and channels via a dedicated bot.
|
| 199 |
+
- [Telemetrio](/toolkit/more/all-tools/telemetrio.md): Telemetr.io offers a range of Telegram-related services based on a catalog of Telegram channels: country and category-specific rankings, curated collections, real-time event tracking, and an API.
|
| 200 |
+
- [Telemetry](/toolkit/more/all-tools/telemetry.md): An analytical search tool for Telegram groups and channels.
|
| 201 |
+
- [Telepathy](/toolkit/more/all-tools/telepathy.md): Telepathy is a versatile Telegram toolkit for OSINT analysts, enabling chat archiving, memberlist gathering, user location lookup, top poster analysis, message mapping, and more.
|
| 202 |
+
- [Tencent Maps](/toolkit/more/all-tools/tencent-maps.md): Tencent Maps is a mapping service provided by the Chinese technology company Tencent. Tencent Maps provides maps, satellite imagery, and street view.
|
| 203 |
+
- [TGStat](/toolkit/more/all-tools/tgstat.md): TGStat is a web-based analytics tool for Telegram that monitors active channels and provides profile analytics and statistics. It tracks channel subscribers’ growth rate, reach, and citation index.
|
| 204 |
+
- [The Information Laundromat](/toolkit/more/all-tools/the-information-laundromat.md): A tool for analyzing content replication and site architecture to detect information laundering.
|
| 205 |
+
- [TinEye](/toolkit/more/all-tools/tineye.md): TinEye is a search engine that allows the user to search using images (reverse image search).
|
| 206 |
+
- [TrueCaller](/toolkit/more/all-tools/truecaller.md): Truecaller is a caller ID app that identifies incoming calls, blocks unwanted numbers, and gathers phone numbers and names from contact lists. It also performs a reverse phone number search.
|
| 207 |
+
- [TruffleHog](/toolkit/more/all-tools/trufflehog.md): Find leaked credentials.
|
| 208 |
+
- [Twitter Advanced Search](/toolkit/more/all-tools/twitter-advanced-search.md): Twitter/X Advanced Search is X's own tool to help users find more precise information on the platform by filtering posts according to criteria such as location, user, date or popularity.
|
| 209 |
+
- [Twitter Location Search](/toolkit/more/all-tools/twitter-location-search.md): Discover real-time conversations and trends in any area with X's built-in location search. Search by latitude and longitude coordinates or distance for targeted local content.
|
| 210 |
+
- [Twitter Video Downloader](/toolkit/more/all-tools/twitter-video-downloader.md): Download videos from X (formerly Twitter) by converting tweet URLs into downloadable video links.
|
| 211 |
+
- [Umbra Space](/toolkit/more/all-tools/umbra-space.md): Umbra is an American synthetic aperture radar (SAR) satellite imaging company that sells on-demand taskings for satellite imagery.
|
| 212 |
+
- [UN Comtrade Database](/toolkit/more/all-tools/un-comtrade-database.md): United Nations free database of global trade.
|
| 213 |
+
- [UNOSAT Analyses](/toolkit/more/all-tools/unosat-analyses.md): UNOSAT Analyses is a tool that maps humanitarian emergencies across the globe utilising United Nations Satellite Centre data.
|
| 214 |
+
- [Uwazi](/toolkit/more/all-tools/uwazi.md): Uwazi is an open-source platform that simplifies the management of document collections, particularly for human rights documentation in the justice and advocacy fields.
|
| 215 |
+
- [VesselFinder](/toolkit/more/all-tools/vesselfinder.md): Live marine vessel tracker
|
| 216 |
+
- [Wayback Machine](/toolkit/more/all-tools/internet-archive.md): The Internet Archive's Wayback Machine lets users view and archive web pages, aiding in historical research and digital preservation.
|
| 217 |
+
- [Web Archives](/toolkit/more/all-tools/web-archives.md): A browser extension to view archived and cached versions of a website on multiple archiving sites.
|
| 218 |
+
- [What CMS](/toolkit/more/all-tools/what-cms.md): A tool that you can use to identify the technologies used to power a website.
|
| 219 |
+
- [what3words](/toolkit/more/all-tools/what3words.md): A proprietary geocode system which identifies any location on the surface of the earth to a resolution of 3 metres. The identifier is a unique combination of three words, available in 60 languagues.
|
| 220 |
+
- [WhatsMyName](/toolkit/more/all-tools/whats-my-name.md): Search for usernames on several hundred platforms
|
| 221 |
+
- [Who posted what?](/toolkit/more/all-tools/who-posted-what.md): A tool that allows a keyword search on Facebook on a specific date or within a specific time frame.
|
| 222 |
+
- [Whoxy](/toolkit/more/all-tools/whoxy.md): Whoxy is a domain search engine or "whois lookup" tool to find (the history of) registration information on a domain, such as the registrar, the status of the domain and the date of registration.
|
| 223 |
+
- [Wikimapia](/toolkit/more/all-tools/wikimapia.md): Wikimapia was a long-running collaborative mapping project that remains partially accessible, providing open source researchers with a unique database of historical, user-generated content.
|
| 224 |
+
- [Wikipedia list of registers](/toolkit/more/all-tools/wikipedia-list-of-registers.md): Wikipedia list of official business registers around the world.
|
| 225 |
+
- [WildEye](/toolkit/more/all-tools/wildeye.md): Tracking tool for data on environmental and wildlife crime cases, including court cases and convictions, across the globe.
|
| 226 |
+
- [Wildlife Trade Portal](/toolkit/more/all-tools/wildlife-trade-portal.md): An open-source tool to search wildlife seizure data worldwide.
|
| 227 |
+
- [World Database on Protected and Conserved Areas](/toolkit/more/all-tools/world-database-protected-areas.md): A comprehensive global database on terrestrial and marine protected areas.
|
| 228 |
+
- [xIFr](/toolkit/more/all-tools/xifr.md): A Firefox add-on for extracting EXIF metadata by right-clicking an image.
|
| 229 |
+
- [Yandex Maps](/toolkit/more/all-tools/yandex-maps.md): A platform offering detailed maps, satellite imagery, street views (static & sometimes dynamic imagery, including aerial views). Often the best available data on Russia & surrounding regions.
|
| 230 |
+
- [Zeeschuimer](/toolkit/more/all-tools/zeeschuimer.md): Zeeschuimer is a browser extension for collecting social media posts that are visible in your web browser, enabling systematic analysis of content from platforms that are hard to scrape via APIs.
|
| 231 |
+
- [Zotero](/toolkit/more/all-tools/zotero.md): A tool for collecting, organizing, annotating, citing, and sharing research sources.
|
| 232 |
+
|
| 233 |
+
---
|
skills/scrolly-sveltekit/SKILL.md
ADDED
|
@@ -0,0 +1,937 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
name: scrolly-sveltekit
|
| 3 |
+
description: Build scroll-driven narrative experiences using SvelteKit 5 and Svelte 5 runes. Includes components for image scrolly, video scrolly, map scrolly, and notice mosaic patterns.
|
| 4 |
+
author: Tom
|
| 5 |
+
version: 1.0.0
|
| 6 |
+
tools: []
|
| 7 |
+
---
|
| 8 |
+
|
| 9 |
+
# Scrollytelling in SvelteKit
|
| 10 |
+
|
| 11 |
+
A comprehensive guide for building scroll-driven narrative experiences using SvelteKit 5 and Svelte 5's runes.
|
| 12 |
+
|
| 13 |
+
## Quick Start
|
| 14 |
+
|
| 15 |
+
```svelte
|
| 16 |
+
<script>
|
| 17 |
+
import ScrollySection from '$lib/components/scrolly/ScrollySection.svelte';
|
| 18 |
+
import MyVisualization from './MyVisualization.svelte';
|
| 19 |
+
|
| 20 |
+
let activeStep = $state(0);
|
| 21 |
+
|
| 22 |
+
const steps = [
|
| 23 |
+
{ text: 'First narrative point' },
|
| 24 |
+
{ text: 'Second narrative point' },
|
| 25 |
+
{ text: 'Third narrative point' }
|
| 26 |
+
];
|
| 27 |
+
</script>
|
| 28 |
+
|
| 29 |
+
<ScrollySection bind:activeStep={activeStep} {steps}>
|
| 30 |
+
{#snippet children({ activeStep })}
|
| 31 |
+
<MyVisualization {activeStep} />
|
| 32 |
+
{/snippet}
|
| 33 |
+
</ScrollySection>
|
| 34 |
+
```
|
| 35 |
+
|
| 36 |
+
---
|
| 37 |
+
|
| 38 |
+
## Core Architecture
|
| 39 |
+
|
| 40 |
+
### The Scrolly Pattern
|
| 41 |
+
|
| 42 |
+
Scrollytelling uses a **sticky background + scrolling foreground** pattern:
|
| 43 |
+
|
| 44 |
+
```
|
| 45 |
+
┌─────────────────────────────────┐
|
| 46 |
+
│ Sticky Visual Layer │ ← position: sticky, z-index: 1
|
| 47 |
+
│ (image/video/map/viz) │ Stays fixed while content scrolls
|
| 48 |
+
│ │
|
| 49 |
+
│ ┌─────────────────────────┐ │
|
| 50 |
+
│ │ Text Box (step 2) │ │ ← Text track overlays visuals
|
| 51 |
+
│ └─────────────────────────┘ │ z-index: 2
|
| 52 |
+
│ │
|
| 53 |
+
└─────────────────────────────────┘
|
| 54 |
+
```
|
| 55 |
+
|
| 56 |
+
**Key principle**: The visual layer uses `position: sticky` to stay in place while the text track scrolls past, creating the illusion of changing visuals as you scroll.
|
| 57 |
+
|
| 58 |
+
---
|
| 59 |
+
|
| 60 |
+
## Component Reference
|
| 61 |
+
|
| 62 |
+
### ScrollySection
|
| 63 |
+
|
| 64 |
+
The main container component that manages the sticky/scroll relationship.
|
| 65 |
+
|
| 66 |
+
**Location**: `$lib/components/scrolly/ScrollySection.svelte`
|
| 67 |
+
|
| 68 |
+
#### Props
|
| 69 |
+
|
| 70 |
+
| Prop | Type | Default | Description |
|
| 71 |
+
|------|------|---------|-------------|
|
| 72 |
+
| `activeStep` | `number` | `0` | Bindable. Current step index |
|
| 73 |
+
| `steps` | `Step[]` | required | Array of step content |
|
| 74 |
+
| `backgroundColor` | `string` | `'#0a0a0a'` | Background color |
|
| 75 |
+
| `showTextBoxes` | `boolean` | `true` | Show text boxes for steps |
|
| 76 |
+
| `textBoxVariant` | `'light' \| 'dark'` | `'light'` | Text box color scheme |
|
| 77 |
+
| `textBoxPosition` | `'center' \| 'left' \| 'right'` | `'center'` | Text box horizontal position |
|
| 78 |
+
| `firstStepOffset` | `number` | `0` | Viewport fraction to push first step down |
|
| 79 |
+
| `onStepEnter` | `(step, direction) => void` | - | Callback when step changes |
|
| 80 |
+
| `onScrollProgress` | `(progress) => void` | - | Callback with 0-1 scroll progress |
|
| 81 |
+
| `children` | `Snippet<[{ activeStep }]>` | - | Visualization snippet |
|
| 82 |
+
|
| 83 |
+
#### Step Interface
|
| 84 |
+
|
| 85 |
+
```typescript
|
| 86 |
+
interface Step {
|
| 87 |
+
text?: string; // HTML content for text box
|
| 88 |
+
title?: string; // Optional title
|
| 89 |
+
image?: string; // Image URL for text box
|
| 90 |
+
bgColor?: string; // Custom background color
|
| 91 |
+
raw?: boolean; // If true, render text without wrapper
|
| 92 |
+
source?: { text: string; url: string }; // Source citation
|
| 93 |
+
imageCredit?: string; // Photo credit
|
| 94 |
+
}
|
| 95 |
+
```
|
| 96 |
+
|
| 97 |
+
#### Example Usage
|
| 98 |
+
|
| 99 |
+
```svelte
|
| 100 |
+
<ScrollySection
|
| 101 |
+
bind:activeStep={heroStep}
|
| 102 |
+
steps={heroSteps}
|
| 103 |
+
backgroundColor="#000000"
|
| 104 |
+
showTextBoxes={true}
|
| 105 |
+
textBoxVariant="light"
|
| 106 |
+
onScrollProgress={(p) => (scrollProgress = p)}
|
| 107 |
+
>
|
| 108 |
+
{#snippet children({ activeStep })}
|
| 109 |
+
<HeroVisualization currentImage={heroImages[activeStep]} />
|
| 110 |
+
{/snippet}
|
| 111 |
+
</ScrollySection>
|
| 112 |
+
```
|
| 113 |
+
|
| 114 |
+
---
|
| 115 |
+
|
| 116 |
+
### ScrollyHelper
|
| 117 |
+
|
| 118 |
+
IntersectionObserver-based step tracker. Determines which step is most visible.
|
| 119 |
+
|
| 120 |
+
**Location**: `$lib/components/scrolly/ScrollyHelper.svelte`
|
| 121 |
+
|
| 122 |
+
#### How It Works
|
| 123 |
+
|
| 124 |
+
1. Wraps child elements (steps)
|
| 125 |
+
2. Creates IntersectionObserver for each child
|
| 126 |
+
3. Tracks intersection ratio for each step
|
| 127 |
+
4. Exports `value` binding with index of most visible step
|
| 128 |
+
5. Returns `undefined` when no steps are in view
|
| 129 |
+
|
| 130 |
+
#### Props
|
| 131 |
+
|
| 132 |
+
| Prop | Type | Default | Description |
|
| 133 |
+
|------|------|---------|-------------|
|
| 134 |
+
| `value` | `number \| undefined` | - | Bindable. Current step index |
|
| 135 |
+
| `root` | `Element \| null` | `null` | Intersection root |
|
| 136 |
+
| `top` | `number` | `0` | Top margin in pixels |
|
| 137 |
+
| `bottom` | `number` | `0` | Bottom margin in pixels |
|
| 138 |
+
| `increments` | `number` | `100` | Threshold granularity |
|
| 139 |
+
|
| 140 |
+
#### Usage
|
| 141 |
+
|
| 142 |
+
```svelte
|
| 143 |
+
<script>
|
| 144 |
+
let currentStep = $state(undefined);
|
| 145 |
+
</script>
|
| 146 |
+
|
| 147 |
+
<ScrollyHelper bind:value={currentStep} top={200} bottom={200}>
|
| 148 |
+
<div class="step">Step 0 content</div>
|
| 149 |
+
<div class="step">Step 1 content</div>
|
| 150 |
+
<div class="step">Step 2 content</div>
|
| 151 |
+
</ScrollyHelper>
|
| 152 |
+
```
|
| 153 |
+
|
| 154 |
+
---
|
| 155 |
+
|
| 156 |
+
### ScrollyTextBox
|
| 157 |
+
|
| 158 |
+
Styled text box component for displaying narrative content.
|
| 159 |
+
|
| 160 |
+
**Location**: `$lib/components/scrolly/ScrollyTextBox.svelte`
|
| 161 |
+
|
| 162 |
+
#### Props
|
| 163 |
+
|
| 164 |
+
| Prop | Type | Default | Description |
|
| 165 |
+
|------|------|---------|-------------|
|
| 166 |
+
| `title` | `string` | `''` | Box title |
|
| 167 |
+
| `image` | `string \| null` | `null` | Image URL |
|
| 168 |
+
| `bgColor` | `string \| null` | `null` | Custom background |
|
| 169 |
+
| `active` | `boolean` | `true` | Active state |
|
| 170 |
+
| `variant` | `'light' \| 'dark'` | `'light'` | Color scheme |
|
| 171 |
+
| `maxWidth` | `'narrow' \| 'wide'` | `'narrow'` | Box width |
|
| 172 |
+
| `source` | `{ text, url } \| null` | `null` | Source citation |
|
| 173 |
+
| `imageCredit` | `string \| null` | `null` | Photo credit |
|
| 174 |
+
| `children` | `Snippet` | - | Content slot |
|
| 175 |
+
|
| 176 |
+
#### Styling
|
| 177 |
+
|
| 178 |
+
- Light variant: White background with blur
|
| 179 |
+
- Dark variant: Near-black background with blur
|
| 180 |
+
- Active state: Full opacity, `translateY(0)`
|
| 181 |
+
- Inactive state: 75% opacity, `translateY(8px)`
|
| 182 |
+
|
| 183 |
+
```css
|
| 184 |
+
/* Active transition */
|
| 185 |
+
.scrolly-text-box {
|
| 186 |
+
opacity: 0.75;
|
| 187 |
+
transform: translateY(8px);
|
| 188 |
+
transition: all 0.35s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
| 189 |
+
}
|
| 190 |
+
|
| 191 |
+
.scrolly-text-box.active {
|
| 192 |
+
opacity: 1;
|
| 193 |
+
transform: translateY(0);
|
| 194 |
+
}
|
| 195 |
+
```
|
| 196 |
+
|
| 197 |
+
---
|
| 198 |
+
|
| 199 |
+
## Visualization Components
|
| 200 |
+
|
| 201 |
+
### Image Scrolly (HeroVisualization)
|
| 202 |
+
|
| 203 |
+
Simple image swapper with fade effects.
|
| 204 |
+
|
| 205 |
+
**Location**: `$lib/components/cleared/HeroVisualization.svelte`
|
| 206 |
+
|
| 207 |
+
```svelte
|
| 208 |
+
<script lang="ts">
|
| 209 |
+
interface Props {
|
| 210 |
+
currentImage: string;
|
| 211 |
+
fadeProgress?: number; // 0-1
|
| 212 |
+
}
|
| 213 |
+
|
| 214 |
+
let { currentImage, fadeProgress = 0 }: Props = $props();
|
| 215 |
+
</script>
|
| 216 |
+
|
| 217 |
+
<div class="hero-visualization">
|
| 218 |
+
<img src={currentImage} alt="Scene" class="hero-image" />
|
| 219 |
+
<div class="hero-overlay"></div>
|
| 220 |
+
{#if fadeProgress > 0}
|
| 221 |
+
<div class="fade-overlay" style:opacity={fadeProgress}></div>
|
| 222 |
+
{/if}
|
| 223 |
+
</div>
|
| 224 |
+
|
| 225 |
+
<style>
|
| 226 |
+
.hero-visualization {
|
| 227 |
+
position: absolute;
|
| 228 |
+
inset: 0;
|
| 229 |
+
width: 100%;
|
| 230 |
+
height: 100%;
|
| 231 |
+
background: #000;
|
| 232 |
+
}
|
| 233 |
+
|
| 234 |
+
.hero-image {
|
| 235 |
+
position: absolute;
|
| 236 |
+
inset: 0;
|
| 237 |
+
width: 100%;
|
| 238 |
+
height: 100%;
|
| 239 |
+
object-fit: cover;
|
| 240 |
+
}
|
| 241 |
+
|
| 242 |
+
.hero-overlay {
|
| 243 |
+
position: absolute;
|
| 244 |
+
inset: 0;
|
| 245 |
+
background: linear-gradient(
|
| 246 |
+
to bottom,
|
| 247 |
+
rgba(0, 0, 0, 0.5) 0%,
|
| 248 |
+
rgba(0, 0, 0, 0.3) 50%,
|
| 249 |
+
rgba(0, 0, 0, 0.5) 100%
|
| 250 |
+
);
|
| 251 |
+
}
|
| 252 |
+
|
| 253 |
+
.fade-overlay {
|
| 254 |
+
position: absolute;
|
| 255 |
+
inset: 0;
|
| 256 |
+
background: #0a0a0a;
|
| 257 |
+
pointer-events: none;
|
| 258 |
+
}
|
| 259 |
+
</style>
|
| 260 |
+
```
|
| 261 |
+
|
| 262 |
+
#### Parent Usage
|
| 263 |
+
|
| 264 |
+
```svelte
|
| 265 |
+
<script>
|
| 266 |
+
let heroStep = $state(0);
|
| 267 |
+
let heroScrollProgress = $state(0);
|
| 268 |
+
|
| 269 |
+
const heroImages = [
|
| 270 |
+
'/images/scene1.jpg',
|
| 271 |
+
'/images/scene2.jpg',
|
| 272 |
+
'/images/scene3.jpg'
|
| 273 |
+
];
|
| 274 |
+
|
| 275 |
+
let currentHeroImage = $derived(heroImages[heroStep] ?? heroImages[0]);
|
| 276 |
+
|
| 277 |
+
// Fade to black on last step
|
| 278 |
+
let heroFadeProgress = $derived(() => {
|
| 279 |
+
if (heroStep !== 2) return 0;
|
| 280 |
+
const fadeStart = 0.75;
|
| 281 |
+
if (heroScrollProgress < fadeStart) return 0;
|
| 282 |
+
return Math.min(1, (heroScrollProgress - fadeStart) / (1 - fadeStart));
|
| 283 |
+
});
|
| 284 |
+
</script>
|
| 285 |
+
|
| 286 |
+
<ScrollySection
|
| 287 |
+
bind:activeStep={heroStep}
|
| 288 |
+
steps={heroSteps}
|
| 289 |
+
onScrollProgress={(p) => (heroScrollProgress = p)}
|
| 290 |
+
>
|
| 291 |
+
{#snippet children({ activeStep })}
|
| 292 |
+
<HeroVisualization
|
| 293 |
+
currentImage={currentHeroImage}
|
| 294 |
+
fadeProgress={heroFadeProgress()}
|
| 295 |
+
/>
|
| 296 |
+
{/snippet}
|
| 297 |
+
</ScrollySection>
|
| 298 |
+
```
|
| 299 |
+
|
| 300 |
+
---
|
| 301 |
+
|
| 302 |
+
### Video Scrolly (VideoScrollyVisualization)
|
| 303 |
+
|
| 304 |
+
Multi-video display with step-based transitions.
|
| 305 |
+
|
| 306 |
+
**Location**: `$lib/components/scrolly/VideoScrollyVisualization.svelte`
|
| 307 |
+
|
| 308 |
+
```svelte
|
| 309 |
+
<script lang="ts">
|
| 310 |
+
interface VideoStep {
|
| 311 |
+
videoSrc: string;
|
| 312 |
+
poster?: string;
|
| 313 |
+
}
|
| 314 |
+
|
| 315 |
+
interface Props {
|
| 316 |
+
activeStep: number;
|
| 317 |
+
videoSteps: VideoStep[];
|
| 318 |
+
}
|
| 319 |
+
|
| 320 |
+
let { activeStep, videoSteps }: Props = $props();
|
| 321 |
+
|
| 322 |
+
let videoRefs: HTMLVideoElement[] = $state([]);
|
| 323 |
+
|
| 324 |
+
$effect(() => {
|
| 325 |
+
// Pause all, play active
|
| 326 |
+
videoRefs.forEach((video, i) => {
|
| 327 |
+
if (video) {
|
| 328 |
+
if (i === activeStep) {
|
| 329 |
+
video.play().catch(() => {});
|
| 330 |
+
} else {
|
| 331 |
+
video.pause();
|
| 332 |
+
video.currentTime = 0;
|
| 333 |
+
}
|
| 334 |
+
}
|
| 335 |
+
});
|
| 336 |
+
});
|
| 337 |
+
</script>
|
| 338 |
+
|
| 339 |
+
<div class="video-scrolly-viz">
|
| 340 |
+
<div class="video-frame">
|
| 341 |
+
{#each videoSteps as step, i}
|
| 342 |
+
<video
|
| 343 |
+
src={step.videoSrc}
|
| 344 |
+
poster={step.poster}
|
| 345 |
+
bind:this={videoRefs[i]}
|
| 346 |
+
class="video-layer"
|
| 347 |
+
class:active={i === activeStep}
|
| 348 |
+
muted
|
| 349 |
+
loop
|
| 350 |
+
playsinline
|
| 351 |
+
preload="metadata"
|
| 352 |
+
/>
|
| 353 |
+
{/each}
|
| 354 |
+
</div>
|
| 355 |
+
<div class="video-vignette"></div>
|
| 356 |
+
</div>
|
| 357 |
+
|
| 358 |
+
<style>
|
| 359 |
+
.video-scrolly-viz {
|
| 360 |
+
position: absolute;
|
| 361 |
+
inset: 0;
|
| 362 |
+
width: 100%;
|
| 363 |
+
height: 100%;
|
| 364 |
+
background: #000;
|
| 365 |
+
}
|
| 366 |
+
|
| 367 |
+
.video-frame {
|
| 368 |
+
position: absolute;
|
| 369 |
+
inset: 1rem;
|
| 370 |
+
display: flex;
|
| 371 |
+
align-items: center;
|
| 372 |
+
justify-content: center;
|
| 373 |
+
}
|
| 374 |
+
|
| 375 |
+
.video-layer {
|
| 376 |
+
position: absolute;
|
| 377 |
+
width: 100%;
|
| 378 |
+
height: 100%;
|
| 379 |
+
object-fit: contain;
|
| 380 |
+
opacity: 0;
|
| 381 |
+
transition: opacity 500ms ease;
|
| 382 |
+
pointer-events: none;
|
| 383 |
+
}
|
| 384 |
+
|
| 385 |
+
.video-layer.active {
|
| 386 |
+
opacity: 1;
|
| 387 |
+
}
|
| 388 |
+
|
| 389 |
+
.video-vignette {
|
| 390 |
+
position: absolute;
|
| 391 |
+
inset: 0;
|
| 392 |
+
background: radial-gradient(ellipse at center, transparent 40%, rgba(0, 0, 0, 0.6) 100%);
|
| 393 |
+
pointer-events: none;
|
| 394 |
+
}
|
| 395 |
+
</style>
|
| 396 |
+
```
|
| 397 |
+
|
| 398 |
+
#### Parent Usage
|
| 399 |
+
|
| 400 |
+
```svelte
|
| 401 |
+
<script>
|
| 402 |
+
let videoStep = $state(0);
|
| 403 |
+
|
| 404 |
+
const videoData = [
|
| 405 |
+
{ videoSrc: '/videos/clip1.mp4' },
|
| 406 |
+
{ videoSrc: '/videos/clip2.mp4' },
|
| 407 |
+
{ videoSrc: '/videos/clip3.mp4' }
|
| 408 |
+
];
|
| 409 |
+
|
| 410 |
+
const videoSteps = [
|
| 411 |
+
{ raw: true, text: '<div class="quote-card">Quote 1</div>' },
|
| 412 |
+
{ raw: true, text: '<div class="quote-card">Quote 2</div>' },
|
| 413 |
+
{ raw: true, text: '<div class="quote-card">Quote 3</div>' }
|
| 414 |
+
];
|
| 415 |
+
</script>
|
| 416 |
+
|
| 417 |
+
<ScrollySection
|
| 418 |
+
bind:activeStep={videoStep}
|
| 419 |
+
steps={videoSteps}
|
| 420 |
+
backgroundColor="#000000"
|
| 421 |
+
showTextBoxes={true}
|
| 422 |
+
>
|
| 423 |
+
{#snippet children({ activeStep })}
|
| 424 |
+
<VideoScrollyVisualization {activeStep} videoSteps={videoData} />
|
| 425 |
+
{/snippet}
|
| 426 |
+
</ScrollySection>
|
| 427 |
+
```
|
| 428 |
+
|
| 429 |
+
---
|
| 430 |
+
|
| 431 |
+
### Map Scrolly (MapScrolly)
|
| 432 |
+
|
| 433 |
+
Interactive map with step-driven camera movements and layer visibility.
|
| 434 |
+
|
| 435 |
+
**Location**: `$lib/components/cleared/MapScrolly.svelte`
|
| 436 |
+
|
| 437 |
+
#### Key Features
|
| 438 |
+
|
| 439 |
+
- MapTiler/Mapbox GL integration
|
| 440 |
+
- Fly-to animations between steps
|
| 441 |
+
- GeoJSON layer management
|
| 442 |
+
- Fade-in/fade-out overlays
|
| 443 |
+
|
| 444 |
+
#### Props
|
| 445 |
+
|
| 446 |
+
| Prop | Type | Default | Description |
|
| 447 |
+
|------|------|---------|-------------|
|
| 448 |
+
| `activeStep` | `number` | required | Current step index |
|
| 449 |
+
| `fadeProgress` | `number` | `0` | Fade-in overlay (1=black, 0=visible) |
|
| 450 |
+
| `fadeOutProgress` | `number` | `0` | Fade-out overlay at end |
|
| 451 |
+
|
| 452 |
+
#### Step Data Structure
|
| 453 |
+
|
| 454 |
+
```json
|
| 455 |
+
{
|
| 456 |
+
"steps": [
|
| 457 |
+
{
|
| 458 |
+
"coordinates": [92.9376, 26.2006],
|
| 459 |
+
"zoom": 7,
|
| 460 |
+
"pitch": 0,
|
| 461 |
+
"bearing": 0,
|
| 462 |
+
"duration": 2000,
|
| 463 |
+
"layers": {
|
| 464 |
+
"evictions": { "opacity": 0.6 },
|
| 465 |
+
"villages": { "visible": true, "opacity": 1.0 }
|
| 466 |
+
}
|
| 467 |
+
}
|
| 468 |
+
]
|
| 469 |
+
}
|
| 470 |
+
```
|
| 471 |
+
|
| 472 |
+
#### Fly-To Implementation
|
| 473 |
+
|
| 474 |
+
```typescript
|
| 475 |
+
function flyToStep(index: number) {
|
| 476 |
+
if (!map || !mapReady) return;
|
| 477 |
+
|
| 478 |
+
const step = steps[index];
|
| 479 |
+
if (!step) return;
|
| 480 |
+
|
| 481 |
+
map.flyTo({
|
| 482 |
+
center: step.coordinates,
|
| 483 |
+
zoom: step.zoom,
|
| 484 |
+
bearing: step.bearing || 0,
|
| 485 |
+
pitch: step.pitch || 0,
|
| 486 |
+
duration: step.duration || 2000,
|
| 487 |
+
easing: (t: number) => t * (2 - t) // Ease-out quad
|
| 488 |
+
});
|
| 489 |
+
|
| 490 |
+
updateLayerVisibility(index);
|
| 491 |
+
}
|
| 492 |
+
```
|
| 493 |
+
|
| 494 |
+
---
|
| 495 |
+
|
| 496 |
+
### Notice Mosaic (NoticeMosaic)
|
| 497 |
+
|
| 498 |
+
Scattered document display with progressive reveal.
|
| 499 |
+
|
| 500 |
+
**Location**: `$lib/components/scrolly/NoticeMosaic.svelte`
|
| 501 |
+
|
| 502 |
+
```svelte
|
| 503 |
+
<script lang="ts">
|
| 504 |
+
interface Notice {
|
| 505 |
+
image: string;
|
| 506 |
+
alt: string;
|
| 507 |
+
title: string;
|
| 508 |
+
subtitle: string;
|
| 509 |
+
excerpt: string;
|
| 510 |
+
}
|
| 511 |
+
|
| 512 |
+
interface Props {
|
| 513 |
+
activeStep: number;
|
| 514 |
+
notices: Notice[];
|
| 515 |
+
backgroundColor?: string;
|
| 516 |
+
}
|
| 517 |
+
|
| 518 |
+
let { activeStep, notices, backgroundColor = '#1a1715' }: Props = $props();
|
| 519 |
+
|
| 520 |
+
let currentNoticeIndex = $derived(Math.min(activeStep, notices.length - 1));
|
| 521 |
+
|
| 522 |
+
// Deterministic positioning with jitter
|
| 523 |
+
function getPosition(index: number): [string, string, number] {
|
| 524 |
+
const positions: [number, number, number][] = [
|
| 525 |
+
[20, 15, -3],
|
| 526 |
+
[50, 25, 2.5],
|
| 527 |
+
[15, 45, -1.5],
|
| 528 |
+
[55, 10, 3]
|
| 529 |
+
];
|
| 530 |
+
|
| 531 |
+
const pos = positions[index % positions.length];
|
| 532 |
+
const seed = index * 7919;
|
| 533 |
+
const jitterX = ((seed % 10) - 5) * 0.5;
|
| 534 |
+
const jitterY = (((seed * 3) % 10) - 5) * 0.5;
|
| 535 |
+
|
| 536 |
+
return [`${pos[0] + jitterX}%`, `${pos[1] + jitterY}%`, pos[2]];
|
| 537 |
+
}
|
| 538 |
+
</script>
|
| 539 |
+
```
|
| 540 |
+
|
| 541 |
+
---
|
| 542 |
+
|
| 543 |
+
## Critical CSS Rules
|
| 544 |
+
|
| 545 |
+
### NEVER Use `position: fixed` for Overlays
|
| 546 |
+
|
| 547 |
+
**Problem**: Fixed overlays cover the ENTIRE viewport, including content below.
|
| 548 |
+
|
| 549 |
+
```css
|
| 550 |
+
/* BAD - Will darken entire page */
|
| 551 |
+
.fade-overlay {
|
| 552 |
+
position: fixed;
|
| 553 |
+
inset: 0;
|
| 554 |
+
background: #0a0a0a;
|
| 555 |
+
z-index: 5;
|
| 556 |
+
}
|
| 557 |
+
|
| 558 |
+
/* GOOD - Scoped to parent container */
|
| 559 |
+
.fade-overlay {
|
| 560 |
+
position: absolute;
|
| 561 |
+
inset: 0;
|
| 562 |
+
background: #0a0a0a;
|
| 563 |
+
pointer-events: none;
|
| 564 |
+
}
|
| 565 |
+
```
|
| 566 |
+
|
| 567 |
+
### Z-Index Guidelines
|
| 568 |
+
|
| 569 |
+
Keep z-index values simple and scoped:
|
| 570 |
+
|
| 571 |
+
| Layer | Z-Index | Description |
|
| 572 |
+
|-------|---------|-------------|
|
| 573 |
+
| Visual layer (sticky) | 1 | Background visualization |
|
| 574 |
+
| Text track | 2 | Scrolling text content |
|
| 575 |
+
| Fade overlays | 10 | Transition effects (inside section) |
|
| 576 |
+
| Footer | 10 | Ensure footer visibility |
|
| 577 |
+
|
| 578 |
+
**AVOID** high z-index values on sections adjacent to ScrollySection:
|
| 579 |
+
|
| 580 |
+
```svelte
|
| 581 |
+
<!-- BAD - Will stack above scrolly -->
|
| 582 |
+
<section class="intro relative z-10">
|
| 583 |
+
|
| 584 |
+
<!-- GOOD - Natural stacking -->
|
| 585 |
+
<section class="intro relative">
|
| 586 |
+
```
|
| 587 |
+
|
| 588 |
+
### Sticky Positioning Requirements
|
| 589 |
+
|
| 590 |
+
For `position: sticky` to work:
|
| 591 |
+
|
| 592 |
+
1. **Use `overflow-x: clip`** instead of `overflow: hidden`:
|
| 593 |
+
```css
|
| 594 |
+
:global(html), :global(body) {
|
| 595 |
+
overflow-x: clip; /* Doesn't break sticky */
|
| 596 |
+
}
|
| 597 |
+
```
|
| 598 |
+
|
| 599 |
+
2. **Add isolation** to the scrolly container:
|
| 600 |
+
```css
|
| 601 |
+
.scroll-section {
|
| 602 |
+
position: relative;
|
| 603 |
+
isolation: isolate;
|
| 604 |
+
}
|
| 605 |
+
```
|
| 606 |
+
|
| 607 |
+
3. **Visual layer structure**:
|
| 608 |
+
```css
|
| 609 |
+
.visual-layer {
|
| 610 |
+
position: sticky;
|
| 611 |
+
top: 0;
|
| 612 |
+
width: 100%;
|
| 613 |
+
height: 100vh;
|
| 614 |
+
z-index: 1;
|
| 615 |
+
}
|
| 616 |
+
```
|
| 617 |
+
|
| 618 |
+
---
|
| 619 |
+
|
| 620 |
+
## Non-Scrolly Content Sections
|
| 621 |
+
|
| 622 |
+
For regular article content without scroll-lock:
|
| 623 |
+
|
| 624 |
+
### Content Section Structure
|
| 625 |
+
|
| 626 |
+
```svelte
|
| 627 |
+
<section class="content-section">
|
| 628 |
+
<div class="content-container">
|
| 629 |
+
<h2 class="content-heading">Section Title</h2>
|
| 630 |
+
<div class="prose-content">
|
| 631 |
+
<p>Article content...</p>
|
| 632 |
+
</div>
|
| 633 |
+
</div>
|
| 634 |
+
</section>
|
| 635 |
+
```
|
| 636 |
+
|
| 637 |
+
### CSS
|
| 638 |
+
|
| 639 |
+
```css
|
| 640 |
+
/* Dark theme content section */
|
| 641 |
+
.content-section {
|
| 642 |
+
position: relative;
|
| 643 |
+
background: #0a0a0a;
|
| 644 |
+
padding: 5rem 1.5rem;
|
| 645 |
+
}
|
| 646 |
+
|
| 647 |
+
.content-container {
|
| 648 |
+
max-width: 42rem;
|
| 649 |
+
margin: 0 auto;
|
| 650 |
+
}
|
| 651 |
+
|
| 652 |
+
.content-heading {
|
| 653 |
+
font-family: 'Playfair Display', Georgia, serif;
|
| 654 |
+
font-size: clamp(1.75rem, 4vw, 2.5rem);
|
| 655 |
+
font-weight: 600;
|
| 656 |
+
line-height: 1.2;
|
| 657 |
+
color: #ffffff;
|
| 658 |
+
margin: 0 0 2rem 0;
|
| 659 |
+
}
|
| 660 |
+
|
| 661 |
+
.prose-content {
|
| 662 |
+
font-family: 'Source Sans 3', system-ui, sans-serif;
|
| 663 |
+
font-size: 1.125rem;
|
| 664 |
+
line-height: 1.85;
|
| 665 |
+
color: rgba(255, 255, 255, 0.85);
|
| 666 |
+
}
|
| 667 |
+
|
| 668 |
+
.prose-content p {
|
| 669 |
+
margin: 0 0 1.5rem 0;
|
| 670 |
+
}
|
| 671 |
+
|
| 672 |
+
.prose-content strong {
|
| 673 |
+
font-weight: 600;
|
| 674 |
+
color: #ffffff;
|
| 675 |
+
}
|
| 676 |
+
|
| 677 |
+
.prose-content blockquote {
|
| 678 |
+
border-left: 4px solid rgba(255, 255, 255, 0.2);
|
| 679 |
+
padding-left: 1.5rem;
|
| 680 |
+
margin: 2rem 0;
|
| 681 |
+
font-style: italic;
|
| 682 |
+
color: rgba(255, 255, 255, 0.6);
|
| 683 |
+
}
|
| 684 |
+
```
|
| 685 |
+
|
| 686 |
+
### Timeline Events
|
| 687 |
+
|
| 688 |
+
```css
|
| 689 |
+
.timeline-events {
|
| 690 |
+
margin: 2rem 0;
|
| 691 |
+
}
|
| 692 |
+
|
| 693 |
+
.event {
|
| 694 |
+
padding-left: 1.5rem;
|
| 695 |
+
border-left: 4px solid #dc2626;
|
| 696 |
+
margin-bottom: 1.5rem;
|
| 697 |
+
}
|
| 698 |
+
|
| 699 |
+
.event-date {
|
| 700 |
+
font-weight: 600;
|
| 701 |
+
color: #ffffff;
|
| 702 |
+
margin: 0 0 0.25rem 0;
|
| 703 |
+
}
|
| 704 |
+
```
|
| 705 |
+
|
| 706 |
+
---
|
| 707 |
+
|
| 708 |
+
## Step Data Patterns
|
| 709 |
+
|
| 710 |
+
### Standard Text Steps
|
| 711 |
+
|
| 712 |
+
```typescript
|
| 713 |
+
const steps = [
|
| 714 |
+
{
|
| 715 |
+
title: 'September 23, 2021',
|
| 716 |
+
text: 'Event description with <strong>emphasis</strong>.',
|
| 717 |
+
source: { text: 'Reuters', url: 'https://...' },
|
| 718 |
+
imageCredit: 'Photographer Name'
|
| 719 |
+
}
|
| 720 |
+
];
|
| 721 |
+
```
|
| 722 |
+
|
| 723 |
+
### Raw HTML Steps (Custom Cards)
|
| 724 |
+
|
| 725 |
+
```typescript
|
| 726 |
+
const steps = [
|
| 727 |
+
{
|
| 728 |
+
raw: true,
|
| 729 |
+
text: `
|
| 730 |
+
<div class="custom-card">
|
| 731 |
+
<span class="date">August 4, 2025</span>
|
| 732 |
+
<blockquote>"Quote text here"</blockquote>
|
| 733 |
+
<a href="..." class="source-link">View source</a>
|
| 734 |
+
</div>
|
| 735 |
+
`
|
| 736 |
+
}
|
| 737 |
+
];
|
| 738 |
+
```
|
| 739 |
+
|
| 740 |
+
### Hero Header (First Step)
|
| 741 |
+
|
| 742 |
+
```typescript
|
| 743 |
+
const heroSteps = [
|
| 744 |
+
{
|
| 745 |
+
raw: true,
|
| 746 |
+
text: `
|
| 747 |
+
<div class="hero-header">
|
| 748 |
+
<h1 class="hero-title">Article Title</h1>
|
| 749 |
+
<p class="hero-desc">Subtitle description</p>
|
| 750 |
+
<div class="hero-byline">
|
| 751 |
+
<p class="byline-label">By</p>
|
| 752 |
+
<p class="byline-authors">Author Names</p>
|
| 753 |
+
<p class="byline-date">January 2026</p>
|
| 754 |
+
</div>
|
| 755 |
+
</div>
|
| 756 |
+
`
|
| 757 |
+
},
|
| 758 |
+
// ... more steps
|
| 759 |
+
];
|
| 760 |
+
```
|
| 761 |
+
|
| 762 |
+
---
|
| 763 |
+
|
| 764 |
+
## Fade Effects
|
| 765 |
+
|
| 766 |
+
### Fade-to-Black at Section End
|
| 767 |
+
|
| 768 |
+
```typescript
|
| 769 |
+
let scrollProgress = $state(0);
|
| 770 |
+
|
| 771 |
+
// Calculate fade: starts at 75% scroll, complete at 100%
|
| 772 |
+
let fadeProgress = $derived(() => {
|
| 773 |
+
const fadeStart = 0.75;
|
| 774 |
+
if (scrollProgress < fadeStart) return 0;
|
| 775 |
+
return Math.min(1, (scrollProgress - fadeStart) / (1 - fadeStart));
|
| 776 |
+
});
|
| 777 |
+
```
|
| 778 |
+
|
| 779 |
+
### Fade-in at Section Start
|
| 780 |
+
|
| 781 |
+
```typescript
|
| 782 |
+
// Fade from black (1) to visible (0) over first 25%
|
| 783 |
+
let fadeInProgress = $derived(() => {
|
| 784 |
+
const fadeEnd = 0.25;
|
| 785 |
+
if (scrollProgress >= fadeEnd) return 0;
|
| 786 |
+
return 1 - (scrollProgress / fadeEnd);
|
| 787 |
+
});
|
| 788 |
+
```
|
| 789 |
+
|
| 790 |
+
### Applying Fade Overlays
|
| 791 |
+
|
| 792 |
+
```svelte
|
| 793 |
+
{#if fadeProgress > 0}
|
| 794 |
+
<div class="fade-overlay" style:opacity={fadeProgress}></div>
|
| 795 |
+
{/if}
|
| 796 |
+
|
| 797 |
+
<style>
|
| 798 |
+
.fade-overlay {
|
| 799 |
+
position: absolute;
|
| 800 |
+
inset: 0;
|
| 801 |
+
background: #0a0a0a;
|
| 802 |
+
pointer-events: none;
|
| 803 |
+
z-index: 10;
|
| 804 |
+
}
|
| 805 |
+
</style>
|
| 806 |
+
```
|
| 807 |
+
|
| 808 |
+
---
|
| 809 |
+
|
| 810 |
+
## Complete Page Structure
|
| 811 |
+
|
| 812 |
+
```svelte
|
| 813 |
+
<div class="article-container">
|
| 814 |
+
<!-- Hero Scrolly -->
|
| 815 |
+
<ScrollySection bind:activeStep={heroStep} steps={heroSteps}>
|
| 816 |
+
{#snippet children({ activeStep })}
|
| 817 |
+
<HeroVisualization ... />
|
| 818 |
+
{/snippet}
|
| 819 |
+
</ScrollySection>
|
| 820 |
+
|
| 821 |
+
<!-- Data Visualization Scrolly -->
|
| 822 |
+
<ScrollySection bind:activeStep={dataStep} steps={dataSteps}>
|
| 823 |
+
{#snippet children({ activeStep })}
|
| 824 |
+
<DataVisualization ... />
|
| 825 |
+
{/snippet}
|
| 826 |
+
</ScrollySection>
|
| 827 |
+
|
| 828 |
+
<!-- Regular Content Section -->
|
| 829 |
+
<section class="content-section">
|
| 830 |
+
<div class="content-container">
|
| 831 |
+
<h2>Section Title</h2>
|
| 832 |
+
<div class="prose-content">...</div>
|
| 833 |
+
</div>
|
| 834 |
+
</section>
|
| 835 |
+
|
| 836 |
+
<!-- Map Scrolly -->
|
| 837 |
+
<ScrollySection bind:activeStep={mapStep} steps={mapSteps}>
|
| 838 |
+
{#snippet children({ activeStep })}
|
| 839 |
+
<MapScrolly ... />
|
| 840 |
+
{/snippet}
|
| 841 |
+
</ScrollySection>
|
| 842 |
+
|
| 843 |
+
<!-- Footer -->
|
| 844 |
+
<footer class="article-footer">...</footer>
|
| 845 |
+
</div>
|
| 846 |
+
|
| 847 |
+
<style>
|
| 848 |
+
.article-container {
|
| 849 |
+
background: #0a0a0a;
|
| 850 |
+
min-height: 100vh;
|
| 851 |
+
}
|
| 852 |
+
|
| 853 |
+
:global(html), :global(body) {
|
| 854 |
+
overflow-x: clip;
|
| 855 |
+
}
|
| 856 |
+
</style>
|
| 857 |
+
```
|
| 858 |
+
|
| 859 |
+
---
|
| 860 |
+
|
| 861 |
+
## Troubleshooting
|
| 862 |
+
|
| 863 |
+
### Video Scrolly Not Visible
|
| 864 |
+
|
| 865 |
+
1. Check z-index on surrounding sections (remove `z-10`)
|
| 866 |
+
2. Ensure no `position: fixed` overlays covering viewport
|
| 867 |
+
3. Verify video files exist at specified paths
|
| 868 |
+
|
| 869 |
+
### Dark Overlay Covering Entire Page
|
| 870 |
+
|
| 871 |
+
**Cause**: Fade overlay using `position: fixed` instead of `absolute`
|
| 872 |
+
|
| 873 |
+
**Fix**:
|
| 874 |
+
```css
|
| 875 |
+
.fade-overlay {
|
| 876 |
+
position: absolute; /* Not fixed! */
|
| 877 |
+
inset: 0;
|
| 878 |
+
}
|
| 879 |
+
```
|
| 880 |
+
|
| 881 |
+
### Sticky Not Working
|
| 882 |
+
|
| 883 |
+
1. Check for `overflow: hidden` on ancestors (use `overflow-x: clip`)
|
| 884 |
+
2. Verify `position: sticky` has `top: 0`
|
| 885 |
+
3. Ensure container has defined height or content
|
| 886 |
+
|
| 887 |
+
### Steps Not Triggering
|
| 888 |
+
|
| 889 |
+
1. Verify step elements have sufficient height (`min-height: 70vh`)
|
| 890 |
+
2. Check IntersectionObserver margin settings
|
| 891 |
+
3. Ensure steps are direct children of ScrollyHelper
|
| 892 |
+
|
| 893 |
+
---
|
| 894 |
+
|
| 895 |
+
## Typography Guidelines
|
| 896 |
+
|
| 897 |
+
```css
|
| 898 |
+
/* Headings */
|
| 899 |
+
font-family: 'Playfair Display', Georgia, serif;
|
| 900 |
+
|
| 901 |
+
/* Body text */
|
| 902 |
+
font-family: 'Source Sans 3', system-ui, sans-serif;
|
| 903 |
+
|
| 904 |
+
/* Monospace/dates */
|
| 905 |
+
font-family: 'JetBrains Mono', 'Courier New', monospace;
|
| 906 |
+
```
|
| 907 |
+
|
| 908 |
+
### Font Loading
|
| 909 |
+
|
| 910 |
+
```svelte
|
| 911 |
+
<svelte:head>
|
| 912 |
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
| 913 |
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="anonymous" />
|
| 914 |
+
<link
|
| 915 |
+
href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500&family=Playfair+Display:wght@400;500;600;700&family=Source+Sans+3:wght@300;400;500;600&display=swap"
|
| 916 |
+
rel="stylesheet"
|
| 917 |
+
/>
|
| 918 |
+
</svelte:head>
|
| 919 |
+
```
|
| 920 |
+
|
| 921 |
+
---
|
| 922 |
+
|
| 923 |
+
## Dependencies
|
| 924 |
+
|
| 925 |
+
```json
|
| 926 |
+
{
|
| 927 |
+
"dependencies": {
|
| 928 |
+
"svelte": "^5.0.0",
|
| 929 |
+
"@sveltejs/kit": "^2.0.0"
|
| 930 |
+
}
|
| 931 |
+
}
|
| 932 |
+
```
|
| 933 |
+
|
| 934 |
+
Optional for specific visualizations:
|
| 935 |
+
- `mapbox-gl` or `@maptiler/sdk` for maps
|
| 936 |
+
- `d3` for data visualizations
|
| 937 |
+
- `gsap` for complex animations
|
skills/scrolly-sveltekit/references/HeroVisualization.svelte
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<script lang="ts">
|
| 2 |
+
/**
|
| 3 |
+
* HeroVisualization - Single image with src swapping
|
| 4 |
+
* Images change via direct calls from parent
|
| 5 |
+
*/
|
| 6 |
+
|
| 7 |
+
interface Props {
|
| 8 |
+
currentImage: string;
|
| 9 |
+
fadeProgress?: number; // 0-1, controls fade-to-black overlay
|
| 10 |
+
}
|
| 11 |
+
|
| 12 |
+
let { currentImage, fadeProgress = 0 }: Props = $props();
|
| 13 |
+
</script>
|
| 14 |
+
|
| 15 |
+
<div class="hero-visualization">
|
| 16 |
+
<img src={currentImage} alt="Assam eviction scene" class="hero-image" />
|
| 17 |
+
<div class="hero-overlay"></div>
|
| 18 |
+
{#if fadeProgress > 0}
|
| 19 |
+
<div class="fade-overlay" style:opacity={fadeProgress}></div>
|
| 20 |
+
{/if}
|
| 21 |
+
</div>
|
| 22 |
+
|
| 23 |
+
<style>
|
| 24 |
+
.hero-visualization {
|
| 25 |
+
position: absolute;
|
| 26 |
+
inset: 0;
|
| 27 |
+
width: 100%;
|
| 28 |
+
height: 100%;
|
| 29 |
+
background: #000;
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
.hero-image {
|
| 33 |
+
position: absolute;
|
| 34 |
+
inset: 0;
|
| 35 |
+
width: 100%;
|
| 36 |
+
height: 100%;
|
| 37 |
+
object-fit: cover;
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
.hero-overlay {
|
| 41 |
+
position: absolute;
|
| 42 |
+
inset: 0;
|
| 43 |
+
background: linear-gradient(
|
| 44 |
+
to bottom,
|
| 45 |
+
rgba(0, 0, 0, 0.5) 0%,
|
| 46 |
+
rgba(0, 0, 0, 0.3) 50%,
|
| 47 |
+
rgba(0, 0, 0, 0.5) 100%
|
| 48 |
+
);
|
| 49 |
+
}
|
| 50 |
+
|
| 51 |
+
.fade-overlay {
|
| 52 |
+
position: absolute;
|
| 53 |
+
inset: 0;
|
| 54 |
+
background: #0a0a0a;
|
| 55 |
+
pointer-events: none;
|
| 56 |
+
}
|
| 57 |
+
</style>
|
skills/scrolly-sveltekit/references/MapScrolly.svelte
ADDED
|
@@ -0,0 +1,346 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<script lang="ts">
|
| 2 |
+
/**
|
| 3 |
+
* MapScrolly - Interactive map visualization for eviction locations
|
| 4 |
+
*
|
| 5 |
+
* Displays a MapTiler map with GeoJSON overlays showing eviction
|
| 6 |
+
* locations and demolished villages. Receives activeStep from parent
|
| 7 |
+
* ScrollySection and flies to appropriate locations.
|
| 8 |
+
*/
|
| 9 |
+
import { onMount, onDestroy } from 'svelte';
|
| 10 |
+
|
| 11 |
+
// MapTiler API Key - TODO: Move to env variable
|
| 12 |
+
const MAPTILER_API_KEY = 'RAt0tXJKfw0bbmg4ox00';
|
| 13 |
+
|
| 14 |
+
interface Props {
|
| 15 |
+
activeStep: number;
|
| 16 |
+
fadeProgress?: number; // 0-1, controls fade-in overlay (1 = black, 0 = visible)
|
| 17 |
+
fadeOutProgress?: number; // 0-1, controls fade-out overlay at end (0 = visible, 1 = black)
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
let { activeStep, fadeProgress = 0, fadeOutProgress = 0 }: Props = $props();
|
| 21 |
+
|
| 22 |
+
// State
|
| 23 |
+
let map: any = null;
|
| 24 |
+
let mapContainer: HTMLDivElement | undefined = $state();
|
| 25 |
+
let steps = $state<any[]>([]);
|
| 26 |
+
let mapReady = $state(false);
|
| 27 |
+
let mapError = $state<string | null>(null);
|
| 28 |
+
let evictionsData: any = null;
|
| 29 |
+
let villagesData: any = null;
|
| 30 |
+
let checkSDKInterval: ReturnType<typeof setInterval> | null = null;
|
| 31 |
+
|
| 32 |
+
// Track previous step to detect changes
|
| 33 |
+
let prevStep = $state(-1);
|
| 34 |
+
|
| 35 |
+
// Load narrative steps and GeoJSON data
|
| 36 |
+
async function loadData() {
|
| 37 |
+
try {
|
| 38 |
+
const [narrativeRes, evictionsRes, villagesRes] = await Promise.all([
|
| 39 |
+
fetch('/data/cleared/map-narrative.json'),
|
| 40 |
+
fetch('/data/cleared/evictions.geojson'),
|
| 41 |
+
fetch('/data/cleared/villages_demolished.geojson')
|
| 42 |
+
]);
|
| 43 |
+
|
| 44 |
+
const narrativeData = await narrativeRes.json();
|
| 45 |
+
steps = narrativeData.steps;
|
| 46 |
+
|
| 47 |
+
evictionsData = await evictionsRes.json();
|
| 48 |
+
villagesData = await villagesRes.json();
|
| 49 |
+
} catch (error) {
|
| 50 |
+
console.error('Error loading map data:', error);
|
| 51 |
+
mapError = 'Failed to load map data';
|
| 52 |
+
}
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
async function initializeMap() {
|
| 56 |
+
if (!window.maptilersdk || !mapContainer) {
|
| 57 |
+
mapError = 'MapTiler SDK not loaded';
|
| 58 |
+
return;
|
| 59 |
+
}
|
| 60 |
+
|
| 61 |
+
try {
|
| 62 |
+
window.maptilersdk.config.apiKey = MAPTILER_API_KEY;
|
| 63 |
+
|
| 64 |
+
const initialStep = steps[0] || {
|
| 65 |
+
coordinates: [92.9376, 26.2006],
|
| 66 |
+
zoom: 7,
|
| 67 |
+
pitch: 0,
|
| 68 |
+
bearing: 0
|
| 69 |
+
};
|
| 70 |
+
|
| 71 |
+
map = new window.maptilersdk.Map({
|
| 72 |
+
container: mapContainer,
|
| 73 |
+
style: `https://api.maptiler.com/maps/019bb7f2-4716-7d50-a72b-f38b90548534/style.json?key=${MAPTILER_API_KEY}`,
|
| 74 |
+
center: initialStep.coordinates,
|
| 75 |
+
zoom: initialStep.zoom,
|
| 76 |
+
pitch: initialStep.pitch,
|
| 77 |
+
bearing: initialStep.bearing,
|
| 78 |
+
interactive: false,
|
| 79 |
+
scrollZoom: false,
|
| 80 |
+
boxZoom: false,
|
| 81 |
+
dragRotate: false,
|
| 82 |
+
dragPan: false,
|
| 83 |
+
keyboard: false,
|
| 84 |
+
doubleClickZoom: false,
|
| 85 |
+
touchZoomRotate: false,
|
| 86 |
+
navigationControl: false,
|
| 87 |
+
attributionControl: true
|
| 88 |
+
});
|
| 89 |
+
|
| 90 |
+
await map.onReadyAsync();
|
| 91 |
+
addMapLayers();
|
| 92 |
+
mapReady = true;
|
| 93 |
+
|
| 94 |
+
// Fly to initial step if activeStep > 0
|
| 95 |
+
if (activeStep > 0 && steps[activeStep]) {
|
| 96 |
+
flyToStep(activeStep);
|
| 97 |
+
}
|
| 98 |
+
} catch (error) {
|
| 99 |
+
console.error('Error initializing map:', error);
|
| 100 |
+
mapError = 'Failed to initialize map';
|
| 101 |
+
}
|
| 102 |
+
}
|
| 103 |
+
|
| 104 |
+
function addMapLayers() {
|
| 105 |
+
if (!evictionsData || !villagesData || !map) return;
|
| 106 |
+
|
| 107 |
+
// Add evictions source and layer
|
| 108 |
+
map.addSource('evictions', {
|
| 109 |
+
type: 'geojson',
|
| 110 |
+
data: evictionsData
|
| 111 |
+
});
|
| 112 |
+
|
| 113 |
+
map.addLayer({
|
| 114 |
+
id: 'evictions-circles',
|
| 115 |
+
type: 'circle',
|
| 116 |
+
source: 'evictions',
|
| 117 |
+
paint: {
|
| 118 |
+
'circle-radius': [
|
| 119 |
+
'interpolate',
|
| 120 |
+
['linear'],
|
| 121 |
+
['get', 'people_evicted'],
|
| 122 |
+
100, 5,
|
| 123 |
+
5000, 12,
|
| 124 |
+
15000, 22
|
| 125 |
+
],
|
| 126 |
+
'circle-color': '#9F3E52',
|
| 127 |
+
'circle-opacity': 0.6,
|
| 128 |
+
'circle-stroke-width': 1,
|
| 129 |
+
'circle-stroke-color': '#ffffff',
|
| 130 |
+
'circle-stroke-opacity': 0.3
|
| 131 |
+
}
|
| 132 |
+
});
|
| 133 |
+
|
| 134 |
+
// Add villages source and layer (initially hidden)
|
| 135 |
+
map.addSource('villages', {
|
| 136 |
+
type: 'geojson',
|
| 137 |
+
data: villagesData
|
| 138 |
+
});
|
| 139 |
+
|
| 140 |
+
map.addLayer({
|
| 141 |
+
id: 'villages-circles',
|
| 142 |
+
type: 'circle',
|
| 143 |
+
source: 'villages',
|
| 144 |
+
paint: {
|
| 145 |
+
'circle-radius': [
|
| 146 |
+
'interpolate',
|
| 147 |
+
['linear'],
|
| 148 |
+
['get', 'population_displaced'],
|
| 149 |
+
100, 8,
|
| 150 |
+
3000, 14,
|
| 151 |
+
9000, 20
|
| 152 |
+
],
|
| 153 |
+
'circle-color': '#35B58B',
|
| 154 |
+
'circle-opacity': 0,
|
| 155 |
+
'circle-stroke-width': 2,
|
| 156 |
+
'circle-stroke-color': '#ffffff',
|
| 157 |
+
'circle-stroke-opacity': 0
|
| 158 |
+
}
|
| 159 |
+
});
|
| 160 |
+
|
| 161 |
+
// Add village labels layer
|
| 162 |
+
map.addLayer({
|
| 163 |
+
id: 'villages-labels',
|
| 164 |
+
type: 'symbol',
|
| 165 |
+
source: 'villages',
|
| 166 |
+
layout: {
|
| 167 |
+
'text-field': ['get', 'name'],
|
| 168 |
+
'text-size': 12,
|
| 169 |
+
'text-offset': [0, 1.5],
|
| 170 |
+
'text-anchor': 'top'
|
| 171 |
+
},
|
| 172 |
+
paint: {
|
| 173 |
+
'text-color': '#ffffff',
|
| 174 |
+
'text-halo-color': '#000000',
|
| 175 |
+
'text-halo-width': 1,
|
| 176 |
+
'text-opacity': 0
|
| 177 |
+
}
|
| 178 |
+
});
|
| 179 |
+
}
|
| 180 |
+
|
| 181 |
+
function updateLayerVisibility(stepIndex: number) {
|
| 182 |
+
if (!map || !mapReady) return;
|
| 183 |
+
|
| 184 |
+
const step = steps[stepIndex];
|
| 185 |
+
if (!step?.layers) return;
|
| 186 |
+
|
| 187 |
+
// Update evictions layer
|
| 188 |
+
if (step.layers.evictions) {
|
| 189 |
+
map.setPaintProperty(
|
| 190 |
+
'evictions-circles',
|
| 191 |
+
'circle-opacity',
|
| 192 |
+
step.layers.evictions.opacity || 0.6
|
| 193 |
+
);
|
| 194 |
+
}
|
| 195 |
+
|
| 196 |
+
// Update villages layer
|
| 197 |
+
if (step.layers.villages) {
|
| 198 |
+
const villageOpacity = step.layers.villages.visible ? (step.layers.villages.opacity || 1.0) : 0;
|
| 199 |
+
map.setPaintProperty('villages-circles', 'circle-opacity', villageOpacity);
|
| 200 |
+
map.setPaintProperty('villages-circles', 'circle-stroke-opacity', villageOpacity);
|
| 201 |
+
map.setPaintProperty('villages-labels', 'text-opacity', villageOpacity > 0.5 ? 1 : 0);
|
| 202 |
+
|
| 203 |
+
// Highlight effect for final step
|
| 204 |
+
if (step.layers.villages.highlight) {
|
| 205 |
+
map.setPaintProperty('villages-circles', 'circle-stroke-width', 3);
|
| 206 |
+
map.setPaintProperty('villages-circles', 'circle-color', '#35B58B');
|
| 207 |
+
} else {
|
| 208 |
+
map.setPaintProperty('villages-circles', 'circle-stroke-width', 2);
|
| 209 |
+
}
|
| 210 |
+
}
|
| 211 |
+
}
|
| 212 |
+
|
| 213 |
+
function flyToStep(index: number) {
|
| 214 |
+
if (!map || !mapReady) return;
|
| 215 |
+
|
| 216 |
+
const step = steps[index];
|
| 217 |
+
if (!step) return;
|
| 218 |
+
|
| 219 |
+
map.flyTo({
|
| 220 |
+
center: step.coordinates,
|
| 221 |
+
zoom: step.zoom,
|
| 222 |
+
bearing: step.bearing || 0,
|
| 223 |
+
pitch: step.pitch || 0,
|
| 224 |
+
duration: step.duration || 2000,
|
| 225 |
+
easing: (t: number) => t * (2 - t)
|
| 226 |
+
});
|
| 227 |
+
|
| 228 |
+
updateLayerVisibility(index);
|
| 229 |
+
}
|
| 230 |
+
|
| 231 |
+
// Effect to fly to step when activeStep changes
|
| 232 |
+
$effect(() => {
|
| 233 |
+
if (mapReady && activeStep !== prevStep) {
|
| 234 |
+
prevStep = activeStep;
|
| 235 |
+
flyToStep(activeStep);
|
| 236 |
+
}
|
| 237 |
+
});
|
| 238 |
+
|
| 239 |
+
onMount(async () => {
|
| 240 |
+
await loadData();
|
| 241 |
+
|
| 242 |
+
// Wait for SDK to be available with cleanup
|
| 243 |
+
checkSDKInterval = setInterval(() => {
|
| 244 |
+
if (window.maptilersdk) {
|
| 245 |
+
if (checkSDKInterval) {
|
| 246 |
+
clearInterval(checkSDKInterval);
|
| 247 |
+
checkSDKInterval = null;
|
| 248 |
+
}
|
| 249 |
+
initializeMap();
|
| 250 |
+
}
|
| 251 |
+
}, 100);
|
| 252 |
+
|
| 253 |
+
// Timeout after 10 seconds
|
| 254 |
+
setTimeout(() => {
|
| 255 |
+
if (checkSDKInterval) {
|
| 256 |
+
clearInterval(checkSDKInterval);
|
| 257 |
+
checkSDKInterval = null;
|
| 258 |
+
if (!mapReady) {
|
| 259 |
+
mapError = 'Map SDK failed to load';
|
| 260 |
+
}
|
| 261 |
+
}
|
| 262 |
+
}, 10000);
|
| 263 |
+
});
|
| 264 |
+
|
| 265 |
+
onDestroy(() => {
|
| 266 |
+
// Clean up interval if still running
|
| 267 |
+
if (checkSDKInterval) {
|
| 268 |
+
clearInterval(checkSDKInterval);
|
| 269 |
+
checkSDKInterval = null;
|
| 270 |
+
}
|
| 271 |
+
// Clean up map
|
| 272 |
+
if (map) {
|
| 273 |
+
map.remove();
|
| 274 |
+
map = null;
|
| 275 |
+
}
|
| 276 |
+
});
|
| 277 |
+
</script>
|
| 278 |
+
|
| 279 |
+
<svelte:head>
|
| 280 |
+
<link rel="preconnect" href="https://cdn.maptiler.com" />
|
| 281 |
+
<link rel="dns-prefetch" href="https://cdn.maptiler.com" />
|
| 282 |
+
<script src="https://cdn.maptiler.com/maptiler-sdk-js/v3.6.1/maptiler-sdk.umd.min.js"></script>
|
| 283 |
+
<link href="https://cdn.maptiler.com/maptiler-sdk-js/v3.6.1/maptiler-sdk.css" rel="stylesheet" />
|
| 284 |
+
</svelte:head>
|
| 285 |
+
|
| 286 |
+
<div class="map-viz">
|
| 287 |
+
<div class="map" bind:this={mapContainer}></div>
|
| 288 |
+
|
| 289 |
+
{#if mapError}
|
| 290 |
+
<div class="map-error">
|
| 291 |
+
<p>{mapError}</p>
|
| 292 |
+
</div>
|
| 293 |
+
{:else if !mapReady}
|
| 294 |
+
<div class="map-loading">
|
| 295 |
+
<p>Loading map...</p>
|
| 296 |
+
</div>
|
| 297 |
+
{/if}
|
| 298 |
+
|
| 299 |
+
{#if fadeProgress > 0 || fadeOutProgress > 0}
|
| 300 |
+
<div class="fade-overlay" style:opacity={Math.max(fadeProgress, fadeOutProgress)}></div>
|
| 301 |
+
{/if}
|
| 302 |
+
</div>
|
| 303 |
+
|
| 304 |
+
<style>
|
| 305 |
+
.map-viz {
|
| 306 |
+
position: absolute;
|
| 307 |
+
inset: 0;
|
| 308 |
+
width: 100%;
|
| 309 |
+
height: 100%;
|
| 310 |
+
background: #0d0d0d;
|
| 311 |
+
}
|
| 312 |
+
|
| 313 |
+
.map {
|
| 314 |
+
width: 100%;
|
| 315 |
+
height: 100%;
|
| 316 |
+
}
|
| 317 |
+
|
| 318 |
+
.map-loading,
|
| 319 |
+
.map-error {
|
| 320 |
+
position: absolute;
|
| 321 |
+
inset: 0;
|
| 322 |
+
display: flex;
|
| 323 |
+
align-items: center;
|
| 324 |
+
justify-content: center;
|
| 325 |
+
background: rgba(13, 13, 13, 0.9);
|
| 326 |
+
}
|
| 327 |
+
|
| 328 |
+
.map-loading p,
|
| 329 |
+
.map-error p {
|
| 330 |
+
font-family: 'Source Sans 3', system-ui, sans-serif;
|
| 331 |
+
font-size: 1rem;
|
| 332 |
+
color: rgba(255, 255, 255, 0.6);
|
| 333 |
+
}
|
| 334 |
+
|
| 335 |
+
.map-error p {
|
| 336 |
+
color: #e57373;
|
| 337 |
+
}
|
| 338 |
+
|
| 339 |
+
.fade-overlay {
|
| 340 |
+
position: absolute;
|
| 341 |
+
inset: 0;
|
| 342 |
+
background: #0a0a0a;
|
| 343 |
+
pointer-events: none;
|
| 344 |
+
z-index: 10;
|
| 345 |
+
}
|
| 346 |
+
</style>
|
skills/scrolly-sveltekit/references/NoticeMosaic.svelte
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<script lang="ts">
|
| 2 |
+
/**
|
| 3 |
+
* NoticeMosaic - Scroll-triggered mosaic of eviction notices
|
| 4 |
+
*
|
| 5 |
+
* Displays document images in a scattered "evidence board" layout,
|
| 6 |
+
* progressively revealing them as activeStep increases. Includes an
|
| 7 |
+
* animated text placard showing details for each notice.
|
| 8 |
+
*/
|
| 9 |
+
import { fade } from 'svelte/transition';
|
| 10 |
+
import { onMount } from 'svelte';
|
| 11 |
+
|
| 12 |
+
export interface Notice {
|
| 13 |
+
image: string;
|
| 14 |
+
alt: string;
|
| 15 |
+
title: string;
|
| 16 |
+
subtitle: string;
|
| 17 |
+
excerpt: string;
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
interface Props {
|
| 21 |
+
activeStep: number;
|
| 22 |
+
notices: Notice[];
|
| 23 |
+
backgroundColor?: string;
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
let { activeStep, notices, backgroundColor = '#1a1715' }: Props = $props();
|
| 27 |
+
|
| 28 |
+
let viewportHeight = $state(0);
|
| 29 |
+
let isMobile = $state(false);
|
| 30 |
+
|
| 31 |
+
// Current notice index based on activeStep
|
| 32 |
+
let currentNoticeIndex = $derived(Math.min(activeStep, notices.length - 1));
|
| 33 |
+
|
| 34 |
+
// Pre-computed positions for each notice (deterministic based on index)
|
| 35 |
+
function getPosition(index: number): [string, string, number] {
|
| 36 |
+
const positions: [number, number, number][] = [
|
| 37 |
+
[20, 15, -3],
|
| 38 |
+
[50, 25, 2.5],
|
| 39 |
+
[15, 45, -1.5],
|
| 40 |
+
[55, 10, 3]
|
| 41 |
+
];
|
| 42 |
+
|
| 43 |
+
const pos = positions[index % positions.length];
|
| 44 |
+
|
| 45 |
+
// Add slight randomization for organic feel (seeded by index)
|
| 46 |
+
const seed = index * 7919;
|
| 47 |
+
const jitterX = ((seed % 10) - 5) * 0.5;
|
| 48 |
+
const jitterY = (((seed * 3) % 10) - 5) * 0.5;
|
| 49 |
+
const jitterRot = (((seed * 7) % 10) - 5) * 0.2;
|
| 50 |
+
|
| 51 |
+
return [
|
| 52 |
+
`${pos[0] + jitterX}%`,
|
| 53 |
+
`${pos[1] + jitterY}%`,
|
| 54 |
+
pos[2] + jitterRot
|
| 55 |
+
];
|
| 56 |
+
}
|
| 57 |
+
|
| 58 |
+
onMount(() => {
|
| 59 |
+
viewportHeight = window.innerHeight;
|
| 60 |
+
isMobile = window.innerWidth < 640;
|
| 61 |
+
|
| 62 |
+
const handleResize = () => {
|
| 63 |
+
viewportHeight = window.innerHeight;
|
| 64 |
+
isMobile = window.innerWidth < 640;
|
| 65 |
+
};
|
| 66 |
+
|
| 67 |
+
window.addEventListener('resize', handleResize);
|
| 68 |
+
return () => window.removeEventListener('resize', handleResize);
|
| 69 |
+
});
|
| 70 |
+
</script>
|
| 71 |
+
|
| 72 |
+
<div class="notice-mosaic" style:background={backgroundColor}>
|
| 73 |
+
<!-- Notice images -->
|
| 74 |
+
{#each notices as notice, i}
|
| 75 |
+
{@const pos = getPosition(i)}
|
| 76 |
+
{#if i <= currentNoticeIndex}
|
| 77 |
+
<div
|
| 78 |
+
class="notice-image"
|
| 79 |
+
class:mobile={isMobile}
|
| 80 |
+
style:left={pos[0]}
|
| 81 |
+
style:top={pos[1]}
|
| 82 |
+
style:transform="rotate({pos[2]}deg)"
|
| 83 |
+
in:fade={{ duration: 350 }}
|
| 84 |
+
>
|
| 85 |
+
<img src={notice.image} alt={notice.alt} loading="lazy" />
|
| 86 |
+
</div>
|
| 87 |
+
{/if}
|
| 88 |
+
{/each}
|
| 89 |
+
|
| 90 |
+
<!-- Text placard -->
|
| 91 |
+
<div class="notice-placard" class:mobile={isMobile}>
|
| 92 |
+
{#if notices[currentNoticeIndex]}
|
| 93 |
+
{#key currentNoticeIndex}
|
| 94 |
+
<div
|
| 95 |
+
class="placard-content"
|
| 96 |
+
in:fade={{ duration: 250, delay: 100 }}
|
| 97 |
+
out:fade={{ duration: 150 }}
|
| 98 |
+
>
|
| 99 |
+
<p class="placard-title">{notices[currentNoticeIndex].title}</p>
|
| 100 |
+
<p class="placard-subtitle">{notices[currentNoticeIndex].subtitle}</p>
|
| 101 |
+
<p class="placard-excerpt">{notices[currentNoticeIndex].excerpt}</p>
|
| 102 |
+
</div>
|
| 103 |
+
{/key}
|
| 104 |
+
{:else}
|
| 105 |
+
<div class="placard-content placard-empty">
|
| 106 |
+
<p class="placard-subtitle">Scroll to reveal eviction notices</p>
|
| 107 |
+
</div>
|
| 108 |
+
{/if}
|
| 109 |
+
</div>
|
| 110 |
+
</div>
|
| 111 |
+
|
| 112 |
+
<style>
|
| 113 |
+
.notice-mosaic {
|
| 114 |
+
position: absolute;
|
| 115 |
+
inset: 0;
|
| 116 |
+
width: 100%;
|
| 117 |
+
height: 100%;
|
| 118 |
+
overflow: hidden;
|
| 119 |
+
}
|
| 120 |
+
|
| 121 |
+
/* Notice image cards - sized like photos on a desk */
|
| 122 |
+
.notice-image {
|
| 123 |
+
position: absolute;
|
| 124 |
+
max-width: 25vw;
|
| 125 |
+
box-shadow:
|
| 126 |
+
0 2px 4px rgba(0, 0, 0, 0.1),
|
| 127 |
+
0 4px 8px rgba(0, 0, 0, 0.1),
|
| 128 |
+
0 8px 16px rgba(0, 0, 0, 0.15),
|
| 129 |
+
0 16px 32px rgba(0, 0, 0, 0.2);
|
| 130 |
+
border-radius: 2px;
|
| 131 |
+
background: #f5f2eb;
|
| 132 |
+
padding: 3px;
|
| 133 |
+
}
|
| 134 |
+
|
| 135 |
+
.notice-image.mobile {
|
| 136 |
+
max-width: 50vw;
|
| 137 |
+
}
|
| 138 |
+
|
| 139 |
+
.notice-image img {
|
| 140 |
+
display: block;
|
| 141 |
+
width: 100%;
|
| 142 |
+
height: auto;
|
| 143 |
+
border-radius: 1px;
|
| 144 |
+
}
|
| 145 |
+
|
| 146 |
+
/* Text placard - museum card style with fixed height to prevent jumping */
|
| 147 |
+
.notice-placard {
|
| 148 |
+
position: absolute;
|
| 149 |
+
bottom: 6%;
|
| 150 |
+
left: 50%;
|
| 151 |
+
transform: translateX(-50%);
|
| 152 |
+
width: 90%;
|
| 153 |
+
max-width: 480px;
|
| 154 |
+
height: 150px;
|
| 155 |
+
background: linear-gradient(135deg, #2a2622 0%, #1f1c19 100%);
|
| 156 |
+
border-left: 3px solid #c45c4a;
|
| 157 |
+
box-shadow:
|
| 158 |
+
0 4px 20px rgba(0, 0, 0, 0.4),
|
| 159 |
+
inset 0 1px 0 rgba(255, 255, 255, 0.05);
|
| 160 |
+
border-radius: 2px;
|
| 161 |
+
overflow: hidden;
|
| 162 |
+
}
|
| 163 |
+
|
| 164 |
+
.notice-placard.mobile {
|
| 165 |
+
bottom: 4%;
|
| 166 |
+
width: 94%;
|
| 167 |
+
height: 140px;
|
| 168 |
+
}
|
| 169 |
+
|
| 170 |
+
/* Content is absolutely positioned so fade doesn't affect container size */
|
| 171 |
+
.placard-content {
|
| 172 |
+
position: absolute;
|
| 173 |
+
inset: 0;
|
| 174 |
+
padding: 1.25rem 1.5rem;
|
| 175 |
+
display: flex;
|
| 176 |
+
flex-direction: column;
|
| 177 |
+
gap: 0.35rem;
|
| 178 |
+
}
|
| 179 |
+
|
| 180 |
+
.notice-placard.mobile .placard-content {
|
| 181 |
+
padding: 1rem 1.25rem;
|
| 182 |
+
}
|
| 183 |
+
|
| 184 |
+
.placard-empty {
|
| 185 |
+
justify-content: center;
|
| 186 |
+
}
|
| 187 |
+
|
| 188 |
+
.placard-title {
|
| 189 |
+
font-family: 'Source Sans 3', system-ui, sans-serif;
|
| 190 |
+
font-size: 1.25rem;
|
| 191 |
+
font-weight: 700;
|
| 192 |
+
color: #ffffff;
|
| 193 |
+
letter-spacing: 0.01em;
|
| 194 |
+
margin: 0;
|
| 195 |
+
}
|
| 196 |
+
|
| 197 |
+
.placard-subtitle {
|
| 198 |
+
font-family: 'Source Sans 3', system-ui, sans-serif;
|
| 199 |
+
font-size: 0.9375rem;
|
| 200 |
+
font-weight: 500;
|
| 201 |
+
color: #d4d0c8;
|
| 202 |
+
margin: 0;
|
| 203 |
+
}
|
| 204 |
+
|
| 205 |
+
.placard-excerpt {
|
| 206 |
+
font-family: 'Courier Prime', 'Courier New', monospace;
|
| 207 |
+
font-size: 1rem;
|
| 208 |
+
font-weight: 500;
|
| 209 |
+
font-style: italic;
|
| 210 |
+
color: #e8715f;
|
| 211 |
+
margin: 0.5rem 0 0 0;
|
| 212 |
+
line-height: 1.5;
|
| 213 |
+
}
|
| 214 |
+
|
| 215 |
+
/* Mobile adjustments */
|
| 216 |
+
@media (max-width: 640px) {
|
| 217 |
+
.placard-title {
|
| 218 |
+
font-size: 1rem;
|
| 219 |
+
}
|
| 220 |
+
|
| 221 |
+
.placard-subtitle {
|
| 222 |
+
font-size: 0.8125rem;
|
| 223 |
+
}
|
| 224 |
+
|
| 225 |
+
.placard-excerpt {
|
| 226 |
+
font-size: 0.875rem;
|
| 227 |
+
}
|
| 228 |
+
}
|
| 229 |
+
</style>
|
skills/scrolly-sveltekit/references/ScrollyHelper.svelte
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<script lang="ts">
|
| 2 |
+
/**
|
| 3 |
+
* ScrollyHelper - IntersectionObserver-based scroll step tracker
|
| 4 |
+
*
|
| 5 |
+
* Tracks which child element is most visible in the viewport and exports
|
| 6 |
+
* a reactive `value` binding indicating the current step index.
|
| 7 |
+
* Returns `undefined` when no steps are in view.
|
| 8 |
+
*
|
| 9 |
+
* Based on sleep-training's Scrolly.svelte pattern.
|
| 10 |
+
*
|
| 11 |
+
* Usage:
|
| 12 |
+
* <ScrollyHelper bind:value={currentStep}>
|
| 13 |
+
* <div class="step">Step 0</div>
|
| 14 |
+
* <div class="step">Step 1</div>
|
| 15 |
+
* <div class="step">Step 2</div>
|
| 16 |
+
* </ScrollyHelper>
|
| 17 |
+
*/
|
| 18 |
+
import { onMount } from 'svelte';
|
| 19 |
+
|
| 20 |
+
interface Props {
|
| 21 |
+
value?: number;
|
| 22 |
+
root?: Element | null;
|
| 23 |
+
top?: number;
|
| 24 |
+
bottom?: number;
|
| 25 |
+
increments?: number;
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
let {
|
| 29 |
+
value = $bindable(undefined),
|
| 30 |
+
root = null,
|
| 31 |
+
top = 0,
|
| 32 |
+
bottom = 0,
|
| 33 |
+
increments = 100
|
| 34 |
+
}: Props = $props();
|
| 35 |
+
|
| 36 |
+
let container: HTMLDivElement;
|
| 37 |
+
let nodes: NodeListOf<Element>;
|
| 38 |
+
let intersectionObservers: IntersectionObserver[] = [];
|
| 39 |
+
const steps: number[] = [];
|
| 40 |
+
const threshold: number[] = [];
|
| 41 |
+
|
| 42 |
+
function mostInView() {
|
| 43 |
+
let maxRatio = 0;
|
| 44 |
+
let maxIndex = 0;
|
| 45 |
+
for (let i = 0; i < steps.length; i++) {
|
| 46 |
+
if (steps[i] > maxRatio) {
|
| 47 |
+
maxRatio = steps[i];
|
| 48 |
+
maxIndex = i;
|
| 49 |
+
}
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
// Simple: return index if something visible, undefined otherwise
|
| 53 |
+
if (maxRatio > 0) value = maxIndex;
|
| 54 |
+
else value = undefined;
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
function createObserver(node: Element, index: number) {
|
| 58 |
+
const handleIntersect = (entries: IntersectionObserverEntry[]) => {
|
| 59 |
+
const ratio = entries[0].intersectionRatio;
|
| 60 |
+
steps[index] = ratio;
|
| 61 |
+
mostInView();
|
| 62 |
+
};
|
| 63 |
+
|
| 64 |
+
const marginTop = top ? top * -1 : 0;
|
| 65 |
+
const marginBottom = bottom ? bottom * -1 : 0;
|
| 66 |
+
const rootMargin = `${marginTop}px 0px ${marginBottom}px 0px`;
|
| 67 |
+
const options = { root, rootMargin, threshold };
|
| 68 |
+
|
| 69 |
+
if (intersectionObservers[index]) {
|
| 70 |
+
intersectionObservers[index].disconnect();
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
const io = new IntersectionObserver(handleIntersect, options);
|
| 74 |
+
io.observe(node);
|
| 75 |
+
intersectionObservers[index] = io;
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
function update() {
|
| 79 |
+
if (!nodes?.length) return;
|
| 80 |
+
nodes.forEach((node, index) => createObserver(node, index));
|
| 81 |
+
}
|
| 82 |
+
|
| 83 |
+
$effect(() => {
|
| 84 |
+
// Re-run update when top or bottom changes
|
| 85 |
+
top;
|
| 86 |
+
bottom;
|
| 87 |
+
update();
|
| 88 |
+
});
|
| 89 |
+
|
| 90 |
+
onMount(() => {
|
| 91 |
+
// Build threshold array (0 to 1 in increments)
|
| 92 |
+
for (let i = 0; i <= increments; i++) {
|
| 93 |
+
threshold.push(i / increments);
|
| 94 |
+
}
|
| 95 |
+
nodes = container.querySelectorAll(':scope > *:not(iframe)');
|
| 96 |
+
update();
|
| 97 |
+
|
| 98 |
+
return () => {
|
| 99 |
+
// Cleanup observers on unmount
|
| 100 |
+
intersectionObservers.forEach((io) => io?.disconnect());
|
| 101 |
+
};
|
| 102 |
+
});
|
| 103 |
+
</script>
|
| 104 |
+
|
| 105 |
+
<div bind:this={container}>
|
| 106 |
+
<slot />
|
| 107 |
+
</div>
|
skills/scrolly-sveltekit/references/ScrollySection.svelte
ADDED
|
@@ -0,0 +1,278 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<script lang="ts">
|
| 2 |
+
/**
|
| 3 |
+
* ScrollySection - Reusable scrollytelling container
|
| 4 |
+
*
|
| 5 |
+
* Based on the sleep-training pattern:
|
| 6 |
+
* - Sticky visualization container
|
| 7 |
+
* - Scroll steps with negative margin-top to overlap sticky
|
| 8 |
+
* - Text boxes appear ON TOP of visualization
|
| 9 |
+
* - STICKY LOGIC: preserves first/last step when scrolling past section
|
| 10 |
+
*/
|
| 11 |
+
import ScrollyHelper from './ScrollyHelper.svelte';
|
| 12 |
+
import ScrollyTextBox from './ScrollyTextBox.svelte';
|
| 13 |
+
import { onMount } from 'svelte';
|
| 14 |
+
|
| 15 |
+
interface SourceLink {
|
| 16 |
+
text: string;
|
| 17 |
+
url: string;
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
interface Step {
|
| 21 |
+
text?: string;
|
| 22 |
+
title?: string;
|
| 23 |
+
image?: string;
|
| 24 |
+
bgColor?: string;
|
| 25 |
+
raw?: boolean; // If true, render text without ScrollyTextBox wrapper
|
| 26 |
+
source?: SourceLink; // Optional source link for citations
|
| 27 |
+
imageCredit?: string; // Optional photo credit
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
interface Props {
|
| 31 |
+
activeStep?: number;
|
| 32 |
+
steps: Step[];
|
| 33 |
+
backgroundColor?: string;
|
| 34 |
+
showTextBoxes?: boolean;
|
| 35 |
+
textBoxVariant?: 'light' | 'dark';
|
| 36 |
+
textBoxPosition?: 'center' | 'left' | 'right';
|
| 37 |
+
firstStepOffset?: number; // Extra viewport fraction to push first step down (0 = default, 0.5 = 50vh lower)
|
| 38 |
+
onStepEnter?: (stepIndex: number, direction: 'up' | 'down') => void;
|
| 39 |
+
onScrollProgress?: (progress: number) => void;
|
| 40 |
+
children?: import('svelte').Snippet<[{ activeStep: number }]>;
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
let {
|
| 44 |
+
activeStep = $bindable(0),
|
| 45 |
+
steps,
|
| 46 |
+
backgroundColor = '#0a0a0a',
|
| 47 |
+
showTextBoxes = true,
|
| 48 |
+
textBoxVariant = 'light',
|
| 49 |
+
textBoxPosition = 'center',
|
| 50 |
+
firstStepOffset = 0,
|
| 51 |
+
onStepEnter,
|
| 52 |
+
onScrollProgress,
|
| 53 |
+
children
|
| 54 |
+
}: Props = $props();
|
| 55 |
+
|
| 56 |
+
// Raw value from ScrollyHelper (can be undefined when nothing visible)
|
| 57 |
+
let rawStep = $state<number | undefined>(undefined);
|
| 58 |
+
|
| 59 |
+
// Internal step that's always valid (applies sticky logic)
|
| 60 |
+
let internalStep = $state(0);
|
| 61 |
+
|
| 62 |
+
// Track previous step for direction detection
|
| 63 |
+
let previousStep = $state<number | undefined>(undefined);
|
| 64 |
+
|
| 65 |
+
// STICKY LOGIC: When rawStep is defined, use it. When undefined (scrolled past),
|
| 66 |
+
// internalStep naturally stays at its current value (first or last step).
|
| 67 |
+
$effect(() => {
|
| 68 |
+
if (rawStep !== undefined) {
|
| 69 |
+
internalStep = rawStep;
|
| 70 |
+
}
|
| 71 |
+
activeStep = internalStep;
|
| 72 |
+
});
|
| 73 |
+
|
| 74 |
+
// Fire onStepEnter callback when internalStep changes
|
| 75 |
+
$effect(() => {
|
| 76 |
+
if (internalStep !== previousStep) {
|
| 77 |
+
const direction = previousStep === undefined || internalStep > previousStep ? 'down' : 'up';
|
| 78 |
+
onStepEnter?.(internalStep, direction);
|
| 79 |
+
previousStep = internalStep;
|
| 80 |
+
}
|
| 81 |
+
});
|
| 82 |
+
|
| 83 |
+
let viewportHeight = $state(800);
|
| 84 |
+
let stepHeight = $derived(Math.max(viewportHeight * 0.7, 520));
|
| 85 |
+
let stepGap = $derived(Math.max(viewportHeight * 0.25, 240));
|
| 86 |
+
let triggerOffset = $derived(Math.round(viewportHeight * 0.25));
|
| 87 |
+
|
| 88 |
+
// Section element reference for scroll progress tracking
|
| 89 |
+
let sectionEl: HTMLElement | undefined = $state();
|
| 90 |
+
|
| 91 |
+
onMount(() => {
|
| 92 |
+
viewportHeight = window.innerHeight;
|
| 93 |
+
const handleResize = () => (viewportHeight = window.innerHeight);
|
| 94 |
+
window.addEventListener('resize', handleResize);
|
| 95 |
+
|
| 96 |
+
// Scroll progress tracking
|
| 97 |
+
const handleScroll = () => {
|
| 98 |
+
if (!sectionEl || !onScrollProgress) return;
|
| 99 |
+
|
| 100 |
+
const rect = sectionEl.getBoundingClientRect();
|
| 101 |
+
const sectionHeight = rect.height;
|
| 102 |
+
const scrolledPast = -rect.top; // How far we've scrolled into/past the section
|
| 103 |
+
const scrollableDistance = sectionHeight - viewportHeight;
|
| 104 |
+
|
| 105 |
+
// Progress: 0 = at top of section, 1 = scrolled past section
|
| 106 |
+
const progress = Math.max(0, Math.min(1, scrolledPast / scrollableDistance));
|
| 107 |
+
onScrollProgress(progress);
|
| 108 |
+
};
|
| 109 |
+
|
| 110 |
+
if (onScrollProgress) {
|
| 111 |
+
window.addEventListener('scroll', handleScroll, { passive: true });
|
| 112 |
+
handleScroll(); // Initial call
|
| 113 |
+
}
|
| 114 |
+
|
| 115 |
+
return () => {
|
| 116 |
+
window.removeEventListener('resize', handleResize);
|
| 117 |
+
if (onScrollProgress) {
|
| 118 |
+
window.removeEventListener('scroll', handleScroll);
|
| 119 |
+
}
|
| 120 |
+
};
|
| 121 |
+
});
|
| 122 |
+
</script>
|
| 123 |
+
|
| 124 |
+
<section
|
| 125 |
+
bind:this={sectionEl}
|
| 126 |
+
class="scroll-section"
|
| 127 |
+
style:background={backgroundColor}
|
| 128 |
+
style={`--vh:${viewportHeight}px`}
|
| 129 |
+
>
|
| 130 |
+
<div class="scroll-inner">
|
| 131 |
+
<!-- Sticky visualization layer -->
|
| 132 |
+
<div class="visual-layer">
|
| 133 |
+
{#if children}
|
| 134 |
+
{@render children({ activeStep: internalStep })}
|
| 135 |
+
{/if}
|
| 136 |
+
</div>
|
| 137 |
+
|
| 138 |
+
<!-- Text track (drives scroll + animation state) -->
|
| 139 |
+
<div class="text-track">
|
| 140 |
+
<ScrollyHelper bind:value={rawStep} top={triggerOffset} bottom={triggerOffset}>
|
| 141 |
+
{#each steps as step, i}
|
| 142 |
+
{@const isFirst = i === 0}
|
| 143 |
+
{@const isLast = i === steps.length - 1}
|
| 144 |
+
{@const active = internalStep === i}
|
| 145 |
+
<div
|
| 146 |
+
class="step"
|
| 147 |
+
class:active
|
| 148 |
+
class:step-first={isFirst}
|
| 149 |
+
style:min-height={isFirst && step.raw ? `${viewportHeight}px` : `${stepHeight}px`}
|
| 150 |
+
style:margin-bottom={`${isLast ? viewportHeight * 0.5 : stepGap}px`}
|
| 151 |
+
style:margin-top={isFirst && firstStepOffset > 0 ? `${viewportHeight * (-1 + firstStepOffset)}px` : undefined}
|
| 152 |
+
>
|
| 153 |
+
{#if step.raw && step.text}
|
| 154 |
+
<!-- Raw content without text box wrapper -->
|
| 155 |
+
<div class="step-raw">
|
| 156 |
+
{@html step.text}
|
| 157 |
+
</div>
|
| 158 |
+
{:else if showTextBoxes && step.text}
|
| 159 |
+
<div
|
| 160 |
+
class="step-content"
|
| 161 |
+
class:position-left={textBoxPosition === 'left'}
|
| 162 |
+
class:position-right={textBoxPosition === 'right'}
|
| 163 |
+
>
|
| 164 |
+
<ScrollyTextBox
|
| 165 |
+
title={step.title || ''}
|
| 166 |
+
image={step.image || null}
|
| 167 |
+
bgColor={step.bgColor || null}
|
| 168 |
+
source={step.source || null}
|
| 169 |
+
imageCredit={step.imageCredit || null}
|
| 170 |
+
{active}
|
| 171 |
+
variant={textBoxVariant}
|
| 172 |
+
>
|
| 173 |
+
{@html step.text}
|
| 174 |
+
</ScrollyTextBox>
|
| 175 |
+
</div>
|
| 176 |
+
{/if}
|
| 177 |
+
</div>
|
| 178 |
+
{/each}
|
| 179 |
+
</ScrollyHelper>
|
| 180 |
+
</div>
|
| 181 |
+
</div>
|
| 182 |
+
</section>
|
| 183 |
+
|
| 184 |
+
<style>
|
| 185 |
+
.scroll-section {
|
| 186 |
+
position: relative;
|
| 187 |
+
width: 100%;
|
| 188 |
+
isolation: isolate;
|
| 189 |
+
}
|
| 190 |
+
|
| 191 |
+
.scroll-inner {
|
| 192 |
+
position: relative;
|
| 193 |
+
}
|
| 194 |
+
|
| 195 |
+
.visual-layer {
|
| 196 |
+
position: sticky;
|
| 197 |
+
top: 0;
|
| 198 |
+
width: 100%;
|
| 199 |
+
height: 100vh;
|
| 200 |
+
min-height: var(--vh);
|
| 201 |
+
display: flex;
|
| 202 |
+
align-items: center;
|
| 203 |
+
justify-content: center;
|
| 204 |
+
z-index: 1;
|
| 205 |
+
}
|
| 206 |
+
|
| 207 |
+
.text-track {
|
| 208 |
+
position: relative;
|
| 209 |
+
z-index: 2;
|
| 210 |
+
max-width: 1200px;
|
| 211 |
+
margin: 0 auto;
|
| 212 |
+
padding: 0 1.5rem calc(var(--vh) * 0.4);
|
| 213 |
+
}
|
| 214 |
+
|
| 215 |
+
.step {
|
| 216 |
+
display: flex;
|
| 217 |
+
align-items: center;
|
| 218 |
+
justify-content: center;
|
| 219 |
+
opacity: 0.35;
|
| 220 |
+
transform: translateY(6px);
|
| 221 |
+
transition: opacity 220ms ease, transform 220ms ease;
|
| 222 |
+
pointer-events: none;
|
| 223 |
+
}
|
| 224 |
+
|
| 225 |
+
.step.active {
|
| 226 |
+
opacity: 1;
|
| 227 |
+
transform: translateY(0);
|
| 228 |
+
}
|
| 229 |
+
|
| 230 |
+
/* First step starts at negative margin to overlap sticky layer from top */
|
| 231 |
+
.step.step-first {
|
| 232 |
+
margin-top: calc(var(--vh) * -1);
|
| 233 |
+
}
|
| 234 |
+
|
| 235 |
+
.step-raw {
|
| 236 |
+
pointer-events: auto;
|
| 237 |
+
width: 100%;
|
| 238 |
+
display: flex;
|
| 239 |
+
align-items: center;
|
| 240 |
+
justify-content: center;
|
| 241 |
+
}
|
| 242 |
+
|
| 243 |
+
.step-content {
|
| 244 |
+
pointer-events: auto;
|
| 245 |
+
max-width: 480px;
|
| 246 |
+
width: 100%;
|
| 247 |
+
padding: 0 0.5rem;
|
| 248 |
+
}
|
| 249 |
+
|
| 250 |
+
.step-content.position-left {
|
| 251 |
+
margin-right: auto;
|
| 252 |
+
}
|
| 253 |
+
|
| 254 |
+
.step-content.position-right {
|
| 255 |
+
margin-left: auto;
|
| 256 |
+
}
|
| 257 |
+
|
| 258 |
+
@media (max-width: 900px) {
|
| 259 |
+
.text-track {
|
| 260 |
+
padding: calc(var(--vh) * 0.2) 1.25rem calc(var(--vh) * 0.35);
|
| 261 |
+
}
|
| 262 |
+
}
|
| 263 |
+
|
| 264 |
+
@media (max-width: 768px) {
|
| 265 |
+
.text-track {
|
| 266 |
+
padding: calc(var(--vh) * 0.18) 1rem calc(var(--vh) * 0.25);
|
| 267 |
+
}
|
| 268 |
+
|
| 269 |
+
.step-content {
|
| 270 |
+
max-width: 360px;
|
| 271 |
+
}
|
| 272 |
+
|
| 273 |
+
.step-content.position-left,
|
| 274 |
+
.step-content.position-right {
|
| 275 |
+
margin: 0 auto;
|
| 276 |
+
}
|
| 277 |
+
}
|
| 278 |
+
</style>
|
skills/scrolly-sveltekit/references/ScrollyTextBox.svelte
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<script lang="ts">
|
| 2 |
+
/**
|
| 3 |
+
* ScrollyTextBox - Unified text box component for scrollytelling sections
|
| 4 |
+
*
|
| 5 |
+
* Used in both photo scrolly and data visualization scrolly for consistent styling.
|
| 6 |
+
* Features active state transitions, responsive sizing, and slot-based content.
|
| 7 |
+
*/
|
| 8 |
+
import type { Snippet } from 'svelte';
|
| 9 |
+
|
| 10 |
+
interface SourceLink {
|
| 11 |
+
text: string;
|
| 12 |
+
url: string;
|
| 13 |
+
}
|
| 14 |
+
|
| 15 |
+
interface Props {
|
| 16 |
+
title?: string;
|
| 17 |
+
image?: string | null;
|
| 18 |
+
bgColor?: string | null;
|
| 19 |
+
active?: boolean;
|
| 20 |
+
variant?: 'light' | 'dark';
|
| 21 |
+
maxWidth?: 'narrow' | 'wide';
|
| 22 |
+
source?: SourceLink | null;
|
| 23 |
+
imageCredit?: string | null;
|
| 24 |
+
children?: Snippet;
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
let {
|
| 28 |
+
title = '',
|
| 29 |
+
image = null,
|
| 30 |
+
bgColor = null,
|
| 31 |
+
active = true,
|
| 32 |
+
variant = 'light',
|
| 33 |
+
maxWidth = 'narrow',
|
| 34 |
+
source = null,
|
| 35 |
+
imageCredit = null,
|
| 36 |
+
children
|
| 37 |
+
}: Props = $props();
|
| 38 |
+
</script>
|
| 39 |
+
|
| 40 |
+
<div
|
| 41 |
+
class="scrolly-text-box"
|
| 42 |
+
class:active
|
| 43 |
+
class:dark={variant === 'dark'}
|
| 44 |
+
class:wide={maxWidth === 'wide'}
|
| 45 |
+
class:has-image={image}
|
| 46 |
+
class:custom-bg={bgColor}
|
| 47 |
+
style:--custom-bg={bgColor}
|
| 48 |
+
>
|
| 49 |
+
{#if image}
|
| 50 |
+
<img src={image} alt="" class="box-image" />
|
| 51 |
+
{/if}
|
| 52 |
+
{#if title}
|
| 53 |
+
<h2 class="box-title">{title}</h2>
|
| 54 |
+
{/if}
|
| 55 |
+
<div class="box-content">
|
| 56 |
+
{@render children?.()}
|
| 57 |
+
</div>
|
| 58 |
+
{#if imageCredit || source}
|
| 59 |
+
<div class="box-footer">
|
| 60 |
+
{#if imageCredit}
|
| 61 |
+
<span class="image-credit">Photo: {imageCredit}</span>
|
| 62 |
+
{/if}
|
| 63 |
+
{#if imageCredit && source}
|
| 64 |
+
<span class="footer-divider">|</span>
|
| 65 |
+
{/if}
|
| 66 |
+
{#if source}
|
| 67 |
+
<a href={source.url} target="_blank" rel="noopener noreferrer" class="source-link">
|
| 68 |
+
Source: {source.text} →
|
| 69 |
+
</a>
|
| 70 |
+
{/if}
|
| 71 |
+
</div>
|
| 72 |
+
{/if}
|
| 73 |
+
</div>
|
| 74 |
+
|
| 75 |
+
<style>
|
| 76 |
+
.scrolly-text-box {
|
| 77 |
+
max-width: 380px;
|
| 78 |
+
background: rgba(255, 255, 255, 0.96);
|
| 79 |
+
backdrop-filter: blur(6px);
|
| 80 |
+
-webkit-backdrop-filter: blur(6px);
|
| 81 |
+
padding: 1.5rem 1.75rem;
|
| 82 |
+
border-radius: 6px;
|
| 83 |
+
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.35);
|
| 84 |
+
opacity: 0.75;
|
| 85 |
+
transform: translateY(8px);
|
| 86 |
+
transition: all 0.35s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
| 87 |
+
pointer-events: auto;
|
| 88 |
+
}
|
| 89 |
+
|
| 90 |
+
.scrolly-text-box.active {
|
| 91 |
+
opacity: 1;
|
| 92 |
+
transform: translateY(0);
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
.scrolly-text-box.wide {
|
| 96 |
+
max-width: 480px;
|
| 97 |
+
}
|
| 98 |
+
|
| 99 |
+
.scrolly-text-box.has-image {
|
| 100 |
+
max-width: 320px;
|
| 101 |
+
}
|
| 102 |
+
|
| 103 |
+
.box-image {
|
| 104 |
+
width: 100%;
|
| 105 |
+
height: auto;
|
| 106 |
+
border-radius: 4px;
|
| 107 |
+
margin-bottom: 1rem;
|
| 108 |
+
}
|
| 109 |
+
|
| 110 |
+
.scrolly-text-box.dark {
|
| 111 |
+
background: rgba(15, 15, 15, 0.92);
|
| 112 |
+
}
|
| 113 |
+
|
| 114 |
+
.scrolly-text-box.custom-bg {
|
| 115 |
+
background: var(--custom-bg);
|
| 116 |
+
}
|
| 117 |
+
|
| 118 |
+
.scrolly-text-box.dark .box-title,
|
| 119 |
+
.scrolly-text-box.custom-bg .box-title {
|
| 120 |
+
color: rgba(255, 255, 255, 0.95);
|
| 121 |
+
}
|
| 122 |
+
|
| 123 |
+
.scrolly-text-box.dark .box-content,
|
| 124 |
+
.scrolly-text-box.custom-bg .box-content {
|
| 125 |
+
color: rgba(255, 255, 255, 0.9);
|
| 126 |
+
}
|
| 127 |
+
|
| 128 |
+
.box-title {
|
| 129 |
+
font-family: 'Playfair Display', Georgia, serif;
|
| 130 |
+
font-size: 1.5rem;
|
| 131 |
+
font-weight: 600;
|
| 132 |
+
line-height: 1.25;
|
| 133 |
+
margin: 0 0 0.75rem 0;
|
| 134 |
+
color: #1a1a1a;
|
| 135 |
+
}
|
| 136 |
+
|
| 137 |
+
.box-content {
|
| 138 |
+
font-family: 'Source Sans 3', system-ui, sans-serif;
|
| 139 |
+
font-size: 1.05rem;
|
| 140 |
+
line-height: 1.65;
|
| 141 |
+
color: #2a2a2a;
|
| 142 |
+
}
|
| 143 |
+
|
| 144 |
+
.box-content :global(p) {
|
| 145 |
+
margin: 0;
|
| 146 |
+
}
|
| 147 |
+
|
| 148 |
+
.box-content :global(p + p) {
|
| 149 |
+
margin-top: 1em;
|
| 150 |
+
}
|
| 151 |
+
|
| 152 |
+
.box-content :global(strong) {
|
| 153 |
+
font-weight: 600;
|
| 154 |
+
color: #000;
|
| 155 |
+
}
|
| 156 |
+
|
| 157 |
+
.scrolly-text-box.dark .box-content :global(strong) {
|
| 158 |
+
color: #fff;
|
| 159 |
+
}
|
| 160 |
+
|
| 161 |
+
/* Category highlight colors - TNH branding */
|
| 162 |
+
.box-content :global(.highlight-env),
|
| 163 |
+
.box-content :global(.highlight-dev),
|
| 164 |
+
.box-content :global(.highlight-admin),
|
| 165 |
+
.box-content :global(.highlight-satra) {
|
| 166 |
+
color: white;
|
| 167 |
+
padding: 0.1em 0.4em;
|
| 168 |
+
border-radius: 3px;
|
| 169 |
+
font-weight: 500;
|
| 170 |
+
box-decoration-break: clone;
|
| 171 |
+
-webkit-box-decoration-break: clone;
|
| 172 |
+
}
|
| 173 |
+
|
| 174 |
+
.box-content :global(.highlight-env) {
|
| 175 |
+
background: #35B58B;
|
| 176 |
+
}
|
| 177 |
+
|
| 178 |
+
.box-content :global(.highlight-dev) {
|
| 179 |
+
background: #9F3E52;
|
| 180 |
+
}
|
| 181 |
+
|
| 182 |
+
.box-content :global(.highlight-admin) {
|
| 183 |
+
background: #E8A84C;
|
| 184 |
+
}
|
| 185 |
+
|
| 186 |
+
.box-content :global(.highlight-satra) {
|
| 187 |
+
background: #6B7FD7;
|
| 188 |
+
}
|
| 189 |
+
|
| 190 |
+
/* Footer with source and image credit */
|
| 191 |
+
.box-footer {
|
| 192 |
+
margin-top: 1rem;
|
| 193 |
+
padding-top: 0.75rem;
|
| 194 |
+
border-top: 1px solid rgba(0, 0, 0, 0.1);
|
| 195 |
+
font-family: 'Source Sans 3', system-ui, sans-serif;
|
| 196 |
+
font-size: 0.8rem;
|
| 197 |
+
color: rgba(0, 0, 0, 0.5);
|
| 198 |
+
display: flex;
|
| 199 |
+
flex-wrap: wrap;
|
| 200 |
+
align-items: center;
|
| 201 |
+
gap: 0.5rem;
|
| 202 |
+
}
|
| 203 |
+
|
| 204 |
+
.scrolly-text-box.dark .box-footer {
|
| 205 |
+
border-top-color: rgba(255, 255, 255, 0.15);
|
| 206 |
+
color: rgba(255, 255, 255, 0.5);
|
| 207 |
+
}
|
| 208 |
+
|
| 209 |
+
.image-credit {
|
| 210 |
+
font-style: italic;
|
| 211 |
+
}
|
| 212 |
+
|
| 213 |
+
.footer-divider {
|
| 214 |
+
opacity: 0.4;
|
| 215 |
+
}
|
| 216 |
+
|
| 217 |
+
.source-link {
|
| 218 |
+
color: inherit;
|
| 219 |
+
text-decoration: none;
|
| 220 |
+
transition: color 0.15s ease;
|
| 221 |
+
}
|
| 222 |
+
|
| 223 |
+
.source-link:hover {
|
| 224 |
+
color: rgba(0, 0, 0, 0.8);
|
| 225 |
+
text-decoration: underline;
|
| 226 |
+
}
|
| 227 |
+
|
| 228 |
+
.scrolly-text-box.dark .source-link:hover {
|
| 229 |
+
color: rgba(255, 255, 255, 0.9);
|
| 230 |
+
}
|
| 231 |
+
|
| 232 |
+
/* Responsive */
|
| 233 |
+
@media (max-width: 600px) {
|
| 234 |
+
.scrolly-text-box {
|
| 235 |
+
max-width: 320px;
|
| 236 |
+
padding: 1.25rem 1.5rem;
|
| 237 |
+
}
|
| 238 |
+
|
| 239 |
+
.scrolly-text-box.wide {
|
| 240 |
+
max-width: 340px;
|
| 241 |
+
}
|
| 242 |
+
|
| 243 |
+
.scrolly-text-box.has-image {
|
| 244 |
+
max-width: 280px;
|
| 245 |
+
padding: 1rem 1.25rem;
|
| 246 |
+
}
|
| 247 |
+
|
| 248 |
+
.box-image {
|
| 249 |
+
margin-bottom: 0.75rem;
|
| 250 |
+
}
|
| 251 |
+
|
| 252 |
+
.box-content {
|
| 253 |
+
font-size: 0.95rem;
|
| 254 |
+
}
|
| 255 |
+
|
| 256 |
+
.box-title {
|
| 257 |
+
font-size: 1.35rem;
|
| 258 |
+
}
|
| 259 |
+
}
|
| 260 |
+
</style>
|
skills/scrolly-sveltekit/references/VideoScrollyVisualization.svelte
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<script lang="ts">
|
| 2 |
+
/**
|
| 3 |
+
* VideoScrollyVisualization - Multi-video scrolly with step-based transitions
|
| 4 |
+
*
|
| 5 |
+
* Used inside ScrollySection to display multiple videos that swap based on
|
| 6 |
+
* activeStep. Includes video play/pause management and opacity transitions.
|
| 7 |
+
*/
|
| 8 |
+
|
| 9 |
+
interface VideoStep {
|
| 10 |
+
videoSrc: string;
|
| 11 |
+
poster?: string;
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
interface Props {
|
| 15 |
+
activeStep: number;
|
| 16 |
+
videoSteps: VideoStep[];
|
| 17 |
+
}
|
| 18 |
+
|
| 19 |
+
let { activeStep, videoSteps }: Props = $props();
|
| 20 |
+
|
| 21 |
+
let videoRefs: HTMLVideoElement[] = $state([]);
|
| 22 |
+
|
| 23 |
+
$effect(() => {
|
| 24 |
+
// Pause all, play active
|
| 25 |
+
videoRefs.forEach((video, i) => {
|
| 26 |
+
if (video) {
|
| 27 |
+
if (i === activeStep) {
|
| 28 |
+
video.play().catch(() => {}); // Handle autoplay restrictions
|
| 29 |
+
} else {
|
| 30 |
+
video.pause();
|
| 31 |
+
video.currentTime = 0;
|
| 32 |
+
}
|
| 33 |
+
}
|
| 34 |
+
});
|
| 35 |
+
});
|
| 36 |
+
</script>
|
| 37 |
+
|
| 38 |
+
<div class="video-scrolly-viz">
|
| 39 |
+
<!-- Video frame with padding for letterboxing effect -->
|
| 40 |
+
<div class="video-frame">
|
| 41 |
+
{#each videoSteps as step, i}
|
| 42 |
+
<video
|
| 43 |
+
src={step.videoSrc}
|
| 44 |
+
poster={step.poster}
|
| 45 |
+
bind:this={videoRefs[i]}
|
| 46 |
+
class="video-layer"
|
| 47 |
+
class:active={i === activeStep}
|
| 48 |
+
muted
|
| 49 |
+
loop
|
| 50 |
+
playsinline
|
| 51 |
+
preload="metadata"
|
| 52 |
+
/>
|
| 53 |
+
{/each}
|
| 54 |
+
</div>
|
| 55 |
+
|
| 56 |
+
<!-- Cinematic vignette overlay -->
|
| 57 |
+
<div class="video-vignette"></div>
|
| 58 |
+
</div>
|
| 59 |
+
|
| 60 |
+
<style>
|
| 61 |
+
.video-scrolly-viz {
|
| 62 |
+
position: absolute;
|
| 63 |
+
inset: 0;
|
| 64 |
+
width: 100%;
|
| 65 |
+
height: 100%;
|
| 66 |
+
background: #000;
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
.video-frame {
|
| 70 |
+
position: absolute;
|
| 71 |
+
inset: 1rem;
|
| 72 |
+
display: flex;
|
| 73 |
+
align-items: center;
|
| 74 |
+
justify-content: center;
|
| 75 |
+
}
|
| 76 |
+
|
| 77 |
+
@media (min-width: 768px) {
|
| 78 |
+
.video-frame {
|
| 79 |
+
inset: 2rem;
|
| 80 |
+
}
|
| 81 |
+
}
|
| 82 |
+
|
| 83 |
+
@media (min-width: 1024px) {
|
| 84 |
+
.video-frame {
|
| 85 |
+
inset: 3rem;
|
| 86 |
+
}
|
| 87 |
+
}
|
| 88 |
+
|
| 89 |
+
.video-layer {
|
| 90 |
+
position: absolute;
|
| 91 |
+
width: 100%;
|
| 92 |
+
height: 100%;
|
| 93 |
+
object-fit: contain;
|
| 94 |
+
opacity: 0;
|
| 95 |
+
transition: opacity 500ms ease;
|
| 96 |
+
pointer-events: none;
|
| 97 |
+
}
|
| 98 |
+
|
| 99 |
+
.video-layer.active {
|
| 100 |
+
opacity: 1;
|
| 101 |
+
}
|
| 102 |
+
|
| 103 |
+
.video-vignette {
|
| 104 |
+
position: absolute;
|
| 105 |
+
inset: 0;
|
| 106 |
+
background: radial-gradient(ellipse at center, transparent 40%, rgba(0, 0, 0, 0.6) 100%);
|
| 107 |
+
pointer-events: none;
|
| 108 |
+
}
|
| 109 |
+
</style>
|