/* ============================================================================
   app.css — composants des pages publiques Michel Vision
   ----------------------------------------------------------------------------
   Chargé après base.css, qui fournit les tokens, le reset et les primitives.
   Ce fichier ne définit AUCUN token : s'il te faut une valeur, elle est dans
   base.css ou elle n'a pas lieu d'exister.

   Le CSS des pages d'administration vit dans legacy.css, chargé uniquement par
   celles-ci via le bloc {% block vendor_css %} de admin_base.html.
   ========================================================================== */

/* ── Socle applicatif ─────────────────────────────────────────────────────────
   Polices chargées via <link rel="preconnect"> + <link> dans layout.html <head>
   (évite la chaîne render-blocking sérialisée CSS → @import → woff2).

   Tokens, reset, body et typographie de base vivent dans base.css : ce fichier
   ne contient plus que ce qui leur est spécifique — aucune redéfinition de
   token ici. */

main { flex: 1; }

/* Nuance de survol des liens : l'ambre s'éclaircit, elle ne change pas de teinte. */
@media (hover: hover) and (pointer: fine) {
    a:hover { color: #FFB224; }
}

/* Bootstrap applique `color: …!important` sur .text-muted : il faut le même
   poids pour reprendre la main sur les pages qui chargent encore bootstrap. */
.text-muted { color: var(--mv-text-muted) !important; }

.mono { font-family: var(--mv-font-mono); }


/* ── En-tête de page ──────────────────────────────────────────────────────── */
.page-header { margin-bottom: var(--s5); }

.page-subtitle {
    max-width: 62ch;
    margin-bottom: 0;
    color: var(--mv-text-muted);
    font-size: var(--t-sm);
}

/* Coque de contenu : fond relevé + filet 1 px. Ni ombre, ni flou. */
.content-shell {
    background: var(--mv-bg-raised);
    border: 1px solid var(--mv-border);
    border-radius: var(--r2);
    padding: clamp(var(--s4), 3vw, var(--s6));
}


/* ── Navbar ───────────────────────────────────────────────────────────────────
   Opaque, en flux normal, une seule apparence : plus de flou, plus d'ombre,
   plus de variante « scrolled ». */
.navbar {
    background: var(--mv-bg-raised) !important;
    border-bottom: 1px solid var(--mv-line);
    padding: var(--s3) 0;
}

.navbar-brand {
    font-family: var(--mv-font-body);
    font-weight: 800;
    font-size: var(--t-body);
    color: var(--mv-text) !important;
    letter-spacing: -0.02em;
    display: inline-flex;
    align-items: center;
    gap: var(--s2);
}

/* Pastille d'état — reste ronde (exception assumée aux deux rayons). */
.brand-dot {
    width: 10px;
    height: 10px;
    background: var(--mv-accent);
    border-radius: 50%;
    flex-shrink: 0;
}

.navbar-nav .nav-link {
    color: var(--mv-text-muted) !important;
    font-size: var(--t-sm);
    font-weight: 600;
    padding: var(--s2) var(--s3) !important;
    border-radius: var(--r1);
    transition: background-color var(--dur-1) var(--ease-out),
                color var(--dur-1) var(--ease-out);
}

/* !important conservé : Bootstrap cible .navbar-nav .nav-link.active (0,3,0)
   et gagnerait sur ce sélecteur par spécificité, quel que soit l'ordre. */
.nav-link.active {
    color: var(--mv-text) !important;
    background: var(--mv-accent-wash);
}

.nav-avatar {
    width: 30px;
    height: 30px;
    background: var(--mv-accent);
    color: var(--mv-accent-ink);
    border-radius: 50%;
    font-size: var(--t-meta);
    font-weight: 800;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.nav-email { color: var(--mv-text-muted) !important; font-size: var(--t-meta); }

.navbar-toggler {
    border: 1px solid var(--mv-border);
    border-radius: var(--r2);
    padding: var(--s1) var(--s2);
    color: var(--mv-text-muted);
    background: transparent;
}

/* Neutralise l'anneau de focus de Bootstrap : le :focus-visible de base.css
   (outline ambre) est la seule indication de focus du site. */
.navbar-toggler:focus { box-shadow: none; }

/* Calque flottant réellement superposé : seul endroit du fichier où une ombre
   est autorisée. */
.dropdown-menu {
    background: var(--mv-bg-overlay);
    border: 1px solid var(--mv-border);
    border-radius: var(--r2);
    box-shadow: var(--shadow-overlay);
    padding: var(--s1);
    min-width: 210px;
}

.dropdown-item {
    color: var(--mv-text);
    border-radius: var(--r1);
    font-size: var(--t-sm);
    padding: var(--s2) var(--s3);
    display: flex;
    align-items: center;
    transition: background-color var(--dur-1) var(--ease-out),
                color var(--dur-1) var(--ease-out);
}

.dropdown-item.text-danger { color: var(--mv-danger) !important; }
.dropdown-divider { border-color: var(--mv-line); margin: var(--s1) 0; }
.dropdown-item-text { padding: var(--s2) var(--s3); color: var(--mv-text-muted); font-size: var(--t-meta); }

@media (hover: hover) and (pointer: fine) {
    .navbar-nav .nav-link:hover {
        color: var(--mv-text) !important;
        background: rgba(255, 255, 255, 0.06);
    }
    .dropdown-item:hover { background: rgba(255, 255, 255, 0.06); color: var(--mv-text); }
    /* Le rouge du libellé porte déjà l'information : le survol reste neutre. */
    .dropdown-item.text-danger:hover { background: rgba(255, 255, 255, 0.06); }
}


/* ── Pastille de pouls ────────────────────────────────────────────────────────
   Témoin « données vivantes » des en-têtes de section d'index.html.
   Reste ronde ; l'amplitude est ramenée à l'opacité seule. */
.pulse-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--mv-accent);
    animation: pulse-anim 1.6s var(--ease-in-out) infinite;
    flex-shrink: 0;
}

.pulse-dot-sm {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--mv-accent);
    animation: pulse-anim 1.6s var(--ease-in-out) infinite;
    flex-shrink: 0;
}

@keyframes pulse-anim {
    0%, 100% { opacity: 1; }
    50%      { opacity: 0.35; }
}

/* ── Grille de fonctionnalités ── */
.feature-grid { margin-bottom: var(--s5); }

.feature-card {
    background: var(--mv-bg-raised);
    border: 1px solid var(--mv-border);
    border-radius: var(--r2);
    height: 100%;
    transition: border-color var(--dur-1) var(--ease-out);
}

@media (hover: hover) and (pointer: fine) {
    .feature-card:hover { border-color: var(--mv-accent-line); }
}

.feature-card .card-body { padding: var(--s5); }

.feature-icon {
    width: 46px;
    height: 46px;
    border-radius: var(--r2);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    margin-bottom: var(--s3);
}

/* Une seule des trois cartes est accentuée : l'analyse est l'action principale.
   Les deux autres sont neutres — la teinte ne distinguait rien, le libellé le fait. */
.feature-icon-accent { background: var(--mv-accent-wash); color: var(--mv-accent); }
.feature-icon-success { background: rgba(255, 255, 255, 0.04); color: var(--mv-text-muted); }
.feature-icon-muted { background: rgba(255, 255, 255, 0.04); color: var(--mv-text-muted); }

.feature-text { color: var(--mv-text-muted); font-size: var(--t-sm); margin-bottom: var(--s4); }

/* ── Panneau de chiffres ── */
.stats-panel {
    background: var(--mv-bg-raised);
    border: 1px solid var(--mv-border);
    border-radius: var(--r2);
    padding: var(--s5);
}

.stat-card { text-align: center; padding: var(--s3); }

.stat-value {
    font-family: var(--mv-font-body);
    font-size: var(--t-h1);
    font-weight: 800;
    letter-spacing: -0.02em;
    color: var(--mv-accent);
    line-height: 1.15;
    font-variant-numeric: tabular-nums;
    margin-bottom: var(--s1);
}

/* ── Cartes ── */
.card {
    background: var(--mv-bg-raised);
    border: 1px solid var(--mv-border);
    border-radius: var(--r2);
}

/* Le relief de l'en-tête vient d'un fond enfoncé + un filet, pas d'une ombre. */
.card-header {
    background: var(--mv-bg-sunken) !important;
    border-bottom: 1px solid var(--mv-line) !important;
    color: var(--mv-text) !important;
    font-weight: 700;
    border-radius: var(--r2) var(--r2) 0 0 !important;
    padding: var(--s3) var(--s4);
}

/* ── Boutons (compléments à base.css) ── */
.btn-light {
    background: var(--mv-text);
    border-color: var(--mv-text);
    color: var(--mv-accent-ink);
}

.btn-outline-light {
    color: var(--mv-text);
    border-color: var(--mv-border);
    background: transparent;
}

@media (hover: hover) and (pointer: fine) {
    .btn-light:hover {
        background: var(--mv-text-muted);
        border-color: var(--mv-text-muted);
        color: var(--mv-accent-ink);
    }

    .btn-outline-light:hover {
        background: rgba(255, 255, 255, 0.06);
        color: var(--mv-text);
        border-color: var(--mv-line-field);
    }
}

/* ── Champs ── */
/* Le menu natif d'un <select> se peint hors page : il lui faut une couleur opaque. */
.form-select option { background: var(--mv-bg-raised); color: var(--mv-text); }

/* ── Tableaux ── */
.table {
    color: var(--mv-text);
    margin-bottom: 0;
    --bs-table-bg: transparent;
    --bs-table-bg-type: transparent;
    --bs-table-color: var(--mv-text);
    --bs-table-color-type: var(--mv-text);
    --bs-table-border-color: var(--mv-line);
    --bs-table-striped-bg: rgba(255, 255, 255, 0.04);
    --bs-table-striped-color: var(--mv-text);
    --bs-table-hover-bg: rgba(255, 255, 255, 0.06);
    --bs-table-hover-color: var(--mv-text);
}

.table thead th {
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--mv-text-muted);
    border-bottom: 1px solid var(--mv-border) !important;
}

.table td { border-color: var(--mv-line); vertical-align: middle; font-variant-numeric: tabular-nums; }

.table-striped > tbody > tr:nth-of-type(odd) > * {
    --bs-table-accent-bg: rgba(255, 255, 255, 0.04);
    color: var(--mv-text);
}

@media (hover: hover) and (pointer: fine) {
    .table-hover tbody tr:hover > * {
        --bs-table-accent-bg: rgba(255, 255, 255, 0.06);
        color: var(--mv-text);
    }
}

/* Ligne mise en avant : accent unique, pas une seconde teinte sémantique. */
.table-success {
    --bs-table-bg: var(--mv-accent-wash);
    --bs-table-color: var(--mv-text);
}

/* ── Authentification ── */
.auth-card {
    background: var(--mv-bg-raised);
    border: 1px solid var(--mv-border);
    border-radius: var(--r2);
    padding: var(--s5);
}

.auth-card h2 { font-size: var(--t-h2); }

/* ── Résultat ── */
.result-vehicle-name {
    font-family: var(--mv-font-body);
    font-size: var(--t-h2);
    font-weight: 800;
    letter-spacing: -0.02em;
    color: var(--mv-text);
    margin-bottom: var(--s1);
}

.result-img-preview {
    width: 100%;
    max-height: 360px;
    object-fit: contain;
    border-radius: var(--r2);
}

/* ── Barres de confiance top-3 ── */
.top3-bar-row {
    display: grid;
    grid-template-columns: 100px 1fr 48px;
    align-items: center;
    gap: var(--s2);
    padding: var(--s1) 0;
}

.top3-bar-row:first-child { padding-top: 0; }

