/* ============================================================
   PALETTE LAB — theme "laboratoire experimental froid"
   Fond degrade glace + halos flous, monospace unique (JetBrains Mono),
   geometrie nette (coins quasi droits), le moins d'encadrements
   possible : les elements se lisent par leur position, pas par une
   boite. Les tokens ci-dessous redefinissent ceux de style.css (partage
   avec le generateur) — sans consequence pour ce dernier puisque ce
   fichier n'est charge que sur cette page.
   ============================================================ */

:root {
  --bg: #eef1f3;
  --panel: #f7fafb;
  --border: #ccd5da;
  --border-strong: #a9b5bb;
  --text: #0e1116;
  --muted: #626c73;
  --faint: #8b959b;
  --accent: #0098c9;
  --good-text: #3f6b4a;
  --warn-text: #8a6a1f;
  --loose-text: #a1483f;
  --s1: 8px; --s2: 16px; --s3: 24px; --s4: 40px; --s5: 64px; --s6: 104px;
  /* Header (nav pilule + Connect) : anthracite dedie, plus neutre/gris que --text qui a un
     leger biais bleu — reserve a ces deux elements. */
  --header-ink: #383838;
  --header-ink-hover: #454545;
  --header-active-pill: #B8B4B3;
}

body {
  font-family: 'JetBrains Mono', ui-monospace, 'SF Mono', monospace;
  background: #ffffff;
  color: var(--text);
  position: relative;
  overflow-x: hidden;
}
main { position: relative; z-index: 1; }

/* Bandeau superieur : logo seul (pas de titre/texte), les onglets remontent sur la meme
   ligne — plus de trait de separation avec le contenu. Colle en haut au scroll (verre
   depoli, coherent avec le dock/les popovers), et se fond en bas plutot que de couper
   le contenu qui defile net — un degrade prolonge son bord bas dans la page. */
header {
  position: sticky; top: 0; z-index: 50;
  border-bottom: none; padding: 28px 28px;
  /* Colonnes symetriques (1fr / auto / 1fr) plutot que (auto / 1fr / auto) : la nav se cale
     sur le vrai centre du bandeau quelle que soit la largeur de la marque vs. Connect, au
     lieu de se centrer entre elles (et deriver des que l'une pese plus que l'autre). */
  display: grid; grid-template-columns: 1fr auto 1fr; grid-template-areas: "brand nav connect";
  align-items: center; gap: 24px;
  background: rgba(255,255,255,0.7);
  backdrop-filter: blur(14px) saturate(160%);
  -webkit-backdrop-filter: blur(14px) saturate(160%);
}
header::after {
  content: ''; position: absolute; top: 100%; left: 0; right: 0; height: 56px;
  background: linear-gradient(to bottom, rgba(255,255,255,0.55), rgba(255,255,255,0));
  pointer-events: none;
}
/* Vrai lien (vers l'onglet "home") : curseur et leger effet au survol pour le signaler.
   Plus de nom a cote du logo (retire, voir CLAUDE.md) — le logo seul porte la marque,
   d'ou align-items:center ici ET sur le header pour qu'il reste cale sur l'axe vertical
   de la pilule d'onglets malgre sa nouvelle taille. */
.brand {
  grid-area: brand; justify-self: start; display: flex; align-items: center;
  flex-shrink: 0; cursor: pointer; transition: opacity 0.15s ease;
  text-decoration: none;
}
.brand:hover { opacity: 0.75; }
.logo-link { display: flex; align-items: center; line-height: 0; opacity: 0.88; }
.logo-mark { width: 50px; height: 50px; }
/* Meme registre que .catalog-switch-btn/.account-switch-btn (contour, pas plein ; pas la
   hauteur du menu central) — demande explicite, desktop et mobile. */
.btn-connect {
  flex-shrink: 0;
  background: none; border: 1px solid var(--border); color: var(--text); border-radius: 999px;
  height: 36px; padding: 0 14px; box-sizing: border-box;
  display: inline-flex; align-items: center; justify-content: center;
  font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif;
  font-size: 12px; font-weight: 600; cursor: pointer;
  transition: border-color 0.15s ease, background 0.15s ease;
}
.btn-connect:hover { border-color: var(--border-strong); background: rgba(14,17,22,0.04); }
/* [hidden] + display explicite : piege deja documente ailleurs dans ce fichier — l'attribut
   seul ne suffit plus a cacher l'element une fois que la regle pose son propre "display". */
.btn-connect[hidden] { display: none; }

main { max-width: 1320px; gap: var(--s5); padding-top: var(--s6); }

/* Flottaison au defilement : chaque grande section de chaque onglet apparait (fondu +
   leger flottement vers le haut) quand elle entre dans la fenetre, plutot que d'etre deja
   la au chargement — donne l'impression de "deposer" les blocs sur la page en descendant.
   Un seul passage (pas de va-et-vient au re-scroll), et desactive si l'utilisateur prefere
   moins de mouvement. */
.reveal { opacity: 0; transform: translateY(28px); transition: opacity 0.7s cubic-bezier(.22,1,.36,1), transform 0.7s cubic-bezier(.22,1,.36,1); }
.reveal.reveal-visible { opacity: 1; transform: translateY(0); }
@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1; transform: none; transition: none; }
}

/* .panel est volontairement sans fond/bordure/ombre par defaut : la structure se lit par
   l'espacement (main { gap }) plutot que par des boites empilees. */
.panel { background: none; border: none; border-radius: 0; padding: 0; }
.panel h2 {
  margin: 0 0 var(--s3); font-size: 11px; font-weight: 600; color: var(--faint);
  text-transform: uppercase; letter-spacing: 0.14em;
}
.hint { color: var(--muted); font-size: 12px; margin: 0 0 10px; line-height: 1.5; }

/* Boutons : redefinis ici (pas seulement via --accent) car le style "rempli" doit rester
   reserve a l'encre (var(--text)), le cyan --accent etant un signal ponctuel, pas une
   couleur de bouton generique. */
.btn-primary, button#generate-btn {
  background: var(--text); color: #f7fafb; border: 1px solid var(--text); border-radius: 8px;
  font-family: inherit; font-weight: 500; letter-spacing: 0.02em;
}
.btn-primary:hover, button#generate-btn:hover { background: #262b33; border-color: #262b33; }
.btn-secondary {
  background: transparent; color: var(--muted); border: 1px solid var(--border); border-radius: 8px;
  font-family: inherit; font-weight: 500; letter-spacing: 0.02em;
}
.btn-secondary:hover { border-color: var(--border-strong); color: var(--text); }

/* Actions secondaires en lien texte plutot qu'en bouton borde — moins de "cases", l'action
   se lit par sa position dans la barre plutot que par un contour. Reserve aux actions
   peu frequentes (Variantes, Copier, Sauvegarder, Supprimer...). */
.action-link {
  background: none; border: none; padding: 4px 0; color: var(--muted);
  font-family: inherit; font-size: 12px; font-weight: 500; letter-spacing: 0.02em;
  cursor: pointer; text-decoration: underline; text-decoration-color: var(--border-strong);
  text-underline-offset: 3px;
}
.action-link:hover { color: var(--text); text-decoration-color: var(--text); }
.action-link.strong { color: var(--text); font-weight: 600; }
.action-link[hidden] { display: none; }
.action-link.confirming, .action-link.confirming:hover {
  color: var(--loose-text); text-decoration-color: var(--loose-text); font-weight: 600;
}

.hint-small { font-size: 11px; opacity: 0.85; max-width: 62ch; }

/* Termes techniques entre crochets plutot qu'en pastille pleine — l'ecriture porte
   l'info, pas un fond colore (touche a match-quality/zone-quality/owned-pick-tag qui
   restent par ailleurs inline, sans changer leur position dans le flux). */
.match-quality, .zone-quality, .owned-pick-tag {
  background: none; border: none; border-radius: 0; padding: 0;
  font-size: 10px; font-weight: 600; white-space: nowrap; letter-spacing: 0.04em;
}
.match-quality::before, .zone-quality::before, .owned-pick-tag::before { content: '['; }
.match-quality::after, .zone-quality::after, .owned-pick-tag::after { content: ']'; }
.match-quality.excellent, .zone-quality-homogene { color: var(--good-text); }
.match-quality.good, .zone-quality-leger { color: var(--warn-text); }
.match-quality.loose, .zone-quality-irregulier { color: var(--loose-text); }
.owned-pick-tag { color: var(--good-text); }
.owned-pick-tag.shared-pick-tag { color: var(--accent); }

/* Formulaire de nom, centre sous la palette resserree (voir .hero-palette-row.saving) — champ
   au meme habillage que les autres saisies de l'appli (input blanc borde, cf. .add-color-hex/
   .popover-hex-input), et validation par icone plutot que par lien texte "Enregistrer". */
.export-save-form {
  display: flex; flex-direction: column; align-items: center; gap: 10px;
  margin: 18px auto 0; width: fit-content; max-width: 340px;
}
.export-save-form[hidden] { display: none; }
.export-save-input-row { display: flex; align-items: center; gap: 8px; }
.export-save-input-row input {
  width: 220px; background: #fff; color: var(--text); border: 1px solid var(--border);
  border-radius: 8px; padding: 8px 12px; font-family: inherit; font-size: 13px;
  transition: border-color 0.15s;
}
.export-save-input-row input:focus { outline: none; border-color: var(--border-strong); }
.export-save-confirm-btn {
  flex-shrink: 0; width: 34px; height: 34px; border-radius: 50%; border: none;
  background: var(--text); color: #f7fafb; cursor: pointer;
  display: flex; align-items: center; justify-content: center; transition: background 0.15s;
}
.export-save-confirm-btn:hover { background: #262b33; }
.export-save-confirm-btn svg { width: 16px; height: 16px; pointer-events: none; }

/* Onglet Palettes enregistrees : chaque palette reprend TEL QUEL le langage de la barre
   Harmonie chromatique (.hero-palette-bar/.hero-block/.hero-info-btn/.slot-detail-panel,
   definis plus bas) — seuls les elements propres a ce contexte (nom + actions de la
   palette, fleche d'envoi) sont a eux. Grand espacement vertical entre palettes plutot
   qu'un separateur, dans le meme esprit aere que le reste de la page. */
/* Mode "choix pour le melange" (voir pickMixTargetFromPalettes) : un bandeau discret rappelle
   pourquoi on est ici et permet d'annuler — le clic sur une couleur reste l'action normale
   de la case, juste detournee tant que ce mode est actif. */
.mix-pick-banner {
  display: flex; align-items: center; justify-content: center; gap: 14px;
  padding: 10px 16px; margin-bottom: var(--s4); border-radius: 999px;
  background: rgba(0,152,201,0.08); border: 1px solid rgba(0,152,201,0.25);
  font-size: 12px; font-weight: 600; color: var(--accent);
}
.mix-pick-banner[hidden] { display: none; }

#saved-palettes-list { display: flex; flex-direction: column; gap: var(--s6); }
.saved-palette-entry { display: flex; flex-direction: column; gap: 12px; }
.saved-palette-header { display: flex; align-items: center; gap: 2px; }
.saved-palette-name { font-size: 15px; font-weight: 700; margin-right: 4px; }
/* Doublon mobile de la fleche/du "+" (voir buildSavedPaletteHtml) : cache par defaut, ne
   s'affiche que sous 640px (regle plus bas) — au desktop, les vrais boutons restent uniquement
   dans .hero-palette-row. */
.saved-palette-header-mobile-actions { display: none; }
.saved-palette-icon-btn {
  width: 24px; height: 24px; border-radius: 50%; border: none; background: none;
  color: var(--faint); cursor: pointer; display: flex; align-items: center; justify-content: center;
  font-size: 15px; line-height: 1; transition: background 0.15s, color 0.15s;
}
.saved-palette-icon-btn:hover { background: rgba(14,17,22,0.08); color: var(--text); }
.saved-palette-icon-btn svg { width: 13px; height: 13px; pointer-events: none; }
.saved-palette-icon-btn.confirming { color: var(--loose-text); background: rgba(161,72,63,0.1); }

/* Fleche de renvoi vers Harmonie chromatique : petit glissement + fondu avant de changer
   d'onglet, purement symbolique (la palette enregistree n'est jamais modifiee). */
.hero-palette-row.saved-palette-sending { transition: transform 0.3s ease, opacity 0.3s ease; transform: translateX(-18px); opacity: 0; }

.add-color-form { display: flex; gap: 8px; margin-top: -2px; flex-wrap: wrap; align-items: center; }
.add-color-form[hidden] { display: none; }
.add-color-form .add-color-hex {
  flex: 1; min-width: 140px; background: #fff; color: var(--text); border: 1px solid var(--border);
  border-radius: 8px; padding: 6px 10px; font-family: inherit; font-size: 13px;
}
.add-color-form .add-color-picker {
  width: 36px; height: 34px; padding: 2px; border-radius: 8px; border: 1px solid var(--border);
  background: #fff; cursor: pointer; flex-shrink: 0;
}
/* Bandeau visible pour les stats — meme habillage verre depoli que les autres bandeaux/
   popovers du site (info banner de Melange, menus flottants). La roue de fidelite reste en
   dehors, juste en dessous. */
/* Le descriptif du choix selectionne dans la roue Need : c'est le texte qui doit permettre de
   DECIDER, donc lisible (13.5px, interligne large) et centre sous la roue, pas un sous-titre. */
.need-choice-detail {
  max-width: 560px; margin: 14px auto 4px; text-align: center;
}
.need-choice-detail[hidden] { display: none; }
/* Police Inter (page marketing), pas la mono heritee du corps de page — demande explicite,
   2026-09-24, textes desormais fixes (voir needChoiceDescription). Marge du haut agrandie
   (2px -> 14px) pour respirer sous le nom du choix (#fidelity-wheel-name, deja en Inter/22px/
   800), et gap entre les 2 phrases legerement elargi (4px -> 6px) pour les lire comme 2 idees
   distinctes plutot qu'un bloc compact. */
.need-choice-detail p {
  margin: 0; font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif;
  font-size: 14px; line-height: 1.6; color: var(--muted);
}
.need-choice-detail p + p { margin-top: 6px; }

/* min-height + centrage vertical : sans ca, le bandeau changeait de hauteur selon l'etat
   affiche (mesure : 108px avec les stats, seulement 93px pendant "calcul en cours", un saut
   de 15px bien visible a chaque debut/fin de calcul) — demande explicite a corriger. La
   valeur vient de la hauteur reelle du bloc de stats (le plus haut des deux etats),
   getBoundingClientRect + une petite marge. */
.need-stats-banner {
  min-height: 112px;
  margin-bottom: 20px; padding: 24px 28px;
  display: flex; align-items: center; justify-content: center;
  background: rgba(255,255,255,0.6);
  backdrop-filter: blur(20px) saturate(180%);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  border: 1px solid rgba(255,255,255,0.7);
  border-radius: 20px;
  box-shadow: 0 1px 2px rgba(14,17,22,0.05), 0 24px 60px rgba(14,17,22,0.16);
}

/* ---- Ecran "moteur a l'arret" (2026-09-21) : meme panneau verre depoli que .need-stats-banner
   (memes valeurs, pas une nouvelle recette), juste plus haut/plus pad puisqu'il porte icone +
   titre + texte + CTA plutot que 4 chiffres. ---- */
/* Bandeau reduit a l'icone + au bouton (2026-09-22, demande explicite "retirons cette phrase" —
   le titre "Ton moteur... a l'arret" est retire, le texte explicatif descend dans son propre
   module juste en dessous, voir .need-idle-explainer plus bas). Hauteur mecaniquement plus
   courte qu'avant (moins de contenu), plus besoin de min-height fige. */
.need-idle {
  padding: 40px 32px;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 18px;
  text-align: center;
  background: rgba(255,255,255,0.6);
  backdrop-filter: blur(20px) saturate(180%);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  border: 1px solid rgba(255,255,255,0.7);
  border-radius: 20px;
  box-shadow: 0 1px 2px rgba(14,17,22,0.05), 0 24px 60px rgba(14,17,22,0.16);
}
.need-idle-icon { width: 34px; height: 34px; color: var(--border-strong); }
/* Repris une 2e fois (2026-09-24, retour explicite "je n'aime toujours pas le bouton... pas une
   pastille noir mais plutot transparent design epure") : le remplissage encre + l'ombre portee
   de la 1ere passe (voir historique) sont retires — fond transparent, simple trait, "POOL" en
   majuscules dans la police Inter de la page marketing (pas la mono du corps de page) plutot
   que le 500/inherit de .btn-primary. Pastille toujours pleinement arrondie (999px, demande
   maintenue de la 1ere passe). .btn-primary reste en classe sur le bouton (HTML) mais chacune
   de ses proprietes visuelles est explicitement reprise ici pour la neutraliser — pas de
   nouvelle classe, cette carte etant le seul endroit du site a vouloir ce traitement. */
.need-idle-btn {
  background: transparent; color: var(--text); border: 1px solid var(--border-strong);
  border-radius: 999px; padding: 14px 40px; cursor: pointer;
  font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif;
  font-size: 13px; font-weight: 600; letter-spacing: 0.14em; text-transform: uppercase;
  box-shadow: none;
  transition: background 0.15s ease, border-color 0.15s ease;
}
.need-idle-btn:hover { background: rgba(14,17,22,0.05); border-color: var(--text); }
/* Bandeau trop "long" en mobile (signale explicitement, 2026-09-24) : le padding vertical
   40px pense pour desktop (carte large, ~1000px+) ne raccourcit pas avec la largeur — sur une
   colonne mobile a 327px, la carte se retrouve proportionnellement bien plus haute que large.
   Padding et espacements resserres pour une carte plus compacte/carree, sans rien retirer de
   son contenu (icone + pastille). */
@media (max-width: 640px) {
  .need-idle { padding: 26px 24px; gap: 14px; }
  .need-idle-icon { width: 28px; height: 28px; }
  .need-idle-btn { padding: 14px 38px; }
}

/* ---- Module explicatif (2026-09-22) : sous le bandeau, dans l'espace vide qu'il laissait —
   meme famille typo que la page marketing (Inter, jamais la mono du corps de page ici, demande
   explicite), nuances noir/gris (var(--text) pour les titres, var(--muted) pour le corps) —
   aucune nouvelle couleur introduite. Centre sur la page, largeur contrainte en ch pour rester
   lisible malgre le centrage (meme choix que les sous-textes de bandeau marketing,
   .home-pitch-sub etc., qui centrent deja des paragraphes multi-lignes sans probleme de
   lisibilite sur ce site). Tres aere : gap genereux entre titre/eyebrow/etapes et entre chaque
   etape (var(--s5), 64px), pour que le contenu plus riche demande explicitement ne se sente
   jamais tasse. ---- */
.need-idle-explainer {
  max-width: 640px; margin: var(--s6) auto 0;
  display: flex; flex-direction: column; align-items: center; text-align: center;
}
/* Specificite #id .class (pas juste .need-idle-explainer-title) : bat expres la regle globale
   .panel h2 (uppercase/11px/faint, pensee pour les petits titres de section) qui l'emportait
   sinon malgre le sous-classement — meme piege deja rencontre sur ce titre avant de passer par
   un <span> ; ici un vrai <h2> est preferable semantiquement, donc on neutralise la regle
   globale par specificite plutot que de changer de balise. */
#panel-need-idle .need-idle-explainer-title {
  font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif; font-weight: 500;
  text-transform: none; letter-spacing: -0.01em;
  font-size: clamp(22px, 2.8vw, 30px); line-height: 1.3; color: var(--text);
  max-width: 26ch; margin: 0 0 var(--s5); text-wrap: balance;
}
.need-idle-explainer-eyebrow {
  font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif; font-size: 13px; font-weight: 600;
  letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted);
  margin-bottom: var(--s4);
}
.need-idle-steps { display: flex; flex-direction: column; gap: var(--s5); width: 100%; }
.need-idle-step { display: flex; flex-direction: column; align-items: center; gap: 10px; }
.need-idle-step-icon { width: 28px; height: 28px; color: var(--border-strong); margin-bottom: 4px; }
.need-idle-step-title {
  font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif; font-size: 17px; font-weight: 600;
  color: var(--text);
}
.need-idle-step-desc {
  font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif; font-size: 15px; font-weight: 400;
  color: var(--muted); line-height: 1.6; max-width: 50ch; margin: 0;
}
/* Etape 3 : 3 paragraphes distincts (pas un seul bloc dense) — petit ecart entre eux pour
   qu'ils se lisent comme 3 idees separees plutot qu'un mur de texte, demande explicite
   "aération". */
.need-idle-step-desc + .need-idle-step-desc { margin-top: 6px; }
.need-stats {
  width: 100%;
  display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
  gap: 24px; text-align: center;
}
.need-stats[hidden] { display: none; }
.need-stat strong { display: block; font-size: 34px; font-weight: 800; color: var(--text); line-height: 1.1; }
.need-stat span { font-size: 11px; color: var(--muted); }

/* Etat de chargement DANS le bandeau (remplace les stats, meme espace) : un texte discret et
   une fine barre qui se remplit — le minimum pour dire "ca travaille", sans rien de bavard.
   width:100% + le min-height/centrage du parent (.need-stats-banner) : ce bloc est plus court
   que .need-stats par nature (pas de gros chiffres), mais garde maintenant le bandeau a
   exactement la meme hauteur au lieu de le laisser retrecir. */
.need-stats-loading {
  width: 100%;
  display: flex; flex-direction: column; align-items: center; gap: 10px;
  padding: 7px 0; text-align: center;
}
.need-stats-loading[hidden] { display: none; }
.need-stats-loading-label {
  font-size: 11px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--faint);
}
.need-stats-loading-track {
  width: min(280px, 70%); height: 5px; border-radius: 999px;
  background: rgba(14,17,22,0.08); overflow: hidden;
}
.need-stats-loading-bar {
  height: 100%; width: 0%; border-radius: 999px;
  background: var(--accent);
  /* Largeur pilotee en JS (showNeedLoading) au rythme du calcul reel — cette transition ne
     fait que lisser les pas de la boucle requestAnimationFrame entre eux. */
  transition: width 0.12s linear;
}
/* % sous la barre (demande explicite) — memes chiffres tabulaires que les stats numeriques du
   site pour que "12%" -> "13%" ne fasse pas sauter la largeur du texte d'un pixel a l'autre. */
.need-stats-loading-pct {
  font-size: 12px; font-weight: 700; color: var(--muted); font-variant-numeric: tabular-nums;
}

/* Reapparition des stats une fois le calcul termine : un fondu tres bref, jamais rejoue au
   simple changement de palier (voir hideNeedLoading, seul point qui pose cette classe). */
@keyframes needStatsAppear {
  from { opacity: 0; transform: translateY(3px); }
  to { opacity: 1; transform: translateY(0); }
}
.need-stats.appearing { animation: needStatsAppear 0.4s cubic-bezier(.22,1,.36,1); }

/* Palettes incluses dans le calcul : replie derriere un bouton (plus de liste toujours
   visible en haut de l'onglet) — un menu qui s'ouvre EN FLUX (pas en survol flottant) : le
   reste de la page (stats, roue de fidelite...) descend avec lui plutot que de rester fige
   derriere, et son propre defilement interne (overflow-y) n'entre plus en conflit avec un
   positionnement absolu/z-index. */
.need-palette-dropdown { display: block; }
.need-palette-toggle-btn {
  display: flex; align-items: center; gap: 10px; width: max-content;
  background: none; border: 1px solid var(--border); border-radius: 999px;
  padding: 10px 16px; font-size: 13px; font-weight: 600; color: var(--text);
  cursor: pointer; transition: border-color 0.15s, background 0.15s;
  /* Meme police que la page marketing (Inter) plutot que la mono heritee du corps de page —
     demande explicite, 2026-09-24 (cohere avec le meme changement deja fait sur le bouton
     "POOL" juste en dessous). */
  font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif;
}
.need-palette-toggle-btn:hover { border-color: var(--border-strong); background: var(--panel); }
.need-palette-toggle-btn[aria-expanded="true"] { border-color: var(--text); }
.need-palette-toggle-count { font-size: 12px; font-weight: 600; color: var(--muted); }
.need-palette-toggle-chevron { width: 14px; height: 14px; color: var(--faint); transition: transform 0.2s ease; }
.need-palette-toggle-btn[aria-expanded="true"] .need-palette-toggle-chevron { transform: rotate(180deg); }

/* Menu largement agrandi (voir buildNeedChoices... non, renderPaletteSelector) pour bien voir
   les teintes de chaque palette — les bandes de couleur sont l'objet principal du menu, pas
   un simple rappel a cote du nom. */
.need-palette-menu {
  margin-top: 10px; width: min(760px, 100%); max-height: 560px; overflow-y: auto;
  padding: 20px; display: flex; flex-direction: column; gap: 14px;
  background: rgba(255,255,255,0.6);
  backdrop-filter: blur(20px) saturate(180%);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  border: 1px solid rgba(255,255,255,0.7); border-radius: 20px;
  box-shadow: 0 1px 2px rgba(14,17,22,0.05), 0 24px 60px rgba(14,17,22,0.16);
}
.need-palette-menu[hidden] { display: none; }
.need-palette-menu-toolbar { display: flex; align-items: center; gap: 10px; }
.need-palette-all-btn {
  flex: none; background: none; border: 1px solid var(--border); border-radius: 999px;
  padding: 5px 14px; font-family: inherit; font-size: 11px; font-weight: 700; letter-spacing: 0.04em;
  text-transform: uppercase; color: var(--muted); cursor: pointer;
  transition: border-color 0.15s, color 0.15s;
}
.need-palette-all-btn:hover { border-color: var(--border-strong); color: var(--text); }
.need-palette-menu-status { font-size: 11px; color: var(--muted); }
.palette-selector-row-list { display: flex; flex-direction: column; gap: 18px; }

