/* Tokens d'espacement et de hauteur de ligne (EV-03) — fondation, importée
 * en premier pour que les variables existent avant toute règle de ce fichier. */
@import url("tokens-aRBDH_e.css");

/* ---------------------------------------------------------------------------
 * Complément Symfony du design V28.
 *
 * Le design lui-même vit dans assets/styles/v28.css, GÉNÉRÉ verbatim depuis
 * _source/Sedam-Valentin-V28-Actions-Tabulaires-Comite.html
 * (php scripts/extract-design-v28.php). Ce fichier est chargé APRÈS et ne
 * contient que ce que la maquette ne pouvait pas prévoir : rendu serveur
 * (thème de formulaire, flashs, pagination), Turbo (frames, modales <dialog>),
 * et les contrôleurs Stimulus (dropzone, tri).
 *
 * Règle : ne jamais redéfinir ici un composant déjà stylé par v28.css. Si le
 * rendu diverge de la maquette, c'est le DOM du gabarit Twig qu'il faut
 * corriger, pas le CSS.
 * ------------------------------------------------------------------------- */

/* ---------- Turbo ---------- */

turbo-frame { display: block; min-width: 0; }
turbo-frame[busy] { opacity: .6; transition: opacity .12s ease; }

/* ---------- Modales CRUD : <dialog> portant le style .modal de la maquette ---------- */

dialog.modal {
    border: 1px solid var(--line);
    padding: 0;
    color: var(--ink);
    /* .modal de la maquette est centré par son .modal-bg ; <dialog> se centre seul. */
    margin: auto;
}
dialog.modal::backdrop { background: rgba(0, 9, 18, .68); backdrop-filter: blur(5px); }
dialog.modal .mh h3 { font-size: 16px; }
dialog.modal .mh .x { margin-left: auto; border: 0; background: none; color: var(--muted); font-size: 22px; cursor: pointer; line-height: 1; }
dialog.modal .mb .field:last-child { margin-bottom: 0; }
dialog.modal form { margin: 0; }

/* ---------- Thème de formulaire : erreurs et aides ---------- */

.field .errors {
    list-style: none;
    margin: 6px 0 0;
    padding: 0;
    color: var(--err);
    font-size: 12px;
}
.field .help { display: block; margin-top: 5px; color: var(--muted); font-size: 11px; }
.field.field-checkbox { display: flex; align-items: center; gap: 8px; }
.field.field-checkbox input { width: auto; }
.field.field-checkbox label { margin: 0; }
.field-row { display: flex; gap: 12px; flex-wrap: wrap; }
.field-row > .field { flex: 1 1 200px; margin-bottom: 14px; }

/* ---------- Flashs (la maquette n'avait que le toast client) ---------- */

.flash {
    border-radius: 12px;
    padding: 11px 15px;
    margin-bottom: 14px;
    border: 1px solid var(--line);
    background: var(--panel);
    color: var(--ink);
    font-size: 13px;
}
.flash-success { border-color: color-mix(in srgb, var(--ok) 45%, var(--line)); color: var(--ok); }
.flash-error { border-color: color-mix(in srgb, var(--err) 45%, var(--line)); color: var(--err); }

/* ---------- Pagination bornée (absente de la maquette : listes non paginées) ---------- */

.pagination {
    display: flex;
    gap: 8px;
    align-items: center;
    margin-top: 18px;
    color: var(--muted);
    font-size: 12px;
}
.pagination a, .pagination span {
    padding: 7px 12px;
    border: 1px solid var(--line);
    border-radius: 10px;
    background: var(--panel);
}
.pagination a:hover { background: var(--hover); text-decoration: none; }
.pagination .current { background: var(--brand); color: #fff; border-color: transparent; }

/* ---------- Accès de démonstration : rangées cliquables en vrais <button> ---------- */

.cred-bubble form { margin: 0; }
.cred-bubble button.cb-row {
    width: 100%;
    border: 0;
    background: none;
    color: inherit;
    font: inherit;
    text-align: left;
}

/* ---------- Contrôleurs Stimulus ---------- */

.dropzone-input {
    border: 2px dashed var(--line);
    border-radius: 10px;
    padding: 12px;
    width: 100%;
    background: var(--field);
    color: var(--ink);
}
.dropzone-input.dragover { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 10%, transparent); }
[data-sortable-handle] { cursor: grab; user-select: none; }
[data-sortable-handle]:active { cursor: grabbing; }
.sortable-ghost { opacity: .45; }

/* ---------- Modules interactifs : bac à sable visuel du HTML/CSS assaini ---------- */

.module-frame {
    border: 1px solid var(--line);
    border-radius: 12px;
    overflow: hidden;
    background: #fff;
    color: #111;
    padding: 14px;
}

/* ---------- Accessibilité ---------- */

.visually-hidden {
    position: absolute;
    width: 1px; height: 1px;
    margin: -1px; padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}
:where(a, button, input, select, textarea, [tabindex]):focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}
/* T46 — la règle ci-dessus a une spécificité NULLE (`:where`) et était battue par
   `v28.css` (`input:focus{outline:none}` et `.v19-grid-search input{box-shadow:none
   !important}`) : le focus était totalement invisible sur #document-q et #assistant-q.
   On force le contour avec une spécificité d'élément et `!important`. */
a:focus-visible, button:focus-visible, input:focus-visible, select:focus-visible,
textarea:focus-visible, [tabindex]:focus-visible,
input:focus, select:focus, textarea:focus {
    outline: 3px solid var(--accent, #37b0e1) !important;
    outline-offset: 2px;
}
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

/* ---------------------------------------------------------------------------
 * Ponts <button> → <a>
 *
 * La maquette naviguait en JavaScript : ses actions étaient des <button
 * onclick>. Ici la navigation est faite de liens (Turbo Drive), et deux
 * sélecteurs de la maquette sont écrits pour `button`. On les rejoue à
 * l'identique pour `a` — mêmes valeurs, aucune invention. À resynchroniser si
 * ces règles changent dans v28.css.
 * ------------------------------------------------------------------------- */

/* v28.css : .v19-page-actions button */
.v19-page-actions a {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    height: 39px;
    padding: 0 14px;
    border-radius: 9px;
    border: 1px solid var(--line);
    background: var(--field);
    color: var(--ink);
    font-size: 9px;
    font-weight: 900;
}
.v19-page-actions a.primary { border-color: var(--accent); background: var(--accent); color: #fff; }
.v19-page-actions a:hover { text-decoration: none; }
.v19-page-actions a svg { width: 14px; height: 14px; }

/* v28.css : .sv28-inline-ops button */
.sv28-inline-ops a {
    display: inline-flex;
    align-items: center;
    height: 36px;
    padding: 0 11px;
    border: 1px solid var(--line);
    border-radius: 9px;
    background: var(--field);
    color: var(--ink);
    font-size: 10px;
    font-weight: 850;
}
.sv28-inline-ops a:hover { text-decoration: none; }

/* v28.css : .sv28-tab est écrit pour <button>, qui centre son texte verticalement. */
a.sv28-tab { display: inline-flex; align-items: center; justify-content: center; }

/* Un formulaire d'action (POST + CSRF) ne doit pas casser l'alignement du rail. */
.sv28-action-host form, .v19-page-actions form { display: contents; }

/* Un bloc « état + commande » (accès d'une machine, activité d'un parc) posé
   DANS un rail d'actions : `display: contents` ci-dessus fait du bouton un
   élément du rail, mais laissait l'étiquette d'état flotter hors de la ligne.
   Elle s'aligne ici, à la même hauteur que les boutons. */
.sv28-action-host .sv28-inline-ops { display: contents; }
.sv28-action-host .sv28-inline-ops > span {
    display: inline-flex;
    align-items: center;
    height: 36px;
    color: var(--muted);
    font-size: 12px;
    font-weight: 700;
    white-space: nowrap;
}
.sv28-action-host .sv28-inline-ops > span.good { color: var(--ok); }
.sv28-action-host .sv28-inline-ops > span.warn { color: var(--warn, #e9a13b); }

/* Déconnexion : formulaire POST (CSRF) transparent pour que le <button> reste
   l'élément de grille de `.sidebar .foot` ; remise à zéro des styles natifs. */
.logout-form { display: contents; }
.logout-btn { border: 0; padding: 0; }

/* Les liens qui portent un composant ne doivent pas hériter du style de lien. */
a:where(.v19-entity-card, .v20-premium-card, .sv28-tab, .sv28-action, .sv28-doc-item, .v4-ai-doc, .cb-row) {
    color: inherit;
    text-decoration: none;
}
a:where(.v19-entity-card, .v20-premium-card, .sv28-tab, .sv28-action, .sv28-doc-item, .v4-ai-doc, .cb-row):hover {
    text-decoration: none;
}
a.sv28-doc-item, a.v4-ai-doc { display: block; }
a.v4-ai-doc { display: flex; }
.sv28-doc-item[aria-current="page"], .v4-ai-doc[aria-current="page"] {
    background: color-mix(in srgb, var(--accent) 8%, transparent);
}
.qr-box img { width: 100%; height: auto; display: block; }

/* ---------------------------------------------------------------------------
 * Écrans qui n'existaient pas dans la maquette
 *
 * Trois écrans du portage n'ont pas d'équivalent V28 : titres de section du
 * centre de contrôle, extrait de recherche plein texte, empilement de modules
 * d'une machine. Réglages d'espacement uniquement, jamais de nouvelle identité.
 * ------------------------------------------------------------------------- */

.sv28-section-title { margin: 26px 0 10px; font-size: 15px; font-weight: 850; color: var(--ink); }
.sv28-section-title:first-child { margin-top: 0; }
.search-extract { grid-column: span 3; }
.module-block { margin-bottom: 22px; }

/* Aperçu documentaire d'une machine : le volet droit est une frame Turbo, sa
   tête réutilise le vocabulaire `.sv28-doc-item` (titre + méta) sans le lien. */
.sv28-doc-preview-pane { min-height: 360px; }
.sv28-doc-preview-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
    padding: 14px 10px;
    border-bottom: 1px solid color-mix(in srgb, var(--line) 58%, transparent);
}
.sv28-doc-preview-head strong { display: block; color: var(--ink); font-size: 14px; }
.sv28-doc-preview-head small { color: var(--muted); font-size: 10px; }

/* Nos fiches portent plus d'actions que celles de la maquette : sur petit écran,
   le rail doit passer à la ligne au lieu de déborder hors du cadre. */
@media (max-width: 1100px) {
    .sv28-action-host { flex-wrap: wrap; }
}

/* ---------------------------------------------------------------------------
 * Logo de l'écran de connexion : déclinaison blanche en thème nuit
 *
 * Le logo est un bleu plein (#0048a8 à #0060a8) sur fond transparent. Sur le
 * héros sombre de `#login`, le mot « VALENTIN » tombait à **1,90:1** de contraste
 * — sous le seuil de 3:1 attendu pour un élément graphique.
 *
 * Réponse retenue : une **déclinaison monochrome blanche**, comme le fait toute
 * charte pour un fond sombre. `brightness(0)` écrase les pixels opaques en noir
 * en préservant la transparence, `invert(1)` les passe en blanc : silhouette
 * exacte du logo, aucun halo, aucun fichier supplémentaire. Le soulignement
 * rouge passe lui aussi en blanc — c'est le propre d'un logo monochrome.
 *
 * `filter` remplace la propriété héritée : l'ombre portée de la maquette
 * (`drop-shadow`) est donc reconduite ici, après la mise en blanc.
 *
 * En thème jour, le logo reste dans ses couleurs de marque sur le héros clair.
 * ------------------------------------------------------------------------- */

html[data-theme="night"] #login .login-logo {
    filter: brightness(0) invert(1) drop-shadow(0 14px 28px rgba(0, 0, 0, .35));
}

/* Même famille de défaut, thème inverse : la baseline sous le logo est un bleu
 * clair (#63beff) que la maquette n'a jamais décliné en thème jour — 1,97:1 sur
 * le héros clair. On reprend le bleu jour que le design utilise déjà juste à
 * côté, pour `.login-security b` : 5,73:1, sans introduire de teinte nouvelle. */
html[data-theme="day"] #login .brand-tag { color: #0b67ad; }

/* ---------------------------------------------------------------------------
 * Écran de connexion : proportions demandées en revue
 *
 * Le logo et sa baseline sont agrandis par rapport à la maquette. Le logo garde
 * `object-fit: contain` : c'est la hauteur de la boîte qui commande la taille
 * rendue, la largeur ne fait que lui laisser la place (ratio 383/122).
 * ------------------------------------------------------------------------- */

/* `!important` obligatoire : la couche de la maquette fixe déjà ces dimensions
   en `!important`, un simple ordre de chargement ne suffirait pas à passer. */
#login .login-logo {
    width: min(560px, 82%) !important;
    height: 158px !important;
    margin-bottom: 22px !important;
}

#login .brand-tag {
    font-size: 13px;
    letter-spacing: .2em;
}

@media (max-width: 1280px) {
    #login .login-logo { height: 136px !important; }
}