.top3-bar-label {
    font-family: var(--mv-font-mono);
    font-size: var(--t-num);
    font-weight: 700;
    color: var(--mv-text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Le candidat n°1 sur plein ID : accent, jamais une seconde teinte. */
.top3-bar-label.top3-first { color: var(--mv-accent); }

.top3-bar-bg {
    height: 8px;
    background: var(--mv-line);
    border-radius: var(--r1);
    overflow: hidden;
}

/* La largeur est posée en inline par analyze.js / history.html (`style.width`).
   La bascule vers transform: scaleX() demande de toucher ce JS — hors périmètre CSS :
   transform-origin et la transition sur transform sont déjà en place pour l'accueillir,
   et l'ancienne transition sur `width` (qui relayoutait à chaque frame) est supprimée. */
.top3-bar-fill {
    height: 100%;
    background: var(--mv-accent);
    border-radius: var(--r1);
    transform-origin: left;
    transition: transform var(--dur-4) var(--ease-out), opacity var(--dur-2) var(--ease-out);
}

/* Paliers = opacité de l'accent unique, pas trois teintes différentes. */
.top3-bar-fill.bar-success { opacity: 1; }
.top3-bar-fill.bar-accent  { opacity: 0.55; }
.top3-bar-fill.bar-muted   { opacity: 0.3; }

.top3-bar-val {
    font-family: var(--mv-font-mono);
    font-size: var(--t-num);
    color: var(--mv-text-muted);
    text-align: right;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

/* ── Pied de page ── */
.footer {
    margin-top: auto;
    background: var(--mv-bg-sunken);
    border-top: 1px solid var(--mv-line);
    padding: var(--s4) 0;
}

.footer small { color: var(--mv-text-muted); }

/* ── Chargement ── */
.spinner-border { color: var(--mv-accent) !important; }

/* ── Pages d'erreur (403 / 404 / 429) ── */
.error-page {
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    text-align: center; padding: var(--sec-tight) var(--s4);
}

/* Le code d'erreur est une DONNÉE, pas un ornement : très grand chiffre mono, en retrait. */
.error-code {
    font-family: var(--mv-font-mono);
    font-size: clamp(5rem, 15vw, 9rem);
    font-weight: 700;
    line-height: 1;
    letter-spacing: -0.02em;
    font-variant-numeric: tabular-nums;
    color: var(--mv-text-faint);
    margin-bottom: var(--s2);
}

.error-icon {
    width: 64px; height: 64px;
    border-radius: var(--r2);
    border: 1px solid var(--mv-line);
    display: inline-flex; align-items: center; justify-content: center;
    margin-bottom: var(--s5);
}

.error-icon-accent { background: var(--mv-accent-wash); border-color: var(--mv-accent-line); color: var(--mv-accent); }
.error-icon-warning { background: var(--mv-accent-wash); border-color: var(--mv-accent-line); color: var(--mv-accent); }

.error-title {
    font-family: var(--mv-font-body);
    font-size: var(--t-h1);
    font-weight: 800;
    letter-spacing: -0.02em;
    color: var(--mv-text);
    margin-bottom: var(--s3);
}

.error-desc {
    max-width: 420px;
    color: var(--mv-text-muted);
    font-size: var(--t-body);
    margin-bottom: var(--s5);
}

/* Identifiant technique de la réponse HTTP → puce mono, filet 1 px, pas de pilule. */
.error-detail {
    display: inline-block;
    padding: var(--s1) var(--s3);
    border-radius: var(--r1);
    background: var(--mv-bg-raised);
    border: 1px solid var(--mv-line);
    color: var(--mv-text-muted);
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    margin-bottom: var(--s5);
}

/* ── Zone de dépôt — aperçu ── */
.drop-zone-preview {
    max-height: 280px;
    object-fit: contain;
    border-radius: var(--r2);
    width: 100%;
}

.drop-zone.has-preview {
    padding: var(--s4);
}

.drop-zone.has-preview .drop-icon,
.drop-zone.has-preview .drop-zone-text,
.drop-zone.has-preview #select-btn {
    display: none;
}

/* ── Historique — grille de cartes ── */
.history-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: var(--s4);
}

@media (max-width: 991px) {
    .history-grid { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 575px) {
    .history-grid { grid-template-columns: 1fr; }
}

.history-card {
    background: var(--mv-bg-raised);
    border: 1px solid var(--mv-line);
    border-radius: var(--r2);
    padding: var(--s4);
    cursor: pointer;
    transition: border-color var(--dur-1) var(--ease-out),
                background-color var(--dur-1) var(--ease-out);
}

@media (hover: hover) and (pointer: fine) {
    .history-card:hover {
        border-color: var(--mv-accent-line);
    }
}

.history-card.expanded {
    border-color: var(--mv-accent-line);
}

.history-vehicle-name {
    font-family: var(--mv-font-body);
    font-size: var(--t-h3);
    font-weight: 800;
    letter-spacing: -0.01em;
    color: var(--mv-text);
    margin-bottom: var(--s2);
}

/* Abstention : le modèle n'a pas committé sur le sous-type (ou la famille) — on signale
   visuellement que ce n'est pas une identification ferme (cf. az-abstained sur /analyze). */
.history-vehicle-name.is-abstained {
    font-style: italic;
    font-weight: 600;
    color: var(--mv-text-muted);
}

.history-abstain-note {
    display: inline-block;
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--mv-text-muted);
    margin-bottom: var(--s3);
}

.history-family-badge {
    display: inline-block;
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    padding: var(--s1) var(--s2);
    border-radius: var(--r1);
    margin-bottom: var(--s3);
}

/* Les trois familles partagent le même traitement : le libellé texte (CHARS / IFV / APC)
   porte déjà l'information, la teinte ne la double pas. */
.family-chars,
.family-ifv,
.family-apc {
    background: var(--mv-accent-wash);
    color: var(--mv-accent);
    border: 1px solid var(--mv-accent-line);
}

.history-conf-row {
    display: flex;
    align-items: center;
    gap: var(--s2);
    margin-bottom: var(--s2);
}

.history-conf-row .progress {
    flex: 1;
    height: 6px;
}

.history-conf-val {
    font-family: var(--mv-font-mono);
    font-size: var(--t-num);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: var(--mv-text);
    white-space: nowrap;
    min-width: 42px;
    text-align: right;
}

.history-meta {
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.history-date {
    font-size: var(--t-meta);
    font-variant-numeric: tabular-nums;
    color: var(--mv-text-muted);
}

.history-model {
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    font-variant-numeric: tabular-nums;
    color: var(--mv-text-muted);
}

/* Détail dépliable (piloté par [hidden] côté template) */
.history-detail {
    margin-top: var(--s3);
    padding-top: var(--s3);
    border-top: 1px solid var(--mv-line);
}

.history-detail-title {
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--mv-text-muted);
    margin-bottom: var(--s2);
}

.history-detail-empty {
    font-size: var(--t-sm);
    color: var(--mv-text-muted);
    font-style: italic;
}

/* ── Divers ── */
#previous-result-zone .card { background: var(--mv-bg-raised); }
#previous-result-zone .table td { color: var(--mv-text-muted); }

::-webkit-scrollbar { width: 7px; height: 7px; }
::-webkit-scrollbar-track { background: var(--mv-bg-sunken); }
::-webkit-scrollbar-thumb { background: var(--mv-border); border-radius: var(--r1); }

@media (hover: hover) and (pointer: fine) {
    ::-webkit-scrollbar-thumb:hover { background: var(--mv-line-field); }
}

/* ── Reliquats Bootstrap (menu mobile, bouton de fermeture) ── */
.navbar-collapse { background: transparent; }

@media (max-width: 991px) {
    .navbar-collapse.collapse.show,
    .navbar-collapse.collapsing {
        background: var(--mv-bg-raised);
        border-radius: var(--r2);
        border: 1px solid var(--mv-border);
        margin-top: var(--s2);
        padding: var(--s2);
    }
}

.btn-close {
    filter: invert(1) grayscale(100%) brightness(2);
    opacity: 0.5;
    transition: opacity var(--dur-1) var(--ease-out);
}

@media (hover: hover) and (pointer: fine) {
    .btn-close:hover { opacity: 0.9; }
}

/* ── HUD tactique ─────────────────────────────────────────────────────────────
   Le quadrillage plein écran (body::before + masque radial) a été supprimé :
   un instrument de mesure n'a pas de fond décoratif. Le grillage ne survit
   qu'à l'intérieur des cadres image (.analyze-hud-image), en local. */
body > * { position: relative; z-index: 1; }

/* Repères d'angle : la seule signature « HUD » qui reste, en filet 1 px */
.tac-card {
    position: relative;
    overflow: visible;
}

.tac-corner {
    position: absolute;
    width: 12px;
    height: 12px;
    pointer-events: none;
    z-index: 2;
}

.tac-corner-tl { top: -1px; left: -1px; border-top: 1px solid var(--mv-accent); border-left: 1px solid var(--mv-accent); }
.tac-corner-tr { top: -1px; right: -1px; border-top: 1px solid var(--mv-accent); border-right: 1px solid var(--mv-accent); }
.tac-corner-bl { bottom: -1px; left: -1px; border-bottom: 1px solid var(--mv-accent); border-left: 1px solid var(--mv-accent); }
.tac-corner-br { bottom: -1px; right: -1px; border-bottom: 1px solid var(--mv-accent); border-right: 1px solid var(--mv-accent); }

/* Trame de balayage sur l'image analysée : texture 1 px neutre, pas un effet */
.tac-scanline {
    position: relative;
    overflow: hidden;
}

.tac-scanline::after {
    content: "";
    position: absolute;
    inset: 0;
    pointer-events: none;
    background-image: repeating-linear-gradient(
        0deg,
        transparent,
        transparent 3px,
        rgba(230, 234, 240, 0.04) 3px,
        rgba(230, 234, 240, 0.04) 4px
    );
    z-index: 3;
}

/* État de chargement : une ligne pleine qui descend, sans halo */
.tac-scanning::before {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    height: 1px;
    background-color: var(--mv-accent);
    z-index: 4;
    animation: tac-scan 2s var(--ease-in-out) infinite;
}

@keyframes tac-scan {
    0% { top: 0; opacity: 0; }
    10% { opacity: 1; }
    90% { opacity: 1; }
    100% { top: 100%; opacity: 0; }
}

/* Réticule sur l'image analysée */
.tac-crosshair::before,
.tac-crosshair::after {
    content: "";
    position: absolute;
    background-color: var(--mv-accent-line);
    z-index: 4;
    pointer-events: none;
}

.tac-crosshair::before {
    top: 50%;
    left: 10%;
    right: 10%;
    height: 1px;
    transform: translateY(-50%);
}

.tac-crosshair::after {
    left: 50%;
    top: 10%;
    bottom: 10%;
    width: 1px;
    transform: translateX(-50%);
}

/* Étiquette de données superposée à l'image (haut-droite) */
.tac-hud-label {
    position: absolute;
    top: var(--s2);
    right: var(--s2);
    z-index: 5;
    display: flex;
    align-items: center;
    gap: var(--s1);
    padding: var(--s1) var(--s2);
    background-color: var(--mv-bg-overlay);
    border: 1px solid var(--mv-accent-line);
    border-radius: var(--r1);
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    color: var(--mv-accent);
    letter-spacing: 0.06em;
    text-transform: uppercase;
}

/* ── Encyclopédie véhicules ─────────────────────────────────────────────── */
.vehicles-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: var(--s4);
}

.vehicle-card {
    background-color: var(--mv-bg-raised);
    border: 1px solid var(--mv-border);
    border-radius: var(--r2);
    padding: var(--s4);
    transition: border-color var(--dur-1) var(--ease-out);
}

@media (hover: hover) and (pointer: fine) {
    /* Le survol change le filet, pas l'altitude : ni translation ni ombre. */
    .vehicle-card:hover { border-color: var(--mv-accent-line); }
}

.vehicle-card-header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    margin-bottom: var(--s4);
    padding-bottom: var(--s3);
    border-bottom: 1px solid var(--mv-line);
}

.vehicle-name {
    font-family: var(--mv-font-body);
    font-size: var(--t-h3);
    font-weight: 800;
    color: var(--mv-text);
    letter-spacing: -0.02em;
}

.vehicle-origin {
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    color: var(--mv-text-muted);
    letter-spacing: 0.06em;
    text-transform: uppercase;
    white-space: nowrap;
}

.vehicle-specs {
    display: flex;
    flex-direction: column;
    gap: var(--s2);
    margin-bottom: var(--s4);
}

.spec-row {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: var(--s3);
}

.spec-key {
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    color: var(--mv-text-muted);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    flex-shrink: 0;
}

.spec-val {
    font-family: var(--mv-font-mono);
    font-size: var(--t-num);
    color: var(--mv-text);
    font-weight: 700;
    text-align: right;
    font-variant-numeric: tabular-nums;
}

.vehicle-confusions {
    padding-top: var(--s3);
    border-top: 1px solid var(--mv-line);
}

.vehicle-confusions .metric-label {
    display: block;
    margin-bottom: var(--s1);
}

.confusion-tags {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s2);
}

.veh-section-sub {
    display: block;
    margin-top: 2px;
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    color: var(--mv-text-muted);
    font-variant-numeric: tabular-nums;
}

/* Confusions connues : le libellé porte l'information, la teinte ne fait que
   la souligner (fond neutre, pas de pastille colorée). */
.confusion-tag {
    display: inline-flex;
    align-items: center;
    padding: var(--s1) var(--s2);
    border-radius: var(--r2);
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    font-weight: 700;
    background-color: var(--mv-bg-sunken);
    color: var(--mv-text);
    border: 1px solid var(--mv-border);
}

.confusion-tag-none {
    background-color: transparent;
    color: var(--mv-text-faint);
    border-color: var(--mv-line);
    font-weight: 400;
    font-style: italic;
}

/* Pictogrammes de famille : une seule teinte pour les trois — le nom de la
   famille est déjà écrit à côté, la couleur n'a rien à distinguer. */
.family-section-icon {
    width: 42px;
    height: 42px;
    border-radius: var(--r2);
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}

.family-section-chars,
.family-section-ifv,
.family-section-apc {
    background-color: var(--mv-accent-wash);
    color: var(--mv-accent);
}

/* ── Jauge radiale (HUD d'analyse) ──────────────────────────────────────── */
.radial-gauge {
    position: relative;
    width: 110px;
    height: 110px;
    flex-shrink: 0;
}

.radial-gauge svg {
    width: 100%;
    height: 100%;
    transform: rotate(-90deg);
}

.radial-gauge-bg {
    fill: none;
    stroke: var(--mv-line);
    stroke-width: 6;
}

/* stroke-dashoffset : seule propriété capable d'animer un arc — l'équivalent
   radial du scaleX() imposé aux barres de données. */
.radial-gauge-fill {
    fill: none;
    stroke: var(--mv-accent);
    stroke-width: 6;
    stroke-linecap: butt;
    transition: stroke-dashoffset var(--dur-4) var(--ease-out);
}

/* Famille / sous-type / haute confiance : même encre, pas de code couleur. */
.radial-gauge-fill.gauge-family { stroke: var(--mv-accent); opacity: 1; }
.radial-gauge-fill.gauge-sub { stroke: var(--mv-accent); opacity: 0.75; }
.radial-gauge-fill.gauge-high { stroke: var(--mv-accent); opacity: 1; }
/* Jauge neutralisée (axe abstenu) : même encre, franchement atténuée — placé
   après gauge-family/sub pour les surcharger à spécificité égale. */
.radial-gauge-fill.gauge-abstained { stroke: var(--mv-accent); opacity: 0.4; }

.radial-gauge-label {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
}

.radial-gauge-pct {
    font-family: var(--mv-font-mono);
    font-size: var(--t-h2);
    font-weight: 700;
    color: var(--mv-text);
    line-height: 1;
    font-variant-numeric: tabular-nums;
}

.radial-gauge-text {
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    color: var(--mv-text-muted);
    text-transform: uppercase;
    letter-spacing: 0.1em;
    margin-top: var(--s1);
}

/* ── HUD d'analyse (mise en page) ───────────────────────────────────────── */
.analyze-hud {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0;
    min-height: 420px;
}

/* Le quadrillage tactique ne vit plus que là : dans le cadre image, en local,
   sans masque — il matérialise le champ de mesure autour de la photo. */
.analyze-hud-image {
    position: relative;
    background-color: var(--mv-bg-sunken);
    background-image:
        linear-gradient(rgba(230, 234, 240, 0.04) 1px, transparent 1px),
        linear-gradient(90deg, rgba(230, 234, 240, 0.04) 1px, transparent 1px);
    background-size: 32px 32px;
    border-right: 1px solid var(--mv-border);
    border-radius: var(--r2) 0 0 var(--r2);
    overflow: hidden;
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 300px;
}

.analyze-hud-image img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    max-height: 450px;
}

.analyze-hud-data {
    padding: var(--s5);
    display: flex;
    flex-direction: column;
    border-radius: 0 var(--r2) var(--r2) 0;
}

.analyze-gauges {
    display: flex;
    gap: var(--s5);
    justify-content: center;
    margin-bottom: var(--s2);
}

@media (max-width: 767px) {
    .analyze-hud {
        grid-template-columns: 1fr;
    }
    .analyze-hud-image {
        border-right: none;
        border-bottom: 1px solid var(--mv-border);
        border-radius: var(--r2) var(--r2) 0 0;
        min-height: 220px;
    }
    .analyze-hud-data {
        border-radius: 0 0 var(--r2) var(--r2);
    }
    .vehicles-grid {
        grid-template-columns: 1fr;
    }
}

/* ── Animations ─────────────────────────────────────────────────────────── */
@keyframes reveal-up {
    from { opacity: 0; transform: translateY(12px); }
    to { opacity: 1; transform: translateY(0); }
}

/* ── Responsive ─────────────────────────────────────────────────────────── */
@media (max-width: 991px) {
    .feature-card .card-body { padding: var(--s4); }
    .navbar-nav .nav-link { padding: var(--s2) var(--s3) !important; }
}

@media (max-width: 767px) {
    .content-shell { border-radius: var(--r2); padding: var(--s4); }
}

/* Défilement doux pour les ancres — jamais si le visiteur a demandé moins de
   mouvement (le bloc reduce complet vit dans base.css). */
@media (prefers-reduced-motion: no-preference) {
    html { scroll-behavior: smooth; }
}

/* ── A6 · Bande de rythme, séquence « Comment ça marche », tagline ── */

/* Bande de rythme : encadre une section « contexte/données » d'un fond creusé discret
   pour rompre la monotonie des sections empilées sur le même fond. */
.mv-band {
    background: var(--mv-bg-sunken);
    border-top: 1px solid var(--mv-line);
    border-bottom: 1px solid var(--mv-line);
    border-radius: var(--r2);
    padding: var(--s6) var(--s5);
    margin: var(--s7) 0;
}
.mv-band > section { margin: 0; }

/* Séquence « Comment ça marche » : proposition de valeur statique, scannable, sous la tagline.
   3 étapes lues de haut en bas, séparées par un filet 1 px ; la 3e (--key) met en avant
   l'abstention (filet d'accent à gauche + lavis). */
.steps-section { margin: var(--s7) 0; }
.steps-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: 0;
    border-top: 1px solid var(--mv-line);
}
.step-card {
    display: flex;
    gap: var(--s3);
    align-items: flex-start;
    background: transparent;
    border: 0;
    border-bottom: 1px solid var(--mv-line);
    padding: var(--s4) var(--s3);
}
.step-card--key {
    background: var(--mv-accent-wash);
    border-left: 2px solid var(--mv-accent);
    padding-left: calc(var(--s3) - 2px);
}
.step-num {
    flex-shrink: 0;
    width: 2rem;
    height: 2rem;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%; /* pastille d'état : reste ronde */
    font-family: var(--mv-font-mono);
    font-weight: 700;
    font-size: var(--t-num);
    font-variant-numeric: tabular-nums;
    color: var(--mv-text-muted);
    background: transparent;
    border: 1px solid var(--mv-border);
}
.step-card--key .step-num {
    color: var(--mv-accent);
    background: var(--mv-accent-wash);
    border-color: var(--mv-accent-line);
}
.step-title {
    font-family: var(--mv-font-body);
    font-weight: 800;
    font-size: var(--t-body);
    margin-bottom: var(--s1);
    color: var(--mv-text);
}
.step-text { font-size: var(--t-sm); color: var(--mv-text-muted); line-height: 1.45; }
.demo-cta { text-align: center; margin-top: var(--s5); }

/* Nom du projet dans le footer */
.footer-brand {
    display: inline-flex;
    align-items: center;
    gap: var(--s2);
    font-family: var(--mv-font-body);
    font-weight: 800;
    font-size: var(--t-body);
    color: var(--mv-text);
    margin-bottom: var(--s2);
}

/* ── Tagline ── */
.tagline-section {
    text-align: center;
    padding: var(--sec-tight) 0;
    margin-bottom: var(--s2);
}

.tagline-heading {
    font-family: var(--mv-font-body);
    font-size: var(--t-display);
    font-weight: 800;
    letter-spacing: -0.02em;
    line-height: 1.15;
    color: var(--mv-text);
    margin-bottom: var(--s5);
    max-width: 820px;
    margin-left: auto;
    margin-right: auto;
}

.tagline-accent {
    color: var(--mv-accent);
}

.tagline-body {
    font-size: var(--t-body);
    color: var(--mv-text-muted);
    max-width: 620px;
    margin: 0 auto var(--s5);
    line-height: 1.7;
}

.tagline-pills {
    display: flex;
    gap: var(--s2);
    flex-wrap: wrap;
    justify-content: center;
}

.tagline-pill {
    display: inline-flex;
    align-items: center;
    padding: var(--s1) var(--s3);
    border-radius: var(--r1);
    background: var(--mv-bg-raised);
    border: 1px solid var(--mv-border);
    color: var(--mv-text-muted);
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.05em;
    font-weight: 400;
}

/* ── Analyse (/analyze) ──
   UI produit multi-étapes : la structure DOM est pilotée par analyze.js (≈40 ids).
   Seule l'apparence change ici — aucun sélecteur renommé. */

/* Page wrapper */
.az-page {
    max-width: 860px;
    margin: 0 auto;
}

/* Colonne étroite centrée : formulaires d'authentification. */
.az-narrow {
    max-width: 420px;
    margin-inline: auto;
}

/* ── En-tête de page ── */
.az-header {
    margin-bottom: var(--s6);
}

.az-kicker-dot {
    width: 6px;
    height: 6px;
    border-radius: 50%;              /* pastille d'état : reste ronde */
    background: var(--mv-accent);
    animation: pulse-anim 1.4s ease-in-out infinite;
    flex-shrink: 0;
}

.az-kicker-sep { opacity: 0.5; }