/* Une pastille a cocher a gauche inclut/exclut la palette entiere — plus de clic sur la ligne
   elle-meme, qui doit rester libre pour que chaque teinte, plus bas, soit individuellement
   cliquable. Meme gabarit de pastille (cercle + coche) que les teintes, en plus grand. */
.palette-selector-row { display: flex; align-items: flex-start; gap: 14px; width: 100%; }
.palette-selector-check {
  flex: none; width: 24px; height: 24px; margin-top: 2px; padding: 0;
  border-radius: 50%; border: 1.5px solid var(--border-strong); background: #fff;
  display: flex; align-items: center; justify-content: center; cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease;
}
.palette-selector-check:hover { border-color: var(--text); }
.palette-selector-check svg { width: 13px; height: 13px; color: #fff; opacity: 0; transition: opacity 0.15s ease; }
.palette-selector-check.checked { background: var(--text); border-color: var(--text); }
.palette-selector-check.checked svg { opacity: 1; }
/* Limite Free "Need avec les palettes de demo uniquement" : la pastille devient un cadenas
   (PRO_LOCK_ICON_SVG, trait fin — pas la coche pleine blanche des autres pastilles, d'ou la
   regle de couleur/opacite dediee ci-dessous). */
.palette-selector-check.locked { background: #fff; }
.palette-selector-check.locked svg { width: 13px; height: 13px; color: var(--muted); opacity: 1; }
.palette-selector-row.locked { cursor: pointer; }

/* Chaque palette devient une large bande de couleur (segments egaux quel que soit le nombre
   de teintes) plutot qu'une ligne de petites pastilles + texte — toutes les palettes
   partagent exactement la meme taille de bande. */
.palette-selector-body {
  flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 8px;
  transition: opacity 0.15s ease;
}
.palette-selector-body.excluded { opacity: 0.4; }
.palette-selector-swatch {
  position: relative; display: flex; gap: 1px; border-radius: 10px; overflow: hidden;
  width: 100%; height: 88px;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,0.35);
}
/* Pastille PRO centree SUR la bande de couleur verrouillee (pas a droite de la ligne, demande
   explicite) : positionnee dans .palette-selector-swatch (contexte relatif ci-dessus), pas dans
   .palette-selector-row comme le premier essai. Fond quasi-opaque + ombre (pas le simple lavis
   --accent de .pro-lock-tag ailleurs) : ici elle flotte sur des couleurs de palette arbitraires,
   pas un fond neutre — le lavis pale devenait illisible sur certaines teintes (rose/lavande
   notamment, constate a l'usage). */
.palette-selector-swatch .pro-lock-tag {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 1;
  background: rgba(255,255,255,0.92); box-shadow: 0 1px 3px rgba(14,17,22,0.28);
}
.palette-selector-name { font-size: 13px; font-weight: 600; }

/* Chaque teinte de la bande porte sa propre pastille de retrait (calcul uniquement, jamais la
   palette elle-meme) : reveleée au survol pour ne pas charger visuellement la bande au repos ;
   une teinte deja retiree reste distinguable (bande assombrie) meme hors survol. */
.need-color-swatch { position: relative; flex: 1; display: block; transition: opacity 0.15s ease; }
.need-color-swatch.excluded { opacity: 0.32; }
.need-color-toggle {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
  width: 24px; height: 24px; padding: 0; border-radius: 50%;
  border: 1.5px solid rgba(255,255,255,0.9); background: rgba(14,17,22,0.4);
  display: flex; align-items: center; justify-content: center;
  opacity: 0; transition: opacity 0.15s ease, background 0.15s ease;
  cursor: pointer;
}
.need-color-swatch:hover .need-color-toggle { opacity: 1; }
.need-color-toggle:hover { background: rgba(14,17,22,0.6); }
.need-color-toggle svg { width: 13px; height: 13px; color: #fff; transition: opacity 0.15s ease; }
.need-color-toggle.excluded svg { opacity: 0; }

/* Liste d'achat : meme "puits" de couleur que les cases Harmonie/Melange (aplat + texte en
   surimpression) plutot que de petites cartes bordees. */
.need-pot-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 10px; }
/* Message d'etat vide (aucun pot) : sans ca, il herite d'une seule colonne de la grille
   (~160px) et son texte se retrouve tasse/coupe au lieu de tenir sur une ligne. */
.need-pot-grid .hint { grid-column: 1 / -1; }
.need-pot-card {
  position: relative; min-height: 120px; border-radius: 10px; padding: 16px;
  display: flex; flex-direction: column; justify-content: flex-end; gap: 2px;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,0.35);
}
.need-pot-name { font-size: 14px; font-weight: 800; line-height: 1.25; }
.need-pot-uses { font-size: 11px; opacity: 0.85; }
/* Croix de retrait de "Mon stock" : visible en permanence (pas seulement au survol) — c'est
   la seule action possible sur ces cases, comme pour la couleur ciblee de Paint Mix. */
#my-stock-grid .need-pot-card .hero-remove-btn { opacity: 1; }

.palette-rename-input {
  background: #fff; color: var(--text); border: 1px solid var(--accent);
  border-radius: 8px; padding: 3px 8px; font-size: 15px; font-weight: 700; font-family: inherit;
}

/* Barre d'onglets en pilule sombre (segmented control) : un indicateur clair glisse derriere
   l'onglet actif (positionne en JS via updateTabIndicator, cf. palette_lab.js) plutot qu'un
   fond plat par bouton — la selection se lit comme un objet qui se deplace, pas comme un
   etat qui s'allume/s'eteint. */
/* Regroupe la pastille "PRO" et la pilule d'onglets sur la meme ligne de grille (grid-area:
   nav, reprise du .tab-bar d'origine) : la pastille doit vivre a gauche de "Create" mais dans
   son propre bandeau, pas fusionnee dans la pilule .tab-bar (voir #pro-badge plus bas). */
.tab-bar-row { grid-area: nav; justify-self: center; display: flex; align-items: center; gap: 10px; }
.tab-bar {
  position: relative;
  display: inline-flex; gap: 2px; padding: 5px; margin-bottom: 0;
  border-radius: 999px; background: var(--header-ink);
}
/* Nudge vers la page tarifs (#pricing, onglet marketing) : reprend le meme registre pastille
   que .account-plan-badge.is-premium (seul signal --accent du design system, ici pour annoncer
   Pro plutot que confirmer un abonnement actif). Disparait une fois le compte Pro
   (voir updateProBadge, palette_lab.js) — pas de recouvrement visuel possible avec le badge
   "Pro" de "Mon compte", qui lui reste toujours visible (etat informatif, pas un CTA). */
.pro-badge {
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  height: 47px; padding: 0 16px; border-radius: 999px;
  background: rgba(0,152,201,0.14); color: var(--accent); border: 1px solid rgba(0,152,201,0.3);
  font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif;
  font-size: 11px; font-weight: 700; letter-spacing: 0.06em; cursor: pointer;
  transition: background 0.15s ease;
}
.pro-badge:hover { background: rgba(0,152,201,0.22); }
.pro-badge[hidden] { display: none; }

/* ---- Systeme de verrouillage "Pro" reutilise partout ou une limite Free bloque une action
   (feuille de route phase 2) : icone cadenas + pastille PRO (meme couleurs que .pro-badge),
   soit en petit tag inline (ligne de palette verrouillee, Need), soit en grand recouvrement
   (resultats Paint Mix verrouilles). ---- */
.pro-lock-icon { width: 16px; height: 16px; color: var(--faint); flex: none; }
.pro-lock-tag {
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif;
  font-size: 10px; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase;
  padding: 3px 9px; border-radius: 999px;
  background: rgba(0,152,201,0.14); color: var(--accent);
}
/* Grand recouvrement : couvre tout un bloc de contenu deja rendu (le parent doit etre en
   position:relative) — flouté plutot que masque, pour que la forme du contenu verrouille
   reste devinable (pas de vrais noms/couleurs lisibles dessous pour autant, voir JS). */
.pro-lock-overlay {
  position: absolute; inset: 0; z-index: 5; border-radius: inherit;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px;
  background: rgba(247,250,251,0.75);
  backdrop-filter: blur(2px); -webkit-backdrop-filter: blur(2px);
  cursor: pointer; text-align: center;
}
.pro-lock-overlay .pro-lock-icon { width: 26px; height: 26px; }
.pro-lock-overlay-text { font-size: 12.5px; color: var(--muted); max-width: 220px; margin: 0; }
/* .btn-connect est pense pour un <button> (display par defaut inline-block) — repris ici sur un
   <span>, sans display explicite il reste inline et son height/padding vertical ne centrent pas
   le texte correctement (pastille mal cadree, constate a l'usage). inline-flex + centrage
   corrige, taille legerement reduite pour ce contexte plus compact que le bandeau. */
.mix-connect-pill {
  display: inline-flex; align-items: center; justify-content: center;
  height: 38px; padding: 0 20px; font-size: 11px;
}

/* Limite Free "detail palette" (Create + Mes palettes, 2026-09-18) : ce qui suit le trait dans le
   panneau ⓘ (correspondance, mélange conseillé, bouton "Explorer") reste visible mais flouté pour
   un compte non-Pro, cadenas + pastille PRO par-dessus (voir wrapDetailBelowDivider,
   palette_lab.js) — le header au-dessus du trait, lui, n'est jamais concerné. */
.slot-detail-locked-wrap { position: relative; }
.slot-detail-locked-content { filter: blur(4px); pointer-events: none; user-select: none; }
.slot-detail-locked-wrap .pro-lock-overlay { margin: -4px; }

.tab-bar-indicator {
  position: absolute; top: 0; left: 0; z-index: 0;
  border-radius: 999px; background: var(--header-active-pill);
  transition: transform 0.35s cubic-bezier(.22,1,.36,1), width 0.35s cubic-bezier(.22,1,.36,1);
}
.tab-btn {
  position: relative; z-index: 1; flex: none; min-width: 0;
  background: none; border: none; border-radius: 999px;
  padding: 11px 22px; cursor: pointer; font-size: 12px; font-weight: 600;
  font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif;
  letter-spacing: 0.04em; text-transform: uppercase;
  color: #f7fafb; text-align: center; white-space: nowrap;
}

/* Barre de navigation mobile (inspiree d'Instagram) : icones seules, fixee en bas d'ecran.
   display:none par defaut (desktop) — activee uniquement dans le @media plus bas, jamais
   visible ni chargee visuellement en dehors du mobile. .tab-btn est deja cable par
   setupTabs()/switchTab() (palette_lab.js, cible TOUS les .tab-btn du document) : ces boutons
   recoivent .active et leurs clics changent d'onglet sans JS supplementaire, simplement en
   partageant la classe et data-tab avec la pilule desktop. */
.bottom-tab-bar {
  display: none;
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 60;
  background: rgba(255,255,255,0.92);
  backdrop-filter: blur(14px) saturate(160%);
  -webkit-backdrop-filter: blur(14px) saturate(160%);
  border-top: 1px solid var(--border);
  padding: 6px 4px calc(6px + env(safe-area-inset-bottom, 0px));
}
.bottom-tab-btn {
  flex: 1 1 0; min-height: 48px;
  display: flex; align-items: center; justify-content: center;
  background: none; border: none; border-radius: 12px; padding: 6px 2px;
  color: var(--faint); cursor: pointer;
}
.bottom-tab-btn svg { width: 25px; height: 25px; }
.bottom-tab-btn.active { color: var(--text); }

@media (max-width: 760px) {
  /* Bandeau fine, une seule ligne, meme comportement au scroll que le desktop (position:sticky
     herite de la regle de base, non touchee ici) — demande explicite. Tient desormais sur une
     ligne sans repli car .catalog-switch-btn/.account-switch-btn passent icone seule
     juste en dessous, au lieu de forcer .header-right a passer a la ligne (ancien comportement,
     retire). */
  header {
    grid-template-columns: 1fr auto;
    grid-template-areas: "brand connect";
    padding: 10px 14px;
  }
  .logo-mark { width: 32px; height: 32px; }
  /* Marque et compte : icone seule sur mobile (nom/chevron masques) pour tenir sur une seule
     ligne fine avec FR/EN — demande explicite « ne garder que les icones ». Le menu deroulant
     associe (contenu, ouverture) reste identique, seul le declencheur change d'aspect. */
  .catalog-switch-btn, .account-switch-btn { padding: 8px; gap: 0; justify-content: center; }
  .catalog-switch-btn span, .account-switch-btn span, .catalog-switch-chevron { display: none; }
  /* La pilule d'onglets du haut (+ la pastille PRO qui vit avec elle) laisse la place a la
     bottom tab bar — demande explicite « tres inspire de la bottom bar d'instagram », qui
     remplace la nav du haut plutot que de s'y ajouter. */
  .tab-bar-row { display: none; }
  .bottom-tab-bar { display: flex; }
  /* Espace reserve en bas de page pour que la barre fixe ne recouvre jamais le dernier bloc
     de contenu (hauteur barre ~48px + paddings ~12px + zone de securite iOS deja geree par
     le padding de la barre elle-meme). */
  body { padding-bottom: 72px; }
  /* Le garde-fou "overflow-x:hidden sur html" a ete retire (2026-09-23, regression constatee) :
     poser overflow sur l'element racine casse le position:sticky du header (le bandeau
     defilait hors ecran au lieu de rester colle en haut — verifie : le rect du header passait
     de top:0 a top:-600px des qu'on retirait cette regle). body garde deja overflow-x:hidden
     (regle de base, non ajoutee ce chantier) et suffit a lui seul contre le debordement
     horizontal — voir le vrai fix cible plus bas (.need-palette-toggle-btn). */
  /* Bug reel trouve a l'audit : .need-palette-toggle-btn (Pool, "Palettes included...") est en
     width:max-content sur desktop (dimensionne pile sur son texte, jamais retourne a la ligne)
     — sur mobile son contenu ne tient pas sur une seule ligne dans son conteneur, il debordait
     de ~26px hors de l'ecran (mesure). Passe en pleine largeur + le libelle (premier span)
     peut retrecir et repasser a la ligne ; le compte et la fleche restent groupes a la fin. */
  .need-palette-toggle-btn { width: 100%; }
  .need-palette-toggle-btn > span:first-child { flex: 1 1 auto; min-width: 0; text-align: left; }
}
.tab-panel { display: flex; flex-direction: column; gap: var(--s5); }
.tab-panel[hidden] { display: none; }
/* Page marketing seule (#tab-home) : ecart plus genereux et EGAL entre chaque section
   (hero/recipe/need/idea/closing) que le gap standard des autres onglets (--s6 104px, pas
   --s5 64px) — demande explicite "espace de vide equivalent et consequent entre chaque
   section" apres le remaniement de l'ordre. Cible seulement #tab-home (id, plus specifique
   que .tab-panel) pour ne pas toucher les autres onglets, qui restent a --s5. */
#tab-home { gap: var(--s6); }

.custom-base-row { display: flex; gap: 8px; margin-bottom: 10px; align-items: center; }
.custom-base-row input[type="text"] {
  flex: 1; background: #fff; color: var(--text); border: 1px solid var(--border);
  border-radius: 8px; padding: 8px 10px; font-family: inherit; font-size: 13px;
}
.custom-base-row input[type="color"] {
  width: 42px; height: 38px; padding: 2px; border-radius: 10px; border: 1px solid var(--border);
  background: #fff; cursor: pointer; flex-shrink: 0;
}
.custom-base-row[hidden], .selected-row[hidden] { display: none; }
.mix-subheading { font-size: 11px; color: var(--faint); text-transform: uppercase; letter-spacing: 0.12em; margin: 20px 0 8px; }

/* ---- Onglet Melange de peinture — memes codes visuels que Harmonie chromatique/Palettes
   enregistrees : cases arrondies, ⓘ + panneau de detail flottant, texte minimal. ---- */

/* Couleur ciblee : titre en gros/gras ancre en haut, le reste des donnees mesurees
   (nom/hex/RGB/LCH) ancre en bas — deux groupes ecartes plutot qu'un bloc centre. */
.mix-target-block {
  position: relative; border-radius: 10px; padding: 28px; min-height: 180px;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,0.35);
  display: flex; flex-direction: column; justify-content: space-between;
}
/* Vide (rien choisi) : meme motif que .hero-block-empty (Harmonie), centre plutot qu'ancre
   haut/bas — rien a ancrer tant qu'aucune donnee n'est encore choisie. */
.mix-target-block-empty {
  background: repeating-linear-gradient(135deg, #dfe6ea, #dfe6ea 10px, #e8edf0 10px, #e8edf0 20px);
  color: var(--muted); align-items: center; justify-content: center; cursor: pointer; gap: 10px;
}
.mix-target-empty-icon { width: 30px; height: 30px; color: var(--border-strong); }
.mix-target-empty-title { font-size: 15px; font-weight: 700; color: var(--muted); }
.mix-target-title { font-size: 26px; font-weight: 800; line-height: 1.2; }
.mix-target-sub { display: flex; flex-direction: column; gap: 2px; }
.mix-target-name { font-size: 13px; font-weight: 700; opacity: 0.9; }
.mix-target-hex { font-size: 13px; opacity: 0.85; }
.mix-target-lch { font-size: 11px; font-weight: 500; opacity: 0.7; letter-spacing: 0.02em; }

/* ---- Explorateur de melange libre — module INDEPENDANT de la couleur ciblee (mix-target-*
   ci-dessus). Cases volontairement DEUX FOIS PLUS HAUTES que la case ciblee (360px vs 180px) :
   contraste large/bas en haut, etroit/haut en bas, meme composition titre-haut/donnees-bas que
   la case ciblee (voir .mix-explorer-slot-eyebrow / -bottom) pour une vraie harmonie visuelle
   plutot qu'une simple variation de taille. ---- */
/* "OU" : seul separateur entre couleur ciblee et melange libre, sans texte ni ligne — la
   taille et le poids suffisent a signaler deux chemins alternatifs. */
.mix-explorer-slots { display: flex; align-items: center; gap: 14px; margin: 18px 0 20px; }
.mix-explorer-slot {
  flex: 1; position: relative; min-height: 360px; border-radius: 10px; padding: 20px;
  display: flex; flex-direction: column; justify-content: space-between; gap: 2px;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,0.35); cursor: pointer;
}
/* Comme la couleur ciblee : la croix reste visible en permanence plutot qu'au seul survol
   (.hero-block:hover ne s'applique pas ici) — supprimer puis remplacer est l'action courante
   sur une case deja choisie, pas la peine de la cacher. */
.mix-explorer-slot .hero-remove-btn { opacity: 1; }
.mix-explorer-slot-empty {
  background: repeating-linear-gradient(135deg, #dfe6ea, #dfe6ea 10px, #e8edf0 10px, #e8edf0 20px);
  color: var(--muted); align-items: center; justify-content: center; gap: 8px;
}
/* Icone et texte principal ("Peinture A"/"B") au meme gabarit que .mix-target-empty-icon /
   .mix-target-empty-title de la case ciblee juste au-dessus (demande explicite) — avant
   sensiblement plus petits ici (22px/13px contre 30px/15px). */
.mix-explorer-slot-icon { width: 30px; height: 30px; color: var(--border-strong); }
/* Ancree en haut de la case (justify-content:space-between du parent) — meme role que
   "Couleur ciblee" sur la case du dessus : un intitule commun aux deux cases A/B. */
.mix-explorer-slot-eyebrow { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; opacity: 0.75; }
.mix-explorer-slot-hint { font-size: 15px; font-weight: 700; margin-top: 2px; }
.mix-explorer-slot-bottom { display: flex; flex-direction: column; gap: 2px; }
.mix-explorer-slot-name { font-size: 13px; font-weight: 800; }
.mix-explorer-slot-hex { font-size: 11px; opacity: 0.85; }
.mix-explorer-plus { flex: none; font-size: 20px; font-weight: 700; color: var(--faint); }

/* Une ligne par peinture active (2 ou 3) : pastille d'identite + nom + curseur + %, plutot
   qu'un seul curseur A/B — chaque curseur ajuste sa peinture et redistribue la difference
   proportionnellement sur les autres (voir redistributeMixExplorerWeights en JS), pour que
   la somme reste toujours 100% quel que soit le nombre de peintures. */
.mix-explorer-ratios[hidden], .mix-explorer-result[hidden] { display: none; }
.mix-explorer-ratios { display: flex; flex-direction: column; gap: 14px; margin-bottom: 28px; }
.mix-explorer-ratio-row { display: flex; align-items: center; gap: 12px; }
.mix-explorer-ratio-dot { width: 14px; height: 14px; border-radius: 50%; flex: none; box-shadow: inset 0 0 0 1px rgba(14,17,22,0.12); }
.mix-explorer-ratio-name {
  font-size: 12px; font-weight: 600; color: var(--muted); width: 96px; flex: none;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.mix-explorer-slider {
  flex: 1; -webkit-appearance: none; appearance: none; height: 6px; border-radius: 999px;
  background: var(--border); outline: none; cursor: pointer;
}
.mix-explorer-slider::-webkit-slider-thumb {
  -webkit-appearance: none; width: 20px; height: 20px; margin-top: -7px; border-radius: 50%;
  background: currentColor; border: 3px solid #fff; box-shadow: 0 1px 4px rgba(14,17,22,0.35); cursor: pointer;
}
.mix-explorer-slider::-moz-range-track { height: 6px; border-radius: 999px; background: var(--border); }
.mix-explorer-slider::-moz-range-thumb {
  width: 20px; height: 20px; border-radius: 50%; background: currentColor; border: 3px solid #fff;
  box-shadow: 0 1px 4px rgba(14,17,22,0.35); cursor: pointer;
}
.mix-explorer-ratio-pct { width: 38px; flex: none; text-align: right; font-size: 12px; font-weight: 700; font-variant-numeric: tabular-nums; }

/* Bandeau resultat : epais, colore en aplat (comme une case Harmonie), volontairement PAS en
   pleine largeur ni pose a plat — centre, plus etroit, avec de l'air et une ombre portee pour
   s'imposer comme LE point d'arrivee du module plutot qu'une simple ligne d'info parmi
   d'autres. */
.mix-explorer-result {
  max-width: 460px; margin: 0 auto; border-radius: 16px; padding: 34px 36px;
  display: flex; flex-direction: column; align-items: center; gap: 6px; text-align: center;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,0.35), 0 16px 36px rgba(14,17,22,0.18);
}
.mix-explorer-result-title { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.14em; opacity: 0.75; margin-bottom: 2px; }
.mix-explorer-result-hex { font-size: 13px; font-weight: 800; }
.mix-explorer-result-lch { font-size: 11px; opacity: 0.8; }

/* ---- Module explicatif "Target" (2026-09-22) : meme design que .need-idle-explainer* (Pool),
   sa propre feuille de regles (convention du fichier — jamais partagees meme identiques).
   Inter partout (jamais la mono du corps de page ici), var(--text)/var(--muted) pour les
   nuances noir/gris, centre, tres aere. ---- */
.mix-mode-explainer {
  max-width: 640px; margin: var(--s6) auto 0;
  display: flex; flex-direction: column; align-items: center; text-align: center;
}
/* [hidden] + display:flex : piege deja documente ailleurs dans ce fichier (l'attribut seul ne
   suffit pas des qu'un selecteur de classe pose aussi une valeur de display — il faut toujours
   sa propre regle [hidden] explicite). Se cache maintenant pour de vrai une fois qu'une cible
   produit un resultat (voir setMixTarget/renderMixTargetEmpty, palette_lab.js). */
.mix-mode-explainer[hidden] { display: none; }
#tab-mix .mix-mode-explainer-title {
  font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif; font-weight: 500;
  text-transform: none; letter-spacing: -0.01em;
  font-size: clamp(22px, 2.8vw, 30px); line-height: 1.3; color: var(--text);
  max-width: 30ch; margin: 0 0 var(--s5); text-wrap: balance;
}
.mix-mode-explainer-eyebrow {
  font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif; font-size: 13px; font-weight: 600;
  letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted);
  margin-bottom: var(--s4);
}
.mix-mode-steps { display: flex; flex-direction: column; gap: var(--s5); width: 100%; }
.mix-mode-step { display: flex; flex-direction: column; align-items: center; gap: 10px; }
.mix-mode-step-icon { width: 28px; height: 28px; color: var(--border-strong); margin-bottom: 4px; }
.mix-mode-step-title {
  font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif; font-size: 17px; font-weight: 600;
  color: var(--text);
}
.mix-mode-step-desc {
  font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif; font-size: 15px; font-weight: 400;
  color: var(--muted); line-height: 1.6; max-width: 50ch; margin: 0;
}
/* Etape 2 : sous-liste de 4 options (pas juste une phrase) — chaque option garde le meme
   registre typo que le corps du texte, seul le titre de l'option ressort (var(--text), 600). */
.mix-mode-step-list {
  list-style: none; margin: 4px 0 0; padding: 0; max-width: 52ch;
  display: flex; flex-direction: column; gap: 10px; text-align: left;
}
.mix-mode-step-list li {
  font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif; font-size: 15px;
  color: var(--muted); line-height: 1.55;
}
.mix-mode-step-list-title { color: var(--text); font-weight: 600; }

/* Choix du type de resultat (teinte pure / melange a 2 / a 3) : meme roue que les regles
   d'harmonie plus haut, jusqu'au nom du .rule-dot pres — juste 3 arrets au lieu de 8. */

/* Une seule proposition affichee au premier plan, avec ses propres fleches gauche/droite pour
   naviguer parmi les alternatives de CE type — masquees (mais l'espace garde, pour ne pas
   decentrer la case) sur "Teinte la plus proche", qui n'a qu'un seul resultat possible.
   Quand il y en a, la precedente/suivante apparaissent en aperçu recule (echelle + rotation
   3D) de part et d'autre — cliquables, pour y aller directement. */
