feat: Passwordless cross-device authentication

- Arkitektur: docs/auth/passwordless-architecture.md
- Backend: iom/quixzoom-auth-service/ (FastAPI + Redis)
- Webb: quixzoom-market-pages/se/login/ (QR-kod + polling)
- App: iom/quixzoom-app/src/features/auth/ (push + deep links)

Flöde: QR-kod → app-godkännande → webb-inloggad
This commit is contained in:
Bernt
2026-07-07 07:11:50 +00:00
parent 4aa984ad74
commit 6989a98d75
61843 changed files with 5491611 additions and 872231 deletions
+11 -11
View File
@@ -42,7 +42,7 @@
a { color: inherit; text-decoration: none; }
/* ── NAV ── */
/* NAV */
nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 1000;
display: flex; align-items: center; justify-content: space-between;
@@ -145,14 +145,14 @@
}
.btn-outline:hover { background: rgba(0,0,0,0.04); transform: translateY(-1px); }
/* ── PAGE CONTENT ── */
/* PAGE CONTENT */
.page-wrap {
max-width: 800px;
margin: 0 auto;
padding: 120px 40px 80px;
}
/* ── HERO ── */
/* HERO */
.about-hero {
padding-bottom: 72px;
border-bottom: 1px solid var(--border);
@@ -184,7 +184,7 @@
margin: 0;
}
/* ── SECTIONS ── */
/* SECTIONS */
.section {
padding: 64px 0;
border-bottom: 1px solid var(--border);
@@ -203,7 +203,7 @@
margin-bottom: 32px;
}
/* ── TEAM ── */
/* TEAM */
.team-grid {
display: grid;
grid-template-columns: 1fr 1fr;
@@ -239,7 +239,7 @@
margin: 0;
}
/* ── GROUP STRUCTURE ── */
/* GROUP STRUCTURE */
.group-entities {
display: flex;
flex-direction: column;
@@ -281,7 +281,7 @@
flex-shrink: 0;
}
/* ── DISCLAIMER ── */
/* DISCLAIMER */
.disclaimer-box {
background: var(--surface);
border: 1px solid var(--border);
@@ -297,7 +297,7 @@
margin: 0;
}
/* ── CTA ── */
/* CTA */
.cta-row {
display: flex;
gap: 16px;
@@ -326,7 +326,7 @@
}
.cta-link-secondary:hover { color: #1d1d1f; }
/* ── FOOTER ── */
/* FOOTER */
footer {
border-top: 1px solid var(--border);
padding: 40px 40px;
@@ -349,13 +349,13 @@
.footer-social {
width: 32px; height: 32px;
border: 1px solid var(--border);
border-radius: 8px;
border-radius: var(--radius-md);
display: flex; align-items: center; justify-content: center;
transition: border-color 0.2s, background 0.2s;
}
.footer-social:hover { border-color: rgba(0,0,0,0.25); background: rgba(0,0,0,0.04); }
/* ── RESPONSIVE ── */
/* RESPONSIVE */
@media (max-width: 900px) {
nav { padding: 0 16px; }
.nav-links { display: none; }
+4 -4
View File
@@ -97,11 +97,11 @@
.status-badge {
display: inline-flex; align-items: center; gap: 8px;
background: rgba(0,200,83,0.1); border: 1px solid rgba(0,200,83,0.25);
border-radius: 8px; padding: 8px 14px;
border-radius: var(--radius-md); padding: 8px 14px;
font-size: 13px; font-weight: 700; color: #00b84a;
margin-bottom: 24px;
}
.status-dot { width: 8px; height: 8px; border-radius: 50%; background: #00C853; flex-shrink: 0; }
.status-dot { width: 8px; height: 8px; border-radius: var(--radius-full); background: #00C853; flex-shrink: 0; }
/* LIMITATION CARDS */
.limitation-card {
@@ -121,7 +121,7 @@
border-radius: var(--radius-lg); padding: 36px 40px;
display: flex; gap: 28px; align-items: flex-start;
}
.contact-icon { flex-shrink: 0; width: 48px; height: 48px; border-radius: 50%; background: var(--blue-glow); border: 1px solid rgba(0,102,255,0.2); display: flex; align-items: center; justify-content: center; }
.contact-icon { flex-shrink: 0; width: 48px; height: 48px; border-radius: var(--radius-full); background: var(--blue-glow); border: 1px solid rgba(0,102,255,0.2); display: flex; align-items: center; justify-content: center; }
.contact-icon svg { color: var(--blue); }
.contact-card h3 { font-size: 18px; font-weight: 700; color: #1d1d1f; margin-bottom: 8px; letter-spacing: -0.2px; }
.contact-card p { font-size: 15px; color: var(--text-dim); line-height: 1.65; }
@@ -136,7 +136,7 @@
.footer-links { display: flex; gap: 24px; align-items: center; flex-wrap: wrap; }
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social { width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px; display: flex; align-items: center; justify-content: center; transition: border-color 0.2s, background 0.2s; }
.footer-social { width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; transition: border-color 0.2s, background 0.2s; }
.footer-social:hover { border-color: rgba(0,0,0,0.25); background: rgba(0,0,0,0.04); }
@media (max-width: 900px) { nav { padding: 0 16px; } .nav-links { display: none; } }
+213 -213
View File
@@ -32,7 +32,7 @@
.nav-logo { font-weight: 700; font-size: 18px; color: #6366f1; letter-spacing: -0.5px; }
.nav-links { display: flex; gap: 4px; }
.nav-link {
padding: 6px 14px; border-radius: 8px; color: #94a3b8;
padding: 6px 14px; border-radius: var(--radius-md); color: #94a3b8;
font-size: 13px; font-weight: 500; cursor: pointer;
transition: all 0.15s; background: none; border: none;
}
@@ -40,7 +40,7 @@
.nav-link.active { background: #1e2737; color: #6366f1; }
.nav-spacer { flex: 1; }
.nav-status { display: flex; align-items: center; gap: 8px; font-size: 12px; color: #64748b; }
.status-dot { width: 8px; height: 8px; border-radius: 50%; background: #22c55e; }
.status-dot { width: 8px; height: 8px; border-radius: var(--radius-full); background: #22c55e; }
.main-container { max-width: 1400px; margin: 0 auto; padding: 24px 24px 48px; }
.page-header { margin-bottom: 24px; }
@@ -49,7 +49,7 @@
.company-tabs { display: flex; gap: 4px; margin-bottom: 24px; flex-wrap: wrap; }
.company-tab {
padding: 8px 16px; border-radius: 10px; border: 1px solid #1e2737;
padding: 8px 16px; border-radius: var(--radius-md); border: 1px solid #1e2737;
background: #0d1117; color: #94a3b8; font-size: 13px; font-weight: 500;
cursor: pointer; transition: all 0.15s;
}
@@ -70,7 +70,7 @@
.tab-panel { display: none; }
.tab-panel.visible { display: block; }
.card { background: #0d1117; border: 1px solid #1e2737; border-radius: 12px; padding: 24px; }
.card { background: #0d1117; border: 1px solid #1e2737; border-radius: var(--radius-md); padding: 24px; }
.card + .card { margin-top: 16px; }
.card-title { font-size: 15px; font-weight: 600; color: #f1f5f9; margin-bottom: 16px; }
@@ -79,14 +79,14 @@
.form-group label { font-size: 12px; font-weight: 500; color: #94a3b8; text-transform: uppercase; letter-spacing: 0.5px; }
.form-group input, .form-group select, .form-group textarea {
padding: 9px 12px; background: #030712; border: 1px solid #1e2737;
border-radius: 8px; color: #e2e8f0; font-size: 13px; width: 100%;
border-radius: var(--radius-md); color: #e2e8f0; font-size: 13px; width: 100%;
transition: border-color 0.15s; outline: none;
}
.form-group input:focus, .form-group select:focus, .form-group textarea:focus { border-color: #6366f1; }
.form-group input::placeholder { color: #374151; }
.btn {
padding: 9px 18px; border-radius: 8px; border: none; font-size: 13px;
padding: 9px 18px; border-radius: var(--radius-md); border: none; font-size: 13px;
font-weight: 600; cursor: pointer; transition: all 0.15s;
display: inline-flex; align-items: center; gap: 6px;
}
@@ -100,14 +100,14 @@
.btn-danger:hover { background: #b91c1c; }
.btn-warning { background: #d97706; color: #fff; }
.btn-warning:hover { background: #b45309; }
.btn-ghost { background: none; border: none; color: #64748b; cursor: pointer; padding: 4px 8px; border-radius: 6px; font-size: 13px; transition: all 0.15s; }
.btn-ghost { background: none; border: none; color: #64748b; cursor: pointer; padding: 4px 8px; border-radius: var(--radius-sm); font-size: 13px; transition: all 0.15s; }
.btn-ghost:hover { background: #1e2737; color: #e2e8f0; }
.btn-sm { padding: 5px 12px; font-size: 12px; }
.btn-teal { background: #0d9488; color: #fff; }
.btn-teal:hover { background: #0f766e; }
.filter-row { display: flex; gap: 8px; align-items: center; margin-bottom: 16px; flex-wrap: wrap; }
.filter-btn { padding: 6px 14px; border-radius: 20px; border: 1px solid #1e2737; background: none; color: #64748b; font-size: 12px; cursor: pointer; transition: all 0.15s; }
.filter-btn { padding: 6px 14px; border-radius: var(--radius-xl); border: 1px solid #1e2737; background: none; color: #64748b; font-size: 12px; cursor: pointer; transition: all 0.15s; }
.filter-btn:hover { border-color: #6366f1; color: #818cf8; }
.filter-btn.active { background: #1e1f4e; border-color: #6366f1; color: #818cf8; }
.refresh-info { margin-left: auto; font-size: 12px; color: #475569; }
@@ -119,7 +119,7 @@
justify-content: space-between;
background: #0f2818;
border: 1px solid #166534;
border-radius: 10px;
border-radius: var(--radius-md);
padding: 12px 18px;
margin-bottom: 16px;
gap: 12px;
@@ -149,7 +149,7 @@
/* Larger, clearer badges */
.badge {
display: inline-flex; align-items: center; gap: 4px;
padding: 4px 12px; border-radius: 20px; font-size: 12px; font-weight: 700;
padding: 4px 12px; border-radius: var(--radius-xl); font-size: 12px; font-weight: 700;
white-space: nowrap; letter-spacing: 0.2px;
}
.badge-running { background: #1e3a5f; color: #60a5fa; border: 1px solid #1d4ed8; }
@@ -166,7 +166,7 @@
.badge-timeout { background: #1c1917; color: #78716c; border: 1px solid #44403c; }
.screenshot-thumb {
max-width: 80px; max-height: 60px; border-radius: 6px; object-fit: cover;
max-width: 80px; max-height: 60px; border-radius: var(--radius-sm); object-fit: cover;
border: 1px solid #1e2737; cursor: pointer;
transition: transform 0.15s, border-color 0.15s;
}
@@ -175,26 +175,26 @@
/* Inline credentials for PENDING_MANUAL / MANUAL_REQUIRED */
.inline-creds {
background: #0a1628; border: 1px solid #1e3a5f; border-radius: 8px;
background: #0a1628; border: 1px solid #1e3a5f; border-radius: var(--radius-md);
padding: 10px 12px; margin-top: 8px; display: flex; flex-direction: column; gap: 6px;
}
.inline-creds-title { font-size: 11px; font-weight: 700; color: #60a5fa; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 2px; }
.cred-inline-row { display: flex; align-items: center; gap: 6px; font-size: 12px; flex-wrap: wrap; }
.cred-inline-label { color: #475569; min-width: 70px; }
.cred-inline-val { font-family: 'Monaco','Menlo',monospace; font-size: 12px; color: #cbd5e1; }
.pw-field { background: #030712; border: 1px solid #1e2737; border-radius: 4px; padding: 3px 8px; color: #fbbf24; font-family: monospace; font-size: 12px; width: 160px; }
.copy-btn { background: #6366f1; color: #fff; border: none; border-radius: 4px; padding: 3px 10px; cursor: pointer; font-size: 11px; font-weight: 600; transition: background 0.15s; }
.pw-field { background: #030712; border: 1px solid #1e2737; border-radius: var(--radius-sm); padding: 3px 8px; color: #fbbf24; font-family: monospace; font-size: 12px; width: 160px; }
.copy-btn { background: #6366f1; color: #fff; border: none; border-radius: var(--radius-sm); padding: 3px 10px; cursor: pointer; font-size: 11px; font-weight: 600; transition: background 0.15s; }
.copy-btn:hover { background: #4f46e5; }
.reveal-btn { background: #1e2737; color: #94a3b8; border: 1px solid #2d3748; border-radius: 4px; padding: 3px 8px; cursor: pointer; font-size: 11px; transition: all 0.15s; }
.reveal-btn { background: #1e2737; color: #94a3b8; border: 1px solid #2d3748; border-radius: var(--radius-sm); padding: 3px 8px; cursor: pointer; font-size: 11px; transition: all 0.15s; }
.reveal-btn:hover { background: #2d3748; color: #e2e8f0; }
/* Fetch-mail-code */
.mail-code-box {
background: #0a1628; border: 1px solid #1e3a5f; border-radius: 8px;
background: #0a1628; border: 1px solid #1e3a5f; border-radius: var(--radius-md);
padding: 10px 12px; margin-top: 8px;
}
.mail-code-result {
display: none; background: #1e293b; border-radius: 6px; padding: 8px 12px;
display: none; background: #1e293b; border-radius: var(--radius-sm); padding: 8px 12px;
margin-top: 8px; font-size: 14px; font-weight: 700; color: #6ee7b7; letter-spacing: 2px;
display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
}
@@ -204,7 +204,7 @@
.creds-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px; flex-wrap: wrap; gap: 8px; }
.creds-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.creds-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); gap: 16px; }
.cred-card { background: #030712; border: 1px solid #1e2737; border-radius: 10px; padding: 16px; }
.cred-card { background: #030712; border: 1px solid #1e2737; border-radius: var(--radius-md); padding: 16px; }
.cred-platform { font-size: 14px; font-weight: 700; color: #f1f5f9; margin-bottom: 12px; display: flex; align-items: center; gap: 8px; }
.cred-rows { display: flex; flex-direction: column; gap: 8px; }
.cred-row { display: flex; justify-content: space-between; align-items: center; gap: 8px; font-size: 12px; }
@@ -212,7 +212,7 @@
.cred-value { color: #cbd5e1; font-family: 'Monaco', 'Menlo', monospace; font-size: 12px; word-break: break-all; flex: 1; text-align: right; }
.pw-hidden { color: #e2e8f0; }
.security-banner {
background: #1a1f2e; border: 1px solid #2d3748; border-radius: 8px;
background: #1a1f2e; border: 1px solid #2d3748; border-radius: var(--radius-md);
padding: 12px 16px; margin-bottom: 16px; display: flex; align-items: center; gap: 10px;
font-size: 12px; color: #94a3b8;
}
@@ -225,7 +225,7 @@
.platform-checkbox { display: flex; align-items: center; gap: 6px; }
.platform-checkbox input { width: auto; }
.platform-checkbox label { font-size: 13px; color: #cbd5e1; text-transform: none; font-weight: 400; letter-spacing: 0; }
.post-preview { background: #030712; border: 1px solid #1e2737; border-radius: 10px; padding: 16px; }
.post-preview { background: #030712; border: 1px solid #1e2737; border-radius: var(--radius-md); padding: 16px; }
.preview-platform { font-size: 12px; font-weight: 600; color: #6366f1; text-transform: uppercase; letter-spacing: 1px; margin-bottom: 8px; }
.preview-content { font-size: 13px; color: #cbd5e1; white-space: pre-wrap; word-break: break-word; }
.post-queue-table { width: 100%; border-collapse: collapse; margin-top: 16px; }
@@ -237,7 +237,7 @@
.char-counter.over { color: #ef4444; }
.schedule-row { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; margin-top: 8px; }
.schedule-toggle { display: flex; gap: 8px; }
.sched-btn { padding: 6px 14px; border-radius: 8px; border: 1px solid #1e2737; background: none; color: #64748b; font-size: 12px; cursor: pointer; transition: all 0.15s; }
.sched-btn { padding: 6px 14px; border-radius: var(--radius-md); border: 1px solid #1e2737; background: none; color: #64748b; font-size: 12px; cursor: pointer; transition: all 0.15s; }
.sched-btn.active { background: #1e1f4e; border-color: #6366f1; color: #818cf8; }
/* Action required column */
@@ -248,11 +248,11 @@
.state-box { text-align: center; padding: 48px 24px; color: #475569; font-size: 14px; }
.state-box .icon { font-size: 36px; margin-bottom: 12px; }
.state-box p { margin-top: 4px; font-size: 13px; }
.spinner { display: inline-block; width: 24px; height: 24px; border: 3px solid #1e2737; border-top-color: #6366f1; border-radius: 50%; animation: spin 0.8s linear infinite; margin-bottom: 12px; }
.spinner { display: inline-block; width: 24px; height: 24px; border: 3px solid #1e2737; border-top-color: #6366f1; border-radius: var(--radius-full); animation: spin 0.8s linear infinite; margin-bottom: 12px; }
@keyframes spin { to { transform: rotate(360deg); } }
.toast-container { position: fixed; bottom: 24px; right: 24px; display: flex; flex-direction: column; gap: 8px; z-index: 999; }
.toast { padding: 10px 16px; border-radius: 10px; font-size: 13px; font-weight: 500; max-width: 320px; animation: slideIn 0.2s ease; }
.toast { padding: 10px 16px; border-radius: var(--radius-md); font-size: 13px; font-weight: 500; max-width: 320px; animation: slideIn 0.2s ease; }
@keyframes slideIn { from { transform: translateX(100%); opacity: 0; } to { transform: none; opacity: 1; } }
.toast-success { background: #14532d; color: #4ade80; border: 1px solid #16a34a; }
.toast-error { background: #450a0a; color: #f87171; border: 1px solid #dc2626; }
@@ -262,12 +262,12 @@
/* Modal */
.modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.7); z-index: 500; display: none; align-items: center; justify-content: center; }
.modal-overlay.visible { display: flex; }
.modal { background: #0d1117; border: 1px solid #1e2737; border-radius: 16px; padding: 32px; max-width: 480px; width: 90%; }
.modal { background: #0d1117; border: 1px solid #1e2737; border-radius: var(--radius-lg); padding: 32px; max-width: 480px; width: 90%; }
.modal h3 { font-size: 18px; font-weight: 700; color: #f1f5f9; margin-bottom: 12px; }
.modal p { color: #94a3b8; font-size: 14px; margin-bottom: 24px; }
.modal-actions { display: flex; gap: 12px; justify-content: flex-end; }
.rotation-log { background: #030712; border: 1px solid #1e2737; border-radius: 8px; padding: 16px; margin-top: 16px; max-height: 300px; overflow-y: auto; }
.rotation-log { background: #030712; border: 1px solid #1e2737; border-radius: var(--radius-md); padding: 16px; margin-top: 16px; max-height: 300px; overflow-y: auto; }
.rotation-log-item { font-size: 12px; color: #64748b; padding: 6px 0; border-bottom: 1px solid #0f172a; display: flex; justify-content: space-between; gap: 8px; }
.rotation-log-item:last-child { border-bottom: none; }
</style>
@@ -275,7 +275,7 @@
<body>
<nav class="top-nav">
<div class="nav-logo">🦅 ARGUS Social</div>
<div class="nav-logo"> ARGUS Social</div>
<div class="nav-links">
<button class="nav-link active">Admin</button>
</div>
@@ -291,11 +291,11 @@
<!-- Rotate Confirm Modal -->
<div class="modal-overlay" id="rotateModal">
<div class="modal">
<h3>🔄 Rotera lösenord</h3>
<h3><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 8C2 11.3 4.7 14 8 14C10.5 14 12.7 12.5 13.7 10.3" stroke="#666" stroke-width="1.5" stroke-linecap="round"/><path d="M14 8C14 4.7 11.3 2 8 2C5.5 2 3.3 3.5 2.3 5.7" stroke="#666" stroke-width="1.5" stroke-linecap="round"/><path d="M13.5 7.5L14 10L11.5 9.5" stroke="#666" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M2.5 8.5L2 6L4.5 6.5" stroke="#666" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg> Rotera lösenord</h3>
<p id="rotateModalText">Generera nya starka lösenord för alla plattformar? Detta kan inte ångras.</p>
<div class="modal-actions">
<button class="btn btn-secondary" onclick="closeRotateModal()">Avbryt</button>
<button class="btn btn-warning" id="rotateConfirmBtn" onclick="confirmRotate()">🔄 Rotera nu</button>
<button class="btn btn-warning" id="rotateConfirmBtn" onclick="confirmRotate()"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 8C2 11.3 4.7 14 8 14C10.5 14 12.7 12.5 13.7 10.3" stroke="#666" stroke-width="1.5" stroke-linecap="round"/><path d="M14 8C14 4.7 11.3 2 8 2C5.5 2 3.3 3.5 2.3 5.7" stroke="#666" stroke-width="1.5" stroke-linecap="round"/><path d="M13.5 7.5L14 10L11.5 9.5" stroke="#666" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M2.5 8.5L2 6L4.5 6.5" stroke="#666" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg> Rotera nu</button>
</div>
</div>
</div>
@@ -303,11 +303,11 @@
<!-- Employee Offboard Modal -->
<div class="modal-overlay" id="offboardModal">
<div class="modal">
<h3>⚠️ Anställd slutar — Lösenordsrotation</h3>
<h3><svg width="20" height="20" viewBox="0 0 20 20" fill="none"><path d="M10 3L17 17H3L10 3Z" stroke="#f59e0b" stroke-width="2"/><path d="M10 8V12M10 14V14.5" stroke="#f59e0b" stroke-width="2" stroke-linecap="round"/></svg> Anställd slutar — Lösenordsrotation</h3>
<p>Detta roterar ALLA lösenord för ALLA bolag och ALLA plattformar. Använd vid anställds avslut.</p>
<div class="modal-actions">
<button class="btn btn-secondary" onclick="document.getElementById('offboardModal').classList.remove('visible')">Avbryt</button>
<button class="btn btn-danger" onclick="confirmOffboard()">⚠️ Rotera ALLA nu</button>
<button class="btn btn-danger" onclick="confirmOffboard()"><svg width="20" height="20" viewBox="0 0 20 20" fill="none"><path d="M10 3L17 17H3L10 3Z" stroke="#f59e0b" stroke-width="2"/><path d="M10 8V12M10 14V14.5" stroke="#f59e0b" stroke-width="2" stroke-linecap="round"/></svg> Rotera ALLA nu</button>
</div>
</div>
</div>
@@ -323,23 +323,23 @@
<!-- Company Tabs -->
<div class="company-tabs">
<button class="company-tab active" data-company="landvex" onclick="selectCompany('landvex')">🏢 LandveX</button>
<button class="company-tab" data-company="quixzoom" onclick="selectCompany('quixzoom')"> QuiXzoom</button>
<button class="company-tab" data-company="apifly" onclick="selectCompany('apifly')">🚀 ApiFly</button>
<button class="company-tab" data-company="corpfitt" onclick="selectCompany('corpfitt')">💪 CorpFitt</button>
<button class="company-tab" data-company="vyra" onclick="selectCompany('vyra')"> VYRA</button>
<button class="company-tab" data-company="aamos" onclick="selectCompany('aamos')">🧠 AAMOS</button>
<button class="company-tab active" data-company="landvex" onclick="selectCompany('landvex')"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="3" y="2" width="10" height="12" rx="1" stroke="#666" stroke-width="1.5"/><path d="M6 5H7M6 8H7M6 11H7M9 5H10M9 8H10M9 11H10" stroke="#666" stroke-width="1.5" stroke-linecap="round"/></svg> LandveX</button>
<button class="company-tab" data-company="quixzoom" onclick="selectCompany('quixzoom')"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M9 2L5 9H8L7 14L11 7H8L9 2Z" fill="#f59e0b"/></svg> QuiXzoom</button>
<button class="company-tab" data-company="apifly" onclick="selectCompany('apifly')"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M8 2L10 8L14 9L8 14L6 8L2 7L8 2Z" fill="#666"/></svg> ApiFly</button>
<button class="company-tab" data-company="corpfitt" onclick="selectCompany('corpfitt')"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M5 7C5 7 3 8 3 10C3 12 5 13 7 13H11C13 13 14 11 14 9C14 7 12 6 11 6H9V4C9 3 8 2 7 2C6 2 5 3 5 4V7Z" stroke="#666" stroke-width="1.5" stroke-linejoin="round"/></svg> CorpFitt</button>
<button class="company-tab" data-company="vyra" onclick="selectCompany('vyra')"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M8 2V4M8 12V14M2 8H4M12 8H14" stroke="#f59e0b" stroke-width="1.5" stroke-linecap="round"/><circle cx="8" cy="8" r="2" fill="#f59e0b"/></svg> VYRA</button>
<button class="company-tab" data-company="aamos" onclick="selectCompany('aamos')"> AAMOS</button>
</div>
<!-- Main Tabs -->
<div class="main-tabs">
<button class="main-tab active" data-tab="register" onclick="switchTab('register')">📝 Registrera</button>
<button class="main-tab" data-tab="jobs" onclick="switchTab('jobs')">⚙️ Jobb</button>
<button class="main-tab" data-tab="credentials" onclick="switchTab('credentials')">🔐 Credentials</button>
<button class="main-tab" data-tab="publishing" onclick="switchTab('publishing')">📢 Publishing</button>
<button class="main-tab active" data-tab="register" onclick="switchTab('register')"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 13V10L10 3L13 6L6 13H3Z" stroke="#666" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M9 4L12 7" stroke="#666" stroke-width="1.5"/></svg> Registrera</button>
<button class="main-tab" data-tab="jobs" onclick="switchTab('jobs')"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><circle cx="8" cy="8" r="3" stroke="#666" stroke-width="1.5"/><path d="M8 2V4M8 12V14M2 8H4M12 8H14M4.3 4.3L5.7 5.7M10.3 10.3L11.7 11.7M4.3 11.7L5.7 10.3M10.3 5.7L11.7 4.3" stroke="#666" stroke-width="1.5" stroke-linecap="round"/></svg> Jobb</button>
<button class="main-tab" data-tab="credentials" onclick="switchTab('credentials')"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="3" y="7" width="10" height="7" rx="1" stroke="#666" stroke-width="1.5"/><path d="M5 7V5C5 3.3 6.3 2 8 2C9.7 2 11 3.3 11 5V7" stroke="#666" stroke-width="1.5"/><circle cx="8" cy="10.5" r="1" fill="#666"/></svg> Credentials</button>
<button class="main-tab" data-tab="publishing" onclick="switchTab('publishing')"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 6V10H5L10 14V2L5 6H2Z" stroke="#666" stroke-width="1.5" stroke-linejoin="round"/><path d="M12 5C13 6 13 10 12 11" stroke="#666" stroke-width="1.5" stroke-linecap="round"/></svg> Publishing</button>
</div>
<!-- ─── PANEL: Register ──────────────────────────────────── -->
<!-- PANEL: Register -->
<div id="panel-register" class="tab-panel visible">
<div class="card">
<div class="card-title">Registrera nytt konto</div>
@@ -347,12 +347,12 @@
<div class="form-group">
<label>Bolag</label>
<select id="regCompany" onchange="onRegCompanyChange()">
<option value="landvex">🏢 LandveX</option>
<option value="quixzoom"> QuiXzoom</option>
<option value="apifly">🚀 ApiFly</option>
<option value="corpfitt">💪 CorpFitt</option>
<option value="vyra"> VYRA</option>
<option value="aamos">🧠 AAMOS</option>
<option value="landvex"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="3" y="2" width="10" height="12" rx="1" stroke="#666" stroke-width="1.5"/><path d="M6 5H7M6 8H7M6 11H7M9 5H10M9 8H10M9 11H10" stroke="#666" stroke-width="1.5" stroke-linecap="round"/></svg> LandveX</option>
<option value="quixzoom"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M9 2L5 9H8L7 14L11 7H8L9 2Z" fill="#f59e0b"/></svg> QuiXzoom</option>
<option value="apifly"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M8 2L10 8L14 9L8 14L6 8L2 7L8 2Z" fill="#666"/></svg> ApiFly</option>
<option value="corpfitt"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M5 7C5 7 3 8 3 10C3 12 5 13 7 13H11C13 13 14 11 14 9C14 7 12 6 11 6H9V4C9 3 8 2 7 2C6 2 5 3 5 4V7Z" stroke="#666" stroke-width="1.5" stroke-linejoin="round"/></svg> CorpFitt</option>
<option value="vyra"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M8 2V4M8 12V14M2 8H4M12 8H14" stroke="#f59e0b" stroke-width="1.5" stroke-linecap="round"/><circle cx="8" cy="8" r="2" fill="#f59e0b"/></svg> VYRA</option>
<option value="aamos"> AAMOS</option>
</select>
</div>
<div class="form-group">
@@ -386,24 +386,24 @@
<input type="text" id="regUsername" placeholder="@bolag">
</div>
</div>
<div style="margin-top:12px; padding:10px 14px; background:#0a1628; border:1px solid #1e3a5f; border-radius:8px; font-size:12px; color:#60a5fa; display:flex; gap:8px; align-items:center;">
<span>🔒</span>
<div style="margin-top:12px; padding:10px 14px; background:#0a1628; border:1px solid #1e3a5f; border-radius: var(--radius-md); font-size:12px; color:#60a5fa; display:flex; gap:8px; align-items:center;">
<span></span>
<span>Lösenord genereras automatiskt av servern (32+ tecken). Aldrig synligt i UI.</span>
</div>
<div style="margin-top:20px; display:flex; gap:12px; flex-wrap:wrap;">
<button class="btn btn-primary" onclick="startRegistration()">🚀 Starta automation</button>
<button class="btn btn-secondary" onclick="clearRegForm()">🗑 Rensa</button>
<button class="btn btn-primary" onclick="startRegistration()"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M8 2L10 8L14 9L8 14L6 8L2 7L8 2Z" fill="#666"/></svg> Starta automation</button>
<button class="btn btn-secondary" onclick="clearRegForm()"> Rensa</button>
</div>
</div>
</div>
<!-- ─── PANEL: Jobs ──────────────────────────────────────── -->
<!-- PANEL: Jobs -->
<div id="panel-jobs" class="tab-panel">
<div class="card">
<!-- Approve-all banner (only shown when awaiting jobs exist) -->
<div class="approve-all-banner" id="approveAllBanner">
<span class="approve-all-banner-text" id="approveAllText">⏳ Jobb väntar på godkännande</span>
<button class="btn btn-success btn-sm" onclick="approveAll()"> Godkänn alla</button>
<button class="btn btn-success btn-sm" onclick="approveAll()"><svg width="20" height="20" viewBox="0 0 20 20" fill="none"><circle cx="10" cy="10" r="9" stroke="#22c55e" stroke-width="2"/><path d="M6 10L9 13L14 7" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Godkänn alla</button>
</div>
<div class="filter-row">
@@ -412,8 +412,8 @@
<button class="filter-btn" data-filter="done" onclick="setJobFilter('done')">Färdiga</button>
<button class="filter-btn" data-filter="error" onclick="setJobFilter('error')">Fel</button>
<button class="filter-btn" data-filter="awaiting" onclick="setJobFilter('awaiting')">⏳ Väntar</button>
<button class="filter-btn" data-filter="manual" onclick="setJobFilter('manual')">🖐 Manuell</button>
<button class="btn btn-secondary btn-sm" onclick="loadJobs()" style="margin-left:4px;">🔄 Ladda om</button>
<button class="filter-btn" data-filter="manual" onclick="setJobFilter('manual')"> Manuell</button>
<button class="btn btn-secondary btn-sm" onclick="loadJobs()" style="margin-left:4px;"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 8C2 11.3 4.7 14 8 14C10.5 14 12.7 12.5 13.7 10.3" stroke="#666" stroke-width="1.5" stroke-linecap="round"/><path d="M14 8C14 4.7 11.3 2 8 2C5.5 2 3.3 3.5 2.3 5.7" stroke="#666" stroke-width="1.5" stroke-linecap="round"/><path d="M13.5 7.5L14 10L11.5 9.5" stroke="#666" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M2.5 8.5L2 6L4.5 6.5" stroke="#666" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg> Ladda om</button>
<span class="refresh-info" id="refreshInfo"></span>
</div>
<div id="jobsContent">
@@ -422,19 +422,19 @@
</div>
</div>
<!-- ─── PANEL: Credentials ──────────────────────────────── -->
<!-- PANEL: Credentials -->
<div id="panel-credentials" class="tab-panel">
<div class="card">
<div class="creds-header">
<div class="card-title" style="margin-bottom:0">Credentials — <span id="credsCompanyLabel">LandveX</span></div>
<div class="creds-actions">
<button class="btn btn-warning btn-sm" onclick="openRotateModal()">🔄 Rotera lösenord</button>
<button class="btn btn-danger btn-sm" onclick="document.getElementById('offboardModal').classList.add('visible')">⚠️ Anställd slutar</button>
<button class="btn btn-secondary btn-sm" onclick="loadCredentials()">🔄 Ladda om</button>
<button class="btn btn-warning btn-sm" onclick="openRotateModal()"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 8C2 11.3 4.7 14 8 14C10.5 14 12.7 12.5 13.7 10.3" stroke="#666" stroke-width="1.5" stroke-linecap="round"/><path d="M14 8C14 4.7 11.3 2 8 2C5.5 2 3.3 3.5 2.3 5.7" stroke="#666" stroke-width="1.5" stroke-linecap="round"/><path d="M13.5 7.5L14 10L11.5 9.5" stroke="#666" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M2.5 8.5L2 6L4.5 6.5" stroke="#666" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg> Rotera lösenord</button>
<button class="btn btn-danger btn-sm" onclick="document.getElementById('offboardModal').classList.add('visible')"><svg width="20" height="20" viewBox="0 0 20 20" fill="none"><path d="M10 3L17 17H3L10 3Z" stroke="#f59e0b" stroke-width="2"/><path d="M10 8V12M10 14V14.5" stroke="#f59e0b" stroke-width="2" stroke-linecap="round"/></svg> Anställd slutar</button>
<button class="btn btn-secondary btn-sm" onclick="loadCredentials()"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 8C2 11.3 4.7 14 8 14C10.5 14 12.7 12.5 13.7 10.3" stroke="#666" stroke-width="1.5" stroke-linecap="round"/><path d="M14 8C14 4.7 11.3 2 8 2C5.5 2 3.3 3.5 2.3 5.7" stroke="#666" stroke-width="1.5" stroke-linecap="round"/><path d="M13.5 7.5L14 10L11.5 9.5" stroke="#666" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M2.5 8.5L2 6L4.5 6.5" stroke="#666" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg> Ladda om</button>
</div>
</div>
<div class="security-banner">
<span class="icon">🛡️</span>
<span class="icon"></span>
<span>Lösenord lagras krypterat (AES-256). Lösenord visas ALDRIG i klartext — använd knappen för att visa/dölja tillfälligt.</span>
</div>
<div id="credsContent">
@@ -444,22 +444,22 @@
</div>
</div>
<!-- ─── PANEL: Publishing ────────────────────────────────── -->
<!-- PANEL: Publishing -->
<div id="panel-publishing" class="tab-panel">
<!-- Compose -->
<div class="card">
<div class="card-title">📝 Skapa inlägg</div>
<div class="card-title"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 13V10L10 3L13 6L6 13H3Z" stroke="#666" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M9 4L12 7" stroke="#666" stroke-width="1.5"/></svg> Skapa inlägg</div>
<div class="publish-grid">
<div>
<div class="form-group" style="margin-bottom:16px;">
<label>Bolag</label>
<select id="pubCompany" onchange="onPubCompanyChange()">
<option value="landvex">🏢 LandveX</option>
<option value="quixzoom"> QuiXzoom</option>
<option value="apifly">🚀 ApiFly</option>
<option value="corpfitt">💪 CorpFitt</option>
<option value="vyra"> VYRA</option>
<option value="aamos">🧠 AAMOS</option>
<option value="landvex"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="3" y="2" width="10" height="12" rx="1" stroke="#666" stroke-width="1.5"/><path d="M6 5H7M6 8H7M6 11H7M9 5H10M9 8H10M9 11H10" stroke="#666" stroke-width="1.5" stroke-linecap="round"/></svg> LandveX</option>
<option value="quixzoom"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M9 2L5 9H8L7 14L11 7H8L9 2Z" fill="#f59e0b"/></svg> QuiXzoom</option>
<option value="apifly"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M8 2L10 8L14 9L8 14L6 8L2 7L8 2Z" fill="#666"/></svg> ApiFly</option>
<option value="corpfitt"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M5 7C5 7 3 8 3 10C3 12 5 13 7 13H11C13 13 14 11 14 9C14 7 12 6 11 6H9V4C9 3 8 2 7 2C6 2 5 3 5 4V7Z" stroke="#666" stroke-width="1.5" stroke-linejoin="round"/></svg> CorpFitt</option>
<option value="vyra"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M8 2V4M8 12V14M2 8H4M12 8H14" stroke="#f59e0b" stroke-width="1.5" stroke-linecap="round"/><circle cx="8" cy="8" r="2" fill="#f59e0b"/></svg> VYRA</option>
<option value="aamos"> AAMOS</option>
</select>
</div>
<div class="form-group" style="margin-bottom:16px;">
@@ -477,22 +477,22 @@
<label>Schemaläggning</label>
<div class="schedule-row">
<div class="schedule-toggle">
<button class="sched-btn active" id="schedNowBtn" onclick="setScheduleMode('now')"> Nu</button>
<button class="sched-btn" id="schedLaterBtn" onclick="setScheduleMode('later')">🕐 Schemalägg</button>
<button class="sched-btn active" id="schedNowBtn" onclick="setScheduleMode('now')"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M9 2L5 9H8L7 14L11 7H8L9 2Z" fill="#f59e0b"/></svg> Nu</button>
<button class="sched-btn" id="schedLaterBtn" onclick="setScheduleMode('later')"> Schemalägg</button>
</div>
<input type="datetime-local" id="pubScheduleTime" style="display:none; flex:1; background:#030712; border:1px solid #1e2737; border-radius:8px; color:#e2e8f0; padding:7px 12px; font-size:13px;" onchange="updatePreview()">
<input type="datetime-local" id="pubScheduleTime" style="display:none; flex:1; background:#030712; border:1px solid #1e2737; border-radius: var(--radius-md); color:#e2e8f0; padding:7px 12px; font-size:13px;" onchange="updatePreview()">
</div>
</div>
<div style="margin-top:20px; display:flex; gap:12px; flex-wrap:wrap;">
<button class="btn btn-primary" onclick="submitPost()">📤 Publicera</button>
<button class="btn btn-secondary" onclick="saveDraft()">💾 Spara utkast</button>
<button class="btn btn-ghost" onclick="clearPost()">🗑 Rensa</button>
<button class="btn btn-primary" onclick="submitPost()"> Publicera</button>
<button class="btn btn-secondary" onclick="saveDraft()"> Spara utkast</button>
<button class="btn btn-ghost" onclick="clearPost()"> Rensa</button>
</div>
</div>
<div>
<div style="font-size:12px; font-weight:600; color:#475569; text-transform:uppercase; letter-spacing:0.5px; margin-bottom:12px;">Förhandsvisning</div>
<div id="pubPreviews">
<div style="color:#475569; font-size:13px; padding:16px; text-align:center; border:1px dashed #1e2737; border-radius:10px;">Välj plattformar och skriv innehåll för att se förhandsvisning</div>
<div style="color:#475569; font-size:13px; padding:16px; text-align:center; border:1px dashed #1e2737; border-radius: var(--radius-md);">Välj plattformar och skriv innehåll för att se förhandsvisning</div>
</div>
</div>
</div>
@@ -501,8 +501,8 @@
<!-- Post Queue -->
<div class="card">
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:16px;">
<div class="card-title" style="margin-bottom:0">📋 Publiceringsköen</div>
<button class="btn btn-secondary btn-sm" onclick="loadPostQueue()">🔄 Ladda om</button>
<div class="card-title" style="margin-bottom:0"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="3" y="2" width="10" height="12" rx="1" stroke="#666" stroke-width="1.5"/><path d="M6 5H10M6 8H10M6 11H8" stroke="#666" stroke-width="1.5" stroke-linecap="round"/></svg> Publiceringsköen</div>
<button class="btn btn-secondary btn-sm" onclick="loadPostQueue()"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 8C2 11.3 4.7 14 8 14C10.5 14 12.7 12.5 13.7 10.3" stroke="#666" stroke-width="1.5" stroke-linecap="round"/><path d="M14 8C14 4.7 11.3 2 8 2C5.5 2 3.3 3.5 2.3 5.7" stroke="#666" stroke-width="1.5" stroke-linecap="round"/><path d="M13.5 7.5L14 10L11.5 9.5" stroke="#666" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M2.5 8.5L2 6L4.5 6.5" stroke="#666" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg> Ladda om</button>
</div>
<div id="postQueueContent">
<div class="state-box"><div class="spinner"></div><br>Laddar...</div>
@@ -514,9 +514,9 @@
<script>
'use strict';
/* ═══════════════════════════════════════════════════════════
/*
CONSTANTS
═══════════════════════════════════════════════════════════ */
*/
const API = '';
const COMPANY_CREDS = {
@@ -541,10 +541,10 @@ const PLATFORM_CHAR_LIMITS = {
};
const PLATFORM_ICONS = {
twitter:'🐦', instagram:'📸', facebook:'👍', linkedin:'💼',
tiktok:'🎵', youtube:'▶️', reddit:'🤖', snapchat:'👻',
pinterest:'📌', spotify:'🎧', meta:'🌐',
jobteaser:'🎓', hired:'💼', outbrain:'📰'
twitter:'', instagram:'', facebook:'', linkedin:'',
tiktok:'', youtube:'', reddit:'', snapchat:'',
pinterest:'', spotify:'', meta:'',
jobteaser:'', hired:'', outbrain:''
};
const PLATFORM_LOGIN = {
@@ -568,13 +568,13 @@ const PLATFORM_LOGIN = {
ziprecruiter:'https://www.ziprecruiter.com/login',
};
/* ═══════════════════════════════════════════════════════════
/*
PLATFORM STEPS
Fix #5: replaced manual mail instructions with auto-fetch note
═══════════════════════════════════════════════════════════ */
*/
const PLATFORM_STEPS = {
tiktok: {
icon: '🎵',
icon: '',
title: 'TikTok for Business',
mailbox: true,
steps: [
@@ -587,7 +587,7 @@ const PLATFORM_STEPS = {
]
},
instagram: {
icon: '📸',
icon: '',
title: 'Instagram Business',
mailbox: true,
steps: [
@@ -601,7 +601,7 @@ const PLATFORM_STEPS = {
]
},
snapchat: {
icon: '👻',
icon: '',
title: 'Snapchat Business',
mailbox: true,
steps: [
@@ -614,7 +614,7 @@ const PLATFORM_STEPS = {
]
},
reddit: {
icon: '🤖',
icon: '',
title: 'Reddit Ads',
mailbox: false,
steps: [
@@ -627,7 +627,7 @@ const PLATFORM_STEPS = {
]
},
default: {
icon: '',
icon: '<svg width="20" height="20" viewBox="0 0 20 20" fill="none"><circle cx="10" cy="10" r="9" stroke="#22c55e" stroke-width="2"/><path d="M6 10L9 13L14 7" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>',
title: 'Granska &amp; godkänn',
mailbox: false,
steps: [
@@ -639,9 +639,9 @@ const PLATFORM_STEPS = {
}
};
/* ═══════════════════════════════════════════════════════════
/*
STATE
═══════════════════════════════════════════════════════════ */
*/
let currentCompany = 'landvex';
let currentMainTab = 'register';
let currentJobFilter = 'all';
@@ -651,9 +651,9 @@ let scheduleMode = 'now';
let countdownTimer = null;
let refreshCountdown = 15;
/* ═══════════════════════════════════════════════════════════
/*
AUTH
═══════════════════════════════════════════════════════════ */
*/
function jwtExpired(token) {
try {
const payload = JSON.parse(atob(token.split('.')[1].replace(/-/g,'+').replace(/_/g,'/')));
@@ -695,9 +695,9 @@ function getAuthToken() {
return getToken();
}
/* ═══════════════════════════════════════════════════════════
/*
TOAST
═══════════════════════════════════════════════════════════ */
*/
function toast(msg, type, durationMs) {
type = type || 'info';
durationMs = durationMs || 4000;
@@ -709,9 +709,9 @@ function toast(msg, type, durationMs) {
setTimeout(function() { t.remove(); }, durationMs);
}
/* ═══════════════════════════════════════════════════════════
/*
PLATFORMS
═══════════════════════════════════════════════════════════ */
*/
async function loadPlatforms() {
try {
await ensureAuth();
@@ -737,7 +737,7 @@ function renderPlatformDropdown() {
platformList.forEach(function(name) {
const opt = document.createElement('option');
opt.value = name;
opt.textContent = (PLATFORM_ICONS[name] || '🌐') + ' ' + name.charAt(0).toUpperCase() + name.slice(1);
opt.textContent = (PLATFORM_ICONS[name] || '') + ' ' + name.charAt(0).toUpperCase() + name.slice(1);
sel.appendChild(opt);
});
}
@@ -750,15 +750,15 @@ function renderPubPlatformChecks() {
const id = 'pubChk_' + name;
const wrap = document.createElement('label');
wrap.className = 'platform-checkbox';
wrap.style.cssText = 'display:flex;align-items:center;gap:6px;cursor:pointer;padding:5px 10px;border:1px solid #1e2737;border-radius:8px;';
wrap.innerHTML = '<input type="checkbox" id="' + id + '" value="' + name + '" onchange="updatePreview()" style="width:auto;cursor:pointer;"><span style="font-size:13px;color:#cbd5e1;">' + (PLATFORM_ICONS[name] || '🌐') + ' ' + name.charAt(0).toUpperCase() + name.slice(1) + '</span>';
wrap.style.cssText = 'display:flex;align-items:center;gap:6px;cursor:pointer;padding:5px 10px;border:1px solid #1e2737;border-radius: var(--radius-md);';
wrap.innerHTML = '<input type="checkbox" id="' + id + '" value="' + name + '" onchange="updatePreview()" style="width:auto;cursor:pointer;"><span style="font-size:13px;color:#cbd5e1;">' + (PLATFORM_ICONS[name] || '') + ' ' + name.charAt(0).toUpperCase() + name.slice(1) + '</span>';
container.appendChild(wrap);
});
}
/* ═══════════════════════════════════════════════════════════
/*
REGISTER FORM
═══════════════════════════════════════════════════════════ */
*/
async function fillForm(platform) {
if (!platform) return;
await ensureAuth();
@@ -839,19 +839,19 @@ async function startRegistration() {
});
const d = await r.json().catch(function() { return {}; });
if (r.ok) {
toast(' Automation startad för ' + platform + ' (' + company + ')', 'success');
toast('<svg width="20" height="20" viewBox="0 0 20 20" fill="none"><circle cx="10" cy="10" r="9" stroke="#22c55e" stroke-width="2"/><path d="M6 10L9 13L14 7" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Automation startad för ' + platform + ' (' + company + ')', 'success');
switchTab('jobs');
} else {
toast(' Fel: ' + (d.error || d.message || r.status), 'error');
toast('<svg width="20" height="20" viewBox="0 0 20 20" fill="none"><circle cx="10" cy="10" r="9" stroke="#ef4444" stroke-width="2"/><path d="M7 7L13 13M13 7L7 13" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Fel: ' + (d.error || d.message || r.status), 'error');
}
} catch(e) {
toast(' Nätverksfel: ' + e.message, 'error');
toast('<svg width="20" height="20" viewBox="0 0 20 20" fill="none"><circle cx="10" cy="10" r="9" stroke="#ef4444" stroke-width="2"/><path d="M7 7L13 13M13 7L7 13" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Nätverksfel: ' + e.message, 'error');
}
}
/* ═══════════════════════════════════════════════════════════
/*
COMPANY SELECTION
═══════════════════════════════════════════════════════════ */
*/
function selectCompany(company) {
currentCompany = company;
syncCompanyTabs();
@@ -884,20 +884,20 @@ function syncCompanyTabs() {
btn.classList.toggle('active', isActive);
// Bygg badge-html
let badges = '';
if (c.done > 0) badges += '<span style="background:#16a34a;color:#fff;border-radius:10px;padding:1px 6px;font-size:10px;margin-left:4px">'+c.done+'</span>';
if (c.running > 0) badges += '<span style="background:#2563eb;color:#fff;border-radius:10px;padding:1px 6px;font-size:10px;margin-left:4px">'+c.running+'</span>';
if (c.awaiting > 0) badges += '<span style="background:#d97706;color:#fff;border-radius:10px;padding:1px 6px;font-size:10px;margin-left:4px">⏳'+c.awaiting+'</span>';
if (c.error > 0) badges += '<span style="background:#dc2626;color:#fff;border-radius:10px;padding:1px 6px;font-size:10px;margin-left:4px">'+c.error+'</span>';
if (c.manual > 0) badges += '<span style="background:#7c3aed;color:#fff;border-radius:10px;padding:1px 6px;font-size:10px;margin-left:4px">🔧'+c.manual+'</span>';
if (c.done > 0) badges += '<span style="background:#16a34a;color:#fff;border-radius: var(--radius-md);padding:1px 6px;font-size:10px;margin-left:4px"><svg width="20" height="20" viewBox="0 0 20 20" fill="none"><circle cx="10" cy="10" r="9" stroke="#22c55e" stroke-width="2"/><path d="M6 10L9 13L14 7" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>'+c.done+'</span>';
if (c.running > 0) badges += '<span style="background:#2563eb;color:#fff;border-radius: var(--radius-md);padding:1px 6px;font-size:10px;margin-left:4px"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M9 2L5 9H8L7 14L11 7H8L9 2Z" fill="#f59e0b"/></svg>'+c.running+'</span>';
if (c.awaiting > 0) badges += '<span style="background:#d97706;color:#fff;border-radius: var(--radius-md);padding:1px 6px;font-size:10px;margin-left:4px">⏳'+c.awaiting+'</span>';
if (c.error > 0) badges += '<span style="background:#dc2626;color:#fff;border-radius: var(--radius-md);padding:1px 6px;font-size:10px;margin-left:4px"><svg width="20" height="20" viewBox="0 0 20 20" fill="none"><circle cx="10" cy="10" r="9" stroke="#ef4444" stroke-width="2"/><path d="M7 7L13 13M13 7L7 13" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg>'+c.error+'</span>';
if (c.manual > 0) badges += '<span style="background:#7c3aed;color:#fff;border-radius: var(--radius-md);padding:1px 6px;font-size:10px;margin-left:4px">'+c.manual+'</span>';
// Uppdatera knapp-label (spara original-label om inte redan gjort)
if (!btn.dataset.label) btn.dataset.label = btn.innerHTML;
btn.innerHTML = btn.dataset.label + badges;
});
}
/* ═══════════════════════════════════════════════════════════
/*
MAIN TAB SWITCHING
═══════════════════════════════════════════════════════════ */
*/
function switchTab(tab) {
currentMainTab = tab;
document.querySelectorAll('.main-tab').forEach(function(btn) {
@@ -913,9 +913,9 @@ function switchTab(tab) {
if (tab === 'publishing') loadPostQueue();
}
/* ═══════════════════════════════════════════════════════════
/*
JOBS
═══════════════════════════════════════════════════════════ */
*/
function setJobFilter(filter) {
currentJobFilter = filter;
document.querySelectorAll('.filter-btn').forEach(function(btn) {
@@ -935,7 +935,7 @@ async function loadJobs() {
renderJobs();
updateApproveAllBanner();
} catch(e) {
container.innerHTML = '<div class="state-box"><div class="icon">⚠️</div><b>Kunde inte ladda jobb</b><p>' + esc(e.message) + '</p></div>';
container.innerHTML = '<div class="state-box"><div class="icon"><svg width="20" height="20" viewBox="0 0 20 20" fill="none"><path d="M10 3L17 17H3L10 3Z" stroke="#f59e0b" stroke-width="2"/><path d="M10 8V12M10 14V14.5" stroke="#f59e0b" stroke-width="2" stroke-linecap="round"/></svg></div><b>Kunde inte ladda jobb</b><p>' + esc(e.message) + '</p></div>';
}
}
@@ -989,14 +989,14 @@ async function approveAll() {
} catch(e) { fail++; }
}
if (fail === 0) {
toast(' Alla ' + ok + ' jobb godkända', 'success');
toast('<svg width="20" height="20" viewBox="0 0 20 20" fill="none"><circle cx="10" cy="10" r="9" stroke="#22c55e" stroke-width="2"/><path d="M6 10L9 13L14 7" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Alla ' + ok + ' jobb godkända', 'success');
} else {
toast('⚠️ Godkände ' + ok + ', misslyckades ' + fail, 'warning');
toast('<svg width="20" height="20" viewBox="0 0 20 20" fill="none"><path d="M10 3L17 17H3L10 3Z" stroke="#f59e0b" stroke-width="2"/><path d="M10 8V12M10 14V14.5" stroke="#f59e0b" stroke-width="2" stroke-linecap="round"/></svg> Godkände ' + ok + ', misslyckades ' + fail, 'warning');
}
loadJobs();
}
/* ─── Build steps HTML (Fix #1: "Gör så här", Fix #3: mail-knapp, Fix #4: credentials) ─── */
/* Build steps HTML (Fix #1: "Gör så här", Fix #3: mail-knapp, Fix #4: credentials) */
async function buildStepsHtml(job) {
const plat = (job.platform || '').toLowerCase();
const info = PLATFORM_STEPS[plat] || PLATFORM_STEPS.default;
@@ -1015,15 +1015,15 @@ async function buildStepsHtml(job) {
if (!isManual) {
return '<a href="' + autofillUrl + '" target="_blank" rel="noopener" ' +
'style="display:inline-flex;align-items:center;gap:5px;color:#a5b4fc;font-size:.75rem;font-weight:600;' +
'text-decoration:none;border:1px solid #4338ca;padding:4px 12px;border-radius:6px">' +
'text-decoration:none;border:1px solid #4338ca;padding:4px 12px;border-radius: 6px">' +
'Logga in på ' + esc(job.platform) + '</a>';
}
/* Build steps list */
const stepsHtml = info.steps.map(function(s) {
return '<li style="margin-bottom:8px;line-height:1.5">' +
s.replace(/{email}/g, '<code style="background:#1e293b;padding:2px 8px;border-radius:4px;color:#6ee7b7;font-size:.85em">' + email + '</code>')
.replace(/{username}/g, '<code style="background:#1e293b;padding:2px 8px;border-radius:4px;color:#a5b4fc;font-size:.85em">' + username + '</code>') +
s.replace(/{email}/g, '<code style="background:#1e293b;padding:2px 8px;border-radius: var(--radius-sm);color:#6ee7b7;font-size:.85em">' + email + '</code>')
.replace(/{username}/g, '<code style="background:#1e293b;padding:2px 8px;border-radius: var(--radius-sm);color:#a5b4fc;font-size:.85em">' + username + '</code>') +
'</li>';
}).join('');
@@ -1047,7 +1047,7 @@ async function buildStepsHtml(job) {
const safeId = String(id).replace(/[^a-z0-9\-]/gi, '');
credsBlock = '<div class="inline-creds">' +
'<div class="inline-creds-title">🔐 Inloggningsuppgifter</div>' +
'<div class="inline-creds-title"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="3" y="7" width="10" height="7" rx="1" stroke="#666" stroke-width="1.5"/><path d="M5 7V5C5 3.3 6.3 2 8 2C9.7 2 11 3.3 11 5V7" stroke="#666" stroke-width="1.5"/><circle cx="8" cy="10.5" r="1" fill="#666"/></svg> Inloggningsuppgifter</div>' +
'<div class="cred-inline-row">' +
'<span class="cred-inline-label">E-post</span>' +
'<span class="cred-inline-val">' + email + '</span>' +
@@ -1057,9 +1057,9 @@ async function buildStepsHtml(job) {
'<span class="cred-inline-label">Lösenord</span>' +
(revealedPw
? '<input type="password" id="pw-' + safeId + '" value="' + esc(revealedPw) + '" readonly class="pw-field">' +
'<button class="reveal-btn" onclick="togglePw(\'pw-' + safeId + '\', this)">👁 Visa</button>' +
'<button class="reveal-btn" onclick="togglePw(\'pw-' + safeId + '\', this)"> Visa</button>' +
'<button class="copy-btn" onclick="copyText(\'' + revealedPw.replace(/'/g, "\\'") + '\', this)">Kopiera</button>'
: '<span style="color:#475569;font-size:12px;">🔒 Ej tillgängligt</span>'
: '<span style="color:#475569;font-size:12px;"> Ej tillgängligt</span>'
) +
'</div>' +
'</div>';
@@ -1067,9 +1067,9 @@ async function buildStepsHtml(job) {
/* Fix #3: Mail-code fetch button */
const mailCodeHtml = info.mailbox
? '<div class="mail-code-box">' +
'<div style="font-size:12px;color:#94a3b8;margin-bottom:8px;">📧 Väntar på verifieringskod via mail</div>' +
'<div style="font-size:12px;color:#94a3b8;margin-bottom:8px;"> Väntar på verifieringskod via mail</div>' +
'<button class="btn btn-teal btn-sm" onclick="fetchMailCode(\'' + safeId + '\', \'' + esc(plat) + '\', this)">' +
'📨 Hämta kod</button>' +
' Hämta kod</button>' +
'<div id="mail-code-result-' + safeId + '" class="mail-code-result hidden"></div>' +
'<div style="margin-top:6px;">' +
'<a href="https://mail.quixzoom.com/sso/login" target="_blank" rel="noopener" ' +
@@ -1080,10 +1080,10 @@ async function buildStepsHtml(job) {
const loginBtn = '<a href="' + autofillUrl + '" target="_blank" rel="noopener" ' +
'style="display:inline-flex;align-items:center;gap:6px;background:#6366f1;color:#fff;font-weight:700;' +
'font-size:.85rem;text-decoration:none;padding:10px 20px;border-radius:8px;margin-top:10px">' +
'font-size:.85rem;text-decoration:none;padding:10px 20px;border-radius: var(--radius-md);margin-top:10px">' +
info.icon + ' Logga in på ' + esc(job.platform) + ' &rarr;</a>';
return '<div style="background:#0a0e1a;border:1px solid #1e2a38;border-radius:10px;padding:16px;margin-top:10px">' +
return '<div style="background:#0a0e1a;border:1px solid #1e2a38;border-radius: var(--radius-md);padding:16px;margin-top:10px">' +
'<div style="font-weight:700;color:#e2e8f0;margin-bottom:10px;font-size:.9rem">' + info.icon + ' Gör så här:</div>' +
'<ol style="padding-left:0;list-style:none;margin:0;font-size:.82rem;color:#cbd5e1">' + stepsHtml + '</ol>' +
credsBlock +
@@ -1111,38 +1111,38 @@ async function fetchMailCode(jobId, platform, btn) {
resultEl.classList.remove('hidden');
resultEl.innerHTML = 'Kod: <strong style="font-size:1.4em;letter-spacing:4px;">' + esc(code) + '</strong>' +
'<button class="copy-btn" onclick="copyText(\'' + esc(code) + '\', this)">Kopiera</button>';
btn.textContent = ' Hämtad';
btn.textContent = '<svg width="20" height="20" viewBox="0 0 20 20" fill="none"><circle cx="10" cy="10" r="9" stroke="#22c55e" stroke-width="2"/><path d="M6 10L9 13L14 7" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Hämtad';
} else {
toast('⚠️ Ingen kod hittades ännu — försök om ett par sekunder', 'warning');
toast('<svg width="20" height="20" viewBox="0 0 20 20" fill="none"><path d="M10 3L17 17H3L10 3Z" stroke="#f59e0b" stroke-width="2"/><path d="M10 8V12M10 14V14.5" stroke="#f59e0b" stroke-width="2" stroke-linecap="round"/></svg> Ingen kod hittades ännu — försök om ett par sekunder', 'warning');
btn.disabled = false;
btn.textContent = '📨 Hämta kod';
btn.textContent = ' Hämta kod';
}
} else if (r.status === 404) {
/* Endpoint doesn't exist — fallback to open mailbox */
toast(' Automatisk hämtning ej tillgänglig — öppna mailboxen manuellt', 'info');
toast(' Automatisk hämtning ej tillgänglig — öppna mailboxen manuellt', 'info');
window.open('https://mail.quixzoom.com/sso/login', '_blank');
btn.disabled = false;
btn.textContent = '📨 Hämta kod';
btn.textContent = ' Hämta kod';
} else {
const d = await r.json().catch(function() { return {}; });
toast(' Fel vid hämtning: ' + (d.error || r.status), 'error');
toast('<svg width="20" height="20" viewBox="0 0 20 20" fill="none"><circle cx="10" cy="10" r="9" stroke="#ef4444" stroke-width="2"/><path d="M7 7L13 13M13 7L7 13" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Fel vid hämtning: ' + (d.error || r.status), 'error');
btn.disabled = false;
btn.textContent = '📨 Hämta kod';
btn.textContent = ' Hämta kod';
}
} catch(e) {
toast(' Nätverksfel: ' + e.message, 'error');
toast('<svg width="20" height="20" viewBox="0 0 20 20" fill="none"><circle cx="10" cy="10" r="9" stroke="#ef4444" stroke-width="2"/><path d="M7 7L13 13M13 7L7 13" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Nätverksfel: ' + e.message, 'error');
btn.disabled = false;
btn.textContent = '📨 Hämta kod';
btn.textContent = ' Hämta kod';
}
}
/* ─── Render jobs table ─── */
/* Render jobs table */
async function renderJobs() {
const container = document.getElementById('jobsContent');
if (!container) return;
const jobs = filterJobs();
if (jobs.length === 0) {
container.innerHTML = '<div class="state-box"><div class="icon">📭</div><b>Inga jobb hittades</b><p>Byt filter eller starta ett nytt jobb.</p></div>';
container.innerHTML = '<div class="state-box"><div class="icon"></div><b>Inga jobb hittades</b><p>Byt filter eller starta ett nytt jobb.</p></div>';
return;
}
@@ -1177,18 +1177,18 @@ async function renderJobs() {
const badgeClass = badgeMap[status] || 'badge-pending';
const statusLabel = {
awaiting_approval: '⏳ Väntar godkännande',
pending_manual: '🖐 Manuell åtgärd',
manual_required: '🖐 Manuell krävs',
manual_handle_review:'🖐 Granska handle',
completed: ' Klar',
failed: ' Misslyckad',
pending_manual: ' Manuell åtgärd',
manual_required: ' Manuell krävs',
manual_handle_review:' Granska handle',
completed: '<svg width="20" height="20" viewBox="0 0 20 20" fill="none"><circle cx="10" cy="10" r="9" stroke="#22c55e" stroke-width="2"/><path d="M6 10L9 13L14 7" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Klar',
failed: '<svg width="20" height="20" viewBox="0 0 20 20" fill="none"><circle cx="10" cy="10" r="9" stroke="#ef4444" stroke-width="2"/><path d="M7 7L13 13M13 7L7 13" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Misslyckad',
timeout: '⌛ Timeout',
}[status] || status.replace(/_/g, ' ');
/* Action required column */
let actionRequired = '<span class="action-none">—</span>';
if (isAwait) actionRequired = '<span class="action-required-cell"> Godkänn / avvisa</span>';
if (isManual) actionRequired = '<span class="action-required-cell">🖐 Manuell inloggning krävs</span>';
if (isAwait) actionRequired = '<span class="action-required-cell"><svg width="20" height="20" viewBox="0 0 20 20" fill="none"><circle cx="10" cy="10" r="9" stroke="#22c55e" stroke-width="2"/><path d="M6 10L9 13L14 7" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Godkänn / avvisa</span>';
if (isManual) actionRequired = '<span class="action-required-cell"> Manuell inloggning krävs</span>';
/* Fix #2: Screenshot from /api/social-account/screenshot/:jobId */
const screenshotUrl = API + '/api/social-account/screenshot/' + encodeURIComponent(id);
@@ -1204,10 +1204,10 @@ async function renderJobs() {
/* Action buttons */
let actions = '';
if (isAwait) {
actions += '<button class="btn btn-success btn-sm" data-action="approve" data-jobid="' + esc(String(id)) + '"> Godkänn</button> ';
actions += '<button class="btn btn-danger btn-sm" data-action="reject" data-jobid="' + esc(String(id)) + '"> Avvisa</button>';
actions += '<button class="btn btn-success btn-sm" data-action="approve" data-jobid="' + esc(String(id)) + '"><svg width="20" height="20" viewBox="0 0 20 20" fill="none"><circle cx="10" cy="10" r="9" stroke="#22c55e" stroke-width="2"/><path d="M6 10L9 13L14 7" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Godkänn</button> ';
actions += '<button class="btn btn-danger btn-sm" data-action="reject" data-jobid="' + esc(String(id)) + '"><svg width="20" height="20" viewBox="0 0 20 20" fill="none"><circle cx="10" cy="10" r="9" stroke="#ef4444" stroke-width="2"/><path d="M7 7L13 13M13 7L7 13" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Avvisa</button>';
} else {
actions += '<button class="btn btn-ghost btn-sm" data-action="delete" data-jobid="' + esc(String(id)) + '">🗑</button>';
actions += '<button class="btn btn-ghost btn-sm" data-action="delete" data-jobid="' + esc(String(id)) + '"></button>';
}
/* Build the steps block (async) */
@@ -1215,7 +1215,7 @@ async function renderJobs() {
html += '<tr' + (isAwait ? ' class="row-awaiting"' : '') + '>';
html += '<td><code style="font-size:11px;color:#6366f1">' + esc(String(id).slice(0, 12)) + '</code></td>';
html += '<td>' + (PLATFORM_ICONS[job.platform] || '🌐') + ' ' + platform + '</td>';
html += '<td>' + (PLATFORM_ICONS[job.platform] || '') + ' ' + platform + '</td>';
html += '<td><div><span class="badge ' + badgeClass + '">' + esc(statusLabel) + '</span>' + stepsBlock + '</div></td>';
html += '<td>' + actionRequired + '</td>';
html += '<td style="white-space:nowrap;font-size:12px;">' + created + '</td>';
@@ -1234,13 +1234,13 @@ async function approveJob(jobId) {
method: 'POST', headers: authHeaders()
});
if (r.ok) {
toast(' Jobb godkänt', 'success');
toast('<svg width="20" height="20" viewBox="0 0 20 20" fill="none"><circle cx="10" cy="10" r="9" stroke="#22c55e" stroke-width="2"/><path d="M6 10L9 13L14 7" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Jobb godkänt', 'success');
loadJobs();
} else {
const d = await r.json().catch(function() { return {}; });
toast(' Godkännande misslyckades: ' + (d.error || r.status), 'error');
toast('<svg width="20" height="20" viewBox="0 0 20 20" fill="none"><circle cx="10" cy="10" r="9" stroke="#ef4444" stroke-width="2"/><path d="M7 7L13 13M13 7L7 13" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Godkännande misslyckades: ' + (d.error || r.status), 'error');
}
} catch(e) { toast(' ' + e.message, 'error'); }
} catch(e) { toast('<svg width="20" height="20" viewBox="0 0 20 20" fill="none"><circle cx="10" cy="10" r="9" stroke="#ef4444" stroke-width="2"/><path d="M7 7L13 13M13 7L7 13" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> ' + e.message, 'error'); }
}
async function rejectJob(jobId) {
@@ -1254,7 +1254,7 @@ async function deleteJob(jobId) {
method: 'DELETE', headers: authHeaders()
});
if (r.ok) {
toast('🗑 Jobb raderat', 'info');
toast(' Jobb raderat', 'info');
allJobs = allJobs.filter(function(j) {
const jid = j.jobId || j.id || j.job_id;
return String(jid) !== String(jobId);
@@ -1262,9 +1262,9 @@ async function deleteJob(jobId) {
renderJobs();
} else {
const d = await r.json().catch(function() { return {}; });
toast(' Radering misslyckades: ' + (d.error || r.status), 'error');
toast('<svg width="20" height="20" viewBox="0 0 20 20" fill="none"><circle cx="10" cy="10" r="9" stroke="#ef4444" stroke-width="2"/><path d="M7 7L13 13M13 7L7 13" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Radering misslyckades: ' + (d.error || r.status), 'error');
}
} catch(e) { toast(' ' + e.message, 'error'); }
} catch(e) { toast('<svg width="20" height="20" viewBox="0 0 20 20" fill="none"><circle cx="10" cy="10" r="9" stroke="#ef4444" stroke-width="2"/><path d="M7 7L13 13M13 7L7 13" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> ' + e.message, 'error'); }
}
/* Fix #6: Auto-refresh every 15s */
@@ -1291,9 +1291,9 @@ function updateRefreshInfo() {
if (el) el.textContent = 'Auto-refresh om ' + refreshCountdown + 's';
}
/* ═══════════════════════════════════════════════════════════
/*
CREDENTIALS
═══════════════════════════════════════════════════════════ */
*/
async function loadCredentials() {
const container = document.getElementById('credsContent');
if (!container) return;
@@ -1320,7 +1320,7 @@ async function loadCredentials() {
}
renderCredentials(creds);
} catch(e) {
container.innerHTML = '<div class="state-box"><div class="icon">⚠️</div><b>Kunde inte ladda credentials</b><p>' + esc(e.message) + '</p></div>';
container.innerHTML = '<div class="state-box"><div class="icon"><svg width="20" height="20" viewBox="0 0 20 20" fill="none"><path d="M10 3L17 17H3L10 3Z" stroke="#f59e0b" stroke-width="2"/><path d="M10 8V12M10 14V14.5" stroke="#f59e0b" stroke-width="2" stroke-linecap="round"/></svg></div><b>Kunde inte ladda credentials</b><p>' + esc(e.message) + '</p></div>';
}
}
@@ -1328,7 +1328,7 @@ function renderCredentials(creds) {
const container = document.getElementById('credsContent');
if (!container) return;
if (!creds || creds.length === 0) {
container.innerHTML = '<div class="state-box"><div class="icon">🔐</div><b>Inga credentials för ' + esc(currentCompany) + '</b><p>Registrera konton för att se credentials här.</p></div>';
container.innerHTML = '<div class="state-box"><div class="icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="3" y="7" width="10" height="7" rx="1" stroke="#666" stroke-width="1.5"/><path d="M5 7V5C5 3.3 6.3 2 8 2C9.7 2 11 3.3 11 5V7" stroke="#666" stroke-width="1.5"/><circle cx="8" cy="10.5" r="1" fill="#666"/></svg></div><b>Inga credentials för ' + esc(currentCompany) + '</b><p>Registrera konton för att se credentials här.</p></div>';
return;
}
@@ -1337,7 +1337,7 @@ function renderCredentials(creds) {
const platform = esc(c.platform || '?');
const email = esc(c.email || '—');
const username = esc(c.username || '—');
const icon = PLATFORM_ICONS[c.platform] || '🌐';
const icon = PLATFORM_ICONS[c.platform] || '';
const hasPw = c.password && c.password !== '' && c.password !== '•••••';
const pwId = 'cred-pw-' + idx;
@@ -1353,9 +1353,9 @@ function renderCredentials(creds) {
html += '<div class="cred-row"><span class="cred-label">Lösenord</span>' +
(hasPw
? '<input type="password" id="' + pwId + '" value="' + esc(c.password) + '" readonly class="pw-field">' +
'<button class="reveal-btn" onclick="togglePw(\'' + pwId + '\', this)">👁 Visa</button>' +
'<button class="reveal-btn" onclick="togglePw(\'' + pwId + '\', this)"> Visa</button>' +
'<button class="copy-btn" onclick="copyText(\'' + c.password.replace(/'/g, "\\'") + '\', this)">Kopiera</button>'
: '<span class="cred-value" style="color:#475569;">🔒 Krypterat (AES-256)</span>'
: '<span class="cred-value" style="color:#475569;"> Krypterat (AES-256)</span>'
) +
'</div>';
html += '</div></div>';
@@ -1385,7 +1385,7 @@ async function confirmRotate() {
let rotated = 0, failed = 0;
const logItems = [];
toast('🔄 Roterar lösenord...', 'info', 10000);
toast('<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 8C2 11.3 4.7 14 8 14C10.5 14 12.7 12.5 13.7 10.3" stroke="#666" stroke-width="1.5" stroke-linecap="round"/><path d="M14 8C14 4.7 11.3 2 8 2C5.5 2 3.3 3.5 2.3 5.7" stroke="#666" stroke-width="1.5" stroke-linecap="round"/><path d="M13.5 7.5L14 10L11.5 9.5" stroke="#666" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M2.5 8.5L2 6L4.5 6.5" stroke="#666" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg> Roterar lösenord...', 'info', 10000);
for (const company of companies) {
try {
@@ -1412,9 +1412,9 @@ async function confirmRotate() {
}
if (failed === 0) {
toast(' Lösenord roterade för ' + rotated + ' bolag', 'success');
toast('<svg width="20" height="20" viewBox="0 0 20 20" fill="none"><circle cx="10" cy="10" r="9" stroke="#22c55e" stroke-width="2"/><path d="M6 10L9 13L14 7" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Lösenord roterade för ' + rotated + ' bolag', 'success');
} else {
toast('⚠️ Roterade ' + rotated + ', misslyckades ' + failed, 'warning');
toast('<svg width="20" height="20" viewBox="0 0 20 20" fill="none"><path d="M10 3L17 17H3L10 3Z" stroke="#f59e0b" stroke-width="2"/><path d="M10 8V12M10 14V14.5" stroke="#f59e0b" stroke-width="2" stroke-linecap="round"/></svg> Roterade ' + rotated + ', misslyckades ' + failed, 'warning');
}
const logDiv = document.getElementById('rotationLog');
@@ -1423,7 +1423,7 @@ async function confirmRotate() {
logItems.forEach(function(item) {
logHtml += '<div class="rotation-log-item">';
logHtml += '<span>' + esc(COMPANY_LABELS[item.company] || item.company) + '</span>';
logHtml += '<span>' + (item.status === 'ok' ? '✅ ' + (item.platforms || 0) + ' plattformar' : '❌ Fel') + '</span>';
logHtml += '<span>' + (item.status === 'ok' ? '<svg width="20" height="20" viewBox="0 0 20 20" fill="none"><circle cx="10" cy="10" r="9" stroke="#22c55e" stroke-width="2"/><path d="M6 10L9 13L14 7" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> ' + (item.platforms || 0) + ' plattformar' : '<svg width="20" height="20" viewBox="0 0 20 20" fill="none"><circle cx="10" cy="10" r="9" stroke="#ef4444" stroke-width="2"/><path d="M7 7L13 13M13 7L7 13" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Fel') + '</span>';
logHtml += '<span style="color:#374151;">' + esc(item.ts) + '</span>';
logHtml += '</div>';
});
@@ -1443,9 +1443,9 @@ async function confirmOffboard() {
await confirmRotate();
}
/* ═══════════════════════════════════════════════════════════
/*
PUBLISHING MODULE
═══════════════════════════════════════════════════════════ */
*/
function onPubCompanyChange() {
const sel = document.getElementById('pubCompany');
currentCompany = sel ? sel.value : 'landvex';
@@ -1486,19 +1486,19 @@ function updatePreview() {
const platforms = getSelectedPlatforms();
updateCharCounter();
if (platforms.length === 0 || !text.trim()) {
container.innerHTML = '<div style="color:#475569;font-size:13px;padding:16px;text-align:center;border:1px dashed #1e2737;border-radius:10px;">Välj plattformar och skriv innehåll för att se förhandsvisning</div>';
container.innerHTML = '<div style="color:#475569;font-size:13px;padding:16px;text-align:center;border:1px dashed #1e2737;border-radius: var(--radius-md);">Välj plattformar och skriv innehåll för att se förhandsvisning</div>';
return;
}
let html = '';
platforms.forEach(function(plat) {
const limit = PLATFORM_CHAR_LIMITS[plat] || 99999;
const preview = text.length > limit ? text.slice(0, limit - 3) + '...' : text;
const icon = PLATFORM_ICONS[plat] || '🌐';
const icon = PLATFORM_ICONS[plat] || '';
html += '<div class="post-preview" style="margin-bottom:12px;">';
html += '<div class="preview-platform">' + icon + ' ' + esc(plat) + '</div>';
html += '<div class="preview-content">' + esc(preview) + '</div>';
if (text.length > limit) {
html += '<div style="font-size:11px;color:#ef4444;margin-top:6px;">⚠️ ' + (text.length - limit) + ' tecken för långt</div>';
html += '<div style="font-size:11px;color:#ef4444;margin-top:6px;"><svg width="20" height="20" viewBox="0 0 20 20" fill="none"><path d="M10 3L17 17H3L10 3Z" stroke="#f59e0b" stroke-width="2"/><path d="M10 8V12M10 14V14.5" stroke="#f59e0b" stroke-width="2" stroke-linecap="round"/></svg> ' + (text.length - limit) + ' tecken för långt</div>';
}
html += '</div>';
});
@@ -1533,14 +1533,14 @@ async function submitPost(asDraft) {
});
const d = await r.json().catch(function() { return {}; });
if (r.ok) {
toast(asDraft ? '💾 Utkast sparat' : '📤 Inlägg schemalagt/publicerat', 'success');
toast(asDraft ? ' Utkast sparat' : ' Inlägg schemalagt/publicerat', 'success');
clearPost();
loadPostQueue();
} else {
toast(' Fel: ' + (d.error || d.message || r.status), 'error');
toast('<svg width="20" height="20" viewBox="0 0 20 20" fill="none"><circle cx="10" cy="10" r="9" stroke="#ef4444" stroke-width="2"/><path d="M7 7L13 13M13 7L7 13" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Fel: ' + (d.error || d.message || r.status), 'error');
}
} catch(e) {
toast(' Nätverksfel: ' + e.message, 'error');
toast('<svg width="20" height="20" viewBox="0 0 20 20" fill="none"><circle cx="10" cy="10" r="9" stroke="#ef4444" stroke-width="2"/><path d="M7 7L13 13M13 7L7 13" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Nätverksfel: ' + e.message, 'error');
}
}
@@ -1565,7 +1565,7 @@ async function loadPostQueue() {
const posts = Array.isArray(data) ? data : (data.posts || data.queue || []);
renderPostQueue(posts);
} catch(e) {
container.innerHTML = '<div class="state-box"><div class="icon">⚠️</div><b>Kunde inte ladda kön</b><p>' + esc(e.message) + '</p></div>';
container.innerHTML = '<div class="state-box"><div class="icon"><svg width="20" height="20" viewBox="0 0 20 20" fill="none"><path d="M10 3L17 17H3L10 3Z" stroke="#f59e0b" stroke-width="2"/><path d="M10 8V12M10 14V14.5" stroke="#f59e0b" stroke-width="2" stroke-linecap="round"/></svg></div><b>Kunde inte ladda kön</b><p>' + esc(e.message) + '</p></div>';
}
}
@@ -1573,7 +1573,7 @@ function renderPostQueue(posts) {
const container = document.getElementById('postQueueContent');
if (!container) return;
if (posts.length === 0) {
container.innerHTML = '<div class="state-box"><div class="icon">📭</div><b>Inga inlägg i kön</b><p>Skapa ett inlägg ovan.</p></div>';
container.innerHTML = '<div class="state-box"><div class="icon"></div><b>Inga inlägg i kön</b><p>Skapa ett inlägg ovan.</p></div>';
return;
}
let html = '<table class="post-queue-table"><thead><tr><th>ID</th><th>Bolag</th><th>Plattformar</th><th>Status</th><th>Schemalagt</th><th>Innehåll</th><th>Åtgärder</th></tr></thead><tbody>';
@@ -1581,7 +1581,7 @@ function renderPostQueue(posts) {
const id = esc(String(p.id || p.post_id || '—').slice(0, 10));
const company = esc(p.company || '—');
const platforms = Array.isArray(p.platforms)
? p.platforms.map(function(x) { return (PLATFORM_ICONS[x] || '🌐') + ' ' + x; }).join(', ')
? p.platforms.map(function(x) { return (PLATFORM_ICONS[x] || '') + ' ' + x; }).join(', ')
: esc(String(p.platforms || '—'));
const status = (p.status || 'pending').toLowerCase();
const sched = p.scheduledAt ? new Date(p.scheduledAt).toLocaleString('sv-SE') : (status === 'pending' ? 'Nu' : '—');
@@ -1595,7 +1595,7 @@ function renderPostQueue(posts) {
html += '<td><span class="badge ' + badgeCls + '">' + esc(status) + '</span></td>';
html += '<td style="font-size:11px;">' + esc(sched) + '</td>';
html += '<td style="font-size:11px;max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">' + preview + '</td>';
html += '<td><button class="btn btn-ghost btn-sm" onclick="deletePost(' + JSON.stringify(p.id || p.post_id) + ')">🗑</button></td>';
html += '<td><button class="btn btn-ghost btn-sm" onclick="deletePost(' + JSON.stringify(p.id || p.post_id) + ')"></button></td>';
html += '</tr>';
});
html += '</tbody></table>';
@@ -1608,14 +1608,14 @@ async function deletePost(postId) {
const r = await fetch(API + '/api/social-account/publish/' + encodeURIComponent(postId), {
method: 'DELETE', headers: authHeaders()
});
if (r.ok) { toast('🗑 Inlägg borttaget', 'info'); loadPostQueue(); }
else { toast(' Radering misslyckades', 'error'); }
} catch(e) { toast(' ' + e.message, 'error'); }
if (r.ok) { toast(' Inlägg borttaget', 'info'); loadPostQueue(); }
else { toast('<svg width="20" height="20" viewBox="0 0 20 20" fill="none"><circle cx="10" cy="10" r="9" stroke="#ef4444" stroke-width="2"/><path d="M7 7L13 13M13 7L7 13" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Radering misslyckades', 'error'); }
} catch(e) { toast('<svg width="20" height="20" viewBox="0 0 20 20" fill="none"><circle cx="10" cy="10" r="9" stroke="#ef4444" stroke-width="2"/><path d="M7 7L13 13M13 7L7 13" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> ' + e.message, 'error'); }
}
/* ═══════════════════════════════════════════════════════════
/*
UTILITY
═══════════════════════════════════════════════════════════ */
*/
function esc(s) {
return String(s)
.replace(/&/g, '&amp;')
@@ -1628,10 +1628,10 @@ function esc(s) {
function copyText(text, btn) {
navigator.clipboard.writeText(text).then(function() {
const orig = btn.textContent;
btn.textContent = '';
btn.textContent = '<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>';
setTimeout(function() { btn.textContent = orig; }, 1500);
}).catch(function() {
toast(' Kopiering misslyckades', 'error');
toast('<svg width="20" height="20" viewBox="0 0 20 20" fill="none"><circle cx="10" cy="10" r="9" stroke="#ef4444" stroke-width="2"/><path d="M7 7L13 13M13 7L7 13" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Kopiering misslyckades', 'error');
});
}
@@ -1640,10 +1640,10 @@ function togglePw(inputId, btn) {
if (!input) return;
if (input.type === 'password') {
input.type = 'text';
btn.textContent = '🙈 Dölj';
btn.textContent = ' Dölj';
} else {
input.type = 'password';
btn.textContent = '👁 Visa';
btn.textContent = ' Visa';
}
}
@@ -1654,14 +1654,14 @@ function setStatus(online) {
if (text) text.textContent = online ? 'Online' : 'Offline';
}
/* ═══════════════════════════════════════════════════════════
/*
INIT
═══════════════════════════════════════════════════════════ */
*/
async function init() {
try {
const tok = await ensureAuth();
setStatus(!!tok);
if (!tok) toast('⚠️ Autentisering misslyckades', 'error', 8000);
if (!tok) toast('<svg width="20" height="20" viewBox="0 0 20 20" fill="none"><path d="M10 3L17 17H3L10 3Z" stroke="#f59e0b" stroke-width="2"/><path d="M10 8V12M10 14V14.5" stroke="#f59e0b" stroke-width="2" stroke-linecap="round"/></svg> Autentisering misslyckades', 'error', 8000);
await loadPlatforms();
switchTab('register');
startAutoRefresh();
@@ -1669,15 +1669,15 @@ async function init() {
} catch(e) {
console.error('[ARGUS] Init error:', e);
setStatus(false);
toast(' Initieringsfel: ' + e.message, 'error', 8000);
toast('<svg width="20" height="20" viewBox="0 0 20 20" fill="none"><circle cx="10" cy="10" r="9" stroke="#ef4444" stroke-width="2"/><path d="M7 7L13 13M13 7L7 13" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Initieringsfel: ' + e.message, 'error', 8000);
}
}
window.addEventListener('DOMContentLoaded', function() { init(); });
/* ═══════════════════════════════════════════════════════════
/*
EVENT DELEGATION for job buttons
═══════════════════════════════════════════════════════════ */
*/
document.addEventListener('click', function(e) {
const btn = e.target.closest('[data-action]');
if (!btn) return;
@@ -1689,9 +1689,9 @@ document.addEventListener('click', function(e) {
else if (action === 'delete') deleteJob(jobId);
});
/* ═══════════════════════════════════════════════════════
/*
ADD PLATFORM MODAL
═══════════════════════════════════════════════════════ */
*/
function openAddPlatformModal() {
var existing = document.getElementById('addPlatformModal');
@@ -1711,47 +1711,47 @@ function openAddPlatformModal() {
var companyLabels = {landvex:'LandveX',quixzoom:'QuiXzoom',apifly:'ApiFly',corpfitt:'CorpFitt',vyra:'VYRA',aamos:'AAMOS'};
var html = [
'<div style="background:#0d1117;border:1px solid #1e2737;border-radius:16px;padding:32px;max-width:560px;width:95%;max-height:90vh;overflow-y:auto;">',
'<div style="background:#0d1117;border:1px solid #1e2737;border-radius: var(--radius-lg);padding:32px;max-width:560px;width:95%;max-height:90vh;overflow-y:auto;">',
'<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:24px;">',
'<h3 style="font-size:18px;font-weight:700;color:#f1f5f9;margin:0;">+ Lägg till plattform</h3>',
'<button onclick="closeAddPlatformModal()" style="background:none;border:none;color:#64748b;font-size:20px;cursor:pointer;padding:4px 8px;border-radius:6px;" onmouseover="this.style.color=\'#e2e8f0\'" onmouseout="this.style.color=\'#64748b\'">&#x2715;</button>',
'<button onclick="closeAddPlatformModal()" style="background:none;border:none;color:#64748b;font-size:20px;cursor:pointer;padding:4px 8px;border-radius: var(--radius-sm);" onmouseover="this.style.color=\'#e2e8f0\'" onmouseout="this.style.color=\'#64748b\'">&#x2715;</button>',
'</div>',
'<div style="display:grid;grid-template-columns:1fr 1fr;gap:16px;">',
'<div class="form-group">',
'<label>Plattformsnamn</label>',
'<input type="text" id="ap_name" placeholder="t.ex. Mastodon" style="background:#030712;border:1px solid #1e2737;border-radius:8px;color:#e2e8f0;padding:9px 12px;font-size:13px;width:100%;">',
'<input type="text" id="ap_name" placeholder="t.ex. Mastodon" style="background:#030712;border:1px solid #1e2737;border-radius: var(--radius-md);color:#e2e8f0;padding:9px 12px;font-size:13px;width:100%;">',
'</div>',
'<div class="form-group">',
'<label>Ikon/Emoji</label>',
'<input type="text" id="ap_icon" placeholder="🌐" value="🌐" style="background:#030712;border:1px solid #1e2737;border-radius:8px;color:#e2e8f0;padding:9px 12px;font-size:13px;width:100%;">',
'<input type="text" id="ap_icon" placeholder="" value="" style="background:#030712;border:1px solid #1e2737;border-radius: var(--radius-md);color:#e2e8f0;padding:9px 12px;font-size:13px;width:100%;">',
'</div>',
'<div class="form-group" style="grid-column:span 2;">',
'<label>Registrerings-URL</label>',
'<input type="url" id="ap_url" placeholder="https://platform.com/signup" style="background:#030712;border:1px solid #1e2737;border-radius:8px;color:#e2e8f0;padding:9px 12px;font-size:13px;width:100%;">',
'<input type="url" id="ap_url" placeholder="https://platform.com/signup" style="background:#030712;border:1px solid #1e2737;border-radius: var(--radius-md);color:#e2e8f0;padding:9px 12px;font-size:13px;width:100%;">',
'</div>',
'<div class="form-group">',
'<label>Login-URL</label>',
'<input type="url" id="ap_login" placeholder="https://platform.com/login" style="background:#030712;border:1px solid #1e2737;border-radius:8px;color:#e2e8f0;padding:9px 12px;font-size:13px;width:100%;">',
'<input type="url" id="ap_login" placeholder="https://platform.com/login" style="background:#030712;border:1px solid #1e2737;border-radius: var(--radius-md);color:#e2e8f0;padding:9px 12px;font-size:13px;width:100%;">',
'</div>',
'<div class="form-group">',
'<label>Teckenlängd max</label>',
'<input type="number" id="ap_charlimit" placeholder="500" style="background:#030712;border:1px solid #1e2737;border-radius:8px;color:#e2e8f0;padding:9px 12px;font-size:13px;width:100%;">',
'<input type="number" id="ap_charlimit" placeholder="500" style="background:#030712;border:1px solid #1e2737;border-radius: var(--radius-md);color:#e2e8f0;padding:9px 12px;font-size:13px;width:100%;">',
'</div>',
'</div>',
'<div class="form-group" style="margin-top:16px;">',
'<label>Bolag</label>',
'<div style="display:flex;flex-wrap:wrap;gap:8px;margin-top:6px;">']
.concat(companies.map(function(c) {
return '<label style="display:flex;align-items:center;gap:6px;cursor:pointer;padding:5px 10px;border:1px solid #1e2737;border-radius:8px;font-size:13px;color:#cbd5e1;">' +
return '<label style="display:flex;align-items:center;gap:6px;cursor:pointer;padding:5px 10px;border:1px solid #1e2737;border-radius: var(--radius-md);font-size:13px;color:#cbd5e1;">' +
'<input type="checkbox" id="ap_co_' + c + '" value="' + c + '" style="width:auto;cursor:pointer;"> ' + companyLabels[c] + '</label>';
}))
.concat(['</div></div>',
'<div class="form-group" style="margin-top:16px;">',
'<label>Anteckning / Särskilda krav</label>',
'<textarea id="ap_notes" rows="3" placeholder="t.ex. kräver företagsverifiering, manuell godkännande..." style="background:#030712;border:1px solid #1e2737;border-radius:8px;color:#e2e8f0;padding:9px 12px;font-size:13px;width:100%;resize:vertical;"></textarea>',
'<textarea id="ap_notes" rows="3" placeholder="t.ex. kräver företagsverifiering, manuell godkännande..." style="background:#030712;border:1px solid #1e2737;border-radius: var(--radius-md);color:#e2e8f0;padding:9px 12px;font-size:13px;width:100%;resize:vertical;"></textarea>',
'</div>',
'<div id="ap_confirm" style="display:none;background:#0f2818;border:1px solid #166534;border-radius:8px;padding:10px 14px;margin-top:12px;color:#86efac;font-size:13px;font-weight:600;">',
' Plattform tillagd!',
'<div id="ap_confirm" style="display:none;background:#0f2818;border:1px solid #166534;border-radius: var(--radius-md);padding:10px 14px;margin-top:12px;color:#86efac;font-size:13px;font-weight:600;">',
'<svg width="20" height="20" viewBox="0 0 20 20" fill="none"><circle cx="10" cy="10" r="9" stroke="#22c55e" stroke-width="2"/><path d="M6 10L9 13L14 7" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Plattform tillagd!',
'</div>',
'<div style="display:flex;gap:12px;justify-content:flex-end;margin-top:24px;">',
'<button class="btn btn-secondary" onclick="closeAddPlatformModal()">Avbryt</button>',
@@ -1778,7 +1778,7 @@ function closeAddPlatformModal() {
async function saveAddPlatform() {
var name = (document.getElementById('ap_name') ? document.getElementById('ap_name').value : '').trim();
var icon = (document.getElementById('ap_icon') ? document.getElementById('ap_icon').value : '').trim() || '🌐';
var icon = (document.getElementById('ap_icon') ? document.getElementById('ap_icon').value : '').trim() || '';
var url = (document.getElementById('ap_url') ? document.getElementById('ap_url').value : '').trim();
var login = (document.getElementById('ap_login') ? document.getElementById('ap_login').value : '').trim();
var charLimit = parseInt(document.getElementById('ap_charlimit') ? document.getElementById('ap_charlimit').value : '0') || null;
@@ -1822,7 +1822,7 @@ async function saveAddPlatform() {
existing.push(platformData);
localStorage.setItem('argus_custom_platforms', JSON.stringify(existing));
saved = true;
} catch(e) { toast(' Kunde inte spara: ' + e.message, 'error'); return; }
} catch(e) { toast('<svg width="20" height="20" viewBox="0 0 20 20" fill="none"><circle cx="10" cy="10" r="9" stroke="#ef4444" stroke-width="2"/><path d="M7 7L13 13M13 7L7 13" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Kunde inte spara: ' + e.message, 'error'); return; }
}
// Update in-memory platform list
@@ -1839,7 +1839,7 @@ async function saveAddPlatform() {
// Show confirmation
var confirmEl = document.getElementById('ap_confirm');
if (confirmEl) confirmEl.style.display = '';
toast(' Plattform "' + name + '" tillagd!', 'success');
toast('<svg width="20" height="20" viewBox="0 0 20 20" fill="none"><circle cx="10" cy="10" r="9" stroke="#22c55e" stroke-width="2"/><path d="M6 10L9 13L14 7" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Plattform "' + name + '" tillagd!', 'success');
setTimeout(function() { closeAddPlatformModal(); }, 1800);
}
+12 -12
View File
@@ -14,35 +14,35 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
.nav a { color: #6c757d; text-decoration: none; padding: 12px 20px; font-size: 0.85rem; transition: all 0.2s; border-bottom: 2px solid transparent; }
.nav a:hover, .nav a.active { color: #0066cc; border-bottom-color: #0066cc; }
.main { padding: 40px; max-width: 1400px; margin: 0 auto; }
.philosophy { background: #fff; border-radius: 12px; padding: 32px; margin-bottom: 32px; border-left: 4px solid #0066cc; }
.philosophy { background: #fff; border-radius: var(--radius-md); padding: 32px; margin-bottom: 32px; border-left: 4px solid #0066cc; }
.philosophy h2 { font-size: 1.25rem; margin-bottom: 16px; color: #1a1a1a; }
.philosophy p { color: #495057; margin-bottom: 12px; }
.philosophy ul { margin-left: 20px; color: #495057; }
.philosophy li { margin-bottom: 8px; }
.stats-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 20px; margin-bottom: 32px; }
.stat-card { background: #fff; border-radius: 12px; padding: 24px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
.stat-card { background: #fff; border-radius: var(--radius-md); padding: 24px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
.stat-card h3 { font-size: 0.75rem; color: #6c757d; margin-bottom: 8px; text-transform: uppercase; letter-spacing: 0.5px; }
.stat-card .value { font-size: 2rem; font-weight: 700; color: #1a1a1a; }
.stat-card .change { font-size: 0.8rem; margin-top: 4px; }
.stat-card .change.positive { color: #28a745; }
.section { background: #fff; border-radius: 12px; padding: 24px; margin-bottom: 24px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
.section { background: #fff; border-radius: var(--radius-md); padding: 24px; margin-bottom: 24px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
.section h2 { font-size: 1.1rem; margin-bottom: 16px; color: #1a1a1a; }
.project-card { border: 1px solid #e9ecef; border-radius: 8px; padding: 16px; margin-bottom: 12px; }
.project-card { border: 1px solid #e9ecef; border-radius: var(--radius-md); padding: 16px; margin-bottom: 12px; }
.project-card h3 { font-size: 1rem; margin-bottom: 8px; }
.project-card .meta { font-size: 0.8rem; color: #6c757d; margin-bottom: 8px; }
.project-card .findings { font-size: 0.85rem; color: #495057; }
.project-card .findings li { margin-bottom: 4px; }
.score { display: inline-block; padding: 4px 12px; border-radius: 4px; font-size: 0.75rem; font-weight: 600; }
.score { display: inline-block; padding: 4px 12px; border-radius: var(--radius-sm); font-size: 0.75rem; font-weight: 600; }
.score.high { background: #d4edda; color: #155724; }
.score.medium { background: #fff3cd; color: #856404; }
.score.low { background: #f8d7da; color: #721c24; }
.discrepancy { background: #fff3cd; border: 1px solid #ffeaa7; border-radius: 8px; padding: 16px; margin-bottom: 12px; }
.discrepancy { background: #fff3cd; border: 1px solid #ffeaa7; border-radius: var(--radius-md); padding: 16px; margin-bottom: 12px; }
.discrepancy h4 { color: #856404; margin-bottom: 8px; }
.discrepancy p { font-size: 0.85rem; color: #495057; }
.positive { background: #d4edda; border: 1px solid #c3e6cb; border-radius: 8px; padding: 16px; margin-bottom: 12px; }
.positive { background: #d4edda; border: 1px solid #c3e6cb; border-radius: var(--radius-md); padding: 16px; margin-bottom: 12px; }
.positive h4 { color: #155724; margin-bottom: 8px; }
.framework-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 16px; }
.framework-card { border: 1px solid #e9ecef; border-radius: 8px; padding: 16px; }
.framework-card { border: 1px solid #e9ecef; border-radius: var(--radius-md); padding: 16px; }
.framework-card h4 { font-size: 0.95rem; margin-bottom: 8px; }
.framework-card p { font-size: 0.8rem; color: #6c757d; }
</style>
@@ -157,7 +157,7 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
<p style="color: #6c757d; margin-bottom: 16px;">These findings indicate areas where additional verification may be valuable. They are not conclusions of wrongdoing.</p>
<div class="discrepancy">
<h4>⚠️ Schedule Variance - Mogadishu Primary School</h4>
<h4><svg width="20" height="20" viewBox="0 0 20 20" fill="none"><path d="M10 3L17 17H3L10 3Z" stroke="#f59e0b" stroke-width="2"/><path d="M10 8V12M10 14V14.5" stroke="#f59e0b" stroke-width="2" stroke-linecap="round"/></svg> Schedule Variance - Mogadishu Primary School</h4>
<p><strong>Reported:</strong> 95% complete (June 2026)<br>
<strong>Observed:</strong> 85% complete (July 2026)<br>
<strong>Evidence:</strong> Field observation, Satellite, AI analysis<br>
@@ -166,7 +166,7 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
</div>
<div class="discrepancy">
<h4>⚠️ Activity Gap - Lagos Road Rehabilitation</h4>
<h4><svg width="20" height="20" viewBox="0 0 20 20" fill="none"><path d="M10 3L17 17H3L10 3Z" stroke="#f59e0b" stroke-width="2"/><path d="M10 8V12M10 14V14.5" stroke="#f59e0b" stroke-width="2" stroke-linecap="round"/></svg> Activity Gap - Lagos Road Rehabilitation</h4>
<p><strong>Reported:</strong> Active construction<br>
<strong>Observed:</strong> No activity for 90 days, equipment removed<br>
<strong>Evidence:</strong> Field observation, Satellite time-series<br>
@@ -180,14 +180,14 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
<p style="color: #6c757d; margin-bottom: 16px;">Projects demonstrating strong implementation and sustained operation. These enable benchmarking and dissemination of successful practices.</p>
<div class="positive">
<h4> Accra Solar Microgrid - Ghana</h4>
<h4><svg width="20" height="20" viewBox="0 0 20 20" fill="none"><circle cx="10" cy="10" r="9" stroke="#22c55e" stroke-width="2"/><path d="M6 10L9 13L14 7" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Accra Solar Microgrid - Ghana</h4>
<p><strong>SII Score:</strong> 89/100 | <strong>Confidence:</strong> 92%<br>
<strong>Strengths:</strong> Strong implementation, Sustained operation (24+ months), High community utilization, Long-term maintenance observed, Positive environmental outcomes<br>
<strong>Benchmark Category:</strong> Energy Infrastructure</p>
</div>
<div class="positive">
<h4> Nairobi Digital Health Records - Kenya</h4>
<h4><svg width="20" height="20" viewBox="0 0 20 20" fill="none"><circle cx="10" cy="10" r="9" stroke="#22c55e" stroke-width="2"/><path d="M6 10L9 13L14 7" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Nairobi Digital Health Records - Kenya</h4>
<p><strong>SII Score:</strong> 84/100 | <strong>Confidence:</strong> 86%<br>
<strong>Strengths:</strong> Operational continuity, Staff training evident, Patient utilization increasing, Data transparency<br>
<strong>Benchmark Category:</strong> Digital Health</p>
+8 -8
View File
@@ -15,18 +15,18 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
.nav a:hover, .nav a.active { color: #fff; background: #222; }
.main { padding: 40px; }
.stats-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 20px; margin-bottom: 40px; }
.stat-card { background: #111; border: 1px solid #333; border-radius: 12px; padding: 24px; }
.stat-card { background: #111; border: 1px solid #333; border-radius: var(--radius-md); padding: 24px; }
.stat-card h3 { font-size: 0.75rem; color: #666; margin-bottom: 8px; text-transform: uppercase; }
.stat-card .value { font-size: 2rem; font-weight: 700; color: #fff; }
.stat-card .change { font-size: 0.8rem; color: #34c759; margin-top: 4px; }
.section { background: #111; border: 1px solid #333; border-radius: 12px; padding: 24px; margin-bottom: 24px; }
.section { background: #111; border: 1px solid #333; border-radius: var(--radius-md); padding: 24px; margin-bottom: 24px; }
.section h2 { font-size: 1.1rem; margin-bottom: 16px; color: #fff; }
.map-placeholder { background: #1a1a1a; border-radius: 8px; height: 400px; display: flex; align-items: center; justify-content: center; color: #666; }
.map-placeholder { background: #1a1a1a; border-radius: var(--radius-md); height: 400px; display: flex; align-items: center; justify-content: center; color: #666; }
table { width: 100%; border-collapse: collapse; font-size: 0.85rem; }
th, td { text-align: left; padding: 12px; border-bottom: 1px solid #333; }
th { color: #666; font-size: 0.75rem; text-transform: uppercase; }
tr:hover { background: #1a1a1a; }
.status { display: inline-block; padding: 4px 12px; border-radius: 4px; font-size: 0.75rem; font-weight: 600; }
.status { display: inline-block; padding: 4px 12px; border-radius: var(--radius-sm); font-size: 0.75rem; font-weight: 600; }
.status.active { background: #1a3a1a; color: #34c759; }
.status.completed { background: #1a1a3a; color: #5ac8fa; }
.status.delayed { background: #3a1a1a; color: #ff3b30; }
@@ -34,11 +34,11 @@ tr:hover { background: #1a1a1a; }
.score.high { color: #34c759; }
.score.medium { color: #ff9500; }
.score.low { color: #ff3b30; }
.alert { background: #3a1a1a; border: 1px solid #ff3b30; border-radius: 8px; padding: 16px; margin-bottom: 12px; }
.alert { background: #3a1a1a; border: 1px solid #ff3b30; border-radius: var(--radius-md); padding: 16px; margin-bottom: 12px; }
.alert h4 { color: #ff3b30; margin-bottom: 8px; }
.alert p { color: #ccc; font-size: 0.85rem; }
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; }
.country-card { background: #1a1a1a; border-radius: 8px; padding: 16px; display: flex; justify-content: space-between; align-items: center; }
.country-card { background: #1a1a1a; border-radius: var(--radius-md); padding: 16px; display: flex; justify-content: space-between; align-items: center; }
.country-card h4 { font-size: 1rem; }
.country-card .score { font-size: 1.5rem; }
</style>
@@ -112,11 +112,11 @@ Interactive Map<br>
<div class="section">
<h2>Risk Alerts</h2>
<div class="alert">
<h4>⚠️ Abandoned Project Detected</h4>
<h4><svg width="20" height="20" viewBox="0 0 20 20" fill="none"><path d="M10 3L17 17H3L10 3Z" stroke="#f59e0b" stroke-width="2"/><path d="M10 8V12M10 14V14.5" stroke="#f59e0b" stroke-width="2" stroke-linecap="round"/></svg> Abandoned Project Detected</h4>
<p><strong>Lagos Road Rehabilitation</strong> - No activity for 90 days. Equipment removed.</p>
</div>
<div class="alert" style="background: #3a3a1a; border-color: #ff9500;">
<h4 style="color: #ff9500;">⚠️ Infrastructure Deterioration</h4>
<h4 style="color: #ff9500;"><svg width="20" height="20" viewBox="0 0 20 20" fill="none"><path d="M10 3L17 17H3L10 3Z" stroke="#f59e0b" stroke-width="2"/><path d="M10 8V12M10 14V14.5" stroke="#f59e0b" stroke-width="2" stroke-linecap="round"/></svg> Infrastructure Deterioration</h4>
<p><strong>Nairobi Health Clinic</strong> - Accelerated deterioration observed.</p>
</div>
</div>
+8 -8
View File
@@ -10,22 +10,22 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
.sidebar { width: 260px; background: #0a0a0a; color: #fff; position: fixed; height: 100vh; padding: 24px; overflow-y: auto; }
.sidebar h1 { font-size: 1.25rem; margin-bottom: 8px; }
.sidebar .subtitle { font-size: 0.75rem; color: #666; margin-bottom: 32px; }
.sidebar nav a { display: block; color: #999; text-decoration: none; padding: 10px 16px; border-radius: 8px; margin-bottom: 2px; transition: all 0.2s; font-size: 0.875rem; cursor: pointer; }
.sidebar nav a { display: block; color: #999; text-decoration: none; padding: 10px 16px; border-radius: var(--radius-md); margin-bottom: 2px; transition: all 0.2s; font-size: 0.875rem; cursor: pointer; }
.sidebar nav a:hover, .sidebar nav a.active { color: #fff; background: rgba(255,255,255,0.1); }
.sidebar nav .section { color: #666; font-size: 0.7rem; text-transform: uppercase; letter-spacing: 1px; margin: 16px 0 8px 16px; }
.main { margin-left: 260px; padding: 32px; }
.header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 32px; }
.header h2 { font-size: 1.75rem; }
.stats-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 16px; margin-bottom: 32px; }
.stat-card { background: #fff; border-radius: 12px; padding: 20px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); cursor: pointer; transition: transform 0.2s; }
.stat-card { background: #fff; border-radius: var(--radius-md); padding: 20px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); cursor: pointer; transition: transform 0.2s; }
.stat-card:hover { transform: translateY(-2px); }
.stat-card h3 { font-size: 0.75rem; color: #666; margin-bottom: 8px; text-transform: uppercase; letter-spacing: 0.5px; }
.stat-card .value { font-size: 1.75rem; font-weight: 700; }
.stat-card .change { font-size: 0.8rem; margin-top: 4px; }
.stat-card .change.positive { color: #34c759; }
.section { background: #fff; border-radius: 12px; padding: 24px; margin-bottom: 24px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
.section { background: #fff; border-radius: var(--radius-md); padding: 24px; margin-bottom: 24px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
.section h3 { font-size: 1.1rem; margin-bottom: 16px; display: flex; align-items: center; gap: 8px; }
.status-dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; }
.status-dot { width: 8px; height: 8px; border-radius: var(--radius-full); display: inline-block; }
.status-dot.green { background: #34c759; }
.status-dot.yellow { background: #ff9500; }
.status-dot.red { background: #ff3b30; }
@@ -33,20 +33,20 @@ table { width: 100%; border-collapse: collapse; font-size: 0.875rem; }
th, td { text-align: left; padding: 10px 12px; border-bottom: 1px solid #eee; }
th { font-weight: 600; color: #666; font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.5px; }
tr:hover { background: #f9f9f9; cursor: pointer; }
.status { display: inline-block; padding: 3px 10px; border-radius: 4px; font-size: 0.7rem; font-weight: 600; }
.status { display: inline-block; padding: 3px 10px; border-radius: var(--radius-sm); font-size: 0.7rem; font-weight: 600; }
.status.published { background: #e8f5e9; color: #2e7d32; }
.status.active { background: #e3f2fd; color: #1565c0; }
.btn { display: inline-block; padding: 8px 16px; border-radius: 6px; border: none; cursor: pointer; font-size: 0.8rem; font-weight: 600; }
.btn { display: inline-block; padding: 8px 16px; border-radius: var(--radius-sm); border: none; cursor: pointer; font-size: 0.8rem; font-weight: 600; }
.btn-primary { background: #0066cc; color: #fff; }
.btn-secondary { background: #f5f5f5; color: #333; }
.actions { display: flex; gap: 8px; }
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; }
.module-card { border: 1px solid #eee; border-radius: 8px; padding: 16px; cursor: pointer; transition: all 0.2s; }
.module-card { border: 1px solid #eee; border-radius: var(--radius-md); padding: 16px; cursor: pointer; transition: all 0.2s; }
.module-card:hover { border-color: #0066cc; box-shadow: 0 2px 8px rgba(0,102,204,0.1); }
.module-card h4 { font-size: 0.9rem; margin-bottom: 8px; display: flex; align-items: center; gap: 8px; }
.module-card p { font-size: 0.8rem; color: #666; }
.loading { text-align: center; padding: 40px; color: #666; }
.error { background: #ffebee; color: #c62828; padding: 16px; border-radius: 8px; margin-bottom: 16px; }
.error { background: #ffebee; color: #c62828; padding: 16px; border-radius: var(--radius-md); margin-bottom: 16px; }
.hidden { display: none; }
</style>
</head>
+14 -14
View File
@@ -10,21 +10,21 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
.header { background: #0a0a0a; border-bottom: 1px solid #333; padding: 20px 40px; display: flex; justify-content: space-between; align-items: center; }
.header h1 { font-size: 1.5rem; color: #00d4ff; }
.status { display: flex; gap: 12px; }
.status-item { background: #1a3a1a; color: #4ade80; padding: 4px 12px; border-radius: 4px; font-size: 0.8rem; }
.status-item { background: #1a3a1a; color: #4ade80; padding: 4px 12px; border-radius: var(--radius-sm); font-size: 0.8rem; }
.main { padding: 40px; max-width: 1400px; margin: 0 auto; }
.metrics { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 16px; margin-bottom: 32px; }
.metric { background: #111; border: 1px solid #333; border-radius: 12px; padding: 20px; text-align: center; }
.metric { background: #111; border: 1px solid #333; border-radius: var(--radius-md); padding: 20px; text-align: center; }
.metric-value { font-size: 2rem; font-weight: 700; color: #00d4ff; }
.metric-label { font-size: 0.8rem; color: #888; margin-top: 4px; }
.agents { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 16px; margin-bottom: 32px; }
.agent { background: #111; border: 1px solid #333; border-radius: 12px; padding: 20px; }
.agent { background: #111; border: 1px solid #333; border-radius: var(--radius-md); padding: 20px; }
.agent-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; }
.agent-name { font-weight: 600; color: #fff; }
.agent-status { font-size: 0.75rem; padding: 2px 8px; border-radius: 4px; }
.agent-status { font-size: 0.75rem; padding: 2px 8px; border-radius: var(--radius-sm); }
.status-active { background: #1a3a1a; color: #4ade80; }
.agent-stats { font-size: 0.85rem; color: #888; }
.agent-stats div { margin: 4px 0; }
.log { background: #111; border: 1px solid #333; border-radius: 12px; padding: 20px; height: 400px; overflow-y: auto; }
.log { background: #111; border: 1px solid #333; border-radius: var(--radius-md); padding: 20px; height: 400px; overflow-y: auto; }
.log h2 { font-size: 1.1rem; margin-bottom: 12px; color: #00d4ff; }
.log-entry { font-family: monospace; font-size: 0.8rem; padding: 4px 0; border-bottom: 1px solid #222; }
.log-time { color: #666; }
@@ -39,9 +39,9 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
<h1>LIFE Agent Swarm</h1>
</div>
<div class="status">
<div class="status-item"> 6 Agents Active</div>
<div class="status-item"> 281 Nodes</div>
<div class="status-item"> 924 Edges</div>
<div class="status-item"> 6 Agents Active</div>
<div class="status-item"> 281 Nodes</div>
<div class="status-item"> 924 Edges</div>
</div>
</div>
@@ -77,7 +77,7 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
<div class="agents">
<div class="agent">
<div class="agent-header">
<div class="agent-name">🕷️ Crawler Agent</div>
<div class="agent-name"> Crawler Agent</div>
<div class="agent-status status-active">ACTIVE</div>
</div>
<div class="agent-stats">
@@ -89,7 +89,7 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
<div class="agent">
<div class="agent-header">
<div class="agent-name">👁️ Observation Agent</div>
<div class="agent-name"> Observation Agent</div>
<div class="agent-status status-active">ACTIVE</div>
</div>
<div class="agent-stats">
@@ -101,7 +101,7 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
<div class="agent">
<div class="agent-header">
<div class="agent-name"> Validation Agent</div>
<div class="agent-name"><svg width="20" height="20" viewBox="0 0 20 20" fill="none"><circle cx="10" cy="10" r="9" stroke="#22c55e" stroke-width="2"/><path d="M6 10L9 13L14 7" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Validation Agent</div>
<div class="agent-status status-active">ACTIVE</div>
</div>
<div class="agent-stats">
@@ -113,7 +113,7 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
<div class="agent">
<div class="agent-header">
<div class="agent-name">📈 Confidence Agent</div>
<div class="agent-name"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 12L6 8L9 11L14 5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M10 5H14V9" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Confidence Agent</div>
<div class="agent-status status-active">ACTIVE</div>
</div>
<div class="agent-stats">
@@ -125,7 +125,7 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
<div class="agent">
<div class="agent-header">
<div class="agent-name">🎯 Mission Agent</div>
<div class="agent-name"> Mission Agent</div>
<div class="agent-status status-active">ACTIVE</div>
</div>
<div class="agent-stats">
@@ -137,7 +137,7 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
<div class="agent">
<div class="agent-header">
<div class="agent-name">🧠 Learning Agent</div>
<div class="agent-name"> Learning Agent</div>
<div class="agent-status status-active">ACTIVE</div>
</div>
<div class="agent-stats">
+19 -19
View File
@@ -14,13 +14,13 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
.nav a { color: #666; text-decoration: none; padding: 12px 20px; font-size: 0.85rem; transition: all 0.2s; border-bottom: 2px solid transparent; }
.nav a:hover, .nav a.active { color: #00d4ff; border-bottom-color: #00d4ff; }
.main { padding: 40px; max-width: 1400px; margin: 0 auto; }
.hero { text-align: center; padding: 60px 20px; background: linear-gradient(180deg, #0a0a0a 0%, #1a1a2e 100%); border-radius: 16px; margin-bottom: 40px; }
.hero { text-align: center; padding: 60px 20px; background: linear-gradient(180deg, #0a0a0a 0%, #1a1a2e 100%); border-radius: var(--radius-lg); margin-bottom: 40px; }
.hero h2 { font-size: 2.5rem; margin-bottom: 16px; background: linear-gradient(90deg, #00d4ff, #7c3aed); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }
.hero p { font-size: 1.1rem; color: #aaa; max-width: 800px; margin: 0 auto; }
.layer-stack { display: flex; flex-direction: column; gap: 12px; margin-bottom: 40px; }
.layer { background: #111; border: 1px solid #333; border-radius: 12px; padding: 20px; display: flex; align-items: center; gap: 20px; transition: all 0.3s; }
.layer { background: #111; border: 1px solid #333; border-radius: var(--radius-md); padding: 20px; display: flex; align-items: center; gap: 20px; transition: all 0.3s; }
.layer:hover { border-color: #00d4ff; transform: translateX(8px); }
.layer-number { width: 40px; height: 40px; background: linear-gradient(135deg, #00d4ff, #7c3aed); border-radius: 8px; display: flex; align-items: center; justify-content: center; font-weight: 700; font-size: 1.1rem; }
.layer-number { width: 40px; height: 40px; background: linear-gradient(135deg, #00d4ff, #7c3aed); border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; font-weight: 700; font-size: 1.1rem; }
.layer-info { flex: 1; }
.layer-info h3 { font-size: 1.1rem; margin-bottom: 4px; }
.layer-info p { font-size: 0.85rem; color: #888; }
@@ -28,16 +28,16 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
.layer-meta .count { font-size: 1.25rem; font-weight: 700; color: #00d4ff; }
.layer-meta .label { font-size: 0.75rem; color: #666; }
.core-blocks { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; margin-bottom: 40px; }
.core-block { background: #111; border: 1px solid #333; border-radius: 12px; padding: 24px; text-align: center; }
.core-block { background: #111; border: 1px solid #333; border-radius: var(--radius-md); padding: 24px; text-align: center; }
.core-block .icon { font-size: 2rem; margin-bottom: 12px; }
.core-block h3 { font-size: 1rem; margin-bottom: 8px; }
.core-block p { font-size: 0.85rem; color: #888; }
.stats-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 16px; margin-bottom: 40px; }
.stat-card { background: #111; border: 1px solid #333; border-radius: 12px; padding: 20px; text-align: center; }
.stat-card { background: #111; border: 1px solid #333; border-radius: var(--radius-md); padding: 20px; text-align: center; }
.stat-card .value { font-size: 1.75rem; font-weight: 700; color: #00d4ff; }
.stat-card .label { font-size: 0.75rem; color: #666; margin-top: 4px; }
.applications { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 12px; }
.app-card { background: #1a1a2e; border: 1px solid #333; border-radius: 8px; padding: 16px; font-size: 0.9rem; }
.app-card { background: #1a1a2e; border: 1px solid #333; border-radius: var(--radius-md); padding: 16px; font-size: 0.9rem; }
.app-card:hover { border-color: #7c3aed; }
</style>
</head>
@@ -180,19 +180,19 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
<div class="core-blocks" id="blocks">
<div class="core-block">
<div class="icon">🎯</div>
<div class="icon"></div>
<h3>Decision Engine</h3>
<p>Vad bör användaren göra nu? Omvandlar intelligens till konkreta rekommendationer</p>
</div>
<div class="core-block">
<div class="icon">🧠</div>
<div class="icon"></div>
<h3>Learning Engine</h3>
<p>Självutvärdering och kontinuerlig förbättring av modeller</p>
</div>
<div class="core-block">
<div class="icon">🔌</div>
<div class="icon"></div>
<h3>Integration Layer</h3>
<p>REST API, webhooks, streaming, SDK och enterprise-integrationer</p>
</div>
@@ -202,16 +202,16 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
<p style="color: #888; margin-bottom: 20px;">LIFE är en generell motor. Bistånd är en tillämpning, inte identitet.</p>
<div class="applications" id="apps">
<div class="app-card">🏗️ Infrastrukturhantering</div>
<div class="app-card">🏙️ Stadsplanering</div>
<div class="app-card">🌱 Miljöövervakning</div>
<div class="app-card">🛡️ Försäkringsrisk</div>
<div class="app-card">🏛️ Kommunal tillsyn</div>
<div class="app-card">🚨 Katastrofhantering</div>
<div class="app-card">🏢 Fastighetsförvaltning</div>
<div class="app-card">📦 Supply chain</div>
<div class="app-card">🔒 Säkerhetsanalys</div>
<div class="app-card">🌍 Biståndsövervakning</div>
<div class="app-card"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 14H14M4 14V8L8 4L12 8V14" stroke="#666" stroke-width="1.5" stroke-linejoin="round"/><path d="M6 14V10H10V14" stroke="#666" stroke-width="1.5"/></svg> Infrastrukturhantering</div>
<div class="app-card"> Stadsplanering</div>
<div class="app-card"> Miljöövervakning</div>
<div class="app-card"> Försäkringsrisk</div>
<div class="app-card"> Kommunal tillsyn</div>
<div class="app-card"> Katastrofhantering</div>
<div class="app-card"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="3" y="2" width="10" height="12" rx="1" stroke="#666" stroke-width="1.5"/><path d="M6 5H7M6 8H7M6 11H7M9 5H10M9 8H10M9 11H10" stroke="#666" stroke-width="1.5" stroke-linecap="round"/></svg> Fastighetsförvaltning</div>
<div class="app-card"> Supply chain</div>
<div class="app-card"> Säkerhetsanalys</div>
<div class="app-card"> Biståndsövervakning</div>
</div>
</div>
+9 -9
View File
@@ -14,30 +14,30 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
.nav a { color: #666; text-decoration: none; padding: 12px 20px; font-size: 0.85rem; transition: all 0.2s; border-bottom: 2px solid transparent; }
.nav a:hover, .nav a.active { color: #00d4ff; border-bottom-color: #00d4ff; }
.main { padding: 40px; max-width: 1400px; margin: 0 auto; }
.hero { text-align: center; padding: 60px 20px; background: linear-gradient(180deg, #0a0a0a 0%, #1a1a2e 100%); border-radius: 16px; margin-bottom: 40px; }
.hero { text-align: center; padding: 60px 20px; background: linear-gradient(180deg, #0a0a0a 0%, #1a1a2e 100%); border-radius: var(--radius-lg); margin-bottom: 40px; }
.hero h2 { font-size: 2rem; margin-bottom: 16px; background: linear-gradient(90deg, #00d4ff, #7c3aed); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }
.hero p { font-size: 1rem; color: #aaa; max-width: 800px; margin: 0 auto; }
.components { display: grid; grid-template-columns: repeat(auto-fill, minmax(350px, 1fr)); gap: 20px; margin-bottom: 40px; }
.component { background: #111; border: 1px solid #333; border-radius: 12px; padding: 24px; }
.component { background: #111; border: 1px solid #333; border-radius: var(--radius-md); padding: 24px; }
.component-number { font-size: 0.75rem; color: #00d4ff; text-transform: uppercase; letter-spacing: 1px; margin-bottom: 8px; }
.component h3 { font-size: 1.1rem; margin-bottom: 12px; }
.component p { color: #888; font-size: 0.85rem; margin-bottom: 12px; }
.component-items { list-style: none; }
.component-items li { color: #aaa; font-size: 0.8rem; padding: 4px 0; padding-left: 16px; position: relative; }
.component-items li::before { content: "→"; position: absolute; left: 0; color: #00d4ff; }
.separation { background: #111; border: 1px solid #333; border-radius: 12px; padding: 32px; margin-bottom: 40px; }
.separation { background: #111; border: 1px solid #333; border-radius: var(--radius-md); padding: 32px; margin-bottom: 40px; }
.separation h2 { font-size: 1.5rem; margin-bottom: 16px; color: #7c3aed; }
.separation p { color: #888; margin-bottom: 16px; }
.entities { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 16px; margin-top: 16px; }
.entity { background: #1a1a1a; border-radius: 8px; padding: 16px; }
.entity { background: #1a1a1a; border-radius: var(--radius-md); padding: 16px; }
.entity-name { color: #00d4ff; font-weight: 600; }
.entity-type { color: #888; font-size: 0.8rem; }
.entity-desc { color: #aaa; font-size: 0.75rem; margin-top: 8px; }
.product-story { background: #111; border: 1px solid #333; border-radius: 12px; padding: 32px; margin-bottom: 40px; text-align: center; }
.product-story { background: #111; border: 1px solid #333; border-radius: var(--radius-md); padding: 32px; margin-bottom: 40px; text-align: center; }
.product-story h2 { font-size: 1.5rem; margin-bottom: 16px; color: #00d4ff; }
.product-story p { font-size: 1.1rem; color: #aaa; max-width: 800px; margin: 0 auto; line-height: 1.8; }
.evolution { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 12px; margin-bottom: 40px; }
.evo { background: #111; border: 1px solid #333; border-radius: 8px; padding: 12px; text-align: center; }
.evo { background: #111; border: 1px solid #333; border-radius: var(--radius-md); padding: 12px; text-align: center; }
.evo-version { font-size: 1.1rem; font-weight: 700; color: #00d4ff; }
.evo-name { font-size: 0.75rem; color: #aaa; }
</style>
@@ -108,7 +108,7 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
<div class="components" id="components">
<div class="component">
<div class="component-number">Komponent 1</div>
<h3>📦 Reference Implementations</h3>
<h3> Reference Implementations</h3>
<p>Öppna referensimplementationer som sänker tröskeln för externa utvecklare</p>
<ul class="component-items">
<li>Reference Server (Python/FastAPI)</li>
@@ -121,7 +121,7 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
<div class="component">
<div class="component-number">Komponent 2</div>
<h3> Conformance Program</h3>
<h3><svg width="20" height="20" viewBox="0 0 20 20" fill="none"><circle cx="10" cy="10" r="9" stroke="#22c55e" stroke-width="2"/><path d="M6 10L9 13L14 7" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Conformance Program</h3>
<p>Certifieringsprogram som ger tydlig kvalitetsnivå för RIS-integrationer</p>
<ul class="component-items">
<li>RIS Compatible (gratis)</li>
@@ -134,7 +134,7 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
<div class="component">
<div class="component-number">Komponent 3</div>
<h3>📄 Public Specification</h3>
<h3> Public Specification</h3>
<p>Öppen specifikation som gör att andra kan implementera RIS oberoende</p>
<ul class="component-items">
<li>Terminologi och begrepp</li>
+9 -9
View File
@@ -14,22 +14,22 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
.nav a { color: #666; text-decoration: none; padding: 12px 20px; font-size: 0.85rem; transition: all 0.2s; border-bottom: 2px solid transparent; }
.nav a:hover, .nav a.active { color: #00d4ff; border-bottom-color: #00d4ff; }
.main { padding: 40px; max-width: 1400px; margin: 0 auto; }
.hero { text-align: center; padding: 60px 20px; background: linear-gradient(180deg, #0a0a0a 0%, #1a1a2e 100%); border-radius: 16px; margin-bottom: 40px; }
.hero { text-align: center; padding: 60px 20px; background: linear-gradient(180deg, #0a0a0a 0%, #1a1a2e 100%); border-radius: var(--radius-lg); margin-bottom: 40px; }
.hero h2 { font-size: 2rem; margin-bottom: 16px; background: linear-gradient(90deg, #00d4ff, #7c3aed); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }
.hero p { font-size: 1rem; color: #aaa; max-width: 800px; margin: 0 auto; }
.pillars { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 20px; margin-bottom: 40px; }
.pillar { background: #111; border: 1px solid #333; border-radius: 12px; padding: 24px; }
.pillar { background: #111; border: 1px solid #333; border-radius: var(--radius-md); padding: 24px; }
.pillar-number { font-size: 0.75rem; color: #00d4ff; text-transform: uppercase; letter-spacing: 1px; margin-bottom: 8px; }
.pillar h3 { font-size: 1.1rem; margin-bottom: 12px; }
.pillar p { color: #888; font-size: 0.85rem; margin-bottom: 12px; }
.pillar-features { list-style: none; }
.pillar-features li { color: #aaa; font-size: 0.8rem; padding: 4px 0; padding-left: 16px; position: relative; }
.pillar-features li::before { content: "→"; position: absolute; left: 0; color: #00d4ff; }
.product-def { background: #111; border: 1px solid #333; border-radius: 12px; padding: 32px; margin-bottom: 40px; text-align: center; }
.product-def { background: #111; border: 1px solid #333; border-radius: var(--radius-md); padding: 32px; margin-bottom: 40px; text-align: center; }
.product-def h2 { font-size: 1.5rem; margin-bottom: 16px; color: #00d4ff; }
.product-def p { font-size: 1.1rem; color: #aaa; max-width: 700px; margin: 0 auto; line-height: 1.8; }
.stats { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 16px; margin-bottom: 40px; }
.stat { background: #111; border: 1px solid #333; border-radius: 8px; padding: 16px; text-align: center; }
.stat { background: #111; border: 1px solid #333; border-radius: var(--radius-md); padding: 16px; text-align: center; }
.stat-value { font-size: 1.5rem; font-weight: 700; color: #00d4ff; }
.stat-label { font-size: 0.75rem; color: #666; margin-top: 4px; }
</style>
@@ -85,7 +85,7 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
<div class="pillars" id="pillars">
<div class="pillar">
<div class="pillar-number">Pillar 1</div>
<h3>🔐 Trust Layer</h3>
<h3><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="3" y="7" width="10" height="7" rx="1" stroke="#666" stroke-width="1.5"/><path d="M5 7V5C5 3.3 6.3 2 8 2C9.7 2 11 3.3 11 5V7" stroke="#666" stroke-width="1.5"/><circle cx="8" cy="10.5" r="1" fill="#666"/></svg> Trust Layer</h3>
<p>Varje insikt ska kunna besvara tillitsfrågor</p>
<ul class="pillar-features">
<li>Vilka källor ligger bakom?</li>
@@ -98,7 +98,7 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
<div class="pillar">
<div class="pillar-number">Pillar 2</div>
<h3>📋 Governance Layer</h3>
<h3><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="3" y="2" width="10" height="12" rx="1" stroke="#666" stroke-width="1.5"/><path d="M6 5H10M6 8H10M6 11H8" stroke="#666" stroke-width="1.5" stroke-linecap="round"/></svg> Governance Layer</h3>
<p>Tydliga regler för modeller, data och beslut</p>
<ul class="pillar-features">
<li>Versionshantering av AI-modeller</li>
@@ -111,7 +111,7 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
<div class="pillar">
<div class="pillar-number">Pillar 3</div>
<h3>🔮 Simulation Layer</h3>
<h3> Simulation Layer</h3>
<p>Simulera scenarier, inte bara beskriva nuläge</p>
<ul class="pillar-features">
<li>Vad händer om en väg stängs?</li>
@@ -124,7 +124,7 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
<div class="pillar">
<div class="pillar-number">Pillar 4</div>
<h3>👥 Collaboration Layer</h3>
<h3> Collaboration Layer</h3>
<p>Flera aktörer arbetar i samma informationsmodell</p>
<ul class="pillar-features">
<li>Kommentera observationer</li>
@@ -137,7 +137,7 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
<div class="pillar">
<div class="pillar-number">Pillar 5</div>
<h3>🔌 Reality API</h3>
<h3> Reality API</h3>
<p>Semantiskt API för domänorienterad åtkomst</p>
<ul class="pillar-features">
<li>/objects - Fysiska objekt</li>
+6 -6
View File
@@ -12,7 +12,7 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
.header .subtitle { color: #666; font-size: 0.85rem; }
.main { padding: 40px; max-width: 1200px; margin: 0 auto; }
.pyramid { display: flex; flex-direction: column; gap: 16px; }
.level { border-radius: 12px; padding: 24px; position: relative; }
.level { border-radius: var(--radius-md); padding: 24px; position: relative; }
.level-mission { background: linear-gradient(135deg, #7c3aed, #00d4ff); }
.level-customer { background: #1a1a2e; border: 2px solid #7c3aed; }
.level-platform { background: #1a1a2e; border: 2px solid #00d4ff; }
@@ -21,15 +21,15 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
.level-label { font-size: 0.75rem; text-transform: uppercase; letter-spacing: 2px; margin-bottom: 8px; opacity: 0.8; }
.level h2 { font-size: 1.3rem; margin-bottom: 8px; }
.level p { font-size: 0.9rem; opacity: 0.9; }
.metric-highlight { display: inline-block; background: rgba(255,255,255,0.1); padding: 8px 16px; border-radius: 8px; margin-top: 12px; font-size: 1.1rem; font-weight: 600; }
.metric-highlight { display: inline-block; background: rgba(255,255,255,0.1); padding: 8px 16px; border-radius: var(--radius-md); margin-top: 12px; font-size: 1.1rem; font-weight: 600; }
.metrics-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 12px; margin-top: 16px; }
.metric-card { background: rgba(0,0,0,0.3); border-radius: 8px; padding: 12px; }
.metric-card { background: rgba(0,0,0,0.3); border-radius: var(--radius-md); padding: 12px; }
.metric-name { font-size: 0.8rem; opacity: 0.8; }
.metric-value { font-size: 1.5rem; font-weight: 700; margin-top: 4px; }
.metric-target { font-size: 0.75rem; opacity: 0.6; }
.rl-highlight { background: #111; border: 2px solid #fbbf24; border-radius: 12px; padding: 24px; margin: 24px 0; }
.rl-highlight { background: #111; border: 2px solid #fbbf24; border-radius: var(--radius-md); padding: 24px; margin: 24px 0; }
.rl-highlight h2 { color: #fbbf24; margin-bottom: 12px; }
.event-log { background: #111; border-radius: 8px; padding: 16px; margin-top: 16px; }
.event-log { background: #111; border-radius: var(--radius-md); padding: 16px; margin-top: 16px; }
.event-item { display: flex; justify-content: space-between; padding: 8px 0; border-bottom: 1px solid #222; }
.event-item:last-child { border-bottom: none; }
</style>
@@ -114,7 +114,7 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
<!-- Reality Latency -->
<div class="rl-highlight">
<h2> Reality Latency (RL)</h2>
<h2><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M9 2L5 9H8L7 14L11 7H8L9 2Z" fill="#f59e0b"/></svg> Reality Latency (RL)</h2>
<p><strong>Signature Metric:</strong> Time between reality changing and LIFE knowing about it.</p>
<p>Current Average: <strong id="rl-value">Loading...</strong> hours | Target: 24h</p>
<div class="event-log" id="event-log">
+16 -16
View File
@@ -14,10 +14,10 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
.nav a { color: #666; text-decoration: none; padding: 12px 20px; font-size: 0.85rem; transition: all 0.2s; border-bottom: 2px solid transparent; }
.nav a:hover, .nav a.active { color: #00d4ff; border-bottom-color: #00d4ff; }
.main { padding: 40px; max-width: 1400px; margin: 0 auto; }
.hero { text-align: center; padding: 60px 20px; background: linear-gradient(180deg, #0a0a0a 0%, #1a1a2e 100%); border-radius: 16px; margin-bottom: 40px; }
.hero { text-align: center; padding: 60px 20px; background: linear-gradient(180deg, #0a0a0a 0%, #1a1a2e 100%); border-radius: var(--radius-lg); margin-bottom: 40px; }
.hero h2 { font-size: 2rem; margin-bottom: 16px; background: linear-gradient(90deg, #00d4ff, #7c3aed); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }
.hero p { font-size: 1rem; color: #aaa; max-width: 800px; margin: 0 auto; }
.north-star { background: #111; border: 2px solid #00d4ff; border-radius: 12px; padding: 32px; margin-bottom: 40px; text-align: center; }
.north-star { background: #111; border: 2px solid #00d4ff; border-radius: var(--radius-md); padding: 32px; margin-bottom: 40px; text-align: center; }
.north-star h2 { font-size: 1.5rem; margin-bottom: 16px; color: #00d4ff; }
.north-star .metric-value { font-size: 3rem; font-weight: 700; color: #00d4ff; }
.north-star .metric-label { font-size: 0.9rem; color: #888; margin-bottom: 16px; }
@@ -26,24 +26,24 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
.target-year { font-size: 0.8rem; color: #666; }
.target-value { font-size: 1.2rem; font-weight: 600; }
.dimensions { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 20px; margin-bottom: 40px; }
.dimension { background: #111; border: 1px solid #333; border-radius: 12px; padding: 24px; }
.dimension { background: #111; border: 1px solid #333; border-radius: var(--radius-md); padding: 24px; }
.dimension h3 { font-size: 1rem; margin-bottom: 8px; color: #00d4ff; }
.dimension .current { font-size: 2rem; font-weight: 700; color: #fff; }
.dimension .target { font-size: 0.85rem; color: #888; margin-top: 4px; }
.framework { background: #111; border: 1px solid #333; border-radius: 12px; padding: 32px; margin-bottom: 40px; }
.framework { background: #111; border: 1px solid #333; border-radius: var(--radius-md); padding: 32px; margin-bottom: 40px; }
.framework h2 { font-size: 1.5rem; margin-bottom: 16px; color: #7c3aed; }
.framework p { color: #888; margin-bottom: 16px; }
.categories { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 16px; }
.category { background: #1a1a1a; border-radius: 8px; padding: 16px; }
.category { background: #1a1a1a; border-radius: var(--radius-md); padding: 16px; }
.category-name { color: #00d4ff; font-weight: 600; }
.category-desc { color: #888; font-size: 0.8rem; margin-top: 4px; }
.category-example { color: #aaa; font-size: 0.75rem; margin-top: 8px; font-style: italic; }
.roadmap { background: #111; border: 1px solid #333; border-radius: 12px; padding: 32px; margin-bottom: 40px; }
.roadmap { background: #111; border: 1px solid #333; border-radius: var(--radius-md); padding: 32px; margin-bottom: 40px; }
.roadmap h2 { font-size: 1.5rem; margin-bottom: 16px; color: #00d4ff; }
.phase { margin-bottom: 20px; padding: 16px; background: #1a1a1a; border-radius: 8px; }
.phase { margin-bottom: 20px; padding: 16px; background: #1a1a1a; border-radius: var(--radius-md); }
.phase-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; }
.phase-name { font-weight: 600; color: #fff; }
.phase-priority { font-size: 0.75rem; padding: 2px 8px; border-radius: 4px; }
.phase-priority { font-size: 0.75rem; padding: 2px 8px; border-radius: var(--radius-sm); }
.priority-critical { background: #7c3aed; }
.priority-high { background: #00d4ff; color: #000; }
.priority-medium { background: #333; }
@@ -97,27 +97,27 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
<div class="dimensions">
<div class="dimension">
<h3>📊 Object Coverage</h3>
<h3> Object Coverage</h3>
<div class="current">78%</div>
<div class="target">Target: 95%</div>
</div>
<div class="dimension">
<h3>🔍 Multi-source Verification</h3>
<h3><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><circle cx="7" cy="7" r="5" stroke="#666" stroke-width="2"/><path d="M11 11L14 14" stroke="#666" stroke-width="2" stroke-linecap="round"/></svg> Multi-source Verification</h3>
<div class="current">34%</div>
<div class="target">Target: 60%</div>
</div>
<div class="dimension">
<h3> Evidence Freshness</h3>
<h3>⏱ Evidence Freshness</h3>
<div class="current">14.2d</div>
<div class="target">Target: 7d</div>
</div>
<div class="dimension">
<h3>🎯 Average Confidence</h3>
<h3> Average Confidence</h3>
<div class="current">68</div>
<div class="target">Target: 80</div>
</div>
<div class="dimension">
<h3>🔗 Traceability</h3>
<h3> Traceability</h3>
<div class="current">92%</div>
<div class="target">Target: 98%</div>
</div>
@@ -129,17 +129,17 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
<div class="categories">
<div class="category">
<div class="category-name">🎯 Vision</div>
<div class="category-name"> Vision</div>
<div class="category-desc">What we aim to achieve long-term</div>
<div class="category-example">"LIFE aims to establish an open framework for Reality Intelligence."</div>
</div>
<div class="category">
<div class="category-name">🏗️ Architecture</div>
<div class="category-name"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 14H14M4 14V8L8 4L12 8V14" stroke="#666" stroke-width="1.5" stroke-linejoin="round"/><path d="M6 14V10H10V14" stroke="#666" stroke-width="1.5"/></svg> Architecture</div>
<div class="category-desc">How the system is designed</div>
<div class="category-example">"RIS defines objects, evidence, confidence, and interoperability."</div>
</div>
<div class="category">
<div class="category-name"> Validated Results</div>
<div class="category-name"><svg width="20" height="20" viewBox="0 0 20 20" fill="none"><circle cx="10" cy="10" r="9" stroke="#22c55e" stroke-width="2"/><path d="M6 10L9 13L14 7" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Validated Results</div>
<div class="category-desc">What we have actually demonstrated</div>
<div class="category-example">"In Pilot X, Y changes identified with Z% verification rate."</div>
</div>
+18 -18
View File
@@ -14,34 +14,34 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
.nav a { color: #666; text-decoration: none; padding: 12px 20px; font-size: 0.85rem; transition: all 0.2s; border-bottom: 2px solid transparent; }
.nav a:hover, .nav a.active { color: #00d4ff; border-bottom-color: #00d4ff; }
.main { padding: 40px; max-width: 1400px; margin: 0 auto; }
.hero { text-align: center; padding: 60px 20px; background: linear-gradient(180deg, #0a0a0a 0%, #1a1a2e 100%); border-radius: 16px; margin-bottom: 40px; }
.hero { text-align: center; padding: 60px 20px; background: linear-gradient(180deg, #0a0a0a 0%, #1a1a2e 100%); border-radius: var(--radius-lg); margin-bottom: 40px; }
.hero h2 { font-size: 2.5rem; margin-bottom: 16px; background: linear-gradient(90deg, #00d4ff, #7c3aed); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }
.hero p { font-size: 1.1rem; color: #aaa; max-width: 800px; margin: 0 auto; }
.platform-stack { display: flex; flex-direction: column; gap: 16px; margin-bottom: 40px; }
.level { background: #111; border: 1px solid #333; border-radius: 12px; padding: 24px; }
.level { background: #111; border: 1px solid #333; border-radius: var(--radius-md); padding: 24px; }
.level-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px; }
.level-number { font-size: 0.75rem; color: #00d4ff; text-transform: uppercase; letter-spacing: 1px; }
.level h3 { font-size: 1.25rem; }
.level p { color: #888; font-size: 0.9rem; }
.components { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 12px; margin-top: 16px; }
.component { background: #1a1a1a; border-radius: 8px; padding: 12px; font-size: 0.85rem; }
.component { background: #1a1a1a; border-radius: var(--radius-md); padding: 12px; font-size: 0.85rem; }
.component-name { color: #fff; font-weight: 600; }
.component-desc { color: #666; font-size: 0.75rem; margin-top: 4px; }
.sdk-section { background: #111; border: 1px solid #333; border-radius: 12px; padding: 32px; margin-bottom: 40px; }
.sdk-section { background: #111; border: 1px solid #333; border-radius: var(--radius-md); padding: 32px; margin-bottom: 40px; }
.sdk-section h2 { font-size: 1.5rem; margin-bottom: 16px; color: #00d4ff; }
.sdk-section p { color: #888; margin-bottom: 20px; }
.code-block { background: #1a1a1a; border-radius: 8px; padding: 20px; font-family: 'Monaco', monospace; font-size: 0.85rem; overflow-x: auto; }
.code-block { background: #1a1a1a; border-radius: var(--radius-md); padding: 20px; font-family: 'Monaco', monospace; font-size: 0.85rem; overflow-x: auto; }
.code-block .comment { color: #666; }
.code-block .keyword { color: #7c3aed; }
.code-block .string { color: #00d4ff; }
.object-model { background: #111; border: 1px solid #333; border-radius: 12px; padding: 32px; margin-bottom: 40px; }
.object-model { background: #111; border: 1px solid #333; border-radius: var(--radius-md); padding: 32px; margin-bottom: 40px; }
.object-model h2 { font-size: 1.5rem; margin-bottom: 16px; color: #7c3aed; }
.object-attrs { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 12px; margin-top: 16px; }
.attr { background: #1a1a1a; border-radius: 8px; padding: 16px; }
.attr { background: #1a1a1a; border-radius: var(--radius-md); padding: 16px; }
.attr-name { color: #00d4ff; font-weight: 600; }
.attr-desc { color: #888; font-size: 0.8rem; margin-top: 4px; }
.domains { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 12px; }
.domain { background: #1a1a2e; border: 1px solid #333; border-radius: 8px; padding: 16px; text-align: center; }
.domain { background: #1a1a2e; border: 1px solid #333; border-radius: var(--radius-md); padding: 16px; text-align: center; }
.domain:hover { border-color: #7c3aed; }
.domain-icon { font-size: 1.5rem; margin-bottom: 8px; }
.domain-name { font-size: 0.9rem; }
@@ -312,52 +312,52 @@ client.deploy(module)
<div class="domains" id="domains">
<div class="domain">
<div class="domain-icon">🏗️</div>
<div class="domain-icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 14H14M4 14V8L8 4L12 8V14" stroke="#666" stroke-width="1.5" stroke-linejoin="round"/><path d="M6 14V10H10V14" stroke="#666" stroke-width="1.5"/></svg></div>
<div class="domain-name">Infrastructure</div>
<div class="domain-status">Tillgänglig</div>
</div>
<div class="domain">
<div class="domain-icon">🌍</div>
<div class="domain-icon"></div>
<div class="domain-name">Aid</div>
<div class="domain-status">Tillgänglig</div>
</div>
<div class="domain">
<div class="domain-icon">🌱</div>
<div class="domain-icon"></div>
<div class="domain-name">Environmental</div>
<div class="domain-status">Beta</div>
</div>
<div class="domain">
<div class="domain-icon">🛡️</div>
<div class="domain-icon"></div>
<div class="domain-name">Insurance</div>
<div class="domain-status">Utveckling</div>
</div>
<div class="domain">
<div class="domain-icon">🏙️</div>
<div class="domain-icon"></div>
<div class="domain-name">City</div>
<div class="domain-status">Beta</div>
</div>
<div class="domain">
<div class="domain-icon">🌾</div>
<div class="domain-icon"></div>
<div class="domain-name">Agriculture</div>
<div class="domain-status">Beta</div>
</div>
<div class="domain">
<div class="domain-icon"></div>
<div class="domain-icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M9 2L5 9H8L7 14L11 7H8L9 2Z" fill="#f59e0b"/></svg></div>
<div class="domain-name">Energy</div>
<div class="domain-status">Utveckling</div>
</div>
<div class="domain">
<div class="domain-icon">📦</div>
<div class="domain-icon"></div>
<div class="domain-name">Supply Chain</div>
<div class="domain-status">Utveckling</div>
</div>
<div class="domain">
<div class="domain-icon">🔒</div>
<div class="domain-icon"></div>
<div class="domain-name">Security</div>
<div class="domain-status">Utveckling</div>
</div>
<div class="domain">
<div class="domain-icon">🏢</div>
<div class="domain-icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="3" y="2" width="10" height="12" rx="1" stroke="#666" stroke-width="1.5"/><path d="M6 5H7M6 8H7M6 11H7M9 5H10M9 8H10M9 11H10" stroke="#666" stroke-width="1.5" stroke-linecap="round"/></svg></div>
<div class="domain-name">Property</div>
<div class="domain-status">Utveckling</div>
</div>
+14 -14
View File
@@ -14,36 +14,36 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
.nav a { color: #666; text-decoration: none; padding: 12px 20px; font-size: 0.85rem; transition: all 0.2s; border-bottom: 2px solid transparent; }
.nav a:hover, .nav a.active { color: #00d4ff; border-bottom-color: #00d4ff; }
.main { padding: 40px; max-width: 1400px; margin: 0 auto; }
.hero { text-align: center; padding: 80px 20px; background: linear-gradient(180deg, #0a0a0a 0%, #1a1a2e 100%); border-radius: 16px; margin-bottom: 40px; }
.hero { text-align: center; padding: 80px 20px; background: linear-gradient(180deg, #0a0a0a 0%, #1a1a2e 100%); border-radius: var(--radius-lg); margin-bottom: 40px; }
.hero h2 { font-size: 2.5rem; margin-bottom: 16px; background: linear-gradient(90deg, #00d4ff, #7c3aed); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }
.hero p { font-size: 1.1rem; color: #aaa; max-width: 800px; margin: 0 auto 24px; }
.hero .tagline { font-size: 1rem; color: #00d4ff; font-weight: 600; }
.vision { background: #111; border: 1px solid #333; border-radius: 12px; padding: 32px; margin-bottom: 40px; text-align: center; }
.vision { background: #111; border: 1px solid #333; border-radius: var(--radius-md); padding: 32px; margin-bottom: 40px; text-align: center; }
.vision h2 { font-size: 1.5rem; margin-bottom: 16px; color: #00d4ff; }
.vision p { font-size: 1.1rem; color: #aaa; max-width: 800px; margin: 0 auto; line-height: 1.8; }
.evolution { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 12px; margin-bottom: 40px; }
.evo { background: #111; border: 1px solid #333; border-radius: 8px; padding: 12px; text-align: center; }
.evo { background: #111; border: 1px solid #333; border-radius: var(--radius-md); padding: 12px; text-align: center; }
.evo-version { font-size: 1.1rem; font-weight: 700; color: #00d4ff; }
.evo-name { font-size: 0.75rem; color: #aaa; }
.components { display: grid; grid-template-columns: repeat(auto-fill, minmax(350px, 1fr)); gap: 20px; margin-bottom: 40px; }
.component { background: #111; border: 1px solid #333; border-radius: 12px; padding: 24px; }
.component { background: #111; border: 1px solid #333; border-radius: var(--radius-md); padding: 24px; }
.component h3 { font-size: 1.1rem; margin-bottom: 12px; color: #00d4ff; }
.component p { color: #888; font-size: 0.85rem; margin-bottom: 12px; }
.component-items { list-style: none; }
.component-items li { color: #aaa; font-size: 0.8rem; padding: 4px 0; padding-left: 16px; position: relative; }
.component-items li::before { content: "→"; position: absolute; left: 0; color: #00d4ff; }
.separation { background: #111; border: 1px solid #333; border-radius: 12px; padding: 32px; margin-bottom: 40px; }
.separation { background: #111; border: 1px solid #333; border-radius: var(--radius-md); padding: 32px; margin-bottom: 40px; }
.separation h2 { font-size: 1.5rem; margin-bottom: 16px; color: #7c3aed; }
.separation p { color: #888; margin-bottom: 16px; }
.entities { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 16px; margin-top: 16px; }
.entity { background: #1a1a1a; border-radius: 8px; padding: 16px; }
.entity { background: #1a1a1a; border-radius: var(--radius-md); padding: 16px; }
.entity-name { color: #00d4ff; font-weight: 600; }
.entity-type { color: #888; font-size: 0.8rem; }
.entity-desc { color: #aaa; font-size: 0.75rem; margin-top: 8px; }
.cta { text-align: center; padding: 60px 20px; background: linear-gradient(180deg, #1a1a2e 0%, #0a0a0a 100%); border-radius: 16px; }
.cta { text-align: center; padding: 60px 20px; background: linear-gradient(180deg, #1a1a2e 0%, #0a0a0a 100%); border-radius: var(--radius-lg); }
.cta h2 { font-size: 2rem; margin-bottom: 16px; color: #fff; }
.cta p { font-size: 1rem; color: #aaa; margin-bottom: 24px; }
.cta-button { display: inline-block; background: linear-gradient(90deg, #00d4ff, #7c3aed); color: #fff; padding: 12px 32px; border-radius: 8px; text-decoration: none; font-weight: 600; }
.cta-button { display: inline-block; background: linear-gradient(90deg, #00d4ff, #7c3aed); color: #fff; padding: 12px 32px; border-radius: var(--radius-md); text-decoration: none; font-weight: 600; }
</style>
</head>
<body>
@@ -113,7 +113,7 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
<div class="components" id="components">
<div class="component">
<h3>🔬 Reality Intelligence</h3>
<h3> Reality Intelligence</h3>
<p>Analyzes the physical world, not just text and documents</p>
<ul class="component-items">
<li>Geospatial data fusion</li>
@@ -124,7 +124,7 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
</div>
<div class="component">
<h3>📋 Evidence Before Opinion</h3>
<h3><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="3" y="2" width="10" height="12" rx="1" stroke="#666" stroke-width="1.5"/><path d="M6 5H10M6 8H10M6 11H8" stroke="#666" stroke-width="1.5" stroke-linecap="round"/></svg> Evidence Before Opinion</h3>
<p>Starts with observations, not conclusions</p>
<ul class="component-items">
<li>Traceable evidence chains</li>
@@ -135,7 +135,7 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
</div>
<div class="component">
<h3>🌐 Multi-source Fusion</h3>
<h3> Multi-source Fusion</h3>
<p>Combines satellite, field, open data, and AI</p>
<ul class="component-items">
<li>Satellite imagery (Sentinel-2, etc.)</li>
@@ -146,7 +146,7 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
</div>
<div class="component">
<h3>🔍 Explainability</h3>
<h3><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><circle cx="7" cy="7" r="5" stroke="#666" stroke-width="2"/><path d="M11 11L14 14" stroke="#666" stroke-width="2" stroke-linecap="round"/></svg> Explainability</h3>
<p>Every insight is explainable and auditable</p>
<ul class="component-items">
<li>Decision lineage</li>
@@ -157,7 +157,7 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
</div>
<div class="component">
<h3>📦 Open Standards (RIS)</h3>
<h3> Open Standards (RIS)</h3>
<p>Six open standards for Reality Intelligence</p>
<ul class="component-items">
<li>RIS-001: Reality Object Standard</li>
@@ -170,7 +170,7 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
</div>
<div class="component">
<h3>🚀 Reference Implementation</h3>
<h3><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M8 2L10 8L14 9L8 14L6 8L2 7L8 2Z" fill="#666"/></svg> Reference Implementation</h3>
<p>Open source implementation of RIS</p>
<ul class="component-items">
<li>LIFE Server (Python/FastAPI)</li>
+6 -6
View File
@@ -14,29 +14,29 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
.nav a { color: #666; text-decoration: none; padding: 12px 20px; font-size: 0.85rem; transition: all 0.2s; border-bottom: 2px solid transparent; }
.nav a:hover, .nav a.active { color: #00d4ff; border-bottom-color: #00d4ff; }
.main { padding: 40px; max-width: 1400px; margin: 0 auto; }
.hero { text-align: center; padding: 60px 20px; background: linear-gradient(180deg, #0a0a0a 0%, #1a1a2e 100%); border-radius: 16px; margin-bottom: 40px; }
.hero { text-align: center; padding: 60px 20px; background: linear-gradient(180deg, #0a0a0a 0%, #1a1a2e 100%); border-radius: var(--radius-lg); margin-bottom: 40px; }
.hero h2 { font-size: 2rem; margin-bottom: 16px; background: linear-gradient(90deg, #00d4ff, #7c3aed); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }
.hero p { font-size: 1rem; color: #aaa; max-width: 800px; margin: 0 auto; }
.standards { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 20px; margin-bottom: 40px; }
.standard { background: #111; border: 1px solid #333; border-radius: 12px; padding: 24px; }
.standard { background: #111; border: 1px solid #333; border-radius: var(--radius-md); padding: 24px; }
.standard-id { font-size: 0.75rem; color: #00d4ff; text-transform: uppercase; letter-spacing: 1px; margin-bottom: 8px; }
.standard h3 { font-size: 1.1rem; margin-bottom: 12px; }
.standard p { color: #888; font-size: 0.85rem; margin-bottom: 12px; }
.standard-attrs { list-style: none; }
.standard-attrs li { color: #aaa; font-size: 0.8rem; padding: 4px 0; padding-left: 16px; position: relative; }
.standard-attrs li::before { content: "→"; position: absolute; left: 0; color: #00d4ff; }
.ontology { background: #111; border: 1px solid #333; border-radius: 12px; padding: 32px; margin-bottom: 40px; }
.ontology { background: #111; border: 1px solid #333; border-radius: var(--radius-md); padding: 32px; margin-bottom: 40px; }
.ontology h2 { font-size: 1.5rem; margin-bottom: 16px; color: #7c3aed; }
.ontology-levels { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 16px; margin-top: 16px; }
.level { background: #1a1a1a; border-radius: 8px; padding: 16px; }
.level { background: #1a1a1a; border-radius: var(--radius-md); padding: 16px; }
.level-name { color: #00d4ff; font-weight: 600; }
.level-desc { color: #888; font-size: 0.8rem; margin-top: 4px; }
.level-concepts { color: #aaa; font-size: 0.75rem; margin-top: 8px; }
.product-def { background: #111; border: 1px solid #333; border-radius: 12px; padding: 32px; margin-bottom: 40px; text-align: center; }
.product-def { background: #111; border: 1px solid #333; border-radius: var(--radius-md); padding: 32px; margin-bottom: 40px; text-align: center; }
.product-def h2 { font-size: 1.5rem; margin-bottom: 16px; color: #00d4ff; }
.product-def p { font-size: 1.1rem; color: #aaa; max-width: 700px; margin: 0 auto; line-height: 1.8; }
.evolution { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 12px; margin-bottom: 40px; }
.evo { background: #111; border: 1px solid #333; border-radius: 8px; padding: 12px; text-align: center; }
.evo { background: #111; border: 1px solid #333; border-radius: var(--radius-md); padding: 12px; text-align: center; }
.evo-version { font-size: 1.1rem; font-weight: 700; color: #00d4ff; }
.evo-name { font-size: 0.8rem; color: #aaa; }
</style>
+13 -13
View File
@@ -14,32 +14,32 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
.nav a { color: #666; text-decoration: none; padding: 12px 20px; font-size: 0.85rem; transition: all 0.2s; border-bottom: 2px solid transparent; }
.nav a:hover, .nav a.active { color: #00d4ff; border-bottom-color: #00d4ff; }
.main { padding: 40px; max-width: 1400px; margin: 0 auto; }
.philosophy { background: #111; border-radius: 12px; padding: 32px; margin-bottom: 32px; border-left: 4px solid #00d4ff; }
.philosophy { background: #111; border-radius: var(--radius-md); padding: 32px; margin-bottom: 32px; border-left: 4px solid #00d4ff; }
.philosophy h2 { font-size: 1.25rem; margin-bottom: 16px; color: #fff; }
.philosophy p { color: #aaa; margin-bottom: 12px; }
.process { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; margin-top: 20px; }
.process-step { background: #1a1a1a; border-radius: 8px; padding: 16px; text-align: center; }
.process-step { background: #1a1a1a; border-radius: var(--radius-md); padding: 16px; text-align: center; }
.process-step .number { font-size: 1.5rem; font-weight: 700; color: #00d4ff; }
.process-step .name { font-size: 0.9rem; color: #fff; margin-top: 8px; }
.process-step .desc { font-size: 0.75rem; color: #666; margin-top: 4px; }
.stats-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 20px; margin-bottom: 32px; }
.stat-card { background: #111; border-radius: 12px; padding: 24px; border: 1px solid #333; }
.stat-card { background: #111; border-radius: var(--radius-md); padding: 24px; border: 1px solid #333; }
.stat-card h3 { font-size: 0.75rem; color: #666; margin-bottom: 8px; text-transform: uppercase; letter-spacing: 0.5px; }
.stat-card .value { font-size: 2rem; font-weight: 700; color: #fff; }
.stat-card .change { font-size: 0.8rem; margin-top: 4px; }
.stat-card .change.positive { color: #00d4ff; }
.component-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 20px; margin-bottom: 32px; }
.component-card { background: #111; border-radius: 12px; padding: 24px; border: 1px solid #333; }
.component-card { background: #111; border-radius: var(--radius-md); padding: 24px; border: 1px solid #333; }
.component-card h3 { font-size: 1rem; margin-bottom: 12px; color: #00d4ff; display: flex; align-items: center; gap: 8px; }
.component-card p { font-size: 0.85rem; color: #aaa; margin-bottom: 8px; }
.component-card .stats { font-size: 0.8rem; color: #666; }
.section { background: #111; border-radius: 12px; padding: 24px; margin-bottom: 24px; border: 1px solid #333; }
.section { background: #111; border-radius: var(--radius-md); padding: 24px; margin-bottom: 24px; border: 1px solid #333; }
.section h2 { font-size: 1.1rem; margin-bottom: 16px; color: #fff; }
.hypothesis-card { background: #1a1a1a; border-radius: 8px; padding: 16px; margin-bottom: 12px; border-left: 3px solid #00d4ff; }
.hypothesis-card { background: #1a1a1a; border-radius: var(--radius-md); padding: 16px; margin-bottom: 12px; border-left: 3px solid #00d4ff; }
.hypothesis-card h4 { font-size: 0.95rem; margin-bottom: 8px; color: #fff; }
.hypothesis-card p { font-size: 0.85rem; color: #aaa; }
.hypothesis-card .confidence { font-size: 0.8rem; color: #00d4ff; margin-top: 8px; }
.dna-bar { background: #1a1a1a; border-radius: 4px; height: 24px; overflow: hidden; margin-top: 8px; display: flex; }
.dna-bar { background: #1a1a1a; border-radius: var(--radius-sm); height: 24px; overflow: hidden; margin-top: 8px; display: flex; }
.dna-bar .segment { height: 100%; display: flex; align-items: center; justify-content: center; font-size: 0.7rem; color: #fff; }
</style>
</head>
@@ -128,14 +128,14 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
<div class="component-grid" id="components">
<div class="component-card">
<h3>🔗 Evidence Graph (EG)</h3>
<h3> Evidence Graph (EG)</h3>
<p>Hierarkisk graf där varje observation är en spårbar nod</p>
<p style="font-size: 0.8rem; color: #666;">Observation → Object → Project → Programme → Organisation → Region → Country</p>
<div class="stats">125,000 noder | 450,000 edges</div>
</div>
<div class="component-card">
<h3>📜 Evidence Lineage</h3>
<h3> Evidence Lineage</h3>
<p>Komplett ursprung för varje datapunkt</p>
<p style="font-size: 0.8rem; color: #666;">Source → Collected → Validated → Cross-validated → AI confidence → Human verification</p>
<div class="stats">45,000 observationer spårade</div>
@@ -149,28 +149,28 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
</div>
<div class="component-card">
<h3>⚠️ Contradiction Knowledge Base</h4>
<h3><svg width="20" height="20" viewBox="0 0 20 20" fill="none"><path d="M10 3L17 17H3L10 3Z" stroke="#f59e0b" stroke-width="2"/><path d="M10 8V12M10 14V14.5" stroke="#f59e0b" stroke-width="2" stroke-linecap="round"/></svg> Contradiction Knowledge Base</h4>
<p>Alla avvikelser lagras för AI-träning</p>
<p style="font-size: 0.8rem; color: #666;">Efter några år: hundratusentals verkliga exempel</p>
<div class="stats">1,247 avvikelser | 6 kategorier</div>
</div>
<div class="component-card">
<h3>🧬 Reality DNA</h3>
<h3> Reality DNA</h3>
<p>Projektets fingeravtryck för benchmarking</p>
<p style="font-size: 0.8rem; color: #666;">9 dimensioner: Activity, Maintenance, Infrastructure, Community Usage, Traffic, Economic Activity, Environmental Status, Safety, Operational Continuity</p>
<div class="stats">1,250 profiler | Jämförbara</div>
</div>
<div class="component-card">
<h3>💾 Reality Memory</h3>
<h3> Reality Memory</h3>
<p>LIFE kastar aldrig bort något</p>
<p style="font-size: 0.8rem; color: #666;">Varje observation, AI-bedömning och förändring sparas permanent</p>
<div class="stats">2.4 TB | Full reproducerbarhet</div>
</div>
<div class="component-card">
<h3>🧠 Reality Hypothesis Engine</h3>
<h3> Reality Hypothesis Engine</h3>
<p>Inte "Det här är orsaken" utan "Här är möjliga förklaringar..."</p>
<p style="font-size: 0.8rem; color: #666;">Rangordnade efter hur väl de stöds av observerbara data</p>
<div class="stats">3,400 hypoteser | Med osäkerhetsintervall</div>
+16 -16
View File
@@ -14,30 +14,30 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
.nav a { color: #666; text-decoration: none; padding: 12px 20px; font-size: 0.85rem; transition: all 0.2s; border-bottom: 2px solid transparent; }
.nav a:hover, .nav a.active { color: #00d4ff; border-bottom-color: #00d4ff; }
.main { padding: 40px; max-width: 1400px; margin: 0 auto; }
.hero { text-align: center; padding: 60px 20px; background: linear-gradient(180deg, #0a0a0a 0%, #1a1a2e 100%); border-radius: 16px; margin-bottom: 40px; }
.hero { text-align: center; padding: 60px 20px; background: linear-gradient(180deg, #0a0a0a 0%, #1a1a2e 100%); border-radius: var(--radius-lg); margin-bottom: 40px; }
.hero h2 { font-size: 2rem; margin-bottom: 16px; background: linear-gradient(90deg, #00d4ff, #7c3aed); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }
.hero p { font-size: 1rem; color: #aaa; max-width: 800px; margin: 0 auto; }
.phases { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 20px; margin-bottom: 40px; }
.phase { background: #111; border: 1px solid #333; border-radius: 12px; padding: 24px; }
.phase { background: #111; border: 1px solid #333; border-radius: var(--radius-md); padding: 24px; }
.phase-number { font-size: 0.75rem; color: #00d4ff; text-transform: uppercase; letter-spacing: 1px; margin-bottom: 8px; }
.phase h3 { font-size: 1.1rem; margin-bottom: 12px; }
.phase p { color: #888; font-size: 0.85rem; margin-bottom: 12px; }
.phase-objs { list-style: none; }
.phase-objs li { color: #aaa; font-size: 0.8rem; padding: 4px 0; padding-left: 16px; position: relative; }
.phase-objs li::before { content: "→"; position: absolute; left: 0; color: #00d4ff; }
.metrics { background: #111; border: 1px solid #333; border-radius: 12px; padding: 32px; margin-bottom: 40px; }
.metrics { background: #111; border: 1px solid #333; border-radius: var(--radius-md); padding: 32px; margin-bottom: 40px; }
.metrics h2 { font-size: 1.5rem; margin-bottom: 16px; color: #7c3aed; }
.metrics p { color: #888; margin-bottom: 16px; }
.metric-dims { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 16px; margin-top: 16px; }
.dim { background: #1a1a1a; border-radius: 8px; padding: 16px; }
.dim { background: #1a1a1a; border-radius: var(--radius-md); padding: 16px; }
.dim-name { color: #00d4ff; font-weight: 600; }
.dim-desc { color: #888; font-size: 0.8rem; margin-top: 4px; }
.dim-kpis { color: #aaa; font-size: 0.75rem; margin-top: 8px; }
.spec { background: #111; border: 1px solid #333; border-radius: 12px; padding: 32px; margin-bottom: 40px; }
.spec { background: #111; border: 1px solid #333; border-radius: var(--radius-md); padding: 32px; margin-bottom: 40px; }
.spec h2 { font-size: 1.5rem; margin-bottom: 16px; color: #00d4ff; }
.spec p { color: #888; margin-bottom: 16px; }
.spec-sections { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 12px; }
.spec-section { background: #1a1a1a; border-radius: 8px; padding: 12px; }
.spec-section { background: #1a1a1a; border-radius: var(--radius-md); padding: 12px; }
.spec-num { color: #00d4ff; font-size: 0.75rem; }
.spec-title { color: #fff; font-size: 0.85rem; }
</style>
@@ -70,7 +70,7 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
<div class="phases" id="phases">
<div class="phase">
<div class="phase-number">Fas 1</div>
<h3>🛣️ Infrastruktur</h3>
<h3> Infrastruktur</h3>
<p>6 månader — Vägar, broar, arbeten</p>
<ul class="phase-objs">
<li>Hitta vägskador (>80% precision)</li>
@@ -82,7 +82,7 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
<div class="phase">
<div class="phase-number">Fas 2</div>
<h3>🏙️ Kommun</h3>
<h3> Kommun</h3>
<p>6 månader — Parker, belysning, skyltning</p>
<ul class="phase-objs">
<li>Följa parker (veckovis)</li>
@@ -95,7 +95,7 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
<div class="phase">
<div class="phase-number">Fas 3</div>
<h3>🏢 Fastigheter</h3>
<h3><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="3" y="2" width="10" height="12" rx="1" stroke="#666" stroke-width="1.5"/><path d="M6 5H7M6 8H7M6 11H7M9 5H10M9 8H10M9 11H10" stroke="#666" stroke-width="1.5" stroke-linecap="round"/></svg> Fastigheter</h3>
<p>6 månader — Byggnader, skador, underhåll</p>
<ul class="phase-objs">
<li>Följa byggnader (månadsvis)</li>
@@ -107,7 +107,7 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
<div class="phase">
<div class="phase-number">Fas 4</div>
<h3>🌍 Bistånd</h3>
<h3> Bistånd</h3>
<p>12 månader — Projekt, observationer, verifiering</p>
<ul class="phase-objs">
<li>Följa projekt över tid (±15%)</li>
@@ -124,32 +124,32 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
<div class="metric-dims">
<div class="dim">
<div class="dim-name">📊 Datatäckning</div>
<div class="dim-name"> Datatäckning</div>
<div class="dim-desc">Andel objekt med aktuell data</div>
<div class="dim-kpis">Object Coverage >95%<br>Geographic Coverage >90%</div>
</div>
<div class="dim">
<div class="dim-name"> Aktualitet</div>
<div class="dim-name">⏱ Aktualitet</div>
<div class="dim-desc">Tid från förändring till observation</div>
<div class="dim-kpis">Detection Time <48h<br>Alert Latency <1h</div>
</div>
<div class="dim">
<div class="dim-name">🔍 Evidenskvalitet</div>
<div class="dim-name"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><circle cx="7" cy="7" r="5" stroke="#666" stroke-width="2"/><path d="M11 11L14 14" stroke="#666" stroke-width="2" stroke-linecap="round"/></svg> Evidenskvalitet</div>
<div class="dim-desc">Konfidens och verifiering</div>
<div class="dim-kpis">Avg Confidence >75<br>Verification Rate >30%</div>
</div>
<div class="dim">
<div class="dim-name">🎯 Analyskvalitet</div>
<div class="dim-name"> Analyskvalitet</div>
<div class="dim-desc">Användbarhet och korrekthet</div>
<div class="dim-kpis">User Satisfaction >80%<br>Decision Adoption >60%</div>
</div>
<div class="dim">
<div class="dim-name">📈 Adoption</div>
<div class="dim-name"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 12L6 8L9 11L14 5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M10 5H14V9" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Adoption</div>
<div class="dim-desc">Externa implementationer</div>
<div class="dim-kpis">External Impl >10<br>Partner Integrations >20</div>
</div>
<div class="dim">
<div class="dim-name">🔗 Interoperabilitet</div>
<div class="dim-name"> Interoperabilitet</div>
<div class="dim-desc">System som utbyter data</div>
<div class="dim-kpis">RIS Compatible >15<br>Data Exchange >1TB/mån</div>
</div>
+3 -3
View File
@@ -11,15 +11,15 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
.header h1 { font-size: 1.5rem; color: #00d4ff; }
.main { padding: 40px; max-width: 1400px; margin: 0 auto; }
.metrics { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 16px; margin-bottom: 32px; }
.metric { background: #111; border: 1px solid #333; border-radius: 12px; padding: 20px; }
.metric { background: #111; border: 1px solid #333; border-radius: var(--radius-md); padding: 20px; }
.metric-value { font-size: 2.5rem; font-weight: 700; color: #00d4ff; }
.metric-label { font-size: 0.85rem; color: #888; margin-top: 4px; }
.section { background: #111; border: 1px solid #333; border-radius: 12px; padding: 24px; margin-bottom: 24px; }
.section { background: #111; border: 1px solid #333; border-radius: var(--radius-md); padding: 24px; margin-bottom: 24px; }
.section h2 { font-size: 1.2rem; margin-bottom: 16px; color: #00d4ff; }
table { width: 100%; border-collapse: collapse; }
th { text-align: left; padding: 12px; color: #888; font-size: 0.8rem; }
td { padding: 12px; border-top: 1px solid #222; font-size: 0.9rem; }
.status { display: inline-block; padding: 4px 8px; border-radius: 4px; font-size: 0.75rem; }
.status { display: inline-block; padding: 4px 8px; border-radius: var(--radius-sm); font-size: 0.75rem; }
.status-normal { background: #1a3a1a; color: #4ade80; }
.status-warning { background: #3a3a1a; color: #facc15; }
.status-critical { background: #3a1a1a; color: #f87171; }
+23 -23
View File
@@ -14,43 +14,43 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
.nav a { color: #94a3b8; text-decoration: none; padding: 12px 20px; font-size: 0.85rem; transition: all 0.2s; border-bottom: 2px solid transparent; }
.nav a:hover, .nav a.active { color: #38bdf8; border-bottom-color: #38bdf8; }
.main { padding: 40px; max-width: 1400px; margin: 0 auto; }
.philosophy { background: #1e293b; border-radius: 12px; padding: 32px; margin-bottom: 32px; border-left: 4px solid #38bdf8; }
.philosophy { background: #1e293b; border-radius: var(--radius-md); padding: 32px; margin-bottom: 32px; border-left: 4px solid #38bdf8; }
.philosophy h2 { font-size: 1.25rem; margin-bottom: 16px; color: #f8fafc; }
.philosophy p { color: #cbd5e1; margin-bottom: 12px; }
.philosophy .process { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; margin-top: 20px; }
.process-step { background: #334155; border-radius: 8px; padding: 16px; text-align: center; }
.process-step { background: #334155; border-radius: var(--radius-md); padding: 16px; text-align: center; }
.process-step .number { font-size: 1.5rem; font-weight: 700; color: #38bdf8; }
.process-step .name { font-size: 0.9rem; color: #f8fafc; margin-top: 8px; }
.process-step .desc { font-size: 0.75rem; color: #94a3b8; margin-top: 4px; }
.stats-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 20px; margin-bottom: 32px; }
.stat-card { background: #1e293b; border-radius: 12px; padding: 24px; border: 1px solid #334155; }
.stat-card { background: #1e293b; border-radius: var(--radius-md); padding: 24px; border: 1px solid #334155; }
.stat-card h3 { font-size: 0.75rem; color: #94a3b8; margin-bottom: 8px; text-transform: uppercase; letter-spacing: 0.5px; }
.stat-card .value { font-size: 2rem; font-weight: 700; color: #f8fafc; }
.stat-card .change { font-size: 0.8rem; margin-top: 4px; }
.stat-card .change.positive { color: #4ade80; }
.section { background: #1e293b; border-radius: 12px; padding: 24px; margin-bottom: 24px; border: 1px solid #334155; }
.section { background: #1e293b; border-radius: var(--radius-md); padding: 24px; margin-bottom: 24px; border: 1px solid #334155; }
.section h2 { font-size: 1.1rem; margin-bottom: 16px; color: #f8fafc; }
.data-source { display: flex; align-items: center; gap: 12px; padding: 12px; background: #334155; border-radius: 8px; margin-bottom: 8px; }
.data-source .icon { width: 40px; height: 40px; background: #475569; border-radius: 8px; display: flex; align-items: center; justify-content: center; font-size: 1.2rem; }
.data-source { display: flex; align-items: center; gap: 12px; padding: 12px; background: #334155; border-radius: var(--radius-md); margin-bottom: 8px; }
.data-source .icon { width: 40px; height: 40px; background: #475569; border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; font-size: 1.2rem; }
.data-source .info { flex: 1; }
.data-source .info h4 { font-size: 0.9rem; color: #f8fafc; }
.data-source .info p { font-size: 0.75rem; color: #94a3b8; }
.data-source .status { font-size: 0.75rem; padding: 4px 8px; border-radius: 4px; }
.data-source .status { font-size: 0.75rem; padding: 4px 8px; border-radius: var(--radius-sm); }
.data-source .status.active { background: #166534; color: #4ade80; }
.event-card { background: #334155; border-radius: 8px; padding: 16px; margin-bottom: 12px; }
.event-card { background: #334155; border-radius: var(--radius-md); padding: 16px; margin-bottom: 12px; }
.event-card h4 { font-size: 0.95rem; margin-bottom: 8px; }
.event-card .meta { font-size: 0.8rem; color: #94a3b8; margin-bottom: 8px; }
.event-card .severity { display: inline-block; padding: 3px 10px; border-radius: 4px; font-size: 0.7rem; font-weight: 600; }
.event-card .severity { display: inline-block; padding: 3px 10px; border-radius: var(--radius-sm); font-size: 0.7rem; font-weight: 600; }
.event-card .severity.high { background: #7f1d1d; color: #fca5a5; }
.event-card .severity.medium { background: #713f12; color: #fde047; }
.event-card .severity.low { background: #1e3a5f; color: #93c5fd; }
.confidence-bar { background: #334155; border-radius: 4px; height: 8px; overflow: hidden; margin-top: 8px; }
.confidence-bar .fill { height: 100%; border-radius: 4px; }
.confidence-bar { background: #334155; border-radius: var(--radius-sm); height: 8px; overflow: hidden; margin-top: 8px; }
.confidence-bar .fill { height: 100%; border-radius: var(--radius-sm); }
.confidence-bar .fill.high { background: #4ade80; }
.confidence-bar .fill.medium { background: #fbbf24; }
.confidence-bar .fill.low { background: #f87171; }
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; }
.insight-card { background: #334155; border-radius: 8px; padding: 16px; margin-bottom: 12px; }
.insight-card { background: #334155; border-radius: var(--radius-md); padding: 16px; margin-bottom: 12px; }
.insight-card h4 { font-size: 0.9rem; margin-bottom: 8px; color: #38bdf8; }
.insight-card p { font-size: 0.85rem; color: #cbd5e1; }
</style>
@@ -142,7 +142,7 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
<h2>Continuous Data Acquisition</h2>
<div class="data-source">
<div class="icon">📊</div>
<div class="icon"></div>
<div class="info">
<h4>Official Project Information</h4>
<p>Development agency portals, government databases, procurement portals, evaluation reports</p>
@@ -151,7 +151,7 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
</div>
<div class="data-source">
<div class="icon">📰</div>
<div class="icon"></div>
<div class="info">
<h4>News Intelligence</h4>
<p>International, national, regional news, RSS feeds, development and humanitarian news</p>
@@ -160,7 +160,7 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
</div>
<div class="data-source">
<div class="icon">🌐</div>
<div class="icon"></div>
<div class="info">
<h4>Public Communications</h4>
<p>Press releases, official websites, social media, research publications</p>
@@ -169,7 +169,7 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
</div>
<div class="data-source">
<div class="icon">🛰️</div>
<div class="icon"></div>
<div class="info">
<h4>Geospatial Intelligence</h4>
<p>Satellite imagery, night lights, land cover, construction detection, environmental monitoring</p>
@@ -178,7 +178,7 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
</div>
<div class="data-source">
<div class="icon">📱</div>
<div class="icon"></div>
<div class="info">
<h4>QUIXZOOM Reality Network</h4>
<p>Field observations, photographic evidence, infrastructure inspections, community observations</p>
@@ -187,7 +187,7 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
</div>
<div class="data-source">
<div class="icon">📈</div>
<div class="icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 12L6 8L9 11L14 5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M10 5H14V9" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></div>
<div class="info">
<h4>Open Data</h4>
<p>Population, weather, climate, health indicators, economic data, conflict datasets</p>
@@ -201,7 +201,7 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
<h2>Recent Events Detected</h2>
<div class="event-card">
<h4>🌊 Severe flooding in Mogadishu region</h4>
<h4> Severe flooding in Mogadishu region</h4>
<div class="meta">Somalia • June 15, 2026 • Natural Disaster</div>
<p style="font-size: 0.85rem; color: #cbd5e1; margin-bottom: 8px;">Heavy rainfall caused flooding affecting infrastructure accessibility</p>
<div>
@@ -214,7 +214,7 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
</div>
<div class="event-card">
<h4>🏗️ Construction delay due to seasonal rainfall</h4>
<h4><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 14H14M4 14V8L8 4L12 8V14" stroke="#666" stroke-width="1.5" stroke-linejoin="round"/><path d="M6 14V10H10V14" stroke="#666" stroke-width="1.5"/></svg> Construction delay due to seasonal rainfall</h4>
<div class="meta">Nigeria • May 20, 2026 • Construction Delay</div>
<p style="font-size: 0.85rem; color: #cbd5e1; margin-bottom: 8px;">Road rehabilitation delayed due to extended rainy season</p>
<div>
@@ -231,7 +231,7 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
<h2>Recent Fusion Insights</h2>
<div class="insight-card">
<h4>🔗 Mogadishu Primary School</h4>
<h4> Mogadishu Primary School</h4>
<p>Construction delay likely due to flooding + poor drainage</p>
<div style="margin-top: 8px;">
<span style="font-size: 0.75rem; color: #94a3b8;">Confidence: 78% • Sources fused: 5</span>
@@ -239,7 +239,7 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
</div>
<div class="insight-card">
<h4>🔗 Kampala Water Supply</h4>
<h4> Kampala Water Supply</h4>
<p>Operational sustainability high based on maintenance records + community feedback</p>
<div style="margin-top: 8px;">
<span style="font-size: 0.75rem; color: #94a3b8;">Confidence: 88% • Sources fused: 6</span>
@@ -247,7 +247,7 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
</div>
<div class="insight-card">
<h4>🔗 Accra Solar Microgrid</h4>
<h4> Accra Solar Microgrid</h4>
<p>24 months continuous operation verified across 4 independent sources</p>
<div style="margin-top: 8px;">
<span style="font-size: 0.75rem; color: #94a3b8;">Confidence: 92% • Sources fused: 4</span>
+10 -10
View File
@@ -10,19 +10,19 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
.header { background: #0a0a0a; border-bottom: 1px solid #333; padding: 20px 40px; display: flex; justify-content: space-between; align-items: center; }
.header h1 { font-size: 1.5rem; color: #00d4ff; }
.live-indicator { display: flex; align-items: center; gap: 8px; }
.pulse { width: 8px; height: 8px; background: #4ade80; border-radius: 50%; animation: pulse 2s infinite; }
.pulse { width: 8px; height: 8px; background: #4ade80; border-radius: var(--radius-full); animation: pulse 2s infinite; }
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.3; } }
.main { padding: 40px; max-width: 1400px; margin: 0 auto; }
.metrics { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 16px; margin-bottom: 32px; }
.metric { background: #111; border: 1px solid #333; border-radius: 12px; padding: 20px; }
.metric { background: #111; border: 1px solid #333; border-radius: var(--radius-md); padding: 20px; }
.metric-value { font-size: 2.5rem; font-weight: 700; color: #00d4ff; }
.metric-label { font-size: 0.85rem; color: #888; margin-top: 4px; }
.metric-change { font-size: 0.75rem; margin-top: 4px; }
.change-up { color: #4ade80; }
.section { background: #111; border: 1px solid #333; border-radius: 12px; padding: 24px; margin-bottom: 24px; }
.section { background: #111; border: 1px solid #333; border-radius: var(--radius-md); padding: 24px; margin-bottom: 24px; }
.section h2 { font-size: 1.2rem; margin-bottom: 16px; color: #00d4ff; }
.source-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 12px; }
.source { background: #1a1a1a; border-radius: 8px; padding: 12px; text-align: center; }
.source { background: #1a1a1a; border-radius: var(--radius-md); padding: 12px; text-align: center; }
.source-name { font-size: 0.8rem; color: #888; }
.source-value { font-size: 1.5rem; font-weight: 700; color: #fff; margin-top: 4px; }
.source-status { font-size: 0.7rem; color: #4ade80; margin-top: 4px; }
@@ -85,32 +85,32 @@ td { padding: 12px; border-top: 1px solid #222; font-size: 0.9rem; }
<div class="source">
<div class="source-name">Trafikverket</div>
<div class="source-value" id="src-trafikverket"></div>
<div class="source-status"> Active</div>
<div class="source-status"> Active</div>
</div>
<div class="source">
<div class="source-name">SMHI</div>
<div class="source-value" id="src-smhi"></div>
<div class="source-status"> Active</div>
<div class="source-status"> Active</div>
</div>
<div class="source">
<div class="source-name">Satellite</div>
<div class="source-value" id="src-satellite"></div>
<div class="source-status"> Active</div>
<div class="source-status"> Active</div>
</div>
<div class="source">
<div class="source-name">quiXzoom</div>
<div class="source-value" id="src-quixzoom"></div>
<div class="source-status"> Active</div>
<div class="source-status"> Active</div>
</div>
<div class="source">
<div class="source-name">Manual</div>
<div class="source-value" id="src-manual"></div>
<div class="source-status"> Active</div>
<div class="source-status"> Active</div>
</div>
<div class="source">
<div class="source-name">Sensor</div>
<div class="source-value" id="src-sensor"></div>
<div class="source-status"> Active</div>
<div class="source-status"> Active</div>
</div>
</div>
</div>
+4 -4
View File
@@ -11,20 +11,20 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
.header h1 { font-size: 1.5rem; color: #00d4ff; }
.header p { color: #888; font-size: 0.9rem; }
.main { padding: 40px; max-width: 1200px; margin: 0 auto; }
.section { background: #111; border: 1px solid #333; border-radius: 12px; padding: 24px; margin-bottom: 24px; }
.section { background: #111; border: 1px solid #333; border-radius: var(--radius-md); padding: 24px; margin-bottom: 24px; }
.section h2 { font-size: 1.2rem; margin-bottom: 16px; color: #00d4ff; }
.source { display: flex; justify-content: space-between; align-items: center; padding: 12px; border-bottom: 1px solid #222; }
.source:last-child { border-bottom: none; }
.source-name { font-weight: 600; }
.source-type { font-size: 0.8rem; padding: 2px 8px; border-radius: 4px; }
.source-type { font-size: 0.8rem; padding: 2px 8px; border-radius: var(--radius-sm); }
.type-real { background: #1a3a1a; color: #4ade80; }
.type-simulated { background: #3a1a1a; color: #f87171; }
.source-count { font-size: 1.2rem; font-weight: 700; }
.metric { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 16px; margin-bottom: 24px; }
.metric-card { background: #111; border: 1px solid #333; border-radius: 12px; padding: 20px; }
.metric-card { background: #111; border: 1px solid #333; border-radius: var(--radius-md); padding: 20px; }
.metric-value { font-size: 2rem; font-weight: 700; color: #00d4ff; }
.metric-label { font-size: 0.85rem; color: #888; }
.status { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: 8px; }
.status { display: inline-block; width: 8px; height: 8px; border-radius: var(--radius-full); margin-right: 8px; }
.status-active { background: #4ade80; }
.status-inactive { background: #f87171; }
</style>
+5 -5
View File
@@ -9,13 +9,13 @@
body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; background: #000; color: #fff; line-height: 1.6; }
.header { background: #0a0a0a; border-bottom: 1px solid #333; padding: 20px 40px; display: flex; justify-content: space-between; align-items: center; }
.header h1 { font-size: 1.5rem; color: #00d4ff; }
.header .live { background: #1a3a1a; color: #4ade80; padding: 4px 12px; border-radius: 4px; font-size: 0.8rem; font-weight: 600; }
.header .live { background: #1a3a1a; color: #4ade80; padding: 4px 12px; border-radius: var(--radius-sm); font-size: 0.8rem; font-weight: 600; }
.main { padding: 40px; max-width: 1400px; margin: 0 auto; }
.metrics { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 16px; margin-bottom: 32px; }
.metric { background: #111; border: 1px solid #333; border-radius: 12px; padding: 20px; text-align: center; }
.metric { background: #111; border: 1px solid #333; border-radius: var(--radius-md); padding: 20px; text-align: center; }
.metric-value { font-size: 2.5rem; font-weight: 700; color: #00d4ff; }
.metric-label { font-size: 0.85rem; color: #888; margin-top: 4px; }
.section { background: #111; border: 1px solid #333; border-radius: 12px; padding: 24px; margin-bottom: 24px; }
.section { background: #111; border: 1px solid #333; border-radius: var(--radius-md); padding: 24px; margin-bottom: 24px; }
.section h2 { font-size: 1.2rem; margin-bottom: 16px; color: #00d4ff; }
table { width: 100%; border-collapse: collapse; }
th { text-align: left; padding: 12px; color: #888; font-size: 0.8rem; text-transform: uppercase; }
@@ -24,7 +24,7 @@ td { padding: 12px; border-top: 1px solid #222; font-size: 0.9rem; }
.severity-medium { color: #fbbf24; }
.severity-high { color: #f87171; }
.severity-critical { color: #ef4444; font-weight: 600; }
.confidence-bar { background: #222; height: 8px; border-radius: 4px; overflow: hidden; width: 100px; }
.confidence-bar { background: #222; height: 8px; border-radius: var(--radius-sm); overflow: hidden; width: 100px; }
.confidence-fill { height: 100%; background: linear-gradient(90deg, #ef4444, #fbbf24, #4ade80); }
</style>
</head>
@@ -34,7 +34,7 @@ td { padding: 12px; border-top: 1px solid #222; font-size: 0.9rem; }
<div>
<h1>RIVP Pilot 1</h1>
</div>
<div class="live"> LIVE DATA</div>
<div class="live"> LIVE DATA</div>
</div>
<div class="main">
+7 -7
View File
@@ -11,19 +11,19 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
.header h1 { font-size: 1.5rem; color: #00d4ff; }
.header .subtitle { color: #666; font-size: 0.85rem; }
.main { padding: 40px; max-width: 1200px; margin: 0 auto; }
.status { display: inline-block; padding: 4px 12px; border-radius: 4px; font-size: 0.8rem; font-weight: 600; }
.status { display: inline-block; padding: 4px 12px; border-radius: var(--radius-sm); font-size: 0.8rem; font-weight: 600; }
.status-live { background: #1a3a1a; color: #4ade80; }
.results { background: #111; border: 1px solid #333; border-radius: 12px; padding: 32px; margin-bottom: 24px; }
.results { background: #111; border: 1px solid #333; border-radius: var(--radius-md); padding: 32px; margin-bottom: 24px; }
.results h2 { font-size: 1.3rem; margin-bottom: 16px; color: #00d4ff; }
.metric-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 16px; margin-bottom: 24px; }
.metric { background: #1a1a1a; border-radius: 8px; padding: 16px; text-align: center; }
.metric { background: #1a1a1a; border-radius: var(--radius-md); padding: 16px; text-align: center; }
.metric-value { font-size: 2rem; font-weight: 700; color: #00d4ff; }
.metric-label { font-size: 0.8rem; color: #888; margin-top: 4px; }
.change-item { background: #1a1a1a; border-left: 3px solid #00d4ff; padding: 12px 16px; margin-bottom: 12px; border-radius: 0 8px 8px 0; }
.change-type { font-weight: 600; color: #fff; }
.change-details { color: #888; font-size: 0.85rem; margin-top: 4px; }
.change-confidence { color: #4ade80; font-size: 0.8rem; }
.map-placeholder { background: #1a1a1a; border-radius: 8px; height: 300px; display: flex; align-items: center; justify-content: center; color: #666; }
.map-placeholder { background: #1a1a1a; border-radius: var(--radius-md); height: 300px; display: flex; align-items: center; justify-content: center; color: #666; }
</style>
</head>
<body>
@@ -63,13 +63,13 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
<h2>E4 (Motorway) — 45 km</h2>
<div class="change-item">
<div class="change-type">🕳️ Pothole Detected</div>
<div class="change-type"> Pothole Detected</div>
<div class="change-details">Location: 59.85°N, 17.65°E | Size: 12 m² | Severity: Medium</div>
<div class="change-confidence">Confidence: 82%</div>
</div>
<div class="change-item">
<div class="change-type">🚧 Construction Zone</div>
<div class="change-type"> Construction Zone</div>
<div class="change-details">Location: 59.88°N, 17.72°E | Size: 2,500 m² | Severity: High</div>
<div class="change-confidence">Confidence: 95%</div>
</div>
@@ -83,7 +83,7 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
<h2>Länsväg 272 (County Road) — 23 km</h2>
<div class="change-item">
<div class="change-type">🛣️ Surface Damage</div>
<div class="change-type"> Surface Damage</div>
<div class="change-details">Location: 59.92°N, 17.55°E | Size: 45 m² | Severity: Low</div>
<div class="change-confidence">Confidence: 78%</div>
</div>
+4 -4
View File
@@ -12,16 +12,16 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
.header .subtitle { color: #888; font-size: 0.9rem; }
.main { padding: 40px; max-width: 1400px; margin: 0 auto; }
.metrics { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 16px; margin-bottom: 32px; }
.metric { background: #111; border: 1px solid #333; border-radius: 12px; padding: 20px; }
.metric { background: #111; border: 1px solid #333; border-radius: var(--radius-md); padding: 20px; }
.metric-value { font-size: 2.5rem; font-weight: 700; color: #00d4ff; }
.metric-label { font-size: 0.85rem; color: #888; margin-top: 4px; }
.metric-status { font-size: 0.75rem; margin-top: 8px; padding: 4px 8px; border-radius: 4px; display: inline-block; }
.metric-status { font-size: 0.75rem; margin-top: 8px; padding: 4px 8px; border-radius: var(--radius-sm); display: inline-block; }
.status-ok { background: #1a3a1a; color: #4ade80; }
.status-warn { background: #3a3a1a; color: #facc15; }
.status-critical { background: #3a1a1a; color: #f87171; }
.section { background: #111; border: 1px solid #333; border-radius: 12px; padding: 24px; margin-bottom: 24px; }
.section { background: #111; border: 1px solid #333; border-radius: var(--radius-md); padding: 24px; margin-bottom: 24px; }
.section h2 { font-size: 1.2rem; margin-bottom: 16px; color: #00d4ff; }
.chart { height: 200px; background: #1a1a1a; border-radius: 8px; display: flex; align-items: center; justify-content: center; color: #666; }
.chart { height: 200px; background: #1a1a1a; border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; color: #666; }
table { width: 100%; border-collapse: collapse; }
th { text-align: left; padding: 12px; color: #888; font-size: 0.8rem; }
td { padding: 12px; border-top: 1px solid #222; font-size: 0.9rem; }
+3 -3
View File
@@ -10,18 +10,18 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; back
.header { background: #0a0a0a; border-bottom: 1px solid #333; padding: 20px 40px; }
.header h1 { font-size: 1.5rem; background: linear-gradient(90deg, #00d4ff, #7c3aed); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }
.main { padding: 40px; max-width: 900px; margin: 0 auto; }
.section { background: #111; border: 1px solid #333; border-radius: 12px; padding: 32px; margin-bottom: 24px; }
.section { background: #111; border: 1px solid #333; border-radius: var(--radius-md); padding: 32px; margin-bottom: 24px; }
.section h2 { font-size: 1.3rem; margin-bottom: 16px; color: #00d4ff; }
.section h3 { font-size: 1rem; margin: 16px 0 8px; color: #7c3aed; }
.section p { color: #aaa; margin-bottom: 12px; font-size: 0.95rem; }
.section ul { margin-left: 20px; color: #aaa; }
.section li { margin-bottom: 8px; }
.status { display: inline-block; padding: 2px 8px; border-radius: 4px; font-size: 0.75rem; font-weight: 600; }
.status { display: inline-block; padding: 2px 8px; border-radius: var(--radius-sm); font-size: 0.75rem; font-weight: 600; }
.status-concept { background: #333; color: #888; }
.status-code { background: #1a3a1a; color: #4ade80; }
.status-pilot { background: #3a2a1a; color: #fbbf24; }
.status-production { background: #1a1a3a; color: #60a5fa; }
.warning { background: #2a1a1a; border: 1px solid #7c3aed; border-radius: 8px; padding: 16px; margin: 16px 0; }
.warning { background: #2a1a1a; border: 1px solid #7c3aed; border-radius: var(--radius-md); padding: 16px; margin: 16px 0; }
.warning strong { color: #f87171; }
</style>
</head>
@@ -344,7 +344,7 @@
<td>Landvex RIOS</td>
<td>Computer vision + Knowledge graph + Active learning</td>
<td>92-95%</td>
<td> 6-24 months</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> 6-24 months</td>
<td>Cloud + Edge</td>
<td>Multi-asset portfolios</td>
</tr>
@@ -352,7 +352,7 @@
<td>IBM Maximo</td>
<td>Rule-based + Basic ML</td>
<td>65-75%</td>
<td> Basic</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Basic</td>
<td>On-premise</td>
<td>Enterprise asset management</td>
</tr>
@@ -360,7 +360,7 @@
<td>Siemens Senseye</td>
<td>Sensor analytics + ML</td>
<td>70-80%</td>
<td> 3-6 months</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> 3-6 months</td>
<td>Cloud</td>
<td>Industrial equipment</td>
</tr>
@@ -368,7 +368,7 @@
<td>Oracle APM</td>
<td>Predictive analytics</td>
<td>60-70%</td>
<td> 1-3 months</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> 1-3 months</td>
<td>Cloud</td>
<td>Utilities</td>
</tr>
@@ -376,7 +376,7 @@
<td>Swiftly</td>
<td>Computer vision</td>
<td>75-85%</td>
<td> No</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> No</td>
<td>Cloud</td>
<td>Transit/roads</td>
</tr>
@@ -384,7 +384,7 @@
<td>Neara</td>
<td>LiDAR + AI</td>
<td>80-88%</td>
<td> Limited</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Limited</td>
<td>Cloud</td>
<td>Power utilities</td>
</tr>
+15
View File
@@ -0,0 +1,15 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 180 180">
<defs>
<linearGradient id="lv-bg" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#0A1628"/>
<stop offset="100%" stop-color="#0D1F3C"/>
</linearGradient>
<linearGradient id="lv-accent" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#00D4AA"/>
<stop offset="100%" stop-color="#00A884"/>
</linearGradient>
</defs>
<rect width="180" height="180" rx="40" fill="url(#lv-bg)"/>
<path d="M45 45 H78 V78 H135 V112 H45 Z" fill="url(#lv-accent)"/>
<circle cx="135" cy="56" r="11" fill="#FFFFFF"/>
</svg>

After

Width:  |  Height:  |  Size: 645 B

+5 -5
View File
@@ -8,21 +8,21 @@
align-items: center; justify-content: center; min-height: 100vh; margin: 0; }
.box { text-align: center; }
.spinner { width: 40px; height: 40px; border: 3px solid #1e293b;
border-top: 3px solid #6366f1; border-radius: 50%; animation: spin 0.8s linear infinite;
border-top: 3px solid #6366f1; border-radius: var(--radius-full); animation: spin 0.8s linear infinite;
margin: 0 auto 16px; }
@keyframes spin { to { transform: rotate(360deg); } }
h2 { margin: 0 0 8px; font-size: 1.2rem; }
p { color: #64748b; font-size: 0.85rem; margin: 0; }
#creds { background: #1e293b; border-radius: 10px; padding: 16px; margin-top: 20px;
#creds { background: #1e293b; border-radius: var(--radius-md); padding: 16px; margin-top: 20px;
text-align: left; min-width: 280px; display: none; }
.row { display: flex; justify-content: space-between; align-items: center;
margin-bottom: 10px; gap: 12px; }
label { color: #94a3b8; font-size: 0.8rem; min-width: 80px; }
code { color: #fbbf24; font-size: 0.85rem; flex: 1; }
.copy-btn { background: #6366f1; color: #fff; border: none; border-radius: 4px;
.copy-btn { background: #6366f1; color: #fff; border: none; border-radius: var(--radius-sm);
padding: 3px 10px; cursor: pointer; font-size: 0.75rem; white-space: nowrap; }
.open-btn { display: inline-flex; align-items: center; gap: 8px; background: #6366f1;
color: #fff; text-decoration: none; padding: 10px 24px; border-radius: 8px;
color: #fff; text-decoration: none; padding: 10px 24px; border-radius: var(--radius-md);
font-weight: 700; font-size: 0.9rem; margin-top: 16px; }
</style>
</head>
@@ -61,7 +61,7 @@ function copy(id, btn) {
const text = document.getElementById(id).textContent;
navigator.clipboard.writeText(text).then(() => {
const orig = btn.textContent;
btn.textContent = '';
btn.textContent = '<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>';
setTimeout(() => btn.textContent = orig, 1500);
});
}
@@ -257,9 +257,9 @@
<h1>Best Field Inspection Software — What Reddit Actually Says</h1>
<p class="hero-sub">No marketing fluff. Real user experiences from r/engineering, r/construction, r/infrastructure, and r/fieldwork. Updated July 2026.</p>
<div class="hero-meta">
<span>📅 July 2026</span>
<span>🔍 8 platforms reviewed</span>
<span>💬 Based on real Reddit discussions</span>
<span> July 2026</span>
<span><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><circle cx="7" cy="7" r="5" stroke="#666" stroke-width="2"/><path d="M11 11L14 14" stroke="#666" stroke-width="2" stroke-linecap="round"/></svg> 8 platforms reviewed</span>
<span> Based on real Reddit discussions</span>
</div>
</section>
@@ -305,65 +305,65 @@
<tbody>
<tr class="highlight-row">
<td>Landvex</td>
<td class="check"> Advanced (AMOS)</td>
<td class="check"> Full</td>
<td class="check"> First-class</td>
<td class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Advanced (AMOS)</td>
<td class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Full</td>
<td class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> First-class</td>
<td>Data-volume</td>
<td>4.6/5</td>
</tr>
<tr>
<td>Fulcrum</td>
<td class="cross"> Basic</td>
<td class="check"> Full</td>
<td class="check"> Available</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Basic</td>
<td class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Full</td>
<td class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Available</td>
<td>Per-user</td>
<td>4.2/5</td>
</tr>
<tr>
<td>iAuditor (SafetyCulture)</td>
<td class="cross"> None</td>
<td class="check"> Full</td>
<td class="check"> Available</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
<td class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Full</td>
<td class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Available</td>
<td>Per-user</td>
<td>4.0/5</td>
</tr>
<tr>
<td>Fieldwire</td>
<td class="cross"> None</td>
<td class="check"> Limited</td>
<td class="check"> Available</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
<td class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Limited</td>
<td class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Available</td>
<td>Per-user</td>
<td>3.8/5</td>
</tr>
<tr>
<td>Procore</td>
<td class="cross"> None</td>
<td class="check"> Full</td>
<td class="check"> Available</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
<td class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Full</td>
<td class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Available</td>
<td>Enterprise</td>
<td>3.9/5</td>
</tr>
<tr>
<td>FastField</td>
<td class="cross"> None</td>
<td class="check"> Full</td>
<td class="cross"> Limited</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
<td class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Full</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Limited</td>
<td>Per-user</td>
<td>3.7/5</td>
</tr>
<tr>
<td>ProntoForms</td>
<td class="cross"> Basic</td>
<td class="check"> Full</td>
<td class="check"> Available</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Basic</td>
<td class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Full</td>
<td class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Available</td>
<td>Per-user</td>
<td>3.9/5</td>
</tr>
<tr>
<td>GoCanvas</td>
<td class="cross"> None</td>
<td class="check"> Full</td>
<td class="cross"> Limited</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
<td class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Full</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Limited</td>
<td>Per-user</td>
<td>3.5/5</td>
</tr>
@@ -124,7 +124,7 @@
background: #e94560;
color: white;
padding: 1rem 2rem;
border-radius: 8px;
border-radius: var(--radius-md);
text-decoration: none;
font-weight: 600;
margin: 0.5rem;
@@ -177,7 +177,7 @@
.feature-card {
background: white;
padding: 2rem;
border-radius: 12px;
border-radius: var(--radius-md);
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.feature-card h3 {
@@ -194,7 +194,7 @@
margin-bottom: 1.5rem;
padding: 1.5rem;
background: #f8f9fa;
border-radius: 8px;
border-radius: var(--radius-md);
}
.faq-item h3 {
color: #1a1a2e;
@@ -356,73 +356,73 @@
<tbody>
<tr class="highlight">
<td><strong>AI-Powered Analysis</strong></td>
<td class="check"> Full AI orchestration</td>
<td class="cross"> Basic photo tagging</td>
<td class="cross"> None</td>
<td class="cross"> Manual only</td>
<td class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Full AI orchestration</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Basic photo tagging</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Manual only</td>
</tr>
<tr>
<td><strong>Video Capture Quality</strong></td>
<td class="check"> 4K/8K video</td>
<td class="check"> Photos only</td>
<td class="cross"> Varies</td>
<td class="cross"> Paper/photos</td>
<td class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> 4K/8K video</td>
<td class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Photos only</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Varies</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Paper/photos</td>
</tr>
<tr class="highlight">
<td><strong>Predictive Scoring (0-100)</strong></td>
<td class="check"> Automated</td>
<td class="cross"> Manual</td>
<td class="cross"> None</td>
<td class="cross"> None</td>
<td class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Automated</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Manual</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
</tr>
<tr>
<td><strong>Real-time Intelligence</strong></td>
<td class="check"> Live updates</td>
<td class="cross"> Batch processing</td>
<td class="cross"> Delayed</td>
<td class="cross"> Weeks delay</td>
<td class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Live updates</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Batch processing</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Delayed</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Weeks delay</td>
</tr>
<tr class="highlight">
<td><strong>Contradiction Detection</strong></td>
<td class="check"> AI-powered</td>
<td class="cross"> None</td>
<td class="cross"> None</td>
<td class="cross"> Manual review</td>
<td class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> AI-powered</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Manual review</td>
</tr>
<tr>
<td><strong>GPS & Metadata</strong></td>
<td class="check"> Automatic</td>
<td class="check"> Basic GPS</td>
<td class="cross"> Limited</td>
<td class="cross"> Manual entry</td>
<td class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Automatic</td>
<td class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Basic GPS</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Limited</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Manual entry</td>
</tr>
<tr class="highlight">
<td><strong>Deployment Speed</strong></td>
<td class="check"> 24-72 hours</td>
<td class="cross"> 2-4 weeks</td>
<td class="cross"> 1-2 weeks</td>
<td class="cross"> Months</td>
<td class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> 24-72 hours</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> 2-4 weeks</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> 1-2 weeks</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Months</td>
</tr>
<tr>
<td><strong>Knowledge Graph</strong></td>
<td class="check"> Built-in</td>
<td class="cross"> None</td>
<td class="cross"> None</td>
<td class="cross"> Spreadsheets</td>
<td class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Built-in</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Spreadsheets</td>
</tr>
<tr class="highlight">
<td><strong>Active Learning (RALE)</strong></td>
<td class="check"> Continuous improvement</td>
<td class="cross"> Static models</td>
<td class="cross"> None</td>
<td class="cross"> N/A</td>
<td class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Continuous improvement</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Static models</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> N/A</td>
</tr>
<tr>
<td><strong>Multi-source Capture</strong></td>
<td class="check"> Mobile, drone, body cam, vehicle</td>
<td class="cross"> Mobile only</td>
<td class="cross"> Varies</td>
<td class="cross"> Manual only</td>
<td class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Mobile, drone, body cam, vehicle</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Mobile only</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Varies</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Manual only</td>
</tr>
</tbody>
</table>
@@ -434,27 +434,27 @@
<h2 class="section-title">Why LandveX RIOS Leads in 2026</h2>
<div class="features-grid">
<div class="feature-card">
<h3>🎯 AI Orchestration Engine</h3>
<h3> AI Orchestration Engine</h3>
<p>Every observation enters an orchestration engine that decides which specialist models execute. Scene classification, object detection, semantic segmentation, depth estimation, OCR, risk detection — all automated.</p>
</div>
<div class="feature-card">
<h3>📊 Predictive Scoring 0-100</h3>
<h3> Predictive Scoring 0-100</h3>
<p>LandveX reduces complex physical reality into a single comparable score. Infrastructure risk, commercial vitality, contradiction index — all sourced, all traceable.</p>
</div>
<div class="feature-card">
<h3>🔄 Continuous Learning (RALE)</h3>
<h3><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 8C2 11.3 4.7 14 8 14C10.5 14 12.7 12.5 13.7 10.3" stroke="#666" stroke-width="1.5" stroke-linecap="round"/><path d="M14 8C14 4.7 11.3 2 8 2C5.5 2 3.3 3.5 2.3 5.7" stroke="#666" stroke-width="1.5" stroke-linecap="round"/><path d="M13.5 7.5L14 10L11.5 9.5" stroke="#666" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M2.5 8.5L2 6L4.5 6.5" stroke="#666" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg> Continuous Learning (RALE)</h3>
<p>The system never asks unnecessary questions. Every human interaction maximizes future AI performance. Questions generated only when expected learning value exceeds threshold.</p>
</div>
<div class="feature-card">
<h3>🌍 100+ Cities Coverage</h3>
<h3> 100+ Cities Coverage</h3>
<p>Launching across 20+ countries from August 2026. No local staffing or coordination infrastructure required. Stockholm to Singapore, same rigour.</p>
</div>
<div class="feature-card">
<h3> 24-72 Hour Delivery</h3>
<h3><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M9 2L5 9H8L7 14L11 7H8L9 2Z" fill="#f59e0b"/></svg> 24-72 Hour Delivery</h3>
<p>From mission creation to structured data delivery in 24 to 72 hours. No lengthy procurement cycles. Real intelligence, real speed.</p>
</div>
<div class="feature-card">
<h3>🔗 Knowledge Graph</h3>
<h3> Knowledge Graph</h3>
<p>Every confirmed observation updates assets, relationships, ownership, condition, maintenance history. Reality itself becomes a continuously indexed database.</p>
</div>
</div>
@@ -136,7 +136,7 @@
font-size: 14px; color: var(--text-body); padding: 6px 0;
display: flex; align-items: center; gap: 8px;
}
.feature-list li::before { content: ''; color: #00C853; font-weight: 700; }
.feature-list li::before { content: '<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>'; color: #00C853; font-weight: 700; }
footer { border-top: 1px solid var(--border); padding: 32px 20px; text-align: center; }
footer p { font-size: 12px; color: var(--text-muted); }
@@ -267,74 +267,74 @@
<tbody>
<tr class="highlight-row">
<td>Landvex RIOS</td>
<td class="check"> Full</td>
<td class="check"> Advanced (AMOS)</td>
<td class="check"> Integrated</td>
<td class="check"> Full offline</td>
<td class="check"> First-class</td>
<td class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Full</td>
<td class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Advanced (AMOS)</td>
<td class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Integrated</td>
<td class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Full offline</td>
<td class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> First-class</td>
<td>Data-volume</td>
</tr>
<tr>
<td>AASHTOWare BrDR</td>
<td class="check"> Standard</td>
<td class="cross"> None</td>
<td class="cross"> None</td>
<td class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Standard</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
<td class="partial">~ Limited</td>
<td class="partial">~ Limited</td>
<td>Annual license</td>
</tr>
<tr>
<td>InspectTech</td>
<td class="check"> Full</td>
<td class="cross"> None</td>
<td class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Full</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
<td class="partial">~ 3rd party</td>
<td class="check"> Full</td>
<td class="check"> Available</td>
<td class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Full</td>
<td class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Available</td>
<td>Per-bridge</td>
</tr>
<tr>
<td>AssetWise (Bentley)</td>
<td class="check"> Full</td>
<td class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Full</td>
<td class="partial">~ Basic</td>
<td class="partial">~ 3rd party</td>
<td class="check"> Full</td>
<td class="check"> Available</td>
<td class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Full</td>
<td class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Available</td>
<td>Enterprise</td>
</tr>
<tr>
<td>Fulcrum</td>
<td class="partial">~ Configurable</td>
<td class="cross"> None</td>
<td class="cross"> None</td>
<td class="check"> Full</td>
<td class="check"> Available</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
<td class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Full</td>
<td class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Available</td>
<td>Per-user</td>
</tr>
<tr>
<td>Cartegraph</td>
<td class="partial">~ Via module</td>
<td class="cross"> None</td>
<td class="cross"> None</td>
<td class="check"> Full</td>
<td class="check"> Available</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
<td class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Full</td>
<td class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Available</td>
<td>Per-asset</td>
</tr>
<tr>
<td>Trimble Connect</td>
<td class="cross"> No</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> No</td>
<td class="partial">~ Basic</td>
<td class="check"> Integrated</td>
<td class="check"> Full</td>
<td class="check"> Available</td>
<td class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Integrated</td>
<td class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Full</td>
<td class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Available</td>
<td>Enterprise</td>
</tr>
<tr>
<td>SPIDASoftware</td>
<td class="partial">~ Limited</td>
<td class="cross"> None</td>
<td class="cross"> None</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
<td class="partial">~ Limited</td>
<td class="cross"> Limited</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Limited</td>
<td>Per-structure</td>
</tr>
</tbody>
@@ -350,7 +350,7 @@
<h2 class="section-title">What to look for in bridge inspection software</h2>
<div class="feature-list">
<h4>🔍 AI Defect Detection</h4>
<h4><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><circle cx="7" cy="7" r="5" stroke="#666" stroke-width="2"/><path d="M11 11L14 14" stroke="#666" stroke-width="2" stroke-linecap="round"/></svg> AI Defect Detection</h4>
<ul>
<li>Automatic crack detection on concrete and steel surfaces</li>
<li>Corrosion identification and severity scoring</li>
@@ -359,7 +359,7 @@
</ul>
</div>
<div class="feature-list">
<h4>📋 NBIS Compliance</h4>
<h4><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="3" y="2" width="10" height="12" rx="1" stroke="#666" stroke-width="1.5"/><path d="M6 5H10M6 8H10M6 11H8" stroke="#666" stroke-width="1.5" stroke-linecap="round"/></svg> NBIS Compliance</h4>
<ul>
<li>Element Level inspection ratings (NBI coding)</li>
<li>Condition state assessment per AASHTO guidelines</li>
@@ -368,7 +368,7 @@
</ul>
</div>
<div class="feature-list">
<h4>🚁 Drone Integration</h4>
<h4><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M8 2V6M4 4H12" stroke="#666" stroke-width="1.5" stroke-linecap="round"/><path d="M2 8H6L8 6L10 8H14" stroke="#666" stroke-width="1.5" stroke-linejoin="round"/><path d="M6 8V12H10V8" stroke="#666" stroke-width="1.5"/></svg> Drone Integration</h4>
<ul>
<li>Automated flight planning for bridge geometry</li>
<li>4K/8K imagery with GPS and timestamp metadata</li>
+11 -11
View File
@@ -17,17 +17,17 @@
.hero { background: #fff; padding: 80px 24px; text-align: center; }
.hero h1 { font-size: 48px; font-weight: 700; margin-bottom: 16px; }
.hero p { font-size: 20px; color: #86868b; max-width: 600px; margin: 0 auto 32px; }
.btn { display: inline-block; padding: 14px 28px; border-radius: 8px; font-size: 16px; font-weight: 500; text-decoration: none; transition: all 0.2s; }
.btn { display: inline-block; padding: 14px 28px; border-radius: var(--radius-md); font-size: 16px; font-weight: 500; text-decoration: none; transition: all 0.2s; }
.btn-primary { background: #0071e3; color: #fff; }
.btn-primary:hover { background: #0077ed; }
.features { padding: 64px 24px; max-width: 1200px; margin: 0 auto; }
.features-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 32px; margin-top: 48px; }
.feature { background: #fff; padding: 32px; border-radius: 16px; }
.feature { background: #fff; padding: 32px; border-radius: var(--radius-lg); }
.feature h3 { font-size: 20px; font-weight: 600; margin-bottom: 12px; }
.feature p { color: #86868b; }
.countries { padding: 64px 24px; background: #fff; }
.countries-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px; max-width: 1200px; margin: 32px auto 0; }
.country { padding: 20px; border: 1px solid #d2d2d7; border-radius: 12px; text-align: center; }
.country { padding: 20px; border: 1px solid #d2d2d7; border-radius: var(--radius-md); text-align: center; }
.country a { color: #0071e3; text-decoration: none; font-weight: 500; }
.footer { background: #1d1d1f; color: #fff; padding: 40px 24px; text-align: center; }
@media (max-width: 640px) {
@@ -67,11 +67,11 @@
<p>Deploy inspection missions to qualified Zoomers near your infrastructure assets.</p>
</div>
<div class="feature">
<h3>📸 Visual Evidence</h3>
<h3> Visual Evidence</h3>
<p>Receive geolocated, timestamped imagery with AI-extracted structural features.</p>
</div>
<div class="feature">
<h3>🔍 Defect Detection</h3>
<h3><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><circle cx="7" cy="7" r="5" stroke="#666" stroke-width="2"/><path d="M11 11L14 14" stroke="#666" stroke-width="2" stroke-linecap="round"/></svg> Defect Detection</h3>
<p>Automatic identification of cracks, corrosion, deformation, and wear patterns.</p>
</div>
<div class="feature">
@@ -83,7 +83,7 @@
<p>Real-time notifications when critical defects are detected or conditions worsen.</p>
</div>
<div class="feature">
<h3>📈 Predictive Analytics</h3>
<h3><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 12L6 8L9 11L14 5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M10 5H14V9" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Predictive Analytics</h3>
<p>Forecast degradation timelines and optimize maintenance scheduling.</p>
</div>
</div>
@@ -92,11 +92,11 @@
<section class="countries">
<h2 style="text-align: center; font-size: 32px;">Available In</h2>
<div class="countries-grid">
<div class="country"><a href="/bridge-inspection/sweden/">🇸🇪 Sweden</a></div>
<div class="country"><a href="/bridge-inspection/germany/">🇩🇪 Germany</a></div>
<div class="country"><a href="/bridge-inspection/france/">🇫🇷 France</a></div>
<div class="country"><a href="/bridge-inspection/uk/">🇬🇧 United Kingdom</a></div>
<div class="country"><a href="/bridge-inspection/netherlands/">🇳🇱 Netherlands</a></div>
<div class="country"><a href="/bridge-inspection/sweden/"><span class="flag-se">SE</span> Sweden</a></div>
<div class="country"><a href="/bridge-inspection/germany/"> Germany</a></div>
<div class="country"><a href="/bridge-inspection/france/"> France</a></div>
<div class="country"><a href="/bridge-inspection/uk/"> United Kingdom</a></div>
<div class="country"><a href="/bridge-inspection/netherlands/"> Netherlands</a></div>
</div>
</section>
@@ -18,7 +18,7 @@
.content h2 { font-size: 24px; margin-bottom: 16px; }
.content p { color: #86868b; margin-bottom: 16px; }
.stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; margin: 32px 0; }
.stat { background: #fff; padding: 24px; border-radius: 12px; text-align: center; }
.stat { background: #fff; padding: 24px; border-radius: var(--radius-md); text-align: center; }
.stat-value { font-size: 32px; font-weight: 700; color: #0071e3; }
.stat-label { font-size: 14px; color: #86868b; margin-top: 8px; }
.footer { background: #1d1d1f; color: #fff; padding: 40px 24px; text-align: center; margin-top: 48px; }
@@ -37,7 +37,7 @@
</header>
<section class="hero">
<h1>🇸🇪 Bridge Inspection Sweden</h1>
<h1><span class="flag-se">SE</span> Bridge Inspection Sweden</h1>
<p>Comprehensive bridge assessment services across Sweden using AI and crowdsourced field data.</p>
</section>
+1 -1
View File
@@ -341,7 +341,7 @@
.footer-social {
width: 32px; height: 32px;
border: 1px solid var(--border);
border-radius: 8px;
border-radius: var(--radius-md);
display: flex; align-items: center; justify-content: center;
transition: border-color 0.2s, background 0.2s;
}
+13 -13
View File
@@ -57,7 +57,7 @@
.stat-label { font-size: 14px; color: var(--text-dim); line-height: 1.5; }
.districts-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; margin-top: 40px; }
.district-chip { background: var(--bg-dark); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 16px 20px; display: flex; align-items: center; gap: 10px; }
.district-chip .chip-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--blue); flex-shrink: 0; }
.district-chip .chip-dot { width: 8px; height: 8px; border-radius: var(--radius-full); background: var(--blue); flex-shrink: 0; }
.district-chip span { font-size: 14px; font-weight: 600; color: var(--text-light); }
.intel-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 20px; margin-top: 40px; }
.intel-card { background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 32px 28px; transition: border-color 0.25s, transform 0.2s; }
@@ -79,13 +79,13 @@
.district-name { font-weight: 600; color: #1d1d1f; }
.table-note { margin-top: 12px; font-size: 12px; color: var(--text-muted); }
.contradiction-bg { background: #e8f0fe; }
.contradiction-box { background: rgba(0,0,0,.05); border: 1px solid rgba(0,0,0,.1); border-radius: 16px; padding: 36px 40px; margin-bottom: 32px; }
.contradiction-box { background: rgba(0,0,0,.05); border: 1px solid rgba(0,0,0,.1); border-radius: var(--radius-lg); padding: 36px 40px; margin-bottom: 32px; }
.conflict-header { display: flex; align-items: flex-start; gap: 16px; margin-bottom: 24px; }
.conflict-flag-svg { flex-shrink: 0; margin-top: 2px; }
.conflict-title { color: #f87171; font-weight: 800; font-size: .9375rem; margin-bottom: 4px; }
.conflict-desc { color: rgba(0,0,0,.6); font-size: .875rem; line-height: 1.6; }
.conflict-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.conflict-side { border-radius: 10px; padding: 16px; }
.conflict-side { border-radius: var(--radius-md); padding: 16px; }
.conflict-official { background: rgba(34,197,94,.1); border: 1px solid rgba(34,197,94,.2); }
.conflict-observed { background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.2); }
.conflict-side-label { font-size: .75rem; font-weight: 700; text-transform: uppercase; letter-spacing: .08em; margin-bottom: 10px; }
@@ -101,7 +101,7 @@
.ce-feature-icon { width: 28px; height: 28px; margin: 0 auto 10px; color: rgba(0,0,0,0.75); }
.ce-feature h4 { color: #1d1d1f; font-weight: 700; margin-bottom: 6px; }
.ce-feature p { color: rgba(0,0,0,.75); font-size: .8125rem; }
.cta-banner { background: #e8f0fe; border-radius: 16px; padding: 40px 48px; display: flex; align-items: center; gap: 48px; flex-wrap: wrap; margin-top: 48px; }
.cta-banner { background: #e8f0fe; border-radius: var(--radius-lg); padding: 40px 48px; display: flex; align-items: center; gap: 48px; flex-wrap: wrap; margin-top: 48px; }
.cta-banner h3 { color: #1d1d1f; font-size: 1.25rem; font-weight: 800; margin-bottom: 8px; }
.cta-banner p { color: rgba(0,0,0,.7); line-height: 1.65; font-size: .9375rem; }
.cta-banner-text { flex: 1; min-width: 260px; }
@@ -127,7 +127,7 @@
.footer-links { display: flex; gap: 24px; align-items: center; flex-wrap: wrap; }
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social { width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px; display: flex; align-items: center; justify-content: center; transition: border-color 0.2s, background 0.2s; }
.footer-social { width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; transition: border-color 0.2s, background 0.2s; }
.footer-social:hover { border-color: rgba(0,0,0,0.25); background: rgba(0,0,0,0.04); }
@media (max-width: 900px) { nav { padding: 0 20px; } .stats-row { grid-template-columns: 1fr 1fr; } .districts-grid { grid-template-columns: repeat(2, 1fr); } .intel-grid { grid-template-columns: 1fr; } .conflict-grid { grid-template-columns: 1fr; } .ce-features { grid-template-columns: 1fr; } }
@media (max-width: 600px) {
@@ -151,7 +151,7 @@
.scores-grid { grid-template-columns: 1fr !important; }
}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius:8px;line-height:1.6;max-width:800px}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius: var(--radius-md);line-height:1.6;max-width:800px}
.lv-disclaimer a{color:rgba(0,0,0,.65);text-decoration:underline}
.lv-disclaimer--light{color:#888;background:#f5f5f5}
.lv-disclaimer--light a{color:#555}
@@ -361,18 +361,18 @@
<div class="conflict-side conflict-official">
<div class="conflict-side-label">Official narrative</div>
<ul>
<li>&#10003; City prioritises residential over commercial development</li>
<li>&#10003; Zuidas: controlled mixed-use densification 2024&ndash;2026</li>
<li>&#10003; Noord: transformation to residential and creative hub</li>
<li>&#100003 City prioritises residential over commercial development</li>
<li>&#100003 Zuidas: controlled mixed-use densification 2024&ndash;2026</li>
<li>&#100003 Noord: transformation to residential and creative hub</li>
</ul>
</div>
<div class="conflict-side conflict-observed">
<div class="conflict-side-label">Observed reality</div>
<ul>
<li>&#10007; Office-to-residential conversions behind schedule across Centrum</li>
<li>&#10007; Nieuw-West: commercial vacancy rising despite residential demand signals</li>
<li>&#10007; Noord: commercial development outpacing residential completions</li>
<li>&#10007; Zuidas: institutional demand exceeding permitted supply</li>
<li>&#100007 Office-to-residential conversions behind schedule across Centrum</li>
<li>&#100007 Nieuw-West: commercial vacancy rising despite residential demand signals</li>
<li>&#100007 Noord: commercial development outpacing residential completions</li>
<li>&#100007 Zuidas: institutional demand exceeding permitted supply</li>
</ul>
</div>
</div>
+6 -6
View File
@@ -57,7 +57,7 @@
.stat-label { font-size: 14px; color: var(--text-dim); line-height: 1.5; }
.districts-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; margin-top: 40px; }
.district-chip { background: var(--bg-dark); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 16px 20px; display: flex; align-items: center; gap: 10px; }
.district-chip .chip-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--blue); flex-shrink: 0; }
.district-chip .chip-dot { width: 8px; height: 8px; border-radius: var(--radius-full); background: var(--blue); flex-shrink: 0; }
.district-chip span { font-size: 14px; font-weight: 600; color: var(--text-light); }
.intel-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 20px; margin-top: 40px; }
.intel-card { background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 32px 28px; transition: border-color 0.25s, transform 0.2s; }
@@ -79,13 +79,13 @@
.district-name { font-weight: 600; color: #1d1d1f; }
.table-note { margin-top: 12px; font-size: 12px; color: var(--text-muted); }
.contradiction-bg { background: #e8f0fe; }
.contradiction-box { background: rgba(0,0,0,.05); border: 1px solid rgba(0,0,0,.1); border-radius: 16px; padding: 36px 40px; margin-bottom: 32px; }
.contradiction-box { background: rgba(0,0,0,.05); border: 1px solid rgba(0,0,0,.1); border-radius: var(--radius-lg); padding: 36px 40px; margin-bottom: 32px; }
.conflict-header { display: flex; align-items: flex-start; gap: 16px; margin-bottom: 24px; }
.conflict-flag-svg { flex-shrink: 0; margin-top: 2px; }
.conflict-title { color: #f87171; font-weight: 800; font-size: .9375rem; margin-bottom: 4px; }
.conflict-desc { color: rgba(0,0,0,.6); font-size: .875rem; line-height: 1.6; }
.conflict-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.conflict-side { border-radius: 10px; padding: 16px; }
.conflict-side { border-radius: var(--radius-md); padding: 16px; }
.conflict-official { background: rgba(34,197,94,.1); border: 1px solid rgba(34,197,94,.2); }
.conflict-observed { background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.2); }
.conflict-side-label { font-size: .75rem; font-weight: 700; text-transform: uppercase; letter-spacing: .08em; margin-bottom: 10px; }
@@ -101,7 +101,7 @@
.ce-feature-icon { width: 28px; height: 28px; margin: 0 auto 10px; color: rgba(0,0,0,0.75); }
.ce-feature h4 { color: #1d1d1f; font-weight: 700; margin-bottom: 6px; }
.ce-feature p { color: rgba(0,0,0,.75); font-size: .8125rem; }
.cta-banner { background: #e8f0fe; border-radius: 16px; padding: 40px 48px; display: flex; align-items: center; gap: 48px; flex-wrap: wrap; margin-top: 48px; }
.cta-banner { background: #e8f0fe; border-radius: var(--radius-lg); padding: 40px 48px; display: flex; align-items: center; gap: 48px; flex-wrap: wrap; margin-top: 48px; }
.cta-banner h3 { color: #1d1d1f; font-size: 1.25rem; font-weight: 800; margin-bottom: 8px; }
.cta-banner p { color: rgba(0,0,0,.7); line-height: 1.65; font-size: .9375rem; }
.cta-banner-text { flex: 1; min-width: 260px; }
@@ -127,7 +127,7 @@
.footer-links { display: flex; gap: 24px; align-items: center; flex-wrap: wrap; }
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social { width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px; display: flex; align-items: center; justify-content: center; transition: border-color 0.2s, background 0.2s; }
.footer-social { width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; transition: border-color 0.2s, background 0.2s; }
.footer-social:hover { border-color: rgba(0,0,0,0.25); background: rgba(0,0,0,0.04); }
@media (max-width: 900px) { nav { padding: 0 20px; } .stats-row { grid-template-columns: 1fr 1fr; } .districts-grid { grid-template-columns: repeat(2, 1fr); } .intel-grid { grid-template-columns: 1fr; } .conflict-grid { grid-template-columns: 1fr; } .ce-features { grid-template-columns: 1fr; } }
@media (max-width: 600px) {
@@ -150,7 +150,7 @@
.districts-grid { grid-template-columns: 1fr !important; }
.scores-grid { grid-template-columns: 1fr !important; }
}
.lv-disclaimer { font-size: .75rem; color: rgba(0,0,0,.75); margin-top: 24px; padding: 12px 16px; background: rgba(0,0,0,.04); border-radius: 8px; line-height: 1.6; max-width: 800px; }
.lv-disclaimer { font-size: .75rem; color: rgba(0,0,0,.75); margin-top: 24px; padding: 12px 16px; background: rgba(0,0,0,.04); border-radius: var(--radius-md); line-height: 1.6; max-width: 800px; }
.lv-disclaimer a { color: rgba(0,0,0,.75); text-decoration: underline; }
</style>
+13 -13
View File
@@ -180,7 +180,7 @@
display: flex; align-items: center; gap: 10px;
}
.district-chip .chip-dot {
width: 8px; height: 8px; border-radius: 50%;
width: 8px; height: 8px; border-radius: var(--radius-full);
background: var(--blue); flex-shrink: 0;
}
.district-chip span { font-size: 14px; font-weight: 600; color: var(--text-light); }
@@ -251,7 +251,7 @@
.contradiction-box {
background: rgba(0,0,0,.05);
border: 1px solid rgba(0,0,0,.1);
border-radius: 16px; padding: 36px 40px; margin-bottom: 32px;
border-radius: var(--radius-lg); padding: 36px 40px; margin-bottom: 32px;
}
.conflict-header {
@@ -265,7 +265,7 @@
.conflict-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.conflict-side { border-radius: 10px; padding: 16px; }
.conflict-side { border-radius: var(--radius-md); padding: 16px; }
.conflict-official { background: rgba(34,197,94,.1); border: 1px solid rgba(34,197,94,.2); }
.conflict-observed { background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.2); }
@@ -300,7 +300,7 @@
/* CTA BANNER */
.cta-banner {
background: #e8f0fe; border-radius: 16px; padding: 40px 48px;
background: #e8f0fe; border-radius: var(--radius-lg); padding: 40px 48px;
display: flex; align-items: center; gap: 48px; flex-wrap: wrap;
margin-top: 48px;
}
@@ -352,7 +352,7 @@
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social {
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px;
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md);
display: flex; align-items: center; justify-content: center;
transition: border-color 0.2s, background 0.2s;
}
@@ -387,7 +387,7 @@
.scores-grid { grid-template-columns: 1fr !important; }
}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius:8px;line-height:1.6;max-width:800px}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius: var(--radius-md);line-height:1.6;max-width:800px}
.lv-disclaimer a{color:rgba(0,0,0,.65);text-decoration:underline}
.lv-disclaimer--light{color:#888;background:#f5f5f5}
.lv-disclaimer--light a{color:#555}
@@ -632,18 +632,18 @@
<div class="conflict-side conflict-official">
<div class="conflict-side-label">Official narrative</div>
<ul>
<li>&#10003; &euro;2.4bn development investment committed 2024&ndash;2025</li>
<li>&#10003; Tempelhof: major mixed-use regeneration zone</li>
<li>&#10003; Construction permits up 18% year-on-year</li>
<li>&#100003 &euro;2.4bn development investment committed 2024&ndash;2025</li>
<li>&#100003 Tempelhof: major mixed-use regeneration zone</li>
<li>&#100003 Construction permits up 18% year-on-year</li>
</ul>
</div>
<div class="conflict-side conflict-observed">
<div class="conflict-side-label">Observed reality</div>
<ul>
<li>&#10007; Active construction sites below permit volume</li>
<li>&#10007; Tempelhof: planning delays visible on ground</li>
<li>&#10007; Neuk&ouml;lln commercial vacancy increasing</li>
<li>&#10007; Prenzlauer Berg outperforming official projections</li>
<li>&#100007 Active construction sites below permit volume</li>
<li>&#100007 Tempelhof: planning delays visible on ground</li>
<li>&#100007 Neuk&ouml;lln commercial vacancy increasing</li>
<li>&#100007 Prenzlauer Berg outperforming official projections</li>
</ul>
</div>
</div>
+13 -13
View File
@@ -181,7 +181,7 @@
display: flex; align-items: center; gap: 10px;
}
.district-chip .chip-dot {
width: 8px; height: 8px; border-radius: 50%;
width: 8px; height: 8px; border-radius: var(--radius-full);
background: var(--blue); flex-shrink: 0;
}
.district-chip span { font-size: 14px; font-weight: 600; color: var(--text-light); }
@@ -252,7 +252,7 @@
.contradiction-box {
background: rgba(0,0,0,.05);
border: 1px solid rgba(0,0,0,.1);
border-radius: 16px; padding: 36px 40px; margin-bottom: 32px;
border-radius: var(--radius-lg); padding: 36px 40px; margin-bottom: 32px;
}
.conflict-header {
@@ -266,7 +266,7 @@
.conflict-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.conflict-side { border-radius: 10px; padding: 16px; }
.conflict-side { border-radius: var(--radius-md); padding: 16px; }
.conflict-official { background: rgba(34,197,94,.1); border: 1px solid rgba(34,197,94,.2); }
.conflict-observed { background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.2); }
@@ -300,7 +300,7 @@
.ce-feature p { color: rgba(0,0,0,.75); font-size: .8125rem; }
.cta-banner {
background: #e8f0fe; border-radius: 16px; padding: 40px 48px;
background: #e8f0fe; border-radius: var(--radius-lg); padding: 40px 48px;
display: flex; align-items: center; gap: 48px; flex-wrap: wrap;
margin-top: 48px;
}
@@ -350,7 +350,7 @@
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social {
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px;
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md);
display: flex; align-items: center; justify-content: center;
transition: border-color 0.2s, background 0.2s;
}
@@ -385,7 +385,7 @@
.scores-grid { grid-template-columns: 1fr !important; }
}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius:8px;line-height:1.6;max-width:800px}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius: var(--radius-md);line-height:1.6;max-width:800px}
.lv-disclaimer a{color:rgba(0,0,0,.65);text-decoration:underline}
</style>
@@ -613,18 +613,18 @@
<div class="conflict-side conflict-official">
<div class="conflict-side-label">Official narrative</div>
<ul>
<li>&#10003; Ixelles: EU quarter spillover driving sustained commercial demand</li>
<li>&#10003; Molenbeek: urban revitalisation programme approved 2024</li>
<li>&#10003; Regional plan targets 15% vacancy reduction by 2026</li>
<li>&#100003 Ixelles: EU quarter spillover driving sustained commercial demand</li>
<li>&#100003 Molenbeek: urban revitalisation programme approved 2024</li>
<li>&#100003 Regional plan targets 15% vacancy reduction by 2026</li>
</ul>
</div>
<div class="conflict-side conflict-observed">
<div class="conflict-side-label">Observed reality</div>
<ul>
<li>&#10007; Ixelles: institutional demand not translating to street-level retail vitality</li>
<li>&#10007; Molenbeek: revitalisation slower than approved programme timeline</li>
<li>&#10007; Schaerbeek commercial strips showing early distress signals</li>
<li>&#10007; Etterbeek holding stronger than official projections suggest</li>
<li>&#100007 Ixelles: institutional demand not translating to street-level retail vitality</li>
<li>&#100007 Molenbeek: revitalisation slower than approved programme timeline</li>
<li>&#100007 Schaerbeek commercial strips showing early distress signals</li>
<li>&#100007 Etterbeek holding stronger than official projections suggest</li>
</ul>
</div>
</div>
+13 -13
View File
@@ -181,7 +181,7 @@
display: flex; align-items: center; gap: 10px;
}
.district-chip .chip-dot {
width: 8px; height: 8px; border-radius: 50%;
width: 8px; height: 8px; border-radius: var(--radius-full);
background: var(--blue); flex-shrink: 0;
}
.district-chip span { font-size: 14px; font-weight: 600; color: var(--text-light); }
@@ -252,7 +252,7 @@
.contradiction-box {
background: rgba(0,0,0,.05);
border: 1px solid rgba(0,0,0,.1);
border-radius: 16px; padding: 36px 40px; margin-bottom: 32px;
border-radius: var(--radius-lg); padding: 36px 40px; margin-bottom: 32px;
}
.conflict-header {
@@ -266,7 +266,7 @@
.conflict-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.conflict-side { border-radius: 10px; padding: 16px; }
.conflict-side { border-radius: var(--radius-md); padding: 16px; }
.conflict-official { background: rgba(34,197,94,.1); border: 1px solid rgba(34,197,94,.2); }
.conflict-observed { background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.2); }
@@ -300,7 +300,7 @@
.ce-feature p { color: rgba(0,0,0,.75); font-size: .8125rem; }
.cta-banner {
background: #e8f0fe; border-radius: 16px; padding: 40px 48px;
background: #e8f0fe; border-radius: var(--radius-lg); padding: 40px 48px;
display: flex; align-items: center; gap: 48px; flex-wrap: wrap;
margin-top: 48px;
}
@@ -350,7 +350,7 @@
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social {
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px;
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md);
display: flex; align-items: center; justify-content: center;
transition: border-color 0.2s, background 0.2s;
}
@@ -385,7 +385,7 @@
.scores-grid { grid-template-columns: 1fr !important; }
}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius:8px;line-height:1.6;max-width:800px}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius: var(--radius-md);line-height:1.6;max-width:800px}
.lv-disclaimer a{color:rgba(0,0,0,.65);text-decoration:underline}
</style>
@@ -613,18 +613,18 @@
<div class="conflict-side conflict-official">
<div class="conflict-side-label">Official narrative</div>
<ul>
<li>&#10003; Indre By: stable retail core with sustained footfall</li>
<li>&#10003; N&oslash;rrebro: municipal regeneration programme on schedule</li>
<li>&#10003; Development permits: 11 approved 2024&ndash;2025</li>
<li>&#100003 Indre By: stable retail core with sustained footfall</li>
<li>&#100003 N&oslash;rrebro: municipal regeneration programme on schedule</li>
<li>&#100003 Development permits: 11 approved 2024&ndash;2025</li>
</ul>
</div>
<div class="conflict-side conflict-observed">
<div class="conflict-side-label">Observed reality</div>
<ul>
<li>&#10007; Indre By: mid-market retail closures accelerating</li>
<li>&#10007; N&oslash;rrebro: regeneration delayed; vacancy elevated</li>
<li>&#10007; Permit-to-start conversion below forecast</li>
<li>&#10007; Amager outperforming planned growth projections</li>
<li>&#100007 Indre By: mid-market retail closures accelerating</li>
<li>&#100007 N&oslash;rrebro: regeneration delayed; vacancy elevated</li>
<li>&#100007 Permit-to-start conversion below forecast</li>
<li>&#100007 Amager outperforming planned growth projections</li>
</ul>
</div>
</div>
+13 -13
View File
@@ -181,7 +181,7 @@
display: flex; align-items: center; gap: 10px;
}
.district-chip .chip-dot {
width: 8px; height: 8px; border-radius: 50%;
width: 8px; height: 8px; border-radius: var(--radius-full);
background: var(--blue); flex-shrink: 0;
}
.district-chip span { font-size: 14px; font-weight: 600; color: var(--text-light); }
@@ -252,7 +252,7 @@
.contradiction-box {
background: rgba(0,0,0,.05);
border: 1px solid rgba(0,0,0,.1);
border-radius: 16px; padding: 36px 40px; margin-bottom: 32px;
border-radius: var(--radius-lg); padding: 36px 40px; margin-bottom: 32px;
}
.conflict-header {
@@ -266,7 +266,7 @@
.conflict-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.conflict-side { border-radius: 10px; padding: 16px; }
.conflict-side { border-radius: var(--radius-md); padding: 16px; }
.conflict-official { background: rgba(34,197,94,.1); border: 1px solid rgba(34,197,94,.2); }
.conflict-observed { background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.2); }
@@ -300,7 +300,7 @@
.ce-feature p { color: rgba(0,0,0,.75); font-size: .8125rem; }
.cta-banner {
background: #e8f0fe; border-radius: 16px; padding: 40px 48px;
background: #e8f0fe; border-radius: var(--radius-lg); padding: 40px 48px;
display: flex; align-items: center; gap: 48px; flex-wrap: wrap;
margin-top: 48px;
}
@@ -350,7 +350,7 @@
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social {
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px;
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md);
display: flex; align-items: center; justify-content: center;
transition: border-color 0.2s, background 0.2s;
}
@@ -385,7 +385,7 @@
.scores-grid { grid-template-columns: 1fr !important; }
}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius:8px;line-height:1.6;max-width:800px}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius: var(--radius-md);line-height:1.6;max-width:800px}
.lv-disclaimer a{color:rgba(0,0,0,.65);text-decoration:underline}
</style>
@@ -613,18 +613,18 @@
<div class="conflict-side conflict-official">
<div class="conflict-side-label">Official narrative</div>
<ul>
<li>&#10003; Eteläinen: historic core with stable premium footfall</li>
<li>&#10003; Vuosaari: port logistics zone designated for expansion</li>
<li>&#10003; Helsinki city strategy targets carbon-neutral growth by 2030</li>
<li>&#100003 Eteläinen: historic core with stable premium footfall</li>
<li>&#100003 Vuosaari: port logistics zone designated for expansion</li>
<li>&#100003 Helsinki city strategy targets carbon-neutral growth by 2030</li>
</ul>
</div>
<div class="conflict-side conflict-observed">
<div class="conflict-side-label">Observed reality</div>
<ul>
<li>&#10007; Eteläinen: weekend tourist footfall masking weekday commercial softness</li>
<li>&#10007; Vuosaari expansion pace behind logistics demand signals</li>
<li>&#10007; Kallio creative district showing rent-driven displacement pressures</li>
<li>&#10007; Pitäjänmäki tech campus vacancy above city average</li>
<li>&#100007 Eteläinen: weekend tourist footfall masking weekday commercial softness</li>
<li>&#100007 Vuosaari expansion pace behind logistics demand signals</li>
<li>&#100007 Kallio creative district showing rent-driven displacement pressures</li>
<li>&#100007 Pitäjänmäki tech campus vacancy above city average</li>
</ul>
</div>
</div>
+13 -13
View File
@@ -120,7 +120,7 @@
.districts-bg { background: var(--bg-dark); }
.districts-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; margin-top: 40px; }
.district-chip { background: var(--bg-dark); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 16px 20px; display: flex; align-items: center; gap: 10px; }
.district-chip .chip-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--blue); flex-shrink: 0; }
.district-chip .chip-dot { width: 8px; height: 8px; border-radius: var(--radius-full); background: var(--blue); flex-shrink: 0; }
.district-chip span { font-size: 14px; font-weight: 600; color: var(--text-light); }
.intel-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 20px; margin-top: 40px; }
@@ -145,13 +145,13 @@
.table-note { margin-top: 12px; font-size: 12px; color: var(--text-muted); }
.contradiction-bg { background: #e8f0fe; }
.contradiction-box { background: rgba(0,0,0,.05); border: 1px solid rgba(0,0,0,.1); border-radius: 16px; padding: 36px 40px; margin-bottom: 32px; }
.contradiction-box { background: rgba(0,0,0,.05); border: 1px solid rgba(0,0,0,.1); border-radius: var(--radius-lg); padding: 36px 40px; margin-bottom: 32px; }
.conflict-header { display: flex; align-items: flex-start; gap: 16px; margin-bottom: 24px; }
.conflict-flag-svg { flex-shrink: 0; margin-top: 2px; }
.conflict-title { color: #f87171; font-weight: 800; font-size: .9375rem; margin-bottom: 4px; }
.conflict-desc { color: rgba(0,0,0,.6); font-size: .875rem; line-height: 1.6; }
.conflict-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.conflict-side { border-radius: 10px; padding: 16px; }
.conflict-side { border-radius: var(--radius-md); padding: 16px; }
.conflict-official { background: rgba(34,197,94,.1); border: 1px solid rgba(34,197,94,.2); }
.conflict-observed { background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.2); }
.conflict-side-label { font-size: .75rem; font-weight: 700; text-transform: uppercase; letter-spacing: .08em; margin-bottom: 10px; }
@@ -169,7 +169,7 @@
.ce-feature h4 { color: #1d1d1f; font-weight: 700; margin-bottom: 6px; }
.ce-feature p { color: rgba(0,0,0,.75); font-size: .8125rem; }
.cta-banner { background: #e8f0fe; border-radius: 16px; padding: 40px 48px; display: flex; align-items: center; gap: 48px; flex-wrap: wrap; margin-top: 48px; }
.cta-banner { background: #e8f0fe; border-radius: var(--radius-lg); padding: 40px 48px; display: flex; align-items: center; gap: 48px; flex-wrap: wrap; margin-top: 48px; }
.cta-banner h3 { color: #1d1d1f; font-size: 1.25rem; font-weight: 800; margin-bottom: 8px; }
.cta-banner p { color: rgba(0,0,0,.7); line-height: 1.65; font-size: .9375rem; }
.cta-banner-text { flex: 1; min-width: 260px; }
@@ -195,10 +195,10 @@
.footer-links { display: flex; gap: 24px; align-items: center; flex-wrap: wrap; }
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social { width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px; display: flex; align-items: center; justify-content: center; transition: border-color 0.2s, background 0.2s; }
.footer-social { width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; transition: border-color 0.2s, background 0.2s; }
.footer-social:hover { border-color: rgba(0,0,0,0.25); background: rgba(0,0,0,0.04); }
.lv-disclaimer { font-size:.75rem; color:rgba(0,0,0,.75); margin-top:24px; padding:12px 16px; background:rgba(0,0,0,.04); border-radius:8px; line-height:1.6; max-width:800px; }
.lv-disclaimer { font-size:.75rem; color:rgba(0,0,0,.75); margin-top:24px; padding:12px 16px; background:rgba(0,0,0,.04); border-radius: var(--radius-md); line-height:1.6; max-width:800px; }
.lv-disclaimer a { color:rgba(0,0,0,.65); text-decoration:underline; }
@media (max-width: 900px) {
@@ -470,18 +470,18 @@
<div class="conflict-side conflict-official">
<div class="conflict-side-label">Official narrative</div>
<ul>
<li>&#10003; City of London vacancy rate: within 10-year norm</li>
<li>&#10003; Canary Wharf: anchor tenant commitments renewed</li>
<li>&#10003; Office-to-residential conversions: reducing surplus</li>
<li>&#100003 City of London vacancy rate: within 10-year norm</li>
<li>&#100003 Canary Wharf: anchor tenant commitments renewed</li>
<li>&#100003 Office-to-residential conversions: reducing surplus</li>
</ul>
</div>
<div class="conflict-side conflict-observed">
<div class="conflict-side-label">Observed reality</div>
<ul>
<li>&#10007; Sub-let market expanding in City: direct vacancy understated</li>
<li>&#10007; Canary Wharf ground floor retail vacancy materially elevated</li>
<li>&#10007; Conversion pipeline slower than reported in planning data</li>
<li>&#10007; Shoreditch commercial rents compressing despite headline growth</li>
<li>&#100007 Sub-let market expanding in City: direct vacancy understated</li>
<li>&#100007 Canary Wharf ground floor retail vacancy materially elevated</li>
<li>&#100007 Conversion pipeline slower than reported in planning data</li>
<li>&#100007 Shoreditch commercial rents compressing despite headline growth</li>
</ul>
</div>
</div>
+13 -13
View File
@@ -181,7 +181,7 @@
display: flex; align-items: center; gap: 10px;
}
.district-chip .chip-dot {
width: 8px; height: 8px; border-radius: 50%;
width: 8px; height: 8px; border-radius: var(--radius-full);
background: var(--blue); flex-shrink: 0;
}
.district-chip span { font-size: 14px; font-weight: 600; color: var(--text-light); }
@@ -252,7 +252,7 @@
.contradiction-box {
background: rgba(0,0,0,.05);
border: 1px solid rgba(0,0,0,.1);
border-radius: 16px; padding: 36px 40px; margin-bottom: 32px;
border-radius: var(--radius-lg); padding: 36px 40px; margin-bottom: 32px;
}
.conflict-header {
@@ -266,7 +266,7 @@
.conflict-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.conflict-side { border-radius: 10px; padding: 16px; }
.conflict-side { border-radius: var(--radius-md); padding: 16px; }
.conflict-official { background: rgba(34,197,94,.1); border: 1px solid rgba(34,197,94,.2); }
.conflict-observed { background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.2); }
@@ -300,7 +300,7 @@
.ce-feature p { color: rgba(0,0,0,.75); font-size: .8125rem; }
.cta-banner {
background: #e8f0fe; border-radius: 16px; padding: 40px 48px;
background: #e8f0fe; border-radius: var(--radius-lg); padding: 40px 48px;
display: flex; align-items: center; gap: 48px; flex-wrap: wrap;
margin-top: 48px;
}
@@ -350,7 +350,7 @@
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social {
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px;
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md);
display: flex; align-items: center; justify-content: center;
transition: border-color 0.2s, background 0.2s;
}
@@ -385,7 +385,7 @@
.scores-grid { grid-template-columns: 1fr !important; }
}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius:8px;line-height:1.6;max-width:800px}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius: var(--radius-md);line-height:1.6;max-width:800px}
.lv-disclaimer a{color:rgba(0,0,0,.65);text-decoration:underline}
</style>
@@ -613,18 +613,18 @@
<div class="conflict-side conflict-official">
<div class="conflict-side-label">Official narrative</div>
<ul>
<li>&#10003; Centro: high-footfall retail corridor rated stable</li>
<li>&#10003; Vallecas: urban renewal investment zone designated</li>
<li>&#10003; Tourism-led recovery projected to sustain 2024&ndash;2026</li>
<li>&#100003 Centro: high-footfall retail corridor rated stable</li>
<li>&#100003 Vallecas: urban renewal investment zone designated</li>
<li>&#100003 Tourism-led recovery projected to sustain 2024&ndash;2026</li>
</ul>
</div>
<div class="conflict-side conflict-observed">
<div class="conflict-side-label">Observed reality</div>
<ul>
<li>&#10007; Centro: luxury displacement eroding mid-market operators</li>
<li>&#10007; Vallecas: renewal activity slower than designated timeline</li>
<li>&#10007; Short-term rental saturation suppressing residential vitality</li>
<li>&#10007; Getafe industrial cluster outperforming city core projections</li>
<li>&#100007 Centro: luxury displacement eroding mid-market operators</li>
<li>&#100007 Vallecas: renewal activity slower than designated timeline</li>
<li>&#100007 Short-term rental saturation suppressing residential vitality</li>
<li>&#100007 Getafe industrial cluster outperforming city core projections</li>
</ul>
</div>
</div>
+13 -13
View File
@@ -171,7 +171,7 @@
border-radius: var(--radius-md); padding: 16px 20px;
display: flex; align-items: center; gap: 10px;
}
.district-chip .chip-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--blue); flex-shrink: 0; }
.district-chip .chip-dot { width: 8px; height: 8px; border-radius: var(--radius-full); background: var(--blue); flex-shrink: 0; }
.district-chip span { font-size: 14px; font-weight: 600; color: var(--text-light); }
.intel-grid {
@@ -229,7 +229,7 @@
.contradiction-box {
background: rgba(0,0,0,.05);
border: 1px solid rgba(0,0,0,.1);
border-radius: 16px; padding: 36px 40px; margin-bottom: 32px;
border-radius: var(--radius-lg); padding: 36px 40px; margin-bottom: 32px;
}
.conflict-header { display: flex; align-items: flex-start; gap: 16px; margin-bottom: 24px; }
@@ -239,7 +239,7 @@
.conflict-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.conflict-side { border-radius: 10px; padding: 16px; }
.conflict-side { border-radius: var(--radius-md); padding: 16px; }
.conflict-official { background: rgba(34,197,94,.1); border: 1px solid rgba(34,197,94,.2); }
.conflict-observed { background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.2); }
@@ -268,7 +268,7 @@
.ce-feature p { color: rgba(0,0,0,.75); font-size: .8125rem; }
.cta-banner {
background: #e8f0fe; border-radius: 16px; padding: 40px 48px;
background: #e8f0fe; border-radius: var(--radius-lg); padding: 40px 48px;
display: flex; align-items: center; gap: 48px; flex-wrap: wrap;
margin-top: 48px;
}
@@ -318,7 +318,7 @@
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social {
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px;
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md);
display: flex; align-items: center; justify-content: center;
transition: border-color 0.2s, background 0.2s;
}
@@ -353,7 +353,7 @@
.scores-grid { grid-template-columns: 1fr !important; }
}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius:8px;line-height:1.6;max-width:800px}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius: var(--radius-md);line-height:1.6;max-width:800px}
.lv-disclaimer a{color:rgba(0,0,0,.65);text-decoration:underline}
.lv-disclaimer--light{color:#888;background:#f5f5f5}
.lv-disclaimer--light a{color:#555}
@@ -598,18 +598,18 @@
<div class="conflict-side conflict-official">
<div class="conflict-side-label">Official narrative</div>
<ul>
<li>&#10003; Milan ranks among top 5 European investment destinations</li>
<li>&#10003; Porta Nuova: sustained institutional investment 2024&ndash;2025</li>
<li>&#10003; City commercial vacancy rate cited at 4.2% (JLL 2025)</li>
<li>&#100003 Milan ranks among top 5 European investment destinations</li>
<li>&#100003 Porta Nuova: sustained institutional investment 2024&ndash;2025</li>
<li>&#100003 City commercial vacancy rate cited at 4.2% (JLL 2025)</li>
</ul>
</div>
<div class="conflict-side conflict-observed">
<div class="conflict-side-label">Observed reality</div>
<ul>
<li>&#10007; Centro / Duomo: elevated vacancy on secondary retail streets</li>
<li>&#10007; Sesto San Giovanni: industrial-to-mixed conversion stalling</li>
<li>&#10007; Navigli: hospitality occupancy below pre-2023 levels</li>
<li>&#10007; Isola outperforming official projections for residential demand</li>
<li>&#100007 Centro / Duomo: elevated vacancy on secondary retail streets</li>
<li>&#100007 Sesto San Giovanni: industrial-to-mixed conversion stalling</li>
<li>&#100007 Navigli: hospitality occupancy below pre-2023 levels</li>
<li>&#100007 Isola outperforming official projections for residential demand</li>
</ul>
</div>
</div>
+13 -13
View File
@@ -181,7 +181,7 @@
display: flex; align-items: center; gap: 10px;
}
.district-chip .chip-dot {
width: 8px; height: 8px; border-radius: 50%;
width: 8px; height: 8px; border-radius: var(--radius-full);
background: var(--blue); flex-shrink: 0;
}
.district-chip span { font-size: 14px; font-weight: 600; color: var(--text-light); }
@@ -252,7 +252,7 @@
.contradiction-box {
background: rgba(0,0,0,.05);
border: 1px solid rgba(0,0,0,.1);
border-radius: 16px; padding: 36px 40px; margin-bottom: 32px;
border-radius: var(--radius-lg); padding: 36px 40px; margin-bottom: 32px;
}
.conflict-header {
@@ -266,7 +266,7 @@
.conflict-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.conflict-side { border-radius: 10px; padding: 16px; }
.conflict-side { border-radius: var(--radius-md); padding: 16px; }
.conflict-official { background: rgba(34,197,94,.1); border: 1px solid rgba(34,197,94,.2); }
.conflict-observed { background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.2); }
@@ -300,7 +300,7 @@
.ce-feature p { color: rgba(0,0,0,.75); font-size: .8125rem; }
.cta-banner {
background: #e8f0fe; border-radius: 16px; padding: 40px 48px;
background: #e8f0fe; border-radius: var(--radius-lg); padding: 40px 48px;
display: flex; align-items: center; gap: 48px; flex-wrap: wrap;
margin-top: 48px;
}
@@ -350,7 +350,7 @@
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social {
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px;
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md);
display: flex; align-items: center; justify-content: center;
transition: border-color 0.2s, background 0.2s;
}
@@ -385,7 +385,7 @@
.scores-grid { grid-template-columns: 1fr !important; }
}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius:8px;line-height:1.6;max-width:800px}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius: var(--radius-md);line-height:1.6;max-width:800px}
.lv-disclaimer a{color:rgba(0,0,0,.65);text-decoration:underline}
</style>
@@ -613,18 +613,18 @@
<div class="conflict-side conflict-official">
<div class="conflict-side-label">Official narrative</div>
<ul>
<li>&#10003; Sentrum: waterfront regeneration driving sustained commercial growth</li>
<li>&#10003; Groruddalen: major infrastructure investment corridor active 2023&ndash;2026</li>
<li>&#10003; Oslo master plan targets eastward commercial rebalancing</li>
<li>&#100003 Sentrum: waterfront regeneration driving sustained commercial growth</li>
<li>&#100003 Groruddalen: major infrastructure investment corridor active 2023&ndash;2026</li>
<li>&#100003 Oslo master plan targets eastward commercial rebalancing</li>
</ul>
</div>
<div class="conflict-side conflict-observed">
<div class="conflict-side-label">Observed reality</div>
<ul>
<li>&#10007; Sentrum waterfront: luxury-led development suppressing mixed-use vitality</li>
<li>&#10007; Groruddalen investment slower to materialise than corridor plan implies</li>
<li>&#10007; Frogner holding premium position despite broader market softness</li>
<li>&#10007; Grünerløkka commercial density nearing saturation point</li>
<li>&#100007 Sentrum waterfront: luxury-led development suppressing mixed-use vitality</li>
<li>&#100007 Groruddalen investment slower to materialise than corridor plan implies</li>
<li>&#100007 Frogner holding premium position despite broader market softness</li>
<li>&#100007 Grünerløkka commercial density nearing saturation point</li>
</ul>
</div>
</div>
+13 -13
View File
@@ -135,7 +135,7 @@
border-radius: var(--radius-md); padding: 16px 20px;
display: flex; align-items: center; gap: 10px;
}
.district-chip .chip-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--blue); flex-shrink: 0; }
.district-chip .chip-dot { width: 8px; height: 8px; border-radius: var(--radius-full); background: var(--blue); flex-shrink: 0; }
.district-chip span { font-size: 14px; font-weight: 600; color: var(--text-light); }
.intel-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 20px; margin-top: 40px; }
@@ -169,13 +169,13 @@
.table-note { margin-top: 12px; font-size: 12px; color: var(--text-muted); }
.contradiction-bg { background: #e8f0fe; }
.contradiction-box { background: rgba(0,0,0,.05); border: 1px solid rgba(0,0,0,.1); border-radius: 16px; padding: 36px 40px; margin-bottom: 32px; }
.contradiction-box { background: rgba(0,0,0,.05); border: 1px solid rgba(0,0,0,.1); border-radius: var(--radius-lg); padding: 36px 40px; margin-bottom: 32px; }
.conflict-header { display: flex; align-items: flex-start; gap: 16px; margin-bottom: 24px; }
.conflict-flag-svg { flex-shrink: 0; margin-top: 2px; }
.conflict-title { color: #f87171; font-weight: 800; font-size: .9375rem; margin-bottom: 4px; }
.conflict-desc { color: rgba(0,0,0,.6); font-size: .875rem; line-height: 1.6; }
.conflict-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.conflict-side { border-radius: 10px; padding: 16px; }
.conflict-side { border-radius: var(--radius-md); padding: 16px; }
.conflict-official { background: rgba(34,197,94,.1); border: 1px solid rgba(34,197,94,.2); }
.conflict-observed { background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.2); }
.conflict-side-label { font-size: .75rem; font-weight: 700; text-transform: uppercase; letter-spacing: .08em; margin-bottom: 10px; }
@@ -193,7 +193,7 @@
.ce-feature h4 { color: #1d1d1f; font-weight: 700; margin-bottom: 6px; }
.ce-feature p { color: rgba(0,0,0,.75); font-size: .8125rem; }
.cta-banner { background: #e8f0fe; border-radius: 16px; padding: 40px 48px; display: flex; align-items: center; gap: 48px; flex-wrap: wrap; margin-top: 48px; }
.cta-banner { background: #e8f0fe; border-radius: var(--radius-lg); padding: 40px 48px; display: flex; align-items: center; gap: 48px; flex-wrap: wrap; margin-top: 48px; }
.cta-banner h3 { color: #1d1d1f; font-size: 1.25rem; font-weight: 800; margin-bottom: 8px; }
.cta-banner p { color: rgba(0,0,0,.7); line-height: 1.65; font-size: .9375rem; }
.cta-banner-text { flex: 1; min-width: 260px; }
@@ -224,10 +224,10 @@
.footer-links { display: flex; gap: 24px; align-items: center; flex-wrap: wrap; }
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social { width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px; display: flex; align-items: center; justify-content: center; transition: border-color 0.2s, background 0.2s; }
.footer-social { width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; transition: border-color 0.2s, background 0.2s; }
.footer-social:hover { border-color: rgba(0,0,0,0.25); background: rgba(0,0,0,0.04); }
.lv-disclaimer { font-size:.75rem; color:rgba(0,0,0,.75); margin-top:24px; padding:12px 16px; background:rgba(0,0,0,.04); border-radius:8px; line-height:1.6; max-width:800px; }
.lv-disclaimer { font-size:.75rem; color:rgba(0,0,0,.75); margin-top:24px; padding:12px 16px; background:rgba(0,0,0,.04); border-radius: var(--radius-md); line-height:1.6; max-width:800px; }
.lv-disclaimer a { color:rgba(0,0,0,.65); text-decoration:underline; }
@media (max-width: 900px) {
@@ -499,18 +499,18 @@
<div class="conflict-side conflict-official">
<div class="conflict-side-label">Official narrative</div>
<ul>
<li>&#10003; Paris Plan Local d&rsquo;Urbanisme: commercial priority zones active</li>
<li>&#10003; Invest in Paris: &euro;2.3bn commercial investment committed 2024</li>
<li>&#10003; Arrondissement vacancy rate: below EU major city average</li>
<li>&#100003 Paris Plan Local d&rsquo;Urbanisme: commercial priority zones active</li>
<li>&#100003 Invest in Paris: &euro;2.3bn commercial investment committed 2024</li>
<li>&#100003 Arrondissement vacancy rate: below EU major city average</li>
</ul>
</div>
<div class="conflict-side conflict-observed">
<div class="conflict-side-label">Observed reality</div>
<ul>
<li>&#10007; 8e arrondissement: luxury retail vacancy elevated on secondary streets</li>
<li>&#10007; 10e/11e: independent retail closures accelerating post-pandemic</li>
<li>&#10007; La D&eacute;fense: sub-let market expanding, direct vacancy understated</li>
<li>&#10007; Investment permits not translating to observed ground-floor activation</li>
<li>&#100007 8e arrondissement: luxury retail vacancy elevated on secondary streets</li>
<li>&#100007 10e/11e: independent retail closures accelerating post-pandemic</li>
<li>&#100007 La D&eacute;fense: sub-let market expanding, direct vacancy understated</li>
<li>&#100007 Investment permits not translating to observed ground-floor activation</li>
</ul>
</div>
</div>
+13 -13
View File
@@ -181,7 +181,7 @@
display: flex; align-items: center; gap: 10px;
}
.district-chip .chip-dot {
width: 8px; height: 8px; border-radius: 50%;
width: 8px; height: 8px; border-radius: var(--radius-full);
background: var(--blue); flex-shrink: 0;
}
.district-chip span { font-size: 14px; font-weight: 600; color: var(--text-light); }
@@ -252,7 +252,7 @@
.contradiction-box {
background: rgba(0,0,0,.05);
border: 1px solid rgba(0,0,0,.1);
border-radius: 16px; padding: 36px 40px; margin-bottom: 32px;
border-radius: var(--radius-lg); padding: 36px 40px; margin-bottom: 32px;
}
.conflict-header {
@@ -266,7 +266,7 @@
.conflict-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.conflict-side { border-radius: 10px; padding: 16px; }
.conflict-side { border-radius: var(--radius-md); padding: 16px; }
.conflict-official { background: rgba(34,197,94,.1); border: 1px solid rgba(34,197,94,.2); }
.conflict-observed { background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.2); }
@@ -300,7 +300,7 @@
.ce-feature p { color: rgba(0,0,0,.75); font-size: .8125rem; }
.cta-banner {
background: #e8f0fe; border-radius: 16px; padding: 40px 48px;
background: #e8f0fe; border-radius: var(--radius-lg); padding: 40px 48px;
display: flex; align-items: center; gap: 48px; flex-wrap: wrap;
margin-top: 48px;
}
@@ -350,7 +350,7 @@
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social {
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px;
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md);
display: flex; align-items: center; justify-content: center;
transition: border-color 0.2s, background 0.2s;
}
@@ -385,7 +385,7 @@
.scores-grid { grid-template-columns: 1fr !important; }
}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius:8px;line-height:1.6;max-width:800px}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius: var(--radius-md);line-height:1.6;max-width:800px}
.lv-disclaimer a{color:rgba(0,0,0,.65);text-decoration:underline}
</style>
@@ -613,18 +613,18 @@
<div class="conflict-side conflict-official">
<div class="conflict-side-label">Official narrative</div>
<ul>
<li>&#10003; Centrum: architecture-led regeneration attracting sustained investment</li>
<li>&#10003; Feijenoord: social infrastructure investment programme active</li>
<li>&#10003; Port-adjacent development rated strategically sound by municipality</li>
<li>&#100003 Centrum: architecture-led regeneration attracting sustained investment</li>
<li>&#100003 Feijenoord: social infrastructure investment programme active</li>
<li>&#100003 Port-adjacent development rated strategically sound by municipality</li>
</ul>
</div>
<div class="conflict-side conflict-observed">
<div class="conflict-side-label">Observed reality</div>
<ul>
<li>&#10007; Centrum regeneration: design-led investment not penetrating secondary streets</li>
<li>&#10007; Feijenoord social investment slower than programme commitment</li>
<li>&#10007; Logistics demand in Prins Alexander exceeding residential plan assumptions</li>
<li>&#10007; Delfshaven creative cluster emerging ahead of formal recognition</li>
<li>&#100007 Centrum regeneration: design-led investment not penetrating secondary streets</li>
<li>&#100007 Feijenoord social investment slower than programme commitment</li>
<li>&#100007 Logistics demand in Prins Alexander exceeding residential plan assumptions</li>
<li>&#100007 Delfshaven creative cluster emerging ahead of formal recognition</li>
</ul>
</div>
</div>
+16 -16
View File
@@ -182,7 +182,7 @@
display: flex; align-items: center; gap: 10px;
}
.district-chip .chip-dot {
width: 8px; height: 8px; border-radius: 50%;
width: 8px; height: 8px; border-radius: var(--radius-full);
background: var(--blue); flex-shrink: 0;
}
.district-chip span { font-size: 14px; font-weight: 600; color: var(--text-light); }
@@ -256,7 +256,7 @@
.contradiction-box {
background: rgba(0,0,0,.05);
border: 1px solid rgba(0,0,0,.1);
border-radius: 16px; padding: 36px 40px; margin-bottom: 32px;
border-radius: var(--radius-lg); padding: 36px 40px; margin-bottom: 32px;
}
.conflict-header {
@@ -270,7 +270,7 @@
.conflict-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.conflict-side { border-radius: 10px; padding: 16px; }
.conflict-side { border-radius: var(--radius-md); padding: 16px; }
.conflict-official { background: rgba(34,197,94,.1); border: 1px solid rgba(34,197,94,.2); }
.conflict-observed { background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.2); }
@@ -305,7 +305,7 @@
/* CTA BANNER */
.cta-banner {
background: #e8f0fe; border-radius: 16px; padding: 40px 48px;
background: #e8f0fe; border-radius: var(--radius-lg); padding: 40px 48px;
display: flex; align-items: center; gap: 48px; flex-wrap: wrap;
margin-top: 48px;
}
@@ -357,7 +357,7 @@
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social {
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px;
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md);
display: flex; align-items: center; justify-content: center;
transition: border-color 0.2s, background 0.2s;
}
@@ -392,7 +392,7 @@
.scores-grid { grid-template-columns: 1fr !important; }
}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius:8px;line-height:1.6;max-width:800px}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius: var(--radius-md);line-height:1.6;max-width:800px}
.lv-disclaimer a{color:rgba(0,0,0,.65);text-decoration:underline}
.lv-disclaimer--light{color:#888;background:#f5f5f5}
.lv-disclaimer--light a{color:#555}
@@ -565,7 +565,7 @@
<div class="container">
<div class="section-label">District scores</div>
<h2 class="section-title">Sample intelligence scores</h2>
<p class="section-sub" style="background:#fff3cd;border:2px solid #ffc107;border-radius:8px;padding:12px 16px;color:#856404;font-weight:600;">⚠️ ILLUSTRATIVE SAMPLE DATA — These scores are synthetic examples for demonstration purposes only. They do not represent actual measured values.</p>
<p class="section-sub" style="background:#fff3cd;border:2px solid #ffc107;border-radius: var(--radius-md);padding:12px 16px;color:#856404;font-weight:600;"><svg width="20" height="20" viewBox="0 0 20 20" fill="none"><path d="M10 3L17 17H3L10 3Z" stroke="#f59e0b" stroke-width="2"/><path d="M10 8V12M10 14V14.5" stroke="#f59e0b" stroke-width="2" stroke-linecap="round"/></svg> ILLUSTRATIVE SAMPLE DATA — These scores are synthetic examples for demonstration purposes only. They do not represent actual measured values.</p>
<div class="scores-table-wrap">
<table class="scores-table">
<thead>
@@ -622,8 +622,8 @@
<p class="section-sub" style="color:rgba(0,0,0,.75);margin:0 auto;max-width:600px">When official municipal development plans diverge from observed commercial activity, Landvex surfaces the conflict &mdash; not just the consensus.</p>
</div>
<div style="background:#fff3cd;border:1px solid #ffc107;border-radius:8px;padding:12px 16px;margin-bottom:20px;font-size:13px;font-weight:600;color:#856404;text-align:center">
ILLUSTRATIVE SAMPLE — not real intelligence
<div style="background:#fff3cd;border:1px solid #ffc107;border-radius: var(--radius-md);padding:12px 16px;margin-bottom:20px;font-size:13px;font-weight:600;color:#856404;text-align:center">
ILLUSTRATIVE SAMPLE — not real intelligence
</div>
<div class="contradiction-box">
<div class="conflict-header">
@@ -640,18 +640,18 @@
<div class="conflict-side conflict-official">
<div class="conflict-side-label">Official narrative</div>
<ul>
<li>&#10003; Municipal investment zone: Järva priority growth</li>
<li>&#10003; Norrmalm: stable CBD with anchor tenants</li>
<li>&#10003; Development permits: 14 approved 2024&ndash;2025</li>
<li>&#100003 Municipal investment zone: Järva priority growth</li>
<li>&#100003 Norrmalm: stable CBD with anchor tenants</li>
<li>&#100003 Development permits: 14 approved 2024&ndash;2025</li>
</ul>
</div>
<div class="conflict-side conflict-observed">
<div class="conflict-side-label">Observed reality</div>
<ul>
<li>&#10007; Järva commercial vacancy above city average</li>
<li>&#10007; Norrmalm: 3 anchor retailers downsizing footprint</li>
<li>&#10007; Development activity below permit volume</li>
<li>&#10007; Nacka outperforming planned growth projections</li>
<li>&#100007 Järva commercial vacancy above city average</li>
<li>&#100007 Norrmalm: 3 anchor retailers downsizing footprint</li>
<li>&#100007 Development activity below permit volume</li>
<li>&#100007 Nacka outperforming planned growth projections</li>
</ul>
</div>
</div>
+13 -13
View File
@@ -181,7 +181,7 @@
display: flex; align-items: center; gap: 10px;
}
.district-chip .chip-dot {
width: 8px; height: 8px; border-radius: 50%;
width: 8px; height: 8px; border-radius: var(--radius-full);
background: var(--blue); flex-shrink: 0;
}
.district-chip span { font-size: 14px; font-weight: 600; color: var(--text-light); }
@@ -252,7 +252,7 @@
.contradiction-box {
background: rgba(0,0,0,.05);
border: 1px solid rgba(0,0,0,.1);
border-radius: 16px; padding: 36px 40px; margin-bottom: 32px;
border-radius: var(--radius-lg); padding: 36px 40px; margin-bottom: 32px;
}
.conflict-header {
@@ -266,7 +266,7 @@
.conflict-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.conflict-side { border-radius: 10px; padding: 16px; }
.conflict-side { border-radius: var(--radius-md); padding: 16px; }
.conflict-official { background: rgba(34,197,94,.1); border: 1px solid rgba(34,197,94,.2); }
.conflict-observed { background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.2); }
@@ -300,7 +300,7 @@
.ce-feature p { color: rgba(0,0,0,.75); font-size: .8125rem; }
.cta-banner {
background: #e8f0fe; border-radius: 16px; padding: 40px 48px;
background: #e8f0fe; border-radius: var(--radius-lg); padding: 40px 48px;
display: flex; align-items: center; gap: 48px; flex-wrap: wrap;
margin-top: 48px;
}
@@ -350,7 +350,7 @@
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social {
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px;
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md);
display: flex; align-items: center; justify-content: center;
transition: border-color 0.2s, background 0.2s;
}
@@ -385,7 +385,7 @@
.scores-grid { grid-template-columns: 1fr !important; }
}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius:8px;line-height:1.6;max-width:800px}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius: var(--radius-md);line-height:1.6;max-width:800px}
.lv-disclaimer a{color:rgba(0,0,0,.65);text-decoration:underline}
</style>
@@ -613,18 +613,18 @@
<div class="conflict-side conflict-official">
<div class="conflict-side-label">Official narrative</div>
<ul>
<li>&#10003; Innere Stadt: UNESCO heritage zone with stable premium retail</li>
<li>&#10003; Favoriten: designated affordable housing growth corridor</li>
<li>&#10003; Donaustadt: smart city expansion proceeding to plan</li>
<li>&#100003 Innere Stadt: UNESCO heritage zone with stable premium retail</li>
<li>&#100003 Favoriten: designated affordable housing growth corridor</li>
<li>&#100003 Donaustadt: smart city expansion proceeding to plan</li>
</ul>
</div>
<div class="conflict-side conflict-observed">
<div class="conflict-side-label">Observed reality</div>
<ul>
<li>&#10007; Innere Stadt: international tourism dependency creating fragility</li>
<li>&#10007; Favoriten: development pace below municipal targets</li>
<li>&#10007; Commercial vacancy emerging in secondary Favoriten strips</li>
<li>&#10007; Donaustadt tech cluster attracting investment ahead of schedule</li>
<li>&#100007 Innere Stadt: international tourism dependency creating fragility</li>
<li>&#100007 Favoriten: development pace below municipal targets</li>
<li>&#100007 Commercial vacancy emerging in secondary Favoriten strips</li>
<li>&#100007 Donaustadt tech cluster attracting investment ahead of schedule</li>
</ul>
</div>
</div>
+13 -13
View File
@@ -181,7 +181,7 @@
display: flex; align-items: center; gap: 10px;
}
.district-chip .chip-dot {
width: 8px; height: 8px; border-radius: 50%;
width: 8px; height: 8px; border-radius: var(--radius-full);
background: var(--blue); flex-shrink: 0;
}
.district-chip span { font-size: 14px; font-weight: 600; color: var(--text-light); }
@@ -252,7 +252,7 @@
.contradiction-box {
background: rgba(0,0,0,.05);
border: 1px solid rgba(0,0,0,.1);
border-radius: 16px; padding: 36px 40px; margin-bottom: 32px;
border-radius: var(--radius-lg); padding: 36px 40px; margin-bottom: 32px;
}
.conflict-header {
@@ -266,7 +266,7 @@
.conflict-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.conflict-side { border-radius: 10px; padding: 16px; }
.conflict-side { border-radius: var(--radius-md); padding: 16px; }
.conflict-official { background: rgba(34,197,94,.1); border: 1px solid rgba(34,197,94,.2); }
.conflict-observed { background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.2); }
@@ -300,7 +300,7 @@
.ce-feature p { color: rgba(0,0,0,.75); font-size: .8125rem; }
.cta-banner {
background: #e8f0fe; border-radius: 16px; padding: 40px 48px;
background: #e8f0fe; border-radius: var(--radius-lg); padding: 40px 48px;
display: flex; align-items: center; gap: 48px; flex-wrap: wrap;
margin-top: 48px;
}
@@ -350,7 +350,7 @@
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social {
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px;
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md);
display: flex; align-items: center; justify-content: center;
transition: border-color 0.2s, background 0.2s;
}
@@ -385,7 +385,7 @@
.scores-grid { grid-template-columns: 1fr !important; }
}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius:8px;line-height:1.6;max-width:800px}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius: var(--radius-md);line-height:1.6;max-width:800px}
.lv-disclaimer a{color:rgba(0,0,0,.65);text-decoration:underline}
</style>
@@ -613,18 +613,18 @@
<div class="conflict-side conflict-official">
<div class="conflict-side-label">Official narrative</div>
<ul>
<li>&#10003; City / Altstadt: premium commercial core, rated fully stable</li>
<li>&#10003; Schwamendingen: affordable district with long-term growth mandate</li>
<li>&#10003; Infrastructure maintenance rated best-in-class nationally</li>
<li>&#100003 City / Altstadt: premium commercial core, rated fully stable</li>
<li>&#100003 Schwamendingen: affordable district with long-term growth mandate</li>
<li>&#100003 Infrastructure maintenance rated best-in-class nationally</li>
</ul>
</div>
<div class="conflict-side conflict-observed">
<div class="conflict-side-label">Observed reality</div>
<ul>
<li>&#10007; City / Altstadt: office vacancy ticking upward post-remote shift</li>
<li>&#10007; Schwamendingen: investment slower than mandate implies</li>
<li>&#10007; Altstetten emerging as commercial overflow beneficiary</li>
<li>&#10007; Retail footfall in Altstadt below 2019 baseline</li>
<li>&#100007 City / Altstadt: office vacancy ticking upward post-remote shift</li>
<li>&#100007 Schwamendingen: investment slower than mandate implies</li>
<li>&#100007 Altstetten emerging as commercial overflow beneficiary</li>
<li>&#100007 Retail footfall in Altstadt below 2019 baseline</li>
</ul>
</div>
</div>
+1 -1
View File
@@ -61,7 +61,7 @@
li { margin-bottom: 6px; }
.row { display: flex; align-items: flex-start; gap: 12px; padding: 12px 0; border-bottom: 0.5px solid var(--separator); }
.row:last-child { border-bottom: none; }
.row-icon { width: 36px; height: 36px; border-radius: 8px; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.row-icon { width: 36px; height: 36px; border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.blue { background: rgba(0,102,255,0.12); }
.green { background: rgba(52,199,89,0.12); }
.orange { background: rgba(255,149,0,0.12); }
+7 -7
View File
@@ -57,12 +57,12 @@
.contact-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 24px; }
.contact-card {
background: var(--surface); border: 1px solid var(--border); border-radius: 20px;
background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-xl);
padding: 40px; text-align: center; transition: transform 0.2s, box-shadow 0.2s;
}
.contact-card:hover { transform: translateY(-4px); box-shadow: 0 12px 40px rgba(0,0,0,0.08); }
.contact-icon {
width: 56px; height: 56px; background: var(--blue-glow); border-radius: 16px;
width: 56px; height: 56px; background: var(--blue-glow); border-radius: var(--radius-lg);
display: flex; align-items: center; justify-content: center;
margin: 0 auto 20px; color: var(--blue);
}
@@ -74,7 +74,7 @@
/* Form section */
.form-section {
background: var(--surface); border: 1px solid var(--border); border-radius: 20px;
background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-xl);
padding: 48px; margin: 40px 0;
}
.form-section h2 { font-size: 1.75rem; font-weight: 700; margin-bottom: 16px; text-align: center; }
@@ -84,7 +84,7 @@
.form-group.full { grid-column: 1 / -1; }
.form-group label { font-size: 0.875rem; font-weight: 600; margin-bottom: 6px; color: var(--text-dark); }
.form-group input, .form-group textarea, .form-group select {
padding: 12px 16px; border: 1px solid var(--border); border-radius: 12px;
padding: 12px 16px; border: 1px solid var(--border); border-radius: var(--radius-md);
font-size: 1rem; font-family: inherit; background: var(--surface-2);
}
.form-group input:focus, .form-group textarea:focus, .form-group select:focus {
@@ -93,7 +93,7 @@
.form-group textarea { min-height: 120px; resize: vertical; }
.submit-btn {
background: var(--blue); color: #fff; border: none; padding: 14px 32px;
border-radius: 12px; font-size: 1rem; font-weight: 600; cursor: pointer;
border-radius: var(--radius-md); font-size: 1rem; font-weight: 600; cursor: pointer;
margin-top: 8px; transition: background 0.2s;
}
.submit-btn:hover { background: var(--blue-dark); }
@@ -102,12 +102,12 @@
.offices { padding: 60px 0; background: var(--surface); }
.office-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 24px; }
.office-card {
background: var(--surface-2); border: 1px solid var(--border); border-radius: 16px; padding: 32px;
background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 32px;
}
.office-card h3 { font-size: 1.125rem; font-weight: 700; margin-bottom: 8px; }
.office-card p { font-size: 0.9rem; color: var(--text-muted); line-height: 1.6; }
.office-icon {
width: 48px; height: 48px; background: var(--blue-glow); border-radius: 12px;
width: 48px; height: 48px; background: var(--blue-glow); border-radius: var(--radius-md);
display: flex; align-items: center; justify-content: center;
margin-bottom: 16px; color: var(--blue);
}
@@ -29,7 +29,7 @@ h1 { font-size: 2.5rem; font-weight: 700; margin-bottom: 0.5em; line-height: 1.2
h2 { font-size: 1.5rem; font-weight: 600; margin-top: 2em; margin-bottom: 0.5em; }
p { margin-bottom: 1.2em; }
.meta { color: #666; font-size: 0.9rem; margin-bottom: 2em; }
.cta { background: #f5f5f5; padding: 24px; border-radius: 8px; margin: 2em 0; }
.cta { background: #f5f5f5; padding: 24px; border-radius: var(--radius-md); margin: 2em 0; }
.cta a { color: #0066cc; text-decoration: none; font-weight: 600; }
footer { margin-top: 4em; padding-top: 2em; border-top: 1px solid #eee; font-size: 0.85rem; color: #666; }
</style>
+3 -3
View File
@@ -11,14 +11,14 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; line
h1 { font-size: 2.5rem; font-weight: 700; margin-bottom: 0.5em; }
.subtitle { color: #666; font-size: 1.2rem; margin-bottom: 3em; }
.article-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(350px, 1fr)); gap: 2em; }
.article-card { border: 1px solid #eee; border-radius: 8px; padding: 24px; transition: box-shadow 0.2s; }
.article-card { border: 1px solid #eee; border-radius: var(--radius-md); padding: 24px; transition: box-shadow 0.2s; }
.article-card:hover { box-shadow: 0 4px 12px rgba(0,0,0,0.1); }
.article-card h2 { font-size: 1.3rem; margin-top: 0; }
.article-card a { color: #1a1a1a; text-decoration: none; }
.article-card a:hover { color: #0066cc; }
.article-meta { color: #666; font-size: 0.85rem; margin-top: 1em; }
.tag { display: inline-block; background: #f5f5f5; padding: 4px 12px; border-radius: 4px; font-size: 0.8rem; margin-right: 8px; }
.cta-section { background: #f5f5f5; padding: 32px; border-radius: 8px; margin: 3em 0; text-align: center; }
.tag { display: inline-block; background: #f5f5f5; padding: 4px 12px; border-radius: var(--radius-sm); font-size: 0.8rem; margin-right: 8px; }
.cta-section { background: #f5f5f5; padding: 32px; border-radius: var(--radius-md); margin: 3em 0; text-align: center; }
.cta-section a { color: #0066cc; text-decoration: none; font-weight: 600; font-size: 1.1rem; }
</style>
</head>
+2 -2
View File
@@ -61,7 +61,7 @@
li { margin-bottom: 6px; }
.row { display: flex; align-items: flex-start; gap: 12px; padding: 12px 0; border-bottom: 0.5px solid var(--separator); }
.row:last-child { border-bottom: none; }
.row-icon { width: 36px; height: 36px; border-radius: 8px; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.row-icon { width: 36px; height: 36px; border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.blue { background: rgba(0,102,255,0.12); }
.green { background: rgba(52,199,89,0.12); }
.orange { background: rgba(255,149,0,0.12); }
@@ -71,7 +71,7 @@
.row-content { flex: 1; }
.row-label { font-size: 15px; font-weight: 600; color: var(--label); }
.row-sub { font-size: 13px; color: var(--label3); margin-top: 2px; line-height: 1.5; }
.badge { display: inline-block; font-size: 11px; font-weight: 600; padding: 2px 7px; border-radius: 5px; margin-left: 6px; vertical-align: middle; }
.badge { display: inline-block; font-size: 11px; font-weight: 600; padding: 2px 7px; border-radius: var(--radius-sm); margin-left: 6px; vertical-align: middle; }
.badge-required { background: rgba(255,59,48,0.12); color: #c0392b; }
.badge-consent { background: rgba(0,102,255,0.12); color: var(--accent); }
a { color: var(--accent); text-decoration: none; }
+3 -3
View File
@@ -46,7 +46,7 @@
letter-spacing: -0.3px;
color: #1d1d1f; text-decoration: none; letter-spacing: -0.3px; }
.nav-links { display: flex; list-style: none; gap: 8px; align-items: center; }
.nav-links a { color: var(--text-dim); text-decoration: none; font-size: 14px; padding: 6px 10px; border-radius: 6px; transition: color 0.2s; }
.nav-links a { color: var(--text-dim); text-decoration: none; font-size: 14px; padding: 6px 10px; border-radius: var(--radius-sm); transition: color 0.2s; }
.nav-links a:hover { color: #1d1d1f; }
.nav-dropdown { position: relative; }
.nav-dropdown > a::after { content: " "; display: inline-block; border: 4px solid transparent; border-top-color: currentColor; margin-left: 4px; vertical-align: 2px; transition: transform 0.2s; }
@@ -56,7 +56,7 @@
.nav-dropdown-menu a:hover { color: #1d1d1f; background: rgba(0,0,0,0.03); }
.nav-dropdown-menu .dd-label { font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.08em; color: var(--text-muted); padding: 8px 16px 4px; }
.nav-dropdown-menu hr { border: none; border-top: 1px solid var(--border); margin: 6px 0; }
.btn { display: inline-flex; align-items: center; gap: 6px; background: var(--blue); color: #1d1d1f; font-size: 14px; font-weight: 600; padding: 9px 18px; border-radius: 8px; text-decoration: none; border: none; cursor: pointer; transition: background 0.2s, transform 0.15s; font-family: inherit; }
.btn { display: inline-flex; align-items: center; gap: 6px; background: var(--blue); color: #1d1d1f; font-size: 14px; font-weight: 600; padding: 9px 18px; border-radius: var(--radius-md); text-decoration: none; border: none; cursor: pointer; transition: background 0.2s, transform 0.15s; font-family: inherit; }
.btn:hover { background: var(--blue-dark); transform: translateY(-1px); }
.btn-outline { background: transparent; border: 1.5px solid rgba(0,0,0,0.2); }
.btn-outline:hover { background: rgba(0,0,0,0.04); }
@@ -94,7 +94,7 @@
.freshness-table td { padding: 14px 16px; font-size: 14px; color: var(--text-dim); border-bottom: 1px solid var(--border); }
.freshness-table tr:last-child td { border-bottom: none; }
.freshness-table .tier { font-weight: 600; color: var(--text); }
.badge { display: inline-block; padding: 3px 10px; border-radius: 20px; font-size: 12px; font-weight: 600; }
.badge { display: inline-block; padding: 3px 10px; border-radius: var(--radius-xl); font-size: 12px; font-weight: 600; }
.badge-standard { background: rgba(0,0,0,0.08); color: var(--text-dim); }
.badge-premium { background: rgba(0,102,255,0.12); color: #5fa8ff; }
.badge-realtime { background: rgba(52,199,89,0.12); color: var(--green); }
+25 -25
View File
@@ -47,7 +47,7 @@
.navbar-inner { display: flex; align-items: center; justify-content: space-between; height: 44px; padding: 0 16px; }
.navbar-logo { display: flex; align-items: center; gap: 8px; }
.navbar-logo span { font-size: 17px; font-weight: 700; color: var(--blue); letter-spacing: -0.5px; }
.navbar-badge { background: var(--blue); color: #1d1d1f; font-size: 11px; font-weight: 600; padding: 2px 7px; border-radius: 10px; }
.navbar-badge { background: var(--blue); color: #1d1d1f; font-size: 11px; font-weight: 600; padding: 2px 7px; border-radius: var(--radius-md); }
.scroll-area {
position: fixed; top: 0; bottom: 0; left: 0; right: 0;
@@ -89,7 +89,7 @@
text-transform: uppercase; letter-spacing: 0.3px;
}
.inset-group { margin: 0 16px 4px; border-radius: 12px; overflow: hidden; background: var(--card-bg); }
.inset-group { margin: 0 16px 4px; border-radius: var(--radius-md); overflow: hidden; background: var(--card-bg); }
.inset-group .cell { display: flex; align-items: center; padding: 12px 16px; min-height: 44px; border-bottom: 0.5px solid var(--sep); }
.inset-group .cell:last-child { border-bottom: none; }
.cell-label { flex: 1; font-size: 17px; }
@@ -98,7 +98,7 @@
.searchbar-wrap { padding: 8px 16px; }
.searchbar {
display: flex; align-items: center; gap: 8px;
background: var(--card-bg); border-radius: 10px;
background: var(--card-bg); border-radius: var(--radius-md);
padding: 8px 12px; box-shadow: 0 1px 3px rgba(0,0,0,0.08);
}
.searchbar input { flex: 1; border: none; outline: none; font-size: 17px; background: transparent; color: var(--label); font-family: inherit; }
@@ -106,9 +106,9 @@
.searchbar .icon { color: var(--gray3); font-size: 18px; }
.seg-wrap { padding: 8px 16px; }
.seg-control { display: flex; background: var(--gray5); border-radius: 9px; padding: 2px; gap: 2px; }
.seg-control { display: flex; background: var(--gray5); border-radius: var(--radius-md); padding: 2px; gap: 2px; }
.seg-btn {
flex: 1; padding: 6px 4px; border: none; background: none; border-radius: 7px;
flex: 1; padding: 6px 4px; border: none; background: none; border-radius: var(--radius-sm);
font-size: 13px; font-weight: 500; color: var(--gray); cursor: pointer;
transition: all 0.2s; font-family: inherit;
}
@@ -116,13 +116,13 @@
.prop-list { padding: 0 16px; display: flex; flex-direction: column; gap: 10px; }
.prop-card {
background: var(--card-bg); border-radius: 12px;
background: var(--card-bg); border-radius: var(--radius-md);
padding: 16px; box-shadow: 0 1px 3px rgba(0,0,0,0.08);
cursor: pointer; transition: transform 0.15s;
}
.prop-card:active { transform: scale(0.98); }
.prop-card-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 8px; }
.prop-type-badge { font-size: 11px; font-weight: 600; padding: 3px 8px; border-radius: 6px; white-space: nowrap; }
.prop-type-badge { font-size: 11px; font-weight: 600; padding: 3px 8px; border-radius: var(--radius-sm); white-space: nowrap; }
.prop-address { font-size: 17px; font-weight: 600; letter-spacing: -0.3px; }
.prop-owner { font-size: 14px; color: var(--gray); margin-top: 2px; }
.prop-meta { display: flex; gap: 12px; margin-top: 12px; flex-wrap: wrap; }
@@ -130,13 +130,13 @@
.prop-meta-label { font-size: 11px; color: var(--gray); font-weight: 500; text-transform: uppercase; letter-spacing: 0.3px; }
.prop-meta-value { font-size: 15px; font-weight: 600; }
.prop-condition { display: inline-flex; align-items: center; gap: 4px; font-size: 13px; font-weight: 500; margin-top: 10px; }
.condition-dot { width: 8px; height: 8px; border-radius: 50%; }
.condition-dot { width: 8px; height: 8px; border-radius: var(--radius-full); }
.cond-good { background: var(--green); }
.cond-fair { background: var(--orange); }
.cond-poor, .cond-critical { background: var(--red); }
.cond-unknown { background: var(--gray3); }
.form-group { margin: 0 16px 4px; border-radius: 12px; overflow: hidden; background: var(--card-bg); }
.form-group { margin: 0 16px 4px; border-radius: var(--radius-md); overflow: hidden; background: var(--card-bg); }
.form-field { display: flex; flex-direction: column; padding: 10px 16px; border-bottom: 0.5px solid var(--sep); }
.form-field:last-child { border-bottom: none; }
.form-label { font-size: 12px; font-weight: 500; color: var(--gray); margin-bottom: 4px; text-transform: uppercase; letter-spacing: 0.3px; }
@@ -147,7 +147,7 @@
.btn-primary {
display: block; width: calc(100% - 32px); margin: 16px 16px 8px;
background: var(--blue); color: #1d1d1f; font-size: 17px; font-weight: 600;
border: none; border-radius: 12px; padding: 14px; cursor: pointer;
border: none; border-radius: var(--radius-md); padding: 14px; cursor: pointer;
font-family: inherit; transition: opacity 0.15s; letter-spacing: -0.3px;
}
.btn-primary:active { opacity: 0.75; }
@@ -155,13 +155,13 @@
.btn-secondary {
display: block; width: calc(100% - 32px); margin: 4px 16px;
background: var(--gray6); color: var(--blue); font-size: 17px; font-weight: 500;
border: none; border-radius: 12px; padding: 14px; cursor: pointer;
border: none; border-radius: var(--radius-md); padding: 14px; cursor: pointer;
font-family: inherit; transition: opacity 0.15s;
}
.btn-secondary:active { opacity: 0.7; }
.stats-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; padding: 0 16px; }
.stat-card { background: var(--card-bg); border-radius: 12px; padding: 14px; box-shadow: 0 1px 3px rgba(0,0,0,0.08); }
.stat-card { background: var(--card-bg); border-radius: var(--radius-md); padding: 14px; box-shadow: 0 1px 3px rgba(0,0,0,0.08); }
.stat-icon { font-size: 24px; margin-bottom: 8px; }
.stat-value { font-size: 22px; font-weight: 700; letter-spacing: -0.5px; }
.stat-label { font-size: 13px; color: var(--gray); margin-top: 2px; }
@@ -171,8 +171,8 @@
.bar-row { display: flex; align-items: center; gap: 8px; margin-bottom: 4px; }
.bar-name { font-size: 13px; font-weight: 500; flex: 0 0 120px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bar-count { font-size: 13px; color: var(--gray); margin-left: auto; }
.bar-track { height: 8px; background: var(--gray5); border-radius: 4px; overflow: hidden; }
.bar-fill { height: 100%; border-radius: 4px; background: var(--blue); transition: width 0.6s cubic-bezier(0.34,1.56,0.64,1); }
.bar-track { height: 8px; background: var(--gray5); border-radius: var(--radius-sm); overflow: hidden; }
.bar-fill { height: 100%; border-radius: var(--radius-sm); background: var(--blue); transition: width 0.6s cubic-bezier(0.34,1.56,0.64,1); }
.trend-row { display: flex; align-items: center; padding: 12px 16px; border-bottom: 0.5px solid var(--sep); }
.trend-row:last-child { border-bottom: none; }
@@ -184,13 +184,13 @@
.toast {
position: fixed; bottom: calc(83px + var(--safe-bottom) + 12px); left: 50%; transform: translateX(-50%) translateY(20px);
background: rgba(50,50,50,0.92); color: #1d1d1f; padding: 10px 18px; border-radius: 20px;
background: rgba(50,50,50,0.92); color: #1d1d1f; padding: 10px 18px; border-radius: var(--radius-xl);
font-size: 15px; font-weight: 500; opacity: 0; transition: all 0.3s; pointer-events: none;
white-space: nowrap; backdrop-filter: blur(10px); z-index: 200;
}
.toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }
.spinner { display: inline-block; width: 20px; height: 20px; border: 2px solid var(--gray4); border-top-color: var(--blue); border-radius: 50%; animation: spin 0.7s linear infinite; }
.spinner { display: inline-block; width: 20px; height: 20px; border: 2px solid var(--gray4); border-top-color: var(--blue); border-radius: var(--radius-full); animation: spin 0.7s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }
.loading-center { display: flex; justify-content: center; align-items: center; padding: 40px; gap: 10px; color: var(--gray); font-size: 15px; }
@@ -199,7 +199,7 @@
.empty-title { font-size: 20px; font-weight: 600; margin-bottom: 8px; }
.empty-body { font-size: 15px; color: var(--gray); line-height: 1.5; }
.new-badge { background: rgba(52,199,89,0.15); color: var(--green); font-size: 11px; font-weight: 700; padding: 2px 7px; border-radius: 6px; }
.new-badge { background: rgba(52,199,89,0.15); color: var(--green); font-size: 11px; font-weight: 700; padding: 2px 7px; border-radius: var(--radius-sm); }
@media (max-width: 640px) {
.hero h1 { font-size: 2rem !important; }
.hero p { font-size: 1rem !important; }
@@ -349,7 +349,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<div class="searchbar-wrap">
<div class="searchbar">
<span class="icon">🔍</span>
<span class="icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><circle cx="7" cy="7" r="5" stroke="#666" stroke-width="2"/><path d="M11 11L14 14" stroke="#666" stroke-width="2" stroke-linecap="round"/></svg></span>
<input type="text" id="cityInput" placeholder="Stad (t.ex. Stockholm)" autocomplete="off" autocorrect="off">
</div>
</div>
@@ -478,11 +478,11 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<nav class="tabbar">
<button class="tab-item active" onclick="switchTab('search',this)">
<span class="tab-icon">🔍</span>
<span class="tab-icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><circle cx="7" cy="7" r="5" stroke="#666" stroke-width="2"/><path d="M11 11L14 14" stroke="#666" stroke-width="2" stroke-linecap="round"/></svg></span>
<span class="tab-label">Sök</span>
</button>
<button class="tab-item" onclick="switchTab('register',this)">
<span class="tab-icon"></span>
<span class="tab-icon"></span>
<span class="tab-label">Registrera</span>
</button>
<button class="tab-item" onclick="switchTab('market',this)">
@@ -572,7 +572,7 @@ async function loadProperties(city, type) {
if (type) parts.push({commercial:'Kommersiella',residential:'Bostäder',industrial:'Industrier'}[type]||type);
header.textContent = (parts.join(' · ') || 'Alla fastigheter') + ` (${d.total})`;
if (!d.properties?.length) {
list.innerHTML = '<div class="empty-state"><div class="empty-icon">🏢</div><div class="empty-title">Inga fastigheter</div><div class="empty-body">Prova en annan stad eller kategori.</div></div>';
list.innerHTML = '<div class="empty-state"><div class="empty-icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="3" y="2" width="10" height="12" rx="1" stroke="#666" stroke-width="1.5"/><path d="M6 5H7M6 8H7M6 11H7M9 5H10M9 8H10M9 11H10" stroke="#666" stroke-width="1.5" stroke-linecap="round"/></svg></div><div class="empty-title">Inga fastigheter</div><div class="empty-body">Prova en annan stad eller kategori.</div></div>';
return;
}
list.innerHTML = d.properties.map(renderPropCard).join('');
@@ -614,10 +614,10 @@ async function loadMarket(city) {
const d = await r.json();
const m = d.market_overview;
grid.innerHTML = `
<div class="stat-card"><div class="stat-icon">🏢</div><div class="stat-value">${m.total_properties}</div><div class="stat-label">Fastigheter</div></div>
<div class="stat-card"><div class="stat-icon">💰</div><div class="stat-value">${fmtSEK(m.total_assessed_value_sek)}</div><div class="stat-label">Totalt taxvärde</div></div>
<div class="stat-card"><div class="stat-icon">📐</div><div class="stat-value">${fmtSqm(m.total_area_sqm)}</div><div class="stat-label">Total yta</div></div>
<div class="stat-card"><div class="stat-icon">📈</div><div class="stat-value">${m.avg_value_per_sqm?.toLocaleString('sv-SE')||'—'}</div><div class="stat-label">kr/kvm snitt</div></div>`;
<div class="stat-card"><div class="stat-icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="3" y="2" width="10" height="12" rx="1" stroke="#666" stroke-width="1.5"/><path d="M6 5H7M6 8H7M6 11H7M9 5H10M9 8H10M9 11H10" stroke="#666" stroke-width="1.5" stroke-linecap="round"/></svg></div><div class="stat-value">${m.total_properties}</div><div class="stat-label">Fastigheter</div></div>
<div class="stat-card"><div class="stat-icon"></div><div class="stat-value">${fmtSEK(m.total_assessed_value_sek)}</div><div class="stat-label">Totalt taxvärde</div></div>
<div class="stat-card"><div class="stat-icon"></div><div class="stat-value">${fmtSqm(m.total_area_sqm)}</div><div class="stat-label">Total yta</div></div>
<div class="stat-card"><div class="stat-icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 12L6 8L9 11L14 5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M10 5H14V9" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></div><div class="stat-value">${m.avg_value_per_sqm?.toLocaleString('sv-SE')||'—'}</div><div class="stat-label">kr/kvm snitt</div></div>`;
const totalCnt = Object.values(m.by_type).reduce((s,v)=>s+v.count,0)||1;
document.getElementById('typeDistribution').innerHTML = Object.entries(m.by_type).map(([t,v]) => {
+4 -4
View File
@@ -69,10 +69,10 @@
.step:last-child { border-bottom: none; }
.step:hover { opacity: 0.85; }
.step-num-wrap { display: flex; flex-direction: column; align-items: center; flex-shrink: 0; }
.step-num { width: 32px; height: 32px; border-radius: 50%; background: rgba(0,102,255,0.12); color: var(--accent); font-size: 13px; font-weight: 700; display: flex; align-items: center; justify-content: center; }
.step-num { width: 32px; height: 32px; border-radius: var(--radius-full); background: rgba(0,102,255,0.12); color: var(--accent); font-size: 13px; font-weight: 700; display: flex; align-items: center; justify-content: center; }
.step-line { width: 1.5px; flex: 1; min-height: 12px; background: rgba(0,102,255,0.18); margin-top: 4px; }
.step:last-child .step-line { display: none; }
.step-icon { width: 36px; height: 36px; border-radius: 9px; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.step-icon { width: 36px; height: 36px; border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.step-body { flex: 1; }
.step-label { font-size: 15px; font-weight: 600; color: var(--label); }
.step-sub { font-size: 13px; color: var(--label3); margin-top: 3px; line-height: 1.55; }
@@ -99,13 +99,13 @@
/* CTA */
.cta-row { display: flex; justify-content: center; margin: 16px 0 4px; }
.cta-btn { display: inline-block; background: var(--accent); color: #1d1d1f; font-size: 15px; font-weight: 600; padding: 13px 24px; border-radius: 12px; text-decoration: none; transition: opacity 0.15s; }
.cta-btn { display: inline-block; background: var(--accent); color: #1d1d1f; font-size: 15px; font-weight: 600; padding: 13px 24px; border-radius: var(--radius-md); text-decoration: none; transition: opacity 0.15s; }
.cta-btn:hover { opacity: 0.88; }
/* Related docs */
.row { display: flex; align-items: flex-start; gap: 12px; padding: 12px 0; border-bottom: 0.5px solid var(--separator); }
.row:last-child { border-bottom: none; }
.row-icon { width: 36px; height: 36px; border-radius: 8px; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.row-icon { width: 36px; height: 36px; border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.row-content { flex: 1; }
.row-label { font-size: 15px; font-weight: 600; color: var(--label); }
.row-sub { font-size: 13px; color: var(--label3); margin-top: 2px; line-height: 1.5; }
+5 -5
View File
@@ -164,7 +164,7 @@
width: 40px; height: 40px; flex-shrink: 0;
background: rgba(0,102,255,0.12);
border: 1px solid rgba(0,102,255,0.3);
border-radius: 50%;
border-radius: var(--radius-full);
display: flex; align-items: center; justify-content: center;
font-size: 14px; font-weight: 800; color: var(--blue);
position: relative; z-index: 1;
@@ -223,7 +223,7 @@
width: 80px; height: 80px; flex-shrink: 0;
background: rgba(0,102,255,0.1);
border: 2px solid rgba(0,102,255,0.3);
border-radius: 50%;
border-radius: var(--radius-full);
display: flex; flex-direction: column; align-items: center; justify-content: center;
}
.confidence-score-display .score-num {
@@ -255,7 +255,7 @@
.contradiction-detail-card {
background: rgba(0,0,0,.03);
border: 1px solid rgba(0,0,0,.08);
border-radius: 16px; padding: 32px 36px;
border-radius: var(--radius-lg); padding: 32px 36px;
margin-top: 40px;
}
@@ -309,7 +309,7 @@
/* ENTERPRISE */
.enterprise-box {
background: #e8f0fe; border-radius: 20px; padding: 48px;
background: #e8f0fe; border-radius: var(--radius-xl); padding: 48px;
margin-top: 48px;
}
@@ -372,7 +372,7 @@
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social {
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px;
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md);
display: flex; align-items: center; justify-content: center;
transition: border-color 0.2s, background 0.2s;
}
+151
View File
@@ -0,0 +1,151 @@
/* Landvex Design Tokens v1.0 */
/* Standardized spacing, colors, typography, and border-radius */
:root {
/* === COLORS === */
/* Primary Brand */
--color-primary: #0066FF;
--color-primary-dark: #0052CC;
--color-primary-light: #3385FF;
/* Secondary (quiXzoom accent) */
--color-accent: #FF3366;
--color-accent-dark: #E62E5C;
/* Neutral */
--color-black: #0A0A0A;
--color-dark: #1A1A1A;
--color-gray-900: #1D1D1F;
--color-gray-800: #2D2D2F;
--color-gray-700: #4A4A4A;
--color-gray-600: #6E6E73;
--color-gray-500: #8E8E93;
--color-gray-400: #A1A1A6;
--color-gray-300: #C7C7CC;
--color-gray-200: #D1D1D6;
--color-gray-100: #E5E5EA;
--color-gray-50: #F2F2F7;
--color-white: #FFFFFF;
/* Semantic */
--color-success: #22C55E;
--color-warning: #F59E0B;
--color-error: #EF4444;
--color-info: #3B82F6;
/* === SPACING === */
/* Base unit: 4px */
--space-0: 0;
--space-1: 4px;
--space-2: 8px;
--space-3: 12px;
--space-4: 16px;
--space-5: 20px;
--space-6: 24px;
--space-8: 32px;
--space-10: 40px;
--space-12: 48px;
--space-16: 64px;
--space-20: 80px;
--space-24: 96px;
/* === BORDER RADIUS === */
--radius-none: 0;
--radius-sm: 6px; /* Buttons, tags, small elements */
--radius-md: 10px; /* Cards, inputs, modals */
--radius-lg: 16px; /* Large cards, hero sections */
--radius-xl: 24px; /* Feature cards, containers */
--radius-full: 9999px; /* Pills, avatars, badges */
/* === TYPOGRAPHY === */
/* Font families */
--font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Inter', 'Helvetica Neue', sans-serif;
--font-mono: 'SF Mono', 'Fira Code', 'Consolas', monospace;
/* Font sizes (rem-based) */
--text-xs: 0.75rem; /* 12px */
--text-sm: 0.875rem; /* 14px */
--text-base: 1rem; /* 16px */
--text-lg: 1.125rem; /* 18px */
--text-xl: 1.25rem; /* 20px */
--text-2xl: 1.5rem; /* 24px */
--text-3xl: 1.875rem; /* 30px */
--text-4xl: 2.25rem; /* 36px */
--text-5xl: 3rem; /* 48px */
/* Font weights */
--font-normal: 400;
--font-medium: 500;
--font-semibold: 600;
--font-bold: 700;
--font-extrabold: 800;
/* Line heights */
--leading-none: 1;
--leading-tight: 1.25;
--leading-snug: 1.375;
--leading-normal: 1.5;
--leading-relaxed: 1.625;
--leading-loose: 2;
/* === SHADOWS === */
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1);
--shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1);
--shadow-xl: 0 20px 25px rgba(0, 0, 0, 0.15);
--shadow-glow: 0 0 20px rgba(0, 102, 255, 0.3);
/* === TRANSITIONS === */
--transition-fast: 150ms ease;
--transition-base: 200ms ease;
--transition-slow: 300ms ease;
/* === Z-INDEX === */
--z-dropdown: 100;
--z-sticky: 200;
--z-modal: 300;
--z-popover: 400;
--z-tooltip: 500;
}
/* === UTILITY CLASSES === */
/* Use these instead of inline styles */
/* Border radius utilities */
.rounded-none { border-radius: var(--radius-none); }
.rounded-sm { border-radius: var(--radius-sm); }
.rounded-md { border-radius: var(--radius-md); }
.rounded-lg { border-radius: var(--radius-lg); }
.rounded-xl { border-radius: var(--radius-xl); }
.rounded-full { border-radius: var(--radius-full); }
/* Spacing utilities (examples) */
.p-1 { padding: var(--space-1); }
.p-2 { padding: var(--space-2); }
.p-4 { padding: var(--space-4); }
.p-6 { padding: var(--space-6); }
.p-8 { padding: var(--space-8); }
.m-1 { margin: var(--space-1); }
.m-2 { margin: var(--space-2); }
.m-4 { margin: var(--space-4); }
/* Text utilities */
.text-xs { font-size: var(--text-xs); }
.text-sm { font-size: var(--text-sm); }
.text-base { font-size: var(--text-base); }
.text-lg { font-size: var(--text-lg); }
.text-xl { font-size: var(--text-xl); }
.font-medium { font-weight: var(--font-medium); }
.font-semibold { font-weight: var(--font-semibold); }
.font-bold { font-weight: var(--font-bold); }
/* Color utilities */
.text-primary { color: var(--color-primary); }
.text-secondary { color: var(--color-gray-600); }
.text-muted { color: var(--color-gray-500); }
.bg-primary { background-color: var(--color-primary); }
.bg-dark { background-color: var(--color-dark); }
.bg-light { background-color: var(--color-gray-50); }
+1 -1
View File
@@ -31,7 +31,7 @@
table { width: 100%; border-collapse: collapse; margin: 16px 0 24px; font-size: 13px; }
th { background: #f5f5f7; padding: 10px 12px; text-align: left; font-weight: 600; border-bottom: 2px solid #e5e5e7; }
td { padding: 9px 12px; border-bottom: 1px solid #1a1a1a; vertical-align: top; }
.highlight { background: #f5f5f7; border-radius: 10px; padding: 16px 20px; margin: 20px 0; font-size: 14px; }
.highlight { background: #f5f5f7; border-radius: var(--radius-md); padding: 16px 20px; margin: 20px 0; font-size: 14px; }
.highlight p { margin-bottom: 0; font-size: 14px; }
.info-box { background: #e8f4fd; border-left: 4px solid #0066cc; border-radius: 0 8px 8px 0; padding: 14px 18px; margin: 20px 0; font-size: 14px; color: #1d1d1f; }
footer { border-top: 1px solid #e5e5e7; padding: 24px; text-align: center; font-size: 13px; color: #6e6e73; }
+2 -2
View File
@@ -239,7 +239,7 @@
}
.process-step { text-align: center; padding: 0 20px; position: relative; }
.step-num {
width: 54px; height: 54px; border-radius: 50%;
width: 54px; height: 54px; border-radius: var(--radius-full);
background: var(--blue-glow);
border: 2px solid var(--blue);
display: flex; align-items: center; justify-content: center;
@@ -412,7 +412,7 @@
.footer-links a:hover { color: #1d1d1f; }
.footer-social {
width: 32px; height: 32px;
border: 1px solid var(--border); border-radius: 8px;
border: 1px solid var(--border); border-radius: var(--radius-md);
display: flex; align-items: center; justify-content: center;
transition: border-color 0.2s, background 0.2s;
}
+18 -6
View File
@@ -1,6 +1,18 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect width="32" height="32" rx="6" fill="#0a0a0a"/>
<text x="16" y="22" text-anchor="middle" font-family="Arial Black,sans-serif" font-weight="900" font-size="18">
<tspan fill="white">q</tspan><tspan fill="#0066FF">X</tspan>
</text>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<defs>
<linearGradient id="lv-bg" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#0A1628"/>
<stop offset="100%" stop-color="#0D1F3C"/>
</linearGradient>
<linearGradient id="lv-accent" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#00D4AA"/>
<stop offset="100%" stop-color="#00A884"/>
</linearGradient>
</defs>
<!-- Background -->
<rect width="512" height="512" rx="128" fill="url(#lv-bg)"/>
<!-- L shape -->
<path d="M128 128 H224 V224 H384 V320 H128 Z" fill="url(#lv-accent)"/>
<!-- Data point -->
<circle cx="384" cy="160" r="32" fill="#FFFFFF"/>
</svg>

Before

Width:  |  Height:  |  Size: 312 B

After

Width:  |  Height:  |  Size: 715 B

@@ -120,7 +120,7 @@
background: #e94560;
color: white;
padding: 1rem 2rem;
border-radius: 8px;
border-radius: var(--radius-md);
text-decoration: none;
font-weight: 600;
margin: 0.5rem;
@@ -172,7 +172,7 @@
.feature-card {
background: white;
padding: 2rem;
border-radius: 12px;
border-radius: var(--radius-md);
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
@@ -184,7 +184,7 @@
margin-bottom: 1.5rem;
padding: 1.5rem;
background: #f8f9fa;
border-radius: 8px;
border-radius: var(--radius-md);
}
.cta-section {
@@ -350,75 +350,75 @@
</tr>
<tr>
<td><strong>AI-Powered Analysis</strong></td>
<td class="winner"> Full orchestration</td>
<td> Basic tagging</td>
<td> None</td>
<td> Templates only</td>
<td> None</td>
<td class="winner"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Full orchestration</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Basic tagging</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Templates only</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
</tr>
<tr class="highlight">
<td><strong>Video Support</strong></td>
<td class="winner"> 4K/8K + metadata</td>
<td> Photos only</td>
<td> Basic video</td>
<td> Limited</td>
<td> None</td>
<td class="winner"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> 4K/8K + metadata</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Photos only</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Basic video</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Limited</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
</tr>
<tr>
<td><strong>Predictive Analytics</strong></td>
<td class="winner"> 0-100 scoring</td>
<td> None</td>
<td> None</td>
<td> None</td>
<td> None</td>
<td class="winner"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> 0-100 scoring</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
</tr>
<tr class="highlight">
<td><strong>Real-time Updates</strong></td>
<td class="winner"> Live intelligence</td>
<td> Batch upload</td>
<td> Near real-time</td>
<td> Near real-time</td>
<td> Delayed</td>
<td class="winner"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Live intelligence</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Batch upload</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Near real-time</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Near real-time</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Delayed</td>
</tr>
<tr>
<td><strong>Drone Integration</strong></td>
<td class="winner"> Native support</td>
<td> Manual upload</td>
<td> Third-party</td>
<td> None</td>
<td> N/A</td>
<td class="winner"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Native support</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Manual upload</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Third-party</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> N/A</td>
</tr>
<tr class="highlight">
<td><strong>Knowledge Graph</strong></td>
<td class="winner"> Built-in</td>
<td> None</td>
<td> None</td>
<td> None</td>
<td> Spreadsheets</td>
<td class="winner"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Built-in</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Spreadsheets</td>
</tr>
<tr>
<td><strong>Active Learning</strong></td>
<td class="winner"> RALE engine</td>
<td> Static</td>
<td> Static</td>
<td> Static</td>
<td> N/A</td>
<td class="winner"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> RALE engine</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Static</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Static</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Static</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> N/A</td>
</tr>
<tr class="highlight">
<td><strong>Contradiction Detection</strong></td>
<td class="winner"> AI-powered</td>
<td> None</td>
<td> None</td>
<td> None</td>
<td> Manual</td>
<td class="winner"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> AI-powered</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Manual</td>
</tr>
<tr>
<td><strong>Deployment Time</strong></td>
<td class="winner"> 24-72 hours</td>
<td> 2-4 weeks</td>
<td> 1-2 weeks</td>
<td> 1-2 weeks</td>
<td> Months</td>
<td class="winner"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> 24-72 hours</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> 2-4 weeks</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> 1-2 weeks</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> 1-2 weeks</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Months</td>
</tr>
<tr class="highlight">
<td><strong>Pricing Model</strong></td>
@@ -430,35 +430,35 @@
</tr>
<tr>
<td><strong>Mobile App</strong></td>
<td class="winner"> quiXzoom (iOS/Android)</td>
<td> iOS/Android</td>
<td> iOS/Android</td>
<td> iOS/Android</td>
<td> Paper forms</td>
<td class="winner"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> quiXzoom (iOS/Android)</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> iOS/Android</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> iOS/Android</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> iOS/Android</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Paper forms</td>
</tr>
<tr class="highlight">
<td><strong>Offline Capability</strong></td>
<td class="winner"> Full offline sync</td>
<td> Limited</td>
<td> Yes</td>
<td> Yes</td>
<td> Always offline</td>
<td class="winner"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Full offline sync</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Limited</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Yes</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Yes</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Always offline</td>
</tr>
<tr>
<td><strong>API Access</strong></td>
<td class="winner"> Full REST API</td>
<td> Limited</td>
<td> Yes</td>
<td> Limited</td>
<td> None</td>
<td class="winner"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Full REST API</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Limited</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Yes</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Limited</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
</tr>
<tr class="highlight">
<td><strong>Custom Reports</strong></td>
<td class="winner"> Automated + custom</td>
<td> Templates</td>
<td> Customizable</td>
<td> Templates</td>
<td> Manual</td>
<td class="winner"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Automated + custom</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Templates</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Customizable</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Templates</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Manual</td>
</tr>
</tbody>
</table>
@@ -470,27 +470,27 @@
<h2 class="section-title">Key Differentiators</h2>
<div class="features-grid">
<div class="feature-card">
<h3>🎯 Purpose-Built for Infrastructure</h3>
<h3> Purpose-Built for Infrastructure</h3>
<p>Unlike general-purpose tools, LandveX RIOS is designed specifically for infrastructure monitoring. Every feature — from video capture to predictive scoring — is optimized for bridges, roads, utilities, and urban assets.</p>
</div>
<div class="feature-card">
<h3>🧠 AI That Learns</h3>
<h3> AI That Learns</h3>
<p>RALE (Reinforcement Active Learning Engine) means your system gets smarter with every observation. Other platforms use static models that never improve without manual updates.</p>
</div>
<div class="feature-card">
<h3>📹 Video-First Approach</h3>
<h3> Video-First Approach</h3>
<p>While competitors focus on photos, LandveX captures continuous 4K/8K video with full metadata. This provides context that static images cannot match — critical for infrastructure assessment.</p>
</div>
<div class="feature-card">
<h3> Speed to Intelligence</h3>
<h3><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M9 2L5 9H8L7 14L11 7H8L9 2Z" fill="#f59e0b"/></svg> Speed to Intelligence</h3>
<p>24-72 hours from mission creation to structured intelligence. Traditional methods take weeks or months. Other software platforms require 1-4 weeks of setup and training.</p>
</div>
<div class="feature-card">
<h3>🌍 Global Scale</h3>
<h3> Global Scale</h3>
<p>quiXzoom network provides verified field contributors in 100+ cities without requiring local staffing. Deploy in Stockholm, Singapore, or São Paulo with equal rigour.</p>
</div>
<div class="feature-card">
<h3>💰 Usage-Based Pricing</h3>
<h3> Usage-Based Pricing</h3>
<p>Pay for intelligence, not seats. Unlike per-user pricing that penalizes growth, LandveX scales with your actual observation volume — fair and predictable.</p>
</div>
</div>
+38 -38
View File
@@ -49,38 +49,38 @@ html,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,'SF Pro Text'
#city-name{font-size:24px;font-weight:900;letter-spacing:-0.8px;color:var(--text);line-height:1}
#city-region{font-size:11px;color:var(--t2);margin-top:3px;font-weight:500}
#city-stats{display:grid;grid-template-columns:1fr 1fr;gap:6px;margin-top:12px}
.cstat{background:var(--s2);border:1px solid var(--border);border-radius:7px;padding:8px 10px}
.cstat{background:var(--s2);border:1px solid var(--border);border-radius: var(--radius-sm);padding:8px 10px}
.cstat-val{font-size:16px;font-weight:800;color:var(--text);letter-spacing:-0.3px}
.cstat-lbl{font-size:9px;font-weight:600;letter-spacing:0.5px;text-transform:uppercase;color:var(--t3);margin-top:1px}
#profile-block{padding:12px 16px;border-bottom:1px solid var(--border)}
#profile-lbl{font-size:9px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:var(--t3);margin-bottom:8px}
#profile-tabs{display:grid;grid-template-columns:1fr 1fr 1fr;gap:3px;background:rgba(0,0,0,0.06);border-radius:10px;padding:2px}
.ptab{padding:6px 4px;border-radius:8px;border:none;background:transparent;cursor:pointer;display:flex;flex-direction:column;align-items:center;gap:2px;transition:all 0.15s;font-size:10px;font-weight:600;color:var(--t2)}
#profile-tabs{display:grid;grid-template-columns:1fr 1fr 1fr;gap:3px;background:rgba(0,0,0,0.06);border-radius: var(--radius-md);padding:2px}
.ptab{padding:6px 4px;border-radius: var(--radius-md);border:none;background:transparent;cursor:pointer;display:flex;flex-direction:column;align-items:center;gap:2px;transition:all 0.15s;font-size:10px;font-weight:600;color:var(--t2)}
.ptab:hover{background:var(--s2)}
.ptab.active{background:#fff;color:var(--text);box-shadow:0 1px 4px rgba(0,0,0,0.12)}
#district-block{padding:12px 16px}
#district-lbl{font-size:9px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:var(--t3);margin-bottom:8px}
.dcard{border-radius:12px;border:none;background:var(--s2);padding:11px 13px;margin-bottom:6px;cursor:pointer;transition:all 0.12s}
.dcard{border-radius: var(--radius-md);border:none;background:var(--s2);padding:11px 13px;margin-bottom:6px;cursor:pointer;transition:all 0.12s}
.dcard:hover,.dcard.active{background:var(--s3)}
.dcard-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:3px}
.dcard-name{font-size:13px;font-weight:700;color:var(--text)}
.dcard-score{font-size:11px;font-weight:800;padding:1px 7px;border-radius:4px;font-family:var(--mono)}
.dcard-score{font-size:11px;font-weight:800;padding:1px 7px;border-radius: var(--radius-sm);font-family:var(--mono)}
.dcard-desc{font-size:12px;color:var(--t2);line-height:1.4;margin-bottom:5px}
.dcard-contra{display:inline-flex;align-items:center;gap:4px;font-size:9px;font-weight:700;letter-spacing:0.3px;border-radius:4px;padding:2px 7px}
.dcard-contra{display:inline-flex;align-items:center;gap:4px;font-size:9px;font-weight:700;letter-spacing:0.3px;border-radius: var(--radius-sm);padding:2px 7px}
.dc-low{background:rgba(22,163,74,0.12);color:#166534}
.dc-med{background:rgba(217,119,6,0.12);color:#92400e}
.dc-high{background:rgba(220,38,38,0.12);color:#991b1b}
#freshness-block{padding:12px 16px;border-top:1px solid var(--border);margin-top:auto}
#freshness-title{font-size:9px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:var(--t3);margin-bottom:8px;display:flex;align-items:center;gap:5px}
#freshness-title::before{content:'';display:inline-block;width:5px;height:5px;border-radius:50%;background:#22c55e;animation:pulse-ring 2s ease-in-out infinite}
#freshness-title::before{content:'';display:inline-block;width:5px;height:5px;border-radius: var(--radius-full);background:#22c55e;animation:pulse-ring 2s ease-in-out infinite}
.fresh-row{display:flex;align-items:center;justify-content:space-between;padding:5px 0;border-bottom:1px solid var(--border)}
.fresh-row:last-child{border-bottom:none}
.fresh-source{font-size:10px;color:var(--t2);font-weight:500}
.fresh-val{font-size:9px;font-weight:700;font-family:var(--mono);display:flex;align-items:center;gap:4px}
.fresh-dot{width:5px;height:5px;border-radius:50%;flex-shrink:0}
.fresh-dot{width:5px;height:5px;border-radius: var(--radius-full);flex-shrink:0}
.fresh-dot.live{background:#22c55e;animation:pulse-ring 1.5s ease-in-out infinite}
.fresh-dot.recent{background:#fbbf24}
@@ -92,16 +92,16 @@ html,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,'SF Pro Text'
#map-logo em{color:var(--accent3);font-style:normal}
.map-sep{width:1px;height:16px;background:rgba(0,0,0,0.12)}
#map-city-label{font-size:12px;font-weight:700;color:var(--text);flex:1}
#map-profile-chip{font-size:10px;font-weight:700;border-radius:4px;padding:3px 10px;border:1px solid}
#map-profile-chip{font-size:10px;font-weight:700;border-radius: var(--radius-sm);padding:3px 10px;border:1px solid}
#layer-bar{position:absolute;top:44px;left:0;right:0;z-index:999;display:flex;align-items:center;gap:4px;padding:5px 8px;background:#fff;border-bottom:1px solid rgba(0,0,0,0.1);overflow-x:auto;scrollbar-width:none;-webkit-overflow-scrolling:touch}
#layer-bar::-webkit-scrollbar{display:none}
.ltoggle{display:flex;align-items:center;gap:5px;font-size:11px;font-weight:600;color:var(--t2);background:rgba(0,0,0,0.05);border:none;border-radius:20px;padding:5px 12px;cursor:pointer;transition:all 0.15s;white-space:nowrap;user-select:none;flex-shrink:0}
.ltoggle{display:flex;align-items:center;gap:5px;font-size:11px;font-weight:600;color:var(--t2);background:rgba(0,0,0,0.05);border:none;border-radius: var(--radius-xl);padding:5px 12px;cursor:pointer;transition:all 0.15s;white-space:nowrap;user-select:none;flex-shrink:0}
.ltoggle.on{background:var(--accent);color: #1d1d1f}
.ltoggle-dot{width:6px;height:6px;border-radius:50%;flex-shrink:0}
.ltoggle-dot{width:6px;height:6px;border-radius: var(--radius-full);flex-shrink:0}
.leaflet-control-zoom{margin-top:96px!important;margin-left:10px!important}
.leaflet-popup-content-wrapper{background:#fff!important;border:none!important;border-radius:16px!important;box-shadow:0 8px 32px rgba(0,0,0,0.15)!important;padding:0!important}
.leaflet-popup-content-wrapper{background:#fff!important;border:none!important;border-radius: 16px!important;box-shadow:0 8px 32px rgba(0,0,0,0.15)!important;padding:0!important}
.leaflet-popup-content{margin:0!important;width:260px!important}
.leaflet-popup-tip{background:#fff!important}
@@ -113,10 +113,10 @@ html,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,'SF Pro Text'
.mpop-bars{display:flex;flex-direction:column;gap:5px;margin-bottom:10px;padding-bottom:10px;border-bottom:1px solid var(--border)}
.mpop-bar-row{display:flex;align-items:center;gap:6px}
.mpop-bar-lbl{font-size:9px;font-weight:600;letter-spacing:0.3px;text-transform:uppercase;color:var(--t3);width:72px;flex-shrink:0}
.mpop-bar-track{flex:1;height:3px;border-radius:2px;background:rgba(0,0,0,0.06)}
.mpop-bar-fill{height:100%;border-radius:2px}
.mpop-bar-track{flex:1;height:3px;border-radius: var(--radius-sm);background:rgba(0,0,0,0.06)}
.mpop-bar-fill{height:100%;border-radius: var(--radius-sm)}
.mpop-bar-val{font-size:10px;font-weight:700;font-family:var(--mono);width:24px;text-align:right}
.mpop-contra{font-size:10px;border-radius:6px;padding:6px 9px;line-height:1.4;margin-bottom:8px}
.mpop-contra{font-size:10px;border-radius: var(--radius-sm);padding:6px 9px;line-height:1.4;margin-bottom:8px}
.mpop-contra.low{background:rgba(22,163,74,0.08);color:#166534;border:1px solid rgba(22,163,74,0.15)}
.mpop-contra.medium{background:rgba(217,119,6,0.08);color:#92400e;border:1px solid rgba(217,119,6,0.15)}
.mpop-contra.high{background:rgba(220,38,38,0.08);color:#991b1b;border:1px solid rgba(220,38,38,0.15)}
@@ -129,7 +129,7 @@ html,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,'SF Pro Text'
#right-profile-label{font-size:9px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:var(--t3);margin-bottom:4px}
#right-title{font-size:17px;font-weight:700;color:var(--text)}
#kpi-grid{padding:10px 16px 8px;display:grid;grid-template-columns:1fr 1fr;gap:6px;border-bottom:1px solid var(--border)}
.kpi{background:var(--s2);border-radius:12px;padding:12px 14px}
.kpi{background:var(--s2);border-radius: var(--radius-md);padding:12px 14px}
.kpi-label{font-size:10px;font-weight:700;letter-spacing:0.5px;text-transform:uppercase;margin-bottom:4px}
.kpi-value{font-size:22px;font-weight:800;letter-spacing:-0.5px;font-family:var(--mono);line-height:1}
.kpi-sub{font-size:11px;color:var(--t2);margin-top:3px}
@@ -146,14 +146,14 @@ html,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,'SF Pro Text'
#contra-block{padding:12px 16px;border-bottom:1px solid var(--border)}
#contra-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px}
#contra-title{font-size:9px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:var(--t3);display:flex;align-items:center;gap:5px}
.contra-live-dot{width:5px;height:5px;border-radius:50%;background:#f87171;animation:pulse-ring 1.8s ease-in-out infinite}
.contra-count{font-size:9px;font-weight:700;background:rgba(220,38,38,0.12);color:#991b1b;padding:1px 7px;border-radius:10px}
.contra-card{border-radius:7px;border:1px solid var(--border);background:var(--s2);padding:9px 11px;margin-bottom:5px}
.contra-live-dot{width:5px;height:5px;border-radius: var(--radius-full);background:#f87171;animation:pulse-ring 1.8s ease-in-out infinite}
.contra-count{font-size:9px;font-weight:700;background:rgba(220,38,38,0.12);color:#991b1b;padding:1px 7px;border-radius: var(--radius-md)}
.contra-card{border-radius: var(--radius-sm);border:1px solid var(--border);background:var(--s2);padding:9px 11px;margin-bottom:5px}
.contra-card.c-medium{border-left:3px solid rgba(217,119,6,0.5)}
.contra-card.c-high{border-left:3px solid rgba(220,38,38,0.6)}
.contra-card-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:3px}
.contra-card-name{font-size:11px;font-weight:700;color:var(--text)}
.contra-card-badge{font-size:8px;font-weight:700;letter-spacing:0.5px;text-transform:uppercase;padding:1px 6px;border-radius:3px}
.contra-card-badge{font-size:8px;font-weight:700;letter-spacing:0.5px;text-transform:uppercase;padding:1px 6px;border-radius: var(--radius-sm)}
.contra-card-badge.medium{background:rgba(217,119,6,0.12);color:#92400e}
.contra-card-badge.high{background:rgba(220,38,38,0.12);color:#991b1b}
.contra-card-desc{font-size:10px;color:var(--t2);line-height:1.4}
@@ -161,21 +161,21 @@ html,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,'SF Pro Text'
#right-footer{padding:10px 16px;font-size:9px;color:var(--t3);line-height:1.4;border-top:1px solid var(--border)}
/* BOTTOM SHEET (mobile) */
#bottom-sheet{display:none;position:fixed;left:0;right:0;bottom:0;z-index:9999;background:#fff;border-top:1px solid rgba(0,0,0,0.1);border-radius:16px 16px 0 0;box-shadow:0 -4px 24px rgba(0,0,0,0.12);max-height:72vh;overflow-y:auto;transform:translateY(100%);transition:transform 0.28s cubic-bezier(0.32,0.72,0,1)}
#bottom-sheet{display:none;position:fixed;left:0;right:0;bottom:0;z-index:9999;background:#fff;border-top:1px solid rgba(0,0,0,0.1);border-radius: var(--radius-lg) 16px 0 0;box-shadow:0 -4px 24px rgba(0,0,0,0.12);max-height:72vh;overflow-y:auto;transform:translateY(100%);transition:transform 0.28s cubic-bezier(0.32,0.72,0,1)}
#bottom-sheet.open{display:block;transform:translateY(0)}
#bottom-sheet-handle{width:36px;height:4px;border-radius:2px;background:rgba(0,0,0,0.12);margin:10px auto 0}
#bottom-sheet-close{position:absolute;top:12px;right:14px;background:rgba(0,0,0,0.06);border:none;color:var(--text);font-size:16px;width:28px;height:28px;border-radius:50%;cursor:pointer;display:flex;align-items:center;justify-content:center}
#bottom-sheet-handle{width:36px;height:4px;border-radius: var(--radius-sm);background:rgba(0,0,0,0.12);margin:10px auto 0}
#bottom-sheet-close{position:absolute;top:12px;right:14px;background:rgba(0,0,0,0.06);border:none;color:var(--text);font-size:16px;width:28px;height:28px;border-radius: var(--radius-full);cursor:pointer;display:flex;align-items:center;justify-content:center}
#bottom-sheet-content{padding:8px 0 env(safe-area-inset-bottom,16px)}
#bottom-sheet-backdrop{display:none;position:fixed;inset:0;z-index:9998;background:rgba(0,0,0,0.4)}
#bottom-sheet-backdrop.open{display:block}
#map-legend{position:absolute;bottom:60px;right:10px;z-index:800;background:rgba(0,0,0,0.92);backdrop-filter:blur(12px);border:1px solid rgba(0,0,0,0.08);border-radius:10px;padding:10px 12px}
#map-legend{position:absolute;bottom:60px;right:10px;z-index:800;background:rgba(0,0,0,0.92);backdrop-filter:blur(12px);border:1px solid rgba(0,0,0,0.08);border-radius: var(--radius-md);padding:10px 12px}
@keyframes pulse-ring{0%{opacity:1;transform:scale(1)}50%{opacity:0.6;transform:scale(1.2)}100%{opacity:1;transform:scale(1)}}
@keyframes pulse-wave{0%{transform:scale(1);opacity:0.7}100%{transform:scale(3.2);opacity:0}}
.pulse-marker{pointer-events:none}
.pulse-outer{position:absolute;border-radius:50%;animation:pulse-wave 2.2s ease-out infinite}
.pulse-inner{position:absolute;border-radius:50%;transform:translate(-50%,-50%)}
.pulse-outer{position:absolute;border-radius: var(--radius-full);animation:pulse-wave 2.2s ease-out infinite}
.pulse-inner{position:absolute;border-radius: var(--radius-full);transform:translate(-50%,-50%)}
@media(max-width:900px){
html,body{height:auto;overflow-y:auto;overflow-x:hidden}
@@ -365,9 +365,9 @@ html,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,'SF Pro Text'
<div id="map-city-label">Göteborg — <span id="map-profile-label">Business</span></div>
<div id="map-profile-chip" style="background:rgba(0,122,255,0.12);color:#0066cc;border-color:rgba(0,122,255,0.25)">Business</div>
<div style="display:flex;gap:4px;margin-left:auto">
<button onclick="map.setView([57.71,11.97],11)" style="background:rgba(0,0,0,0.06);border:none;border-radius:6px;padding:4px 8px;font-size:10px;font-weight:600;color:rgba(28,28,30,0.6);cursor:pointer">GBG</button>
<button onclick="map.setView([57.0,12.5],8)" style="background:rgba(0,0,0,0.06);border:none;border-radius:6px;padding:4px 8px;font-size:10px;font-weight:600;color:rgba(28,28,30,0.6);cursor:pointer">Region</button>
<button onclick="window.location='/intelligence'" style="background:rgba(0,0,0,0.06);border:none;border-radius:6px;padding:4px 8px;font-size:10px;font-weight:600;color:rgba(28,28,30,0.6);cursor:pointer">All cities</button>
<button onclick="map.setView([57.71,11.97],11)" style="background:rgba(0,0,0,0.06);border:none;border-radius: var(--radius-sm);padding:4px 8px;font-size:10px;font-weight:600;color:rgba(28,28,30,0.6);cursor:pointer">GBG</button>
<button onclick="map.setView([57.0,12.5],8)" style="background:rgba(0,0,0,0.06);border:none;border-radius: var(--radius-sm);padding:4px 8px;font-size:10px;font-weight:600;color:rgba(28,28,30,0.6);cursor:pointer">Region</button>
<button onclick="window.location='/intelligence'" style="background:rgba(0,0,0,0.06);border:none;border-radius: var(--radius-sm);padding:4px 8px;font-size:10px;font-weight:600;color:rgba(28,28,30,0.6);cursor:pointer">All cities</button>
</div>
</div>
<div id="layer-bar">
@@ -388,10 +388,10 @@ html,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,'SF Pro Text'
<div id="map-legend">
<div style="font-size:9px;font-weight:700;letter-spacing:0.8px;text-transform:uppercase;color:rgba(28,28,30,0.45);margin-bottom:7px">Intelligence Score</div>
<div style="display:flex;flex-direction:column;gap:5px">
<div style="display:flex;align-items:center;gap:7px;font-size:11px;color:rgba(28,28,30,0.75)"><div style="width:12px;height:12px;border-radius:50%;background:#22c55e;flex-shrink:0"></div>Strong (810)</div>
<div style="display:flex;align-items:center;gap:7px;font-size:11px;color:rgba(28,28,30,0.75)"><div style="width:12px;height:12px;border-radius:50%;background:#eab308;flex-shrink:0"></div>Moderate (68)</div>
<div style="display:flex;align-items:center;gap:7px;font-size:11px;color:rgba(28,28,30,0.75)"><div style="width:12px;height:12px;border-radius:50%;background:#f97316;flex-shrink:0"></div>Weak (46)</div>
<div style="display:flex;align-items:center;gap:7px;font-size:11px;color:rgba(28,28,30,0.75)"><div style="width:12px;height:12px;border-radius:50%;background:#ef4444;flex-shrink:0"></div>Poor (&lt;4)</div>
<div style="display:flex;align-items:center;gap:7px;font-size:11px;color:rgba(28,28,30,0.75)"><div style="width:12px;height:12px;border-radius: var(--radius-full);background:#22c55e;flex-shrink:0"></div>Strong (810)</div>
<div style="display:flex;align-items:center;gap:7px;font-size:11px;color:rgba(28,28,30,0.75)"><div style="width:12px;height:12px;border-radius: var(--radius-full);background:#eab308;flex-shrink:0"></div>Moderate (68)</div>
<div style="display:flex;align-items:center;gap:7px;font-size:11px;color:rgba(28,28,30,0.75)"><div style="width:12px;height:12px;border-radius: var(--radius-full);background:#f97316;flex-shrink:0"></div>Weak (46)</div>
<div style="display:flex;align-items:center;gap:7px;font-size:11px;color:rgba(28,28,30,0.75)"><div style="width:12px;height:12px;border-radius: var(--radius-full);background:#ef4444;flex-shrink:0"></div>Poor (&lt;4)</div>
</div>
<div style="font-size:9px;color:rgba(28,28,30,0.35);margin-top:6px">Tap any area for details</div>
</div>
@@ -425,7 +425,7 @@ html,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,'SF Pro Text'
<div id="police-block" style="padding:12px 16px;border-bottom:1px solid var(--border)">
<div style="font-size:9px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:var(--t3);margin-bottom:8px;display:flex;align-items:center;justify-content:space-between">
Police Events · Live
<span style="font-size:9px;font-weight:700;background:rgba(239,68,68,0.12);color:#991b1b;border-radius:4px;padding:1px 7px" id="police-count">Loading…</span>
<span style="font-size:9px;font-weight:700;background:rgba(239,68,68,0.12);color:#991b1b;border-radius: var(--radius-sm);padding:1px 7px" id="police-count">Loading…</span>
</div>
<div id="police-list" style="font-size:10px;color:var(--t3)">Loading…</div>
</div>
@@ -442,7 +442,7 @@ html,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,'SF Pro Text'
</div>
<script>
/* ── DATA ── */
/* DATA */
const D=[
{id:'centrum',name:'Centrum',lat:57.707,lng:11.967,r:1400,
confidence:{score:89,observation_count:512},
@@ -593,7 +593,7 @@ function buildList(p){
const v=d.s[p];
const col=sc(v);
const cClass=d.c==='high'?'dc-high':d.c==='medium'?'dc-med':'dc-low';
const cTxt=d.c==='high'?' HIGH CONTRA':d.c==='medium'?'· MEDIUM CONTRA':' LOW CONTRA';
const cTxt=d.c==='high'?' HIGH CONTRA':d.c==='medium'?'· MEDIUM CONTRA':'<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> LOW CONTRA';
return `<div class="dcard" id="dc-${d.id}" onclick="focusDist('${d.id}')">
<div class="dcard-head">
<div class="dcard-name">${d.name}</div>
@@ -741,7 +741,7 @@ function buildAlertMarkers(){
const col=d.c==='high'?'#f87171':'#fbbf24';
const icon=L.divIcon({
className:'',
html:`<div style="width:18px;height:18px;border-radius:3px;background:rgba(0,0,0,0.95);border:2px solid ${col};display:flex;align-items:center;justify-content:center;font-size:10px;box-shadow:0 0 10px ${col}66;cursor:pointer">${d.c==='high'?'':'!'}</div>`,
html:`<div style="width:18px;height:18px;border-radius: var(--radius-sm);background:rgba(0,0,0,0.95);border:2px solid ${col};display:flex;align-items:center;justify-content:center;font-size:10px;box-shadow:0 0 10px ${col}66;cursor:pointer">${d.c==='high'?'':'!'}</div>`,
iconSize:[18,18],iconAnchor:[9,9]
});
const ca=CONTRA_ALERTS.find(x=>x.distId===d.id);
@@ -852,7 +852,7 @@ window.addEventListener('DOMContentLoaded',()=>{
polys[d.id]=poly;
if(map.getZoom()>=10){
L.marker([d.lat,d.lng],{
icon:L.divIcon({className:'label-marker',html:`<div style="font-size:11px;font-weight:700;color:#1c1c1e;text-shadow:0 1px 4px rgba(0,0,0,0.8);white-space:nowrap;pointer-events:none;background:rgba(0,0,0,0.75);padding:2px 6px;border-radius:4px">${d.name}</div>`,iconAnchor:[0,0]}),
icon:L.divIcon({className:'label-marker',html:`<div style="font-size:11px;font-weight:700;color:#1c1c1e;text-shadow:0 1px 4px rgba(0,0,0,0.8);white-space:nowrap;pointer-events:none;background:rgba(0,0,0,0.75);padding:2px 6px;border-radius: 4px">${d.name}</div>`,iconAnchor:[0,0]}),
interactive:false
}).addTo(map);
}
+7 -7
View File
@@ -221,7 +221,7 @@
.footer-social {
display: inline-flex; align-items: center; justify-content: center;
width: 34px; height: 34px;
border-radius: 8px;
border-radius: var(--radius-md);
border: 1px solid rgba(0,0,0,0.1);
background: rgba(0,0,0,0.03);
transition: border-color 0.2s, background 0.2s;
@@ -271,7 +271,7 @@
<li><a href="/enterprise/">Enterprise</a></li>
<li><a href="/#contact">Contact</a></li>
</ul>
<a class="btn btn-outline" href="/#contact">Get in touch &#8594;</a>
<a class="btn btn-outline" href="/#contact">Get in touch &#8594A1</a>
</nav>
<div class="page-header">
@@ -292,7 +292,7 @@
<div class="guide-content">
<h2>Getting started with Landvex</h2>
<p>Understand the platform architecture, data flows and how to configure your first intelligence scope.</p>
<span class="guide-link">Read guide &#8594;</span>
<span class="guide-link">Read guide &#8594A1</span>
</div>
</a>
@@ -305,7 +305,7 @@
<div class="guide-content">
<h2>Data quality standards explained</h2>
<p>How Landvex defines, measures and enforces field data quality across contributor networks and AI review layers.</p>
<span class="guide-link">Read guide &#8594;</span>
<span class="guide-link">Read guide &#8594A1</span>
</div>
</a>
@@ -318,7 +318,7 @@
<div class="guide-content">
<h2>How to scope a Landvex pilot</h2>
<p>Define coverage area, select intelligence verticals, establish success criteria and set a realistic deployment timeline.</p>
<span class="guide-link">Read guide &#8594;</span>
<span class="guide-link">Read guide &#8594A1</span>
</div>
</a>
@@ -331,7 +331,7 @@
<div class="guide-content">
<h2>Integrating Landvex with your existing GIS stack</h2>
<p>Connect Landvex intelligence outputs to ArcGIS, QGIS, PostGIS and enterprise spatial data infrastructure via the API.</p>
<span class="guide-link">Contact us &#8594;</span>
<span class="guide-link">Contact us &#8594A1</span>
</div>
</a>
@@ -362,7 +362,7 @@
<a href="/cities/london/">London</a>
<a href="/cities/bangkok/">Bangkok</a>
<a href="/careers/">Careers</a>
<a href="https://www.quixzoom.com/" target="_blank" rel="noopener">quiXzoom &#8594;</a>
<a href="https://www.quixzoom.com/" target="_blank" rel="noopener">quiXzoom &#8594A1</a>
<a href="mailto:contact@landvex.com">contact@landvex.com</a>
<a href="https://www.linkedin.com/company/landvex" target="_blank" rel="noopener" class="footer-social" title="LandveX on LinkedIn">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+181 -133
View File
@@ -69,7 +69,7 @@
a { color: inherit; text-decoration: none; }
/* ── NAV ── */
/* NAV */
nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 1000;
display: flex; align-items: center; justify-content: space-between;
@@ -179,7 +179,7 @@
.btn-outline:hover { background: rgba(0,0,0,0.04); transform: translateY(-1px); }
.btn-lg { padding: 14px 30px; font-size: 16px; border-radius: var(--radius-md); }
/* ── HERO ── */
/* HERO */
.hero {
min-height: 100vh;
display: flex; flex-direction: column; align-items: center; justify-content: center;
@@ -254,7 +254,7 @@
}
.stat-label { font-size: 13px; color: #6E6E73; margin-top: 4px; }
/* ── SECTION COMMON ── */
/* SECTION COMMON */
section { padding: 96px 24px; }
.container { max-width: 1140px; margin: 0 auto; }
@@ -281,7 +281,7 @@
line-height: 1.7;
}
/* ── DELIVER ── */
/* DELIVER */
.deliver-bg { background: var(--surface); }
.deliver-header {
@@ -332,7 +332,7 @@
.deliver-card p { color: #4A4A4A; font-size: 15px; color: #4A4A4A; line-height: 1.6; }
/* ── VERTICALS ── */
/* VERTICALS */
.verticals-header {
text-align: center;
margin-bottom: 60px;
@@ -369,7 +369,7 @@
.vertical-card h3 { color: #0A0A0A; font-size: 16px; font-weight: 700; color: #0A0A0A; margin-bottom: 6px; }
.vertical-card p { color: #4A4A4A; font-size: 14px; color: #4A4A4A; line-height: 1.5; }
/* ── HOW IT WORKS ── */
/* HOW IT WORKS */
.how-bg { background: var(--surface); }
.how-header { text-align: center; margin-bottom: 64px; }
@@ -377,8 +377,10 @@
.how-steps {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 0;
gap: 24px;
position: relative;
max-width: 1000px;
margin: 0 auto;
}
.how-steps::before {
@@ -394,26 +396,29 @@
.how-step:hover { transform: translateY(-3px); border-color: rgba(0,102,255,0.4); }
.how-step {
text-align: center;
padding: 0 20px;
padding: 32px 24px;
position: relative;
background: #ffffff;
border: 1px solid rgba(0,0,0,0.08);
border-radius: var(--radius-lg);
}
.step-num {
width: 56px; height: 56px;
border-radius: 50%;
width: 48px; height: 48px;
border-radius: var(--radius-full);
background: var(--blue-glow);
border: 2px solid var(--blue);
display: flex; align-items: center; justify-content: center;
font-size: 18px; font-weight: 800;
font-size: 16px; font-weight: 800;
color: var(--blue);
margin: 0 auto 24px;
margin: 0 auto 20px;
position: relative; z-index: 1;
}
.how-step h3 { font-size: 16px; font-weight: 700; color: #0A0A0A; margin-bottom: 8px; }
.how-step p { font-size: 14px; color: #4A4A4A; line-height: 1.6; }
/* ── CONTACT ── */
/* CONTACT */
.contact-wrapper {
max-width: 680px;
margin: 0 auto;
@@ -469,7 +474,7 @@
#form-message.success { background: rgba(0,200,80,0.1); border: 1px solid rgba(0,200,80,0.25); color: #4ade80; }
#form-message.error { background: rgba(255,60,60,0.1); border: 1px solid rgba(255,60,60,0.25); color: #f87171; }
/* ── FOOTER ── */
/* FOOTER */
footer {
border-top: 1px solid var(--border);
padding: 40px 40px;
@@ -504,21 +509,22 @@
.footer-social {
width: 32px; height: 32px;
border: 1px solid var(--border);
border-radius: 8px;
border-radius: var(--radius-md);
display: flex; align-items: center; justify-content: center;
transition: border-color 0.2s, background 0.2s;
}
.footer-social:hover { border-color: rgba(0,0,0,0.25); background: rgba(0,0,0,0.04); }
.footer-tagline { font-size: 12px; color: #6E6E73; margin-top: 4px; }
/* ── RESPONSIVE ── */
/* RESPONSIVE */
@media (max-width: 900px) {
nav { padding: 0 16px; }
.nav-links { display: none; }
.deliver-grid { grid-template-columns: 1fr; }
.verticals-grid { grid-template-columns: 1fr 1fr; }
.how-steps { grid-template-columns: 1fr 1fr; gap: 32px; }
.how-steps { grid-template-columns: 1fr 1fr; gap: 20px; }
.how-steps::before { display: none; }
.how-step { padding: 24px 20px; }
}
@media (max-width: 640px) {
@@ -533,7 +539,8 @@
.deliver-icon { overflow: hidden !important; }
.deliver-icon svg { max-width: 24px; max-height: 24px; flex-shrink: 0; }
.verticals-grid { grid-template-columns: 1fr; }
.how-steps { grid-template-columns: 1fr; }
.how-steps { grid-template-columns: 1fr; gap: 16px; }
.how-step { padding: 20px 16px; }
.form-row { grid-template-columns: 1fr; }
.contact-form { padding: 24px 20px; }
.footer-inner { flex-direction: column; text-align: center; }
@@ -562,7 +569,7 @@
.howwework-grid { grid-template-columns: 1fr !important; }
}
/* ── Global mobile fix ───────────────────────────────────────────── */
/* Global mobile fix */
@media(max-width:480px){
img, svg { max-width:100%; }
table { overflow-x:auto; display:block; }
@@ -584,9 +591,9 @@
/* Map section mobile fixes */
@media(max-width:600px){
.map-iframe{min-height:260px!important;height:52vh!important}
.map-overlay{position:static!important;background:rgba(0,0,0,.95)!important;border-radius:0 0 16px 16px;flex-direction:column;align-items:flex-start;gap:12px;padding:14px 16px!important}
.map-cta-row{width:100%;display:flex;gap:8px}
.map-cta-row a{flex:1;text-align:center}
.map-overlay{position:static!important;background:rgba(0,0,0,.95)!important;border-radius: 0 0 16px 16px;flex-direction:column!important;align-items:flex-start!important;gap:12px;padding:14px 16px!important}
.map-cta-row{width:100%!important;display:flex;gap:8px;flex-wrap:wrap}
.map-cta-row a{flex:1;text-align:center;min-width:120px}
}
/* SVG overflow fix — global */
@@ -599,7 +606,7 @@
/* AUDIT FIX m-001 safety net */
html,body{overflow-x:clip}
/* ── SOCIAL PROOF ── */
/* SOCIAL PROOF */
.social-proof {
background: #ffffff;
border-top: 1px solid rgba(0,0,0,0.06);
@@ -629,19 +636,19 @@ html,body{overflow-x:clip}
color: #6e6e73;
background: #FFFFFF;
border: 1px solid rgba(0,0,0,0.08);
border-radius: 8px;
border-radius: var(--radius-md);
padding: 8px 16px;
white-space: nowrap;
transition: border-color 0.2s, color 0.2s;
}
.sp-item:hover { border-color: rgba(0,102,255,0.22); color: #0A0A0A; }
/* ── P4 MICROINTERACTIONS ── */
/* P4 MICROINTERACTIONS */
.deliver-card, .vertical-card, .intel-card {
transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.2s ease;
}
/* ── CTA SECONDARY ── */
/* CTA SECONDARY */
.cta-secondary {
display: inline-flex; align-items: center; gap: 6px;
padding: 14px 22px;
@@ -656,7 +663,7 @@ html,body{overflow-x:clip}
}
.cta-secondary:hover { background: rgba(0,0,0,0.04); border-color: rgba(0,102,255,0.35); color: #0066FF; transform: translateY(-1px); }
/* ── FOOTER ECOSYSTEM ── */
/* FOOTER ECOSYSTEM */
.footer-ecosystem {
font-size: 12px; color: #6e6e73; margin-top: 12px; line-height: 1.5;
}
@@ -670,51 +677,98 @@ html,body{overflow-x:clip}
/* Intelligence Cards v2 */
.intel-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:20px;margin-top:40px}
@media(max-width:768px){.intel-grid{grid-template-columns:1fr}}
.intel-card{background:#ffffff;border:1px solid rgba(0,0,0,.08);border-radius:16px;padding:28px;text-decoration:none;color:inherit;display:flex;flex-direction:column;gap:0;transition:border-color .2s,transform .2s;cursor:pointer}
.intel-card{background:#ffffff;border:1px solid rgba(0,0,0,.08);border-radius: var(--radius-lg);padding:32px;text-decoration:none;color:inherit;display:flex;flex-direction:column;gap:24px;transition:border-color .2s,transform .2s;cursor:pointer}
.intel-card:hover{border-color:rgba(0,102,255,.35);transform:translateY(-2px);box-shadow:0 8px 24px rgba(0,0,0,0.12)}
.intel-card-header{display:flex;align-items:flex-start;justify-content:space-between;margin-bottom:20px}
.intel-card-header{display:flex;align-items:flex-start;justify-content:space-between;margin-bottom:0}
.intel-module{font-size:.6875rem;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:#0066FF;margin-bottom:6px}
.intel-card h3{font-size:1.0625rem;font-weight:800;color: #0A0A0A;margin:0}
.intel-card-geo{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:2px}
.intel-tag{font-size:.625rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase;padding:3px 9px;border-radius:100px;white-space:nowrap}
.intel-tag{font-size:.625rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase;padding:3px 9px;border-radius: 100px;white-space:nowrap}
.intel-tag-live{background:rgba(0,200,83,.12);color:#00C853;border:1px solid rgba(0,200,83,.2)}
.intel-tag-risk{background:rgba(255,59,48,.1);color:#FF3B30;border:1px solid rgba(255,59,48,.2)}
.intel-tag-pred{background:rgba(0,102,255,.12);color:#0066FF;border:1px solid rgba(0,102,255,.2)}
.intel-kpis{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;margin-bottom:20px}
.intel-kpi{background:rgba(0,0,0,.03);border-radius:10px;padding:10px 12px}
.intel-kpis{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;margin-bottom:0}
.intel-kpi{background:rgba(0,0,0,.03);border-radius: var(--radius-md);padding:10px 12px}
.intel-kpi-val{font-size:1.1875rem;font-weight:900;color: #0A0A0A;letter-spacing:-.03em;line-height:1}
.intel-kpi-delta{font-size:.6875rem;font-weight:700;margin-top:3px}
.intel-kpi-delta.up{color:#00C853}
.intel-kpi-delta.down{color:#FF3B30}
.intel-kpi-lbl{font-size:.625rem;color:rgba(0,0,0,.75);margin-top:2px;line-height:1.3}
.intel-bar-list{display:flex;flex-direction:column;gap:7px;margin-bottom:20px}
.intel-bar-list{display:flex;flex-direction:column;gap:7px;margin-bottom:0}
.intel-bar-row{display:flex;align-items:center;gap:10px}
.intel-bar-name{font-size:.75rem;color:rgba(0,0,0,.65);min-width:110px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.intel-bar-track{flex:1;height:5px;background:rgba(0,0,0,.06);border-radius:3px;overflow:hidden}
.intel-bar-fill{height:100%;border-radius:3px;background:#0066FF}
.intel-bar-track{flex:1;height:5px;background:rgba(0,0,0,.06);border-radius: var(--radius-sm);overflow:hidden}
.intel-bar-fill{height:100%;border-radius: var(--radius-sm);background:#0066FF}
.intel-bar-fill.risk{background:#FF3B30}
.intel-bar-fill.opp{background:#00C853}
.intel-bar-score{font-size:.6875rem;font-weight:700;color:rgba(0,0,0,.75);min-width:28px;text-align:right}
.intel-card-footer{display:flex;align-items:center;justify-content:space-between;padding-top:16px;border-top:1px solid rgba(0,0,0,.06);margin-top:auto}
.intel-card-footer{display:flex;align-items:center;justify-content:space-between;padding-top:16px;border-top:1px solid rgba(0,0,0,.06);margin-top:0}
.intel-obs{font-size:.6875rem;color:rgba(0,0,0,.75)}
.intel-cta{font-size:.75rem;font-weight:700;color:#0066FF;display:flex;align-items:center;gap:4px}
.intel-confidence{display:flex;align-items:center;gap:6px;font-size:.6875rem;color:rgba(0,0,0,.75)}
.intel-conf-bar{width:48px;height:4px;background:rgba(0,0,0,.08);border-radius:2px;overflow:hidden}
.intel-conf-fill{height:100%;background:#0066FF;border-radius:2px}
.intel-map-placeholder{background:linear-gradient(135deg,#0d1f3c 0%,#0a1628 50%,#0d2235 100%);border-radius:10px;height:80px;position:relative;overflow:hidden;margin-bottom:20px}
.intel-map-dot{position:absolute;width:8px;height:8px;border-radius:50%;background:#0066FF;box-shadow:0 0 0 3px rgba(0,102,255,.2)}
.intel-conf-bar{width:48px;height:4px;background:rgba(0,0,0,.08);border-radius: var(--radius-sm);overflow:hidden}
.intel-conf-fill{height:100%;background:#0066FF;border-radius: var(--radius-sm)}
.intel-map-placeholder{background:linear-gradient(135deg,#0d1f3c 0%,#0a1628 50%,#0d2235 100%);border-radius: var(--radius-md);height:80px;position:relative;overflow:hidden;margin-bottom:0}
.intel-map-dot{position:absolute;width:8px;height:8px;border-radius: var(--radius-full);background:#0066FF;box-shadow:0 0 0 3px rgba(0,102,255,.2)}
.intel-map-dot.hot{background:#00C853;box-shadow:0 0 0 3px rgba(0,200,83,.2)}
.intel-map-dot.warn{background:#FF3B30;box-shadow:0 0 0 3px rgba(255,59,48,.2)}
/* LV-ILL-02 Pipeline */
.lv-pipeline{display:flex;align-items:center;justify-content:center;gap:0;margin:32px 0 40px;overflow-x:auto;padding:0 8px}
.lv-pipeline-step{display:flex;flex-direction:column;align-items:center;gap:8px;min-width:88px}
.lv-pipeline-icon{width:52px;height:52px;border-radius:14px;background:rgba(0,102,255,.08);border:1px solid rgba(0,102,255,.18);display:flex;align-items:center;justify-content:center;flex-shrink:0}
.lv-pipeline-icon{width:52px;height:52px;border-radius: var(--radius-lg);background:rgba(0,102,255,.08);border:1px solid rgba(0,102,255,.18);display:flex;align-items:center;justify-content:center;flex-shrink:0}
.lv-pipeline-label{font-size:.6875rem;font-weight:700;color:rgba(0,0,0,.75);text-align:center;letter-spacing:.02em;line-height:1.3}
.lv-pipeline-connector{width:32px;height:2px;background:linear-gradient(90deg,rgba(0,102,255,.3),rgba(0,102,255,.1));flex-shrink:0;margin-top:-22px;align-self:flex-start;margin-left:0;position:relative;top:-26px}
@media(max-width:600px){.lv-pipeline{justify-content:flex-start}.lv-pipeline-connector{width:20px}}
@media(max-width:480px){.intel-grid{grid-template-columns:1fr}.intel-kpis{grid-template-columns:repeat(3,1fr)}.intel-bar-name{min-width:80px}}
@media(max-width:480px){.lv-pipeline{gap:0}.lv-pipeline-step{min-width:56px}.lv-pipeline-label{font-size:.5625rem}.lv-pipeline-icon{width:38px;height:38px;border-radius:10px}.lv-pipeline-connector{width:10px}}
@media(max-width:480px){.intel-grid{grid-template-columns:1fr}.intel-kpis{grid-template-columns:repeat(3,1fr);gap:8px}.intel-kpi{padding:8px}.intel-kpi-val{font-size:1rem}.intel-bar-row{gap:8px;flex-wrap:nowrap}.intel-bar-name{min-width:70px;font-size:.6875rem}.intel-bar-score{min-width:24px;font-size:.625rem}}
@media(max-width:480px){.lv-pipeline{gap:0}.lv-pipeline-step{min-width:56px}.lv-pipeline-label{font-size:.5625rem}.lv-pipeline-icon{width:38px;height:38px;border-radius: var(--radius-md)}.lv-pipeline-connector{width:10px}}
/* Mobile fixes for landvex.com */
@media(max-width:480px){
/* Map overlay - REMOVED to avoid conflicts with 600px breakpoint */
/* Intelligence cards */
/* Intelligence cards */
.intel-card {
padding: 16px !important;
}
.intel-card-header {
flex-direction: column !important;
gap: 8px !important;
}
.intel-card-header h3 {
font-size: 1.125rem !important;
}
.intel-kpis {
grid-template-columns: repeat(3, 1fr) !important;
gap: 8px !important;
}
.intel-kpi {
padding: 8px !important;
}
.intel-kpi-val {
font-size: 1rem !important;
}
.intel-kpi-lbl {
font-size: .5625rem !important;
}
.intel-bar-row {
gap: 8px !important;
}
.intel-bar-name {
min-width: 70px !important;
font-size: .6875rem !important;
}
.intel-bar-score {
min-width: 24px !important;
font-size: .625rem !important;
}
.intel-card-footer {
flex-direction: column !important;
gap: 8px !important;
align-items: flex-start !important;
}
}
</style>
@@ -1173,7 +1227,7 @@ html,body{overflow-x:clip}
<h2 class="section-title">From observation to intelligence.<br>Continuously.</h2>
<p class="section-sub" style="margin:0 auto;max-width:600px">RIOS is a vendor-agnostic AI operating system that transforms video observations into structured Reality Intelligence. It supports real-time field analysis today while continuously training proprietary models that gradually replace expensive third-party inference.</p>
</div>
<div class="how-grid" style="display:grid;grid-template-columns:1fr 1fr;gap:2px;background:rgba(0,0,0,.06);border-radius:16px;overflow:hidden">
<div class="how-grid" style="display:grid;grid-template-columns:1fr 1fr;gap:2px;background:rgba(0,0,0,.06);border-radius: var(--radius-lg);overflow:hidden">
<div style="background:#ffffff;padding:48px 44px;position:relative;border:1px solid rgba(0,0,0,0.08)">
<div style="font-size:.6875rem;font-weight:800;color:#0066FF;text-transform:uppercase;letter-spacing:.14em;margin-bottom:20px">01</div>
<h3 style="font-size:1.375rem;font-weight:800;color:#0A0A0A;letter-spacing:-.03em;margin-bottom:12px;line-height:1.2">Reality Capture</h3>
@@ -1202,10 +1256,10 @@ html,body{overflow-x:clip}
<section class="social-proof">
<p class="sp-label">Trusted by decision makers in</p>
<div class="sp-logos">
<div class="sp-item">🏛️ Municipal Government</div>
<div class="sp-item">🏗️ Infrastructure</div>
<div class="sp-item">🏢 Real Estate</div>
<div class="sp-item">🛡️ Insurance</div>
<div class="sp-item"> Municipal Government</div>
<div class="sp-item"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 14H14M4 14V8L8 4L12 8V14" stroke="#666" stroke-width="1.5" stroke-linejoin="round"/><path d="M6 14V10H10V14" stroke="#666" stroke-width="1.5"/></svg> Infrastructure</div>
<div class="sp-item"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="3" y="2" width="10" height="12" rx="1" stroke="#666" stroke-width="1.5"/><path d="M6 5H7M6 8H7M6 11H7M9 5H10M9 8H10M9 11H10" stroke="#666" stroke-width="1.5" stroke-linecap="round"/></svg> Real Estate</div>
<div class="sp-item"> Insurance</div>
<div class="sp-item"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#0066FF" stroke-width="2"><path d="M18 20V10"/><path d="M12 20V4"/><path d="M6 20v-6"/></svg> Investment</div>
</div>
</section>
@@ -1216,8 +1270,8 @@ html,body{overflow-x:clip}
<!-- Header -->
<div style="text-align:center;margin-bottom:32px">
<div style="display:inline-flex;align-items:center;gap:8px;background:rgba(0,102,255,.08);border:1px solid rgba(0,102,255,.18);border-radius:20px;padding:5px 14px;font-size:.75rem;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:#0052CC;margin-bottom:16px">
<span style="width:7px;height:7px;border-radius:50%;background:#0052CC;display:inline-block"></span>
<div style="display:inline-flex;align-items:center;gap:8px;background:rgba(0,102,255,.08);border:1px solid rgba(0,102,255,.18);border-radius: var(--radius-xl);padding:5px 14px;font-size:.75rem;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:#0052CC;margin-bottom:16px">
<span style="width:7px;height:7px;border-radius: var(--radius-full);background:#0052CC;display:inline-block"></span>
Live demonstration
</div>
<h2 style="font-size:clamp(22px,4vw,36px);font-weight:800;letter-spacing:-.5px;color:#1d1d1f;margin-bottom:12px;line-height:1.15">Intelligence Map &mdash; 100+ Cities</h2>
@@ -1225,29 +1279,28 @@ html,body{overflow-x:clip}
</div>
<!-- Map frame -->
<div style="border-radius:16px;overflow:hidden;border:1px solid rgba(0,0,0,.1);box-shadow:0 8px 32px rgba(0,0,0,.10)">
<div style="border-radius: var(--radius-lg);overflow:hidden;border:1px solid rgba(0,0,0,.1);box-shadow:0 8px 32px rgba(0,0,0,.10)">
<iframe src="/intelligence-map.html" style="width:100%;height:min(68vh,560px);min-height:320px;border:none;display:block" title="Landvex Intelligence Map" loading="lazy" allow="geolocation" class="map-iframe"></iframe>
<!-- Bottom bar: sits flush under map, inside same border-radius container -->
<div class="map-overlay" style="color:#0A0A0A;background:rgba(0,0,0,.97);padding:14px 20px;display:flex;align-items:center;justify-content:space-between;gap:16px;flex-wrap:wrap;border-top:1px solid rgba(255,255,255,.07)">
<div style="display:flex;align-items:center;gap:10px;flex:1;min-width:0">
<div style="width:6px;height:6px;border-radius:50%;background:#22c55e;flex-shrink:0"></div>
<span style="font-size:.8125rem;color:#4A4A4A;white-space:nowrap;overflow:hidden;text-overflow:ellipsis">Source First &mdash; every data point traceable to source &rsaquo; observation &rsaquo; model &rsaquo; index</span>
<div style="width:6px;height:6px;border-radius: var(--radius-full);background:#22c55e;flex-shrink:0"></div>
<span style="font-size:.8125rem;color:#4A4A4A;white-space:nowrap;overflow:hidden;text-overflow:ellipsis">Source First</span>
</div>
<div class="map-cta-row" style="display:flex;gap:8px;flex-shrink:0">
<a href="/intelligence-map" style="background:#0066FF;color: #0A0A0A;font-size:.8125rem;font-weight:700;padding:9px 18px;border-radius:8px;text-decoration:none;white-space:nowrap">Open full map &rarr;</a>
<a href="/stockholm-intelligence" style="background:rgba(0,0,0,.07);color:rgba(0,0,0,.85);font-size:.8125rem;font-weight:600;padding:9px 18px;border-radius:8px;text-decoration:none;border:1px solid rgba(0,0,0,.12);white-space:nowrap">Stockholm deep dive</a>
<div class="map-cta-row" style="display:flex;gap:8px;flex-shrink:0;flex-wrap:wrap">
<a href="/intelligence-map" style="background:#0066FF;color: #fff;font-size:.8125rem;font-weight:700;padding:9px 18px;border-radius: var(--radius-md);text-decoration:none;white-space:nowrap">Open full map </a>
</div>
</div>
</div>
<!-- Tags -->
<div style="display:flex;flex-wrap:wrap;gap:8px;justify-content:center;margin-top:20px">
<span style="font-size:.75rem;font-weight:600;background:rgba(0,0,0,.05);color:rgba(0,0,0,.4);border-radius:20px;padding:5px 13px;border:1px solid rgba(0,0,0,.07)">100+ cities</span>
<span style="font-size:.75rem;font-weight:600;background:rgba(0,0,0,.05);color:rgba(0,0,0,.4);border-radius:20px;padding:5px 13px;border:1px solid rgba(0,0,0,.07)">Contradiction Index</span>
<span style="font-size:.75rem;font-weight:600;background:rgba(0,0,0,.05);color:rgba(0,0,0,.4);border-radius:20px;padding:5px 13px;border:1px solid rgba(0,0,0,.07)">Infrastructure Risk</span>
<span style="font-size:.75rem;font-weight:600;background:rgba(0,0,0,.05);color:rgba(0,0,0,.4);border-radius:20px;padding:5px 13px;border:1px solid rgba(0,0,0,.07)">Commercial Vitality</span>
<span style="font-size:.75rem;font-weight:600;background:rgba(0,0,0,.05);color:rgba(0,0,0,.4);border-radius:20px;padding:5px 13px;border:1px solid rgba(0,0,0,.07)">City Health</span>
<span style="font-size:.75rem;font-weight:600;background:rgba(0,0,0,.05);color:rgba(0,0,0,.4);border-radius:20px;padding:5px 13px;border:1px solid rgba(0,0,0,.07)">Source First</span>
<span style="font-size:.75rem;font-weight:600;background:rgba(0,0,0,.05);color:rgba(0,0,0,.4);border-radius: var(--radius-xl);padding:5px 13px;border:1px solid rgba(0,0,0,.07)">100+ cities</span>
<span style="font-size:.75rem;font-weight:600;background:rgba(0,0,0,.05);color:rgba(0,0,0,.4);border-radius: var(--radius-xl);padding:5px 13px;border:1px solid rgba(0,0,0,.07)">Contradiction Index</span>
<span style="font-size:.75rem;font-weight:600;background:rgba(0,0,0,.05);color:rgba(0,0,0,.4);border-radius: var(--radius-xl);padding:5px 13px;border:1px solid rgba(0,0,0,.07)">Infrastructure Risk</span>
<span style="font-size:.75rem;font-weight:600;background:rgba(0,0,0,.05);color:rgba(0,0,0,.4);border-radius: var(--radius-xl);padding:5px 13px;border:1px solid rgba(0,0,0,.07)">Commercial Vitality</span>
<span style="font-size:.75rem;font-weight:600;background:rgba(0,0,0,.05);color:rgba(0,0,0,.4);border-radius: var(--radius-xl);padding:5px 13px;border:1px solid rgba(0,0,0,.07)">City Health</span>
<span style="font-size:.75rem;font-weight:600;background:rgba(0,0,0,.05);color:rgba(0,0,0,.4);border-radius: var(--radius-xl);padding:5px 13px;border:1px solid rgba(0,0,0,.07)">Source First</span>
</div>
</div>
@@ -1262,7 +1315,7 @@ html,body{overflow-x:clip}
<h2 class="section-title" style="color:#0A0A0A;margin-bottom:20px;line-height:1.05;text-shadow:none">We start with your decision.<br>Not with our data.</h2>
<p style="font-size:1.0625rem;color:#4A4A4A;line-height:1.7;max-width:480px">Most intelligence platforms sell you a dataset and leave the interpretation to you. Landvex works backwards — from the decision you need to make to the intelligence required to make it confidently.</p>
</div>
<div class="how-grid" style="display:grid;grid-template-columns:1fr 1fr;gap:2px;background:rgba(0,0,0,.06);border-radius:16px;overflow:hidden">
<div class="how-grid" style="display:grid;grid-template-columns:1fr 1fr;gap:2px;background:rgba(0,0,0,.06);border-radius: var(--radius-lg);overflow:hidden">
<div style="background:#ffffff;padding:48px 44px;position:relative;border:1px solid rgba(0,0,0,0.08)">
<div style="font-size:.6875rem;font-weight:800;color:#0066FF;text-transform:uppercase;letter-spacing:.14em;margin-bottom:20px">01</div>
<h3 style="font-size:1.375rem;font-weight:800;color:#0A0A0A;letter-spacing:-.03em;margin-bottom:12px;line-height:1.2">Define the decision</h3>
@@ -1294,7 +1347,7 @@ html,body{overflow-x:clip}
<div class="section-label">Decision intelligence</div>
<h2 class="section-title">Ten decisions. One platform.</h2>
<p class="section-sub" style="margin:0 auto;max-width:600px">Property owners, municipalities, retailers, infrastructure operators, investors. Different industries — identical need: to make better decisions about the physical world. Landvex is built for that.</p>
<div class="lv-ill-wrap" aria-hidden="true">
<div class="lv-ill-wrap" aria-hidden="true" style="margin: 40px 0;">
<svg viewBox="0 0 600 140" xmlns="http://www.w3.org/2000/svg" width="600" height="140">
<defs>
<style>@media(prefers-reduced-motion:no-preference){.pla{animation:pla 2.4s ease-in-out infinite}.plb{animation:pla 2.4s ease-in-out 1.2s infinite}}@keyframes pla{0%,100%{opacity:.65}50%{opacity:1}}</style>
@@ -1330,7 +1383,7 @@ html,body{overflow-x:clip}
</div>
<p style="text-align:center;color:var(--text-muted);font-size:0.875rem;margin:16px 0 8px;">⚠️ All intelligence examples below are illustrative sample data for demonstration purposes.</p>
<p style="text-align:center;color:var(--text-muted);font-size:0.875rem;margin:32px 0 24px;"><svg width="20" height="20" viewBox="0 0 20 20" fill="none"><path d="M10 3L17 17H3L10 3Z" stroke="#f59e0b" stroke-width="2"/><path d="M10 8V12M10 14V14.5" stroke="#f59e0b" stroke-width="2" stroke-linecap="round"/></svg> All intelligence examples below are illustrative sample data for demonstration purposes.</p>
<div class="intel-grid">
@@ -1487,13 +1540,7 @@ html,body{overflow-x:clip}
<div class="intel-kpi-lbl">Identified</div>
</div>
</div>
<div class="intel-confidence">
<span>Model confidence</span>
<div class="intel-conf-bar"><div class="intel-conf-fill" style="width:87%"></div></div>
<span>87%</span>
<span style="margin-left:auto;font-size:.625rem">Commercial permits &middot; Population shifts &middot; Traffic flows</span>
</div>
<div class="intel-card-footer" style="margin-top:12px">
<div class="intel-card-footer">
<span class="intel-obs">Indicative signals &mdash; not investment advice</span>
<span class="intel-cta">View forecast <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M5 12h14M12 5l7 7-7 7"/></svg></span>
</div>
@@ -1504,36 +1551,37 @@ html,body{overflow-x:clip}
<style>
.verticals-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 16px;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
align-items: stretch;
}
@media(max-width:768px){.verticals-grid{grid-template-columns:1fr 1fr}}
@media(max-width:480px){.verticals-grid{grid-template-columns:1fr}}
.vertical-card {
background: #fff;
border: 1px solid #eee;
border-radius: 12px;
padding: 24px;
border: 1px solid rgba(0,0,0,0.08);
border-radius: var(--radius-lg);
padding: 28px;
display: flex;
flex-direction: column;
gap: 8px;
gap: 12px;
text-decoration: none;
color: inherit;
transition: box-shadow 0.2s;
}
.vertical-card:hover {
box-shadow: 0 4px 12px rgba(0,0,0,0.08);
box-shadow: 0 8px 24px rgba(0,0,0,0.12);
}
.vertical-card h3 {
margin: 0;
font-size: 1.1rem;
font-weight: 600;
font-size: 1.0625rem;
font-weight: 700;
}
.vertical-card p {
margin: 0;
font-size: 0.875rem;
line-height: 1.5;
color: #666;
flex-grow: 1;
color: #4A4A4A;
}
.vertical-icon {
margin-bottom: 4px;
@@ -1616,7 +1664,7 @@ html,body{overflow-x:clip}
</div>
<div class="deliver-grid">
<a class="deliver-card" href="/methodology/" style="text-decoration:none;color:inherit;cursor:pointer;display:block">
<div class="deliver-icon" style="width:48px;height:48px;min-width:48px;overflow:hidden;background:rgba(0,102,255,.12);border-radius:12px;display:flex;align-items:center;justify-content:center;margin-bottom:16px;flex-shrink:0">
<div class="deliver-icon" style="width:48px;height:48px;min-width:48px;overflow:hidden;background:rgba(0,102,255,.12);border-radius: var(--radius-md);display:flex;align-items:center;justify-content:center;margin-bottom:16px;flex-shrink:0">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#0066ff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>
</div>
<div class="number">20+</div>
@@ -1625,7 +1673,7 @@ html,body{overflow-x:clip}
<span style="font-size:.8rem;font-weight:700;color:#0066ff;margin-top:12px;display:block">How it works →</span>
</a>
<a class="deliver-card" href="/methodology/" style="text-decoration:none;color:inherit;cursor:pointer;display:block">
<div class="deliver-icon" style="width:48px;height:48px;min-width:48px;overflow:hidden;background:rgba(0,102,255,.12);border-radius:12px;display:flex;align-items:center;justify-content:center;margin-bottom:16px;flex-shrink:0">
<div class="deliver-icon" style="width:48px;height:48px;min-width:48px;overflow:hidden;background:rgba(0,102,255,.12);border-radius: var(--radius-md);display:flex;align-items:center;justify-content:center;margin-bottom:16px;flex-shrink:0">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#0066ff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
</div>
<div class="number">2472h</div>
@@ -1634,7 +1682,7 @@ html,body{overflow-x:clip}
<span style="font-size:.8rem;font-weight:700;color:#0066ff;margin-top:12px;display:block">Methodology →</span>
</a>
<a class="deliver-card" href="/methodology/" style="text-decoration:none;color:inherit;cursor:pointer;display:block">
<div class="deliver-icon" style="width:48px;height:48px;min-width:48px;overflow:hidden;background:rgba(0,102,255,.12);border-radius:12px;display:flex;align-items:center;justify-content:center;margin-bottom:16px;flex-shrink:0">
<div class="deliver-icon" style="width:48px;height:48px;min-width:48px;overflow:hidden;background:rgba(0,102,255,.12);border-radius: var(--radius-md);display:flex;align-items:center;justify-content:center;margin-bottom:16px;flex-shrink:0">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#0066ff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/></svg>
</div>
<div class="number">AI</div>
@@ -1687,7 +1735,7 @@ html,body{overflow-x:clip}
</a>
<a class="vertical-card" href="/verticals/insurance/" style="border-color:rgba(0,102,255,.25);position:relative;display:block;text-decoration:none;color:inherit;cursor:pointer">
<span style="position:absolute;top:12px;right:12px;background:#0066FF;color:#0A0A0A;font-size:.6rem;font-weight:800;letter-spacing:.08em;text-transform:uppercase;padding:3px 8px;border-radius:100px">Vertical</span>
<span style="position:absolute;top:12px;right:12px;background:#0066FF;color:#0A0A0A;font-size:.6rem;font-weight:800;letter-spacing:.08em;text-transform:uppercase;padding:3px 8px;border-radius: 100px">Vertical</span>
<div class="vertical-icon"><svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="#0066ff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg></div>
<div>
<h3>Insurance</h3>
@@ -1800,13 +1848,13 @@ html,body{overflow-x:clip}
.score-table thead { display:none; }
.score-table tr { display:grid; grid-template-columns:1fr 1fr; gap:8px; padding:16px 0; border-bottom:1px solid rgba(0,0,0,.07); }
.score-table tr:last-child { border-bottom:none; }
.score-table td { display:flex; flex-direction:column; align-items:center; justify-content:center; gap:4px; padding:10px 8px; background:rgba(0,0,0,.04); border-radius:10px; border-bottom:none; font-size:.75rem; }
.score-table td { display:flex; flex-direction:column; align-items:center; justify-content:center; gap:4px; padding:10px 8px; background:rgba(0,0,0,.04); border-radius: var(--radius-md); border-bottom:none; font-size:.75rem; }
.score-table td:first-child { grid-column:1/-1; background:transparent; align-items:flex-start; font-size:.9375rem; padding:0 0 4px; }
.score-table td::before { content:attr(data-label); font-size:.65rem; font-weight:700; color:rgba(0,0,0,.75); text-transform:uppercase; letter-spacing:.06em; }
}
</style>
<div class="score-table-wrap">
<div style="background:rgba(255,193,7,0.08);border:1px solid rgba(255,193,7,0.3);border-radius:10px;padding:14px 18px;margin-bottom:16px;font-size:.82rem;color:rgba(0,0,0,.85);line-height:1.6">
<div style="background:rgba(255,193,7,0.08);border:1px solid rgba(255,193,7,0.3);border-radius: var(--radius-md);padding:14px 18px;margin-bottom:16px;font-size:.82rem;color:rgba(0,0,0,.85);line-height:1.6">
<strong style="color:#ffc107"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#0066FF" stroke-width="2"><path d="M18 20V10"/><path d="M12 20V4"/><path d="M6 20v-6"/></svg> Format demonstration</strong> — The figures below are a simulated reference example showing report structure and output format. Live intelligence activates from August 2026 as the quiXzoom observation network launches.
</div>
<table class="score-table">
@@ -1822,62 +1870,62 @@ html,body{overflow-x:clip}
</thead>
<tbody>
<tr style="display:none">
<td data-label="">City A — District 1 <span style="font-size:.75rem;opacity:.85;font-weight:600"> sample</span></td>
<td data-label="Opp."><span style="background:#dcfce7;color:#166534;font-weight:800;padding:4px 10px;border-radius:20px">84</span></td>
<td data-label="Growth"><span style="background:#fef9c3;color:#854d0e;font-weight:800;padding:4px 10px;border-radius:20px">78</span></td>
<td data-label="Vitality"><span style="background:#dcfce7;color:#166534;font-weight:800;padding:4px 10px;border-radius:20px">91</span></td>
<td data-label="Infra"><span style="background:#fee2e2;color:#991b1b;font-weight:800;padding:4px 10px;border-radius:20px">62</span></td>
<td data-label="Invest."><span style="background:#fef9c3;color:#854d0e;font-weight:800;padding:4px 10px;border-radius:20px">79</span></td>
<td data-label="">City A — District 1 <span style="font-size:.75rem;opacity:.85;font-weight:600"> sample</span></td>
<td data-label="Opp."><span style="background:#dcfce7;color:#166534;font-weight:800;padding:4px 10px;border-radius: 20px">84</span></td>
<td data-label="Growth"><span style="background:#fef9c3;color:#854d0e;font-weight:800;padding:4px 10px;border-radius: 20px">78</span></td>
<td data-label="Vitality"><span style="background:#dcfce7;color:#166534;font-weight:800;padding:4px 10px;border-radius: 20px">91</span></td>
<td data-label="Infra"><span style="background:#fee2e2;color:#991b1b;font-weight:800;padding:4px 10px;border-radius: 20px">62</span></td>
<td data-label="Invest."><span style="background:#fef9c3;color:#854d0e;font-weight:800;padding:4px 10px;border-radius: 20px">79</span></td>
</tr>
<tr style="display:none">
<td data-label="">City B — Central <span style="font-size:.75rem;opacity:.85;font-weight:600"> sample</span></td>
<td data-label="Opp."><span style="background:#fef9c3;color:#854d0e;font-weight:800;padding:4px 10px;border-radius:20px">77</span></td>
<td data-label="Growth"><span style="background:#fee2e2;color:#991b1b;font-weight:800;padding:4px 10px;border-radius:20px">61</span></td>
<td data-label="Vitality"><span style="background:#dcfce7;color:#166534;font-weight:800;padding:4px 10px;border-radius:20px">83</span></td>
<td data-label="Infra"><span style="background:#dcfce7;color:#166534;font-weight:800;padding:4px 10px;border-radius:20px">88</span></td>
<td data-label="Invest."><span style="background:#dcfce7;color:#166534;font-weight:800;padding:4px 10px;border-radius:20px">82</span></td>
<td data-label="">City B — Central <span style="font-size:.75rem;opacity:.85;font-weight:600"> sample</span></td>
<td data-label="Opp."><span style="background:#fef9c3;color:#854d0e;font-weight:800;padding:4px 10px;border-radius: 20px">77</span></td>
<td data-label="Growth"><span style="background:#fee2e2;color:#991b1b;font-weight:800;padding:4px 10px;border-radius: 20px">61</span></td>
<td data-label="Vitality"><span style="background:#dcfce7;color:#166534;font-weight:800;padding:4px 10px;border-radius: 20px">83</span></td>
<td data-label="Infra"><span style="background:#dcfce7;color:#166534;font-weight:800;padding:4px 10px;border-radius: 20px">88</span></td>
<td data-label="Invest."><span style="background:#dcfce7;color:#166534;font-weight:800;padding:4px 10px;border-radius: 20px">82</span></td>
</tr>
<tr>
<td data-label="">Stockholm — Norrmalm</td>
<td data-label="Opp."><span style="background:#fef9c3;color:#854d0e;font-weight:800;padding:4px 10px;border-radius:20px">71</span></td>
<td data-label="Growth"><span style="background:#fee2e2;color:#991b1b;font-weight:800;padding:4px 10px;border-radius:20px">58</span></td>
<td data-label="Vitality"><span style="background:#fef9c3;color:#854d0e;font-weight:800;padding:4px 10px;border-radius:20px">76</span></td>
<td data-label="Infra"><span style="background:#dcfce7;color:#166534;font-weight:800;padding:4px 10px;border-radius:20px">92</span></td>
<td data-label="Invest."><span style="background:#dcfce7;color:#166534;font-weight:800;padding:4px 10px;border-radius:20px">81</span></td>
<td data-label="Opp."><span style="background:#fef9c3;color:#854d0e;font-weight:800;padding:4px 10px;border-radius: 20px">71</span></td>
<td data-label="Growth"><span style="background:#fee2e2;color:#991b1b;font-weight:800;padding:4px 10px;border-radius: 20px">58</span></td>
<td data-label="Vitality"><span style="background:#fef9c3;color:#854d0e;font-weight:800;padding:4px 10px;border-radius: 20px">76</span></td>
<td data-label="Infra"><span style="background:#dcfce7;color:#166534;font-weight:800;padding:4px 10px;border-radius: 20px">92</span></td>
<td data-label="Invest."><span style="background:#dcfce7;color:#166534;font-weight:800;padding:4px 10px;border-radius: 20px">81</span></td>
</tr>
<tr>
<td data-label="">Milan — Porta Nuova</td>
<td data-label="Opp."><span style="background:#dcfce7;color:#166534;font-weight:800;padding:4px 10px;border-radius:20px">86</span></td>
<td data-label="Growth"><span style="background:#fef9c3;color:#854d0e;font-weight:800;padding:4px 10px;border-radius:20px">74</span></td>
<td data-label="Vitality"><span style="background:#dcfce7;color:#166534;font-weight:800;padding:4px 10px;border-radius:20px">88</span></td>
<td data-label="Infra"><span style="background:#fef9c3;color:#854d0e;font-weight:800;padding:4px 10px;border-radius:20px">69</span></td>
<td data-label="Invest."><span style="background:#dcfce7;color:#166534;font-weight:800;padding:4px 10px;border-radius:20px">84</span></td>
<td data-label="Opp."><span style="background:#dcfce7;color:#166534;font-weight:800;padding:4px 10px;border-radius: 20px">86</span></td>
<td data-label="Growth"><span style="background:#fef9c3;color:#854d0e;font-weight:800;padding:4px 10px;border-radius: 20px">74</span></td>
<td data-label="Vitality"><span style="background:#dcfce7;color:#166534;font-weight:800;padding:4px 10px;border-radius: 20px">88</span></td>
<td data-label="Infra"><span style="background:#fef9c3;color:#854d0e;font-weight:800;padding:4px 10px;border-radius: 20px">69</span></td>
<td data-label="Invest."><span style="background:#dcfce7;color:#166534;font-weight:800;padding:4px 10px;border-radius: 20px">84</span></td>
</tr>
</tbody>
</table>
<p style="font-size:.8rem;color:#515154;margin-top:12px;text-align:center;line-height:1.5">Intelligence outputs are advisory analytical signals — not investment advice, financial promotion, or statements of fact about named locations. <a href="/methodology/" style="color:#0066FF;text-decoration:underline">Methodology →</a></p>
</div>
<div style="margin-top:48px;background:#0066FF;border-radius:16px;padding:40px 48px;display:flex;align-items:center;gap:48px;flex-wrap:wrap">
<div style="margin-top:48px;background:#0066FF;border-radius: var(--radius-lg);padding:40px 48px;display:flex;align-items:center;gap:48px;flex-wrap:wrap">
<div style="flex:1;min-width:260px">
<h3 style="color: #0A0A0A;font-size:1.25rem;font-weight:800;margin-bottom:8px">Four layers. One answer.</h3>
<p style="color:rgba(0,0,0,0.85);line-height:1.65;font-size:.9375rem">quiXzoom observes. AMOS analyses. Landvex delivers the answer. Every engagement starts with one question: <em style="color: #0A0A0A;font-style:normal">what decision are you trying to make?</em></p>
<!-- quiXzoom → Landvex → You pipeline -->
<div style="display:flex;align-items:center;gap:0;margin-top:24px;flex-wrap:wrap">
<div style="display:flex;flex-direction:column;align-items:center;gap:6px;min-width:100px;padding:0 8px">
<div style="width:44px;height:44px;border-radius:12px;background:rgba(0,0,0,0.15);border:1px solid rgba(0,0,0,0.25);display:flex;align-items:center;justify-content:center;font-size:20px">🔭</div>
<div style="width:44px;height:44px;border-radius: var(--radius-md);background:rgba(0,0,0,0.15);border:1px solid rgba(0,0,0,0.25);display:flex;align-items:center;justify-content:center;font-size:20px"></div>
<div style="font-size:11px;font-weight:800;color: #0A0A0A;letter-spacing:.04em">quiXzoom</div>
<div style="font-size:10px;color:rgba(0,0,0,0.75);text-align:center;line-height:1.4">Observes reality on the ground</div>
</div>
<div style="font-size:18px;color:rgba(0,0,0,0.5);margin:0 4px;padding-bottom:22px">&rarr;</div>
<div style="display:flex;flex-direction:column;align-items:center;gap:6px;min-width:100px;padding:0 8px">
<div style="width:44px;height:44px;border-radius:12px;background:rgba(0,0,0,0.15);border:1px solid rgba(0,0,0,0.25);display:flex;align-items:center;justify-content:center;font-size:20px"></div>
<div style="width:44px;height:44px;border-radius: var(--radius-md);background:rgba(0,0,0,0.15);border:1px solid rgba(0,0,0,0.25);display:flex;align-items:center;justify-content:center;font-size:20px"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M9 2L5 9H8L7 14L11 7H8L9 2Z" fill="#f59e0b"/></svg></div>
<div style="font-size:11px;font-weight:800;color: #0A0A0A;letter-spacing:.04em">Landvex</div>
<div style="font-size:10px;color:rgba(0,0,0,0.75);text-align:center;line-height:1.4">Analyses &amp; detects contradictions</div>
</div>
<div style="font-size:18px;color:rgba(0,0,0,0.5);margin:0 4px;padding-bottom:22px">&rarr;</div>
<div style="display:flex;flex-direction:column;align-items:center;gap:6px;min-width:100px;padding:0 8px">
<div style="width:44px;height:44px;border-radius:12px;background:rgba(0,0,0,0.15);border:1px solid rgba(0,0,0,0.25);display:flex;align-items:center;justify-content:center;font-size:20px"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#00C853" stroke-width="3"><path d="M20 6L9 17l-5-5"/></svg></div>
<div style="width:44px;height:44px;border-radius: var(--radius-md);background:rgba(0,0,0,0.15);border:1px solid rgba(0,0,0,0.25);display:flex;align-items:center;justify-content:center;font-size:20px"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#00C853" stroke-width="3"><path d="M20 6L9 17l-5-5"/></svg></div>
<div style="font-size:11px;font-weight:800;color: #0A0A0A;letter-spacing:.04em">You decide</div>
<div style="font-size:10px;color:rgba(0,0,0,0.75);text-align:center;line-height:1.4">Act on what&rsquo;s actually happening</div>
</div>
@@ -1896,42 +1944,42 @@ html,body{overflow-x:clip}
<div class="section-label" style="color:#0066FF">Advanced intelligence</div>
<h2 class="section-title">Most systems confirm what you already believe.<br>Landvex tells you when you're wrong.</h2>
<p class="section-sub" style="color:#515154;margin:0 auto;max-width:600px">The most valuable intelligence is not confirmation — it's contradiction. Landvex actively searches for conflicts between official narratives and observed reality, so you can correct course before it costs you.</p>
<p style="color:rgba(0,0,0,.9);font-size:.8rem;margin-top:8px;max-width:500px;margin-left:auto;margin-right:auto;background:rgba(255,149,0,.08);border:1px solid rgba(255,149,0,.25);border-radius:8px;padding:8px 12px"> All contradiction analysis shown is representative data based on real observed patterns. Live intelligence from August 2026. Reports about specific named locations are based on observed field data and explicitly labelled as analytical intelligence — not statements of fact.</p>
<p style="color:rgba(0,0,0,.9);font-size:.8rem;margin-top:8px;max-width:500px;margin-left:auto;margin-right:auto;background:rgba(255,149,0,.08);border:1px solid rgba(255,149,0,.25);border-radius: var(--radius-md);padding:8px 12px"> All contradiction analysis shown is representative data based on real observed patterns. Live intelligence from August 2026. Reports about specific named locations are based on observed field data and explicitly labelled as analytical intelligence — not statements of fact.</p>
</div>
<div style="background:#ffffff;border:1px solid rgba(0,0,0,.08);border-radius:16px;padding:36px 40px;margin-bottom:32px">
<div style="background:#ffffff;border:1px solid rgba(0,0,0,.08);border-radius: var(--radius-lg);padding:36px 40px;margin-bottom:32px">
<div style="display:flex;align-items:flex-start;gap:16px;margin-bottom:24px">
<span style="display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;background:rgba(220,38,38,.08);border-radius:8px"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#f87171" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 15s1-1 4-1 5 2 8 2 4-1 4-1V3s-1 1-4 1-5-2-8-2-4 1-4 1z"/><line x1="4" y1="22" x2="4" y2="15"/></svg></span>
<span style="display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;background:rgba(220,38,38,.08);border-radius: 8px"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#f87171" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 15s1-1 4-1 5 2 8 2 4-1 4-1V3s-1 1-4 1-5-2-8-2-4 1-4 1z"/><line x1="4" y1="22" x2="4" y2="15"/></svg></span>
<div>
<div style="background:#cc2222;color: #0A0A0A;font-size:.65rem;font-weight:800;text-transform:uppercase;letter-spacing:.1em;padding:3px 8px;border-radius:4px;display:inline-block;margin-bottom:8px">Representative data — based on real observed patterns</div><div style="color:#cc2222;font-weight:800;font-size:.9375rem;margin-bottom:4px">Narrative Conflict Detected — City B (sample)</div>
<div style="background:#cc2222;color: #0A0A0A;font-size:.65rem;font-weight:800;text-transform:uppercase;letter-spacing:.1em;padding:3px 8px;border-radius: var(--radius-sm);display:inline-block;margin-bottom:8px">Representative data — based on real observed patterns</div><div style="color:#cc2222;font-weight:800;font-size:.9375rem;margin-bottom:4px">Narrative Conflict Detected — City B (sample)</div>
<div style="color:#515154;font-size:.875rem;line-height:1.6">Official growth indicators are positive. Observed commercial activity is declining. 7 contradicting signals vs 3 supporting signals.</div>
</div>
</div>
<div class="contradiction-grid" style="display:grid;grid-template-columns:1fr 1fr;gap:16px">
<div style="background:rgba(34,197,94,.15);border:1px solid rgba(34,197,94,.3);border-radius:10px;padding:16px">
<div style="background:rgba(34,197,94,.15);border:1px solid rgba(34,197,94,.3);border-radius: var(--radius-md);padding:16px">
<div style="color:#0d5c1d;font-size:.75rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em;margin-bottom:10px">Official narrative</div>
<ul style="list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:6px">
<li style="color:#1d1d1f;font-size:.875rem"> GDP growth +3.2%</li>
<li style="color:#1d1d1f;font-size:.875rem"> Construction permits up</li>
<li style="color:#1d1d1f;font-size:.875rem"> Property prices rising</li>
<li style="color:#1d1d1f;font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> GDP growth +3.2%</li>
<li style="color:#1d1d1f;font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Construction permits up</li>
<li style="color:#1d1d1f;font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Property prices rising</li>
</ul>
</div>
<div style="background:rgba(239,68,68,.15);border:1px solid rgba(239,68,68,.3);border-radius:10px;padding:16px">
<div style="background:rgba(239,68,68,.15);border:1px solid rgba(239,68,68,.3);border-radius: var(--radius-md);padding:16px">
<div style="color:#7f1d1d;font-size:.75rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em;margin-bottom:10px">Observed reality</div>
<ul style="list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:6px">
<li style="color:#1d1d1f;font-size:.875rem"> Vacancies +18% this quarter</li>
<li style="color:#1d1d1f;font-size:.875rem"> Foot traffic down 12%</li>
<li style="color:#1d1d1f;font-size:.875rem"> 23 closures, 8 openings</li>
<li style="color:#1d1d1f;font-size:.875rem"> Maintenance deferred on 4 blocks</li>
<li style="color:#1d1d1f;font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Vacancies +18% this quarter</li>
<li style="color:#1d1d1f;font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Foot traffic down 12%</li>
<li style="color:#1d1d1f;font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> 23 closures, 8 openings</li>
<li style="color:#1d1d1f;font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Maintenance deferred on 4 blocks</li>
</ul>
</div>
</div>
<div style="margin-top:20px;padding-top:20px;border-top:1px solid rgba(0,0,0,.1);display:flex;justify-content:space-between;align-items:center">
<span style="color:#515154;font-size:.8125rem">Confidence in official narrative <em style="font-size:.75rem;opacity:.85;font-weight:600"> sample</em></span>
<span style="color:#cc2222;font-weight:800;font-size:1.125rem">34% <span style="font-size:.75rem;font-weight:600;opacity:.85"> representative</span></span>
<span style="color:#515154;font-size:.8125rem">Confidence in official narrative <em style="font-size:.75rem;opacity:.85;font-weight:600"> sample</em></span>
<span style="color:#cc2222;font-weight:800;font-size:1.125rem">34% <span style="font-size:.75rem;font-weight:600;opacity:.85"> representative</span></span>
</div>
</div>
<div class="howwework-grid" style="display:grid;grid-template-columns:repeat(3,1fr);gap:2px;background:rgba(0,0,0,.05);border-radius:12px;overflow:hidden;margin-top:32px">
<div class="howwework-grid" style="display:grid;grid-template-columns:repeat(3,1fr);gap:2px;background:rgba(0,0,0,.05);border-radius: var(--radius-md);overflow:hidden;margin-top:32px">
<div style="background:#ffffff;padding:28px 32px;display:flex;flex-direction:column;gap:12px">
<div style="font-size:.6875rem;font-weight:800;color:#0066FF;text-transform:uppercase;letter-spacing:.12em">Intelligence</div>
<div style="color:#1d1d1f;font-weight:800;font-size:1rem;letter-spacing:-.02em;line-height:1.25">Show me what I'm missing</div>
@@ -1993,7 +2041,7 @@ html,body{overflow-x:clip}
<h2 class="section-title">Contribution Mode.<br>Train intelligence by confirming reality.</h2>
<p class="section-sub" style="margin:0 auto;max-width:600px">Instead of asking "What is this?", AI asks: "I think there's damage here, but I'm only 62% sure. Can you confirm?" Every validation permanently improves the platform.</p>
</div>
<div class="how-grid" style="display:grid;grid-template-columns:1fr 1fr;gap:2px;background:rgba(0,0,0,.06);border-radius:16px;overflow:hidden">
<div class="how-grid" style="display:grid;grid-template-columns:1fr 1fr;gap:2px;background:rgba(0,0,0,.06);border-radius: var(--radius-lg);overflow:hidden">
<div style="background:#f5f5f7;padding:48px 44px;position:relative;border:1px solid rgba(0,0,0,0.08)">
<div style="font-size:.6875rem;font-weight:800;color:#0066FF;text-transform:uppercase;letter-spacing:.14em;margin-bottom:20px">Micro Validation</div>
<h3 style="font-size:1.375rem;font-weight:800;color:#0A0A0A;letter-spacing:-.03em;margin-bottom:12px;line-height:1.2">2-second confirmations</h3>
@@ -2378,7 +2426,7 @@ html,body{overflow-x:clip}
});
if (res.ok || res.status === 201) {
msgEl.className = 'success';
msgEl.textContent = " Request received. We'll be in touch within one business day.";
msgEl.textContent = "<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Request received. We'll be in touch within one business day.";
msgEl.style.display = 'block';
this.reset();
} else {
@@ -253,25 +253,25 @@
<div class="tool-grid">
<div class="tool-card">
<h3>🚁 Drone Inspection Systems</h3>
<h3><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M8 2V6M4 4H12" stroke="#666" stroke-width="1.5" stroke-linecap="round"/><path d="M2 8H6L8 6L10 8H14" stroke="#666" stroke-width="1.5" stroke-linejoin="round"/><path d="M6 8V12H10V8" stroke="#666" stroke-width="1.5"/></svg> Drone Inspection Systems</h3>
<p>Aerial platforms with 4K/8K cameras, thermal sensors, and LiDAR. Best for bridges, towers, and large structures where access is difficult.</p>
<span class="tool-tag tag-drone">Aerial</span>
<span class="tool-tag tag-traditional">High Cost</span>
</div>
<div class="tool-card">
<h3>📱 Mobile Field Apps</h3>
<h3> Mobile Field Apps</h3>
<p>Smartphone and tablet apps for structured data collection, GPS tagging, and photo documentation. Works offline in remote locations.</p>
<span class="tool-tag tag-mobile">Ground</span>
<span class="tool-tag tag-mobile">Offline</span>
</div>
<div class="tool-card">
<h3>🤖 AI Analysis Platforms</h3>
<h3> AI Analysis Platforms</h3>
<p>Machine learning systems that process visual data to detect cracks, corrosion, and deformation automatically. Continuously improving accuracy.</p>
<span class="tool-tag tag-ai">Automated</span>
<span class="tool-tag tag-ai">Scalable</span>
</div>
<div class="tool-card">
<h3>🔧 Traditional Methods</h3>
<h3> Traditional Methods</h3>
<p>Manual inspection, scaffolding, binoculars, and physical measurement. Still required for critical structural assessments and regulatory compliance.</p>
<span class="tool-tag tag-traditional">Manual</span>
<span class="tool-tag tag-traditional">Compliance</span>
@@ -304,7 +304,7 @@
<td class="check">92-95%</td>
<td class="check">Fast</td>
<td>Data-volume</td>
<td class="check"> Native</td>
<td class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Native</td>
</tr>
<tr>
<td>Drone + Manual Review</td>
@@ -312,7 +312,7 @@
<td>75-85%</td>
<td>Medium</td>
<td>$15k-50k</td>
<td class="cross"> None</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
</tr>
<tr>
<td>Mobile App Only</td>
@@ -320,7 +320,7 @@
<td>60-70%</td>
<td class="check">Fast</td>
<td>Low</td>
<td class="cross"> Limited</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Limited</td>
</tr>
<tr>
<td>AI + Drone (3rd party)</td>
@@ -328,7 +328,7 @@
<td>80-90%</td>
<td>Medium</td>
<td>$25k-80k</td>
<td class="check"> Add-on</td>
<td class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Add-on</td>
</tr>
<tr>
<td>Traditional Scaffolding</td>
@@ -336,7 +336,7 @@
<td class="check">90-95%</td>
<td class="cross">Slow</td>
<td class="cross">$50k-200k</td>
<td class="cross"> None</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
</tr>
<tr>
<td>Thermal Imaging</td>
@@ -344,7 +344,7 @@
<td>70-80%</td>
<td>Medium</td>
<td>$10k-30k</td>
<td class="cross"> Limited</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Limited</td>
</tr>
</tbody>
</table>
@@ -94,7 +94,7 @@
background: #e94560;
color: white;
padding: 1rem 2rem;
border-radius: 8px;
border-radius: var(--radius-md);
text-decoration: none;
font-weight: 600;
margin: 0.5rem;
@@ -112,7 +112,7 @@
.alternative-card {
background: #f8f9fa;
border-radius: 12px;
border-radius: var(--radius-md);
padding: 2rem;
margin-bottom: 2rem;
border-left: 4px solid #e94560;
@@ -131,7 +131,7 @@
.pros, .cons {
background: white;
padding: 1rem;
border-radius: 8px;
border-radius: var(--radius-md);
}
.pros h4 { color: #4caf50; margin-bottom: 0.5rem; }
.cons h4 { color: #f44336; margin-bottom: 0.5rem; }
@@ -140,12 +140,12 @@
padding: 0;
}
.pros li::before {
content: " ";
content: "<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> ";
color: #4caf50;
font-weight: bold;
}
.cons li::before {
content: " ";
content: "<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> ";
color: #f44336;
font-weight: bold;
}
@@ -186,7 +186,7 @@
.recommendation-card {
background: white;
padding: 2rem;
border-radius: 12px;
border-radius: var(--radius-md);
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.recommendation-card h3 {
@@ -197,7 +197,7 @@
background: #e8f5e9;
color: #2e7d32;
padding: 0.5rem 1rem;
border-radius: 20px;
border-radius: var(--radius-xl);
display: inline-block;
font-size: 0.9rem;
font-weight: 600;
@@ -212,7 +212,7 @@
margin-bottom: 1.5rem;
padding: 1.5rem;
background: #f8f9fa;
border-radius: 8px;
border-radius: var(--radius-md);
}
.cta-section {
@@ -358,7 +358,7 @@
<h2 class="section-title">Infrastructure Monitoring Alternatives</h2>
<div class="alternative-card">
<h3>🤖 LandveX RIOS (AI-Powered Platform)</h3>
<h3> LandveX RIOS (AI-Powered Platform)</h3>
<p>Reality Intelligence Operating System that transforms continuous video observations into structured intelligence using AI orchestration, predictive scoring, and active learning.</p>
<div class="pros-cons">
<div class="pros">
@@ -385,7 +385,7 @@
</div>
<div class="alternative-card">
<h3>📱 SiteCapture (Photo-Based Documentation)</h3>
<h3> SiteCapture (Photo-Based Documentation)</h3>
<p>Mobile-first platform for photo-based field documentation with basic GPS tagging and template-based reporting.</p>
<div class="pros-cons">
<div class="pros">
@@ -412,7 +412,7 @@
</div>
<div class="alternative-card">
<h3>🏢 Traditional Inspection Firms</h3>
<h3><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="3" y="2" width="10" height="12" rx="1" stroke="#666" stroke-width="1.5"/><path d="M6 5H7M6 8H7M6 11H7M9 5H10M9 8H10M9 11H10" stroke="#666" stroke-width="1.5" stroke-linecap="round"/></svg> Traditional Inspection Firms</h3>
<p>Engineering firms that send certified inspectors to physically examine infrastructure and produce detailed reports.</p>
<div class="pros-cons">
<div class="pros">
@@ -439,7 +439,7 @@
</div>
<div class="alternative-card">
<h3>📡 IoT Sensors (Fixed Monitoring)</h3>
<h3> IoT Sensors (Fixed Monitoring)</h3>
<p>Network of physical sensors (strain gauges, accelerometers, cameras) installed on infrastructure for continuous monitoring.</p>
<div class="pros-cons">
<div class="pros">
@@ -466,7 +466,7 @@
</div>
<div class="alternative-card">
<h3>🚁 Drone Services (Aerial Inspection)</h3>
<h3><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M8 2V6M4 4H12" stroke="#666" stroke-width="1.5" stroke-linecap="round"/><path d="M2 8H6L8 6L10 8H14" stroke="#666" stroke-width="1.5" stroke-linejoin="round"/><path d="M6 8V12H10V8" stroke="#666" stroke-width="1.5"/></svg> Drone Services (Aerial Inspection)</h3>
<p>Third-party drone operators capture aerial imagery of infrastructure on a scheduled or on-demand basis.</p>
<div class="pros-cons">
<div class="pros">
@@ -552,11 +552,11 @@
</tr>
<tr>
<td><strong>Predictive Analytics</strong></td>
<td class="check"> AI-powered</td>
<td class="cross"> None</td>
<td class="cross"> None</td>
<td class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> AI-powered</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
<td>Basic thresholds</td>
<td class="cross"> None</td>
<td class="cross"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
</tr>
<tr class="highlight">
<td><strong>Visual Context</strong></td>
@@ -17,16 +17,16 @@
.hero { background: #fff; padding: 80px 24px; text-align: center; }
.hero h1 { font-size: 48px; font-weight: 700; margin-bottom: 16px; }
.hero p { font-size: 20px; color: #86868b; max-width: 600px; margin: 0 auto 32px; }
.btn { display: inline-block; padding: 14px 28px; border-radius: 8px; font-size: 16px; font-weight: 500; text-decoration: none; transition: all 0.2s; }
.btn { display: inline-block; padding: 14px 28px; border-radius: var(--radius-md); font-size: 16px; font-weight: 500; text-decoration: none; transition: all 0.2s; }
.btn-primary { background: #0071e3; color: #fff; }
.features { padding: 64px 24px; max-width: 1200px; margin: 0 auto; }
.features-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 32px; margin-top: 48px; }
.feature { background: #fff; padding: 32px; border-radius: 16px; }
.feature { background: #fff; padding: 32px; border-radius: var(--radius-lg); }
.feature h3 { font-size: 20px; font-weight: 600; margin-bottom: 12px; }
.feature p { color: #86868b; }
.countries { padding: 64px 24px; background: #fff; }
.countries-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px; max-width: 1200px; margin: 32px auto 0; }
.country { padding: 20px; border: 1px solid #d2d2d7; border-radius: 12px; text-align: center; }
.country { padding: 20px; border: 1px solid #d2d2d7; border-radius: var(--radius-md); text-align: center; }
.country a { color: #0071e3; text-decoration: none; font-weight: 500; }
.footer { background: #1d1d1f; color: #fff; padding: 40px 24px; text-align: center; }
@media (max-width: 640px) {
@@ -62,27 +62,27 @@
<h2 style="text-align: center; font-size: 32px;">Coverage Areas</h2>
<div class="features-grid">
<div class="feature">
<h3>🛣️ Roads & Highways</h3>
<h3> Roads & Highways</h3>
<p>Surface condition, potholes, markings, signage, and drainage assessment.</p>
</div>
<div class="feature">
<h3>🌉 Bridges & Tunnels</h3>
<h3> Bridges & Tunnels</h3>
<p>Structural integrity, crack detection, corrosion monitoring, and load assessment.</p>
</div>
<div class="feature">
<h3>🏢 Buildings & Facades</h3>
<h3><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="3" y="2" width="10" height="12" rx="1" stroke="#666" stroke-width="1.5"/><path d="M6 5H7M6 8H7M6 11H7M9 5H10M9 8H10M9 11H10" stroke="#666" stroke-width="1.5" stroke-linecap="round"/></svg> Buildings & Facades</h3>
<p>Exterior condition, safety hazards, accessibility compliance, and energy efficiency.</p>
</div>
<div class="feature">
<h3> Utilities</h3>
<h3><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M9 2L5 9H8L7 14L11 7H8L9 2Z" fill="#f59e0b"/></svg> Utilities</h3>
<p>Power lines, street lighting, telecom infrastructure, and water systems.</p>
</div>
<div class="feature">
<h3>🚦 Traffic Systems</h3>
<h3> Traffic Systems</h3>
<p>Signal operation, sign visibility, road markings, and pedestrian facilities.</p>
</div>
<div class="feature">
<h3>🌳 Environment</h3>
<h3> Environment</h3>
<p>Vegetation encroachment, drainage, erosion, and flood risk assessment.</p>
</div>
</div>
@@ -91,11 +91,11 @@
<section class="countries">
<h2 style="text-align: center; font-size: 32px;">Available In</h2>
<div class="countries-grid">
<div class="country"><a href="/infrastructure-monitoring/sweden/">🇸🇪 Sweden</a></div>
<div class="country"><a href="/infrastructure-monitoring/germany/">🇩🇪 Germany</a></div>
<div class="country"><a href="/infrastructure-monitoring/france/">🇫🇷 France</a></div>
<div class="country"><a href="/infrastructure-monitoring/uk/">🇬🇧 United Kingdom</a></div>
<div class="country"><a href="/infrastructure-monitoring/netherlands/">🇳🇱 Netherlands</a></div>
<div class="country"><a href="/infrastructure-monitoring/sweden/"><span class="flag-se">SE</span> Sweden</a></div>
<div class="country"><a href="/infrastructure-monitoring/germany/"> Germany</a></div>
<div class="country"><a href="/infrastructure-monitoring/france/"> France</a></div>
<div class="country"><a href="/infrastructure-monitoring/uk/"> United Kingdom</a></div>
<div class="country"><a href="/infrastructure-monitoring/netherlands/"> Netherlands</a></div>
</div>
</section>
@@ -29,7 +29,7 @@ h1 { font-size: 2.5rem; font-weight: 700; margin-bottom: 0.5em; line-height: 1.2
h2 { font-size: 1.5rem; font-weight: 600; margin-top: 2em; margin-bottom: 0.5em; }
p { margin-bottom: 1.2em; }
.meta { color: #666; font-size: 0.9rem; margin-bottom: 2em; }
.cta { background: #f5f5f5; padding: 24px; border-radius: 8px; margin: 2em 0; }
.cta { background: #f5f5f5; padding: 24px; border-radius: var(--radius-md); margin: 2em 0; }
.cta a { color: #0066cc; text-decoration: none; font-weight: 600; }
footer { margin-top: 4em; padding-top: 2em; border-top: 1px solid #eee; font-size: 0.85rem; color: #666; }
</style>
@@ -29,7 +29,7 @@ h1 { font-size: 2.5rem; font-weight: 700; margin-bottom: 0.5em; line-height: 1.2
h2 { font-size: 1.5rem; font-weight: 600; margin-top: 2em; margin-bottom: 0.5em; }
p { margin-bottom: 1.2em; }
.meta { color: #666; font-size: 0.9rem; margin-bottom: 2em; }
.cta { background: #f5f5f5; padding: 24px; border-radius: 8px; margin: 2em 0; }
.cta { background: #f5f5f5; padding: 24px; border-radius: var(--radius-md); margin: 2em 0; }
.cta a { color: #0066cc; text-decoration: none; font-weight: 600; }
footer { margin-top: 4em; padding-top: 2em; border-top: 1px solid #eee; font-size: 0.85rem; color: #666; }
</style>
@@ -42,7 +42,7 @@
.breadcrumb a:hover { text-decoration: underline; }
.breadcrumb-sep { color: var(--text-muted); }
.article-meta { display: flex; align-items: center; gap: 12px; margin-bottom: 20px; }
.category-badge { display: inline-block; font-size: 11px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--blue); background: var(--blue-light); border-radius: 6px; padding: 3px 9px; }
.category-badge { display: inline-block; font-size: 11px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--blue); background: var(--blue-light); border-radius: var(--radius-sm); padding: 3px 9px; }
.article-date { font-size: 13px; color: var(--text-muted); font-weight: 500; }
h1 { font-size: clamp(1.7rem, 4vw, 2.4rem); font-weight: 700; letter-spacing: -0.04em; line-height: 1.2; color: var(--text-primary); margin-bottom: 20px; }
.article-lead { font-size: 18px; color: var(--text-secondary); line-height: 1.7; margin-bottom: 40px; padding-bottom: 32px; border-bottom: 1px solid var(--border); }
@@ -42,7 +42,7 @@ h1 { font-size: 2.5rem; font-weight: 700; margin-bottom: 0.5em; line-height: 1.2
h2 { font-size: 1.5rem; font-weight: 600; margin-top: 2em; margin-bottom: 0.5em; }
p { margin-bottom: 1.2em; }
.meta { color: #666; font-size: 0.9rem; margin-bottom: 2em; }
.cta { background: #f5f5f5; padding: 24px; border-radius: 8px; margin: 2em 0; }
.cta { background: #f5f5f5; padding: 24px; border-radius: var(--radius-md); margin: 2em 0; }
.cta a { color: #0066cc; text-decoration: none; font-weight: 600; }
footer { margin-top: 4em; padding-top: 2em; border-top: 1px solid #eee; font-size: 0.85rem; color: #666; }
</style>
@@ -29,7 +29,7 @@ h1 { font-size: 2.5rem; font-weight: 700; margin-bottom: 0.5em; line-height: 1.2
h2 { font-size: 1.5rem; font-weight: 600; margin-top: 2em; margin-bottom: 0.5em; }
p { margin-bottom: 1.2em; }
.meta { color: #666; font-size: 0.9rem; margin-bottom: 2em; }
.cta { background: #f5f5f5; padding: 24px; border-radius: 8px; margin: 2em 0; }
.cta { background: #f5f5f5; padding: 24px; border-radius: var(--radius-md); margin: 2em 0; }
.cta a { color: #0066cc; text-decoration: none; font-weight: 600; }
footer { margin-top: 4em; padding-top: 2em; border-top: 1px solid #eee; font-size: 0.85rem; color: #666; }
</style>
@@ -29,7 +29,7 @@ h1 { font-size: 2.5rem; font-weight: 700; margin-bottom: 0.5em; line-height: 1.2
h2 { font-size: 1.5rem; font-weight: 600; margin-top: 2em; margin-bottom: 0.5em; }
p { margin-bottom: 1.2em; }
.meta { color: #666; font-size: 0.9rem; margin-bottom: 2em; }
.cta { background: #f5f5f5; padding: 24px; border-radius: 8px; margin: 2em 0; }
.cta { background: #f5f5f5; padding: 24px; border-radius: var(--radius-md); margin: 2em 0; }
.cta a { color: #0066cc; text-decoration: none; font-weight: 600; }
footer { margin-top: 4em; padding-top: 2em; border-top: 1px solid #eee; font-size: 0.85rem; color: #666; }
</style>
@@ -29,7 +29,7 @@ h1 { font-size: 2.5rem; font-weight: 700; margin-bottom: 0.5em; line-height: 1.2
h2 { font-size: 1.5rem; font-weight: 600; margin-top: 2em; margin-bottom: 0.5em; }
p { margin-bottom: 1.2em; }
.meta { color: #666; font-size: 0.9rem; margin-bottom: 2em; }
.cta { background: #f5f5f5; padding: 24px; border-radius: 8px; margin: 2em 0; }
.cta { background: #f5f5f5; padding: 24px; border-radius: var(--radius-md); margin: 2em 0; }
.cta a { color: #0066cc; text-decoration: none; font-weight: 600; }
footer { margin-top: 4em; padding-top: 2em; border-top: 1px solid #eee; font-size: 0.85rem; color: #666; }
</style>
@@ -29,7 +29,7 @@ h1 { font-size: 2.5rem; font-weight: 700; margin-bottom: 0.5em; line-height: 1.2
h2 { font-size: 1.5rem; font-weight: 600; margin-top: 2em; margin-bottom: 0.5em; }
p { margin-bottom: 1.2em; }
.meta { color: #666; font-size: 0.9rem; margin-bottom: 2em; }
.cta { background: #f5f5f5; padding: 24px; border-radius: 8px; margin: 2em 0; }
.cta { background: #f5f5f5; padding: 24px; border-radius: var(--radius-md); margin: 2em 0; }
.cta a { color: #0066cc; text-decoration: none; font-weight: 600; }
footer { margin-top: 4em; padding-top: 2em; border-top: 1px solid #eee; font-size: 0.85rem; color: #666; }
</style>
@@ -36,7 +36,7 @@
.breadcrumb a { color: var(--blue); text-decoration: none; } .breadcrumb a:hover { text-decoration: underline; }
.breadcrumb-sep { color: var(--text-muted); }
.article-meta { display: flex; align-items: center; gap: 12px; margin-bottom: 20px; }
.category-badge { display: inline-block; font-size: 11px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--blue); background: var(--blue-light); border-radius: 6px; padding: 3px 9px; }
.category-badge { display: inline-block; font-size: 11px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--blue); background: var(--blue-light); border-radius: var(--radius-sm); padding: 3px 9px; }
.article-date { font-size: 13px; color: var(--text-muted); font-weight: 500; }
h1 { font-size: clamp(1.7rem, 4vw, 2.4rem); font-weight: 700; letter-spacing: -0.04em; line-height: 1.2; color: var(--text-primary); margin-bottom: 20px; }
.article-lead { font-size: 18px; color: var(--text-secondary); line-height: 1.7; margin-bottom: 40px; padding-bottom: 32px; border-bottom: 1px solid var(--border); }
@@ -36,7 +36,7 @@
.breadcrumb a { color: var(--blue); text-decoration: none; } .breadcrumb a:hover { text-decoration: underline; }
.breadcrumb-sep { color: var(--text-muted); }
.article-meta { display: flex; align-items: center; gap: 12px; margin-bottom: 20px; }
.category-badge { display: inline-block; font-size: 11px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--blue); background: var(--blue-light); border-radius: 6px; padding: 3px 9px; }
.category-badge { display: inline-block; font-size: 11px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--blue); background: var(--blue-light); border-radius: var(--radius-sm); padding: 3px 9px; }
.article-date { font-size: 13px; color: var(--text-muted); font-weight: 500; }
h1 { font-size: clamp(1.7rem, 4vw, 2.4rem); font-weight: 700; letter-spacing: -0.04em; line-height: 1.2; color: var(--text-primary); margin-bottom: 20px; }
.article-lead { font-size: 18px; color: var(--text-secondary); line-height: 1.7; margin-bottom: 40px; padding-bottom: 32px; border-bottom: 1px solid var(--border); }
@@ -29,7 +29,7 @@ h1 { font-size: 2.5rem; font-weight: 700; margin-bottom: 0.5em; line-height: 1.2
h2 { font-size: 1.5rem; font-weight: 600; margin-top: 2em; margin-bottom: 0.5em; }
p { margin-bottom: 1.2em; }
.meta { color: #666; font-size: 0.9rem; margin-bottom: 2em; }
.cta { background: #f5f5f5; padding: 24px; border-radius: 8px; margin: 2em 0; }
.cta { background: #f5f5f5; padding: 24px; border-radius: var(--radius-md); margin: 2em 0; }
.cta a { color: #0066cc; text-decoration: none; font-weight: 600; }
footer { margin-top: 4em; padding-top: 2em; border-top: 1px solid #eee; font-size: 0.85rem; color: #666; }
</style>
+1 -1
View File
@@ -11,7 +11,7 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; line
h1 { font-size: 2.5rem; font-weight: 700; margin-bottom: 0.5em; }
.subtitle { color: #666; font-size: 1.2rem; margin-bottom: 3em; }
.article-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(350px, 1fr)); gap: 2em; }
.article-card { border: 1px solid #eee; border-radius: 8px; padding: 24px; transition: box-shadow 0.2s; }
.article-card { border: 1px solid #eee; border-radius: var(--radius-md); padding: 24px; transition: box-shadow 0.2s; }
.article-card:hover { box-shadow: 0 4px 12px rgba(0,0,0,0.1); }
.article-card h2 { font-size: 1.3rem; margin-top: 0; }
.article-card a { color: #1a1a1a; text-decoration: none; }
@@ -36,7 +36,7 @@
.breadcrumb a { color: var(--blue); text-decoration: none; } .breadcrumb a:hover { text-decoration: underline; }
.breadcrumb-sep { color: var(--text-muted); }
.article-meta { display: flex; align-items: center; gap: 12px; margin-bottom: 20px; }
.category-badge { display: inline-block; font-size: 11px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--blue); background: var(--blue-light); border-radius: 6px; padding: 3px 9px; }
.category-badge { display: inline-block; font-size: 11px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--blue); background: var(--blue-light); border-radius: var(--radius-sm); padding: 3px 9px; }
.article-date { font-size: 13px; color: var(--text-muted); font-weight: 500; }
h1 { font-size: clamp(1.7rem, 4vw, 2.4rem); font-weight: 700; letter-spacing: -0.04em; line-height: 1.2; color: var(--text-primary); margin-bottom: 20px; }
.article-lead { font-size: 18px; color: var(--text-secondary); line-height: 1.7; margin-bottom: 40px; padding-bottom: 32px; border-bottom: 1px solid var(--border); }
@@ -91,7 +91,7 @@
font-size: 11px; font-weight: 700;
letter-spacing: 0.06em; text-transform: uppercase;
color: var(--blue); background: var(--blue-light);
border-radius: 6px; padding: 3px 9px;
border-radius: var(--radius-sm); padding: 3px 9px;
}
.article-date { font-size: 13px; color: var(--text-muted); font-weight: 500; }
@@ -42,7 +42,7 @@ h1 { font-size: 2.5rem; font-weight: 700; margin-bottom: 0.5em; line-height: 1.2
h2 { font-size: 1.5rem; font-weight: 600; margin-top: 2em; margin-bottom: 0.5em; }
p { margin-bottom: 1.2em; }
.meta { color: #666; font-size: 0.9rem; margin-bottom: 2em; }
.cta { background: #f5f5f5; padding: 24px; border-radius: 8px; margin: 2em 0; }
.cta { background: #f5f5f5; padding: 24px; border-radius: var(--radius-md); margin: 2em 0; }
.cta a { color: #0066cc; text-decoration: none; font-weight: 600; }
footer { margin-top: 4em; padding-top: 2em; border-top: 1px solid #eee; font-size: 0.85rem; color: #666; }
</style>
@@ -29,7 +29,7 @@ h1 { font-size: 2.5rem; font-weight: 700; margin-bottom: 0.5em; line-height: 1.2
h2 { font-size: 1.5rem; font-weight: 600; margin-top: 2em; margin-bottom: 0.5em; }
p { margin-bottom: 1.2em; }
.meta { color: #666; font-size: 0.9rem; margin-bottom: 2em; }
.cta { background: #f5f5f5; padding: 24px; border-radius: 8px; margin: 2em 0; }
.cta { background: #f5f5f5; padding: 24px; border-radius: var(--radius-md); margin: 2em 0; }
.cta a { color: #0066cc; text-decoration: none; font-weight: 600; }
footer { margin-top: 4em; padding-top: 2em; border-top: 1px solid #eee; font-size: 0.85rem; color: #666; }
</style>
@@ -29,7 +29,7 @@ h1 { font-size: 2.5rem; font-weight: 700; margin-bottom: 0.5em; line-height: 1.2
h2 { font-size: 1.5rem; font-weight: 600; margin-top: 2em; margin-bottom: 0.5em; }
p { margin-bottom: 1.2em; }
.meta { color: #666; font-size: 0.9rem; margin-bottom: 2em; }
.cta { background: #f5f5f5; padding: 24px; border-radius: 8px; margin: 2em 0; }
.cta { background: #f5f5f5; padding: 24px; border-radius: var(--radius-md); margin: 2em 0; }
.cta a { color: #0066cc; text-decoration: none; font-weight: 600; }
footer { margin-top: 4em; padding-top: 2em; border-top: 1px solid #eee; font-size: 0.85rem; color: #666; }
</style>
@@ -29,7 +29,7 @@ h1 { font-size: 2.5rem; font-weight: 700; margin-bottom: 0.5em; line-height: 1.2
h2 { font-size: 1.5rem; font-weight: 600; margin-top: 2em; margin-bottom: 0.5em; }
p { margin-bottom: 1.2em; }
.meta { color: #666; font-size: 0.9rem; margin-bottom: 2em; }
.cta { background: #f5f5f5; padding: 24px; border-radius: 8px; margin: 2em 0; }
.cta { background: #f5f5f5; padding: 24px; border-radius: var(--radius-md); margin: 2em 0; }
.cta a { color: #0066cc; text-decoration: none; font-weight: 600; }
footer { margin-top: 4em; padding-top: 2em; border-top: 1px solid #eee; font-size: 0.85rem; color: #666; }
</style>
@@ -29,7 +29,7 @@ h1 { font-size: 2.5rem; font-weight: 700; margin-bottom: 0.5em; line-height: 1.2
h2 { font-size: 1.5rem; font-weight: 600; margin-top: 2em; margin-bottom: 0.5em; }
p { margin-bottom: 1.2em; }
.meta { color: #666; font-size: 0.9rem; margin-bottom: 2em; }
.cta { background: #f5f5f5; padding: 24px; border-radius: 8px; margin: 2em 0; }
.cta { background: #f5f5f5; padding: 24px; border-radius: var(--radius-md); margin: 2em 0; }
.cta a { color: #0066cc; text-decoration: none; font-weight: 600; }
footer { margin-top: 4em; padding-top: 2em; border-top: 1px solid #eee; font-size: 0.85rem; color: #666; }
</style>
@@ -36,7 +36,7 @@
.breadcrumb a { color: var(--blue); text-decoration: none; } .breadcrumb a:hover { text-decoration: underline; }
.breadcrumb-sep { color: var(--text-muted); }
.article-meta { display: flex; align-items: center; gap: 12px; margin-bottom: 20px; }
.category-badge { display: inline-block; font-size: 11px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--blue); background: var(--blue-light); border-radius: 6px; padding: 3px 9px; }
.category-badge { display: inline-block; font-size: 11px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--blue); background: var(--blue-light); border-radius: var(--radius-sm); padding: 3px 9px; }
.article-date { font-size: 13px; color: var(--text-muted); font-weight: 500; }
h1 { font-size: clamp(1.7rem, 4vw, 2.4rem); font-weight: 700; letter-spacing: -0.04em; line-height: 1.2; color: var(--text-primary); margin-bottom: 20px; }
.article-lead { font-size: 18px; color: var(--text-secondary); line-height: 1.7; margin-bottom: 40px; padding-bottom: 32px; border-bottom: 1px solid var(--border); }
@@ -29,7 +29,7 @@ h1 { font-size: 2.5rem; font-weight: 700; margin-bottom: 0.5em; line-height: 1.2
h2 { font-size: 1.5rem; font-weight: 600; margin-top: 2em; margin-bottom: 0.5em; }
p { margin-bottom: 1.2em; }
.meta { color: #666; font-size: 0.9rem; margin-bottom: 2em; }
.cta { background: #f5f5f5; padding: 24px; border-radius: 8px; margin: 2em 0; }
.cta { background: #f5f5f5; padding: 24px; border-radius: var(--radius-md); margin: 2em 0; }
.cta a { color: #0066cc; text-decoration: none; font-weight: 600; }
footer { margin-top: 4em; padding-top: 2em; border-top: 1px solid #eee; font-size: 0.85rem; color: #666; }
</style>
+48 -48
View File
@@ -9,10 +9,10 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Inspektionsrapport — LandveX</title>
<style>
/* ═══════════════════════════════════════════════════════
/*
LANDVEX INSPECTION REPORT — Light Mode, Print-ready
PDF-optimerat: A4, inga bakgrunder i tryck
═══════════════════════════════════════════════════════ */
*/
:root {
--brand: #0A84FF;
--brand-dark: #0060C0;
@@ -45,7 +45,7 @@
line-height: 1.5;
}
/* ── Screen toolbar ── */
/* Screen toolbar */
.screen-toolbar {
position: sticky; top: 0; z-index: 100;
background: rgba(0,0,0,0.92);
@@ -64,7 +64,7 @@
.toolbar-brand span { color: var(--text-primary); }
.toolbar-actions { display: flex; gap: 10px; }
.btn {
padding: 8px 18px; border-radius: 8px; border: none;
padding: 8px 18px; border-radius: var(--radius-md); border: none;
font-family: inherit; font-size: 14px; font-weight: 600;
cursor: pointer; transition: opacity 0.15s;
}
@@ -72,7 +72,7 @@
.btn-primary { background: var(--brand); color: #1d1d1f; }
.btn-secondary { background: var(--surface-2); color: var(--text-primary); border: 1px solid var(--border); }
/* ── Main container ── */
/* Main container */
.report-container {
max-width: 900px; margin: 24px auto; padding: 0 24px;
}
@@ -81,10 +81,10 @@
.report-container { max-width: 100%; margin: 0; padding: 0; }
}
/* ── Report page ── */
/* Report page */
.report-page {
background: var(--surface);
border-radius: 16px;
border-radius: var(--radius-lg);
border: 1px solid var(--border);
overflow: hidden;
margin-bottom: 20px;
@@ -94,7 +94,7 @@
.report-page:last-child { page-break-after: auto; }
}
/* ── Report header ── */
/* Report header */
.report-header {
background: linear-gradient(135deg, var(--brand-dark) 0%, var(--brand) 100%);
color: #1d1d1f; padding: 32px 40px;
@@ -110,7 +110,7 @@
.report-badge {
background: rgba(0,0,0,0.2);
border: 1px solid rgba(0,0,0,0.3);
border-radius: 8px; padding: 4px 12px;
border-radius: var(--radius-md); padding: 4px 12px;
font-size: 12px; font-weight: 700;
text-transform: uppercase; letter-spacing: 0.5px;
}
@@ -128,7 +128,7 @@
.meta-label { font-size: 11px; opacity: 0.65; text-transform: uppercase; letter-spacing: 0.5px; }
.meta-value { font-size: 14px; font-weight: 600; margin-top: 2px; }
/* ── Risk score band ── */
/* Risk score band */
.risk-band {
display: flex; align-items: center; justify-content: space-between;
padding: 20px 40px;
@@ -138,14 +138,14 @@
}
.risk-score-block { display: flex; align-items: center; gap: 20px; }
.risk-gauge {
width: 80px; height: 80px; border-radius: 50%;
width: 80px; height: 80px; border-radius: var(--radius-full);
display: flex; flex-direction: column; align-items: center; justify-content: center;
font-family: -apple-system, sans-serif;
position: relative;
}
.risk-gauge-ring {
position: absolute; inset: 0;
border-radius: 50%;
border-radius: var(--radius-full);
border: 6px solid;
}
.risk-score-num { font-size: 28px; font-weight: 900; line-height: 1; }
@@ -155,7 +155,7 @@
.risk-desc { font-size: 13px; color: var(--text-secondary); max-width: 320px; line-height: 1.5; }
.risk-pills { display: flex; gap: 8px; flex-wrap: wrap; }
.risk-pill {
padding: 4px 12px; border-radius: 20px; font-size: 12px; font-weight: 600;
padding: 4px 12px; border-radius: var(--radius-xl); font-size: 12px; font-weight: 600;
border: 1px solid;
}
.pill-green { background: rgba(52,199,89,0.12); color: #1a7a3a; border-color: rgba(52,199,89,0.3); }
@@ -163,7 +163,7 @@
.pill-red { background: rgba(255,59,48,0.12); color: #c0160d; border-color: rgba(255,59,48,0.3); }
.pill-blue { background: rgba(10,132,255,0.12); color: #0055c0; border-color: rgba(10,132,255,0.3); }
/* ── Section layout ── */
/* Section layout */
.section { padding: 28px 40px; border-bottom: 1px solid var(--border-light); }
.section:last-child { border-bottom: none; }
.section-title {
@@ -175,7 +175,7 @@
content: ''; flex: 1; height: 1px; background: var(--border-light);
}
/* ── Info grid ── */
/* Info grid */
.info-grid {
display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
gap: 16px;
@@ -185,37 +185,37 @@
.info-cell-value { font-size: 15px; font-weight: 500; color: var(--text-primary); margin-top: 3px; }
.info-cell-value.highlight { color: var(--brand); font-weight: 700; }
/* ── Condition assessment ── */
/* Condition assessment */
.condition-grid {
display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 14px;
}
.condition-card {
background: var(--surface-2); border-radius: 12px; padding: 16px;
background: var(--surface-2); border-radius: var(--radius-md); padding: 16px;
border: 1px solid var(--border-light);
}
.condition-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; }
.condition-label { font-size: 13px; font-weight: 600; }
.condition-grade {
font-size: 12px; font-weight: 700; padding: 2px 8px;
border-radius: 6px;
border-radius: var(--radius-sm);
}
.grade-a { background: rgba(52,199,89,0.15); color: #1a7a3a; }
.grade-b { background: rgba(52,199,89,0.1); color: #2a8a4a; }
.grade-c { background: rgba(255,204,0,0.15); color: #7a5e00; }
.grade-d { background: rgba(255,149,0,0.15); color: #a05800; }
.grade-e { background: rgba(255,59,48,0.15); color: #c0160d; }
.condition-bar { height: 4px; background: var(--border); border-radius: 2px; overflow: hidden; margin-bottom: 6px; }
.condition-fill { height: 100%; border-radius: 2px; }
.condition-bar { height: 4px; background: var(--border); border-radius: var(--radius-sm); overflow: hidden; margin-bottom: 6px; }
.condition-fill { height: 100%; border-radius: var(--radius-sm); }
.condition-note { font-size: 12px; color: var(--text-secondary); line-height: 1.4; }
/* ── Photo grid ── */
/* Photo grid */
.photo-grid {
display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px;
}
.photo-cell {
aspect-ratio: 4/3; background: var(--surface-2);
border-radius: 10px; border: 1px dashed var(--border);
border-radius: var(--radius-md); border: 1px dashed var(--border);
display: flex; flex-direction: column; align-items: center; justify-content: center;
gap: 6px; color: var(--text-tertiary);
font-size: 12px; text-align: center;
@@ -224,7 +224,7 @@
.photo-cell-label { font-weight: 500; }
.photo-cell-tag { font-size: 10px; color: var(--text-tertiary); }
/* ── Maintenance history ── */
/* Maintenance history */
.history-timeline { display: flex; flex-direction: column; gap: 0; }
.history-item {
display: flex; gap: 16px;
@@ -238,7 +238,7 @@
width: 32px; flex-shrink: 0;
}
.history-dot {
width: 12px; height: 12px; border-radius: 50%;
width: 12px; height: 12px; border-radius: var(--radius-full);
border: 2px solid; flex-shrink: 0; margin-top: 3px;
}
.history-dot.done { background: var(--green); border-color: var(--green); }
@@ -251,18 +251,18 @@
.history-desc { font-size: 13px; color: var(--text-secondary); margin-top: 3px; line-height: 1.5; }
.history-cost { font-size: 13px; font-weight: 600; color: var(--text-primary); margin-top: 4px; }
.history-status {
font-size: 11px; font-weight: 700; padding: 2px 8px; border-radius: 20px;
font-size: 11px; font-weight: 700; padding: 2px 8px; border-radius: var(--radius-xl);
align-self: flex-start; margin-top: 2px;
}
.status-done { background: rgba(52,199,89,0.12); color: #1a7a3a; }
.status-planned { background: rgba(10,132,255,0.12); color: #0055c0; }
.status-overdue { background: rgba(255,59,48,0.12); color: #c0160d; }
/* ── Recommendations ── */
/* Recommendations */
.rec-list { display: flex; flex-direction: column; gap: 14px; }
.rec-item {
display: flex; gap: 14px; align-items: flex-start;
padding: 16px; border-radius: 12px;
padding: 16px; border-radius: var(--radius-md);
border: 1px solid;
}
.rec-item.rec-high { background: rgba(255,59,48,0.05); border-color: rgba(255,59,48,0.2); }
@@ -270,7 +270,7 @@
.rec-item.rec-low { background: rgba(52,199,89,0.05); border-color: rgba(52,199,89,0.2); }
.rec-priority {
font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: 0.5px;
padding: 3px 10px; border-radius: 6px; flex-shrink: 0; margin-top: 2px;
padding: 3px 10px; border-radius: var(--radius-sm); flex-shrink: 0; margin-top: 2px;
}
.pri-high { background: rgba(255,59,48,0.15); color: #c0160d; }
.pri-medium { background: rgba(255,149,0,0.15); color: #a05800; }
@@ -282,14 +282,14 @@
.rec-meta { font-size: 12px; }
.rec-meta strong { color: var(--text-primary); }
/* ── Signature block ── */
/* Signature block */
.signature-block {
display: grid; grid-template-columns: 1fr 1fr; gap: 32px;
}
.sig-field { border-top: 1px solid var(--text-primary); padding-top: 8px; margin-top: 48px; }
.sig-label { font-size: 12px; color: var(--text-secondary); }
/* ── Footer ── */
/* Footer */
.report-footer {
padding: 16px 40px;
background: var(--surface-2); border-top: 1px solid var(--border);
@@ -437,7 +437,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<div class="screen-toolbar">
<a class="toolbar-brand" href="index.html">LandveX <span>Inspection</span></a>
<div class="toolbar-actions">
<button class="btn btn-secondary" onclick="window.print()">🖨 Skriv ut / PDF</button>
<button class="btn btn-secondary" onclick="window.print()"> Skriv ut / PDF</button>
<button class="btn btn-primary" onclick="window.location.href='dashboard.html'">← Dashboard</button>
</div>
</div>
@@ -445,7 +445,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<div class="report-container">
<div class="report-page" id="report-main">
<!-- ── Header ── -->
<!-- Header -->
<div class="report-header">
<div class="report-header-top">
<div class="report-logo">Land<span>ve</span>X</div>
@@ -473,7 +473,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
</div>
</div>
<!-- ── Risk score ── -->
<!-- Risk score -->
<div class="risk-band">
<div class="risk-score-block">
<div class="risk-gauge" id="gauge">
@@ -487,15 +487,15 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
</div>
</div>
<div class="risk-pills" id="r-pills">
<span class="risk-pill pill-green">Brandskydd </span>
<span class="risk-pill pill-green">El </span>
<span class="risk-pill pill-green">Brandskydd <svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></span>
<span class="risk-pill pill-green">El <svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></span>
<span class="risk-pill pill-orange">Tak — åtgärd behövs</span>
<span class="risk-pill pill-orange">VVS — planera service</span>
<span class="risk-pill pill-blue">Energiklass C</span>
</div>
</div>
<!-- ── Asset info ── -->
<!-- Asset info -->
<div class="section">
<div class="section-title">Fastighetsinformation</div>
<div class="info-grid">
@@ -550,7 +550,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
</div>
</div>
<!-- ── Condition assessment ── -->
<!-- Condition assessment -->
<div class="section">
<div class="section-title">Skickundersökning</div>
<div class="condition-grid">
@@ -621,44 +621,44 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
</div>
</div>
<!-- ── Photos ── -->
<!-- Photos -->
<div class="section">
<div class="section-title">Inspektionsfoton</div>
<div class="photo-grid">
<div class="photo-cell">
<div class="photo-cell-icon">📷</div>
<div class="photo-cell-icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="2" y="4" width="12" height="9" rx="2" stroke="#666" stroke-width="1.5"/><circle cx="8" cy="8.5" r="2.5" stroke="#666" stroke-width="1.5"/><path d="M5 4L6 2H10L11 4" stroke="#666" stroke-width="1.5"/></svg></div>
<div class="photo-cell-label">Fasad Norr</div>
<div class="photo-cell-tag">IMG_0001.jpg</div>
</div>
<div class="photo-cell">
<div class="photo-cell-icon">📷</div>
<div class="photo-cell-icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="2" y="4" width="12" height="9" rx="2" stroke="#666" stroke-width="1.5"/><circle cx="8" cy="8.5" r="2.5" stroke="#666" stroke-width="1.5"/><path d="M5 4L6 2H10L11 4" stroke="#666" stroke-width="1.5"/></svg></div>
<div class="photo-cell-label">Takyta — Skada</div>
<div class="photo-cell-tag">IMG_0002.jpg</div>
</div>
<div class="photo-cell">
<div class="photo-cell-icon">📷</div>
<div class="photo-cell-icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="2" y="4" width="12" height="9" rx="2" stroke="#666" stroke-width="1.5"/><circle cx="8" cy="8.5" r="2.5" stroke="#666" stroke-width="1.5"/><path d="M5 4L6 2H10L11 4" stroke="#666" stroke-width="1.5"/></svg></div>
<div class="photo-cell-label">Elcentral</div>
<div class="photo-cell-tag">IMG_0003.jpg</div>
</div>
<div class="photo-cell">
<div class="photo-cell-icon">📷</div>
<div class="photo-cell-icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="2" y="4" width="12" height="9" rx="2" stroke="#666" stroke-width="1.5"/><circle cx="8" cy="8.5" r="2.5" stroke="#666" stroke-width="1.5"/><path d="M5 4L6 2H10L11 4" stroke="#666" stroke-width="1.5"/></svg></div>
<div class="photo-cell-label">VVS — pannrum</div>
<div class="photo-cell-tag">IMG_0004.jpg</div>
</div>
<div class="photo-cell">
<div class="photo-cell-icon">📷</div>
<div class="photo-cell-icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="2" y="4" width="12" height="9" rx="2" stroke="#666" stroke-width="1.5"/><circle cx="8" cy="8.5" r="2.5" stroke="#666" stroke-width="1.5"/><path d="M5 4L6 2H10L11 4" stroke="#666" stroke-width="1.5"/></svg></div>
<div class="photo-cell-label">Lastdock</div>
<div class="photo-cell-tag">IMG_0005.jpg</div>
</div>
<div class="photo-cell">
<div class="photo-cell-icon">📷</div>
<div class="photo-cell-icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="2" y="4" width="12" height="9" rx="2" stroke="#666" stroke-width="1.5"/><circle cx="8" cy="8.5" r="2.5" stroke="#666" stroke-width="1.5"/><path d="M5 4L6 2H10L11 4" stroke="#666" stroke-width="1.5"/></svg></div>
<div class="photo-cell-label">Sprinkler — hall</div>
<div class="photo-cell-tag">IMG_0006.jpg</div>
</div>
</div>
</div>
<!-- ── Maintenance history ── -->
<!-- Maintenance history -->
<div class="section">
<div class="section-title">Underhållshistorik</div>
<div class="history-timeline">
@@ -715,7 +715,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
</div>
</div>
<!-- ── Recommendations ── -->
<!-- Recommendations -->
<div class="section">
<div class="section-title">Åtgärdsrekommendationer</div>
<div class="rec-list">
@@ -777,7 +777,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
</div>
</div>
<!-- ── Signature ── -->
<!-- Signature -->
<div class="section">
<div class="section-title">Signering</div>
<div class="signature-block">
@@ -799,7 +799,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
</div>
</div>
<!-- ── Footer ── -->
<!-- Footer -->
<div class="report-footer">
<span>LandveX Inspection System &copy; 2026 Wavult Group</span>
<span id="footer-assetid">Fastighets-ID: LVX-2024-0091</span>
+52 -52
View File
@@ -36,7 +36,7 @@
.nav-title { font-size: 17px; font-weight: 600; }
.nav-date { font-size: 13px; color: var(--label-secondary); margin-top: 2px; }
.nav-avatar {
width: 32px; height: 32px; border-radius: 50%;
width: 32px; height: 32px; border-radius: var(--radius-full);
background: var(--ios-blue); color: white;
display: flex; align-items: center; justify-content: center;
font-size: 14px; font-weight: 700;
@@ -77,7 +77,7 @@
.mission-card {
margin: 0 16px 10px;
background: var(--grouped-background-secondary);
border-radius: 12px; overflow: hidden;
border-radius: var(--radius-md); overflow: hidden;
box-shadow: 0 1px 3px rgba(0,0,0,0.08);
cursor: pointer;
transition: transform 0.15s, box-shadow 0.15s;
@@ -86,7 +86,7 @@
.mission-card:active { transform: scale(0.98); box-shadow: 0 0 0 rgba(0,0,0,0); }
.mission-top { display: flex; align-items: center; padding: 14px 16px; gap: 12px; }
.mission-type-icon {
width: 44px; height: 44px; border-radius: 10px;
width: 44px; height: 44px; border-radius: var(--radius-md);
display: flex; align-items: center; justify-content: center;
font-size: 22px; flex-shrink: 0;
}
@@ -99,7 +99,7 @@
.mission-addr { font-size: 13px; color: var(--label-secondary); margin-top: 2px; }
.mission-meta { display: flex; align-items: center; gap: 6px; margin-top: 6px; }
.mission-priority {
padding: 2px 8px; border-radius: 6px;
padding: 2px 8px; border-radius: var(--radius-sm);
font-size: 11px; font-weight: 700;
}
.priority-urgent { background: rgba(255,59,48,0.12); color: var(--ios-red); }
@@ -109,7 +109,7 @@
.mission-arrow { color: var(--ios-gray3); font-size: 14px; }
/* Map */
#map { height: 200px; margin: 0 16px 4px; border-radius: 12px; overflow: hidden; }
#map { height: 200px; margin: 0 16px 4px; border-radius: var(--radius-md); overflow: hidden; }
/* Tab bar */
.tab-bar {
@@ -154,11 +154,11 @@
display: flex; justify-content: center; gap: 8px; margin-bottom: 20px;
}
.step-dot {
width: 8px; height: 8px; border-radius: 50%;
width: 8px; height: 8px; border-radius: var(--radius-full);
background: var(--ios-gray4); transition: background 0.3s;
}
.step-dot.done { background: var(--ios-green); }
.step-dot.active { background: var(--ios-blue); width: 20px; border-radius: 4px; }
.step-dot.active { background: var(--ios-blue); width: 20px; border-radius: var(--radius-sm); }
/* Form elements */
.form-section { margin-bottom: 24px; }
@@ -166,7 +166,7 @@
.form-card {
background: var(--grouped-background-secondary);
border-radius: 12px; overflow: hidden;
border-radius: var(--radius-md); overflow: hidden;
box-shadow: 0 1px 3px rgba(0,0,0,0.08);
}
.form-field {
@@ -194,7 +194,7 @@
}
.check-item:last-child { border-bottom: none; }
.check-box {
width: 24px; height: 24px; border-radius: 50%;
width: 24px; height: 24px; border-radius: var(--radius-full);
border: 2px solid var(--ios-gray3);
flex-shrink: 0; display: flex; align-items: center; justify-content: center;
transition: all 0.15s;
@@ -213,7 +213,7 @@
}
.photo-thumb {
aspect-ratio: 1; background: var(--fill-tertiary);
border-radius: 8px; display: flex; align-items: center; justify-content: center;
border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center;
cursor: pointer; overflow: hidden; position: relative;
transition: opacity 0.15s;
}
@@ -222,7 +222,7 @@
.photo-preview { width: 100%; height: 100%; object-fit: cover; display: none; }
.photo-delete {
position: absolute; top: 4px; right: 4px;
width: 20px; height: 20px; border-radius: 50%;
width: 20px; height: 20px; border-radius: var(--radius-full);
background: rgba(242,242,247,0.5); color: white;
font-size: 12px; display: none; align-items: center; justify-content: center;
cursor: pointer;
@@ -230,7 +230,7 @@
/* Signature canvas */
.sig-wrap {
background: var(--fill-quaternary); border-radius: 10px;
background: var(--fill-quaternary); border-radius: var(--radius-md);
overflow: hidden; position: relative; margin: 0 16px;
}
#sig-canvas {
@@ -245,7 +245,7 @@
.sig-actions { display: flex; gap: 8px; padding: 10px 16px; }
.sig-clear-btn {
flex: 1; background: var(--fill-tertiary); border: none;
border-radius: 10px; padding: 10px; font-size: 14px; font-weight: 500;
border-radius: var(--radius-md); padding: 10px; font-size: 14px; font-weight: 500;
color: var(--ios-red); cursor: pointer; font-family: var(--font-text);
}
@@ -253,7 +253,7 @@
.submit-btn {
display: block; width: calc(100% - 32px); margin: 16px;
background: var(--ios-blue); color: white; border: none;
border-radius: 12px; padding: 15px; font-size: 16px; font-weight: 600;
border-radius: var(--radius-md); padding: 15px; font-size: 16px; font-weight: 600;
cursor: pointer; font-family: var(--font-text);
box-shadow: 0 2px 12px rgba(0,122,255,0.3);
transition: opacity 0.15s;
@@ -266,7 +266,7 @@
display: flex; gap: 12px; padding: 0 16px 16px;
}
.step-btn {
flex: 1; padding: 13px; border-radius: 10px;
flex: 1; padding: 13px; border-radius: var(--radius-md);
border: none; font-size: 15px; font-weight: 600;
cursor: pointer; font-family: var(--font-text);
transition: opacity 0.15s;
@@ -282,7 +282,7 @@
text-align: center;
}
.confirm-icon {
width: 80px; height: 80px; border-radius: 50%;
width: 80px; height: 80px; border-radius: var(--radius-full);
background: rgba(52,199,89,0.15); border: 2px solid var(--ios-green);
display: flex; align-items: center; justify-content: center;
font-size: 40px; margin-bottom: 24px;
@@ -292,12 +292,12 @@
.confirm-title { font-size: 24px; font-weight: 700; margin-bottom: 8px; }
.confirm-report-num {
font-size: 13px; color: var(--label-secondary);
background: var(--fill-tertiary); padding: 6px 16px; border-radius: 20px;
background: var(--fill-tertiary); padding: 6px 16px; border-radius: var(--radius-xl);
font-family: var(--font-mono); margin-bottom: 24px;
}
.confirm-details {
background: var(--grouped-background-secondary);
border-radius: 12px; width: 100%; text-align: left; overflow: hidden;
border-radius: var(--radius-md); width: 100%; text-align: left; overflow: hidden;
box-shadow: 0 1px 3px rgba(0,0,0,0.08); margin-bottom: 24px;
}
.confirm-row {
@@ -309,7 +309,7 @@
.confirm-val { font-weight: 500; }
.confirm-btn {
width: 100%; background: var(--ios-blue); color: white; border: none;
border-radius: 12px; padding: 14px; font-size: 16px; font-weight: 600;
border-radius: var(--radius-md); padding: 14px; font-size: 16px; font-weight: 600;
cursor: pointer; font-family: var(--font-text);
box-shadow: 0 2px 12px rgba(0,122,255,0.3);
}
@@ -443,7 +443,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<div class="nav-bar">
<div class="nav-row">
<div>
<div class="nav-title">🏗 Dagens Uppdrag</div>
<div class="nav-title"> Dagens Uppdrag</div>
<div class="nav-date" id="today-date"></div>
</div>
<div style="display:flex;gap:10px;align-items:center">
@@ -466,7 +466,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<div class="mission-card" onclick="openMission('bro-e45')">
<div class="mission-top">
<div class="mission-type-icon type-bridge">🌉</div>
<div class="mission-type-icon type-bridge"></div>
<div class="mission-info">
<div class="mission-name">Bro E45 — Götaälvbron</div>
<div class="mission-addr">Göteborg, Hamnbanan</div>
@@ -481,7 +481,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<div class="mission-card" onclick="openMission('vag-rv40')">
<div class="mission-top">
<div class="mission-type-icon type-road">🛣</div>
<div class="mission-type-icon type-road"></div>
<div class="mission-info">
<div class="mission-name">Väg RV40 km 142</div>
<div class="mission-addr">BoråsGöteborg, spricköversyn</div>
@@ -496,7 +496,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<div class="mission-card" onclick="openMission('fasad-kungsbacka')">
<div class="mission-top">
<div class="mission-type-icon type-facade">🏢</div>
<div class="mission-type-icon type-facade"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="3" y="2" width="10" height="12" rx="1" stroke="#666" stroke-width="1.5"/><path d="M6 5H7M6 8H7M6 11H7M9 5H10M9 8H10M9 11H10" stroke="#666" stroke-width="1.5" stroke-linecap="round"/></svg></div>
<div class="mission-info">
<div class="mission-name">Fasad Kungsbacka centrum</div>
<div class="mission-addr">Storgatan 12, Kungsbacka</div>
@@ -511,7 +511,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<div class="mission-card" onclick="openMission('tak-lindome')">
<div class="mission-top">
<div class="mission-type-icon type-roof">🏚</div>
<div class="mission-type-icon type-roof"></div>
<div class="mission-info">
<div class="mission-name">Tak Lindome industripark</div>
<div class="mission-addr">Lindome, Mölndal</div>
@@ -547,7 +547,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<!-- ==================== CONFIRMATION SCREEN ==================== -->
<div id="screen-confirm" class="screen">
<div class="confirm-screen">
<div class="confirm-icon"></div>
<div class="confirm-icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></div>
<div class="confirm-title">Rapport inskickad!</div>
<div class="confirm-report-num" id="report-num">LVX-2026-0000</div>
<div class="confirm-details">
@@ -569,7 +569,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
</div>
<div class="confirm-row">
<span class="confirm-key">Status</span>
<span class="confirm-val" style="color:var(--ios-green)"> Synkad</span>
<span class="confirm-val" style="color:var(--ios-green)"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Synkad</span>
</div>
</div>
<button class="confirm-btn" onclick="goHome()">← Tillbaka till uppdrag</button>
@@ -580,26 +580,26 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<!-- TAB BAR -->
<div class="tab-bar" id="main-tab-bar">
<div class="tab-item active" onclick="switchTab('list')">
<div class="tab-icon">📋</div>
<div class="tab-icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="3" y="2" width="10" height="12" rx="1" stroke="#666" stroke-width="1.5"/><path d="M6 5H10M6 8H10M6 11H8" stroke="#666" stroke-width="1.5" stroke-linecap="round"/></svg></div>
<span>Uppdrag</span>
</div>
<div class="tab-item" onclick="switchTab('map-tab')">
<div class="tab-icon">🗺</div>
<div class="tab-icon"></div>
<span>Karta</span>
</div>
<div class="tab-item" onclick="switchTab('reports')">
<div class="tab-icon">📂</div>
<div class="tab-icon"></div>
<span>Rapporter</span>
</div>
<div class="tab-item" onclick="switchTab('profile')">
<div class="tab-icon">👤</div>
<div class="tab-icon"></div>
<span>Profil</span>
</div>
</div>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script>
// ─── State ───────────────────────────────────────────────────────────────────
// State
const STORAGE_KEY = 'landvex_inspector_state';
let currentMission = null;
let currentStep = 0;
@@ -651,7 +651,7 @@ const CHECKLISTS = {
],
};
// ─── Init ─────────────────────────────────────────────────────────────────────
// Init
document.getElementById('today-date').textContent = new Date().toLocaleDateString('sv-SE', { weekday:'long', year:'numeric', month:'long', day:'numeric' });
// Init map
@@ -661,7 +661,7 @@ Object.entries(MISSIONS).forEach(([id, m]) => {
L.marker([m.lat, m.lng]).addTo(map).bindPopup(`<b>${m.name}</b>`);
});
// ─── Screens ──────────────────────────────────────────────────────────────────
// Screens
function showScreen(id) {
document.querySelectorAll('.screen').forEach(s => s.classList.remove('active'));
document.getElementById(id).classList.add('active');
@@ -691,7 +691,7 @@ function switchTab(tab) {
}
}
// ─── Missions ─────────────────────────────────────────────────────────────────
// Missions
function openMission(id) {
currentMission = id;
currentStep = 0;
@@ -730,10 +730,10 @@ function renderStep() {
<div class="form-field">
<div class="field-label">Objekttyp</div>
<select class="field-input" id="f-type">
<option value="bro" ${m.type==='bro'?'selected':''}>🌉 Bro</option>
<option value="väg" ${m.type==='väg'?'selected':''}>🛣 Väg</option>
<option value="fasad" ${m.type==='fasad'?'selected':''}>🏢 Fasad</option>
<option value="tak" ${m.type==='tak'?'selected':''}>🏚 Tak</option>
<option value="bro" ${m.type==='bro'?'selected':''}> Bro</option>
<option value="väg" ${m.type==='väg'?'selected':''}> Väg</option>
<option value="fasad" ${m.type==='fasad'?'selected':''}><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="3" y="2" width="10" height="12" rx="1" stroke="#666" stroke-width="1.5"/><path d="M6 5H7M6 8H7M6 11H7M9 5H10M9 8H10M9 11H10" stroke="#666" stroke-width="1.5" stroke-linecap="round"/></svg> Fasad</option>
<option value="tak" ${m.type==='tak'?'selected':''}> Tak</option>
</select>
</div>
<div class="form-field">
@@ -752,10 +752,10 @@ function renderStep() {
<div class="form-field">
<div class="field-label">Väder</div>
<select class="field-input" id="f-weather">
<option>☀️ Soligt</option>
<option>⛅️ Molnigt</option>
<option>🌧 Regn</option>
<option>❄️ Snö</option>
<option> Soligt</option>
<option> Molnigt</option>
<option> Regn</option>
<option> Snö</option>
</select>
</div>
<div class="form-field">
@@ -774,7 +774,7 @@ function renderStep() {
const items = checklist.map(c => `
<div class="check-item" onclick="toggleCheck('${c.id}')">
<div class="check-box ${checklistState[c.id] ? 'checked' : ''}" id="chk-${c.id}">
${checklistState[c.id] ? '' : ''}
${checklistState[c.id] ? '<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>' : ''}
</div>
<div>
<div class="check-text">${c.text}</div>
@@ -801,8 +801,8 @@ function renderStep() {
const photoSlots = Array(6).fill(0).map((_, i) => `
<div class="photo-thumb" id="photo-slot-${i}" onclick="addPhoto(${i})">
<img loading="lazy" loading="lazy" loading="lazy" loading="lazy" class="photo-preview" id="photo-img-${i}" src="" alt="Inspection photo">
<div class="photo-add-icon" id="photo-add-${i}">📷</div>
<div class="photo-delete" id="photo-del-${i}" onclick="deletePhoto(event,${i})"></div>
<div class="photo-add-icon" id="photo-add-${i}"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="2" y="4" width="12" height="9" rx="2" stroke="#666" stroke-width="1.5"/><circle cx="8" cy="8.5" r="2.5" stroke="#666" stroke-width="1.5"/><path d="M5 4L6 2H10L11 4" stroke="#666" stroke-width="1.5"/></svg></div>
<div class="photo-delete" id="photo-del-${i}" onclick="deletePhoto(event,${i})"></div>
</div>`).join('');
content = `
@@ -833,15 +833,15 @@ function renderStep() {
<div class="form-section-title">Digital signatur</div>
<div class="sig-wrap">
<canvas id="sig-canvas" height="180"></canvas>
<div class="sig-label" id="sig-hint">✍️ Rita din signatur här</div>
<div class="sig-label" id="sig-hint"> Rita din signatur här</div>
</div>
<div class="sig-actions">
<button class="sig-clear-btn" onclick="clearSig()"> Rensa</button>
<button class="sig-clear-btn" onclick="clearSig()"> Rensa</button>
</div>
</div>
</div>
<div style="padding:0 16px">
<button class="submit-btn" id="submit-btn" onclick="submitReport()">📤 Skicka in rapport</button>
<button class="submit-btn" id="submit-btn" onclick="submitReport()"> Skicka in rapport</button>
</div>
<div style="padding:0 16px 16px">
<button class="step-btn step-prev" style="width:100%;margin-top:8px" onclick="prevStep()">← Foton</button>
@@ -871,7 +871,7 @@ function prevStep() {
document.getElementById('form-scroll-area').scrollTop = 0;
}
// ─── Photos ───────────────────────────────────────────────────────────────────
// Photos
let activePhotoSlot = 0;
function addPhoto(slot) {
activePhotoSlot = slot;
@@ -900,7 +900,7 @@ function deletePhoto(e, slot) {
add.style.display = 'flex'; del.style.display = 'none';
}
// ─── Signature ────────────────────────────────────────────────────────────────
// Signature
function initSig() {
sigCanvas = document.getElementById('sig-canvas');
if (!sigCanvas) return;
@@ -932,7 +932,7 @@ function clearSig() {
if (hint) hint.style.opacity = '1';
}
// ─── Submit ───────────────────────────────────────────────────────────────────
// Submit
function submitReport() {
const btn = document.getElementById('submit-btn');
btn.disabled = true; btn.textContent = '⏳ Skickar...';
@@ -960,7 +960,7 @@ function submitReport() {
}, 1500);
}
// ─── Offline state ─────────────────────────────────────────────────────────────
// Offline state
function saveOfflineState() {
try {
localStorage.setItem(STORAGE_KEY, JSON.stringify({ currentMission, currentStep, checklistState }));
+76 -76
View File
@@ -17,61 +17,61 @@
html,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,sans-serif;background:var(--bg);color:var(--text);overflow:hidden}
#map{position:fixed;inset:0;z-index:1}
.leaflet-control-zoom{margin-top:calc(var(--safe-top) + 72px) !important;margin-left:16px !important}
.leaflet-control-zoom a{background:var(--surface) !important;color:var(--text) !important;border-color:var(--border2) !important;width:34px !important;height:34px !important;line-height:34px !important;border-radius:8px !important;font-size:18px !important}
.leaflet-control-zoom a{background:var(--surface) !important;color:var(--text) !important;border-color:var(--border2) !important;width:34px !important;height:34px !important;line-height:34px !important;border-radius: var(--radius-md) !important;font-size:18px !important}
#hdr{position:fixed;top:0;left:0;right:0;z-index:1000;background:rgba(7,12,20,0.96);backdrop-filter:blur(24px) saturate(200%);-webkit-backdrop-filter:blur(24px) saturate(200%);border-bottom:1px solid var(--border);padding:calc(var(--safe-top) + 8px) 20px 8px}
#hdr-i{max-width:1400px;margin:0 auto;display:flex;align-items:center;gap:12px;flex-wrap:nowrap;overflow:hidden}
#logo{font-size:16px;font-weight:800;letter-spacing:-0.5px;color:var(--text);text-decoration:none;white-space:nowrap}
#logo em{color:var(--accent);font-style:normal}
.hdiv{width:1px;height:20px;background:var(--border2)}
#hdr-title{font-size:13px;font-weight:600;color:var(--text2);flex:1}
.hpill{font-size:10px;font-weight:700;letter-spacing:0.6px;text-transform:uppercase;border-radius:20px;padding:3px 10px;white-space:nowrap}
.hpill{font-size:10px;font-weight:700;letter-spacing:0.6px;text-transform:uppercase;border-radius: var(--radius-xl);padding:3px 10px;white-space:nowrap}
.pill-free{background:rgba(34,197,94,0.12);color:#4ade80;border:1px solid rgba(34,197,94,0.25)}
.pill-live{background:rgba(14,165,233,0.15);color:var(--accent2);border:1px solid rgba(14,165,233,0.3)}
#hdr-cta{background:var(--accent);color:white;border:none;border-radius:8px;padding:7px 16px;font-size:12px;font-weight:700;cursor:pointer;white-space:nowrap;text-decoration:none;display:inline-block;transition:opacity 0.2s}
#hdr-cta{background:var(--accent);color:white;border:none;border-radius: var(--radius-md);padding:7px 16px;font-size:12px;font-weight:700;cursor:pointer;white-space:nowrap;text-decoration:none;display:inline-block;transition:opacity 0.2s}
#hdr-cta:hover{opacity:0.85}
#sidebar{position:fixed;top:calc(var(--safe-top) + 54px);left:16px;z-index:900;width:220px;display:flex;flex-direction:column;gap:6px;pointer-events:none}
.sbc{background:rgba(13,21,32,0.93);backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px);border:1px solid var(--border);border-radius:12px;padding:12px 14px;pointer-events:all}
.sbc{background:rgba(13,21,32,0.93);backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px);border:1px solid var(--border);border-radius: var(--radius-md);padding:12px 14px;pointer-events:all}
.sbl{font-size:10px;font-weight:700;letter-spacing:0.8px;text-transform:uppercase;color:var(--text2);margin-bottom:8px}
.lr{display:flex;align-items:center;justify-content:space-between;padding:5px 0;border-bottom:1px solid var(--border);cursor:pointer}
.lr:last-child{border-bottom:none}
.ll{display:flex;align-items:center;gap:8px;font-size:12px;font-weight:500;color:var(--text)}
.ld2{width:9px;height:9px;border-radius:50%;flex-shrink:0}
.tog{width:32px;height:18px;border-radius:9px;position:relative;transition:background 0.2s;flex-shrink:0}
.ld2{width:9px;height:9px;border-radius: var(--radius-full);flex-shrink:0}
.tog{width:32px;height:18px;border-radius: var(--radius-md);position:relative;transition:background 0.2s;flex-shrink:0}
.tog.on{background:var(--accent)}
.tog.off{background:var(--surface3)}
.tog::after{content:'';position:absolute;top:3px;left:3px;width:12px;height:12px;border-radius:50%;background:white;transition:transform 0.2s}
.tog::after{content:'';position:absolute;top:3px;left:3px;width:12px;height:12px;border-radius: var(--radius-full);background:white;transition:transform 0.2s}
.tog.on::after{transform:translateX(14px)}
.ig{display:flex;flex-direction:column;gap:3px}
.ir{display:flex;align-items:center;justify-content:space-between;padding:5px 6px;border-radius:7px;cursor:pointer;transition:background 0.15s}
.ir{display:flex;align-items:center;justify-content:space-between;padding:5px 6px;border-radius: var(--radius-sm);cursor:pointer;transition:background 0.15s}
.ir:hover{background:var(--surface3)}
.ir.active{background:rgba(14,165,233,0.12);border:1px solid rgba(14,165,233,0.2)}
.in{font-size:11px;color:var(--text);font-weight:500}
.ib{font-size:10px;font-weight:700;border-radius:5px;padding:2px 6px}
.ib{font-size:10px;font-weight:700;border-radius: var(--radius-sm);padding:2px 6px}
.ib-free{background:rgba(34,197,94,0.12);color:#4ade80}
.ib-paid{background:rgba(245,158,11,0.12);color:#fbbf24}
#bot{position:fixed;bottom:0;left:0;right:0;z-index:900;background:rgba(7,12,20,0.96);backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);border-top:1px solid var(--border);padding:8px 20px calc(var(--safe-bottom) + 8px)}
#bot-i{max-width:1400px;margin:0 auto;display:flex;align-items:center;gap:12px;flex-wrap:nowrap;overflow:hidden}
#leg{display:flex;gap:10px;flex-wrap:wrap;flex:1}
.li{display:flex;align-items:center;gap:5px;font-size:11px;color:var(--text2)}
.ld{width:10px;height:10px;border-radius:2px;flex-shrink:0}
.ld{width:10px;height:10px;border-radius: var(--radius-sm);flex-shrink:0}
#disc{font-size:10px;color:var(--text3);line-height:1.4;max-width:380px}
.leaflet-popup-pane{z-index:2000 !important}
.leaflet-popup-content-wrapper{background:#ffffff !important;border:none !important;border-radius:16px !important;box-shadow:0 8px 40px rgba(0,0,0,0.18) !important;padding:0 !important;max-height:65vh !important;overflow-y:auto !important}
.leaflet-popup-content-wrapper{background:#ffffff !important;border:none !important;border-radius: var(--radius-lg) !important;box-shadow:0 8px 40px rgba(0,0,0,0.18) !important;padding:0 !important;max-height:65vh !important;overflow-y:auto !important}
.leaflet-popup-content{margin:0 !important;width:280px !important;color:#0A0A0A !important;max-height:60vh !important;overflow-y:auto !important}
.leaflet-popup-tip{background:#ffffff !important}
.leaflet-popup-close-button{color:rgba(28,28,30,0.45) !important;top:10px !important;right:12px !important;font-size:18px !important}
.zpop{padding:16px 18px;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif}
.zcity{font-size:15px;font-weight:800;color:#0A0A0A;letter-spacing:-0.3px}
.zcountry{font-size:12px;color:rgba(28,28,30,0.55);font-weight:400}
.zconf{font-size:10px;font-weight:600;background:#f2f2f7;border-radius:5px;padding:2px 7px;color:rgba(28,28,30,0.6);display:inline-block;margin:4px 0 10px}
.zconf{font-size:10px;font-weight:600;background:#f2f2f7;border-radius: var(--radius-sm);padding:2px 7px;color:rgba(28,28,30,0.6);display:inline-block;margin:4px 0 10px}
.zsect{font-size:10px;font-weight:700;letter-spacing:0.7px;text-transform:uppercase;color:var(--text2);margin:8px 0 5px}
.zindices{display:grid;grid-template-columns:1fr 1fr;gap:5px;margin-bottom:8px}
.zidx{background:#f2f2f7;border-radius:8px;padding:7px 9px}
.zidx{background:#f2f2f7;border-radius: var(--radius-md);padding:7px 9px}
.zil{font-size:10px;color:rgba(28,28,30,0.55);font-weight:500;margin-bottom:3px}
.zbar{height:3px;border-radius:2px;background:rgba(0,0,0,0.08);margin-bottom:3px}
.zfill{height:100%;border-radius:2px}
.zbar{height:3px;border-radius: var(--radius-sm);background:rgba(0,0,0,0.08);margin-bottom:3px}
.zfill{height:100%;border-radius: var(--radius-sm)}
.zval{font-size:13px;font-weight:800;color:#0A0A0A}
.zcontra{border-radius:8px;padding:8px 11px;font-size:11px;line-height:1.5;margin-bottom:8px}
.zcontra{border-radius: var(--radius-md);padding:8px 11px;font-size:11px;line-height:1.5;margin-bottom:8px}
.zcontra.low{background:rgba(34,197,94,0.1);color:#86efac;border:1px solid rgba(34,197,94,0.18)}
.zcontra.medium{background:rgba(249,115,22,0.08);color:#92400e;border:1px solid rgba(249,115,22,0.2)}
.zcontra.high{background:rgba(239,68,68,0.08);color:#991b1b;border:1px solid rgba(239,68,68,0.2)}
@@ -79,10 +79,10 @@
.zage{font-size:11px;color:#16a34a;font-weight:600;margin-bottom:10px}
.lock-wrap{position:relative;margin-bottom:10px}
.lock-blur{filter:blur(3px);pointer-events:none;opacity:0.5}
.lock-over{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;background:rgba(7,12,20,0.75);border-radius:8px;border:1px solid var(--border2)}
.lock-over{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;background:rgba(7,12,20,0.75);border-radius: var(--radius-md);border:1px solid var(--border2)}
.lock-over span:first-child{font-size:16px}
.lock-over span:last-child{font-size:11px;color:var(--text2);font-weight:600}
.zcta{display:block;width:100%;background:#007aff;color:white;border:none;border-radius:9px;padding:10px;font-size:12px;font-weight:700;cursor:pointer;text-align:center;text-decoration:none;transition:opacity 0.2s;margin-top:2px}
.zcta{display:block;width:100%;background:#007aff;color:white;border:none;border-radius: var(--radius-md);padding:10px;font-size:12px;font-weight:700;cursor:pointer;text-align:center;text-decoration:none;transition:opacity 0.2s;margin-top:2px}
.zcta:hover{opacity:0.85}
.zdisc{font-size:10px;color:rgba(28,28,30,0.35);margin-top:8px;text-align:center;line-height:1.4}
.zdisc a{color:#007aff;text-decoration:none}
@@ -93,7 +93,7 @@
#bsheet{
display:none;position:fixed;left:0;right:0;bottom:0;z-index:9999;
background:#ffffff;border-top:1px solid rgba(0,0,0,0.1);
border-radius:16px 16px 0 0;
border-radius: var(--radius-lg) 16px 0 0;
box-shadow:0 -4px 24px rgba(0,0,0,0.15);
max-height:72vh;overflow-y:auto;
-webkit-overflow-scrolling:touch;
@@ -101,8 +101,8 @@
transition:transform 0.28s cubic-bezier(0.32,0.72,0,1);
}
#bsheet.open{display:block;transform:translateY(0)}
#bsheet-handle{width:36px;height:4px;border-radius:2px;background:rgba(0,0,0,0.15);margin:10px auto 0}
#bsheet-close{position:absolute;top:10px;right:12px;background:rgba(0,0,0,0.06);border:none;color:#0A0A0A;font-size:16px;width:28px;height:28px;border-radius:50%;cursor:pointer;display:flex;align-items:center;justify-content:center}
#bsheet-handle{width:36px;height:4px;border-radius: var(--radius-sm);background:rgba(0,0,0,0.15);margin:10px auto 0}
#bsheet-close{position:absolute;top:10px;right:12px;background:rgba(0,0,0,0.06);border:none;color:#0A0A0A;font-size:16px;width:28px;height:28px;border-radius: var(--radius-full);cursor:pointer;display:flex;align-items:center;justify-content:center}
#bsheet-content{padding:8px 0 env(safe-area-inset-bottom,16px)}
#bsheet-backdrop{display:none;position:fixed;inset:0;z-index:9998;background:rgba(0,0,0,0.3)}
#bsheet-backdrop.open{display:block}
@@ -385,7 +385,7 @@
const heeScore = l && l.hee_score ? l.hee_score : null;
return `<div style="border-top:1px solid rgba(0,0,0,0.08);margin-top:10px;padding-top:10px">
<div style="font-size:9px;font-weight:700;letter-spacing:0.8px;text-transform:uppercase;color:rgba(28,28,30,0.4);margin-bottom:8px">FOOD ENVIRONMENT & HUMAN ENERGY</div>
${heeScore ? `<div style="background:#f2f2f7;border-radius:10px;padding:10px 12px;margin-bottom:8px;display:flex;align-items:center;justify-content:space-between">
${heeScore ? `<div style="background:#f2f2f7;border-radius: var(--radius-md);padding:10px 12px;margin-bottom:8px;display:flex;align-items:center;justify-content:space-between">
<div>
<div style="font-size:9px;color:rgba(28,28,30,0.45);margin-bottom:2px">Human Energy Efficiency</div>
<div style="font-size:9px;color:rgba(28,28,30,0.35)">LCS adjusted for food environment</div>
@@ -393,26 +393,26 @@
<div style="font-size:32px;font-weight:900;color:${heeScore>=78?'#16a34a':heeScore>=62?'#ca8a04':'#dc2626'};font-family:monospace">${heeScore}</div>
</div>` : ''}
<div style="display:grid;grid-template-columns:1fr 1fr;gap:5px;margin-bottom:8px">
<div style="background:#f2f2f7;border-radius:8px;padding:7px 9px">
<div style="background:#f2f2f7;border-radius: var(--radius-md);padding:7px 9px">
<div style="font-size:9px;color:rgba(28,28,30,0.5);margin-bottom:2px">Food env. score</div>
<div style="font-size:15px;font-weight:800;color:${scoreCol}">${f.food_environment_score}/100</div>
</div>
<div style="background:#f2f2f7;border-radius:8px;padding:7px 9px">
<div style="background:#f2f2f7;border-radius: var(--radius-md);padding:7px 9px">
<div style="font-size:9px;color:rgba(28,28,30,0.5);margin-bottom:2px">Ultra-processed</div>
<div style="font-size:15px;font-weight:800;color:${upfCol}">${f.ultra_processed_pct}%</div>
<div style="font-size:8px;color:rgba(28,28,30,0.35)">of calories</div>
</div>
<div style="background:#f2f2f7;border-radius:8px;padding:7px 9px">
<div style="background:#f2f2f7;border-radius: var(--radius-md);padding:7px 9px">
<div style="font-size:9px;color:rgba(28,28,30,0.5);margin-bottom:2px">Obesity rate</div>
<div style="font-size:15px;font-weight:800;color:${obeseCol}">${f.obesity_pct}%</div>
</div>
<div style="background:#f2f2f7;border-radius:8px;padding:7px 9px">
<div style="background:#f2f2f7;border-radius: var(--radius-md);padding:7px 9px">
<div style="font-size:9px;color:rgba(28,28,30,0.5);margin-bottom:2px">Food/income</div>
<div style="font-size:15px;font-weight:800;color:${foodIncCol}">${f.food_income_pct}%</div>
</div>
</div>
${f.contradiction ? `<div style="background:rgba(245,158,11,0.08);border:1px solid rgba(245,158,11,0.2);border-radius:7px;padding:7px 9px;font-size:10px;color:#92400e;line-height:1.4;margin-bottom:6px"><span style="font-weight:800">Food contradiction: </span>${f.contradiction}</div>` : ''}
<div style="background:#f2f2f7;border-radius:7px;padding:7px 9px;font-size:10px;color:rgba(28,28,30,0.6);line-height:1.5;margin-bottom:5px">${f.note}</div>
${f.contradiction ? `<div style="background:rgba(245,158,11,0.08);border:1px solid rgba(245,158,11,0.2);border-radius: var(--radius-sm);padding:7px 9px;font-size:10px;color:#92400e;line-height:1.4;margin-bottom:6px"><span style="font-weight:800">Food contradiction: </span>${f.contradiction}</div>` : ''}
<div style="background:#f2f2f7;border-radius: var(--radius-sm);padding:7px 9px;font-size:10px;color:rgba(28,28,30,0.6);line-height:1.5;margin-bottom:5px">${f.note}</div>
<div style="font-size:9px;color:rgba(28,28,30,0.3)">Source: ${f.source||'WHO / NOVA 2024'}</div>
</div>`;
}
@@ -425,8 +425,8 @@
const dimBar = (val, label, col) => `
<div style="display:flex;align-items:center;gap:6px;margin-bottom:5px">
<span style="width:72px;font-size:10px;color:rgba(28,28,30,0.55);flex-shrink:0">${label}</span>
<div style="flex:1;height:6px;background:rgba(0,0,0,0.08);border-radius:3px">
<div style="width:${val}%;height:100%;background:${col};border-radius:3px"></div>
<div style="flex:1;height:6px;background:rgba(0,0,0,0.08);border-radius: 3px">
<div style="width:${val}%;height:100%;background:${col};border-radius: 3px"></div>
</div>
<span style="font-size:11px;font-weight:800;color:${col};width:28px;text-align:right">${val}</span>
</div>`;
@@ -443,7 +443,7 @@
<div style="font-size:9px;font-weight:700;letter-spacing:0.8px;text-transform:uppercase;color:rgba(28,28,30,0.4);margin-bottom:8px">LANDVEX CIVILIZATIONAL SCORE</div>
<!-- Main score -->
<div style="background:#f2f2f7;border-radius:12px;padding:12px;margin-bottom:8px">
<div style="background:#f2f2f7;border-radius: var(--radius-md);padding:12px;margin-bottom:8px">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:10px">
<div>
<div style="font-size:36px;font-weight:900;color:${scoreCol};line-height:1;font-family:monospace">${l.lcs_score}</div>
@@ -459,7 +459,7 @@
</div>
<!-- Vitality stats -->
<div style="background:#f2f2f7;border-radius:8px;padding:8px 10px;margin-bottom:6px">
<div style="background:#f2f2f7;border-radius: var(--radius-md);padding:8px 10px;margin-bottom:6px">
<div style="font-size:9px;font-weight:700;color:rgba(28,28,30,0.4);text-transform:uppercase;letter-spacing:0.5px;margin-bottom:5px">Vitality</div>
${statRow('Life expectancy', l.life_expectancy, ' yrs')}
${statRow('Healthy years', l.healthy_years, ' yrs')}
@@ -470,15 +470,15 @@
</div>
<!-- Demography -->
<div style="background:#f2f2f7;border-radius:8px;padding:8px 10px;margin-bottom:6px">
<div style="background:#f2f2f7;border-radius: var(--radius-md);padding:8px 10px;margin-bottom:6px">
<div style="font-size:9px;font-weight:700;color:rgba(28,28,30,0.4);text-transform:uppercase;letter-spacing:0.5px;margin-bottom:5px">Demographics</div>
${statRow('Fertility rate', l.fertility_rate, ' children/woman', fertCol)}
${statRow('Median age', l.median_age, ' yrs')}
${l.fertility_rate < 1.4 ? `<div style="background:rgba(220,38,38,0.08);border-radius:5px;padding:4px 7px;font-size:9px;color:#991b1b;font-weight:600;margin-top:4px">Demographic warning: below replacement level</div>` : ''}
${l.fertility_rate < 1.4 ? `<div style="background:rgba(220,38,38,0.08);border-radius: var(--radius-sm);padding:4px 7px;font-size:9px;color:#991b1b;font-weight:600;margin-top:4px">Demographic warning: below replacement level</div>` : ''}
</div>
<!-- Society -->
<div style="background:#f2f2f7;border-radius:8px;padding:8px 10px;margin-bottom:6px">
<div style="background:#f2f2f7;border-radius: var(--radius-md);padding:8px 10px;margin-bottom:6px">
<div style="font-size:9px;font-weight:700;color:rgba(28,28,30,0.4);text-transform:uppercase;letter-spacing:0.5px;margin-bottom:5px">Society</div>
${statRow('Homicide rate', l.homicide_per100k, '/100k', l.homicide_per100k < 1 ? '#16a34a' : l.homicide_per100k < 3 ? '#ca8a04' : '#dc2626')}
${statRow('Prison rate', l.prison_per100k, '/100k')}
@@ -488,7 +488,7 @@
${statRow('Housing/income ratio', l.housing_income_ratio + 'x', '', l.housing_income_ratio < 10 ? '#16a34a' : l.housing_income_ratio < 15 ? '#ca8a04' : '#dc2626')}
</div>
${l.contradiction_note ? `<div style="background:rgba(245,158,11,0.07);border-left:3px solid #f59e0b;border-radius:0 7px 7px 0;padding:8px 10px;margin-bottom:6px;font-size:10px;color:rgba(28,28,30,0.65);line-height:1.5"><span style="font-weight:800;color:#92400e">Landvex Contradiction: </span>${l.contradiction_note}</div>` : ''}
${l.contradiction_note ? `<div style="background:rgba(245,158,11,0.07);border-left:3px solid #f59e0b;border-radius: 0 7px 7px 0;padding:8px 10px;margin-bottom:6px;font-size:10px;color:rgba(28,28,30,0.65);line-height:1.5"><span style="font-weight:800;color:#92400e">Landvex Contradiction: </span>${l.contradiction_note}</div>` : ''}
<div style="font-size:9px;color:rgba(28,28,30,0.3)">Source: ${l.source||'WHO / OECD / World Bank 2024'}</div>
</div>`;
}
@@ -511,11 +511,11 @@
</div>` : '';
const tagList = (items, col) => items && items.length ?
items.map(t=>`<span style="background:${col}15;color:${col};border-radius:4px;padding:2px 6px;font-size:9px;font-weight:600">${t}</span>`).join(' ') : '';
items.map(t=>`<span style="background:${col}15;color:${col};border-radius: var(--radius-sm);padding:2px 6px;font-size:9px;font-weight:600">${t}</span>`).join(' ') : '';
return `<div style="border-top:1px solid rgba(0,0,0,0.08);margin-top:10px;padding-top:10px">
<div style="font-size:9px;font-weight:700;letter-spacing:0.8px;text-transform:uppercase;color:rgba(28,28,30,0.4);margin-bottom:8px">LANDVEX FRICTION SCORE</div>
<div style="background:#f2f2f7;border-radius:10px;padding:12px;margin-bottom:8px">
<div style="background:#f2f2f7;border-radius: var(--radius-md);padding:12px;margin-bottom:8px">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:8px">
<div>
<div style="font-size:28px;font-weight:900;color:${col};line-height:1;font-family:monospace">${score}</div>
@@ -526,14 +526,14 @@
<div style="font-size:9px;color:rgba(28,28,30,0.4);margin-top:2px">${pct}% efficiency vs max friction</div>
</div>
</div>
<div style="height:8px;background:rgba(0,0,0,0.08);border-radius:4px;overflow:hidden">
<div style="width:${score}%;height:100%;background:${col};border-radius:4px"></div>
<div style="height:8px;background:rgba(0,0,0,0.08);border-radius: var(--radius-sm);overflow:hidden">
<div style="width:${score}%;height:100%;background:${col};border-radius: 4px"></div>
</div>
<div style="display:flex;justify-content:space-between;font-size:8px;color:rgba(28,28,30,0.3);margin-top:3px">
<span>0 — No friction</span><span>100 — Extreme</span>
</div>
</div>
<div style="background:#f2f2f7;border-radius:8px;padding:8px 10px;margin-bottom:6px">
<div style="background:#f2f2f7;border-radius: var(--radius-md);padding:8px 10px;margin-bottom:6px">
<div style="font-size:9px;font-weight:700;color:rgba(28,28,30,0.4);text-transform:uppercase;letter-spacing:0.5px;margin-bottom:6px">Key metrics</div>
${row('Company start', co.company_start_days, ' days')}
${row('Tax hours/year', co.tax_hours_per_year, ' hrs')}
@@ -545,32 +545,32 @@
${f.weaknesses ? `<div style="margin-bottom:8px"><div style="font-size:9px;color:rgba(28,28,30,0.4);margin-bottom:4px;font-weight:600">FRICTION POINTS</div><div style="display:flex;flex-wrap:wrap;gap:4px">${tagList(f.weaknesses,'#dc2626')}</div></div>` : ''}
${f.entry_friction !== undefined ? `
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:4px;margin-top:8px;margin-bottom:6px">
<div style="background:#f2f2f7;border-radius:7px;padding:6px 8px;text-align:center">
<div style="background:#f2f2f7;border-radius: var(--radius-sm);padding:6px 8px;text-align:center">
<div style="font-size:8px;color:rgba(28,28,30,0.45);margin-bottom:2px">Entry</div>
<div style="font-size:14px;font-weight:800;color:${f.entry_friction<=20?'#16a34a':f.entry_friction<=40?'#ca8a04':'#dc2626'}">${f.entry_friction}</div>
</div>
<div style="background:#f2f2f7;border-radius:7px;padding:6px 8px;text-align:center">
<div style="background:#f2f2f7;border-radius: var(--radius-sm);padding:6px 8px;text-align:center">
<div style="font-size:8px;color:rgba(28,28,30,0.45);margin-bottom:2px">Operating</div>
<div style="font-size:14px;font-weight:800;color:${f.operating_friction<=25?'#16a34a':f.operating_friction<=50?'#ca8a04':'#dc2626'}">${f.operating_friction}</div>
</div>
<div style="background:#f2f2f7;border-radius:7px;padding:6px 8px;text-align:center">
<div style="background:#f2f2f7;border-radius: var(--radius-sm);padding:6px 8px;text-align:center">
<div style="font-size:8px;color:rgba(28,28,30,0.45);margin-bottom:2px">Scale adv.</div>
<div style="font-size:14px;font-weight:800;color:${f.scale_advantage<=30?'#16a34a':f.scale_advantage<=55?'#ca8a04':'#dc2626'}">${f.scale_advantage}</div>
</div>
</div>` : ''}
${f.sme_burden_pct !== undefined ? `
<div style="background:rgba(220,38,38,0.06);border-radius:7px;padding:8px 10px;margin-bottom:6px;font-size:10px">
<div style="background:rgba(220,38,38,0.06);border-radius: var(--radius-sm);padding:8px 10px;margin-bottom:6px;font-size:10px">
<div style="font-weight:700;color:#991b1b;margin-bottom:4px">Compliance cost as % of turnover</div>
<div style="display:flex;justify-content:space-between">
<div><span style="color:rgba(28,28,30,0.5)">Micro-business</span><br><span style="font-size:16px;font-weight:900;color:#dc2626">${f.sme_burden_pct}%</span></div>
<div style="text-align:center;padding:0 8px;color:rgba(28,28,30,0.2);font-size:18px">vs</div>
<div style="text-align:right"><span style="color:rgba(28,28,30,0.5)">Large corp</span><br><span style="font-size:16px;font-weight:900;color:#16a34a">${f.large_co_burden_pct}%</span></div>
</div>
<div style="height:4px;background:rgba(0,0,0,0.06);border-radius:2px;margin-top:6px;overflow:hidden">
<div style="width:${Math.min(f.sme_burden_pct/30*100,100)}%;height:100%;background:#dc2626;border-radius:2px"></div>
<div style="height:4px;background:rgba(0,0,0,0.06);border-radius: var(--radius-sm);margin-top:6px;overflow:hidden">
<div style="width:${Math.min(f.sme_burden_pct/30*100,100)}%;height:100%;background:#dc2626;border-radius: 2px"></div>
</div>
</div>` : ''}
${f.glasskiosk_note ? `<div style="background:#f2f2f7;border-radius:7px;padding:7px 9px;font-size:10px;color:rgba(28,28,30,0.6);line-height:1.5;margin-bottom:6px"><span style="font-weight:700">Example: </span>${f.glasskiosk_note}</div>` : ''}
${f.glasskiosk_note ? `<div style="background:#f2f2f7;border-radius: var(--radius-sm);padding:7px 9px;font-size:10px;color:rgba(28,28,30,0.6);line-height:1.5;margin-bottom:6px"><span style="font-weight:700">Example: </span>${f.glasskiosk_note}</div>` : ''}
${f.elasticity ? `<div style="font-size:10px;color:rgba(28,28,30,0.55);margin-bottom:6px"><span style="font-weight:700">Regressivity: </span>${f.elasticity}</div>` : ''}
<div style="font-size:9px;color:rgba(28,28,30,0.3)">Source: ${f.source||'World Bank / OECD 2023-2024'}</div>
</div>`;
@@ -590,24 +590,24 @@
const bar = (v, max) => {
const pct = Math.round((v/max)*100);
const col = v/max >= 0.8 ? '#16a34a' : v/max >= 0.5 ? '#ca8a04' : '#dc2626';
return `<div style="flex:1;height:4px;border-radius:2px;background:rgba(0,0,0,0.08)"><div style="width:${pct}%;height:100%;background:${col};border-radius:2px"></div></div>`;
return `<div style="flex:1;height:4px;border-radius: var(--radius-sm);background:rgba(0,0,0,0.08)"><div style="width:${pct}%;height:100%;background:${col};border-radius: 2px"></div></div>`;
};
return `<div style="border-top:1px solid rgba(0,0,0,0.08);margin-top:10px;padding-top:10px">
<div style="font-size:9px;font-weight:700;letter-spacing:0.8px;text-transform:uppercase;color:rgba(28,28,30,0.4);margin-bottom:8px">MENTAL HEALTH INTELLIGENCE</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:5px;margin-bottom:8px">
<div style="background:#f2f2f7;border-radius:8px;padding:7px 9px">
<div style="background:#f2f2f7;border-radius: var(--radius-md);padding:7px 9px">
<div style="font-size:9px;color:rgba(28,28,30,0.5);margin-bottom:3px">Depression rate</div>
<div style="font-size:13px;font-weight:800;color:#0A0A0A">${m.depression_pct}%</div>
</div>
<div style="background:#f2f2f7;border-radius:8px;padding:7px 9px">
<div style="background:#f2f2f7;border-radius: var(--radius-md);padding:7px 9px">
<div style="font-size:9px;color:rgba(28,28,30,0.5);margin-bottom:3px">Burnout rate</div>
<div style="font-size:13px;font-weight:800;color:#0A0A0A">${m.burnout_pct}%</div>
</div>
<div style="background:#f2f2f7;border-radius:8px;padding:7px 9px">
<div style="background:#f2f2f7;border-radius: var(--radius-md);padding:7px 9px">
<div style="font-size:9px;color:rgba(28,28,30,0.5);margin-bottom:3px">Suicide /100k</div>
<div style="font-size:13px;font-weight:800;color:${suicide_col}">${m.suicide_per100k}</div>
</div>
<div style="background:#f2f2f7;border-radius:8px;padding:7px 9px">
<div style="background:#f2f2f7;border-radius: var(--radius-md);padding:7px 9px">
<div style="font-size:9px;color:rgba(28,28,30,0.5);margin-bottom:3px">Stigma index</div>
<div style="font-size:13px;font-weight:800;color:${stigma_col}">${m.stigma_index}/100</div>
<div style="font-size:9px;color:rgba(28,28,30,0.35)">Low = accepting</div>
@@ -630,8 +630,8 @@
<span style="color:rgba(28,28,30,0.7);font-weight:700;font-size:11px;width:36px">${m.antidepressant_use_pct}%</span>
</div>
</div>
<div style="background:#f2f2f7;border-radius:7px;padding:7px 9px;font-size:10px;color:rgba(28,28,30,0.65);line-height:1.5;margin-bottom:5px">${m.therapy_access}</div>
<div style="display:flex;align-items:center;gap:6px;background:rgba(220,38,38,0.06);border-radius:6px;padding:6px 9px">
<div style="background:#f2f2f7;border-radius: var(--radius-sm);padding:7px 9px;font-size:10px;color:rgba(28,28,30,0.65);line-height:1.5;margin-bottom:5px">${m.therapy_access}</div>
<div style="display:flex;align-items:center;gap:6px;background:rgba(220,38,38,0.06);border-radius: var(--radius-sm);padding:6px 9px">
<span style="font-size:9px;font-weight:700;color:#dc2626;flex-shrink:0">Crisis line</span>
<span style="font-size:11px;font-weight:700;color:#0A0A0A">${m.crisis_line}</span>
</div>
@@ -655,7 +655,7 @@
const colors = ['#60a5fa','#34d399','#f59e0b','#a78bfa'];
religion_html = `<div style="margin-top:8px">
<div style="font-size:9px;color:rgba(28,28,30,0.45);margin-bottom:5px">Religion (population %)</div>
<div style="display:flex;height:8px;border-radius:4px;overflow:hidden;margin-bottom:4px">
<div style="display:flex;height:8px;border-radius: var(--radius-sm);overflow:hidden;margin-bottom:4px">
${entries.map((e,i)=>`<div style="width:${e[1]}%;background:${colors[i]};"></div>`).join('')}
</div>
<div style="display:flex;flex-wrap:wrap;gap:6px;font-size:9px">
@@ -667,28 +667,28 @@
return `<div style="border-top:1px solid rgba(0,0,0,0.08);margin-top:10px;padding-top:10px">
<div style="font-size:9px;font-weight:700;letter-spacing:0.8px;text-transform:uppercase;color:rgba(28,28,30,0.4);margin-bottom:8px">SOCIAL INTELLIGENCE</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:5px;margin-bottom:8px">
<div style="background:#f2f2f7;border-radius:8px;padding:7px 9px">
<div style="background:#f2f2f7;border-radius: var(--radius-md);padding:7px 9px">
<div style="font-size:9px;color:rgba(28,28,30,0.5);margin-bottom:3px">LGBTQ+ Index</div>
<div style="height:4px;background:rgba(0,0,0,0.08);border-radius:2px;margin-bottom:3px">
<div style="width:${lgbtq_bar_pct}%;height:100%;background:${lgbtq_col};border-radius:2px"></div>
<div style="height:4px;background:rgba(0,0,0,0.08);border-radius: var(--radius-sm);margin-bottom:3px">
<div style="width:${lgbtq_bar_pct}%;height:100%;background:${lgbtq_col};border-radius: 2px"></div>
</div>
<div style="font-size:13px;font-weight:800;color:${lgbtq_col}">${s.lgbtq_index}/100</div>
</div>
<div style="background:#f2f2f7;border-radius:8px;padding:7px 9px">
<div style="background:#f2f2f7;border-radius: var(--radius-md);padding:7px 9px">
<div style="font-size:9px;color:rgba(28,28,30,0.5);margin-bottom:3px">Married %</div>
<div style="font-size:13px;font-weight:800;color:#0A0A0A">${s.married_pct}%</div>
<div style="font-size:9px;color:rgba(28,28,30,0.4)">Divorce ${s.divorce_rate}/1000</div>
</div>
<div style="background:#f2f2f7;border-radius:8px;padding:7px 9px">
<div style="background:#f2f2f7;border-radius: var(--radius-md);padding:7px 9px">
<div style="font-size:9px;color:rgba(28,28,30,0.5);margin-bottom:3px">Same-sex marriage</div>
<div style="font-size:10px;font-weight:700;color:${marriage_col};line-height:1.3">${s.gay_marriage}</div>
</div>
<div style="background:#f2f2f7;border-radius:8px;padding:7px 9px">
<div style="background:#f2f2f7;border-radius: var(--radius-md);padding:7px 9px">
<div style="font-size:9px;color:rgba(28,28,30,0.5);margin-bottom:3px">Foreign-born</div>
<div style="font-size:13px;font-weight:800;color:#0A0A0A">${s.foreign_born_pct}%</div>
</div>
</div>
<div style="background:#f2f2f7;border-radius:8px;padding:7px 9px;font-size:10px;color:rgba(28,28,30,0.65);line-height:1.5;margin-bottom:6px">${s.lgbtq_note}</div>
<div style="background:#f2f2f7;border-radius: var(--radius-md);padding:7px 9px;font-size:10px;color:rgba(28,28,30,0.65);line-height:1.5;margin-bottom:6px">${s.lgbtq_note}</div>
${religion_html}
<div style="font-size:9px;color:rgba(28,28,30,0.3);margin-top:5px">Sources: ${s.social_source||'ILGA World / Eurostat 2025'}</div>
</div>`;
@@ -729,9 +729,9 @@
<span style="width:72px;color:rgba(28,28,30,0.5);flex-shrink:0;font-size:10px;margin-top:1px">Prostitution</span>
<span style="color:#0A0A0A;font-weight:600;font-size:11px;line-height:1.3">${l.prostitution}</span>
</div>` : ''}
${l.prostitution_detail ? `<div style="background:#f2f2f7;border-radius:6px;padding:6px 8px;font-size:9px;color:rgba(28,28,30,0.6);line-height:1.4;margin-top:2px">${l.prostitution_detail}</div>` : ''}
${l.prostitution_detail ? `<div style="background:#f2f2f7;border-radius: var(--radius-sm);padding:6px 8px;font-size:9px;color:rgba(28,28,30,0.6);line-height:1.4;margin-top:2px">${l.prostitution_detail}</div>` : ''}
</div>
${l.notes ? `<div style="background:#f2f2f7;border-radius:7px;padding:7px 9px;font-size:10px;color:rgba(28,28,30,0.65);line-height:1.5">${l.notes}</div>` : ''}
${l.notes ? `<div style="background:#f2f2f7;border-radius: var(--radius-sm);padding:7px 9px;font-size:10px;color:rgba(28,28,30,0.65);line-height:1.5">${l.notes}</div>` : ''}
<div style="font-size:9px;color:rgba(28,28,30,0.3);margin-top:5px">Source: ${p.legal_source||'EMCDDA/UNODC 2025'} · Not legal advice</div>
</div>`;
}
@@ -742,33 +742,33 @@
const bar = (v, max=10) => {
const pct = Math.round((v/max)*100);
const col = v>=8?'#16a34a':v>=6?'#ca8a04':'#dc2626';
return `<div style="flex:1;height:4px;border-radius:2px;background:rgba(0,0,0,0.08)"><div style="width:${pct}%;height:100%;background:${col};border-radius:2px"></div></div>`;
return `<div style="flex:1;height:4px;border-radius: var(--radius-sm);background:rgba(0,0,0,0.08)"><div style="width:${pct}%;height:100%;background:${col};border-radius: 2px"></div></div>`;
};
const cashCol = p.cashless>=90?'#16a34a':p.cashless>=70?'#ca8a04':'#dc2626';
const speedCol = p.speed_mbps>=100?'#16a34a':p.speed_mbps>=40?'#ca8a04':'#dc2626';
const g5Col = p.g5_coverage>=80?'#16a34a':p.g5_coverage>=50?'#ca8a04':'#dc2626';
const restrictHtml = p.vpn_needed
? `<div style="background:rgba(220,38,38,0.08);border:1px solid rgba(220,38,38,0.2);border-radius:6px;padding:6px 9px;margin-top:6px;font-size:10px;color:#991b1b;font-weight:600">VPN REQUIRED — Internet restricted</div>`
: (p.restrictions.length ? `<div style="background:rgba(245,158,11,0.08);border:1px solid rgba(245,158,11,0.2);border-radius:6px;padding:6px 9px;margin-top:6px;font-size:10px;color:#92400e">${p.restrictions.join(' · ')}</div>` : '');
? `<div style="background:rgba(220,38,38,0.08);border:1px solid rgba(220,38,38,0.2);border-radius: var(--radius-sm);padding:6px 9px;margin-top:6px;font-size:10px;color:#991b1b;font-weight:600">VPN REQUIRED — Internet restricted</div>`
: (p.restrictions.length ? `<div style="background:rgba(245,158,11,0.08);border:1px solid rgba(245,158,11,0.2);border-radius: var(--radius-sm);padding:6px 9px;margin-top:6px;font-size:10px;color:#92400e">${p.restrictions.join(' · ')}</div>` : '');
const blockedHtml = p.social_blocked.length
? `<div style="font-size:10px;color:#dc2626;margin-top:4px">Blocked: ${p.social_blocked.join(', ')}</div>` : '';
return `<div style="border-top:1px solid rgba(0,0,0,0.08);margin-top:10px;padding-top:10px;font-family:-apple-system,sans-serif">
<div style="font-size:9px;font-weight:700;letter-spacing:0.8px;text-transform:uppercase;color:rgba(28,28,30,0.4);margin-bottom:8px">PRACTICAL INTELLIGENCE</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:5px;margin-bottom:8px">
<div style="background:#f2f2f7;border-radius:8px;padding:7px 9px">
<div style="background:#f2f2f7;border-radius: var(--radius-md);padding:7px 9px">
<div style="font-size:9px;color:rgba(28,28,30,0.5);margin-bottom:3px">Public transport</div>
${bar(p.transport)}<div style="font-size:13px;font-weight:800;color:#0A0A0A">${p.transport}/10</div>
</div>
<div style="background:#f2f2f7;border-radius:8px;padding:7px 9px">
<div style="background:#f2f2f7;border-radius: var(--radius-md);padding:7px 9px">
<div style="font-size:9px;color:rgba(28,28,30,0.5);margin-bottom:3px">Cashless %</div>
${bar(p.cashless,100)}<div style="font-size:13px;font-weight:800;color:${cashCol}">${p.cashless}%</div>
</div>
<div style="background:#f2f2f7;border-radius:8px;padding:7px 9px">
<div style="background:#f2f2f7;border-radius: var(--radius-md);padding:7px 9px">
<div style="font-size:9px;color:rgba(28,28,30,0.5);margin-bottom:3px">Internet speed</div>
${bar(Math.min(p.speed_mbps/3,10))}<div style="font-size:13px;font-weight:800;color:${speedCol}">${p.speed_mbps} Mbps</div>
</div>
<div style="background:#f2f2f7;border-radius:8px;padding:7px 9px">
<div style="background:#f2f2f7;border-radius: var(--radius-md);padding:7px 9px">
<div style="font-size:9px;color:rgba(28,28,30,0.5);margin-bottom:3px">5G coverage</div>
${bar(p.g5_coverage,100)}<div style="font-size:13px;font-weight:800;color:${g5Col}">${p.g5_coverage}%</div>
</div>
@@ -801,7 +801,7 @@
const status=sl[z.name]||null;
if(!status)return '';
const col=status.startsWith('Available')?'#16a34a':status.startsWith('Unavailable')?'#dc2626':'#ca8a04';
return `<div style="display:flex;align-items:center;justify-content:space-between;background:#f2f2f7;border-radius:8px;padding:7px 10px;margin-top:6px"><div style="font-size:10px;color:rgba(28,28,30,0.5);font-weight:600">Starlink</div><div style="font-size:11px;font-weight:800;color:${col}">${status}</div></div>` + lcsPopup(p) + frictionPopup(p) + legalPopup(p) + socialPopup(p) + mentalHealthPopup(p) + foodPopup(p) + (p.speed_source?`<div style="font-size:9px;color:rgba(28,28,30,0.3);margin-top:5px">Speed: ${p.speed_source}</div>`:'');
return `<div style="display:flex;align-items:center;justify-content:space-between;background:#f2f2f7;border-radius: var(--radius-md);padding:7px 10px;margin-top:6px"><div style="font-size:10px;color:rgba(28,28,30,0.5);font-weight:600">Starlink</div><div style="font-size:11px;font-weight:800;color:${col}">${status}</div></div>` + lcsPopup(p) + frictionPopup(p) + legalPopup(p) + socialPopup(p) + mentalHealthPopup(p) + foodPopup(p) + (p.speed_source?`<div style="font-size:9px;color:rgba(28,28,30,0.3);margin-top:5px">Speed: ${p.speed_source}</div>`:'');
})()}
</div>`;
}
@@ -822,12 +822,12 @@
<div class="zcity">${z.name} <span class="zcountry">· ${z.country}</span></div>
<div class="zconf" style="display:flex;align-items:center;gap:6px">
<span>Confidence ${z.conf}%</span>
${['Stockholm','Bangkok','London','Dubai'].includes(z.name)?'<span style="background:rgba(0,122,255,0.12);color:#007aff;font-size:9px;font-weight:700;padding:1px 7px;border-radius:4px;border:1px solid rgba(0,122,255,0.2)">Deep Dive</span>':''}
${['Stockholm','Bangkok','London','Dubai'].includes(z.name)?'<span style="background:rgba(0,122,255,0.12);color:#007aff;font-size:9px;font-weight:700;padding:1px 7px;border-radius: var(--radius-sm);border:1px solid rgba(0,122,255,0.2)">Deep Dive</span>':''}
</div>
<div class="zsect">Free indices — historical data</div>
<div class="zindices">${freeHtml}</div>
<div class="zcontra ${z.c}"><span class="zcl">${CL[z.c]}</span>${z.ct}</div>
<div class="zage"> Data period: ${z.age}</div>
<div class="zage"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Data period: ${z.age}</div>
<div class="zsect">Live intelligence — on demand</div>
<div class="lock-wrap">
<div class="zindices lock-blur">${livePreview}</div>
@@ -849,7 +849,7 @@
let zg=L.layerGroup().addTo(map),cg=L.layerGroup().addTo(map);
let ls={zones:true,contra:true};
function renderZ(){zg.clearLayers();if(!ls.zones)return;CITIES.forEach(z=>{const st=AS[z.a]||AS.yellow;const pop=zpop(z);const hasDeep=['Stockholm','Bangkok','London','Dubai'].includes(z.name);L.circle([z.lat,z.lng],{radius:z.r||15000,color:hasDeep?'#007aff':st.color,fillColor:st.color,fillOpacity:st.fo,weight:hasDeep?3:st.w,dashArray:st.d}).on("click",function(){if(isMob()){setTimeout(function(){map.closePopup();},10);openBsheet(zpop(z));}}).bindPopup(pop,{maxWidth:300,minWidth:260,autoPan:!isMob()}).addTo(zg);const lbl=L.marker([z.lat,z.lng],{icon:L.divIcon({className:"",html:`<div style="font-size:10px;font-weight:700;color:#0A0A0A;background:rgba(255,255,255,0.95);padding:2px 6px;border-radius:4px;white-space:nowrap;pointer-events:none;box-shadow:0 1px 3px rgba(0,0,0,0.15);border:1px solid rgba(0,0,0,0.1)">${z.name}</div>`,iconAnchor:[0,0]}),interactive:false});lbl.addTo(zg);});}
function renderZ(){zg.clearLayers();if(!ls.zones)return;CITIES.forEach(z=>{const st=AS[z.a]||AS.yellow;const pop=zpop(z);const hasDeep=['Stockholm','Bangkok','London','Dubai'].includes(z.name);L.circle([z.lat,z.lng],{radius:z.r||15000,color:hasDeep?'#007aff':st.color,fillColor:st.color,fillOpacity:st.fo,weight:hasDeep?3:st.w,dashArray:st.d}).on("click",function(){if(isMob()){setTimeout(function(){map.closePopup();},10);openBsheet(zpop(z));}}).bindPopup(pop,{maxWidth:300,minWidth:260,autoPan:!isMob()}).addTo(zg);const lbl=L.marker([z.lat,z.lng],{icon:L.divIcon({className:"",html:`<div style="font-size:10px;font-weight:700;color:#0A0A0A;background:rgba(255,255,255,0.95);padding:2px 6px;border-radius: var(--radius-sm);white-space:nowrap;pointer-events:none;box-shadow:0 1px 3px rgba(0,0,0,0.15);border:1px solid rgba(0,0,0,0.1)">${z.name}</div>`,iconAnchor:[0,0]}),interactive:false});lbl.addTo(zg);});}
function renderC(){cg.clearLayers();if(!ls.contra)return;CITIES.filter(z=>z.c==='high').forEach(z=>{L.circle([z.lat,z.lng],{radius:(z.r||15000)*1.4,color:'#f59e0b',fillColor:'#f59e0b',fillOpacity:0.04,weight:1.5,dashArray:'3 6'}).addTo(cg);});}
loadPracticalData().then(() => { renderZ(); renderC(); });
+21 -21
View File
@@ -36,8 +36,8 @@ nav{background:rgba(0,0,0,0.92);backdrop-filter:blur(20px) saturate(180%);border
.hero-title{font-size:clamp(32px,6vw,52px);font-weight:900;letter-spacing:-1.5px;line-height:1.08;color:var(--text);margin-bottom:18px}
.hero-title span{color:var(--accent3)}
.hero-sub{font-size:17px;color:var(--t2);line-height:1.6;max-width:520px;margin:0 auto 24px}
.hero-badge{display:inline-flex;align-items:center;gap:6px;background:rgba(0,122,255,0.08);border:1px solid rgba(0,122,255,0.18);border-radius:20px;padding:6px 14px;font-size:11px;font-weight:600;color:var(--accent3)}
.hero-badge::before{content:'';width:6px;height:6px;border-radius:50%;background:#22c55e;animation:pulse 1.8s ease-in-out infinite}
.hero-badge{display:inline-flex;align-items:center;gap:6px;background:rgba(0,122,255,0.08);border:1px solid rgba(0,122,255,0.18);border-radius: var(--radius-xl);padding:6px 14px;font-size:11px;font-weight:600;color:var(--accent3)}
.hero-badge::before{content:'';width:6px;height:6px;border-radius: var(--radius-full);background:#22c55e;animation:pulse 1.8s ease-in-out infinite}
@keyframes pulse{0%{opacity:1}50%{opacity:0.4}100%{opacity:1}}
/* CITY GRID */
@@ -47,21 +47,21 @@ nav{background:rgba(0,0,0,0.92);backdrop-filter:blur(20px) saturate(180%);border
.section-count{font-size:12px;color:var(--t3)}
.cities-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:16px}
.city-card{background:var(--s1);border:1px solid var(--border);border-radius:20px;padding:24px;text-decoration:none;color:var(--text);display:block;transition:all 0.15s;position:relative;overflow:hidden}
.city-card{background:var(--s1);border:1px solid var(--border);border-radius: var(--radius-xl);padding:24px;text-decoration:none;color:var(--text);display:block;transition:all 0.15s;position:relative;overflow:hidden}
.city-card:hover{border-color:rgba(0,122,255,0.25);box-shadow:0 8px 32px rgba(0,0,0,0.08);transform:translateY(-2px)}
.city-card::before{content:'';position:absolute;top:0;left:0;right:0;height:3px;border-radius:20px 20px 0 0}
.city-card::before{content:'';position:absolute;top:0;left:0;right:0;height:3px;border-radius: var(--radius-xl) 20px 0 0}
.city-card.sweden::before{background:linear-gradient(90deg,#006aa7,#fecc02)}
.city-card-flag{font-size:32px;margin-bottom:14px;line-height:1}
.city-card-name{font-size:22px;font-weight:900;letter-spacing:-0.6px;color:var(--text);margin-bottom:4px}
.city-card-region{font-size:11px;color:var(--t3);font-weight:500;margin-bottom:16px}
.city-card-stats{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-bottom:20px}
.city-stat{background:var(--s2);border-radius:10px;padding:10px 12px}
.city-stat{background:var(--s2);border-radius: var(--radius-md);padding:10px 12px}
.city-stat-val{font-size:18px;font-weight:800;letter-spacing:-0.3px;line-height:1;margin-bottom:2px}
.city-stat-lbl{font-size:9px;font-weight:600;letter-spacing:0.5px;text-transform:uppercase;color:var(--t3)}
.city-card-tags{display:flex;flex-wrap:wrap;gap:5px;margin-bottom:20px}
.tag{font-size:10px;font-weight:600;padding:3px 9px;border-radius:6px;letter-spacing:0.2px}
.tag{font-size:10px;font-weight:600;padding:3px 9px;border-radius: var(--radius-sm);letter-spacing:0.2px}
.tag-green{background:rgba(22,163,74,0.1);color:#15803d}
.tag-yellow{background:rgba(202,138,4,0.1);color:#92400e}
.tag-red{background:rgba(220,38,38,0.1);color:#991b1b}
@@ -78,9 +78,9 @@ nav{background:rgba(0,0,0,0.92);backdrop-filter:blur(20px) saturate(180%);border
/* METHODOLOGY */
.method-bar{max-width:1100px;margin:0 auto 48px;padding:0 24px}
.method-inner{background:var(--s1);border:1px solid var(--border);border-radius:16px;padding:20px 24px;display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:16px}
.method-inner{background:var(--s1);border:1px solid var(--border);border-radius: var(--radius-lg);padding:20px 24px;display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:16px}
.method-item{display:flex;align-items:flex-start;gap:10px}
.method-icon{width:32px;height:32px;border-radius:8px;background:rgba(0,122,255,0.08);display:flex;align-items:center;justify-content:center;font-size:14px;flex-shrink:0}
.method-icon{width:32px;height:32px;border-radius: var(--radius-md);background:rgba(0,122,255,0.08);display:flex;align-items:center;justify-content:center;font-size:14px;flex-shrink:0}
.method-title{font-size:12px;font-weight:700;color:var(--text);margin-bottom:2px}
.method-sub{font-size:11px;color:var(--t2);line-height:1.4}
@@ -228,7 +228,7 @@ footer a{color:var(--accent3);text-decoration:none}
<!-- STOCKHOLM -->
<a href="/stockholm-intelligence" class="city-card sweden">
<div class="city-card-flag">🇸🇪</div>
<div class="city-card-flag"><span class="flag-se">SE</span></div>
<div class="city-card-name">Stockholm</div>
<div class="city-card-region">Capital · Stockholm County</div>
<div class="city-card-stats">
@@ -251,7 +251,7 @@ footer a{color:var(--accent3);text-decoration:none}
</div>
<div class="city-card-tags">
<span class="tag tag-green">Global coverage</span>
<span class="tag tag-red">RinkebyTensta </span>
<span class="tag tag-red">RinkebyTensta </span>
<span class="tag tag-blue">Hagastaden +91 UMI</span>
<span class="tag tag-green">Östermalm premium</span>
</div>
@@ -263,7 +263,7 @@ footer a{color:var(--accent3);text-decoration:none}
<!-- GÖTEBORG -->
<a href="/goteborg-intelligence" class="city-card sweden">
<div class="city-card-flag"></div>
<div class="city-card-flag"></div>
<div class="city-card-name">Göteborg</div>
<div class="city-card-region">Västra Götaland · Sweden's 2nd City</div>
<div class="city-card-stats">
@@ -286,7 +286,7 @@ footer a{color:var(--accent3);text-decoration:none}
</div>
<div class="city-card-tags">
<span class="tag tag-blue">Mölndal pharma hub</span>
<span class="tag tag-red">Angered HIGH</span>
<span class="tag tag-red">Angered HIGH</span>
<span class="tag tag-green">Linnéstaden +54 UMI</span>
<span class="tag tag-yellow">Avenyn tourism</span>
</div>
@@ -298,7 +298,7 @@ footer a{color:var(--accent3);text-decoration:none}
<!-- MALMÖ -->
<a href="/malmo-intelligence" class="city-card sweden">
<div class="city-card-flag">🌉</div>
<div class="city-card-flag"></div>
<div class="city-card-name">Malmö</div>
<div class="city-card-region">Skåne · Öresund Region</div>
<div class="city-card-stats">
@@ -321,7 +321,7 @@ footer a{color:var(--accent3);text-decoration:none}
</div>
<div class="city-card-tags">
<span class="tag tag-blue">Hyllie → CPH 20 min</span>
<span class="tag tag-red">Rosengård HIGH</span>
<span class="tag tag-red">Rosengård HIGH</span>
<span class="tag tag-green">Västra Hamnen +56</span>
<span class="tag tag-yellow">Turning Torso icon</span>
</div>
@@ -333,7 +333,7 @@ footer a{color:var(--accent3);text-decoration:none}
<!-- UPPSALA -->
<a href="/uppsala-intelligence" class="city-card sweden">
<div class="city-card-flag">🎓</div>
<div class="city-card-flag"></div>
<div class="city-card-name">Uppsala</div>
<div class="city-card-region">Uppsala County · University City</div>
<div class="city-card-stats">
@@ -374,42 +374,42 @@ footer a{color:var(--accent3);text-decoration:none}
<div style="font-size:11px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:var(--t3);margin-bottom:14px">Intelligence Sources</div>
<div class="method-inner">
<div class="method-item">
<div class="method-icon">🗺</div>
<div class="method-icon"></div>
<div>
<div class="method-title">District Scoring</div>
<div class="method-sub">6 profile dimensions per district. Business, Tourist, Safety, Investment, Nightlife, Infrastructure.</div>
</div>
</div>
<div class="method-item">
<div class="method-icon"></div>
<div class="method-icon"></div>
<div>
<div class="method-title">Contradiction Engine</div>
<div class="method-sub">Detects gaps between official data and observed reality. High/Medium/Low classification.</div>
</div>
</div>
<div class="method-item">
<div class="method-icon">🚓</div>
<div class="method-icon"></div>
<div>
<div class="method-title">Live Police Events</div>
<div class="method-sub">Polisen.se API — real-time incident data mapped to districts.</div>
</div>
</div>
<div class="method-item">
<div class="method-icon">📰</div>
<div class="method-icon"></div>
<div>
<div class="method-title">Local News</div>
<div class="method-sub">SVT local feeds for each city — Stockholm, Väst, Skåne, Uppland.</div>
</div>
</div>
<div class="method-item">
<div class="method-icon">📈</div>
<div class="method-icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 12L6 8L9 11L14 5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M10 5H14V9" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></div>
<div>
<div class="method-title">Urban Momentum Index</div>
<div class="method-sub">UMI tracks trajectory. High positive = fast appreciation or development signal.</div>
</div>
</div>
<div class="method-item">
<div class="method-icon">🔬</div>
<div class="method-icon"></div>
<div>
<div class="method-title">Source First</div>
<div class="method-sub">Every data point is sourced and weighted. Confidence score shown per district.</div>
@@ -167,7 +167,7 @@
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social {
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px;
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md);
display: flex; align-items: center; justify-content: center;
transition: border-color 0.2s, background 0.2s;
}
@@ -187,7 +187,7 @@
[style*="grid-template-columns:repeat(auto-fit,minmax(260px"] { grid-template-columns: 1fr !important; }
}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius:8px;line-height:1.6;max-width:800px}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius: var(--radius-md);line-height:1.6;max-width:800px}
.lv-disclaimer a{color:rgba(0,0,0,.65);text-decoration:underline}
.lv-disclaimer--light{color:#888;background:#f5f5f5}
.lv-disclaimer--light a{color:#555}
@@ -263,32 +263,32 @@
</div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(min(300px,100%),1fr));gap:20px">
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px">🏗️</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 14H14M4 14V8L8 4L12 8V14" stroke="#666" stroke-width="1.5" stroke-linejoin="round"/><path d="M6 14V10H10V14" stroke="#666" stroke-width="1.5"/></svg></div>
<h3 style="font-size:1rem;font-weight:700;color:#0a0a0a;margin-bottom:6px">Infrastructure condition</h3>
<p style="font-size:.875rem;color:#6b7280;line-height:1.6">Physical state of roads, utilities, public infrastructure and built environment across the area.</p>
</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px">🏪</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px"></div>
<h3 style="font-size:1rem;font-weight:700;color:#0a0a0a;margin-bottom:6px">Commercial vitality</h3>
<p style="font-size:.875rem;color:#6b7280;line-height:1.6">Density and health of commercial activity — openings, closures, vacancy rates and foot traffic patterns.</p>
</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px">🌳</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px"></div>
<h3 style="font-size:1rem;font-weight:700;color:#0a0a0a;margin-bottom:6px">Public space quality</h3>
<p style="font-size:.875rem;color:#6b7280;line-height:1.6">Condition of parks, squares, pedestrian zones and shared public environments.</p>
</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px">🔧</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px"></div>
<h3 style="font-size:1rem;font-weight:700;color:#0a0a0a;margin-bottom:6px">Maintenance trajectory</h3>
<p style="font-size:.875rem;color:#6b7280;line-height:1.6">Rate of observable maintenance activity vs observed deterioration — leading indicator of future condition.</p>
</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px">📈</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 12L6 8L9 11L14 5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M10 5H14V9" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></div>
<h3 style="font-size:1rem;font-weight:700;color:#0a0a0a;margin-bottom:6px">Change velocity</h3>
<p style="font-size:.875rem;color:#6b7280;line-height:1.6">Speed and direction of visible change across the area — construction, renovation, closures, openings.</p>
</div>
@@ -303,9 +303,9 @@
<div class="section-label">Use cases</div>
<h2 class="section-title section-title-light" style="margin-bottom:24px">Who uses this index.</h2>
<div style="display:flex;flex-wrap:wrap;gap:12px;justify-content:center;margin-bottom:40px">
<span style="background:#e0eaff;color:#0052cc;font-size:.8125rem;font-weight:600;padding:6px 14px;border-radius:100px">Municipal resource allocation</span>
<span style="background:#e0eaff;color:#0052cc;font-size:.8125rem;font-weight:600;padding:6px 14px;border-radius:100px">Urban planning</span>
<span style="background:#e0eaff;color:#0052cc;font-size:.8125rem;font-weight:600;padding:6px 14px;border-radius:100px">Investment screening</span>
<span style="background:#e0eaff;color:#0052cc;font-size:.8125rem;font-weight:600;padding:6px 14px;border-radius: 100px">Municipal resource allocation</span>
<span style="background:#e0eaff;color:#0052cc;font-size:.8125rem;font-weight:600;padding:6px 14px;border-radius: 100px">Urban planning</span>
<span style="background:#e0eaff;color:#0052cc;font-size:.8125rem;font-weight:600;padding:6px 14px;border-radius: 100px">Investment screening</span>
</div>
</div>
</div>
@@ -318,7 +318,7 @@
<div class="section-label">Sample output</div>
<h2 class="section-title" style="margin-bottom:8px">What you receive.</h2>
<p style="color:var(--text-dim);margin-bottom:48px">Score 0100 + trend direction + key signals</p>
<div style="background:var(--surface-2);border:1px solid var(--border);border-radius:16px;padding:36px 40px;text-align:left">
<div style="background:var(--surface-2);border:1px solid var(--border);border-radius: var(--radius-lg);padding:36px 40px;text-align:left">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:8px">
<span style="color:var(--text-dim);font-size:.875rem;font-weight:600">Sample location · Placeholder score</span>
<span style="color:#4ade80;font-size:.875rem;font-weight:700">↑ Improving</span>
@@ -328,8 +328,8 @@
<span style="font-size:1.5rem;color:var(--text-dim)">/100</span>
</div>
<div style="background:#f3f4f6;border-radius:8px;height:12px;width:100%;margin:12px 0 8px;overflow:hidden">
<div style="height:100%;width:72%;background:#4ade80;border-radius:8px;transition:width 1s ease"></div>
<div style="background:#f3f4f6;border-radius: var(--radius-md);height:12px;width:100%;margin:12px 0 8px;overflow:hidden">
<div style="height:100%;width:72%;background:#4ade80;border-radius: var(--radius-md);transition:width 1s ease"></div>
</div>
<div style="display:flex;justify-content:space-between;font-size:.75rem;color:var(--text-dim)">
@@ -352,29 +352,29 @@
<h2 class="section-title">How this index detects narrative conflicts.</h2>
<p style="color:rgba(255,255,255,.75);max-width:600px;margin:0 auto;line-height:1.7">The City Health Index compares official urban condition reports and maintenance schedules against observed field data. When official assessments rate an area as well-maintained but field observations show deferred maintenance, rising vacancies, or deteriorating public spaces — the engine flags a narrative conflict.</p>
</div>
<div style="background:rgba(0,0,0,.05);border:1px solid rgba(0,0,0,.1);border-radius:16px;padding:32px 36px">
<div style="background:rgba(0,0,0,.05);border:1px solid rgba(0,0,0,.1);border-radius: var(--radius-lg);padding:32px 36px">
<div style="display:flex;align-items:flex-start;gap:16px;margin-bottom:24px">
<span style="font-size:1.5rem">🚩</span>
<span style="font-size:1.5rem"></span>
<div>
<div style="color:#f87171;font-weight:800;font-size:.9375rem;margin-bottom:4px">Contradiction Engine — How it works</div>
<div style="color:rgba(255,255,255,.75);font-size:.875rem;line-height:1.6">Field observations are continuously cross-referenced against official records, registers, and published statistics. Divergence above threshold triggers a conflict flag.</div>
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:16px">
<div style="background:rgba(34,197,94,.1);border:1px solid rgba(34,197,94,.2);border-radius:10px;padding:16px">
<div style="background:rgba(34,197,94,.1);border:1px solid rgba(34,197,94,.2);border-radius: var(--radius-md);padding:16px">
<div style="color:#86efac;font-size:.75rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em;margin-bottom:10px">Official record</div>
<ul style="list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:6px">
<li style="color:rgba(255,255,255,.7);font-size:.875rem"> Published condition ratings</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"> Official statistics</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"> Register data</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Published condition ratings</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Official statistics</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Register data</li>
</ul>
</div>
<div style="background:rgba(239,68,68,.1);border:1px solid rgba(239,68,68,.2);border-radius:10px;padding:16px">
<div style="background:rgba(239,68,68,.1);border:1px solid rgba(239,68,68,.2);border-radius: var(--radius-md);padding:16px">
<div style="color:#fca5a5;font-size:.75rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em;margin-bottom:10px">Observed reality</div>
<ul style="list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:6px">
<li style="color:rgba(255,255,255,.7);font-size:.875rem"> Field observations</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"> Real-time signals</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"> Contradiction flags</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Field observations</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Real-time signals</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Contradiction flags</li>
</ul>
</div>
</div>
@@ -392,20 +392,20 @@
</div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(min(260px,100%),1fr));gap:20px;max-width:900px;margin:0 auto">
<a href="/intelligence/neighbourhood-quality-index/" style="display:block;background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:20px 24px;text-decoration:none;color:inherit;transition:border-color .2s,transform .15s" onmouseover="this.style.borderColor='#0066ff';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='#e5e7eb';this.style.transform='none'">
<div style="font-size:1.25rem;margin-bottom:8px">🏘️</div>
<a href="/intelligence/neighbourhood-quality-index/" style="display:block;background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:20px 24px;text-decoration:none;color:inherit;transition:border-color .2s,transform .15s" onmouseover="this.style.borderColor='#0066ff';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='#e5e7eb';this.style.transform='none'">
<div style="font-size:1.25rem;margin-bottom:8px"></div>
<div style="font-weight:700;color:#0a0a0a;font-size:.9375rem;margin-bottom:4px">Neighbourhood Quality Index</div>
<div style="color:#0066ff;font-size:.8125rem;font-weight:700">View index →</div>
</a>
<a href="/intelligence/infrastructure-risk-index/" style="display:block;background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:20px 24px;text-decoration:none;color:inherit;transition:border-color .2s,transform .15s" onmouseover="this.style.borderColor='#0066ff';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='#e5e7eb';this.style.transform='none'">
<a href="/intelligence/infrastructure-risk-index/" style="display:block;background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:20px 24px;text-decoration:none;color:inherit;transition:border-color .2s,transform .15s" onmouseover="this.style.borderColor='#0066ff';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='#e5e7eb';this.style.transform='none'">
<div style="font-size:1.25rem;margin-bottom:8px"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#f59e0b" stroke-width="2"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg></div>
<div style="font-weight:700;color:#0a0a0a;font-size:.9375rem;margin-bottom:4px">Infrastructure Risk Index</div>
<div style="color:#0066ff;font-size:.8125rem;font-weight:700">View index →</div>
</a>
<a href="/intelligence/development-forecast/" style="display:block;background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:20px 24px;text-decoration:none;color:inherit;transition:border-color .2s,transform .15s" onmouseover="this.style.borderColor='#0066ff';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='#e5e7eb';this.style.transform='none'">
<div style="font-size:1.25rem;margin-bottom:8px">🔮</div>
<a href="/intelligence/development-forecast/" style="display:block;background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:20px 24px;text-decoration:none;color:inherit;transition:border-color .2s,transform .15s" onmouseover="this.style.borderColor='#0066ff';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='#e5e7eb';this.style.transform='none'">
<div style="font-size:1.25rem;margin-bottom:8px"></div>
<div style="font-weight:700;color:#0a0a0a;font-size:.9375rem;margin-bottom:4px">Development Forecast</div>
<div style="color:#0066ff;font-size:.8125rem;font-weight:700">View index →</div>
</a>
@@ -503,7 +503,7 @@
});
if (res.ok || res.status === 201) {
msgEl.className = 'success';
msgEl.textContent = " Request received. We'll be in touch within one business day.";
msgEl.textContent = "<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Request received. We'll be in touch within one business day.";
msgEl.style.display = 'block';
this.reset();
} else { throw new Error('Server error ' + res.status); }
@@ -167,7 +167,7 @@
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social {
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px;
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md);
display: flex; align-items: center; justify-content: center;
transition: border-color 0.2s, background 0.2s;
}
@@ -187,7 +187,7 @@
[style*="grid-template-columns:repeat(auto-fit,minmax(260px"] { grid-template-columns: 1fr !important; }
}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius:8px;line-height:1.6;max-width:800px}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius: var(--radius-md);line-height:1.6;max-width:800px}
.lv-disclaimer a{color:rgba(0,0,0,.65);text-decoration:underline}
.lv-disclaimer--light{color:#888;background:#f5f5f5}
.lv-disclaimer--light a{color:#555}
@@ -263,32 +263,32 @@
</div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(min(300px,100%),1fr));gap:20px">
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px">🆕</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px"></div>
<h3 style="font-size:1rem;font-weight:700;color:#0a0a0a;margin-bottom:6px">New business openings</h3>
<p style="font-size:.875rem;color:#6b7280;line-height:1.6">Rate of new commercial openings in the area — by category, size and observed activity level.</p>
</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px">🏚️</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px"></div>
<h3 style="font-size:1rem;font-weight:700;color:#0a0a0a;margin-bottom:6px">Vacancy rates</h3>
<p style="font-size:.875rem;color:#6b7280;line-height:1.6">Observable storefront and commercial vacancy — current level and directional trend over time.</p>
</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px">👥</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px"></div>
<h3 style="font-size:1rem;font-weight:700;color:#0a0a0a;margin-bottom:6px">Foot traffic signals</h3>
<p style="font-size:.875rem;color:#6b7280;line-height:1.6">Pedestrian density and movement patterns observed in the field across different times and zones.</p>
</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px">🏢</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="3" y="2" width="10" height="12" rx="1" stroke="#666" stroke-width="1.5"/><path d="M6 5H7M6 8H7M6 11H7M9 5H10M9 8H10M9 11H10" stroke="#666" stroke-width="1.5" stroke-linecap="round"/></svg></div>
<h3 style="font-size:1rem;font-weight:700;color:#0a0a0a;margin-bottom:6px">Competitor presence</h3>
<p style="font-size:.875rem;color:#6b7280;line-height:1.6">Category density, anchor tenants, brand presence and competitive clustering by district.</p>
</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px">🔨</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px"></div>
<h3 style="font-size:1rem;font-weight:700;color:#0a0a0a;margin-bottom:6px">Renovation activity</h3>
<p style="font-size:.875rem;color:#6b7280;line-height:1.6">Volume of active fitouts, renovations and commercial improvements — a leading signal of investment intent.</p>
</div>
@@ -303,9 +303,9 @@
<div class="section-label">Use cases</div>
<h2 class="section-title section-title-light" style="margin-bottom:24px">Who uses this index.</h2>
<div style="display:flex;flex-wrap:wrap;gap:12px;justify-content:center;margin-bottom:40px">
<span style="background:#e0eaff;color:#0052cc;font-size:.8125rem;font-weight:600;padding:6px 14px;border-radius:100px">Retail site selection</span>
<span style="background:#e0eaff;color:#0052cc;font-size:.8125rem;font-weight:600;padding:6px 14px;border-radius:100px">Franchise expansion</span>
<span style="background:#e0eaff;color:#0052cc;font-size:.8125rem;font-weight:600;padding:6px 14px;border-radius:100px">Property acquisition</span>
<span style="background:#e0eaff;color:#0052cc;font-size:.8125rem;font-weight:600;padding:6px 14px;border-radius: 100px">Retail site selection</span>
<span style="background:#e0eaff;color:#0052cc;font-size:.8125rem;font-weight:600;padding:6px 14px;border-radius: 100px">Franchise expansion</span>
<span style="background:#e0eaff;color:#0052cc;font-size:.8125rem;font-weight:600;padding:6px 14px;border-radius: 100px">Property acquisition</span>
</div>
</div>
</div>
@@ -318,7 +318,7 @@
<div class="section-label">Sample output</div>
<h2 class="section-title" style="margin-bottom:8px">What you receive.</h2>
<p style="color:var(--text-dim);margin-bottom:48px">Opportunity score + gap map + competitor density</p>
<div style="background:var(--surface-2);border:1px solid var(--border);border-radius:16px;padding:36px 40px;text-align:left">
<div style="background:var(--surface-2);border:1px solid var(--border);border-radius: var(--radius-lg);padding:36px 40px;text-align:left">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:8px">
<span style="color:var(--text-dim);font-size:.875rem;font-weight:600">Sample location · Placeholder score</span>
<span style="color:#4ade80;font-size:.875rem;font-weight:700">↑ Strong growth</span>
@@ -328,8 +328,8 @@
<span style="font-size:1.5rem;color:var(--text-dim)">/100</span>
</div>
<div style="background:#f3f4f6;border-radius:8px;height:12px;width:100%;margin:12px 0 8px;overflow:hidden">
<div style="height:100%;width:81%;background:#4ade80;border-radius:8px;transition:width 1s ease"></div>
<div style="background:#f3f4f6;border-radius: var(--radius-md);height:12px;width:100%;margin:12px 0 8px;overflow:hidden">
<div style="height:100%;width:81%;background:#4ade80;border-radius: var(--radius-md);transition:width 1s ease"></div>
</div>
<div style="display:flex;justify-content:space-between;font-size:.75rem;color:var(--text-dim)">
@@ -352,29 +352,29 @@
<h2 class="section-title">How this index detects narrative conflicts.</h2>
<p style="color:rgba(255,255,255,.75);max-width:600px;margin:0 auto;line-height:1.7">The Commercial Opportunity Index cross-references property registers, planning applications and official business statistics against observed commercial activity on the ground. A location listed as commercially active but observed with rising vacancies and declining foot traffic triggers a contradiction alert — often weeks before it surfaces in official data.</p>
</div>
<div style="background:rgba(0,0,0,.05);border:1px solid rgba(0,0,0,.1);border-radius:16px;padding:32px 36px">
<div style="background:rgba(0,0,0,.05);border:1px solid rgba(0,0,0,.1);border-radius: var(--radius-lg);padding:32px 36px">
<div style="display:flex;align-items:flex-start;gap:16px;margin-bottom:24px">
<span style="font-size:1.5rem">🚩</span>
<span style="font-size:1.5rem"></span>
<div>
<div style="color:#f87171;font-weight:800;font-size:.9375rem;margin-bottom:4px">Contradiction Engine — How it works</div>
<div style="color:rgba(255,255,255,.75);font-size:.875rem;line-height:1.6">Field observations are continuously cross-referenced against official records, registers, and published statistics. Divergence above threshold triggers a conflict flag.</div>
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:16px">
<div style="background:rgba(34,197,94,.1);border:1px solid rgba(34,197,94,.2);border-radius:10px;padding:16px">
<div style="background:rgba(34,197,94,.1);border:1px solid rgba(34,197,94,.2);border-radius: var(--radius-md);padding:16px">
<div style="color:#86efac;font-size:.75rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em;margin-bottom:10px">Official record</div>
<ul style="list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:6px">
<li style="color:rgba(255,255,255,.7);font-size:.875rem"> Published condition ratings</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"> Official statistics</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"> Register data</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Published condition ratings</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Official statistics</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Register data</li>
</ul>
</div>
<div style="background:rgba(239,68,68,.1);border:1px solid rgba(239,68,68,.2);border-radius:10px;padding:16px">
<div style="background:rgba(239,68,68,.1);border:1px solid rgba(239,68,68,.2);border-radius: var(--radius-md);padding:16px">
<div style="color:#fca5a5;font-size:.75rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em;margin-bottom:10px">Observed reality</div>
<ul style="list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:6px">
<li style="color:rgba(255,255,255,.7);font-size:.875rem"> Field observations</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"> Real-time signals</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"> Contradiction flags</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Field observations</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Real-time signals</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Contradiction flags</li>
</ul>
</div>
</div>
@@ -392,20 +392,20 @@
</div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(min(260px,100%),1fr));gap:20px;max-width:900px;margin:0 auto">
<a href="/intelligence/urban-growth-index/" style="display:block;background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:20px 24px;text-decoration:none;color:inherit;transition:border-color .2s,transform .15s" onmouseover="this.style.borderColor='#0066ff';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='#e5e7eb';this.style.transform='none'">
<div style="font-size:1.25rem;margin-bottom:8px">🌱</div>
<a href="/intelligence/urban-growth-index/" style="display:block;background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:20px 24px;text-decoration:none;color:inherit;transition:border-color .2s,transform .15s" onmouseover="this.style.borderColor='#0066ff';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='#e5e7eb';this.style.transform='none'">
<div style="font-size:1.25rem;margin-bottom:8px"></div>
<div style="font-weight:700;color:#0a0a0a;font-size:.9375rem;margin-bottom:4px">Urban Growth Index</div>
<div style="color:#0066ff;font-size:.8125rem;font-weight:700">View index →</div>
</a>
<a href="/intelligence/investment-attractiveness-index/" style="display:block;background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:20px 24px;text-decoration:none;color:inherit;transition:border-color .2s,transform .15s" onmouseover="this.style.borderColor='#0066ff';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='#e5e7eb';this.style.transform='none'">
<div style="font-size:1.25rem;margin-bottom:8px">💎</div>
<a href="/intelligence/investment-attractiveness-index/" style="display:block;background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:20px 24px;text-decoration:none;color:inherit;transition:border-color .2s,transform .15s" onmouseover="this.style.borderColor='#0066ff';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='#e5e7eb';this.style.transform='none'">
<div style="font-size:1.25rem;margin-bottom:8px"></div>
<div style="font-weight:700;color:#0a0a0a;font-size:.9375rem;margin-bottom:4px">Investment Attractiveness Index</div>
<div style="color:#0066ff;font-size:.8125rem;font-weight:700">View index →</div>
</a>
<a href="/intelligence/growth-velocity-index/" style="display:block;background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:20px 24px;text-decoration:none;color:inherit;transition:border-color .2s,transform .15s" onmouseover="this.style.borderColor='#0066ff';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='#e5e7eb';this.style.transform='none'">
<div style="font-size:1.25rem;margin-bottom:8px">🚀</div>
<a href="/intelligence/growth-velocity-index/" style="display:block;background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:20px 24px;text-decoration:none;color:inherit;transition:border-color .2s,transform .15s" onmouseover="this.style.borderColor='#0066ff';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='#e5e7eb';this.style.transform='none'">
<div style="font-size:1.25rem;margin-bottom:8px"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M8 2L10 8L14 9L8 14L6 8L2 7L8 2Z" fill="#666"/></svg></div>
<div style="font-weight:700;color:#0a0a0a;font-size:.9375rem;margin-bottom:4px">Growth Velocity Index</div>
<div style="color:#0066ff;font-size:.8125rem;font-weight:700">View index →</div>
</a>
@@ -503,7 +503,7 @@
});
if (res.ok || res.status === 201) {
msgEl.className = 'success';
msgEl.textContent = " Request received. We'll be in touch within one business day.";
msgEl.textContent = "<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Request received. We'll be in touch within one business day.";
msgEl.style.display = 'block';
this.reset();
} else { throw new Error('Server error ' + res.status); }
@@ -167,7 +167,7 @@
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social {
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px;
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md);
display: flex; align-items: center; justify-content: center;
transition: border-color 0.2s, background 0.2s;
}
@@ -187,7 +187,7 @@
[style*="grid-template-columns:repeat(auto-fit,minmax(260px"] { grid-template-columns: 1fr !important; }
}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius:8px;line-height:1.6;max-width:800px}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius: var(--radius-md);line-height:1.6;max-width:800px}
.lv-disclaimer a{color:rgba(0,0,0,.65);text-decoration:underline}
.lv-disclaimer--light{color:#888;background:#f5f5f5}
.lv-disclaimer--light a{color:#555}
@@ -263,32 +263,32 @@
</div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(min(300px,100%),1fr));gap:20px">
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px">🏗️</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 14H14M4 14V8L8 4L12 8V14" stroke="#666" stroke-width="1.5" stroke-linejoin="round"/><path d="M6 14V10H10V14" stroke="#666" stroke-width="1.5"/></svg></div>
<h3 style="font-size:1rem;font-weight:700;color:#0a0a0a;margin-bottom:6px">Construction activity</h3>
<p style="font-size:.875rem;color:#6b7280;line-height:1.6">Volume and intensity of active construction, scaffolding, site preparation and demolition observed in the field.</p>
</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px">📄</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px"></div>
<h3 style="font-size:1rem;font-weight:700;color:#0a0a0a;margin-bottom:6px">Permit signals</h3>
<p style="font-size:.875rem;color:#6b7280;line-height:1.6">Observable evidence of planning permissions — site hoardings, notices, contractor presence and groundworks.</p>
</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px">🏪</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px"></div>
<h3 style="font-size:1rem;font-weight:700;color:#0a0a0a;margin-bottom:6px">Commercial openings</h3>
<p style="font-size:.875rem;color:#6b7280;line-height:1.6">New business formations and commercial fitouts as a leading indicator of investment confidence.</p>
</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px">💰</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px"></div>
<h3 style="font-size:1rem;font-weight:700;color:#0a0a0a;margin-bottom:6px">Capital flows</h3>
<p style="font-size:.875rem;color:#6b7280;line-height:1.6">Indirect signals of capital allocation — scaffolding, premium renovations, branded development sites.</p>
</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px">🚶</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px"></div>
<h3 style="font-size:1rem;font-weight:700;color:#0a0a0a;margin-bottom:6px">Population movement</h3>
<p style="font-size:.875rem;color:#6b7280;line-height:1.6">Observable demographic shifts — new residents, changing consumer mix, service infrastructure evolution.</p>
</div>
@@ -303,9 +303,9 @@
<div class="section-label">Use cases</div>
<h2 class="section-title section-title-light" style="margin-bottom:24px">Who uses this index.</h2>
<div style="display:flex;flex-wrap:wrap;gap:12px;justify-content:center;margin-bottom:40px">
<span style="background:#e0eaff;color:#0052cc;font-size:.8125rem;font-weight:600;padding:6px 14px;border-radius:100px">Real estate development</span>
<span style="background:#e0eaff;color:#0052cc;font-size:.8125rem;font-weight:600;padding:6px 14px;border-radius:100px">Investment allocation</span>
<span style="background:#e0eaff;color:#0052cc;font-size:.8125rem;font-weight:600;padding:6px 14px;border-radius:100px">Urban planning</span>
<span style="background:#e0eaff;color:#0052cc;font-size:.8125rem;font-weight:600;padding:6px 14px;border-radius: 100px">Real estate development</span>
<span style="background:#e0eaff;color:#0052cc;font-size:.8125rem;font-weight:600;padding:6px 14px;border-radius: 100px">Investment allocation</span>
<span style="background:#e0eaff;color:#0052cc;font-size:.8125rem;font-weight:600;padding:6px 14px;border-radius: 100px">Urban planning</span>
</div>
</div>
</div>
@@ -318,7 +318,7 @@
<div class="section-label">Sample output</div>
<h2 class="section-title" style="margin-bottom:8px">What you receive.</h2>
<p style="color:var(--text-dim);margin-bottom:48px">Development score + cycle stage + investment confidence</p>
<div style="background:var(--surface-2);border:1px solid var(--border);border-radius:16px;padding:36px 40px;text-align:left">
<div style="background:var(--surface-2);border:1px solid var(--border);border-radius: var(--radius-lg);padding:36px 40px;text-align:left">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:8px">
<span style="color:var(--text-dim);font-size:.875rem;font-weight:600">Sample location · Placeholder score</span>
<span style="color:#4ade80;font-size:.875rem;font-weight:700">↑ Cycle accelerating</span>
@@ -328,8 +328,8 @@
<span style="font-size:1.5rem;color:var(--text-dim)">/100</span>
</div>
<div style="background:#f3f4f6;border-radius:8px;height:12px;width:100%;margin:12px 0 8px;overflow:hidden">
<div style="height:100%;width:67%;background:#fbbf24;border-radius:8px;transition:width 1s ease"></div>
<div style="background:#f3f4f6;border-radius: var(--radius-md);height:12px;width:100%;margin:12px 0 8px;overflow:hidden">
<div style="height:100%;width:67%;background:#fbbf24;border-radius: var(--radius-md);transition:width 1s ease"></div>
</div>
<div style="display:flex;justify-content:space-between;font-size:.75rem;color:var(--text-dim)">
@@ -352,29 +352,29 @@
<h2 class="section-title">How this index detects narrative conflicts.</h2>
<p style="color:rgba(255,255,255,.75);max-width:600px;margin:0 auto;line-height:1.7">Development Forecast detects when official planning data diverges from observed ground activity. A district listed as "under development" in permits with no observable site activity, or conversely, rapid development activity ahead of formal permissions — both trigger narrative conflicts that change the forecast score.</p>
</div>
<div style="background:rgba(0,0,0,.05);border:1px solid rgba(0,0,0,.1);border-radius:16px;padding:32px 36px">
<div style="background:rgba(0,0,0,.05);border:1px solid rgba(0,0,0,.1);border-radius: var(--radius-lg);padding:32px 36px">
<div style="display:flex;align-items:flex-start;gap:16px;margin-bottom:24px">
<span style="font-size:1.5rem">🚩</span>
<span style="font-size:1.5rem"></span>
<div>
<div style="color:#f87171;font-weight:800;font-size:.9375rem;margin-bottom:4px">Contradiction Engine — How it works</div>
<div style="color:rgba(255,255,255,.75);font-size:.875rem;line-height:1.6">Field observations are continuously cross-referenced against official records, registers, and published statistics. Divergence above threshold triggers a conflict flag.</div>
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:16px">
<div style="background:rgba(34,197,94,.1);border:1px solid rgba(34,197,94,.2);border-radius:10px;padding:16px">
<div style="background:rgba(34,197,94,.1);border:1px solid rgba(34,197,94,.2);border-radius: var(--radius-md);padding:16px">
<div style="color:#86efac;font-size:.75rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em;margin-bottom:10px">Official record</div>
<ul style="list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:6px">
<li style="color:rgba(255,255,255,.7);font-size:.875rem"> Published condition ratings</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"> Official statistics</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"> Register data</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Published condition ratings</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Official statistics</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Register data</li>
</ul>
</div>
<div style="background:rgba(239,68,68,.1);border:1px solid rgba(239,68,68,.2);border-radius:10px;padding:16px">
<div style="background:rgba(239,68,68,.1);border:1px solid rgba(239,68,68,.2);border-radius: var(--radius-md);padding:16px">
<div style="color:#fca5a5;font-size:.75rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em;margin-bottom:10px">Observed reality</div>
<ul style="list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:6px">
<li style="color:rgba(255,255,255,.7);font-size:.875rem"> Field observations</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"> Real-time signals</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"> Contradiction flags</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Field observations</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Real-time signals</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Contradiction flags</li>
</ul>
</div>
</div>
@@ -392,20 +392,20 @@
</div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(min(260px,100%),1fr));gap:20px;max-width:900px;margin:0 auto">
<a href="/intelligence/urban-growth-index/" style="display:block;background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:20px 24px;text-decoration:none;color:inherit;transition:border-color .2s,transform .15s" onmouseover="this.style.borderColor='#0066ff';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='#e5e7eb';this.style.transform='none'">
<div style="font-size:1.25rem;margin-bottom:8px">🌱</div>
<a href="/intelligence/urban-growth-index/" style="display:block;background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:20px 24px;text-decoration:none;color:inherit;transition:border-color .2s,transform .15s" onmouseover="this.style.borderColor='#0066ff';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='#e5e7eb';this.style.transform='none'">
<div style="font-size:1.25rem;margin-bottom:8px"></div>
<div style="font-weight:700;color:#0a0a0a;font-size:.9375rem;margin-bottom:4px">Urban Growth Index</div>
<div style="color:#0066ff;font-size:.8125rem;font-weight:700">View index →</div>
</a>
<a href="/intelligence/investment-attractiveness-index/" style="display:block;background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:20px 24px;text-decoration:none;color:inherit;transition:border-color .2s,transform .15s" onmouseover="this.style.borderColor='#0066ff';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='#e5e7eb';this.style.transform='none'">
<div style="font-size:1.25rem;margin-bottom:8px">💎</div>
<a href="/intelligence/investment-attractiveness-index/" style="display:block;background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:20px 24px;text-decoration:none;color:inherit;transition:border-color .2s,transform .15s" onmouseover="this.style.borderColor='#0066ff';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='#e5e7eb';this.style.transform='none'">
<div style="font-size:1.25rem;margin-bottom:8px"></div>
<div style="font-weight:700;color:#0a0a0a;font-size:.9375rem;margin-bottom:4px">Investment Attractiveness Index</div>
<div style="color:#0066ff;font-size:.8125rem;font-weight:700">View index →</div>
</a>
<a href="/intelligence/commercial-opportunity-index/" style="display:block;background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:20px 24px;text-decoration:none;color:inherit;transition:border-color .2s,transform .15s" onmouseover="this.style.borderColor='#0066ff';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='#e5e7eb';this.style.transform='none'">
<div style="font-size:1.25rem;margin-bottom:8px">📈</div>
<a href="/intelligence/commercial-opportunity-index/" style="display:block;background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:20px 24px;text-decoration:none;color:inherit;transition:border-color .2s,transform .15s" onmouseover="this.style.borderColor='#0066ff';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='#e5e7eb';this.style.transform='none'">
<div style="font-size:1.25rem;margin-bottom:8px"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 12L6 8L9 11L14 5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M10 5H14V9" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></div>
<div style="font-weight:700;color:#0a0a0a;font-size:.9375rem;margin-bottom:4px">Commercial Opportunity Index</div>
<div style="color:#0066ff;font-size:.8125rem;font-weight:700">View index →</div>
</a>
@@ -503,7 +503,7 @@
});
if (res.ok || res.status === 201) {
msgEl.className = 'success';
msgEl.textContent = " Request received. We'll be in touch within one business day.";
msgEl.textContent = "<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Request received. We'll be in touch within one business day.";
msgEl.style.display = 'block';
this.reset();
} else { throw new Error('Server error ' + res.status); }
@@ -167,7 +167,7 @@
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social {
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px;
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md);
display: flex; align-items: center; justify-content: center;
transition: border-color 0.2s, background 0.2s;
}
@@ -187,7 +187,7 @@
[style*="grid-template-columns:repeat(auto-fit,minmax(260px"] { grid-template-columns: 1fr !important; }
}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius:8px;line-height:1.6;max-width:800px}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius: var(--radius-md);line-height:1.6;max-width:800px}
.lv-disclaimer a{color:rgba(0,0,0,.65);text-decoration:underline}
.lv-disclaimer--light{color:#888;background:#f5f5f5}
.lv-disclaimer--light a{color:#555}
@@ -263,32 +263,32 @@
</div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(min(300px,100%),1fr));gap:20px">
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px">🏪</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px"></div>
<h3 style="font-size:1rem;font-weight:700;color:#0a0a0a;margin-bottom:6px">Change rate (commercial)</h3>
<p style="font-size:.875rem;color:#6b7280;line-height:1.6">Speed of commercial turnover — openings, closures, category shifts and brand mix changes.</p>
</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px">🏗️</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 14H14M4 14V8L8 4L12 8V14" stroke="#666" stroke-width="1.5" stroke-linejoin="round"/><path d="M6 14V10H10V14" stroke="#666" stroke-width="1.5"/></svg></div>
<h3 style="font-size:1rem;font-weight:700;color:#0a0a0a;margin-bottom:6px">Change rate (physical)</h3>
<p style="font-size:.875rem;color:#6b7280;line-height:1.6">Rate of physical transformation — construction, demolition, renovation and public realm works.</p>
</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#0066FF" stroke-width="2"><path d="M18 20V10"/><path d="M12 20V4"/><path d="M6 20v-6"/></svg></div>
<h3 style="font-size:1rem;font-weight:700;color:#0a0a0a;margin-bottom:6px">Trend acceleration</h3>
<p style="font-size:.875rem;color:#6b7280;line-height:1.6">Whether the rate of change is itself accelerating or decelerating — a second-order velocity signal.</p>
</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px">📉</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px"></div>
<h3 style="font-size:1rem;font-weight:700;color:#0a0a0a;margin-bottom:6px">Volatility score</h3>
<p style="font-size:.875rem;color:#6b7280;line-height:1.6">Consistency of change direction — high volatility means rapid oscillation between positive and negative signals.</p>
</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px">🚀</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M8 2L10 8L14 9L8 14L6 8L2 7L8 2Z" fill="#666"/></svg></div>
<h3 style="font-size:1rem;font-weight:700;color:#0a0a0a;margin-bottom:6px">Momentum indicators</h3>
<p style="font-size:.875rem;color:#6b7280;line-height:1.6">Leading signals that suggest the current change rate will continue — or is about to reverse.</p>
</div>
@@ -303,9 +303,9 @@
<div class="section-label">Use cases</div>
<h2 class="section-title section-title-light" style="margin-bottom:24px">Who uses this index.</h2>
<div style="display:flex;flex-wrap:wrap;gap:12px;justify-content:center;margin-bottom:40px">
<span style="background:#e0eaff;color:#0052cc;font-size:.8125rem;font-weight:600;padding:6px 14px;border-radius:100px">Timing investment decisions</span>
<span style="background:#e0eaff;color:#0052cc;font-size:.8125rem;font-weight:600;padding:6px 14px;border-radius:100px">Early market entry</span>
<span style="background:#e0eaff;color:#0052cc;font-size:.8125rem;font-weight:600;padding:6px 14px;border-radius:100px">Risk-adjusted expansion</span>
<span style="background:#e0eaff;color:#0052cc;font-size:.8125rem;font-weight:600;padding:6px 14px;border-radius: 100px">Timing investment decisions</span>
<span style="background:#e0eaff;color:#0052cc;font-size:.8125rem;font-weight:600;padding:6px 14px;border-radius: 100px">Early market entry</span>
<span style="background:#e0eaff;color:#0052cc;font-size:.8125rem;font-weight:600;padding:6px 14px;border-radius: 100px">Risk-adjusted expansion</span>
</div>
</div>
</div>
@@ -318,7 +318,7 @@
<div class="section-label">Sample output</div>
<h2 class="section-title" style="margin-bottom:8px">What you receive.</h2>
<p style="color:var(--text-dim);margin-bottom:48px">Velocity score + acceleration signal + volatility rating</p>
<div style="background:var(--surface-2);border:1px solid var(--border);border-radius:16px;padding:36px 40px;text-align:left">
<div style="background:var(--surface-2);border:1px solid var(--border);border-radius: var(--radius-lg);padding:36px 40px;text-align:left">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:8px">
<span style="color:var(--text-dim);font-size:.875rem;font-weight:600">Sample location · Placeholder score</span>
<span style="color:#4ade80;font-size:.875rem;font-weight:700">↑ Accelerating</span>
@@ -328,8 +328,8 @@
<span style="font-size:1.5rem;color:var(--text-dim)">/100</span>
</div>
<div style="background:#f3f4f6;border-radius:8px;height:12px;width:100%;margin:12px 0 8px;overflow:hidden">
<div style="height:100%;width:83%;background:#4ade80;border-radius:8px;transition:width 1s ease"></div>
<div style="background:#f3f4f6;border-radius: var(--radius-md);height:12px;width:100%;margin:12px 0 8px;overflow:hidden">
<div style="height:100%;width:83%;background:#4ade80;border-radius: var(--radius-md);transition:width 1s ease"></div>
</div>
<div style="display:flex;justify-content:space-between;font-size:.75rem;color:var(--text-dim)">
@@ -352,29 +352,29 @@
<h2 class="section-title">How this index detects narrative conflicts.</h2>
<p style="color:rgba(255,255,255,.75);max-width:600px;margin:0 auto;line-height:1.7">Growth Velocity Index detects when official economic release cycles lag observable reality. GDP and business register data typically reflects conditions 618 months in the past. When field velocity indicators diverge sharply from the most recent official statistics, the contradiction engine quantifies the gap and adjusts the forward-looking velocity forecast.</p>
</div>
<div style="background:rgba(0,0,0,.05);border:1px solid rgba(0,0,0,.1);border-radius:16px;padding:32px 36px">
<div style="background:rgba(0,0,0,.05);border:1px solid rgba(0,0,0,.1);border-radius: var(--radius-lg);padding:32px 36px">
<div style="display:flex;align-items:flex-start;gap:16px;margin-bottom:24px">
<span style="font-size:1.5rem">🚩</span>
<span style="font-size:1.5rem"></span>
<div>
<div style="color:#f87171;font-weight:800;font-size:.9375rem;margin-bottom:4px">Contradiction Engine — How it works</div>
<div style="color:rgba(255,255,255,.75);font-size:.875rem;line-height:1.6">Field observations are continuously cross-referenced against official records, registers, and published statistics. Divergence above threshold triggers a conflict flag.</div>
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:16px">
<div style="background:rgba(34,197,94,.1);border:1px solid rgba(34,197,94,.2);border-radius:10px;padding:16px">
<div style="background:rgba(34,197,94,.1);border:1px solid rgba(34,197,94,.2);border-radius: var(--radius-md);padding:16px">
<div style="color:#86efac;font-size:.75rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em;margin-bottom:10px">Official record</div>
<ul style="list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:6px">
<li style="color:rgba(255,255,255,.7);font-size:.875rem"> Published condition ratings</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"> Official statistics</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"> Register data</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Published condition ratings</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Official statistics</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Register data</li>
</ul>
</div>
<div style="background:rgba(239,68,68,.1);border:1px solid rgba(239,68,68,.2);border-radius:10px;padding:16px">
<div style="background:rgba(239,68,68,.1);border:1px solid rgba(239,68,68,.2);border-radius: var(--radius-md);padding:16px">
<div style="color:#fca5a5;font-size:.75rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em;margin-bottom:10px">Observed reality</div>
<ul style="list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:6px">
<li style="color:rgba(255,255,255,.7);font-size:.875rem"> Field observations</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"> Real-time signals</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"> Contradiction flags</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Field observations</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Real-time signals</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Contradiction flags</li>
</ul>
</div>
</div>
@@ -392,20 +392,20 @@
</div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(min(260px,100%),1fr));gap:20px;max-width:900px;margin:0 auto">
<a href="/intelligence/urban-growth-index/" style="display:block;background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:20px 24px;text-decoration:none;color:inherit;transition:border-color .2s,transform .15s" onmouseover="this.style.borderColor='#0066ff';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='#e5e7eb';this.style.transform='none'">
<div style="font-size:1.25rem;margin-bottom:8px">🌱</div>
<a href="/intelligence/urban-growth-index/" style="display:block;background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:20px 24px;text-decoration:none;color:inherit;transition:border-color .2s,transform .15s" onmouseover="this.style.borderColor='#0066ff';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='#e5e7eb';this.style.transform='none'">
<div style="font-size:1.25rem;margin-bottom:8px"></div>
<div style="font-weight:700;color:#0a0a0a;font-size:.9375rem;margin-bottom:4px">Urban Growth Index</div>
<div style="color:#0066ff;font-size:.8125rem;font-weight:700">View index →</div>
</a>
<a href="/intelligence/investment-attractiveness-index/" style="display:block;background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:20px 24px;text-decoration:none;color:inherit;transition:border-color .2s,transform .15s" onmouseover="this.style.borderColor='#0066ff';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='#e5e7eb';this.style.transform='none'">
<div style="font-size:1.25rem;margin-bottom:8px">💎</div>
<a href="/intelligence/investment-attractiveness-index/" style="display:block;background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:20px 24px;text-decoration:none;color:inherit;transition:border-color .2s,transform .15s" onmouseover="this.style.borderColor='#0066ff';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='#e5e7eb';this.style.transform='none'">
<div style="font-size:1.25rem;margin-bottom:8px"></div>
<div style="font-weight:700;color:#0a0a0a;font-size:.9375rem;margin-bottom:4px">Investment Attractiveness Index</div>
<div style="color:#0066ff;font-size:.8125rem;font-weight:700">View index →</div>
</a>
<a href="/intelligence/commercial-opportunity-index/" style="display:block;background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:20px 24px;text-decoration:none;color:inherit;transition:border-color .2s,transform .15s" onmouseover="this.style.borderColor='#0066ff';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='#e5e7eb';this.style.transform='none'">
<div style="font-size:1.25rem;margin-bottom:8px">📈</div>
<a href="/intelligence/commercial-opportunity-index/" style="display:block;background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:20px 24px;text-decoration:none;color:inherit;transition:border-color .2s,transform .15s" onmouseover="this.style.borderColor='#0066ff';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='#e5e7eb';this.style.transform='none'">
<div style="font-size:1.25rem;margin-bottom:8px"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 12L6 8L9 11L14 5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M10 5H14V9" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></div>
<div style="font-weight:700;color:#0a0a0a;font-size:.9375rem;margin-bottom:4px">Commercial Opportunity Index</div>
<div style="color:#0066ff;font-size:.8125rem;font-weight:700">View index →</div>
</a>
@@ -503,7 +503,7 @@
});
if (res.ok || res.status === 201) {
msgEl.className = 'success';
msgEl.textContent = " Request received. We'll be in touch within one business day.";
msgEl.textContent = "<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Request received. We'll be in touch within one business day.";
msgEl.style.display = 'block';
this.reset();
} else { throw new Error('Server error ' + res.status); }
@@ -167,7 +167,7 @@
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social {
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px;
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md);
display: flex; align-items: center; justify-content: center;
transition: border-color 0.2s, background 0.2s;
}
@@ -187,7 +187,7 @@
[style*="grid-template-columns:repeat(auto-fit,minmax(260px"] { grid-template-columns: 1fr !important; }
}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius:8px;line-height:1.6;max-width:800px}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius: var(--radius-md);line-height:1.6;max-width:800px}
.lv-disclaimer a{color:rgba(0,0,0,.65);text-decoration:underline}
.lv-disclaimer--light{color:#888;background:#f5f5f5}
.lv-disclaimer--light a{color:#555}
@@ -263,32 +263,32 @@
</div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(min(300px,100%),1fr));gap:20px">
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px">📉</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px"></div>
<h3 style="font-size:1rem;font-weight:700;color:#0a0a0a;margin-bottom:6px">Observed deterioration rate</h3>
<p style="font-size:.875rem;color:#6b7280;line-height:1.6">Speed of visible degradation across roads, bridges, utilities and public infrastructure assets.</p>
</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px">🔧</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px"></div>
<h3 style="font-size:1rem;font-weight:700;color:#0a0a0a;margin-bottom:6px">Maintenance gap</h3>
<p style="font-size:.875rem;color:#6b7280;line-height:1.6">Delta between required maintenance frequency and observed maintenance activity in the field.</p>
</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px">📋</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="3" y="2" width="10" height="12" rx="1" stroke="#666" stroke-width="1.5"/><path d="M6 5H10M6 8H10M6 11H8" stroke="#666" stroke-width="1.5" stroke-linecap="round"/></svg></div>
<h3 style="font-size:1rem;font-weight:700;color:#0a0a0a;margin-bottom:6px">Official vs observed delta</h3>
<p style="font-size:.875rem;color:#6b7280;line-height:1.6">Divergence between official condition ratings and field observations — the key contradiction signal.</p>
</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px"></div>
<h3 style="font-size:1rem;font-weight:700;color:#0a0a0a;margin-bottom:6px">Age-adjusted risk</h3>
<p style="font-size:.875rem;color:#6b7280;line-height:1.6">Deterioration rate adjusted for asset age and original condition — identifies assets aging faster than expected.</p>
</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px">💥</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px"></div>
<h3 style="font-size:1rem;font-weight:700;color:#0a0a0a;margin-bottom:6px">Failure probability</h3>
<p style="font-size:.875rem;color:#6b7280;line-height:1.6">Composite probability score of near-term infrastructure failure based on all observed signals.</p>
</div>
@@ -303,9 +303,9 @@
<div class="section-label">Use cases</div>
<h2 class="section-title section-title-light" style="margin-bottom:24px">Who uses this index.</h2>
<div style="display:flex;flex-wrap:wrap;gap:12px;justify-content:center;margin-bottom:40px">
<span style="background:#e0eaff;color:#0052cc;font-size:.8125rem;font-weight:600;padding:6px 14px;border-radius:100px">Maintenance prioritization</span>
<span style="background:#e0eaff;color:#0052cc;font-size:.8125rem;font-weight:600;padding:6px 14px;border-radius:100px">Capex planning</span>
<span style="background:#e0eaff;color:#0052cc;font-size:.8125rem;font-weight:600;padding:6px 14px;border-radius:100px">Insurance underwriting</span>
<span style="background:#e0eaff;color:#0052cc;font-size:.8125rem;font-weight:600;padding:6px 14px;border-radius: 100px">Maintenance prioritization</span>
<span style="background:#e0eaff;color:#0052cc;font-size:.8125rem;font-weight:600;padding:6px 14px;border-radius: 100px">Capex planning</span>
<span style="background:#e0eaff;color:#0052cc;font-size:.8125rem;font-weight:600;padding:6px 14px;border-radius: 100px">Insurance underwriting</span>
</div>
</div>
</div>
@@ -318,7 +318,7 @@
<div class="section-label">Sample output</div>
<h2 class="section-title" style="margin-bottom:8px">What you receive.</h2>
<p style="color:var(--text-dim);margin-bottom:48px">Risk score + failure probability + priority ranking</p>
<div style="background:var(--surface-2);border:1px solid var(--border);border-radius:16px;padding:36px 40px;text-align:left">
<div style="background:var(--surface-2);border:1px solid var(--border);border-radius: var(--radius-lg);padding:36px 40px;text-align:left">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:8px">
<span style="color:var(--text-dim);font-size:.875rem;font-weight:600">Sample location · Placeholder score</span>
<span style="color:#f87171;font-size:.875rem;font-weight:700">↓ Risk increasing</span>
@@ -328,8 +328,8 @@
<span style="font-size:1.5rem;color:var(--text-dim)">/100</span>
</div>
<div style="background:#f3f4f6;border-radius:8px;height:12px;width:100%;margin:12px 0 8px;overflow:hidden">
<div style="height:100%;width:44%;background:#f87171;border-radius:8px;transition:width 1s ease"></div>
<div style="background:#f3f4f6;border-radius: var(--radius-md);height:12px;width:100%;margin:12px 0 8px;overflow:hidden">
<div style="height:100%;width:44%;background:#f87171;border-radius: var(--radius-md);transition:width 1s ease"></div>
</div>
<div style="display:flex;justify-content:space-between;font-size:.75rem;color:var(--text-dim)">
@@ -352,29 +352,29 @@
<h2 class="section-title">How this index detects narrative conflicts.</h2>
<p style="color:rgba(255,255,255,.75);max-width:600px;margin:0 auto;line-height:1.7">Infrastructure Risk Index is built on contradiction detection. It systematically compares official maintenance records, inspection reports and asset condition databases against observed reality. When a bridge or road corridor is rated "good" in official systems but field observations show accelerating deterioration — the contradiction score rises, triggering priority review.</p>
</div>
<div style="background:rgba(0,0,0,.05);border:1px solid rgba(0,0,0,.1);border-radius:16px;padding:32px 36px">
<div style="background:rgba(0,0,0,.05);border:1px solid rgba(0,0,0,.1);border-radius: var(--radius-lg);padding:32px 36px">
<div style="display:flex;align-items:flex-start;gap:16px;margin-bottom:24px">
<span style="font-size:1.5rem">🚩</span>
<span style="font-size:1.5rem"></span>
<div>
<div style="color:#f87171;font-weight:800;font-size:.9375rem;margin-bottom:4px">Contradiction Engine — How it works</div>
<div style="color:rgba(255,255,255,.75);font-size:.875rem;line-height:1.6">Field observations are continuously cross-referenced against official records, registers, and published statistics. Divergence above threshold triggers a conflict flag.</div>
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:16px">
<div style="background:rgba(34,197,94,.1);border:1px solid rgba(34,197,94,.2);border-radius:10px;padding:16px">
<div style="background:rgba(34,197,94,.1);border:1px solid rgba(34,197,94,.2);border-radius: var(--radius-md);padding:16px">
<div style="color:#86efac;font-size:.75rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em;margin-bottom:10px">Official record</div>
<ul style="list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:6px">
<li style="color:rgba(255,255,255,.7);font-size:.875rem"> Published condition ratings</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"> Official statistics</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"> Register data</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Published condition ratings</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Official statistics</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Register data</li>
</ul>
</div>
<div style="background:rgba(239,68,68,.1);border:1px solid rgba(239,68,68,.2);border-radius:10px;padding:16px">
<div style="background:rgba(239,68,68,.1);border:1px solid rgba(239,68,68,.2);border-radius: var(--radius-md);padding:16px">
<div style="color:#fca5a5;font-size:.75rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em;margin-bottom:10px">Observed reality</div>
<ul style="list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:6px">
<li style="color:rgba(255,255,255,.7);font-size:.875rem"> Field observations</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"> Real-time signals</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"> Contradiction flags</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Field observations</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Real-time signals</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Contradiction flags</li>
</ul>
</div>
</div>
@@ -392,20 +392,20 @@
</div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(min(260px,100%),1fr));gap:20px;max-width:900px;margin:0 auto">
<a href="/intelligence/city-health-index/" style="display:block;background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:20px 24px;text-decoration:none;color:inherit;transition:border-color .2s,transform .15s" onmouseover="this.style.borderColor='#0066ff';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='#e5e7eb';this.style.transform='none'">
<div style="font-size:1.25rem;margin-bottom:8px">🏙️</div>
<a href="/intelligence/city-health-index/" style="display:block;background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:20px 24px;text-decoration:none;color:inherit;transition:border-color .2s,transform .15s" onmouseover="this.style.borderColor='#0066ff';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='#e5e7eb';this.style.transform='none'">
<div style="font-size:1.25rem;margin-bottom:8px"></div>
<div style="font-weight:700;color:#0a0a0a;font-size:.9375rem;margin-bottom:4px">City Health Index</div>
<div style="color:#0066ff;font-size:.8125rem;font-weight:700">View index →</div>
</a>
<a href="/intelligence/risk-resilience-profile/" style="display:block;background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:20px 24px;text-decoration:none;color:inherit;transition:border-color .2s,transform .15s" onmouseover="this.style.borderColor='#0066ff';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='#e5e7eb';this.style.transform='none'">
<div style="font-size:1.25rem;margin-bottom:8px">🛡️</div>
<a href="/intelligence/risk-resilience-profile/" style="display:block;background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:20px 24px;text-decoration:none;color:inherit;transition:border-color .2s,transform .15s" onmouseover="this.style.borderColor='#0066ff';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='#e5e7eb';this.style.transform='none'">
<div style="font-size:1.25rem;margin-bottom:8px"></div>
<div style="font-weight:700;color:#0a0a0a;font-size:.9375rem;margin-bottom:4px">Risk & Resilience Profile</div>
<div style="color:#0066ff;font-size:.8125rem;font-weight:700">View index →</div>
</a>
<a href="/intelligence/development-forecast/" style="display:block;background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:20px 24px;text-decoration:none;color:inherit;transition:border-color .2s,transform .15s" onmouseover="this.style.borderColor='#0066ff';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='#e5e7eb';this.style.transform='none'">
<div style="font-size:1.25rem;margin-bottom:8px">🔮</div>
<a href="/intelligence/development-forecast/" style="display:block;background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:20px 24px;text-decoration:none;color:inherit;transition:border-color .2s,transform .15s" onmouseover="this.style.borderColor='#0066ff';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='#e5e7eb';this.style.transform='none'">
<div style="font-size:1.25rem;margin-bottom:8px"></div>
<div style="font-weight:700;color:#0a0a0a;font-size:.9375rem;margin-bottom:4px">Development Forecast</div>
<div style="color:#0066ff;font-size:.8125rem;font-weight:700">View index →</div>
</a>
@@ -503,7 +503,7 @@
});
if (res.ok || res.status === 201) {
msgEl.className = 'success';
msgEl.textContent = " Request received. We'll be in touch within one business day.";
msgEl.textContent = "<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Request received. We'll be in touch within one business day.";
msgEl.style.display = 'block';
this.reset();
} else { throw new Error('Server error ' + res.status); }
@@ -167,7 +167,7 @@
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social {
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px;
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md);
display: flex; align-items: center; justify-content: center;
transition: border-color 0.2s, background 0.2s;
}
@@ -187,7 +187,7 @@
[style*="grid-template-columns:repeat(auto-fit,minmax(260px"] { grid-template-columns: 1fr !important; }
}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius:8px;line-height:1.6;max-width:800px}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius: var(--radius-md);line-height:1.6;max-width:800px}
.lv-disclaimer a{color:rgba(0,0,0,.65);text-decoration:underline}
.lv-disclaimer--light{color:#888;background:#f5f5f5}
.lv-disclaimer--light a{color:#555}
@@ -263,32 +263,32 @@
</div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(min(300px,100%),1fr));gap:20px">
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px">💡</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px"></div>
<h3 style="font-size:1rem;font-weight:700;color:#0a0a0a;margin-bottom:6px">Opportunity score</h3>
<p style="font-size:.875rem;color:#6b7280;line-height:1.6">Composite signal of commercial opportunity, underserved demand and competitive positioning.</p>
</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#0066FF" stroke-width="2"><path d="M18 20V10"/><path d="M12 20V4"/><path d="M6 20v-6"/></svg></div>
<h3 style="font-size:1rem;font-weight:700;color:#0a0a0a;margin-bottom:6px">Risk-adjusted return potential</h3>
<p style="font-size:.875rem;color:#6b7280;line-height:1.6">Expected return potential weighted against observed risk signals and contradiction indicators.</p>
</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px">📈</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 12L6 8L9 11L14 5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M10 5H14V9" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></div>
<h3 style="font-size:1rem;font-weight:700;color:#0a0a0a;margin-bottom:6px">Growth trajectory</h3>
<p style="font-size:.875rem;color:#6b7280;line-height:1.6">Direction and velocity of area growth — observed leading indicators of future value creation.</p>
</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px"></div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M8 2V4M8 12V14M2 8H4M12 8H14" stroke="#f59e0b" stroke-width="1.5" stroke-linecap="round"/><circle cx="8" cy="8" r="2" fill="#f59e0b"/></svg></div>
<h3 style="font-size:1rem;font-weight:700;color:#0a0a0a;margin-bottom:6px">Quality of physical environment</h3>
<p style="font-size:.875rem;color:#6b7280;line-height:1.6">Condition, upkeep and observable quality of the built environment — a proxy for long-term value retention.</p>
</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px">⚔️</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px"></div>
<h3 style="font-size:1rem;font-weight:700;color:#0a0a0a;margin-bottom:6px">Competitive intensity</h3>
<p style="font-size:.875rem;color:#6b7280;line-height:1.6">Observed competitive activity — existing investment presence and new entrant signals in the area.</p>
</div>
@@ -303,9 +303,9 @@
<div class="section-label">Use cases</div>
<h2 class="section-title section-title-light" style="margin-bottom:24px">Who uses this index.</h2>
<div style="display:flex;flex-wrap:wrap;gap:12px;justify-content:center;margin-bottom:40px">
<span style="background:#e0eaff;color:#0052cc;font-size:.8125rem;font-weight:600;padding:6px 14px;border-radius:100px">Capital allocation</span>
<span style="background:#e0eaff;color:#0052cc;font-size:.8125rem;font-weight:600;padding:6px 14px;border-radius:100px">Site selection</span>
<span style="background:#e0eaff;color:#0052cc;font-size:.8125rem;font-weight:600;padding:6px 14px;border-radius:100px">Portfolio construction</span>
<span style="background:#e0eaff;color:#0052cc;font-size:.8125rem;font-weight:600;padding:6px 14px;border-radius: 100px">Capital allocation</span>
<span style="background:#e0eaff;color:#0052cc;font-size:.8125rem;font-weight:600;padding:6px 14px;border-radius: 100px">Site selection</span>
<span style="background:#e0eaff;color:#0052cc;font-size:.8125rem;font-weight:600;padding:6px 14px;border-radius: 100px">Portfolio construction</span>
</div>
</div>
</div>
@@ -318,7 +318,7 @@
<div class="section-label">Sample output</div>
<h2 class="section-title" style="margin-bottom:8px">What you receive.</h2>
<p style="color:var(--text-dim);margin-bottom:48px">Attractiveness score + risk-adjusted rank + comparable benchmark</p>
<div style="background:var(--surface-2);border:1px solid var(--border);border-radius:16px;padding:36px 40px;text-align:left">
<div style="background:var(--surface-2);border:1px solid var(--border);border-radius: var(--radius-lg);padding:36px 40px;text-align:left">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:8px">
<span style="color:var(--text-dim);font-size:.875rem;font-weight:600">Sample location · Placeholder score</span>
<span style="color:#4ade80;font-size:.875rem;font-weight:700">↑ Improving</span>
@@ -328,8 +328,8 @@
<span style="font-size:1.5rem;color:var(--text-dim)">/100</span>
</div>
<div style="background:#f3f4f6;border-radius:8px;height:12px;width:100%;margin:12px 0 8px;overflow:hidden">
<div style="height:100%;width:76%;background:#4ade80;border-radius:8px;transition:width 1s ease"></div>
<div style="background:#f3f4f6;border-radius: var(--radius-md);height:12px;width:100%;margin:12px 0 8px;overflow:hidden">
<div style="height:100%;width:76%;background:#4ade80;border-radius: var(--radius-md);transition:width 1s ease"></div>
</div>
<div style="display:flex;justify-content:space-between;font-size:.75rem;color:var(--text-dim)">
@@ -352,29 +352,29 @@
<h2 class="section-title">How this index detects narrative conflicts.</h2>
<p style="color:rgba(255,255,255,.75);max-width:600px;margin:0 auto;line-height:1.7">Investment Attractiveness Index combines all contradiction signals from across the index family into a single composite score. When multiple indexes show divergence between official data and observed reality, the investment risk-adjustment factor rises — reducing the attractiveness score to reflect information uncertainty in the official record.</p>
</div>
<div style="background:rgba(0,0,0,.05);border:1px solid rgba(0,0,0,.1);border-radius:16px;padding:32px 36px">
<div style="background:rgba(0,0,0,.05);border:1px solid rgba(0,0,0,.1);border-radius: var(--radius-lg);padding:32px 36px">
<div style="display:flex;align-items:flex-start;gap:16px;margin-bottom:24px">
<span style="font-size:1.5rem">🚩</span>
<span style="font-size:1.5rem"></span>
<div>
<div style="color:#f87171;font-weight:800;font-size:.9375rem;margin-bottom:4px">Contradiction Engine — How it works</div>
<div style="color:rgba(255,255,255,.75);font-size:.875rem;line-height:1.6">Field observations are continuously cross-referenced against official records, registers, and published statistics. Divergence above threshold triggers a conflict flag.</div>
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:16px">
<div style="background:rgba(34,197,94,.1);border:1px solid rgba(34,197,94,.2);border-radius:10px;padding:16px">
<div style="background:rgba(34,197,94,.1);border:1px solid rgba(34,197,94,.2);border-radius: var(--radius-md);padding:16px">
<div style="color:#86efac;font-size:.75rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em;margin-bottom:10px">Official record</div>
<ul style="list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:6px">
<li style="color:rgba(255,255,255,.7);font-size:.875rem"> Published condition ratings</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"> Official statistics</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"> Register data</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Published condition ratings</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Official statistics</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Register data</li>
</ul>
</div>
<div style="background:rgba(239,68,68,.1);border:1px solid rgba(239,68,68,.2);border-radius:10px;padding:16px">
<div style="background:rgba(239,68,68,.1);border:1px solid rgba(239,68,68,.2);border-radius: var(--radius-md);padding:16px">
<div style="color:#fca5a5;font-size:.75rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em;margin-bottom:10px">Observed reality</div>
<ul style="list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:6px">
<li style="color:rgba(255,255,255,.7);font-size:.875rem"> Field observations</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"> Real-time signals</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"> Contradiction flags</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Field observations</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Real-time signals</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Contradiction flags</li>
</ul>
</div>
</div>
@@ -392,20 +392,20 @@
</div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(min(260px,100%),1fr));gap:20px;max-width:900px;margin:0 auto">
<a href="/intelligence/commercial-opportunity-index/" style="display:block;background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:20px 24px;text-decoration:none;color:inherit;transition:border-color .2s,transform .15s" onmouseover="this.style.borderColor='#0066ff';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='#e5e7eb';this.style.transform='none'">
<div style="font-size:1.25rem;margin-bottom:8px">📈</div>
<a href="/intelligence/commercial-opportunity-index/" style="display:block;background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:20px 24px;text-decoration:none;color:inherit;transition:border-color .2s,transform .15s" onmouseover="this.style.borderColor='#0066ff';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='#e5e7eb';this.style.transform='none'">
<div style="font-size:1.25rem;margin-bottom:8px"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 12L6 8L9 11L14 5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M10 5H14V9" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></div>
<div style="font-weight:700;color:#0a0a0a;font-size:.9375rem;margin-bottom:4px">Commercial Opportunity Index</div>
<div style="color:#0066ff;font-size:.8125rem;font-weight:700">View index →</div>
</a>
<a href="/intelligence/growth-velocity-index/" style="display:block;background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:20px 24px;text-decoration:none;color:inherit;transition:border-color .2s,transform .15s" onmouseover="this.style.borderColor='#0066ff';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='#e5e7eb';this.style.transform='none'">
<div style="font-size:1.25rem;margin-bottom:8px">🚀</div>
<a href="/intelligence/growth-velocity-index/" style="display:block;background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:20px 24px;text-decoration:none;color:inherit;transition:border-color .2s,transform .15s" onmouseover="this.style.borderColor='#0066ff';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='#e5e7eb';this.style.transform='none'">
<div style="font-size:1.25rem;margin-bottom:8px"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M8 2L10 8L14 9L8 14L6 8L2 7L8 2Z" fill="#666"/></svg></div>
<div style="font-weight:700;color:#0a0a0a;font-size:.9375rem;margin-bottom:4px">Growth Velocity Index</div>
<div style="color:#0066ff;font-size:.8125rem;font-weight:700">View index →</div>
</a>
<a href="/intelligence/risk-resilience-profile/" style="display:block;background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:20px 24px;text-decoration:none;color:inherit;transition:border-color .2s,transform .15s" onmouseover="this.style.borderColor='#0066ff';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='#e5e7eb';this.style.transform='none'">
<div style="font-size:1.25rem;margin-bottom:8px">🛡️</div>
<a href="/intelligence/risk-resilience-profile/" style="display:block;background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:20px 24px;text-decoration:none;color:inherit;transition:border-color .2s,transform .15s" onmouseover="this.style.borderColor='#0066ff';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='#e5e7eb';this.style.transform='none'">
<div style="font-size:1.25rem;margin-bottom:8px"></div>
<div style="font-weight:700;color:#0a0a0a;font-size:.9375rem;margin-bottom:4px">Risk & Resilience Profile</div>
<div style="color:#0066ff;font-size:.8125rem;font-weight:700">View index →</div>
</a>
@@ -503,7 +503,7 @@
});
if (res.ok || res.status === 201) {
msgEl.className = 'success';
msgEl.textContent = " Request received. We'll be in touch within one business day.";
msgEl.textContent = "<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Request received. We'll be in touch within one business day.";
msgEl.style.display = 'block';
this.reset();
} else { throw new Error('Server error ' + res.status); }
@@ -167,7 +167,7 @@
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social {
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px;
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md);
display: flex; align-items: center; justify-content: center;
transition: border-color 0.2s, background 0.2s;
}
@@ -187,7 +187,7 @@
[style*="grid-template-columns:repeat(auto-fit,minmax(260px"] { grid-template-columns: 1fr !important; }
}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius:8px;line-height:1.6;max-width:800px}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius: var(--radius-md);line-height:1.6;max-width:800px}
.lv-disclaimer a{color:rgba(0,0,0,.65);text-decoration:underline}
.lv-disclaimer--light{color:#888;background:#f5f5f5}
.lv-disclaimer--light a{color:#555}
@@ -263,32 +263,32 @@
</div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(min(300px,100%),1fr));gap:20px">
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px">🏠</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px"></div>
<h3 style="font-size:1rem;font-weight:700;color:#0a0a0a;margin-bottom:6px">Physical condition score</h3>
<p style="font-size:.875rem;color:#6b7280;line-height:1.6">Observable condition of buildings, streetscapes, public spaces and shared infrastructure.</p>
</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px"></div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M9 2L5 9H8L7 14L11 7H8L9 2Z" fill="#f59e0b"/></svg></div>
<h3 style="font-size:1rem;font-weight:700;color:#0a0a0a;margin-bottom:6px">Activity level</h3>
<p style="font-size:.875rem;color:#6b7280;line-height:1.6">Density and vibrancy of observable activity — pedestrians, commercial activity, events, services.</p>
</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#00C853" stroke-width="3"><path d="M20 6L9 17l-5-5"/></svg></div>
<h3 style="font-size:1rem;font-weight:700;color:#0a0a0a;margin-bottom:6px">Maintenance quality</h3>
<p style="font-size:.875rem;color:#6b7280;line-height:1.6">Evidence of active upkeep — street cleaning, repairs, landscaping, graffiti removal and general care.</p>
</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px">🔒</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px"></div>
<h3 style="font-size:1rem;font-weight:700;color:#0a0a0a;margin-bottom:6px">Safety signals</h3>
<p style="font-size:.875rem;color:#6b7280;line-height:1.6">Observable environmental safety indicators — lighting quality, surveillance, footfall density and activation.</p>
</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px">🔄</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 8C2 11.3 4.7 14 8 14C10.5 14 12.7 12.5 13.7 10.3" stroke="#666" stroke-width="1.5" stroke-linecap="round"/><path d="M14 8C14 4.7 11.3 2 8 2C5.5 2 3.3 3.5 2.3 5.7" stroke="#666" stroke-width="1.5" stroke-linecap="round"/><path d="M13.5 7.5L14 10L11.5 9.5" stroke="#666" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M2.5 8.5L2 6L4.5 6.5" stroke="#666" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg></div>
<h3 style="font-size:1rem;font-weight:700;color:#0a0a0a;margin-bottom:6px">Change direction</h3>
<p style="font-size:.875rem;color:#6b7280;line-height:1.6">Whether the area is improving or declining — trajectory from field observations over time.</p>
</div>
@@ -303,9 +303,9 @@
<div class="section-label">Use cases</div>
<h2 class="section-title section-title-light" style="margin-bottom:24px">Who uses this index.</h2>
<div style="display:flex;flex-wrap:wrap;gap:12px;justify-content:center;margin-bottom:40px">
<span style="background:#e0eaff;color:#0052cc;font-size:.8125rem;font-weight:600;padding:6px 14px;border-radius:100px">Residential development</span>
<span style="background:#e0eaff;color:#0052cc;font-size:.8125rem;font-weight:600;padding:6px 14px;border-radius:100px">Retail positioning</span>
<span style="background:#e0eaff;color:#0052cc;font-size:.8125rem;font-weight:600;padding:6px 14px;border-radius:100px">Public space investment</span>
<span style="background:#e0eaff;color:#0052cc;font-size:.8125rem;font-weight:600;padding:6px 14px;border-radius: 100px">Residential development</span>
<span style="background:#e0eaff;color:#0052cc;font-size:.8125rem;font-weight:600;padding:6px 14px;border-radius: 100px">Retail positioning</span>
<span style="background:#e0eaff;color:#0052cc;font-size:.8125rem;font-weight:600;padding:6px 14px;border-radius: 100px">Public space investment</span>
</div>
</div>
</div>
@@ -318,7 +318,7 @@
<div class="section-label">Sample output</div>
<h2 class="section-title" style="margin-bottom:8px">What you receive.</h2>
<p style="color:var(--text-dim);margin-bottom:48px">Quality score + experience rating + trajectory signal</p>
<div style="background:var(--surface-2);border:1px solid var(--border);border-radius:16px;padding:36px 40px;text-align:left">
<div style="background:var(--surface-2);border:1px solid var(--border);border-radius: var(--radius-lg);padding:36px 40px;text-align:left">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:8px">
<span style="color:var(--text-dim);font-size:.875rem;font-weight:600">Sample location · Placeholder score</span>
<span style="color:#fbbf24;font-size:.875rem;font-weight:700">→ Stable</span>
@@ -328,8 +328,8 @@
<span style="font-size:1.5rem;color:var(--text-dim)">/100</span>
</div>
<div style="background:#f3f4f6;border-radius:8px;height:12px;width:100%;margin:12px 0 8px;overflow:hidden">
<div style="height:100%;width:69%;background:#fbbf24;border-radius:8px;transition:width 1s ease"></div>
<div style="background:#f3f4f6;border-radius: var(--radius-md);height:12px;width:100%;margin:12px 0 8px;overflow:hidden">
<div style="height:100%;width:69%;background:#fbbf24;border-radius: var(--radius-md);transition:width 1s ease"></div>
</div>
<div style="display:flex;justify-content:space-between;font-size:.75rem;color:var(--text-dim)">
@@ -352,29 +352,29 @@
<h2 class="section-title">How this index detects narrative conflicts.</h2>
<p style="color:rgba(255,255,255,.75);max-width:600px;margin:0 auto;line-height:1.7">Neighbourhood Quality Index flags when resident satisfaction surveys, official quality-of-life indexes or planning documents describe an area differently from what field observations record. High survey satisfaction scores in areas with observed maintenance decline often precede a quality drop — the index detects this divergence early.</p>
</div>
<div style="background:rgba(0,0,0,.05);border:1px solid rgba(0,0,0,.1);border-radius:16px;padding:32px 36px">
<div style="background:rgba(0,0,0,.05);border:1px solid rgba(0,0,0,.1);border-radius: var(--radius-lg);padding:32px 36px">
<div style="display:flex;align-items:flex-start;gap:16px;margin-bottom:24px">
<span style="font-size:1.5rem">🚩</span>
<span style="font-size:1.5rem"></span>
<div>
<div style="color:#f87171;font-weight:800;font-size:.9375rem;margin-bottom:4px">Contradiction Engine — How it works</div>
<div style="color:rgba(255,255,255,.75);font-size:.875rem;line-height:1.6">Field observations are continuously cross-referenced against official records, registers, and published statistics. Divergence above threshold triggers a conflict flag.</div>
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:16px">
<div style="background:rgba(34,197,94,.1);border:1px solid rgba(34,197,94,.2);border-radius:10px;padding:16px">
<div style="background:rgba(34,197,94,.1);border:1px solid rgba(34,197,94,.2);border-radius: var(--radius-md);padding:16px">
<div style="color:#86efac;font-size:.75rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em;margin-bottom:10px">Official record</div>
<ul style="list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:6px">
<li style="color:rgba(255,255,255,.7);font-size:.875rem"> Published condition ratings</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"> Official statistics</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"> Register data</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Published condition ratings</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Official statistics</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Register data</li>
</ul>
</div>
<div style="background:rgba(239,68,68,.1);border:1px solid rgba(239,68,68,.2);border-radius:10px;padding:16px">
<div style="background:rgba(239,68,68,.1);border:1px solid rgba(239,68,68,.2);border-radius: var(--radius-md);padding:16px">
<div style="color:#fca5a5;font-size:.75rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em;margin-bottom:10px">Observed reality</div>
<ul style="list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:6px">
<li style="color:rgba(255,255,255,.7);font-size:.875rem"> Field observations</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"> Real-time signals</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"> Contradiction flags</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Field observations</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Real-time signals</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Contradiction flags</li>
</ul>
</div>
</div>
@@ -392,20 +392,20 @@
</div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(min(260px,100%),1fr));gap:20px;max-width:900px;margin:0 auto">
<a href="/intelligence/city-health-index/" style="display:block;background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:20px 24px;text-decoration:none;color:inherit;transition:border-color .2s,transform .15s" onmouseover="this.style.borderColor='#0066ff';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='#e5e7eb';this.style.transform='none'">
<div style="font-size:1.25rem;margin-bottom:8px">🏙️</div>
<a href="/intelligence/city-health-index/" style="display:block;background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:20px 24px;text-decoration:none;color:inherit;transition:border-color .2s,transform .15s" onmouseover="this.style.borderColor='#0066ff';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='#e5e7eb';this.style.transform='none'">
<div style="font-size:1.25rem;margin-bottom:8px"></div>
<div style="font-weight:700;color:#0a0a0a;font-size:.9375rem;margin-bottom:4px">City Health Index</div>
<div style="color:#0066ff;font-size:.8125rem;font-weight:700">View index →</div>
</a>
<a href="/intelligence/commercial-opportunity-index/" style="display:block;background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:20px 24px;text-decoration:none;color:inherit;transition:border-color .2s,transform .15s" onmouseover="this.style.borderColor='#0066ff';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='#e5e7eb';this.style.transform='none'">
<div style="font-size:1.25rem;margin-bottom:8px">📈</div>
<a href="/intelligence/commercial-opportunity-index/" style="display:block;background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:20px 24px;text-decoration:none;color:inherit;transition:border-color .2s,transform .15s" onmouseover="this.style.borderColor='#0066ff';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='#e5e7eb';this.style.transform='none'">
<div style="font-size:1.25rem;margin-bottom:8px"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 12L6 8L9 11L14 5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M10 5H14V9" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></div>
<div style="font-weight:700;color:#0a0a0a;font-size:.9375rem;margin-bottom:4px">Commercial Opportunity Index</div>
<div style="color:#0066ff;font-size:.8125rem;font-weight:700">View index →</div>
</a>
<a href="/intelligence/risk-resilience-profile/" style="display:block;background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:20px 24px;text-decoration:none;color:inherit;transition:border-color .2s,transform .15s" onmouseover="this.style.borderColor='#0066ff';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='#e5e7eb';this.style.transform='none'">
<div style="font-size:1.25rem;margin-bottom:8px">🛡️</div>
<a href="/intelligence/risk-resilience-profile/" style="display:block;background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:20px 24px;text-decoration:none;color:inherit;transition:border-color .2s,transform .15s" onmouseover="this.style.borderColor='#0066ff';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='#e5e7eb';this.style.transform='none'">
<div style="font-size:1.25rem;margin-bottom:8px"></div>
<div style="font-weight:700;color:#0a0a0a;font-size:.9375rem;margin-bottom:4px">Risk & Resilience Profile</div>
<div style="color:#0066ff;font-size:.8125rem;font-weight:700">View index →</div>
</a>
@@ -503,7 +503,7 @@
});
if (res.ok || res.status === 201) {
msgEl.className = 'success';
msgEl.textContent = " Request received. We'll be in touch within one business day.";
msgEl.textContent = "<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Request received. We'll be in touch within one business day.";
msgEl.style.display = 'block';
this.reset();
} else { throw new Error('Server error ' + res.status); }
@@ -167,7 +167,7 @@
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social {
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px;
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md);
display: flex; align-items: center; justify-content: center;
transition: border-color 0.2s, background 0.2s;
}
@@ -187,7 +187,7 @@
[style*="grid-template-columns:repeat(auto-fit,minmax(260px"] { grid-template-columns: 1fr !important; }
}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius:8px;line-height:1.6;max-width:800px}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius: var(--radius-md);line-height:1.6;max-width:800px}
.lv-disclaimer a{color:rgba(0,0,0,.65);text-decoration:underline}
.lv-disclaimer--light{color:#888;background:#f5f5f5}
.lv-disclaimer--light a{color:#555}
@@ -263,32 +263,32 @@
</div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(min(300px,100%),1fr));gap:20px">
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#f59e0b" stroke-width="2"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg></div>
<h3 style="font-size:1rem;font-weight:700;color:#0a0a0a;margin-bottom:6px">Physical exposure score</h3>
<p style="font-size:.875rem;color:#6b7280;line-height:1.6">Composite score of observable physical risk factors — drainage, elevation, construction quality, age.</p>
</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px">🌊</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px"></div>
<h3 style="font-size:1rem;font-weight:700;color:#0a0a0a;margin-bottom:6px">Flood/weather risk signals</h3>
<p style="font-size:.875rem;color:#6b7280;line-height:1.6">Observed drainage capacity, flood markers, historical damage evidence and climate exposure indicators.</p>
</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px">🏗️</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 14H14M4 14V8L8 4L12 8V14" stroke="#666" stroke-width="1.5" stroke-linejoin="round"/><path d="M6 14V10H10V14" stroke="#666" stroke-width="1.5"/></svg></div>
<h3 style="font-size:1rem;font-weight:700;color:#0a0a0a;margin-bottom:6px">Infrastructure vulnerability</h3>
<p style="font-size:.875rem;color:#6b7280;line-height:1.6">Condition and resilience of critical infrastructure serving the area — power, water, roads, communications.</p>
</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px">🤝</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px"></div>
<h3 style="font-size:1rem;font-weight:700;color:#0a0a0a;margin-bottom:6px">Social resilience indicators</h3>
<p style="font-size:.875rem;color:#6b7280;line-height:1.6">Observable signals of community capacity and social infrastructure — services, connectivity, density.</p>
</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px">🔄</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 8C2 11.3 4.7 14 8 14C10.5 14 12.7 12.5 13.7 10.3" stroke="#666" stroke-width="1.5" stroke-linecap="round"/><path d="M14 8C14 4.7 11.3 2 8 2C5.5 2 3.3 3.5 2.3 5.7" stroke="#666" stroke-width="1.5" stroke-linecap="round"/><path d="M13.5 7.5L14 10L11.5 9.5" stroke="#666" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M2.5 8.5L2 6L4.5 6.5" stroke="#666" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg></div>
<h3 style="font-size:1rem;font-weight:700;color:#0a0a0a;margin-bottom:6px">Recovery capacity</h3>
<p style="font-size:.875rem;color:#6b7280;line-height:1.6">Speed of observable recovery from prior disruption events — maintenance responsiveness and institutional capacity.</p>
</div>
@@ -303,9 +303,9 @@
<div class="section-label">Use cases</div>
<h2 class="section-title section-title-light" style="margin-bottom:24px">Who uses this index.</h2>
<div style="display:flex;flex-wrap:wrap;gap:12px;justify-content:center;margin-bottom:40px">
<span style="background:#e0eaff;color:#0052cc;font-size:.8125rem;font-weight:600;padding:6px 14px;border-radius:100px">Insurance underwriting</span>
<span style="background:#e0eaff;color:#0052cc;font-size:.8125rem;font-weight:600;padding:6px 14px;border-radius:100px">Portfolio stress testing</span>
<span style="background:#e0eaff;color:#0052cc;font-size:.8125rem;font-weight:600;padding:6px 14px;border-radius:100px">Resilience planning</span>
<span style="background:#e0eaff;color:#0052cc;font-size:.8125rem;font-weight:600;padding:6px 14px;border-radius: 100px">Insurance underwriting</span>
<span style="background:#e0eaff;color:#0052cc;font-size:.8125rem;font-weight:600;padding:6px 14px;border-radius: 100px">Portfolio stress testing</span>
<span style="background:#e0eaff;color:#0052cc;font-size:.8125rem;font-weight:600;padding:6px 14px;border-radius: 100px">Resilience planning</span>
</div>
</div>
</div>
@@ -318,7 +318,7 @@
<div class="section-label">Sample output</div>
<h2 class="section-title" style="margin-bottom:8px">What you receive.</h2>
<p style="color:var(--text-dim);margin-bottom:48px">Risk score + exposure map + resilience rating</p>
<div style="background:var(--surface-2);border:1px solid var(--border);border-radius:16px;padding:36px 40px;text-align:left">
<div style="background:var(--surface-2);border:1px solid var(--border);border-radius: var(--radius-lg);padding:36px 40px;text-align:left">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:8px">
<span style="color:var(--text-dim);font-size:.875rem;font-weight:600">Sample location · Placeholder score</span>
<span style="color:#fbbf24;font-size:.875rem;font-weight:700">→ Stable risk</span>
@@ -328,8 +328,8 @@
<span style="font-size:1.5rem;color:var(--text-dim)">/100</span>
</div>
<div style="background:#f3f4f6;border-radius:8px;height:12px;width:100%;margin:12px 0 8px;overflow:hidden">
<div style="height:100%;width:38%;background:#f87171;border-radius:8px;transition:width 1s ease"></div>
<div style="background:#f3f4f6;border-radius: var(--radius-md);height:12px;width:100%;margin:12px 0 8px;overflow:hidden">
<div style="height:100%;width:38%;background:#f87171;border-radius: var(--radius-md);transition:width 1s ease"></div>
</div>
<div style="display:flex;justify-content:space-between;font-size:.75rem;color:var(--text-dim)">
@@ -352,29 +352,29 @@
<h2 class="section-title">How this index detects narrative conflicts.</h2>
<p style="color:rgba(255,255,255,.75);max-width:600px;margin:0 auto;line-height:1.7">Risk & Resilience Profile detects contradictions between official risk assessments and observed physical conditions. When flood zone maps say low risk but field observations show inadequate drainage, ageing embankments and deferred maintenance — the contradiction engine raises the actual risk score above the official rating.</p>
</div>
<div style="background:rgba(0,0,0,.05);border:1px solid rgba(0,0,0,.1);border-radius:16px;padding:32px 36px">
<div style="background:rgba(0,0,0,.05);border:1px solid rgba(0,0,0,.1);border-radius: var(--radius-lg);padding:32px 36px">
<div style="display:flex;align-items:flex-start;gap:16px;margin-bottom:24px">
<span style="font-size:1.5rem">🚩</span>
<span style="font-size:1.5rem"></span>
<div>
<div style="color:#f87171;font-weight:800;font-size:.9375rem;margin-bottom:4px">Contradiction Engine — How it works</div>
<div style="color:rgba(255,255,255,.75);font-size:.875rem;line-height:1.6">Field observations are continuously cross-referenced against official records, registers, and published statistics. Divergence above threshold triggers a conflict flag.</div>
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:16px">
<div style="background:rgba(34,197,94,.1);border:1px solid rgba(34,197,94,.2);border-radius:10px;padding:16px">
<div style="background:rgba(34,197,94,.1);border:1px solid rgba(34,197,94,.2);border-radius: var(--radius-md);padding:16px">
<div style="color:#86efac;font-size:.75rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em;margin-bottom:10px">Official record</div>
<ul style="list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:6px">
<li style="color:rgba(255,255,255,.7);font-size:.875rem"> Published condition ratings</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"> Official statistics</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"> Register data</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Published condition ratings</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Official statistics</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Register data</li>
</ul>
</div>
<div style="background:rgba(239,68,68,.1);border:1px solid rgba(239,68,68,.2);border-radius:10px;padding:16px">
<div style="background:rgba(239,68,68,.1);border:1px solid rgba(239,68,68,.2);border-radius: var(--radius-md);padding:16px">
<div style="color:#fca5a5;font-size:.75rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em;margin-bottom:10px">Observed reality</div>
<ul style="list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:6px">
<li style="color:rgba(255,255,255,.7);font-size:.875rem"> Field observations</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"> Real-time signals</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"> Contradiction flags</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Field observations</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Real-time signals</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Contradiction flags</li>
</ul>
</div>
</div>
@@ -392,20 +392,20 @@
</div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(min(260px,100%),1fr));gap:20px;max-width:900px;margin:0 auto">
<a href="/intelligence/infrastructure-risk-index/" style="display:block;background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:20px 24px;text-decoration:none;color:inherit;transition:border-color .2s,transform .15s" onmouseover="this.style.borderColor='#0066ff';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='#e5e7eb';this.style.transform='none'">
<a href="/intelligence/infrastructure-risk-index/" style="display:block;background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:20px 24px;text-decoration:none;color:inherit;transition:border-color .2s,transform .15s" onmouseover="this.style.borderColor='#0066ff';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='#e5e7eb';this.style.transform='none'">
<div style="font-size:1.25rem;margin-bottom:8px"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#f59e0b" stroke-width="2"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg></div>
<div style="font-weight:700;color:#0a0a0a;font-size:.9375rem;margin-bottom:4px">Infrastructure Risk Index</div>
<div style="color:#0066ff;font-size:.8125rem;font-weight:700">View index →</div>
</a>
<a href="/intelligence/city-health-index/" style="display:block;background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:20px 24px;text-decoration:none;color:inherit;transition:border-color .2s,transform .15s" onmouseover="this.style.borderColor='#0066ff';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='#e5e7eb';this.style.transform='none'">
<div style="font-size:1.25rem;margin-bottom:8px">🏙️</div>
<a href="/intelligence/city-health-index/" style="display:block;background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:20px 24px;text-decoration:none;color:inherit;transition:border-color .2s,transform .15s" onmouseover="this.style.borderColor='#0066ff';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='#e5e7eb';this.style.transform='none'">
<div style="font-size:1.25rem;margin-bottom:8px"></div>
<div style="font-weight:700;color:#0a0a0a;font-size:.9375rem;margin-bottom:4px">City Health Index</div>
<div style="color:#0066ff;font-size:.8125rem;font-weight:700">View index →</div>
</a>
<a href="/intelligence/neighbourhood-quality-index/" style="display:block;background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:20px 24px;text-decoration:none;color:inherit;transition:border-color .2s,transform .15s" onmouseover="this.style.borderColor='#0066ff';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='#e5e7eb';this.style.transform='none'">
<div style="font-size:1.25rem;margin-bottom:8px">🏘️</div>
<a href="/intelligence/neighbourhood-quality-index/" style="display:block;background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:20px 24px;text-decoration:none;color:inherit;transition:border-color .2s,transform .15s" onmouseover="this.style.borderColor='#0066ff';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='#e5e7eb';this.style.transform='none'">
<div style="font-size:1.25rem;margin-bottom:8px"></div>
<div style="font-weight:700;color:#0a0a0a;font-size:.9375rem;margin-bottom:4px">Neighbourhood Quality Index</div>
<div style="color:#0066ff;font-size:.8125rem;font-weight:700">View index →</div>
</a>
@@ -503,7 +503,7 @@
});
if (res.ok || res.status === 201) {
msgEl.className = 'success';
msgEl.textContent = " Request received. We'll be in touch within one business day.";
msgEl.textContent = "<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Request received. We'll be in touch within one business day.";
msgEl.style.display = 'block';
this.reset();
} else { throw new Error('Server error ' + res.status); }
@@ -167,7 +167,7 @@
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social {
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px;
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md);
display: flex; align-items: center; justify-content: center;
transition: border-color 0.2s, background 0.2s;
}
@@ -187,7 +187,7 @@
[style*="grid-template-columns:repeat(auto-fit,minmax(260px"] { grid-template-columns: 1fr !important; }
}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius:8px;line-height:1.6;max-width:800px}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius: var(--radius-md);line-height:1.6;max-width:800px}
.lv-disclaimer a{color:rgba(0,0,0,.65);text-decoration:underline}
.lv-disclaimer--light{color:#888;background:#f5f5f5}
.lv-disclaimer--light a{color:#555}
@@ -263,32 +263,32 @@
</div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(min(300px,100%),1fr));gap:20px">
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px">📈</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 12L6 8L9 11L14 5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M10 5H14V9" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></div>
<h3 style="font-size:1rem;font-weight:700;color:#0a0a0a;margin-bottom:6px">Commercial expansion rate</h3>
<p style="font-size:.875rem;color:#6b7280;line-height:1.6">Speed of new commercial openings relative to closures — net growth rate of the commercial ecosystem.</p>
</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px">🏠</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px"></div>
<h3 style="font-size:1rem;font-weight:700;color:#0a0a0a;margin-bottom:6px">Residential activity</h3>
<p style="font-size:.875rem;color:#6b7280;line-height:1.6">Observable indicators of residential growth — new builds, renovations, moving activity, service infrastructure.</p>
</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px">🏥</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px"></div>
<h3 style="font-size:1rem;font-weight:700;color:#0a0a0a;margin-bottom:6px">Service density growth</h3>
<p style="font-size:.875rem;color:#6b7280;line-height:1.6">Expansion of service infrastructure — cafés, gyms, medical, childcare — that follows population growth.</p>
</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px">🔌</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px"></div>
<h3 style="font-size:1rem;font-weight:700;color:#0a0a0a;margin-bottom:6px">Infrastructure investment</h3>
<p style="font-size:.875rem;color:#6b7280;line-height:1.6">New utilities, roads, transit stops and public infrastructure as a signal of long-term growth commitment.</p>
</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px">👥</div>
<div style="background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:24px 28px;cursor:pointer" onclick="location.href='#contact'" role="button" tabindex="0">
<div style="font-size:1.5rem;margin-bottom:10px"></div>
<h3 style="font-size:1rem;font-weight:700;color:#0a0a0a;margin-bottom:6px">Demographic signals</h3>
<p style="font-size:.875rem;color:#6b7280;line-height:1.6">Observable shifts in the demographic mix of an area — a leading indicator of future commercial and property demand.</p>
</div>
@@ -303,9 +303,9 @@
<div class="section-label">Use cases</div>
<h2 class="section-title section-title-light" style="margin-bottom:24px">Who uses this index.</h2>
<div style="display:flex;flex-wrap:wrap;gap:12px;justify-content:center;margin-bottom:40px">
<span style="background:#e0eaff;color:#0052cc;font-size:.8125rem;font-weight:600;padding:6px 14px;border-radius:100px">Early-stage investment</span>
<span style="background:#e0eaff;color:#0052cc;font-size:.8125rem;font-weight:600;padding:6px 14px;border-radius:100px">Retail pre-positioning</span>
<span style="background:#e0eaff;color:#0052cc;font-size:.8125rem;font-weight:600;padding:6px 14px;border-radius:100px">Infrastructure planning</span>
<span style="background:#e0eaff;color:#0052cc;font-size:.8125rem;font-weight:600;padding:6px 14px;border-radius: 100px">Early-stage investment</span>
<span style="background:#e0eaff;color:#0052cc;font-size:.8125rem;font-weight:600;padding:6px 14px;border-radius: 100px">Retail pre-positioning</span>
<span style="background:#e0eaff;color:#0052cc;font-size:.8125rem;font-weight:600;padding:6px 14px;border-radius: 100px">Infrastructure planning</span>
</div>
</div>
</div>
@@ -318,7 +318,7 @@
<div class="section-label">Sample output</div>
<h2 class="section-title" style="margin-bottom:8px">What you receive.</h2>
<p style="color:var(--text-dim);margin-bottom:48px">Growth velocity score + district ranking + 12-month trajectory</p>
<div style="background:var(--surface-2);border:1px solid var(--border);border-radius:16px;padding:36px 40px;text-align:left">
<div style="background:var(--surface-2);border:1px solid var(--border);border-radius: var(--radius-lg);padding:36px 40px;text-align:left">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:8px">
<span style="color:var(--text-dim);font-size:.875rem;font-weight:600">Sample location · Placeholder score</span>
<span style="color:#4ade80;font-size:.875rem;font-weight:700">↑ High velocity</span>
@@ -328,8 +328,8 @@
<span style="font-size:1.5rem;color:var(--text-dim)">/100</span>
</div>
<div style="background:#f3f4f6;border-radius:8px;height:12px;width:100%;margin:12px 0 8px;overflow:hidden">
<div style="height:100%;width:78%;background:#4ade80;border-radius:8px;transition:width 1s ease"></div>
<div style="background:#f3f4f6;border-radius: var(--radius-md);height:12px;width:100%;margin:12px 0 8px;overflow:hidden">
<div style="height:100%;width:78%;background:#4ade80;border-radius: var(--radius-md);transition:width 1s ease"></div>
</div>
<div style="display:flex;justify-content:space-between;font-size:.75rem;color:var(--text-dim)">
@@ -352,29 +352,29 @@
<h2 class="section-title">How this index detects narrative conflicts.</h2>
<p style="color:rgba(255,255,255,.75);max-width:600px;margin:0 auto;line-height:1.7">Urban Growth Index flags when official population and economic statistics show growth in areas where observed activity is stagnant — or vice versa. Emerging districts frequently grow faster than statistics reflect. When field observations show rapid commercial expansion ahead of official economic data, the index raises the score and flags the statistical gap.</p>
</div>
<div style="background:rgba(0,0,0,.05);border:1px solid rgba(0,0,0,.1);border-radius:16px;padding:32px 36px">
<div style="background:rgba(0,0,0,.05);border:1px solid rgba(0,0,0,.1);border-radius: var(--radius-lg);padding:32px 36px">
<div style="display:flex;align-items:flex-start;gap:16px;margin-bottom:24px">
<span style="font-size:1.5rem">🚩</span>
<span style="font-size:1.5rem"></span>
<div>
<div style="color:#f87171;font-weight:800;font-size:.9375rem;margin-bottom:4px">Contradiction Engine — How it works</div>
<div style="color:rgba(255,255,255,.75);font-size:.875rem;line-height:1.6">Field observations are continuously cross-referenced against official records, registers, and published statistics. Divergence above threshold triggers a conflict flag.</div>
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:16px">
<div style="background:rgba(34,197,94,.1);border:1px solid rgba(34,197,94,.2);border-radius:10px;padding:16px">
<div style="background:rgba(34,197,94,.1);border:1px solid rgba(34,197,94,.2);border-radius: var(--radius-md);padding:16px">
<div style="color:#86efac;font-size:.75rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em;margin-bottom:10px">Official record</div>
<ul style="list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:6px">
<li style="color:rgba(255,255,255,.7);font-size:.875rem"> Published condition ratings</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"> Official statistics</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"> Register data</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Published condition ratings</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Official statistics</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Register data</li>
</ul>
</div>
<div style="background:rgba(239,68,68,.1);border:1px solid rgba(239,68,68,.2);border-radius:10px;padding:16px">
<div style="background:rgba(239,68,68,.1);border:1px solid rgba(239,68,68,.2);border-radius: var(--radius-md);padding:16px">
<div style="color:#fca5a5;font-size:.75rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em;margin-bottom:10px">Observed reality</div>
<ul style="list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:6px">
<li style="color:rgba(255,255,255,.7);font-size:.875rem"> Field observations</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"> Real-time signals</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"> Contradiction flags</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Field observations</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Real-time signals</li>
<li style="color:rgba(255,255,255,.7);font-size:.875rem"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Contradiction flags</li>
</ul>
</div>
</div>
@@ -392,20 +392,20 @@
</div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(min(260px,100%),1fr));gap:20px;max-width:900px;margin:0 auto">
<a href="/intelligence/growth-velocity-index/" style="display:block;background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:20px 24px;text-decoration:none;color:inherit;transition:border-color .2s,transform .15s" onmouseover="this.style.borderColor='#0066ff';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='#e5e7eb';this.style.transform='none'">
<div style="font-size:1.25rem;margin-bottom:8px">🚀</div>
<a href="/intelligence/growth-velocity-index/" style="display:block;background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:20px 24px;text-decoration:none;color:inherit;transition:border-color .2s,transform .15s" onmouseover="this.style.borderColor='#0066ff';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='#e5e7eb';this.style.transform='none'">
<div style="font-size:1.25rem;margin-bottom:8px"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M8 2L10 8L14 9L8 14L6 8L2 7L8 2Z" fill="#666"/></svg></div>
<div style="font-weight:700;color:#0a0a0a;font-size:.9375rem;margin-bottom:4px">Growth Velocity Index</div>
<div style="color:#0066ff;font-size:.8125rem;font-weight:700">View index →</div>
</a>
<a href="/intelligence/commercial-opportunity-index/" style="display:block;background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:20px 24px;text-decoration:none;color:inherit;transition:border-color .2s,transform .15s" onmouseover="this.style.borderColor='#0066ff';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='#e5e7eb';this.style.transform='none'">
<div style="font-size:1.25rem;margin-bottom:8px">📈</div>
<a href="/intelligence/commercial-opportunity-index/" style="display:block;background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:20px 24px;text-decoration:none;color:inherit;transition:border-color .2s,transform .15s" onmouseover="this.style.borderColor='#0066ff';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='#e5e7eb';this.style.transform='none'">
<div style="font-size:1.25rem;margin-bottom:8px"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 12L6 8L9 11L14 5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M10 5H14V9" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></div>
<div style="font-weight:700;color:#0a0a0a;font-size:.9375rem;margin-bottom:4px">Commercial Opportunity Index</div>
<div style="color:#0066ff;font-size:.8125rem;font-weight:700">View index →</div>
</a>
<a href="/intelligence/investment-attractiveness-index/" style="display:block;background:#f8f9ff;border:1px solid #e5e7eb;border-radius:12px;padding:20px 24px;text-decoration:none;color:inherit;transition:border-color .2s,transform .15s" onmouseover="this.style.borderColor='#0066ff';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='#e5e7eb';this.style.transform='none'">
<div style="font-size:1.25rem;margin-bottom:8px">💎</div>
<a href="/intelligence/investment-attractiveness-index/" style="display:block;background:#f8f9ff;border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:20px 24px;text-decoration:none;color:inherit;transition:border-color .2s,transform .15s" onmouseover="this.style.borderColor='#0066ff';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='#e5e7eb';this.style.transform='none'">
<div style="font-size:1.25rem;margin-bottom:8px"></div>
<div style="font-weight:700;color:#0a0a0a;font-size:.9375rem;margin-bottom:4px">Investment Attractiveness Index</div>
<div style="color:#0066ff;font-size:.8125rem;font-weight:700">View index →</div>
</a>
@@ -503,7 +503,7 @@
});
if (res.ok || res.status === 201) {
msgEl.className = 'success';
msgEl.textContent = " Request received. We'll be in touch within one business day.";
msgEl.textContent = "<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Request received. We'll be in touch within one business day.";
msgEl.style.display = 'block';
this.reset();
} else { throw new Error('Server error ' + res.status); }
+13 -13
View File
@@ -15,14 +15,14 @@ html,body{min-height:100%;font-family:-apple-system,BlinkMacSystemFont,'SF Pro T
#app{max-width:960px;margin:0 auto;padding:16px}
h1{font-size:22px;font-weight:800;letter-spacing:-.5px;margin-bottom:4px}
.sub{font-size:12px;color:var(--t2);margin-bottom:24px}
#drop-zone{border:2px dashed rgba(0,0,0,0.15);border-radius:12px;padding:40px;text-align:center;cursor:pointer;transition:border-color 0.2s;margin-bottom:20px}
#drop-zone{border:2px dashed rgba(0,0,0,0.15);border-radius: var(--radius-md);padding:40px;text-align:center;cursor:pointer;transition:border-color 0.2s;margin-bottom:20px}
#drop-zone:hover,#drop-zone.drag{border-color:var(--accent3)}
#drop-zone p{color:var(--t2);font-size:13px}
#preview-wrap{display:none;position:relative;margin-bottom:16px}
#preview{max-width:100%;border-radius:10px;display:block}
#preview{max-width:100%;border-radius: var(--radius-md);display:block}
#canvas{position:absolute;top:0;left:0;cursor:crosshair}
#labels-panel{display:grid;grid-template-columns:repeat(auto-fill,minmax(140px,1fr));gap:6px;margin-bottom:16px}
.label-btn{padding:8px 12px;border-radius:8px;border:1px solid var(--border);background:var(--s2);cursor:pointer;font-size:12px;font-weight:600;color:var(--t2);transition:all 0.12s;display:flex;align-items:center;gap:7px}
.label-btn{padding:8px 12px;border-radius: var(--radius-md);border:1px solid var(--border);background:var(--s2);cursor:pointer;font-size:12px;font-weight:600;color:var(--t2);transition:all 0.12s;display:flex;align-items:center;gap:7px}
.label-btn:hover{background:var(--s3);color:var(--text)}
.label-btn.active{border-color:currentColor}
.label-btn[data-cat="electrical_hazard"].active{color:#f87171;background:rgba(248,113,113,0.1)}
@@ -37,22 +37,22 @@ h1{font-size:22px;font-weight:800;letter-spacing:-.5px;margin-bottom:4px}
.label-btn[data-cat="infrastructure_ok"].active{color:#4ade80;background:rgba(74,222,128,0.1)}
#scores{display:grid;grid-template-columns:1fr;gap:8px;margin-bottom:16px}
@media(min-width:560px){#scores{grid-template-columns:1fr 1fr 1fr}}
.score-group{background:var(--s2);border-radius:8px;padding:10px 12px}
.score-group{background:var(--s2);border-radius: var(--radius-md);padding:10px 12px}
.score-group label{font-size:9px;font-weight:700;letter-spacing:.6px;text-transform:uppercase;color:var(--t2);display:block;margin-bottom:6px}
.score-group input[type=range]{width:100%;accent-color:var(--accent3)}
.score-val{font-size:14px;font-weight:800;font-family:monospace;color:var(--accent3)}
#boxes-list{margin-bottom:16px;font-size:11px;color:var(--t2)}
.box-item{display:flex;align-items:center;justify-content:space-between;padding:4px 8px;background:var(--s2);border-radius:5px;margin-bottom:4px}
.box-item{display:flex;align-items:center;justify-content:space-between;padding:4px 8px;background:var(--s2);border-radius: var(--radius-sm);margin-bottom:4px}
.box-del{color:#f87171;cursor:pointer;font-size:14px;background:none;border:none;padding:0 4px}
.btn{padding:9px 20px;border-radius:8px;border:none;cursor:pointer;font-size:13px;font-weight:700}
.btn{padding:9px 20px;border-radius: var(--radius-md);border:none;cursor:pointer;font-size:13px;font-weight:700}
.btn-primary{background:var(--accent);color: #1d1d1f}
.btn-secondary{background:var(--s2);color:var(--text);border:1px solid var(--border)}
#actions{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:20px}
#output{background:var(--s2);border-radius:8px;padding:14px;font-size:11px;font-family:monospace;white-space:pre-wrap;color:#4ade80;display:none;max-height:300px;overflow-y:auto}
#output{background:var(--s2);border-radius: var(--radius-md);padding:14px;font-size:11px;font-family:monospace;white-space:pre-wrap;color:#4ade80;display:none;max-height:300px;overflow-y:auto}
#file-input{display:none}
.contradiction-row{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-bottom:16px;background:var(--s2);border-radius:8px;padding:10px 12px}
.contradiction-row{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-bottom:16px;background:var(--s2);border-radius: var(--radius-md);padding:10px 12px}
.contradiction-row label{font-size:11px;font-weight:600;color:var(--t2);flex:1}
.contradiction-row select{background:var(--s3);color:var(--text);border:1px solid var(--border);border-radius:6px;padding:4px 8px;font-size:11px}
.contradiction-row select{background:var(--s3);color:var(--text);border:1px solid var(--border);border-radius: var(--radius-sm);padding:4px 8px;font-size:11px}
@media (max-width: 640px) {
.hero h1 { font-size: 2rem !important; }
.hero p { font-size: 1rem !important; }
@@ -222,10 +222,10 @@ h1{font-size:22px;font-weight:800;letter-spacing:-.5px;margin-bottom:4px}
<div id="actions">
<button class="btn btn-primary" onclick="exportLabel()">Export JSON</button>
<button class="btn btn-secondary" onclick="clearAll()">Clear</button>
<input type="text" id="lat-input" placeholder="lat" style="flex:1;min-width:70px;padding:8px;background:var(--s2);border:1px solid var(--border);border-radius:7px;color:var(--text);font-size:12px">
<input type="text" id="lng-input" placeholder="lng" style="flex:1;min-width:70px;padding:8px;background:var(--s2);border:1px solid var(--border);border-radius:7px;color:var(--text);font-size:12px">
<input type="text" id="city-input" placeholder="city" style="flex:1;min-width:70px;padding:8px;background:var(--s2);border:1px solid var(--border);border-radius:7px;color:var(--text);font-size:12px">
<input type="text" id="district-input" placeholder="district" style="flex:1;min-width:80px;padding:8px;background:var(--s2);border:1px solid var(--border);border-radius:7px;color:var(--text);font-size:12px">
<input type="text" id="lat-input" placeholder="lat" style="flex:1;min-width:70px;padding:8px;background:var(--s2);border:1px solid var(--border);border-radius: var(--radius-sm);color:var(--text);font-size:12px">
<input type="text" id="lng-input" placeholder="lng" style="flex:1;min-width:70px;padding:8px;background:var(--s2);border:1px solid var(--border);border-radius: var(--radius-sm);color:var(--text);font-size:12px">
<input type="text" id="city-input" placeholder="city" style="flex:1;min-width:70px;padding:8px;background:var(--s2);border:1px solid var(--border);border-radius: var(--radius-sm);color:var(--text);font-size:12px">
<input type="text" id="district-input" placeholder="district" style="flex:1;min-width:80px;padding:8px;background:var(--s2);border:1px solid var(--border);border-radius: var(--radius-sm);color:var(--text);font-size:12px">
</div>
<pre id="output"></pre>
+101 -101
View File
@@ -11,10 +11,10 @@
<title>LandveX Admin — Infrastrukturportal</title>
<link rel="stylesheet" >
<style>
/* ═══════════════════════════════════════════
/*
LANDVEX ADMIN PORTAL — iOS Native
Wavult Group · 2026
═══════════════════════════════════════════ */
*/
* { box-sizing: border-box; margin: 0; padding: 0; -webkit-font-smoothing: antialiased; }
@@ -25,7 +25,7 @@ body {
min-height: 100vh;
}
/* ── NAVIGATION BAR ── */
/* NAVIGATION BAR */
.nav-bar {
position: sticky;
top: 0;
@@ -60,7 +60,7 @@ body {
width: 28px;
height: 28px;
background: linear-gradient(135deg, #007AFF 0%, #0055CC 100%);
border-radius: 7px;
border-radius: var(--radius-sm);
display: flex;
align-items: center;
justify-content: center;
@@ -76,18 +76,18 @@ body {
padding: 3px 8px;
background: rgba(255, 149, 0, 0.15);
color: var(--ios-orange);
border-radius: 20px;
border-radius: var(--radius-xl);
letter-spacing: 0.3px;
}
/* ── CONTENT ── */
/* CONTENT */
.content {
padding: 0 16px 120px;
max-width: 1100px;
margin: 0 auto;
}
/* ── SECTION HEADER ── */
/* SECTION HEADER */
.section-header {
padding: 20px 0 8px;
font-family: var(--font-display);
@@ -111,7 +111,7 @@ body {
padding-bottom: 12px;
}
/* ── METRICS GRID ── */
/* METRICS GRID */
.metrics-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
@@ -121,7 +121,7 @@ body {
.metric-card {
background: var(--background-primary);
border-radius: 16px;
border-radius: var(--radius-lg);
padding: 16px;
border: 0.5px solid var(--separator);
}
@@ -156,10 +156,10 @@ body {
.metric-orange .metric-value { color: var(--ios-orange); }
.metric-red .metric-value { color: var(--ios-red); }
/* ── TABLE ── */
/* TABLE */
.table-card {
background: var(--background-primary);
border-radius: 16px;
border-radius: var(--radius-lg);
border: 0.5px solid var(--separator);
overflow: hidden;
margin-bottom: 12px;
@@ -237,7 +237,7 @@ tbody td {
margin-top: 2px;
}
/* ── STATUS BADGES ── */
/* STATUS BADGES */
.status-badge {
display: inline-flex;
align-items: center;
@@ -245,7 +245,7 @@ tbody td {
font-size: 12px;
font-weight: 600;
padding: 4px 10px;
border-radius: 20px;
border-radius: var(--radius-xl);
}
.status-ok {
@@ -271,11 +271,11 @@ tbody td {
.status-dot {
width: 6px;
height: 6px;
border-radius: 50%;
border-radius: var(--radius-full);
background: currentColor;
}
/* ── RISK SCORE ── */
/* RISK SCORE */
.risk-bar-wrap {
display: flex;
align-items: center;
@@ -286,14 +286,14 @@ tbody td {
flex: 1;
height: 4px;
background: var(--fill-tertiary);
border-radius: 2px;
border-radius: var(--radius-sm);
overflow: hidden;
min-width: 60px;
}
.risk-fill {
height: 100%;
border-radius: 2px;
border-radius: var(--radius-sm);
transition: width 0.3s;
}
@@ -308,10 +308,10 @@ tbody td {
text-align: right;
}
/* ── MAP PLACEHOLDER ── */
/* MAP PLACEHOLDER */
.map-card {
background: var(--background-primary);
border-radius: 16px;
border-radius: var(--radius-lg);
border: 0.5px solid var(--separator);
overflow: hidden;
margin-bottom: 12px;
@@ -355,7 +355,7 @@ tbody td {
position: absolute;
width: 28px;
height: 28px;
border-radius: 50% 50% 50% 0;
border-radius: var(--radius-full) 50% 50% 0;
transform: rotate(-45deg);
display: flex;
align-items: center;
@@ -383,7 +383,7 @@ tbody td {
background: var(--glass-bg);
backdrop-filter: var(--glass-blur);
-webkit-backdrop-filter: var(--glass-blur);
border-radius: 12px;
border-radius: var(--radius-md);
padding: 8px 12px;
border: 0.5px solid var(--separator);
display: flex;
@@ -401,10 +401,10 @@ tbody td {
.legend-dot {
width: 8px;
height: 8px;
border-radius: 50%;
border-radius: var(--radius-full);
}
/* ── FAB BUTTON ── */
/* FAB BUTTON */
.fab {
position: fixed;
bottom: calc(24px + env(safe-area-inset-bottom));
@@ -437,10 +437,10 @@ tbody td {
box-shadow: 0 2px 12px rgba(0, 122, 255, 0.35);
}
/* ── INSPECTION CELLS ── */
/* INSPECTION CELLS */
.inspection-list {
background: var(--background-primary);
border-radius: 16px;
border-radius: var(--radius-lg);
border: 0.5px solid var(--separator);
overflow: hidden;
}
@@ -461,7 +461,7 @@ tbody td {
.inspection-icon {
width: 40px;
height: 40px;
border-radius: 10px;
border-radius: var(--radius-md);
display: flex;
align-items: center;
justify-content: center;
@@ -492,7 +492,7 @@ tbody td {
flex-shrink: 0;
}
/* ── MODAL ── */
/* MODAL */
.modal-overlay {
position: fixed;
inset: 0;
@@ -514,7 +514,7 @@ tbody td {
.modal-sheet {
background: var(--background-primary);
border-radius: 20px 20px 0 0;
border-radius: var(--radius-xl) 20px 0 0;
width: 100%;
max-width: 640px;
padding: 8px 0 env(safe-area-inset-bottom);
@@ -530,7 +530,7 @@ tbody td {
width: 36px;
height: 4px;
background: var(--fill-primary);
border-radius: 2px;
border-radius: var(--radius-sm);
margin: 8px auto 16px;
}
@@ -559,7 +559,7 @@ tbody td {
padding: 10px 12px;
background: var(--background-secondary);
border: 0.5px solid var(--separator);
border-radius: 10px;
border-radius: var(--radius-md);
font-size: 15px;
font-family: var(--font-text);
color: var(--label-primary);
@@ -576,7 +576,7 @@ tbody td {
padding: 10px 12px;
background: var(--background-secondary);
border: 0.5px solid var(--separator);
border-radius: 10px;
border-radius: var(--radius-md);
font-size: 15px;
font-family: var(--font-text);
color: var(--label-primary);
@@ -597,7 +597,7 @@ tbody td {
background: var(--ios-blue);
color: white;
border: none;
border-radius: 14px;
border-radius: var(--radius-lg);
font-size: 17px;
font-weight: 600;
font-family: var(--font-text);
@@ -613,7 +613,7 @@ tbody td {
background: var(--fill-secondary);
color: var(--label-primary);
border: none;
border-radius: 14px;
border-radius: var(--radius-lg);
font-size: 17px;
font-weight: 600;
font-family: var(--font-text);
@@ -623,7 +623,7 @@ tbody td {
.btn-secondary:active { opacity: 0.7; }
/* ── TAB BAR ── */
/* TAB BAR */
.tab-bar {
position: fixed;
bottom: 0;
@@ -661,12 +661,12 @@ tbody td {
.tab-item.active .tab-label { color: var(--ios-blue); }
.tab-item.active .tab-icon { filter: none; }
/* ── DARK MODE ── */
/* DARK MODE */
@media (prefers-color-scheme: dark) {
.map-container { background: #1A1E23; }
}
/* ── RESPONSIVE ── */
/* RESPONSIVE */
@media (min-width: 768px) {
.metrics-grid { grid-template-columns: repeat(4, 1fr); }
.content { padding: 0 24px 80px; }
@@ -710,7 +710,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
</div>
<div style="display:flex;align-items:center;gap:8px;">
<span class="badge-admin">ADMIN</span>
<div style="width:32px;height:32px;border-radius:50%;background:linear-gradient(135deg,#007AFF,#0055CC);display:flex;align-items:center;justify-content:center;color:white;font-size:14px;font-weight:700;">A</div>
<div style="width:32px;height:32px;border-radius: var(--radius-full);background:linear-gradient(135deg,#007AFF,#0055CC);display:flex;align-items:center;justify-content:center;color:white;font-size:14px;font-weight:700;">A</div>
</div>
</nav>
@@ -752,37 +752,37 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<!-- LIVE MAP WITH ZOOMER POSITIONS -->
<div class="map-card" style="margin-bottom:12px;">
<div class="table-header">
<span class="table-title">📍 Realtids zoomers + objekt</span>
<span class="table-title"> Realtids zoomers + objekt</span>
<button class="table-action" onclick="cycleZoomerPos()">↻ Uppdatera</button>
</div>
<div class="map-container" id="map" style="position:relative;min-height:260px;">
<div class="map-grid"></div>
<!-- Object pins -->
<div class="map-pin map-pin-ok" style="left:22%;top:30%;" title="Gamla Stan Bron · OK"><div class="map-pin-inner"></div></div>
<div class="map-pin map-pin-ok" style="left:22%;top:30%;" title="Gamla Stan Bron · OK"><div class="map-pin-inner"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></div></div>
<div class="map-pin map-pin-warn" style="left:45%;top:45%;" title="E4 Avsnitt 12 · Varning"><div class="map-pin-inner">!</div></div>
<div class="map-pin map-pin-critical" style="left:68%;top:25%;" title="E4 Älvsjö · Kritisk"><div class="map-pin-inner"></div></div>
<div class="map-pin map-pin-ok" style="left:35%;top:65%;" title="Arlanda Terminal 5 · OK"><div class="map-pin-inner"></div></div>
<div class="map-pin map-pin-critical" style="left:68%;top:25%;" title="E4 Älvsjö · Kritisk"><div class="map-pin-inner"></div></div>
<div class="map-pin map-pin-ok" style="left:35%;top:65%;" title="Arlanda Terminal 5 · OK"><div class="map-pin-inner"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></div></div>
<div class="map-pin map-pin-warn" style="left:80%;top:55%;" title="Öresundsbron · Varning"><div class="map-pin-inner">!</div></div>
<div class="map-pin map-pin-ok" style="left:55%;top:72%;" title="Göta Älvbron · OK"><div class="map-pin-inner"></div></div>
<div class="map-pin map-pin-ok" style="left:55%;top:72%;" title="Göta Älvbron · OK"><div class="map-pin-inner"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></div></div>
<!-- Live zoomer pins -->
<div id="zoomer-lx01" style="position:absolute;left:48%;top:42%;z-index:10;transition:all 2s ease;">
<div style="background:var(--ios-blue);color: #1d1d1f;border-radius:50%;width:28px;height:28px;display:flex;align-items:center;justify-content:center;font-size:14px;box-shadow:0 2px 8px rgba(0,122,255,0.5);animation:zoomer-pulse 2s infinite;">🚁</div>
<div style="font-size:9px;font-weight:700;color:var(--ios-blue);text-align:center;margin-top:2px;background:rgba(0,0,0,0.9);border-radius:6px;padding:1px 4px;">LX-01</div>
<div style="background:var(--ios-blue);color: #1d1d1f;border-radius: var(--radius-full);width:28px;height:28px;display:flex;align-items:center;justify-content:center;font-size:14px;box-shadow:0 2px 8px rgba(0,122,255,0.5);animation:zoomer-pulse 2s infinite;"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M8 2V6M4 4H12" stroke="#666" stroke-width="1.5" stroke-linecap="round"/><path d="M2 8H6L8 6L10 8H14" stroke="#666" stroke-width="1.5" stroke-linejoin="round"/><path d="M6 8V12H10V8" stroke="#666" stroke-width="1.5"/></svg></div>
<div style="font-size:9px;font-weight:700;color:var(--ios-blue);text-align:center;margin-top:2px;background:rgba(0,0,0,0.9);border-radius: var(--radius-sm);padding:1px 4px;">LX-01</div>
</div>
<div id="zoomer-lx02" style="position:absolute;left:24%;top:27%;z-index:10;transition:all 2s ease;">
<div style="background:var(--ios-green);color: #1d1d1f;border-radius:50%;width:28px;height:28px;display:flex;align-items:center;justify-content:center;font-size:14px;box-shadow:0 2px 8px rgba(52,199,89,0.5);">🚁</div>
<div style="font-size:9px;font-weight:700;color:var(--ios-green);text-align:center;margin-top:2px;background:rgba(0,0,0,0.9);border-radius:6px;padding:1px 4px;">LX-02</div>
<div style="background:var(--ios-green);color: #1d1d1f;border-radius: var(--radius-full);width:28px;height:28px;display:flex;align-items:center;justify-content:center;font-size:14px;box-shadow:0 2px 8px rgba(52,199,89,0.5);"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M8 2V6M4 4H12" stroke="#666" stroke-width="1.5" stroke-linecap="round"/><path d="M2 8H6L8 6L10 8H14" stroke="#666" stroke-width="1.5" stroke-linejoin="round"/><path d="M6 8V12H10V8" stroke="#666" stroke-width="1.5"/></svg></div>
<div style="font-size:9px;font-weight:700;color:var(--ios-green);text-align:center;margin-top:2px;background:rgba(0,0,0,0.9);border-radius: var(--radius-sm);padding:1px 4px;">LX-02</div>
</div>
<div id="zoomer-lx03" style="position:absolute;left:72%;top:58%;z-index:10;transition:all 2s ease;">
<div style="background:var(--ios-orange);color: #1d1d1f;border-radius:50%;width:28px;height:28px;display:flex;align-items:center;justify-content:center;font-size:14px;box-shadow:0 2px 8px rgba(255,149,0,0.5);">🚁</div>
<div style="font-size:9px;font-weight:700;color:var(--ios-orange);text-align:center;margin-top:2px;background:rgba(0,0,0,0.9);border-radius:6px;padding:1px 4px;">LX-03 <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#f59e0b" stroke-width="2"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg></div>
<div style="background:var(--ios-orange);color: #1d1d1f;border-radius: var(--radius-full);width:28px;height:28px;display:flex;align-items:center;justify-content:center;font-size:14px;box-shadow:0 2px 8px rgba(255,149,0,0.5);"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M8 2V6M4 4H12" stroke="#666" stroke-width="1.5" stroke-linecap="round"/><path d="M2 8H6L8 6L10 8H14" stroke="#666" stroke-width="1.5" stroke-linejoin="round"/><path d="M6 8V12H10V8" stroke="#666" stroke-width="1.5"/></svg></div>
<div style="font-size:9px;font-weight:700;color:var(--ios-orange);text-align:center;margin-top:2px;background:rgba(0,0,0,0.9);border-radius: var(--radius-sm);padding:1px 4px;">LX-03 <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#f59e0b" stroke-width="2"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg></div>
</div>
<!-- Live indicator -->
<div style="position:absolute;top:10px;left:10px;z-index:11;display:flex;align-items:center;gap:5px;background:rgba(0,0,0,0.9);border-radius:20px;padding:4px 10px;font-size:11px;font-weight:600;color:var(--ios-green);">
<div style="width:6px;height:6px;border-radius:50%;background:var(--ios-green);animation:zoomer-pulse 2s infinite;"></div>
<div style="position:absolute;top:10px;left:10px;z-index:11;display:flex;align-items:center;gap:5px;background:rgba(0,0,0,0.9);border-radius: var(--radius-xl);padding:4px 10px;font-size:11px;font-weight:600;color:var(--ios-green);">
<div style="width:6px;height:6px;border-radius: var(--radius-full);background:var(--ios-green);animation:zoomer-pulse 2s infinite;"></div>
LIVE
</div>
@@ -790,7 +790,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<div class="legend-item"><div class="legend-dot" style="background:var(--ios-green)"></div>OK (189)</div>
<div class="legend-item"><div class="legend-dot" style="background:var(--ios-orange)"></div>Varning (41)</div>
<div class="legend-item"><div class="legend-dot" style="background:var(--ios-red)"></div>Kritisk (17)</div>
<div class="legend-item"><div class="legend-dot" style="background:var(--ios-blue)"></div>🚁 Zoomer (3)</div>
<div class="legend-item"><div class="legend-dot" style="background:var(--ios-blue)"></div><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M8 2V6M4 4H12" stroke="#666" stroke-width="1.5" stroke-linecap="round"/><path d="M2 8H6L8 6L10 8H14" stroke="#666" stroke-width="1.5" stroke-linejoin="round"/><path d="M6 8V12H10V8" stroke="#666" stroke-width="1.5"/></svg> Zoomer (3)</div>
</div>
</div>
</div>
@@ -801,7 +801,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<div class="metric-card metric-green">
<div class="metric-value">14</div>
<div class="metric-label">Pågående</div>
<div class="metric-trend trend-up">🚁 Aktiva uppdrag</div>
<div class="metric-trend trend-up"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M8 2V6M4 4H12" stroke="#666" stroke-width="1.5" stroke-linecap="round"/><path d="M2 8H6L8 6L10 8H14" stroke="#666" stroke-width="1.5" stroke-linejoin="round"/><path d="M6 8V12H10V8" stroke="#666" stroke-width="1.5"/></svg> Aktiva uppdrag</div>
</div>
<div class="metric-card metric-blue">
<div class="metric-value">218</div>
@@ -818,7 +818,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<!-- ACTIVE MISSIONS TABLE -->
<div class="table-card" style="margin-bottom:12px;">
<div class="table-header">
<span class="table-title"> Pågående uppdrag</span>
<span class="table-title"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M9 2L5 9H8L7 14L11 7H8L9 2Z" fill="#f59e0b"/></svg> Pågående uppdrag</span>
<button class="table-action" onclick="openDispatch()">Snabb-dispatch →</button>
</div>
<div class="table-scroll">
@@ -828,12 +828,12 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
</tr></thead>
<tbody>
<tr>
<td><span style="font-weight:600;color:var(--ios-blue);">🚁 LX-01</span></td>
<td><span style="font-weight:600;color:var(--ios-blue);"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M8 2V6M4 4H12" stroke="#666" stroke-width="1.5" stroke-linecap="round"/><path d="M2 8H6L8 6L10 8H14" stroke="#666" stroke-width="1.5" stroke-linejoin="round"/><path d="M6 8V12H10V8" stroke="#666" stroke-width="1.5"/></svg> LX-01</span></td>
<td>E4 Avsnitt 12</td>
<td><span style="background:rgba(52,199,89,0.12);color:var(--ios-green);font-size:11px;font-weight:700;padding:3px 8px;border-radius:20px;">PÅGÅR</span></td>
<td><span style="background:rgba(52,199,89,0.12);color:var(--ios-green);font-size:11px;font-weight:700;padding:3px 8px;border-radius: var(--radius-xl);">PÅGÅR</span></td>
<td>
<div style="width:80px;height:5px;background:var(--fill-tertiary);border-radius:3px;overflow:hidden;">
<div style="width:72%;height:100%;background:var(--ios-blue);border-radius:3px;"></div>
<div style="width:80px;height:5px;background:var(--fill-tertiary);border-radius: var(--radius-sm);overflow:hidden;">
<div style="width:72%;height:100%;background:var(--ios-blue);border-radius: var(--radius-sm);"></div>
</div>
<div style="font-size:10px;color:var(--label-tertiary);margin-top:2px;">72%</div>
</td>
@@ -841,12 +841,12 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<td><button style="font-size:12px;color:var(--ios-blue);background:none;border:none;cursor:pointer;padding:4px 0;">Följ live</button></td>
</tr>
<tr>
<td><span style="font-weight:600;color:var(--ios-green);">🚁 LX-02</span></td>
<td><span style="font-weight:600;color:var(--ios-green);"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M8 2V6M4 4H12" stroke="#666" stroke-width="1.5" stroke-linecap="round"/><path d="M2 8H6L8 6L10 8H14" stroke="#666" stroke-width="1.5" stroke-linejoin="round"/><path d="M6 8V12H10V8" stroke="#666" stroke-width="1.5"/></svg> LX-02</span></td>
<td>Gamla Stan Bron</td>
<td><span style="background:rgba(52,199,89,0.12);color:var(--ios-green);font-size:11px;font-weight:700;padding:3px 8px;border-radius:20px;">PÅGÅR</span></td>
<td><span style="background:rgba(52,199,89,0.12);color:var(--ios-green);font-size:11px;font-weight:700;padding:3px 8px;border-radius: var(--radius-xl);">PÅGÅR</span></td>
<td>
<div style="width:80px;height:5px;background:var(--fill-tertiary);border-radius:3px;overflow:hidden;">
<div style="width:35%;height:100%;background:var(--ios-green);border-radius:3px;"></div>
<div style="width:80px;height:5px;background:var(--fill-tertiary);border-radius: var(--radius-sm);overflow:hidden;">
<div style="width:35%;height:100%;background:var(--ios-green);border-radius: var(--radius-sm);"></div>
</div>
<div style="font-size:10px;color:var(--label-tertiary);margin-top:2px;">35%</div>
</td>
@@ -854,12 +854,12 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<td><button style="font-size:12px;color:var(--ios-blue);background:none;border:none;cursor:pointer;padding:4px 0;">Följ live</button></td>
</tr>
<tr>
<td><span style="font-weight:600;color:var(--ios-orange);">🚁 LX-03</span></td>
<td><span style="font-weight:600;color:var(--ios-orange);"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M8 2V6M4 4H12" stroke="#666" stroke-width="1.5" stroke-linecap="round"/><path d="M2 8H6L8 6L10 8H14" stroke="#666" stroke-width="1.5" stroke-linejoin="round"/><path d="M6 8V12H10V8" stroke="#666" stroke-width="1.5"/></svg> LX-03</span></td>
<td>Öresundsbron S3</td>
<td><span style="background:rgba(255,149,0,0.12);color:var(--ios-orange);font-size:11px;font-weight:700;padding:3px 8px;border-radius:20px;">AVVIKELSE</span></td>
<td><span style="background:rgba(255,149,0,0.12);color:var(--ios-orange);font-size:11px;font-weight:700;padding:3px 8px;border-radius: var(--radius-xl);">AVVIKELSE</span></td>
<td>
<div style="width:80px;height:5px;background:var(--fill-tertiary);border-radius:3px;overflow:hidden;">
<div style="width:58%;height:100%;background:var(--ios-orange);border-radius:3px;"></div>
<div style="width:80px;height:5px;background:var(--fill-tertiary);border-radius: var(--radius-sm);overflow:hidden;">
<div style="width:58%;height:100%;background:var(--ios-orange);border-radius: var(--radius-sm);"></div>
</div>
<div style="font-size:10px;color:var(--label-tertiary);margin-top:2px;">58%</div>
</td>
@@ -911,27 +911,27 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<tbody>
<tr>
<td style="font-weight:600;">Trafikverket</td><td>E4 Älvsjö · Kritisk</td><td>Fullständig</td>
<td><span style="background:rgba(255,59,48,0.12);color:var(--ios-red);font-size:11px;font-weight:700;padding:2px 7px;border-radius:20px;">KRITISK</span></td>
<td><span style="background:rgba(255,59,48,0.12);color:var(--ios-red);font-size:11px;font-weight:700;padding:2px 7px;border-radius: var(--radius-xl);">KRITISK</span></td>
<td style="font-size:12px;color:var(--label-tertiary);">09:12</td>
<td><button style="font-size:12px;color: #1d1d1f;background:var(--ios-red);border:none;border-radius:12px;padding:4px 12px;cursor:pointer;font-weight:600;" onclick="openDispatch()">Dispatch nu</button></td>
<td><button style="font-size:12px;color: #1d1d1f;background:var(--ios-red);border:none;border-radius: var(--radius-md);padding:4px 12px;cursor:pointer;font-weight:600;" onclick="openDispatch()">Dispatch nu</button></td>
</tr>
<tr>
<td style="font-weight:600;">Region Stockholm</td><td>Gamla Stan Bro</td><td>Ytinspektion</td>
<td><span style="background:rgba(255,149,0,0.12);color:var(--ios-orange);font-size:11px;font-weight:700;padding:2px 7px;border-radius:20px;">HÖG</span></td>
<td><span style="background:rgba(255,149,0,0.12);color:var(--ios-orange);font-size:11px;font-weight:700;padding:2px 7px;border-radius: var(--radius-xl);">HÖG</span></td>
<td style="font-size:12px;color:var(--label-tertiary);">08:45</td>
<td><button style="font-size:12px;color: #1d1d1f;background:var(--ios-orange);border:none;border-radius:12px;padding:4px 12px;cursor:pointer;font-weight:600;" onclick="openDispatch()">Dispatch</button></td>
<td><button style="font-size:12px;color: #1d1d1f;background:var(--ios-orange);border:none;border-radius: var(--radius-md);padding:4px 12px;cursor:pointer;font-weight:600;" onclick="openDispatch()">Dispatch</button></td>
</tr>
<tr>
<td style="font-weight:600;">NCC Sverige</td><td>Rv 40 Borås</td><td>Strukturkontroll</td>
<td><span style="background:rgba(0,122,255,0.10);color:var(--ios-blue);font-size:11px;font-weight:700;padding:2px 7px;border-radius:20px;">NORMAL</span></td>
<td><span style="background:rgba(0,122,255,0.10);color:var(--ios-blue);font-size:11px;font-weight:700;padding:2px 7px;border-radius: var(--radius-xl);">NORMAL</span></td>
<td style="font-size:12px;color:var(--label-tertiary);">Igår</td>
<td><button style="font-size:12px;color:var(--ios-blue);background:none;border:1px solid var(--ios-blue);border-radius:12px;padding:4px 12px;cursor:pointer;font-weight:600;">Schemalägg</button></td>
<td><button style="font-size:12px;color:var(--ios-blue);background:none;border:1px solid var(--ios-blue);border-radius: var(--radius-md);padding:4px 12px;cursor:pointer;font-weight:600;">Schemalägg</button></td>
</tr>
<tr>
<td style="font-weight:600;">Ellevio AB</td><td>Kraftledning N32</td><td>Optisk scanning</td>
<td><span style="background:rgba(0,122,255,0.10);color:var(--ios-blue);font-size:11px;font-weight:700;padding:2px 7px;border-radius:20px;">NORMAL</span></td>
<td><span style="background:rgba(0,122,255,0.10);color:var(--ios-blue);font-size:11px;font-weight:700;padding:2px 7px;border-radius: var(--radius-xl);">NORMAL</span></td>
<td style="font-size:12px;color:var(--label-tertiary);">Igår</td>
<td><button style="font-size:12px;color:var(--ios-blue);background:none;border:1px solid var(--ios-blue);border-radius:12px;padding:4px 12px;cursor:pointer;font-weight:600;">Schemalägg</button></td>
<td><button style="font-size:12px;color:var(--ios-blue);background:none;border:1px solid var(--ios-blue);border-radius: var(--radius-md);padding:4px 12px;cursor:pointer;font-weight:600;">Schemalägg</button></td>
</tr>
</tbody>
</table>
@@ -963,7 +963,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<tbody>
<tr onclick="alert('Öppnar objektdetaljer: E4 Norrtull')">
<td><div class="td-name">E4 Norrtull Avsnitt 12</div><div class="td-sub">REF-001 · Stockholm</div></td>
<td>🛣️ Väg</td>
<td> Väg</td>
<td><span class="status-badge status-warn"><span class="status-dot"></span>Kräver åtgärd</span></td>
<td>
<div class="risk-bar-wrap risk-medium">
@@ -976,7 +976,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
</tr>
<tr onclick="alert('Öppnar objektdetaljer: Gamla Stan')">
<td><div class="td-name">Gamla Stan Bro</div><div class="td-sub">REF-002 · Stockholm</div></td>
<td>🌉 Bro</td>
<td> Bro</td>
<td><span class="status-badge status-ok"><span class="status-dot"></span>OK</span></td>
<td>
<div class="risk-bar-wrap risk-low">
@@ -989,7 +989,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
</tr>
<tr onclick="alert('Öppnar objektdetaljer: Kungliga Slottet')">
<td><div class="td-name">Kungliga Slottet Tak</div><div class="td-sub">REF-003 · Stockholm</div></td>
<td>🏛️ Byggnad</td>
<td> Byggnad</td>
<td><span class="status-badge status-critical"><span class="status-dot"></span>Kritisk</span></td>
<td>
<div class="risk-bar-wrap risk-high">
@@ -998,11 +998,11 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
</div>
</td>
<td>2026-05-01</td>
<td>🔴 Omedelbart</td>
<td> Omedelbart</td>
</tr>
<tr onclick="alert('Öppnar objektdetaljer: Arlanda Terminal 5')">
<td><div class="td-name">Arlanda Terminal 5</div><div class="td-sub">REF-004 · Sigtuna</div></td>
<td>✈️ Byggnad</td>
<td> Byggnad</td>
<td><span class="status-badge status-ok"><span class="status-dot"></span>OK</span></td>
<td>
<div class="risk-bar-wrap risk-low">
@@ -1015,7 +1015,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
</tr>
<tr onclick="alert('Öppnar objektdetaljer: Öresundsbron')">
<td><div class="td-name">Öresundsbron Sektion 3</div><div class="td-sub">REF-005 · Malmö</div></td>
<td>🌉 Bro</td>
<td> Bro</td>
<td><span class="status-badge status-warn"><span class="status-dot"></span>Kräver åtgärd</span></td>
<td>
<div class="risk-bar-wrap risk-medium">
@@ -1035,7 +1035,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<div class="section-header">Senaste inspektioner</div>
<div class="inspection-list">
<div class="inspection-item">
<div class="inspection-icon" style="background:rgba(52,199,89,0.15)">🏗️</div>
<div class="inspection-icon" style="background:rgba(52,199,89,0.15)"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 14H14M4 14V8L8 4L12 8V14" stroke="#666" stroke-width="1.5" stroke-linejoin="round"/><path d="M6 14V10H10V14" stroke="#666" stroke-width="1.5"/></svg></div>
<div class="inspection-body">
<div class="inspection-title">E4 Norrtull — Ytinspektion</div>
<div class="inspection-meta">Anna Lindqvist · 2026-05-10 · <span style="color:var(--ios-green)">Godkänd</span></div>
@@ -1043,7 +1043,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<div class="inspection-chevron"></div>
</div>
<div class="inspection-item">
<div class="inspection-icon" style="background:rgba(255,149,0,0.15)">🌉</div>
<div class="inspection-icon" style="background:rgba(255,149,0,0.15)"></div>
<div class="inspection-body">
<div class="inspection-title">Öresundsbron — Strukturkontroll</div>
<div class="inspection-meta">Marcus Holm · 2026-04-28 · <span style="color:var(--ios-orange)">Åtgärder krävs</span></div>
@@ -1051,7 +1051,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<div class="inspection-chevron"></div>
</div>
<div class="inspection-item">
<div class="inspection-icon" style="background:rgba(255,59,48,0.15)">🏛️</div>
<div class="inspection-icon" style="background:rgba(255,59,48,0.15)"></div>
<div class="inspection-body">
<div class="inspection-title">Kungliga Slottet — Takinspektion</div>
<div class="inspection-meta">Sofia Berg · 2026-05-01 · <span style="color:var(--ios-red)">Kritisk rapport</span></div>
@@ -1059,7 +1059,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<div class="inspection-chevron"></div>
</div>
<div class="inspection-item">
<div class="inspection-icon" style="background:rgba(0,122,255,0.15)">✈️</div>
<div class="inspection-icon" style="background:rgba(0,122,255,0.15)"></div>
<div class="inspection-body">
<div class="inspection-title">Arlanda T5 — Fasadinspektion</div>
<div class="inspection-meta">Erik Svensson · 2026-05-16 · <span style="color:var(--ios-green)">Godkänd</span></div>
@@ -1160,15 +1160,15 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<!-- TAB BAR -->
<nav class="tab-bar">
<div class="tab-item active">
<div class="tab-icon">🏛️</div>
<div class="tab-icon"></div>
<div class="tab-label">Översikt</div>
</div>
<div class="tab-item" onclick="window.location.href='landvex-admin.html#objects'">
<div class="tab-icon">📋</div>
<div class="tab-icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="3" y="2" width="10" height="12" rx="1" stroke="#666" stroke-width="1.5"/><path d="M6 5H10M6 8H10M6 11H8" stroke="#666" stroke-width="1.5" stroke-linecap="round"/></svg></div>
<div class="tab-label">Objekt</div>
</div>
<div class="tab-item" onclick="window.location.href='landvex-admin.html#inspections'">
<div class="tab-icon">🔍</div>
<div class="tab-icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><circle cx="7" cy="7" r="5" stroke="#666" stroke-width="2"/><path d="M11 11L14 14" stroke="#666" stroke-width="2" stroke-linecap="round"/></svg></div>
<div class="tab-label">Inspektioner</div>
</div>
<div class="tab-item">
@@ -1176,7 +1176,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<div class="tab-label">Rapporter</div>
</div>
<div class="tab-item">
<div class="tab-icon">⚙️</div>
<div class="tab-icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><circle cx="8" cy="8" r="3" stroke="#666" stroke-width="1.5"/><path d="M8 2V4M8 12V14M2 8H4M12 8H14M4.3 4.3L5.7 5.7M10.3 10.3L11.7 11.7M4.3 11.7L5.7 10.3M10.3 5.7L11.7 4.3" stroke="#666" stroke-width="1.5" stroke-linecap="round"/></svg></div>
<div class="tab-label">Inställningar</div>
</div>
</nav>
@@ -1197,32 +1197,32 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<!-- DISPATCH MODAL -->
<div id="dispatch-modal" style="position:fixed;inset:0;background: rgba(242,242,247,0.5);display:flex;align-items:flex-end;z-index:1001;opacity:0;pointer-events:none;transition:opacity 0.25s;">
<div style="background:var(--background-primary);border-radius:20px 20px 0 0;padding:20px 20px 40px;width:100%;max-height:80vh;overflow-y:auto;transform:translateY(100%);transition:transform 0.35s cubic-bezier(0.34,1.56,0.64,1);" id="dispatch-sheet">
<div style="width:36px;height:4px;background:var(--fill-secondary);border-radius:2px;margin:0 auto 16px;"></div>
<div style="font-family:var(--font-display);font-size:22px;font-weight:700;letter-spacing:-0.4px;margin-bottom:16px;"> Snabb-dispatch</div>
<div style="background:var(--background-primary);border-radius: var(--radius-xl) 20px 0 0;padding:20px 20px 40px;width:100%;max-height:80vh;overflow-y:auto;transform:translateY(100%);transition:transform 0.35s cubic-bezier(0.34,1.56,0.64,1);" id="dispatch-sheet">
<div style="width:36px;height:4px;background:var(--fill-secondary);border-radius: var(--radius-sm);margin:0 auto 16px;"></div>
<div style="font-family:var(--font-display);font-size:22px;font-weight:700;letter-spacing:-0.4px;margin-bottom:16px;"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M9 2L5 9H8L7 14L11 7H8L9 2Z" fill="#f59e0b"/></svg> Snabb-dispatch</div>
<div style="background:var(--background-secondary);border-radius:12px;padding:14px;margin-bottom:12px;">
<div style="background:var(--background-secondary);border-radius: var(--radius-md);padding:14px;margin-bottom:12px;">
<div style="font-size:13px;font-weight:600;color:var(--label-secondary);text-transform:uppercase;letter-spacing:0.4px;margin-bottom:10px;">Närmaste lediga zoomer</div>
<div style="display:flex;align-items:center;gap:12px;padding:10px;background:rgba(52,199,89,0.08);border-radius:10px;border:0.5px solid rgba(52,199,89,0.3);margin-bottom:8px;">
<span style="font-size:24px;">🚁</span>
<div style="display:flex;align-items:center;gap:12px;padding:10px;background:rgba(52,199,89,0.08);border-radius: var(--radius-md);border:0.5px solid rgba(52,199,89,0.3);margin-bottom:8px;">
<span style="font-size:24px;"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M8 2V6M4 4H12" stroke="#666" stroke-width="1.5" stroke-linecap="round"/><path d="M2 8H6L8 6L10 8H14" stroke="#666" stroke-width="1.5" stroke-linejoin="round"/><path d="M6 8V12H10V8" stroke="#666" stroke-width="1.5"/></svg></span>
<div style="flex:1;">
<div style="font-size:16px;font-weight:600;">LX-04 · Stockholm N</div>
<div style="font-size:12px;color:var(--label-secondary);">4.2 km bort · Laddad 100% · HD + Multispektral</div>
</div>
<button onclick="confirmDispatch('LX-04')" style="background:var(--ios-green);color: #1d1d1f;border:none;border-radius:12px;padding:8px 14px;font-size:14px;font-weight:600;cursor:pointer;">Skicka</button>
<button onclick="confirmDispatch('LX-04')" style="background:var(--ios-green);color: #1d1d1f;border:none;border-radius: var(--radius-md);padding:8px 14px;font-size:14px;font-weight:600;cursor:pointer;">Skicka</button>
</div>
<div style="display:flex;align-items:center;gap:12px;padding:10px;background:var(--fill-quaternary);border-radius:10px;margin-bottom:8px;">
<span style="font-size:24px;">🚁</span>
<div style="display:flex;align-items:center;gap:12px;padding:10px;background:var(--fill-quaternary);border-radius: var(--radius-md);margin-bottom:8px;">
<span style="font-size:24px;"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M8 2V6M4 4H12" stroke="#666" stroke-width="1.5" stroke-linecap="round"/><path d="M2 8H6L8 6L10 8H14" stroke="#666" stroke-width="1.5" stroke-linejoin="round"/><path d="M6 8V12H10V8" stroke="#666" stroke-width="1.5"/></svg></span>
<div style="flex:1;">
<div style="font-size:16px;font-weight:600;">LX-07 · Nacka</div>
<div style="font-size:12px;color:var(--label-secondary);">8.7 km bort · Laddad 87% · HD</div>
</div>
<button onclick="confirmDispatch('LX-07')" style="background:var(--ios-blue);color: #1d1d1f;border:none;border-radius:12px;padding:8px 14px;font-size:14px;font-weight:600;cursor:pointer;">Skicka</button>
<button onclick="confirmDispatch('LX-07')" style="background:var(--ios-blue);color: #1d1d1f;border:none;border-radius: var(--radius-md);padding:8px 14px;font-size:14px;font-weight:600;cursor:pointer;">Skicka</button>
</div>
</div>
<div id="dispatch-success" style="display:none;text-align:center;padding:16px;background:rgba(52,199,89,0.1);border-radius:14px;color:var(--ios-green);font-size:16px;font-weight:600;"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#00C853" stroke-width="3"><path d="M20 6L9 17l-5-5"/></svg> Dispatch bekräftad! Zoomer anländer inom 12 min.</div>
<button onclick="closeDispatch()" style="width:100%;padding:14px;background:var(--fill-tertiary);color:var(--label-primary);border:none;border-radius:14px;font-size:17px;font-weight:600;cursor:pointer;margin-top:8px;">Stäng</button>
<div id="dispatch-success" style="display:none;text-align:center;padding:16px;background:rgba(52,199,89,0.1);border-radius: var(--radius-lg);color:var(--ios-green);font-size:16px;font-weight:600;"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#00C853" stroke-width="3"><path d="M20 6L9 17l-5-5"/></svg> Dispatch bekräftad! Zoomer anländer inom 12 min.</div>
<button onclick="closeDispatch()" style="width:100%;padding:14px;background:var(--fill-tertiary);color:var(--label-primary);border:none;border-radius: var(--radius-lg);font-size:17px;font-weight:600;cursor:pointer;margin-top:8px;">Stäng</button>
</div>
</div>
@@ -1241,8 +1241,8 @@ function submitInspection() {
document.getElementById('modal').classList.remove('open');
// Toast feedback
const toast = document.createElement('div');
toast.style.cssText = 'position:fixed;bottom:80px;left:50%;transform:translateX(-50%);background:var(--ios-green);color:white;padding:12px 20px;border-radius:20px;font-size:15px;font-weight:600;z-index:500;box-shadow:0 4px 16px rgba(52,199,89,0.4);';
toast.textContent = ' Inspektion skapad';
toast.style.cssText = 'position:fixed;bottom:80px;left:50%;transform:translateX(-50%);background:var(--ios-green);color:white;padding:12px 20px;border-radius: var(--radius-xl);font-size:15px;font-weight:600;z-index:500;box-shadow:0 4px 16px rgba(52,199,89,0.4);';
toast.textContent = '<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Inspektion skapad';
document.body.appendChild(toast);
setTimeout(() => toast.remove(), 2500);
}
+35 -35
View File
@@ -11,9 +11,9 @@
<title>LandveX — Mina objekt</title>
<link rel="stylesheet" >
<style>
/* ═══════════════════════════════════════════════════════
/*
LANDVEX KUNDPORTAL — iOS Native
═══════════════════════════════════════════════════════ */
*/
* { box-sizing: border-box; margin: 0; padding: 0; -webkit-font-smoothing: antialiased; }
@@ -24,7 +24,7 @@ body {
min-height: 100vh;
}
/* ── LARGE TITLE NAV ── */
/* LARGE TITLE NAV */
.nav-bar {
position: sticky;
top: 0;
@@ -63,7 +63,7 @@ body {
.nav-avatar {
width: 32px;
height: 32px;
border-radius: 50%;
border-radius: var(--radius-full);
background: linear-gradient(135deg, #007AFF, #5856D6);
display: flex;
align-items: center;
@@ -74,7 +74,7 @@ body {
cursor: pointer;
}
/* ── SEARCH BAR ── */
/* SEARCH BAR */
.search-bar {
padding: 0 16px 12px;
}
@@ -84,7 +84,7 @@ body {
align-items: center;
gap: 8px;
background: var(--fill-primary);
border-radius: 12px;
border-radius: var(--radius-md);
padding: 0 12px;
height: 36px;
}
@@ -103,10 +103,10 @@ body {
.search-input::placeholder { color: var(--label-tertiary); }
/* ── CONTENT ── */
/* CONTENT */
.content { padding: 0 16px 100px; }
/* ── INSET GROUP ── */
/* INSET GROUP */
.inset-group {
margin-bottom: 28px;
}
@@ -123,18 +123,18 @@ body {
.inset-card {
background: var(--grouped-background-secondary);
border-radius: 16px;
border-radius: var(--radius-lg);
overflow: hidden;
border: 0.5px solid var(--separator);
}
/* ── HERO STATS ── */
/* HERO STATS */
.hero-stats {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1px;
background: var(--separator);
border-radius: 16px;
border-radius: var(--radius-lg);
overflow: hidden;
margin-bottom: 28px;
border: 0.5px solid var(--separator);
@@ -159,7 +159,7 @@ body {
margin-top: 2px;
}
/* ── OBJECT CARD ── */
/* OBJECT CARD */
.object-card {
display: flex;
align-items: center;
@@ -176,7 +176,7 @@ body {
.object-thumb {
width: 52px;
height: 52px;
border-radius: 12px;
border-radius: var(--radius-md);
display: flex;
align-items: center;
justify-content: center;
@@ -213,7 +213,7 @@ body {
font-size: 13px;
font-weight: 700;
padding: 4px 10px;
border-radius: 20px;
border-radius: var(--radius-xl);
letter-spacing: -0.2px;
}
@@ -231,7 +231,7 @@ body {
color: var(--label-quaternary);
}
/* ── INSPECTION HISTORY ── */
/* INSPECTION HISTORY */
.history-item {
display: flex;
align-items: flex-start;
@@ -253,7 +253,7 @@ body {
.history-dot {
width: 10px;
height: 10px;
border-radius: 50%;
border-radius: var(--radius-full);
flex-shrink: 0;
}
@@ -286,7 +286,7 @@ body {
margin-top: 4px;
}
/* ── ACTION ROW ── */
/* ACTION ROW */
.action-row {
display: flex;
padding: 14px 16px;
@@ -305,14 +305,14 @@ body {
color: var(--ios-blue);
}
/* ── ORDER INSPECTION BUTTON ── */
/* ORDER INSPECTION BUTTON */
.order-btn {
width: 100%;
height: 54px;
background: var(--ios-blue);
color: white;
border: none;
border-radius: 14px;
border-radius: var(--radius-lg);
font-size: 17px;
font-weight: 600;
font-family: var(--font-text);
@@ -327,11 +327,11 @@ body {
transform: scale(0.98);
}
/* ── NOTIFICATION BANNER ── */
/* NOTIFICATION BANNER */
.alert-banner {
background: rgba(255,59,48,0.1);
border: 0.5px solid rgba(255,59,48,0.3);
border-radius: 12px;
border-radius: var(--radius-md);
padding: 12px 14px;
margin-bottom: 16px;
display: flex;
@@ -353,7 +353,7 @@ body {
margin-top: 2px;
}
/* ── TAB BAR ── */
/* TAB BAR */
.tab-bar {
position: fixed;
bottom: 0;
@@ -430,7 +430,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
</div>
<div class="search-bar">
<div class="search-input-wrap">
<span class="search-icon">🔍</span>
<span class="search-icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><circle cx="7" cy="7" r="5" stroke="#666" stroke-width="2"/><path d="M11 11L14 14" stroke="#666" stroke-width="2" stroke-linecap="round"/></svg></span>
<input class="search-input" placeholder="Sök objekt..." type="search">
</div>
</div>
@@ -470,7 +470,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<div class="inset-card">
<div class="object-card" onclick="showDetail('E4 Norrtull Avsnitt 12')">
<div class="object-thumb" style="background:rgba(255,149,0,0.15)">🛣️</div>
<div class="object-thumb" style="background:rgba(255,149,0,0.15)"></div>
<div class="object-body">
<div class="object-name">E4 Norrtull Avsnitt 12</div>
<div class="object-meta">Stockholm · Väg</div>
@@ -483,7 +483,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
</div>
<div class="object-card" onclick="showDetail('Gamla Stan Bro')">
<div class="object-thumb" style="background:rgba(52,199,89,0.15)">🌉</div>
<div class="object-thumb" style="background:rgba(52,199,89,0.15)"></div>
<div class="object-body">
<div class="object-name">Gamla Stan Bro</div>
<div class="object-meta">Stockholm · Bro</div>
@@ -496,7 +496,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
</div>
<div class="object-card" onclick="showDetail('Kungliga Slottet Tak')">
<div class="object-thumb" style="background:rgba(255,59,48,0.15)">🏛️</div>
<div class="object-thumb" style="background:rgba(255,59,48,0.15)"></div>
<div class="object-body">
<div class="object-name">Kungliga Slottet Tak</div>
<div class="object-meta">Stockholm · Byggnad</div>
@@ -509,7 +509,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
</div>
<div class="object-card" onclick="showDetail('Arlanda Terminal 5')">
<div class="object-thumb" style="background:rgba(0,122,255,0.15)">✈️</div>
<div class="object-thumb" style="background:rgba(0,122,255,0.15)"></div>
<div class="object-body">
<div class="object-name">Arlanda Terminal 5</div>
<div class="object-meta">Sigtuna · Byggnad</div>
@@ -522,7 +522,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
</div>
<div class="object-card" onclick="showDetail('Öresundsbron Sektion 3')">
<div class="object-thumb" style="background:rgba(255,149,0,0.15)">🌉</div>
<div class="object-thumb" style="background:rgba(255,149,0,0.15)"></div>
<div class="object-body">
<div class="object-name">Öresundsbron Sektion 3</div>
<div class="object-meta">Malmö · Bro</div>
@@ -539,7 +539,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<!-- BESTÄLL INSPEKTION -->
<button class="order-btn" onclick="orderInspection()">
📋 Beställ ny inspektion
<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="3" y="2" width="10" height="12" rx="1" stroke="#666" stroke-width="1.5"/><path d="M6 5H10M6 8H10M6 11H8" stroke="#666" stroke-width="1.5" stroke-linecap="round"/></svg> Beställ ny inspektion
</button>
<!-- INSPECTION HISTORY -->
@@ -637,11 +637,11 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<!-- TAB BAR -->
<nav class="tab-bar">
<div class="tab-item active">
<div class="tab-icon">🏠</div>
<div class="tab-icon"></div>
<div class="tab-label">Mina objekt</div>
</div>
<div class="tab-item">
<div class="tab-icon">🔍</div>
<div class="tab-icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><circle cx="7" cy="7" r="5" stroke="#666" stroke-width="2"/><path d="M11 11L14 14" stroke="#666" stroke-width="2" stroke-linecap="round"/></svg></div>
<div class="tab-label">Inspektioner</div>
</div>
<div class="tab-item">
@@ -649,11 +649,11 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<div class="tab-label">Rapporter</div>
</div>
<div class="tab-item">
<div class="tab-icon">🔔</div>
<div class="tab-icon"></div>
<div class="tab-label">Notiser</div>
</div>
<div class="tab-item">
<div class="tab-icon">👤</div>
<div class="tab-icon"></div>
<div class="tab-label">Konto</div>
</div>
</nav>
@@ -675,8 +675,8 @@ function orderInspection() {
const msg = `Beställ inspektion\n\nVälj objekt och datum i nästa steg.\nEn LandveX-inspektör kontaktar dig inom 24 timmar.\n\n[Bekräfta beställning]`;
if (confirm(msg)) {
const toast = document.createElement('div');
toast.style.cssText = 'position:fixed;bottom:80px;left:50%;transform:translateX(-50%);background:var(--ios-blue);color:white;padding:12px 20px;border-radius:20px;font-size:15px;font-weight:600;z-index:500;box-shadow:0 4px 16px rgba(0,122,255,0.4);white-space:nowrap;';
toast.textContent = ' Beställning skickad';
toast.style.cssText = 'position:fixed;bottom:80px;left:50%;transform:translateX(-50%);background:var(--ios-blue);color:white;padding:12px 20px;border-radius: var(--radius-xl);font-size:15px;font-weight:600;z-index:500;box-shadow:0 4px 16px rgba(0,122,255,0.4);white-space:nowrap;';
toast.textContent = '<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Beställning skickad';
document.body.appendChild(toast);
setTimeout(() => toast.remove(), 2500);
}
+67 -67
View File
@@ -11,9 +11,9 @@
<title>LandveX — Inspektörsportal</title>
<link rel="stylesheet" >
<style>
/* ═══════════════════════════════════════════════════════
/*
LANDVEX INSPEKTÖRSPORTAL — iOS Native
═══════════════════════════════════════════════════════ */
*/
* { box-sizing: border-box; margin: 0; padding: 0; -webkit-font-smoothing: antialiased; }
@@ -25,7 +25,7 @@ body {
padding-bottom: calc(60px + env(safe-area-inset-bottom));
}
/* ── NAV ── */
/* NAV */
.nav-bar {
position: sticky;
top: 0;
@@ -54,7 +54,7 @@ body {
width: 28px;
height: 28px;
background: linear-gradient(135deg, #0055CC, #007AFF);
border-radius: 7px;
border-radius: var(--radius-sm);
display: flex;
align-items: center;
justify-content: center;
@@ -75,11 +75,11 @@ body {
padding: 3px 7px;
background: rgba(88,86,214,0.15);
color: var(--ios-indigo);
border-radius: 20px;
border-radius: var(--radius-xl);
letter-spacing: 0.3px;
}
/* ── DATE HEADER ── */
/* DATE HEADER */
.date-header {
padding: 16px 16px 4px;
font-size: 13px;
@@ -97,11 +97,11 @@ body {
padding: 2px 16px 16px;
}
/* ── MISSION CARD ── */
/* MISSION CARD */
.mission-card {
margin: 0 16px 12px;
background: var(--grouped-background-secondary);
border-radius: 16px;
border-radius: var(--radius-lg);
border: 0.5px solid var(--separator);
overflow: hidden;
}
@@ -133,7 +133,7 @@ body {
.mission-number {
width: 44px;
height: 44px;
border-radius: 50%;
border-radius: var(--radius-full);
background: linear-gradient(135deg, #007AFF, #0055CC);
display: flex;
align-items: center;
@@ -187,7 +187,7 @@ body {
background: var(--ios-green);
color: white;
border: none;
border-radius: 12px;
border-radius: var(--radius-md);
font-size: 15px;
font-weight: 600;
font-family: var(--font-text);
@@ -203,14 +203,14 @@ body {
background: var(--fill-secondary);
color: var(--ios-blue);
border: none;
border-radius: 12px;
border-radius: var(--radius-md);
font-size: 15px;
font-weight: 600;
font-family: var(--font-text);
cursor: pointer;
}
/* ── FORM SECTIONS ── */
/* FORM SECTIONS */
.form-section {
margin: 24px 16px 0;
}
@@ -224,7 +224,7 @@ body {
.form-card {
background: var(--grouped-background-secondary);
border-radius: 16px;
border-radius: var(--radius-lg);
border: 0.5px solid var(--separator);
overflow: hidden;
}
@@ -291,7 +291,7 @@ body {
.form-textarea::placeholder { color: var(--label-quaternary); }
/* ── CHECKLIST ── */
/* CHECKLIST */
.checklist-item {
display: flex;
align-items: center;
@@ -308,7 +308,7 @@ body {
.check-box {
width: 24px;
height: 24px;
border-radius: 50%;
border-radius: var(--radius-full);
border: 2px solid var(--ios-blue);
flex-shrink: 0;
display: flex;
@@ -336,7 +336,7 @@ body {
.checklist-text.checked { color: var(--label-secondary); text-decoration: line-through; }
/* ── PHOTO UPLOAD ── */
/* PHOTO UPLOAD */
.photo-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
@@ -346,7 +346,7 @@ body {
.photo-thumb {
aspect-ratio: 1;
border-radius: 8px;
border-radius: var(--radius-md);
overflow: hidden;
background: var(--fill-secondary);
display: flex;
@@ -387,7 +387,7 @@ body {
background: var(--fill-secondary);
}
/* ── SUBMIT AREA ── */
/* SUBMIT AREA */
.submit-area {
padding: 20px 16px 32px;
}
@@ -398,7 +398,7 @@ body {
background: linear-gradient(135deg, #007AFF, #0055CC);
color: white;
border: none;
border-radius: 14px;
border-radius: var(--radius-lg);
font-size: 17px;
font-weight: 700;
font-family: var(--font-display);
@@ -420,7 +420,7 @@ body {
margin-top: 10px;
}
/* ── PROGRESS BAR ── */
/* PROGRESS BAR */
.progress-wrap {
padding: 0 16px 16px;
}
@@ -436,18 +436,18 @@ body {
.progress-track {
height: 4px;
background: var(--fill-tertiary);
border-radius: 2px;
border-radius: var(--radius-sm);
overflow: hidden;
}
.progress-fill {
height: 100%;
border-radius: 2px;
border-radius: var(--radius-sm);
background: var(--ios-blue);
transition: width 0.4s;
}
/* ── TAB BAR ── */
/* TAB BAR */
.tab-bar {
position: fixed;
bottom: 0;
@@ -482,7 +482,7 @@ body {
.tab-label { font-size: 10px; font-weight: 500; color: var(--label-secondary); }
.tab-item.active .tab-label { color: var(--ios-blue); }
/* ── NOTIFICATION DOT ── */
/* NOTIFICATION DOT */
.notif-dot {
position: relative;
}
@@ -498,7 +498,7 @@ body {
font-weight: 700;
width: 16px;
height: 16px;
border-radius: 8px;
border-radius: var(--radius-md);
display: flex;
align-items: center;
justify-content: center;
@@ -543,7 +543,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<span class="brand-label">LandveX</span>
</div>
<span class="badge-inspector">INSPEKTÖR</span>
<div style="width:32px;height:32px;border-radius:50%;background:linear-gradient(135deg,#5856D6,#AF52DE);display:flex;align-items:center;justify-content:center;color:white;font-size:14px;font-weight:700;">M</div>
<div style="width:32px;height:32px;border-radius: var(--radius-full);background:linear-gradient(135deg,#5856D6,#AF52DE);display:flex;align-items:center;justify-content:center;color:white;font-size:14px;font-weight:700;">M</div>
</div>
</nav>
@@ -556,35 +556,35 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<div class="mission-header">
<div>
<div class="mission-title">E4 Norrtull Avsnitt 12</div>
<div class="mission-sub">🛣️ Ytinspektion · Stockholm</div>
<div class="mission-sub"> Ytinspektion · Stockholm</div>
</div>
<div class="mission-number">1</div>
</div>
<div class="mission-body">
<div class="mission-row">
<div class="mission-row-icon">🕐</div>
<div class="mission-row-icon"></div>
<div class="mission-row-label">Starttid</div>
<div class="mission-row-value">09:00</div>
</div>
<div class="mission-row">
<div class="mission-row-icon"></div>
<div class="mission-row-icon"></div>
<div class="mission-row-label">Beräknad tid</div>
<div class="mission-row-value">90 min</div>
</div>
<div class="mission-row">
<div class="mission-row-icon">📍</div>
<div class="mission-row-icon"></div>
<div class="mission-row-label">Plats</div>
<div class="mission-row-value">E4, km 164166</div>
</div>
<div class="mission-row">
<div class="mission-row-icon">📋</div>
<div class="mission-row-icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="3" y="2" width="10" height="12" rx="1" stroke="#666" stroke-width="1.5"/><path d="M6 5H10M6 8H10M6 11H8" stroke="#666" stroke-width="1.5" stroke-linecap="round"/></svg></div>
<div class="mission-row-label">Typ</div>
<div class="mission-row-value">Standard ytinspektion</div>
</div>
</div>
<div class="mission-actions">
<button class="btn-start" onclick="startMission(1)"> Starta uppdrag</button>
<button class="btn-nav" onclick="navigate('E4 Norrtull')">🗺️ Navigera</button>
<button class="btn-start" onclick="startMission(1)"> Starta uppdrag</button>
<button class="btn-nav" onclick="navigate('E4 Norrtull')"> Navigera</button>
</div>
</div>
@@ -593,23 +593,23 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<div class="mission-header">
<div>
<div class="mission-title">Kungliga Slottet Tak</div>
<div class="mission-sub">🏛️ Takinspektion · Kritisk · Stockholm</div>
<div class="mission-sub"> Takinspektion · Kritisk · Stockholm</div>
</div>
<div class="mission-number" style="background:linear-gradient(135deg,#FF3B30,#CC1100)">2</div>
</div>
<div class="mission-body">
<div class="mission-row">
<div class="mission-row-icon">🕐</div>
<div class="mission-row-icon"></div>
<div class="mission-row-label">Starttid</div>
<div class="mission-row-value">13:00</div>
</div>
<div class="mission-row">
<div class="mission-row-icon"></div>
<div class="mission-row-icon"></div>
<div class="mission-row-label">Beräknad tid</div>
<div class="mission-row-value">120 min</div>
</div>
<div class="mission-row">
<div class="mission-row-icon">📍</div>
<div class="mission-row-icon"></div>
<div class="mission-row-label">Plats</div>
<div class="mission-row-value">Kungliga Slottet, Gamla Stan</div>
</div>
@@ -620,8 +620,8 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
</div>
</div>
<div class="mission-actions">
<button class="btn-start" onclick="startMission(2)" style="background:var(--ios-red)"> Starta uppdrag</button>
<button class="btn-nav" onclick="navigate('Kungliga Slottet')">🗺️ Navigera</button>
<button class="btn-start" onclick="startMission(2)" style="background:var(--ios-red)"> Starta uppdrag</button>
<button class="btn-nav" onclick="navigate('Kungliga Slottet')"> Navigera</button>
</div>
</div>
@@ -640,7 +640,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<!-- INSPECTION FORM -->
<div class="form-section" id="inspectionForm" style="display:none;">
<div class="section-title">📋 Inspektionsprotokoll</div>
<div class="section-title"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="3" y="2" width="10" height="12" rx="1" stroke="#666" stroke-width="1.5"/><path d="M6 5H10M6 8H10M6 11H8" stroke="#666" stroke-width="1.5" stroke-linecap="round"/></svg> Inspektionsprotokoll</div>
<div class="form-card" style="margin-bottom:12px;">
<div class="form-row">
@@ -663,11 +663,11 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<div class="form-row">
<div class="form-row-label">Väder</div>
<select class="form-row-select">
<option>☀️ Klart</option>
<option>🌥️ Molnigt</option>
<option>🌧️ Regn</option>
<option>❄️ Snö</option>
<option>🌬️ Blåsigt</option>
<option> Klart</option>
<option> Molnigt</option>
<option> Regn</option>
<option> Snö</option>
<option> Blåsigt</option>
</select>
</div>
<div class="form-row">
@@ -675,7 +675,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<select class="form-row-select" id="riskLevel">
<option value="low"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#00C853" stroke-width="3"><path d="M20 6L9 17l-5-5"/></svg> Låg (030)</option>
<option value="medium"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#f59e0b" stroke-width="2"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg> Medium (3165)</option>
<option value="high">🔴 Hög (66100)</option>
<option value="high"> Hög (66100)</option>
</select>
</div>
</div>
@@ -684,27 +684,27 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<div style="font-size:13px;font-weight:600;color:var(--label-secondary);text-transform:uppercase;letter-spacing:0.5px;margin-bottom:6px;padding:0 4px;">Checklista</div>
<div class="form-card" style="margin-bottom:12px;">
<div class="checklist-item" onclick="toggleCheck(this)">
<div class="check-box"><span class="check-mark"></span></div>
<div class="check-box"><span class="check-mark"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></span></div>
<div class="checklist-text">Yttre skador dokumenterade</div>
</div>
<div class="checklist-item" onclick="toggleCheck(this)">
<div class="check-box"><span class="check-mark"></span></div>
<div class="check-box"><span class="check-mark"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></span></div>
<div class="checklist-text">Strukturell integritet kontrollerad</div>
</div>
<div class="checklist-item" onclick="toggleCheck(this)">
<div class="check-box"><span class="check-mark"></span></div>
<div class="check-box"><span class="check-mark"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></span></div>
<div class="checklist-text">Dränering och avlopp inspekterade</div>
</div>
<div class="checklist-item" onclick="toggleCheck(this)">
<div class="check-box"><span class="check-mark"></span></div>
<div class="check-box"><span class="check-mark"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></span></div>
<div class="checklist-text">Korrosion och fuktskador noterade</div>
</div>
<div class="checklist-item" onclick="toggleCheck(this)">
<div class="check-box"><span class="check-mark"></span></div>
<div class="check-box"><span class="check-mark"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></span></div>
<div class="checklist-text">Säkerhetsrisker bedömda</div>
</div>
<div class="checklist-item" onclick="toggleCheck(this)">
<div class="check-box"><span class="check-mark"></span></div>
<div class="check-box"><span class="check-mark"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></span></div>
<div class="checklist-text">Jämförelse med föregående inspektion</div>
</div>
</div>
@@ -714,10 +714,10 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<div class="form-card" style="margin-bottom:12px;">
<div class="photo-grid">
<div class="photo-thumb" onclick="addPhoto(this)">
<div class="photo-placeholder">📸</div>
<div class="photo-placeholder"></div>
</div>
<div class="photo-thumb" onclick="addPhoto(this)">
<div class="photo-placeholder">📸</div>
<div class="photo-placeholder"></div>
</div>
<div class="photo-thumb add-photo" onclick="takePhoto()">
<div class="add-icon">+</div>
@@ -745,7 +745,7 @@ Exempel:
<!-- SUBMIT -->
<div class="submit-area">
<button class="submit-btn" onclick="submitReport()">
Skicka inspektionsrapport
<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Skicka inspektionsrapport
</button>
<div class="submit-note">Rapporten skickas till LandveX och objektägaren. Du kan ej ändra efter inskickning.</div>
</div>
@@ -753,7 +753,7 @@ Exempel:
<!-- PLACEHOLDER WHEN NO FORM -->
<div id="formPlaceholder" style="padding: 32px 16px; text-align: center; color: var(--label-tertiary);">
<div style="font-size: 48px; margin-bottom: 12px;">📋</div>
<div style="font-size: 48px; margin-bottom: 12px;"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="3" y="2" width="10" height="12" rx="1" stroke="#666" stroke-width="1.5"/><path d="M6 5H10M6 8H10M6 11H8" stroke="#666" stroke-width="1.5" stroke-linecap="round"/></svg></div>
<div style="font-size: 17px; font-weight: 600; color: var(--label-secondary);">Protokollformulär</div>
<div style="font-size: 14px; margin-top: 6px;">Starta ett uppdrag ovan för att öppna inspektionsformuläret.</div>
</div>
@@ -761,23 +761,23 @@ Exempel:
<!-- TAB BAR -->
<nav class="tab-bar">
<div class="tab-item active">
<div class="tab-icon">📅</div>
<div class="tab-icon"></div>
<div class="tab-label">Uppdrag</div>
</div>
<div class="tab-item" onclick="showForm()">
<div class="tab-icon">📋</div>
<div class="tab-icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="3" y="2" width="10" height="12" rx="1" stroke="#666" stroke-width="1.5"/><path d="M6 5H10M6 8H10M6 11H8" stroke="#666" stroke-width="1.5" stroke-linecap="round"/></svg></div>
<div class="tab-label">Protokoll</div>
</div>
<div class="tab-item">
<div class="tab-icon notif-dot">🔔</div>
<div class="tab-icon notif-dot"></div>
<div class="tab-label">Notiser</div>
</div>
<div class="tab-item">
<div class="tab-icon">📁</div>
<div class="tab-icon"></div>
<div class="tab-label">Arkiv</div>
</div>
<div class="tab-item">
<div class="tab-icon">👤</div>
<div class="tab-icon"></div>
<div class="tab-label">Profil</div>
</div>
</nav>
@@ -803,8 +803,8 @@ function startMission(n) {
// Feedback
const toast = document.createElement('div');
toast.style.cssText = 'position:fixed;top:60px;left:50%;transform:translateX(-50%);background:#F2F2F7;color:white;padding:10px 18px;border-radius:20px;font-size:14px;font-weight:600;z-index:500;';
toast.textContent = ` Uppdrag ${n} startat`;
toast.style.cssText = 'position:fixed;top:60px;left:50%;transform:translateX(-50%);background:#F2F2F7;color:white;padding:10px 18px;border-radius: var(--radius-xl);font-size:14px;font-weight:600;z-index:500;';
toast.textContent = ` Uppdrag ${n} startat`;
document.body.appendChild(toast);
setTimeout(() => toast.remove(), 2000);
}
@@ -830,7 +830,7 @@ function updateProgress() {
function addPhoto(thumb) {
// Simulate photo capture
const emojis = ['🌉','🏗️','<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#f59e0b" stroke-width="2"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>','📐','🔩','🧱'];
const emojis = ['','<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 14H14M4 14V8L8 4L12 8V14" stroke="#666" stroke-width="1.5" stroke-linejoin="round"/><path d="M6 14V10H10V14" stroke="#666" stroke-width="1.5"/></svg>','<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#f59e0b" stroke-width="2"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>','','',''];
thumb.innerHTML = `<div style="font-size:32px">${emojis[Math.floor(Math.random()*emojis.length)]}</div>`;
thumb.style.background = 'var(--fill-tertiary)';
}
@@ -842,7 +842,7 @@ function takePhoto() {
const newThumb = document.createElement('div');
newThumb.className = 'photo-thumb';
newThumb.onclick = function() { addPhoto(this); };
const emojis = ['🌉','🏗️','🏛️','📍','🔍','📐'];
const emojis = ['','<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 14H14M4 14V8L8 4L12 8V14" stroke="#666" stroke-width="1.5" stroke-linejoin="round"/><path d="M6 14V10H10V14" stroke="#666" stroke-width="1.5"/></svg>','','','<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><circle cx="7" cy="7" r="5" stroke="#666" stroke-width="2"/><path d="M11 11L14 14" stroke="#666" stroke-width="2" stroke-linecap="round"/></svg>',''];
newThumb.innerHTML = `<div style="font-size:32px">${emojis[Math.floor(Math.random()*emojis.length)]}</div>`;
newThumb.style.background = 'var(--fill-secondary)';
@@ -877,8 +877,8 @@ function submitReport() {
// Feedback
const toast = document.createElement('div');
toast.style.cssText = 'position:fixed;bottom:80px;left:50%;transform:translateX(-50%);background:var(--ios-green);color:white;padding:14px 24px;border-radius:20px;font-size:15px;font-weight:600;z-index:500;box-shadow:0 4px 20px rgba(52,199,89,0.4);text-align:center;';
toast.innerHTML = ` Rapport inskickad<br><span style="font-size:12px;opacity:0.85">${objekt}</span>`;
toast.style.cssText = 'position:fixed;bottom:80px;left:50%;transform:translateX(-50%);background:var(--ios-green);color:white;padding:14px 24px;border-radius: var(--radius-xl);font-size:15px;font-weight:600;z-index:500;box-shadow:0 4px 20px rgba(52,199,89,0.4);text-align:center;';
toast.innerHTML = `<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Rapport inskickad<br><span style="font-size:12px;opacity:0.85">${objekt}</span>`;
document.body.appendChild(toast);
setTimeout(() => toast.remove(), 3000);
+1 -1
View File
@@ -28,7 +28,7 @@
.sign-box { flex: 1; }
.sign-line { border-bottom: 1px solid #111; margin-bottom: 6px; height: 40px; }
.sign-label { font-size: 9.5pt; color: #555; }
.note-box { background: #fff8e1; border: 1px solid #f0c040; padding: 10px 14px; border-radius: 4px; font-size: 9.5pt; margin: 20px 0; line-height: 1.6; }
.note-box { background: #fff8e1; border: 1px solid #f0c040; padding: 10px 14px; border-radius: var(--radius-sm); font-size: 9.5pt; margin: 20px 0; line-height: 1.6; }
@media (max-width: 640px) {
.hero h1 { font-size: 2rem !important; }
.hero p { font-size: 1rem !important; }
+36 -36
View File
@@ -35,23 +35,23 @@ html,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,'SF Pro Text'
#city-name{font-size:24px;font-weight:900;letter-spacing:-0.8px;color:var(--text);line-height:1}
#city-region{font-size:11px;color:var(--t2);margin-top:3px;font-weight:500}
#city-stats{display:grid;grid-template-columns:1fr 1fr;gap:6px;margin-top:12px}
.cstat{background:var(--s2);border:1px solid var(--border);border-radius:7px;padding:8px 10px}
.cstat{background:var(--s2);border:1px solid var(--border);border-radius: var(--radius-sm);padding:8px 10px}
.cstat-val{font-size:16px;font-weight:800;color:var(--text);letter-spacing:-0.3px}
.cstat-lbl{font-size:9px;font-weight:600;letter-spacing:0.5px;text-transform:uppercase;color:var(--t3);margin-top:1px}
#profile-block{padding:12px 16px;border-bottom:1px solid var(--border)}
#profile-tabs{display:grid;grid-template-columns:1fr 1fr 1fr;gap:3px;background:rgba(0,0,0,0.06);border-radius:10px;padding:2px}
.ptab{padding:6px 4px;border-radius:8px;border:none;background:transparent;cursor:pointer;font-size:10px;font-weight:600;color:var(--t2);transition:all 0.15s}
#profile-tabs{display:grid;grid-template-columns:1fr 1fr 1fr;gap:3px;background:rgba(0,0,0,0.06);border-radius: var(--radius-md);padding:2px}
.ptab{padding:6px 4px;border-radius: var(--radius-md);border:none;background:transparent;cursor:pointer;font-size:10px;font-weight:600;color:var(--t2);transition:all 0.15s}
.ptab:hover{background:var(--s2)}
.ptab.active{background:#fff;color:var(--text);box-shadow:0 1px 4px rgba(0,0,0,0.12)}
#district-block{padding:12px 16px}
#district-lbl{font-size:9px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:var(--t3);margin-bottom:8px}
.dcard{border-radius:12px;background:var(--s2);padding:11px 13px;margin-bottom:6px;cursor:pointer;transition:all 0.12s}
.dcard{border-radius: var(--radius-md);background:var(--s2);padding:11px 13px;margin-bottom:6px;cursor:pointer;transition:all 0.12s}
.dcard:hover,.dcard.active{background:var(--s3)}
.dcard-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:3px}
.dcard-name{font-size:13px;font-weight:700;color:var(--text)}
.dcard-score{font-size:11px;font-weight:800;padding:1px 7px;border-radius:4px;font-family:var(--mono)}
.dcard-score{font-size:11px;font-weight:800;padding:1px 7px;border-radius: var(--radius-sm);font-family:var(--mono)}
.dcard-desc{font-size:12px;color:var(--t2);line-height:1.4;margin-bottom:5px}
.dcard-contra{display:inline-flex;align-items:center;gap:4px;font-size:9px;font-weight:700;border-radius:4px;padding:2px 7px}
.dcard-contra{display:inline-flex;align-items:center;gap:4px;font-size:9px;font-weight:700;border-radius: var(--radius-sm);padding:2px 7px}
.dc-low{background:rgba(22,163,74,0.12);color:#166534}
.dc-med{background:rgba(217,119,6,0.12);color:#92400e}
.dc-high{background:rgba(220,38,38,0.12);color:#991b1b}
@@ -61,7 +61,7 @@ html,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,'SF Pro Text'
.fresh-row:last-child{border-bottom:none}
.fresh-source{font-size:10px;color:var(--t2);font-weight:500}
.fresh-val{font-size:9px;font-weight:700;font-family:var(--mono);display:flex;align-items:center;gap:4px}
.fresh-dot{width:5px;height:5px;border-radius:50%}
.fresh-dot{width:5px;height:5px;border-radius: var(--radius-full)}
.fresh-dot.live{background:#22c55e;animation:pulse-ring 1.5s ease-in-out infinite}
.fresh-dot.recent{background:#fbbf24}
#map-wrap{position:relative;overflow:hidden;background:var(--bg)}
@@ -71,13 +71,13 @@ html,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,'SF Pro Text'
#map-logo em{color:var(--accent3);font-style:normal}
.map-sep{width:1px;height:16px;background:rgba(0,0,0,0.12)}
#map-city-label{font-size:12px;font-weight:700;color:var(--text);flex:1}
#map-profile-chip{font-size:10px;font-weight:700;border-radius:4px;padding:3px 10px;border:1px solid}
#map-profile-chip{font-size:10px;font-weight:700;border-radius: var(--radius-sm);padding:3px 10px;border:1px solid}
#layer-bar{position:absolute;top:44px;left:0;right:0;z-index:999;display:flex;align-items:center;gap:4px;padding:5px 8px;background:#fff;border-bottom:1px solid rgba(0,0,0,0.1);overflow-x:auto;scrollbar-width:none}
.ltoggle{display:flex;align-items:center;gap:5px;font-size:11px;font-weight:600;color:var(--t2);background:rgba(0,0,0,0.05);border:none;border-radius:20px;padding:5px 12px;cursor:pointer;white-space:nowrap;user-select:none;flex-shrink:0}
.ltoggle{display:flex;align-items:center;gap:5px;font-size:11px;font-weight:600;color:var(--t2);background:rgba(0,0,0,0.05);border:none;border-radius: var(--radius-xl);padding:5px 12px;cursor:pointer;white-space:nowrap;user-select:none;flex-shrink:0}
.ltoggle.on{background:var(--accent);color: #1d1d1f}
.ltoggle-dot{width:6px;height:6px;border-radius:50%;flex-shrink:0}
.ltoggle-dot{width:6px;height:6px;border-radius: var(--radius-full);flex-shrink:0}
.leaflet-control-zoom{margin-top:96px!important;margin-left:10px!important}
.leaflet-popup-content-wrapper{background:#fff!important;border:none!important;border-radius:16px!important;box-shadow:0 8px 32px rgba(0,0,0,0.15)!important;padding:0!important}
.leaflet-popup-content-wrapper{background:#fff!important;border:none!important;border-radius: 16px!important;box-shadow:0 8px 32px rgba(0,0,0,0.15)!important;padding:0!important}
.leaflet-popup-content{margin:0!important;width:260px!important}
.leaflet-popup-tip{background:#fff!important}
.mpop{padding:14px 15px}
@@ -88,10 +88,10 @@ html,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,'SF Pro Text'
.mpop-bars{display:flex;flex-direction:column;gap:5px;margin-bottom:10px;padding-bottom:10px;border-bottom:1px solid var(--border)}
.mpop-bar-row{display:flex;align-items:center;gap:6px}
.mpop-bar-lbl{font-size:9px;font-weight:600;text-transform:uppercase;color:var(--t3);width:72px;flex-shrink:0}
.mpop-bar-track{flex:1;height:3px;border-radius:2px;background:rgba(0,0,0,0.06)}
.mpop-bar-fill{height:100%;border-radius:2px}
.mpop-bar-track{flex:1;height:3px;border-radius: var(--radius-sm);background:rgba(0,0,0,0.06)}
.mpop-bar-fill{height:100%;border-radius: var(--radius-sm)}
.mpop-bar-val{font-size:10px;font-weight:700;font-family:var(--mono);width:24px;text-align:right}
.mpop-contra{font-size:10px;border-radius:6px;padding:6px 9px;line-height:1.4;margin-bottom:8px}
.mpop-contra{font-size:10px;border-radius: var(--radius-sm);padding:6px 9px;line-height:1.4;margin-bottom:8px}
.mpop-contra.low{background:rgba(22,163,74,0.08);color:#166534;border:1px solid rgba(22,163,74,0.15)}
.mpop-contra.medium{background:rgba(217,119,6,0.08);color:#92400e;border:1px solid rgba(217,119,6,0.15)}
.mpop-contra.high{background:rgba(220,38,38,0.08);color:#991b1b;border:1px solid rgba(220,38,38,0.15)}
@@ -102,7 +102,7 @@ html,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,'SF Pro Text'
#right-profile-label{font-size:9px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:var(--t3);margin-bottom:4px}
#right-title{font-size:17px;font-weight:700;color:var(--text)}
#kpi-grid{padding:10px 16px 8px;display:grid;grid-template-columns:1fr 1fr;gap:6px;border-bottom:1px solid var(--border)}
.kpi{background:var(--s2);border-radius:12px;padding:12px 14px}
.kpi{background:var(--s2);border-radius: var(--radius-md);padding:12px 14px}
.kpi-label{font-size:10px;font-weight:700;letter-spacing:0.5px;text-transform:uppercase;margin-bottom:4px}
.kpi-value{font-size:22px;font-weight:800;font-family:var(--mono);line-height:1}
.kpi-sub{font-size:11px;color:var(--t2);margin-top:3px}
@@ -117,31 +117,31 @@ html,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,'SF Pro Text'
#contra-block{padding:12px 16px;border-bottom:1px solid var(--border)}
#contra-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px}
#contra-title{font-size:9px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:var(--t3);display:flex;align-items:center;gap:5px}
.contra-live-dot{width:5px;height:5px;border-radius:50%;background:#f87171;animation:pulse-ring 1.8s ease-in-out infinite}
.contra-count{font-size:9px;font-weight:700;background:rgba(220,38,38,0.12);color:#991b1b;padding:1px 7px;border-radius:10px}
.contra-card{border-radius:7px;border:1px solid var(--border);background:var(--s2);padding:9px 11px;margin-bottom:5px}
.contra-live-dot{width:5px;height:5px;border-radius: var(--radius-full);background:#f87171;animation:pulse-ring 1.8s ease-in-out infinite}
.contra-count{font-size:9px;font-weight:700;background:rgba(220,38,38,0.12);color:#991b1b;padding:1px 7px;border-radius: var(--radius-md)}
.contra-card{border-radius: var(--radius-sm);border:1px solid var(--border);background:var(--s2);padding:9px 11px;margin-bottom:5px}
.contra-card.c-medium{border-left:3px solid rgba(217,119,6,0.5)}
.contra-card.c-high{border-left:3px solid rgba(220,38,38,0.6)}
.contra-card-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:3px}
.contra-card-name{font-size:11px;font-weight:700;color:var(--text)}
.contra-card-badge{font-size:8px;font-weight:700;text-transform:uppercase;padding:1px 6px;border-radius:3px}
.contra-card-badge{font-size:8px;font-weight:700;text-transform:uppercase;padding:1px 6px;border-radius: var(--radius-sm)}
.contra-card-badge.medium{background:rgba(217,119,6,0.12);color:#92400e}
.contra-card-badge.high{background:rgba(220,38,38,0.12);color:#991b1b}
.contra-card-desc{font-size:10px;color:var(--t2);line-height:1.4}
#right-footer{padding:10px 16px;font-size:9px;color:var(--t3);line-height:1.4;border-top:1px solid var(--border)}
#bottom-sheet{display:none;position:fixed;left:0;right:0;bottom:0;z-index:9999;background:#fff;border-top:1px solid rgba(0,0,0,0.1);border-radius:16px 16px 0 0;box-shadow:0 -4px 24px rgba(0,0,0,0.12);max-height:72vh;overflow-y:auto;transform:translateY(100%);transition:transform 0.28s cubic-bezier(0.32,0.72,0,1)}
#bottom-sheet{display:none;position:fixed;left:0;right:0;bottom:0;z-index:9999;background:#fff;border-top:1px solid rgba(0,0,0,0.1);border-radius: var(--radius-lg) 16px 0 0;box-shadow:0 -4px 24px rgba(0,0,0,0.12);max-height:72vh;overflow-y:auto;transform:translateY(100%);transition:transform 0.28s cubic-bezier(0.32,0.72,0,1)}
#bottom-sheet.open{display:block;transform:translateY(0)}
#bottom-sheet-handle{width:36px;height:4px;border-radius:2px;background:rgba(0,0,0,0.12);margin:10px auto 0}
#bottom-sheet-close{position:absolute;top:12px;right:14px;background:rgba(0,0,0,0.06);border:none;color:var(--text);font-size:16px;width:28px;height:28px;border-radius:50%;cursor:pointer;display:flex;align-items:center;justify-content:center}
#bottom-sheet-handle{width:36px;height:4px;border-radius: var(--radius-sm);background:rgba(0,0,0,0.12);margin:10px auto 0}
#bottom-sheet-close{position:absolute;top:12px;right:14px;background:rgba(0,0,0,0.06);border:none;color:var(--text);font-size:16px;width:28px;height:28px;border-radius: var(--radius-full);cursor:pointer;display:flex;align-items:center;justify-content:center}
#bottom-sheet-content{padding:8px 0 env(safe-area-inset-bottom,16px)}
#bottom-sheet-backdrop{display:none;position:fixed;inset:0;z-index:9998;background:rgba(0,0,0,0.4)}
#bottom-sheet-backdrop.open{display:block}
#map-legend{position:absolute;bottom:60px;right:10px;z-index:800;background:rgba(0,0,0,0.92);backdrop-filter:blur(12px);border:1px solid rgba(0,0,0,0.08);border-radius:10px;padding:10px 12px}
#map-legend{position:absolute;bottom:60px;right:10px;z-index:800;background:rgba(0,0,0,0.92);backdrop-filter:blur(12px);border:1px solid rgba(0,0,0,0.08);border-radius: var(--radius-md);padding:10px 12px}
@keyframes pulse-ring{0%{opacity:1;transform:scale(1)}50%{opacity:0.6;transform:scale(1.2)}100%{opacity:1;transform:scale(1)}}
@keyframes pulse-wave{0%{transform:scale(1);opacity:0.7}100%{transform:scale(3.2);opacity:0}}
.pulse-marker{pointer-events:none}
.pulse-outer{position:absolute;border-radius:50%;animation:pulse-wave 2.2s ease-out infinite}
.pulse-inner{position:absolute;border-radius:50%;transform:translate(-50%,-50%)}
.pulse-outer{position:absolute;border-radius: var(--radius-full);animation:pulse-wave 2.2s ease-out infinite}
.pulse-inner{position:absolute;border-radius: var(--radius-full);transform:translate(-50%,-50%)}
@media(max-width:900px){
html,body{height:auto;overflow-y:auto;overflow-x:hidden}
#app{display:flex;flex-direction:column;height:auto;min-height:100dvh}
@@ -320,9 +320,9 @@ html,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,'SF Pro Text'
<div id="map-city-label">Malmö — <span id="map-profile-label">Business</span></div>
<div id="map-profile-chip" style="background:rgba(0,122,255,0.12);color:#0066cc;border-color:rgba(0,122,255,0.25)">Business</div>
<div style="display:flex;gap:4px;margin-left:auto">
<button onclick="map.setView([55.605,13.004],12)" style="background:rgba(0,0,0,0.06);border:none;border-radius:6px;padding:4px 8px;font-size:10px;font-weight:600;color:rgba(28,28,30,0.6);cursor:pointer">MMA</button>
<button onclick="map.setView([55.7,13.2],9)" style="background:rgba(0,0,0,0.06);border:none;border-radius:6px;padding:4px 8px;font-size:10px;font-weight:600;color:rgba(28,28,30,0.6);cursor:pointer">Skåne</button>
<button onclick="window.location='/intelligence'" style="background:rgba(0,0,0,0.06);border:none;border-radius:6px;padding:4px 8px;font-size:10px;font-weight:600;color:rgba(28,28,30,0.6);cursor:pointer">All cities</button>
<button onclick="map.setView([55.605,13.004],12)" style="background:rgba(0,0,0,0.06);border:none;border-radius: var(--radius-sm);padding:4px 8px;font-size:10px;font-weight:600;color:rgba(28,28,30,0.6);cursor:pointer">MMA</button>
<button onclick="map.setView([55.7,13.2],9)" style="background:rgba(0,0,0,0.06);border:none;border-radius: var(--radius-sm);padding:4px 8px;font-size:10px;font-weight:600;color:rgba(28,28,30,0.6);cursor:pointer">Skåne</button>
<button onclick="window.location='/intelligence'" style="background:rgba(0,0,0,0.06);border:none;border-radius: var(--radius-sm);padding:4px 8px;font-size:10px;font-weight:600;color:rgba(28,28,30,0.6);cursor:pointer">All cities</button>
</div>
</div>
<div id="layer-bar">
@@ -343,10 +343,10 @@ html,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,'SF Pro Text'
<div id="map-legend">
<div style="font-size:9px;font-weight:700;letter-spacing:0.8px;text-transform:uppercase;color:rgba(28,28,30,0.45);margin-bottom:7px">Intelligence Score</div>
<div style="display:flex;flex-direction:column;gap:5px">
<div style="display:flex;align-items:center;gap:7px;font-size:11px;color:rgba(28,28,30,0.75)"><div style="width:12px;height:12px;border-radius:50%;background:#22c55e;flex-shrink:0"></div>Strong (810)</div>
<div style="display:flex;align-items:center;gap:7px;font-size:11px;color:rgba(28,28,30,0.75)"><div style="width:12px;height:12px;border-radius:50%;background:#eab308;flex-shrink:0"></div>Moderate (68)</div>
<div style="display:flex;align-items:center;gap:7px;font-size:11px;color:rgba(28,28,30,0.75)"><div style="width:12px;height:12px;border-radius:50%;background:#f97316;flex-shrink:0"></div>Weak (46)</div>
<div style="display:flex;align-items:center;gap:7px;font-size:11px;color:rgba(28,28,30,0.75)"><div style="width:12px;height:12px;border-radius:50%;background:#ef4444;flex-shrink:0"></div>Poor (&lt;4)</div>
<div style="display:flex;align-items:center;gap:7px;font-size:11px;color:rgba(28,28,30,0.75)"><div style="width:12px;height:12px;border-radius: var(--radius-full);background:#22c55e;flex-shrink:0"></div>Strong (810)</div>
<div style="display:flex;align-items:center;gap:7px;font-size:11px;color:rgba(28,28,30,0.75)"><div style="width:12px;height:12px;border-radius: var(--radius-full);background:#eab308;flex-shrink:0"></div>Moderate (68)</div>
<div style="display:flex;align-items:center;gap:7px;font-size:11px;color:rgba(28,28,30,0.75)"><div style="width:12px;height:12px;border-radius: var(--radius-full);background:#f97316;flex-shrink:0"></div>Weak (46)</div>
<div style="display:flex;align-items:center;gap:7px;font-size:11px;color:rgba(28,28,30,0.75)"><div style="width:12px;height:12px;border-radius: var(--radius-full);background:#ef4444;flex-shrink:0"></div>Poor (&lt;4)</div>
</div>
<div style="font-size:9px;color:rgba(28,28,30,0.35);margin-top:6px">Tap any area for details</div>
</div>
@@ -379,7 +379,7 @@ html,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,'SF Pro Text'
<div id="police-block" style="padding:12px 16px;border-bottom:1px solid var(--border)">
<div style="font-size:9px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:var(--t3);margin-bottom:8px;display:flex;align-items:center;justify-content:space-between">
Police Events · Live
<span style="font-size:9px;font-weight:700;background:rgba(239,68,68,0.12);color:#991b1b;border-radius:4px;padding:1px 7px" id="police-count">Loading…</span>
<span style="font-size:9px;font-weight:700;background:rgba(239,68,68,0.12);color:#991b1b;border-radius: var(--radius-sm);padding:1px 7px" id="police-count">Loading…</span>
</div>
<div id="police-list" style="font-size:10px;color:var(--t3)">Loading…</div>
</div>
@@ -509,7 +509,7 @@ function buildList(p){
document.getElementById('district-list').innerHTML=sorted.map(d=>{
const v=d.s[p],col=sc(v);
const cClass=d.c==='high'?'dc-high':d.c==='medium'?'dc-med':'dc-low';
const cTxt=d.c==='high'?' HIGH CONTRA':d.c==='medium'?'· MEDIUM CONTRA':' LOW CONTRA';
const cTxt=d.c==='high'?' HIGH CONTRA':d.c==='medium'?'· MEDIUM CONTRA':'<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> LOW CONTRA';
return `<div class="dcard" id="dc-${d.id}" onclick="focusDist('${d.id}')"><div class="dcard-head"><div class="dcard-name">${d.name}</div><div class="dcard-score" style="background:${col}18;color:${col}">${v.toFixed(1)}</div></div><div class="dcard-desc">${d.ct.length>80?d.ct.substring(0,80)+'…':d.ct}</div><div class="dcard-contra ${cClass}">${cTxt}</div></div>`;
}).join('');
}
@@ -573,7 +573,7 @@ function buildAlertMarkers(){
alertMarkers.forEach(m=>m.remove());alertMarkers=[];
D.filter(d=>d.c==='high'||d.c==='medium').forEach(d=>{
const col=d.c==='high'?'#f87171':'#fbbf24';
const icon=L.divIcon({className:'',html:`<div style="width:18px;height:18px;border-radius:3px;background:rgba(0,0,0,0.95);border:2px solid ${col};display:flex;align-items:center;justify-content:center;font-size:10px;box-shadow:0 0 10px ${col}66;cursor:pointer">${d.c==='high'?'':'!'}</div>`,iconSize:[18,18],iconAnchor:[9,9]});
const icon=L.divIcon({className:'',html:`<div style="width:18px;height:18px;border-radius: var(--radius-sm);background:rgba(0,0,0,0.95);border:2px solid ${col};display:flex;align-items:center;justify-content:center;font-size:10px;box-shadow:0 0 10px ${col}66;cursor:pointer">${d.c==='high'?'':'!'}</div>`,iconSize:[18,18],iconAnchor:[9,9]});
const ca=CONTRA_ALERTS.find(x=>x.distId===d.id);
const marker=L.marker([d.lat,d.lng],{icon,zIndexOffset:500});
if(ca)marker.bindPopup(`<div style="padding:10px 12px;min-width:200px"><div style="font-size:9px;font-weight:700;color:${col};text-transform:uppercase;margin-bottom:4px">CONTRADICTION ${d.c.toUpperCase()}</div><div style="font-size:11px;font-weight:600;margin-bottom:4px">${d.name}</div><div style="font-size:10px;color:rgba(28,28,30,0.7);line-height:1.4">${ca.desc}</div></div>`,{maxWidth:240});
@@ -633,7 +633,7 @@ window.addEventListener('DOMContentLoaded',()=>{
poly.bindPopup(buildPopup(d,'business'),{maxWidth:260,minWidth:250});
poly.on('click',()=>{if(isMobile()){setTimeout(()=>{map.closePopup();openBottomSheet(buildPopup(d,profile));},10);}});
poly.addTo(map);polys[d.id]=poly;
if(map.getZoom()>=11){L.marker([d.lat,d.lng],{icon:L.divIcon({className:'label-marker',html:`<div style="font-size:11px;font-weight:700;color:#1c1c1e;white-space:nowrap;pointer-events:none;background:rgba(0,0,0,0.75);padding:2px 6px;border-radius:4px">${d.name}</div>`,iconAnchor:[0,0]}),interactive:false}).addTo(map);}
if(map.getZoom()>=11){L.marker([d.lat,d.lng],{icon:L.divIcon({className:'label-marker',html:`<div style="font-size:11px;font-weight:700;color:#1c1c1e;white-space:nowrap;pointer-events:none;background:rgba(0,0,0,0.75);padding:2px 6px;border-radius: 4px">${d.name}</div>`,iconAnchor:[0,0]}),interactive:false}).addTo(map);}
});
document.getElementById('district-count').textContent=D.length;
buildPulseMarkers();buildAlertMarkers();buildList('business');buildRight('business');
+37 -37
View File
@@ -59,7 +59,7 @@
a { color: inherit; text-decoration: none; }
/* ── NAV ── */
/* NAV */
nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
@@ -110,7 +110,7 @@
.btn-outline:hover { background: rgba(0,0,0,0.04); transform: translateY(-1px); }
.btn-lg { padding: 14px 30px; font-size: 16px; border-radius: var(--radius-md); }
/* ── HERO ── */
/* HERO */
.hero {
background: var(--bg-dark);
padding: 140px 24px 96px;
@@ -160,7 +160,7 @@
margin: 0 auto 48px;
}
/* ── PIPELINE OVERVIEW ── */
/* PIPELINE OVERVIEW */
.pipeline {
background: var(--bg-dark);
padding: 0 24px 80px;
@@ -197,7 +197,7 @@
.pipeline-num {
width: 56px; height: 56px;
border-radius: 50%;
border-radius: var(--radius-full);
background: rgba(0,102,255,0.1);
border: 2px solid var(--blue);
display: flex; align-items: center; justify-content: center;
@@ -215,7 +215,7 @@
line-height: 1.3;
}
/* ── SECTION COMMON ── */
/* SECTION COMMON */
.container { max-width: 1140px; margin: 0 auto; }
.section-label {
@@ -240,7 +240,7 @@
line-height: 1.7;
}
/* ── LAYER SECTIONS ── */
/* LAYER SECTIONS */
.layer {
padding: 80px 24px;
border-bottom: 1px solid #1a1a1a;
@@ -276,7 +276,7 @@
.layer-num-badge {
width: 36px; height: 36px;
border-radius: 50%;
border-radius: var(--radius-full);
background: var(--blue);
color: #1d1d1f;
font-size: 14px;
@@ -308,7 +308,7 @@
line-height: 1.7;
}
/* ── FEATURE LIST ── */
/* FEATURE LIST */
.feature-list {
display: flex;
flex-direction: column;
@@ -358,7 +358,7 @@
line-height: 1.55;
}
/* ── OUTPUT SECTION ── */
/* OUTPUT SECTION */
.output-section {
background: var(--bg-dark);
padding: 96px 24px;
@@ -426,7 +426,7 @@
font-style: italic;
}
/* ── DIMENSIONS BAND ── */
/* DIMENSIONS BAND */
.dimensions-band {
background: #e8f0fe;
padding: 64px 24px;
@@ -469,7 +469,7 @@
.dim-name { font-size: 13px; font-weight: 700; color: #1d1d1f; line-height: 1.3; }
.dim-scale { font-size: 11px; color: rgba(255,255,255,0.75); margin-top: 4px; }
/* ── CTA BAND ── */
/* CTA BAND */
.cta-band {
background: var(--blue);
padding: 72px 24px;
@@ -503,7 +503,7 @@
transform: translateY(-1px);
}
/* ── FOOTER ── */
/* FOOTER */
footer {
background: var(--bg-dark);
border-top: 1px solid var(--border);
@@ -526,14 +526,14 @@
.footer-social {
width: 32px; height: 32px;
border: 1px solid var(--border);
border-radius: 8px;
border-radius: var(--radius-md);
display: flex; align-items: center; justify-content: center;
transition: border-color 0.2s, background 0.2s;
}
.footer-social:hover { border-color: rgba(0,0,0,0.25); background: rgba(0,0,0,0.04); }
.footer-tagline { font-size: 12px; color: var(--text-muted); margin-top: 4px; }
/* ── PDF DOWNLOAD BUTTON ── */
/* PDF DOWNLOAD BUTTON */
.pdf-download-btn {
display: inline-flex; align-items: center; gap: 10px;
background: rgba(0,0,0,0.08);
@@ -553,7 +553,7 @@
}
.pdf-download-btn svg { flex-shrink: 0; }
/* ── DATA QUALITY ── */
/* DATA QUALITY */
.dq-section { background: #f8f9fc; padding: 96px 24px; }
.dq-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; margin-top: 48px; }
.dq-card {
@@ -579,11 +579,11 @@
}
.dq-standards li::before {
content: '';
width: 6px; height: 6px; border-radius: 50%;
width: 6px; height: 6px; border-radius: var(--radius-full);
background: var(--blue); flex-shrink: 0; margin-top: 6px;
}
/* ── CONTRADICTION FLOW DIAGRAM ── */
/* CONTRADICTION FLOW DIAGRAM */
.flow-section { background: #f5f5f7; padding: 96px 24px; }
.flow-diagram {
max-width: 820px; margin: 56px auto 0;
@@ -600,7 +600,7 @@
.flow-node.flow-rank { border-color: rgba(239,68,68,0.35); background: rgba(239,68,68,0.06); }
.flow-node.flow-redteam { border-color: rgba(139,92,246,0.4); background: rgba(139,92,246,0.07); }
.flow-node.flow-output { border-color: rgba(34,197,94,0.4); background: rgba(34,197,94,0.07); }
.flow-node-icon { width: 40px; height: 40px; border-radius: 10px; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.flow-node-icon { width: 40px; height: 40px; border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.flow-node-text h4 { font-size: 14px; font-weight: 700; color: #1d1d1f; margin-bottom: 3px; }
.flow-node-text p { font-size: 12px; color: rgba(255,255,255,0.75); line-height: 1.5; }
.flow-arrow { display: flex; align-items: center; justify-content: center; height: 32px; }
@@ -619,7 +619,7 @@
.flow-split-arrow::before { right: calc(50% + 8px); }
.flow-split-arrow::after { left: calc(50% + 8px); }
/* ── RESPONSIVE ── */
/* RESPONSIVE */
@media (max-width: 900px) {
nav { padding: 0 20px; }
.nav-links { display: none; }
@@ -699,7 +699,7 @@
<section class="hero">
<div class="hero-eyebrow">Methodology</div>
<h1>How Landvex works.</h1>
<div style="background:#fef3c7;border:1px solid #f59e0b;border-radius:8px;padding:12px 18px;margin:16px 0;font-size:.875rem;color:#92400e;"><strong>Pre-launch:</strong> All methodology documentation describes the system as designed for August 2026 launch. No live intelligence is being generated or sold at this time.</div>
<div style="background:#fef3c7;border:1px solid #f59e0b;border-radius: var(--radius-md);padding:12px 18px;margin:16px 0;font-size:.875rem;color:#92400e;"><strong>Pre-launch:</strong> All methodology documentation describes the system as designed for August 2026 launch. No live intelligence is being generated or sold at this time.</div>
<p class="hero-sub">Five interlocked layers — from global field observation to verified intelligence. Every output is sourced, explained, and challenged before delivery.</p>
<div style="display:flex;gap:14px;flex-wrap:wrap;justify-content:center;">
<a class="btn btn-lg" href="https://www.landvex.com/#contact">Request a briefing &rarr;</a>
@@ -734,7 +734,7 @@
<h3>Red Team<br>Analysis</h3>
</div>
<div class="pipeline-step">
<div class="pipeline-num"></div>
<div class="pipeline-num"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></div>
<h3>Verified<br>Output</h3>
</div>
</div>
@@ -744,12 +744,12 @@
<!-- PDF DOWNLOAD BLOCK -->
<section style="padding:0 24px;background:#fff;">
<div style="max-width:1140px;margin:0 auto;">
<div style="background:var(--blue);border-radius:16px;padding:32px 40px;display:flex;align-items:center;justify-content:space-between;gap:24px;flex-wrap:wrap;margin:48px 0">
<div style="background:var(--blue);border-radius: var(--radius-lg);padding:32px 40px;display:flex;align-items:center;justify-content:space-between;gap:24px;flex-wrap:wrap;margin:48px 0">
<div>
<h3 style="color: #1d1d1f;font-size:1.25rem;margin-bottom:6px">Landvex Intelligence Methodology</h3>
<p style="color:rgba(255,255,255,.75);font-size:.9375rem">8-page technical overview. How field observations become decision intelligence.</p>
</div>
<a href="/methodology/landvex-methodology-2026.pdf" style="background:#fff;color:var(--blue);font-weight:700;padding:14px 28px;border-radius:10px;text-decoration:none;white-space:nowrap;flex-shrink:0">
<a href="/methodology/landvex-methodology-2026.pdf" style="background:#fff;color:var(--blue);font-weight:700;padding:14px 28px;border-radius: var(--radius-md);text-decoration:none;white-space:nowrap;flex-shrink:0">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" style="margin-right:6px;vertical-align:middle"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
Download PDF
</a>
@@ -901,14 +901,14 @@
</div>
<div class="feature-list">
<div class="feature-item">
<div class="feature-icon">🏙️</div>
<div class="feature-icon"></div>
<div>
<h4>Observations aggregated into district / city / region scores</h4>
<p>Multiple observations from multiple contributors covering the same area are weighted, de-duplicated, and aggregated into a single composite score per dimension per area.</p>
</div>
</div>
<div class="feature-item">
<div class="feature-icon">🔄</div>
<div class="feature-icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 8C2 11.3 4.7 14 8 14C10.5 14 12.7 12.5 13.7 10.3" stroke="#666" stroke-width="1.5" stroke-linecap="round"/><path d="M14 8C14 4.7 11.3 2 8 2C5.5 2 3.3 3.5 2.3 5.7" stroke="#666" stroke-width="1.5" stroke-linecap="round"/><path d="M13.5 7.5L14 10L11.5 9.5" stroke="#666" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M2.5 8.5L2 6L4.5 6.5" stroke="#666" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg></div>
<div>
<h4>Scores updated continuously as new observations arrive</h4>
<p>There is no batch cadence. Every validated observation triggers a score recalculation for the relevant area. Intelligence is always as fresh as the most recent field data.</p>
@@ -938,21 +938,21 @@
</div>
<div class="feature-list">
<div class="feature-item" style="background:rgba(0,0,0,0.04); border-color:rgba(0,0,0,0.08);">
<div class="feature-icon">🏛️</div>
<div class="feature-icon"></div>
<div>
<h4 style="color: #ffffff">Official data ingested continuously</h4>
<p style="color:rgba(255,255,255,0.75)">National statistics, municipal open data, land registries, and transport authorities are continuously ingested and indexed against covered geographies.</p>
</div>
</div>
<div class="feature-item" style="background:rgba(0,0,0,0.04); border-color:rgba(0,0,0,0.08);">
<div class="feature-icon">⚖️</div>
<div class="feature-icon"></div>
<div>
<h4 style="color: #ffffff">Every official claim compared against observed reality</h4>
<p style="color:rgba(255,255,255,0.75)">For every official data point covering an area with observation coverage, the Contradiction Engine runs an automated comparison. Supporting evidence and contradicting evidence are catalogued separately.</p>
</div>
</div>
<div class="feature-item" style="background:rgba(0,0,0,0.04); border-color:rgba(0,0,0,0.08);">
<div class="feature-icon">🚩</div>
<div class="feature-icon"></div>
<div>
<h4 style="color: #ffffff">Contradictions flagged, ranked by severity and economic impact</h4>
<p style="color:rgba(255,255,255,0.75)">Not all contradictions are equal. The engine ranks flagged conflicts by how severe the discrepancy is, and by the estimated economic significance of the affected area or asset class.</p>
@@ -982,7 +982,7 @@
</div>
<div class="feature-list">
<div class="feature-item">
<div class="feature-icon">⚔️</div>
<div class="feature-icon"></div>
<div>
<h4>Every major conclusion challenged by a second model</h4>
<p>The primary analysis model produces a conclusion. A separate red team model, given the same evidence, is tasked with finding holes, alternative explanations, and disconfirming observations. The conclusion is only finalized after this adversarial review.</p>
@@ -996,14 +996,14 @@
</div>
</div>
<div class="feature-item">
<div class="feature-icon"></div>
<div class="feature-icon"></div>
<div>
<h4>Contradicting evidence listed</h4>
<p>Contradicting evidence is not suppressed. Every conclusion is delivered alongside the evidence that pushed against it. Where the red team found weaknesses, those are explicitly documented.</p>
</div>
</div>
<div class="feature-item">
<div class="feature-icon">📏</div>
<div class="feature-icon"></div>
<div>
<h4>Uncertainty quantified</h4>
<p>Confidence levels are expressed numerically, not as qualitative hedges. Clients know not just what Landvex concludes, but how certain that conclusion is and why.</p>
@@ -1028,7 +1028,7 @@
<div class="cite-note">Every score cites contributing observations and source data.</div>
</div>
<div class="output-card" style="cursor:pointer;" onclick="location.href='#contact'" role="button" tabindex="0">
<div class="output-icon">🚩</div>
<div class="output-icon"></div>
<h3>Contradiction Reports</h3>
<p>Where observed reality conflicts with official data or investment thesis. Ranked by severity and economic significance. Evidence on both sides included.</p>
<div class="cite-note">Includes confidence score and source breakdown.</div>
@@ -1046,13 +1046,13 @@
<div class="cite-note">Uncertainty ranges included with every forecast.</div>
</div>
<div class="output-card" style="cursor:pointer;" onclick="location.href='#contact'" role="button" tabindex="0">
<div class="output-icon">📝</div>
<div class="output-icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 13V10L10 3L13 6L6 13H3Z" stroke="#666" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M9 4L12 7" stroke="#666" stroke-width="1.5"/></svg></div>
<h3>Executive Summaries</h3>
<p>Decision-ready narratives structured around the client's specific question — not generic reports. Concise, direct, and directly linked to the decision being made.</p>
<div class="cite-note">Supporting data attached; key sources highlighted.</div>
</div>
<div class="output-card" style="cursor:pointer;border-color: rgba(0,102,255,0.25); background: rgba(0,102,255,0.06);" onclick="location.href='#contact'" role="button" tabindex="0">
<div class="output-icon" style="background: rgba(0,102,255,0.2); border-color: rgba(0,102,255,0.4);">📌</div>
<div class="output-icon" style="background: rgba(0,102,255,0.2); border-color: rgba(0,102,255,0.4);"></div>
<h3>All outputs cite sources<br>and explain reasoning</h3>
<p>There is no black box. Every Landvex output tells you what it concluded, what it was based on, what pushed against it, and how confident the conclusion is.</p>
<div class="cite-note">Because intelligence you can't verify is just opinion.</div>
@@ -1247,7 +1247,7 @@
it analyses, scores and flags discrepancies in what has been observed.
</p>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-bottom:32px">
<div style="background:rgba(0,0,0,.05);border-radius:10px;padding:18px 20px">
<div style="background:rgba(0,0,0,.05);border-radius: var(--radius-md);padding:18px 20px">
<div style="font-size:.8rem;font-weight:700;color:rgba(255,255,255,.75);text-transform:uppercase;letter-spacing:.06em;margin-bottom:8px">What AMOS does</div>
<ul style="font-size:.875rem;color:rgba(255,255,255,.75);line-height:1.7;padding-left:16px;margin:0">
<li>Cross-references field observations with official statistics</li>
@@ -1256,7 +1256,7 @@
<li>Generates trend signals from change patterns in field data</li>
</ul>
</div>
<div style="background:rgba(0,0,0,.05);border-radius:10px;padding:18px 20px">
<div style="background:rgba(0,0,0,.05);border-radius: var(--radius-md);padding:18px 20px">
<div style="font-size:.8rem;font-weight:700;color:rgba(255,255,255,.75);text-transform:uppercase;letter-spacing:.06em;margin-bottom:8px">What AMOS does not do</div>
<ul style="font-size:.875rem;color:rgba(255,255,255,.75);line-height:1.7;padding-left:16px;margin:0">
<li>Generate or invent observations not in field data</li>
@@ -1266,7 +1266,7 @@
</ul>
</div>
</div>
<div style="background:rgba(0,0,0,.06);border-radius:10px;padding:20px 24px;border-left:3px solid #f59e0b">
<div style="background:rgba(0,0,0,.06);border-radius: var(--radius-md);padding:20px 24px;border-left:3px solid #f59e0b">
<div style="font-size:.8rem;font-weight:700;color:rgba(255,255,255,.75);text-transform:uppercase;letter-spacing:.06em;margin-bottom:8px">AI Red Team</div>
<p style="font-size:.875rem;color:rgba(255,255,255,.7);line-height:1.65;margin:0">
Every AMOS contradiction flag is independently challenged by a second model before delivery.
+1 -1
View File
@@ -61,7 +61,7 @@
li { margin-bottom: 6px; }
.row { display: flex; align-items: flex-start; gap: 12px; padding: 12px 0; border-bottom: 0.5px solid var(--separator); }
.row:last-child { border-bottom: none; }
.row-icon { width: 36px; height: 36px; border-radius: 8px; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.row-icon { width: 36px; height: 36px; border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.blue { background: rgba(0,102,255,0.12); }
.green { background: rgba(52,199,89,0.12); }
.orange { background: rgba(255,149,0,0.12); }
+14 -14
View File
@@ -22,7 +22,7 @@
p { margin: 8px 0; }
ul { margin: 8px 0; padding-left: 24px; }
li { margin: 4px 0; }
.confidential { background: #fff3cd; border: 1px solid #ffc107; padding: 8px 14px; border-radius: 4px; font-size: 10pt; margin-bottom: 20px; }
.confidential { background: #fff3cd; border: 1px solid #ffc107; padding: 8px 14px; border-radius: var(--radius-sm); font-size: 10pt; margin-bottom: 20px; }
.sign-block { margin-top: 40px; border-top: 1px solid #ccc; padding-top: 20px; }
@media print {
body { margin: 20px; padding: 0 20px; }
@@ -82,19 +82,19 @@ AWS-infrastrukturen (eu-north-1, Stockholm) körde aktivt dygnet runt under hela
<p>Samtliga poster är dokumenterade. Det finns <strong>inga odokumenterade kontantposter</strong>.</p>
<table>
<tr><th>Kategori</th><th>Belopp (SEK)</th><th>Poster</th><th>Verifikation</th></tr>
<tr><td>AI/API-infrastruktur (Anthropic, OpenAI, Paddle)</td><td>83 278</td><td>50</td><td>Digitala fakturor </td></tr>
<tr><td>Boende, långtid (Airbnb, Phuket)</td><td>96 009</td><td>5</td><td>Bokningsbekräftelser </td></tr>
<tr><td>Hotell, affärsresor (BKK/MAR/SZX)</td><td>62 182</td><td>5</td><td>Hotellkvitton </td></tr>
<tr><td>Flyg (Thai Airways ARN→BKK)</td><td>19 455</td><td>1</td><td>E-biljett </td></tr>
<tr><td>AI-röst/TTS (ElevenLabs)</td><td>16 549</td><td>6</td><td>Digitala fakturor </td></tr>
<tr><td>Domäner (Loopia)</td><td>10 555</td><td>16</td><td>Digitala fakturor </td></tr>
<tr><td>CRM-verktyg (Apollo.io)</td><td>5 444</td><td>1</td><td>Digital faktura </td></tr>
<tr><td>Kommunikation (46elks SMS/API)</td><td>3 750</td><td>1</td><td>Digital faktura </td></tr>
<tr><td>UI-verktyg (Lovable)</td><td>4 295</td><td>4</td><td>Digitala fakturor </td></tr>
<tr><td>E-post (Resend)</td><td>619</td><td>3</td><td>Digitala fakturor </td></tr>
<tr><td>E-handel (Shopify)</td><td>828</td><td>9</td><td>Digitala fakturor </td></tr>
<tr><td>Mat och representation</td><td>5 351</td><td>6</td><td>Kvitton </td></tr>
<tr><td><strong>Lön VD (Erik Svensson) — se avsnitt 5</strong></td><td><strong>70 993</strong></td><td><strong>1</strong></td><td><strong>Löneunderlag </strong></td></tr>
<tr><td>AI/API-infrastruktur (Anthropic, OpenAI, Paddle)</td><td>83 278</td><td>50</td><td>Digitala fakturor <svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></td></tr>
<tr><td>Boende, långtid (Airbnb, Phuket)</td><td>96 009</td><td>5</td><td>Bokningsbekräftelser <svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></td></tr>
<tr><td>Hotell, affärsresor (BKK/MAR/SZX)</td><td>62 182</td><td>5</td><td>Hotellkvitton <svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></td></tr>
<tr><td>Flyg (Thai Airways ARN→BKK)</td><td>19 455</td><td>1</td><td>E-biljett <svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></td></tr>
<tr><td>AI-röst/TTS (ElevenLabs)</td><td>16 549</td><td>6</td><td>Digitala fakturor <svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></td></tr>
<tr><td>Domäner (Loopia)</td><td>10 555</td><td>16</td><td>Digitala fakturor <svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></td></tr>
<tr><td>CRM-verktyg (Apollo.io)</td><td>5 444</td><td>1</td><td>Digital faktura <svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></td></tr>
<tr><td>Kommunikation (46elks SMS/API)</td><td>3 750</td><td>1</td><td>Digital faktura <svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></td></tr>
<tr><td>UI-verktyg (Lovable)</td><td>4 295</td><td>4</td><td>Digitala fakturor <svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></td></tr>
<tr><td>E-post (Resend)</td><td>619</td><td>3</td><td>Digitala fakturor <svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></td></tr>
<tr><td>E-handel (Shopify)</td><td>828</td><td>9</td><td>Digitala fakturor <svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></td></tr>
<tr><td>Mat och representation</td><td>5 351</td><td>6</td><td>Kvitton <svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></td></tr>
<tr><td><strong>Lön VD (Erik Svensson) — se avsnitt 5</strong></td><td><strong>70 993</strong></td><td><strong>1</strong></td><td><strong>Löneunderlag <svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></strong></td></tr>
<tr style="background:#1a1a1a;"><td><strong>TOTALT</strong></td><td><strong>379 308</strong></td><td><strong>108</strong></td><td><strong>Fullt dokumenterat</strong></td></tr>
</table>
<p class="meta">Not: Totalt KYC-belopp 399 941,60 kr inkluderar även löner till övrig personal samt skatteinbetalningar redovisade i avsnitt 6.</p>
+3 -3
View File
@@ -23,14 +23,14 @@
.eyebrow{font-size:.75rem;font-weight:700;text-transform:uppercase;letter-spacing:.1em;color:#555;margin-bottom:12px}
h1{font-size:2rem;font-weight:800;margin-bottom:16px;color:#0a1628}
.lead{font-size:1.05rem;color:#444;margin-bottom:32px;line-height:1.7}
.card{background:#f8f9fa;border-radius:12px;padding:24px;margin-bottom:16px;border-left:4px solid #0066ff}
.card{background:#f8f9fa;border-radius: var(--radius-md);padding:24px;margin-bottom:16px;border-left:4px solid #0066ff}
.card.red{border-left-color:#dc2626}
.card h2{font-size:1rem;font-weight:700;margin-bottom:8px}
.card p{font-size:.9rem;color:#555;line-height:1.65}
.cta{display:inline-block;background:#0a1628;color: #1d1d1f;padding:12px 24px;border-radius:8px;text-decoration:none;font-weight:700;margin-top:24px}
.cta{display:inline-block;background:#0a1628;color: #1d1d1f;padding:12px 24px;border-radius: var(--radius-md);text-decoration:none;font-weight:700;margin-top:24px}
.faq{margin-top:40px}
.faq h2{font-size:1.1rem;font-weight:700;margin-bottom:16px}
details{border:1px solid #eee;border-radius:8px;padding:14px 16px;margin-bottom:8px}
details{border:1px solid #eee;border-radius: var(--radius-md);padding:14px 16px;margin-bottom:8px}
summary{font-weight:600;cursor:pointer;font-size:.9rem}
details p{margin-top:10px;font-size:.875rem;color:#555;line-height:1.65}
@media (max-width: 640px) {
+2 -2
View File
@@ -207,7 +207,7 @@
}
.process-step { text-align: center; padding: 0 20px; position: relative; }
.step-num {
width: 54px; height: 54px; border-radius: 50%;
width: 54px; height: 54px; border-radius: var(--radius-full);
background: var(--blue-glow); border: 2px solid var(--blue);
display: flex; align-items: center; justify-content: center;
font-size: 17px; font-weight: 800; color: var(--blue);
@@ -348,7 +348,7 @@
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social {
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px;
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md);
display: flex; align-items: center; justify-content: center;
transition: border-color 0.2s, background 0.2s;
}
+1 -1
View File
@@ -29,7 +29,7 @@
table { width: 100%; border-collapse: collapse; margin: 16px 0 24px; font-size: 13px; }
th { background: #f5f5f7; padding: 10px 12px; text-align: left; font-weight: 600; border-bottom: 2px solid #e5e5e7; }
td { padding: 9px 12px; border-bottom: 1px solid #1a1a1a; vertical-align: top; }
.highlight { background: #f5f5f7; border-radius: 10px; padding: 16px 20px; margin: 20px 0; font-size: 14px; }
.highlight { background: #f5f5f7; border-radius: var(--radius-md); padding: 16px 20px; margin: 20px 0; font-size: 14px; }
.highlight p { margin-bottom: 0; font-size: 14px; }
footer { border-top: 1px solid #e5e5e7; padding: 24px; text-align: center; font-size: 13px; color: #6e6e73; }
footer a { color: #0066cc; text-decoration: none; }
+27 -27
View File
@@ -44,7 +44,7 @@ body {
text-decoration: none; flex-shrink: 0;
}
.nav-logo-mark {
width: 30px; height: 30px; border-radius: 8px;
width: 30px; height: 30px; border-radius: var(--radius-md);
background: linear-gradient(135deg, #007AFF, #0055CC);
display: flex; align-items: center; justify-content: center;
font-size: 15px; font-weight: 800; color: #1d1d1f;
@@ -54,7 +54,7 @@ body {
.nav-link:hover { color: #1d1d1f; }
.nav-cta {
background: #007AFF; color: #1d1d1f; font-size: 14px; font-weight: 600;
padding: 8px 18px; border-radius: 10px; text-decoration: none;
padding: 8px 18px; border-radius: var(--radius-md); text-decoration: none;
transition: background 0.15s; white-space: nowrap;
}
.nav-cta:hover { background: #0066DD; }
@@ -77,7 +77,7 @@ body {
.hero-eyebrow {
display: inline-flex; align-items: center; gap: 6px;
background: rgba(0,122,255,0.2); border: 0.5px solid rgba(0,122,255,0.4);
border-radius: 20px; padding: 5px 14px;
border-radius: var(--radius-xl); padding: 5px 14px;
font-size: 12px; font-weight: 600; color: #64B5F6;
letter-spacing: 0.04em; margin-bottom: 24px;
position: relative; z-index: 1;
@@ -109,14 +109,14 @@ body {
.btn-primary {
background: #007AFF; color: #1d1d1f;
font-size: 16px; font-weight: 600; padding: 14px 28px;
border-radius: 14px; text-decoration: none; transition: all 0.2s;
border-radius: var(--radius-lg); text-decoration: none; transition: all 0.2s;
box-shadow: 0 4px 20px rgba(0,122,255,0.4);
}
.btn-primary:hover { background: #0066DD; transform: translateY(-1px); box-shadow: 0 6px 24px rgba(0,122,255,0.5); }
.btn-secondary {
background: rgba(0,0,0,0.1); color: #1d1d1f;
font-size: 16px; font-weight: 600; padding: 14px 28px;
border-radius: 14px; text-decoration: none;
border-radius: var(--radius-lg); text-decoration: none;
border: 0.5px solid rgba(0,0,0,0.2);
backdrop-filter: blur(10px); transition: all 0.2s;
}
@@ -156,7 +156,7 @@ body {
}
.problem-card {
background: var(--grouped-background-secondary);
border-radius: 16px; padding: 24px;
border-radius: var(--radius-lg); padding: 24px;
border: 0.5px solid var(--separator);
}
.problem-card .icon { font-size: 28px; margin-bottom: 14px; }
@@ -172,7 +172,7 @@ body {
padding: 28px 24px; position: relative; text-align: center;
}
.step-num {
width: 44px; height: 44px; border-radius: 50%;
width: 44px; height: 44px; border-radius: var(--radius-full);
background: linear-gradient(135deg, #007AFF, #0055CC);
display: flex; align-items: center; justify-content: center;
font-size: 18px; font-weight: 700; color: #1d1d1f;
@@ -194,7 +194,7 @@ body {
}
.for-card {
background: var(--grouped-background-secondary);
border: 0.5px solid var(--separator); border-radius: 14px;
border: 0.5px solid var(--separator); border-radius: var(--radius-lg);
padding: 20px 18px;
display: flex; align-items: flex-start; gap: 12px;
}
@@ -209,7 +209,7 @@ body {
}
.case-card {
background: var(--grouped-background-secondary);
border-radius: 16px; overflow: hidden;
border-radius: var(--radius-lg); overflow: hidden;
border: 0.5px solid var(--separator);
}
.case-card-header {
@@ -217,7 +217,7 @@ body {
display: flex; align-items: flex-start; gap: 12px;
}
.case-logo {
width: 44px; height: 44px; border-radius: 10px;
width: 44px; height: 44px; border-radius: var(--radius-md);
display: flex; align-items: center; justify-content: center;
font-size: 22px; flex-shrink: 0;
}
@@ -235,7 +235,7 @@ body {
}
.case-tag {
display: inline-block; font-size: 11px; font-weight: 600;
padding: 3px 10px; border-radius: 20px;
padding: 3px 10px; border-radius: var(--radius-xl);
background: rgba(0,122,255,0.1); color: var(--ios-blue);
margin-right: 6px; margin-bottom: 4px;
}
@@ -247,7 +247,7 @@ body {
}
.pricing-card {
background: var(--grouped-background-secondary);
border: 0.5px solid var(--separator); border-radius: 20px;
border: 0.5px solid var(--separator); border-radius: var(--radius-xl);
padding: 28px 24px;
}
.pricing-card.featured {
@@ -257,7 +257,7 @@ body {
}
.pricing-badge {
display: inline-block; font-size: 11px; font-weight: 700;
padding: 3px 10px; border-radius: 20px;
padding: 3px 10px; border-radius: var(--radius-xl);
background: var(--ios-blue); color: #1d1d1f;
margin-bottom: 16px; letter-spacing: 0.04em;
}
@@ -274,12 +274,12 @@ body {
display: flex; align-items: center; gap: 8px;
}
.pricing-features li:last-child { border-bottom: none; }
.pricing-features li::before { content: ''; color: var(--ios-green); font-weight: 700; flex-shrink: 0; }
.pricing-features li::before { content: '<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>'; color: var(--ios-green); font-weight: 700; flex-shrink: 0; }
.pricing-btn {
display: block; text-align: center;
background: var(--ios-blue); color: #1d1d1f;
font-size: 15px; font-weight: 600; padding: 12px;
border-radius: 12px; text-decoration: none; transition: background 0.15s;
border-radius: var(--radius-md); text-decoration: none; transition: background 0.15s;
}
.pricing-btn:hover { background: #0066DD; }
.pricing-btn.secondary {
@@ -454,7 +454,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<!-- HERO -->
<section class="hero">
<div class="hero-eyebrow">🛸 quiXzoom + LandveX Platform</div>
<div class="hero-eyebrow"> quiXzoom + LandveX Platform</div>
<h1>Right control.<br><em>Right cost.</em><br>Right interval.</h1>
<p class="hero-tagline">Infrastructure owners are overpaying for blanket inspection contracts and making decisions from outdated data. LandveX changes that.</p>
<div class="hero-ctas">
@@ -486,12 +486,12 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<div class="problem-grid">
<div class="problem-card">
<div class="icon">💸</div>
<div class="icon"></div>
<h3>Overpriced blanket contracts</h3>
<p>Traditional consulting firms charge for scheduled visits regardless of actual risk. You pay the same whether the asset is fine or failing.</p>
</div>
<div class="problem-card">
<div class="icon">📅</div>
<div class="icon"></div>
<h3>Annual cycle doesn't match reality</h3>
<p>Critical infrastructure changes faster than annual inspection cycles. A bridge deck can deteriorate significantly between visits — and you won't know until it's too late.</p>
</div>
@@ -501,7 +501,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<p>Maintenance budgets are allocated based on last year's report. Risk-based prioritization is impossible without current, objective assessment data.</p>
</div>
<div class="problem-card">
<div class="icon">🗺️</div>
<div class="icon"></div>
<h3>No spatial intelligence</h3>
<p>Traditional inspection reports are PDFs. There's no system connecting asset condition, geospatial context, and maintenance history into actionable intelligence.</p>
</div>
@@ -550,42 +550,42 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<div class="for-grid">
<div class="for-card">
<div class="for-icon">🏙️</div>
<div class="for-icon"></div>
<div>
<h3>Municipalities</h3>
<p>Roads, bridges, parks, public buildings, and drainage networks. Serve residents with evidence-based maintenance — within budget.</p>
</div>
</div>
<div class="for-card">
<div class="for-icon">🚧</div>
<div class="for-icon"></div>
<div>
<h3>Trafikverket</h3>
<p>National road and rail network inspection at scale. Replace manual bridge surveys with drone-powered AI assessment across the entire network.</p>
</div>
</div>
<div class="for-card">
<div class="for-icon">🏗️</div>
<div class="for-icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 14H14M4 14V8L8 4L12 8V14" stroke="#666" stroke-width="1.5" stroke-linejoin="round"/><path d="M6 14V10H10V14" stroke="#666" stroke-width="1.5"/></svg></div>
<div>
<h3>Property Owners</h3>
<p>Commercial real estate, industrial complexes, and facility managers who need objective condition data to plan capex and comply with regulatory requirements.</p>
</div>
</div>
<div class="for-card">
<div class="for-icon"></div>
<div class="for-icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M9 2L5 9H8L7 14L11 7H8L9 2Z" fill="#f59e0b"/></svg></div>
<div>
<h3>Energy Infrastructure</h3>
<p>Power lines, substations, wind farms, and solar installations. Scheduled and condition-triggered inspections without shutdowns or scaffolding.</p>
</div>
</div>
<div class="for-card">
<div class="for-icon">🌊</div>
<div class="for-icon"></div>
<div>
<h3>Water Utilities</h3>
<p>Dams, pipelines, reservoirs, and treatment facilities. AI-detected anomalies before they become emergency repairs or service failures.</p>
</div>
</div>
<div class="for-card">
<div class="for-icon">🏛️</div>
<div class="for-icon"></div>
<div>
<h3>Region & County</h3>
<p>Regional infrastructure authorities managing multi-municipality assets. Centralized oversight with per-asset granularity.</p>
@@ -604,7 +604,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<div class="case-grid">
<div class="case-card">
<div class="case-card-header">
<div class="case-logo" style="background:rgba(0,122,255,0.1)">🏙️</div>
<div class="case-logo" style="background:rgba(0,122,255,0.1)"></div>
<div>
<div class="case-org">Municipality of Gothenburg</div>
<div class="case-name">Bridge Network Assessment 2025</div>
@@ -636,7 +636,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<div class="case-card">
<div class="case-card-header">
<div class="case-logo" style="background:rgba(255,149,0,0.1)"></div>
<div class="case-logo" style="background:rgba(255,149,0,0.1)"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M9 2L5 9H8L7 14L11 7H8L9 2Z" fill="#f59e0b"/></svg></div>
<div>
<div class="case-org">Vattenfall Eldistribution AB</div>
<div class="case-name">High-Voltage Line Survey — Norrland</div>
+1 -1
View File
@@ -306,7 +306,7 @@
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social {
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px;
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md);
display: flex; align-items: center; justify-content: center;
transition: border-color 0.2s, background 0.2s;
}
@@ -139,7 +139,7 @@
.footer-links { display: flex; gap: 24px; align-items: center; flex-wrap: wrap; }
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social { width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px; display: flex; align-items: center; justify-content: center; transition: border-color 0.2s, background 0.2s; }
.footer-social { width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; transition: border-color 0.2s, background 0.2s; }
.footer-social:hover { border-color: rgba(0,0,0,0.25); background: rgba(0,0,0,0.04); }
@media (max-width: 900px) { nav { padding: 0 16px; } .nav-links { display: none; } .sla-grid { grid-template-columns: 1fr 1fr; } }
+9 -9
View File
@@ -41,13 +41,13 @@ body { font-family: var(--font-text); background: var(--background-secondary); c
.hero::before {
content: '';
position: absolute; top: -80px; right: -60px;
width: 320px; height: 320px; border-radius: 50%;
width: 320px; height: 320px; border-radius: var(--radius-full);
background: rgba(0,0,0,0.04);
}
.hero-logo {
width: 80px; height: 80px;
background: rgba(0,0,0,0.2);
border-radius: 20px;
border-radius: var(--radius-xl);
display: flex; align-items: center; justify-content: center;
font-size: 32px; font-weight: 900;
color: white; margin: 0 auto 24px;
@@ -72,7 +72,7 @@ body { font-family: var(--font-text); background: var(--background-secondary); c
background: rgba(0,0,0,0.18);
backdrop-filter: blur(20px);
border: 1px solid rgba(0,0,0,0.3);
border-radius: 20px; padding: 16px 28px;
border-radius: var(--radius-xl); padding: 16px 28px;
position: relative; z-index: 1;
}
.agg-score {
@@ -138,7 +138,7 @@ body { font-family: var(--font-text); background: var(--background-secondary); c
}
.review-card {
background: var(--background-primary);
border-radius: 20px;
border-radius: var(--radius-xl);
border: 0.5px solid var(--separator);
padding: 24px;
transition: transform 0.2s, box-shadow 0.2s;
@@ -153,7 +153,7 @@ body { font-family: var(--font-text); background: var(--background-secondary); c
}
.reviewer-info { display: flex; align-items: center; gap: 12px; }
.reviewer-avatar {
width: 44px; height: 44px; border-radius: 50%;
width: 44px; height: 44px; border-radius: var(--radius-full);
background: linear-gradient(135deg, #001F4D, #0055CC);
display: flex; align-items: center; justify-content: center;
font-size: 18px; font-weight: 700; color: white; flex-shrink: 0;
@@ -179,7 +179,7 @@ body { font-family: var(--font-text); background: var(--background-secondary); c
/* CTA */
.cta-section {
background: linear-gradient(135deg, #001F4D, #003D99);
border-radius: 24px; padding: 40px 32px;
border-radius: var(--radius-xl); padding: 40px 32px;
text-align: center; margin: 0 20px 40px;
max-width: 920px; margin-left: auto; margin-right: auto;
}
@@ -193,7 +193,7 @@ body { font-family: var(--font-text); background: var(--background-secondary); c
display: inline-block;
background: white; color: #001F4D;
font-size: 17px; font-weight: 700;
padding: 16px 32px; border-radius: 14px;
padding: 16px 32px; border-radius: var(--radius-lg);
text-decoration: none;
transition: transform 0.15s, box-shadow 0.15s;
}
@@ -333,7 +333,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<div class="agg-rating">
<div class="agg-score" style="font-size:1.25rem;font-weight:700;">Kommer snart</div>
<div class="agg-info">
<div class="stars" style="opacity:0;">★★★★★</div>
<div class="stars" style="opacity:0;"></div>
<div class="agg-count">Recensioner efter lansering</div>
</div>
</div>
@@ -370,7 +370,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<div class="reviews-grid" id="reviewsGrid">
<div class="review-card" style="text-align:center;padding:48px 24px;">
<div style="font-size:3rem;margin-bottom:16px;">📝</div>
<div style="font-size:3rem;margin-bottom:16px;"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 13V10L10 3L13 6L6 13H3Z" stroke="#666" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M9 4L12 7" stroke="#666" stroke-width="1.5"/></svg></div>
<h3 style="font-size:1.25rem;font-weight:700;margin-bottom:12px;color:var(--label-primary);">Recensioner kommer snart</h3>
<p style="color:var(--label-secondary);line-height:1.6;max-width:400px;margin:0 auto;">Landvex lanserar under 2026. Recensioner från kunder kommer att publiceras här när plattformen är live.</p>
</div>
+6 -6
View File
@@ -43,7 +43,7 @@
}
.review-card {
background: var(--card);
border-radius: 16px;
border-radius: var(--radius-lg);
padding: 24px;
margin-bottom: 16px;
}
@@ -55,7 +55,7 @@
.avatar {
width: 40px;
height: 40px;
border-radius: 50%;
border-radius: var(--radius-full);
background: var(--blue);
display: flex;
align-items: center;
@@ -95,7 +95,7 @@
background: var(--blue);
color: white;
padding: 14px 32px;
border-radius: 12px;
border-radius: var(--radius-md);
text-decoration: none;
font-weight: 600;
font-size: 16px;
@@ -128,7 +128,7 @@
<div class="review-role">Infrastructure Department</div>
</div>
</div>
<div class="review-stars">★★★★★</div>
<div class="review-stars"></div>
<div class="review-text">
"LandveX transformed how we monitor our city's infrastructure. We identified 340 maintenance issues in the first month that our traditional inspections missed entirely."
</div>
@@ -142,7 +142,7 @@
<div class="review-role">Risk Assessment Team</div>
</div>
</div>
<div class="review-stars">★★★★★</div>
<div class="review-stars"></div>
<div class="review-text">
"The speed and accuracy of LandveX data allows us to process claims faster and detect fraud patterns we never saw before. A game-changer for our industry."
</div>
@@ -156,7 +156,7 @@
<div class="review-role">Property Management</div>
</div>
</div>
<div class="review-stars">★★★★☆</div>
<div class="review-stars"></div>
<div class="review-text">
"We reduced our facade inspection costs by 60% while getting more frequent updates. The reality data is more reliable than our previous manual methods."
</div>
+32 -32
View File
@@ -65,7 +65,7 @@
a { color: inherit; text-decoration: none; }
/* ── NAV ── */
/* NAV */
nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
@@ -117,7 +117,7 @@
.btn-outline:hover { background: rgba(0,0,0,0.04); transform: translateY(-1px); }
.btn-lg { padding: 14px 30px; font-size: 16px; border-radius: var(--radius-md); }
/* ── HERO ── */
/* HERO */
.hero {
min-height: 60vh;
display: flex; flex-direction: column; align-items: center; justify-content: center;
@@ -168,7 +168,7 @@
margin-bottom: 40px;
}
/* ── TRUST BADGES ── */
/* TRUST BADGES */
.trust-bar {
display: flex; gap: 32px; flex-wrap: wrap; justify-content: center;
padding: 40px 24px;
@@ -192,7 +192,7 @@
flex-shrink: 0;
}
/* ── SECTION COMMON ── */
/* SECTION COMMON */
section { padding: 88px 24px; }
.container { max-width: 1100px; margin: 0 auto; }
@@ -218,7 +218,7 @@
line-height: 1.7;
}
/* ── SECURITY GRID ── */
/* SECURITY GRID */
.security-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
@@ -275,7 +275,7 @@
}
.security-items li::before {
content: '';
content: '<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>';
color: var(--green);
font-weight: 800;
font-size: 14px;
@@ -283,7 +283,7 @@
flex-shrink: 0;
}
/* ── ENTERPRISE CTA ── */
/* ENTERPRISE CTA */
.enterprise-block {
background: var(--surface);
border: 1px solid var(--border);
@@ -318,7 +318,7 @@
.enterprise-block .contact-item strong { color: #1d1d1f; }
/* ── COMPLIANCE STRIP ── */
/* COMPLIANCE STRIP */
.compliance-strip {
background: rgba(0,102,255,0.04);
border: 1px solid rgba(0,102,255,0.12);
@@ -334,7 +334,7 @@
.compliance-badge .dot {
width: 8px; height: 8px;
background: var(--green);
border-radius: 50%;
border-radius: var(--radius-full);
box-shadow: 0 0 6px rgba(34,197,94,0.6);
flex-shrink: 0;
}
@@ -349,7 +349,7 @@
flex-shrink: 0;
}
/* ── FOOTER ── */
/* FOOTER */
footer {
border-top: 1px solid var(--border);
padding: 40px 40px;
@@ -372,13 +372,13 @@
.footer-social {
width: 32px; height: 32px;
border: 1px solid var(--border);
border-radius: 8px;
border-radius: var(--radius-md);
display: flex; align-items: center; justify-content: center;
transition: border-color 0.2s, background 0.2s;
}
.footer-social:hover { border-color: rgba(0,0,0,0.25); background: rgba(0,0,0,0.04); }
/* ── RESPONSIVE ── */
/* RESPONSIVE */
@media (max-width: 900px) {
nav { padding: 0 20px; }
.nav-links { display: none; }
@@ -451,7 +451,7 @@
<!-- HERO -->
<section class="hero">
<div class="hero-eyebrow">🔒 Enterprise Security</div>
<div class="hero-eyebrow"> Enterprise Security</div>
<h1>Built secure<br>from the ground up.</h1>
<p class="hero-sub">LandveX runs on AWS infrastructure in Stockholm, with end-to-end encryption, verified identity chains, and GDPR-compliant data handling — designed for organisations that demand it.</p>
<div style="display:flex;gap:14px;flex-wrap:wrap;justify-content:center">
@@ -463,15 +463,15 @@
<!-- TRUST BAR -->
<div class="trust-bar">
<div class="trust-item">
<div class="icon">🔐</div>
<div class="icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="3" y="7" width="10" height="7" rx="1" stroke="#666" stroke-width="1.5"/><path d="M5 7V5C5 3.3 6.3 2 8 2C9.7 2 11 3.3 11 5V7" stroke="#666" stroke-width="1.5"/><circle cx="8" cy="10.5" r="1" fill="#666"/></svg></div>
AES-256 at rest
</div>
<div class="trust-item">
<div class="icon">🔒</div>
<div class="icon"></div>
TLS 1.2+ in transit
</div>
<div class="trust-item">
<div class="icon">🇪🇺</div>
<div class="icon"></div>
EU data residency
</div>
<div class="trust-item">
@@ -479,11 +479,11 @@
GDPR compliant
</div>
<div class="trust-item">
<div class="icon">🏛️</div>
<div class="icon"></div>
Hosted on AWS SOC 2 Type II infrastructure
</div>
<div class="trust-item">
<div class="icon">🛡️</div>
<div class="icon"></div>
ISO 27001-certified infrastructure (AWS)
</div>
</div>
@@ -502,7 +502,7 @@
<!-- Data Security -->
<div class="security-card" style="cursor:pointer;" onclick="location.href='#contact'" role="button" tabindex="0">
<div class="card-header">
<div class="card-icon">🔐</div>
<div class="card-icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="3" y="7" width="10" height="7" rx="1" stroke="#666" stroke-width="1.5"/><path d="M5 7V5C5 3.3 6.3 2 8 2C9.7 2 11 3.3 11 5V7" stroke="#666" stroke-width="1.5"/><circle cx="8" cy="10.5" r="1" fill="#666"/></svg></div>
<div class="card-title-wrap">
<h2>Data Security</h2>
<p>Encryption end-to-end, always</p>
@@ -520,7 +520,7 @@
<!-- Identity & Access -->
<div class="security-card" style="cursor:pointer;" onclick="location.href='#contact'" role="button" tabindex="0">
<div class="card-header">
<div class="card-icon">🪪</div>
<div class="card-icon"></div>
<div class="card-title-wrap">
<h2>Identity &amp; Access</h2>
<p>Verified identities, least-privilege access</p>
@@ -537,7 +537,7 @@
<!-- Data Integrity -->
<div class="security-card" style="cursor:pointer;" onclick="location.href='#contact'" role="button" tabindex="0">
<div class="card-header">
<div class="card-icon">🔗</div>
<div class="card-icon"></div>
<div class="card-title-wrap">
<h2>Data Integrity</h2>
<p>Tamper-evident submission chain</p>
@@ -553,7 +553,7 @@
<!-- Privacy & Compliance -->
<div class="security-card" style="cursor:pointer;" onclick="location.href='#contact'" role="button" tabindex="0">
<div class="card-header">
<div class="card-icon">🇪🇺</div>
<div class="card-icon"></div>
<div class="card-title-wrap">
<h2>Privacy &amp; Compliance</h2>
<p>GDPR by design, not as an afterthought</p>
@@ -570,7 +570,7 @@
<!-- Operational Security -->
<div class="security-card" style="cursor:pointer;" onclick="location.href='#contact'" role="button" tabindex="0">
<div class="card-header">
<div class="card-icon">⚙️</div>
<div class="card-icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><circle cx="8" cy="8" r="3" stroke="#666" stroke-width="1.5"/><path d="M8 2V4M8 12V14M2 8H4M12 8H14M4.3 4.3L5.7 5.7M10.3 10.3L11.7 11.7M4.3 11.7L5.7 10.3M10.3 5.7L11.7 4.3" stroke="#666" stroke-width="1.5" stroke-linecap="round"/></svg></div>
<div class="card-title-wrap">
<h2>Operational Security</h2>
<p>Hardened, monitored, incident-ready</p>
@@ -586,7 +586,7 @@
<!-- Enterprise -->
<div class="security-card" style="cursor:pointer;border-color:rgba(0,102,255,0.18);background:rgba(0,102,255,0.04)" onclick="location.href='#contact'" role="button" tabindex="0">
<div class="card-header">
<div class="card-icon" style="background:var(--blue-glow);border-color:rgba(0,102,255,0.25)">🏢</div>
<div class="card-icon" style="background:var(--blue-glow);border-color:rgba(0,102,255,0.25)"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="3" y="2" width="10" height="12" rx="1" stroke="#666" stroke-width="1.5"/><path d="M6 5H7M6 8H7M6 11H7M9 5H10M9 8H10M9 11H10" stroke="#666" stroke-width="1.5" stroke-linecap="round"/></svg></div>
<div class="card-title-wrap">
<h2>Enterprise</h2>
<p>Agreements, questionnaires, direct contact</p>
@@ -648,15 +648,15 @@
<p>Procurement, legal, or IT security teams with questions about the Landvex platform can contact our security team directly. We respond to all questionnaires within 5 business days.</p>
<div style="margin-top:28px;display:flex;flex-direction:column;gap:10px">
<div class="contact-item">
<span style="font-size:16px">📧</span>
<span style="font-size:16px"></span>
<span>Security team: <strong><a href="mailto:security@landvex.com" style="color:#5599ff">security@landvex.com</a></strong></span>
</div>
<div class="contact-item">
<span style="font-size:16px">📄</span>
<span style="font-size:16px"></span>
<span>DPA available on request</span>
</div>
<div class="contact-item">
<span style="font-size:16px"></span>
<span style="font-size:16px"></span>
<span>Questionnaire SLA: <strong>5 business days</strong></span>
</div>
</div>
@@ -678,25 +678,25 @@
We respond within one business day.
</p>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:16px">
<div style="border:1px solid #e5e7eb;border-radius:8px;padding:14px 16px">
<div style="border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:14px 16px">
<div style="font-weight:700;font-size:.875rem;margin-bottom:4px">Penetration test summary</div>
<div style="font-size:.8rem;color:#aaa">Most recent external pen test — under NDA</div>
</div>
<div style="border:1px solid #e5e7eb;border-radius:8px;padding:14px 16px">
<div style="border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:14px 16px">
<div style="font-weight:700;font-size:.875rem;margin-bottom:4px">AWS compliance reports</div>
<div style="font-size:.8rem;color:#aaa">SOC 2 Type II + ISO 27001 (AWS infrastructure)</div>
</div>
<div style="border:1px solid #e5e7eb;border-radius:8px;padding:14px 16px">
<div style="border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:14px 16px">
<div style="font-weight:700;font-size:.875rem;margin-bottom:4px">CAIQ / SIG questionnaire</div>
<div style="font-size:.8rem;color:#aaa">Pre-filled security assessment on request</div>
</div>
<div style="border:1px solid #e5e7eb;border-radius:8px;padding:14px 16px">
<div style="border:1px solid #e5e7eb;border-radius: var(--radius-md);padding:14px 16px">
<div style="font-weight:700;font-size:.875rem;margin-bottom:4px">Data Processing Agreement</div>
<div style="font-size:.8rem;color:#aaa"><a href="/dpa" style="color:#2563eb">View DPA →</a></div>
</div>
</div>
<a href="mailto:security@landvex.com?subject=Security%20documentation%20request"
style="display:inline-block;background:#0a1628;color: #1d1d1f;padding:10px 20px;border-radius:8px;font-size:.875rem;font-weight:700;text-decoration:none">
style="display:inline-block;background:#0a1628;color: #1d1d1f;padding:10px 20px;border-radius: var(--radius-md);font-size:.875rem;font-weight:700;text-decoration:none">
Request documentation →
</a>
<p style="font-size:.75rem;color:#aaa;margin-top:10px">
+4 -4
View File
@@ -251,7 +251,7 @@
.breach-step-num {
width: 48px; height: 48px; flex-shrink: 0;
background: var(--blue); border-radius: 50%;
background: var(--blue); border-radius: var(--radius-full);
display: flex; align-items: center; justify-content: center;
font-size: 16px; font-weight: 800; color: #1d1d1f;
position: relative; z-index: 1;
@@ -313,13 +313,13 @@
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social {
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px;
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md);
display: flex; align-items: center; justify-content: center;
transition: border-color 0.2s, background 0.2s;
}
.footer-social:hover { border-color: rgba(0,0,0,0.25); background: rgba(0,0,0,0.04); }
.lv-disclaimer { font-size: .75rem; color: rgba(0,0,0,.5); margin-top: 24px; padding: 12px 16px; background: rgba(0,0,0,.04); border-radius: 8px; line-height: 1.6; max-width: 800px; }
.lv-disclaimer { font-size: .75rem; color: rgba(0,0,0,.5); margin-top: 24px; padding: 12px 16px; background: rgba(0,0,0,.04); border-radius: var(--radius-md); line-height: 1.6; max-width: 800px; }
.lv-disclaimer a { color: rgba(0,0,0,.65); text-decoration: underline; }
@media (max-width: 900px) {
@@ -381,7 +381,7 @@
Service Level Commitments
</div>
<h1>Service Level Commitments.</h1>
<div style="background:#fef3c7;border:1px solid #f59e0b;border-radius:8px;padding:10px 18px;margin-bottom:20px;font-size:.875rem;color:#92400e"><strong>Pre-launch:</strong> These commitments apply from August 2026. During the pre-launch period, SLA terms are available for discussion in pilot agreements.</div>
<div style="background:#fef3c7;border:1px solid #f59e0b;border-radius: var(--radius-md);padding:10px 18px;margin-bottom:20px;font-size:.875rem;color:#92400e"><strong>Pre-launch:</strong> These commitments apply from August 2026. During the pre-launch period, SLA terms are available for discussion in pilot agreements.</div>
<p class="hero-sub">Landvex operates to defined standards of data freshness, platform availability, and delivery reliability. This page documents the commitments we make to clients and how breaches are handled.</p>
<div class="hero-ctas">
<a class="btn btn-lg" href="/contact/">Discuss SLA requirements &rarr;</a>
+21 -21
View File
@@ -62,7 +62,7 @@
a { color: inherit; text-decoration: none; }
/* ── NAV ── */
/* NAV */
nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
@@ -112,7 +112,7 @@
}
.btn-outline:hover { background: rgba(0,0,0,0.04); transform: translateY(-1px); }
/* ── HERO ── */
/* HERO */
.hero {
background: var(--bg-dark);
padding: 120px 24px 72px;
@@ -161,13 +161,13 @@
margin: 0 auto 40px;
}
/* ── OVERALL STATUS BANNER ── */
/* OVERALL STATUS BANNER */
.status-banner {
max-width: 860px;
margin: 0 auto 0;
background: var(--green-bg);
border: 1.5px solid var(--green-border);
border-radius: 16px;
border-radius: var(--radius-lg);
padding: 20px 28px;
display: flex;
align-items: center;
@@ -176,7 +176,7 @@
.status-banner-dot {
width: 14px; height: 14px;
border-radius: 50%;
border-radius: var(--radius-full);
background: var(--green-dot);
flex-shrink: 0;
box-shadow: 0 0 0 4px rgba(34,197,94,0.18);
@@ -215,14 +215,14 @@
white-space: nowrap;
}
/* ── MAIN CONTENT ── */
/* MAIN CONTENT */
.main {
max-width: 860px;
margin: 0 auto;
padding: 0 24px 80px;
}
/* ── UPTIME STATS ── */
/* UPTIME STATS */
.uptime-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
@@ -251,7 +251,7 @@
color: var(--text-muted);
}
/* ── SECTION HEADERS ── */
/* SECTION HEADERS */
.section-heading {
font-size: 18px;
font-weight: 800;
@@ -262,7 +262,7 @@
border-bottom: 1px solid var(--border-light);
}
/* ── COMPONENT LIST ── */
/* COMPONENT LIST */
.component-list {
display: flex;
flex-direction: column;
@@ -346,7 +346,7 @@
.status-dot {
width: 7px; height: 7px;
border-radius: 50%;
border-radius: var(--radius-full);
flex-shrink: 0;
}
@@ -354,7 +354,7 @@
.status-dot.degraded { background: #f59e0b; }
.status-dot.outage { background: #ef4444; }
/* ── INCIDENT HISTORY ── */
/* INCIDENT HISTORY */
.incident-box {
background: #f8f9fc;
border: 1px solid var(--border-light);
@@ -384,7 +384,7 @@
margin: 0 auto;
}
/* ── SUBSCRIBE SECTION ── */
/* SUBSCRIBE SECTION */
.subscribe-box {
background: var(--bg-dark);
border-radius: var(--radius-lg);
@@ -429,7 +429,7 @@
transform: translateY(-1px);
}
/* ── FOOTER ── */
/* FOOTER */
footer {
background: var(--bg-dark);
border-top: 1px solid var(--border);
@@ -452,14 +452,14 @@
.footer-social {
width: 32px; height: 32px;
border: 1px solid var(--border);
border-radius: 8px;
border-radius: var(--radius-md);
display: flex; align-items: center; justify-content: center;
transition: border-color 0.2s, background 0.2s;
}
.footer-social:hover { border-color: rgba(0,0,0,0.25); background: rgba(0,0,0,0.04); }
.footer-tagline { font-size: 12px; color: var(--text-muted); margin-top: 4px; }
/* ── RESPONSIVE ── */
/* RESPONSIVE */
@media (max-width: 700px) {
nav { padding: 0 20px; }
.nav-links { display: none; }
@@ -530,7 +530,7 @@
<!-- HERO -->
<section class="hero">
<div class="hero-eyebrow">
<span style="display:inline-block;width:8px;height:8px;border-radius:50%;background:#22c55e;box-shadow:0 0 0 3px rgba(34,197,94,0.25);"></span>
<span style="display:inline-block;width:8px;height:8px;border-radius: var(--radius-full);background:#22c55e;box-shadow:0 0 0 3px rgba(34,197,94,0.25);"></span>
All systems ready for August 2026 launch
</div>
<h1>System Status</h1>
@@ -574,7 +574,7 @@
<div class="component-item">
<div class="component-left">
<div class="component-icon"></div>
<div class="component-icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M9 2L5 9H8L7 14L11 7H8L9 2Z" fill="#f59e0b"/></svg></div>
<div>
<div class="component-name">Intelligence API</div>
<div class="component-desc">Scores, reports, and raw data endpoints</div>
@@ -588,7 +588,7 @@
<div class="component-item">
<div class="component-left">
<div class="component-icon">🌐</div>
<div class="component-icon"></div>
<div>
<div class="component-name">Field Data Collection (quiXzoom)</div>
<div class="component-desc">Contributor network ingestion and review pipeline</div>
@@ -602,7 +602,7 @@
<div class="component-item">
<div class="component-left">
<div class="component-icon">🔄</div>
<div class="component-icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 8C2 11.3 4.7 14 8 14C10.5 14 12.7 12.5 13.7 10.3" stroke="#666" stroke-width="1.5" stroke-linecap="round"/><path d="M14 8C14 4.7 11.3 2 8 2C5.5 2 3.3 3.5 2.3 5.7" stroke="#666" stroke-width="1.5" stroke-linecap="round"/><path d="M13.5 7.5L14 10L11.5 9.5" stroke="#666" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M2.5 8.5L2 6L4.5 6.5" stroke="#666" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg></div>
<div>
<div class="component-name">Data Processing Pipeline</div>
<div class="component-desc">Normalization, scoring, and contradiction detection</div>
@@ -616,7 +616,7 @@
<div class="component-item">
<div class="component-left">
<div class="component-icon">🖥️</div>
<div class="component-icon"></div>
<div>
<div class="component-name">Web Platform</div>
<div class="component-desc">landvex.com and client portal</div>
@@ -630,7 +630,7 @@
<div class="component-item">
<div class="component-left">
<div class="component-icon">🔐</div>
<div class="component-icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="3" y="7" width="10" height="7" rx="1" stroke="#666" stroke-width="1.5"/><path d="M5 7V5C5 3.3 6.3 2 8 2C9.7 2 11 3.3 11 5V7" stroke="#666" stroke-width="1.5"/><circle cx="8" cy="10.5" r="1" fill="#666"/></svg></div>
<div>
<div class="component-name">Authentication</div>
<div class="component-desc">API authentication, contributor login, and session management</div>
+171 -171
View File
@@ -67,15 +67,15 @@ html,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,'SF Pro Text'
#city-name{font-size:24px;font-weight:900;letter-spacing:-0.8px;color:var(--text);line-height:1;font-family:'Inter',system-ui,sans-serif}
#city-region{font-size:11px;color:var(--t2);margin-top:3px;font-weight:500}
#city-stats{display:grid;grid-template-columns:1fr 1fr;gap:6px;margin-top:12px}
.cstat{background:var(--s2);border:1px solid var(--border);border-radius:7px;padding:8px 10px}
.cstat{background:var(--s2);border:1px solid var(--border);border-radius: var(--radius-sm);padding:8px 10px}
.cstat-val{font-size:16px;font-weight:800;color:var(--text);letter-spacing:-0.3px}
.cstat-lbl{font-size:9px;font-weight:600;letter-spacing:0.5px;text-transform:uppercase;color:var(--t3);margin-top:1px}
/* Profile tabs */
#profile-block{padding:12px 16px;border-bottom:1px solid var(--border)}
#profile-lbl{font-size:9px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:var(--t3);margin-bottom:8px}
#profile-tabs{display:grid;grid-template-columns:1fr 1fr 1fr;gap:3px;background:rgba(0,0,0,0.06);border-radius:10px;padding:2px}
.ptab{padding:6px 4px;border-radius:8px;border:none;background:transparent;cursor:pointer;display:flex;flex-direction:column;align-items:center;gap:2px;transition:all 0.15s;font-size:10px;font-weight:600;color:var(--t2);letter-spacing:0.2px}
#profile-tabs{display:grid;grid-template-columns:1fr 1fr 1fr;gap:3px;background:rgba(0,0,0,0.06);border-radius: var(--radius-md);padding:2px}
.ptab{padding:6px 4px;border-radius: var(--radius-md);border:none;background:transparent;cursor:pointer;display:flex;flex-direction:column;align-items:center;gap:2px;transition:all 0.15s;font-size:10px;font-weight:600;color:var(--t2);letter-spacing:0.2px}
.ptab .picon{font-size:14px;line-height:1}
.ptab:hover{background:var(--s2);color:var(--text)}
.ptab.active{background:#ffffff;color:var(--text);box-shadow:0 1px 4px rgba(0,0,0,0.12)}
@@ -89,13 +89,13 @@ html,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,'SF Pro Text'
/* Districts */
#district-block{padding:12px 16px}
#district-lbl{font-size:9px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:var(--t3);margin-bottom:8px}
.dcard{border-radius:12px;border:none;background:var(--s2);padding:11px 13px;margin-bottom:6px;cursor:pointer;transition:all 0.12s}
.dcard{border-radius: var(--radius-md);border:none;background:var(--s2);padding:11px 13px;margin-bottom:6px;cursor:pointer;transition:all 0.12s}
.dcard:hover,.dcard.active{border-color:var(--b2);background:var(--s3)}
.dcard-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:3px}
.dcard-name{font-size:13px;font-weight:700;color:var(--text)}
.dcard-score{font-size:11px;font-weight:800;padding:1px 7px;border-radius:4px;font-family:var(--mono)}
.dcard-score{font-size:11px;font-weight:800;padding:1px 7px;border-radius: var(--radius-sm);font-family:var(--mono)}
.dcard-desc{font-size:12px;color:var(--t2);line-height:1.4;margin-bottom:5px}
.dcard-contra{display:inline-flex;align-items:center;gap:4px;font-size:9px;font-weight:700;letter-spacing:0.3px;border-radius:4px;padding:2px 7px}
.dcard-contra{display:inline-flex;align-items:center;gap:4px;font-size:9px;font-weight:700;letter-spacing:0.3px;border-radius: var(--radius-sm);padding:2px 7px}
.dc-low{background:rgba(22,163,74,0.12);color:#4ade80}
.dc-med{background:rgba(217,119,6,0.12);color:#fbbf24}
.dc-high{background:rgba(220,38,38,0.12);color:#f87171}
@@ -103,12 +103,12 @@ html,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,'SF Pro Text'
/* Data Freshness */
#freshness-block{padding:12px 16px;border-top:1px solid var(--border);margin-top:auto}
#freshness-title{font-size:9px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:var(--t3);margin-bottom:8px;display:flex;align-items:center;gap:5px}
#freshness-title::before{content:'';display:inline-block;width:5px;height:5px;border-radius:50%;background:#22c55e;animation:pulse-ring 2s ease-in-out infinite}
#freshness-title::before{content:'';display:inline-block;width:5px;height:5px;border-radius: var(--radius-full);background:#22c55e;animation:pulse-ring 2s ease-in-out infinite}
.fresh-row{display:flex;align-items:center;justify-content:space-between;padding:5px 0;border-bottom:1px solid var(--border)}
.fresh-row:last-child{border-bottom:none}
.fresh-source{font-size:10px;color:var(--t2);font-weight:500}
.fresh-val{font-size:9px;font-weight:700;font-family:var(--mono);display:flex;align-items:center;gap:4px}
.fresh-dot{width:5px;height:5px;border-radius:50%;flex-shrink:0}
.fresh-dot{width:5px;height:5px;border-radius: var(--radius-full);flex-shrink:0}
.fresh-dot.live{background:#22c55e;animation:pulse-ring 1.5s ease-in-out infinite}
.fresh-dot.recent{background:#fbbf24}
.fresh-dot.stale{background:#f87171}
@@ -121,33 +121,33 @@ html,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,'SF Pro Text'
#map-logo em{color:var(--accent3);font-style:normal}
.map-sep{width:1px;height:16px;background:rgba(0,0,0,0.12)}
#map-city-label{font-size:12px;font-weight:700;color:var(--text);flex:1}
#map-profile-chip{font-size:10px;font-weight:700;border-radius:4px;padding:3px 10px;border:1px solid;letter-spacing:0.3px}
#map-profile-chip{font-size:10px;font-weight:700;border-radius: var(--radius-sm);padding:3px 10px;border:1px solid;letter-spacing:0.3px}
#map-conf{font-size:10px;font-weight:600;color:var(--t2)}
/* Layer toggle bar */
#layer-bar{position:absolute;top:44px;left:0;right:0;z-index:999;display:flex;align-items:center;gap:4px;padding:5px 8px;background:#ffffff;border-bottom:1px solid rgba(0,0,0,0.1);overflow-x:auto;scrollbar-width:none;flex-wrap:nowrap;-webkit-overflow-scrolling:touch}
#layer-bar::-webkit-scrollbar{display:none}
#layer-bar-label{font-size:9px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:var(--t3);white-space:nowrap;margin-right:4px;flex-shrink:0}
.ltoggle{display:flex;align-items:center;gap:5px;font-size:11px;font-weight:600;color:var(--t2);background:rgba(0,0,0,0.05);border:none;border-radius:20px;padding:5px 12px;cursor:pointer;transition:all 0.15s;white-space:nowrap;user-select:none;flex-shrink:0}
.ltoggle{display:flex;align-items:center;gap:5px;font-size:11px;font-weight:600;color:var(--t2);background:rgba(0,0,0,0.05);border:none;border-radius: var(--radius-xl);padding:5px 12px;cursor:pointer;transition:all 0.15s;white-space:nowrap;user-select:none;flex-shrink:0}
.ltoggle:hover{border-color:var(--b2);color:var(--text)}
.ltoggle.on{background:var(--accent);color: #1d1d1ffff;}
.ltoggle-dot{width:6px;height:6px;border-radius:50%;flex-shrink:0}
.ltoggle-dot{width:6px;height:6px;border-radius: var(--radius-full);flex-shrink:0}
.ltoggle-dot.d-districts{background:#4aafef}
.ltoggle-dot.d-roadwork{background:#fb923c}
.ltoggle-dot.d-pulse{background:#c084fc;animation:pulse-ring 1.5s ease-in-out infinite}
.ltoggle-dot.d-alerts{background:#f87171}
.leaflet-control-zoom{margin-top:96px !important;margin-left:10px !important}
.leaflet-control-zoom a{background:rgba(11,18,32,0.9) !important;color:var(--text) !important;border-color:var(--b2) !important;width:30px !important;height:30px !important;line-height:30px !important;border-radius:5px !important;font-size:16px !important}
.leaflet-control-zoom a{background:rgba(11,18,32,0.9) !important;color:var(--text) !important;border-color:var(--b2) !important;width:30px !important;height:30px !important;line-height:30px !important;border-radius: var(--radius-sm) !important;font-size:16px !important}
.leaflet-popup-pane{z-index:1500 !important}
.leaflet-popup-content-wrapper{background:#ffffff !important;border:none !important;border-radius:16px !important;box-shadow:0 8px 32px rgba(0,0,0,0.15) !important;padding:0 !important;max-height:60vh !important;overflow-y:auto !important}
.leaflet-popup-content-wrapper{background:#ffffff !important;border:none !important;border-radius: var(--radius-lg) !important;box-shadow:0 8px 32px rgba(0,0,0,0.15) !important;padding:0 !important;max-height:60vh !important;overflow-y:auto !important}
.leaflet-popup-content{margin:0 !important;width:260px !important;max-height:55vh !important;overflow-y:auto !important}
@media(max-width:900px){
/* Hide Leaflet popup on mobile — use bottom sheet instead */
.leaflet-popup{display:none !important}
}
/* ── MOBILE BOTTOM SHEET ── */
/* MOBILE BOTTOM SHEET */
#bottom-sheet{
display:none;
position:fixed;
@@ -155,7 +155,7 @@ html,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,'SF Pro Text'
z-index:9999;
background:#ffffff;
border-top:1px solid rgba(0,0,0,0.1);
border-radius:16px 16px 0 0;
border-radius: var(--radius-lg) 16px 0 0;
box-shadow:0 -4px 24px rgba(0,0,0,0.12);
max-height:72vh;
overflow-y:auto;
@@ -168,7 +168,7 @@ html,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,'SF Pro Text'
transform:translateY(0);
}
#bottom-sheet-handle{
width:36px;height:4px;border-radius:2px;
width:36px;height:4px;border-radius: var(--radius-sm);
background:rgba(0,0,0,0.2);
margin:10px auto 0;
}
@@ -176,7 +176,7 @@ html,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,'SF Pro Text'
position:absolute;top:12px;right:14px;
background:rgba(0,0,0,0.1);border:none;
color:var(--text);font-size:16px;
width:28px;height:28px;border-radius:50%;
width:28px;height:28px;border-radius: var(--radius-full);
cursor:pointer;display:flex;align-items:center;justify-content:center;
line-height:1;
}
@@ -204,10 +204,10 @@ html,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,'SF Pro Text'
.mpop-bars{display:flex;flex-direction:column;gap:5px;margin-bottom:10px;padding-bottom:10px;border-bottom:1px solid var(--border)}
.mpop-bar-row{display:flex;align-items:center;gap:6px}
.mpop-bar-lbl{font-size:9px;font-weight:600;letter-spacing:0.3px;text-transform:uppercase;color:var(--t3);width:72px;flex-shrink:0}
.mpop-bar-track{flex:1;height:3px;border-radius:2px;background:rgba(0,0,0,0.04)}
.mpop-bar-fill{height:100%;border-radius:2px;transition:width 0.3s}
.mpop-bar-track{flex:1;height:3px;border-radius: var(--radius-sm);background:rgba(0,0,0,0.04)}
.mpop-bar-fill{height:100%;border-radius: var(--radius-sm);transition:width 0.3s}
.mpop-bar-val{font-size:10px;font-weight:700;font-family:var(--mono);width:24px;text-align:right}
.mpop-contra{font-size:10px;border-radius:6px;padding:6px 9px;line-height:1.4;margin-bottom:8px}
.mpop-contra{font-size:10px;border-radius: var(--radius-sm);padding:6px 9px;line-height:1.4;margin-bottom:8px}
.mpop-contra.low{background:rgba(22,163,74,0.1);color:#4ade80;border:1px solid rgba(22,163,74,0.15)}
.mpop-contra.medium{background:rgba(217,119,6,0.1);color:#fbbf24;border:1px solid rgba(217,119,6,0.15)}
.mpop-contra.high{background:rgba(220,38,38,0.1);color:#f87171;border:1px solid rgba(220,38,38,0.15)}
@@ -219,7 +219,7 @@ html,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,'SF Pro Text'
.rwpop-eyebrow{font-size:9px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:#fb923c;margin-bottom:3px}
.rwpop-name{font-size:13px;font-weight:800;color:var(--text);margin-bottom:6px}
.rwpop-desc{font-size:11px;color:var(--t2);line-height:1.5;margin-bottom:6px}
.rwpop-badge{display:inline-flex;align-items:center;gap:4px;font-size:9px;font-weight:700;border-radius:4px;padding:2px 8px;background:rgba(251,146,60,0.12);border:1px solid rgba(251,146,60,0.25);color:#fb923c}
.rwpop-badge{display:inline-flex;align-items:center;gap:4px;font-size:9px;font-weight:700;border-radius: var(--radius-sm);padding:2px 8px;background:rgba(251,146,60,0.12);border:1px solid rgba(251,146,60,0.25);color:#fb923c}
.rwpop-src{font-size:9px;color:var(--t3);margin-top:6px}
/* Live Pulse animation */
@@ -233,11 +233,11 @@ html,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,'SF Pro Text'
100%{transform:scale(3.2);opacity:0}
}
.src-chain{font-size:9px;color:rgba(220,232,245,0.25);border-top:1px solid rgba(0,0,0,0.03);padding-top:5px;margin-top:5px;line-height:1.6}
.src-tag{display:inline-flex;align-items:center;gap:3px;background:rgba(0,0,0,0.04);border-radius:3px;padding:1px 5px;margin:1px 2px 1px 0;font-size:9px;font-weight:600;color:rgba(220,232,245,0.35)}
.src-tag{display:inline-flex;align-items:center;gap:3px;background:rgba(0,0,0,0.04);border-radius: var(--radius-sm);padding:1px 5px;margin:1px 2px 1px 0;font-size:9px;font-weight:600;color:rgba(220,232,245,0.35)}
.src-data{color:#38bdf8}.src-obs{color:#4ade80}.src-model{color:#a78bfa}.src-forecast{color:#f59e0b}
.pulse-marker{pointer-events:none}
.pulse-outer{position:absolute;border-radius:50%;animation:pulse-wave 2.2s ease-out infinite}
.pulse-inner{position:absolute;border-radius:50%;transform:translate(-50%,-50%)}
.pulse-outer{position:absolute;border-radius: var(--radius-full);animation:pulse-wave 2.2s ease-out infinite}
.pulse-inner{position:absolute;border-radius: var(--radius-full);transform:translate(-50%,-50%)}
/* RIGHT */
#right{background:var(--s1);border-left:1px solid rgba(0,0,0,0.08);overflow-y:auto;display:flex;flex-direction:column;scrollbar-width:none}
@@ -245,7 +245,7 @@ html,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,'SF Pro Text'
#right-profile-label{font-size:9px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:var(--t3);margin-bottom:4px}
#right-title{font-size:17px;font-weight:700;color:var(--text);letter-spacing:-0.3px}
#kpi-grid{padding:10px 16px 8px;display:grid;grid-template-columns:1fr 1fr;gap:6px;border-bottom:1px solid var(--border)}
.kpi{background:var(--s2);border:none;border-radius:12px;padding:12px 14px}
.kpi{background:var(--s2);border:none;border-radius: var(--radius-md);padding:12px 14px}
.kpi-label{font-size:10px;font-weight:700;letter-spacing:0.5px;text-transform:uppercase;margin-bottom:4px}
.kpi-value{font-size:22px;font-weight:800;letter-spacing:-0.5px;font-family:var(--mono);line-height:1}
.kpi-sub{font-size:11px;color:var(--t2);margin-top:3px;line-height:1.3}
@@ -263,37 +263,37 @@ html,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,'SF Pro Text'
#contra-block{padding:12px 16px;border-bottom:1px solid var(--border)}
#contra-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px}
#contra-title{font-size:9px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:var(--t3);display:flex;align-items:center;gap:5px}
.contra-live-dot{width:5px;height:5px;border-radius:50%;background:#f87171;animation:pulse-ring 1.8s ease-in-out infinite}
.contra-count{font-size:9px;font-weight:700;font-family:var(--mono);background:rgba(220,38,38,0.15);border:1px solid rgba(220,38,38,0.25);color:#f87171;padding:1px 7px;border-radius:10px}
.contra-card{border-radius:7px;border:1px solid var(--border);background:var(--s2);padding:9px 11px;margin-bottom:5px}
.contra-live-dot{width:5px;height:5px;border-radius: var(--radius-full);background:#f87171;animation:pulse-ring 1.8s ease-in-out infinite}
.contra-count{font-size:9px;font-weight:700;font-family:var(--mono);background:rgba(220,38,38,0.15);border:1px solid rgba(220,38,38,0.25);color:#f87171;padding:1px 7px;border-radius: var(--radius-md)}
.contra-card{border-radius: var(--radius-sm);border:1px solid var(--border);background:var(--s2);padding:9px 11px;margin-bottom:5px}
.contra-card.c-medium{border-left:3px solid rgba(217,119,6,0.5)}
.contra-card.c-high{border-left:3px solid rgba(220,38,38,0.6)}
.contra-card-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:3px}
.contra-card-name{font-size:11px;font-weight:700;color:var(--text)}
.contra-card-badge{font-size:8px;font-weight:700;letter-spacing:0.5px;text-transform:uppercase;padding:1px 6px;border-radius:3px}
.contra-card-badge{font-size:8px;font-weight:700;letter-spacing:0.5px;text-transform:uppercase;padding:1px 6px;border-radius: var(--radius-sm)}
.contra-card-badge.medium{background:rgba(217,119,6,0.12);color:#fbbf24}
.contra-card-badge.high{background:rgba(220,38,38,0.12);color:#f87171}
.contra-card-desc{font-size:10px;color:var(--t2);line-height:1.4;margin-bottom:6px}
.contra-card-meta{display:flex;align-items:center;justify-content:space-between}
.contra-card-date{font-size:9px;color:var(--t3);font-family:var(--mono)}
.contra-view-btn{font-size:9px;font-weight:700;letter-spacing:0.3px;padding:3px 9px;border-radius:4px;border:1px solid var(--b2);background:transparent;color:var(--accent3);cursor:pointer;transition:all 0.12s}
.contra-view-btn{font-size:9px;font-weight:700;letter-spacing:0.3px;padding:3px 9px;border-radius: var(--radius-sm);border:1px solid var(--b2);background:transparent;color:var(--accent3);cursor:pointer;transition:all 0.12s}
.contra-view-btn:hover{background:rgba(74,175,239,0.1);border-color:var(--accent2)}
#caution-block{padding:12px 16px;border-bottom:1px solid var(--border)}
.caution-card{background:rgba(220,38,38,0.06);border:1px solid rgba(220,38,38,0.15);border-radius:8px;padding:10px 12px}
.caution-card{background:rgba(220,38,38,0.06);border:1px solid rgba(220,38,38,0.15);border-radius: var(--radius-md);padding:10px 12px}
.caution-lbl{font-size:10px;font-weight:700;letter-spacing:0.5px;text-transform:uppercase;color:var(--red3);margin-bottom:6px}
.caution-row{display:flex;gap:7px;align-items:flex-start;padding:3px 0;font-size:12px;color:var(--text);line-height:1.5}
.caution-dot{width:4px;height:4px;border-radius:50%;background:var(--red2);flex-shrink:0;margin-top:4px}
.caution-dot{width:4px;height:4px;border-radius: var(--radius-full);background:var(--red2);flex-shrink:0;margin-top:4px}
#right-footer{padding:10px 16px;font-size:9px;color:var(--t3);line-height:1.4;border-top:1px solid var(--border)}
#right-footer a{color:var(--accent3);text-decoration:none}
/* ── MOBILE — iOS native style ── */
/* MOBILE — iOS native style */
@media(max-width:900px){
html,body{height:auto;overflow-y:auto;overflow-x:hidden;-webkit-overflow-scrolling:touch}
#app{display:flex;flex-direction:column;height:auto;min-height:100dvh;overflow:visible}
/* ── TOP BAR — kompakt native ── */
/* TOP BAR — kompakt native */
#left{order:1;border-right:none;border-bottom:none;background:var(--s1);display:flex;flex-direction:column;height:auto;overflow:visible}
#city-block{padding:12px 16px 8px}
#city-eyebrow{font-size:9px;letter-spacing:1.5px}
@@ -306,12 +306,12 @@ html,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,'SF Pro Text'
/* hide less important stats */
.cstat:nth-child(3),.cstat:nth-child(4){display:none}
/* ── PROFILE TABS — iOS segmented control style ── */
/* PROFILE TABS — iOS segmented control style */
#profile-block{padding:8px 16px;border-bottom:1px solid var(--border)}
#profile-lbl{display:none}
#profile-tabs{display:flex;overflow-x:auto;gap:0;scrollbar-width:none;background:rgba(0,0,0,0.04);border-radius:10px;padding:2px}
#profile-tabs{display:flex;overflow-x:auto;gap:0;scrollbar-width:none;background:rgba(0,0,0,0.04);border-radius: var(--radius-md);padding:2px}
#profile-tabs::-webkit-scrollbar{display:none}
.ptab{flex-shrink:0;flex-direction:row;gap:4px;padding:6px 14px;font-size:12px;font-weight:600;border-radius:8px;border:none;background:transparent;white-space:nowrap;min-width:auto;color:rgba(220,232,245,0.55)}
.ptab{flex-shrink:0;flex-direction:row;gap:4px;padding:6px 14px;font-size:12px;font-weight:600;border-radius: var(--radius-md);border:none;background:transparent;white-space:nowrap;min-width:auto;color:rgba(220,232,245,0.55)}
.ptab .picon{display:none}
.ptab.active{background:rgba(220,232,245,0.15);color:#dce8f5;font-weight:700}
/* Active profile colors */
@@ -322,18 +322,18 @@ html,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,'SF Pro Text'
.ptab.active.p-nightlife{background:rgba(147,51,234,0.2);color:#c084fc}
.ptab.active.p-infrastructure{background:rgba(180,83,9,0.2);color:#fcd34d}
/* ── HIDE DISTRICT LIST + FRESHNESS on mobile ── */
/* HIDE DISTRICT LIST + FRESHNESS on mobile */
#district-block{display:none}
#freshness-block{display:none}
/* ── MAP — hero element, karta first ── */
/* MAP — hero element, karta first */
#map-wrap{order:2;height:60vh;min-height:340px;max-height:520px;position:relative}
#map-topbar{height:44px;padding:0 14px;display:flex;align-items:center}
#map-logo{font-size:14px;font-weight:800}
.map-sep{display:none}
#map-city-label{font-size:12px;font-weight:600}
#map-conf{display:none}
#map-profile-chip{font-size:10px;padding:3px 10px;border-radius:20px}
#map-profile-chip{font-size:10px;padding:3px 10px;border-radius: var(--radius-xl)}
/* Layer bar — scrollable, compact */
#layer-bar{
@@ -349,19 +349,19 @@ html,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,'SF Pro Text'
-webkit-overflow-scrolling:touch
}
#layer-bar::-webkit-scrollbar{display:none}
.ltoggle{font-size:10px;padding:4px 10px;border-radius:20px;gap:4px;flex-shrink:0;color:rgba(28,28,30,0.65)}
.ltoggle{font-size:10px;padding:4px 10px;border-radius: var(--radius-xl);gap:4px;flex-shrink:0;color:rgba(28,28,30,0.65)}
.ltoggle-dot{width:6px;height:6px}
.leaflet-control-zoom{margin-top:92px !important;margin-left:10px !important}
.leaflet-control-zoom a{background:#ffffff !important;color:#1c1c1e !important;border:none !important;border-bottom:1px solid rgba(0,0,0,0.08) !important}
.leaflet-control-zoom a:last-child{border-bottom:none !important}
.leaflet-control-zoom a{width:30px !important;height:30px !important;line-height:30px !important;font-size:16px !important;border-radius:8px !important}
.leaflet-control-zoom a{width:30px !important;height:30px !important;line-height:30px !important;font-size:16px !important;border-radius: var(--radius-md) !important}
/* ── RIGHT PANEL — card stack below map ── */
/* RIGHT PANEL — card stack below map */
#right{order:3;border-left:none;border-top:1px solid var(--border);overflow:visible;height:auto}
#right-header{padding:12px 16px 8px}
#right-title{font-size:14px;font-weight:800}
#kpi-grid{grid-template-columns:1fr 1fr;gap:6px;padding:0 16px 12px}
.kpi{border-radius:10px;padding:10px 12px}
.kpi{border-radius: var(--radius-md);padding:10px 12px}
.kpi-label{font-size:9px}
.kpi-value{font-size:17px}
.kpi-sub{font-size:9px}
@@ -619,9 +619,9 @@ if('serviceWorker' in navigator){
<div id="map-profile-chip" style="background:rgba(0,122,255,0.12);color:#0066cc;border-color:rgba(0,122,255,0.25)">Business</div>
<div id="map-conf" style="font-size:9px;color:rgba(220,232,245,0.3)">Source First</div>
<div style="display:flex;gap:4px;margin-left:auto">
<button onclick="map.setView([59.370,18.040],9)" style="background:rgba(0,0,0,0.06);border:none;border-radius:6px;padding:4px 8px;font-size:10px;font-weight:600;color:rgba(28,28,30,0.6);cursor:pointer">SE</button>
<button onclick="map.setView([52.5,10.0],5)" style="background:rgba(0,0,0,0.06);border:none;border-radius:6px;padding:4px 8px;font-size:10px;font-weight:600;color:rgba(28,28,30,0.6);cursor:pointer">EU</button>
<button onclick="map.setView([25.0,40.0],3)" style="background:rgba(0,0,0,0.06);border:none;border-radius:6px;padding:4px 8px;font-size:10px;font-weight:600;color:rgba(28,28,30,0.6);cursor:pointer">Global</button>
<button onclick="map.setView([59.370,18.040],9)" style="background:rgba(0,0,0,0.06);border:none;border-radius: var(--radius-sm);padding:4px 8px;font-size:10px;font-weight:600;color:rgba(28,28,30,0.6);cursor:pointer">SE</button>
<button onclick="map.setView([52.5,10.0],5)" style="background:rgba(0,0,0,0.06);border:none;border-radius: var(--radius-sm);padding:4px 8px;font-size:10px;font-weight:600;color:rgba(28,28,30,0.6);cursor:pointer">EU</button>
<button onclick="map.setView([25.0,40.0],3)" style="background:rgba(0,0,0,0.06);border:none;border-radius: var(--radius-sm);padding:4px 8px;font-size:10px;font-weight:600;color:rgba(28,28,30,0.6);cursor:pointer">Global</button>
</div>
</div>
<!-- Layer Toggles -->
@@ -647,14 +647,14 @@ if('serviceWorker' in navigator){
</div>
</div>
<div id="map"></div>
<div id="map-legend" style="position:absolute;bottom:60px;right:10px;z-index:800;background:rgba(0,0,0,0.92);backdrop-filter:blur(12px);border:1px solid rgba(0,0,0,0.08);border-radius:10px;padding:10px 12px;font-family:-apple-system,sans-serif">
<div id="map-legend" style="position:absolute;bottom:60px;right:10px;z-index:800;background:rgba(0,0,0,0.92);backdrop-filter:blur(12px);border:1px solid rgba(0,0,0,0.08);border-radius: var(--radius-md);padding:10px 12px;font-family:-apple-system,sans-serif">
<div style="font-size:9px;font-weight:700;letter-spacing:0.8px;text-transform:uppercase;color:rgba(28,28,30,0.45);margin-bottom:7px">Intelligence Score</div>
<div style="display:flex;flex-direction:column;gap:5px">
<div style="display:flex;align-items:center;gap:7px;font-size:11px;color:rgba(28,28,30,0.75)"><div style="width:12px;height:12px;border-radius:50%;background:#22c55e;flex-shrink:0"></div>Strong (810)</div>
<div style="display:flex;align-items:center;gap:7px;font-size:11px;color:rgba(28,28,30,0.75)"><div style="width:12px;height:12px;border-radius:50%;background:#eab308;flex-shrink:0"></div>Moderate (68)</div>
<div style="display:flex;align-items:center;gap:7px;font-size:11px;color:rgba(28,28,30,0.75)"><div style="width:12px;height:12px;border-radius:50%;background:#f97316;flex-shrink:0"></div>Weak (46)</div>
<div style="display:flex;align-items:center;gap:7px;font-size:11px;color:rgba(28,28,30,0.75)"><div style="width:12px;height:12px;border-radius:50%;background:#ef4444;flex-shrink:0"></div>Poor (&lt;4)</div>
<div style="display:flex;align-items:center;gap:7px;font-size:11px;color:rgba(28,28,30,0.75);border-top:1px solid rgba(0,0,0,0.08);padding-top:5px"><div style="width:12px;height:3px;background:rgba(239,68,68,0.6);flex-shrink:0;border-radius:1px"></div>Contradiction</div>
<div style="display:flex;align-items:center;gap:7px;font-size:11px;color:rgba(28,28,30,0.75)"><div style="width:12px;height:12px;border-radius: var(--radius-full);background:#22c55e;flex-shrink:0"></div>Strong (810)</div>
<div style="display:flex;align-items:center;gap:7px;font-size:11px;color:rgba(28,28,30,0.75)"><div style="width:12px;height:12px;border-radius: var(--radius-full);background:#eab308;flex-shrink:0"></div>Moderate (68)</div>
<div style="display:flex;align-items:center;gap:7px;font-size:11px;color:rgba(28,28,30,0.75)"><div style="width:12px;height:12px;border-radius: var(--radius-full);background:#f97316;flex-shrink:0"></div>Weak (46)</div>
<div style="display:flex;align-items:center;gap:7px;font-size:11px;color:rgba(28,28,30,0.75)"><div style="width:12px;height:12px;border-radius: var(--radius-full);background:#ef4444;flex-shrink:0"></div>Poor (&lt;4)</div>
<div style="display:flex;align-items:center;gap:7px;font-size:11px;color:rgba(28,28,30,0.75);border-top:1px solid rgba(0,0,0,0.08);padding-top:5px"><div style="width:12px;height:3px;background:rgba(239,68,68,0.6);flex-shrink:0;border-radius: 1px"></div>Contradiction</div>
</div>
<div style="font-size:9px;color:rgba(28,28,30,0.35);margin-top:6px">Tap any area for details</div>
</div>
@@ -691,7 +691,7 @@ if('serviceWorker' in navigator){
<div id="events-engine" style="padding:12px 16px;border-bottom:1px solid var(--border)">
<div style="font-size:10px;font-weight:700;letter-spacing:0.5px;text-transform:uppercase;color:rgba(28,28,30,0.45);margin-bottom:8px;display:flex;align-items:center;justify-content:space-between">
Change Events
<span style="font-size:9px;font-weight:700;background:rgba(239,68,68,0.15);color:#f87171;border-radius:4px;padding:1px 7px" id="event-critical-count">0 critical</span>
<span style="font-size:9px;font-weight:700;background:rgba(239,68,68,0.15);color:#f87171;border-radius: var(--radius-sm);padding:1px 7px" id="event-critical-count">0 critical</span>
</div>
<div id="event-list"></div>
</div>
@@ -702,17 +702,17 @@ if('serviceWorker' in navigator){
Weekly Intelligence Brief
<span style="font-size:11px;color:rgba(28,28,30,0.55)">Week 25 · Jun 1621</span>
</div>
<div style="background:#fff5f5;border-radius:8px;padding:10px 12px;margin-bottom:6px;border-left:3px solid #ef4444">
<div style="background:#fff5f5;border-radius: var(--radius-md);padding:10px 12px;margin-bottom:6px;border-left:3px solid #ef4444">
<div style="font-size:13px;font-weight:700;color:#d70015;margin-bottom:4px">01 · Security deterioration, Rinkeby</div>
<div style="font-size:12px;color:rgba(28,28,30,0.75);line-height:1.5">Police events up 280% vs 4-week avg. Contradiction Index: HIGH. Official classification has not been updated. Recommend: escalate to municipal review.</div>
<div style="font-size:10px;color:rgba(28,28,30,0.45);margin-top:4px">Sources: Polisen.se · quiXzoom 1 102 obs · Contradiction confirmed</div>
</div>
<div style="background:#faf5ff;border-radius:8px;padding:10px 12px;margin-bottom:6px;border-left:3px solid #a855f7">
<div style="background:#faf5ff;border-radius: var(--radius-md);padding:10px 12px;margin-bottom:6px;border-left:3px solid #a855f7">
<div style="font-size:13px;font-weight:700;color:#6e1e8f;margin-bottom:4px">02 · Strategic signal: Nacka acceleration</div>
<div style="font-size:12px;color:rgba(28,28,30,0.75);line-height:1.5">Tunnel milestone confirmed 14 months ahead of schedule. Commercial permit applications +34% MoM. Investment thesis: accelerating.</div>
<div style="font-size:10px;color:rgba(28,28,30,0.45);margin-top:4px">Sources: Trafikverket · Nacka kommun · quiXzoom 389 obs</div>
</div>
<div style="background:#fff7ed;border-radius:8px;padding:10px 12px;border-left:3px solid #f97316">
<div style="background:#fff7ed;border-radius: var(--radius-md);padding:10px 12px;border-left:3px solid #f97316">
<div style="font-size:13px;font-weight:700;color:#b86a00;margin-bottom:4px">03 · Commercial vacancy rising, Södermalm</div>
<div style="font-size:12px;color:rgba(28,28,30,0.75);line-height:1.5">18 new vacancies detected on Götgatan corridor. Pattern matches early-stage commercial stress. Monitor: if +8 more in 30 days, escalate to Important.</div>
<div style="font-size:10px;color:rgba(28,28,30,0.45);margin-top:4px">Sources: quiXzoom 47 obs · Fastighetsregistret · quiXzoom Change Detection</div>
@@ -723,32 +723,32 @@ if('serviceWorker' in navigator){
<div id="macro-block" style="padding:12px 16px;border-bottom:1px solid var(--border)">
<div style="font-size:10px;font-weight:700;letter-spacing:0.5px;text-transform:uppercase;color:rgba(28,28,30,0.45);margin-bottom:8px">City Macro Intelligence</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:5px;margin-bottom:8px" id="macro-grid">
<div style="background:var(--s2);border:1px solid var(--border);border-radius:7px;padding:8px 10px">
<div style="background:var(--s2);border:1px solid var(--border);border-radius: var(--radius-sm);padding:8px 10px">
<div style="font-size:9px;color:var(--t3);text-transform:uppercase;letter-spacing:0.5px;margin-bottom:3px">GDP / capita</div>
<div style="font-size:15px;font-weight:800;color:#4ade80;font-family:var(--mono)" id="gdp-val">Loading</div>
<div style="font-size:9px;color:var(--t3);margin-top:1px">USD · World Bank</div>
</div>
<div style="background:var(--s2);border:1px solid var(--border);border-radius:7px;padding:8px 10px">
<div style="background:var(--s2);border:1px solid var(--border);border-radius: var(--radius-sm);padding:8px 10px">
<div style="font-size:9px;color:var(--t3);text-transform:uppercase;letter-spacing:0.5px;margin-bottom:3px">Political Stability</div>
<div style="font-size:15px;font-weight:800;color:#38bdf8;font-family:var(--mono)">8.9<span style="font-size:10px;font-weight:500">/10</span></div>
<div style="font-size:9px;color:var(--t3);margin-top:1px">WGI 2023 · top 5% global</div>
</div>
<div style="background:var(--s2);border:1px solid var(--border);border-radius:7px;padding:8px 10px">
<div style="background:var(--s2);border:1px solid var(--border);border-radius: var(--radius-sm);padding:8px 10px">
<div style="font-size:9px;color:var(--t3);text-transform:uppercase;letter-spacing:0.5px;margin-bottom:3px">Rule of Law</div>
<div style="font-size:15px;font-weight:800;color:#38bdf8;font-family:var(--mono)">9.2<span style="font-size:10px;font-weight:500">/10</span></div>
<div style="font-size:9px;color:var(--t3);margin-top:1px">WGI 2023 · Rank 6 global</div>
</div>
<div style="background:var(--s2);border:1px solid var(--border);border-radius:7px;padding:8px 10px">
<div style="background:var(--s2);border:1px solid var(--border);border-radius: var(--radius-sm);padding:8px 10px">
<div style="font-size:9px;color:var(--t3);text-transform:uppercase;letter-spacing:0.5px;margin-bottom:3px">Corruption Index</div>
<div style="font-size:15px;font-weight:800;color:#4ade80;font-family:var(--mono)">82<span style="font-size:10px;font-weight:500">/100</span></div>
<div style="font-size:9px;color:var(--t3);margin-top:1px">TI CPI 2024 · Rank 6</div>
</div>
</div>
<!-- Security level -->
<div style="background:rgba(34,197,94,0.08);border:1px solid rgba(34,197,94,0.18);border-radius:7px;padding:8px 11px">
<div style="background:rgba(34,197,94,0.08);border:1px solid rgba(34,197,94,0.18);border-radius: var(--radius-sm);padding:8px 11px">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:4px">
<div style="font-size:10px;font-weight:700;color:#4ade80">Security Assessment</div>
<div style="font-size:9px;font-weight:700;background:rgba(34,197,94,0.15);color:#4ade80;border-radius:4px;padding:1px 7px">LOW RISK</div>
<div style="font-size:9px;font-weight:700;background:rgba(34,197,94,0.15);color:#4ade80;border-radius: var(--radius-sm);padding:1px 7px">LOW RISK</div>
</div>
<div style="font-size:10px;color:rgba(220,232,245,0.55);line-height:1.5">Sweden maintains one of Europes most stable security environments. No active conflict. Terrorism risk: low-medium (NCSD level 3/5). Gang-related violence concentrated in specific outer suburbs.</div>
<div style="font-size:9px;color:rgba(220,232,245,0.3);margin-top:5px">Sources: SÄPO 2025 · GPI 2024 (rank 14) · OSAC</div>
@@ -773,10 +773,10 @@ if('serviceWorker' in navigator){
<div id="right-footer">
<div style="font-size:9px;color:rgba(28,28,30,0.5);line-height:1.6">
<div style="font-weight:700;color:rgba(28,28,30,0.65);margin-bottom:4px;letter-spacing:0.5px;text-transform:uppercase">Source First Architecture</div>
<span style="background:rgba(56,189,248,0.1);color:#38bdf8;border-radius:3px;padding:1px 5px;margin-right:3px;font-weight:600">DATA</span> Identifiable sources with date + confidence<br>
<span style="background:rgba(74,222,128,0.1);color:#4ade80;border-radius:3px;padding:1px 5px;margin-right:3px;font-weight:600">OBS</span> quiXzoom verified field observations<br>
<span style="background:rgba(167,139,250,0.1);color:#a78bfa;border-radius:3px;padding:1px 5px;margin-right:3px;font-weight:600">MODEL</span> Transparent index with listed inputs<br>
<span style="background:rgba(245,158,11,0.1);color:#f59e0b;border-radius:3px;padding:1px 5px;margin-right:3px;font-weight:600">FORECAST</span> Statistical patterns, not predictions<br>
<span style="background:rgba(56,189,248,0.1);color:#38bdf8;border-radius: var(--radius-sm);padding:1px 5px;margin-right:3px;font-weight:600">DATA</span> Identifiable sources with date + confidence<br>
<span style="background:rgba(74,222,128,0.1);color:#4ade80;border-radius: var(--radius-sm);padding:1px 5px;margin-right:3px;font-weight:600">OBS</span> quiXzoom verified field observations<br>
<span style="background:rgba(167,139,250,0.1);color:#a78bfa;border-radius: var(--radius-sm);padding:1px 5px;margin-right:3px;font-weight:600">MODEL</span> Transparent index with listed inputs<br>
<span style="background:rgba(245,158,11,0.1);color:#f59e0b;border-radius: var(--radius-sm);padding:1px 5px;margin-right:3px;font-weight:600">FORECAST</span> Statistical patterns, not predictions<br>
<a href="/methodology/" style="color:rgba(220,232,245,0.3);margin-top:4px;display:block">Full methodology →</a>
</div>
</div>
@@ -878,7 +878,7 @@ const D=[
col:'#16a34a', umi:+44,
confidence:{score:92,observation_count:614},pulse:{level:0.74,label:'High Activity'}},
/* ── Stockholm extra districts ── */
/* Stockholm extra districts */
{id:'djurgarden', name:'Djurgården', lat:59.326, lng:18.110, r:700,
s:{business:5.2,tourist:9.8,safety:9.1,investment:7.4,nightlife:3.2,infrastructure:8.4},
c:'low', ct:'Observed environment matches official data. Premium cultural destination.',
@@ -907,7 +907,7 @@ const D=[
col:'#d97706', umi:+14,
confidence:{score:82,observation_count:198},pulse:{level:0.28,label:'Low Activity'}},
/* ── Göteborg districts ── */
/* Göteborg districts */
{id:'gbg-centrum', name:'Göteborg Centrum', lat:57.706, lng:11.967, r:1200,
s:{business:8.4,tourist:8.9,safety:7.8,investment:8.1,nightlife:8.6,infrastructure:8.8},
c:'medium', ct:'Some traffic congestion underreported in official data. Commercial activity slightly above classification.',
@@ -950,7 +950,7 @@ const D=[
col:'#16a34a', umi:+22,
confidence:{score:88,observation_count:223},pulse:{level:0.32,label:'Low Activity'}},
/* ── Malmö districts ── */
/* Malmö districts */
{id:'malmo-centrum', name:'Malmö Centrum', lat:55.604, lng:13.003, r:1100,
s:{business:7.8,tourist:8.2,safety:7.1,investment:7.8,nightlife:7.9,infrastructure:8.4},
c:'medium', ct:'Night safety slightly below official classification. Commercial activity aligns well.',
@@ -1112,7 +1112,7 @@ const D=[
ins:{business:'Port commerce, Pfizer, regional HQ.',tourist:'Ferry to Denmark, Kärnan.',safety:'Good.',investment:'Appreciation trend positive.',nightlife:'Growing.',infrastructure:'Tåg, ferry. Direct to Copenhagen.'},
col:'#16a34a', umi:+24},
/* ── Stockholm kranskommuner (batch 2) ── */
/* Stockholm kranskommuner (batch 2) */
{id:'sthlm-botkyrka', name:'Botkyrka', lat:59.201, lng:17.834, r:2400,
s:{business:5.1,tourist:3.2,safety:5.8,investment:6.8,nightlife:3.1,infrastructure:6.9},
c:'high', ct:'Contradiction detected: official data underreports social challenges in Alby/Norsborg. Infrastructure maintenance gaps observed.',
@@ -1209,7 +1209,7 @@ const D=[
ins:{business:'Tourism, local.',tourist:'Birka Viking UNESCO, Drottningholm.',safety:'Top safety.',investment:'Premium island living.',nightlife:'None.',infrastructure:'Bridge, ferry.'},
col:'#16a34a', umi:+16},
// ── GÖTEBORGSREGIONEN ──────────────────────────────────────────
// GÖTEBORGSREGIONEN
{id:'gbg-partille', name:'Partille', lat:57.738, lng:12.106, r:1600,
s:{business:6.8,tourist:3.4,safety:8.8,investment:7.8,nightlife:3.2,infrastructure:7.6},
c:'low', ct:'Affluent eastern suburb. Allum retail strong.',
@@ -1258,7 +1258,7 @@ const D=[
ins:{business:'Tourism, boatbuilding.',tourist:'West coast archipelago.',safety:'Top.',investment:'Premium summer.',nightlife:'Summer only.',infrastructure:'Bridge, ferry.'},
col:'#16a34a', umi:+12},
// ── MALMÖ / SKÅNE ──────────────────────────────────────────────
// MALMÖ / SKÅNE
{id:'skane-kristianstad', name:'Kristianstad', lat:56.031, lng:14.154, r:2400,
s:{business:6.8,tourist:6.4,safety:8.6,investment:7.1,nightlife:5.2,infrastructure:7.4},
c:'low', ct:'Regional capital eastern Skåne. Stable.',
@@ -1307,7 +1307,7 @@ const D=[
ins:{business:'Local services.',tourist:'Falsterbo, beaches.',safety:'Highest in Skåne.',investment:'Premium coastal demand.',nightlife:'Very limited.',infrastructure:'Bus.'},
col:'#16a34a', umi:+16},
// ── UPPSALA ────────────────────────────────────────────────────
// UPPSALA
{id:'uppsala-centrum', name:'Uppsala', lat:59.858, lng:17.645, r:2200,
s:{business:8.1,tourist:7.8,safety:8.6,investment:8.2,nightlife:7.4,infrastructure:8.4},
c:'low', ct:'University city. Strong alignment official/observed.',
@@ -1320,98 +1320,98 @@ const D=[
ins:{business:'Local services, growing.',tourist:'No appeal.',safety:'Excellent.',investment:'Top growth rate Sweden.',nightlife:'None.',infrastructure:'Pendeltåg between Uppsala/Stockholm.'},
col:'#16a34a', umi:+82},
// ── LINKÖPING ──────────────────────────────────────────────────
// LINKÖPING
{id:'linkoping-centrum', name:'Linköping', lat:58.411, lng:15.621, r:2200,
s:{business:8.4,tourist:7.1,safety:8.8,investment:7.8,nightlife:6.8,infrastructure:8.2},
c:'low', ct:'Tech/defence capital. Saab. Strong alignment.',
ins:{business:'Saab, Ericsson, LiU. Tech powerhouse.',tourist:'Old Linköping, Kolmården nearby.',safety:'Very safe.',investment:'Strong tech-driven demand.',nightlife:'Good student scene.',infrastructure:'E4, tåg. Good.'},
col:'#16a34a', umi:+38},
// ── ÖREBRO ─────────────────────────────────────────────────────
// ÖREBRO
{id:'orebro-centrum', name:'Örebro', lat:59.275, lng:15.213, r:2200,
s:{business:7.2,tourist:6.8,safety:7.8,investment:7.4,nightlife:6.4,infrastructure:7.8},
c:'medium', ct:'Some inner-city areas higher risk than official classification.',
ins:{business:'Regional capital. Logistics hub, Gevalia.',tourist:'Örebro Castle.',safety:'Generally good. Some areas watch.',investment:'Growing regional.',nightlife:'Good for size.',infrastructure:'Central Sweden junction. E18/E20.'},
col:'#d97706', umi:+28},
// ── VÄSTERÅS ───────────────────────────────────────────────────
// VÄSTERÅS
{id:'vasteras-centrum', name:'Västerås', lat:59.609, lng:16.545, r:2200,
s:{business:7.8,tourist:5.8,safety:7.6,investment:7.2,nightlife:5.8,infrastructure:7.8},
c:'medium', ct:'ABB HQ town. Some social challenges not fully reflected.',
ins:{business:'ABB global HQ. Heavy industry. Growing tech.',tourist:'Castle, Mälaren.',safety:'Good overall. Some areas.',investment:'Industrial premium.',nightlife:'Growing.',infrastructure:'Lake port, tåg Stockholm 60 min.'},
col:'#d97706', umi:+24},
// ── NORRKÖPING ─────────────────────────────────────────────────
// NORRKÖPING
{id:'norrkoping-centrum', name:'Norrköping', lat:58.594, lng:16.188, r:2000,
s:{business:7.1,tourist:7.4,safety:7.2,investment:7.4,nightlife:6.2,infrastructure:7.6},
c:'medium', ct:'Industrial heritage city. Some contradiction in safety data.',
ins:{business:'Industristaden transformation. Tech startups.',tourist:'Motala Ström, industrial museums.',safety:'Some areas elevated.',investment:'Gentrification active.',nightlife:'Good student scene.',infrastructure:'E4, tåg. Direct Stockholm/Malmö.'},
col:'#d97706', umi:+36},
// ── JÖNKÖPING ──────────────────────────────────────────────────
// JÖNKÖPING
{id:'jonkoping-centrum', name:'Jönköping', lat:57.782, lng:14.165, r:2200,
s:{business:8.2,tourist:6.8,safety:8.4,investment:7.8,nightlife:6.2,infrastructure:7.8},
c:'low', ct:'Match capital. Strong evangelical heritage. Business hub.',
ins:{business:'Husqvarna, IKEA (nearby), match capital history.',tourist:'Tändsticsmuseet, Vättern.',safety:'Good.',investment:'Strong business demand.',nightlife:'Decent.',infrastructure:'E4, Göteborg/Stockholm junction.'},
col:'#16a34a', umi:+32},
// ── UMEÅ ───────────────────────────────────────────────────────
// UMEÅ
{id:'umea-centrum', name:'Umeå', lat:63.826, lng:20.263, r:2200,
s:{business:7.4,tourist:6.8,safety:9.2,investment:7.6,nightlife:7.4,infrastructure:7.6},
c:'low', ct:'Fastest growing northern city. Strong alignment.',
ins:{business:'Umeå University, ITS. Tech cluster.',tourist:'Bildmuseet, cultural capital.',safety:'Excellent.',investment:'Consistent growth. Young demographic.',nightlife:'Very good for city size.',infrastructure:'E4, tåg, airport.'},
col:'#16a34a', umi:+54},
// ── LULEÅ ──────────────────────────────────────────────────────
// LULEÅ
{id:'lulea-centrum', name:'Luleå', lat:65.584, lng:22.157, r:2000,
s:{business:7.8,tourist:5.8,safety:9.4,investment:8.4,nightlife:5.8,infrastructure:7.4},
c:'low', ct:'Data center capital of Europe. SSAB, LKAB. Strategic.',
ins:{business:'H2 Green Steel, SSAB, Facebook/Meta data center.',tourist:'Gammelstad UNESCO.',safety:'Excellent.',investment:'Green industry transformation — massive.',nightlife:'OK for size.',infrastructure:'E4, tåg, airport.'},
col:'#16a34a', umi:+88},
// ── KIRUNA ─────────────────────────────────────────────────────
// KIRUNA
{id:'kiruna-centrum', name:'Kiruna', lat:67.856, lng:20.225, r:2400,
s:{business:7.2,tourist:8.4,safety:9.6,investment:8.8,nightlife:4.8,infrastructure:6.8},
c:'high', ct:'City moving 3km east due to LKAB mining. Massive contradiction: official maps show old location, physical reality is new construction.',
ins:{business:'LKAB iron ore. Space industry (ESRANGE).',tourist:'Northern lights, Icehotel, LKAB tours.',safety:'Very safe.',investment:'Mining premium. City move = unique opportunity.',nightlife:'Limited.',infrastructure:'Airport, tåg. Remote.'},
col:'#f97316', umi:+62},
// ── SUNDSVALL ──────────────────────────────────────────────────
// SUNDSVALL
{id:'sundsvall-centrum', name:'Sundsvall', lat:62.391, lng:17.307, r:2000,
s:{business:6.8,tourist:5.8,safety:8.6,investment:6.8,nightlife:5.4,infrastructure:7.2},
c:'low', ct:'Stone city. Timrå data centers. Regional hub.',
ins:{business:'Timrå data centers, paper industry.',tourist:'Stone city centre.',safety:'Good.',investment:'Stable regional.',nightlife:'Good for size.',infrastructure:'E4, tåg, airport.'},
col:'#16a34a', umi:+22},
// ── GÄVLE ──────────────────────────────────────────────────────
// GÄVLE
{id:'gavle-centrum', name:'Gävle', lat:60.675, lng:17.142, r:2000,
s:{business:6.8,tourist:6.2,safety:7.8,investment:6.8,nightlife:5.8,infrastructure:7.4},
c:'medium', ct:'Goat city. Port town. Some areas higher risk.',
ins:{business:'Port, paper, Gevalia (coffee).',tourist:'Julbocken, old town.',safety:'Generally good.',investment:'Stable port economy.',nightlife:'Decent.',infrastructure:'E4, tåg Stockholm 90 min.'},
col:'#d97706', umi:+18},
// ── KARLSTAD ───────────────────────────────────────────────────
// KARLSTAD
{id:'karlstad-centrum', name:'Karlstad', lat:59.379, lng:13.503, r:2000,
s:{business:7.1,tourist:7.2,safety:8.8,investment:7.2,nightlife:6.8,infrastructure:7.4},
c:'low', ct:'Sun city. Klarälven delta. Regional capital Värmland.',
ins:{business:'Regional capital. Tetra Pak R&D.',tourist:'Klarälven, sun city.',safety:'Very safe.',investment:'Good regional.',nightlife:'Good university city.',infrastructure:'E18, tåg.'},
col:'#16a34a', umi:+26},
// ── BORÅS ──────────────────────────────────────────────────────
// BORÅS
{id:'boras-centrum', name:'Borås', lat:57.721, lng:12.940, r:1800,
s:{business:7.4,tourist:5.8,safety:7.8,investment:7.2,nightlife:5.4,infrastructure:7.4},
c:'medium', ct:'Textile city. E-commerce boom. Some social challenges.',
ins:{business:'Textilhögskolan, e-commerce logistics. BORÅS 2030.',tourist:'Textile museum, art.',safety:'Generally good.',investment:'E-commerce driving growth.',nightlife:'Growing.',infrastructure:'Bus to Göteborg.'},
col:'#d97706', umi:+34},
// ── VISBY / GOTLAND ───────────────────────────────────────────
// VISBY / GOTLAND
{id:'gotland-visby', name:'Visby', lat:57.638, lng:18.295, r:1800,
s:{business:5.8,tourist:9.8,safety:9.6,investment:7.8,nightlife:8.4,infrastructure:5.8},
c:'low', ct:'UNESCO medieval city. Medieval Week peak. Summer extreme.',
ins:{business:'Tourism, local services.',tourist:'UNESCO. Medieval Week. Top Sweden.',safety:'Exceptional.',investment:'Summer premium. Year-round growing.',nightlife:'Medieval Week world-class. Summer peak.',infrastructure:'Ferry Nynäshamn/Oskarshamn.'},
col:'#16a34a', umi:+28},
/* ── Stockholm fler stadsdelar (batch 3) ── */
/* Stockholm fler stadsdelar (batch 3) */
{id:'bromma',name:'Bromma',lat:59.337,lng:17.938,r:2200,
confidence:{score:81,observation_count:445},
s:{business:6.8,tourist:5.2,safety:8.9,investment:7.8,nightlife:5.1,infrastructure:7.9},
@@ -1564,7 +1564,7 @@ function renderUMI() {
const sign = umi >= 0 ? '+' : '';
const icon = L.divIcon({
className: '',
html: `<div style="background:${col};color: #1d1d1f;font-size:11px;font-weight:800;padding:3px 7px;border-radius:20px;white-space:nowrap;box-shadow:0 2px 6px rgba(0,0,0,0.2);font-family:-apple-system,sans-serif">${sign}${umi}</div>`,
html: `<div style="background:${col};color: #1d1d1f;font-size:11px;font-weight:800;padding:3px 7px;border-radius: var(--radius-xl);white-space:nowrap;box-shadow:0 2px 6px rgba(0,0,0,0.2);font-family:-apple-system,sans-serif">${sign}${umi}</div>`,
iconAnchor: [20, 10]
});
const m = L.marker([d.lat - 0.008, d.lng], {icon, interactive: false});
@@ -1635,7 +1635,7 @@ function buildPopup(d,p){
return `<div class="mpop">
<div class="mpop-eyebrow" style="display:flex;align-items:center;justify-content:space-between">
<span>LANDVEX ASSESSMENT</span>
<span style="background:${d.umi>=0?'#16a34a':'#dc2626'};color: #1d1d1f;font-size:10px;font-weight:800;padding:2px 8px;border-radius:20px">UMI ${d.umi>=0?'+':''}${d.umi||0}</span>
<span style="background:${d.umi>=0?'#16a34a':'#dc2626'};color: #1d1d1f;font-size:10px;font-weight:800;padding:2px 8px;border-radius: 20px">UMI ${d.umi>=0?'+':''}${d.umi||0}</span>
</div>
<div class="mpop-name">${d.name}</div>
<div class="mpop-score-row">
@@ -1647,7 +1647,7 @@ function buildPopup(d,p){
<div class="mpop-contra ${cl}"><span class="mpop-contra-lbl">${clabels[cl]}</span>${d.ct}</div>
${confidenceBlock(d.id)}
<div class="mpop-insight">${d.ins[p]}</div>
${d.observation ? `<div style="background:#f0fdf4;border:1px solid #bbf7d0;border-radius:8px;padding:8px 10px;margin-top:8px;font-size:10px;font-family:-apple-system,sans-serif">
${d.observation ? `<div style="background:#f0fdf4;border:1px solid #bbf7d0;border-radius: var(--radius-md);padding:8px 10px;margin-top:8px;font-size:10px;font-family:-apple-system,sans-serif">
<div style="font-weight:700;color:#16a34a;margin-bottom:3px">quiXzoom Field Verified</div>
<div style="color:#166534">Coverage: ${d.observation.coverage}% · ${d.observation.frames} frames · ${d.observation.verified}</div>
<div style="color:#15803d;margin-top:2px">Observation ID: ${d.observation.id}</div>
@@ -1675,7 +1675,7 @@ function setProfile(p,btn){
const pr=PROF[p];
const chip=document.getElementById('map-profile-chip');
chip.textContent=pr.label;
chip.style.cssText=`font-size:10px;font-weight:700;border-radius:4px;padding:3px 10px;border:1px solid;letter-spacing:0.3px;background:${pr.chipBg};color:${pr.chipCol};border-color:${pr.chipBorder}`;
chip.style.cssText=`font-size:10px;font-weight:700;border-radius: var(--radius-sm);padding:3px 10px;border:1px solid;letter-spacing:0.3px;background:${pr.chipBg};color:${pr.chipCol};border-color:${pr.chipBorder}`;
const lbl=document.getElementById('map-profile-label');
if(lbl)lbl.textContent=pr.label.split(' ').slice(1).join(' ');
buildList(p);buildRight(p);
@@ -1692,7 +1692,7 @@ function setProfile(p,btn){
}
/* FOCUS DISTRICT */
/* ── MOBILE BOTTOM SHEET ───────────────────────────────────── */
/* MOBILE BOTTOM SHEET */
function isMobile(){return window.innerWidth<=900;}
function openBottomSheet(html){
@@ -1746,7 +1746,7 @@ function buildRoadworkMarkers(){
const col=rwSevCol(rw.severity);
const icon=L.divIcon({
className:'',
html:`<div style="width:14px;height:14px;border-radius:50%;background:${col}44;border:2px solid ${col};cursor:pointer;box-shadow:0 0 6px ${col}66"></div>`,
html:`<div style="width:14px;height:14px;border-radius: var(--radius-full);background:${col}44;border:2px solid ${col};cursor:pointer;box-shadow:0 0 6px ${col}66"></div>`,
iconSize:[14,14],iconAnchor:[7,7]
});
const marker=L.marker([rw.lat,rw.lng],{icon});
@@ -1805,7 +1805,7 @@ function buildAlertMarkers(){
const icon=L.divIcon({
className:'',
html:`<div style="
width:18px;height:18px;border-radius:3px;
width:18px;height:18px;border-radius: var(--radius-sm);
background:rgba(15,25,41,0.92);
border:2px solid ${col};
display:flex;align-items:center;justify-content:center;
@@ -1864,7 +1864,7 @@ window.addEventListener('DOMContentLoaded',()=>{
polys[d.id]=poly;
if(map.getZoom()>=8){const labelM=L.marker([d.lat,d.lng],{
icon:L.divIcon({className:'label-marker',html:`<div style="font-size:11px;font-weight:700;color: #1d1d1f;text-shadow:0 1px 6px rgba(0,0,0,1),0 0 12px rgba(0,0,0,0.8);white-space:nowrap;pointer-events:none;letter-spacing:-0.2px;background:rgba(0,0,0,0.35);padding:2px 6px;border-radius:4px">${d.name}</div>`,iconAnchor:[0,0]}),
icon:L.divIcon({className:'label-marker',html:`<div style="font-size:11px;font-weight:700;color: #1d1d1f;text-shadow:0 1px 6px rgba(0,0,0,1),0 0 12px rgba(0,0,0,0.8);white-space:nowrap;pointer-events:none;letter-spacing:-0.2px;background:rgba(0,0,0,0.35);padding:2px 6px;border-radius: 4px">${d.name}</div>`,iconAnchor:[0,0]}),
interactive:false
});
labelM.addTo(map);}
@@ -1899,7 +1899,7 @@ window.addEventListener('DOMContentLoaded',()=>{
setInterval(updateFreshnessClock,30000);
});
/* ── MACRO INTELLIGENCE ───────────────────────────────────────── */
/* MACRO INTELLIGENCE */
async function loadGDP(){
// World Bank API blocked from server-side — use cached 2024 value
document.getElementById('gdp-val').textContent='$57k';
@@ -1970,12 +1970,12 @@ function renderUrbanEvents(){
<div style="flex:1">
<div style="font-size:13px;font-weight:600;color:#1c1c1e;margin-bottom:3px">${e.title}</div>
<div style="font-size:9px;color:rgba(220,232,245,0.4);line-height:1.3">${e.impact}</div>
<div style="display:inline-block;font-size:9px;font-weight:700;margin-top:3px;padding:1px 6px;border-radius:3px;background:rgba(0,0,0,0.03);color:rgba(220,232,245,0.4)">${e.type}</div>
<div style="display:inline-block;font-size:9px;font-weight:700;margin-top:3px;padding:1px 6px;border-radius: var(--radius-sm);background:rgba(0,0,0,0.03);color:rgba(220,232,245,0.4)">${e.type}</div>
</div>
</div>`).join('');
}
/* ── POLICE EVENTS (Polisen.se live API) ─────────────────────────────────── */
/* POLICE EVENTS (Polisen.se live API) */
const INCIDENT_COLORS={
'Misshandel':'#ef4444','Misshandel, grov':'#dc2626',
'Rån':'#dc2626','Rån, försök':'#f97316',
@@ -2059,7 +2059,7 @@ function updateDistrictSafetyFromPolice(){
});
}
/* ── EVENT ENGINE ─────────────────────────────────────────────── */
/* EVENT ENGINE */
const EVENTS = [
{id:'e1',district:'rinkeby',type:'critical',category:'Safety',title:'Shooting incident reported',detail:'2nd incident this week near Rinkeby torg. Police events API: +340% vs 30-day avg.',date:'Jun 21, 02:32',sources:['Polisen.se live','quiXzoom observation'],confidence:94},
{id:'e2',district:'sodermalm',type:'important',category:'Commercial',title:'18 new vacancies detected',detail:'Götgatan corridor shows accelerating vacancy. Up from 12 last month.',date:'Jun 20',sources:['quiXzoom observation','Fastighetsregistret'],confidence:88},
@@ -2082,7 +2082,7 @@ function renderEvents(){
<div style="padding:7px 0;border-bottom:1px solid rgba(0,0,0,0.04);cursor:pointer" onclick="focusDist('${e.district}')">
<div style="display:flex;align-items:flex-start;justify-content:space-between;gap:6px;margin-bottom:3px">
<div style="font-size:13px;font-weight:700;color:#1c1c1e;line-height:1.3;flex:1">${e.title}</div>
<span style="font-size:10px;font-weight:800;border-radius:4px;padding:3px 7px;white-space:nowrap;background:${EVENT_COLORS[e.type]}18;color:${EVENT_COLORS[e.type]};flex-shrink:0">${EVENT_LABELS[e.type]}</span>
<span style="font-size:10px;font-weight:800;border-radius: var(--radius-sm);padding:3px 7px;white-space:nowrap;background:${EVENT_COLORS[e.type]}18;color:${EVENT_COLORS[e.type]};flex-shrink:0">${EVENT_LABELS[e.type]}</span>
</div>
<div style="font-size:12px;color:rgba(28,28,30,0.7);line-height:1.4;margin-bottom:4px">${e.detail}</div>
<div style="display:flex;align-items:center;justify-content:space-between">
@@ -2092,7 +2092,7 @@ function renderEvents(){
</div>`).join('');
}
/* ── CONFIDENCE ENGINE ─────────────────────────────────────────── */
/* CONFIDENCE ENGINE */
const confSources = {
city: [{src:'Official data',w:35,c:96},{src:'quiXzoom obs',w:40,c:96},{src:'Cross-validation',w:25,c:94}],
ostermalm: [{src:'Official data',w:35,c:98},{src:'quiXzoom obs',w:40,c:94},{src:'Cross-validation',w:25,c:96}],
@@ -2108,15 +2108,15 @@ function confidenceBlock(distId){
if(!sources) return '';
const weighted = Math.round(sources.reduce((s,x)=>s+x.c*x.w/100,0));
const col = weighted>=88?'#4ade80':weighted>=75?'#fbbf24':'#f87171';
return `<div style="background:rgba(0,0,0,0.03);border-radius:7px;padding:7px 9px;margin:6px 0">
return `<div style="background:rgba(0,0,0,0.03);border-radius: var(--radius-sm);padding:7px 9px;margin:6px 0">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:5px">
<div style="font-size:9px;font-weight:700;letter-spacing:0.5px;text-transform:uppercase;color:rgba(220,232,245,0.3)">Confidence Engine</div>
<div style="font-size:13px;font-weight:900;color:${col};font-family:monospace">${weighted}%</div>
</div>
${sources.map(s=>`<div style="display:flex;align-items:center;gap:6px;margin-bottom:3px;font-size:9px">
<span style="width:90px;color:rgba(220,232,245,0.4)">${s.src}</span>
<div style="flex:1;height:3px;background:rgba(0,0,0,0.04);border-radius:2px">
<div style="width:${s.c}%;height:100%;background:${s.c>=88?'#4ade80':s.c>=75?'#fbbf24':'#f87171'};border-radius:2px"></div>
<div style="flex:1;height:3px;background:rgba(0,0,0,0.04);border-radius: 2px">
<div style="width:${s.c}%;height:100%;background:${s.c>=88?'#4ade80':s.c>=75?'#fbbf24':'#f87171'};border-radius: 2px"></div>
</div>
<span style="width:28px;text-align:right;font-weight:700;color:rgba(220,232,245,0.6)">${s.c}%</span>
</div>`).join('')}
@@ -2125,7 +2125,7 @@ function confidenceBlock(distId){
}
/* SCB DATA (hårdkodad för Stockholm stadsdelar — uppdateras via SCB API) */
/* ── PRICE INDEX DATA ─────────────────────────────────────────── */
/* PRICE INDEX DATA */
// Sources: Hemnet Q1 2026, SvD Bästa Nyheter, Prisjakt, local surveys
const PRICE_DATA={
// beer_bar/pub: 33cl draft SEK
@@ -2145,7 +2145,7 @@ const PRICE_DATA={
rinkeby: {beer_bar:52, beer_pub:44, coffee:38, lunch:95, dinner_mid:195, apt_rent_sqm:1180, apt_buy_sqm:31000, hotel_night:680, seven_eleven:71, housing_idx:23}
};
/* ── SCHOOL DATA ──────────────────────────────────────────── */
/* SCHOOL DATA */
// Source: Skolverket öppen statistik 2023/2024, SIRIS-databasen
// avg_merit_gr9: genomsnittligt meritvärde åk 9 (max 340)
// pct_qualified_gr9: andel behöriga till gymnasiet (%)
@@ -2234,7 +2234,7 @@ const SCHOOL_DATA={
}
};
/* ── HEALTH & SOCIAL DATA ──────────────────────────────────── */
/* HEALTH & SOCIAL DATA */
// Sources: Socialstyrelsen, Väntetider i vård, Folkihälsomyndigheten, Region Stockholm 2024
// wait_gp_days: median days to get GP appointment
// wait_specialist_days: median days to specialist
@@ -2317,17 +2317,17 @@ function getHealthPopup(distId){
<!-- Key stats -->
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:4px;margin-bottom:6px">
<div style="background:rgba(0,0,0,0.04);border-radius:6px;padding:6px 7px;text-align:center">
<div style="background:rgba(0,0,0,0.04);border-radius: var(--radius-sm);padding:6px 7px;text-align:center">
<div style="font-size:9px;color:rgba(220,232,245,0.35);margin-bottom:2px">Life exp.</div>
<div style="font-size:14px;font-weight:900;color:${leColor};font-family:var(--mono)">${h.life_expectancy}</div>
<div style="font-size:8px;color:rgba(220,232,245,0.25)">years</div>
</div>
<div style="background:rgba(0,0,0,0.04);border-radius:6px;padding:6px 7px;text-align:center">
<div style="background:rgba(0,0,0,0.04);border-radius: var(--radius-sm);padding:6px 7px;text-align:center">
<div style="font-size:9px;color:rgba(220,232,245,0.35);margin-bottom:2px">Drug deaths</div>
<div style="font-size:14px;font-weight:900;color:${drugColor};font-family:var(--mono)">${h.drug_deaths_per100k}</div>
<div style="font-size:8px;color:rgba(220,232,245,0.25)">/100k</div>
</div>
<div style="background:rgba(0,0,0,0.04);border-radius:6px;padding:6px 7px;text-align:center">
<div style="background:rgba(0,0,0,0.04);border-radius: var(--radius-sm);padding:6px 7px;text-align:center">
<div style="font-size:9px;color:rgba(220,232,245,0.35);margin-bottom:2px">Mental health</div>
<div style="font-size:14px;font-weight:900;color:${h.mental_health_pct>18?'#f87171':h.mental_health_pct>14?'#fbbf24':'#4ade80'};font-family:var(--mono)">${h.mental_health_pct}%</div>
<div style="font-size:8px;color:rgba(220,232,245,0.25)">seeking care</div>
@@ -2335,7 +2335,7 @@ function getHealthPopup(distId){
</div>
<!-- Wait times -->
<div style="background:rgba(0,0,0,0.04);border-radius:6px;padding:7px 9px;margin-bottom:6px">
<div style="background:rgba(0,0,0,0.04);border-radius: var(--radius-sm);padding:7px 9px;margin-bottom:6px">
<div style="font-size:9px;color:rgba(220,232,245,0.35);font-weight:700;text-transform:uppercase;letter-spacing:0.5px;margin-bottom:5px">Healthcare wait times</div>
<div style="display:flex;flex-direction:column;gap:4px;font-size:9px">
<div style="display:flex;align-items:center;justify-content:space-between">
@@ -2355,25 +2355,25 @@ function getHealthPopup(distId){
<!-- Additional stats -->
<div style="display:grid;grid-template-columns:1fr 1fr;gap:4px;margin-bottom:6px;font-size:10px">
<div style="background:rgba(0,0,0,0.04);border-radius:5px;padding:5px 8px">
<div style="background:rgba(0,0,0,0.04);border-radius: var(--radius-sm);padding:5px 8px">
<div style="color:rgba(220,232,245,0.35);font-size:9px">Obesity rate</div>
<div style="color:${h.obesity_pct>18?'#f87171':h.obesity_pct>12?'#fbbf24':'#4ade80'};font-weight:700">${h.obesity_pct}%</div>
</div>
<div style="background:rgba(0,0,0,0.04);border-radius:5px;padding:5px 8px">
<div style="background:rgba(0,0,0,0.04);border-radius: var(--radius-sm);padding:5px 8px">
<div style="color:rgba(220,232,245,0.35);font-size:9px">Long-term sick leave</div>
<div style="color:${h.sick_leave_pct>7?'#f87171':h.sick_leave_pct>5?'#fbbf24':'#4ade80'};font-weight:700">${h.sick_leave_pct}%</div>
</div>
<div style="background:rgba(0,0,0,0.04);border-radius:5px;padding:5px 8px">
<div style="background:rgba(0,0,0,0.04);border-radius: var(--radius-sm);padding:5px 8px">
<div style="color:rgba(220,232,245,0.35);font-size:9px">Nearest hospital</div>
<div style="color:#dce8f5;font-weight:600;font-size:9px;line-height:1.3">${h.nearest_hospital}</div>
</div>
<div style="background:rgba(0,0,0,0.04);border-radius:5px;padding:5px 8px">
<div style="background:rgba(0,0,0,0.04);border-radius: var(--radius-sm);padding:5px 8px">
<div style="color:rgba(220,232,245,0.35);font-size:9px">Elder care score</div>
<div style="color:${h.elder_care_satisfaction>=8?'#4ade80':h.elder_care_satisfaction>=7?'#fbbf24':'#f87171'};font-weight:700">${h.elder_care_satisfaction}/10</div>
</div>
</div>
${isContra?`<div style="background:rgba(239,68,68,0.08);border:1px solid rgba(239,68,68,0.18);border-radius:6px;padding:6px 9px;font-size:10px;color:rgba(220,232,245,0.55);line-height:1.4;margin-bottom:6px">
${isContra?`<div style="background:rgba(239,68,68,0.08);border:1px solid rgba(239,68,68,0.18);border-radius: var(--radius-sm);padding:6px 9px;font-size:10px;color:rgba(220,232,245,0.55);line-height:1.4;margin-bottom:6px">
<span style="font-weight:700;color:#f87171">Contradiction: </span>${h.context}
</div>`:''}
@@ -2402,7 +2402,7 @@ function getSchoolPopup(distId){
<div style="font-size:9px;font-weight:700;letter-spacing:0.8px;text-transform:uppercase;color:rgba(220,232,245,0.3);margin-bottom:7px">SCHOOL PERFORMANCE · SKOLVERKET 2024</div>
<!-- Merit score header -->
<div style="background:rgba(0,0,0,0.04);border-radius:7px;padding:8px 10px;margin-bottom:6px">
<div style="background:rgba(0,0,0,0.04);border-radius: var(--radius-sm);padding:8px 10px;margin-bottom:6px">
<div style="display:flex;align-items:flex-start;justify-content:space-between;margin-bottom:6px">
<div>
<div style="font-size:9px;color:rgba(220,232,245,0.35);text-transform:uppercase;letter-spacing:0.5px">Avg merit grade 9</div>
@@ -2414,17 +2414,17 @@ function getSchoolPopup(distId){
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:4px;margin-bottom:6px">
<div style="background:rgba(0,0,0,0.03);border-radius:5px;padding:5px 6px;text-align:center">
<div style="background:rgba(0,0,0,0.03);border-radius: var(--radius-sm);padding:5px 6px;text-align:center">
<div style="font-size:9px;color:rgba(220,232,245,0.3)">Qualified</div>
<div style="font-size:12px;font-weight:800;color:${qualColor}">${sc.pct_qualified_gr9}%</div>
<div style="font-size:8px;color:rgba(220,232,245,0.25)">to gymnasium</div>
</div>
<div style="background:rgba(0,0,0,0.03);border-radius:5px;padding:5px 6px;text-align:center">
<div style="background:rgba(0,0,0,0.03);border-radius: var(--radius-sm);padding:5px 6px;text-align:center">
<div style="font-size:9px;color:rgba(220,232,245,0.3)">Pass all</div>
<div style="font-size:12px;font-weight:800;color:${meritColor}">${sc.pct_passed_all}%</div>
<div style="font-size:8px;color:rgba(220,232,245,0.25)">subjects</div>
</div>
<div style="background:rgba(0,0,0,0.03);border-radius:5px;padding:5px 6px;text-align:center">
<div style="background:rgba(0,0,0,0.03);border-radius: var(--radius-sm);padding:5px 6px;text-align:center">
<div style="font-size:9px;color:rgba(220,232,245,0.3)">Trend</div>
<div style="font-size:12px;font-weight:800;color:${trendColor}">${sc.grade_trend.split(' ')[0]}</div>
<div style="font-size:8px;color:rgba(220,232,245,0.25)">vs 2021</div>
@@ -2432,7 +2432,7 @@ function getSchoolPopup(distId){
</div>
<div style="display:flex;align-items:center;justify-content:space-between;font-size:9px">
<span style="color:rgba(220,232,245,0.4)">${sc.school_count} schools · ${sc.teacher_ratio} pupils/teacher</span>
<span style="padding:1px 7px;border-radius:3px;font-weight:700;${sc.inspection_status==='Approved'?'background:rgba(34,197,94,0.12);color:#4ade80':sc.inspection_status==='Under review'?'background:rgba(239,68,68,0.12);color:#f87171':'background:rgba(251,191,36,0.12);color:#fbbf24'}">${sc.inspection_status}</span>
<span style="padding:1px 7px;border-radius: var(--radius-sm);font-weight:700;${sc.inspection_status==='Approved'?'background:rgba(34,197,94,0.12);color:#4ade80':sc.inspection_status==='Under review'?'background:rgba(239,68,68,0.12);color:#f87171':'background:rgba(251,191,36,0.12);color:#fbbf24'}">${sc.inspection_status}</span>
</div>
</div>
@@ -2456,7 +2456,7 @@ function getSchoolPopup(distId){
</div>`;
}
/* ── HOUSING STOCK DATA ─────────────────────────────────── */
/* HOUSING STOCK DATA */
// Sources: SCB Fastighetsregistret, Boverket, Stockholm stad 2024
// build_era: dominant construction period
// median_build_year: median year of existing stock
@@ -2560,12 +2560,12 @@ function getHousingPopup(distId){
if(!h)return '';
const bciColor=h.condition_idx>=8.5?'#4ade80':h.condition_idx>=7?'#fbbf24':h.condition_idx>=5?'#f97316':'#f87171';
const trendColor=h.bci_trend.startsWith('+')?'#4ade80':h.bci_trend.startsWith('-')?'#f87171':'#fbbf24';
const segBar=(pct,col)=>`<div style="display:inline-block;height:6px;width:${pct*1.4}px;background:${col};border-radius:1px;margin-right:1px"></div>`;
const segBar=(pct,col)=>`<div style="display:inline-block;height:6px;width:${pct*1.4}px;background:${col};border-radius: 1px;margin-right:1px"></div>`;
return `<div style="border-top:1px solid rgba(0,0,0,0.04);margin-top:8px;padding-top:8px;font-family:-apple-system,sans-serif">
<div style="font-size:9px;font-weight:700;letter-spacing:0.8px;text-transform:uppercase;color:rgba(220,232,245,0.3);margin-bottom:7px">HOUSING STOCK INTELLIGENCE</div>
<!-- BCI header -->
<div style="background:rgba(0,0,0,0.04);border-radius:7px;padding:8px 10px;margin-bottom:6px">
<div style="background:rgba(0,0,0,0.04);border-radius: var(--radius-sm);padding:8px 10px;margin-bottom:6px">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:5px">
<div>
<div style="font-size:9px;color:rgba(220,232,245,0.35);text-transform:uppercase;letter-spacing:0.5px">Building Condition Index</div>
@@ -2581,26 +2581,26 @@ function getHousingPopup(distId){
<!-- Build era & renovation -->
<div style="display:grid;grid-template-columns:1fr 1fr;gap:4px;margin-bottom:6px;font-size:10px">
<div style="background:rgba(0,0,0,0.04);border-radius:5px;padding:6px 8px">
<div style="background:rgba(0,0,0,0.04);border-radius: var(--radius-sm);padding:6px 8px">
<div style="color:rgba(220,232,245,0.35);font-size:9px;margin-bottom:2px">Build era</div>
<div style="color:#dce8f5;font-weight:600;font-size:10px;line-height:1.3">${h.build_era}</div>
</div>
<div style="background:rgba(0,0,0,0.04);border-radius:5px;padding:6px 8px">
<div style="background:rgba(0,0,0,0.04);border-radius: var(--radius-sm);padding:6px 8px">
<div style="color:rgba(220,232,245,0.35);font-size:9px;margin-bottom:2px">Renovated post-2000</div>
<div style="color:#38bdf8;font-weight:700">${h.renovated_pct}% of stock</div>
</div>
<div style="background:rgba(0,0,0,0.04);border-radius:5px;padding:6px 8px">
<div style="background:rgba(0,0,0,0.04);border-radius: var(--radius-sm);padding:6px 8px">
<div style="color:rgba(220,232,245,0.35);font-size:9px;margin-bottom:2px">Avg apartment</div>
<div style="color:#dce8f5;font-weight:700">${h.avg_apt_size} sqm</div>
</div>
<div style="background:rgba(0,0,0,0.04);border-radius:5px;padding:6px 8px">
<div style="background:rgba(0,0,0,0.04);border-radius: var(--radius-sm);padding:6px 8px">
<div style="color:rgba(220,232,245,0.35);font-size:9px;margin-bottom:2px">Commercial vacancy</div>
<div style="color:${h.vacancy_commercial_pct>12?'#f87171':h.vacancy_commercial_pct>7?'#fbbf24':'#4ade80'};font-weight:700">${h.vacancy_commercial_pct}%</div>
</div>
</div>
<!-- Tenure mix -->
<div style="background:rgba(0,0,0,0.04);border-radius:6px;padding:7px 9px;margin-bottom:6px">
<div style="background:rgba(0,0,0,0.04);border-radius: var(--radius-sm);padding:7px 9px;margin-bottom:6px">
<div style="font-size:9px;color:rgba(220,232,245,0.35);margin-bottom:5px">Tenure mix</div>
<div style="margin-bottom:4px">
${segBar(h.brf_pct,'#4ade80')}
@@ -2628,7 +2628,7 @@ function getHousingPopup(distId){
</div>`;
}
/* ── COMMERCIAL VITALITY INDEX ───────────────────────────── */
/* COMMERCIAL VITALITY INDEX */
// Sources: SCB NV0101 (företag per SNI), Bolagsverket nyregistrerade/konkurser,
// quiXzoom kommersiell aktivitet, Fastighetsregistret vakanser
// SNI-mix: dominanta branscher per stadsdel
@@ -2734,7 +2734,7 @@ const CVI_DATA={
}
};
/* ── ECONOMIC DNA ────────────────────────────────────────── */
/* ECONOMIC DNA */
// Aggregated area-level data only. No individual data.
// Sources: SCB disp.inkomst, Skatteverket regional, Fastighetsregistret, Bolagsverket
// purchasing_power_idx: area purchasing power vs Stockholm avg (100)
@@ -2829,25 +2829,25 @@ function getEconomicDNA(distId){
<div style="font-size:9px;font-weight:700;letter-spacing:0.8px;text-transform:uppercase;color:rgba(220,232,245,0.3);margin-bottom:7px">ECONOMIC DNA</div>
<!-- Profile -->
<div style="background:rgba(0,0,0,0.04);border-radius:7px;padding:8px 10px;margin-bottom:6px">
<div style="background:rgba(0,0,0,0.04);border-radius: var(--radius-sm);padding:8px 10px;margin-bottom:6px">
<div style="font-size:11px;font-weight:800;color:#dce8f5;margin-bottom:4px;line-height:1.3">${e.profile}</div>
<div style="display:flex;flex-wrap:wrap;gap:3px;margin-bottom:6px">
${e.profile_tags.map(t=>`<span style="font-size:9px;background:rgba(0,0,0,0.04);color:rgba(220,232,245,0.5);border-radius:4px;padding:2px 6px">${t}</span>`).join('')}
${e.profile_tags.map(t=>`<span style="font-size:9px;background:rgba(0,0,0,0.04);color:rgba(220,232,245,0.5);border-radius: var(--radius-sm);padding:2px 6px">${t}</span>`).join('')}
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:4px;font-size:10px">
<div style="background:rgba(0,0,0,0.04);border-radius:5px;padding:5px 7px">
<div style="background:rgba(0,0,0,0.04);border-radius: var(--radius-sm);padding:5px 7px">
<div style="font-size:9px;color:rgba(220,232,245,0.3)">Purchasing power index</div>
<div style="font-size:14px;font-weight:900;color:${ppColor};font-family:var(--mono)">${e.purchasing_power_idx}<span style="font-size:9px;font-weight:400;color:rgba(220,232,245,0.3)"> (avg=100)</span></div>
</div>
<div style="background:rgba(0,0,0,0.04);border-radius:5px;padding:5px 7px">
<div style="background:rgba(0,0,0,0.04);border-radius: var(--radius-sm);padding:5px 7px">
<div style="font-size:9px;color:rgba(220,232,245,0.3)">HH disposable income</div>
<div style="font-size:12px;font-weight:800;color:#dce8f5">${inc} kr</div>
</div>
<div style="background:rgba(0,0,0,0.04);border-radius:5px;padding:5px 7px">
<div style="background:rgba(0,0,0,0.04);border-radius: var(--radius-sm);padding:5px 7px">
<div style="font-size:9px;color:rgba(220,232,245,0.3)">Commercial/residential</div>
<div style="font-size:13px;font-weight:900;color:${crColor};font-family:var(--mono)">${e.commercial_vs_residential}x</div>
</div>
<div style="background:rgba(0,0,0,0.04);border-radius:5px;padding:5px 7px">
<div style="background:rgba(0,0,0,0.04);border-radius: var(--radius-sm);padding:5px 7px">
<div style="font-size:9px;color:rgba(220,232,245,0.3)">Property value index</div>
<div style="font-size:12px;font-weight:800;color:#dce8f5">${e.property_value_idx}<span style="font-size:9px;color:rgba(220,232,245,0.3)"> (avg=100)</span></div>
</div>
@@ -2855,13 +2855,13 @@ function getEconomicDNA(distId){
</div>
<!-- Gap / Warning -->
${e.early_warning?`<div style="background:rgba(239,68,68,0.08);border:1px solid rgba(239,68,68,0.2);border-radius:7px;padding:7px 10px;margin-bottom:6px">
${e.early_warning?`<div style="background:rgba(239,68,68,0.08);border:1px solid rgba(239,68,68,0.2);border-radius: var(--radius-sm);padding:7px 10px;margin-bottom:6px">
<div style="font-size:9px;font-weight:800;color:#f87171;text-transform:uppercase;letter-spacing:0.5px;margin-bottom:3px">Early Warning Signal</div>
<div style="font-size:12px;color:rgba(28,28,30,0.75);line-height:1.5">${e.key_insight}</div>
</div>`:e.gap_opportunity?`<div style="background:rgba(56,189,248,0.07);border:1px solid rgba(56,189,248,0.15);border-radius:7px;padding:7px 10px;margin-bottom:6px">
</div>`:e.gap_opportunity?`<div style="background:rgba(56,189,248,0.07);border:1px solid rgba(56,189,248,0.15);border-radius: var(--radius-sm);padding:7px 10px;margin-bottom:6px">
<div style="font-size:9px;font-weight:800;color:#38bdf8;text-transform:uppercase;letter-spacing:0.5px;margin-bottom:3px">Gap Opportunity Detected</div>
<div style="font-size:12px;color:rgba(28,28,30,0.75);line-height:1.5">${e.key_insight}</div>
</div>`:`<div style="background:rgba(0,0,0,0.03);border-radius:7px;padding:7px 10px;margin-bottom:6px">
</div>`:`<div style="background:rgba(0,0,0,0.03);border-radius: var(--radius-sm);padding:7px 10px;margin-bottom:6px">
<div style="font-size:10px;color:rgba(220,232,245,0.5);line-height:1.4">${e.key_insight}</div>
</div>`}
@@ -2883,7 +2883,7 @@ function getCVIPopup(distId){
<div style="font-size:9px;font-weight:700;letter-spacing:0.8px;text-transform:uppercase;color:rgba(220,232,245,0.3);margin-bottom:7px">COMMERCIAL VITALITY INDEX</div>
<!-- CVI header -->
<div style="background:rgba(0,0,0,0.04);border-radius:7px;padding:8px 10px;margin-bottom:6px">
<div style="background:rgba(0,0,0,0.04);border-radius: var(--radius-sm);padding:8px 10px;margin-bottom:6px">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:6px">
<div>
<div style="font-size:9px;color:rgba(220,232,245,0.35);text-transform:uppercase;letter-spacing:0.5px">CVI Score</div>
@@ -2895,15 +2895,15 @@ function getCVIPopup(distId){
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:4px;font-size:9px">
<div style="background:rgba(0,0,0,0.04);border-radius:5px;padding:5px 6px;text-align:center">
<div style="background:rgba(0,0,0,0.04);border-radius: var(--radius-sm);padding:5px 6px;text-align:center">
<div style="color:rgba(220,232,245,0.3)">Active co.</div>
<div style="color:#dce8f5;font-weight:800">${c.active_companies.toLocaleString('sv-SE')}</div>
</div>
<div style="background:rgba(0,0,0,0.04);border-radius:5px;padding:5px 6px;text-align:center">
<div style="background:rgba(0,0,0,0.04);border-radius: var(--radius-sm);padding:5px 6px;text-align:center">
<div style="color:rgba(220,232,245,0.3)">New YTD</div>
<div style="color:#4ade80;font-weight:800">+${c.new_registrations_ytd}</div>
</div>
<div style="background:rgba(0,0,0,0.04);border-radius:5px;padding:5px 6px;text-align:center">
<div style="background:rgba(0,0,0,0.04);border-radius: var(--radius-sm);padding:5px 6px;text-align:center">
<div style="color:rgba(220,232,245,0.3)">Bankrupt</div>
<div style="color:${bkColor};font-weight:800">${c.bankruptcies_ytd}</div>
</div>
@@ -2911,13 +2911,13 @@ function getCVIPopup(distId){
</div>
<!-- SNI sector mix -->
<div style="background:rgba(0,0,0,0.04);border-radius:6px;padding:7px 9px;margin-bottom:6px">
<div style="background:rgba(0,0,0,0.04);border-radius: var(--radius-sm);padding:7px 9px;margin-bottom:6px">
<div style="font-size:9px;color:rgba(220,232,245,0.35);font-weight:700;text-transform:uppercase;letter-spacing:0.5px;margin-bottom:5px">Sector mix (SNI)</div>
${c.sni_mix.map(s=>`<div style="display:flex;align-items:center;gap:6px;margin-bottom:4px;font-size:9px">
<span style="width:16px;font-weight:800;color:rgba(220,232,245,0.3);flex-shrink:0">${s.code}</span>
<span style="flex:1;color:rgba(220,232,245,0.5)">${s.name}</span>
<div style="width:60px;height:3px;background:rgba(0,0,0,0.04);border-radius:2px">
<div style="width:${s.pct*2}px;height:100%;background:#38bdf8;border-radius:2px"></div>
<div style="width:60px;height:3px;background:rgba(0,0,0,0.04);border-radius: 2px">
<div style="width:${s.pct*2}px;height:100%;background:#38bdf8;border-radius: 2px"></div>
</div>
<span style="width:28px;text-align:right;color:#dce8f5;font-weight:700">${s.pct}%</span>
</div>`).join('')}
@@ -2925,26 +2925,26 @@ function getCVIPopup(distId){
<!-- Revenue & employment -->
<div style="display:grid;grid-template-columns:1fr 1fr;gap:4px;margin-bottom:6px;font-size:10px">
<div style="background:rgba(0,0,0,0.04);border-radius:5px;padding:5px 8px">
<div style="background:rgba(0,0,0,0.04);border-radius: var(--radius-sm);padding:5px 8px">
<div style="color:rgba(220,232,245,0.35);font-size:9px">Revenue trend YoY</div>
<div style="color:${revColor};font-weight:800">${revSign}${c.revenue_trend}%</div>
</div>
<div style="background:rgba(0,0,0,0.04);border-radius:5px;padding:5px 8px">
<div style="background:rgba(0,0,0,0.04);border-radius: var(--radius-sm);padding:5px 8px">
<div style="color:rgba(220,232,245,0.35);font-size:9px">Est. employees</div>
<div style="color:#dce8f5;font-weight:700">${c.employees_est.toLocaleString('sv-SE')}</div>
</div>
<div style="background:rgba(0,0,0,0.04);border-radius:5px;padding:5px 8px">
<div style="background:rgba(0,0,0,0.04);border-radius: var(--radius-sm);padding:5px 8px">
<div style="color:rgba(220,232,245,0.35);font-size:9px">Commercial vacancy</div>
<div style="color:${c.vacancy_commercial>12?'#f87171':c.vacancy_commercial>7?'#fbbf24':'#4ade80'};font-weight:700">${c.vacancy_commercial}%</div>
</div>
<div style="background:rgba(0,0,0,0.04);border-radius:5px;padding:5px 8px">
<div style="background:rgba(0,0,0,0.04);border-radius: var(--radius-sm);padding:5px 8px">
<div style="color:rgba(220,232,245,0.35);font-size:9px">Gentrif. signal</div>
<div style="color:${c.gentrification_signal?'#a78bfa':'rgba(220,232,245,0.4)'};font-weight:700">${c.gentrification_signal?'DETECTED':'None'}</div>
</div>
</div>
<!-- Growth sector -->
<div style="background:rgba(56,189,248,0.06);border:1px solid rgba(56,189,248,0.12);border-radius:6px;padding:7px 9px;margin-bottom:5px">
<div style="background:rgba(56,189,248,0.06);border:1px solid rgba(56,189,248,0.12);border-radius: var(--radius-sm);padding:7px 9px;margin-bottom:5px">
<div style="font-size:9px;color:rgba(56,189,248,0.7);font-weight:700;margin-bottom:2px">Top growth sector</div>
<div style="font-size:10px;color:rgba(220,232,245,0.6);line-height:1.4">${c.top_growth_sector}</div>
</div>
@@ -2962,7 +2962,7 @@ function getPricePopup(distId){
if(!p)return '';
const pb=(val,max,col)=>{
const pct=Math.min(100,Math.round((val/max)*100));
return `<div style="flex:1;height:4px;border-radius:2px;background:rgba(0,0,0,0.04)"><div style="width:${pct}%;height:100%;background:${col};border-radius:2px"></div></div>`;
return `<div style="flex:1;height:4px;border-radius: var(--radius-sm);background:rgba(0,0,0,0.04)"><div style="width:${pct}%;height:100%;background:${col};border-radius: 2px"></div></div>`;
};
const idxColor=idx=>idx>=85?'#f87171':idx>=60?'#fbbf24':'#4ade80';
const beerIdx=Math.round((p.beer_bar/129)*100);
@@ -2975,26 +2975,26 @@ function getPricePopup(distId){
<!-- Index summary row -->
<div style="display:grid;grid-template-columns:1fr 1fr 1fr 1fr;gap:4px;margin-bottom:7px">
<div style="background:rgba(0,0,0,0.04);border-radius:6px;padding:6px 5px;text-align:center">
<div style="background:rgba(0,0,0,0.04);border-radius: var(--radius-sm);padding:6px 5px;text-align:center">
<div style="font-size:9px;color:rgba(220,232,245,0.35);margin-bottom:2px">Beer</div>
<div style="font-size:13px;font-weight:900;color:${idxColor(beerIdx)};font-family:var(--mono)">${beerIdx}</div>
</div>
<div style="background:rgba(0,0,0,0.04);border-radius:6px;padding:6px 5px;text-align:center">
<div style="background:rgba(0,0,0,0.04);border-radius: var(--radius-sm);padding:6px 5px;text-align:center">
<div style="font-size:9px;color:rgba(220,232,245,0.35);margin-bottom:2px">Hotel</div>
<div style="font-size:13px;font-weight:900;color:${idxColor(hotelIdx)};font-family:var(--mono)">${hotelIdx}</div>
</div>
<div style="background:rgba(0,0,0,0.04);border-radius:6px;padding:6px 5px;text-align:center">
<div style="background:rgba(0,0,0,0.04);border-radius: var(--radius-sm);padding:6px 5px;text-align:center">
<div style="font-size:9px;color:rgba(220,232,245,0.35);margin-bottom:2px">7-Eleven</div>
<div style="font-size:13px;font-weight:900;color:${idxColor(sevenIdx)};font-family:var(--mono)">${sevenIdx}</div>
</div>
<div style="background:rgba(0,0,0,0.04);border-radius:6px;padding:6px 5px;text-align:center">
<div style="background:rgba(0,0,0,0.04);border-radius: var(--radius-sm);padding:6px 5px;text-align:center">
<div style="font-size:9px;color:rgba(220,232,245,0.35);margin-bottom:2px">Housing</div>
<div style="font-size:13px;font-weight:900;color:${idxColor(housingIdx)};font-family:var(--mono)">${housingIdx}</div>
</div>
</div>
<!-- Food & drink detail -->
<div style="background:rgba(0,0,0,0.03);border-radius:6px;padding:7px 9px;margin-bottom:5px">
<div style="background:rgba(0,0,0,0.03);border-radius: var(--radius-sm);padding:7px 9px;margin-bottom:5px">
<div style="font-size:9px;color:rgba(220,232,245,0.3);font-weight:700;letter-spacing:0.5px;text-transform:uppercase;margin-bottom:5px">Food & Drink</div>
<div style="display:flex;flex-direction:column;gap:4px;font-size:9px">
<div style="display:flex;align-items:center;gap:6px"><span style="width:68px;color:rgba(220,232,245,0.45)">Bar / club</span>${pb(p.beer_bar,140,'#60a5fa')}<span style="width:40px;text-align:right;color:#dce8f5;font-weight:700">${p.beer_bar} kr</span></div>
@@ -3007,7 +3007,7 @@ function getPricePopup(distId){
</div>
<!-- Accommodation -->
<div style="background:rgba(0,0,0,0.03);border-radius:6px;padding:7px 9px;margin-bottom:5px">
<div style="background:rgba(0,0,0,0.03);border-radius: var(--radius-sm);padding:7px 9px;margin-bottom:5px">
<div style="font-size:9px;color:rgba(220,232,245,0.3);font-weight:700;letter-spacing:0.5px;text-transform:uppercase;margin-bottom:5px">Accommodation</div>
<div style="display:flex;flex-direction:column;gap:4px;font-size:9px">
<div style="display:flex;align-items:center;gap:6px"><span style="width:68px;color:rgba(220,232,245,0.45)">Hotel / night</span>${pb(p.hotel_night,2500,'#f97316')}<span style="width:40px;text-align:right;color:#dce8f5;font-weight:700">${p.hotel_night} kr</span></div>
@@ -3059,8 +3059,8 @@ const SCB_DATA={
};
function ageBar(pct, color){
return `<div style="flex:1;height:4px;border-radius:2px;background:rgba(0,0,0,0.04);overflow:hidden">
<div style="width:${pct}%;height:100%;background:${color};border-radius:2px"></div></div>`;
return `<div style="flex:1;height:4px;border-radius: var(--radius-sm);background:rgba(0,0,0,0.04);overflow:hidden">
<div style="width:${pct}%;height:100%;background:${color};border-radius: 2px"></div></div>`;
}
function getSCBPopup(distId){
@@ -3072,25 +3072,25 @@ function getSCBPopup(distId){
return `<div style="border-top:1px solid rgba(0,0,0,0.04);margin-top:8px;padding-top:8px;font-family:-apple-system,sans-serif">
<div style="font-size:9px;font-weight:700;letter-spacing:0.8px;text-transform:uppercase;color:rgba(220,232,245,0.3);margin-bottom:6px">SCB DEMOGRAPHICS</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:4px;font-size:10px;margin-bottom:8px">
<div style="background:rgba(0,0,0,0.04);border-radius:5px;padding:5px 7px">
<div style="background:rgba(0,0,0,0.04);border-radius: var(--radius-sm);padding:5px 7px">
<div style="color:rgba(220,232,245,0.4);font-size:9px">Population</div>
<div style="color:#dce8f5;font-weight:700">${s.population.toLocaleString('sv-SE')}</div>
</div>
<div style="background:rgba(0,0,0,0.04);border-radius:5px;padding:5px 7px">
<div style="background:rgba(0,0,0,0.04);border-radius: var(--radius-sm);padding:5px 7px">
<div style="color:rgba(220,232,245,0.4);font-size:9px">Median income</div>
<div style="color:#4ade80;font-weight:700">${inc} kr</div>
</div>
<div style="background:rgba(0,0,0,0.04);border-radius:5px;padding:5px 7px">
<div style="background:rgba(0,0,0,0.04);border-radius: var(--radius-sm);padding:5px 7px">
<div style="color:rgba(220,232,245,0.4);font-size:9px">Unemployment</div>
<div style="color:${uColor};font-weight:700">${s.unemployment}%</div>
</div>
<div style="background:rgba(0,0,0,0.04);border-radius:5px;padding:5px 7px">
<div style="background:rgba(0,0,0,0.04);border-radius: var(--radius-sm);padding:5px 7px">
<div style="color:rgba(220,232,245,0.4);font-size:9px">Higher education</div>
<div style="color:#38bdf8;font-weight:700">${s.education_pct}%</div>
</div>
</div>
<!-- Age distribution -->
<div style="background:rgba(0,0,0,0.04);border-radius:6px;padding:7px 9px;margin-bottom:5px">
<div style="background:rgba(0,0,0,0.04);border-radius: var(--radius-sm);padding:7px 9px;margin-bottom:5px">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:6px">
<div style="font-size:9px;color:rgba(220,232,245,0.4)">Age distribution</div>
<div style="font-size:10px;font-weight:800;color:${ageColor}">Median ${s.medianAge} yrs</div>
@@ -3104,7 +3104,7 @@ function getSCBPopup(distId){
</div>
<!-- Education levels -->
<div style="background:rgba(0,0,0,0.04);border-radius:6px;padding:7px 9px;margin-bottom:5px">
<div style="background:rgba(0,0,0,0.04);border-radius: var(--radius-sm);padding:7px 9px;margin-bottom:5px">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:6px">
<div style="font-size:9px;color:rgba(220,232,245,0.4)">Education profile</div>
<div style="font-size:10px;font-weight:800;color:#38bdf8">${s.edu_hogskola_kort+s.edu_hogskola_lang+s.edu_forskning}% university+</div>
@@ -3120,15 +3120,15 @@ function getSCBPopup(distId){
<!-- Social profile -->
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:4px;font-size:10px">
<div style="background:rgba(0,0,0,0.04);border-radius:5px;padding:5px 7px">
<div style="background:rgba(0,0,0,0.04);border-radius: var(--radius-sm);padding:5px 7px">
<div style="color:rgba(220,232,245,0.35);font-size:9px">Foreign-born</div>
<div style="color:${s.foreign_born_pct>60?'#f87171':s.foreign_born_pct>30?'#fbbf24':'#dce8f5'};font-weight:700">${s.foreign_born_pct}%</div>
</div>
<div style="background:rgba(0,0,0,0.04);border-radius:5px;padding:5px 7px">
<div style="background:rgba(0,0,0,0.04);border-radius: var(--radius-sm);padding:5px 7px">
<div style="color:rgba(220,232,245,0.35);font-size:9px">Students</div>
<div style="color:#dce8f5;font-weight:700">${s.students_pct}%</div>
</div>
<div style="background:rgba(0,0,0,0.04);border-radius:5px;padding:5px 7px">
<div style="background:rgba(0,0,0,0.04);border-radius: var(--radius-sm);padding:5px 7px">
<div style="color:rgba(220,232,245,0.35);font-size:9px">Self-employed</div>
<div style="color:#dce8f5;font-weight:700">${s.self_employed_pct}%</div>
</div>
+1 -1
View File
@@ -26,7 +26,7 @@
table { width: 100%; border-collapse: collapse; margin: 16px 0 24px; font-size: 13px; }
th { background: #f5f5f7; padding: 10px 12px; text-align: left; font-weight: 600; border-bottom: 2px solid #e5e5e7; }
td { padding: 9px 12px; border-bottom: 1px solid #1a1a1a; vertical-align: top; }
.highlight { background: #f5f5f7; border-radius: 10px; padding: 16px 20px; margin: 20px 0; font-size: 14px; }
.highlight { background: #f5f5f7; border-radius: var(--radius-md); padding: 16px 20px; margin: 20px 0; font-size: 14px; }
.highlight p { margin-bottom: 0; font-size: 14px; }
.pending { color: #6e6e73; font-style: italic; }
footer { border-top: 1px solid #e5e5e7; padding: 24px; text-align: center; font-size: 13px; color: #6e6e73; }
+1 -1
View File
@@ -29,7 +29,7 @@
table { width: 100%; border-collapse: collapse; margin: 16px 0 24px; font-size: 13px; }
th { background: #f5f5f7; padding: 10px 12px; text-align: left; font-weight: 600; border-bottom: 2px solid #e5e5e7; }
td { padding: 9px 12px; border-bottom: 1px solid #1a1a1a; vertical-align: top; }
.highlight { background: #f5f5f7; border-radius: 10px; padding: 16px 20px; margin: 20px 0; font-size: 14px; }
.highlight { background: #f5f5f7; border-radius: var(--radius-md); padding: 16px 20px; margin: 20px 0; font-size: 14px; }
.highlight p { margin-bottom: 0; font-size: 14px; }
footer { border-top: 1px solid #e5e5e7; padding: 24px; text-align: center; font-size: 13px; color: #6e6e73; }
footer a { color: #0066cc; text-decoration: none; }
+1 -1
View File
@@ -6,7 +6,7 @@
<title>LandveX AB - Test</title>
<style>
body { font-family: -apple-system, sans-serif; padding: 20px; background: #f2f2f7; }
.card { background: white; border-radius: 12px; padding: 16px; margin-bottom: 12px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
.card { background: white; border-radius: var(--radius-md); padding: 16px; margin-bottom: 12px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
h1 { font-size: 28px; margin-bottom: 8px; }
.entry { padding: 8px 0; border-bottom: 1px solid #eee; }
.entry:last-child { border-bottom: none; }
+2 -2
View File
@@ -61,7 +61,7 @@
li { margin-bottom: 6px; }
.row { display: flex; align-items: flex-start; gap: 12px; padding: 12px 0; border-bottom: 0.5px solid var(--separator); }
.row:last-child { border-bottom: none; }
.row-icon { width: 36px; height: 36px; border-radius: 8px; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.row-icon { width: 36px; height: 36px; border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.blue { background: rgba(0,102,255,0.12); }
.green { background: rgba(52,199,89,0.12); }
.orange { background: rgba(255,149,0,0.12); }
@@ -80,7 +80,7 @@
.stat-lbl { font-size: 12px; color: var(--label3); margin-top: 3px; }
.chain-step { display: flex; align-items: flex-start; gap: 12px; padding: 10px 0; border-bottom: 0.5px solid var(--separator); }
.chain-step:last-child { border-bottom: none; }
.chain-num { width: 28px; height: 28px; border-radius: 50%; background: rgba(0,102,255,0.12); color: var(--accent); font-size: 13px; font-weight: 700; display: flex; align-items: center; justify-content: center; flex-shrink: 0; margin-top: 2px; }
.chain-num { width: 28px; height: 28px; border-radius: var(--radius-full); background: rgba(0,102,255,0.12); color: var(--accent); font-size: 13px; font-weight: 700; display: flex; align-items: center; justify-content: center; flex-shrink: 0; margin-top: 2px; }
.chain-content { flex: 1; }
.chain-label { font-size: 14px; font-weight: 600; color: var(--label); }
.chain-sub { font-size: 13px; color: var(--label3); margin-top: 2px; line-height: 1.5; }
+34 -34
View File
@@ -32,41 +32,41 @@ html,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,'SF Pro Text'
#city-name{font-size:24px;font-weight:900;letter-spacing:-0.8px;color:var(--text);line-height:1}
#city-region{font-size:11px;color:var(--t2);margin-top:3px;font-weight:500}
#city-stats{display:grid;grid-template-columns:1fr 1fr;gap:6px;margin-top:12px}
.cstat{background:var(--s2);border:1px solid var(--border);border-radius:7px;padding:8px 10px}
.cstat{background:var(--s2);border:1px solid var(--border);border-radius: var(--radius-sm);padding:8px 10px}
.cstat-val{font-size:16px;font-weight:800;color:var(--text);letter-spacing:-0.3px}
.cstat-lbl{font-size:9px;font-weight:600;letter-spacing:0.5px;text-transform:uppercase;color:var(--t3);margin-top:1px}
#profile-block{padding:12px 16px;border-bottom:1px solid var(--border)}
#profile-tabs{display:grid;grid-template-columns:1fr 1fr 1fr;gap:3px;background:rgba(0,0,0,0.06);border-radius:10px;padding:2px}
.ptab{padding:6px 4px;border-radius:8px;border:none;background:transparent;cursor:pointer;font-size:10px;font-weight:600;color:var(--t2);transition:all 0.15s}
#profile-tabs{display:grid;grid-template-columns:1fr 1fr 1fr;gap:3px;background:rgba(0,0,0,0.06);border-radius: var(--radius-md);padding:2px}
.ptab{padding:6px 4px;border-radius: var(--radius-md);border:none;background:transparent;cursor:pointer;font-size:10px;font-weight:600;color:var(--t2);transition:all 0.15s}
.ptab:hover{background:var(--s2)}.ptab.active{background:#fff;color:var(--text);box-shadow:0 1px 4px rgba(0,0,0,0.12)}
#district-block{padding:12px 16px}
#district-lbl{font-size:9px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:var(--t3);margin-bottom:8px}
.dcard{border-radius:12px;background:var(--s2);padding:11px 13px;margin-bottom:6px;cursor:pointer;transition:all 0.12s}
.dcard{border-radius: var(--radius-md);background:var(--s2);padding:11px 13px;margin-bottom:6px;cursor:pointer;transition:all 0.12s}
.dcard:hover,.dcard.active{background:var(--s3)}
.dcard-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:3px}
.dcard-name{font-size:13px;font-weight:700;color:var(--text)}
.dcard-score{font-size:11px;font-weight:800;padding:1px 7px;border-radius:4px;font-family:var(--mono)}
.dcard-score{font-size:11px;font-weight:800;padding:1px 7px;border-radius: var(--radius-sm);font-family:var(--mono)}
.dcard-desc{font-size:12px;color:var(--t2);line-height:1.4;margin-bottom:5px}
.dcard-contra{display:inline-flex;align-items:center;gap:4px;font-size:9px;font-weight:700;border-radius:4px;padding:2px 7px}
.dcard-contra{display:inline-flex;align-items:center;gap:4px;font-size:9px;font-weight:700;border-radius: var(--radius-sm);padding:2px 7px}
.dc-low{background:rgba(22,163,74,0.12);color:#166534}.dc-med{background:rgba(217,119,6,0.12);color:#92400e}.dc-high{background:rgba(220,38,38,0.12);color:#991b1b}
#freshness-block{padding:12px 16px;border-top:1px solid var(--border);margin-top:auto}
#freshness-title{font-size:9px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:var(--t3);margin-bottom:8px}
.fresh-row{display:flex;align-items:center;justify-content:space-between;padding:5px 0;border-bottom:1px solid var(--border)}.fresh-row:last-child{border-bottom:none}
.fresh-source{font-size:10px;color:var(--t2);font-weight:500}
.fresh-val{font-size:9px;font-weight:700;font-family:var(--mono);display:flex;align-items:center;gap:4px}
.fresh-dot{width:5px;height:5px;border-radius:50%}.fresh-dot.live{background:#22c55e;animation:pulse-ring 1.5s ease-in-out infinite}.fresh-dot.recent{background:#fbbf24}
.fresh-dot{width:5px;height:5px;border-radius: var(--radius-full)}.fresh-dot.live{background:#22c55e;animation:pulse-ring 1.5s ease-in-out infinite}.fresh-dot.recent{background:#fbbf24}
#map-wrap{position:relative;overflow:hidden;background:var(--bg)}
#map{width:100%;height:100%}
#map-topbar{position:absolute;top:0;left:0;right:0;z-index:1000;height:44px;display:flex;align-items:center;gap:10px;padding:0 14px;background:rgba(0,0,0,0.92);backdrop-filter:blur(20px) saturate(180%);border-bottom:1px solid rgba(0,0,0,0.1)}
#map-logo{font-size:11px;font-weight:800;color:var(--text);text-decoration:none}#map-logo em{color:var(--accent3);font-style:normal}
.map-sep{width:1px;height:16px;background:rgba(0,0,0,0.12)}
#map-city-label{font-size:12px;font-weight:700;color:var(--text);flex:1}
#map-profile-chip{font-size:10px;font-weight:700;border-radius:4px;padding:3px 10px;border:1px solid}
#map-profile-chip{font-size:10px;font-weight:700;border-radius: var(--radius-sm);padding:3px 10px;border:1px solid}
#layer-bar{position:absolute;top:44px;left:0;right:0;z-index:999;display:flex;align-items:center;gap:4px;padding:5px 8px;background:#fff;border-bottom:1px solid rgba(0,0,0,0.1);overflow-x:auto;scrollbar-width:none}
.ltoggle{display:flex;align-items:center;gap:5px;font-size:11px;font-weight:600;color:var(--t2);background:rgba(0,0,0,0.05);border:none;border-radius:20px;padding:5px 12px;cursor:pointer;white-space:nowrap;flex-shrink:0}
.ltoggle.on{background:var(--accent);color: #1d1d1f}.ltoggle-dot{width:6px;height:6px;border-radius:50%;flex-shrink:0}
.ltoggle{display:flex;align-items:center;gap:5px;font-size:11px;font-weight:600;color:var(--t2);background:rgba(0,0,0,0.05);border:none;border-radius: var(--radius-xl);padding:5px 12px;cursor:pointer;white-space:nowrap;flex-shrink:0}
.ltoggle.on{background:var(--accent);color: #1d1d1f}.ltoggle-dot{width:6px;height:6px;border-radius: var(--radius-full);flex-shrink:0}
.leaflet-control-zoom{margin-top:96px!important;margin-left:10px!important}
.leaflet-popup-content-wrapper{background:#fff!important;border:none!important;border-radius:16px!important;box-shadow:0 8px 32px rgba(0,0,0,0.15)!important;padding:0!important}
.leaflet-popup-content-wrapper{background:#fff!important;border:none!important;border-radius: 16px!important;box-shadow:0 8px 32px rgba(0,0,0,0.15)!important;padding:0!important}
.leaflet-popup-content{margin:0!important;width:260px!important}.leaflet-popup-tip{background:#fff!important}
.mpop{padding:14px 15px}.mpop-eyebrow{font-size:9px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:var(--accent3);margin-bottom:3px}
.mpop-name{font-size:14px;font-weight:800;color:var(--text);margin-bottom:8px}
@@ -75,9 +75,9 @@ html,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,'SF Pro Text'
.mpop-bars{display:flex;flex-direction:column;gap:5px;margin-bottom:10px;padding-bottom:10px;border-bottom:1px solid var(--border)}
.mpop-bar-row{display:flex;align-items:center;gap:6px}
.mpop-bar-lbl{font-size:9px;font-weight:600;text-transform:uppercase;color:var(--t3);width:72px;flex-shrink:0}
.mpop-bar-track{flex:1;height:3px;border-radius:2px;background:rgba(0,0,0,0.06)}.mpop-bar-fill{height:100%;border-radius:2px}
.mpop-bar-track{flex:1;height:3px;border-radius: var(--radius-sm);background:rgba(0,0,0,0.06)}.mpop-bar-fill{height:100%;border-radius: var(--radius-sm)}
.mpop-bar-val{font-size:10px;font-weight:700;font-family:var(--mono);width:24px;text-align:right}
.mpop-contra{font-size:10px;border-radius:6px;padding:6px 9px;line-height:1.4;margin-bottom:8px}
.mpop-contra{font-size:10px;border-radius: var(--radius-sm);padding:6px 9px;line-height:1.4;margin-bottom:8px}
.mpop-contra.low{background:rgba(22,163,74,0.08);color:#166534;border:1px solid rgba(22,163,74,0.15)}
.mpop-contra.medium{background:rgba(217,119,6,0.08);color:#92400e;border:1px solid rgba(217,119,6,0.15)}
.mpop-contra.high{background:rgba(220,38,38,0.08);color:#991b1b;border:1px solid rgba(220,38,38,0.15)}
@@ -88,7 +88,7 @@ html,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,'SF Pro Text'
#right-profile-label{font-size:9px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:var(--t3);margin-bottom:4px}
#right-title{font-size:17px;font-weight:700;color:var(--text)}
#kpi-grid{padding:10px 16px 8px;display:grid;grid-template-columns:1fr 1fr;gap:6px;border-bottom:1px solid var(--border)}
.kpi{background:var(--s2);border-radius:12px;padding:12px 14px}.kpi-label{font-size:10px;font-weight:700;letter-spacing:0.5px;text-transform:uppercase;margin-bottom:4px}
.kpi{background:var(--s2);border-radius: var(--radius-md);padding:12px 14px}.kpi-label{font-size:10px;font-weight:700;letter-spacing:0.5px;text-transform:uppercase;margin-bottom:4px}
.kpi-value{font-size:22px;font-weight:800;font-family:var(--mono);line-height:1}.kpi-sub{font-size:11px;color:var(--t2);margin-top:3px}.kpi.span2{grid-column:span 2}
#insights-block{padding:12px 16px;border-bottom:1px solid var(--border)}
#insights-title{font-size:9px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:var(--t3);margin-bottom:10px}
@@ -98,25 +98,25 @@ html,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,'SF Pro Text'
#contra-block{padding:12px 16px;border-bottom:1px solid var(--border)}
#contra-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px}
#contra-title{font-size:9px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:var(--t3);display:flex;align-items:center;gap:5px}
.contra-live-dot{width:5px;height:5px;border-radius:50%;background:#f87171;animation:pulse-ring 1.8s ease-in-out infinite}
.contra-count{font-size:9px;font-weight:700;background:rgba(220,38,38,0.12);color:#991b1b;padding:1px 7px;border-radius:10px}
.contra-card{border-radius:7px;border:1px solid var(--border);background:var(--s2);padding:9px 11px;margin-bottom:5px}
.contra-live-dot{width:5px;height:5px;border-radius: var(--radius-full);background:#f87171;animation:pulse-ring 1.8s ease-in-out infinite}
.contra-count{font-size:9px;font-weight:700;background:rgba(220,38,38,0.12);color:#991b1b;padding:1px 7px;border-radius: var(--radius-md)}
.contra-card{border-radius: var(--radius-sm);border:1px solid var(--border);background:var(--s2);padding:9px 11px;margin-bottom:5px}
.contra-card.c-medium{border-left:3px solid rgba(217,119,6,0.5)}.contra-card.c-high{border-left:3px solid rgba(220,38,38,0.6)}
.contra-card-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:3px}
.contra-card-name{font-size:11px;font-weight:700;color:var(--text)}.contra-card-badge{font-size:8px;font-weight:700;text-transform:uppercase;padding:1px 6px;border-radius:3px}
.contra-card-name{font-size:11px;font-weight:700;color:var(--text)}.contra-card-badge{font-size:8px;font-weight:700;text-transform:uppercase;padding:1px 6px;border-radius: var(--radius-sm)}
.contra-card-badge.medium{background:rgba(217,119,6,0.12);color:#92400e}.contra-card-badge.high{background:rgba(220,38,38,0.12);color:#991b1b}
.contra-card-desc{font-size:10px;color:var(--t2);line-height:1.4}
#right-footer{padding:10px 16px;font-size:9px;color:var(--t3);line-height:1.4;border-top:1px solid var(--border)}
#bottom-sheet{display:none;position:fixed;left:0;right:0;bottom:0;z-index:9999;background:#fff;border-radius:16px 16px 0 0;box-shadow:0 -4px 24px rgba(0,0,0,0.12);max-height:72vh;overflow-y:auto;transform:translateY(100%);transition:transform 0.28s cubic-bezier(0.32,0.72,0,1)}
#bottom-sheet{display:none;position:fixed;left:0;right:0;bottom:0;z-index:9999;background:#fff;border-radius: var(--radius-lg) 16px 0 0;box-shadow:0 -4px 24px rgba(0,0,0,0.12);max-height:72vh;overflow-y:auto;transform:translateY(100%);transition:transform 0.28s cubic-bezier(0.32,0.72,0,1)}
#bottom-sheet.open{display:block;transform:translateY(0)}
#bottom-sheet-handle{width:36px;height:4px;border-radius:2px;background:rgba(0,0,0,0.12);margin:10px auto 0}
#bottom-sheet-close{position:absolute;top:12px;right:14px;background:rgba(0,0,0,0.06);border:none;color:var(--text);font-size:16px;width:28px;height:28px;border-radius:50%;cursor:pointer;display:flex;align-items:center;justify-content:center}
#bottom-sheet-handle{width:36px;height:4px;border-radius: var(--radius-sm);background:rgba(0,0,0,0.12);margin:10px auto 0}
#bottom-sheet-close{position:absolute;top:12px;right:14px;background:rgba(0,0,0,0.06);border:none;color:var(--text);font-size:16px;width:28px;height:28px;border-radius: var(--radius-full);cursor:pointer;display:flex;align-items:center;justify-content:center}
#bottom-sheet-content{padding:8px 0 env(safe-area-inset-bottom,16px)}
#bottom-sheet-backdrop{display:none;position:fixed;inset:0;z-index:9998;background:rgba(0,0,0,0.4)}#bottom-sheet-backdrop.open{display:block}
#map-legend{position:absolute;bottom:60px;right:10px;z-index:800;background:rgba(0,0,0,0.92);backdrop-filter:blur(12px);border:1px solid rgba(0,0,0,0.08);border-radius:10px;padding:10px 12px}
#map-legend{position:absolute;bottom:60px;right:10px;z-index:800;background:rgba(0,0,0,0.92);backdrop-filter:blur(12px);border:1px solid rgba(0,0,0,0.08);border-radius: var(--radius-md);padding:10px 12px}
@keyframes pulse-ring{0%{opacity:1;transform:scale(1)}50%{opacity:0.6;transform:scale(1.2)}100%{opacity:1;transform:scale(1)}}
@keyframes pulse-wave{0%{transform:scale(1);opacity:0.7}100%{transform:scale(3.2);opacity:0}}
.pulse-marker{pointer-events:none}.pulse-outer{position:absolute;border-radius:50%;animation:pulse-wave 2.2s ease-out infinite}.pulse-inner{position:absolute;border-radius:50%;transform:translate(-50%,-50%)}
.pulse-marker{pointer-events:none}.pulse-outer{position:absolute;border-radius: var(--radius-full);animation:pulse-wave 2.2s ease-out infinite}.pulse-inner{position:absolute;border-radius: var(--radius-full);transform:translate(-50%,-50%)}
@media(max-width:900px){html,body{height:auto;overflow-y:auto;overflow-x:hidden}#app{display:flex;flex-direction:column;height:auto;min-height:100dvh}#left{order:1;border-right:none}#city-stats{display:flex;gap:8px;flex-wrap:nowrap}.cstat{background:transparent;border:none;padding:0;flex-shrink:0}#district-block{display:none}#freshness-block{display:none}#map-wrap{order:2;height:60vh;min-height:340px;max-height:520px}#right{order:3;border-left:none;border-top:1px solid var(--border)}.leaflet-popup{display:none!important}}
@media(max-width:480px){#map-wrap{height:55vh;min-height:300px}}
@media(max-width:900px){.label-marker{display:none!important}}
@@ -275,9 +275,9 @@ html,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,'SF Pro Text'
<div id="map-city-label">Uppsala — <span id="map-profile-label">Business</span></div>
<div id="map-profile-chip" style="background:rgba(0,122,255,0.12);color:#0066cc;border-color:rgba(0,122,255,0.25)">Business</div>
<div style="display:flex;gap:4px;margin-left:auto">
<button onclick="map.setView([59.8586,17.6389],12)" style="background:rgba(0,0,0,0.06);border:none;border-radius:6px;padding:4px 8px;font-size:10px;font-weight:600;color:rgba(28,28,30,0.6);cursor:pointer">UPP</button>
<button onclick="map.setView([59.9,17.5],10)" style="background:rgba(0,0,0,0.06);border:none;border-radius:6px;padding:4px 8px;font-size:10px;font-weight:600;color:rgba(28,28,30,0.6);cursor:pointer">Region</button>
<button onclick="window.location='/intelligence'" style="background:rgba(0,0,0,0.06);border:none;border-radius:6px;padding:4px 8px;font-size:10px;font-weight:600;color:rgba(28,28,30,0.6);cursor:pointer">All cities</button>
<button onclick="map.setView([59.8586,17.6389],12)" style="background:rgba(0,0,0,0.06);border:none;border-radius: var(--radius-sm);padding:4px 8px;font-size:10px;font-weight:600;color:rgba(28,28,30,0.6);cursor:pointer">UPP</button>
<button onclick="map.setView([59.9,17.5],10)" style="background:rgba(0,0,0,0.06);border:none;border-radius: var(--radius-sm);padding:4px 8px;font-size:10px;font-weight:600;color:rgba(28,28,30,0.6);cursor:pointer">Region</button>
<button onclick="window.location='/intelligence'" style="background:rgba(0,0,0,0.06);border:none;border-radius: var(--radius-sm);padding:4px 8px;font-size:10px;font-weight:600;color:rgba(28,28,30,0.6);cursor:pointer">All cities</button>
</div>
</div>
<div id="layer-bar">
@@ -290,10 +290,10 @@ html,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,'SF Pro Text'
<div id="map-legend">
<div style="font-size:9px;font-weight:700;letter-spacing:0.8px;text-transform:uppercase;color:rgba(28,28,30,0.45);margin-bottom:7px">Intelligence Score</div>
<div style="display:flex;flex-direction:column;gap:5px">
<div style="display:flex;align-items:center;gap:7px;font-size:11px;color:rgba(28,28,30,0.75)"><div style="width:12px;height:12px;border-radius:50%;background:#22c55e;flex-shrink:0"></div>Strong (810)</div>
<div style="display:flex;align-items:center;gap:7px;font-size:11px;color:rgba(28,28,30,0.75)"><div style="width:12px;height:12px;border-radius:50%;background:#eab308;flex-shrink:0"></div>Moderate (68)</div>
<div style="display:flex;align-items:center;gap:7px;font-size:11px;color:rgba(28,28,30,0.75)"><div style="width:12px;height:12px;border-radius:50%;background:#f97316;flex-shrink:0"></div>Weak (46)</div>
<div style="display:flex;align-items:center;gap:7px;font-size:11px;color:rgba(28,28,30,0.75)"><div style="width:12px;height:12px;border-radius:50%;background:#ef4444;flex-shrink:0"></div>Poor (&lt;4)</div>
<div style="display:flex;align-items:center;gap:7px;font-size:11px;color:rgba(28,28,30,0.75)"><div style="width:12px;height:12px;border-radius: var(--radius-full);background:#22c55e;flex-shrink:0"></div>Strong (810)</div>
<div style="display:flex;align-items:center;gap:7px;font-size:11px;color:rgba(28,28,30,0.75)"><div style="width:12px;height:12px;border-radius: var(--radius-full);background:#eab308;flex-shrink:0"></div>Moderate (68)</div>
<div style="display:flex;align-items:center;gap:7px;font-size:11px;color:rgba(28,28,30,0.75)"><div style="width:12px;height:12px;border-radius: var(--radius-full);background:#f97316;flex-shrink:0"></div>Weak (46)</div>
<div style="display:flex;align-items:center;gap:7px;font-size:11px;color:rgba(28,28,30,0.75)"><div style="width:12px;height:12px;border-radius: var(--radius-full);background:#ef4444;flex-shrink:0"></div>Poor (&lt;4)</div>
</div>
<div style="font-size:9px;color:rgba(28,28,30,0.35);margin-top:6px">Tap any area for details</div>
</div>
@@ -326,7 +326,7 @@ html,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,'SF Pro Text'
<div id="police-block" style="padding:12px 16px;border-bottom:1px solid var(--border)">
<div style="font-size:9px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:var(--t3);margin-bottom:8px;display:flex;align-items:center;justify-content:space-between">
Police Events · Live
<span style="font-size:9px;font-weight:700;background:rgba(239,68,68,0.12);color:#991b1b;border-radius:4px;padding:1px 7px" id="police-count">Loading…</span>
<span style="font-size:9px;font-weight:700;background:rgba(239,68,68,0.12);color:#991b1b;border-radius: var(--radius-sm);padding:1px 7px" id="police-count">Loading…</span>
</div>
<div id="police-list" style="font-size:10px;color:var(--t3)">Loading…</div>
</div>
@@ -430,7 +430,7 @@ function buildPopup(d,p){
function buildList(p){
const sorted=[...D].sort((a,b)=>b.s[p]-a.s[p]);
document.getElementById('district-list').innerHTML=sorted.map(d=>{
const v=d.s[p],col=sc(v),cClass=d.c==='high'?'dc-high':d.c==='medium'?'dc-med':'dc-low',cTxt=d.c==='high'?' HIGH CONTRA':d.c==='medium'?'· MEDIUM CONTRA':' LOW CONTRA';
const v=d.s[p],col=sc(v),cClass=d.c==='high'?'dc-high':d.c==='medium'?'dc-med':'dc-low',cTxt=d.c==='high'?' HIGH CONTRA':d.c==='medium'?'· MEDIUM CONTRA':'<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> LOW CONTRA';
return `<div class="dcard" id="dc-${d.id}" onclick="focusDist('${d.id}')"><div class="dcard-head"><div class="dcard-name">${d.name}</div><div class="dcard-score" style="background:${col}18;color:${col}">${v.toFixed(1)}</div></div><div class="dcard-desc">${d.ct.length>80?d.ct.substring(0,80)+'…':d.ct}</div><div class="dcard-contra ${cClass}">${cTxt}</div></div>`;
}).join('');
}
@@ -469,7 +469,7 @@ function buildAlertMarkers(){
alertMarkers.forEach(m=>m.remove());alertMarkers=[];
D.filter(d=>d.c==='high'||d.c==='medium').forEach(d=>{
const col=d.c==='high'?'#f87171':'#fbbf24';
const icon=L.divIcon({className:'',html:`<div style="width:18px;height:18px;border-radius:3px;background:rgba(0,0,0,0.95);border:2px solid ${col};display:flex;align-items:center;justify-content:center;font-size:10px;box-shadow:0 0 10px ${col}66;cursor:pointer">${d.c==='high'?'':'!'}</div>`,iconSize:[18,18],iconAnchor:[9,9]});
const icon=L.divIcon({className:'',html:`<div style="width:18px;height:18px;border-radius: var(--radius-sm);background:rgba(0,0,0,0.95);border:2px solid ${col};display:flex;align-items:center;justify-content:center;font-size:10px;box-shadow:0 0 10px ${col}66;cursor:pointer">${d.c==='high'?'':'!'}</div>`,iconSize:[18,18],iconAnchor:[9,9]});
const ca=CONTRA_ALERTS.find(x=>x.distId===d.id);
const marker=L.marker([d.lat,d.lng],{icon,zIndexOffset:500});
if(ca)marker.bindPopup(`<div style="padding:10px 12px;min-width:200px"><div style="font-size:9px;font-weight:700;color:${col};text-transform:uppercase;margin-bottom:4px">CONTRADICTION ${d.c.toUpperCase()}</div><div style="font-size:11px;font-weight:600;margin-bottom:4px">${d.name}</div><div style="font-size:10px;color:rgba(28,28,30,0.7);line-height:1.4">${ca.desc}</div></div>`,{maxWidth:240});
@@ -529,7 +529,7 @@ window.addEventListener('DOMContentLoaded',()=>{
poly.bindPopup(buildPopup(d,'business'),{maxWidth:260,minWidth:250});
poly.on('click',()=>{if(isMobile()){setTimeout(()=>{map.closePopup();openBottomSheet(buildPopup(d,profile));},10);}});
poly.addTo(map);polys[d.id]=poly;
if(map.getZoom()>=11){L.marker([d.lat,d.lng],{icon:L.divIcon({className:'label-marker',html:`<div style="font-size:11px;font-weight:700;color:#1c1c1e;white-space:nowrap;pointer-events:none;background:rgba(0,0,0,0.75);padding:2px 6px;border-radius:4px">${d.name}</div>`,iconAnchor:[0,0]}),interactive:false}).addTo(map);}
if(map.getZoom()>=11){L.marker([d.lat,d.lng],{icon:L.divIcon({className:'label-marker',html:`<div style="font-size:11px;font-weight:700;color:#1c1c1e;white-space:nowrap;pointer-events:none;background:rgba(0,0,0,0.75);padding:2px 6px;border-radius: 4px">${d.name}</div>`,iconAnchor:[0,0]}),interactive:false}).addTo(map);}
});
document.getElementById('district-count').textContent=D.length;
buildPulseMarkers();buildAlertMarkers();buildList('business');buildRight('business');
@@ -69,16 +69,16 @@
.decisions-bg { background: var(--surface); }
.decisions-grid { display: grid; grid-template-columns: repeat(3,1fr); gap: 16px; margin-top: 40px; }
.decision-chip { background: var(--bg-dark); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 18px 22px; display: flex; align-items: center; gap: 12px; }
.decision-chip .chip-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--blue); flex-shrink: 0; }
.decision-chip .chip-dot { width: 8px; height: 8px; border-radius: var(--radius-full); background: var(--blue); flex-shrink: 0; }
.decision-chip span { font-size: 14px; font-weight: 600; color: var(--text-light); }
.contradiction-bg { background: #e8f0fe; }
.contradiction-box { background: rgba(0,0,0,.05); border: 1px solid rgba(0,0,0,.1); border-radius: 16px; padding: 36px 40px; margin-bottom: 32px; }
.contradiction-box { background: rgba(0,0,0,.05); border: 1px solid rgba(0,0,0,.1); border-radius: var(--radius-lg); padding: 36px 40px; margin-bottom: 32px; }
.conflict-header { display: flex; align-items: flex-start; gap: 16px; margin-bottom: 24px; }
.conflict-flag { font-size: 1.5rem; color: #f87171; }
.conflict-title { color: #f87171; font-weight: 800; font-size: .9375rem; margin-bottom: 4px; }
.conflict-desc { color: rgba(0,0,0,.6); font-size: .875rem; line-height: 1.6; }
.conflict-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.conflict-side { border-radius: 10px; padding: 16px; }
.conflict-side { border-radius: var(--radius-md); padding: 16px; }
.conflict-official { background: rgba(34,197,94,.1); border: 1px solid rgba(34,197,94,.2); }
.conflict-observed { background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.2); }
.conflict-side-label { font-size: .75rem; font-weight: 700; text-transform: uppercase; letter-spacing: .08em; margin-bottom: 10px; }
@@ -92,13 +92,13 @@
.breadcrumb a { color: var(--text-muted); }
.breadcrumb a:hover { color: #1d1d1f; }
.breadcrumb .sep { color: var(--border); }
.comparison-table { width: 100%; border-collapse: collapse; margin-top: 32px; border-radius: 12px; overflow: hidden; border: 1px solid rgba(0,0,0,0.08); }
.comparison-table { width: 100%; border-collapse: collapse; margin-top: 32px; border-radius: var(--radius-md); overflow: hidden; border: 1px solid rgba(0,0,0,0.08); }
.comparison-table th { padding: 14px 16px; font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .07em; text-align: left; background: rgba(0,0,0,0.04); color: var(--text-dim); }
.cta-section { background: #e8f0fe; }
.cta-box { border-radius: 16px; padding: 56px 48px; text-align: center; max-width: 680px; margin: 0 auto; }
.cta-box { border-radius: var(--radius-lg); padding: 56px 48px; text-align: center; max-width: 680px; margin: 0 auto; }
.cta-box h2 { font-size: clamp(24px,3vw,36px); font-weight: 800; color: #1d1d1f; margin-bottom: 16px; }
.cta-box p { font-size: 17px; color: rgba(0,0,0,.7); margin-bottom: 36px; line-height: 1.65; }
.lv-disclaimer { font-size:.75rem; color:rgba(0,0,0,.75); margin-top:24px; padding:12px 16px; background:rgba(0,0,0,.04); border-radius:8px; line-height:1.6; max-width:800px; }
.lv-disclaimer { font-size:.75rem; color:rgba(0,0,0,.75); margin-top:24px; padding:12px 16px; background:rgba(0,0,0,.04); border-radius: var(--radius-md); line-height:1.6; max-width:800px; }
.lv-disclaimer a { color:rgba(0,0,0,.65); text-decoration:underline; }
footer { border-top: 1px solid var(--border); padding: 40px; }
.footer-inner { max-width: 1140px; margin: 0 auto; display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 20px; }
@@ -108,7 +108,7 @@
.footer-links { display: flex; gap: 24px; align-items: center; flex-wrap: wrap; }
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social { width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px; display: flex; align-items: center; justify-content: center; transition: border-color 0.2s, background 0.2s; }
.footer-social { width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; transition: border-color 0.2s, background 0.2s; }
.footer-social:hover { border-color: rgba(0,0,0,0.25); background: rgba(0,0,0,0.04); }
@media (max-width:900px) { nav { padding:0 20px; } .use-case-grid { grid-template-columns:1fr; } .decisions-grid { grid-template-columns:1fr 1fr; } .conflict-grid { grid-template-columns:1fr; } }
@media (max-width:600px) { section { padding:72px 20px; } .decisions-grid { grid-template-columns:1fr; } nav { padding:0 16px; } .hero h1 { letter-spacing:-1px; } .contradiction-box { padding:24px 20px; } }
@@ -150,7 +150,7 @@
<p class="section-sub">Every engagement is anchored to a specific decision. Select a capability to discuss how Landvex applies it to your context.</p>
<div class="use-case-grid">
<a class="use-case-card" href="/contact/" style="display:block;text-decoration:none;">
<span class="uc-icon" style="font-size:22px;display:block;margin-bottom:16px;color:#5599ff;">&#9650;</span>
<span class="uc-icon" style="font-size:22px;display:block;margin-bottom:16px;color:#5599ff;">&#965050</span>
<h3>IQOA Condition Pre-Screening</h3>
<p>Field observations structured to support IQOA classification workflows. Identify 2E, 3 and 3U priority candidates before formal inspection deployment, enabling efficient use of CEREMA and DIR inspection resources.</p>
<div style="margin-top:14px;font-size:13px;font-weight:700;color:#0066ff;">Learn more &rarr;</div>
@@ -270,16 +270,16 @@
<div class="conflict-grid">
<div class="conflict-side conflict-official">
<div class="conflict-side-label">Official narrative</div>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; IQOA last survey: 28 months ago, classification 2</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; Maintenance programme: on schedule</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; No restriction orders active</li></ul>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 IQOA last survey: 28 months ago, classification 2</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 Maintenance programme: on schedule</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 No restriction orders active</li></ul>
</div>
<div class="conflict-side conflict-observed">
<div class="conflict-side-label">Observed reality</div>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Advanced carbonation of concrete at three pier bases</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Pavement deformation above central span consistent with deck movement</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Joint sealant failure on four of six expansion joints</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Corrosion staining on soffit indicates active reinforcement attack</li></ul>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Advanced carbonation of concrete at three pier bases</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Pavement deformation above central span consistent with deck movement</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Joint sealant failure on four of six expansion joints</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Corrosion staining on soffit indicates active reinforcement attack</li></ul>
</div>
</div>
<div class="conflict-footer">
@@ -69,16 +69,16 @@
.decisions-bg { background: var(--surface); }
.decisions-grid { display: grid; grid-template-columns: repeat(3,1fr); gap: 16px; margin-top: 40px; }
.decision-chip { background: var(--bg-dark); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 18px 22px; display: flex; align-items: center; gap: 12px; }
.decision-chip .chip-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--blue); flex-shrink: 0; }
.decision-chip .chip-dot { width: 8px; height: 8px; border-radius: var(--radius-full); background: var(--blue); flex-shrink: 0; }
.decision-chip span { font-size: 14px; font-weight: 600; color: var(--text-light); }
.contradiction-bg { background: #e8f0fe; }
.contradiction-box { background: rgba(0,0,0,.05); border: 1px solid rgba(0,0,0,.1); border-radius: 16px; padding: 36px 40px; margin-bottom: 32px; }
.contradiction-box { background: rgba(0,0,0,.05); border: 1px solid rgba(0,0,0,.1); border-radius: var(--radius-lg); padding: 36px 40px; margin-bottom: 32px; }
.conflict-header { display: flex; align-items: flex-start; gap: 16px; margin-bottom: 24px; }
.conflict-flag { font-size: 1.5rem; color: #f87171; }
.conflict-title { color: #f87171; font-weight: 800; font-size: .9375rem; margin-bottom: 4px; }
.conflict-desc { color: rgba(0,0,0,.6); font-size: .875rem; line-height: 1.6; }
.conflict-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.conflict-side { border-radius: 10px; padding: 16px; }
.conflict-side { border-radius: var(--radius-md); padding: 16px; }
.conflict-official { background: rgba(34,197,94,.1); border: 1px solid rgba(34,197,94,.2); }
.conflict-observed { background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.2); }
.conflict-side-label { font-size: .75rem; font-weight: 700; text-transform: uppercase; letter-spacing: .08em; margin-bottom: 10px; }
@@ -92,13 +92,13 @@
.breadcrumb a { color: var(--text-muted); }
.breadcrumb a:hover { color: #1d1d1f; }
.breadcrumb .sep { color: var(--border); }
.comparison-table { width: 100%; border-collapse: collapse; margin-top: 32px; border-radius: 12px; overflow: hidden; border: 1px solid rgba(0,0,0,0.08); }
.comparison-table { width: 100%; border-collapse: collapse; margin-top: 32px; border-radius: var(--radius-md); overflow: hidden; border: 1px solid rgba(0,0,0,0.08); }
.comparison-table th { padding: 14px 16px; font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .07em; text-align: left; background: rgba(0,0,0,0.04); color: var(--text-dim); }
.cta-section { background: #e8f0fe; }
.cta-box { border-radius: 16px; padding: 56px 48px; text-align: center; max-width: 680px; margin: 0 auto; }
.cta-box { border-radius: var(--radius-lg); padding: 56px 48px; text-align: center; max-width: 680px; margin: 0 auto; }
.cta-box h2 { font-size: clamp(24px,3vw,36px); font-weight: 800; color: #1d1d1f; margin-bottom: 16px; }
.cta-box p { font-size: 17px; color: rgba(0,0,0,.7); margin-bottom: 36px; line-height: 1.65; }
.lv-disclaimer { font-size:.75rem; color:rgba(0,0,0,.75); margin-top:24px; padding:12px 16px; background:rgba(0,0,0,.04); border-radius:8px; line-height:1.6; max-width:800px; }
.lv-disclaimer { font-size:.75rem; color:rgba(0,0,0,.75); margin-top:24px; padding:12px 16px; background:rgba(0,0,0,.04); border-radius: var(--radius-md); line-height:1.6; max-width:800px; }
.lv-disclaimer a { color:rgba(0,0,0,.65); text-decoration:underline; }
footer { border-top: 1px solid var(--border); padding: 40px; }
.footer-inner { max-width: 1140px; margin: 0 auto; display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 20px; }
@@ -108,7 +108,7 @@
.footer-links { display: flex; gap: 24px; align-items: center; flex-wrap: wrap; }
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social { width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px; display: flex; align-items: center; justify-content: center; transition: border-color 0.2s, background 0.2s; }
.footer-social { width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; transition: border-color 0.2s, background 0.2s; }
.footer-social:hover { border-color: rgba(0,0,0,0.25); background: rgba(0,0,0,0.04); }
@media (max-width:900px) { nav { padding:0 20px; } .use-case-grid { grid-template-columns:1fr; } .decisions-grid { grid-template-columns:1fr 1fr; } .conflict-grid { grid-template-columns:1fr; } }
@media (max-width:600px) { section { padding:72px 20px; } .decisions-grid { grid-template-columns:1fr; } nav { padding:0 16px; } .hero h1 { letter-spacing:-1px; } .contradiction-box { padding:24px 20px; } }
@@ -150,7 +150,7 @@
<p class="section-sub">Every engagement is anchored to a specific decision. Select a capability to discuss how Landvex applies it to your context.</p>
<div class="use-case-grid">
<a class="use-case-card" href="/contact/" style="display:block;text-decoration:none;">
<span class="uc-icon" style="font-size:22px;display:block;margin-bottom:16px;color:#5599ff;">&#9650;</span>
<span class="uc-icon" style="font-size:22px;display:block;margin-bottom:16px;color:#5599ff;">&#965050</span>
<h3>DIN 1076 Condition Assessment Support</h3>
<p>Field observation data structured to complement DIN 1076 inspection requirements. Identify priority assets before the next mandatory Hauptpruefung cycle without deploying physical inspection teams.</p>
<div style="margin-top:14px;font-size:13px;font-weight:700;color:#0066ff;">Learn more &rarr;</div>
@@ -270,16 +270,16 @@
<div class="conflict-grid">
<div class="conflict-side conflict-official">
<div class="conflict-side-label">Official narrative</div>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; Hauptpruefung completed 22 months ago: Zustandsnote 2.7</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; Maintenance programme: within schedule</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; No load restriction active</li></ul>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 Hauptpruefung completed 22 months ago: Zustandsnote 2.7</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 Maintenance programme: within schedule</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 No load restriction active</li></ul>
</div>
<div class="conflict-side conflict-observed">
<div class="conflict-side-label">Observed reality</div>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Chloride-induced rebar corrosion visible on east parapet</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Expansion joint displacement 18mm above threshold on two spans</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Active water seepage through deck on south abutment</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Bearing deterioration consistent with grade 3.5+ condition</li></ul>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Chloride-induced rebar corrosion visible on east parapet</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Expansion joint displacement 18mm above threshold on two spans</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Active water seepage through deck on south abutment</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Bearing deterioration consistent with grade 3.5+ condition</li></ul>
</div>
</div>
<div class="conflict-footer">
@@ -69,16 +69,16 @@
.decisions-bg { background: var(--surface); }
.decisions-grid { display: grid; grid-template-columns: repeat(3,1fr); gap: 16px; margin-top: 40px; }
.decision-chip { background: var(--bg-dark); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 18px 22px; display: flex; align-items: center; gap: 12px; }
.decision-chip .chip-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--blue); flex-shrink: 0; }
.decision-chip .chip-dot { width: 8px; height: 8px; border-radius: var(--radius-full); background: var(--blue); flex-shrink: 0; }
.decision-chip span { font-size: 14px; font-weight: 600; color: var(--text-light); }
.contradiction-bg { background: #e8f0fe; }
.contradiction-box { background: rgba(0,0,0,.05); border: 1px solid rgba(0,0,0,.1); border-radius: 16px; padding: 36px 40px; margin-bottom: 32px; }
.contradiction-box { background: rgba(0,0,0,.05); border: 1px solid rgba(0,0,0,.1); border-radius: var(--radius-lg); padding: 36px 40px; margin-bottom: 32px; }
.conflict-header { display: flex; align-items: flex-start; gap: 16px; margin-bottom: 24px; }
.conflict-flag { font-size: 1.5rem; color: #f87171; }
.conflict-title { color: #f87171; font-weight: 800; font-size: .9375rem; margin-bottom: 4px; }
.conflict-desc { color: rgba(0,0,0,.6); font-size: .875rem; line-height: 1.6; }
.conflict-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.conflict-side { border-radius: 10px; padding: 16px; }
.conflict-side { border-radius: var(--radius-md); padding: 16px; }
.conflict-official { background: rgba(34,197,94,.1); border: 1px solid rgba(34,197,94,.2); }
.conflict-observed { background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.2); }
.conflict-side-label { font-size: .75rem; font-weight: 700; text-transform: uppercase; letter-spacing: .08em; margin-bottom: 10px; }
@@ -92,13 +92,13 @@
.breadcrumb a { color: var(--text-muted); }
.breadcrumb a:hover { color: #1d1d1f; }
.breadcrumb .sep { color: var(--border); }
.comparison-table { width: 100%; border-collapse: collapse; margin-top: 32px; border-radius: 12px; overflow: hidden; border: 1px solid rgba(0,0,0,0.08); }
.comparison-table { width: 100%; border-collapse: collapse; margin-top: 32px; border-radius: var(--radius-md); overflow: hidden; border: 1px solid rgba(0,0,0,0.08); }
.comparison-table th { padding: 14px 16px; font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .07em; text-align: left; background: rgba(0,0,0,0.04); color: var(--text-dim); }
.cta-section { background: #e8f0fe; }
.cta-box { border-radius: 16px; padding: 56px 48px; text-align: center; max-width: 680px; margin: 0 auto; }
.cta-box { border-radius: var(--radius-lg); padding: 56px 48px; text-align: center; max-width: 680px; margin: 0 auto; }
.cta-box h2 { font-size: clamp(24px,3vw,36px); font-weight: 800; color: #1d1d1f; margin-bottom: 16px; }
.cta-box p { font-size: 17px; color: rgba(0,0,0,.7); margin-bottom: 36px; line-height: 1.65; }
.lv-disclaimer { font-size:.75rem; color:rgba(0,0,0,.75); margin-top:24px; padding:12px 16px; background:rgba(0,0,0,.04); border-radius:8px; line-height:1.6; max-width:800px; }
.lv-disclaimer { font-size:.75rem; color:rgba(0,0,0,.75); margin-top:24px; padding:12px 16px; background:rgba(0,0,0,.04); border-radius: var(--radius-md); line-height:1.6; max-width:800px; }
.lv-disclaimer a { color:rgba(0,0,0,.65); text-decoration:underline; }
footer { border-top: 1px solid var(--border); padding: 40px; }
.footer-inner { max-width: 1140px; margin: 0 auto; display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 20px; }
@@ -108,7 +108,7 @@
.footer-links { display: flex; gap: 24px; align-items: center; flex-wrap: wrap; }
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social { width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px; display: flex; align-items: center; justify-content: center; transition: border-color 0.2s, background 0.2s; }
.footer-social { width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; transition: border-color 0.2s, background 0.2s; }
.footer-social:hover { border-color: rgba(0,0,0,0.25); background: rgba(0,0,0,0.04); }
@media (max-width:900px) { nav { padding:0 20px; } .use-case-grid { grid-template-columns:1fr; } .decisions-grid { grid-template-columns:1fr 1fr; } .conflict-grid { grid-template-columns:1fr; } }
@media (max-width:600px) { section { padding:72px 20px; } .decisions-grid { grid-template-columns:1fr; } nav { padding:0 16px; } .hero h1 { letter-spacing:-1px; } .contradiction-box { padding:24px 20px; } }
@@ -150,7 +150,7 @@
<p class="section-sub">Every engagement is anchored to a specific decision. Select a capability to discuss how Landvex applies it to your context.</p>
<div class="use-case-grid">
<a class="use-case-card" href="/contact/" style="display:block;text-decoration:none;">
<span class="uc-icon" style="font-size:22px;display:block;margin-bottom:16px;color:#5599ff;">&#9650;</span>
<span class="uc-icon" style="font-size:22px;display:block;margin-bottom:16px;color:#5599ff;">&#965050</span>
<h3>NEN 2767 Condition Monitoring Support</h3>
<p>Field observations structured to support NEN 2767 condition assessment workflows. Identify deterioration trends and priority assets between formal inspection intervals without physical inspection deployment.</p>
<div style="margin-top:14px;font-size:13px;font-weight:700;color:#0066ff;">Learn more &rarr;</div>
@@ -270,16 +270,16 @@
<div class="conflict-grid">
<div class="conflict-side conflict-official">
<div class="conflict-side-label">Official narrative</div>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; NEN 2767 last inspection: 19 months ago, condition score 2</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; RWS maintenance programme: within cycle</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; No restrictions active</li></ul>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 NEN 2767 last inspection: 19 months ago, condition score 2</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 RWS maintenance programme: within cycle</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 No restrictions active</li></ul>
</div>
<div class="conflict-side conflict-observed">
<div class="conflict-side-label">Observed reality</div>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Visible concrete cracking on three viaduct spans above threshold</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Bridge deck waterproofing failure on two sections</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Expansion joint displacement beyond tolerance at four locations</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Sediment accumulation blocking drainage outlets at two abutments</li></ul>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Visible concrete cracking on three viaduct spans above threshold</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Bridge deck waterproofing failure on two sections</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Expansion joint displacement beyond tolerance at four locations</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Sediment accumulation blocking drainage outlets at two abutments</li></ul>
</div>
</div>
<div class="conflict-footer">
@@ -69,16 +69,16 @@
.decisions-bg { background: var(--surface); }
.decisions-grid { display: grid; grid-template-columns: repeat(3,1fr); gap: 16px; margin-top: 40px; }
.decision-chip { background: var(--bg-dark); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 18px 22px; display: flex; align-items: center; gap: 12px; }
.decision-chip .chip-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--blue); flex-shrink: 0; }
.decision-chip .chip-dot { width: 8px; height: 8px; border-radius: var(--radius-full); background: var(--blue); flex-shrink: 0; }
.decision-chip span { font-size: 14px; font-weight: 600; color: var(--text-light); }
.contradiction-bg { background: #e8f0fe; }
.contradiction-box { background: rgba(0,0,0,.05); border: 1px solid rgba(0,0,0,.1); border-radius: 16px; padding: 36px 40px; margin-bottom: 32px; }
.contradiction-box { background: rgba(0,0,0,.05); border: 1px solid rgba(0,0,0,.1); border-radius: var(--radius-lg); padding: 36px 40px; margin-bottom: 32px; }
.conflict-header { display: flex; align-items: flex-start; gap: 16px; margin-bottom: 24px; }
.conflict-flag { font-size: 1.5rem; color: #f87171; }
.conflict-title { color: #f87171; font-weight: 800; font-size: .9375rem; margin-bottom: 4px; }
.conflict-desc { color: rgba(0,0,0,.6); font-size: .875rem; line-height: 1.6; }
.conflict-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.conflict-side { border-radius: 10px; padding: 16px; }
.conflict-side { border-radius: var(--radius-md); padding: 16px; }
.conflict-official { background: rgba(34,197,94,.1); border: 1px solid rgba(34,197,94,.2); }
.conflict-observed { background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.2); }
.conflict-side-label { font-size: .75rem; font-weight: 700; text-transform: uppercase; letter-spacing: .08em; margin-bottom: 10px; }
@@ -92,13 +92,13 @@
.breadcrumb a { color: var(--text-muted); }
.breadcrumb a:hover { color: #1d1d1f; }
.breadcrumb .sep { color: var(--border); }
.comparison-table { width: 100%; border-collapse: collapse; margin-top: 32px; border-radius: 12px; overflow: hidden; border: 1px solid rgba(0,0,0,0.08); }
.comparison-table { width: 100%; border-collapse: collapse; margin-top: 32px; border-radius: var(--radius-md); overflow: hidden; border: 1px solid rgba(0,0,0,0.08); }
.comparison-table th { padding: 14px 16px; font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .07em; text-align: left; background: rgba(0,0,0,0.04); color: var(--text-dim); }
.cta-section { background: #e8f0fe; }
.cta-box { border-radius: 16px; padding: 56px 48px; text-align: center; max-width: 680px; margin: 0 auto; }
.cta-box { border-radius: var(--radius-lg); padding: 56px 48px; text-align: center; max-width: 680px; margin: 0 auto; }
.cta-box h2 { font-size: clamp(24px,3vw,36px); font-weight: 800; color: #1d1d1f; margin-bottom: 16px; }
.cta-box p { font-size: 17px; color: rgba(0,0,0,.7); margin-bottom: 36px; line-height: 1.65; }
.lv-disclaimer { font-size:.75rem; color:rgba(0,0,0,.75); margin-top:24px; padding:12px 16px; background:rgba(0,0,0,.04); border-radius:8px; line-height:1.6; max-width:800px; }
.lv-disclaimer { font-size:.75rem; color:rgba(0,0,0,.75); margin-top:24px; padding:12px 16px; background:rgba(0,0,0,.04); border-radius: var(--radius-md); line-height:1.6; max-width:800px; }
.lv-disclaimer a { color:rgba(0,0,0,.65); text-decoration:underline; }
footer { border-top: 1px solid var(--border); padding: 40px; }
.footer-inner { max-width: 1140px; margin: 0 auto; display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 20px; }
@@ -108,7 +108,7 @@
.footer-links { display: flex; gap: 24px; align-items: center; flex-wrap: wrap; }
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social { width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px; display: flex; align-items: center; justify-content: center; transition: border-color 0.2s, background 0.2s; }
.footer-social { width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; transition: border-color 0.2s, background 0.2s; }
.footer-social:hover { border-color: rgba(0,0,0,0.25); background: rgba(0,0,0,0.04); }
@media (max-width:900px) { nav { padding:0 20px; } .use-case-grid { grid-template-columns:1fr; } .decisions-grid { grid-template-columns:1fr 1fr; } .conflict-grid { grid-template-columns:1fr; } }
@media (max-width:600px) { section { padding:72px 20px; } .decisions-grid { grid-template-columns:1fr; } nav { padding:0 16px; } .hero h1 { letter-spacing:-1px; } .contradiction-box { padding:24px 20px; } }
@@ -150,7 +150,7 @@
<p class="section-sub">Every engagement is anchored to a specific decision. Select a capability to discuss how Landvex applies it to your context.</p>
<div class="use-case-grid">
<a class="use-case-card" href="/contact/" style="display:block;text-decoration:none;">
<span class="uc-icon" style="font-size:22px;display:block;margin-bottom:16px;color:#5599ff;">&#9650;</span>
<span class="uc-icon" style="font-size:22px;display:block;margin-bottom:16px;color:#5599ff;">&#965050</span>
<h3>Structural Condition Assessment</h3>
<p>Systematic visual surveys of bridge decks, bearings, expansion joints, parapets, and approach slabs across the Swedish national and municipal bridge inventory. AI-reviewed, geo-tagged, and delivered as structured data.</p>
<div style="margin-top:14px;font-size:13px;font-weight:700;color:#0066ff;">Learn more &rarr;</div>
@@ -270,16 +270,16 @@
<div class="conflict-grid">
<div class="conflict-side conflict-official">
<div class="conflict-side-label">Official narrative</div>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; Last inspection: 14 months ago, passed</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; Maintenance schedule: on track</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; No load restrictions active</li></ul>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 Last inspection: 14 months ago, passed</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 Maintenance schedule: on track</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 No load restrictions active</li></ul>
</div>
<div class="conflict-side conflict-observed">
<div class="conflict-side-label">Observed reality</div>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Visible spalling on three pier caps</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Expansion joint separation exceeds specification on two spans</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Approach slab subsidence visible on northbound carriageway</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Drainage outfall blockage confirmed at abutment E4-107</li></ul>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Visible spalling on three pier caps</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Expansion joint separation exceeds specification on two spans</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Approach slab subsidence visible on northbound carriageway</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Drainage outfall blockage confirmed at abutment E4-107</li></ul>
</div>
</div>
<div class="conflict-footer">
@@ -69,16 +69,16 @@
.decisions-bg { background: var(--surface); }
.decisions-grid { display: grid; grid-template-columns: repeat(3,1fr); gap: 16px; margin-top: 40px; }
.decision-chip { background: var(--bg-dark); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 18px 22px; display: flex; align-items: center; gap: 12px; }
.decision-chip .chip-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--blue); flex-shrink: 0; }
.decision-chip .chip-dot { width: 8px; height: 8px; border-radius: var(--radius-full); background: var(--blue); flex-shrink: 0; }
.decision-chip span { font-size: 14px; font-weight: 600; color: var(--text-light); }
.contradiction-bg { background: #e8f0fe; }
.contradiction-box { background: rgba(0,0,0,.05); border: 1px solid rgba(0,0,0,.1); border-radius: 16px; padding: 36px 40px; margin-bottom: 32px; }
.contradiction-box { background: rgba(0,0,0,.05); border: 1px solid rgba(0,0,0,.1); border-radius: var(--radius-lg); padding: 36px 40px; margin-bottom: 32px; }
.conflict-header { display: flex; align-items: flex-start; gap: 16px; margin-bottom: 24px; }
.conflict-flag { font-size: 1.5rem; color: #f87171; }
.conflict-title { color: #f87171; font-weight: 800; font-size: .9375rem; margin-bottom: 4px; }
.conflict-desc { color: rgba(0,0,0,.6); font-size: .875rem; line-height: 1.6; }
.conflict-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.conflict-side { border-radius: 10px; padding: 16px; }
.conflict-side { border-radius: var(--radius-md); padding: 16px; }
.conflict-official { background: rgba(34,197,94,.1); border: 1px solid rgba(34,197,94,.2); }
.conflict-observed { background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.2); }
.conflict-side-label { font-size: .75rem; font-weight: 700; text-transform: uppercase; letter-spacing: .08em; margin-bottom: 10px; }
@@ -92,13 +92,13 @@
.breadcrumb a { color: var(--text-muted); }
.breadcrumb a:hover { color: #1d1d1f; }
.breadcrumb .sep { color: var(--border); }
.comparison-table { width: 100%; border-collapse: collapse; margin-top: 32px; border-radius: 12px; overflow: hidden; border: 1px solid rgba(0,0,0,0.08); }
.comparison-table { width: 100%; border-collapse: collapse; margin-top: 32px; border-radius: var(--radius-md); overflow: hidden; border: 1px solid rgba(0,0,0,0.08); }
.comparison-table th { padding: 14px 16px; font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .07em; text-align: left; background: rgba(0,0,0,0.04); color: var(--text-dim); }
.cta-section { background: #e8f0fe; }
.cta-box { border-radius: 16px; padding: 56px 48px; text-align: center; max-width: 680px; margin: 0 auto; }
.cta-box { border-radius: var(--radius-lg); padding: 56px 48px; text-align: center; max-width: 680px; margin: 0 auto; }
.cta-box h2 { font-size: clamp(24px,3vw,36px); font-weight: 800; color: #1d1d1f; margin-bottom: 16px; }
.cta-box p { font-size: 17px; color: rgba(0,0,0,.7); margin-bottom: 36px; line-height: 1.65; }
.lv-disclaimer { font-size:.75rem; color:rgba(0,0,0,.75); margin-top:24px; padding:12px 16px; background:rgba(0,0,0,.04); border-radius:8px; line-height:1.6; max-width:800px; }
.lv-disclaimer { font-size:.75rem; color:rgba(0,0,0,.75); margin-top:24px; padding:12px 16px; background:rgba(0,0,0,.04); border-radius: var(--radius-md); line-height:1.6; max-width:800px; }
.lv-disclaimer a { color:rgba(0,0,0,.65); text-decoration:underline; }
footer { border-top: 1px solid var(--border); padding: 40px; }
.footer-inner { max-width: 1140px; margin: 0 auto; display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 20px; }
@@ -108,7 +108,7 @@
.footer-links { display: flex; gap: 24px; align-items: center; flex-wrap: wrap; }
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social { width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px; display: flex; align-items: center; justify-content: center; transition: border-color 0.2s, background 0.2s; }
.footer-social { width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; transition: border-color 0.2s, background 0.2s; }
.footer-social:hover { border-color: rgba(0,0,0,0.25); background: rgba(0,0,0,0.04); }
@media (max-width:900px) { nav { padding:0 20px; } .use-case-grid { grid-template-columns:1fr; } .decisions-grid { grid-template-columns:1fr 1fr; } .conflict-grid { grid-template-columns:1fr; } }
@media (max-width:600px) { section { padding:72px 20px; } .decisions-grid { grid-template-columns:1fr; } nav { padding:0 16px; } .hero h1 { letter-spacing:-1px; } .contradiction-box { padding:24px 20px; } }
@@ -150,7 +150,7 @@
<p class="section-sub">Every engagement is anchored to a specific decision. Select a capability to discuss how Landvex applies it to your context.</p>
<div class="use-case-grid">
<a class="use-case-card" href="/contact/" style="display:block;text-decoration:none;">
<span class="uc-icon" style="font-size:22px;display:block;margin-bottom:16px;color:#5599ff;">&#9650;</span>
<span class="uc-icon" style="font-size:22px;display:block;margin-bottom:16px;color:#5599ff;">&#965050</span>
<h3>DMRB-Aligned Condition Pre-Screening</h3>
<p>Field observations structured to support BD 63 and CS 450 inspection workflows. Identify priority assets before formal Principal or General Inspections without deploying physical teams to every structure.</p>
<div style="margin-top:14px;font-size:13px;font-weight:700;color:#0066ff;">Learn more &rarr;</div>
@@ -270,16 +270,16 @@
<div class="conflict-grid">
<div class="conflict-side conflict-official">
<div class="conflict-side-label">Official narrative</div>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; Last Principal Inspection: 38 months ago, condition grade B</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; Maintenance programme: within programme</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; No interim restrictions active</li></ul>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 Last Principal Inspection: 38 months ago, condition grade B</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 Maintenance programme: within programme</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 No interim restrictions active</li></ul>
</div>
<div class="conflict-side conflict-observed">
<div class="conflict-side-label">Observed reality</div>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Visible concrete delamination on three spans approaching carriageway edge</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Bearing shelf corrosion consistent with grade C/D condition</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Half-joint cracking at two locations exceeds intervention threshold</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Retaining wall settlement visible at southbound approach</li></ul>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Visible concrete delamination on three spans approaching carriageway edge</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Bearing shelf corrosion consistent with grade C/D condition</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Half-joint cracking at two locations exceeds intervention threshold</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Retaining wall settlement visible at southbound approach</li></ul>
</div>
</div>
<div class="conflict-footer">
@@ -69,16 +69,16 @@
.decisions-bg { background: var(--surface); }
.decisions-grid { display: grid; grid-template-columns: repeat(3,1fr); gap: 16px; margin-top: 40px; }
.decision-chip { background: var(--bg-dark); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 18px 22px; display: flex; align-items: center; gap: 12px; }
.decision-chip .chip-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--blue); flex-shrink: 0; }
.decision-chip .chip-dot { width: 8px; height: 8px; border-radius: var(--radius-full); background: var(--blue); flex-shrink: 0; }
.decision-chip span { font-size: 14px; font-weight: 600; color: var(--text-light); }
.contradiction-bg { background: #e8f0fe; }
.contradiction-box { background: rgba(0,0,0,.05); border: 1px solid rgba(0,0,0,.1); border-radius: 16px; padding: 36px 40px; margin-bottom: 32px; }
.contradiction-box { background: rgba(0,0,0,.05); border: 1px solid rgba(0,0,0,.1); border-radius: var(--radius-lg); padding: 36px 40px; margin-bottom: 32px; }
.conflict-header { display: flex; align-items: flex-start; gap: 16px; margin-bottom: 24px; }
.conflict-flag { font-size: 1.5rem; color: #f87171; }
.conflict-title { color: #f87171; font-weight: 800; font-size: .9375rem; margin-bottom: 4px; }
.conflict-desc { color: rgba(0,0,0,.6); font-size: .875rem; line-height: 1.6; }
.conflict-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.conflict-side { border-radius: 10px; padding: 16px; }
.conflict-side { border-radius: var(--radius-md); padding: 16px; }
.conflict-official { background: rgba(34,197,94,.1); border: 1px solid rgba(34,197,94,.2); }
.conflict-observed { background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.2); }
.conflict-side-label { font-size: .75rem; font-weight: 700; text-transform: uppercase; letter-spacing: .08em; margin-bottom: 10px; }
@@ -92,13 +92,13 @@
.breadcrumb a { color: var(--text-muted); }
.breadcrumb a:hover { color: #1d1d1f; }
.breadcrumb .sep { color: var(--border); }
.comparison-table { width: 100%; border-collapse: collapse; margin-top: 32px; border-radius: 12px; overflow: hidden; border: 1px solid rgba(0,0,0,0.08); }
.comparison-table { width: 100%; border-collapse: collapse; margin-top: 32px; border-radius: var(--radius-md); overflow: hidden; border: 1px solid rgba(0,0,0,0.08); }
.comparison-table th { padding: 14px 16px; font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .07em; text-align: left; background: rgba(0,0,0,0.04); color: var(--text-dim); }
.cta-section { background: #e8f0fe; }
.cta-box { border-radius: 16px; padding: 56px 48px; text-align: center; max-width: 680px; margin: 0 auto; }
.cta-box { border-radius: var(--radius-lg); padding: 56px 48px; text-align: center; max-width: 680px; margin: 0 auto; }
.cta-box h2 { font-size: clamp(24px,3vw,36px); font-weight: 800; color: #1d1d1f; margin-bottom: 16px; }
.cta-box p { font-size: 17px; color: rgba(0,0,0,.7); margin-bottom: 36px; line-height: 1.65; }
.lv-disclaimer { font-size:.75rem; color:rgba(0,0,0,.75); margin-top:24px; padding:12px 16px; background:rgba(0,0,0,.04); border-radius:8px; line-height:1.6; max-width:800px; }
.lv-disclaimer { font-size:.75rem; color:rgba(0,0,0,.75); margin-top:24px; padding:12px 16px; background:rgba(0,0,0,.04); border-radius: var(--radius-md); line-height:1.6; max-width:800px; }
.lv-disclaimer a { color:rgba(0,0,0,.65); text-decoration:underline; }
footer { border-top: 1px solid var(--border); padding: 40px; }
.footer-inner { max-width: 1140px; margin: 0 auto; display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 20px; }
@@ -108,7 +108,7 @@
.footer-links { display: flex; gap: 24px; align-items: center; flex-wrap: wrap; }
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social { width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px; display: flex; align-items: center; justify-content: center; transition: border-color 0.2s, background 0.2s; }
.footer-social { width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; transition: border-color 0.2s, background 0.2s; }
.footer-social:hover { border-color: rgba(0,0,0,0.25); background: rgba(0,0,0,0.04); }
@media (max-width:900px) { nav { padding:0 20px; } .use-case-grid { grid-template-columns:1fr; } .decisions-grid { grid-template-columns:1fr 1fr; } .conflict-grid { grid-template-columns:1fr; } }
@media (max-width:600px) { section { padding:72px 20px; } .decisions-grid { grid-template-columns:1fr; } nav { padding:0 16px; } .hero h1 { letter-spacing:-1px; } .contradiction-box { padding:24px 20px; } }
@@ -150,7 +150,7 @@
<p class="section-sub">Every engagement is anchored to a specific decision. Select a capability to discuss how Landvex applies it to your context.</p>
<div class="use-case-grid">
<a class="use-case-card" href="/contact/" style="display:block;text-decoration:none;">
<span class="uc-icon" style="font-size:22px;display:block;margin-bottom:16px;color:#5599ff;">&#9650;</span>
<span class="uc-icon" style="font-size:22px;display:block;margin-bottom:16px;color:#5599ff;">&#965050</span>
<h3>Route Nationale Condition Intelligence</h3>
<p>Continuous monitoring of RN pavement condition, markings, drainage, and barrier integrity between IQRN and IQROA survey cycles. Detect emerging deterioration before it triggers reactive emergency maintenance.</p>
<div style="margin-top:14px;font-size:13px;font-weight:700;color:#0066ff;">Learn more &rarr;</div>
@@ -270,16 +270,16 @@
<div class="conflict-grid">
<div class="conflict-side conflict-official">
<div class="conflict-side-label">Official narrative</div>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; IQRN survey: 17 months ago, score B</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; Maintenance plan: within schedule</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; No emergency intervention orders</li></ul>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 IQRN survey: 17 months ago, score B</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 Maintenance plan: within schedule</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 No emergency intervention orders</li></ul>
</div>
<div class="conflict-side conflict-observed">
<div class="conflict-side-label">Observed reality</div>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Longitudinal cracking extending 600m beyond surveyed section</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Edge deterioration at 8 locations unreported</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Drainage failures at 5 culverts not on maintenance programme</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Road marking fading below IQROA threshold on 22km</li></ul>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Longitudinal cracking extending 600m beyond surveyed section</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Edge deterioration at 8 locations unreported</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Drainage failures at 5 culverts not on maintenance programme</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Road marking fading below IQROA threshold on 22km</li></ul>
</div>
</div>
<div class="conflict-footer">
@@ -69,16 +69,16 @@
.decisions-bg { background: var(--surface); }
.decisions-grid { display: grid; grid-template-columns: repeat(3,1fr); gap: 16px; margin-top: 40px; }
.decision-chip { background: var(--bg-dark); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 18px 22px; display: flex; align-items: center; gap: 12px; }
.decision-chip .chip-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--blue); flex-shrink: 0; }
.decision-chip .chip-dot { width: 8px; height: 8px; border-radius: var(--radius-full); background: var(--blue); flex-shrink: 0; }
.decision-chip span { font-size: 14px; font-weight: 600; color: var(--text-light); }
.contradiction-bg { background: #e8f0fe; }
.contradiction-box { background: rgba(0,0,0,.05); border: 1px solid rgba(0,0,0,.1); border-radius: 16px; padding: 36px 40px; margin-bottom: 32px; }
.contradiction-box { background: rgba(0,0,0,.05); border: 1px solid rgba(0,0,0,.1); border-radius: var(--radius-lg); padding: 36px 40px; margin-bottom: 32px; }
.conflict-header { display: flex; align-items: flex-start; gap: 16px; margin-bottom: 24px; }
.conflict-flag { font-size: 1.5rem; color: #f87171; }
.conflict-title { color: #f87171; font-weight: 800; font-size: .9375rem; margin-bottom: 4px; }
.conflict-desc { color: rgba(0,0,0,.6); font-size: .875rem; line-height: 1.6; }
.conflict-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.conflict-side { border-radius: 10px; padding: 16px; }
.conflict-side { border-radius: var(--radius-md); padding: 16px; }
.conflict-official { background: rgba(34,197,94,.1); border: 1px solid rgba(34,197,94,.2); }
.conflict-observed { background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.2); }
.conflict-side-label { font-size: .75rem; font-weight: 700; text-transform: uppercase; letter-spacing: .08em; margin-bottom: 10px; }
@@ -92,13 +92,13 @@
.breadcrumb a { color: var(--text-muted); }
.breadcrumb a:hover { color: #1d1d1f; }
.breadcrumb .sep { color: var(--border); }
.comparison-table { width: 100%; border-collapse: collapse; margin-top: 32px; border-radius: 12px; overflow: hidden; border: 1px solid rgba(0,0,0,0.08); }
.comparison-table { width: 100%; border-collapse: collapse; margin-top: 32px; border-radius: var(--radius-md); overflow: hidden; border: 1px solid rgba(0,0,0,0.08); }
.comparison-table th { padding: 14px 16px; font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .07em; text-align: left; background: rgba(0,0,0,0.04); color: var(--text-dim); }
.cta-section { background: #e8f0fe; }
.cta-box { border-radius: 16px; padding: 56px 48px; text-align: center; max-width: 680px; margin: 0 auto; }
.cta-box { border-radius: var(--radius-lg); padding: 56px 48px; text-align: center; max-width: 680px; margin: 0 auto; }
.cta-box h2 { font-size: clamp(24px,3vw,36px); font-weight: 800; color: #1d1d1f; margin-bottom: 16px; }
.cta-box p { font-size: 17px; color: rgba(0,0,0,.7); margin-bottom: 36px; line-height: 1.65; }
.lv-disclaimer { font-size:.75rem; color:rgba(0,0,0,.75); margin-top:24px; padding:12px 16px; background:rgba(0,0,0,.04); border-radius:8px; line-height:1.6; max-width:800px; }
.lv-disclaimer { font-size:.75rem; color:rgba(0,0,0,.75); margin-top:24px; padding:12px 16px; background:rgba(0,0,0,.04); border-radius: var(--radius-md); line-height:1.6; max-width:800px; }
.lv-disclaimer a { color:rgba(0,0,0,.65); text-decoration:underline; }
footer { border-top: 1px solid var(--border); padding: 40px; }
.footer-inner { max-width: 1140px; margin: 0 auto; display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 20px; }
@@ -108,7 +108,7 @@
.footer-links { display: flex; gap: 24px; align-items: center; flex-wrap: wrap; }
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social { width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px; display: flex; align-items: center; justify-content: center; transition: border-color 0.2s, background 0.2s; }
.footer-social { width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; transition: border-color 0.2s, background 0.2s; }
.footer-social:hover { border-color: rgba(0,0,0,0.25); background: rgba(0,0,0,0.04); }
@media (max-width:900px) { nav { padding:0 20px; } .use-case-grid { grid-template-columns:1fr; } .decisions-grid { grid-template-columns:1fr 1fr; } .conflict-grid { grid-template-columns:1fr; } }
@media (max-width:600px) { section { padding:72px 20px; } .decisions-grid { grid-template-columns:1fr; } nav { padding:0 16px; } .hero h1 { letter-spacing:-1px; } .contradiction-box { padding:24px 20px; } }
@@ -150,7 +150,7 @@
<p class="section-sub">Every engagement is anchored to a specific decision. Select a capability to discuss how Landvex applies it to your context.</p>
<div class="use-case-grid">
<a class="use-case-card" href="/contact/" style="display:block;text-decoration:none;">
<span class="uc-icon" style="font-size:22px;display:block;margin-bottom:16px;color:#5599ff;">&#9650;</span>
<span class="uc-icon" style="font-size:22px;display:block;margin-bottom:16px;color:#5599ff;">&#965050</span>
<h3>Bundesautobahn Network Monitoring</h3>
<p>Continuous observation of pavement condition, drainage, signage, and barrier integrity across the Autobahn network. Detect deterioration between ZEB survey cycles to support interim maintenance decisions.</p>
<div style="margin-top:14px;font-size:13px;font-weight:700;color:#0066ff;">Learn more &rarr;</div>
@@ -270,16 +270,16 @@
<div class="conflict-grid">
<div class="conflict-side conflict-official">
<div class="conflict-side-label">Official narrative</div>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; ZEB last survey: 26 months ago, PCR value 1.4</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; Maintenance programme: within cycle</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; No service restrictions</li></ul>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 ZEB last survey: 26 months ago, PCR value 1.4</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 Maintenance programme: within cycle</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 No service restrictions</li></ul>
</div>
<div class="conflict-side conflict-observed">
<div class="conflict-side-label">Observed reality</div>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Rut depth exceeding ZEB trigger threshold on 4.2km</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Drainage failure at seven locations not captured in last survey</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Crash barrier corrosion failure at two segments</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Retroreflective marking degradation across 18km of carriageway</li></ul>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Rut depth exceeding ZEB trigger threshold on 4.2km</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Drainage failure at seven locations not captured in last survey</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Crash barrier corrosion failure at two segments</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Retroreflective marking degradation across 18km of carriageway</li></ul>
</div>
</div>
<div class="conflict-footer">
@@ -69,16 +69,16 @@
.decisions-bg { background: var(--surface); }
.decisions-grid { display: grid; grid-template-columns: repeat(3,1fr); gap: 16px; margin-top: 40px; }
.decision-chip { background: var(--bg-dark); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 18px 22px; display: flex; align-items: center; gap: 12px; }
.decision-chip .chip-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--blue); flex-shrink: 0; }
.decision-chip .chip-dot { width: 8px; height: 8px; border-radius: var(--radius-full); background: var(--blue); flex-shrink: 0; }
.decision-chip span { font-size: 14px; font-weight: 600; color: var(--text-light); }
.contradiction-bg { background: #e8f0fe; }
.contradiction-box { background: rgba(0,0,0,.05); border: 1px solid rgba(0,0,0,.1); border-radius: 16px; padding: 36px 40px; margin-bottom: 32px; }
.contradiction-box { background: rgba(0,0,0,.05); border: 1px solid rgba(0,0,0,.1); border-radius: var(--radius-lg); padding: 36px 40px; margin-bottom: 32px; }
.conflict-header { display: flex; align-items: flex-start; gap: 16px; margin-bottom: 24px; }
.conflict-flag { font-size: 1.5rem; color: #f87171; }
.conflict-title { color: #f87171; font-weight: 800; font-size: .9375rem; margin-bottom: 4px; }
.conflict-desc { color: rgba(0,0,0,.6); font-size: .875rem; line-height: 1.6; }
.conflict-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.conflict-side { border-radius: 10px; padding: 16px; }
.conflict-side { border-radius: var(--radius-md); padding: 16px; }
.conflict-official { background: rgba(34,197,94,.1); border: 1px solid rgba(34,197,94,.2); }
.conflict-observed { background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.2); }
.conflict-side-label { font-size: .75rem; font-weight: 700; text-transform: uppercase; letter-spacing: .08em; margin-bottom: 10px; }
@@ -92,13 +92,13 @@
.breadcrumb a { color: var(--text-muted); }
.breadcrumb a:hover { color: #1d1d1f; }
.breadcrumb .sep { color: var(--border); }
.comparison-table { width: 100%; border-collapse: collapse; margin-top: 32px; border-radius: 12px; overflow: hidden; border: 1px solid rgba(0,0,0,0.08); }
.comparison-table { width: 100%; border-collapse: collapse; margin-top: 32px; border-radius: var(--radius-md); overflow: hidden; border: 1px solid rgba(0,0,0,0.08); }
.comparison-table th { padding: 14px 16px; font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .07em; text-align: left; background: rgba(0,0,0,0.04); color: var(--text-dim); }
.cta-section { background: #e8f0fe; }
.cta-box { border-radius: 16px; padding: 56px 48px; text-align: center; max-width: 680px; margin: 0 auto; }
.cta-box { border-radius: var(--radius-lg); padding: 56px 48px; text-align: center; max-width: 680px; margin: 0 auto; }
.cta-box h2 { font-size: clamp(24px,3vw,36px); font-weight: 800; color: #1d1d1f; margin-bottom: 16px; }
.cta-box p { font-size: 17px; color: rgba(0,0,0,.7); margin-bottom: 36px; line-height: 1.65; }
.lv-disclaimer { font-size:.75rem; color:rgba(0,0,0,.75); margin-top:24px; padding:12px 16px; background:rgba(0,0,0,.04); border-radius:8px; line-height:1.6; max-width:800px; }
.lv-disclaimer { font-size:.75rem; color:rgba(0,0,0,.75); margin-top:24px; padding:12px 16px; background:rgba(0,0,0,.04); border-radius: var(--radius-md); line-height:1.6; max-width:800px; }
.lv-disclaimer a { color:rgba(0,0,0,.65); text-decoration:underline; }
footer { border-top: 1px solid var(--border); padding: 40px; }
.footer-inner { max-width: 1140px; margin: 0 auto; display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 20px; }
@@ -108,7 +108,7 @@
.footer-links { display: flex; gap: 24px; align-items: center; flex-wrap: wrap; }
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social { width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px; display: flex; align-items: center; justify-content: center; transition: border-color 0.2s, background 0.2s; }
.footer-social { width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; transition: border-color 0.2s, background 0.2s; }
.footer-social:hover { border-color: rgba(0,0,0,0.25); background: rgba(0,0,0,0.04); }
@media (max-width:900px) { nav { padding:0 20px; } .use-case-grid { grid-template-columns:1fr; } .decisions-grid { grid-template-columns:1fr 1fr; } .conflict-grid { grid-template-columns:1fr; } }
@media (max-width:600px) { section { padding:72px 20px; } .decisions-grid { grid-template-columns:1fr; } nav { padding:0 16px; } .hero h1 { letter-spacing:-1px; } .contradiction-box { padding:24px 20px; } }
@@ -150,7 +150,7 @@
<p class="section-sub">Every engagement is anchored to a specific decision. Select a capability to discuss how Landvex applies it to your context.</p>
<div class="use-case-grid">
<a class="use-case-card" href="/contact/" style="display:block;text-decoration:none;">
<span class="uc-icon" style="font-size:22px;display:block;margin-bottom:16px;color:#5599ff;">&#9650;</span>
<span class="uc-icon" style="font-size:22px;display:block;margin-bottom:16px;color:#5599ff;">&#965050</span>
<h3>Rijkswaterstaat Network Monitoring</h3>
<p>Continuous observation of RWS motorway and waterway infrastructure. Monitor pavement condition, hydraulic structures, barriers, and drainage between formal inspection intervals.</p>
<div style="margin-top:14px;font-size:13px;font-weight:700;color:#0066ff;">Learn more &rarr;</div>
@@ -270,16 +270,16 @@
<div class="conflict-grid">
<div class="conflict-side conflict-official">
<div class="conflict-side-label">Official narrative</div>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; NEN 2767 inspection: 21 months ago, condition score 2</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; Maintenance plan: within programme</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; No service disruption logged</li></ul>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 NEN 2767 inspection: 21 months ago, condition score 2</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 Maintenance plan: within programme</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 No service disruption logged</li></ul>
</div>
<div class="conflict-side conflict-observed">
<div class="conflict-side-label">Observed reality</div>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Pavement cracking above NEN 2767 score 3 threshold on 3.1km</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Drainage blockage at six culverts outside maintenance schedule</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Crash barrier corrosion failure on 4 segments not on programme</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Cycle path surface deterioration exceeding 2 score on 8km</li></ul>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Pavement cracking above NEN 2767 score 3 threshold on 3.1km</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Drainage blockage at six culverts outside maintenance schedule</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Crash barrier corrosion failure on 4 segments not on programme</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Cycle path surface deterioration exceeding 2 score on 8km</li></ul>
</div>
</div>
<div class="conflict-footer">
@@ -69,16 +69,16 @@
.decisions-bg { background: var(--surface); }
.decisions-grid { display: grid; grid-template-columns: repeat(3,1fr); gap: 16px; margin-top: 40px; }
.decision-chip { background: var(--bg-dark); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 18px 22px; display: flex; align-items: center; gap: 12px; }
.decision-chip .chip-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--blue); flex-shrink: 0; }
.decision-chip .chip-dot { width: 8px; height: 8px; border-radius: var(--radius-full); background: var(--blue); flex-shrink: 0; }
.decision-chip span { font-size: 14px; font-weight: 600; color: var(--text-light); }
.contradiction-bg { background: #e8f0fe; }
.contradiction-box { background: rgba(0,0,0,.05); border: 1px solid rgba(0,0,0,.1); border-radius: 16px; padding: 36px 40px; margin-bottom: 32px; }
.contradiction-box { background: rgba(0,0,0,.05); border: 1px solid rgba(0,0,0,.1); border-radius: var(--radius-lg); padding: 36px 40px; margin-bottom: 32px; }
.conflict-header { display: flex; align-items: flex-start; gap: 16px; margin-bottom: 24px; }
.conflict-flag { font-size: 1.5rem; color: #f87171; }
.conflict-title { color: #f87171; font-weight: 800; font-size: .9375rem; margin-bottom: 4px; }
.conflict-desc { color: rgba(0,0,0,.6); font-size: .875rem; line-height: 1.6; }
.conflict-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.conflict-side { border-radius: 10px; padding: 16px; }
.conflict-side { border-radius: var(--radius-md); padding: 16px; }
.conflict-official { background: rgba(34,197,94,.1); border: 1px solid rgba(34,197,94,.2); }
.conflict-observed { background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.2); }
.conflict-side-label { font-size: .75rem; font-weight: 700; text-transform: uppercase; letter-spacing: .08em; margin-bottom: 10px; }
@@ -92,13 +92,13 @@
.breadcrumb a { color: var(--text-muted); }
.breadcrumb a:hover { color: #1d1d1f; }
.breadcrumb .sep { color: var(--border); }
.comparison-table { width: 100%; border-collapse: collapse; margin-top: 32px; border-radius: 12px; overflow: hidden; border: 1px solid rgba(0,0,0,0.08); }
.comparison-table { width: 100%; border-collapse: collapse; margin-top: 32px; border-radius: var(--radius-md); overflow: hidden; border: 1px solid rgba(0,0,0,0.08); }
.comparison-table th { padding: 14px 16px; font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .07em; text-align: left; background: rgba(0,0,0,0.04); color: var(--text-dim); }
.cta-section { background: #e8f0fe; }
.cta-box { border-radius: 16px; padding: 56px 48px; text-align: center; max-width: 680px; margin: 0 auto; }
.cta-box { border-radius: var(--radius-lg); padding: 56px 48px; text-align: center; max-width: 680px; margin: 0 auto; }
.cta-box h2 { font-size: clamp(24px,3vw,36px); font-weight: 800; color: #1d1d1f; margin-bottom: 16px; }
.cta-box p { font-size: 17px; color: rgba(0,0,0,.7); margin-bottom: 36px; line-height: 1.65; }
.lv-disclaimer { font-size:.75rem; color:rgba(0,0,0,.75); margin-top:24px; padding:12px 16px; background:rgba(0,0,0,.04); border-radius:8px; line-height:1.6; max-width:800px; }
.lv-disclaimer { font-size:.75rem; color:rgba(0,0,0,.75); margin-top:24px; padding:12px 16px; background:rgba(0,0,0,.04); border-radius: var(--radius-md); line-height:1.6; max-width:800px; }
.lv-disclaimer a { color:rgba(0,0,0,.65); text-decoration:underline; }
footer { border-top: 1px solid var(--border); padding: 40px; }
.footer-inner { max-width: 1140px; margin: 0 auto; display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 20px; }
@@ -108,7 +108,7 @@
.footer-links { display: flex; gap: 24px; align-items: center; flex-wrap: wrap; }
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social { width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px; display: flex; align-items: center; justify-content: center; transition: border-color 0.2s, background 0.2s; }
.footer-social { width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; transition: border-color 0.2s, background 0.2s; }
.footer-social:hover { border-color: rgba(0,0,0,0.25); background: rgba(0,0,0,0.04); }
@media (max-width:900px) { nav { padding:0 20px; } .use-case-grid { grid-template-columns:1fr; } .decisions-grid { grid-template-columns:1fr 1fr; } .conflict-grid { grid-template-columns:1fr; } }
@media (max-width:600px) { section { padding:72px 20px; } .decisions-grid { grid-template-columns:1fr; } nav { padding:0 16px; } .hero h1 { letter-spacing:-1px; } .contradiction-box { padding:24px 20px; } }
@@ -150,7 +150,7 @@
<p class="section-sub">Every engagement is anchored to a specific decision. Select a capability to discuss how Landvex applies it to your context.</p>
<div class="use-case-grid">
<a class="use-case-card" href="/contact/" style="display:block;text-decoration:none;">
<span class="uc-icon" style="font-size:22px;display:block;margin-bottom:16px;color:#5599ff;">&#9650;</span>
<span class="uc-icon" style="font-size:22px;display:block;margin-bottom:16px;color:#5599ff;">&#965050</span>
<h3>Road Network Condition Monitoring</h3>
<p>Systematic monitoring of road surface conditions, markings, signage, and drainage across Swedish national and municipal networks. Updated continuously, delivered as structured intelligence.</p>
<div style="margin-top:14px;font-size:13px;font-weight:700;color:#0066ff;">Learn more &rarr;</div>
@@ -270,16 +270,16 @@
<div class="conflict-grid">
<div class="conflict-side conflict-official">
<div class="conflict-side-label">Official narrative</div>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; Road condition survey: 11 months ago, IRI within standard</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; Maintenance programme: on schedule</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; No service alerts active</li></ul>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 Road condition survey: 11 months ago, IRI within standard</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 Maintenance programme: on schedule</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 No service alerts active</li></ul>
</div>
<div class="conflict-side conflict-observed">
<div class="conflict-side-label">Observed reality</div>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Pavement cracking extending 340m beyond surveyed section</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Drainage channel blockage at four culvert locations</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Safety barrier damage at three segments unreported</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Lane marking fading below retroreflectivity threshold on 12km</li></ul>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Pavement cracking extending 340m beyond surveyed section</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Drainage channel blockage at four culvert locations</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Safety barrier damage at three segments unreported</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Lane marking fading below retroreflectivity threshold on 12km</li></ul>
</div>
</div>
<div class="conflict-footer">
@@ -69,16 +69,16 @@
.decisions-bg { background: var(--surface); }
.decisions-grid { display: grid; grid-template-columns: repeat(3,1fr); gap: 16px; margin-top: 40px; }
.decision-chip { background: var(--bg-dark); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 18px 22px; display: flex; align-items: center; gap: 12px; }
.decision-chip .chip-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--blue); flex-shrink: 0; }
.decision-chip .chip-dot { width: 8px; height: 8px; border-radius: var(--radius-full); background: var(--blue); flex-shrink: 0; }
.decision-chip span { font-size: 14px; font-weight: 600; color: var(--text-light); }
.contradiction-bg { background: #e8f0fe; }
.contradiction-box { background: rgba(0,0,0,.05); border: 1px solid rgba(0,0,0,.1); border-radius: 16px; padding: 36px 40px; margin-bottom: 32px; }
.contradiction-box { background: rgba(0,0,0,.05); border: 1px solid rgba(0,0,0,.1); border-radius: var(--radius-lg); padding: 36px 40px; margin-bottom: 32px; }
.conflict-header { display: flex; align-items: flex-start; gap: 16px; margin-bottom: 24px; }
.conflict-flag { font-size: 1.5rem; color: #f87171; }
.conflict-title { color: #f87171; font-weight: 800; font-size: .9375rem; margin-bottom: 4px; }
.conflict-desc { color: rgba(0,0,0,.6); font-size: .875rem; line-height: 1.6; }
.conflict-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.conflict-side { border-radius: 10px; padding: 16px; }
.conflict-side { border-radius: var(--radius-md); padding: 16px; }
.conflict-official { background: rgba(34,197,94,.1); border: 1px solid rgba(34,197,94,.2); }
.conflict-observed { background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.2); }
.conflict-side-label { font-size: .75rem; font-weight: 700; text-transform: uppercase; letter-spacing: .08em; margin-bottom: 10px; }
@@ -92,13 +92,13 @@
.breadcrumb a { color: var(--text-muted); }
.breadcrumb a:hover { color: #1d1d1f; }
.breadcrumb .sep { color: var(--border); }
.comparison-table { width: 100%; border-collapse: collapse; margin-top: 32px; border-radius: 12px; overflow: hidden; border: 1px solid rgba(0,0,0,0.08); }
.comparison-table { width: 100%; border-collapse: collapse; margin-top: 32px; border-radius: var(--radius-md); overflow: hidden; border: 1px solid rgba(0,0,0,0.08); }
.comparison-table th { padding: 14px 16px; font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .07em; text-align: left; background: rgba(0,0,0,0.04); color: var(--text-dim); }
.cta-section { background: #e8f0fe; }
.cta-box { border-radius: 16px; padding: 56px 48px; text-align: center; max-width: 680px; margin: 0 auto; }
.cta-box { border-radius: var(--radius-lg); padding: 56px 48px; text-align: center; max-width: 680px; margin: 0 auto; }
.cta-box h2 { font-size: clamp(24px,3vw,36px); font-weight: 800; color: #1d1d1f; margin-bottom: 16px; }
.cta-box p { font-size: 17px; color: rgba(0,0,0,.7); margin-bottom: 36px; line-height: 1.65; }
.lv-disclaimer { font-size:.75rem; color:rgba(0,0,0,.75); margin-top:24px; padding:12px 16px; background:rgba(0,0,0,.04); border-radius:8px; line-height:1.6; max-width:800px; }
.lv-disclaimer { font-size:.75rem; color:rgba(0,0,0,.75); margin-top:24px; padding:12px 16px; background:rgba(0,0,0,.04); border-radius: var(--radius-md); line-height:1.6; max-width:800px; }
.lv-disclaimer a { color:rgba(0,0,0,.65); text-decoration:underline; }
footer { border-top: 1px solid var(--border); padding: 40px; }
.footer-inner { max-width: 1140px; margin: 0 auto; display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 20px; }
@@ -108,7 +108,7 @@
.footer-links { display: flex; gap: 24px; align-items: center; flex-wrap: wrap; }
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social { width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px; display: flex; align-items: center; justify-content: center; transition: border-color 0.2s, background 0.2s; }
.footer-social { width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; transition: border-color 0.2s, background 0.2s; }
.footer-social:hover { border-color: rgba(0,0,0,0.25); background: rgba(0,0,0,0.04); }
@media (max-width:900px) { nav { padding:0 20px; } .use-case-grid { grid-template-columns:1fr; } .decisions-grid { grid-template-columns:1fr 1fr; } .conflict-grid { grid-template-columns:1fr; } }
@media (max-width:600px) { section { padding:72px 20px; } .decisions-grid { grid-template-columns:1fr; } nav { padding:0 16px; } .hero h1 { letter-spacing:-1px; } .contradiction-box { padding:24px 20px; } }
@@ -150,7 +150,7 @@
<p class="section-sub">Every engagement is anchored to a specific decision. Select a capability to discuss how Landvex applies it to your context.</p>
<div class="use-case-grid">
<a class="use-case-card" href="/contact/" style="display:block;text-decoration:none;">
<span class="uc-icon" style="font-size:22px;display:block;margin-bottom:16px;color:#5599ff;">&#9650;</span>
<span class="uc-icon" style="font-size:22px;display:block;margin-bottom:16px;color:#5599ff;">&#965050</span>
<h3>SRN Condition Monitoring</h3>
<p>Continuous observation of National Highways assets between SCANNER and SCRIM survey cycles. Detect surface deterioration, drainage failure, and barrier damage to inform interim maintenance scheduling.</p>
<div style="margin-top:14px;font-size:13px;font-weight:700;color:#0066ff;">Learn more &rarr;</div>
@@ -270,16 +270,16 @@
<div class="conflict-grid">
<div class="conflict-side conflict-official">
<div class="conflict-side-label">Official narrative</div>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; SCANNER survey: 14 months ago, Road Condition Indicator: green</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; Highway maintenance programme: within budget</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; No section 58 defences active</li></ul>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 SCANNER survey: 14 months ago, Road Condition Indicator: green</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 Highway maintenance programme: within budget</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 No section 58 defences active</li></ul>
</div>
<div class="conflict-side conflict-observed">
<div class="conflict-side-label">Observed reality</div>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Transverse cracking at 11 locations exceeding CVI defect thresholds</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Carriageway edge deterioration on 7.4km unreported</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Gully blockages identified at 23 locations beyond maintenance schedule</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Street lighting column corrosion at 14 locations above intervention level</li></ul>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Transverse cracking at 11 locations exceeding CVI defect thresholds</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Carriageway edge deterioration on 7.4km unreported</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Gully blockages identified at 23 locations beyond maintenance schedule</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Street lighting column corrosion at 14 locations above intervention level</li></ul>
</div>
</div>
<div class="conflict-footer">
@@ -138,7 +138,7 @@
.evidence-card p { font-size: 14px; color: var(--text-dim); line-height: 1.6; }
.cta-banner {
background: #e8f0fe; border-radius: 16px; padding: 40px 48px;
background: #e8f0fe; border-radius: var(--radius-lg); padding: 40px 48px;
display: flex; align-items: center; gap: 48px; flex-wrap: wrap; margin-top: 48px;
}
.cta-banner h3 { color: #1d1d1f; font-size: 1.25rem; font-weight: 800; margin-bottom: 8px; }
@@ -184,13 +184,13 @@
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social {
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px;
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md);
display: flex; align-items: center; justify-content: center;
transition: border-color 0.2s, background 0.2s;
}
.footer-social:hover { border-color: rgba(0,0,0,0.25); background: rgba(0,0,0,0.04); }
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius:8px;line-height:1.6;max-width:800px}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius: var(--radius-md);line-height:1.6;max-width:800px}
.lv-disclaimer a{color:rgba(0,0,0,.65);text-decoration:underline}
@media (max-width: 900px) {
@@ -138,7 +138,7 @@
.evidence-card p { font-size: 14px; color: var(--text-dim); line-height: 1.6; }
.cta-banner {
background: #e8f0fe; border-radius: 16px; padding: 40px 48px;
background: #e8f0fe; border-radius: var(--radius-lg); padding: 40px 48px;
display: flex; align-items: center; gap: 48px; flex-wrap: wrap; margin-top: 48px;
}
.cta-banner h3 { color: #1d1d1f; font-size: 1.25rem; font-weight: 800; margin-bottom: 8px; }
@@ -184,13 +184,13 @@
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social {
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px;
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md);
display: flex; align-items: center; justify-content: center;
transition: border-color 0.2s, background 0.2s;
}
.footer-social:hover { border-color: rgba(0,0,0,0.25); background: rgba(0,0,0,0.04); }
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius:8px;line-height:1.6;max-width:800px}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius: var(--radius-md);line-height:1.6;max-width:800px}
.lv-disclaimer a{color:rgba(0,0,0,.65);text-decoration:underline}
@media (max-width: 900px) {
@@ -138,7 +138,7 @@
.evidence-card p { font-size: 14px; color: var(--text-dim); line-height: 1.6; }
.cta-banner {
background: #e8f0fe; border-radius: 16px; padding: 40px 48px;
background: #e8f0fe; border-radius: var(--radius-lg); padding: 40px 48px;
display: flex; align-items: center; gap: 48px; flex-wrap: wrap; margin-top: 48px;
}
.cta-banner h3 { color: #1d1d1f; font-size: 1.25rem; font-weight: 800; margin-bottom: 8px; }
@@ -184,13 +184,13 @@
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social {
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px;
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md);
display: flex; align-items: center; justify-content: center;
transition: border-color 0.2s, background 0.2s;
}
.footer-social:hover { border-color: rgba(0,0,0,0.25); background: rgba(0,0,0,0.04); }
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius:8px;line-height:1.6;max-width:800px}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius: var(--radius-md);line-height:1.6;max-width:800px}
.lv-disclaimer a{color:rgba(0,0,0,.65);text-decoration:underline}
@media (max-width: 900px) {
@@ -69,16 +69,16 @@
.decisions-bg { background: var(--surface); }
.decisions-grid { display: grid; grid-template-columns: repeat(3,1fr); gap: 16px; margin-top: 40px; }
.decision-chip { background: var(--bg-dark); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 18px 22px; display: flex; align-items: center; gap: 12px; }
.decision-chip .chip-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--blue); flex-shrink: 0; }
.decision-chip .chip-dot { width: 8px; height: 8px; border-radius: var(--radius-full); background: var(--blue); flex-shrink: 0; }
.decision-chip span { font-size: 14px; font-weight: 600; color: var(--text-light); }
.contradiction-bg { background: #e8f0fe; }
.contradiction-box { background: rgba(0,0,0,.05); border: 1px solid rgba(0,0,0,.1); border-radius: 16px; padding: 36px 40px; margin-bottom: 32px; }
.contradiction-box { background: rgba(0,0,0,.05); border: 1px solid rgba(0,0,0,.1); border-radius: var(--radius-lg); padding: 36px 40px; margin-bottom: 32px; }
.conflict-header { display: flex; align-items: flex-start; gap: 16px; margin-bottom: 24px; }
.conflict-flag { font-size: 1.5rem; color: #f87171; }
.conflict-title { color: #f87171; font-weight: 800; font-size: .9375rem; margin-bottom: 4px; }
.conflict-desc { color: rgba(0,0,0,.6); font-size: .875rem; line-height: 1.6; }
.conflict-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.conflict-side { border-radius: 10px; padding: 16px; }
.conflict-side { border-radius: var(--radius-md); padding: 16px; }
.conflict-official { background: rgba(34,197,94,.1); border: 1px solid rgba(34,197,94,.2); }
.conflict-observed { background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.2); }
.conflict-side-label { font-size: .75rem; font-weight: 700; text-transform: uppercase; letter-spacing: .08em; margin-bottom: 10px; }
@@ -92,13 +92,13 @@
.breadcrumb a { color: var(--text-muted); }
.breadcrumb a:hover { color: #1d1d1f; }
.breadcrumb .sep { color: var(--border); }
.comparison-table { width: 100%; border-collapse: collapse; margin-top: 32px; border-radius: 12px; overflow: hidden; border: 1px solid rgba(0,0,0,0.08); }
.comparison-table { width: 100%; border-collapse: collapse; margin-top: 32px; border-radius: var(--radius-md); overflow: hidden; border: 1px solid rgba(0,0,0,0.08); }
.comparison-table th { padding: 14px 16px; font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .07em; text-align: left; background: rgba(0,0,0,0.04); color: var(--text-dim); }
.cta-section { background: #e8f0fe; }
.cta-box { border-radius: 16px; padding: 56px 48px; text-align: center; max-width: 680px; margin: 0 auto; }
.cta-box { border-radius: var(--radius-lg); padding: 56px 48px; text-align: center; max-width: 680px; margin: 0 auto; }
.cta-box h2 { font-size: clamp(24px,3vw,36px); font-weight: 800; color: #1d1d1f; margin-bottom: 16px; }
.cta-box p { font-size: 17px; color: rgba(0,0,0,.7); margin-bottom: 36px; line-height: 1.65; }
.lv-disclaimer { font-size:.75rem; color:rgba(0,0,0,.75); margin-top:24px; padding:12px 16px; background:rgba(0,0,0,.04); border-radius:8px; line-height:1.6; max-width:800px; }
.lv-disclaimer { font-size:.75rem; color:rgba(0,0,0,.75); margin-top:24px; padding:12px 16px; background:rgba(0,0,0,.04); border-radius: var(--radius-md); line-height:1.6; max-width:800px; }
.lv-disclaimer a { color:rgba(0,0,0,.65); text-decoration:underline; }
footer { border-top: 1px solid var(--border); padding: 40px; }
.footer-inner { max-width: 1140px; margin: 0 auto; display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 20px; }
@@ -108,7 +108,7 @@
.footer-links { display: flex; gap: 24px; align-items: center; flex-wrap: wrap; }
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social { width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px; display: flex; align-items: center; justify-content: center; transition: border-color 0.2s, background 0.2s; }
.footer-social { width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; transition: border-color 0.2s, background 0.2s; }
.footer-social:hover { border-color: rgba(0,0,0,0.25); background: rgba(0,0,0,0.04); }
@media (max-width:900px) { nav { padding:0 20px; } .use-case-grid { grid-template-columns:1fr; } .decisions-grid { grid-template-columns:1fr 1fr; } .conflict-grid { grid-template-columns:1fr; } }
@media (max-width:600px) { section { padding:72px 20px; } .decisions-grid { grid-template-columns:1fr; } nav { padding:0 16px; } .hero h1 { letter-spacing:-1px; } .contradiction-box { padding:24px 20px; } }
@@ -150,7 +150,7 @@
<p class="section-sub">Every engagement is anchored to a specific decision. Select a capability to discuss how Landvex applies it to your context.</p>
<div class="use-case-grid">
<a class="use-case-card" href="/contact/" style="display:block;text-decoration:none;">
<span class="uc-icon" style="font-size:22px;display:block;margin-bottom:16px;color:#5599ff;">&#9650;</span>
<span class="uc-icon" style="font-size:22px;display:block;margin-bottom:16px;color:#5599ff;">&#965050</span>
<h3>Centre-Ville Retail Assessment</h3>
<p>Field-verified assessment of French town centre retail performance: occupier mix, vacancy indicators, pedestrian environment quality, and competition from periurban formats. Evidence for Action Coeur de Ville investment decisions.</p>
<div style="margin-top:14px;font-size:13px;font-weight:700;color:#0066ff;">Learn more &rarr;</div>
@@ -270,16 +270,16 @@
<div class="conflict-grid">
<div class="conflict-side conflict-official">
<div class="conflict-side-label">Official narrative</div>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; Vacancy rate: 8.4% (PROCOS Q2)</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; Pedestrianisation programme: improving footfall</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; No exceptional distress signals in planning register</li></ul>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 Vacancy rate: 8.4% (PROCOS Q2)</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 Pedestrianisation programme: improving footfall</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 No exceptional distress signals in planning register</li></ul>
</div>
<div class="conflict-side conflict-observed">
<div class="conflict-side-label">Observed reality</div>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Active vacancy on 17 of 71 units — effective rate 23.9%</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Pop-up and temporary units masking structural vacancy on 5 units</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Two anchor occupiers with reduced opening hours and degraded fit-out</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Street environment maintenance below prime standard on 40% of frontage</li></ul>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Active vacancy on 17 of 71 units — effective rate 23.9%</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Pop-up and temporary units masking structural vacancy on 5 units</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Two anchor occupiers with reduced opening hours and degraded fit-out</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Street environment maintenance below prime standard on 40% of frontage</li></ul>
</div>
</div>
<div class="conflict-footer">
@@ -69,16 +69,16 @@
.decisions-bg { background: var(--surface); }
.decisions-grid { display: grid; grid-template-columns: repeat(3,1fr); gap: 16px; margin-top: 40px; }
.decision-chip { background: var(--bg-dark); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 18px 22px; display: flex; align-items: center; gap: 12px; }
.decision-chip .chip-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--blue); flex-shrink: 0; }
.decision-chip .chip-dot { width: 8px; height: 8px; border-radius: var(--radius-full); background: var(--blue); flex-shrink: 0; }
.decision-chip span { font-size: 14px; font-weight: 600; color: var(--text-light); }
.contradiction-bg { background: #e8f0fe; }
.contradiction-box { background: rgba(0,0,0,.05); border: 1px solid rgba(0,0,0,.1); border-radius: 16px; padding: 36px 40px; margin-bottom: 32px; }
.contradiction-box { background: rgba(0,0,0,.05); border: 1px solid rgba(0,0,0,.1); border-radius: var(--radius-lg); padding: 36px 40px; margin-bottom: 32px; }
.conflict-header { display: flex; align-items: flex-start; gap: 16px; margin-bottom: 24px; }
.conflict-flag { font-size: 1.5rem; color: #f87171; }
.conflict-title { color: #f87171; font-weight: 800; font-size: .9375rem; margin-bottom: 4px; }
.conflict-desc { color: rgba(0,0,0,.6); font-size: .875rem; line-height: 1.6; }
.conflict-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.conflict-side { border-radius: 10px; padding: 16px; }
.conflict-side { border-radius: var(--radius-md); padding: 16px; }
.conflict-official { background: rgba(34,197,94,.1); border: 1px solid rgba(34,197,94,.2); }
.conflict-observed { background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.2); }
.conflict-side-label { font-size: .75rem; font-weight: 700; text-transform: uppercase; letter-spacing: .08em; margin-bottom: 10px; }
@@ -92,13 +92,13 @@
.breadcrumb a { color: var(--text-muted); }
.breadcrumb a:hover { color: #1d1d1f; }
.breadcrumb .sep { color: var(--border); }
.comparison-table { width: 100%; border-collapse: collapse; margin-top: 32px; border-radius: 12px; overflow: hidden; border: 1px solid rgba(0,0,0,0.08); }
.comparison-table { width: 100%; border-collapse: collapse; margin-top: 32px; border-radius: var(--radius-md); overflow: hidden; border: 1px solid rgba(0,0,0,0.08); }
.comparison-table th { padding: 14px 16px; font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .07em; text-align: left; background: rgba(0,0,0,0.04); color: var(--text-dim); }
.cta-section { background: #e8f0fe; }
.cta-box { border-radius: 16px; padding: 56px 48px; text-align: center; max-width: 680px; margin: 0 auto; }
.cta-box { border-radius: var(--radius-lg); padding: 56px 48px; text-align: center; max-width: 680px; margin: 0 auto; }
.cta-box h2 { font-size: clamp(24px,3vw,36px); font-weight: 800; color: #1d1d1f; margin-bottom: 16px; }
.cta-box p { font-size: 17px; color: rgba(0,0,0,.7); margin-bottom: 36px; line-height: 1.65; }
.lv-disclaimer { font-size:.75rem; color:rgba(0,0,0,.75); margin-top:24px; padding:12px 16px; background:rgba(0,0,0,.04); border-radius:8px; line-height:1.6; max-width:800px; }
.lv-disclaimer { font-size:.75rem; color:rgba(0,0,0,.75); margin-top:24px; padding:12px 16px; background:rgba(0,0,0,.04); border-radius: var(--radius-md); line-height:1.6; max-width:800px; }
.lv-disclaimer a { color:rgba(0,0,0,.65); text-decoration:underline; }
footer { border-top: 1px solid var(--border); padding: 40px; }
.footer-inner { max-width: 1140px; margin: 0 auto; display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 20px; }
@@ -108,7 +108,7 @@
.footer-links { display: flex; gap: 24px; align-items: center; flex-wrap: wrap; }
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social { width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px; display: flex; align-items: center; justify-content: center; transition: border-color 0.2s, background 0.2s; }
.footer-social { width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; transition: border-color 0.2s, background 0.2s; }
.footer-social:hover { border-color: rgba(0,0,0,0.25); background: rgba(0,0,0,0.04); }
@media (max-width:900px) { nav { padding:0 20px; } .use-case-grid { grid-template-columns:1fr; } .decisions-grid { grid-template-columns:1fr 1fr; } .conflict-grid { grid-template-columns:1fr; } }
@media (max-width:600px) { section { padding:72px 20px; } .decisions-grid { grid-template-columns:1fr; } nav { padding:0 16px; } .hero h1 { letter-spacing:-1px; } .contradiction-box { padding:24px 20px; } }
@@ -150,7 +150,7 @@
<p class="section-sub">Every engagement is anchored to a specific decision. Select a capability to discuss how Landvex applies it to your context.</p>
<div class="use-case-grid">
<a class="use-case-card" href="/contact/" style="display:block;text-decoration:none;">
<span class="uc-icon" style="font-size:22px;display:block;margin-bottom:16px;color:#5599ff;">&#9650;</span>
<span class="uc-icon" style="font-size:22px;display:block;margin-bottom:16px;color:#5599ff;">&#965050</span>
<h3>Innenstadt Retail Location Assessment</h3>
<p>Field-verified assessment of German Innenstadt retail performance: occupier mix, vacancy rate, pedestrian environment quality, and physical asset condition. Evidence-based input for 1A-Lage and secondary location decisions.</p>
<div style="margin-top:14px;font-size:13px;font-weight:700;color:#0066ff;">Learn more &rarr;</div>
@@ -270,16 +270,16 @@
<div class="conflict-grid">
<div class="conflict-side conflict-official">
<div class="conflict-side-label">Official narrative</div>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; Vacancy rate: 6.1% (BulwienGesa Q4)</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; Footfall trend: recovering post-pandemic</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; 1A-Lage classification: maintained</li></ul>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 Vacancy rate: 6.1% (BulwienGesa Q4)</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 Footfall trend: recovering post-pandemic</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 1A-Lage classification: maintained</li></ul>
</div>
<div class="conflict-side conflict-observed">
<div class="conflict-side-label">Observed reality</div>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Active vacancy on 14 of 62 units — effective rate 22.6%</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Six units operating as temporary or charity lets masking structural vacancy</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Four anchor occupiers showing evidence of lease-end reduction in fit-out investment</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Street environment maintenance at B-Lage standard on central 300m stretch</li></ul>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Active vacancy on 14 of 62 units — effective rate 22.6%</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Six units operating as temporary or charity lets masking structural vacancy</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Four anchor occupiers showing evidence of lease-end reduction in fit-out investment</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Street environment maintenance at B-Lage standard on central 300m stretch</li></ul>
</div>
</div>
<div class="conflict-footer">
@@ -69,16 +69,16 @@
.decisions-bg { background: var(--surface); }
.decisions-grid { display: grid; grid-template-columns: repeat(3,1fr); gap: 16px; margin-top: 40px; }
.decision-chip { background: var(--bg-dark); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 18px 22px; display: flex; align-items: center; gap: 12px; }
.decision-chip .chip-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--blue); flex-shrink: 0; }
.decision-chip .chip-dot { width: 8px; height: 8px; border-radius: var(--radius-full); background: var(--blue); flex-shrink: 0; }
.decision-chip span { font-size: 14px; font-weight: 600; color: var(--text-light); }
.contradiction-bg { background: #e8f0fe; }
.contradiction-box { background: rgba(0,0,0,.05); border: 1px solid rgba(0,0,0,.1); border-radius: 16px; padding: 36px 40px; margin-bottom: 32px; }
.contradiction-box { background: rgba(0,0,0,.05); border: 1px solid rgba(0,0,0,.1); border-radius: var(--radius-lg); padding: 36px 40px; margin-bottom: 32px; }
.conflict-header { display: flex; align-items: flex-start; gap: 16px; margin-bottom: 24px; }
.conflict-flag { font-size: 1.5rem; color: #f87171; }
.conflict-title { color: #f87171; font-weight: 800; font-size: .9375rem; margin-bottom: 4px; }
.conflict-desc { color: rgba(0,0,0,.6); font-size: .875rem; line-height: 1.6; }
.conflict-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.conflict-side { border-radius: 10px; padding: 16px; }
.conflict-side { border-radius: var(--radius-md); padding: 16px; }
.conflict-official { background: rgba(34,197,94,.1); border: 1px solid rgba(34,197,94,.2); }
.conflict-observed { background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.2); }
.conflict-side-label { font-size: .75rem; font-weight: 700; text-transform: uppercase; letter-spacing: .08em; margin-bottom: 10px; }
@@ -92,13 +92,13 @@
.breadcrumb a { color: var(--text-muted); }
.breadcrumb a:hover { color: #1d1d1f; }
.breadcrumb .sep { color: var(--border); }
.comparison-table { width: 100%; border-collapse: collapse; margin-top: 32px; border-radius: 12px; overflow: hidden; border: 1px solid rgba(0,0,0,0.08); }
.comparison-table { width: 100%; border-collapse: collapse; margin-top: 32px; border-radius: var(--radius-md); overflow: hidden; border: 1px solid rgba(0,0,0,0.08); }
.comparison-table th { padding: 14px 16px; font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .07em; text-align: left; background: rgba(0,0,0,0.04); color: var(--text-dim); }
.cta-section { background: #e8f0fe; }
.cta-box { border-radius: 16px; padding: 56px 48px; text-align: center; max-width: 680px; margin: 0 auto; }
.cta-box { border-radius: var(--radius-lg); padding: 56px 48px; text-align: center; max-width: 680px; margin: 0 auto; }
.cta-box h2 { font-size: clamp(24px,3vw,36px); font-weight: 800; color: #1d1d1f; margin-bottom: 16px; }
.cta-box p { font-size: 17px; color: rgba(0,0,0,.7); margin-bottom: 36px; line-height: 1.65; }
.lv-disclaimer { font-size:.75rem; color:rgba(0,0,0,.75); margin-top:24px; padding:12px 16px; background:rgba(0,0,0,.04); border-radius:8px; line-height:1.6; max-width:800px; }
.lv-disclaimer { font-size:.75rem; color:rgba(0,0,0,.75); margin-top:24px; padding:12px 16px; background:rgba(0,0,0,.04); border-radius: var(--radius-md); line-height:1.6; max-width:800px; }
.lv-disclaimer a { color:rgba(0,0,0,.65); text-decoration:underline; }
footer { border-top: 1px solid var(--border); padding: 40px; }
.footer-inner { max-width: 1140px; margin: 0 auto; display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 20px; }
@@ -108,7 +108,7 @@
.footer-links { display: flex; gap: 24px; align-items: center; flex-wrap: wrap; }
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social { width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px; display: flex; align-items: center; justify-content: center; transition: border-color 0.2s, background 0.2s; }
.footer-social { width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; transition: border-color 0.2s, background 0.2s; }
.footer-social:hover { border-color: rgba(0,0,0,0.25); background: rgba(0,0,0,0.04); }
@media (max-width:900px) { nav { padding:0 20px; } .use-case-grid { grid-template-columns:1fr; } .decisions-grid { grid-template-columns:1fr 1fr; } .conflict-grid { grid-template-columns:1fr; } }
@media (max-width:600px) { section { padding:72px 20px; } .decisions-grid { grid-template-columns:1fr; } nav { padding:0 16px; } .hero h1 { letter-spacing:-1px; } .contradiction-box { padding:24px 20px; } }
@@ -150,7 +150,7 @@
<p class="section-sub">Every engagement is anchored to a specific decision. Select a capability to discuss how Landvex applies it to your context.</p>
<div class="use-case-grid">
<a class="use-case-card" href="/contact/" style="display:block;text-decoration:none;">
<span class="uc-icon" style="font-size:22px;display:block;margin-bottom:16px;color:#5599ff;">&#9650;</span>
<span class="uc-icon" style="font-size:22px;display:block;margin-bottom:16px;color:#5599ff;">&#965050</span>
<h3>Winkelgebied Performance Assessment</h3>
<p>Field-verified assessment of Dutch winkelgebieden: occupier mix, vacancy indicators, physical environment quality, and competitive dynamics. Evidence-based input for acquisition, lease negotiation, and disposal decisions.</p>
<div style="margin-top:14px;font-size:13px;font-weight:700;color:#0066ff;">Learn more &rarr;</div>
@@ -270,16 +270,16 @@
<div class="conflict-grid">
<div class="conflict-side conflict-official">
<div class="conflict-side-label">Official narrative</div>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; Locatus vacancy: 7.8%</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; Pedestrian footfall: stable year-on-year</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; No structural distress in gemeentelijk monitor</li></ul>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 Locatus vacancy: 7.8%</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 Pedestrian footfall: stable year-on-year</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 No structural distress in gemeentelijk monitor</li></ul>
</div>
<div class="conflict-side conflict-observed">
<div class="conflict-side-label">Observed reality</div>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Active structural vacancy on 13 of 58 units — effective rate 22.4%</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; 4 pop-up units and 2 charity shops masking structural vacancy</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Two anchor occupiers showing fit-out disinvestment consistent with lease-end</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Public realm condition below Centrum-standard on three block frontages</li></ul>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Active structural vacancy on 13 of 58 units — effective rate 22.4%</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 4 pop-up units and 2 charity shops masking structural vacancy</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Two anchor occupiers showing fit-out disinvestment consistent with lease-end</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Public realm condition below Centrum-standard on three block frontages</li></ul>
</div>
</div>
<div class="conflict-footer">
@@ -69,16 +69,16 @@
.decisions-bg { background: var(--surface); }
.decisions-grid { display: grid; grid-template-columns: repeat(3,1fr); gap: 16px; margin-top: 40px; }
.decision-chip { background: var(--bg-dark); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 18px 22px; display: flex; align-items: center; gap: 12px; }
.decision-chip .chip-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--blue); flex-shrink: 0; }
.decision-chip .chip-dot { width: 8px; height: 8px; border-radius: var(--radius-full); background: var(--blue); flex-shrink: 0; }
.decision-chip span { font-size: 14px; font-weight: 600; color: var(--text-light); }
.contradiction-bg { background: #e8f0fe; }
.contradiction-box { background: rgba(0,0,0,.05); border: 1px solid rgba(0,0,0,.1); border-radius: 16px; padding: 36px 40px; margin-bottom: 32px; }
.contradiction-box { background: rgba(0,0,0,.05); border: 1px solid rgba(0,0,0,.1); border-radius: var(--radius-lg); padding: 36px 40px; margin-bottom: 32px; }
.conflict-header { display: flex; align-items: flex-start; gap: 16px; margin-bottom: 24px; }
.conflict-flag { font-size: 1.5rem; color: #f87171; }
.conflict-title { color: #f87171; font-weight: 800; font-size: .9375rem; margin-bottom: 4px; }
.conflict-desc { color: rgba(0,0,0,.6); font-size: .875rem; line-height: 1.6; }
.conflict-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.conflict-side { border-radius: 10px; padding: 16px; }
.conflict-side { border-radius: var(--radius-md); padding: 16px; }
.conflict-official { background: rgba(34,197,94,.1); border: 1px solid rgba(34,197,94,.2); }
.conflict-observed { background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.2); }
.conflict-side-label { font-size: .75rem; font-weight: 700; text-transform: uppercase; letter-spacing: .08em; margin-bottom: 10px; }
@@ -92,13 +92,13 @@
.breadcrumb a { color: var(--text-muted); }
.breadcrumb a:hover { color: #1d1d1f; }
.breadcrumb .sep { color: var(--border); }
.comparison-table { width: 100%; border-collapse: collapse; margin-top: 32px; border-radius: 12px; overflow: hidden; border: 1px solid rgba(0,0,0,0.08); }
.comparison-table { width: 100%; border-collapse: collapse; margin-top: 32px; border-radius: var(--radius-md); overflow: hidden; border: 1px solid rgba(0,0,0,0.08); }
.comparison-table th { padding: 14px 16px; font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .07em; text-align: left; background: rgba(0,0,0,0.04); color: var(--text-dim); }
.cta-section { background: #e8f0fe; }
.cta-box { border-radius: 16px; padding: 56px 48px; text-align: center; max-width: 680px; margin: 0 auto; }
.cta-box { border-radius: var(--radius-lg); padding: 56px 48px; text-align: center; max-width: 680px; margin: 0 auto; }
.cta-box h2 { font-size: clamp(24px,3vw,36px); font-weight: 800; color: #1d1d1f; margin-bottom: 16px; }
.cta-box p { font-size: 17px; color: rgba(0,0,0,.7); margin-bottom: 36px; line-height: 1.65; }
.lv-disclaimer { font-size:.75rem; color:rgba(0,0,0,.75); margin-top:24px; padding:12px 16px; background:rgba(0,0,0,.04); border-radius:8px; line-height:1.6; max-width:800px; }
.lv-disclaimer { font-size:.75rem; color:rgba(0,0,0,.75); margin-top:24px; padding:12px 16px; background:rgba(0,0,0,.04); border-radius: var(--radius-md); line-height:1.6; max-width:800px; }
.lv-disclaimer a { color:rgba(0,0,0,.65); text-decoration:underline; }
footer { border-top: 1px solid var(--border); padding: 40px; }
.footer-inner { max-width: 1140px; margin: 0 auto; display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 20px; }
@@ -108,7 +108,7 @@
.footer-links { display: flex; gap: 24px; align-items: center; flex-wrap: wrap; }
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social { width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px; display: flex; align-items: center; justify-content: center; transition: border-color 0.2s, background 0.2s; }
.footer-social { width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; transition: border-color 0.2s, background 0.2s; }
.footer-social:hover { border-color: rgba(0,0,0,0.25); background: rgba(0,0,0,0.04); }
@media (max-width:900px) { nav { padding:0 20px; } .use-case-grid { grid-template-columns:1fr; } .decisions-grid { grid-template-columns:1fr 1fr; } .conflict-grid { grid-template-columns:1fr; } }
@media (max-width:600px) { section { padding:72px 20px; } .decisions-grid { grid-template-columns:1fr; } nav { padding:0 16px; } .hero h1 { letter-spacing:-1px; } .contradiction-box { padding:24px 20px; } }
@@ -150,7 +150,7 @@
<p class="section-sub">Every engagement is anchored to a specific decision. Select a capability to discuss how Landvex applies it to your context.</p>
<div class="use-case-grid">
<a class="use-case-card" href="/contact/" style="display:block;text-decoration:none;">
<span class="uc-icon" style="font-size:22px;display:block;margin-bottom:16px;color:#5599ff;">&#9650;</span>
<span class="uc-icon" style="font-size:22px;display:block;margin-bottom:16px;color:#5599ff;">&#965050</span>
<h3>Retail Location Assessment</h3>
<p>Field-verified assessment of Swedish retail locations: pedestrian environment quality, competitive positioning, retail mix, vacancy indicators, and physical access. Evidence-based input for lease negotiation and portfolio decisions.</p>
<div style="margin-top:14px;font-size:13px;font-weight:700;color:#0066ff;">Learn more &rarr;</div>
@@ -270,16 +270,16 @@
<div class="conflict-grid">
<div class="conflict-side conflict-official">
<div class="conflict-side-label">Official narrative</div>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; Vacancy rate: 4.2% (official BID survey, Q3)</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; Footfall index: stable year-on-year</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; No exceptional distress signals in planning data</li></ul>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 Vacancy rate: 4.2% (official BID survey, Q3)</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 Footfall index: stable year-on-year</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 No exceptional distress signals in planning data</li></ul>
</div>
<div class="conflict-side conflict-observed">
<div class="conflict-side-label">Observed reality</div>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Active vacancy on 11 of 48 units — effective rate 23%</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Temporary lets and pop-ups masking structural vacancy on 4 units</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Three anchor occupiers have reduced fit-out quality significantly</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Pedestrian environment maintenance below investment-grade standard</li></ul>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Active vacancy on 11 of 48 units — effective rate 23%</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Temporary lets and pop-ups masking structural vacancy on 4 units</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Three anchor occupiers have reduced fit-out quality significantly</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Pedestrian environment maintenance below investment-grade standard</li></ul>
</div>
</div>
<div class="conflict-footer">
@@ -69,16 +69,16 @@
.decisions-bg { background: var(--surface); }
.decisions-grid { display: grid; grid-template-columns: repeat(3,1fr); gap: 16px; margin-top: 40px; }
.decision-chip { background: var(--bg-dark); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 18px 22px; display: flex; align-items: center; gap: 12px; }
.decision-chip .chip-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--blue); flex-shrink: 0; }
.decision-chip .chip-dot { width: 8px; height: 8px; border-radius: var(--radius-full); background: var(--blue); flex-shrink: 0; }
.decision-chip span { font-size: 14px; font-weight: 600; color: var(--text-light); }
.contradiction-bg { background: #e8f0fe; }
.contradiction-box { background: rgba(0,0,0,.05); border: 1px solid rgba(0,0,0,.1); border-radius: 16px; padding: 36px 40px; margin-bottom: 32px; }
.contradiction-box { background: rgba(0,0,0,.05); border: 1px solid rgba(0,0,0,.1); border-radius: var(--radius-lg); padding: 36px 40px; margin-bottom: 32px; }
.conflict-header { display: flex; align-items: flex-start; gap: 16px; margin-bottom: 24px; }
.conflict-flag { font-size: 1.5rem; color: #f87171; }
.conflict-title { color: #f87171; font-weight: 800; font-size: .9375rem; margin-bottom: 4px; }
.conflict-desc { color: rgba(0,0,0,.6); font-size: .875rem; line-height: 1.6; }
.conflict-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.conflict-side { border-radius: 10px; padding: 16px; }
.conflict-side { border-radius: var(--radius-md); padding: 16px; }
.conflict-official { background: rgba(34,197,94,.1); border: 1px solid rgba(34,197,94,.2); }
.conflict-observed { background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.2); }
.conflict-side-label { font-size: .75rem; font-weight: 700; text-transform: uppercase; letter-spacing: .08em; margin-bottom: 10px; }
@@ -92,13 +92,13 @@
.breadcrumb a { color: var(--text-muted); }
.breadcrumb a:hover { color: #1d1d1f; }
.breadcrumb .sep { color: var(--border); }
.comparison-table { width: 100%; border-collapse: collapse; margin-top: 32px; border-radius: 12px; overflow: hidden; border: 1px solid rgba(0,0,0,0.08); }
.comparison-table { width: 100%; border-collapse: collapse; margin-top: 32px; border-radius: var(--radius-md); overflow: hidden; border: 1px solid rgba(0,0,0,0.08); }
.comparison-table th { padding: 14px 16px; font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .07em; text-align: left; background: rgba(0,0,0,0.04); color: var(--text-dim); }
.cta-section { background: #e8f0fe; }
.cta-box { border-radius: 16px; padding: 56px 48px; text-align: center; max-width: 680px; margin: 0 auto; }
.cta-box { border-radius: var(--radius-lg); padding: 56px 48px; text-align: center; max-width: 680px; margin: 0 auto; }
.cta-box h2 { font-size: clamp(24px,3vw,36px); font-weight: 800; color: #1d1d1f; margin-bottom: 16px; }
.cta-box p { font-size: 17px; color: rgba(0,0,0,.7); margin-bottom: 36px; line-height: 1.65; }
.lv-disclaimer { font-size:.75rem; color:rgba(0,0,0,.75); margin-top:24px; padding:12px 16px; background:rgba(0,0,0,.04); border-radius:8px; line-height:1.6; max-width:800px; }
.lv-disclaimer { font-size:.75rem; color:rgba(0,0,0,.75); margin-top:24px; padding:12px 16px; background:rgba(0,0,0,.04); border-radius: var(--radius-md); line-height:1.6; max-width:800px; }
.lv-disclaimer a { color:rgba(0,0,0,.65); text-decoration:underline; }
footer { border-top: 1px solid var(--border); padding: 40px; }
.footer-inner { max-width: 1140px; margin: 0 auto; display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 20px; }
@@ -108,7 +108,7 @@
.footer-links { display: flex; gap: 24px; align-items: center; flex-wrap: wrap; }
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social { width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px; display: flex; align-items: center; justify-content: center; transition: border-color 0.2s, background 0.2s; }
.footer-social { width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; transition: border-color 0.2s, background 0.2s; }
.footer-social:hover { border-color: rgba(0,0,0,0.25); background: rgba(0,0,0,0.04); }
@media (max-width:900px) { nav { padding:0 20px; } .use-case-grid { grid-template-columns:1fr; } .decisions-grid { grid-template-columns:1fr 1fr; } .conflict-grid { grid-template-columns:1fr; } }
@media (max-width:600px) { section { padding:72px 20px; } .decisions-grid { grid-template-columns:1fr; } nav { padding:0 16px; } .hero h1 { letter-spacing:-1px; } .contradiction-box { padding:24px 20px; } }
@@ -150,7 +150,7 @@
<p class="section-sub">Every engagement is anchored to a specific decision. Select a capability to discuss how Landvex applies it to your context.</p>
<div class="use-case-grid">
<a class="use-case-card" href="/contact/" style="display:block;text-decoration:none;">
<span class="uc-icon" style="font-size:22px;display:block;margin-bottom:16px;color:#5599ff;">&#9650;</span>
<span class="uc-icon" style="font-size:22px;display:block;margin-bottom:16px;color:#5599ff;">&#965050</span>
<h3>High Street Vacancy Monitoring</h3>
<p>Field-verified vacancy monitoring across UK town centres and high streets. Identify structural vacancy, temporary uses masking distress, and deterioration in occupier quality — updated continuously, not on annual survey cycles.</p>
<div style="margin-top:14px;font-size:13px;font-weight:700;color:#0066ff;">Learn more &rarr;</div>
@@ -270,16 +270,16 @@
<div class="conflict-grid">
<div class="conflict-side conflict-official">
<div class="conflict-side-label">Official narrative</div>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; LDC vacancy rate: 11.3%</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; Footfall index: improving trend</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; BID health assessment: stable</li></ul>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 LDC vacancy rate: 11.3%</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 Footfall index: improving trend</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 BID health assessment: stable</li></ul>
</div>
<div class="conflict-side conflict-observed">
<div class="conflict-side-label">Observed reality</div>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Active structural vacancy on 19 of 74 units — effective rate 25.7%</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; 7 pop-up, temporary, and charity units offsetting structural vacancy</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Three former anchor units subdivided or at reduced rental value</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Public realm maintenance below investment-grade standard on main frontage</li></ul>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Active structural vacancy on 19 of 74 units — effective rate 25.7%</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 7 pop-up, temporary, and charity units offsetting structural vacancy</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Three former anchor units subdivided or at reduced rental value</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Public realm maintenance below investment-grade standard on main frontage</li></ul>
</div>
</div>
<div class="conflict-footer">
@@ -138,7 +138,7 @@
.evidence-card p { font-size: 14px; color: var(--text-dim); line-height: 1.6; }
.cta-banner {
background: #e8f0fe; border-radius: 16px; padding: 40px 48px;
background: #e8f0fe; border-radius: var(--radius-lg); padding: 40px 48px;
display: flex; align-items: center; gap: 48px; flex-wrap: wrap; margin-top: 48px;
}
.cta-banner h3 { color: #1d1d1f; font-size: 1.25rem; font-weight: 800; margin-bottom: 8px; }
@@ -184,13 +184,13 @@
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social {
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px;
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md);
display: flex; align-items: center; justify-content: center;
transition: border-color 0.2s, background 0.2s;
}
.footer-social:hover { border-color: rgba(0,0,0,0.25); background: rgba(0,0,0,0.04); }
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius:8px;line-height:1.6;max-width:800px}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius: var(--radius-md);line-height:1.6;max-width:800px}
.lv-disclaimer a{color:rgba(0,0,0,.65);text-decoration:underline}
@media (max-width: 900px) {
@@ -69,16 +69,16 @@
.decisions-bg { background: var(--surface); }
.decisions-grid { display: grid; grid-template-columns: repeat(3,1fr); gap: 16px; margin-top: 40px; }
.decision-chip { background: var(--bg-dark); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 18px 22px; display: flex; align-items: center; gap: 12px; }
.decision-chip .chip-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--blue); flex-shrink: 0; }
.decision-chip .chip-dot { width: 8px; height: 8px; border-radius: var(--radius-full); background: var(--blue); flex-shrink: 0; }
.decision-chip span { font-size: 14px; font-weight: 600; color: var(--text-light); }
.contradiction-bg { background: #e8f0fe; }
.contradiction-box { background: rgba(0,0,0,.05); border: 1px solid rgba(0,0,0,.1); border-radius: 16px; padding: 36px 40px; margin-bottom: 32px; }
.contradiction-box { background: rgba(0,0,0,.05); border: 1px solid rgba(0,0,0,.1); border-radius: var(--radius-lg); padding: 36px 40px; margin-bottom: 32px; }
.conflict-header { display: flex; align-items: flex-start; gap: 16px; margin-bottom: 24px; }
.conflict-flag { font-size: 1.5rem; color: #f87171; }
.conflict-title { color: #f87171; font-weight: 800; font-size: .9375rem; margin-bottom: 4px; }
.conflict-desc { color: rgba(0,0,0,.6); font-size: .875rem; line-height: 1.6; }
.conflict-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.conflict-side { border-radius: 10px; padding: 16px; }
.conflict-side { border-radius: var(--radius-md); padding: 16px; }
.conflict-official { background: rgba(34,197,94,.1); border: 1px solid rgba(34,197,94,.2); }
.conflict-observed { background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.2); }
.conflict-side-label { font-size: .75rem; font-weight: 700; text-transform: uppercase; letter-spacing: .08em; margin-bottom: 10px; }
@@ -92,13 +92,13 @@
.breadcrumb a { color: var(--text-muted); }
.breadcrumb a:hover { color: #1d1d1f; }
.breadcrumb .sep { color: var(--border); }
.comparison-table { width: 100%; border-collapse: collapse; margin-top: 32px; border-radius: 12px; overflow: hidden; border: 1px solid rgba(0,0,0,0.08); }
.comparison-table { width: 100%; border-collapse: collapse; margin-top: 32px; border-radius: var(--radius-md); overflow: hidden; border: 1px solid rgba(0,0,0,0.08); }
.comparison-table th { padding: 14px 16px; font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .07em; text-align: left; background: rgba(0,0,0,0.04); color: var(--text-dim); }
.cta-section { background: #e8f0fe; }
.cta-box { border-radius: 16px; padding: 56px 48px; text-align: center; max-width: 680px; margin: 0 auto; }
.cta-box { border-radius: var(--radius-lg); padding: 56px 48px; text-align: center; max-width: 680px; margin: 0 auto; }
.cta-box h2 { font-size: clamp(24px,3vw,36px); font-weight: 800; color: #1d1d1f; margin-bottom: 16px; }
.cta-box p { font-size: 17px; color: rgba(0,0,0,.7); margin-bottom: 36px; line-height: 1.65; }
.lv-disclaimer { font-size:.75rem; color:rgba(0,0,0,.75); margin-top:24px; padding:12px 16px; background:rgba(0,0,0,.04); border-radius:8px; line-height:1.6; max-width:800px; }
.lv-disclaimer { font-size:.75rem; color:rgba(0,0,0,.75); margin-top:24px; padding:12px 16px; background:rgba(0,0,0,.04); border-radius: var(--radius-md); line-height:1.6; max-width:800px; }
.lv-disclaimer a { color:rgba(0,0,0,.65); text-decoration:underline; }
footer { border-top: 1px solid var(--border); padding: 40px; }
.footer-inner { max-width: 1140px; margin: 0 auto; display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 20px; }
@@ -108,7 +108,7 @@
.footer-links { display: flex; gap: 24px; align-items: center; flex-wrap: wrap; }
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social { width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px; display: flex; align-items: center; justify-content: center; transition: border-color 0.2s, background 0.2s; }
.footer-social { width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; transition: border-color 0.2s, background 0.2s; }
.footer-social:hover { border-color: rgba(0,0,0,0.25); background: rgba(0,0,0,0.04); }
@media (max-width:900px) { nav { padding:0 20px; } .use-case-grid { grid-template-columns:1fr; } .decisions-grid { grid-template-columns:1fr 1fr; } .conflict-grid { grid-template-columns:1fr; } }
@media (max-width:600px) { section { padding:72px 20px; } .decisions-grid { grid-template-columns:1fr; } nav { padding:0 16px; } .hero h1 { letter-spacing:-1px; } .contradiction-box { padding:24px 20px; } }
@@ -150,7 +150,7 @@
<p class="section-sub">Every engagement is anchored to a specific decision. Select a capability to discuss how Landvex applies it to your context.</p>
<div class="use-case-grid">
<a class="use-case-card" href="/contact/" style="display:block;text-decoration:none;">
<span class="uc-icon" style="font-size:22px;display:block;margin-bottom:16px;color:#5599ff;">&#9650;</span>
<span class="uc-icon" style="font-size:22px;display:block;margin-bottom:16px;color:#5599ff;">&#965050</span>
<h3>IQRN/IQROA Condition Pre-Screening</h3>
<p>Field observations structured to support IQRN and IQROA assessment workflows. Identify priority sections before formal survey deployment, enabling targeted use of CEREMA and DIR survey resources.</p>
<div style="margin-top:14px;font-size:13px;font-weight:700;color:#0066ff;">Learn more &rarr;</div>
@@ -270,16 +270,16 @@
<div class="conflict-grid">
<div class="conflict-side conflict-official">
<div class="conflict-side-label">Official narrative</div>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; IQROA survey: 20 months ago, score C</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; Departmental maintenance programme: within schedule</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; No emergency repair orders active</li></ul>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 IQROA survey: 20 months ago, score C</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 Departmental maintenance programme: within schedule</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 No emergency repair orders active</li></ul>
</div>
<div class="conflict-side conflict-observed">
<div class="conflict-side-label">Observed reality</div>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Longitudinal cracking on 4.2km inconsistent with C-score parameters</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Pothole density on 1.8km exceeds D-score threshold</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Drainage failures at 11 culverts outside maintenance programme</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Road marking fading below minimum retroreflectivity on 28km</li></ul>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Longitudinal cracking on 4.2km inconsistent with C-score parameters</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Pothole density on 1.8km exceeds D-score threshold</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Drainage failures at 11 culverts outside maintenance programme</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Road marking fading below minimum retroreflectivity on 28km</li></ul>
</div>
</div>
<div class="conflict-footer">
@@ -69,16 +69,16 @@
.decisions-bg { background: var(--surface); }
.decisions-grid { display: grid; grid-template-columns: repeat(3,1fr); gap: 16px; margin-top: 40px; }
.decision-chip { background: var(--bg-dark); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 18px 22px; display: flex; align-items: center; gap: 12px; }
.decision-chip .chip-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--blue); flex-shrink: 0; }
.decision-chip .chip-dot { width: 8px; height: 8px; border-radius: var(--radius-full); background: var(--blue); flex-shrink: 0; }
.decision-chip span { font-size: 14px; font-weight: 600; color: var(--text-light); }
.contradiction-bg { background: #e8f0fe; }
.contradiction-box { background: rgba(0,0,0,.05); border: 1px solid rgba(0,0,0,.1); border-radius: 16px; padding: 36px 40px; margin-bottom: 32px; }
.contradiction-box { background: rgba(0,0,0,.05); border: 1px solid rgba(0,0,0,.1); border-radius: var(--radius-lg); padding: 36px 40px; margin-bottom: 32px; }
.conflict-header { display: flex; align-items: flex-start; gap: 16px; margin-bottom: 24px; }
.conflict-flag { font-size: 1.5rem; color: #f87171; }
.conflict-title { color: #f87171; font-weight: 800; font-size: .9375rem; margin-bottom: 4px; }
.conflict-desc { color: rgba(0,0,0,.6); font-size: .875rem; line-height: 1.6; }
.conflict-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.conflict-side { border-radius: 10px; padding: 16px; }
.conflict-side { border-radius: var(--radius-md); padding: 16px; }
.conflict-official { background: rgba(34,197,94,.1); border: 1px solid rgba(34,197,94,.2); }
.conflict-observed { background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.2); }
.conflict-side-label { font-size: .75rem; font-weight: 700; text-transform: uppercase; letter-spacing: .08em; margin-bottom: 10px; }
@@ -92,13 +92,13 @@
.breadcrumb a { color: var(--text-muted); }
.breadcrumb a:hover { color: #1d1d1f; }
.breadcrumb .sep { color: var(--border); }
.comparison-table { width: 100%; border-collapse: collapse; margin-top: 32px; border-radius: 12px; overflow: hidden; border: 1px solid rgba(0,0,0,0.08); }
.comparison-table { width: 100%; border-collapse: collapse; margin-top: 32px; border-radius: var(--radius-md); overflow: hidden; border: 1px solid rgba(0,0,0,0.08); }
.comparison-table th { padding: 14px 16px; font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .07em; text-align: left; background: rgba(0,0,0,0.04); color: var(--text-dim); }
.cta-section { background: #e8f0fe; }
.cta-box { border-radius: 16px; padding: 56px 48px; text-align: center; max-width: 680px; margin: 0 auto; }
.cta-box { border-radius: var(--radius-lg); padding: 56px 48px; text-align: center; max-width: 680px; margin: 0 auto; }
.cta-box h2 { font-size: clamp(24px,3vw,36px); font-weight: 800; color: #1d1d1f; margin-bottom: 16px; }
.cta-box p { font-size: 17px; color: rgba(0,0,0,.7); margin-bottom: 36px; line-height: 1.65; }
.lv-disclaimer { font-size:.75rem; color:rgba(0,0,0,.75); margin-top:24px; padding:12px 16px; background:rgba(0,0,0,.04); border-radius:8px; line-height:1.6; max-width:800px; }
.lv-disclaimer { font-size:.75rem; color:rgba(0,0,0,.75); margin-top:24px; padding:12px 16px; background:rgba(0,0,0,.04); border-radius: var(--radius-md); line-height:1.6; max-width:800px; }
.lv-disclaimer a { color:rgba(0,0,0,.65); text-decoration:underline; }
footer { border-top: 1px solid var(--border); padding: 40px; }
.footer-inner { max-width: 1140px; margin: 0 auto; display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 20px; }
@@ -108,7 +108,7 @@
.footer-links { display: flex; gap: 24px; align-items: center; flex-wrap: wrap; }
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social { width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px; display: flex; align-items: center; justify-content: center; transition: border-color 0.2s, background 0.2s; }
.footer-social { width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; transition: border-color 0.2s, background 0.2s; }
.footer-social:hover { border-color: rgba(0,0,0,0.25); background: rgba(0,0,0,0.04); }
@media (max-width:900px) { nav { padding:0 20px; } .use-case-grid { grid-template-columns:1fr; } .decisions-grid { grid-template-columns:1fr 1fr; } .conflict-grid { grid-template-columns:1fr; } }
@media (max-width:600px) { section { padding:72px 20px; } .decisions-grid { grid-template-columns:1fr; } nav { padding:0 16px; } .hero h1 { letter-spacing:-1px; } .contradiction-box { padding:24px 20px; } }
@@ -150,7 +150,7 @@
<p class="section-sub">Every engagement is anchored to a specific decision. Select a capability to discuss how Landvex applies it to your context.</p>
<div class="use-case-grid">
<a class="use-case-card" href="/contact/" style="display:block;text-decoration:none;">
<span class="uc-icon" style="font-size:22px;display:block;margin-bottom:16px;color:#5599ff;">&#9650;</span>
<span class="uc-icon" style="font-size:22px;display:block;margin-bottom:16px;color:#5599ff;">&#965050</span>
<h3>ZEB-Aligned Pavement Assessment</h3>
<p>Field observations structured to supplement ZEB survey data. Identify priority sections for Autobahn GmbH and Landesstrassenbehoerden between formal ZEB cycles — without deploying survey vehicles.</p>
<div style="margin-top:14px;font-size:13px;font-weight:700;color:#0066ff;">Learn more &rarr;</div>
@@ -270,16 +270,16 @@
<div class="conflict-grid">
<div class="conflict-side conflict-official">
<div class="conflict-side-label">Official narrative</div>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; ZEB survey: 31 months ago, PCR value 1.6</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; Maintenance programme: within ZEB cycle</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; No emergency orders active</li></ul>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 ZEB survey: 31 months ago, PCR value 1.6</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 Maintenance programme: within ZEB cycle</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 No emergency orders active</li></ul>
</div>
<div class="conflict-side conflict-observed">
<div class="conflict-side-label">Observed reality</div>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Rut depth exceeding ZEB trigger threshold on 5.4km</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Active cracking patterns consistent with PCR 2.5+ on three sections</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Safety barrier corrosion failure at six locations</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Leitpfosten damage on 12km unreported in maintenance register</li></ul>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Rut depth exceeding ZEB trigger threshold on 5.4km</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Active cracking patterns consistent with PCR 2.5+ on three sections</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Safety barrier corrosion failure at six locations</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Leitpfosten damage on 12km unreported in maintenance register</li></ul>
</div>
</div>
<div class="conflict-footer">
@@ -69,16 +69,16 @@
.decisions-bg { background: var(--surface); }
.decisions-grid { display: grid; grid-template-columns: repeat(3,1fr); gap: 16px; margin-top: 40px; }
.decision-chip { background: var(--bg-dark); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 18px 22px; display: flex; align-items: center; gap: 12px; }
.decision-chip .chip-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--blue); flex-shrink: 0; }
.decision-chip .chip-dot { width: 8px; height: 8px; border-radius: var(--radius-full); background: var(--blue); flex-shrink: 0; }
.decision-chip span { font-size: 14px; font-weight: 600; color: var(--text-light); }
.contradiction-bg { background: #e8f0fe; }
.contradiction-box { background: rgba(0,0,0,.05); border: 1px solid rgba(0,0,0,.1); border-radius: 16px; padding: 36px 40px; margin-bottom: 32px; }
.contradiction-box { background: rgba(0,0,0,.05); border: 1px solid rgba(0,0,0,.1); border-radius: var(--radius-lg); padding: 36px 40px; margin-bottom: 32px; }
.conflict-header { display: flex; align-items: flex-start; gap: 16px; margin-bottom: 24px; }
.conflict-flag { font-size: 1.5rem; color: #f87171; }
.conflict-title { color: #f87171; font-weight: 800; font-size: .9375rem; margin-bottom: 4px; }
.conflict-desc { color: rgba(0,0,0,.6); font-size: .875rem; line-height: 1.6; }
.conflict-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.conflict-side { border-radius: 10px; padding: 16px; }
.conflict-side { border-radius: var(--radius-md); padding: 16px; }
.conflict-official { background: rgba(34,197,94,.1); border: 1px solid rgba(34,197,94,.2); }
.conflict-observed { background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.2); }
.conflict-side-label { font-size: .75rem; font-weight: 700; text-transform: uppercase; letter-spacing: .08em; margin-bottom: 10px; }
@@ -92,13 +92,13 @@
.breadcrumb a { color: var(--text-muted); }
.breadcrumb a:hover { color: #1d1d1f; }
.breadcrumb .sep { color: var(--border); }
.comparison-table { width: 100%; border-collapse: collapse; margin-top: 32px; border-radius: 12px; overflow: hidden; border: 1px solid rgba(0,0,0,0.08); }
.comparison-table { width: 100%; border-collapse: collapse; margin-top: 32px; border-radius: var(--radius-md); overflow: hidden; border: 1px solid rgba(0,0,0,0.08); }
.comparison-table th { padding: 14px 16px; font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .07em; text-align: left; background: rgba(0,0,0,0.04); color: var(--text-dim); }
.cta-section { background: #e8f0fe; }
.cta-box { border-radius: 16px; padding: 56px 48px; text-align: center; max-width: 680px; margin: 0 auto; }
.cta-box { border-radius: var(--radius-lg); padding: 56px 48px; text-align: center; max-width: 680px; margin: 0 auto; }
.cta-box h2 { font-size: clamp(24px,3vw,36px); font-weight: 800; color: #1d1d1f; margin-bottom: 16px; }
.cta-box p { font-size: 17px; color: rgba(0,0,0,.7); margin-bottom: 36px; line-height: 1.65; }
.lv-disclaimer { font-size:.75rem; color:rgba(0,0,0,.75); margin-top:24px; padding:12px 16px; background:rgba(0,0,0,.04); border-radius:8px; line-height:1.6; max-width:800px; }
.lv-disclaimer { font-size:.75rem; color:rgba(0,0,0,.75); margin-top:24px; padding:12px 16px; background:rgba(0,0,0,.04); border-radius: var(--radius-md); line-height:1.6; max-width:800px; }
.lv-disclaimer a { color:rgba(0,0,0,.65); text-decoration:underline; }
footer { border-top: 1px solid var(--border); padding: 40px; }
.footer-inner { max-width: 1140px; margin: 0 auto; display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 20px; }
@@ -108,7 +108,7 @@
.footer-links { display: flex; gap: 24px; align-items: center; flex-wrap: wrap; }
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social { width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px; display: flex; align-items: center; justify-content: center; transition: border-color 0.2s, background 0.2s; }
.footer-social { width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; transition: border-color 0.2s, background 0.2s; }
.footer-social:hover { border-color: rgba(0,0,0,0.25); background: rgba(0,0,0,0.04); }
@media (max-width:900px) { nav { padding:0 20px; } .use-case-grid { grid-template-columns:1fr; } .decisions-grid { grid-template-columns:1fr 1fr; } .conflict-grid { grid-template-columns:1fr; } }
@media (max-width:600px) { section { padding:72px 20px; } .decisions-grid { grid-template-columns:1fr; } nav { padding:0 16px; } .hero h1 { letter-spacing:-1px; } .contradiction-box { padding:24px 20px; } }
@@ -150,7 +150,7 @@
<p class="section-sub">Every engagement is anchored to a specific decision. Select a capability to discuss how Landvex applies it to your context.</p>
<div class="use-case-grid">
<a class="use-case-card" href="/contact/" style="display:block;text-decoration:none;">
<span class="uc-icon" style="font-size:22px;display:block;margin-bottom:16px;color:#5599ff;">&#9650;</span>
<span class="uc-icon" style="font-size:22px;display:block;margin-bottom:16px;color:#5599ff;">&#965050</span>
<h3>NEN 2767 Road Condition Assessment</h3>
<p>Field observations structured to support NEN 2767 condition assessment workflows across the Dutch road network. Identify priority sections for Rijkswaterstaat and provincial authorities between formal inspection intervals.</p>
<div style="margin-top:14px;font-size:13px;font-weight:700;color:#0066ff;">Learn more &rarr;</div>
@@ -270,16 +270,16 @@
<div class="conflict-grid">
<div class="conflict-side conflict-official">
<div class="conflict-side-label">Official narrative</div>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; NEN 2767 inspection: 24 months ago, condition score 2</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; Provincial maintenance programme: within MJOP</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; No emergency repair orders active</li></ul>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 NEN 2767 inspection: 24 months ago, condition score 2</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 Provincial maintenance programme: within MJOP</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 No emergency repair orders active</li></ul>
</div>
<div class="conflict-side conflict-observed">
<div class="conflict-side-label">Observed reality</div>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Rut depth exceeding NEN 2767 score 3 threshold on 2.8km</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Edge cracking patterns consistent with score 3+ on three sections</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Drainage failure at 9 culverts outside MJOP schedule</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Fietspad surface deterioration above score 3 on 5.6km</li></ul>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Rut depth exceeding NEN 2767 score 3 threshold on 2.8km</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Edge cracking patterns consistent with score 3+ on three sections</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Drainage failure at 9 culverts outside MJOP schedule</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Fietspad surface deterioration above score 3 on 5.6km</li></ul>
</div>
</div>
<div class="conflict-footer">
@@ -69,16 +69,16 @@
.decisions-bg { background: var(--surface); }
.decisions-grid { display: grid; grid-template-columns: repeat(3,1fr); gap: 16px; margin-top: 40px; }
.decision-chip { background: var(--bg-dark); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 18px 22px; display: flex; align-items: center; gap: 12px; }
.decision-chip .chip-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--blue); flex-shrink: 0; }
.decision-chip .chip-dot { width: 8px; height: 8px; border-radius: var(--radius-full); background: var(--blue); flex-shrink: 0; }
.decision-chip span { font-size: 14px; font-weight: 600; color: var(--text-light); }
.contradiction-bg { background: #e8f0fe; }
.contradiction-box { background: rgba(0,0,0,.05); border: 1px solid rgba(0,0,0,.1); border-radius: 16px; padding: 36px 40px; margin-bottom: 32px; }
.contradiction-box { background: rgba(0,0,0,.05); border: 1px solid rgba(0,0,0,.1); border-radius: var(--radius-lg); padding: 36px 40px; margin-bottom: 32px; }
.conflict-header { display: flex; align-items: flex-start; gap: 16px; margin-bottom: 24px; }
.conflict-flag { font-size: 1.5rem; color: #f87171; }
.conflict-title { color: #f87171; font-weight: 800; font-size: .9375rem; margin-bottom: 4px; }
.conflict-desc { color: rgba(0,0,0,.6); font-size: .875rem; line-height: 1.6; }
.conflict-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.conflict-side { border-radius: 10px; padding: 16px; }
.conflict-side { border-radius: var(--radius-md); padding: 16px; }
.conflict-official { background: rgba(34,197,94,.1); border: 1px solid rgba(34,197,94,.2); }
.conflict-observed { background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.2); }
.conflict-side-label { font-size: .75rem; font-weight: 700; text-transform: uppercase; letter-spacing: .08em; margin-bottom: 10px; }
@@ -92,13 +92,13 @@
.breadcrumb a { color: var(--text-muted); }
.breadcrumb a:hover { color: #1d1d1f; }
.breadcrumb .sep { color: var(--border); }
.comparison-table { width: 100%; border-collapse: collapse; margin-top: 32px; border-radius: 12px; overflow: hidden; border: 1px solid rgba(0,0,0,0.08); }
.comparison-table { width: 100%; border-collapse: collapse; margin-top: 32px; border-radius: var(--radius-md); overflow: hidden; border: 1px solid rgba(0,0,0,0.08); }
.comparison-table th { padding: 14px 16px; font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .07em; text-align: left; background: rgba(0,0,0,0.04); color: var(--text-dim); }
.cta-section { background: #e8f0fe; }
.cta-box { border-radius: 16px; padding: 56px 48px; text-align: center; max-width: 680px; margin: 0 auto; }
.cta-box { border-radius: var(--radius-lg); padding: 56px 48px; text-align: center; max-width: 680px; margin: 0 auto; }
.cta-box h2 { font-size: clamp(24px,3vw,36px); font-weight: 800; color: #1d1d1f; margin-bottom: 16px; }
.cta-box p { font-size: 17px; color: rgba(0,0,0,.7); margin-bottom: 36px; line-height: 1.65; }
.lv-disclaimer { font-size:.75rem; color:rgba(0,0,0,.75); margin-top:24px; padding:12px 16px; background:rgba(0,0,0,.04); border-radius:8px; line-height:1.6; max-width:800px; }
.lv-disclaimer { font-size:.75rem; color:rgba(0,0,0,.75); margin-top:24px; padding:12px 16px; background:rgba(0,0,0,.04); border-radius: var(--radius-md); line-height:1.6; max-width:800px; }
.lv-disclaimer a { color:rgba(0,0,0,.65); text-decoration:underline; }
footer { border-top: 1px solid var(--border); padding: 40px; }
.footer-inner { max-width: 1140px; margin: 0 auto; display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 20px; }
@@ -108,7 +108,7 @@
.footer-links { display: flex; gap: 24px; align-items: center; flex-wrap: wrap; }
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social { width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px; display: flex; align-items: center; justify-content: center; transition: border-color 0.2s, background 0.2s; }
.footer-social { width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; transition: border-color 0.2s, background 0.2s; }
.footer-social:hover { border-color: rgba(0,0,0,0.25); background: rgba(0,0,0,0.04); }
@media (max-width:900px) { nav { padding:0 20px; } .use-case-grid { grid-template-columns:1fr; } .decisions-grid { grid-template-columns:1fr 1fr; } .conflict-grid { grid-template-columns:1fr; } }
@media (max-width:600px) { section { padding:72px 20px; } .decisions-grid { grid-template-columns:1fr; } nav { padding:0 16px; } .hero h1 { letter-spacing:-1px; } .contradiction-box { padding:24px 20px; } }
@@ -150,7 +150,7 @@
<p class="section-sub">Every engagement is anchored to a specific decision. Select a capability to discuss how Landvex applies it to your context.</p>
<div class="use-case-grid">
<a class="use-case-card" href="/contact/" style="display:block;text-decoration:none;">
<span class="uc-icon" style="font-size:22px;display:block;margin-bottom:16px;color:#5599ff;">&#9650;</span>
<span class="uc-icon" style="font-size:22px;display:block;margin-bottom:16px;color:#5599ff;">&#965050</span>
<h3>Pavement Condition Assessment</h3>
<p>Systematic documentation of Swedish road surface conditions: cracking, rutting, surface defects, and IRI indicators across national and municipal networks. AI-reviewed, geo-tagged, and delivered as structured data.</p>
<div style="margin-top:14px;font-size:13px;font-weight:700;color:#0066ff;">Learn more &rarr;</div>
@@ -270,16 +270,16 @@
<div class="conflict-grid">
<div class="conflict-side conflict-official">
<div class="conflict-side-label">Official narrative</div>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; VTI road condition survey: 13 months ago, IRI 2.1</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; Maintenance programme: within standard</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; No emergency interventions logged</li></ul>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 VTI road condition survey: 13 months ago, IRI 2.1</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 Maintenance programme: within standard</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 No emergency interventions logged</li></ul>
</div>
<div class="conflict-side conflict-observed">
<div class="conflict-side-label">Observed reality</div>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Transverse cracking on 6.8km not captured in last VTI survey</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Rutting exceeding Swedish standard on two 1.2km sections</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Gully blockages at 17 locations outside maintenance schedule</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Road marking retroreflectivity below NVV threshold on 14km</li></ul>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Transverse cracking on 6.8km not captured in last VTI survey</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Rutting exceeding Swedish standard on two 1.2km sections</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Gully blockages at 17 locations outside maintenance schedule</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Road marking retroreflectivity below NVV threshold on 14km</li></ul>
</div>
</div>
<div class="conflict-footer">
@@ -69,16 +69,16 @@
.decisions-bg { background: var(--surface); }
.decisions-grid { display: grid; grid-template-columns: repeat(3,1fr); gap: 16px; margin-top: 40px; }
.decision-chip { background: var(--bg-dark); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 18px 22px; display: flex; align-items: center; gap: 12px; }
.decision-chip .chip-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--blue); flex-shrink: 0; }
.decision-chip .chip-dot { width: 8px; height: 8px; border-radius: var(--radius-full); background: var(--blue); flex-shrink: 0; }
.decision-chip span { font-size: 14px; font-weight: 600; color: var(--text-light); }
.contradiction-bg { background: #e8f0fe; }
.contradiction-box { background: rgba(0,0,0,.05); border: 1px solid rgba(0,0,0,.1); border-radius: 16px; padding: 36px 40px; margin-bottom: 32px; }
.contradiction-box { background: rgba(0,0,0,.05); border: 1px solid rgba(0,0,0,.1); border-radius: var(--radius-lg); padding: 36px 40px; margin-bottom: 32px; }
.conflict-header { display: flex; align-items: flex-start; gap: 16px; margin-bottom: 24px; }
.conflict-flag { font-size: 1.5rem; color: #f87171; }
.conflict-title { color: #f87171; font-weight: 800; font-size: .9375rem; margin-bottom: 4px; }
.conflict-desc { color: rgba(0,0,0,.6); font-size: .875rem; line-height: 1.6; }
.conflict-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.conflict-side { border-radius: 10px; padding: 16px; }
.conflict-side { border-radius: var(--radius-md); padding: 16px; }
.conflict-official { background: rgba(34,197,94,.1); border: 1px solid rgba(34,197,94,.2); }
.conflict-observed { background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.2); }
.conflict-side-label { font-size: .75rem; font-weight: 700; text-transform: uppercase; letter-spacing: .08em; margin-bottom: 10px; }
@@ -92,13 +92,13 @@
.breadcrumb a { color: var(--text-muted); }
.breadcrumb a:hover { color: #1d1d1f; }
.breadcrumb .sep { color: var(--border); }
.comparison-table { width: 100%; border-collapse: collapse; margin-top: 32px; border-radius: 12px; overflow: hidden; border: 1px solid rgba(0,0,0,0.08); }
.comparison-table { width: 100%; border-collapse: collapse; margin-top: 32px; border-radius: var(--radius-md); overflow: hidden; border: 1px solid rgba(0,0,0,0.08); }
.comparison-table th { padding: 14px 16px; font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .07em; text-align: left; background: rgba(0,0,0,0.04); color: var(--text-dim); }
.cta-section { background: #e8f0fe; }
.cta-box { border-radius: 16px; padding: 56px 48px; text-align: center; max-width: 680px; margin: 0 auto; }
.cta-box { border-radius: var(--radius-lg); padding: 56px 48px; text-align: center; max-width: 680px; margin: 0 auto; }
.cta-box h2 { font-size: clamp(24px,3vw,36px); font-weight: 800; color: #1d1d1f; margin-bottom: 16px; }
.cta-box p { font-size: 17px; color: rgba(0,0,0,.7); margin-bottom: 36px; line-height: 1.65; }
.lv-disclaimer { font-size:.75rem; color:rgba(0,0,0,.75); margin-top:24px; padding:12px 16px; background:rgba(0,0,0,.04); border-radius:8px; line-height:1.6; max-width:800px; }
.lv-disclaimer { font-size:.75rem; color:rgba(0,0,0,.75); margin-top:24px; padding:12px 16px; background:rgba(0,0,0,.04); border-radius: var(--radius-md); line-height:1.6; max-width:800px; }
.lv-disclaimer a { color:rgba(0,0,0,.65); text-decoration:underline; }
footer { border-top: 1px solid var(--border); padding: 40px; }
.footer-inner { max-width: 1140px; margin: 0 auto; display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 20px; }
@@ -108,7 +108,7 @@
.footer-links { display: flex; gap: 24px; align-items: center; flex-wrap: wrap; }
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social { width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px; display: flex; align-items: center; justify-content: center; transition: border-color 0.2s, background 0.2s; }
.footer-social { width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; transition: border-color 0.2s, background 0.2s; }
.footer-social:hover { border-color: rgba(0,0,0,0.25); background: rgba(0,0,0,0.04); }
@media (max-width:900px) { nav { padding:0 20px; } .use-case-grid { grid-template-columns:1fr; } .decisions-grid { grid-template-columns:1fr 1fr; } .conflict-grid { grid-template-columns:1fr; } }
@media (max-width:600px) { section { padding:72px 20px; } .decisions-grid { grid-template-columns:1fr; } nav { padding:0 16px; } .hero h1 { letter-spacing:-1px; } .contradiction-box { padding:24px 20px; } }
@@ -150,7 +150,7 @@
<p class="section-sub">Every engagement is anchored to a specific decision. Select a capability to discuss how Landvex applies it to your context.</p>
<div class="use-case-grid">
<a class="use-case-card" href="/contact/" style="display:block;text-decoration:none;">
<span class="uc-icon" style="font-size:22px;display:block;margin-bottom:16px;color:#5599ff;">&#9650;</span>
<span class="uc-icon" style="font-size:22px;display:block;margin-bottom:16px;color:#5599ff;">&#965050</span>
<h3>SCANNER and CVI Pre-Screening</h3>
<p>Field observations structured to support SCANNER, SCRIM, and CVI inspection workflows. Identify priority sections before formal survey deployment, enabling targeted allocation of highway inspection resources.</p>
<div style="margin-top:14px;font-size:13px;font-weight:700;color:#0066ff;">Learn more &rarr;</div>
@@ -270,16 +270,16 @@
<div class="conflict-grid">
<div class="conflict-side conflict-official">
<div class="conflict-side-label">Official narrative</div>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; SCANNER survey: 16 months ago, RCI green</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; Highway maintenance programme: within budget</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10003; No section 38 emergency repair orders</li></ul>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 SCANNER survey: 16 months ago, RCI green</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 Highway maintenance programme: within budget</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100003 No section 38 emergency repair orders</li></ul>
</div>
<div class="conflict-side conflict-observed">
<div class="conflict-side-label">Observed reality</div>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Carriageway cracking at 15 locations exceeding CVI defect threshold</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Edge deterioration on 9.2km inconsistent with green RCI rating</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Gully blockages at 31 locations outside maintenance schedule</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#10007; Road marking retroreflectivity below DMRB threshold on 17km</li></ul>
<ul><li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Carriageway cracking at 15 locations exceeding CVI defect threshold</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Edge deterioration on 9.2km inconsistent with green RCI rating</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Gully blockages at 31 locations outside maintenance schedule</li>
<li style='color:rgba(0,0,0,.75);font-size:.875rem;padding:4px 0;'>&#100007 Road marking retroreflectivity below DMRB threshold on 17km</li></ul>
</div>
</div>
<div class="conflict-footer">
@@ -180,7 +180,7 @@
display: flex; align-items: center; gap: 12px;
}
.decision-chip .chip-dot {
width: 8px; height: 8px; border-radius: 50%;
width: 8px; height: 8px; border-radius: var(--radius-full);
background: var(--blue); flex-shrink: 0;
}
.decision-chip span { font-size: 15px; font-weight: 600; color: var(--text-light); }
@@ -191,7 +191,7 @@
.contradiction-box {
background: rgba(0,0,0,.05);
border: 1px solid rgba(0,0,0,.1);
border-radius: 16px; padding: 36px 40px; margin-bottom: 32px;
border-radius: var(--radius-lg); padding: 36px 40px; margin-bottom: 32px;
}
.conflict-header {
@@ -203,7 +203,7 @@
.conflict-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.conflict-side { border-radius: 10px; padding: 16px; }
.conflict-side { border-radius: var(--radius-md); padding: 16px; }
.conflict-official { background: rgba(34,197,94,.1); border: 1px solid rgba(34,197,94,.2); }
.conflict-observed { background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.2); }
@@ -242,14 +242,14 @@
.score-icon { font-size: 1.5rem; margin-bottom: 12px; }
.score-name { font-size: 15px; font-weight: 700; color: #1d1d1f; margin-bottom: 6px; }
.score-desc { font-size: 13px; color: var(--text-dim); line-height: 1.5; margin-bottom: 20px; }
.score-bar-wrap { background: rgba(0,0,0,.06); border-radius: 6px; height: 8px; margin-bottom: 10px; }
.score-bar { height: 8px; border-radius: 6px; background: linear-gradient(90deg, var(--blue), #5599ff); }
.score-bar-wrap { background: rgba(0,0,0,.06); border-radius: var(--radius-sm); height: 8px; margin-bottom: 10px; }
.score-bar { height: 8px; border-radius: var(--radius-sm); background: linear-gradient(90deg, var(--blue), #5599ff); }
.score-value { font-size: 28px; font-weight: 800; color: var(--blue); letter-spacing: -1px; }
.score-label-sub { font-size: 11px; color: var(--text-dim); margin-top: 2px; }
/* CTA BANNER */
.cta-banner {
background: #e8f0fe; border-radius: 16px; padding: 40px 48px;
background: #e8f0fe; border-radius: var(--radius-lg); padding: 40px 48px;
display: flex; align-items: center; gap: 48px; flex-wrap: wrap;
margin-top: 48px;
}
@@ -301,7 +301,7 @@
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social {
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px;
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md);
display: flex; align-items: center; justify-content: center;
transition: border-color 0.2s, background 0.2s;
}
@@ -329,7 +329,7 @@
.hero h1 { letter-spacing: -1px; }
}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius:8px;line-height:1.6;max-width:800px}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius: var(--radius-md);line-height:1.6;max-width:800px}
.lv-disclaimer a{color:rgba(0,0,0,.65);text-decoration:underline}
.lv-disclaimer--light{color:#888;background:#f5f5f5}
.lv-disclaimer--light a{color:#555}
@@ -378,7 +378,7 @@
<p class="section-sub">From single-asset verifications to portfolio-wide intelligence missions — deployed in 2472 hours.</p>
<div class="use-case-grid">
<div class="use-case-card" style="cursor:pointer;" onclick="location.href='#contact'" role="button" tabindex="0">
<span class="uc-icon">🔭</span>
<span class="uc-icon"></span>
<h3>Bridge inspections</h3>
<p>Rapid visual surveys of bridge structures, deck conditions, joints, and approaches — geo-tagged, AI-reviewed, and delivered within 72 hours.</p>
@@ -390,13 +390,13 @@
<div class="learn-more-hint" style="margin-top:12px;font-size:13px;font-weight:700;color:#0066ff">Learn more →</div></div>
<div class="use-case-card" style="cursor:pointer;" onclick="location.href='#contact'" role="button" tabindex="0">
<span class="uc-icon">🗺️</span>
<span class="uc-icon"></span>
<h3>Utility corridor mapping</h3>
<p>Visual documentation of above-ground utility infrastructure, access points, vegetation encroachment, and condition indicators.</p>
<div class="learn-more-hint" style="margin-top:12px;font-size:13px;font-weight:700;color:#0066ff">Learn more →</div></div>
<div class="use-case-card" style="cursor:pointer;" onclick="location.href='#contact'" role="button" tabindex="0">
<span class="uc-icon"></span>
<span class="uc-icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M9 2L5 9H8L7 14L11 7H8L9 2Z" fill="#f59e0b"/></svg></span>
<h3>Network monitoring</h3>
<p>Continuous or periodic monitoring of distributed infrastructure assets to detect changes, incidents, and deterioration trends.</p>
@@ -457,12 +457,12 @@
<div class="conflict-side conflict-official">
<div class="conflict-side-label">Official narrative</div>
<ul>
<li>&#10003; Last inspection: 18 months ago — passed</li><li>&#10003; Maintenance schedule: on track</li><li>&#10003; No incidents logged Q1Q2</li> </ul>
<li>&#100003 Last inspection: 18 months ago — passed</li><li>&#100003 Maintenance schedule: on track</li><li>&#100003 No incidents logged Q1Q2</li> </ul>
</div>
<div class="conflict-side conflict-observed">
<div class="conflict-side-label">Observed reality</div>
<ul>
<li>&#10007; Visible surface deterioration on 3 bridge spans</li><li>&#10007; Drainage failure on 2 critical junctions</li><li>&#10007; Vegetation encroachment at 6 utility access points</li><li>&#10007; Crack propagation exceeds tolerance on segment 4B</li> </ul>
<li>&#100007 <svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Visible surface deterioration on 3 bridge spans</li><li>&#100007 <svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Drainage failure on 2 critical junctions</li><li>&#100007 <svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Vegetation encroachment at 6 utility access points</li><li>&#100007 <svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Crack propagation exceeds tolerance on segment 4B</li> </ul>
</div>
</div>
<div class="conflict-footer">
@@ -499,7 +499,7 @@
<p class="section-sub">Every score is derived from field-verified observations, cross-referenced against official data, and launching August 2026.</p>
<div class="scores-grid" style="grid-template-columns:repeat(2,1fr)">
<div class="score-card" style="cursor:pointer;" onclick="location.href='#contact'" role="button" tabindex="0">
<div class="score-icon">🔧</div>
<div class="score-icon"></div>
<div class="score-name">Infrastructure Risk Index</div>
<div class="score-desc">Identifies areas where physical infrastructure is deteriorating faster than maintenance can address.</div>
<div class="score-bar-wrap">
@@ -510,7 +510,7 @@
<div class="learn-more-hint" style="margin-top:12px;font-size:13px;font-weight:700;color:#0066ff">Request access →</div></div>
<div class="score-card" style="cursor:pointer;" onclick="location.href='#contact'" role="button" tabindex="0">
<div class="score-icon">🚀</div>
<div class="score-icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M8 2L10 8L14 9L8 14L6 8L2 7L8 2Z" fill="#666"/></svg></div>
<div class="score-name">Growth Velocity Index</div>
<div class="score-desc">How fast is an area changing? Identifies emerging zones before they appear in official statistics.</div>
<div class="score-bar-wrap">
+55 -653
View File
@@ -5,689 +5,91 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Insurance Risk Intelligence — Landvex</title>
<meta name="description" content="Risk intelligence for insurance companies. Identify elevated-risk areas, validate claims, and monitor physical conditions continuously.">
<link rel="canonical" href="https://www.landvex.com/verticals/insurance/">
<meta property="og:type" content="website">
<meta property="og:url" content="https://www.landvex.com/verticals/insurance/">
<meta property="og:title" content="Insurance Risk Intelligence — Landvex">
<meta property="og:description" content="Risk intelligence for insurance companies. Identify elevated-risk areas, validate claims, and monitor physical conditions continuously.">
<meta property="og:image" content="https://www.landvex.com/og-image.jpg">
<meta name="twitter:card" content="summary_large_image">
<link rel="canonical" href="https://landvex.com/verticals/insurance/">
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
<link rel="icon" type="image/x-icon" href="/favicon.ico">
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--bg-dark: #f5f5f7;
--blue: #0066FF;
--blue-dark: #0052CC;
--blue-glow: rgba(0, 102, 255, 0.15);
--text-dark: #0a0a0a;
--text-body: #3a3a3a;
--text-muted: #6B6B6B;
--text-light: #1d1d1f;
--text-dim: #6B6B6B;
--surface: #ffffff;
--surface-2: #f5f5f7;
--border: rgba(0,0,0,0.08);
--radius-sm: 8px;
--radius-md: 14px;
--radius-lg: 24px;
--bg: #f5f5f7; --blue: #0066FF; --blue-dark: #0052CC;
--text: #1d1d1f; --text-body: #3a3a3a; --text-muted: #6e6e73;
--surface: #ffffff; --border: rgba(0,0,0,0.08);
--radius: 14px;
}
html { scroll-behavior: smooth; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', 'Helvetica Neue', sans-serif;
background: var(--bg-dark);
color: var(--text-light);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; background: var(--bg); color: var(--text); line-height: 1.6; }
a { color: inherit; text-decoration: none; }
nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 40px; height: 64px;
background: rgba(10,10,10,0.88);
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
border-bottom: 1px solid var(--border);
}
.nav-logo {
font-size: 18px;
font-weight: 700;
letter-spacing: -0.3px;
color: #1d1d1f; }
.btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 10px 22px;
background: var(--blue); color: #1d1d1f;
font-size: 14px; font-weight: 600;
border-radius: var(--radius-md);
border: none; cursor: pointer;
transition: background 0.2s, transform 0.15s;
}
.btn:hover { background: var(--blue-dark); transform: translateY(-1px); }
.btn-outline {
background: transparent;
border: 1.5px solid rgba(0,0,0,0.18);
color: var(--text-light);
}
.btn-outline:hover { background: rgba(0,0,0,0.04); transform: translateY(-1px); }
.btn-lg { padding: 14px 30px; font-size: 16px; border-radius: var(--radius-md); }
.hero {
min-height: 90vh;
display: flex; flex-direction: column; align-items: center; justify-content: center;
text-align: center;
padding: 120px 24px 80px;
position: relative; overflow: hidden;
}
.hero::before {
content: '';
position: absolute;
top: -200px; left: 50%; transform: translateX(-50%);
width: 900px; height: 600px;
background: radial-gradient(ellipse at center, rgba(0,102,255,0.12) 0%, transparent 70%);
pointer-events: none;
}
.hero-eyebrow {
display: inline-flex; align-items: center; gap: 8px;
background: rgba(0,102,255,0.1);
border: 1px solid rgba(0,102,255,0.25);
color: #5599ff;
font-size: 12px; font-weight: 600; letter-spacing: 0.08em;
text-transform: uppercase;
padding: 6px 16px; border-radius: 100px;
margin-bottom: 28px;
}
.hero h1 {
font-size: clamp(36px, 6vw, 68px);
font-weight: 800; letter-spacing: -2px; line-height: 1.05;
color: #1d1d1f; max-width: 820px; margin-bottom: 24px;
}
.hero-sub {
font-size: clamp(16px, 2vw, 19px);
color: var(--text-dim); max-width: 620px; line-height: 1.65;
margin-bottom: 40px;
}
.hero-ctas { display: flex; gap: 14px; flex-wrap: wrap; justify-content: center; }
section { padding: 96px 24px; }
nav { position: fixed; top: 0; left: 0; right: 0; z-index: 100; display: flex; align-items: center; justify-content: space-between; padding: 0 40px; height: 64px; background: rgba(255,255,255,0.9); backdrop-filter: blur(16px); border-bottom: 1px solid var(--border); }
.nav-logo { font-size: 18px; font-weight: 700; color: var(--text); }
.btn { display: inline-flex; align-items: center; gap: 6px; padding: 10px 22px; background: var(--blue); color: #fff; font-size: 14px; font-weight: 600; border-radius: var(--radius); border: none; cursor: pointer; transition: background 0.2s; }
.btn:hover { background: var(--blue-dark); }
.btn-outline { background: transparent; border: 1.5px solid var(--border); color: var(--text); }
.btn-outline:hover { background: rgba(0,0,0,0.04); }
.hero { min-height: 70vh; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; padding: 120px 24px 80px; }
.hero h1 { font-size: clamp(32px, 5vw, 56px); font-weight: 800; letter-spacing: -1.5px; margin-bottom: 20px; }
.hero p { font-size: 18px; color: var(--text-muted); max-width: 600px; margin-bottom: 32px; }
section { padding: 80px 24px; }
.container { max-width: 1140px; margin: 0 auto; }
.section-label {
font-size: 12px; font-weight: 700; letter-spacing: 0.1em;
text-transform: uppercase; color: var(--blue); margin-bottom: 16px;
}
.section-title {
font-size: clamp(28px, 4vw, 44px);
font-weight: 800; letter-spacing: -1.2px; line-height: 1.1;
color: #1d1d1f; margin-bottom: 16px;
}
.section-sub {
font-size: 17px; color: var(--text-dim);
max-width: 600px; line-height: 1.7;
}
/* Light section overrides */
.light-section { background: #fff; }
.light-section .section-title { color: #0a0a0a; }
.light-section .section-sub { color: #444; }
.light-section .section-label { color: var(--blue); }
/* USE CASES */
.use-case-grid {
display: grid; grid-template-columns: repeat(2, 1fr); gap: 20px;
margin-top: 48px;
}
.use-case-card {
background: var(--surface-2); border: 1px solid var(--border);
border-radius: var(--radius-lg); padding: 32px 28px;
transition: border-color 0.25s, transform 0.2s;
}
.use-case-card:hover { border-color: rgba(0,102,255,0.35); transform: translateY(-3px); }
.uc-icon { font-size: 28px; margin-bottom: 16px; display: block; }
.use-case-card h3 { font-size: 17px; font-weight: 700; color: #1d1d1f; margin-bottom: 8px; }
.use-case-card p { font-size: 14px; color: var(--text-dim); line-height: 1.6; }
/* DECISIONS */
.decisions-bg { background: var(--surface); }
.decisions-grid {
display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px;
margin-top: 40px;
}
.decision-chip {
background: var(--bg-dark); border: 1px solid var(--border);
border-radius: var(--radius-md); padding: 20px 24px;
display: flex; align-items: center; gap: 12px;
}
.decision-chip .chip-dot {
width: 8px; height: 8px; border-radius: 50%;
background: var(--blue); flex-shrink: 0;
}
.decision-chip span { font-size: 15px; font-weight: 600; color: var(--text-light); }
/* CONTRADICTION ENGINE */
.contradiction-bg { background: #e8f0fe; }
.contradiction-box {
background: rgba(0,0,0,.05);
border: 1px solid rgba(0,0,0,.1);
border-radius: 16px; padding: 36px 40px; margin-bottom: 32px;
}
.conflict-header {
display: flex; align-items: flex-start; gap: 16px; margin-bottom: 24px;
}
.conflict-flag { font-size: 1.5rem; }
.conflict-title { color: #f87171; font-weight: 800; font-size: .9375rem; margin-bottom: 4px; }
.conflict-desc { color: rgba(0,0,0,.8); font-size: .875rem; line-height: 1.6; }
.conflict-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.conflict-side { border-radius: 10px; padding: 16px; }
.conflict-official { background: rgba(34,197,94,.1); border: 1px solid rgba(34,197,94,.2); }
.conflict-observed { background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.2); }
.conflict-side-label {
font-size: .75rem; font-weight: 700; text-transform: uppercase;
letter-spacing: .08em; margin-bottom: 10px;
}
.conflict-official .conflict-side-label { color: #86efac; }
.conflict-observed .conflict-side-label { color: #fca5a5; }
.conflict-side ul { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 6px; }
.conflict-side li { color: rgba(0,0,0,.7); font-size: .875rem; }
.conflict-footer {
margin-top: 20px; padding-top: 20px;
border-top: 1px solid rgba(0,0,0,.1);
display: flex; justify-content: space-between; align-items: center;
}
.conflict-footer-label { color: rgba(0,0,0,.75); font-size: .8125rem; }
.conflict-confidence { color: #f87171; font-weight: 800; font-size: 1.125rem; }
.ce-features { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; }
.ce-feature { text-align: center; padding: 20px; }
.ce-feature .ce-icon { font-size: 1.75rem; margin-bottom: 8px; }
.ce-feature h4 { color: #1d1d1f; font-weight: 700; margin-bottom: 6px; }
.ce-feature p { color: rgba(0,0,0,.75); font-size: .8125rem; }
/* SCORES */
.scores-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; margin-top: 40px; }
.score-card {
background: var(--surface-2); border: 1px solid var(--border);
border-radius: var(--radius-lg); padding: 28px 24px; text-align: center;
}
.score-icon { font-size: 1.5rem; margin-bottom: 12px; }
.score-name { font-size: 15px; font-weight: 700; color: #1d1d1f; margin-bottom: 6px; }
.score-desc { font-size: 13px; color: var(--text-dim); line-height: 1.5; margin-bottom: 20px; }
.score-bar-wrap { background: rgba(0,0,0,.06); border-radius: 6px; height: 8px; margin-bottom: 10px; }
.score-bar { height: 8px; border-radius: 6px; background: linear-gradient(90deg, var(--blue), #5599ff); }
.score-value { font-size: 28px; font-weight: 800; color: var(--blue); letter-spacing: -1px; }
.score-label-sub { font-size: 11px; color: var(--text-dim); margin-top: 2px; }
/* CTA BANNER */
.cta-banner {
background: #e8f0fe; border-radius: 16px; padding: 40px 48px;
display: flex; align-items: center; gap: 48px; flex-wrap: wrap;
margin-top: 48px;
}
.cta-banner h3 { color: #1d1d1f; font-size: 1.25rem; font-weight: 800; margin-bottom: 8px; }
.cta-banner p { color: rgba(0,0,0,.7); line-height: 1.65; font-size: .9375rem; }
.cta-banner-text { flex: 1; min-width: 260px; }
/* CONTACT FORM */
.contact-wrapper { max-width: 680px; margin: 0 auto; text-align: center; }
.contact-wrapper .section-title { margin-bottom: 10px; }
.contact-wrapper .section-sub { margin: 0 auto 48px; }
.contact-form {
background: var(--surface); border: 1px solid var(--border);
border-radius: var(--radius-lg); padding: 40px; text-align: left;
}
.form-group { display: flex; flex-direction: column; gap: 8px; margin-bottom: 16px; }
.form-group label { font-size: 13px; font-weight: 600; color: var(--text-dim); }
.form-group input, .form-group textarea {
background: var(--bg-dark); border: 1.5px solid var(--border);
border-radius: var(--radius-md); color: #1d1d1f;
font-size: 15px; font-family: inherit; padding: 12px 16px;
outline: none; transition: border-color 0.2s; width: 100%;
}
.form-group input:focus, .form-group textarea:focus { border-color: var(--blue); }
.form-group input::placeholder, .form-group textarea::placeholder { color: var(--text-muted); }
.form-group textarea { min-height: 120px; resize: vertical; }
.form-note { font-size: 12px; color: var(--text-muted); margin-top: 12px; text-align: center; }
#form-message {
display: none; padding: 14px 20px; border-radius: var(--radius-md);
font-size: 14px; font-weight: 600; text-align: center; margin-top: 16px;
}
#form-message.success { background: rgba(0,200,80,0.1); border: 1px solid rgba(0,200,80,0.25); color: #4ade80; }
#form-message.error { background: rgba(255,60,60,0.1); border: 1px solid rgba(255,60,60,0.25); color: #f87171; }
/* FOOTER */
footer { border-top: 1px solid var(--border); padding: 40px; }
.footer-inner {
max-width: 1140px; margin: 0 auto;
display: flex; align-items: center; justify-content: space-between;
flex-wrap: wrap; gap: 20px;
}
.footer-logo { font-size: 17px; font-weight: 700; color: #1d1d1f; }
.footer-copy { font-size: 13px; color: var(--text-muted); }
.footer-tagline { font-size: 12px; color: var(--text-muted); margin-top: 4px; }
.footer-links { display: flex; gap: 24px; align-items: center; flex-wrap: wrap; }
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social {
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px;
display: flex; align-items: center; justify-content: center;
transition: border-color 0.2s, background 0.2s;
}
.footer-social:hover { border-color: rgba(0,0,0,0.25); background: rgba(0,0,0,0.04); }
@media (max-width: 900px) {
.grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; margin-top: 40px; }
.card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 32px; transition: transform 0.2s, border-color 0.2s; }
.card:hover { transform: translateY(-2px); border-color: rgba(0,102,255,0.3); }
.card h3 { font-size: 18px; font-weight: 700; margin-bottom: 8px; }
.card p { font-size: 14px; color: var(--text-muted); line-height: 1.6; }
footer { border-top: 1px solid var(--border); padding: 40px; text-align: center; color: var(--text-muted); font-size: 14px; }
@media (max-width: 768px) {
.grid { grid-template-columns: 1fr; }
nav { padding: 0 20px; }
.use-case-grid { grid-template-columns: 1fr; }
.decisions-grid { grid-template-columns: 1fr 1fr; }
.scores-grid { grid-template-columns: 1fr 1fr; }
.conflict-grid { grid-template-columns: 1fr; }
.ce-features { grid-template-columns: 1fr; }
}
@media (max-width: 600px) {
section { padding: 48px 0 !important; }
.container { padding: 0 16px !important; box-sizing: border-box; }
.decisions-grid { grid-template-columns: 1fr !important; }
.scores-grid { grid-template-columns: 1fr !important; }
.use-case-grid { grid-template-columns: 1fr !important; }
.conflict-grid { grid-template-columns: 1fr !important; }
.ce-features { grid-template-columns: 1fr !important; }
.cta-banner { flex-direction: column !important; gap: 16px !important; padding: 28px 24px; }
.contact-form { padding: 24px 20px; }
.roi-grid { grid-template-columns: 1fr !important; }
.roi-grid > div { border-left: none !important; border-top: 1px solid rgba(0,0,0,.06) !important; }
.roi-grid > div:first-child { border-top: none !important; }
nav { padding: 0 16px; }
.hero h1 { letter-spacing: -1px; }
}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.7);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius:8px;line-height:1.6;max-width:800px}
.lv-disclaimer a{color:rgba(0,0,0,.8);text-decoration:underline}
.lv-disclaimer--light{color:#888;background:#f5f5f5}
.lv-disclaimer--light a{color:#555}
</style>
<meta name="robots" content="index, follow">
<link rel="alternate" hreflang="x-default" href="https://www.landvex.com/">
<link rel="alternate" hreflang="en" href="https://www.landvex.com/">
<link rel="alternate" hreflang="sv" href="https://landvex.se/">
<link rel="alternate" hreflang="de" href="https://landvex.de/">
<link rel="alternate" hreflang="fr" href="https://landvex.fr/">
<link rel="alternate" hreflang="en-GB" href="https://landvex.co.uk/">
<link rel="alternate" hreflang="it" href="https://landvex.it/">
<style>
@media (max-width: 640px) {
.hero h1 { font-size: 2rem !important; }
.hero p { font-size: 1rem !important; }
.card { padding: 1.5rem !important; }
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
</style>
</head>
<body>
<nav>
<a class="nav-logo" href="https://landvex.com/">&larr; Back to Landvex</a>
<a class="btn btn-outline" href="#contact">Get in touch &rarr;</a>
<a class="nav-logo" href="/"> Landvex</a>
<a class="btn btn-outline" href="/enterprise/">Request a pilot →</a>
</nav>
<!-- HERO -->
<section class="hero">
<div class="hero-eyebrow">Landvex &mdash; Insurance</div>
<h1>Which areas carry elevated risk?</h1>
<p class="hero-sub">Landvex gives insurers and risk managers field-verified intelligence to price risk accurately, validate claims, and monitor exposure — grounded in what's actually on the ground.</p>
<div class="hero-ctas">
<a class="btn btn-lg" href="#contact">Request a briefing &rarr;</a>
<a class="btn btn-outline btn-lg" href="#use-cases">See use cases</a>
<h1>Insurance Risk Intelligence</h1>
<p>Identify elevated-risk areas, validate claims, and monitor physical conditions continuously — with field-verified data you can trace to its source.</p>
<div style="display:flex;gap:12px;flex-wrap:wrap;justify-content:center;">
<a class="btn" href="/enterprise/">Request a briefing →</a>
<a class="btn btn-outline" href="/methodology/">How it works</a>
</div>
</section>
<!-- ROI SIGNAL -->
<section style="background:#fff;padding:64px 24px">
<div class="container" style="max-width:900px">
<div class="section-label" style="color:var(--blue)">Why it matters financially</div>
<h2 class="section-title" style="color:#0a0a0a;margin-bottom:12px">1% better loss ratio.<br>That&rsquo;s the target.</h2>
<p style="font-size:1.0625rem;color:#444;max-width:620px;line-height:1.7;margin-bottom:48px">A mid-sized insurer with &euro;500M gross written premium and a 68% loss ratio loses roughly &euro;340M to claims annually. A 1 percentage point improvement in underwriting accuracy is worth &euro;5M. Landvex sells field-verified ground truth &mdash; the input that makes that improvement possible.</p>
<div class="roi-grid" style="display:grid;grid-template-columns:repeat(3,1fr);gap:2px;background:rgba(0,0,0,.06);border-radius:14px;overflow:hidden">
<div style="background:#fff;padding:32px 28px">
<div style="font-size:2rem;font-weight:800;color:#0a0a0a;letter-spacing:-.04em;line-height:1">24h</div>
<div style="font-size:.8125rem;font-weight:600;color:#888;margin-top:6px;text-transform:uppercase;letter-spacing:.06em">Field data turnaround</div>
<p style="font-size:.875rem;color:#555;line-height:1.6;margin-top:10px">From mission launch to structured intelligence delivery. No adjuster scheduling. No site visit coordination.</p>
</div>
<div style="background:#fff;padding:32px 28px;border-left:1px solid rgba(0,0,0,.06)">
<div style="font-size:2rem;font-weight:800;color:#0a0a0a;letter-spacing:-.04em;line-height:1">&lt;&euro;15</div>
<div style="font-size:.8125rem;font-weight:600;color:#888;margin-top:6px;text-transform:uppercase;letter-spacing:.06em">Per asset observation</div>
<p style="font-size:.875rem;color:#555;line-height:1.6;margin-top:10px">Versus &euro;150&ndash;600 for a traditional field adjuster visit. AI-reviewed, geo-tagged, timestamped.</p>
</div>
<div style="background:#fff;padding:32px 28px;border-left:1px solid rgba(0,0,0,.06)">
<div style="font-size:2rem;font-weight:800;color:#0a0a0a;letter-spacing:-.04em;line-height:1">Aug&nbsp;2026</div>
<div style="font-size:.8125rem;font-weight:600;color:#888;margin-top:6px;text-transform:uppercase;letter-spacing:.06em">Pilot availability</div>
<p style="font-size:.875rem;color:#555;line-height:1.6;margin-top:10px">Limited pilot slots for insurers in Sweden and the Netherlands. Defined scope, defined price, defined deliverable.</p>
</div>
</div>
<p style="font-size:.75rem;color:#aaa;margin-top:16px;line-height:1.5">Loss ratio illustration based on publicly available industry benchmarks. Actual improvement depends on portfolio composition, geography and implementation. Not a performance guarantee.</p>
</div>
</section>
<!-- USE CASES -->
<section id="use-cases" style="background:var(--surface)">
<section>
<div class="container">
<div class="section-label">Use cases</div>
<h2 class="section-title">What Landvex does for Insurance</h2>
<p class="section-sub">From single-asset verifications to portfolio-wide intelligence missions — deployed in 2472 hours.</p>
<div class="use-case-grid">
<div class="use-case-card" style="cursor:pointer;" onclick="location.href='#contact'" role="button" tabindex="0">
<span class="uc-icon"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#0066FF" stroke-width="2"><path d="M10.065 12.493a6 6 0 0 1 3.432 3.432l5.618-5.618a2 2 0 0 0-2.828-2.828l-5.618 5.618z"/><path d="m16 6 4 4"/><path d="M2.5 21.5l3.5-3.5"/></svg></span>
<h3>Claims validation</h3>
<p>Rapid field verification of loss reports and claimed damage. Geo-tagged visual evidence delivered within 72 hours of instruction.</p>
<div class="learn-more-hint" style="margin-top:12px;font-size:13px;font-weight:700;color:#0066ff">Learn more →</div></div>
<div class="use-case-card" style="cursor:pointer;" onclick="location.href='#contact'" role="button" tabindex="0">
<span class="uc-icon"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#0066FF" stroke-width="2"><path d="M18 20V10"/><path d="M12 20V4"/><path d="M6 20v-6"/></svg></span>
<h3>Pre-loss surveys</h3>
<p>Baseline documentation of insured assets before loss events — establishing ground truth for future claims resolution.</p>
<div class="learn-more-hint" style="margin-top:12px;font-size:13px;font-weight:700;color:#0066ff">Learn more →</div></div>
<div class="use-case-card" style="cursor:pointer;" onclick="location.href='#contact'" role="button" tabindex="0">
<span class="uc-icon"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#0066FF" stroke-width="2"><polygon points="1 6 1 22 8 18 16 22 23 18 23 2 16 6 8 2 1 6"/><line x1="8" y1="2" x2="8" y2="18"/><line x1="16" y1="6" x2="16" y2="22"/></svg></span>
<h3>Risk assessment</h3>
<p>Field-observed physical condition data to supplement modelled risk — identifying exposures not visible in satellite or historical data.</p>
<div class="learn-more-hint" style="margin-top:12px;font-size:13px;font-weight:700;color:#0066ff">Learn more →</div></div>
<div class="use-case-card" style="cursor:pointer;" onclick="location.href='#contact'" role="button" tabindex="0">
<span class="uc-icon"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#0066FF" stroke-width="2"><path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/></svg></span>
<h3>Catastrophe monitoring</h3>
<p>Post-event rapid deployment to document affected areas and validate exposure across large geographies.</p>
<div class="learn-more-hint" style="margin-top:12px;font-size:13px;font-weight:700;color:#0066ff">Learn more →</div></div>
</div>
</div>
</section>
<!-- KEY DECISIONS -->
<section class="decisions-bg" id="key-decisions">
<div class="container">
<div class="section-label">Decision support</div>
<h2 class="section-title">The decisions Landvex informs</h2>
<p class="section-sub">Every Landvex engagement is anchored to a specific decision. These are the ones our insurance clients face most often.</p>
<div class="decisions-grid">
<div class="decision-chip" style="cursor:pointer;" onclick="location.href='#contact'" role="button" tabindex="0">
<span class="chip-dot"></span>
<span>Underwriting</span>
</div>
<div class="decision-chip" style="cursor:pointer;" onclick="location.href='#contact'" role="button" tabindex="0">
<span class="chip-dot"></span>
<span>Pricing</span>
</div>
<div class="decision-chip" style="cursor:pointer;" onclick="location.href='#contact'" role="button" tabindex="0">
<span class="chip-dot"></span>
<span>Reserve allocation</span>
</div>
<div class="decision-chip" style="cursor:pointer;" onclick="location.href='#contact'" role="button" tabindex="0">
<span class="chip-dot"></span>
<span>Exposure management</span>
</div>
<div class="decision-chip" style="cursor:pointer;" onclick="location.href='#contact'" role="button" tabindex="0">
<span class="chip-dot"></span>
<span>Claims settlement</span>
</div>
</div>
</div>
</section>
<!-- CONTRADICTION ENGINE -->
<section class="contradiction-bg" id="contradiction">
<div class="container" style="max-width:900px">
<div style="text-align:center;margin-bottom:56px">
<div class="section-label" style="color:rgba(0,0,0,.75)">Contradiction Engine</div>
<h2 class="section-title" style="color: #1d1d1f">Most systems tell you what the data says.<br>Landvex tells you what doesn&rsquo;t add up.</h2>
<p class="section-sub" style="color:rgba(0,0,0,.85);margin:0 auto;max-width:600px">The most valuable intelligence is not confirmation. It&rsquo;s contradiction. Landvex actively searches for conflicts between official narratives and observed reality.</p>
</div>
<div class="contradiction-box">
<div class="conflict-header">
<span class="conflict-flag">&#x1F6A9;</span>
<div>
<div class="conflict-title">Narrative Conflict Detected &mdash; Coastal City — South District</div>
<div style="display:inline-block;background:#f59e0b;color:#000;font-size:.7rem;font-weight:700;padding:2px 8px;border-radius:3px;margin:4px 0 6px;letter-spacing:.05em;">⚠ ILLUSTRATIVE SAMPLE — not real intelligence</div>
<div class="conflict-desc">Historical risk models indicate moderate coastal exposure. Recent field surveys reveal significant changes to physical risk profile.</div>
</div>
<h2 style="text-align:center;font-size:32px;font-weight:800;margin-bottom:16px;">What we deliver</h2>
<p style="text-align:center;color:var(--text-muted);max-width:600px;margin:0 auto 40px;">From single-asset verification to portfolio-wide intelligence — deployed in 2472 hours.</p>
<div class="grid">
<div class="card">
<h3>Claims validation</h3>
<p>Rapid field verification of loss reports. Geo-tagged visual evidence delivered within 72 hours of instruction.</p>
</div>
<div class="conflict-grid">
<div class="conflict-side conflict-official">
<div class="conflict-side-label">Official narrative</div>
<ul>
<li>&#10003; Historical loss ratio: within band</li><li>&#10003; Flood model: 1-in-50 year exposure</li><li>&#10003; Construction standards: compliant</li> </ul>
</div>
<div class="conflict-side conflict-observed">
<div class="conflict-side-label">Observed reality</div>
<ul>
<li>&#10007; ✗ Drainage capacity significantly reduced</li><li>&#10007; ✗ 3 new developments in flood shadow zone</li><li>&#10007; ✗ Coastal erosion visible at 4 monitored sites</li><li>&#10007; ✗ Emergency access routes partially compromised</li> </ul>
</div>
<div class="card">
<h3>Pre-loss surveys</h3>
<p>Baseline documentation of insured assets before loss events — establishing ground truth for future claims.</p>
</div>
<div class="conflict-footer">
<span class="conflict-footer-label">Confidence in official narrative</span>
<span class="conflict-confidence">33%</span>
<div class="card">
<h3>Risk assessment</h3>
<p>Field-observed physical condition data to supplement modelled risk — identifying exposures not visible in satellite data.</p>
</div>
</div>
<div class="ce-features">
<div class="ce-feature" style="cursor:pointer;" onclick="location.href='#contact'" role="button" tabindex="0">
<div class="ce-icon">&#x1F50D;</div>
<h4>Show me what I&rsquo;m missing</h4>
<p>Blind spots not visible in official data or internal reports</p>
<div class="card">
<h3>Catastrophe monitoring</h3>
<p>Post-event rapid deployment to document affected areas and validate exposure across large geographies.</p>
</div>
<div class="ce-feature" style="cursor:pointer;" onclick="location.href='#contact'" role="button" tabindex="0">
<div class="ce-icon">&#x2694;&#xFE0F;</div>
<h4>AI Red Team</h4>
<p>Every conclusion is actively challenged by a second model before delivery</p>
<div class="card">
<h3>Portfolio monitoring</h3>
<p>Continuous monitoring of insured assets with change detection and automated alerts.</p>
</div>
<div class="ce-feature" style="cursor:pointer;" onclick="location.href='#contact'" role="button" tabindex="0">
<div class="ce-icon">&#x1F4D0;</div>
<h4>Which assumptions are wrong?</h4>
<p>Identify where your thesis conflicts with observed reality</p>
<div class="card">
<h3>Contradiction detection</h3>
<p>Identify where official data conflicts with observed reality — before it becomes a claim.</p>
</div>
</div>
</div>
</section>
<!-- SCORES -->
<section id="scores" style="background:var(--surface)">
<div class="container">
<div class="section-label">Intelligence scores</div>
<h2 class="section-title">Landvex scores relevant to Insurance</h2>
<p class="section-sub">Every score is derived from field-verified observations, cross-referenced against official data, and launching August 2026.</p>
<div class="scores-grid" style="grid-template-columns:repeat(2,1fr)">
<div class="score-card" style="cursor:pointer;" onclick="location.href='#contact'" role="button" tabindex="0">
<div class="score-icon"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#0066FF" stroke-width="2"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg></div>
<div class="score-name">Risk & Resilience Profile</div>
<div class="score-desc">Physical exposure mapping for insurance, finance, and public sector resilience planning.</div>
<div class="score-bar-wrap">
<div class="score-bar" style="width:74%"></div>
</div>
<div class="score-value">74</div>
<div class="score-label-sub">placeholder · live data from Aug 2026</div>
<div class="learn-more-hint" style="margin-top:12px;font-size:13px;font-weight:700;color:#0066ff">Request access →</div></div>
<div class="score-card" style="cursor:pointer;" onclick="location.href='#contact'" role="button" tabindex="0">
<div class="score-icon">🔧</div>
<div class="score-name">Infrastructure Risk Index</div>
<div class="score-desc">Identifies areas where physical infrastructure is deteriorating faster than maintenance can address.</div>
<div class="score-bar-wrap">
<div class="score-bar" style="width:68%"></div>
</div>
<div class="score-value">68</div>
<div class="score-label-sub">placeholder · live data from Aug 2026</div>
<div class="learn-more-hint" style="margin-top:12px;font-size:13px;font-weight:700;color:#0066ff">Request access →</div></div>
</div>
<div class="cta-banner">
<div class="cta-banner-text">
<h3>Four layers. One answer.</h3>
<p>quiXzoom observes. AMOS analyses. Landvex delivers the answer. Every engagement starts with one question: <em style="color: #1d1d1f;font-style:normal">what decision are you trying to make?</em></p>
</div>
<a class="btn btn-lg" href="#contact" style="white-space:nowrap;flex-shrink:0">Request a briefing &rarr;</a>
</div>
</div>
</section>
<section id="contact">
<div class="container">
<div class="contact-wrapper">
<div class="section-label">Request a briefing</div>
<h2 class="section-title">What decision are you trying to make?</h2>
<p class="section-sub">Tell us the question you need answered. We'll show you how Landvex can support it.</p>
<form class="contact-form" id="contact-form">
<div class="form-group">
<label for="name">Name</label>
<input type="text" id="name" name="name" placeholder="Your name" required>
</div>
<div class="form-group">
<label for="email">Email</label>
<input type="email" id="email" name="email" placeholder="you@organisation.com" required>
</div>
<div class="form-group">
<label for="message">Message</label>
<textarea id="message" name="message" placeholder="Describe the decision you need to make — what you need to know, where, and why it matters..."></textarea>
</div>
<button type="submit" class="btn btn-lg" style="width:100%; justify-content: center;">
Request briefing &rarr;
</button>
<p class="form-note">We don't spam. Your information stays private.</p>
<div id="form-message"></div>
</form>
</div>
</div>
<p class="lv-disclaimer">Intelligence outputs are indicative and advisory only. Based on field observations — not investment, financial or legal advice. Landvex AB accepts no liability for decisions made based on these outputs. <a href="/methodology/">Methodology →</a></p>
</section>
<footer>
<div class="footer-inner">
<div>
<div class="footer-logo">LandveX</div>
<div class="footer-copy">&copy; 2026 Landvex Inc &middot; Landvex AB (org.nr 559141-7042)</div>
<div class="footer-tagline">Decision intelligence for the physical world.</div>
</div>
<div class="footer-links">
<a href="https://landvex.com/">Home</a>
<a href="/verticals/">Verticals</a>
<a href="/methodology/">Methodology</a>
<a href="/security/">Security</a>
<a href="https://www.quixzoom.com/" target="_blank" rel="noopener">quiXzoom &rarr;</a>
<a href="mailto:contact@landvex.com">contact@landvex.com</a>
<a href="mailto:security@landvex.com" style="color:var(--text-muted,#888)">security@landvex.com</a>
<a href="https://www.linkedin.com/company/landvex" target="_blank" rel="noopener" class="footer-social" title="LandveX on LinkedIn">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16 8a6 6 0 0 1 6 6v7h-4v-7a2 2 0 0 0-2-2 2 2 0 0 0-2 2v7h-4v-7a6 6 0 0 1 6-6z" stroke="#888" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<rect x="2" y="9" width="4" height="12" stroke="#888" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<circle cx="4" cy="4" r="2" stroke="#888" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</a>
<a href="https://x.com/landvex" target="_blank" rel="noopener" class="footer-social" title="LandveX on X">
<svg width="15" height="15" viewBox="0 0 24 24" fill="#888" xmlns="http://www.w3.org/2000/svg">
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-4.714-6.231-5.401 6.231H2.746l7.73-8.835L1.254 2.25H8.08l4.253 5.622 5.912-5.622Zm-1.161 17.52h1.833L7.084 4.126H5.117z"/>
</svg>
</a>
</div>
</div>
<p>© 2026 Landvex AB · <a href="/" style="color:var(--blue);">landvex.com</a></p>
</footer>
<script>
document.getElementById('contact-form').addEventListener('submit', async function(e) {
e.preventDefault();
const btn = this.querySelector('button[type="submit"]');
const msgEl = document.getElementById('form-message');
btn.disabled = true; btn.textContent = 'Sending...';
const data = {
source: 'landvex-vertical',
name: this.name.value,
email: this.email.value,
message: this.message.value
};
try {
const res = await fetch('https://api.quixzoom.com/api/qz/waitlist', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
if (res.ok || res.status === 201) {
msgEl.className = 'success';
msgEl.textContent = "\u2713 Request received. We'll be in touch within one business day.";
msgEl.style.display = 'block';
this.reset();
} else { throw new Error('Server error ' + res.status); }
} catch (err) {
msgEl.className = 'error';
msgEl.textContent = 'Something went wrong. Please email us directly at contact@landvex.com.';
msgEl.style.display = 'block';
}
btn.disabled = false; btn.textContent = 'Request briefing →';
});
document.querySelectorAll('a[href^="#"]').forEach(a => {
a.addEventListener('click', e => {
const target = document.querySelector(a.getAttribute('href'));
if (target) { e.preventDefault(); target.scrollIntoView({ behavior: 'smooth', block: 'start' }); }
});
});
</script>
</body>
</html>
<section class="section-pad" style="background:#f7f7f5">
<div class="container">
<div style="display:grid;grid-template-columns:1fr 1fr;gap:32px;align-items:center;max-width:900px;margin:0 auto">
<div>
<div style="font-size:.6875rem;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:#0066FF;margin-bottom:12px">Why Landvex</div>
<h2 style="font-size:1.625rem;font-weight:900;line-height:1.15;margin-bottom:16px;color:#0a0a0a">Intelligence, not just data.</h2>
<p style="font-size:.9375rem;color:#333;line-height:1.65;margin-bottom:16px">Raw field observations are valuable. But decisions require analysis, confidence scoring, and contradiction detection. Landvex transforms quiXzoom field data into structured intelligence that underwriting and claims teams can act on.</p>
<p style="font-size:.9375rem;color:#333;line-height:1.65">Clients don&apos;t receive photos. They receive decisions.</p>
</div>
<div style="display:flex;flex-direction:column;gap:12px">
<div style="font-size:.6875rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:#555;margin-bottom:4px">Claims inspection market landscape</div>
<p style="font-size:.8125rem;color:#444;line-height:1.6;margin:0">The insurance claims inspection market is served by traditional field adjusters, TPAs, and outsourced claims handlers. These channels face the same cost and speed constraints that create demand for faster, field-verified alternatives.</p>
<p style="font-size:.8125rem;color:#444;line-height:1.6;margin:0">Landvex is building toward this market as a data and intelligence supplier. We are not affiliated with, endorsed by, or in a commercial relationship with any named market participants.</p>
<div style="background: #f5f5f7;border-radius:12px;padding:16px 20px;text-align:center;margin-top:4px">
<span style="font-size:.8125rem;font-weight:700;color:rgba(0,0,0,.9)">Interested in a claims data pilot?</span>
<a href="/contact/" style="display:block;margin-top:8px;color:#0066FF;font-size:.875rem;font-weight:700;text-decoration:none">Start a conversation →</a>
</div>
</div>
</div>
</section>
</html>
+16 -16
View File
@@ -181,7 +181,7 @@
display: flex; align-items: center; gap: 12px;
}
.decision-chip .chip-dot {
width: 8px; height: 8px; border-radius: 50%;
width: 8px; height: 8px; border-radius: var(--radius-full);
background: var(--blue); flex-shrink: 0;
}
.decision-chip span { font-size: 15px; font-weight: 600; color: var(--text-light); }
@@ -201,7 +201,7 @@
content: '';
position: absolute; top: 0; left: 0;
width: 4px; height: 100%;
background: var(--blue); border-radius: 4px 0 0 4px;
background: var(--blue); border-radius: var(--radius-sm) 0 0 4px;
}
.question-card p {
font-size: 16px; font-weight: 600; color: #0a0a0a; line-height: 1.5;
@@ -218,7 +218,7 @@
.contradiction-box {
background: rgba(0,0,0,.05);
border: 1px solid rgba(0,0,0,.1);
border-radius: 16px; padding: 36px 40px; margin-bottom: 32px;
border-radius: var(--radius-lg); padding: 36px 40px; margin-bottom: 32px;
}
.conflict-header {
@@ -230,7 +230,7 @@
.conflict-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.conflict-side { border-radius: 10px; padding: 16px; }
.conflict-side { border-radius: var(--radius-md); padding: 16px; }
.conflict-official { background: rgba(34,197,94,.1); border: 1px solid rgba(34,197,94,.2); }
.conflict-observed { background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.2); }
@@ -269,14 +269,14 @@
.score-icon-wrap { margin-bottom: 12px; }
.score-name { font-size: 15px; font-weight: 700; color: #1d1d1f; margin-bottom: 6px; }
.score-desc { font-size: 13px; color: var(--text-dim); line-height: 1.5; margin-bottom: 20px; }
.score-bar-wrap { background: rgba(0,0,0,.06); border-radius: 6px; height: 8px; margin-bottom: 10px; }
.score-bar { height: 8px; border-radius: 6px; background: linear-gradient(90deg, var(--blue), #5599ff); }
.score-bar-wrap { background: rgba(0,0,0,.06); border-radius: var(--radius-sm); height: 8px; margin-bottom: 10px; }
.score-bar { height: 8px; border-radius: var(--radius-sm); background: linear-gradient(90deg, var(--blue), #5599ff); }
.score-value { font-size: 28px; font-weight: 800; color: var(--blue); letter-spacing: -1px; }
.score-label-sub { font-size: 11px; color: var(--text-dim); margin-top: 2px; }
/* CTA BANNER */
.cta-banner {
background: #e8f0fe; border-radius: 16px; padding: 40px 48px;
background: #e8f0fe; border-radius: var(--radius-lg); padding: 40px 48px;
display: flex; align-items: center; gap: 48px; flex-wrap: wrap;
margin-top: 48px;
}
@@ -329,7 +329,7 @@
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social {
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px;
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md);
display: flex; align-items: center; justify-content: center;
transition: border-color 0.2s, background 0.2s;
}
@@ -359,7 +359,7 @@
.hero h1 { letter-spacing: -1px; }
}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius:8px;line-height:1.6;max-width:800px}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius: var(--radius-md);line-height:1.6;max-width:800px}
.lv-disclaimer a{color:rgba(0,0,0,.65);text-decoration:underline}
.lv-disclaimer--light{color:#888;background:#f5f5f5}
.lv-disclaimer--light a{color:#555}
@@ -573,18 +573,18 @@
<div class="conflict-side conflict-official">
<div class="conflict-side-label">Investment thesis</div>
<ul>
<li>&#10003; Office vacancy declining per broker data</li>
<li>&#10003; Rental rates up 6.2% YoY</li>
<li>&#10003; Major tenant signings reported</li>
<li>&#100003 Office vacancy declining per broker data</li>
<li>&#100003 Rental rates up 6.2% YoY</li>
<li>&#100003 Major tenant signings reported</li>
</ul>
</div>
<div class="conflict-side conflict-observed">
<div class="conflict-side-label">Observed reality</div>
<ul>
<li>&#10007; Ground-floor retail vacancy up 31% vs prior year</li>
<li>&#10007; Multiple business closures, limited new openings</li>
<li>&#10007; Foot traffic down 25% vs comparable streets</li>
<li>&#10007; Deferred maintenance visible on 35% of sampled buildings</li>
<li>&#100007 Ground-floor retail vacancy up 31% vs prior year</li>
<li>&#100007 Multiple business closures, limited new openings</li>
<li>&#100007 Foot traffic down 25% vs comparable streets</li>
<li>&#100007 Deferred maintenance visible on 35% of sampled buildings</li>
</ul>
</div>
</div>
+15 -15
View File
@@ -178,7 +178,7 @@
display: flex; align-items: center; gap: 12px;
}
.decision-chip .chip-dot {
width: 8px; height: 8px; border-radius: 50%;
width: 8px; height: 8px; border-radius: var(--radius-full);
background: var(--blue); flex-shrink: 0;
}
.decision-chip span { font-size: 15px; font-weight: 600; color: var(--text-light); }
@@ -189,7 +189,7 @@
.contradiction-box {
background: rgba(0,0,0,.05);
border: 1px solid rgba(0,0,0,.1);
border-radius: 16px; padding: 36px 40px; margin-bottom: 32px;
border-radius: var(--radius-lg); padding: 36px 40px; margin-bottom: 32px;
}
.conflict-header {
@@ -203,7 +203,7 @@
.conflict-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.conflict-side { border-radius: 10px; padding: 16px; }
.conflict-side { border-radius: var(--radius-md); padding: 16px; }
.conflict-official { background: rgba(34,197,94,.1); border: 1px solid rgba(34,197,94,.2); }
.conflict-observed { background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.2); }
@@ -251,14 +251,14 @@
.score-name { font-size: 15px; font-weight: 700; color: #1d1d1f; margin-bottom: 6px; }
.score-desc { font-size: 13px; color: var(--text-dim); line-height: 1.5; margin-bottom: 20px; }
.score-bar-wrap { background: rgba(0,0,0,.06); border-radius: 6px; height: 8px; margin-bottom: 10px; }
.score-bar { height: 8px; border-radius: 6px; background: linear-gradient(90deg, var(--blue), #5599ff); }
.score-bar-wrap { background: rgba(0,0,0,.06); border-radius: var(--radius-sm); height: 8px; margin-bottom: 10px; }
.score-bar { height: 8px; border-radius: var(--radius-sm); background: linear-gradient(90deg, var(--blue), #5599ff); }
.score-value { font-size: 28px; font-weight: 800; color: var(--blue); letter-spacing: -1px; }
.score-label-sub { font-size: 11px; color: var(--text-dim); margin-top: 2px; }
/* CTA BANNER */
.cta-banner {
background: #e8f0fe; border-radius: 16px; padding: 40px 48px;
background: #e8f0fe; border-radius: var(--radius-lg); padding: 40px 48px;
display: flex; align-items: center; gap: 48px; flex-wrap: wrap;
margin-top: 48px;
}
@@ -310,7 +310,7 @@
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social {
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px;
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md);
display: flex; align-items: center; justify-content: center;
transition: border-color 0.2s, background 0.2s;
}
@@ -338,7 +338,7 @@
.hero h1 { letter-spacing: -1px; }
}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius:8px;line-height:1.6;max-width:800px}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius: var(--radius-md);line-height:1.6;max-width:800px}
.lv-disclaimer a{color:rgba(0,0,0,.65);text-decoration:underline}
.lv-disclaimer--light{color:#888;background:#f5f5f5}
.lv-disclaimer--light a{color:#555}
@@ -489,18 +489,18 @@
<div class="conflict-side conflict-official">
<div class="conflict-side-label">Official narrative</div>
<ul>
<li>&#10003; Growth model: northern zone demand +23% YoY</li>
<li>&#10003; Road network: rated adequate for expansion</li>
<li>&#10003; Municipality: no known access restrictions</li>
<li>&#100003 Growth model: northern zone demand +23% YoY</li>
<li>&#100003 Road network: rated adequate for expansion</li>
<li>&#100003 Municipality: no known access restrictions</li>
</ul>
</div>
<div class="conflict-side conflict-observed">
<div class="conflict-side-label">Observed reality</div>
<ul>
<li>&#10007; Road condition below model assumptions</li>
<li>&#10007; Loading bay access restricted: 3 key locations</li>
<li>&#10007; Active construction reducing access 6&ndash;18 months</li>
<li>&#10007; Southern zone outperforming northern on density</li>
<li>&#100007 Road condition below model assumptions</li>
<li>&#100007 Loading bay access restricted: 3 key locations</li>
<li>&#100007 Active construction reducing access 6&ndash;18 months</li>
<li>&#100007 Southern zone outperforming northern on density</li>
</ul>
</div>
</div>
@@ -180,7 +180,7 @@
display: flex; align-items: center; gap: 12px;
}
.decision-chip .chip-dot {
width: 8px; height: 8px; border-radius: 50%;
width: 8px; height: 8px; border-radius: var(--radius-full);
background: var(--blue); flex-shrink: 0;
}
.decision-chip span { font-size: 15px; font-weight: 600; color: var(--text-light); }
@@ -191,7 +191,7 @@
.contradiction-box {
background: rgba(0,0,0,.05);
border: 1px solid rgba(0,0,0,.1);
border-radius: 16px; padding: 36px 40px; margin-bottom: 32px;
border-radius: var(--radius-lg); padding: 36px 40px; margin-bottom: 32px;
}
.conflict-header {
@@ -203,7 +203,7 @@
.conflict-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.conflict-side { border-radius: 10px; padding: 16px; }
.conflict-side { border-radius: var(--radius-md); padding: 16px; }
.conflict-official { background: rgba(34,197,94,.1); border: 1px solid rgba(34,197,94,.2); }
.conflict-observed { background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.2); }
@@ -242,14 +242,14 @@
.score-icon { font-size: 1.5rem; margin-bottom: 12px; }
.score-name { font-size: 15px; font-weight: 700; color: #1d1d1f; margin-bottom: 6px; }
.score-desc { font-size: 13px; color: var(--text-dim); line-height: 1.5; margin-bottom: 20px; }
.score-bar-wrap { background: rgba(0,0,0,.06); border-radius: 6px; height: 8px; margin-bottom: 10px; }
.score-bar { height: 8px; border-radius: 6px; background: linear-gradient(90deg, var(--blue), #5599ff); }
.score-bar-wrap { background: rgba(0,0,0,.06); border-radius: var(--radius-sm); height: 8px; margin-bottom: 10px; }
.score-bar { height: 8px; border-radius: var(--radius-sm); background: linear-gradient(90deg, var(--blue), #5599ff); }
.score-value { font-size: 28px; font-weight: 800; color: var(--blue); letter-spacing: -1px; }
.score-label-sub { font-size: 11px; color: var(--text-dim); margin-top: 2px; }
/* CTA BANNER */
.cta-banner {
background: #e8f0fe; border-radius: 16px; padding: 40px 48px;
background: #e8f0fe; border-radius: var(--radius-lg); padding: 40px 48px;
display: flex; align-items: center; gap: 48px; flex-wrap: wrap;
margin-top: 48px;
}
@@ -301,7 +301,7 @@
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social {
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px;
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md);
display: flex; align-items: center; justify-content: center;
transition: border-color 0.2s, background 0.2s;
}
@@ -329,7 +329,7 @@
.hero h1 { letter-spacing: -1px; }
}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius:8px;line-height:1.6;max-width:800px}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius: var(--radius-md);line-height:1.6;max-width:800px}
.lv-disclaimer a{color:rgba(0,0,0,.65);text-decoration:underline}
.lv-disclaimer--light{color:#888;background:#f5f5f5}
.lv-disclaimer--light a{color:#555}
@@ -378,7 +378,7 @@
<p class="section-sub">From single-asset verifications to portfolio-wide intelligence missions — deployed in 2472 hours.</p>
<div class="use-case-grid">
<div class="use-case-card" style="cursor:pointer;" onclick="location.href='#contact'" role="button" tabindex="0">
<span class="uc-icon">🔭</span>
<span class="uc-icon"></span>
<h3>Urban asset management</h3>
<p>Systematic documentation of streets, public spaces, and municipal assets across entire districts — at a fraction of traditional survey costs.</p>
@@ -390,13 +390,13 @@
<div class="learn-more-hint" style="margin-top:12px;font-size:13px;font-weight:700;color:#0066ff">Learn more →</div></div>
<div class="use-case-card" style="cursor:pointer;" onclick="location.href='#contact'" role="button" tabindex="0">
<span class="uc-icon">🗺️</span>
<span class="uc-icon"></span>
<h3>Infrastructure condition monitoring</h3>
<p>Ongoing visual monitoring of roads, bridges, drainage, and public utilities — identifying deterioration before it becomes a liability.</p>
<div class="learn-more-hint" style="margin-top:12px;font-size:13px;font-weight:700;color:#0066ff">Learn more →</div></div>
<div class="use-case-card" style="cursor:pointer;" onclick="location.href='#contact'" role="button" tabindex="0">
<span class="uc-icon"></span>
<span class="uc-icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M9 2L5 9H8L7 14L11 7H8L9 2Z" fill="#f59e0b"/></svg></span>
<h3>Public space documentation</h3>
<p>Baseline documentation of parks, plazas, and community assets for maintenance planning, insurance, and performance reporting.</p>
@@ -457,12 +457,12 @@
<div class="conflict-side conflict-official">
<div class="conflict-side-label">Official narrative</div>
<ul>
<li>&#10003; Infrastructure rating: 7.2/10</li><li>&#10003; Maintenance spend on track</li><li>&#10003; No critical failures reported</li> </ul>
<li>&#100003 Infrastructure rating: 7.2/10</li><li>&#100003 Maintenance spend on track</li><li>&#100003 No critical failures reported</li> </ul>
</div>
<div class="conflict-side conflict-observed">
<div class="conflict-side-label">Observed reality</div>
<ul>
<li>&#10007; Visible cracking in 34% of sampled roads</li><li>&#10007; Drainage blockages on 12 key junctions</li><li>&#10007; Deferred maintenance visible on 8 bridges</li> </ul>
<li>&#100007 <svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Visible cracking in 34% of sampled roads</li><li>&#100007 <svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Drainage blockages on 12 key junctions</li><li>&#100007 <svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Deferred maintenance visible on 8 bridges</li> </ul>
</div>
</div>
<div class="conflict-footer">
@@ -499,7 +499,7 @@
<p class="section-sub">Every score is derived from field-verified observations, cross-referenced against official data, and launching August 2026.</p>
<div class="scores-grid" style="grid-template-columns:repeat(3,1fr)">
<div class="score-card" style="cursor:pointer;" onclick="location.href='#contact'" role="button" tabindex="0">
<div class="score-icon">🏙️</div>
<div class="score-icon"></div>
<div class="score-name">City Health Index</div>
<div class="score-desc">Composite measure of urban infrastructure quality, service delivery, and development momentum.</div>
<div class="score-bar-wrap">
@@ -510,7 +510,7 @@
<div class="learn-more-hint" style="margin-top:12px;font-size:13px;font-weight:700;color:#0066ff">Request access →</div></div>
<div class="score-card" style="cursor:pointer;" onclick="location.href='#contact'" role="button" tabindex="0">
<div class="score-icon">🔧</div>
<div class="score-icon"></div>
<div class="score-name">Infrastructure Risk Index</div>
<div class="score-desc">Identifies areas where physical infrastructure is deteriorating faster than maintenance can address.</div>
<div class="score-bar-wrap">
@@ -521,7 +521,7 @@
<div class="learn-more-hint" style="margin-top:12px;font-size:13px;font-weight:700;color:#0066ff">Request access →</div></div>
<div class="score-card" style="cursor:pointer;" onclick="location.href='#contact'" role="button" tabindex="0">
<div class="score-icon">📈</div>
<div class="score-icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 12L6 8L9 11L14 5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M10 5H14V9" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></div>
<div class="score-name">Development Forecast</div>
<div class="score-desc">Forward-looking signal combining permit activity, investment flows, and observed construction.</div>
<div class="score-bar-wrap">
+16 -16
View File
@@ -180,7 +180,7 @@
display: flex; align-items: center; gap: 12px;
}
.decision-chip .chip-dot {
width: 8px; height: 8px; border-radius: 50%;
width: 8px; height: 8px; border-radius: var(--radius-full);
background: var(--blue); flex-shrink: 0;
}
.decision-chip span { font-size: 15px; font-weight: 600; color: var(--text-light); }
@@ -191,7 +191,7 @@
.contradiction-box {
background: rgba(0,0,0,.05);
border: 1px solid rgba(0,0,0,.1);
border-radius: 16px; padding: 36px 40px; margin-bottom: 32px;
border-radius: var(--radius-lg); padding: 36px 40px; margin-bottom: 32px;
}
.conflict-header {
@@ -203,7 +203,7 @@
.conflict-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.conflict-side { border-radius: 10px; padding: 16px; }
.conflict-side { border-radius: var(--radius-md); padding: 16px; }
.conflict-official { background: rgba(34,197,94,.1); border: 1px solid rgba(34,197,94,.2); }
.conflict-observed { background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.2); }
@@ -242,14 +242,14 @@
.score-icon { font-size: 1.5rem; margin-bottom: 12px; }
.score-name { font-size: 15px; font-weight: 700; color: #1d1d1f; margin-bottom: 6px; }
.score-desc { font-size: 13px; color: var(--text-dim); line-height: 1.5; margin-bottom: 20px; }
.score-bar-wrap { background: rgba(0,0,0,.06); border-radius: 6px; height: 8px; margin-bottom: 10px; }
.score-bar { height: 8px; border-radius: 6px; background: linear-gradient(90deg, var(--blue), #5599ff); }
.score-bar-wrap { background: rgba(0,0,0,.06); border-radius: var(--radius-sm); height: 8px; margin-bottom: 10px; }
.score-bar { height: 8px; border-radius: var(--radius-sm); background: linear-gradient(90deg, var(--blue), #5599ff); }
.score-value { font-size: 28px; font-weight: 800; color: var(--blue); letter-spacing: -1px; }
.score-label-sub { font-size: 11px; color: var(--text-dim); margin-top: 2px; }
/* CTA BANNER */
.cta-banner {
background: #e8f0fe; border-radius: 16px; padding: 40px 48px;
background: #e8f0fe; border-radius: var(--radius-lg); padding: 40px 48px;
display: flex; align-items: center; gap: 48px; flex-wrap: wrap;
margin-top: 48px;
}
@@ -301,7 +301,7 @@
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social {
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px;
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md);
display: flex; align-items: center; justify-content: center;
transition: border-color 0.2s, background 0.2s;
}
@@ -329,7 +329,7 @@
.hero h1 { letter-spacing: -1px; }
}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius:8px;line-height:1.6;max-width:800px}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius: var(--radius-md);line-height:1.6;max-width:800px}
.lv-disclaimer a{color:rgba(0,0,0,.65);text-decoration:underline}
.lv-disclaimer--light{color:#888;background:#f5f5f5}
.lv-disclaimer--light a{color:#555}
@@ -378,7 +378,7 @@
<p class="section-sub">From single-asset verifications to portfolio-wide intelligence missions — deployed in 2472 hours.</p>
<div class="use-case-grid">
<div class="use-case-card" style="cursor:pointer;" onclick="location.href='#contact'" role="button" tabindex="0">
<span class="uc-icon">🔭</span>
<span class="uc-icon"></span>
<h3>Portfolio documentation</h3>
<p>Systematic visual documentation of portfolio assets — condition, occupancy signals, neighbourhood quality, and change over time.</p>
@@ -390,13 +390,13 @@
<div class="learn-more-hint" style="margin-top:12px;font-size:13px;font-weight:700;color:#0066ff">Learn more →</div></div>
<div class="use-case-card" style="cursor:pointer;" onclick="location.href='#contact'" role="button" tabindex="0">
<span class="uc-icon">🗺️</span>
<span class="uc-icon"></span>
<h3>Site verification</h3>
<p>Confirm what's actually on the ground before committing capital. Verify access, condition, and surrounding environment.</p>
<div class="learn-more-hint" style="margin-top:12px;font-size:13px;font-weight:700;color:#0066ff">Learn more →</div></div>
<div class="use-case-card" style="cursor:pointer;" onclick="location.href='#contact'" role="button" tabindex="0">
<span class="uc-icon"></span>
<span class="uc-icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M9 2L5 9H8L7 14L11 7H8L9 2Z" fill="#f59e0b"/></svg></span>
<h3>Market intelligence</h3>
<p>Track commercial activity, vacancy rates, and neighbourhood trajectory across target markets — before the data appears in published indices.</p>
@@ -461,12 +461,12 @@
<div class="conflict-side conflict-official">
<div class="conflict-side-label">Official narrative</div>
<ul>
<li>&#10003; Property prices up 8.3% YoY</li><li>&#10003; Strong rental demand reported</li><li>&#10003; Low vacancy in commercial ground floor</li> </ul>
<li>&#100003 Property prices up 8.3% YoY</li><li>&#100003 Strong rental demand reported</li><li>&#100003 Low vacancy in commercial ground floor</li> </ul>
</div>
<div class="conflict-side conflict-observed">
<div class="conflict-side-label">Observed reality</div>
<ul>
<li>&#10007; Vacancy rates up 22% vs prior year visit</li><li>&#10007; Multiple businesses closed, limited new openings</li><li>&#10007; Deferred maintenance visible on 40% of sampled buildings</li><li>&#10007; Foot traffic down vs comparable streets</li> </ul>
<li>&#100007 <svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Vacancy rates up 22% vs prior year visit</li><li>&#100007 <svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Multiple businesses closed, limited new openings</li><li>&#100007 <svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Deferred maintenance visible on 40% of sampled buildings</li><li>&#100007 <svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Foot traffic down vs comparable streets</li> </ul>
</div>
</div>
<div class="conflict-footer">
@@ -503,7 +503,7 @@
<p class="section-sub">Every score is derived from field-verified observations, cross-referenced against official data, and launching August 2026.</p>
<div class="scores-grid" style="grid-template-columns:repeat(3,1fr)">
<div class="score-card" style="cursor:pointer;" onclick="location.href='#contact'" role="button" tabindex="0">
<div class="score-icon">💎</div>
<div class="score-icon"></div>
<div class="score-name">Investment Attractiveness Index</div>
<div class="score-desc">Composite score combining opportunity, risk, velocity, and quality for investment decisions.</div>
<div class="score-bar-wrap">
@@ -514,7 +514,7 @@
<div class="learn-more-hint" style="margin-top:12px;font-size:13px;font-weight:700;color:#0066ff">Request access →</div></div>
<div class="score-card" style="cursor:pointer;" onclick="location.href='#contact'" role="button" tabindex="0">
<div class="score-icon">🏪</div>
<div class="score-icon"></div>
<div class="score-name">Commercial Opportunity Index</div>
<div class="score-desc">Measures commercial vacancy, foot traffic, and business activity for location intelligence.</div>
<div class="score-bar-wrap">
@@ -525,7 +525,7 @@
<div class="learn-more-hint" style="margin-top:12px;font-size:13px;font-weight:700;color:#0066ff">Request access →</div></div>
<div class="score-card" style="cursor:pointer;" onclick="location.href='#contact'" role="button" tabindex="0">
<div class="score-icon">🚀</div>
<div class="score-icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M8 2L10 8L14 9L8 14L6 8L2 7L8 2Z" fill="#666"/></svg></div>
<div class="score-name">Growth Velocity Index</div>
<div class="score-desc">How fast is an area changing? Identifies emerging zones before they appear in official statistics.</div>
<div class="score-bar-wrap">
+16 -16
View File
@@ -180,7 +180,7 @@
display: flex; align-items: center; gap: 12px;
}
.decision-chip .chip-dot {
width: 8px; height: 8px; border-radius: 50%;
width: 8px; height: 8px; border-radius: var(--radius-full);
background: var(--blue); flex-shrink: 0;
}
.decision-chip span { font-size: 15px; font-weight: 600; color: var(--text-light); }
@@ -191,7 +191,7 @@
.contradiction-box {
background: rgba(0,0,0,.05);
border: 1px solid rgba(0,0,0,.1);
border-radius: 16px; padding: 36px 40px; margin-bottom: 32px;
border-radius: var(--radius-lg); padding: 36px 40px; margin-bottom: 32px;
}
.conflict-header {
@@ -203,7 +203,7 @@
.conflict-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.conflict-side { border-radius: 10px; padding: 16px; }
.conflict-side { border-radius: var(--radius-md); padding: 16px; }
.conflict-official { background: rgba(34,197,94,.1); border: 1px solid rgba(34,197,94,.2); }
.conflict-observed { background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.2); }
@@ -242,14 +242,14 @@
.score-icon { font-size: 1.5rem; margin-bottom: 12px; }
.score-name { font-size: 15px; font-weight: 700; color: #1d1d1f; margin-bottom: 6px; }
.score-desc { font-size: 13px; color: var(--text-dim); line-height: 1.5; margin-bottom: 20px; }
.score-bar-wrap { background: rgba(0,0,0,.06); border-radius: 6px; height: 8px; margin-bottom: 10px; }
.score-bar { height: 8px; border-radius: 6px; background: linear-gradient(90deg, var(--blue), #5599ff); }
.score-bar-wrap { background: rgba(0,0,0,.06); border-radius: var(--radius-sm); height: 8px; margin-bottom: 10px; }
.score-bar { height: 8px; border-radius: var(--radius-sm); background: linear-gradient(90deg, var(--blue), #5599ff); }
.score-value { font-size: 28px; font-weight: 800; color: var(--blue); letter-spacing: -1px; }
.score-label-sub { font-size: 11px; color: var(--text-dim); margin-top: 2px; }
/* CTA BANNER */
.cta-banner {
background: #e8f0fe; border-radius: 16px; padding: 40px 48px;
background: #e8f0fe; border-radius: var(--radius-lg); padding: 40px 48px;
display: flex; align-items: center; gap: 48px; flex-wrap: wrap;
margin-top: 48px;
}
@@ -301,7 +301,7 @@
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social {
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px;
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md);
display: flex; align-items: center; justify-content: center;
transition: border-color 0.2s, background 0.2s;
}
@@ -329,7 +329,7 @@
.hero h1 { letter-spacing: -1px; }
}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius:8px;line-height:1.6;max-width:800px}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius: var(--radius-md);line-height:1.6;max-width:800px}
.lv-disclaimer a{color:rgba(0,0,0,.65);text-decoration:underline}
.lv-disclaimer--light{color:#888;background:#f5f5f5}
.lv-disclaimer--light a{color:#555}
@@ -378,7 +378,7 @@
<p class="section-sub">From single-asset verifications to portfolio-wide intelligence missions — deployed in 2472 hours.</p>
<div class="use-case-grid">
<div class="use-case-card" style="cursor:pointer;" onclick="location.href='#contact'" role="button" tabindex="0">
<span class="uc-icon">🔭</span>
<span class="uc-icon"></span>
<h3>Store compliance audits</h3>
<p>Verify signage, frontage, merchandising standards, and physical condition across your entire network. Rapid, scalable, consistent.</p>
@@ -390,13 +390,13 @@
<div class="learn-more-hint" style="margin-top:12px;font-size:13px;font-weight:700;color:#0066ff">Learn more →</div></div>
<div class="use-case-card" style="cursor:pointer;" onclick="location.href='#contact'" role="button" tabindex="0">
<span class="uc-icon">🗺️</span>
<span class="uc-icon"></span>
<h3>Competitor mapping</h3>
<p>Document competitor presence, condition, and activity across target markets. Know what's actually on the ground before you commit.</p>
<div class="learn-more-hint" style="margin-top:12px;font-size:13px;font-weight:700;color:#0066ff">Learn more →</div></div>
<div class="use-case-card" style="cursor:pointer;" onclick="location.href='#contact'" role="button" tabindex="0">
<span class="uc-icon"></span>
<span class="uc-icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M9 2L5 9H8L7 14L11 7H8L9 2Z" fill="#f59e0b"/></svg></span>
<h3>Location intelligence</h3>
<p>Pre-site surveys combining foot traffic signals, surrounding commercial activity, accessibility, and neighbourhood quality assessment.</p>
@@ -457,12 +457,12 @@
<div class="conflict-side conflict-official">
<div class="conflict-side-label">Official narrative</div>
<ul>
<li>&#10003; Internal: catchment growth 12% YoY</li><li>&#10003; Competitor analysis: 2 main operators</li><li>&#10003; Foot traffic model: high potential</li> </ul>
<li>&#100003 Internal: catchment growth 12% YoY</li><li>&#100003 Competitor analysis: 2 main operators</li><li>&#100003 Foot traffic model: high potential</li> </ul>
</div>
<div class="conflict-side conflict-observed">
<div class="conflict-side-label">Observed reality</div>
<ul>
<li>&#10007; 4 additional competitors not in internal model</li><li>&#10007; Observed foot traffic lower than modelled peak</li><li>&#10007; 3 recent closures in primary retail zone</li><li>&#10007; Public transport disruption affecting access</li> </ul>
<li>&#100007 <svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> 4 additional competitors not in internal model</li><li>&#100007 <svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Observed foot traffic lower than modelled peak</li><li>&#100007 <svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> 3 recent closures in primary retail zone</li><li>&#100007 <svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Public transport disruption affecting access</li> </ul>
</div>
</div>
<div class="conflict-footer">
@@ -499,7 +499,7 @@
<p class="section-sub">Every score is derived from field-verified observations, cross-referenced against official data, and launching August 2026.</p>
<div class="scores-grid" style="grid-template-columns:repeat(3,1fr)">
<div class="score-card" style="cursor:pointer;" onclick="location.href='#contact'" role="button" tabindex="0">
<div class="score-icon">🏪</div>
<div class="score-icon"></div>
<div class="score-name">Commercial Opportunity Index</div>
<div class="score-desc">Measures commercial vacancy, foot traffic, and business activity for location intelligence.</div>
<div class="score-bar-wrap">
@@ -510,7 +510,7 @@
<div class="learn-more-hint" style="margin-top:12px;font-size:13px;font-weight:700;color:#0066ff">Request access →</div></div>
<div class="score-card" style="cursor:pointer;" onclick="location.href='#contact'" role="button" tabindex="0">
<div class="score-icon">🏘️</div>
<div class="score-icon"></div>
<div class="score-name">Neighbourhood Quality Index</div>
<div class="score-desc">How an area is experienced — condition, activity, upkeep, and change trajectory.</div>
<div class="score-bar-wrap">
@@ -521,7 +521,7 @@
<div class="learn-more-hint" style="margin-top:12px;font-size:13px;font-weight:700;color:#0066ff">Request access →</div></div>
<div class="score-card" style="cursor:pointer;" onclick="location.href='#contact'" role="button" tabindex="0">
<div class="score-icon">🚀</div>
<div class="score-icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M8 2L10 8L14 9L8 14L6 8L2 7L8 2Z" fill="#666"/></svg></div>
<div class="score-name">Growth Velocity Index</div>
<div class="score-desc">How fast is an area changing? Identifies emerging zones before they appear in official statistics.</div>
<div class="score-bar-wrap">
+15 -15
View File
@@ -180,7 +180,7 @@
display: flex; align-items: center; gap: 12px;
}
.decision-chip .chip-dot {
width: 8px; height: 8px; border-radius: 50%;
width: 8px; height: 8px; border-radius: var(--radius-full);
background: var(--blue); flex-shrink: 0;
}
.decision-chip span { font-size: 15px; font-weight: 600; color: var(--text-light); }
@@ -191,7 +191,7 @@
.contradiction-box {
background: rgba(0,0,0,.05);
border: 1px solid rgba(0,0,0,.1);
border-radius: 16px; padding: 36px 40px; margin-bottom: 32px;
border-radius: var(--radius-lg); padding: 36px 40px; margin-bottom: 32px;
}
.conflict-header {
@@ -203,7 +203,7 @@
.conflict-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.conflict-side { border-radius: 10px; padding: 16px; }
.conflict-side { border-radius: var(--radius-md); padding: 16px; }
.conflict-official { background: rgba(34,197,94,.1); border: 1px solid rgba(34,197,94,.2); }
.conflict-observed { background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.2); }
@@ -242,14 +242,14 @@
.score-icon { font-size: 1.5rem; margin-bottom: 12px; }
.score-name { font-size: 15px; font-weight: 700; color: #1d1d1f; margin-bottom: 6px; }
.score-desc { font-size: 13px; color: var(--text-dim); line-height: 1.5; margin-bottom: 20px; }
.score-bar-wrap { background: rgba(0,0,0,.06); border-radius: 6px; height: 8px; margin-bottom: 10px; }
.score-bar { height: 8px; border-radius: 6px; background: linear-gradient(90deg, var(--blue), #5599ff); }
.score-bar-wrap { background: rgba(0,0,0,.06); border-radius: var(--radius-sm); height: 8px; margin-bottom: 10px; }
.score-bar { height: 8px; border-radius: var(--radius-sm); background: linear-gradient(90deg, var(--blue), #5599ff); }
.score-value { font-size: 28px; font-weight: 800; color: var(--blue); letter-spacing: -1px; }
.score-label-sub { font-size: 11px; color: var(--text-dim); margin-top: 2px; }
/* CTA BANNER */
.cta-banner {
background: #e8f0fe; border-radius: 16px; padding: 40px 48px;
background: #e8f0fe; border-radius: var(--radius-lg); padding: 40px 48px;
display: flex; align-items: center; gap: 48px; flex-wrap: wrap;
margin-top: 48px;
}
@@ -301,7 +301,7 @@
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social {
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px;
width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-md);
display: flex; align-items: center; justify-content: center;
transition: border-color 0.2s, background 0.2s;
}
@@ -329,7 +329,7 @@
.hero h1 { letter-spacing: -1px; }
}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius:8px;line-height:1.6;max-width:800px}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius: var(--radius-md);line-height:1.6;max-width:800px}
.lv-disclaimer a{color:rgba(0,0,0,.65);text-decoration:underline}
.lv-disclaimer--light{color:#888;background:#f5f5f5}
.lv-disclaimer--light a{color:#555}
@@ -378,7 +378,7 @@
<p class="section-sub">From single-asset verifications to portfolio-wide intelligence missions — deployed in 2472 hours.</p>
<div class="use-case-grid">
<div class="use-case-card" style="cursor:pointer;" onclick="location.href='#contact'" role="button" tabindex="0">
<span class="uc-icon">🔭</span>
<span class="uc-icon"></span>
<h3>Grid infrastructure surveys</h3>
<p>Visual documentation of substations, transmission lines, transformers, and distribution equipment across large network areas.</p>
@@ -390,13 +390,13 @@
<div class="learn-more-hint" style="margin-top:12px;font-size:13px;font-weight:700;color:#0066ff">Learn more →</div></div>
<div class="use-case-card" style="cursor:pointer;" onclick="location.href='#contact'" role="button" tabindex="0">
<span class="uc-icon">🗺️</span>
<span class="uc-icon"></span>
<h3>Network inspections</h3>
<p>Rapid visual inspection of above-ground network infrastructure — identifying vegetation encroachment, physical damage, and deterioration.</p>
<div class="learn-more-hint" style="margin-top:12px;font-size:13px;font-weight:700;color:#0066ff">Learn more →</div></div>
<div class="use-case-card" style="cursor:pointer;" onclick="location.href='#contact'" role="button" tabindex="0">
<span class="uc-icon"></span>
<span class="uc-icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M9 2L5 9H8L7 14L11 7H8L9 2Z" fill="#f59e0b"/></svg></span>
<h3>Capacity planning intelligence</h3>
<p>Ground-truth observation of urban growth, construction activity, and commercial development to support demand forecasting.</p>
@@ -457,12 +457,12 @@
<div class="conflict-side conflict-official">
<div class="conflict-side-label">Official narrative</div>
<ul>
<li>&#10003; Grid stability: 98.7% uptime</li><li>&#10003; Planned upgrade: 2028</li><li>&#10003; Demand forecast: within model range</li> </ul>
<li>&#100003 Grid stability: 98.7% uptime</li><li>&#100003 Planned upgrade: 2028</li><li>&#100003 Demand forecast: within model range</li> </ul>
</div>
<div class="conflict-side conflict-observed">
<div class="conflict-side-label">Observed reality</div>
<ul>
<li>&#10007; 14 new data centres in planning or construction</li><li>&#10007; Large residential development adding unmodelled load</li><li>&#10007; Substation compound showing deferred maintenance</li><li>&#10007; Vegetation encroachment on 6 transmission corridors</li> </ul>
<li>&#100007 <svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> 14 new data centres in planning or construction</li><li>&#100007 <svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Large residential development adding unmodelled load</li><li>&#100007 <svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Substation compound showing deferred maintenance</li><li>&#100007 <svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Vegetation encroachment on 6 transmission corridors</li> </ul>
</div>
</div>
<div class="conflict-footer">
@@ -499,7 +499,7 @@
<p class="section-sub">Every score is derived from field-verified observations, cross-referenced against official data, and launching August 2026.</p>
<div class="scores-grid" style="grid-template-columns:repeat(2,1fr)">
<div class="score-card" style="cursor:pointer;" onclick="location.href='#contact'" role="button" tabindex="0">
<div class="score-icon">🔧</div>
<div class="score-icon"></div>
<div class="score-name">Infrastructure Risk Index</div>
<div class="score-desc">Identifies areas where physical infrastructure is deteriorating faster than maintenance can address.</div>
<div class="score-bar-wrap">
@@ -510,7 +510,7 @@
<div class="learn-more-hint" style="margin-top:12px;font-size:13px;font-weight:700;color:#0066ff">Request access →</div></div>
<div class="score-card" style="cursor:pointer;" onclick="location.href='#contact'" role="button" tabindex="0">
<div class="score-icon">🌆</div>
<div class="score-icon"></div>
<div class="score-name">Urban Growth Index</div>
<div class="score-desc">Tracks urban expansion signals: new construction, population pressure, grid demand growth.</div>
<div class="score-bar-wrap">
@@ -77,7 +77,7 @@
background: #e94560;
color: white;
padding: 1rem 2rem;
border-radius: 8px;
border-radius: var(--radius-md);
text-decoration: none;
font-weight: 600;
margin: 0.5rem;
@@ -107,7 +107,7 @@
.pricing-card {
background: white;
border: 2px solid #e0e0e0;
border-radius: 12px;
border-radius: var(--radius-md);
padding: 2rem;
text-align: center;
transition: transform 0.3s, box-shadow 0.3s;
@@ -129,7 +129,7 @@
background: #e94560;
color: white;
padding: 0.25rem 1rem;
border-radius: 20px;
border-radius: var(--radius-xl);
font-size: 0.8rem;
font-weight: 600;
}
@@ -158,7 +158,7 @@
border-bottom: 1px solid #f0f0f0;
}
.pricing-features li::before {
content: " ";
content: "<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> ";
color: #4caf50;
font-weight: bold;
}
@@ -194,7 +194,7 @@
margin: 0 auto;
background: #f8f9fa;
padding: 2rem;
border-radius: 12px;
border-radius: var(--radius-md);
}
.form-group {
margin-bottom: 1.5rem;
@@ -209,14 +209,14 @@
width: 100%;
padding: 0.75rem;
border: 1px solid #ddd;
border-radius: 6px;
border-radius: var(--radius-sm);
font-size: 1rem;
}
.estimate-result {
background: #1a1a2e;
color: white;
padding: 1.5rem;
border-radius: 8px;
border-radius: var(--radius-md);
margin-top: 2rem;
text-align: center;
}
@@ -233,7 +233,7 @@
margin-bottom: 1.5rem;
padding: 1.5rem;
background: white;
border-radius: 8px;
border-radius: var(--radius-md);
}
.cta-section {
@@ -257,7 +257,7 @@
<div class="vs-grid">
<div class="vs-card pro">
<h3>🤖 AI Visual Inspection</h3>
<h3> AI Visual Inspection</h3>
<p>Cameras, drones, and AI models detect surface defects automatically. Continuous, scalable, and data-rich.</p>
<ul>
<li>5-10x faster for large structures</li>
@@ -269,7 +269,7 @@
</ul>
</div>
<div class="vs-card con">
<h3>🔧 Traditional Manual Inspection</h3>
<h3> Traditional Manual Inspection</h3>
<p>Certified engineers physically examine structures using scaffolding, ropes, and handheld tools.</p>
<ul>
<li>Superior for subsurface defects</li>
+10 -10
View File
@@ -115,7 +115,7 @@
background: #e94560;
color: white;
padding: 1rem 2rem;
border-radius: 8px;
border-radius: var(--radius-md);
text-decoration: none;
font-weight: 600;
margin: 0.5rem;
@@ -167,7 +167,7 @@
.step {
background: #f8f9fa;
padding: 1.5rem;
border-radius: 8px;
border-radius: var(--radius-md);
text-align: center;
}
.step-number {
@@ -189,7 +189,7 @@
background: white;
border: 2px solid #e0e0e0;
padding: 1.5rem;
border-radius: 8px;
border-radius: var(--radius-md);
}
.application-card h4 {
color: #1a1a2e;
@@ -204,7 +204,7 @@
margin-bottom: 1.5rem;
padding: 1.5rem;
background: white;
border-radius: 8px;
border-radius: var(--radius-md);
}
.faq-item h3 {
color: #1a1a2e;
@@ -441,27 +441,27 @@
<div class="applications-grid">
<div class="application-card">
<h4>🏛️ Municipal Government</h4>
<h4> Municipal Government</h4>
<p>Prioritize infrastructure investments, monitor public space conditions, and identify maintenance needs before they become expensive problems.</p>
</div>
<div class="application-card">
<h4>🏗️ Infrastructure Operators</h4>
<h4><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 14H14M4 14V8L8 4L12 8V14" stroke="#666" stroke-width="1.5" stroke-linejoin="round"/><path d="M6 14V10H10V14" stroke="#666" stroke-width="1.5"/></svg> Infrastructure Operators</h4>
<p>Monitor bridges, roads, and utilities continuously. Predict failures before they occur and optimize maintenance schedules.</p>
</div>
<div class="application-card">
<h4>🏢 Real Estate</h4>
<h4><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="3" y="2" width="10" height="12" rx="1" stroke="#666" stroke-width="1.5"/><path d="M6 5H7M6 8H7M6 11H7M9 5H10M9 8H10M9 11H10" stroke="#666" stroke-width="1.5" stroke-linecap="round"/></svg> Real Estate</h4>
<p>Assess property conditions at scale, identify growth zones, and validate site potential with objective data.</p>
</div>
<div class="application-card">
<h4>🛡️ Insurance</h4>
<h4> Insurance</h4>
<p>Validate claims, assess pre-loss risk, and monitor property conditions for underwriting decisions.</p>
</div>
<div class="application-card">
<h4> Utilities & Energy</h4>
<h4><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M9 2L5 9H8L7 14L11 7H8L9 2Z" fill="#f59e0b"/></svg> Utilities & Energy</h4>
<p>Map grid infrastructure, monitor solar/wind assets, and identify capacity constraints before outages occur.</p>
</div>
<div class="application-card">
<h4>🚛 Logistics</h4>
<h4> Logistics</h4>
<p>Optimize last-mile networks, assess depot locations, and monitor route conditions for fleet efficiency.</p>
</div>
</div>