/* ==========================================================================
   Composants — cartes, boutons, chemin, mascotte, phrases
   ========================================================================== */

/* --- Cartes -------------------------------------------------------------- */
.card {
  background: var(--surface);
  border-radius: var(--r-lg);
  padding: var(--sp-5);
  box-shadow: var(--sh-md);
  border: 2px solid var(--sakura-soft);
}

.card--flat   { box-shadow: var(--sh-sm); }
.card--mint   { background: var(--menthe-soft); border-color: var(--menthe); }
.card--lilac  { background: var(--lavande-soft); border-color: var(--lavande); }
.card--lemon  { background: #FFFCEC; border-color: var(--citron); }

.card__head {
  display: flex; align-items: center; gap: var(--sp-2);
  font-weight: 800;
  font-size: var(--fs-md);
  margin-bottom: var(--sp-3);
}

/* --- Boutons ------------------------------------------------------------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-2);
  min-height: var(--tap);
  padding: var(--sp-3) var(--sp-5);
  border-radius: var(--r-full);
  font-weight: 700;
  background: var(--surface);
  color: var(--encre);
  box-shadow: var(--sh-sm), var(--sh-in);
  transition: transform .15s cubic-bezier(.34,1.56,.64,1), box-shadow .15s, filter .15s;
}
.btn:hover  { filter: brightness(1.03); transform: translateY(-2px); }
.btn:active { transform: translateY(1px) scale(.97); box-shadow: var(--sh-sm); }

.btn--primary { background: linear-gradient(160deg, var(--sakura), var(--sakura-deep)); color: #fff; }
.btn--mint    { background: var(--menthe); }
.btn--lilac   { background: var(--lavande); color: #fff; }
.btn--ghost   { background: transparent; box-shadow: none; color: var(--encre-doux); }
.btn--block   { display: flex; width: 100%; }
.btn--sm      { min-height: 36px; padding: var(--sp-2) var(--sp-4); font-size: var(--fs-sm); }

.btn[disabled] { opacity: .45; pointer-events: none; }

/* ==========================================================================
   Le chemin — page d'accueil
   ========================================================================== */
.path {
  margin: var(--sp-5) auto 0;
}

/* --- Un acte : une étape du voyage, posée sur son paysage ------------------ */

.acte {
  position: relative;
  isolation: isolate;              /* le décor reste sous CE bloc, pas sous la page */
  padding-bottom: var(--sp-5);
}

/* Le panneau est volontairement pâle et fondu aux deux bouts : il doit se
   lire comme une atmosphère, jamais comme une image sur laquelle on aurait
   posé du texte. Le fondu du haut masque en prime le raccord avec l'acte
   précédent — deux illustrations ne se raboutent jamais proprement. */
.acte__decor {
  position: absolute;
  inset: 0;
  z-index: 0;
  /* `background-image` est posé en style en ligne par home.js — une `url()`
     passée par variable CSS serait résolue relativement à CETTE feuille, donc
     depuis `css/`, et sortirait en 404. */
  background-repeat: no-repeat;
  /* Centré et non calé en haut : à 100 % de large le panneau fait ~534 px pour
     un acte d'environ 890, il ne couvrait donc que l'en-tête. Au centre, le
     paysage accompagne les chapitres — c'est là qu'il sert. Ni `cover` ni un
     étirement vertical : le premier rognerait les bords gauche et droit, où
     tout le décor a justement été placé, le second déformerait la maison. */
  background-position: center;
  background-size: 100% auto;
  /* Chaque panneau contient déjà un sentier dessiné, et l'œil finit par voir
     deux chemins. L'opacité seule ne suffisait pas à le régler : à 45 % le
     pavé de l'illustration restait net, donc au même plan que le nôtre. Un
     flou d'un pixel et demi le renvoie derrière — c'est la netteté, pas
     l'intensité, qui décide de ce qu'on lit au premier plan. */
  opacity: .5;
  filter: blur(1.5px) saturate(.9);
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 10%, #000 90%, transparent);
          mask-image: linear-gradient(to bottom, transparent, #000 10%, #000 90%, transparent);
  pointer-events: none;
}

/* Voile crème entre le paysage et le texte. Il est porté par `.acte` et non
   par `.acte__decor` : `opacity` s'applique au groupe entier, un pseudo-élément
   posé à l'intérieur du décor serait atténué avec lui et ne voilerait rien.
   Sans ce calque, « CHAPITRE 1 » en rose clair passait sur un fond rose. */
.acte::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 0;
  background: var(--creme);
  opacity: .34;
  pointer-events: none;
}

.acte__head, .acte__rows { position: relative; z-index: 1; }

/* Halo crème sous l'en-tête. Une plaque rectangulaire couperait le paysage en
   deux ; une ellipse fondue éclaircit juste ce qu'il faut sous le texte centré
   et reste invisible en tant que forme. */
.acte__head {
  position: relative;
  text-align: center;
  padding: var(--sp-6) var(--sp-4) var(--sp-4);
  background: radial-gradient(ellipse 62% 54% at 50% 62%,
              rgb(255 249 243 / .88) 0%, rgb(255 249 243 / .6) 55%, transparent 78%);
}
.acte__borne {
  width: 150px; height: 150px;
  display: block;
  margin: 0 auto var(--sp-2);
  object-fit: contain;
  filter: drop-shadow(0 6px 10px rgb(93 65 87 / .12));
}
.acte__eyebrow {
  font-family: var(--font-fr);
  font-size: 0.7rem; font-weight: 700;
  letter-spacing: .1em;
  color: var(--lavande-encre);
  margin: 0;
}
.acte__title {
  font-size: var(--fs-lg);
  margin: 2px 0 var(--sp-1);
}
.acte__lead {
  font-size: var(--fs-xs);
  color: var(--encre-doux);
  margin: 0 auto;
  max-width: 30ch;
  text-wrap: balance;
}
.acte__count {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  margin: var(--sp-3) 0 0;
  padding: 3px var(--sp-3);
  border-radius: var(--r-full);
  background: rgb(255 255 255 / .72);
  font-size: var(--fs-xs);
  color: var(--encre-doux);
}
.acte__bar {
  width: 46px; height: 6px;
  border-radius: var(--r-full);
  background: var(--sakura-soft);
  overflow: hidden;
}
.acte__bar i { display: block; height: 100%; background: var(--menthe); }
.acte.is-complete .acte__count { background: var(--menthe-soft); color: var(--encre); }

/* --- Une ligne du chemin --------------------------------------------------- */

/* La pastille bascule réellement de part et d'autre de l'axe (± 9 %), et un
   segment de sentier la relie à la suivante : c'est ce décalage qui fait
   serpenter le chemin, le libellé partant toujours du côté opposé. */
.path__row {
  position: relative;
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: center;
  gap: var(--sp-3);
  min-height: 98px;
  padding: var(--sp-2) 0;
}

/* Le segment descend du centre de CETTE pastille vers le centre de la
   suivante — d'où `top: 50%` et une hauteur d'une ligne entière. */
/* `width: 100%` est indispensable : le viewBox carré donne au SVG un ratio
   intrinsèque de 1, et une largeur `auto` s'aligne alors sur la hauteur — le
   sentier se retrouvait écrasé sur 98 px contre le bord gauche, sans que
   `left: 0; right: 0` y change quoi que ce soit. */
.path__link {
  position: absolute;
  left: 0; top: 50%;
  width: 100%; height: 100%;
  z-index: 0;
  pointer-events: none;
  overflow: visible;
}
.path__link-road {
  fill: none;
  stroke: var(--sakura);
  stroke-width: 18;
  stroke-linecap: round;
}
/* Les « joints » du pavage : un pointillé clair posé sur la bande, qui suffit
   à faire lire un sentier pavé plutôt qu'un simple trait. */
.path__link-joints {
  fill: none;
  stroke: var(--creme);
  stroke-width: 2.5;
  stroke-linecap: round;
  stroke-dasharray: 1 11;
}
.path__row.is-done .path__link-road { stroke: var(--menthe); }

/* La pastille est positionnée en absolu, et non placée dans la grille : un
   pourcentage de marge sur un élément de grille se résout sur la largeur de
   sa COLONNE, alors que le SVG du sentier, lui, est étiré sur la largeur de
   la ligne entière. Les deux ne tombaient pas au même endroit. En absolu, le
   `left` et le viewBox partagent la même boîte de référence — 59 % ici, 59
   dans le tracé. Le libellé reste dans la grille : c'est lui qui doit
   continuer à dicter la hauteur de la ligne. */
.stone {
  position: absolute;
  z-index: 2;
  top: 50%;
  left: 59%;
  transform: translate(-50%, -50%);
}
.path__row:nth-child(even) .stone { left: 41%; }

/* Repère « いまここ » — le premier chapitre non tamponné. Sur 28 entrées,
   retrouver sa place à la main est la friction qui fait refermer l'appli. */
.stone__here {
  position: absolute;
  left: 50%; top: -14px;
  transform: translateX(-50%);
  padding: 1px var(--sp-2);
  border-radius: var(--r-full);
  background: var(--sakura-deep);
  color: #fff;
  font-size: 0.6rem; font-weight: 800;
  white-space: nowrap;
  box-shadow: var(--sh-sm);
}

/* Apparition au défilement. Les lignes hors écran restent invisibles mais
   gardent leur place : aucun décalage de mise en page au moment du révélé. */
.path__row, .acte__head { transition: opacity .5s ease, transform .5s ease; }
@media (prefers-reduced-motion: no-preference) {
  .path__row:not(.is-in), .acte__head:not(.is-in) {
    opacity: 0;
    transform: translateY(14px);
  }
}

/* Le libellé occupe la moitié opposée à la pastille. La réserve latérale tient
   compte du disque, qui déborde de 9 % dans cette moitié-là. */
/* Le libellé est une étiquette posée sur le paysage, pas du texte flottant
   dessus. Deux raisons, dans cet ordre : sur un décor chargé, `.stone__sub`
   tombait à 2,85 de contraste — l'étiquette le ramène au niveau du reste du
   site ; et une pancarte au bord d'un sentier, c'est aussi ce que l'objet
   *est*. `justify-self` la fait rétrécir sur son texte : une plaque large
   comme la colonne aurait redécoupé le paysage en bandes. */
.stone__info {
  grid-column: 1; grid-row: 1;
  justify-self: end;
  text-align: right;
  margin-right: var(--sp-3);
  padding: var(--sp-2) var(--sp-3);
  border-radius: var(--r-sm);
  background: rgb(255 249 243 / .84);
  box-shadow: 0 2px 6px rgb(93 65 87 / .06);
}
.path__row:nth-child(even) .stone__info {
  grid-column: 2;
  justify-self: start;
  text-align: left;
  margin: 0 0 0 var(--sp-3);
}

.stone__chap {
  display: block;
  font-family: var(--font-fr);
  font-size: 0.7rem; font-weight: 700;
  color: var(--lavande-encre);
  letter-spacing: .06em;
}
/* `balance` : l'étiquette rétrécit sur son texte, et sans lui le japonais
   partait régulièrement sur un dernier caractère seul (「…動詞 / 詞」). */
.stone__title { display: block; font-size: var(--fs-sm); font-weight: 800; line-height: 1.4;
                text-wrap: balance; }
.stone__sub   { display: block; font-size: var(--fs-xs); color: var(--encre-doux);
                text-wrap: balance; }

.stone__badge {
  display: inline-block;
  margin-bottom: 2px;
  padding: 1px var(--sp-2);
  border-radius: var(--r-full);
  background: var(--menthe-soft);
  border: 1.5px solid var(--menthe);
  font-size: 0.65rem; font-weight: 800;
  letter-spacing: .02em;
}

.stone__disc {
  width: 62px; height: 62px;
  display: grid; place-items: center;
  border-radius: var(--r-full);
  background: var(--surface);
  border: 3px solid var(--sakura-soft);
  box-shadow: var(--sh-md);
  font-size: 1.6rem;
  position: relative;
  transition: transform .2s cubic-bezier(.34,1.56,.64,1), box-shadow .2s;
}
.stone:hover  .stone__disc { transform: scale(1.09) rotate(-4deg); }
.stone:active .stone__disc { transform: scale(.94); }

.stone__num {
  position: absolute; top: -6px; left: -6px;
  width: 24px; height: 24px;
  display: grid; place-items: center;
  border-radius: var(--r-full);
  background: var(--lavande);
  color: #fff;
  font-family: var(--font-fr);
  font-size: 0.7rem; font-weight: 700;
  box-shadow: var(--sh-sm);
}

/* Piste vocabulaire — un anneau menthe autour de la pastille. On passe par
   outline et non par border/background : ces deux canaux sont déjà pris par
   l'état d'avancement (未学習 / 学習中 / クリア), les deux informations
   doivent rester lisibles ensemble. */
.stone--vocab .stone__disc {
  outline: 3px dashed var(--menthe);
  outline-offset: 4px;
}
.stone--vocab .stone__num { background: var(--menthe); color: var(--encre); }

/* Légende sous la barre de progression */
.path-legend {
  display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-4);
  margin-top: var(--sp-3);
  font-size: var(--fs-xs);
  color: var(--encre-doux);
}
.path-legend span { display: inline-flex; align-items: center; gap: var(--sp-2); }
.path-legend__dot {
  width: 14px; height: 14px; border-radius: var(--r-full);
  background: var(--surface);
  border: 3px solid var(--sakura-soft);
}
.path-legend__dot--vocab { border-color: var(--menthe); border-style: dashed; }