.mix-result-stage { display: flex; align-items: center; gap: 12px; }
.mix-result-stage[hidden] { display: none; }
.mix-carousel {
  position: relative; flex: 1; min-width: 0; min-height: 190px;
  display: flex; align-items: center; justify-content: center;
  perspective: 1100px;
}
.mix-result-card-wrap { position: relative; z-index: 2; width: 62%; max-width: 440px; }
.mix-carousel-side {
  position: absolute; top: 50%; width: 54%; max-width: 380px; z-index: 1;
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity 0.4s ease, transform 0.4s cubic-bezier(.22,1,.36,1);
}
.mix-carousel-side.mix-carousel-active { opacity: 0.45; visibility: visible; pointer-events: auto; cursor: pointer; }
.mix-carousel-side.mix-carousel-active:hover { opacity: 0.7; }
.mix-carousel-prev { left: 0; transform: translate(-30%, -50%) scale(0.74) rotateY(24deg); transform-origin: right center; }
.mix-carousel-next { right: 0; transform: translate(30%, -50%) scale(0.74) rotateY(-24deg); transform-origin: left center; }
.mix-carousel-side .mix-card { pointer-events: none; }
@media (prefers-reduced-motion: reduce) {
  .mix-carousel-side { transition: none; }
}
/* Deplacees a l'interieur de .mix-carousel, dans l'espace vide entre la carte active (62%,
   centree) et l'apercu recule de la proposition voisine — demande explicite : elles se
   confondaient avec le clic sur l'apercu lui-meme quand elles etaient tout a fait a
   l'exterieur du carousel, de part et d'autre des apercus. z-index au-dessus des deux
   (.mix-result-card-wrap z-index:2, .mix-carousel-side z-index:1). */
.mix-alt-arrow {
  position: absolute; top: 50%; transform: translateY(-50%); z-index: 3;
  flex-shrink: 0; width: 32px; height: 32px; border-radius: 50%; border: none;
  background: none; color: var(--faint); cursor: pointer;
  display: flex; align-items: center; justify-content: center; transition: background 0.15s, color 0.15s;
}
/* Calees sur la geometrie reelle de la carte active (.mix-result-card-wrap : width:62%,
   max-width:440px, donc demi-largeur min(31%, 220px)) plutot qu'un pourcentage fixe du
   carousel : un pourcentage fixe ne suivait plus le bord de la carte des que le
   max-width:440px prenait le dessus sur les 62% (carousel large), et l'ecart mesure au
   premier essai (9%) tombait carrement sur l'apercu recule au lieu de l'espace vide entre
   les deux (verifie via getBoundingClientRect). 14px d'ecart entre le bord de la carte et la
   fleche, quelle que soit la largeur du carousel. */
#mix-alt-prev { left: calc(50% - min(31%, 220px) - 46px); }
#mix-alt-next { right: calc(50% - min(31%, 220px) - 46px); }
.mix-alt-arrow:hover { background: rgba(14,17,22,0.08); color: var(--text); }
.mix-alt-arrow svg { width: 16px; height: 16px; pointer-events: none; }
.mix-alt-arrow.mix-alt-arrow-inactive { visibility: hidden; pointer-events: none; }

/* Case de resultat : les peintures a melanger sont le contenu principal, en haut et en gros —
   la correspondance (etat + ΔE) vient en second, en bas. Le detail (proportions) vit desormais
   dans le bandeau permanent sous la case, pas dans un panneau a la demande. Deux groupes
   ancres haut/bas, comme la cible. Reduite (etait 240px) pour laisser la place aux apercus
   3D de part et d'autre. */
.mix-card {
  position: relative; min-height: 190px; border-radius: 10px;
  padding: 20px; display: flex; flex-direction: column; justify-content: space-between;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,0.35);
}
/* Limite Free "Paint Mix" (visiteur non connecte : toujours verrouille : compte Free connecte :
   verrouille une fois le quota du jour epuise) — pas de vraie couleur/donnee ici, .pro-lock-
   overlay (position:absolute, voir plus haut) remplit la case entiere. */
.mix-card-locked { background: var(--panel); box-shadow: none; }
/* Option "A partir de mon stock" sans aucune peinture en stock (2026-09-22, demande explicite) :
   meme case que les autres etats (verrouille compris), juste un message centre a la place d'un
   resultat. */
.mix-card-empty-stock {
  background: var(--panel); box-shadow: none;
  align-items: center; justify-content: center; text-align: center;
}
.mix-card-empty-stock-text { font-size: 13.5px; color: var(--muted); line-height: 1.5; max-width: 34ch; }
/* ---- Pastille "liquid" Target/Free (2026-09-22) : meme recette que .pricing-toggle (verre
   depoli + indicateur sombre glissant), dupliquee sous son propre nom (convention du fichier).
   Centree sous le bandeau principal, visible uniquement sur cet onglet (vit dans #tab-mix). ---- */
.mix-mode-switch {
  position: relative; display: flex; width: fit-content; gap: 2px; padding: 4px;
  margin: 0 auto var(--s4); border-radius: 999px;
  background: rgba(255,255,255,0.55);
  backdrop-filter: blur(16px) saturate(180%);
  -webkit-backdrop-filter: blur(16px) saturate(180%);
  border: 1px solid rgba(255,255,255,0.7);
  box-shadow: 0 1px 2px rgba(14,17,22,0.05), 0 10px 28px rgba(14,17,22,0.08);
}
.mix-mode-switch-indicator {
  position: absolute; top: 0; left: 0; z-index: 0;
  border-radius: 999px; background: var(--text);
  transition: transform 0.3s cubic-bezier(.22,1,.36,1), width 0.3s cubic-bezier(.22,1,.36,1);
}
.mix-mode-switch-btn {
  position: relative; z-index: 1; flex: none;
  background: none; border: none; border-radius: 999px;
  padding: 9px 22px; cursor: pointer; font-size: 12px; font-weight: 600;
  font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif;
  letter-spacing: 0.03em; color: var(--muted); white-space: nowrap;
  transition: color 0.2s ease;
}
.mix-mode-switch-btn:hover { color: var(--text); }
.mix-mode-switch-btn.active, .mix-mode-switch-btn.active:hover { color: #f7fafb; }

/* Pastille ronde qui mord le coin haut-gauche du bandeau cible (2026-09-18, demande explicite —
   remplace l'ancienne ligne de texte "N/5 recherche(s)..."), juste le nombre de recherches
   restantes. */
.mix-target-wrap { position: relative; }
.mix-usage-badge {
  position: absolute; top: -10px; left: -10px; z-index: 2;
  width: 32px; height: 32px; border-radius: 50%;
  background: var(--text); color: #f7fafb;
  display: flex; align-items: center; justify-content: center;
  font-family: 'JetBrains Mono', monospace; font-size: 13px; font-weight: 700;
  box-shadow: 0 2px 6px rgba(14,17,22,0.25), 0 0 0 3px #fff;
}
.mix-usage-badge[hidden] { display: none; }
.mix-card-top { pointer-events: none; padding-right: 8px; }
/* Numero de la proposition (melanges a 2/3 peintures uniquement) : la meilleure est
   "Option 1", la derniere "Option N" — meme ordre que la navigation aux fleches. */
.mix-card-option {
  display: block; font-size: 10px; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.06em; opacity: 0.75; margin-bottom: 4px;
}
.mix-card-paints { font-size: 15px; font-weight: 800; line-height: 1.3; }
.mix-card-bottom { display: flex; flex-direction: column; gap: 4px; pointer-events: none; }
.match-quality.mix-card-quality-big {
  color: inherit; font-size: 13px; font-weight: 800; text-transform: uppercase; letter-spacing: 0.02em;
}
.mix-card-de { font-size: 22px; font-weight: 800; line-height: 1.15; }

/* Etiquette de qualite ("[notable gap — closest available shade]", la plus longue des 4
   variantes) : .match-quality herite white-space:nowrap (base commune a tous ses usages,
   partout ailleurs sur une seule ligne courte) — sur la case Paint Mix mobile, plus etroite,
   ce texte long deborde du bandeau colore au lieu de s'y arreter (signale explicitement,
   2026-09-24). Repli mobile uniquement, scope a .mix-card-quality-big (pas la regle .match-
   quality generale, qui reste inchangee pour ses autres usages) : autorise le retour a la
   ligne et reduit legerement la taille pour limiter le nombre de lignes necessaires. */
@media (max-width: 640px) {
  .match-quality.mix-card-quality-big { white-space: normal; font-size: 11px; line-height: 1.35; }
}

/* Petite animation, subtile, au changement de proposition (fleches gauche/droite) — pas au
   changement de type (roue), qui a deja son propre glissement de points. */
@keyframes mixCardPop {
  from { opacity: 0.5; transform: scale(0.95); }
  to { opacity: 1; transform: scale(1); }
}
.mix-result-card-wrap.mix-pop { animation: mixCardPop 0.3s cubic-bezier(.22,1,.36,1); }
@media (prefers-reduced-motion: reduce) {
  .mix-result-card-wrap.mix-pop { animation: none; }
}

/* Bandeau d'info permanent (peinture(s) a melanger + proportions), sous la case — meme verre
   depoli que l'ancien panneau ⓘ, mais toujours visible et avec des carres de couleur plus
   gros pour rester lisible a cette echelle. Reduit un peu pour rester proportionne a la
   case, elle-meme plus petite qu'avant. */
.mix-info-banner {
  /* Legerement plus large que .mix-result-card-wrap (width:62%, max-width:440px) juste
     au-dessus — demande explicite, pour que les bandes de couleur des peintures a melanger
     depassent un peu la case "Option N". */
  width: 68%; max-width: 480px; margin: 0 auto; padding: 16px 20px;
  background: rgba(255,255,255,0.6);
  backdrop-filter: blur(20px) saturate(180%);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  border: 1px solid rgba(255,255,255,0.7);
  border-radius: 20px;
  box-shadow: 0 1px 2px rgba(14,17,22,0.05), 0 24px 60px rgba(14,17,22,0.16);
  display: flex; flex-direction: column; gap: 10px;
}
.mix-info-banner[hidden] { display: none; }
/* Pleine largeur sur mobile (demande explicite, 2026-09-24) : les 68%/480px pense pour desktop
   retombaient a ~255px sur un ecran de 375px, serrant les bandes de recette (badge + nom + %)
   plus que necessaire. Comme les autres composants pleine largeur du site en mobile (barre de
   palette Create, bandeaux Need...), le bandeau suit ici la largeur de sa colonne plutot qu'un
   pourcentage pense pour du desktop — les bandes de couleur (deja flex, largeur auto) en
   profitent directement, sans changement de markup. */
@media (max-width: 640px) {
  .mix-info-banner { width: 100%; max-width: none; }
}

.mix-detail-pct { margin-left: auto; font-weight: 700; }

#image-wrap { display: flex; justify-content: center; }
/* Epouse exactement les dimensions rendues de l'image (jamais celles, plus larges, de
   #image-wrap qui la centre) — repere fiable pour tout ce qui se positionne dessus. */
.pipette-frame { position: relative; display: inline-block; line-height: 0; max-width: 100%; }
#image-wrap img { max-width: 640px; width: 100%; border-radius: 10px; cursor: crosshair; display: block; border: 1px solid var(--border); }
/* Input fichier natif : seul le bouton integre est stylable, reprend le langage
   btn-secondary plutot que le chrome par defaut du navigateur. */
#image-input {
  margin-top: 4px; font-family: inherit; font-size: 12px; color: var(--muted);
}
#image-input::file-selector-button {
  background: transparent; color: var(--muted); border: 1px solid var(--border);
  border-radius: 8px; padding: 7px 14px; font-family: inherit; font-size: 12px; font-weight: 500;
  cursor: pointer; margin-right: 10px; transition: border-color 0.15s, color 0.15s;
}
#image-input::file-selector-button:hover { border-color: var(--border-strong); color: var(--text); }

/* Couleurs reelles (dominantes / pipette) — un clic les ajoute directement a la barre de
   palette au-dessus, pas de correspondance Angelus ici (volontairement fluide/minimal). */
.image-swatch-row { display: flex; flex-wrap: wrap; gap: 8px; }
.image-swatch {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  background: none; border: none; border-radius: 0;
  padding: 0; cursor: pointer; font-family: inherit; transition: transform 0.1s;
}
.image-swatch:hover { transform: translateY(-1px); }
.image-swatch-color { width: 52px; height: 52px; border-radius: 10px; border: 1px solid var(--border); }
.image-swatch-hex { font-size: 11px; color: var(--muted); }

/* Apercu de la zone de prelevement au survol de l'image (avant le clic) */
.pick-zone-preview {
  position: absolute; border-radius: 50%; pointer-events: none;
  border: 2px dashed var(--text); box-shadow: 0 0 0 1px rgba(255,255,255,0.8);
  transform: translate(0, 0);
}

/* Cartes de zones prelevees, dans l'editeur de "Prelevement manuel" : memes specimens
   arrondis que la grille automatique/manuelle (fond = couleur prelevee, # + L), avec juste un
   bouton de suppression en incrustation, comme le "×" des cases de la grille — toute zone
   prelevee entre automatiquement dans la moyenne, plus de case a cocher a gerer. */
.zone-pick-card { position: relative; }
.zone-pick-remove {
  position: absolute; top: 8px; right: 8px; z-index: 1; width: 20px; height: 20px; border-radius: 50%;
  background: rgba(255,255,255,0.75); border: 1px solid var(--border); color: var(--muted);
  font-size: 13px; line-height: 1; cursor: pointer; padding: 0;
  display: flex; align-items: center; justify-content: center; transition: background 0.15s, color 0.15s;
}
.zone-pick-remove:hover { background: #fbeceb; border-color: var(--loose-text); color: var(--loose-text); }
.zone-pick-card .zone-quality { margin-top: 2px; }

.zone-validation-panel {
  margin-top: 10px; padding: 12px 0 0; background: none; border: none; border-top: 1px solid var(--border);
  border-radius: 0; display: flex; flex-wrap: wrap; align-items: center; gap: 12px;
}
.zone-validation-status { font-size: 12px; font-weight: 600; flex: 1; min-width: 200px; }
.zone-validation-status.ok { color: var(--good-text); }
.zone-validation-status.warn { color: var(--warn-text); }
.zone-validation-preview { display: flex; align-items: center; gap: 8px; }
.zone-validation-preview .image-swatch-color { width: 28px; height: 28px; border-radius: 8px; display: block; }
/* A gauche de l'aplat moyenne (voir buildManualValidationHtml) : precise explicitement qu'une
   moyenne est en jeu plutot que de laisser croire a une couleur prelevee telle quelle. */
.zone-validation-avg-label { font-size: 11px; color: var(--muted); white-space: nowrap; }
.zone-validation-panel .hint { flex-basis: 100%; margin: 0; }

/* ---- Barre d'outils Harmonie (couleur(s) de depart + regle) ---- */

.harmony-toolbar { padding: 0; }
.harmony-toolbar-row { display: flex; gap: 24px; flex-wrap: wrap; align-items: flex-start; }
.toolbar-field { display: flex; flex-direction: column; gap: 6px; }
.toolbar-field[hidden] { display: none; }
.toolbar-field-grow { flex: 1; min-width: 260px; }
.toolbar-field-label {
  font-size: 10px; color: var(--faint); text-transform: uppercase;
  letter-spacing: 0.1em; font-weight: 600;
}
/* Outil de selection de couleur du popover (roue native + hex) : rendu plus structure —
   roue en disque, hex en simple ligne soulignee ; validation au clavier (Entree), pas de
   bouton "OK" separe. */
.custom-base-row.compact { margin-bottom: 0; gap: 12px; }
.custom-base-row.compact input[type="color"] {
  width: 38px; height: 38px; padding: 0; border-radius: 50%;
  border: 1px solid rgba(14,17,22,0.12); background: none; overflow: hidden; flex-shrink: 0;
}
.custom-base-row.compact input[type="text"] {
  flex: 1; min-width: 0; background: none; border: none; border-bottom: 1px solid var(--border);
  border-radius: 0; padding: 6px 2px; font-size: 13px;
}
.custom-base-row.compact input[type="text"]:focus { outline: none; border-bottom-color: var(--text); }

/* ---- Roue de regles d'harmonie : disque horizontal sous la palette, 70% de sa largeur,
   centre. Les points s'organisent autour du point actif (agrandi/fonce) et s'estompent en
   s'ecartant, comme un cadran vu en perspective — pas de numeros, juste le nom + une
   description courte en dessous, mis a jour a chaque defilement. Fleches de part et
   d'autre pour naviguer ; le glissement des points est une simple transition CSS sur leur
   position/opacite (les <span> sont crees une fois, seul leur style change). ---- */
.rule-wheel {
  width: 70%; max-width: 640px; margin: 0 auto;
  display: grid; grid-template-columns: auto 1fr auto; align-items: center;
  column-gap: 14px; row-gap: 8px;
}
.rule-wheel[hidden] { display: none; }
/* Ecart supplementaire specifique a la roue Paint Mix (pas .rule-wheel en general : ne pas
   toucher aux autres roues, Create/Need) — en plus du gap:12px herite de .harmony-hero.
   Demande explicite : les fleches de cette roue (teinte proche / 2 peintures / 3 peintures)
   se confondaient visuellement avec celles du bandeau juste en dessous
   (#mix-result-stage, qui fait defiler les propositions DANS un type de melange) tant
   qu'elles restaient aussi proches l'une de l'autre. Marge fixe, independante du contenu
   (nom/description varient en longueur selon le type choisi) : les 3 types gardent donc
   exactement le meme ecart avec le bandeau des options. */
#mix-kind-wheel { margin-bottom: 20px; }
.rule-wheel-track { position: relative; height: 68px; overflow: hidden; }
.rule-dot {
  position: absolute; left: 50%; top: 50%; width: 8px; height: 8px; border-radius: 50%;
  background: var(--border-strong);
  transition: transform 0.45s cubic-bezier(.22,1,.36,1), opacity 0.45s cubic-bezier(.22,1,.36,1),
    background 0.3s ease, width 0.3s ease, height 0.3s ease;
}
.rule-dot.current { width: 11px; height: 11px; background: var(--text); }
.rule-wheel-arrow {
  width: 34px; height: 34px; border-radius: 50%; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  background: none; border: none; color: var(--muted); cursor: pointer;
  transition: background 0.18s ease, color 0.18s ease;
}
.rule-wheel-arrow:hover { background: rgba(14,17,22,0.08); color: var(--text); }
.rule-wheel-arrow svg { width: 16px; height: 16px; pointer-events: none; }
/* Pour une roue a echelle ordonnee (ex. fidelite/economie) plutot qu'a categories cycliques :
   desactive le bout de course au lieu de boucler (meme principe que .mix-alt-arrow-inactive). */
.rule-wheel-arrow.rule-wheel-arrow-inactive { visibility: hidden; pointer-events: none; }
.rule-wheel-info { grid-column: 1 / -1; text-align: center; }
.rule-wheel-name {
  font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif;
  font-size: 18px; font-weight: 700; letter-spacing: 0.01em;
}
.rule-wheel-desc { font-size: 11px; color: var(--faint); margin-top: 3px; }
/* Dans la roue Need, la ligne sous le nom porte le nombre de pots : c'est le chiffre de la
   decision, pas une legende — on lui donne le poids correspondant. */
/* Le nombre de pots vit deja, en grand, dans le bandeau de stats juste au-dessus : sous la
   roue, le nom du choix ("Recommandé"...) est ce qui compte — mis en avant, pas la case
   desc (retiree) qui repetait "N pots à acheter". */
#fidelity-wheel .rule-wheel-name { font-size: 22px; font-weight: 800; }
#fidelity-wheel .rule-wheel-desc { display: none; }

.ambiance-panel { margin-top: -28px; }


/* ---- Ambiance : 3 curseurs bipolaires (neutre au centre) — le remplissage part du milieu
   plutot que du bord, pour montrer l'ecart au neutre comme un vrai instrument de mesure. ---- */
.ambiance-sliders { display: flex; flex-direction: column; gap: 22px; margin-bottom: 12px; }
.ambiance-slider-row { display: flex; align-items: center; gap: 12px; }
.ambiance-slider-row input[type="range"] { flex: 1; }
.ambiance-axis-label {
  font-size: 12px; font-weight: 600; color: var(--faint); white-space: nowrap; width: 56px;
  text-transform: uppercase; letter-spacing: 0.06em;
}
.ambiance-axis-label-end { text-align: right; }

/* Curseurs personnalises : trait fin, coherent sur toute la page plutot que le style natif
   du systeme (qui variait selon le navigateur). */
input[type="range"] {
  -webkit-appearance: none; appearance: none; height: 2px; background: var(--border);
  border-radius: 0; outline: none; accent-color: var(--text);
}
input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none; width: 11px; height: 11px; background: var(--text);
  border: 2px solid #eef3f6; cursor: pointer; margin-top: 0;
}
input[type="range"]::-moz-range-thumb {
  width: 9px; height: 9px; background: var(--text); border: 2px solid #eef3f6; cursor: pointer; border-radius: 0;
}
input[type="range"]::-moz-range-track { height: 2px; background: var(--border); }

/* Curseurs d'ambiance specifiquement : piste plus epaisse (le fondu bipolaire peint en JS
   s'y lit mieux) et curseur en cercle bague, meme langage que les points de la roue de
   regles et les marqueurs du disque chromatique. */
.ambiance-slider-row input[type="range"] {
  height: 3px; border-radius: 2px; background: var(--border);
}
.ambiance-slider-row input[type="range"]::-webkit-slider-thumb {
  width: 14px; height: 14px; border-radius: 50%; background: var(--text);
  border: 2px solid #ffffff; box-shadow: 0 0 0 1px var(--border-strong);
}
.ambiance-slider-row input[type="range"]::-moz-range-thumb {
  width: 12px; height: 12px; border-radius: 50%; background: var(--text);
  border: 2px solid #ffffff; box-shadow: 0 0 0 1px var(--border-strong);
}
.ambiance-slider-row input[type="range"]::-moz-range-track { height: 3px; border-radius: 2px; }

/* ---- Selecteur de marque (en-tete) ---- */
/* Bouton pilule dans le meme registre que .btn-connect, mais en retrait (contour, pas plein) :
   c'est un etat, pas une action principale. Le menu reprend le verre depoli des panneaux. */
/* Le groupe (marque + Connect) occupe la colonne de droite et se cale a son extremite :
   Connect retrouve son bord droit d'origine, la marque le suit a distance fixe. C'est le
   groupe qui porte le placement dans la grille, plus le bouton seul (il est imbrique). */
.header-right { grid-area: connect; justify-self: end; display: flex; align-items: center; gap: 12px; flex-shrink: 0; }
/* Sélecteur de langue (feuille de route phase 3, socle) : 2 boutons plutôt qu'un menu déroulant
   comme .catalog-switch — un simple aller-retour entre 2 valeurs ne justifie pas un popover. */
.lang-switch { display: flex; border: 1px solid var(--border); border-radius: 999px; padding: 2px; gap: 2px; }
.lang-switch-option {
  background: none; border: none; border-radius: 999px; cursor: pointer;
  font-family: inherit; font-size: 11px; font-weight: 700; letter-spacing: 0.02em;
  color: var(--muted); padding: 6px 10px; transition: background 0.15s ease, color 0.15s ease;
}
.lang-switch-option:hover { color: var(--text); }
.lang-switch-option.active { background: var(--header-ink); color: #f7fafb; }
.catalog-switch { position: relative; }
.catalog-switch-btn {
  display: flex; align-items: center; gap: 8px;
  padding: 9px 14px; border-radius: 999px;
  background: none; border: 1px solid var(--border); color: var(--text);
  font-family: inherit; font-size: 12px; font-weight: 600; cursor: pointer;
  transition: border-color 0.15s ease, background 0.15s ease;
}
.catalog-switch-btn:hover { border-color: var(--border-strong); background: rgba(14,17,22,0.04); }
.catalog-switch-btn[aria-expanded="true"] { border-color: var(--text); }
.catalog-switch-icon { width: 15px; height: 15px; color: var(--muted); }
.catalog-switch-chevron { width: 13px; height: 13px; color: var(--faint); transition: transform 0.2s ease; }
.catalog-switch-btn[aria-expanded="true"] .catalog-switch-chevron { transform: rotate(180deg); }

.catalog-switch-menu {
  position: absolute; top: calc(100% + 8px); right: 0; z-index: 40;
  min-width: 260px; padding: 8px; display: flex; flex-direction: column; gap: 2px;
  background: rgba(255,255,255,0.6);
  backdrop-filter: blur(20px) saturate(180%);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  border: 1px solid rgba(255,255,255,0.7); border-radius: 20px;
  box-shadow: 0 1px 2px rgba(14,17,22,0.05), 0 24px 60px rgba(14,17,22,0.16);
}
.catalog-switch-menu[hidden] { display: none; }
.catalog-option {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 10px 12px; border-radius: 12px; text-align: left; cursor: pointer;
  background: none; border: none; font-family: inherit; color: var(--text);
  transition: background 0.15s ease;
}
.catalog-option:hover { background: rgba(14,17,22,0.06); }

/* Etat "connecte" du bandeau : icone seule (pas de texte "Account"/"Compte", demande
   explicite 2026-09-28) — hauteur/largeur fixes en border-box pour rester alignee au pixel
   pres sur le bouton a sa gauche (.catalog-switch-btn, 36px de haut), quels que soient les
   metriques de police (meme raisonnement que .btn-connect juste au-dessus). */
.account-switch { position: relative; font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif; }
.account-switch-btn {
  display: flex; align-items: center; justify-content: center;
  height: 36px; width: 36px; padding: 0; box-sizing: border-box; border-radius: 999px;
  background: none; border: 1px solid var(--border); color: var(--text);
  font-family: inherit; font-size: 12px; font-weight: 600; cursor: pointer;
  transition: border-color 0.15s ease, background 0.15s ease;
}
.account-switch-btn span, .account-switch-btn .catalog-switch-chevron { display: none; }
.account-switch-btn:hover { border-color: var(--border-strong); background: rgba(14,17,22,0.04); }
.account-switch-btn[aria-expanded="true"] { border-color: var(--text); }
.account-switch-icon { width: 16px; height: 16px; color: var(--muted); }

/* ---- Fenetre de connexion / inscription ---- */

.auth-modal-backdrop {
  position: fixed; inset: 0; z-index: 100;
  background: rgba(14,17,22,0.45);
  display: flex; align-items: center; justify-content: center; padding: 20px;
}
.auth-modal-backdrop[hidden] { display: none; }
.auth-modal-card {
  position: relative; width: 100%; max-width: 380px;
  font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif;
  background: rgba(255,255,255,0.85);
  backdrop-filter: blur(20px) saturate(180%);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  border: 1px solid rgba(255,255,255,0.7); border-radius: 20px;
  box-shadow: 0 1px 2px rgba(14,17,22,0.05), 0 24px 60px rgba(14,17,22,0.24);
  padding: 32px 28px 24px;
}
.auth-modal-close { top: 16px; right: 16px; }
.auth-modal-title { font-size: 18px; font-weight: 700; margin: 0 0 20px; }
.auth-modal-logo { width: 32px; height: 32px; display: block; opacity: 0.88; margin: 0 auto; }
/* Texte masque visuellement mais lu par les lecteurs d'ecran : porte le nom accessible du
   dialogue (aria-labelledby) une fois le titre texte remplace par le logo. */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}
