diff --git a/README.md b/README.md index 7129add8..0e6a7af7 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ -# MXmap — Email Providers of Swiss Municipalities +# MXmap — municipal email infrastructure maps [](https://github.com/davidhuser/mxmap/actions/workflows/ci.yml) -An interactive map showing where Swiss municipalities host their email — whether with US hyperscalers (Microsoft, Google, AWS) or Swiss providers or other solutions. +Interactive maps showing where Swiss municipalities host their email and how deeply their DNS is tied to US hyperscalers (Microsoft, Google, AWS) versus Swiss providers and self-hosted solutions. -**[View the live map](https://mxmap.ch)** +**[View the live maps](https://mxmap.ch)** [](https://mxmap.ch) diff --git a/css/map.css b/css/map.css index 2acb41e9..2eb75721 100644 --- a/css/map.css +++ b/css/map.css @@ -11,20 +11,20 @@ body { display: flex; flex-direction: column; height: 100dvh; overflow: hidden; #info-bar { background: #f0f1f5; border-bottom: 1px solid #d8dae0; overflow: clip; flex-shrink: 0; position: relative; z-index: 1; - max-height: 600px; padding: 16px 20px; + min-height: 120px; max-height: 600px; padding: 13px 20px; box-shadow: 0 2px 6px rgba(0,0,0,0.1); } -#info-bar.collapsed { max-height: 0; padding-top: 0; padding-bottom: 0; } +#info-bar.collapsed { min-height: 0; max-height: 0; padding-top: 0; padding-bottom: 0; } .info-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); - gap: 14px; + gap: 12px; } .info-card h3 { font-size: 14px; font-weight: 600; margin-bottom: 4px; color: #1a1a2e; } .info-card p { font-size: 13px; color: #444; line-height: 1.5; margin: 0; } .info-card a { color: #2563eb; text-decoration: none; } .info-card a:hover { text-decoration: underline; } -#generated { font-size: 11px; color: #999; margin-top: 4px; } +#generated { font-size: 12px; color: #999; margin-top: 4px; } .info-title { display: none; font-size: 16px; font-weight: 600; color: #1a1a2e; margin-bottom: 12px; } .legend-toggle { @@ -42,8 +42,12 @@ body { display: flex; flex-direction: column; height: 100dvh; overflow: hidden; .legend-group { margin-top: 8px; } .legend-group:first-child { margin-top: 4px; } .legend-group-label { - font-size: 11px; color: #999; text-transform: uppercase; - letter-spacing: 0.3px; font-weight: 600; display: block; margin-bottom: 2px; + font-size: 13px; color: #333; + letter-spacing: 0.3px; font-weight: 600; display: block; margin-bottom: 4px; +} +.legend-other { + margin-top: 10px; padding-top: 8px; border-top: 1px solid #e5e7eb; + font-size: 12px; color: #555; } #map { flex: 1; position: relative; width: 100%; will-change: transform; } @@ -108,7 +112,7 @@ body { display: flex; flex-direction: column; height: 100dvh; overflow: hidden; .cat-badge.unknown { background: #f3f4f6; color: #6b7280; } .color-toggle { - display: block; margin-top: 8px; padding: 3px 8px; border-radius: 3px; + display: block; margin: 8px auto 0; padding: 3px 8px; border-radius: 3px; font-size: 11px; font-weight: 500; cursor: pointer; background: #f3f4f6; border: 1px solid #d1d5db; color: #374151; } diff --git a/css/shared.css b/css/shared.css index 043abc16..2c1e72c2 100644 --- a/css/shared.css +++ b/css/shared.css @@ -8,36 +8,24 @@ body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans- position: relative; z-index: 2000; } .header-left { display: flex; align-items: center; gap: 10px; min-width: 0; } -.brand { font-size: 17px; font-weight: 700; white-space: nowrap; margin: 0; } +.brand { + font-size: 14px; font-weight: 700; white-space: nowrap; margin: 0; + line-height: 1.05; +} .brand a { color: #fff; text-decoration: none; } -.beta-badge { - font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; - background: rgba(255,255,255,0.15); border: 1px solid rgba(255,255,255,0.3); - color: #f0c040; padding: 1px 6px; border-radius: 3px; vertical-align: middle; - cursor: default; +.brand-tag { + display: block; font-weight: 400; font-size: 11px; + opacity: 0.6; letter-spacing: 0.3px; margin-top: 1px; } .header-right { display: flex; align-items: center; gap: 12px; flex-shrink: 0; } .header-disclaimer { - flex: 1; text-align: center; font-size: 12px; color: #f6ed95; + flex: 1; text-align: center; font-size: 12px; color: #fff37f; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; padding: 0 12px; } #nav { display: flex; align-items: center; gap: 8px; position: relative; margin-left: 6px; } -.nav-primary { display: flex; align-items: center; gap: 6px; } -.nav-primary .header-link { - font-size: 13px; padding: 5px 14px; border-radius: 4px; font-weight: 500; - border: 1px solid rgba(255,255,255,0.3); color: #ccc; text-decoration: none; -} -.nav-primary .header-link:hover { - background: rgba(255,255,255,0.15); color: #fff; -} -.nav-primary .header-link.active { - background: rgba(255,255,255,0.2); color: #fff; font-weight: 600; - border-color: rgba(255,255,255,0.5); -} - .nav-menu-toggle { background: none; border: none; color: #a0a0b0; font-size: 18px; cursor: pointer; padding: 2px 4px; line-height: 1; @@ -56,12 +44,7 @@ body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans- .nav-menu .header-link:hover { color: #fff; } .nav-menu .header-link.active { color: #fff; font-weight: 600; } .nav-menu .header-link:last-child { border-bottom: none; } -.nav-menu .nav-menu-mobile { display: none; } @media (max-width: 600px) { - .beta-badge { display: none; } - .header-disclaimer { font-size: 10px; padding: 0 6px; } - .nav-primary .header-link { font-size: 11px; padding: 2px 8px; } - .nav-primary { display: none; } - .nav-menu .nav-menu-mobile { display: block; } + .header-disclaimer { display: none; } } diff --git a/datenschutz.html b/datenschutz.html index 700ed60e..5fc24804 100644 --- a/datenschutz.html +++ b/datenschutz.html @@ -4,7 +4,7 @@ -
Zur Darstellung der Karte werden externe Ressourcen von Drittanbietern geladen. Dabei wird Ihre IP-Adresse an die jeweiligen Server übermittelt:
unpkg.com — Leaflet-Kartenbibliothek und Schweizer Gemeindegrenzen (TopoJSON)basemaps.cartocdn.com — Kartenkacheln (CARTO)unpkg.com: Leaflet-Kartenbibliothek und Schweizer Gemeindegrenzen (TopoJSON)basemaps.cartocdn.com: Kartenkacheln (CARTO)A map of all ~2,100 Swiss municipalities showing which provider handles their official email — grouped by jurisdiction — based on public DNS records and other public network signals.
+A map of all ~2,100 Swiss municipalities, grouped by jurisdiction. Each is colored by the provider handling its official email, based on public DNS records and other public network signals.
Digital sovereignty: US-based providers are subject to the US CLOUD Act, which allows US authorities to request stored data, regardless of where it is physically hosted. This map makes the current provider landscape visible.
+US-based providers are subject to the US CLOUD Act, allowing US authorities to request stored data regardless of where it is hosted. This map makes the current provider landscape visible.
Each municipality's official domain is checked via 11 signals from DNS records, SMTP banners, ASN lookups, and a public Microsoft API endpoint, then classified by provider type with confidence scoring.
-Disclaimer: DNS records indicate mail routing and authorized senders, not necessarily where data is stored.
+Each municipality's email domain is checked against multiple public DNS and network signals, then classified by provider type.
+Classifications may contain errors. DNS records indicate mail routing, not necessarily where data is stored.
If you notice an error, please submit an issue.
The code and data are on GitHub.
- If you have noticed an error, please submit an issue.
The code and data are on GitHub.
A map of all ~2,100 Swiss municipalities showing whether their official domain is registered as a Microsoft 365 tenant — detected via a public Microsoft endpoint.
-A registered M365 tenant means the municipality's domain is in Microsoft Entra ID, implying (but not confirming) the use of M365 services such as Teams, SharePoint, or OneDrive — but not necessarily email. See the Email Map for email hosting.
-Each domain is queried against Microsoft's public getuserrealm.srf endpoint (unauthenticated). It returns: Managed (fully cloud-managed identity), Federated (hybrid, on-premises AD synced to cloud), or no result.
Caveat: Tenant presence confirms domain registration, not specific service usage. Some tenants could be inactive.
-The code and data are on GitHub.
- If you have noticed an error, please submit an issue.