/* États d'une pierre */
.stone--doing .stone__disc {
  border-color: var(--sakura-deep);
  animation: pulse-ring 2.2s ease-in-out infinite;
}
.stone--done  .stone__disc { background: var(--menthe-soft); border-color: var(--menthe); }
.stone__check {
  position: absolute; right: -4px; bottom: -4px;
  width: 24px; height: 24px;
  display: grid; place-items: center;
  border-radius: var(--r-full);
  background: var(--succes);
  color: #fff;
  font-size: 0.75rem;
  box-shadow: var(--sh-sm);
}

/* ==========================================================================
   Mascotte
   ========================================================================== */
/* Pas de largeur en dur : elle écraserait le paramètre `size` du composant.
   L'attribut width porte la dimension, height:auto garde la proportion. */
.mascot { height: auto; max-width: 100%; overflow: visible; }
.mascot__body { transform-origin: 60px 96px; }

/* Les états ne se jouent plus par bascule de calques : chaque pose est une
   illustration à part entière. Ces règles ne servent donc plus qu'au SVG de
   repli, quand un PNG ne se charge pas. */
.mascot .eye-happy,
.mascot .eye-closed { display: none; }
.mascot.is-happy  .eye-open,   .mascot.is-sleepy .eye-open   { display: none; }
.mascot.is-happy  .eye-happy   { display: block; }
.mascot.is-sleepy .eye-closed  { display: block; }

