/* Newsreader plutôt que Fraunces depuis le 10/09/2026 : le f et le j de Fraunces portent
   un terminal en boule très marqué qui vient coller la lettre d'après (« Profil », « fin »,
   « jus », « affinité ») — remonté comme illisible. Ce n'était ni les ligatures ni l'axe
   WONK (déjà à 0), c'est son dessin. Newsreader garde le même esprit éditorial avec des
   lettres sobres. Les poids 600/700 et l'italique 600 sont tous utilisés par les canvas de
   partage (cf. app.js), d'où leur chargement explicite ici. */
@import url('https://fonts.googleapis.com/css2?family=Newsreader:ital,opsz,wght@0,6..72,600..700;1,6..72,600&display=swap');

:root{
  --bg:#0b0e14;
  --bg-alt:#10141d;
  --card:#141924;
  --card-border:#1f2733;
  --text:#e7ebf3;
  --text-muted:#8792a3;
  --green:#31d0aa;
  --red:#ff6b7a;
  --blue:#5b8cff;
  --purple:#a276ff;
  --amber:#ffb84d;
  --wine:#c2385b;
  /* Système de rayons/ombres unifié (31/08/2026, retour de Damien : "pas assez pro" — le CSS
     avait accumulé ~10 valeurs de border-radius différentes et une box-shadow bricolée par
     composant après 10 jours d'itérations au cas par cas). 3 rôles, jamais un de plus :
     r-sm = contrôles/petits éléments, r-lg (= --radius, alias conservé pour compat) = cartes/
     panneaux, r-pill = chips/badges/barres de progression. Mockup validé par Damien avant
     portage : cf. le canvas "Refonte Vinifique" (session du 31/08/2026). */
  --r-sm:10px;
  --r-lg:22px;
  --r-pill:999px;
  --radius:var(--r-lg);
  --e1:0 1px 2px rgba(4,6,10,.55), 0 14px 30px -20px rgba(4,6,10,.65);
  --e2:0 22px 46px -18px rgba(4,6,10,.7);
  --glow-wine:0 12px 26px -8px rgba(194,56,91,.55);
  --glow-amber:0 10px 22px -8px rgba(255,184,77,.4);
}

*{box-sizing:border-box;}
/* L'attribut `hidden` doit TOUJOURS gagner (14/09/2026). Le navigateur ne lui applique
   `display:none` que par sa feuille par défaut : n'importe quelle règle à nous qui pose un
   `display` sur le même élément l'emporte, et l'élément reste visible alors que le JS le croit
   caché — sans aucune erreur nulle part. Deux cas trouvés le jour même : l'invitation au plan
   de cave (`.cave-invite{display:flex}`, signalée par Damien — « que je fasse plus tard ou
   essayer, le message reste toujours ») et le bouton de rafale (`.btn{display:inline-flex}`),
   affiché depuis toujours alors qu'il ne doit apparaître qu'avec un meuble existant. Le projet
   contournait déjà le piège au cas par cas (`.gc[hidden]`, `.cave-vue-toggle[hidden]`) ; cette
   règle unique le règle pour tout élément présent ou futur.
   ⚠️ Corollaire pour les tests : vérifier la VISIBILITÉ RÉELLE (is_visible), jamais la
   propriété `el.hidden` — elle valait bien `true` dans les deux cas. */
[hidden]{display:none!important;}

