/* ============================================================================
   calculator.css — Le parcours de devis
   Rail de progression, transitions d'étapes, panneau prix animé,
   jauge de volume, grille d'objets, récapitulatif, écran de succès.
   ============================================================================ */

/* ======================= RAIL DE PROGRESSION ============================= */
/* Le rail d'étapes n'est pas une carte.
   ---------------------------------------------------------------------------
   Il l'était : fond, bordure, ombre, rayon. Sur une page qui empile déjà le
   cadre de l'application, la carte d'estimation et la carte d'étape, ça faisait
   un quatrième contour pour une information qui n'est pas un contenu mais un
   repère. Un repère se pose sur la page, il ne s'encadre pas.
   Il ne reste que l'espacement, et un souffle latéral pour que les ombres des
   pastilles ne soient pas rognées. */
.progressRail{
  position:relative;
  margin-bottom:var(--sp-5);
  padding:0 var(--sp-1) var(--sp-1);
}
.progressRail__head{
  display:flex; align-items:baseline; justify-content:space-between; gap:var(--sp-3);
  margin-bottom:var(--sp-3);
}
.progressRail__label{ font-size:var(--fs-sm); font-weight:700; color:var(--muted-strong); }
/* Sur une carte blanche, --muted tenait largement. Le rail n'ayant plus de
   carte, son libellé s'est retrouvé sur le fond de page, à 4,54:1 — au-dessus
   du seuil, mais sans marge : la moindre palette client un peu plus claire le
   ferait passer dessous. --muted-strong rend cette marge. */
.progressRail__count{
  font-size:var(--fs-sm); font-weight:800; color:var(--accent-strong);
  font-variant-numeric:tabular-nums;
}

/* Barre : #progressBar est piloté en width par core.js -> renderSteps() */
/* La piste est passée du blanc d'une carte au fond de la page. --surface-sunken
   se dérive de --card : sur une carte blanche il tranchait, sur le fond de page
   les deux se confondent et la piste disparaît — il ne resterait que la portion
   remplie, flottant sans repère de longueur. On la dérive donc du texte, comme
   les bords, et on l'affine : sans carte autour, huit pixels pèsent lourd. */
.progress{
  position:relative;
  height:6px;
  background:color-mix(in oklab, var(--text) 12%, var(--bg));
  border-radius:var(--r-pill);
  overflow:hidden;
}
#progressBar{
  height:100%; width:0;
  border-radius:var(--r-pill);
  background:linear-gradient(90deg,
    color-mix(in oklab, var(--accent) 70%, white 12%),
    var(--accent));
  box-shadow:0 0 12px var(--accent-40);
  transition:width var(--dur-slow) var(--ease-spring);
}
/* Reflet qui balaie la barre : signal de progression */
#progressBar::after{
  content:"";
  position:absolute; inset:0;
  background:linear-gradient(90deg,transparent,rgba(255,255,255,.45),transparent);
  transform:translateX(-100%);
  animation:sheen 2.6s var(--ease-smooth) infinite;
}
@keyframes sheen{ 0%{ transform:translateX(-100%);} 55%,100%{ transform:translateX(320%);} }

/* Puces d'étapes — les boutons sont générés par core.js (.stepBtn) */
.steps{
  display:flex; gap:var(--sp-2);
  margin-top:var(--sp-4);
  overflow-x:auto;
  scrollbar-width:none;
  scroll-snap-type:x proximity;
  padding-bottom:2px;
}
.steps::-webkit-scrollbar{ display:none; }

.stepBtn{
  flex:0 0 auto;
  scroll-snap-align:start;
  display:inline-flex; align-items:center; gap:8px;
  padding:9px 15px 9px 9px;
  background:var(--surface-sunken);
  border:1px solid transparent;
  border-radius:var(--r-pill);
  color:var(--muted);
  font-size:var(--fs-sm); font-weight:700;
  text-align:left;
  cursor:pointer;
  transition:background var(--dur-fast), color var(--dur-fast),
             border-color var(--dur-fast), transform var(--dur-fast) var(--ease-spring);
}
/* Pastille numérotée, générée en CSS à partir de data-step */
.stepBtn::before{
  content:attr(data-step);
  display:grid; place-items:center;
  width:24px; height:24px; flex:0 0 auto;
  border-radius:50%;
  background:var(--surface);
  color:var(--muted);
  font-size:var(--fs-xs); font-weight:800;
  box-shadow:var(--shadow-xs);
  transition:background var(--dur-med), color var(--dur-med), transform var(--dur-med) var(--ease-spring);
}
.stepBtn:hover{ background:var(--surface-hover); color:var(--text); }
.stepBtn:active{ transform:scale(.96); }
.stepBtn[aria-current="true"]{
  background:var(--accent-14);
  border-color:var(--accent-40);
  color:var(--accent-strong);
}
.stepBtn[aria-current="true"]::before{
  background:var(--accent);
  color:var(--accent-contrast);
  transform:scale(1.06);
}
/* Étape validée : la pastille devient une coche */
.stepBtn.is-done{ color:var(--muted-strong); }
.stepBtn.is-done::before{
  content:"✓";
  background:var(--ok);
  color:#fff;
}

/* Sur telephone, sept pastilles libellees ne tiennent pas : le rail defilait,
   mais sa barre etant masquee, le visiteur voyait trois etapes et croyait que
   les autres n'existaient pas. On garde les numeros seuls — les sept tiennent
   alors sur une ligne — et le libelle de l'etape en cours est de toute facon
   repete juste en dessous, dans le titre de la section, et dans « Etape 3 sur 7 ».

   CE BLOC ETAIT ECRIT PLUS HAUT, AVANT LES REGLES QU'IL SURCHARGE. Une requête
   média n'ajoute aucune spécificité : à égalité, c'est la dernière déclaration
   qui gagne, et `.stepBtn{ flex:0 0 auto }` écrasait donc `flex:1 1 0`. Les
   sept pastilles gardaient leur largeur naturelle — 386 px de contenu dans
   347 px — et débordaient de 26 px, ce qui faisait glisser TOUTE la page du
   formulaire public sur un téléphone de 375 px.

   `min-width:0` en plus : sans lui, `flex:1 1 0` refuse quand même de descendre
   sous la largeur du contenu, et la correction n'aurait rien corrigé. */