.az-header-title {
    font-size: var(--t-h1);
    font-weight: 800;
    letter-spacing: -0.02em;
    margin-bottom: var(--s2);
    color: var(--mv-text);
}

.az-header-sub {
    color: var(--mv-text-muted);
    font-size: var(--t-body);
    margin-bottom: 0;
}

/* ── Zone de dépôt ── */
.az-upload-zone {
    margin-bottom: var(--s5);
}

.az-drop {
    position: relative;
    border: 1px solid var(--mv-border);
    border-radius: var(--r2);
    background-color: var(--mv-bg-sunken);
    min-height: 260px;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: border-color var(--dur-2) var(--ease-out),
                background-color var(--dur-2) var(--ease-out);
    overflow: hidden;
}

/* États pilotés par le JS (drag & drop) — hors media hover. */
.az-drop.border-primary,
.az-drop.is-dragover {
    border-color: var(--mv-accent-line);
    background-color: var(--mv-bg-raised);
}

/* Coins de cadrage (HUD) */
.az-drop-corner {
    position: absolute;
    width: 16px;
    height: 16px;
    pointer-events: none;
    z-index: 2;
    transition: border-color var(--dur-2) var(--ease-out), opacity var(--dur-2) var(--ease-out);
}

.az-drop-corner-tl { top: 12px; left: 12px; border-top: 1px solid var(--mv-border); border-left: 1px solid var(--mv-border); }
.az-drop-corner-tr { top: 12px; right: 12px; border-top: 1px solid var(--mv-border); border-right: 1px solid var(--mv-border); }
.az-drop-corner-bl { bottom: 12px; left: 12px; border-bottom: 1px solid var(--mv-border); border-left: 1px solid var(--mv-border); }
.az-drop-corner-br { bottom: 12px; right: 12px; border-bottom: 1px solid var(--mv-border); border-right: 1px solid var(--mv-border); }

.az-drop.border-primary .az-drop-corner {
    border-color: var(--mv-accent);
}

/* Réticule */
.az-reticle {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 120px;
    height: 120px;
    color: var(--mv-accent);
    pointer-events: none;
    opacity: 0.45;
    z-index: 0;
    transition: opacity var(--dur-3) var(--ease-out);
}

/* Suivi du curseur posé par le JS sur le SVG. */
.az-reticle-svg {
    width: 100%;
    height: 100%;
    transition: transform var(--dur-1) var(--ease-out);
    transform-origin: center;
}

.az-drop.has-preview .az-reticle { display: none; }

/* Contenu de la zone de dépôt */
.az-drop-content {
    position: relative;
    z-index: 1;
    text-align: center;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0;
    padding: var(--s6) var(--s5);
}

.az-drop.has-preview .az-drop-content { display: none; }

.az-drop-icon {
    width: 56px;
    height: 56px;
    border-radius: var(--r2);
    background-color: var(--mv-bg-overlay);
    border: 1px solid var(--mv-border);
    color: var(--mv-accent);
    display: flex;
    align-items: center;
    justify-content: center;
    margin-bottom: var(--s4);
    transition: background-color var(--dur-2) var(--ease-out), border-color var(--dur-2) var(--ease-out);
}

.az-drop.border-primary .az-drop-icon {
    background-color: var(--mv-accent-wash);
    border-color: var(--mv-accent-line);
}

.az-drop-title {
    font-weight: 700;
    font-size: var(--t-body);
    color: var(--mv-text);
    margin-bottom: var(--s1);
}

.az-drop-hint {
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    letter-spacing: 0.06em;
    color: var(--mv-text-muted);
    margin-bottom: var(--s5);
    text-transform: uppercase;
}

.az-select-btn {
    background-color: var(--mv-accent);
    border: 1px solid var(--mv-accent);
    color: var(--mv-accent-ink);
    font-family: var(--mv-font-body);
    font-size: var(--t-sm);
    font-weight: 700;
    letter-spacing: 0;
    padding: var(--s2) var(--s4);
    border-radius: var(--r2);
    transition: background-color var(--dur-1) var(--ease-out), border-color var(--dur-1) var(--ease-out);
}

/* Libellé HUD de la zone de dépôt */
.az-drop-hud-label {
    position: absolute;
    bottom: 14px;
    right: 14px;
    z-index: 3;
    pointer-events: none;
}

.az-drop-hud-text {
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--mv-text-muted);
}

/* Aperçu de l'image dans la zone de dépôt */
.az-drop .drop-zone-preview {
    position: relative;
    z-index: 1;
    max-height: 300px;
    object-fit: contain;
    border-radius: var(--r2);
    width: 100%;
    padding: var(--s3);
}

/* Ligne d'info fichier */
.az-file-info {
    display: flex;
    align-items: center;
    gap: var(--s2);
    margin-top: var(--s3);
    padding: var(--s2) var(--s3);
    background-color: var(--mv-bg-sunken);
    border: 1px solid var(--mv-line);
    border-radius: var(--r2);
    color: var(--mv-text-muted);
    font-size: var(--t-sm);
}

.az-file-name {
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    color: var(--mv-text);
}

.az-clear-btn {
    display: inline-flex;
    align-items: center;
    gap: var(--s1);
    background-color: transparent;
    border: 1px solid var(--mv-line-field);
    color: var(--mv-danger);
    font-size: var(--t-mono);
    font-weight: 700;
    padding: var(--s1) var(--s3);
    border-radius: var(--r2);
    cursor: pointer;
    flex-shrink: 0;
    transition: background-color var(--dur-1) var(--ease-out), border-color var(--dur-1) var(--ease-out);
}

/* Bouton d'analyse */
.az-submit-btn {
    width: 100%;
    margin-top: var(--s3);
    padding: var(--s3);
    background-color: var(--mv-accent);
    border: 1px solid var(--mv-accent);
    border-radius: var(--r2);
    color: var(--mv-accent-ink);
    font-weight: 700;
    font-size: var(--t-body);
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--s2);
    transition: background-color var(--dur-1) var(--ease-out), border-color var(--dur-1) var(--ease-out);
}

/* ── Chargement ── */
.az-loading {
    background-color: var(--mv-bg-raised);
    border: 1px solid var(--mv-line);
    border-radius: var(--r2);
    margin-bottom: var(--s5);
    overflow: hidden;
}

/* Radar */
.az-loading-placeholder {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 100%;
    min-height: 240px;
}

.az-loading-radar {
    position: relative;
    width: 120px;
    height: 120px;
}

.az-radar-ring {
    position: absolute;
    inset: 0;
    border-radius: 50%;              /* forme du radar, pas un arrondi de conteneur */
    border: 1px solid var(--mv-accent);
    animation: radar-pulse 2s ease-out infinite;
}

.az-radar-ring-1 { animation-delay: 0s; }
.az-radar-ring-2 { animation-delay: 0.65s; }
.az-radar-ring-3 { animation-delay: 1.3s; }

@keyframes radar-pulse {
    0% { transform: scale(0.1); opacity: 0.8; }
    100% { transform: scale(1.4); opacity: 0; }
}

/* Balayage : arc 1 px en rotation (plus de conic-gradient). */
.az-radar-sweep {
    position: absolute;
    inset: 0;
    border-radius: 50%;
    border: 1px solid transparent;
    border-top-color: var(--mv-accent);
    animation: radar-spin 2s linear infinite;
}

@keyframes radar-spin {
    from { transform: rotate(0deg); }
    to { transform: rotate(360deg); }
}

/* Étapes du pipeline */
.az-loading-label {
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--mv-text-muted);
    margin-bottom: var(--s4);
}

.az-loading-steps {
    display: flex;
    flex-direction: column;
    gap: var(--s2);
    margin-bottom: var(--s5);
    text-align: left;
}

.az-loading-step {
    display: flex;
    align-items: center;
    gap: var(--s2);
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    color: var(--mv-text-muted);
    transition: color var(--dur-3) var(--ease-out);
}

.az-step-dot {
    width: 7px;
    height: 7px;
    border-radius: 50%;              /* pastille d'état : reste ronde */
    background-color: var(--mv-line-field);
    flex-shrink: 0;
    transition: background-color var(--dur-3) var(--ease-out);
}

.az-loading-step.az-step-active {
    color: var(--mv-accent);
}

.az-loading-step.az-step-active .az-step-dot {
    background-color: var(--mv-accent);
    animation: pulse-anim 1s ease-in-out infinite;
}

.az-loading-step.az-step-done {
    color: var(--mv-success);
}

.az-loading-step.az-step-done .az-step-dot {
    background-color: var(--mv-success);
}

.az-loading-progress {
    height: 2px;
    background-color: rgba(255, 255, 255, 0.06);
    border-radius: var(--r1);
    overflow: hidden;
    width: 160px;
}

/* Barre indéterminée : animée en transform (plus de width/margin-left). */
.az-loading-bar {
    height: 100%;
    width: 40%;
    background-color: var(--mv-accent);
    border-radius: var(--r1);
    transform-origin: left;
    animation: loading-bar 2s var(--ease-in-out) infinite;
}

@keyframes loading-bar {
    0%   { transform: translateX(-100%) scaleX(0.4); }
    50%  { transform: translateX(120%) scaleX(1); }
    100% { transform: translateX(300%) scaleX(0.4); }
}

/* ── Carte d'erreur ── */
.az-error-card {
    display: flex;
    align-items: center;
    gap: var(--s3);
    padding: var(--s3) var(--s4);
    background-color: var(--mv-bg-raised);
    border: 1px solid var(--mv-danger);
    border-radius: var(--r2);
    color: var(--mv-danger);
    margin-bottom: var(--s5);
}

.az-error-card svg { flex-shrink: 0; color: var(--mv-danger); }
.az-error-card > span { flex: 1; font-size: var(--t-sm); color: var(--mv-text); }
.az-error-close { margin-left: auto; flex-shrink: 0; }

/* Bouton « Réessayer » dans la carte d'erreur (relance la même image, sans re-sélection). */
.az-retry-btn {
    flex-shrink: 0;
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    min-height: 36px;
    padding: var(--s2) var(--s3);
    border-radius: var(--r2);
    border: 1px solid var(--mv-danger);
    background-color: transparent;
    color: var(--mv-danger);
    cursor: pointer;
    transition: background-color var(--dur-1) var(--ease-out);
}

/* ── Résultat ── */
.az-result {
    background-color: var(--mv-bg-raised);
    border: 1px solid var(--mv-line);
    border-radius: var(--r2);
    overflow: hidden;
    margin-bottom: var(--s5);
}

.az-result-topbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: var(--s2) var(--s5);
    background-color: var(--mv-bg-sunken);
    border-bottom: 1px solid var(--mv-line);
}

.az-result-topbar-label {
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    font-weight: 700;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--mv-success);
}

.az-result-topbar-right {
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    color: var(--mv-text-muted);
    letter-spacing: 0.06em;
    font-variant-numeric: tabular-nums;
}

/* États de la barre, alignés sur le verdict calibré (pilotés par showResult).
   --mv-success / --mv-signal / --mv-warning sont lus en dur par analyze.js : ne pas les
   remplacer. La teinte est doublée par le libellé texte (VERDICT_ARIA). */
.az-result-topbar-dot { background-color: var(--mv-success); }
.az-result-topbar.topbar-high   { border-bottom-color: var(--mv-success); }
.az-result-topbar.topbar-high   .az-result-topbar-label { color: var(--mv-success); }
.az-result-topbar.topbar-high   .az-result-topbar-dot   { background-color: var(--mv-success); }
.az-result-topbar.topbar-medium { border-bottom-color: var(--mv-signal); }
.az-result-topbar.topbar-medium .az-result-topbar-label { color: var(--mv-signal); }
.az-result-topbar.topbar-medium .az-result-topbar-dot   { background-color: var(--mv-signal); }
.az-result-topbar.topbar-low    { border-bottom-color: var(--mv-warning); }
.az-result-topbar.topbar-low    .az-result-topbar-label { color: var(--mv-warning); }
.az-result-topbar.topbar-low    .az-result-topbar-dot   { background-color: var(--mv-warning); }

/* Bloc d'identification (au-dessus des jauges) */
.az-result-id {
    text-align: center;
    margin-bottom: var(--s5);
}

.az-result-family-label {
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--mv-text-muted);
    margin-bottom: var(--s1);
}

.az-result-vehicle-name {
    font-family: var(--mv-font-body);
    font-size: var(--t-h1);
    font-weight: 800;
    letter-spacing: -0.02em;
    color: var(--mv-text);
    line-height: 1.1;
}

/* Abstention (verdict low) : sous-type non confirmé, dégradé visuellement. */
.az-result-vehicle-name.az-abstained {
    font-style: italic;
    font-weight: 600;
    color: var(--mv-text-muted);
}

/* Abstention groupe (family_confidence trop basse) : famille non identifiée, signalée en
   couleur d'alerte sur le libellé famille (#result-family porte az-result-family-label). */
.az-result-family-label.az-abstained {
    color: var(--mv-warning);
    font-style: italic;
}

.az-top3-wrap { flex: 1; }

.az-disclaimer {
    font-family: var(--mv-font-body);
    font-size: var(--t-meta);
    color: var(--mv-text-muted);
    letter-spacing: 0;
}

/* ── Actions de la zone de dépôt (fichier + caméra) ── */
.az-drop-actions {
    display: flex;
    gap: var(--s2);
    flex-wrap: wrap;
    justify-content: center;
    align-items: center;
}

.az-camera-btn {
    display: inline-flex;
    align-items: center;
    gap: var(--s2);
    background-color: transparent;
    border: 1px solid var(--mv-border);
    color: var(--mv-text-muted);
    font-family: var(--mv-font-body);
    font-size: var(--t-sm);
    font-weight: 700;
    letter-spacing: 0;
    padding: var(--s2) var(--s4);
    border-radius: var(--r2);
    transition: background-color var(--dur-1) var(--ease-out),
                border-color var(--dur-1) var(--ease-out),
                color var(--dur-1) var(--ease-out);
}

/* ── Bandeau de verdict (confiance calibrée) ── */
.az-verdict {
    display: flex;
    align-items: center;
    gap: var(--s3);
    padding: var(--s3) var(--s5);
    background-color: var(--mv-bg-sunken);
    border-bottom: 1px solid var(--mv-border);
}

.az-verdict-icon {
    flex-shrink: 0;
    width: 38px;
    height: 38px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--r2);
    background-color: var(--mv-bg-overlay);
    border: 1px solid var(--mv-line);
}

.az-verdict-label {
    font-family: var(--mv-font-body);
    font-weight: 700;
    font-size: var(--t-body);
    letter-spacing: -0.01em;
    line-height: 1.15;
}

.az-verdict-hint {
    font-size: var(--t-sm);
    /* L'explication d'abstention est une info utile, pas un détail discret : on reste sur
       --mv-text-muted (contraste vérifié), jamais plus faible. */
    color: var(--mv-text-muted);
    margin-top: 1px;
}

.az-verdict-high { border-bottom-color: var(--mv-success); }
.az-verdict-high .az-verdict-icon { border-color: var(--mv-success); color: var(--mv-success); }
.az-verdict-high .az-verdict-label { color: var(--mv-success); }

.az-verdict-medium { border-bottom-color: var(--mv-signal); }
.az-verdict-medium .az-verdict-icon { border-color: var(--mv-signal); color: var(--mv-signal); }
.az-verdict-medium .az-verdict-label { color: var(--mv-signal); }

/* « low » = prudence/abstention (pas erreur) → ambre, aligné sur topbar-low/jauge/toast.
   Le rouge --mv-danger reste réservé aux vraies erreurs (error-zone). */
.az-verdict-low { border-bottom-color: var(--mv-warning); }
.az-verdict-low .az-verdict-icon { border-color: var(--mv-warning); color: var(--mv-warning); }
.az-verdict-low .az-verdict-label { color: var(--mv-warning); }

/* ── Nom du véhicule → lien encyclopédie ── */
.az-result-vehicle-link {
    display: inline-flex;
    align-items: center;
    gap: var(--s2);
    text-decoration: none;
    color: inherit;
}

.az-result-vehicle-arrow {
    color: var(--mv-text-muted);
    opacity: 0;
    transform: translateX(-3px);
    transition: opacity var(--dur-1) var(--ease-out),
                transform var(--dur-1) var(--ease-out),
                color var(--dur-1) var(--ease-out);
}

/* ── Fiabilité par classe ── */
.az-reliability {
    background-color: var(--mv-bg-sunken);
    border: 1px solid var(--mv-line);
    border-radius: var(--r2);
    padding: var(--s3);
    margin-bottom: var(--s2);
}

.az-reliability-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--s2);
    margin-bottom: var(--s2);
}

.az-reliability-title {
    font-family: var(--mv-font-body);
    font-size: var(--t-meta);
    letter-spacing: 0;
    color: var(--mv-text-muted);
}

.az-reliability-title span { color: var(--mv-text); }

.az-reliability-pct {
    font-family: var(--mv-font-mono);
    font-weight: 700;
    font-size: var(--t-h3);
    line-height: 1;
    color: var(--mv-text);
    font-variant-numeric: tabular-nums;
}

.az-reliability-track {
    height: 6px;
    background-color: rgba(255, 255, 255, 0.06);
    border-radius: var(--r1);
    overflow: hidden;
}