.auth-field { display: flex; flex-direction: column; gap: 6px; font-size: 12px; color: var(--muted); margin-bottom: 14px; }
.auth-field input, .auth-field select {
  background: #fff; color: var(--text); border: 1px solid var(--border);
  border-radius: 8px; padding: 10px 12px; font-family: inherit; font-size: 14px;
}
.auth-field input:focus, .auth-field select:focus { outline: none; border-color: var(--text); }
/* Case d'acceptation des CGV/politique de confidentialite (inscription uniquement) : premiere
   case a cocher du site, pas de gabarit existant a reprendre — registre proche de .auth-field
   (meme taille de texte/couleur muted) mais en ligne, la case alignee sur la 1ere ligne de texte
   plutot que centree verticalement sur tout le bloc (le texte peut passer sur 2 lignes selon la
   largeur de la modale). */
.auth-terms-field {
  display: flex; align-items: flex-start; gap: 8px; font-size: 12px; line-height: 1.5;
  color: var(--muted); margin-bottom: 14px; cursor: pointer;
}
.auth-terms-field input[type="checkbox"] {
  margin-top: 2px; flex-shrink: 0; width: 15px; height: 15px; accent-color: var(--text);
  cursor: pointer;
}
.auth-terms-field a { color: var(--text); text-decoration: underline; }
.auth-terms-field a:hover { color: var(--accent); }
#auth-signup-fields[hidden] { display: none; }
#auth-terms-field[hidden] { display: none; }
#auth-password-confirm-field[hidden] { display: none; }
#auth-email-field[hidden] { display: none; }
#auth-password-field[hidden] { display: none; }
.auth-forgot-row { margin: -6px 0 14px; text-align: right; }
.auth-forgot-row[hidden] { display: none; }
.auth-forgot-row .action-link { font-size: 12px; }
.auth-error { font-size: 12.5px; color: var(--loose-text); margin: 0 0 14px; }
.auth-error[hidden] { display: none; }
.auth-submit { width: 100%; padding: 11px 0; margin-top: 4px; cursor: pointer; }
/* align-items:center + meme font-size sur les deux enfants (le bouton .action-link impose sa
   propre taille, 12px, differente du 12.5px herite ici par le span sinon) : sans ca, le texte
   "Deja un compte ?" et le bouton "Se connecter" ne partagent pas la meme ligne de base malgre
   le flex row (defaut align-items:stretch, tailles de police differentes). */
.auth-switch-mode { margin: 18px 0 0; text-align: center; font-size: 12.5px; color: var(--muted); display: flex; align-items: center; gap: 6px; justify-content: center; flex-wrap: wrap; }
.auth-switch-mode .action-link { font-size: 12.5px; padding: 0; }

/* ---- Fenetre "Passe a Pro" : meme carte que la connexion, un seul message + un seul CTA. ---- */
.upgrade-modal-message { font-size: 13.5px; color: var(--muted); line-height: 1.5; margin: 0 0 20px; }

/* ---- Fenetre "Mon compte" : meme carte que la connexion, un peu plus large (3 sections
   independantes empilees plutot qu'un seul petit formulaire). ---- */
.account-modal-card { max-width: 420px; max-height: 85vh; overflow-y: auto; }
.account-section { margin: 0; }
.account-section-title { font-size: 13px; font-weight: 700; margin: 0 0 14px; }
.account-modal-card .slot-detail-divider { margin: 22px 0; }
.auth-success { font-size: 12.5px; color: var(--good-text); margin: 0 0 14px; }
.auth-success[hidden] { display: none; }
.account-delete-actions { display: flex; align-items: center; gap: 16px; margin-top: 10px; }
/* Meme registre que .dock-btn.confirming (fond/texte rouges attenues) : la seule action
   destructrice et irreversible du site, elle doit se distinguer visuellement de tout le reste. */
.btn-danger {
  background: rgba(161,72,63,0.14); color: var(--loose-text); border: 1px solid rgba(161,72,63,0.3);
  border-radius: 8px; font-family: inherit; font-weight: 500; letter-spacing: 0.02em; padding: 9px 16px; cursor: pointer;
  transition: background 0.15s ease;
}
.btn-danger:hover { background: rgba(161,72,63,0.22); }

/* Free en neutre discret ; Pro reprend --accent (le seul signal ponctuel du design system,
   volontairement reserve aux etats qui doivent se remarquer) pour que l'abonnement actif
   se distingue vraiment d'un simple statut par defaut. */
.account-plan-badge {
  display: inline-block; font-size: 12px; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.04em; padding: 6px 14px; border-radius: 999px;
  background: rgba(14,17,22,0.06); color: var(--muted);
}
.account-plan-badge.is-premium { background: rgba(0,152,201,0.14); color: var(--accent); }
.account-plan-actions { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 14px; }
.account-plan-actions[hidden] { display: none; }

