/* MALUNE — design system.
   Reproduction fidele de l'artefact valide :
   app/claude-design/Malune - App complète.dc.html + _ds/
   Tokens, composants et animations repris a l'identique du bundle du DS.
   Ne pas inventer de valeur hors de ce fichier. */

/* ======================= TOKENS ======================= */
:root {
  /* Couleurs de fond */
  --ink-abyss: #0E1425;  --ink-night: #141B33;  --ink-night-2: #111730;
  --ink-night-high: #1A2246;
  --nebula-1: #1E2A55;   --nebula-2: #182142;   --nebula-3: #171F3E;
  --card-navy: #1D2647;  --card-glass: rgba(29,38,71,.55);

  /* Couleurs de marque */
  --amber: #FFB65C;  --halo: #FFD9A0;  --cream: #FFF4E3;
  --moonlight: #F4E9D4;  --lavender: #A7A6D9;  --muted: #6D77A0;
  --ink-on-amber: #3A2410;
  --danger: #E8A0A0;  --danger-bg: rgba(232,160,160,.10);
  --success: #8FD4A8; --success-bg: rgba(143,212,168,.12);
  --pending: #FFD9A0;

  /* Surfaces et bordures */
  --surface-page: var(--ink-abyss);
  --surface-card: var(--card-navy);
  --surface-card-glass: var(--card-glass);
  --surface-input: rgba(14,20,37,.6);
  --border-night: #2E3A66;
  --border-default: var(--border-night);
  --border-halo: rgba(255,217,160,.22);
  --border-halo-strong: rgba(255,217,160,.32);
  --blur-glass: 6px;

  /* Texte */
  --text-primary: var(--cream);
  --text-secondary: var(--lavender);
  --text-tertiary: var(--moonlight);
  --text-muted: var(--muted);
  --text-on-amber: var(--ink-on-amber);

  /* Typo */
  --font-serif: "Fraunces", Georgia, serif;
  --font-sans: "Nunito", "Segoe UI", sans-serif;
  --text-display: clamp(34px, 11vw, 54px);
  --text-wordmark: clamp(46px, 11vw, 64px);
  --text-h1: clamp(28px, 7vw, 38px);
  --text-h2: clamp(26px, 5vw, 40px);
  --text-h3: 19.5px;
  --text-body-lg: 16.5px;  --text-body: 15px;  --text-body-sm: 14.5px;
  --text-small: 13px;  --text-micro: 12px;  --text-nano: 11.5px;
  --leading-body: 1.6;  --leading-quote: 1.75;  --leading-tight: 1.14;
  --leading-title: 1.2;
  --tracking-brand: .42em;  --tracking-eyebrow: .22em;  --tracking-label: .09em;
  --weight-body: 400; --weight-semibold: 600; --weight-bold: 700; --weight-black: 800;

  /* Espacements */
  --space-1:4px;   --space-2:6px;   --space-3:8px;   --space-4:10px;
  --space-5:12px;  --space-6:14px;  --space-7:16px;  --space-8:18px;
  --space-9:20px;  --space-10:22px; --space-11:24px; --space-12:26px;
  --space-14:30px; --space-16:34px; --space-18:38px; --space-20:40px;
  --space-24:48px; --space-28:56px; --space-32:64px; --space-40:80px;
  --page-margin: 24px;  --page-max-width: 640px;  --page-max-width-wide: 760px;
  --section-padding: 72px;
  --card-padding-x: 22px; --card-padding-y: 24px;
  --input-padding-x: 16px; --input-padding-y: 14px;

  /* Formes */
  --radius-card: 20px; --radius-card-lg: 22px; --radius-input: 14px;
  --radius-pill: 99px; --radius-sm: 12px;

  /* Ombres */
  --shadow-halo: 0 0 30px rgba(255,182,92,.35);
  --shadow-halo-strong: 0 0 0 10px rgba(255,182,92,.10), 0 0 50px rgba(255,182,92,.4);
  --shadow-halo-text: 0 0 42px rgba(255,217,160,.28);
  --shadow-halo-card: 0 0 34px rgba(255,201,133,.15);
  --shadow-card-deep: 0 18px 50px rgba(6,9,22,.5);
  --shadow-card-float: 0 16px 40px rgba(6,9,22,.42);
  --shadow-button: 0 6px 24px rgba(255,182,92,.28), inset 0 1px 0 rgba(255,255,255,.35);
  --shadow-button-hover: 0 10px 30px rgba(255,182,92,.4), inset 0 1px 0 rgba(255,255,255,.35);
  --shadow-play: 0 8px 26px rgba(255,182,92,.35);

  /* Mouvement */
  --duration-instant:.15s; --duration-fast:.2s; --duration-base:.25s;
  --duration-scroll-reveal:.8s; --duration-reveal:.9s;
  --ease-standard: ease;
  --ease-toast: cubic-bezier(.2,.9,.3,1.2);
}

/* ======================= ANIMATIONS (identiques au DS) ======================= */
@keyframes ml-scintille { 0%,100%{opacity:.12} 50%{opacity:.9} }
@keyframes ml-filante { 0%{opacity:0;transform:translate(0,0)} 6%{opacity:.9}
  18%{opacity:0;transform:translate(-220px,180px)} 100%{opacity:0} }
@keyframes ml-apparition { to{opacity:1;transform:none} }
@keyframes ml-flotte { 0%,100%{transform:translateY(0)} 50%{transform:translateY(-10px)} }
@keyframes ml-respire { 0%,100%{transform:scale(1);opacity:.85} 50%{transform:scale(1.12);opacity:1} }
@keyframes ml-derive { 0%,100%{transform:translateY(0) rotate(0deg)} 50%{transform:translateY(-7px) rotate(-2deg)} }
@keyframes ml-zzz { 0%{opacity:0;transform:translate(0,6px)} 30%{opacity:.9} 70%{opacity:.5}
  100%{opacity:0;transform:translate(8px,-14px)} }
@keyframes ml-pulse { 0%,100%{box-shadow:0 0 0 10px rgba(255,182,92,.10),0 0 40px rgba(255,182,92,.32)}
  50%{box-shadow:0 0 0 16px rgba(255,182,92,.06),0 0 62px rgba(255,182,92,.5)} }
@keyframes ml-pulse-sm { 0%,100%{box-shadow:0 0 0 6px rgba(255,182,92,.12),0 8px 26px rgba(255,182,92,.35)}
  50%{box-shadow:0 0 0 12px rgba(255,182,92,.07),0 8px 26px rgba(255,182,92,.45)} }
@keyframes ml-egaliseur { 0%,100%{height:20%} 25%{height:95%} 50%{height:40%} 75%{height:70%} }
@keyframes ml-bob { 0%,100%{transform:translateY(0)} 50%{transform:translateY(5px)} }
@keyframes ml-marquee-l { from{transform:translateX(0)} to{transform:translateX(-50%)} }
@keyframes ml-marquee-r { from{transform:translateX(-50%)} to{transform:translateX(0)} }

/* ======================= BASE ======================= */
* { box-sizing: border-box; }

/* `hidden` doit toujours gagner : une regle d'auteur avec `display: flex`
   ecrase sinon le `display: none` du navigateur, et l'element continue
   d'intercepter les clics tout en etant invisible. Bug vecu sur le voile
   de la veillee, qui bloquait toute la page. */
[hidden] { display: none !important; }

body {
  margin: 0;
  background:
    radial-gradient(520px 360px at 50% -90px, var(--nebula-1) 0%, transparent 62%),
    radial-gradient(420px 320px at 88% 30%, var(--nebula-2) 0%, transparent 55%),
    linear-gradient(180deg, var(--ink-night), var(--ink-night-2));
  background-attachment: fixed;
  color: var(--text-primary);
  font: var(--weight-body) var(--text-body)/var(--leading-body) var(--font-sans);
  -webkit-font-smoothing: antialiased;
  min-height: 100vh;
}

.ml-page { position: relative; z-index: 1; max-width: 430px; margin: 0 auto; }
.ml-page.large { max-width: 760px; }
.ml-bloc { padding: 44px 26px 0; }
.ml-bloc:first-child { padding-top: 34px; }
.ml-bloc.serre { padding-top: 24px; }
.ml-fin { padding-bottom: 44px; }

h1, h2, h3 { margin: 0; font-family: var(--font-serif); font-weight: var(--weight-semibold);
  color: var(--text-primary); }
h1 { font-size: 31px; line-height: var(--leading-title); text-wrap: pretty; }
h2 { font-size: 26px; line-height: 1.25; letter-spacing: -.01em; text-wrap: pretty; }
h3 { font-size: var(--text-h3); line-height: 1.3; }
p { margin: 14px 0; }
a { color: var(--halo); }

/* ======================= MARQUE ======================= */
.ml-wordmark {
  font: var(--weight-semibold) 24px var(--font-serif);
  letter-spacing: var(--tracking-brand); margin-right: -.42em;
  text-transform: lowercase; color: var(--cream);
  text-shadow: var(--shadow-halo-text);
}
.ml-wordmark.petit { font-size: 15px; color: var(--lavender); text-shadow: none; }

/* Lune endormie (MoonMark) */
.ml-lune { position: relative; width: 96px; height: 96px; display: inline-block; }
.ml-lune::before {
  content: ""; position: absolute; inset: -55%; border-radius: 50%;
  background: radial-gradient(circle, rgba(255,182,92,.28) 0%, rgba(255,182,92,.10) 42%, transparent 68%);
  animation: ml-respire 7s ease-in-out infinite;
}
.ml-lune > svg { position: relative; width: 100%; height: 100%;
  animation: ml-derive 9s ease-in-out infinite; }
.ml-lune .zzz { position: absolute; font-family: var(--font-serif); opacity: 0; }
.ml-lune .zzz1 { color: var(--halo); font-size: 15px; right: -3px; top: 10px;
  animation: ml-zzz 5s ease-in-out infinite; }
.ml-lune .zzz2 { color: var(--lavender); font-size: 11px; right: -12px; top: 0;
  animation: ml-zzz 5s ease-in-out infinite 1.4s; }

/* Ciel etoile (StarField) */
.ml-ciel { position: fixed; inset: 0; overflow: hidden; pointer-events: none; z-index: 0; }
.ml-ciel i { position: absolute; border-radius: 50%; background: var(--moonlight);
  animation: ml-scintille 3.4s ease-in-out infinite; }
.ml-ciel i.filante { width: 2px; height: 2px; background: #fff;
  box-shadow: 0 0 6px 1px #fff; opacity: 0;
  animation: ml-filante 9s linear infinite; }

/* Etoile de separation (Divider) */
.ml-divider { display: flex; justify-content: center; padding: 4px 0; }
.ml-divider svg { opacity: .5; }

/* ======================= TYPOGRAPHIE UTILITAIRE ======================= */
.ml-eyebrow { font: var(--weight-bold) var(--text-micro) var(--font-sans);
  letter-spacing: var(--tracking-eyebrow); text-transform: uppercase;
  color: var(--text-secondary); margin: 0 0 14px; }
.ml-lede { font: var(--weight-body) 15.5px/1.6 var(--font-sans);
  color: var(--lavender); margin: 14px 0 0; }
.ml-note { font: italic var(--weight-body) var(--text-small) var(--font-serif);
  color: var(--muted); margin: 13px 0 0; }
.ml-micro { font: var(--weight-body) 12.5px/1.55 var(--font-sans); color: var(--muted); }
.ml-secondaire { color: var(--text-secondary); }
.ml-centre { text-align: center; }

/* En-tete de section (SectionHeading) */
.ml-heading { text-align: center; }
.ml-heading h2 { font-size: var(--text-h2); }
.ml-heading .sous { color: var(--text-secondary); font-size: var(--text-body-lg);
  max-width: 46ch; margin: 16px auto 0; }

/* ======================= BOUTONS ======================= */
.ml-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  cursor: pointer; font: var(--weight-bold) 15px var(--font-sans);
  text-decoration: none; border-radius: var(--radius-pill); padding: 14px 26px;
  background: linear-gradient(180deg, var(--halo), var(--amber));
  color: var(--text-on-amber); box-shadow: var(--shadow-halo); border: none;
  transition: transform .15s ease, box-shadow .2s ease, background .2s ease;
}
.ml-btn:hover { box-shadow: 0 0 42px rgba(255,182,92,.5); }
.ml-btn:active { transform: scale(.96); }
.ml-btn[disabled] { opacity: .5; cursor: not-allowed; }
.ml-btn.pleine { display: flex; width: 100%; }
.ml-btn.ghost { background: rgba(255,255,255,.04); color: var(--halo);
  border: 1.5px solid rgba(255,217,160,.32); box-shadow: none; }