/* Exception assumée à la règle « transform/opacity uniquement » : la largeur est
   posée en inline par analyze.js:395-396 (`bar.style.width = pct + "%"`, précédé
   d'un 0% et d'un setTimeout de 600 ms qui attend une transition sur width).
   Passer en scaleX() imposerait de réécrire ce contrat JS pour animer UNE barre
   affichée une fois par analyse — le coût de compositing est nul à cette échelle. */
.az-reliability-bar {
    height: 100%;
    width: 0;
    border-radius: var(--r1);
    background-color: var(--mv-accent);
    transition: width var(--dur-4) var(--ease-out);
}

.az-reliability-note {
    font-family: var(--mv-font-body);
    font-size: var(--t-meta);
    color: var(--mv-text-muted);
    margin-top: var(--s2);
    font-variant-numeric: tabular-nums;
}

/* Paliers de fiabilité : jamais une teinte différente (l'info est dans le chiffre et la
   note), seulement une opacité décroissante. */
.az-reliability.rel-high .az-reliability-pct { color: var(--mv-text); opacity: 1; }
.az-reliability.rel-high .az-reliability-bar { background-color: var(--mv-accent); opacity: 1; }
.az-reliability.rel-medium .az-reliability-pct { color: var(--mv-text); opacity: 0.85; }
.az-reliability.rel-medium .az-reliability-bar { background-color: var(--mv-accent); opacity: 0.75; }
.az-reliability.rel-low .az-reliability-pct { color: var(--mv-text); opacity: 0.7; }
.az-reliability.rel-low .az-reliability-bar { background-color: var(--mv-accent); opacity: 0.5; }

/* ── Résultat précédent ── */
/* Distingué par un fond enfoncé + un filet discret, pas par un voile d'opacité. */
.az-prev-result {
    margin-bottom: var(--s4);
    opacity: 0.8;
    transition: opacity var(--dur-2) var(--ease-out);
}

.az-prev-inner {
    display: flex;
    align-items: center;
    gap: var(--s3);
    padding: var(--s2) var(--s4);
    background-color: var(--mv-bg-sunken);
    border: 1px solid var(--mv-line);
    border-radius: var(--r2);
    flex-wrap: wrap;
}

.az-prev-label {
    display: flex;
    align-items: center;
    gap: var(--s1);
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--mv-text-muted);
    flex-shrink: 0;
}

.az-prev-content { flex: 1; font-size: var(--t-sm); }
.az-prev-badges { display: flex; gap: var(--s1); align-items: center; }

.az-prev-version {
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    color: var(--mv-text-muted);
    font-variant-numeric: tabular-nums;
}

/* ── Survols (pointeur fin uniquement) ── */
@media (hover: hover) and (pointer: fine) {
    .az-drop:hover {
        border-color: var(--mv-accent-line);
        background-color: var(--mv-bg-raised);
    }

    .az-drop:hover .az-drop-corner {
        border-color: var(--mv-accent);
    }

    .az-drop:hover .az-drop-icon {
        background-color: var(--mv-accent-wash);
        border-color: var(--mv-accent-line);
    }

    .az-select-btn:hover {
        background-color: #FFB224;
        border-color: #FFB224;
        color: var(--mv-accent-ink);
    }

    .az-clear-btn:hover {
        background-color: rgba(255, 255, 255, 0.06);
        border-color: var(--mv-danger);
        color: var(--mv-danger);
    }

    .az-submit-btn:hover {
        background-color: #FFB224;
        border-color: #FFB224;
        color: var(--mv-accent-ink);
    }

    .az-retry-btn:hover { background-color: rgba(255, 255, 255, 0.06); }

    .az-camera-btn:hover {
        background-color: rgba(255, 255, 255, 0.06);
        border-color: var(--mv-accent-line);
        color: var(--mv-text);
    }

    .az-result-vehicle-link:hover .az-result-vehicle-name { color: var(--mv-accent); }
    .az-result-vehicle-link:hover .az-result-vehicle-arrow { opacity: 1; transform: none; color: var(--mv-accent); }

    .az-prev-result:hover { opacity: 1; }
}

/* ── Mouvement réduit ── */
@media (prefers-reduced-motion: reduce) {
    .az-kicker-dot,
    .az-radar-ring,
    .az-radar-sweep,
    .az-loading-bar,
    .az-loading-step.az-step-active .az-step-dot {
        animation: none;
    }

    .az-radar-ring { opacity: 0.5; transform: none; }
    .az-loading-bar { width: 100%; transform: none; }
    .az-reticle-svg,
    .az-reliability-bar,
    .az-result-vehicle-arrow {
        transition: none;
    }
}

/* ── Encyclopédie (véhicules) ── */
.veh-nav {
    display: flex;
    gap: var(--s2);
    margin-bottom: var(--s4);
    flex-wrap: wrap;
}

/* Note de pont 3-familles ↔ 2-groupes (cohérence vocabulaire avec /analyze). */
.veh-group-bridge {
    max-width: 760px;
    margin: 0 0 var(--s6);
    font-size: var(--t-sm);
    line-height: 1.55;
    color: var(--mv-text-muted);
}

.veh-group-bridge strong { color: var(--mv-text); }

/* Ancre de navigation : porte un état actif, d'où la classe dédiée. */
.veh-nav-btn {
    display: inline-flex;
    align-items: center;
    gap: var(--s2);
    padding: var(--s2) var(--s4);
    border: 1px solid var(--mv-border);
    border-radius: var(--r1);
    background-color: var(--mv-bg-raised);
    color: var(--mv-text-muted);
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    text-decoration: none;
    transition: background-color var(--dur-1) var(--ease-out),
                border-color var(--dur-1) var(--ease-out),
                color var(--dur-1) var(--ease-out);
}

/* Pastille d'état : reste ronde. */
.veh-nav-dot {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    flex-shrink: 0;
    background-color: currentColor;
}

.veh-nav-count {
    padding: 1px var(--s2);
    border-radius: var(--r1);
    background-color: rgba(255, 255, 255, 0.06);
    color: var(--mv-text-muted);
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    font-variant-numeric: tabular-nums;
}

@media (hover: hover) and (pointer: fine) {
    /* Une seule teinte : la famille est déjà nommée par le libellé, la couleur ne
       porte aucune information à elle seule. */
    .veh-nav-chars:hover,
    .veh-nav-ifv:hover,
    .veh-nav-apc:hover {
        border-color: var(--mv-accent-line);
        background-color: var(--mv-accent-wash);
        color: var(--mv-accent);
    }
}

.veh-section-head {
    display: flex;
    align-items: center;
    gap: var(--s3);
    margin-bottom: var(--s4);
    padding: var(--s3) var(--s4);
    border: 1px solid var(--mv-border);
    border-left-width: 3px;
    border-radius: var(--r1);
    background-color: var(--mv-bg-raised);
}

/* Le filet de gauche marque la section, pas la famille (libellé = information). */
.veh-section-chars,
.veh-section-ifv,
.veh-section-apc { border-left-color: var(--mv-accent); }

.veh-section-info { flex: 1; }

.veh-section-title {
    font-size: var(--t-body);
    font-weight: 800;
    letter-spacing: -0.02em;
    margin-bottom: var(--s1);
}

.veh-accuracy-badge {
    flex-shrink: 0;
    padding: var(--s1) var(--s3);
    border: 1px solid transparent;
    border-radius: var(--r1);
    font-family: var(--mv-font-mono);
    font-size: var(--t-num);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

.veh-accuracy-chars,
.veh-accuracy-ifv,
.veh-accuracy-apc {
    background-color: var(--mv-accent-wash);
    border-color: var(--mv-accent-line);
    color: var(--mv-accent);
}

/* ── Historique ── */
.hz-filter {
    display: flex;
    align-items: center;
    gap: var(--s3);
    margin-bottom: var(--s5);
    padding: var(--s3) var(--s4);
    background-color: var(--mv-bg-raised);
    border: 1px solid var(--mv-border);
    border-radius: var(--r1);
    flex-wrap: wrap;
}

.hz-filter-form {
    display: flex;
    align-items: center;
    gap: var(--s2);
    flex: 1;
    flex-wrap: wrap;
}

.hz-select {
    min-width: 130px;
    flex: 1;
    padding: var(--s2) var(--s3);
    background-color: var(--mv-bg-sunken);
    border: 1px solid var(--mv-line-field);
    border-radius: var(--r1);
    color: var(--mv-text);
    font-family: var(--mv-font-body);
    font-size: var(--t-sm);
    transition: border-color var(--dur-1) var(--ease-out);
}

.hz-select.hz-select-sm { min-width: 100px; }

.hz-select:focus {
    border-color: var(--mv-accent);
    outline: 1px solid var(--mv-accent-line);
    outline-offset: 1px;
}

.hz-filter-btn {
    flex-shrink: 0;
    padding: var(--s2) var(--s5);
    background-color: var(--mv-accent-wash);
    border: 1px solid var(--mv-accent-line);
    border-radius: var(--r1);
    color: var(--mv-accent);
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    white-space: nowrap;
    cursor: pointer;
    transition: background-color var(--dur-1) var(--ease-out),
                border-color var(--dur-1) var(--ease-out),
                color var(--dur-1) var(--ease-out);
}

@media (hover: hover) and (pointer: fine) {
    .hz-filter-btn:hover {
        border-color: var(--mv-accent);
        color: #FFB224;
    }
}

.hz-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: var(--s8) var(--s6);
    text-align: center;
    background-color: var(--mv-bg-raised);
    border: 1px solid var(--mv-line);
    border-radius: var(--r2);
}

.hz-empty-icon {
    color: var(--mv-text-faint);
    margin-bottom: var(--s4);
}

.hz-empty-title {
    font-family: var(--mv-font-body);
    font-size: var(--t-h3);
    font-weight: 800;
    letter-spacing: -0.02em;
    margin-bottom: var(--s2);
}

.hz-empty-sub {
    font-size: var(--t-sm);
    color: var(--mv-text-muted);
    margin-bottom: 0;
}

.hz-pagination .page-link {
    background-color: var(--mv-bg-raised);
    border: 1px solid var(--mv-border);
    border-radius: var(--r1);
    color: var(--mv-text-muted);
    font-family: var(--mv-font-mono);
    font-size: var(--t-meta);
    font-variant-numeric: tabular-nums;
    transition: background-color var(--dur-1) var(--ease-out),
                border-color var(--dur-1) var(--ease-out),
                color var(--dur-1) var(--ease-out);
}

@media (hover: hover) and (pointer: fine) {
    .hz-pagination .page-link:hover {
        background-color: var(--mv-accent-wash);
        border-color: var(--mv-accent-line);
        color: var(--mv-accent);
    }
}

/* État désactivé : l'estompe est ici porteuse de sens. */
.hz-pagination .page-item.disabled .page-link { opacity: 0.4; }

.hz-pagination-info { color: var(--mv-text-muted); }

@media (max-width: 767px) {
    .hz-filter { gap: var(--s2); padding: var(--s3); }
    .hz-filter-kicker { display: none; }
    .hz-select { min-width: 0; font-size: var(--t-meta); }
}

/* ── Contribution ── */
.az-contrib-selector {
    margin-bottom: var(--s5);
}

.az-contrib-selector-label {
    display: flex;
    align-items: center;
    gap: var(--s2);
    margin-bottom: var(--s2);
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--mv-text-muted);
}

.az-contrib-select {
    padding: var(--s3) var(--s4);
    background-color: var(--mv-bg-sunken);
    border: 1px solid var(--mv-line-field);
    border-radius: var(--r1);
    color: var(--mv-text);
    font-family: var(--mv-font-body);
    font-size: var(--t-body);
    transition: border-color var(--dur-1) var(--ease-out);
}

.az-contrib-select:focus {
    border-color: var(--mv-accent);
    outline: 1px solid var(--mv-accent-line);
    outline-offset: 1px;
}

.az-contrib-selector-hint {
    font-size: var(--t-meta);
    color: var(--mv-text-muted);
    margin-top: var(--s2);
    margin-bottom: 0;
}

.az-contrib-queue {
    display: flex;
    flex-direction: column;
    gap: var(--s2);
}

.az-contrib-file-card {
    display: flex;
    align-items: center;
    gap: var(--s3);
    padding: var(--s3) var(--s4);
    background-color: var(--mv-bg-raised);
    border: 1px solid var(--mv-line);
    border-radius: var(--r1);
    transition: border-color var(--dur-1) var(--ease-out);
    animation: reveal-up var(--dur-4) var(--ease-out) both;
}

@media (hover: hover) and (pointer: fine) {
    .az-contrib-file-card:hover { border-color: var(--mv-accent-line); }
}

.az-contrib-file-thumb {
    width: 52px;
    height: 52px;
    object-fit: cover;
    border: 1px solid var(--mv-border);
    border-radius: var(--r2);
    flex-shrink: 0;
}

.az-contrib-file-info {
    flex: 1;
    min-width: 0;
}

.az-contrib-file-name {
    font-size: var(--t-sm);
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    color: var(--mv-text);
}

.az-contrib-file-size {
    margin-top: 2px;
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    font-variant-numeric: tabular-nums;
    color: var(--mv-text-muted);
}

.az-contrib-status {
    flex-shrink: 0;
    padding: 3px var(--s2);
    border: 1px solid transparent;
    border-radius: var(--r1);
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

.az-contrib-status.pending {
    background-color: rgba(255, 255, 255, 0.04);
    border-color: var(--mv-line);
    color: var(--mv-text-muted);
}

.az-contrib-status.uploading {
    background-color: var(--mv-accent-wash);
    border-color: var(--mv-accent-line);
    color: var(--mv-accent);
}

.az-contrib-status.done {
    background-color: color-mix(in srgb, var(--mv-success) 12%, transparent);
    border-color: color-mix(in srgb, var(--mv-success) 40%, transparent);
    color: var(--mv-success);
}

.az-contrib-status.duplicate {
    background-color: color-mix(in srgb, var(--mv-warning) 12%, transparent);
    border-color: color-mix(in srgb, var(--mv-warning) 35%, transparent);
    color: var(--mv-warning);
}

.az-contrib-status.error {
    background-color: color-mix(in srgb, var(--mv-danger) 12%, transparent);
    border-color: color-mix(in srgb, var(--mv-danger) 40%, transparent);
    color: var(--mv-danger);
}

.az-contrib-remove {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    padding: var(--s1) var(--s2);
    background-color: transparent;
    border: 1px solid var(--mv-line);
    border-radius: var(--r2);
    color: var(--mv-text-muted);
    line-height: 1;
    cursor: pointer;
    transition: background-color var(--dur-1) var(--ease-out),
                border-color var(--dur-1) var(--ease-out),
                color var(--dur-1) var(--ease-out);
}

@media (hover: hover) and (pointer: fine) {
    .az-contrib-remove:hover {
        background-color: color-mix(in srgb, var(--mv-danger) 8%, transparent);
        border-color: var(--mv-danger);
        color: var(--mv-danger);
    }
}

/* ── Responsive ── */
@media (max-width: 767px) {
    .az-header-title { font-size: var(--t-h2); }
    .az-drop { min-height: 200px; }
    .az-result-vehicle-name { font-size: var(--t-h2); }
    .az-contrib-file-name { max-width: 140px; }
}

/* ── Sections de données — home (conflit · recall · encyclopédie · avant/après · live) ── */

/* ═══ Bilan du conflit (page d'accueil — pertes russes Oryx) ═══ */
.conflict-section { margin: 0; }

/* ── 3 compteurs principaux ── */
.conflict-counters {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: var(--s4);
    margin-bottom: var(--s4);
}

.conflict-counter {
    background: var(--mv-bg-raised);
    border: 1px solid var(--mv-line);
    border-radius: var(--r2);
    padding: var(--s5) var(--s4);
    text-align: center;
    transition: border-color var(--dur-1) var(--ease-out);
}

@media (hover: hover) and (pointer: fine) {
    .conflict-counter:hover { border-color: var(--mv-accent-line); }
}

/* le compteur d'entrée (pertes totales) se signale par un filet, pas par un halo */
.conflict-counter.is-primary { border-color: var(--mv-danger); }

.conflict-counter-value {
    font-family: var(--mv-font-body);
    font-weight: 800;
    font-size: clamp(1.9rem, 4.4vw, 2.9rem);
    line-height: 1.05;
    letter-spacing: -0.02em;
    color: var(--mv-accent);
    font-variant-numeric: tabular-nums;
}

.conflict-counter.is-primary .conflict-counter-value { color: var(--mv-danger); }

.conflict-counter-label {
    margin-top: var(--s2);
    font-weight: 700;
    font-size: var(--t-sm);
    color: var(--mv-text);
}

.conflict-counter-sub {
    margin-top: var(--s1);
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    color: var(--mv-text-muted);
    letter-spacing: 0.02em;
    font-variant-numeric: tabular-nums;
}

.conflict-counter.is-signal .conflict-counter-value { color: var(--mv-signal); }

/* ── Rappel / breakdown par catégorie ── */
.conflict-breakdown {
    background: var(--mv-bg-raised);
    border: 1px solid var(--mv-line);
    border-radius: var(--r2);
    padding: clamp(1rem, 2.4vw, 1.4rem);
}

.conflict-breakdown-head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--s2);
    margin-bottom: var(--s4);
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--mv-text-muted);
}

.conflict-bd-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
    gap: var(--s3);
}

