Files
Planification/viewer.css
T

2445 lines
63 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.
/* ==========================================================================
Thème clair (défaut)
========================================================================== */
:root {
--bg: #f4f5f7;
--bg-elevated: #ffffff;
--bg-muted: #f0f1f3;
--bg-hover: #f7f8fa;
--border: #e2e4e8;
--border-strong: #cfd3da;
--text: #1a1f2b;
--text-muted: #5b6573;
--text-faint: #8892a0;
--accent: #0f4f8b;
--accent-soft: #e1ecf7;
--danger: #b03030;
--danger-soft: #fbe6e6;
--warn: #b87a00;
--warn-soft: #fff2d6;
--ok: #2e7b4a;
--ok-soft: #dff0e4;
/* Palette par type d'intervention */
--c-livraison: #2563eb;
--c-livraison-soft: #dbeafe;
--c-recup: #16a34a;
--c-recup-soft: #dcfce7;
--c-remplacement: #ea580c;
--c-remplacement-soft: #fed7aa;
--c-incident: #8b5cf6;
--c-incident-soft: #ede9fe;
--c-installation: #2563eb;
--c-installation-soft: #dbeafe;
--c-rollout: #92400e; /* brun */
--c-rollout-soft: #fde68a;
--c-reservation: #f59e0b; /* jaune/ambre */
--c-reservation-soft: #fef3c7;
--c-autre: #6b7280;
--c-autre-soft: #e5e7eb;
/* Statuts clos */
--c-closed: #15803d; /* vert foncé = Clôturé */
--c-closed-soft: #bbf7d0;
--c-resolved: #4ade80; /* vert clair = Résolu */
--c-resolved-soft: #dcfce7;
--shadow: 0 1px 3px rgba(20, 30, 50, 0.06), 0 1px 2px rgba(20, 30, 50, 0.04);
--shadow-hover: 0 2px 8px rgba(20, 30, 50, 0.08);
--radius: 8px;
--font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
--mono: ui-monospace, SFMono-Regular, "SF Mono", Consolas, "Liberation Mono", monospace;
}
[data-theme="dark"] {
--bg: #16181d;
--bg-elevated: #21242b;
--bg-muted: #1c1f25;
--bg-hover: #2a2e36;
--border: #2e333c;
--border-strong: #414754;
--text: #e6e8ec;
--text-muted: #9ba2ad;
--text-faint: #6a727e;
--accent: #5ea8e8;
--accent-soft: #223348;
--danger: #e87878;
--danger-soft: #3b2626;
--warn: #d9a753;
--warn-soft: #3a2e1a;
--ok: #78c59a;
--ok-soft: #1f3a2b;
--c-livraison: #60a5fa;
--c-livraison-soft: #1e3a5f;
--c-recup: #4ade80;
--c-recup-soft: #14432a;
--c-remplacement: #fb923c;
--c-remplacement-soft: #4a2512;
--c-incident: #a78bfa;
--c-incident-soft: #2e1065;
--c-installation: #60a5fa;
--c-installation-soft: #1e3a5f;
--c-autre: #9ca3af;
--c-autre-soft: #2a2e36;
--c-closed: #22c55e;
--c-closed-soft: #14432a;
--c-resolved: #86efac;
--c-resolved-soft: #0f3320;
--shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
--shadow-hover: 0 2px 10px rgba(0, 0, 0, 0.4);
}
/* ==========================================================================
Base
========================================================================== */
* { box-sizing: border-box; }
html, body {
margin: 0;
padding: 0;
background: var(--bg);
color: var(--text);
font-family: var(--font);
font-size: 14px;
line-height: 1.5;
}
.hidden { display: none !important; }
/* ==========================================================================
Topbar
========================================================================== */
.topbar {
position: sticky;
top: 0;
z-index: 10;
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 20px;
background: var(--bg-elevated);
border-bottom: 1px solid var(--border);
box-shadow: var(--shadow);
gap: 12px;
flex-wrap: wrap;
}
.topbar-left {
display: flex;
align-items: center;
gap: 14px;
flex: 1;
min-width: 0;
}
.topbar h1 {
margin: 0;
font-size: 18px;
font-weight: 600;
color: var(--text);
white-space: nowrap;
}
.capture-info {
font-size: 12px;
color: var(--text-muted);
white-space: nowrap;
}
.refresh-check {
font-size: 14px;
color: var(--c-recup); /* vert */
font-weight: 700;
opacity: 0;
transform: scale(0.5);
transition: opacity 0.25s ease, transform 0.25s ease;
pointer-events: none;
}
.refresh-check.visible {
opacity: 1;
transform: scale(1);
}
.refresh-check.hidden {
display: none;
}
.topbar-right {
display: flex;
gap: 8px;
flex-shrink: 0;
}
/* Bannière de session expirée (v4.1.12) — sticky sous la topbar, non bloquante */
.session-banner {
position: sticky;
top: 56px;
z-index: 8;
display: flex;
align-items: center;
gap: 12px;
padding: 12px 18px;
/* v4.2.5 : rouge plus vif + bord plus épais pour visibilité max */
background: linear-gradient(90deg, #c93030, #d84848);
color: #fff;
border-top: 2px solid #ff6060;
border-bottom: 2px solid #7a1515;
font-size: 14px;
font-weight: 500;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
/* petite animation d'apparition pour attirer l'œil */
animation: session-banner-in 0.22s ease-out;
}
@keyframes session-banner-in {
from { opacity: 0; transform: translateY(-6px); }
to { opacity: 1; transform: translateY(0); }
}
/* v4.2.5 : variante ORANGE pour "EV inaccessible" (distinct de session expirée) */
.session-banner.ev-banner {
background: linear-gradient(90deg, #c77920, #e09a3a);
border-top: 2px solid #ffbb60;
border-bottom: 2px solid #7a4a15;
}
.session-banner.ev-banner .btn-primary {
color: #8a4a10;
}
.session-banner.hidden {
display: none;
}
.session-banner-icon {
font-size: 20px;
flex-shrink: 0;
}
.session-banner-text {
flex: 1;
line-height: 1.4;
}
.session-banner-text strong {
font-weight: 600;
}
.session-banner .btn-primary {
background: #fff;
color: #9a2020;
border: 0;
font-weight: 600;
}
.session-banner .btn-primary:hover {
background: #f0f0f0;
}
.session-banner .btn-sm {
padding: 5px 12px;
font-size: 12px;
/* v4.2.5 : btn-sm non-primary dans la bannière = contour blanc */
background: transparent;
color: #fff;
border: 1px solid rgba(255, 255, 255, 0.5);
font-weight: 500;
}
.session-banner .btn-sm:hover {
background: rgba(255, 255, 255, 0.12);
}
.session-banner .btn-primary.btn-sm {
/* reset : le primary override le style du btn-sm */
background: #fff;
color: #9a2020;
border: 0;
font-weight: 600;
}
.session-banner.ev-banner .btn-primary.btn-sm {
color: #8a4a10;
}
.session-banner .btn-icon {
background: transparent;
color: #fff;
border: 0;
font-size: 20px;
line-height: 1;
padding: 4px 8px;
cursor: pointer;
}
.session-banner .btn-icon:hover {
background: rgba(255,255,255,0.15);
}
/* Barre de progression pendant le rafraichissement — v4.1.12 : texte
toujours lisible, que la zone verte l'ait atteint ou non (utilise
mix-blend-mode:difference pour inverser la couleur du texte là où la
barre verte est dessous). */
.progress-bar {
position: sticky;
top: 56px;
z-index: 9;
height: 22px;
/* v4.1.17 : backdrop-blur sur toute la barre → ce qui défile derrière
est légèrement flouté sur TOUTE la largeur. Pas d'opacité sombre
ajoutée, transparence préservée. */
background: rgba(128, 128, 128, 0.08);
backdrop-filter: blur(3px);
-webkit-backdrop-filter: blur(3px);
border-bottom: 1px solid var(--border, rgba(128, 128, 128, 0.2));
overflow: hidden;
}
.progress-bar.hidden {
display: none;
}
.progress-bar-fill {
position: absolute;
left: 0;
top: 0;
bottom: 0;
background: linear-gradient(90deg, #2ea043, #3fb950);
width: 0%;
transition: width 240ms ease-out;
box-shadow: 0 0 8px rgba(63, 185, 80, 0.3);
}
.progress-bar-label {
position: relative;
display: block;
text-align: center;
line-height: 22px;
font-size: 12px;
font-weight: 700;
color: #fff;
pointer-events: none;
letter-spacing: 0.3px;
/* v4.1.14/17 : text-shadow multi-directionnel (halo sombre autour du
texte). Le backdrop-blur est sur toute la barre, plus besoin de pill. */
text-shadow:
0 0 2px rgba(0, 0, 0, 0.95),
0 0 3px rgba(0, 0, 0, 0.85),
0 1px 2px rgba(0, 0, 0, 0.75),
0 -1px 2px rgba(0, 0, 0, 0.75),
1px 0 2px rgba(0, 0, 0, 0.75),
-1px 0 2px rgba(0, 0, 0, 0.75);
z-index: 2;
}
/* Navigation de date */
.date-nav {
display: flex;
align-items: center;
gap: 4px;
flex-wrap: nowrap;
}
/* v2026.5.17 : faux input date custom avec nom du jour */
.date-custom-wrapper {
position: relative;
display: inline-flex;
align-items: center;
}
.date-custom {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 5px 10px 5px 12px;
border: 1px solid var(--border);
border-radius: 6px;
background: var(--bg-muted);
color: var(--text);
font-family: inherit;
font-size: 13px;
font-weight: 500;
cursor: pointer;
white-space: nowrap;
user-select: none;
transition: border-color 0.15s, background 0.15s;
}
.date-custom:hover {
border-color: var(--border-strong);
background: var(--bg-hover);
}
.date-custom:focus {
outline: 2px solid var(--accent);
outline-offset: -1px;
}
.date-custom-icon {
font-size: 13px;
opacity: 0.7;
}
.date-input-hidden {
position: absolute;
top: 100%;
left: 0;
width: 1px;
height: 1px;
opacity: 0;
pointer-events: none;
}
/* v2026.5.17 : masquer l'ancien date-picker-day s'il traîne (compat) */
.date-picker-day { display: none; }
.btn-nav {
padding: 6px 10px;
font-size: 13px;
min-width: 32px;
}
.btn-today {
padding: 6px 10px;
font-size: 12px;
}
.date-input {
padding: 5px 8px;
border: 1px solid var(--border);
border-radius: 6px;
background: var(--bg-muted);
color: var(--text);
font-family: inherit;
font-size: 13px;
cursor: pointer;
}
.date-input:hover {
border-color: var(--border-strong);
}
.date-input:focus {
outline: 2px solid var(--accent);
outline-offset: -1px;
}
.btn {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 6px 12px;
background: var(--bg-muted);
color: var(--text);
border: 1px solid var(--border);
border-radius: 6px;
font-size: 13px;
font-family: inherit;
cursor: pointer;
transition: background 0.1s, border-color 0.1s;
}
.btn:hover {
background: var(--bg-hover);
border-color: var(--border-strong);
}
.btn:active {
transform: translateY(1px);
}
.btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.btn-icon {
padding: 6px 10px;
font-size: 15px;
}
.btn-subtle {
opacity: 0.75;
font-size: 12px;
}
.btn-subtle:hover {
opacity: 1;
}
/* v4.1.12 : boutons refresh plus clairs visuellement.
- "Vérifier" (partiel) : style discret, icône demi-rotation
- "Tout recharger" (total) : plus affirmé, icône double-flèche circulaire */
.btn-refresh {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 6px 10px;
}
.btn-refresh-icon {
width: 15px;
height: 15px;
flex-shrink: 0;
color: currentColor;
}
.btn-refresh-label {
font-size: 12px;
line-height: 1;
}
.btn-refresh-strong {
background: var(--bg-subtle, rgba(63, 185, 80, 0.08));
border-color: var(--border-strong);
}
.btn-refresh-strong:hover {
background: rgba(63, 185, 80, 0.18);
border-color: rgba(63, 185, 80, 0.5);
}
.btn-refresh-icon.spinning {
animation: refresh-spin 0.9s linear infinite;
transform-origin: 50% 50%;
}
@keyframes refresh-spin {
to { transform: rotate(360deg); }
}
.btn-primary {
background: var(--accent);
color: white;
border-color: var(--accent);
}
.btn-primary:hover {
background: var(--accent);
opacity: 0.9;
}
/* Bouton "Arrêter" (apparaît pdt un refresh manuel) */
.btn-abort {
background: var(--danger-soft);
color: var(--danger);
border-color: var(--danger);
}
.btn-abort:hover {
background: var(--danger);
color: white;
border-color: var(--danger);
}
#refresh-icon.spinning {
display: inline-block;
animation: spin 0.8s linear infinite;
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
/* ==========================================================================
Écrans d'état
========================================================================== */
.loading {
padding: 40px 20px;
text-align: center;
color: var(--text-muted);
font-size: 14px;
}
.error-box {
margin: 20px;
padding: 14px 18px;
background: var(--danger-soft);
color: var(--danger);
border: 1px solid var(--danger);
border-radius: var(--radius);
font-size: 14px;
line-height: 1.55;
}
.session-needed {
max-width: 500px;
margin: 60px auto;
padding: 28px 32px;
background: var(--bg-elevated);
border: 1px solid var(--border);
border-radius: var(--radius);
box-shadow: var(--shadow);
text-align: center;
}
.session-needed h2 {
margin: 0 0 12px 0;
color: var(--text);
}
.session-needed p {
margin: 10px 0;
color: var(--text-muted);
}
.session-needed code {
background: var(--bg-muted);
padding: 2px 6px;
border-radius: 3px;
font-family: var(--mono);
font-size: 12px;
}
.session-needed button {
margin-top: 14px;
}
/* ==========================================================================
Stats globales
========================================================================== */
.stats {
display: flex;
flex-wrap: wrap;
align-items: baseline;
gap: 8px;
padding: 12px 20px 4px 20px;
font-size: 13px;
color: var(--text-muted);
}
.global-stat b {
color: var(--text);
font-weight: 600;
}
.global-stat-main b {
font-size: 16px;
}
.global-stat-sub {
color: var(--text-faint);
font-size: 12px;
}
.global-stat-sep {
color: var(--text-faint);
opacity: 0.5;
}
/* ==========================================================================
Grille de cartes
========================================================================== */
.cards {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
gap: 14px;
padding: 14px 20px 40px 20px;
}
.card {
background: var(--bg-elevated);
border: 1px solid var(--border);
border-radius: var(--radius);
box-shadow: var(--shadow);
overflow: hidden;
display: flex;
flex-direction: column;
}
.card-header {
padding: 10px 14px;
border-bottom: 1px solid var(--border);
background: var(--bg-muted);
display: flex;
justify-content: space-between;
align-items: center;
gap: 10px;
}
.card-tech-name {
font-weight: 600;
font-size: 14px;
color: var(--text);
}
.card-tech-badge {
font-size: 11px;
padding: 2px 8px;
border-radius: 10px;
text-transform: uppercase;
letter-spacing: 0.04em;
font-weight: 600;
}
.badge-pompier {
background: var(--danger-soft);
color: var(--danger);
}
.badge-absent {
background: var(--bg-muted);
color: var(--text-faint);
border: 1px solid var(--border);
}
.badge-count {
background: var(--accent-soft);
color: var(--accent);
}
.card-body {
padding: 0;
flex: 1;
}
.card-empty {
padding: 14px;
color: var(--text-faint);
font-size: 13px;
font-style: italic;
text-align: center;
}
.card.is-pompier {
border-left: 3px solid var(--danger);
}
.card.is-absent {
opacity: 0.85;
}
.card.is-absent .card-header {
background: var(--bg);
}
/* ==========================================================================
Frise de temps
========================================================================== */
.timeline {
padding: 12px 14px 6px 14px;
background: var(--bg-muted);
border-bottom: 1px solid var(--border);
position: relative;
}
.timeline-pompier {
background: var(--danger-soft);
}
.timeline-bar {
position: relative;
height: 20px;
background: var(--bg-elevated);
border: 1px solid var(--border);
border-radius: 3px;
overflow: hidden;
}
.timeline-hole {
position: absolute;
top: 0;
bottom: 0;
background: repeating-linear-gradient(
45deg,
transparent 0 4px,
rgba(0, 0, 0, 0.035) 4px 8px
);
cursor: help;
transition: background 0.1s;
}
[data-theme="dark"] .timeline-hole {
background: repeating-linear-gradient(
45deg,
transparent 0 4px,
rgba(255, 255, 255, 0.04) 4px 8px
);
}
.timeline-hole:hover {
background: var(--ok-soft);
}
.timeline-slot {
position: absolute;
top: 0;
bottom: 0;
cursor: help;
transition: filter 0.1s;
border-right: 1px solid var(--bg-elevated);
}
.timeline-slot.color-livraison { background: var(--c-livraison); }
.timeline-slot.color-installation { background: var(--c-installation); }
.timeline-slot.color-recup { background: var(--c-recup); }
.timeline-slot.color-remplacement { background: var(--c-remplacement); }
.timeline-slot.color-incident { background: var(--c-incident); }
.timeline-slot.color-rollout { background: var(--c-rollout); }
.timeline-slot.color-reservation { background: var(--c-reservation); }
.timeline-slot.color-autre { background: var(--c-autre); }
/* Statuts clos sur la timeline */
.timeline-slot.status-closed { background: var(--c-closed); }
.timeline-slot.status-resolved { background: var(--c-resolved); }
.timeline-slot.kind-absence {
/* v5.0.15 : uni gris-noir au lieu de rayé, plus lisible */
background: #2a2f36;
border-right: 1px solid var(--bg-elevated);
}
.timeline-slot:hover,
.timeline-slot.highlight {
filter: brightness(1.12);
outline: 2px solid var(--text);
outline-offset: -2px;
z-index: 2;
}
.timeline-noon {
position: absolute;
top: -2px;
bottom: -2px;
width: 1px;
background: var(--border-strong);
z-index: 1;
pointer-events: none;
}
.timeline-scale {
position: relative;
height: 14px;
margin-top: 4px;
}
.timeline-tick {
position: absolute;
transform: translateX(-50%);
font-size: 10px;
color: var(--text-faint);
font-family: var(--mono);
}
/* Stats par carte */
.card-stats {
display: flex;
align-items: baseline;
justify-content: space-between;
padding: 10px 14px;
background: var(--bg-muted);
border-bottom: 1px solid var(--border);
}
.stat-total {
display: flex;
align-items: baseline;
gap: 6px;
}
.stat-total-num {
font-size: 22px;
font-weight: 700;
color: var(--text);
line-height: 1;
}
.stat-total-lbl {
font-size: 12px;
color: var(--text-muted);
}
.stat-split {
display: flex;
align-items: center;
gap: 6px;
font-size: 12px;
color: var(--text-faint);
}
.stat-split-item b {
color: var(--text-muted);
font-weight: 600;
}
.stat-split-sep {
opacity: 0.4;
}
/* Notes de statut */
.card-status-note {
padding: 8px 14px;
font-size: 12px;
font-weight: 500;
text-align: center;
}
.card-status-note.pompier {
background: var(--danger-soft);
color: var(--danger);
border-bottom: 1px solid var(--border);
}
.card-status-note.absent {
background: var(--bg);
color: var(--text-muted);
border-bottom: 1px solid var(--border);
font-style: italic;
}
.card-empty.subtle {
font-size: 12px;
opacity: 0.7;
}
.intervention.highlight {
background: var(--bg-hover);
}
/* ==========================================================================
Interventions — layout v2 (heures verticales)
========================================================================== */
.intervention-v2 {
display: grid;
grid-template-columns: 4px 58px 1fr auto;
grid-template-rows: auto auto;
/* v4.1.17 : la ligne du bas (right) s'étend maintenant sur les 2 colonnes
droite (right + status) pour que la signature aille vraiment jusqu'au
bord droit. Le ✓ status est positionné en absolute par-dessus. */
grid-template-areas:
"dot time ref copy"
"dot time right right";
gap: 2px 10px;
align-items: start;
padding: 10px 12px 12px 8px;
border-top: 1px solid var(--border);
cursor: default;
transition: background 0.08s;
position: relative;
}
.intervention-v2:first-child { border-top: none; }
.intervention-v2:hover { background: var(--bg-hover); }
/* Pastille colorée (barre verticale) */
.intervention-v2 .intervention-dot {
grid-area: dot;
width: 4px;
height: 100%;
border-radius: 2px;
align-self: stretch;
}
.intervention-v2.color-livraison .intervention-dot { background: var(--c-livraison); }
.intervention-v2.color-installation .intervention-dot { background: var(--c-installation); }
.intervention-v2.color-recup .intervention-dot { background: var(--c-recup); }
.intervention-v2.color-remplacement .intervention-dot { background: var(--c-remplacement); }
.intervention-v2.color-incident .intervention-dot { background: var(--c-incident); }
.intervention-v2.color-rollout .intervention-dot { background: var(--c-rollout); }
.intervention-v2.color-reservation .intervention-dot { background: var(--c-reservation); }
.intervention-v2.color-autre .intervention-dot { background: var(--c-autre); }
.intervention-v2.clickable { cursor: pointer; }
.intervention-v2.clickable:active { transform: translateY(1px); }
.intervention-v2.status-closed {
background: var(--c-closed-soft);
box-shadow: inset 4px 0 0 var(--c-closed);
}
.intervention-v2.status-closed:hover {
background: var(--c-closed-soft);
filter: brightness(0.96);
}
.intervention-v2.status-closed .intervention-dot {
background: var(--c-closed);
width: 5px;
}
.intervention-v2.status-resolved {
background: var(--c-resolved-soft);
box-shadow: inset 4px 0 0 var(--c-resolved);
}
.intervention-v2.status-resolved:hover {
background: var(--c-resolved-soft);
filter: brightness(0.96);
}
.intervention-v2.status-resolved .intervention-dot {
background: var(--c-resolved);
width: 5px;
}
/* v4.2.5 : statut "terminée par le tech" (commentaire LOGIN: détecté).
Vert PLUS CLAIR que status-closed (distinction visuelle du ✓ simple
vs ✓✓ double). */
.intervention-v2.status-terminated {
background: var(--c-recup-soft, rgba(63, 185, 80, 0.12));
box-shadow: inset 4px 0 0 var(--c-recup, #3fb950);
}
.intervention-v2.status-terminated:hover {
background: var(--c-recup-soft, rgba(63, 185, 80, 0.12));
filter: brightness(0.96);
}
.intervention-v2.status-terminated .intervention-dot {
background: var(--c-recup, #3fb950);
width: 5px;
}
.intervention-v2.status-terminated .iv-status-check {
color: var(--c-recup, #3fb950);
}
.timeline-slot.status-terminated { background: var(--c-recup, #3fb950); }
/* v4.2.5 : carte "en cours d'analyse" (ghost juste disparu, on re-fetch la
fiche pour décider du sort). Opacité réduite + petit spinner discret. */
.intervention-v2._checking {
opacity: 0.6;
position: relative;
}
.intervention-v2._checking::after {
content: "";
position: absolute;
right: 10px;
top: 50%;
width: 12px;
height: 12px;
margin-top: -6px;
border: 2px solid var(--border, #ccc);
border-top-color: var(--text-muted, #666);
border-radius: 50%;
animation: iv-check-spin 0.9s linear infinite;
}
@keyframes iv-check-spin {
to { transform: rotate(360deg); }
}
/* .intervention-v2.is-ghost : retirée en v4.3.3 — on ne barre plus les
cartes. La gestion des tickets disparus se fait via _disappearStatus
(vert ✓/✓✓) ou _disappearRemove (retrait total). */
/* Ligne 1 : REF en titre centré gros gras */
.iv-ref-header {
grid-area: ref;
font-family: var(--mono);
font-size: 15px;
font-weight: 700;
color: var(--text);
letter-spacing: 0.03em;
text-align: center;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
padding: 2px 0;
}
.iv-ref-header.no-ref {
font-family: var(--font);
font-weight: 500;
color: var(--text-faint);
}
.iv-status-check {
/* v4.1.17 : absolute en bas à droite (la grid-area "status" a été
fusionnée avec "right" pour étendre la signature jusqu'au bord). */
position: absolute;
right: 10px;
bottom: 10px;
font-size: 16px;
font-weight: 700;
color: var(--c-closed);
pointer-events: none;
/* Au-dessus de la signature, mais discret */
z-index: 1;
}
.intervention-v2.status-resolved .iv-status-check { color: var(--c-resolved); }
/* v4.2.5 : ✓✓ double check (clôturé/résolu) — un peu plus petit pour tenir
les 2 caractères. Espacement négatif pour les rapprocher. */
.iv-status-check.double {
font-size: 14px;
letter-spacing: -3px;
padding-right: 3px; /* compenser le letter-spacing côté droit */
}
.intervention-copy {
grid-area: copy;
align-self: start;
padding: 2px 6px;
background: transparent;
color: var(--text-faint);
border: 1px solid transparent;
border-radius: 4px;
cursor: pointer;
font-size: 11px;
opacity: 0;
transition: opacity 0.1s, background 0.1s, color 0.1s;
font-family: inherit;
/* v2026.5.17 : figer largeur/hauteur pour que le changement 📋 → ✓ pendant
la copie ne fasse pas bouger le titre centré dans la grid */
min-width: 28px;
min-height: 22px;
text-align: center;
box-sizing: border-box;
}
.intervention-v2:hover .intervention-copy { opacity: 1; }
.intervention-copy:hover {
background: var(--bg-muted);
color: var(--text);
border-color: var(--border);
}
.intervention-copy.copied {
color: var(--ok);
background: var(--ok-soft);
opacity: 1;
}
/* Ligne 2 GAUCHE : heures VERTICALES, centrées par rapport au bloc droit */
.iv-time-vertical {
grid-area: time;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center; /* centrage vertical */
align-self: center; /* centrage dans la cellule grille */
gap: 1px;
font-family: var(--mono);
font-size: 12px;
color: var(--text);
height: 100%;
}
.iv-time-start, .iv-time-end {
font-weight: 600;
letter-spacing: 0.02em;
}
.iv-time-arrow {
font-size: 11px;
color: var(--text-muted);
line-height: 1;
}
.intervention-v2.is-pompier-line .iv-time-start,
.intervention-v2.is-pompier-line .iv-time-end {
color: var(--danger);
font-weight: 700;
}
/* Ligne 2 DROITE : lieu / contact+tél / bas (catégorie + signature) */
.iv-right {
grid-area: right;
min-width: 0;
display: flex;
flex-direction: column;
gap: 6px;
}
/* Lieu : ville (MAJ gras) + adresse (italique noir) */
.iv-lieu-block {
display: flex;
flex-direction: column;
gap: 0;
}
.iv-lieu-ville {
font-size: 13px;
font-weight: 700;
color: var(--text);
letter-spacing: 0.04em;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.iv-lieu-adresse {
font-size: 12.5px;
font-style: italic;
font-weight: 400;
color: var(--text); /* noir, pas gris */
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Contact + tél — wrap intelligent (pas de coupure de mot) */
.iv-contact-line {
font-size: 13px;
word-break: normal;
overflow-wrap: break-word;
white-space: normal;
line-height: 1.35;
}
.iv-contact {
font-weight: 600;
color: var(--text);
}
.iv-sep {
color: var(--text-faint);
font-weight: 400;
margin: 0 2px;
}
.iv-phone {
font-family: var(--mono);
color: var(--text-muted);
font-weight: 400;
font-size: 12px;
white-space: nowrap; /* numéro pas coupé */
}
/* Bas : catégorie à gauche + signature à droite */
.iv-bottom-line {
display: flex;
justify-content: space-between;
align-items: baseline;
gap: 8px;
font-size: 12px;
/* v4.1.14 : forcer la ligne à occuper 100% de largeur du parent */
width: 100%;
}
.iv-category {
color: var(--text-muted);
font-weight: 400;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
/* v4.1.15 : taille naturelle (pas de flex:1 qui étirait le texte et
rendait la signature juste à côté). Sans flex, la catégorie reste à
son contenu + justify-content:space-between pousse la signature à
l'extrême droite du parent. */
min-width: 0;
flex: 0 1 auto;
max-width: calc(100% - 70px);
}
.iv-signature {
color: var(--text-faint);
font-size: 11px;
font-family: var(--mono);
flex-shrink: 0;
letter-spacing: 0.02em;
text-align: right;
/* v4.1.15/17 : margin-left: auto pour collage garanti à droite */
margin-left: auto;
white-space: nowrap;
}
/* v4.1.17 : si statut clos/résolu, le ✓ est à droite en absolute → décaler
la signature pour ne pas se chevaucher */
.intervention-v2.status-closed .iv-signature,
.intervention-v2.status-resolved .iv-signature {
padding-right: 22px;
}
/* Réservation (créneau bloqué par un coordinateur) */
.iv-ref-header.is-reservation-title {
color: var(--c-reservation);
font-family: var(--font);
letter-spacing: 0.02em;
}
.iv-reservation-par {
font-size: 13px;
font-weight: 600;
color: var(--text);
}
.iv-reservation-sujet {
font-size: 12.5px;
color: var(--text-muted);
font-style: italic;
}
/* ──────────────────────────────────────────────────────────────────────────
Anciens styles .intervention (v1) — gardés pour ne pas casser le reste
────────────────────────────────────────────────────────────────────────── */
.intervention {
display: flex;
align-items: center;
gap: 10px;
padding: 8px 14px 8px 10px;
border-top: 1px solid var(--border);
cursor: default;
transition: background 0.08s;
position: relative;
}
.intervention:first-child { border-top: none; }
.intervention:hover { background: var(--bg-hover); }
.intervention-dot {
flex-shrink: 0;
width: 4px;
align-self: stretch;
margin: 2px 4px 2px 0;
border-radius: 2px;
}
/* ==========================================================================
Tooltip
========================================================================== */
.tooltip {
position: fixed !important;
/* v4.2.4 : forcer un stacking context propre et l'isolation pour que le
tooltip ne soit pas affecté par un éventuel filter/transform/contain
sur un ancêtre (qui casserait position:fixed). `contain: layout` et
`will-change: transform` garantissent aussi que le navigateur traite
ce tooltip indépendamment. */
isolation: isolate;
contain: layout;
z-index: 100;
max-width: 620px;
max-height: calc(100vh - 40px);
overflow-y: auto;
padding: 12px 14px;
background: var(--bg-elevated);
color: var(--text);
border: 1px solid var(--border-strong);
border-radius: 8px;
box-shadow: var(--shadow-hover);
font-size: 13px;
line-height: 1.5;
pointer-events: none;
opacity: 0;
transition: opacity 0.1s;
user-select: text;
-webkit-user-select: text;
}
.tooltip.visible {
opacity: 1;
/* v4.1.10 : permet à la souris d'entrer dans la bulle pour la garder
visible (persistance au hover) et, en mode pinned, pour sélectionner. */
pointer-events: auto;
/* v4.2 : curseur texte par défaut (pour signaler que c'est sélectionnable) */
cursor: text;
}
.tooltip.pinned {
/* Bulle épinglée : bordure verte pour indiquer le mode */
border-color: var(--c-accent, #3fb950);
box-shadow: 0 0 0 2px rgba(63, 185, 80, 0.15), var(--shadow-hover);
}
/* v4.1.13/14 : barre d'actions en haut à droite de la bulle
(recharger cette iv + épingler) */
.tooltip-actions {
position: absolute;
top: 6px;
right: 6px;
display: flex;
gap: 2px;
z-index: 5;
}
.tooltip-actionbtn {
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
border-radius: 4px;
font-size: 13px;
opacity: 0.55;
transition: opacity 0.15s, background 0.15s, transform 0.15s;
user-select: none;
color: var(--text-muted);
}
.tooltip-actionbtn svg {
width: 14px;
height: 14px;
}
.tooltip-actionbtn:hover {
opacity: 1;
background: rgba(255, 255, 255, 0.08);
color: var(--text);
}
.tooltip-actionbtn.spinning svg {
animation: refresh-spin 0.8s linear infinite;
transform-origin: 50% 50%;
}
/* L'ancien .tooltip-pinbtn garde ses variantes */
.tooltip-pinbtn {
filter: grayscale(100%);
}
.tooltip-pinbtn:hover {
filter: grayscale(0%);
}
.tooltip.pinned .tooltip-pinbtn {
opacity: 1;
filter: grayscale(0%);
transform: rotate(-30deg);
background: rgba(63, 185, 80, 0.15);
}
/* v4.1.15 : référence dans la bulle avec bouton copier inline */
.tt-ref-cell {
display: inline-flex;
align-items: center;
gap: 8px;
}
.tt-ref-val {
font-family: var(--mono, monospace);
}
.tt-copy-btn {
background: transparent;
border: 1px solid var(--border);
color: var(--text-muted);
width: 26px;
height: 22px;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0;
transition: background 0.12s, color 0.12s, border-color 0.12s;
}
.tt-copy-btn:hover {
background: var(--bg-hover);
color: var(--text);
border-color: var(--border-strong);
}
.tt-copy-btn.copied {
background: rgba(63, 185, 80, 0.2);
border-color: #3fb950;
color: #3fb950;
}
.tooltip dl {
margin: 0;
display: grid;
grid-template-columns: auto 1fr;
column-gap: 10px;
row-gap: 4px;
}
.tooltip dt {
color: var(--text-muted);
font-size: 12px;
font-weight: 500;
white-space: nowrap;
}
.tooltip dd {
margin: 0;
color: var(--text);
font-size: 13px;
word-break: break-word;
}
.tooltip dd.description,
.tooltip dd.commentaire {
white-space: pre-wrap;
}
.tooltip dd.commentaire {
padding: 6px 8px;
background: var(--bg-muted);
border-radius: 4px;
border-left: 2px solid var(--c-closed);
font-style: italic;
}
.tooltip hr {
border: none;
border-top: 1px solid var(--border);
margin: 8px 0;
grid-column: 1 / -1;
}
/* Badge de statut dans le tooltip */
.status-pill {
display: inline-block;
padding: 2px 8px;
border-radius: 10px;
font-size: 11px;
font-weight: 600;
letter-spacing: 0.02em;
}
.status-pill.closed {
background: var(--c-closed-soft);
color: var(--c-closed);
}
.status-pill.resolved {
background: var(--c-resolved-soft);
color: var(--c-resolved);
}
.status-pill.ongoing {
background: var(--accent-soft);
color: var(--accent);
}
.status-pill.other {
background: var(--bg-muted);
color: var(--text-muted);
}
/* ==========================================================================
Toasts de notification (ouverture d'intervention)
========================================================================== */
.toast-stack {
position: fixed;
bottom: 20px;
right: 20px;
z-index: 200;
display: flex;
flex-direction: column-reverse; /* les nouveaux en bas, les anciens au-dessus */
gap: 8px;
pointer-events: none;
}
.toast {
min-width: 200px;
max-width: 340px;
padding: 10px 14px;
background: var(--bg-elevated);
color: var(--text);
border: 1px solid var(--border-strong);
border-left: 3px solid var(--c-livraison);
border-radius: 6px;
box-shadow: var(--shadow-hover);
font-size: 13px;
opacity: 0;
transform: translateX(40px);
transition: opacity 0.18s ease-out, transform 0.18s ease-out;
pointer-events: auto;
}
.toast.visible {
opacity: 1;
transform: translateX(0);
}
.toast.leaving {
opacity: 0;
transform: translateX(40px);
}
.toast-label {
color: var(--text-muted);
font-size: 11px;
margin-right: 6px;
}
.toast-ref {
font-family: var(--mono);
font-weight: 700;
letter-spacing: 0.02em;
}
/* ─────────────────────────────────────────────────────────────────────────
v4.1.20 : Modal central de confirmation (vider cache)
───────────────────────────────────────────────────────────────────────── */
.modal-overlay {
position: fixed;
inset: 0;
z-index: 10000;
display: flex;
align-items: center;
justify-content: center;
/* Flou + assombrissement léger de l'arrière-plan */
background: rgba(0, 0, 0, 0.35);
backdrop-filter: blur(4px);
-webkit-backdrop-filter: blur(4px);
animation: modal-fade-in 0.15s ease-out;
}
@keyframes modal-fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
.modal-card {
background: var(--bg, #ffffff);
color: var(--text, #111);
border: 1px solid var(--border, rgba(128, 128, 128, 0.25));
border-radius: 12px;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.25),
0 2px 8px rgba(0, 0, 0, 0.15);
padding: 24px 24px 20px;
width: min(440px, 92vw);
max-height: 90vh;
overflow-y: auto;
animation: modal-card-in 0.18s cubic-bezier(0.16, 1, 0.3, 1);
}
@keyframes modal-card-in {
from { opacity: 0; transform: translateY(8px) scale(0.98); }
to { opacity: 1; transform: translateY(0) scale(1); }
}
.modal-title {
margin: 0 0 12px 0;
font-size: 18px;
font-weight: 700;
color: var(--text, #111);
}
.modal-message {
margin: 0 0 20px 0;
font-size: 13px;
line-height: 1.5;
color: var(--text-muted, #555);
}
.modal-actions {
display: flex;
flex-direction: column;
gap: 8px;
}
.modal-actions .btn {
width: 100%;
padding: 10px 14px;
font-size: 13px;
font-weight: 600;
text-align: center;
border-radius: 8px;
cursor: pointer;
transition: background 0.12s, transform 0.06s;
border: 1px solid transparent;
}
.modal-actions .btn:active { transform: translateY(1px); }
/* Vider le cache du jour : danger modéré (orange) */
.btn-modal-danger {
background: rgba(234, 128, 38, 0.12);
color: #c85a00;
border-color: rgba(234, 128, 38, 0.3);
}
.btn-modal-danger:hover {
background: rgba(234, 128, 38, 0.22);
}
/* Vider tout le cache : danger fort (rouge) */
.btn-modal-danger-strong {
background: rgba(220, 60, 60, 0.12);
color: #c03030;
border-color: rgba(220, 60, 60, 0.3);
}
.btn-modal-danger-strong:hover {
background: rgba(220, 60, 60, 0.22);
}
/* Annuler : neutre */
.btn-modal-cancel {
background: transparent;
color: var(--text-muted, #666);
border-color: var(--border, rgba(128, 128, 128, 0.3));
margin-top: 4px;
}
.btn-modal-cancel:hover {
background: var(--bg-hover, rgba(128, 128, 128, 0.08));
}
/* v4.2.5 : bouton primaire (action principale) pour modals d'alerte */
.btn-modal-primary {
background: var(--c-accent, #3fb950);
color: #fff;
border-color: var(--c-accent, #3fb950);
}
.btn-modal-primary:hover {
filter: brightness(1.08);
}
/* ─────────────────────────────────────────────────────────────────────────
v4.1.20 : Message d'absence récurrente (Pillonel vendredi)
───────────────────────────────────────────────────────────────────────── */
.tech-absence-recurring {
padding: 14px 12px;
text-align: center;
font-size: 13px;
font-style: italic;
color: var(--text-faint, #888);
background: rgba(128, 128, 128, 0.04);
border-top: 1px solid var(--border, rgba(128, 128, 128, 0.15));
border-bottom: 1px solid var(--border, rgba(128, 128, 128, 0.15));
}
/* v4.2 : contact en rouge quand anomalie détectée (Contact + Personne de
contact présents tous les deux dans l'action = situation suspecte).
On signale visuellement pour que l'user aille vérifier dans la fiche. */
.iv-contact-line.iv-contact-anomalie {
color: #dc3030;
}
.iv-contact-line.iv-contact-anomalie .iv-contact,
.iv-contact-line.iv-contact-anomalie .iv-phone {
color: #dc3030;
}
/* v4.2 : badge utilisateur EasyVista connecté (en haut à droite de la topbar) */
.current-user {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 4px 10px;
font-size: 12px;
font-weight: 500;
color: var(--text-muted, #666);
background: rgba(128, 128, 128, 0.08);
border: 1px solid var(--border, rgba(128, 128, 128, 0.2));
border-radius: 999px;
margin-right: 8px;
max-width: 220px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.current-user::before {
content: "👤";
font-size: 11px;
opacity: 0.7;
}
.current-user.hidden {
display: none;
}
/* ─────────────────────────────────────────────────────────────────────────
v4.2.3 : pastille utilisateur (initiales) dans la topbar gauche
───────────────────────────────────────────────────────────────────────── */
.user-badge {
display: inline-flex;
align-items: center;
justify-content: center;
width: 30px;
height: 30px;
padding: 0;
margin-right: 10px;
font-size: 11px;
font-weight: 700;
letter-spacing: 0.5px;
color: #fff;
background: var(--user-badge-color, #5b6372);
border: 1px solid rgba(255, 255, 255, 0.12);
border-radius: 50%;
cursor: pointer;
transition: transform 0.1s, box-shadow 0.12s;
flex-shrink: 0;
user-select: none;
}
.user-badge:hover {
transform: scale(1.06);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}
.user-badge:active {
transform: scale(0.97);
}
.user-badge.hidden {
display: none;
}
.user-badge.open {
/* Indication visuelle quand la popup nom est ouverte */
box-shadow: 0 0 0 2px var(--user-badge-color, #5b6372) inset,
0 0 0 2px rgba(255, 255, 255, 0.15);
}
/* Popup du nom complet, affichée juste sous la pastille au clic */
.user-name-popup {
position: fixed;
z-index: 10050;
padding: 8px 14px;
background: var(--bg, #ffffff);
color: var(--text, #111);
border: 1px solid var(--border, rgba(128, 128, 128, 0.25));
border-radius: 8px;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18),
0 2px 6px rgba(0, 0, 0, 0.12);
font-size: 13px;
font-weight: 500;
white-space: nowrap;
animation: user-popup-in 0.12s ease-out;
}
.user-name-popup.hidden {
display: none;
}
@keyframes user-popup-in {
from { opacity: 0; transform: translateY(-4px); }
to { opacity: 1; transform: translateY(0); }
}
/* ─────────────────────────────────────────────────────────────────────────
v4.2.6 : boutons d'action topbar (Absence, Douchette)
───────────────────────────────────────────────────────────────────────── */
.btn-action {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 6px 12px;
font-size: 13px;
font-weight: 500;
background: transparent;
color: var(--text, #e0e0e0);
border: 1px solid var(--border, rgba(128, 128, 128, 0.3));
border-radius: 6px;
cursor: pointer;
transition: background 0.12s, border-color 0.12s;
}
.btn-action:hover {
background: var(--bg-hover, rgba(128, 128, 128, 0.12));
border-color: var(--border-strong, rgba(128, 128, 128, 0.5));
}
.btn-action:active {
transform: translateY(1px);
}
.btn-action-icon {
width: 16px;
height: 16px;
flex-shrink: 0;
}
.btn-action-emoji {
font-size: 14px;
line-height: 1;
}
.btn-action-label {
white-space: nowrap;
}
/* ─────────────────────────────────────────────────────────────────────────
v4.2.6 : modals Absence et Douchette
───────────────────────────────────────────────────────────────────────── */
.modal-card.modal-wide {
width: min(520px, 92vw);
}
.modal-form-group {
display: flex;
flex-direction: column;
gap: 6px;
margin-bottom: 14px;
}
.modal-form-row {
display: flex;
gap: 8px;
align-items: center;
}
.modal-form-row > * {
flex: 1;
}
.modal-form-label {
font-size: 12px;
font-weight: 500;
color: var(--text-muted, #888);
text-transform: uppercase;
letter-spacing: 0.3px;
}
.modal-form-input,
.modal-form-select {
padding: 8px 10px;
font-size: 13px;
background: var(--bg, #fff);
color: var(--text, #111);
border: 1px solid var(--border, rgba(128, 128, 128, 0.3));
border-radius: 6px;
font-family: inherit;
}
.modal-form-input:focus,
.modal-form-select:focus {
outline: none;
border-color: var(--c-accent, #3fb950);
box-shadow: 0 0 0 2px rgba(63, 185, 80, 0.15);
}
/* Liste checkboxes techniciens */
.modal-tech-list {
display: flex;
flex-direction: column;
gap: 4px;
/* v4.2.8 : plus de max-height → tous les techs (max 8 + "Tout") visibles
d'un coup sans avoir à scroller dans la liste. */
padding: 6px;
background: var(--bg-muted, rgba(128, 128, 128, 0.06));
border: 1px solid var(--border, rgba(128, 128, 128, 0.2));
border-radius: 6px;
}
.modal-tech-item {
display: flex;
align-items: center;
gap: 10px;
padding: 6px 8px;
border-radius: 4px;
cursor: pointer;
font-size: 13px;
transition: background 0.1s;
}
.modal-tech-item:hover {
background: var(--bg-hover, rgba(128, 128, 128, 0.12));
}
.modal-tech-item input[type="checkbox"] {
width: 15px;
height: 15px;
cursor: pointer;
accent-color: var(--c-accent, #3fb950);
}
.modal-tech-item.tech-selectall {
font-weight: 600;
border-bottom: 1px solid var(--border, rgba(128, 128, 128, 0.2));
padding-bottom: 8px;
margin-bottom: 2px;
}
.modal-tech-item.tech-selectall:hover {
background: var(--bg-hover, rgba(128, 128, 128, 0.12));
}
/* Boutons Appliquer/Envoyer/Annuler côte à côte */
.modal-actions.horizontal {
flex-direction: row;
gap: 8px;
}
.modal-actions.horizontal .btn {
flex: 1;
}
/* ─────────────────────────────────────────────────────────────────────────
v4.2.9 : blocage du scroll arrière quand une modal est ouverte.
La classe body.modal-open est ajoutée/retirée automatiquement par
initModalScrollLock() dans viewer.js dès qu'un .modal-overlay existe.
───────────────────────────────────────────────────────────────────────── */
body.modal-open {
overflow: hidden;
}
/* v4.2.9 : pied de page discret en bas à droite — affiche auteur + date + version */
.app-footer {
position: fixed;
right: 8px;
bottom: 4px;
font-size: 10px;
color: var(--text-faint, #8892a0);
opacity: 0.55;
pointer-events: none; /* ne capture pas les clics */
user-select: none;
font-variant-numeric: tabular-nums;
letter-spacing: 0.2px;
z-index: 1; /* sous les modals (qui sont à 10000) */
}
.app-footer:hover {
opacity: 0.85;
}
/* ─────────────────────────────────────────────────────────────────────────
v4.3.0 : conflit d'horaire entre 2 interventions d'un même tech.
Les heures s'affichent en rouge + icône ⚠ à côté.
───────────────────────────────────────────────────────────────────────── */
.iv-time-vertical.iv-time-overlap .iv-time-start,
.iv-time-vertical.iv-time-overlap .iv-time-end,
.iv-time-vertical.iv-time-overlap .iv-time-arrow {
color: var(--danger, #b03030) !important;
font-weight: 700;
}
.iv-time-overlap-warn {
color: var(--danger, #b03030);
font-size: 14px;
font-weight: 700;
line-height: 1;
margin-top: 2px;
cursor: help;
text-align: center;
}
/* ─────────────────────────────────────────────────────────────────────────
v4.3.0 : popups épinglés détachés
Ancrés au contenu (position:absolute coord document) → scrollent avec
la page. Persistent jusqu'à fermeture explicite.
───────────────────────────────────────────────────────────────────────── */
.tooltip.pinned-popup {
position: absolute !important; /* override le fixed du .tooltip */
/* v4.3.3 corr : les popups épinglées doivent passer DERRIÈRE la topbar
quand on scrolle (topbar sticky z-index 10). Donc on met 5 : au-dessus
du contenu normal, mais sous la topbar / bannières / modals. */
z-index: 5 !important;
opacity: 1 !important;
pointer-events: auto !important;
/* Bordure plus visible pour distinguer du tooltip live */
border: 2px solid var(--accent, #0f4f8b);
box-shadow: 0 8px 24px rgba(0,0,0,0.18);
/* Pas de contain: layout (hérité) car ça limite le rendu ; on laisse */
animation: pinned-popup-in 0.15s ease-out;
/* Le padding-top est augmenté pour accueillir la barre de drag. */
padding-top: 28px !important;
}
@keyframes pinned-popup-in {
from { opacity: 0; transform: scale(0.96); }
to { opacity: 1; transform: scale(1); }
}
/* v4.3.3 : animation de sortie (symétrique à l'apparition) quand on
désépingle. Appliquée par la classe .unpinning. */
.tooltip.pinned-popup.unpinning,
.tooltip.soft-unpinned.unpinning {
animation: pinned-popup-out 0.18s ease-in forwards !important;
}
@keyframes pinned-popup-out {
from { opacity: 1; transform: scale(1); }
to { opacity: 0; transform: scale(0.94); }
}
/* v4.3.3 corr : quand une popup est désépinglée "mou", elle perd son look
"épinglé" et redevient un tooltip normal visuellement, tout en gardant
sa position absolute (pour ne pas sauter). */
.tooltip.soft-unpinned {
position: absolute !important;
z-index: 5 !important;
opacity: 1 !important;
pointer-events: auto !important;
/* Pas de bordure bleue, pas de padding-top (plus de dragbar), juste les
styles de base du tooltip (hérités de .tooltip). */
border: 1px solid var(--border-strong) !important;
box-shadow: var(--shadow-hover) !important;
padding-top: 12px !important;
animation: none !important;
}
/* v4.3.3 : Barre de drag en haut de la popup épinglée, permet de la
déplacer (le contenu lui-même garde la sélection de texte possible). */
.pinned-popup-dragbar {
position: absolute;
top: 0;
left: 0;
right: 0;
height: 22px;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(
to bottom,
var(--bg-muted, rgba(128,128,128,0.08)) 0%,
transparent 100%
);
border-bottom: 1px solid var(--border, rgba(128,128,128,0.15));
border-radius: 6px 6px 0 0;
cursor: grab;
user-select: none;
-webkit-user-select: none;
}
.pinned-popup-dragbar:active,
.pinned-popup.dragging .pinned-popup-dragbar {
cursor: grabbing;
}
/* Petite grippe visuelle au milieu pour signaler que c'est déplaçable */
.pinned-popup-dragbar::before {
content: "";
width: 32px;
height: 3px;
border-radius: 3px;
background: var(--border-strong, rgba(128,128,128,0.35));
}
/* Pendant le drag, on fige l'animation pour éviter les tremblements */
.pinned-popup.dragging {
animation: none !important;
transition: none !important;
cursor: grabbing !important;
box-shadow: 0 12px 32px rgba(0,0,0,0.28);
}
/* Bouton × de fermeture du popup épinglé */
.pinned-popup-close {
position: absolute;
top: 3px;
right: 6px;
width: 22px;
height: 22px;
padding: 0;
line-height: 1;
font-size: 18px;
font-weight: 400;
color: var(--text-muted, #888);
background: transparent;
border: none;
border-radius: 4px;
cursor: pointer;
transition: background 0.1s, color 0.1s;
z-index: 2; /* au-dessus de la dragbar */
}
.pinned-popup-close:hover {
background: var(--danger-soft, #fbe6e6);
color: var(--danger, #b03030);
}
/* ─────────────────────────────────────────────────────────────────────────
v5.0.0 : horloge au milieu de la topbar (HH:MM, pas de secondes)
───────────────────────────────────────────────────────────────────────── */
.app-clock {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
font-size: 22px;
font-weight: 600;
font-variant-numeric: tabular-nums;
color: var(--text);
letter-spacing: 1px;
pointer-events: none;
user-select: none;
}
.topbar { position: sticky; /* déja défini plus haut */ }
/* topbar doit être en position: relative parent pour que .app-clock absolute
se positionne par rapport à elle */
header.topbar { position: sticky !important; }
header.topbar::before {
content: "";
position: absolute;
inset: 0;
pointer-events: none;
}
/* ─────────────────────────────────────────────────────────────────────────
v5.0.0 : ligne rouge "heure actuelle" sur la timeline (uniquement si on
affiche la date d'aujourd'hui). v5.0.1 : plus visible.
───────────────────────────────────────────────────────────────────────── */
.timeline-now-line {
position: absolute;
top: -2px;
bottom: -2px;
width: 4px;
background: #ff3030;
z-index: 5;
pointer-events: none;
box-shadow: 0 0 6px rgba(255, 48, 48, 0.8),
0 0 2px rgba(255, 48, 48, 1);
border-radius: 2px;
margin-left: -2px; /* centre la barre sur la position exacte */
}
.timeline-now-line::after {
content: "";
position: absolute;
top: -4px;
left: 50%;
transform: translateX(-50%);
width: 12px;
height: 12px;
background: #ff3030;
border-radius: 50%;
box-shadow: 0 0 8px rgba(255, 48, 48, 0.9);
}
/* ─────────────────────────────────────────────────────────────────────────
v5.0.0 : Panel admin (menu caché 5 clics sur titre)
───────────────────────────────────────────────────────────────────────── */
.admin-overlay {
/* hérite de .modal-overlay */
align-items: flex-start;
padding: 30px 20px;
}
.admin-panel-card {
background: var(--bg-elevated);
border: 1px solid var(--border);
border-radius: 8px;
width: 100%;
max-width: 1100px;
height: calc(100vh - 60px);
display: flex;
flex-direction: column;
box-shadow: 0 12px 40px rgba(0,0,0,0.3);
overflow: hidden;
}
.admin-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 20px;
border-bottom: 1px solid var(--border);
background: var(--bg);
}
.admin-title {
margin: 0;
font-size: 18px;
font-weight: 600;
}
.admin-close-btn {
background: transparent;
border: none;
font-size: 24px;
line-height: 1;
cursor: pointer;
padding: 4px 10px;
color: var(--text-muted);
border-radius: 4px;
}
.admin-close-btn:hover {
background: var(--danger-soft);
color: var(--danger);
}
.admin-body {
display: flex;
flex: 1;
min-height: 0;
}
.admin-sidebar {
width: 180px;
background: var(--bg);
border-right: 1px solid var(--border);
padding: 10px 0;
display: flex;
flex-direction: column;
gap: 2px;
flex-shrink: 0;
}
.admin-nav-btn {
text-align: left;
padding: 10px 18px;
background: transparent;
border: none;
cursor: pointer;
font-size: 14px;
color: var(--text);
border-left: 3px solid transparent;
transition: background 0.12s, border-color 0.12s;
}
.admin-nav-btn:hover {
background: var(--bg-hover);
}
.admin-nav-btn.active {
background: var(--bg-elevated);
border-left-color: var(--accent);
font-weight: 600;
}
.admin-content {
flex: 1;
padding: 20px 24px;
overflow-y: auto;
}
.admin-section-title {
margin: 0 0 8px 0;
font-size: 20px;
font-weight: 600;
}
.admin-section-desc {
margin: 0 0 16px 0;
color: var(--text-muted);
font-size: 13px;
}
.admin-team-table {
width: 100%;
border-collapse: collapse;
margin-top: 10px;
}
.admin-team-table th,
.admin-team-table td {
padding: 8px 10px;
border-bottom: 1px solid var(--border);
text-align: left;
vertical-align: middle;
}
.admin-team-table th {
background: var(--bg);
font-size: 12px;
text-transform: uppercase;
letter-spacing: 0.5px;
font-weight: 600;
color: var(--text-muted);
}
.admin-input {
width: 100%;
padding: 6px 8px;
border: 1px solid var(--border);
border-radius: 4px;
background: var(--bg);
color: var(--text);
font-size: 13px;
box-sizing: border-box;
}
.admin-input-id {
font-family: var(--mono);
max-width: 100px;
}
.admin-day-cb {
display: inline-flex;
align-items: center;
gap: 2px;
margin-right: 6px;
font-size: 11px;
cursor: pointer;
user-select: none;
}
.admin-day-cb input[type="checkbox"] {
margin: 0 2px 0 0;
}
.admin-del-btn {
background: transparent;
border: none;
cursor: pointer;
font-size: 16px;
color: var(--text-muted);
padding: 4px 8px;
border-radius: 4px;
}
.admin-del-btn:hover {
background: var(--danger-soft);
color: var(--danger);
}
.admin-readonly {
background: var(--bg);
border: 1px solid var(--border);
border-radius: 4px;
padding: 12px;
font-family: var(--mono);
font-size: 12px;
overflow-x: auto;
}
.admin-diag-grid {
display: grid;
grid-template-columns: 200px 1fr;
gap: 8px 16px;
margin: 16px 0;
font-size: 13px;
}
.admin-diag-grid > div {
padding: 4px 0;
}
/* ─────────────────────────────────────────────────────────────────────────
v5.0.0 : bouton supprimer dans le tooltip (absence / réservation)
───────────────────────────────────────────────────────────────────────── */
.tooltip-delete-btn {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 5px 10px;
background: var(--danger-soft, #fbe6e6);
border: 1px solid var(--danger, #b03030);
color: var(--danger, #b03030);
border-radius: 4px;
cursor: pointer;
font-size: 12px;
font-weight: 500;
margin-top: 4px;
}
.tooltip-delete-btn:hover:not(:disabled) {
background: var(--danger, #b03030);
color: #fff;
}
.tooltip-delete-btn:disabled {
opacity: 0.6;
cursor: wait;
}
/* Bouton danger dans les modals */
.btn-danger,
.modal-btn-danger {
background: var(--danger, #b03030);
color: #fff;
border: 1px solid var(--danger, #b03030);
}
.btn-danger:hover,
.modal-btn-danger:hover {
background: #8e2020;
}
/* v5.0.1 : ligne d'équipe exclue (pas cochée) - apparaît grisée */
.admin-team-table tr.admin-row-excluded {
opacity: 0.45;
}
.admin-team-table tr.admin-row-excluded input[type="text"] {
background: var(--bg);
}
/* v5.0.1 : bouton supprimer sur la carte "Absent toute la journée" */
.absence-delete-wrap {
margin-top: 8px;
text-align: center;
}
.absence-delete-wrap .tooltip-delete-btn {
font-size: 11px;
padding: 4px 8px;
}
/* v5.0.4 : boutons preset matin / après-midi / journée dans modal absence */
.modal-preset-row {
gap: 8px;
flex-wrap: wrap;
}
.modal-preset-btn {
flex: 1;
min-width: 100px;
padding: 8px 10px;
font-size: 13px;
cursor: pointer;
}
/* ==========================================================================
v5.0.9 : Compteur de session EasyVista (topbar)
========================================================================== */
.app-session {
position: absolute;
top: 50%;
left: calc(50% + 60px); /* à droite de l'horloge (~60px de décalage) */
transform: translateY(-50%);
display: flex;
align-items: center;
gap: 8px;
padding: 4px 10px;
border-radius: 14px;
font-size: 13px;
font-weight: 500;
font-variant-numeric: tabular-nums;
z-index: 9;
background: rgba(0, 0, 0, 0.05);
transition: background 0.3s, color 0.3s;
}
.app-session.hidden {
display: none;
}
.app-session .session-icon {
font-size: 14px;
}
.app-session .session-time {
font-weight: 600;
}
.app-session .session-extend-btn {
margin-left: 4px;
padding: 3px 8px;
font-size: 11px;
border-radius: 10px;
border: 1px solid currentColor;
background: transparent;
color: inherit;
cursor: pointer;
font-weight: 500;
transition: background 0.2s;
}
.app-session .session-extend-btn:hover {
background: rgba(255, 255, 255, 0.2);
}
.app-session .session-extend-btn:disabled {
opacity: 0.6;
cursor: default;
}
/* État warning (2-5 min) : jaune */
.app-session.session-warn {
background: #f5c518;
color: #2a2100;
}
.app-session.session-warn .session-extend-btn {
border-color: #2a2100;
}
/* État critical (< 2 min) : rouge + pulse */
.app-session.session-critical {
background: #e74c3c;
color: #fff;
animation: session-pulse 1s infinite;
}
.app-session.session-critical .session-extend-btn {
border-color: #fff;
background: rgba(255, 255, 255, 0.15);
font-weight: 600;
}
.app-session.session-critical .session-extend-btn:hover {
background: rgba(255, 255, 255, 0.3);
}
@keyframes session-pulse {
0%, 100% { box-shadow: 0 0 0 0 rgba(231, 76, 60, 0.5); }
50% { box-shadow: 0 0 0 6px rgba(231, 76, 60, 0); }
}
/* Bouton "Me reconnecter" dans la bannière session expirée */
.session-expired-reconnect-btn {
margin-left: 12px;
padding: 6px 14px;
border-radius: 4px;
background: #fff;
color: #c0392b;
border: none;
font-weight: 600;
cursor: pointer;
font-size: 13px;
transition: background 0.2s;
}
.session-expired-reconnect-btn:hover {
background: #f8d7da;
}
/* Bannière "Reconnexion en cours" */
.banner-reconnecting {
background: #3498db;
color: #fff;
padding: 10px 20px;
display: flex;
align-items: center;
gap: 10px;
font-size: 14px;
font-weight: 500;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
}
.banner-reconnecting.hidden {
display: none;
}
.banner-reconnecting .banner-spinner {
font-size: 16px;
animation: spin-slow 2s linear infinite;
}
@keyframes spin-slow {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
/* ==========================================================================
v5.0.11 : bannières reconnect avec boutons Annuler + choix réseau
========================================================================== */
.banner-reconnecting .banner-cancel-btn,
.banner-reconnect-failed .banner-cancel-btn {
margin-left: auto;
padding: 5px 12px;
background: transparent;
color: inherit;
border: 1px solid currentColor;
border-radius: 4px;
font-size: 13px;
cursor: pointer;
font-weight: 500;
transition: background 0.2s;
}
.banner-reconnecting .banner-cancel-btn:hover,
.banner-reconnect-failed .banner-cancel-btn:hover {
background: rgba(255, 255, 255, 0.15);
}
.banner-reconnect-failed {
background: #e67e22;
color: #fff;
padding: 10px 20px;
display: flex;
align-items: center;
gap: 12px;
font-size: 14px;
font-weight: 500;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
}
.banner-reconnect-failed.hidden {
display: none;
}
.banner-reconnect-failed .banner-icon {
font-size: 18px;
}
.banner-reconnect-failed .banner-btn-primary {
padding: 6px 14px;
border-radius: 4px;
background: #fff;
color: #c0392b;
border: none;
font-weight: 600;
cursor: pointer;
font-size: 13px;
transition: background 0.2s;
}
.banner-reconnect-failed .banner-btn-primary:hover {
background: #f8d7da;
}