.catalog-option-check {
  flex: none; width: 18px; height: 18px; border-radius: 50%;
  border: 1.5px solid var(--border-strong); background: #fff;
  display: flex; align-items: center; justify-content: center;
}
.catalog-option-check svg { width: 11px; height: 11px; color: #fff; opacity: 0; }
.catalog-option.active .catalog-option-check { background: var(--text); border-color: var(--text); }
.catalog-option.active .catalog-option-check svg { opacity: 1; }
.catalog-option-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.catalog-option-brand { font-size: 13px; font-weight: 600; }
.catalog-option-meta { font-size: 10.5px; color: var(--muted); }

/* ---- Zone hero : la palette generee, grande et cliquable, en tete de page ---- */

.harmony-hero {
  background: none; border: none; border-radius: 0; padding: 0; display: flex; flex-direction: column; gap: 12px;
}
.harmony-hero[hidden] { display: none; }
.hero-toolbar { position: relative; display: flex; align-items: center; justify-content: center; padding-bottom: 4px; }

/* Messages de statut (confirmations/erreurs des actions du dock) : garde en DOM pour le JS
   qui les alimente, mais retires visuellement — plus de phrase flottante sous le menu. */
#identity-status, #copy-status { display: none; }

/* Dock d'actions "liquid glass" : verre depoli translucide, coins tres arrondis, icones
   seules — l'action se reconnait a son pictogramme, pas a du texte. */
.hero-dock {
  display: inline-flex; align-items: center; gap: 3px; margin: 0 auto;
  padding: 7px; border-radius: 999px;
  background: rgba(255,255,255,0.55);
  backdrop-filter: blur(16px) saturate(180%);
  -webkit-backdrop-filter: blur(16px) saturate(180%);
  border: 1px solid rgba(255,255,255,0.7);
  box-shadow: 0 1px 2px rgba(14,17,22,0.05), 0 10px 28px rgba(14,17,22,0.08);
}
.dock-btn {
  position: relative; width: 42px; height: 42px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center; flex-shrink: 0;
  background: none; border: none; padding: 0; cursor: pointer; color: var(--muted);
  transition: background 0.18s ease, color 0.18s ease;
}
.dock-btn[hidden] { display: none; }
.dock-btn svg { width: 18px; height: 18px; pointer-events: none; }
.dock-btn:hover { background: rgba(14,17,22,0.08); color: var(--text); }
.dock-btn:active { background: rgba(14,17,22,0.14); }
.dock-btn.confirming { background: rgba(161,72,63,0.14); color: var(--loose-text); }
.dock-btn.confirming:hover { background: rgba(161,72,63,0.2); }
.dock-btn.active { background: rgba(0,152,201,0.14); color: var(--accent); }

.dock-sep { width: 1px; height: 24px; background: rgba(14,17,22,0.12); margin: 0 3px; flex-shrink: 0; }
/* Doublon mobile de #hero-add-case-btn (voir palette_lab.html) : cache par defaut, ne
   s'affiche que sous 640px (regle plus bas), en meme temps que le bouton d'origine se cache. */
#hero-add-case-btn-mobile, .hero-add-case-btn-mobile-sep { display: none; }

/* Disque chromatique : petite feature en superposition (verre depoli) sous le dock, plutot
   qu'un onglet a part entiere qui prend toute la largeur en continu. */
.wheel-popover {
  position: absolute; top: calc(100% + 10px); left: 50%; transform: translateX(-50%);
  z-index: 30; padding: 16px;
  background: rgba(255,255,255,0.6);
  backdrop-filter: blur(20px) saturate(180%);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  border: 1px solid rgba(255,255,255,0.7);
  border-radius: 20px;
  box-shadow: 0 1px 2px rgba(14,17,22,0.05), 0 24px 60px rgba(14,17,22,0.16);
}
.wheel-popover[hidden] { display: none; }
.wheel-popover .wheel-wrap { padding: 0; }

/* Bandeau palette : chaque case arrondie individuellement (effet "coupelles d'echantillon"
   plutot qu'un bloc unique decoupe). Les reperes d'angle façon calibration (::before/::after)
   ont ete retires (2026-09-23, demande explicite "pas joli et s'adapte mal sur mobile") —
   desktop et mobile, pas de repli. */
.hero-palette-bar {
  position: relative; display: flex; gap: 4px; overflow: visible;
  min-height: 340px; border: none;
  transition: max-width 0.4s cubic-bezier(.22,1,.36,1), min-height 0.4s cubic-bezier(.22,1,.36,1),
    transform 0.45s cubic-bezier(.4,0,.2,1), opacity 0.4s ease;
}
.hero-block {
  position: relative; min-width: 64px; cursor: pointer;
  display: flex; align-items: flex-end; padding: 20px;
  border-radius: 10px; box-shadow: inset 0 0 0 1px rgba(255,255,255,0.35);
  transition: flex-grow 0.3s ease;
}
.hero-block.locked { box-shadow: inset 0 0 0 2px var(--accent); }
.hero-block.selected { box-shadow: inset 0 0 0 2px rgba(255,255,255,0.9); }
.hero-block.locked.selected { box-shadow: inset 0 0 0 2px var(--accent), inset 0 0 0 4px rgba(255,255,255,0.9); }

/* Au repos (deverrouille) : discret, fond clair translucide — pas de raison d'attirer l'oeil
   tant que rien n'est verrouille. Verrouille (.hero-block.locked) : fond fonce, comme avant,
   volontairement garde pour rester bien visible (demande explicite "laisse le fonce quand
   c'est locke pour plus de visibilite") — c'est l'etat qui doit se remarquer. */
.hero-lock-btn {
  position: absolute; top: 10px; right: 10px; width: 30px; height: 30px;
  border-radius: 50%; border: 1px solid rgba(255,255,255,0.4); background: rgba(255,255,255,0.22);
  color: #fff; font-size: 13px; cursor: pointer; display: flex; align-items: center; justify-content: center;
  transition: background 0.15s ease;
}
.hero-lock-btn:hover { background: rgba(255,255,255,0.38); }
.hero-block.locked .hero-lock-btn { background: rgba(0,0,0,0.4); border-color: rgba(255,255,255,0.32); }
.hero-block.locked .hero-lock-btn:hover { background: rgba(0,0,0,0.55); }

.hero-remove-btn {
  position: absolute; top: 10px; left: 10px; width: 24px; height: 24px;
  border-radius: 50%; border: 1px solid rgba(255,255,255,0.32); background: rgba(0,0,0,0.32);
  color: #fff; font-size: 14px; line-height: 1; cursor: pointer; display: flex; align-items: center; justify-content: center;
  opacity: 0; transition: opacity 0.15s, background 0.15s;
}
.hero-block:hover .hero-remove-btn { opacity: 1; }
/* Couleur ciblee (Paint Mix) : contrairement aux autres cases, reste visible en permanence —
   c'est la seule action possible sur ce bloc une fois une couleur choisie, pas la peine de la
   cacher au repos. */
.mix-target-block .hero-remove-btn { opacity: 1; }
.hero-remove-btn:hover { background: #8f2020; }
/* Palettes enregistrees : pas de bouton verrou (top-right libre), la croix de retrait
   d'une couleur s'y place plutot que sur la gauche comme pour une case Harmonie. */
.hero-remove-btn.top-right { left: auto; right: 10px; }

/* Bouton detail (ⓘ) : toujours visible (pas de opacity:0 au repos comme la suppression) —
   c'est le point d'entree explicite du panneau, il doit se voir sans avoir a survoler. */
/* Meme gabarit que .hero-lock-btn (30px, meme inset 10px depuis le bord — haut pour le
   cadenas, bas pour celui-ci, alignement vertical parfait sur le meme axe droit) : avant
   legerement plus petit (26px) sans raison de l'etre, demande explicite d'uniformiser. */
.hero-info-btn {
  position: absolute; bottom: 10px; right: 10px; width: 30px; height: 30px;
  border-radius: 50%; border: 1px solid rgba(255,255,255,0.32); background: rgba(0,0,0,0.32);
  color: #fff; cursor: pointer; display: flex; align-items: center; justify-content: center;
  transition: background 0.15s;
}
.hero-info-btn:hover { background: rgba(0,0,0,0.5); }

/* "+" a droite de la palette, meme langage discret que les icones du dock : invisible au
   repos, cercle gris au survol — pas de contour pointille, pas de boite permanente. */
.hero-palette-row { display: flex; align-items: center; gap: 10px; }
.hero-palette-row .hero-palette-bar { flex: 1; min-width: 0; }
.hero-add-btn {
  flex-shrink: 0; width: 38px; height: 38px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: none; border: none; padding: 0; margin: 0; color: var(--faint); cursor: pointer;
  transition: background 0.18s ease, color 0.18s ease, opacity 0.2s ease, width 0.3s ease, margin 0.3s ease;
}
.hero-add-btn svg { width: 16px; height: 16px; pointer-events: none; }
.hero-add-btn:hover { background: rgba(14,17,22,0.08); color: var(--text); }

/* Animation "classement" au moment de nommer/sauvegarder : la palette se resserre en parts
   egales et se centre (le "+" s'efface pour lui laisser toute la place), puis — une fois
   validee — glisse vers la droite comme si elle rejoignait Palettes enregistrees. Purement
   symbolique : la palette de travail n'est ni modifiee ni deplacee.
   La ligne passe en colonne (bar puis formulaire de nom empiles) et la hauteur de depart est
   figee en JS (voir setupIdentityActions) avant le retrecissement — sans ca, la case perd sa
   hauteur pleine largeur et tout ce qui suit (roue des regles, etc.) remonte avec elle. */
.hero-palette-row.saving { flex-direction: column; }
.hero-palette-row.saving .hero-palette-bar { max-width: 380px; min-height: 170px; margin: 0 auto; }
.hero-palette-row.saving .hero-block { flex-grow: 1 !important; }
.hero-palette-row.saving .hero-add-btn { opacity: 0; width: 0; height: 0; pointer-events: none; }
.hero-palette-row.filing .hero-palette-bar { transform: translateX(60vw) scale(0.85); opacity: 0; }
/* Mobile : la palette reste HORIZONTALE (comme le desktop, colonnes proportionnelles a chaque
   pourcentage de role) plutot qu'empilee verticalement — demande explicite, l'ancien empilement
   plein-largeur est retire plus bas (.hero-palette-bar/.hero-block). */

.variants-panel { display: flex; flex-wrap: wrap; gap: 8px; }
.variants-panel[hidden] { display: none; }
.variant-card {
  flex: 1; min-width: 140px; height: 52px; display: flex; border-radius: 10px; overflow: hidden;
  border: 1px solid var(--border); cursor: pointer; padding: 0; background: none; transition: border-color 0.15s;
}
.variant-card:hover { border-color: var(--accent); }
.variant-segment { display: block; }

.hero-block-label { display: flex; flex-direction: column; gap: 2px; pointer-events: none; }
.hero-block-role { font-size: 10px; text-transform: uppercase; letter-spacing: 0.1em; font-weight: 600; opacity: 0.85; }
/* Precision "vraie teinte Angelus" a cote du nom — casse normale et plus discrete pour rester
   un qualificatif secondaire, pas un second nom au meme poids visuel. */
.hero-block-angelus-tag { text-transform: none; font-weight: 500; letter-spacing: 0; opacity: 0.75; }
.hero-block-pct { font-size: 22px; font-weight: 700; line-height: 1.1; }
.hero-block-hex { font-size: 11px; font-weight: 500; opacity: 0.8; margin-top: 4px; }

/* Infos colorimetriques reelles (LCH) — memes donnees que le detail de case, juste
   affichees directement sur le bloc. */
.hero-block-lch { font-size: 10px; font-weight: 500; opacity: 0.7; margin-top: 6px; letter-spacing: 0.02em; }

.hero-block-empty {
  background: repeating-linear-gradient(135deg, #dfe6ea, #dfe6ea 10px, #e8edf0 10px, #e8edf0 20px);
  color: var(--muted); align-items: center; justify-content: center; flex-direction: column; gap: 8px; text-align: center;
}
.hero-block-empty .hero-block-label { align-items: center; }
.hero-block-empty-hint { font-size: 11px; color: var(--faint); }

/* Groupes de teintes repliables — utilises par les onglets "Angelus"/"Mes palettes" du
   panneau de choix de couleur unifie, et par la liste "Peintures deja en stock" (Pots
   necessaires). Repere structure (petite majuscule + +/- discret) plutot qu'un triangle natif. */
.color-group { border-top: 1px solid var(--border); }
.color-group:first-child { border-top: none; }
.color-group summary {
  cursor: pointer; padding: 9px 2px; color: var(--muted); font-size: 10.5px; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.08em; list-style: none;
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  transition: color 0.15s;
}
.color-group summary::-webkit-details-marker { display: none; }
.color-group summary::after { content: '+'; color: var(--faint); font-weight: 400; font-size: 13px; }
.color-group[open] summary::after { content: '−'; }
.color-group summary:hover { color: var(--text); }
.color-group[open] summary { color: var(--text); }

.swatch-row { display: flex; flex-wrap: wrap; gap: 8px; padding: 2px 0 16px; }
.swatch {
  width: 28px; height: 28px; border-radius: 8px; cursor: pointer;
  border: 1px solid rgba(14,17,22,0.08); position: relative; flex-shrink: 0;
  transition: transform 0.12s ease, box-shadow 0.12s ease;
}
.swatch:hover { transform: translateY(-2px) scale(1.06); box-shadow: 0 6px 14px rgba(14,17,22,0.22); }
.swatch:hover::after {
  content: attr(data-name);
  position: absolute; bottom: 36px; left: 50%; transform: translateX(-50%);
  background: var(--text); color: #f7fafb; padding: 3px 7px; border-radius: 6px;
  font-size: 10px; white-space: nowrap; z-index: 5; font-family: inherit;
}

/* Carte de detail (ⓘ) : meme verre depoli que le disque chromatique / le picker de couleur,
   mais dans le flux normal — sous TOUTES les cases (pas ancree a une seule), centree, juste
   assez large pour son contenu (pas de pleine largeur), et pousse le dock d'actions plus bas
   quand elle apparait. Separateur fin entre donnees mesurees (hex/RGB/LCH) et analyse
   (Angelus/melange) — seule touche "labo" gardee ici, sans repere d'angle. */
.slot-detail-panel {
  position: relative; align-self: center;
  width: fit-content; max-width: min(92%, 560px);
  padding: 16px 18px;
  background: rgba(255,255,255,0.6);
  backdrop-filter: blur(20px) saturate(180%);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  border: 1px solid rgba(255,255,255,0.7);
  border-radius: 20px;
  box-shadow: 0 1px 2px rgba(14,17,22,0.05), 0 24px 60px rgba(14,17,22,0.16);
  display: flex; flex-direction: column; gap: 10px;
  opacity: 0; transform: translateY(-6px);
  transition: opacity 0.28s ease, transform 0.28s cubic-bezier(.22,1,.36,1);
}
.slot-detail-panel.panel-visible { opacity: 1; transform: translateY(0); }
.slot-detail-panel[hidden] { display: none; }
.slot-detail-close {
  position: absolute; top: 14px; right: 16px; width: 20px; height: 20px; border-radius: 50%;
  background: none; border: none; display: flex; align-items: center; justify-content: center;
  color: var(--muted); font-size: 15px; cursor: pointer; line-height: 1; transition: background 0.15s, color 0.15s;
}
.slot-detail-close:hover { background: rgba(14,17,22,0.08); color: var(--text); }
.slot-detail-divider { border: none; border-top: 1px solid var(--border); margin: 0; }

@media (max-width: 640px) {
  /* Colonnes proportionnelles conservees (flex-direction:row herite) mais allongees pour
     donner du volume aux cases, forcement plus etroites qu'au desktop (demande explicite
     "les alonger un peu pour creer du volume") ; padding et polices reduits pour rester
     lisibles dans une colonne etroite (jusqu'a 5 cases sur ~340px utiles en monochromatique).
     Ligne LCH masquee ici : en plus de desencombrer, elle etait la cause du chevauchement
     avec le bouton ⓘ sous l'ancien empilement plein-largeur — le detail reste disponible
     au tap sur ⓘ. */
  .hero-palette-bar { min-height: 260px; }
  .hero-block { padding: 10px; min-width: 56px; }
  .hero-block-pct { font-size: 17px; }
  .hero-block-role { font-size: 8.5px; }
  .hero-block-hex { font-size: 9px; }
  .hero-block-lch { display: none; }
  /* Cadenas (haut-droite), retrait (haut-gauche) et ⓘ (bas-droite) etaient chacun a leur
     coin d'origine (pense pour des cases bien plus larges en desktop) : sur une colonne
     aussi etroite (56px+), retrait et cadenas se chevauchaient (24+30px de boutons sur
     56-77px de large), et le ⓘ mordait sur le texte role/%/hex qui occupe maintenant toute
     la largeur disponible en bas — signale explicitement, 2026-09-23. Empiles en colonne a
     droite a la place : plus de conflit avec le texte (relegue au bas, entierement libre),
     plus de chevauchement entre eux (36px d'ecart, boutons reduits a 26px).
     Le retrait n'est plus reserve au survol (opacity:0 par defaut, .hero-block:hover pour le
     reveler) : au toucher il n'y a pas de vrai hover permanent, ce qui le faisait apparaitre
     de facon aleatoire et superposee au cadenas selon l'etat de "sticky hover" du navigateur
     mobile — toujours visible ici, comme les 2 autres.
     :has() (support desormais large) distingue Create (cadenas present, 3 controles empiles)
     de Mes palettes (pas de cadenas, 2 controles) sans dupliquer 2 jeux de regles complets. */
  .hero-palette-bar .hero-block .hero-lock-btn,
  .hero-palette-bar .hero-block .hero-remove-btn,
  .hero-palette-bar .hero-block .hero-info-btn {
    width: 26px; height: 26px;
  }
  .hero-palette-bar .hero-block .hero-remove-btn { opacity: 1; left: auto; right: 10px; top: 10px; bottom: auto; }
  .hero-palette-bar .hero-block .hero-info-btn { top: 46px; right: 10px; bottom: auto; }
  .hero-palette-bar .hero-block:has(.hero-lock-btn) .hero-remove-btn { top: 46px; }
  .hero-palette-bar .hero-block:has(.hero-lock-btn) .hero-info-btn { top: 82px; }
  /* Le "+" (Create) et la paire fleche/"+" (Mes palettes) desequilibraient la ligne palette
     desormais horizontale — demande explicite de les deplacer hors de cette ligne. */
  #hero-add-case-btn { display: none; }
  #hero-add-case-btn-mobile, .hero-add-case-btn-mobile-sep { display: flex; }
  .saved-palette-entry .hero-palette-row > .hero-add-btn { display: none; }
  .saved-palette-header-mobile-actions { display: flex; align-items: center; gap: 4px; margin-left: auto; }
}

/* ---- Zone de prelevement : toujours visible (plus de repli), zone de depot glisser-
   deposer minimaliste — icone seule, pas de texte d'instruction (le geste se comprend
   de lui-meme, meme langage discret que le reste de l'interface). ---- */

/* Police Inter (page marketing) pour tout le bandeau, plutot que la mono heritee du corps de
   page (demande explicite, 2026-09-24, meme traitement que l'onglet Stock) — un seul point
   d'entree sur le conteneur : aucune classe de dessous (pastille Auto/Manuel, titre/hint
   d'invite, libelle de taille de prelevement...) ne fixe son propre font-family. */
.zone-prelevement { font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif; }

.zone-dropzone {
  display: flex; align-items: center; justify-content: center;
  height: 120px; border: 1px solid var(--border); border-radius: 14px;
  color: var(--faint); cursor: pointer; background: none;
  transition: border-color 0.15s ease, background 0.15s ease, color 0.15s ease;
}
.zone-dropzone[hidden] { display: none; }
.zone-dropzone:hover { border-color: var(--border-strong); color: var(--muted); }
.zone-dropzone:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.zone-dropzone.dragover {
  border-color: var(--accent); background: rgba(0,152,201,0.06); color: var(--accent);
}
.zone-dropzone svg { pointer-events: none; }

/* Barre "Taille du prelevement" : meme largeur que l'image (mesuree en JS), centree, et
   meme habillage bipolaire-like que les curseurs d'Ambiance (piste epaisse, curseur bague) —
   ici la valeur part du bord gauche (0..15), pas du centre, puisqu'il n'y a pas de neutre. */
.zone-radius-field {
  margin: 0 auto var(--s3); max-width: 640px;
  display: flex; flex-direction: column; gap: 6px;
}
.zone-radius-field[hidden] { display: none; }
.zone-radius-field input[type="range"] {
  height: 3px; border-radius: 2px; background: var(--border);
}
.zone-radius-field input[type="range"]::-webkit-slider-thumb {
  width: 14px; height: 14px; border-radius: 50%; background: var(--text);
  border: 2px solid #ffffff; box-shadow: 0 0 0 1px var(--border-strong);
}
.zone-radius-field input[type="range"]::-moz-range-thumb {
  width: 12px; height: 12px; border-radius: 50%; background: var(--text);
  border: 2px solid #ffffff; box-shadow: 0 0 0 1px var(--border-strong);
}
.zone-radius-field input[type="range"]::-moz-range-track { height: 3px; border-radius: 2px; }

.zone-image-area { display: flex; align-items: center; justify-content: center; gap: var(--s3); }
.pipette-wrap { position: relative; max-width: 640px; }
.pipette-wrap:not(:empty) { margin-top: var(--s3); }
/* Zoom actif : l'image grandit en pixels reels (cf. applyImageZoom) plutot que via un
   transform, pour que le cadre (inline-block, shrink-wrap) grandisse avec elle et garde la
   croix de retrait a son vrai coin. Le conteneur devient defilant plutot que de deborder sur
   le reste de la page. */
.pipette-wrap.zoomed { overflow: auto; max-height: 560px; border-radius: 10px; }
/* #image-wrap centre son contenu (justify-content:center, voir plus bas) quand l'image est
   plus etroite que le conteneur — une fois zoomee et plus large que lui, ce centrage
   deborderait a parts egales des deux cotes et rendrait le cote gauche inaccessible au
   defilement (scrollLeft ne descend jamais sous 0). On aligne donc a gauche pendant le zoom. */
#image-wrap.zoomed { justify-content: flex-start; }
.pipette-frame.zoomed { max-width: none; }
/* Selecteur par id necessaire ici pour l'emporter sur "#image-wrap img { max-width:640px }"
   (regle de base plus specifique qu'une simple combinaison de classes). */
#image-wrap.zoomed img { max-width: none; }

/* Barre de zoom, meme habillage que "Taille du prelevement" juste au-dessus (piste epaisse,
   curseur bague blanc) mais verticale et postee a gauche de l'image, + un "+"/"-" a ses deux
   bouts plutot qu'une simple piste nue. */
.zone-zoom-field {
  display: flex; flex-direction: column; align-items: center; gap: 10px; flex-shrink: 0;
  margin-top: var(--s3);
}
.zone-zoom-field[hidden] { display: none; }
.zone-zoom-btn {
  flex-shrink: 0; width: 30px; height: 30px; border-radius: 50%; border: none;
  background: none; color: var(--muted); cursor: pointer;
  display: flex; align-items: center; justify-content: center; transition: background 0.15s, color 0.15s;
}
.zone-zoom-btn:hover { background: rgba(14,17,22,0.08); color: var(--text); }
.zone-zoom-btn svg { width: 16px; height: 16px; pointer-events: none; }
.zone-zoom-slider-wrap { width: 20px; height: 120px; display: flex; align-items: center; justify-content: center; }
.zone-zoom-slider-wrap input[type="range"] {
  width: 120px; height: 3px; margin: 0; border-radius: 2px; background: var(--border);
  transform: rotate(-90deg);
}
.zone-zoom-slider-wrap input[type="range"]::-webkit-slider-thumb {
  width: 14px; height: 14px; border-radius: 50%; background: var(--text);
  border: 2px solid #ffffff; box-shadow: 0 0 0 1px var(--border-strong);
}
.zone-zoom-slider-wrap input[type="range"]::-moz-range-thumb {
  width: 12px; height: 12px; border-radius: 50%; background: var(--text);
  border: 2px solid #ffffff; box-shadow: 0 0 0 1px var(--border-strong);
}
.zone-zoom-slider-wrap input[type="range"]::-moz-range-track { height: 3px; border-radius: 2px; }
/* Contrepoids invisible, meme largeur que .zone-zoom-field : sans lui, centrer la ligne
   entiere (barre de zoom + image) decale l'image vers la droite par rapport a "Taille du
   prelevement" au-dessus — avec, l'image reste sur le meme axe, la barre venant se poser a
   sa gauche sans la pousser. */
.zone-zoom-field-spacer { width: 30px; flex-shrink: 0; }
.zone-zoom-field-spacer[hidden] { display: none; }
@media (max-width: 640px) {
  .zone-image-area { flex-direction: column-reverse; }
  .zone-zoom-field { flex-direction: row; margin-top: 0; }
  .zone-zoom-field-spacer { display: none; }
  .zone-zoom-slider-wrap { width: 120px; height: 20px; }
  .zone-zoom-slider-wrap input[type="range"] { width: 120px; transform: none; }
}

/* ---- Roue des blocs de prelevement (au-dessus de la barre + de l'image) : un seul bloc
   visible a la fois — "Prelevement automatique" puis un par bandeau manuel — navigable aux
   fleches, meme principe que la roue des regles d'harmonie plus haut (coins arrondis,
   reperes d'angle repris de la barre de palette pour la case affichee). ---- */
.zone-cases-row {
  display: flex; flex-direction: column; align-items: center; gap: var(--s2);
  margin-bottom: var(--s3);
}
.zone-cases-row[hidden] { display: none; }

.zone-case-stage {
  position: relative; width: 100%;
  border-radius: 10px; box-shadow: inset 0 0 0 1px rgba(14,17,22,0.06);
}
.zone-case-stage::before, .zone-case-stage::after {
  content: ''; position: absolute; width: 14px; height: 14px; pointer-events: none;
  border: 1.5px solid var(--border-strong);
}
.zone-case-stage::before { top: -8px; left: -8px; border-right: none; border-bottom: none; }
.zone-case-stage::after { bottom: -8px; right: -8px; border-left: none; border-top: none; }
.zone-case-stage.slide-in { animation: zoneCaseSlideIn 0.22s ease; }
@keyframes zoneCaseSlideIn {
  from { opacity: 0; transform: translateX(calc(var(--zone-slide-dir, 1) * 12px)); }
  to { opacity: 1; transform: translateX(0); }
}
@media (prefers-reduced-motion: reduce) {
  .zone-case-stage.slide-in { animation: none; }
}

.zone-case {
  position: relative; min-height: 280px; max-height: 460px; overflow-y: auto;
  border-radius: 10px; padding: 28px; background: var(--panel);
}
/* Etat vide (pas encore d'image, ou analyse en cours) : boite nettement moins haute que la
   grille remplie (min-height de base) — sinon le message, centre dans tout l'espace restant,
   se retrouve loin de l'en-tete au-dessus. */
.zone-case-empty { display: flex; flex-direction: column; min-height: 170px; }
.zone-case-invite {
  flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 6px; text-align: center; margin: 0 auto; max-width: 42ch;
}
.zone-case-invite-title { font-size: 14px; font-weight: 700; color: var(--text); }
.zone-case-invite p { margin: 0; }

/* Bloc "prelevement automatique" : une grille de cases-couleur, une par dominante detectee —
   simplifiee au strict minimum (# + L) pour rester lisible a cette echelle agrandie, plus
   grand et centre, seul bloc actif au premier affichage. */
.zone-case-auto-header {
  display: flex; align-items: center; justify-content: center; position: relative;
  margin-bottom: var(--s4);
}
.zone-case-icon-btn {
  position: absolute; top: 50%; transform: translateY(-50%);
  width: 28px; height: 28px; border-radius: 50%; border: 1px solid var(--border);
  background: none; color: var(--muted); cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  transition: background 0.15s, color 0.15s;
}
.zone-case-icon-btn:hover { background: rgba(14,17,22,0.08); color: var(--text); }
.zone-case-icon-btn svg { width: 14px; height: 14px; pointer-events: none; }
.zone-case-icon-btn.right { right: 0; }
.zone-case-icon-btn.left { left: 0; }

.zone-auto-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: 14px;
}
.zone-auto-swatch {
  position: relative;
  display: flex; flex-direction: column; align-items: center; justify-content: flex-end; gap: 5px;
  min-height: 160px; border-radius: 10px; padding: 14px 10px;
}
.zone-auto-hex { font-size: 15px; font-weight: 700; }
.zone-auto-lch { font-size: 12px; opacity: 0.85; }

/* Bloc "prelevement manuel" (vue editeur) : meme coquille et memes specimens que le bloc
   automatique, en-tete centre avec une fleche de retour au lieu du bouton d'envoi. */
.zone-case-band-body { display: flex; flex-direction: column; gap: 14px; }

/* Bloc "prelevement manuel" (vue grille) : une case par coloris, vide ou remplie, dans la
   meme grille que le bloc automatique — un "×" au survol vide la case, cliquer dessus ouvre
   l'editeur de prelevement pour cette case precise. */
.zone-manual-swatch {
  position: relative; min-height: 160px; border-radius: 10px; overflow: hidden;
}
.zone-manual-swatch-empty {
  background: repeating-linear-gradient(135deg, #dfe6ea, #dfe6ea 10px, #e8edf0 10px, #e8edf0 20px);
  color: var(--muted);
}
.zone-manual-edit {
  position: absolute; inset: 0; width: 100%; height: 100%; border: none; background: none;
  color: inherit; cursor: pointer; font-family: inherit; padding: 14px 10px;
  display: flex; flex-direction: column; align-items: center; justify-content: flex-end; gap: 5px;
}
.zone-manual-swatch-empty .zone-manual-edit { justify-content: center; }
.zone-manual-empty-hint { font-size: 12px; color: var(--faint); }
.zone-manual-remove {
  position: absolute; top: 8px; right: 8px; z-index: 1; width: 20px; height: 20px; border-radius: 50%;
  background: rgba(255,255,255,0.75); border: 1px solid var(--border); color: var(--muted);
  font-size: 13px; line-height: 1; cursor: pointer; padding: 0; opacity: 0; transition: opacity 0.15s;
  display: flex; align-items: center; justify-content: center;
}
.zone-manual-swatch:hover .zone-manual-remove { opacity: 1; }
.zone-manual-add-cell {
  min-height: 160px; border-radius: 10px; border: 1px dashed var(--border);
  background: none; color: var(--faint); cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  transition: border-color 0.15s, color 0.15s, background 0.15s;
}
.zone-manual-add-cell:hover { border-color: var(--border-strong); color: var(--text); background: rgba(14,17,22,0.03); }
.zone-manual-add-cell svg { width: 20px; height: 20px; pointer-events: none; }

/* Choix Auto/Manuel : meme verre depoli blanc que le dock d'actions au-dessus de la palette
   (.hero-dock) — volontairement DIFFERENT de la pilule sombre du menu general (bandeau
   superieur), pour que l'oeil distingue tout de suite une navigation generale (page) d'une
   navigation locale (a l'interieur d'un bloc). Indicateur sombre glissant (meme encre que les
   boutons pleins de l'appli) derriere le mode actif, plutot qu'un fond clair inverse. */
.zone-mode-switch {
  position: relative; display: flex; width: fit-content; gap: 2px; padding: 4px;
  margin: 0 auto; border-radius: 999px;
  background: rgba(255,255,255,0.55);
  backdrop-filter: blur(16px) saturate(180%);
  -webkit-backdrop-filter: blur(16px) saturate(180%);
  border: 1px solid rgba(255,255,255,0.7);
  box-shadow: 0 1px 2px rgba(14,17,22,0.05), 0 10px 28px rgba(14,17,22,0.08);
}
.zone-mode-switch-indicator {
  position: absolute; top: 0; left: 0; z-index: 0;
  border-radius: 999px; background: var(--text);
  transition: transform 0.35s cubic-bezier(.22,1,.36,1), width 0.35s cubic-bezier(.22,1,.36,1);
}
.zone-mode-btn {
  position: relative; z-index: 1; flex: none;
  background: none; border: none; border-radius: 999px;
  padding: 8px 18px; cursor: pointer; font-size: 11px; font-weight: 600;
  font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif;
  letter-spacing: 0.04em; text-transform: uppercase; color: var(--muted); white-space: nowrap;
  transition: color 0.2s ease;
}
.zone-mode-btn:hover { color: var(--text); }
.zone-mode-btn.active, .zone-mode-btn.active:hover { color: #f7fafb; }

/* ---- Pilule Libre / Mes palettes / Angelus — meme recette que .zone-mode-switch (Auto/
   Manuel), reprise pour le panneau de choix de couleur unifie ci-dessous : un seul et meme
   commutateur partout ou une case choisit une couleur (Harmonie, Melange). ---- */
.picker-switch {
  position: relative; display: flex; width: fit-content; gap: 2px; padding: 4px;
  margin: 0 auto 16px; border-radius: 999px;
  background: rgba(255,255,255,0.55);
  backdrop-filter: blur(16px) saturate(180%);
  -webkit-backdrop-filter: blur(16px) saturate(180%);
  border: 1px solid rgba(255,255,255,0.7);
  box-shadow: 0 1px 2px rgba(14,17,22,0.05), 0 10px 28px rgba(14,17,22,0.08);
}
.picker-switch-indicator {
  position: absolute; top: 0; left: 0; z-index: 0;
  border-radius: 999px; background: var(--text);
  transition: transform 0.3s cubic-bezier(.22,1,.36,1), width 0.3s cubic-bezier(.22,1,.36,1);
}
.picker-switch-btn {
  position: relative; z-index: 1; flex: none;
  background: none; border: none; border-radius: 999px;
  padding: 9px 20px; cursor: pointer; font-size: 12px; font-weight: 600;
  font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif;
  letter-spacing: 0.03em; color: var(--muted); white-space: nowrap;
  transition: color 0.2s ease;
}
.picker-switch-btn:hover { color: var(--text); }
.picker-switch-btn.active, .picker-switch-btn.active:hover { color: #f7fafb; }

/* Panneau de choix de couleur unifie : meme carte de verre depoli que le detail (ⓘ), en flux
   normal (pousse ce qui suit vers le bas), mais volontairement plus genereux — c'est un
   ecran de choix a part entiere, pas juste une ligne d'info. Police Inter (page marketing),
   pas la mono heritee du corps de page (demande explicite, 2026-09-24, meme traitement que
   Stock/prelevement sur image) — .picker-hex-input garde sa propre police mono explicite
   (code hex, pas un libelle), non affectee par cet ancetre. Partage par les 3 contextes
   (Create, Paint Mix cible, Melange libre) puisque tous les 3 reutilisent .color-picker-panel. */
.color-picker-panel {
  max-width: min(94%, 640px); padding: 22px 26px; gap: 4px;
  font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif;
}
.picker-tab-body { display: flex; flex-direction: column; min-width: 300px; }
.picker-tab-body[hidden] { display: none; }

/* ---- Selecteur "Libre" : carre saturation/valeur + reglette de teinte, toujours visible
   (jamais de popover systeme) — meme carte de verre que le reste, curseurs ronds a bordure
   blanche, comme les pickers de reference (Figma/Photoshop) plutot que l'input natif du
   navigateur, incoherent et peu soigne d'un OS a l'autre. ---- */
.picker-hex-input {
  width: 100%; padding: 11px 14px; margin-bottom: 14px; box-sizing: border-box;
  border: 1px solid var(--border); border-radius: 8px; background: #fff;
  font-family: 'JetBrains Mono', ui-monospace, monospace; font-size: 13px; font-weight: 600;
  color: var(--text); letter-spacing: 0.02em; transition: border-color 0.15s;
}
.picker-hex-input:focus { outline: none; border-color: var(--text); }
.picker-sv {
  position: relative; width: 100%; height: 170px; border-radius: 12px;
  box-shadow: inset 0 0 0 1px rgba(14,17,22,0.1); cursor: crosshair;
  margin-bottom: 16px; overflow: hidden; touch-action: none;
}
.picker-sv-white, .picker-sv-black { position: absolute; inset: 0; pointer-events: none; }
.picker-sv-white { background: linear-gradient(to right, #fff, rgba(255,255,255,0)); }
.picker-sv-black { background: linear-gradient(to top, #000, rgba(0,0,0,0)); }
.picker-sv-thumb {
  position: absolute; width: 18px; height: 18px; border-radius: 50%;
  border: 2px solid #fff; box-shadow: 0 0 0 1px rgba(14,17,22,0.35), 0 2px 6px rgba(14,17,22,0.3);
  transform: translate(-50%, -50%); pointer-events: none;
}
.picker-hue {
  position: relative; width: 100%; height: 16px; border-radius: 999px; cursor: pointer;
  background: linear-gradient(to right, #f00, #ff0, #0f0, #0ff, #00f, #f0f, #f00);
  touch-action: none;
}
.picker-hue-thumb {
  position: absolute; top: 50%; width: 20px; height: 20px; border-radius: 50%;
  background: #fff; border: 2px solid #fff;
  box-shadow: 0 0 0 1px rgba(14,17,22,0.35), 0 1px 4px rgba(14,17,22,0.3);
  transform: translate(-50%, -50%); pointer-events: none;
}
/* width:auto sur ce conteneur a defilement ne remonte pas sa largeur au panneau parent
   (width:fit-content) — sans min-width explicite le panneau reste bloque a la largeur de
   l'onglet Libre. On force donc ces deux onglets (Mes palettes/Angelus) a s'elargir pour
   montrer davantage de pastilles par ligne. */
.picker-swatch-groups { max-height: 320px; overflow-y: auto; padding-right: 4px; min-width: min(80vw, 560px); }
/* Format genereux du nouveau panneau : pastilles un peu plus grandes que dans l'ancien popover
   exigu (28px), sans toucher au reste des usages partages de .swatch (Pots necessaires...). */
.picker-swatch-groups .swatch { width: 34px; height: 34px; }
.picker-swatch-groups .color-group summary { font-size: 11.5px; padding: 11px 2px; }

/* Menu de choix de couleur (Libre/Mes palettes/marque — Create, Paint Mix cible, Melange
   libre : les 3 partagent .color-picker-panel) : signale explicitement sur tout le site,
   2026-09-23 — "le free n'est pas aligne", "my palettes deborde sur la droite", "Angelus...
   deborde aussi". Cause commune aux 3 : .slot-detail-panel est width:fit-content, et
   .picker-tab-body (300px, ci-dessus) / .picker-swatch-groups (min(80vw,560px), juste
   au-dessus) imposent chacun un plancher de largeur pense pour du desktop, plus large que
   l'interieur du panneau une fois celui-ci plafonne a 94vw par .color-picker-panel — le
   contenu deborde alors du cadre en verre depoli (visible malgre l'absence de scroll
   horizontal de PAGE, puisque le debordement reste sous 100vw). "Free" en souffre aussi, mais
   dans l'autre sens : la pastille de switch (Libre/Mes palettes/marque, .picker-switch) est
   width:fit-content sur ses 3 boutons — elle reste a SA propre largeur naturelle (~283px,
   mesuree) au lieu de suivre celle, plus etroite une fois corrigee ci-dessous (~253px), du
   contenu juste en dessous : les deux bords ne tombent plus au meme endroit, d'ou l'alignement
   casse signale.
   Fige le panneau a sa largeur maximale (au lieu de suivre l'onglet actif), fait suivre
   min-width:0 + width:100% aux 2 planchers fautifs, et etire la pastille de switch a cette
   meme largeur (ses 3 boutons passant de largeur naturelle a flex:1 pour se partager
   equitablement l'espace, comme un vrai segmented control) : les 3 onglets ET le switch
   remplissent alors exactement le meme cadre, sans jamais le faire sauter de taille en
   changeant d'onglet (la vraie raison d'etre de ces planchers a l'origine, cf leurs propres
   commentaires) — juste une largeur fixe au lieu d'une largeur minimale, sans effet en desktop
   ou le panneau avait de toute facon la place. Placee apres les regles de base (pas dans le
   bloc mobile partage plus haut) : la cascade CSS tranche par ordre d'apparition dans le
   fichier a specificite egale, une media query n'apporte aucune specificite supplementaire —
   un override place AVANT la regle de base qu'il vise perd contre elle. */
@media (max-width: 640px) {
  .color-picker-panel { width: min(94%, 640px); }
  .picker-tab-body { min-width: 0; width: 100%; }
  .picker-swatch-groups { min-width: 0; width: 100%; }
  .picker-switch { width: 100%; }
  .picker-switch-btn { flex: 1; text-align: center; padding-left: 4px; padding-right: 4px; }
}

.zone-image-remove-btn {
  position: absolute; top: 10px; right: 10px; width: 28px; height: 28px;
  border-radius: 50%; border: 1px solid rgba(255,255,255,0.32); background: rgba(0,0,0,0.4);
  color: #fff; cursor: pointer; display: flex; align-items: center; justify-content: center;
  transition: background 0.15s;
}
.zone-image-remove-btn svg { width: 14px; height: 14px; pointer-events: none; }
.zone-image-remove-btn:hover { background: #8f2020; }

.wheel-wrap { display: flex; justify-content: center; padding: 10px 0; }
.wheel {
  position: relative; width: 260px; height: 260px; border-radius: 50%;
  background: conic-gradient(
    hsl(0,80%,55%), hsl(60,80%,55%), hsl(120,80%,55%), hsl(180,80%,55%),
    hsl(240,80%,55%), hsl(300,80%,55%), hsl(360,80%,55%)
  );
  border: 1px solid var(--border);
}
.wheel-marker {
  position: absolute; width: 16px; height: 16px; border-radius: 50%;
  border: 1.5px solid #fff; box-shadow: 0 0 0 1px var(--text);
  transform: translate(-50%, -50%);
}

.wheel-marker.locked { border-color: var(--accent); box-shadow: 0 0 0 1.5px var(--accent); }
.role-card-row {
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  font-size: 11px; color: var(--text); margin-bottom: 4px;
}
.role-card-row .mini-dot { width: 15px; height: 15px; border-radius: 6px; border: 1px solid var(--border); flex-shrink: 0; }
.role-card-row .match-quality { margin-left: auto; }
.role-card-actions { margin-top: 8px; }
/* "Explorer les melanges" (panneau de detail Create/Mes palettes) : meme pastille minimaliste
   transparente que le bouton "POOL" (2026-09-24, demande explicite "design minimaliste"),
   dupliquee plutot que partagee via une classe commune — .need-idle-btn porte aussi une
   surcharge mobile propre a son contexte (bandeau Pool) qui n'a pas de sens ici. */
.slot-detail-explore-btn {
  background: transparent; color: var(--text); border: 1px solid var(--border-strong);
  border-radius: 999px; padding: 10px 24px; cursor: pointer;
  font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif;
  font-size: 12px; font-weight: 600; letter-spacing: 0.12em; text-transform: uppercase;
  transition: background 0.15s ease, border-color 0.15s ease;
}
.slot-detail-explore-btn:hover { background: rgba(14,17,22,0.05); border-color: var(--text); }

/* Ordre de melange (buildRecipeRowsHtml) : pastille de rang devant chaque ligne, tag
   "a incorporer progressivement" sur la peinture la moins dosee (la plus concentree), note
   d'explication en dessous des lignes. Meme registre visuel que .match-quality/.owned-pick-tag
   (bracket-style) pour le tag, mais en --muted : ce n'est pas un jugement de qualite. */
.mix-order-badge {
  display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0;
  width: 16px; height: 16px; border-radius: 50%; border: 1px solid var(--border-strong);
  font-size: 9px; font-weight: 700; color: var(--muted);
}
.mix-order-caution {
  background: none; border: none; padding: 0; font-size: 10px; font-weight: 600;
  letter-spacing: 0.04em; white-space: nowrap; color: var(--muted);
}
.mix-order-caution::before { content: '['; }
.mix-order-caution::after { content: ']'; }
.mix-order-note {
  flex-basis: 100%; margin: 6px 0 0; font-size: 11px; line-height: 1.5;
  color: var(--muted); font-style: italic;
}
/* Paint Mix uniquement (opts.inter, buildRecipeRowsHtml) — police de la page marketing/des
   explications, comme le reste des textes deja harmonises ce chantier. Pool garde la mono par
   defaut du site (demande explicite de ne pas y toucher). */
.mix-order-note-inter { font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif; }

/* Bandes de couleur pleine largeur pour une vraie recette (buildRecipeBandHtml, 2026-09-23,
   demande explicite) — remplace la ligne pastille+nom+% pour les cas ou il y a plusieurs
   peintures a melanger : l'aplat de couleur EST la peinture, nom + % ecrits directement dessus,
   bien plus lisible. Legerement plus large que la case "Option N" au-dessus (voir la largeur
   des conteneurs parents, .mix-info-banner/.need-detail-item, non touchee ici). */
.recipe-band-list { display: flex; flex-direction: column; gap: 10px; margin: 6px 0 0; }
.recipe-band {
  display: flex; align-items: center; gap: 14px;
  min-height: 64px; box-sizing: border-box; border-radius: 10px; padding: 14px 18px;
}
.recipe-band-badge {
  flex-shrink: 0; width: 24px; height: 24px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-size: 12px; font-weight: 700;
}
.recipe-band-name { flex: 1; min-width: 0; font-size: 16px; font-weight: 700; }
.recipe-band-tag {
  display: block; margin-top: 2px; font-size: 10px; font-weight: 600;
  letter-spacing: 0.04em; text-transform: uppercase; opacity: 0.85;
}
.recipe-band-pct { flex-shrink: 0; font-size: 26px; font-weight: 700; }

.arrow { color: var(--muted); }

/* ---- Onglet Stock ---- */

/* Police Inter (page marketing) pour tout l'onglet, plutot que la mono heritee du corps de
   page (demande explicite, 2026-09-24) — un seul point d'entree sur le conteneur de l'onglet :
   aucune des classes de dessous (titres de categorie, noms de peinture, hint...) ne fixe son
   propre font-family, la cascade suffit donc a tout couvrir sans regle par regle. */
#tab-stock { font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif; }

/* Bandeau visible, meme habillage verre depoli que le bandeau de stats de "Need" — utilise a
   la fois pour "Mon stock" et pour chaque categorie Angelus ci-dessous. */
.stock-banner {
  padding: 24px 28px;
  background: rgba(255,255,255,0.6);
  backdrop-filter: blur(20px) saturate(180%);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  border: 1px solid rgba(255,255,255,0.7);
  border-radius: 20px;
  box-shadow: 0 1px 2px rgba(14,17,22,0.05), 0 24px 60px rgba(14,17,22,0.16);
}
.stock-category-banner { margin-bottom: 20px; }
.stock-category-banner:last-child { margin-bottom: 0; }
.stock-category-header {
  display: flex; align-items: baseline; justify-content: space-between; gap: 10px; margin-bottom: 16px;
}
.stock-category-title { font-size: 13px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; color: var(--text); }
.stock-category-count { font-size: 11px; color: var(--muted); white-space: nowrap; }

/* "Mon stock" : le libelle vit desormais DANS le bandeau (meme .stock-category-header que
   chaque famille de peinture plus bas), plus en <h2> au-dessus — un seul bandeau verre
   depoli, pas une boite + un titre separes au-dessus. Vide, le bandeau garde la meme
   epaisseur qu'avec une peinture (min-height:120px sur le message, identique a
   .need-pot-card) : sans ca il s'affaissait a la hauteur d'une seule ligne de texte des
   qu'on retirait la derniere peinture du stock. */
#my-stock-grid .hint {
  min-height: 120px; margin: 0;
  display: flex; align-items: center; justify-content: center; text-align: center;
}

/* Bouton info (Peintures) : remplace la phrase "Clique sur une teinte..." toujours visible
   par un point d'entree discret — meme langage que les autres dock icon-buttons du site
   (transparent au repos, fond leger au survol), volontairement PETIT ici (pas de demande
   de le rendre gros comme celui de Need). */
.stock-info-btn {
  width: 26px; height: 26px; border-radius: 50%; flex-shrink: 0; margin-bottom: var(--s3);
  border: 1px solid var(--border); background: transparent; color: var(--muted); cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.stock-info-btn:hover { background: rgba(14,17,22,0.08); color: var(--text); border-color: var(--border-strong); }
.stock-info-btn svg { width: 14px; height: 14px; }
#owned-paints-info-panel { margin-bottom: var(--s3); }
#owned-paints-info-panel .hint { margin: 0; }

/* ---- Onglet Pots necessaires — memes codes visuels que les onglets precedents ---- */

.owned-swatch-row { display: flex; flex-wrap: wrap; gap: 14px; padding: 6px 0 12px; }
.owned-swatch-item { display: flex; flex-direction: column; align-items: center; gap: 5px; width: 60px; }
.owned-swatch-label { font-size: 9.5px; color: var(--muted); text-align: center; line-height: 1.2; }
.owned-swatch {
  width: 52px; height: 52px; border-radius: 10px; cursor: pointer; position: relative;
  border: 1px solid var(--border); transition: transform 0.1s, border-color 0.15s;
}
.owned-swatch:hover { transform: translateY(-2px); }
.owned-swatch.selected { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.owned-swatch-check {
  position: absolute; bottom: -6px; right: -6px; width: 20px; height: 20px; border-radius: 50%;
  background: var(--accent); color: #fff; font-size: 11px; font-weight: 800;
  display: flex; align-items: center; justify-content: center; opacity: 0;
  transition: opacity 0.15s; border: 2px solid var(--bg);
}
.owned-swatch.selected .owned-swatch-check { opacity: 1; }

/* Detail par couleur : plus de liste divisee par des traits — l'espacement seul separe les
   cartes (comme .panel), et les puits de couleur sont plus grands (memes proportions que les
   cases Harmonie/Melange). */
.need-detail-list { display: flex; flex-direction: column; gap: 22px; }
/* Chaque couleur porte son propre panneau deroulant (voir renderShoppingDetail) : en colonne
   pour que .slot-detail-panel (align-self:center) se centre sous SA case, et pousse
   uniquement les couleurs suivantes vers le bas quand il s'ouvre. */
.need-detail-item { display: flex; flex-direction: column; gap: 14px; }
/* Refonte 2026-09-24 (demande explicite : le texte a cote des 2 cases — recette + ΔE — etait
   "charge et peu lisible"). Les 2 bandeaux Ciblee/Obtenue passent de largeur fixe (150px) a
   flex:1 chacun : ensemble ils occupent desormais toute la largeur de la section (comme
   .hero-palette-bar, .mix-info-banner...), plus seulement une petite portion a gauche suivie
   d'un grand vide a droite. flex-wrap retire : 2 enfants fluides ne debordent jamais (l'ancien
   3e enfant texte, .need-detail-info, est ce qui forçait le wrap — supprime avec lui). Tout ce
   qui etait a cote (recette, ΔE) vit maintenant EN SURIMPRESSION sur le bandeau "Obtenue"
   lui-meme (voir plus bas) plutot qu'a cote en texte separe. */
.need-detail-card { display: flex; align-items: center; gap: 16px; }
/* Beaucoup plus grand qu'un bouton ⓘ ordinaire (42px, avant 28px ; icone 20px, avant 13px) et
   un fond leger au repos plutot que transparent (les autres dock icon-buttons du site restent
   transparents au repos, mais celui-ci remplace desormais l'ancienne info texte "N palette(s)"
   — demande explicite qu'il soit "beaucoup plus gros et visible", pas juste une action
   secondaire discrete).
   2e passe (2026-09-24) : deplace du texte a cote vers le CENTRE du bandeau "Obtenue" lui-meme
   (demande explicite) — position absolue + verre translucide blanc (comme .hero-info-btn) au
   lieu du gris neutre d'avant (pense pour un fond de page uni), pour rester lisible sur une
   couleur arbitraire ; `color:inherit` recupere le blanc/noir deja choisi par
   getSwatchTextColor sur le bandeau parent. */
.need-detail-info-btn {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 1;
  width: 42px; height: 42px; border-radius: 50%;
  border: 1px solid rgba(255,255,255,0.4); background: rgba(255,255,255,0.24); color: inherit; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  transition: background 0.15s;
}
.need-detail-info-btn:hover { background: rgba(255,255,255,0.4); }
.need-detail-info-btn svg { pointer-events: none; }

/* Menu deroulant plus large et plus lisible qu'ailleurs (le panneau de detail de case est
   partage avec Create/My palettes, dont le gabarit compact reste inchange) — ne contient plus
   que les peintures a melanger + proportions (2026-09-17, demande explicite : couleur
   ciblee/obtenue deja affichees juste au-dessus sur les 2 cases, ne plus les repeter ici —
   voir buildShoppingDetailInfoHtml). Carres de couleur des peintures agrandis (28px, avant
   22px) pour rester lisibles a cote du reste, agrandi aussi. */
.need-detail-item .slot-detail-panel { width: 100%; max-width: 820px; padding: 22px 26px; gap: 14px; }
.need-detail-item .role-card-row { font-size: 15px; gap: 12px; margin-bottom: 10px; }
.need-detail-item .role-card-row .mini-dot { width: 28px; height: 28px; border-radius: 8px; }
.need-detail-item .mix-detail-pct { font-size: 15px; }
/* Bandeaux "Ciblee"/"Obtenue" : fluides (flex:1, plus 150px fixe) et bien plus hauts (220px,
   avant 100px) pour porter tout le contenu qui vivait a cote avant (2026-09-24) — meme esprit
   que .need-pot-card, juste beaucoup plus grand puisqu'il n'y en a que 2 par ligne au lieu
   d'une grille. position:relative sur "Obtenue" : ancre le ΔE (bas-droite) et le bouton ⓘ
   (centre), tous deux en position absolue. */
.need-detail-swatch {
  flex: 1 1 0; min-width: 0; height: 220px; border-radius: 10px;
  display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-end;
  padding: 18px; gap: 3px;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,0.35);
}
.need-detail-swatch-result { position: relative; }
/* Agrandi (13px -> 16px) et police Inter de la page marketing (pas la mono heritee du corps
   de page) — demande explicite, 2026-09-24, pour plus de lisibilite au-dessus du hex. */
.need-detail-tag {
  font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif;
  font-size: 16px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; opacity: 0.9;
}
.need-detail-hex { font-family: 'JetBrains Mono', monospace; font-size: 13px; font-weight: 600; opacity: 0.85; }
.need-detail-arrow { color: var(--muted); flex-shrink: 0; font-size: 22px; }
/* Nom(s) de peinture(s) seuls (pas les %, reserves au panneau ⓘ derriere le bandeau) — demande
   explicite de garder ce bandeau simple, l'info complete restant a un clic. Tronque avec "..."
   plutot que de deborder : un melange a 3 peintures + le badge "deja en stock" peut depasser la
   largeur du bandeau sur une fenetre etroite, le detail complet reste de toute facon disponible
   au clic sur ⓘ. */
.need-detail-paints {
  font-size: 12px; font-weight: 600; opacity: 0.85;
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* color:inherit — regle du design system (.match-quality sur un fond sature arbitraire,
   jamais sa couleur semantique, illisible sur certaines teintes). Cale en HAUT a droite (pas
   en bas) : teste en bas-droite d'abord, ca entrait en collision avec le(s) nom(s) de
   peinture(s) des qu'un melange a 3 peintures + badge "deja en stock" depassait en longueur
   (verifie : les 2 textes se chevauchaient). En haut, jamais sur le meme "etage" que le
   nom+hex+peintures ancres en bas — aucune collision possible quelle que soit la longueur du
   texte, quel que soit le nombre de peintures du melange. */
.need-detail-quality { position: absolute; right: 18px; top: 18px; color: inherit; }
/* Le color:inherit ci-dessus se fait battre par .match-quality.excellent/.good/.loose et
   .owned-pick-tag.shared-pick-tag (2 classes chacune, meme specificite que .need-detail-quality
   seule mais definies plus tot dans la feuille de style — la cascade tranche par ordre
   d'apparition a specificite egale). Ces 2 selecteurs-ci egalisent la specificite ET
   apparaissent apres : ils l'emportent enfin. Demande explicite 2026-09-24 : "correspondance"
   et "peinture mutualisee" doivent suivre la couleur de texte de la case (noir/blanc selon la
   teinte), pas leur couleur semantique habituelle — illisible sur certains fonds satures. */
.need-detail-swatch .match-quality,
.need-detail-swatch .owned-pick-tag { color: inherit; }

/* Mobile uniquement (signale explicitement, 2026-09-24) : la case "Obtenue" retombe a ~140px
   de large (2 bandeaux fluides sur une colonne ~327px) — largement sous les 262px que prend
   ".need-detail-quality" sur une seule ligne (white-space:nowrap herite de .match-quality),
   qui debordait alors bien au-dela du bandeau, jusque sur la case "Ciblee" voisine. left+right
   (au lieu d'une largeur au contenu) bornent le texte a l'interieur du bandeau et l'autorisent
   a retourner a la ligne — display:block necessaire pour qu'un span en position:absolute
   respecte left ET right (un inline ne s'etire pas entre les deux). Meme raisonnement deja
   applique a .mix-card-quality-big (Paint Mix) pour ce meme texte, la variante la plus longue.
   Noms de peinture retires sur ce meme bandeau (toujours tronques meme en ellipsis, illisibles
   a cette largeur) — le detail complet reste a un clic sur la goutte, comme aujourd'hui. */
@media (max-width: 640px) {
  .need-detail-quality {
    display: block; left: 18px; right: 18px; top: 14px;
    white-space: normal; text-align: right; font-size: 10.5px; line-height: 1.3;
  }
  .need-detail-paints { display: none; }
}

.lab-actions { display: flex; gap: 16px; align-items: center; margin-top: 10px; flex-wrap: wrap; }
.lab-actions[hidden] { display: none; }

/* ============================================================
   ONGLET ACCUEIL (page marketing) — voir #tab-home dans palette_lab.html.
   Meme bandeau que tous les autres onglets (aucune regle dediee ici pour header/.tab-bar/
   .brand/.btn-connect : c'est le point de tout ce refactor, ne jamais les redupliquer).
   Classes prefixees "home-" pour rester a l'ecart des ".hero-*" deja utilisees par l'onglet
   Create (composant different, memes racines de nom sinon).
   ============================================================ */

.home-hero {
  /* Bas remonte de 56px a 96px (2026-09-21, demande explicite "un peu d'espacement" entre le
     hero et .home-pitch juste en dessous) : +40px (var(--s4)) en plus du gap standard
     (var(--s6), 104px) deja herite de #tab-home. */
  padding: 72px var(--s3) 96px;
  display: flex; flex-direction: column; align-items: center; text-align: center; gap: var(--s2);
}
.home-hero-eyebrow {
  font-family: 'Archivo Black', 'Inter', sans-serif; font-size: 14px;
  letter-spacing: 0.14em; color: var(--faint); margin: 0;
}
.home-hero-title {
  font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif; font-weight: 500;
  font-size: clamp(40px, 8vw, 96px); line-height: 1.05; letter-spacing: -0.02em;
  text-wrap: balance; margin: 0;
}
.home-hero-subtitle {
  font-family: 'JetBrains Mono', ui-monospace, monospace; font-size: clamp(14px, 1.6vw, 18px);
  color: var(--muted); letter-spacing: 0.01em; margin: 0;
}
.home-hero-cta {
  margin-top: var(--s2);
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--header-ink); color: #f7fafb; border: none; border-radius: 999px;
  height: 47px; padding: 0 30px; cursor: pointer;
  font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif; font-size: 12px; font-weight: 600;
  letter-spacing: 0.05em; text-transform: uppercase;
  transition: background 0.15s ease, transform 0.15s ease;
}
.home-hero-cta:hover { background: var(--header-ink-hover); transform: translateY(-1px); }

.home-scroll-hint {
  color: var(--faint); width: 20px; height: 20px; margin-top: var(--s2);
  animation: home-bob 2.2s ease-in-out infinite;
}
.home-scroll-hint svg { width: 100%; height: 100%; }
@keyframes home-bob {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(6px); }
}
@media (prefers-reduced-motion: reduce) {
  .home-scroll-hint { animation: none; }
}

/* ---- Bandeau dedie a Create : meme registre que le bandeau juste en dessous (titre + UN
   bandeau, pas 3 cartes). ---- */
.home-create-banner {
  max-width: 1320px; margin: 0 auto; width: 100%;
  /* padding-bottom: var(--s5) (24+40, meme total que .home-pitch un peu plus haut) en plus du
     gap standard de #tab-home (var(--s6)) : ce bandeau est desormais suivi de .home-pricing
     (2026-09-17), dont le titre n'a lui-meme aucune marge/padding au-dessus — sans ce
     padding-bottom, l'ecart tombait a 104px (juste le gap) au lieu des 168px deja etablis
     ailleurs sur cette page (.home-pitch -> .home-need-banner), signale explicitement comme
     "trop resserre par rapport aux autres sections". Avant l'ajout de .home-pricing, ce
     bandeau precedait directement .home-closing (qui a son propre habillage de separation,
     bordure + padding-top) et n'avait donc pas besoin de ce padding — plus vrai maintenant
     qu'un bandeau ordinaire s'intercale entre les deux. */
  padding: 0 var(--s3) var(--s5);
  display: flex; flex-direction: column; align-items: center; text-align: center;
}
/* Taille calquee sur la reference cosmos.so : un titre nettement plus dominant que le reste
   de la page (voir capture transmise), pas juste "un peu plus gros que le corps de texte". */
.home-create-banner-title {
  font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif; font-weight: 500;
  font-size: clamp(30px, 4.8vw, 56px); line-height: 1.1; letter-spacing: -0.015em;
  margin: 0 0 var(--s4); text-wrap: balance;
}
/* Tres large et court, comme la reference cosmos.so (ratio 10:3, un peu moins extreme que
   3:1 — largeur et taille des cases inchangees, seule la hauteur baisse legerement pour
   reduire le vide haut/bas autour de la mini-demo, laquelle reste plafonnee a 220px
   (.home-demo-stage) donc ne grandit pas avec ce changement. */
.home-create-banner-visual {
  width: 100%; aspect-ratio: 10 / 3; border-radius: 16px;
  background: var(--panel); border: 1px solid var(--border);
  display: flex; align-items: center; justify-content: center;
  overflow: hidden;
}
/* Pas une vraie capture d'ecran (pas encore prise) : une mini-demo CSS en boucle, sans
   fichier video. Le libelle de regle ET la rangee de cases changent ensemble, 5 etats sur
   une boucle de 6s (~1.2s/etat) — chaque etat montre le vrai nombre de couleurs de cette
   regle dans Create (voir RULES/ROLE_INFO dans palette_lab.js), pas toujours 5 cases
   recolorees comme avant. */
.home-demo {
  width: 100%; height: 100%; box-sizing: border-box;
  padding: var(--s3) var(--s4);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--s2);
}
.home-demo-mode {
  position: relative; width: 100%; height: 16px;
  font-family: 'JetBrains Mono', monospace; font-size: 12px; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--faint);
}
/* 12s (avant 6s, 12e passe : "ralenti les changements") : chaque etat tient desormais ~2.4s
   au lieu de ~1.2s. home-mode-fade reste inchangee (ses % sont relatifs a la duree de chaque
   element, pas une valeur absolue), seuls la duree et les delais (multiplies par 2) changent
   ici comme sur .home-demo-state juste en dessous — les deux doivent rester synchronises. */
.home-demo-mode span {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  opacity: 0; animation: home-mode-fade 12s ease infinite;
}
.home-demo-mode span:nth-child(1) { animation-delay: 0s; }
.home-demo-mode span:nth-child(2) { animation-delay: 2.4s; }
.home-demo-mode span:nth-child(3) { animation-delay: 4.8s; }
.home-demo-mode span:nth-child(4) { animation-delay: 7.2s; }
.home-demo-mode span:nth-child(5) { animation-delay: 9.6s; }

/* Remplace l'ancienne .home-demo-row (5 cases fixes, juste recolorees) : chaque etat est
   maintenant son propre bloc empile (comme .home-needdemo-compare-state), avec son propre
   nombre de cases. home-mode-fade est reutilisee telle quelle pour ces etats (meme forme
   d'opacite, meme duree que .home-demo-mode span juste au-dessus, synchronisees) — le calage
   en % est relatif au cycle de CHAQUE element, donc changer la duree/les delais ici suffit,
   pas besoin de toucher au keyframe. */
.home-demo-stage { position: relative; width: 100%; flex: 1; max-height: 220px; }
.home-demo-state {
  position: absolute; inset: 0; display: flex; gap: var(--s1);
  opacity: 0; animation: home-mode-fade 12s ease infinite;
}
.home-demo-state-1 { animation-delay: 0s; }
.home-demo-state-2 { animation-delay: 2.4s; }
.home-demo-state-3 { animation-delay: 4.8s; }
.home-demo-state-4 { animation-delay: 7.2s; }
.home-demo-state-5 { animation-delay: 9.6s; }

@keyframes home-mode-fade {
  0%, 3% { opacity: 0; }
  6%, 19% { opacity: 1; }
  22%, 100% { opacity: 0; }
}

.home-demo-swatch {
  height: 100%; border-radius: 10px;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,0.35);
}
/* Largeurs relatives = les vraies proportions de role de Create (ROLE_INFO /
   MONOCHROME_ROLE_INFO, palette_lab.js), pas une repartition egale : flex-grow (flex-basis a
   0) donne un ratio identique a un pourcentage, et la somme fait toujours 100 par etat, donc
   la rangee occupe toujours 100% du bandeau quel que soit le nombre de cases actives (2 a 5).
   2 couleurs (Complementary) : 70/30. 3 couleurs (Analogous, Triadic) : 60/30/10. 4 couleurs
   (Tetradic) : 50/25/15/10. 5 couleurs, monochromatique : 15/20/30/20/15 (pic au centre,
   "Principale" — c'est litteralement MONOCHROME_ROLE_INFO). */
.hdw-10 { flex: 10 0 0%; }
.hdw-15 { flex: 15 0 0%; }
.hdw-20 { flex: 20 0 0%; }
.hdw-25 { flex: 25 0 0%; }
.hdw-30 { flex: 30 0 0%; }
.hdw-50 { flex: 50 0 0%; }
.hdw-60 { flex: 60 0 0%; }
.hdw-70 { flex: 70 0 0%; }

@media (prefers-reduced-motion: reduce) {
  .home-demo-mode span, .home-demo-state { animation: none; opacity: 0; }
  .home-demo-mode span:first-child, .home-demo-state-1 { opacity: 1; }
}
/* :empty { display:none } reste en place au cas ou elle serait revidee un jour. Meme police,
   taille et espacement avec le bandeau que .home-need-banner-desc / .home-pitch-sub (clamp
   17-21px, ecart var(--s4)) — demande explicite de coherence entre les 3 bandeaux. */
.home-create-banner-desc {
  max-width: 74ch;
  font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif; font-weight: 400;
  font-size: clamp(17px, 2.2vw, 21px); line-height: 1.5; color: var(--muted);
  margin: var(--s4) 0 0; text-wrap: balance;
}
.home-create-banner-desc:empty { display: none; }

/* ---- Page tarifs publique (feuille de route, phase 2) : section de l'onglet marketing, pas
   une page a part ni reservee a "Mon compte" (voir #pricing, palette_lab.html). Meme largeur et
   meme registre de titre que les bandeaux Create/Paint Mix ci-dessus. ---- */
.home-pricing {
  max-width: 1320px; margin: 0 auto; width: 100%;
  padding: 0 var(--s3);
  display: flex; flex-direction: column; align-items: center; text-align: center;
}
.home-pricing-title {
  font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif; font-weight: 500;
  font-size: clamp(30px, 4.8vw, 56px); line-height: 1.1; letter-spacing: -0.015em;
  margin: 0; text-wrap: balance;
}
/* Meme police/taille que les sous-textes des autres bandeaux (.home-create-banner-desc etc.),
   mais placee ICI directement sous le titre (comme .home-hero-subtitle) plutot qu'apres le
   visuel : les cartes ci-dessous portent deja leur propre detail (features), un texte en plus
   en dessous ferait doublon. */
.home-pricing-subtitle {
  max-width: 60ch;
  font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif; font-weight: 400;
  font-size: clamp(17px, 2.2vw, 21px); line-height: 1.5; color: var(--muted);
  margin: var(--s4) 0 0; text-wrap: balance;
}
/* Pastille Monthly/Yearly au-dessus des 2 cartes (2026-09-17, demande explicite) — meme recette
   que .zone-mode-switch (Auto/Manuel, onglet Create) : verre depoli blanc, indicateur sombre
   glissant positionne en JS (positionPricingToggleIndicator). Dupliquee plutot que partagee,
   comme .zone-mode-switch/.picker-switch le sont deja entre elles (convention du fichier). */
.pricing-toggle {
  position: relative; display: flex; width: fit-content; gap: 2px; padding: 4px;
  margin-top: var(--s4); border-radius: 999px;
  background: rgba(255,255,255,0.55);
  backdrop-filter: blur(16px) saturate(180%);
  -webkit-backdrop-filter: blur(16px) saturate(180%);
  border: 1px solid rgba(255,255,255,0.7);
  box-shadow: 0 1px 2px rgba(14,17,22,0.05), 0 10px 28px rgba(14,17,22,0.08);
}
.pricing-toggle-indicator {
  position: absolute; top: 0; left: 0; z-index: 0;
  border-radius: 999px; background: var(--text);
  transition: transform 0.3s cubic-bezier(.22,1,.36,1), width 0.3s cubic-bezier(.22,1,.36,1);
}
.pricing-toggle-btn {
  position: relative; z-index: 1; flex: none;
  background: none; border: none; border-radius: 999px;
  padding: 9px 20px; cursor: pointer; font-size: 12px; font-weight: 600;
  font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif;
  letter-spacing: 0.03em; color: var(--muted); white-space: nowrap;
  transition: color 0.2s ease;
}
.pricing-toggle-btn:hover { color: var(--text); }
.pricing-toggle-btn.active, .pricing-toggle-btn.active:hover { color: #f7fafb; }

.home-pricing-grid {
  width: 100%; max-width: 760px; margin-top: var(--s3);
  display: grid; grid-template-columns: 1fr 1fr; gap: var(--s2);
}
.home-pricing-card {
  border-radius: 16px; background: var(--panel); border: 1px solid var(--border);
  padding: var(--s3); text-align: left;
  display: flex; flex-direction: column; gap: var(--s2);
}
/* Seule carte a vraiment se remarquer (le --accent, signal ponctuel du design system, sert ici
   a designer l'offre mise en avant) — la carte Free reste dans le registre neutre habituel. */
.home-pricing-card-pro { background: #fff; border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.home-pricing-card-head { display: flex; flex-direction: column; gap: 4px; }
.home-pricing-card-name {
  font-family: 'JetBrains Mono', monospace; font-size: 12px; font-weight: 600;
  letter-spacing: 0.06em; text-transform: uppercase; color: var(--faint);
}
.home-pricing-card-price { display: flex; align-items: baseline; gap: 8px; }
/* Ancien prix (Monthly) barre a cote de l'equivalent mensuel de l'offre annuelle — visible
   uniquement en Yearly (voir setPricingInterval, palette_lab.js). */
.home-pricing-card-amount-old { font-size: 20px; font-weight: 500; color: var(--faint); text-decoration: line-through; }
.home-pricing-card-amount-old[hidden] { display: none; }
.home-pricing-card-amount { font-size: 34px; font-weight: 600; letter-spacing: -0.01em; }
.home-pricing-card-period { font-size: 13px; color: var(--muted); }
.home-pricing-card-alt { font-size: 12.5px; color: var(--muted); margin: 0; }
.home-pricing-card-save { color: var(--good-text); font-weight: 600; }
.home-pricing-card-features { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; flex: 1; }
.home-pricing-card-features li {
  font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif;
  font-size: 14.5px; line-height: 1.5; color: var(--text); padding-left: 18px; position: relative;
}
/* Rond de couleur plutot qu'une coche : vert (--good-text) = illimite, orange (--warn-text) =
   limite — les 2 tokens semantiques deja utilises ailleurs (match-quality...), pas de nouvelle
   couleur inventee (2026-09-17, demande explicite « pas des petites coches... des petits ronds
   de couleurs »). Chaque <li> porte la classe correspondante (tier-unlimited/tier-limited). */
.home-pricing-card-features li::before {
  content: ''; position: absolute; left: 0; top: 50%; transform: translateY(-50%);
  width: 7px; height: 7px; border-radius: 50%;
}
.home-pricing-card-features li.tier-unlimited::before { background: var(--good-text); }
.home-pricing-card-features li.tier-limited::before { background: var(--warn-text); }
/* Etoile jaune : uniquement Paint Mix/Need en Pro (demande explicite) — les 2 fonctions
   verrouillees par quota en Free, mises en avant comme le vrai debloque de l'abonnement. Jaune
   "etoile" dedie (pas un token du design system : --accent/--good-text/--warn-text portent tous
   un sens deja pris ailleurs, une 4e teinte ponctuelle purement decorative ne les recouvre pas). */
.home-pricing-card-features li.tier-star::after {
  content: '★'; margin-left: 6px; color: #f2b705; font-size: 16px; vertical-align: -1px;
}
.home-pricing-card-cta { width: 100%; padding: 11px; font-size: 13px; }
.home-pricing-card-cta-group { display: flex; flex-direction: column; gap: 8px; }
.home-pricing-card-cta-group[hidden] { display: none; }
.home-pricing-card-cta-group .btn-primary,
.home-pricing-card-cta-group .btn-secondary { width: 100%; padding: 11px; font-size: 13px; }

@media (max-width: 640px) {
  .home-pricing-grid { grid-template-columns: 1fr; max-width: 380px; }
}

/* ---- Bandeau concret (reference cosmos.so) : titre, 3 cartes-outils cliquables, sous-texte.
   Les 3 visuels sont des degrades extraits de vraies teintes du catalogue (pas des gris
   neutres), en attente de vraies captures d'ecran de l'outil (etiquette "screenshot coming"). ---- */
.home-pitch {
  /* Meme largeur que .home-create-banner (1320px, pas 1080) : ce bandeau doit pouvoir
     s'etendre aussi large que celui de Create. */
  max-width: 1320px; margin: 0 auto; width: 100%;
  /* padding-bottom:var(--s3) + .home-showcase margin-bottom:var(--s4) juste en dessous = 64px
     de vide en plus du gap standard, pour un total 168px avant .home-accuracy-banner — remis en
     place (2026-09-21, demande explicite "meme espacement que les autres sections entre
     elles") : .home-accuracy-banner a la meme recette pour l'ecart qui le suit (avant Need), ce
     bandeau-ci ne doit pas etre le seul a rester au gap standard (104px) entre les deux. */
  padding: 0 var(--s3) var(--s3);
  display: flex; flex-direction: column; align-items: center; text-align: center;
}
/* Meme taille et meme ecart en dessous que .home-create-banner-title, pour rester au meme
   registre visuel d'un bandeau a l'autre. */
.home-pitch-title {
  font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif; font-weight: 500;
  font-size: clamp(30px, 4.8vw, 56px); line-height: 1.1; letter-spacing: -0.015em;
  margin: 0 0 var(--s4); text-wrap: balance;
}
/* Remplace l'ancien bandeau a 3 cartes egales (Create/Paint Mix/Need) : Create a maintenant
   son propre bandeau au-dessus, il ne reste que Paint Mix + Need a montrer ici, sous forme
   d'un cube (carre) a gauche et d'un rectangle a droite plutot que 3 vignettes identiques.
   Grille, pas flex : cube et rectangle sur la MEME ligne de grille (le rectangle s'etire —
   align-items:stretch, par defaut en grid aussi — exactement a la hauteur du cube, ligne
   auto-dimensionnee sur son aspect-ratio:1), la legende du cube sur la ligne suivante, dans
   sa seule colonne — le rectangle n'en herite pas la hauteur. */
.home-showcase {
  width: 100%; display: grid; grid-template-columns: 38% 1fr;
  /* row-gap separe de column-gap (au lieu d'un gap unique) : le texte sous le rectangle doit
     avoir le meme ecart avec son bandeau que .home-need-banner-desc (var(--s4)), demande
     explicite — laisse le column-gap entre cube et rectangle inchange (var(--s2)). Applique
     aussi a .home-showcase-caption (meme ligne de grille) pour garder les 2 legendes
     symetriques, comme voulu ailleurs dans ce fichier. */
  row-gap: var(--s4); column-gap: var(--s2); margin-bottom: var(--s4);
}
.home-showcase-cube { grid-column: 1; grid-row: 1; aspect-ratio: 1; }
.home-showcase-rect { grid-column: 2; grid-row: 1; }
.home-showcase-cube,
.home-showcase-rect {
  border-radius: 16px; background: var(--panel); border: 1px solid var(--border);
  display: flex; align-items: center; justify-content: center; overflow: hidden;
}
.home-showcase-note {
  font-family: 'JetBrains Mono', monospace; font-size: 11px; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--faint);
}
/* Meme police que le texte sous le bandeau Create (.home-create-banner-desc) : Inter, pas
   la mono du corps de page. */
/* Meme taille/interligne que les autres sous-textes de la page (.home-need-banner-desc,
   .home-pitch-sub, .home-create-banner-desc) : clamp 17-21px, 1.5 — demande explicite de
   coherence. */
.home-showcase-caption {
  grid-column: 1; grid-row: 2;
  font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif; font-weight: 400;
  font-size: clamp(17px, 2.2vw, 21px); line-height: 1.5; color: var(--muted); text-align: center;
  margin: 0;
}

/* ---- Mini-demo (pas un screenshot) du glissement de marque, dans le cube : meme pilule que
   #catalog-switch-btn (memes icones/proportions, juste agrandie), le nom qui glisse d'une
   marque a l'autre plutot qu'un simple fondu — echo direct du vrai selecteur du bandeau. ---- */
.home-brand-pill {
  display: flex; align-items: center; gap: 10px;
  padding: 14px 22px; border-radius: 999px; border: 1px solid var(--border);
  font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif; font-size: 15px; font-weight: 600;
  color: var(--text); background: #fff;
}
.home-brand-pill-icon { width: 18px; height: 18px; color: var(--muted); flex-shrink: 0; }
.home-brand-pill-chevron { width: 15px; height: 15px; color: var(--faint); flex-shrink: 0; }
.home-brand-pill-track {
  position: relative; height: 1.2em; min-width: 132px; overflow: hidden;
}
.home-brand-pill-track span {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: flex-start;
  white-space: nowrap; opacity: 0; transform: translateY(8px);
  animation: home-brand-slide 6s ease infinite;
}
.home-brand-pill-track span:nth-child(1) { animation-delay: 0s; }
.home-brand-pill-track span:nth-child(2) { animation-delay: 1.2s; }
.home-brand-pill-track span:nth-child(3) { animation-delay: 2.4s; }
.home-brand-pill-track span:nth-child(4) { animation-delay: 3.6s; }
.home-brand-pill-track span:nth-child(5) { animation-delay: 4.8s; }
@keyframes home-brand-slide {
  0% { opacity: 0; transform: translateY(8px); }
  3%, 17% { opacity: 1; transform: translateY(0); }
  20%, 100% { opacity: 0; transform: translateY(-8px); }
}
@media (prefers-reduced-motion: reduce) {
  .home-brand-pill-track span { animation: none; opacity: 0; transform: none; }
  .home-brand-pill-track span:first-child { opacity: 1; }
}

/* ---- Mini-demo (pas un screenshot) d'un Paint Mix, dans le rectangle — calquee sur l'onglet
   reel : couleur ciblee en bandeau plein largeur en haut, meme traitement que .hero-block
   (fond couleur, libelle + hex ancres en bas a gauche en blanc) ; en dessous, un carrousel
   qui glisse tout seul vers la droite (echo de .mix-carousel) entre 3 propositions reelles
   Angelus (2 ou 3 peintures selon le cas), detail des peintures (case + nom + dosage) bien
   visible sous chaque proposition. ---- */
.home-mixdemo { width: 100%; height: 100%; display: flex; flex-direction: column; }
.home-mixdemo-target {
  flex: 0 0 46%; width: 100%; background: #C1502E;
  display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-end;
  gap: 2px; padding: var(--s2);
}
.home-mixdemo-target-role {
  font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif; font-size: 11px; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.1em; color: #fff; opacity: 0.85;
}
.home-mixdemo-target-hex {
  font-family: 'JetBrains Mono', monospace; font-size: 22px; font-weight: 700; color: #fff;
}
.home-mixdemo-carousel { position: relative; flex: 1; overflow: hidden; }
/* opacity:0 de base (manquait ici, present partout ailleurs sur les mini-demos qui bouclent —
   .home-needdemo-compare-state, .home-demo-state, .home-brand-pill-track span) : tant que le
   animation-delay d'une carte (2s/4s pour les cartes 2/3) n'a pas demarre, sans cette valeur
   de repli la carte s'affiche a l'opacite par defaut (1, pleinement visible) au lieu d'etre
   masquee — cause reelle du bug signale ("les couleurs se superposent... 4/5 premieres
   secondes", verifie via getBoundingClientRect/getComputedStyle : cartes 2 et 3 bloquees a
   opacity:1 jusqu'a l'ecoulement de leur delai). */
.home-mixdemo-card {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  gap: var(--s2); padding: 0 var(--s2);
  opacity: 0; animation: home-mixdemo-slide 6s ease infinite;
}
.home-mixdemo-card-1 { animation-delay: 0s; }
.home-mixdemo-card-2 { animation-delay: 2s; }
.home-mixdemo-card-3 { animation-delay: 4s; }
@keyframes home-mixdemo-slide {
  0% { transform: translateX(48px); opacity: 0; }
  8%, 25% { transform: translateX(0); opacity: 1; }
  33%, 100% { transform: translateX(-48px); opacity: 0; }
}
.home-mixdemo-paint { display: flex; align-items: center; gap: 10px; }
.home-mixdemo-paint-swatch {
  width: 38px; height: 38px; border-radius: 9px; flex-shrink: 0;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,0.35);
}
.home-mixdemo-paint-info { display: flex; flex-direction: column; gap: 2px; text-align: left; }
.home-mixdemo-paint-name {
  font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif; font-size: 12px; font-weight: 600;
  color: var(--text); white-space: nowrap;
}
.home-mixdemo-paint-pct { font-family: 'JetBrains Mono', monospace; font-size: 11px; color: var(--muted); }
.home-mixdemo-plus { font-size: 16px; color: var(--faint); flex-shrink: 0; }
@media (prefers-reduced-motion: reduce) {
  .home-mixdemo-card { animation: none; opacity: 0; transform: none; }
  .home-mixdemo-card-1 { opacity: 1; }
}

/* Sous le rectangle (pas centre sous tout le bandeau comme avant) : meme colonne que
   .home-showcase-rect, meme traitement typo que .home-showcase-caption (Inter, pas la mono
   du corps de page) pour rester coherent avec la legende du cube a cote. text-wrap:balance
   pour un retour a la ligne propre si le texte finit sur 2 lignes. */
/* Meme police, meme taille et meme interligne que .home-need-banner-desc (clamp 17-21px,
   line-height 1.5) — demande explicite de coherence avec le texte du bandeau Need. */
.home-pitch-sub {
  grid-column: 2; grid-row: 2;
  font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif; font-weight: 400;
  font-size: clamp(17px, 2.2vw, 21px); line-height: 1.5; color: var(--muted); text-align: center;
  margin: 0; text-wrap: balance;
}

/* ---- Bandeau "rigueur scientifique" (2026-09-21) : meme gabarit titre/visuel/texte que les
   autres bandeaux (Create/Need), son propre jeu de regles (convention du fichier : jamais
   partagees meme identiques).
   2e version, meme jour ("trop petit par rapport a la taille du bandeau... beaucoup plus sexy
   visuellement") : la 1ere version (2 petites cases + fleche, centrees dans un grand cadre gris
   avec plein de vide autour) est remplacee par un diptyque PLEIN CADRE — .home-accuracydemo-
   split remplit tout .home-accuracy-banner-visual (plus de centrage/padding sur le visuel lui-
   meme, voir plus bas), chaque .home-accuracydemo-panel est une bande de couleur pleine hauteur
   qui prend 50% de la largeur. La couleur EST desormais le visuel entier, pas un detail dedans.
   Texte ancre en bas a gauche de chaque panneau, meme traitement que .home-mixdemo-target
   (bandeau couleur ciblee de Paint Mix) : petit libelle majuscule, gros hex mono en dessous. ---- */
.home-accuracy-banner {
  max-width: 1320px; margin: 0 auto; width: 100%;
  /* padding-bottom:var(--s5) (64px, = l'ancien var(--s3)+var(--s4) de .home-pitch/.home-showcase,
     voir leurs commentaires) : conserve l'ecart plus genereux juste avant .home-need-banner,
     comme avant l'insertion de ce bandeau entre les deux — meme recette deja utilisee par
     .home-create-banner pour la meme raison, juste avant .home-pricing. */
  padding: 0 var(--s3) var(--s5);
  display: flex; flex-direction: column; align-items: center; text-align: center;
}
.home-accuracy-banner-title {
  font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif; font-weight: 500;
  font-size: clamp(30px, 4.8vw, 56px); line-height: 1.1; letter-spacing: -0.015em;
  margin: 0 0 var(--s2); text-wrap: balance;
}
/* Meme registre que .home-showcase-note (petite legende mono majuscule, tres discrete) : nomme
   les 2 vraies peintures utilisees dans le visuel juste en dessous, une seule fois pour les 2
   panneaux (ils partent de la meme recette). */
/* Chips de couleur reelles (pas juste du texte) ajoutees devant le libelle (2026-09-21,
   demande explicite "on ne voit pas quelles couleurs on melange") : meme couleurs exactes que
   les 2 pots utilises dans le calcul (#C61E25, #FFFFFF) — reste minimaliste, une seule petite
   rangee, pas de redesign du diptyque en dessous. */
.home-accuracy-banner-recipe {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  margin: 0 0 var(--s3);
}
.home-accuracy-banner-recipe-chip {
  width: 20px; height: 20px; border-radius: 6px;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,0.35);
}
.home-accuracy-banner-recipe-chip-white { box-shadow: inset 0 0 0 1px var(--border); }
.home-accuracy-banner-recipe-sep {
  font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif; font-size: 14px; font-weight: 600;
  color: var(--faint);
}
.home-accuracy-banner-recipe-text {
  font-family: 'JetBrains Mono', monospace; font-size: 12px; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--faint);
}
.home-accuracy-banner-visual {
  width: 100%; aspect-ratio: 10 / 3; border-radius: 16px;
  border: 1px solid var(--border); overflow: hidden;
}
.home-accuracy-banner-desc {
  max-width: 84ch;
  font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif; font-weight: 400;
  font-size: clamp(17px, 2.2vw, 21px); line-height: 1.5; color: var(--muted);
  margin: var(--s4) 0 0; text-wrap: balance;
}

.home-accuracydemo-split { display: flex; width: 100%; height: 100%; gap: 3px; }
.home-accuracydemo-panel {
  flex: 1; display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-end;
  gap: 6px; padding: var(--s4);
}
.home-accuracydemo-panel-label {
  display: flex; align-items: center; gap: 6px;
  font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif; font-size: 14px; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.08em; opacity: 0.85;
}
.home-accuracydemo-panel-check { flex: 0 0 auto; width: 16px; height: 16px; opacity: 1; }
.home-accuracydemo-panel-hex {
  font-family: 'JetBrains Mono', monospace; font-size: clamp(26px, 3.2vw, 38px); font-weight: 700;
}

@media (max-width: 760px) {
  .home-accuracy-banner-visual { aspect-ratio: auto; height: auto; }
  .home-accuracydemo-split { flex-direction: column; gap: 3px; }
  .home-accuracydemo-panel { padding: var(--s3); min-height: 140px; }
}

/* ---- Bandeau Need : meme configuration que le bandeau Create (titre + UN bandeau large +
   texte), copie/collee de ses regles pour rester au meme registre visuel. Visuel et texte
   volontairement vides pour l'instant (bordure en pointilles + note "banner coming", meme
   convention que le bandeau Create avant d'etre rempli). ---- */
/* padding-bottom: var(--s5) (pas juste var(--s3)) en plus du gap standard de #tab-home
   (var(--s6)) : .home-pitch cumule son propre padding-bottom:var(--s3) ET le margin-bottom:
   var(--s4) de son .home-showcase interne (168px de vide visuel jusqu'au titre Need, mesure
   via getBoundingClientRect), alors que .home-need-banner n'avait ni l'un ni l'autre (128px
   seulement jusqu'au titre Create) — d'ou l'ecart visuel plus resserre signale explicitement.
   var(--s3)+var(--s4) = var(--s5) (24+40=64) : meme total de vide sans avoir a rajouter un
   wrapper equivalent a .home-showcase ici. */
.home-need-banner {
  max-width: 1320px; margin: 0 auto; width: 100%;
  padding: 0 var(--s3) var(--s5);
  display: flex; flex-direction: column; align-items: center; text-align: center;
}
.home-need-banner-title {
  font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif; font-weight: 500;
  font-size: clamp(30px, 4.8vw, 56px); line-height: 1.1; letter-spacing: -0.015em;
  margin: 0 0 var(--s4); text-wrap: balance;
}
/* Rallonge (7/2 -> 9/4) : les target colors etaient trop entassees a 7/2, il leur faut plus
   de hauteur pour respirer (voir .home-needdemo-palette-row span, remontee a 54px). */
.home-need-banner-visual {
  width: 100%; aspect-ratio: 9 / 4; border-radius: 16px;
  background: var(--panel); border: 1px solid var(--border);
  display: flex; align-items: center; justify-content: center;
  overflow: hidden;
}

/* ---- Mini-demo (pas un screenshot) du probleme que resout Need — 4e passe, 2026-09-16 :
   mise en page agrandie (le bandeau lui-meme passe a aspect-ratio 5/2, plus genereux que
   les 10/3 des 2 bandeaux au-dessus, pour laisser respirer 5 palettes + un cote droit plus
   grand), le trait de separation remplace par une fleche (meme langage que les icones
   d'onglet), et les libelles d'etat en vrai titre plutot qu'en petite legende mono.
   Gauche : "Your target colors" (ancre en haut) + meme langage que "My palettes" (barre
   pleine largeur de cases collees, .hero-palette-bar/.hero-block simplifie) — 5 palettes,
   melange de teintes sourdes (reprises du bandeau Create) et vives (nouvelles).
   Droite : 2 etats qui defilent (boucle 6s, 3s chacun) — "Without our engine" (19 pots, un
   par couleur cible, en vrac) vs "With our engine" (6 pots reels NOMMES, chacun avec son
   compteur d'usage ×N). ---- */
.home-needdemo { width: 100%; height: 100%; display: flex; align-items: center; background: var(--panel); }
.home-needdemo-col {
  flex: 1 1 50%; height: 100%; display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: var(--s2); padding: var(--s4);
}
/* Pas de justify-content:flex-start ici (heritee comme les 2 colonnes de
   .home-needdemo-col : centre) — sinon le bloc entier (libelle + palettes) reste colle en
   haut et laisse tout le vide en bas, desequilibre par rapport a la colonne de droite,
   elle bien centree. Le libelle garde son ancrage a gauche horizontalement
   (.home-needdemo-heading { align-self:flex-start }), independant de ce centrage vertical. */
/* Plus lisible que la 1ere version (var(--faint), 13px, trop pale) : plus fonce, plus grand.
   height/line-height 28px = meme boite que .home-needdemo-heading-cycle (voir plus bas) : les
   deux libelles doivent occuper la meme hauteur pour que tout ce qui suit (palettes a gauche,
   grille de pots a droite) demarre exactement au meme niveau — condition necessaire pour que
   les rangees s'alignent visuellement d'une colonne a l'autre (10e passe, demande explicite
   "aligne les target colors avec les couleurs with our engine, parfaitement aligne").
   align-self:center (pas flex-start) — demande explicite (2026-09-23) de centrer ce libelle
   au-dessus des couleurs, desktop ET mobile, comme "Without/With our engine" en face (deja
   centre via justify-content sur .home-needdemo-heading-cycle span). Largeur auto (shrink-to-
   fit du texte) + centrage du parent (.home-needdemo-col : align-items:center) suffit, aucune
   largeur explicite necessaire. */
.home-needdemo-heading {
  align-self: center;
  height: 28px; line-height: 28px;
  font-family: 'JetBrains Mono', monospace; font-size: 14px; font-weight: 600; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--muted);
}
/* Fleche esthetique entre les deux colonnes : remplace le trait de separation, meme langage
   que les icones monoline du reste du site (voir .home-hero-scroll-hint, catalog-switch...). */
.home-needdemo-arrow {
  flex: 0 0 auto; width: 34px; height: 34px; color: var(--border-strong);
  display: flex; align-items: center; justify-content: center;
  /* centree sur toute la hauteur de .home-needdemo par defaut, mais les rangees (le vrai
     contenu a relier) sont decalees vers le bas du libelle (28px) + son ecart (16px) : on
     redescend la fleche de la moitie (22px, verifie via getBoundingClientRect) pour la
     centrer sur le bloc de rangees, pas sur la colonne entiere. transform (pas margin) pour
     un decalage plein, pas la moitie qu'align-items:center absorbe avec une marge simple. */
  transform: translateY(22px);
}
.home-needdemo-arrow svg { width: 100%; height: 100%; }

/* Meme langage que "My palettes" : barre pleine largeur de cases collees (gap 5px, coins
   arrondis 10px comme .hero-block), pas des puces isolees — pleine largeur (pas de
   max-width) mais moins hautes que la passe precedente (40px, pas 48 : trop hautes pour 5
   palettes empilees). Hex en surimpression (meme esprit que .hero-block-hex) : couleur de
   texte fixee a la main par case (voir style="color:...") pour rester lisible sur chaque
   teinte, claire ou sombre. */
.home-needdemo-palettes { width: 100%; display: flex; flex-direction: column; gap: 12px; }
.home-needdemo-palette-row { display: flex; gap: 6px; width: 100%; }
/* 58px (avant 54) : rallongees pour matcher exactement la hauteur des cartes
   .home-needdemo-engine-pot (10e passe) — meme hauteur + meme gap (12px) des deux cotes =
   meme "pas" de rangee (70px), donc les 5 rangees de gauche tombent pile sur les 5 rangees de
   la grille de droite une fois les deux blocs demarres au meme niveau (voir .home-needdemo-
   heading / .home-needdemo-compare plus bas). */
.home-needdemo-palette-row span {
  flex: 1; height: 58px; border-radius: 10px;
  display: flex; align-items: center; justify-content: center;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,0.35);
}
.home-needdemo-palette-row span em {
  font-family: 'JetBrains Mono', monospace; font-style: normal; font-size: 10px;
  letter-spacing: 0.01em; opacity: 0.85;
}

/* Titres d'etat mis en avant (pas une petite legende mono) : meme registre que les autres
   titres de mini-demo sur la page. Hauteur fixee a 28px (avant 1.3em ~27.3px) pour matcher au
   pixel pres .home-needdemo-heading — voir son commentaire pour le pourquoi (alignement des
   rangees).
   width:100% (pas seulement min-width:230px) — demande explicite (2026-09-23) : les span
   enfants sont en position:absolute, donc ne contribuent PAS a la largeur intrinseque de ce
   bloc (shrink-to-fit) — sans largeur explicite il retombait exactement sur son min-width
   (230px), trop etroit une fois le texte passe en MAJUSCULES (text-transform ci-dessus) pour
   tenir sur une ligne en desktop, dans les 2 langues ("WITHOUT OUR ENGINE" comme "SANS NOTRE
   MOTEUR"). white-space:nowrap sur les span (regle suivante) est le vrai garde-fou : la
   largeur seule ne suffit qu'une fois combinee avec elle. */
.home-needdemo-heading-cycle {
  position: relative; height: 28px; min-width: 230px; width: 100%;
  font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif; font-size: 21px; font-weight: 700;
  color: var(--text); text-transform: uppercase;
}
.home-needdemo-heading-cycle span {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  font: inherit; color: inherit; white-space: nowrap;
  opacity: 0; transform: translateY(6px); animation: home-needdemo-fade 6s ease infinite;
}
/* Hauteur fixe (338px = 5 rangees de 58px + 4 ecarts de 12px, voir .home-needdemo-engine-grid)
   au lieu de flex:1 (10e passe) : flex:1 laissait ce bloc remplir tout le reste de la colonne,
   ce qui poussait .home-needdemo-heading-cycle tout en haut pendant que la colonne de gauche
   (bloc libelle+palettes de taille fixe) restait centree comme un tout — les deux libelles
   demarraient donc a des hauteurs differentes et les rangees ne pouvaient pas s'aligner. Une
   hauteur fixe identique au bloc de gauche fait que .home-needdemo-col centre les deux blocs
   de la meme facon des deux cotes. */
.home-needdemo-compare { position: relative; width: 100%; height: 338px; }
.home-needdemo-compare-state {
  position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: 16px;
  opacity: 0; transform: translateY(6px); animation: home-needdemo-fade 6s ease infinite;
}
/* "With our engine" seul (l'etat qui doit s'aligner avec les target colors) : la grille est
   calee en haut du bloc (meme niveau que les rangees de gauche) plutot que centree — le
   nombre de pots en dessous deborde alors sous les 338px (il y a de la marge : le bloc entier
   est plus etroit que la colonne, voir le centrage global plus haut), sans jamais toucher au
   "without our engine" au-dessus, reste centre comme avant. Recentre en mobile (colonnes
   empilees, plus de contrainte d'alignement — voir media query plus bas). */
/* gap reduit (16px -> 8px, cette state uniquement) : l'ajout du cadre "In stock" (padding +
   bordure, voir .home-needdemo-instock plus bas) a fait deborder le bloc sous le bandeau —
   resserre les 2 gaps de flex-column plutot que de toucher au gap partage de la state-1
   (naive grid, jamais concernee par ce debordement). */
.home-needdemo-compare-state-2 { justify-content: flex-start; gap: 6px; }

/* "Without our engine" : une vraie grille (11 colonnes), pas un flex-wrap qui laissait un
   "blob" irregulier flotter dans beaucoup de vide (11e passe, refonte harmonie visuelle
   demandee explicitement). 43 pots sur 11 colonnes = 4 rangees, la derniere remplie a 10/11 —
   bord quasi net des 2 cotes, presque aucun "trou". Largeur 100% (avant fixee a 328px, 12e
   passe : ca laissait 133px de vide de chaque cote dans la colonne, contrairement a gauche ou
   les target colors touchent les bords de leur colonne — demande explicite "equilibre la
   marge a droite et a gauche"), meme largeur maintenant que .home-needdemo-engine-grid juste
   en dessous, elle aussi passee a 100%. Cases plus grandes qu'avant (~43px a 514px de large,
   contre 26px a 328px) grace a cet elargissement. */
.home-needdemo-naive-grid {
  display: grid; grid-template-columns: repeat(11, 1fr); gap: 4px; width: 100%;
}
.home-needdemo-naive-grid span {
  aspect-ratio: 1; border-radius: 6px;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,0.35);
}

/* "With our engine" : 6 cartes rectangulaires en grille 2 colonnes x 3 lignes
   (grid-auto-flow:column — les 3 premieres du HTML remplissent la colonne de gauche, les 3
   suivantes la colonne de droite). Nom + "Reused ×N" (mot en toutes lettres, pas juste le
   symbole) tous deux EN SURIMPRESSION dans la carte — seul cet etat porte du texte sur ses
   cases (pas la grille "without our engine" juste au-dessus), pour marquer le contraste
   "aucune reflexion" vs "chaque pot est identifie". */
/* 10 pots (resultat reel, pas 6 : voir commentaire HTML) -> grille 2 colonnes x 5 lignes,
   toujours grid-auto-flow:column pour garder "colonne de gauche puis colonne de droite". */
/* width:100% (avant 158px fixe par carte, la grille suivait juste la somme) : meme
   elargissement que .home-needdemo-naive-grid juste au-dessus, meme raison (12e passe,
   "equilibre la marge a droite et a gauche") — les cartes s'etirent maintenant dans leurs
   colonnes 1fr au lieu de laisser du vide de chaque cote de la grille. */
/* Petit bandeau "In stock" isole en tete du bloc (2026-09-21, demande explicite) : plutot
   qu'une pastille sur chaque carte, les 2 peintures deja possedees (Flat White, Navy Blue)
   sortent de la grille de 10 pour vivre dans leur propre mini-section, meme carte/style
   (.home-needdemo-engine-pot reutilisee telle quelle), juste regroupees sous un label. La
   grille des 8 pots restants passe de 5 a 4 lignes en consequence.
   Corrige juste apres (meme session) : sans cadre propre, le simple gap:16px herite de
   .home-needdemo-compare-state ne suffisait pas a distinguer ces 2 cartes du reste — ca
   donnait l'impression que TOUTES les peintures etaient en stock. Fond + bordure teintes en
   vert (meme couleur que le label et que .owned-pick-tag ailleurs dans l'app, juste en
   version tres diluee — pas de nouveau token, meme recette que .saved-palette-icon-btn.
   confirming qui fait deja un wash rgba() a partir d'un token existant plutot que d'en
   ajouter un) : un vrai cadre visuel, pas juste un espacement. */
.home-needdemo-instock {
  display: flex; flex-direction: column; gap: 4px; width: 100%;
  padding: 6px 10px; border-radius: 12px;
  background: rgba(63,107,74,0.08); border: 1px solid rgba(63,107,74,0.22);
}
.home-needdemo-instock-label {
  font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif; font-weight: 600;
  font-size: 12px; color: var(--good-text); align-self: flex-start;
}
.home-needdemo-instock-row {
  display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; width: 100%;
}
.home-needdemo-engine-grid {
  display: grid; grid-auto-flow: column; grid-template-rows: repeat(4, auto);
  grid-template-columns: repeat(2, 1fr); gap: 12px; width: 100%;
}
/* 46px (avant 58) : le bloc "With our engine" (cadre "My stock" + grille) a grossi de 74px
   au total (412px contre 338px a gauche) depuis l'ajout du cadre "My stock" — desequilibre
   signale explicitement. Reduire la hauteur des cartes (ici + dans .home-needdemo-instock-row,
   meme classe reutilisee) plutot que d'agrandir la colonne de gauche : le rapprochement pixel-
   parfait rangee a rangee entre les 2 colonnes etait deja rompu par le cadre "My stock" (qui
   n'est plus une simple rangee de 58px comme les autres), donc rien a perdre a resserrer ce
   cote plutot que l'autre. */
.home-needdemo-engine-pot {
  height: 46px; border-radius: 12px; overflow: hidden;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,0.35);
}
.home-needdemo-engine-name {
  font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif; font-size: 15px; font-weight: 700;
  white-space: nowrap;
}
.home-needdemo-engine-count {
  font-family: 'JetBrains Mono', monospace; font-size: 11px; font-weight: 600; opacity: 0.85;
  white-space: nowrap;
}

/* De retour DANS chaque etat, juste sous sa grille (12e passe, demande explicite "remets le
   43 paints sous les couleurs") : la 11e passe l'avait detache en position fixe pour eviter
   qu'il saute de position d'un etat a l'autre, mais l'ecart resultant avec la grille
   "without our engine" (bien plus courte, centree dans les 338px) rendait le nombre trop
   eloigne des couleurs qu'il decrit. Redevient un simple enfant du flex de chaque etat — pas
   de position/opacity/animation propres, il suit le fondu de son etat parent comme le reste
   de son contenu. */
.home-needdemo-compare-number {
  font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif; font-size: 26px; font-weight: 800;
  color: var(--text);
}

.home-needdemo-heading-cycle span:nth-child(1), .home-needdemo-compare-state-1 { animation-delay: 0s; }
.home-needdemo-heading-cycle span:nth-child(2), .home-needdemo-compare-state-2 { animation-delay: 3s; }
@keyframes home-needdemo-fade {
  0% { opacity: 0; transform: translateY(6px); }
  3%, 47% { opacity: 1; transform: translateY(0); }
  50%, 100% { opacity: 0; transform: translateY(-6px); }
}
@media (prefers-reduced-motion: reduce) {
  .home-needdemo-heading-cycle span, .home-needdemo-compare-state { animation: none; opacity: 0; transform: none; }
  .home-needdemo-heading-cycle span:first-child, .home-needdemo-compare-state-1 { opacity: 1; transform: none; }
}

/* Meme police et meme ecart avec le bandeau que .home-create-banner-desc (var(--s4)), mais
   taille et largeur legerement reduites par rapport a lui (clamp 19-25px/74ch) : ce texte-ci
   est plus long (compte-rendu du moteur, pas juste "every rule, every mood"), donc a 74ch/25px
   il retombait sur 3 lignes. max-width en ch suit le font-size (ch = largeur d'un caractere a
   la taille courante) : reduire seulement la taille ne change PAS le nombre de caracteres par
   ligne, il faut aussi elargir le ch-cap — verifie via getBoundingClientRect (82ch = 2 lignes,
   74ch = 3, quelle que soit la taille). 84ch/17-21px choisi comme le plus proche de la
   reference Create tout en tenant sur 2 lignes de facon fiable sur toute la plage clamp. */
/* Chiffre choc ajoute au-dessus du sous-texte existant (2026-09-21, demande explicite) :
   garde le meme ecart avec le bandeau que .home-need-banner-desc avait auparavant
   (margin-top: var(--s4)) — c'est desormais LUI qui touche le bandeau, .home-need-banner-desc
   est descendu d'un ecart plus petit (var(--s2)) pour laisser la place sans dedoubler le vide. */
.home-need-banner-stat {
  font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif; font-weight: 600;
  font-size: clamp(15px, 2vw, 19px); letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--accent); margin: var(--s4) 0 0; text-wrap: balance;
}
.home-need-banner-desc {
  max-width: 84ch;
  font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif; font-weight: 400;
  font-size: clamp(17px, 2.2vw, 21px); line-height: 1.5; color: var(--muted);
  margin: var(--s2) 0 0; text-wrap: balance;
}
.home-need-banner-desc:empty { display: none; }