.conflict-bd-card {
    background: var(--mv-bg-sunken);
    border: 1px solid var(--mv-line);
    border-radius: var(--r1);
    padding: var(--s3);
    text-align: center;
}

/* catégorie couverte par l'app : accent (l'unique) + libellé texte */
.conflict-bd-card.is-app {
    border-color: var(--mv-accent-line);
    background: var(--mv-accent-wash);
}

.conflict-bd-value {
    font-family: var(--mv-font-body);
    font-weight: 800;
    font-size: clamp(1.25rem, 2.4vw, 1.6rem);
    letter-spacing: -0.02em;
    color: var(--mv-text);
    font-variant-numeric: tabular-nums;
}

.conflict-bd-card.is-app .conflict-bd-value { color: var(--mv-accent); }

.conflict-bd-name {
    margin-top: var(--s1);
    font-size: var(--t-sm);
    font-weight: 600;
    color: var(--mv-text);
}

.conflict-bd-sub {
    margin-top: var(--s1);
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    color: var(--mv-text-muted);
    font-variant-numeric: tabular-nums;
}

.conflict-foot {
    margin: var(--s4) 0 0;
    text-align: center;
    font-size: var(--t-meta);
    color: var(--mv-text-muted);
    line-height: 1.5;
}

@media (max-width: 767px) {
    .conflict-counters { grid-template-columns: 1fr; gap: var(--s3); }
}

/* ═══ Précision par classe (home — recall honnête) ═══ */
.recall-section { margin: 0; }

.recall-grid {
    display: grid;
    grid-template-columns: 2fr 2fr 1fr;
    gap: var(--s4);
    align-items: start;
}

.recall-family {
    background: var(--mv-bg-raised);
    border: 1px solid var(--mv-line);
    border-radius: var(--r2);
    padding: var(--s4);
}

.recall-family-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--s2);
    margin-bottom: var(--s3);
    padding-bottom: var(--s3);
    border-bottom: 1px solid var(--mv-line);
}

.recall-family-name {
    font-family: var(--mv-font-body);
    font-weight: 800;
    font-size: var(--t-h3);
    letter-spacing: -0.02em;
}

/* les trois familles partagent la même teinte : le nom de la famille est écrit en toutes lettres */
.recall-fam-chars,
.recall-fam-ifv,
.recall-fam-apc { color: var(--mv-text-muted); }

.recall-row {
    display: grid;
    grid-template-columns: 104px 1fr 92px;
    align-items: center;
    gap: var(--s3);
    padding: var(--s1) 0;
}

/* paliers de performance : une seule couleur, l'échelon passe en opacité (jamais en teinte) */
.recall-high { --c: var(--mv-accent); --o: 1; }
.recall-good { --c: var(--mv-accent); --o: 0.85; }
.recall-mid  { --c: var(--mv-accent); --o: 0.7; }
.recall-low  { --c: var(--mv-accent); --o: 0.55; }

.recall-name { font-size: var(--t-sm); font-weight: 600; color: var(--mv-text); white-space: nowrap; }

.recall-n {
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    color: var(--mv-text-muted);
    font-variant-numeric: tabular-nums;
}

.recall-bar {
    height: 7px;
    background: var(--mv-bg-sunken);
    border: 1px solid var(--mv-line);
    border-radius: var(--r1);
    overflow: hidden;
}

/* Même exception que .az-reliability-bar : --w est un POURCENTAGE posé inline par
   index.html:312 (`style="--w: {{ c.recall }}%"`). scaleX() n'accepte les
   pourcentages que depuis CSS Transforms L2 ; width les accepte partout, et
   douze barres courtes révélées une seule fois ne coûtent rien à animer. */
.recall-bar-fill {
    display: block;
    height: 100%;
    width: 0;
    border-radius: var(--r1);
    background: var(--c);
    opacity: var(--o, 1);
    transition: width var(--dur-4) var(--ease-out);
}

.recall-section.in-view .recall-bar-fill { width: var(--w, 0); }

.recall-val {
    font-family: var(--mv-font-mono);
    font-size: var(--t-num);
    font-weight: 700;
    color: var(--c);
    opacity: var(--o, 1);
    text-align: right;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* Le chiffre brut passe sous le chiffre honnête plutôt qu'à côté : deux
   mesures différentes ne se lisent pas sur la même ligne. */
.recall-val .recall-n { display: block; margin-top: 2px; }

@media (max-width: 767px) {
    .recall-grid { grid-template-columns: 1fr; }
}

/* ═══ Aperçu encyclopédie (home — 12 blindés) ═══ */
.enc-section { margin: 0; }

.enc-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    gap: var(--s3);
}

.enc-card {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: var(--s1);
    background: var(--mv-bg-raised);
    border: 1px solid var(--mv-line);
    border-radius: var(--r2);
    padding: var(--s3);
    text-decoration: none;
    transition: border-color var(--dur-1) var(--ease-out);
}

@media (hover: hover) and (pointer: fine) {
    .enc-card:hover { border-color: var(--mv-accent-line); }
}

/* --fc conservé pour compatibilité template : une seule teinte pour les 3 familles */
.enc-fam-chars,
.enc-fam-ifv,
.enc-fam-apc { --fc: var(--mv-accent); }

.enc-card-name {
    font-family: var(--mv-font-body);
    font-weight: 800;
    font-size: var(--t-h3);
    color: var(--mv-text);
    letter-spacing: -0.02em;
}

.enc-card-trait { font-family: var(--mv-font-mono); font-size: var(--t-mono); color: var(--mv-text-muted); }

.enc-card-fam {
    position: absolute;
    top: var(--s2);
    right: var(--s3);
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--mv-text-faint);
}

.enc-cta { text-align: center; margin-top: var(--s5); }

/* ═══ Avant/après : signal drone brut → identifié (home) ═══ */
.ba-section { margin: 0; }

.ba-tabs { display: flex; justify-content: center; gap: var(--s2); margin: 0 0 var(--s4); }

.ba-tab {
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--mv-text-muted);
    background: var(--mv-bg-raised);
    border: 1px solid var(--mv-line);
    border-radius: var(--r1);
    padding: var(--s2) var(--s3);
    cursor: pointer;
    transition: color var(--dur-1) var(--ease-out),
                border-color var(--dur-1) var(--ease-out),
                background-color var(--dur-1) var(--ease-out);
}

@media (hover: hover) and (pointer: fine) {
    .ba-tab:hover { color: var(--mv-text); border-color: var(--mv-accent-line); }
}

.ba-tab.is-active {
    color: var(--mv-accent);
    border-color: var(--mv-accent-line);
    background: var(--mv-accent-wash);
}

.ba-stage { max-width: 860px; margin: 0 auto; }

/* La visibilité est désormais portée par .ba-panel : le stage accueille aussi
   un panneau vidéo, qui n'est pas un slider. */
.ba-panel { display: none; }
.ba-panel.is-active { display: block; }

.ba-caption {
    margin: var(--s3) 0 0;
    font-size: var(--t-meta);
    color: var(--mv-text-muted);
    max-width: 62ch;
}

.ba-slider {
    position: relative;
    width: 100%;
    aspect-ratio: 16 / 9;
    border: 1px solid var(--mv-border);
    border-radius: var(--r2);
    overflow: hidden;
    user-select: none;
    touch-action: pan-y;
}
.ba-pane { position: absolute; inset: 0; }
.ba-pane img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* gauche = signal brut : désaturé, flou, assombri (aucune trame décorative) */
.ba-before img { filter: saturate(0.45) contrast(1.12) brightness(0.82) blur(1.4px); }
.ba-before::after {
    content: '';
    position: absolute;
    inset: 0;
    pointer-events: none;
    background: var(--mv-bg-sunken);
    opacity: 0.35;
}

/* droite = identifié (révélé par la poignée) */
.ba-after { clip-path: inset(0 calc(100% - var(--pos, 50%)) 0 0); }

.ba-tag {
    position: absolute;
    bottom: var(--s3);
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    padding: 2px 6px;
    border-radius: var(--r1);
}
.ba-tag-raw { right: var(--s3); color: var(--mv-text-muted); background: var(--mv-bg-overlay); border: 1px solid var(--mv-line); }
.ba-tag-id  { left: var(--s3); color: var(--mv-success); background: var(--mv-bg-overlay); border: 1px solid var(--mv-success); }

.ba-box {
    position: absolute;
    border: 2px solid var(--mv-accent);
    border-radius: var(--r1);
}
/* toutes les boîtes de détection partagent l'accent : la classe est lue dans le libellé */
.ba-box-apc { border-color: var(--mv-accent); }
.ba-box-label {
    position: absolute;
    top: -1.7rem;
    left: -2px;
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    font-weight: 700;
    background: var(--mv-accent);
    color: var(--mv-accent-ink);
    padding: 2px 6px;
    border-radius: var(--r1);
    white-space: nowrap;
}
.ba-box-apc .ba-box-label { background: var(--mv-accent); color: var(--mv-accent-ink); }
.ba-box-ifv { border-color: var(--mv-accent); }
.ba-box-ifv .ba-box-label { background: var(--mv-accent); color: var(--mv-accent-ink); }

.ba-divider {
    position: absolute;
    top: 0;
    bottom: 0;
    left: var(--pos, 50%);
    width: 2px;
    background: var(--mv-accent);
    transform: translateX(-1px);
    pointer-events: none;
}
/* poignée dessinée en CSS (l'ancien glyphe '⟺' dépendait du rendu de police) */
.ba-divider::after {
    content: '';
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 14px;
    height: 14px;
    background: var(--mv-accent-ink);
    border: 2px solid var(--mv-accent);
    border-radius: var(--r1);
}
.ba-range {
    position: absolute; inset: 0; width: 100%; height: 100%; margin: 0;
    opacity: 0; cursor: ew-resize; -webkit-appearance: none; appearance: none; background: transparent;
}
.ba-range::-webkit-slider-thumb { -webkit-appearance: none; width: 40px; height: 100%; cursor: ew-resize; }
.ba-range::-moz-range-thumb { width: 40px; height: 100%; border: 0; background: transparent; cursor: ew-resize; }
/* Focus clavier visible (WCAG 2.4.7) : le range est en opacity:0, on révèle le contour
   sur le conteneur + on renforce la poignée quand le slider a le focus. */
.ba-range:focus-visible { outline: none; }
.ba-slider:has(.ba-range:focus-visible) { outline: 3px solid var(--mv-accent); outline-offset: 3px; }
.ba-range:focus-visible ~ .ba-divider { background: #FFB224; }

/* ─────────────────────────────────────────────────────────────
   Section « Localisation temps réel » — vidéo HUD après l'avant/après.
   Réutilise l'en-tête de section commune ; cadre = un simple filet
   1 px, la vidéo porte déjà tout son HUD.
   ───────────────────────────────────────────────────────────── */
.lf-section { margin-top: 0; }

.lf-frame {
    position: relative;
    width: 100%;
    max-width: 860px;
    margin: var(--s5) auto 0;
    aspect-ratio: 16 / 9;
    border-radius: var(--r2);
    overflow: hidden;
    background: var(--mv-bg-sunken);
    border: 1px solid var(--mv-border);
}

.lf-video {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

/* filet intérieur : recadre la vidéo au pixel sans vignette ni ombre */
.lf-frame::after {
    content: "";
    position: absolute;
    inset: 0;
    pointer-events: none;
    border: 1px solid var(--mv-line);
    border-radius: var(--r2);
}


/* ============================================================================
   Coquille du layout — navigation, menus, alertes, toasts
   ----------------------------------------------------------------------------
   Écrit à la main pour remplacer les quatre composants Bootstrap que le site
   utilisait vraiment. Bootstrap n'est plus chargé sur les pages publiques.
   ========================================================================== */

/* Lien d'évitement : visible seulement au clavier, première tabulation. */
.skip-link {
    position: absolute;
    left: var(--s4);
    top: -3rem;
    z-index: 100;
    padding: var(--s2) var(--s4);
    background: var(--mv-accent);
    color: var(--mv-accent-ink);
    border-radius: var(--r2);
    font-size: var(--t-sm);
    font-weight: 700;
    transition: top var(--dur-2) var(--ease-out);
}
.skip-link:focus-visible { top: var(--s3); text-decoration: none; }

.navbar-inner {
    display: flex;
    align-items: center;
    gap: var(--s4);
    position: relative;
}

.navbar-brand { margin-right: auto; }

.navbar-nav {
    display: flex;
    align-items: center;
    gap: var(--s1);
    list-style: none;
    margin: 0;
    padding: 0;
}

.nav-panel { display: flex; align-items: center; gap: var(--s3); }

/* Menu déroulant utilisateur --------------------------------------------- */
.mv-dd { position: relative; }

.mv-dd-trigger {
    display: inline-flex;
    align-items: center;
    gap: var(--s2);
    padding: var(--s1) var(--s2);
    background: transparent;
    border: 0;
    border-radius: var(--r2);
    color: var(--mv-text-muted);
    transition: background-color var(--dur-1) var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
    .mv-dd-trigger:hover { background: rgba(255, 255, 255, 0.06); }
}

/* Calque flottant : il occulte réellement le contenu, donc il a droit à
   l'unique ombre du système. */
.mv-dd-menu {
    position: absolute;
    right: 0;
    top: calc(100% + 6px);
    z-index: 60;
    display: flex;
    flex-direction: column;
}
.mv-dd-menu[hidden] { display: none; }

/* Burger — sous 900 px seulement -------------------------------------------- */
.navbar-toggler { display: none; }

@media (max-width: 899.98px) {
    .navbar-toggler { display: inline-flex; align-items: center; }

    .nav-panel {
        position: absolute;
        inset: calc(100% + var(--s3)) 0 auto;
        flex-direction: column;
        align-items: stretch;
        gap: var(--s3);
        padding: var(--s4);
        background: var(--mv-bg-raised);
        border: 1px solid var(--mv-border);
        border-radius: var(--r2);
        box-shadow: var(--shadow-overlay);
        opacity: 0;
        transform: translateY(-6px);
        transition: opacity var(--dur-3) var(--ease-out),
                    transform var(--dur-3) var(--ease-out);
    }
    .nav-panel.is-open { opacity: 1; transform: none; }
    .nav-panel[hidden] { display: none; }

    .navbar-nav { flex-direction: column; align-items: stretch; gap: var(--s1); }
    .nav-link { display: block; }

    /* Sur mobile le menu est déjà déplié : le déroulant redevient une liste. */
    .mv-dd-menu {
        position: static;
        box-shadow: none;
        border: 0;
        border-top: 1px solid var(--mv-line);
        border-radius: 0;
        padding: var(--s2) 0 0;
    }
}

/* Au-dessus de 900 px, l'attribut `hidden` du panneau est sans effet : il ne
   sert qu'au burger mobile. */
@media (min-width: 900px) {
    .nav-panel[hidden] { display: flex; }
}

/* Pied de page --------------------------------------------------------------- */
.footer-brand {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--s2);
}

/* Toasts ---------------------------------------------------------------------- */
#toast-container {
    position: fixed;
    top: var(--s4);
    right: var(--s4);
    z-index: 1080;
    display: flex;
    flex-direction: column;
    gap: var(--s2);
    pointer-events: none;
}

.mv-toast {
    pointer-events: auto;
    display: flex;
    align-items: center;
    gap: var(--s2);
    min-width: 17.5rem;
    max-width: 25rem;
    padding: var(--s2) var(--s3);
    background: var(--mv-bg-overlay);
    border: 1px solid var(--mv-border);
    border-left: 2px solid var(--mv-text-muted);
    border-radius: var(--r2);
    box-shadow: var(--shadow-overlay);
    color: var(--mv-text);
    font-size: var(--t-sm);
    font-weight: 600;
    opacity: 0;
    transform: translateY(-6px) scale(0.98);
    transition: opacity var(--dur-2) var(--ease-out),
                transform var(--dur-2) var(--ease-out);
}
.mv-toast.is-in { opacity: 1; transform: none; }
.mv-toast.is-out { opacity: 0; transform: translateY(-4px); transition-duration: var(--dur-1); }

/* La couleur passe par un filet de 2 px, pas par la couleur du texte : le
   message reste à 16:1 au lieu de ~5:1. */
.mv-toast.is-success { border-left-color: var(--mv-success); }
.mv-toast.is-error   { border-left-color: var(--mv-danger); }
.mv-toast.is-warning { border-left-color: var(--mv-accent); }
.mv-toast.is-info    { border-left-color: var(--mv-text-muted); }

.mv-toast-icon { font-weight: 800; line-height: 1; }
.mv-toast-body { flex-grow: 1; }
.mv-toast-close { flex-shrink: 0; font-size: var(--t-body); }


/* ============================================================================
   Page d'accueil
   ----------------------------------------------------------------------------
   Une famille de mise en page par section : hero asymétrique, bandeau de
   mesures, séquence numérotée, scène à onglets, tableau de données, grille de
   cartes, bande pleine largeur, clôture. Aucune ne se répète.
   ========================================================================== */

/* ── Hero : deux colonnes asymétriques, hauteur pilotée par le contenu ──────── */
.hero {
    padding: var(--s8) 0 var(--sec-tight);
    border-bottom: 1px solid var(--mv-line);
}

.hero-inner {
    display: grid;
    gap: var(--s7);
    align-items: center;
}