/* Titre et chapeau partagent exactement la largeur utile du panneau : ils
   s'alignent par construction, sans largeur magique à maintenir. La colonne est
   élargie vers la droite pour que « machines SEDAM VALENTIN » tienne sur une
   ligne — il lui fallait 941 px à la taille d'origine, contre 748 disponibles.
   `!important` partout : la maquette verrouille ces quatre propriétés au même
   niveau (`#login h1`, `#login p.lead`, `#login .left`). */
#login .left {
    padding-right: clamp(28px, 2.5vw, 40px) !important;
}

/* Taille calibrée par mesure, pas à l'estime : « machines avec Sedam Valentin »
   doit tenir sur une ligne jusqu'à 981 px — dernière largeur où le panneau
   gauche est affiché. La largeur disponible ne suit pas le viewport (le padding
   est en clamp), d'où deux paliers plutôt qu'un coefficient unique.
   Marge conservée ~5 % pour absorber une substitution de police. */
#login h1 {
    max-width: 100% !important;
    font-size: clamp(30px, 3.7vw, 56px) !important;
}

@media (max-width: 1100px) {
    #login h1 { font-size: 3.35vw !important; }
}

#login h1 span { white-space: nowrap; }

#login p.lead {
    max-width: 100% !important;
}

/* ---------------------------------------------------------------------------
 * Écran de connexion sur mobile : la marque redevient visible
 *
 * La maquette masque purement et simplement le panneau gauche sous 980 px
 * (`#login .left { display:none }`) : sur smartphone, il ne restait que le
 * formulaire, sans logo ni texte.
 *
 * On repasse l'écran en une colonne et on ordonne les blocs autour du
 * formulaire : logo et signature au-dessus — l'identité se voit tout de suite —
 * puis le formulaire, immédiatement utilisable sans défiler, et enfin le
 * discours. C'est l'ordre qui sert un outil de terrain : on arrive ici après
 * avoir scanné un QR code, la connexion prime sur la lecture.
 *
 * `display: contents` sur `.left` fait disparaître la boîte sans toucher au
 * gabarit : ses enfants deviennent enfants de `#login` et peuvent donc être
 * réordonnés individuellement.
 * ------------------------------------------------------------------------- */

@media (max-width: 980px) {
    /* Spécificité alignée sur `#login:not(.hidden)` de la maquette, qui impose
       `display:grid` — sans quoi la mise en colonne ne passerait pas. */
    #login:not(.hidden) {
        display: flex !important;
        flex-direction: column !important;
        align-items: center !important;
        justify-content: flex-start !important;
        padding: 26px 14px 36px !important;
        gap: 0 !important;
        /* `#login` est en `position:fixed` avec `overflow:hidden` : sans cela, tout
           ce qui dépasse la hauteur de l'écran est coupé ET inatteignable. */
        overflow-x: hidden !important;
        overflow-y: auto !important;
        overscroll-behavior: contain;
        /* Barres d'outils mobiles : `dvh` suit leur apparition, `vh` non. */
        height: 100dvh !important;
    }

    /* Filigrane « SV » ancré hors cadre (bottom:-8%) : il ajouterait du vide
       à faire défiler une fois l'overflow libéré. */
    #login::after { display: none !important; }

    #login .left { display: contents !important; }
    /* Décor absolu calibré pour un grand panneau : il déborderait ici. */
    #login .login-tech-signature { display: none !important; }

    /* Une seule colonne : tous les blocs partagent la largeur de la carte,
       donc le même bord gauche. */
    #login .login-logo,
    #login .brand-tag,
    #login .right,
    #login h1,
    #login p.lead {
        width: min(520px, calc(100vw - 28px)) !important;
        max-width: min(520px, calc(100vw - 28px)) !important;
        /* Enfants d'un conteneur flex en colonne : sans cela ils se laissent
           comprimer sous leur hauteur naturelle et leur contenu déborde —
           la bulle de démonstration passait par-dessus le titre. */
        flex: 0 0 auto !important;
    }

    #login .login-logo {
        order: 1;
        height: 56px !important;
        margin: 0 0 9px !important;
    }
    #login .brand-tag {
        order: 2;
        margin: 0 0 18px;
        font-size: 10px;
        letter-spacing: .14em;
    }
    #login .right {
        order: 3;
        margin: 0 !important;
        min-height: 0 !important;
    }
    #login h1 {
        order: 4;
        margin: 30px 0 0 !important;
        font-size: clamp(24px, 6.6vw, 32px) !important;
        line-height: 1.04 !important;
    }
    #login p.lead {
        order: 5;
        margin: 12px 0 0 !important;
        font-size: 14px !important;
    }
}

/* ---------------------------------------------------------------------------
 * Navigation latérale en accordéons (volet admin)
 *
 * HTML natif `<details>/<summary>` : repli/dépli sans JavaScript, au clavier,
 * annoncé par les lecteurs d'écran. L'accordéon qui contient la route courante
 * est rendu `open` par le serveur (voir templates/_nav.html.twig). On ne
 * redéfinit ici que ce que la maquette ne pouvait pas prévoir — le vocabulaire
 * `.nav a` de v28.css reste la référence pour les liens eux-mêmes.
 * ------------------------------------------------------------------------- */

.nav .nav-acc { margin: 6px 0; }