/* ---- CTA final, fait echo au hero. ---- */
.home-closing {
  display: flex; flex-direction: column; align-items: center; gap: var(--s2);
  padding: var(--s5) var(--s3) 0; text-align: center;
  border-top: 1px solid var(--border);
}
.home-closing-eyebrow {
  font-family: 'Archivo Black', 'Inter', sans-serif; font-size: 12px;
  letter-spacing: 0.14em; color: var(--faint); margin: 0;
}
.home-closing-title {
  font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif; font-weight: 500;
  font-size: clamp(24px, 3.6vw, 38px); letter-spacing: -0.015em; margin: 0;
}
/* Attribution Mixbox (licence gratuite jusqu'a 10 000 euros de revenus, lien visible
   n'importe ou sur le site) : vivait ici, deplacee dans le pied de page commun
   (.site-footer-credit) maintenant qu'il existe sur toutes les pages — pas de raison de la
   dupliquer. */

@media (max-width: 760px) {
  /* Bandeaux de section a la largeur de l'ecran, comme la palette de Create (demande explicite,
     2026-09-23) : chaque section (.home-hero, .home-pitch, .home-create-banner,
     .home-accuracy-banner, .home-need-banner, .home-pricing, .home-closing) porte son propre
     padding horizontal var(--s3)=24px, qui s'ajoutait au 24px deja pose par <main> — 48px de
     marge cumulee de chaque cote sur mobile (mesure), contre 24px pour la palette de Create
     (seul le padding de <main>). Les paddings haut/bas de chaque section restent inchanges
     (seuls left/right sont mis a 0 ici) — ne touche pas aux tailles de titres/sous-textes. */
  .home-hero, .home-pitch, .home-create-banner, .home-accuracy-banner,
  .home-need-banner, .home-pricing, .home-closing {
    padding-left: 0; padding-right: 0;
  }
  .home-create-banner-visual { aspect-ratio: 4 / 3; }
  /* height:auto (pas aspect-ratio:9/16) : avec overflow:hidden sur ce panneau, un
     aspect-ratio fige coupait silencieusement le bas du contenu des que celui-ci grandissait
     un peu (verifie : la grille "without our engine" et le compteur en dessous depassaient de
     ~260px un panneau de 496px de haut apres l'ajout du compteur a position fixe, 11e passe).
     Le contenu empile (5 rangees + fleche + colonne droite) determine sa propre hauteur au
     lieu de tenter de deviner un ratio qui tienne pour toujours. */
  .home-need-banner-visual { aspect-ratio: auto; height: auto; }
  /* align-items:center (regle de base, pour centrer les colonnes verticalement en ligne sur
     desktop) fait le contraire une fois empile en colonne ici : les colonnes se reduisent a
     la largeur de leur contenu au lieu de remplir la largeur — d'ou le chevauchement observe
     (.home-needdemo-compare a width:100% d'un parent lui-meme quasi nul). stretch corrige. */
  .home-needdemo { flex-direction: column; align-items: stretch; }
  .home-needdemo-col-left { padding-top: var(--s2); }
  .home-needdemo-col { height: auto; padding: var(--s2); gap: 10px; }
  /* .home-needdemo-compare-state est en position:absolute (inset:0) : il lui faut un
     ancetre a hauteur definie, or .home-needdemo-col passe en height:auto ci-dessus —
     sans min-height explicite ici, le conteneur s'effondrerait a 0 et les 2 etats se
     chevaucheraient avec le reste de la colonne. */
  /* 5 lignes (10 pots reels, pas 3) : plus haut qu'avant. height:auto reprend le dessus sur le
     338px fixe du bureau (voir plus haut) : en mobile les colonnes sont empilees, pas cote a
     cote, donc plus besoin d'une hauteur figee pour l'alignement — min-height reste juste un
     plancher pour eviter l'effondrement (cf commentaire ci-dessus). Idem pour le calage en
     haut de "with our engine" (justify-content:flex-start) : inutile ici, on revient au
     centrage d'origine. */
  /* 325px (pas 340) : rapproche avec les hauteurs naturelles mesurees des 2 etats une fois les
     correctifs ci-dessous appliques (~320-325px chacun, voir grille agrandie plus bas) — moins
     de vide artificiel en haut/bas de chaque etat une fois centre (bug signale explicitement,
     2026-09-23 : "bandeau completement destructure"). */
  .home-needdemo-compare { height: auto; min-height: 325px; }
  .home-needdemo-compare-state-2 { justify-content: center; }
  /* align-self:center (par defaut colle a gauche : align-items:stretch sur .home-needdemo en
     colonne ne s'applique pas a un item a largeur explicite, qui retombe donc au bord de depart
     du cross-axis) + marge verticale : separateur bien centre entre les 2 blocs empiles, plus de
     "coince" comme sur ordi. */
  .home-needdemo-arrow { transform: rotate(90deg); width: 26px; height: 26px; align-self: center; margin: 2px 0; }
  .home-needdemo-palettes { max-width: 260px; gap: 8px; }
  .home-needdemo-palette-row span { height: 36px; }
  .home-needdemo-palette-row span em { font-size: 7.5px; }
  /* width:100% (en plus de min-width:0) : sans largeur explicite, ce bloc (enfant d'un flex en
     colonne avec align-items:center herite de .home-needdemo-col) retombait a 0px de large — ses
     spans en position:absolute;inset:0 heritaient donc aussi de 0px, forcant le texte
     "With our engine"/"Without our engine" a un retour a la ligne par mot (bug reel, pas un
     artefact de mesure — confirme via getBoundingClientRect en conditions reelles). */
  .home-needdemo-heading-cycle { font-size: 17px; min-width: 0; width: 100%; }
  /* Pleine largeur de colonne (comme .home-needdemo-engine-grid juste en dessous), pas 248px
     fixe : les cases retombaient a ~20px, illisibles (signale explicitement). 8 colonnes (au
     lieu de 11) => cases ~37px, bien plus lisibles, et la hauteur naturelle de cet etat se
     rapproche de celle de "With our engine" (voir .home-needdemo-compare plus haut). */
  .home-needdemo-naive-grid { width: 100%; grid-template-columns: repeat(8, 1fr); gap: 4px; }
  .home-needdemo-engine-grid { gap: 8px; }
  /* width retiree (avant 120px fixe) : sur une grille a 2 colonnes 1fr, une largeur explicite
     plus etroite que la piste laissait un vide a droite de chaque carte — meme correctif que
     .home-needdemo-naive-grid juste au-dessus. */
  .home-needdemo-engine-pot { height: 46px; }
  .home-needdemo-engine-name { font-size: 12.5px; }
  .home-needdemo-engine-count { font-size: 9.5px; }
  .home-needdemo-compare-number { font-size: 21px; }
  .home-demo-stage { max-height: 140px; }
  /* Chaque case suivie de sa propre legende, plutot que les 2 cases puis les 2 legendes :
     plus logique une fois empile en une seule colonne. */
  .home-showcase { grid-template-columns: 1fr; }
  .home-showcase-cube { grid-column: 1; grid-row: 1; aspect-ratio: 4 / 3; }
  .home-showcase-caption { grid-column: 1; grid-row: 2; }
  .home-showcase-rect { grid-column: 1; grid-row: 3; aspect-ratio: 4 / 3; }
  .home-pitch-sub { grid-column: 1; grid-row: 4; }
  .home-brand-pill { padding: 11px 18px; font-size: 13px; }
  .home-mixdemo-card { gap: 6px; padding: 0 8px; }
  .home-mixdemo-paint { gap: 4px; }
  .home-mixdemo-paint-swatch { width: 26px; height: 26px; border-radius: 7px; }
  .home-mixdemo-paint-name { font-size: 10px; max-width: 15vw; overflow: hidden; text-overflow: ellipsis; }
  .home-mixdemo-paint-pct { font-size: 10px; }
  .home-mixdemo-plus { font-size: 13px; }
  .home-mixdemo-target-hex { font-size: 18px; }
}

