/* ───────────────────────────────────────────────
   Tokens
   ─────────────────────────────────────────────── */
:root{
  --ink:        #3B2E43;
  --ink-soft:   #6E5C72;
  --berry:      #C4497A;
  --honey:      #F4B740;
  --iris:       #E8E6FA;
  --rose:       #FBE7EF;
  --paper:      #FFFDFB;

  --frame:      clamp(1.25rem, 4vw, 3rem);
  --ease-soft:  cubic-bezier(.22,.61,.36,1);
  --ease-back:  cubic-bezier(.34,1.46,.64,1);
}

*,*::before,*::after{ box-sizing:border-box; }

html,body{ height:100%; }

body{
  margin:0;
  overflow:hidden;
  background:var(--rose);
  color:var(--ink);
  font-family:"Karla","Avenir Next",-apple-system,system-ui,sans-serif;
  font-weight:400;
  -webkit-font-smoothing:antialiased;
  text-rendering:optimizeLegibility;
  -webkit-tap-highlight-color:transparent;
  touch-action:manipulation;
}

/* ───────────────────────────────────────────────
   Le ciel : il se réchauffe à chaque clic
   ─────────────────────────────────────────────── */
.sky{
  position:fixed; inset:0; z-index:0;
  transition:background 1400ms var(--ease-soft);
}
body[data-beat="1"] .sky{
  background:radial-gradient(125% 95% at 50% 0%,   #F2EFFF 0%, #FBF2F6 52%, #FDF6F0 100%);
}
body[data-beat="2"] .sky{
  background:radial-gradient(125% 95% at 50% 12%,  #FCEBF2 0%, #FDF0E9 52%, #FFF7EE 100%);
}
body[data-beat="3"] .sky{
  background:radial-gradient(115% 85% at 50% 38%,  #FFF3D9 0%, #FDE4EE 45%, #F3E6FC 100%);
}
/* on retient son souffle : la page se calme avant la révélation */
body[data-beat="4"] .sky{
  background:radial-gradient(120% 90% at 50% 46%,  #F7F0F8 0%, #F0EAF5 55%, #F8F2F4 100%);
}
/* toujours en apnée : le ciel reste calme, à peine plus clair */
body[data-beat="5"] .sky{
  background:radial-gradient(120% 90% at 50% 40%,  #F9F3FA 0%, #F2ECF6 55%, #FAF4F5 100%);
}
/* le grand ciel du départ */
body[data-beat="6"] .sky{
  background:radial-gradient(130% 100% at 50% 22%, #EAF3FD 0%, #FBEBF2 48%, #FFF5E4 100%);
}

/* halo derrière le texte : juste de quoi rester lisible sur un cercle flou */
.halo{
  position:fixed; inset:0; z-index:2; pointer-events:none;
  background:radial-gradient(50% 36% at 50% 50%,
    rgba(255,253,251,.78) 0%,
    rgba(255,253,251,.46) 45%,
    rgba(255,253,251,0)   78%);
  transition:opacity 900ms var(--ease-soft);
}

/* ───────────────────────────────────────────────
   Décorations flottantes
   trois formes : cercle flou, arc fin, étincelle
   ─────────────────────────────────────────────── */
.floaters{ position:fixed; inset:0; z-index:1; pointer-events:none; }

.floater{
  position:absolute;
  left:var(--x); top:var(--y);
  width:var(--size);
  aspect-ratio:1;
  opacity:0;
  transform:translate(-50%,-50%) rotate(var(--r0));
  transition:opacity 1100ms var(--ease-soft) var(--in-delay, 0ms);
  will-change:transform,opacity;
}
.floater.is-in{ opacity:var(--op, .8); }

.floater svg{ display:block; width:100%; height:100%; overflow:visible; }

/* ── cercle flou : le fond de la couronne ──
   dégradé radial plutôt que filter:blur, moins coûteux en animation.
   deux couches : la teinte de base, et le miel qui prend le relais
   aux temps forts (fondu croisé, pour ne pas brunir l'iris) */
.floater--blob::before,
.floater--blob::after{
  content:"";
  position:absolute; inset:0;
  border-radius:50%;
  transition:opacity 1400ms var(--ease-soft);
}
.floater--blob::before{
  background:radial-gradient(circle at 50% 50%, var(--c) 0%, transparent 68%);
}
.floater--blob::after{
  background:radial-gradient(circle at 50% 50%, rgba(244,183,64,.40) 0%, transparent 68%);
  opacity:0;
}
.floater--blob[data-tone="0"]{ --c:rgba(155,140,224,.48); }   /* iris */
.floater--blob[data-tone="1"]{ --c:rgba(245,139,166,.40); }   /* rose */
.floater--blob[data-tone="2"]{ --c:rgba(244,183,64,.32);  }   /* miel */

body[data-beat="3"] .floater--blob::before,
body[data-beat="6"] .floater--blob::before{ opacity:.3; }
body[data-beat="3"] .floater--blob::after,
body[data-beat="6"] .floater--blob::after{ opacity:1; }

/* ── arc fin : structure sans peser ── */
.floater--arc circle{
  fill:none;
  stroke:var(--c);
  stroke-width:1.5;
  stroke-linecap:round;
}
.floater--arc[data-tone="0"]{ --c:rgba(196,73,122,.62); }    /* berry */
.floater--arc[data-tone="1"]{ --c:rgba(110,92,114,.55); }    /* ink-soft */
.floater--arc[data-tone="2"]{ --c:rgba(155,140,224,.75); }   /* iris */

/* ── étincelle : le seul vrai accent ── */
.floater--spark path{ fill:var(--c); }
.floater--spark[data-tone="0"]{ --c:var(--honey); }
.floater--spark[data-tone="1"]{ --c:var(--berry); }
.floater--spark[data-tone="2"]{ --c:var(--honey); }

.is-animated .floater--spark svg{
  animation:twinkle var(--twinkle, 4s) ease-in-out var(--delay) infinite;
}
@keyframes twinkle{
  0%,100%{ opacity:1;   transform:scale(1); }
  50%    { opacity:.45; transform:scale(.82); }
}

.is-animated .floater{
  animation:drift var(--dur) var(--ease-soft) var(--delay) infinite;
}
@keyframes drift{
  0%   { transform:translate(-50%,-50%) translate(0,0) rotate(var(--r0)); }
  50%  { transform:translate(-50%,-50%) translate(var(--dx),var(--dy)) rotate(var(--r1)); }
  100% { transform:translate(-50%,-50%) translate(0,0) rotate(var(--r0)); }
}

/* aux temps forts, les étincelles prennent toute leur lumière */
body[data-beat="3"] .floater--spark,
body[data-beat="6"] .floater--spark{ --op:1; }
body[data-beat="3"] .halo{ opacity:.82; }
body[data-beat="6"] .halo{ opacity:.88; }

/* ───────────────────────────────────────────────
   Scène et textes
   ─────────────────────────────────────────────── */
.stage{
  position:fixed; inset:0; z-index:3;
  display:grid; place-items:center;
  padding:calc(var(--frame) + env(safe-area-inset-top,0px)) var(--frame)
          calc(var(--frame) + 4.5rem + env(safe-area-inset-bottom,0px));
  pointer-events:none;
}

.beat{
  grid-area:1/1;
  max-width:22ch;
  text-align:center;
  opacity:0;
  visibility:hidden;
  transform:translateY(14px) scale(.985);
  filter:blur(6px);
  transition:
    opacity 620ms var(--ease-soft),
    transform 620ms var(--ease-soft),
    filter 620ms var(--ease-soft),
    visibility 0s linear 640ms;
}
.beat.is-active{
  opacity:1;
  visibility:visible;
  transform:none;
  filter:none;
  transition-delay:220ms, 220ms, 220ms, 0s;
}

.line{
  margin:0;
  font-family:"Fraunces",Georgia,serif;
  font-variation-settings:"SOFT" 60,"WONK" 1,"opsz" 96;
  font-weight:400;
  font-size:clamp(2rem, 6.2vw, 3.4rem);
  line-height:1.1;
  letter-spacing:-.015em;
  text-wrap:balance;
}

.sub{
  margin:1.15rem auto 0;
  max-width:30ch;
  font-size:clamp(.95rem, 2.6vw, 1.05rem);
  font-weight:300;
  line-height:1.65;
  color:var(--ink-soft);
  text-wrap:pretty;
}

/* ── le final ── */
.beat--final{ max-width:min(96vw, 34rem); }

.final{
  margin:0;
  font-family:"Fraunces",Georgia,serif;
  font-variation-settings:"SOFT" 90,"WONK" 1,"opsz" 144;
  font-weight:500;
  font-size:clamp(2.6rem, 18vw, 6rem);
  line-height:.95;
  letter-spacing:-.03em;
  color:var(--berry);
}
.final__word{
  display:block;
  opacity:0;
  transform:translateY(.35em) rotate(-2deg);
}
.final__word:nth-child(2){
  font-style:normal;
  font-size:.86em;
  transform-origin:left center;
}
.is-final .final__word{
  animation:rise 900ms var(--ease-back) forwards;
}
.is-final .final__word:nth-child(1){ animation-delay:120ms; }
.is-final .final__word:nth-child(2){ animation-delay:300ms; }
@keyframes rise{
  to{ opacity:1; transform:none; }
}

.name{
  margin:1.1rem 0 0;
  font-family:"Fraunces",Georgia,serif;
  font-variation-settings:"SOFT" 100,"WONK" 1,"opsz" 40;
  font-weight:300;
  font-size:clamp(1.25rem, 4.4vw, 1.85rem);
  letter-spacing:.06em;
  color:var(--ink);
  opacity:0;
}
.is-final .name{ animation:rise 900ms var(--ease-back) 520ms forwards; }

.sub--final{ opacity:0; }
.is-final .sub--final{ animation:rise 900ms var(--ease-soft) 720ms forwards; }

/* petit trait de miel sous le prénom */
.name::after{
  content:"";
  display:block;
  width:2.4rem; height:2px;
  margin:.9rem auto 0;
  border-radius:2px;
  background:var(--honey);
}

/* ───────────────────────────────────────────────
   La révélation : un ticket d'événement

   Minutage du final, tout est enchaîné sur .is-reveal :
     0 ms     le ticket se pose
     150 ms   « on se casse faire le nouvel an à »
     500 ms   l'avion décolle, il vole 1,1 s
     1600 ms  « Londres » atterrit          ← les confettis partent ici (script.js)
     2000 ms  les « !! »
     2150 ms  la perforation et le talon
     2350 ms  le mot du bas
   ─────────────────────────────────────────────── */
.beat--reveal{ max-width:min(96vw, 36rem); }

.ticket{
  width:min(88vw, 23rem);
  margin:0 auto;
  opacity:0;
  transform:translateY(20px) scale(.96);
  /* l'ombre est portée par le parent : en filter, elle épouse les encoches
     découpées dans le papier, ce qu'un box-shadow ne saurait pas faire */
  filter:
    drop-shadow(0 1px 1px rgba(59,46,67,.06))
    drop-shadow(0 20px 40px rgba(59,46,67,.16));
}
.is-reveal .ticket{ animation:ticket-in 820ms var(--ease-back) forwards; }
@keyframes ticket-in{
  to{ opacity:1; transform:none; }
}

.ticket__paper{
  --stub:5.4rem;    /* hauteur du talon, donc hauteur de la découpe */
  --notch:13px;

  position:relative;
  padding:1.9rem 1.5rem 0;
  border-radius:16px;
  background:var(--paper);

  /* les deux encoches : le papier est vraiment découpé, pas recouvert */
  -webkit-mask:
    radial-gradient(circle var(--notch) at 0    calc(100% - var(--stub)), transparent 98%, #000 100%),
    radial-gradient(circle var(--notch) at 100% calc(100% - var(--stub)), transparent 98%, #000 100%);
  -webkit-mask-composite:source-in;
  mask:
    radial-gradient(circle var(--notch) at 0    calc(100% - var(--stub)), transparent 98%, #000 100%),
    radial-gradient(circle var(--notch) at 100% calc(100% - var(--stub)), transparent 98%, #000 100%);
  mask-composite:intersect;
}

.reveal__up{
  margin:0;
  font-family:"Fraunces",Georgia,serif;
  font-variation-settings:"SOFT" 80,"WONK" 1,"opsz" 30;
  font-weight:300;
  font-size:clamp(.95rem, 3.4vw, 1.15rem);
  line-height:1.35;
  letter-spacing:.04em;
  color:var(--ink-soft);
  text-wrap:balance;
  opacity:0;
}
.is-reveal .reveal__up{ animation:rise 800ms var(--ease-soft) 150ms forwards; }

/* ── la bande de vol ── */
.flight{
  display:block;
  width:100%; height:auto;
  margin:.35rem 0 .1rem;
}
.flight__trail{
  fill:none;
  stroke:rgba(196,73,122,.45);
  stroke-width:1.6;
  stroke-linecap:round;
  stroke-dasharray:1 7;
}
/* le masque découvre la traînée au passage de l'avion */
.flight__reveal{
  stroke-dasharray:100;
  stroke-dashoffset:100;
}
.flight__plane{
  fill:var(--berry);
  offset-path:path("M14 47C80 15 200 13 286 31");
  offset-rotate:auto;
  offset-distance:0%;
  transform-box:fill-box;
  transform-origin:center;
  opacity:0;
}
.is-reveal .flight__reveal{ animation:draw 1100ms var(--ease-soft) 500ms forwards; }
.is-reveal .flight__plane{ animation:fly 1100ms var(--ease-soft) 500ms forwards; }
@keyframes draw{
  to{ stroke-dashoffset:0; }
}
@keyframes fly{
  0%   { offset-distance:0%;   opacity:0; }
  12%  { opacity:1; }
  88%  { opacity:1; }
  100% { offset-distance:100%; opacity:1; }
}

.reveal__city{
  margin:.1rem 0 1.5rem;
  font-family:"Fraunces",Georgia,serif;
  font-variation-settings:"SOFT" 100,"WONK" 1,"opsz" 144;
  font-weight:600;
  font-size:clamp(2rem, 10vw, 3.3rem);
  line-height:.98;
  letter-spacing:-.04em;
  color:var(--berry);
  opacity:0;
  transform:scale(.88);
}
.is-reveal .reveal__city{ animation:land 780ms var(--ease-back) 1600ms forwards; }
@keyframes land{
  to{ opacity:1; transform:none; }
}

.reveal__bang{
  display:inline-block;
  margin-left:.04em;
  color:var(--honey);
  letter-spacing:-.06em;
  opacity:0;
  transform:translateY(.2em) rotate(-6deg) scale(.7);
}
.is-reveal .reveal__bang{ animation:pop 620ms var(--ease-back) 2000ms forwards; }
@keyframes pop{
  to{ opacity:1; transform:rotate(6deg); }
}

/* ── la perforation et le talon ── */
.ticket__perf{
  position:absolute;
  left:.8rem; right:.8rem;
  bottom:var(--stub);
  border-top:1px dashed rgba(110,92,114,.4);
}

.ticket__stub{
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap:.6rem;
  height:var(--stub);
  margin:0;
  opacity:0;
}
.ticket__field{ text-align:left; }
.ticket__field dt{
  margin:0 0 .2em;
  font-size:.56rem;
  font-weight:600;
  letter-spacing:.14em;
  text-transform:uppercase;
  color:rgba(110,92,114,.7);
}
.ticket__field dd{
  margin:0;
  font-size:.78rem;
  font-weight:500;
  letter-spacing:.01em;
  color:var(--ink);
  white-space:nowrap;
}
.ticket__field dd sup{
  font-size:.62em;
  vertical-align:.45em;
}
.is-reveal .ticket__stub{ animation:rise 700ms var(--ease-soft) 2150ms forwards; }

.sub--reveal{
  margin-top:1.4rem;
  opacity:0;
}
.is-reveal .sub--reveal{ animation:rise 900ms var(--ease-soft) 2350ms forwards; }

/* ───────────────────────────────────────────────
   Invitation à toucher
   ─────────────────────────────────────────────── */
.hint{
  position:fixed; z-index:5;
  left:50%; bottom:calc(1.8rem + env(safe-area-inset-bottom,0px));
  transform:translateX(-50%);
  margin:0;
  font-size:.8rem; font-weight:400;
  letter-spacing:.09em;
  color:var(--ink-soft);
  opacity:0;
  pointer-events:none;
  transition:opacity 700ms var(--ease-soft);
}
.hint.is-shown{ opacity:.62; animation:breathe 3.4s ease-in-out 1s infinite; }
.hint.is-gone{ opacity:0; animation:none; }
@keyframes breathe{
  0%,100%{ opacity:.62; }
  50%    { opacity:.26; }
}

/* ───────────────────────────────────────────────
   Zone cliquable plein écran
   ─────────────────────────────────────────────── */
.advance{
  position:fixed; inset:0; z-index:6;
  width:100%; height:100%;
  padding:0; margin:0;
  border:0; background:transparent;
  cursor:pointer;
  font:inherit; color:inherit;
}
.advance:focus{ outline:none; }
.advance:focus-visible{
  outline:2px solid var(--berry);
  outline-offset:-10px;
  border-radius:12px;
}

.confetti{
  position:fixed; inset:0; z-index:7;
  width:100%; height:100%;
  pointer-events:none;
}

/* ───────────────────────────────────────────────
   Mobile
   ─────────────────────────────────────────────── */
@media (max-width: 640px){
  .halo{
    background:radial-gradient(64% 34% at 50% 48%,
      rgba(255,253,251,.84) 0%,
      rgba(255,253,251,.55) 48%,
      rgba(255,253,251,0)   82%);
  }
  .beat{ max-width:19ch; }
  /* les deux temps forts gardent toute la largeur : cette règle doit rester
     après .beat, sinon max-width:19ch les bride aussi */
  .beat--final,
  .beat--reveal{ max-width:96vw; }
}

@media (max-height: 520px) and (orientation: landscape){
  .line{ font-size:clamp(1.6rem, 5vw, 2.2rem); }
  .final{ font-size:clamp(2.2rem, 9vw, 3.4rem); }
  .reveal__city{ font-size:clamp(1.8rem, 7vw, 2.6rem); margin-bottom:1.1rem; }
  .flight{ margin:.15rem 0 0; }
  .sub{ margin-top:.7rem; }
}

/* ───────────────────────────────────────────────
   Mouvement réduit
   ─────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce){
  .is-animated .floater,
  .is-animated .floater--spark svg{ animation:none; }
  .floater--blob::before,
  .floater--blob::after{ transition:none; }
  .hint.is-shown{ animation:none; }
  .final__word,.name,.sub--final,
  .reveal__up,.reveal__city,.reveal__bang,.sub--reveal,
  .ticket,.ticket__stub,.flight__plane{ opacity:1; transform:none; }
  .flight__reveal{ stroke-dashoffset:0; }
  .flight__plane{ offset-distance:100%; }
  .is-final .final__word,
  .is-final .name,
  .is-final .sub--final,
  .is-reveal .reveal__up,
  .is-reveal .reveal__city,
  .is-reveal .reveal__bang,
  .is-reveal .sub--reveal,
  .is-reveal .ticket,
  .is-reveal .ticket__stub,
  .is-reveal .flight__reveal,
  .is-reveal .flight__plane{ animation:none; }
  .beat{ filter:none; transform:none; transition:opacity 300ms linear, visibility 0s linear 320ms; }
  .beat.is-active{ transition-delay:120ms, 0s; }
  .sky{ transition:background 400ms linear; }
}
