/* ============================================================
   ÉTOILIA — Identité visuelle
   Palette "nuit étoilée douce" constante à travers tous les
   mondes : seul l'accent (--accent) change selon le monde actif.
   ============================================================ */

/* Baloo 2 AUTO-HÉBERGÉE (police VARIABLE : un seul fichier couvre les
   graisses 500-800). Avant : Google Fonts, seule dépendance réseau de l'app
   — hors-ligne, toute la typo retombait sur une police système. Le bloc
   latin couvre aussi œ/Œ (U+0152-0153) ; latin-ext = filet accents rares. */
@font-face {
  font-family: 'Baloo 2';
  font-style: normal;
  font-weight: 500 800;
  font-display: swap;
  src: url('../assets/fonts/baloo2-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Baloo 2';
  font-style: normal;
  font-weight: 500 800;
  font-display: swap;
  src: url('../assets/fonts/baloo2-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
  --night: #2d2a6e;
  --night-deep: #1e1b4f;
  --violet: #6c5ce7;
  --violet-soft: #a89df0;
  --pink: #ff8fc8;
  --pink-soft: #ffd3e9;
  --star: #ffd66b;
  --star-deep: #f5a623;
  --turquoise: #5bd6d6;
  --green: #7ed98b;
  --red-soft: #ff7e79;
  --cream: #fdf8ff;
  --card: #ffffff;
  --ink: #3a2e58;
  --ink-soft: #8a7fa8;
  --accent: var(--violet);
  --radius-lg: 26px;
  --radius-md: 18px;
  --radius-sm: 12px;
  --shadow: 0 6px 20px rgba(45, 42, 110, 0.13);
  --shadow-soft: 0 3px 10px rgba(45, 42, 110, 0.09);
  --font: 'Baloo 2', 'Comic Sans MS', 'Segoe UI', sans-serif;
}

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

html, body {
  margin: 0;
  padding: 0;
  font-family: var(--font);
  color: var(--ink);
  /* fond doux « ciel d'Étoilia » (E1) ; le dégradé sert de secours et
     transparaît si l'image n'existe pas */
  background: url('../assets/ui/bg.png') center top / cover no-repeat fixed,
    linear-gradient(180deg, #efe9ff 0%, #fdf3ff 45%, #fff8ec 100%);
  min-height: 100%;
  /* tablette/iOS : neutralise le pinch-zoom et le double-tap-zoom (iOS
     IGNORE user-scalable=no : sans ça, une enfant peut zoomer l'interface
     et rester bloquée dans une vue agrandie). */
  touch-action: manipulation;
}

body {
  overscroll-behavior-y: none;
  /* appui long = geste du JEU (décocher, valider) : on neutralise la
     sélection de texte et la loupe/menu contextuel iOS qui surgissaient
     pendant le maintien. Ré-autorisé sur les champs (écran Parents). */
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}
input, textarea {
  -webkit-user-select: text;
  user-select: text;
}

button {
  font-family: var(--font);
  border: none;
  cursor: pointer;
  background: none;
  color: inherit;
}

input, select, textarea {
  font-family: var(--font);
  font-size: 16px;
  border: 2px solid #e4dcf5;
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  color: var(--ink);
  background: #fff;
  width: 100%;
}
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--violet-soft);
}

#app {
  max-width: 540px;
  margin: 0 auto;
  min-height: 100vh; /* fallback vieilles WebView (dvh ignoré) */
  min-height: 100dvh; /* dvh : suit le viewport réel (le vh débordait de 10 px) */
  display: flex;
  flex-direction: column;
  position: relative;
}

/* ---------- Splash ---------- */
.boot-splash {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-height: 80vh;
  gap: 12px;
  color: var(--ink-soft);
}
.boot-star { font-size: 64px; animation: spin-soft 2.4s ease-in-out infinite; }
@keyframes spin-soft {
  0%, 100% { transform: rotate(-12deg) scale(1); }
  50% { transform: rotate(12deg) scale(1.15); }
}

/* ---------- En-tête ---------- */
.header {
  position: sticky;
  top: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 12px;
  background: linear-gradient(135deg, var(--night) 0%, var(--violet) 100%);
  color: #fff;
  border-radius: 0 0 var(--radius-lg) var(--radius-lg);
  box-shadow: var(--shadow);
}
.header .mascot { width: 30px; height: 30px; flex: none; }
.h-spacer { flex: 1; }
/* pastille profil : transparente sur la barre bleue (écrans-menu) */
.profile-chip {
  display: flex; align-items: center; gap: 7px;
  background: none; border: none; padding: 2px 4px;
  color: #fff; cursor: pointer; flex: none;
}
.profile-chip .name {
  font-size: 16px; font-weight: 800; color: inherit;
  max-width: 130px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* ====== BANDEAU INTÉGRÉ : flotte par-dessus la scène (Chemin, Maison) ======
   plus de bande bleue séparée — des pastilles diégétiques lisibles posées
   sur l'image (clics qui traversent partout SAUF sur les pastilles). */
.header-float {
  position: absolute; top: 0; left: 0; right: 0;
  background: none; box-shadow: none; border-radius: 0;
  /* ⚠️ PAS d'inset ici : en paysage la Dynamic Island est sur le FLANC (à
     mi-hauteur), pas dans les angles. Les insets repoussaient inutilement le
     prénom et la roue vers le centre — « ça fait bizarre » (Bruno 2026-09-08).
     Les coins restent donc les coins. */
  padding: 10px 14px;
  z-index: 40;
  pointer-events: none;
}
.header-float > * { pointer-events: auto; }
.header-float .h-spacer { pointer-events: none; }
.header-float .profile-chip {
  background: rgba(255, 255, 255, 0.86);
  border-radius: 999px; padding: 3px 13px 3px 5px;
  box-shadow: var(--shadow-soft);
  color: var(--night);
}
.header-float .currency {
  background: rgba(255, 255, 255, 0.86);
  color: var(--night); box-shadow: var(--shadow-soft);
}
.header-float .currency #cur-stars { color: var(--star-deep); }
.header-float .currency img { filter: none; }
/* Roue des réglages et compteur d'étoiles AGRANDIS (retour Bruno 2026-09-08 :
   « trop petits »). Ce sont les deux seuls repères permanents de l'écran, et
   la roue est une cible tactile pour un adulte : 48 px, le minimum iOS. */
.header-float .gear-btn {
  background: rgba(255, 255, 255, 0.86);
  border-radius: 50%; padding: 0;
  /* flex: none + min-width : sans ça le bouton se faisait écraser par le
     conteneur flex et sortait à 28 px de large au lieu de 48. */
  flex: none; width: 48px; min-width: 48px; height: 48px;
  display: grid; place-items: center;
  box-shadow: var(--shadow-soft); opacity: 1;
}
.header-float .gear-btn img, .header-float .gear-btn svg { width: 28px; height: 28px; }
.header-float .currency {
  flex: none;
  padding: 7px 15px; gap: 6px; font-size: 19px; font-weight: 800;
}
.header-float .currency img { width: 24px !important; height: 24px !important; }
.currency {
  display: flex;
  align-items: center;
  gap: 4px;
  background: rgba(255, 255, 255, 0.16);
  border-radius: 999px;
  padding: 3px 9px 3px 6px;
  font-weight: 800;
  font-size: 14px;
}
.currency .ico { font-size: 18px; }
/* halo clair derrière les icônes-images de monnaie : le cristal sombre
   restait illisible sur l'en-tête violet */
.currency img { filter: drop-shadow(0 0 3px rgba(255, 255, 255, 0.95)) drop-shadow(0 0 6px rgba(255, 255, 255, 0.55)); }
.currency.bump { animation: bump 0.5s ease; }
@keyframes bump {
  0% { transform: scale(1); }
  40% { transform: scale(1.35); background: rgba(255, 255, 255, 0.35); }
  100% { transform: scale(1); }
}
.gear-btn {
  font-size: 18px;
  opacity: 0.75;
  padding: 6px;
}

/* ---------- Contenu ---------- */
.screen {
  flex: 1;
  padding: 16px 14px 18px;
  animation: screen-in 0.25s ease;
}
/* glissement sans fondu : un rendu figé à la première image reste lisible
   (les captures d'écran de l'outil de preview gèlent les animations) */
@keyframes screen-in {
  from { transform: translateY(10px); }
  to { transform: none; }
}

.section-title {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 18px;
  font-weight: 800;
  margin: 18px 4px 10px;
}
.section-title .big { font-size: 24px; }
.section-title.dim { opacity: 0.55; }

/* ---------- Missions ---------- */
.day-banner {
  display: flex;
  align-items: center;
  gap: 12px;
  background: linear-gradient(135deg, #fff 0%, #f3edff 100%);
  border-radius: var(--radius-lg);
  padding: 12px 16px;
  box-shadow: var(--shadow-soft);
  margin-bottom: 6px;
}
.day-banner .ring { flex: none; }
.day-banner .txt { flex: 1; }
.day-banner .txt .t1 { font-weight: 800; font-size: 17px; }
.day-banner .txt .t2 { font-size: 13px; color: var(--ink-soft); }

.task-card {
  display: flex;
  align-items: center;
  gap: 12px;
  background: var(--card);
  border-radius: var(--radius-lg);
  padding: 12px 14px;
  box-shadow: var(--shadow-soft);
  border: 3px solid transparent;
  transition: transform 0.15s ease, border-color 0.2s, opacity 0.3s;
  text-align: left;
  width: 100%;
}
.task-card:active { transform: scale(0.97); }
.task-card .t-emoji {
  font-size: 38px;
  width: 56px;
  height: 56px;
  flex: none;
  display: grid;
  place-items: center;
  background: #f5f0ff;
  border-radius: 18px;
}
.task-card .t-label {
  flex: 1;
  font-size: 17px;
  font-weight: 700;
  line-height: 1.15;
}
.task-card .t-stars {
  font-size: 13px;
  font-weight: 800;
  color: var(--star-deep);
  white-space: nowrap;
}
.task-card .t-check {
  width: 34px;
  height: 34px;
  flex: none;
  border-radius: 50%;
  border: 3px dashed #d9d0ef;
  display: grid;
  place-items: center;
  font-size: 18px;
  color: transparent;
}
.task-card.done {
  border-color: var(--green);
  background: #f2fcf3;
  opacity: 0.85;
}
.task-card.done .t-check {
  border: none;
  background: var(--green);
  color: #fff;
  animation: pop-check 0.4s cubic-bezier(0.2, 1.8, 0.4, 1);
}
.task-card.done .t-label { text-decoration: line-through; text-decoration-color: #b9e5bf; }
.task-card .t-parent {
  font-size: 12px;
  color: var(--violet);
  font-weight: 700;
}
@keyframes pop-check {
  0% { transform: scale(0); }
  100% { transform: scale(1); }
}

.all-done-card {
  text-align: center;
  padding: 22px 16px;
  background: linear-gradient(135deg, #fff6dd 0%, #ffeef9 100%);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-soft);
  margin-top: 14px;
}
.all-done-card .big-emoji { font-size: 52px; }
.all-done-card .t1 { font-size: 20px; font-weight: 800; margin-top: 4px; }
.all-done-card .t2 { color: var(--ink-soft); font-size: 14px; }

/* ---------- Scène / Monde ---------- */
.scene {
  position: relative;
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: var(--shadow);
  min-height: 340px;
  display: flex;
  align-items: flex-end;
  justify-content: center;
}
.scene .decor {
  position: absolute;
  font-size: 26px;
  animation: float 4s ease-in-out infinite;
  filter: drop-shadow(0 2px 3px rgba(0, 0, 0, 0.12));
}
@keyframes float {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-12px); }
}
.scene .ground {
  position: absolute;
  bottom: -40px;
  left: -10%;
  width: 120%;
  height: 110px;
  border-radius: 50%;
}
.scene .char-slot {
  position: relative;
  z-index: 5;
  width: 220px;
  margin-bottom: 6px;
  animation: idle 3.2s ease-in-out infinite;
  transform-origin: bottom center;
}
/* le compagnon est VIVANT : respiration + léger flottement + balancement très
   subtil (partagé Chemin + Maison via .scene/.maison-scene .char-slot ;
   transform-origin déjà en bottom center → pivote/respire depuis les pieds) */
@keyframes idle {
  0%   { transform: translateY(0) scaleY(1) rotate(0deg); }
  25%  { transform: translateY(-3px) scaleY(1.012) rotate(-0.7deg); }
  50%  { transform: translateY(1px) scaleY(0.985) rotate(0deg); }
  75%  { transform: translateY(-2px) scaleY(1.006) rotate(0.7deg); }
  100% { transform: translateY(0) scaleY(1) rotate(0deg); }
}
/* accessibilité : aucune animation continue si l'utilisateur réduit le mouvement */
@media (prefers-reduced-motion: reduce) {
  .char-slot, .scene .char-slot, .maison-scene .char-slot,
  .char-slot.char-react, .char-slot.char-evolve { animation: none !important; }
}
.scene .world-name {
  position: absolute;
  top: 10px;
  left: 12px;
  z-index: 6;
  background: rgba(255, 255, 255, 0.85);
  border-radius: 999px;
  padding: 5px 14px;
  font-weight: 800;
  font-size: 14px;
  color: var(--ink);
  box-shadow: var(--shadow-soft);
}

/* ---------- Boutons ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  border-radius: 18px;
  padding: 13px 18px;
  font-size: 16px;
  font-weight: 800;
  background: var(--accent);
  color: #fff;
  box-shadow: 0 4px 0 rgba(45, 42, 110, 0.25);
  transition: transform 0.1s ease, filter 0.15s;
}
.btn:active { transform: translateY(3px); box-shadow: 0 1px 0 rgba(45, 42, 110, 0.25); }
.btn.secondary { background: #efe9fb; color: var(--ink); box-shadow: 0 4px 0 #ddd2f2; }
.btn.star { background: linear-gradient(135deg, var(--star) 0%, var(--star-deep) 100%); color: #6b4a00; box-shadow: 0 4px 0 #d18f1d; }
.btn.green { background: var(--green); box-shadow: 0 4px 0 #57b966; }
.btn.pink { background: var(--pink); box-shadow: 0 4px 0 #e76aa8; }
.btn.danger { background: var(--red-soft); box-shadow: 0 4px 0 #d65550; }
.btn.small { padding: 8px 14px; font-size: 14px; border-radius: 14px; }
.btn.block { width: 100%; }
.btn:disabled { filter: grayscale(0.7); opacity: 0.6; }

/* ---------- Boutique / grilles ---------- */
.tabs {
  display: flex;
  gap: 6px;
  background: #efe9fb;
  padding: 5px;
  border-radius: 999px;
  margin-bottom: 14px;
}
.tabs button {
  flex: 1;
  border-radius: 999px;
  padding: 9px 4px;
  font-weight: 800;
  font-size: 13.5px;
  color: var(--ink-soft);
}
.tabs button.active {
  background: #fff;
  color: var(--ink);
  box-shadow: var(--shadow-soft);
}

.shop-item {
  background: var(--card);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-soft);
  padding: 14px 10px 12px;
  text-align: center;
  border: 3px solid transparent;
  position: relative;
}
.shop-item.owned { border-color: var(--green); }
.shop-item.active-item { border-color: var(--star-deep); }
.shop-item .preview {
  height: 110px;
  display: grid;
  place-items: center;
  margin-bottom: 6px;
}
.shop-item .preview svg { max-height: 110px; max-width: 100%; }
.shop-item .preview .big { font-size: 56px; }
.shop-item .i-name { font-weight: 800; font-size: 15px; line-height: 1.1; }
.shop-item .i-sub { font-size: 12px; color: var(--ink-soft); margin-bottom: 8px; }
.shop-item .badge {
  position: absolute;
  top: 8px;
  right: 10px;
  font-size: 16px;
}

/* ---------- Cadeaux ---------- */
.gift-card {
  display: flex;
  align-items: center;
  gap: 12px;
  background: var(--card);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-soft);
  padding: 12px 14px;
}
.gift-card .g-emoji {
  font-size: 34px;
  width: 54px;
  height: 54px;
  flex: none;
  display: grid;
  place-items: center;
  background: #fff4f9;
  border-radius: 16px;
}
.gift-card .g-label { flex: 1; font-weight: 700; font-size: 15.5px; line-height: 1.15; }
.gift-card .g-label .g-sub { font-size: 12.5px; color: var(--ink-soft); font-weight: 600; }
@keyframes shake {
  0%, 100% { transform: rotate(0); }
  20% { transform: rotate(-10deg) scale(1.05); }
  40% { transform: rotate(10deg) scale(1.1); }
  60% { transform: rotate(-8deg) scale(1.12); }
  80% { transform: rotate(6deg) scale(1.08); }
}