.mascot .zzz { opacity: 0; }
.mascot.is-sleepy .zzz { opacity: 1; animation: float-zzz 2.6s ease-in-out infinite; }

.mascot .sparkle { opacity: 0; }
.mascot.is-happy .sparkle { opacity: 1; animation: twinkle 1s ease-in-out infinite; }

/* Bulle de dialogue */
.mascot-block {
  display: flex; align-items: flex-end; gap: var(--sp-2);
  margin: var(--sp-4) 0;
}
.bubble {
  position: relative;
  flex: 1;
  background: var(--surface);
  border: 2px solid var(--sakura-soft);
  border-radius: var(--r-lg);
  padding: var(--sp-3) var(--sp-4);
  font-size: var(--fs-sm);
  font-weight: 500;
  box-shadow: var(--sh-sm);
}
.bubble::before {
  content: '';
  position: absolute; left: -9px; bottom: 20px;
  width: 14px; height: 14px;
  background: var(--surface);
  border-left: 2px solid var(--sakura-soft);
  border-bottom: 2px solid var(--sakura-soft);
  transform: rotate(45deg);
  border-radius: 0 0 0 4px;
}

/* ==========================================================================
   Carte de phrase — le composant central du site
   ========================================================================== */
.phrase {
  background: var(--surface);
  border: 2px solid var(--sakura-soft);
  border-radius: var(--r-md);
  padding: var(--sp-4);
  box-shadow: var(--sh-sm);
}
.phrase + .phrase { margin-top: var(--sp-3); }

.phrase__fr {
  font-family: var(--font-fr);
  font-size: var(--fs-lg);
  font-weight: 700;
  line-height: 1.45;
  color: var(--encre);
}

.phrase__ipa {
  font-family: var(--font-fr);
  font-size: var(--fs-sm);
  color: var(--encre-doux);
  margin-top: var(--sp-1);
}

/* Le katakana est une béquille : volontairement discret */
.phrase__kata {
  font-size: var(--fs-xs);
  color: var(--encre-pale);
  letter-spacing: .04em;
}

.phrase__ja {
  margin-top: var(--sp-2);
  padding-top: var(--sp-2);
  border-top: 2px dotted var(--sakura-soft);
  font-size: var(--fs-sm);
  font-weight: 500;
}

.phrase__note {
  margin-top: var(--sp-2);
  font-size: var(--fs-xs);
  color: var(--encre-doux);
  background: var(--lavande-soft);
  border-radius: var(--r-sm);
  padding: var(--sp-2) var(--sp-3);
}

.phrase__audio {
  display: flex; gap: var(--sp-2); align-items: center;
  margin-top: var(--sp-3);
}

.audio-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-2);
  min-height: var(--tap);
  padding: 0 var(--sp-5);
  border-radius: var(--r-full);
  background: linear-gradient(160deg, var(--sakura), var(--sakura-deep));
  color: #fff;
  font-weight: 700;
  box-shadow: var(--sh-sm), var(--sh-in);
  transition: transform .15s cubic-bezier(.34,1.56,.64,1);
}
.audio-btn:active { transform: scale(.95); }
.audio-btn--slow  { background: var(--lavande); padding: 0 var(--sp-4); font-size: var(--fs-sm); }
.audio-btn.is-playing { animation: wiggle .6s ease-in-out infinite; }

/* Variantes compactes : dans les fiches de mots et les dialogues, le bouton
   se répète beaucoup — il reste tapable (44px) mais cesse de crier. */
.audio-btn--sm   { min-height: 38px; padding: 0 var(--sp-4); font-size: var(--fs-sm); }
.audio-btn--mini {
  min-width: var(--tap); width: var(--tap); height: var(--tap);
  padding: 0; font-size: 1.05rem; flex: none;
}

/* ==========================================================================
   Fiche de mot — chapitres de vocabulaire 🍬
   ========================================================================== */
.wordset { display: grid; gap: var(--sp-3); }

.word {
  background: var(--surface);
  border: 2px solid var(--menthe);
  border-radius: var(--r-md);
  padding: var(--sp-4);
  box-shadow: var(--sh-sm);
}

.word__head { display: flex; align-items: center; gap: var(--sp-2); }

.word__fr {
  flex: 1;
  min-width: 0;
  font-family: var(--font-fr);
  font-size: var(--fs-lg);
  font-weight: 700;
  line-height: 1.35;
}

/* Le genre est LE point dur pour une japonophone : on le code en couleur
   plutôt que de le cacher dans l'article. */