@media (max-width:560px){
  .progressRail{ padding:0 2px var(--sp-1); }
  .steps{ gap:6px; overflow-x:auto; }
  .stepBtn{
    padding:8px; gap:0; justify-content:center;
    flex:1 1 0; min-width:0;
  }
  .stepBtn__label{ display:none; }
}

/* ========================= ÉTAPES DU FORMULAIRE ========================== */
.step{ position:relative; }
.step[hidden]{ display:none !important; }

/* Entrée directionnelle : la classe est posée par ux.js avant l'affichage */
.step.is-entering-fwd{ animation:stepInFwd var(--dur-slow) var(--ease-out); }
.step.is-entering-back{ animation:stepInBack var(--dur-slow) var(--ease-out); }
@keyframes stepInFwd{
  from{ opacity:0; transform:translateX(28px) scale(.99); }
  to{ opacity:1; transform:none; }
}
@keyframes stepInBack{
  from{ opacity:0; transform:translateX(-28px) scale(.99); }
  to{ opacity:1; transform:none; }
}

.step__head{
  display:flex; align-items:flex-start; gap:var(--sp-3);
  padding:var(--sp-5) var(--sp-5) 0;
}
.step__badge{
  width:46px; height:46px; flex:0 0 auto;
  display:grid; place-items:center;
  border-radius:var(--r-md);
  background:var(--accent-14);
  color:var(--accent-strong);
}
/* Chaque étape décale légèrement la teinte de l'accent : le parcours gagne en
   couleur sans jamais sortir de la palette choisie par le client — la rotation
   s'applique à une couleur déjà dérivée de --accent. */
#step-2 .step__badge{ filter:hue-rotate(38deg); }
#step-3 .step__badge{ filter:hue-rotate(76deg); }
#step-4 .step__badge{ filter:hue-rotate(-42deg); }
#step-5 .step__badge{ filter:hue-rotate(150deg); }
#step-6 .step__badge{ filter:hue-rotate(196deg); }
.step__titles{ min-width:0; }
.step__title{ font-size:var(--fs-xl); font-weight:800; letter-spacing:-.02em; }
.step__sub{ color:var(--muted); font-size:var(--fs-sm); margin-top:2px; }

/* Apparition en cascade des champs : effet de « déroulé » */
.step.is-entering-fwd .field,
.step.is-entering-back .field{
  animation:fieldIn var(--dur-slow) var(--ease-out) backwards;
}
@keyframes fieldIn{ from{ opacity:0; transform:translateY(10px); } to{ opacity:1; transform:none; } }
.step .field:nth-child(1){ animation-delay:40ms; }
.step .field:nth-child(2){ animation-delay:80ms; }
.step .field:nth-child(3){ animation-delay:120ms; }
.step .field:nth-child(4){ animation-delay:160ms; }
.step .field:nth-child(5){ animation-delay:200ms; }
.step .field:nth-child(6){ animation-delay:240ms; }

/* ============================= ITINÉRAIRE =============================== */
/* Les deux adresses reliées par un trait, comme une feuille de route */
.itinerary{ position:relative; display:flex; flex-direction:column; gap:var(--sp-4); }
.itinerary::before{
  content:"";
  position:absolute; left:21px; top:44px; bottom:44px;
  width:2px;
  background:repeating-linear-gradient(to bottom,
    var(--border-strong) 0 6px, transparent 6px 12px);
}
.itinerary__leg{ display:flex; gap:var(--sp-3); align-items:flex-start; }
.itinerary__dot{
  width:44px; height:44px; flex:0 0 auto;
  display:grid; place-items:center;
  border-radius:50%;
  background:var(--surface);
  border:2px solid var(--border-strong);
  font-size:18px;
  z-index:1;
  transition:border-color var(--dur-med), transform var(--dur-med) var(--ease-spring),
             box-shadow var(--dur-med);
}
.itinerary__leg.is-filled .itinerary__dot{
  border-color:var(--accent);
  box-shadow:var(--shadow-glow);
  transform:scale(1.06);
}
.itinerary__field{ flex:1 1 auto; min-width:0; }

/* Bandeau « distance calculée » */
.distanceCard{
  display:flex; align-items:center; gap:var(--sp-3);
  margin-top:var(--sp-4);
  padding:var(--sp-3) var(--sp-4);
  background:var(--accent-soft-bg);
  border:1px dashed var(--accent-40);
  border-radius:var(--r-md);
}
.distanceCard__icon{ display:grid; place-items:center; color:var(--accent); }
.distanceCard__value{ font-size:var(--fs-lg); font-weight:800; font-variant-numeric:tabular-nums; }
.distanceCard__label{ color:var(--muted); font-size:var(--fs-xs); }
.distanceCard.is-computing{ opacity:.7; }
.distanceCard.is-fresh{ animation:pop var(--dur-slow) var(--ease-spring); }

/* Raccourcis de date */
.quickDates{ display:flex; flex-wrap:wrap; align-items:center; gap:var(--sp-2); margin-top:var(--sp-2); }
.quickDates__lead{ color:var(--faint); }

/* =========================== ACCÈS / LOGEMENTS ========================== */
.placePanel{
  padding:var(--sp-4);
  background:var(--surface-raised);
  border:1px solid var(--border-soft);
  border-radius:var(--r-md);
}
.placePanel__head{
  display:flex; align-items:center; gap:var(--sp-2);
  margin-bottom:var(--sp-4);
  font-weight:800; font-size:var(--fs-md);
}
.placePanel__head .badge{ margin-left:auto; }
/* .placePanel est un <fieldset> et .placePanel__head sa <legend> : on rétablit
   la bordure et le fond que la remise à zéro des fieldsets a retirés. */