/* ---------- Bibliothèque d'albums (v2) ---------- */
.library-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
}
.library-item {
  text-align: center;
  padding: 0;
}
.library-item:active .cover-wrap { transform: scale(0.94); }
.cover-wrap {
  position: relative;
  transition: transform 0.15s ease;
  filter: drop-shadow(0 5px 10px rgba(45, 42, 110, 0.25));
}
.cover-img {
  width: 100%;
  display: block;
}
.cover-fallback {
  aspect-ratio: 3 / 4;
  display: grid;
  place-items: center;
  font-size: 44px;
  background: #efe9fb;
  border-radius: 12px;
}
.cover-locked { filter: grayscale(0.9) brightness(0.75); }
.cover-badge {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  font-size: 30px;
  filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.4));
}
.library-item .i-name { font-weight: 800; font-size: 13px; line-height: 1.1; margin-top: 6px; }
.library-item .i-sub { font-size: 11.5px; color: var(--ink-soft); font-weight: 700; }
.cover-chip { display: none; }

/* révélation d'un accessoire débloqué (récompense à l'évolution) :
   pop élastique, transform uniquement (pas d'opacity:0 — captures preview) */
@keyframes accPop {
  0%   { transform: scale(0.15) rotate(-10deg); }
  65%  { transform: scale(1.12) rotate(4deg); }
  100% { transform: scale(1) rotate(0deg); }
}
.shop-item.acc-reveal {
  animation: accPop 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) both;
  background: radial-gradient(120% 120% at 50% 0%, #fff 58%, #fdf3ff 100%);
  border: none;
  border-radius: 22px;
  box-shadow: 0 14px 34px rgba(150, 90, 200, 0.22), var(--shadow-soft);
}
/* carte d'accessoire mystère : cadeau fermé, on ne paie que pour découvrir */
.shop-item.acc-mystery {
  background: var(--card);
}

/* bibliothèque en bois (E2) : couvertures posées sur les planches */
/* bornée en paysage pour tenir SANS scroll vertical */
.library-shelf { position: relative; margin: 0 auto 10px; max-width: min(520px, 60vh); }
.library-shelf .shelf-img { width: 100%; display: block; filter: drop-shadow(0 6px 12px rgba(45, 42, 110, 0.2)); }
.library-item.on-shelf {
  position: absolute;
  width: 22%;
  transform: translateX(-50%);
  padding: 0;
}
.library-item.on-shelf .i-name,
.library-item.on-shelf .i-sub { display: none; }
.library-item.on-shelf .cover-chip {
  display: block;
  position: absolute;
  bottom: -7px;
  left: 50%;
  transform: translateX(-50%);
  background: #fff;
  border-radius: 999px;
  padding: 2px 9px;
  font-size: 11.5px;
  font-weight: 800;
  color: var(--ink);
  box-shadow: var(--shadow-soft);
  white-space: nowrap;
}
.library-item.on-shelf .cover-wrap { filter: drop-shadow(0 4px 6px rgba(45, 42, 110, 0.35)); }

/* pièces cachées de l'image mystère — surface « emballage magique » */
.puzzle-cover {
  position: absolute;
  display: grid;
  place-items: center;
  background:
    radial-gradient(120% 80% at 28% 22%, rgba(255, 255, 255, 0.5) 0%, rgba(255, 255, 255, 0) 42%),
    radial-gradient(90% 70% at 80% 80%, rgba(255, 214, 107, 0.16) 0%, rgba(255, 214, 107, 0) 45%),
    linear-gradient(150deg, #8f80f2 0%, #6c5ce7 50%, #5a49c4 100%);
  box-shadow:
    inset 0 2px 3px rgba(255, 255, 255, 0.55),
    inset 0 -7px 14px rgba(45, 42, 110, 0.5),
    inset 0 0 0 1px rgba(255, 255, 255, 0.16);
  padding: 0;
  cursor: pointer;
  overflow: hidden;
  transition: transform 0.12s ease, filter 0.15s;
}
/* poussière d'étoiles (texture d'emballage) */
.puzzle-cover::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image:
    radial-gradient(circle, rgba(255, 255, 255, 0.8) 0, rgba(255, 255, 255, 0) 1.6px),
    radial-gradient(circle, rgba(255, 255, 255, 0.55) 0, rgba(255, 255, 255, 0) 1.3px),
    radial-gradient(circle, rgba(255, 214, 107, 0.6) 0, rgba(255, 214, 107, 0) 1.3px);
  background-size: 48px 48px, 48px 48px, 48px 48px;
  background-position: 9px 13px, 31px 35px, 39px 7px;
  opacity: 0.7;
}
/* reflet qui balaie doucement la case (effet « brillant à gratter ») */
.puzzle-cover::after {
  content: "";
  position: absolute;
  top: 0;
  left: -65%;
  width: 55%;
  height: 100%;
  background: linear-gradient(100deg, transparent, rgba(255, 255, 255, 0.4), transparent);
  transform: skewX(-18deg);
  pointer-events: none;
  animation: puzzle-shimmer 3.8s ease-in-out infinite;
}
@keyframes puzzle-shimmer {
  0%, 72% { left: -65%; }
  100% { left: 135%; }
}
.puzzle-cover:active { transform: scale(0.93); filter: brightness(1.12); }
.puzzle-cover .qm {
  display: flex;
  align-items: center;
  gap: 3px;
  font-size: 14px;
  font-weight: 800;
  color: #6b4a00;
  background: linear-gradient(180deg, #ffe9ad 0%, #ffd66b 100%);
  border: 1.5px solid #fff3cf;
  border-radius: 999px;
  padding: 3px 9px 3px 8px;
  box-shadow: 0 2px 4px rgba(45, 42, 110, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.75);
}
.puzzle-cover.revealing {
  animation: piece-reveal 0.5s ease forwards;
  pointer-events: none;
}
@keyframes piece-reveal {
  to { opacity: 0; transform: scale(1.35) rotate(8deg); }
}

/* ---------- Albums de gommettes ---------- */
.album-scene {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 10;
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: var(--shadow);
}

/* ---------- Modales ---------- */
.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(30, 27, 79, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 100;
  padding: 20px;
  animation: fade-in 0.2s ease;
}
@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }
.modal {
  position: relative;
  background: var(--cream);
  border-radius: var(--radius-lg);
  width: 100%;
  max-width: 500px;
  max-height: 92vh; /* fallback vieilles WebView (dvh ignoré) */
  max-height: 92dvh;        /* tient en paysage */
  overflow: hidden;         /* PAS de scroll vertical : le contenu s'adapte */
  display: flex; flex-direction: column;
  padding: 16px 16px 14px;
  box-shadow: 0 16px 50px rgba(0, 0, 0, 0.3);
  animation: modal-in 0.25s cubic-bezier(0.2, 1.4, 0.4, 1);
  text-align: center;
}
@keyframes modal-in {
  from { transform: scale(0.85) translateY(20px); }
  to { transform: none; }
}
.modal h3 { margin: 2px 0 8px; font-size: 18px; }
.modal .modal-emoji { font-size: 52px; }
.modal .modal-actions {
  display: flex;
  gap: 10px;
  margin-top: 14px;
}
.modal .modal-actions .btn { flex: 1; }

/* ---------- Formulaires de l'écran Parents ---------- */
/* La modale est en `overflow: hidden` et `locked` (ni croix ni clic
   extérieur) : si le formulaire dépasse, les boutons deviennent INJOIGNABLES
   et on est piégé dans la popup (vécu sur iPhone en paysage — impossible
   d'ajouter une mission). Ici : le CORPS défile, le titre et les boutons
   restent ancrés, donc « Enregistrer » est TOUJOURS atteignable. */
.modal.modal-form > div {
  display: flex; flex-direction: column;
  flex: 1 1 auto; min-height: 0;
  text-align: left; width: 100%;
}
.modal-form h3, .modal-form .modal-actions { flex: none; }
.modal-form .form-scroll {
  flex: 1 1 auto; min-height: 0;
  overflow-y: auto; -webkit-overflow-scrolling: touch;
  padding-right: 4px;
}
/* la grille d'emojis suggérés ne doit pas manger tout le formulaire */
.modal-form .emoji-sug { max-height: 30vh; overflow-y: auto; }

/* ---------- Mission : validation en DEUX temps ---------- */
/* 1er toucher = la carte s'ouvre sur une croix et une coche ; seule la coche
   crédite les étoiles (demande de Bruno 2026-09-08). Les ronds sont ceux des
   popups de confirmation : même langage visuel partout. */