.ml-btn.ghost:hover { background: rgba(255,255,255,.08); box-shadow: none; }
.ml-btn.valide { background: linear-gradient(180deg, #A8E4C0, var(--success));
  color: #12301E; box-shadow: 0 0 26px rgba(143,212,168,.3); }
.ml-btn.refus { background: transparent; color: var(--danger);
  border: 1.5px solid rgba(232,160,160,.4); box-shadow: none; }
.ml-btn.refus:hover { background: var(--danger-bg); }
.ml-btn.arrondi { border-radius: 14px; padding: 16px 20px;
  background: linear-gradient(160deg, var(--halo) 0%, var(--amber) 55%, #F5A44B 100%);
  box-shadow: var(--shadow-button); }
.ml-btn.arrondi:hover { box-shadow: var(--shadow-button-hover); transform: translateY(-1px); }
.ml-btns { display: flex; flex-direction: column; gap: 11px; }
.ml-btns.ligne { flex-direction: row; }
.ml-btns.ligne > * { flex: 1; }

/* ======================= CARTES ======================= */
.ml-carte { border-radius: var(--radius-card); border: 1px solid var(--border-default);
  padding: var(--card-padding-y) var(--card-padding-x);
  background: var(--surface-card-glass); backdrop-filter: blur(var(--blur-glass)); }
.ml-carte.solide { background: var(--surface-card); backdrop-filter: none; }
.ml-carte.float { background: linear-gradient(175deg, rgba(46,58,102,.28), rgba(29,38,71,.65));
  box-shadow: var(--shadow-card-float); backdrop-filter: none; }
.ml-carte.halo { border-color: var(--border-halo); box-shadow: var(--shadow-halo-card); }
.ml-carte.halo-fort { border-color: var(--border-halo-strong); box-shadow: var(--shadow-halo-card);
  border-radius: var(--radius-card-lg); padding: 30px 24px; }

/* Citation narrative */
.ml-citation { background: var(--surface-card-glass); backdrop-filter: blur(var(--blur-glass));
  border: 1px solid var(--border-halo); border-radius: 20px; padding: 26px 24px;
  box-shadow: var(--shadow-halo-card); }
.ml-citation p { font: italic var(--weight-body) 16.5px/var(--leading-quote) var(--font-serif);
  color: var(--moonlight); margin: 0; }
.ml-citation .signature { font: var(--weight-semibold) 13.5px var(--font-sans);
  color: var(--lavender); text-align: right; margin: 14px 0 0; }

/* ======================= PROMESSES / ETAPES ======================= */
.ml-trust { display: flex; gap: 16px; align-items: flex-start; padding: 18px 6px;
  border-bottom: 1px solid rgba(46,58,102,.6); }
.ml-trust .icone { flex: 0 0 44px; width: 44px; height: 44px; border-radius: 13px;
  background: rgba(255,182,92,.09); border: 1px solid rgba(255,182,92,.22);
  display: flex; align-items: center; justify-content: center; }
.ml-trust h3 { font-size: 18px; margin: 0; }
.ml-trust p { font-size: var(--text-body-sm); color: var(--text-secondary); margin: 4px 0 0; }

.ml-step { background: linear-gradient(175deg, rgba(46,58,102,.28), rgba(29,38,71,.65));
  border: 1px solid var(--border-default); border-radius: 20px; padding: 24px 24px 26px;
  display: flex; gap: 18px; align-items: flex-start; }
.ml-step .num { font: var(--weight-semibold) 34px/1 var(--font-serif); color: var(--amber);
  flex: 0 0 auto; width: 40px; text-align: center; text-shadow: 0 0 24px rgba(255,182,92,.5); }
.ml-step h3 { font-size: 19.5px; margin: 0; }
.ml-step p { font-size: var(--text-body); color: var(--text-secondary); margin: 6px 0 0; }

/* ======================= STATS / BADGES / CHIPS ======================= */
.ml-stats { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 10px; }
.ml-stat { text-align: center; padding: 18px 10px; background: var(--surface-card-glass);
  border: 1px solid var(--border-default); border-radius: var(--radius-card);
  backdrop-filter: blur(var(--blur-glass)); }
.ml-stat .valeur { font: var(--weight-semibold) 34px/1 var(--font-serif); color: var(--halo); }
.ml-stat .label { margin-top: 7px; font: var(--weight-bold) var(--text-nano) var(--font-sans);
  letter-spacing: .08em; color: var(--text-muted); text-transform: uppercase; }

.ml-badge { display: inline-block; background: linear-gradient(180deg, var(--halo), var(--amber));
  color: var(--text-on-amber); font: var(--weight-black) var(--text-nano) var(--font-sans);
  letter-spacing: var(--tracking-label); text-transform: uppercase; padding: 7px 18px;
  border-radius: var(--radius-pill); white-space: nowrap; box-shadow: var(--shadow-halo); }

.ml-chip { display: inline-flex; align-items: center; gap: 6px; padding: 6px 13px;
  border-radius: var(--radius-pill); font: var(--weight-bold) 12.5px var(--font-sans);
  background: rgba(167,166,217,.12); color: var(--lavender);
  border: 1px solid rgba(167,166,217,.25); }
.ml-chip.amber { background: rgba(255,182,92,.12); color: var(--halo);
  border-color: rgba(255,182,92,.3); }
.ml-chip.valide { background: var(--success-bg); color: var(--success);
  border-color: rgba(143,212,168,.3); }
.ml-chip.regle { background: rgba(14,20,37,.5); color: var(--text-muted);
  border-color: var(--border-default); font-weight: var(--weight-semibold); }
.ml-chip.danger { background: var(--danger-bg); color: var(--danger);
  border-color: rgba(232,160,160,.3); }

/* ======================= FORMULAIRES ======================= */
.ml-label { display: block; font: var(--weight-bold) var(--text-micro) var(--font-sans);
  letter-spacing: .06em; color: var(--text-secondary); margin-bottom: 8px;
  text-transform: uppercase; }
.ml-label .opt { text-transform: none; letter-spacing: 0; opacity: .7; }
.ml-field { width: 100%; padding: var(--input-padding-y) var(--input-padding-x);
  border-radius: var(--radius-input); background: var(--surface-input);
  border: 1px solid var(--border-default); color: var(--text-primary);
  font: var(--weight-body) var(--text-body) var(--font-sans); outline: none;
  transition: border-color .2s, box-shadow .2s; }
.ml-field:focus { border-color: var(--amber); box-shadow: 0 0 0 3px rgba(255,182,92,.15); }
.ml-field::placeholder { color: var(--text-muted); opacity: 1; }
textarea.ml-field { resize: vertical; min-height: 74px; }
.ml-champ + .ml-champ { margin-top: 18px; }

/* Choix en pilules (RadioPill) */
.ml-pills { display: flex; gap: 10px; }
.ml-pills label { flex: 1; text-align: center; padding: 12px; border-radius: 12px;
  cursor: pointer; font: var(--weight-body) 16px var(--font-sans);
  background: var(--surface-input); border: 1px solid var(--border-default);
  color: var(--text-primary); transition: border-color .2s, box-shadow .2s, background .2s; }
.ml-pills input { position: absolute; opacity: 0; pointer-events: none; }
/* La pastille cochée s'allume : le fond va sur le LABEL, jamais sur le span.
   `input:checked + span` était un repli pour les navigateurs sans :has() ; il
   peignait un rectangle ambre derrière les mots eux-mêmes, qui épousait les
   deux lignes de « 6-7 ans » et ressemblait à une sélection de texte (signalé
   deux fois par Adem, mal diagnostiqué la première : ce n'en était pas une). */
/* Pas de halo de 3 px autour de la pastille cochée : la bordure ambre suffit à
   dire « c'est celle-ci ». Le halo dessinait un second trait autour du premier
   et se lisait comme un encadré en trop (Adem, 28/07). Même retrait sur les
   pastilles du parcours d'inscription (.dc-pill), pour une seule grammaire. */
.ml-pills label:has(input:checked) {
  background: rgba(255,182,92,.15); border-color: var(--amber);
  color: var(--halo); font-weight: var(--weight-bold); }
.ml-pills input:checked + span { color: var(--halo);
  font-weight: var(--weight-bold); }

/* Chips selectionnables (passions, peurs) */
.ml-choix { display: flex; flex-wrap: wrap; gap: 9px; }
.ml-choix label { cursor: pointer; }
.ml-choix input { position: absolute; opacity: 0; pointer-events: none; }
.ml-choix span { display: inline-block; padding: 9px 15px; border-radius: var(--radius-pill);
  font: var(--weight-semibold) 14px var(--font-sans); background: var(--surface-input);
  border: 1px solid var(--border-default); color: var(--text-primary);
  transition: all var(--duration-fast) var(--ease-standard); }
.ml-choix input:checked + span { background: rgba(255,182,92,.15); border-color: var(--amber);
  color: var(--halo); font-weight: var(--weight-bold);
  box-shadow: 0 0 0 3px rgba(255,182,92,.15); }

/* Progression d'etapes */
.ml-etapes { display: flex; gap: 7px; }
.ml-etapes i { flex: 1; height: 3px; border-radius: 3px; background: var(--border-default);
  transition: background var(--duration-base) var(--ease-standard); }
.ml-etapes i.faite { background: var(--amber); }

/* ======================= AUDIO ======================= */
.ml-audiocard { background: linear-gradient(175deg, rgba(46,58,102,.35), rgba(29,38,71,.75));
  border: 1px solid var(--border-default); border-radius: var(--radius-card-lg);
  padding: 26px 24px; box-shadow: var(--shadow-card-deep); }
.ml-audiocard .rangee { display: flex; align-items: center; gap: 18px; }
.ml-audiocard .titre { font: var(--weight-body) 18px var(--font-serif); color: var(--text-primary); }
.ml-audiocard .legende { font-size: 13.5px; color: var(--text-secondary); margin-top: 3px; }
.ml-audiocard .temps { font-size: 12.5px; color: var(--text-secondary); margin-top: 8px; }

.ml-play { flex: 0 0 64px; width: 64px; height: 64px; border-radius: 50%; border: none;
  cursor: pointer; display: flex; align-items: center; justify-content: center;
  background: radial-gradient(circle at 32% 28%, var(--halo), var(--amber) 60%, #F09A3E);
  box-shadow: 0 0 0 6px rgba(255,182,92,.12), 0 8px 26px rgba(255,182,92,.35); }
.ml-play.joue { animation: ml-pulse-sm 2.2s ease-in-out infinite; }
.ml-play svg { fill: var(--text-on-amber); }

/* Egaliseur decoratif */
.ml-egaliseur { display: flex; align-items: flex-end; gap: 3px; height: 26px; margin-top: 10px; }
.ml-egaliseur i { width: 3px; border-radius: 2px; background: var(--amber);
  height: 20%; opacity: .45; }
.ml-audiocard.joue .ml-egaliseur i { opacity: .9; animation: ml-egaliseur 1.1s ease-in-out infinite; }
.ml-audiocard.joue .ml-egaliseur i:nth-child(3n+1) { animation-delay: 0s; }
.ml-audiocard.joue .ml-egaliseur i:nth-child(3n+2) { animation-delay: .15s; }
.ml-audiocard.joue .ml-egaliseur i:nth-child(3n+3) { animation-delay: .3s; }

/* Lecteur reel (AudioPlayer) */
.ml-lecteur { display: flex; align-items: center; gap: 16px; padding: 14px 16px;
  background: rgba(14,20,37,.45); border: 1px solid var(--border-default);
  border-radius: 16px; }
.ml-lecteur .piste { flex: 1; min-width: 0; }
.ml-onde { display: flex; align-items: center; gap: 3px; height: 34px; cursor: pointer; }
.ml-onde span { width: 3.5px; border-radius: 3px; background: var(--border-default);
  transition: background .3s; }
.ml-onde span.lu { background: var(--amber); }
.ml-lecteur .compteur { display: flex; justify-content: space-between;
  font: var(--weight-bold) var(--text-nano) var(--font-sans); color: var(--text-muted);
  margin-top: 7px; }
.ml-lecteur .gros { width: 64px; height: 64px; border-radius: 50%; border: none; flex: 0 0 auto;
  cursor: pointer; background: linear-gradient(180deg, var(--halo), var(--amber));
  box-shadow: var(--shadow-halo); display: flex; align-items: center; justify-content: center; }
.ml-lecteur .gros.joue { box-shadow: var(--shadow-halo-strong); }

/* ======================= TEMOIGNAGES ======================= */
.ml-temoignage { position: relative; background: linear-gradient(180deg, var(--card-navy) 0%, #1A2344 100%);
  border: 1px solid var(--border-default); border-radius: var(--radius-card-lg);
  padding: 24px 22px 20px; box-shadow: var(--shadow-card-float); overflow: hidden; }
.ml-temoignage .guillemet { font: var(--weight-body) 56px/.6 var(--font-serif);
  color: var(--amber); opacity: .32; height: 22px; display: block; margin-bottom: 6px; }
.ml-temoignage .quote { font: italic var(--weight-body) 15.5px/1.65 var(--font-serif);
  color: var(--moonlight); margin: 0; }
.ml-temoignage .auteur { display: flex; align-items: center; gap: 11px; margin-top: 16px; }
.ml-temoignage .initiale { width: 38px; height: 38px; border-radius: 50%; flex: 0 0 auto;
  background: linear-gradient(180deg, var(--halo), var(--amber)); color: var(--text-on-amber);
  font: var(--weight-black) 16px var(--font-sans); display: flex;
  align-items: center; justify-content: center; }
.ml-temoignage .nom { font: var(--weight-bold) 14.5px var(--font-sans); color: var(--text-primary); }
.ml-temoignage .role { font-size: var(--text-micro); color: var(--text-muted); }

/* ======================= LIGNES LIVREES / STUDIO ======================= */
.ml-livree { display: flex; align-items: center; gap: 14px; padding: 15px 4px;
  border-bottom: 1px solid rgba(46,58,102,.55); }
.ml-livree:last-child { border-bottom: none; }
.ml-livree .rond { width: 44px; height: 44px; border-radius: 14px; flex: 0 0 auto;
  background: rgba(255,182,92,.09); border: 1px solid rgba(255,182,92,.22);
  display: flex; align-items: center; justify-content: center; }
.ml-livree .titre { font: var(--weight-body) 16.5px var(--font-serif); color: var(--text-primary); }
.ml-livree .meta { font-size: var(--text-micro); color: var(--text-muted); margin-top: 2px; }

/* Alertes du controle qualite */
.ml-alerte { border-radius: var(--radius-sm); padding: 12px 14px; margin: 10px 0;
  font-size: var(--text-body); border: 1px solid var(--border-default);
  background: rgba(14,20,37,.5); color: var(--text-secondary); }
.ml-alerte.bloquant { border-color: rgba(232,160,160,.4); background: var(--danger-bg);
  color: var(--danger); }
.ml-alerte.attention { border-color: rgba(255,182,92,.35); color: var(--halo); }

/* FAQ */
.ml-faq { background: rgba(29,38,71,.55); border: 1px solid var(--border-default);
  border-radius: 16px; overflow: hidden; transition: border-color .25s; }
.ml-faq[open] { border-color: rgba(255,182,92,.4); }
.ml-faq summary { cursor: pointer; padding: 18px 20px; list-style: none;
  font: 500 17px var(--font-serif); color: var(--text-primary);
  display: flex; justify-content: space-between; align-items: center; gap: 12px; }
.ml-faq summary::-webkit-details-marker { display: none; }
.ml-faq summary::after { content: "+"; color: var(--amber); font: var(--weight-body) 22px var(--font-sans);
  transition: transform .25s; flex: 0 0 auto; }
.ml-faq[open] summary::after { transform: rotate(45deg); }
.ml-faq .reponse { padding: 0 20px 20px; font-size: var(--text-body);
  color: var(--text-secondary); line-height: 1.65; }

/* Prix */
.ml-prix { display: flex; align-items: baseline; justify-content: center; gap: 10px; }
.ml-prix .barre { font: var(--weight-bold) 17px var(--font-sans); color: var(--muted);
  text-decoration: line-through; }
.ml-prix .montant { font: var(--weight-black) 44px/1 var(--font-sans); color: var(--cream); }
.ml-prix .par { font: var(--weight-semibold) 15px var(--font-sans); color: var(--lavender); }
.ml-avantages { display: flex; flex-direction: column; gap: 9px; text-align: left;
  font: var(--weight-body) var(--text-body-sm)/1.5 var(--font-sans); color: var(--moonlight); }
.ml-avantages > div { display: flex; gap: 10px; }
.ml-avantages .coche { color: var(--halo); font-weight: var(--weight-black); }

/* Toast */
.ml-toast { position: fixed; left: 50%; bottom: 30px; z-index: 60;
  transform: translate(-50%, 80px); opacity: 0;
  background: linear-gradient(180deg, var(--halo), var(--amber)); color: var(--text-on-amber);
  padding: 14px 26px; border-radius: var(--radius-pill);
  font: var(--weight-bold) var(--text-body-sm) var(--font-sans);
  box-shadow: var(--shadow-halo); transition: transform .5s var(--ease-toast), opacity .4s;
  pointer-events: none; }
.ml-toast.visible { transform: translate(-50%, 0); opacity: 1; }

/* Barre d'etat facon telephone (haut d'ecran) */
.ml-statut { display: flex; justify-content: space-between; align-items: center;
  padding: 14px 24px 4px; font: var(--weight-bold) var(--text-small) var(--font-sans);
  color: var(--moonlight); }
.ml-statut .batterie { width: 21px; height: 10px; border: 1px solid rgba(244,233,212,.5);
  border-radius: 3px; position: relative; }
.ml-statut .batterie i { position: absolute; inset: 1.5px; right: 7px;
  background: var(--moonlight); border-radius: 1.5px; }

/* Apparition au defilement */
.ml-reveal { opacity: 0; transform: translateY(12px);
  animation: ml-apparition var(--duration-reveal) var(--ease-standard) forwards; }

/* Texte d'histoire */
.ml-histoire { font: var(--weight-body) 17px/var(--leading-quote) var(--font-serif);
  color: var(--moonlight); white-space: pre-wrap; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .001ms !important;
    animation-iteration-count: 1 !important; transition-duration: .001ms !important; }
  .ml-reveal { opacity: 1; transform: none; }
}

/* ======================= NAVIGATION D'ÉCRAN ======================= */
/* Trois colonnes, les deux latérales de MÊME largeur : c'est la seule façon
   d'avoir le titre exactement au milieu de l'écran. En `space-between`, il
   était poussé de 12 px par le chevron de retour, et d'autant plus quand la
   droite portait les quatre étoiles d'étape (74 px) — le décalage changeait
   donc d'un écran à l'autre. Adem, 02/08 : « le titre est au milieu pour Sa
   fiche et Offrir […] fais en sorte que la mise en page soit identique ». */
.ml-nav { display: grid; grid-template-columns: 74px 1fr 74px; align-items: center;
  gap: 12px; padding: 10px 24px 0; }
.ml-nav .titre { text-align: center; }
.ml-nav > a { text-decoration: none; color: var(--lavender); font-size: 26px;
  line-height: 1; width: 24px; }
.ml-nav .titre { font: var(--weight-bold) var(--text-micro) var(--font-sans);
  letter-spacing: var(--tracking-eyebrow); text-transform: uppercase;
  color: var(--text-secondary); }
.ml-etoiles { display: flex; gap: 5px; width: 74px; justify-content: flex-end; }
.ml-etoiles svg path { fill: var(--border-default); }
.ml-etoiles svg.faite path { fill: var(--halo); }

/* Carte « fiche de l'histoire » (1B) */
.ml-fiche-extrait { background: rgba(14,20,37,.45); border: 1px solid var(--border-default);
  border-radius: 18px; padding: 20px 20px 18px; }
.ml-fiche-extrait .date { font: var(--weight-bold) var(--text-nano) var(--font-sans);
  letter-spacing: .12em; text-transform: uppercase; color: var(--muted); margin: 0; }
.ml-fiche-extrait .titre { font: var(--weight-body) 19px var(--font-serif);
  color: var(--text-primary); margin: 10px 0 0; }
.ml-fiche-extrait .pour { font-size: var(--text-body-sm); color: var(--text-secondary);
  margin: 8px 0 0; }
.ml-fiche-extrait .chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; }

/* Prix verrouillé tant que l'extrait n'a pas été lancé (1B) */
.ml-verrou { display: flex; align-items: center; gap: 12px; padding: 16px 18px;
  background: rgba(14,20,37,.4); border: 1px dashed var(--border-default);
  border-radius: 16px; }
.ml-verrou p { margin: 0; font: italic var(--weight-body) var(--text-body-sm) var(--font-serif);
  color: var(--muted); }

/* Choix de formule (1C) */
.ml-formules { display: flex; gap: 11px; }
.ml-formules label { flex: 1; cursor: pointer; }
.ml-formules input { position: absolute; opacity: 0; pointer-events: none; }
.ml-formules .boite { padding: 16px 14px; border-radius: 16px; text-align: center;
  background: var(--surface-input); border: 1px solid var(--border-default);
  transition: all var(--duration-fast) var(--ease-standard); }
.ml-formules input:checked + .boite { background: rgba(255,182,92,.12);
  border-color: var(--amber); box-shadow: 0 0 0 3px rgba(255,182,92,.12); }
.ml-formules .type { font: var(--weight-bold) var(--text-micro) var(--font-sans);
  letter-spacing: .08em; text-transform: uppercase; color: var(--text-secondary); }
.ml-formules .prix { font: var(--weight-black) 26px var(--font-sans);
  color: var(--cream); margin-top: 6px; }
.ml-formules .detail { font-size: var(--text-micro); color: var(--muted); margin-top: 4px; }

/* Déroulé de la nuit (1E) */
.ml-etapes-nuit { display: flex; flex-direction: column; gap: 2px; }
.ml-moment { display: flex; gap: 16px; align-items: flex-start; padding: 14px 0; }
.ml-moment .puce { flex: 0 0 auto; width: 12px; display: flex; flex-direction: column;
  align-items: center; padding-top: 5px; }
.ml-moment .puce b { width: 9px; height: 9px; border-radius: 50%;
  background: var(--amber); box-shadow: 0 0 14px rgba(255,182,92,.6); }
.ml-moment .puce i { flex: 1; width: 1px; background: var(--border-default);
  margin-top: 6px; min-height: 22px; }
.ml-moment:last-child .puce i { display: none; }
.ml-moment .quand { font: var(--weight-bold) var(--text-micro) var(--font-sans);
  letter-spacing: .08em; text-transform: uppercase; color: var(--halo); margin: 0; }
.ml-moment .quoi { font-size: var(--text-body-sm); color: var(--text-secondary);
  margin: 5px 0 0; }

/* Récapitulatif de fiche, façon ex-libris (1D) */
.ml-exlibris { background: linear-gradient(175deg, rgba(46,58,102,.3), rgba(29,38,71,.7));
  border: 1px solid var(--border-halo); border-radius: 18px; padding: 20px; }
.ml-exlibris .entete { font: var(--weight-body) 17px var(--font-serif);
  color: var(--halo); margin: 0 0 14px; text-align: center; }
.ml-exlibris .ligne { display: flex; justify-content: space-between; gap: 14px;
  padding: 9px 0; border-top: 1px solid rgba(46,58,102,.55); }
.ml-exlibris .ligne:first-of-type { border-top: none; }
.ml-exlibris .cle { font: var(--weight-bold) var(--text-micro) var(--font-sans);
  letter-spacing: .06em; text-transform: uppercase; color: var(--muted); flex: 0 0 auto; }
.ml-exlibris .val { font-size: var(--text-body-sm); color: var(--moonlight);
  text-align: right; }

/* Bandeau de réassurance */
.ml-reassurance { display: flex; gap: 12px; align-items: flex-start; padding: 15px 16px;
  background: rgba(255,182,92,.05); border: 1px solid rgba(255,182,92,.18);
  border-radius: 14px; }
.ml-reassurance p { margin: 0; font-size: 13.5px; color: var(--text-secondary); }

/* ======================= LE CIEL (1H) =======================
   Reproduction exacte du design : des points lumineux, pas des étoiles
   dessinées. Le point sélectionné grossit et s'entoure d'un halo. */
.ml-ciel-carte { position: relative; height: 290px; margin-top: 10px; }
.ml-ciel-carte .trace { position: absolute; inset: 0; width: 100%; height: 100%;
  overflow: visible; }
.ml-ciel-carte .etoile {
  position: absolute; width: 26px; height: 26px; margin: -13px 0 0 -13px;
  border-radius: 50%; border: none; cursor: pointer; background: transparent;
  display: flex; align-items: center; justify-content: center; padding: 0;
  -webkit-tap-highlight-color: transparent;
}
.ml-ciel-carte .etoile i {
  width: 7px; height: 7px; border-radius: 50%; background: var(--halo);
  box-shadow: 0 0 10px rgba(255, 217, 160, .7);
  transition: all .25s ease;
}
.ml-ciel-carte .etoile.active i {
  width: 11px; height: 11px;
  box-shadow: 0 0 0 5px rgba(255, 182, 92, .18), 0 0 18px rgba(255, 182, 92, .9);
}
/* La prochaine livraison existe déjà, en pointillés qui respirent. */
.ml-ciel-carte .future {
  position: absolute; width: 11px; height: 11px; margin: -5px 0 0 -5px;
  border-radius: 50%; border: 1.5px dashed var(--muted);
  animation: ml-respire 4.5s ease-in-out infinite;
}
.ml-ciel-carte .etiquette-future {
  position: absolute; margin: 10px 0 0 -14px;
  font: var(--weight-semibold) 10px var(--font-sans); color: var(--muted);
}

/* La fiche de l'étoile touchée. */
.ml-selection { background: var(--surface-card-glass); backdrop-filter: blur(var(--blur-glass));
  border: 1px solid var(--border-night); border-radius: 20px; padding: 19px 20px;
  margin-top: 8px; }
.ml-selection .titre { font: var(--weight-semibold) 17.5px/1.35 var(--font-serif);
  color: var(--cream); margin: 0; }
.ml-selection .pour { font: italic var(--weight-body) 13px var(--font-serif);
  color: var(--halo); margin: 6px 0 0; }
.ml-selection .meta { font: var(--weight-body) 12.5px var(--font-sans);
  color: var(--lavender); margin: 4px 0 0; }
.ml-selection .actions { display: flex; align-items: center; gap: 14px; margin-top: 14px; }
.ml-selection .lire { width: 44px; height: 44px; border-radius: 50%; border: none;
  cursor: pointer; flex: 0 0 auto; display: flex; align-items: center; justify-content: center;
  background: linear-gradient(180deg, var(--halo), var(--amber));
  box-shadow: var(--shadow-halo); }
.ml-selection .lire span { width: 0; height: 0; margin-left: 3px;
  border-left: 13px solid var(--ink-on-amber);
  border-top: 8px solid transparent; border-bottom: 8px solid transparent; }
.ml-selection a { font: var(--weight-bold) 13.5px var(--font-sans); color: var(--halo);
  text-decoration: none; }

/* Dégradé de fin de texte sur la page histoire (1I) */
.ml-fondu { position: absolute; left: 0; right: 0; bottom: 0; height: 90px;
  background: linear-gradient(180deg, transparent, var(--card-glass) 65%);
  pointer-events: none; }

/* ======================= LA VEILLÉE (1J) ======================= */
.ml-veillee { position: relative; min-height: 74vh; display: flex;
  align-items: center; justify-content: center; }
.ml-veillee .contenu { display: flex; flex-direction: column; align-items: center;
  padding: 0 26px; width: 100%; }
.ml-play.grand { width: 96px; height: 96px; flex: 0 0 96px;
  box-shadow: 0 0 0 10px rgba(255,182,92,.08), 0 10px 40px rgba(255,182,92,.4); }
.ml-play.grand.joue { animation: ml-pulse 3.4s ease-in-out infinite; }
.ml-veillee .voile { position: fixed; inset: 0; z-index: 40; display: flex;
  flex-direction: column; align-items: center; justify-content: center; gap: 14px;
  background: var(--ink-abyss); opacity: 0; cursor: pointer;
  transition: opacity 1.6s ease; }
.ml-veillee .voile.visible { opacity: .97; }
.ml-veillee .bonne-nuit { font: 400 19px var(--font-serif); color: rgba(255,217,160,.5);
  margin: 0; }
.ml-veillee .rallumer { font: 400 12px var(--font-sans); color: rgba(109,119,160,.7);
  margin: 0; letter-spacing: .06em; }

/* ======================= RÉGLAGES (1K) ======================= */
.ml-bascule { width: 50px; height: 29px; border-radius: var(--radius-pill); flex: 0 0 auto;
  border: 1px solid var(--border-default); background: var(--surface-input);
  cursor: pointer; padding: 0; position: relative;
  transition: background var(--duration-base), border-color var(--duration-base); }
.ml-bascule span { position: absolute; top: 3px; left: 3px; width: 21px; height: 21px;
  border-radius: 50%; background: var(--muted); transition: all var(--duration-base); }
.ml-bascule.on { background: rgba(255,182,92,.2); border-color: var(--amber); }
.ml-bascule.on span { left: 24px; background: linear-gradient(180deg, var(--halo), var(--amber)); }

/* ======================= STUDIO (1M / 1O) ======================= */
.ml-prenom { background: rgba(255,182,92,.18); color: var(--halo);
  border-radius: 4px; padding: 0 3px; }
.ml-choix span.grave { border-color: rgba(232,160,160,.45); color: var(--danger); }
.ml-choix input:checked + span.grave { background: var(--danger-bg);
  border-color: var(--danger); color: var(--danger);
  box-shadow: 0 0 0 3px rgba(232,160,160,.14); }

/* Feuille qui monte du bas (renvoi à l'atelier) */
.ml-feuille { position: fixed; inset: 0; z-index: 50; display: flex; align-items: flex-end;
  background: rgba(6,9,22,.6); opacity: 0; transition: opacity var(--duration-base); }
.ml-feuille.visible { opacity: 1; }
.ml-feuille .contenu { width: 100%; max-width: 430px; margin: 0 auto;
  background: var(--ink-night); border-radius: 24px 24px 0 0;
  border: 1px solid var(--border-default); border-bottom: none;
  padding: 14px 24px 30px; max-height: 88vh; overflow-y: auto;
  transform: translateY(100%); transition: transform var(--duration-base) var(--ease-toast); }
.ml-feuille.visible .contenu { transform: none; }
.ml-feuille .poignee { width: 40px; height: 4px; border-radius: 4px;
  background: var(--border-default); margin: 0 auto 18px; }

/* Pas de surlignage de texte ni de flash au toucher sur les choix cliquables :
   on tape sur une pilule, on ne sélectionne pas son libellé. */
.ml-pills label, .ml-choix label, .ml-formules label,
.ml-btn, .ml-chip, .ml-faq summary, .ml-bascule, .ml-play, .ml-etoiles svg {
  -webkit-tap-highlight-color: transparent;
  -webkit-user-select: none;
  user-select: none;
}
.ml-pills label span, .ml-choix label span, .ml-formules .boite { pointer-events: none; }
.ml-pills label:focus-within span, .ml-choix label:focus-within span {
  outline: 2px solid rgba(255,182,92,.5); outline-offset: 2px;
}

/* ======================= BARRE DE NAVIGATION (espace famille) ======================= */
.ml-tabbar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 45;
  display: grid; grid-template-columns: repeat(5, 1fr);
  max-width: 430px; margin: 0 auto;
  background: rgba(14, 20, 37, .88);
  backdrop-filter: blur(18px); -webkit-backdrop-filter: blur(18px);
  border-top: 1px solid var(--border-default);
  padding: 9px 6px calc(9px + env(safe-area-inset-bottom, 0px));
}
.ml-tabbar a {
  display: flex; flex-direction: column; align-items: center; gap: 5px;
  text-decoration: none; color: var(--muted); padding: 4px 2px;
  -webkit-tap-highlight-color: transparent; -webkit-user-select: none; user-select: none;
  transition: color var(--duration-fast) var(--ease-standard);
}
/* `overflow: visible` : un SVG rogne par défaut ce qui sort de son viewBox, et
   les silhouettes qui sautent y perdaient le haut du crâne — une barre
   invisible leur coupait la tête en plein vol (Adem, 29/07). Le lien et la
   barre ne rognent rien, donc le saut peut désormais dépasser librement. */
.ml-tabbar svg { width: 23px; height: 23px; overflow: visible; }
.ml-tabbar span {
  font: var(--weight-bold) 10.5px var(--font-sans);
  letter-spacing: .02em; white-space: nowrap;
}
.ml-tabbar a:hover { color: var(--lavender); }
.ml-tabbar a.actif { color: var(--halo); }
.ml-tabbar a.actif svg { filter: drop-shadow(0 0 9px rgba(255, 182, 92, .55)); }
/* Le point sous l'onglet actif a été retiré (Adem, 28/07) : la couleur ambre
   et le halo disent déjà où l'on est, et l'icône animée le dit encore mieux. */
.ml-tabbar a { position: relative; }

/* Les pages à barre laissent la place en bas. */
body.avec-tabbar .ml-fin { padding-bottom: calc(96px + env(safe-area-inset-bottom, 0px)); }
body.avec-tabbar .ml-feuille .contenu { padding-bottom: calc(110px + env(safe-area-inset-bottom, 0px)); }

/* La veillée passe au-dessus de tout : l'écran doit pouvoir disparaître. */
.ml-veillee .voile { z-index: 60; }

/* ======================= FINITION : MATIÈRE ET SOUFFLE =======================
   Ce qui distingue une app soignée d'une maquette : du grain, de la hiérarchie
   typographique, des bordures qui se voient moins, et des choses qui réagissent
   sous le doigt. Rien d'ajouté ici n'invente de couleur hors du design system. */

/* Grain photographique très léger : donne de la matière au noir, qui sinon
   sonne « écran » plutôt que « nuit ». */
body::after {
  content: ""; position: fixed; inset: 0; z-index: 2; pointer-events: none;
  opacity: .035;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* Hiérarchie : les titres respirent davantage, les libellés se font discrets. */
h1 { font-size: clamp(30px, 8.5vw, 36px); letter-spacing: -.018em; }
h2 { letter-spacing: -.016em; }
.ml-eyebrow { font-size: 11px; opacity: .82; }
.ml-lede { font-size: 16px; line-height: 1.62; }

/* Les cartes flottent au lieu d'être encadrées : bordure plus discrète,
   dégradé interne, ombre portée douce. */
.ml-carte {
  border-color: rgba(46, 58, 102, .55);
  background:
    linear-gradient(180deg, rgba(255, 255, 255, .022), transparent 42%),
    var(--surface-card-glass);
  box-shadow: 0 1px 0 rgba(255, 255, 255, .035) inset, 0 12px 34px rgba(6, 9, 22, .32);
}
.ml-carte.halo { box-shadow: 0 1px 0 rgba(255, 217, 160, .07) inset,
  var(--shadow-halo-card), 0 14px 40px rgba(6, 9, 22, .34); }

/* Tout ce qui se touche répond au doigt. */
.ml-btn:active { transform: scale(.985); }
.ml-carte a:active, .ml-livree:active { opacity: .82; }
.ml-choix span:active, .ml-pills label:active span { transform: scale(.98); }
.ml-choix span, .ml-pills label span { transition: transform .12s ease, all var(--duration-fast); }

/* Pas d'apparition au chargement : la page doit etre la, tout de suite.
   (Une cascade en `backwards` renvoyait les blocs a l'invisible en fin
   d'animation — page blanche. Le mouvement gratuit ne vaut jamais ce risque.) */

/* ======================= BARRE : RÉACTION AU TOUCHER ======================= */
/* Chaque icône a son geste : la lune se couche, l'étoile scintille,
   le profil respire, le cœur bat, le rouage tourne. */
.ml-tabbar svg { transition: transform .16s cubic-bezier(.34,.8,.3,1); }
.ml-tabbar a:active svg { transform: scale(.9); }

/* ---------------------------------------------------------------------------
   LES CINQ ICÔNES, UNE VIE CHACUNE.

   Ces animations existaient déjà, mais uniquement `.presse`, c'est-à-dire au
   clic — et un clic sur un lien change de page dans la seconde : personne ne
   les a jamais vues (Adem, 28/07 : « j'aimerais avoir une animation sur chaque
   icône »). Elles se jouent désormais sur l'onglet ACTIF, donc sur celui où on
   vient d'arriver, et se rejouent avec une longue pause : le geste occupe le
   premier quart du cycle, le reste est immobile. Une barre où cinq icônes
   s'agitent en continu serait insupportable dans une chambre à 21 h.
   --------------------------------------------------------------------------- */

/* 1 · Ce soir — la lune se remplit puis se vide, comme ses phases */
@keyframes ml-icone-lune {
  0%, 4%   { transform: rotate(0deg); }
  12%      { transform: rotate(-16deg); }
  26%      { transform: rotate(4deg); }
  34%, 100% { transform: rotate(0deg); }
}
@keyframes ml-icone-lune-fond {
  0%, 6%   { fill: rgba(255, 182, 92, 0); }
  20%, 30% { fill: rgba(255, 182, 92, .85); }
  46%, 100% { fill: rgba(255, 182, 92, 0); }
}
.ml-tabbar a.actif[data-icone="lune"] svg {
  animation: ml-icone-lune 6.5s ease-in-out infinite;
}
.ml-tabbar a.actif[data-icone="lune"] path {
  animation: ml-icone-lune-fond 6.5s ease-in-out infinite;
}

/* 2 · Le ciel — l'étoile scintille : elle s'allume fort, faiblit, revient */
@keyframes ml-icone-etoile {
  0%, 3%    { transform: scale(1) rotate(0deg); opacity: 1; filter: none; }
  9%        { transform: scale(1.2) rotate(9deg); opacity: 1;
              filter: drop-shadow(0 0 11px rgba(255, 217, 160, 1)); }
  17%       { transform: scale(.95) rotate(0deg); opacity: .35; filter: none; }
  25%       { transform: scale(1.14) rotate(-7deg); opacity: 1;
              filter: drop-shadow(0 0 13px rgba(255, 182, 92, .95)); }
  34%, 100% { transform: scale(1) rotate(0deg); opacity: 1; filter: none; }
}
.ml-tabbar a.actif[data-icone="etoile"] svg {
  animation: ml-icone-etoile 5.5s ease-in-out infinite;
}

/* 3 · Sa fiche — le petit bonhomme saute, et s'écrase en retombant */
@keyframes ml-icone-profil {
  0%, 6%    { transform: translateY(0) scaleY(1); }
  11%       { transform: translateY(1px) scaleY(.88); }   /* il plie les genoux */
  20%       { transform: translateY(-6px) scaleY(1.1); }  /* il décolle */
  29%       { transform: translateY(0) scaleY(.92); }     /* il retombe */
  36%, 100% { transform: translateY(0) scaleY(1); }
}
.ml-tabbar a.actif[data-icone="profil"] svg {
  animation: ml-icone-profil 5.8s cubic-bezier(.3, .9, .3, 1) infinite;
}

/* 4 · Offrir — le cœur tremble, s'arrête, retremble (deux battements) */
@keyframes ml-icone-coeur {
  0%, 5%    { transform: scale(1) rotate(0deg); }
  9%        { transform: scale(1.18) rotate(-4deg); }
  13%       { transform: scale(.98) rotate(3deg); }
  17%       { transform: scale(1.13) rotate(-2deg); }
  22%       { transform: scale(1) rotate(0deg); }
  26%       { transform: scale(1.07) rotate(2deg); }
  31%, 100% { transform: scale(1) rotate(0deg); }
}
.ml-tabbar a.actif[data-icone="coeur"] svg {
  animation: ml-icone-coeur 5.2s ease-out infinite;
  filter: drop-shadow(0 0 9px rgba(255, 182, 92, .55));
}

/* 5 · Réglages — le rouage fait un tour entier, puis se cale */
@keyframes ml-icone-rouage {
  0%, 8%    { transform: rotate(0deg); }
  30%       { transform: rotate(340deg); }
  36%       { transform: rotate(352deg); }
  42%, 100% { transform: rotate(360deg); }
}
.ml-tabbar a.actif[data-icone="rouage"] svg {
  animation: ml-icone-rouage 6s cubic-bezier(.3, .85, .35, 1) infinite;
}

/* Au clic, l'icône répond par une pression courte (`.presse`, définie plus
   bas) : on ne voit que ~200 ms avant que la page suivante n'arrive, donc
   c'est un accusé de réception, pas un numéro. Le vrai numéro se joue à
   l'arrivée, sur l'onglet devenu actif.

   Personne ne doit subir ces mouvements : réglage système respecté. */

/* 6 · Studio, à écouter — l'onde bat comme un son qui passe */
@keyframes ml-icone-onde {
  0%, 6%    { transform: scaleY(1); }
  14%       { transform: scaleY(.72); }
  22%       { transform: scaleY(1.18); }
  30%, 100% { transform: scaleY(1); }
}
.ml-tabbar a.actif[data-icone="onde"] svg {
  animation: ml-icone-onde 4.6s ease-in-out infinite;
}

/* 7 · Studio, validées — la coche se TRACE dans le rond, puis s'efface.
   Adem n'aimait pas la version qui faisait sauter l'icône entière : ici seul
   le trait vit, comme une main qui coche puis tourne la page. Le chemin fait
   ~11 unités : dasharray un peu plus long pour que le trait parte propre. */
.ml-tabbar a[data-icone="coche"] .trait { stroke-dasharray: 12; }
/* Un SEUL tracé par tour : la version précédente effaçait le trait par
   l'autre bout, ce qui donnait l'impression d'un second tracé avant la
   coupure (Adem, 29/07 : « y'a 2 boucles avant le reset au lieu d'une »).
   Ici : la coche se dessine, se lit, s'efface en fondu, et le retour du
   trait à zéro se fait pendant qu'elle est invisible. */
@keyframes ml-icone-coche {
  0%       { stroke-dashoffset: 12; opacity: 0; }
  6%       { stroke-dashoffset: 12; opacity: 1; }
  26%      { stroke-dashoffset: 0;  opacity: 1; }   /* tracée */
  72%      { stroke-dashoffset: 0;  opacity: 1; }   /* on la laisse se lire */
  84%      { stroke-dashoffset: 0;  opacity: 0; }   /* fondu */
  85%,100% { stroke-dashoffset: 12; opacity: 0; }   /* retour, invisible */
}
.ml-tabbar a.actif[data-icone="coche"] .trait {
  animation: ml-icone-coche 5s ease-in-out infinite;
}

/* 8 · Studio, atelier — la flèche fait demi-tour */
@keyframes ml-icone-retour {
  0%, 10%   { transform: rotate(0deg); }
  34%       { transform: rotate(-352deg); }
  42%, 100% { transform: rotate(-360deg); }
}
.ml-tabbar a.actif[data-icone="retour"] svg {
  animation: ml-icone-retour 6.2s cubic-bezier(.3, .85, .35, 1) infinite;
}

/* 9 · Studio, familles — elles sautent, exactement comme la silhouette de la
   barre des familles (Adem, 29/07 : « l'échange rend mal, fais-les juste
   sauter comme pour la partie famille »). Le petit part un cheveu après le
   grand : deux sauts identiques au même instant font mécanique. */
.ml-tabbar a[data-icone="familles"] .grand,
.ml-tabbar a[data-icone="familles"] .petit { transform-box: fill-box;
  transform-origin: center bottom; }
.ml-tabbar a.actif[data-icone="familles"] .grand {
  animation: ml-icone-profil 5.8s cubic-bezier(.3, .9, .3, 1) infinite;
}
.ml-tabbar a.actif[data-icone="familles"] .petit {
  animation: ml-icone-profil 5.8s cubic-bezier(.3, .9, .3, 1) infinite;
  animation-delay: .12s;
}

@media (prefers-reduced-motion: reduce) {
  .ml-tabbar a.actif svg, .ml-tabbar a.actif path { animation: none !important; }
  .ml-alerte-livraison.urgent { animation: none !important; }
}

/* Les pages à barre laissent la place en bas. */
body.avec-tabbar .ml-fin { padding-bottom: calc(96px + env(safe-area-inset-bottom, 0px)); }
body.avec-tabbar .ml-feuille .contenu { padding-bottom: calc(110px + env(safe-area-inset-bottom, 0px)); }

/* La veillée passe au-dessus de tout : l'écran doit pouvoir disparaître. */
.ml-veillee .voile { z-index: 60; }

/* ======================= FINITION : MATIÈRE ET SOUFFLE =======================
   Ce qui distingue une app soignée d'une maquette : du grain, de la hiérarchie
   typographique, des bordures qui se voient moins, et des choses qui réagissent
   sous le doigt. Rien d'ajouté ici n'invente de couleur hors du design system. */

/* Grain photographique très léger : donne de la matière au noir, qui sinon
   sonne « écran » plutôt que « nuit ». */
body::after {
  content: ""; position: fixed; inset: 0; z-index: 2; pointer-events: none;
  opacity: .035;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* Hiérarchie : les titres respirent davantage, les libellés se font discrets. */
h1 { font-size: clamp(30px, 8.5vw, 36px); letter-spacing: -.018em; }
h2 { letter-spacing: -.016em; }
.ml-eyebrow { font-size: 11px; opacity: .82; }
.ml-lede { font-size: 16px; line-height: 1.62; }

/* Les cartes flottent au lieu d'être encadrées : bordure plus discrète,
   dégradé interne, ombre portée douce. */
.ml-carte {
  border-color: rgba(46, 58, 102, .55);
  background:
    linear-gradient(180deg, rgba(255, 255, 255, .022), transparent 42%),
    var(--surface-card-glass);
  box-shadow: 0 1px 0 rgba(255, 255, 255, .035) inset, 0 12px 34px rgba(6, 9, 22, .32);
}
.ml-carte.halo { box-shadow: 0 1px 0 rgba(255, 217, 160, .07) inset,
  var(--shadow-halo-card), 0 14px 40px rgba(6, 9, 22, .34); }

/* Tout ce qui se touche répond au doigt. */
.ml-btn:active { transform: scale(.985); }
.ml-carte a:active, .ml-livree:active { opacity: .82; }
.ml-choix span:active, .ml-pills label:active span { transform: scale(.98); }
.ml-choix span, .ml-pills label span { transition: transform .12s ease, all var(--duration-fast); }

/* Pas d'apparition au chargement : la page doit etre la, tout de suite.
   (Une cascade en `backwards` renvoyait les blocs a l'invisible en fin
   d'animation — page blanche. Le mouvement gratuit ne vaut jamais ce risque.) */

/* ======================= BARRE : RÉACTION AU TOUCHER ======================= */
/* Une pression franche et courte, pas un rebond de dessin anime. */
.ml-tabbar svg { transition: transform .16s cubic-bezier(.34, .8, .3, 1); }
.ml-tabbar a:active svg { transform: scale(.9); }
.ml-tabbar a.presse svg { animation: ml-tab-pression .22s cubic-bezier(.34, .8, .3, 1); }
@keyframes ml-tab-pression {
  0%   { transform: scale(.9); }
  100% { transform: scale(1); }
}

/* ======================= LES MODES DE VEILLÉE =======================
   Une seule vue à la fois : le décor change, le lecteur reste en bas. */
.ml-modes { display: flex; gap: 8px; padding: 14px 24px 0; overflow-x: auto;
  scrollbar-width: none; -webkit-overflow-scrolling: touch; }
.ml-modes::-webkit-scrollbar { display: none; }
.ml-modes button, .ml-modes a { flex: 0 0 auto; padding: 8px 15px;
  border-radius: var(--radius-pill); background: var(--surface-input);
  border: 1px solid var(--border-default); color: var(--text-secondary);
  font: var(--weight-semibold) 13px var(--font-sans); cursor: pointer;
  text-decoration: none; -webkit-tap-highlight-color: transparent;
  transition: all var(--duration-fast) var(--ease-standard); }
.ml-modes button.actif, .ml-modes a.actif { background: rgba(255,182,92,.13);
  border-color: var(--amber); color: var(--halo); font-weight: var(--weight-bold); }
.ml-modes button:active { transform: scale(.96); }

/* Structure : un décor qui change, un lecteur qui reste. */
.ml-veillee { display: flex; flex-direction: column;
  min-height: calc(100vh - 130px); padding-bottom: 30px; }
.ml-decor { flex: 1; position: relative; display: flex; align-items: center;
  justify-content: center; min-height: 300px; overflow: hidden; }
.ml-vue { display: none; width: 100%; }
.ml-vue.visible { display: block; }

/* Le lecteur, toujours au même endroit. */
.ml-commandes { display: flex; flex-direction: column; align-items: center;
  padding: 0 26px; }
.ml-play.grand { width: 88px; height: 88px; flex: 0 0 88px;
  box-shadow: 0 0 0 9px rgba(255,182,92,.07), 0 10px 38px rgba(255,182,92,.38); }
.ml-play.grand.joue { animation: ml-pulse 3.4s ease-in-out infinite; }

/* A · Le voile : rien, juste la lune. */
.ml-vue-voile { text-align: center; }
.ml-vue-voile .lune-posee { opacity: .55; }

/* B · Le ciel ouvert : la constellation se trace avec l'histoire. */
.ml-vue-ciel svg { width: 100%; max-width: 340px; height: auto; display: block;
  margin: 0 auto; }
.ml-vue-ciel circle { fill: var(--halo); filter: drop-shadow(0 0 6px rgba(255,217,160,.85)); }
.ml-vue-ciel polyline { transition: stroke-dashoffset .35s linear; }
.ml-vue-ciel .legende { text-align: center; margin-top: 12px; }

/* C · La braise : une lueur qui respire, le téléphone devient veilleuse. */
.ml-vue-braise { text-align: center; }
.ml-vue-braise .lueur { width: 150px; height: 5px; border-radius: 99px; margin: 26px auto 0;
  background: linear-gradient(90deg, transparent, var(--amber), transparent);
  box-shadow: 0 0 44px 15px rgba(255,182,92,.32);
  animation: ml-braise 5.5s ease-in-out infinite; }
@keyframes ml-braise {
  0%, 100% { opacity: .35; transform: scaleX(.8); }
  50%      { opacity: .95; transform: scaleX(1.08); }
}

/* D · Les paroles : le texte suit la voix. */
.ml-vue-paroles { max-height: 46vh; overflow-y: auto; padding: 10px 4px;
  scrollbar-width: none; -webkit-mask-image: linear-gradient(180deg,
    transparent, #000 14%, #000 86%, transparent); }
.ml-vue-paroles::-webkit-scrollbar { display: none; }
.ml-vue-paroles p { font: 400 19px/1.7 var(--font-serif); color: rgba(167,166,217,.3);
  margin: 0 0 20px; text-wrap: pretty;
  transition: color .45s ease, transform .45s ease; }
.ml-vue-paroles p.passee { color: rgba(167,166,217,.2); }
.ml-vue-paroles p.dite { color: var(--cream); text-shadow: 0 0 30px rgba(255,217,160,.28);
  transform: translateX(3px); }

/* Le voile de nuit, commun au mode « voile » et au mode « braise ». */
.ml-nuit { position: fixed; inset: 0; z-index: 60; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 12px;
  background: var(--ink-abyss); opacity: 0; cursor: pointer;
  transition: opacity 1.6s ease; }
.ml-nuit.visible { opacity: .97; }
.ml-nuit.braise { justify-content: flex-end; padding-bottom: 16vh; }
.ml-nuit .bonne-nuit { font: 400 19px var(--font-serif); color: rgba(255,217,160,.5); margin: 0; }
.ml-nuit .rallumer { font: 400 12px var(--font-sans); color: rgba(109,119,160,.7);
  margin: 0; letter-spacing: .06em; }

/* ======================= 1Q · LE CALENDRIER DES SOIRS ======================= */
.ml-calendrier .entetes, .ml-calendrier .grille {
  display: grid; grid-template-columns: repeat(7, 1fr); gap: 6px; }
.ml-calendrier .entetes span { text-align: center;
  font: var(--weight-bold) 10.5px var(--font-sans); letter-spacing: .08em;
  color: var(--muted); text-transform: uppercase; padding-bottom: 8px; }
.ml-calendrier .case {
  aspect-ratio: 1; border-radius: 11px; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 3px; text-decoration: none;
  background: rgba(14,20,37,.4); border: 1px solid rgba(46,58,102,.4);
  transition: all var(--duration-fast) var(--ease-standard); }
.ml-calendrier .case b { font: var(--weight-semibold) 11px var(--font-sans);
  color: var(--muted); }
.ml-calendrier .case i { width: 5px; height: 5px; border-radius: 50%; background: transparent; }
.ml-calendrier .case.hors { opacity: .3; }
.ml-calendrier .case.pleine { background: rgba(255,182,92,.12);
  border-color: rgba(255,182,92,.4); }
.ml-calendrier .case.pleine i { background: var(--amber);
  box-shadow: 0 0 9px rgba(255,182,92,.85); }
.ml-calendrier .case.pleine b { color: var(--halo); }
.ml-calendrier .case.pleine:active { transform: scale(.93); }
.ml-calendrier .case.attendu i { background: none;
  border: 1px dashed rgba(167,166,217,.6); width: 7px; height: 7px; }
.ml-calendrier .case.aujourdhui { border-color: var(--lavender); }
.ml-legende { display: flex; gap: 18px; justify-content: center; margin-top: 16px;
  font: var(--weight-body) 11.5px var(--font-sans); color: var(--muted); }
.ml-legende span { display: inline-flex; align-items: center; gap: 6px; }
.ml-legende i { width: 6px; height: 6px; border-radius: 50%; }
.ml-legende i.pleine { background: var(--amber); box-shadow: 0 0 8px rgba(255,182,92,.8); }
.ml-legende i.attendu { border: 1px dashed rgba(167,166,217,.6); }

/* La bascule de vue reprend les pastilles des modes, en lien. */
.ml-modes a { flex: 0 0 auto; padding: 8px 15px; border-radius: var(--radius-pill);
  background: var(--surface-input); border: 1px solid var(--border-default);
  color: var(--text-secondary); font: var(--weight-semibold) 13px var(--font-sans);
  text-decoration: none; transition: all var(--duration-fast) var(--ease-standard); }
.ml-modes a.actif { background: rgba(255,182,92,.13); border-color: var(--amber);
  color: var(--halo); font-weight: var(--weight-bold); }

/* ===== Styles calculés du prototype, figés en classes ===== */
/* chip(on) */
.dc-chip { padding:9px 15px; border-radius:99px; cursor:pointer;
  font:700 13.5px var(--font-sans); transition:all .2s ease; user-select:none;
  -webkit-tap-highlight-color:transparent;
  background:var(--surface-input); border:1px solid var(--border-night);
  color:var(--lavender); }
.dc-chip.on, .dc-chip input:checked ~ span { background:rgba(255,182,92,.15);
  border-color:var(--amber); color:var(--halo); }
label.dc-chip { display:inline-block; }
label.dc-chip input { position:absolute; opacity:0; pointer-events:none; }
label.dc-chip:has(input:checked) { background:rgba(255,182,92,.15);
  border-color:var(--amber); color:var(--halo); }

/* planCard(on) */
.dc-plan { text-align:left; padding:14px 15px; border-radius:18px; cursor:pointer;
  transition:all .2s ease; background:var(--surface-input);
  border:1px solid var(--border-night); box-shadow:none; display:block; }
.dc-plan input { position:absolute; opacity:0; pointer-events:none; }
.dc-plan:has(input:checked) { background:rgba(255,182,92,.1);
  border-color:var(--amber); box-shadow:var(--shadow-halo-card); }

/* mailTrack / mailKnob */
.dc-bascule { position:relative; width:45px; height:26px; border-radius:99px;
  cursor:pointer; background:var(--surface-input); border:1px solid var(--border-night);
  transition:all .25s ease; padding:0; display:block; }
.dc-bascule span { position:absolute; top:3px; left:3px; width:20px; height:20px;
  border-radius:50%; background:var(--muted); transition:all .25s ease; }
.dc-bascule.on { background:rgba(255,182,92,.25); border-color:var(--amber); }
.dc-bascule.on span { left:22px; background:var(--halo); }

/* stepStars */
.dc-etape { opacity:.25; transform:scale(1); transition:all .3s ease; }
.dc-etape.faite { opacity:1; }
.dc-etape.courante { opacity:1; transform:scale(1.25); }

/* qCard : la première carte de la file porte le halo */
.dc-file { background:var(--surface-card-glass); backdrop-filter:blur(6px);
  border-radius:20px; padding:17px 18px; border:1px solid var(--border-night);
  box-shadow:none; }
.dc-file.premiere { border-color:var(--border-halo-strong);
  box-shadow:var(--shadow-halo-card); }

/* Le point d'une étoile du ciel */
.dc-etoile span { width:7px; height:7px; border-radius:50%; background:var(--halo);
  box-shadow:0 0 10px rgba(255,217,160,.7); transition:all .25s ease; display:block; }
.dc-etoile.active span { width:11px; height:11px;
  box-shadow:0 0 0 5px rgba(255,182,92,.18), 0 0 18px rgba(255,182,92,.9); }

/* Le choix du genre : la maquette dessinait deux boutons, dont un déjà
   allumé (« Un garçon ») et aucun champ derrière. Devenus des <label> qui
   portent un vrai radio, ils gardent le style exact du design ; l'état
   allumé, que React posait en dur, passe ici par :has(input:checked). */
.dc-genre { flex:1; display:block; text-align:center; box-sizing:border-box;
  font:600 13.5px var(--font-sans); color:var(--lavender);
  background:rgba(14,20,37,.4); border:1.5px solid var(--border-night);
  padding:12px; border-radius:12px; cursor:pointer; transition:all .2s ease; }
.dc-genre input { position:absolute; width:1px; height:1px; opacity:0;
  pointer-events:none; }
.dc-genre:has(input:checked) { color:var(--halo); background:rgba(255,182,92,.1);
  border-color:var(--amber); }

/* Un champ qui manque ne se signale pas en rouge criard : un liseré ambre. */
.ml-field[aria-invalid="true"] { border-color:var(--amber);
  box-shadow:0 0 0 3px rgba(255,182,92,.15); }

/* RadioPill : l'age choisi. Sans ces regles, le clic COCHE bien le champ mais
   rien ne bouge a l'ecran — le parent croit que ca ne marche pas. L'etat de
   repos est ici (et non en style inline) parce qu'un style inline bat
   toujours la feuille : sinon `:has()` ne peut rien repeindre.
   Valeurs reprises telles quelles du bundle du design system. */
.dc-pill { background:var(--surface-input); border:1px solid var(--border-default);
  color:var(--text-primary); font-weight:400; }
.dc-pill:has(input:checked) { background:rgba(255,182,92,.15);
  border-color:var(--amber); box-shadow:0 0 0 3px rgba(255,182,92,.15);
  color:var(--halo); font-weight:700; }

/* Les 4 etoiles du parcours, en haut a droite de chaque ecran traverse. */
.ml-progres { position:absolute; top:15px; right:18px; z-index:5;
  display:flex; align-items:center; gap:7px; }
.dc-ecran { position:relative; }

/* Les liens du design portent des styles de bouton : le souligne par defaut
   du navigateur n'a rien a faire dessus. */
.ml-page a { text-decoration:none; }
/* @@fin-styles-calcules (tout ce qui précède est réécrit par cabler.py) */
/* ===== Lecteur audio branché derrière les visuels du design ===== */
[data-lecteur] audio { display: none; }
[data-lecteur].sans-audio .dc-play { opacity: .4; cursor: default; box-shadow: none; }
.dc-audiocard.joue .dc-egaliseur i { opacity: .9;
  animation: ml-egaliseur 1.1s ease-in-out infinite; }
.dc-audiocard.joue .dc-egaliseur i:nth-child(3n+2) { animation-delay: .15s; }
.dc-audiocard.joue .dc-egaliseur i:nth-child(3n+3) { animation-delay: .3s; }

/* ⚠ LE COURT ÉCRAN BLEU ENTRE DEUX PAGES (Adem, 11/08 : « quand j'appuie sur
   une touche ou une icône, y'a un court écran bleu qui se met »).

   Ce n'était PAS l'écran de lancement qui se rejouait : vérifié, il ne
   s'affiche qu'une fois par session, et les pages répondent en 40 à 60 ms.
   C'était la coupure native entre deux documents : le navigateur efface
   l'ancienne page, peint le fond de la nouvelle, puis son contenu.

   Les transitions de vue suppriment cette coupure : le navigateur garde
   l'image de la page qu'on quitte jusqu'à ce que la suivante soit peinte, et
   fond l'une dans l'autre. On appuie, ça charge, sans rien entre. La durée
   est volontairement courte (140 ms) : au-delà, on remplacerait un clignotement
   par une lenteur. Un navigateur qui ne connaît pas ces règles les ignore et
   retrouve exactement le comportement d'avant. */
@view-transition { navigation: auto; }

::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: .14s;
  animation-timing-function: ease;
}

/* Personne ne doit subir une transition qu'il a demandé de ne pas voir. */
@media (prefers-reduced-motion: reduce) {
  ::view-transition-old(root), ::view-transition-new(root) { animation: none; }
}

/* ⚠ LE FOND BLANC SUR LES CÔTÉS (Adem, 11/08, sur ordinateur).
   Le dégradé du body est posé en `background-attachment: fixed` : il est
   peint sur le cadre de la fenêtre, et RIEN n'est peint au-delà. Dès qu'on en
   sort — rebond de scroll sur Mac, instant du chargement, zoom, impression —
   le navigateur montre sa couleur par défaut, c'est-à-dire du blanc, de part
   et d'autre des 430 px de l'app. Une couleur unie sous le dégradé ferme
   définitivement le trou, sans rien changer à l'apparence.
   Sur `html` aussi : c'est lui qui donne sa couleur au canevas. */
html { background-color: var(--ink-night); }
body { background-color: var(--ink-night); }

/* « + Ajouter un frère ou une sœur » — la fiche, volet 1. Discret : la
   plupart des familles n'ont qu'un enfant, il ne doit pas concurrencer le
   bouton « Continuer ». Il disparaît une fois la fratrie au complet. */
.ml-ajoute-enfant {
  width: 100%; margin-top: 10px; padding: 13px 16px; cursor: pointer;
  background: transparent; border: 1.5px dashed rgba(255,217,160,.32);
  border-radius: 14px; color: var(--halo);
  font: 700 13.5px var(--font-sans);
  transition: border-color .2s, background .2s;
}
.ml-ajoute-enfant:hover { background: rgba(255,255,255,.04); border-color: rgba(255,217,160,.5); }
.ml-ajoute-enfant:active { transform: scale(.99); }

/* Retour au sommaire — aperçu de la refonte uniquement, jamais en prod.
   ⚠ Deux fois mal placé le 11/08 : en HAUT à droite il couvrait les 4 étoiles
   du parcours (invisibles là où l'on vient justement les éprouver), et en BAS
   à gauche il recouvrait la barre de navigation, au point d'intercepter le
   clic sur « Ce soir ». Il vit désormais sur le flanc gauche, à mi-hauteur :
   ni les étoiles, ni le chevron de retour, ni la barre du bas. */
.ml-retour-apercu {
  position: fixed; left: 8px; top: 50%; transform: translateY(-50%); z-index: 90;
  opacity: .55;
  background: rgba(11,16,32,.82); border: 1px solid rgba(255,182,92,.35);
  color: var(--halo); text-decoration: none; backdrop-filter: blur(6px);
  font: 700 11.5px var(--font-sans); padding: 7px 11px; border-radius: 99px;
}
.ml-retour-apercu:hover { opacity: 1; }
.ml-retour-apercu:active { transform: translateY(-50%) scale(.96); }
body.sans-surcouche .ml-retour-apercu { display: none; }

/* La bascule du courrier du soir sortait de son cadre une fois allumée
   (signalé par Adem le 28/07). Elle n'était pas coupable : posée dans un flex
   à côté d'un texte long, elle se faisait comprimer de 45 px à 35, pendant que
   le rond gardait sa course de 22 px et débordait de 8. Mesuré, pas supposé. */
.dc-bascule { flex: 0 0 auto; box-sizing: content-box; }

/* Cf. .ml-pills plus haut : la pastille cochée garde sa bordure, pas son halo. */
.dc-pill:has(input:checked) { box-shadow: none; }

/* Studio — la barre de décision, toujours sous le pouce. Le texte d'une
   histoire fait ~2 000 signes : sans elle, il faut lire jusqu'en bas puis
   remonter pour valider, à chaque histoire et tous les soirs. */
/* `sticky` ne suffisait pas : posée en fin de page, la barre n'apparaissait
   qu'une fois le texte scrollé jusqu'au bout — soit exactement le trajet
   qu'on voulait éviter. Elle est donc FIXE, comme la barre d'onglets. */
.ml-decision {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 40;
  max-width: 430px; margin: 0 auto;
  padding: 16px 20px calc(16px + env(safe-area-inset-bottom, 0px));
  background: linear-gradient(180deg, rgba(11, 16, 32, .82) 0%, rgba(11, 16, 32, .97) 34%);
  backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);
  border-top: 1px solid var(--border-night);
}
.ml-decision > div:first-child { margin-top: 0 !important; }
/* La note dépliée peut être haute : elle défile dans la barre plutôt que de
   pousser les boutons hors de l'écran. */
.ml-decision [data-motif] { max-height: 46vh; overflow-y: auto; }
/* Le contenu garde de la place sous lui, sinon la barre couvre la fin du
   texte de l'histoire — celle qu'Adem doit justement relire. */
body:has(.ml-decision) .dc-ecran { padding-bottom: 190px; }

/* Studio — les mots jamais confirmés à l'oreille. En AMBRE, pas en vert : le
   vert coché du design system dit « règle respectée », soit l'inverse du
   message (« ça correspond à quoi les mots en vert ? », Adem, 28/07). */
.dc-chip-oreille { display: inline-flex; align-items: center; gap: 5px;
  padding: 6px 12px; border-radius: var(--radius-pill); cursor: pointer;
  font: 700 11.5px var(--font-sans); white-space: nowrap;
  background: rgba(255, 182, 92, .12); color: var(--halo);
  border: 1px solid rgba(255, 182, 92, .3);
  -webkit-tap-highlight-color: transparent; }
.dc-chip-oreille:active { transform: scale(.95); }

/* Le volet de correction : c'est ici que le moat grossit. */
.ml-correction { margin-top: 14px; padding: 15px 16px; border-radius: 16px;
  background: rgba(255, 182, 92, .07); border: 1px solid rgba(255, 182, 92, .3); }
.ml-correction label { display: block; font: 700 12px var(--font-sans);
  color: var(--lavender); margin-bottom: 9px; }
.ml-correction label b { color: var(--halo); }
.ml-correction input[type="text"] { width: 100%; box-sizing: border-box;
  padding: 13px 15px; border-radius: 13px; background: var(--surface-input);
  border: 1.5px solid var(--border-default); color: var(--text-primary);
  font: 600 16px var(--font-sans); }
.ml-correction input[type="text"]:focus { outline: none;
  border-color: var(--amber); box-shadow: 0 0 0 3px rgba(255, 182, 92, .15); }
.ml-correction .ml-micro { margin: 9px 0 0; }
.ml-correction-actions { display: flex; gap: 9px; margin-top: 13px; }
.ml-correction-actions button { flex: 1; padding: 12px 10px; cursor: pointer;
  border-radius: var(--radius-pill); font: 700 13px var(--font-sans);
  background: linear-gradient(180deg, var(--halo), var(--amber));
  color: var(--text-on-amber); border: none; }
.ml-correction-actions button[data-correction-annule] { flex: 0 0 auto;
  background: transparent; color: var(--muted);
  border: 1px solid var(--border-night); }

/* ===== Studio — la barre du bas, comme chez les familles =====
   Quatre rubriques : une histoire validée ou renvoyée sortait de l'écran sans
   laisser de trace (« je l'ai validée mais elle a disparu, je peux pas
   suivre »). Adem a voulu la même navigation que côté famille, en bas et en
   icônes, plutôt que des onglets en haut. */
.ml-tabbar.studio { grid-template-columns: repeat(4, 1fr); }
.ml-tabbar.studio a { position: relative; }
.ml-tabbar.studio .pastille {
  position: absolute; top: -1px; left: 50%; margin-left: 7px;
  min-width: 17px; height: 17px; padding: 0 4px; border-radius: 9px;
  display: flex; align-items: center; justify-content: center;
  font: 800 10.5px/1 var(--font-sans); font-style: normal;
  background: linear-gradient(180deg, var(--halo), var(--amber));
  color: var(--text-on-amber); box-shadow: var(--shadow-halo);
}
/* Les écrans du Studio laissent la place à leur barre. */
body:has(.ml-tabbar.studio) .dc-ecran { padding-bottom: 104px; }

/* L'alerte de livraison : la seule chose qui ne peut pas attendre qu'on aille
   la chercher. Sous six heures du créneau, elle passe en rouge et respire. */
.ml-alerte-livraison { margin: 14px 26px 0; padding: 13px 16px; border-radius: 14px;
  background: rgba(255, 182, 92, .12); border: 1px solid rgba(255, 182, 92, .32); }
.ml-alerte-livraison b { display: block;
  font: 800 13.5px var(--font-sans); color: var(--halo); }
.ml-alerte-livraison span { display: block; margin-top: 3px;
  font: 400 12px var(--font-sans); color: var(--lavender); }
.ml-alerte-livraison.urgent { background: rgba(232, 160, 160, .13);
  border-color: rgba(232, 160, 160, .4); animation: ml-respire 3.4s ease-in-out infinite; }
.ml-alerte-livraison.urgent b { color: var(--danger); }

/* « 1re histoire » : l'essai gratuit, promis sous 24 h. */
.dc-badge-premiere { display: inline-block; margin-left: 8px; vertical-align: 2px;
  padding: 3px 9px; border-radius: var(--radius-pill); white-space: nowrap;
  font: 800 10px var(--font-sans); letter-spacing: .04em; text-transform: uppercase;
  background: rgba(143, 212, 168, .14); color: var(--success);
  border: 1px solid rgba(143, 212, 168, .32); }

/* La fiche d'une famille : ce qu'elle a reçu, et depuis quand. */
.ml-famille-chiffres { display: grid; grid-template-columns: repeat(3, 1fr);
  gap: 8px; margin-top: 13px; }
.ml-famille-chiffres div { text-align: center; padding: 10px 6px;
  border-radius: 12px; background: rgba(14, 20, 37, .5);
  border: 1px solid var(--border-night); }
.ml-famille-chiffres b { display: block; font: 600 14px var(--font-serif);
  color: var(--cream); line-height: 1.25; }
.ml-famille-chiffres span { display: block; margin-top: 3px;
  font: 700 9.5px var(--font-sans); letter-spacing: .06em;
  text-transform: uppercase; color: var(--muted); }
.ml-famille.resiliee, .ml-famille.en_pause { opacity: .58; }
.dc-etat.active { color: var(--success); }
.dc-etat.essai { color: var(--halo); }
.dc-etat.en_pause, .dc-etat.resiliee { color: var(--muted); font-weight: 600; }

.ml-endormi { text-align: center; padding: 0 26px;
  min-height: 62vh; display: flex; flex-direction: column;
  align-items: center; justify-content: center; }
.ml-endormi .ml-lune { margin: 0 auto; opacity: .9; }
.ml-endormi p { margin: 30px auto 0; max-width: 280px;
  font: italic 400 17px/1.6 var(--font-serif); color: var(--muted); }

/* Le lien vers l'espace d'une famille : explicite, jamais au détour d'un clic. */
.ml-lien-espace { display: block; margin-top: 6px; padding: 13px; text-align: center;
  border-radius: var(--radius-pill); text-decoration: none;
  font: 700 13px var(--font-sans); color: var(--lavender);
  background: rgba(14, 20, 37, .5); border: 1px solid var(--border-night); }

.ml-vide-studio { margin: 26px 26px 0; padding: 22px 18px; text-align: center;
  border-radius: 16px; border: 1px dashed var(--border-night);
  font: italic 400 14px var(--font-serif); color: var(--muted); }

/* L'âge d'une ligne : une histoire reçue lundi et pas écoutée mercredi rate
   sa livraison. C'est l'information qui manque dans une file en FIFO. */
.dc-attente { font: 600 11px var(--font-sans); color: var(--muted); }
.dc-attente.tarde { color: var(--danger); }

/* Studio — l'état de chaque ligne de la file. Ambre pour ce qui attend une
   oreille, discret pour ce qui attend Malune, rouge pour ce qui est bloqué :
   la file se lit d'un coup d'œil sans rien ouvrir. */
.dc-etat { font: 700 12px var(--font-sans); white-space: nowrap;
  color: var(--halo); flex: 0 0 auto; }
.dc-etat.atelier, .dc-etat.ecriture, .dc-etat.file { color: var(--muted);
  font-weight: 600; }
.dc-etat.echec { color: var(--danger); }
.dc-etat.validee, .dc-etat.livree { color: var(--success); font-weight: 600; }
/* Une histoire réécrite revient à valider : elle doit se distinguer d'une
   nouvelle, sinon Adem réécoute sans savoir qu'il l'avait déjà refusée. */
.dc-etat.revient { color: var(--halo);
  background: rgba(255, 182, 92, .12); border: 1px solid rgba(255, 182, 92, .3);
  padding: 4px 10px; border-radius: var(--radius-pill); font-size: 11px; }
/* Une ligne qui n'attend pas d'oreille s'efface un peu : le regard va sur ce
   qui reste à écouter. */
.dc-file:has(.dc-etat.atelier), .dc-file:has(.dc-etat.ecriture),
.dc-file:has(.dc-etat.file) { opacity: .62; }

/* Studio — l'état d'une histoire renvoyée. Sans lui, l'écran réaffichait
   l'ancien texte sans rien dire : impossible de savoir si le renvoi avait pris. */
.ml-bandeau-atelier { margin: 14px 20px 0; padding: 12px 15px; border-radius: 14px;
  background: rgba(255, 182, 92, .11); border: 1px solid rgba(255, 182, 92, .3);
  color: var(--halo); font: 700 12.5px/1.5 var(--font-sans); }
.ml-bandeau-atelier span { display: block; margin-top: 4px;
  font-weight: 400; color: var(--lavender); }
.ml-bandeau-atelier.echec { background: rgba(232, 160, 160, .12);
  border-color: rgba(232, 160, 160, .35); color: var(--danger); }
.ml-bandeau-atelier.echec span { font-size: 11.5px; word-break: break-word; }
.ml-bandeau-atelier button { margin-top: 10px; padding: 9px 16px; cursor: pointer;
  border-radius: var(--radius-pill); font: 700 12.5px var(--font-sans);
  background: rgba(255, 255, 255, .05); color: var(--halo);
  border: 1.5px solid rgba(255, 217, 160, .32); }
/* Le message technique existe encore, mais replié : utile pour comprendre,
   inutile à lire quand on veut juste relancer. */
.ml-detail-echec { margin-top: 10px; }
.ml-detail-echec > summary { cursor: pointer; list-style: none;
  font: 600 11px var(--font-sans); color: var(--muted);
  text-decoration: underline; -webkit-tap-highlight-color: transparent; }
.ml-detail-echec > summary::-webkit-details-marker { display: none; }
.ml-detail-echec p { margin: 7px 0 0; font: 400 11px/1.5 ui-monospace, monospace;
  color: var(--muted); word-break: break-word; }

/* Studio — la fiche telle que la mère l'a remplie, et le coût de l'histoire.
   Deux choses qui manquaient au moment précis de la décision. */
.ml-fiche-enfant { margin-top: 12px; }
.ml-fiche-corps { display: flex; flex-direction: column; gap: 10px; margin-top: 10px; }
.ml-fiche-enfant-bloc { padding: 12px 14px; border-radius: 13px;
  background: rgba(14, 20, 37, .5); border: 1px solid var(--border-night); }
.ml-fiche-enfant-bloc p { margin: 0; font: 400 12.5px/1.6 var(--font-sans);
  color: var(--lavender); }
.ml-fiche-enfant-bloc p b { color: var(--muted); font-weight: 700;
  text-transform: uppercase; font-size: 10px; letter-spacing: .06em;
  margin-right: 5px; }
.ml-fiche-enfant-bloc .nom { font: 600 14.5px var(--font-serif);
  color: var(--cream); margin-bottom: 5px; }
.ml-fiche-enfant-bloc .peur { color: var(--danger); }
.ml-fiche-enfant-bloc .peur b { color: var(--danger); opacity: .75; }

.ml-cout { display: grid; grid-template-columns: 1fr 1fr; gap: 9px; margin-top: 12px; }
/* Le total prend toute la largeur : c'est le chiffre qui compte au moment de
   valider, les deux autres l'expliquent. */
.ml-cout .total { grid-column: 1 / -1;
  background: rgba(255, 182, 92, .08); border-color: rgba(255, 182, 92, .28); }
.ml-cout .total b { color: var(--halo); font-size: 17px; }
.ml-cout div { text-align: center; padding: 11px 8px; border-radius: 13px;
  background: rgba(14, 20, 37, .45); border: 1px solid var(--border-night); }
.ml-cout b { display: block; font: 600 15px var(--font-serif); color: var(--cream); }
.ml-cout span { display: block; margin-top: 3px; font: 700 9.5px var(--font-sans);
  letter-spacing: .05em; text-transform: uppercase; color: var(--muted); }

/* Studio — le contrôle automatique, replié par défaut. Déplié, il occupait
   tout l'écran et repoussait le lecteur : on lit le compte, on ouvre si on
   veut. Un point bloquant ouvre le volet de lui-même. */
.ml-controle { margin-top: 14px; }
.ml-controle > summary { cursor: pointer; list-style: none;
  padding: 9px 13px; border-radius: 12px;
  background: rgba(167, 166, 217, .08); border: 1px solid var(--border-night);
  color: var(--lavender); font: 700 12.5px var(--font-sans);
  -webkit-tap-highlight-color: transparent; }
.ml-controle > summary::-webkit-details-marker { display: none; }
.ml-controle > summary::after { content: " ›"; float: right;
  transition: transform .2s ease; display: inline-block; }
.ml-controle[open] > summary::after { transform: rotate(90deg); }
.ml-controle[open] > summary { color: var(--halo);
  border-color: rgba(255, 182, 92, .3); }

/* Studio — ce que le contrôle automatique a trouvé. Il tournait à chaque
   ouverture et n'était affiché nulle part : Adem validait à l'aveugle. */
.ml-alerte { padding: 9px 13px; border-radius: 12px;
  font: 600 12.5px/1.45 var(--font-sans); }
.ml-alerte.bloquant { background: rgba(232, 160, 160, .12); color: var(--danger);
  border: 1px solid rgba(232, 160, 160, .35); }
.ml-alerte.attention { background: rgba(255, 182, 92, .1); color: var(--halo);
  border: 1px solid rgba(255, 182, 92, .28); }
.ml-alerte.info { background: rgba(167, 166, 217, .08); color: var(--lavender);
  border: 1px solid var(--border-night); }

/* ===== La page d'écoute d'une histoire =====
   Reprise du dessin de la page de livraison (design-kit-export/
   prod-page-histoire.html) : c'est déjà la page que les familles connaissent,
   et elle est plus juste que l'écran du prototype. Le bloc commercial de la
   version publique est retiré, la famille est déjà cliente. */
.ml-ecoute { position: relative; min-height: 100vh; min-height: 100dvh;
  display: flex; flex-direction: column;
  background: radial-gradient(125% 90% at 50% -8%, #1A2246 0%, #141B34 52%, #0E1425 100%); }
.ml-ecoute-haut { display: flex; align-items: center; justify-content: space-between;
  padding: 14px 22px 0; flex: 0 0 auto; }
.ml-ecoute-retour { color: var(--lavender); font: 400 22px/1 var(--font-sans);
  text-decoration: none; width: 34px; }
.ml-ecoute-marque { font: 600 12px var(--font-serif); letter-spacing: .42em;
  margin-right: -.42em; color: var(--halo); }
.ml-ecoute-haut > span:last-child { width: 34px; }
.ml-ecoute-corps { flex: 1; display: flex; flex-direction: column; align-items: center;
  justify-content: center; text-align: center; padding: 18px 26px 46px; }
.ml-ecoute-corps .ml-lune { margin: 6px auto 0; }
.ml-ecoute-kicker { font: italic 400 15px var(--font-serif); color: var(--lavender);
  margin: 20px 0 0; }
.ml-ecoute-titre { font: 600 30px/1.16 var(--font-serif); color: var(--cream);
  margin: 8px 0 0; text-wrap: balance; }
.ml-ecoute-cmd { display: flex; align-items: center; justify-content: center;
  gap: 20px; margin-top: 30px; }
.ml-ecoute-saut { width: 54px; height: 54px; border-radius: 50%; cursor: pointer;
  background: rgba(29,38,71,.55); border: 1px solid var(--border-night);
  color: var(--lavender); display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 1px; }
.ml-ecoute-saut .fleche { font-size: 17px; line-height: 1; }
.ml-ecoute-saut .sec { font: 700 9.5px var(--font-sans); }
.ml-ecoute-play { width: 92px; height: 92px; border-radius: 50%; border: none;
  cursor: pointer; flex: 0 0 auto; display: flex; align-items: center;
  justify-content: center;
  background: radial-gradient(circle at 32% 28%, var(--halo), var(--amber) 62%, #F09A3E);
  box-shadow: 0 0 0 12px rgba(255,182,92,.07), 0 0 54px rgba(255,182,92,.32); }
.ml-ecoute-play .ico-play { width: 0; height: 0; margin-left: 6px;
  border-left: 26px solid var(--text-on-amber);
  border-top: 17px solid transparent; border-bottom: 17px solid transparent; }
.ml-ecoute-play .ico-pause { display: flex; gap: 8px; }
.ml-ecoute-play .ico-pause i { width: 8px; height: 30px; border-radius: 4px;
  background: var(--text-on-amber); }
.ml-ecoute-meta { font: 400 13.5px var(--font-sans); color: var(--lavender);
  margin: 22px 0 0; }
.ml-ecoute-meta b { color: var(--halo); }
.ml-ecoute-barre { width: 100%; max-width: 340px; margin: 18px auto 0; }
.ml-ecoute-piste { height: 4px; border-radius: 4px; background: rgba(46,58,102,.75);
  overflow: hidden; cursor: pointer; display: block; }
.ml-ecoute-piste [data-progression] { display: block; height: 100%; width: 0;
  border-radius: 4px; transition: width .3s linear;
  background: linear-gradient(90deg, var(--amber), var(--halo)); }
.ml-ecoute-temps { display: flex; justify-content: space-between; margin-top: 8px;
  font: 700 11.5px var(--font-sans); color: var(--text-muted); }
.ml-ecoute-paroles { margin-top: 26px; padding: 13px 26px; cursor: pointer;
  border-radius: var(--radius-pill); background: rgba(255,255,255,.04);
  border: 1.5px solid rgba(255,217,160,.32); color: var(--halo);
  font: 700 14.5px var(--font-sans); }
.ml-ecoute-dedicace { font: italic 400 14px/1.7 var(--font-serif);
  color: var(--lavender); margin: 30px 0 0; max-width: 330px; }
.ml-ecoute-signe { font: 400 12px var(--font-sans); color: var(--muted);
  margin: 10px 0 0; }
.ml-ecoute-signe .coche { color: var(--halo); }

/* ===== Les paroles, plein écran =====
   Référence donnée par Adem : Spotify. Une ligne allumée à la fois, en gros,
   le reste en retrait, et le texte qui défile tout seul avec la voix. Les
   blocs sont courts (moteur/paroles.py les coupe à ~12 mots) : une phrase de
   trois lignes resterait allumée vingt secondes sans que rien ne bouge. */
.ml-paroles { position: fixed; inset: 0; z-index: 120; display: flex;
  flex-direction: column; background: linear-gradient(180deg, #141B34, #0B1020 70%); }
/* ⚠ L'ENCOCHE. Cet écran est `position: fixed; inset: 0` : il couvre TOUTE la
   fenêtre, barre d'état comprise. Sans `env(safe-area-inset-top)`, le titre et
   la croix passaient sous l'heure et la batterie (constaté sur l'iPhone d'Adem
   le 03/08). Le bas de cet écran gérait déjà sa zone sûre ; le haut avait été
   oublié — et ça ne se voit sur AUCUN navigateur de bureau, où la valeur
   vaut 0. */
.ml-paroles-haut { flex: 0 0 auto; display: flex; align-items: center;
  justify-content: space-between; gap: 14px;
  padding: calc(18px + env(safe-area-inset-top)) 20px 12px;
  width: 100%; max-width: 720px; margin: 0 auto; }
.ml-paroles-titre { font: 600 14px var(--font-serif); color: var(--lavender);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ml-paroles-fermer { width: 36px; height: 36px; flex: 0 0 auto; border-radius: 50%;
  border: 1px solid var(--border-night); background: rgba(29,38,71,.5);
  color: var(--lavender); font-size: 14px; cursor: pointer; }
.ml-paroles-defile { flex: 1; overflow-y: auto; padding: 0 26px;
  width: 100%; max-width: 720px; margin: 0 auto;
  scrollbar-width: none; -webkit-overflow-scrolling: touch;
  scroll-behavior: smooth; }
.ml-paroles-defile::-webkit-scrollbar { display: none; }
.ml-paroles-marge { height: 42vh; }

/* Le grain Spotify : très gros, sans-serif lourd, aligné à gauche, et surtout
   des lignes à venir qui restent LISIBLES. La première version les mettait à
   34 % d'opacité en serif : on ne lisait que la ligne courante, donc on ne
   pouvait pas prendre la voix de vitesse pour conter soi-même. Le contraste
   se joue entre « lisible » et « allumé », pas entre « visible » et « effacé ». */
.ml-parole { font: 800 clamp(27px, 6.6vw, 40px)/1.24 var(--font-sans);
  color: rgba(199,201,232,.58); letter-spacing: -.012em;
  margin: 0 0 22px; cursor: pointer; text-wrap: pretty;
  transition: color .3s ease, opacity .3s ease; }
.ml-parole.dite { color: rgba(167,166,217,.32); }
.ml-parole.courante { color: #FFFFFF; }
.ml-paroles-bas { flex: 0 0 auto; display: flex; align-items: center;
  justify-content: center; gap: 14px; padding: 14px 20px calc(22px + env(safe-area-inset-bottom));
  border-top: 1px solid rgba(46,58,102,.55);
  width: 100%; max-width: 720px; margin: 0 auto; }
.ml-paroles-saut { width: 46px; height: 46px; border-radius: 50%; cursor: pointer;
  border: 1px solid var(--border-night); background: rgba(29,38,71,.5);
  color: var(--lavender); font-size: 17px; }
.ml-paroles-play { min-width: 118px; padding: 13px 22px; cursor: pointer;
  border: none; border-radius: var(--radius-pill); font: 700 14px var(--font-sans);
  background: linear-gradient(180deg, var(--halo), var(--amber));
  color: var(--text-on-amber); }
/* ⚠ « Reprendre le fil » se posait SUR le bouton Pause. Les 96 px comptaient
   la barre du bas (14 + 46 + 22 + une marge), mais oubliaient la zone sûre du
   bas de l'iPhone — encore ~34 px, qui décalaient le badge d'autant. Sur un
   écran de bureau, `env(safe-area-inset-bottom)` vaut 0 : les deux valeurs
   sont identiques, et le défaut y est strictement invisible. */
.ml-paroles-suivre { position: absolute; left: 50%; transform: translateX(-50%);
  bottom: calc(96px + env(safe-area-inset-bottom));
  padding: 9px 18px; border-radius: var(--radius-pill);
  background: rgba(255,182,92,.14); border: 1px solid rgba(255,182,92,.35);
  color: var(--halo); font: 700 12.5px var(--font-sans); cursor: pointer; }

@media (max-height: 700px) {
  .ml-parole { font-size: 24px; margin-bottom: 17px; }
  .ml-ecoute-titre { font-size: 26px; }
  .ml-paroles-marge { height: 34vh; }
}

/* Un appui ne doit rien surligner en bleu : les pastilles d'âge et de genre
   sont des <label>, que le navigateur traite comme du texte sélectionnable
   (signalé par Adem le 28/07 : « les âges sont surlignés quand on clique »). */
.dc-pill, .dc-genre, .dc-bascule, .dc-play, .dc-plan, .ml-parole,
.ml-pills label, .ml-pills span, .ml-choix label, .ml-formules label,
.ml-ecoute-saut, .ml-ecoute-play, .ml-ecoute-paroles,
[data-paroles] button, [data-paroles] span {
  -webkit-tap-highlight-color: transparent;
  -webkit-user-select: none; user-select: none;
}

/* La rangée des mois défile horizontalement : la barre grise du navigateur
   n'a rien à faire sur un ciel de nuit. `scrollbar-width:none` couvre Firefox
   et les navigateurs récents, cette règle-ci couvre Safari et Chrome. */
[data-mois-liste]::-webkit-scrollbar { display: none; }

/* --- Zones de sécurité iOS (encoche, barre de gestes) ----------------------
   Actives seulement depuis que le viewport porte `viewport-fit=cover` : sans
   lui, `env(safe-area-inset-*)` vaut 0 et ces règles ne font rien.

   La barre de statut est déclarée `black-translucent`, donc le contenu passe
   DESSOUS et le dégradé de la nuit remonte jusqu'en haut de l'écran — plus de
   bande blanche. En contrepartie il faut décaler le contenu, sinon le titre de
   chaque écran se glisserait derrière l'heure. */
.dc-ecran, .ml-ecoute { padding-top: env(safe-area-inset-top, 0px); }

/* Le Studio n'utilise pas toujours `.dc-ecran` : ses écrans écrits à la main
   (familles, fiche d'une famille) ont leur propre châssis. */
body.avec-tabbar > .ml-page { padding-top: env(safe-area-inset-top, 0px); }

/* …mais trois écrans portent LES DEUX : « Ce soir », « Le ciel » et
   « Réglages » sont transposés du design (donc `.dc-ecran`) ET posés sous la
   barre du bas (donc `.ml-page` décalée). L'encoche était réservée deux fois,
   soit ~94 px de vide en haut sur un iPhone installé, alors que « Sa fiche »
   et « Offrir », écrits à la main sans `.dc-ecran`, tombaient juste. Le
   décalage appartient au châssis de la page, pas à l'écran qu'elle contient. */
body.avec-tabbar .dc-ecran { padding-top: 0; }

/* …et le décalage n'était que la moitié du défaut. Ces trois écrans portaient
   AUSSI leur propre dégradé de fond, posé par-dessus celui du `body` : deux
   nébuleuses au lieu de trois, une origine différente, et le fond du body qui
   restait visible sur la bande de l'encoche. D'où la couture en haut et
   l'impression, décrite par Adem, « d'une page mise par-dessus », alors que
   « Sa fiche » et « Offrir » n'ont pas de `.dc-ecran` et laissent le fond de la
   page courir d'un bord à l'autre. On laisse donc le fond du body traverser.
   `!important` est nécessaire : ce dégradé est un style INLINE écrit par le
   pipeline de transposition, on ne peut pas le battre autrement. */
body.avec-tabbar .dc-ecran { background: none !important; }

/* --- Le chevron de retour ---------------------------------------------------
   Il mesurait 5 × 20 px : un caractère « ‹ » sans surface autour. Impossible à
   viser au doigt — Apple recommande 44 px, le geste le plus courant de l'app
   en offrait cinq. On agrandit la ZONE TOUCHÉE sans grossir le signe :
   l'apparence ne change pas, seul le confort. */
.dc-ecran a[style*="font:400 20px/1"],
.ml-ecoute a[style*="font:400 20px/1"],
.ml-ecoute-retour {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 44px; min-height: 44px; margin-left: -12px;
  -webkit-tap-highlight-color: transparent;
}

/* La barre du bas est FIXE : sans réserve sous le contenu, les derniers
   éléments passent dessous. Sur « Ce soir », le bouton « Relire sa fiche »
   était entièrement masqué par la barre — visible en filigrane, intouchable.
   Les règles existantes ne couvraient que `.ml-fin` et les feuilles ; les
   écrans transposés du design n'ont ni l'un ni l'autre. */
body.avec-tabbar .dc-ecran {
  padding-bottom: calc(104px + env(safe-area-inset-bottom, 0px));
}

/* Le chevron de retour de l'écran de paiement : même zone tactile de 44 px
   que les autres, posé face à la barre d'étapes. */
.ml-retour-paiement {
  display: inline-flex; align-items: center; justify-content: center;
  margin-left: -12px;
  min-width: 44px; min-height: 44px; border: none;
  color: var(--lavender); font: 400 20px/1 var(--font-sans);
  -webkit-tap-highlight-color: transparent;
}

/* Le titre de l'écran de paiement et son chevron, sur la même ligne.
   Le paragraphe du titre porte une marge basse de 16 px dans son style : dans
   un conteneur centré verticalement, sa boîte fait donc 31 px pour 15 px de
   texte, et le texte remonte de 8 px par rapport au chevron. On déplace la
   marge sur le conteneur pour que les deux s'alignent vraiment. */
.ml-titre-retour { display: flex; align-items: center; gap: 2px; margin-bottom: 16px; }
.ml-titre-retour > p { margin-bottom: 0 !important; }

/* --- L'invitation aux notifications, à la première visite ------------------
   Une carte à nous, jamais le pop-up d'Apple d'emblée : sur iPhone, un refus
   système est définitif. Celle-ci se décline sans conséquence. */
.ml-invite-voile {
  position: fixed; inset: 0; z-index: 90;
  display: flex; align-items: flex-end; justify-content: center;
  background: rgba(6, 10, 22, .58); backdrop-filter: blur(3px);
  opacity: 0; transition: opacity .3s ease;
  padding: 0 14px calc(18px + env(safe-area-inset-bottom, 0px));
}
.ml-invite-voile.visible { opacity: 1; }
.ml-invite {
  width: 100%; max-width: 420px;
  background: var(--surface-card-glass, #141B30);
  border: 1px solid var(--border-night, #232B47);
  border-radius: 24px; padding: 24px 22px 18px; text-align: center;
  transform: translateY(14px); transition: transform .32s cubic-bezier(.2,.8,.3,1);
  box-shadow: 0 18px 50px rgba(0, 0, 0, .45);
}
.ml-invite-voile.visible .ml-invite { transform: translateY(0); }
/* C'était un emoji 🌙 générique. Depuis le 02/08 c'est LE logo (`/static/
   lune.svg`, le même fichier que l'icône) : une carte qui invite à poser
   Malune sur l'écran d'accueil doit montrer ce qu'on va y poser. */
.ml-invite-lune {
  display: block; width: 46px; height: 46px; margin: 0 auto;
  filter: drop-shadow(0 6px 18px rgba(255, 182, 92, .35));
}
.ml-invite h2 {
  font: 600 19px/1.3 var(--font-serif); color: var(--cream);
  margin: 12px 0 0;
}
.ml-invite p {
  font: 400 13.5px/1.6 var(--font-sans); color: var(--lavender);
  margin: 9px 0 20px;
}
.ml-invite-oui {
  display: block; width: 100%; cursor: pointer; border: none;
  border-radius: var(--radius-pill, 99px); padding: 15px 20px;
  font: 700 15px var(--font-sans); color: var(--text-on-amber, #2A1B08);
  background: linear-gradient(180deg, var(--halo, #FFD9A0), var(--amber, #FFB65C));
  box-shadow: var(--shadow-halo);
}
.ml-invite-oui[disabled] { opacity: .6; }
.ml-invite-non {
  display: block; width: 100%; margin-top: 6px; cursor: pointer;
  background: transparent; border: none; padding: 12px;
  font: 600 13px var(--font-sans); color: var(--muted);
}

/* --- La fête du paiement ----------------------------------------------------
   LE moment où une mère devient cliente. Un toast en bas d'écran ne suffisait
   pas, d'autant que la confirmation dépendait d'un retour de Stripe qui n'est
   jamais arrivé le 02/08. On rejoue ici la promesse du produit — « son étoile
   s'allume » — avec les matières de la marque : la nuit, l'ambre, le halo.
   Rien à cliquer : un toucher suffit à passer, sinon l'écran se retire seul. */
.ml-fete {
  position: fixed; inset: 0; z-index: 90;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 4px; padding: 0 32px; text-align: center;
  background: radial-gradient(420px 320px at 50% 42%, rgba(46, 58, 102, .55) 0%, transparent 70%),
              rgba(8, 11, 30, .90);
  backdrop-filter: blur(7px);
  animation: ml-fete-entre .5s ease both;
}
.ml-fete.part { animation: ml-fete-sort .55s ease both; }

/* Le halo respire derrière l'étoile : c'est lui qui donne la chaleur. */
.ml-fete-halo {
  position: absolute; width: 260px; height: 260px; margin-bottom: 78px;
  border-radius: 50%; pointer-events: none;
  background: radial-gradient(circle, rgba(255, 182, 92, .38) 0%, transparent 62%);
  animation: ml-fete-halo 2.6s ease-in-out infinite;
}
/* La marque elle-même, pas un pictogramme de circonstance : c'est la lune
   endormie du design system, celle de l'écran d'accueil et de l'icône. */
.ml-fete-marque {
  margin-bottom: 26px;
  filter: drop-shadow(0 0 30px rgba(255, 182, 92, .55));
  animation: ml-fete-marque 1.1s cubic-bezier(.16, 1, .3, 1) both;
}
.ml-fete-titre {
  margin: 0; font: 600 30px/1.2 var(--font-serif); color: var(--cream, #F6EFE4);
  animation: ml-fete-texte .7s ease .35s both;
}
.ml-fete-texte {
  margin: 10px 0 0; font: 400 15px/1.6 var(--font-sans);
  color: var(--lavender, #A7A6D9); max-width: 300px;
  animation: ml-fete-texte .7s ease .55s both;
}

/* Six étincelles qui montent, jamais deux au même rythme : une cadence
   régulière se voit tout de suite et fait mécanique. */
.ml-fete-etincelles { position: absolute; width: 220px; height: 220px; margin-bottom: 78px; }
.ml-fete-etincelles i {
  position: absolute; width: 4px; height: 4px; border-radius: 50%;
  background: var(--moonlight, #FFF7E8); opacity: 0;
  animation: ml-fete-etincelle 2.8s ease-out infinite;
}
.ml-fete-etincelles i:nth-child(1) { left: 12%; top: 62%; animation-delay: .20s; }
.ml-fete-etincelles i:nth-child(2) { left: 30%; top: 78%; animation-delay: .95s; }
.ml-fete-etincelles i:nth-child(3) { left: 52%; top: 88%; animation-delay: .55s; }
.ml-fete-etincelles i:nth-child(4) { left: 71%; top: 72%; animation-delay: 1.40s; }
.ml-fete-etincelles i:nth-child(5) { left: 86%; top: 58%; animation-delay: .75s; }
.ml-fete-etincelles i:nth-child(6) { left: 62%; top: 46%; animation-delay: 1.80s; }

@keyframes ml-fete-entre { from { opacity: 0 } to { opacity: 1 } }
@keyframes ml-fete-sort  { to { opacity: 0; visibility: hidden } }
/* Le logo se pose, il ne bondit pas : pas de rotation, une marque ne se
   penche pas. Un léger dépassement d'échelle suffit à donner la vie. */
@keyframes ml-fete-marque {
  0%   { opacity: 0; transform: scale(.72) }
  62%  { opacity: 1; transform: scale(1.05) }
  100% { opacity: 1; transform: scale(1) }
}
@keyframes ml-fete-halo {
  0%, 100% { transform: scale(.92); opacity: .75 }
  50%      { transform: scale(1.08); opacity: 1 }
}
@keyframes ml-fete-texte {
  from { opacity: 0; transform: translateY(10px) }
  to   { opacity: 1; transform: translateY(0) }
}
@keyframes ml-fete-etincelle {
  0%   { opacity: 0; transform: translateY(0) scale(.6) }
  25%  { opacity: 1; transform: translateY(-18px) scale(1) }
  100% { opacity: 0; transform: translateY(-64px) scale(.4) }
}

/* Le message reste, le mouvement disparaît : une confirmation ne doit jamais
   dépendre d'une animation pour être lue. */
@media (prefers-reduced-motion: reduce) {
  .ml-fete, .ml-fete-marque, .ml-fete-titre, .ml-fete-texte,
  .ml-fete-halo, .ml-fete-etincelles i { animation: none !important; opacity: 1 }
  .ml-fete-etincelles { display: none }
}

/* --- « Malune écrit… » -------------------------------------------------------
   Le bandeau qui manquait le 02/08. L'écran ne disait rien pendant que le
   moteur travaillait, et cinq clics impatients ont coûté cinq écritures. */
.ml-bandeau-atelier.ecrit {
  border-color: rgba(255, 182, 92, .45);
  background: rgba(255, 182, 92, .10);
  color: var(--halo, #FFD9A0);
}
.ml-points { display: inline-flex; gap: 3px; margin-left: 4px; vertical-align: baseline; }
.ml-points i {
  width: 3px; height: 3px; border-radius: 50%;
  background: currentColor; opacity: .25;
  animation: ml-points 1.4s ease-in-out infinite;
}
.ml-points i:nth-child(2) { animation-delay: .18s; }
.ml-points i:nth-child(3) { animation-delay: .36s; }
@keyframes ml-points {
  0%, 100% { opacity: .25; transform: translateY(0) }
  40%      { opacity: 1;   transform: translateY(-2px) }
}
/* Un bouton qui a déjà lancé le moteur ne doit plus avoir l'air cliquable. */
.ml-bandeau-atelier button[disabled] { opacity: .55; cursor: default; }
@media (prefers-reduced-motion: reduce) {
  .ml-points i { animation: none; opacity: .6 }
}

/* --- Le ciel du Studio -------------------------------------------------------
   Le champ d'étoiles et les deux filantes existent depuis toujours dans
   `base.html`, mais les écrans du Studio posaient leur propre fond opaque
   par-dessus : Adem ne les avait jamais vus. Même correctif que côté famille
   le 02/08 — on laisse le fond de la page traverser. `!important` parce que ce
   dégradé est un style INLINE écrit par le pipeline de transposition. */
body.studio .dc-ecran { background: none !important; }
/* La barre du bas garde son fond : c'est elle qui doit rester lisible. */
body.studio .ml-tabbar { background: rgba(14, 20, 37, .92); }

/* --- Filtrer les familles ----------------------------------------------------
   « Tout est mélangé » (Adem, 02/08). Quatre onglets, chacun avec son compte.
   Ils défilent horizontalement plutôt que de se serrer : sur un téléphone,
   quatre libellés + quatre nombres ne tiennent pas sans devenir illisibles. */
.ml-filtres {
  display: flex; gap: 8px; margin-top: 16px; padding: 0 26px 2px;
  overflow-x: auto; scrollbar-width: none;
}
.ml-filtres::-webkit-scrollbar { display: none; }
.ml-filtre {
  display: inline-flex; align-items: center; gap: 7px; flex: 0 0 auto;
  padding: 9px 15px; border-radius: var(--radius-pill, 99px);
  border: 1px solid var(--border-default, rgba(46, 58, 102, .9));
  background: rgba(255, 255, 255, .03);
  font: 700 13px var(--font-sans); color: var(--text-secondary, #A7A6D9);
  text-decoration: none; white-space: nowrap;
  transition: border-color .2s ease, color .2s ease, background .2s ease;
}
.ml-filtre b {
  font-weight: 800; font-size: 12px; padding: 1px 7px;
  border-radius: var(--radius-pill, 99px);
  background: rgba(255, 255, 255, .07); color: inherit;
  font-variant-numeric: tabular-nums;
}
/* Chaque onglet porte SA couleur une fois actif (Adem, 02/08). Ce ne sont pas
   des teintes décoratives : ce sont déjà celles des cartes de familles
   (`.dc-etat`), donc l'œil fait le lien entre le filtre et ce qu'il montre. */
.ml-filtre.actif { border-color: currentColor; }
.ml-filtre.actif b { background: rgba(255, 255, 255, .14); }

.ml-filtre.actif[data-cle="toutes"] {
  color: var(--cream, #FFF4E3); background: rgba(255, 244, 227, .10);
}
.ml-filtre.actif[data-cle="essai"] {
  color: var(--halo, #FFD9A0); background: rgba(255, 217, 160, .10);
}
.ml-filtre.actif[data-cle="active"] {
  color: var(--success, #8FD4A8); background: var(--success-bg, rgba(143, 212, 168, .12));
}
.ml-filtre.actif[data-cle="resiliee"] {
  color: var(--danger, #E8A0A0); background: var(--danger-bg, rgba(232, 160, 160, .10));
}
.ml-filtre:focus-visible { outline: 2px solid var(--halo, #FFD9A0); outline-offset: 2px; }

/* --- Glisser dans l'histoire -------------------------------------------------
   La barre d'écoute se manipule au doigt (Adem, 02/08). On agrandit la zone
   touchée sans grossir le dessin : une onde de 26 px de haut est impossible à
   viser, Apple recommande 44. Et pendant le glissement, la barre s'allume pour
   qu'on sache que c'est bien elle qu'on tient. */
.dc-onde, [data-seek] {
  cursor: pointer; touch-action: none;
  -webkit-tap-highlight-color: transparent;
  position: relative;
}
.dc-onde::after, [data-seek]::after {
  content: ""; position: absolute; left: 0; right: 0;
  top: 50%; transform: translateY(-50%);
  height: 44px; min-height: 100%;
}
.dc-onde.en-glisse i { opacity: 1; }
[data-seek].en-glisse { filter: brightness(1.15); }

/* --- Le guide d'installation, en étapes -------------------------------------
   Remplace un paragraphe où il fallait aller chercher le mot « Partager » au
   milieu d'une phrase (Adem, 02/08 : « ultra simple, ultra clair, précis »).
   Un geste par ligne, numéroté, avec le signe que la mère cherche à l'écran. */
.ml-etapes {
  list-style: none; margin: 18px 0 4px; padding: 0;
  display: flex; flex-direction: column; gap: 10px; text-align: left;
}
.ml-etape {
  display: flex; align-items: center; gap: 12px;
  padding: 12px 14px; border-radius: 14px;
  background: rgba(255, 255, 255, .04);
  border: 1px solid var(--border-night, rgba(46, 58, 102, .9));
}
.ml-etape-n {
  flex: 0 0 26px; width: 26px; height: 26px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: linear-gradient(180deg, var(--halo, #FFD9A0), var(--amber, #FFB65C));
  color: var(--text-on-amber, #2A1B08);
  font: 800 13px var(--font-sans); font-variant-numeric: tabular-nums;
}
.ml-etape-txt { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.ml-etape-titre { font: 600 14.5px/1.35 var(--font-sans); color: var(--cream, #F6EFE4); }
.ml-etape-titre b { color: var(--halo, #FFD9A0); font-weight: 800; }
.ml-etape-detail { font: 400 12.5px/1.4 var(--font-sans); color: var(--lavender, #A7A6D9); }
/* Le signe que l'on cherche des yeux sur son propre écran. */
.ml-etape-icone {
  flex: 0 0 auto; font-size: 17px; color: var(--halo, #FFD9A0); opacity: .85;
  min-width: 22px; text-align: center;
}

/* ------------------------------------------------------------------
   Le rail des avis de l'accueil (1A), transpose du carrousel du site
   public (`.mlw-rails`) le 12/08. Deux comportements, comme la-bas :
     · au doigt sur telephone, avec accroche — un defilement automatique
       est illisible sur 76vw de large ;
     · en marquee lent des qu'il y a la place, ou l'oeil suit sans agir.
   Les cartes gardent le style de l'app (variables --card-navy etc.),
   seul le MECANISME vient du site : on transpose, on ne recree pas.
   ------------------------------------------------------------------ */
.ml-rail {
  overflow-x: auto; scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  padding: 6px 2px 12px; scrollbar-width: none;
}
.ml-rail::-webkit-scrollbar { display: none; }
.ml-piste { display: flex; gap: 14px; width: max-content; will-change: transform; }
.ml-piste > article { flex: 0 0 76vw; max-width: 330px; scroll-snap-align: center; }
/* La copie ne sert qu'a boucler le marquee : sur telephone elle ferait
   defiler deux fois les memes mots. */
.ml-piste .ml-copie { display: none; }

@media (min-width: 561px) {
  .ml-rail {
    overflow: hidden; scroll-snap-type: none; padding: 6px 0 12px;
    -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 12%, #000 88%, transparent 100%);
            mask-image: linear-gradient(90deg, transparent 0, #000 12%, #000 88%, transparent 100%);
  }
  .ml-piste { animation: ml-defile 46s linear infinite; }
  .ml-piste > article { flex: 0 0 330px; }
  .ml-piste .ml-copie { display: block; }
  .ml-rail:hover .ml-piste { animation-play-state: paused; }
}
@keyframes ml-defile { from { transform: translateX(0); } to { transform: translateX(-50%); } }
/* Un defilement qu'on n'a pas demande se coupe si le systeme le dit. */
@media (prefers-reduced-motion: reduce) {
  .ml-piste { animation: none !important; }
  .ml-rail { overflow-x: auto; scroll-snap-type: x mandatory; }
  .ml-piste .ml-copie { display: none; }
}