.word__gender {
  flex: none;
  padding: 2px var(--sp-2);
  border-radius: var(--r-full);
  font-size: var(--fs-xs);
  font-weight: 800;
  line-height: 1.6;
}
.word__gender--m { background: var(--lavande-soft); color: #6B4E9B; }
.word__gender--f { background: var(--sakura-soft);  color: #B3416A; }

.word__ja { margin-top: var(--sp-1); font-size: var(--fs-sm); font-weight: 500; }

.word__ex {
  margin-top: var(--sp-3);
  padding-top: var(--sp-3);
  border-top: 2px dotted var(--menthe);
}
.word__ex-fr {
  font-family: var(--font-fr);
  font-size: var(--fs-md);
  font-weight: 600;
  line-height: 1.5;
}
.word__ex-ja { margin-top: var(--sp-1); font-size: var(--fs-xs); color: var(--encre-doux); }

/* ==========================================================================
   Dialogue — bulles alternées gauche / droite
   ========================================================================== */
.dialogue__scene {
  font-size: var(--fs-sm);
  color: var(--encre-doux);
  margin-bottom: var(--sp-3);
}

.dialogue__lines { display: grid; gap: var(--sp-3); margin-top: var(--sp-4); }

.dline { display: flex; align-items: flex-start; gap: var(--sp-2); }
.dline--b { flex-direction: row-reverse; }

.dline__who {
  flex: none;
  width: 32px; height: 32px;
  display: grid; place-items: center;
  border-radius: var(--r-full);
  background: var(--lavande);
  color: #fff;
  font-family: var(--font-fr);
  font-weight: 800;
  font-size: var(--fs-sm);
}
.dline--b .dline__who { background: var(--sakura-deep); }

.dline__bubble {
  flex: 1;
  min-width: 0;
  background: var(--lavande-soft);
  border-radius: var(--r-md);
  border-top-left-radius: var(--r-sm);
  padding: var(--sp-3);
}
.dline--b .dline__bubble {
  background: var(--sakura-soft);
  border-top-left-radius: var(--r-md);
  border-top-right-radius: var(--r-sm);
}

.dline__fr {
  font-family: var(--font-fr);
  font-size: var(--fs-md);
  font-weight: 700;
  line-height: 1.45;
}
.dline__ja {
  margin-top: var(--sp-2);
  font-size: var(--fs-xs);
  color: var(--encre-doux);
}
.dline__audio { display: flex; gap: var(--sp-1); margin-top: var(--sp-2); }

/* ==========================================================================
   Blocs pédagogiques
   ========================================================================== */
.note-block {
  border-radius: var(--r-md);
  padding: var(--sp-4);
  font-size: var(--fs-sm);
  border: 2px solid transparent;
}
.note-block__title {
  display: flex; align-items: center; gap: var(--sp-2);
  font-weight: 800;
  margin-bottom: var(--sp-2);
}
.note-block--tip     { background: var(--menthe-soft);  border-color: var(--menthe); }
.note-block--pitfall { background: #FFF1EE;             border-color: #FFC9BF; }

/* Tableaux : scroll interne, jamais de débordement de page */
.table-wrap { overflow-x: auto; border-radius: var(--r-md); border: 2px solid var(--sakura-soft); }
.t {
  width: 100%;
  border-collapse: collapse;
  background: var(--surface);
  font-size: var(--fs-sm);
}
.t th, .t td { padding: var(--sp-3); text-align: left; border-bottom: 1px solid var(--sakura-soft); }
.t th { background: var(--sakura-soft); font-weight: 800; white-space: nowrap; }
.t tr:last-child td { border-bottom: none; }
.t .fr { font-family: var(--font-fr); font-weight: 700; }

/* --- Barre de progression ------------------------------------------------ */
.progress {
  height: 14px;
  background: var(--sakura-soft);
  border-radius: var(--r-full);
  overflow: hidden;
}
.progress__bar {
  height: 100%;
  border-radius: var(--r-full);
  background: linear-gradient(90deg, var(--menthe), var(--ciel));
  transition: width .6s cubic-bezier(.34,1.2,.64,1);
}

/* --- Fil d'ariane / retour ---------------------------------------------- */
.back-link {
  display: inline-flex; align-items: center; gap: var(--sp-1);
  min-height: var(--tap);
  font-size: var(--fs-sm);
  font-weight: 700;
  color: var(--encre-doux);
}

/* --- Bandeaux illustrés -------------------------------------------------- */

/* Les illustrations portent leur propre fond crème : pas de cadre, pas
   d'ombre — le bandeau doit se poser sur la page, pas s'en détacher comme une
   vignette. Seuls les angles sont arrondis, au rayon des cartes. */
.home-hero,
.chapter-head__band {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--r-lg);
  overflow: hidden;
}

.home-hero img { display: block; width: 100%; height: auto; }

/* Le bandeau précède le titre dans le flux, mais l'en-tête reste une pile
   simple : c'est la grille de .stack qui gère l'espacement des vues. */
.chapter-head { display: grid; gap: var(--sp-1); }
.chapter-head__band { margin-bottom: var(--sp-1); }

/* --- Appel à réviser, sur l'accueil -------------------------------------- */
.review-cta {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-4);
  transition: transform .15s cubic-bezier(.34,1.56,.64,1);
}
.review-cta:hover  { transform: translateY(-2px); }
.review-cta:active { transform: scale(.98); }
.review-cta__ico { font-size: 1.8rem; flex: none; }
.review-cta__txt { display: grid; flex: 1; min-width: 0; line-height: 1.45; }
.review-cta__txt small { font-size: var(--fs-xs); color: var(--encre-doux); }
.review-cta__go {
  flex: none;
  font-family: var(--font-fr); font-size: var(--fs-lg); font-weight: 700;
  color: var(--encre-pale);
}

/* --- Série d'apprentissage ----------------------------------------------- */
.streak-card {
  display: flex; align-items: center; gap: var(--sp-4);
  padding: var(--sp-4) var(--sp-5);
}
.streak-card__flame { font-size: 2.2rem; flex: none; line-height: 1; }
.streak-card__num {
  font-family: var(--font-fr);
  font-size: var(--fs-xl); font-weight: 700; line-height: 1.1;
  font-variant-numeric: tabular-nums;
}
.streak-card__num small { font-size: var(--fs-base); margin-left: 2px; }
.streak-card__lbl  { font-size: var(--fs-xs); color: var(--encre-doux); line-height: 1.5; }
.streak-card__best {
  margin-left: auto; flex: none;
  text-align: right;
  font-size: var(--fs-xs); color: var(--encre-doux); line-height: 1.5;
}

/* --- Carnet de tampons --------------------------------------------------- */
.stamp-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(84px, 1fr));
  gap: var(--sp-3);
}

.stamp {
  aspect-ratio: 1;
  position: relative;
  display: grid; place-items: center;
  /* Le numéro est en position absolue en bas : cette réserve empêche le
     tampon, bien plus large que l'ancien emoji, de venir le recouvrir. */
  padding-bottom: 18px;
  border-radius: var(--r-md);
  background: var(--surface);
  border: 3px dashed var(--sakura-soft);
  transition: transform .2s cubic-bezier(.34,1.56,.64,1);
}
.stamp:active { transform: scale(.94); }
.stamp__face { font-size: 2rem; filter: grayscale(1); opacity: .3; }
/* Le tampon se règle sur la hauteur disponible, pas sur la largeur : la case
   est carrée, une largeur en pourcentage viendrait toujours mordre sur le
   numéro. Ici il occupe exactement la place laissée par la réserve du bas. */