@media (min-width: 960px) {
    .hero-inner { grid-template-columns: 1.05fr 1fr; gap: var(--s8); }
}

.hero-title {
    font-size: clamp(2.125rem, 3.5vw, 3rem);
    font-weight: 800;
    line-height: 1.06;
    letter-spacing: -0.03em;
    margin: 0 0 var(--s4);
    text-wrap: balance;
}

.hero-lede {
    max-width: 46ch;
    margin: 0 0 var(--s5);
    color: var(--mv-text-muted);
    font-size: 1.0625rem;
    line-height: 1.55;
}

.hero-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s5); }

.hero-frame { margin: 0; }

.hero-frame img {
    width: 100%;
    height: auto;
    border: 1px solid var(--mv-border);
    border-radius: var(--r2);
}

.hero-frame-cap {
    margin-top: var(--s2);
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--mv-text-faint);
}

/* ── Bandeau de mesures : l'argument du produit, dès le premier écran ──────── */
.metrics-strip { padding: var(--sec-tight) 0 var(--s6); border-bottom: 1px solid var(--mv-line); }

.metrics-row {
    display: grid;
    gap: var(--s5);
    margin: 0 0 var(--s5);
}

@media (min-width: 720px) {
    .metrics-row { grid-template-columns: repeat(3, 1fr); gap: 0; }
    /* Filets verticaux entre les mesures : la séparation vient d'une ligne,
       pas d'une carte. */
    .metric + .metric { border-left: 1px solid var(--mv-line); padding-left: var(--s6); }
    .metric { padding-right: var(--s6); }
}

.metric dt {
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--mv-text-muted);
    margin-bottom: var(--s2);
}

.metric dd {
    margin: 0;
    font-size: clamp(2rem, 4.5vw, 3rem);
    font-weight: 800;
    line-height: 1;
    letter-spacing: -0.03em;
    color: var(--mv-accent);
    font-variant-numeric: tabular-nums;
}

/* La troisième mesure est le chiffre non flatteur : elle reste lisible mais ne
   prend pas l'accent. */
.metric:last-child dd { color: var(--mv-text); }

.metrics-note {
    max-width: 68ch;
    margin: 0;
    color: var(--mv-text-muted);
    font-size: var(--t-sm);
}

/* ── Sections courantes ────────────────────────────────────────────────────── */
.steps-section,
.ba-section,
.recall-section,
.enc-section { padding: var(--sec) 0; }

.enc-section { padding-bottom: var(--sec-tight); }

/* ── Clôture : une seule sortie, en bas de page ────────────────────────────── */
.closing {
    padding: var(--sec) 0;
    max-width: 52ch;
}

.closing-title {
    font-size: var(--t-h2);
    font-weight: 700;
    letter-spacing: -0.02em;
    margin: 0 0 var(--s3);
}

.closing-text { color: var(--mv-text-muted); margin: 0 0 var(--s5); }

.closing-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s5); }


/* ============================================================================
   Hero vitrine — pleine largeur, vidéo de fond
   ----------------------------------------------------------------------------
   Composition : le haut de l'image reste clair et lisible (c'est la preuve :
   la boîte de détection et son étiquette y sont visibles), la copie est ancrée
   en bas dans la zone que le voile assombrit, et le bandeau de mesures ferme
   la section sur un fond opaque.

   Trois contraintes ont dicté les valeurs ci-dessous, toutes mesurées sur
   static/videos/hero_loop.mp4 image par image (ffmpeg, 14 échantillons) :

   1. Luminance. Zone de texte (x 0-55 %, y 45-78 % de la source), pire bloc
      mesuré = 207/255 (81 %). Le voile est calibré sur CE pire cas, pas sur la
      moyenne (122/255).
   2. Le montage source porte un bandeau « SUBSCRIBE / SUBSCRIBED » incrusté
      entre 83 % et 95 % de la hauteur d'image. --hf-crop le sort du cadre,
      le bandeau de mesures opaque sert de second garde-fou.
   3. body > * { z-index: 1 } (app.css) fait peindre chaque section par-dessus
      la précédente. Sans le z-index: 0 ci-dessous, ce hero opaque passerait
      DEVANT le menu déroulant de la navbar.
   ========================================================================== */

.hero-full {
    /* Écrase body > * { z-index: 1 } : sinon le hero recouvre le menu navbar. */
    position: relative;
    z-index: 0;

    isolation: isolate;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;

    /* La boucle a été ré-encodée propre depuis demo_drone_family_web.mp4 : plus
       de bandeau « SUBSCRIBE » incrusté à rogner, le média occupe donc
       exactement la hauteur du hero. */
    --hf-crop: 100%;
    /* Hauteur de la navbar, qui n'est pas fixed : le hero doit la déduire pour
       tenir dans le premier écran. */
    --hf-nav: 3.5rem;

    /* svh et pas vh : la barre d'adresse iOS fausse vh. La ligne vh reste en
       repli pour les moteurs qui ignorent svh.
       max() = plancher, min() = le plafond demandé (900 px). */
    min-height: max(32rem, min(calc(100vh - var(--hf-nav)), 56.25rem));
    min-height: max(32rem, min(calc(100svh - var(--hf-nav)), 56.25rem));

    background: var(--mv-bg-sunken);
    border-bottom: 1px solid var(--mv-line);
}

/* ── Calque média ───────────────────────────────────────────────────────────
   L'affiche est un <img> et pas seulement l'attribut poster : elle peut ainsi
   porter fetchpriority="high" et devenir le LCP, peinte immédiatement, pendant
   que la vidéo ne charge rien tant que le JS n'a pas décidé de la jouer. */
.hf-media {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: var(--hf-crop);
    z-index: 0;
    pointer-events: none;
}

.hf-poster,
.hf-video {
    display: block;
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    /* « top » et pas « center » : tout le rognage est pris en bas, là où se
       trouve l'incrustation parasite. */
    object-position: center top;
}

/* La vidéo se substitue à l'affiche par un fondu, une fois qu'elle joue
   vraiment. Sous reduced-motion elle ne joue jamais : l'affiche reste. */
.hf-video {
    opacity: 0;
    transition: opacity 500ms var(--ease-out);
}
.hf-video.is-playing { opacity: 1; }

/* ── Voile de lisibilité ────────────────────────────────────────────────────
   Pas un décor : un outil de contraste. Les positions sont en rem depuis le
   BAS, pas en pourcentage, pour que le palier sombre couvre toujours la même
   hauteur physique que la copie, quelle que soit la taille du hero.

   Contrôle sur le pire bloc mesuré (207/255) :
     à 34rem du bas (haut du kicker), alpha 0,64  → --mv-text à 6,9:1
     à 20rem du bas (titre),           alpha 0,80 → --mv-text à 10:1
   Le palier haut (0,30) n'a pas de texte : il sert seulement à faire tenir le
   vert saturé de la vidéo dans la palette. */
.hf-veil {
    position: absolute;
    inset: 0;
    z-index: 1;
    pointer-events: none;
    background: linear-gradient(to top,
        rgba(11, 14, 19, 0.86) 0,
        rgba(11, 14, 19, 0.80) 20rem,
        rgba(11, 14, 19, 0.64) 34rem,
        rgba(11, 14, 19, 0.36) 46rem,
        rgba(11, 14, 19, 0.30) 100%);
}

/* ── Copie ──────────────────────────────────────────────────────────────────
   Ancrée en bas par le justify-content du parent. */
.hf-inner {
    position: relative;
    z-index: 2;
    padding-top: var(--s8);
    padding-bottom: var(--s7);
}

/* Sur photo, --mv-text-muted tombe à 2,7:1 et l'ambre à 3,5:1 : la copie du
   hero passe en --mv-text. L'accent reste porté par le bouton plein et par le
   repère carré, qui sont opaques. */
.hf-kicker { color: var(--mv-text); }

.hf-tick {
    flex: 0 0 auto;
    width: 0.5rem;
    height: 0.5rem;
    background: var(--mv-accent);
    border-radius: var(--r1);
}

.hf-title {
    font-size: clamp(2rem, 4.6vw, 3.75rem);
    font-weight: 800;
    line-height: 1.04;
    letter-spacing: -0.03em;
    margin: 0 0 var(--s4);
    text-wrap: balance;
}

.hf-lede {
    max-width: 44ch;
    margin: 0 0 var(--s6);
    color: var(--mv-text);
    font-size: 1.0625rem;
    line-height: 1.55;
}

.hf-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--s5);
}

.hf-actions .btn-quiet {
    color: var(--mv-text);
    border-color: var(--mv-line-field);
}

/* ── Bandeau de mesures ─────────────────────────────────────────────────────
   Opaque, et c'est délibéré à trois titres : il garantit le contraste des
   chiffres sans dépendre de l'image, il ferme la composition sur une ligne
   droite, et il masque le bas de la vidéo source. */
.hf-metrics {
    position: relative;
    z-index: 2;
    background: var(--mv-bg-sunken);
    border-top: 1px solid var(--mv-line);
}

.hf-row {
    display: grid;
    gap: var(--s3);
    /* « 0 auto » et pas « 0 » : la rangée porte aussi la classe .container,
       dont le margin-inline: auto assure l'alignement sur la copie du hero.
       Un margin: 0 le supprimerait et collerait les chiffres au bord. */
    margin: 0 auto;
    padding-top: var(--s5);
    padding-bottom: var(--s5);
}

.hf-metric dt {
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    font-weight: 400;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--mv-text-muted);
    margin: 0;
}

.hf-metric dd {
    margin: 0;
    font-family: var(--mv-font-mono);
    font-weight: 700;
    line-height: 1;
    letter-spacing: -0.02em;
    /* Les chaînes viennent formatées « 91,1 % » : en chasse fixe, l'espace
       qui précède le signe occupe une chasse pleine et le détache du nombre.
       On la resserre ici plutôt que de toucher au format côté Python. */
    word-spacing: -0.3em;
    font-variant-numeric: tabular-nums;
    color: var(--mv-text);
}

/* Seule la mesure honnête prend l'accent. Les deux autres sont le contexte qui
   la rend crédible, pas des arguments : elles restent en texte plein. */
.hf-metric--key dd { color: var(--mv-accent); }

/* Étroit : trois lignes de relevé, libellé à gauche, chiffre à droite. Plus
   compact qu'une pile de gros chiffres, donc le bandeau reste bas. */
@media (max-width: 719.98px) {
    .hf-metric {
        display: flex;
        align-items: baseline;
        justify-content: space-between;
        gap: var(--s4);
    }
    .hf-metric + .hf-metric {
        border-top: 1px solid var(--mv-line);
        padding-top: var(--s3);
    }
    .hf-metric dt { max-width: 22ch; }
    .hf-metric dd { font-size: 1.375rem; }
}

/* Large : trois colonnes séparées par un filet, comme le reste du site. */
@media (min-width: 720px) {
    .hf-row {
        grid-template-columns: repeat(3, 1fr);
        gap: 0;
    }
    .hf-metric { padding-right: var(--s6); }
    .hf-metric + .hf-metric {
        border-left: 1px solid var(--mv-line);
        padding-left: var(--s6);
    }
    .hf-metric dt { margin-bottom: var(--s3); }
    .hf-metric dd { font-size: clamp(1.625rem, 2.4vw, 2.125rem); }
}

/* ── Entrée en cascade ──────────────────────────────────────────────────────
   620 ms : c'est une animation d'entrée, pas un retour d'interaction, donc
   elle a le droit de dépasser --dur-4. transform + opacity uniquement.
   Le bandeau lui-même n'est PAS translaté : le décaler d'un pixel découvrirait
   la bande basse de la vidéo qu'il a justement pour rôle de masquer. Seule sa
   rangée de chiffres monte. */
@keyframes hf-rise {
    from { opacity: 0; transform: translate3d(0, 14px, 0); }
    to   { opacity: 1; transform: translate3d(0, 0, 0); }
}

.hf-kicker,
.hf-title,
.hf-lede,
.hf-actions,
.hf-row {
    animation: hf-rise 620ms var(--ease-out) both;
}

.hf-kicker  { animation-delay:  40ms; }
.hf-title   { animation-delay: 100ms; }
.hf-lede    { animation-delay: 180ms; }
.hf-actions { animation-delay: 250ms; }
.hf-row     { animation-delay: 330ms; }

/* Le bloc global de base.css force animation-duration à 0,001 ms mais NE
   touche PAS animation-delay : sans ce bloc, un lecteur en reduced-motion
   verrait un texte invisible pendant 330 ms avant qu'il n'apparaisse d'un
   coup. On coupe donc l'animation entière, pas seulement sa durée. */
@media (prefers-reduced-motion: reduce) {
    .hf-kicker,
    .hf-title,
    .hf-lede,
    .hf-actions,
    .hf-row {
        animation: none;
    }
    .hf-video { transition: none; }
}

/* ============================================================================
   L · CHORÉGRAPHIE DE SCROLL
   ----------------------------------------------------------------------------
   Couche de mouvement transversale : révélations à l'entrée, section épinglée,
   parallaxe de cadre, barre de progression de lecture.

   Règle du bloc : rien ici ne dépend d'un listener scroll. Le mouvement vient
   d'IntersectionObserver (JS), de position:sticky, ou d'animation-timeline.
   Tout se neutralise sous prefers-reduced-motion (bloc L5, en fin de bloc).
   ========================================================================== */


/* ── L1 · Révélation à l'entrée ───────────────────────────────────────────────
   POINT CRITIQUE : aucune règle de ce fichier ne pose opacity:0 sur un
   [data-reveal]. L'état masqué vit UNIQUEMENT derrière .is-armed, une classe
   que seul le JS pose, juste avant d'observer l'élément. Si le script ne se
   charge pas, plante, ou si IntersectionObserver manque, rien n'est armé et la
   page reste intégralement lisible. Un `[data-reveal] { opacity: 0 }` en CSS
   pur transformerait le moindre échec JS en page blanche.

   La transition est déclarée sur l'état RÉVÉLÉ, pas sur l'état armé : sinon
   l'ajout de .is-armed (opacity 1 → 0) s'animerait lui aussi et l'élément
   ferait un fondu sortant de 700 ms avant d'entrer. */
.is-armed {
    opacity: 0;
    transform: translateY(20px);
}

.is-armed.is-revealed {
    opacity: 1;
    transform: none;
    /* 700 ms : au-dessus de --dur-4, autorisé parce que c'est une entrée,
       jamais un retour d'interaction. --reveal-delay est posé par le JS sur
       les enfants d'un groupe échelonné (0 par défaut). */
    transition: opacity   700ms var(--ease-out) var(--reveal-delay, 0ms),
                transform 700ms var(--ease-out) var(--reveal-delay, 0ms);
}


/* ── L2 · Section épinglée ────────────────────────────────────────────────────
   Aucun JS de pinning : position:sticky fait tout le travail. Le JS ne sert
   qu'à permuter le visuel quand une étape traverse la bande centrale.
   La navbar du site n'est ni fixed ni sticky : le décalage haut n'a donc pas
   à compenser sa hauteur. */
.pin-section { padding: var(--sec) 0; }

.pin-grid {
    display: grid;
    gap: var(--s6);
}

.pin-frame {
    position: relative;
    aspect-ratio: 16 / 9;
    background: var(--mv-bg-sunken);
    border: 1px solid var(--mv-border);
    border-radius: var(--r2);
    overflow: hidden;
}

.pin-shot {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    opacity: 0;
    transition: opacity var(--dur-4) var(--ease-out);
}
.pin-shot.is-active { opacity: 1; }

/* Légendes : même pile que les images, une seule visible à la fois. La hauteur
   réservée évite que la colonne saute quand la légende change de longueur. */
.pin-caps {
    position: relative;
    min-height: 2.8em;
    margin-top: var(--s3);
}

.pin-cap {
    position: absolute;
    inset: 0;
    margin: 0;
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    line-height: 1.4;
    letter-spacing: 0.04em;
    color: var(--mv-text-muted);
    opacity: 0;
    transition: opacity var(--dur-3) var(--ease-out);
}
.pin-cap.is-active { opacity: 1; }

/* Colonne texte ------------------------------------------------------------- */
.pin-steps {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: var(--s5);
}

.pin-step {
    display: flex;
    gap: var(--s3);
    align-items: flex-start;
    border-left: 2px solid var(--mv-line);
    padding-left: var(--s4);
    transition: border-color var(--dur-3) var(--ease-out);
}
.pin-step.is-current { border-left-color: var(--mv-accent); }

.pin-num {
    flex-shrink: 0;
    width: 2rem;
    height: 2rem;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--mv-border);
    border-radius: 50%; /* pastille d'état réel : l'étape courante */
    background: transparent;
    color: var(--mv-text-muted);
    font-family: var(--mv-font-mono);
    font-size: var(--t-num);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    transition: color var(--dur-3) var(--ease-out),
                border-color var(--dur-3) var(--ease-out),
                background-color var(--dur-3) var(--ease-out);
}
.pin-step.is-current .pin-num {
    color: var(--mv-accent);
    border-color: var(--mv-accent-line);
    background: var(--mv-accent-wash);
}

.pin-step-body { flex: 1 1 auto; min-width: 0; }

.pin-step-title {
    font-size: var(--t-body);
    font-weight: 800;
    margin: 0 0 var(--s1);
    color: var(--mv-text);
}

