/* invit.evnt.tn - Public invitation styles */

.inv-stage {
  min-height: 100vh;
  display: flex; flex-direction: column; align-items: center; justify-content: flex-start;
  gap: 32px;
  padding: max(20px, 4vh) 16px 60px;
  perspective: 1600px;
  perspective-origin: 50% 30%;
  position: relative;
  overflow-x: hidden;
  /* Fond par défaut : crème (pas blanc) pour s'accorder avec les enveloppes/papier
   * et éviter le rectangle blanc qui apparaissait derrière les enveloppes-images. */
  background:
    radial-gradient(circle at 50% 18%, color-mix(in srgb, var(--accent, #111) 7%, transparent), transparent 32%),
    var(--card-bg, #F5F1EA);
}
/* Lorsque l'enveloppe est fermée : on centre verticalement pour montrer juste l'enveloppe */
.inv-stage:not(.opened) {
  justify-content: center;
  gap: 0;
}
.inv-stage.opened:not(.has-env-image):not(.is-flyer) {
  gap: 0;
}

/* ==========================================================================
 * RÈGLE BLINDÉE : "pas d'image enveloppe = pas d'enveloppe du tout"
 * --------------------------------------------------------------------------
 * Indépendamment de ce que renvoie render.php, si l'.inv-stage n'a pas
 * la classe .has-env-image (= aucune image d'enveloppe résolue), on cache
 * complètement la coquille CSS dorée par défaut et on force la carte à
 * être visible directement (comportement flyer-like).
 * Ce filet protège contre tout déploiement partiel du PHP ou template
 * mal configuré qui laisserait passer une enveloppe sans image.
 * ========================================================================== */
.inv-stage:not(.has-env-image):not(.is-flyer) .env-frame,
.inv-stage:not(.has-env-image):not(.is-flyer) #replayOpen,
.inv-stage:not(.has-env-image):not(.is-flyer) .env-hint {
  display: none !important;
}
.inv-stage:not(.has-env-image):not(.is-flyer) .inv-card {
  max-height: none !important;
  overflow: visible !important;
  pointer-events: auto !important;
  opacity: 1 !important;
  transform: none !important;
  animation: card-direct-fade-in .7s ease-out forwards !important;
}
.inv-stage:not(.has-env-image):not(.is-flyer) .inv-card .card-corner,
.inv-stage:not(.has-env-image):not(.is-flyer) .inv-card .card-photo,
.inv-stage:not(.has-env-image):not(.is-flyer) .inv-card .card-eyebrow,
.inv-stage:not(.has-env-image):not(.is-flyer) .inv-card .card-names,
.inv-stage:not(.has-env-image):not(.is-flyer) .inv-card .card-message,
.inv-stage:not(.has-env-image):not(.is-flyer) .inv-card .card-divider,
.inv-stage:not(.has-env-image):not(.is-flyer) .inv-card .card-detail {
  opacity: 1 !important;
  transform: none !important;
}
.inv-stage:not(.has-env-image):not(.is-flyer) .inv-card .card-corner,
.inv-stage:not(.has-env-image):not(.is-flyer) .inv-card .card-photo,
.inv-stage:not(.has-env-image):not(.is-flyer) .inv-card .card-eyebrow,
.inv-stage:not(.has-env-image):not(.is-flyer) .inv-card .card-names,
.inv-stage:not(.has-env-image):not(.is-flyer) .inv-card .card-message,
.inv-stage:not(.has-env-image):not(.is-flyer) .inv-card .card-divider,
.inv-stage:not(.has-env-image):not(.is-flyer) .inv-card .card-detail {
  animation: card-direct-stagger .55s cubic-bezier(.2,.65,.35,1) forwards !important;
  opacity: 0;
}
.inv-stage:not(.has-env-image):not(.is-flyer) .inv-card .card-eyebrow { animation-delay: .15s !important; }
.inv-stage:not(.has-env-image):not(.is-flyer) .inv-card .card-names   { animation-delay: .30s !important; }
.inv-stage:not(.has-env-image):not(.is-flyer) .inv-card .card-message { animation-delay: .45s !important; }
.inv-stage:not(.has-env-image):not(.is-flyer) .inv-card .card-divider { animation-delay: .55s !important; }
.inv-stage:not(.has-env-image):not(.is-flyer) .inv-card .card-detail:nth-of-type(1) { animation-delay: .65s !important; }
.inv-stage:not(.has-env-image):not(.is-flyer) .inv-card .card-detail:nth-of-type(2) { animation-delay: .75s !important; }
.inv-stage:not(.has-env-image):not(.is-flyer) .inv-card .card-detail:nth-of-type(3) { animation-delay: .85s !important; }
.inv-stage:not(.has-env-image):not(.is-flyer) .inv-card .card-detail:nth-of-type(n+4) { animation-delay: .95s !important; }
.inv-stage:not(.has-env-image):not(.is-flyer) .inv-extras {
  opacity: 1 !important;
  transform: none !important;
  max-height: none !important;
  pointer-events: auto !important;
  transition: none !important;
}
@keyframes card-direct-fade-in {
  from { opacity: 0; transform: translateY(16px); }
  to   { opacity: 1; transform: none; }
}
@keyframes card-direct-stagger {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: none; }
}

/* ============================================================
   ENVELOPE - pattern Saahil Jaffer
   Flap : rotateX(0 → 180deg), origin top, backface hidden
   Card : émerge de l'enveloppe (translate up + scale up), enveloppe reste visible
   ============================================================ */
.env-frame {
  position: relative;
  width: min(560px, 90vw);
  aspect-ratio: 3/2;
  cursor: pointer;
  transform-style: preserve-3d;
  z-index: 10;
  transition: transform .7s cubic-bezier(.4, 0, .2, 1) .8s,
              opacity .65s ease 1.75s,
              max-height .4s linear 2.05s,
              margin .4s linear 2.05s;
}
.env-frame:focus-visible { outline: 2px solid var(--accent, #C9A961); outline-offset: 8px; }
.env-frame:hover { transform: translateY(-5px) scale(1.01); transition: transform .3s; }

.env-kicker {
  position: absolute;
  left: 50%;
  bottom: calc(100% + 18px);
  transform: translateX(-50%);
  width: max-content;
  max-width: min(320px, 86vw);
  text-align: center;
  font-family: var(--sans);
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--card-fg, #111) 72%, transparent);
}

/* État ouvert : l'enveloppe RESTE visible (rétrécie + inclinée) à côté de la carte.
 * Cohérence avec le mode has-env-image - l'enveloppe sert de "preuve" du design.
 * Plus de disparition complète qui cassait l'UX. */
.env-frame.opened {
  cursor: default;
  pointer-events: none;
  transform: scale(.74) rotate(-3deg) translateY(14px);
  opacity: 1;
  filter: drop-shadow(0 14px 28px rgba(0,0,0,.18));
  overflow: visible;
  margin-bottom: -160px;     /* Enveloppe collée à la carte, sceau visible au milieu */
}

/* Back of envelope */
.env-back {
  position: absolute; inset: 0;
  background:
    linear-gradient(120deg, rgba(255,255,255,.22), transparent 42%),
    linear-gradient(135deg, var(--env-bg, #C9A961) 0%, var(--env-bg-dark, #9F8240) 100%);
  background-size: cover; background-position: center;
  border-radius: 6px;
  box-shadow:
    0 24px 60px rgba(0,0,0,.22),
    0 6px 20px rgba(0,0,0,.10);
  overflow: visible;
  transform-style: preserve-3d;
}

/* Liner : intérieur de l'enveloppe, visible UNIQUEMENT après ouverture du flap.
 * Z-index: derrière le flap fermé, devant le pocket. */
.env-liner {
  position: absolute;
  inset: 0;
  background: var(--env-liner, linear-gradient(135deg, #fffaf0, #f2e4c8));
  background-size: 38px 38px, cover;
  clip-path: polygon(0 0, 100% 0, 50% 50%);
  opacity: 0;
  z-index: 2;
  transition: opacity .3s .35s ease;
}
.env-frame.opened .env-liner { opacity: .92; }

/* Top flap - pattern Saahil Jaffer EXACT : 4 keyframe stops (0/33/66/100)
 * Closed = rotateX(0), open = rotateX(180deg)
 * transform-origin: center top, backface-visibility hidden */
.env-flap {
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 100%;
  background: linear-gradient(180deg, var(--env-flap, #C9A961) 0%, var(--env-flap-dark, #7A6230) 100%);
  clip-path: polygon(0 0, 100% 0, 50% 50%);
  transform-origin: center top;
  transform: rotateX(0deg);
  z-index: 5;
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
  box-shadow: 0 4px 10px rgba(0,0,0,.12);
  will-change: transform;
}
.env-frame.opened .env-flap {
  animation: flap-open 1.4s cubic-bezier(.22, .61, .36, 1) 0s forwards;
}
@keyframes flap-open {
  0%   { transform: rotateX(0deg);   }
  60%  { transform: rotateX(150deg); }
  100% { transform: rotateX(180deg); }
}

/* Tampon : posé sous le centre de l'enveloppe avec effet cire pressée */
.env-seal {
  position: absolute;
  /* Position du sceau sur l'enveloppe. left=50% = centre horizontal. La
     verticale est pilotée par --seal-top (défaut 60%) : un vrai cachet de cire
     se pose sur la pochette, sous le rabat - donc un peu sous le centre, pas
     au milieu géométrique. Surchargée par enveloppe via inline --seal-top. */
  top: var(--seal-top, 60%); left: var(--seal-left, 50%);
  transform: translate(-50%, -50%);
  /* Tampon CSS (cire colorée) : taille augmentée pour plus d'impact visuel
     dans le rendu final (avant : 48-64px → maintenant 72-100px). */
  width: clamp(72px, 18vw, 100px);
  height: clamp(72px, 18vw, 100px);
  border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-family: var(--serif-display), serif;
  font-weight: 600;
  font-size: 1.15rem;
  letter-spacing: 0;
  background: radial-gradient(circle at 35% 35%, rgba(255,255,255,.18), transparent 58%), var(--seal-color, #C9A961);
  /* Effet "cire pressée" : ombre portée nette + reflet intérieur + bord crénelé subtil */
  box-shadow:
    0 4px 10px rgba(0,0,0,.35),
    0 1px 2px rgba(0,0,0,.5),
    inset 0 2px 3px rgba(255,255,255,.32),
    inset 0 -5px 9px rgba(0,0,0,.28);
  filter: drop-shadow(0 2px 4px rgba(0,0,0,.4));
  background-position: center; background-size: cover;
  pointer-events: none;
  z-index: 6;
  transition: opacity .25s ease, transform .35s cubic-bezier(.4,0,.6,1);
}
/* Sceau : reste visible au centre de la zone de chevauchement enveloppe/carte.
 * Pas de fade - il sert de pivot visuel entre les deux éléments. */
.env-frame.opened .env-seal {
  opacity: 1;
  z-index: 20;
  /* Sceau ouvert : on garde son scale légèrement augmenté pour qu'il reste
     un point focal entre l'enveloppe et la carte. */
  transform: translate(-50%, -50%) scale(1.1) rotate(0deg);
  transition: opacity .35s ease .9s, transform .6s cubic-bezier(.4,0,.2,1) .9s;
}

/* Quand le sceau est une image PNG (cire avec transparence) :
 * on retire ENTIÈREMENT le disque coloré (radial-gradient + couleur de cire),
 * le bevel inset et le border-radius. Seule la drop-shadow de l'image reste.
 * Note : background-image est SANS !important pour que l'inline style="background-image:url(...)"
 * de render.php puisse l'emporter (sinon on perdrait l'image du tampon). */
.env-seal.env-seal-image {
  background-color: transparent !important;
  background-image: none;  /* L'inline style gagne en spécificité (1,0,0,0) */
  background-size: contain !important;
  background-repeat: no-repeat !important;
  background-position: center !important;
  border-radius: 0 !important;
  box-shadow: none !important;
  /* Tampon image PNG (admin biblio) : taille augmentée pour mieux ressortir
     dans le rendu final (avant : 56-80px → maintenant 88-128px). */
  width: clamp(88px, 22vw, 128px);
  height: clamp(88px, 22vw, 128px);
  filter: drop-shadow(0 4px 8px rgba(0,0,0,.35)) drop-shadow(0 1px 2px rgba(0,0,0,.4));
}

/* Pocket (front lip with V cut) - devant le flap fermé visuellement
 * mais doit laisser passer la carte qui émerge ; z-index 4 (sous flap) */
.env-pocket {
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 100%;
  background:
    linear-gradient(0deg, var(--env-bg, #C9A961) 0%, var(--env-bg-dark, #9F8240) 100%);
  clip-path: polygon(0 100%, 100% 100%, 100% 0, 50% 50%, 0 0);
  z-index: 4;
}

/* ============ ENVELOPPE AVEC IMAGE (variante admin uploadée) ============
 * Animation finale "courrier déballé sur la table" :
 *   - State 0 (closed) : enveloppe centrée pleine taille avec sceau
 *   - Click → sceau se brise (existant), puis :
 *   - State 1 (opened) : enveloppe se rétrécit + glisse à gauche,
 *     reste vivante avec une légère inclinaison
 *   - Carte émerge en parallèle au centre, prend la place principale
 *   - Les DEUX restent visibles côte-à-côte (l'enveloppe sert de "preuve" du design)
 */

/* ============================================================================
 * MODULE : TEXTURES PAPIER (fallback CSS quand pas d'image uploadée)
 * ============================================================================
 * Activé via la classe `.card-texture-{slug}` sur .inv-card .card-inner.
 * S'applique en background-image PAR-DESSUS la couleur de fond (--card-bg).
 * Patterns SVG inline (pas de dépendance externe - fonctionne offline).
 * ============================================================================ */

/* Note : sélecteurs simples (sans .card-inner ancestre) pour que les classes
 * fonctionnent ÉGALEMENT sur les vignettes du picker (qui n'ont pas .card-inner). */

.card-texture-none { /* pas de texture, fond uni */ }

/* IMPORTANT : tous les .card-texture-* doivent forcer background-repeat: repeat
 * et écraser le no-repeat hérité de .card-inner - sinon la texture s'affiche
 * uniquement dans une petite zone (rectangle grey au centre du papier blanc). */

/* === TEXTURES NEUTRES ===========================================================
 * IMPORTANT : ces textures sont uniquement des PATTERNS (noir/transparent
 * à très faible opacité). La COULEUR du papier vient de --card-bg (choix user).
 * La texture se POSE dessus sans imposer de teinte propre.
 * ============================================================================= */

/* === Textures : OVERLAY via ::before pour NE PAS écraser le background-image
   du papier (admin/JS) qui est posé en inline style.
   Avant : background-image !important sur l'élément lui-même → la texture noise
   écrasait l'image variant du papier (ex: ourson → ciel/montgolfière invisible).
   Maintenant : pseudo ::before absolu en superposition. */
.card-texture-paper,
.card-texture-cardboard,
.card-texture-linen,
.card-texture-old-paper {
  position: relative;
}
.card-texture-paper > *,
.card-texture-cardboard > *,
.card-texture-linen > *,
.card-texture-old-paper > * {
  position: relative;
  z-index: 2;
}
.card-texture-paper::before,
.card-texture-cardboard::before,
.card-texture-linen::before,
.card-texture-old-paper::before {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 1;
  border-radius: inherit;
}

/* === Lisse (défaut) - grain papier mat ultra-fin */
.card-texture-paper::before {
  background-image:
    url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='400' height='400'><filter id='p'><feTurbulence type='fractalNoise' baseFrequency='1.4' numOctaves='2' stitchTiles='stitch' seed='3'/><feColorMatrix values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .10 0'/></filter><rect width='100%25' height='100%25' filter='url(%23p)'/></svg>");
  background-size: 400px 400px;
  background-repeat: repeat;
}

/* === Coton - fibres irrégulières horizontales + grain */
.card-texture-cardboard::before {
  background-image:
    url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='480' height='480'><filter id='c1'><feTurbulence type='fractalNoise' baseFrequency='0.04 0.7' numOctaves='2' stitchTiles='stitch' seed='5'/><feColorMatrix values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .09 0'/></filter><rect width='100%25' height='100%25' filter='url(%23c1)'/></svg>"),
    url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='240' height='240'><filter id='c2'><feTurbulence type='fractalNoise' baseFrequency='2.5' numOctaves='1' stitchTiles='stitch' seed='7'/><feColorMatrix values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .06 0'/></filter><rect width='100%25' height='100%25' filter='url(%23c2)'/></svg>");
  background-size: 480px 480px, 240px 240px;
  background-repeat: repeat, repeat;
}

/* === Lin - trame textile tissée */
.card-texture-linen::before {
  background-image:
    repeating-linear-gradient(45deg, transparent 0 2px, rgba(0,0,0,.045) 2px 3px, transparent 3px 5px),
    repeating-linear-gradient(-45deg, transparent 0 2px, rgba(0,0,0,.045) 2px 3px, transparent 3px 5px),
    radial-gradient(circle at 1.5px 1.5px, rgba(0,0,0,.04) 1.2px, transparent 1.4px),
    url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='280' height='280'><filter id='l'><feTurbulence type='fractalNoise' baseFrequency='2.0' numOctaves='1' stitchTiles='stitch' seed='2'/><feColorMatrix values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .06 0'/></filter><rect width='100%25' height='100%25' filter='url(%23l)'/></svg>");
  background-size: 6px 6px, 6px 6px, 4px 4px, 280px 280px;
  background-repeat: repeat, repeat, repeat, repeat;
}

/* === Vélin (granuleux avec taches naturelles) */
.card-texture-old-paper::before {
  background-image:
    radial-gradient(ellipse at center, transparent 55%, rgba(0,0,0,.06) 100%),
    radial-gradient(ellipse 60px 40px at 18% 22%, rgba(0,0,0,.06) 0%, transparent 70%),
    radial-gradient(ellipse 80px 50px at 82% 75%, rgba(0,0,0,.05) 0%, transparent 65%),
    radial-gradient(ellipse 50px 35px at 65% 28%, rgba(0,0,0,.04) 0%, transparent 70%),
    radial-gradient(ellipse 40px 30px at 28% 78%, rgba(0,0,0,.05) 0%, transparent 65%),
    url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='420' height='420'><filter id='o'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3' stitchTiles='stitch' seed='8'/><feColorMatrix values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .12 0'/></filter><rect width='100%25' height='100%25' filter='url(%23o)'/></svg>");
  background-size: 100% 100%, auto, auto, auto, auto, 420px 420px;
  background-repeat: no-repeat, no-repeat, no-repeat, no-repeat, no-repeat, repeat;
}

/* ============================================================================
 * MODULE : ENVELOPE-IMAGE PREVIEW
 * ============================================================================
 * Activé via la classe `.has-env-image` (alias propres : .env-photo, .env-photo--big,
 * .env-photo--overlap, .preview-envelope-lg, .final-envelope-overlap).
 * Couvre TOUT le rendu de l'enveloppe quand l'admin a uploadé un PNG (ex: Mickey,
 * Spiderman, Ballon, etc.) au lieu d'utiliser l'enveloppe CSS générique.
 *
 * Effets :
 *   1. Cache les calques flap/pocket/liner (l'image les contient déjà)
 *   2. Stage transparent (pas de rectangle noir/cream derrière le PNG transparent)
 *   3. Frame sans bordure ni shadow inline (drop-shadow doux via filter)
 *   4. Layout row sur desktop, colonne sur mobile, overlap enveloppe+carte
 *   5. Animation card-reveal douce
 * ============================================================================
 */

/* 1. Calques cachés */
.has-env-image .env-flap,
.has-env-image .env-pocket,
.has-env-image .env-liner,
.env-photo .env-flap,
.env-photo .env-pocket,
.env-photo .env-liner {
  display: none !important;
}

/* Enveloppe-image : look propre, plein cadre, ombre douce */
.has-env-image .env-back {
  /* L'enveloppe doit être ENTIÈREMENT visible (pas de crop). `contain` fait tenir
     l'image complète dans le cadre quel que soit son ratio - aucun bord rogné.
     (Avant : `118% auto` zoomait et rognait les bords de l'image.) */
  background-size: contain !important;
  background-position: center !important;
  background-repeat: no-repeat !important;
  /* Pas de border-radius ni de box-shadow → l'image uploadée garde son look natif */
  border-radius: 0 !important;
  box-shadow: none !important;
  will-change: transform, opacity;
  background-color: transparent !important;
}

/* Composition SUPERPOSÉE : la carte reste CENTRÉE et DEVANT (z-index 30),
 * légèrement décalée vers la droite ; l'enveloppe passe DERRIÈRE, décalée vers
 * la gauche (elle dépasse). On empile en colonne (carte centrée dans le flux),
 * l'enveloppe est positionnée en absolu derrière sur grand écran (cf. media). */
.has-env-image.inv-stage.opened {
  flex-direction: column !important;
  flex-wrap: nowrap !important;
  align-items: center !important;
  justify-content: center !important;
  gap: clamp(8px, 2vw, 20px) !important;
}
/* inv-extras et confetti : pleine largeur sous la composition. En COLONNE,
   flex-basis pilote la HAUTEUR → on utilise flex:0 0 auto + width:100%. */
.has-env-image.inv-stage.opened > .inv-extras,
.has-env-image.inv-stage.opened > .confetti-burst {
  flex: 0 0 auto !important;
  width: 100% !important;
  margin-top: 24px;
}
.has-env-image.inv-stage.opened > .inv-extras {
  /* Centre les inv-extras sous la composition enveloppe+carte */
  align-self: center;
  display: flex;
  flex-direction: column;
  align-items: center;
}

/* Override les anciennes règles qui faisaient disparaître l'enveloppe complètement.
 * On la rétrécit + tilt, mais elle reste TOUJOURS visible. */
.has-env-image .env-frame {
  transition:
    transform .9s cubic-bezier(.4, 0, .2, 1),
    opacity .55s ease;
  width: min(500px, 86vw);
  flex: 0 0 auto;
  background: transparent !important;
  border: 0 !important;
  box-shadow: none !important;
  /* Drop-shadow douce uniquement (pas de halo blanc) */
  filter: drop-shadow(0 18px 36px rgba(0,0,0,.18));
}
.has-env-image .env-frame.opened {
  /* CRITIQUE : on annule les valeurs cassantes de l'animation par défaut */
  opacity: 1 !important;
  max-height: none !important;
  margin: 0 !important;     /* Pas de chevauchement : la carte (z-index 30) ne doit
                               PAS recouvrir l'enveloppe → enveloppe 100% visible */
  pointer-events: auto;
  cursor: default;
  transform: scale(.96) rotate(-3deg) translateY(0) !important;
  filter: drop-shadow(0 14px 28px rgba(0,0,0,.18));
}
.has-env-image .env-frame.opened:hover {
  transform: scale(.98) rotate(-2deg) translateY(-4px) !important;
}

/* Carte : flux normal (flex item), fade-in + zoom propre, sans translate négatif.
   DEVANT l'enveloppe (z-index 30) et centrée. */
.has-env-image.inv-stage.opened .inv-card {
  /* Reset les transformations héritées de l'animation par défaut */
  transform: none;
  max-height: none;
  overflow: visible;
  pointer-events: auto;
  flex: 0 1 auto;
  position: relative;
  z-index: 30;
  animation: env-img-card-reveal 1.05s cubic-bezier(.2, .85, .25, 1) .25s forwards;
}
@keyframes env-img-card-reveal {
  0% {
    opacity: 0;
    transform: translateY(28px) scale(.88);
  }
  55% {
    opacity: 1;
    transform: translateY(-3px) scale(1.015);
  }
  100% {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

/* Hint "Ouvrir l'invitation ↓" reste valide */

/* Grand écran : composition SUPERPOSÉE. La carte reste CENTRÉE dans le flux ;
 * l'enveloppe passe en ABSOLU, DERRIÈRE (z-index 1), bien décalée vers la
 * GAUCHE de sorte que son centre (et donc le sceau) dépasse à gauche de la
 * carte au lieu d'être masqué derrière. */
@media (min-width: 821px) {
  .has-env-image .env-frame.opened {
    position: absolute !important;
    z-index: 1 !important;
    top: 48% !important;
    left: 50% !important;
    width: min(450px, 43vw) !important;
    margin: 0 !important;
    transform: translate(-50%, -50%) translateX(-82%) rotate(-6deg) !important;
    filter: drop-shadow(0 22px 40px rgba(0,0,0,.22));
    transition: transform .9s cubic-bezier(.4, 0, .2, 1), opacity .55s ease;
  }
  .has-env-image .env-frame.opened:hover {
    transform: translate(-50%, -50%) translateX(-82%) rotate(-3deg) translateY(-6px) !important;
  }
}

/* Tablette / mobile : composition SUPERPOSÉE (comme desktop) - la carte vient
   se POSITIONNER SUR l'enveloppe après le clic, au lieu de s'empiler en dessous.
   L'enveloppe passe en absolu DERRIÈRE (z-index 1), légèrement remontée et
   inclinée ; la carte reste dans le flux, centrée, DEVANT (z-index 30). */
@media (max-width: 820px) {
  .has-env-image.inv-stage.opened {
    flex-direction: column !important;
    justify-content: center !important;
    position: relative !important;
  }
  .has-env-image .env-frame.opened {
    position: absolute !important;
    z-index: 1 !important;
    top: 46% !important;
    left: 50% !important;
    width: min(330px, 72vw) !important;
    margin: 0 !important;
    transform: translate(-50%, -50%) rotate(-4deg) !important;
    filter: drop-shadow(0 18px 34px rgba(0,0,0,.22));
    transition: transform .8s cubic-bezier(.4, 0, .2, 1), opacity .5s ease;
  }
  .has-env-image .env-frame.opened:hover {
    transform: translate(-50%, -50%) rotate(-2deg) translateY(-5px) !important;
  }
  /* La carte se pose PAR-DESSUS l'enveloppe (translateY part de plus bas
     pour donner l'impression de sortir de l'enveloppe et de venir s'y poser). */
  @keyframes env-img-card-reveal {
    0%   { opacity: 0; transform: translateY(44px) scale(.86); }
    55%  { opacity: 1; transform: translateY(-4px) scale(1.015); }
    100% { opacity: 1; transform: translateY(0) scale(1); }
  }
}

/* Reduced motion : pas d'animation, juste un fade simple */
@media (prefers-reduced-motion: reduce) {
  .has-env-image .env-frame,
  .has-env-image.inv-stage.opened .inv-card {
    animation: none !important;
    transition: opacity .3s ease !important;
  }
}

/* Open hint */
.env-hint {
  margin-top: 14px;
  font-family: var(--sans);
  color: var(--ink);
  font-size: .82rem;
  font-weight: 600;
  letter-spacing: .04em;
  white-space: nowrap;
  animation: env-pulse 2s infinite;
  transition: opacity .4s;
}
.inv-stage.opened .env-hint { opacity: 0; pointer-events: none; }
@keyframes env-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .55; } }

/* === Backward-compat: keep old .envelope-wrap rules so any old refs still work === */
.envelope-wrap {
  position: relative;
  width: min(420px, 88vw);
  aspect-ratio: 3/2;
  cursor: pointer;
  transition: opacity .8s, transform .8s;
}
.envelope-wrap.opened { pointer-events: none; opacity: 0; transform: scale(.85); }

.envelope {
  position: absolute; inset: 0;
  background: var(--env-bg, #E5D4A1);
  border-radius: 6px;
  box-shadow: 0 30px 80px rgba(0,0,0,.25), inset 0 0 60px rgba(0,0,0,.05);
  overflow: hidden;
}

/* Front body (where address would be) */
.envelope-body {
  position: absolute; inset: 0;
  background:
    linear-gradient(135deg, var(--env-bg, #E5D4A1), var(--env-bg-dark, #C9A961));
  display: flex; align-items: center; justify-content: center;
  flex-direction: column;
  z-index: 1;
}
.envelope-body .seal {
  width: 64px; height: 64px; border-radius: 50%;
  background: var(--env-seal, #1A2B4A);
  display: flex; align-items: center; justify-content: center;
  color: var(--env-seal-fg, #C9A961);
  font-family: var(--serif-display); font-size: 1.4rem;
  box-shadow: 0 4px 16px rgba(0,0,0,.2);
  margin-bottom: 12px;
}
.envelope-body .hint {
  font-family: var(--serif); font-style: italic;
  color: var(--env-text, #1A2B4A);
  font-size: .95rem;
  letter-spacing: .04em;
}

/* Triangle flap - top of envelope */
.envelope-flap {
  position: absolute; inset: 0;
  background: linear-gradient(135deg, var(--env-flap, #C9A961), var(--env-flap-dark, #9F8240));
  clip-path: polygon(0 0, 100% 0, 50% 60%);
  transform-origin: top center;
  transition: transform 1.2s cubic-bezier(.7,.05,.2,1);
  z-index: 3;
  box-shadow: 0 4px 12px rgba(0,0,0,.15);
}
.envelope-wrap.opened .envelope-flap { transform: rotateX(180deg); }

/* Letter inside */
.envelope-letter {
  position: absolute; inset: 8% 6% 12%;
  background: var(--cream);
  border-radius: 4px;
  z-index: 2;
  transform: translateY(0);
  transition: transform 1.2s .3s cubic-bezier(.7,.05,.2,1);
  box-shadow: 0 4px 16px rgba(0,0,0,.1);
}
.envelope-wrap.opened .envelope-letter { transform: translateY(-30%); }

.envelope-cta {
  position: absolute; bottom: -56px; left: 50%; transform: translateX(-50%);
  font-family: var(--serif); font-style: italic;
  color: var(--ink); font-size: 1rem;
  white-space: nowrap;
  animation: pulse 2s infinite;
}
@keyframes pulse { 0%,100% { opacity: 1; } 50% { opacity: .6; } }

/* ====== ENVELOPE IMAGE MODE (when {slug}-envelope.jpg is provided) ====== */
.envelope-wrap .env-image {
  width: 100%; height: 100%;
  object-fit: contain;
  border-radius: 6px;
  user-select: none; -webkit-user-drag: none;
  filter: drop-shadow(0 30px 60px rgba(0,0,0,.25));
  transition: transform .9s cubic-bezier(.7,.05,.2,1), opacity .9s;
}
.envelope-wrap .env-hint {
  position: absolute; bottom: -56px; left: 50%; transform: translateX(-50%);
  font-family: var(--serif); font-style: italic;
  color: var(--ink); font-size: 1rem;
  white-space: nowrap;
  animation: pulse 2s infinite;
  opacity: .85;
}
.envelope-wrap.opened .env-image { transform: scale(.6) translateY(-30%); opacity: 0; }
.envelope-wrap.opened .env-hint  { opacity: 0; transition: opacity .3s; }
/* When an envelope image is used, re-shape the wrap to its natural ratio */
.has-env-image .envelope-wrap { aspect-ratio: 3/2; }

/* ====== CARD - émerge de l'enveloppe (pattern Saahil Jaffer) ======
 * Fermée : max-height 0 (pas d'espace dans le layout) + opacity 0.
 * Ouverte : on libère la hauteur ET on lance @keyframes card-emerge :
 *   la carte commence translatée vers le haut + scalée petite (donc
 *   visuellement dans la position de l'enveloppe), puis glisse vers
 *   sa position naturelle en grandissant. Z-index 30 pour passer devant
 *   le flap qui s'est ouvert.
 */
.inv-card {
  position: relative;
  z-index: 30;
  width: min(560px, 96vw);
  opacity: 0;
  max-height: 0;
  overflow: hidden;
  pointer-events: none;
  transform-origin: 50% 100%;
  will-change: transform, opacity;
  transition: max-height 0s .35s, overflow 0s .35s;
}
/* === Animation "papier qui sort de l'enveloppe - du BAS vers le HAUT" ===
 * Pattern :
 *   - State initial : carte cachée, complètement à l'intérieur de l'enveloppe
 *     (translateY 90% → carte en bas, hors-vue derrière l'enveloppe)
 *   - Le sceau se brise + le flap s'ouvre
 *   - La carte glisse VERS LE HAUT en sortant lentement de l'enveloppe
 *   - Position finale : carte au-dessus de l'enveloppe (translateY négatif)
 *
 * Pas de rotation, juste une translation verticale propre + scale.
 */
@keyframes card-slide-out {
  0% {
    transform: translateY(90%) scale(.78);
    opacity: 0;
    z-index: 1;
  }
  15% {
    opacity: 1;
    z-index: 25;
  }
  60% {
    transform: translateY(20%) scale(.92);
    z-index: 30;
  }
  85% {
    transform: translateY(2%) scale(1);
    z-index: 30;
  }
  100% {
    transform: translateY(0) scale(1);
    opacity: 1;
    z-index: 30;
  }
}
.inv-stage.opened .inv-card {
  max-height: none;
  overflow: visible;
  pointer-events: auto;
  animation: card-slide-out 1.8s 1.0s cubic-bezier(.16, 1, .3, 1) forwards;
}

/* ====== Stagger texte (pattern Pinterest "Golden floral") ======
 * Une fois la carte révélée, chaque ligne apparaît en cascade :
 * eyebrow → noms → divider → message → date → lieu → dress code → RSVP.
 * Délai cumulé depuis le début de l'ouverture : flap (1.4s) + slide (1.8s) = 2.8s → on commence à 2.6s.
 * Stagger 180ms entre éléments, fade-in + remontée de 8px. */
@keyframes card-stagger-in {
  0%   { opacity: 0; transform: translateY(10px); }
  100% { opacity: 1; transform: translateY(0); }
}
.inv-stage.opened .inv-card .card-corner,
.inv-stage.opened .inv-card .card-photo,
.inv-stage.opened .inv-card .card-eyebrow,
.inv-stage.opened .inv-card .card-names,
.inv-stage.opened .inv-card .card-message,
.inv-stage.opened .inv-card .card-divider,
.inv-stage.opened .inv-card .card-detail {
  opacity: 0;
  animation: card-stagger-in .55s cubic-bezier(.2, .65, .35, 1) forwards;
}
.inv-stage.opened .inv-card .card-corner    { animation-delay: 1.45s; }
.inv-stage.opened .inv-card .card-photo     { animation-delay: 1.50s; }
.inv-stage.opened .inv-card .card-eyebrow   { animation-delay: 1.55s; }
.inv-stage.opened .inv-card .card-names     { animation-delay: 1.70s; }
.inv-stage.opened .inv-card .card-message   { animation-delay: 1.85s; }
.inv-stage.opened .inv-card .card-divider   { animation-delay: 1.95s; transform-origin: center; }
.inv-stage.opened .inv-card .card-detail:nth-of-type(1) { animation-delay: 2.05s; }
.inv-stage.opened .inv-card .card-detail:nth-of-type(2) { animation-delay: 2.15s; }
.inv-stage.opened .inv-card .card-detail:nth-of-type(3) { animation-delay: 2.25s; }
.inv-stage.opened .inv-card .card-detail:nth-of-type(4) { animation-delay: 2.35s; }
.inv-stage.opened .inv-card .card-detail:nth-of-type(5) { animation-delay: 2.45s; }
.inv-stage.opened .inv-card .card-detail:nth-of-type(n+6) { animation-delay: 2.55s; }
.inv-stage.is-flyer {
  justify-content: flex-start;
  gap: 18px;
}
.inv-stage.is-flyer .inv-card {
  max-height: none;
  overflow: visible;
  pointer-events: auto;
  width: min(640px, 96vw);
  position: relative;
  /* Reveal scan : opacité + clip-path gauche→droite + léger zoom. */
  animation: flyer-reveal 1.25s cubic-bezier(.2, .8, .2, 1) both;
}
@keyframes flyer-reveal {
  0%   { opacity: 0; transform: scale(1.06); clip-path: inset(0 100% 0 0); }
  55%  { opacity: 1; clip-path: inset(0 0 0 0); }
  100% { opacity: 1; transform: scale(1); clip-path: inset(0 0 0 0); }
}
/* Ligne de scan dorée qui sweep gauche→droite pendant la révélation */
.inv-stage.is-flyer .inv-card::before {
  content: '';
  position: absolute;
  top: 0; bottom: 0; left: -25%;
  width: 25%;
  background: linear-gradient(90deg, transparent 0%, rgba(255,255,255,.55) 45%, rgba(212,175,55,.35) 55%, transparent 100%);
  filter: blur(10px);
  pointer-events: none;
  z-index: 4;
  animation: flyer-scan 1.4s cubic-bezier(.2, .8, .2, 1) .15s both;
}
@keyframes flyer-scan {
  0%   { left: -25%; opacity: 0; }
  15%  { opacity: 1; }
  85%  { opacity: 1; }
  100% { left: 100%; opacity: 0; }
}
.inv-stage.is-flyer .card-inner {
  /* Les flyers gardent leur propre format (variable) - pas de ratio carte imposé. */
  aspect-ratio: auto;
  min-height: min(860px, calc(100vh - 160px));
}
.inv-stage.is-flyer .inv-card.flyer-image-only .card-inner {
  min-height: 0;
  padding: 0;
  background: transparent !important;
  border: 0;
  box-shadow: 0 30px 90px rgba(0,0,0,.16);
  overflow: hidden;
}
/* Mode "affiche" (card_image_only) : carte = image complète, sans texte, MAIS
 * l'enveloppe est conservée (pas en contexte is-flyer). Même rendu image que le
 * flyer image-only. */
.inv-card.card-affiche .card-inner {
  min-height: 0;
  padding: 0;
  background: transparent !important;
  border: 0 !important;
  box-shadow: 0 30px 90px rgba(0,0,0,.16) !important;
  overflow: hidden;
}
.inv-card.card-affiche .flyer-art { width: 100%; height: auto; display: block; border-radius: inherit; }
.flyer-art {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 10px;
  /* Ken Burns subtil continu après la révélation - donne vie au flyer */
  animation: flyer-ken-burns 14s ease-in-out 1.6s infinite alternate;
  transform-origin: center;
}
@keyframes flyer-ken-burns {
  from { transform: scale(1); }
  to   { transform: scale(1.035); }
}
.inv-stage.is-flyer .card-eyebrow,
.inv-stage.is-flyer .card-divider {
  display: none;
}

/* ====== Flyer editorial refresh ======
 * Les flyers texte avaient tendance a paraitre "gabarit Canva":
 * gros titre lourd, petits textes trop faibles et peu de respiration.
 * Ici on rapproche le rendu d'une vraie carte premium avec une colonne
 * centrale plus nette, des micro-labels plus soignes et moins d'elements
 * parasites dans les coins. */
.inv-stage.is-flyer .inv-card:not(.flyer-image-only) .card-inner {
  min-height: min(920px, calc(100vh - 148px));
  padding:
    clamp(40px, 7vw, 62px)
    clamp(26px, 6vw, 58px)
    clamp(44px, 8vw, 72px);
  border-radius: 18px;
  overflow: hidden;
  box-shadow:
    0 36px 90px rgba(0,0,0,.14),
    0 12px 34px rgba(0,0,0,.08),
    inset 0 0 0 1px rgba(255,255,255,.58);
}
.inv-stage.is-flyer .inv-card:not(.flyer-image-only) .card-inner::after {
  display: none;
}
.inv-stage.is-flyer .inv-card:not(.flyer-image-only) .card-inner::before {
  content: '';
  position: absolute;
  inset: 18px;
  width: auto;
  height: auto;
  left: 18px;
  right: 18px;
  top: 18px;
  bottom: 18px;
  transform: none;
  border-radius: 14px;
  border: 1px solid color-mix(in srgb, var(--card-fg, #1A2B4A) 12%, transparent);
  background:
    linear-gradient(180deg, rgba(255,255,255,.22), rgba(255,255,255,.04) 22%, transparent 44%),
    radial-gradient(circle at top center, color-mix(in srgb, var(--accent, #C9A961) 14%, transparent), transparent 34%);
  pointer-events: none;
  z-index: 0;
}
.inv-stage.is-flyer .inv-card:not(.flyer-image-only) .card-corner-tl,
.inv-stage.is-flyer .inv-card:not(.flyer-image-only) .card-corner-tr {
  display: none;
}
.inv-stage.is-flyer .inv-card:not(.flyer-image-only) .card-eyebrow {
  display: block;
  position: relative;
  z-index: 2;
  margin: 0 auto 18px;
  font-family: var(--sans);
  font-style: normal;
  font-size: .72rem;
  font-weight: 600;
  letter-spacing: .26em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--card-fg, #1A2B4A) 55%, transparent);
}
.inv-stage.is-flyer .inv-card:not(.flyer-image-only) .card-names {
  position: relative;
  z-index: 2;
  max-width: 9ch;
  margin: 0 auto 14px;
  font-size: clamp(2.65rem, 7.4vw, 4.4rem);
  line-height: .94;
  letter-spacing: -.03em;
  text-wrap: balance;
  color: var(--card-fg, #1A2B4A);
  text-shadow: 0 2px 10px rgba(255,255,255,.15);
}
.inv-stage.is-flyer .inv-card:not(.flyer-image-only) .card-host {
  margin: 0 0 16px;
  font-size: 1rem;
}
.inv-stage.is-flyer .inv-card:not(.flyer-image-only) .card-message {
  position: relative;
  z-index: 2;
  max-width: 30ch;
  margin: 0 auto 34px;
  font-size: 1.02rem;
  line-height: 1.7;
  color: color-mix(in srgb, var(--card-fg, #1A2B4A) 76%, transparent);
}
.inv-stage.is-flyer .inv-card:not(.flyer-image-only) .card-divider {
  display: block;
  width: 68px;
  margin: 0 auto 34px;
  opacity: .26;
}
.inv-stage.is-flyer .inv-card:not(.flyer-image-only) .card-detail {
  position: relative;
  z-index: 2;
  max-width: 31ch;
  margin: 0 auto 26px !important;
}
.inv-stage.is-flyer .inv-card:not(.flyer-image-only) .card-detail .label {
  margin-bottom: 10px;
  font-family: var(--sans);
  font-style: normal;
  font-size: .7rem;
  font-weight: 700;
  letter-spacing: .22em;
  color: color-mix(in srgb, var(--card-fg, #1A2B4A) 48%, transparent);
}
.inv-stage.is-flyer .inv-card:not(.flyer-image-only) .card-detail .value {
  font-size: clamp(1.28rem, 3.2vw, 1.78rem);
  line-height: 1.24 !important;
}
.inv-stage.is-flyer .inv-card:not(.flyer-image-only) .card-detail .muted {
  font-size: .98rem;
  line-height: 1.55;
  color: color-mix(in srgb, var(--card-fg, #1A2B4A) 70%, transparent);
}
.inv-stage.is-flyer .inv-card:not(.flyer-image-only) .card-detail-date .card-date-box {
  display: inline-flex;
  flex-direction: column;
  gap: 8px;
  min-width: min(100%, 280px);
  padding: 18px 28px;
  border-radius: 18px;
  border: 1px solid color-mix(in srgb, var(--card-fg, #1A2B4A) 10%, transparent);
  background:
    linear-gradient(180deg, rgba(255,255,255,.54), rgba(255,255,255,.24)),
    color-mix(in srgb, var(--accent, #C9A961) 8%, transparent);
  box-shadow: 0 12px 28px rgba(0,0,0,.06);
}
.inv-stage.is-flyer .inv-card:not(.flyer-image-only) .card-rsvp-row {
  max-width: 31ch;
  margin: 34px auto 0 !important;
  padding-top: 18px;
  border-top: 1px solid color-mix(in srgb, var(--card-fg, #1A2B4A) 10%, transparent);
}
.inv-stage.is-flyer .inv-card:not(.flyer-image-only) .card-corner-bl,
.inv-stage.is-flyer .inv-card:not(.flyer-image-only) .card-corner-br {
  font-family: var(--sans);
  font-style: normal;
  font-size: .72rem;
  font-weight: 600;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--card-fg, #1A2B4A) 56%, transparent);
}

@media (max-width: 640px) {
  .inv-stage.is-flyer .inv-card:not(.flyer-image-only) .card-inner {
    min-height: min(820px, calc(100vh - 122px));
    padding:
      clamp(34px, 8vw, 44px)
      clamp(20px, 5vw, 28px)
      clamp(36px, 9vw, 50px);
  }
  .inv-stage.is-flyer .inv-card:not(.flyer-image-only) .card-inner::before {
    inset: 12px;
  }
  .inv-stage.is-flyer .inv-card:not(.flyer-image-only) .card-eyebrow {
    margin-bottom: 14px;
    font-size: .66rem;
    letter-spacing: .2em;
  }
  .inv-stage.is-flyer .inv-card:not(.flyer-image-only) .card-names {
    max-width: 11ch;
    font-size: clamp(2.2rem, 11.2vw, 3.3rem);
    line-height: .98;
  }
  .inv-stage.is-flyer .inv-card:not(.flyer-image-only) .card-message {
    max-width: 26ch;
    margin-bottom: 28px;
    font-size: .95rem;
  }
  .inv-stage.is-flyer .inv-card:not(.flyer-image-only) .card-detail {
    max-width: 100%;
    margin-bottom: 22px !important;
  }
  .inv-stage.is-flyer .inv-card:not(.flyer-image-only) .card-detail .value {
    font-size: clamp(1.14rem, 5.6vw, 1.5rem);
  }
  .inv-stage.is-flyer .inv-card:not(.flyer-image-only) .card-detail-date .card-date-box {
    width: 100%;
    padding: 16px 18px;
    border-radius: 16px;
  }
  .inv-stage.is-flyer .inv-card:not(.flyer-image-only) .card-rsvp-row {
    margin-top: 28px !important;
    padding-top: 14px;
  }
}
/* Accessibilité : pas d'anim continue si l'user le demande */
@media (prefers-reduced-motion: reduce) {
  .inv-stage.is-flyer .inv-card { animation: none; clip-path: none; opacity: 1; transform: none; }
  .inv-stage.is-flyer .inv-card::before { display: none; }
  .flyer-art { animation: none; }
}

/* ====== Mobile : carte mise en avant, animation raccourcie ======
 * Sur mobile, on garde la séquence enveloppe → ouverture → carte mais en
 * timings divisés par ~2. La carte prend toute la largeur (max 96vw) une
 * fois sortie pour rester l'élément central. Stagger conservé mais accéléré. */
@media (max-width: 640px) {
  .env-frame.opened .env-flap {
    animation-duration: .9s;
  }
  .inv-stage.opened .inv-card {
    animation-duration: 1.2s;
    animation-delay: .65s;
    width: min(420px, 96vw);
  }
  .inv-stage.opened .inv-card .card-corner    { animation-delay: .95s; }
  .inv-stage.opened .inv-card .card-photo     { animation-delay: 1.00s; }
  .inv-stage.opened .inv-card .card-eyebrow   { animation-delay: 1.05s; }
  .inv-stage.opened .inv-card .card-names     { animation-delay: 1.15s; }
  .inv-stage.opened .inv-card .card-message   { animation-delay: 1.25s; }
  .inv-stage.opened .inv-card .card-divider   { animation-delay: 1.35s; }
  .inv-stage.opened .inv-card .card-detail:nth-of-type(1) { animation-delay: 1.45s; }
  .inv-stage.opened .inv-card .card-detail:nth-of-type(2) { animation-delay: 1.55s; }
  .inv-stage.opened .inv-card .card-detail:nth-of-type(3) { animation-delay: 1.65s; }
  .inv-stage.opened .inv-card .card-detail:nth-of-type(4) { animation-delay: 1.75s; }
  .inv-stage.opened .inv-card .card-detail:nth-of-type(5) { animation-delay: 1.85s; }
  .inv-stage.opened .inv-card .card-detail:nth-of-type(n+6) { animation-delay: 1.95s; }
  .inv-extras { transition-delay: 2.05s !important; }
  /* Enveloppe fade-out doux après ouverture pour laisser la carte seule en scène */
  .env-frame.opened {
    transition: opacity .8s 1.6s, transform .8s 1.6s;
    opacity: .22;
    transform: translateY(-6px) scale(.94);
    pointer-events: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .inv-stage.opened .inv-card,
  .inv-stage.opened .inv-card * {
    animation: none !important;
    opacity: 1 !important;
    transform: none !important;
  }
  .env-flap, .env-frame, .env-seal { transition-duration: .15s !important; }
}

/* ====== EXTRAS (countdown + map + actions) - fade in just after card ====== */
.inv-extras {
  width: min(560px, 96vw);
  display: flex; flex-direction: column;
  gap: 24px;
  color: var(--page-fg, var(--card-fg, var(--navy)));   /* lisible sur fond de page (clair ou foncé) */
  opacity: 0;
  transform: translateY(12px);
  max-height: 0;
  overflow: hidden;
  pointer-events: none;
  transition:
    opacity .6s 2.80s,
    transform .6s 2.80s,
    max-height 0s 2.80s;
}
.inv-stage.opened .inv-extras {
  opacity: 1;
  transform: none;
  max-height: none;
  pointer-events: auto;
}

/* Buttons inside the invitation : reprennent l'accent du papier choisi */
.inv-stage .btn-gold {
  background: var(--accent, var(--gold));
  color: #fff;
  border-color: var(--accent, var(--gold));
}
.inv-stage .btn-gold:hover {
  filter: brightness(.92);
  background: var(--accent, var(--gold));
  color: #fff;
}
.inv-stage .btn-outline {
  border-color: var(--accent, var(--gold));
  color: var(--page-fg, var(--card-fg, var(--navy)));
}
.inv-stage .btn-outline:hover {
  background: var(--accent, var(--gold));
  border-color: var(--accent, var(--gold));
  color: #fff;
}
.inv-stage .btn-ghost {
  color: var(--card-fg, var(--navy));
}
.inv-stage .btn-ghost:hover {
  background: color-mix(in srgb, var(--accent, var(--gold)) 12%, transparent);
}

/* RSVP form héritant des couleurs */
.inv-stage .rsvp-form {
  background: color-mix(in srgb, var(--accent, var(--gold)) 5%, var(--card-bg, var(--cream)));
  border-color: color-mix(in srgb, var(--accent, var(--gold)) 25%, transparent);
}

/* Calendar tile/border picks up accent too */
.inv-stage .cal-tile { border-color: var(--accent, var(--gold)); }
.inv-stage .cal-tile .cal-day { background: var(--accent, var(--gold)); }
.inv-stage .cal-tile .cal-num { color: var(--card-fg, var(--navy)); }
.inv-stage .cal-counter .cal-eyebrow { color: var(--accent, var(--gold-dark)); }
.inv-stage .cal-counter .cal-line .cd-d,
.inv-stage .cal-counter .cal-line .cd-h,
.inv-stage .cal-counter .cal-line .cd-m,
.inv-stage .cal-counter .cal-line .cd-s { color: var(--page-fg, var(--card-fg, var(--navy))); }

.card-inner {
  width: 100%;
  /* Format portrait par défaut (1500:2200) : la carte est un peu plus longue.
     aspect-ratio est une suggestion - si le texte déborde, la carte grandit. */
  aspect-ratio: 1500 / 2200;
  background: var(--card-bg, var(--cream));
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  border-radius: 10px;
  padding: clamp(48px, 8vw, 88px) clamp(28px, 5vw, 64px);
  text-align: center;
  box-shadow:
    0 30px 90px rgba(0,0,0,.18),
    0 8px 30px rgba(0,0,0,.08),
    inset 0 0 0 1px rgba(255,255,255,.4);
  border: 1px solid var(--card-border, var(--line));
  position: relative;
}

/* ============================================================================
 * FIX CROP : quand un visuel template (card_image_url) est appliqué en fond,
 * on force un ratio 3/4 portrait + background-size: contain pour que TOUS les
 * éléments décoratifs (Hello Kitty aux coins, motifs sur les bords, etc.)
 * soient visibles, identique à la mini-prévisualisation .basic-preview-card.
 *
 * - aspect-ratio: 3/4  → la carte adopte la forme d'une vraie invitation portrait
 *                       (si le contenu texte dépasse, min-height: auto laisse
 *                       le card grandir verticalement sans casser le layout)
 * - background-size: contain → l'image entière tient dans le card, jamais cropée.
 *   Les zones non couvertes par l'image prennent --card-bg (cohérent visuellement).
 *
 * S'applique UNIQUEMENT quand .inv-stage a la classe .has-bg-image (= visuel
 * template présent) pour ne pas affecter les cartes texte-only ni les vidéos.
 * ============================================================================ */
.inv-stage.has-bg-image .card-inner {
  /* Uploads de dimensions VARIABLES : la carte adopte le ratio RÉEL de l'image,
     posé par le JS dans --card-img-ratio (lecture de naturalWidth/Height). Ainsi
     l'image remplit le cadre bord à bord, sans bande crème ni vide, quelle que
     soit sa dimension (portrait, carré, paysage). Fallback 1500/2200 avant le JS. */
  aspect-ratio: var(--card-img-ratio, 1500 / 2200);
  min-height: auto;
  /* cover (et non contain) : comme le ratio du cadre = ratio de l'image, l'image
     entière reste visible TOUT EN remplissant - pas de letterbox. Si le texte
     pousse la carte plus haut que le ratio, cover évite la bande crème. */
  background-size: cover;
  background-position: center;
  background-color: var(--card-bg, var(--cream));
  /* Liseré coloré du thème retiré sur image plein cadre (le bleu baby-blue
     jurait sur Spider-Man) : l'image va jusqu'au bord arrondi. On neutralise
     À LA FOIS la bordure ET l'inset box-shadow tracé par les cadres
     (frame-fine-line par défaut = inset 0 0 0 1px var(--accent)). On ne garde
     qu'une ombre portée douce. !important pour battre les règles .frame-*. */
  border-color: transparent !important;
  box-shadow: 0 30px 90px rgba(0,0,0,.18), 0 8px 30px rgba(0,0,0,.08) !important;
  /* Sur image plein cadre, le décor occupe souvent le haut : on descend tout le
     bloc texte d'un cran avec un padding-haut plus généreux. */
  padding-top: clamp(72px, 12vw, 132px);
}

/* Sur écrans étroits, on tolère légèrement plus carré pour ne pas créer une
 * carte trop haute sur mobile (la lisibilité prime). */
@media (max-width: 520px) {
  .inv-stage.has-bg-image .card-inner {
    /* Mobile : on respecte aussi le ratio réel de l'image (fallback 1500/2200). */
    aspect-ratio: var(--card-img-ratio, 1500 / 2200);
    padding-top: clamp(60px, 16vw, 96px);
  }
}

/* ====== Tier "animated" : vidéo de fond (MP4/WebM) sous la carte ====== */
/* La <video> est posée en absolute remplissant le card-inner ; le contenu
 * texte reste au-dessus via position:relative + z-index. Le card-inner devient
 * transparent (le fond visible = la vidéo) tout en conservant l'overlay couleur
 * via box-shadow inset pour préserver la lisibilité du texte. */
.card-inner.has-bg-video {
  background: transparent;
  overflow: hidden;
  isolation: isolate;
}
.card-bg-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: 0;
  pointer-events: none;
  border-radius: inherit;
}
/* Voile vidéo léger : on garde la lisibilité sans éteindre les couleurs du template. */
.card-inner.has-bg-video::before {
  content: "";
  position: absolute;
  inset: 0;
  background:
    linear-gradient(180deg, rgba(0,0,0,.28) 0%, rgba(0,0,0,.08) 38%, rgba(0,0,0,.34) 100%),
    rgba(0,0,0,.08);
  z-index: 1;
  pointer-events: none;
  border-radius: inherit;
}
.card-inner.has-bg-video > *:not(.card-bg-video) {
  position: relative;
  z-index: 2;
}
/* Force TOUT le texte en blanc + ombre sur les cartes animées.
 * !important nécessaire pour écraser les couleurs dorées des layouts hérités. */
.card-inner.has-bg-video,
.card-inner.has-bg-video *:not(.card-bg-video):not(svg):not(path) {
  color: #fff !important;
  text-shadow: 0 1px 4px rgba(0,0,0,.72), 0 0 14px rgba(0,0,0,.42);
}
/* Restaure une opacité pleine sur les labels qui étaient atténués */
.card-inner.has-bg-video .card-eyebrow,
.card-inner.has-bg-video .card-detail .label,
.card-inner.has-bg-video .card-detail .muted,
.card-inner.has-bg-video .muted {
  opacity: 1 !important;
}
/* Divider/lignes en blanc semi-transparent (pas couleur accent dorée) */
.card-inner.has-bg-video .card-divider,
.card-inner.has-bg-video hr,
.card-inner.has-bg-video .divider {
  background: rgba(255,255,255,.85) !important;
  border-color: rgba(255,255,255,.85) !important;
  opacity: 1 !important;
}
/* Médaillon photo : bordure blanche douce */
.card-inner.has-bg-video .card-photo {
  border-color: rgba(255,255,255,.7) !important;
  box-shadow: 0 4px 18px rgba(0,0,0,.35);
}

/* ====== Corner monograms (Paperless-Post-style editorial labels) ====== */
.card-corner {
  position: absolute;
  font-family: var(--serif), serif;
  font-style: italic;
  font-size: .82rem;
  letter-spacing: .15em;
  color: var(--accent, var(--gold-dark));
  opacity: .85;
  z-index: 2;
}
.card-corner-tl { top: 22px; left: 26px; }
.card-corner-tr { top: 22px; right: 26px; }
.card-corner-bl { bottom: 22px; left: 26px; }
.card-corner-br { bottom: 22px; right: 26px; }
[dir="rtl"] .card-corner-tl,
[dir="rtl"] .card-corner-tr,
[dir="rtl"] .card-corner-bl,
[dir="rtl"] .card-corner-br { letter-spacing: 0; }

/* Rangée RSVP / ASAP.
   - Cartes texte normales : wrapper inerte (hauteur 0), les coins restent en
     position absolue dans les angles bas (style monogramme éditorial).
   - Cartes à image de fond : hauteur variable → on sort les coins du position
     absolu et on les pose en flux, en rangée justifiée sous le contenu, pour
     qu'ils soient toujours bien placés (jamais détachés ni en chevauchement). */
.inv-stage.has-bg-image .card-inner .card-rsvp-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 18px;
  width: 100%;
  margin-top: 30px;
}
.inv-stage.has-bg-image .card-inner .card-rsvp-row .card-corner-bl,
.inv-stage.has-bg-image .card-inner .card-rsvp-row .card-corner-br {
  position: static !important;
  inset: auto !important;
}

/* ====== Card frame / texture choices ====== */
.inv-card.frame-none .card-inner {
  border-color: transparent;
  box-shadow: 0 24px 70px rgba(0,0,0,.12);
}
.inv-card.frame-fine-line .card-inner {
  box-shadow: 0 28px 80px rgba(0,0,0,.14), inset 0 0 0 1px var(--accent, #111);
}
.inv-card.frame-double-line .card-inner {
  box-shadow:
    0 28px 80px rgba(0,0,0,.14),
    inset 0 0 0 1px var(--accent, #111),
    inset 0 0 0 8px var(--card-bg, #fff),
    inset 0 0 0 9px color-mix(in srgb, var(--accent, #111) 55%, transparent);
}
.inv-card.frame-soft-shadow .card-inner {
  box-shadow: 0 34px 95px rgba(0,0,0,.2), 0 8px 24px rgba(0,0,0,.1);
}
.inv-card.frame-deckled .card-inner {
  border-style: dashed;
  border-color: color-mix(in srgb, var(--accent, #111) 65%, transparent);
}
.inv-card.frame-arabesque .card-inner::before {
  content: '';
  position: absolute;
  inset: 18px;
  pointer-events: none;
  opacity: .28;
  background:
    linear-gradient(var(--accent, #111), var(--accent, #111)) top center / 70% 1px no-repeat,
    linear-gradient(var(--accent, #111), var(--accent, #111)) bottom center / 70% 1px no-repeat,
    radial-gradient(circle at 0 0, transparent 0 14px, var(--accent, #111) 15px 16px, transparent 17px) top left / 38px 38px no-repeat,
    radial-gradient(circle at 100% 0, transparent 0 14px, var(--accent, #111) 15px 16px, transparent 17px) top right / 38px 38px no-repeat,
    radial-gradient(circle at 0 100%, transparent 0 14px, var(--accent, #111) 15px 16px, transparent 17px) bottom left / 38px 38px no-repeat,
    radial-gradient(circle at 100% 100%, transparent 0 14px, var(--accent, #111) 15px 16px, transparent 17px) bottom right / 38px 38px no-repeat;
  z-index: 1;
}
@media (max-width: 480px) {
  .card-corner { font-size: .72rem; }
  .card-corner-tl, .card-corner-tr { top: 16px; }
  .card-corner-tl, .card-corner-bl { left: 18px; }
  .card-corner-tr, .card-corner-br { right: 18px; }
  .card-corner-bl, .card-corner-br { bottom: 16px; }
}

/* ========== Boutons de partage social ========== */
.card-share {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  margin-top: 18px; padding-top: 16px;
  border-top: 1px solid color-mix(in srgb, var(--accent, #C9A961) 25%, transparent);
  justify-content: center;
  /* On borne la largeur pour que les 6 boutons se replient sur 2 lignes (3 + 3),
     centrés sous la carte, au lieu de s'étaler sur une seule ligne très large. */
  max-width: 320px;
  margin-inline: auto;
}
.card-share-label {
  font-family: var(--serif), serif; font-style: italic;
  font-size: .85rem; color: var(--card-fg, var(--navy));
  opacity: .7;
  /* Le label "Partager :" prend toute la largeur → les boutons se replient
     en dessous, bien centrés sur leurs propres lignes (≈ 2 lignes). */
  flex: 1 0 100%;
  text-align: center;
  margin-bottom: 2px;
}
.card-share-btn {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 8px 14px;
  border-radius: 999px;
  font-size: .82rem; font-weight: 500;
  background: rgba(255,255,255,.7);
  border: 1px solid color-mix(in srgb, var(--accent, #C9A961) 30%, transparent);
  color: var(--card-fg, var(--navy));
  text-decoration: none;
  cursor: pointer;
  transition: transform .2s ease, box-shadow .2s ease, background .2s ease;
}
.card-share-btn:hover {
  transform: translateY(-1px);
  box-shadow: 0 4px 10px rgba(0,0,0,.08);
}
.card-share-btn svg { flex-shrink: 0; }

/* Couleurs de marque par bouton (au hover, accent de la plateforme) */
.share-whatsapp:hover  { background: #25D366; color: #fff; border-color: #25D366; }
.share-facebook:hover  { background: #1877F2; color: #fff; border-color: #1877F2; }
.share-messenger:hover { background: #0084FF; color: #fff; border-color: #0084FF; }
.share-twitter:hover   { background: #000;    color: #fff; border-color: #000; }
.share-email:hover     { background: var(--card-fg, #1A2B4A); color: var(--card-bg, #fff); }
.share-copy:hover      { background: var(--accent, #C9A961); color: #fff; border-color: var(--accent, #C9A961); }
.share-copy.share-copied { background: #16A34A; color: #fff; border-color: #16A34A; }

@media (max-width: 480px) {
  .card-share { gap: 6px; }
  .card-share-btn { padding: 7px 11px; font-size: .76rem; }
  .card-share-label { display: none; }
}

/* Mode photo-background : boutons plus contrastés */
.inv-card.layout-photo-background.has-photo .card-share-btn {
  background: rgba(255,255,255,.92);
  color: #1A2B4A;
  border-color: rgba(255,255,255,.4);
}

/* Mode flyer : centré, pas de border-top */
.card-share.card-share-flyer {
  border-top: 0;
  padding-top: 0;
  margin-top: 0;
}

/* ========== Logo (corporate) - affiché en bas de la carte ========== */
.card-logo {
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 28px auto 0;
  padding-top: 22px;
  border-top: 1px solid color-mix(in srgb, var(--accent, #C9A961) 35%, transparent);
  width: 60%;
  max-width: 220px;
}
.card-logo img {
  max-width: 100%;
  max-height: 70px;
  object-fit: contain;
  filter: drop-shadow(0 2px 4px rgba(0,0,0,.08));
}
.inv-card.layout-photo-background .card-logo {
  border-top-color: rgba(255,255,255,.4);
}
.inv-card.layout-photo-background .card-logo img {
  filter: drop-shadow(0 2px 6px rgba(0,0,0,.4)) brightness(1.05);
}
@media (max-width: 480px) {
  .card-logo { margin-top: 22px; padding-top: 16px; max-width: 180px; }
  .card-logo img { max-height: 56px; }
}

/* ====== Layouts (text-only / photo-frame / photo-background) ====== */

/* Layout 1 - text-only : default, nothing to add */

/* Layout 2 - photo-frame : photo en haut, texte en dessous
 * Le ::before n'apparaît QUE si une photo a été uploadée (sinon
 * on n'affiche pas un bandeau de couleur d'accent à vide).
 * Détection : la classe .has-photo est ajoutée par le renderer si $photo_url. */
.inv-card.layout-photo-frame.has-photo .card-inner {
  padding-top: 0;
}
.inv-card.layout-photo-frame.has-photo .card-inner::before {
  content: '';
  display: block;
  height: 220px;
  margin: 0 -1px 28px;
  background-image: var(--card-photo, none);
  background-size: cover;
  background-position: center;
  border-radius: 12px 12px 0 0;
  position: relative;
  z-index: 0;
}

/* ============================================================
   Photo médaillon rond (.card-photo) - élément HTML rendu par render.php
   quand l'utilisateur a uploadé une photo. Visible quel que soit le
   layout (text-only, photo-frame, photo-round).
   Caché si layout-photo-background (qui utilise déjà la photo en plein fond).
   ============================================================ */
.inv-card .card-photo { display: none; }
.inv-card.has-photo .card-photo {
  display: block;
  width: clamp(96px, 28%, 160px);
  aspect-ratio: 1;
  background-image: var(--card-photo, none);
  background-size: cover;
  background-position: center;
  border-radius: 50%;
  border: 3px solid var(--card-bg, #fff);
  box-shadow: 0 6px 18px rgba(0,0,0,.18), 0 0 0 1px var(--accent, #C9A961);
  margin: 0 auto 22px;
  position: relative;
  z-index: 5;
  flex: 0 0 auto;
}
/* Sur layout-photo-background, la photo est en plein fond, on cache le médaillon */
.inv-card.layout-photo-background.has-photo .card-photo { display: none; }
/* Sur layout-photo-frame, le ::before fait déjà la photo en bandeau, on cache le médaillon */
.inv-card.layout-photo-frame.has-photo .card-photo { display: none; }

/* Layout 3 - photo-background : photo plein fond + overlay sombre + texte par-dessus
 * Effet plein-fond actif uniquement si .has-photo (sinon on rend comme text-only). */
.inv-card.layout-photo-background.has-photo .card-inner {
  background-image: var(--card-photo, none);
  background-size: cover;
  background-position: center;
  color: #fff;
  position: relative;
  isolation: isolate;
  min-height: 480px;
}
.inv-card.layout-photo-background.has-photo .card-inner::before {
  content: '';
  position: absolute; inset: 0;
  background:
    linear-gradient(
      180deg,
      var(--photo-overlay-top, rgba(0, 0, 0, .21)) 0%,
      var(--photo-overlay-mid, rgba(0, 0, 0, .13)) 52%,
      var(--photo-overlay-base, rgba(0, 0, 0, .38)) 100%
    );
  border-radius: inherit;
  z-index: -1;
}
.inv-card.layout-photo-background .card-divider {
  background: rgba(255,255,255,.6);
}

/* When the card uses a background image, add a subtle inner glow so text stays legible */
.has-bg-image .card-inner {
  background-color: transparent;
  position: relative;
  isolation: isolate;
}
.has-bg-image .card-inner::before {
  content: '';
  position: absolute; inset: 0;
  /* Reset des propriétés héritées de .card-inner::before (ligne décorative)
     pour éviter un fragment 80×1px qui projette une ombre/halo gris au centre. */
  width: auto; height: auto;
  top: 0; left: 0; right: 0; bottom: 0;
  transform: none;
  /* Voile retiré : il désaturait la couleur du papier (beige → gris perçu).
     Si l'image de fond rend le texte illisible, l'utilisateur peut ajuster
     la couleur Accent/Texte via le picker dédié. */
  background: none;
  border-radius: inherit;
  z-index: -1;
  pointer-events: none;
}
/* Also remove the small horizontal lines that we add to plain themes (they often clash with imagery) */
.has-bg-image .card-inner::after { display: none; }

/* ============================================================================
 * LISIBILITÉ TEXTE SUR IMAGE DE FOND - ADAPTIVE (Option B)
 * ----------------------------------------------------------------------------
 * Le JS de render.php analyse l'image de fond et ajoute UNE des classes :
 *   .bg-image-light → image globalement claire → texte SOMBRE
 *   .bg-image-dark  → image globalement sombre → texte CLAIR
 *
 * Pas de panneaux/cadres : juste la couleur de texte + text-shadow qui crée
 * le contraste naturel. L'image reste entièrement visible.
 *
 * Fallback (avant que le JS tourne, ou si CORS bloque) : on garde le code
 * en .bg-image-dark par défaut (texte clair) - c'est le cas le plus fréquent.
 * ============================================================================ */

/* Centrage texte conservé */
.has-bg-image .card-eyebrow,
.has-bg-image .card-names,
.has-bg-image .card-message,
.has-bg-image .card-detail,
.has-bg-image .card-date {
  text-align: center;
}

/* === DEFAULT / DARK BACKGROUND : texte clair + ombre sombre ===
   S'applique quand l'image est sombre OU avant que le JS tourne. */
.has-bg-image .card-eyebrow,
.has-bg-image .card-names,
.has-bg-image .card-message,
.has-bg-image .card-detail .label,
.has-bg-image .card-detail .value,
.has-bg-image .card-detail .muted,
.has-bg-image .card-date,
.has-bg-image .card-corner-bl,
.has-bg-image .card-corner-br,
.has-bg-image.bg-image-dark .card-eyebrow,
.has-bg-image.bg-image-dark .card-names,
.has-bg-image.bg-image-dark .card-message,
.has-bg-image.bg-image-dark .card-detail .label,
.has-bg-image.bg-image-dark .card-detail .value,
.has-bg-image.bg-image-dark .card-detail .muted,
.has-bg-image.bg-image-dark .card-host,
.has-bg-image.bg-image-dark .card-date,
.has-bg-image.bg-image-dark .card-corner-bl,
.has-bg-image.bg-image-dark .card-corner-br {
  color: #FFFDF9 !important;
  /* Ombre directionnelle douce uniquement : pas de halo "0 0" qui détoure
     les lettres (effet contour disgracieux). */
  text-shadow: 0 1px 4px rgba(0, 0, 0, .55) !important;
  background: transparent !important;
  -webkit-backdrop-filter: none !important;
  backdrop-filter: none !important;
  border: 0 !important;
  padding: 0 !important;
}

/* === LIGHT BACKGROUND : texte sombre + ombre claire (halo) ===
   Détecté par JS = luminance image > 0.55 */
.has-bg-image.bg-image-light .card-eyebrow,
.has-bg-image.bg-image-light .card-names,
.has-bg-image.bg-image-light .card-message,
.has-bg-image.bg-image-light .card-detail .label,
.has-bg-image.bg-image-light .card-detail .value,
.has-bg-image.bg-image-light .card-detail .muted,
.has-bg-image.bg-image-light .card-host,
.has-bg-image.bg-image-light .card-date,
.has-bg-image.bg-image-light .card-corner-bl,
.has-bg-image.bg-image-light .card-corner-br {
  color: #1A1A1A !important;
  /* Ombre claire directionnelle douce uniquement : pas de halo "0 0" qui
     détoure les lettres (le contour blanc autour du texte était disgracieux). */
  text-shadow: 0 1px 3px rgba(255, 255, 255, .7) !important;
  background: transparent !important;
  -webkit-backdrop-filter: none !important;
  backdrop-filter: none !important;
  border: 0 !important;
  padding: 0 !important;
}

/* Eyebrow et message gardent leur padding original - réinjection après reset */
.has-bg-image .card-message {
  padding: 0 12px !important;
  max-width: 80%;
  /* max-width sans marge auto = bloc collé à gauche → texte décentré.
     margin auto recentre le bloc (et donc le texte) au milieu de la carte. */
  margin-left: auto !important;
  margin-right: auto !important;
}

/* Trait de séparation retiré sur les cartes à image de fond (look plus épuré). */
.inv-stage.has-bg-image .card-inner .card-divider {
  display: none !important;
}

/* Plus d'air vertical sur les cartes à image de fond - le texte paraissait
   trop serré (titre collé à la valeur, sections trop rapprochées). */
.inv-stage.has-bg-image .card-inner .card-eyebrow { margin-bottom: 28px !important; }
.inv-stage.has-bg-image .card-inner .card-names   { margin-bottom: 18px !important; line-height: 1.18 !important; }
.inv-stage.has-bg-image .card-inner .card-message { margin-top: 28px !important; margin-bottom: 28px !important; }
.inv-stage.has-bg-image .card-inner .card-detail  { margin-top: 34px !important; margin-bottom: 34px !important; }
.inv-stage.has-bg-image .card-inner .card-detail .label { margin-bottom: 10px !important; }
.inv-stage.has-bg-image .card-inner .card-detail .value { line-height: 1.4 !important; }
.inv-stage.has-bg-image .card-inner .card-rsvp-row { margin-top: 40px !important; }

/* Dernier filet de sécurité : aucune étiquette ne doit redevenir un pavé gris
   sur les cartes illustrées. Certains navigateurs gardent longtemps d'anciennes
   règles en cache ; ces sélecteurs plus larges protègent aussi les textes
   injectés/inline sans masquer le fond floral ou papier. */
.inv-stage.has-bg-image .card-inner :where(
  .card-eyebrow,
  .card-names,
  .card-message,
  .card-detail,
  .card-detail .label,
  .card-detail .value,
  .card-detail .muted,
  .card-corner
) {
  background: transparent !important;
  background-color: transparent !important;
  box-shadow: none !important;
  -webkit-backdrop-filter: none !important;
  backdrop-filter: none !important;
  border-color: transparent !important;
}

.inv-stage.has-bg-image .card-detail {
  display: block;
  padding: 0 !important;
  margin: clamp(28px, 4.2vw, 44px) 0 !important;
}

.inv-stage.has-bg-image .card-eyebrow {
  margin-bottom: clamp(22px, 3vw, 34px) !important;
}

.inv-stage.has-bg-image .card-names {
  margin-bottom: clamp(26px, 3.5vw, 42px) !important;
  line-height: 1.16 !important;
}

.inv-stage.has-bg-image .card-detail .label {
  display: block;
  margin-bottom: 12px !important;
}

.inv-stage.has-bg-image .card-detail .value {
  line-height: 1.22 !important;
}

.inv-stage.has-bg-image .card-detail .muted {
  display: block;
  margin-top: 10px !important;
}

/* === Exception : panneau discret UNIQUEMENT sur la date/heure ===
   Le titre ("Date et heure") reste propre (texte + ombre). Seule la valeur
   date + l'heure reçoivent un léger backing translucide (info clé à lire vite).
   Sélecteur spécifique (0,4,0+) pour passer DEVANT le filet de sécurité. */
.inv-stage.has-bg-image .card-inner .card-detail-date .card-date-box {
  display: inline-block !important;
  margin-top: 4px !important;
  padding: 14px 32px !important;
  border-radius: 12px !important;
  background: rgba(0, 0, 0, .24) !important;
  -webkit-backdrop-filter: blur(6px) saturate(1.05) !important;
  backdrop-filter: blur(6px) saturate(1.05) !important;
  box-shadow: 0 6px 20px rgba(0, 0, 0, .18) !important;
}
.inv-stage.has-bg-image.bg-image-light .card-inner .card-detail-date .card-date-box {
  background: rgba(255, 255, 255, .42) !important;
}

/* === Mode opt-in conservé : .img-lift-mode pour éclaircir une image
   particulièrement sombre (ajout de classe côté admin par template) === */
.has-bg-image.img-lift-mode .card-inner::before {
  content: '';
  position: absolute; inset: 0;
  background:
    radial-gradient(ellipse at center, rgba(255,253,249,.15) 0%, rgba(255,253,249,.02) 60%, transparent 100%),
    rgba(255,253,249,.08);
  z-index: 0;
  border-radius: inherit;
  pointer-events: none;
}
/* For dark-tone backgrounds, themes can override with .theme-noir.has-bg-image etc. */
.card-inner::before, .card-inner::after {
  content: ''; position: absolute; left: 50%; transform: translateX(-50%);
  width: 80px; height: 1px; background: var(--accent, var(--gold));
}
.card-inner::before { top: 30px; }
.card-inner::after { bottom: 30px; }

.card-eyebrow {
  font-family: var(--serif); font-style: italic;
  color: var(--accent, var(--gold-dark));
  letter-spacing: .15em; text-transform: uppercase; font-size: .82rem;
  margin-bottom: 20px;
}
.card-names {
  font-family: var(--card-font, var(--serif-display));
  font-weight: var(--card-font-weight, 500);
  font-size: clamp(2.2rem, 6vw, 3.4rem);
  color: var(--card-fg, var(--navy));
  line-height: 1.1;
  margin-bottom: 8px;
}
.card-amp {
  font-family: var(--serif); font-style: italic;
  color: var(--accent, var(--gold-dark));
  font-size: 1.6rem; margin: 8px 0;
}
/* Contact / VIP / hôte : ligne secondaire discrète sous le titre. */
.card-host {
  font-family: var(--serif); font-style: italic;
  color: color-mix(in srgb, var(--card-fg, var(--ink)) 80%, transparent);
  font-size: 1.05rem;
  letter-spacing: .02em;
  margin: -2px 0 6px;
}
.card-message {
  font-family: var(--serif); font-style: italic;
  color: color-mix(in srgb, var(--card-fg, var(--ink)) 78%, transparent);
  font-size: 1.05rem; line-height: 1.7;
  margin: 24px 0;
}

.card-divider { width: 60%; height: 1px; background: var(--accent, var(--gold)); margin: 32px auto; opacity: .4; }

.card-detail {
  display: flex; flex-direction: column; gap: 4px;
  margin: 24px 0;
  /* Garantie : aucun fond gris ne doit être posé derrière les détails
     (date/lieu/thème). Si un thème ou un override a injecté un background,
     on l'écrase pour préserver la lecture sur la texture papier. */
  background: transparent !important;
  box-shadow: none !important;
}
.card-detail .label,
.card-detail .value,
.card-detail .muted { background: transparent !important; }
.card-detail .label {
  font-family: var(--serif); font-style: italic;
  font-size: .85rem;
  color: color-mix(in srgb, var(--card-fg, var(--ink)) 62%, transparent);
  letter-spacing: .1em; text-transform: uppercase;
}
.card-detail .value {
  font-family: var(--serif-display); font-size: 1.3rem;
  color: var(--card-fg, var(--navy));
}

/* Adresse cliquable → Google Maps. Lien discret : hérite la couleur, souligné
 * au survol, avec une petite épingle. En aperçu, c'est un <span> non navigable. */
.card-location-link {
  color: inherit;
  text-decoration: none;
  cursor: pointer;
  display: block;
  transition: opacity .15s ease;
}
a.card-location-link:hover { opacity: .82; }
a.card-location-link:hover .value { text-decoration: underline; text-underline-offset: 3px; }
.card-location-pin {
  font-size: .8em;
  opacity: .6;
  margin-left: 4px;
  vertical-align: middle;
}

/* ====== Bloc info AFFICHE (titre + lieu sous l'image importée/IA) ====== */
.affiche-info {
  max-width: 460px;
  margin: 18px auto 4px;
  padding: 0 16px;
  text-align: center;
  font-family: var(--sans, system-ui, sans-serif);
}
.affiche-info-title {
  font-family: var(--serif-display, 'Playfair Display', serif);
  font-size: clamp(1.15rem, 3.4vw, 1.5rem);
  line-height: 1.25;
  color: var(--page-fg, var(--paper-fg, #1A2B4A));
  margin-bottom: 8px;
}
.affiche-info-loc {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  justify-content: center;
  font-size: .98rem;
  line-height: 1.5;
  color: var(--page-fg, var(--paper-fg, #1A2B4A));
}
.affiche-info-loc .muted { opacity: .7; }
.affiche-info-pin { opacity: .7; }
a.affiche-info-loc:hover .affiche-info-loc-text { text-decoration: underline; text-underline-offset: 3px; }

/* ====== Calendar block (tile + countdown) ====== */
.cal-block {
  margin: 28px auto;
  max-width: 320px;
  text-align: center;
}

/* Calendar tile - vraie tuile de calendrier mural */
.cal-tile {
  background: #fff;
  border: 1px solid var(--accent, var(--gold));
  border-radius: 12px;
  overflow: hidden;
  box-shadow: 0 8px 24px rgba(0,0,0,.08), 0 2px 6px rgba(0,0,0,.04);
  display: inline-block;
  width: min(220px, 100%);
  position: relative;
}
.cal-tile::before,
.cal-tile::after {
  content: ''; position: absolute; top: -6px;
  width: 8px; height: 12px;
  background: #C0C0C0;
  border-radius: 2px 2px 0 0;
  box-shadow: 0 1px 2px rgba(0,0,0,.2);
}
.cal-tile::before { left: 22%; }
.cal-tile::after  { right: 22%; }

.cal-tile .cal-day {
  background: var(--accent, var(--gold));
  color: #fff;
  padding: 8px 0;
  font-family: var(--serif), serif;
  font-style: italic;
  font-size: .85rem;
  letter-spacing: .15em;
  text-transform: uppercase;
}
.cal-tile .cal-num {
  font-family: var(--serif-display), serif;
  font-size: 4.2rem;
  font-weight: 500;
  line-height: 1;
  color: var(--card-fg, var(--navy));
  padding: 16px 0 8px;
  font-variant-numeric: tabular-nums;
}
.cal-tile .cal-month {
  font-family: var(--serif), serif;
  font-style: italic;
  font-size: .95rem;
  color: var(--muted);
  padding-bottom: 14px;
  letter-spacing: .04em;
}

/* Compact countdown line below tile */
.cal-counter {
  margin-top: 18px;
}
.cal-counter .cal-eyebrow {
  display: block;
  font-family: var(--serif), serif;
  font-style: italic;
  color: var(--accent, var(--gold-dark));
  font-size: .82rem;
  letter-spacing: .1em;
  margin-bottom: 8px;
  opacity: .8;
}
.cal-counter .cal-line {
  display: inline-flex;
  align-items: baseline;
  gap: 4px;
  font-family: var(--serif-display), serif;
  font-variant-numeric: tabular-nums;
  font-size: 1.1rem;
  color: var(--page-fg, var(--card-fg, var(--navy)));
}
.cal-counter .cal-line .cd-d,
.cal-counter .cal-line .cd-h,
.cal-counter .cal-line .cd-m,
.cal-counter .cal-line .cd-s {
  font-weight: 500;
  font-size: 1.4rem;
  margin-left: 6px;
}
.cal-counter .cal-line .cd-d { margin-left: 0; }
.cal-counter .cal-line .cd-lbl {
  font-family: var(--serif), serif;
  font-style: italic;
  font-size: .85rem;
  color: var(--page-fg-soft, var(--muted));
  margin-right: 4px;
}

@media (max-width: 480px) {
  .cal-tile { width: 200px; }
  .cal-tile .cal-num { font-size: 3.6rem; padding: 12px 0 6px; }
  .cal-counter .cal-line .cd-d,
  .cal-counter .cal-line .cd-h,
  .cal-counter .cal-line .cd-m,
  .cal-counter .cal-line .cd-s { font-size: 1.2rem; }
}

/* ====== Action buttons row ====== */
.card-actions { display: flex; flex-direction: column; gap: 10px; margin-top: 32px; }
.card-actions .btn { width: 100%; }

/* ====== Map ====== */
.inv-map {
  margin-top: 32px;
  border-radius: 12px; overflow: hidden;
  border: 1px solid var(--card-border, var(--line));
  height: 240px;
  background: linear-gradient(180deg, rgba(255,255,255,.7), rgba(255,255,255,.35));
}
.inv-map iframe { width: 100%; height: 100%; border: 0; }
.inv-map-fallback {
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  padding: 24px;
  text-align: center;
  background:
    linear-gradient(135deg, rgba(201,169,97,.14), rgba(255,255,255,.22)),
    var(--card-bg, var(--cream));
}
.inv-map-fallback strong {
  font-family: var(--serif-display);
  color: var(--card-fg, var(--navy));
}

/* ====== Music toggle - disque vinyle qui tourne ====== */
.music-toggle {
  position: fixed; bottom: 20px; right: 20px;
  box-sizing: border-box; padding: 0;
  width: 54px; height: 54px; border-radius: 50%;
  /* Vinyle : sillons sombres + étiquette dorée au centre */
  background:
    radial-gradient(circle at 50% 50%,
      #15151a 0 28%,
      var(--gold, #C9A961) 28% 44%,
      #232329 44% 56%,
      #15151a 56% 72%,
      #26262c 72% 86%,
      #15151a 86% 100%);
  color: var(--navy, #1A2B4A);
  display: flex; align-items: center; justify-content: center;
  box-shadow: var(--shadow); z-index: 30;
  font-size: .9rem; font-weight: 700;
  border: 1px solid rgba(0,0,0,.45);
  /* Le disque tourne en continu mais reste en pause tant que la musique
     n'est pas lancée (classe .playing ajoutée par le JS). */
  animation: vinyl-spin 3.2s linear infinite;
  animation-play-state: paused;
}
.music-toggle.playing { animation-play-state: running; }
/* Halo pulsant pendant la lecture */
.music-toggle.playing::after {
  content: ''; position: absolute; inset: -6px;
  border: 2px solid var(--gold); border-radius: 50%;
  animation: ring 1.6s infinite;
}
@keyframes vinyl-spin { to { transform: rotate(360deg); } }
@keyframes ring { 0% { transform: scale(.8); opacity: 1; } 100% { transform: scale(1.4); opacity: 0; } }
@media (prefers-reduced-motion: reduce) {
  .music-toggle { animation: none !important; }
}

.replay-open {
  position: fixed;
  top: 18px;
  right: 18px;
  z-index: 90;
  width: 42px;
  height: 42px;
  border-radius: 50%;
  border: 1px solid rgba(0,0,0,.12);
  background: rgba(255,255,255,.86);
  color: var(--card-fg, #111);
  display: grid;
  place-items: center;
  font-size: 1.05rem;
  cursor: pointer;
  opacity: 0;
  pointer-events: none;
  transform: translateY(-6px);
  box-shadow: 0 10px 28px rgba(0,0,0,.10);
  transition: opacity .25s ease, transform .25s ease, background .2s ease;
}
.inv-stage.opened .replay-open {
  opacity: 1;
  pointer-events: auto;
  transform: none;
}
.replay-open:hover {
  background: #fff;
  transform: translateY(-1px);
}

/* ====== RSVP form ====== */
.rsvp-form {
  background: var(--white);
  padding: 32px;
  border-radius: 16px;
  margin-top: 32px;
  text-align: start;
  border: 1px solid var(--line);
}
.rsvp-status-row { display: flex; gap: 8px; flex-wrap: wrap; }
.rsvp-status-row label {
  flex: 1; min-width: 90px; padding: 12px;
  border: 1.5px solid var(--line); border-radius: 10px;
  text-align: center; cursor: pointer;
  transition: all .2s;
}
.rsvp-status-row input { display: none; }
.rsvp-status-row input:checked + span,
.rsvp-status-row label:has(input:checked) {
  border-color: var(--gold); background: rgba(201,169,97,.08); color: var(--gold-dark);
}

/* ====== Per-template themes ====== */
/* Elegant Wedding */
.theme-elegant {
  --env-bg: #F5E6E0; --env-bg-dark: #D4A574;
  --env-flap: #C9A961; --env-flap-dark: #9F8240;
  --env-seal: #1A2B4A; --env-seal-fg: #C9A961;
  --env-text: #1A2B4A;
  --card-bg: #FAF7F2; --card-fg: #1A2B4A;
  --card-border: #E8E2D6; --accent: #C9A961;
}

/* Baby Shower */
.theme-baby {
  --env-bg: #FCE4EC; --env-bg-dark: #F8BBD0;
  --env-flap: #B8C7DC; --env-flap-dark: #90A4C0;
  --env-seal: #6E84A8; --env-seal-fg: #FCE4EC;
  --env-text: #6E84A8;
  --card-bg: #FFF8F8; --card-fg: #6E84A8;
  --card-border: #F8BBD0; --accent: #E8A4C0;
}

/* Corporate */
.theme-corporate {
  --env-bg: #2D4068; --env-bg-dark: #1A2B4A;
  --env-flap: #C9A961; --env-flap-dark: #9F8240;
  --env-seal: #C9A961; --env-seal-fg: #1A2B4A;
  --env-text: #FAF7F2;
  --card-bg: #1A2B4A; --card-fg: #FAF7F2;
  --card-border: #2D4068; --accent: #C9A961;
}
/* Theme-specific text colors retirés : ils écrasaient la personnalisation
 * du papier choisi par l'utilisateur. Les couleurs viennent maintenant uniquement
 * des CSS variables (--card-fg / --accent / --card-bg) injectées par render.php. */

/* Birthday Girl */
.theme-bday-girl {
  --env-bg: #FFD6E5; --env-bg-dark: #FF8FAE;
  --env-flap: #FF8FAE; --env-flap-dark: #E36B91;
  --env-seal: #B53D6B; --env-seal-fg: #FFD6E5;
  --env-text: #B53D6B;
  --card-bg: #FFF5F8; --card-fg: #B53D6B;
  --card-border: #FFD6E5; --accent: #FF8FAE;
}

/* Birthday Boy */
.theme-bday-boy {
  --env-bg: #BDE0FE; --env-bg-dark: #4D9DE0;
  --env-flap: #4D9DE0; --env-flap-dark: #2A6DB0;
  --env-seal: #1A4778; --env-seal-fg: #BDE0FE;
  --env-text: #1A4778;
  --card-bg: #F0F8FF; --card-fg: #1A4778;
  --card-border: #BDE0FE; --accent: #4D9DE0;
}

/* ====== Additional themes ====== */

/* Floral Wedding */
.theme-floral {
  --env-bg: #FFE5DC; --env-bg-dark: #F5C0B0;
  --env-flap: #E8B4C0; --env-flap-dark: #C5849D;
  --env-seal: #6B3E4D; --env-seal-fg: #FFE5DC;
  --env-text: #6B3E4D;
  --card-bg: #FFFAF5; --card-fg: #6B3E4D;
  --card-border: #F5D5DC; --accent: #C5849D;
}

/* Modern Wedding */
.theme-modern-wedding {
  --env-bg: #ECEAE3; --env-bg-dark: #C8C4B8;
  --env-flap: #8A8378; --env-flap-dark: #6C6660;
  --env-seal: #2C2825; --env-seal-fg: #ECEAE3;
  --env-text: #2C2825;
  --card-bg: #F5F2EB; --card-fg: #2C2825;
  --card-border: #D8D5CC; --accent: #8A8378;
}
/* .theme-modern-wedding .card-names font-family override retiré
 *   - il écrasait le choix de typographie de l'utilisateur. */

/* Vintage Wedding */
.theme-vintage {
  --env-bg: #F0DCB0; --env-bg-dark: #C9A678;
  --env-flap: #A87838; --env-flap-dark: #7A5424;
  --env-seal: #5C3A20; --env-seal-fg: #F0DCB0;
  --env-text: #5C3A20;
  --card-bg: #FAF1E0; --card-fg: #5C3A20;
  --card-border: #E0C088; --accent: #A87838;
}
.theme-vintage .card-inner::before { content: '❦'; background: none; height: auto; color: #A87838; font-size: 18px; top: 24px; }
.theme-vintage .card-inner::after { content: '❦'; background: none; height: auto; color: #A87838; font-size: 18px; bottom: 24px; transform: translateX(-50%) rotate(180deg); }

/* Tropical Wedding */
.theme-tropical {
  --env-bg: #A8B89C; --env-bg-dark: #5A7B65;
  --env-flap: #4A8060; --env-flap-dark: #2C5040;
  --env-seal: #2C5040; --env-seal-fg: #FFFBEF;
  --env-text: #2C5040;
  --card-bg: #EEF7F2; --card-fg: #15372B;
  --card-border: #C5D8C8; --accent: #4D8B72;
}

/* Henna - bordeaux + terracotta */
.theme-henna {
  --env-bg: #6B1F2C; --env-bg-dark: #2C1015;
  --env-flap: #8B2D3C; --env-flap-dark: #4A1620;
  --env-seal: #D7B56D; --env-seal-fg: #2C1015;
  --env-text: #FAF1E0;
  --card-bg: #F6E4DA; --card-fg: #3B2218;
  --card-border: #C9A47C; --accent: #9E4B35;
}
.theme-henna-minimal {
  --env-bg: #F6E4DA; --env-bg-dark: #C9A47C;
  --env-flap: #9E4B35; --env-flap-dark: #6B2D1F;
  --env-seal: #6B1F2C; --env-seal-fg: #FAF1E0;
  --env-text: #3B2218;
  --card-bg: #FFFFFF; --card-fg: #3B2218;
  --card-border: #E5C8B0; --accent: #9E4B35;
}

/* Bohemian Wedding */
.theme-boho {
  --env-bg: #E5C8A8; --env-bg-dark: #C9A578;
  --env-flap: #B8845C; --env-flap-dark: #8C6040;
  --env-seal: #6B4528; --env-seal-fg: #E5C8A8;
  --env-text: #6B4528;
  --card-bg: #F5E8D5; --card-fg: #6B4528;
  --card-border: #E0C898; --accent: #B8845C;
}

/* Noir Wedding */
.theme-noir {
  --env-bg: #1A1A1A; --env-bg-dark: #0E0E0E;
  --env-flap: #D4AF37; --env-flap-dark: #A88A2C;
  --env-seal: #D4AF37; --env-seal-fg: #0E0E0E;
  --env-text: #F5E8C8;
  --card-bg: #0E0E0E; --card-fg: #F5E8C8;
  --card-border: #2C2C2C; --accent: #D4AF37;
}
/* theme-noir text overrides retirés (écrasaient le papier user) */

/* Baby Shower variants */
.theme-baby-pink {
  --env-bg: #FFE0EC; --env-bg-dark: #FFB5D0;
  --env-flap: #E08AAA; --env-flap-dark: #B53D6B;
  --env-seal: #B53D6B; --env-seal-fg: #FFE0EC;
  --env-text: #B53D6B;
  --card-bg: #FFF5F8; --card-fg: #B53D6B;
  --card-border: #FFD6E5; --accent: #E08AAA;
}

.theme-baby-blue {
  --env-bg: #DDEEFF; --env-bg-dark: #A8C8E8;
  --env-flap: #5A8AB8; --env-flap-dark: #2A5C8C;
  --env-seal: #1A4778; --env-seal-fg: #DDEEFF;
  --env-text: #1A4778;
  --card-bg: #F5FAFF; --card-fg: #1A4778;
  --card-border: #BDE0FE; --accent: #5A8AB8;
}

.theme-jungle {
  --env-bg: #E5D098; --env-bg-dark: #C9A961;
  --env-flap: #5A8050; --env-flap-dark: #2A4A1A;
  --env-seal: #2A4A1A; --env-seal-fg: #E5D098;
  --env-text: #2A4A1A;
  --card-bg: #FFFBE5; --card-fg: #2C5040;
  --card-border: #C9D898; --accent: #6B8C50;
}

/* Birthday Girl variants */
.theme-unicorn {
  --env-bg: #FFE5F5; --env-bg-dark: #FFB5DC;
  --env-flap: #C58FE0; --env-flap-dark: #8C5BB0;
  --env-seal: #6B4B8C; --env-seal-fg: #FFE5F5;
  --env-text: #6B4B8C;
  --card-bg: linear-gradient(135deg, #FFFAFF 0%, #F5E5FF 100%); --card-fg: #6B4B8C;
  --card-border: #E5C8FF; --accent: #C58FE0;
}

.theme-floral-pink {
  --env-bg: #FFD0E0; --env-bg-dark: #F5A0C0;
  --env-flap: #D58FA8; --env-flap-dark: #A85878;
  --env-seal: #8C3E5A; --env-seal-fg: #FFD0E0;
  --env-text: #8C3E5A;
  --card-bg: #FFF8FA; --card-fg: #8C3E5A;
  --card-border: #FFD0E0; --accent: #D58FA8;
}

/* Birthday Boy variants */
.theme-space {
  --env-bg: #1A1B3A; --env-bg-dark: #0E0E2A;
  --env-flap: #FFD088; --env-flap-dark: #C99B2D;
  --env-seal: #FFD088; --env-seal-fg: #0E0E2A;
  --env-text: #F5F5F8;
  --card-bg: #14152C; --card-fg: #F5F5F8;
  --card-border: #2A2D5A; --accent: #FFD088;
}
/* theme-space text overrides retirés */

.theme-dino {
  --env-bg: #C5E0B4; --env-bg-dark: #6FA34E;
  --env-flap: #6FA34E; --env-flap-dark: #4A7530;
  --env-seal: #2A4A1A; --env-seal-fg: #C5E0B4;
  --env-text: #2A4A1A;
  --card-bg: #F8FCEF; --card-fg: #2A4A1A;
  --card-border: #C5E0B4; --accent: #6FA34E;
}

/* Corporate variants */
.theme-gala {
  --env-bg: #0E0E0E; --env-bg-dark: #2C2C2C;
  --env-flap: #D4AF37; --env-flap-dark: #A88A2C;
  --env-seal: #D4AF37; --env-seal-fg: #0E0E0E;
  --env-text: #FFE5A0;
  --card-bg: #0E0E0E; --card-fg: #FFE5A0;
  --card-border: #2C2C2C; --accent: #D4AF37;
}
/* theme-gala text overrides retirés */

.theme-launch {
  --env-bg: #F0F0F2; --env-bg-dark: #D8D8DC;
  --env-flap: #FF6B35; --env-flap-dark: #C04F20;
  --env-seal: #FF6B35; --env-seal-fg: #FFFFFF;
  --env-text: #1A1A1A;
  --card-bg: #FFFFFF; --card-fg: #1A1A1A;
  --card-border: #E5E5E8; --accent: #FF6B35;
}
/* .theme-launch .card-names font override retiré */

/* RTL adjustments */
[dir="rtl"] .music-toggle { right: auto; left: 20px; }
[dir="rtl"] .replay-open { right: auto; left: 18px; }
[dir="rtl"] .card-eyebrow,
[dir="rtl"] .card-detail .label,
[dir="rtl"] .countdown .lbl { letter-spacing: 0; }

/* Mobile */
@media (max-width: 600px) {
  .countdown .num { font-size: 1.4rem; }
  .countdown .unit { padding: 12px 4px; }
  .card-inner::before, .card-inner::after { width: 50px; }
}

/* Mode PREVIEW : on garde le rendu et l'animation identiques à la vraie invitation.
 * Le récap au-dessus permet de vérifier toutes les infos saisies - l'animation
 * tourne normalement pour valider l'expérience finale. Pas d'override CSS. */

/* Mode FLYER : confetti + enveloppe masqués (animation principale gérée plus haut) */
.inv-stage.is-flyer .confetti-burst { display: none; }
.inv-stage.is-flyer .env-frame,
.inv-stage.is-flyer .env-hint { display: none !important; }

/* Curseur custom retiré - utilisation des curseurs natifs partout sauf
 * sur l'enveloppe fermée qui reste pointer pour signaler "cliquable". */
.inv-stage:not(.opened) .env-frame { cursor: pointer; }

/* ========== Confetti festif (déclenché à l'ouverture) ========== */
.inv-stage .confetti-burst {
  position: absolute;
  top: 28%; left: 0; right: 0;
  pointer-events: none;
  z-index: 50;
  height: 0;
  display: none;
}
.inv-stage.opened .confetti-burst { display: block; }

.confetti-piece {
  position: absolute;
  top: 0; left: 50%;
  width: 10px; height: 14px;
  background: var(--accent, #C9A961);
  opacity: 0;
  transform-origin: center;
  border-radius: 1px;
  animation: confetti-fall 2.4s cubic-bezier(.25,.6,.4,1) forwards;
}
.confetti-piece:nth-child(odd)  { background: var(--card-fg, #1A2B4A); }
.confetti-piece:nth-child(3n)   { background: #E8A4C0; border-radius: 50%; width: 8px; height: 8px; }
.confetti-piece:nth-child(5n)   { background: #6B8C50; }
.confetti-piece:nth-child(7n)   { background: #4D9DE0; }
.confetti-piece:nth-child(11n)  { background: #C9A961; transform: rotate(45deg); }

@keyframes confetti-fall {
  0%   { opacity: 0; transform: translate(0, 0) rotate(0deg) scale(.4); }
  10%  { opacity: 1; }
  100% {
    opacity: 0;
    transform:
      translate(var(--cf-x, 0), var(--cf-y, 70vh))
      rotate(var(--cf-r, 540deg))
      scale(1);
  }
}

@media (prefers-reduced-motion: reduce) {
  .inv-stage .confetti-burst { display: none !important; }
}

/* ========== Map pin custom (couleur accent du papier) ========== */
.leaflet-div-icon.inv-map-pin {
  background: transparent !important;
  border: 0 !important;
}
.inv-map-pin svg { display: block; }
.leaflet-popup-content-wrapper {
  border-radius: 8px;
  border: 1px solid var(--accent, #C9A961);
}
.leaflet-popup-tip { background: var(--card-bg, #FAF7F2) !important; }
.leaflet-popup-content { font-family: var(--card-font, 'Playfair Display'), serif; }

/* Note : l'ancien "minimal monochrome skin" qui forçait toutes les variables
 * à blanc/noir avec !important a été supprimé - il écrasait les couleurs
 * d'enveloppe/papier choisies par l'utilisateur, rendant la personnalisation
 * invisible. Les couleurs viennent maintenant de :
 *   - render.php $stage_styles (--card-bg/--card-fg/--accent/--card-font/--env-liner)
 *   - les défauts CSS définis sur .env-* / .inv-card via var(--*, fallback)
 */