.stamp__img { height: 100%; width: auto; max-width: 100%; min-height: 0; }
.stamp__num {
  position: absolute; bottom: 6px;
  font-family: var(--font-fr);
  font-size: 0.7rem; font-weight: 700;
  /* Sans cette remise à 1, la hauteur de ligne héritée du corps de texte
     ajoute une dizaine de pixels invisibles qui remontent sous le tampon. */
  line-height: 1;
  color: var(--encre-pale);
}

.stamp.is-owned {
  background: var(--menthe-soft);
  border-style: solid;
  border-color: var(--menthe);
  box-shadow: var(--sh-sm);
  transform: rotate(-3deg);
}
/* La bordure code déjà l'état (pointillée = à faire, pleine = gagné) : la
   piste vocabulaire passe donc par un marqueur d'angle, pas par la bordure. */
.stamp--vocab::after {
  content: '🍬';
  position: absolute; top: 4px; right: 5px;
  font-size: 0.7rem;
  opacity: .75;
}

.stamp.is-owned:nth-child(even) { transform: rotate(2.5deg); }
.stamp.is-owned .stamp__face { filter: none; opacity: 1; }
.stamp.is-owned .stamp__num  { color: var(--encre-doux); }

/* ==========================================================================
   Quiz
   ========================================================================== */
.quiz__bar {
  display: flex; align-items: center; gap: var(--sp-3);
  margin-bottom: var(--sp-4);
}
.quiz__count {
  flex: none;
  font-family: var(--font-fr);
  font-size: var(--fs-sm); font-weight: 700;
  color: var(--encre-doux);
  font-variant-numeric: tabular-nums;
}
.quiz__bar .progress { flex: 1; height: 10px; }

.qcard {
  background: var(--surface);
  border: 2px solid var(--sakura-soft);
  border-radius: var(--r-lg);
  padding: var(--sp-5);
  box-shadow: var(--sh-md);
}
.qcard__type {
  display: inline-block;
  margin-bottom: var(--sp-2);
  padding: 2px var(--sp-3);
  border-radius: var(--r-full);
  background: var(--lavande-soft);
  color: #6B4E9B;
  font-size: var(--fs-xs); font-weight: 800;
}
.qcard__q { font-size: var(--fs-md); font-weight: 800; line-height: 1.7; }
.qcard__body { margin-top: var(--sp-4); }

/* Le trou à remplir doit sauter aux yeux : c'est lui la question. */
.blank {
  padding: 0 var(--sp-1);
  border-radius: var(--r-sm);
  background: var(--citron);
  font-family: var(--font-fr);
  letter-spacing: -.08em;
}

/* --- Choix multiples ----------------------------------------------------- */
.choices { display: grid; gap: var(--sp-2); }

.choice {
  display: flex; align-items: center; gap: var(--sp-3);
  width: 100%;
  min-height: var(--tap);
  padding: var(--sp-3) var(--sp-4);
  text-align: left;
  border-radius: var(--r-md);
  background: var(--surface);
  border: 2px solid var(--sakura-soft);
  box-shadow: var(--sh-sm);
  transition: transform .14s cubic-bezier(.34,1.56,.64,1), border-color .14s, background .14s;
}
.choice:hover:not([disabled]) { transform: translateY(-2px); border-color: var(--sakura); }
.choice:active { transform: scale(.98); }
.choice[disabled] { opacity: 1; cursor: default; }   /* la correction reste lisible */

.choice__mark {
  flex: none;
  width: 28px; height: 28px;
  display: grid; place-items: center;
  border-radius: var(--r-full);
  background: var(--lavande-soft);
  font-family: var(--font-fr);
  font-size: var(--fs-sm); font-weight: 800;
  color: #6B4E9B;
}
.choice__txt { font-weight: 600; line-height: 1.5; }

.choice.is-correct { background: var(--menthe-soft); border-color: var(--menthe); }
.choice.is-correct .choice__mark { background: var(--menthe); color: var(--encre); }
.choice.is-wrong   { background: var(--erreur-soft); border-color: var(--erreur); }
.choice.is-wrong .choice__mark   { background: var(--erreur); color: #fff; }
.choice:not(.is-correct):not(.is-wrong)[disabled] { opacity: .5; }

/* --- Texte à trous ------------------------------------------------------- */
.fill__input {
  width: 100%;
  min-height: var(--tap);
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--r-md);
  border: 2px solid var(--sakura-soft);
  background: var(--surface);
  color: var(--encre);
  font-family: var(--font-fr);
  font-size: var(--fs-lg); font-weight: 700;
  text-align: center;
}
.fill__input:focus { outline: none; border-color: var(--sakura-deep); box-shadow: var(--sh-sm); }
.fill__input.is-correct { background: var(--menthe-soft); border-color: var(--menthe); }
.fill__input.is-wrong   { background: var(--erreur-soft); border-color: var(--erreur); }

/* --- Jetons : accents et remise en ordre --------------------------------- */
.chip {
  min-height: 40px;
  padding: var(--sp-2) var(--sp-4);
  border-radius: var(--r-full);
  background: var(--surface);
  border: 2px solid var(--lavande);
  font-family: var(--font-fr);
  font-size: var(--fs-base); font-weight: 700;
  box-shadow: var(--sh-sm);
  transition: transform .12s cubic-bezier(.34,1.56,.64,1);
}
.chip:active { transform: scale(.93); }
.chip[disabled] { opacity: 1; cursor: default; }

/* Un clavier d'accents, pas une rangée de boutons d'action : en retrait de la
   saisie, et en grille plutôt qu'en flux — douze touches réparties 4 × 3
   tombent juste, là où un retour à la ligne libre laisse une dernière rangée
   bancale. */
.fill__accents {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--sp-2);
  margin: var(--sp-4) 0;
}
.chip--accent {
  min-height: var(--tap);
  padding: 0;
  border-color: var(--sakura-soft);
  background: var(--creme);
  box-shadow: none;
}

.order__line {
  display: flex; flex-wrap: wrap; gap: var(--sp-2);
  align-items: center;
  min-height: 60px;
  padding: var(--sp-3);
  border-radius: var(--r-md);
  border: 2px dashed var(--lavande);
  background: var(--lavande-soft);
}
.order__line.is-correct { border-style: solid; border-color: var(--menthe); background: var(--menthe-soft); }
.order__line.is-wrong   { border-style: solid; border-color: var(--erreur); background: var(--erreur-soft); }
.order__hint { font-size: var(--fs-sm); color: var(--encre-doux); }
.chip--placed { border-color: var(--sakura-deep); }

.order__pool {
  display: flex; flex-wrap: wrap; gap: var(--sp-2);
  justify-content: center;
  margin: var(--sp-4) 0;
  min-height: 40px;
}

/* --- Correction ---------------------------------------------------------- */
.verdict {
  margin-top: var(--sp-4);
  padding: var(--sp-4);
  border-radius: var(--r-md);
  border: 2px solid transparent;
  animation: fade-up .3s cubic-bezier(.22,1,.36,1) both;
}
.verdict--ok { background: var(--menthe-soft); border-color: var(--menthe); }
.verdict--ko { background: var(--erreur-soft); border-color: var(--erreur); }

