/* ════════════════════════════════════════════════════
   Coyote Informatique — catalogue public de configurations
   Mobile-first, cohérent avec homepage.css (thème clair)
   ════════════════════════════════════════════════════ */

/* CATALOGUE-PUBLIC-BUDGET-9B1 — même cause, même correctif que CONFIGURATIONS-PHOTO-DESKTOP-
   WIDTH-5F sur .cfg-detail (voir plus bas) : .cfg-main est un enfant flex de body (homepage.css,
   display:flex/flex-direction:column). Sur l'axe transversal, les marges auto désactivent
   l'étirement par défaut (align-self:stretch) — l'élément se dimensionne alors sur son contenu,
   puis se centre. Tant que la grille contenait des tuiles, ce contenu était large et l'effet
   invisible ; dès que le filtre budget les masque toutes, il ne reste que la ligne du curseur et
   le message d'état vide, et le bloc se rétracte brutalement vers le centre.
   width:100% force l'étirement réel jusqu'au max-width, quel que soit le contenu visible : la
   largeur du catalogue ne dépend plus du nombre de tuiles affichées. box-sizing:border-box
   (homepage.css) garde le padding inclus — aucun calcul de largeur artificiel n'est ajouté. */
.cfg-main {
    width: 100%;
    max-width: 1360px;
    margin: 0 auto;
    padding: 28px 20px 56px;
}

/* ── En-tête de page — CATALOGUE-PUBLIC-HEADER-9A ─────────────────────────────
   Remplace le bloc titre/sous-titre qui vivait dans le contenu (.cfg-header /
   .cfg-title / .cfg-subtitle, supprimés avec leur markup : ils n'étaient utilisés
   que par /configurations/index.php). L'identité de la page est portée par le
   header, ce qui rend ~89 px de hauteur aux produits sans rien retirer au sens.

   TOUT est scopé sous .cfg-site-header : homepage.css n'est pas touché, la
   homepage et /configurations/view.php gardent exactement leur header actuel.

   Desktop : grille 1fr auto 1fr. Les deux colonnes latérales étant égales, le
   bloc central est réellement centré dans le header — pas simplement posé entre
   un logo et un bouton de largeurs différentes. La hauteur 72px de
   .site-header-inner (homepage.css) est conservée : le header ne grandit pas. */
.cfg-site-header .site-header-inner {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    gap: 16px;
}
.cfg-site-header .brand    { grid-column: 1; justify-self: start; }
.cfg-site-header .site-nav { grid-column: 3; justify-self: end; }

.cfg-header-id {
    grid-column: 2;
    justify-self: center;
    text-align: center;
    min-width: 0;
}
.cfg-header-title {
    font-size: 19px;
    font-weight: 700;
    color: #111318;
    margin: 0;
    line-height: 1.25;
}
.cfg-header-sub {
    font-size: 12.5px;
    color: #6b7280;
    margin: 2px 0 0;
    line-height: 1.3;
}

/* Tablette / petit desktop — homepage.css bascule .site-header-inner en grille
   1fr auto 1fr avec le logo centré, masque .site-nav et affiche un .nav-toggle.
   Cette page n'a PAS de hamburger (aucun bouton, aucun JS) : sans ces règles, le
   bouton Accueil disparaîtrait purement et simplement sous 860px. On rétablit
   donc les trois zones sur une seule ligne. */
@media (max-width: 860px) {
    .cfg-site-header .site-header-inner {
        grid-template-columns: auto 1fr auto;
        gap: 12px;
        padding: 10px 14px;
    }
    .cfg-site-header .brand      { grid-column: 1; justify-self: start; }
    .cfg-site-header .site-nav   { grid-column: 3; justify-self: end; display: flex; }
    .cfg-site-header .brand-logo { width: auto; height: 40px; max-height: 40px; }
    .cfg-header-id    { grid-column: 2; }
    .cfg-header-title { font-size: 17px; }
    .cfg-header-sub   { font-size: 12px; }
    /* Zone tactile confortable — le bouton reste le seul retour vers l'accueil. */
    .cfg-site-header .site-nav .btn { min-height: 40px; }
}

/* Mobile — trois zones sur une ligne deviennent illisibles : à 320px, le logo et
   le bouton ne laissent pas assez de place au titre. Deux rangées plutôt qu'un
   empilement forcé : logo + Accueil, puis l'identité de page sur la seconde
   ligne. Aucun chevauchement, aucun débordement horizontal, logo et bouton
   gardent une taille normale. */
