feat(ui): redesign GeoIntel as Atlas Workbench
This commit is contained in:
+13
-5
@@ -113,11 +113,19 @@ The workbench uses a task-based shell instead of a single long panel stack. `App
|
||||
|
||||
The premium V1 presentation layer lives in `src/styles/premium.css`. It groups navigation by Workspace, Analyze and Deliver, removes the permanent inspector column, gives desktop/ultrawide workspaces stable readable widths and switches narrow screens to full-width content with a horizontally scrollable navigation rail. API calls and workflow state remain owned by the existing hooks.
|
||||
|
||||
The final visual polish layer lives in `src/styles/visual-polish.css`. It keeps
|
||||
the map dominant, places the results rail beside the map when the viewport is
|
||||
wide enough, preserves a readable two-column map flow on smaller desktops and
|
||||
stacks the context header and navigation explicitly on mobile. It contains no
|
||||
workflow or API logic.
|
||||
The Stitch-guided Atlas Workbench redesign lives in
|
||||
`src/styles/atlas-workbench.css`, with the shared icon navigation in
|
||||
`components/shell/WorkbenchNavigation.tsx`. The application uses one compact
|
||||
navigation rail, one context bar and task-specific work surfaces. The primary
|
||||
map keeps the theme rail, map and insight rail in one row on normal desktop
|
||||
and gives additional ultrawide space to the map instead of stretching text
|
||||
panels. Mobile keeps all workspaces in a horizontally scrollable rail and
|
||||
bounds the theme list so the map remains reachable early in the flow.
|
||||
|
||||
The visual contract, Stitch project and screen references are documented in
|
||||
`docs/UI_DESIGN_SYSTEM.md`. The redesign changes presentation and information
|
||||
hierarchy only; API, persistence and GIS behavior remain owned by the existing
|
||||
hooks and services.
|
||||
|
||||
## Current component boundaries
|
||||
|
||||
|
||||
Generated
+10
@@ -8,6 +8,7 @@
|
||||
"name": "geointel-frontend",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"lucide-react": "^1.25.0",
|
||||
"maplibre-gl": "^4.7.1",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
@@ -2415,6 +2416,15 @@
|
||||
"yallist": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/lucide-react": {
|
||||
"version": "1.25.0",
|
||||
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.25.0.tgz",
|
||||
"integrity": "sha512-/mdJTRbiwcLOQ1NZZK1amZF9rIZyvO18D6r9TngE6TG1NmqHgFuT4eE7Xrkm9UsXMbBJD1NlfwHVltCDWHrOTw==",
|
||||
"license": "ISC",
|
||||
"peerDependencies": {
|
||||
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/lz-string": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz",
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"lucide-react": "^1.25.0",
|
||||
"maplibre-gl": "^4.7.1",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
|
||||
+48
-78
@@ -1,7 +1,7 @@
|
||||
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||
import './styles/app.css'
|
||||
import './styles/premium.css'
|
||||
import './styles/visual-polish.css'
|
||||
import './styles/atlas-workbench.css'
|
||||
import { ChangeDetectionPanel } from './components/analysis/ChangeDetectionPanel'
|
||||
import { GeoAssistantPanel } from './components/assistant/GeoAssistantPanel'
|
||||
import { DatasetPanel } from './components/datasets/DatasetPanel'
|
||||
@@ -18,6 +18,11 @@ import { QualityResultsPanel } from './components/quality/QualityResultsPanel'
|
||||
import type { DatasetCreateResponse } from './types'
|
||||
import { ProviderPanel } from './components/providers/ProviderPanel'
|
||||
import { SegmentationLab } from './components/segmentation/SegmentationLab'
|
||||
import {
|
||||
WorkbenchNavigation,
|
||||
type WorkspaceNavigationGroup,
|
||||
type WorkspaceNavigationItem,
|
||||
} from './components/shell/WorkbenchNavigation'
|
||||
import { useChangeDetectionWorkflow } from './hooks/useChangeDetectionWorkflow'
|
||||
import { useDemoWorkflow } from './hooks/useDemoWorkflow'
|
||||
import { useCoverageResolver } from './hooks/useCoverageResolver'
|
||||
@@ -51,7 +56,7 @@ function isVectorDatasetType(datasetType: string): boolean {
|
||||
return datasetType === 'vector' || datasetType === 'geojson'
|
||||
}
|
||||
|
||||
const workspaceNavItems: Array<{ key: WorkspaceKey; label: string; description: string }> = [
|
||||
const workspaceNavItems: WorkspaceNavigationItem[] = [
|
||||
{ key: 'overview', label: 'Status', description: 'Beschikbaarheid en aandachtspunten' },
|
||||
{ key: 'data', label: 'Bronnen', description: 'Gebieden en ingeladen gegevens' },
|
||||
{ key: 'map', label: 'Kaart', description: 'Selecteren, uitlezen en vergelijken' },
|
||||
@@ -62,7 +67,7 @@ const workspaceNavItems: Array<{ key: WorkspaceKey; label: string; description:
|
||||
{ key: 'system', label: 'Beheer', description: 'Databronnen en systeemstatus' },
|
||||
]
|
||||
|
||||
const workspaceNavGroups: Array<{ label: string; keys: WorkspaceKey[] }> = [
|
||||
const workspaceNavGroups: WorkspaceNavigationGroup[] = [
|
||||
{ label: 'Verkennen', keys: ['map', 'data'] },
|
||||
{ label: 'Vragen', keys: ['assistant'] },
|
||||
{ label: 'Analyseren', keys: ['analysis', 'ai'] },
|
||||
@@ -779,71 +784,52 @@ function App(): JSX.Element {
|
||||
>
|
||||
Ga naar de werkruimte
|
||||
</a>
|
||||
<header className="workbench-topbar">
|
||||
<div className="brand-block">
|
||||
<span className="brand-mark" aria-hidden="true">GI</span>
|
||||
<div>
|
||||
<h1>GeoIntel</h1>
|
||||
<p>GeoAI-werkruimte · gebiedsdata en analyse</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="context-bar" aria-label="Actieve werkcontext">
|
||||
<div>
|
||||
<span>Werkruimte</span>
|
||||
<strong title={projectContextLabel}>{projectContextLabel}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>Gebied</span>
|
||||
<strong title={areaContextLabel}>{areaContextLabel}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>Bron</span>
|
||||
<strong title={datasetContextLabel}>{datasetContextLabel}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>Kaartlaag</span>
|
||||
<strong title={layerContextLabel}>{layerContextLabel}</strong>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<p className="sr-only" role="status" aria-live="polite">{workspaceStatusMessage}</p>
|
||||
{errorMessage ? <p className="error" role="alert">{errorMessage}</p> : null}
|
||||
|
||||
<div className="workbench-layout">
|
||||
<aside className="workbench-sidebar" aria-label="Navigatie van de werkruimte">
|
||||
<nav aria-label="Hoofdonderdelen">
|
||||
{workspaceNavGroups.map((group) => (
|
||||
<div className="nav-group" key={group.label}>
|
||||
<p className="nav-section-label">{group.label}</p>
|
||||
{group.keys.map((key) => {
|
||||
const item = workspaceNavItems.find((candidate) => candidate.key === key)
|
||||
if (!item) {
|
||||
return null
|
||||
}
|
||||
return (
|
||||
<button
|
||||
key={item.key}
|
||||
type="button"
|
||||
className={item.key === activeWorkspace ? 'nav-item nav-item-active' : 'nav-item'}
|
||||
onClick={() => setActiveWorkspace(item.key)}
|
||||
aria-current={item.key === activeWorkspace ? 'page' : undefined}
|
||||
aria-label={`Open ${item.label}: ${item.description}`}
|
||||
data-testid={`workspace-nav-${item.key}`}
|
||||
>
|
||||
<span>{item.label}</span>
|
||||
<small>{item.description}</small>
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
<WorkbenchNavigation
|
||||
activeWorkspace={activeWorkspace}
|
||||
groups={workspaceNavGroups}
|
||||
items={workspaceNavItems}
|
||||
onSelect={setActiveWorkspace}
|
||||
/>
|
||||
|
||||
<div className="workbench-stage">
|
||||
<header className="workbench-topbar">
|
||||
<div className="mobile-brand">
|
||||
<span className="brand-mark" aria-hidden="true">GI</span>
|
||||
<strong>GeoIntel</strong>
|
||||
</div>
|
||||
<div className="context-bar" aria-label="Actieve werkcontext">
|
||||
<div>
|
||||
<span>Werkruimte</span>
|
||||
<strong title={projectContextLabel}>{projectContextLabel}</strong>
|
||||
</div>
|
||||
))}
|
||||
</nav>
|
||||
</aside>
|
||||
<div>
|
||||
<span>Gebied</span>
|
||||
<strong title={areaContextLabel}>{areaContextLabel}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>Bron</span>
|
||||
<strong title={datasetContextLabel}>{datasetContextLabel}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>Kaartlaag</span>
|
||||
<strong title={layerContextLabel}>{layerContextLabel}</strong>
|
||||
</div>
|
||||
</div>
|
||||
<div className="context-health" aria-label="Systeemstatus">
|
||||
<span aria-hidden="true" />
|
||||
<strong>{workspaceDataLoading ? 'Laden' : errorMessage ? 'Aandacht' : 'Gereed'}</strong>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div className="workbench-content">
|
||||
|
||||
<main
|
||||
ref={workbenchMainRef}
|
||||
className="workbench-main"
|
||||
className={activeWorkspace === 'map' ? 'workbench-main workbench-main-map' : 'workbench-main'}
|
||||
id="workspace-main"
|
||||
tabIndex={-1}
|
||||
aria-busy={workspaceDataLoading}
|
||||
@@ -868,24 +854,6 @@ function App(): JSX.Element {
|
||||
) : null}
|
||||
</div>
|
||||
</div> : null}
|
||||
<div className="workspace-command-bar" aria-label="Snelkoppelingen">
|
||||
<div className="workspace-nav-cluster">
|
||||
{workspaceNavItems.slice(0, 5).map((item) => (
|
||||
<button
|
||||
key={item.key}
|
||||
type="button"
|
||||
className={item.key === activeWorkspace ? 'command-chip command-chip-active' : 'command-chip'}
|
||||
onClick={() => setActiveWorkspace(item.key)}
|
||||
aria-pressed={item.key === activeWorkspace}
|
||||
aria-label={`Ga naar ${item.label}`}
|
||||
>
|
||||
{item.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<span>{selectedProject ? `Actief bereik: ${projectContextLabel}` : 'De regionale gegevens worden ingeladen.'}</span>
|
||||
</div>
|
||||
|
||||
{activeWorkspace === 'overview' ? (
|
||||
<OverviewWorkspace
|
||||
selectedProject={selectedProject}
|
||||
@@ -1330,6 +1298,8 @@ function App(): JSX.Element {
|
||||
/>
|
||||
</aside>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -1868,8 +1868,8 @@ export function MapWorkspace({
|
||||
<header className="geo-explorer-header">
|
||||
<div>
|
||||
<p className="eyebrow">{activeScopeLabel} · geografische verkenner</p>
|
||||
<h2>Wat bevindt zich in dit gebied?</h2>
|
||||
<p>Kies een datathema, teken een rechthoek en lees de beschikbare gegevens meteen uit.</p>
|
||||
<h2>Gebied analyseren</h2>
|
||||
<p>Kies een thema, teken een rechthoek en lees de beschikbare gegevens.</p>
|
||||
</div>
|
||||
<div className="geo-analysis-mode" role="tablist" aria-label="Analyseperiode">
|
||||
<button
|
||||
@@ -1932,8 +1932,8 @@ export function MapWorkspace({
|
||||
<div className="geo-panel-heading">
|
||||
<span>1</span>
|
||||
<div>
|
||||
<h3>Kies een datathema</h3>
|
||||
<p>Dit zijn databronnen, geen AI-modellen.</p>
|
||||
<h3>Thema</h3>
|
||||
<p>Kies welke gegevens u wilt meten.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="geo-loaded-scope" aria-label="Ingeladen regiobereik">
|
||||
@@ -2307,7 +2307,7 @@ export function MapWorkspace({
|
||||
<div className="geo-panel-heading">
|
||||
<span>3</span>
|
||||
<div>
|
||||
<h3>Resultaten</h3>
|
||||
<h3>Inzichten</h3>
|
||||
<p>Alleen gemeten gegevens uit beschikbare bronnen.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
import {
|
||||
Activity,
|
||||
Bot,
|
||||
Database,
|
||||
Download,
|
||||
Map,
|
||||
ScanSearch,
|
||||
Settings,
|
||||
ShieldCheck,
|
||||
type LucideIcon,
|
||||
} from 'lucide-react'
|
||||
import type { WorkspaceKey } from '../overview/OverviewWorkspace'
|
||||
|
||||
export interface WorkspaceNavigationItem {
|
||||
key: WorkspaceKey
|
||||
label: string
|
||||
description: string
|
||||
}
|
||||
|
||||
export interface WorkspaceNavigationGroup {
|
||||
label: string
|
||||
keys: WorkspaceKey[]
|
||||
}
|
||||
|
||||
interface WorkbenchNavigationProps {
|
||||
activeWorkspace: WorkspaceKey
|
||||
groups: WorkspaceNavigationGroup[]
|
||||
items: WorkspaceNavigationItem[]
|
||||
onSelect: (workspace: WorkspaceKey) => void
|
||||
}
|
||||
|
||||
const workspaceIcons: Record<WorkspaceKey, LucideIcon> = {
|
||||
map: Map,
|
||||
data: Database,
|
||||
assistant: Bot,
|
||||
analysis: ShieldCheck,
|
||||
ai: ScanSearch,
|
||||
exports: Download,
|
||||
overview: Activity,
|
||||
system: Settings,
|
||||
}
|
||||
|
||||
export function WorkbenchNavigation({
|
||||
activeWorkspace,
|
||||
groups,
|
||||
items,
|
||||
onSelect,
|
||||
}: WorkbenchNavigationProps): JSX.Element {
|
||||
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>
|
||||
<span className="sidebar-brand-copy">
|
||||
<strong>GeoIntel</strong>
|
||||
<small>Atlas Workbench</small>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<nav aria-label="Hoofdonderdelen">
|
||||
{groups.map((group) => (
|
||||
<div className="nav-group" key={group.label}>
|
||||
<p className="nav-section-label">{group.label}</p>
|
||||
{group.keys.map((key) => {
|
||||
const item = items.find((candidate) => candidate.key === key)
|
||||
if (!item) {
|
||||
return null
|
||||
}
|
||||
const Icon = workspaceIcons[item.key]
|
||||
return (
|
||||
<button
|
||||
key={item.key}
|
||||
type="button"
|
||||
className={item.key === activeWorkspace ? 'nav-item nav-item-active' : 'nav-item'}
|
||||
onClick={() => onSelect(item.key)}
|
||||
aria-current={item.key === activeWorkspace ? 'page' : undefined}
|
||||
aria-label={`Open ${item.label}: ${item.description}`}
|
||||
title={item.description}
|
||||
data-testid={`workspace-nav-${item.key}`}
|
||||
>
|
||||
<Icon className="nav-item-icon" aria-hidden="true" strokeWidth={1.8} />
|
||||
<span>{item.label}</span>
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
))}
|
||||
</nav>
|
||||
</aside>
|
||||
)
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,515 +0,0 @@
|
||||
/* GeoIntel visual polish: a restrained map-first presentation layer. */
|
||||
|
||||
:root {
|
||||
--visual-canvas: #eef2f1;
|
||||
--visual-surface: #ffffff;
|
||||
--visual-surface-soft: #f7f9f8;
|
||||
--visual-ink: #172a2d;
|
||||
--visual-muted: #657477;
|
||||
--visual-line: #d7e1df;
|
||||
--visual-line-strong: #b8c8c4;
|
||||
--visual-teal: #087d73;
|
||||
--visual-teal-deep: #056057;
|
||||
--visual-teal-soft: #e7f4f1;
|
||||
--visual-amber: #a76616;
|
||||
--visual-amber-soft: #fff7e9;
|
||||
}
|
||||
|
||||
.workbench-shell {
|
||||
background: var(--visual-canvas);
|
||||
color: var(--visual-ink);
|
||||
}
|
||||
|
||||
.app-shell > header.workbench-topbar {
|
||||
min-height: 4.35rem;
|
||||
grid-template-columns: minmax(14rem, 17rem) minmax(0, 1fr);
|
||||
gap: 1.15rem;
|
||||
padding-inline: clamp(1rem, 1.5vw, 1.75rem);
|
||||
background: rgba(255, 255, 255, 0.985);
|
||||
}
|
||||
|
||||
.brand-block {
|
||||
gap: 0.8rem;
|
||||
}
|
||||
|
||||
.brand-mark {
|
||||
width: 2.35rem;
|
||||
height: 2.35rem;
|
||||
flex-basis: 2.35rem;
|
||||
border: 1px solid #20464a;
|
||||
border-radius: 8px;
|
||||
background: #153b40;
|
||||
box-shadow: 0 2px 5px rgba(21, 59, 64, 0.14);
|
||||
}
|
||||
|
||||
.brand-block h1 {
|
||||
color: var(--visual-ink);
|
||||
font-size: 1.08rem;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
.app-shell > header.workbench-topbar .brand-block p {
|
||||
color: #718083;
|
||||
font-size: 0.71rem;
|
||||
}
|
||||
|
||||
.context-bar {
|
||||
border-color: var(--visual-line);
|
||||
border-radius: 8px;
|
||||
background: var(--visual-surface-soft);
|
||||
}
|
||||
|
||||
.context-bar > div {
|
||||
min-height: 2.8rem;
|
||||
padding-inline: 0.8rem;
|
||||
}
|
||||
|
||||
.context-bar span {
|
||||
color: #708082;
|
||||
font-size: 0.6rem;
|
||||
letter-spacing: 0.03em;
|
||||
}
|
||||
|
||||
.context-bar strong {
|
||||
color: var(--visual-ink);
|
||||
font-size: 0.79rem;
|
||||
}
|
||||
|
||||
.workbench-layout {
|
||||
grid-template-columns: 13.25rem minmax(0, 1fr);
|
||||
height: calc(100dvh - 4.35rem);
|
||||
}
|
||||
|
||||
.workbench-sidebar {
|
||||
padding: 1rem 0.8rem;
|
||||
border-right-color: var(--visual-line);
|
||||
background: #f6f8f7;
|
||||
}
|
||||
|
||||
.workbench-sidebar nav {
|
||||
gap: 1.15rem;
|
||||
}
|
||||
|
||||
.nav-group {
|
||||
gap: 0.28rem;
|
||||
}
|
||||
|
||||
.nav-section-label {
|
||||
padding-inline: 0.62rem;
|
||||
color: #7c898a;
|
||||
font-size: 0.6rem;
|
||||
letter-spacing: 0.07em;
|
||||
}
|
||||
|
||||
.workbench-sidebar .nav-item {
|
||||
min-height: 2.55rem;
|
||||
border-radius: 7px;
|
||||
padding: 0.48rem 0.7rem;
|
||||
color: #3e5153;
|
||||
}
|
||||
|
||||
.workbench-sidebar .nav-item span {
|
||||
font-size: 0.82rem;
|
||||
font-weight: 750;
|
||||
}
|
||||
|
||||
.workbench-sidebar .nav-item-active {
|
||||
border-color: #b8d9d2;
|
||||
background: var(--visual-teal-soft);
|
||||
color: var(--visual-teal-deep);
|
||||
box-shadow: inset 3px 0 0 var(--visual-teal);
|
||||
}
|
||||
|
||||
.workbench-main {
|
||||
padding: 1.25rem clamp(1rem, 1.8vw, 2rem) 2rem;
|
||||
}
|
||||
|
||||
.workbench-main > * {
|
||||
width: min(100%, 118rem);
|
||||
}
|
||||
|
||||
.workspace-heading {
|
||||
margin-bottom: 1rem;
|
||||
padding-bottom: 0.82rem;
|
||||
border-bottom-color: var(--visual-line);
|
||||
}
|
||||
|
||||
.workspace-heading h2 {
|
||||
color: var(--visual-ink);
|
||||
font-size: 1.48rem;
|
||||
}
|
||||
|
||||
.workspace-heading-actions > p {
|
||||
color: var(--visual-muted);
|
||||
}
|
||||
|
||||
/* Normal desktop keeps the three map steps in one purposeful working row. */
|
||||
@media (min-width: 1181px) {
|
||||
.geo-explorer-layout {
|
||||
grid-template-columns: minmax(15rem, 16.5rem) minmax(0, 1fr) minmax(17rem, 19rem);
|
||||
gap: 0.85rem;
|
||||
height: clamp(36rem, calc(100dvh - 9.8rem), 68rem);
|
||||
min-height: 36rem;
|
||||
}
|
||||
|
||||
.geo-theme-panel,
|
||||
.geo-results-panel,
|
||||
.geo-map-stage {
|
||||
border-color: var(--visual-line);
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 1px 2px rgba(23, 42, 45, 0.035);
|
||||
}
|
||||
|
||||
.geo-results-panel {
|
||||
grid-column: auto;
|
||||
display: flex;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.geo-map-toolbar {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.geo-map-actions {
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1800px) {
|
||||
.workbench-layout {
|
||||
grid-template-columns: 14rem minmax(0, 1fr);
|
||||
}
|
||||
|
||||
.workbench-main {
|
||||
padding-inline: clamp(1.5rem, 2.2vw, 3rem);
|
||||
}
|
||||
|
||||
.geo-explorer-layout {
|
||||
grid-template-columns: minmax(17rem, 19rem) minmax(42rem, 1fr) minmax(21rem, 24rem);
|
||||
}
|
||||
}
|
||||
|
||||
.geo-explorer {
|
||||
gap: 0.9rem;
|
||||
}
|
||||
|
||||
.geo-explorer-header {
|
||||
gap: 1.25rem;
|
||||
border-bottom-color: var(--visual-line);
|
||||
padding-bottom: 0.95rem;
|
||||
}
|
||||
|
||||
.geo-explorer-header h2 {
|
||||
color: var(--visual-ink);
|
||||
font-size: clamp(1.45rem, 1.8vw, 1.9rem);
|
||||
font-weight: 760;
|
||||
}
|
||||
|
||||
.geo-explorer-header p:last-child {
|
||||
color: var(--visual-muted);
|
||||
font-size: 0.86rem;
|
||||
}
|
||||
|
||||
.geo-analysis-mode {
|
||||
border-color: var(--visual-line-strong);
|
||||
background: #f3f7f6;
|
||||
}
|
||||
|
||||
.geo-analysis-mode button {
|
||||
color: #687879;
|
||||
}
|
||||
|
||||
.geo-analysis-mode button.active {
|
||||
color: var(--visual-teal-deep);
|
||||
box-shadow: 0 1px 4px rgba(23, 42, 45, 0.11);
|
||||
}
|
||||
|
||||
.geo-panel-heading > span {
|
||||
background: var(--visual-ink);
|
||||
}
|
||||
|
||||
.geo-panel-heading h3,
|
||||
.geo-results-title-row h4 {
|
||||
color: var(--visual-ink);
|
||||
}
|
||||
|
||||
.geo-panel-heading p,
|
||||
.geo-results-empty p {
|
||||
color: var(--visual-muted);
|
||||
}
|
||||
|
||||
.geo-theme-panel,
|
||||
.geo-results-panel,
|
||||
.geo-map-stage {
|
||||
background: var(--visual-surface);
|
||||
}
|
||||
|
||||
.geo-theme-panel,
|
||||
.geo-results-panel {
|
||||
padding: 0.85rem;
|
||||
}
|
||||
|
||||
.geo-theme-list {
|
||||
gap: 0.42rem;
|
||||
scrollbar-gutter: stable;
|
||||
}
|
||||
|
||||
.geo-theme-option {
|
||||
min-height: 3.35rem;
|
||||
border-color: #dce6e3;
|
||||
border-radius: 7px;
|
||||
padding: 0.52rem 0.56rem;
|
||||
}
|
||||
|
||||
.geo-theme-option:not(:disabled):hover {
|
||||
border-color: #98c5ba;
|
||||
background: #f5faf8;
|
||||
}
|
||||
|
||||
.geo-theme-option-active {
|
||||
border-color: #6da99b;
|
||||
background: var(--visual-teal-soft);
|
||||
box-shadow: inset 3px 0 0 var(--visual-teal);
|
||||
}
|
||||
|
||||
.geo-theme-option strong {
|
||||
color: #233639;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.geo-theme-option small {
|
||||
color: #6e7d7f;
|
||||
font-size: 0.66rem;
|
||||
}
|
||||
|
||||
.geo-theme-option i {
|
||||
color: var(--visual-teal-deep);
|
||||
font-size: 0.57rem;
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
|
||||
.geo-loaded-scope {
|
||||
border-color: #d6e5e1;
|
||||
border-radius: 7px;
|
||||
background: #f6faf9;
|
||||
}
|
||||
|
||||
.geo-source-summary {
|
||||
border-top-color: var(--visual-line);
|
||||
}
|
||||
|
||||
.geo-source-summary strong {
|
||||
color: #223639;
|
||||
}
|
||||
|
||||
.geo-map-stage {
|
||||
border-color: var(--visual-line-strong);
|
||||
box-shadow: 0 3px 14px rgba(23, 42, 45, 0.08);
|
||||
}
|
||||
|
||||
.geo-map-toolbar {
|
||||
border-bottom-color: var(--visual-line);
|
||||
background: #fbfcfc;
|
||||
}
|
||||
|
||||
.geo-map-actions .primary-action,
|
||||
.geo-result-next-actions .primary-action {
|
||||
border-color: var(--visual-teal);
|
||||
background: var(--visual-teal);
|
||||
}
|
||||
|
||||
.geo-map-actions .primary-action:hover:not(:disabled),
|
||||
.geo-result-next-actions .primary-action:hover:not(:disabled) {
|
||||
border-color: var(--visual-teal-deep);
|
||||
background: var(--visual-teal-deep);
|
||||
}
|
||||
|
||||
.geo-map-canvas {
|
||||
background: #dce9e6;
|
||||
}
|
||||
|
||||
.geo-map-legend {
|
||||
border-color: rgba(23, 42, 45, 0.18);
|
||||
border-radius: 6px;
|
||||
background: rgba(255, 255, 255, 0.96);
|
||||
box-shadow: 0 3px 12px rgba(23, 42, 45, 0.12);
|
||||
}
|
||||
|
||||
.geo-results-panel {
|
||||
scrollbar-gutter: stable;
|
||||
}
|
||||
|
||||
.geo-results-empty,
|
||||
.geo-results-loading {
|
||||
border-color: #cbdad6;
|
||||
background: #fafcfb;
|
||||
}
|
||||
|
||||
.geo-primary-metrics > div {
|
||||
border-color: #dbe6e3;
|
||||
border-radius: 6px;
|
||||
background: #f7faf9;
|
||||
}
|
||||
|
||||
.geo-primary-metrics > div:first-child {
|
||||
border-top: 2px solid var(--visual-teal);
|
||||
}
|
||||
|
||||
.geo-primary-metrics strong {
|
||||
color: #183c3b;
|
||||
}
|
||||
|
||||
.geo-theme-result-row {
|
||||
border-color: #e0e9e7;
|
||||
background: #fbfcfc;
|
||||
}
|
||||
|
||||
.geo-theme-result-row:hover {
|
||||
border-color: #b9d4cc;
|
||||
background: #f4faf8;
|
||||
}
|
||||
|
||||
.geo-result-next-actions {
|
||||
border-color: #c4ddd7;
|
||||
border-radius: 7px;
|
||||
background: #f2faf7;
|
||||
}
|
||||
|
||||
.geo-result-next-actions strong {
|
||||
color: #174a42;
|
||||
}
|
||||
|
||||
.geo-result-next-actions small {
|
||||
color: #607773;
|
||||
}
|
||||
|
||||
@media (max-width: 1180px) and (min-width: 921px) {
|
||||
.geo-explorer-layout {
|
||||
grid-template-columns: minmax(15rem, 17rem) minmax(0, 1fr);
|
||||
grid-template-rows: auto auto;
|
||||
height: auto;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.geo-results-panel {
|
||||
grid-column: 1 / -1;
|
||||
display: grid;
|
||||
grid-template-columns: minmax(13rem, 0.7fr) minmax(0, 1.3fr);
|
||||
align-items: start;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1359px) and (min-width: 921px) {
|
||||
/* Keep the map readable until the viewport can support a result rail. */
|
||||
.geo-explorer-layout {
|
||||
grid-template-columns: minmax(15rem, 17rem) minmax(28rem, 1fr);
|
||||
grid-template-rows: clamp(34rem, calc(100dvh - 10rem), 54rem) auto;
|
||||
height: auto;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.geo-results-panel {
|
||||
grid-column: 1 / -1;
|
||||
display: grid;
|
||||
grid-template-columns: minmax(13rem, 0.7fr) minmax(0, 1.3fr);
|
||||
align-items: start;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1799px) and (min-width: 1360px) {
|
||||
/* A two-column work row keeps the selection step wide at standard desktop sizes. */
|
||||
.geo-explorer-layout {
|
||||
grid-template-columns: minmax(16rem, 18rem) minmax(34rem, 1fr);
|
||||
grid-template-rows: clamp(36rem, calc(100dvh - 10rem), 58rem) auto;
|
||||
height: auto;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.geo-results-panel {
|
||||
grid-column: 1 / -1;
|
||||
display: grid;
|
||||
grid-template-columns: minmax(14rem, 0.7fr) minmax(0, 1.3fr);
|
||||
align-items: start;
|
||||
overflow: visible;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 920px) {
|
||||
.app-shell > header.workbench-topbar {
|
||||
position: relative;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 0.65rem;
|
||||
min-height: auto;
|
||||
}
|
||||
|
||||
.app-shell > header.workbench-topbar {
|
||||
padding-block: 0.7rem;
|
||||
}
|
||||
|
||||
.workbench-sidebar {
|
||||
background: #ffffff;
|
||||
scrollbar-color: #a9bfba transparent;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
.workbench-sidebar::-webkit-scrollbar {
|
||||
height: 4px;
|
||||
}
|
||||
|
||||
.workbench-sidebar::-webkit-scrollbar-thumb {
|
||||
border-radius: 4px;
|
||||
background: #a9bfba;
|
||||
}
|
||||
|
||||
.workbench-sidebar::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.workbench-sidebar .nav-item {
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
.workbench-sidebar .nav-item-active {
|
||||
border-color: #b8d9d2;
|
||||
box-shadow: inset 0 -3px 0 var(--visual-teal);
|
||||
}
|
||||
|
||||
.geo-explorer-layout {
|
||||
gap: 0.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 620px) {
|
||||
.workbench-main {
|
||||
padding-inline: 0.75rem;
|
||||
}
|
||||
|
||||
.geo-explorer-header {
|
||||
gap: 0.8rem;
|
||||
}
|
||||
|
||||
.geo-explorer-header h2 {
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
|
||||
.geo-theme-panel,
|
||||
.geo-results-panel {
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
.geo-map-stage {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.geo-map-canvas .map-container {
|
||||
min-height: 25rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.workbench-shell button {
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user