.verdict__head { font-size: var(--fs-md); font-weight: 800; }
.verdict__answer { margin-top: var(--sp-2); font-size: var(--fs-sm); }
.verdict__answer .fr { font-family: var(--font-fr); font-size: var(--fs-md); }
.verdict__given { margin-top: var(--sp-1); font-size: var(--fs-xs); color: var(--encre-doux); }
.verdict__why {
  margin-top: var(--sp-3);
  padding-top: var(--sp-3);
  border-top: 2px dotted rgba(93, 65, 87, .18);
  font-size: var(--fs-sm);
}
.verdict + .btn { margin-top: var(--sp-4); }

/* --- Écran de résultat --------------------------------------------------- */
.result {
  display: grid; justify-items: center; gap: var(--sp-1);
  padding: var(--sp-5) var(--sp-4);
  border-radius: var(--r-lg);
  background: var(--surface);
  border: 2px solid var(--sakura-soft);
  box-shadow: var(--sh-md);
  text-align: center;
}
.result--win { background: var(--menthe-soft); border-color: var(--menthe); }

.result__stamp {
  width: 148px; height: auto;
  margin-bottom: var(--sp-2);
  animation: stamp-pop .7s cubic-bezier(.34,1.56,.64,1) both;
}
.result__ico { font-size: 3rem; line-height: 1.2; }
/* Repli quand le PNG du tampon manque : sans cette remise à zéro, l'emoji
   hériterait du gris et de la transparence du carnet de tampons. */
.result .stamp__face { font-size: 3rem; filter: none; opacity: 1; }

.result__pct {
  font-family: var(--font-fr);
  font-size: var(--fs-2xl); font-weight: 700;
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
}
.result__pct small { font-size: var(--fs-lg); color: var(--encre-doux); }
.result__frac { font-size: var(--fs-sm); }
.result__msg  { margin-top: var(--sp-2); font-size: var(--fs-sm); font-weight: 700; }

/* ==========================================================================
   Cartes de révision — たんごカード
   ========================================================================== */