/* ============================================================
   PIED DE PAGE COMMUN — un seul <footer>, hors de <main> pour
   s'etirer sur toute la largeur. Le site etant une page unique a
   onglets (palette_lab.html) plus 5 pages autonomes (mentions
   legales, CGV, confidentialite, cookies, contact), ce meme bloc
   HTML est reutilise tel quel partout — pas de systeme de gabarit
   partage (le projet n'utilise pas Jinja pour le contenu), donc
   duplique volontairement dans chaque fichier plutot qu'invente un
   mecanisme d'inclusion pour 6 occurrences.
   ============================================================ */
.site-footer { border-top: 1px solid var(--border); margin-top: var(--s6); }
.site-footer-inner {
  max-width: 1320px; margin: 0 auto; padding: var(--s4) var(--s3);
  display: flex; flex-direction: column; gap: var(--s3);
}
.site-footer-top {
  display: flex; align-items: flex-start; justify-content: space-between;
  flex-wrap: wrap; gap: var(--s3);
}
/* Logo seul, pas de nom a cote : meme parti pris que le bandeau superieur (voir .brand) —
   coherent, et evite qu'un futur renommage du site touche au pied de page. */
.site-footer-brand { display: flex; align-items: center; text-decoration: none; opacity: 0.7; transition: opacity 0.15s ease; }
.site-footer-brand:hover { opacity: 1; }
.site-footer-brand .logo-mark { width: 26px; height: 26px; }
.site-footer-links { display: flex; flex-wrap: wrap; gap: var(--s3); list-style: none; margin: 0; padding: 0; }
.site-footer-links a {
  font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif; font-size: 13px;
  color: var(--muted); text-decoration: none;
}
.site-footer-links a:hover { color: var(--text); text-decoration: underline; }
.site-footer-bottom {
  display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: var(--s2);
  padding-top: var(--s3); border-top: 1px solid var(--border);
}
.site-footer-copyright, .site-footer-credit {
  font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif; font-size: 12px;
  color: var(--faint); margin: 0; text-decoration: none;
}
a.site-footer-credit:hover { color: var(--muted); text-decoration: underline; }