.tc-confirm {
  position: absolute; inset: 0; z-index: 3;
  display: none; align-items: center; justify-content: center; gap: 16px;
  border-radius: inherit;
  background: rgba(255, 255, 255, 0.95);
  -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px);
}
.task-card.mh-card.armed .tc-confirm {
  display: flex;
  animation: tc-ouvre 0.16s ease-out;
}
@keyframes tc-ouvre { from { opacity: 0; transform: scale(0.94); } to { opacity: 1; transform: none; } }
/* les ronds s'adaptent aux cartes courtes (64 px de haut au minimum) */
.tc-confirm .choice-round { width: 48px; height: 48px; }
.m-grid.solo .tc-confirm .choice-round { width: clamp(44px, 7.5vh, 60px); height: clamp(44px, 7.5vh, 60px); }
/* VERT pour valider : même code couleur que le ✓ des missions réussies */
.tc-confirm .choice-round.yes {
  background: linear-gradient(135deg, #7fd88f 0%, var(--green) 100%);
  box-shadow: 0 5px 0 #4aa863, 0 9px 16px rgba(60, 170, 100, 0.32);
}
/* la carte armée se distingue du reste */
.task-card.mh-card.armed { box-shadow: 0 0 0 3px var(--violet-soft), 0 6px 16px rgba(45, 42, 110, 0.18); }

/* ---------- Gain d'étoiles : « +N ⭐ » qui jaillit ---------- */
/* L'enfant ne lit pas : le CHIFFRE et l'étoile disent à eux seuls ce qui
   vient d'être gagné (demande de Bruno 2026-09-08). */
.gain-stars {
  position: fixed; z-index: 90; pointer-events: none;
  transform: translate(-50%, -50%);
  display: flex; align-items: center; gap: 6px;
  padding: 8px 16px 8px 14px;
  border-radius: 999px;
  background: linear-gradient(180deg, #fffdf5, #fff2c9);
  box-shadow: 0 10px 26px rgba(45, 42, 110, 0.28), inset 0 0 0 2px #fff;
  font-size: 30px; font-weight: 900; color: #b8860b;
  animation: gain-stars 1.5s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}
.gain-stars img { width: 34px; height: 34px; }
@keyframes gain-stars {
  0%   { opacity: 0; transform: translate(-50%, -50%) scale(0.4); }
  22%  { opacity: 1; transform: translate(-50%, -50%) scale(1.15); }
  38%  { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  100% { opacity: 0; transform: translate(-50%, -170%) scale(0.85); }
}

/* ---------- Écran de chargement ---------- */
/* ZÉRO TEXTE (charte enfant) : l'image d'accueil + une barre qui se remplit.
   Elle attend que tout le contexte soit DÉCODÉ, pas seulement téléchargé. */
.load-screen { justify-content: center; gap: 18px; overflow: hidden; }
.load-screen .ps-hero {
  flex: 1 1 auto; min-height: 90px;
  width: auto; max-width: 94%;
  object-fit: contain;
  border-radius: var(--radius-lg); box-shadow: var(--shadow);
}
.load-screen .load-track { flex: none; }
.load-track {
  width: min(320px, 62vw); height: 14px;
  border-radius: 999px;
  background: rgba(108, 92, 231, 0.16);
  box-shadow: inset 0 1px 3px rgba(45, 42, 110, 0.18);
  overflow: hidden;
}
.load-fill {
  width: 0%; height: 100%;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--violet-soft, #a99bff), var(--violet, #6c5ce7));
  transition: width 0.25s ease;
}

/* ---------- Export / import de sauvegarde ---------- */
/* La modale doit TENIR en paysage sur iPhone (393 px de haut) : colonne
   flex, la zone de texte absorbe le reste et défile SEULE. */
.modal.modal-io { max-width: min(560px, 94vw); }
.modal-io .io-modal {
  display: flex; flex-direction: column;
  flex: 1 1 auto; min-height: 0; gap: 8px;
  text-align: left;
}
.modal-io .io-modal h3 { margin: 0 44px 0 2px; text-align: center; }
.modal-io .modal-actions { margin-top: 0; }
.modal-io textarea { flex: 1 1 auto; min-height: 56px; resize: none; }
/* zone d'export : PAS un <textarea> — sur iPhone, le sélectionner pour
   copier fait surgir le clavier et ne copie rien (retour Bruno). */
.save-text {
  flex: 1 1 auto; min-height: 56px;
  margin: 0; padding: 8px 10px;
  background: #fff;
  border: 2px solid #e4dcf5; border-radius: var(--radius-sm);
  font-family: ui-monospace, Menlo, Consolas, monospace;
  font-size: 12px; line-height: 1.45; color: var(--ink);
  white-space: pre-wrap; word-break: break-all;
  overflow: auto; -webkit-overflow-scrolling: touch;
  -webkit-user-select: text; user-select: text;
}

/* ---------- Confirmation d'achat (enfant) + révélation ---------- */
/* confirmation = dialogue COMPACT (plus le large cadre vide hérité), et le
   rond ✕ sert de « non » → on retire la croix de coin redondante. */
.modal.modal-confirm { max-width: 340px; padding: 20px 18px 18px; }
.modal-confirm .modal-close { display: none; }
/* révélation d'accessoire : CADRÉE — image centrée, compacte, sans croix de
   coin redondante (clic à l'extérieur ferme et revient au coffre). */
.modal.modal-reveal { max-width: 360px; padding: 18px; }
.modal-reveal .modal-close { display: none; }
.reveal-card { display: flex; flex-direction: column; align-items: center; gap: 12px; }
.reveal-acc-img {
  max-height: 46vh; max-width: 100%; display: block;
  filter: drop-shadow(0 8px 16px rgba(120, 80, 180, 0.3));
  animation: revealPop 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) both;
}
.reveal-acc-name {
  font-weight: 800; font-size: 16px; color: var(--ink);
  background: linear-gradient(135deg, #fff6dd, #ffeef9);
  border-radius: 999px; padding: 5px 18px;
}
.confirm-card {
  display: flex; flex-direction: column; align-items: center;
  gap: 14px; text-align: center;
}
.confirm-hero { font-size: 54px; line-height: 1; display: flex; align-items: center; justify-content: center; }
.confirm-hero img {
  max-height: 116px; max-width: 100%;
  filter: drop-shadow(0 6px 10px rgba(80, 60, 140, 0.25));
}
.confirm-title { margin: 0; font-size: 22px; font-weight: 800; color: var(--night);
  display: inline-flex; align-items: center; justify-content: center; gap: 5px; }
.confirm-title img { vertical-align: middle; }
.confirm-text { margin: 0; color: var(--ink-soft); font-size: 13px; max-width: 300px; line-height: 1.3; }
.confirm-actions { display: flex; gap: 26px; align-items: center; justify-content: center; margin-top: 2px; }
.confirm-actions.labelled { gap: 12px; }
/* popup de PAIEMENT simplifiée : le prix EST l'élément principal */
.confirm-card.pay { gap: 22px; }
.confirm-card.pay .confirm-title { font-size: 34px; gap: 8px; }
/* deux RONDS équilibrés : ✕ doux / ✓ joyeux (pré-lecteur) */
.choice-round {
  width: 74px; height: 74px; border-radius: 50%;
  display: grid; place-items: center; border: none; cursor: pointer;
  transition: transform 0.12s, box-shadow 0.12s;
}
.choice-round.no {
  background: #fff; color: #b7a8da;
  box-shadow: 0 4px 0 #e6dff7, var(--shadow-soft);
}
.choice-round.yes {
  background: linear-gradient(135deg, var(--pink) 0%, var(--violet) 100%);
  color: #fff;
  box-shadow: 0 5px 0 #aa63d2, 0 9px 16px rgba(150, 80, 200, 0.34);
}
.choice-round.yes.star {
  background: linear-gradient(135deg, var(--star) 0%, var(--star-deep) 100%);
  box-shadow: 0 5px 0 #d18f1d, 0 9px 16px rgba(220, 150, 30, 0.34);
}
.choice-round:active { transform: translateY(3px) scale(0.97); box-shadow: var(--shadow-soft); }

/* ---------- « Pas assez d'étoiles » (feedback pré-lecteur) ---------- */
/* compteur de l'en-tête : tremblement ROUGE quand le solde est insuffisant */
@keyframes lack-shake {
  0%, 100% { transform: translateX(0); }
  15% { transform: translateX(-5px); }
  30% { transform: translateX(5px); }
  45% { transform: translateX(-4px); }
  60% { transform: translateX(4px); }
  78% { transform: translateX(-2px); }
}
.currency.lacking {
  animation: lack-shake 0.5s ease;
  background: rgba(255, 86, 86, 0.92);
  box-shadow: 0 0 0 3px rgba(255, 86, 86, 0.32);
}
/* bulle centrale : rangée d'étoiles (pleines = on a / éteintes = il manque) */
.nostars-overlay {
  position: fixed; inset: 0; z-index: 200;
  display: flex; align-items: center; justify-content: center;
  background: rgba(30, 27, 79, 0.26);
  animation: ns-fade 0.18s ease;
}
.nostars-overlay.out { opacity: 0; transition: opacity 0.2s ease; }
@keyframes ns-fade { from { opacity: 0; } to { opacity: 1; } }
.nostars-card {
  position: relative;
  background: var(--cream);
  border-radius: var(--radius-lg);
  padding: 24px 28px;
  box-shadow: 0 16px 50px rgba(0, 0, 0, 0.3);
  animation: modal-in 0.3s cubic-bezier(0.2, 1.4, 0.4, 1);
}
.nostars-card .ns-star-big { display: inline-flex; line-height: 0; }
.nostars-card .ns-x {
  position: absolute; top: -14px; right: -14px;
  width: 46px; height: 46px; border-radius: 50%;
  background: #ff5a5a; color: #fff;
  display: grid; place-items: center;
  box-shadow: 0 4px 0 #d83b3b, var(--shadow-soft);
}

/* bouton de fermeture DISCRET en coin (remplace les gros boutons « ✅ »).
   44px = minimum tactile (44pt iOS / 48dp Android) : c'est un bouton que
   l'ENFANT utilise. */
.modal-close {
  position: absolute; top: 6px; right: 6px; z-index: 3;
  width: 44px; height: 44px; border-radius: 50%;
  display: grid; place-items: center;
  font-size: 20px; font-weight: 800; line-height: 1;
  color: var(--ink-soft);
  background: rgba(0,0,0,0.05);
  box-shadow: inset 0 0 0 1.5px rgba(58,46,88,0.12);
  transition: transform 0.12s, background 0.15s;
}
.modal-close:hover { background: rgba(0,0,0,0.09); }
.modal-close:active { transform: scale(0.88); }

/* zone d'items des modales : défile HORIZONTALEMENT si trop large,
   jamais verticalement. */
.modal-row {
  display: flex; gap: 10px; justify-content: center;
  overflow-x: auto; overflow-y: hidden;
  padding: 4px 2px 6px; width: 100%;
}
.modal-row > * { flex: 0 0 auto; }

/* bouton à maintenir (validation parent) */
.hold-btn {
  position: relative;
  overflow: hidden;
  touch-action: none; /* appui long : le navigateur ne requalifie pas en scroll */
  width: 100%;
  border-radius: 18px;
  padding: 16px;
  font-size: 17px;
  font-weight: 800;
  background: #efe9fb;
  color: var(--ink);
}
.hold-btn .hold-fill {
  position: absolute;
  inset: 0;
  width: 0%;
  background: linear-gradient(90deg, var(--green), var(--turquoise));
  transition: width 0.1s linear;
  z-index: 0;
}
.hold-btn span { position: relative; z-index: 1; }

/* pavé PIN */
.pin-dots { display: flex; gap: 12px; justify-content: center; margin: 14px 0; }
.pin-dot {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: #ddd2f2;
}
.pin-dot.on { background: var(--violet); }
.pin-pad {
  display: grid;
  grid-template-columns: repeat(3, 72px);
  gap: 11px;
  margin: 0 auto;
}
.pin-pad button {
  width: 72px; height: 62px;
  font-size: 27px;
  font-weight: 800;
  background: #fff;
  border-radius: 16px;
  box-shadow: 0 3px 0 #e7dffa, var(--shadow-soft);
}
.pin-pad button:active { transform: translateY(2px); box-shadow: var(--shadow-soft); }
/* le « 0 » (11e et dernière touche) se centre sous le 7-8-9 */
.pin-pad button:last-child { grid-column: 2; }

/* pavé PIN en PAYSAGE : 2 colonnes (infos à gauche, clavier à droite) */
.pinpad-modal { display: flex; align-items: center; justify-content: center; gap: 26px; padding: 6px 10px; }
.pinpad-left { display: flex; flex-direction: column; align-items: center; gap: 6px; min-width: 140px; }
.pinpad-left h3 { margin: 2px 0; font-size: 18px; }
.pinpad-lock { width: 54px; }
.pinpad-modal .pin-dots { margin: 8px 0; gap: 12px; }
.pinpad-modal .pin-dot { width: 16px; height: 16px; }

/* ---------- Ticket ---------- */
.ticket {
  background: repeating-linear-gradient(135deg, #fff 0 14px, #fff7e6 14px 28px);
  border: 3px dashed var(--star-deep);
  border-radius: var(--radius-lg);
  padding: 18px;
  text-align: center;
}
.ticket .tk-emoji { font-size: 56px; }
.ticket .tk-title { font-size: 19px; font-weight: 800; margin: 6px 0 2px; }
.ticket .tk-sub { color: var(--ink-soft); font-size: 13.5px; }
.path-ribbon {
  /* le ruban reste plein cadre : ce sont les FLÈCHES de mois qui portent
     l'écart à l'encoche, car elles sont positionnées en JS (cf. path.js). */
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}
.path-decor {
  position: absolute;
  transform: translate(-50%, -50%);
  z-index: 1;
  animation: float 4.5s ease-in-out infinite;
  filter: drop-shadow(0 3px 4px rgba(45, 42, 110, 0.25));
}
/* props posés au sol : pas de flottement, ombre portée au pied */
.path-decor-static {
  animation: none;
  filter: drop-shadow(0 5px 3px rgba(45, 42, 110, 0.22));
}
.path-node {
  position: absolute;
  width: 52px;
  height: 52px;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  display: grid;
  place-items: center;
  padding: 0;
  box-shadow: 0 4px 0 rgba(45, 42, 110, 0.3);
  transition: transform 0.12s ease;
  z-index: 3;
}
.path-node:active { transform: translate(-50%, -50%) scale(0.92); }
.path-node.perfect {
  background: linear-gradient(135deg, #ffe9ad, #ffd66b);
  border: 3px solid #f5a623;
}
.path-node.partial {
  background: linear-gradient(135deg, #8fe3e3, #5bd6d6);
  border: 3px solid #38b8b8;
}
.path-node.partial .pn-count { font-size: 19px; font-weight: 800; color: #fff; }
.path-node.empty {
  background: #dde3f2;
  border: 3px solid #c2cbe2;
}
.path-node.future {
  background: rgba(255, 255, 255, 0.6);
  border: 3px dashed rgba(45, 42, 110, 0.35);
  box-shadow: none;
}
.path-node .pn-num { font-size: 17px; font-weight: 800; color: var(--ink-soft); }
.path-node.today {
  border: 4px solid var(--violet);
  background: #fff;
  animation: today-pulse 1.6s ease-in-out infinite;
  z-index: 4;
}
.path-node.today .pn-num { color: var(--violet); }
@keyframes today-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(108, 92, 231, 0.55); }
  50% { box-shadow: 0 0 0 12px rgba(108, 92, 231, 0); }
}
/* cases en images dessinées (planche C2) */
.path-node.img-mode {
  background: none;
  border: none;
  box-shadow: none;
  width: 58px;
  height: 58px;
}
.path-node.img-mode .pn-img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  filter: drop-shadow(0 3px 4px rgba(45, 42, 110, 0.3));
}
/* la case ÉTOILE (case-etoile) a plus de marge transparente dans son cadre
   (87% de remplissage vs 94% pour rouge/bleue) + une étoile qui dépasse en
   haut → sa pastille paraissait plus PETITE. On la grossit un peu pour
   égaliser la taille des disques (Bruno). */
.path-node.img-mode.pn-gold .pn-img { transform: scale(1.1); }
.path-node.img-mode .pn-overlay {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  font-size: 17px;
  font-weight: 800;
  color: #fff;
  text-shadow: 0 2px 3px rgba(45, 42, 110, 0.7);
  transform: translateY(-3px);
}
.path-node.img-mode .pn-overlay.soft { color: rgba(255, 255, 255, 0.95); font-size: 15px; }
.path-node.img-mode.today {
  animation: none;
}
.path-node.img-mode.today .pn-img {
  animation: today-glow 1.6s ease-in-out infinite;
}
@keyframes today-glow {
  0%, 100% { filter: drop-shadow(0 0 4px rgba(255, 214, 107, 0.9)); }
  50% { filter: drop-shadow(0 0 16px rgba(255, 214, 107, 1)); }
}

/* JOURS PASSÉS : on réutilise les CASES 3D images (or=case-etoile,
   vert=case-verte, rouge=case-rouge — recolorisées depuis case-turquoise).
   Le numéro se pose sur le dôme via .pn-overlay (déjà stylé en img-mode).
   Le bloc ci-dessous = REPLI cercles CSS si les images manquent. */
.path-node.pn-day:not(.img-mode) { width: 54px; height: 54px; background: none; border: none; box-shadow: none; }
.path-node.pn-gold:not(.img-mode) {
  background: radial-gradient(circle at 35% 28%, #fff6d0, #ffd66b 58%, #f0a81e);
  border: 3px solid #fff0bf;
  box-shadow: 0 0 0 2px #f0a81e, 0 6px 14px rgba(240, 168, 30, 0.5);
}
.path-node.pn-green:not(.img-mode) {
  background: radial-gradient(circle at 35% 28%, #d4f7d8, #7ed98b 58%, #46b35e);
  border: 3px solid #e4f8e6;
  box-shadow: 0 5px 12px rgba(70, 179, 94, 0.45);
}
.path-node.pn-red:not(.img-mode) {
  background: radial-gradient(circle at 35% 28%, #ffd8d5, #ff8a85 58%, #e0544f);
  border: 3px solid #ffe7e5;
  box-shadow: 0 5px 12px rgba(224, 84, 79, 0.45);
}
.path-node.pn-day .pn-day-n {
  font-size: 18px; font-weight: 800; color: #fff;
  text-shadow: 0 1px 2px rgba(45, 42, 110, 0.45);
}

/* CHECKLIST des missions, en bas à droite (remplace coffre + pancarte) */
/* Les deux pastilles de coin du Chemin (missions à droite, compagnon à
   gauche) étaient collées aux bords — « très très dans le coin, elles touchent
   presque le bord » (Bruno 2026-09-08). On les décolle, et on ajoute l'inset
   d'encoche ET celui de la barre home.
   ⚠️ 18 px et non 26 : à 26 elles paraissaient flotter loin du coin — l'écart
   doit rester SUBTIL (Bruno). Repère : 14 = collé, 26 = trop loin.
   ⚠️⚠️ `max()` ET NON `calc(18px + inset)` — voir « RÈGLE DE L'ENCOCHE » :
   · `calc(18px + inset)` repoussait la pastille à 77 px du bord, bien plus
     loin que nécessaire (« pas assez dans les coins », Bruno 2026-09-08) ;
   · un 18 px SEC, lui, laissait la pastille passer DERRIÈRE l'îlot dès que
     celui-ci se trouvait de ce côté (« quand je tourne le tel, on a le même
     souci de l'autre côté », Bruno 2026-09-09).
   `max(18px, inset)` prend le plus contraignant des deux : 18 px du côté sans
   encoche (bien dans le coin), juste ce qu'il faut du côté de l'encoche.
   L'inset du BAS (barre home) reste, lui : il est réel. */
.hub-checklist {
  position: absolute; z-index: 8;
  right: max(18px, env(safe-area-inset-right, 0px));
  bottom: calc(16px + env(safe-area-inset-bottom, 0px));
  width: 92px; height: 92px; padding: 0; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  /* FOND BLANC (carte) derrière la checklist, comme la vignette compagnon */
  background: radial-gradient(circle at 32% 26%, rgba(255, 255, 255, 0.97), rgba(255, 255, 255, 0.8));
  border: 2px solid rgba(255, 255, 255, 0.92); border-radius: 22px;
  box-shadow: 0 4px 12px rgba(45, 42, 110, 0.24), inset 0 -4px 10px rgba(108, 92, 231, 0.10);
}
.hub-checklist .h-ico { background: none; border: none; box-shadow: none; padding: 0; }
.hub-checklist .h-ico img { width: 74px !important; height: 74px !important; object-fit: contain; }
.hub-checklist .hc-svg { filter: drop-shadow(0 4px 8px rgba(45, 42, 110, 0.28)); }
.hub-checklist .hc-svg svg { width: 66px; height: 78px; display: block; }
.hub-checklist .h-badge { top: -4px; right: -4px; min-width: 24px; height: 24px; font-size: 13px; }

/* BANNIÈRE DU MOIS : plaque décorée (emblème de saison + nom), en HAUT-CENTRE,
   compacte pour s'aligner avec la ligne d'en-tête (profil / étoiles) */
.hub-monthname {
  position: absolute; left: 50%; top: 9px; transform: translateX(-50%); z-index: 9;
  display: flex; align-items: center; gap: 8px;
  padding: 5px 15px 5px 10px;
  background: radial-gradient(circle at 30% 24%, rgba(255, 255, 255, 0.95), rgba(255, 255, 255, 0.82));
  border: 1.5px solid rgba(255, 255, 255, 0.9); border-radius: 999px;
  box-shadow: var(--shadow-soft);
  white-space: nowrap;
}
.hub-monthname .mb-emblem { display: inline-flex; line-height: 0; }
.hub-monthname .mb-name {
  font-family: 'Baloo 2', system-ui, sans-serif;
  font-weight: 800; font-size: 17px; line-height: 1; color: var(--night);
}
.hub-monthname .mb-year {
  font-weight: 800; font-size: 11px; color: var(--violet);
  background: rgba(108, 92, 231, 0.12); border-radius: 999px; padding: 2px 6px;
}

/* flèches de changement de MOIS aux EXTRÉMITÉS du chemin (dans le contenu qui
   défile → visibles seulement tout au début / toute la fin du chemin) */
.hub-month-edge {
  position: absolute; transform: translate(-50%, -50%); z-index: 9;
  width: 52px; height: 52px; border-radius: 50%; border: none; cursor: pointer;
  display: grid; place-items: center;
  font-size: 30px; font-weight: 800; line-height: 1; color: var(--ink);
  background: rgba(255, 255, 255, 0.92);
  box-shadow: 0 4px 0 #d8cff0, var(--shadow);
}
.hub-month-edge:active { transform: translate(-50%, -50%) scale(0.9); }

/* case COMPAGNON en bas à gauche → page du personnage (perso BIEN visible) */
.hub-charbtn {
  position: absolute; z-index: 8;
  left: max(18px, env(safe-area-inset-left, 0px));   /* cf. .hub-checklist */
  bottom: calc(16px + env(safe-area-inset-bottom, 0px));
  width: 92px; height: 92px; cursor: pointer; padding: 0;
  overflow: hidden;
  background: radial-gradient(circle at 32% 26%, rgba(255, 255, 255, 0.96), rgba(255, 255, 255, 0.74));
  border: 2px solid rgba(255, 255, 255, 0.92); border-radius: 22px;
  box-shadow: 0 4px 12px rgba(45, 42, 110, 0.24), inset 0 -4px 10px rgba(108, 92, 231, 0.10);
}
.hub-charbtn svg {
  width: 100%; height: 100%;
  transform-origin: bottom center; transform: scale(1.12) translateY(4%);
}
.hub-charbtn:active { transform: scale(0.94); }

/* numéro du jour : petite étiquette CHAUDE et arrondie, posée juste SOUS la
   case, teintée selon l'état (s'intègre au décor pastel, pas un chip froid). */
.path-day-label {
  position: absolute;
  transform: translate(-50%, 0);
  z-index: 3;
  min-width: 15px; text-align: center;
  font-size: 12.5px; font-weight: 800; line-height: 1;
  padding: 2px 8px; border-radius: 999px;
  color: #6b4a00;
  background: linear-gradient(180deg, #fffaf0, #ffeec9);
  box-shadow: 0 2px 4px rgba(120, 90, 30, 0.28), inset 0 1px 0 rgba(255, 255, 255, 0.7);
}
.path-day-label.pdl-gold  { color: #7a5300; background: linear-gradient(180deg, #fff6d4, #ffe19a); box-shadow: 0 2px 4px rgba(180, 130, 20, 0.32), inset 0 1px 0 rgba(255,255,255,0.8); }
.path-day-label.pdl-green { color: #2f6b3d; background: linear-gradient(180deg, #e9f9ec, #c4eecb); box-shadow: 0 2px 4px rgba(60, 150, 80, 0.3), inset 0 1px 0 rgba(255,255,255,0.7); }
.path-day-label.pdl-red   { color: #9e3a35; background: linear-gradient(180deg, #fff0ee, #ffd4cf); box-shadow: 0 2px 4px rgba(200, 90, 80, 0.3), inset 0 1px 0 rgba(255,255,255,0.7); }
.path-day-label.pdl-future { color: #3f6b9a; background: linear-gradient(180deg, #f0f7ff, #d6e9fb); box-shadow: 0 2px 4px rgba(80, 130, 190, 0.28), inset 0 1px 0 rgba(255,255,255,0.7); opacity: 0.95; }
.path-flag {
  position: absolute;
  transform: translate(-50%, -50%);
  font-size: 24px;
  z-index: 3;
  filter: drop-shadow(0 2px 3px rgba(0, 0, 0, 0.25));
}
.path-companion {
  position: absolute;
  width: 96px;
  transform: translate(-50%, -100%);
  z-index: 5;
  animation: idle 3.2s ease-in-out infinite;
  pointer-events: none;
  filter: drop-shadow(0 4px 6px rgba(45, 42, 110, 0.35));
}

/* ---------- Onboarding ---------- */
/* écrans d'accueil : COMPACTS pour tenir en PAYSAGE (pas de débordement
   vertical). On remplit la hauteur dispo, contenu centré, scroll en
   dernier recours seulement. */
.onboard {
  height: 100vh; /* fallback vieilles WebView (dvh ignoré) */
  height: 100dvh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 8px 16px;
  text-align: center;
  gap: 8px;
  overflow-y: auto;
}
.onboard h1 { font-size: 22px; margin: 0; color: var(--night); }
.onboard h2 { font-size: 18px; margin: 0; }
.onboard p.lead { color: var(--ink-soft); font-size: 13px; margin: 0; max-width: 360px; line-height: 1.25; }
.onboard .btn { padding: 9px 22px; }
.onboard .big-input {
  font-size: 22px;
  text-align: center;
  font-weight: 800;
  max-width: 220px;
  padding: 7px 10px;
}
.choice-row { display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; }
.choice-big {
  background: #fff;
  border-radius: var(--radius-md);
  box-shadow: var(--shadow);
  padding: 8px 18px;
  font-size: 15px;
  font-weight: 800;
  border: 3px solid transparent;
  transition: transform 0.15s;
}
.choice-big:active { transform: scale(0.95); }
.choice-big.selected { border-color: var(--violet); background: #f4f0ff; }
.choice-big .ce { font-size: 30px; display: block; }
.choice-big .ce img { height: 44px; width: auto; }

/* Choix du genre (onboarding) : GROS sur téléphone — bien visible/tappable.
   La taille de l'avatar est posée en inline par imgOrEmoji (124px) ; ici on
   gonfle la carte, le libellé et les titres. Ciblé → n'affecte PAS « Qui joue ? ». */
.gender-pick h2 { font-size: 27px; }
.gender-pick .lead { font-size: 17px; }
.gender-pick .choice-row { gap: 26px; margin-top: 4px; }
.gender-pick .choice-big {
  padding: 18px 34px;
  font-size: 23px;
  border-radius: var(--radius-lg);
  border-width: 4px;
}
.gender-pick .choice-big .ce { margin-bottom: 10px; }

/* Écran d'accueil (step1) : on resserre l'habillage pour une IMAGE bien
   plus grande (héros), sans jamais scroller — phone paysage. */
.welcome-step { gap: 6px; padding: 6px 14px; }
.welcome-step h1 { font-size: 21px; }
.welcome-step .lead { font-size: 12.5px; max-width: 420px; }
.welcome-step .btn { padding: 9px 26px; }
/* l'image épouse son cadre (display block → pas d'espace sous la ligne de base) */
.onboard-hero { display: block; }
/* lien « Retour aux profils » : flotte en coin, ne mange pas de hauteur */
.onboard-back {
  position: absolute; top: 8px; left: 10px; z-index: 5;
}

/* Écran « Qui joue ? » : tient sans scroll, visuel plafonné, vignette « + » */
.profile-select { overflow: hidden; justify-content: center; gap: 10px; }
.profile-select .ps-hero {
  /* L'image prend TOUTE la place que le titre et les vignettes lui laissent
     (Bruno : « agrandis-la, occupe un peu l'espace »). Plafond en dvh
     remplacé par `flex: 1` : plus de nombre magique à recaler écran par
     écran, et zéro scroll puisque le reste est en `flex: none`. */
  flex: 1 1 auto; min-height: 90px;
  width: auto; max-width: 94%;
  object-fit: contain;
  border-radius: var(--radius-lg); box-shadow: var(--shadow);
}
/* titre et vignettes : hauteur naturelle, jamais compressés par l'image */
.profile-select h1, .profile-select .choice-row { flex: none; }
.choice-add {
  border-style: dashed; border-color: var(--violet);
  background: #f7f4ff; color: var(--violet);
}
.choice-big .ce-add {
  font-size: 38px; line-height: 44px; font-weight: 900;
  color: var(--violet);
}
/* carrousel d'œufs : UNE rangée CENTRÉE, défilement horizontal si trop large */
.starter-carousel {
  display: flex; gap: 10px;
  width: 100%; max-width: 100%;
  justify-content: center; align-items: stretch;
  overflow-x: auto; padding: 4px 4px 6px;
}
/* les 5 œufs se PARTAGENT toute la largeur (plus de bandes vides sur les
   bords) et sont aussi GRANDS que la place le permet */
.starter-carousel .shop-item {
  flex: 1 1 0; min-width: 0; max-width: 190px;
  padding: 8px 6px 6px;
  display: flex; flex-direction: column; align-items: center; justify-content: flex-end;
}
.starter-carousel .shop-item .preview { height: auto; margin-bottom: 4px; }
.starter-carousel .shop-item .preview img { max-height: none; }
.starter-carousel .shop-item .i-sub { font-size: 15px; letter-spacing: 3px; margin: 0; }

/* écran de choix de l'œuf : habillage resserré → place maximale au carrousel */
.egg-pick { gap: 6px; padding: 6px 10px; }
.egg-pick h2 { font-size: 22px; }
.egg-pick .lead { font-size: 13px; }

/* ---------- Admin ---------- */
/* ---------- ÉCRAN PARENTS : liste à gauche, fiche à droite ----------
   Refonte 2026-09-08 : l'accordéon empilé imposait de longs défilements sur
   téléphone EN PAYSAGE (7 en-têtes remplissaient l'écran). On exploite la
   largeur : la liste des rubriques tient à gauche, la fiche s'ouvre à côté.
   Sur écran étroit (portrait), la même structure repasse en colonne. */
.app-version {
  font-size: 12px; font-weight: 700; color: var(--ink-soft);
  background: rgba(255,255,255,0.6); border-radius: 999px; padding: 3px 9px;
  margin-left: 8px; align-self: center;
}
.admin-split { display: flex; gap: 14px; align-items: flex-start; }
.admin-nav {
  flex: 0 0 210px; display: flex; flex-direction: column; gap: 6px;
  position: sticky; top: 8px;
}
.admin-nav .as-head {
  width: 100%; justify-content: flex-start; margin: 0;
  background: #fff; border: 2px solid transparent;
}
.admin-nav .as-head.on {
  border-color: var(--violet); background: #f3efff; color: var(--violet);
}
.admin-panel {
  flex: 1; min-width: 0;
  background: #fff; border-radius: var(--radius-md);
  padding: 14px 16px; box-shadow: var(--shadow-soft);
}
.admin-panel .ap-title {
  margin: 0 0 12px; font-size: 16px; color: var(--ink);
  padding-bottom: 10px; border-bottom: 2px solid #f0ecfa;
}
/* écran étroit (portrait téléphone) : la liste repasse AU-DESSUS de la fiche,
   en grille de pastilles pour ne pas manger toute la hauteur. */
@media (max-width: 620px) {
  .admin-split { flex-direction: column; gap: 10px; }
  .admin-nav {
    flex: none; position: static; width: 100%;
    display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  }
  .admin-panel { width: 100%; box-sizing: border-box; }
}

.admin-section { margin-bottom: 14px; }
.admin-section .as-head {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 10px;
  background: var(--card);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-soft);
  padding: 13px 15px;
  font-weight: 800;
  font-size: 16px;
  text-align: left;
}
.admin-section .as-head .chev { margin-left: auto; transition: transform 0.2s; }
.admin-section.open .as-head .chev { transform: rotate(90deg); }
.admin-section .as-body {
  display: none;
  padding: 12px 4px;
}
.admin-section.open .as-body { display: block; }

.form-row { margin-bottom: 10px; text-align: left; }
.form-row label {
  display: block;
  font-size: 13px;
  font-weight: 800;
  color: var(--ink-soft);
  margin-bottom: 4px;
}
.form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }

.day-toggles { display: flex; gap: 5px; }
.day-toggles button {
  flex: 1;
  padding: 8px 0;
  border-radius: 10px;
  background: #efe9fb;
  font-weight: 800;
  font-size: 13px;
  color: var(--ink-soft);
}
.day-toggles button.on { background: var(--violet); color: #fff; }

.admin-task-row {
  display: flex;
  align-items: center;
  gap: 10px;
  background: var(--card);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-soft);
  padding: 10px 12px;
  margin-bottom: 8px;
}
.admin-task-row .at-emoji { font-size: 26px; }
.admin-task-row .at-label { flex: 1; font-weight: 700; font-size: 14.5px; line-height: 1.15; }
.admin-task-row .at-label .at-sub { font-size: 12px; color: var(--ink-soft); font-weight: 600; }
.admin-task-row.inactive { opacity: 0.45; }
.icon-btn {
  font-size: 18px;
  padding: 7px;
  border-radius: 10px;
  background: #f3eefc;
}

.stat-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
.stat-box {
  background: var(--card);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-soft);
  text-align: center;
  padding: 12px 6px;
}
.stat-box .sv { font-size: 22px; font-weight: 800; color: var(--violet); }
.stat-box .sl { font-size: 11.5px; color: var(--ink-soft); font-weight: 700; }

.hint {
  font-size: 13px;
  color: var(--ink-soft);
  background: #f6f1ff;
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  margin: 8px 0;
  text-align: left;
}

/* ---------- Confetti / célébration ---------- */
#confetti-canvas {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 200;
}
.float-star {
  position: fixed;
  z-index: 150;
  font-size: 26px;
  pointer-events: none;
  animation: fly-up 1s ease-out forwards;
}
@keyframes fly-up {
  0% { transform: translateY(0) scale(1); opacity: 1; }
  100% { transform: translateY(-90px) scale(1.6); opacity: 0; }
}

/* Tili qui surgit pour féliciter après une mission */
.mascot-cheer {
  position: fixed;
  bottom: 86px;
  right: 10px;
  z-index: 160;
  pointer-events: none;
  animation: cheer-pop 1.4s cubic-bezier(0.2, 1.4, 0.4, 1) forwards;
}
.mascot-cheer img {
  width: 110px;
  filter: drop-shadow(0 6px 10px rgba(45, 42, 110, 0.4));
}
@keyframes cheer-pop {
  0% { transform: translateY(130px) rotate(8deg); }
  18% { transform: translateY(-6px) rotate(-3deg); }
  28% { transform: translateY(0) rotate(0); }
  80% { transform: translateY(0); }
  100% { transform: translateY(150px); }
}

.celebrate-overlay {
  position: fixed;
  inset: 0;
  z-index: 120;
  background: rgba(30, 27, 79, 0.75);
  display: flex;
  align-items: center;
  justify-content: center;
  animation: fade-in 0.3s ease;
}
.celebrate-box {
  text-align: center;
  color: #fff;
  padding: 24px;
}
.celebrate-box .cb-emoji { font-size: 90px; animation: bump 0.8s ease infinite; }
.celebrate-box h2 { font-size: 28px; margin: 10px 0 4px; }
.celebrate-box p { font-size: 17px; opacity: 0.9; margin: 0 0 18px; }
/* bouton « Youpi ! » : grosse pastille dorée, lisible sur le voile sombre */
.celebrate-box .cb-btn {
  font-size: 20px; font-weight: 800;
  padding: 14px 48px; border-radius: 999px;
  box-shadow: 0 5px 0 #d18f1d, 0 10px 22px rgba(0, 0, 0, 0.35);
}
.celebrate-box .cb-btn:active { transform: translateY(3px); box-shadow: 0 2px 0 #d18f1d, 0 5px 12px rgba(0, 0, 0, 0.3); }

@media (max-width: 380px) {
}


/* ============================================================
   REFONTE V3 — paysage, navigation diégétique
   ============================================================ */

/* l'app s'élargit en paysage (le hub et la Maison sont plein écran) */
@media (min-aspect-ratio: 1/1) {
  #app { max-width: 1100px; }
  /* les écrans-LIEUX (Chemin/Maison/Missions, body.no-vscroll) restent
     PLEIN CADRE : sur iPad Pro / Galaxy Tab (>1100px), le plafond laissait
     des bandes latérales avec une couture visible sur le fond peint. */
  body.no-vscroll #app { max-width: none; }
}

/* écrans-lieux : plein cadre, VRAIMENT sans marges.
   Le padding de 8-10 px laissait un liseré clair tout autour du décor, très
   visible sur téléphone (retour Bruno 2026-09-08 : « on voit que ça fait pas
   tout l'écran »). Les marges d'encoche sont portées par le DÉCOR lui-même
   (voir plus bas) : le fond reste plein écran, seul le contenu est écarté. */
.screen.hub, .screen.maison { padding: 0; }

/* ----- LE CHEMIN (hub horizontal) ----- */
.hub-scene {
  position: relative;
  height: calc(100vh - 104px); /* fallback vieilles WebView (dvh ignoré) */
  height: calc(100dvh - 104px);
  min-height: 300px;
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: var(--shadow-soft);
}
.hub-scroll {
  height: 100%;
  overflow-x: auto;
  overflow-y: hidden;
  -webkit-overflow-scrolling: touch;
}
.hub-inner { position: relative; height: 100%; }
.hub-horizon { position: absolute; left: 0; right: 0; top: 0; height: 46%; }
.hub-horizon-fade { position: absolute; left: 0; right: 0; bottom: 0; height: 50%; }
.hub-inner .path-ribbon { position: absolute; inset: 0; width: 100%; height: 100%; }
.hub-companion {
  /* taille adaptée à la hauteur d'écran (évite qu'il domine / monte trop sur petit écran) */
  width: clamp(112px, 23vh, 146px); height: clamp(112px, 23vh, 146px); cursor: pointer;
  pointer-events: auto;              /* .path-companion l'avait coupé : le toucher est LE bouton principal */
  animation: none;                   /* l'animation idle écrasait le centrage translate */
  transform: translate(-50%, -92%);  /* debout sur la case */
  padding: 0;
}
.hub-companion svg {
  width: 100%; height: 100%;
  animation: idle 3.2s ease-in-out infinite;
  transform-origin: bottom center;
}

/* ----- objets-boutons (hotspots) ----- */
.hotspot {
  position: absolute; z-index: 6;
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  cursor: pointer; padding: 4px;
}
.hotspot .h-ico { line-height: 1; position: relative; }
.hotspot:active .h-ico { transform: scale(0.92); }
/* socle clair derrière les objets-boutons : ils ressortent sur la scène */
.hotspot .h-ico, .hub-coffre .h-ico {
  display: grid; place-items: center;
  background: radial-gradient(circle at 32% 26%, rgba(255, 255, 255, 0.96), rgba(255, 255, 255, 0.74));
  border: 2px solid rgba(255, 255, 255, 0.92);
  border-radius: 26px;
  padding: 9px;
  box-shadow: 0 4px 12px rgba(45, 42, 110, 0.24), inset 0 -4px 10px rgba(108, 92, 231, 0.10);
}
.h-badge {
  position: absolute; top: 0; right: 4px; z-index: 2;
  background: #ff7e79; color: #fff; border-radius: 999px;
  min-width: 20px; height: 20px; display: grid; place-items: center;
  font-size: 12px; font-weight: 800; box-shadow: var(--shadow-soft);
}
@keyframes hotspotWiggle {
  0%, 100% { transform: rotate(0); }
  25% { transform: rotate(-4deg); }
  75% { transform: rotate(4deg); }
}
.h-sparkle .h-ico { animation: hotspotWiggle 1.6s ease-in-out infinite; }
.h-dodo .h-ico { opacity: 0.85; filter: grayscale(0.35) drop-shadow(0 4px 6px rgba(45,42,110,0.2)); }
@keyframes etoilePulse {
  0%, 100% { transform: scale(1); filter: drop-shadow(0 0 6px rgba(255, 214, 107, 0.8)); }
  50% { transform: scale(1.18); filter: drop-shadow(0 0 16px rgba(255, 214, 107, 1)); }
}
.h-retour .h-ico { transform: scaleX(-1); }

/* ----- LA MAISON (scène plein écran) ----- */
.maison .scene.maison-scene {
  height: calc(100vh - 104px); /* fallback vieilles WebView (dvh ignoré) */
  height: calc(100dvh - 104px);
  min-height: 300px;
  width: 100%;
}
/* le compagnon est le HÉROS de la scène : surélevé au-dessus de la plaque
   (il était recouvert), taille adaptée à la hauteur d'écran */
.maison-scene .char-slot {
  width: clamp(330px, 74vh, 470px);
  margin-bottom: clamp(44px, 12vh, 80px);
}
/* LA CROISSANCE DOIT SE VOIR (Bruno) : cadre CONSTANT pour TOUS les stades →
   comme renderCharacter agrandit déjà le perso en interne (stageScale 0.5→1),
   le Magique s'affiche ~2× l'œuf, de façon MONOTONE (fini l'ancien boost
   œuf/bébé qui les rendait aussi gros que le Magique et créait un creux au
   stade Petit). Seul l'ŒUF (dessiné petit) garde un léger agrandissement. */
.maison-scene .char-slot.cs-egg {
  width: clamp(380px, 86vh, 510px);
  margin-bottom: clamp(40px, 11vh, 74px);
}
.maison-plate {
  position: absolute; left: 50%; bottom: 8px; transform: translateX(-50%);
  z-index: 6; width: min(380px, 58%);
  background: rgba(255, 255, 255, 0.93); border-radius: 16px;
  padding: 7px 14px 8px; text-align: center; box-shadow: var(--shadow-soft);
  cursor: pointer;
}
.maison-plate .c-name { font-weight: 800; font-size: 14px; }

/* ----- le nid : œufs mystère ----- */
.shop-item { position: relative; }
.egg-asleep .preview { filter: grayscale(0.75) brightness(0.97); opacity: 0.8; }
.egg-zzz { position: absolute; top: 6px; right: 10px; font-size: 20px; z-index: 2; }
@keyframes eggWiggle {
  0%, 100% { transform: rotate(0); }
  20% { transform: rotate(-3deg); }
  40% { transform: rotate(3deg); }
  60% { transform: rotate(-2deg); }
  80% { transform: rotate(2deg); }
}
.egg-awake .preview { animation: eggWiggle 1.1s ease-in-out infinite; transform-origin: 50% 90%; }

/* ----- portrait sur tablette/téléphone : invitation à tourner ----- */
.rotate-hint {
  display: none; position: fixed; inset: 0; z-index: 9999;
  flex-direction: column; align-items: center; justify-content: center; gap: 10px;
  background: linear-gradient(180deg, #efe9ff 0%, #fff8ec 100%);
  font-weight: 800; font-size: 22px; color: var(--ink); text-align: center;
}
@media (max-aspect-ratio: 4/5) and (pointer: coarse) {
  .rotate-hint { display: flex; }
}
@keyframes rotateHint { 0%, 100% { transform: rotate(0); } 50% { transform: rotate(-90deg); } }
.rotate-hint .rh-ico { font-size: 64px; animation: rotateHint 2s ease-in-out infinite; }
.back-picto { font-size: 18px; }

/* plaque de la Maison : jauge en cœurs + picto du prochain pas */
.maison-plate { display: flex; align-items: center; justify-content: center; gap: 8px; }
.plate-hearts { display: flex; flex-wrap: wrap; justify-content: center; gap: 1px; font-size: 17px; }

/* ----- LE LIVRE : l'histoire en tableaux, pages qui se tournent ----- */
.modal:has(> .livre-modal) { max-width: 660px; background: #fff8ec; }


/* ----- MISSIONS V3.1 : décor PEINT du compagnon + panneaux clairs flottants,
   zéro scroll vertical (défilement horizontal seulement). Plus de bandeau bleu
   ni de parchemins bois (refonte sur retour Bruno). ----- */
.m-screen {
  position: relative;
  height: 100vh; width: 100%; /* fallback vieilles WebView (dvh ignoré) */
  height: 100dvh; width: 100%;
  overflow: hidden;
  display: flex; align-items: center; justify-content: center;
  background: linear-gradient(180deg, #efeaff 0%, #f7eefb 100%); /* repli sans fond */
}
/* voile de lisibilité par-dessus le fond peint (cartes bien lisibles) */
.m-veil {
  position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.30) 0%, rgba(255, 255, 255, 0.06) 36%, rgba(255, 255, 255, 0.12) 100%);
}
/* retour au Chemin : pastille flottante (charte des hotspots de la Maison) */
.m-back {
  /* plus GRAND et un peu décollé du coin (Bruno 2026-09-08) — même repère que
     les pastilles du Chemin : 18 px, plus les insets. */
  position: absolute; z-index: 6;
  /* `max()` : 18 px du côté sans encoche (le bouton reste dans son coin),
     l'inset du côté de l'encoche (sinon il passe dessous quand on tourne le
     téléphone). Surtout PAS `calc(18px + inset)` : 77 px, beaucoup trop loin. */
  top: 18px;
  left: max(18px, env(safe-area-inset-left, 0px));
  width: 78px; height: 78px; border: none; cursor: pointer;
  border-radius: 20px;
  background: radial-gradient(120% 120% at 50% 22%, #fff 58%, #f1ebff 100%);
  box-shadow: 0 7px 16px rgba(45, 42, 110, 0.24);
  display: grid; place-items: center;
  transition: transform 0.12s ease;
}
.m-back:active { transform: translateY(2px) scale(0.96); }

/* accès au COMPAGNON depuis les missions : vignette en bas à gauche, en
   miroir du retour au Chemin (Bruno 2026-09-09). Même taille, même charte
   que la pastille du Chemin — le compagnon lui-même, pas un picto.
   ⚠️ `max()` et l'inset du BAS : cf. « RÈGLE DE L'ENCOCHE ». */
.m-char {
  position: absolute; z-index: 6;
  bottom: calc(16px + env(safe-area-inset-bottom, 0px));
  left: max(18px, env(safe-area-inset-left, 0px));
  width: 78px; height: 78px; padding: 0; border: none; cursor: pointer;
  border-radius: 20px; overflow: hidden;
  background: radial-gradient(120% 120% at 50% 22%, #fff 58%, #f1ebff 100%);
  box-shadow: 0 7px 16px rgba(45, 42, 110, 0.24);
  display: grid; place-items: center;
  transition: transform 0.12s ease;
}
.m-char:active { transform: translateY(2px) scale(0.96); }
.m-char svg, .m-char img { width: 100%; height: 100%; object-fit: contain; }
.m-back img { width: 58px; height: 58px; object-fit: contain; }
.m-col {
  flex: none;                                        /* largeur AUTO = variable selon le nb de sous-colonnes */
  display: flex; flex-direction: column; gap: 8px;
  padding: 12px 14px 16px;
  border-radius: 22px;
  background: rgba(255, 255, 255, 0.9);
  -webkit-backdrop-filter: blur(5px); backdrop-filter: blur(5px);
  box-shadow: 0 14px 32px rgba(45, 42, 110, 0.16);
  overflow: hidden;
}
/* à l'intérieur d'un panneau : sous-colonnes de MAX 3 tâches, côte à côte */
.m-grid { flex: 1; display: flex; gap: 12px; align-items: flex-start; }
.m-col.now { box-shadow: 0 0 0 3px var(--star), 0 14px 32px rgba(45, 42, 110, 0.22); }
.m-col-head {
  align-self: center; flex: none;
  background: none; border: none; cursor: pointer;
  display: grid; place-items: center;
  padding: 2px 6px 6px;
}
.m-col-head img, .m-col-head svg { width: 32px; height: 32px; object-fit: contain; }
.m-col.now .m-col-head { animation: hotspotWiggle 1.8s ease-in-out infinite; }

/* carte mission : pastille À GAUCHE (centrée), phrase EN HAUT sur toute la
   largeur (multi-lignes), étoiles dans le COIN BAS-DROITE. Hauteur IDENTIQUE
   pour toutes + cadre léger (retour Bruno : fini les cadres gris de tailles
   différentes). ✓ = badge d'angle sur la pastille une fois réussi. */
.task-card.mh-card {
  display: flex; align-items: center; gap: 10px;
  height: 64px; flex: none;
  padding: 8px 12px 8px 9px;
  border-radius: 16px; border: none;
  position: relative;
  box-shadow: 0 3px 9px rgba(45, 42, 110, 0.10);
}
.task-card.mh-card .t-emoji {
  position: relative; flex: none;
  width: 42px; height: 42px; border-radius: 12px; font-size: 24px;
}
.task-card.mh-card .t-body {
  /* colonne : la phrase, PUIS les étoiles — en FLUX. En absolu (coin
     bas-droite) elles passaient PAR-DESSUS le texte dès que la phrase tenait
     sur 3 lignes (carte étroite) : « Nuit propre, sans pip⭐⭐⭐ »
     (capture Bruno 2026-09-08). Le flux rend le chevauchement impossible. */
  flex: 1; min-width: 0; align-self: stretch;
  display: flex; flex-direction: column; justify-content: center; gap: 3px;
}
.task-card.mh-card .t-label {
  font-size: 13.5px; font-weight: 700; line-height: 1.16; color: var(--night);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.task-card.mh-card .t-stars {
  flex: none; align-self: flex-end;
  display: flex; gap: 0; font-size: 11px; line-height: 1;
}
.task-card.mh-card .t-check {
  position: absolute; top: -6px; right: -6px;
  width: 19px; height: 19px; border-radius: 50%;
  display: none; place-items: center; font-size: 12px;
}
.task-card.mh-card.done {
  background: #f3fcf4; opacity: 1;
  touch-action: none; /* siège de l'appui long de décochage : pas de requalification en scroll (les cartes non cochées, elles, laissent passer le défilement de secours) */
}
.task-card.mh-card.done .t-check {
  display: grid; background: var(--green); color: #fff; border: 2px solid #fff;
}

/* ===== UN SEUL MOMENT à l'écran (matin / journée / soir) =====
   Le panneau prend toute la place et les cartes S'ÉTIRENT pour être le plus
   grandes possible ; les flèches ◀ ▶ changent de moment. Zéro scroll. */
.m-stage {
  position: relative; z-index: 4;
  display: flex; align-items: stretch; gap: 10px;
  width: 100%; height: 100vh; /* fallback vieilles WebView (dvh ignoré) */
  width: 100%; height: 100dvh;
  padding: 14px 12px;
  box-sizing: border-box;
}
.m-col.solo {
  flex: 1; min-width: 0;
  /* Le panneau prend TOUTE la largeur disponible, en s'arrêtant juste avant
     l'encoche — et d'autant de l'autre côté pour rester centré (retour Bruno
     2026-09-08 : « prends tout l'espace jusqu'à l'encoche et équivalent de
     l'autre côté »). Les 78 px fixes d'avant rétrécissaient trop les cartes :
     les phrases se coupaient en plein milieu. */
  /* ⚠️⚠️ AUCUN inset ici : `.m-screen` en pose DÉJÀ un en padding, et les
     flèches en posaient un TROISIÈME. Sur l'iPhone de Bruno (852 px, inset
     59 px) le panneau tombait à 326 px au lieu de 542 — les cartes à 155 px,
     donc du texte sur 3 lignes qui passait sous les étoiles. Mesuré, pas
     supposé : 59 + 12 + 59 + 62 + 10 + 59 = 261 px perdus de CHAQUE côté. */
  margin-left: 12px;
  margin-right: 12px;
  gap: 6px;
}
/* la grille occupe TOUT le panneau : les cartes s'étirent (align stretch).
   minmax(64px, 1fr) : les rangées ne s'écrasent jamais sous 64px — au-delà de
   ~12 missions (ou sur un écran très court), le défilement de SECOURS prend
   le relais au lieu de rogner des cartes injoignables. */
.m-grid.solo {
  flex: 1; min-height: 0;
  display: grid; gap: 12px;
  align-items: stretch; justify-items: stretch;
  /* Rangées à la HAUTEUR DU CONTENU, bornées : `1fr` distribuait TOUTE la
     hauteur du panneau entre les rangées → avec 2 missions, des cartes de
     297 px au grand vide central (retour Bruno 2026-09-08 : « c'est très
     moche »). Désormais chaque carte prend sa hauteur naturelle et le bloc
     est réparti verticalement dans le panneau. */
  grid-auto-rows: minmax(64px, auto);
  align-content: space-evenly;
  overflow-y: auto;                  /* secours seulement : ne s'active que si ça ne tient pas */
}
.m-grid.solo.cols-1 { grid-template-columns: 1fr; }
.m-grid.solo.cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.m-grid.solo.cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
/* dans ce mode, la carte remplit sa case (plus de hauteur fixe de 64px) */
.m-grid.solo .task-card.mh-card {
  height: auto;
  /* hauteur CONFORTABLE mais BORNÉE : grandit un peu avec l'écran (doigt de
     4 ans), sans jamais devenir une carte à moitié vide. */
  min-height: clamp(64px, 11vh, 104px);
  padding: 10px 16px 10px 12px; gap: 14px;
}
/* plancher 28px (et non 48) : sur un écran court (téléphone paysage ~375px),
   9vh passe sous 48px et un plancher trop haut faisait déborder la pastille
   de sa carte. */
.m-grid.solo .task-card.mh-card .t-emoji { width: clamp(28px, 9vh, 88px); height: clamp(28px, 9vh, 88px); border-radius: 18px; }
.m-grid.solo .task-card.mh-card .t-emoji img,
.m-grid.solo .task-card.mh-card .t-emoji svg { width: 78%; height: 78%; object-fit: contain; }
.m-grid.solo .task-card.mh-card .t-label { font-size: clamp(15px, 2.4vh, 24px); -webkit-line-clamp: 2; }
.m-grid.solo .task-card.mh-card .t-stars { font-size: clamp(12px, 1.8vh, 18px); }
.m-grid.solo .task-card.mh-card .t-check { width: 26px; height: 26px; font-size: 16px; top: -8px; right: -8px; }
/* rien à faire pour ce moment */
.m-none {
  flex: 1; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 12px;
  font-weight: 800; font-size: 19px; color: var(--ink-soft);
}
/* flèches = pictos seuls (charte : zéro texte sur les écrans enfant) */
.m-arrow {
  flex: none; align-self: center;
  width: 62px; height: 92px; border: none; cursor: pointer;
  border-radius: 22px;
  background: rgba(255, 255, 255, 0.86);
  -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px);
  box-shadow: 0 8px 18px rgba(45, 42, 110, 0.18);
  display: grid; place-items: center;
  transition: transform 0.12s ease, opacity 0.15s ease;
}
.m-arrow:active { transform: scale(0.94); }
.m-arrow-ico { font-size: 46px; line-height: 1; font-weight: 800; color: var(--violet); margin-top: -6px; }
.m-arrow.off { opacity: 0.22; pointer-events: none; }

/* appui long : la carte « se serre » pendant le maintien */
.task-card.holding { animation: holdShrink 0.7s linear forwards; }
@keyframes holdShrink {
  to { transform: scale(0.88); filter: saturate(0.5); }
}


/* ----- zéro scroll vertical sur les LIEUX (chemin, maison, missions) :
   le body est verrouillé, les scènes remplissent l'espace en flex
   (fini les calculs en dvh qui débordaient de quelques pixels) ----- */
body.no-vscroll { overflow: hidden; }
body.no-vscroll #app { height: 100vh; min-height: 0; overflow: hidden; } /* fallback vieilles WebView (dvh ignoré) */
body.no-vscroll #app { height: 100dvh; min-height: 0; overflow: hidden; }
.screen.hub, .screen.maison {
  display: flex; flex-direction: column; min-height: 0;
}
.hub-scene { flex: 1; min-height: 0; height: auto; }
.maison .scene.maison-scene { flex: 1; min-height: 0; height: auto; }
/* le min-height de base de .scene (340px) tronquait le bas sur téléphone */
body.no-vscroll .scene { min-height: 0; }


/* pastille du monde : emoji seul, cliquable (la voix nomme le monde) */
.maison-scene .world-name { font-size: 22px; padding: 6px 11px; cursor: pointer; line-height: 1; }


/* ----- colonnes d'objets-boutons : socles UNIFORMES, espacement égal,
   alignement identique à gauche et à droite ----- */
.h-col {
  /* top dégagé pour laisser le bandeau flottant (pastilles) au-dessus.
     68px et non 52 : la pastille du prénom, agrandie avec l'entête, chevauchait
     le premier objet de la colonne gauche (retour Bruno 2026-09-08). */
  position: absolute; top: 68px; bottom: 2%; z-index: 6;
  display: flex; flex-direction: column;
  justify-content: space-between; align-items: center;
  gap: 5px;
}
/* ============================================================
   RÈGLE DE L'ENCOCHE (mesurée sur l'iPhone de Bruno, 2026-09-08)
   ------------------------------------------------------------
   En PAYSAGE, iOS annonce le MÊME inset des DEUX côtés (59 px ici), alors
   que la Dynamic Island n'occupe qu'une BANDE VERTICALE CENTRALE (~125 pt
   de haut) sur UN SEUL flanc. Conséquences :
   · un élément qui longe le bord SUR TOUTE LA HAUTEUR (colonnes d'objets de
     la Maison) ou qui est CENTRÉ verticalement (flèches de mois du Chemin)
     traverse la bande → il LUI FAUT l'inset ;
   · un élément de COIN (haut ou bas) est hors de la bande → l'inset ne le
     protège de rien et le repousse de 59 px vers le centre. PAS d'inset.
   · NE JAMAIS empiler deux sources d'inset sur le même élément : `.m-screen`,
     `.screen` et `.topbar` en posent déjà un en padding. Un padding N'AGIT
     PAS sur les enfants en position absolue (ils se calent sur la padding-box,
     dont le bord EXTERNE est celui du parent) : ceux-là portent leur inset
     eux-mêmes, les autres NON.
   ============================================================ */
/* MARGES DE SÉCURITÉ iPhone (encoche, Dynamic Island, barre home).
   Le fond d'un élément couvre sa PADDING-BOX : en posant les insets en padding
   sur le décor, l'image reste plein écran ET tout le contenu (y compris les
   enfants positionnés en absolu, calés sur la padding-box) s'écarte des bords.
   `env()` vaut 0 sans encoche → aucune incidence sur desktop.
   ⚠️ À appliquer sur CHAQUE écran, pas seulement la Maison : sur iPhone en
   paysage l'encoche masquait des cases du Chemin et la corbeille de l'écran
   Parents (retour Bruno 2026-09-08). */
.m-screen, .screen, .topbar {
  padding-left: env(safe-area-inset-left, 0px);
  padding-right: env(safe-area-inset-right, 0px);
}
/* ⚠️ Les colonnes d'objets de la Maison portent l'inset DIRECTEMENT, et non via
   le padding du parent : c'est plus fiable (pas de dépendance au conteneur
   positionné) et c'est là que le problème se voyait — la Dynamic Island
   recouvrait le portail et le coffre (retour Bruno 2026-09-08).
   La marge minimale de 1,2 % est conservée, l'inset s'y AJOUTE. */
.h-col-left  { left:  calc(1.2% + env(safe-area-inset-left, 0px)); }
.h-col-right { right: calc(1.2% + env(safe-area-inset-right, 0px)); }
/* Les flèches des missions NE portent PAS d'inset : elles sont en flux dans
   `.m-stage`, lui-même dans `.m-screen` qui pose déjà l'inset en padding.
   Le doublon rétrécissait le panneau d'autant (mesuré). */
/* chaque socle prend une part ÉGALE de la colonne (donc jamais tronqué),
   aussi grand que possible, plafonné à 96 px sur grand écran */
.h-col .hotspot { flex: 1 1 0; min-height: 0; display: flex; padding: 0; }
.h-col .hotspot { position: relative; } /* relative : les pastilles (prix, notif)
   restent ancrées sur LEUR socle, pas sur la colonne */
/* socle carré qui remplit la hauteur disponible de sa part de colonne
   (flex), plafonné à 92 px : impossible de déborder => jamais tronqué */
.h-col .hotspot .h-ico {
  height: 100%; aspect-ratio: 1 / 1;
  max-height: 96px; max-width: 96px;
  width: auto;
}
/* peu de rembourrage + image plein cadre : le dessin remplit le socle
   (les objets sont des formes arrondies à coins transparents → pas de
   débordement disgracieux sur les coins arrondis du socle) */
.h-col .hotspot .h-ico { padding: 5px; }
.h-col .hotspot .h-ico img {
  width: 100% !important; height: 100% !important;
  max-width: none; max-height: none;
  object-fit: contain;
}
/* prix en pastille de coin (comme la pastille de notification).
   ⚠️ Cette pastille est POSÉE SUR UN OBJET du décor (bol, coffre, portail…) :
   elle doit rester un badge d'angle, pas masquer le dessin. Elle faisait
   57×37 px sur une icône de 96×96 — le quart supérieur du bol disparaissait
   (retour Bruno 2026-09-08). Elle est donc plus compacte ET davantage sortie
   du cadre. Les prix des LISTES (.price-chip) gardent leur taille : la
   consigne « prix plus gros, identiques partout » portait sur eux. */
.h-price {
  position: absolute; top: -8px; right: -8px; z-index: 2;
  display: flex; align-items: center; gap: 3px;
  background: linear-gradient(135deg, var(--star) 0%, var(--star-deep) 100%);
  color: #6b4a00; border-radius: 999px;
  padding: 2px 8px; font-size: 15px; font-weight: 800;
  box-shadow: 0 2px 5px rgba(45, 42, 110, 0.3);
  border: 2px solid #fff;
}
.h-price img { width: 16px !important; height: 16px !important; }
.fond-item {
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  background: #fff; border-radius: 14px; padding: 6px; box-shadow: var(--shadow-soft);
  border: 3px solid transparent;
}
.fond-item.active-item { border-color: var(--star-deep); }
.fond-thumb { width: 100%; aspect-ratio: 16/9; border-radius: 10px; border: 2px solid #ece5fb; }

/* nid de fin de parcours : socle DORÉ scintillant (récompense ultime) */
.h-nid-gold .h-ico {
  background: radial-gradient(circle at 32% 26%, #fff7d6, #ffe08a 60%, #ffcf5a);
  border-color: #ffd66b;
  box-shadow: 0 0 0 3px #ffd66b, 0 0 18px 4px rgba(245,166,35,0.55), var(--shadow-soft);
  animation: goldPulse 1.6s ease-in-out infinite;
}
@keyframes goldPulse {
  0%,100% { box-shadow: 0 0 0 3px #ffd66b, 0 0 14px 3px rgba(245,166,35,0.45), var(--shadow-soft); }
  50% { box-shadow: 0 0 0 3px #ffe9a8, 0 0 24px 7px rgba(245,166,35,0.7), var(--shadow-soft); }
}


/* emplacement vide : garde l'alignement des 3 slots de la colonne droite */
.h-col .h-slot-empty { flex: 1 1 0; min-height: 0; pointer-events: none; }


/* ----- V3.1 : amorce magique de l'accueil (révélation du 1er tableau) ----- */
.reveal-onboard { text-align: center; gap: 6px; padding: 6px 12px; }
.reveal-onboard h2 { font-size: 20px; margin: 0; }
/* tableau de révélation : borné par la HAUTEUR pour tenir en paysage —
   AGRANDI (héros de l'écran) */
.reveal-tableau {
  height: min(66vh, 300px); aspect-ratio: 16/9; width: auto; max-width: 94vw;
  margin: 4px auto; border-radius: 16px; box-shadow: var(--shadow); border: 4px solid #fff;
  animation: revealPop 0.7s cubic-bezier(0.34, 1.56, 0.64, 1) both;
}
/* bouton « Continuer » : largeur AUTO (plus pleine largeur), discret */
.reveal-btn { margin-top: 6px; padding: 10px 40px; }
.reveal-egg {
  max-height: 42vh; max-width: 70%; margin: 4px auto; display: block;
  animation: revealPop 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) both;
  filter: drop-shadow(0 6px 12px rgba(45,42,110,0.25));
}
@keyframes revealPop {
  0% { transform: scale(0.25) rotate(-8deg); }
  65% { transform: scale(1.08) rotate(3deg); }
  100% { transform: scale(1) rotate(0); }
}


/* ===== REDESIGN DES MODALES (v=75) : compactes, sans scroll vertical ===== */
/* perso affiché en haut de la garde-robe : petit, centré */
.wardrobe-char { width: clamp(96px, 22vh, 140px); margin: 0 auto 4px; }
.wardrobe-char svg { width: 100%; height: auto; }
.modal-empty { text-align: center; font-size: 34px; padding: 14px; }

/* items des modales (accessoires, œufs, compagnons) : compacts en rangée */
.modal-row .shop-item { width: 108px; padding: 7px; position: relative; }
.modal-row .shop-item .preview { height: 86px; display: grid; place-items: center; }
.modal-row .shop-item .i-sub { font-size: 12px; margin-top: 3px; }

/* sélection : liseré DORÉ + petit badge étoile en coin (fini le check vert) */
.shop-item.active-item { border-color: var(--star-deep); box-shadow: 0 0 0 2px var(--star), var(--shadow-soft); }
.worn-badge {
  position: absolute; top: 4px; right: 4px; z-index: 2;
  width: 22px; height: 22px; border-radius: 50%;
  display: grid; place-items: center;
  background: #fff; box-shadow: var(--shadow-soft);
}
/* chip de prix (cadenas + n + étoile) sur les items verrouillés */
.price-chip {
  display: inline-flex; align-items: center; gap: 4px;
  background: linear-gradient(135deg, var(--star) 0%, var(--star-deep) 100%);
  color: #6b4a00; border: 2px solid #fff; border-radius: 999px;
  padding: 3px 12px; font-size: 17px; font-weight: 800;
  box-shadow: 0 2px 6px rgba(45, 42, 110, 0.25);
  margin-top: 4px;
}
.price-chip img { width: 20px !important; height: 20px !important; }

/* fond-item dans une rangée de modale */
.fond-item { width: 132px; position: relative; }
.fond-item .fond-thumb { width: 100%; }
.modal-livre .livre-modal { padding-top: 6px; }


/* livre V2 : cadre d'image à TAILLE FIXE (verrouillé = même cadre → la
   modale ne se redimensionne JAMAIS), flèches sur les côtés, image MAX,
   plus de pastilles en haut ni de barre de nav en bas. */
.modal-livre { max-width: 660px; }
.livre-stage { display: flex; align-items: center; justify-content: center; gap: 8px; }
.livre-frame {
  flex: none;
  height: min(66vh, 300px); aspect-ratio: 16 / 9; width: auto; max-width: 74vw;
  border-radius: var(--radius-lg); box-shadow: var(--shadow-soft);
  background-position: center; background-size: cover; background-repeat: no-repeat;
  position: relative; overflow: hidden;
}
.livre-caption {
  min-height: 44px; margin-top: 6px;
  display: flex; align-items: center; justify-content: center;
}
/* bouton d'évolution (tableau terminé) : l'enfant déclenche quand il veut */
.livre-evolve-btn {
  font-size: 16px; font-weight: 800;
  padding: 10px 26px; border-radius: 999px;
  animation: evolve-pulse 1.2s ease-in-out infinite;
}
@keyframes evolve-pulse {
  0%, 100% { transform: scale(1); box-shadow: 0 4px 0 #d18f1d, var(--shadow-soft); }
  50% { transform: scale(1.06); box-shadow: 0 4px 0 #d18f1d, 0 0 16px 3px rgba(245, 166, 35, 0.55); }
}
.livre-cap-pill {
  font-weight: 800; font-size: 14px; color: var(--ink);
  background: linear-gradient(135deg, #fff6dd, #ffeef9);
  border-radius: 999px; padding: 5px 16px;
}

/* ----- LIVRE V3.1 : GALERIE sur UNE page (Bruno) ----- */
.livre-galmodal { padding-top: 4px; }
.livre-gallery {
  display: flex; flex-wrap: wrap; justify-content: center; gap: 12px;
  width: min(640px, 92vw); max-width: 100%;
}
.livre-gtile {
  flex: 0 0 auto; width: calc(33.333% - 8px); aspect-ratio: 16 / 9;  /* 3 par rangée → 5 tableaux sur 2 rangées, ZÉRO scroll */
  border: none; border-radius: var(--radius-lg); cursor: pointer;
  background-position: center; background-size: cover; background-repeat: no-repeat;
  background-color: #efe6d4;
  position: relative; overflow: hidden;
  box-shadow: var(--shadow-soft);
  transition: transform 0.12s ease;
}
.livre-gtile:active { transform: scale(0.96); }
.livre-gtile.done:hover { transform: translateY(-2px); box-shadow: var(--shadow); }
/* MYSTÈRE : on CACHE l'image par un motif « emballage magique » (Bruno) —
   seuls les tableaux FINIS montrent l'image ; à révéler / verrouillés = motif. */
.livre-gtile.reveal, .livre-gtile.locked {
  background:
    radial-gradient(120% 80% at 28% 22%, rgba(255, 255, 255, 0.42) 0%, rgba(255, 255, 255, 0) 42%),
    linear-gradient(150deg, #8f80f2 0%, #6c5ce7 50%, #5a49c4 100%);
}
.livre-gtile.reveal::before, .livre-gtile.locked::before {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background-image:
    radial-gradient(circle, rgba(255, 255, 255, 0.78) 0, rgba(255, 255, 255, 0) 1.6px),
    radial-gradient(circle, rgba(255, 214, 107, 0.6) 0, rgba(255, 214, 107, 0) 1.3px);
  background-size: 26px 26px, 26px 26px;
  background-position: 7px 9px, 18px 19px;
  opacity: 0.7;
}
.gt-badge, .gt-lock {
  position: absolute; inset: 0; z-index: 2;
  display: grid; place-items: center;
}
.gt-badge { animation: evolve-pulse 1.2s ease-in-out infinite; }
.gt-badge img { width: 42px !important; height: 42px !important;
  filter: drop-shadow(0 0 9px rgba(255, 214, 107, 0.95)); }
.gt-lock img { width: 34px !important; height: 34px !important;
  filter: drop-shadow(0 2px 5px rgba(0, 0, 0, 0.55)); }

/* ----- PLEIN ÉCRAN : admirer un tableau fini ----- */
.modal.modal-livre-full { max-width: min(94vw, 920px); background: #fff8ec; padding: 14px; }
.livre-full { display: flex; flex-direction: column; gap: 10px; align-items: center; }
.livre-full-img {
  width: 100%; aspect-ratio: 16 / 9; max-height: 76vh;
  background-position: center; background-size: contain; background-repeat: no-repeat;
  border-radius: var(--radius-lg);
}
.livre-full-name { font-weight: 800; font-size: 17px; color: var(--ink); text-align: center; }

/* modales à CARTES (coffre, fonds, nid, nouvelles surprises) : TAILLE
   CONSTANTE (même cadre partout, demande de Bruno) et JAMAIS tronquées —
   les cartes sont dimensionnées en vh donc tiennent sur tout écran ; trop
   de cartes → défilement horizontal (jamais de coupe verticale). */
.modal.modal-cards {
  width: min(96vw, 720px); max-width: min(96vw, 720px);
  height: min(88vh, 340px); /* fallback vieilles WebView (dvh ignoré) */
  height: min(88dvh, 340px);
  justify-content: center;
}
.modal-cards .modal-row {
  gap: clamp(6px, 1.4vw, 14px); width: 100%;
  /* TOUT TIENT À L'ÉCRAN, zéro scroll : les cartes se PARTAGENT la largeur et
     rétrécissent pour rentrer en une seule rangée (cf. .shop-item ci-dessous). */
  justify-content: center; align-items: center;
  overflow: hidden; flex-wrap: nowrap;
}
.modal-cards .shop-item {
  flex: 1 1 0; min-width: 0; max-width: 168px;
  padding: 12px 8px 9px;
}
.modal-cards .shop-item .preview { height: min(30vh, 132px); margin-bottom: 8px; }
.modal-cards .shop-item .preview img { max-height: min(28vh, 124px) !important; max-width: 100% !important; height: auto !important; object-fit: contain; }
.modal-cards .shop-item .preview svg { max-height: min(28vh, 124px) !important; max-width: 100% !important; }
.modal-cards .fond-item { width: 188px; }

/* le compagnon RÉAGIT au câlin/repas : rebond élastique (montre qu'il se
   passe quelque chose — le simple remplissage du cœur ne suffisait pas) */
.char-slot.char-react { animation: char-react 0.6s cubic-bezier(0.34, 1.56, 0.64, 1); }
@keyframes char-react {
  0% { transform: scale(1); }
  25% { transform: scale(1.13) translateY(-9px); }
  52% { transform: scale(0.95) translateY(0); }
  74% { transform: scale(1.05); }
  100% { transform: scale(1); }
}

/* ----- ÉVOLUTION = un ÉVÉNEMENT (Bruno) : le nouveau compagnon ÉMERGE en
   grandissant (pop lumineux), un flash plein écran masque la bascule de forme,
   une onde de lumière jaillit du compagnon. ----- */
.char-slot.char-evolve { animation: char-evolve 1.15s cubic-bezier(0.34, 1.56, 0.64, 1); }
@keyframes char-evolve {
  0%   { transform: scale(0.45); filter: brightness(2.6) drop-shadow(0 0 34px #fff); opacity: 0.25; }
  35%  { transform: scale(1.2);  filter: brightness(1.7) drop-shadow(0 0 26px #ffd66b); opacity: 1; }
  62%  { transform: scale(0.93); filter: brightness(1.15); }
  100% { transform: scale(1); filter: none; }
}
.evolve-screenflash {
  position: fixed; inset: 0; z-index: 60; pointer-events: none;
  background: radial-gradient(circle at 50% 44%, #ffffff 0%, #fff7e0 55%, #ffe6b0 100%);
  animation: evolve-screenflash 0.75s ease-out forwards;
}
@keyframes evolve-screenflash {
  0% { opacity: 0; }
  26% { opacity: 0.95; }
  100% { opacity: 0; }
}
.evolve-burst {
  position: fixed; transform: translate(-50%, -50%); z-index: 55; pointer-events: none;
  width: 12px; height: 12px; border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 255, 255, 0.95) 0%, rgba(255, 214, 107, 0.65) 40%, transparent 70%);
  animation: evolve-burst 1s ease-out forwards;
}
@keyframes evolve-burst {
  0% { width: 12px; height: 12px; opacity: 0; }
  18% { opacity: 1; }
  100% { width: 540px; height: 540px; opacity: 0; }
}
/* cœurs qui s'envolent depuis le compagnon */
.float-heart {
  position: fixed; z-index: 150; pointer-events: none;
  font-size: 28px; line-height: 1;
  animation: heart-up 1.3s ease-out forwards;
}
.float-heart img { width: 30px; height: 30px; display: block; }
@keyframes heart-up {
  0% { transform: translateY(0) scale(0.5); opacity: 0; }
  18% { opacity: 1; transform: translateY(-12px) scale(1.1); }
  100% { transform: translateY(-130px) scale(1.2); opacity: 0; }
}