.flash {
  display: grid; justify-items: center; gap: var(--sp-2);
  padding: var(--sp-6) var(--sp-4);
  border-radius: var(--r-lg);
  background: var(--surface);
  border: 2px solid var(--sakura-soft);
  box-shadow: var(--sh-md);
  text-align: center;
}
.flash--drill { border-color: var(--peche); border-style: dashed; }
.flash__drill { font-size: var(--fs-xs); font-weight: 700; color: #C77A4E; }

.flash__tag {
  padding: 2px var(--sp-3);
  border-radius: var(--r-full);
  background: var(--lavande-soft);
  font-size: var(--fs-xs); font-weight: 700;
  color: var(--encre-doux);
}

.flash__word,
.flash__fr {
  font-family: var(--font-fr);
  font-weight: 700;
  line-height: 1.4;
  margin-top: var(--sp-2);
}
.flash__word { font-size: var(--fs-xl); display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; justify-content: center; }
.flash__fr   { font-size: var(--fs-lg); }

.flash__hint { font-size: var(--fs-xs); color: var(--encre-pale); }

.flash__answer[hidden] { display: none; }
.flash__answer {
  width: 100%;
  margin-top: var(--sp-4);
  padding-top: var(--sp-4);
  border-top: 2px dotted var(--sakura-soft);
  animation: fade-up .28s cubic-bezier(.22,1,.36,1) both;
}
.flash__ja { font-size: var(--fs-md); font-weight: 700; margin-top: var(--sp-2); }

/* La phrase d'exemple est le vrai contenu du verso d'une carte de mot :
   elle a droit à son propre cadre, pas à une ligne de plus. */
.flash__ex {
  margin-top: var(--sp-4);
  padding: var(--sp-4);
  border-radius: var(--r-md);
  background: var(--menthe-soft);
  border: 2px solid var(--menthe);
}
.flash__ex .phrase__audio { justify-content: center; }

.grade { display: flex; gap: var(--sp-3); margin-top: var(--sp-4); }
.grade .btn { flex: 1; }
.grade__yes { background: var(--menthe); }
.grade__no  { background: var(--peche); }

/* --- Répartition dans les 5 boîtes --------------------------------------- */
.boxes {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  align-items: end;
  gap: var(--sp-2);
  height: 110px;
}
.boxes__col {
  display: grid;
  grid-template-rows: 1fr auto auto;
  justify-items: center;
  align-items: end;
  gap: 2px;
  height: 100%;
}
.boxes__bar {
  width: 70%;
  min-height: 4px;
  align-self: end;
  border-radius: var(--r-sm) var(--r-sm) 4px 4px;
  background: linear-gradient(180deg, var(--menthe), var(--ciel));
}
.boxes__n   { font-family: var(--font-fr); font-size: var(--fs-xs); font-weight: 700; line-height: 1.4; }
.boxes__lbl { font-family: var(--font-fr); font-size: 0.65rem; color: var(--encre-pale); line-height: 1.4; }

/* --- Confettis ----------------------------------------------------------- */
.confetti {
  position: fixed; inset: 0;
  pointer-events: none;
  z-index: 30;
  overflow: hidden;
}
.confetti i {
  position: absolute;
  top: 0;
  font-style: normal;
  line-height: 1;
  animation: confetti-fall cubic-bezier(.35,.15,.6,.85) forwards;
}

/* --- Invitation à installer ---------------------------------------------- */

.install { position: relative; }

.install__close {
  position: absolute;
  top: var(--sp-1);
  right: var(--sp-1);
  width: var(--tap);
  height: var(--tap);
  padding: 0;
  border: 0;
  border-radius: var(--r-full);
  background: transparent;
  color: var(--encre-doux);
  font-size: var(--fs-base);
  line-height: 1;
  cursor: pointer;
}
.install__close:hover { background: var(--sakura-soft); color: var(--encre); }

.install__why {
  margin: 0 0 var(--sp-3);
  font-size: var(--fs-sm);
  line-height: 1.9;
  color: var(--encre-doux);
}

/* `base.css` retire les puces de toutes les listes ; ici la numérotation porte
   du sens — ce sont trois gestes à faire dans l'ordre — donc on la remet. */
.install__steps {
  margin: 0;
  padding-left: 1.5em;
  list-style: decimal;
  font-size: var(--fs-sm);
  line-height: 2.1;
  color: var(--encre);
}
.install__steps li::marker { color: var(--sakura-deep); font-weight: 700; }

/* L'icône de partage est dessinée dans le texte : elle doit s'asseoir sur la
   ligne de base comme un caractère, pas flotter au-dessus. */
.install__share {
  display: inline-block;
  vertical-align: -.3em;
  margin: 0 .15em;
  color: var(--sakura-deep);
}

/* --- Sauvegarde cloud & code de restauration ----------------------------- */

.sync {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin: 0;
  padding: var(--sp-3);
  background: var(--menthe-soft);
  border-radius: var(--r-sm);
}
.sync__ico { font-size: var(--fs-lg); line-height: 1; }
.sync__label {
  font-size: var(--fs-sm);
  font-weight: 700;
  color: var(--encre);
}
.sync__label small {
  display: block;
  font-weight: 500;
  font-size: var(--fs-xs);
  color: var(--encre-doux);
}

/* L'« あいことば » se recopie à la main depuis un écran de téléphone : chasse
   fixe pour distinguer les caractères, et de l'air entre eux. */
.passcode {
  margin: 0 0 var(--sp-3);
  padding: var(--sp-4) var(--sp-2);
  text-align: center;
  font-family: ui-monospace, 'SFMono-Regular', 'M PLUS Rounded 1c', monospace;
  font-size: var(--fs-md);
  font-weight: 700;
  letter-spacing: .08em;
  color: var(--encre);
  background: var(--citron);
  border: 2px dashed var(--sakura-deep);
  border-radius: var(--r-sm);
  user-select: all;
  overflow-wrap: anywhere;
}

.sync-warn {
  margin: var(--sp-3) 0 0;
  padding: var(--sp-3);
  font-size: var(--fs-sm);
  line-height: 1.7;
  color: var(--encre);
  background: var(--erreur-soft);
  border-radius: var(--r-sm);
}

.restore-msg {
  margin: var(--sp-3) 0 0;
  min-height: 1.4em;
  font-size: var(--fs-sm);
  font-weight: 700;
  color: var(--encre-doux);
}
.restore-msg.is-ok    { color: var(--succes); }
.restore-msg.is-error { color: var(--erreur); }

/* --- État vide ----------------------------------------------------------- */
.empty {
  text-align: center;
  padding: var(--sp-7) var(--sp-4);
  color: var(--encre-doux);
}
.empty__ico { font-size: 3rem; margin-bottom: var(--sp-3); }

/* --- 発音ラボ ------------------------------------------------------------- */
.lab-group + .lab-group {
  margin-top: var(--sp-7);
  padding-top: var(--sp-6);
  border-top: 2px dashed var(--lavande-soft);
}
.lab-group > * + * { margin-top: var(--sp-3); }

.lab-group__sub {
  font-family: var(--font-fr);
  font-size: var(--fs-sm);
  color: var(--encre-doux);
  margin-top: calc(var(--sp-2) * -1);
}

.pairs { display: grid; gap: var(--sp-3); }

/* Les deux mots gardent la même largeur : c'est la comparaison qui est
   l'objet de l'exercice, aucun des deux ne doit dominer visuellement. */
.pair {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: stretch;
  gap: var(--sp-2);
}

.pair__vs {
  align-self: center;
  font-size: var(--fs-sm);
  color: var(--encre-pale);
}

.pair__side {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  min-height: var(--tap);
  padding: var(--sp-3) var(--sp-2);
  border-radius: var(--r-md);
  border: 2px solid transparent;
  background: var(--surface);
  box-shadow: var(--sh-sm);
  cursor: pointer;
  text-align: center;
  transition: transform .12s ease, border-color .12s ease;
}
.pair__side--a { border-color: var(--lavande); }
.pair__side--b { border-color: var(--menthe); }
.pair__side:active { transform: scale(.97); }
.pair__side.is-playing { border-color: var(--sakura-deep); }

.pair__fr {
  font-family: var(--font-fr);
  font-size: var(--fs-lg);
  font-weight: 700;
  line-height: 1.2;
}
.pair__ipa  { font-family: var(--font-fr); font-size: var(--fs-xs); color: var(--encre-doux); }
.pair__kata { font-size: var(--fs-xs); color: var(--encre-pale); letter-spacing: .04em; }
.pair__ja   { font-size: var(--fs-xs); color: var(--encre-doux); margin-top: 2px; }
.pair__ico  { font-size: var(--fs-sm); opacity: .55; }

/* ==========================================================================
   じしょ — le dictionnaire
   ========================================================================== */

/* La barre reste sous l'en-tête pendant qu'on fait défiler 364 fiches : sans
   elle, corriger une recherche imposerait de remonter toute la liste. */
.dico-barre {
  position: sticky;
  top: calc(var(--header-h) + env(safe-area-inset-top));
  z-index: 10;
  /* Le champ prend toute la largeur et le filtre passe dessous : côte à côte,
     le sélecteur de chapitre mangeait assez de place pour tronquer le
     placeholder — or c'est lui qui apprend qu'on peut chercher en japonais. */
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: center;
  gap: var(--sp-2);
  margin: 0 calc(var(--sp-4) * -1);
  padding: var(--sp-3) var(--sp-4);
  background: rgb(255 249 243 / .94);
  backdrop-filter: blur(12px);
  border-bottom: 2px solid var(--sakura-soft);
}

.dico-champ { grid-column: 1 / -1; }

.dico-champ, .dico-select {
  min-height: var(--tap);
  padding: 0 var(--sp-3);
  border: 2px solid var(--sakura-soft);
  border-radius: var(--r-full);
  background: var(--surface);
  color: var(--encre);
  font-family: inherit;
  font-size: var(--fs-base);   /* 16px : en dessous, iOS zoome à la mise au point */
}
.dico-champ:focus, .dico-select:focus { outline: none; border-color: var(--sakura-deep); }
/* La croix native de `type=search` est un carré gris hors palette. */
.dico-champ::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; }
.dico-champ::placeholder { color: var(--encre-pale); }

.dico-select {
  min-height: 38px;
  max-width: 11rem;
  padding-right: var(--sp-2);
  font-size: var(--fs-sm);
}

.dico-compte {
  margin: 0;
  font-size: var(--fs-xs);
  color: var(--encre-doux);
  font-variant-numeric: tabular-nums;
}

.dico-liste { display: grid; gap: var(--sp-3); margin-top: var(--sp-4); }

.dico-lettre {
  margin: var(--sp-3) 0 0;
  font-family: var(--font-fr);
  font-size: var(--fs-sm);
  font-weight: 800;
  letter-spacing: .12em;
  color: var(--lavande-encre);
}

.dico-fiche {
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--r-md);
  background: var(--surface);
  box-shadow: var(--sh-sm);
}
.dico-fiche__tete { display: flex; align-items: center; gap: var(--sp-2); }
.dico-fiche__fr {
  font-family: var(--font-fr);
  font-size: var(--fs-md);
  font-weight: 700;
  line-height: 1.3;
  flex: 1;
}
.dico-fiche__ja  { margin: 2px 0 0; font-weight: 700; }
.dico-fiche__son { margin: 2px 0 0; font-size: var(--fs-xs); color: var(--encre-doux); }
.dico-fiche__son .dico-fiche__kata { color: var(--encre-pale); letter-spacing: .04em; }