fieldset.placePanel{
  border:1px solid var(--border-soft);
  padding:var(--sp-4);
}
.placePanel .field + .field{ margin-top:var(--sp-4); }

/* ============================== VOLUME ================================== */
/* Jauge « remplissage du camion » : --fill est posé par ux.js (0 → 100) */
.volumeGauge{
  display:flex; align-items:center; gap:var(--sp-4);
  padding:var(--sp-4);
  background:var(--accent-soft-bg);
  border:1px solid var(--accent-24);
  border-radius:var(--r-md);
  margin-bottom:var(--sp-4);
}
.volumeGauge__truck{ display:grid; place-items:center; flex:0 0 auto; color:var(--accent); }
.volumeGauge__body{ flex:1 1 auto; min-width:0; }
.volumeGauge__top{ display:flex; align-items:baseline; justify-content:space-between; gap:var(--sp-2); }
.volumeGauge__value{
  font-size:var(--fs-xl); font-weight:900; letter-spacing:-.02em;
  font-variant-numeric:tabular-nums;
}
.volumeGauge__hint{ color:var(--muted); font-size:var(--fs-xs); }
.volumeGauge__bar{
  position:relative;
  height:10px; margin-top:8px;
  background:var(--surface-sunken);
  border-radius:var(--r-pill);
  overflow:hidden;
}
.volumeGauge__fill{
  height:100%; width:calc(var(--fill,0) * 1%);
  border-radius:var(--r-pill);
  background:linear-gradient(90deg,
    color-mix(in oklab, var(--accent) 62%, white 16%),
    var(--accent));
  transition:width var(--dur-slow) var(--ease-spring);
}
.volumeGauge.is-bumped .volumeGauge__value{ animation:bump var(--dur-med) var(--ease-spring); }

/* Grille d'objets courants — rendue par assets/js/form.js */
.itemsGrid{
  display:grid; gap:var(--sp-3);
  /* 220 px ne laissaient au libellé que la place d'un « Petit… » : le pas
     − / + est de largeur fixe et prenait tout le reste. On élargit, et le
     libellé passe à la ligne au lieu d'être coupé. */
  grid-template-columns:repeat(auto-fill,minmax(236px,1fr));
}
.itemCard{
  display:flex; flex-wrap:wrap; align-items:center; justify-content:space-between;
  gap:var(--sp-2) var(--sp-3);
  padding:var(--sp-4);
  background:var(--surface);
  border:1px solid var(--border-soft);
  border-radius:var(--r-md);
  transition:border-color var(--dur-fast), background var(--dur-fast),
             transform var(--dur-fast) var(--ease-spring), box-shadow var(--dur-med);
}
.itemCard:hover{ border-color:var(--border-strong); transform:translateY(-2px); box-shadow:var(--shadow-md); }
/* Objet sélectionné (qté > 0) : marqué par ux.js */
.itemCard.is-picked{
  border-color:var(--accent);
  background:var(--accent-soft-bg);
  box-shadow:var(--shadow-glow);
}
.itemCard .meta{ display:flex; flex-direction:column; gap:2px; min-width:0; }
.itemCard .meta strong{
  font-family:var(--font-title-family);
  font-size:var(--fs-md); font-weight:700;
  line-height:1.25;
  overflow-wrap:anywhere;   /* « Canapé 3 places » se lit en entier */
}
.itemCard .meta small{ color:var(--muted); font-size:var(--fs-xs); }
.itemCard .qtyWrap{ display:flex; align-items:center; gap:var(--sp-2); flex:0 0 auto; }
.itemCard .qtyWrap > .note{ display:none; }
.itemCard input[type="number"]{ width:56px; }

/* ============================ RÉCAPITULATIF ============================= */
.summary{ display:grid; gap:var(--sp-4); }
.summaryBlock{
  padding:var(--sp-4);
  background:var(--surface-raised);
  border:1px solid var(--border-soft);
  border-radius:var(--r-md);
}
.summaryBlock h4{
  display:flex; align-items:center; gap:8px;
  font-size:var(--fs-sm); font-weight:800;
  text-transform:uppercase; letter-spacing:.06em;
  color:var(--muted);
  margin-bottom:var(--sp-3);
}
#summaryDetails{ display:flex; flex-direction:column; gap:8px; font-size:var(--fs-md); }
#summaryDetails > div{
  padding-bottom:8px;
  border-bottom:1px dashed var(--border-soft);
}
#summaryDetails > div:last-child{ border-bottom:0; padding-bottom:0; }
#summaryDetails strong{ color:var(--muted-strong); font-weight:700; }

.summaryPrice .card{
  background:linear-gradient(135deg, var(--accent), color-mix(in oklab, var(--accent) 62%, var(--text) 22%));
  color:var(--accent-contrast);
  border:0;
  box-shadow:var(--shadow-pop);
  font-size:var(--fs-lg); font-weight:700;
}
.summaryPrice .card strong{ font-size:var(--fs-xl); font-weight:900; }
.summaryPrice .card .note{
  display:block; margin-top:4px;
  color:inherit; opacity:.88;
  font-size:var(--fs-sm); font-weight:600;
}

/* ============================== CODE PROMO ============================== */
/* Discret tant qu'on n'en a pas : un lien, pas un champ. Le visiteur sans code
   ne doit pas se demander s'il paie trop cher faute d'en avoir un. */