.nav .nav-acc-summary {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    min-height: 42px;
    padding: 10px 12px;
    border-radius: 5px;
    color: var(--muted);
    font-size: 12px;
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
    cursor: pointer;
    list-style: none;
    user-select: none;
}
.nav .nav-acc-summary::-webkit-details-marker { display: none; }
.nav .nav-acc-summary:hover { color: var(--ink); background: var(--hover); }
.nav .nav-acc[open] > .nav-acc-summary { color: var(--ink); }
/* Contraste jour aligné sur les liens de nav (v28.css : #627d97). */
html[data-theme="day"] .nav .nav-acc-summary { color: #627d97; }

.nav .nav-acc-chevron {
    width: 15px;
    height: 15px;
    flex: none;
    opacity: .75;
    transition: transform .18s ease;
}
.nav .nav-acc[open] > .nav-acc-summary .nav-acc-chevron { transform: rotate(180deg); }

.nav .nav-acc-panel { padding: 2px 0 6px; }

/* Repli par défaut sur petit écran ? Non : le tiroir fait ~328 px, les libellés
   restent lisibles. Aucun traitement spécial n'est requis. */

/* ---------------------------------------------------------------------------
 * Carte d'entité « métrique » — patron sv29
 *
 * Macro : `metric_card()` dans templates/design/v28.html.twig.
 * Maquette validée : docs/maquettes/2026-10-03-cartes-modeles/05-metrique-parc.html
 *
 * Pourquoi ici et non dans v28.css : v28.css est généré verbatim depuis la
 * maquette d'origine et ne connaît pas ce patron.
 *
 * Pourquoi une classe NEUVE et non `.v19-entity-card` : la couche sv22 de
 * v28.css impose à cette classe son fond, son anatomie et ses tailles en
 * `!important`. S'y greffer imposerait un `!important` sur chaque déclaration
 * ci-dessous. La carte sv29 porte donc sa propre matière — plus calme, sans
 * quadrillage technique — et le contrôleur Stimulus `filter` a été élargi
 * pour la reconnaître.
 * ------------------------------------------------------------------------- */

.sv29-card {
    --sv29-accent: #45b7ff;
    --sv29-p: 0;
    position: relative;
    isolation: isolate;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    padding: 18px 19px 0;
    border: 1px solid var(--line);
    border-radius: 18px;
    background: linear-gradient(165deg, var(--panel2, var(--card)), var(--card) 60%, var(--soft));
    box-shadow: var(--shadow);
    color: var(--ink);
    cursor: pointer;
    transition: transform .22s cubic-bezier(.2, .75, .25, 1), border-color .22s ease, box-shadow .22s ease;
}
/* Halo d'accent : une seule source de couleur, pas de trame. */
.sv29-card::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    background: radial-gradient(240px 170px at 108% 6%,
        color-mix(in srgb, var(--sv29-accent) 13%, transparent), transparent 70%);
}
.sv29-card::after {
    content: "";
    position: absolute;
    left: 19px;
    right: 19px;
    top: 0;
    height: 1px;
    z-index: 1;
    pointer-events: none;
    background: linear-gradient(90deg, transparent,
        color-mix(in srgb, var(--sv29-accent) 55%, #fff), transparent);
    opacity: .45;
}
.sv29-card > * { position: relative; z-index: 2; }
.sv29-card:hover {
    transform: translateY(-3px);
    border-color: color-mix(in srgb, var(--sv29-accent) 36%, var(--line));
    box-shadow: 0 22px 48px rgba(0, 0, 0, .26);
}
html[data-theme="day"] .sv29-card:hover { box-shadow: 0 20px 44px rgba(35, 78, 111, .14); }
.sv29-card:focus-visible { outline: 2px solid var(--sv29-accent); outline-offset: 3px; }

/* Teintes par nature d'entité — mêmes valeurs que la couche sv22 de v28.css. */
.sv29-card[data-entity-kind="type"]    { --sv29-accent: #45b7ff; }
.sv29-card[data-entity-kind="model"]   { --sv29-accent: #9b7cff; }
.sv29-card[data-entity-kind="machine"] { --sv29-accent: #20c1ff; }
.sv29-card[data-entity-kind="park"]    { --sv29-accent: #4cc983; }

/* L'icône occupe la première colonne sur DEUX lignes — la hauteur du libellé de
   nature plus celle du titre. Le reste (lead, chips, rattachements) court sur
   toute la largeur, sous ce bloc d'identité. */
.sv29-main {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    column-gap: 13px;
    align-items: center;
    padding-bottom: 18px;
}
.sv29-icon {
    grid-row: 1 / span 2;
    display: grid;
    place-items: center;
    width: 42px;
    height: 42px;
    flex: none;
    border: 1px solid color-mix(in srgb, var(--sv29-accent) 32%, var(--line));
    border-radius: 12px;
    background: color-mix(in srgb, var(--sv29-accent) 10%, transparent);
    color: var(--sv29-accent);
}
.sv29-icon svg {
    width: 21px;
    height: 21px;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.7;
    stroke-linecap: round;
    stroke-linejoin: round;
}
.sv29-kind-label {
    grid-column: 2;
    align-self: end;
    min-width: 0;
    overflow: hidden;
    color: var(--sv29-accent);
    font-size: 9.5px;
    font-weight: 900;
    letter-spacing: .16em;
    text-overflow: ellipsis;
    text-transform: uppercase;
    white-space: nowrap;
}
.sv29-lead, .sv29-chips, .sv29-lines { grid-column: 1 / -1; }

/* ---- titre ---- */
.sv29-title { grid-column: 2; align-self: start; display: flex; align-items: baseline; gap: 9px; min-width: 0; }
.sv29-title strong {
    min-width: 0;
    overflow: hidden;
    color: var(--ink);
    font-size: 19px;
    font-weight: 800;
    line-height: 1.15;
    letter-spacing: -.03em;
    text-overflow: ellipsis;
    white-space: nowrap;
}
/* Les références et numéros de série sont des identifiants : chasse fixe. */
.sv29-title strong.sv29-mono {
    font-family: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 21px;
    font-weight: 700;
    letter-spacing: -.02em;
}
.sv29-lead {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    margin: 6px 0 0;
    color: var(--muted);
    font-size: 12px;
    line-height: 1.45;
}

/* ---- pastilles d'identité (machine : modèle, n° de série, QR…) ---- */
.sv29-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 11px; }
.sv29-chip {
    display: inline-flex;
    align-items: baseline;
    gap: 5px;
    max-width: 100%;
    padding: 4px 9px;
    border: 1px solid var(--line);
    border-radius: 8px;
    background: var(--field);
}
.sv29-chip small {
    flex: none;
    color: var(--muted);
    font-size: 9px;
    font-weight: 900;
    letter-spacing: .11em;
    text-transform: uppercase;
}
.sv29-chip b {
    min-width: 0;
    overflow: hidden;
    color: var(--ink);
    font-size: 11.5px;
    font-weight: 800;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* L'état, toujours en DERNIÈRE chip : même place sur toutes les cartes, et la
   seule couleur saturée de la carte. */
.sv29-chip-etat { margin-left: auto; border-color: color-mix(in srgb, var(--ok) 38%, var(--line));
    background: color-mix(in srgb, var(--ok) 12%, transparent); }
.sv29-chip-etat b { color: var(--ok); }
.sv29-chip-etat.off { border-color: var(--line); background: var(--field); }
.sv29-chip-etat.off b { color: var(--muted); }

/* ---- rattachements : une ligne, une icône (société, parc) ---- */
.sv29-lines { display: flex; flex-direction: column; gap: 5px; margin-top: 10px; }
.sv29-line {
    display: flex;
    align-items: center;
    gap: 7px;
    min-width: 0;
    overflow: hidden;
    color: var(--muted);
    font-size: 11.5px;
    font-weight: 600;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.sv29-line-icon { display: grid; place-items: center; width: 14px; height: 14px; flex: none; }
.sv29-line-icon svg {
    width: 14px;
    height: 14px;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.8;
    stroke-linecap: round;
    stroke-linejoin: round;
}

@media (max-width: 420px) {
    .sv29-main { column-gap: 10px; }
    .sv29-icon { width: 36px; height: 36px; }
    .sv29-icon svg { width: 18px; height: 18px; }
}
@media (prefers-reduced-motion: reduce) {
    .sv29-card { transition: none; }
    .sv29-card:hover { transform: none; }
}

/* ---------------------------------------------------------------------------
 * Menu déroulant d'actions (`<details>` natif) — exports du QR.
 *
 * `<details>`/`<summary>` plutôt qu'un contrôleur : l'ouverture, la fermeture et
 * l'accès au clavier sont donnés par le navigateur. Le seul besoin est de
 * flotter la liste au-dessus du contenu, et c'est du CSS.
 * ------------------------------------------------------------------------- */
.sv28-menu { position: relative; display: inline-block; }
.sv28-menu > summary { cursor: pointer; list-style: none; }
.sv28-menu > summary::-webkit-details-marker { display: none; }
.sv28-menu > summary::after { content: '▾'; margin-left: 6px; font-size: 10px; opacity: .7; }
.sv28-menu[open] > summary { border-color: var(--accent, var(--line)); }
.sv28-menu-liste {
    position: absolute;
    right: 0;
    top: calc(100% + 6px);
    z-index: 30;
    min-width: 120px;
    padding: 5px;
    border: 1px solid var(--line);
    border-radius: 10px;
    background: var(--panel, var(--card));
    box-shadow: 0 18px 38px rgba(0, 0, 0, .28);
}
.sv28-menu-liste a {
    display: block;
    padding: 7px 10px;
    border-radius: 7px;
    color: var(--ink);
    font-size: 12px;
    font-weight: 700;
    text-decoration: none;
}
.sv28-menu-liste a:hover { background: var(--hover, var(--soft)); }

/* T47 — lien d'évitement : premier élément focusable, visible au focus. */
.sv28-skip-link {
    position: absolute;
    left: -9999px;
    top: 0;
    z-index: 1000;
    padding: 10px 16px;
    background: var(--panel, #0b1c2c);
    color: var(--ink, #fff);
    border-radius: 0 0 8px 0;
    font-weight: 700;
}
.sv28-skip-link:focus {
    left: 0;
    outline: 3px solid var(--accent, #37b0e1);
    outline-offset: 2px;
}

/* T67 — avancement d'indexation sur la fiche document.
   Toutes les couleurs passent par les variables du design : la liste se lit dans
   les deux thèmes (`html[data-theme]`) sans règle dédiée. */
.sv28-progress {
    margin: 0 0 18px;
    padding: 14px 16px;
    background: var(--panel, #08233a);
    border: 1px solid var(--line, rgba(69, 170, 239, .2));
    border-radius: var(--radius, 14px);
}
.sv28-progress-head {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    align-items: baseline;
    margin: 0 0 10px;
    font-size: 13px;
    color: var(--muted, #8aa9c1);
}
.sv28-progress-head strong { color: var(--ink, #eaf6ff); font-size: 14px; }

.sv28-steps { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
.sv28-step {
    display: grid;
    grid-template-columns: 10px 1fr auto auto;
    align-items: center;
    gap: 10px;
    padding: 6px 0;
    border-top: 1px solid var(--line, rgba(69, 170, 239, .2));
    font-size: 12px;
}
.sv28-step:first-child { border-top: 0; }
.sv28-step-label { color: var(--ink, #eaf6ff); }
.sv28-step-count, .sv28-step-state { color: var(--muted, #8aa9c1); font-variant-numeric: tabular-nums; }

/* Pastille d'état : la seule marque visuelle de l'avancement, doublée par le
   libellé textuel de la colonne d'état (jamais la couleur seule). */
.sv28-step-dot {
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background: var(--line, #294b66);
    box-shadow: inset 0 0 0 1px var(--line, #294b66);
}
.sv28-step.is-fait .sv28-step-dot { background: var(--ok, #39dd86); }
.sv28-step.is-en_cours .sv28-step-dot { background: var(--accent, #1597ff); animation: sv28-pulse 1.4s ease-in-out infinite; }
.sv28-step.is-echec .sv28-step-dot { background: var(--err, #ff465d); }
.sv28-step.is-sans_objet .sv28-step-dot { background: var(--warn, #ffb439); }
.sv28-step.is-echec .sv28-step-state { color: var(--err, #ff465d); }

@keyframes sv28-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .35; } }
@media (prefers-reduced-motion: reduce) {
    .sv28-step.is-en_cours .sv28-step-dot { animation: none; }
}

/* Barre d'avancement : vocabulaire `.role-meter > i` de la maquette, stylé pour
   ce contexte (v28.css ne le définit que dans la carte latérale et la note héro).
   La largeur vient de `data-part` — INTERFACE_RULES.md proscrit le style inline,
   que la CSP stricte interdirait de toute façon. */
.sv28-step .role-meter {
    grid-column: 2 / -1;
    position: relative;
    height: 4px;
    border-radius: 999px;
    background: var(--soft, #0b2a44);
    overflow: hidden;
}
.sv28-step .role-meter i {
    display: block;
    height: 100%;
    width: 0;
    border-radius: inherit;
    background: linear-gradient(90deg, var(--brand, #0879df), var(--accent-2, #48b9ff));
}
.sv28-step .role-meter[data-part="0"] i { width: 0; }
.sv28-step .role-meter[data-part="10"] i { width: 10%; }
.sv28-step .role-meter[data-part="20"] i { width: 20%; }
.sv28-step .role-meter[data-part="30"] i { width: 30%; }
.sv28-step .role-meter[data-part="40"] i { width: 40%; }
.sv28-step .role-meter[data-part="50"] i { width: 50%; }
.sv28-step .role-meter[data-part="60"] i { width: 60%; }
.sv28-step .role-meter[data-part="70"] i { width: 70%; }
.sv28-step .role-meter[data-part="80"] i { width: 80%; }
.sv28-step .role-meter[data-part="90"] i { width: 90%; }
.sv28-step .role-meter[data-part="100"] i { width: 100%; }

/* ===========================================================================
 * COUCHE sv31 — coque compacte
 *
 * Source : docs/maquettes/2026-10-07-coque-compacte-famille/famille-machine.html
 *
 * Elle REMPLACE l'habillage de coque des couches v19 → v29 de `v28.css` :
 * arborescence, en-tête, ambiances, palette. `v28.css` étant GÉNÉRÉ (et truffé de
 * `!important` depuis la couche sv22), cette couche en emploie là où il n'y a pas
 * d'autre moyen de reprendre la main. Chaque `!important` écrase une règle de
 * `v28.css` précisément identifiée, jamais par confort.
 *
 * Trois partis pris :
 *   - un seul accent, le bleu SEDAM (`--sv31-c`) ; seuls les états gardent une
 *     couleur propre (vert / ambre / rouge) ;
 *   - un halo de lumière en haut à droite, posé en voile fixe au-dessus de
 *     l'interface (`mix-blend-mode` écarté : tous les compositeurs ne l'honorent pas) ;
 *   - des surfaces en léger dégradé, d'une valeur au-dessus du fond de page.
 * ------------------------------------------------------------------------- */

:root {
    --sv31-c: #0879df;          /* accent unique — bleu SEDAM VALENTIN (--sv-blue) */
    --sv31-sw: 244px;           /* arborescence déployée */
    --sv31-rail: 76px;          /* arborescence repliée */
    --sv31-mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, monospace;
}

/* ---------- Ambiances : bleu profond (défaut), nuit, jour ---------- */

body[data-theme="blue"] {
    --sv31-page: #04101f; --sv31-s0: #0a1c31; --sv31-s1: #0d2239; --sv31-s2: #122942;
    --sv31-well: #071829; --sv31-ink: #e6eefa; --sv31-ink2: #bbcfe6; --sv31-muted: #7d96b4;
    --sv31-b: #1c3a59; --sv31-b2: #13293f; --sv31-hover: #14324f;
    --sv31-sidebar: #030c17; --sv31-top: #061527;
    --sv31-ok: #36b981; --sv31-warn: #e0a53a; --sv31-err: #ef6a62;
    --sv31-sh: 0 1px 2px rgba(0, 0, 0, .4);
    --sv31-sh-h: 0 4px 14px rgba(0, 0, 0, .45);
    --sv31-halo: rgba(46, 150, 235, .22);
}
body[data-theme="night"] {
    --sv31-page: #0c0e11; --sv31-s0: #14171b; --sv31-s1: #181c21; --sv31-s2: #1d2228;
    --sv31-well: #101317; --sv31-ink: #e9edf2; --sv31-ink2: #c3cad2; --sv31-muted: #8a939d;
    --sv31-b: #2c333b; --sv31-b2: #23292f; --sv31-hover: #232a32;
    --sv31-sidebar: #0e1114; --sv31-top: #111418;
    --sv31-ok: #35b87c; --sv31-warn: #d99f36; --sv31-err: #e9645c;
    --sv31-sh: 0 1px 2px rgba(0, 0, 0, .45);
    --sv31-sh-h: 0 4px 14px rgba(0, 0, 0, .5);
    --sv31-halo: rgba(150, 178, 212, .15);
}
body[data-theme="day"] {
    --sv31-c: #076fc8;          /* le bleu se fonce sur surface claire, pour le contraste */
    --sv31-page: #eceff4; --sv31-s0: #f7fafd; --sv31-s1: #fdfeff; --sv31-s2: #ffffff;
    --sv31-well: #f2f6fa; --sv31-ink: #14293d; --sv31-ink2: #2e4a63; --sv31-muted: #64798c;
    --sv31-b: #d6e0e9; --sv31-b2: #e6ecf2; --sv31-hover: #eef4fa;
    --sv31-sidebar: #ffffff; --sv31-top: #ffffff;
    --sv31-ok: #17874f; --sv31-warn: #a9711a; --sv31-err: #c23c38;
    --sv31-sh: 0 1px 2px rgba(21, 48, 74, .07);
    --sv31-sh-h: 0 4px 14px rgba(21, 48, 74, .12);
    --sv31-halo: rgba(8, 121, 223, .10);
}

/* Les jetons de `v28.css` sont réalignés sur ceux-ci : les composants non repris
   par cette couche (modales, formulaires, pagination) suivent l'ambiance. */
body[data-theme="blue"],
body[data-theme="night"],
body[data-theme="day"] {
    --bg: var(--sv31-page); --card: var(--sv31-s1); --field: var(--sv31-well);
    --soft: var(--sv31-well); --line: var(--sv31-b); --ink: var(--sv31-ink);
    --muted: var(--sv31-muted); --accent: var(--sv31-c); --brand: var(--sv31-c);
    --ok: var(--sv31-ok); --warn: var(--sv31-warn); --err: var(--sv31-err);
    --panel: var(--sv31-s1); --panel2: var(--sv31-s2); --hover: var(--sv31-hover);
    --top: var(--sv31-top); --sidebar: var(--sv31-sidebar); --hero: var(--sv31-s1);
    --shadow: var(--sv31-sh);
}

body[data-role] {
    background: var(--sv31-page) !important;
    color: var(--sv31-ink);
}
/* v28 pose une trame et un biseau sur ::before / ::after du body : la coque
   compacte ne les veut pas, le halo les remplace. */
body[data-role]::before { display: none !important; }
body[data-role]::after {
    content: "" !important;
    position: fixed !important;
    inset: 0 !important;
    z-index: 100 !important;
    pointer-events: none !important;
    background: radial-gradient(66% 72% at 100% 0, var(--sv31-halo), transparent 62%) !important;
    opacity: 1 !important;
}

/* ---------- Arborescence ---------- */

body[data-role] .sidebar {
    display: flex; flex-direction: column;
    height: 100vh !important;
    width: var(--sv31-sw) !important;
    flex: 0 0 var(--sv31-sw) !important;
    background: var(--sv31-sidebar) !important;
    border-right: 1px solid var(--sv31-b);
    z-index: 50;                /* le panneau flottant du rail doit couvrir le contenu */
    overflow: visible;
}
body[data-role] .sidebar::before,
body[data-role] .sidebar::after { display: none !important; }
body[data-role][data-nav="compact"] .sidebar {
    width: var(--sv31-rail) !important;
    flex: 0 0 var(--sv31-rail) !important;
    overflow: visible !important;      /* le panneau flottant doit déborder */
}

/* Cartouche de marque : le bloc blanc contient le logo ET la mention. */
body[data-role] .sidebar .logo {
    display: block !important;
    min-height: 0 !important;
    padding: 14px 16px 12px !important;
    background: #fff !important;
    border-bottom: 1px solid var(--sv31-b) !important;
}
body[data-role] .brand-logo {
    width: 100% !important;
    height: auto !important;
    filter: none !important;
}
body[data-role] .brand-copy {
    display: block !important;
    margin-top: 11px !important;
    text-align: center;
    white-space: nowrap;
}
body[data-role] .brand-copy b,
body[data-role] .brand-copy small {
    display: inline !important;
    color: #0b57a4 !important;
    font-size: 8px !important;
    font-weight: 700 !important;
    letter-spacing: .12em !important;
}
body[data-role] .brand-copy b::after { content: "\00a0\00a0"; }
.sv31-monogram {
    display: none;
    color: #0b57a4;
    font-size: 15px;
    font-weight: 800;
    letter-spacing: -.02em;
    text-align: center;
}
body[data-role][data-nav="compact"] .sidebar .logo { padding: 14px 0 !important; }
body[data-role][data-nav="compact"] .brand-logo,
body[data-role][data-nav="compact"] .brand-copy { display: none !important; }
body[data-nav="compact"] .sv31-monogram { display: block; }

body[data-role] .nav { flex: 1; min-height: 0; padding: 12px 12px 18px !important; overflow: auto; }
body[data-role] .sidebar .foot { flex: none; min-height: 0 !important; }
/* Interligne : c'est la HAUTEUR DE LIGNE qui fait l'aération du menu, pas le
   padding — déjà aligné sur la maquette. Trois couches de v28.css empilent un
   `min-height` sur l'entrée (44, puis 46, puis 48 px en admin) et une marge de
   4 px ; sv31 ne les reprenait pas, et une entrée faisait 48 px au lieu des
   37 px de la maquette. On les remet à zéro et on fixe `line-height`, sinon
   l'entrée hérite de la hauteur de ligne du corps (24 px pour 13 px de texte). */
body[data-role] .nav a,
body[data-role] .nav-acc-summary {
    display: flex; align-items: center; gap: 12px;
    min-height: 0 !important;
    margin: 0 !important;
    padding: 9px 12px !important;
    border-radius: 8px !important;
    color: var(--sv31-ink2) !important;
    font-size: 13px !important;
    line-height: 1.3 !important;
    font-weight: 500 !important;
    background: none !important;
    transition: background .14s ease, color .14s ease;
}
body[data-role] .nav a::before,
body[data-role] .nav a::after { display: none !important; }
body[data-role] .nav a:hover,
body[data-role] .nav-acc-summary:hover { background: var(--sv31-hover) !important; color: var(--sv31-ink) !important; }
body[data-role] .nav a.active {
    position: relative;
    background: var(--sv31-hover) !important;
    color: var(--sv31-ink) !important;
    font-weight: 700 !important;
}
/* Repère de l'écran courant : un filet d'accent, dans l'entrée. */
body[data-role] .nav a.active::before {
    content: "" !important;
    display: block !important;
    position: absolute; left: 0; top: 7px; bottom: 7px;
    width: 3px; border-radius: 2px;
    background: var(--sv31-c) !important;
}
body[data-role] .nav a .ic,
body[data-role] .nav a svg { width: 19px !important; height: 19px !important; flex: none; opacity: .92; }
/* Le compteur ne doit pas commander la hauteur de l'entrée : v28.css lui donne
   une boîte de 24 px, soit 6 px de plus que la ligne de texte. */
body[data-role] .nav .badge {
    margin-left: auto;
    height: auto !important; min-width: 0 !important;
    padding: 1px 7px !important;
    font-size: 10.5px !important;
    line-height: 1.3 !important;
}
body[data-role] .nav a > span:not(.badge) { flex: 1; min-width: 0; text-align: left; }
/* L'entrée de premier niveau porte seule une respiration, comme dans la maquette. */
body[data-role] .nav > a { margin-bottom: 6px !important; font-weight: 600 !important; }

body[data-role] .nav-acc { margin: 3px 0; position: relative; }
body[data-role] .nav-acc-summary { cursor: pointer; font-weight: 600 !important; font-size: 12.5px !important; list-style: none; }
body[data-role] .nav-acc-summary::-webkit-details-marker { display: none; }
body[data-role] .nav-acc-label { flex: 1; min-width: 0; letter-spacing: 0 !important; text-transform: none !important; font-size: 12.5px !important; }
body[data-role] .nav-acc-chevron { width: 14px !important; height: 14px !important; opacity: .6; }
body[data-role] .nav-acc[open] > .nav-acc-summary { color: var(--sv31-ink) !important; }
.sv31-gi { width: 19px; height: 19px; flex: none; }
body[data-role] .nav-acc-panel {
    padding: 2px 0 6px !important;
    margin: 2px 0 0 21px !important;
    border-left: 1px solid var(--sv31-b2);
}
body[data-role] .nav-acc-panel a { padding: 8px 12px !important; font-size: 12.5px !important; margin: 1px 0 !important; }
/* Une sous-entrée est d'un cran en dessous : son icône aussi (maquette). */
body[data-role] .nav-acc-panel a .ic,
body[data-role] .nav-acc-panel a svg { width: 17px !important; height: 17px !important; opacity: .8; }
.nav-acc-panel-head { display: none; }

/* Rail : seules les icônes de groupe subsistent ; le sous-menu s'ouvre à droite. */
body[data-role][data-nav="compact"] .nav { overflow: visible !important; padding: 12px 10px !important; }
body[data-role][data-nav="compact"] .nav > a > span:not(.badge),
body[data-role][data-nav="compact"] .nav-acc-label,
body[data-role][data-nav="compact"] .nav-acc-chevron,
body[data-role][data-nav="compact"] .nav .grp { display: none !important; }
body[data-role][data-nav="compact"] .nav > a,
body[data-role][data-nav="compact"] .nav-acc-summary { justify-content: center; padding: 12px 0 !important; }
body[data-role][data-nav="compact"] .nav-acc[open] > .nav-acc-summary { background: var(--sv31-hover) !important; }
body[data-role][data-nav="compact"] .nav-acc[open] .nav-acc-panel {
    position: absolute; left: calc(100% + 8px); top: -6px; z-index: 70; width: 226px;
    margin: 0 !important; padding: 6px !important;
    border: 1px solid var(--sv31-b); border-radius: 10px;
    background: var(--sv31-s1);
    box-shadow: 0 6px 24px rgba(0, 0, 0, .3);
}
body[data-role][data-theme="day"][data-nav="compact"] .nav-acc[open] .nav-acc-panel {
    box-shadow: 0 6px 24px rgba(21, 48, 74, .14);
}
body[data-nav="compact"] .nav-acc-panel-head {
    display: block;
    padding: 6px 10px 8px;
    margin-bottom: 2px;
    border-bottom: 1px solid var(--sv31-b2);
    color: var(--sv31-muted);
    font-size: 9.5px;
    font-weight: 700;
    letter-spacing: .12em;
    text-transform: uppercase;
}

body[data-role] .sidebar .foot {
    display: flex; align-items: center; gap: 10px;
    padding: 11px 12px !important;
    border-top: 1px solid var(--sv31-b2) !important;
    background: none !important;
}
.sv31-nav-hint { min-width: 0; color: var(--sv31-muted); font-size: 10.5px; line-height: 1.35; }
body[data-role][data-nav="compact"] .sidebar .foot { justify-content: center; padding: 11px 0 !important; }
body[data-nav="compact"] .sv31-nav-hint { display: none; }
.sv31-icon-btn {
    display: grid; place-items: center;
    width: 32px; height: 32px; flex: none;
    border: 1px solid var(--sv31-b); border-radius: 8px;
    background: var(--sv31-s1); color: var(--sv31-ink2);
    cursor: pointer; transition: .14s ease;
}
.sv31-icon-btn:hover { background: var(--sv31-hover); color: var(--sv31-ink); }
.sv31-icon-btn svg { width: 16px; height: 16px; }

/* ---------- Zone ③ : ambiance, identité, déconnexion ---------- */

.main { position: relative; }
.sv31-identity {
    position: absolute; top: 12px; right: 20px; z-index: 40;
    display: flex; align-items: center; gap: 6px;
}
/* Les en-têtes d'écran occupent la première ligne du contenu : on leur réserve
   la gouttière, sinon le titre passerait sous l'identité. */
body[data-role] .v19-page-head,
body[data-role] .sv28-detail-head { padding-right: 320px; }

.sv31-themes { display: flex; align-items: center; gap: 2px; height: 38px; }
.sv31-themes button {
    display: grid; place-items: center;
    width: 30px; height: 30px;
    border: 0; border-radius: 7px;
    background: none; color: var(--sv31-muted);
    cursor: pointer; transition: .14s ease;
}
.sv31-themes button:hover { color: var(--sv31-ink); background: var(--sv31-hover); }
.sv31-themes button.on { color: var(--sv31-ink); background: var(--sv31-hover); }
.sv31-themes button svg { width: 16px; height: 16px; }
.sv31-themes button:focus-visible { outline: 2px solid var(--sv31-c); outline-offset: 1px; }

.sv31-me { position: relative; }
.sv31-me > summary {
    display: flex; align-items: center; gap: 10px;
    height: 38px; padding: 0 10px 0 4px;
    border: 1px solid var(--sv31-b); border-radius: 8px;
    background: var(--sv31-s1);
    list-style: none; cursor: pointer; transition: .14s ease;
}
.sv31-me > summary::-webkit-details-marker { display: none; }
.sv31-me > summary:hover,
.sv31-me[open] > summary { background: var(--sv31-hover); }
.sv31-av {
    display: grid; place-items: center;
    width: 28px; height: 28px; flex: none;
    border-radius: 6px;
    background: var(--sv31-c); color: #fff;
    font-size: 11px; font-weight: 700;
}
.sv31-who { min-width: 0; line-height: 1.25; }
.sv31-who b { display: block; color: var(--sv31-ink); font-size: 12px; font-weight: 700; white-space: nowrap; }
.sv31-who small { display: block; color: var(--sv31-muted); font-size: 10.5px; white-space: nowrap; }
.sv31-caret { width: 14px; height: 14px; flex: none; color: var(--sv31-muted); transition: transform .16s ease; }
.sv31-me[open] .sv31-caret { transform: rotate(180deg); }
.sv31-me-menu {
    position: absolute; right: 0; top: calc(100% + 6px); z-index: 80;
    min-width: 212px; padding: 6px;
    border: 1px solid var(--sv31-b); border-radius: 9px;
    background: var(--sv31-s1);
    box-shadow: 0 6px 24px rgba(0, 0, 0, .3);
}
body[data-theme="day"] .sv31-me-menu { box-shadow: 0 6px 24px rgba(21, 48, 74, .14); }
.sv31-me-head { padding: 8px 10px 9px; margin-bottom: 4px; border-bottom: 1px solid var(--sv31-b2); }
.sv31-me-head b { display: block; color: var(--sv31-ink); font-size: 12.5px; font-weight: 700; }
.sv31-me-head small { display: block; margin-top: 2px; color: var(--sv31-muted); font-size: 11px; }
.sv31-me-menu button {
    display: flex; align-items: center; gap: 10px;
    width: 100%; padding: 9px 10px;
    border: 0; border-radius: 7px;
    background: none; color: var(--sv31-ink2);
    font: inherit; font-size: 12.5px; font-weight: 600; text-align: left; cursor: pointer;
}
.sv31-me-menu button:hover { background: var(--sv31-hover); color: var(--sv31-ink); }
.sv31-me-menu svg { width: 15px; height: 15px; flex: none; }

/* ---------- En-tête d'écran : icône nue, titre, onglets sans conteneur ---------- */

/* L'en-tête d'écran est la première ligne du contenu : les 32 px de marge haute
   de `.content` l'enfonçaient sous le bloc d'identité, qui lui est calé à 12 px
   du haut. Les deux doivent partager la même ligne — c'est tout le propos de la
   zone ① de la maquette. On retire la marge là, et seulement là, où le contenu
   s'ouvre sur un en-tête (v28.css fait déjà ce retrait en dessous de 1100 px). */
body[data-role] .content:has(> .sv28-page),
body[data-role] .content:has(> .v19-page-head),
body[data-role] .content:has(> .sv24-viewbar) { padding-top: 0 !important; }

/* Compacité : la maquette tient l'en-tête en 113 px — 13 px de marge haute, une
   ligne de 38 px (alignée sur le bloc d'identité, qui fait 38 px lui aussi),
   12 px de gouttière, la ligne d'onglets, 12 px de marge basse. v28.css fixait
   `min-height:132px` sur l'identité d'objet et 62 px sur le rail d'onglets —
   deux reliques de l'en-tête monumental d'avant sv31, qui portaient la coque à
   238 px. On les remet à zéro et c'est le contenu qui donne la hauteur. */
body[data-role] .sv28-detail-head,
body[data-role] .v19-page-head {
    background: none !important;
    border: 0 !important;
    box-shadow: none !important;
    min-height: 0 !important;
    padding-top: 13px !important;
    padding-bottom: 12px !important;
}
body[data-role] .sv28-detail-head {
    /* 82 px de colonne d'icône pour une icône de 28 px : la colonne suit l'icône. */
    grid-template-columns: auto minmax(0, 1fr) auto !important;
    gap: 12px !important;
}
/* La ligne d'identité vaut 38 px, comme le bloc utilisateur d'en face : sans ce
   plancher, un titre sans sous-titre remonterait de 7 px et les deux ne
   seraient plus sur la même ligne optique. */
body[data-role] .sv28-head-copy {
    display: flex; flex-direction: column; justify-content: center;
    min-height: 38px;
}
body[data-role] .sv28-subtitle { margin: 2px 0 0 !important; font-size: 11px !important; }
body[data-role] .sv28-detail-head::before,
body[data-role] .sv28-detail-head::after,
body[data-role] .v19-page-head::before,
body[data-role] .v19-page-head::after { display: none !important; }
body[data-role] .sv28-object-icon {
    width: 28px !important; height: 28px !important;
    border: 0 !important;
    background: none !important;
    box-shadow: none !important;
    color: var(--sv31-c) !important;
}
body[data-role] .sv28-object-icon svg { width: 22px !important; height: 22px !important; }
body[data-role] .sv28-title,
body[data-role] .v19-page-head h1 {
    font-size: 19px !important;
    font-weight: 700 !important;
    letter-spacing: -.01em !important;
    text-transform: none !important;
    color: var(--sv31-ink) !important;
}
body[data-role] .sv28-breadcrumb { font-size: 11px !important; color: var(--sv31-muted) !important; }

/* Rail d'onglets : segmented control compact façon maquette (`.ep-mode`).
   Un conteneur discret, des segments courts et lisibles ; l'onglet retenu porte
   un fond posé. La navigation reste serveur (liens + `aria-current`). */
body[data-role] .sv28-tabsline {
    background: none !important;
    border: 0 !important;
    box-shadow: none !important;
    min-height: 0 !important;
    align-items: center !important;
    padding: 0 0 12px !important;
}
body[data-role] .sv28-tabs {
    display: inline-flex !important;
    align-items: center !important;
    width: max-content !important;
    max-width: 100% !important;
    gap: 2px !important;
    padding: 2px !important;
    border: 1px solid var(--sv31-b2) !important;
    border-radius: 10px !important;
    background: var(--sv31-well) !important;
    box-shadow: none !important;
}
body[data-role] .sv28-tab {
    min-height: 0 !important;
    height: 26px !important;
    padding: 0 12px !important;
    gap: 6px !important;
    border: 0 !important;
    border-radius: 8px !important;
    background: none !important;
    color: var(--sv31-muted) !important;
    font-size: 12.5px !important;
    font-weight: 500 !important;
    letter-spacing: 0 !important;
    text-transform: none !important;
    box-shadow: none !important;
    transition: background .12s linear, color .12s linear !important;
}
body[data-role] .sv28-tab::before,
body[data-role] .sv28-tab::after { display: none !important; }
body[data-role] .sv28-tab:hover { color: var(--sv31-ink2) !important; background: var(--sv31-hover) !important; }
body[data-role] .sv28-tab.on {
    border: 0 !important;
    background: var(--sv31-s2) !important;
    color: var(--sv31-ink) !important;
    font-weight: 600 !important;
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .05), var(--sv31-sh) !important;
}
body[data-role] .sv28-tab-count {
    height: auto !important; min-width: 0 !important;
    padding: 1px 7px !important;
    border-radius: 999px !important;
    background: var(--sv31-b2) !important;
    color: var(--sv31-muted) !important;
    font-size: 10.5px !important;
    line-height: 1.45 !important;
}
body[data-role] .sv28-tab.on .sv28-tab-count { background: var(--sv31-c) !important; color: #fff !important; }
/* Zone ④ : l'action de l'onglet. Hauteur de la maquette (34 px), sur ce rail
   seulement — ailleurs `.sv28-action` garde ses 42 px. */
body[data-role] .sv28-action-host { align-items: center !important; padding: 0 0 0 12px !important; }
body[data-role] .sv28-tabsline .sv28-action { height: 34px !important; }

/* ---------- Surfaces : une valeur au-dessus du fond, en léger dégradé ---------- */

body[data-role] :is(.card, .v19-entity-card, .v20-premium-card, .v21-entity-card,
                    .sv29-card, .admin-panel, .v4-panel, .v15-section, .v16-info-section,
                    .sv28-info-wrap, .sv28-data-list, .modal) {
    border: 1px solid var(--sv31-b) !important;
    border-radius: 10px !important;
    background: linear-gradient(170deg, var(--sv31-s2), var(--sv31-s1) 58%, var(--sv31-s0)) !important;
    box-shadow: var(--sv31-sh) !important;
    color: var(--sv31-ink) !important;
}
body[data-role] :is(.card, .v19-entity-card, .v20-premium-card, .v21-entity-card,
                    .sv29-card, .admin-panel, .v4-panel)::before,
body[data-role] :is(.card, .v19-entity-card, .v20-premium-card, .v21-entity-card,
                    .sv29-card, .admin-panel, .v4-panel)::after { display: none !important; }

@media (max-width: 1280px) { :root { --sv31-sw: 220px; } }
@media (max-width: 1100px) {
    .sv31-identity { position: static; justify-content: flex-end; padding: 10px 16px 0; }
    body[data-role] .v19-page-head,
    body[data-role] .sv28-detail-head { padding-right: 20px; }
    .sv31-who { display: none; }
}

/* Identité sur une ligne : « Type : libellé », le type en gris, le libellé en gras. */
body[data-role] .sv28-title { display: flex; align-items: baseline; gap: 9px; min-width: 0; }
.sv31-vtype { color: var(--sv31-muted); font-weight: 500; }
.sv31-vtype::after { content: " : "; color: var(--sv31-muted); }
.sv31-vcode {
    flex: none;
    padding: 2px 8px;
    border: 1px solid var(--sv31-b);
    border-radius: 5px;
    background: var(--sv31-well);
    color: var(--sv31-muted);
    font-family: var(--sv31-mono);
    font-size: 11px;
    font-weight: 600;
}
@media (max-width: 860px) { .sv31-vtype, .sv31-vcode { display: none; } }

/* Le sélecteur de langue et la déconnexion partagent l'allure des entrées du menu. */
.sv31-me-menu .sv31-lang { margin: 0; }
.sv31-me-menu .sv31-lang button { color: var(--sv31-ink2); }
.sv31-me-menu .sv31-lang button.on { color: var(--sv31-ink); }
.sv31-me-menu form + form { border-top: 1px solid var(--sv31-b2); margin-top: 4px; padding-top: 4px; }

/* Lot 2 — fiche famille : bascule d'affichage et Paramétrage à deux colonnes. */
.sv28-viewswitch { display: inline-flex; gap: 4px; margin: 0 0 12px; }
.sv28-view {
    padding: 4px 10px;
    border: 1px solid var(--line);
    border-radius: 6px;
    color: var(--ink);
    text-decoration: none;
    font-size: 12px;
}
.sv28-view.on { border-color: var(--accent); color: var(--accent); font-weight: 600; }

/* EV-08 : `.sv28-view` habille aussi un <span> inerte (segment courant) et un
 * <button> (segment de mutation, `design/toggle.html.twig` macro `switch`) —
 * sans ces deux règles, le bouton garde le chrome par défaut du navigateur et
 * le span perd le padding (inline sans boîte). `.sv28-toggle-form` rend le
 * <form> invisible à la mise en page flex (patron déjà en place pour
 * `.sv28-action-host form`) : le bouton devient un segment au même niveau
 * que le span d'état, pas un bloc à part. */
.sv28-toggle-form { display: contents; }
button.sv28-view { appearance: none; background: none; cursor: pointer; font-family: inherit; margin: 0; }

/* ---------- Formulaires : deux colonnes, bascule, listes déroulantes ---------- */

/* Corps en deux colonnes (popin de CRÉATION et page d'ÉDITION). */
.sv31-form-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 0 20px; }
@media (max-width: 640px) { .sv31-form-cols { grid-template-columns: 1fr; } }
.sv31-form-col { min-width: 0; }
.sv31-form-full { margin-top: 4px; }
.sv31-form-page { max-width: 780px; }
.sv31-form-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 12px; }

/* Bascule Actif / désactivé (case à cocher stylée) dans un formulaire. */
.sv31-switch { display: inline-flex; align-items: center; gap: 10px; cursor: pointer; }
.sv31-switch input[type="checkbox"] {
    appearance: none; -webkit-appearance: none;
    width: 40px; height: 22px; margin: 0; flex: none;
    border-radius: 999px; background: var(--sv31-b2);
    border: 1px solid var(--sv31-b);
    position: relative; cursor: pointer; transition: background .15s linear;
}
.sv31-switch input[type="checkbox"]::after {
    content: ""; position: absolute; top: 2px; left: 2px;
    width: 16px; height: 16px; border-radius: 50%;
    background: #fff; box-shadow: 0 1px 2px rgba(0, 0, 0, .3);
    transition: transform .15s linear;
}
.sv31-switch input[type="checkbox"]:checked { background: var(--sv31-ok); border-color: var(--sv31-ok); }
.sv31-switch input[type="checkbox"]:checked::after { transform: translateX(18px); }
/* « Inactif » se lit ROUGE partout, y compris sur la bascule de formulaire :
   la même couleur porte le même sens, quel que soit le composant. */
.sv31-switch input[type="checkbox"]:not(:checked) { background: var(--sv31-err); border-color: var(--sv31-err); }
.sv31-switch-label { color: var(--sv31-ink2); font-size: 13px; }

/* Listes déroulantes : les options s'affichaient en blanc sur fond blanc
   (constaté dans les popins). On force des options lisibles, quel que soit le thème. */
select option { color: #0f1b26 !important; background-color: #ffffff !important; }
span.sv28-view { display: inline-block; }
.sv28-params-grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.6fr);
    gap: 20px;
    align-items: start;
}
.sv28-params-col { min-width: 0; }
.sv28-params-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.sv28-params-title {
    margin: 0 0 10px;
    color: var(--muted);
    font-size: 13px;
    letter-spacing: .04em;
    text-transform: uppercase;
}
@media (max-width: 900px) { .sv28-params-grid { grid-template-columns: 1fr; } }

/* T60 — classes sémantiques qui n'avaient aucune règle : on leur rend leur rôle,
   au lieu de les laisser orphelines (une classe sans règle ne garde rien). */
.sv28-num { text-align: right; font-variant-numeric: tabular-nums; }
.sv28-doc-actions { margin-bottom: 12px; }
.sv28-filter-error {
    margin: 0 0 12px;
    padding: 8px 12px;
    border: 1px solid var(--err);
    border-radius: 6px;
    color: var(--err);
    font-weight: 600;
}
.sv28-error { max-width: 560px; margin: 56px auto; padding: 0 16px; text-align: center; }
.sv28-error h1 { margin: 0 0 8px; }
.sv28-group-title {
    margin: 18px 0 10px;
    color: var(--muted);
    font-size: 13px;
    font-weight: 600;
    letter-spacing: .04em;
    text-transform: uppercase;
}
.sv28-machine-group, .sv28-machine-section { margin: 0 0 18px; }
.sv28-inline-edit { display: flex; gap: 8px; align-items: center; }
.sv28-inline-input { flex: 1 1 auto; min-width: 0; }
.sv28-sources { margin: 8px 0 0; padding: 0; list-style: none; }
.sv28-sources li { font-size: 12px; }

/* Revue documentaire, formulaires d'en-tête et bannière : classes qui n'avaient
   aucune règle (constat T60). */
.field-invalid { border-color: var(--err); }
.field-invalid label { color: var(--err); }
.field-warning {
    margin: 0 0 10px;
    padding: 8px 12px;
    border: 1px solid var(--warn);
    border-radius: 6px;
    color: var(--warn);
}
.review-unit { padding: 12px 0; border-bottom: 1px solid var(--line); }
.review-unit h3 { margin: 0 0 4px; }
.review-source { margin: 0; color: var(--muted); font-size: 12px; }
.review-provenance { margin: 8px 0; padding-left: 18px; }
.review-provenance small { color: var(--muted); }
.review-proof { display: block; max-width: 240px; margin: 6px 0; border: 1px solid var(--line); border-radius: 6px; }
.review-proof-unavailable { margin: 6px 0; color: var(--muted); font-size: 12px; font-style: italic; }
.review-pagination { display: flex; gap: 12px; align-items: center; margin-top: 16px; }
.theme-form, .sv31-nav-form { display: inline; margin: 0; }
.ring-outer { fill: none; stroke: var(--line); }

/* ===========================================================================
 * COUCHE sv31 — partiels deux colonnes (V1)
 *
 * Source : docs/maquettes/2026-10-08-{documentation-machine,vue-inspection-question,
 * assistant-deux-colonnes}/ (état du 2026-10-08).
 *
 * Styles des blocs mutualisés V1 : cadre deux colonnes, colonne de lecture,
 * groupes de résultats, lignes de passage, barre de filtres, pastilles, mesures
 * et outils. Consommés par les écrans V3 (Documentation), V4 (Support) et V5
 * (Inspection) — ajoutés ici, jamais réécrits par un écran.
 * ------------------------------------------------------------------------- */

.sv31-duo { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.2fr); gap: 14px;
    /* Les deux colonnes tiennent la MÊME hauteur et défilent séparément : on ne
       perd jamais le document de vue en parcourant les passages, ni l'inverse.
       `align-items: start` les laissait se rétracter chacune sur son contenu. */
    align-items: stretch; min-height: min(62vh, 680px); }
.sv31-duo-gauche, .sv31-duo-droite { min-width: 0; display: flex; flex-direction: column; }
@media (max-width: 1000px) { .sv31-duo { grid-template-columns: 1fr; } }

/* `max-height` comme la colonne de gauche, et c'est ce qui FAIT défiler la
   colonne de lecture séparément : sans plafond, la ligne de la grille prenait la
   hauteur du document, le cadre ne coupait rien et c'était la PAGE ENTIÈRE qui
   défilait — l'`overflow: auto` de la frame ne servait à rien, et une source
   citée ne pouvait pas être amenée sous les yeux. */
.sv31-doc-pane { border: 1px solid var(--sv31-b); border-radius: 10px; background: linear-gradient(170deg, var(--sv31-s2), var(--sv31-s1) 58%, var(--sv31-s0)); box-shadow: var(--sv31-sh); min-height: 180px; max-height: calc(100vh - 220px); flex: 1; display: flex; flex-direction: column; overflow: hidden; }
.sv31-doc-pane > turbo-frame { flex: 1; min-height: 0; overflow: auto; display: block; }
.sv31-doc-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; padding: 10px 14px; border-bottom: 1px solid var(--sv31-b2); }
.sv31-doc-head b { color: var(--sv31-ink); font-size: 13px; }
.sv31-doc-head small { color: var(--sv31-muted); font-size: 11px; }
.sv31-doc-body { padding: 14px; color: var(--sv31-ink2); line-height: var(--line-height-base); }
.sv31-doc-body mark { background: color-mix(in srgb, var(--sv31-c) 30%, transparent); color: var(--sv31-ink); border-radius: 3px; padding: 0 2px; }

/* `.bubble` (v28.css) pose `white-space: pre-wrap`, pensé pour l'ancien texte
 * brut. Le contenu n'est plus jamais du texte brut non balisé (Markdown rendu,
 * ou un statut court sans saut de ligne) : `normal` lève l'héritage à la racine,
 * `.md-content` le répète explicitement ci-dessous pour rester correct même
 * réutilisée hors de `.bubble` (colonne de lecture). */
.msg .bubble { white-space: normal; }

/* ---------------------------------------------------------------------------
 * `.md-content` — habillage Markdown PARTAGÉ entre la bulle de chat
 * (`assistant/_messages.html.twig`) et le document ouvert dans la colonne de
 * lecture (`design/doc_pane.html.twig`). UNE seule feuille de règles : c'est
 * l'exigence EV-02 du dossier `2026-10-10-refonte-chat-support-design-system`
 * — sans elle, les deux rendus Markdown (même moteur CommonMark) auraient
 * deux apparences différentes.
 * ------------------------------------------------------------------------- */
/* ⚠️ `white-space: normal` est ESSENTIEL ici, pas cosmétique. `.bubble` (v28.css,
 * GÉNÉRÉ) pose `white-space: pre-wrap` — hérité par tout son contenu, pensé pour
 * l'ancien texte brut (un seul bloc, sans balisage). Avec le rendu Markdown
 * structuré (titres, listes, paragraphes), `pre-wrap` rend VISIBLES les retours à
 * la ligne du HTML lui-même (indentation Twig, saut de ligne que CommonMark pose
 * entre deux `<li>`) EN PLUS des marges `.md-content` ci-dessous : chaque point de
 * liste se retrouvait séparé par un vide bien plus grand que la marge réglée.
 * Signalé le 2026-10-10 (capture d'écran du porteur). */
.md-content { white-space: normal; line-height: var(--line-height-base); }
.md-content > :first-child { margin-top: 0; }
.md-content > :last-child { margin-bottom: 0; }
.md-content p { margin: 0 0 var(--space-3); }
.md-content h1, .md-content h2, .md-content h3,
.md-content h4, .md-content h5, .md-content h6 { margin: var(--space-4) 0 var(--space-2); line-height: var(--line-height-tight); color: var(--sv31-ink); }
.md-content h1 { font-size: 1.3em; }
.md-content h2 { font-size: 1.18em; }
.md-content h3 { font-size: 1.08em; }
.md-content ul, .md-content ol { margin: 0 0 var(--space-3); padding-left: 1.4em; }
.md-content li { margin: var(--space-1) 0 0; }
.md-content li:first-child { margin-top: 0; }
.md-content li > ul, .md-content li > ol { margin: var(--space-1) 0 0; }
.md-content strong { font-weight: 600; }
.md-content blockquote { margin: 0 0 var(--space-3); padding: var(--space-1) var(--space-3); border-left: 3px solid var(--sv31-c); color: var(--sv31-muted); }
.md-content code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 0.92em; background: color-mix(in srgb, var(--sv31-ink) 6%, transparent); border-radius: 4px; padding: 0.15em 0.4em; }
.md-content pre { margin: 0 0 var(--space-3); padding: var(--space-2) var(--space-3); border-radius: 8px; background: color-mix(in srgb, var(--sv31-ink) 6%, transparent); overflow-x: auto; }
.md-content pre code { background: none; padding: 0; }
.md-content a { color: var(--sv31-c); text-decoration: underline; }

/* Avatar de l'assistant (`.msg .ava`, 30×30px en v28.css) : le monogramme réel
 * (73×122px, extrait du logotype) y remplace le texte « SV ». `.ava-logo` est
 * le nom de classe passé à la macro `icons.monogramme()` — elle ne redéfinit
 * jamais `.sv-monogramme`, utilisée ailleurs (modale plan) avec sa propre
 * taille. */
.msg .ava { position: relative; overflow: hidden; }
.msg .ava .ava-logo { width: 16px; height: 27px; }

/* Avatar « parlant » (EV-05) : signal discret posé par le SERVEUR pendant
 * qu'une réponse est en cours de génération (`message.statut == 'pending'`),
 * jamais par un script. Aucun second asset (sprite de bouche) n'existe :
 * une pulsation du monogramme suffit à signifier « Sedam répond ». */
@keyframes sv31-avatar-talking { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.15); } }
.msg .ava.talking .ava-logo { animation: sv31-avatar-talking 1.1s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) {
    .msg .ava.talking .ava-logo { animation: none; }
}
/* Vue PDF : la visionneuse native du navigateur emplit la colonne de lecture.
   Le cadre est neutre, et le lien de repli reste visible juste dessous. */
.sv31-doc-pdf { padding: 0; }
.sv31-doc-pdf-frame { width: 100%; height: 70vh; border: 0; display: block; background: var(--sv31-well); }
/* Numéro de citation S1, S2… : discret, mais repère stable entre le texte et la
   liste des sources. */
.sv31-src-n { font-family: var(--sv31-mono); font-size: 10.5px; font-weight: 700; color: var(--sv31-muted); margin-right: 4px; }
.sv31-doc-meta { margin: 0; padding: 12px 14px; }
.sv31-doc-meta div { display: flex; justify-content: space-between; padding: 4px 0; border-bottom: 1px solid var(--sv31-b2); }
.sv31-doc-meta dt { color: var(--sv31-muted); }
.sv31-doc-meta dd { margin: 0; color: var(--sv31-ink); }
.sv31-doc-empty { padding: 24px 14px; color: var(--sv31-muted); text-align: center; }

/* En-tête de la colonne de lecture : identité, bascule Document/Métadonnées,
   outils et fermeture. La bascule est un groupe de liens de frame — la vue
   courante porte `aria-current="page"`, et c'est ce qui la met en évidence. */
.sv31-doc-id { display: inline-flex; align-items: baseline; gap: 8px; min-width: 0; }
.sv31-doc-actions { display: inline-flex; align-items: center; gap: 6px; }
.sv31-doc-close { font-weight: 700; padding: 2px 9px; }
.sv31-doc-vue { display: inline-flex; gap: 2px; border: 1px solid var(--sv31-b); border-radius: 6px; overflow: hidden; }
.sv31-doc-vue-btn { font-size: 11px; padding: 2px 8px; color: var(--sv31-muted); text-decoration: none; }
.sv31-doc-vue-btn:hover { background: var(--sv31-hover); color: var(--sv31-ink); }
.sv31-doc-vue-btn[aria-current="page"] { background: var(--sv31-c); color: #fff; }
.sv31-doc-etiquette { margin: 0; padding: 8px 14px 0; font-family: var(--sv31-mono); font-size: 10px;
    text-transform: uppercase; letter-spacing: .04em; color: var(--sv31-muted); }

.sv31-result-group { border: 1px solid var(--sv31-b2); border-radius: 8px; margin-bottom: 10px; overflow: hidden; }
.sv31-rg-head { display: flex; align-items: center; gap: 8px; padding: 8px 12px; cursor: pointer; background: var(--sv31-well); color: var(--sv31-ink2); }
.sv31-rg-scope { font-size: 11px; color: var(--sv31-muted); }
.sv31-rg-title { font-weight: 600; color: var(--sv31-ink); }
.sv31-rg-count { margin-left: auto; font-size: 11px; color: var(--sv31-muted); }
.sv31-rg-rows { padding: 4px 8px; }

.sv31-passage-row { display: block; padding: 8px 10px; border-radius: 6px; text-decoration: none; color: var(--sv31-ink2); }
.sv31-passage-row:hover { background: var(--sv31-hover); }
.sv31-passage-row.disparu { opacity: .55; }
.sv31-pr-head { display: flex; align-items: center; gap: 6px; }
.sv31-pr-head b { color: var(--sv31-ink); font-size: 12.5px; }
.sv31-pr-mark { font-style: normal; font-size: 10px; padding: 1px 6px; border-radius: 999px; border: 1px solid var(--sv31-b); color: var(--sv31-muted); }
.sv31-pr-mark.mot { color: var(--sv31-ok); border-color: color-mix(in srgb, var(--sv31-ok) 50%, transparent); }
.sv31-pr-mark.approchant { color: var(--sv31-warn); border-color: color-mix(in srgb, var(--sv31-warn) 50%, transparent); }
.sv31-pr-mark.disparu { color: var(--sv31-err); border-color: color-mix(in srgb, var(--sv31-err) 50%, transparent); }
.sv31-pr-excerpt { display: block; margin-top: 3px; font-size: 12px; color: var(--sv31-muted); }
.sv31-pr-score { display: block; margin-top: 3px; font-family: var(--sv31-mono); font-size: 11px; color: var(--sv31-muted); }

.sv31-filter-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; padding: 8px 0; }
.sv31-filter select { background: var(--sv31-well); color: var(--sv31-ink2); border: 1px solid var(--sv31-b); border-radius: 6px; padding: 4px 8px; }
.sv31-filter-submit { background: var(--sv31-c); color: #fff; border: 0; border-radius: 6px; padding: 5px 12px; cursor: pointer; }
.sv31-filter-count { margin-left: auto; font-size: 12px; color: var(--sv31-muted); }

.sv31-pastille { display: inline-flex; align-items: center; gap: 4px; padding: 1px 8px; border-radius: 999px; border: 1px solid var(--sv31-b); font-size: 11px; color: var(--sv31-muted); }
.sv31-pastille.ok, .sv31-pastille.mot { color: var(--sv31-ok); border-color: color-mix(in srgb, var(--sv31-ok) 50%, transparent); }
.sv31-pastille.warn, .sv31-pastille.approchant { color: var(--sv31-warn); border-color: color-mix(in srgb, var(--sv31-warn) 50%, transparent); }
.sv31-pastille.err, .sv31-pastille.disparu { color: var(--sv31-err); border-color: color-mix(in srgb, var(--sv31-err) 50%, transparent); }

.sv31-mesures { display: inline-flex; flex-wrap: wrap; gap: 10px; }
.sv31-mesure { display: inline-flex; flex-direction: column; }
.sv31-mesure small { color: var(--sv31-muted); font-size: 10px; text-transform: uppercase; letter-spacing: .04em; }
.sv31-mesure b { color: var(--sv31-ink); font-size: 12.5px; }

.sv31-col-tools { display: inline-flex; gap: 6px; }
.sv31-col-tool { font-size: 12px; color: var(--sv31-ink2); text-decoration: none; padding: 2px 8px; border: 1px solid var(--sv31-b); border-radius: 6px; }
.sv31-col-tool:hover { background: var(--sv31-hover); color: var(--sv31-ink); }

/* ---------------------------------------------------------------------------
 * sv31 — écran « Documentation d'une machine » (V3)
 *
 * Source : docs/maquettes/2026-10-08-documentation-machine/ (état du 2026-10-08).
 *
 * Ce bloc style ce que l'écran ajoute AUTOUR des partiels communs : le cadre de
 * colonne, son en-tête, la barre de recherche et la liste documentaire. Les
 * blocs mutualisés (duo, doc_pane, result_group, passage_row, filter_bar)
 * gardent leurs règles ci-dessus — aucune n'est redite ici.
 *
 * Les deux colonnes défilent SÉPARÉMENT : on ne perd jamais le document de vue
 * en parcourant les passages, ni l'inverse.
 * ------------------------------------------------------------------------- */

.sv31-col { display: flex; flex-direction: column; flex: 1; min-width: 0; min-height: 0; overflow: hidden;
    border: 1px solid var(--sv31-b); border-radius: 10px;
    background: linear-gradient(170deg, var(--sv31-s2), var(--sv31-s1) 58%, var(--sv31-s0));
    box-shadow: var(--sv31-sh); max-height: calc(100vh - 220px); }
.sv31-col-tete { display: flex; align-items: center; gap: 10px; flex: none; min-height: 46px;
    padding: 7px 12px; border-bottom: 1px solid var(--sv31-b); }
.sv31-col-titre { font-size: 12.5px; font-weight: 700; color: var(--sv31-ink); min-width: 0;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sv31-col-titre em { font-style: normal; font-weight: 500; color: var(--sv31-muted); }
.sv31-col-tete .sv31-mesures { margin-left: auto; flex: none; }
.sv31-col-pied { flex: none; margin: 0; padding: 9px 12px; border-top: 1px solid var(--sv31-b2);
    background: var(--sv31-well); color: var(--sv31-muted); font-size: 11.5px; }
.sv31-col-pied a { color: var(--sv31-c); }

/* Un champ, un bouton : aucun mode à choisir, donc aucun réglage à styler. */
.sv31-rech { flex: none; padding: 12px; border-bottom: 1px solid var(--sv31-b); background: var(--sv31-well); }
.sv31-rech-ligne { display: flex; gap: 8px; align-items: center; }
.sv31-champ { flex: 1 1 240px; min-width: 160px; }
.sv31-champ input { width: 100%; height: 38px; padding: 0 13px; border: 1px solid var(--sv31-b);
    border-radius: 9px; background: var(--sv31-s1); color: var(--sv31-ink); font: inherit; font-size: 13.5px; }
.sv31-champ input::placeholder { color: var(--sv31-muted); }
.sv31-champ input:focus-visible { outline: none; border-color: var(--sv31-c);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--sv31-c) 15%, transparent); }
.sv31-rech-btn { flex: none; height: 38px; padding: 0 16px; border: 0; border-radius: 9px;
    background: var(--sv31-c); color: #fff; font-weight: 600; cursor: pointer; }
.sv31-rech-aide { margin: 8px 0 0; color: var(--sv31-muted); font-size: 11.5px; line-height: 1.5; }
.sv31-rech-aide b { color: var(--sv31-warn); font-weight: 600; }

/* Le jeu de résultats défile seul ; l'en-tête d'un groupe colle en haut. */
.sv31-jeu { flex: 1; min-height: 0; overflow: auto; padding: 8px; }
.sv31-jeu .sv31-rg-head { position: sticky; top: 0; z-index: 2; }
.sv31-col .sv31-filter-bar { flex: none; padding: 9px 12px; background: var(--sv31-well);
    border-bottom: 1px solid var(--sv31-b); }
.sv31-rg-mono { width: 24px; height: 24px; border-radius: 7px; display: grid; place-items: center;
    flex: none; background: var(--sv31-c); color: #fff; font-size: 10px; font-weight: 800; }
.sv31-pr-meta { display: block; margin-top: 2px; color: var(--sv31-muted); font-size: 11px;
    font-family: var(--sv31-mono); }
.sv31-pr-excerpt { quotes: none; }
.sv31-pr-excerpt mark { background: color-mix(in srgb, var(--sv31-c) 28%, transparent);
    color: var(--sv31-ink); border-radius: 3px; padding: 0 2px; }

/* Sans terme saisi, la documentation héritée reste la porte d'entrée. */
.sv31-doc-liste { flex: 1; min-height: 0; overflow: auto; padding: 8px; }
.sv31-doc-item { display: block; padding: 8px 10px; border-radius: 8px; text-decoration: none;
    border: 1px solid transparent; }
.sv31-doc-item:hover { background: var(--sv31-hover); border-color: var(--sv31-b2); }
.sv31-doc-item b { display: block; color: var(--sv31-ink); font-size: 12.5px; }
.sv31-doc-item small { color: var(--sv31-muted); font-size: 11px; }

@media (max-width: 1000px) {
    /* Colonnes empilées : chacune reprend la hauteur de son contenu, et c'est la
       page qui défile (le contrôleur `doc-anchor` suit ce changement de zone). */
    .sv31-col, .sv31-doc-pane { max-height: none; }
    .sv31-duo { min-height: 0; }
}

/* ---------------------------------------------------------------------------
 * sv31 — écran « Support interactif » à deux colonnes (V4)
 *
 * Source : docs/maquettes/2026-10-08-assistant-deux-colonnes/.
 *
 * L'atelier perd sa troisième colonne : l'historique passe dans un `<details>`
 * de l'en-tête, et les documents dans un ACCORDÉON sous l'échange (révision du
 * 2026-10-09 — la colonne de droite est la place du document ouvert, pas celle
 * d'un catalogue). Ce bloc ne style QUE ce que V4 ajoute — le cadre de colonne,
 * le cadre deux colonnes et la colonne de lecture restent ceux du socle.
 * ------------------------------------------------------------------------- */

/* Le fil défile seul, entre l'en-tête et le composeur qui, eux, ne bougent pas. */
.sv31-fil { display: block; flex: 1; min-height: 0; overflow: auto; }
.v4-ai-main .composer { flex: none; }

/* EV-07 : le déclencheur de la modale d'historique (remplace le `<details>`
 * `.sv31-histo` — règles retirées, plus aucun gabarit ne les consomme). */
.sv31-histo-ouvrir { margin-left: auto; flex: none; cursor: pointer; font-size: 12px;
    color: var(--sv31-ink2); border: 1px solid var(--sv31-b); border-radius: 7px;
    padding: 4px 10px; text-decoration: none; }
.sv31-histo-ouvrir:hover { background: var(--sv31-hover); color: var(--sv31-ink); }
.sv31-histo-modal-liste { max-height: 60vh; overflow: auto; }
.sv31-histo-modal-liste a { display: block; padding: 7px 9px; border-radius: 7px; text-decoration: none; }
.sv31-histo-modal-liste a:hover { background: var(--sv31-hover); }
.sv31-histo-modal-liste b { display: block; color: var(--sv31-ink); font-size: 12.5px; }
.sv31-histo-modal-liste small { color: var(--sv31-muted); font-size: 11px; }
.sv31-histo-vide { margin: 0; padding: 8px; color: var(--sv31-muted); font-size: 12px; }

.sv31-doc-liste-tete { margin: 0 0 6px; padding: 2px 10px; color: var(--sv31-muted);
    font-size: 10px; text-transform: uppercase; letter-spacing: .05em; }

/*
 * La documentation de la machine, en accordéon sous l'échange : fermée, elle ne
 * coûte qu'une ligne ; ouverte, elle défile sans pousser le composeur hors de
 * l'écran. `flex: none` est indispensable — le fil, lui, garde tout le reste.
 */
.sv31-docs-acc { flex: none; border-top: 1px solid var(--sv31-b); }
.sv31-docs-acc > summary { cursor: pointer; list-style: none; display: flex; align-items: center;
    gap: 8px; padding: 8px 12px; font-size: 12px; color: var(--sv31-ink2); }
.sv31-docs-acc > summary::-webkit-details-marker { display: none; }
.sv31-docs-acc > summary::before { content: '▸'; color: var(--sv31-muted); font-size: 10px; }
.sv31-docs-acc[open] > summary::before { content: '▾'; }
.sv31-docs-acc > summary:hover { background: var(--sv31-hover); color: var(--sv31-ink); }
.sv31-docs-acc > summary b { margin-left: auto; color: var(--sv31-muted); font-size: 11px;
    font-family: var(--sv31-mono); }
.sv31-docs-acc .sv31-doc-liste { flex: none; max-height: 190px; }

/*
 * Sections de page du document ouvert. Le numéro vient de la donnée (`data-page`)
 * et non d'un libellé écrit en PHP : rien à traduire dans un service de rendu.
 * `est-cible` marque la page de la source citée — c'est elle que le contrôleur
 * `doc-anchor` amène sous les yeux.
 */
.sv31-doc-page { position: relative; padding-top: 10px; border-top: 1px dashed var(--sv31-b); }
.sv31-doc-page:first-child { border-top: 0; }
.sv31-doc-page::before { content: attr(data-page); position: absolute; top: 6px; right: 0;
    color: var(--sv31-muted); font-size: 10px; font-family: var(--sv31-mono); }
.sv31-doc-page.est-cible { background: color-mix(in srgb, var(--sv31-c) 7%, transparent);
    box-shadow: inset 2px 0 0 var(--sv31-c); border-radius: 0 6px 6px 0; padding-left: 10px; }

/*
 * EV-33 — la source OUVERTE se distingue. Sans JavaScript et sans recharger la
 * colonne gauche, le focus est la seule marque disponible : le lien cliqué le
 * conserve après la navigation de frame. Limite assumée et écrite : la marque
 * s'efface si l'utilisateur déplace son focus ailleurs.
 */
.sv31-source { border-radius: 6px; padding: 1px 6px; text-decoration: underline;
    text-underline-offset: 2px; }
.sv31-source:hover { background: var(--sv31-hover); }
.sv31-source:focus { outline: none; background: color-mix(in srgb, var(--sv31-c) 18%, transparent);
    box-shadow: inset 0 0 0 1px var(--sv31-c); text-decoration: none; }
.sv31-source:focus-visible { box-shadow: inset 0 0 0 1px var(--sv31-c), 0 0 0 2px var(--sv31-halo); }
/* Appel de citation `[S1]` dans le texte d'une réponse : discret, mais cliquable
   vers la source. Échappé puis balisé côté serveur (filtre `citations`). */
.sv31-cite { font-weight: 700; font-size: .85em; color: var(--sv31-c); text-decoration: none; }
.sv31-cite:hover { text-decoration: underline; }

/* ---------------------------------------------------------------------------
 * sv31 — écran « Inspection d'une question » (V5)
 *
 * Source : docs/maquettes/2026-10-08-vue-inspection-question/.
 *
 * Deux colonnes permanentes : les onglets ne commandent QUE la gauche. Ce bloc
 * ne style que ce que l'écran ajoute — le cadre, la colonne de lecture, les
 * groupes et les lignes restent ceux du socle.
 * ------------------------------------------------------------------------- */

.sv31-insp-mesures { margin: 0 0 12px; padding: 10px 14px; border: 1px solid var(--sv31-b);
    border-radius: 10px; background: var(--sv31-well); }
.sv31-insp-outils { display: flex; justify-content: flex-end; margin: 0 0 12px; }
.sv31-col > turbo-frame { display: flex; flex-direction: column; flex: 1; min-height: 0; }
.sv31-insp-corps { flex: 1; min-height: 0; overflow: auto; padding: 14px; }

.sv31-insp-question { margin: 0 0 12px; padding: 10px 12px; border-left: 3px solid var(--sv31-c);
    background: var(--sv31-well); border-radius: 0 8px 8px 0; color: var(--sv31-ink2); }
.sv31-insp-question b { display: block; margin-bottom: 3px; color: var(--sv31-muted);
    font-size: 10px; text-transform: uppercase; letter-spacing: .05em; }
.sv31-insp-texte { color: var(--sv31-ink); line-height: var(--line-height-base); }
.sv31-insp-soustitre { margin: 18px 0 6px; color: var(--sv31-muted); font-size: 10px;
    text-transform: uppercase; letter-spacing: .05em; }
.sv31-insp-biblio { margin: 0; padding-left: 20px; color: var(--sv31-ink2); font-size: 12.5px; }
.sv31-insp-biblio li { margin-bottom: 4px; }
.sv31-insp-biblio a { color: var(--sv31-c); }

/*
 * L'aveu de la vue dégradée se VOIT : taire la limite ferait croire que l'écran
 * distingue cité et non cité.
 */
.sv31-insp-avis { margin: 0 0 12px; padding: 9px 12px; border: 1px dashed var(--sv31-warn);
    border-radius: 8px; color: var(--sv31-warn); font-size: 12px; line-height: 1.5; }

.sv31-insp-lien { display: inline-block; margin-bottom: 6px; font-size: 11px; color: var(--sv31-c);
    text-decoration: none; border: 1px solid var(--sv31-b); border-radius: 999px; padding: 1px 9px; }
.sv31-insp-lien:hover { background: var(--sv31-hover); }

/* ============================================================== M4 / M5
   Onglet « Module » : liste de fichiers, et modale de plan interactif.

   Couche ajoutée en FIN DE FICHIER, comme la couche sv31 : elle doit gagner
   sur la maquette v28 sans la modifier.
   ====================================================================== */

/* ---- La liste de fichiers (M4) ---- */

.sv31-fichiers { display: flex; flex-direction: column; gap: 8px; }

.sv31-fichier {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto auto;
    grid-template-areas: "lien etat ops" "meta meta meta" "err err err";
    align-items: center;
    gap: 2px 12px;
    padding: 12px 14px;
    border: 1px solid var(--line);
    border-radius: 12px;
    background: var(--field);
}
.sv31-fichier.ouvrable:hover { border-color: var(--accent); background: var(--hover); }
.sv31-fichier.inerte { opacity: .72; }

.sv31-fichier-lien { grid-area: lien; color: var(--ink); text-decoration: none; }
.sv31-fichier-lien:hover { text-decoration: underline; }
.sv31-fichier-meta { grid-area: meta; color: var(--muted); font-size: 12px; }
.sv31-fichier-etat { grid-area: etat; }
.sv31-fichier-ops { grid-area: ops; font-size: 13px; }
.sv31-fichier-erreur {
    grid-area: err;
    margin: 6px 0 0;
    padding: 8px 10px;
    border-radius: 8px;
    background: rgba(220, 53, 69, .12);
    color: var(--ink);
    font-size: 12px;
}

/* ---- La modale de plan (M5) ----
   99 % de la hauteur et de la largeur : le patron plafonne à 460 px et 90 vh,
   il faut donc écraser les trois bornes, `overflow` compris (la couche v28
   pose `overflow:hidden!important` sur `.modal`). */

.modal.sv-plan {
    width: 99vw !important;
    height: 99vh !important;
    max-width: none !important;
    max-height: none !important;
    display: flex;
    flex-direction: column;
    padding: 0;
}
.modal.sv-plan .mh { flex: 0 0 auto; display: flex; align-items: center; gap: 10px; }
.modal.sv-plan .mh h2 { margin: 0; font-size: 16px; }
/* Le `.x` du patron porte déjà `margin-left:auto` : il occupe la place qu'avait
   « Nouvel onglet » dans AppliPlan. Rien à repositionner, seulement à agrandir
   assez pour être une cible tactile honnête. */
.modal.sv-plan .mh .x { width: 36px; height: 36px; border-radius: 10px; font-size: 22px; line-height: 1; }
.modal.sv-plan .mh .x:hover { background: var(--hover); }

/* Le signe est plus HAUT que large (73 × 122) : on dimensionne par la
   hauteur, la largeur suit. L'ancienne règle l'écrasait. */
.sv-monogramme { height: 30px; width: auto; flex: 0 0 auto; display: block; }

.modal.sv-plan .mb.sv-plan-corps {
    flex: 1 1 auto;
    min-height: 0;          /* sans cela, l'iframe pousse la modale hors écran */
    padding: 0;
    overflow: auto;
}

.sv-plan-scene { position: relative; width: 100%; height: 100%; background: #fff; }
.sv-plan-cadre { width: 100%; height: 100%; border: 0; display: block; background: #fff; }

.sv-plan-voile {
    position: absolute;
    inset: 0;
    z-index: 2;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 16px;
    background: var(--panel);
    transition: opacity .4s;
}
.sv-plan-voile.efface { opacity: 0; pointer-events: none; }

.sv-plan-rouet {
    width: 46px; height: 46px;
    border: 4px solid var(--line);
    border-top-color: var(--accent);
    border-radius: 50%;
    animation: sv-plan-tourne 1s linear infinite;
}
@keyframes sv-plan-tourne { to { transform: rotate(360deg); } }
/* Une animation perpétuelle est pénible pour qui y est sensible, et le voile
   reste lisible sans elle. */
@media (prefers-reduced-motion: reduce) {
    .sv-plan-rouet { animation: none; }
}

.sv-plan-jauge {
    width: min(420px, 78vw);
    height: 8px;
    border: 1px solid var(--line);
    border-radius: 999px;
    background: var(--field);
    overflow: hidden;
}
.sv-plan-jauge > i { display: block; height: 100%; width: 0; background: var(--accent); transition: width .2s; }
.sv-plan-message { margin: 0; color: var(--muted); font-size: 14px; text-align: center; padding: 0 20px; }

/* ---- Outils de l'en-tête de la modale (M5 §4) ---- */

.sv-plan-outils { display: flex; align-items: center; gap: 8px; margin-left: auto; }

.sv-plan-outil {
    display: inline-flex;
    align-items: center;
    height: 30px;
    padding: 0 11px;
    font-size: 12px;
    color: var(--ink);
    background: var(--field);
    border: 1px solid var(--line);
    border-radius: 8px;
    cursor: pointer;
    text-decoration: none;
    white-space: nowrap;
}
.sv-plan-outil:hover { background: var(--hover); border-color: var(--accent); }

/* « Autre plan » : un <details> natif, donc ouvrable au clavier et fermable à
   Échap sans une ligne de JavaScript. */
.sv-plan-choix { position: relative; }
.sv-plan-choix > summary {
    list-style: none;
    display: inline-flex;
    align-items: center;
    height: 30px;
    padding: 0 11px;
    font-size: 12px;
    color: var(--ink);
    background: var(--field);
    border: 1px solid var(--line);
    border-radius: 8px;
    cursor: pointer;
    white-space: nowrap;
}
.sv-plan-choix > summary::-webkit-details-marker { display: none; }
.sv-plan-choix > summary:hover { background: var(--hover); border-color: var(--accent); }
.sv-plan-choix-liste {
    position: absolute;
    top: calc(100% + 6px);
    right: 0;
    z-index: 5;
    min-width: 220px;
    max-height: 50vh;
    overflow: auto;
    padding: 6px;
    background: var(--panel);
    border: 1px solid var(--line);
    border-radius: 10px;
    box-shadow: 0 18px 40px rgba(0, 0, 0, .35);
}
.sv-plan-choix-liste a {
    display: block;
    padding: 7px 10px;
    border-radius: 7px;
    color: var(--ink);
    font-size: 13px;
    text-decoration: none;
}
.sv-plan-choix-liste a:hover { background: var(--hover); }

/* Le `.x` garde sa place à l'extrême droite, après les outils. */
.modal.sv-plan .mh .x { margin-left: 0; }

/* En plein écran, la scène occupe tout : le fond blanc du plan sinon laisse
   apparaître les bords de la modale. */
.sv-plan-scene:fullscreen { width: 100vw; height: 100vh; background: #fff; }

@media (max-width: 720px) {
    /* Sur mobile, l'en-tête n'a pas la place : les outils passent sous le titre. */
    .modal.sv-plan .mh { flex-wrap: wrap; }
    .sv-plan-outils { margin-left: 0; width: 100%; order: 3; }
}

/* ===========================================================================
 * Fiche machine compacte — deux colonnes / quatre groupes (2026-10-11)
 *
 * Source : docs/maquettes/2026-10-11-fiche-machine-deux-colonnes/fiche-machine.html
 * Consommé par templates/machine/_tab_fiche.html.twig.
 *
 * Rien de v28.css n'est redéfini : on ajoute un tableau PLUS DENSE que
 * `sv28-info-table` (17 px de padding / 18 px de valeur), parce que quatre
 * groupes doivent tenir sans défilement inutile. Les jetons viennent de la
 * couche sv31, donc les trois ambiances suivent sans règle dédiée.
 * ------------------------------------------------------------------------- */

.sv31-fiche-grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 18px;
    align-items: start;
}
.sv31-fiche-col { min-width: 0; display: flex; flex-direction: column; gap: 18px; }

.sv31-fiche-group {
    background: linear-gradient(170deg, var(--sv31-s2), var(--sv31-s1));
    border: 1px solid var(--sv31-b);
    border-radius: 14px;
    box-shadow: var(--sv31-sh);
    overflow: hidden;
}

.sv31-fiche-head {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 11px 12px 11px 14px;
    border-bottom: 1px solid var(--sv31-b2);
}
.sv31-fiche-ico { flex: none; display: grid; place-items: center; color: var(--sv31-c); }
.sv31-fiche-ico svg { width: 18px; height: 18px; }
.sv31-fiche-title {
    flex: 1 1 auto;
    min-width: 0;
    margin: 0;
    color: var(--sv31-ink);
    font-size: 13px;
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
}

.sv31-fiche-edit {
    flex: none;
    display: grid;
    place-items: center;
    width: 30px;
    height: 30px;
    color: var(--sv31-muted);
    background: var(--sv31-well);
    border: 1px solid var(--sv31-b);
    border-radius: 9px;
}
.sv31-fiche-edit svg { width: 15px; height: 15px; }
.sv31-fiche-edit:hover { color: var(--sv31-ink); border-color: var(--sv31-c); background: var(--sv31-hover); text-decoration: none; }
.sv31-fiche-edit:focus-visible { outline: 2px solid var(--sv31-c); outline-offset: 1px; }

/* Bouton libellé d'un groupe (ex. « Fiche » du groupe Client) : même gabarit que
   le crayon, mais il porte un mot et une icône. */
.sv31-fiche-action {
    flex: none;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    height: 30px;
    padding: 0 10px;
    color: var(--sv31-muted);
    background: var(--sv31-well);
    border: 1px solid var(--sv31-b);
    border-radius: 9px;
    font-size: 12px;
    font-weight: 650;
    text-decoration: none;
}
.sv31-fiche-action svg { width: 15px; height: 15px; }
.sv31-fiche-action:hover { color: var(--sv31-ink); border-color: var(--sv31-c); background: var(--sv31-hover); text-decoration: none; }
.sv31-fiche-action:focus-visible { outline: 2px solid var(--sv31-c); outline-offset: 1px; }
.sv31-fiche-action.primary { color: #fff; background: var(--sv31-c); border-color: var(--sv31-c); }
.sv31-fiche-action.primary:hover { filter: brightness(1.08); }

/* Réaffectation du parc : déroulant + confirmation, dans le groupe Parc. */
.sv31-fiche-parc {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 10px;
    padding: 12px 14px;
    border-top: 1px solid var(--sv31-b2);
}
.sv31-fiche-field { display: flex; flex-direction: column; gap: 4px; flex: 1 1 200px; min-width: 0; }
.sv31-fiche-field > span { color: var(--sv31-muted); font-size: 11.5px; font-weight: 700; letter-spacing: .02em; }
.sv31-fiche-field select {
    width: 100%;
    height: 30px;
    padding: 0 9px;
    color: var(--sv31-ink);
    background: var(--sv31-well);
    border: 1px solid var(--sv31-b);
    border-radius: 8px;
    font: inherit;
    font-size: 12.5px;
}
.sv31-fiche-field select:focus-visible { outline: 2px solid var(--sv31-c); outline-offset: 1px; }
.sv31-fiche-parc-ops { display: inline-flex; align-items: center; gap: 8px; }

.sv31-fiche-wrap { padding: 4px 0; }
.sv31-fiche-table { width: 100%; border-collapse: collapse; table-layout: fixed; }
.sv31-fiche-table th,
.sv31-fiche-table td { padding: 7px 14px; text-align: left; vertical-align: baseline; }
.sv31-fiche-table th {
    width: 42%;
    color: var(--sv31-muted);
    font-size: 11.5px;
    font-weight: 700;
    letter-spacing: .02em;
}
.sv31-fiche-table td { color: var(--sv31-ink); font-size: 13.5px; font-weight: 650; word-break: break-word; }
.sv31-fiche-table tbody tr { border-bottom: 1px solid color-mix(in srgb, var(--sv31-b) 55%, transparent); }
.sv31-fiche-table tbody tr:last-child { border-bottom: 0; }
.sv31-fiche-table tbody tr:hover { background: var(--sv31-hover); }
.sv31-fiche-mono { font-family: var(--sv31-mono); font-size: 12.5px; }

.sv31-fiche-src {
    display: inline-block;
    margin-left: 8px;
    padding: 1px 7px;
    border-radius: 999px;
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: .02em;
    vertical-align: 1px;
}
.sv31-fiche-src.inherit { color: var(--sv31-muted); background: var(--sv31-well); border: 1px solid var(--sv31-b); }
.sv31-fiche-src.override {
    color: var(--sv31-c);
    background: color-mix(in srgb, var(--sv31-c) 14%, transparent);
    border: 1px solid color-mix(in srgb, var(--sv31-c) 40%, transparent);
}
.sv31-fiche-note { margin: 0; padding: 6px 14px 12px; color: var(--sv31-muted); font-size: 11px; }

/* Sous 900 px, les deux colonnes s'empilent : rien n'est masqué, l'ordre des
   groupes (Famille, Caractéristiques, Client, Parc) est conservé. */
@media (max-width: 900px) {
    .sv31-fiche-grid { grid-template-columns: 1fr; }
}

/* ===========================================================================
 * État « actif / inactif » — un seul bouton, vert ou rouge, PARTOUT (2026-10-11)
 *
 * Remplace l'habillage segmenté des bascules d'état. Le clic soumet un POST
 * (CSRF scopé, Turbo Stream) : la mécanique serveur est celle des bascules,
 * seul l'habillage change. Un même sens, une même couleur sur tous les écrans :
 *   actif   → vert  (--ok)
 *   inactif → rouge (--err)
 * Les deux jetons sont fournis par la couche sv31, donc les trois ambiances
 * suivent sans règle dédiée.
 * ------------------------------------------------------------------------- */

.sv31-etat-form { display: inline; margin: 0; }
.sv31-etat {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    height: 26px;
    padding: 0 10px;
    border: 1px solid transparent;
    border-radius: 999px;
    font-size: 12px;
    font-weight: 700;
    letter-spacing: .02em;
    background: var(--sv31-well);
    color: var(--sv31-ink2);
}
.sv31-etat-dot { flex: none; width: 8px; height: 8px; border-radius: 50%; background: currentColor; }
.sv31-etat.on {
    color: var(--ok);
    background: color-mix(in srgb, var(--ok) 15%, transparent);
    border-color: color-mix(in srgb, var(--ok) 45%, transparent);
}
.sv31-etat.off {
    color: var(--err);
    background: color-mix(in srgb, var(--err) 15%, transparent);
    border-color: color-mix(in srgb, var(--err) 45%, transparent);
}
.sv31-etat-static { cursor: default; }
button.sv31-etat { appearance: none; cursor: pointer; font-family: inherit; }
button.sv31-etat:hover { filter: brightness(1.12); }
button.sv31-etat:focus-visible { outline: 2px solid var(--sv31-c); outline-offset: 1px; }