/* Le genre est la donnée la plus coûteuse à réapprendre plus tard, et
   l'article ne le trahit pas devant une voyelle (« l'âge »). */
.dico-g {
  flex: none;
  padding: 1px var(--sp-2);
  border-radius: var(--r-full);
  font-size: 0.65rem;
  font-weight: 800;
}
.dico-g--m { background: var(--ciel);        color: var(--encre); }
.dico-g--f { background: var(--sakura-soft); color: var(--encre); }

.dico-fiche__ex {
  margin-top: var(--sp-2);
  padding: var(--sp-2) var(--sp-3);
  border-left: 3px solid var(--menthe);
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
  background: var(--menthe-soft);
}
.dico-fiche__exfr {
  display: flex; align-items: center; gap: var(--sp-2);
  margin: 0;
  font-family: var(--font-fr);
  font-size: var(--fs-sm);
  font-weight: 600;
}
.dico-fiche__exja { margin: 2px 0 0; font-size: var(--fs-xs); color: var(--encre-doux); }

.dico-fiche__ch { margin: var(--sp-2) 0 0; font-size: var(--fs-xs); }
.dico-fiche__ch a { color: var(--encre-doux); text-decoration: underline; text-underline-offset: 2px; }

/* ==========================================================================
   La porte — l'écran de déverrouillage, premier écran du site

   Le basculement se joue sur une seule classe, posée sur `<html>` par
   `js/gate.js` avant le premier peint. Volontairement écrit dans ce sens :
   sans JavaScript, ou si le script ne se charge pas, la porte reste fermée
   plutôt que de s'ouvrir en grand — c'est le seul sens acceptable pour
   l'échec d'une porte.
   ========================================================================== */
html:not(.mayu-ouvert) .app-header,
html:not(.mayu-ouvert) .app-main,
html:not(.mayu-ouvert) .tabbar,
html:not(.mayu-ouvert) .petals { display: none; }

.porte { display: none; }
html:not(.mayu-ouvert) .porte {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: grid;
  place-items: center;
  overflow-y: auto;
  padding: var(--sp-6) var(--sp-4) calc(var(--sp-6) + env(safe-area-inset-bottom));
  /* Un lever de jour plutôt que le fond du site : on n'est pas encore
     dedans, on est devant. */
  background:
    radial-gradient(ellipse 90% 55% at 50% 0%, var(--sakura-soft), transparent 72%),
    radial-gradient(ellipse 70% 45% at 15% 100%, var(--lavande-soft), transparent 70%),
    radial-gradient(ellipse 70% 40% at 90% 92%, var(--menthe-soft), transparent 72%),
    var(--creme);
}

.porte__petales { position: absolute; inset: 0; overflow: hidden; pointer-events: none; }

.porte__carte {
  position: relative;
  width: 100%;
  max-width: 340px;
  /* La marge haute réserve la place du médaillon, qui déborde du haut de la
     carte par une marge négative. Sans elle, le chat sortirait de l'écran sur
     un téléphone court. */
  margin-top: 72px;
  padding: 0 var(--sp-5) var(--sp-5);
  background: var(--surface);
  border: 2px solid var(--sakura-soft);
  border-radius: var(--r-xl);
  box-shadow: var(--sh-lg);
  text-align: center;
  animation: fade-up .5s cubic-bezier(.22,1,.36,1) both;
}
.porte__carte.is-faux { animation: porte-secousse .5s cubic-bezier(.36,.07,.19,.97) both; }

.porte__chatbox { display: grid; justify-items: center; gap: var(--sp-3); }

/* Le chat déborde du haut de la carte : posé à l'intérieur, il aurait l'air
   d'une illustration parmi d'autres — à cheval sur le bord, c'est lui qui
   tient la porte. L'anneau crème le détache du fond blanc. */
.porte__chat {
  width: 132px; height: 132px;
  margin-top: -72px;
  border-radius: 50%;
  background: radial-gradient(circle at 50% 42%, var(--blanc) 38%, var(--sakura-soft) 100%);
  box-shadow: 0 0 0 6px var(--surface), var(--sh-md);
}

.porte__bulle {
  flex: none;
  width: 100%;
  text-align: center;
  font-size: var(--fs-sm);
}
/* La bulle passe sous le chat : sa pointe doit donc viser le haut, alors que
   celle de `.bubble` vise la gauche. Les quatre bords sont réécrits, sans
   quoi il resterait deux traits du losange d'origine. */
.porte__bulle::before {
  left: 50%; top: -9px; bottom: auto;
  border: 2px solid var(--sakura-soft);
  border-right: none; border-bottom: none;
  border-radius: 4px 0 0 0;
  transform: translateX(-50%) rotate(45deg);
}

.porte__titre { display: grid; justify-items: center; gap: 1px; margin-top: var(--sp-5); }
.porte__mark  { font-size: var(--fs-lg); line-height: 1; }
.porte__titre b {
  font-family: var(--font-fr);
  font-size: var(--fs-xl);
  font-weight: 700;
  color: var(--encre);
}
.porte__titre small {
  font-size: var(--fs-xs);
  font-weight: 500;
  color: var(--encre-doux);
  letter-spacing: .14em;
}

.porte__form { display: grid; gap: var(--sp-3); margin-top: var(--sp-5); }
.porte__champbox { position: relative; }

.porte__champ {
  width: 100%;
  min-height: 52px;
  /* Rembourrage symétrique bien que l'œil ne soit que d'un côté : le texte
     tapé doit rester centré, sinon il glisse vers la gauche à mesure qu'il
     s'allonge. */
  padding: var(--sp-3) calc(var(--tap) + var(--sp-1));
  border: 2px solid var(--sakura-soft);
  border-radius: var(--r-full);
  background: var(--creme);
  font: inherit;
  font-size: var(--fs-base);        /* 16px : en dessous, iOS zoome à la mise au point */
  color: var(--encre);
  text-align: center;
  transition: border-color .18s, background .18s;
}
.porte__champ::placeholder { color: var(--encre-pale); font-size: var(--fs-sm); }
.porte__champ:focus { outline: none; border-color: var(--sakura-deep); background: var(--surface); }
.porte__champ[aria-invalid='true'] { border-color: var(--erreur); background: var(--erreur-soft); }

.porte__oeil {
  position: absolute; right: 4px; top: 50%;
  translate: 0 -50%;
  width: var(--tap); height: var(--tap);
  display: grid; place-items: center;
  font-size: var(--fs-md); line-height: 1;
  border-radius: var(--r-full);
}

.porte__entrer { display: flex; width: 100%; }

.porte__note {
  margin-top: var(--sp-4);
  font-size: var(--fs-xs);
  line-height: 1.75;
  color: var(--encre-doux);
}
