-/* === zndr.dk — (dark-first) === */
-
/* ---------- Dark mode (defaults) ---------- */
:root{
--bg:#1a1b1e; /* page background */
border:none;
}
-/* Static dim for LAN-only / offline (stripe overlay FIXED) */
-.card.is-lan,.card.is-offline{opacity:.85;position:relative}
-.card.is-lan::after,.card.is-offline::after{
- content:''; /* REQUIRED */
- position:absolute; inset:0; /* REQUIRED */
- pointer-events:none;
- border-radius:10px;
- background:repeating-linear-gradient(
+/* Neutral stripe overlay for LAN-only / offline cards */
+.card.is-lan::after,
+.card.is-offline::after {
+ content: '';
+ position: absolute;
+ inset: 0;
+ pointer-events: none;
+ border-radius: 10px;
+ background: repeating-linear-gradient(
45deg,
- transparent 0 6px,
- rgba(16,163,127,.08) 6px 9px,
- rgba(239,68,68,.08) 9px 12px
+ transparent 0 12px,
+ rgba(255, 255, 255, 0.08) 12px 24px
);
}
.card{background:var(--panel);border:1px solid var(--ring);box-shadow:none}
.card:hover{box-shadow:0 4px 10px rgba(0,0,0,.10)}
- /* If you prefer a neutral chip in light mode, uncomment:
- .chip{background:#eef2f7;color:#0f172a;border:1px solid #dfe3ea}
- */
-
- .card.is-lan::after,.card.is-offline::after{
- background:repeating-linear-gradient(45deg,transparent 0 6px, rgba(0,0,0,.03) 6px 12px);
+ /* Light mode adjustment: stronger contrast */
+ .card.is-lan::after,
+ .card.is-offline::after {
+ background: repeating-linear-gradient(
+ 45deg,
+ transparent 0 12px,
+ rgba(0, 0, 0, 0.08) 12px 24px
+ );
}
}
{% endfor %}
</main>
+<script>
+(function () {
+ const TIMEOUT_MS = 3500;
+
+ // Find all cards that have a URL to test
+ const cards = Array.from(document.querySelectorAll('.card[data-url]'));
+
+ function markOnline(card) {
+ // If it was previously flagged offline (e.g. navigation back), clear it
+ card.classList.remove('is-offline');
+ card.removeAttribute('title');
+ }
+
+ function markOffline(card, reason) {
+ card.classList.add('is-offline');
+ card.title = reason || 'This service did not respond from your current network.';
+ }
+
+ cards.forEach(card => {
+ const base = card.getAttribute('data-url') || '';
+ const custom = card.getAttribute('data-probe'); // optional custom path like "/api/health"
+ let probeUrl;
+
+ try {
+ const u = new URL(base);
+
+ // Don’t try to fetch http:// from an https:// page (mixed-content would block)
+ if (location.protocol === 'https:' && u.protocol !== 'https:') {
+ markOffline(card, 'Blocked mixed-content (HTTP) from HTTPS page.');
+ return;
+ }
+
+ // Build probe URL: custom path (same origin) or /favicon.ico
+ probeUrl = (custom ? (u.origin + custom) : (u.origin + '/favicon.ico')) + '?t=' + Date.now();
+
+ const img = new Image();
+ const timer = setTimeout(() => {
+ img.src = ''; // cancel
+ markOffline(card, 'Timed out.');
+ }, TIMEOUT_MS);
+
+ img.onload = () => { clearTimeout(timer); markOnline(card); };
+ img.onerror = () => { clearTimeout(timer); markOffline(card, 'Probe failed.'); };
+
+ img.referrerPolicy = 'no-referrer';
+ img.decoding = 'async';
+ img.src = probeUrl;
+
+ } catch (e) {
+ markOffline(card, 'Invalid URL.');
+ }
+ });
+})();
+</script>
+
"name": "zndr.dk",
"short_name": "zndr",
"icons": [
- {
- "src": "/icon-192.png",
- "sizes": "192x192",
- "type": "image/png"
- },
- {
- "src": "/icon-512.png",
- "sizes": "512x512",
- "type": "image/png"
- }
+ { "src": "/icon-192.png", "sizes": "192x192", "type": "image/png", "purpose": "maskable any" },
+ { "src": "/icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable any" }
],
"start_url": "/",
"display": "standalone",