@media (max-width: 540px) {
    .cfg-site-header .site-header-inner {
        grid-template-columns: auto 1fr;
        grid-template-areas:
            "brand nav"
            "id    id";
        row-gap: 6px;
        padding: 10px 14px;
    }
    .cfg-site-header .brand    { grid-area: brand; justify-self: start; }
    .cfg-site-header .site-nav { grid-area: nav;   justify-self: end; }
    .cfg-header-id {
        grid-area: id;
        justify-self: start;
        text-align: left;
    }
    .cfg-header-title { font-size: 16px; }
    /* CATALOGUE-PUBLIC-HEADER-9A1 — le sous-texte disparaît dès 540px, et non plus
       seulement sous 380px : c'est la seule ligne réellement secondaire du header,
       et c'est elle qui rendait le header mobile trop haut. Le sens de la page reste
       porté par le H1 « Nos configurations », le <title> et la meta description.
       Aucun impact au-dessus de 540px : le sous-texte y reste affiché tel quel. */
    .cfg-header-sub { display: none; }
}

/* ── Delta fiche détail — CATALOGUE-PUBLIC-DETAIL-HEADER-10B ──────────────────
   /configurations/view.php porte .cfg-site-header ET .cfg-detail-site-header :
   elle hérite donc de tout le comportement validé ci-dessus (grille, .site-nav
   rétablie sous 860px, logo 40px puis 34px, bouton à zone tactile confortable)
   sans qu'aucune de ces règles soit recopiée.

   Seule différence réelle : la fiche n'a pas de bloc d'identité central — son
   titre est le nom de la machine, dans le contenu, et l'audit 10A demandait
   explicitement de ne pas en ajouter un dans le header. La grille à deux rangées
   du bloc 540px ci-dessus créerait donc une rangée « id » vide et son row-gap.
   On revient à une seule rangée : LOGO à gauche, Configurations à droite.
   Ce bloc doit rester APRÈS celui de 9A : même spécificité, c'est l'ordre qui
   tranche. */
@media (max-width: 540px) {
    .cfg-detail-site-header .site-header-inner {
        grid-template-columns: auto 1fr;
        grid-template-areas: "brand nav";
    }
}

/* Très petit écran — seul le logo est encore réduit ; le H1 et le bouton Accueil
   restent intacts. Le sous-texte est déjà masqué depuis 540px (bloc ci-dessus). */
@media (max-width: 380px) {
    .cfg-site-header .brand-logo { height: 34px; max-height: 34px; }
}

/* ── Contrôle « Budget max. » — CATALOGUE-PUBLIC-BUDGET-9B ────────────────────
   Une ligne, pas une carte : ni fond coloré, ni bordure, ni titre « Filtres ».
   Placé entre le header et la grille, dans .cfg-main, donc aligné sur la grille.

   [hidden] est déclaré explicitement : la règle UA `[hidden]{display:none}` a une
   spécificité inférieure à `.cfg-budget{display:flex}` et serait sans effet. Sans
   cette ligne, le contrôle resterait visible alors que le JS ne l'a pas activé. */
.cfg-budget {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    column-gap: 14px;
    row-gap: 8px;
    margin: 0 0 18px;
}
.cfg-budget[hidden] { display: none; }

