Files
lidar_rendu/lidar_pipeline/web/map.css
Antoine Jacquin 72b10cd228 Corriger la revue stage1 de la refonte : fiche de dalle, thème du fond, cadre d'export, tokens de couleur
Retour/Échap depuis la fiche détaillée ne retouche plus la hauteur du volet
téléphone (openTab remplacé par un rétablissement direct de l'onglet) ; le
fond OSM suit le thème sauf choix explicite (migration du localStorage et des
défauts serveur en v3, plus de dark:true hérité de l'ex-webapp) et part du
bon thème dès sa création (plus de flash sombre) ; l'onglet Export mémorisé ne
recadre plus la vue au premier rendu (lien partagé préservé) et referme son
cadre pendant que la fiche détaillée est ouverte ; la qualité affichée suit le
nom réel de la dalle rendue ; un second clic referme la fiche au lieu de
déplacer son contour derrière elle ; couleurs en dur restantes tokenisées
(--tile-sel, --gps, .gen-log) et lisibles en thème clair ; localStorage
corrompu (lidar-panel, lidar-print) n'y casse plus l'interface.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-27 18:52:30 +02:00

275 lines
18 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* Carte LiDAR — tuiles XYZ, habillage minimal type carte grand public. */
/* Réglages communs : toute règle y puise (couleurs, texte, arrondis,
espacements, ombres, superpositions). Deux palettes : claire par défaut,
sombre si le système le demande (sauf clair forcé) ou si elle est forcée. */
:root {
color-scheme: light;
--bg: #eef0f3;
--surface: rgba(251, 251, 250, 0.96);
--surface-2: #eef0f3; /* champs, segments, boutons neutres */
--border: #d6d9de;
--text: #1d232b;
--muted: #5b6573;
--accent: #1f6fb2;
--on-accent: #ffffff;
--track: #cfd4da;
--print: #d99a00;
--gen-running: #d9730d;
--gen-pending: #0b8fc0;
--gen-failed: #c8452c;
--gen-sel: var(--accent);
/* Contour de sélection de dalle : plus sombre en thème clair (lisible sur
le fond OSM clair), jaune franc en thème sombre. */
--tile-sel: #b8860b;
--gps: #39d98a;
--fs-xs: 11px; --fs-sm: 12px; --fs-md: 13px; --fs-lg: 16px;
--r-sm: 6px; --r-md: 10px; --r-full: 999px;
--sp-1: 4px; --sp-2: 8px; --sp-3: 12px; --sp-4: 16px;
--shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.18);
--shadow-lg: 0 6px 24px rgba(0, 0, 0, 0.28);
--z-map-ui: 800; --z-panel: 900; --z-popup: 950;
--z-loading: 960; --z-toast: 1000; --z-modal: 1100;
}
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) {
color-scheme: dark;
--bg: #0b0e13; --surface: rgba(16, 20, 27, 0.95); --surface-2: rgba(255, 255, 255, 0.06);
--border: rgba(255, 255, 255, 0.12); --text: #d9dee6; --muted: #8b94a3;
--accent: #5aa9e6; --on-accent: #0b0e13; --track: rgba(255, 255, 255, 0.18);
--print: #ffd166; --gen-running: #f4a261; --gen-pending: #4cc9f0; --gen-failed: #e76f51;
--tile-sel: #ffd24a;
--shadow-sm: 0 4px 16px rgba(0, 0, 0, 0.4); --shadow-lg: 0 6px 24px rgba(0, 0, 0, 0.45);
}
}
:root[data-theme="dark"] {
color-scheme: dark;
--bg: #0b0e13; --surface: rgba(16, 20, 27, 0.95); --surface-2: rgba(255, 255, 255, 0.06);
--border: rgba(255, 255, 255, 0.12); --text: #d9dee6; --muted: #8b94a3;
--accent: #5aa9e6; --on-accent: #0b0e13; --track: rgba(255, 255, 255, 0.18);
--print: #ffd166; --gen-running: #f4a261; --gen-pending: #4cc9f0; --gen-failed: #e76f51;
--tile-sel: #ffd24a;
--shadow-sm: 0 4px 16px rgba(0, 0, 0, 0.4); --shadow-lg: 0 6px 24px rgba(0, 0, 0, 0.45);
}
* { box-sizing: border-box; margin: 0; padding: 0; }
[hidden] { display: none !important; }
html, body { height: 100%; overflow: hidden; background: var(--bg); color: var(--text);
font: var(--fs-md)/1.45 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; }
#map { position: absolute; inset: 0; background: var(--bg); }
.leaflet-container { background: var(--bg); font: inherit; }
/* Fond OSM assombri pour laisser ressortir les rendus LiDAR. Filtre posé sur
le conteneur de la couche, pas sur chaque tuile : même rendu (opérations
par pixel), mais une seule passe de filtre au lieu d'une par tuile —
−30 % de CPU (moteur de rendu + GPU) en déplacement, mesuré sous Chrome. */
.leaflet-tile-pane .leaflet-layer.base-dark { filter: brightness(0.38) contrast(1.1) saturate(0.7); }
.info-sec { margin-top: 10px; }
.info-sec h4 { margin: 0 0 4px; font-size: var(--fs-xs); font-weight: 700; letter-spacing: 0.06em;
text-transform: uppercase; color: var(--muted); }
.info-kv { display: grid; grid-template-columns: max-content 1fr; gap: 2px 10px; font-size: var(--fs-sm); }
.info-kv dt { color: var(--muted); }
.info-kv dd { margin: 0; overflow-wrap: anywhere; }
.info-dl { display: inline-block; margin-top: 6px; padding: 6px 10px; border-radius: var(--r-sm);
background: color-mix(in srgb, var(--accent) 18%, transparent); color: var(--text); text-decoration: none; font-weight: 600; }
.info-dl:hover { background: color-mix(in srgb, var(--accent) 30%, transparent); }
.rose-cap { font-size: var(--fs-xs); color: var(--text); line-height: 1.3; }
.rose-cap span { color: var(--muted); font-size: var(--fs-xs); }
.tile-head .sec-title { margin: 0; flex: 1; }
.leaflet-popup-content-wrapper, .leaflet-popup-tip { background: var(--surface); color: var(--text);
box-shadow: var(--shadow-lg); }
.leaflet-popup-content-wrapper { border-radius: var(--r-md); border: 1px solid var(--border); }
.leaflet-popup-content { margin: var(--sp-2) var(--sp-3); font-size: var(--fs-sm); line-height: 1.45; }
.leaflet-container a.leaflet-popup-close-button { color: var(--muted); }
.tile-pop b { font-size: var(--fs-md); }
.tile-pop .row { margin-top: var(--sp-2); }
/* --- composants communs --------------------------------------------------- */
.btn, button { display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-1);
min-height: 32px; padding: 0 var(--sp-3); border-radius: var(--r-sm); border: 1px solid var(--border);
background: var(--surface-2); color: var(--text); font: inherit; font-size: var(--fs-sm); cursor: pointer; }
.btn:hover, button:hover { border-color: var(--accent); }
.btn:disabled, button:disabled { opacity: 0.5; cursor: default; }
.btn-primary { background: var(--accent); border-color: var(--accent); color: var(--on-accent); font-weight: 600; }
.btn-primary:hover { filter: brightness(1.08); }
.btn.accent, .btn.busy { border-color: var(--accent); color: var(--accent); }
.btn-ghost { background: transparent; }
.btn-icon { min-height: 0; padding: var(--sp-1) var(--sp-2); border: none; background: none;
color: var(--muted); font-size: var(--fs-lg); }
.btn-icon:hover { color: var(--text); }
.row { display: flex; gap: var(--sp-2); align-items: center; }
.row > .btn, .row > .field { flex: 1; min-width: 0; }
.field { height: 32px; min-width: 0; width: 100%; padding: 0 var(--sp-2); border-radius: var(--r-sm);
border: 1px solid var(--border); background: var(--surface-2); color: var(--text);
font: inherit; font-size: var(--fs-sm); }
select.field[multiple] { height: auto; padding: var(--sp-1); }
.field option { background-color: var(--surface); color: var(--text); }
.seg { display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; border: 1px solid var(--border);
border-radius: var(--r-sm); overflow: hidden; }
.seg button { min-height: 32px; border: none; border-radius: 0; background: var(--surface-2); color: var(--muted); }
.seg button + button { border-left: 1px solid var(--border); }
.seg button[aria-pressed="true"] { background: var(--accent); color: var(--on-accent); font-weight: 600; }
.seg button:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.hint { font-size: var(--fs-sm); color: var(--muted); margin: var(--sp-1) 0; }
input[type="range"] { -webkit-appearance: none; appearance: none; width: 100%; height: 20px;
background: transparent; cursor: pointer; }
input[type="range"]::-webkit-slider-runnable-track { height: 4px; border-radius: 2px; background: var(--track); }
input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; width: 14px; height: 14px;
margin-top: -5px; border-radius: 50%; background: var(--accent); border: 2px solid var(--surface); }
input[type="range"]::-moz-range-track { height: 4px; border-radius: 2px; background: var(--track); }
input[type="range"]::-moz-range-thumb { width: 10px; height: 10px; border-radius: 50%;
background: var(--accent); border: 2px solid var(--surface); }
@media (max-width: 720px) {
.btn, button, .field, .seg button { min-height: 40px; }
.field { height: 40px; }
}
/* Ligne du fond de carte : œil, nom, opacité. */
.base-row { display: grid; grid-template-columns: 32px minmax(0, 1fr) auto; align-items: center;
gap: var(--sp-2); padding: var(--sp-1) 0; }
.base-row .base-ctl { grid-column: 2 / -1; display: grid; grid-template-columns: minmax(0, 1fr) 40px;
gap: var(--sp-2); align-items: center; }
/* Affichage : une couche principale, la précision seule ou superposée. */
.view-row { display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: center;
gap: 8px; padding: 8px 4px 4px; border-top: 1px solid var(--border); margin-top: 4px; }
.view-label { font-size: var(--fs-xs); font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted); }
.view-row .layer-name { color: var(--text); }
.prec-ctl { display: grid; grid-template-columns: auto minmax(0, 1fr) 34px; gap: 8px;
align-items: center; padding: 6px 4px 0; font-size: var(--fs-xs); color: var(--muted); }
.prec-legend { padding: 8px 4px 2px; }
.prec-bar { display: grid; grid-template-columns: repeat(16, 1fr); height: 10px;
border: 1px solid var(--border); border-radius: 3px; overflow: hidden; }
.prec-ticks { display: flex; justify-content: space-between; font-size: var(--fs-xs); color: var(--muted);
font-variant-numeric: tabular-nums; margin-top: 2px; }
.prec-cap { font-size: var(--fs-xs); color: var(--muted); margin-top: 2px; }
.eye { width: 24px; height: 24px; padding: 0; border: none; background: none; color: var(--muted);
display: flex; align-items: center; justify-content: center; }
.eye.on { color: var(--accent); }
.eye svg { width: 16px; height: 16px; }
.layer-name { font-size: var(--fs-sm); color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.base-row .layer-name { color: var(--text); cursor: pointer; }
.base-row:not(.on) .layer-name { color: var(--muted); }
.pct { font-size: var(--fs-xs); color: var(--muted); text-align: right; font-variant-numeric: tabular-nums; }
#tools { position: fixed; right: var(--sp-3); bottom: calc(var(--sheet-h) + var(--sp-3));
z-index: var(--z-map-ui); display: flex; flex-direction: column; gap: var(--sp-2); }
@media (max-width: 720px) {
#tools { top: var(--sp-3); bottom: auto; }
}
/* L'attribution Leaflet reste en bas à droite : les outils passent au-dessus. */
@media (min-width: 721px) { #tools { bottom: 44px; } }
#tools button { width: 38px; height: 38px; padding: 0; font-size: var(--fs-lg); background: var(--surface);
border: 1px solid var(--border); box-shadow: var(--shadow-sm); }
/* Indicateur de chargement : tuiles restantes pour finir la vue courante
(fond + LiDAR), barre de progression du lot en cours. */
.tileload { position: fixed; left: calc(var(--panel-w) + (100vw - var(--panel-w)) / 2); top: auto;
bottom: calc(var(--sheet-h) + var(--sp-3)); transform: translateX(-50%); z-index: var(--z-loading);
display: flex; align-items: center; gap: 8px; padding: 5px 12px; border-radius: var(--r-full);
background: var(--surface); border: 1px solid var(--border); font-size: var(--fs-sm);
box-shadow: var(--shadow-sm); pointer-events: none;
transition: opacity 0.3s ease; white-space: nowrap; }
.tileload.done { opacity: 0; }
.tl-spin { width: 12px; height: 12px; border-radius: var(--r-full); flex: none;
border: 2px solid color-mix(in srgb, var(--accent) 25%, transparent); border-top-color: var(--accent);
animation: tlspin 0.8s linear infinite; }
.tileload.done .tl-spin { animation: none; border-color: var(--accent); }
@keyframes tlspin { to { transform: rotate(360deg); } }
.tl-bar { width: 60px; height: 4px; border-radius: var(--r-sm); background: var(--track);
overflow: hidden; flex: none; }
.tl-bar span { display: block; height: 100%; width: 0; background: var(--accent);
transition: width 0.2s ease; }
.toast { position: fixed; left: calc(var(--panel-w) + (100vw - var(--panel-w)) / 2);
bottom: calc(var(--sheet-h) + 56px); transform: translateX(-50%); z-index: var(--z-toast);
background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-sm);
padding: 7px 12px; font-size: var(--fs-sm); color: var(--text); max-width: 80vw; text-align: center; }
.meta { font-size: var(--fs-xs); color: var(--muted); margin: 2px 0; }
.meta b { color: var(--text); font-weight: 600; }
.mono { font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: var(--fs-xs);
background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--r-sm);
padding: 6px 8px; word-break: break-all; margin: 4px 0 8px; }
.use-note { font-size: var(--fs-xs); color: var(--muted); line-height: 1.5; }
.leaflet-control-attribution { background: var(--surface) !important; color: var(--muted) !important; }
.leaflet-control-attribution a { color: var(--accent) !important; }
/* --- génération de tuiles ------------------------------------------------ */
.print-msg { font-size: var(--fs-sm); margin-left: 8px; }
/* #1b1b1b (glyphe + bordure) reste en dur volontairement : --print est un jaune/ambre
proche dans les deux thèmes (#d99a00 clair, #ffd166 sombre), un texte sombre fixe
y garde un contraste correct dans les deux cas — un token --text/--on-accent
changerait de sens (clair en thème sombre) et casserait ce contraste. */
.print-handle { display: flex; align-items: center; justify-content: center; cursor: move;
font-size: var(--fs-lg); color: #1b1b1b; background: var(--print); border: 2px solid #1b1b1b;
border-radius: var(--r-full); box-shadow: 0 1px 6px rgba(0,0,0,0.5); }
body.drawing { cursor: crosshair !important; }
body.drawing .leaflet-container { cursor: crosshair !important; }
.gen-row { display: grid; grid-template-columns: 92px minmax(0, 1fr); gap: 6px;
align-items: center; margin: 4px 0; font-size: var(--fs-xs); color: var(--muted); }
.chk { display: flex; gap: 7px; align-items: center; font-size: var(--fs-xs);
color: var(--muted); margin: 4px 0; cursor: pointer; }
.chk input { accent-color: var(--accent); }
.gen-log { font: var(--fs-xs)/1.5 ui-monospace, "SF Mono", Menlo, monospace; color: var(--text);
background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--r-sm);
padding: 6px 8px; max-height: 130px; overflow-y: auto; white-space: pre-wrap;
word-break: break-all; margin: 6px 0; }
#tools button.busy { color: var(--accent); border-color: var(--accent); }
/* Cadres des dalles du run : orange pulsant = rendu en cours, bleu = en
attente, rouge = échec. Les dalles faites n'ont pas de cadre. */
@keyframes genpulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.35; } }
.gen-frame-running { stroke: var(--gen-running); fill: color-mix(in srgb, var(--gen-running) 13%, transparent); animation: genpulse 1.4s ease-in-out infinite; }
.gen-frame-pending { stroke: var(--gen-pending); fill: color-mix(in srgb, var(--gen-pending) 8%, transparent); stroke-dasharray: 6 4; }
.gen-frame-failed { stroke: var(--gen-failed); fill: color-mix(in srgb, var(--gen-failed) 20%, transparent); }
.gen-sel { stroke: var(--accent); fill: color-mix(in srgb, var(--accent) 12%, transparent); cursor: pointer; }
/* --- panneau unique à onglets ------------------------------------------------
Ordinateur : colonne à gauche, repliable en bande d'icônes.
Téléphone : volet en bas à trois hauteurs (fermé / mi-hauteur / plein). */
#panel { position: fixed; z-index: var(--z-panel); display: flex; flex-direction: column;
background: var(--surface); color: var(--text); border: 1px solid var(--border);
box-shadow: var(--shadow-lg); backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
left: var(--sp-3); top: var(--sp-3); bottom: var(--sp-3); width: 340px; border-radius: var(--r-md); }
.panel-head { display: flex; align-items: center; gap: var(--sp-1); padding: var(--sp-2);
border-bottom: 1px solid var(--border); }
.panel-tabs { display: flex; gap: var(--sp-1); flex: 1; min-width: 0; }
.panel-tabs button { flex: 1; min-width: 0; flex-direction: column; gap: 0; min-height: 44px;
padding: var(--sp-1); border: none; background: transparent; color: var(--muted); }
.panel-tabs button[aria-selected="true"] { background: var(--surface-2); color: var(--accent); }
.tab-ico { font-size: var(--fs-lg); line-height: 1.1; }
.tab-lbl { font-size: var(--fs-xs); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
.panel-body { flex: 1; overflow-y: auto; padding: var(--sp-3); overscroll-behavior: contain; }
.panel-body section > * + * { margin-top: var(--sp-2); }
.panel-stat { font-size: var(--fs-sm); color: var(--muted); }
.sec-title { font-size: var(--fs-xs); font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
color: var(--muted); margin-top: var(--sp-4); }
.legend { display: flex; align-items: center; gap: var(--sp-3); }
.legend canvas { width: 96px; height: 96px; flex: none; }
.sheet-grip { display: none; }
#panel.collapsed { width: 60px; }
#panel.collapsed .panel-body, #panel.collapsed .tab-lbl { display: none; }
#panel.collapsed .panel-head { flex-direction: column; border-bottom: none; }
#panel.collapsed .panel-tabs { flex-direction: column; align-self: stretch; }
#panel.collapsed #panelCollapse { transform: rotate(180deg); }
/* La carte, l'échelle et les messages se décalent de la largeur du panneau. */
body { --panel-w: 364px; --sheet-h: 0px; }
body.panel-collapsed { --panel-w: 84px; }
.leaflet-bottom.leaflet-left { left: var(--panel-w); }
@media (max-width: 720px) {
body, body.panel-collapsed { --panel-w: 0px; }
#panel, #panel.collapsed { left: 0; right: 0; top: auto; bottom: 0; width: auto;
border-radius: var(--r-md) var(--r-md) 0 0; border-bottom: none; transition: height 0.2s ease; }
#panel[data-sheet="closed"] { height: 72px; }
#panel[data-sheet="half"] { height: 45vh; }
#panel[data-sheet="full"] { height: calc(100vh - var(--sp-4)); }
#panel[data-sheet="full"] { height: calc(100dvh - var(--sp-4)); }
#panel[data-sheet="closed"] .panel-body { display: none; }
#panelCollapse { display: none; }
.sheet-grip { display: flex; justify-content: center; padding: var(--sp-2) 0 0; cursor: grab; touch-action: none; }
.sheet-grip span { width: 36px; height: 4px; border-radius: var(--r-full); background: var(--muted); }
.panel-head { padding: var(--sp-1) var(--sp-2); }
.leaflet-bottom { bottom: var(--sheet-h); }
/* Attribution sur toute la largeur : l'échelle passe au-dessus d'elle,
l'indicateur de chargement en haut de l'écran (à gauche des outils). */
.leaflet-bottom.leaflet-left .leaflet-control-scale { margin-bottom: 28px; }
.tileload { top: var(--sp-3); bottom: auto; }
}