.promoBloc{ display:grid; gap:var(--sp-2); }
.promoBloc__ouvrir{
  justify-self:start;
  display:inline-flex; align-items:center; gap:8px;
  padding:6px 2px;
  background:none; border:0; cursor:pointer;
  color:var(--accent-strong);
  font:inherit; font-size:var(--fs-md); font-weight:700;
  text-decoration:underline; text-underline-offset:3px;
}
.promoBloc__ouvrir:hover{ color:var(--accent); }
.promoBloc__saisie{
  display:grid; grid-template-columns:minmax(0, 1fr) auto; gap:var(--sp-2);
  align-items:stretch;
  max-width:440px;
}
.promoBloc__saisie[hidden]{ display:none; }
.promoBloc__saisie input{ text-transform:uppercase; letter-spacing:.06em; }
.promoBloc__applique{
  display:flex; flex-wrap:wrap; align-items:center; gap:6px 12px;
  padding:var(--sp-3) var(--sp-4);
  background:var(--ok-bg);
  border:1px dashed color-mix(in oklab, var(--ok) 55%, var(--border));
  border-radius:var(--r-md);
}
.promoBloc__applique[hidden]{ display:none; }
.promoBloc__code{
  display:inline-flex; align-items:center; gap:6px;
  color:var(--ok); font-weight:800; letter-spacing:.04em;
}
.promoBloc__effet{ color:var(--text); font-weight:600; }
.promoBloc__retirer{
  margin-left:auto;
  background:none; border:0; cursor:pointer; padding:4px 2px;
  color:var(--muted-strong); font:inherit; font-size:var(--fs-sm);
  text-decoration:underline; text-underline-offset:3px;
}
.promoBloc__retirer:hover{ color:var(--danger); }
.promoBloc__message{ min-height:0; font-size:var(--fs-sm); color:var(--ok); }
.promoBloc__message:empty{ display:none; }
.promoBloc__message.is-erreur{ color:var(--danger); }

