feat: establish GeoIntel and ITWorx branding
GeoIntel release gates / Compile, test, contracts and builds (push) Canceled after 0s
GeoIntel release gates / Python and npm vulnerability policy (push) Canceled after 0s
GeoIntel release gates / GIS image, SBOM and container scan (push) Canceled after 0s

This commit is contained in:
Jens
2026-07-26 02:51:58 +02:00
parent b65db69bfc
commit d79f751773
18 changed files with 126 additions and 45 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 106 KiB

+4 -18
View File
@@ -1,20 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" role="img" aria-labelledby="title desc">
<title id="title">GeoIntel Kempen</title>
<desc id="desc">Hexagonal map and intelligence mark for the GeoIntel workbench.</desc>
<defs>
<linearGradient id="bg" x1="32" x2="224" y1="224" y2="32" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#0f766e"/>
<stop offset="0.52" stop-color="#2563eb"/>
<stop offset="1" stop-color="#111827"/>
</linearGradient>
</defs>
<rect width="256" height="256" rx="48" fill="url(#bg)"/>
<path d="M128 28 213 77v102l-85 49-85-49V77z" fill="#ffffff" opacity="0.12"/>
<path d="M128 45 198 86v84l-70 41-70-41V86z" fill="none" stroke="#e0f2fe" stroke-width="10"/>
<path d="M79 91 128 63l49 28v57l-49 28-49-28z" fill="#ecfeff" opacity="0.95"/>
<path d="M86 145c18-10 37-10 56 0 17 9 33 9 48 0v26l-62 36-62-36v-19c7 0 14-2 20-7z" fill="#14b8a6" opacity="0.9"/>
<path d="M66 106c17 9 34 9 51 0 20-11 40-11 60 0 5 3 10 5 15 6" fill="none" stroke="#2563eb" stroke-width="9" stroke-linecap="round"/>
<circle cx="128" cy="120" r="19" fill="#111827"/>
<circle cx="128" cy="120" r="8" fill="#67e8f9"/>
<path d="M128 101v-24M128 163v-24M109 120H85M171 120h-24" stroke="#111827" stroke-width="8" stroke-linecap="round"/>
<title id="title">GeoIntel Atlas</title>
<desc id="desc">Een geografische lens met contourlijnen, kompasrichting en locatiepunt.</desc>
<defs><linearGradient id="bg" x1="30" y1="18" x2="222" y2="238" gradientUnits="userSpaceOnUse"><stop stop-color="#10564E"/><stop offset="1" stop-color="#062824"/></linearGradient><linearGradient id="land" x1="69" y1="54" x2="190" y2="207" gradientUnits="userSpaceOnUse"><stop stop-color="#D9FFF7"/><stop offset="1" stop-color="#62D5C0"/></linearGradient><filter id="shadow" x="-25%" y="-25%" width="150%" height="150%"><feDropShadow dx="0" dy="9" stdDeviation="9" flood-color="#001C19" flood-opacity=".35"/></filter></defs>
<rect width="256" height="256" rx="56" fill="url(#bg)"/><path d="M26 76C63 57 92 68 120 56s59-33 110-14M20 116c41-17 70-9 101-22s68-29 116-9M20 158c38-16 73-6 108-21s67-22 109-7M33 202c40-16 65-5 99-18s57-17 90-8" fill="none" stroke="#A9EBDD" stroke-opacity=".16" stroke-width="8" stroke-linecap="round"/><g filter="url(#shadow)"><path d="M128 35c51 0 93 41 93 93 0 51-42 93-93 93s-93-42-93-93c0-52 42-93 93-93Z" fill="#082F2B" stroke="#86E4D2" stroke-width="9"/><path d="M128 58c38 0 70 31 70 70 0 38-32 70-70 70s-70-32-70-70c0-39 32-70 70-70Z" fill="none" stroke="#D8FFF7" stroke-opacity=".22" stroke-width="5" stroke-dasharray="2 12" stroke-linecap="round"/><path d="M86 156c18-8 29-4 44-11 16-8 24-22 43-23M79 135c19-8 33-4 48-12 16-8 24-21 42-24M83 113c13-6 27-4 39-10 15-8 25-19 38-22" fill="none" stroke="url(#land)" stroke-width="8" stroke-linecap="round"/><path d="M128 72v14M128 170v14M72 128h14M170 128h14" stroke="#E9FFF9" stroke-width="7" stroke-linecap="round"/><path d="m128 92 12 25 25 11-25 12-12 25-12-25-25-12 25-11 12-25Z" fill="#EFCB72" stroke="#FFF5CE" stroke-width="4" stroke-linejoin="round"/><circle cx="128" cy="128" r="9" fill="#082F2B" stroke="#FFF" stroke-width="4"/></g>
</svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