body{
  margin:0;
  font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;
  background:radial-gradient(circle at 20% -10%, #33161f 0%, var(--bg) 48%) fixed;
  color:var(--text);
  min-height:100vh;
}

a{color:var(--wine);}

/* ---------- Écran de chargement initial (24/08/2026) ---------- */
.app-loader{
  position:fixed;inset:0;z-index:100;background:var(--bg);
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:20px;
  transition:opacity .35s ease;
}
.app-loader.hidden{opacity:0;pointer-events:none;}
.app-loader-mark{
  width:64px;height:64px;border-radius:16px;overflow:hidden;
  box-shadow:var(--glow-wine);
  animation:loader-pulse 1.6s ease-in-out infinite;
}
.app-loader-mark img{width:100%;height:100%;object-fit:cover;display:block;}
@keyframes loader-pulse{0%,100%{transform:scale(1);}50%{transform:scale(1.08);}}
.app-loader-bar{width:140px;height:4px;border-radius:var(--r-pill);background:var(--card-border);overflow:hidden;}
.app-loader-bar-fill{
  width:40%;height:100%;border-radius:var(--r-pill);
  background:linear-gradient(135deg,var(--wine),var(--amber));
  animation:loader-slide 1.1s ease-in-out infinite;
}
@keyframes loader-slide{0%{transform:translateX(-100%);}100%{transform:translateX(350%);}}
@media (prefers-reduced-motion:reduce){
  .app-loader-mark{animation:none;}
  .app-loader-bar-fill{animation:none;width:100%;}
}

.auth-page{display:flex;align-items:center;justify-content:center;min-height:100vh;padding:20px;}
.auth-card{background:var(--card);border:1px solid var(--card-border);border-radius:var(--radius);
  padding:32px;width:100%;max-width:380px;}
.auth-card h1{margin:0 0 4px;font-size:24px;font-family:'Newsreader',Georgia,serif;font-weight:600;}
.auth-card .brand-sub{color:var(--text-muted);font-size:13px;margin-bottom:24px;}
.auth-card label{display:block;font-size:13px;color:var(--text-muted);margin:14px 0 6px;}
.auth-card input{
  width:100%;padding:10px 12px;background:var(--bg-alt);border:1px solid var(--card-border);
  border-radius:var(--r-sm);color:var(--text);font-size:16px;
}
.auth-card .check-majeur{display:flex;align-items:center;gap:9px;margin:16px 0 4px;
  color:var(--text);font-size:14px;cursor:pointer;}
.auth-card .check-majeur input{width:18px;height:18px;padding:0;margin:0;flex:none;accent-color:var(--wine);}
.auth-card .error{color:var(--red);font-size:13px;margin-top:10px;min-height:16px;}
.auth-card .ok{color:var(--green);font-size:13px;margin-top:10px;}
.auth-card .switch{margin-top:18px;font-size:13px;color:var(--text-muted);text-align:center;}
.auth-badge{
  width:48px;height:48px;border-radius:50%;display:flex;align-items:center;justify-content:center;
  font-size:22px;font-weight:700;margin:0 auto 16px;
}
.auth-badge.success{background:rgba(49,208,170,.15);color:var(--green);}
.auth-badge.error{background:rgba(194,56,91,.15);color:var(--wine);}

/* Boutons alignés sur le langage visuel du reste de l'appli (dégradés/ombres des cartes,
   liseré amber) plutôt que des boutons de formulaire par défaut — retour de Damien
   21/08/2026 ("j'ai envie qu'ils appartiennent à l'appli, là c'est fait vraiment basique"). */
.btn{
  padding:11px 16px;border-radius:var(--r-sm);border:none;font-size:14px;cursor:pointer;font-weight:700;
  display:inline-flex;align-items:center;justify-content:center;gap:7px;
  transition:transform .12s ease, box-shadow .12s ease, background .12s ease, border-color .12s ease, filter .12s ease;
}
.btn svg{width:15px;height:15px;flex-shrink:0;}
.btn:active{transform:scale(.97);}
.btn-primary{
  background:linear-gradient(135deg,#e0507a,var(--wine) 55%,#8a2140);color:#fff;width:100%;margin-top:20px;
  box-shadow:var(--glow-wine);
}
.btn-primary:hover{filter:brightness(1.08);}
.btn-secondary{
  background:linear-gradient(160deg, rgba(255,255,255,.06), rgba(255,255,255,0) 55%), var(--card);
  border:1px solid rgba(255,184,77,.16);color:var(--text);
}
.btn-secondary:hover{border-color:rgba(255,184,77,.35);}
.btn-ghost{
  background:linear-gradient(160deg, rgba(255,255,255,.03), rgba(255,255,255,0) 55%), transparent;
  border:1px solid var(--card-border);color:var(--text-muted);
}
.btn-ghost:hover{border-color:rgba(255,184,77,.3);color:var(--text);background:rgba(255,184,77,.05);}
.btn-danger{color:var(--red);border-color:rgba(255,107,122,.25);}
.btn-danger:hover{border-color:rgba(255,107,122,.5);background:rgba(255,107,122,.08);color:var(--red);}
.btn-block{width:100%;margin-top:6px;}
.btn:disabled{opacity:.5;cursor:not-allowed;transform:none;}

.app-shell{display:flex;min-height:100vh;}
.sidebar{width:220px;border-right:1px solid var(--card-border);padding:20px 12px;display:flex;flex-direction:column;}
.brand{font-family:'Newsreader',Georgia,serif;font-weight:600;font-size:19px;
  background:linear-gradient(135deg,var(--wine),var(--amber));
  -webkit-background-clip:text;background-clip:text;color:transparent;padding:0 8px;}
.nav-item{
  display:flex;align-items:center;gap:10px;padding:10px 12px;border-radius:var(--r-sm);color:var(--text-muted);
  text-decoration:none;font-size:14px;margin-top:4px;background:transparent;border:none;width:100%;
  text-align:left;cursor:pointer;
}
/* :hover seulement sur un appareil à pointeur (23/09/2026) : sur mobile il restait collé
   après un appui, avec la pastille grise de tap d'Android par-dessus. */
@media (hover:hover){.nav-item:hover{background:rgba(194,56,91,.12);color:var(--text);}}
.nav-item{-webkit-tap-highlight-color:transparent;-webkit-user-select:none;user-select:none;}
.nav-item.active{background:rgba(194,56,91,.18);color:var(--text);}
/* Sur bureau (sidebar verticale), les blocs gauche/droite ne doivent rien changer à
   l'affichage : display:contents les "efface" pour que leurs boutons restent des enfants
   flex directs de .sidebar, comme avant leur introduction. Redéfini en mobile ci-dessous. */
.nav-group{display:contents;}
.nav-icon{display:flex;line-height:1;}
.nav-icon svg{width:18px;height:18px;stroke:currentColor;}
.sidebar-footer{margin-top:auto;font-size:12px;color:var(--text-muted);padding:0 8px;}

.main{flex:1;padding:28px;max-width:1000px;}
/* Admin (01/09/2026, retour de Damien : "le gros tableau fiche vin rentre pas, faut scroll
   sur la droite... j'aimerais une belle vue") : le max-width:1000px ci-dessus est pensé pour
   l'appli grand public (colonne de lecture confortable sur un écran large) — beaucoup trop
   étroit pour un tableau de 13 colonnes consulté sur PC. Pas de max-width du tout ici : le
   scroll horizontal ajouté sur chaque table (cf. admin.html) reste le filet de secours pour
   un écran vraiment étroit, mais ne devrait plus jamais se déclencher sur un écran de bureau
   normal une fois cette largeur libérée. */
.admin-main{max-width:none;}
.main h2{margin-top:0;font-family:'Newsreader',Georgia,serif;font-weight:600;}
.section-title{margin:24px 0 10px;font-size:15px;color:var(--text-muted);text-transform:uppercase;letter-spacing:.04em;}
.section-title:first-of-type{margin-top:0;}
.muted{color:var(--text-muted);font-size:14px;}

.field-input{
  display:block;width:100%;padding:10px 12px;margin:6px 0 12px;background:var(--bg-alt);
  border:1px solid var(--card-border);border-radius:var(--r-sm);color:var(--text);font-size:16px;font-family:inherit;
}
select.field-input{appearance:auto;}
textarea.field-input{min-height:70px;resize:vertical;}

.scan-actions{display:flex;gap:10px;flex-wrap:wrap;margin-bottom:14px;}
.scan-actions .btn{flex:1;min-width:150px;}
/* Grille 2 colonnes plutôt qu'un flex qui finissait presque toujours par empiler les boutons
   un par ligne (leur largeur cumulée dépassait de peu la largeur dispo sur mobile) — retour
   de Damien 21/08/2026 ("les boutons sont tous sur la gauche, pas possible d'en mettre 2 sur
   la même ligne ??"). Boutons plus compacts (padding/police réduits) pour ce contexte
   secondaire précisément pour que 2 tiennent bien côte à côte. */
.actions-row{display:grid;grid-template-columns:repeat(2, 1fr);gap:8px;}
.actions-row .btn{padding:9px 10px;font-size:12.5px;}
.actions-row .btn svg{width:13px;height:13px;}
/* Nombre impair de boutons (25/08/2026, retour de Damien) : le dernier se retrouvait tout
   seul collé à gauche sur sa ligne — :last-child + :nth-child(odd) n'est vrai que quand le
   total est impair, donc ne touche jamais les cas pairs (déjà bien alignés en 2 colonnes). */
.actions-row .btn:last-child:nth-child(odd){grid-column:1 / -1;justify-self:center;width:auto;min-width:180px;}

/* Chips de filtre couleur (cave) — proposées dans le mockup, demandées par Damien 21/08/2026. */
.color-chips{display:flex;gap:8px;margin-bottom:16px;overflow-x:auto;}
/* Chips accord inversé (01/09/2026) : à la différence des filtres ci-dessus (peu nombreux,
   scroll horizontal acceptable), ~10 chips plat/moment en scroll étaient "bof" niveau
   interface (retour de Damien) — repli sur plusieurs lignes plutôt qu'un scroll caché. */
.wrap-chips{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:10px;}
.chip-group-label{
  font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;
  color:var(--text-muted);margin:0 0 6px 2px;
}
/* Plat (teinte vin) vs Moment (teinte amber) — même palette rouge/amber que le reste de
   l'appli, pas une 3e couleur inventée (retour de Damien 01/09/2026). */
.chip-plat{border-color:rgba(194,56,91,.3);color:var(--wine);background:rgba(194,56,91,.05);}
.chip-moment{border-color:rgba(255,184,77,.35);color:var(--amber);background:rgba(255,184,77,.07);}
.chip{
  flex-shrink:0;padding:7px 15px;border-radius:var(--r-pill);font-size:12.5px;font-weight:700;
  border:1px solid var(--card-border);color:var(--text-muted);background:rgba(255,255,255,.03);
  cursor:pointer;
}
.chip.active{border-color:transparent;color:#fff;background:linear-gradient(135deg,var(--wine),#8a2140);}
/* Chaque chip actif reprend la couleur du vin qu'il filtre (mêmes teintes que les bouteilles
   illustrées, cf. BOTTLE_PALETTES dans app.js) plutôt qu'un violet/bordeaux générique partout
   — retour de Damien 21/08/2026. Texte sombre sur les fonds clairs (blanc/pétillant) pour
   rester lisible. */
.chip.active[data-couleur="blanc"]{background:linear-gradient(135deg,#e9e4b8,#c9c17a);color:#332b0f;}
.chip.active[data-couleur="rose"]{background:linear-gradient(135deg,#f0c2ce,#d8748f);color:#3a0d1a;}
.chip.active[data-couleur="petillant"]{background:linear-gradient(135deg,#ecd98a,#c9a227);color:#332600;}
.chip.active[data-readiness="ready"]{background:var(--green);border-color:transparent;color:#04211a;}
.chip.active[data-readiness="wait"]{background:var(--amber);border-color:transparent;color:#332600;}

/* Bandeau résumé en haut de la cave (total, prêtes/à garder, barre de couleur). */
.cave-summary{margin-bottom:18px;}
.cave-bar{display:flex;height:8px;border-radius:var(--r-pill);overflow:hidden;background:var(--bg-alt);margin-bottom:9px;}
.cave-bar-seg{height:100%;}
.cave-summary-stats{font-size:13px;color:var(--text-muted);}

/* Page Profil : tuiles de stats, donut couleur, cartes "région/cépage préféré". */
.profile-stats-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:12px;margin-bottom:8px;}
.profile-tile{
  background:var(--bg-alt);border:1px solid var(--card-border);border-radius:var(--radius);
  padding:16px;text-align:center;box-shadow:var(--e1);
}
.profile-tile-value{font-family:'Newsreader',Georgia,serif;font-weight:600;font-size:28px;color:var(--amber);}
.profile-tile-label{font-size:12px;color:var(--text-muted);margin-top:4px;}
.donut-wrap{display:flex;align-items:center;gap:24px;flex-wrap:wrap;margin-top:14px;}
.donut{width:160px;height:160px;border-radius:50%;position:relative;flex-shrink:0;}
.donut-hole{position:absolute;inset:22px;border-radius:50%;background:var(--card);}
.donut-legend{display:flex;flex-direction:column;gap:9px;}
.donut-legend-item{display:flex;align-items:center;gap:8px;font-size:13.5px;}
.donut-dot{width:11px;height:11px;border-radius:50%;flex-shrink:0;}
.timeline-chart{display:flex;align-items:flex-end;gap:10px;margin-top:14px;overflow-x:auto;padding-bottom:2px;}
.timeline-bar-wrap{display:flex;flex-direction:column;align-items:center;flex-shrink:0;width:34px;}
.timeline-bar-track{height:80px;width:18px;display:flex;align-items:flex-end;}
.timeline-bar{width:100%;min-height:3px;border-radius:var(--r-sm) var(--r-sm) 0 0;background:linear-gradient(180deg,var(--amber),var(--red));}
.timeline-bar-count{font-size:11px;color:var(--text-muted);margin-top:4px;}
.timeline-bar-label{font-size:10.5px;color:var(--text-muted);margin-top:2px;white-space:nowrap;}
/* Carte de bienvenue Profil, compte tout neuf (24/08/2026, idée reprise de betting). */
.welcome-card{
  text-align:center;padding:44px 24px;
  background:linear-gradient(135deg,rgba(194,56,91,.12),rgba(255,184,77,.07));
  border:1px solid rgba(194,56,91,.25);border-radius:var(--radius);box-shadow:var(--e1);
}
.welcome-icon{font-size:36px;margin-bottom:10px;}
.welcome-card h2{font-family:'Newsreader',Georgia,serif;font-weight:600;font-size:20px;margin:0 0 10px;}
.welcome-card p{max-width:420px;margin:0 auto 20px;line-height:1.6;color:var(--text-muted);}
.welcome-card .btn{padding:12px 24px;font-size:14px;}

/* ---------- Succès : badges/défi hebdo (24/08/2026), cf. badges_service.py ---------- */
.badges-ledger{display:flex;align-items:center;gap:16px;padding:16px 18px;margin-bottom:18px;}
.badges-ledger-num{
  font-family:'Newsreader',Georgia,serif;font-weight:600;font-size:24px;color:var(--amber);
  flex-shrink:0;
}
.badges-ledger-num span{font-size:14px;color:var(--text-muted);font-weight:500;}
.badges-ledger-body{flex:1;min-width:140px;}
.badges-ledger-bar{height:8px;border-radius:var(--r-pill);background:var(--bg-alt);overflow:hidden;margin-bottom:7px;}
.badges-ledger-bar>i{display:block;height:100%;border-radius:var(--r-pill);background:linear-gradient(90deg,var(--wine),var(--amber));}
.badges-ledger-note{font-size:12.5px;color:var(--text-muted);}

.hero-medals{display:flex;gap:16px;flex-wrap:wrap;padding:18px;margin-bottom:18px;}
.hero-medals-label{width:100%;font-size:12px;letter-spacing:.04em;text-transform:uppercase;color:var(--text-muted);}

.weekly-ticket{
  display:flex;background:var(--card);border:1px solid var(--card-border);border-radius:var(--radius);
  margin-bottom:24px;overflow:hidden;position:relative;box-shadow:var(--e1);
}
.weekly-ticket::after{content:"";position:absolute;left:110px;top:0;bottom:0;border-left:2px dashed var(--card-border);}
.weekly-ticket-stub{
  width:110px;flex-shrink:0;padding:16px 10px;display:flex;flex-direction:column;
  align-items:center;justify-content:center;gap:6px;
  background:linear-gradient(160deg, rgba(194,56,91,.18), rgba(194,56,91,.06));
}
.weekly-ticket-flame{font-size:26px;line-height:1;}
.weekly-ticket-streak{font-weight:700;font-size:18px;color:var(--amber);}
.weekly-ticket-streak-label{font-size:10px;color:var(--text-muted);text-transform:uppercase;text-align:center;letter-spacing:.04em;}
.weekly-ticket-main{flex:1;padding:16px 18px;}
.weekly-ticket-title{font-family:'Newsreader',Georgia,serif;font-weight:600;font-size:16px;margin-bottom:4px;}
.weekly-ticket-desc{font-size:12.5px;color:var(--text-muted);margin-bottom:10px;}
.weekly-ticket-progress{display:flex;align-items:center;gap:10px;}
.weekly-ticket-bar{flex:1;height:7px;border-radius:var(--r-pill);background:var(--bg-alt);overflow:hidden;}
.weekly-ticket-bar>i{display:block;height:100%;border-radius:var(--r-pill);background:linear-gradient(90deg,var(--wine),var(--amber));}
.weekly-ticket-days{font-size:11.5px;color:var(--text-muted);white-space:nowrap;}
.weekly-ticket-edit{background:none;border:none;color:var(--text-muted);text-decoration:underline;font-size:11.5px;cursor:pointer;padding:0;margin-top:8px;}

/* Bannière d'invitation aux notifications push (24/08/2026, cf. initPushPrompt dans app.js). */
.push-prompt-banner{
  display:flex;align-items:center;justify-content:space-between;gap:16px;flex-wrap:wrap;
  background:linear-gradient(160deg, rgba(255,255,255,.06), rgba(255,255,255,0) 55%), var(--card);
  border:1px solid rgba(255,184,77,.16);border-radius:var(--radius);padding:16px 18px;margin-bottom:20px;
  box-shadow:var(--e1);
}
.push-prompt-text{display:flex;flex-direction:column;gap:2px;font-size:13.5px;color:var(--text-muted);}
.push-prompt-text strong{color:var(--text);font-family:'Newsreader',Georgia,serif;font-size:15px;}
.push-prompt-actions{display:flex;gap:8px;flex-shrink:0;}
.push-prompt-actions .btn{padding:9px 16px;}

/* Carte d'exploration — cœur visuel de l'onglet Succès (cf. ROADMAP.md). Façon carte à
   gratter de voyage : silhouette grisée, zones colorées uniquement là où l'utilisateur a
   déjà goûté un vin (cf. loadCarte/renderCarteMonde/renderCarteFrance dans app.js).
   Refonte du 25/08/2026 (retour de Damien, "faut aller plus bas... puis tout en haut pour
   revenir, pas intuitif") : le détail d'une zone et le retour à la vue France entière sont
   désormais des éléments FLOTTANTS ancrés dans la carte elle-même (bottom-sheet + pastille
   de retour), plus besoin de scroller dans un sens puis dans l'autre. */
.carte-card{padding-bottom:14px;}
.carte-head{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:4px;flex-wrap:wrap;}
.carte-title{font-family:'Newsreader',Georgia,serif;font-weight:600;font-size:17px;margin-right:auto;}
.carte-toggle{display:flex;gap:6px;background:var(--bg-alt);border-radius:var(--r-pill);padding:3px;}
.carte-toggle-btn{
  background:none;border:none;color:var(--text-muted);font-size:12.5px;padding:6px 12px;
  border-radius:var(--r-pill);cursor:pointer;white-space:nowrap;
}
.carte-toggle-btn.active{background:var(--card-border);color:var(--text);}
.carte-stats-line{font-size:12.5px;color:var(--text-muted);margin-bottom:10px;}

/* Passeport des régions (25/08/2026) : remplace la bannière "défi" en une ligne par une
   rangée de sceaux façon carnet de voyage — cliquables, sautent directement dans la carte
   régionale calibrée correspondante (fast-travel, cf. wireCarteChallenge dans app.js). */
.passport-card{
  background:var(--card);border:1px solid var(--card-border);border-radius:var(--radius);
  padding:14px 16px;margin-bottom:14px;box-shadow:var(--e1);
}
.passport-head{display:flex;align-items:baseline;justify-content:space-between;margin-bottom:10px;}
.passport-title{font-family:'Newsreader',Georgia,serif;font-weight:600;font-size:15px;}
.passport-count{font-size:12.5px;color:var(--text-muted);}
.passport-seals{display:flex;gap:12px;overflow-x:auto;padding:2px 2px 6px;-webkit-overflow-scrolling:touch;}
.passport-seal{
  flex:0 0 auto;display:flex;flex-direction:column;align-items:center;gap:6px;
  background:none;border:none;cursor:pointer;width:62px;
}
.passport-seal-icon{
  width:46px;height:46px;border-radius:50%;display:flex;align-items:center;justify-content:center;
  font-size:19px;position:relative;border:1px solid var(--card-border);
  background:var(--bg-alt);color:var(--text-muted);transition:transform .15s;
}
.passport-seal:hover .passport-seal-icon{transform:translateY(-2px);}
.passport-seal.visited .passport-seal-icon{
  background:radial-gradient(circle at 32% 28%,var(--amber),var(--wine) 78%);color:#1a1006;
  border-color:transparent;box-shadow:var(--glow-amber);
}
.passport-seal.target .passport-seal-icon{
  border:2px dashed var(--amber);animation:passportPulse 2.2s ease-in-out infinite;
}
@keyframes passportPulse{0%,100%{box-shadow:0 0 0 0 rgba(255,184,77,.35);}50%{box-shadow:0 0 0 7px rgba(255,184,77,0);}}
.passport-seal-name{font-size:10.5px;color:var(--text-muted);text-align:center;line-height:1.15;}
.passport-seal.visited .passport-seal-name{color:var(--text);}
.passport-tail{margin-top:10px;font-size:12.5px;color:var(--text-muted);}
.passport-tail.done{color:var(--green);}

/* Carte SVG proprement dite — position:relative pour ancrer les éléments flottants
   (pastille de retour + fiche détail en bottom-sheet) directement dedans. */
/* Fond de carte éclairci (25/08/2026, retour de Damien : "le fond de la map c'est trop
   noir") — var(--bg-alt) était trop proche du noir pur, les zones non colorées (fill à très
   faible opacité) devenaient quasi invisibles dessus et la carte ressemblait à un rectangle
   noir avec juste une tache de couleur. */
.carte-body{position:relative;background:#1b2230;border-radius:var(--r-sm);overflow:hidden;transition:opacity .16s ease;}
.carte-body.fading{opacity:0;}
.carte-svg{display:block;width:100%;height:auto;}
.carte-zone{stroke:#1b2230;stroke-width:.6;transition:fill .15s;}
.carte-zone.visited{cursor:pointer;stroke:var(--bg);}
.carte-zone.visited:hover{filter:brightness(1.2);}
/* Vignette "voir sur la carte" (25/08/2026) : région ciblée en surbrillance pleine, pas
   cliquable (pas de heatmap dégustation ici, juste "c'est ici que ce vin est né"). */
.carte-zone.highlight{stroke:var(--amber);stroke-width:1.4;}
.carte-contour{stroke:#3a4456;stroke-width:1.2;}

.carte-float-back{
  position:absolute;top:10px;left:10px;z-index:5;
  background:rgba(11,14,20,.75);backdrop-filter:blur(6px);border:1px solid rgba(255,255,255,.16);
  color:var(--text);font-size:12px;padding:7px 13px;border-radius:var(--r-pill);cursor:pointer;
}
.carte-float-back:hover{border-color:var(--amber);color:var(--amber);}

/* Fiche détail en bottom-sheet, flottante par-dessus la carte (plus un bloc sous la carte
   qu'il fallait scroller pour atteindre) — glisse depuis le bas au clic sur une zone. */
.carte-detail{
  position:absolute;left:0;right:0;bottom:0;z-index:6;
  background:rgba(16,20,29,.97);backdrop-filter:blur(10px);
  border-top:1px solid var(--card-border);border-radius:var(--radius) var(--radius) 0 0;
  padding:16px 18px 18px;max-height:65%;overflow-y:auto;
  transform:translateY(102%);transition:transform .3s cubic-bezier(.22,.9,.32,1);
}
.carte-detail.open{transform:translateY(0);}
.carte-detail-close{
  position:absolute;top:10px;right:12px;background:none;border:none;color:var(--text-muted);
  font-size:20px;line-height:1;cursor:pointer;padding:4px;
}
.carte-detail-close:hover{color:var(--text);}
.carte-detail-title{font-family:'Newsreader',Georgia,serif;font-weight:600;font-size:14px;margin:0 22px 6px 0;color:var(--amber);}
.carte-detail-list{margin:0;padding-left:18px;font-size:13px;color:var(--text-muted);}
.carte-detail-list li{margin-bottom:3px;}
.carte-detail-zoom{
  margin-top:10px;background:none;border:1px solid var(--card-border);color:var(--amber);
  font-size:12.5px;padding:6px 12px;border-radius:var(--r-sm);cursor:pointer;
}
.carte-detail-zoom:hover{border-color:var(--amber);}

.badge-shelf{margin-bottom:28px;}
.badge-shelf-head{display:flex;align-items:baseline;gap:10px;margin-bottom:12px;padding-bottom:8px;border-bottom:1px solid var(--card-border);}
.badge-shelf-title{font-family:'Newsreader',Georgia,serif;font-weight:600;font-size:16px;}
.badge-shelf-count{font-size:12px;color:var(--text-muted);margin-left:auto;}

.medal-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(130px,1fr));gap:14px;}
.medal{
  background:var(--card);border:1px solid var(--card-border);border-radius:var(--radius);
  padding:16px 10px 12px;text-align:center;display:flex;flex-direction:column;align-items:center;gap:8px;
  cursor:pointer;box-shadow:var(--e1);
}
.medal-coin{
  width:52px;height:52px;border-radius:50%;display:flex;align-items:center;justify-content:center;
  font-size:23px;flex-shrink:0;position:relative;
}
.medal-coin::before{content:"";position:absolute;inset:-3px;border-radius:50%;border:1px dashed rgba(255,255,255,.14);}
.medal[data-tier="bronze"] .medal-coin{background:radial-gradient(circle at 32% 28%,#c98a4c,#7a4a22 72%);box-shadow:0 2px 8px rgba(154,90,40,.35);}
.medal[data-tier="argent"] .medal-coin{background:radial-gradient(circle at 32% 28%,#d7dee8,#8891a0 72%);box-shadow:0 2px 8px rgba(150,160,175,.3);}
.medal[data-tier="or"] .medal-coin{background:radial-gradient(circle at 32% 28%,#ffe08a,#c99a2e 72%);box-shadow:0 2px 10px rgba(255,190,60,.35);}
.medal[data-tier="diamant"] .medal-coin{background:radial-gradient(circle at 32% 28%,#bfe9ff,#6fb8e0 72%);box-shadow:0 2px 10px rgba(120,200,240,.35);}
.medal.locked .medal-coin{background:var(--bg-alt);filter:grayscale(1);opacity:.5;}
.medal.locked{opacity:.7;}
.medal-name{font-family:'Newsreader',Georgia,serif;font-weight:600;font-size:12.5px;line-height:1.25;}
.medal-crit{font-size:10.5px;color:var(--text-muted);line-height:1.35;min-height:2.4em;}
.medal-tag{font-size:9.5px;letter-spacing:.04em;text-transform:uppercase;padding:2px 7px;border-radius:var(--r-pill);font-weight:600;background:var(--bg-alt);color:var(--text-muted);}
.medal.unlocked .medal-tag{color:var(--amber);background:rgba(255,184,77,.14);}
.tier-dots{display:flex;gap:4px;}
.tier-dots i{width:5px;height:5px;border-radius:50%;background:var(--card-border);display:block;}
.tier-dots i.on{background:var(--amber);}

/* Écran plein écran de déblocage de badge (24/08/2026, demande de Damien) — se ferme au clic
   n'importe où dessus (cf. showBadgeUnlock/dismissBadgeUnlock dans app.js). */
.badge-unlock-overlay{
  position:fixed;inset:0;background:rgba(11,14,20,.88);z-index:300;cursor:pointer;
  display:flex;align-items:center;justify-content:center;padding:24px;
  animation:badgeUnlockFadeIn .25s ease;
}
@keyframes badgeUnlockFadeIn{from{opacity:0;}to{opacity:1;}}
.badge-unlock-card{
  display:flex;flex-direction:column;align-items:center;text-align:center;max-width:340px;
  animation:badgeUnlockCardIn .5s cubic-bezier(.2,1.4,.4,1);
}
@keyframes badgeUnlockCardIn{from{opacity:0;transform:translateY(18px) scale(.94);}to{opacity:1;transform:none;}}
.badge-unlock-kicker{
  font-size:12.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--amber);
  font-weight:700;margin-bottom:22px;
}
.badge-unlock-coin{
  width:140px;height:140px;border-radius:50%;display:flex;align-items:center;justify-content:center;
  font-size:64px;position:relative;margin-bottom:22px;
  animation:badgeUnlockCoinIn .7s cubic-bezier(.2,1.6,.3,1), badgeUnlockPulse 2.4s ease-in-out .7s infinite;
}
.badge-unlock-coin::before{
  content:"";position:absolute;inset:-8px;border-radius:50%;border:2px dashed rgba(255,255,255,.22);
  animation:badgeUnlockSpin 16s linear infinite;
}
@keyframes badgeUnlockCoinIn{0%{transform:scale(0) rotate(-30deg);opacity:0;}70%{transform:scale(1.15) rotate(8deg);opacity:1;}100%{transform:scale(1) rotate(0);}}
@keyframes badgeUnlockPulse{0%,100%{box-shadow:0 0 0 0 rgba(255,184,77,.35);}50%{box-shadow:0 0 0 18px rgba(255,184,77,0);}}
@keyframes badgeUnlockSpin{from{transform:rotate(0);}to{transform:rotate(360deg);}}
.badge-unlock-coin[data-tier="bronze"]{background:radial-gradient(circle at 32% 28%,#c98a4c,#7a4a22 72%);}
.badge-unlock-coin[data-tier="argent"]{background:radial-gradient(circle at 32% 28%,#d7dee8,#8891a0 72%);}
.badge-unlock-coin[data-tier="or"]{background:radial-gradient(circle at 32% 28%,#ffe08a,#c99a2e 72%);}
.badge-unlock-coin[data-tier="diamant"]{background:radial-gradient(circle at 32% 28%,#bfe9ff,#6fb8e0 72%);}
.badge-unlock-name{font-family:'Newsreader',Georgia,serif;font-weight:600;font-size:26px;color:var(--text);margin-bottom:10px;}
.badge-unlock-desc{font-size:14.5px;color:var(--text-muted);line-height:1.5;}
.badge-unlock-hint{margin-top:28px;font-size:12px;color:var(--text-muted);opacity:.7;}

/* Wrapped annuel (26/08/2026, façon Spotify Wrapped) — direction "minimal grande classe"
   choisie après comparaison de 4 pistes. z-index au-dessus de tout, y compris
   .badge-unlock-overlay (un badge peut se débloquer alors qu'on regarde son Wrapped). */
.wrapped-overlay{
  position:fixed;inset:0;z-index:400;background:radial-gradient(circle at 20% -10%, #33161f 0%, var(--bg) 48%) fixed, var(--bg);
  color:var(--text);display:flex;flex-direction:column;
  padding:calc(env(safe-area-inset-top,0px) + 14px) 22px calc(env(safe-area-inset-bottom,0px) + 26px);
  animation:badgeUnlockFadeIn .3s ease;
  /* 07/09/2026 : le glissé horizontal ajouté le jour même faisait sélectionner le texte au
     passage — d'où un aplat gris de surbrillance à chaque changement d'écran (retour de
     Damien : « comme si je sélectionnais la page »). Un Wrapped n'a rien de sélectionnable :
     on coupe la sélection ET la pastille de tap d'Android, et on annonce au navigateur que les
     gestes horizontaux nous appartiennent (`touch-action`) pour qu'il ne tente ni sélection ni
     défilement pendant le glissé. */
  user-select:none;-webkit-user-select:none;
  -webkit-tap-highlight-color:transparent;
  touch-action:pan-y;
}
.wrapped-overlay ::selection{background:transparent;}

/* Halo d'ambiance (07/09/2026) : une lueur de la couleur de l'écran en cours, posée du côté où
   son contenu s'est placé (cf. WRAPPED_POSES dans app.js). C'est ce qui fait qu'on ne regarde
   pas le même écran toute la séquence — la lumière se déplace avec le texte. Derrière les
   bulles et les diapositives, jamais cliquable. */
/* Le fond de l'écran en cours (07/09/2026, 2e passe) : une vraie nappe de sa couleur, pas la
   lueur à 17% du premier jet — sur capture, elle ne se voyait pas et tous les écrans se
   ressemblaient. Deux sources : une nappe large du côté de la composition, et une seconde plus
   serrée à l'opposé, pour que le fond ne soit jamais un aplat plat. */
.wrapped-halo{
  position:absolute;inset:0;z-index:0;pointer-events:none;
  background:
    radial-gradient(120% 70% at var(--halo-pos,50% 8%), var(--halo-color,#ffb84d) 0%, transparent 62%),
    radial-gradient(80% 50% at var(--halo-pos2,50% 100%), var(--halo-color,#ffb84d) 0%, transparent 70%);
  opacity:.34;
}
/* Une custom property dans un `radial-gradient` ne s'anime pas (il faudrait `@property`) : on
   ne cherche donc pas à faire glisser le halo, on le refait apparaître en fondu à chaque écran.
   Rejoué en retirant/reposant la classe après un reflow, comme le flash. */
.wrapped-halo.entre{animation:wrappedHaloFondu 1.1s ease-out;}
@keyframes wrappedHaloFondu{from{opacity:0;}to{opacity:.34;}}
/* Bulles ambiantes (26/08/2026, retour de Damien : "être mega inventif", palette recalée sur
   la vraie identité de l'appli plutôt que sur mon doré inventé) — montent en continu derrière
   les diapositives, jamais réinjectées au changement d'écran (posées une fois dans le HTML
   statique, cf. templates/index.html), pointer-events:none pour ne jamais intercepter le tap
   précédent/suivant. 8 tailles/vitesses/délais fixes plutôt qu'un vrai hasard JS : suffisant
   pour ne pas paraître mécanique, plus simple à raisonner. */
.wrapped-bubbles{position:absolute;inset:0;overflow:hidden;z-index:1;pointer-events:none;}
.wrapped-bubble{
  position:absolute;bottom:-24px;border-radius:50%;
  background:radial-gradient(circle at 35% 30%, rgba(255,255,255,.4), rgba(255,184,77,.16) 60%, transparent 76%);
  animation:wrappedBubbleRise linear infinite;
}
@keyframes wrappedBubbleRise{
  0%{transform:translateY(0) translateX(0);opacity:0;}
  10%{opacity:.6;}
  85%{opacity:.35;}
  100%{transform:translateY(-115vh) translateX(var(--drift,12px));opacity:0;}
}
.wrapped-bubble:nth-child(1){left:8%;width:10px;height:10px;animation-duration:9s;animation-delay:0s;--drift:14px;}
.wrapped-bubble:nth-child(2){left:20%;width:6px;height:6px;animation-duration:7s;animation-delay:1.4s;--drift:-10px;}
.wrapped-bubble:nth-child(3){left:34%;width:13px;height:13px;animation-duration:11s;animation-delay:.6s;--drift:8px;}
.wrapped-bubble:nth-child(4){left:48%;width:7px;height:7px;animation-duration:8s;animation-delay:2.5s;--drift:-16px;}
.wrapped-bubble:nth-child(5){left:62%;width:11px;height:11px;animation-duration:10s;animation-delay:.2s;--drift:10px;}
.wrapped-bubble:nth-child(6){left:74%;width:5px;height:5px;animation-duration:6.5s;animation-delay:3.2s;--drift:-8px;}
.wrapped-bubble:nth-child(7){left:86%;width:9px;height:9px;animation-duration:9.5s;animation-delay:1.8s;--drift:12px;}
.wrapped-bubble:nth-child(8){left:94%;width:6px;height:6px;animation-duration:7.5s;animation-delay:2.9s;--drift:-6px;}

/* Flash coloré au changement d'écran (26/08/2026, retour de Damien : "d'une slide à l'autre
   ça ne se ressemble pas assez") — un éclair de couleur (accent propre à l'écran qui arrive,
   cf. fireWrappedFlash dans app.js) marque chaque transition, comme un flash d'appareil
   photo, plutôt qu'un simple fondu. mix-blend-mode:screen : éclaircit sans jamais opacifier
   en noir/gris (safe sur fond sombre). */
.wrapped-flash{
  position:absolute;inset:0;z-index:6;pointer-events:none;opacity:0;
  background:var(--flash-color, var(--amber));mix-blend-mode:screen;
}
.wrapped-flash.firing{animation:wrappedFlash .4s ease-out;}
@keyframes wrappedFlash{0%{opacity:.6;}100%{opacity:0;}}

.wrapped-progress{display:flex;gap:5px;position:relative;z-index:2;}
.wrapped-progress-seg{flex:1;height:2px;background:rgba(255,184,77,.22);border-radius:2px;overflow:hidden;}
.wrapped-progress-seg-fill{height:100%;width:0%;background:linear-gradient(90deg,var(--amber),var(--wine));}
.wrapped-progress-seg.done .wrapped-progress-seg-fill{width:100%;}
/* L'écran en cours part de 0 et se remplit sur sa durée réelle, posée en style inline par
   armWrappedAutoAdvance (07/09/2026) — avant, la règle mettait 100% tout de suite et la barre
   ne disait rien du temps restant. */
.wrapped-progress-seg.active .wrapped-progress-seg-fill{width:0%;transition:width linear;}
.wrapped-close{
  position:absolute;top:calc(env(safe-area-inset-top,0px) + 12px);right:18px;z-index:3;
  width:30px;height:30px;border:none;background:transparent;color:#5b6272;font-size:15px;
  font-weight:300;cursor:pointer;
}
.wrapped-slide{
  /* space-evenly, pas center (07/09/2026, 3e passe) : centrer un contenu court laissait
     l'ecran a moitie vide — « ca prend qu'un petit espace, ca fait comme si j'etais en
     multi-ecran ». Les temps de l'ecran (kicker, visuel, chiffre, proverbe) se repartissent
     desormais sur toute la hauteur. space-evenly et pas space-between : les ecrans a deux
     temps seulement (intro, cloture) seraient sinon colles aux bords. */
  flex:1;display:flex;flex-direction:column;align-items:center;justify-content:space-evenly;
  gap:10px;padding:3vh 0;
  text-align:center;position:relative;z-index:2;cursor:pointer;
  animation-duration:.4s;animation-timing-function:cubic-bezier(.16,.9,.3,1);animation-fill-mode:backwards;
}
/* Mouvement distinct par type d'écran (26/08/2026, retour de Damien : "la c'est le même
   écran à chaque fois" — chaque famille de diapositive a désormais sa propre entrée, pas
   un unique fondu générique partout). Rôle du conteneur .wrapped-slide : juste METTRE
   L'ÉCRAN en place (rapide, discret) — le vrai spectacle vient des .wrapped-beat à
   l'intérieur (cf. plus bas), qui arrivent l'un après l'autre au lieu de tout d'un coup
   ("kicker → visuel → pause → chiffre qui arrive fort → proverbe", retour de Damien : "sur
   Spotify y'a tout un truc avant d'arriver au résultat"). Nom d'animation posé sur .kind-*
   plutôt que directement sur .wrapped-slide pour pouvoir la faire rejouer à chaque
   diapositive (cf. renderWrappedSlide : la classe est retirée puis reposée après un reflow
   forcé). */
.wrapped-slide.kind-intro,.wrapped-slide.kind-closing{animation-name:wrappedFadeIn;}
.wrapped-slide.kind-stat{animation-name:wrappedFadeIn;}
.wrapped-slide.kind-quiz{animation-name:wrappedSlideUp;}
.wrapped-slide.kind-special{animation-name:wrappedTiltIn;}
/* Entrées alternées (07/09/2026, 2e passe) : posées par renderWrappedSlide en plus de la
   famille d'écran, pour que deux écrans qui se suivent ne bougent pas pareil. Elles portent sur
   le conteneur, les `wrapped-beat` gardent leur rythme intérieur par-dessus. */
.wrapped-slide.entree-zoom{animation-name:wrappedZoomIn;animation-duration:.55s;}
.wrapped-slide.entree-cote{animation-name:wrappedFromSide;animation-duration:.5s;}
.wrapped-slide.entree-monte{animation-name:wrappedSlideUp;animation-duration:.5s;}
@keyframes wrappedZoomIn{from{opacity:0;transform:scale(1.14);}to{opacity:1;transform:none;}}
@keyframes wrappedFromSide{from{opacity:0;transform:translateX(26px);}to{opacity:1;transform:none;}}
@keyframes wrappedFadeIn{from{opacity:0;}to{opacity:1;}}
@keyframes wrappedSlideUp{from{opacity:0;transform:translateY(18px);}to{opacity:1;transform:none;}}
@keyframes wrappedTiltIn{from{opacity:0;transform:rotate(-2deg);}to{opacity:1;transform:none;}}

/* Compositions (07/09/2026, DEUXIÈME passe — la première était mauvaise et Damien l'a dit :
   « c'est juste le même truc mais en bas à gauche, ultra bizarre, on dirait un bug »).
   Il avait raison : pousser un bloc étroit dans un coin d'un écran vide à 80% ne fait pas une
   composition, ça fait un alignement raté. Vu sur captures avant/après, cette fois.

   Ce qui fait une page, c'est l'inverse : le contenu prend TOUTE la largeur, et c'est
   l'alignement du texte (gauche/centre) plus l'ancrage vertical qui changent — la mise en page
   d'un magazine, pas un bloc qui se déplace. Le HTML reste le même. */
.wrapped-slide > *{width:100%;max-width:min(94vw,420px);}

/* TROISIÈME passe (07/09/2026). Les deux premières déplaçaient le bloc : d'abord dans un
   coin, puis ancré en haut ou en bas et aligné à gauche. Damien, deux fois de suite :
   « ultra bizarre », puis « ça prend qu'un petit espace, ça fait comme si j'étais en
   multi-écran ». Il ne demandait pas de déplacer le contenu — il demandait l'inverse :
   « que ça prenne tout l'espace quand même mais différent ».

   Donc plus aucun ancrage ni alignement variable : tout est centré, plein cadre, réparti
   sur la hauteur. Ce qui change d'un écran à l'autre, c'est l'ÉCHELLE (le chiffre et le
   kicker enflent ou se resserrent) et l'ENTRÉE (zoom / côté / montée) — deux variations qui
   n'ouvrent jamais de vide. Vérifié sur captures. */
.wrapped-slide.amp-large .wrapped-num{font-size:min(58vw,268px);}
.wrapped-slide.amp-large .wrapped-kicker{font-size:13px;letter-spacing:.38em;}
.wrapped-slide.amp-large .wrapped-unit{font-size:23px;}
.wrapped-slide.amp-serre .wrapped-num{font-size:min(36vw,164px);}
.wrapped-slide.amp-serre .wrapped-kicker{font-size:10px;letter-spacing:.22em;}
.wrapped-slide.amp-serre .wrapped-lead{font-size:17px;max-width:320px;}

/* Les "temps" d'un écran (26/08/2026) : chaque bloc de contenu arrive l'un après l'autre,
   délai posé en style inline par app.js (cf. wrappedStatSlideHtml et les rendus des écrans
   spéciaux) — construit le suspense au lieu de tout balancer d'un coup. .wrapped-num-beat
   a une arrivée plus marquée (rebond) que les autres, réservée au "résultat" qui doit
   marquer le coup après la mise en place. */
.wrapped-beat{opacity:0;animation:wrappedBeatFade .55s cubic-bezier(.2,1,.3,1) both;}
@keyframes wrappedBeatFade{from{opacity:0;transform:translateY(10px);}to{opacity:1;transform:none;}}
.wrapped-num-beat{animation-name:wrappedBeatPop;}
@keyframes wrappedBeatPop{0%{opacity:0;transform:scale(.55);}65%{opacity:1;transform:scale(1.08);}100%{opacity:1;transform:scale(1);}}

.wrapped-kicker{font-size:11px;font-weight:500;letter-spacing:.3em;text-transform:uppercase;color:var(--text-muted);line-height:1.6;}
.wrapped-rule{width:22px;height:1px;background:var(--amber);margin:26px auto;animation:wrappedRuleDraw .6s ease-out backwards;animation-delay:.15s;}
@keyframes wrappedRuleDraw{from{width:0;opacity:0;}to{width:22px;opacity:1;}}
/* margin-inline:auto indispensable : ces blocs ont une largeur propre plus etroite que le
   conteneur (420px). Sans marge auto ils se collent a gauche, meme avec text-align:center —
   c'est ce qui faisait dire « la citation en bas n'est plus centrée avec le reste ». */
.wrapped-lead{font-family:'Newsreader',Georgia,serif;font-weight:600;font-size:15px;color:var(--text-muted);max-width:280px;line-height:1.45;margin-inline:auto;}
.wrapped-num{
  /* 07/09/2026 : 92px maxi, c'était un chiffre de tableau de bord au milieu d'un écran vide.
     Le chiffre EST le sujet de l'écran — il doit le remplir. */
  font-family:'Newsreader',Georgia,serif;font-weight:600;font-size:min(44vw,200px);line-height:.92;
  margin:4px 0 2px;font-variant-numeric:tabular-nums;letter-spacing:-.02em;
  background:linear-gradient(110deg,var(--amber) 38%,#ffe9c2 50%,var(--wine) 62%);
  background-size:260% 100%;-webkit-background-clip:text;background-clip:text;color:transparent;
  animation:wrappedShimmer 4.5s linear infinite;
}
@keyframes wrappedShimmer{0%{background-position:180% 0;}100%{background-position:-80% 0;}}

/* ---------- Écrans ajoutés le 07/09/2026 (ROADMAP section 8) ---------- */

/* « Le voyage » : les régions arrivent une par une, comme des tampons sur un passeport. Le
   délai est porté en style inline par app.js (une valeur par étape). */
.wrapped-etapes{display:flex;flex-wrap:wrap;gap:7px;justify-content:center;max-width:300px;margin:18px auto 4px;}
.wrapped-etape{
  font-size:12.5px;padding:5px 11px;border-radius:999px;
  border:1px solid rgba(255,184,77,.4);color:var(--amber);white-space:nowrap;
  opacity:0;animation:wrappedTampon .5s cubic-bezier(.2,1.4,.4,1) both;
}
@keyframes wrappedTampon{
  0%{opacity:0;transform:scale(1.5) rotate(-6deg);}
  70%{opacity:1;transform:scale(.96) rotate(1deg);}
  100%{opacity:1;transform:none;}
}

/* « Ton palais a bougé » : le point glisse de la valeur du début à celle de la fin, le point
   de départ reste visible en creux — c'est le déplacement qui est le sujet, pas la valeur. */
.wrapped-axe{width:min(78vw,290px);margin:22px auto 6px;}
.wrapped-axe-nom{font-size:11px;letter-spacing:.28em;text-transform:uppercase;color:var(--text-muted);margin-bottom:14px;}
.wrapped-axe-piste{position:relative;height:3px;border-radius:3px;background:rgba(255,184,77,.18);}
.wrapped-axe-point{position:absolute;top:50%;width:13px;height:13px;border-radius:50%;transform:translate(-50%,-50%);}
.wrapped-axe-point.avant{background:transparent;border:1.5px solid rgba(255,184,77,.55);}
.wrapped-axe-point.apres{
  background:var(--amber);box-shadow:0 0 14px rgba(255,184,77,.65);
  animation:wrappedAxeGlisse 1.5s cubic-bezier(.5,0,.2,1) both;animation-delay:.75s;
}
@keyframes wrappedAxeGlisse{from{left:var(--de);}to{left:var(--a);}}
.wrapped-axe-bornes{display:flex;justify-content:space-between;margin-top:12px;font-size:10.5px;letter-spacing:.16em;text-transform:uppercase;color:var(--text-muted);}

/* Le palmarès : trois lignes, une par superlatif. */
.wrapped-podium-ligne{display:flex;gap:12px;align-items:flex-start;text-align:left;max-width:290px;margin:9px auto;}
.wrapped-podium-emoji{font-size:20px;line-height:1.2;}
.wrapped-podium-titre{display:block;font-size:10.5px;letter-spacing:.2em;text-transform:uppercase;color:var(--text-muted);}
.wrapped-podium-vin{display:block;font-family:'Newsreader',Georgia,serif;font-size:15px;color:var(--text);margin-top:2px;line-height:1.3;}
.wrapped-podium-detail{display:block;font-size:12px;color:var(--amber);margin-top:2px;}

/* La dormeuse et le portrait. */
.wrapped-dodo{font-size:40px;animation:wrappedDodo 2.6s ease-in-out infinite;}
@keyframes wrappedDodo{0%,100%{transform:translateY(0) rotate(-4deg);}50%{transform:translateY(-7px) rotate(4deg);}}
.wrapped-plume{font-size:32px;animation:wrappedEcrit 1.6s ease-in-out infinite;}
@keyframes wrappedEcrit{0%,100%{transform:rotate(-8deg) translateX(-3px);}50%{transform:rotate(4deg) translateX(3px);}}
.wrapped-portrait{
  font-family:'Newsreader',Georgia,serif;font-size:19px;line-height:1.5;color:var(--text);
  max-width:300px;margin:8px auto;
}
/* Le curseur de la machine à écrire, tant que la phrase n'est pas finie. */
.wrapped-portrait #wrapped-portrait-texte::after{
  content:'';display:inline-block;width:2px;height:.95em;margin-left:2px;vertical-align:-2px;
  background:var(--amber);animation:wrappedCurseur .9s steps(1) infinite;
}
@keyframes wrappedCurseur{0%,49%{opacity:1;}50%,100%{opacity:0;}}

/* Pause (appui maintenu, geste des stories) : tout s'estompe sauf l'écran lui-même, pour dire
   clairement « c'est en pause » sans rien recouvrir. */
.wrapped-overlay.en-pause .wrapped-progress,
.wrapped-overlay.en-pause .wrapped-close{opacity:.25;transition:opacity .2s;}
.wrapped-overlay.en-pause .wrapped-slide{transform:scale(.985);transition:transform .25s;}

@media (prefers-reduced-motion: reduce){
  .wrapped-etape,.wrapped-axe-point.apres,.wrapped-dodo,.wrapped-plume{animation:none;opacity:1;}
  .wrapped-axe-point.apres{left:var(--a);}
}
.wrapped-unit{font-family:'Newsreader',Georgia,serif;font-weight:600;font-size:19px;color:var(--text);}
.wrapped-proverb{
  font-family:'Newsreader',Georgia,serif;font-style:italic;font-size:13.5px;color:rgba(231,235,243,.6);
  max-width:280px;line-height:1.55;margin:22px auto 0;
}
.wrapped-foot{position:relative;z-index:2;text-align:center;font-size:10.5px;color:#5b6272;letter-spacing:.06em;margin-top:10px;}
.wrapped-reveal-btn{
  margin-top:26px;background:transparent;border:1px solid rgba(255,184,77,.5);color:var(--amber);
  border-radius:20px;padding:9px 22px;font-size:13px;cursor:pointer;font-family:inherit;
}
.wrapped-tastant{
  display:flex;align-items:baseline;gap:8px;justify-content:center;
}
/* 07/09/2026 : la note EST le résultat de cet écran, elle avait la taille d'une légende. */
.wrapped-note{
  font-family:'Newsreader',Georgia,serif;font-weight:600;font-size:min(20vw,74px);line-height:1;
  color:var(--amber);letter-spacing:-.01em;
}
.wrapped-tastant{display:flex;align-items:baseline;gap:6px;justify-content:center;margin:6px 0 2px;}

/* Quiz à 4 choix (26/08/2026, demande de Damien — "une proposition de 4 réponses", pas
   juste une révélation textuelle). Bonne réponse en ambre + surbrillance au clic, mauvaise
   réponse (si choisie) grisée/barrée ; les deux autres s'estompent — cf. wireWrappedQuiz
   dans app.js. */
.wrapped-quiz-grid{
  display:grid;grid-template-columns:1fr 1fr;gap:10px;margin:22px auto 0;width:100%;max-width:300px;
}
.wrapped-quiz-btn{
  background:rgba(255,184,77,.06);border:1px solid rgba(255,184,77,.3);color:var(--text);
  border-radius:12px;padding:12px 10px;font-size:13px;font-family:inherit;cursor:pointer;
  transition:opacity .3s ease, transform .2s ease, border-color .3s ease, background .3s ease;
}
.wrapped-quiz-btn.correct{background:rgba(255,184,77,.22);border-color:var(--amber);color:#ffe9c2;font-weight:600;transform:scale(1.04);}
/* Réponse cliquée = la bonne du premier coup (26/08/2026, "un truc cool qui se voit") :
   rebond plus marqué + halo, en plus du simple surlignage ci-dessus (qui s'affiche même en
   cas d'erreur, sur la bonne réponse). */
.wrapped-quiz-btn.guessed-right{
  animation:wrappedQuizWin .6s cubic-bezier(.2,1.6,.3,1);
  box-shadow:0 0 0 3px rgba(255,184,77,.5), 0 10px 22px -6px rgba(255,184,77,.6);
}
@keyframes wrappedQuizWin{0%{transform:scale(1);}45%{transform:scale(1.2) rotate(-2deg);}72%{transform:scale(.97) rotate(1deg);}100%{transform:scale(1.04) rotate(0);}}
.wrapped-quiz-win-badge{
  font-family:'Newsreader',Georgia,serif;font-weight:600;font-size:14px;color:var(--amber);
  margin-bottom:8px;opacity:0;animation:wrappedBeatPop .5s cubic-bezier(.2,1.4,.4,1) .05s both;
}
.wrapped-quiz-btn.wrong{background:rgba(255,107,122,.1);border-color:rgba(255,107,122,.4);color:var(--text-muted);text-decoration:line-through;}
.wrapped-quiz-btn.faded{opacity:.35;}
.wrapped-quiz-feedback{
  margin-top:16px;font-family:'Newsreader',Georgia,serif;font-style:italic;font-size:13px;color:var(--amber);
  opacity:0;animation:wrappedFadeIn .4s ease forwards;
}

/* Illustrations dessinées à la main (26/08/2026, retour de Damien : "des formes, des
   animations, des bouteilles qui trinquent, des verres" — pas de librairie externe, SVG +
   CSS pur, même esprit que le reste de l'appli, cf. bottleVisualHtml dans app.js). */

/* Verre à vin en ligne (contour or) — réutilisé vide pour le toast (intro/fin) et rempli
   (liquid coloré, cf. wrappedGlassSvg/animateWrappedNumber) pour les écrans de stat/portrait
   couleur : le remplissage progresse EN MÊME TEMPS que le compteur, pas un simple décor. */
.wrapped-glass-svg{width:78px;height:auto;overflow:visible;filter:drop-shadow(0 6px 14px rgba(255,184,77,.18));}

/* Toast (écran de clôture) : deux verres glissent l'un vers l'autre, se cognent (petit éclat
   ambre au point de contact), puis oscillent doucement — bouclé en continu tant que l'écran
   reste affiché. */
.wrapped-toast-wrap{position:relative;width:150px;height:100px;margin:6px auto 14px;}
.wrapped-toast-glass{position:absolute;top:6px;animation-duration:1.1s;animation-timing-function:cubic-bezier(.2,.8,.3,1);animation-fill-mode:both;}
.wrapped-toast-left{left:6px;transform-origin:50% 90%;animation-name:wrappedToastInLeft;}
.wrapped-toast-right{right:6px;transform-origin:50% 90%;animation-name:wrappedToastInRight;}
.wrapped-toast-left .wrapped-glass-svg{animation:wrappedToastSwayLeft 3.2s ease-in-out 1.1s infinite;}
.wrapped-toast-right .wrapped-glass-svg{animation:wrappedToastSwayRight 3.2s ease-in-out 1.1s infinite;}
@keyframes wrappedToastInLeft{0%{transform:translate(-70px,10px) rotate(-38deg);opacity:0;}70%{transform:translate(4px,0) rotate(-16deg);opacity:1;}100%{transform:translate(0,0) rotate(-13deg);opacity:1;}}
@keyframes wrappedToastInRight{0%{transform:translate(70px,10px) rotate(38deg);opacity:0;}70%{transform:translate(-4px,0) rotate(16deg);opacity:1;}100%{transform:translate(0,0) rotate(13deg);opacity:1;}}
@keyframes wrappedToastSwayLeft{0%,100%{transform:rotate(0deg);}50%{transform:rotate(-4deg);}}
@keyframes wrappedToastSwayRight{0%,100%{transform:rotate(0deg);}50%{transform:rotate(4deg);}}
.wrapped-toast-spark{
  position:absolute;left:50%;top:38%;width:10px;height:10px;margin:-5px 0 0 -5px;border-radius:50%;
  background:radial-gradient(circle,#fff3d0,var(--amber) 55%,transparent 72%);opacity:0;
  animation:wrappedSpark .6s ease-out .78s forwards;
}
@keyframes wrappedSpark{0%{opacity:0;transform:scale(.2);}40%{opacity:1;transform:scale(2.6);}100%{opacity:0;transform:scale(3.6);}}

/* Bouchon qui saute (écran d'intro, 26/08/2026 — remplace le toast générique pour l'ouverture,
   plus identifiable "Vinifique" qu'une simple paire de verres). Bouteille au vrai
   dégradé rouge vin (mêmes teintes que BOTTLE_PALETTES.rouge dans app.js), bouchon qui
   s'éjecte en tournant avec un éclat ambre au moment où il quitte le goulot. */
.wrapped-cork-wrap{position:relative;width:78px;height:150px;margin:2px auto 12px;}
.wrapped-cork-bottle{position:absolute;bottom:0;left:0;width:78px;height:auto;filter:drop-shadow(0 12px 20px rgba(0,0,0,.45));}
.wrapped-cork{
  position:absolute;top:33px;left:33px;width:12px;height:15px;border-radius:2px 2px 1px 1px;
  background:linear-gradient(180deg,#e8c78a,#b98a4a);
  animation:wrappedCorkFly 1.3s cubic-bezier(.22,.85,.35,1) .3s both;
}
@keyframes wrappedCorkFly{
  0%{transform:translate(0,0) rotate(0deg);opacity:1;}
  55%{transform:translate(7px,-56px) rotate(75deg);opacity:1;}
  100%{transform:translate(11px,-70px) rotate(115deg);opacity:0;}
}
.wrapped-cork-burst{
  position:absolute;top:28px;left:40px;width:6px;height:6px;margin:-3px 0 0 -3px;border-radius:50%;
  background:radial-gradient(circle,#fff3d0,var(--amber) 55%,transparent 72%);opacity:0;
  animation:wrappedSpark .55s ease-out .55s forwards;
}

/* Rangée de bouteilles — devenue une GRILLE (26/08/2026, retour de Damien : "d'une slide à
   l'autre ça ne se ressemble pas assez") : plusieurs rangées façon étagère de cave plutôt
   qu'une seule ligne, composition différente de tous les autres écrans de stat. */
.wrapped-bottle-row{
  display:grid;grid-template-columns:repeat(5,1fr);gap:12px 10px;
  margin:10px auto 4px;max-width:210px;justify-items:center;
}
.wrapped-bottle-icon{width:22px;height:auto;opacity:0;animation:wrappedBottlePop .5s cubic-bezier(.2,1.4,.4,1) both;}
@keyframes wrappedBottlePop{0%{opacity:0;transform:translateY(10px) scale(.6);}100%{opacity:1;transform:none;}}
.wrapped-bottle-extra{
  grid-column:span 1;align-self:center;
  font-family:'Newsreader',Georgia,serif;font-weight:600;font-size:15px;color:var(--amber);
  opacity:0;animation:wrappedFadeIn .5s ease both;
}

/* Médaille des badges — même langage visuel que .badge-unlock-coin ailleurs dans l'appli,
   recalée sur ambre/wine plutôt que sur l'or "or/argent/bronze" des tiers de badge (ici on
   compte un TOTAL de badges, pas un tier précis) — tourne en arrivant puis se balance.
   Agrandie et devenue la vraie vedette de l'écran (26/08/2026) : le chiffre passe en petite
   légende à côté plutôt qu'en gros chiffre générique comme les autres écrans. */
.wrapped-medal{
  width:104px;height:104px;border-radius:50%;display:flex;align-items:center;justify-content:center;
  font-size:48px;margin:8px auto 2px;position:relative;
  background:radial-gradient(circle at 32% 28%,#ffe9c2,var(--wine) 85%);
  box-shadow:0 14px 32px -10px rgba(255,184,77,.55);
  animation:wrappedMedalIn .8s cubic-bezier(.2,1.5,.3,1) .3s both, wrappedMedalSway 2.6s ease-in-out 1.1s infinite;
}
.wrapped-medal-caption{
  font-family:'Newsreader',Georgia,serif;font-weight:600;font-size:15px;color:var(--text-muted);margin-top:14px;
}
.wrapped-medal-caption b{color:var(--amber);font-size:26px;font-variant-numeric:tabular-nums;margin:0 3px;}

/* Verre héros (écran "dégustations", 26/08/2026) : le chiffre est superposé DANS le verre
   (pas empilé en dessous comme les autres écrans) — signature visuelle du stat le plus
   important du Wrapped, seul écran où le résultat et le visuel ne font qu'un. */
/* 07/09/2026 : le chiffre était DANS le verre, en 68px — avec le passage du chiffre géant sur
   tous les autres écrans, celui-ci se retrouvait deux fois plus petit que les suivants et
   illisible sur le liquide. Le verre devient un décor derrière, et le chiffre passe devant à la
   taille des autres : il déborde du verre, et c'est justement le geste. */
.wrapped-hero-glass{position:relative;width:min(62vw,230px);margin:2px auto 0;}
.wrapped-hero-glass .wrapped-glass-svg{width:100%;opacity:.5;filter:drop-shadow(0 14px 28px rgba(194,56,91,.35));}
.wrapped-hero-num{
  position:absolute;left:50%;top:38%;transform:translate(-50%,-50%);z-index:2;
  font-family:'Newsreader',Georgia,serif;font-weight:600;font-size:min(36vw,160px);line-height:.9;
  font-variant-numeric:tabular-nums;letter-spacing:-.02em;
  background:linear-gradient(110deg,var(--amber) 38%,#ffe9c2 50%,var(--wine) 62%);
  background-size:260% 100%;-webkit-background-clip:text;background-clip:text;color:transparent;
  animation:wrappedShimmer 4.5s linear infinite;
  filter:drop-shadow(0 4px 18px rgba(0,0,0,.5));
}

/* La valeur clé d'un écran qui n'est pas un chiffre (le mois, la couleur dominante) : même
   présence typographique qu'un chiffre géant, sinon ces écrans-là paraissent vides à côté
   (07/09/2026, 2e passe — vu sur captures). `wrapped-mot-uni` garde une couleur pleine plutôt
   que le dégradé, pour les cas où la teinte porte un sens (la couleur du vin). */
.wrapped-mot{
  font-family:'Newsreader',Georgia,serif;font-weight:600;font-size:min(17vw,66px);line-height:1.02;
  letter-spacing:-.01em;margin:2px 0 4px;
  background:linear-gradient(110deg,var(--amber) 38%,#ffe9c2 50%,var(--wine) 62%);
  background-size:260% 100%;-webkit-background-clip:text;background-clip:text;color:transparent;
  animation:wrappedShimmer 4.5s linear infinite;
}
.wrapped-mot-uni{background:none;-webkit-background-clip:border-box;background-clip:border-box;animation:none;}

/* Un écran ancré en haut laissait tout le bas vide. Le proverbe descend l'occuper : contenu en
   haut, respiration au milieu, chute en bas — une page, pas un bloc qui flotte. */

/* Cœurs qui montent (écran "likes reçus", 26/08/2026) : remplace le verre par des petits
   cœurs qui s'élèvent en cascade, plus cohérent avec "like" qu'un verre de vin — seul écran
   de stat sans aucun verre. */
.wrapped-hearts-wrap{position:relative;width:100%;max-width:260px;height:110px;margin:8px auto 0;}
.wrapped-heart{position:absolute;bottom:0;width:22px;height:20px;opacity:0;animation:wrappedHeartRise 1.3s cubic-bezier(.2,.9,.3,1) both;}
@keyframes wrappedHeartRise{0%{opacity:0;transform:translateY(16px) scale(.5);}35%{opacity:1;}100%{opacity:1;transform:translateY(-42px) scale(1);}}
@keyframes wrappedMedalIn{0%{opacity:0;transform:scale(.3) rotate(-50deg);}100%{opacity:1;transform:scale(1) rotate(0);}}
@keyframes wrappedMedalSway{0%,100%{transform:rotate(0deg);}50%{transform:rotate(6deg);}}

/* Portrait couleur ("tu es plutôt...", écran bonus 26/08/2026) : le verre se remplit de la
   VRAIE couleur dominante (rouge/blanc/rosé/pétillant), pas un simple pastille plate. */
.wrapped-color-glass{margin:10px auto 4px;}

/* Vignette d'accroche (Profil, décembre uniquement ou admin en aperçu) — cf. loadProfile. */
.wrapped-teaser{
  background:radial-gradient(circle at 20% -10%, #33161f 0%, var(--bg) 60%), var(--card);
  border:1px solid rgba(255,184,77,.3);border-radius:var(--radius);padding:20px;margin-bottom:20px;
  display:flex;align-items:center;justify-content:space-between;gap:16px;cursor:pointer;
}
.wrapped-teaser-text{font-family:'Newsreader',Georgia,serif;font-weight:600;font-size:15px;}
.wrapped-teaser-sub{font-size:12px;color:var(--text-muted);margin-top:3px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;font-weight:400;}
.wrapped-teaser-arrow{color:var(--amber);font-size:20px;flex-shrink:0;}

/* Modal de détail d'un badge (25/08/2026) — même panneau que .notif-panel, contenu centré
   façon carte médaille plutôt que liste. */
.badge-detail-panel{text-align:center;max-width:360px;}
.badge-detail-coin{
  width:88px;height:88px;border-radius:50%;display:flex;align-items:center;justify-content:center;
  font-size:44px;margin:8px auto 14px;position:relative;
}
.badge-detail-coin::before{content:"";position:absolute;inset:-4px;border-radius:50%;border:1px dashed rgba(255,255,255,.18);}
.badge-detail-coin[data-tier="bronze"]{background:radial-gradient(circle at 32% 28%,#c98a4c,#7a4a22 72%);}
.badge-detail-coin[data-tier="argent"]{background:radial-gradient(circle at 32% 28%,#d7dee8,#8891a0 72%);}
.badge-detail-coin[data-tier="or"]{background:radial-gradient(circle at 32% 28%,#ffe08a,#c99a2e 72%);}
.badge-detail-coin[data-tier="diamant"]{background:radial-gradient(circle at 32% 28%,#bfe9ff,#6fb8e0 72%);}
.badge-detail-name{font-family:'Newsreader',Georgia,serif;font-weight:600;font-size:20px;color:var(--text);margin:0 0 8px;}
.badge-detail-desc{font-size:13.5px;color:var(--text-muted);line-height:1.5;margin:0 0 12px;}
.badge-detail-status{font-size:13.5px;color:var(--text-muted);}
.badge-detail-status.unlocked{color:var(--amber);font-weight:600;}
.badge-detail-tiers{list-style:none;margin:0;padding:0;text-align:left;}
.badge-detail-tier{display:flex;align-items:center;justify-content:space-between;padding:8px 4px;border-bottom:1px solid var(--card-border);font-size:13.5px;}
.badge-detail-tier:last-child{border-bottom:none;}
.badge-detail-tier.unlocked{color:var(--text);font-weight:600;}
.badge-detail-tier:not(.unlocked){color:var(--text-muted);}

/* Vignette "voir sur la carte" (25/08/2026) : même carte SVG que la vraie carte
   d'exploration (réutilise renderCarteFrance/renderCarteMonde), mais bridée en hauteur pour
   rester une vignette compacte plutôt qu'une vraie navigation — le SVG garde son
   preserveAspectRatio, il se contente de rétrécir dans ce cadre. */
/* max-height retiré (25/08/2026, retour de Damien : "je veux [la carte] en entier") — la
   vignette montre maintenant la France/le monde entiers (cf. app.js showWineMapVignette),
   plus une zone zoomée compacte : une hauteur plafonnée à 200px la rognait. */
.wine-map-preview{
  position:relative;overflow:hidden;border-radius:var(--r-sm);
  background:#1b2230;margin-top:8px;
}
.wine-map-preview .carte-svg{display:block;width:100%;height:auto;}
.wine-map-label{font-family:'Newsreader',Georgia,serif;font-weight:600;font-size:16px;margin:0;}

/* Bouteille illustrée en SVG (dégradé de verre, capsule, reflet, ombre portée, photo scannée
   composée dans le SVG — cf. bottleIllustrationHtml dans app.js). Remplace la v1 en
   clip-path CSS + couleur plate, qui ne ressemblait plus à la maquette validée par Damien
   (21/08/2026 : "les bouteilles sont pas comme celle que tu m'avais montrée"). */
/* Agrandie (38x64 -> 54x90) — trop petite pour bien distinguer la bouteille dans les cartes
   cave/historique, retour de Damien 21/08/2026 ("on voyait mieux la bouteille" sur le
   mockup). Une vraie refonte en cartes plus carrées (comme le mockup) reste à faire. */
.bottle-visual{position:relative;width:54px;height:90px;flex-shrink:0;}
.bottle-illustration{width:100%;height:100%;display:block;overflow:visible;}
/* Pastille "prête à boire"/"à garder" (estimation IA boire_debut/boire_fin) — visible sans
   déplier la carte, demande de Damien 21/08/2026. */
.readiness-dot{
  position:absolute;top:-3px;right:-3px;width:13px;height:13px;border-radius:50%;
  border:2px solid var(--card);
}
.readiness-ready{background:var(--green);}
.readiness-wait{background:var(--amber);}
.readiness-late{background:var(--red);}
.qty-badge{
  font-size:11px;font-weight:700;color:var(--amber);background:rgba(255,184,77,.16);
  padding:2px 9px;border-radius:var(--r-pill);
}
.badges-row{display:flex;gap:6px;margin-top:9px;flex-wrap:wrap;}
.bottle-detail{margin-top:14px;padding-top:14px;border-top:1px solid var(--card-border);}
.edit-form{margin-top:14px;padding-top:14px;border-top:1px dashed var(--card-border);}

.wine-photo{
  display:block;width:100%;max-height:420px;object-fit:contain;border-radius:var(--radius);
  box-shadow:var(--e2);margin-bottom:16px;background:var(--bg-alt);
}

.fiche-block{margin:18px 0;}
.fiche-block-label{
  font-size:12px;color:var(--text-muted);text-transform:uppercase;letter-spacing:.04em;
  display:flex;align-items:center;gap:8px;margin-bottom:6px;
}
.fiche-block-label svg{width:14px;height:14px;flex-shrink:0;}
.ia-tag{
  font-size:10px;background:rgba(194,56,91,.15);color:var(--wine);padding:2px 8px;
  border-radius:var(--r-pill);text-transform:none;letter-spacing:0;
}
.tag-pill{
  font-size:10px;background:rgba(135,146,163,.15);color:var(--text-muted);padding:2px 8px;
  border-radius:var(--r-pill);text-transform:none;letter-spacing:0;
}
.fiche-block-text{font-size:14.5px;line-height:1.6;}

.community-avg-badge{
  display:inline-flex;align-items:center;gap:3px;font-size:12px;font-weight:600;
  color:var(--amber);white-space:nowrap;
}
.community-avg-badge svg{width:12px;height:12px;}
.community-avg-count{color:var(--text-muted);font-weight:500;}

.my-tasting-block{
  border-top:1px solid var(--card-border);padding-top:16px;margin-top:20px;
}
.taste-card{
  display:flex;gap:14px;align-items:center;background:var(--card);border:1px solid var(--card-border);
  border-radius:var(--radius);padding:14px 16px;
}
.taste-note{
  font-family:'Newsreader',Georgia,serif;font-size:26px;font-weight:600;color:var(--amber);flex-shrink:0;
}
.taste-note span{font-size:13px;color:var(--text-muted);font-weight:500;}
.taste-note-empty{color:var(--text-muted);}
.taste-card-text{font-size:13.5px;color:var(--text);line-height:1.5;}
.taste-comment{font-style:italic;}
.taste-external{color:var(--text-muted);font-size:12.5px;margin-top:4px;}
.community-ratings .taste-card{margin-bottom:8px;align-items:flex-start;}
.taste-comment-meta{color:var(--text-muted);font-size:11.5px;margin-bottom:2px;display:flex;align-items:center;gap:6px;flex-wrap:wrap;}

/* Like + réponses sous une dégustation (retour de Damien 23/08/2026, "mode avis utile"). */
.taste-social-actions{display:flex;align-items:center;gap:14px;margin-top:8px;}
.like-btn{
  display:inline-flex;align-items:center;gap:4px;background:none;border:none;cursor:pointer;
  color:var(--text-muted);font-size:12.5px;padding:0;
}
.like-btn svg{width:15px;height:15px;}
.like-btn.liked{color:var(--wine);}
.like-btn.liked svg{fill:var(--wine);}
.reply-toggle-btn{
  background:none;border:none;cursor:pointer;color:var(--text-muted);font-size:12.5px;
  padding:0;text-decoration:underline;text-underline-offset:2px;
}
.reply-list{margin-top:8px;display:flex;flex-direction:column;gap:8px;}
.reply-bubble{
  background:var(--bg-alt);border-radius:var(--r-sm);padding:8px 12px;font-size:12.5px;
  line-height:1.5;
}
.reply-box{display:flex;gap:8px;margin-top:8px;align-items:flex-start;}
.reply-box .reply-input{margin:0;min-height:auto;flex:1;}
.reply-box .btn{width:auto;padding:9px 14px;font-size:12.5px;}

/* Pastille pseudo — bouton fixe, ouvre l'écran Compte (24/08/2026, cf. .tab-panel#tab-compte).
   Placée à gauche de la cloche, même hauteur, même logique "pas de topbar persistante". */
.account-pill{
  position:fixed;top:calc(env(safe-area-inset-top, 0px) + 14px);right:64px;z-index:30;
  height:42px;border-radius:var(--r-pill);border:1px solid var(--card-border);
  background:var(--card);color:var(--text);display:flex;align-items:center;gap:8px;
  padding:0 14px 0 5px;cursor:pointer;max-width:160px;box-shadow:var(--e1);
}
.account-pill-avatar{
  width:32px;height:32px;border-radius:50%;flex-shrink:0;display:flex;align-items:center;
  justify-content:center;font-family:'Newsreader',Georgia,serif;font-weight:600;font-size:14px;
  color:#fff;background:linear-gradient(135deg,var(--wine),var(--amber));
}
.account-pill-name{
  font-size:12.5px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
@media (max-width:520px){
  .account-pill{max-width:none;padding:0;width:42px;justify-content:center;}
  .account-pill-name{display:none;}
  .account-pill-avatar{width:100%;height:100%;}
}

/* Cloche de notifications — bouton fixe, visible sur tous les onglets (pas de barre d'en-tête
   persistante ailleurs dans l'appli). */
.notif-bell{
  position:fixed;top:calc(env(safe-area-inset-top, 0px) + 14px);right:14px;z-index:30;
  width:42px;height:42px;border-radius:50%;border:1px solid var(--card-border);
  background:var(--card);color:var(--text);display:flex;align-items:center;justify-content:center;
  cursor:pointer;box-shadow:var(--e1);
}
.notif-bell svg{width:19px;height:19px;}
.notif-badge{
  position:absolute;top:-4px;right:-4px;min-width:18px;height:18px;border-radius:9px;
  background:var(--wine);color:#fff;font-size:10.5px;font-weight:700;align-items:center;
  justify-content:center;padding:0 4px;
}
.notif-backdrop{
  position:fixed;inset:0;background:rgba(11,14,20,.7);z-index:200;
  align-items:flex-start;justify-content:center;padding:70px 16px 16px;
}
/* Le bouton "Voir sur la carte" vit DANS la fiche vin plein écran (z-index:220, cf. plus
   bas) — sans ceci la vignette s'ouvrait bien mais restait invisible, cachée derrière la
   fiche (retour de Damien 25/08/2026 : "faut quitter la fiche et là on la voit"). */
#wine-map-backdrop{z-index:230;}
.notif-panel{width:100%;max-width:400px;max-height:75vh;overflow-y:auto;}
/* Accord inversé (01/09/2026) : panneau un peu plus large que .notif-panel par défaut (liste
   de vins avec vignette, pas juste du texte) + chips plat/moment sur la même ligne scrollable
   que les filtres couleur/prêt-à-boire de l'onglet Cave (.color-chips, réutilisé tel quel). */
.pairing-panel{max-width:480px;}
.pairing-reason{
  display:flex;align-items:baseline;gap:6px;font-size:12.5px;color:var(--text-muted);
  margin-top:4px;line-height:1.4;
}
/* Boutade de l'accord inversé (01/09/2026, easter eggs) : carte distincte des résultats
   normaux — dégradé vin/amber en clin d'œil au médaillon de l'artefact de preview, jamais la
   même mise en avant qu'un vrai accord pour ne pas laisser croire que c'est un résultat
   sérieux. */
.pairing-boutade{
  background:linear-gradient(135deg, rgba(194,56,91,.14), rgba(255,184,77,.1));
  border:1px solid rgba(255,184,77,.3);border-radius:var(--r-lg);
  padding:16px 18px;margin-bottom:14px;text-align:center;
}
.pairing-boutade-text{font-family:'Newsreader',Georgia,serif;font-style:italic;font-size:15px;line-height:1.5;color:var(--text);}
/* Médaillon généré (repris de l'artefact de preview) : un par famille d'easter egg, "unique à
   chaque fois" via un seed aléatoire tiré à l'affichage — cf. eggMedalHtml. */
.egg-medal{width:72px;height:72px;margin:0 auto 12px;position:relative;}
.egg-medal svg{width:100%;height:100%;display:block;}
.egg-medal-emoji{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:26px;}
.pairing-boutade-egg{margin-top:8px;font-size:12px;font-weight:600;color:var(--amber);}

/* Échec de l'accord inversé (05/09/2026) : jusqu'ici un simple <p class="muted"> gris, qui
   après ~90 s d'attente ressemblait à une appli plantée (retour d'Aurélie via Damien). Carte
   assumée + bouton pour relancer sur place, sans avoir à retaper son envie. */
.pairing-error{
  background:var(--card);border:1px solid var(--card-border);border-radius:var(--r-lg);
  padding:16px 18px;text-align:center;
}
.pairing-error p{margin:0 0 12px;font-size:13.5px;line-height:1.5;color:var(--text-muted);}
.pairing-error .btn{width:auto;padding:8px 18px;}

.notif-panel-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:12px;}
.notif-item{padding:10px 0;border-bottom:1px solid var(--card-border);font-size:13.5px;}
.notif-item:last-child{border-bottom:none;}
.notif-item.unread{color:var(--text);}
.notif-item:not(.unread){color:var(--text-muted);}

.gout-bars{margin-top:26px;}
.gout-row{margin:26px 0 12px;}
.gout-labels{display:flex;justify-content:space-between;font-size:12px;color:var(--text-muted);margin-bottom:9px;font-weight:600;}
.gout-track{position:relative;height:9px;border-radius:var(--r-pill);background:var(--bg-alt);border:1px solid var(--card-border);}
.gout-fill{position:absolute;top:-1px;bottom:-1px;left:-1px;border-radius:var(--r-pill);background:linear-gradient(90deg,var(--amber),var(--wine));}
/* Bulle de valeur flottant au-dessus du curseur (retour de Damien, 21/08/2026 : la maquette
   avec bulle "marchait pas" — le vrai code affichait encore l'ancien petit point). */
.gout-bubble{
  position:absolute;top:-29px;transform:translateX(-50%);background:var(--text);color:var(--card);
  font-size:11px;font-weight:800;padding:3px 8px;border-radius:var(--r-sm);white-space:nowrap;
}
.gout-bubble::after{
  content:"";position:absolute;left:50%;bottom:-4px;transform:translateX(-50%) rotate(45deg);
  width:8px;height:8px;background:var(--text);border-radius:1px;
}
.gout-slider-row{display:flex;align-items:center;gap:10px;margin:0 0 12px;}
.gout-slider-row input[type=range]{flex:1;accent-color:var(--wine);}
.gout-slider-value{width:24px;text-align:right;font-size:13px;}

/* Profil de dégustation (onglet Profil, 31/08/2026, v4) : reprend TEL QUEL le langage visuel
   du Wrapped déjà validé par Damien (.wrapped-kicker/.wrapped-rule plus haut dans ce fichier)
   plutôt qu'une métaphore inventée pour l'occasion (barres de jeu vidéo puis podium sportif,
   les deux refusées) — un "hero" mis en scène par catégorie, pas un classement. */
.taste-hero-stack{margin-top:6px;}
.taste-hero{text-align:center;padding:18px 0;}
.taste-hero:first-child{padding-top:6px;}
.taste-hero + .taste-hero{border-top:1px solid var(--card-border);}
.taste-hero .wrapped-rule{margin:12px auto;}
.taste-hero-name{
  font-family:'Newsreader',Georgia,serif;font-weight:600;font-size:26px;line-height:1.2;
  background:linear-gradient(110deg,var(--amber) 20%,#ffe9c2 45%,var(--wine) 75%);
  background-size:220% 100%;-webkit-background-clip:text;background-clip:text;color:transparent;
}
.taste-hero-sub{font-size:12.5px;color:var(--text-muted);}
.taste-hero-runners{font-size:11.5px;color:var(--text-muted);margin-top:10px;line-height:1.5;font-style:italic;}

.chip-link{
  display:inline-flex;align-items:center;gap:6px;margin-top:6px;font-size:13px;font-weight:600;
  text-decoration:none;border:1px solid var(--card-border);border-radius:var(--r-pill);
  padding:6px 14px;
}
.chip-link svg{width:14px;height:14px;flex-shrink:0;}
.chip-link:hover{border-color:var(--wine);}
/* Variante <button> (cf. carteLinkHtml dans app.js, "Voir sur la carte") — même habillage
   que le lien de fiche, mais un bouton n'hérite pas de la couleur de lien par défaut. */
button.chip-link{background:none;color:var(--wine);cursor:pointer;font:inherit;margin-right:8px;}
.aroma-tags{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:10px;}
.aroma-chip{
  background:var(--bg-alt);border:1px solid var(--card-border);border-radius:var(--r-pill);
  padding:5px 12px;font-size:12.5px;white-space:nowrap;
}
.dish-chips{display:flex;flex-wrap:wrap;gap:8px;}
.dish-chip{
  display:inline-flex;align-items:center;gap:7px;padding:8px 13px;border-radius:var(--r-pill);
  background:linear-gradient(160deg, rgba(255,255,255,.05), rgba(255,255,255,0) 55%), var(--card);
  border:1px solid rgba(255,184,77,.14);font-size:13px;font-weight:600;
}
.dish-chip svg{width:15px;height:15px;flex-shrink:0;color:var(--amber);}

.card{background:var(--card);border:1px solid var(--card-border);border-radius:var(--radius);padding:20px;margin-bottom:16px;box-shadow:var(--e1);}

.tab-panel{display:none;}
.tab-panel.active{display:block;}

.scan-drop{
  border:2px dashed var(--card-border);border-radius:var(--radius);padding:40px 20px;text-align:center;
  cursor:pointer;color:var(--text-muted);
}
.scan-drop:hover{border-color:var(--wine);color:var(--text);}
.scan-drop img{max-width:100%;max-height:220px;border-radius:var(--r-sm);margin-top:12px;}

/* Barre de progression indéterminée pendant un scan (1-2 appels IA à la suite, souvent
   30s-1min) — on ne connaît pas la vraie progression, donc un dégradé qui glisse en boucle
   plutôt qu'un pourcentage inventé. */
.scan-loading-bar{
  height:8px;border-radius:var(--r-pill);background:var(--bg-alt);overflow:hidden;margin-bottom:14px;
}
.scan-loading-fill{
  height:100%;width:40%;border-radius:var(--r-pill);
  background:linear-gradient(90deg,var(--amber),var(--wine));
  animation:scan-loading-slide 1.3s ease-in-out infinite;
}
@keyframes scan-loading-slide{
  0%{transform:translateX(-100%);}
  100%{transform:translateX(350%);}
}

.confidence-badge{
  display:inline-block;padding:2px 10px;border-radius:var(--r-pill);font-size:12px;font-weight:600;
}
.confidence-high{background:rgba(49,208,170,.15);color:var(--green);}
.confidence-mid{background:rgba(255,184,77,.15);color:var(--amber);}
.confidence-low{background:rgba(255,107,122,.15);color:var(--red);}

/* Carte des vins (03/09/2026) : vignette de la photo pour le contexte + liste des vins lus
   en dessous, ordre de lecture conservé (pas de superposition pixel-parfaite sur la photo —
   fiabilité des bbox par ligne pas encore éprouvée, cf. ROADMAP.md section 1 pour le choix). */
.carte-thumb-wrap{margin-bottom:14px;}
.carte-thumb-wrap img{width:100%;border-radius:var(--r-lg);display:block;}
.carte-item{
  display:flex;align-items:center;gap:10px;padding:12px 0;
  border-bottom:1px solid var(--card-border);
}
.carte-item:last-child{border-bottom:none;}
.carte-item-main{flex:1;min-width:0;}
.carte-item-name{font-weight:600;font-size:14.5px;line-height:1.3;}
.carte-item-meta{font-size:12px;color:var(--text-muted);margin-top:2px;}
.carte-item-side{display:flex;flex-direction:column;align-items:flex-end;gap:4px;flex-shrink:0;}
.carte-item-price{font-size:13px;font-weight:600;color:var(--text);}
.carte-affinity-chip{
  display:inline-flex;align-items:center;gap:4px;font-size:11px;font-weight:600;
  padding:2px 9px;border-radius:var(--r-pill);white-space:nowrap;
}
.carte-affinity-good{background:rgba(49,208,170,.15);color:var(--green);}
.carte-affinity-ok{background:rgba(255,184,77,.15);color:var(--amber);}
.carte-affinity-bad{background:rgba(255,107,122,.15);color:var(--red);}
.carte-affinity-locked{background:rgba(135,146,163,.15);color:var(--text-muted);}
.carte-item-unmatched{font-size:10px;color:var(--text-muted);}

/* Intertitre d'une rubrique de la carte du restaurant (14/09/2026) — repris de la photo, jamais
   déduit de la couleur du vin (cf. static/app.js:carteListHtml). Volontairement discret : c'est
   un repère de lecture, pas un titre de section de l'appli. */
.carte-section{
  font-family:'Newsreader',Georgia,serif;font-size:12px;letter-spacing:.14em;text-transform:uppercase;
  color:var(--amber);margin:18px 0 8px;padding-bottom:6px;border-bottom:1px solid var(--card-border);
}
.carte-section:first-child{margin-top:4px;}

.carte-item-clickable{cursor:pointer;}
.carte-item-clickable:active{background:var(--bg-alt);}

.wine-fiche{display:grid;gap:2px;margin:16px 0;}
.wine-fiche .row{display:flex;justify-content:space-between;gap:12px;font-size:14px;border-bottom:1px solid var(--card-border);padding:9px 0;}
.wine-fiche .row span:first-child{color:var(--text-muted);flex-shrink:0;}
.wine-fiche .row span:last-child{text-align:right;}

/* Grille de tuiles carrées plutôt qu'une liste de lignes fines — retour de Damien
   21/08/2026 après avoir revu la maquette ("les cases étaient plus carrées, on voyait mieux
   la bouteille"). Une tuile passe en pleine largeur (.expanded) une fois dépliée : la fiche
   complète (barres de goût, chips accords...) serait écrasée dans une seule colonne étroite
   sinon. */
.bottle-list,.tasting-list{display:grid;grid-template-columns:repeat(2, 1fr);gap:12px;}
.bottle-item,.tasting-item{
  background:linear-gradient(160deg, rgba(255,255,255,.04), rgba(255,255,255,0) 45%), var(--card);
  border:1px solid rgba(255,184,77,.12);border-radius:var(--radius);padding:14px;
  box-shadow:var(--e1);
}
/* Fiche vin en plein écran (25/08/2026, retour de Damien : en scrollant une fiche ouverte,
   les autres vignettes de la grille restaient visibles en dessous — "ça fait bizarre" — et
   le bouton retour du téléphone ne fermait rien de spécifique. Remplace l'ancien
   grid-column:1/-1 (carte large mais toujours DANS la grille) — cf. static/app.js
   openWineDetail()/popstate pour la partie navigation associée.
   🐛 Corrigé le 26/08/2026 (retour de Damien, page d'accueil publique cassée en scrollant) :
   `templates/landing.html` réutilise volontairement `.bottle-item.expanded` pour sa carte de
   démo (décorative, pointer-events:none) — sans le `body:not(.landing)` ci-dessous, cette
   carte de démo héritait du `position:fixed;inset:0` pensé pour l'app connectée et prenait
   tout l'écran au scroll. `style.css` est chargé par les deux pages (landing ET app), donc
   toute règle liée à `.expanded` qui doit rester propre à l'app connectée doit être scopée
   ainsi — pas juste celle-ci, la prochaine aussi. */
body:not(.landing) .bottle-item.expanded,body:not(.landing) .tasting-item.expanded{
  position:fixed;inset:0;z-index:220;background:var(--bg);
  overflow-y:auto;border-radius:0;box-shadow:none;
  padding:calc(env(safe-area-inset-top, 0px) + 14px) 16px calc(env(safe-area-inset-bottom, 0px) + 24px);
}
/* Sur PC (retour de Damien : "sur le tel nickel mais sur le site internet sur pc" — les
   cépages/lignes de la fiche, en display:flex justify-content:space-between sans contrainte
   de largeur, s'étiraient sur toute la largeur de l'écran) : contenu recentré et limité en
   largeur, le fond reste plein écran. Sans effet sur mobile (viewport déjà < 640px). */
body:not(.landing) .bottle-item.expanded .bottle-tile,body:not(.landing) .tasting-item.expanded .bottle-tile,
body:not(.landing) .bottle-item.expanded .bottle-detail,body:not(.landing) .tasting-item.expanded .bottle-detail{
  max-width:640px;margin-left:auto;margin-right:auto;
}
.detail-float-back{
  position:sticky;top:0;z-index:2;margin-bottom:10px;display:block;
  background:rgba(11,14,20,.75);backdrop-filter:blur(6px);border:1px solid rgba(255,255,255,.16);
  color:var(--text);font-size:12px;padding:7px 13px;border-radius:var(--r-pill);cursor:pointer;
}
.detail-float-back:hover{border-color:var(--amber);color:var(--amber);}
/* Confirmation visuelle après "Enregistrer la correction" (25/08/2026) : la carte est
   reconstruite depuis zéro par loadCellar()/loadHistory(), ce flash est ce qui rend visible
   qu'une correction a bien été prise en compte plutôt que rien ne sembler se passer. */
.bottle-item.flash-updated,.tasting-item.flash-updated{animation:flashUpdated 1.5s ease-out;}
@keyframes flashUpdated{
  0%{box-shadow:0 0 0 3px rgba(94,196,120,.7), var(--e1);}
  100%{box-shadow:var(--e1);}
}
.bottle-tile{
  display:flex;flex-direction:column;align-items:center;text-align:center;gap:10px;cursor:pointer;
}
.expanded .bottle-tile{flex-direction:row;text-align:left;align-items:center;gap:14px;}
.bottle-tile-text{min-width:0;width:100%;}
.expanded .bottle-tile-text{width:auto;flex:1;}
.bottle-item .title,.tasting-item .title,#scan-result-title{
  font-family:'Newsreader',Georgia,serif;font-weight:600;font-size:15px;line-height:1.25;
}
.bottle-item .sub,.tasting-item .sub{color:var(--text-muted);font-size:12.5px;margin-top:3px;}
.bottle-tile .badges-row{justify-content:center;}
.expanded .bottle-tile .badges-row{justify-content:flex-start;}

table{width:100%;border-collapse:collapse;font-size:13px;}
th,td{text-align:left;padding:8px 10px;border-bottom:1px solid var(--card-border);}
th{color:var(--text-muted);font-weight:600;}

.camera-overlay{
  position:fixed;inset:0;background:#000;z-index:100;display:flex;flex-direction:column;
  align-items:center;justify-content:center;
}
.camera-overlay video{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;}
/* Rectangle de visée sur l'étiquette (pourcentages purs, pas de repère SVG carré qui se
   déformait sur un écran de tel portrait — cf. commentaire dans index.html). Le halo sombre
   autour est un box-shadow géant (technique "spotlight") : la seule zone claire est le
   rectangle lui-même. */
.camera-guide{
  /* Portrait (plus haut que large) : les étiquettes/bouteilles sont en portrait, un cadre
     paysage cadrait mal l'étiquette (retour de Damien 23/08/2026). */
  position:absolute;left:20%;top:15%;width:60%;height:65%;
  border:2px solid rgba(255,255,255,.85);border-radius:var(--r-sm);
  box-shadow:0 0 0 9999px rgba(11,14,20,.6);pointer-events:none;
}
/* Carte des vins (03/09/2026) : cadre bien plus large que le rectangle étroit pensé pour une
   étiquette de bouteille — une carte entière a besoin de tout le cadrage disponible. */
.camera-guide.wide{left:5%;top:8%;width:90%;height:84%;}
.camera-hint{
  position:absolute;top:calc(env(safe-area-inset-top, 0px) + 20px);left:0;right:0;text-align:center;
  color:#fff;font-size:14px;text-shadow:0 2px 8px rgba(0,0,0,.8);padding:0 20px;
}
.camera-controls{
  position:absolute;bottom:calc(env(safe-area-inset-bottom, 0px) + 28px);left:0;right:0;
  display:flex;align-items:center;justify-content:space-around;padding:0 24px;
}
.camera-shutter{
  width:68px;height:68px;border-radius:50%;background:#fff;border:4px solid rgba(255,255,255,.35);
  cursor:pointer;
}
/* Flash (05/09/2026) : beaucoup de bouteilles se scannent le soir, dans une cave ou au
   restaurant. Bouton discret tant qu'il est éteint, ambre une fois allumé. */
.camera-torch{
  width:46px;height:46px;border-radius:50%;display:flex;align-items:center;justify-content:center;
  background:rgba(255,255,255,.12);border:1px solid rgba(255,255,255,.3);color:#fff;cursor:pointer;
}
.camera-torch svg{width:22px;height:22px;}
.camera-torch.on{background:var(--amber);border-color:var(--amber);color:#1a1204;}
.camera-torch.on svg{fill:currentColor;}
.camera-controls .btn-ghost{color:#fff;border-color:rgba(255,255,255,.4);width:84px;}
.camera-native-link{
  position:absolute;bottom:calc(env(safe-area-inset-bottom, 0px) + 110px);left:0;right:0;
  text-align:center;background:none;border:none;color:#fff;font-size:13px;
  text-decoration:underline;text-underline-offset:3px;opacity:.85;
}
.camera-focus-ring{
  position:absolute;width:64px;height:64px;border:2px solid #fff;border-radius:50%;
  transform:translate(-50%,-50%);pointer-events:none;opacity:0;
}
.camera-focus-ring.active{opacity:1;transition:opacity .15s ease, transform .15s ease;}

.rating-dial{position:relative;width:180px;height:180px;margin:10px auto 20px;touch-action:none;}
.rating-dial svg{width:100%;height:100%;cursor:pointer;}
.rating-track{fill:none;stroke:var(--bg-alt);stroke-width:14;stroke-linecap:round;}
.rating-fill{fill:none;stroke:var(--wine);stroke-width:14;stroke-linecap:round;}
.rating-handle{fill:var(--wine);stroke:var(--card);stroke-width:4;}
.rating-value{
  position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);
  font-family:'Newsreader',Georgia,serif;font-size:38px;font-weight:600;color:var(--text);
  pointer-events:none;white-space:nowrap;
}
.rating-max{font-size:16px;color:var(--text-muted);margin-left:2px;}

/* --- Mobile : menu du bas au lieu du menu gauche --- */
@media (max-width: 720px){
  .app-shell{flex-direction:column;}
  .sidebar{
    position:fixed;left:0;right:0;bottom:0;width:100%;height:68px;
    flex-direction:row;justify-content:space-around;align-items:stretch;
    border-right:none;border-top:1px solid rgba(255,255,255,.08);
    padding:0 2px;background:linear-gradient(180deg, rgba(58,15,30,.9), rgba(11,14,20,.95));
    backdrop-filter:blur(18px);z-index:20;
  }
  .brand,.sidebar-footer{display:none;}
  /* Chaque bloc occupe sa propre moitié de la barre (flex:1 à poids égal) et répartit ses
     propres onglets en son sein — le FAB scan (position:absolute, hors flux) se retrouve
     ainsi toujours exactement à la frontière entre les deux blocs, jamais derrière un onglet,
     peu importe le nombre d'onglets de chaque côté (cf. commentaire dans index.html). */
  .nav-group{display:flex;flex:1;justify-content:space-around;}
  .nav-item{
    flex:1;flex-direction:column;gap:2px;padding:6px 2px;margin-top:0;text-align:center;
    justify-content:center;
  }
  .nav-icon svg{width:20px;height:20px;}
  .nav-label{font-size:9px;white-space:nowrap;}
  /* Bouton "scan" en rond posé SUR la barre (pas de clip-path pour "fusionner" la barre et
     le rond — premier essai raté, cf. commentaire dans index.html) : juste un cercle centré
     qui déborde de quelques pixels au-dessus, sans anneau de découpe, comme la référence
     envoyée par Damien (21/08/2026). Spécificité .sidebar .nav-item[data-tab="scan"]
     pour passer devant .nav-item.active/:hover (même poids sinon, cf. cascade). */
  .sidebar .nav-item[data-tab="scan"]{
    position:absolute;left:50%;top:-10px;transform:translateX(-50%);
    width:52px;height:52px;border-radius:50%;padding:0;
    display:flex;align-items:center;justify-content:center;
    background:linear-gradient(135deg,#e0507a,var(--wine) 55%,#8a2140);
    box-shadow:var(--glow-wine);
    color:#fff;
  }
  .sidebar .nav-item[data-tab="scan"]:hover,
  .sidebar .nav-item[data-tab="scan"].active{background:linear-gradient(135deg,#e0507a,var(--wine) 55%,#8a2140);}
  .sidebar .nav-item[data-tab="scan"] .nav-icon svg{width:21px;height:21px;}
  .sidebar .nav-item[data-tab="scan"] .nav-label{display:none;}
  .main{padding:16px;padding-bottom:80px;max-width:100%;}
  .scan-actions{flex-direction:column;}
  .scan-actions .btn{min-width:0;}
}

/* Sélecteur de quantité à l'ajout en cave (remplace un ancien prompt() en texte libre —
   retour de Damien 23/08/2026 : "on peut écrire de la merde et ça passe"). z-index:230 (pas
   200) depuis le 26/08/2026 : bug remonté par Damien — le bouton "Prix / cadeau / occasion"
   (cf. openBottleDetailsEditor, réutilise .qty-picker-backdrop) ouvert depuis une fiche déjà
   en plein écran (.expanded, z-index:220, cf. plus bas) apparaissait DERRIÈRE elle, invisible
   sans faire "retour" d'abord. Même z-index que #wine-map-backdrop, corrigé pour la même
   raison le 25/08/2026. */
.qty-picker-backdrop{
  position:fixed;inset:0;background:rgba(11,14,20,.7);z-index:230;
  display:flex;align-items:center;justify-content:center;padding:20px;
}
.qty-picker{
  background:var(--card);border:1px solid var(--card-border);border-radius:var(--radius);
  padding:24px;width:100%;max-width:320px;text-align:center;
}
.qty-picker-title{font-weight:600;margin-bottom:16px;}
.qty-picker-stepper{
  display:flex;align-items:center;justify-content:center;gap:20px;margin-bottom:14px;
}
.qty-picker-stepper .qty-btn{width:44px;height:44px;padding:0;font-size:20px;border-radius:50%;}
.qty-picker-value{font-size:28px;font-weight:600;min-width:2ch;}
.qty-picker-packs{display:flex;gap:10px;justify-content:center;margin-bottom:18px;}
.qty-picker-actions{display:flex;gap:10px;}
.qty-picker-actions .btn{flex:1;}
/* Feuille de notation (14/09/2026) : même habillage que le sélecteur de quantité, mais elle
   accueille le formulaire #tasting-form DÉPLACÉ depuis le résultat de scan (roue + commentaire,
   cf. openRatingSheet) — donc un peu plus large, alignée à gauche comme un formulaire, et
   défilante quand le clavier mobile mange la hauteur utile. */
.rating-sheet{max-width:380px;max-height:88vh;overflow-y:auto;text-align:left;}
.rating-sheet .qty-picker-title{
  text-align:center;font-family:'Newsreader',Georgia,serif;font-size:17px;margin-bottom:2px;
}
.rating-sheet-sub{text-align:center;font-size:12.5px;margin:0 0 10px;}
.rating-sheet #tasting-form{margin-top:0!important;}
.rating-sheet #save-tasting{width:100%!important;}   /* bat le style inline du bouton */
.rating-sheet-cancel{width:100%;margin-top:8px;}

/* Modale générique admin (détail utilisateur) — même structure que .qty-picker-backdrop. */
.admin-modal-backdrop{
  position:fixed;inset:0;background:rgba(11,14,20,.7);z-index:200;
  align-items:center;justify-content:center;padding:20px;
}
.admin-modal{width:100%;max-width:480px;max-height:80vh;overflow-y:auto;}
.admin-modal:has(.recrop-wrap){max-width:640px;}

/* Recadrage manuel étiquette/bouteille (admin, cf. app.py:api_admin_recrop_wine). */
.recrop-wrap{position:relative;display:inline-block;max-width:100%;cursor:crosshair;margin-bottom:12px;}
.recrop-wrap img{max-width:100%;max-height:60vh;display:block;-webkit-user-drag:none;user-select:none;}
.recrop-box{
  position:absolute;border:2px dashed var(--wine);background:rgba(194,56,91,.15);
  display:none;pointer-events:none;
}
.recrop-actions{display:flex;gap:10px;margin-bottom:16px;flex-wrap:wrap;}

/* Étiquette en quadrilatère (02/09/2026) : 4 coins déplaçables un par un ("angle par angle",
   demande de Damien) pour coller aux vrais bords d'une étiquette photographiée de travers,
   plutôt qu'un simple rectangle qui laisse du fond dans les coins — cf. app.py:crop_quad.
   Poignées en `touch-action:none` + Pointer Events (pas mousedown/mousemove comme l'outil
   rectangle existant) : Damien teste sur Android/Brave, un vrai geste de glisser-déposer au
   doigt a besoin des événements pointer unifiés, pas d'une émulation souris approximative. */
.recrop-quad-svg{position:absolute;inset:0;width:100%;height:100%;pointer-events:none;display:none;}
.recrop-quad-poly{fill:rgba(194,56,91,.15);stroke:var(--wine);stroke-width:2;}
.recrop-handle{
  position:absolute;width:28px;height:28px;margin:-14px 0 0 -14px;border-radius:50%;
  background:var(--wine);border:2px solid #fff;box-shadow:var(--e1);display:none;
  touch-action:none;cursor:grab;z-index:2;
}
.recrop-handle:active{cursor:grabbing;transform:scale(1.15);}
.recrop-thumbs{display:grid;grid-template-columns:1fr 1fr;gap:14px;}
.recrop-thumbs img{max-width:100%;border-radius:var(--r-sm);border:1px solid var(--card-border);}
/* Remplacement direct de l'étiquette par un fichier (02/09/2026) : filet de secours quand la
   photo complète a été purgée et que le recadrage ci-dessus est inutilisable. */
.recrop-upload-label{margin-top:16px;padding-top:14px;border-top:1px solid var(--card-border);}
.recrop-upload-label input[type=file]{display:block;margin-bottom:8px;font-size:13px;color:var(--text-muted);}

/* Barre d'action groupée du tableau Fiches vins (01/09/2026, retour de Damien : "je coche et
   elle vient direct se foutre au-dessus de la 1ère ligne que je viens de cocher" — 1er essai
   en position:sticky:top:0 explicitement refusé, "faut descendre le tableau [pour la voir
   avec la ligne]", pas ce qui était demandé). Générée comme une VRAIE ligne de tableau
   (`<tr class="wines-bulk-row"><td colspan="...">`, cf. wineBulkBarHtml dans admin.html)
   insérée juste avant la 1ère ligne cochée — se déplace donc naturellement avec le contenu au
   lieu d'un calcul de position en JS (fragile, à recalculer au scroll/resize). Pas de
   max-height fixe (contrairement au 1er essai) : `flex-wrap` absorbe le nombre variable de
   boutons (1 à 6 selon la sélection) sans jamais couper le dernier. */
.wines-bulk-row td{padding:0;border-bottom:none;}
.wines-bulk-actions{
  display:flex;align-items:center;gap:10px;flex-wrap:wrap;
  background:var(--card);border:1px solid var(--card-border);border-radius:var(--r-sm);
  box-shadow:var(--e2);padding:10px 14px;margin:6px 0;
}

/* Barre de nav admin sur mobile (01/09/2026, audit demandé par Damien) : 7 items texte
   ("Utilisateurs", "← Retour à l'appli"...) dans les mêmes flex:1 à poids égal que le menu du
   bas de l'appli principale (.sidebar en @media(max-width:720px) plus haut) se seraient
   tassés/coupés — bien plus d'items ici que les 4-5 icônes de l'appli, jamais eu besoin
   d'égaliser leur largeur. Scopé à .admin-sidebar (jamais .sidebar seul) : ne touche PAS au
   menu du bas de l'appli principale, dont le calage du FAB scan est documenté comme fragile
   dans CLAUDE.md (piège de parité 23/08/2026) — défilement horizontal ici à la place, plus
   simple et sans risque de recasser ce mécanisme ailleurs. */
@media (max-width: 720px){
  .admin-sidebar{
    justify-content:flex-start;overflow-x:auto;-webkit-overflow-scrolling:touch;
  }
  .admin-sidebar .nav-item{
    flex:0 0 auto;white-space:nowrap;padding:6px 14px;font-size:12.5px;
  }
}

/* Pied de page partagé — pages publiques uniquement (cf. templates/_footer.html). Reprend
   l'apparence de l'ancien .landing-footer (local à landing.css avant le 24/08/2026), déplacé
   ici pour être réutilisable sur toutes les pages publiques (mentions légales,
   confidentialité, contact) sans dupliquer la règle. */
.site-footer{
  padding:40px 24px 60px;text-align:center;color:var(--text-muted);font-size:13px;
}
.site-footer a{color:var(--text-muted);text-decoration:underline;text-underline-offset:3px;}
.site-footer a:hover{color:var(--text);}

/* Message sanitaire obligatoire (loi Evin, art. L3323-4 CSP), présent sur toute surface
   publique ou promotionnelle — cf. templates/_footer.html. Discret mais LISIBLE : pas plus
   petit que 11px, pas en couleur de fond, et jamais masqué derrière un dépliant. */
.mention-alcool{
  margin:14px 0 0;font-size:11.5px;line-height:1.5;text-align:center;
  color:var(--text-muted);opacity:.85;
}

/* Pied de page léger dans l'app connectée (24/08/2026) — pas .site-footer (bien trop haut/
   massif pour être répété sous CHAQUE onglet) : une simple ligne de liens, cf. index.html
   pour comment elle reste visible quel que soit l'onglet actif. */
.app-footer-links{
  text-align:center;font-size:12px;margin:24px 0 4px;padding-top:16px;
  border-top:1px solid var(--card-border);
}

/* ===================================================================================
   Grande Cave / mode avancé (04/09/2026) — cf. ROADMAP.md section 7bis.
   Maquette validée : https://claude.ai/code/artifact/6015ef21-ed9f-4cf1-b429-a39d52327cef

   Le meuble est dessiné en CSS pur (alvéoles = culs de bouteille en dégradé radial), JAMAIS
   en SVG : c'est ce qui permet d'afficher une cave de 1000 places sans ramer. Les vraies
   illustrations bouteille (bottleIllustrationHtml) sont réservées à la vue d'un seul étage.
   =================================================================================== */

/* Le plan vit DANS l'onglet Cave (04/09/2026) et non plus en plein écran : le menu du bas
   reste visible, et il n'y a plus de niveau de navigation à refermer pour rentrer chez soi. */
.gc{display:block;}
.gc[hidden]{display:none;}
.gc-inner{max-width:640px;margin:0 auto;}

.gc-bar{
  display:flex;align-items:center;gap:12px;
  padding:2px 0 12px;border-bottom:1px solid var(--card-border);margin-bottom:14px;
}
.gc-bar .gc-back{
  flex:0 0 auto;width:36px;height:36px;border-radius:50%;background:var(--card);
  border:1px solid var(--card-border);color:var(--text);font-size:16px;cursor:pointer;
}
.gc-bar h2{margin:0;font-size:19px;line-height:1.2;}
.gc-bar .gc-type{
  display:block;font-size:10px;letter-spacing:.14em;text-transform:uppercase;
  color:var(--text-muted);margin-bottom:3px;
}
.gc-bar .gc-cap{
  margin-left:auto;font-size:13px;color:var(--text-muted);white-space:nowrap;
  font-variant-numeric:tabular-nums;
}
.gc-bar .gc-cap strong{color:var(--text);}

/* ---- Habillages : 4 matières, mêmes règles de rendu, seuls les tokens changent ---- */
.gc-rack,.gc-mat{
  --r1:#5a4029;--r2:#3d2b1c;--r3:#241809;
  --grain:rgba(255,255,255,.022);
  --bay-c:#090705;
  --plank:linear-gradient(180deg,#8a6640,#4a3320 46%,#1d1309);
  --plate-fg:#e3c979;--plate-bg:rgba(201,162,39,.16);--plate-bd:rgba(201,162,39,.3);
  --inner-fx:none;
}
[data-style="chene"]{
  --r1:#c39a63;--r2:#9d7143;--r3:#70502f;--grain:rgba(255,255,255,.05);--bay-c:#2b1e12;
  --plank:linear-gradient(180deg,#e2b87e,#a97b45 46%,#71512c);
  --plate-fg:#3f2d18;--plate-bg:rgba(255,240,210,.55);--plate-bd:rgba(90,64,32,.5);
}
[data-style="pierre"]{
  --r1:#726a61;--r2:#4e4740;--r3:#2f2b27;--grain:rgba(255,255,255,.035);--bay-c:#131110;
  --plank:linear-gradient(180deg,#968d81,#5a534b 46%,#34302c);
  --plate-fg:#ded7c9;--plate-bg:rgba(222,215,201,.14);--plate-bd:rgba(222,215,201,.3);
}
[data-style="frigo"]{
  --r1:#2b313a;--r2:#1a1f27;--r3:#0e1116;--grain:rgba(255,255,255,.03);--bay-c:#06090d;
  --plank:linear-gradient(180deg,#a8b4c4,#616c7a 46%,#2b323b);
  --plate-fg:#a9c8ff;--plate-bg:rgba(91,140,255,.16);--plate-bd:rgba(91,140,255,.38);
  --inner-fx:inset 0 0 46px rgba(91,140,255,.16);
}

/* ---- Le meuble ---- */
.gc-rack{
  border-radius:12px;padding:10px 9px 12px;border:1px solid rgba(0,0,0,.5);
  background:
    repeating-linear-gradient(91deg,var(--grain) 0 1px,transparent 1px 5px),
    linear-gradient(180deg,var(--r1),var(--r2) 42%,var(--r3));
  box-shadow:inset 0 1px 0 rgba(255,255,255,.14),inset 0 -2px 6px rgba(0,0,0,.5),var(--e1);
}
.gc-rack-inner{
  background:var(--bay-c);border-radius:7px;padding:7px 6px 3px;
  box-shadow:inset 0 3px 14px rgba(0,0,0,.9),inset 0 0 0 1px rgba(0,0,0,.6),var(--inner-fx);
}
[data-style="pierre"] .gc-rack-inner{
  background-image:
    repeating-linear-gradient(0deg,rgba(0,0,0,.4) 0 1px,transparent 1px 27px),
    repeating-linear-gradient(90deg,rgba(0,0,0,.3) 0 1px,transparent 1px 36px);
}
[data-style="frigo"] .gc-plank{height:4px;border-radius:1px;}

.gc-etage{display:flex;align-items:stretch;gap:6px;margin-bottom:9px;}
.gc-etage:last-child{margin-bottom:2px;}
.gc-etage-no{
  flex:0 0 13px;align-self:center;text-align:right;font-size:9px;
  color:rgba(231,235,243,.28);font-variant-numeric:tabular-nums;
}
.gc-spine{flex:0 0 3px;border-radius:var(--r-pill);background:transparent;}
.gc-bay{flex:1;min-width:0;}
.gc-cells{display:grid;grid-template-columns:repeat(var(--places),1fr);gap:4px;}
.gc-plank{
  height:7px;margin-top:5px;border-radius:2px;background:var(--plank);
  box-shadow:0 1px 0 rgba(0,0,0,.7),0 3px 6px rgba(0,0,0,.45);
}

/* ---- Alvéole : vide = trou creusé, pleine = cul de bouteille bombé ---- */
.gc-cell{
  aspect-ratio:1;border-radius:50%;padding:0;position:relative;border:none;cursor:pointer;
  background:radial-gradient(circle at 50% 38%,#120c07,#060403 70%);
  box-shadow:inset 0 2px 5px rgba(0,0,0,.95),inset 0 -1px 0 rgba(255,255,255,.05);
  transition:transform .16s,box-shadow .16s;
}
.gc-cell.filled{
  background:
    radial-gradient(circle at 34% 26%,rgba(255,255,255,.5) 0 8%,transparent 42%),
    radial-gradient(circle at 42% 34%,var(--p1),var(--p2) 52%,var(--p3) 100%);
  box-shadow:inset 0 0 0 1.5px rgba(0,0,0,.55),inset 0 -3px 5px rgba(0,0,0,.55),0 1px 1px rgba(0,0,0,.6);
}
.gc-cell.filled::after{
  content:"";position:absolute;inset:26%;border-radius:50%;
  box-shadow:inset 0 0 0 1px rgba(0,0,0,.28);
}
.gc-cell[data-col="rouge"]{--p1:#8a2140;--p2:#5c1530;--p3:#250810;}
.gc-cell[data-col="blanc"]{--p1:#f3edc9;--p2:#d9cf95;--p3:#a89b57;}
.gc-cell[data-col="rose"]{--p1:#f6d3dd;--p2:#e59fb3;--p3:#b85a76;}
.gc-cell[data-col="petillant"]{--p1:#f6e6b0;--p2:#d9b34a;--p3:#9c7420;}
/* Couleur absente ou non reconnue (`wines_reference.couleur = 'inconnu'`, ce que le scan
   renvoie quand il n'a pas su trancher). Sans cette regle, --p1/--p2/--p3 restaient
   indefinies : la pastille se rendait sans remplissage et l'alveole occupee passait pour
   un trou (constate le 07/09/2026 — 9 bouteilles rangees, 8 visibles et 2 trous).
   Gris verre, volontairement neutre : on montre qu'il y a une bouteille, sans lui inventer
   une couleur. */
.gc-cell[data-col="inconnu"]{--p1:#9aa3ad;--p2:#6b7480;--p3:#333a43;}
.gc-cell.gc-target{box-shadow:0 0 0 2px var(--amber),0 0 14px rgba(255,184,77,.5);}
/* Meuble d'illustration (aperçu d'un habillage, aperçu du constructeur) : purement visuel,
   aucune alvéole cliquable — sans ça un clic déclencherait la feuille de remplissage alors
   qu'aucun meuble n'existe encore en base (gcCurrentCave() vaut null à ce moment-là). */
.gc-rack.gc-preview .gc-cell{pointer-events:none;}
.gc-preview-legend{
  text-align:center;font-size:11.5px;color:var(--text-muted);margin:8px 0 0;
}

/* ---- Plaque gravée d'une zone ---- */
.gc-zone-plate{
  display:flex;align-items:center;gap:8px;margin:2px 0 8px 22px;padding:4px 10px;
  border-radius:3px;background:var(--plate-bg);border:1px solid var(--plate-bd);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.16),0 1px 2px rgba(0,0,0,.5);
}
.gc-zone-plate .pin{
  width:6px;height:6px;border-radius:50%;background:var(--zc);flex:0 0 auto;
  box-shadow:0 0 6px var(--zc);
}
.gc-zone-plate .nom{
  font-family:'Newsreader',Georgia,serif;font-size:11px;font-weight:600;letter-spacing:.11em;
  text-transform:uppercase;color:var(--plate-fg);flex:1;min-width:0;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.gc-zone-plate .cnt{
  font-size:10px;color:var(--plate-fg);opacity:.62;font-variant-numeric:tabular-nums;
}
.gc-zone-plate .del{
  background:none;border:none;color:var(--plate-fg);opacity:.6;cursor:pointer;
  font-size:13px;padding:0 2px;
}

/* ---- Carrousel de meubles ---- */
.gc-caves{display:flex;overflow-x:auto;scroll-snap-type:x mandatory;gap:14px;scrollbar-width:none;}
.gc-caves::-webkit-scrollbar{display:none;}
.gc-cave{flex:0 0 100%;scroll-snap-align:center;min-width:0;}
.gc-dots{display:flex;justify-content:center;gap:7px;padding:14px 0 4px;flex-wrap:wrap;}
.gc-dot{
  width:6px;height:6px;padding:0;border:none;border-radius:50%;background:var(--card-border);
  cursor:pointer;transition:.2s;
}
.gc-dot.on{background:var(--wine);width:20px;border-radius:var(--r-pill);}
.gc-dot-nom{display:none;}

/* ---- Bac « pas encore rangées » ---- */
.gc-tray{
  margin:14px 0 0;padding:11px 13px;background:var(--card);
  border:1px dashed var(--card-border);border-radius:var(--r-lg);
}
.gc-tray-head{
  display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:9px;
  font-size:12.5px;color:var(--text-muted);
}
.gc-tray-head strong{color:var(--text);}
.gc-tray-row{display:flex;gap:7px;overflow-x:auto;scrollbar-width:none;padding-bottom:2px;}
.gc-tray-row::-webkit-scrollbar{display:none;}
.gc-tray-row .gc-cell{flex:0 0 30px;width:30px;}
.gc-tray-row .gc-cell.sel{box-shadow:0 0 0 2px var(--amber),0 0 14px rgba(255,184,77,.5);}
.gc-tray-empty{font-size:12.5px;color:var(--text-muted);}

/* ---- Mode édition : le trait au doigt ---- */
.gc-editing .gc-rack{touch-action:none;user-select:none;-webkit-user-select:none;}
.gc-editing .gc-cell{opacity:.42;pointer-events:none;}
.gc-etage.picked .gc-bay{
  box-shadow:0 0 0 2px var(--amber),0 0 18px rgba(255,184,77,.4);border-radius:6px;
}
.gc-hint{
  padding:10px 13px;border-radius:var(--r-sm);font-size:12.5px;line-height:1.5;margin:0 0 12px;
  background:rgba(255,184,77,.1);border:1px solid rgba(255,184,77,.28);color:#f0d09a;
}
.gc-swatches{display:flex;gap:9px;margin:12px 0 14px;}
.gc-swatches button{
  width:30px;height:30px;border-radius:50%;border:2px solid transparent;cursor:pointer;
}
.gc-swatches button.on{border-color:var(--text);transform:scale(1.12);}
.gc-styles{display:flex;gap:8px;overflow-x:auto;scrollbar-width:none;padding-bottom:2px;margin-bottom:14px;}
.gc-styles::-webkit-scrollbar{display:none;}
/* `color` explicite indispensable : un <button> n'hérite PAS de la couleur du texte de la page,
   il retombe sur le noir du navigateur — donc illisible sur un fond sombre (retour de Damien
   04/09/2026 : "les boutons c'est écrit en noir j'arrive pas à lire"). Piège à garder en tête
   pour tout nouveau bouton de l'app qui n'utilise pas les classes .btn existantes. */
.gc-style-opt{
  flex:0 0 auto;width:80px;padding:6px;border-radius:var(--r-sm);cursor:pointer;
  background:var(--bg-alt);border:1px solid var(--card-border);transition:.16s;
  color:var(--text);
}
.gc-style-opt.on{border-color:var(--amber);background:rgba(255,184,77,.1);}
.gc-style-opt .gc-mat{
  height:42px;border-radius:5px;margin-bottom:6px;position:relative;overflow:hidden;
  background:
    repeating-linear-gradient(91deg,var(--grain) 0 1px,transparent 1px 5px),
    linear-gradient(180deg,var(--r1),var(--r2) 42%,var(--r3));
  box-shadow:inset 0 1px 0 rgba(255,255,255,.14);
}
.gc-style-opt .gc-mat i{
  position:absolute;left:5px;right:5px;bottom:6px;height:4px;border-radius:1px;background:var(--plank);
}
.gc-style-opt .gc-mat u{
  position:absolute;left:9px;bottom:13px;width:13px;height:13px;border-radius:50%;
  text-decoration:none;box-shadow:inset 0 0 0 1.5px rgba(0,0,0,.55);
  background:
    radial-gradient(circle at 34% 26%,rgba(255,255,255,.5) 0 8%,transparent 42%),
    radial-gradient(circle at 42% 34%,#8a2140,#5c1530 52%,#250810);
}
.gc-style-opt .gc-mat u.b2{
  left:26px;
  background:
    radial-gradient(circle at 34% 26%,rgba(255,255,255,.5) 0 8%,transparent 42%),
    radial-gradient(circle at 42% 34%,#f3edc9,#d9cf95 52%,#a89b57);
}
.gc-style-opt b{display:block;font-size:10.5px;font-weight:600;text-align:center;line-height:1.25;}

/* ---- Champs du constructeur ---- */
.gc-field{
  display:flex;align-items:center;justify-content:space-between;gap:12px;padding:9px 0;
}
.gc-field + .gc-field{border-top:1px solid var(--card-border);}
.gc-field label{font-size:13px;color:var(--text-muted);}
.gc-field .val{display:flex;align-items:center;gap:10px;font-weight:600;font-variant-numeric:tabular-nums;}
.gc-step{
  width:30px;height:30px;border-radius:50%;background:var(--bg-alt);border:1px solid var(--card-border);
  color:var(--text);font-size:16px;cursor:pointer;
}

/* ---- Onboarding ---- */
.gc-opt{
  display:flex;align-items:center;gap:12px;width:100%;text-align:left;padding:14px;
  border-radius:var(--r-sm);background:var(--card);border:1px solid var(--card-border);
  margin-bottom:8px;cursor:pointer;color:var(--text);transition:.16s;
}
.gc-opt.on{border-color:var(--wine);background:rgba(194,56,91,.12);}
.gc-opt .ic{font-size:20px;}
.gc-opt b{display:block;font-size:13.5px;}
.gc-opt span.sub{font-size:11.5px;color:var(--text-muted);}
.gc-verdict{
  padding:17px;border-radius:var(--r-lg);border:1px solid rgba(194,56,91,.45);
  background:linear-gradient(160deg,rgba(194,56,91,.2),rgba(194,56,91,.05));
  box-shadow:var(--glow-wine);
}
.gc-verdict h3{margin:0 0 7px;font-size:18px;}
.gc-verdict p{margin:0 0 13px;font-size:13px;color:#f0cfd8;line-height:1.55;}
.gc-verdict .why{
  display:block;margin:0 0 13px;padding-left:11px;border-left:2px solid rgba(194,56,91,.5);
  font-size:12px;color:var(--text-muted);
}

/* ---- Grande Cave, étape 2 : le tiroir (un étage ouvert en pleine largeur) ---- */
.gc-drawer-head{display:flex;align-items:center;gap:10px;margin:4px 0 14px;}
.gc-drawer-head h3{margin:0;font-size:18px;line-height:1.2;}
.gc-drawer-head small{
  display:block;font-size:10.5px;letter-spacing:.13em;text-transform:uppercase;
  color:var(--plate-fg,#e3c979);margin-top:3px;
}
.gc-strip{
  display:flex;gap:12px;overflow-x:auto;scroll-snap-type:x proximity;
  padding:4px 2px 12px;scrollbar-width:none;
}
.gc-strip::-webkit-scrollbar{display:none;}
.gc-slot-card{
  flex:0 0 auto;scroll-snap-align:center;width:106px;display:flex;flex-direction:column;
  align-items:center;gap:7px;padding:8px 6px;border-radius:var(--r-lg);text-align:center;
  border:1px solid transparent;background:none;color:var(--text);cursor:pointer;transition:.18s;
}
.gc-slot-card.sel{background:var(--card);border-color:rgba(194,56,91,.5);box-shadow:var(--glow-wine);}
.gc-slot-card .bottle-visual{width:62px;height:150px;flex-shrink:0;}
.gc-slot-card .nom{
  font-size:11.5px;font-weight:600;line-height:1.25;overflow:hidden;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;
}
.gc-slot-card .place{
  font-size:9.5px;letter-spacing:.12em;text-transform:uppercase;color:var(--text-muted);
}
.gc-ghost{
  width:62px;height:150px;border-radius:12px 12px 16px 16px;border:1.5px dashed var(--card-border);
  display:grid;place-items:center;font-size:22px;color:var(--text-muted);
}
.gc-ghost.pending{border-style:solid;border-color:rgba(255,184,77,.5);color:var(--amber);}
.gc-drawer-plank{
  height:9px;border-radius:3px;background:var(--plank);box-shadow:0 2px 10px rgba(0,0,0,.6);
}

/* Alvéole réservée par une rafale photo en cours d'analyse */
.gc-cell.pending{
  background:radial-gradient(circle at 50% 38%,#3a2a10,#120c07 70%);
  animation:gc-pulse 1.4s ease-in-out infinite;
}
@keyframes gc-pulse{
  0%,100%{box-shadow:inset 0 2px 5px rgba(0,0,0,.95),0 0 0 0 rgba(255,184,77,.5);}
  50%{box-shadow:inset 0 2px 5px rgba(0,0,0,.95),0 0 0 3px rgba(255,184,77,.28);}
}
@media (prefers-reduced-motion:reduce){
  .gc-cell.pending{animation:none;box-shadow:inset 0 2px 5px rgba(0,0,0,.95),0 0 0 2px rgba(255,184,77,.4);}
}
.gc-burst-note{
  margin:10px 0 0;padding:10px 13px;border-radius:var(--r-sm);font-size:12.5px;line-height:1.5;
  background:rgba(255,184,77,.1);border:1px solid rgba(255,184,77,.28);color:#f0d09a;
}

/* Écran de la rafale photo : reste affiché du début à la fin, l'appareil photo n'étant
   qu'une excursion dont on revient (cf. gcBurstEcran, 07/09/2026). */
.gc-burst{margin:12px 0 0;text-align:center;}
.gc-burst-count{
  font-family:var(--font-head);font-size:64px;line-height:1;font-weight:700;
  color:var(--amber);margin-bottom:4px;
}
.gc-burst-sub{margin:0 0 14px;font-size:12.5px;line-height:1.5;color:var(--text-muted);}
.gc-burst .btn-block{margin-top:0;}

/* Bandeau "bouteille en main" (04/09/2026) : tant qu'il est affiché, toucher un trou range
   directement — un tap par bouteille, aucune confirmation. */
.gc-armed{
  position:sticky;top:8px;z-index:3;display:flex;align-items:center;gap:10px;
  margin:0 0 12px;padding:10px 14px;border-radius:var(--r-pill);
  background:rgba(255,184,77,.14);border:1px solid rgba(255,184,77,.45);
  color:#f0d09a;font-size:12.5px;box-shadow:var(--e1);
}
.gc-armed span:first-child{flex:1;min-width:0;}
.gc-armed strong{color:var(--text);}
.gc-armed-count{
  display:inline-block;margin-left:6px;padding:1px 8px;border-radius:var(--r-pill);
  background:rgba(255,184,77,.22);font-size:11px;
}
.gc-actions-main{display:flex;gap:8px;margin-top:14px;}
.gc-actions-main .btn{flex:1;}
.gc-ranger-row{
  display:flex;align-items:center;gap:11px;width:100%;padding:10px 0;cursor:pointer;
  background:none;border:none;border-top:1px solid var(--card-border);color:var(--text);
}
.gc-ranger-row .gc-cell{width:26px;flex:0 0 26px;pointer-events:none;}
/* Un étage entier est cliquable : on l'ouvre en touchant n'importe où dessus. */
.gc-etage{cursor:pointer;}
.gc-tray-row .gc-cell.sel{box-shadow:0 0 0 2px var(--amber),0 0 14px rgba(255,184,77,.55);}

/* En-tête de l'onglet Cave + bascule Liste / Plan (04/09/2026) */
/* La pastille de compte (right:64px) et la cloche (right:14px) sont en position:fixed en haut
   à droite : sans cette réserve, la bascule Liste/Plan passait dessous et devenait
   inatteignable (retour de Damien 04/09/2026). Le titre et la bascule restent donc groupés
   vers le centre, la zone de droite appartient aux deux boutons flottants. */
.cave-head{
  display:flex;align-items:center;gap:12px;margin-bottom:0;
  padding-right:112px;min-height:42px;
}
.cave-vue-toggle{
  display:flex;flex:0 0 auto;padding:3px;gap:3px;border-radius:var(--r-pill);
  background:var(--bg-alt);border:1px solid var(--card-border);
}
.cave-vue-toggle[hidden]{display:none;}
.cave-vue-toggle button{
  padding:6px 14px;border:none;border-radius:var(--r-pill);background:none;cursor:pointer;
  color:var(--text-muted);font-size:12.5px;font-weight:600;transition:.16s;
}
.cave-vue-toggle button.on{background:var(--wine);color:#fff;box-shadow:var(--glow-wine);}

/* Les deux actions du plan doivent avoir exactement le même gabarit : .btn-primary embarque
   width:100% et margin-top:20px dans son style de base (pensé pour un bouton pleine largeur),
   ce qui décalait et déformait "Ajouter" à côté de "Ranger" (retour de Damien 04/09/2026,
   "c'est pas du tout le même format, ça fait cul"). */
.gc-actions-main .btn{flex:1;width:auto;margin-top:0;}

/* ---- Étape 3 : calques de couleur & spotlight (04/09/2026) ----
   Les filtres de la cave classique ne pouvaient pas être repris tels quels sur un meuble : on
   ne fait pas disparaître une bouteille de son alvéole. Ils deviennent donc soit une
   COLORATION de toute la grille (calque), soit un SURLIGNAGE d'un sous-ensemble (spotlight). */

/* Calque apogée */
.gc-rack[data-calque="apogee"] .gc-cell.filled[data-r="ready"]{--p1:#7df3d6;--p2:#31d0aa;--p3:#0d5b48;}
.gc-rack[data-calque="apogee"] .gc-cell.filled[data-r="wait"]{--p1:#9db6e8;--p2:#5b8cff;--p3:#182a52;}
.gc-rack[data-calque="apogee"] .gc-cell.filled[data-r="late"]{--p1:#ffa8b2;--p2:#ff6b7a;--p3:#5c1a22;}
.gc-rack[data-calque="apogee"] .gc-cell.filled[data-r="inconnu"]{--p1:#4a5260;--p2:#333a46;--p3:#171c24;}
/* Calque valeur */
.gc-rack[data-calque="valeur"] .gc-cell.filled[data-v="bas"]{--p1:#4a5260;--p2:#333a46;--p3:#171c24;}
.gc-rack[data-calque="valeur"] .gc-cell.filled[data-v="moyen"]{--p1:#e0c078;--p2:#b08d3c;--p3:#4a3812;}
.gc-rack[data-calque="valeur"] .gc-cell.filled[data-v="haut"]{--p1:#ffe9a8;--p2:#c9a227;--p3:#6a5010;}
.gc-rack[data-calque="valeur"] .gc-cell.filled[data-v="inconnu"]{--p1:#3a4049;--p2:#272c34;--p3:#14171c;}
/* Calque affinité */
.gc-rack[data-calque="affinite"] .gc-cell.filled[data-a="bas"]{--p1:#454b57;--p2:#2f343e;--p3:#15181e;}
.gc-rack[data-calque="affinite"] .gc-cell.filled[data-a="moyen"]{--p1:#d98aa1;--p2:#a2415c;--p3:#40151f;}
.gc-rack[data-calque="affinite"] .gc-cell.filled[data-a="haut"]{--p1:#ff9ab4;--p2:#c2385b;--p3:#54101f;}
.gc-rack[data-calque="affinite"] .gc-cell.filled[data-a="inconnu"]{--p1:#3a4049;--p2:#272c34;--p3:#14171c;}

/* Spotlight : ce qui correspond s'allume, le reste s'éteint (jamais ne disparaît — une
   bouteille reste physiquement dans son alvéole). */
.gc-rack.searching .gc-cell{opacity:.16;filter:saturate(.2);}
.gc-rack.searching .gc-cell.hit{
  opacity:1;filter:none;transform:scale(1.1);
  box-shadow:0 0 0 2px var(--amber),0 0 16px 2px rgba(255,184,77,.55),inset 0 -3px 5px rgba(0,0,0,.55);
}
.gc-spot{
  display:flex;align-items:center;gap:8px;margin:0 0 10px;padding:9px 13px;
  background:var(--card);border:1px solid var(--card-border);border-radius:var(--r-pill);
}
.gc-spot .ico{opacity:.6;font-size:13px;}
.gc-spot input{flex:1;min-width:0;background:none;border:none;color:var(--text);font:inherit;font-size:13px;}
.gc-spot input::placeholder{color:var(--text-muted);}
.gc-spot input:focus{outline:none;}
.gc-spot-clear{
  background:none;border:none;color:var(--text-muted);cursor:pointer;font-size:14px;padding:0 2px;
}
.gc-chips{display:flex;gap:6px;overflow-x:auto;scrollbar-width:none;padding-bottom:2px;margin-bottom:10px;}
.gc-chips::-webkit-scrollbar{display:none;}
.gc-chip{
  flex:0 0 auto;padding:6px 12px;border-radius:var(--r-pill);font-size:12px;font-weight:600;
  background:var(--card);border:1px solid var(--card-border);color:var(--text-muted);cursor:pointer;
  transition:.16s;
}
.gc-chip.on{background:rgba(255,184,77,.14);border-color:var(--amber);color:var(--amber);}
.gc-calques .gc-chip.on{background:var(--wine);border-color:var(--wine);color:#fff;}
.gc-calque-aide{margin:0 0 12px;font-size:11.5px;color:var(--text-muted);}
.gc-spot-count{margin:0 0 10px;font-size:12px;color:var(--amber);}

/* Pense-bête : les places vides d'une zone NOMMÉE disent quoi racheter (04/09/2026). */
.gc-manques{
  margin-top:14px;padding:11px 13px;background:var(--card);
  border:1px solid var(--card-border);border-radius:var(--r-lg);
}
.gc-manques-head{
  font-size:10.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--text-muted);
  margin-bottom:8px;
}
.gc-manque{display:flex;align-items:center;gap:9px;padding:4px 0;font-size:12.5px;}
.gc-manque i{width:7px;height:7px;border-radius:50%;flex:0 0 auto;}
.gc-manque span{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.gc-manque strong{color:var(--amber);font-weight:600;font-size:12px;white-space:nowrap;}

/* Message éphémère du plan (déplacement, échange) — moins brutal qu'un alert(). */
.gc-toast{
  position:fixed;left:50%;bottom:88px;transform:translateX(-50%) translateY(140%);
  background:var(--card);border:1px solid var(--card-border);color:var(--text);
  padding:11px 17px;border-radius:var(--r-pill);font-size:12.5px;box-shadow:var(--e2);
  z-index:250;max-width:92vw;text-align:center;pointer-events:none;
  transition:transform .26s cubic-bezier(.2,.9,.3,1.2);
}
.gc-toast.on{transform:translateX(-50%) translateY(0);}
@media (prefers-reduced-motion:reduce){.gc-toast{transition:none;}}

/* Actions « vin » de la feuille d'alvéole (07/09/2026) : deux colonnes pour que six boutons
   tiennent sans transformer la feuille en liste interminable. Même habillage que les boutons
   d'action de la carte de la liste, ce sont les mêmes actions. */
.gc-actions-vin{display:grid;grid-template-columns:1fr 1fr;gap:7px;margin-top:9px;}
.gc-actions-vin .btn{margin:0;font-size:12.5px;padding:9px 8px;}
@media (max-width:359px){.gc-actions-vin{grid-template-columns:1fr;}}

/* =====================================================================================
   Interface bureau (04/09/2026) — le plan tenait dans une colonne de 640px pensée pour le
   téléphone : sur un écran large, un meuble minuscule perdu au milieu du vide. À partir de
   900px il passe en deux colonnes (le meuble à gauche, les outils à droite) et la pagination
   devient de vrais onglets nommés, cliquables à la souris plutôt qu'un swipe.
   L'ordre du DOM (outils → meuble → extras) reste celui du mobile : c'est la grille qui
   replace les blocs, aucune duplication de balisage.
   ===================================================================================== */
@media (min-width: 900px) {
  .gc-inner{max-width:none;}

  .gc-plan-layout{
    display:grid;grid-template-columns:minmax(0,1fr) 320px;gap:26px;align-items:start;
  }
  .gc-plan-tools{grid-column:2;grid-row:1;}
  .gc-plan-main{grid-column:1;grid-row:1 / span 2;min-width:0;}
  .gc-plan-extra{grid-column:2;grid-row:2;}
  .gc-plan-tools .gc-spot,
  .gc-plan-tools .gc-chips{margin-bottom:10px;}
  .gc-actions-sec{display:flex;flex-direction:column;gap:8px;margin-top:14px;}
  .gc-actions-sec .btn{width:100%;}

  /* Les alvéoles grandissent avec la colonne mais restent des culs de bouteille, pas des
     assiettes : plafond de 58px, grille centrée quand le meuble est petit. */
  .gc-cells{grid-template-columns:repeat(var(--places), minmax(0, 58px));justify-content:center;gap:7px;}
  .gc-etage{gap:9px;}
  .gc-etage-no{flex:0 0 18px;font-size:11px;}

  /* Onglets nommés à la place des pastilles */
  .gc-dots{justify-content:flex-start;gap:8px;padding:16px 0 4px;}
  .gc-dot{
    width:auto;height:auto;border-radius:var(--r-pill);background:var(--card);
    border:1px solid var(--card-border);padding:7px 15px;
  }
  .gc-dot.on{background:var(--wine);border-color:var(--wine);width:auto;}
  .gc-dot-nom{display:inline;font-size:12.5px;font-weight:600;color:var(--text-muted);}
  .gc-dot.on .gc-dot-nom{color:#fff;}

  /* Le tiroir a la place d'afficher tout un étage sans faire défiler */
  .gc-strip{flex-wrap:wrap;justify-content:center;}
  .gc-slot-card{width:118px;}

  /* La feuille (fiche, rangement, zone) ne s'étire pas sur toute la largeur */
  #gc-sheet > .card{max-width:640px;}

  /* Le meuble d'aperçu et le constructeur restent en colonne de lecture */
  .gc-preview{max-width:520px;margin:0 auto;}
}

/* Écran vraiment large : on laisse respirer sans que le meuble devienne caricatural. */
@media (min-width: 1240px) {
  .gc-plan-layout{grid-template-columns:minmax(0,1fr) 360px;}
  .gc-cells{grid-template-columns:repeat(var(--places), minmax(0, 66px));}
}

/* ---- Guide interactif de la cave (04/09/2026) ----
   Une barre, pas une modale : le guide doit laisser l'écran utilisable, puisque tout son
   principe est de faire accomplir le geste pour de vrai. */
.gc-guide{
  position:sticky;top:8px;z-index:4;display:flex;align-items:flex-start;gap:12px;
  margin:0 0 14px;padding:12px 14px;border-radius:var(--r-lg);
  background:linear-gradient(160deg, rgba(194,56,91,.22), rgba(194,56,91,.08));
  border:1px solid rgba(194,56,91,.5);box-shadow:var(--glow-wine);
}
.gc-guide-num{
  flex:0 0 auto;font-family:'Newsreader',Georgia,serif;font-size:22px;font-weight:600;
  color:#fff;line-height:1;padding-top:2px;
}
.gc-guide-num span{font-size:12px;color:rgba(255,255,255,.55);}
.gc-guide-txt{flex:1;min-width:0;}
.gc-guide-txt strong{display:block;color:#fff;font-size:14px;margin-bottom:3px;}
.gc-guide-txt span{display:block;color:#f0cfd8;font-size:12.5px;line-height:1.5;}
.gc-guide-quit{
  flex:0 0 auto;background:none;border:none;color:rgba(255,255,255,.62);cursor:pointer;
  font-size:12px;text-decoration:underline;text-underline-offset:3px;padding:2px 0;
}
/* L'élément que l'étape attend, mis en évidence sans bloquer le reste de l'écran. */
.gc-guide-cible{
  outline:2px solid var(--amber);outline-offset:3px;border-radius:var(--r-sm);
  animation:gc-guide-pulse 1.6s ease-in-out infinite;
}
@keyframes gc-guide-pulse{
  0%,100%{box-shadow:0 0 0 0 rgba(255,184,77,.35);}
  50%{box-shadow:0 0 0 7px rgba(255,184,77,0);}
}
@media (prefers-reduced-motion:reduce){.gc-guide-cible{animation:none;}}

.gc-guide-offre{
  padding:17px;border-radius:var(--r-lg);margin:16px 0 4px;
  background:linear-gradient(160deg, rgba(255,184,77,.16), rgba(255,184,77,.04));
  border:1px solid rgba(255,184,77,.45);
}
.gc-guide-offre-titre{
  font-family:'Newsreader',Georgia,serif;font-size:19px;font-weight:600;color:var(--text);
  margin-bottom:7px;
}
.gc-guide-offre p{margin:0 0 14px;font-size:13px;color:#f0d09a;line-height:1.55;}
.gc-guide-offre-actions{display:flex;flex-direction:column;gap:8px;}
.gc-guide-offre-actions .btn{width:100%;margin:0;}


/* Désambiguïsation après un scan (05/09/2026) : « c'est laquelle ? ». Des choix qu'on
   reconnaît à l'œil — la vignette d'étiquette d'abord, le texte ensuite. */
.scan-choix h3{margin:0 0 6px;font-family:'Newsreader',Georgia,serif;}
.scan-choix > p{font-size:13px;line-height:1.5;margin:0 0 14px;}
.choix-item{
  display:flex;align-items:center;gap:12px;width:100%;text-align:left;
  background:var(--card);border:1px solid var(--card-border);border-radius:var(--r-lg);
  padding:10px 12px;margin-bottom:10px;color:var(--text);cursor:pointer;
  font-family:inherit;font-size:14px;transition:border-color .15s, transform .1s;
}
.choix-item:hover{border-color:rgba(255,184,77,.45);}
.choix-item:active{transform:scale(.99);}
.choix-item:disabled{opacity:.5;cursor:default;}
.choix-item.choix-actif{border-color:var(--amber);opacity:1;}
.choix-item img,.choix-vignette-vide{
  width:52px;height:68px;object-fit:cover;border-radius:var(--r-sm);flex:0 0 auto;
}
.choix-vignette-vide{
  display:flex;align-items:center;justify-content:center;font-size:24px;
  background:var(--bg-alt);border:1px dashed var(--card-border);
}
.choix-texte{display:flex;flex-direction:column;gap:2px;min-width:0;}
.choix-texte .muted{font-size:12.5px;}
.choix-autre{border-style:dashed;}


/* Choix du mode de scan (14/09/2026) : bouteille ou carte des vins, présentés côte à côte.
   Avant, la carte des vins était un bouton fantôme sous le bloc photo, introduit par « Ou... » —
   lu comme une option de repli, et jamais ouvert par un seul membre en dix jours. */
.scan-modes{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-bottom:16px;}
.scan-mode{
  display:flex;flex-direction:column;align-items:center;gap:3px;padding:14px 10px;cursor:pointer;
  background:var(--card);border:1px solid var(--card-border);border-radius:var(--radius);
  color:var(--text-muted);font:inherit;text-align:center;
}
.scan-mode:hover{border-color:rgba(255,184,77,.4);}
.scan-mode.on{
  border-color:var(--amber);color:var(--text);
  background:linear-gradient(160deg, rgba(255,184,77,.12), rgba(255,255,255,0) 60%), var(--card);
}
.scan-mode-emoji{font-size:22px;line-height:1;}
.scan-mode-title{font-family:'Newsreader',Georgia,serif;font-size:14.5px;font-weight:600;}
.scan-mode-sub{font-size:11.5px;}

/* Invitation à essayer le plan de cave (14/09/2026) : proposée une seule fois, à qui a déjà
   de quoi ranger. Refusée = plus jamais reproposée (localStorage, comme la bannière push). */
.cave-invite{
  display:flex;align-items:center;gap:12px;margin-bottom:16px;padding:14px;
  background:linear-gradient(160deg, rgba(255,184,77,.10), rgba(255,255,255,0) 60%), var(--card);
  border:1px solid rgba(255,184,77,.3);border-radius:var(--radius);
}
.cave-invite-text{flex:1;min-width:0;}
.cave-invite-title{font-family:'Newsreader',Georgia,serif;font-size:15px;font-weight:600;}
.cave-invite-sub{color:var(--text-muted);font-size:12.5px;margin-top:2px;}
.cave-invite-actions{display:flex;flex-direction:column;gap:6px;flex-shrink:0;}
.cave-invite-actions .btn{width:auto;padding:7px 12px;font-size:12px;}
@media (max-width: 520px){
  .cave-invite{flex-direction:column;align-items:stretch;}
  .cave-invite-actions{flex-direction:row;}
  .cave-invite-actions .btn{flex:1;}
}

/* Confirmation d'un champ douteux après un scan (14/09/2026, idée de Damien) — cf.
   wine_service.champs_a_confirmer. Volontairement sobre : c'est une étape de passage, pas un
   formulaire. Elle n'apparaît que sur doute réel, jamais sur un scan bien lu. */
.scan-confirm h3{margin-top:0;}
.confirm-champ{margin:16px 0;}
.confirm-champ label{display:block;margin-bottom:6px;font-size:14px;}
.confirm-champ .field-input{margin:0;}
.confirm-couleurs{display:flex;flex-wrap:wrap;gap:8px;}
.confirm-couleur{
  padding:9px 13px;border-radius:var(--r-pill);cursor:pointer;font:inherit;font-size:13px;
  background:var(--card);border:1px solid var(--card-border);color:var(--text-muted);
}
.confirm-couleur.on{border-color:var(--amber);color:var(--text);background:rgba(255,184,77,.12);}

/* Lien d'invitation (15/09/2026, ROADMAP 9.2). */
.invite-row{display:flex;gap:8px;}
.invite-row .field-input{flex:1;min-width:0;font-size:13px;}
.invite-de{
  background:linear-gradient(160deg, rgba(255,184,77,.12), rgba(255,255,255,0) 60%), var(--card);
  border:1px solid rgba(255,184,77,.3);border-radius:var(--radius);
  padding:12px 14px;margin:0 0 16px;font-size:14px;line-height:1.5;
}
/* Page de sondage (15/09/2026) : cible des boutons d'un email. La grille sert aux sondages
   à choix multiple (20 options) ; la rangée simple reste pour un oui/non. */
.sondage-options{display:flex;flex-wrap:wrap;gap:8px;margin-top:18px;}
.sondage-options.en-grille{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));}
@media (max-width:420px){ .sondage-options.en-grille{grid-template-columns:1fr;} }
.sondage-opt{
  flex:1;min-width:130px;padding:13px 12px;border-radius:var(--radius);cursor:pointer;
  background:var(--card);border:1px solid var(--card-border);color:var(--text-muted);
  font:inherit;font-size:14px;text-align:center;display:flex;flex-direction:column;gap:2px;
  transition:opacity .15s,border-color .15s;
}
.sondage-opt:hover{border-color:rgba(255,184,77,.4);}
.sondage-opt.on{
  border-color:var(--amber);color:var(--text);
  background:linear-gradient(160deg, rgba(255,184,77,.14), rgba(255,255,255,0) 60%), var(--card);
}
.sondage-opt.sature{opacity:.4;}
.sondage-nom{font-family:'Newsreader',Georgia,serif;font-size:16px;font-weight:600;}
.sondage-sens{font-size:11.5px;color:var(--text-muted);}
.sondage-compteur{
  font-size:12.5px;color:var(--text-muted);margin:16px 0 0;letter-spacing:.06em;
  text-transform:uppercase;
}
.sondage-compteur.complet{color:var(--amber);}
/* Message anti-spam du premier écran (16/09/2026) : présent mais discret — c'est une
   consigne utile, pas une alerte. */
.welcome-mail{
  margin-top:18px; text-align:left; background:var(--bg-alt);
  border:1px solid var(--card-border); border-left:3px solid var(--amber);
  border-radius:0 10px 10px 0; padding:12px 14px;
}
.welcome-mail-titre{font-weight:600; font-size:13.5px; margin-bottom:5px;}
.welcome-mail p{margin:0; font-size:12.5px; line-height:1.55; color:var(--text-muted);}

/* Encart d'installation iOS (15/09/2026) : des étapes à suivre, pas un paragraphe — la
   personne les lit le téléphone en main. */
.ios-steps{margin:12px 0;padding-left:0;list-style:none;counter-reset:ios;
  display:flex;flex-direction:column;gap:9px;}
.ios-steps li{counter-increment:ios;display:grid;grid-template-columns:24px 1fr;gap:10px;
  align-items:center;font-size:13.5px;color:var(--text);}
.ios-steps li::before{content:counter(ios);font-size:11.5px;color:var(--wine);
  border:1px solid var(--card-border);border-radius:50%;width:24px;height:24px;
  display:grid;place-items:center;}
.ios-inapp{font-size:12.5px;color:var(--text);background:rgba(255,184,77,.08);
  border-left:3px solid var(--amber);border-radius:0 8px 8px 0;padding:10px 12px;margin:12px 0 0;}
.ios-share{display:inline-block;border:1px solid var(--card-border);border-radius:5px;
  padding:0 5px;font-size:12px;}


/* Appel à inviter au démarrage (15/09/2026) — plus chaleureux que le sélecteur de quantité
   dont il reprend le fond : c'est une demande personnelle, pas une boîte de dialogue. */
.invite-prompt-carte{max-width:380px;text-align:left;}
.invite-prompt-emoji{font-size:40px;line-height:1;text-align:center;}
.invite-prompt-titre{
  font-family:'Newsreader',Georgia,serif;font-size:23px;text-align:center;margin:10px 0 16px;
  color:var(--amber);
}
.invite-prompt-texte{font-size:14px;line-height:1.6;margin:0 0 10px;}

/* Page de vote publique (15/09/2026) : le visiteur n'a pas de compte et découvre l'appli
   en même temps que la question. */
.vote-intro{
  font-size:14.5px;line-height:1.6;margin:0 0 18px;padding:14px;border-radius:var(--radius);
  background:linear-gradient(160deg, rgba(255,184,77,.10), rgba(255,255,255,0) 60%), var(--card);
  border:1px solid rgba(255,184,77,.3);
}
.vote-intro .muted{font-size:13px;}
/* --- Bulle « Écris-moi » + feuille de retour (17/09/2026) ---------------------------------
   Fixe en bas à droite sur tous les écrans, au-dessus du menu du bas sur mobile. Un bouton
   de 52px plutôt qu'un champ de saisie permanent : une zone de texte collée en bas mangeait
   60-70px de hauteur sur CHAQUE écran, pour un geste qu'on fait une fois de temps en temps.
   z-index 30 = même étage que la pastille de compte et la cloche, donc toujours sous les
   feuilles/modales (200+) : la bulle ne doit jamais flotter par-dessus ce qu'on lit. */
.feedback-fab{
  position:fixed;right:16px;bottom:calc(env(safe-area-inset-bottom, 0px) + 20px);z-index:30;
  width:52px;height:52px;border-radius:50%;border:1px solid rgba(255,255,255,.14);
  background:linear-gradient(160deg, var(--wine), #6b1230);
  box-shadow:0 10px 26px rgba(0,0,0,.45);cursor:pointer;
  display:flex;align-items:center;justify-content:center;padding:0;
  transition:transform .15s ease, box-shadow .15s ease;
}
.feedback-fab:active{transform:scale(.93);}
.feedback-fab-emoji{font-size:23px;line-height:1;}
@media (max-width: 720px){
  /* Au-dessus du menu du bas (68px), jamais dedans : il est déjà plein et le FAB scan y est
     centré — cf. le piège de parité documenté dans index.html. */
  .feedback-fab{bottom:calc(env(safe-area-inset-bottom, 0px) + 80px);right:14px;}
}
/* Mise en évidence après le mot de bienvenue : trois pulsations, puis plus rien. Sert une
   seule fois, à montrer où est la bulle dont le mot vient de parler. */
@keyframes feedback-fab-pulse{
  0%{box-shadow:0 10px 26px rgba(0,0,0,.45), 0 0 0 0 rgba(140,21,53,.6);}
  70%{box-shadow:0 10px 26px rgba(0,0,0,.45), 0 0 0 18px rgba(140,21,53,0);}
  100%{box-shadow:0 10px 26px rgba(0,0,0,.45), 0 0 0 0 rgba(140,21,53,0);}
}
.feedback-fab.pulse{animation:feedback-fab-pulse 1.4s ease-out 3;}
.feedback-carte{max-width:400px;text-align:left;}
.feedback-carte .qty-picker-title{text-align:left;}
.mot-bulle-demo{
  display:inline-flex;align-items:center;justify-content:center;width:26px;height:26px;
  border-radius:50%;background:linear-gradient(160deg, var(--wine), #6b1230);
  font-size:13px;vertical-align:middle;margin:0 1px;
}

/* Sonde du matin, Admin > Usage IA (18/09/2026) : l'état se lit d'un coup d'œil, l'alerte se
   détache du reste de la carte sans crier. */
.sonde-tete{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-bottom:10px;}
.sonde-etat{font-size:12px;font-weight:600;border-radius:var(--r-pill);padding:3px 10px;}
.sonde-etat.ok{background:rgba(49,208,170,.15);color:var(--green);}
.sonde-etat.ko{background:rgba(255,107,122,.15);color:var(--red);}
.sonde-etat.attente{background:rgba(255,184,77,.14);color:var(--amber);}
.sonde-alerte{background:rgba(255,107,122,.08);border-left:3px solid var(--red);
  border-radius:0 8px 8px 0;padding:10px 12px;margin:0 0 12px;font-size:13.5px;}
.sonde-repli{font-size:11px;color:var(--amber);border:1px solid currentColor;border-radius:6px;
  padding:0 5px;margin-left:4px;}

/* Badges d'événement et énigmes (18/09/2026). Pendant la fenêtre d'un événement, la médaille
   pas encore gagnée reprend des couleurs : c'est le moment d'agir, elle ne doit pas dormir
   grisée comme les autres. */
.medal.locked.en-cours{opacity:1;border-color:rgba(255,184,77,.55);}
.medal.locked.en-cours .medal-coin{filter:none;opacity:1;}
.medal.locked.en-cours .medal-tag{color:var(--amber);font-weight:600;}
.badge-enigme{font-family:'Newsreader',Georgia,serif;font-style:italic;font-size:16px;color:var(--text);}

/* « Débloqué grâce à » dans le détail d'un badge (18/09/2026, idée d'Aurélie). */
.badge-preuve{display:grid;grid-template-columns:52px 1fr 14px;gap:12px;align-items:center;width:100%;
  text-align:left;background:var(--bg-alt);border:1px solid var(--card-border);border-radius:var(--r-sm);
  padding:10px 12px;margin:0 0 14px;color:var(--text);font:inherit;cursor:pointer}
.badge-preuve:hover{border-color:rgba(255,184,77,.45)}
.badge-preuve:focus-visible{outline:2px solid var(--amber);outline-offset:2px}
.badge-preuve-img{width:52px;height:52px;border-radius:8px;overflow:hidden;background:var(--card-border);
  display:grid;place-items:center;font-size:22px}
.badge-preuve-img img{width:100%;height:100%;object-fit:cover}
.badge-preuve-texte{display:flex;flex-direction:column;gap:2px;min-width:0;font-size:13.5px;line-height:1.35}
.badge-preuve-texte strong{overflow-wrap:anywhere}
.badge-preuve-lab{font-size:10.5px;letter-spacing:1.2px;text-transform:uppercase;color:var(--text-muted)}
.badge-preuve-detail{color:var(--amber);font-size:12.5px}
.badge-preuve-fleche{color:var(--text-muted);font-size:20px}

/* Logotype Vinifique (bascule du 23/09/2026) : SVG vectorisé, aucune police à charger. */
.brand-logo{display:block;height:26px;width:auto;}
.auth-card h1 .brand-logo-auth{height:40px;margin:2px 0 6px;}