@media (max-width: 760px) {
  .site-footer-top { flex-direction: column; align-items: flex-start; }
  .site-footer-bottom { flex-direction: column; align-items: flex-start; gap: 6px; }
}

/* ============================================================
   PAGES LEGALES (mentions-legales, cgv, confidentialite, cookies,
   contact) — pages autonomes, sans le JS de l'app (pas d'onglets a
   piloter ici) : seul le <header> avec le logo (identique a
   palette_lab.html, deja stylé par la regle "header{}" partagee)
   et ce pied de page sont repris, pour rester reconnaissable comme
   la meme app sans dupliquer la logique des onglets/compte.
   ============================================================ */
.legal-main {
  display: block; max-width: 760px; margin: 0 auto; padding: var(--s5) var(--s3) var(--s6);
  font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif;
}
.legal-eyebrow {
  font-family: 'Archivo Black', 'Inter', sans-serif; font-size: 11px;
  letter-spacing: 0.14em; color: var(--faint); margin: 0 0 var(--s1);
}
.legal-title { font-weight: 500; font-size: clamp(28px, 4vw, 40px); letter-spacing: -0.015em; margin: 0 0 var(--s2); }
.legal-updated { font-size: 13px; color: var(--muted); margin: 0 0 var(--s4); }
.legal-meta {
  font-size: 13.5px; line-height: 1.7; color: var(--muted); border: 1px solid var(--border);
  border-radius: 10px; padding: var(--s2) var(--s3); margin: 0 0 var(--s4);
}
.legal-meta strong { color: var(--text); font-weight: 600; }
.legal-todo { color: var(--warn-text); font-weight: 600; }
.legal-main h2 { font-weight: 600; font-size: 19px; margin: var(--s4) 0 var(--s2); }
.legal-main h3 { font-weight: 600; font-size: 15px; margin: var(--s3) 0 var(--s1); }
.legal-main p, .legal-main li { font-size: 14.5px; line-height: 1.65; color: var(--text); }
.legal-main p { margin: 0 0 var(--s2); }
.legal-main ul { margin: 0 0 var(--s2); padding-left: 20px; }
.legal-main li { margin-bottom: 6px; }
.legal-main a { color: var(--accent); }
.legal-main strong { font-weight: 600; }
.legal-contact-block {
  border: 1px solid var(--border); border-radius: 10px; padding: var(--s3);
  margin: 0 0 var(--s3);
}
.legal-contact-block p { margin: 0 0 4px; }
.legal-contact-block p:last-child { margin-bottom: 0; }

@media (max-width: 760px) {
  .legal-main { padding: var(--s4) var(--s3) var(--s5); }
}

/* ============================================================
   BANDEAU DE CONSENTEMENT COOKIES (Google Analytics) — meme
   registre visuel que le header (verre depoli) et le pied de page
   (bordure fine). Accepter/Refuser sont deux VRAIS boutons de
   meme gabarit (.btn-primary/.btn-secondary, pas un bouton plein
   contre un simple lien) : la CNIL sanctionne en priorite un
   refus rendu plus difficile qu'un acceptation. Rien ne se
   charge tant que ce bandeau n'a pas ete tranche une fois (voir
   setupCookieBanner, palette_lab.js, et le petit script equivalent
   duplique dans les 5 pages legales autonomes).
   ============================================================ */
.cookie-banner {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 70;
  background: rgba(255,255,255,0.92);
  backdrop-filter: blur(14px) saturate(160%);
  -webkit-backdrop-filter: blur(14px) saturate(160%);
  border-top: 1px solid var(--border);
}
.cookie-banner[hidden] { display: none; }
.cookie-banner-inner {
  max-width: 1320px; margin: 0 auto; padding: var(--s2) var(--s3);
  display: flex; align-items: center; justify-content: space-between; gap: var(--s3); flex-wrap: wrap;
}
.cookie-banner-text {
  flex: 1 1 420px; font-family: 'Inter', ui-sans-serif, 'Helvetica Neue', sans-serif;
  font-size: 13px; line-height: 1.5; color: var(--muted); margin: 0;
}
.cookie-banner-text a { color: var(--text); text-decoration: underline; }
.cookie-banner-actions { display: flex; align-items: center; gap: 10px; flex-shrink: 0; }
/* margin:0 : .btn-primary herite malgre lui un margin-top:10px d'une regle plus ancienne
   (static/style.css, #generate-btn) que .btn-secondary n'a pas - sans ce reset, les deux
   boutons n'avaient pas la meme hauteur dans ce bandeau flex (etire en align-items:stretch par
   defaut, la marge asymetrique reduisait la hauteur du seul bouton "Accepter"). */
/* min-width egale (pas juste meme hauteur) : "Accepter"/"Accept" et "Refuser"/"Decline" n'ont
   pas la meme longueur de texte, une simple egalite de padding laissait deux largeurs
   differentes malgre des boutons de meme gabarit visuel par ailleurs. */
.cookie-banner-actions button { margin: 0; padding: 9px 18px; font-size: 13px; min-width: 112px; text-align: center; cursor: pointer; }

@media (max-width: 760px) {
  .cookie-banner-inner { padding: var(--s2) var(--s3); }
  .cookie-banner-actions { width: 100%; }
  .cookie-banner-actions button { flex: 1; }
  /* Sur l'app (pas les pages legales, qui n'ont pas cette barre), laisse la place a la barre
     de nav mobile fixee en bas — sinon les deux bandeaux se superposent. */
  body:has(.bottom-tab-bar) .cookie-banner { bottom: 64px; }
}