+15
View File
@@ -11289,3 +11289,18 @@ Validation:
- frontend TypeScript check and production build: passed;
- frontend dependency audit: zero vulnerabilities at the high threshold;
- React review: named isolated component, derived configuration, no new state/effects, decorative content hidden from assistive technology.
## 2026-07-26 - GeoIntel and ITWorx brand system
Implemented:
- replaced the former letter-based and mountain icons with one original GeoIntel Atlas mark combining a geographic lens, contour lines, compass direction and a measured location point;
- applied the same source mark to the workbench rail, mobile header, landing header, SVG favicon, PNG browser fallback, Apple touch icon and Unraid application icon;
- added the approved ITWorx.tech wordmark as a restrained maker signature in the workbench rail and public landing footer, explicitly crediting Jens without competing with the GeoIntel product identity;
- kept the product icon vector-native for crisp rendering from favicon to server tile and generated matching PNG derivatives from that canonical SVG.
Validation:
- visually inspected the canonical 512 px render and the 32 px favicon derivative;
- verified desktop and mobile landing-page renders with the new GeoIntel and ITWorx assets present, meaningful page content and no Vite error overlay; expected API 500 responses remained limited to the absent local backend;
- React review confirmed named, isolated brand components and accessible decorative versus meaningful image semantics.
+8
View File
@@ -890,3 +890,11 @@ This file now starts with the current implementation status. Older preparation/b
selection-size budgets.
- [ ] Provision additional national-scale baseline datasets before exposing
more overview themes; do not synthesize regional detail at national scale.
# Sprint 226 - GeoIntel and ITWorx brand system
- [x] Replace the legacy GI/mountain artwork with a distinctive atlas and location mark.
- [x] Use one canonical SVG across in-product branding and the browser favicon.
- [x] Generate matching 32 px, Apple touch and Unraid PNG derivatives.
- [x] Add the approved ITWorx.tech identity as a subtle Jens maker signature.
- [ ] Revisit alternative lockups only when an approved GeoIntel brand guide exists.
+2
View File
@@ -4,6 +4,8 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" type="image/svg+xml" href="/geointel-icon.svg" />
<link rel="icon" type="image/png" sizes="32x32" href="/geointel-icon-32.png" />
<link rel="apple-touch-icon" sizes="180x180" href="/geointel-icon-180.png" />
<meta name="theme-color" content="#087266" />
<meta name="description" content="GeoIntel Atlas is de operationele GeoAI-workbench voor België en de Belgische Noordzee." />
<title>GeoIntel</title>
Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 106 KiB

+22 -15
View File
@@ -1,20 +1,27 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" role="img" aria-labelledby="title desc">
<title id="title">GeoIntel Kempen</title>
<desc id="desc">Hexagonal map and intelligence mark for the GeoIntel workbench.</desc>
<title id="title">GeoIntel Atlas</title>
<desc id="desc">Een geografische lens met contourlijnen, kompasrichting en locatiepunt.</desc>
<defs>
<linearGradient id="bg" x1="32" x2="224" y1="224" y2="32" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#0f766e"/>
<stop offset="0.52" stop-color="#2563eb"/>
<stop offset="1" stop-color="#111827"/>
<linearGradient id="bg" x1="30" y1="18" x2="222" y2="238" gradientUnits="userSpaceOnUse">
<stop stop-color="#10564E"/>
<stop offset="1" stop-color="#062824"/>
</linearGradient>
<linearGradient id="land" x1="69" y1="54" x2="190" y2="207" gradientUnits="userSpaceOnUse">
<stop stop-color="#D9FFF7"/>
<stop offset="1" stop-color="#62D5C0"/>
</linearGradient>
<filter id="shadow" x="-25%" y="-25%" width="150%" height="150%">
<feDropShadow dx="0" dy="9" stdDeviation="9" flood-color="#001C19" flood-opacity=".35"/>
</filter>
</defs>
<rect width="256" height="256" rx="48" fill="url(#bg)"/>
<path d="M128 28 213 77v102l-85 49-85-49V77z" fill="#ffffff" opacity="0.12"/>
<path d="M128 45 198 86v84l-70 41-70-41V86z" fill="none" stroke="#e0f2fe" stroke-width="10"/>
<path d="M79 91 128 63l49 28v57l-49 28-49-28z" fill="#ecfeff" opacity="0.95"/>
<path d="M86 145c18-10 37-10 56 0 17 9 33 9 48 0v26l-62 36-62-36v-19c7 0 14-2 20-7z" fill="#14b8a6" opacity="0.9"/>
<path d="M66 106c17 9 34 9 51 0 20-11 40-11 60 0 5 3 10 5 15 6" fill="none" stroke="#2563eb" stroke-width="9" stroke-linecap="round"/>
<circle cx="128" cy="120" r="19" fill="#111827"/>
<circle cx="128" cy="120" r="8" fill="#67e8f9"/>
<path d="M128 101v-24M128 163v-24M109 120H85M171 120h-24" stroke="#111827" stroke-width="8" stroke-linecap="round"/>
<rect width="256" height="256" rx="56" fill="url(#bg)"/>
<path d="M26 76C63 57 92 68 120 56s59-33 110-14M20 116c41-17 70-9 101-22s68-29 116-9M20 158c38-16 73-6 108-21s67-22 109-7M33 202c40-16 65-5 99-18s57-17 90-8" fill="none" stroke="#A9EBDD" stroke-opacity=".16" stroke-width="8" stroke-linecap="round"/>
<g filter="url(#shadow)">
<path d="M128 35c51 0 93 41 93 93 0 51-42 93-93 93s-93-42-93-93c0-52 42-93 93-93Z" fill="#082F2B" stroke="#86E4D2" stroke-width="9"/>
<path d="M128 58c38 0 70 31 70 70 0 38-32 70-70 70s-70-32-70-70c0-39 32-70 70-70Z" fill="none" stroke="#D8FFF7" stroke-opacity=".22" stroke-width="5" stroke-dasharray="2 12" stroke-linecap="round"/>
<path d="M86 156c18-8 29-4 44-11 16-8 24-22 43-23M79 135c19-8 33-4 48-12 16-8 24-21 42-24M83 113c13-6 27-4 39-10 15-8 25-19 38-22" fill="none" stroke="url(#land)" stroke-width="8" stroke-linecap="round"/>
<path d="M128 72v14M128 170v14M72 128h14M170 128h14" stroke="#E9FFF9" stroke-width="7" stroke-linecap="round"/>
<path d="m128 92 12 25 25 11-25 12-12 25-12-25-25-12 25-11 12-25Z" fill="#EFCB72" stroke="#FFF5CE" stroke-width="4" stroke-linejoin="round"/>
<circle cx="128" cy="128" r="9" fill="#082F2B" stroke="#FFF" stroke-width="4"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