/* La remise, sous le total du panneau de prix. */
.pricePromo{
  margin-top:6px;
  font-size:var(--fs-sm); font-weight:700;
  color:var(--ok);
}
.pricePromo[hidden]{ display:none; }
.pricePanel--sombre .pricePromo{ color:color-mix(in oklab, #4ade80 80%, white); }

/* ====================== DÉCLARATION DE VALEUR (étape 6) ================== */
/* Deux montants coexistent dans le parcours : le prix de la prestation et la
   valeur déclarée du mobilier. Ils ne doivent jamais se ressembler. Le prix est
   en dégradé d'accent ; la valeur déclarée reste neutre, encadrée en pointillés,
   et porte son avertissement sous les yeux. */
.declaredIntro,
.declaredWarn,
.declaredLegal{
  display:flex; align-items:flex-start; gap:var(--sp-2);
  margin-bottom:var(--sp-4);
}
.declaredIntro .icon,
.declaredWarn .icon,
.declaredLegal .icon{ flex:0 0 auto; margin-top:2px; }
.declaredWarn{ color:var(--warn); margin:var(--sp-3) 0 0; }
.declaredLegal{ margin:var(--sp-4) 0 0; }

fieldset.declaredBlock{
  padding:var(--sp-4);
  margin-bottom:var(--sp-4);
  background:var(--surface-raised);
  border:1px solid var(--border-soft);
  border-radius:var(--r-md);
}
legend.declaredBlock__head{
  display:flex; align-items:center; gap:var(--sp-2);
  margin-bottom:var(--sp-3);
  font-weight:800; font-size:var(--fs-md);
}

.declaredGrid{ display:flex; flex-direction:column; gap:var(--sp-2); }
.declaredRow{
  display:grid; grid-template-columns:1fr 170px;
  align-items:center; gap:var(--sp-3);
  padding:var(--sp-2) 0;
}
.declaredRow + .declaredRow{ border-top:1px solid var(--border-soft); }
.declaredRow__label{
  display:flex; align-items:baseline; gap:6px;
  font-family:var(--font-title-family);
  font-size:var(--fs-md); font-weight:700;
  color:var(--muted-strong);
  min-width:0;
}
.declaredRow__qty{ color:var(--faint); font-weight:600; font-size:var(--fs-sm); }
@media (max-width:520px){
  .declaredRow{ grid-template-columns:1fr; gap:6px; }
}

.declaredTotal{
  display:flex; align-items:flex-start; justify-content:space-between; gap:var(--sp-4);
  padding:var(--sp-4);
  border:1px dashed var(--border-strong);
  border-radius:var(--r-md);
  background:var(--surface-sunken);
}
.declaredTotal__texte{ min-width:0; }
.declaredTotal__label{ font-weight:800; font-size:var(--fs-md); }
.declaredTotal__hint{ color:var(--faint); font-size:var(--fs-xs); line-height:1.5; margin-top:4px; }
.declaredTotal__valeur{
  flex:0 0 auto;
  font-family:var(--font-title-family);
  font-size:var(--fs-xl); font-weight:900;
  font-variant-numeric:tabular-nums;
  color:var(--muted-strong);
}
@media (max-width:520px){
  .declaredTotal{ flex-direction:column; }
}

/* Le montant qui varie, annoncé.
   Le prix bougeait sans que rien ne le signale : le visiteur cochait une
   option et devait deviner ce qu'elle lui coûtait. La pastille apparaît à
   côté du total, monte et s'efface. C'est le seul moment de récompense du
   parcours, il valait la peine d'être rendu visible. */
.priceDelta{
  position:absolute; right:var(--sp-5); top:var(--sp-3);
  display:inline-flex; align-items:center;
  padding:4px 10px;
  border-radius:var(--r-pill);
  font-size:var(--fs-sm); font-weight:800;
  font-variant-numeric:tabular-nums;
  white-space:nowrap;
  pointer-events:none;
  opacity:0;
}
/* Le couple accent / accent-contrast est celui du bouton principal : c'est le
   seul dont la lisibilite est garantie sur n'importe quelle palette, puisque
   le client choisit lui-meme les deux. --accent-14 / --accent-strong tombait a
   2,82:1 sur un theme sombre. */
.priceDelta--hausse{ background:var(--accent); color:var(--accent-contrast); }
/* Une baisse n'est pas un succes, c'est une information : neutre, et surtout
   lisible. Le couple --ok / --ok-bg ne tenait que 2,91:1, sous le minimum. */
.priceDelta--baisse{ background:var(--surface-sunken); color:var(--muted-strong); }
.priceDelta.is-live{ animation:deltaMonte 1500ms var(--ease-out) forwards; }
@keyframes deltaMonte{
  0%   { opacity:0; transform:translateY(6px)  scale(.9); }
  14%  { opacity:1; transform:translateY(0)    scale(1); }
  70%  { opacity:1; transform:translateY(-4px) scale(1); }
  100% { opacity:0; transform:translateY(-14px) scale(.96); }
}
@media (prefers-reduced-motion: reduce){
  .priceDelta.is-live{ animation:deltaFondu 1500ms linear forwards; }
  @keyframes deltaFondu{ 0%,80%{ opacity:1; } 100%{ opacity:0; } }
}

/* =========================== PANNEAU DE PRIX ============================ */
/* AUX COULEURS DU FORMULAIRE, PAR DÉFAUT (10/10/2026).
   ---------------------------------------------------------------------------
   Le panneau était un aplat sombre calculé (le texte mêlé de noir et de la
   couleur principale) : sur un formulaire clair aux couleurs du déménageur, il
   détonnait — deux palettes sur une même page. Il prend désormais le fond, la
   bordure et le texte des autres cartes ; le montant, lui, garde la couleur
   principale, qui suffit à le mettre hors de concurrence.
   L'ancien aplat reste au choix du déménageur (Configurer › Design › Bloc de
   l'estimation : cfg.prixSombre), sous .pricePanel--sombre ci-dessous. */
.pricePanel{
  position:sticky; top:96px;
  overflow:hidden;
  border:1px solid var(--border-soft);
  background:var(--card);
  box-shadow:var(--shadow-md);
}
.pricePanel__label{ color:var(--muted); }

/* L'ANCRAGE SOMBRE, au choix (.pricePanel--sombre).
   ---------------------------------------------------------------------------
   Ce panneau était une carte claire de plus au milieu de cartes claires : sur
   un écran où le fond, les cartes et les champs sont à moins de 1,3:1 les uns
   des autres, ajouter une nuance ne crée aucune hiérarchie — tout flotte au
   même niveau, et c'est exactement ce qui donne à une interface un air de
   décennie précédente.
   Une masse sombre et saturée règle le problème d'un coup : elle donne à l'œil
   un point d'appui, elle met le montant — la seule chose que le visiteur
   cherche vraiment — hors de toute concurrence, et elle rend au blanc des
   cartes sa fonction de respiration. */
.pricePanel.pricePanel--sombre{
  border-color:transparent;
  background:var(--degrade-sombre);
}
.pricePanel--sombre .pricePanel__label{ color:var(--sur-sombre-doux); }
/* Tout ce qui vit dans le panneau sombre passe en clair : un enfant resté sur
   --muted deviendrait invisible, et ce serait le montant qu'on perdrait. */
.pricePanel--sombre .note,
.pricePanel--sombre .priceHint,
.pricePanel--sombre .priceFacts{ color:color-mix(in oklab, white 72%, var(--text)); }
.pricePanel--sombre .priceFacts strong{ color:var(--sur-sombre); }

/* La pastille « en direct » se rhabille pour le fond sombre.
   Ses couleurs d'origine sont bâties sur du TRANSPARENT — un aplat d'accent à
   8 % qui se compose avec ce qu'il y a dessous. Sur une carte claire, cela
   donnait du violet foncé sur violet très pâle ; sur le panneau sombre, la
   même règle a produit du violet sur violet foncé, mesuré à 1,18:1, soit une
   pastille illisible. Un jeton translucide n'est jamais portable d'un fond à
   l'autre : il faut le redire ici, en clair sur voile blanc. */
.pricePanel--sombre .badge--accent{
  background:color-mix(in oklab, white 18%, transparent);
  border-color:color-mix(in oklab, white 26%, transparent);
  color:var(--sur-sombre);
}
.pricePanel__head{
  display:flex; align-items:center; justify-content:space-between; gap:var(--sp-2);
  padding:var(--sp-4) var(--sp-5) 0;
}
.pricePanel__label{
  font-size:var(--fs-xs); font-weight:800;
  text-transform:uppercase; letter-spacing:.08em;
}
.pricePanel__body{ position:relative; padding:var(--sp-2) var(--sp-5) var(--sp-5); }

.price{
  font-family:var(--font-title-family);
  font-size:var(--fs-display);
  font-weight:900;
  letter-spacing:-.03em;
  line-height:1.05;
  font-variant-numeric:tabular-nums;
  /* Le dégradé reste volontairement proche de --text : certains clients
     choisissent une couleur principale très sombre sur une carte sombre,
     et un dégradé qui filait jusqu'à l'accent rendait les derniers chiffres
     illisibles. */
  /* Aux couleurs du formulaire, le montant prend la couleur principale,
     rapprochée du texte : lisible sur la carte quelle que soit la palette
     (--accent-strong, le même que les liens et les titres de section). */
  color:var(--accent-strong);
}
/* Sur l'aplat sombre, le montant part du blanc et file vers l'accent
   éclairci : il reste lisible de bout en bout, ce que ne garantissait pas un
   dégradé qui rejoignait une couleur choisie par le client. */
.pricePanel--sombre .price{
  background:linear-gradient(135deg,
    #ffffff,
    color-mix(in oklab, white 70%, var(--accent)));
  -webkit-background-clip:text; background-clip:text;
  color:transparent;
}
/* Pulsation quand le montant change (posée par ux.js) */
.price.is-bumped{ animation:priceBump var(--dur-slow) var(--ease-spring); }
@keyframes priceBump{
  0%{ transform:scale(1); }
  38%{ transform:scale(1.07); }
  100%{ transform:scale(1); }
}
/* Halo qui pulse une fois à chaque recalcul */
.pricePanel.is-bumped::after{
  content:"";
  position:absolute; inset:0;
  background:radial-gradient(60% 50% at 50% 0%, var(--accent-24), transparent 70%);
  animation:haloFade var(--dur-slow) var(--ease-out) forwards;
  pointer-events:none;
}
@keyframes haloFade{ from{ opacity:1; } to{ opacity:0; } }

#priceNote{ display:none; }  /* remplacé par les pastilles ci-dessous */
.priceFacts{
  display:flex; flex-wrap:wrap; gap:6px;
  margin-top:var(--sp-3);
}
.priceHint{
  margin-top:var(--sp-3);
  padding-top:var(--sp-3);
  border-top:1px dashed var(--border-soft);
  color:var(--muted); font-size:var(--fs-xs);
}

/* « Voir le prix », la fine barre collée en bas sur téléphone (partials/
   form.php). Elle déplie le cadre « Estimation TTC » au-dessus d'elle : ce
   cadre ne s'affiche plus en haut du formulaire, où il prenait le premier
   écran (David, 06/11/2026). Replié par défaut ; html.prix-ouvert l'ouvre
   (ux.js, brancherVoirLePrix). Sur ordinateur : la colonne de droite, comme
   avant, et pas de barre. */
.priceBar{
  --priceBar-h:calc(46px + env(safe-area-inset-bottom));
  position:fixed; left:0; right:0; bottom:var(--barreBas-h, 0px); z-index:40;   /* au-dessus de la barre du bas (base.css) */
  display:none;
  align-items:center; justify-content:center; gap:8px;
  width:100%; min-height:var(--priceBar-h); margin:0;
  padding:8px 16px calc(8px + env(safe-area-inset-bottom));
  border:0; border-top:1px solid var(--border-soft); border-radius:0;
  background:color-mix(in oklab, var(--card) 94%, transparent);
  backdrop-filter:blur(14px) saturate(140%);
  -webkit-backdrop-filter:blur(14px) saturate(140%);
  box-shadow:0 -8px 30px -12px color-mix(in oklab, var(--text) 30%, transparent);
  color:var(--accent-strong, var(--accent));
  font:inherit; font-size:var(--fs-sm); font-weight:800;
  cursor:pointer;
  -webkit-tap-highlight-color:transparent;
}
.priceBar:focus-visible{ outline:2px solid var(--accent); outline-offset:-3px; }
.priceBar__chevron{ transition:transform var(--dur) var(--ease-out); transform:rotate(180deg); }
html.prix-ouvert .priceBar__chevron{ transform:none; }
@media (max-width:1040px){
  .priceBar{ display:flex; }
  .pricePanel{ position:static; }
  /* Le cadre quitte le haut du formulaire… */
  .layout__rail{ display:none; }
  /* …et se déplie au-dessus de la barre, quand on la touche. */
  html.prix-ouvert .layout__rail{
    display:block;
    position:fixed; left:0; right:0; z-index:41;
    bottom:calc(var(--barreBas-h, 0px) + 46px + env(safe-area-inset-bottom));
    max-height:min(70vh, 560px); overflow:auto; overscroll-behavior:contain;
    padding:0 10px 8px;
    animation:prixMonte var(--dur) var(--ease-out);
  }
  html.prix-ouvert .layout__rail .pricePanel{
    box-shadow:0 -18px 40px -16px color-mix(in oklab, var(--text) 45%, transparent);
  }
  /* Prix masqué, ou gardé pour la fin : rien à déplier, même ouvert avant. */
  html.prix-ouvert .layout.layout--sansPrix > .layout__rail{ display:none; }
  /* La page garde la place de la barre : elle couvrait les boutons du bas. */
  body:has(.priceBar:not(.hidden)){ padding-bottom:calc(46px + env(safe-area-inset-bottom)); }
}
@keyframes prixMonte{ from{ transform:translateY(16px); opacity:0; } to{ transform:none; opacity:1; } }
@media (prefers-reduced-motion: reduce){
  html.prix-ouvert .layout__rail{ animation:none; }
  .priceBar__chevron{ transition:none; }
}
/* Dans l'espace pro, la bulle de l'assistant se pose juste au-dessus de la
   barre du bas, à droite (assistant.css) : le prix lui laisse sa place. */
@media (max-width:900px){
  body:has(#barreBas):has(#assistant) .priceBar{ padding-right:76px; }
}

/* ========================= NAVIGATION BAS DE PAGE ======================= */
/* Ni fond ni filet (David, 13/10/2026) : la barre se fondait mal dans le site
   du déménageur, où le formulaire est intégré. Seuls les boutons restent. */
.footerBar{
  display:flex; align-items:center; gap:var(--sp-3);
  justify-content:flex-end;
  padding:var(--sp-4) var(--sp-5);
  border:0;
  background:transparent;
}
.footerBar__meta{ margin-right:auto; color:var(--muted); font-size:var(--fs-sm); font-weight:600; }

/* Mention « données personnelles », sous le bouton d'envoi de l'étape 6. */
.privacyNote{
  display:flex; align-items:flex-start; gap:var(--sp-2);
  padding:var(--sp-3) var(--sp-5) var(--sp-4);
  font-size:var(--fs-xs);
}
.privacyNote .icon{ flex:0 0 auto; margin-top:2px; }
.privacyNote a{ color:inherit; text-decoration:underline; }
@media (max-width:520px){
  .footerBar{ flex-wrap:wrap; }
  .footerBar__meta{ flex:1 1 100%; margin:0 0 var(--sp-2); }
  .footerBar .btn{ flex:1 1 0; }
}

/* Indicateur de sauvegarde automatique */
.autosave{
  display:inline-flex; align-items:center; gap:6px;
  color:var(--muted); font-size:var(--fs-xs); font-weight:600;
  opacity:0;
  transition:opacity var(--dur-med);
}
.autosave.is-visible{ opacity:1; }
.autosave::before{ content:"✓"; color:var(--ok); font-weight:900; }

/* ============================ ÉCRAN DE SUCCÈS =========================== */
.successView{
  display:none;
  padding:var(--sp-9) var(--sp-5);
  text-align:center;
}
.successView.is-visible{ display:block; animation:stepInFwd var(--dur-slow) var(--ease-out); }
.successView__mark{
  width:96px; height:96px; margin:0 auto var(--sp-5);
  display:grid; place-items:center;
  border-radius:50%;
  background:var(--ok-bg);
  color:var(--ok);
  animation:successPop 620ms var(--ease-spring);
}
@keyframes successPop{
  0%{ transform:scale(0); opacity:0; }
  60%{ transform:scale(1.14); opacity:1; }
  100%{ transform:scale(1); }
}
.successView h2{ font-size:var(--fs-xl); font-weight:900; margin-bottom:var(--sp-2); }
.successView p{ color:var(--muted); max-width:46ch; margin:0 auto; }

/* Confettis (générés par ux.js) */
.confetti{
  position:fixed; inset:0; z-index:9998;
  pointer-events:none; overflow:hidden;
}
.confetti i{
  position:absolute; top:-12px;
  width:9px; height:14px;
  border-radius:2px;
  opacity:0;
  animation:confettiFall var(--fall,2.4s) var(--ease-smooth) forwards;
}
@keyframes confettiFall{
  0%{ opacity:1; transform:translateY(0) rotate(0deg); }
  100%{ opacity:0; transform:translateY(104vh) rotate(720deg); }
}

/* ============================== SQUELETTE =============================== */
/* Affiché tant que la config client n'est pas chargée */
.skeleton{
  background:linear-gradient(90deg,
    var(--surface-sunken) 25%,
    var(--surface-hover) 37%,
    var(--surface-sunken) 63%);
  background-size:400% 100%;
  border-radius:var(--r-sm);
  animation:shimmer 1.5s linear infinite;
}
@keyframes shimmer{ from{ background-position:100% 0; } to{ background-position:0 0; } }
#appRoot.is-loading .step,
#appRoot.is-loading .footerBar{ opacity:.4; pointer-events:none; }

/* ===================== Objets courants avec photo ======================== */
.itemCard--withImg{ padding-left:var(--sp-3); }
/* 56 px à l'écran pour une photo de 240 px : nette sur un écran fin. */
.itemCard__img{
  width:56px; height:56px; flex:0 0 auto;
  object-fit:cover;
  border-radius:var(--r-sm);
  background:var(--surface-sunken);
}

/* ===========================================================================
   VOS OBJETS, PIÈCE PAR PIÈCE
   Un onglet par pièce (form.js, renderCommonItems), la grille de la pièce
   ouverte dessous. Sur téléphone, les onglets défilent de côté, comme la barre
   du professionnel. La pastille dit combien d'objets sont comptés dans la pièce :
   on voit d'un coup d'œil celles qu'on n'a pas encore faites.
   =========================================================================== */
.piecesOnglets{
  display:flex; gap:4px; flex-wrap:nowrap;
  overflow-x:auto; scrollbar-width:none;
  /* Largeur nulle pour le calcul, toute la place à l'affichage : sans cela, la
     longueur naturelle des onglets (près de 1 000 px) élargissait l'étape
     entière, et le formulaire débordait de l'écran du téléphone. */
  width:0; min-width:100%;
  margin:0 0 var(--sp-3); padding:4px;
  background:var(--surface-sunken);
  border-radius:var(--r-pill);
}
.piecesOnglets::-webkit-scrollbar{ display:none; }
.piecesOnglets[hidden]{ display:none; }
.pieceOnglet{
  display:inline-flex; align-items:center; gap:7px; flex:0 0 auto;
  min-height:40px; padding:8px 14px;
  border:0; border-radius:var(--r-pill);
  background:transparent; color:var(--muted-strong);
  font:inherit; font-size:var(--fs-sm); font-weight:700; white-space:nowrap;
  cursor:pointer;
  transition:background var(--dur-fast), color var(--dur-fast), box-shadow var(--dur-fast);
}
.pieceOnglet:hover{ background:var(--surface-hover); color:var(--text); }
/* La pièce ouverte : un aplat ET un trait — la couleur seule ne suffit pas. */
.pieceOnglet[aria-selected="true"]{
  background:var(--card); color:var(--text);
  box-shadow:var(--shadow-xs), inset 0 -2px 0 var(--accent);
}
.pieceOnglet:focus-visible{ outline:2px solid var(--accent); outline-offset:2px; }
.pieceOnglet__compte{
  display:inline-grid; place-items:center;
  min-width:20px; height:20px; padding:0 6px;
  border-radius:var(--r-pill);
  background:var(--accent); color:var(--accent-contrast);
  font-size:11px; font-weight:800; line-height:1;
}
.pieceOnglet__compte[hidden]{ display:none; }

/* La photo que le visiteur joint d'un objet compté : une ligne sous la carte. */
.itemCard__photo{
  flex:1 1 100%;
  display:flex; align-items:center; flex-wrap:wrap; gap:var(--sp-2);
  padding-top:var(--sp-2);
  border-top:1px dashed var(--border-soft);
}
.itemCard__photo[hidden]{ display:none; }
.itemCard__photoBtn{
  display:inline-flex; align-items:center; gap:6px;
  min-height:34px; padding:6px 12px;
  border:1px solid var(--border); border-radius:var(--r-pill);
  background:var(--surface); color:var(--text);
  font:inherit; font-size:var(--fs-xs); font-weight:700;
  cursor:pointer;
}
.itemCard__photoBtn:hover{ border-color:var(--accent); color:var(--accent-strong); }
.itemCard__photoBtn[disabled]{ opacity:.5; cursor:not-allowed; }
.itemCard__facultatif{ font-weight:500; color:var(--muted); }
.itemCard__maPhoto{ width:44px; height:44px; object-fit:cover; border-radius:var(--r-sm); }
.itemCard__photoOk{ margin-right:auto; color:var(--ok); font-size:var(--fs-xs); font-weight:700; }
.itemCard__photoEtat{ flex:1 1 100%; color:var(--muted); font-size:var(--fs-xs); }
.itemCard__photoEtat:empty{ display:none; }
.objetsPhotosNote{ margin-top:var(--sp-3); }

/* ============ Repli quand le calcul de distance est indisponible ========= */
/* Le module doit se dégrader, pas tomber : on le dit au visiteur et on lui
   rend la main sur le champ kilomètres. */
.distanceCard--fallback{
  background:var(--warn-bg);
  border-color:color-mix(in oklab, var(--warn) 45%, transparent);
}
.distanceCard--fallback .distanceCard__icon{ color:var(--warn); }

/* ===========================================================================
   LES PHOTOS D'ACCES
   ===========================================================================
   Ce qui coute cher a un demenageur, ce n'est presque jamais le volume : c'est
   l'acces. Ces blocs demandent une photo la ou l'oeil vaut mieux qu'un chiffre
   — l'ascenseur portes ouvertes, la cage d'escalier, la rue devant la porte.

   Ils vivent DANS les panneaux de lieu, sous les champs qu'ils completent : une
   photo d'ascenseur demandee a trois etapes de la question « ascenseur ? » ne
   se rattacherait a rien dans la tete du visiteur.
   =========================================================================== */

.photoGroupe{
  display:flex; flex-direction:column; gap:var(--sp-2);
  margin-top:var(--sp-4);
  padding-top:var(--sp-4);
  border-top:1px dashed var(--border-soft);
}

.photoBloc{
  display:grid;
  grid-template-columns:1fr auto;
  grid-template-areas:"texte actions" "apercu apercu" "etat etat";
  align-items:center;
  gap:var(--sp-2) var(--sp-3);
  padding:var(--sp-3);
  border:1px solid var(--border-soft);
  border-radius:10px;
  background:var(--surface-sunken);
}

.photoBloc__texte{ grid-area:texte; display:flex; flex-direction:column; gap:2px; min-width:0; }
.photoBloc__titre{ font-weight:600; font-size:.92rem; }
.photoBloc__aide{ font-size:.82rem; line-height:1.45; color:var(--muted); }

.photoBloc__actions{ grid-area:actions; display:flex; gap:var(--sp-2); flex:0 0 auto; }

/* La vignette : assez grande pour juger d'un acces d'un coup d'oeil, pas assez
   pour pousser le bouton « Continuer » hors de l'ecran sur un telephone. */
.photoBloc__apercu{
  grid-area:apercu;
  display:block; width:100%; max-height:190px;
  object-fit:cover; object-position:center;
  border-radius:8px; border:1px solid var(--border);
}

.photoBloc__etat{ grid-area:etat; margin:0; font-size:.8rem; color:var(--muted); }
.photoBloc--pleine{ border-color:var(--border-strong); }

/* Un bouton discret, a la taille d'une ligne de champ. */
.btn--sm{ padding:6px 12px; font-size:.85rem; }

/* Sur telephone, le bouton passe sous le texte : cote a cote, le libelle
   « Ajouter une photo » comprimait l'explication a deux mots par ligne. */
@media (max-width:560px){
  .photoBloc{
    grid-template-columns:1fr;
    grid-template-areas:"texte" "actions" "apercu" "etat";
    align-items:stretch;
  }
  .photoBloc__actions .btn{ flex:1 1 auto; }
}

/* ===========================================================================
   LA DISPONIBILITE DE LA DATE
   ===========================================================================
   Un avertissement, pas un mur : la demande part quand meme. Bloquer ferait
   perdre des demandes que le demenageur aurait peut-etre voulu prendre — en
   decalant d'un jour, en sortant un second camion, en appelant un confrere.
   =========================================================================== */

.chargeNote{
  margin:var(--sp-2) 0 0 0;
  padding:var(--sp-2) var(--sp-3);
  border-radius:8px;
  font-size:.86rem; line-height:1.5;
}
.chargeNote--info{ display:none; }
.chargeNote--alerte{
  background:#fff6e8;
  border:1px solid #f0d3a4;
  color:#7a4a08;
}

/* Le jour propose se prend d'un clic : retaper une date qu'on vient de lire
   est le genre de petite friction qui fait abandonner. */
.chargeNote__lien{
  padding:0; border:0; background:none;
  font:inherit; font-weight:700; color:inherit;
  text-decoration:underline; cursor:pointer;
}
.chargeNote__lien:hover{ text-decoration:none; }

/* ============================================================================
   INTÉGRATION AU SITE (Configurer › Design — core.js, applyIntegration)
   Posé sur <html> ; limité au formulaire (.layout, .progressRail), pour que la
   page Configurer, qui rend aussi le formulaire en aperçu, garde ses cartes.
   ============================================================================ */

/* Sans cadre : les étapes et l'estimation n'ont plus ni fond, ni bordure, ni
   ombre — elles prennent le fond de la page du site, comme son propre contenu.
   L'estimation sombre (cfg.prixSombre) garde son aplat : c'est un choix. */
html.sans-cadre .layout .card.step,
html.sans-cadre .layout .successView,
html.sans-cadre .layout .pricePanel:not(.pricePanel--sombre){
  background:transparent;
  border-color:transparent;
  box-shadow:none;
}
html.sans-cadre .layout .card.step > .pad,
html.sans-cadre .layout .card.step > .step__head{ padding-left:0; padding-right:0; }
html.sans-cadre .layout .pricePanel:not(.pricePanel--sombre) .pricePanel__head,
html.sans-cadre .layout .pricePanel:not(.pricePanel--sombre) .pricePanel__body{ padding-left:0; padding-right:0; }

/* Étapes compactes : une fine barre et « Étape 2 sur 7 », sans le libellé ni
   les pastilles — sous l'en-tête du site, elles faisaient doublon. */
html.etapes-compactes .progressRail__label,
html.etapes-compactes .progressRail .steps{ display:none; }
html.etapes-compactes .progressRail__head{ justify-content:flex-end; margin-bottom:var(--sp-2); }
html.etapes-compactes .progressRail{ margin-bottom:var(--sp-4); }
html.sans-cadre .layout .card.step > .footerBar{ padding-left:0; padding-right:0; }