.cfg-budget-label {
    font-size: 13px;
    font-weight: 600;
    color: #4b5563;
    white-space: nowrap;
}
.cfg-budget-value {
    font-size: 14px;
    font-weight: 700;
    color: #111318;
    white-space: nowrap;
    /* Largeur réservée : le curseur ne se décale pas quand le montant change de
       nombre de chiffres pendant le déplacement. */
    min-width: 76px;
}
.cfg-budget-range {
    flex: 1 1 auto;
    min-width: 0;
    max-width: 420px;
    height: 24px;
    cursor: pointer;
    /* Orange déjà utilisé par .btn-primary et .cfg-tile-cta — aucune couleur
       nouvelle. Sur un moteur sans accent-color, le curseur garde son rendu natif
       et reste parfaitement fonctionnel. */
    accent-color: #f97316;
}
.cfg-budget-range:focus-visible { outline: 3px solid #f97316; outline-offset: 3px; }

/* ── Tri — CATALOGUE-PUBLIC-SORT-9D ──────────────────────────────────────────
   Même barre que le budget, pas une seconde barre. margin-left:auto pousse le
   groupe à droite : le curseur reste l'élément dominant de la ligne. Le libellé
   réutilise .cfg-budget-label (même primitive validée), .cfg-sort-label ne sert
   qu'au placement. */
.cfg-sort-label { margin-left: auto; }

/* Select natif volontairement non redessiné : flèche du système, ouverture
   native, comportement clavier/tactile inchangé. Blanc, bordure #d1d5db et
   radius 6px repris de .btn-ghost (homepage.css) — aucune teinte nouvelle, et
   l'orange reste réservé au curseur et au CTA des cartes. */
.cfg-sort-select {
    font-family: inherit;
    font-size: 13px;
    color: #111318;
    background: #ffffff;
    border: 1px solid #d1d5db;
    border-radius: 6px;
    padding: 5px 8px;
    min-height: 34px;
    max-width: 100%;
    cursor: pointer;
}
.cfg-sort-select:hover { border-color: #9ca3af; }
.cfg-sort-select:focus-visible { outline: 3px solid #f97316; outline-offset: 2px; }

/* Mobile — deux lignes : « Budget max. » à gauche, montant à droite, puis le
   curseur sur toute la largeur utile. Même logique que le header 9A/9A1, qui
   n'est pas modifié par ce lot. */
@media (max-width: 540px) {
    .cfg-budget {
        display: grid;
        grid-template-columns: auto 1fr;
        align-items: center;
        column-gap: 10px;
        row-gap: 4px;
        margin-bottom: 14px;
    }
    .cfg-budget-label { grid-column: 1; }
    .cfg-budget-value { grid-column: 2; justify-self: end; min-width: 0; }
    .cfg-budget-range {
        grid-column: 1 / -1;
        max-width: none;
        height: 30px;
    }
    /* CATALOGUE-PUBLIC-SORT-9D — troisième rangée : « Trier » à gauche, select à
       droite. Le placement automatique de la grille suffit (colonnes 1 et 2, la
       rangée du curseur occupant déjà 1/-1) — aucune zone nommée à introduire.
       Budget et tri ne sont jamais forcés sur une même ligne en dessous de 540px. */
    .cfg-sort-label  { grid-column: 1; margin-left: 0; }
    .cfg-sort-select { grid-column: 2; justify-self: end; min-height: 40px; }
}

/* Avis d'indisponibilité — CATALOGUE-PUBLIC-CONTACT-UNAVAILABLE-11E4.
   Alerte volontairement sobre : une indisponibilité commerciale n'est pas une
   erreur critique, elle ne mérite ni rouge ni bandeau. Fond, bordure et texte
   reprennent des valeurs DÉJÀ employées par le site (#f3f4f6 des vignettes,
   #d1d5db de .btn-ghost, #374151 de son texte) — aucune couleur nouvelle. */
.cfg-notice {
    background: #f3f4f6;
    border: 1px solid #d1d5db;
    border-radius: 8px;
    padding: 10px 14px;
    margin: 0 0 16px;
    font-size: 13.5px;
    line-height: 1.45;
    color: #374151;
}

.cfg-empty {
    border: 1px dashed #d1d5db;
    border-radius: 10px;
    padding: 48px 24px;
    text-align: center;
    color: #6b7280;
    font-size: 14px;
}

/* ── Grille de tuiles — mobile-first : 1 colonne, puis 2, puis 3 ── */
.cfg-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: 16px;
}
@media (min-width: 640px) {
    .cfg-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 1024px) {
    .cfg-grid { grid-template-columns: repeat(3, 1fr); }
}

.cfg-tile {
    display: flex;
    flex-direction: column;
    background: #ffffff;
    border: 1px solid rgba(0,0,0,.08);
    border-radius: 10px;
    overflow: hidden;
    text-decoration: none;
    color: inherit;
    transition: box-shadow .15s, border-color .15s;
}
/* CATALOGUE-PUBLIC-BUDGET-9B — même piège que .cfg-budget : `display:flex` ci-dessus
   l'emporte sur la règle UA `[hidden]{display:none}`. Sans cette ligne, une tuile hors
   budget resterait affichée malgré son attribut hidden. */
.cfg-tile[hidden] { display: none; }
.cfg-tile:hover {
    box-shadow: 0 6px 20px rgba(0,0,0,.08);
    border-color: rgba(0,0,0,.14);
}

.cfg-tile-photo {
    aspect-ratio: 16 / 10;
    background: #f3f4f6;
    overflow: hidden;
}
.cfg-tile-photo img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}
.cfg-photo-placeholder,
.cfg-detail-photo.cfg-photo-placeholder {
    background: linear-gradient(135deg, #f3f4f6, #e5e7eb);
}

/* CATALOGUE-PUBLIC-CARD-9C — corps de tuile resserré. Le padding et les interlignes
   baissent légèrement pour compenser la ligne « Disponible » ajoutée ; l'espacement
   entre titre, état, caractéristiques, prix et CTA reste explicite (gap 5px + marge
   propre avant le prix), la carte ne devient pas un bloc compact illisible. */
.cfg-tile-body {
    padding: 12px 14px 14px;
    display: flex;
    flex-direction: column;
    gap: 5px;
}
.cfg-tile-title {
    font-size: 15px;
    font-weight: 700;
    color: #111318;
    margin: 0;
    line-height: 1.3;
}
/* État de disponibilité — discret par construction : petit, texte simple, pastille
   réduite à un point de 6px construit en CSS (donc jamais lu par un lecteur d'écran
   comme un caractère parasite). #15803d est le vert déjà utilisé par le projet sur
   fond clair (facture-document.css, public/facture.php) — aucune teinte nouvelle. */
.cfg-tile-avail {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    font-weight: 600;
    color: #15803d;
    margin: 0;
    line-height: 1.35;
}
.cfg-tile-avail::before {
    content: "";
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: currentColor;
    flex-shrink: 0;
}
/* Caractéristiques — 2 lignes maximum, desktop comme mobile. Le texte complet reste
   dans le markup : c'est un plafond d'AFFICHAGE, pas une troncature de données, et il
   ne coûte aucun JavaScript. Le trio -webkit- est la forme réellement implémentée par
   les moteurs ; `line-clamp` est ajouté pour la version standardisée à venir. */
.cfg-tile-carac {
    font-size: 12.5px;
    color: #6b7280;
    margin: 0;
    line-height: 1.4;
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    overflow: hidden;
}
.cfg-tile-price {
    font-size: 17px;
    font-weight: 700;
    color: #111318;
    margin: 6px 0 0;
    line-height: 1.2;
}
/* CTA — repère visuel seulement : toute la tuile est déjà un <a>. Orange et taille
   inchangés (CATALOGUE-PUBLIC-CARD-9C), seul l'interligne est resserré. */
.cfg-tile-cta {
    font-size: 12.5px;
    font-weight: 600;
    color: #f97316;
    line-height: 1.2;
}

/* ── Fiche détaillée ─────────────────────────────────── */
.cfg-detail {
    width: 100%;
    max-width: 760px;
    margin: 0 auto;
    padding: 28px 20px 56px;
}
/* CONFIGURATIONS-PHOTO-DESKTOP-WIDTH-5F — .cfg-detail est un enfant flex de body (homepage.css,
   display:flex/flex-direction:column) : sans width explicite, un enfant flex avec margin:auto se
   dimensionne sur son contenu au lieu de s'étirer jusqu'à max-width, quel que soit le viewport.
   width:100% force l'étirement réel jusqu'au max-width applicable (760px base, 1120px ≥1024px
   via le bloc 5E ci-dessous, inchangé). box-sizing:border-box (homepage.css) garde le padding
   inclus dans cette largeur. */
/* CONFIGURATIONS-PHOTO-DESKTOP-WIDTH-5E — le plafond 760px (MVP initial) limitait la grande
   photo à ~720×405px sur desktop alors que le mobile utilise déjà presque toute la largeur
   disponible sans jamais atteindre ce plafond. Élargissement desktop uniquement — mobile/tablette
   strictement inchangés. */
@media (min-width: 1024px) {
    .cfg-detail {
        max-width: 1120px;
    }
}
.cfg-back {
    display: inline-block;
    font-size: 13px;
    color: #6b7280;
    text-decoration: none;
    margin-bottom: 16px;
}
.cfg-back:hover { color: #111318; }

/* ── Première vue produit — CATALOGUE-PUBLIC-DETAIL-HERO-10C ──────────────────
   MOBILE / TABLETTE (< 1024px) : une seule colonne. Volontairement SANS gap —
   l'espacement vertical reste celui que produisent déjà les marges existantes
   (.cfg-detail-photo 12px, .cfg-gallery 20px, .cfg-detail-price 20px). Le rythme
   mobile actuel est donc conservé au pixel près ; seul l'ordre change, le CTA
   remontant avec le résumé.

   DESKTOP (>= 1024px) : deux colonnes. La zone utile fait ~1080px ; en 3fr/2fr
   la photo occupe ~629px au lieu de 1080, donc son conteneur 16/9 passe d'environ
   608px de haut à ~354px — c'est la disposition, pas un plafond de hauteur, qui
   ramène titre, prix et CTA dans la première vue. Aucun max-height, aucun crop,
   aucune variante d'image, aucun JS : object-fit:contain (7C) reste intact.
   align-items:start : le résumé démarre en haut de la photo, jamais centré. */
.cfg-product-hero {
    display: grid;
}
.cfg-product-media,
.cfg-product-summary,
.cfg-lignes-block { min-width: 0; }
/* CATALOGUE-PUBLIC-CONTACT-LAYOUT-11E3 — le bloc composants est entré dans la grille : il porte
   désormais lui-même l'écart qui venait du margin-bottom du hero (24px, retiré ci-dessus pour ne
   pas le compter deux fois). Valeur identique, rendu identique — dans tous les états et à toutes
   les largeurs. En état ouvert desktop, c'est aussi l'écart entre la galerie et « Configuration ».
   Aucune marge négative, aucun position:absolute, aucune hauteur en pixels : flux CSS pur. */
.cfg-lignes-block { margin-top: 24px; }
/* Le titre était suivi de la date de publication (6px) puis du prix (16px). Cette
   ligne intermédiaire ayant disparu, les 6px seuls colleraient le prix au titre :
   l'écart avant le prix est rétabli à sa valeur d'origine. */
.cfg-product-summary .cfg-detail-title { margin-bottom: 12px; }

@media (min-width: 1024px) {
    .cfg-product-hero {
        grid-template-columns: minmax(0, 3fr) minmax(300px, 2fr);
        column-gap: 32px;
        align-items: start;
    }

    /* ── Position de « Configuration » — CATALOGUE-PUBLIC-CONTACT-LAYOUT-11E3 ──
       BLOCAGE D'ORIGINE : les composants étaient un FRÈRE du hero, placé après lui
       dans le flux. Ils attendaient donc la fin de la rangée de grille, dont la
       hauteur est celle de la colonne la plus haute — le formulaire ouvert. D'où
       un vide sous la galerie pendant que la liste patientait plus bas.

       ÉTAT FERMÉ (défaut) : pleine largeur en rangée 2, exactement le rendu
       actuel. C'est aussi le repli des navigateurs sans :has(), qui retrouvent
       donc à l'identique la page d'aujourd'hui — dégradation parfaitement
       silencieuse.

       ÉTAT OUVERT : le résumé s'étend sur les deux rangées de la colonne droite,
       et les composants prennent la colonne gauche sous la galerie. La liste ne
       dépend plus de la hauteur du formulaire.

       :has() n'est pas un réflexe : aucun sélecteur CSS ne permet de styler un
       ancêtre ou un frère PRÉCÉDENT d'après l'état d'un descendant. L'alternative
       serait du JavaScript, que ce lot exclut. Support : Chrome 105+,
       Safari 15.4+, Firefox 121+. */
    .cfg-lignes-block { grid-column: 1 / -1; }

    .cfg-product-hero:has(.cfg-request[open]) .cfg-product-summary {
        grid-row: 1 / span 2;
    }
    .cfg-product-hero:has(.cfg-request[open]) .cfg-lignes-block {
        grid-column: 1;
        grid-row: 2;
    }
    /* Un peu plus de présence commerciale sur desktop uniquement — pas un hero
       marketing : +6px sur le titre, +6px sur le prix, interlignes resserrés pour
       que le gain de taille ne coûte pas de hauteur. Mobile strictement inchangé. */
    .cfg-detail-title { font-size: 28px; line-height: 1.2; }
    .cfg-detail-price { font-size: 32px; line-height: 1.2; }
}

/* ── Première vue MOBILE — CATALOGUE-PUBLIC-DETAIL-MOBILE-10D ─────────────────
   Le hero 10C fonctionne mais empile encore titre, prix et un gros cartouche CTA :
   ~222px avant les composants. Ce bloc ramène cette zone à ~93px, sans toucher au
   desktop (tout est sous 540px) ni à la photo, à la galerie ou aux composants.

   Titre et prix appartiennent à la même information produit : ils passent sur une
   ligne, alignés sur la ligne de base. Le titre occupe minmax(0, 1fr) — il peut
   donc revenir sur deux lignes et se casser au besoin (overflow-wrap) sans jamais
   pousser le prix hors écran ; le prix, en colonne `auto` avec nowrap, n'est
   jamais coupé. Interlignes resserrés à 1.25 : les tailles ne changent pas
   (22px / 26px, déjà dans la cible), c'est le 1.6 hérité du body qui coûtait la
   hauteur. */
@media (max-width: 540px) {
    /* CATALOGUE-PUBLIC-CONTACT-LAYOUT-11E3 — l'espace entre le header et
       « ← Toutes les configurations » vient d'une seule règle : le padding-top de
       .cfg-detail (28px). Ni marge sur .cfg-back (qui n'a qu'un margin-bottom),
       ni gap de conteneur. 28 → 18px : 10px gagnés, le lien reste nettement
       détaché du header. Desktop non concerné, la règle est sous 540px. */
    .cfg-detail { padding-top: 18px; }

    .cfg-product-heading {
        display: grid;
        grid-template-columns: minmax(0, 1fr) auto;
        column-gap: 12px;
        align-items: baseline;
        margin-bottom: 14px;
    }
    .cfg-product-summary .cfg-detail-title {
        margin-bottom: 0;
        line-height: 1.25;
        overflow-wrap: break-word;
    }
    .cfg-product-summary .cfg-detail-price {
        margin: 0;
        line-height: 1.25;
        white-space: nowrap;
        justify-self: end;
    }

    /* Cartouche neutralisé : plus de fond, de bordure ni de padding autonome — il
       ne reste que l'action. La phrase d'accroche est masquée (le libellé du bouton
       est explicite à lui seul) ; elle reste affichée sur desktop, où le bloc encadré
       validé en 10C n'est pas modifié. */
    .cfg-cta-block {
        background: none;
        border: 0;
        border-radius: 0;
        padding: 0;
        text-align: left;
    }
    .cfg-cta-text { display: none; }
    /* Action principale de la page : pleine largeur du résumé, hauteur tactile
       confortable sans devenir un pavé, texte un cran plus lisible que le 13px des
       boutons secondaires. Orange, radius et focus restent ceux de .btn-primary. */
    .cfg-cta-block .btn {
        width: 100%;
        min-height: 46px;
        font-size: 14px;
    }

    /* Bouton « Configurations » du header de la FICHE uniquement (10B) : un peu moins
       imposant. Seul le rembourrage horizontal et la taille de police baissent — la
       hauteur tactile de 40px est conservée, et ni le logo, ni le desktop, ni le
       header de la liste /configurations/ ne sont touchés. */
    .cfg-detail-site-header .site-nav .btn {
        padding-left: 12px;
        padding-right: 12px;
        font-size: 12.5px;
        min-height: 40px;
    }
}

/* Très petits écrans — en dessous de ~360px, la colonne du titre devient trop
   étroite pour rester lisible à côté du prix. Repli assumé : nom sur une ligne,
   prix en dessous. Aucune collision, aucun texte compressé, aucun scroll
   horizontal — c'est le cas prévu par le lot, pas un accident de mise en page. */
@media (max-width: 359px) {
    .cfg-product-heading {
        grid-template-columns: minmax(0, 1fr);
        row-gap: 2px;
    }
    .cfg-product-summary .cfg-detail-price { justify-self: start; }
}

/* CONFIGURATIONS-PHOTO-PERF-5B — conteneur figé (position + aspect-ratio) : les deux calques
   (thumb placeholder, display) occupent exactement le même rectangle CSS quelles que soient
   leurs dimensions intrinsèques (width/height:100% + object-fit, jamais influencées par le
   fichier) — aucun CLS, aucun changement de taille perceptible lors du swap. */
.cfg-detail-photo {
    position: relative;
    aspect-ratio: 16 / 9;
    background: #f3f4f6;
    border-radius: 10px;
    margin-bottom: 12px;
    overflow: hidden;
}
/* CONFIGURATIONS-PHOTO-NO-CROP-7C — object-fit: contain sur la GRANDE photo uniquement.
   Le pipeline (etude_photo_resize_contain) n'a jamais rogné : il préserve le ratio et le dérivé
   display conserve l'image entière. Le crop était produit ici seul, par object-fit: cover dans un
   conteneur 16/9 — une source portrait 1536x2048 (3:4) y perdait ~58 % de sa hauteur.
   Le conteneur garde son rectangle stable (aspect-ratio + position:relative, bloc 5B ci-dessus) :
   les deux calques occupent toujours exactement le même rectangle CSS, donc aucun CLS et aucun
   recadrage perceptible au swap thumb -> display. Le fond #f3f4f6 du conteneur, déjà présent et
   identique aux vignettes, sert de fond neutre là où le ratio ne remplit pas le rectangle —
   aucun flou décoratif, aucune duplication de l'image.
   Les miniatures (.cfg-tile-photo img, .cfg-gallery-thumb img) restent en cover : à 72x54 px un
   léger rognage est légitime et attendu. */
.cfg-detail-photo img {
    position: absolute;
    inset: 0;
    display: block;
    width: 100%;
    height: 100%;
    object-fit: contain;
}
.cfg-main-placeholder { z-index: 1; }
/* CATALOGUE-PUBLIC-GALLERY-PLACEHOLDER-FIX-11J — masquage du placeholder, posé UNIQUEMENT par le
   JavaScript après un chargement display réussi. Aucune règle globale ne le masque : sans
   JavaScript, la classe n'est jamais ajoutée et le placeholder continue d'afficher la première
   photo. La transition est déclarée ici, sur l'état cible, et reprend exactement la durée du
   fondu de .cfg-main-display : les deux calques se croisent au lieu de laisser apparaître le fond
   gris pendant les 120 ms d'apparition du display. Cette règle ne produit que ce masquage. */
.cfg-main-placeholder.is-hidden {
    opacity: 0;
    transition: opacity .12s ease-out;
}
.cfg-main-display {
    z-index: 2;
    opacity: 0;
    transition: opacity .12s ease-out;
}
.cfg-main-display.is-visible { opacity: 1; }

.cfg-gallery {
    display: flex;
    gap: 8px;
    overflow-x: auto;
    margin-bottom: 20px;
}
.cfg-gallery-thumb {
    flex: 0 0 auto;
    width: 72px;
    height: 54px;
    border-radius: 6px;
    overflow: hidden;
    border: 2px solid transparent;
    cursor: pointer;
    padding: 0;
    background: #f3f4f6;
}
.cfg-gallery-thumb:hover,
.cfg-gallery-thumb:focus-visible,
.cfg-gallery-thumb.is-selected { border-color: #f97316; }
/* Feedback de clic sobre le temps du chargement du display (typiquement 50-200ms) — pas de
   spinner, pas d'overlay, pas de texte : une simple atténuation discrète et temporaire. */
.cfg-gallery-thumb.is-loading { opacity: .65; }
.cfg-gallery-thumb img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}
.cfg-detail-title {
    font-size: 22px;
    font-weight: 700;
    color: #111318;
    margin: 0 0 6px;
}
.cfg-detail-price {
    font-size: 26px;
    font-weight: 700;
    color: #111318;
    margin: 0 0 20px;
}

/* CATALOGUE-PUBLIC-DETAIL-COMPONENTS-10H — titre de section. Une seule règle : 18px
   convient à la fois au mobile et au desktop (cible 18–20px), inutile de dédoubler
   pour un point d'écart. La bordure haute de .cfg-lignes ci-dessous fournit déjà la
   séparation visuelle — aucun bloc vertical supplémentaire n'est créé. */
.cfg-lignes-title {
    font-size: 18px;
    font-weight: 700;
    color: #111318;
    margin: 0 0 10px;
    line-height: 1.3;
}
.cfg-lignes {
    border-top: 1px solid rgba(0,0,0,.08);
    margin-bottom: 24px;
}
.cfg-ligne {
    display: flex;
    justify-content: space-between;
    gap: 16px;
    padding: 11px 0;
    border-bottom: 1px solid rgba(0,0,0,.08);
}
.cfg-ligne-info { flex: 1; min-width: 0; }
.cfg-ligne-desig { font-size: 13.5px; font-weight: 600; color: #111318; }
.cfg-ligne-desc  { font-size: 12px; color: #6b7280; margin-top: 2px; }
.cfg-ligne-qte   { font-size: 12.5px; color: #6b7280; white-space: nowrap; }

.cfg-cta-block {
    background: #fafafa;
    border: 1px solid rgba(0,0,0,.08);
    border-radius: 10px;
    padding: 20px;
    text-align: center;
}
.cfg-cta-text {
    font-size: 13.5px;
    color: #4b5563;
    margin: 0 0 14px;
}

/* ── Actions de la fiche — CATALOGUE-PUBLIC-DETAIL-SHARE-10F ──────────────────
   Pile verticale compacte : l'action principale (orange, .btn-primary) reste
   dominante, « Partager » est secondaire. Aucune carte supplémentaire, aucun
   second bouton orange, aucun logo de réseau social.

   Le bouton secondaire réutilise .btn-ghost (homepage.css) tel quel — fond
   transparent, bordure #d1d5db, texte #374151, hover et focus déjà validés.
   .cfg-share-btn n'est qu'un point d'accroche : il n'apporte aucune couleur
   nouvelle et ne redéfinit rien.

   [hidden] est déclaré explicitement : .btn { display: inline-flex } l'emporte
   sur la règle UA [hidden]{display:none}. Sans cette ligne, le bouton serait
   visible avant que le JS ne l'ait activé — exactement ce que la mise en place
   progressive cherche à éviter. */
.cfg-cta-actions {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 10px;
}
.cfg-share-btn[hidden] { display: none; }

/* ── Formulaire de demande — CATALOGUE-PUBLIC-CONTACT-FORM-11E ────────────────
   <details> natif, aucun JavaScript. Le panneau occupe toute la largeur de la
   colonne résumé : .cfg-cta-actions centre ses enfants, il faut donc l'étirer
   explicitement, sinon il se réduirait à la largeur de son contenu. */
.cfg-request { width: 100%; }

/* Le <summary> prend l'apparence de l'action principale. Le marqueur natif est
   retiré des deux façons nécessaires (standard + WebKit/Safari) ; le focus
   clavier n'est PAS supprimé, .btn:focus-visible (homepage.css) s'applique
   toujours. cursor:pointer parce que .btn le pose déjà sur les vrais boutons. */
.cfg-request-summary {
    width: 100%;
    list-style: none;
    cursor: pointer;
}
.cfg-request-summary::-webkit-details-marker { display: none; }
.cfg-request-summary::marker { content: ''; }

.cfg-request-panel {
    margin-top: 12px;
    text-align: left;
}
.cfg-request-intro,
.cfg-request-ok,
.cfg-request-error,
.cfg-request-rgpd {
    margin: 0 0 10px;
    line-height: 1.45;
}
.cfg-request-intro { font-size: 13px; color: #4b5563; }
/* Vert déjà utilisé par le projet sur fond clair (facture-document.css) et par la
   pastille « Disponible » des tuiles — aucune teinte nouvelle. */
.cfg-request-ok    { font-size: 13.5px; font-weight: 600; color: #15803d; margin-bottom: 0; }
/* #b91c1c est le rouge d'erreur DÉJÀ employé par le projet sur fond clair
   (public/assets/css/hortense-widget.css) — aucune teinte nouvelle. Texte coloré
   et non bandeau d'alerte : l'erreur reste dans le flux du panneau. */
.cfg-request-error { font-size: 13px; font-weight: 600; color: #b91c1c; }
.cfg-request-rgpd  { font-size: 11.5px; color: #6b7280; margin-bottom: 0; }

/* ── Alternative email — CATALOGUE-PUBLIC-CONTACT-UX-11E2 ─────────────────────
   Séparateur « OU » franc : le texte est dans le flux (donc lu par un lecteur
   d'écran), les deux filets sont des pseudo-éléments purement décoratifs. */
.cfg-request-or {
    display: flex;
    align-items: center;
    gap: 12px;
    margin: 14px 0;
    font-size: 12px;
    font-weight: 700;
    letter-spacing: .08em;
    color: #6b7280;
}
.cfg-request-or::before,
.cfg-request-or::after {
    content: "";
    flex: 1 1 auto;
    height: 1px;
    background: rgba(0,0,0,.12);
}

/* Vraie action secondaire, pas un lien discret. Trois niveaux nettement séparés :
   .btn-primary orange (envoyer la demande) > cette alternative, .btn-ghost
   RENFORCÉ (bordure #9ca3af et texte foncé en gras) > « Partager », .btn-ghost
   standard. Aucune couleur nouvelle : #9ca3af et #111318 viennent déjà de
   .btn-ghost:hover (homepage.css). */
.cfg-request-mailto {
    width: 100%;
    min-height: 44px;
    font-weight: 700;
    color: #111318;
    border-color: #9ca3af;
    text-align: center;
}
.cfg-request-mailto:hover { background: #f3f4f6; border-color: #6b7280; }

/* ── Densité desktop de la zone contact — CATALOGUE-PUBLIC-CONTACT-UX-11E2 ────
   CAUSE DU VIDE : le hero est une grille 3fr/2fr en align-items:start (10C). La
   hauteur de la rangée est celle de la colonne la plus haute. Colonne média ≈
   440px (photo 16/9 ≈ 354 + galerie 74). Colonne résumé formulaire OUVERT ≈
   684px. Les 244px d'écart sont exactement le trou visible SOUS la photo, à
   gauche du bas du formulaire. Chaque pixel retiré au panneau supprime donc un
   pixel de vide, mécaniquement.
   align-items:start reste indispensable : le passer à stretch ou center
   déplacerait le vide sans le supprimer et repousserait le titre vers le bas.
   Compaction DESKTOP UNIQUEMENT — le rendu mobile validé n'est pas touché. */
@media (min-width: 1024px) {
    /* Le cartouche encadré coûtait 42px de bordure et de rembourrage pour un
       simple fond gris. Le mobile s'en passe déjà depuis 10D : les deux paliers
       deviennent cohérents, et la zone gagne en densité sans rien perdre. */
    .cfg-cta-block {
        background: none;
        border: 0;
        border-radius: 0;
        padding: 0;
    }
    /* La phrase d'accroche disait la même chose que l'introduction du panneau,
       juste au-dessus d'elle. Une seule des deux suffit — c'est celle qui est
       dans le formulaire, là où le visiteur agit, qui est conservée. */
    .cfg-cta-text { display: none; }

    .cfg-request-panel { margin-top: 10px; }
    .cfg-request-intro { margin-bottom: 8px; }
    .cfg-request-label { font-size: 12px; line-height: 1.2; margin-bottom: 3px; }
    .cfg-request-input { min-height: 36px; padding: 6px 10px; margin-bottom: 6px; }
    .cfg-request-submit { min-height: 40px; }
    .cfg-request-or { margin: 12px 0; }
    .cfg-request-mailto { min-height: 40px; }
}

.cfg-request-form { display: block; }
.cfg-request-label {
    display: block;
    font-size: 12.5px;
    font-weight: 600;
    color: #374151;
    margin: 0 0 4px;
}
/* Champs pleine largeur, desktop comme mobile : la colonne résumé est déjà
   étroite, une largeur intermédiaire n'apporterait rien. box-sizing:border-box
   vient de homepage.css — le padding reste inclus, donc aucun débordement. */
.cfg-request-input {
    width: 100%;
    font-family: inherit;
    font-size: 14px;
    color: #111318;
    background: #ffffff;
    border: 1px solid #d1d5db;
    border-radius: 6px;
    padding: 8px 10px;
    min-height: 40px;
    margin: 0 0 12px;
}
.cfg-request-input:focus-visible { outline: 3px solid #f97316; outline-offset: 2px; }
.cfg-request-textarea { min-height: 0; resize: vertical; }
.cfg-request-submit { width: 100%; min-height: 44px; }

/* Honeypot — hors écran plutôt que display:none : il doit rester un champ réel
   que les automates remplissent, tout en étant strictement inatteignable pour un
   humain (invisible, hors flux de tabulation via tabindex=-1 sur l'input). */
.cfg-request-hp {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

/* Sous 540px, les règles CTA de 10D (width:100%, min-height:46px, font-size:14px)
   s'appliquent déjà aux DEUX boutons via .cfg-cta-block .btn : le groupe reste
   pleine largeur et tactile sans qu'une seule valeur soit redéclarée ici. Seul
   l'écart vertical est resserré pour que le hero mobile ne redevienne pas massif. */
@media (max-width: 540px) {
    .cfg-cta-actions { align-items: stretch; gap: 8px; }
}

.cfg-404 {
    max-width: 560px;
    margin: 80px auto;
    padding: 0 20px;
    text-align: center;
}
.cfg-404 h1 { font-size: 20px; color: #111318; }
.cfg-404 p  { font-size: 14px; color: #6b7280; }