+1 -1
View File
@@ -812,7 +812,7 @@ function WorkbenchApp({ username, loggingOut, onLogout }: WorkbenchAppProps): JS
<div className="workbench-stage">
<header className="workbench-topbar">
<div className="mobile-brand">
<span className="brand-mark" aria-hidden="true">GI</span>
<img className="brand-mark geointel-mark" src="/geointel-icon.svg" alt="" aria-hidden="true" />
<strong>GeoIntel</strong>
</div>
<div className="context-bar" aria-label="Actieve werkcontext">
@@ -27,6 +27,7 @@ describe('LandingPage', () => {
render(<LandingPage onAuthenticated={onAuthenticated} />)
expect(screen.getByRole('heading', { name: /Operationele GIS-analyse/i })).toBeTruthy()
expect(screen.getByRole('img', { name: 'ITWorx.tech' })).toBeTruthy()
fireEvent.change(screen.getByLabelText('Gebruikersnaam'), { target: { value: 'operator' } })
fireEvent.change(screen.getByLabelText('Wachtwoord'), { target: { value: 'correct' } })
fireEvent.click(screen.getAllByRole('button', { name: 'Inloggen' })[1])
+7 -2
View File
@@ -16,6 +16,8 @@ import { login } from '../../services/api/auth'
import type { AuthSession } from '../../services/api/auth'
import { formatError } from '../../lib/formatError'
import '../../styles/landing.css'
import { GeoIntelMark } from '../brand/GeoIntelBrand'
import { ItWorxSignature } from '../brand/ItWorxSignature'
interface LandingPageProps {
onAuthenticated: (session: AuthSession) => void
@@ -86,7 +88,7 @@ export function LandingPage({ onAuthenticated, serviceError = null }: LandingPag
<a className="landing-skip-link" href="#login-panel">Ga naar aanmelden</a>
<header className="landing-header">
<a className="landing-brand" href="#top" aria-label="GeoIntel Atlas startpagina">
<span className="landing-brand-mark" aria-hidden="true">GI</span>
<GeoIntelMark className="landing-brand-mark" />
<span>GeoIntel Atlas</span>
</a>
<button
@@ -222,7 +224,10 @@ export function LandingPage({ onAuthenticated, serviceError = null }: LandingPag
<footer className="landing-footer">
<div><strong>GeoIntel Atlas Workbench</strong><p>Operationele GIS-analyse voor België en de Belgische Noordzee.</p></div>
<p>© {new Date().getFullYear()} GeoIntel · Interne operatoromgeving</p>
<div className="landing-footer-ownership">
<ItWorxSignature />
<p>© {new Date().getFullYear()} GeoIntel · Interne operatoromgeving</p>
</div>
</footer>
</div>
)
@@ -0,0 +1,14 @@
interface GeoIntelMarkProps {
className?: string
}
export function GeoIntelMark({ className = '' }: GeoIntelMarkProps): JSX.Element {
return (
<img
className={`geointel-mark ${className}`.trim()}
src="/geointel-icon.svg"
alt=""
aria-hidden="true"
/>
)
}
@@ -0,0 +1,8 @@
export function ItWorxSignature(): JSX.Element {
return (
<div className="itworx-signature" aria-label="Ontwikkeld door Jens van ITWorx.tech">
<span>Ontwikkeld door Jens</span>
<img src="/itworx-wordmark.png" alt="ITWorx.tech" />
</div>
)
}
@@ -10,6 +10,8 @@ import {
type LucideIcon,
} from 'lucide-react'
import type { WorkspaceKey } from '../overview/OverviewWorkspace'
import { GeoIntelMark } from '../brand/GeoIntelBrand'
import { ItWorxSignature } from '../brand/ItWorxSignature'
export interface WorkspaceNavigationItem {
key: WorkspaceKey
@@ -49,7 +51,7 @@ export function WorkbenchNavigation({
return (
<aside className="workbench-sidebar" aria-label="Navigatie van de werkruimte">
<div className="sidebar-brand" aria-label="GeoIntel">
<span className="brand-mark" aria-hidden="true">GI</span>
<GeoIntelMark className="brand-mark" />
<span className="sidebar-brand-copy">
<strong>GeoIntel</strong>
<small>Atlas Workbench</small>
@@ -85,6 +87,7 @@ export function WorkbenchNavigation({
</div>
))}
</nav>
<ItWorxSignature />
</aside>
)
}
+33 -2
View File
@@ -74,6 +74,8 @@ body {
/* Brand rail */
.workbench-sidebar {
display: flex;
flex-direction: column;
border: 0;
padding: 1rem 0.85rem 1.25rem;
color: rgba(255, 255, 255, 0.76);
@@ -100,13 +102,42 @@ body {
height: 2.65rem;
border: 1px solid rgba(173, 238, 227, 0.44);
border-radius: 0.8rem;
color: #dffaf5;
background: rgba(255, 255, 255, 0.09);
object-fit: cover;
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.14),
0 10px 24px rgba(0, 0, 0, 0.16);
}
.workbench-sidebar > nav {
flex: 1 1 auto;
}
.itworx-signature {
display: grid;
gap: 0.38rem;
}
.itworx-signature > span {
color: rgba(223, 250, 245, 0.48);
font-size: 0.58rem;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.itworx-signature img {
display: block;
width: 7.4rem;
height: auto;
}
.workbench-sidebar > .itworx-signature {
margin: 1.25rem 0.45rem 0;
border-top: 1px solid rgba(255, 255, 255, 0.09);
padding-top: 1rem;
opacity: 0.88;
}
.sidebar-brand-copy {
display: grid;
gap: 0.12rem;
+7 -6
View File
@@ -71,14 +71,10 @@ body.landing-body {
}
.landing-brand-mark {
display: grid;
width: 2rem;
height: 2rem;
place-items: center;
border: 1px solid #8ec7bc;
border-radius: 4px;
background: #e7faf6;
font-size: 0.76rem;
border-radius: 0.48rem;
box-shadow: 0 5px 14px rgba(6, 40, 36, 0.19);
}
.landing-nav {
@@ -321,6 +317,10 @@ body.landing-body {
.landing-footer { display: flex; gap: 2rem; align-items: center; justify-content: space-between; padding: 2rem clamp(1.25rem, 4vw, 4.5rem); background: #061815; color: #fff; }
.landing-footer strong { font-family: "Manrope", sans-serif; }
.landing-footer p { margin: 0.35rem 0 0; color: rgba(255, 255, 255, 0.58); font-size: 0.7rem; }
.landing-footer-ownership { display: grid; gap: 0.65rem; justify-items: end; text-align: right; }
.landing-footer .itworx-signature { display: flex; gap: 0.7rem; align-items: center; }
.landing-footer .itworx-signature > span { color: rgba(255, 255, 255, 0.48); font-size: 0.58rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; }
.landing-footer .itworx-signature img { width: 7.7rem; height: auto; filter: drop-shadow(0 3px 8px rgba(0, 0, 0, 0.25)); }
.landing-auth-loading {
display: grid;
@@ -363,6 +363,7 @@ body.landing-body {
.landing-workflow { padding: 3.5rem 1rem; }
.landing-workflow-map { min-height: 28rem; }
.landing-footer { align-items: flex-start; flex-direction: column; }
.landing-footer-ownership { justify-items: start; text-align: left; }
}
@media (prefers-reduced-motion: reduce) {