.pin-step-text {
    font-size: var(--t-sm);
    line-height: 1.5;
    color: var(--mv-text-muted);
    margin: 0;
}

/* Image jumelle de l'étape : c'est la version mobile du visuel épinglé.
   Même URL que le .pin-shot correspondant, donc une seule requête réseau. */
.pin-step-shot { margin: 0 0 var(--s4); }
.pin-step-shot img {
    width: 100%;
    border: 1px solid var(--mv-border);
    border-radius: var(--r2);
}
.pin-step-shot figcaption {
    margin-top: var(--s2);
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    letter-spacing: 0.04em;
    color: var(--mv-text-muted);
}

/* Sous 900 px : pas d'épinglage, pile verticale image + texte -------------- */
@media (max-width: 899.98px) {
    .pin-visual { display: none; }
    .pin-step {
        flex-direction: column;
        border-left: 0;
        padding-left: 0;
        border-top: 1px solid var(--mv-line);
        padding-top: var(--s4);
    }
    .pin-step.is-current { border-top-color: var(--mv-line); }
    .pin-steps { gap: var(--s6); }
}

/* À partir de 900 px : deux colonnes, la gauche est épinglée --------------- */
@media (min-width: 900px) {
    .pin-grid {
        grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
        gap: var(--s8);
        align-items: start;
    }
    .pin-visual {
        position: sticky;
        top: var(--s6);
        align-self: start;
    }
    /* L'image jumelée à l'étape ne sert qu'en pile mobile. */
    .pin-step-shot { display: none; }

    .pin-steps { gap: 0; }
    .pin-step {
        /* Chaque étape est bien plus haute que la bande de déclenchement du
           JS (20 vh) : impossible qu'un défilement rapide la saute. */
        min-height: 46vh;
        flex-direction: row;
        justify-content: flex-start;
        align-items: center;
        padding-block: var(--s6);
    }
}


/* ── L3 · Parallaxe de cadre ──────────────────────────────────────────────────
   Dérive de 10 px sur toute la traversée du viewport. Appliquée au CADRE
   entier (figure + filet), jamais à l'image seule : une image translatée à
   l'intérieur d'un cadre en overflow:hidden découvrirait une bande de fond.
   Pas de repli JS : sans animation-timeline, aucun mouvement, point. */
@keyframes mv-drift {
    from { transform: translateY(10px); }
    to   { transform: translateY(-10px); }
}

@supports (animation-timeline: view()) {
    @media (prefers-reduced-motion: no-preference) {
        [data-parallax] {
            animation: mv-drift linear both;
            animation-timeline: view();
            animation-range: cover;
        }
    }
}


/* ── L4 · Progression de lecture ──────────────────────────────────────────────
   Filet ambre de 2 px, piloté par la timeline de scroll du document. Aucun
   listener. Par défaut l'élément n'existe pas visuellement : sur un navigateur
   sans animation-timeline il resterait figé à scaleX(0), donc on le cache. */
@keyframes mv-scroll-grow {
    from { transform: scaleX(0); }
    to   { transform: scaleX(1); }
}

.mv-scroll-progress { display: none; }

@supports (animation-timeline: scroll()) {
    @media (prefers-reduced-motion: no-preference) {
        .mv-scroll-progress {
            display: block;
            position: fixed;
            inset: 0 0 auto 0;
            height: 2px;
            z-index: 90;
            background: var(--mv-accent);
            transform: scaleX(0);
            transform-origin: 0 50%;
            pointer-events: none;
            animation: mv-scroll-grow linear both;
            animation-timeline: scroll(root block);
        }
    }
}


/* ── L5 · Mouvement réduit ────────────────────────────────────────────────────
   base.css neutralise déjà les durées globalement ; ce bloc est explicite pour
   que la couche de chorégraphie soit lisible seule. L'épinglage est CONSERVÉ
   (position:sticky n'est pas une animation), seule la permutation devient
   instantanée. */
@media (prefers-reduced-motion: reduce) {
    .is-armed,
    .is-armed.is-revealed {
        opacity: 1 !important;
        transform: none !important;
        transition: none !important;
    }

    .pin-shot,
    .pin-cap,
    .pin-step,
    .pin-num { transition: none !important; }

    [data-parallax] {
        animation: none !important;
        transform: none !important;
    }

    .mv-scroll-progress { display: none !important; }
}

/* ============================================================================
   Galerie de détections (#galerie) — « Ce que le modèle voit vraiment »
   ----------------------------------------------------------------------------
   Grille asymétrique 12 colonnes, placement explicite, aucune dépendance JS
   pour la mise en page. Le JS n'ajoute que deux choses : la révélation à
   l'entrée (.js-reveal / .is-in) et le filtre (.is-filtered / .is-out).
   Sans JS : les 12 cellules sont visibles et correctement placées.
   ========================================================================== */

.gal-section { padding: var(--sec) 0; }

/* En-tête : titre à gauche, filtre à droite, empilés sous 900 px. */
.gal-head {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: var(--s5);
    flex-wrap: wrap;
    margin-bottom: var(--s6);
}
.gal-head .sec-head { margin-bottom: 0; flex: 1 1 34ch; }

.gal-controls { display: flex; flex-direction: column; align-items: flex-start; gap: var(--s2); }

.gal-filter { display: flex; flex-wrap: wrap; gap: var(--s2); }

.gal-fbtn {
    display: inline-flex;
    align-items: center;
    gap: var(--s2);
    padding: var(--s2) var(--s3);
    background: transparent;
    border: 1px solid var(--mv-border);
    border-radius: var(--r2);
    color: var(--mv-text-muted);
    font-family: var(--mv-font-body);
    font-size: var(--t-sm);
    font-weight: 700;
    line-height: 1.3;
    cursor: pointer;
    transition: color var(--dur-1) var(--ease-out),
                border-color var(--dur-1) var(--ease-out),
                background-color var(--dur-1) var(--ease-out);
}
.gal-fbtn-n {
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    font-weight: 400;
    color: var(--mv-text-faint);
    font-variant-numeric: tabular-nums;
}
@media (hover: hover) and (pointer: fine) {
    .gal-fbtn:hover { color: var(--mv-text); border-color: var(--mv-line-field); }
}
.gal-fbtn[aria-pressed="true"] {
    background: var(--mv-accent-wash);
    border-color: var(--mv-accent-line);
    color: var(--mv-text);
}
.gal-fbtn[aria-pressed="true"] .gal-fbtn-n { color: var(--mv-accent); }

.gal-count {
    margin: 0;
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    color: var(--mv-text-faint);
    font-variant-numeric: tabular-nums;
}

/* ── Grille ───────────────────────────────────────────────────────────────────
   align-items: start volontaire : les images gardent leur 16:9 exact, aucune
   n'est recadrée (l'incrustation du HUD touche les quatre bords, un recadrage
   couperait le libellé). Les quelques pixels d'air au bas d'une cellule haute
   sont le prix de cette garantie. */
.gal-grid {
    display: grid;
    grid-template-columns: repeat(12, 1fr);
    gap: var(--s5) var(--s4);
    align-items: start;
}

.gal-cell {
    display: flex;
    flex-direction: column;
    margin: 0;
    min-width: 0;
    /* Durée du filtre : c'est un retour d'interaction, il reste sous 240 ms. */
    transition: opacity var(--dur-3) var(--ease-out),
                transform var(--dur-3) var(--ease-out);
}
.gal-cell[hidden] { display: none; }

/* Placement explicite : 4 blocs « une grande + deux petites empilées »,
   le côté de la grande alterne. 8 colonnes contre 2 × 4 colonnes : les
   hauteurs 16:9 coïncident à un demi-gap près. */
.gal-grid > .gal-cell:nth-child(1)  { grid-column: 1 / 9;  grid-row: 1 / 3; }
.gal-grid > .gal-cell:nth-child(2)  { grid-column: 9 / 13; grid-row: 1; }
.gal-grid > .gal-cell:nth-child(3)  { grid-column: 9 / 13; grid-row: 2; }
.gal-grid > .gal-cell:nth-child(4)  { grid-column: 1 / 5;  grid-row: 3; }
.gal-grid > .gal-cell:nth-child(5)  { grid-column: 1 / 5;  grid-row: 4; }
.gal-grid > .gal-cell:nth-child(6)  { grid-column: 5 / 13; grid-row: 3 / 5; }
.gal-grid > .gal-cell:nth-child(7)  { grid-column: 1 / 9;  grid-row: 5 / 7; }
.gal-grid > .gal-cell:nth-child(8)  { grid-column: 9 / 13; grid-row: 5; }
.gal-grid > .gal-cell:nth-child(9)  { grid-column: 9 / 13; grid-row: 6; }
.gal-grid > .gal-cell:nth-child(10) { grid-column: 1 / 5;  grid-row: 7; }
.gal-grid > .gal-cell:nth-child(11) { grid-column: 1 / 5;  grid-row: 8; }
.gal-grid > .gal-cell:nth-child(12) { grid-column: 5 / 13; grid-row: 7 / 9; }

/* Filtré : le placement explicite laisserait des trous, on repasse en grille
   régulière de trois colonnes. Même spécificité (0,3,0) que les règles
   nth-child ci-dessus, donc l'ordre source décide : NE PAS remonter ce bloc. */
.gal-grid.is-filtered > .gal-cell { grid-column: auto / span 4; grid-row: auto; }

/* ── Cellule ─────────────────────────────────────────────────────────────── */
.gal-shot {
    position: relative;
    border: 1px solid var(--mv-border);
    border-radius: var(--r2);
    overflow: hidden;
    background: var(--mv-bg-sunken);
    transition: border-color var(--dur-1) var(--ease-out);
}
.gal-shot img {
    width: 100%;
    height: auto;
    aspect-ratio: 16 / 9;   /* toutes les sources font 960 × 540 */
    object-fit: cover;
    display: block;
    transition: transform var(--dur-3) var(--ease-out);
}

/* Relevé chiffré. Par défaut (tactile, pas de survol possible) il est dans le
   flux, sous l'image : la légende est toujours lisible. */
.gal-read {
    margin: 0;
    display: grid;
    grid-template-columns: auto 1fr;
    gap: var(--s1) var(--s4);
    padding: var(--s3);
    background: var(--mv-bg-overlay);
    border-top: 1px solid var(--mv-border);
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    line-height: 1.4;
}
.gal-read dt { color: var(--mv-text-faint); letter-spacing: 0.04em; }
.gal-read dd { margin: 0; color: var(--mv-text); font-variant-numeric: tabular-nums; }

.gal-cap {
    display: flex;
    flex-direction: column;
    gap: var(--s2);
    padding-top: var(--s3);
}

.gal-status {
    display: inline-flex;
    align-items: center;
    gap: var(--s2);
    margin: 0;
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--mv-text-muted);
}
/* Pastille ronde : admise ici parce qu'elle marque un état sémantique réel
   (le verdict). Elle ne fait que souligner le mot, qui porte l'information. */
.gal-dot { width: 7px; height: 7px; border-radius: 50%; flex: none; border: 1px solid transparent; }
.gal-cell[data-status="identified"] .gal-dot { background: var(--mv-success); }
.gal-cell[data-status="family"]     .gal-dot { background: var(--mv-warning); }
.gal-cell[data-status="abstained"]  .gal-dot { background: transparent; border-color: var(--mv-text-faint); }
.gal-cell[data-status="abstained"]  .gal-status { color: var(--mv-text-faint); }

.gal-line { margin: 0; font-size: var(--t-sm); color: var(--mv-text-muted); }

.gal-foot {
    margin: var(--s6) 0 0;
    max-width: 62ch;
    font-size: var(--t-meta);
    color: var(--mv-text-faint);
}

/* ── Survol : pointeur fin uniquement ─────────────────────────────────────────
   Le relevé passe en calque au-dessus de l'image et se révèle. Il quitte le
   flux, donc la hauteur de la cellule ne bouge pas : aucun reflow de grille. */
@media (hover: hover) and (pointer: fine) {
    .gal-read {
        position: absolute;
        left: 0;
        right: 0;
        bottom: 0;
        border-top: 1px solid var(--mv-accent-line);
        opacity: 0;
        transform: translateY(100%);
        transition: opacity var(--dur-2) var(--ease-out),
                    transform var(--dur-3) var(--ease-out);
    }
    .gal-cell:hover .gal-read,
    .gal-cell:focus-within .gal-read { opacity: 1; transform: none; }
    .gal-cell:hover .gal-shot,
    .gal-cell:focus-within .gal-shot { border-color: var(--mv-accent-line); }
    .gal-cell:hover .gal-shot img { transform: scale(1.02); }
}

/* ── Révélation à l'entrée ───────────────────────────────────────────────────
   .js-reveal n'est posée que par le JS : sans JS, rien n'est masqué. Le JS la
   retire une fois toutes les cellules entrées, ce qui rend la main au timing
   court du filtre (spécificité 0,3,1 contre 0,2,0 ici). */
.gal-grid.js-reveal > .gal-cell {
    opacity: 0;
    transform: translateY(18px);
    transition: opacity 640ms var(--ease-out), transform 640ms var(--ease-out);
}
.gal-grid.js-reveal > .gal-cell.is-in { opacity: 1; transform: none; }

/* Sortie de filtre */
.gal-grid > .gal-cell.is-out { opacity: 0; transform: translateY(6px); }

/* ── Adaptatif ───────────────────────────────────────────────────────────────
   Les sélecteurs reprennent :nth-child(n) pour égaler la spécificité (0,3,0)
   des placements explicites ; l'ordre source fait le reste. */
@media (max-width: 1023.98px) {
    .gal-grid { gap: var(--s5) var(--s3); }
    .gal-grid > .gal-cell:nth-child(n) { grid-column: auto / span 6; grid-row: auto; }
    .gal-grid.is-filtered > .gal-cell  { grid-column: auto / span 6; }
}
@media (max-width: 639.98px) {
    .gal-head { flex-direction: column; align-items: stretch; }
    .gal-grid > .gal-cell:nth-child(n) { grid-column: auto / span 12; }
    .gal-grid.is-filtered > .gal-cell  { grid-column: auto / span 12; }
    .gal-fbtn { flex: 1 1 auto; justify-content: center; }
}

/* Mouvement réduit : base.css neutralise déjà les durées, on neutralise ici
   les états de départ, y compris si le JS n'a pas encore posé .is-in. */
@media (prefers-reduced-motion: reduce) {
    .gal-grid.js-reveal > .gal-cell { opacity: 1; transform: none; }
    .gal-cell:hover .gal-shot img { transform: none; }
    .gal-read { opacity: 1 !important; transform: none !important; }
}

/* Variante optionnelle : galerie plus large que le conteneur 1140 px.
   À poser sur la <section> si l'assembleur veut plus de densité.
   (Le débordement est neutralisé par padding-inline, pas de scroll latéral.) */
.gal-section--bleed .gal-grid,
.gal-section--bleed .gal-head {
    margin-inline: calc(50% - 50vw);
    padding-inline: max(var(--s4), calc(50vw - 690px));
}

/* ═══════════════════════════════════════════════════════════════════════════
   Sections de données — confusions · versions · marquee
   ---------------------------------------------------------------------------
   Aucun token redéfini. Aucun backdrop-filter, aucun dégradé, aucune ombre.
   Un seul accent. Rayons --r1 / --r2 uniquement.
   Les durées > --dur-4 sont réservées aux entrées au scroll ; tout retour
   d'interaction (survol) reste sous 240 ms.
   ═════════════════════════════════════════════════════════════════════════ */


/* ═══ A · Où le modèle se trompe ═══════════════════════════════════════════ */

.cfx-section { padding: var(--sec) 0; }

/* Texte réservé aux lecteurs d'écran : porte le sens de la flèche « → ». */
.cfx-sr {
    position: absolute;
    width: 1px; height: 1px;
    margin: -1px; padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

/* ── Deux images de sortie réelle ───────────────────────────────────────── */
.cfx-evidence {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--s4);
    margin-bottom: var(--s6);
}

.cfx-shot {
    margin: 0;
    border: 1px solid var(--mv-border);
    border-radius: var(--r2);
    background: var(--mv-bg-raised);
    overflow: hidden;
}

.cfx-shot img {
    width: 100%;
    aspect-ratio: 16 / 9;
    object-fit: cover;
    border-bottom: 1px solid var(--mv-line);
}

.cfx-shot figcaption {
    display: block;
    padding: var(--s4);
}

.cfx-shot-read {
    display: block;
    font-family: var(--mv-font-mono);
    font-size: var(--t-num);
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.02em;
    color: var(--mv-accent);
    margin-bottom: var(--s2);
}

.cfx-shot-alt { color: var(--mv-text-faint); margin-left: var(--s2); }

.cfx-shot-note {
    display: block;
    font-size: var(--t-sm);
    line-height: 1.55;
    color: var(--mv-text-muted);
}

/* ── Légende de lecture, collante ───────────────────────────────────────── */
.cfx-key {
    position: sticky;
    top: 0;
    z-index: 2;
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--s2) var(--s4);
    padding: var(--s3) 0;
    margin-bottom: var(--s5);
    background: var(--mv-bg);
    border-bottom: 1px solid var(--mv-border);
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--mv-text-faint);
}

.cfx-key-read { display: inline-flex; align-items: baseline; gap: var(--s2); }
.cfx-key-a { color: var(--mv-text); }
.cfx-key-b { color: var(--mv-text-muted); }
.cfx-key-scale { text-transform: none; letter-spacing: 0.02em; }

/* ── Grille par famille ─────────────────────────────────────────────────── */
.cfx-grid {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--s5);
    align-items: start;
}

.cfx-fam { min-width: 0; }

.cfx-fam-head {
    padding-bottom: var(--s3);
    border-bottom: 1px solid var(--mv-line);
    margin-bottom: var(--s3);
}

.cfx-fam-name {
    font-size: var(--t-h3);
    font-weight: 700;
    letter-spacing: -0.012em;
    margin: 0 0 var(--s1);
}

.cfx-fam-stat {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--s2);
    margin: 0 0 var(--s2);
}

.cfx-fam-acc {
    font-family: var(--mv-font-mono);
    font-size: var(--t-num);
    font-variant-numeric: tabular-nums;
    font-weight: 700;
    color: var(--mv-text);
}

.cfx-fam-meta {
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    font-variant-numeric: tabular-nums;
    color: var(--mv-text-faint);
}

.cfx-fam-verdict {
    margin: 0;
    font-size: var(--t-sm);
    line-height: 1.5;
    color: var(--mv-text-muted);
    text-wrap: pretty;
}

/* ── Lignes de couple ───────────────────────────────────────────────────── */
.cfx-pairs {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
}

.cfx-pair {
    display: grid;
    grid-template-columns: minmax(0, 1.15fr) minmax(36px, 0.75fr) 3.2rem 3rem;
    align-items: center;
    gap: var(--s2);
    padding: var(--s2) 0;
    border-bottom: 1px solid var(--mv-line);
}

.cfx-pair:last-child { border-bottom: 0; }

.cfx-names {
    display: block;
    min-width: 0;
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    letter-spacing: 0.02em;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.cfx-names .cfx-arrow { margin: 0 0.35em; }

.cfx-names b { font-weight: 700; color: var(--mv-text); }
.cfx-names i { font-style: normal; color: var(--mv-text-muted); }

.cfx-arrow { color: var(--mv-text-faint); }

.cfx-track {
    display: block;
    height: 6px;
    border-radius: var(--r1);
    background: rgba(255, 255, 255, 0.06);
    overflow: hidden;
}

/* Largeur = part de la classe rapportée à une échelle 0 à 30 %.
   L'animation d'entrée se fait en scaleX : transform uniquement. */
.cfx-fill {
    display: block;
    height: 100%;
    width: calc((var(--rate, 0) / 30) * 100%);
    max-width: 100%;
    border-radius: var(--r1);
    background: var(--mv-accent);
    opacity: 0.55;
    transform-origin: left center;
}

.cfx-pair.is-lead .cfx-fill { opacity: 1; }
.cfx-pair.is-lead .cfx-names b { color: var(--mv-accent); }

.cfx-rate {
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    font-variant-numeric: tabular-nums;
    text-align: right;
    color: var(--mv-text);
}

.cfx-count {
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    font-variant-numeric: tabular-nums;
    text-align: right;
    color: var(--mv-text-muted);
}

.cfx-den { color: var(--mv-text-faint); }

/* Ligne « aucune confusion » : pas de barre, le texte occupe le reste. */
.cfx-pair--none { grid-template-columns: minmax(0, 1fr) minmax(0, 1.7fr); }

.cfx-none {
    grid-column: 2;
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    line-height: 1.4;
    color: var(--mv-text-faint);
    text-align: right;
    white-space: normal;
}

/* ── Étage routage de famille ───────────────────────────────────────────── */
.cfx-routing {
    margin-top: var(--s5);
    padding-top: var(--s4);
    border-top: 1px solid var(--mv-border);
}

.cfx-routing-title {
    font-size: var(--t-body);
    font-weight: 700;
    letter-spacing: -0.01em;
    margin: 0 0 var(--s1);
}

.cfx-routing-text {
    margin: 0 0 var(--s3);
    font-size: var(--t-sm);
    line-height: 1.5;
    color: var(--mv-text-muted);
    text-wrap: pretty;
}

.cfx-foot {
    margin: var(--s6) 0 0;
    max-width: 78ch;
    font-size: var(--t-meta);
    line-height: 1.6;
    color: var(--mv-text-faint);
}

.cfx-foot code {
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    color: var(--mv-text-muted);
    background: rgba(255, 255, 255, 0.04);
    border: 1px solid var(--mv-line);
    border-radius: var(--r1);
    padding: 1px var(--s1);
}

/* ── Entrées au scroll (section A) ──────────────────────────────────────── */
.cfx-shot {
    opacity: 0;
    transform: translateY(14px);
    transition: opacity 640ms var(--ease-out), transform 640ms var(--ease-out);
    transition-delay: calc(var(--i, 0) * 90ms);
}

.cfx-pair {
    opacity: 0;
    transform: translateY(6px);
    transition: opacity 520ms var(--ease-out), transform 520ms var(--ease-out);
    transition-delay: calc(var(--i, 0) * 26ms);
}

.cfx-fill {
    transform: scaleX(0);
    transition: transform 760ms var(--ease-out);
    transition-delay: calc(var(--i, 0) * 26ms + 110ms);
}

.cfx-section.is-in .cfx-shot,
.cfx-section.is-in .cfx-pair { opacity: 1; transform: none; }
.cfx-section.is-in .cfx-fill { transform: scaleX(1); }

@media (max-width: 991.98px) {
    .cfx-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .cfx-grid > .cfx-fam:last-child { grid-column: 1 / -1; }
}

@media (max-width: 639.98px) {
    .cfx-evidence { grid-template-columns: 1fr; }
    .cfx-grid { grid-template-columns: 1fr; gap: var(--s6); }
    .cfx-key { position: static; }
    .cfx-pair { grid-template-columns: minmax(0, 1fr) 3.2rem 3rem; }
    .cfx-pair .cfx-track { grid-column: 1 / -1; grid-row: 2; }
    .cfx-pair--none { grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr); }
}


/* ═══ B · Ce que les versions ont changé ═══════════════════════════════════ */

.vers-section { padding: var(--sec) 0; }

.vers-track {
    list-style: none;
    margin: 0;
    padding: 0 0 var(--s4);
    display: flex;
    gap: var(--s4);
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scroll-snap-type: x proximity;
    scrollbar-width: thin;
    scrollbar-color: var(--mv-border) transparent;
}

.vers-card {
    flex: 0 0 clamp(15.5rem, 78vw, 19rem);
    scroll-snap-align: start;
    display: flex;
    flex-direction: column;
    padding: var(--s5);
}

/* La version en production est la seule à porter le filet ambre. */
.vers-card.is-current { border-color: var(--mv-accent-line); }

.vers-head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--s2);
    margin-bottom: var(--s3);
}

.vers-tag {
    font-family: var(--mv-font-mono);
    font-size: var(--t-num);
    font-weight: 700;
    letter-spacing: 0.06em;
    color: var(--mv-text);
    border: 1px solid var(--mv-border);
    border-radius: var(--r1);
    padding: 1px var(--s2);
}

.vers-card.is-current .vers-tag {
    color: var(--mv-accent-ink);
    background: var(--mv-accent);
    border-color: var(--mv-accent);
}

.vers-date {
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    color: var(--mv-text-faint);
}

.vers-title {
    font-size: var(--t-h3);
    font-weight: 700;
    letter-spacing: -0.012em;
    margin: 0 0 var(--s2);
}

.vers-text {
    margin: 0 0 var(--s4);
    font-size: var(--t-sm);
    line-height: 1.55;
    color: var(--mv-text-muted);
    text-wrap: pretty;
}

.vers-reads {
    margin: 0 0 var(--s4);
    display: flex;
    flex-direction: column;
    border-top: 1px solid var(--mv-line);
}

/* Les cartes ont des hauteurs différentes : le delta est collé en bas pour que
   la ligne de deltas s'aligne d'une carte à l'autre. */
.vers-card > *:last-child { margin-bottom: 0; }

.vers-read {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--s3);
    padding: var(--s3) 0;
    border-bottom: 1px solid var(--mv-line);
}

.vers-read dt {
    font-size: var(--t-mono);
    font-family: var(--mv-font-mono);
    line-height: 1.4;
    color: var(--mv-text-muted);
}

.vers-n {
    display: block;
    color: var(--mv-text-faint);
    font-variant-numeric: tabular-nums;
}

.vers-read dd {
    margin: 0;
    flex-shrink: 0;
    font-family: var(--mv-font-mono);
    font-size: var(--t-h3);
    font-weight: 700;
    line-height: 1.1;
    font-variant-numeric: tabular-nums;
    letter-spacing: -0.01em;
    color: var(--mv-text);
}

/* Lecture secondaire : présente, mais elle ne porte pas la démonstration. */
.vers-read.is-quiet dd { color: var(--mv-text-muted); font-weight: 400; font-size: var(--t-body); }
.vers-read.is-quiet dt { color: var(--mv-text-faint); }

/* Le seul chiffre qui prend l'accent sur la frise : la mesure honnête de v42. */
.vers-read.is-key dd { color: var(--mv-accent); font-size: var(--t-h2); }
.vers-read.is-key dt { color: var(--mv-text); }

.vers-delta {
    margin: auto 0 0;
    padding-top: var(--s3);
    border-top: 1px solid var(--mv-line);
    font-size: var(--t-mono);
    line-height: 1.5;
    color: var(--mv-text-faint);
}

.vers-delta-num {
    display: inline-block;
    font-family: var(--mv-font-mono);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.02em;
    margin-right: var(--s2);
}

.vers-delta.is-up .vers-delta-num { color: var(--mv-accent); }
/* Chute due au changement de règle : ce n'est pas une régression, pas de rouge. */
.vers-delta.is-rule .vers-delta-num { color: var(--mv-text); }

.vers-foot {
    margin: var(--s5) 0 0;
    max-width: 80ch;
    font-size: var(--t-meta);
    line-height: 1.7;
    color: var(--mv-text-faint);
}

.vers-foot code {
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    color: var(--mv-text-muted);
    background: rgba(255, 255, 255, 0.04);
    border: 1px solid var(--mv-line);
    border-radius: var(--r1);
    padding: 1px var(--s1);
    white-space: nowrap;
}

/* ── Entrée au scroll (section B) ───────────────────────────────────────── */
.vers-card {
    opacity: 0;
    transform: translateY(16px);
    transition: opacity 620ms var(--ease-out), transform 620ms var(--ease-out),
                border-color var(--dur-2) var(--ease-out);
    transition-delay: calc(var(--i, 0) * 80ms);
}

.vers-section.is-in .vers-card { opacity: 1; transform: none; }

@media (min-width: 992px) {
    /* Au-dessus de 992 px la frise tient en entier : plus de défilement. */
    .vers-track { overflow-x: visible; }
    .vers-card { flex: 1 1 0; min-width: 0; }
}


/* ═══ C · Marquee des 12 sous-types ════════════════════════════════════════ */

.mq-section { padding: var(--sec-tight) 0; }

.mq-title {
    display: flex;
    justify-content: center;
    margin: 0 0 var(--s4);
    color: var(--mv-text-faint);
}

/* Bande pleine largeur, même motif de rupture que .mv-band mais sans padding
   latéral : le texte doit toucher les deux bords. */
.mq-band {
    margin-inline: calc(50% - 50vw);
    background: var(--mv-bg-sunken);
    border-top: 1px solid var(--mv-line);
    border-bottom: 1px solid var(--mv-line);
    overflow: hidden;
    padding-block: var(--s4);
}

.mq-track {
    display: flex;
    width: max-content;
    will-change: transform;
    animation: mq-scroll 46s linear infinite;
}

@keyframes mq-scroll {
    from { transform: translateX(0); }
    to   { transform: translateX(-50%); }
}

/* Arrêt au survol et dès qu'un élément de la bande reçoit le focus clavier. */
@media (hover: hover) and (pointer: fine) {
    .mq-band:hover .mq-track { animation-play-state: paused; }
}
.mq-band:focus-within .mq-track { animation-play-state: paused; }

.mq-list {
    display: flex;
    align-items: center;
    list-style: none;
    margin: 0;
    padding: 0;
}

.mq-list li {
    display: flex;
    align-items: center;
    flex-shrink: 0;
    font-family: var(--mv-font-mono);
    font-size: clamp(1.125rem, 2.4vw, 1.625rem);
    font-weight: 700;
    letter-spacing: 0.04em;
    line-height: 1.2;
    color: var(--mv-text-muted);
    padding-right: var(--s5);
}

/* Le point sépare deux noms. Il est aussi présent sur le dernier item de chaque
   copie : c'est lui qui rend le raccord de boucle invisible. */
.mq-list li::after {
    content: "·";
    margin-left: var(--s5);
    color: var(--mv-accent);
    font-weight: 400;
}


/* ═══ Mouvement réduit ═════════════════════════════════════════════════════
   base.css écrase déjà les durées globalement, mais son
   `animation-duration: 0.001ms` figerait le marquee sur sa dernière image,
   c'est-à-dire décalé de 50 %. Il faut donc couper l'animation, pas l'accélérer.
   Les entrées au scroll sont neutralisées ici aussi : le JS n'a plus rien à
   faire, tout est déjà dans son état final.
   ═════════════════════════════════════════════════════════════════════════ */
@media (prefers-reduced-motion: reduce) {
    .cfx-shot,
    .cfx-pair,
    .vers-card { opacity: 1 !important; transform: none !important; }

    .cfx-fill { transform: scaleX(1) !important; }

    .mq-track {
        animation: none !important;
        width: 100%;
        flex-wrap: wrap;
    }
    .mq-list {
        flex-wrap: wrap;
        justify-content: center;
        width: 100%;
    }
    /* La copie du marquee n'a plus de raison d'être sans défilement. */
    .mq-list[aria-hidden="true"] { display: none; }
    .mq-track { will-change: auto; }
    .mq-list li { padding-right: var(--s3); padding-block: var(--s1); }
    .mq-list li::after { margin-left: var(--s3); }
}

/* ═══════════════════════════════════════════════════════════════════════════
   Le porteur — scène 3D du drone
   ---------------------------------------------------------------------------
   Le canvas n'est visible qu'une fois la scène montée (.is-3d-ready posée par
   drone.js). Avant ça, et si three.js ne se charge pas, c'est le repli textuel
   qui occupe la place : la section n'est jamais un trou vide.
   ═══════════════════════════════════════════════════════════════════════════ */
.porteur-section { padding: var(--sec) 0; }

.porteur-grid { display: grid; gap: var(--s6); align-items: start; }

@media (min-width: 900px) {
    .porteur-grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--s8); }
}

.porteur-stage {
    position: relative;
    aspect-ratio: 4 / 3;
    background: var(--mv-bg-sunken);
    border: 1px solid var(--mv-border);
    border-radius: var(--r2);
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
}

#drone-canvas {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    opacity: 0;
    cursor: grab;
    touch-action: pan-y;
    transition: opacity var(--dur-4) var(--ease-out);
}
#drone-canvas.is-dragging { cursor: grabbing; }
.is-3d-ready #drone-canvas { opacity: 1; }

.porteur-hint {
    position: absolute;
    bottom: var(--s3);
    left: 50%;
    transform: translateX(-50%);
    margin: 0;
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--mv-text-faint);
    opacity: 0;
    transition: opacity var(--dur-3) var(--ease-out);
}
.is-3d-ready .porteur-hint { opacity: 1; }

/* Repli : occupe la scène tant que la 3D n'est pas là, et le reste s'il elle
   ne vient jamais. Toujours dans le DOM, donc toujours lu par un lecteur d'écran. */
.porteur-fallback {
    max-width: 34ch;
    margin: 0;
    padding: var(--s5);
    text-align: center;
    color: var(--mv-text-muted);
    font-size: var(--t-sm);
}
.is-3d-ready .porteur-fallback {
    position: absolute;
    width: 1px; height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

.porteur-chain { list-style: none; margin: 0 0 var(--s6); padding: 0; }

.porteur-chain li { padding: var(--s4) 0; border-top: 1px solid var(--mv-line); }
.porteur-chain li:first-child { border-top: 0; padding-top: 0; }

.porteur-step {
    display: block;
    margin-bottom: var(--s2);
    font-weight: 700;
    letter-spacing: -0.01em;
}

.porteur-chain p { margin: 0; color: var(--mv-text-muted); font-size: var(--t-sm); }

.porteur-specs {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: var(--s4);
    margin: 0 0 var(--s3);
    padding-top: var(--s4);
    border-top: 1px solid var(--mv-line);
}

.porteur-specs dt {
    font-family: var(--mv-font-mono);
    font-size: var(--t-mono);
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--mv-text-muted);
    margin-bottom: var(--s1);
}

.porteur-specs dd {
    margin: 0;
    font-family: var(--mv-font-mono);
    font-size: var(--t-h3);
    font-weight: 700;
    color: var(--mv-accent);
    font-variant-numeric: tabular-nums;
}

.porteur-src { margin: 0; font-size: var(--t-mono); color: var(--mv-text-faint); }
.porteur-src code { font-family: var(--mv-font-mono); color: var(--mv-text-muted); }
