/* ==========================================================================
   FIESTA FRANCO-MEXICANA · main.css v3 — thème clair

   ⚠ EN-TÊTE CORRIGÉ LE 18/08/2026. Il annonçait « crème · ROUGE · crème ·
   SOMBRE · rouge · crème · SOMBRE · crème · bleu CCI ». Mesuré sur la page
   rendue : 14 sections sur 14 en crème. Le rythme décrit n'existait plus —
   `.sponsors` avait été repassée au clair exprès (voir « ② LA SECTION ALLIÉS
   PASSE AU CLAIR »), `.zstack` aussi. Un commentaire qui ment coûte plus cher
   que pas de commentaire : on corrige ce qu'on lit.

   RYTHME RÉEL, et il est NARRATIF — chaque sol correspond à ce qu'on demande
   au lecteur, pas à une alternance décorative :

     SÉDUIRE    hero → expositores        crème   #F7F1E4
     DÉCIDER    programa · talleres       crème   (les billets portent le
                                                   contraste, en encre)
     PRÉPARER   práctica · plano · faq    SABLE   #F2EBDA  ← .section--prep
     SIGNER     embajadores → footer      crème, puis bleu CCI au pied de page

   Le contraste vient de l'alternance, pas d'un fond noir permanent.
   Le CSS pose les états. GSAP pilote les transitions.
   ========================================================================== */

*,*::before,*::after{box-sizing:border-box}
*{margin:0}
html{-webkit-text-size-adjust:100%}
img,svg,video{display:block;max-width:100%}
button,input{font:inherit;color:inherit}
ul{list-style:none;padding:0}
a{color:inherit;text-decoration:none}

/* ══ LES TITRES HÉRITENT LEUR COULEUR ═══════════════════════════════════
   Le build Tailwind du thème PARENT porte une règle de base
   `h1,h2{color:var(--color-heading)}`, et le parent injecte en plus un
   <style> inline qui fixe `--color-heading:#111827`. Résultat sous
   WordPress — et seulement là, l'aperçu statique ne charge pas ce
   fichier : tout <h1>/<h2> posé sur fond sombre virait au noir. Le titre
   du CTA de pied de page et le « En 1826… » du Bicentenaire, précisément,
   parce qu'ils tenaient leur crème de l'HÉRITAGE.

   ⚠ Reclasser les feuilles ne suffit PAS, et c'est le piège : l'héritage
   ne bat jamais une règle d'élément, quel que soit l'ordre du fichier.
   `.footer{color:crème}` ne peut rien contre `h2{color:#111827}` — il
   faut ANNULER la règle du parent, pas espérer passer devant.

   C'est ce que fait `color:inherit`, et c'est là que l'ordre redevient
   utile : à spécificité égale (0,0,1), c'est la dernière feuille qui
   tranche — d'où la dépendance déclarée dans functions.php pour que
   main.css s'imprime après le build parent. Les deux pièces vont
   ensemble ; l'une sans l'autre ne fait rien.

   Toute couleur de titre voulue passe par une CLASSE (`.display`,
   `.footer__h`, `.fact__k`…), qui l'emporte de toute façon. */
h1,h2,h3,h4,h5,h6{color:inherit}

body{
  font-family:var(--f-sans);font-size:var(--t-body);line-height:1.6;
  color:var(--c-ink);background:var(--c-cream);
  -webkit-font-smoothing:antialiased;overflow-x:clip;
}
html.lenis,html.lenis body{height:auto}
.lenis.lenis-smooth{scroll-behavior:auto!important}

:focus-visible{outline:3px solid var(--c-rojo);outline-offset:4px;border-radius:3px}
::selection{background:var(--c-rojo);color:#fff}

.skip{position:absolute;left:var(--s-4);top:-100px;z-index:300;background:var(--c-ink);color:var(--c-cream);padding:var(--s-3) var(--s-5);border-radius:var(--r-pill);font-weight:600}
.skip:focus{top:var(--s-4)}

.wrap{width:100%;max-width:var(--maxw);margin-inline:auto;padding-inline:var(--gutter)}
.section{padding-block:var(--section-y);position:relative}
.sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}

.label{font-size:var(--t-label);font-weight:700;letter-spacing:var(--ls-label);text-transform:uppercase;color:var(--c-rojo-txt)}

.display{
  font-family:var(--f-sans);
  /* font-variation-settings:'wdth' 122,'wght' 900; */
  font-stretch:122%;
  line-height:.94;
  letter-spacing:var(--ls-mega);text-transform:uppercase;
}
/* Instrument Serif a une hauteur d'œil bien plus faible qu'Archivo 900 :
   à taille égale il paraît deux fois plus petit. Compensation optique. */
.display em{
  font-family:var(--f-sans);font-style:italic;font-weight:400;
  font-variation-settings:normal;font-stretch:100%;
  font-size:1.18em;line-height:.82;
  text-transform:none;letter-spacing:-.005em;color:var(--c-rojo-txt);
  display:inline-block;translate:0 .045em;
}

/* ── Primitives d'animation ──────────────────────────────────────────────
   1. .split — chaque mot dans un masque   2. .curtain — rideau clip-path
   3. .fx-up — micro-translation                                         */
.split{display:inline}
/* Les capitales espagnoles portent des accents AU-DESSUS de la hauteur de
   capitale (Ú, Á, É, Ñ). Le masque doit donc respirer en haut, sinon il les
   décapite. Le padding donne la place, la marge négative garde l'interlignage. */
.split .w{display:inline-block;overflow:hidden;vertical-align:top;
  padding:.14em 0 .08em;margin:-.14em 0 -.08em}
.split .w i{display:inline-block;font-style:inherit;will-change:transform}
.js .split .w i{transform:translateY(110%)}
.curtain{clip-path:var(--clip-shown)}
.js .curtain{clip-path:var(--clip-hidden)}
.js .fx-up{opacity:0;transform:translateY(28px)}

@media (prefers-reduced-motion:reduce){
  .js .split .w i{transform:none!important}
  .js .curtain{clip-path:var(--clip-shown)!important}
  .js .fx-up{opacity:1!important;transform:none!important}
  *{animation-duration:.01ms!important;transition-duration:.01ms!important}
}

/* ── Boutons ─────────────────────────────────────────────────────────────── */
.btn{
  position:relative;display:inline-flex;align-items:center;justify-content:center;gap:var(--s-2);
  min-height:56px;padding:0 var(--s-6);border:0;border-radius:var(--r-pill);cursor:pointer;
  font-weight:700;font-size:.9375rem;letter-spacing:.02em;text-transform:uppercase;
  overflow:hidden;isolation:isolate;transition:color .4s var(--ease-out);
}
.btn::before{content:'';position:absolute;inset:0;z-index:-1;border-radius:inherit;
  background:var(--btn-fill,var(--c-ink));transform:scale(0);transform-origin:center;
  transition:transform .55s var(--ease-out)}
.btn:hover::before{transform:scale(1.05)}
.btn--primary{background:var(--c-rojo);color:#fff;--btn-fill:var(--c-rojo-lo);
  box-shadow:0 12px 30px -14px rgba(228,0,43,.7)}
.btn--ghost{background:transparent;color:var(--c-ink);box-shadow:inset 0 0 0 1.5px var(--c-line);--btn-fill:var(--c-ink);
  transition:color .4s var(--ease-out),box-shadow .25s var(--ease-out)}
/* Le contour plein disparaît au survol : ce sont les points qui le
   remplacent. Les garder tous les deux donnait deux cercles concentriques. */
.btn--ghost:hover,.btn--ghost:focus-visible{box-shadow:inset 0 0 0 1.5px transparent}
.btn--ghost:hover{color:var(--c-cream)}
.btn--sm{min-height:44px;padding:0 var(--s-5);font-size:.8125rem}
.btn--dark{background:var(--c-ink);color:var(--c-cream);--btn-fill:var(--c-rojo)}

/* ── Header ──────────────────────────────────────────────────────────────── */
/* Le header démarre AU-DESSUS du hero sombre : son texte est crème. Une
   fois collé sur le contenu clair, il repasse en encre. Deux états, une
   seule barre. */
.header:not(.is-solid){color:var(--c-cream)}
.header:not(.is-solid) .brand__sub,
.header:not(.is-solid) .lang{color:rgba(247,241,228,.72)}
.header:not(.is-solid) .lang button[aria-pressed="true"]{color:var(--c-cream)}
.header:not(.is-solid) .lang button:hover{background:rgba(247,241,228,.16)}
.header:not(.is-solid) .burger span{background:var(--c-cream)}
.header:not(.is-solid) .crosslink{background:rgba(247,241,228,.15);backdrop-filter:blur(10px);color:var(--c-cream)}
.header:not(.is-solid) .crosslink:hover{background:rgba(247,241,228,.26)}
.header:not(.is-solid) .btn--primary{box-shadow:0 0 0 1.5px rgba(247,241,228,.32),0 14px 32px -14px rgba(228,0,43,.8)}

/* ── BANDEAU DÉFILANT ───────────────────────────────────────────────────
   Vert México : la deuxième couleur dominante entre en scène dès le
   premier pixel, avant même le logotype. */
.header__banner{overflow:hidden;background:var(--c-ink-2);color:var(--c-cream);
  height:34px;display:flex;align-items:center;
  transition:height .45s var(--ease-out),opacity .3s}
.header.is-solid .header__banner{height:0;opacity:0}
.header__banner-track{display:flex;width:max-content;white-space:nowrap;
  animation:banner-run 38s linear infinite}
.header__banner-track span{display:inline-flex;align-items:center;gap:var(--s-4);
  padding-inline:var(--s-4);font-size:.6875rem;font-weight:700;
  letter-spacing:var(--ls-label);text-transform:uppercase}
.header__banner-track i{font-style:normal;color:var(--c-peach)}
.header__banner:hover .header__banner-track{animation-play-state:paused}
@keyframes banner-run{to{transform:translateX(-50%)}}
@media(prefers-reduced-motion:reduce){
  .header__banner-track{animation:none;justify-content:center;width:100%}
}

/* ── MI PASE ────────────────────────────────────────────────────────────
   Pastille discrète tant qu'elle est vide, impossible à manquer dès
   qu'un atelier est réservé. */
.header__pase{display:inline-flex;align-items:center;gap:8px;flex:none;
  padding:7px 7px 7px var(--s-4);border-radius:var(--r-pill);
  border:1px solid currentColor;font-size:.6875rem;font-weight:700;
  letter-spacing:.06em;text-transform:uppercase;
  transition:background-color .3s,color .3s}
.header__pase:hover{background:var(--c-ink-2);border-color:var(--c-ink-2);color:var(--c-cream)}
.header__pase-n{display:grid;place-items:center;min-width:22px;height:22px;
  padding-inline:6px;border-radius:var(--r-pill);
  background:var(--c-rojo);color:#fff;font-size:.8125rem;font-weight:900;
  font-variant-numeric:tabular-nums}
@media(max-width:760px){.header__pase span{display:none}
  .header__pase{padding:7px}}

.header{position:fixed;inset:0 0 auto 0;z-index:120;border-bottom:1px solid transparent;
  transition:transform .5s var(--ease-out),background-color .4s,border-color .4s}
.header.is-solid{background:rgba(247,241,228,.88);backdrop-filter:blur(16px) saturate(1.3);border-bottom-color:var(--c-line)}
.header.is-hidden{transform:translateY(-101%)}
.header__inner{display:flex;align-items:center;gap:var(--s-5);min-height:74px}
.brand{display:flex;align-items:center;gap:var(--s-3);margin-right:auto}

/* Logotype à deux variantes. Les deux images occupent la MÊME cellule de
   grille : on croise l'opacité au lieu de basculer display, ce qui évite
   tout saut de mise en page au moment où l'en-tête devient solide. */
.brand__logo-wrap{display:grid;flex:none}
.brand__logo{grid-area:1/1;height:60px;width:auto;flex:none;
  transition:opacity .4s var(--ease-out)}
.brand__logo--dark{opacity:0}
.header.is-solid .brand__logo--light{opacity:0}
.header.is-solid .brand__logo--dark{opacity:1}
@media(max-width:640px){.brand__logo{height:38px}}

/* Conservé : d'anciens gabarits peuvent encore l'utiliser. */
.brand__mark{width:32px;height:32px;flex:none}
.brand__name{font-family:var(--f-display);font-weight:900;font-variation-settings:'wdth' 112,'wght' 900;font-size:.9375rem;letter-spacing:.02em;text-transform:uppercase;line-height:1.05}
.brand__sub{display:block;font-size:.5625rem;letter-spacing:var(--ls-label);text-transform:uppercase;color:var(--c-muted);font-family:var(--f-sans);font-weight:500}
/* La navigation reste VISIBLE en permanence, y compris au-dessus du hero.
   Elle était en opacity:0 et ne se révélait qu'une fois l'en-tête devenu
   solide : en haut de page, le site n'avait donc aucun menu apparent.
   La référence artefact garde la sienne toujours affichée — logo à
   gauche, navigation à droite, une seule action. On fait pareil. */
.nav{display:flex;gap:var(--s-6);align-items:center}
.nav a{font-size:.8125rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em;position:relative;padding-block:4px}
/* Le soulignement annonçait `transition:transform` mais animait `width` :
   la propriété transitionnée n'était pas celle qui changeait, donc le
   trait apparaissait d'un coup. En scaleX il glisse, et il est composé
   par le GPU. */
.nav a::after{content:'';position:absolute;left:0;bottom:0;height:1.5px;width:100%;
  background:currentColor;transform:scaleX(0);transform-origin:left;
  transition:transform .45s var(--ease-out)}
.nav a:hover::after,.nav a:focus-visible::after{transform:scaleX(1)}
.header.is-solid .nav a::after{background:var(--c-rojo)}
.lang{display:flex;align-items:center;gap:6px;font-size:.75rem;color:var(--c-muted);letter-spacing:.06em}
.lang button{border:0;background:none;cursor:pointer;font:inherit;color:inherit;
  padding:4px 6px;border-radius:5px;transition:color .25s,background-color .25s}
.lang button:hover{background:rgba(10,37,64,.07)}
.lang button[aria-pressed="true"]{color:var(--c-ink);font-weight:700}
.menu__lang button{color:var(--c-muted-inv)}
.menu__lang button:hover{background:rgba(247,241,228,.12)}
.menu__lang button[aria-pressed="true"]{color:var(--c-cream)}
/* ── MENU MOBILE ──────────────────────────────────────────────────────────
   Sous 960 px la navigation disparaissait sans rien pour la remplacer.
   Le panneau plein écran traite la navigation comme du contenu, pas comme
   une liste comprimée dans un tiroir.                                     */
.burger{display:none;position:relative;z-index:130;width:46px;height:46px;flex:none;
  border:0;background:none;cursor:pointer;padding:0}
.burger span{position:absolute;left:11px;width:24px;height:2px;background:var(--c-ink);
  border-radius:2px;transition:transform .45s var(--ease-out),background-color .3s,transform .45s var(--ease-out)}
.burger span:nth-child(1){top:19px}
.burger span:nth-child(2){top:27px;width:16px}
.burger[aria-expanded="true"] span{background:var(--c-cream);width:24px}
.burger[aria-expanded="true"] span:nth-child(1){transform:translateY(4px) rotate(45deg)}
.burger[aria-expanded="true"] span:nth-child(2){transform:translateY(-4px) rotate(-45deg)}

.menu{position:fixed;inset:0;z-index:125;background:var(--c-ink);color:var(--c-cream);
  display:grid;grid-template-rows:1fr auto;
  padding:96px var(--gutter) calc(var(--s-6) + env(safe-area-inset-bottom));
  overflow-y:auto;clip-path:circle(0 at calc(100% - 40px) 40px)}
.menu[hidden]{display:none}
.menu__nav{display:grid;align-content:center;gap:var(--s-2)}
.menu__nav a{display:flex;align-items:baseline;gap:var(--s-4);
  font-family:var(--f-display);font-weight:900;font-variation-settings:'wdth' 118,'wght' 900;
  font-size:clamp(2.2rem,13vw,3.6rem);line-height:1.02;text-transform:uppercase;letter-spacing:-.03em;
  padding-block:var(--s-2);transition:color .3s}
.menu__nav a:active{color:var(--c-coral)}
.menu__nav i{font-style:normal;font-size:.8125rem;letter-spacing:var(--ls-label);
  color:var(--c-coral);font-family:var(--f-sans);font-weight:700;flex:none}
.menu__nav span{display:block;overflow:hidden}

.menu__foot{display:grid;gap:var(--s-4);justify-items:start;padding-top:var(--s-6);
  border-top:1px solid var(--c-line-inv)}
.menu__meta{font-size:var(--t-small);color:var(--c-muted-inv)}
.menu__foot .btn{width:100%}
.menu__lang{font-size:.875rem;color:var(--c-muted-inv);letter-spacing:.06em}
.menu__lang b{color:var(--c-cream)}
.menu__sponsors{display:grid;gap:var(--s-3);width:100%;padding-top:var(--s-4);
  border-top:1px solid var(--c-line-inv)}
.menu__sponsors .presented__label{color:var(--c-muted-inv)}
.menu__sponsors .presented__logos{gap:var(--s-6)}

@media(max-width:960px){
  .nav,.lang{display:none}
  .burger{display:block}
  .header__inner{min-height:62px}
}
@media(max-width:520px){.header__cta{display:none}}   /* la barre sticky porte déjà le CTA */
@media(min-width:961px){.menu{display:none!important}}

/* ── États du formulaire ──────────────────────────────────────────────────
   Un formulaire qui ne dit rien quand il échoue est un formulaire cassé.
   Trois états manquaient : erreur de champ, envoi en cours, échec serveur. */
.field__err{font-size:.875rem;font-weight:600;color:var(--c-rojo-txt);display:flex;align-items:center;gap:6px}
.field__err::before{content:'!';display:grid;place-items:center;width:15px;height:15px;flex:none;
  border-radius:50%;background:var(--c-rojo);color:#fff;font-size:.625rem;font-weight:800}
.field.is-error input{border-color:var(--c-rojo);background:rgba(228,0,43,.04)}
.consent.is-error{color:var(--c-rojo-txt)}

.form-alert{font-size:.875rem;font-weight:600;color:var(--c-rojo-txt);
  background:rgba(228,0,43,.08);border-left:3px solid var(--c-rojo);
  padding:var(--s-3) var(--s-4);border-radius:0 var(--r-sm) var(--r-sm) 0}
.form-alert[hidden]{display:none}

.spinner{display:none;width:17px;height:17px;border-radius:50%;flex:none;
  border:2px solid rgba(255,255,255,.35);border-top-color:#fff;animation:spin .7s linear infinite}
@keyframes spin{to{transform:rotate(360deg)}}
.btn.is-loading{pointer-events:none;opacity:.85}
.btn.is-loading .btn__label{display:none}
.btn.is-loading .spinner{display:block}
@media(prefers-reduced-motion:reduce){.spinner{animation-duration:2s}}

/* Passerelle vers la semaine B2B — l'équivalent de leur lien vers le
   festival jumeau, mais qui sert une vraie conversion : les participants
   des forums sont à trois jours du festival, et ils ont une famille. */
.crosslink{display:inline-flex;align-items:center;gap:var(--s-3);margin-inline:auto;
  padding:7px var(--s-4) 7px 7px;border-radius:var(--r-pill);
  background:var(--c-ink);color:var(--c-cream);
  font-size:.6875rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase;
  transition:background-color .3s,transform .5s var(--ease-out)}
.crosslink:hover{background:var(--c-cci);transform:translateY(-2px)}
.crosslink__mark{display:grid;place-items:center;width:26px;height:26px;border-radius:50%;flex:none;
  background:var(--c-peach);color:var(--c-ink);font-size:.5625rem;font-weight:900;letter-spacing:.04em}
@media(max-width:1180px){.crosslink{display:none}}

/* ── HERO ────────────────────────────────────────────────────────────────
   Dédoublonné : les dates, le lieu, la gratuité et les sponsors ne sont
   plus répétés à gauche ET dans le pase. Le pase porte le pratique,
   la colonne gauche porte le récit.                                      */
/* ── HERO SOMBRE ─────────────────────────────────────────────────────────
   Une porte sombre, puis le site clair. Le hero est le seul écran où la
   photo règne ; le ticker rouge assure la transition vers le crème.      */
.hero{position:relative;min-height:100svh;display:flex;align-items:flex-end;
  padding:120px 0 var(--s-7);overflow:hidden;isolation:isolate;
  background:var(--c-ink);color:var(--c-cream)}
.hero .label{color:var(--c-coral)}
.hero h1{color:var(--c-cream)}
.hero .display em{color:var(--c-coral)}
.hero__lead{color:var(--c-cream-2)}
.hero__lead em{color:var(--c-coral)}
.hero .btn--ghost{color:var(--c-cream);box-shadow:inset 0 0 0 1.5px var(--c-line-inv);--btn-fill:var(--c-cream)}
.hero .btn--ghost:hover{color:var(--c-ink)}
.hero .btn--primary{box-shadow:0 0 0 1.5px rgba(247,241,228,.3),0 14px 32px -14px rgba(228,0,43,.8)}
.hero .presented{border-top-color:var(--c-line-inv)}
.hero .presented__label{color:var(--c-muted-inv)}
.hero .count{color:var(--c-muted-inv)}
.hero .count b{color:var(--c-coral)}
.hero__rail a{color:var(--c-muted-inv)}
.hero__rail a:hover,.hero__rail a.is-on{color:var(--c-cream)}
.hero__rail a:hover::before,.hero__rail a.is-on::before{background:var(--c-coral)}
/* Le pase est posé sur du sombre : ses encoches prennent cette couleur. */
.hero .pass{--notch-bg:var(--c-ink)}
/* ── Visuel de fond ───────────────────────────────────────────────────────
   Notre hero est CLAIR et le titre est en encre : une photo pleine cadre
   non traitée le rendrait illisible. Trois couches :
     1. la photo, désaturée — elle donne la texture et l'échelle
     2. un voile crème en biais, opaque à gauche où vit la typo, ouvert à
        droite où se pose le pase
     3. les halos de couleur, atténués pour ne pas concurrencer l'image      */
.hero__photo{position:absolute;inset:0;z-index:-5;width:100%;height:100%;
  object-fit:cover;object-position:center 32%;
  filter:grayscale(1) contrast(1.18) brightness(.72);will-change:transform}
/* Un seul voile, en biais : dense à gauche où vit la typo, ouvert à droite
   où se pose le pase. Vérifié : encre sur ce fond reste à ~10:1, donc la
   photo peut garder toute son intensité. */
.hero__veil{position:absolute;inset:0;z-index:-4;pointer-events:none;
  background:
    linear-gradient(100deg,
      rgba(10,37,64,.86) 0%, rgba(10,37,64,.66) 36%,
      rgba(10,37,64,.34) 68%, rgba(10,37,64,.5) 100%),
    linear-gradient(to top, rgba(10,37,64,.92) 0%, rgba(10,37,64,.3) 34%, transparent 62%)}

/* Halos flous sur crème — l'atmosphère de festivent, pas un fond noir */
.hero__bg{position:absolute;inset:-18%;z-index:-3;
  background:
    radial-gradient(38% 34% at 84% 6%,rgba(var(--glow-azul),.26),transparent 66%),
    radial-gradient(48% 40% at 30% 108%,rgba(var(--glow-red),.34),transparent 70%),
    transparent;
  filter:blur(5px);will-change:transform}
.hero__grain{position:absolute;inset:0;z-index:-2;background-image:var(--grain);opacity:.16;mix-blend-mode:overlay;pointer-events:none}
.hero__mega{position:absolute;z-index:-1;left:50%;top:48%;translate:-50% -50%;width:100%;text-align:center;
  font-family:var(--f-display);font-weight:900;font-variation-settings:'wdth' 125,'wght' 900;
  font-size:clamp(9rem,26vw,26rem);line-height:.78;
  color:transparent;-webkit-text-stroke:1.5px rgba(10,37,64,.11);pointer-events:none;user-select:none;white-space:nowrap}
.hero__inner{display:grid;gap:var(--s-7);position:relative}
/* ⚠ `max-width:100%` EST CE QUI EMPÊCHE LE HERO DE S'ÉLARGIR SANS FIN, ET
   `min-width:0` NE SUFFIT PAS. Ce bloc est une case de grille de 350 px sur
   téléphone, mais `justify-items:center` (posé plus bas) le dimensionne au
   CONTENU au lieu de l'étirer sur sa case : il prenait la largeur de son
   enfant le plus large, quel qu'il soit, et `.hero` le rognait en silence.
   Mesuré le 04/09 sur un écran de 390 px : 2 229 px de large.

   Ce qui débordait alors n'était pas visible partout — le titre et le bouton
   sont centrés, donc ils avaient l'air normaux. Seule la liste
   « Gastronomía · Artesanías · … », plus large que l'écran à 459 px, se
   voyait tranchée aux deux bords : elle n'avait aucune raison de passer à la
   ligne puisqu'on lui offrait 2 229 px.

   Avec cette borne, chaque enfant reçoit enfin la largeur réelle de l'écran :
   la liste passe à la ligne d'elle-même, et le bandeau des partenaires — dont
   le rail fait deux fois la largeur des logos — cesse de tirer son parent.
   ⚠ `max-width:100%` NE SUFFIT PAS NON PLUS — ESSAYÉ, PIRE. Il borne la
     BOÎTE mais pas la PISTE : la colonne restait dimensionnée sur le contenu
     le plus large, et `justify-items:center` y centrait les enfants. La
     liste des tags se retrouvait alors 885 px à DROITE de son parent au lieu
     de déborder symétriquement. Une boîte étroite autour d'une piste large
     déplace le problème, elle ne le résout pas.

   `minmax(0, 1fr)` s'attaque à la piste elle-même : sa contribution en
   max-content vaut zéro, donc elle ne peut plus tirer le bloc, qui reprend
   la largeur de sa case. Chaque enfant reçoit enfin la largeur réelle de
   l'écran, la liste passe à la ligne d'elle-même, et le bandeau des
   partenaires — dont le rail fait deux fois la largeur des logos — cesse de
   tirer son parent. */
.hero__main{display:grid;grid-template-columns:minmax(0,1fr);
  gap:var(--s-6);min-width:0;align-content:center}
.hero h1{font-size:var(--t-hero);max-width:16ch}
.hero__eyebrow{display:flex;align-items:center;gap:var(--s-3);flex-wrap:wrap}
.flagrule{height:3px;width:88px;border:0;flex:none;
  background:linear-gradient(90deg,var(--c-azul) 0 20%,#fff 20% 40%,var(--c-rojo) 40% 60%,#fff 60% 80%,var(--c-verde) 80% 100%)}
.hero__actions{display:flex;flex-wrap:wrap;gap:var(--s-3);align-items:center}
/* Compte à rebours : une seule ligne discrète, plus quatre gros blocs */
.count{display:flex;align-items:baseline;gap:var(--s-2);font-size:var(--t-small);
  color:var(--c-muted);margin-top:var(--s-4);justify-content:flex-end}
.count b{font-family:var(--f-display);font-weight:900;font-variation-settings:'wdth' 112,'wght' 900;
  font-size:1.5rem;line-height:1;color:var(--c-rojo-txt);font-variant-numeric:tabular-nums}
/* Rail de navigation vertical — il se pose SOUS le logo, en haut à gauche,
   et défile avec le hero. Passé le hero, le header collant reprend la main.

   La grille en zones nommées : le rail occupe le haut, le pase la colonne
   de droite sur toute la hauteur, et le titre se pose en bas. C'est la
   distribution de la référence — haut : navigation et billet, bas : la typo. */
.hero__rail{display:none}
.hero__lead{font-size:var(--t-lead);color:var(--c-muted);max-width:34ch}
.hero__lead em{font-family:var(--f-sans);font-style:italic;color:var(--c-rojo-txt);font-size:1.12em}

@media(min-width:1100px){
  /* Le hero se resserre : le contenu vient au ras des bords, comme dans la
     référence. La typo gagne la place que les marges lui prenaient. */
  .hero{align-items:stretch;padding:82px 0 var(--s-5)}
  .hero__inner{
    grid-template-columns:150px minmax(0,1fr) 390px;
    grid-template-rows:auto 1fr;
    grid-template-areas:
      'rail .    pase'
      'main main pase';
    column-gap:var(--s-6);row-gap:var(--s-5);align-items:start;
    max-width:1600px;padding-inline:clamp(1.5rem,3vw,3rem)}
  .hero__rail{display:grid;grid-area:rail;align-content:start;gap:var(--s-3);padding-top:4px}
  .hero__main{grid-area:main;align-self:end;gap:var(--s-4)}
  /* Le billet est ancré EN HAUT et occupe le quart supérieur droit. */
  .pass{grid-area:pase;align-self:start;margin-top:-10px}
  .hero h1{max-width:none;font-size:clamp(2.8rem,8vw,10.4rem);line-height:.86}
  .hero__lead{font-size:clamp(1.05rem,1.7vw,1.45rem);max-width:40ch}
}
.hero__rail a{position:relative;font-size:.75rem;font-weight:700;letter-spacing:.12em;
  text-transform:uppercase;padding-left:var(--s-4);color:var(--c-muted);transition:color .3s}
.hero__rail a::before{content:'';position:absolute;left:0;top:50%;translate:0 -50%;
  width:7px;height:1.5px;background:currentColor;transition:width .4s var(--ease-out)}
.hero__rail a:hover,.hero__rail a.is-on{color:var(--c-ink)}
.hero__rail a:hover::before,.hero__rail a.is-on::before{width:14px;background:var(--c-rojo)}

/* Motif de papier découpé, en fond. Il descend du papel picado — le même
   geste que la perforation des billets. */
.hero__motif{position:absolute;z-index:-1;left:-5vw;bottom:-7vw;width:min(30vw,380px);
  fill:var(--c-coral);opacity:.09;pointer-events:none;rotate:-8deg}
@media(max-width:900px){.hero__motif{width:52vw;left:-14vw;bottom:-14vw}}

/* SPONSORS N1 — lockup de présentation, seul endroit du hero où ils figurent */
.presented{display:flex;align-items:center;gap:var(--s-5);flex-wrap:wrap;padding-top:var(--s-5);border-top:1px solid var(--c-line)}
.presented__label{font-size:var(--t-label);letter-spacing:var(--ls-label);text-transform:uppercase;color:var(--c-muted)}
.presented__logos{display:flex;align-items:center;gap:var(--s-6);flex-wrap:wrap}
/* Logos sponsors — fichiers client détourés (fond opaque supprimé, alpha
   déduit de l'écart de luminance) et déclinés en blanc / encre selon le fond.
   Équilibrage optique : un losange étroit et un mot-symbole large ne pèsent
   pas pareil à hauteur égale. */
.logo img{width:auto;display:block;object-fit:contain}
.logo[data-sponsor="renault"] img{height:112%}
.logo[data-sponsor="telcel"] img{height:84%}
.logo:has(img){letter-spacing:0}

.logo{display:inline-flex;align-items:center;color:inherit;
  font-family:var(--f-display);font-weight:900;font-variation-settings:'wdth' 105,'wght' 900;
  letter-spacing:.1em;text-transform:uppercase;opacity:.92;
  transition:opacity .3s,transform .5s var(--ease-out)}
.logo:hover{opacity:1;transform:translateY(-2px)}
.logo--n1{height:var(--logo-n1);font-size:clamp(.95rem,1.7vw,1.25rem)}
.logo--n2{height:var(--logo-n2);font-size:.875rem}
.logo--n3{height:var(--logo-n3);font-size:.875rem;opacity:.55}
.logo--n3:hover{opacity:1}

/* ══════════════════════════════════════════════════════════════════════════
   EL PASE — le billet du hero
   Il porte TOUT le pratique : dates, lieu, gratuité, ajout au calendrier.
   Il répond au pain point n°1 du projet — « faut-il un billet ? » — en
   montrant qu'il n'en faut pas.
   ══════════════════════════════════════════════════════════════════════════ */
.pass{
  --notch:26px;--notch-bg:var(--c-cream);
  position:relative;background:none;color:#fff;display:grid;rotate:-1.6deg;
  filter:drop-shadow(0 26px 44px rgba(228,0,43,.26));
  transition:rotate .6s var(--ease-out),translate .6s var(--ease-out);
}
.pass:hover{rotate:0deg;translate:0 -5px}
/* Bord festonné — du papier découpé, pas un code-barres. Les demi-cercles
   sont retirés du haut, comme sur un vrai billet perforé. */
.pass__picado{height:15px;background:var(--c-rojo);
  -webkit-mask:radial-gradient(circle 8px at 50% 0,transparent 98%,#000) 0 0/16px 15px repeat-x;
  mask:radial-gradient(circle 8px at 50% 0,transparent 98%,#000) 0 0/16px 15px repeat-x}
.pass__top{position:relative;background:var(--c-rojo);
  padding:var(--s-6) var(--s-6) var(--s-7);display:grid;gap:var(--s-4);justify-items:start}
/* Encoches basses du corps : le bord haut de la déchirure */
.pass__top::before,.pass__top::after{content:'';position:absolute;bottom:calc(var(--notch)/-2);
  width:var(--notch);height:var(--notch);border-radius:50%;background: var(--c-cream);z-index:3}
.pass__top::before{left:calc(var(--notch)/-2)}
.pass__top::after{right:calc(var(--notch)/-2)}
.pass__kicker{font-size:.5625rem;letter-spacing:var(--ls-label);text-transform:uppercase;opacity:.85}
.pass__dates{font-family:var(--f-display);font-weight:900;font-variation-settings:'wdth' 122,'wght' 900;
  font-size:clamp(2.4rem,3.4vw,3.5rem);line-height:.84;letter-spacing:-.045em;text-transform:uppercase}
.pass__where{font-size:.9375rem;opacity:.94;line-height:1.4}
.pass__free{display:inline-flex;align-items:center;gap:6px;background:#fff;color:var(--c-rojo-txt);
  padding:6px var(--s-3);border-radius:var(--r-pill);font-size:.625rem;font-weight:800;
  letter-spacing:var(--ls-label);text-transform:uppercase}

.pass__stub{position:relative;display:grid;gap:var(--s-3);justify-items:center;
  padding:var(--s-6) var(--s-5) var(--s-5);width:100%;
  background:var(--c-rojo-lo);color:#fff;border-radius:0 0 var(--r-md) var(--r-md);
  transition:translate .55s var(--ease-out),rotate .55s var(--ease-out)}
/* Déchirure : la souche descend et pivote. Le vide révèle le fond de page.
   ⚠ CANAL : `translate`/`rotate`, PAS `transform` — même convention que
     .pass juste au-dessus. GSAP pilote la déchirure au scroll et écrit,
     lui, dans `transform` (en style inline). Écrits dans le même canal,
     l'inline écraserait le survol et celui-ci cesserait de fonctionner dès
     le premier pixel de défilement. Séparés, les deux se COMPOSENT : le
     survol creuse la déchirure que le scroll a déjà ouverte. */
.pass:hover .pass__stub{translate:0 16px;rotate:-1.2deg}
.pass__stub::before,.pass__stub::after{content:'';position:absolute;top:calc(var(--notch)/-2);
  width:var(--notch);height:var(--notch);border-radius:50%;background:var(--c-peach);z-index:3}
.pass__stub::before{left:calc(var(--notch)/-2)}
.pass__stub::after{right:calc(var(--notch)/-2)}
.pass__perf{position:absolute;left:16px;right:16px;top:-1px;height:2px;z-index:2;opacity:.5;
  background:linear-gradient(90deg,var(--notch-bg,var(--c-cream)) 50%,transparent 0);background-size:11px 2px}
/* Action principale : Google Calendar en un clic */
.pass__cta{width:100%;border:0;cursor:pointer;background:#fff;color:var(--c-rojo-txt);
  padding:14px var(--s-4);border-radius:var(--r-pill);
  font-size:.8125rem;font-weight:800;letter-spacing:.06em;text-transform:uppercase;
  font-family:var(--f-display);font-variation-settings:'wdth' 108,'wght' 800;
  transition:background-color .3s,transform .4s var(--ease-out)}
.pass__cta:hover{background:var(--c-cream);transform:translateY(-2px)}
/* Secondaires — ↗ ouvre un onglet · ↓ télécharge (affordance avant le clic) */
.pass__alt{display:flex;align-items:center;justify-content:center;gap:var(--s-2);flex-wrap:wrap;font-size:.8125rem;opacity:.85}
.pass__alt button{display:inline-flex;align-items:center;gap:4px;border:0;background:none;color:#fff;
  cursor:pointer;font:inherit;padding:3px 7px;border-radius:var(--r-pill);
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.3);transition:background-color .25s}
.pass__alt button:hover{background:rgba(255,255,255,.18)}
.pass__alt i{font-style:normal;font-size:.8em;opacity:.75}
@media(max-width:1099px){.pass{rotate:0deg;max-width:420px}}

/* ── TICKER ──────────────────────────────────────────────────────────────── */
.ticker{overflow:hidden;background:var(--c-rojo);color:#fff;padding-block:var(--s-4)}
.ticker__row{display:flex;gap:var(--s-6);width:max-content;will-change:transform}
.ticker__row span{font-family:var(--f-display);font-weight:900;font-variation-settings:'wdth' 118,'wght' 900;
  font-size:clamp(1.1rem,2.6vw,2rem);text-transform:uppercase;letter-spacing:-.01em;white-space:nowrap;
  display:flex;align-items:center;gap:var(--s-6)}
.ticker__row span::after{content:'✦';color:rgba(255,255,255,.6);font-size:.6em}

/* ── MANIFESTO ───────────────────────────────────────────────────────────── */
/* ── MANIFESTO — titre serif géant aux mots décalés + deux colonnes ────
   L'ancienne règle `.manifesto p` imposait du display 3rem en capitales à
   TOUS les paragraphes de la section. Elle est retirée plutôt que
   surchargée : elle aurait aussi frappé l'intitulé et la citation, avec une
   spécificité (0,1,1) supérieure à `.label`. */
.manifesto{padding-block:var(--s-10)}
/* Sur mobile, le manifeste resserre ses marges verticales (demande du
   14/09/2026). Placée juste après la règle de base : même spécificité, plus
   tard dans la feuille — elle l'emporte sous 760 px sans rien changer au-dessus. */
@media(max-width:760px){.manifesto{padding-block:var(--s-1)}}
/* Le bloc prend la largeur de son contenu (~780 px) et se centre dans le
   `wrap`. C'est la disposition de la référence : leur rangée fait 920 px
   pour un conteneur de 1440, encastrée des deux côtés — pas collée à
   gauche. L'intitulé, dans le même conteneur, suit le mouvement et reste
   aligné sur le bord gauche du titre. */
.mf-block{width:fit-content;max-width:100%;margin-inline:auto}
.manifesto .label{display:block;margin-bottom:var(--s-7)}

/* ⚠ CORRECTION — `space-between` était une erreur de lecture.

   Chez Artefact, le bloc n'occupe PAS la largeur du conteneur : mesuré sur
   leur page, il fait ~920 px pour un conteneur de 1440.

       titre 327px  ·  écart 143px  ·  colonnes 2 × 210px

   Poussé aux deux bords, le nôtre creusait 353 px de vide au milieu — deux
   moitiés qui ne se parlaient plus. C'est un écart FIXE et modeste qui
   tient le titre et le texte ensemble.

   Le bloc reste calé à gauche du `wrap`, pas centré : tout le reste de la
   page (chiffres, zones, ateliers) démarre à ce bord. Le blanc à droite est
   une marge éditoriale assumée, pas un trou. */
.mf{display:flex;align-items:center;flex-wrap:wrap;
  gap:var(--s-6) clamp(2.5rem,9vw,9rem)}
.mf__word{flex:0 0 auto}


.wordcut{margin:0;color:inherit;
 font-family: var(--f-sans);font-weight:400;
  text-transform:uppercase;line-height:.8;letter-spacing:-.01em;
  /* UN seul bouton de réglage par section : --wc-size. La couleur, elle,
     est héritée — le mot est clair sur les sections sombres et foncé sur
     les claires sans qu'aucune règle n'ait à le savoir. */
  font-size:var(--wc-size,clamp(2.6rem,6.5vw,5.6rem))}
.wordcut .w{display:block;width:100%}


.wordcut{width:fit-content;max-width:100%}
.wordcut--offset .w:nth-child(2){text-align:right}

/* Les deux usages ne diffèrent que par la taille et la couleur. */
.mf__word{--wc-size:clamp(3.2rem,9.5vw,8.5rem)}

/* Colonnes calibrées sur les leurs : ~228 px chacune, soit une trentaine de
   signes. C'est étroit, et c'est voulu — c'est cette étroitesse qui fait
   que le texte se lit comme une colonne d'affiche et non comme un
   paragraphe de page. Mes 351 px précédents cassaient l'effet. */
.mf__cols{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  gap:var(--s-5);font-size:var(--t-body);line-height:1.6;
  flex:1 1 26rem;max-width:30rem}
.mf__col p{margin:0 0 var(--s-4)}
.mf__col p:last-child{margin-bottom:0}
.mf__col em{font-family:var(--f-sans);font-style:italic;font-weight:400;
  font-variation-settings:normal;color:var(--c-rojo-txt)}

.mf__quote{margin:0;color:var(--c-muted)}
.mf__quote p{margin:0 0 var(--s-3)}
.mf__quote cite{font-style:normal;font-size:var(--t-label);font-weight:700;
  letter-spacing:var(--ls-label);text-transform:uppercase;color:var(--c-ink)}

@media(max-width:1100px){
  /* Sous cette largeur, deux colonnes de 228 px deviendraient des rubans de
     quinze signes : on repasse en une seule, un peu plus large. */
  .mf__cols{grid-template-columns:1fr;flex-basis:24rem;max-width:28rem}
}
@media(max-width:900px){
  .mf{flex-direction:column;align-items:stretch;gap:var(--s-6)}
  .mf__cols{flex-basis:auto;max-width:none}
  .mf__word{--wc-size:clamp(3rem,17vw,6rem)}
}

.js .manifesto .mw{opacity:.18}

/* ── PROOF ───────────────────────────────────────────────────────────────── */
/* ⚠ LE NOMBRE DE COLONNES SUIT LE NOMBRE DE CHIFFRES. Il était figé à quatre ;
   le jour où la bande en a reçu un cinquième (le compte des marques, arrivé
   d'`expositores` le 19/08/2026), le cinquième tombait seul sur une deuxième
   rangée, avec un filet qui pendait sous lui. Le répéteur accepte n'importe
   quel nombre de lignes : la grille doit l'accepter aussi. */
.proof__grid{display:grid;grid-template-columns:repeat(var(--n,4),1fr);border-top:1px solid var(--c-line)}
.proof__i{padding:var(--s-6) var(--s-4);border-left:1px solid var(--c-line);border-bottom:1px solid var(--c-line)}
.proof__i:first-child{border-left:0}
.proof__n{font-family:var(--f-display);font-weight:900;font-variation-settings:'wdth' 118,'wght' 900;
  font-size:clamp(2.2rem,5vw,4rem);line-height:1;font-variant-numeric:tabular-nums}
.proof__l{font-size:var(--t-label);letter-spacing:var(--ls-label);text-transform:uppercase;color:var(--c-muted);margin-top:var(--s-2)}
/* Sur téléphone la bande retombe à deux colonnes ; le filet de gauche ne doit
   sauter que sur les cellules qui OUVRENT une rangée — `nth-child(3)` ne
   valait que pour quatre chiffres exactement. */
@media(max-width:760px){
  .proof__grid{grid-template-columns:repeat(2,1fr)}
  .proof__i:nth-child(odd){border-left:0}
}

/* ── ZONES · bloc SOMBRE + défilement horizontal épinglé ─────────────────
   Le contraste vient de l'alternance des sections, pas d'un fond noir
   permanent. Ici le sombre sert la dramaturgie du scroll horizontal.    */
.hz{position:relative;background:var(--c-ink-2);color:var(--c-cream)}
.hz .label{color:var(--c-coral)}
.hz__head{display:flex;justify-content:space-between;align-items:flex-end;gap:var(--s-5);padding-bottom:var(--s-6);flex-wrap:wrap}
.hz__head h2{font-size:var(--t-h2)}
.hz__head .display em{color:var(--c-coral)}
.hz__hint{font-size:var(--t-label);letter-spacing:var(--ls-label);text-transform:uppercase;color:var(--c-muted-inv)}
.hz__pin{overflow:hidden}
.hz__track{display:flex;gap:var(--s-4);width:max-content;padding-inline:var(--gutter);will-change:transform}
.hzp{position:relative;isolation:isolate;flex:none;width:min(78vw,520px);aspect-ratio:3/4;border-radius:var(--r-lg);overflow:hidden;
  padding:var(--s-6);display:flex;flex-direction:column;justify-content:flex-end;color:var(--c-cream)}
.hzp__bg{position:absolute;inset:-8%;z-index:-2;background:var(--zbg,linear-gradient(150deg,var(--c-ink-3),var(--c-ink)));will-change:transform}
.hzp::after{content:'';position:absolute;inset:0;z-index:-1;background:linear-gradient(to top,rgba(20,16,14,.94) 6%,rgba(20,16,14,.1) 62%)}
.hzp__num{font-family:var(--f-display);font-weight:900;font-variation-settings:'wdth' 125,'wght' 900;
  font-size:clamp(3.5rem,8vw,6.5rem);line-height:.8;color:transparent;-webkit-text-stroke:1.5px var(--c-coral);
  position:absolute;top:var(--s-5);right:var(--s-5);z-index:1;opacity:.85}
.hzp__name{font-family:var(--f-display);font-weight:900;font-variation-settings:'wdth' 112,'wght' 900;
  font-size:var(--t-h3);text-transform:uppercase;letter-spacing:-.02em;margin-top:var(--s-2)}
.hzp__desc{font-size:var(--t-small);color:var(--c-cream-2);margin-top:var(--s-3);max-width:34ch;
  display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}
/* SPONSORS N2 — puce contextuelle de zone */
.hzp__sponsor{display:inline-flex;align-items:center;gap:var(--s-2);align-self:flex-start;margin-top:var(--s-4);
  padding:7px var(--s-4);border-radius:var(--r-pill);background:rgba(247,241,228,.1);border:1px solid var(--c-line-inv);
  backdrop-filter:blur(8px);font-size:.625rem;letter-spacing:.12em;text-transform:uppercase;color:var(--c-cream-2)}

/* Une section épinglée doit tenir DANS le viewport, sinon tout ce qui
   dépasse est invisible : la page ne défile plus verticalement. */
@media(min-width:901px){
  .hz{min-height:100svh;display:flex;flex-direction:column;justify-content:center;padding-block:clamp(1.5rem,4vh,3rem)}
  .hz__head{padding-bottom:var(--s-5);flex:none}
  .hz__head h2{font-size:clamp(2rem,4.4vw,3.9rem)}
  .hz__pin{flex:none}
  .hzp{width:clamp(300px,29vw,420px);aspect-ratio:auto;height:min(58svh,540px);padding:var(--s-5)}
  .hzp__num{font-size:clamp(2.8rem,5.5vw,4.6rem)}
}
@media(max-width:900px){
  .hz__pin{overflow-x:auto;scroll-snap-type:x mandatory;-webkit-overflow-scrolling:touch;scrollbar-width:none}
  .hz__pin::-webkit-scrollbar{display:none}
  .hzp{scroll-snap-align:center;width:82vw}
}

/* ── PAPEL PICADO ──────────────────────────────────────────────────────────
   Le papier découpé des fêtes mexicaines et la perforation d'un billet
   parlent la même langue : du papier qu'on découpe.                     */
.picado{
  height:46px;border:0;
  background:repeating-linear-gradient(90deg,
    var(--c-rojo) 0 62px, var(--c-coral) 62px 124px,
    var(--c-rojo-lo) 124px 186px, var(--c-coral) 186px 248px);
  -webkit-mask-image:var(--picado-mask);mask-image:var(--picado-mask);
  -webkit-mask-size:62px 46px;mask-size:62px 46px;
  -webkit-mask-repeat:repeat-x;mask-repeat:repeat-x;
}

/* Section talleres : même crème que la page, hook conservé pour le JS */
/* ── SECTIONS BLEUES ────────────────────────────────────────────────────
   Le site est clair. Seules les sections qui étaient sombres deviennent
   bleues : elles ponctuent le crème au lieu de le remplacer.
   Sur elles, l'accent passe du rouge au pêche — le rouge en texte sur ce
   bleu ne fait que 2,2:1, le pêche monte à 9:1.                        */
.hz,.sponsors{color:var(--c-cream)}
.hz .label,.sponsors .label,.footer .label{color:var(--c-coral)}
.hz .display em,.sponsors .display em,.footer .display em{color:var(--c-coral)}
.sponsors .head p{color:var(--c-cream-2)}
.hz .btn--ghost,.sponsors .btn--ghost{color:var(--c-cream);box-shadow:inset 0 0 0 1.5px var(--c-line-inv);--btn-fill:var(--c-cream)}
.hz .btn--ghost:hover,.sponsors .btn--ghost:hover{color:var(--c-ink)}
/* Sur bleu, un bouton rouge n'a que 3,2:1 avec son fond : un liseré crème
   rend la limite du composant explicite (WCAG 1.4.11). */
.hz .btn--primary,.sponsors .btn--primary,.footer .btn--primary,
.stub-fab{box-shadow:0 0 0 1.5px rgba(247,241,228,.3),0 14px 32px -14px rgba(228,0,43,.75)}

/* Les deux seules sections crème : elles portent du travail (formulaire,
   carte). Sur elles, l'accent redevient rouge — le pêche n'a pas assez de
   contraste sur du clair. */
.talleres{background:var(--c-cream);color:var(--c-ink)}
.talleres .label,.plano .label{color:var(--c-rojo-txt)}
.talleres .display em,.plano .display em{color:var(--c-rojo-txt)}
.talleres .head p,.plano .head p{color:var(--c-muted)}
.talleres .btn--ghost,.plano .btn--ghost{color:var(--c-ink);box-shadow:inset 0 0 0 1.5px var(--c-line);--btn-fill:var(--c-ink)}
.talleres .btn--ghost:hover,.plano .btn--ghost:hover{color:var(--c-cream)}
.talleres .btn--primary,.plano .btn--primary{box-shadow:0 12px 30px -14px rgba(228,0,43,.7)}

/* ── TALLERES · le billet comme interface ────────────────────────────────
   Chez nous c'est littéral : le brief dit que l'e-mail de confirmation
   sert de billet à l'entrée. La carte, l'e-mail et le billet sont le
   même objet.                                                           */
.head{display:grid;gap:var(--s-4);max-width:60ch;margin-bottom:var(--s-8)}
/* Le mot découpé règle sa propre taille : sans cette exception, `.head h2`
   (0,1,1) l'emporterait sur `.wordcut` (0,1,0) et tous les titres
   reviendraient au calibre Archivo. */
.head h2:not(.wordcut){font-size:var(--t-h2)}
.head p{color:var(--c-muted);font-size:var(--t-lead)}
.tickets{display:grid;gap:var(--s-5)}

/* Aucun fond sur le conteneur : sinon la souche glisse sur la couleur du
   billet et la déchirure ne se voit pas. */
.ticket{
  --notch:24px;--notch-bg:var(--c-cream);   /* la section talleres est crème */
  /* Souche élargie : c'est la part ROUGE du billet. Plus elle pèse, plus
     la déchirure se lit — c'était le déséquilibre. */
  --stub:190px;
  position:relative;display:flex;background:none;color:var(--c-cream);
  filter:drop-shadow(0 20px 32px rgba(20,16,14,.22));
  transition:transform .6s var(--ease-out),filter .6s var(--ease-out);
}
.ticket:hover{transform:translateY(-5px);filter:drop-shadow(0 30px 46px rgba(20,16,14,.34))}
.ticket__body{position:relative;flex:1;min-width:0;padding:var(--s-6);
  display:grid;gap:var(--s-3);align-content:center;
  /* Bleu allégé : le navy profond écrasait le rouge, et la déchirure
     disparaissait dans la masse. Un bleu plus clair rend la souche au
     moins aussi présente que le corps. */
  background:var(--c-ink-3);border-radius:var(--r-md) 0 0 var(--r-md)}
.ticket__body::before,.ticket__body::after{content:'';position:absolute;right:calc(var(--notch)/-2);
  width:var(--notch);height:var(--notch);border-radius:50%;    background: var(--c-cream);z-index:3}
.ticket__body::before{top:calc(var(--notch)/-2)}
.ticket__body::after{bottom:calc(var(--notch)/-2)}
.ticket__when{font-size:var(--t-label);letter-spacing:var(--ls-label);text-transform:uppercase;color:var(--c-rosa);font-weight:800}
.ticket__title{font-family:var(--f-display);font-weight:900;font-variation-settings:'wdth' 110,'wght' 900;
  font-size:clamp(1.25rem,2.4vw,1.9rem);line-height:1.02;text-transform:uppercase;letter-spacing:-.025em}
.ticket__by{font-size:var(--t-small);color:var(--c-cream-2);display:flex;align-items:center;gap:var(--s-2);flex-wrap:wrap}
.ticket__meta{display:flex;gap:var(--s-4);flex-wrap:wrap;font-size:.75rem;letter-spacing:.1em;text-transform:uppercase;color:var(--c-muted-inv)}

/* La souche est le CTA : elle se DÉCHIRE au survol, elle ne change pas
   de couleur — la micro-interaction raconte le geste réel. */
.ticket__stub{flex:none;width:var(--stub);position:relative;
  background:var(--c-rojo);color:#fff;border:0;cursor:pointer;font:inherit;
  border-radius:0 var(--r-md) var(--r-md) 0;
  display:grid;place-content:center;justify-items:center;gap:2px;padding:var(--s-5) var(--s-4);text-align:center;
  transition:transform .55s var(--ease-out),background-color .35s}
/* ⚠ TROIS DÉFAUTS, UNE SEULE CAUSE : le `translateX(18px)`.

     · la souche glissait de 18 px hors du billet, découvrant un creux où
       apparaissait l'ombre portée du survol — le « gris » ;
     · `.curtain` pose un `clip-path` explicite sur le billet, borné à
       100 % de sa boîte : tout ce qui dépassait à droite était DÉCOUPÉ,
       d'où le texte de la souche qui disparaissait ;
     · et le décalage désalignait les encoches des deux moitiés.

   La déchirure se fait donc par ROTATION seule, charnière au bord gauche.
   Le geste reste, le billet ne se disloque plus. */
.ticket__stub{transform-origin:left center}
.ticket:hover .ticket__stub{transform:rotate(1.4deg)}
.ticket__stub:hover{background:var(--c-rojo-lo)}
.ticket__stub::before,.ticket__stub::after{content:'';position:absolute;left:calc(var(--notch)/-2);
  width:var(--notch);height:var(--notch);border-radius:50%;    background: var(--c-cream);z-index:3}
.ticket__stub::before{top:calc(var(--notch)/-2)}
.ticket__stub::after{bottom:calc(var(--notch)/-2)}
.ticket__perf{position:absolute;z-index:2;left:-1px;top:16px;bottom:16px;width:2px;opacity:.5;
  background:linear-gradient(var(--notch-bg,var(--c-cream)) 50%,transparent 0);background-size:2px 11px}
.ticket__n{font-family:var(--f-display);font-weight:900;font-variation-settings:'wdth' 118,'wght' 900;
  font-size:2.6rem;line-height:.9;font-variant-numeric:tabular-nums}
.ticket__u{font-size:.625rem;letter-spacing:var(--ls-label);text-transform:uppercase;opacity:.85}
.ticket__go{margin-top:var(--s-3);font-size:.75rem;font-weight:800;letter-spacing:.14em;text-transform:uppercase;display:flex;align-items:center;gap:6px}
.ticket__go::after{content:'→';transition:transform .4s var(--ease-out)}
.ticket:hover .ticket__go::after{transform:translateX(5px)}
/* Le folio vertical écrivait par-dessus le nombre de places et le
   « Reservar » : il n'avait pas de couloir à lui. La souche lui en
   réserve un (padding-right), il cesse de se superposer au reste. */
.ticket__stub{padding-right:26px}
.ticket__folio{position:absolute;right:9px;top:50%;translate:0 -50%;writing-mode:vertical-rl;
  font-size:.5rem;letter-spacing:.34em;text-transform:uppercase;opacity:.62}
/* Complet → la souche devient liste d'attente. Jamais un cul-de-sac. */
/* Complet : la souche prend le navy profond — elle doit se distinguer du
   corps, qui est désormais dans ce bleu-là. */
.ticket--full .ticket__stub{background:var(--c-ink)}
.ticket--full .ticket__stub:hover{background:var(--c-muted)}
.ticket--full .ticket__when{color:var(--c-muted-inv)}

.seats{display:grid;gap:var(--s-2);max-width:340px}
.seats__bar{height:6px;border-radius:var(--r-pill);background:rgba(247,241,228,.16);overflow:hidden}
.seats__fill{display:block;height:100%;width:100%;border-radius:inherit;background:var(--c-rosa);
  transform:scaleX(0);transform-origin:0 50%;will-change:transform}
.seats__txt{font-size:var(--t-small);font-weight:700;color:var(--c-rosa);font-variant-numeric:tabular-nums}
.seats--ok .seats__fill{background:#22C48A}
.seats--ok .seats__txt{color:#22C48A}
.seats--full .seats__fill{background:var(--c-muted-inv)}
.seats--full .seats__txt{color:var(--c-muted-inv)}
.live-dot{display:inline-block;width:7px;height:7px;border-radius:50%;background:var(--c-rosa);margin-right:6px;animation:pulse 2s infinite}
@keyframes pulse{0%,100%{opacity:1;transform:scale(1)}50%{opacity:.35;transform:scale(.8)}}

@media(max-width:760px){
  .ticket{flex-direction:column}
  .ticket__body{padding:var(--s-5);border-radius:var(--r-md) var(--r-md) 0 0}
  .ticket__body::before{right:calc(var(--notch)/-2);top:auto;bottom:calc(var(--notch)/-2)}
  .ticket__body::after{right:auto;left:calc(var(--notch)/-2);bottom:calc(var(--notch)/-2)}
  .ticket__stub{width:auto;border-radius:0 0 var(--r-md) var(--r-md);grid-auto-flow:column;place-content:center;gap:var(--s-4);padding:var(--s-4)}
  .ticket__stub::before{left:calc(var(--notch)/-2);top:calc(var(--notch)/-2)}
  .ticket__stub::after{left:auto;right:calc(var(--notch)/-2);top:calc(var(--notch)/-2);bottom:auto}
  .ticket__perf{left:16px;right:16px;top:-1px;bottom:auto;width:auto;height:2px;
    background:linear-gradient(90deg,var(--notch-bg,var(--c-cream)) 50%,transparent 0);background-size:11px 2px}
  .ticket:hover .ticket__stub{transform:translateY(9px)}
  .ticket__n{font-size:1.9rem}
  .ticket__go{margin-top:0}
  .ticket__folio{display:none}
}

/* ── SPONSORS N1 · section SOMBRE dédiée ────────────────────────────────
   Placée APRÈS le bloc de conversion : le visiteur est déjà engagé, donc
   c'est une impression de qualité, pas une interruption.                */
.sponsors{background:var(--c-ink-2);color:var(--c-cream)}
.sponsors .label{color:var(--c-coral)}
.sponsors .display em{color:var(--c-coral)}
.sponsors__grid{align-items:start;display:grid;gap:var(--s-4);grid-template-columns:repeat(2,1fr)}
/* ── ALIADOS · fiches éditoriales ─────────────────────────────────────
   Les cartes à dégradé bleu et halo flou étaient le seul endroit du site
   qui parlait ce langage — coins arrondis, `filter:blur(80px)`, boîte
   sombre posée sur le vert. Tout le reste de la page est plat et
   typographique : blocs de couleur francs, filets fins, titres qui
   portent.

   Les fiches s'y alignent : pas de boîte, un filet haut, le nom en grand,
   le rôle en étiquette, le texte en colonne étroite. Ce qui distingue les
   deux entrées n'est plus un cadre, c'est la hiérarchie typographique. */
.scard{position:relative;display:grid;gap:var(--s-3);align-content:start;
  padding-top:var(--s-5);border-top:2px solid var(--c-line-inv);
  transition:border-color .4s var(--ease-out)}
.scard:hover{border-top-color:var(--c-peach)}
.scard__role{order:-1;font-size:var(--t-label);letter-spacing:var(--ls-label);
  text-transform:uppercase;color:var(--c-peach)}
.scard__txt{color:var(--c-cream-2);font-size:var(--t-small);max-width:44ch;line-height:1.6}
.scard .logo--n1{font-family:var(--f-display);font-weight:900;text-transform:uppercase;
  letter-spacing:-.025em;line-height:1;font-size:clamp(1.6rem,3.4vw,2.6rem);
  color:var(--c-cream)}
.scard .logo--n2{font-family:var(--f-display);font-weight:800;text-transform:uppercase;
  letter-spacing:-.01em;font-size:clamp(.95rem,1.6vw,1.15rem);color:var(--c-cream-2)}
.arrow{display:inline-flex;align-items:center;gap:var(--s-2);font-size:var(--t-small);font-weight:800;text-transform:uppercase;letter-spacing:.06em}
.arrow::after{content:'→';transition:transform .4s var(--ease-out)}
.arrow:hover::after{transform:translateX(6px)}
@media(max-width:820px){.sponsors__grid{grid-template-columns:1fr}}

/* ── SPONSORS N3 · marcas participantes ──────────────────────────────────
   Contre-proposition au carrousel 3–5 s : défilement continu, tous les
   logos restent visibles, pause au survol, off si reduced-motion.       */
.marquee{padding-block:var(--s-7);border-block:1px solid var(--c-line);overflow:hidden}
.marquee__head{display:flex;justify-content:space-between;align-items:baseline;gap:var(--s-4);margin-bottom:var(--s-6)}
.marquee__row{display:flex;align-items:center;gap:var(--s-8);width:max-content;will-change:transform}
.marquee__row+.marquee__row{margin-top:var(--s-5)}

/* ── MODALE D'INSCRIPTION ────────────────────────────────────────────────── */
.modal{position:fixed;inset:0;z-index:200;display:grid;place-items:end center;pointer-events:none}
.modal[hidden]{display:none}
.modal__veil{position:absolute;inset:0;background:rgba(20,16,14,.7);backdrop-filter:blur(6px);opacity:0}
.modal__panel{position:relative;width:min(100%,520px);background:var(--c-cream);color:var(--c-ink);
  border-radius:var(--r-lg) var(--r-lg) 0 0;padding:var(--s-7) var(--s-6) calc(var(--s-6) + env(safe-area-inset-bottom));
  display:grid;gap:var(--s-4);transform:translateY(100%);pointer-events:auto;max-height:92svh;overflow-y:auto}
/* ⚠ SUR MOBILE C'EST UNE FEUILLE, PAS UNE BOÎTE CENTRÉE — et il faut que ça
   SE VOIE. `place-items:end center` l'ancre en bas : c'est le bon motif, elle
   arrive sous le pouce. Mais à 92svh elle *presque* remplit l'écran, et cet
   état-là se lit comme un défaut de centrage plutôt que comme une intention
   (remarque du 18/08).

   Deux correctifs, aucun ne change le motif :
   · la poignée en tête — le signal universel d'une feuille glissante. Un seul
     trait, et l'ancrage en bas devient lisible comme un choix ;
   · le rembourrage se resserre, donc la bande floue au-dessus s'élargit : on
     voit qu'il reste une page derrière, ce qui est tout le propos d'une feuille. */
/* ══ ESSAI DU 18/08 — MODALE CENTRÉE SUR MOBILE ═══════════════════════════
   Version précédente : feuille ancrée en bas (`place-items:end center`), avec
   poignée et coins arrondis en haut seulement. Motif standard sur mobile, et
   le bouton de validation tombait sous le pouce.

   Ici on centre, à la demande. Quatre réglages bougent ENSEMBLE — changer le
   seul `place-items` laisserait le panneau surgir par le bas avec deux coins
   carrés flottant au milieu de l'écran :
     ① `place-items:center` + un rembourrage sur `.modal`, sinon le panneau
        touche les bords ;
     ② les QUATRE coins arrondis ;
     ③ le départ passe de `translateY(100%)` — une hauteur entière sous
        l'écran — à 24 px : l'animation GSAP `y:0` reste identique, elle ne
        parcourt plus qu'une courte distance ;
     ④ la poignée disparaît : elle annonce une feuille glissante, elle
        mentirait sur une boîte centrée.

   ⚠ POUR REVENIR À LA FEUILLE : rétablir ce bloc depuis l'historique. Le
     compromis assumé ici est que « CONFIRMAR MI LUGAR » remonte au milieu de
     l'écran, plus loin du pouce.
   ═════════════════════════════════════════════════════════════════════════ */
@media(max-width:719px){
  .modal{place-items:center;padding:var(--s-4)}
  .modal__panel{
    border-radius:var(--r-lg);
    transform:translateY(24px);
    padding-block:var(--s-6) calc(var(--s-6) + env(safe-area-inset-bottom));
    gap:var(--s-3);
    max-height:86svh;
  }
  .modal__panel::before{display:none}
}
@media(min-width:720px){.modal{place-items:center}.modal__panel{border-radius:var(--r-lg)}}
.modal__close{position:absolute;top:var(--s-4);right:var(--s-4);width:40px;height:40px;border-radius:50%;
  border:1px solid var(--c-line);background:none;cursor:pointer;display:grid;place-items:center;font-size:1.1rem}
.modal h3{font-family:var(--f-display);font-weight:900;font-variation-settings:'wdth' 105,'wght' 900;
  font-size:1.5rem;text-transform:uppercase;letter-spacing:-.02em;line-height:1.05;padding-right:var(--s-7)}
.field{display:grid;gap:var(--s-2)}
.field label{font-size:var(--t-label);letter-spacing:var(--ls-label);text-transform:uppercase;font-weight:700;color:var(--c-muted)}
.field input{padding:15px var(--s-4);border:1.5px solid var(--c-line);border-radius:var(--r-sm);background:#fff;transition:border-color .3s}
.field input:focus{border-color:var(--c-rojo);outline:none}
.consent{display:flex;gap:var(--s-3);align-items:flex-start;font-size:var(--t-small);color:var(--c-muted);line-height:1.45}
.consent input{margin-top:3px;width:20px;height:20px;accent-color:var(--c-rojo);flex:none}
.note{font-size:var(--t-small);color:var(--c-muted);background:color-mix(in srgb,var(--c-verde) 10%,transparent);border-left:3px solid var(--c-verde);
  padding:var(--s-3) var(--s-4);border-radius:0 var(--r-sm) var(--r-sm) 0}
.modal__ok{display:none;text-align:center;gap:var(--s-4)}
.modal__ok h3{padding:0}
.is-done .modal__form{display:none}
.is-done .modal__ok{display:grid}

/* ══════════════════════════════════════════════════════════════════════════
   BICENTENARIO — section bleue, frise horizontale
   Le sens sert le désir, il ne le remplace pas : 5 jalons, 30 secondes de
   lecture. Si ça dépasse un écran, c'est trop long.
   ══════════════════════════════════════════════════════════════════════════ */
.bic{background:var(--c-ink-2);color:var(--c-cream)}
.bic .label{color:var(--c-coral)}
.bic .display em{color:var(--c-coral)}
.bic .head p{color:var(--c-cream-2)}
.bic__line{display:grid;grid-template-columns:repeat(5,1fr);gap:var(--s-4);position:relative;
  counter-reset:none;padding-top:var(--s-6)}
.bic__line::before{content:'';position:absolute;left:0;right:0;top:calc(var(--s-6) + 9px);
  height:1px;background:var(--c-line-inv)}
.bic__step{position:relative;padding-top:var(--s-5)}
.bic__step::before{content:'';position:absolute;left:0;top:3px;width:13px;height:13px;border-radius:50%;
  background:var(--c-ink-2);box-shadow:inset 0 0 0 2px var(--c-cci-lite)}
.bic__step--now::before{background:var(--c-coral);box-shadow:0 0 0 5px rgba(255,122,110,.22)}
.bic__year{font-family:var(--f-display);font-weight:900;font-variation-settings:'wdth' 118,'wght' 900;
  font-size:clamp(1.6rem,3vw,2.4rem);line-height:1;letter-spacing:-.03em;display:block;
  color:var(--c-cci-lite)}
.bic__step--now .bic__year{color:var(--c-coral)}
.bic__t{font-weight:700;font-size:var(--t-small);margin-top:var(--s-3);line-height:1.3}
.bic__d{font-size:var(--t-small);color:var(--c-cream-2);margin-top:var(--s-2);line-height:1.5}
/* Marqueur visuel des contenus en attente du client — invisible en prod. */
[data-todo]{opacity:.7;font-style:italic}
[data-todo]::before{content:'⌛ ';font-style:normal}
@media(max-width:900px){
  .bic__line{grid-template-columns:1fr;gap:var(--s-5);padding-left:var(--s-5)}
  .bic__line::before{left:6px;right:auto;top:var(--s-6);bottom:0;width:1px;height:auto}
  .bic__step{padding-top:0;padding-left:var(--s-5)}
  .bic__step::before{left:-19px;top:8px}
}

/* ══════════════════════════════════════════════════════════════════════════
   INFORMACIÓN PRÁCTICA + FAQ
   Le dernier obstacle avant la venue. La FAQ traite en tête le pain point
   n°1 : l'ambiguïté « faut-il un billet ? ».
   ══════════════════════════════════════════════════════════════════════════ */
.practica{background:var(--c-cream);color:var(--c-ink); overflow: hidden;}
.practica .label{color:var(--c-rojo-txt)}
.practica .display em{color:var(--c-rojo-txt)}
.practica .head p{color:var(--c-muted)}
/* ── PRÁCTICA · les quatre faits d'abord, en bandeau ──────────────────
   Ils étaient empilés dans une colonne étroite à gauche, en concurrence
   avec la FAQ. Or CE SONT EUX qu'on vient chercher : quand, où, combien,
   comment y aller. Quatre colonnes en tête, valeurs en gros — la réponse
   se prend d'un coup d'œil, et la FAQ récupère toute la largeur en
   dessous pour ce qui demande une phrase. */
.practica__grid{display:grid;gap:var(--s-8)}
.practica__facts{display:grid;grid-template-columns:repeat(4,1fr);
  gap:0;border-top:1px solid var(--c-line)}
.fact{padding:var(--s-5) var(--s-5) var(--s-5) 0;
  border-bottom:1px solid var(--c-line)}
@media(min-width:761px){
  .fact + .fact{padding-left:var(--s-5);border-left:1px solid var(--c-line)}
}
.fact__k{font-size:var(--t-label);letter-spacing:var(--ls-label);text-transform:uppercase;
  color:var(--c-rojo-txt);font-weight:700;margin-bottom:var(--s-2)}
.fact__v{font-family:var(--f-display);font-weight:900;font-variation-settings:'wdth' 112,'wght' 900;
  font-size:1.0625rem;text-transform:uppercase;letter-spacing:-.01em;line-height:1.2}
.fact__n{font-size:var(--t-small);color:var(--c-muted);margin-top:var(--s-2)}
.practica__cta{display:flex;gap:var(--s-3);flex-wrap:wrap;margin-top:var(--s-2)}

/* Accordéon en <details> natif : accessible au clavier et aux lecteurs
   d'écran sans une ligne de JavaScript. */
/* La FAQ prend toute la largeur : deux colonnes de questions, réponse
   sous chacune. Elle cesse d'être une colonne comprimée à droite. */
.faq{display:grid;align-content:start;
  grid-template-columns:repeat(auto-fit,minmax(min(28rem,100%),1fr));
  column-gap:var(--s-8)}
.faq__i{border-bottom:1px solid var(--c-line)}
.faq__i summary{display:flex;align-items:center;justify-content:space-between;gap:var(--s-4);
  cursor:pointer;list-style:none;padding:var(--s-4) 0;
  font-family:var(--f-display);font-weight:900;font-variation-settings:'wdth' 105,'wght' 900;
  font-size:clamp(1rem,1.8vw,1.2rem);text-transform:uppercase;letter-spacing:-.015em;line-height:1.2;
  transition:color .25s}
.faq__i summary::-webkit-details-marker{display:none}
.faq__i summary::after{content:'';flex:none;width:13px;height:13px;
  border-right:2px solid var(--c-rojo);border-bottom:2px solid var(--c-rojo);
  transform:rotate(45deg) translate(-3px,-3px);transition:transform .35s var(--ease-out)}
.faq__i[open] summary::after{transform:rotate(-135deg) translate(-3px,-3px)}
.faq__i summary:hover{color:var(--c-rojo-txt)}
.faq__i p{padding-bottom:var(--s-5);max-width:60ch;color:var(--c-muted);font-size:var(--t-small)}
.faq__i p a{color:var(--c-rojo-txt);text-decoration:underline;text-underline-offset:3px}
@media(max-width:900px){.practica__grid{grid-template-columns:1fr;gap:var(--s-6)}}

/* ══════════════════════════════════════════════════════════════════════════
   PLANO — aperçu de la carte interactive (3e page du brief)
   Le sponsor est DANS la carte : on lit « Escenario Renault » en cherchant
   son chemin. C'est le placement le plus efficace du dispositif.
   ══════════════════════════════════════════════════════════════════════════ */
/* Section plano : même crème que la page, hook conservé pour le JS */
.plano{background:var(--c-cream);color:var(--c-ink)}
.plano__filters{display:flex;gap:var(--s-2);flex-wrap:wrap;margin-bottom:var(--s-5)}
.chip{border:0;cursor:pointer;font:inherit;font-size:.875rem;font-weight:700;
  padding:9px var(--s-4);border-radius:var(--r-pill);background:rgba(20,16,14,.06);color:var(--c-ink);
  transition:background-color .25s,color .25s}
.chip:hover{background:rgba(20,16,14,.12)}
.chip.is-on{background:var(--c-ink);color:var(--c-cream)}

/* ⚠ 1.5fr ET NON 1.9fr DEPUIS LE 18/08. La colonne de droite ne porte plus
   seulement six lignes de légende : elle porte les MARQUES, dépliées sous leur
   zone. À 1.9fr elle tombait sous 350 px, où un logo, un nom, une description
   et un numéro de stand ne tiennent pas sur une ligne. Le schéma perd un peu
   de largeur et n'en souffre pas — son viewBox est vectoriel. */
.plano__grid{display:grid;grid-template-columns:1.5fr 1fr;gap:var(--s-5);align-items:start}
.plano__map{background:#EFE7D6;border-radius:var(--r-lg);padding:var(--s-4);margin:0;overflow:hidden}
.plano__map svg{width:100%;height:auto;display:block}

/* ⚠ LE VERT DU PARC SUIT LE JETON, il ne le recopie plus. Il était écrit
   `rgba(0,135,92,.09)` — c'est-à-dire #00875C, l'ANCIENNE valeur de
   `--c-verde`, restée dans `assets/css/tokens.css` qui n'est plus chargé. Le
   jeton vivant (build Tailwind) vaut #005D4F : le plan portait donc un vert
   que plus rien d'autre sur le site n'utilisait. Écart invisible à 9 %
   d'opacité, franc partout ailleurs. */
.pk{fill:color-mix(in srgb,var(--c-verde) 9%,transparent);stroke:rgba(20,16,14,.22);stroke-width:2}   /* parc */
.pt{fill:none;stroke:rgba(20,16,14,.2);stroke-width:3;stroke-dasharray:9 9}/* senderos */
.pz rect{fill:rgba(20,16,14,.055);stroke:rgba(20,16,14,.3);stroke-width:1.6;
  transition:fill .3s var(--ease-out),stroke .3s var(--ease-out)}
.pz{cursor:pointer}
.pz:hover rect,.pz.is-hi rect{fill:rgba(228,0,43,.13);stroke:var(--c-rojo)}
.pn{font-family:var(--f-display);font-weight:900;font-size:30px;fill:rgba(20,16,14,.28);letter-spacing:-.03em}
.pz:hover .pn,.pz.is-hi .pn{fill:var(--c-rojo)}
.pl{font-family:var(--f-display);font-weight:900;font-size:23px;fill:var(--c-ink);
  text-transform:uppercase;letter-spacing:-.02em}
.ps{font-family:var(--f-display);font-weight:900;font-size:23px;fill:var(--c-rojo-txt);
  text-transform:uppercase;letter-spacing:-.02em}   /* le nom du sponsor */
.pi{font-family:var(--f-sans);font-size:14px;fill:var(--c-muted);letter-spacing:.06em;text-transform:uppercase}
.pe circle{fill:var(--c-rojo)}
/* Filtre : les zones non concernées s'effacent au lieu de disparaître */
.plano__map[data-filter-target]:not([data-filter-target="all"]) .pz{opacity:.2;transition:opacity .35s}

/* ⚠ CORRIGÉ LE 18/08/2026 — LA SÉLECTION S'EFFAÇAIT AVEC LES AUTRES.
   `.plano__map .pz.is-match` pèse (0,3,0) ; la règle d'effacement au-dessus
   pèse (0,4,0) — un attribut et un `:not()` comptent chacun au niveau classe.
   La plus SPÉCIFIQUE gagne, et l'ordre des règles n'y change rien : filtrer
   sur « Gourmet » faisait pâlir les six zones, Gourmet comprise. Mesuré :
   opacité 0,2 partout, `is-match` bien posée sur la bonne zone.

   Le bug dormait depuis la conception du plan : rien ne rendait les puces de
   filtre, donc il ne s'est jamais déclenché. Il s'est réveillé le jour où le
   bloc a émis le balisage que le CSS attendait.

   La correction reprend le MÊME préfixe et ajoute `.is-match` — (0,5,0) —
   plutôt qu'un `!important` qui gagnerait sans dire pourquoi. */
.plano__map[data-filter-target]:not([data-filter-target="all"]) .pz.is-match{opacity:1}

/* ⚠ LES STYLES ONT MIGRÉ DU `<li>` VERS LE `<button>` LE 18/08/2026.
   La ligne portait `cursor:pointer` sans être actionnable : elle promettait un
   clic qu'aucun code n'écoutait, et restait injoignable au clavier. C'est
   maintenant un vrai contrôle qui filtre — le `<li>` n'est plus qu'un élément
   de liste, et il garde `is-hi` (posé par le survol croisé avec la carte). */
.plano__legend{display:grid;gap:2px;counter-reset:z;margin:0;padding:0;list-style:none}
.plano__legend button{display:flex;gap:var(--s-3);align-items:flex-start;width:100%;
  padding:var(--s-3);border:0;border-radius:var(--r-sm);background:none;
  font:inherit;color:inherit;text-align:left;cursor:pointer;
  transition:background-color .25s}
.plano__legend button:hover,.plano__legend li.is-hi button{background:rgba(228,0,43,.08)}
.plano__legend li.is-on button{background:rgba(228,0,43,.13)}
/* ⚠ PORTÉE RESSERRÉE AU BOUTON DE ZONE LE 18/08. Écrites `.plano__legend b`
   et `.plano__legend span`, ces règles pèsent (0,1,1) et écrasaient
   `.stand__name` et `.stand__what` (0,1,0), qui vivent MAINTENANT dans la
   légende : les noms de marques héritaient du rouge et du corps de la légende,
   les descriptions passaient de 12 à 15 px en gras. Le `>` limite la portée à
   la ligne de zone, sans toucher aux poids. */
.plano__legend button > b{font-family:var(--f-display);font-weight:900;font-size:.9375rem;color:var(--c-rojo-txt);flex:none;padding-top:2px}
.plano__legend button > span{display:grid;font-weight:650;font-size:.9375rem;line-height:1.25}
.plano__legend button > span > i{font-style:normal;font-weight:400;font-size:.8125rem;color:var(--c-muted);margin-top:3px}
/* Le compte — la seule chose que la légende dit et que le répertoire tait.
   Aligné à droite et en chiffres tabulaires : on le compare d'une zone à
   l'autre sans lire. */
.plano__legend .zn{margin-left:auto;flex:none;align-self:center;
  font-style:normal;font-size:.875rem;font-weight:700;
  font-variant-numeric:tabular-nums;letter-spacing:.03em;
  color:var(--c-ink);background:rgba(20,16,14,.07);
  padding:3px 9px;border-radius:var(--r-pill);white-space:nowrap}

.plano__cta{display:flex;align-items:center;gap:var(--s-4);flex-wrap:wrap;margin-top:var(--s-6)}
.plano__note{font-size:var(--t-small);color:var(--c-muted)}
@media(max-width:900px){.plano__grid{grid-template-columns:1fr}}

/* ── LES MARQUES, REPLIÉES SOUS LEUR ZONE ─────────────────────────────────
   Le répertoire autonome (`.plano__dir`, en bas de section) a été SUPPRIMÉ le
   18/08 : il répétait la structure de la légende et obligeait à descendre de
   deux écrans pour relier une zone de la carte à ses marques. Les mêmes lignes
   vivent maintenant dans un panneau dépliable sous chaque ligne de légende.

   ⚠ ON N'ANIME PAS LA HAUTEUR. Le panneau s'affiche par `hidden` — instantané,
   et il sort de l'arbre d'accessibilité quand il est fermé. Seul le CONTENU
   entre en scène, sur `opacity` et `transform` uniquement. */
.zpanel[hidden]{display:none}
/* ⚠ LE RETRAIT SE DÉCIDE AU NIVEAU DE LA LISTE, PAS DU PANNEAU. Sans logos,
   il vaut la largeur de la colonne des numéros : les noms de marques tombent
   alors sous le nom de la zone. Avec logos, il retombe à zéro — c'est le LOGO
   qui prend la place du numéro. Le rythme reste le même dans les deux cas :
   une colonne d'identifiants à gauche, les noms juste après.

   Cumuler les deux retraits poussait les noms de marques 75 px à droite des
   noms de zone : deux hiérarchies au lieu d'une. */
.zpanel{padding:0 var(--s-3) var(--s-3) 0}
.zpanel .plano__stands{animation:zpIn .34s var(--ease-out) both}
@keyframes zpIn{from{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:none}}
@media(prefers-reduced-motion:reduce){
  .zpanel .plano__stands{animation:none}
}

/* La ligne « Por ubicar » : hors numérotation, et signalée comme un manque. */
.zorphan .zn{background:rgba(228,0,43,.12);color:var(--c-rojo-txt)}
.zorphan b{opacity:.45}

.plano__stands{display:grid;gap:2px;margin:0;list-style:none;
  padding:0 0 0 calc(var(--s-3) + 1.9rem)}
.plano__stands.has-logos{padding-left:var(--s-3)}
.stand{display:flex;align-items:center;gap:var(--s-3);
  padding:var(--s-2) var(--s-3);border-radius:var(--r-sm);
  transition:background-color .25s var(--ease-out)}
.stand:hover{background:rgba(228,0,43,.06)}
/* ⚠ LE LOGO EST FACULTATIF et ne conditionne rien : sans lui, le nom porte la
   ligne. Mais la BOÎTE, elle, est réservée pour toute la liste dès qu'une seule
   marque en a un (`.has-logos`, décidé en PHP zone par zone) — sinon la ligne
   qui porte un logo décale son nom d'une soixantaine de pixels et la colonne
   cesse d'être balayable.

   ⚠ 52×40 ET NON 34×34. À 34 px un logo réduit n'est plus reconnaissable :
   il coûtait sa place sans rendre le service pour lequel il est là. Le format
   est paysage parce que la plupart des logos de marque le sont — un carré
   bride les mots-symboles allongés à un tiers de leur lisibilité. */
.stand__logo{flex:none;width:52px;height:40px;display:grid;place-items:center}
.plano__stands:not(.has-logos) .stand__logo{display:none}
.stand__logo img{max-width:100%;max-height:100%;width:auto;height:auto;object-fit:contain}
.stand__txt{display:grid;min-width:0}
.stand__name{font-weight:700;font-size:.875rem;line-height:1.25;overflow-wrap:anywhere}
/* Dans une colonne étroite, la description PASSE À LA LIGNE au lieu d'être
   coupée : deux lignes lisibles valent mieux qu'une phrase tronquée au tiers. */
.stand__what{font-size:var(--t-small);color:var(--c-muted);line-height:1.35;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
/* `tabular-nums` : des numéros de stand alignés se comparent d'un coup d'œil. */
.stand__no{margin-left:auto;flex:none;font-family:var(--f-display);font-weight:900;
  font-variation-settings:'wdth' 108,'wght' 900;
  font-size:.875rem;font-variant-numeric:tabular-nums;
  color:var(--c-ink);background:rgba(20,16,14,.07);
  padding:3px 9px;border-radius:var(--r-pill)}

/* ══════════════════════════════════════════════════════════════════════════
   FOOTER — la zone institutionnelle : c'est ici que la CCI signe.
   Fond bleu CCI officiel, réchauffé par des halos orange (jamais rouges :
   #E4002B sur #004079 donne 1,55:1, ça vibre et ça ne se lit pas).
   ══════════════════════════════════════════════════════════════════════════ */
/* ── LE PIED DE PAGE SE DÉCOUVRE ──────────────────────────────────────
   La bande sponsors ne défile pas avec le reste : elle reste collée au bas
   du viewport, DERRIÈRE la couche claire, et se libère à mesure que
   celle-ci remonte.

   Deux conditions, et l'une était bloquante :
     · `overflow:hidden` a quitté `.footer`. Un ancêtre en overflow:hidden
       devient un conteneur de défilement, et `position:sticky` s'y résout
       — l'effet ne partait tout simplement pas. Le clip ne servait qu'aux
       dégradés de `.footer__bg` : il est descendu sur `.footer__stage`,
       qui ne contient pas la bande.
     · la couche claire a besoin d'un fond OPAQUE à elle. Tant que le fond
       vivait sur `.footer`, `.wrap` était transparent : il n'y avait rien
       pour masquer ce qui se tenait dessous.

   Aucun JS, aucun ScrollTrigger : `sticky` avec `bottom` décale l'élément
   VERS LE HAUT par rapport à son flux, dans la limite de son conteneur.
   C'est exactement le mouvement recherché, et il se dégrade en pied de
   page normal partout où `sticky` n'existe pas. */
.footer{position:relative;isolation:isolate;
  background:var(--c-cci);color:var(--c-cream)}

.footer__stage{position:relative;z-index:1;
  isolation:isolate;overflow:hidden;
  background:var(--c-cci);padding-top:var(--s-9)}
.footer__bg{position:absolute;inset:-20%;z-index:-1;pointer-events:none;
  background:
    radial-gradient(36% 32% at 78% 6%,rgba(0,84,72,.55),transparent 66%),
    radial-gradient(40% 34% at 14% 18%,rgba(0,72,156,.42),transparent 68%),
    radial-gradient(32% 28% at 58% 46%,rgba(127,176,224,.30),transparent 66%),
    radial-gradient(50% 40% at 34% 98%,rgba(0,48,92,.75),transparent 70%);
  filter:blur(10px)}

/* Filets qui se tracent au scroll — leurs <svg><line pathLength> en CSS pur */
.rule{position:absolute;background:rgba(127,176,224,.34);transform-origin:0 0}
.rule--h{left:0;top:0;height:1px;width:100%;transform:scaleX(0)}
.rule--v{left:0;top:0;width:1px;height:100%;transform:scaleY(0)}
.rule.is-drawn{transform:none;transition:transform 1.1s var(--ease-out)}
@media(prefers-reduced-motion:reduce){.rule{transform:none!important}}

/* ── CTA de clôture · centré, pleine largeur, comme Festivent ───────────── */
footer .footer__cta{display:grid;justify-items:center;gap:var(--s-5);text-align:center;
  width:100%;max-width:min(100%,980px);margin-inline:auto;padding-bottom:var(--s-9)}

.footer__cta{display:grid;justify-items:center;gap:var(--s-5);text-align:center;
  width:100%;max-width:min(100%,980px);margin-inline:auto;padding-bottom:0px;}
.footer__cta h2{font-size:clamp(2.2rem,5.4vw,4.8rem);max-width:none}
.footer__cta .display em{color:var(--c-coral)}
/* L'étiquette collée de travers — leur « EN VENTE DÈS MAINTENANT! »

   ⚠ SES COULEURS SONT DES VARIABLES, PAS DES VALEURS — 19/08/2026.
   Elle est née dans le pied de page, sur le bleu institutionnel : crème sur
   bleu, 9,79:1, une étiquette parfaitement lisible. Le bloc `cta` a ensuite
   réutilisé la même classe sur une SECTION DE PAGE, qui est crème. Résultat
   mesuré : crème sur crème, contraste 1,00:1 — la forme n'existait plus,
   seul flottait son texte rouge. Constaté sur expositores, talleres, prensa,
   ser-patrocinador et les pages atelier, soit les cinq occurrences hors pied
   de page.

   L'identité de cette étiquette n'est pas « crème », c'est « une forme qui
   TRANCHE sur son fond ». Sur fond clair, la réponse est donc l'inversion,
   pas une autre teinte claire : sand et peach ont été calculés, ils donnent
   1,16:1 et 1,09:1 — le texte redevient lisible mais la forme reste absente.

   ⚠ ET SURTOUT PAS DU ROUGE PLEIN. Dans chaque bloc `cta`, à quelques
   centimètres au-dessous, il y a DÉJÀ un objet rouge plein : le bouton
   principal. Vérifié sur les quatre pages. Une étiquette rouge lui volerait
   la vedette, alors que c'est lui qui doit gagner.

   Passer par `--sticker-bg` / `--sticker-fg` plutôt que de surcharger
   `background` : le jour où une variante sombre du bloc `cta` existera, elle
   redéclarera les deux jetons au lieu de se battre en spécificité. */
.sticker{display:inline-block;
  --sticker-bg:var(--c-cream);--sticker-fg:var(--c-rojo-txt);
  background:var(--sticker-bg);color:var(--sticker-fg);
  padding:9px var(--s-4);rotate:-2.2deg;
  font-size:var(--t-label);font-weight:800;letter-spacing:var(--ls-label);text-transform:uppercase}

/* Hors pied de page, l'étiquette est posée sur du crème : les rôles
   s'inversent. 13,62:1 pour la forme, contre 1,00:1 avant. */
.cta-block .sticker{--sticker-bg:var(--c-ink);--sticker-fg:var(--c-cream)}

/* ⚠ `.btn-glow` A ÉTÉ SUPPRIMÉ — c'était un DOUBLON de `.btn`.
   Il avait sa propre pilule, son propre survol, et son propre liseré :
   un <svg><rect pathLength="100"> écrit à la main dans le gabarit. Or
   `pathLength` n'est honoré que sur <path> — ce que le bloc `.btn > .glow`
   plus bas explique déjà, parce que le bug y avait DÉJÀ été rencontré et
   corrigé. Ignoré sur le <rect>, `stroke-dasharray` se lisait en pixels :
   un motif de 100 px sur un contour de 567, qui ne bouclait pas. C'était
   l'arc blanc à couture du pied de page.

   Les deux boutons sont passés en `.btn--primary` / `.btn--ghost`. Ils
   reçoivent donc le liseré injecté par main.js, recalculé à leur taille
   et remesuré au changement de langue. Un composant en moins, un bug en
   moins, et le pied de page se survole comme le reste du site. */

/* Le conteneur du liseré reste : il sert au `.btn > .glow` injecté. */
.glow{position:absolute;inset:0;width:100%;height:100%;overflow:visible;pointer-events:none}

.footer__main{display:grid;grid-template-columns:1fr 1.15fr;gap:var(--s-8)}
.footer__col{position:relative;display:grid;gap:var(--s-5);align-content:start}
.footer__col--nav{padding-left:var(--s-8);gap:0}
.footer__col > .btn{justify-self:start}
.footer__h{font-family:var(--f-display);font-weight:900;font-variation-settings:'wdth' 112,'wght' 900;
  font-size:clamp(1.5rem,2.6vw,2.2rem);line-height:1.02;text-transform:uppercase;letter-spacing:-.02em}
.footer__row{position:relative;padding-block:var(--s-6)}

.footer__nav{display:grid;gap:var(--s-2)}
.footer__nav a{font-family:var(--f-display);font-weight:900;font-variation-settings:'wdth' 112,'wght' 900;
  font-size:clamp(1.3rem,2.4vw,2rem);text-transform:uppercase;letter-spacing:-.02em;
  width:max-content;transition:color .3s,transform .45s var(--ease-out)}
.footer__nav a:hover{color:var(--c-cci-lite);transform:translateX(8px)}
.footer__subgrid{display:grid;grid-template-columns:repeat(2,1fr);gap:var(--s-6)}
.footer__navtitle{font-size:var(--t-label);letter-spacing:var(--ls-label);text-transform:uppercase;
  color:var(--c-cci-lite);margin-bottom:var(--s-3)}
.footer__sub{display:grid;gap:var(--s-2);font-size:var(--t-small)}
.footer__sub a{color:rgba(247,241,228,.84);width:max-content;transition:color .25s}
.footer__sub a:hover{color:var(--c-cci-lite)}

.footer__socials{display:flex;gap:var(--s-3)}
.footer__socials a{display:grid;place-items:center;width:44px;height:44px;border-radius:50%;
  box-shadow:inset 0 0 0 1px rgba(127,176,224,.4);
  transition:background-color .3s,color .3s,transform .45s var(--ease-out)}
.footer__socials a:hover{background:var(--c-cream);color:var(--c-cci);transform:translateY(-3px)}
.footer__socials svg{width:22px;height:22px;fill:currentColor}

.footer__legal{display:grid;grid-template-columns:1.4fr 1fr auto;gap:var(--s-4);align-items:center;
  margin-top:var(--s-8);padding-block:var(--s-5);border-top:1px solid rgba(127,176,224,.3);
  font-size:.875rem;color:rgba(247,241,228,.7)}
.footer__legal div:first-child{display:flex;gap:var(--s-5);flex-wrap:wrap}
.footer__legal a:hover{color:var(--c-cci-lite)}
.to-top{width:44px;height:44px;border-radius:50%;border:0;cursor:pointer;background:none;color:var(--c-cream);
  box-shadow:inset 0 0 0 1px rgba(127,176,224,.4);font-size:1.1rem;
  transition:background-color .3s,color .3s,transform .45s var(--ease-out)}
.to-top:hover{background:var(--c-cream);color:var(--c-cci);transform:translateY(-3px)}

@media(max-width:960px){
  .footer__main{grid-template-columns:1fr}
  .footer__col--nav{padding-left:0}
  .rule--v{display:none}
  .footer__legal{grid-template-columns:1fr;text-align:left}
}
@media(max-width:640px){.footer__subgrid{grid-template-columns:1fr}}

/* ── BAS DE FOOTER · remerciement partenaires ────────────────────────────
   Bleu plus profond que le footer : la rangée se détache sans changer de
   famille de couleur.  */
/* `margin-top` supprimé : il poussait la bande vers le bas et mangeait
   d'autant la distance sur laquelle elle peut remonter. La séparation ne
   vient plus d'un écart, elle vient du recouvrement lui-même. */
.thanks{position:sticky;bottom:0;z-index:0;
  background:var(--c-cci-deep);
  border-top:1px solid rgba(127,176,224,.28)}

/* MOBILE — le mot est un vrai titre : il passe AVANT les plaques et se lit
   (#7FB0E0, 4,57:1). Superposer sur 375 px écraserait les deux. */
.thanks__marquee{overflow:hidden;padding-block:var(--s-5) var(--s-3)}
.thanks__row{gap:var(--s-7)}
.thanks__row span{font-family:var(--f-display);font-weight:900;font-variation-settings:'wdth' 122,'wght' 900;
  font-size:clamp(1.8rem,5.4vw,4.4rem);line-height:.9;text-transform:uppercase;letter-spacing:-.04em;
  color:var(--c-cci-lite);white-space:nowrap;display:flex;align-items:center;gap:var(--s-7)}
.thanks__row span::after{content:'✦';color:var(--c-coral);font-size:.34em}

/* Le message d'abord, les deux logos EN DESSOUS, seuls et centrés. */
.thanks__logos{display:flex;align-items:center;justify-content:center;
  gap:clamp(var(--s-5),5vw,var(--s-8));flex-wrap:wrap;padding-block:var(--s-7) var(--s-5)}

/* ── ≥960px · LE MOT PASSE DERRIÈRE LES PLAQUES ─────────────────────────
   Le geste de Festivent : le titre cesse d'être une ligne à lire pour
   devenir la TEXTURE du fond, et les logos flottent dessus. Deux
   conditions pour que ce soit une amélioration et pas une bouillie :
     · le contraste tombe au ras du fond (rgba(127,176,224,.20) sur
       #00305C ≈ 1,3:1). C'est délibéré : `.thanks__marquee` porte déjà
       aria-hidden, le mot n'est plus une information — les plaques et
       leurs `alt` le sont, et la section #patrocinadores nomme la chose ;
     · les logos sont sur plaque BLANCHE opaque. Sans elle, un logo
       détouré posé sur un mot de 10 rem serait illisible.
   Superposition par GRILLE, pas par `position:absolute` : les deux
   couches partagent la même cellule, la hauteur de bande reste dictée par
   son contenu, et rien n'est sorti du flux.
   Le seuil est 960 et non 768 : c'est la largeur à partir de laquelle les
   six plaques tiennent sur UNE ligne. En dessous elles passent à deux
   rangées, et le mot se retrouve coincé dans l'interligne — la
   superposition n'a de sens que sur une rangée unique. */
@media(min-width:960px){
  .thanks{display:grid;grid-template-columns:100%}
  /* Les deux rangées défilantes sont en `width:max-content` : sans
     `min-width:0`, un élément de grille refuse de descendre sous la
     largeur de son contenu et la bande déborderait en largeur. */
  .thanks > *{min-width:0}

  .thanks__marquee{grid-row:1;grid-column:1;align-self:center;padding-block:0}
  .thanks__row{gap:clamp(3rem,6vw,7rem)}
  .thanks__row span{font-size:clamp(4rem,11vw,10rem);letter-spacing:-.05em;
    color:rgba(127,176,224,.20);gap:clamp(3rem,6vw,7rem)}
  /* Le ✦ corail était l'accent le plus clair de la ligne : sur une couche
     de fond, il redeviendrait le point le plus lumineux de la bande. */
  .thanks__row span::after{color:inherit}

  /* Même cellule, au-dessus. Le padding donne à la bande sa hauteur — le
     mot, plus court, s'y centre. */
  .thanks__logos{grid-row:1;grid-column:1;align-self:center;
    position:relative;z-index:1;
    padding-block:clamp(3.5rem,7vw,5.5rem)}

  .thanks__brands{grid-row:2;grid-column:1}
}
.thanks__sep{width:1px;height:44px;background:rgba(127,176,224,.4);flex:none}
@media(max-width:640px){.thanks__sep{width:44px;height:1px}}

/* Bloc de marque — gabarit d'un vrai lockup sponsor : un signe + un
   mot-symbole. Échanger le <svg> contre le fichier officiel suffit. */
/* Les vrais logos, à hauteur optique équivalente (le losange Renault est
   plus étroit que le mot-symbole Telcel : il est donc un peu plus haut). */
.brandmark{display:inline-flex;align-items:center;opacity:.96;
  transition:opacity .3s,transform .5s var(--ease-out)}
.brandmark:hover{opacity:1;transform:translateY(-4px)}
.brandmark img{width:auto;display:block}
.brandmark[data-sponsor="renault"] img{height:clamp(48px,5.6vw,70px)}
.brandmark[data-sponsor="telcel"] img{height:clamp(36px,4.2vw,52px)}

.thanks__note{text-align:center;font-size:var(--t-label);letter-spacing:var(--ls-label);
  text-transform:uppercase;color:var(--c-cci-lite);padding-bottom:var(--s-6)}

/* NIVEAU 3 · les 40 marques, sous les deux principales. Un seul endroit
   parle des partenaires, et la hiérarchie s'y lit d'un coup d'œil. */
.thanks__brands{border-top:1px solid rgba(127,176,224,.24);padding-block:var(--s-5) var(--s-7);overflow:hidden}
.thanks__brandstitle{text-align:center;font-size:var(--t-label);letter-spacing:var(--ls-label);
  text-transform:uppercase;color:rgba(247,241,228,.55);margin-bottom:var(--s-5)}
.thanks__brandsrow{display:flex;align-items:center;gap:var(--s-7);width:max-content;
  color:var(--c-cream);will-change:transform}
/* L'opacité était le seul levier pour rendre cette rangée plus discrète
   que celle du dessus, tant qu'elle n'affichait que du texte. Sur une
   plaque blanche elle donne un gris sale : c'est la HAUTEUR de plaque qui
   porte la hiérarchie désormais (26–36 px ici, 38–54 px au-dessus). */
.thanks__brandsrow .logo--n3{opacity:1;font-size:.9375rem}

/* Signature de l'organisateur — comble la colonne gauche du footer */
.footer__org{position:relative;display:grid;gap:var(--s-3);margin-top:var(--s-6);padding-top:var(--s-6);max-width:40ch}
.footer__orgname{font-family:var(--f-display);font-weight:900;font-variation-settings:'wdth' 112,'wght' 900;
  font-size:1.15rem;text-transform:uppercase;letter-spacing:.02em}
/* `li` autant que `p` : dès qu'un champ d'éditeur peut apparaître dans un
   bloc, styler le seul `p` laisse les listes à la taille du corps — deux
   fois plus grosses que le texte qui les précède. Le cas s'est présenté ici
   à la première liste saisie en back-office. */
.footer__org p,
.footer__org li,
/* Le CONTENEUR aussi, et pas seulement ses paragraphes : un champ
   d'éditeur peut renvoyer du texte nu, sans balise. C'est arrivé — la note
   sortait à 16 px dans un bloc réglé à 13, parce qu'aucun `p` n'existait
   pour porter la règle. */
.footer__org .rte{font-size:.8125rem;color:rgba(247,241,228,.72);line-height:1.5}
.footer__org a{text-decoration:underline;text-underline-offset:3px}
.footer__org a:hover{color:var(--c-cci-lite)}

/* ── CTA sticky mobile ───────────────────────────────────────────────────── */
.sticky{position:fixed;inset:auto 0 0 0;z-index:110;display:none;gap:var(--s-3);
  padding:var(--s-3) var(--s-4) calc(var(--s-3) + env(safe-area-inset-bottom));
  background:rgba(247,241,228,.95);backdrop-filter:blur(16px);border-top:1px solid var(--c-line);transform:translateY(110%)}
.sticky .btn{flex:1}
@media(max-width:900px){.sticky{display:flex}}

/* ── SOUCHE FLOTTANTE ────────────────────────────────────────────────────── */
.stub-fab{position:fixed;right:26px;bottom:26px;z-index:105;display:none;
  background:var(--c-rojo);color:#fff;border:0;cursor:pointer;font:inherit;
  padding:var(--s-4) var(--s-5);border-radius:var(--r-sm);text-align:center;
  rotate:-3deg;opacity:0;translate:0 24px;
  transition:rotate .5s var(--ease-out),scale .5s var(--ease-out),background-color .3s;
  box-shadow:0 20px 44px -18px rgba(228,0,43,.6)}
.stub-fab:hover{rotate:0deg;scale:1.04;background:var(--c-rojo-lo)}
.stub-fab::before,.stub-fab::after{content:'';position:absolute;left:-9px;width:18px;height:18px;border-radius:50%;background:var(--c-cream)}
.stub-fab::before{top:-9px}.stub-fab::after{bottom:-9px}
.stub-fab b{display:block;font-family:var(--f-display);font-weight:900;font-variation-settings:'wdth' 118,'wght' 900;
  font-size:.9375rem;letter-spacing:.04em;text-transform:uppercase}
.stub-fab span{display:block;font-size:.5625rem;letter-spacing:var(--ls-label);text-transform:uppercase;opacity:.85;margin-top:3px}
@media(min-width:901px){.stub-fab{display:block}}

/* ── HERO · PROFONDEUR DE CHAMP ─────────────────────────────────────────
   D'après festivent.ca. Les lavis sont en CSS plutôt qu'en PNG : même
   rendu, aucune requête, et la couleur suit les jetons de marque.
   Les emplacements d'objets attendent les découpes.                     */
.hero__objects{position:absolute;inset:0;overflow:hidden;pointer-events:none;z-index:1}
.hero__wash{position:absolute;display:block;width:52vmax;aspect-ratio:1;
  border-radius:50%;filter:blur(100px);opacity:.5;will-change:transform}
.hero__wash--azul  {background:var(--c-cci);   top:-22%; left:-14%}
.hero__wash--verde {background:var(--c-ink-2); bottom:-28%; left:26%}
.hero__wash--carmin{background:var(--c-rojo);  top:6%;  right:-18%; width:34vmax; opacity:.34}
@media(prefers-reduced-motion:no-preference){
  .hero__wash{animation:wash-drift 30s ease-in-out infinite alternate}
  .hero__wash--verde {animation-duration:38s;animation-delay:-8s}
  .hero__wash--carmin{animation-duration:34s;animation-delay:-14s}
}
@keyframes wash-drift{
  from{transform:translate3d(0,0,0) scale(1)}
  to  {transform:translate3d(5%,-7%,0) scale(1.16)}
}

/* Paire net + flou : c'est le doublon qui crée la profondeur. */
.hero__obj{position:absolute;display:block;width:auto;pointer-events:none}
.hero__obj--near{height:38vmin;right:6%;bottom:8%;opacity:.95}
.hero__obj--far {height:22vmin;left:8%;top:16%;filter:blur(6px);opacity:.6}
@media(max-width:760px){
  .hero__obj--near{height:26vmin;right:-4%}
  .hero__obj--far {display:none}
}

/* ══ HERO · CORRECTIONS DE LISIBILITÉ ══════════════════════════════════
   Constat sur le rendu : le titre était l'élément le plus grand et le
   moins lisible. Deux causes, deux corrections.

   1. Le duotone passait du bleu au vert EN TRAVERSANT LE TEAL, et le teal
      tombait exactement derrière le titre. On comprime le milieu : le bas
      atteint le vert profond, le titre retrouve un fond franc.
   2. Le voile était uniforme. Il devient directionnel — dense en bas à
      gauche où vit la typo, clair en haut à droite où vit le pase.      */
.hero__veil{
  background:
    linear-gradient(to bottom,
      rgba(0,42,79,.58)  0%,
      rgba(0,42,79,.42) 26%,
      rgba(0,61,52,.70) 62%,
      rgba(0,61,52,.88) 100%),
    radial-gradient(120% 90% at 12% 88%, rgba(0,42,79,.72), transparent 62%);
}

/* Crème pleine, non teintée. Le titre est l'élément le plus important de
   la page : il n'a aucune raison d'être le plus pâle. */
.hero .display{color:var(--c-cream);text-shadow:0 2px 40px rgba(0,26,48,.34)}
.hero__lead{color:rgba(247,241,228,.92)}

/* Les lavis de couleur passaient SOUS la photo — donc invisibles.
   Au-dessus, en fondu doux, ils redonnent du mouvement à un hero qui
   serait sinon une affiche fixe. */
/* CORRIGÉ : le hero empile ses fonds en z-index NÉGATIF (photo -5, voile
   -4, bg -3, grain -2) tandis que .hero__inner est à 0. Posés à +2, les
   lavis passaient AU-DESSUS du titre et le teintaient en vert-de-gris —
   c'est ce qui rendait « 200 AÑOS » pâle et tout le hero brumeux.
   Ils reviennent sous le contenu, entre le voile et le grain. */
.hero__objects{z-index:-3;mix-blend-mode:screen;opacity:.34}

/* Le compte à rebours vit dans la souche du pase. */
.pass__stub .count{margin:var(--s-3) 0 0;text-align:center;
  font-size:.8125rem;letter-spacing:.04em;color:var(--c-ink)}
.pass__stub .count b{font-size:1.05rem;font-weight:900;
  font-variant-numeric:tabular-nums;color:var(--c-rojo)}

/* ── Séquence d'entrée ──────────────────────────────────────────────────
   Poussée lente sur la photo plutôt qu'un panoramique : le mouvement se
   sent sans qu'on puisse le pointer. C'est ce qui distingue un hero
   vivant d'une affiche.                                                 */
@media (prefers-reduced-motion:no-preference){
  .hero__photo{animation:hero-push 1.6s var(--ease-out) both}
}
@keyframes hero-push{from{transform:scale(1.06)}to{transform:scale(1)}}

/* ══ CORRECTIONS DE RENDU (2e passe) ═══════════════════════════════════ */

/* Le sous-titre héritait de --c-muted (#5A6B7D, ardoise foncée) déclaré
   plus bas dans le fichier : illisible sur une photo sombre. */
.hero .hero__lead{color:var(--c-cream);opacity:.94}
.hero .hero__lead em{color:var(--c-peach)}

/* ── LE PASE ────────────────────────────────────────────────────────────
   Il butait contre « Reservar taller » de l'en-tête. On le centre
   verticalement dans le hero et on le décolle de la barre. */
@media(min-width:1100px){
  .hero__inner{align-items:center}
  .pass{align-self:center;margin-top:var(--s-8)}
}

/* ── BOUTONS : plus de saut au survol ───────────────────────────────────
   Le déplacement vertical faisait « sauter » le bouton sous le curseur.
   On le remplace par le liseré pointillé animé de la référence : le
   contour court le long de la forme, l'élément lui-même ne bouge pas. */
/* NE PAS remettre overflow:visible ici. `.btn` compte sur overflow:hidden
   pour contenir le remplissage `::before` (scale 0 → 1.05). En le passant
   à visible, le remplissage débordait de la pilule au survol — c'est ce
   qui donnait l'impression que le bouton « sautait ».
   Le liseré pointillé vit donc À L'INTÉRIEUR du bouton, comme sur la
   référence où les points sont en retrait du bord. */
.btn{position:relative}
.btn:hover{transform:none}
/* ── LISERÉ POINTILLÉ QUI COURT ─────────────────────────────────────────
   La technique de la référence : un <rect> SVG dont on anime le
   stroke-dashoffset. Le tiret PARCOURT le périmètre — il ne tourne pas
   sur lui-même. (Ma première version faisait pivoter la boîte entière :
   ce n'est pas le même effet du tout.)

   pathLength="100" normalise le tracé : quelle que soit la largeur du
   bouton, le périmètre vaut 100. Un dasharray de 14/86 donne donc
   toujours un tiret de 14 % — identique sur un bouton large ou étroit,
   sans calcul JS.

   vector-effect:non-scaling-stroke garde l'épaisseur constante alors que
   le SVG est étiré aux dimensions du bouton.                            */
.btn > .glow{
  position:absolute;inset:2px;width:calc(100% - 4px);height:calc(100% - 4px);
  pointer-events:none;color:inherit;overflow:visible;
  /* z-index NÉGATIF, pas positif. `.btn` porte isolation:isolate, donc le
     -1 reste confiné au bouton. En z-index:1 le SVG était peint APRÈS le
     contenu inline : les points passaient par-dessus le libellé — c'est ce
     qui cassait le survol. Il vient maintenant se placer au-dessus du
     remplissage `::before` (même couche, plus loin dans l'ordre du DOM)
     mais sous le texte. */
  z-index:-1;
}
.btn > .glow path{
  fill:none;stroke:currentColor;stroke-width:2.2;
  stroke-linecap:round;
  /* pathLength="100" est FIABLE sur <path> — il ne l'est pas sur <rect>,
     que Safari notamment ignore. Là où il était ignoré, `0 2.2` se lisait
     en PIXELS : un point tous les 2,2 px, soit un pointillé si dense qu'il
     paraissait plein. C'était ça, le survol « cassé ». */
  stroke-dasharray:0 2.4;
  stroke-dashoffset:0;
  opacity:0;transition:opacity .22s var(--ease-out);
}
.btn:hover > .glow path,
.btn:focus-visible > .glow path{
  opacity:1;
  animation:glow-march 1.1s linear infinite;
}
/* Décalage d'exactement une période : boucle sans raccord visible. */
@keyframes glow-march{to{stroke-dashoffset:-2.4}}

@media(prefers-reduced-motion:reduce){
  /* Cible <path>, comme le reste : la règle visait encore <rect> et ne
     s'appliquait donc plus à rien. */
  .btn:hover > .glow path,.btn:focus-visible > .glow path{
    animation:none;stroke-dasharray:none;opacity:.55;
  }
}

/* ── SPONSORS DANS LE HERO ──────────────────────────────────────────────
   Ils étaient minuscules et détachés en bas à gauche. On leur donne un
   filet, de l'air et une taille qui respecte le niveau 1 de la
   hiérarchie sponsors définie en design (--logo-n1). */
.hero .presented{display:flex;align-items:center;gap:var(--s-5);flex-wrap:wrap;
  padding-top:var(--s-5);border-top:1px solid rgba(247,241,228,.22)}
.hero .presented__label{font-size:.625rem;letter-spacing:var(--ls-label);
  text-transform:uppercase;color:rgba(247,241,228,.66)}
.hero .presented__logos{display:flex;align-items:center;gap:var(--s-6)}
.hero .presented__logos img{height:var(--logo-n1);width:auto;
  filter:brightness(0) invert(1);opacity:.92;
  transition:opacity .3s var(--ease-out)}
.hero .presented__logos a:hover img{opacity:1}

/* En-tête à trois blocs, d'après artefact : logo / navigation / action.
   Plus rien au centre — le crosslink est passé dans le bandeau. */
.header__banner-track a{text-decoration:none;border-bottom:1px solid rgba(247,241,228,.45)}
.header__banner-track a:hover{border-bottom-color:var(--c-cream)}
.nav{margin-left:auto}

/* ══ NAVIGATION EN PASTILLES ═══════════════════════════════════════════
   Du texte nu au-dessus d'une photo dépend entièrement du voile pour
   rester lisible. Une pastille porte son propre fond : elle tient à
   n'importe quel endroit de n'importe quelle image. */
.nav a{
  padding:10px var(--s-5);border-radius:var(--r-pill);
  background:rgba(247,241,228,.14);backdrop-filter:blur(10px);
  border:1px solid rgba(247,241,228,.22);
  transition:background-color .3s var(--ease-out),color .3s var(--ease-out),
             border-color .3s var(--ease-out);
}
.nav a:hover{background:var(--c-cream);color:var(--c-ink);border-color:var(--c-cream)}
.header.is-solid .nav a{background:transparent;border-color:var(--c-line);color:var(--c-ink)}
.header.is-solid .nav a:hover{background:var(--c-ink);color:var(--c-cream);border-color:var(--c-ink)}
/* La pastille porte déjà l'état au survol : le soulignement ferait double. */
.nav a::after{display:none}

/* ══ SPONSORS · DEUX NIVEAUX NOMMÉS ════════════════════════════════════ */
.hero .presented{display:flex;align-items:center;gap:var(--s-6);flex-wrap:wrap}
.presented__tier{display:flex;flex-direction:column;gap:var(--s-3)}
.presented__sep{align-self:stretch;width:1px;min-height:44px;
  background-image:linear-gradient(rgba(247,241,228,.5) 3px, transparent 3px);
  background-size:1px 8px}
.header.is-solid .presented__sep,
.plate-cal .presented__sep{background-image:linear-gradient(var(--c-line) 3px, transparent 3px)}

/* ══ INFO PRÁCTICA · déclencheur permanent ════════════════════════════
   ⚠ La barre est en position:fixed : elle recouvrait le bloc sponsors du
   hero. On réserve donc sa hauteur en bas du hero (voir --practica-h),
   sinon elle mange les logos — exactement le chevauchement constaté. */
:root{--practica-h:64px}

.practica-bar__toggle{
  display:flex;align-items:center;justify-content:space-between;gap:var(--s-4);
  width:100%;padding:14px var(--s-5);border:0;cursor:pointer;
  border-radius:var(--r-pill);background:var(--c-cream);color:var(--c-ink);
  font:inherit;font-size:.8125rem;font-weight:700;letter-spacing:.08em;
  text-transform:uppercase;box-shadow:0 14px 34px -18px rgba(0,26,48,.6)}
.practica-bar__toggle i{display:grid;place-items:center;width:26px;height:26px;
  border-radius:50%;background:var(--c-cci);color:var(--c-cream);font-size:.7rem;
  transition:transform .4s var(--ease-out)}
.practica-bar__toggle[aria-expanded="true"] i{transform:rotate(180deg)}

/* La place réservée sous le hero pour ne pas masquer les sponsors. */
.hero{padding-bottom:calc(var(--practica-h) + var(--s-6))}

/* ══ SUPERPOSITION ════════════════════════════════════════════════════
   Deux panneaux qui se CROISENT : le blanc monte depuis le bas, le bleu
   entre par la droite. Ils ne sont pas collés aux bords — une marge et un
   rayon les font lire comme deux cartons posés sur la page, pas comme un
   écran qui bascule.

   La fermeture rejoue l'inverse, et le bleu part le premier : ce décalage
   est ce qui donne la sensation qu'ils se détachent au lieu de disparaître
   ensemble. */
/* Les panneaux étant exclusifs, ils se SUPERPOSENT. En flex, le blanc
   gardait sa colonne de 420 px même hors écran : le bleu s'en trouvait
   rétréci d'autant, sans raison visible. */
.ov{position:fixed;inset:0;z-index:125}
.ov[hidden]{display:none}

/* Un voile derrière : sans lui, les panneaux flottent sur une page nette
   et l'œil ne sait plus lequel des deux plans est actif. */
.ov::before{content:'';position:absolute;inset:0;z-index:-1;
  background:rgba(0,26,48,.42);backdrop-filter:blur(3px);
  opacity:0;transition:opacity .5s var(--ease-out)}
.ov.is-practica::before,.ov.is-menu::before{opacity:1}

/* Panneau BLANC — le pratique. Monte depuis le bas. */
.ov__practica{
  position:absolute;z-index:2;
  left:0;top:0;bottom:var(--practica-h);
  width:min(420px,100vw);
  /* Collés : aucune marge extérieure, l'arrondi n'existe que sur le bord
     INTÉRIEUR. Les deux panneaux s'emboîtent au lieu de flotter. */
  border-radius:0 var(--r-lg) var(--r-lg) 0;
  background:var(--c-cream);color:var(--c-ink);
  display:flex;flex-direction:column;justify-content:space-between;
  padding:var(--s-7) var(--s-6);overflow-y:auto;
  transform:translateY(100%);
  /* ⚠ `bottom` ET `border-radius` DANS LA TRANSITION — sans quoi le panneau
     SAUTE à la fermeture (constaté le 18/08). En se fermant, `.practica-open`
     quitte <html> d'un coup : `bottom` repasse instantanément de la hauteur de
     la barre à zéro, et l'arrondi change de coins. Le saut se produit AVANT
     que la sortie ne se joue, ce qui le rend d'autant plus visible. */
  transition:transform .62s var(--ease-io) .06s,
             bottom .45s var(--ease-out),
             border-radius .45s var(--ease-out);
  will-change:transform}
.ov.is-practica .ov__practica{transform:none}

/* `.ov__practica-nav` (la liste de six liens en pointillés) a été retirée
   du balisage : trois de ses entrées pointaient sur la même ancre. La
   feuille porte l'information elle-même — voir `.ov__facts` en fin de
   fichier. Ses styles partent avec elle, sinon ils survivent comme les
   « réglages sans câblage » que ce fichier a déjà payés deux fois. */
/* .ov__plan retiré avec son bouton. Le panneau se justifie donc
   simplement en haut plutôt que d'écarter un pied qui n'existe plus. */
.ov__practica{justify-content:flex-start}

/* Panneau BLEU — la navigation. Entre par la droite. */
.ov__menu{
  position:absolute;z-index:1;
  inset:0;bottom:var(--practica-h);
  background:var(--c-ink);color:var(--c-cream);
  border-radius:var(--r-lg) 0 0 var(--r-lg);
  display:flex;flex-direction:column;justify-content:center;gap:var(--s-7);
  padding:var(--s-9) var(--s-7);overflow-y:auto;
  transform:translateX(100%);
  transition:transform .62s var(--ease-io);
  will-change:transform}
.ov.is-menu .ov__menu{transform:none}

/* COLLÉS : quand le blanc est là, le bleu démarre exactement à son bord
   droit au lieu de passer dessous. Les deux se touchent, le seul arrondi
   est celui du joint. */
.ov.is-practica .ov__menu{left:min(420px,100vw)}
@media(max-width:760px){
  /* Deux panneaux ne tiennent pas sur un téléphone : le bleu reprend tout. */
  .ov.is-practica .ov__menu{left:0}
  .ov.is-menu .ov__practica{transform:translateY(100%)}
}

/* À la fermeture, le bleu s'en va d'abord : c'est ce léger décalage qui
   fait « se détacher » les deux plans au lieu de les effacer d'un bloc. */
.ov.is-closing .ov__menu{transition-delay:0s}
.ov.is-closing .ov__practica{transition-delay:.1s}

/* Le panneau bleu n'est pas là : il ne doit pas intercepter les clics. */
.ov:not(.is-menu) .ov__menu{pointer-events:none}
.ov:not(.is-practica) .ov__practica{pointer-events:none}
.ov__close{position:absolute;top:var(--s-5);right:var(--s-5);
  width:56px;height:56px;border:0;border-radius:50%;cursor:pointer;
  background:var(--c-cream);color:var(--c-ink);font-size:1.25rem;line-height:1}
.ov__close:hover{background:var(--c-rojo);color:#fff}
.ov__nav{display:flex;flex-direction:column;align-items:center;gap:var(--s-3)}
.ov__nav a{font-family:var(--f-display);font-weight:900;
  font-variation-settings:'wdth' 112,'wght' 900;
  font-size:clamp(2rem,5vw,3.6rem);line-height:1.02;letter-spacing:-.02em;
  text-transform:uppercase;text-decoration:none;color:var(--c-cci-lite);
  transition:color .3s var(--ease-out)}
.ov__nav a:hover{color:var(--c-cream)}
.ov__cols{display:grid;grid-template-columns:1fr 1fr;gap:var(--s-6);
  padding-top:var(--s-6);border-top:1px dotted rgba(247,241,228,.3)}
.ov__cols nav{display:flex;flex-direction:column;gap:var(--s-3);text-align:center}
.ov__cols a{font-size:.8125rem;letter-spacing:.08em;text-transform:uppercase;
  text-decoration:none;color:rgba(247,241,228,.78)}
.ov__cols a:hover{color:var(--c-cream)}
.ov__legal{text-align:center;font-size:.875rem;color:rgba(247,241,228,.5)}

@media(max-width:760px){
  .ov__practica{width:100vw;border-radius:0}
  
  .practica-bar__toggle{border-radius:0;justify-content:center;min-height:var(--practica-h)}
  /* ⚠ UNE SEULE COLONNE, ET C'EST LA BONNE DÉCISION — essayé à deux le
     24/08, revenu le jour même. À 375 px, deux colonnes laissent ~160 px par
     entrée : « Zonas del festival », « Preguntas frecuentes », « Aviso de
     privacidad » et « CCI France México ↗ » se cassaient tous en deux lignes,
     et les deux colonnes cessaient de s'aligner. Une liste centrée qui défile
     vaut mieux qu'une grille qui se disloque.

     Le menu déborde donc d'environ 120 px sur un téléphone court. C'est le
     comportement normal d'un menu plein écran, et le rembourrage corrigé
     au-dessus en a déjà repris 56. */
  .ov__cols{grid-template-columns:1fr}
}
@media(prefers-reduced-motion:reduce){
  .ov__practica,.ov__menu{transition:none}
}
@media print{.ov{display:none}}

/* Un logo non confirmé reste visible en maquette mais doit se SIGNALER :
   sans marque, il finit par passer pour un fait acquis. À retirer le jour
   où le client tranche. */
.hero .presented__logos{display:flex;align-items:center;gap:var(--s-5);justify-content: center;}
[data-unconfirmed]{position:relative}
[data-unconfirmed]::after{
  content:"por confirmar";position:absolute;left:50%;top:calc(100% + 6px);
  transform:translateX(-50%) rotate(calc(var(--ag-tilt) * -1));white-space:nowrap;
  font-size:.5rem;letter-spacing:.14em;text-transform:uppercase;
  color:rgba(247,241,228,.55)}

/* ══ BURGER — visible À TOUTES LES TAILLES ═════════════════════════════
   Il était en display:none, révélé seulement sous 960 px. Or la
   superposition (panneau bleu + panneau blanc) est désormais une
   fonctionnalité de bureau aussi : sans burger, elle n'a plus de
   déclencheur sur grand écran. Comme la référence, qui garde le sien en
   4e position après les trois pastilles. */
.burger{display:grid;place-items:center;border-radius:var(--r-pill);
  background:rgba(247,241,228,.14);border:1px solid rgba(247,241,228,.22);
  backdrop-filter:blur(10px);cursor:pointer;
  transition:background-color .3s var(--ease-out),border-color .3s var(--ease-out)}
.burger:hover{background:var(--c-cream);border-color:var(--c-cream)}
.burger:hover span{background:var(--c-ink)}
.header.is-solid .burger{background:transparent;border-color:var(--c-line)}
.header.is-solid .burger:hover{background:var(--c-ink);border-color:var(--c-ink)}
.header.is-solid .burger:hover span{background:var(--c-cream)}

/* Partenaires en attente de logotypes : traités comme des mots, pas
   déguisés en logos. Un faux logo en maquette finit toujours par être
   pris pour le vrai. */
.presented__logos--text{display:flex;align-items:center;gap:var(--s-4);flex-wrap:wrap}
.presented__logos--text a{font-family:var(--f-display);font-weight:900;
  font-variation-settings:'wdth' 96,'wght' 800;
  font-size:1.0625rem;letter-spacing:.01em;text-decoration:none;
  color:var(--c-cream);opacity:.9;transition:opacity .3s var(--ease-out)}
.presented__logos--text a:hover{opacity:1}
/* [data-awaiting-logos]{position:relative} */
/* [data-awaiting-logos]::after{
  content:"logotipos pendientes";position:absolute;left:0;top:calc(100% + 6px);
  font-size:.5rem;letter-spacing:.14em;text-transform:uppercase;
  color:rgba(247,241,228,.45)} */

/* La silhouette : le net devant à droite, le flou derrière à gauche.
   Le fondu la fait sortir du décor au lieu d'y être collée. */
.hero__obj{mix-blend-mode:screen;opacity:.22;filter:none}
.hero__obj--near{height:auto;width:min(46vw,620px);right:-2%;bottom:calc(var(--practica-h) + 2%);opacity:.26}
.hero__obj--far{height:auto;width:min(30vw,380px);left:2%;top:22%;filter:blur(5px);opacity:.16}
@media(max-width:760px){
  .hero__obj--near{width:80vw;right:-10%}
  .hero__obj--far{display:none}
}

/* ══ BOUTONS NON PLEINS : le contour change, le fond ne bouge pas ══════
   Un bouton fantôme se remplissait au survol (::before passait de scale 0
   à 1.05) et le texte s'inversait. Les points venaient donc se poser sur
   un aplat sombre.

   Attendu : le bouton reste transparent, et le trait plein est simplement
   REMPLACÉ par le pointillé. Les boutons PLEINS (primaire) gardent leur
   remplissage — la règle ne vise que les non pleins. */
.btn--ghost:hover::before,
.btn--ghost:focus-visible::before{transform:scale(0)}

/* La couleur du texte doit alors rester celle du repos : elle avait été
   inversée dans chaque contexte pour compenser un remplissage qui n'existe
   plus. On la rétablit contexte par contexte, à spécificité égale. */
.btn--ghost:hover,.btn--ghost:focus-visible{color:var(--c-ink)}
.hero .btn--ghost:hover,.hero .btn--ghost:focus-visible{color:var(--c-cream)}
.hz .btn--ghost:hover,.sponsors .btn--ghost:hover,
.hz .btn--ghost:focus-visible,.sponsors .btn--ghost:focus-visible{color:var(--c-cream)}
.talleres .btn--ghost:hover,.plano .btn--ghost:hover,
.talleres .btn--ghost:focus-visible,.plano .btn--ghost:focus-visible{color:var(--c-ink)}
.ov__practica .btn--ghost:hover,.ov__practica .btn--ghost:focus-visible{color:var(--c-ink)}


.practica-open .practica-bar__toggle{
  border-radius:0;
  background:#CFE0F5;
  color:var(--c-cci);
  min-height:var(--practica-h);
  padding-inline:var(--s-6);
  box-shadow:none}
.practica-open .practica-bar__toggle i{
  background:var(--c-ink);color:var(--c-cream);
  width:36px;height:36px;font-size:.8rem}


.practica-bar__toggle{padding-inline:var(--s-6)}


/* 2. ESPACE EN HAUT. Les panneaux montaient jusqu'au bord de l'écran et
      recouvraient le bandeau. On les descend sous lui : l'information
      essentielle (entrée libre, dates) reste lisible pendant la navigation,
      et les panneaux se lisent comme posés SUR la page, pas comme un écran
      qui la remplace. */
.ov{top:var(--s-6)}
.ov__practica{border-radius:0 var(--r-lg) var(--r-lg) 0}
.ov__menu{border-radius:var(--r-lg) 0 0 var(--r-lg)}

/* 3. AU SURVOL : LES POINTS, RIEN D'AUTRE.
      Le remplissage `::before` balayait encore le fond des boutons pleins.
      Cumulé au liseré pointillé, ça faisait deux effets pour un seul geste.
      Le fond ne bouge plus — sur AUCUN bouton — et seuls les points
      apparaissent. */
.btn:hover::before,
.btn:focus-visible::before{transform:scale(0)}
.btn--primary:hover,.btn--primary:focus-visible{background:var(--c-rojo);color:#fff}
.btn--dark:hover,.btn--dark:focus-visible{background:var(--c-ink);color:var(--c-cream)}

/* ══ L'ESPACE DU HAUT, PROPREMENT ══════════════════════════════════════

   Deux défauts se cumulaient dans cette bande :

   1. Le voile était en position:absolute DANS .ov — or .ov commence
      désormais 32 px plus bas. La bande du haut n'était donc pas voilée
      du tout : on y voyait la page en clair, à côté d'un fond assombri.
      En position:fixed, il couvre l'écran entier quoi qu'il arrive.

   2. Le bandeau vert restait visible dans cette bande et ressemblait à un
      résidu d'affichage. Il se replie — il a la même transition de hauteur
      que lorsque l'en-tête devient solide, donc le geste est déjà connu. */
.ov::before{position:fixed;inset:0}

.menu-open .header__banner,
.practica-open .header__banner{height:0;opacity:0}

/* L'en-tête entier s'efface aussi : la superposition porte sa propre
   navigation et sa propre croix, deux barres de navigation superposées
   n'apprennent rien à personne. */
.menu-open .header__inner,
.practica-open .header__inner{opacity:0;pointer-events:none}
.header__inner{transition:opacity .3s var(--ease-out)}

/* ══ MARGES SYMÉTRIQUES ═══════════════════════════════════════════════

   Le haut valait 32 px et le bas 64 px : les panneaux réservaient la
   hauteur de la barre « Info práctica » même quand elle est masquée —
   ce qui est justement le cas dès que le menu est ouvert.

   La marge est désormais la même des deux côtés. Quand le pratique est
   ouvert, la barre remonte au niveau de cette marge et redevient le socle
   du panneau, qui s'arrête juste au-dessus d'elle. */
.ov{bottom:var(--s-6)}
.ov__practica{top:0;bottom:0}
.ov__menu{inset:0}


.practica-open .ov__practica{bottom:var(--practica-h)}

/* Panneau et socle forment un seul bloc : l'arrondi bas passe du panneau
   à la barre, sinon on verrait deux angles arrondis se chevaucher. */
.practica-open .ov__practica{border-radius:0 var(--r-lg) 0 0}
.practica-open .practica-bar__toggle{border-radius:0 0 var(--r-lg) 0}

/* ══ PLUS DE BANDE VIDE SOUS LE PANNEAU ═══════════════════════════════

   Le panneau s'arrêtait à --practica-h du bas et la barre était censée
   combler exactement ce creux. Deux géométries indépendantes qui doivent
   coïncider au pixel : au moindre écart, une bande transparente apparaît.

   On supprime le problème au lieu de l'ajuster. Le panneau descend
   JUSQU'EN BAS — il est blanc sur toute sa hauteur — et réserve
   simplement un padding intérieur pour que son contenu ne passe pas sous
   la barre. La barre se pose par-dessus. Aucun raccord à calibrer. */
.practica-open .ov__practica{
  bottom:0;
  padding-bottom:calc(var(--practica-h) + var(--s-5));
  border-radius:0 var(--r-lg) var(--r-lg) 0}

.practica-open .practica-bar__toggle{
  border-radius:0 0 var(--r-lg) 0;
  background:#CFE0F5}

/* Quand le MENU est ouvert, la barre « Info práctica » est masquée — le
   panneau blanc n'a donc plus aucune raison de lui réserver un padding.
   C'était ça, le vide en bas à gauche : de la place gardée pour un élément
   absent. Le panneau reprend son padding normal et son angle bas. */
.menu-open .ov__practica{
  padding-bottom:var(--s-7);
  border-radius:0 var(--r-lg) var(--r-lg) 0}


/* ══ LE PASE · UN SEUL ROUGE ══════════════════════════════════════════

   La souche était d'un second rouge plus sombre. Les deux teintes se
   lisaient comme une erreur d'impression plutôt que comme une déchirure,
   et surtout le compteur y tombait à 1,56:1 — invisible.

   Sur un vrai billet, la souche est la partie CLAIRE : c'est le talon
   qu'on détache. En la passant en crème, on est plus fidèle à l'objet ET
   la lisibilité se règle toute seule. Le billet passe de quatre couleurs
   à deux. */
.pass__stub{background:var(--c-cream);color:var(--c-ink)}

/* ── RANG DE L'ACTION · LA PILULE ROUGE EST RÉSERVÉE ──────────────────
   Cette règle remplissait le bouton en --c-rojo. Elle datait du passage de
   la souche sur crème : le fond blanc d'origine ne tenait plus, on a rempli
   en rouge. Sauf que --c-rojo plein, c'est exactement .btn--primary — donc
   « Añadir a Google Calendar » s'affichait au même rang que « Reservar un
   taller », à un écran d'écart, dans le même hero.

   Or les deux actions ne pèsent pas pareil. L'entrée du festival est LIBRE :
   la seule conversion du site, c'est la réservation d'atelier. Ajouter à
   l'agenda est un « je verrai plus tard » — utile, mais c'est le geste de
   quelqu'un qui NE convertit PAS maintenant.

   Règle posée : la pilule PLEINE ROUGE n'appartient qu'à « reservar ». Le
   rouge reste présent ailleurs (filets, badges, en-tête du pass) — c'est la
   forme pleine qui porte le rang, pas la teinte.

   L'encre donne un bouton franchement actionnable, premier de SA carte,
   sans disputer le rang au CTA. Contraste 13,6:1 sur crème (9,8:1 au
   survol) — l'ancien rouge sur blanc plafonnait à 5,0:1. */
.pass__cta{background:var(--c-ink);color:var(--c-cream)}
.pass__cta:hover{background:var(--c-ink-3);color:var(--c-cream)}

/* Secondaires et compteur : encre sur crème. */
.pass__alt{color:var(--c-muted);opacity:1}
.pass__alt button{color:var(--c-ink)}
.pass__alt button:hover{background:rgba(0,64,121,.09)}
.pass__stub .count{color:var(--c-muted)}
.pass__stub .count b{color:var(--c-rojo-txt)}   /* 6,35:1 sur crème ✓ */

/* La perforation se lit désormais en creux sombre sur la crème. */
.pass__perf{background:linear-gradient(90deg,rgba(0,42,79,.35) 50%,transparent 0);
  background-size:11px 2px;opacity:1}

/* ══ HERO · CADRAGE ET ÉQUILIBRE DES COULEURS ═════════════════════════

   1. LE CADRAGE. object-position valait `center 32%` : le cadre se calait
      sur le HAUT de la photo, c'est-à-dire les bâtiments. Les chapiteaux
      et la foule — le seul contenu qui dise « fête » — étaient repoussés
      hors champ ou masqués par le titre.

      On recadre par object-position plutôt qu'en découpant le fichier :
      aucune perte de qualité, et le cadrage reste ajustable d'une ligne
      le jour où le client livre l'original en pleine résolution. */
.hero__photo{object-position:center 74%}

/* 2. LE BLEU. Le dégradé virait au vert dès 62 % : le bleu ne survivait
      que dans la bande d'en-tête, où il passait pour de la chrome plutôt
      que pour une couleur de marque. Vous demandiez que le bleu ET le
      vert dominent — c'était devenu du vert avec un liseré bleu.

      Le bleu tient désormais jusqu'aux deux tiers, donc sous le titre ;
      le vert ne prend que le dernier tiers, là où il rencontre le bandeau
      de sponsors. Le titre gagne aussi en contraste : le bleu est le fond
      le plus sombre des deux. */
.hero__veil{
  background:
    linear-gradient(to bottom,
      rgba(0,42,79,.64)   0%,
      rgba(0,42,79,.54)  38%,
      rgba(0,48,66,.62)  66%,
      rgba(0,61,52,.78)  86%,
      rgba(0,61,52,.90) 100%),
    radial-gradient(130% 95% at 10% 78%, rgba(0,42,79,.78), transparent 66%);
}

/* ══ HERO · LA GRANDE ROUE ════════════════════════════════════════════
   La photo précédente faisait 800×421 dans une fenêtre au ratio quasi
   identique : avec object-fit:cover, il ne restait presque aucun
   débordement vertical, donc object-position n'avait rien à déplacer.
   Recadrer ne pouvait pas fonctionner — il fallait changer d'image.

   Celle-ci est plus petite mais dit ce qu'il faut dès le premier coup
   d'œil : nuit, lumières, foule, fête foraine. On adoucit un peu moins
   la luminosité, les lumières de la roue devant rester des lumières. */
.hero__photo{object-position:center 50%;
  filter:grayscale(.82) contrast(1.1) brightness(.82)}

/* Le logotype Aztlán extrait du PDF n'emporte PAS son canal alpha — le
   masque est stocké séparément. Posé tel quel il s'affichait en carré
   clair. Retour au texte tant que le client n'a pas livré le fichier
   détouré. Même cas que le logotype Festival, dans _a-composer/. */

/* ══ PHOTO · TRAME DEMI-TON ═══════════════════════════════════════════
   La photo de la grande roue ne fait que 490×258 : en plein cadre, elle
   pixellise. Plutôt que de la subir, on l'ASSUME — une trame de points
   fine par-dessus, et le manque de définition se lit comme un parti pris
   d'impression au lieu d'un défaut.

   C'est exactement ce que fait la référence : sa montgolfière est tramée,
   ce qui lui permet d'agrandir une image sans qu'on y voie un défaut. */
.hero__photo{filter:grayscale(.78) contrast(1.22) brightness(.8)}
.hero__halftone{
  position:absolute;inset:0;z-index:-4;pointer-events:none;
  background-image:radial-gradient(circle at center, rgba(0,0,0,.55) 32%, transparent 33%);
  background-size:4px 4px;
  mix-blend-mode:overlay;opacity:.55}

/* ══ INFO PRÁCTICA · D'APRÈS LA RÉFÉRENCE ═════════════════════════════
   Fond bleu clair et non crème, chevron en cercle AJOURÉ plutôt qu'en
   pastille pleine, et davantage d'air : la barre passe de bouton à
   bandeau. */
.practica-bar__toggle{
  background:#CFE0F5;
  color:var(--c-ink);
  padding:calc(var(--s-4) + 2px) var(--s-6);
  min-height:var(--practica-h);
  font-size:.875rem;letter-spacing:.1em;
  box-shadow:0 14px 34px -20px rgba(0,26,48,.45)}
.practica-bar__toggle:hover{background:#BDD5F0}
.practica-bar__toggle i{
  width:40px;height:40px;
  background:transparent;
  border:1.5px solid var(--c-ink);
  color:var(--c-ink);
  font-size:.75rem}
.practica-open .practica-bar__toggle{background:#CFE0F5}
.practica-open .practica-bar__toggle i{background:transparent;color:var(--c-ink);
  border-color:var(--c-ink);width:40px;height:40px}

/* ══ HERO · CONTENU ANCRÉ EN BAS, ET TOUJOURS RESPONSIVE ══════════════

   Centrer verticalement n'a rien à voir avec la responsivité : le
   centrage, c'est `align-items` ; la responsivité vient de `min-height`
   + du flux. On peut donc décentrer sans rien perdre.

   J'avais centré le hero pour écarter le pase du bouton « Reservar
   taller » de l'en-tête. Mauvaise solution : ça vidait le milieu du cadre
   et tassait tout en bas. On revient à l'ancrage bas d'origine, et le
   pase s'aligne sur la MÊME ligne de base que le titre — les deux
   colonnes partagent un pied, ce qui tient la composition sans centrer.

   Reste responsive parce que rien n'est figé : min-height (pas height),
   marges en clamp(), et si le contenu dépasse sur un écran court, le hero
   grandit au lieu de rogner. */
@media(min-width:1100px){
  /* CENTRÉ, finalement — et pour une raison de composition, pas de goût :
     ancré en bas, le bandeau sponsors venait buter contre la barre « Info
     práctica ». Centrer libère cet intervalle et rend l'espace du bas
     lisible comme une marge plutôt que comme un tassement.

     La responsivité n'en dépend pas : c'est toujours min-height + flux qui
     la portent, `align-items` ne fait que positionner. */
  .hero__inner{align-items:center}
  .hero__main{align-self:center}
  .pass{align-self:center;margin-top:0}
}

/* Sur écran court, le hero cesse d'imposer 100svh : il prend la hauteur
   de son contenu. Sans ça, un portable en paysage rognerait le pase. */
@media(max-height:760px){
  .hero{min-height:auto;padding-block:96px var(--s-7)}
}


/* ══ ALIGNEMENT COMMUN BARRE + PANNEAU ════════════════════════════════
   La barre restait sur la gouttière une fois fermée mais repassait à ras
   du bord une fois ouverte : elle se déplaçait latéralement au moment
   précis du clic. Même défaut que le saut de largeur corrigé plus tôt.

   Un seul repère désormais — --practica-x — partagé par la barre ET par
   le panneau blanc. Ils ne peuvent plus diverger, quel que soit l'état. */
:root{--practica-x:var(--gutter);--practica-w:min(420px, calc(100vw - var(--gutter) * 2))}


.ov__practica{left:var(--practica-x);width:var(--practica-w)}

/* Le panneau bleu démarre au bord droit du blanc — décalage compris. */
.ov.is-practica .ov__menu{left:calc(var(--practica-x) + var(--practica-w))}

@media(max-width:760px){
  :root{--practica-x:0px;--practica-w:100vw}
  
  .ov.is-practica .ov__menu{left:0}
}

/* ══ ARRONDIS · CONSÉQUENCE DU DÉCALAGE ═══════════════════════════════
   Les rayons dataient de l'époque où le panneau était à ras du bord
   gauche : angles gauches carrés, angles droits arrondis. Maintenant
   qu'il est décalé de la gouttière, son bord gauche est visible — et un
   angle vif y traîne comme un oubli.

   Règle simple : un bord VISIBLE s'arrondit, un bord SOUDÉ reste droit. */

/* Panneau seul (menu ouvert) : quatre bords visibles, quatre arrondis. */
.menu-open .ov__practica{border-radius:var(--r-lg)}

/* Panneau + socle : le joint reste droit, tout le reste s'arrondit. */
.practica-open .ov__practica{border-radius:var(--r-lg) var(--r-lg) 0 0}
.practica-open .practica-bar__toggle{border-radius:0 0 var(--r-lg) var(--r-lg)}

/* Le panneau bleu se décale aussi du bord droit : sans ça la superposition
   serait décalée à gauche et collée à droite. */
.ov__menu{right:var(--practica-x);border-radius:var(--r-lg)}
@media(max-width:760px){.ov__menu{right:0}}

/* ══ DEUX DERNIERS ARRONDIS ═══════════════════════════════════════════

   1. LE BAS CARRÉ QUAND LE BURGER EST OUVERT.
      Le burger active les DEUX états à la fois. `.menu-open` et
      `.practica-open` ont la même spécificité (0,2,0), donc c'est l'ordre
      du fichier qui tranchait — et `.practica-open`, écrit plus bas,
      imposait un bas carré destiné à souder la barre… qui est justement
      masquée dans ce cas. D'où l'angle vif.
      On départage par une spécificité supérieure plutôt que par l'ordre :
      l'intention devient explicite et ne dépendra plus d'un déplacement
      de règle. */
.menu-open.practica-open .ov__practica{border-radius:var(--r-lg)}

/* 2. LE CRÈME QUI DÉPASSE SOUS LA BARRE.
      Le panneau descend jusqu'en bas et la barre se pose par-dessus. Mais
      la barre a des angles bas ARRONDIS tandis que le panneau, dessous,
      les avait CARRÉS : le crème débordait donc exactement dans l'échancrure
      de chaque arrondi. Le panneau prend le même rayon — plus rien à
      dépasser. */
.practica-open .ov__practica{border-radius:var(--r-lg)}

/* ══ LE LISERÉ BLEU QUI DÉPASSAIT ════════════════════════════════════
   Le panneau bleu est décalé de --practica-x du bord droit, mais il
   sortait en translateX(100%) — c'est-à-dire de SA PROPRE LARGEUR. Il
   s'arrêtait donc exactement --practica-x trop tôt, et cette bande
   restait visible. Il faut sortir de la largeur PLUS le décalage. */
.ov__menu{transform:translateX(calc(100% + var(--practica-x) + 4px))}
.ov.is-menu .ov__menu{transform:none}

/* ══ L'EN-TÊTE RESTE ACCESSIBLE AVEC LE PANNEAU PRATIQUE ═════════════
   Je masquais l'en-tête dès qu'un panneau s'ouvrait. C'était juste pour
   le menu bleu — qui porte sa propre navigation et sa propre croix — mais
   faux pour le panneau blanc : il occupe une colonne à gauche, laisse tout
   le reste de l'écran libre, et sans burger on ne peut plus appeler le
   menu par-dessus. Or c'est exactement le geste voulu : le pratique reste
   ouvert, le bleu vient s'y accoler.

   L'en-tête repasse donc AU-DESSUS de la superposition dans ce cas, sinon
   le voile intercepte les clics. */
.practica-open .header__banner{height:34px;opacity:1}
.practica-open .header__inner{opacity:1;pointer-events:auto}
.practica-open:not(.menu-open) .header{z-index:135}

/* Le menu bleu, lui, continue de masquer l'en-tête : deux navigations
   superposées ne renseignent personne. */
.menu-open .header__banner{height:0;opacity:0}
.menu-open .header__inner{opacity:0;pointer-events:none}

/* ══ LE DOCK ══════════════════════════════════════════════════════════
   Un seul objet fixe en bas, deux moitiés. Il remplace la barre pratique
   isolée, le FAB, la barre .sticky et le CTA d'en-tête. */
.dock{
  position:fixed;z-index:130;
  left:var(--practica-x);right:var(--practica-x);bottom:var(--s-5);
  display:flex;align-items:stretch;
  /* Accolées, pas écartées : `space-between` faisait lire deux widgets
     sans rapport au lieu d'une seule barre. */
  justify-content:flex-start;gap:var(--s-4);
  pointer-events:none;               /* seuls les enfants captent le clic */
  transition:transform .4s var(--ease-io),opacity .28s var(--ease-out)}
.dock > *{pointer-events:auto}

/* Gauche — ORIENTER. Sa largeur est celle du panneau blanc : c'est le même
   repère, donc le panneau s'ouvre exactement dans son axe. */
.dock__practica{flex:0 0 var(--practica-w)}

/* Droite — CONVERTIR. Elle prend le reste : à 78 jours de l'événement,
   c'est la conversion qui doit peser le plus. */
.dock__cta{
  display:flex;flex-direction:column;justify-content:center;
  gap:2px;
  border-radius:var(--r-pill);background:var(--c-rojo);color:#fff;
  text-decoration:none;text-align:center;
  box-shadow:0 14px 34px -18px rgba(220,15,70,.8);
  transition:background-color .3s var(--ease-out)}
.dock__cta:hover{background:var(--c-rojo-txt)}
.dock__cta b{font-family:var(--f-display);font-weight:900;
  font-variation-settings:'wdth' 108,'wght' 900;
  font-size:.9375rem;letter-spacing:.06em;text-transform:uppercase}
.dock__cta span{font-size:.8125rem;letter-spacing:.08em;opacity:.9}

/* Ouvert, la moitié gauche redevient le socle du panneau : elle descend au
   ras et le CTA s'efface — on ne convertit pas et on ne s'oriente pas dans
   le même geste. */
/* ⚠ CORRECTIF — le CTA disparaissait dès qu'on ouvrait « Info práctica ».

   La cause n'était pas le `display:none` mais le `right:auto` : le dock se
   rétractait à la largeur du panneau, donc le CTA n'avait plus de place et
   il fallait bien le masquer. Les deux allaient ensemble.

   Le dock garde donc sa largeur. Sa moitié gauche reste le socle du panneau
   (elle a déjà exactement sa largeur), et le CTA reste à sa place, à droite.
   Le dock est en z-index 130, le voile en 125 : il flotte au-dessus, il est
   lisible et cliquable.

   Le coût d'avant était réel : consulter l'adresse puis vouloir réserver
   obligeait à refermer le panneau d'abord. */
.practica-open .dock{left:var(--practica-x);right:var(--practica-x);bottom:var(--s-6)}
@media(max-width:760px){
  /* Sur mobile le panneau occupe tout l'écran : là, il n'y a effectivement
     plus de place pour le CTA, et le masquer reste juste. */
  .practica-open .dock__cta{display:none}
}

/* Menu bleu ouvert : le dock s'en va entièrement, comme la barre avant lui.
   Même geste de sortie, donc rien de nouveau à apprendre. */
.menu-open .dock{opacity:0;pointer-events:none;transform:translateY(120%)}

@media(max-width:760px){
  .dock{left:0;right:0;bottom:0;gap:0}
  .dock__practica{flex:0 0 44%}
  .dock__cta{border-radius:0}
  .practica-open .dock{right:0}
}
@media(prefers-reduced-motion:reduce){.dock{transition:none}}

/* Les anciens éléments ont disparu du balisage — on retire leurs styles
   pour qu'ils ne resurgissent pas dans un futur gabarit. */
.stub-fab,.sticky{display:none!important}

/* ══ LE CTA DU DOCK · TAILLE RÉELLE ═══════════════════════════════════
   Il était en flex:1 : il avalait toute la largeur restante, soit une
   barre rouge de plus de mille pixels. La priorité se marque par la
   COULEUR et la position, pas par la surface — un aplat rouge sur toute la
   largeur n'est pas prioritaire, il est envahissant. Le rouge reste le seul
   aplat saturé de l'écran, ça suffit à dire ce qui compte. */
.dock__cta{
  flex:0 0 auto;
  min-width:min(300px, 42vw);
  padding:var(--s-3) var(--s-7)}

@media(max-width:760px){
  /* Sur mobile la barre reprend toute la largeur : là, le partage 44/56
     est justifié — c'est la seule surface tactile disponible. */
  .dock__cta{flex:1;min-width:0;padding-inline:var(--s-4)}
}

/* Le dock naît MASQUÉ et n'entre qu'une fois le hero dépassé. Le hero
   porte déjà « Reservar un taller » et « Ver el programa » : superposer le
   dock par-dessus donnait deux fois la même action dans le premier écran.
   L'état est en CSS, donc il tient même si aucune librairie ne charge. */
/* Seule la MOITIÉ DROITE se retire au-dessus du hero.

   Je masquais le dock entier : « Info práctica » disparaissait avec le
   CTA, alors qu'il n'a jamais fait doublon avec quoi que ce soit. Dans la
   référence il est collant dès le premier pixel, et c'est juste — c'est un
   outil d'orientation, disponible en permanence.

   Le CTA, lui, se tait tant que le hero porte le sien. */
.dock.is-over-hero .dock__cta{
  transform:translateY(150%);opacity:0;visibility:hidden;pointer-events:none}
.dock__cta{transition:transform .45s var(--ease-io),
                      opacity .3s var(--ease-out),
                      background-color .3s var(--ease-out)}


/* Seul dans le hero, le CTA prend la place que les deux boutons se
   partageaient. La référence lui donne une pilule large et généreuse —
   un bouton isolé et petit paraîtrait timide, pas épuré. */
.btn--lg{min-height:68px;padding-inline:var(--s-9);font-size:1.0625rem;letter-spacing:.05em}
/* ⚠ UN BOUTON NE DOIT JAMAIS ÊTRE PLUS LARGE QUE L'ÉCRAN — mesuré le 17/08 :
   `--s-9` vaut 6 rem, soit 192 px de rembourrage ; avec un libellé long en
   capitales (« Ver los otros 6 talleres »), le bouton atteignait 457 px sur un
   écran de 390 et faisait défiler TOUTE LA PAGE de 87 px horizontalement.
   Le SVG `.glow` injecté par main.js n'y était pour rien : il épouse la boîte,
   il ne la crée pas. */
.btn{max-width:100%}
@media (max-width:640px){
  .btn--lg{padding-inline:var(--s-5);min-height:62px;font-size:1rem}
  /* Le libellé peut passer à la ligne plutôt que pousser la boîte. */
  .btn{white-space:normal;text-align:center;line-height:1.25}
}
@media(max-width:600px){
  .hero__actions .btn--lg{width:100%;padding-inline:var(--s-5)}
}


/* ══ 3. LE CTA DANS LE PANNEAU BLEU ═══════════════════════════════════ */
.ov__cta{
  align-self:center;display:flex;flex-direction:column;align-items:center;gap:2px;
  padding:var(--s-4) var(--s-8);border-radius:var(--r-pill);
  background:var(--c-rojo);color:#fff;text-decoration:none;text-align:center;
  transition:background-color .3s var(--ease-out)}
.ov__cta:hover{background:var(--c-cream);color:var(--c-rojo-txt)}
.ov__cta b{font-family:var(--f-display);font-weight:900;
  font-variation-settings:'wdth' 108,'wght' 900;
  font-size:1rem;letter-spacing:.06em;text-transform:uppercase}
.ov__cta span{font-size:.8125rem;letter-spacing:.08em;opacity:.9}

/* ══ DOCK · MINCE ET PERMANENT ════════════════════════════════════════
   Il ne disparaît plus au scroll. Il devient DISCRET : une seule ligne,
   52 px, translucide. Il cesse de gêner parce qu'il est léger, pas parce
   qu'il est absent — et un élément stable devient du mobilier, qu'on ne
   remarque plus.

   La ligne « Entrada libre · Lugares limitados » quitte le bouton : elle
   est déjà dans le bandeau du haut et sur chaque billet. */
.dock__practica,.dock__cta{min-height:52px;padding-block:0}
.dock__cta span{display:none}
.dock__cta b{font-size:.875rem}
.dock__practica{font-size:.875rem}
.dock__practica i{width:32px;height:32px}

/* Translucide : le texte qui passe dessous reste lisible. */
.dock__practica{backdrop-filter:blur(14px) saturate(1.2);
  background:color-mix(in srgb,#CFE0F5 90%,transparent)}
.dock__cta{backdrop-filter:blur(14px);
  background:color-mix(in srgb,var(--c-rojo) 95%,transparent)}

/* Masquage par PERTINENCE — jamais par geste. */
.dock.is-redundant{
  transform:translateY(calc(100% + var(--s-6)));opacity:0;pointer-events:none}

body{scroll-padding-bottom:calc(52px + var(--s-6))}

@media(prefers-reduced-motion:reduce){.dock{transition:none}}

/* ══ BURGER · SURVOL SUR EN-TÊTE TRANSPARENT ══════════════════════════
   `.header:not(.is-solid) .burger span` vaut (0,3,0) et impose des barres
   CRÈME. Mon `.burger:hover span` ne vaut que (0,2,0) : il perdait, et au
   survol on obtenait des barres crème sur un fond devenu crème —
   invisibles.

   On égalise en incluant le même contexte dans le sélecteur de survol. */
.header:not(.is-solid) .burger:hover span,
.header:not(.is-solid) .burger:focus-visible span{background:var(--c-ink)}

/* Même piège de l'autre côté : en-tête solide, le bouton passe en encre,
   les barres doivent donc virer au crème. */
.header.is-solid .burger:hover span,
.header.is-solid .burger:focus-visible span{background:var(--c-cream)}

/* ══ RESTE DE L'ANCIENNE BARRE AUTONOME ═══════════════════════════════
   `.practica-bar__toggle` porte `width:100%` — légitime quand il était
   seul dans son conteneur, faux depuis qu'il partage une ligne flex avec
   le CTA.

   Tant que la base flex est fixée à --practica-w, la largeur ne se voit
   pas. Mais l'état compact passe en `flex:0 0 auto` : c'est alors `width`
   qui décide, et le bouton réclame TOUTE la ligne — d'où le bandeau bleu
   pleine largeur et le CTA écrasé contre le bord droit.

   Dans le dock, la largeur vient donc uniquement du flex. */
.dock__practica{width:auto}


/* ═══════════════════════════════════════════════════════════════════════
   ADDITIONS · revue UX/UI
   Tout ce qui suit n'utilise QUE des jetons. Aucune couleur en dur.
   ═══════════════════════════════════════════════════════════════════════ */

/* ── 1 · HERO — les quatre mots de l'affiche ─────────────────────────── */
.hero__tags{display:flex;flex-wrap:wrap;gap:var(--s-2) var(--s-3);
  list-style:none;margin:var(--s-4) 0 0;padding:0}
.hero__tags li{font-family:var(--f-display);font-weight:700;
  font-size:clamp(.7rem,1.4vw,.8125rem);letter-spacing:.12em;text-transform:uppercase;
  color:var(--c-peach);padding:6px 14px;border-radius:var(--r-pill);
  box-shadow:inset 0 0 0 1.5px rgba(255,201,163,.36)}

/* ── 2 · ZONES — lien contextuel vers la réservation ─────────────────── */
.hzp__link{align-self:flex-start;margin-top:var(--s-3);padding-bottom:2px;
  font-family:var(--f-display);font-weight:700;font-size:.875rem;letter-spacing:.04em;
  color:var(--c-peach);text-decoration:none;
  border-bottom:1.5px solid rgba(255,201,163,.4);transition:border-color .3s}
.hzp__link:hover{border-color:var(--c-peach)}

/* ── 3 · AGENDA — le bloc qui manquait ───────────────────────────────
   Sur --c-ink-3 : plus clair que .hz (ink-2) qui le précède, il amorce la
   descente vers la bande crème (talleres, plano, práctica). Le programme
   se lit comme un tableau officiel, pas comme une section utilitaire.  */
/* ══════════════════════════════════════════════════════════════════════
   AGENDA — bande de couleur épinglée

   Sol clair, et par-dessus un panneau plein écran, épinglé : il reste
   immobile pendant que la page défile et se déforme au fil du parcours —
   il pivote, rétrécit, et ses deux coins hauts se coupent.

   La bande est inclinée une fois pour toutes. Le mouvement vient du
   défilement, qui la fait traverser l'écran — c'est ce que montrent les
   captures de la référence.

   Pas d'animation pilotée : une bande longue et lisible ne peut pas être
   visiblement animée en rotation. Chez Artefact le panneau fait 100vh et
   ne contient rien, donc il traverse l'écran en un seul défilement et la
   déformation est massive. Le nôtre porte un agenda de 1 600 px : le même
   geste s'étale sur trois fois plus de course, et il faut le brider pour
   que le texte ne sorte pas de la bande. Les deux contraintes s'excluent.

   DEUX RÉGLAGES, ET RIEN D'AUTRE :
     --ag-tilt   l'inclinaison de la bande.
     --ag-panel  sa couleur. Tout le bloc en dérive : texte, voiles,
                 filets, accents.
   ══════════════════════════════════════════════════════════════════════ */
.agenda{
  /* ── LA SEULE LIGNE À TOUCHER POUR CHANGER LA COULEUR ── */
  --ag-panel:var(--c-verde);
  --ag-tilt:-2deg;

  /* Dérivés : contrastés sur la bande, quelle que soit sa teinte. */
  --ag-fg:var(--c-cream);
  --ag-fg-soft:var(--c-cream-2);
  --ag-veil:rgba(251,247,240,.09);
  --ag-veil-hi:rgba(251,247,240,.16);
  --ag-line:rgba(251,247,240,.26);
  --ag-accent:var(--c-peach);

  /* La moitié basse du sol : sable par défaut, pour rejoindre `practica` qui
     suit l'agenda sur l'accueil. `.agenda--solo` la repasse en crème. */
  --ag-sol-bas:var(--c-sand);
  --ag-cta:var(--c-coral);

  position:relative;overflow:clip;
  background:var(--c-cream);
  padding-block:clamp(7rem,12vw,10rem)}

/* Calque de décor : aucun contenu, aucun clic. */
.agenda__stage{position:absolute;inset:0;z-index:0}

/* Retirée de 3,5 rem en haut et en bas : la bande de crème qui apparaît
   au-dessus et en dessous est ce qui rend l'inclinaison lisible. À ras
   bord, on ne verrait que deux triangles dans les coins. */
/* ⚠ LE BORD HAUT DU CLIP EST PLAT — ET C'EST STRUCTUREL.
   Il valait `polygon(4% 1.6%, 96% 3.2%, …)` : une seconde pente, en plus
   de la rotation. Or cette pente est un POURCENTAGE de la hauteur du
   panneau, qui dépend du contenu — l'arête réelle variait donc de -1,55deg
   à -1,03deg selon la longueur de l'agenda. Aucune valeur fixe ne pouvait
   la suivre, et rien ne pouvait s'aligner dessus.

   Bord haut ramené à plat : l'inclinaison visible devient exactement
   --ag-tilt, une constante. Le papel picado peut alors s'y accrocher, et
   changer l'angle en un seul endroit continuera de tout emmener.

   Le trapèze est conservé (4% / 96%) : c'est lui qui donne la perspective,
   et il ne dépend d'aucune hauteur. */
/* ── LE REPÈRE : L'ANGLE SE DÉCLARE UNE FOIS ──────────────────────────
   La rotation appartenait au panneau. Tout ce qui devait rester parallèle
   à la bande devait donc être SON enfant — et héritait alors aussi de sa
   découpe. C'est ce qui décapitait la guirlande.

   L'angle remonte d'un cran, dans une boîte sans fond ni découpe. Le
   panneau ne porte plus que sa forme ; la guirlande vit à côté de lui,
   dans le même repère. Les deux tournent parce qu'ils sont dans la même
   boîte tournée, pas parce qu'on a recopié une valeur : la divergence
   devient impossible plutôt qu'improbable.

   --ag-inset-b n'est pas symétrique de --ag-inset-t, et c'est mesuré :
   la rotation de --ag-tilt sur une boîte large de 124 % de la section
   descend le coin bas de 0,0216 × largeur (33 px à 1512 px, 55 px à
   2560 px), et la guirlande pend 27 px de plus. À 3,5rem fixes, la pointe
   ressortait de la section dès ~1400 px de large — reprise aussitôt par
   l'`overflow:clip` de la section ET par le crème de `.talleres`, qui
   peint par-dessus (deux `position:relative`, même z-index, ordre du DOM).
   La part proportionnelle suit la largeur ; il n'y a plus rien à rogner. */
/* ⚠ LE SOL DE LA SECTION RESTE CELUI DE LA PAGE — et j'ai essayé le contraire,
   à tort. Le panneau est incliné : sous son arête basse il reste forcément une
   bande du fond de section (58 px à 1440), et une autre au-dessus (25 px).
   C'est la place que la rotation réclame, ce n'est pas un défaut.

   J'ai voulu peindre cette bande basse en SABLE, pour qu'elle rejoigne
   `practica` qui suivait alors la section. L'ordre des blocs a rebougé,
   `talleres` (crème) est repassé dessous — et c'est le sable qui est devenu la
   rayure visible. Le décor dépendait de la couleur du VOISIN : exactement le
   défaut que le dégradé prétendait corriger, retourné.

   La bande garde donc le crème de la page. Contre une section crème elle est
   invisible ; contre le sable de `practica` elle se lit comme ce qu'elle est —
   la limite entre une section crème et une section sable, la même que partout
   ailleurs. Aucune hypothèse sur ce qui suit. */
/* ══ L'AGENDA SEULE SUR SA PAGE ═════════════════════════════════════════
   Le modificateur ne change PAS la couleur du panneau — le vert reste le vert
   partout. Il ne corrige qu'une chose : la moitié basse du sol, décrite plus
   bas, qui suppose que la section suivante est sable.

   ⚠ AU PASSAGE, UN BUG PRÉEXISTANT REPÉRÉ ICI LE 24/08 — non corrigé, car
     invisible tant que le vert reste : `.agenda .label{color:var(--ag-accent)}`
     est battue par `.agenda .head p.label, .sponsors …, .bic …, .tiltp …
     {color:var(--c-peach)}`, de spécificité 0,2,1 contre 0,2,0, qui code la
     pêche EN DUR. La règle au jeton est donc morte. Personne ne l'a vu parce
     que `--ag-accent` vaut justement `--c-peach` : les deux disent la même
     chose par coïncidence. Le jour où l'on voudra un autre accent, c'est là
     qu'il faudra revenir. */
.agenda--solo{--ag-sol-bas:var(--c-cream)}

.agenda{
  --ag-inset-t:3.5rem;
  --ag-inset-b:calc(3.5rem + 2.3vw);

  /* ⚠ LE SOL EST EN DEUX MOITIÉS, ET C'EST UN CHOIX ASSUMÉ — PAS UN RÉFLEXE.
     Le panneau est incliné : sous son arête basse il reste 58 px du sol de la
     section, et 25 px au-dessus. C'est la place que la rotation réclame.

     Cette bande basse est peinte en SABLE pour rejoindre `practica`, qui suit
     l'agenda et ouvre le mouvement PRÉPARER. La césure est à 50 %, donc à
     plus de 800 px sous l'arête haute du panneau et loin au-dessus de son
     arête basse : elle est invisible à toutes les hauteurs de section.

     ⚠ ET ELLE SUPPOSE QUE LA SECTION SUIVANTE EST SABLE. C'est la seule
       hypothèse de tout ce fichier sur l'ordre des blocs, et elle s'est déjà
       retournée une fois : le 19/08, `talleres` (crème) est repassé sous
       l'agenda et le sable est devenu une rayure visible. Si l'ordre change
       de nouveau, C'EST ICI qu'il faut revenir. Rien d'autre ne le signalera.

     ⚠ CORRIGÉ LE 24/08 : la teinte basse est passée en JETON. L'hypothèse
       s'est retournée une deuxième fois, sur la page « Programa » où l'agenda
       est suivi du CTA (crème) — le sable redevenait la rayure annoncée
       ci-dessus. `.agenda--solo`, posée par le bloc hors accueil, bascule le
       jeton sans toucher au reste. */
  background:linear-gradient(to bottom, var(--c-cream) 50%, var(--ag-sol-bas) 50%);
}

.agenda__frame{
  position:absolute;
  inset:var(--ag-inset-t) -12% var(--ag-inset-b);
  transform-origin:50%;
  transform:rotate(var(--ag-tilt))}

/* Le panneau remplit le repère. `overflow:hidden` reste : `agenda__texture`
   est en 22vw et `nowrap`, elle déborde à toutes les largeurs utiles. */
.agenda__panel{
  position:absolute;inset:0;overflow:hidden;
  background:var(--ag-panel);
  clip-path:polygon(4% 0%, 96% 0%, 100% 100%, 0% 100%)}

/* ── LA GUIRLANDE PEND SOUS LA BANDE ──────────────────────────────────
   Accrochée au bord BAS, pas au bord haut : le papel picado se suspend,
   il ne se pose pas — le masque ouvre ses pointes vers le bas. Une
   guirlande calée en tête de section barrait l'entrée ; en pied, elle
   referme la section et annonce la suivante.

   58 % de sa hauteur passent sous l'arête. Ce n'est pas un réglage à
   l'œil : le masque est plein de 0 à 28 px et pointu de 28 à 46 — il faut
   donc descendre d'au moins 39 % pour qu'un fanion soit un fanion. */
.agenda__frame > .picado{
  position:absolute;z-index:2;
  left:0;right:0;bottom:0;
  width:auto;margin:0;
  transform:translateY(58%)}


/* Texture typographique, contre-tournée pour rester droite à l'écran. */
.agenda__texture{position:absolute;left:50%;top:8%;
  transform:translateX(-50%);
  white-space:nowrap;pointer-events:none;user-select:none;
  font-family:var(--f-display);font-weight:900;
  font-variation-settings:'wdth' 125,'wght' 900;
  font-size:clamp(8rem,22vw,20rem);line-height:.8;letter-spacing:-.05em;
  color:var(--ag-fg);opacity:.10}

/* ── Le contenu, posé sur la bande ─────────────────────────────────── */
/* Le contenu se tient au MILIEU de la bande, jamais près des arêtes.
   C'est la condition pour que la déformation ne le découvre pas — et
   accessoirement une ligne d'agenda de 1 372 px était bien trop étirée :
   l'heure et l'étiquette se retrouvaient à un mètre l'une de l'autre. */
.agenda > .wrap{position:relative;z-index:1;color:var(--ag-fg);
  max-width:62rem}
.agenda .label{color:var(--ag-accent)}
.agenda .head p{color:var(--ag-fg-soft)}

.agenda__days{display:flex;gap:var(--s-3);flex-wrap:wrap;margin-bottom:var(--s-4)}
.agenda__day{cursor:pointer;font:inherit;text-align:left;border:0;display:grid;gap:2px;
  padding:var(--s-4) var(--s-5);border-radius:var(--r-md);
  background:var(--ag-veil);color:var(--ag-fg);
  box-shadow:inset 0 0 0 1.5px var(--ag-line);
  transition:background-color .3s,box-shadow .3s,transform .4s var(--ease-out)}
.agenda__day b{font-family:var(--f-display);font-weight:900;text-transform:uppercase;
  font-size:clamp(1.1rem,2.4vw,1.5rem);letter-spacing:-.02em;line-height:1}
.agenda__day span{font-size:.875rem;color:var(--ag-fg-soft)}
.agenda__day:hover{background:var(--ag-veil-hi)}
.agenda__day.is-on{background:var(--ag-fg);color:var(--ag-panel);
  box-shadow:inset 0 0 0 1.5px var(--ag-fg);transform:translateY(-2px)}
.agenda__day.is-on span{color:var(--ag-panel);opacity:.75}

.agenda__filters{display:flex;gap:var(--s-2);flex-wrap:wrap;margin-bottom:var(--s-6)}
.agenda .chip{background:var(--ag-veil);color:var(--ag-fg)}
.agenda .chip:hover{background:var(--ag-veil-hi)}
.agenda .chip.is-on{background:var(--ag-fg);color:var(--ag-panel)}
.agenda .chip--avail{box-shadow:inset 0 0 0 1.5px var(--ag-cta)}
.agenda .chip--avail[aria-pressed="true"]{background:var(--ag-cta);color:var(--c-ink);box-shadow:none}

.agenda__list{list-style:none;margin:0;padding:0;display:grid;gap:var(--s-2)}
.agenda__i{display:grid;grid-template-columns:auto 1fr auto;gap:var(--s-4);align-items:center;
  padding:var(--s-4) var(--s-5);border-radius:var(--r-md);
  background:var(--ag-veil);border-left:3px solid transparent;
  transition:background-color .3s,border-color .3s}
.agenda__i:hover{background:var(--ag-veil-hi)}
/* Le filet de gauche encode la catégorie : la liste se lit en diagonale,
   avant même d'avoir lu les titres. */
.agenda__i[data-cat="taller"] {border-left-color:var(--ag-cta)}
.agenda__i[data-cat="musica"] {border-left-color:var(--c-rojo)}
.agenda__i[data-cat="gourmet"]{border-left-color:var(--ag-accent)}
.agenda__i[data-cat="oficial"]{border-left-color:var(--c-cci-lite)}

.agenda__h{margin:0;min-width:4.5ch;color:var(--ag-accent);
  font-family:var(--f-display);font-weight:900;letter-spacing:-.02em;
  font-size:clamp(1.05rem,2vw,1.35rem)}
.agenda__c h3{margin:0 0 3px;font-family:var(--f-display);font-weight:700;
  font-size:var(--t-body);line-height:1.25;text-transform:none;letter-spacing:0}
.agenda__z{margin:0;font-size:var(--t-small);color:var(--ag-fg-soft)}

.agenda__tag{white-space:nowrap;padding:5px 11px;border-radius:var(--r-pill);
  font-size:.6875rem;font-weight:700;letter-spacing:var(--ls-label);text-transform:uppercase}
.agenda__tag--free{color:var(--ag-fg-soft);box-shadow:inset 0 0 0 1.5px var(--ag-line)}

/* Le nombre de places vit DANS le bouton : c'est l'information qui décide,
   elle ne doit pas être à côté de l'action mais dedans. */
.agenda__book{display:grid;justify-items:center;gap:1px;text-decoration:none;
  padding:8px 14px;border-radius:var(--r-md);
  background:var(--ag-cta);color:var(--c-ink);
  transition:transform .4s var(--ease-out),filter .3s}
.agenda__book b{font-family:var(--f-display);font-weight:900;font-size:1.15rem;line-height:1}
.agenda__book span{font-size:.625rem;letter-spacing:.08em;text-transform:uppercase;opacity:.92}
.agenda__book:hover{transform:translateY(-2px);filter:brightness(1.08)}
.agenda__book--full{background:transparent;color:var(--ag-accent);
  box-shadow:inset 0 0 0 1.5px var(--ag-line)}

/* La queue de journée non communiquée : dite, pas inventée. */
.agenda__pending{margin-top:var(--s-3);padding:var(--s-5);border-radius:var(--r-md);
  border:1.5px dashed var(--ag-line);color:var(--ag-fg-soft);font-size:var(--t-small)}
.agenda__pending p{margin:0}
.agenda__pending b{color:var(--ag-fg)}

/* ── Aucun résultat pour la combinaison de filtres ────────────────────────
   Même boîte pointillée que la note préliminaire : c'est le même registre —
   un message du site, pas une activité. Le bouton de sortie est DANS la
   phrase, là où le regard s'arrête, et non renvoyé vers la barre de filtres
   qu'il faudrait d'abord retrouver. */
.agenda__vide{margin-top:var(--s-3);padding:var(--s-5);border-radius:var(--r-md);
  border:1.5px dashed var(--ag-line);color:var(--ag-fg-soft);font-size:var(--t-small)}
.agenda__vide p{margin:0}
.agenda__reset{font:inherit;color:var(--ag-fg);background:none;border:0;padding:0;
  margin-left:.35em;cursor:pointer;text-decoration:underline;text-underline-offset:3px}
.agenda__reset:hover,.agenda__reset:focus-visible{color:var(--c-rojo)}

@media(max-width:640px){
  .agenda__i{grid-template-columns:auto 1fr;row-gap:var(--s-3)}
  .agenda__i>:last-child{grid-column:2;justify-self:start}
}

/* ── 4 · PLANO — la recherche passe devant le plan ───────────────────
   900 m² : le visiteur ne se perd pas, il cherche UNE marque parmi 40.  */
.plano__search{margin-bottom:var(--s-5)}
.plano__search input{width:100%;font:inherit;font-size:var(--t-lead);
  padding:var(--s-4) var(--s-5);border-radius:var(--r-pill);
  border:1.5px solid var(--c-line);background:#fff;color:var(--c-ink);
  transition:border-color .3s,box-shadow .3s}
.plano__search input::placeholder{color:var(--c-muted)}
.plano__search input:focus{outline:0;border-color:var(--c-ink);
  box-shadow:0 0 0 4px rgba(10,37,64,.1)}
.plano__searchhint{margin:var(--s-2) 0 0;font-size:var(--t-small);color:var(--c-muted)}

/* ── 5 · PARTENAIRES EN TYPOGRAPHIE (logos non livrés) ───────────────── */
.scard__marks{display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--s-2) var(--s-4);margin:0}
.sponsors__brands{margin-top:var(--s-7);padding-top:var(--s-6);
  border-top:1px solid var(--c-line-inv);text-align:center}
.sponsors__brandstitle{margin:0 0 var(--s-4);font-size:var(--t-label);font-weight:700;
  letter-spacing:var(--ls-label);text-transform:uppercase;color:var(--c-cci-lite)}
.sponsors__brandsrow{display:flex;flex-wrap:wrap;justify-content:center;align-items:center;
  gap:var(--s-5);margin:0}
.sponsors__sep{width:1px;height:1.6em;background:var(--c-line-inv)}
.sponsors__note{margin:var(--s-4) 0 0;font-size:var(--t-small);color:var(--c-cci-lite)}

.brandmark--text{font-family:var(--f-display);font-weight:900;text-transform:uppercase;
  letter-spacing:-.01em;line-height:1;font-size:var(--logo-n1);
  color:var(--c-cream);text-decoration:none}
/* Emplacement vide au bon gabarit : la composition reste jugeable,
   et rien n'est affirmé sur des marques non confirmées. */
.logo--slot{display:inline-block;width:clamp(84px,12vw,124px);height:var(--logo-n3);
  border-radius:6px;border:1.5px dashed var(--c-line-inv);opacity:.5}
.thanks__brandsnote{margin:var(--s-4) 0 0;font-size:var(--t-small);
  color:var(--c-cci-lite);text-align:center}

/* ── 6 · MODE LIVE — le jour J, la page change de métier ─────────────
   Elle ne SÉDUIT plus, elle ORIENTE. Une seule classe sur <html>, posée
   côté serveur par le plugin en comparant l'heure courante (fuseau du
   lieu) aux dates de l'édition.                                        */
.live{display:none}
.is-live .live{display:block;position:relative;z-index:2;
  background:var(--c-ink);color:var(--c-cream);padding-block:var(--s-6)}
.live__now{display:flex;align-items:center;gap:6px;margin:0 0 var(--s-4);
  font-size:var(--t-label);font-weight:700;letter-spacing:var(--ls-label);
  text-transform:uppercase;color:var(--c-coral)}
.live__grid{display:grid;gap:var(--s-4);
  grid-template-columns:repeat(auto-fit,minmax(240px,1fr))}
.live__card{display:grid;gap:6px;align-content:start;padding:var(--s-5);
  border-radius:var(--r-md);background:rgba(247,241,228,.06);
  box-shadow:inset 0 0 0 1.5px var(--c-line-inv)}
.live__card h2{margin:0;font-family:var(--f-display);font-weight:900;text-transform:uppercase;
  font-size:clamp(1.3rem,3vw,1.9rem);letter-spacing:-.03em;line-height:1}
.live__k{margin:0;font-size:var(--t-label);font-weight:700;letter-spacing:var(--ls-label);
  text-transform:uppercase;color:var(--c-cci-lite)}
.live__z{margin:0;font-size:var(--t-small);color:var(--c-cream-2)}
.live__card--now{background:var(--c-ink-3);box-shadow:inset 0 0 0 1.5px rgba(255,201,163,.35)}
.live__card--now .live__k{color:var(--c-peach)}
.live__card--free{box-shadow:inset 0 0 0 1.5px color-mix(in srgb,var(--c-verde) 60%,transparent)}
.live__card--free .live__k{color:var(--c-verde)}
.live__card--free .btn{justify-self:start;margin-top:var(--s-2)}

/* Ce qui n'a plus de sens une fois sur place : le récit institutionnel
   et le compte à rebours d'un événement déjà commencé. */
.is-live .manifesto,
.is-live .bic,
.is-live .why,
.is-live #count{display:none}
/* La bande partenaires, elle, RESTE affichée le jour J : les impressions
   sponsors comptent surtout pendant l'événement. Seul le récit part. */
/* Les proportions du dock s'inversent : orienter passe devant convertir. */
.is-live .dock__practica{flex:1 1 auto}
.is-live .dock__cta{flex:0 0 auto}

/* Pour prévisualiser le mode festival sans bouton de démo : ajouter la
   classe `is-live` sur <html> dans l'inspecteur. En production, c'est le
   plugin qui la pose, en comparant l'heure courante aux dates de l'édition. */

@media(prefers-reduced-motion:reduce){
  .agenda__day,.agenda__book,.hzp__link,.agenda__i{transition:none}
  .agenda__day.is-on{transform:none}
}

/* ── 7 · POR QUÉ 2026 — l'emblème officiel remplace la frise ─────────
   Les styles .bic (plus haut) sont CONSERVÉS : la frise complète part sur
   la page dédiée /bicentenario, elle n'est pas supprimée du projet.     */
.why{display:grid;grid-template-columns:minmax(180px,300px) 1fr;
  gap:var(--s-6) var(--s-7);align-items:center;
  padding-bottom:var(--s-7);margin-bottom:var(--s-7);
  border-bottom:1px solid var(--c-line-inv)}
.why__emblem{margin:0}
/* Le fichier client est sur fond blanc : sur le bleu il ferait un pavé.
   Monté sur une plaque crème, il se lit comme un sceau — ce qu'il est. */
.why__emblem img{display:block;width:100%;height:auto;
  background:var(--c-cream);border-radius:var(--r-lg);padding:var(--s-5)}
.why__txt{display:grid;gap:var(--s-3)}
.why .label{color:var(--c-peach)}
.why__h{margin:0;font-family:var(--f-display);font-weight:900;text-transform:uppercase;
  font-size:clamp(1.35rem,3vw,2.3rem);letter-spacing:-.03em;line-height:1.04}
.why__txt p{margin:0;color:var(--c-cream-2)}

@media(max-width:760px){
  .why{grid-template-columns:1fr;gap:var(--s-5)}
  .why__emblem{max-width:240px}
}

/* ── 8 · LE PANNEAU « INFO PRÁCTICA » SOUS L'EN-TÊTE ──────────────────

   SYMPTÔME 1 — en haut de page : le logotype disparaît.
   SYMPTÔME 2 — après scroll : la limite entre en-tête et panneau devient
   illisible.

   UNE SEULE CAUSE. `.ov` commençait à --s-6 (32 px) du haut, alors que
   l'en-tête mesure 108 px bandeau déplié, 74 px replié. Les deux se
   superposaient donc TOUJOURS ; seul le rendu du chevauchement changeait :

     · au sommet, l'en-tête n'est pas `.is-solid` : il affiche sa variante
       CLAIRE, prévue pour le hero sombre. Posée sur le crème du panneau,
       elle devient invisible — d'où le logotype fantôme ;
     · une fois scrollé, l'en-tête devient crème translucide et se pose sur
       un panneau crème : deux surfaces de même couleur, plus de frontière.

   Le panneau blanc laisse volontairement l'en-tête visible et cliquable
   (`.practica-open:not(.menu-open) .header{z-index:135}`), pour qu'on
   puisse appeler le menu bleu par-dessus. Il doit donc commencer SOUS lui,
   et suivre sa hauteur réelle — 34 px de moins dès que le bandeau se
   replie. Le menu bleu, lui, garde son plein écran : il masque l'en-tête. */
:root{
  --header-banner-h:34px;
  --header-inner-h :74px;
  --header-h       :calc(var(--header-banner-h) + var(--header-inner-h));
}
@media(max-width:960px){ :root{--header-inner-h:62px} }

/* Même durée que le repli du bandeau : les deux gestes n'en font qu'un. */
.ov{transition:top .45s var(--ease-out)}
.practica-open:not(.menu-open) .ov{top:var(--header-h)}
.practica-open:not(.menu-open) .header.is-solid ~ .ov{top:var(--header-inner-h)}

/* L'en-tête reprend son filet même transparent : sur le crème du panneau,
   c'est lui qui matérialise la frontière entre les deux surfaces. */
.practica-open:not(.menu-open) .header{border-bottom-color:var(--c-line)}

@media(prefers-reduced-motion:reduce){ .ov{transition:none} }

/* ══════════════════════════════════════════════════════════════════════
   ZONES · EMPILEMENT COLLANT — d'après la section « Calendar » d'Artefact

   Chaque bande est `position:sticky` avec un `top` qui croît de 2 rem par
   bande. Elles s'arrêtent donc en escalier et la suivante recouvre la
   précédente en laissant voir sa tranche. Aucun JavaScript : le geste est
   entièrement porté par le défilement natif.
   ══════════════════════════════════════════════════════════════════════ */
.zstack{background:var(--c-cream);color:var(--c-ink)}
.zstack .label{color:var(--c-rojo-txt)}

/* ⚠ PLUS DE GRILLE. Une grille impose deux colonnes qui se partagent la
   largeur : les bandes se retrouvent forcément décalées à droite du centre
   de l'écran. Chez Artefact tout est en positionnement absolu dans une
   même boîte — les bandes sont CENTRÉES et le titre flotte par-dessous, à
   gauche. C'est cette structure-là qu'il faut, pas un compromis de grille. */
.zstack__grid{position:relative;
  /* Largeur de la colonne du titre — une seule valeur, consommée par le
     titre ET par la garde du bloc de bandes. */
  --zs-title-w:min(16rem,24vw)}

/* Deux niveaux, comme chez eux : une boîte absolue qui couvre TOUTE la
   hauteur de la section, et à l'intérieur le titre en `sticky`. Un sticky
   posé directement sur une boîte de la hauteur de son texte n'a aucune
   course : il ne collerait jamais. */
.zstack__head{position:absolute;left:0;top:0;bottom:0;z-index:1;
  width:var(--zs-title-w);pointer-events:none}
.zstack__head-in{position:sticky;pointer-events:auto;
  top:calc(var(--header-inner-h,74px) + var(--s-6))}
.zstack__word{--wc-size:clamp(2rem,4.2vw,3.6rem)}
.zstack__head .label{margin-bottom:var(--s-4)}
/* ── SOUS-TITRE DU MOT DÉCOUPÉ ────────────────────────────────────────
   La phrase que le mot ne peut pas porter. Le mot dit DE QUOI il s'agit,
   la phrase dit CE QU'ON Y GAGNE. Séparer les deux est ce qui permet au
   titre d'être un seul mot sans rien perdre en information. */
.wordcut-sub{margin:var(--s-4) 0 0;max-width:26ch;color:inherit;
  font-family:var(--f-display);font-weight:800;
  font-variation-settings:'wdth' 108,'wght' 800;
  text-transform:uppercase;letter-spacing:-.015em;line-height:1.14;
  font-size:clamp(1rem,1.8vw,1.35rem)}
.wordcut-sub em{font-family:var(--f-sans);font-style:italic;font-weight:400;
  font-variation-settings:normal;text-transform:none;color:var(--c-rojo-txt)}
/* Sur fond sombre, le rouge d'accent tombe à 2,2:1 — la pêche prend le relais. */
.sponsors .wordcut-sub em{color:var(--c-peach)}
.zstack .wordcut-sub{max-width:16ch}
.zstack__hint{margin-top:var(--s-5);font-size:var(--t-label);
  letter-spacing:var(--ls-label);text-transform:uppercase;color:var(--c-muted)}

/* Les bandes : un bloc CENTRÉ sur l'écran, indépendant du titre.
   58rem ≈ 928 px — mesuré chez eux, les cartes font ~896 px et leur centre
   tombe sur le centre de l'écran. Le titre passe dessous (z-index 1), donc
   la bande mord sur ses dernières lettres : c'est l'alignement voulu. */
 /* ⚠ GARDE ANTI-CHEVAUCHEMENT.
    Le bloc est centré, donc son bord gauche remonte vers le titre dès que
    la fenêtre rétrécit — à 1250 px il lui passait dessus. Plafonner la
    largeur à « tout sauf deux colonnes de titre » garantit un écart
    CONSTANT des deux côtés, quelle que soit la largeur. Le bloc reste
    centré, il maigrit au lieu d'empiéter. */
.zstack__list{position:relative;z-index:2;
  list-style:none;margin:0 auto;padding:0;display:grid;gap:var(--s-5);
  max-width:min(58rem, calc(100% - (var(--zs-title-w) + var(--s-6)) * 2))}

.zs{position:sticky;top:var(--zs-top,10rem)}
.zs:nth-child(1){--zs-top:8rem}
.zs:nth-child(2){--zs-top:10rem}
.zs:nth-child(3){--zs-top:12rem}
.zs:nth-child(4){--zs-top:14rem}
.zs:nth-child(5){--zs-top:16rem}
.zs:nth-child(6){--zs-top:18rem}
/* Au-delà de six, l'escalier cesse de se creuser : sinon la 10ᵉ bande
   s'arrêterait à 26 rem du haut et il ne resterait plus d'écran. */
.zs:nth-child(n+7){--zs-top:18rem}

/* ── LE CYCLE DE PRÉSENTATION ─────────────────────────────────────────
   Couleur, rotation, retraits : ils étaient écrits en `style=""` sur
   chaque bande. C'était de la présentation transportée par le contenu —
   et surtout, une 7ᵉ zone saisie par le client n'aurait eu AUCUN style.

   En cycle de six (`6n+k`), n'importe quel nombre de zones fonctionne :
   la septième reprend l'apparence de la première. Le plugin n'émettra donc
   que du contenu, jamais une couleur. */
/* ⚠ Recalibrées pour un sol CRÈME : la bande crème et la bande pêche
   d'avant disparaissaient dessus. Les six teintes sont maintenant toutes
   franches, alternées clair/sombre pour que la pile se lise de loin. */
.zs:nth-child(6n+1){--zs-bg:var(--c-coral);   --zs-fg:var(--c-ink);   --zs-rot:-1.6deg;--zs-x:2rem;  --zs-r:4.5rem}
.zs:nth-child(6n+2){--zs-bg:var(--c-rojo);    --zs-fg:var(--c-cream); --zs-rot:1.1deg; --zs-x:5.5rem;--zs-r:0}
.zs:nth-child(6n+3){--zs-bg:var(--c-verde);   --zs-fg:var(--c-cream); --zs-rot:-1.3deg;--zs-x:1rem;  --zs-r:6.5rem}
.zs:nth-child(6n+4){--zs-bg:var(--c-ink);     --zs-fg:var(--c-cream); --zs-rot:1.7deg; --zs-x:3.5rem;--zs-r:2rem}
.zs:nth-child(6n+5){--zs-bg:var(--c-azul);    --zs-fg:var(--c-cream); --zs-rot:-0.9deg;--zs-x:0;     --zs-r:5rem}
.zs:nth-child(6n+6){--zs-bg:var(--c-cci-lite);--zs-fg:var(--c-ink);   --zs-rot:1.4deg; --zs-x:4.5rem;--zs-r:1rem}

/* La rotation porte sur la carte INTERNE, pas sur l'élément collant :
   un `transform` sur un `position:sticky` crée un contexte de conteneur
   qui annule l'adhérence. C'est exactement la structure d'Artefact —
   wrapper collant, bloc tourné à l'intérieur. */
/* Chaque bande a SON retrait gauche et SA réserve à droite : c'est ce qui
   fait qu'elles n'ont ni la même largeur ni le même départ, comme les
   `left`/`width` variables de la référence. Alignées au cordeau, l'effet
   d'empilement disparaît — on ne verrait qu'une pile de rectangles. */
.zs__card{background:var(--zs-bg);color:var(--zs-fg);
  margin-left:var(--zs-x,0);margin-right:var(--zs-r,0);
  padding:var(--s-5) var(--s-6);border-radius:var(--r-md);
  transform:rotate(var(--zs-rot,0deg));
  box-shadow:0 22px 52px -28px rgba(0,42,79,.34)}

.zs__when{margin:0 0 var(--s-3);font-family:var(--f-sans);font-weight:400;
  text-transform:uppercase;line-height:.85;letter-spacing:-.01em;
  font-size:clamp(1.3rem,2.6vw,2.1rem);opacity:.9}
.zs__when b{font-weight:400;font-variant-numeric:tabular-nums}

.zs__name{margin:0;font-family:var(--f-display);font-weight:900;
  font-variation-settings:'wdth' 112,'wght' 900;text-transform:uppercase;
  line-height:.92;letter-spacing:-.035em;
  font-size:clamp(1.7rem,4vw,3.2rem)}

.zs__desc{margin:var(--s-3) 0 0;font-size:var(--t-small);line-height:1.5;
  max-width:52ch;opacity:.82}

.zs__link{display:inline-block;margin-top:var(--s-3);padding-bottom:2px;
  font-family:var(--f-display);font-weight:700;font-size:.875rem;
  letter-spacing:.04em;text-decoration:none;color:inherit;
  border-bottom:1.5px solid currentColor;opacity:.85;transition:opacity .3s}
.zs__link:hover{opacity:1}

@media(max-width:1024px){
  /* Sous 1024, la colonne ne laisse plus assez de place : le titre
     repasse dans le flux, au-dessus des bandes. */
  .zstack__head{position:static;width:auto;margin-bottom:var(--s-6)}
  .zstack__head-in{position:static}
  .zstack__list{max-width:none}
  .zs__card{margin-left:0;margin-right:0}
  /* Le titre cesse de coller : sur un écran étroit il mangerait la moitié
     de la hauteur utile pendant tout l'empilement. */
  .zstack__head{position:static}
  .zs:nth-child(1){--zs-top:5rem}
  .zs:nth-child(2){--zs-top:6.5rem}
  .zs:nth-child(3){--zs-top:8rem}
  .zs:nth-child(4){--zs-top:9.5rem}
  .zs:nth-child(5){--zs-top:11rem}
  .zs:nth-child(6){--zs-top:12.5rem}
  .zs:nth-child(n+7){--zs-top:12.5rem}
}

/* ══════════════════════════════════════════════════════════════════════
   PANNEAU INCLINÉ — d'après la section « Jury » d'Artefact

   Le panneau est épinglé et se déforme au fil du défilement, suivant la
   loi relevée sur leur page à trois points de mesure (écart 0,044 %) :

       retrait latéral 3,696·r      coin haut-gauche 1,773·r
       échelle  1 − 0,0333·r        coin haut-droit  3,546·r

   Le paramètre est `--tp-r`, l'angle en degrés, écrit par le JavaScript.

   ⚠ DEUX CALQUES, JAMAIS UN SEUL. Le panneau va jusqu'à 84 % d'échelle et
   perd 36 % de sa largeur en haut : il ne peut se permettre ça que parce
   qu'il ne porte RIEN (`aria-hidden`, `pointer-events:none`). Le contenu
   vit dans un calque distinct, jamais déformé, borné à 36 rem centrales.
   À 5°, le panneau ne couvre plus que 792 px sur 1 508 : il reste 108 px
   de marge de chaque côté du contenu. C'est cette marge qui autorise
   l'amplitude complète.

   C'est la leçon de l'agenda : un panneau qui porte du texte ne peut pas
   se déformer autant, et un panneau qui se déforme autant ne peut pas
   porter de texte.
   ══════════════════════════════════════════════════════════════════════ */
.tiltp{
  --tp-panel:var(--c-azul);
  position:relative;overflow:clip;
  background:var(--c-ink);color:var(--c-cream);
  min-height:200vh}

.tiltp__stage{position:absolute;inset:0;z-index:0;pointer-events:none}

.tiltp__panel{
  --tp-r:0;
  position:sticky;top:0;height:100vh;width:100%;overflow:hidden;
  background:var(--tp-panel);transform-origin:50%;
  transform:rotate(calc(var(--tp-r) * 1deg))
            scale(calc(1 - var(--tp-r) * 0.0333));
  clip-path:polygon(
    calc(var(--tp-r) * 3.696%)        calc(var(--tp-r) * 1.773%),
    calc(100% - var(--tp-r) * 3.696%) calc(var(--tp-r) * 3.546%),
    100% 100%,
    0% 100%)}

/* Texture typographique, à l'opacité de la leur. */
.tiltp__texture{position:absolute;left:50%;top:12%;
  transform:translateX(-50%);
  white-space:nowrap;pointer-events:none;user-select:none;
  font-family:var(--f-display);font-weight:900;
  font-variation-settings:'wdth' 125,'wght' 900;
  font-size:clamp(7rem,19vw,17rem);line-height:.8;letter-spacing:-.05em;
  color:var(--c-cream);opacity:.10}

/* Le contenu — au-dessus, au centre, jamais déformé. */
.tiltp__content{position:relative;z-index:1;
  max-width:36rem;margin-inline:auto;
  padding-block:42vh 30vh}
.tiltp .label{color:var(--c-peach)}
.tiltp .wordcut-sub{max-width:22ch}

.tiltp__people{list-style:none;margin:var(--s-8) 0 0;padding:0;
  display:grid;gap:var(--s-5)}
.tiltp__p{display:grid;grid-template-columns:auto 1fr;gap:var(--s-5);
  align-items:center;padding-top:var(--s-5);
  border-top:1px solid var(--c-line-inv)}
.tiltp__portrait{width:72px;height:72px;border-radius:50%;
  background:rgba(251,247,240,.14);
  box-shadow:inset 0 0 0 1.5px var(--c-line-inv)}
.tiltp__name{margin:0;font-family:var(--f-display);font-weight:900;
  text-transform:uppercase;letter-spacing:-.02em;line-height:1.05;
  font-size:clamp(1.1rem,2.2vw,1.5rem)}
.tiltp__role{margin:4px 0 0;grid-column:2;
  font-size:var(--t-small);line-height:1.4;color:var(--c-cream-2)}

/* ── Le renvoi vers l'entretien ──────────────────────────────────────────
   Même colonne que la fonction — la carte est une grille : le portrait tient
   la colonne 1, le texte la 2. Souligné en permanence : c'est un lien dans un
   bloc de texte, et rien d'autre ne le signale. */
.tiltp__video{margin:6px 0 0;grid-column:2;font-size:var(--t-small);line-height:1.4}
.tiltp__video a{color:var(--c-peach);text-decoration:underline;
  text-underline-offset:3px;padding-block:2px;
  transition:color .25s var(--ease-out)}
.tiltp__video a:hover,.tiltp__video a:focus-visible{color:var(--c-cream)}

@media(max-width:900px){
  .tiltp{min-height:0}
  .tiltp__panel{position:absolute;inset:0;height:auto;
    transform:rotate(-1.5deg);clip-path:none}
  .tiltp__content{padding-block:var(--s-9)}
}

/* ── LE DOCK, RÉDUIT À SA MOITIÉ GAUCHE SUR DESKTOP ───────────────────
   Deux barres persistantes — l'en-tête et le dock complet — encombraient
   la lecture. Mais supprimer le dock entier retirait aussi le seul accès
   direct au panneau d'informations pratiques.

   Sur desktop il ne reste donc QUE ce bouton, tel qu'il était : une
   porte, pas une barre. Le CTA, lui, vit désormais dans le hero.
   Sur mobile le dock garde ses deux moitiés : c'est la seule surface
   tactile permanente. */
/* ── LE CTA DU DOCK REVIENT, MAIS SOUS CONDITION ──────────────────────
   J'avais posé ici `.dock__cta{display:none}` sur desktop, en croyant que
   le CTA faisait doublon avec celui du hero. Il le faisait — mais SEULEMENT
   pendant le hero, et le fichier savait déjà le gérer : `.is-over-hero`
   (posée par IntersectionObserver) le fait glisser hors champ puis revenir.
   Un `display:none` en dur écrasait cette mécanique au lieu de s'appuyer
   dessus. Règle retirée.

   La condition est étendue à sa conclusion logique : le CTA flottant ne
   doit JAMAIS proposer ce qui est déjà à l'écran.

     · hero            → il porte son propre bouton
     · #talleres       → les vrais billets sont là, chacun avec le sien.
                         Un bouton qui défile vers l'endroit où l'on se
                         trouve déjà est une commande morte : on clique,
                         rien ne bouge.
     · CTA de clôture  → le pied de page porte sa propre invitation

   Partout ailleurs il apparaît — et notamment dans `zonas`, seule section
   de la page qui ne contenait aucune porte vers la réservation.

   Effet de bord voulu sur l'emphase : le CTA flottant est rouge plein. Ces
   trois exclusions garantissent qu'il n'y a jamais DEUX pilules rouges
   pleines à l'écran en même temps — la règle posée sur le hero tient donc
   sur toute la longueur de la page, pas seulement au premier écran.

   `visibility` + `translate` plutôt que `display` : l'élément garde sa
   place dans la barre, donc « Info práctica » ne se déplace pas quand le
   CTA va et vient. Et la transition existe. */
.dock.is-cta-quiet .dock__cta{
  transform:translateY(150%);opacity:0;visibility:hidden;pointer-events:none}

/* ── LE BILLET PORTE L'ACTION ─────────────────────────────────────────
   Le CTA était sous les quatre mots de l'affiche, à douze pixels du
   texte : deux appels concurrents dans le même bloc. Dans le billet, il
   prolonge l'information au lieu de la doubler. */
/* Les deux actions du hero, sous les quatre mots de l'affiche.
   Le billet ne peut pas les porter : le hero est en `min-height:100svh`
   avec `overflow:hidden`, et tout ce que le billet gagne en hauteur se
   fait rogner par le bas. */
.hero__actions{display:flex;flex-wrap:wrap;gap:var(--s-4);align-items:center}
.hero .btn--ghost{color:var(--c-cream);box-shadow:inset 0 0 0 1.5px rgba(247,241,228,.42);
  --btn-fill:var(--c-cream)}
.hero .btn--ghost:hover{color:var(--c-ink)}

/* ══ LES ARRONDIS TIENNENT PENDANT LA SORTIE ══════════════════════════
   Le rayon du panneau blanc est accroché à `practica-open` / `menu-open`,
   posées sur <html>. Or le JS les retire d'un coup à la fermeture, tandis
   que la transition de sortie, elle, court encore ~720 ms. `border-radius`
   n'étant pas animé, il CLAQUE : le panneau retombait sur son rayon de
   base (bord gauche carré) et sortait de l'écran en rectangle.

   `is-closing` est justement la classe qui survit jusqu'à la fin de
   l'animation — c'est le bon crochet. Spécificité (0,3,0) : elle l'emporte
   sans dépendre de sa position dans le fichier.

   Pas de variante mobile : à l'état ouvert, `.practica-open` (0,2,0) bat
   déjà le `border-radius:0` du bloc @media (une media query n'ajoute pas
   de spécificité). Le panneau est donc arrondi sur mobile aussi, et la
   sortie doit refléter l'état ouvert — sinon on ne fait que déplacer le
   claquement au lieu de le supprimer.

   Le panneau bleu n'est pas concerné : son rayon est inconditionnel. */
.ov.is-closing .ov__practica{border-radius:var(--r-lg)}

/* ══════════════════════════════════════════════════════════════════════
   HERO · HIÉRARCHIE DE CONVERSION
   Le premier écran était beau mais plat : treize formulations pour quatre
   informations, et l'objet le plus voyant n'était pas cliquable. Ce bloc
   corrige la hiérarchie ; le contenu, lui, a été dédupliqué côté gabarit.
   ══════════════════════════════════════════════════════════════════════ */

/* ── ③ UN SEUL ROUGE SATURÉ, ET IL EST CLIQUABLE ──────────────────────
   Le billet et le CTA partageaient --c-rojo. Sauf que le billet occupe
   environ six fois la surface du bouton : le regard allait donc au DÉCOR,
   pas à l'action. Le rouge n'a jamais été le problème — c'est le fait que
   le plus grand aplat de la page ne mène nulle part.

   On ne retire pas le rouge du billet : c'est lui, le « wow », et c'est la
   couleur du carton d'invitation client. On le descend d'un cran, sur
   --color-carmin-deep (#B80B39), déjà présent dans les jetons. Le carmín
   pur redevient exclusif au bouton — assez proche pour rester la même
   famille, assez distinct pour que l'œil sache lequel des deux appelle. */
.pass__top,
.pass__picado{background:var(--color-carmin-deep,#B80B39)}

/* Le liseré « ENTRADA LIBRE » se détachait sur le carmín vif ; sur le
   carmín profond il gagne encore en contraste. Rien à changer, mais on le
   note : c'est ce qui garantit que la gratuité reste lisible d'un coup
   d'œil, et c'est le premier argument de l'événement. */

/* ── ④ LA PREUVE, COLLÉE AU BOUTON ───────────────────────────────────
   Gratuit ET limité : les deux seules questions qui restent à l'instant du
   clic. Volontairement discrète — elle ne doit pas concurrencer le bouton
   qu'elle sert, seulement lever le dernier doute une fois le regard posé. */
.hero__proof{margin:var(--s-3) 0 0;font-family:var(--f-mono);
  font-size:clamp(.8125rem,1.15vw,.9375rem);letter-spacing:.06em;
  color:var(--c-cream);opacity:.72}

/* ── ④ bis · LE COMPTE À REBOURS REMONTE ─────────────────────────────
   « Faltan 75 días » était un murmure gris en bas de la souche. C'est
   pourtant le seul élément de la page qui change tout seul, et l'urgence
   est le levier le plus fort d'un événement daté à places limitées.
   Il passe en chiffre lu, pas en note de bas de page. */
.pass__stub .count{font-family:var(--f-mono);letter-spacing:.04em}
.pass__stub .count b{font-size:1.5rem;line-height:1;
  font-family:var(--f-display);font-variation-settings:'wdth' 112,'wght' 900;
  margin-inline:2px}

/* ── ⑤ LES ÉTIQUETTES CESSENT D'ÊTRE DES BOUTONS ──────────────────────
   Deux problèmes distincts se cachaient dans ces quatre mots, et il a fallu
   deux passes pour les démêler.

   1. FAUSSE AFFORDANCE : pastille + contour + casse haute, sans clic.
      Réglé en les rendant cliquables — le contenu existe, autant y mener.

   2. CONCURRENCE VISUELLE, révélée par le premier correctif : une pastille
      arrondie à contour EST le vocabulaire du bouton. En poser quatre juste
      au-dessus du seul vrai bouton de la page lui donne quatre rivaux, au
      moment précis où l'on demande un engagement. C'est la règle d'emphase
      qui gouverne déjà .btn--primary et .pass__cta : la FORME porte le
      rang, pas seulement la couleur.

   La sortie n'est pas de les supprimer. Le h1 porte la signature
   institutionnelle (« 200 años de amistad ») et ne dit donc pas ce qu'on
   vient FAIRE ; l'affiche du client hiérarchise elle-même dates → nom →
   ce qu'on y fait. Sans ces quatre mots, le premier écran n'a plus une
   seule promesse sensorielle.

   On garde les mots, on retire le costume. Filet typographique séparé par
   des points médians : ça se lit comme une énumération, pas comme une
   barre d'outils. Le lien reste — souligné au survol, l'affordance honnête
   d'un lien de texte. Bénéfice secondaire : une seule ligne au lieu d'une
   rangée qui se replie, donc le bouton remonte dans l'écran. */
.hero__tags{gap:0 var(--s-3);margin-top:var(--s-3);align-items:baseline}
.hero__tags li{padding:0;box-shadow:none;display:inline-flex;align-items:baseline;gap:var(--s-3)}
/* ⚠ LE SÉPARATEUR SUIT SON MOT, IL NE PRÉCÈDE PAS LE SUIVANT.
   Il était porté par `li + li::before` : tant que la liste tenait sur une
   ligne, cela revenait au même. Depuis qu'elle passe à la ligne sur
   téléphone (04/09), un point ouvrait la seconde ligne — « · TALLERES » —
   ce qui se lit comme une puce oubliée. Rattaché au mot qui le précède, il
   ne peut plus se retrouver en tête de ligne. Le compte est le même :
   trois séparateurs pour quatre entrées, l'un exclut le premier élément,
   l'autre le dernier. */
.hero__tags li:not(:last-child)::after{content:'·';opacity:.45;font-weight:400}
.hero__tags a{color:inherit;text-decoration:none;
  padding-bottom:2px;border-bottom:1.5px solid transparent;
  transition:border-color .28s var(--ease-out),color .28s var(--ease-out)}
.hero__tags a:hover,
.hero__tags a:focus-visible{color:var(--c-cream);border-bottom-color:currentColor}


/* ══════════════════════════════════════════════════════════════════════
   LE DOCK · UNE SEULE CAPSULE
   L'intention était juste — orienter à gauche, convertir à droite — mais
   le rendu donnait DEUX autocollants posés sur la page, qui se mélangeaient
   au contenu. Quatre causes, toutes dans la peinture, aucune dans la
   structure :

     1. Deux objets. Une pilule bleu pâle, une pilule rouge, un `gap` entre
        elles, et CHACUNE son ombre portée. Rien ne disait qu'elles
        appartenaient au même meuble.
     2. La couleur du bouton était une couleur du CONTENU. #CFE0F5 est à
        deux doigts de --c-cci-lite, la carte « Zona stands » de `zonas` :
        le bouton s'y dissolvait.
     3. La translucidité était décorative. `color-mix(… 90%, transparent)`
        derrière un `blur(14px)` donne un aplat opaque : le flou ne se
        voyait pas, donc rien ne signalait « je flotte AU-DESSUS ».
     4. Aucun sol. Ni filet, ni ombre commune. Un objet sans bord se
        confond avec ce qu'il recouvre.

   Le principe qui répond aux quatre : le dock est du MOBILIER
   D'INTERFACE. Il doit se lire comme POSÉ SUR la page, jamais comme un
   morceau de la page. Un seul contenant, un filet d'encre, une ombre
   unique, et un vrai flou — le contenu se brouille visiblement en passant
   dessous, et c'est ce flou qui dit la profondeur. Le rouge redevient ce
   qu'il est : le seul aplat saturé, posé DANS le meuble.

   Pourquoi crème et non encre. Sur `zonas`, six couleurs défilent
   derrière, dont --c-ink et --c-azul : un rail encre y disparaîtrait
   exactement comme le bleu pâle disparaît aujourd'hui — même bug, autre
   teinte. Sur cette section AUCUN remplissage n'est sûr ; ce sont le
   filet, l'ombre et le flou qui séparent, pas la couleur. Le crème est
   simplement celui qui contraste avec le plus de cartes, et il garde le
   rouge comme unique saturation à l'écran.
   ══════════════════════════════════════════════════════════════════════ */

/* ── 1 · LE CONTENANT ─────────────────────────────────────────────────
   Le dock s'étirait d'un bord à l'autre en `pointer-events:none`, avec du
   vide à droite : légitime tant qu'il n'était qu'un cadre de placement.
   Devenu visible, il doit se réduire à son contenu — sinon on peint une
   barre crème sur toute la largeur de l'écran. */
.dock{
  right:auto;
  width:max-content;max-width:calc(100vw - var(--practica-x) * 2);
  align-items:center;gap:var(--s-2);padding:6px;
  border-radius:var(--r-pill);
  /* 92 % et non 80 : au-dessous, le fond traversant TEINTE le rail —
     au-dessus du hero vert il virait au sauge, et le crème n'était plus
     du crème. Le flou reste utile (il adoucit les 8 % qui passent), mais
     ce sont le filet et l'ombre qui portent la profondeur, pas la
     transparence. La lisibilité passe donc devant l'effet. */
  background:color-mix(in srgb,var(--c-cream) 92%,transparent);
  -webkit-backdrop-filter:blur(22px) saturate(1.45);
  backdrop-filter:blur(22px) saturate(1.45);
  /* Le filet d'abord — net, il dessine le bord. L'ombre ensuite — large et
     basse, elle décolle l'objet. Une seule ombre pour tout le meuble. */
  box-shadow:0 0 0 1px color-mix(in srgb,var(--c-ink) 16%,transparent),
             0 22px 46px -26px color-mix(in srgb,var(--c-ink) 75%,transparent);
  pointer-events:auto}

/* ── 2 · LES DEUX MOITIÉS DEVIENNENT DES SEGMENTS ─────────────────────
   Elles perdent tout ce qui les faisait lire comme des objets autonomes :
   fond propre, ombre propre, flou propre. La largeur ne vient plus de
   --practica-w : le rail n'a pas à faire 420 px pour que le panneau
   s'ouvre dans son axe — c'est le BORD GAUCHE qui est commun, et il l'est
   toujours (--practica-x pour les deux). */
.dock__practica{
  flex:0 0 auto;width:auto;min-width:0;
  background:transparent;box-shadow:none;
  -webkit-backdrop-filter:none;backdrop-filter:none;
  color:var(--c-ink);
  padding-inline:var(--s-5);border-radius:var(--r-pill);
  transition:background-color .25s var(--ease-out)}
.dock__practica:hover,
.dock__practica:focus-visible{
  background:color-mix(in srgb,var(--c-ink) 8%,transparent)}
.dock__practica i{
  background:transparent;color:var(--c-ink);
  border:1.5px solid color-mix(in srgb,var(--c-ink) 45%,transparent)}

/* ── 3 · LE CTA, PILULE INSÉRÉE DANS LE RAIL ──────────────────────────
   Il garde sa couleur et sa forme — c'est le seul rouge plein de l'écran,
   et le vocabulaire des pilules est celui de toute la page. Il perd son
   ombre : elle appartient désormais au meuble, pas à la pièce. */
.dock__cta{
  box-shadow:none;
  -webkit-backdrop-filter:none;backdrop-filter:none;
  background:var(--c-rojo);
  min-width:min(260px,38vw);padding-inline:var(--s-6)}

/* ── 4 · LE RAIL NE SE DÉFORME PLUS : IL EST LÀ, OU IL N'Y EST PAS ────
   Trois mécaniques différentes cohabitaient pour un seul objet : le CTA
   s'escamotait sur le hero (`is-over-hero`), se taisait sur #talleres et
   le pied de page (`is-cta-quiet`), et le dock entier se retirait sur ces
   mêmes zones (`is-redundant`). Deux d'entre elles visaient déjà les
   mêmes sections.

   Une seule suffit, et c'est la plus simple : le rail SORT DU CADRE là où
   il ferait doublon — hero, #talleres, pied de page — et reste identique
   partout ailleurs. Un meuble qui change de forme selon l'endroit n'est
   pas du mobilier, c'est une animation ; un meuble qui entre et sort est
   lisible du premier coup.

   Trois bénéfices en même temps :
     · plus de trou crème au bout du rail quand le CTA s'en va ;
     · plus de largeur variable, donc le panneau tombe toujours juste ;
     · plus rien à animer d'autre qu'un `transform` et une `opacity`,
       conformément à la règle de perf du projet — l'ancien repli du CTA
       animait `max-width`, `padding` et `margin`, c'est-à-dire de la mise
       en page à chaque image.

   Ce que ça coûte, et c'est assumé : « Info práctica » n'est plus présent
   sur le premier écran. L'en-tête y porte déjà « Plano » et le menu, donc
   l'orientation n'y est pas perdue — elle passe juste par le haut. */
.dock.is-over-hero,
.dock.is-cta-quiet,
.dock.is-redundant{
  transform:translateY(calc(100% + var(--s-6)));
  opacity:0;pointer-events:none}
.dock{transition:transform .45s var(--ease-io),opacity .3s var(--ease-out)}
.dock__cta{transition:background-color .3s var(--ease-out)}

/* ── 5 · PANNEAU « INFO PRÁCTICA » OUVERT ─────────────────────────────
   L'état ouvert repeignait la barre en bleu, la passait à 64 px et lui
   donnait un angle carré : c'était le dessin du SOCLE, quand la barre
   servait de plinthe au panneau. Le rail flotte maintenant PAR-DESSUS le
   panneau, qui descend déjà jusqu'en bas — il n'y a plus de raccord à
   faire, donc plus de raison de changer d'apparence à l'ouverture. Un
   meuble stable ne se déguise pas quand on ouvre un tiroir. */
.practica-open .dock__practica{
  background:transparent;color:var(--c-ink);
  border-radius:var(--r-pill);box-shadow:none;
  min-height:52px;padding-inline:var(--s-5)}
.practica-open .dock__practica:hover{
  background:color-mix(in srgb,var(--c-ink) 8%,transparent)}
.practica-open .dock__practica i{
  background:transparent;color:var(--c-ink);
  width:32px;height:32px;font-size:.75rem;
  border:1.5px solid color-mix(in srgb,var(--c-ink) 45%,transparent)}
.practica-open .dock{right:auto}

/* ── 6 · MOBILE · LA BARRE RESTE PLEINE LARGEUR ───────────────────────
   Elle était déjà UN seul objet : deux segments jointifs, aucun gap. Elle
   garde donc son dessin et ne récupère que ce qui lui manquait — un filet
   haut et une ombre montante, pour qu'elle ait un bord elle aussi. */
@media(max-width:760px){
  .dock{
    left:0;right:0;bottom:0;
    width:auto;max-width:none;padding:0;gap:0;border-radius:0;
    background:color-mix(in srgb,var(--c-cream) 94%,transparent);
    box-shadow:0 -1px 0 0 color-mix(in srgb,var(--c-ink) 16%,transparent),
               0 -18px 34px -26px color-mix(in srgb,var(--c-ink) 70%,transparent)}

  /* Le partage 44/56 était une proportion, pas une mesure : à 390 px il
     laisse 140 px pour un libellé, un chevron et deux gouttières, et
     « INFO PRÁCTICA » se casse en deux lignes. Le pratique prend donc ce
     qu'il lui faut, le CTA prend le reste — il n'a pas de largeur à
     défendre, seulement une zone tactile à offrir. */
  .dock__practica{flex:0 0 auto;white-space:nowrap;
    border-radius:0;padding-inline:var(--s-4)}
  .dock__cta{flex:1 1 auto;min-width:0;max-width:none;
    border-radius:0;padding-inline:var(--s-4)}

  /* La barre est collée au bas de l'écran : elle sort donc de sa propre
     hauteur, sans la marge que le rail flottant doit franchir. */
  .dock.is-over-hero,
  .dock.is-cta-quiet,
  .dock.is-redundant{transform:translateY(100%)}

  .practica-open .dock__practica{flex:1 1 auto}
  .practica-open .dock{left:0;right:0}
}

/* ── 7 · L'ANCRE DE DÉFILEMENT SUIT LA NOUVELLE HAUTEUR ───────────────
   52 px de segment + 2 × 6 px de rail. Une ancre calée sur 52 laisserait
   la dernière ligne sous le meuble. */
body{scroll-padding-bottom:calc(64px + var(--s-6))}

@media(prefers-reduced-motion:reduce){
  .dock__cta,.dock__practica{transition:none}
}

/* ── 8 · UNE SEULE LARGEUR POUR LE RAIL ET SON PANNEAU ────────────────
   Trois largeurs se succédaient au MÊME bord gauche :

     · le bouton seul, sur le hero          ~205 px
     · le rail complet, une fois le CTA là  ~495 px
     · le panneau « Info práctica »          420 px

   Le panneau ne tombait donc ni sur l'un ni sur l'autre, et son bord
   droit coupait le CTA rouge en deux. Trois objets censés être liés,
   trois mesures : c'est ça qu'on lit comme du désordre, pas la présence
   du dock.

   Le rail prend la largeur du panneau — TOUJOURS, quel que soit l'état
   du CTA. C'est ce qui refait de lui un SOCLE : le panneau se lève
   exactement dans son axe, sur ses deux bords. Effet second, aussi
   important : le rail ne s'élargit plus au scroll. Le CTA entre DANS un
   meuble déjà dimensionné au lieu d'en repousser le bord.

   Le pratique absorbe la place libre (`flex:1`) plutôt que de la laisser
   en creux à droite : libellé à gauche, chevron au bout — le dessin
   d'origine de la barre. Le CTA se glisse entre les deux. */
.dock{width:var(--practica-w)}
.dock__practica{flex:1 1 auto;min-width:0}
.dock__cta{flex:0 1 auto;min-width:0;padding-inline:var(--s-5)}

/* ── 9 · LA LARGEUR TIENT DANS LES DEUX LANGUES ───────────────────────
   Mesuré dans le navigateur, pas estimé : en espagnol le rail tient
   largement dans 420 px, en français il lui faut ~470 px — « Infos
   pratiques » et « Réserver un atelier » sont plus longs, et les deux
   libellés se cassaient en deux lignes, ce qui faisait grandir le rail
   en hauteur et écrasait le chevron en ellipse.

   Le libellé ne se coupe donc plus, le chevron ne se comprime plus, et
   la mesure commune passe à 500 px — le français demande 478 px au plus
   serré, la marge restante absorbe les écarts de rendu de police. C'est
   --practica-w qu'on déplace, pas la largeur du rail : le panneau, le
   rail et le décalage du menu bleu restent accrochés au même jeton,
   donc ils bougent ensemble.

   Le CTA ne rétrécit plus (`flex:0 0 auto`) : à `0 1 auto` il cédait
   16 px au partage et rognait « Réserver un atelier ». C'est le pratique
   qui absorbe le jeu, parce que lui a de la place à donner — l'espace
   entre son libellé et son chevron. */
:root{--practica-w:min(500px, calc(100vw - var(--gutter) * 2))}
.dock__practica,
.dock__cta b{white-space:nowrap}
.dock__practica i{flex:0 0 auto}
.dock__practica{padding-inline:var(--s-4)}
.dock__cta{flex:0 0 auto;padding-inline:var(--s-4)}

/* ── LE RAIL SE RAPPROCHE DU BORD ─────────────────────────────────────
   --practica-x valait --gutter, soit 56 px sur grand écran : c'est la
   marge du CONTENU, et le rail n'est pas du contenu. Le mobilier
   d'interface se tient plus près du bord que le texte — sinon il flotte
   au milieu de nulle part, ni ancré sur le bord ni aligné sur la colonne.

   Réduit de moitié. On déplace le JETON, pas le rail : le panneau « Info
   práctica » et le décalage du menu bleu y sont accrochés, donc les trois
   bougent ensemble et l'alignement obtenu tient toujours. */
:root{--practica-x:clamp(.75rem, 2vw, 1.75rem)}

/* Les jetons mobiles doivent être redéclarés EN DERNIER : une media query
   n'ajoute aucune spécificité, donc les `:root` ci-dessus écrasaient les
   valeurs pleine largeur posées plus haut dans le fichier — la barre du
   bas et le panneau tombaient à 350 px sur un écran de 390 px, et se
   décollaient du bord. Ce bloc doit rester le dernier de la famille. */
@media(max-width:760px){
  :root{--practica-x:0px;--practica-w:100vw}
}

/* ── LA NOTE SOUS LES CHIFFRES ────────────────────────────────────────
   Les quatre compteurs annoncent des faits ; celui des talleres est
   encore préliminaire, et deux autres chiffres attendent la CCI. Le dire
   SOUS la grille plutôt que dans un commentaire : le client relit la
   page, pas le code. [data-todo] fournit déjà l'italique et le sablier. */
.proof__pending{
  margin:var(--s-4) 0 0;
  font-size:var(--t-small);color:var(--c-muted);
  max-width:62ch}

/* ══════════════════════════════════════════════════════════════════════
   LOGOS SPONSORS · DE LA TYPOGRAPHIE AUX VRAIS FICHIERS
   Les noms étaient composés en texte, faute de fichiers. Ils sont arrivés
   — mais PAS détourés, et c'est ça qui commande tout ce bloc. Mesuré
   fichier par fichier, canal alpha à l'appui :

     alstom · ambassade · publicis   fond BLANC opaque
     renault                         blanc sur fond NOIR opaque
     aztlán                          blanc sur fond VIOLET opaque
     prismatic                       transparent (deux versions livrées,
                                     lettrage blanc et lettrage sombre)

   Or les trois emplacements sponsors sont sur fond SOMBRE : le hero, la
   section Aliados (--c-ink-2) et le bandeau de pied de page. Posés tels
   quels, on obtiendrait trois rectangles blancs, un rectangle noir et un
   carré violet alignés — l'aspect « mur de sponsors bricolé ».

   La sortie n'est pas de bidouiller chaque cas : c'est LA PLAQUE. Un
   support blanc unique, même hauteur, même arrondi, même respiration.
   C'est le traitement standard des murs de partenaires, et il a une
   qualité que rien d'autre n'a ici : les trois fichiers à fond blanc s'y
   fondent EXACTEMENT, sans qu'on touche à un pixel d'un fichier client.

   Deux exceptions, toutes deux réversibles :
     · Renault reçoit `invert(1)`. Le fichier est blanc sur noir ; inversé
       il devient noir sur blanc et se fond dans la plaque. C'est la
       version négative du logo, prévue par toute charte monochrome — pas
       une retouche de la marque.
     · Aztlán garde son carré violet à fond perdu. Son logo EST une
       pastille violette : lui coller une marge blanche autour en ferait
       un autocollant. Il devient la seule tuile colorée de la rangée,
       et c'est juste — c'est le LIEU, pas un sponsor.

   ⚠ À DEMANDER AU CLIENT : les fichiers détourés (PNG alpha ou SVG). Avec
     eux, la plaque devient optionnelle et les logos peuvent se poser
     directement sur le fond sombre, en blanc. Tant qu'ils n'arrivent pas,
     la plaque est la seule solution qui ne dégrade aucune marque.
   ══════════════════════════════════════════════════════════════════════ */

/* ── LA PLAQUE ────────────────────────────────────────────────────────
   `--plate-h` = hauteur de RÉFÉRENCE du contenu, redéfinie par
   emplacement. `--k` = coefficient optique par marque (voir plus bas). */
/* ⚠ TOUT EMPLACEMENT DE LOGO DOIT FIGURER DANS CES QUATRE GROUPES.
   `.thanks__brandsrow` (le bandeau défilant du pied de page) n'y était
   pas. Une hauteur lui avait pourtant été écrite plus bas — un
   `--plate-h` que RIEN ne lisait ici, donc sans effet : les images
   tombaient sur leur taille intrinsèque, 1085 px de large pour Prismatic,
   et débordaient sur le texte. Même piège que la guirlande d'Agenda et
   que le CSS mort de `semana` : la moitié d'un mécanisme, qu'on croit
   entier parce que le réglage existe. Le réglage n'est pas le câblage. */
.presented__logos a[data-sponsor],
.scard .logo[data-sponsor],
.sponsors__brandsrow .logo[data-sponsor],
.thanks__brandsrow .logo[data-sponsor],
.thanks__logos .brandmark[data-sponsor]{
  --plate-h:44px;
  display:inline-flex;align-items:center;justify-content:center;
  height:auto;width:auto;padding:10px 16px;
  border-radius:12px;background:#fff;overflow:hidden;
  box-shadow:0 12px 28px -20px rgba(0,0,0,.85);
  transition:transform .5s var(--ease-out),box-shadow .4s var(--ease-out)}

.presented__logos a[data-sponsor] img,
.scard .logo[data-sponsor] img,
.sponsors__brandsrow .logo[data-sponsor] img,
.thanks__brandsrow .logo[data-sponsor] img,
.thanks__logos .brandmark[data-sponsor] img{
  display:block;width:auto;object-fit:contain;

  /* ⚠ UN PLANCHER, PARCE QUE `--k` EST CALIBRÉ POUR UNE GRANDE PLAQUE.
     Les coefficients ont été dérivés sur la plaque de 70 px du hero de
     bureau. Appliqués à une plaque de 42, ils donnent 12,6 px de haut pour
     Prismatic — un mot-symbole illisible. L'équilibre optique a raison
     entre logos ; il n'a rien à dire sur le seuil en dessous duquel un
     nom cesse de se lire. Ce `max()` ne change RIEN au-dessus de 47 px de
     plaque : à 1440 px Prismatic fait 21 px et le plancher ne l'atteint
     jamais. Il ne mord que là où le système décrochait. */
  /* `--k` = la table écrite à la main (ÉQUILIBRAGE OPTIQUE, plus bas) ;
     `--k-auto` = le calcul fait depuis les dimensions du fichier
     (`ccimx_brand_k_auto()`, inc/brands.php), pour toute marque que la table
     ne connaît pas. Ajouté le 21/09/2026 : sans lui, une marque neuve
     retombait sur 1 et cassait la rangée. */
  height:max(15px, calc(var(--plate-h) * var(--k, var(--k-auto, 1))));
  /* Les héritages typographiques du gabarit texte n'ont plus d'objet et
     déformaient la boîte : opacité, filtres de teinte, hauteurs en %. */
  filter:none;opacity:1}

/* ⚠ LE FILET, POUR L'IMAGE QUI N'A PAS DE TAILLE.
   Le correctif de fond est côté PHP (`ccimx_svg_metadata`, inc/brands.php) :
   il donne à WordPress les dimensions d'un SVG, que celui-ci ne sait pas
   mesurer. Cette règle-ci est ce qui reste si un fichier passe quand même
   à travers — et il en passera, la CCI téléverse sans nous prévenir.

   Ce qu'il se passe sans elle, mesuré le 04/09 sur le logo Webedia : la
   feuille du navigateur pose `contain:size` sur tout `img`, ce qui SUPPRIME
   le rapport interne du fichier. Le seul rapport qui survit est celui que le
   navigateur fabrique à partir des ATTRIBUTS `width`/`height`. Sans eux, plus
   aucun rapport : `height` reste ferme, mais rien ne le transmet à la
   largeur, et `width:auto` sur une image sans taille REMPLIT la place — 3 000
   px de largeur demandée, ramenés par `flex-shrink` à la rangée entière,
   1 116 px, les six autres partenaires rejetés en dessous.

   ⚠ CE N'ÉTAIT PAS UN DÉFAUT DE `--k`. À rapport connu la plaque faisait
     417 px. La panne est EN AMONT de la table d'équilibrage — d'où cette
     règle ici, dans le mécanisme de la plaque, et non parmi les coefficients.

   ⚠ NI `aspect-ratio`, NI `flex:0 0 auto`. Le premier, non scopé, écrase le
     rapport que le navigateur tire des attributs et déforme les SIX autres
     (mesuré : Aztlán 70 → 280 px). Le second supprime le `flex-shrink` qui
     est aujourd'hui la seule chose contenant le dégât : Webedia passait à
     3 000 px et sortait de la page. Le candidat qui aggrave ce qu'il corrige.

   `:not([width])` rend la règle INERTE dès que l'image porte ses dimensions,
   donc inerte sur les sept logos actuels. Le facteur 6 est le plus petit qui
   laisse un mot-symbole 6:1 atteindre la pleine hauteur de sa plaque, et
   `object-fit:contain` garde le dessin juste en dessous. */
[data-sponsor] img:not([width]){max-width:calc(var(--plate-h) * 6)}

/* ── ÉQUILIBRAGE OPTIQUE ──────────────────────────────────────────────
   À hauteur égale, un mot-symbole allongé écrase un logo carré. Les
   coefficients sont dérivés des ratios réels des fichiers :
     prismatic 8,6:1 · alstom 3,7:1 · renault 1,2:1 · publicis 1,1:1
     ambassade 1:1   · aztlán 1:1                                      */
[data-sponsor="ambassade"]{--k:1}
[data-sponsor="aztlan"]   {--k:1}
[data-sponsor="renault"]  {--k:.92}
[data-sponsor="publicis"] {--k:.90}
[data-sponsor="alstom"]   {--k:.52}
[data-sponsor="prismatic"]{--k:.30}
/* ⚠ WEBEDIA MANQUAIT À CETTE TABLE, ET UN MANQUE ICI NE SE VOIT PAS :
     `var(--k, 1)` retombe silencieusement sur 1, la valeur d'un logo CARRÉ.
     Un mot-symbole de rapport 5,96:1 traité comme un carré occupe six fois
     la largeur qu'il devrait — c'est ce qui, avec l'absence de dimensions
     dans le fichier (voir inc/brands.php), lui faisait prendre la rangée
     entière le 04/09.

     La loi que suit cette table est celle de l'AIRE CONSTANTE, k = 1/√rapport :
       ambassade 1:1   → 1     (posé 1)
       renault 1,14:1  → .936  (posé .92)
       publicis 1,13:1 → .941  (posé .90)
       alstom 3,67:1   → .522  (posé .52)
       prismatic 8,54:1→ .342  (posé .30 — resserré à la main, un mot-symbole
                                très allongé domine encore à aire égale)
     Webedia 5,96:1 → .409, resserré de la même façon → .36. */
[data-sponsor="webedia"]  {--k:.36}
/* Mezcal Godin, rapport 2,17:1 → 1/√r = .68. La loi tombe juste, sans
   resserrement : ce n'est pas un mot-symbole très allongé. */
[data-sponsor="mezcal-godin"]{--k:.68}
/* VOSH est carré (1:1) — même traitement qu'Ambassade et Publicis. */
[data-sponsor="vosh"]     {--k:1}
/* Saint James, 1,76:1 → 1/√r = .754. La loi tombe juste, pas de retouche. */
[data-sponsor="saint-james"]{--k:.75}
/* Sephora, 5,59:1 → 1/√r = .423, resserré comme Webedia → .38. Ajouté le
   21/09/2026 : sans cette ligne il tombait sur 1, sa plaque prenait trois
   fois la largeur des autres et renvoyait Alstom à la ligne dans le hero. */
[data-sponsor="sephora"]  {--k:.38}

/* Renault : version négative (voir en tête de bloc). */
/* .presented__logos a[data-sponsor="renault"] img,
.scard .logo[data-sponsor="renault"] img,
.sponsors__brandsrow .logo[data-sponsor="renault"] img,
.thanks__brandsrow .logo[data-sponsor="renault"] img,
.thanks__logos .brandmark[data-sponsor="renault"] img{filter:invert(1)} */

/* Aztlán : sa pastille violette remplit la plaque, sans marge blanche. */
.presented__logos a[data-sponsor="aztlan"],
.scard .logo[data-sponsor="aztlan"],
.sponsors__brandsrow .logo[data-sponsor="aztlan"],
.thanks__brandsrow .logo[data-sponsor="aztlan"],
.thanks__logos .brandmark[data-sponsor="aztlan"]{padding:0}
.thanks__logos .brandmark[data-sponsor="aztlan"] img,
.scard .logo[data-sponsor="aztlan"] img{height:calc(var(--plate-h) + 20px)}

/* ── HAUTEURS PAR EMPLACEMENT ─────────────────────────────────────────
   Le pied de page est le mur officiel : c'est là qu'ils sont les plus
   grands. Le hero est une caution discrète, il reste petit. */
.thanks__logos .brandmark[data-sponsor]{--plate-h:clamp(38px,4.4vw,54px)}
.scard .logo[data-sponsor]{--plate-h:clamp(44px,5vw,62px)}
.sponsors__brandsrow .logo[data-sponsor]{--plate-h:clamp(32px,3.6vw,44px)}
.presented__logos a[data-sponsor]{--plate-h:clamp(30px,3.2vw,40px);padding:8px 12px;border-radius:10px}

/* Survol : la plaque se soulève, comme les billets et les cartes. */
.thanks__logos .brandmark:hover,
.presented__logos a[data-sponsor]:hover{
  transform:translateY(-4px);box-shadow:0 18px 34px -20px rgba(0,0,0,.9)}

/* ── CE QUE LA PLAQUE REND CADUC ──────────────────────────────────────
   Règles écrites pour du TEXTE ou pour des fichiers détourés qu'on
   n'a pas. Laissées actives, elles repeignaient les logos en blanc plein
   (`brightness(0) invert(1)`) ou leur imposaient des tailles de police. */
.hero .presented__logos img{filter:none;height:auto;opacity:1}
.scard .logo[data-sponsor]{font-size:0;letter-spacing:0;line-height:0}
.sponsors__brandsrow{align-items:center;flex-wrap:wrap;gap:var(--s-5)}
.thanks__logos{gap:clamp(var(--s-4),3vw,var(--s-6))}

/* Les filets verticaux séparaient des mots. Entre deux plaques, ils
   ajoutent une troisième forme sans rien séparer de plus. */
.thanks__logos .thanks__sep{display:none}

@media(prefers-reduced-motion:reduce){
  .thanks__logos .brandmark:hover,
  .presented__logos a[data-sponsor]:hover{transform:none}
}

/* Les filets verticaux de la rangée « marcas » séparaient des mots ; entre
   deux plaques ils ajoutent une troisième forme sans rien séparer. */
.sponsors__brandsrow .sponsors__sep{display:none}

/* En mouvement réduit, l'en-tête ne glisse pas : il est là ou il n'y est
   pas. La règle de perf du projet est respectée dans les deux cas — seul
   `transform` est animé. */
@media(prefers-reduced-motion:reduce){
  .header{transition:none}
}

/* ══════════════════════════════════════════════════════════════════════
   HERO · DEUX EFFETS QUI NE S'AFFICHAIENT PAS
   ══════════════════════════════════════════════════════════════════════ */

/* ── 1 · LA DÉCHIRURE AU SURVOL DU PASE ───────────────────────────────
   Elle était bien écrite (`.pass:hover .pass__stub{translate;rotate}`) et
   elle ne se voyait plus. Mesuré dans le navigateur : GSAP écrit EN LIGNE
   sur la souche, dès le chargement,

       translate:none; rotate:none; scale:none; transform:translate(0,0)

   parce qu'il neutralise les propriétés individuelles pour tout piloter
   par `transform`. Un style en ligne bat n'importe quelle feuille : la
   règle de survol était morte, sans erreur ni avertissement.

   Les billets de #talleres, eux, marchent toujours — GSAP n'y touche pas.
   C'était donc bien le pase, et lui seul.

   `!important` est ici le bon outil, pas un pansement : `translate` et
   `rotate` s'appliquent AVANT `transform` dans la composition CSS. La
   déchirure du survol vient donc s'ajouter à celle du scroll au lieu de
   l'écraser — les deux gestes se cumulent, ce qui est exactement ce que
   le fichier décrit (« le survol creuse la déchirure que le scroll a déjà
   ouverte »). */
.pass__stub{transition:translate .45s var(--ease-out),rotate .45s var(--ease-out)}
.pass:hover .pass__stub{translate:0 16px !important;rotate:-1.2deg !important}
@media(prefers-reduced-motion:reduce){
  .pass__stub{transition:none}
  .pass:hover .pass__stub{translate:none !important;rotate:none !important}
}

/* ── 2 · LA SILHOUETTE AZTLÁN–PARIS ───────────────────────────────────
   Elle est dans le hero depuis le début, en deux exemplaires (net et
   flou) — mais on ne la voyait pas. Le fichier livré est un JPEG à fond
   BLANC OPAQUE, posé en `mix-blend-mode:screen`. Or `screen` avec du
   blanc redonne du blanc : au lieu de détourer la silhouette, il posait
   un voile blanc rectangulaire sur le hero, dans lequel les gris de la
   grande roue disparaissaient.

   `invert(1)` d'abord : le fond blanc devient NOIR, et le noir en `screen`
   est parfaitement transparent. Les silhouettes, elles, passent en clair
   et ressortent. C'est un détourage gratuit, sans toucher au fichier —
   le même principe que le logo Renault du pied de page.

   Reste la bande verte du bas : 28 px sur 254, mesurés, un reste de la
   capture du deck. Inversée elle virerait au rose et ferait une barre en
   travers du hero. On la coupe — 11 % exactement.

   Les opacités montent (.16/.26 → .30/.42) : elles avaient été baissées
   pour rendre le voile blanc supportable. Le problème n'existant plus,
   la silhouette peut enfin se voir. */
.hero__obj{
  clip-path:inset(0 0 11% 0);
  filter:invert(1);
  opacity:.30}
.hero__obj--near{opacity:.42}
.hero__obj--far{filter:invert(1) blur(5px);opacity:.24}

/* ── 3 · LE CORPS DU BILLET, MAINTENANT CLIQUABLE ─────────────────────
   `.pass__top` est passé de <div> à <a>. Il hérite donc du soulignement
   et de la couleur de lien du navigateur : on remet le dessin d'origine,
   et on ajoute la seule chose qui manquait — un signal discret que c'est
   une porte. La ville souligne au survol : c'est la ligne qui appelle le
   plus « où ça, exactement ? », donc c'est elle qui répond. */
.pass__top{color:inherit;text-decoration:none;cursor:pointer}
.pass__where{
  text-decoration:underline solid transparent 1.5px;
  text-underline-offset:4px;
  transition:text-decoration-color .3s var(--ease-out)}
.pass:hover .pass__where{text-decoration-color:currentColor}
.pass__top:focus-visible{outline:3px solid var(--c-cream);outline-offset:-6px}

/* ── 4 · LE HERO SANS PHOTO ───────────────────────────────────────────
   La trame demi-ton existait pour masquer la pixellisation de la photo.
   Sans photo, elle tramait un aplat uni : des points visibles sur un fond
   plat, soit exactement le défaut d'impression qu'elle servait à imiter
   quand il y avait une matière à imiter. On la garde — le hero garderait
   sinon un aplat trop lisse pour le reste de la page — mais deux fois
   plus discrète, et sur `soft-light` plutôt qu'`overlay` : elle donne du
   papier sans donner du bruit.

   ⚠ Ces deux valeurs sont à REMETTRE (.55 / overlay) le jour où une photo
     revient dans le hero : c'est sur une photo qu'elles étaient justes. */
.hero__halftone{opacity:.26;mix-blend-mode:soft-light}

/* ══════════════════════════════════════════════════════════════════════
   LE HERO REVIENT AU CLAIR — LA RÉFÉRENCE LE DISAIT DÉJÀ
   Festivent, la référence citée partout dans ce fichier, ouvre sur un
   fond CLAIR en dégradé, sans aucune photographie : des objets détourés
   qui flottent en deux exemplaires (net et flou), des halos de couleur
   très diffus, et le contenu centré verticalement.

   Ce fichier l'écrivait noir sur blanc dès l'origine — « Halos flous sur
   crème : l'atmosphère de festivent, PAS un fond noir » — puis le hero a
   dérivé vers l'encre, couche après couche, pour compenser une photo trop
   petite qu'il fallait bien assombrir pour qu'elle passe.

   La photo partie, plus rien ne justifie le fond sombre. On revient donc
   à l'intention de départ. Trois gains qui ne sont pas que du goût :

     · la silhouette Aztlán–Paris est un JPEG GRIS SUR BLANC. Sur fond
       clair, `multiply` la détoure NATIVEMENT — le blanc est le neutre du
       multiply, il disparaît tout seul. Plus d'invert, plus de bidouille :
       le fichier est enfin utilisé pour ce qu'il est ;
     · le billet rouge devient l'objet le plus fort du premier écran, ce
       qui est exactement le rang qu'on veut lui donner ;
     · le rythme des sections reste tenu : le premier vrai contraste passe
       sur la bande rouge du manifesto, au lieu d'être brûlé dès le hero.

   Le contenu ne bouge pas — même titre, même CTA, même billet, mêmes
   quatre mots. Seul le SOL change.
   ══════════════════════════════════════════════════════════════════════ */

/* ── 1 · LE SOL ───────────────────────────────────────────────────────
   Crème, et centré verticalement. Le `padding-bottom` réservait la
   hauteur de la barre « Info práctica » — or le dock se retire désormais
   entièrement au-dessus du hero : cette réserve ne protégeait plus rien
   et creusait une bande vide de 64 px au bas du premier écran. */
.hero{
  background:var(--c-cream);color:var(--c-ink);
  align-items:center;
  padding-block:clamp(96px,12vh,132px) clamp(var(--s-7),8vh,var(--s-9))}

/* Le voile portait le contraste de la photo. Sans photo, il devient ce
   que Festivent met à cet endroit : une respiration de lumière. Chaud en
   haut, un souffle bleu en bas — assez pour que le fond ne soit pas un
   aplat, trop peu pour se remarquer. */
.hero__veil{
  background:
    linear-gradient(to bottom,
      rgba(255,255,255,.62)  0%,
      rgba(255,255,255,.10) 38%,
      rgba(0,42,79,.05)     78%,
      rgba(0,42,79,.09)    100%),
    radial-gradient(120% 85% at 86% 6%, rgba(0,64,121,.10), transparent 64%)}

/* ── 2 · LES HALOS ET LA SILHOUETTE ───────────────────────────────────
   Le fondu passait par le conteneur (`screen`, opacité .34) : un seul
   réglage pour deux objets qui n'ont pas le même métier. Chacun prend
   donc le sien.

   `multiply` pour les deux : sur fond clair c'est le mode qui DÉPOSE de
   la matière au lieu d'en enlever. Pour la silhouette, c'est en prime le
   détourage gratuit — le blanc du JPEG est le neutre du multiply. */
.hero__objects{mix-blend-mode:normal;opacity:1}
.hero__wash{mix-blend-mode:multiply;opacity:.17}
.hero__wash--verde{opacity:.13}
.hero__wash--carmin{opacity:.15}

/* La silhouette était à peine perceptible : elle héritait de l'opacité du
   conteneur (.34) MULTIPLIÉE par la sienne (.26) — soit 9 % en réalité.
   Elle reprend sa pleine valeur, et de la place : chez Festivent les
   objets sont grands, c'est ce qui fait le décor. */
.hero__obj{
  mix-blend-mode:multiply;
  filter:none;clip-path:inset(0 0 11% 0);opacity:.85}
.hero__obj--near{width:min(54vw,760px);right:-4%;bottom:4%;opacity:.85}
.hero__obj--far {width:min(34vw,440px);left:0;top:14%;filter:blur(6px);opacity:.45}
@media(max-width:760px){
  .hero__obj--near{width:96vw;right:-12%;bottom:2%;opacity:.7}
}

/* La trame demi-ton et le grain existaient pour donner du corps à une
   photo. Sur un aplat clair, ils ne doivent plus que suggérer le papier. */
.hero__halftone{mix-blend-mode:multiply;opacity:.07}
.hero__grain{mix-blend-mode:multiply;opacity:.10}

/* ── 3 · LE TEXTE PASSE À L'ENCRE ─────────────────────────────────────
   Aucun mot ne change, seulement leur couleur. Le rouge profond reprend
   les accents que le corail portait sur fond sombre : sur crème, le
   corail tombe sous le seuil de contraste. */
.hero .display,.hero h1{color:var(--c-ink);text-shadow:none}
.hero .label,.hero .display em,.hero .hero__lead em{color:var(--c-rojo-txt)}
.hero .hero__lead,.hero__lead{color:var(--c-muted);opacity:1}
.hero__proof{color:var(--c-muted)}
.hero__tags{color:var(--c-ink)}
.hero__tags a:hover,.hero__tags a:focus-visible{color:var(--c-rojo-txt)}
.hero .presented{border-top-color:var(--c-line)}
.hero .presented__label{color:var(--c-muted)}
.hero .btn--ghost{color:var(--c-ink);box-shadow:inset 0 0 0 1.5px var(--c-line);--btn-fill:var(--c-ink)}
.hero .btn--ghost:hover,.hero .btn--ghost:focus-visible{color:var(--c-cream)}

/* ── 4 · L'EN-TÊTE SUIT LE SOL ────────────────────────────────────────
   `.header:not(.is-solid)` — l'état « au-dessus du hero » — était écrit
   pour un hero SOMBRE : texte crème, barres du burger crème, logotype en
   version claire. Sur fond crème, tout cela devient invisible. C'est le
   ricochet obligé du changement de sol, et il ne concerne que cet état :
   `.is-solid`, lui, était déjà en encre. */
.header:not(.is-solid){color:var(--c-ink)}
.header:not(.is-solid) .brand__sub,
.header:not(.is-solid) .lang{color:var(--c-muted)}
.header:not(.is-solid) .lang button[aria-pressed="true"]{color:var(--c-ink)}
.header:not(.is-solid) .lang button:hover{background:rgba(10,37,64,.08)}
.header:not(.is-solid) .burger{border-color:var(--c-line)}
.header:not(.is-solid) .burger span{background:var(--c-ink)}
.header:not(.is-solid) .burger:hover{background:var(--c-ink);border-color:var(--c-ink)}
.header:not(.is-solid) .burger:hover span,
.header:not(.is-solid) .burger:focus-visible span{background:var(--c-cream)}
.header:not(.is-solid) .nav a{border-color:var(--c-line);color:var(--c-ink)}
.header:not(.is-solid) .nav a:hover{background:var(--c-ink);color:var(--c-cream);border-color:var(--c-ink)}
.header:not(.is-solid) .crosslink{background:rgba(10,37,64,.07);color:var(--c-ink);backdrop-filter:none}
.header:not(.is-solid) .crosslink:hover{background:rgba(10,37,64,.14)}
.header:not(.is-solid) .btn--primary{box-shadow:0 14px 32px -14px rgba(228,0,43,.6)}
/* Le logotype a deux versions ; sur fond clair c'est la sombre, dans les
   DEUX états de l'en-tête. La bascule claire/sombre n'a plus lieu d'être
   tant que le hero est crème — elle repart si une photo sombre revient. */
.header:not(.is-solid) .brand__logo--light{opacity:0}
.header:not(.is-solid) .brand__logo--dark{opacity:1}

/* ── 5 · LES LOGOS DU HERO ────────────────────────────────────────────
   La rangée n'en portait qu'un. Elle porte maintenant la même liste que
   le mur du pied de page — c'est la même liste, elle ne peut pas être
   complète en bas et tronquée en haut. Plaques réduites : dans le premier
   écran elles accompagnent, elles ne concurrencent pas.
   ⚠ Elles exposent donc quatre marques non confirmées dans le PREMIER
     écran, l'endroit le plus visible du site. Voir la note du bandeau. */
.hero .presented__logos{flex-wrap:wrap;gap:var(--s-3)}
.hero .presented__logos a[data-sponsor]{--plate-h:clamp(22px,2.4vw,30px);padding:6px 10px;border-radius:8px;
  box-shadow:0 8px 20px -16px rgba(10,37,64,.7),0 0 0 1px rgba(10,37,64,.07)}

/* ── 6 · CE QUE LE FOND CLAIR A RÉVÉLÉ ────────────────────────────────
   Les quatre mots de l'affiche ressortaient en rgb(255,235,199) — le
   pêche, écrit pour un fond encre. Sur crème, mesuré, ils tombaient à
   1,2:1 : littéralement illisibles. C'est le ricochet classique d'un
   changement de sol, et il ne se voit qu'en regardant. */
.hero__tags,.hero__tags li,.hero__tags a{color:var(--c-ink)}
.hero__tags li:not(:last-child)::after{color:var(--c-muted)}
.hero__tags a:hover,.hero__tags a:focus-visible{color:var(--c-rojo-txt)}

/* La silhouette est un JPEG : son « blanc » n'est pas 255 mais 250-253,
   à cause de la compression. En `multiply`, ces trois points d'écart
   suffisent à dessiner le RECTANGLE du fichier sur le fond clair.
   `brightness` remonte les presque-blancs à 255 — ils redeviennent le
   neutre du multiply et le cadre disparaît — sans toucher aux gris de la
   silhouette, trop bas pour être saturés.

   Le masque finit le travail sur les bords : un objet détouré n'a pas
   d'arête. Chez Festivent les montgolfières flottent, elles ne sont pas
   posées dans un cadre. */
.hero__obj{
  filter:brightness(1.07);
  -webkit-mask-image:radial-gradient(115% 130% at 50% 62%, #000 58%, transparent 92%);
  mask-image:radial-gradient(115% 130% at 50% 62%, #000 58%, transparent 92%)}
.hero__obj--far{filter:brightness(1.07) blur(6px)}

/* Le halo rouge du bas partait de 30 % de la largeur et remontait haut
   dans le premier écran : sur fond sombre il ne se voyait pas, sur crème
   il pose une tache rosée sous le titre. On le repousse vers l'angle et
   on l'adoucit — un halo, pas une forme. */
.hero__bg{
  background:
    radial-gradient(38% 34% at 88% 4%,rgba(var(--glow-azul),.20),transparent 66%),
    radial-gradient(42% 34% at 12% 116%,rgba(var(--glow-red),.20),transparent 72%),
    transparent}

/* ── 7 · LE RECTANGLE FANTÔME DE LA SILHOUETTE ────────────────────────
   Un cadre plus clair se dessinait autour de la silhouette, exactement
   là où le lavis carmin passait derrière elle. Cause : `.hero__objects`
   porte `z-index:-3`, donc il crée un CONTEXTE D'EMPILEMENT — un groupe.
   Ses enfants se mélangent entre eux à l'intérieur du groupe, puis le
   groupe entier est composité sur le hero. En posant `normal` sur le
   groupe, je faisais peindre son résultat tel quel : le blanc du JPEG
   recouvrait le lavis au lieu de disparaître.

   Le `multiply` doit donc être sur le groupe ET sur les enfants :
     · entre enfants, il préserve ce qui passe dessous ;
     · sur le groupe, il rend le blanc neutre vis-à-vis du fond.

   C'est le piège classique du blend mode : il ne se règle jamais sur le
   seul élément qu'on regarde, mais sur la pile entière. */
.hero__objects{mix-blend-mode:multiply;opacity:1}

/* La bande verte du bas laissait un liseré turquoise : 11 % coupait au
   ras du dégradé d'anticrénelage du JPEG. On coupe deux points plus bas,
   et le masque du bas est renforcé pour que la silhouette se termine en
   fondu — un objet qui flotte n'a pas d'arête inférieure. */
.hero__obj{
  clip-path:inset(0 0 13.5% 0);
  -webkit-mask-image:radial-gradient(120% 118% at 50% 50%, #000 46%, transparent 88%);
  mask-image:radial-gradient(120% 118% at 50% 50%, #000 46%, transparent 88%)}

/* ══════════════════════════════════════════════════════════════════════
   HERO · RECOMPOSITION FESTIVENT
   Trois griefs mesurés, trois réponses.

   ① LE SOL N'ÉTAIT PLUS CRÈME. Échantillonné dans le navigateur :
     #CBD3D7 en haut à gauche, #D0D9D3 sous le sous-titre, alors que les
     sections voisines sont à #F7F1E4. Le canal BLEU passait au-dessus du
     rouge — l'inverse exact d'un crème, qui est chaud. Cinq couches en
     `multiply` empilées (trois lavis, la trame, le grain) plus un voile
     bleuté : `multiply` n'ajoute jamais de lumière, chaque couche
     assombrit ET refroidit. Le premier écran ne ressemblait plus au site.

     Seule la silhouette a besoin du multiply — c'est ce qui la détoure.
     La trame et le grain passent donc en `soft-light`, qui module sans
     salir, et les lavis descendent à des valeurs de souffle.

   ② LA SILHOUETTE ÉTAIT UN FILIGRANE. Chez Festivent, les montgolfières
     sont des OBJETS : grandes, franches, coupées par le bord du cadre.
     La nôtre se cachait. Elle grandit, sort du cadre, et surtout elle est
     posée SUR les lavis : étant en multiply, elle prend leur teinte —
     bleue à gauche, chaude à droite. La couleur est gratuite, aucun
     filtre, aucun fichier retouché.

   ③ LA COMPOSITION ÉTAIT EN COLONNES. La référence centre : un axe, le
     titre en pleine largeur, l'objet posé dessous et coupé par le bas de
     l'écran. Le contenu ne change pas — ni un mot, ni un bouton — c'est
     la GRILLE qui change.
   ══════════════════════════════════════════════════════════════════════ */

/* ── ① LE SOL REDEVIENT CRÈME ─────────────────────────────────────────*/
.hero__halftone{mix-blend-mode:soft-light;opacity:.09}
.hero__grain{mix-blend-mode:soft-light;opacity:.12}
.hero__veil{
  background:
    linear-gradient(to bottom,
      rgba(255,255,255,.50)  0%,
      rgba(255,255,255,.08) 44%,
      rgba(255,255,255,0)  100%)}
.hero__bg{
  background:
    radial-gradient(40% 36% at 92% 2%,rgba(var(--glow-azul),.14),transparent 68%),
    radial-gradient(44% 36% at 6% 110%,rgba(var(--glow-red),.14),transparent 74%),
    transparent}
/* Deux souffles seulement, dans les angles. Le lavis vert central était
   le principal responsable du gris-vert : il occupait tout le milieu de
   l'écran, juste derrière le titre. */
.hero__wash--azul  {opacity:.10}
.hero__wash--carmin{opacity:.10}
.hero__wash--verde {opacity:.05;bottom:-40%;left:auto;right:-16%}

/* ── ② LES SILHOUETTES DEVIENNENT DES OBJETS ──────────────────────────
   Nettes à droite, floues à gauche : le doublon net/flou est ce qui crée
   la profondeur de champ chez Festivent. Coupées par les bords, comme
   des objets qui passent — pas des vignettes posées au milieu. */
.hero__obj--near{
  width:min(62vw,880px);right:-8%;bottom:-2%;opacity:.9}
.hero__obj--far{
  width:min(40vw,520px);left:-8%;top:8%;filter:brightness(1.07) blur(7px);opacity:.55}
@media(max-width:900px){
  .hero__obj--near{width:120vw;right:-24%;bottom:-2%;opacity:.55}
  .hero__obj--far{display:none}
}

/* ── ③ LA COMPOSITION CENTRÉE ─────────────────────────────────────────*/
.hero__inner{justify-items:center;text-align:center}
.hero__main{justify-items:center}
.hero__eyebrow,.hero__tags,.hero__actions,.hero .presented{justify-content:center}
.hero__lead,.hero__proof{margin-inline:auto}
.hero h1{margin-inline:auto}

@media(min-width:1100px){
  .hero{align-items:center;padding-block:clamp(96px,11vh,124px) 0}
  /* Une seule colonne, deux rangées : le texte, puis l'objet. Le rail de
     150 px n'a jamais existé dans le gabarit — sa colonne était vide. */
  .hero__inner{
    grid-template-columns:minmax(0,1fr);
    grid-template-rows:auto auto;
    grid-template-areas:'main' 'pase';
    row-gap:clamp(var(--s-6),4.5vh,var(--s-8));
    max-width:1180px;padding-inline:var(--s-6);align-items:center}
  .hero__main{grid-area:main;align-self:auto;gap:clamp(var(--s-4),1.6vh,var(--s-5))}
  .hero h1{max-width:20ch;font-size:clamp(3rem,6.4vw,7.2rem);line-height:.9}
  .hero__lead{max-width:44ch;font-size:clamp(1.05rem,1.5vw,1.35rem)}

  /* Le billet est POSÉ, et il dépasse. `.hero` porte overflow:hidden :
     c'est le bord de l'écran qui le coupe, comme un billet qu'on glisse
     dans une fente. La marge négative retire aussi la part cachée du
     calcul de hauteur — sinon le hero grandirait pour rien. */
  .pass{
    grid-area:pase;justify-self:center;align-self:end;
    margin:0 0 calc(-1 * clamp(80px,15vh,180px));
    rotate:-1.2deg;max-width:420px}
}

/* ── LE BILLET NE SE DISSOUT PLUS ─────────────────────────────────────
   Sa souche est crème sur un fond crème : le bas de l'objet s'effaçait.
   Sur fond sombre le contraste faisait le travail ; sur fond clair il
   faut un bord. Un filet d'encre très pâle et une ombre portée basse —
   le même vocabulaire que le rail « Info práctica ». */
.pass{
  box-shadow:0 0 0 1px rgba(10,37,64,.07),0 40px 80px -50px rgba(10,37,64,.9)}

/* ── LE TITRE TIENT LA PROMESSE DU SOUS-TITRE ─────────────────────────
   « Un fin de semana de fiesta » et un titre d'un seul bleu : le premier
   écran ne tenait pas ce qu'il annonçait. La seconde ligne passe au rouge
   PROFOND — --c-rojo-txt, pas --c-rojo. La règle du fichier vise un seul
   APLAT saturé (le CTA, le billet) ; du texte dans un rouge foncé ne
   concurrence pas un aplat, il réveille. */
.hero h1 span.split:last-of-type{color:var(--c-rojo-txt)}

/* ── LES LOGOS REDEVIENNENT UNE LIGNE ─────────────────────────────────
   Six plaques blanches dans le premier écran, c'était un mur de sponsors
   avant d'avoir donné envie de venir — et ils ont déjà leur mur en pied
   de page. Les plaques sautent ; `multiply` fait le détourage, comme pour
   la silhouette : le blanc des fichiers devient neutre sur le crème.
   Renault garde son inversion (blanc sur noir → noir sur blanc), sinon
   il déposerait un rectangle noir.
   Le filet horizontal disparaît aussi : il soulignait le vide du bas. */
.hero .presented{border-top:0;padding-top:0;gap:var(--s-4)}
.hero .presented__logos{gap:clamp(var(--s-4),2vw,var(--s-6));align-items:center}
.hero .presented__logos a[data-sponsor]{
  background:none;box-shadow:none;padding:0;border-radius:0;
  --plate-h:clamp(20px,2.1vw,27px)}
.hero .presented__logos a[data-sponsor] img{mix-blend-mode:multiply;opacity:.78}
.hero .presented__logos a[data-sponsor]:hover img{opacity:1}
.hero .presented__logos a[data-sponsor]:hover{transform:none;box-shadow:none}

/* ── LE BANDEAU REJOINT LA PALETTE DU HERO ────────────────────────────
   Il était le SEUL vert du premier écran : plus une barre d'outils collée
   en haut qu'une bande de marque. En encre, il appartient à la même
   famille que le titre, le billet et les logos. Le vert garde son rôle,
   il est la couleur de la section Agenda. */
.header__banner{background:var(--c-ink)}

/* ── RÉGLAGES APRÈS PREMIER RENDU ─────────────────────────────────────
   Trois choses que seule la maquette à l'écran pouvait dire.

   ① Le doublon flou dessinait un RECTANGLE pâle en haut à gauche. Le
     masque radial s'arrêtait à 88 % : au bord du cadre il était encore
     partiellement opaque, donc la coupe du `clip-path` se voyait. Il doit
     atteindre zéro AVANT le bord de l'élément, sinon il ne masque rien
     là où c'est justement nécessaire.

   ② Le billet était coupé trop haut : la souche entière passait sous la
     ligne de flottaison. Qu'elle soit tronquée est voulu — « Añadir a
     Google Calendar » ne doit plus rivaliser avec le CTA — mais on doit
     voir la perforation, sinon ce n'est plus un billet qui entre dans une
     fente, c'est un rectangle rouge qui s'arrête. On resserre le rythme
     vertical du bloc de texte pour lui rendre une centaine de pixels.

   ③ Les silhouettes restaient trop discrètes pour tenir le rôle d'objet.
     Elles montent en présence et se décalent vers les bords. */
.hero__obj{
  -webkit-mask-image:radial-gradient(102% 104% at 50% 50%, #000 26%, transparent 70%);
  mask-image:radial-gradient(102% 104% at 50% 50%, #000 26%, transparent 70%)}
.hero__obj--near{opacity:1;bottom:-6%;right:-12%;width:min(66vw,940px)}
.hero__obj--far{opacity:.7;left:-12%;top:2%;width:min(42vw,560px)}

@media(min-width:1100px){
  /* Rythme resserré : le titre reste dominant, tout le reste se rapproche.
     Ce sont ces pixels-là qui repassent au billet. */
  .hero{padding-block:clamp(84px,9.5vh,108px) 0}
  .hero__inner{row-gap:clamp(var(--s-5),3vh,var(--s-6))}
  .hero__main{gap:clamp(var(--s-3),1.2vh,var(--s-4))}
  .hero h1{font-size:clamp(2.8rem,5.8vw,6.4rem)}
  .hero__lead{font-size:clamp(1rem,1.35vw,1.22rem)}
  .hero__actions{margin-top:var(--s-2)}
  .pass{
    max-width:400px;
    margin-bottom:calc(-1 * clamp(40px,9vh,110px))}
}

/* ── DEUX DÉFAUTS QUE LE RENDU A MONTRÉS ──────────────────────────────

   ① Le masque radial ne pouvait pas marcher sur une image LARGE. Avec
     `radial-gradient(102% 104%)`, le bord de l'élément tombe à 49 % du
     rayon — donc encore à moitié opaque : la coupe rectangulaire restait
     visible en haut à gauche. Un dégradé radial ne peut pas éteindre les
     quatre bords d'un rectangle 3,5:1 sans dévorer le sujet au centre.

     Deux dégradés linéaires croisés le font proprement : l'un éteint la
     gauche et la droite, l'autre le haut et le bas, et `mask-composite`
     ne garde que l'intersection. Chaque bord s'éteint là où il faut,
     indépendamment du format de l'image.

   ② L'exergue « FESTIVAL FRANCIA-MÉXICO » venait buter dans les pastilles
     de l'en-tête. En resserrant le rythme j'ai pris les pixels au mauvais
     endroit : ceux du haut protégeaient le contenu de l'en-tête fixe. Le
     retrait se fait donc plus bas, sur la marge du billet. */
.hero__obj{
  -webkit-mask-image:
    linear-gradient(to right,  transparent 0, #000 15%, #000 85%, transparent 100%),
    linear-gradient(to bottom, transparent 0, #000 17%, #000 83%, transparent 100%);
  mask-image:
    linear-gradient(to right,  transparent 0, #000 15%, #000 85%, transparent 100%),
    linear-gradient(to bottom, transparent 0, #000 17%, #000 83%, transparent 100%);
  -webkit-mask-composite:source-in;
  mask-composite:intersect}

@media(min-width:1100px){
  .hero{padding-block:clamp(118px,14vh,148px) 0}
  .pass{margin-bottom:calc(-1 * clamp(90px,15vh,190px))}
}

/* ── LES HALOS CHANGENT DE COUCHE ─────────────────────────────────────
   Test isolant dans le navigateur : en masquant les trois lavis, le sol
   remonte de (226,230,232) à (252,250,245). Ils portaient donc TOUT le
   gris, à eux seuls, même descendus à 5-10 % d'opacité.

   La raison est arithmétique, pas esthétique : les lavis sont dans
   `.hero__objects`, groupe passé en `multiply` pour détourer la
   silhouette. Or `multiply` avec du bleu #004079 ou du vert #005D4F ne
   peut QUE assombrir et refroidir — ce sont des couleurs sombres. Baisser
   l'opacité ne change pas la nature de l'opération, ça la dilue.

   On les déplace donc d'une couche. `.hero__bg` est composité NORMALEMENT
   (aucun blend) : des teintes claires y déposent de la couleur sans
   retirer de lumière. C'est ce que fait la référence — un ciel pastel,
   pas un voile sale.

   `.hero__objects` ne contient plus que les silhouettes : le groupe
   multiply retrouve son unique raison d'être, le détourage. */
.hero__wash{display:none}
.hero__bg{
  filter:blur(4px);
  background:
    radial-gradient(46% 44% at 90% -4%,
      color-mix(in srgb,var(--c-cci-lite) 46%,transparent), transparent 72%),
    radial-gradient(38% 40% at 2% 4%,
      color-mix(in srgb,var(--c-cci-lite) 30%,transparent), transparent 74%),
    radial-gradient(54% 46% at 8% 110%,
      color-mix(in srgb,var(--c-peach) 62%,transparent), transparent 76%),
    radial-gradient(42% 38% at 96% 106%,
      color-mix(in srgb,var(--c-rosa) 34%,transparent), transparent 76%)}

/* La bande verte du deck laissait encore un trait sombre au bas de la
   silhouette : 13,5 % coupait au ras. Deux points de plus, et le fondu
   du bas commence plus tôt pour absorber ce qui reste. */
.hero__obj{clip-path:inset(0 0 16% 0)}

/* ── LA SILHOUETTE DEVIENT UN HORIZON ─────────────────────────────────
   Le doublon net + flou est la technique de Festivent, et elle marche
   chez eux parce que leurs objets sont des MONTGOLFIÈRES : des formes
   isolées, reconnaissables floues comme nettes. Notre fichier n'est pas
   un objet, c'est un PANORAMA — une frise de 3,5:1 qui aligne la grande
   roue, la tour Eiffel et l'Arc de Triomphe.

   Floutée et posée en haut à gauche, cette frise ne se lisait pas comme
   un objet lointain : elle faisait un rectangle pâle, avec une arête. On
   arrête de lui faire jouer un rôle qui n'est pas le sien.

   Une frise veut être un HORIZON. Pleine largeur, au bas du hero, sous le
   billet : la ville s'étend derrière l'objet, et le billet se pose devant
   elle. On y gagne la profondeur que le doublon cherchait, sans artefact
   — et le sujet devient lisible au lieu d'être deviné. */
.hero__obj--far{display:none}
.hero__obj--near{
  left:0;right:auto;width:100%;bottom:-3%;
  opacity:.9;
  -webkit-mask-image:
    linear-gradient(to right,  transparent 0, #000 12%, #000 88%, transparent 100%),
    linear-gradient(to bottom, transparent 0, #000 22%, #000 92%, transparent 100%);
  mask-image:
    linear-gradient(to right,  transparent 0, #000 12%, #000 88%, transparent 100%),
    linear-gradient(to bottom, transparent 0, #000 22%, #000 92%, transparent 100%);
  mask-composite:intersect}
@media(max-width:900px){
  .hero__obj--near{width:150%;left:-25%;opacity:.6}
}

/* ══════════════════════════════════════════════════════════════════════
   LE PASE PASSE À L'HORIZONTALE
   Deux reproches, une seule cause : posé debout sous le CTA, le billet
   mesurait près de 500 px. Il ne pouvait donc qu'être coupé par le bas de
   l'écran, ou pousser tout le reste hors du premier écran. Le rogner
   n'était pas une solution, c'était le symptôme.

   La réponse n'est pas de le rapetisser — un billet minuscule perd sa
   qualité d'objet. C'est de le COUCHER.

   Et ce n'est pas une invention : le site a DÉJÀ un billet horizontal,
   `.ticket`, dans la section Talleres — corps à gauche, souche à droite,
   perforation verticale. Le pase du hero était le seul billet debout de
   tout le site. En le couchant, il passe de ~500 px à ~200 px, plus rien
   n'est tronqué, la souche redevient lisible… et les deux billets du site
   parlent enfin le même langage.

   Sur mobile il reste debout : à 390 px de large, deux colonnes ne
   tiennent pas, et la hauteur n'y est plus le problème.
   ══════════════════════════════════════════════════════════════════════ */
@media(min-width:760px){
  .pass{
    --stub:clamp(230px,26vw,300px);
    display:flex;align-items:stretch;
    width:min(760px,100%);max-width:none;
    rotate:-.5deg}
  .pass:hover{rotate:0deg;translate:0 -4px}

  /* Le corps prend la place restante ; la souche a une largeur fixe, comme
     dans `.ticket`. Les rayons suivent la nouvelle géométrie. */
  .pass__top{
    flex:1;min-width:0;
    padding:var(--s-6);gap:var(--s-3);align-content:center;
    border-radius:var(--r-md) 0 0 var(--r-md)}
  .pass__stub{
    flex:0 0 var(--stub);width:auto;
    padding:var(--s-5) var(--s-4);align-content:center;
    border-radius:0 var(--r-md) var(--r-md) 0}

  /* Les encoches marquent la couture. Elle était horizontale, elle est
     verticale : elles passent des coins bas/haut aux bords droite/gauche. */
  .pass__top::before,.pass__top::after{
    left:auto;right:calc(var(--notch)/-2);bottom:auto;background:var(--c-cream)}
  .pass__top::before{top:calc(var(--notch)/-2)}
  .pass__top::after{bottom:calc(var(--notch)/-2)}
  .pass__stub::before,.pass__stub::after{
    left:calc(var(--notch)/-2);right:auto;top:auto;background:var(--c-cream)}
  .pass__stub::before{top:calc(var(--notch)/-2)}
  .pass__stub::after{bottom:calc(var(--notch)/-2)}

  /* La ligne de perforation bascule elle aussi. */
  .pass__perf{
    left:-1px;right:auto;top:16px;bottom:16px;width:2px;height:auto;
    background:linear-gradient(rgba(0,42,79,.35) 50%,transparent 0);
    background-size:2px 11px}

  /* Le feston reste en haut : c'est le bord déchiré du billet, pas une
     décoration de couture. Il court sur toute la largeur. */
  .pass__picado{grid-area:auto;position:absolute;top:0;left:0;right:0;z-index:4}
  .pass__top{padding-top:calc(var(--s-6) + 15px)}
  .pass__stub{padding-top:calc(var(--s-5) + 15px)}

  /* La déchirure au survol part sur le côté, pas vers le bas. */
  .pass:hover .pass__stub{translate:14px 0 !important;rotate:1deg !important}

  /* Le dépassement dans le bas de l'écran n'a plus lieu d'être : à 200 px
     de haut, le billet tient entièrement dans le premier écran. */
  .pass{margin-bottom:0}
}

@media(min-width:1100px){
  .pass{justify-self:center;align-self:center;margin:0;max-width:none}
  .hero{padding-block:clamp(112px,13vh,140px) clamp(var(--s-6),6vh,var(--s-8))}
}

/* ── LES LOGOS RESPIRENT ──────────────────────────────────────────────
   27 px au plus haut : ils se lisaient comme une frise décorative, pas
   comme des partenaires. On les remonte de moitié et on écarte, sans
   revenir aux plaques — le détourage par `multiply` tient toujours. */
.hero .presented__logos a[data-sponsor]{--plate-h:clamp(30px,3.2vw,42px)}
.hero .presented__logos{gap:clamp(var(--s-5),2.6vw,var(--s-7))}
.hero .presented__logos a[data-sponsor] img{opacity:.88}
.hero .presented__label{font-size:.6875rem}

/* ── LE PASE COUCHÉ TROUVE SA HAUTEUR ─────────────────────────────────
   Mesuré : 292 px, et le hero débordait de 94 px sur un écran de 800.
   Couché, un billet doit faire la hauteur d'un billet — pas celle d'une
   carte. Trois réglages, aucun compromis sur le contenu :

     · le feston disparaît. Sur un billet couché il court au-dessus des
       DEUX moitiés : rouge sur rouge il est invisible, rouge sur la
       souche crème il dessine une frange qui n'appartient à rien. Le
       `.ticket` de Talleres n'en a pas non plus — l'identité du billet
       couché vient de la perforation verticale et des encoches ;
     · les dates descendent d'un cran : à 3,5 rem elles imposaient la
       hauteur de tout l'objet ;
     · le bouton calendrier tient sur une ligne — c'est lui qui faisait
       gonfler la souche en passant à deux. */
@media(min-width:760px){
  .pass__picado{display:none}
  .pass__top{padding:var(--s-5) var(--s-6);gap:var(--s-2)}
  .pass__stub{padding:var(--s-4) var(--s-4);gap:var(--s-2);--stub:clamp(250px,25vw,296px)}
  .pass__dates{font-size:clamp(1.9rem,2.5vw,2.7rem)}
  .pass__where{font-size:.875rem}
  .pass__cta{font-size:.75rem;padding-inline:var(--s-4);white-space:nowrap}
  .pass__stub .count{margin-top:var(--s-1)}
}

/* ── LE PREMIER ÉCRAN S'ADAPTE AUX ÉCRANS COURTS ──────────────────────
   Un hero calé sur `100svh` n'est responsive que si son CONTENU l'est
   aussi. À 800 px de haut — un portable 13 pouces, barre d'onglets
   comprise — le titre gardait sa taille de grand écran et tout partait
   sous la ligne de flottaison. Les tailles suivent donc la hauteur
   disponible, pas seulement la largeur. */
@media(min-width:1100px) and (max-height:900px){
  .hero{padding-block:clamp(96px,12vh,120px) var(--s-5)}
  .hero__inner{row-gap:var(--s-5)}
  .hero__main{gap:var(--s-3)}
  .hero h1{font-size:clamp(2.6rem,4.6vw,4.6rem)}
  .hero__lead{font-size:1.05rem}
  .hero .presented__logos a[data-sponsor]{--plate-h:clamp(26px,2.6vw,32px)}
  .pass__dates{font-size:2.1rem}
}

/* ══════════════════════════════════════════════════════════════════════
   LE PASE PREND SON BORD DÉCOUPÉ
   Les deux billets de référence — le Lviv Tattoo Fest et le Buenas Open —
   ne partagent ni la couleur, ni la typo, ni la mise en page. Ils
   partagent UN geste : le bord latéral découpé en demi-cercles, continu.
   Pas deux encoches à la couture : une dentelure sur toute la hauteur.

   C'est ce détail qui fait lire « billet » avant même qu'on ait lu un
   mot. Nos deux encoches disaient « carte avec une entaille ».

   Technique : le même masque que le feston qui existait déjà en haut du
   pase (`.pass__picado`), tourné de 90°. Une tuile de 100 % × 16 px, un
   cercle de 8 px accroché au bord, répétée verticalement.

   Le masque est posé sur les deux MOITIÉS, pas sur `.pass` : l'ombre
   portée vit sur le parent, elle est donc calculée APRÈS le masque et
   épouse la dentelure au lieu de dessiner un rectangle. Un masque posé
   sur le parent l'aurait au contraire découpée net.

   Conséquence : les encoches de la couture, qui débordaient de la boîte
   de chaque moitié, seraient rognées par ce masque. Elles remontent donc
   d'un cran, sur `.pass` lui-même, qui n'est pas masqué.
   ══════════════════════════════════════════════════════════════════════ */
@media(min-width:760px){
  .pass__top{
    border-radius:0;
    -webkit-mask:radial-gradient(circle 8px at 0 50%, transparent 98%,#000) 0 0/100% 16px repeat-y;
    mask:radial-gradient(circle 8px at 0 50%, transparent 98%,#000) 0 0/100% 16px repeat-y}
  .pass__stub{
    border-radius:0;
    -webkit-mask:radial-gradient(circle 8px at 100% 50%, transparent 98%,#000) 0 0/100% 16px repeat-y;
    mask:radial-gradient(circle 8px at 100% 50%, transparent 98%,#000) 0 0/100% 16px repeat-y}

  /* Les encoches de la couture passent sur le parent. Elles se placent au
     droit de la ligne de perforation, calculée depuis la largeur du talon. */
  .pass__top::before,.pass__top::after,
  .pass__stub::before,.pass__stub::after{display:none}
  .pass::before,.pass::after{
    content:'';position:absolute;z-index:3;
    left:calc(100% - var(--stub) - var(--notch)/2);
    width:var(--notch);height:var(--notch);border-radius:50%;
    background:var(--c-cream)}
  .pass::before{top:calc(var(--notch)/-2)}
  .pass::after{bottom:calc(var(--notch)/-2)}

  /* Le filet rectangulaire que j'avais posé pour empêcher la souche de se
     dissoudre ne peut plus servir : il tracerait un cadre droit au travers
     de la dentelure. C'est l'ombre portée, désormais fidèle à la forme,
     qui détache le billet du fond. */
  .pass{box-shadow:none;filter:drop-shadow(0 18px 30px rgba(10,37,64,.16))}
  .pass:hover{filter:drop-shadow(0 26px 40px rgba(10,37,64,.22))}

  /* ── LE FOLIO VERTICAL ──────────────────────────────────────────────
     Il court le long du bord découpé, comme le « LVIV TATTOO FEST » de la
     référence. Le talon lui réserve un couloir pour qu'il ne passe pas
     sous le compte à rebours — même correction que sur les billets
     d'atelier, où le folio écrivait par-dessus le nombre de places. */
  .pass__stub{padding-right:calc(var(--s-4) + 22px);position:relative}
  .pass__folio{
    position:absolute;right:16px;top:50%;translate:0 -50%;
    writing-mode:vertical-rl;
    font-size:.5rem;letter-spacing:.34em;text-transform:uppercase;
    color:var(--c-muted);opacity:.7;white-space:nowrap;pointer-events:none}
}
@media(max-width:759px){
  .pass__folio{display:none}
}

/* ── LES LOGOS, ENCORE UN CRAN ────────────────────────────────────────
   42 px ne suffisaient pas : les marques à mot-symbole large (Prismatic,
   Alstom) tombent à 12-14 px de hauteur de lettre une fois le ratio
   appliqué. On monte la référence, et les coefficients optiques font le
   reste — c'est justement leur rôle. */
.hero .presented__logos a[data-sponsor]{--plate-h:clamp(38px,4.2vw,54px)}
.hero .presented__label{font-size:.75rem;letter-spacing:.14em}
@media(min-width:1100px) and (max-height:900px){
  .hero .presented__logos a[data-sponsor]{--plate-h:clamp(32px,3.4vw,44px)}
}

/* ── LE CORPS DU BILLET SE CENTRE ─────────────────────────────────────
   `justify-items:start` venait du billet DEBOUT, où le texte s'appuyait
   sur le bord gauche comme sur une affiche. Couché et posé sur l'axe
   central du hero, le même alignement le décentrait : le bloc penchait à
   gauche dans une moitié deux fois plus large que lui. */
@media(min-width:760px){
  .pass__top{justify-items:center;text-align:center}
  .pass__free{align-self:center}
}

/* ── LE FOLIO NE DOIT PAS DÉPASSER ────────────────────────────────────
   « Festival Francia-México · 2026 » composé verticalement à .34em
   d'interlettrage demande plus de 230 px : le talon en fait 217. Le texte
   sortait donc par le bas.

   Un folio se lit de près, il n'a pas à être long : on garde ce qui
   identifie l'événement et on laisse tomber le mot « Festival », que le
   billet dit déjà trois fois. Interlettrage resserré, et un garde-fou —
   si un jour le texte rallonge, il sera coupé au lieu de déborder. */
@media(min-width:760px){
  .pass__folio{
    font-size:.4375rem;letter-spacing:.24em;
    max-height:calc(100% - var(--s-5));overflow:hidden}
}

/* ══════════════════════════════════════════════════════════════════════
   LES BILLETS D'ATELIER PRENNENT LE MÊME BORD
   Le pase du hero et les billets de Talleres sont le même objet à deux
   échelles. Depuis que le pase porte une dentelure sur ses bords
   extérieurs, garder les billets d'atelier avec des angles arrondis
   ferait lire deux familles là où il n'y en a qu'une.

   Même recette, à la lettre — et c'est bien le signe qu'elle est juste :
   masque sur les deux MOITIÉS pour que l'ombre portée du parent épouse la
   découpe, encoches de la couture remontées sur le parent, angles remis
   à plat du côté dentelé.

   Uniquement à partir de 760 px : en dessous le billet passe en colonne,
   la couture devient horizontale, et une dentelure verticale n'aurait
   plus rien à découper.
   ══════════════════════════════════════════════════════════════════════ */
@media(min-width:761px){
  .ticket__body{
    border-radius:0;
    -webkit-mask:radial-gradient(circle 8px at 0 50%, transparent 98%,#000) 0 0/100% 16px repeat-y;
    mask:radial-gradient(circle 8px at 0 50%, transparent 98%,#000) 0 0/100% 16px repeat-y}
  .ticket__stub{
    border-radius:0;
    -webkit-mask:radial-gradient(circle 8px at 100% 50%, transparent 98%,#000) 0 0/100% 16px repeat-y;
    mask:radial-gradient(circle 8px at 100% 50%, transparent 98%,#000) 0 0/100% 16px repeat-y}

  /* Les quatre encoches de la couture débordaient de chaque moitié : le
     masque les aurait rognées. Elles passent sur `.ticket`, qui n'est pas
     masqué — deux suffisent, une en haut, une en bas de la couture. */
  .ticket__body::before,.ticket__body::after,
  .ticket__stub::before,.ticket__stub::after{display:none}
  .ticket{position:relative}
  .ticket::before,.ticket::after{
    content:'';position:absolute;z-index:3;
    left:calc(100% - var(--stub) - var(--notch)/2);
    width:var(--notch);height:var(--notch);border-radius:50%;
    background:var(--c-cream)}
  .ticket::before{top:calc(var(--notch)/-2)}
  .ticket::after{bottom:calc(var(--notch)/-2)}

  /* La souche pivotait autour de son bord gauche au survol : sur un bord
     dentelé, la rotation faisait glisser les demi-cercles l'un sur
     l'autre. Elle se décale désormais, comme le talon du pase. */
  .ticket:hover .ticket__stub{transform:translateX(10px)}
}

/* ── LE GRIS DANS LES DENTELURES ──────────────────────────────────────
   Vérifié en désactivant le filtre : les demi-lunes grises n'étaient pas
   un fond, c'était l'OMBRE PORTÉE du billet.

   Et ce n'est pas réglable : une morsure de 8 px est entourée de matière
   sur trois côtés, donc l'ombre de cette matière tombe forcément dedans.
   Réduire le flou ne fait que concentrer le gris au lieu de l'étaler ;
   décaler l'ombre vers le bas ne change rien aux morsures latérales. Une
   ombre et une découpe fine sont physiquement incompatibles.

   Les billets d'atelier s'en passent très bien : du bleu nuit et du rouge
   plein sur du crème n'ont besoin de personne pour se détacher. C'est
   d'ailleurs le cas des deux billets de référence — celui de Lviv n'a
   aucune ombre, et celui de Buenas la remplace par une texture de papier.

   La dentelure devient alors le seul relief, et c'est mieux ainsi : elle
   se lit comme une découpe, plus comme un objet posé sur une table. */
@media(min-width:761px){
  .ticket,.ticket:hover{filter:none}
  /* (retiré) Le billet se soulevait de 6 px. Depuis que la déchirure
     fonctionne, ce soulèvement fait doublon : deux gestes pour un seul
     message. Voir « LE SURVOL NE FAIT PLUS QUE DÉCHIRER » en fin de
     fichier. */
}

/* Le pase du hero est dans le même cas, en plus discret — son ombre est
   quatre fois plus légère et le fond du hero est nuancé. Elle passe
   néanmoins sous le seuil du visible dans les morsures. Le talon crème,
   lui, ne peut pas se passer d'un bord sur un fond crème : il le reprend
   par un très léger dégradé interne, qui ne déborde donc jamais. */
@media(min-width:760px){
  .pass{filter:drop-shadow(0 10px 14px rgba(10,37,64,.10))}
  .pass:hover{filter:drop-shadow(0 16px 20px rgba(10,37,64,.14))}
  .pass__stub{
    background:linear-gradient(180deg,
      color-mix(in srgb,var(--c-cream) 92%,#fff),
      color-mix(in srgb,var(--c-cream) 88%,var(--c-ink)))}
}

/* ── LA PERFORATION APPARTIENT À LA DÉCHIRURE, PAS AU TALON ────────────
   Au survol, le talon glisse — et la ligne pointillée glissait avec lui,
   parce qu'elle est son enfant dans le balisage. On voyait donc le talon
   s'éloigner EN EMPORTANT la perforation, pendant que les encoches, elles,
   restaient sur place. Trois éléments censés marquer la même couture
   partaient dans deux directions : c'est ce chevauchement.

   Physiquement, une perforation ne suit pas le talon : c'est la trace de
   l'endroit où le papier s'est séparé. Elle reste donc avec le corps, et
   le talon s'en va nu — comme un vrai billet déchiré.

   On la contre-translate exactement de ce que le talon parcourt. La
   valeur est dupliquée, c'est le prix à payer pour ne pas déplacer
   l'élément dans le gabarit ; elles doivent rester en phase. */
.ticket:hover .ticket__perf{transform:translateX(-10px)}
.ticket__perf{transition:transform .55s var(--ease-out)}

@media(min-width:760px){
  .pass__perf{transition:transform .55s var(--ease-out)}
  .pass:hover .pass__perf{transform:translateX(-14px)}
}

/* ── LE MASQUE ET LE MOUVEMENT NE PEUVENT PAS VIVRE SUR LE MÊME ÉLÉMENT ─
   Constaté au navigateur : au survol, la dentelure du talon disparaissait
   et son bord droit redevenait droit. Le `mask-image` calculé, lui, était
   inchangé — ce n'était donc pas une règle écrasée.

   La cause est le compositing. Un `transform` promeut l'élément sur sa
   propre couche ; Chrome recompose alors le talon sans réappliquer son
   masque. Le bord découpé « se cache » exactement le temps du geste.

   Preuve par l'autre moitié : le CORPS garde sa dentelure pendant que le
   billet entier se soulève. Un transform sur le PARENT ne casse rien —
   le masque de l'enfant est appliqué avant, dans son espace local, et
   c'est le résultat déjà découpé qui est déplacé.

   D'où la sortie : descendre le masque d'un niveau. La couleur du talon
   passe sur son ::before — un calque qui ne bouge pas — et c'est LUI qui
   porte la découpe. Le talon, libéré du masque, peut être déplacé autant
   qu'on veut : il emporte un enfant déjà découpé.

   Les pseudo-éléments sont disponibles : ils portaient les encoches de la
   couture, remontées sur le parent quand la dentelure est arrivée.

   ⚠ Vaut pour TOUT élément masqué qu'on anime — c'est aussi le cas du
     talon du pase, que GSAP décale au scroll : sans ce correctif, sa
     dentelure disparaissait dès le premier pixel de défilement. */
@media(min-width:761px){
  .ticket__stub{background:transparent;isolation:isolate;-webkit-mask:none;mask:none}
  .ticket__stub::before{
    content:'';display:block;position:absolute;inset:0;z-index:-1;
    background:var(--c-rojo);
    -webkit-mask:radial-gradient(circle 8px at 100% 50%, transparent 98%,#000) 0 0/100% 16px repeat-y;
    mask:radial-gradient(circle 8px at 100% 50%, transparent 98%,#000) 0 0/100% 16px repeat-y;
    transition:background-color .35s var(--ease-out)}
  .ticket__stub:hover::before{background:var(--c-rojo-lo)}
  .ticket--full .ticket__stub::before{background:var(--c-ink)}
  .ticket--full .ticket__stub:hover::before{background:var(--c-muted)}
}

@media(min-width:760px){
  .pass__stub{background:transparent;isolation:isolate;-webkit-mask:none;mask:none}
  .pass__stub::before{
    content:'';display:block;position:absolute;inset:0;z-index:-1;
    background:linear-gradient(180deg,
      color-mix(in srgb,var(--c-cream) 92%,#fff),
      color-mix(in srgb,var(--c-cream) 88%,var(--c-ink)));
    -webkit-mask:radial-gradient(circle 8px at 100% 50%, transparent 98%,#000) 0 0/100% 16px repeat-y;
    mask:radial-gradient(circle 8px at 100% 50%, transparent 98%,#000) 0 0/100% 16px repeat-y}
}

/* Le ::before du talon servait d'ENCOCHE avant de servir de calque : il
   traînait donc encore `width:26px`, `height:26px` et `border-radius:50%`.
   `inset:0` ne suffit pas à les annuler — une largeur explicite l'emporte
   sur le décalage droit — et le calque se réduisait à un petit disque.
   On remet sa géométrie à plat, explicitement. */
@media(min-width:761px){
  .ticket__stub::before{width:auto;height:auto;border-radius:0}
}
@media(min-width:760px){
  .pass__stub::before{width:auto;height:auto;border-radius:0}
}

/* `isolation:isolate` remettait le problème qu'on venait de résoudre :
   il crée un groupe de composition, et c'est précisément ce groupe qui
   fait perdre le masque. Testé variante par variante dans le navigateur —
   sans lui, la dentelure revient.

   Le calque peut s'en passer : `.ticket` et `.pass` n'ont aucun fond, un
   z-index négatif ne peut donc rien passer derrière qui se verrait. */
@media(min-width:761px){
  .ticket__stub{isolation:auto}
}
@media(min-width:760px){
  .pass__stub{isolation:auto}
}

/* ══════════════════════════════════════════════════════════════════════
   LA DÉCHIRURE ET LA DENTELURE NE PEUVENT PAS COEXISTER
   Trois variantes testées dans le navigateur, aucune ne tient :

     · masque sur le talon lui-même          → perdu dès le transform
     · masque descendu sur son ::before      → perdu aussi
     · calque en couche normale (z-index:0)  → perdu aussi

   La règle qui ressort : dans Chrome, un `transform` posé sur l'élément
   masqué — ou sur n'importe quel élément entre le masque et la racine de
   peinture — fait tomber le masque. Seul un transform sur le PARENT est
   sans effet : c'est pourquoi le corps garde sa dentelure quand le billet
   entier se soulève.

   Le talon doit bouger pour se déchirer, et c'est lui qui est découpé.
   Les deux fonctions demandent la même propriété sur le même élément :
   ce n'est pas un réglage à trouver, c'est une incompatibilité.

   ON GARDE LA DENTELURE. C'est le geste que portent les deux billets de
   référence, il est visible en permanence, et c'est lui qui fait lire
   « billet » avant le premier mot. La déchirure était une micro-animation
   au survol — agréable, mais qui ne se voit qu'au passage de la souris et
   jamais sur mobile.

   ⚠ Le vrai danger était ailleurs : GSAP décalait le talon du pase AU
     SCROLL. La dentelure du hero aurait donc disparu dès le premier pixel
     de défilement, sans que personne ne touche à rien. Ce tween part
     aussi — voir main.js.

   Le survol garde sa récompense, portée par le parent : le billet se
   soulève, le pase se redresse. Aucun masque n'est concerné. */
@media(min-width:761px){
  .ticket:hover .ticket__stub{transform:none}
  .ticket:hover .ticket__perf{transform:none}
}
@media(min-width:760px){
  .pass:hover .pass__stub{translate:none !important;rotate:none !important}
  .pass:hover .pass__perf{transform:none}
}

/* ── LE BILLET RESSERRE SA LARGEUR ────────────────────────────────────
   Le corps prenait toute la colonne : le texte occupait son tiers gauche
   et les deux autres tiers restaient vides. Un billet n'est pas une
   bannière — sa largeur doit venir de son contenu, pas du conteneur.
   La souche garde sa mesure, c'est donc bien la partie bleue qui rend
   la place. */
.tickets{max-width:900px}

/* ── LE ROUGE SUR LE BLEU : 2,09:1 ────────────────────────────────────
   Mesuré, pas estimé : --c-rojo (220,15,70) sur le corps #0F4272 donne
   2,09:1. Le minimum lisible pour du texte courant est 4,5:1 — on était
   à moins de la moitié. La date et le nombre de places, les deux
   informations qu'on lit en diagonale, étaient les moins lisibles du
   billet.

   Le rouge saturé ne peut pas marcher là : c'est une couleur SOMBRE, et
   le fond l'est aussi. La palette prévoit d'ailleurs le cas — --c-peach
   et --c-coral sont documentés comme « détails » et « urgence : places
   restantes » sur fond bleu. Ils n'étaient simplement pas utilisés ici.

     date/heure  --c-peach                      8,94:1
     places      --c-coral éclairci             ~5,2:1
     complet     vert éclairci                  ~6,3:1

   Les jauges suivent leur libellé : une barre et son texte qui ne disent
   pas la même chose se lisent comme deux informations. */
.ticket__body .ticket__when{color:var(--c-peach)}
.ticket__body .seats__txt{color:color-mix(in srgb,var(--c-coral) 84%,#fff)}
.ticket__body .seats__fill{background:color-mix(in srgb,var(--c-coral) 84%,#fff)}
.ticket__body .seats--ok .seats__txt{color:color-mix(in srgb,#22C48A 70%,#fff)}
.ticket__body .seats--ok .seats__fill{background:color-mix(in srgb,#22C48A 70%,#fff)}
.ticket__body .live-dot{background:currentColor}

/* ══════════════════════════════════════════════════════════════════════
   BILLETS D'ATELIER · QUATRE CORRECTIONS
   ══════════════════════════════════════════════════════════════════════ */

/* ── ① C'EST LA ZONE BLEUE QUI DEVAIT MAIGRIR, PAS LE BILLET ──────────
   J'avais rétréci le billet entier — la souche a maigri avec lui, alors
   qu'elle ne demandait rien. Le billet reprend toute la colonne et c'est
   la SOUCHE qui s'élargit : le bleu rend la place, le rouge la prend. */
.tickets{max-width:none}
@media(min-width:761px){
  .ticket{--stub:clamp(280px,26vw,400px)}
}

/* ── ② « COMPLET » AVAIT PERDU SA DÉCOUPE ─────────────────────────────
   `.ticket--full .ticket__stub{background:var(--c-ink)}` vaut (0,2,0) et
   battait mon `.ticket__stub{background:transparent}` (0,1,0) : la souche
   gardait donc un fond plein, peint PAR-DESSUS le calque découpé. D'où un
   bord droit, et lui seul. La couleur descend sur le calque, comme pour
   les autres. */
@media(min-width:761px){
  .ticket--full .ticket__stub{background:transparent}
}

/* ── ③ « COMPLET » ET « PRESQUE PLEIN » AVAIENT LA MÊME COULEUR ───────
   Ma règle sur `.ticket__body .seats__fill` (0,2,0) écrasait
   `.seats--full .seats__fill` (0,2,0) par ordre d'écriture : la jauge
   « complet » repassait en ambre, la couleur de l'urgence. Trois états,
   trois couleurs — c'est tout l'intérêt d'une jauge :

     il reste de la place   vert
     il en reste peu        ambre
     complet                bleu clair, neutre — il n'y a plus rien à
                            décider, donc plus rien à signaler          */
.ticket__body .seats--full .seats__txt,
.ticket--full .ticket__body .seats__txt{color:var(--c-muted-inv)}
.ticket__body .seats--full .seats__fill,
.ticket--full .ticket__body .seats__fill{background:var(--c-muted-inv)}

/* ── ④ LE GESTE DE DÉCHIRURE, AUTREMENT ───────────────────────────────
   Vérifié une quatrième fois, masque déplacé sur le calque enfant : un
   `transform` sur le talon le fait tomber quand même. La déchirure ne
   peut donc pas revenir telle quelle — pas sans renoncer à la dentelure.

   On garde la dentelure et on change le GESTE. Ce qui doit réagir au
   survol, ce n'est pas le talon : c'est la COUTURE. Les deux encoches
   s'ouvrent et la ligne de perforation s'affirme — le billet a l'air sur
   le point de céder, sans qu'aucune pièce ne bouge.

   Ni l'une ni l'autre n'est masquée : les encoches vivent sur `.ticket`,
   la perforation est un simple filet. On reste sur `transform` et
   `opacity`, conformément à la règle de perf du projet. */
@media(min-width:761px){
  .ticket::before,.ticket::after{
    transition:transform .45s var(--ease-out)}
  .ticket:hover::before{transform:scale(1.28) translateY(-2px)}
  .ticket:hover::after{transform:scale(1.28) translateY(2px)}
  .ticket__perf{transition:opacity .35s var(--ease-out)}
  .ticket:hover .ticket__perf{opacity:1}
}

/* ── LE TALON DU PASE REDEVIENT CRÈME ─────────────────────────────────
   Je lui avais donné un dégradé interne pour qu'il ne se dissolve pas
   dans le fond crème du hero. C'était vrai quand il était un rectangle
   plein : sans ombre ni bord, il disparaissait.

   Ça ne l'est plus. Depuis qu'il porte une dentelure, il a une SILHOUETTE
   — et une silhouette se détache toute seule, sans avoir besoin d'être
   teintée. Le dégradé ne faisait plus que le salir : 12 % d'encre dans du
   crème, ça donne du gris, et un billet gris n'est plus un billet.

   Retour au crème plein. Ce sont la découpe, la perforation et les deux
   encoches qui disent où finit le papier. */
@media(min-width:760px){
  .pass__stub::before{background:var(--c-cream)}
}

/* ── LES ENCOCHES DU PASE TOMBAIENT À CÔTÉ DE LA COUTURE ──────────────
   Mesuré : couture à 447 px, encoches centrées à 460. Deux causes qui
   s'additionnent, et la même leçon.

   ① `--stub` était déclaré DEUX FOIS — 300 px sur `.pass`, 296 px sur
      `.pass__stub`. Les encoches, posées sur `.pass`, calculaient donc
      leur position avec une mesure que le talon n'utilisait pas.

   ② Même accordées, elles seraient tombées à côté : `flex:0 0 296px`
      n'impose rien tant que `min-width` vaut `auto`, c'est-à-dire la
      largeur mini du CONTENU. Le bouton calendrier poussait le talon à
      310 px. La base flex n'était qu'un souhait.

   Une seule déclaration, sur le parent, et un talon qui ne peut plus
   déborder sa base. Les encoches retombent sur la couture — et elles
   n'ont plus qu'une seule mesure à suivre. */
@media(min-width:760px){
  .pass__stub{--stub:initial;min-width:0}
  .pass{--stub:clamp(250px,25vw,300px)}
}

/* `--stub:initial` était le mauvais mot-clé : sur une propriété
   personnalisée il produit la valeur « invalide garantie », ce qui rend
   `flex:0 0 var(--stub)` invalide À SON TOUR — la déclaration entière
   tombe et le talon repasse en `flex:0 1 auto`, donc à la taille de son
   contenu. C'est exactement ce qu'on cherchait à empêcher.
   `inherit` est le mot juste : le talon reprend la mesure du parent. */
@media(min-width:760px){
  .pass__stub{--stub:inherit}
}

/* ══════════════════════════════════════════════════════════════════════
   LA DÉCHIRURE REVIENT — ON PEINT LA DENTELURE AU LIEU DE LA DÉCOUPER
   J'avais conclu à une incompatibilité. Elle n'existait qu'avec UNE
   technique : le masque. Le masque tombe dès qu'un transform entre en
   jeu, c'est mesuré et ça n'a pas de contournement.

   Mais découper n'est pas la seule façon d'obtenir un bord dentelé. On
   peut aussi PEINDRE des demi-cercles couleur fond par-dessus le bord.
   Le résultat à l'écran est identique — et un `background-image` survit à
   tous les transforms du monde, parce qu'il est peint dans l'espace local
   de l'élément avant que le transform ne le déplace.

   Ce que ça coûte, et il faut le dire : la dentelure n'est plus une
   découpe, c'est un trompe-l'œil. Elle suppose de connaître la couleur du
   fond. Ici on la connaît — la section Talleres et le hero sont en crème
   plein — et le thème avait déjà prévu le jeton pour ça : `--notch-bg`,
   utilisé par les encoches depuis le début et documenté « la section
   talleres est crème ». On s'en sert.

   ⚠ Si un jour un billet est posé sur une image ou un dégradé marqué, il
     faudra revenir au masque — et renoncer au mouvement du talon. Les
     deux ne peuvent pas coexister.
   ══════════════════════════════════════════════════════════════════════ */
@media(min-width:761px){
  /* Plus de masque, plus de calque intermédiaire : les moitiés
     retrouvent leur fond, tout simplement. */
  .ticket__body,.ticket__stub{-webkit-mask:none;mask:none}
  .ticket__stub{background:var(--c-rojo)}
  .ticket__stub:hover{background:var(--c-rojo-lo)}
  .ticket--full .ticket__stub{background:var(--c-ink)}
  .ticket--full .ticket__stub:hover{background:var(--c-muted)}

  /* Les deux bandes de demi-cercles. 8 px de large — le rayon — collées
     au bord extérieur, une tuile tous les 16 px. */
  .ticket__body::before,
  .ticket__stub::after{
    content:'';display:block;position:absolute;top:0;bottom:0;z-index:2;
    width:8px;height:auto;border-radius:0;background-repeat:repeat-y}
  .ticket__body::before{
    left:0;right:auto;
    background-image:radial-gradient(circle 8px at 0 8px,
      var(--notch-bg,var(--c-cream)) 98%, transparent 100%);
    background-size:8px 16px}
  .ticket__stub::after{
    right:0;left:auto;
    background-image:radial-gradient(circle 8px at 8px 8px,
      var(--notch-bg,var(--c-cream)) 98%, transparent 100%);
    background-size:8px 16px}

  /* ET LA DÉCHIRURE. Le talon peut enfin bouger : il n'a plus de masque à
     perdre. La perforation reste sur la ligne de rupture — elle est la
     trace de la déchirure, pas un morceau du talon. */
  .ticket:hover .ticket__stub{transform:translateX(12px)}
  .ticket:hover .ticket__perf{transform:translateX(-12px)}
  .ticket:hover::before,.ticket:hover::after{transform:none}
}

@media(min-width:760px){
  .pass__top,.pass__stub{-webkit-mask:none;mask:none}
  .pass__stub{background:var(--c-cream)}
  .pass__stub::before{display:none}
  .pass__top::before,
  .pass__stub::after{
    content:'';display:block;position:absolute;top:0;bottom:0;z-index:2;
    width:8px;height:auto;border-radius:0;background-color:transparent;
    background-repeat:repeat-y}
  .pass__top::before{
    left:0;right:auto;
    background-image:radial-gradient(circle 8px at 0 8px,
      var(--c-cream) 98%, transparent 100%);
    background-size:8px 16px}
  .pass__stub::after{
    right:0;left:auto;
    background-image:radial-gradient(circle 8px at 8px 8px,
      var(--c-cream) 98%, transparent 100%);
    background-size:8px 16px}

  .pass:hover .pass__stub{translate:14px 0 !important;rotate:.8deg !important}
  .pass:hover .pass__perf{transform:translateX(-14px)}
}

/* Les bandes héritaient du `background: var(--c-cream)` des anciennes
   encoches — un aplat qui remplissait les 8 px et recouvrait le dégradé.
   Le bord paraissait donc droit, simplement décalé. Seul le dégradé doit
   peindre : la couleur de fond passe à transparent.
   (La règle existait déjà pour le pase ; elle manquait pour les billets.) */
@media(min-width:761px){
  .ticket__body::before,.ticket__stub::after{background-color:transparent}
}

/* ══════════════════════════════════════════════════════════════════════
   LE RIDEAU ROGNAIT LA DÉCHIRURE
   Mesuré au navigateur, état `:hover` forcé : au survol le talon part
   bien de 12 px, mais son bord droit redevient DROIT et les encoches
   s'évanouissent. Cette fois le masque n'y est pour rien — c'est le
   rideau de révélation.

   `.curtain` anime un `clip-path` et GSAP laisse la valeur d'arrivée en
   style INLINE, définitivement : `polygon(0 0, 100% 0, 100% 100%, 0 100%)`,
   soit exactement la boîte du billet. Tout ce qui sort de la boîte est
   donc découpé à vie. La dentelure du talon fait 8 px et la déchirure
   l'emmène 12 px plus loin : les deux sortent, les deux disparaissent.
   C'est le défaut déjà identifié à l'époque du `translateX(18px)` ; il
   est revenu avec la déchirure, par une autre porte.

   Deux gestes :
   ① le rideau du billet devient plus large que sa boîte — 16 px de marge
      à droite, de quoi loger les 12 px de course et les 8 px de dentelure.
      Le haut et le bas restent bornés à 100 % : c'est ce bord-là qui coupe
      les encoches en demi-lunes, il doit rester ;
   ② la révélation terminée, main.js rend le `clip-path` à la CSS
      (`.is-shown`) — sans quoi la valeur figée en inline continuerait
      d'ignorer le jeton.                                                */
.js .curtain.is-shown{clip-path:var(--clip-shown)}
@media(min-width:761px){
  .ticket{--clip-shown:polygon(0 0, calc(100% + 16px) 0, calc(100% + 16px) 100%, 0 100%)}
}

/* ══════════════════════════════════════════════════════════════════════
   UNE DÉCHIRURE A DEUX BORDS DÉCHIRÉS
   Le rognage réparé, il restait le geste : la fente s'ouvrait entre deux
   bords parfaitement rectilignes. Ce n'est pas un billet qui se déchire,
   c'est un rectangle rouge qui glisse. Trois détails manquaient, et ce
   sont les trois qui disent « papier » :

     · les BORDS. Un papier séparé le long d'une perforation garde ses
       demi-trous — chaque trou se coupe en deux, une moitié pour le
       corps, l'autre pour le talon, au MÊME endroit. On les peint comme
       la dentelure extérieure (des demi-cercles couleur fond), mais au
       rythme de la perforation, 11 px, et on ne les révèle qu'au survol :
       au repos la couture reste la ligne pointillée du dessin d'origine ;
     · la PERFORATION. Elle ne peut pas rester en même temps que les
       demi-trous, elle les redoublerait à 1 px près. Elle s'efface pendant
       que la frange apparaît : c'est la même ligne, une fois pointillée,
       une fois déchirée. Au passage son contre-déplacement redevient
       transitionné — la règle `transition:opacity` posée plus haut avait
       écrasé celle sur `transform`, et la ligne SAUTAIT d'un coup pendant
       que le talon glissait sur .55s ;
     · les ENCOCHES. Centrées sur la couture, elles se retrouvaient au
       milieu de la fente, crème sur crème : invisibles, et les deux coins
       gauche du talon redevenaient carrés. Elles se recentrent dans la
       fente et mordent alors les deux moitiés, 6 px de chaque côté.

   Rien ne bouge au repos : tout est en `opacity` et `transform`.        */
@media(min-width:761px){
  .ticket__body::after,
  .ticket__stub::before{
    content:'';display:block;position:absolute;top:0;bottom:0;
    width:6px;height:auto;border-radius:0;z-index:2;opacity:0;
    background-color:transparent;background-repeat:repeat-y;
    -webkit-mask:none;mask:none;
    transition:opacity .4s var(--ease-out)}
  .ticket__body::after{
    left:auto;right:0;
    background-image:radial-gradient(circle 6px at 6px 5.5px,
      var(--notch-bg,var(--c-cream)) 98%, transparent 100%);
    background-size:6px 11px}

  /* Le ::before du talon a servi trois fois — encoche, calque masqué, et
     maintenant frange. Ses deux premiers rôles lui ont laissé des fonds
     PLUS spécifiques que `.ticket__stub::before` (`.ticket--full …`,
     `…:hover…`) : ils repeindraient une barre pleine par-dessus les
     demi-trous. On redéclare donc la frange sur chacun d'eux, à
     spécificité égale et en dernier. */
  .ticket__stub::before,
  .ticket__stub:hover::before,
  .ticket--full .ticket__stub::before,
  .ticket--full .ticket__stub:hover::before{
    left:0;right:auto;
    background-color:transparent;
    background-image:radial-gradient(circle 6px at 0 5.5px,
      var(--notch-bg,var(--c-cream)) 98%, transparent 100%);
    background-size:6px 11px;background-repeat:repeat-y}

  .ticket:hover .ticket__body::after,
  .ticket:hover .ticket__stub::before{opacity:1}

  .ticket__perf{transition:transform .55s var(--ease-out),opacity .4s var(--ease-out)}
  .ticket:hover .ticket__perf{opacity:0}

  .ticket:hover::before,.ticket:hover::after{transform:translateX(6px)}
}

/* ══════════════════════════════════════════════════════════════════════
   LE SURVOL NE FAIT PLUS QUE DÉCHIRER
   Deux gestes se jouaient en même temps : l'objet ENTIER se soulevait — le
   billet de 5-6 px, le pase qui se redressait et montait — pendant que la
   souche se déchirait. Deux fois « il se passe quelque chose » pour un seul
   message, et le soulèvement volait la vedette à la déchirure, qui est
   pourtant la seule à raconter le geste réel.

   On garde celui qui dit quelque chose. Le reste ne bouge plus.
   (Les ombres de survol restent : ce n'est pas du mouvement.)        */
.ticket:hover{transform:none}
.pass:hover{translate:none}
.pass,.pass:hover{rotate:var(--pass-rot,-1.6deg)}
@media(max-width:1099px){.pass,.pass:hover{--pass-rot:0deg}}
@media(min-width:760px){.pass,.pass:hover{--pass-rot:-.5deg}}

/* ══════════════════════════════════════════════════════════════════════
   LE PASE DU HERO PREND LA DÉCHIRURE DES BILLETS D'ATELIER
   C'est le même objet à deux échelles, et sa déchirure était restée la
   version d'avant : la souche partait EN TOURNANT, la perforation partait
   avec elle en éventail — elle est son enfant dans le balisage — et les
   deux bords de la fente restaient rectilignes. À côté des billets de
   Talleres, ça ne tenait plus.

   Même recette, à la lettre, et pour les mêmes raisons :
     · translation pure, sans rotation : la rotation faisait diverger la
       perforation du bord qu'elle est censée marquer, et désalignait les
       deux dentelures ;
     · une frange sur chaque bord de la couture, révélée au survol — les
       demi-trous que laisse un papier séparé le long d'une perforation ;
     · la perforation s'efface pendant que la frange apparaît ;
     · les encoches se recentrent dans la fente et mordent les deux
       moitiés au lieu d'y disparaître.

   Les deux pseudo-éléments nécessaires étaient libres : les encoches de la
   couture sont montées sur `.pass` il y a deux blocs, laissant
   `.pass__top::after` et `.pass__stub::before` sans emploi.            */
@media(min-width:760px){
  .pass:hover .pass__stub{translate:14px 0 !important;rotate:none !important}

  .pass__top::after,
  .pass__stub::before{
    content:'';display:block;position:absolute;top:0;bottom:0;
    width:6px;height:auto;border-radius:0;z-index:2;opacity:0;
    background-color:transparent;background-repeat:repeat-y;
    -webkit-mask:none;mask:none;
    transition:opacity .4s var(--ease-out)}
  .pass__top::after{
    left:auto;right:0;
    background-image:radial-gradient(circle 6px at 6px 5.5px,
      var(--c-cream) 98%, transparent 100%);
    background-size:6px 11px}
  .pass__stub::before{
    left:0;right:auto;
    background-image:radial-gradient(circle 6px at 0 5.5px,
      var(--c-cream) 98%, transparent 100%);
    background-size:6px 11px}
  .pass:hover .pass__top::after,
  .pass:hover .pass__stub::before{opacity:1}

  .pass__perf{transition:transform .55s var(--ease-out),opacity .4s var(--ease-out)}
  .pass:hover .pass__perf{opacity:0}

  .pass::before,.pass::after{transition:transform .45s var(--ease-out)}
  .pass:hover::before,.pass:hover::after{transform:translateX(7px)}
}

/* ══════════════════════════════════════════════════════════════════════
   LE PASE REPASSE EN DÉCOUPE RÉELLE — LE BLANC « DEVANT » LE BILLET
   Signalé à l'œil, et c'est exact : on voyait des pastilles BLANCHES
   posées par-dessus le billet, à l'endroit même des bords stylés.

   Ce n'est pas une ombre ni un filtre, c'est le trompe-l'œil. La dentelure
   du pase n'était pas découpée, elle était PEINTE : des demi-cercles
   couleur `--c-cream` déposés sur le bord, plus deux disques crème pour
   les encoches de la couture. Le procédé ne tient qu'à une condition —
   que le fond soit un aplat de cette couleur exacte. C'est vrai dans
   Talleres (crème plein), c'est FAUX dans le hero, qui a un voile en
   dégradé, des lavis et des silhouettes. Les faux trous ne trouaient donc
   rien : ils recouvraient. D'où le blanc devant le billet.

   Le fichier disait déjà quoi faire — « ⚠ Si un jour un billet est posé
   sur une image ou un dégradé marqué, il faudra revenir au masque » — et
   n'en voyait pas le moyen, parce qu'un `transform` faisait alors tomber
   le masque dans Chrome. RETESTÉ dans Chrome 151, quatre cas de figure :
   masque + transform sur le même élément, transform sur le parent,
   transform animé avec `will-change` — la dentelure tient dans TOUS. Le
   bug est corrigé en amont, la contrainte n'existe plus.

   Le masque revient donc, et il porte les trois découpes d'un coup, en
   `mask-composite:intersect` : la dentelure extérieure, et les deux quarts
   d'encoche de la couture. Chaque moitié emporte ses propres encoches —
   c'est ce qui les fait se déchirer en deux quand le talon part, sans une
   ligne de plus.

   ⚠ Ce qui reste peint : les franges de la déchirure, à l'intérieur du
     papier, et seulement au survol. Elles ne débordent jamais sur le fond.
   ══════════════════════════════════════════════════════════════════════ */
@media(min-width:760px){
  .pass__top{
    -webkit-mask-image:
      radial-gradient(circle 8px at 0 50%, transparent 98%,#000),
      radial-gradient(circle 13px at 100% 0, transparent 98%,#000),
      radial-gradient(circle 13px at 100% 100%, transparent 98%,#000);
    mask-image:
      radial-gradient(circle 8px at 0 50%, transparent 98%,#000),
      radial-gradient(circle 13px at 100% 0, transparent 98%,#000),
      radial-gradient(circle 13px at 100% 100%, transparent 98%,#000);
    -webkit-mask-size:100% 16px,100% 100%,100% 100%;
    mask-size:100% 16px,100% 100%,100% 100%;
    -webkit-mask-repeat:repeat-y,no-repeat,no-repeat;
    mask-repeat:repeat-y,no-repeat,no-repeat;
    -webkit-mask-composite:source-in;
    mask-composite:intersect}

  .pass__stub{
    -webkit-mask-image:
      radial-gradient(circle 8px at 100% 50%, transparent 98%,#000),
      radial-gradient(circle 13px at 0 0, transparent 98%,#000),
      radial-gradient(circle 13px at 0 100%, transparent 98%,#000);
    mask-image:
      radial-gradient(circle 8px at 100% 50%, transparent 98%,#000),
      radial-gradient(circle 13px at 0 0, transparent 98%,#000),
      radial-gradient(circle 13px at 0 100%, transparent 98%,#000);
    -webkit-mask-size:100% 16px,100% 100%,100% 100%;
    mask-size:100% 16px,100% 100%,100% 100%;
    -webkit-mask-repeat:repeat-y,no-repeat,no-repeat;
    mask-repeat:repeat-y,no-repeat,no-repeat;
    -webkit-mask-composite:source-in;
    mask-composite:intersect}

  /* Les deux bandes peintes et les deux disques d'encoche n'ont plus
     d'objet : ils sont dans le masque, en vrai. */
  .pass__top::before,
  .pass__stub::after{display:none}
  .pass::before,.pass::after{display:none}

  /* L'OMBRE RESTE, ET ELLE SE RESSERRE.
     Essayée à zéro, comme sur les billets d'atelier : le talon disparaît.
     Ce n'est pas le même cas de figure — un billet d'atelier est bleu nuit
     et rouge sur du crème, il se détache tout seul ; le talon du pase est
     crème SUR du crème, sa silhouette ne suffit pas, même découpée pour
     de vrai.
     Mais une ombre large se dépose dans les morsures et, au survol, remplit
     la fente d'une bande grise — c'était l'autre défaut visible. On garde
     donc le contact (décalage vertical, flou court) et on abandonne le
     halo : l'ombre dit « posé sur », elle ne déborde plus de 14 px. */
  .pass{filter:drop-shadow(0 3px 5px rgba(10,37,64,.16))}
  .pass:hover{filter:drop-shadow(0 5px 7px rgba(10,37,64,.18))}
}

/* ══════════════════════════════════════════════════════════════════════
   LE TALON DU PASE PASSE À L'ENCRE
   Le talon était crème, et le hero est crème. Deux blocs de la même
   couleur qui se touchent : il ne se lisait plus comme du papier posé
   sur un sol, mais comme un trou dans la page. La découpe réelle, l'ombre
   courte, rien n'y suffisait — on ne détache pas une couleur d'elle-même.

   Ce que ça retire, et il faut le dire : la règle « sur un vrai billet, la
   souche est la partie CLAIRE ». Elle avait été posée pour régler un
   problème de lisibilité (le compteur tombait à 1,56:1 sur le rouge), pas
   pour elle-même — et la lisibilité se règle aussi bien à l'encre, avec
   des rapports plus élevés qu'en crème.

   Ce que ça apporte : le pase parle enfin la langue des billets d'atelier
   — deux moitiés, deux couleurs franches sur crème — et le premier écran
   reprend les trois teintes du drapeau au lieu de deux.

   Tout ce qui vivait sur le crème s'inverse ici, rapport par rapport :
     · pilule pleine crème sur encre        13,6:1
     · secondaires en --c-cream-2            7,1:1  (le jeton est fait pour ça)
     · le nombre du compte à rebours en corail  5,6:1 — le rouge pur reste
       interdit sur le bleu (2,16:1), le corail est sa forme lisible.
   ══════════════════════════════════════════════════════════════════════ */
.pass__stub{background:var(--c-ink);color:var(--c-cream)}
.pass__cta{background:var(--c-cream);color:var(--c-ink)}
.pass__cta:hover{background:#fff;color:var(--c-ink)}
.pass__alt{color:var(--c-cream-2);opacity:1}
.pass__alt button{color:var(--c-cream)}
.pass__alt button:hover{background:rgba(247,241,228,.16)}
.pass__stub .count{color:var(--c-cream-2)}
.pass__stub .count b{color:var(--c-coral)}
@media(min-width:760px){
  /* Le folio et la perforation étaient écrits pour un fond clair : à
     l'encre ils disparaissaient. */
  .pass__folio{color:var(--c-cream);opacity:.42}
  .pass__perf{background:linear-gradient(rgba(247,241,228,.55) 50%,transparent 0);
    background-size:2px 11px}
}
@media(max-width:759px){
  /* Même correction pour le billet debout : la couture y est horizontale,
     et son pointillé sombre disparaissait lui aussi sur l'encre. */
  .pass__perf{background:linear-gradient(90deg,rgba(247,241,228,.55) 50%,transparent 0);
    background-size:11px 2px}
}

/* ══════════════════════════════════════════════════════════════════════
   PASSE DE COHÉRENCE SUR LES TITRES
   Audit mesuré section par section. Trois défauts, dont deux invisibles
   à l'œil parce qu'ils se manifestent comme « c'est bizarre » plutôt que
   comme une erreur.
   ══════════════════════════════════════════════════════════════════════ */

/* ── ① L'EXERGUE ÉTAIT ÉCRASÉ DANS QUATRE SECTIONS ────────────────────
   `.head p{font-size:var(--t-lead);color:var(--c-muted)}` vaut (0,1,1) ;
   `.label` ne vaut que (0,1,0). Or l'exergue EST un `<p class="label">`.
   Dans toute section qui utilise le bloc `.head`, il était donc rendu en
   taille de chapô — 21 px au lieu de 11 — et en gris de texte courant au
   lieu de sa couleur d'accent.

   Mesuré : programa, talleres, plano, práctica à 21 px ; zonas,
   embajadores, patrocinadores à 11 px. Ces trois-là n'utilisent pas
   `.head`, c'est tout ce qui les sauvait. Quatre sections sur sept
   affichaient un exergue qui n'en était plus un.

   La couleur suit le fond, pas la section : accent rouge sur crème,
   pêche sur sombre. Deux règles au lieu de quatre valeurs. */
.head p.label{font-size:var(--t-label);letter-spacing:var(--ls-label);
  font-weight:700;color:var(--c-rojo-txt)}
.agenda .head p.label,
.sponsors .head p.label,
.bic .head p.label,
.tiltp .head p.label{color:var(--c-peach)}

/* ── ② DEUX TITRES ÉTAIENT COUPÉS, EN SILENCE ─────────────────────────
   `.wordcut .w{display:block;width:100%}` combiné à `overflow:hidden` —
   nécessaire à la révélation — donne un conteneur à la largeur de la
   colonne et un mot qui ne rétrécit pas. Le mot n'est donc pas ajusté :
   il est TRONQUÉ, sans le moindre signe.

     zonas       « UNIVERSOS »   mot 336 px dans 256 px →  80 px perdus
     embajadores « EMBAJADORES » mot 684 px dans 464 px → 220 px perdus

   Le second était le plus grave et personne ne l'avait vu : le titre
   perdait ses quatre dernières lettres.

   Chacun se règle là où est sa cause :
     · zonas — la colonne est trop étroite pour son propre titre. Elle
       s'élargit, et l'empilement de cartes se resserre d'autant (les deux
       sont calculés depuis le même jeton, ils restent donc accordés) ;
     · embajadores — la colonne fait 36 rem parce que c'est la mesure de
       LECTURE du sous-titre et de la liste. Le titre, lui, n'a aucune
       raison de s'y tenir : il déborde vers l'extérieur, où il y a de la
       place, sans toucher au texte. */
@media(min-width:900px){
  .zstack{--zs-title-w:min(21rem,26vw)}
}
.tiltp .wordcut{max-width:min(88vw,46rem)}

/* ── ③ LE GARDE-FOU : PLUS DE COUPE SILENCIEUSE ───────────────────────
   Corriger ces deux-là ne suffit pas : la prochaine traduction plus
   longue, le prochain `--wc-size` mal calibré rejoueraient la scène. La
   révélation a besoin de masquer le mot VERTICALEMENT pendant qu'il
   remonte — elle n'a jamais eu besoin de le couper horizontalement.

   `clip-path` fait exactement cette distinction : on ferme en haut et en
   bas, on ouvre en grand à gauche et à droite. Un mot trop long débordera
   donc — visiblement, réparable — au lieu de disparaître.

   Le `clip-path` est sur le conteneur, le `transform` sur le mot qu'il
   contient : deux éléments différents, la combinaison qui fonctionne. */
.split .w{overflow:visible;clip-path:inset(0 -100vmax)}

/* Correctifs de ciblage, après vérification au navigateur :

   · `--zs-title-w` est déclaré sur `.zstack__grid`, pas sur `.zstack`.
     Ma règle sur le parent était donc écrasée par le descendant. C'est le
     bon endroit de toute façon : la variable sert au titre ET à la garde
     du bloc de bandes, les deux vivent dans la grille.

   · le titre d'embajadores n'était pas bridé par `max-width` mais par sa
     largeur CALCULÉE : `width:fit-content` sur un conteneur dont l'unique
     enfant est un bloc en `width:100%` ne peut pas remonter la largeur du
     mot — la mesure est circulaire. `max-content` la lit directement. */
.zstack__grid{--zs-title-w:min(22rem,27vw)}
.tiltp .wordcut{width:max-content;max-width:min(88vw,46rem)}

/* ── LA CAUSE RACINE DE LA COUPE ──────────────────────────────────────
   Je réglais les symptômes section par section. La cause est une seule
   déclaration : `.wordcut .w{width:100%}`. Un bloc en largeur 100 % prend
   la mesure de son conteneur et ne peut pas grandir pour son contenu —
   le mot qui dépasse est donc coupé, où qu'il soit.

   Ce `100%` a pourtant une raison : `.wordcut--offset` aligne sa seconde
   ligne à droite, ce qui exige une boîte pleine largeur. On garde donc le
   plancher et on lève le plafond : `min-width:100%` conserve l'alignement,
   `width:max-content` laisse la boîte suivre le mot.

   Avec ça, aucun titre ne peut plus être tronqué — dans aucune section,
   aucune langue, aucune taille. Le garde-fou en `clip-path` reste une
   ceinture de sécurité, il n'a normalement plus rien à rattraper. */
.wordcut .w{width:max-content;min-width:100%}

/* ── LE COMPTEUR DEVIENT CLIQUABLE ────────────────────────────────────
   Les cases passent de <div> à <a> : elles héritent donc du soulignement
   et de la couleur de lien du navigateur, qu'on remet à plat. La grille,
   elle, ne change pas — c'est le même gabarit.

   Le libellé monte de 11 à 13 px. C'est lui qui porte le sens (« 9 » seul
   ne dit rien) et il était trois fois plus petit que le chiffre qu'il
   explique ; le rapport était à l'envers.

   L'affordance reste discrète : un filet se trace sous le chiffre et le
   libellé passe à l'accent. Pas de flèche — quatre flèches sur une ligne
   feraient une barre d'outils. */
.proof__i{
  display:block;color:inherit;text-decoration:none;position:relative;
  transition:background-color .3s var(--ease-out)}
.proof__i:hover{background:color-mix(in srgb,var(--c-ink) 4%,transparent)}
.proof__l{font-size:var(--t-small);letter-spacing:.12em;
  transition:color .3s var(--ease-out)}
.proof__i:hover .proof__l{color:var(--c-rojo-txt)}
.proof__n{position:relative;display:inline-block}
.proof__n::after{
  content:'';position:absolute;left:0;right:0;bottom:-.06em;height:2px;
  background:var(--c-rojo);
  transform:scaleX(0);transform-origin:0 50%;
  transition:transform .4s var(--ease-out)}
.proof__i:hover .proof__n::after,
.proof__i:focus-visible .proof__n::after{transform:scaleX(1)}
.proof__i:focus-visible{outline:3px solid var(--c-rojo);outline-offset:-3px}
@media(prefers-reduced-motion:reduce){
  .proof__n::after{transition:none}
}

/* ── LES BANDES RÉCUPÈRENT LA PLACE DE DROITE ─────────────────────────
   Mesuré à 1510 px : grille 1440, titre 352, bandes 560 — et 440 px de
   vide à droite. Le bloc était plus étroit que le vide qu'il laissait.

   La cause est une garde bien intentionnée : pour que les bandes ne
   passent jamais sous le titre quand la fenêtre rétrécit, leur largeur
   plafonnait à « tout sauf DEUX colonnes de titre ». Symétrique, donc
   sûr — mais la colonne de droite ne protège rien : il n'y a personne.

   On réserve du côté où il y a quelqu'un. Une marge gauche fixe, égale à
   la colonne du titre, garantit le même écart à toutes les largeurs ; à
   droite, le bloc s'étend jusqu'à son plafond de lisibilité (58 rem).
   La garde est plus forte qu'avant — c'est une marge, plus un calcul —
   et elle rend 370 px aux cartes.

   Le bloc n'est donc plus centré sur la section mais posé après le
   titre. C'est plus juste : ces deux-là se lisent ensemble, un titre à
   gauche et son empilement à droite, pas deux objets flottants. */
/* ⚠ 1025 px, pas 900 : c'est À CETTE LARGEUR que le titre quitte sa
   colonne absolue pour se poser au-dessus des bandes (@media max-width
   1024px, plus haut dans le fichier). En calant ma règle sur 900, je
   réservais encore une colonne de titre à gauche entre 900 et 1024 —
   mesuré : les bandes démarraient à 357 px alors que le titre occupait
   déjà toute la largeur au-dessus. Deux règles qui décrivent la même
   bascule doivent partager sa borne. */
@media(min-width:1025px){
  .zstack__list{
    margin-left:calc(var(--zs-title-w) + var(--s-7));
    margin-right:auto;
    max-width:min(58rem, calc(100% - var(--zs-title-w) - var(--s-7)))}
}

/* ══════════════════════════════════════════════════════════════════════
   LA FIN DE PAGE RETROUVE SON ALTERNANCE
   Séquence mesurée avant : navy #002A4F + panneau #00529E → vert #005D4F
   → bleu #004079. Quatre blocs sombres d'affilée, trois bleus. Et les
   deux bleus d'embajadores ne sont séparés que d'environ 30 % de
   luminance : le panneau qui s'anime ne se lisait pas comme un objet mais
   comme une variation d'éclairage.

   Le panneau doit rester SOMBRE — le texte posé dessus est crème — donc
   on ne pouvait pas l'éclaircir, seulement changer sa teinte. Le vert
   était le seul candidat… et il était pris par la section suivante. Les
   deux décisions étaient liées, elles se règlent ensemble :

     embajadores   navy + panneau VERT
     alliés        CRÈME
     footer        bleu institutionnel

   Chaque couleur n'apparaît plus qu'une fois, et le crème rétabli au
   milieu redonne la respiration que le fichier annonce en tête (« crème ·
   ROUGE · crème · SOMBRE… ») et qui avait disparu.

   Bénéfice qui n'était pas l'objectif : trois des six logos partenaires
   ont un fond BLANC opaque. Sur crème, `multiply` les détoure gratuitement
   — les plaques deviennent inutiles ici, comme dans le hero.
   ══════════════════════════════════════════════════════════════════════ */

/* ── ① LE PANNEAU ANIMÉ PASSE AU VERT ─────────────────────────────────*/
.tiltp{--tp-panel:var(--c-verde)}

/* ── ② LA SECTION ALLIÉS PASSE AU CLAIR ───────────────────────────────
   Tout ce qui suit ne fait qu'inverser des couleurs écrites pour un fond
   sombre. Aucune structure ne bouge. */
.sponsors{background:var(--c-cream);color:var(--c-ink)}
.sponsors .label,
.sponsors .head p.label,
.sponsors .display em,
.sponsors .scard__role,
.sponsors .why .label{color:var(--c-rojo-txt)}
.sponsors .head p,
.sponsors .scard__txt,
.sponsors .sponsors__note,
.sponsors .why__txt p{color:var(--c-muted)}
.sponsors .scard{border-top-color:var(--c-line)}
.sponsors .scard:hover{border-top-color:var(--c-rojo)}
.sponsors .scard .logo--n1{color:var(--c-ink)}
.sponsors .scard .logo--n2{color:var(--c-muted)}
.sponsors .sponsors__brands{border-top-color:var(--c-line)}
.sponsors .sponsors__brandstitle{color:var(--c-muted)}
.sponsors .sponsors__sep{background:var(--c-line)}
.sponsors .btn--ghost{color:var(--c-ink);box-shadow:inset 0 0 0 1.5px var(--c-line);--btn-fill:var(--c-ink)}
.sponsors .btn--ghost:hover,.sponsors .btn--ghost:focus-visible{color:var(--c-cream)}
.sponsors .why__h{color:var(--c-ink)}
.sponsors .arrow{color:var(--c-rojo-txt)}
/* Le lien d'invitation vers Ser patrocinador, sous la rangée de logos :
   centré comme elle, séparé d'elle par un pas de plus que ses propres logos. */
.sponsors__join{margin:var(--s-6) 0 0;text-align:center}

/* L'emblème du bicentenaire était monté sur une plaque crème parce qu'il
   est fourni sur fond blanc et qu'il aurait fait un pavé sur le bleu. Sur
   crème, la plaque n'a plus d'objet : le blanc s'y fond presque, et
   `multiply` achève le travail. */
.sponsors .why__emblem img{background:none;mix-blend-mode:multiply}

/* Les logos perdent leurs plaques ici aussi : le fond est connu et clair,
   `multiply` détoure. Renault garde son inversion — blanc sur noir sans
   elle déposerait un rectangle noir. */
.sponsors .scard .logo[data-sponsor],
.sponsors .sponsors__brandsrow .logo[data-sponsor]{
  background:none;box-shadow:none;padding:0;border-radius:0}
.sponsors .scard .logo[data-sponsor] img,
.sponsors .sponsors__brandsrow .logo[data-sponsor] img{mix-blend-mode:multiply}

/* Deux restes du fond sombre, vus au rendu :

   · l'italique du sous-titre (« celebración ») était en pêche — une
     couleur faite pour le bleu, invisible sur crème ;
   · Renault gardait un pavé blanc. Son fichier est blanc sur noir ;
     inversé, le noir devient un blanc de compression (~250, pas 255), et
     `multiply` assombrit donc très légèrement ce presque-blanc. Un point
     de luminosité en plus le ramène à 255, où le multiply est neutre.
     Même correctif que pour la silhouette du hero. */
.sponsors .wordcut-sub em{color:var(--c-rojo-txt)}
/* .sponsors .logo[data-sponsor="renault"] img{filter:invert(1) brightness(1.06)} */

/* ── ON INVERSE : SOL CLAIR, PANNEAU SOMBRE ───────────────────────────
   Le panneau est une forme qui tourne, se réduit et se recadre au scroll.
   Une forme ne se voit que contre ce qui n'est pas elle : posée sur un
   fond sombre, qu'elle soit bleue ou verte, elle reste une variation
   d'éclairage. C'était le fond du problème, pas la teinte.

   Éclaircir le PANNEAU était impossible — le contenu posé dessus est
   crème. Mais rien n'obligeait le SOL à être sombre. On échange donc les
   rôles : sol crème, panneau encre.

     · l'effet se voit enfin — à chaque degré de rotation, les angles
       découvrent du crème, et c'est ce contraste qui dessine la forme ;
     · le contenu ne bouge pas d'un pixel : il était crème sur encre, il
       l'est toujours, simplement l'encre est passée du fond au panneau ;
     · et le second bleu disparaît de la section, ce qui était la demande
       de départ.

   Le panneau est `sticky` sur 100vh et le contenu commence à 42vh : il
   est donc toujours couvert. Par précaution, la section porte quand même
   l'encre comme couleur de texte — si un élément passait un jour hors du
   panneau, il resterait lisible sur le crème au lieu de disparaître. */
.tiltp{
  background:var(--c-cream);
  color:var(--c-ink);
  --tp-panel:var(--c-ink)}
.tiltp__content{color:var(--c-cream)}
.tiltp .wordcut-sub{color:var(--c-cream)}

/* ── LE PANNEAU DÉBORDE EN HAUTEUR, PAS EN LARGEUR ────────────────────
   Sol clair, l'effet se voit — et il montre aussitôt sa faille : le
   panneau se réduit (scale jusqu'à .9) et sa bande haute découvre du
   crème. Le contenu qui s'y trouve est crème lui aussi : il disparaît.
   C'est exactement ce que tu pressentais — « le fond devrait être clair
   mais le contenu sera illisible ».

   Ce n'est pas un choix à faire, c'est une géométrie à corriger. La forme
   se lit par ses bords LATÉRAUX inclinés — c'est la rotation qui la
   dessine, pas la réduction verticale. On donne donc au panneau de la
   hauteur en trop (108 vh, remonté de 4 vh) : il couvre toujours le texte
   du haut en bas, et ses côtés continuent de pencher contre le crème.

   La réduction d'échelle est adoucie en conséquence — à .9 elle rognait
   assez pour rouvrir la brèche malgré le dépassement. */
.tiltp__panel{
  height:108vh;top:-4vh;
  transform:rotate(calc(var(--tp-r) * 1deg))
            scale(calc(1 - var(--tp-r) * 0.018))}

/* Le `clip-path` incline aussi le bord HAUT du panneau : avec seulement
   4 vh de dépassement, la pente redescendait dans l'écran et laissait la
   première ligne sur le crème. On double le dépassement — les pourcentages
   du clip étant relatifs au panneau, un panneau plus haut repousse la
   pente d'autant au-dessus de la ligne de flottaison. */
.tiltp__panel{height:118vh;top:-9vh}

/* ── LE MOT DE FOND TIENT DANS L'ÉCRAN ────────────────────────────────
   Mesuré : « EMBAJADORES » à 272 px de corps occupe 2643 px dans une
   fenêtre de 1510 — 1133 px de débord, on n'en voyait que le tiers
   central. Un mot qui déborde d'un quart se lit comme une texture ; un
   mot dont il manque la moitié se lit comme un défaut.

   Le rapport largeur/corps de cette police à ce réglage est de 9,7 pour
   l'espagnol. À 8,6vw, le mot occupe donc ~84 % de la fenêtre en espagnol
   et ~91 % en français (« AMBASSADEURS », un signe de plus) : il respire
   des deux côtés, dans les deux langues, à toutes les largeurs — un corps
   en vw garde le même rapport quelle que soit la fenêtre.

   En dessous de 900 px on garde le débord d'origine : sur un écran
   étroit, un mot qui sort du cadre EST une texture, et le réduire le
   rendrait simplement illisible. */
@media(min-width:900px){
  .tiltp__texture{font-size:8.6vw}
}

/* ── LA TEXTURE REDEVIENT UNE BANDE, ET PASSE DERRIÈRE LE TITRE ───────
   La texture de l'agenda est une chaîne RÉPÉTÉE — « AGENDA · AGENDA ·
   AGENDA ». C'est ce qui fait que son débord se lit comme une bande qui
   continue hors du cadre, et non comme un mot tronqué.

   La nôtre était un mot seul : en débordant, elle paraissait cassée. Je
   l'avais donc rapetissée pour qu'elle tienne — ce qui réglait la coupe
   mais lui retirait sa nature de texture, et cassait la parenté avec
   l'agenda. Le bon correctif était dans le CONTENU, pas dans la taille :
   le mot se répète, et retrouve la mesure de sa voisine.

   Elle descend aussi derrière le bloc de titre. Perchée à 12 % elle
   flottait seule en haut de l'écran, sans rien à quoi se rattacher ;
   posée derrière l'exergue et le titre, elle redevient ce qu'elle est —
   le fond de ce bloc, comme la bande de l'agenda l'est du sien. */
/* Même mesure que l'agenda, au réglage près : clamp(8rem, 22vw, 20rem).
   J'avais voulu la réduire pour qu'une occurrence entière soit lisible —
   c'était passer à côté de l'effet. Chez l'agenda aussi la bande est
   largement coupée : on lit « AG », « ENDA », « A », et c'est justement ce
   fragment géant qui fait la matière. Une texture n'est pas là pour être
   lue, elle est là pour donner une échelle et une profondeur au bloc
   qu'elle porte. Le titre, lui, se charge de nommer.

   Les deux sections partagent donc désormais la même règle : chaîne
   répétée, corps identique, posée derrière le bloc de titre. */
.tiltp__texture{
  top:34%;
  font-size:clamp(8rem,22vw,20rem)}

/* ══════════════════════════════════════════════════════════════════════
   EMBAJADORES — LE TITRE REPREND LE DESSUS
   Mesuré avant de toucher à quoi que ce soit : le `<h2>` de cette section
   et celui d'Agenda sont RIGOUREUSEMENT identiques — Archivo 400, 90 px,
   interligne .8, crème, même composant. Le titre n'était donc pas en
   cause ; c'est son entourage qui le tuait, sur trois points.

   ① LA HIÉRARCHIE ÉTAIT INVERSÉE. `.wordcut` est en poids 400,
      `.wordcut-sub` en poids 800 : l'exergue est plus gras que le titre
      qu'il commente. Dans Agenda ça ne se voit pas, « Dos días, hora por
      hora » tient sur une ligne. Ici la phrase fait TROIS lignes de
      capitales grasses et pèse plus lourd que le mot. L'exergue redevient
      donc ce qu'il est — un chapô : casse normale, poids courant, mesure
      de lecture. On ne touche pas à `.wordcut`, qui va bien partout.

   ② LE MOT ÉTAIT ÉCRIT DEUX FOIS. La texture répétait EMBAJADORES à
      20 rem derrière un titre qui dit… Embajadores, et le fragment visible
      tombait sur « AJADO » : ni lu, ni abstrait. Une texture doit donner
      de l'échelle, pas bégayer le titre. Elle dit désormais autre chose —
      les deux pays — et redescend sous le bloc de titre, à un corps où un
      motif complet reste lisible.

   ③ LE TITRE N'AVAIT AUCUN APPUI. Colonne de 36 rem centrée, 42vh de vide
      au-dessus, et toute la moitié droite du panneau inoccupée. Il flottait
      au milieu d'un aplat. Il prend maintenant la largeur, et les quatre
      dirigeants passent à sa droite en deux colonnes : le panneau est plein
      de haut en bas, le titre a quelque chose à annoncer.
   ══════════════════════════════════════════════════════════════════════ */

/* ① le chapô cesse de concurrencer le titre */
.tiltp .wordcut-sub{
  font-family:var(--f-sans);font-weight:400;font-variation-settings:normal;
  text-transform:none;letter-spacing:0;line-height:1.55;
  font-size:clamp(1.0625rem,1.35vw,1.25rem);
  max-width:30ch;color:var(--c-cream-2)}

/* ② la texture descend, se calme, et ne répète plus le titre */
.tiltp__texture{
  top:auto;bottom:5%;
  font-size:clamp(4.5rem,11vw,10rem);
  opacity:.085}

/* ③ deux colonnes : le titre annonce, la liste occupe */
@media(min-width:1100px){
  /* ⚠ La largeur utile n'est PAS celle de la section. Le panneau est
     incliné de 3,5° ET rogné : son bord haut ne court que de 12,9 % à
     87,1 % de la largeur (c'est le `clip-path`, dérivé de `--tp-r`), et
     ses côtés penchent. Un contenu à 92vw sortait donc du navy — titre
     amputé de son E à gauche, noms coupés à droite, en crème sur crème,
     donc invisibles. La mesure du contenu suit la partie sûre du panneau,
     et le bloc descend sous la pente du haut. */
  .tiltp__content{
    max-width:min(72rem,76vw);
    display:grid;
    grid-template-columns:minmax(0,20rem) minmax(0,1fr);
    column-gap:clamp(2rem,4vw,4.5rem);row-gap:var(--s-3);
    align-items:start;
    padding-block:clamp(11rem,26vh,17rem) clamp(6rem,11vh,9rem)}
  .tiltp__content > .label{grid-column:1 / -1;grid-row:1}
  .tiltp__content > .wordcut{grid-column:1 / -1;grid-row:2}
  .tiltp__content > .wordcut-sub{grid-column:1;grid-row:3;margin-top:var(--s-6)}
  .tiltp__people{grid-column:2;grid-row:3;margin-top:var(--s-6);
    grid-template-columns:repeat(2,minmax(0,1fr));
    column-gap:clamp(1.75rem,3vw,3rem)}
  /* Le portrait passe au-dessus du nom : en colonne étroite, la mise en
     ligne portrait + nom + rôle cassait les noms longs sur trois lignes. */
  .tiltp__p{grid-template-columns:1fr;gap:var(--s-3);align-items:start}
  .tiltp__role,
  .tiltp__video{grid-column:1}
  .tiltp__portrait{width:56px;height:56px}
  /* La hauteur reste à 200vh (valeur de base) : c'est elle qui donne sa
     COURSE à l'inclinaison — `run = hauteur de section − hauteur d'écran`.
     La rogner accélérait la bascule au point de la rendre nerveuse. */
}

/* ══════════════════════════════════════════════════════════════════════
   L'INCLINAISON REDEVIENT UNE FORME, PAS UN ÉVÉNEMENT
   Relevé sur la référence (artefact-ai-film-festival.com/rules), page
   entière, à six positions de défilement : ses panneaux portent
   `rotate(4deg)` et `rotate(-4deg)` en dur, constants. Zéro élément de la
   page ne change de style entre deux positions, et deux captures prises
   au même endroit — une en descendant, une en remontant — sont identiques
   au bit près. Il n'y a AUCUNE animation liée au défilement : ce qui bouge
   là-bas, c'est `position:sticky` (cinq blocs qui se collent en haut
   pendant que le suivant glisse par-dessus) et une vidéo en boucle.

   Notre panneau, lui, dérivait sa rotation ET une réduction d'échelle de
   la position de défilement : il basculait de 0 à 5° en descendant et se
   redressait en remontant. Les valeurs sont pourtant symétriques — je les
   ai mesurées — mais l'œil ne lit pas une position, il lit une TRANSITION,
   et une transition a un sens. D'où « pas le même rendu en montant ».

   L'angle est donc figé. Le mécanisme sticky, lui, était déjà là et il
   reste : c'est le panneau qui tient l'écran pendant que le contenu
   défile. On garde le geste de la référence, on retire ce qu'elle n'a
   jamais fait.                                                          */
/* (retiré) `.tiltp__panel{--tp-r:3.5}` figeait l'angle : c'est main.js
   qui le pilote à nouveau, de 0 à 5° sur la traversée. */

/* Le billet mobile du panneau : `--tp-r` étant désormais une constante, la
   règle ≤900 px héritait de la rotation ET de l'échelle du desktop, alors
   qu'elle avait posé sa propre inclinaison (−1,5°, sans découpe) — et que
   les blocs ajoutés depuis (transform, height) passent APRÈS elle dans le
   fichier et la battaient. On la repose ici, en dernier, une fois pour
   toutes. */
/* (caduc) La règle qui remettait le panneau à plat sous 900 px répondait à
   un problème disparu : le panneau était alors pleine largeur, et la moindre
   rotation ouvrait deux coins crème. Depuis qu'il déborde de 12 % de chaque
   côté — comme la bande d'Agenda — l'inclinaison ne découvre plus rien, et
   elle vaut donc à toutes les tailles. `--tp-r` n'existe plus. */

/* ── LES PORTRAITS ARRIVENT ───────────────────────────────────────────
   Le rond n'était qu'un `data-todo`. Il reçoit une photo, et trois
   réglages qui ne sont pas cosmétiques :
     · `object-position: 50% 18%` — deux des quatre fichiers sont des
       plans larges (buste, décor). Centré, le visage se retrouvait à
       moitié hors du disque ;
     · désaturation — les quatre photos viennent de quatre lieux et de
       quatre lumières (mur blanc, feuillage, tableau rose, studio gris).
       En couleur, la rangée n'a aucune unité ; en gris, elle en a une, et
       le navy du panneau devient leur fond commun ;
     · le liseré passe sur un pseudo-élément : une ombre interne se peint
       SOUS le contenu, l'image l'aurait recouverte. */
.tiltp__portrait{position:relative;overflow:hidden;box-shadow:none}
.tiltp__portrait img{width:100%;height:100%;display:block;
  object-fit:cover;object-position:50% 18%;
  filter:grayscale(1) contrast(1.06) brightness(1.02)}
.tiltp__portrait::after{content:'';position:absolute;inset:0;border-radius:50%;
  box-shadow:inset 0 0 0 1.5px var(--c-line-inv)}
@media(min-width:1100px){
  .tiltp__portrait{width:84px;height:84px}
}

/* (retiré) LA PILE COLLANTE — `.tiltp{position:sticky}` + la section
   suivante par-dessus. C'était le mécanisme exact de la référence, et il
   se voyait ; il ne donnait pas la sensation voulue, et il coupait les
   portraits sur une ligne horizontale en montant. L'inclinaison animée
   reprend sa place. Le bloc entier est dans _backup si besoin. */


/* ══════════════════════════════════════════════════════════════════════
   LE PANNEAU DEVIENT UN CHAPITRE À DEUX TEMPS
   Le demi-écran de navy vide sous les portraits ne se réglait pas en le
   décorant : un vide n'est somptueux que s'il encadre quelque chose. Il
   encadre donc le second temps du même propos — qui porte l'événement,
   puis pourquoi il existe.

   Le bloc « Por qué 2026 » vient de la section Aliados, où il ouvrait sur
   un sujet qui n'était pas le sien. Deux gains, un coût :
     + il retrouve son fond d'origine — il est écrit pour l'encre
       (`.why .label` en pêche, `.why__txt p` en `--c-cream-2`), et quatre
       règles de rattrapage avaient dû le repeindre pour le crème ;
     + Aliados ouvre enfin sur Aliados ;
     − l'emblème ne peut pas suivre : son fond blanc n'est détouré que par
       `mix-blend-mode: multiply`, qui exige un fond clair. Il reste donc
       dans Aliados, où il signe l'entrée de section — seul, il n'a plus de
       colonne de texte à côté de lui, la grille passe à une colonne.

   Le filet et l'écart disent la rupture : ce n'est pas la suite de la
   liste, c'est le deuxième mouvement.                                    */
.tiltp__why{display:grid;gap:var(--s-3);max-width:44ch}
.tiltp__why .why__h{margin:0;font-family:var(--f-display);font-weight:900;
  text-transform:uppercase;letter-spacing:-.02em;line-height:1.06;
  font-size:clamp(1.35rem,2.6vw,2rem)}
.tiltp__why-p{margin:0;color:var(--c-cream-2);line-height:1.6}
.tiltp__why .label{color:var(--c-peach)}

@media(min-width:1100px){
  .tiltp__why{
    grid-column:2;max-width:52ch;
    margin-top:clamp(4rem,9vh,7rem);
    padding-top:clamp(2rem,4vh,3rem);
    border-top:1px solid var(--c-line-inv)}
  /* La course de la bascule se resserre : le contenu occupe désormais la
     plage qui était vide, 170vh suffisent là où il en fallait 200. */
  .tiltp{min-height:170vh}
}
@media(max-width:1099px){
  .tiltp__why{margin-top:var(--s-8);padding-top:var(--s-6);
    border-top:1px solid var(--c-line-inv)}
}

/* (le conteneur `.why` a entièrement quitté Aliados : le texte est monté
   dans le panneau, l'emblème l'a suivi sur sa plaque. La section ouvre
   maintenant directement sur son propre sujet.) */

/* ── L'EMBLÈME DU BICENTENAIRE, SUR SA PLAQUE ─────────────────────────
   Il rejoint la déclaration qu'il illustre, dans la colonne restée vide.
   La plaque n'est pas un ornement, c'est une nécessité : le fichier est du
   trait noir et une typo en couleur sur blanc — aucun mode de fusion ne le
   pose sur du bleu sans le détruire (voir le commentaire du gabarit).
   C'est le même procédé que les plaques de logos sponsors, à ceci près
   qu'ici elle se lit comme une carte posée sur le panneau, et non comme un
   rectangle blanc oublié sur du crème.

   On prend le fichier VERTICAL (`-03`) : les trois formats existent pour
   ça, et c'est une colonne étroite.

   ⚠ Le jour où la version blanche arrive, tout ce bloc se réduit à une
     image sans fond — la plaque, le rayon et le padding disparaissent.  */
.tiltp__emblem{margin:0;width:fit-content;
  background:var(--c-cream);border-radius:14px;
  padding:clamp(.9rem,1.5vw,1.4rem);
  box-shadow:0 18px 34px -24px rgba(0,0,0,.6)}
.tiltp__emblem img{display:block;height:auto;width:clamp(120px,17vw,190px)}

@media(min-width:1100px){
  /* Les deux temps du chapitre partagent le même écart : la plaque
     s'aligne sur la première ligne de la déclaration, pas sur son filet. */
  .tiltp__content{--beat-gap:clamp(4rem,9vh,7rem);--beat-pad:clamp(2rem,4vh,3rem)}
  .tiltp__why{margin-top:var(--beat-gap);padding-top:var(--beat-pad)}
  .tiltp__emblem{grid-column:1;grid-row:4;
    margin-top:calc(var(--beat-gap) + var(--beat-pad))}
}
@media(max-width:1099px){
  .tiltp__emblem{margin-top:var(--s-7)}
}

/* ══════════════════════════════════════════════════════════════════════
   LA BANDE FRANCE — LA SYMÉTRIQUE DE LA BANDE MEXIQUE
   L'inclinaison animée est abandonnée. Ce n'est pas un renoncement : la
   bonne réponse était déjà dans le fichier, à deux sections de là.

   `.agenda` n'est pas « une section verte ». C'est LA BANDE MEXIQUE :
   `--ag-panel` vaut `--c-verde`, le vert du drapeau ; elle penche à −2° ;
   et il pend dessous un PAPEL PICADO, le papier découpé des fêtes
   mexicaines. Tout y est signé.

   Embajadores devient donc sa symétrique, terme à terme :

       Mexique (agenda)              France (embajadores)
       ────────────────────────────  ────────────────────────────
       --c-verde, vert du drapeau    --c-azul, bleu du drapeau
       −2°                           +2°
       trapèze pincé en HAUT         trapèze pincé en BAS
       papel picado SOUS la bande    feston de store AU-DESSUS
       (papier découpé de fête)      (la toile des terrasses)

   Le feston n'est pas un motif décoratif choisi au hasard : c'est le même
   geste que le papel picado — une toile découpée en festons — et c'est
   déjà le vocabulaire du site, celui des billets. Les deux cultures se
   répondent par leur façon de découper le papier et le tissu.

   Et la page y gagne un rythme : bande verte penchée à gauche, bande bleue
   penchée à droite, le crème entre les deux.
   ══════════════════════════════════════════════════════════════════════ */
.tiltp{
  /* ── LA SEULE LIGNE À TOUCHER POUR CHANGER LA COULEUR ── */
  --tp-panel:var(--c-azul);
  --tp-tilt:2deg;

  --feston-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='62' height='46'%3E%3Cpath fill='%23fff' fill-rule='evenodd' d='M0 0h62v22a31 24 0 0 1-62 0Z M31 6a3.5 3.5 0 1 0 .01 7A3.5 3.5 0 0 0 31 6Z'/%3E%3C/svg%3E");

  position:relative;overflow:clip;
  background:var(--c-cream);color:var(--c-ink);
  min-height:0;
  padding-block:clamp(7rem,12vw,10rem)}

/* Le panneau redevient ce qu'il est chez Agenda : une bande posée dans la
   section, débordante sur les côtés, avec du crème au-dessus et en dessous
   — c'est cette marge qui rend l'inclinaison lisible. Plus de `sticky`,
   plus de 200vh, plus de hauteur en vh : la bande fait la hauteur du
   contenu, et le vide disparaît de lui-même. */
.tiltp__panel{
  position:absolute;inset:3.5rem -12%;height:auto;width:auto;overflow:hidden;
  background:var(--tp-panel);transform-origin:50%;
  transform:rotate(var(--tp-tilt));
  clip-path:polygon(0% 0%, 100% 0%, 96% 100%, 4% 100%);
  -webkit-mask:none;mask:none}

/* Le feston vit DANS la bande, et il peut : il est calé à `top:0`, donc
   entièrement DANS la découpe. Le picado d'Agenda, lui, PEND sous l'arête —
   au même endroit il perdait ses pointes, coupées par le `clip-path` du
   panneau (que `overflow:visible` ne lève pas). D'où `.agenda__frame` :
   un repère tourné, partagé, sans découpe. Ne pas généraliser l'un à
   l'autre — ce qui les sépare, c'est de quel côté de l'arête ils tombent. */
.tiltp__feston{
  position:absolute;left:0;right:0;top:0;height:46px;z-index:1;
  /* ⚠ `--c-sand` ET NON `--c-cream` depuis le 18/08. Le feston est la frange
     festonnée du panneau : ses bandes claires doivent se confondre avec le SOL
     qu'elles surmontent, pour que seul le rouge se lise comme un feston. Avec
     la crème on obtenait trois tons empilés sur 46 px — sol sable, bande
     crème, bande rouge — et la frange se lisait comme un bandeau rapporté.
     Le jeton, pas une valeur figée : le sol a déjà changé deux fois. */
  background:repeating-linear-gradient(90deg,
    var(--c-sand) 0 62px, var(--c-rojo) 62px 124px);
  -webkit-mask-image:var(--feston-mask);mask-image:var(--feston-mask);
  -webkit-mask-size:62px 46px;mask-size:62px 46px;
  -webkit-mask-repeat:repeat-x;mask-repeat:repeat-x}

/* La typo de fond redescend sous le feston. */
.tiltp__texture{top:auto;bottom:6%;opacity:.10}

/* Le contenu reprend le rythme d'Agenda : plus de réserve pour une
   bascule qui n'existe plus. */
@media(min-width:1100px){
  .tiltp{min-height:0}
  .tiltp__content{padding-block:0}
}
@media(max-width:1099px){
  .tiltp__content{padding-block:0}
}

/* ══════════════════════════════════════════════════════════════════════
   LES LOGOS PARTENAIRES MONTENT D'UN CRAN
   Demande directe : les marques doivent peser plus lourd. Le système de
   plaques n'a qu'un seul bouton par emplacement — `--plate-h`, la hauteur
   de RÉFÉRENCE du contenu — et chaque marque garde son coefficient optique
   `--k`. On ne touche donc qu'aux quatre hauteurs, et les rapports entre
   marques restent ceux qui ont été calibrés.

   Environ +35 % partout, sauf dans le hero où l'on se limite à +30 % : le
   billet du premier écran est juste en dessous, et c'est lui qui doit
   gagner le regard.

   ⚠ Renault, Prismatic, Publicis et Alstom ne sont PAS des partenaires
     confirmés — la CCI l'a écrit le 11/08 (voir Docs/DECISIONS-CCI-2026-08-11.md).
     Ils sont ici comme références visuelles, en attendant la vraie liste et
     le classement Premium / standard. À remplacer avant la mise en ligne.  */
.thanks__logos .brandmark[data-sponsor]{--plate-h:clamp(52px,6vw,74px)}
.scard .logo[data-sponsor]{--plate-h:clamp(58px,6.6vw,84px)}
.sponsors__brandsrow .logo[data-sponsor]{--plate-h:clamp(44px,4.9vw,60px)}
.presented__logos a[data-sponsor]{--plate-h:clamp(40px,4.3vw,54px)}

/* Le hero a sa propre échelle, redéfinie quatre fois dans le fichier au fil
   des passes : on la reprend au même endroit pour chacune, sinon la
   dernière écrase les autres et l'écart entre tailles d'écran disparaît. */
.hero .presented__logos a[data-sponsor]{--plate-h:clamp(50px,5.5vw,70px)}
@media(max-width:900px){
  /* ⚠ CE CLAMP ÉTAIT MORT, ET IL EN AVAIT L'AIR VIVANT. `4.4vw` ne dépasse
     42 px qu'au-delà de 954 px de large — c'est-à-dire nulle part dans la
     plage que cette requête gouverne. Sur les 260 px qui séparent 641 de
     900, la plaque valait donc 42 px FIXE, puis sautait à 50 au passage de
     900. Le terme fluide ne servait à rien : trois valeurs pour un seul
     nombre.
     Le nouveau terme monte réellement de 46 à 50 et rejoint la règle du
     dessus exactement à 900 px, sans marche. Vérifié : 641→46,0 · 800→48,5
     · 900→50,0, contre 50,0 pour la règle bureau au même point. */
  .hero .presented__logos a[data-sponsor]{--plate-h:clamp(46px,calc(36.1px + 1.55vw),50px)}

  /* ⚠ L'ÉCART SUIT LA PLAQUE, SINON UN LOGO PART SEUL À LA LIGNE.
     Les sept plaques tiennent largement — mesuré à 641 px : 490 px de
     logos dans 583 px de rangée. Ce qui débordait, c'est l'écart de 24 px
     hérité du bureau : six intervalles réclamaient 144 px là où il n'en
     restait que 94. Alstom se retrouvait seul sur une seconde ligne,
     centré — ce qui se lit comme un oubli, pas comme une mise en page.
     Le budget réel par intervalle va de 15,6 px à 641 à 24 px à 900 ;
     ce clamp reste dessous sur toute la plage. */
  .hero .presented__logos{gap:clamp(14px,2.2vw,24px)}
}

/* ══════════════════════════════════════════════════════════════════════
   LES QUESTIONS DEVIENNENT UNE SECTION
   Elles vivaient dans la grille de Práctica, à côté des faits. Deux
   métiers, une seule forme : la donnée de référence qu'on balaie, et le
   traitement d'objection qu'on ne lit qu'en cas de doute.

   La structure vient de la référence citée (artefact-ai-film-festival.com) :
   une fente 1/3 – 2/3, le titre et la SORTIE à gauche, les questions à
   droite en UNE colonne. Ce qu'on ne lui prend pas, c'est l'aplat de
   couleur plein cadre : notre page a déjà deux surfaces inclinées, la
   verte d'Agenda et la bleue d'Embajadores, et une troisième les
   banaliserait. Le contraste passe donc par le RAIL — un bloc étroit en
   encre — pas par la page.

   ⚠ UNE seule colonne, et c'est la correction la plus importante : en
     deux colonnes, ouvrir une question à gauche décale toute la colonne
     de droite (le grid recalcule la hauteur de rangée) et le lecteur perd
     des yeux la question qu'il lisait.
   ══════════════════════════════════════════════════════════════════════ */
.faqs{background:var(--c-cream)}
.faqs__grid{display:grid;gap:var(--s-7)}

.faqs__rail{
  background:var(--c-ink);color:var(--c-cream);
  border-radius:var(--r-lg);
  padding:clamp(var(--s-5),3.4vw,var(--s-7));
  display:grid;gap:var(--s-3);align-content:start}
/* Le mot hérite sa couleur — il devient crème ici sans qu'une règle ait à
   le savoir. Il rétrécit en revanche : « DUDAS » à 90 px déborderait d'une
   colonne au tiers de la page. */
.faqs__rail .wordcut{--wc-size:clamp(2.4rem,4.4vw,3.6rem)}
.faqs__rail .label{color:var(--c-peach)}
.faqs__rail .wordcut-sub{color:var(--c-cream-2);max-width:22ch}
.faqs__rail .wordcut-sub em{color:var(--c-peach)}

.faqs__exit{margin-top:var(--s-5);padding-top:var(--s-5);
  border-top:1px solid var(--c-line-inv);display:grid;gap:var(--s-3);justify-items:start}
.faqs__exit-q{margin:0;font-family:var(--f-display);font-weight:800;
  font-variation-settings:'wdth' 105,'wght' 800;
  text-transform:uppercase;letter-spacing:-.01em;line-height:1.15;
  font-size:clamp(.95rem,1.5vw,1.1rem)}
/* Sur l'encre du rail, le bouton sombre disparaîtrait : il s'inverse. */
.faqs__rail .btn--dark{background:var(--c-cream);color:var(--c-ink);border-color:transparent}
.faqs__rail .btn--dark:hover{background:#fff;color:var(--c-ink)}

/* UNE colonne, et des lignes qui respirent : la question devient un objet,
   plus une ligne de liste. */
.faqs .faq{grid-template-columns:1fr;column-gap:0}
.faqs .faq__i:first-child{border-top:1px solid var(--c-line)}
.faqs .faq__i summary{padding:var(--s-5) 0;font-size:clamp(1.05rem,2vw,1.35rem)}
.faqs .faq__i p{max-width:68ch;padding-bottom:var(--s-6)}

@media(min-width:1000px){
  .faqs__grid{grid-template-columns:minmax(0,22rem) minmax(0,1fr);
    gap:clamp(2.5rem,5vw,5rem);align-items:start}
  /* Le rail tient l'écran pendant qu'on parcourt les questions : le titre
     ne doit pas défiler avec elles, sinon la section perd son nom au bout
     de trois ouvertures. */
  .faqs__rail{position:sticky;top:calc(var(--header-h,72px) + var(--s-5))}
}

/* Práctica : le fait qui DÉBLOQUE la venue cesse d'avoir le même poids que
   les trois autres. Quatre colonnes identiques donnaient le même rang à une
   promesse (« entrada libre ») et à un trou (« líneas por confirmar »).

   ⚠ 1ʳᵉ TENTATIVE, CORRIGÉE LE MÊME JOUR. On avait porté ce seul fait à 44 px
   en laissant les deux autres à 17 : ça n'a pas rendu la section attirante, ça
   a rendu UNE cellule bruyante. Grossir un élément est une insistance, pas une
   hiérarchie — le déséquilibre se voyait plus que le message.

   Ce qui manquait n'était pas une taille mais un OBJET : Práctica était la
   seule section de la page sans aplat coloré. Zonas a sa bande sombre, Programa
   sa bande verte, Embajadores son panneau incliné, Talleres ses billets. Trois
   cellules et des filets de 1 px se lisent comme un formulaire, quelle que soit
   la taille des mots qu'on y met.

   Voir le panneau `.practica__facts` en fin de fichier. */
.fact--key .fact__v{color:var(--c-coral);
  font-variation-settings:'wdth' 118,'wght' 900;
  line-height:1.05;letter-spacing:-.02em;text-wrap:balance}
.fact--key .fact__n{margin-top:var(--s-3)}

/* ⚠ L'ÉLARGISSEMENT DE LA COLONNE EST EN FIN DE FICHIER, pas ici : une règle
   `.practica__facts{grid-template-columns:repeat(3,1fr)}` suit plus bas, à
   spécificité égale, et gagnait par l'ordre. Mesuré : 389/389/389 alors que la
   ligne réclamait trois lignes de retour. */

/* Les deux boutons de Práctica étaient un 5ᵉ enfant de la grille de faits :
   ils atterrissaient dans la première colonne sur un quart de largeur, donc
   l'un sous l'autre. Depuis que la FAQ est partie, ça se voyait. Ils
   reprennent toute la largeur, sous le filet. */
.practica__cta{grid-column:1 / -1;margin-top:var(--s-5)}

/* ══════════════════════════════════════════════════════════════════════
   PRÁCTICA ABSORBE LE PLAN
   La section Plano disait deux fois ce que Zonas avait déjà dit, pour
   1 461 px, et promettait un moteur de recherche sans données. Ce qui
   reste — le schéma — rejoint les infos pratiques : « où c'est » et
   « comment y aller » sont la même question, elles étaient séparées par
   une frontière de section.

   Trois faits au lieu de quatre : « Cómo llegar » quitte la rangée, où il
   n'avait qu'un quart de largeur pour dire « líneas por confirmar », et
   prend sa vraie place à côté du plan, avec l'itinéraire.
   ══════════════════════════════════════════════════════════════════════ */
.practica__facts{grid-template-columns:repeat(3,1fr)}

.practica__map{display:grid;gap:var(--s-6);}
.practica__how{display:grid;gap:var(--s-2);align-content:start}
.practica__how-v{margin:0;font-family:var(--f-display);font-weight:900;
  font-variation-settings:'wdth' 112,'wght' 900;
  font-size:clamp(1.05rem,1.9vw,1.35rem);text-transform:uppercase;
  letter-spacing:-.01em;line-height:1.2}
.practica__how-n{margin:0;font-size:var(--t-small);color:var(--c-muted);line-height:1.5;max-width:32ch}
.practica__how .practica__cta{margin-top:var(--s-4)}

/* L'ADRESSE — `<address>` s'affiche en italique par défaut dans tous les
   navigateurs. Une adresse qu'on recopie dans une application de course se lit
   mieux droite, et le romain la distingue mieux de la note en gris juste en
   dessous. `font-style` explicite, donc, plutôt qu'un héritage subi. */
.practica__how-addr{margin:0 0 var(--s-2);font-style:normal;font-size:.875rem;
  line-height:1.5;color:var(--c-ink);max-width:32ch}

/* Le second intertitre (« Accesibilidad ») a besoin d'air au-dessus : sans
   lui, la grille `gap` le colle à la phrase d'orientation et les deux sujets
   n'en font plus qu'un. En deux colonnes il ouvre la sienne — l'air n'a plus
   lieu d'être, il désaligne les deux têtes de colonne. */
.practica__how-k{margin-top:var(--s-3)}
.practica__howcol{display:grid;gap:var(--s-2);align-content:start}
@media(min-width:1000px){
  /* ⚠ PAS DEUX COLONNES ÉGALES. Mesuré : « Cómo llegar » porte 4 lignes,
     « Accesibilidad » en porte 7 — et elles occupaient 593 px chacune. Or
     « comment je viens » est ce que cherchent TOUS les visiteurs, et l'accès
     ce que cherchent quelques-uns, mais complètement. Deux besoins de
     fréquences très différentes ne méritent pas le même poids : la première
     colonne prend l'avantage, la seconde reste entière. */
  .practica__how{grid-template-columns:1.25fr 1fr;gap:clamp(2rem,4vw,3.5rem);align-items:start}
  .practica__how-k{margin-top:0}
}

/* ⚠ RÈGLE RETIRÉE LE 18/08/2026. Elle donnait à `.practica__map` deux colonnes
   — 1.7fr pour le schéma, 1fr pour le texte de transport. Le schéma est parti
   dans le bloc `plano` : il ne restait qu'un enfant, donc la seconde colonne
   était un vide de plus de la moitié de la section sur grand écran. Les deux
   colonnes vivent désormais DANS `.practica__how`, où elles ont du contenu des
   deux côtés. */
@media(max-width:760px){
  .practica__facts{grid-template-columns:1fr}
}

/* ══════════════════════════════════════════════════════════════════════
   LE TITRE DE ZONAS RENTRE DANS LA COLONNE
   Constat de l'œil, confirmé à la mesure : « LAS ZONAS · UNIVERSOS » commence
   à x=30 quand tous les autres titres de section commencent à x=86. Un écart
   de 56 px — exactement la gouttière.

   La cause est une règle de positionnement, pas un réglage : `.zstack__head`
   est en `position:absolute` avec `left:0`, et le bloc contenant d'un élément
   absolu est la BOÎTE DE REMPLISSAGE de son ancêtre positionné. `left:0` se
   cale donc au bord extérieur de `.wrap`, AVANT son `padding-inline`. Tout ce
   qui est en flux normal dans la même boîte, lui, commence après. D'où un
   titre qui a l'air de sortir du gabarit — il en sort vraiment.
   ══════════════════════════════════════════════════════════════════════ */
.zstack__head{left:var(--gutter)}

/* ── DEUX FAMILLES DE TITRES, ET C'EST UNE RÈGLE ──────────────────────
   L'autre raison pour laquelle « UNIVERSOS » semblait étranger : il fait
   58 px quand Agenda, Talleres, Práctica, Embajadores et Aliados font 90.
   Ce n'était pas un accident — un titre à 90 px mesure ~520 px et la colonne
   du rail en fait 352 : il déborderait.

   Ce n'était pas une règle non plus, tant qu'il était le seul dans ce cas.
   Depuis que la FAQ a son rail, ils sont deux : on fixe donc la même valeur
   pour les deux, et l'exception devient un système —

     titre de section pleine largeur ............ --wc-size par défaut (90 px)
     titre de RAIL (colonne étroite, collante) ... --wc-rail

   Le jour où un troisième rail apparaît, il n'y a qu'un jeton à lire. */
:root{--wc-rail:clamp(2.2rem,4.3vw,3.6rem)}
.zstack__word,
.faqs__rail .wordcut{--wc-size:var(--wc-rail)}

/* ══════════════════════════════════════════════════════════════════════
   LA FRISE SE POSE SUR LE SOL
   Elle flottait. Mesuré : le hero fait 940 px, la frise s'arrête à 891 —
   49 px au-dessus de la limite de section — et cette fin n'est pas un
   fondu, c'est l'arête franche du `clip-path: inset(0 0 16% 0)` qui retire
   la bande sombre du fichier source. Les monuments étaient donc tranchés
   en l'air, sur une horizontale que rien n'expliquait, avec du crème en
   dessous jusqu'à la section suivante.

   Trois états sont possibles pour un décor en bas de hero — reposer sur la
   ligne, se dissoudre avant, ou être coupé. On était dans le troisième,
   le seul qui se lit comme un défaut de recadrage.

   On prend le premier : la frise DESCEND de la hauteur exacte que le clip
   lui retire, si bien que son arête vient coïncider avec le bas du hero.
   L'horizon devient un sol, les monuments s'y plantent, et le billet se
   tient dessus.

   `translateY` en pourcentage se calcule sur la hauteur de L'ÉLÉMENT —
   c'est ce qui rend la compensation exacte quelle que soit la largeur de
   l'écran. Un `bottom` en pourcentage, lui, se serait calculé sur le hero
   et aurait bougé avec lui. */
.hero__obj--near{
  bottom:0;
  transform:translateY(16%);
  /* ⚠ LE FONDU DU BAS DOIT MOURIR SUR LA LIGNE DU CLIP, PAS APRÈS.
     Première tentative : je l'avais supprimé, en pensant que l'arête du
     clip ferait un sol. À l'œil, non — une horizontale franche en travers
     d'une grande roue et d'un pavillon ne se lit pas comme un horizon,
     elle se lit comme un recadrage raté. Et pour cause : le fichier source
     ne CONTIENT pas de sol, il est coupé par sa propre bande sombre.

     Deuxième tentative, la bonne : le fondu revient, mais il atteint zéro
     à 84 % — exactement là où `clip-path: inset(0 0 16% 0)` tranche. La
     frise se dissout donc avant sa propre coupe : on ne voit plus jamais
     l'arête, et le décor s'évanouit dans le crème au bas du hero.

     Les 84 % ne sont pas un réglage à l'œil : c'est 100 % − 16 %, la
     valeur du clip. Si le clip bouge, cette valeur doit bouger avec. */
  -webkit-mask-image:
    linear-gradient(to right,  transparent 0, #000 12%, #000 88%, transparent 100%),
    linear-gradient(to bottom, transparent 0, #000 22%, #000 62%, transparent 84%);
  mask-image:
    linear-gradient(to right,  transparent 0, #000 12%, #000 88%, transparent 100%),
    linear-gradient(to bottom, transparent 0, #000 22%, #000 62%, transparent 84%)}

/* ══════════════════════════════════════════════════════════════════════
   LA TYPO DE FOND CESSE D'ÊTRE TRANCHÉE
   Mesuré : « MÉXICO · FRANCIA · MÉXICO · FRANCIA » occupe 3 830 px dans une
   fenêtre de 1 500 — 1 183 px de débord de chaque côté. On n'en voyait donc
   jamais un motif complet, mais un fragment arbitraire : « RANCIA · MÉXICO »,
   coupé net en pleine lettre aux deux bords. Et le fragment CHANGE avec la
   largeur d'écran : personne ne voit la même chose.

   Une texture peut déborder — c'est même ce qui lui donne son échelle. Ce
   qu'elle ne peut pas, c'est se faire couper au rasoir sur une arête franche :
   l'œil lit une erreur de recadrage, pas une matière.

   Deux réglages, et un seul principe : ce qui déborde doit se DISSOUDRE.

   ① le corps descend pour qu'un motif entier — « MÉXICO · FRANCIA » — tienne
      dans l'écran. À 7,4vw il mesure ~1 290 px sur 1 500 : on lit le message
      en entier, au centre, à toutes les largeurs usuelles ;
   ② les extrémités s'effacent en fondu. Les répétitions qui sortent encore
      s'évanouissent au lieu d'être sciées, et la bande se lit comme un
      motif continu qui passe derrière le panneau.
   ══════════════════════════════════════════════════════════════════════ */
.tiltp__texture{
  font-size:clamp(2.6rem,7.4vw,7.2rem);
  -webkit-mask-image:linear-gradient(to right,
    transparent 0, #000 15%, #000 85%, transparent 100%);
  mask-image:linear-gradient(to right,
    transparent 0, #000 15%, #000 85%, transparent 100%)}

/* ── LA TYPO DE FOND DESCEND SOUS LE CONTENU ──────────────────────────
   Mesuré : elle mordait de 44 px sur l'emblème et de 111 px sur le bloc
   « En 1826 ». Une texture qui passe DERRIÈRE du texte le rend illisible
   même à 10 % d'opacité — les contreformes des capitales font des trous
   dans les lignes.

   Deux gestes, parce qu'un seul ne suffisait pas : il ne restait que 66 px
   sous elle avant le bas du panneau, donc la descendre ne dégageait que la
   moitié du chevauchement.

     ① elle se cale au plancher du panneau ;
     ② le contenu gagne la respiration qui manquait — et comme la typo est
        ancrée au BAS, ce qu'on ajoute en bas de contenu l'éloigne d'autant
        du texte. Le même réglage sert deux fois.                          */
.tiltp__texture{bottom:.75rem}
/* ⚠ Seulement en BAS. La marge haute est portée par la section elle-même
   depuis le passage en bande (`padding-block` sur `.tiltp`) : la remettre
   ici l'ajoutait une seconde fois — 244 px de vide et une section qui
   passait de 1 443 à 1 847 px pour dégager 57 px de texte. */
@media(min-width:1100px){
  .tiltp__content{padding-block:0 clamp(5rem,9vh,8rem)}
}

/* ══════════════════════════════════════════════════════════════════════
   LA MÊME RÈGLE POUR LES DEUX BANDES
   Ce qui a été corrigé sur la bande France vaut à l'identique sur celle du
   Mexique — et le défaut y était plus grave : « AGENDA · AGENDA · AGENDA »
   mesurait 5 675 px dans une fenêtre de 1 500 (2 088 px de débord par côté)
   et passait 338 px DERRIÈRE le titre et les onglets de jour.

   La règle, maintenant écrite pour les deux :

     ① un motif complet doit tenir dans l'écran — sinon on ne lit qu'un
        fragment, et il change d'un visiteur à l'autre ;
     ② ce qui déborde se DISSOUT — un fondu aux deux bouts, jamais une
        coupe franche ;
     ③ la typo ne passe jamais derrière du texte. Même à 10 %, les
        contreformes des capitales trouent les lignes.

   Le miroir des deux bandes est préservé : côté France le feston est en
   haut et la typo en bas ; côté Mexique le papel picado est en bas, la
   typo reste donc en haut. Elles se calent au même écart de leur bord —
   .75rem — et partagent désormais le même corps.
   ══════════════════════════════════════════════════════════════════════ */
.agenda__texture{
  top:.75rem;bottom:auto;
  font-size:clamp(2.6rem,7.4vw,7.2rem);
  -webkit-mask-image:linear-gradient(to right,
    transparent 0, #000 15%, #000 85%, transparent 100%);
  mask-image:linear-gradient(to right,
    transparent 0, #000 15%, #000 85%, transparent 100%)}

/* Le contenu descend sous la typo. Côté France c'est le bas qui gagnait de
   la marge, ici c'est le haut : les deux bandes s'écartent de leur mot de
   fond du même geste, dans le sens qui leur est propre. */
.agenda > .wrap{padding-top:clamp(3rem,6vh,5rem)}


/* ── LE MOT TIENT DANS L'ÉCRAN, DONC IL N'Y A PLUS RIEN À FONDRE ───────
   Le fondu ne se voyait pas, et l'arithmétique dit pourquoi : il faisait
   15 % de L'ÉLÉMENT, or l'élément ne dépassait que de 234 px de chaque
   côté. Au bord de l'écran on était encore à 87 % d'opacité — la coupe
   restait franche. Élargir le fondu n'aurait fait que déplacer le problème :
   un masque posé sur l'élément ne sait pas où est le bord de la FENÊTRE.

   La bonne correction est en amont : le mot est raccourci pour tenir.
   « AGENDA · AGENDA » et « MÉXICO · FRANCIA » mesurent 87-88 % de la
   largeur de la fenêtre — et comme le corps est en `vw`, cette proportion
   ne bouge JAMAIS : le motif est entier à toutes les largeurs, sans
   masque, sans coupe.

   Le fondu reste, très court, pour un seul cas : sous ~700 px le corps
   bute sur son minimum de 2,6 rem et le mot redéborde. Là, il sert. */
.agenda__texture,
.tiltp__texture{
  -webkit-mask-image:linear-gradient(to right,
    transparent 0, #000 7%, #000 93%, transparent 100%);
  mask-image:linear-gradient(to right,
    transparent 0, #000 7%, #000 93%, transparent 100%)}

/* ══════════════════════════════════════════════════════════════════════
   TROIS CORRECTIONS MOBILES ET UNE DE COHÉRENCE
   ══════════════════════════════════════════════════════════════════════ */

/* ── ① LE BILLET NE S'EFFACE PLUS AVANT D'AVOIR ÉTÉ LU ────────────────
   Mesuré à 390 px : `.hero__inner` passe de 1 à 0,54 d'opacité entre 0 et
   600 px de défilement (repli GSAP, main.js). Sur desktop c'est juste — tout
   le hero est visible d'un coup, le repli accompagne la sortie. Sur mobile le
   hero est plus haut que l'écran : le billet n'apparaît QU'APRÈS avoir
   défilé, c'est-à-dire précisément quand l'effacement a commencé. On lui
   demande de lire un objet qu'on est en train de faire disparaître.

   Le repli garde donc son déplacement — il reste utile — mais il ne touche
   plus à l'opacité sous 900 px. `!important` parce que GSAP écrit en style
   inline, et c'est le seul moyen de le contredire sans toucher au tween
   partagé avec le desktop. */
@media(max-width:900px){
  .hero__inner{opacity:1!important}
}

/* ── ② LE RAIL DE LA FAQ RENTRE DANS LA COLONNE, SUR MOBILE AUSSI ─────
   Même défaut que le titre de Zonas, autre cause : ici le rail est une
   carte à fond encre, et son padding intérieur décale son libellé de 24 px
   vers la droite. Mesuré : tous les libellés de section commencent à x=20,
   celui de la FAQ à x=44.
   Sous 1000 px la carte n'apporte plus rien — elle n'a pas de colonne à
   tenir — donc elle redevient un bloc de texte aligné comme les autres. */
@media(max-width:999px){
  .faqs__rail{background:none;padding:0;border-radius:0}
  .faqs__rail .label{color:var(--c-rojo-txt)}
  .faqs__rail .wordcut,
  .faqs__rail .faqs__exit-q{color:var(--c-ink)}
  .faqs__rail .wordcut-sub{color:var(--c-muted)}
  .faqs__rail .wordcut-sub em{color:var(--c-rojo-txt)}
  .faqs__exit{border-top-color:var(--c-line)}
  .faqs__rail .btn--dark{background:var(--c-ink);color:var(--c-cream)}
  .faqs__rail .btn--dark:hover{background:var(--c-ink-3);color:var(--c-cream)}
}

/* ── ③ LE BOUTON DU PIED DE PAGE REJOINT LA FAMILLE ───────────────────
   « Reservar mi taller » est le MÊME acte que « Reservar un taller » du
   hero et que celui du dock. Il en avait pourtant l'apparence inverse :
   crème sur bleu (`.btn-glow--solid`), quand la règle du site — écrite
   dans le fichier — réserve la PILULE ROUGE PLEINE à « reservar ».

   Cette intention est désormais portée par la VARIANTE STANDARD et non
   plus par un jeu de règles à part : `.btn--primary` EST la pilule rouge
   pleine, et `.footer .btn--primary` lui ajoute déjà le liseré crème de
   contraste. « Suscríbete al boletín » garde sa forme creuse en
   `.btn--ghost` — pas la même action, et deux pilules pleines côte à côte
   se disputeraient le regard.

   Les trois règles qui vivaient ici ne faisaient que réécrire ce que la
   variante donne gratuitement. Supprimées avec `.btn-glow`.             */

/* Sur le bleu du pied de page, le bouton creux s'inverse — même geste
   qu'en `.hz` : liseré et texte crème, remplissage crème au survol. */
.footer .btn--ghost{color:var(--c-cream);
  box-shadow:inset 0 0 0 1.5px var(--c-line-inv);--btn-fill:var(--c-cream)}
.footer .btn--ghost:hover,
.footer .btn--ghost:focus-visible{color:var(--cream)}
.footer .btn--ghost span {
    padding: 16px 0px;
}

/* ── LA GUIRLANDE ─────────────────────────────────────────────────────
   Elle se réglait ici, en correctif, alors que sa géométrie est celle de
   la bande. Deux passes plus loin dans le fichier, on ne voyait plus que
   la moitié du dispositif — et c'est comme ça qu'on a levé `overflow` en
   croyant lever la découpe.

   Tout est remonté à sa source : `.agenda__frame` / `.agenda__panel` /
   `.agenda__frame > .picado`, en un seul endroit. Rien à régler ici.     */

/* ══════════════════════════════════════════════════════════════════════
   MOBILE — « INFO PRÁCTICA » DEVIENT UNE FEUILLE, PAS UN ÉCRAN
   Constat : le panneau prenait TOUT l'écran pour six liens, laissait la
   moitié basse vide, et faisait disparaître « Reservar taller » — on perdait
   la seule conversion du site pendant qu'on consultait un horaire.

   Le commentaire d'origine disait « il n'y a plus de place pour le CTA, le
   masquer reste juste ». C'était vrai tant que le panneau montait jusqu'en
   haut. Il n'a aucune raison de le faire : six liens tiennent dans les deux
   tiers bas de l'écran. On garde donc le contenu ET le bouton.

   C'est aussi le geste que les gens connaissent — une feuille qui monte du
   bas, comme les partages iOS ou les fiches Google Maps.
   ══════════════════════════════════════════════════════════════════════ */
@media(max-width:760px){
  .practica-open .ov__practica{
    top:auto;height:auto;max-height:64svh;overflow-y:auto;
    border-radius:var(--r-lg) var(--r-lg) 0 0;
    box-shadow:0 -18px 40px -18px rgba(10,37,64,.35)}
  /* Le CTA reste : c'est la seule conversion de la page. */
  .practica-open .dock__cta{display:flex}
  /* (Le rembourrage de la liste de six liens vivait ici. La liste n'existe
     plus — la feuille porte les faits, et chaque bloc porte son propre
     rembourrage.) */
}

/* (Le rattrapage de couleur du liseré du pied de page vivait ici. Il n'a
   plus d'objet : `.btn > .glow path` prend `stroke:currentColor`, donc le
   liseré est blanc sur la pilule rouge et crème sur le bouton creux, sans
   qu'aucune valeur n'ait à être redite.) */

/* ══════════════════════════════════════════════════════════════════════
   LE PROGRAMME NATIONAL
   Placée APRÈS Embajadores : le lecteur grand public a déjà tout eu — le
   programme, les ateliers, le plan, les infos, les réponses. Ce bloc ne
   dispute donc rien à la conversion ; il donne une issue au visiteur
   professionnel, qui jusqu'ici arrivait sur un site portant le logo de la
   Semana de Francia sans jamais trouver la Semana de Francia.

   Trois colonnes = trois villes. Chaque volet est une ligne, sans jour :
   seules NOS dates sont écrites, parce que ce sont les seules qu'on tienne.
   ══════════════════════════════════════════════════════════════════════ */
/* ⚠ Le jeu de règles qui vivait ici (`__list`, `__city`, `__place`, `__when`,
   `__events`, `__note`) est mort depuis la compression de la section : le
   gabarit n'émet plus ces classes. Trente lignes de CSS qu'on croyait actives
   — c'est le même piège que la guirlande d'Agenda, et il a coûté aussi cher :
   la grille 3 colonnes qu'on a fini par réécrire plus bas était DÉJÀ ici.
   Supprimé. Tout le bloc vit désormais en un seul endroit, plus bas.        */

/* ── LE MOT DÉCOUPÉ DU MANIFESTO, SUR MOBILE ──────────────────────────
   Mesuré à 390 px : le bloc du titre ne faisait que 171 px de large dans un
   conteneur de 350 — parce que `.wordcut{width:fit-content}` le calibre sur
   sa ligne la plus large, « CELE ». La composition en escalier
   (`--offset` aligne la 2ᵉ ligne à droite) devenait alors invisible : les
   trois lignes se superposaient dans la même colonne étroite, calées à
   gauche. D'où l'impression d'un titre mal posé.

   Sur mobile il prend toute la largeur, et le corps monte pour la remplir :
   l'escalier redevient lisible, et le mot pèse ce qu'un titre doit peser. */
@media(max-width:760px){
  .mf__word{--wc-size:clamp(3.6rem,29vw,8.5rem);width:100%}
}

/* (`.semana__here` supprimé avec le reste du bloc mort — plus émis.) */

/* ══════════════════════════════════════════════════════════════════════
   ① LA FRISE REVIENT DANS LE HERO MOBILE
   Constat : sur 390 px, le décor est INVISIBLE — 105 px de frise en bas
   d'un hero de 1 302, sous la ligne de flottaison et derrière le billet.
   Le premier écran mobile est un aplat crème.

   La cause n'est pas le réglage mobile, qui demandait pourtant `width:150%`
   pour élargir la frise : c'est le `img{max-width:100%}` du preflight
   Tailwind, qui la rabote silencieusement à 100 %. La règle mobile croyait
   agir depuis le début et n'a jamais rien fait.

   On lève le plafond, on élargit vraiment, et on remonte l'opacité : sur un
   petit écran une silhouette à 0,6 disparaît, alors qu'à 0,85 elle donne un
   fond au titre sans jamais lui disputer la lecture.                      */
@media(max-width:900px){
  .hero__obj--near{
    max-width:none;width:175%;left:-38%;opacity:.85;
    /* Élargie, elle restait invisible : le hero mobile fait 1 302 px pour un
       écran de 844, et une frise calée sur son plancher tombe 470 px sous la
       ligne de flottaison. Elle remonte au quart bas — derrière la rangée de
       logos et le haut du billet, exactement le rôle qu'elle joue en desktop.
       La compensation de `translateY` tombe avec : il n'y a plus de sol de
       section à rejoindre ici. */
    bottom:20%;transform:none}
}

/* ══════════════════════════════════════════════════════════════════════
   ② LE MOT DU MANIFESTO REPASSE SOUS LE TITRE DU HERO
   Mesuré : 136 px contre 87 pour le `<h1>` sur desktop (+56 %), et 113
   contre 38 sur mobile — soit TROIS FOIS le titre de la page. Ce n'est
   plus un accent éditorial, c'est une inversion de hiérarchie : l'élément
   le plus gros du site n'est plus sa promesse.

   Le mot découpé reste un objet graphique, donc il garde le dessus sur le
   texte courant — mais il passe SOUS le `<h1>`. On baisse le mot plutôt que
   de monter le titre : le hero est une composition serrée (titre, chapô,
   tags, CTA, preuve, logos, billet), y toucher déplacerait le billet.     */
.mf__word{--wc-size:clamp(2.6rem,7.4vw,6.6rem)}
@media(max-width:760px){
  .mf__word{--wc-size:clamp(2.6rem,18vw,4.6rem)}
}

/* ══════════════════════════════════════════════════════════════════════
   ③ LE PASSAGE DE « SEMANA » À « ALIADOS »
   Deux sections crème qui se suivent, toutes deux institutionnelles, sans
   rien entre elles : le lecteur ne sait pas qu'il change de sujet. Les
   autres jointures du site sont marquées par un changement de sol (bande
   verte, bande bleue) — ici il n'y en a pas, et on n'en ajoute pas une
   troisième.

   ⚠ RÉSOLU AUTREMENT — le `flagrule` posé ici est SUPPRIMÉ.
   Il ne pouvait pas tenir, pour deux raisons mesurables :

     · deux de ses cinq bandes étaient BLANCHES, sur `--c-cream` (#F7F1E4).
       Soit 1,05:1 : invisibles. Le drapeau se lisait comme trois tirets
       détachés, pas comme une barre ;
     · il était pleine largeur quand tout le contenu de la section est dans
       `.wrap` : il ne s'alignait sur rien.

   Mais surtout, le diagnostic du haut de ce commentaire était juste et sa
   conclusion l'a trahi : « les autres jointures sont marquées par un
   changement de sol… et on n'en ajoute pas une troisième ». On refusait le
   remède en ayant identifié la maladie. Ce refus se comprenait quand la
   section faisait 793 px — une quatrième bande de cette taille aurait mangé
   la respiration crème. Elle en fait 250 : ce n'est plus un chapitre, c'est
   un panneau indicateur, et un panneau indicateur a un fond.

   La section prend donc le sol. Voir le bloc « BANDEAU NATIONAL » plus bas :
   il n'y a plus de séparateur parce qu'il n'y a plus rien à séparer.       */

/* ── LE BANDEAU DÉFILANT REÇOIT SES LOGOS ─────────────────────────────
   La mécanique existait depuis le début — `[data-marquee="-1"]`, boucle
   GSAP, pause au survol, et une accélération à la vitesse du défilement —
   mais elle tournait sur 24 emplacements VIDES en pointillés.

   C'est exactement le dispositif que la CCI a décrit : un bandeau en bas de
   site, tous sponsors confondus. Il est garni des six marques actuelles,
   répétées quatre fois pour qu'aucun trou n'apparaisse jamais dans la
   boucle. Le jour où la vraie liste arrive, c'est la boucle `foreach` du
   plugin qui remplace ces six-là — la mécanique ne bouge pas.

   ⚠ Le niveau « Premium » se traduira ICI par la hauteur de plaque : les
     jetons existent déjà (`--logo-n1/n2/n3`). Premium = n1, standard = n3.  */
/* ⚠ DEUX RÉGLAGES GOUVERNENT CE LOGO, ET LE PLUS PETIT GAGNE. Cette
   variable fixe la hauteur VOULUE (`height:max(15px, --plate-h * --k)`,
   plus haut) ; la plaque de la rangée défilante, plus bas, fixe un cadre à
   hauteur FIXE dont `max-height:100%` fait un PLAFOND. Régler l'un sans
   l'autre ne donne rien : monter cette variable seule laisse le plafond
   rabattre l'image, monter le cadre seul laisse cette variable la retenir.
   Les deux ont été accordés le 11/09/2026 — la valeur ci-dessous et celle
   du bloc « rangée défilante » se lisent ensemble. */
.thanks__brandsrow .logo[data-sponsor]{--plate-h:clamp(42px,3.4vw,46px)}
/* ⚠ LE PLANCHER À 34 px LAISSAIT LA PLAQUE À MOITIÉ VIDE SOUS 900 px. Le
   cadre y offre 48 px de contenu (60 de plaque moins 2×5,6 de padding) et
   cette variable n'en réclamait que 34 : vingt-six pixels de blanc autour
   d'un logo déjà petit. Le plancher suit maintenant ce que le cadre donne
   vraiment — 42 px partout, 46 au-delà de 1350. */
.thanks__brandsrow{align-items:center}

/* ══════════════════════════════════════════════════════════════════════
   LE BANDEAU « PROGRAMME NATIONAL »
   Il remplace la section précédente : 793 px, un titre de 90 px et deux
   colonnes vides pour trois villes et trois mois. Un panneau indicateur se
   lit d'un coup — il n'a pas besoin d'un chapitre, il a besoin d'une ligne.
   ══════════════════════════════════════════════════════════════════════ */
/* ── ① LE SOL RESTE CRÈME — ET C'EST L'ORDRE DES SECTIONS QUI L'IMPOSE
   J'ai essayé --c-ink ici : le token était libre, mais le VOISINAGE ne
   l'était pas. L'ordre réel est `#embajadores` (bande colorée, arête
   inclinée) → `semana` → `#patrocinadores` (crème). Une bande sombre à cet
   endroit met deux sombres à la file, séparés par 45 px de crème : ça ne
   fait pas un rythme, ça fait un accident.

   Le rythme inscrit plus haut dans ce fichier — « embajadores · alliés ·
   footer » — ne mentionne pas `semana`. C'est là le vrai statut de cette
   section : elle n'est pas un chapitre, c'est le PALIER entre le dernier
   bloc coloré et le bloc institutionnel. Un palier se traverse ; il ne
   change pas de sol.

   Donc pas de fond, pas de filet : c'est la FORME qui sépare (② et ③).
   Le titre géant d'ALIADOS juste après fait déjà, à lui seul, une ouverture
   de chapitre que rien n'a besoin d'annoncer. */
.semana{background:var(--c-cream);color:var(--c-ink);
  padding-block:clamp(3rem,7vh,5rem)}
.semana .label{color:var(--c-rojo-txt)}

/* ── ② LE TITRE ET LA SORTIE SE PARTAGENT LA LIGNE ────────────────────
   Le lien était sous les villes, et la moitié droite de la section restait
   vide sur toute sa hauteur. Sujet à gauche, issue à droite : la largeur
   est occupée, et l'œil va de l'un à l'autre sans redescendre. */
/* Empilé (mobile), le lien passe SOUS les villes : c'est donc le bloc
   entier qui porte le filet de fermeture, sinon le bouton resterait
   orphelin de l'autre côté. En trois colonnes le lien remonte sur la ligne
   du titre, et le filet repasse sur les villes elles-mêmes — voir ④. */
.semana__bar{display:grid;gap:var(--s-5);
  padding-bottom:var(--s-5);
  border-bottom:2px solid var(--c-line)}
.semana__h{margin:0;max-width:44ch;color:var(--c-ink);
  font-family:var(--f-display);font-weight:800;
  font-variation-settings:'wdth' 105,'wght' 800;
  text-transform:uppercase;letter-spacing:-.015em;line-height:1.16;
  font-size:clamp(1.05rem,1.9vw,1.4rem)}
.semana__cta{margin:0}

/* ── ③ TROIS VILLES, TROIS PARTS ÉGALES ───────────────────────────────
   En `flex` avec un `gap`, les trois villes s'entassaient à gauche et
   s'arrêtaient au tiers de la largeur ; le badge élargissait CDMX, si bien
   qu'on lisait « une grosse ville et deux restes » au lieu d'une série.

   Trois colonnes de même part, séparées d'un filet. La série redevient une
   série — et comme les colonnes suivent les mois, la chronologie que le
   titre annonce (octobre → décembre) se lit enfin dans la mise en page au
   lieu d'être seulement écrite. */
.semana__cities{list-style:none;margin:0;padding:var(--s-5) 0 0;
  border-top:1px solid var(--c-line);
  display:grid;gap:var(--s-4)}
.semana__cities li{display:flex;flex-wrap:wrap;align-items:baseline;
  gap:var(--s-2);font-size:var(--t-small);color:var(--c-muted)}
.semana__cities b{font-family:var(--f-display);font-weight:900;
  font-variation-settings:'wdth' 110,'wght' 900;
  text-transform:uppercase;letter-spacing:-.01em;color:var(--c-ink);
  font-size:clamp(.95rem,1.5vw,1.05rem)}
/* Le rouge plein ne sert qu'une fois par écran. Ici il dit « nous ». */
.semana__cities .semana__badge{font-style:normal;font-size:.625rem;
  letter-spacing:var(--ls-label);text-transform:uppercase;font-weight:800;
  color:#fff;background:var(--c-rojo);border-radius:var(--r-pill);
  padding:3px 9px;white-space:nowrap}

/* Placement explicite : en flux automatique, `cities` (2ᵉ dans le DOM)
   irait occuper la colonne de droite et le lien passerait sous le titre. */
@media(min-width:860px){
  .semana__bar{grid-template-columns:minmax(0,1fr) auto;align-items:end}
  .semana__intro{grid-area:1 / 1}
  .semana__cta{grid-area:1 / 2;justify-self:end}
  .semana__cities{grid-area:2 / 1 / 3 / -1;
    grid-template-columns:repeat(3,minmax(0,1fr));gap:0}
  .semana__cities li + li{border-left:1px solid var(--c-line);
    padding-left:clamp(1.25rem,2.5vw,2.5rem)}

  /* ── ④ LE FILET DE PIED, QUI FERME ET QUI DIT ─────────────────────
     Il manquait une frontière EN BAS, et là seulement : au-dessus, l'arête
     inclinée de la bande `#embajadores` en fait déjà une.

     Le tricolore pleine largeur essayé avant échouait pour deux raisons
     qu'on peut lever ensemble : il ne s'alignait sur rien, et deux de ses
     cinq bandes étaient blanches sur crème, donc absentes.

     Ici le filet est porté par les trois colonnes elles-mêmes : trois
     segments = trois villes = trois mois. Il ferme la section ET il mesure
     l'« octubre a diciembre » du titre. Celui de CDMX est plein et rouge —
     c'est notre mois, et le rouge ne sert qu'une fois par écran. Les deux
     autres restent en filet : à venir, pas encore à nous. */
  .semana__bar{padding-bottom:0;border-bottom:0}
  .semana__cities li{padding-bottom:var(--s-5);
    border-bottom:2px solid var(--c-line)}
  .semana__cities li:first-child{border-bottom-color:var(--c-rojo)}
}

/* ══════════════════════════════════════════════════════════════════════
   LA GUIRLANDE PEND, ELLE NE FLOTTE PLUS
   Elle était posée à l'intérieur du vert, collée au bord : géométriquement
   juste (0 px d'écart, mesuré) mais faux visuellement — une guirlande
   s'accroche à une arête et PEND en dessous. Elle enjambe donc le bord :
   40 % dans le vert, 60 % au-dessus du crème.

   ⚠ CE QUI L'EN EMPÊCHAIT N'ÉTAIT PAS `overflow:hidden`.
   On l'a cru, et on l'a levé — sans effet visible. Le panneau porte DEUX
   découpes indépendantes, `overflow` et `clip-path`, et c'est la seconde
   qui coupait : elle s'applique même en `overflow:visible`. La guirlande
   perdait 58 % de sa hauteur, c'est-à-dire la totalité de ses pointes.

   Le vrai correctif est en amont (`.agenda__frame`) : la guirlande sort de
   l'élément découpé tout en gardant son angle. Il ne reste ici que la marge
   basse de la section, qui elle est bien utile — sans elle le coin bas du
   panneau incliné viendrait sur le texte.

   ⚠ ÉLARGIE LE 19/08/2026 : ELLE NE COUVRAIT QUE LE COIN, PAS LA GUIRLANDE.
   Elle valait `clamp(5rem,9vw,7rem)` — 112 px à 1440. Or le papel picado pend
   140 px au-dessus du bas de section : la marge laissait donc la guirlande
   retomber DANS le texte. Personne ne le voyait parce que la note « Programa
   preliminar » fermait chaque journée et repoussait la dernière ligne. Le jour
   où la CCI a coché « Programme final », la note a disparu et le chevauchement
   est apparu : 28 px à 1440, 14 px à 390, mesurés.

   Un espacement qui dépend d'un élément FACULTATIF n'est pas un espacement.
   Il est désormais exprimé à partir du retrait du cadre, auquel s'ajoute la
   part pendante de la guirlande. */
.agenda{padding-bottom:calc(var(--ag-inset-b) + 5rem)}

/* ══════════════════════════════════════════════════════════════════════
   SUPERPOSITION · EXPÉRIENCE TÉLÉPHONE
   Constaté à 390 px, panneaux ouverts et transitions terminées :

     · `.ov` gardait ses marges de 32 px en haut ET en bas. Sur un
       ordinateur, ces marges font lire les panneaux comme deux cartons
       posés sur la page. Sur un téléphone, elles laissent deux bandes de
       page voilée aux deux bords, et surtout le panneau blanc — sorti par
       `translateY(100%)` — ne descend que jusqu'à la marge basse : son
       bord haut reste à l'écran sous le panneau bleu. C'était le liseré
       crème collé en bas de l'écran, menu ouvert.
     · le panneau blanc n'avait ni titre ni croix. Sa seule sortie était le
       socle « Info práctica » du dock, qui se retire tout seul dans
       #talleres et dans le pied de page (`.dock.is-redundant`).
     · aucun sélecteur de langue sous 960 px. ⚠ Corrigé à l'époque par un
       second exemplaire dans le menu, RETIRÉ depuis le 18/08/2026 : la
       bascule n'est plus publique, le français viendra par WPML.

   Sur téléphone, un panneau prend TOUT l'écran, porte son nom et sa
   sortie. Les marges et les arrondis restent au-dessus de 760 px, où ils
   veulent dire quelque chose.
   ══════════════════════════════════════════════════════════════════════ */

/* ══════════════════════════════════════════════════════════════════════
   LE VOILE NE MANGE PLUS LES CLICS QU'IL NE DOIT PLUS MANGER

   1 · LA FENÊTRE MORTE DE 760 ms.
       `.ov` reste dans le flux le temps de l'animation de sortie — il le
       faut, sinon la sortie est coupée net. Mais il gardait ses clics
       pendant tout ce temps : on tape le voile pour fermer, on tape
       aussitôt « Reservar », et ce second geste part dans le vide. Mesuré
       à T+300 ms : `elementFromPoint` sur un bouton d'atelier renvoyait
       encore `DIV.ov is-closing`. C'est ça, « le bouton ne répond plus ».
       Dès que la fermeture commence, l'ensemble devient transparent aux
       clics — les panneaux, eux, sont déjà neutralisés par leurs propres
       règles `pointer-events` juste au-dessus.

   2 · LE PANNEAU BLANC N'EST PLUS MODAL — À AUCUNE TAILLE.
       Sur 1380 px il occupe 420 px : bloquer les 960 restants derrière
       un voile n'a aucun sens.

       Et sur téléphone non plus, contrairement à ce que j'avais d'abord
       tranché. La feuille s'arrête aux trois quarts : au-dessus d'elle
       restent DEUX billets entiers, avec leur « Reservar » bien lisible.
       Du contenu visible qui ne répond pas se lit comme une panne, pas
       comme une protection — l'argument du « tap accidentel sur ce qu'on
       ne voit pas » ne tient que pour un panneau qui couvre tout, et
       celui-ci n'a jamais couvert tout.

       Il devient donc un tiroir dans les deux cas : voile allégé et
       traversant, ombre portée sur le panneau pour qu'il reste un plan
       distinct. On réserve un atelier en gardant l'adresse ouverte —
       ce que le commentaire d'origine décrivait déjà comme l'intention
       (« le pratique reste ouvert »). Fermeture : la croix, Échap, ou la
       barre.

       Le menu bleu, lui, reste modal : il couvre l'écran entier, il n'y
       a rien derrière avec quoi interagir.
   ══════════════════════════════════════════════════════════════════════ */
.ov.is-closing{pointer-events:none}

.ov.is-practica:not(.is-menu){pointer-events:none}
.ov.is-practica:not(.is-menu) .ov__practica{pointer-events:auto}
.ov.is-practica:not(.is-menu)::before{
  background:rgba(0,26,48,.14);backdrop-filter:none}
.ov.is-practica:not(.is-menu) .ov__practica{
  box-shadow:0 30px 60px -30px rgba(10,37,64,.55)}

/* ── Le panneau ne rembourre plus, ses blocs le font ────────────────────
   Il portait `padding:var(--s-7) var(--s-6)`. Avec un en-tête à filet et
   des blocs séparés par des filets, ce rembourrage extérieur empêchait
   les traits d'aller d'un bord à l'autre — un filet qui s'arrête à 24 px
   du bord n'est plus une séparation, c'est une décoration. Chaque bloc
   porte donc son propre rembourrage, les filets traversent. */
.ov__practica{padding:0}

/* ── En-tête de feuille · titre + sortie (toutes tailles) ───────────── */
.ov__sheethead{display:flex;align-items:center;justify-content:space-between;
  gap:var(--s-4);padding:var(--s-5);border-bottom:1px solid var(--c-line)}
.ov__sheettitle{font-family:var(--f-display);font-weight:900;
  font-variation-settings:'wdth' 112,'wght' 900;
  font-size:1.125rem;letter-spacing:.02em;text-transform:uppercase;
  color:var(--c-cci);margin:0}
.ov__sheetclose{flex:none;display:grid;place-items:center;
  width:44px;height:44px;border:1px solid var(--c-line);border-radius:50%;
  background:transparent;color:var(--c-ink);font-size:1rem;line-height:1;cursor:pointer;
  transition:background-color .3s var(--ease-out),color .3s var(--ease-out),
             border-color .3s var(--ease-out)}
.ov__sheetclose:hover,.ov__sheetclose:focus-visible{
  background:var(--c-ink);border-color:var(--c-ink);color:var(--c-cream)}

/* ── Les trois faits ────────────────────────────────────────────────────
   Un seul style de filet dans toute la feuille : une hairline pleine
   entre les blocs. Les pointillés sous chaque lien faisaient un second
   système de séparation, et le dernier d'entre eux ne séparait plus rien.

   La date passe en typo d'affiche. C'est la seule chose qu'on vient
   vérifier en premier, et c'est ce qui raccroche la feuille au reste du
   site — le même caractère que « 200 AÑOS » deux écrans plus haut. */
.ov__facts{display:grid;padding:0}
.ov__fact{display:grid;gap:2px;padding:var(--s-4) var(--s-5)}
.ov__fact + .ov__fact{border-top:1px solid var(--c-line)}
.ov__factk{margin:0 0 6px;
  font-size:var(--t-label);letter-spacing:var(--ls-label);text-transform:uppercase;
  font-weight:700;color:var(--c-rojo)}
.ov__factbig{margin:0;
  font-family:var(--f-display);font-weight:900;
  font-variation-settings:'wdth' 112,'wght' 900;
  font-size:clamp(1.75rem,7vw,2.25rem);line-height:1;
  letter-spacing:-.02em;text-transform:uppercase;color:var(--c-cci)}
.ov__factv{margin:0;font-size:1rem;font-weight:600;line-height:1.35;color:var(--c-ink)}
.ov__factn{margin:0;font-size:var(--t-small);line-height:1.4;color:var(--c-muted)}
.ov__factbig + .ov__factn{margin-top:4px}
.ov__factcta{margin:var(--s-3) 0 0}

/* « Entrada libre » est LA réponse qui décide de venir ou non : c'est le
   seul fait qui prend la couleur d'accent. */
.ov__fact--key .ov__factv{color:var(--c-rojo-txt)}

/* ══ SIGNAL DE DÉFILEMENT — DEUX TENTATIVES, DEUX RETRAITS ═════════════
   Le 24/08, deux essais pour dire « il y a la suite » sous le menu :

   · `mask-image` — rendait le PANNEAU transparent, pas son contenu : la page
     défilait visiblement derrière le bas du menu. Lu comme un défaut de rendu.
   · un `::after` en dégradé sticky — délavait le dernier élément visible MÊME
     QUAND rien ne dépassait, parce qu'un fondu bas fond toujours ce qui est en
     bas. La ligne légale devenait illisible sur les téléphones où le menu tient
     entièrement.

   Le fond du problème : un fondu ne doit apparaître QUE s'il reste du contenu,
   et le CSS seul ne sait pas le dire — `background-attachment:local` gère le
   cas, mais un fond passe DERRIÈRE le texte et ne peut donc pas l'estomper.
   Il faudrait un écouteur de défilement.

   Retiré. Un menu plein écran qui défile est un comportement compris de tous,
   et le navigateur affiche son propre indicateur au toucher. Si le sujet
   revient, c'est par JS qu'il faut le traiter, pas par une astuce CSS. */

/* ── Pastilles `.ov__links` — RETIRÉES LE 24/08/2026 ─────────────────────
   Les trois liens sont partis dans le panneau bleu (voir header.php). Styler
   un élément qui n'est plus rendu, c'est laisser une trace que la prochaine
   personne prendra pour une régression. */

/* ── Sélecteur de langue du menu — RETIRÉ LE 18/08/2026 ─────────────── */
/* Les règles `.ov__lang` vivaient ici. Le sélecteur a quitté le menu (voir
   le commentaire dans header.php) : styler un élément qui n'est plus rendu
   nulle part, c'est une piste que quelqu'un suivra un jour pour rien. */

/* ── ≤760px · les panneaux prennent l'écran entier ──────────────────── */
@media(max-width:760px){
  /* La marge basse est ce qui laissait dépasser le bord du panneau blanc :
     `translateY(100%)` le descend de sa propre hauteur, donc jusqu'au bas
     de `.ov` — pas jusqu'au bas de l'écran. Sans marge, les deux
     coïncident. */
  .ov{top:0;bottom:0}
  /* Le bleu seul passe en plein écran. Le blanc GARDE ses angles hauts
     arrondis : c'est une feuille qui monte du bas (voir « INFO PRÁCTICA
     DEVIENT UNE FEUILLE » plus haut), et l'arrondi est ce qui la fait lire
     comme telle. */
  .ov__menu{border-radius:0}

  /* La feuille descend au ras du bord — la bande de page qui restait
     visible sous elle venait de la marge basse de `.ov`, pas d'elle. Le
     dock se pose par-dessus : elle réserve donc sa hauteur en padding
     plutôt qu'en écart, sinon le dernier lien passe dessous. */
  .practica-open .ov__practica{bottom:0;padding-bottom:calc(52px + var(--s-6));
    overscroll-behavior:contain;
    /* 64svh était calé sur six liens. La feuille porte maintenant trois
       faits, un bouton d'itinéraire et trois pastilles : à 64 % la
       dernière rangée passait sous le pli et devenait invisible — un
       menu caché dans un panneau qui tient en un écran, c'est le défaut
       qu'on vient de corriger ailleurs. Elle tient entière à 74 %, et le
       décor reste visible au-dessus. */
    max-height:74svh}

  /* Le bleu : on remonte le contenu en haut de panneau plutôt que de le
     centrer. Centré, il laissait un vide de 100 px sous la croix et
     poussait le sélecteur de langue et les mentions hors de l'écran dès
     qu'un téléphone est un peu court. */
  /* ⚠ LE REMBOURRAGE HAUT SUIT LA CROIX MOBILE, PAS CELLE DU DESKTOP.
     Il valait `calc(var(--s-5) + 56px)` = 80 px, c'est-à-dire l'offset et la
     taille du bouton en version large. Or la ligne suivante le redéfinit ici
     même à 48 px posé à `--s-4` : 64 px suffisent. Seize pixels étaient
     réservés pour dégager un bouton qui n'existe pas à cette taille.

     ⚠ NE PAS DESCENDRE PLUS BAS en croyant gagner encore : sous 64 px, la
       première ligne du menu passe à hauteur du bouton. Elles ne se
       chevauchent pas horizontalement — le menu est centré, la croix à droite
       — mais l'équilibre visuel se casse.

     L'écart passe de 32 à 24 px et le rembourrage bas de 48 à 32 : sur un
     écran de 667 px le panneau débordait de 116 px, il n'en reste que 60,
     désormais annoncés par le dégradé. */
  .ov__menu{justify-content:flex-start;gap:var(--s-5);
    padding:calc(var(--s-4) + 48px) var(--s-5) var(--s-6);
    overscroll-behavior:contain}
  .ov__close{top:var(--s-4);right:var(--s-4);width:48px;height:48px}

  /* Le CTA cassait en quatre lignes dans une pilule : trop de rembourrage
     latéral pour la largeur disponible. */
  .ov__cta{width:100%;max-width:22rem;padding-inline:var(--s-5);
    border-radius:var(--r-lg)}
  .ov__cta b{font-size:.9375rem}

  /* ── LA BARRE NE RÉPÈTE PAS CE QUI EST OUVERT ──────────────────────
     Feuille ouverte, le dock affichait encore « INFO PRÁCTICA ⌄ » : le
     même intitulé que le titre de la feuille, à 40 px sous lui, en
     pilule grise. C'était le SOCLE de la version précédente — il servait
     à la fois d'étiquette et de fermeture. La feuille porte désormais
     les deux (titre + croix), donc il ne reste qu'un doublon qui a l'air
     désactivé.

     Il s'efface, et « Reservar taller » prend la barre entière : le seul
     geste qui reste à faire depuis cette feuille, c'est réserver. Il
     revient intact à la fermeture — `practica-open` tombe au même
     instant. */
  .practica-open .dock__practica{display:none}
  .practica-open .dock__cta{flex:1 1 100%}

  /* `.practica-open .dock{bottom:var(--s-6)}` (le rail flottant du bureau)
     vaut 0,2,0 et l'emportait sur le `bottom:0` mobile, qui n'en vaut que
     0,1,0 : la barre décollait de 32 px et laissait une bande crème sous
     elle. Sur téléphone la barre est soudée au bord, ouverte comme
     fermée — c'est aussi la seule position qui tombe sous le pouce. */
  .practica-open .dock{bottom:0}
}

/* ══════════════════════════════════════════════════════════════════════
   .rte — LE CONTENU ÉCRIT DANS L'ÉDITEUR

   POURQUOI CETTE CLASSE EXISTE

   Le thème remet à zéro deux choses en tête de fichier, et il a raison de
   le faire pour un site dessiné :

       ul { list-style: none; padding: 0 }
       a  { color: inherit; text-decoration: none }

   Ces règles servent les composants — pastilles, billets, navigations —
   où une puce ou un soulignement seraient des accidents. Mais elles
   frappent AUSSI tout ce que le client écrit dans un champ WYSIWYG. Une
   liste à puces saisie en back-office ressort sans puce et sans retrait :
   trois lignes empilées qui ne se lisent plus comme une liste. C'est le
   défaut constaté sur la note de pied de page.

   La remise à zéro reste donc en place, et on la DÉFAIT dans un périmètre
   nommé. Tout champ d'éditeur s'affiche à l'intérieur d'un `.rte` — la
   note de pied de page aujourd'hui, le bilan d'édition, les infos de
   transport et le contenu des blocs demain.

   ⚠ Ne pas mettre `.rte` sur autre chose que du contenu d'éditeur : sur un
     composant dessiné, il rendrait les puces que le dessin retire.
   ══════════════════════════════════════════════════════════════════════ */

/* Le rythme vertical : uniquement ENTRE les éléments, jamais avant le
   premier ni après le dernier — sinon le bloc ne s'aligne plus sur ce qui
   l'entoure. */
.rte > * + *{margin-top:var(--s-3)}

/* Les listes retrouvent puce et retrait. `padding-left` en em et non en
   rem : le retrait suit la taille du texte, donc il reste juste dans le
   pied de page (13 px) comme dans un bloc éditorial (18 px). */
.rte ul,
.rte ol{margin:0;padding-left:1.25em;list-style-position:outside}
.rte ul{list-style-type:disc}
.rte ol{list-style-type:decimal}
.rte li + li{margin-top:.35em}

/* Une liste imbriquée reste lisible : cercle plutôt que disque, pour que
   les deux niveaux se distinguent sans changer de taille. */
.rte ul ul{list-style-type:circle;margin-top:.35em}

/* Les liens redeviennent des liens. `text-underline-offset` aligne le
   soulignement sur celui du reste du site (`.footer__org a`). */
.rte a{text-decoration:underline;text-underline-offset:3px}

/* L'éditeur produit `<strong>` et `<em>` : sans ces deux lignes, ils
   héritent d'une graisse ou d'une italique déjà imposée par le contexte et
   ne se voient plus. */
.rte strong,
.rte b{font-weight:700}
.rte em,
.rte i{font-style:italic}

/* Une citation saisie par le client. Filet à gauche plutôt que guillemets
   typographiques : le contenu peut être dans n'importe quelle langue. */
.rte blockquote{margin-left:0;padding-left:var(--s-4);
  border-left:2px solid currentColor;opacity:.85}

/* ══════════════════════════════════════════════════════════════════════
   LISIBILITÉ — LE PLANCHER TYPOGRAPHIQUE

   Mesuré : 57 sélecteurs descendent sous 14 px, dont 8 sous 10 px. Tout
   n'est pas à corriger — un folio de billet à 8 px est un ornement, pas une
   information. La règle appliquée distingue donc deux natures :

     · TEXTE QU'ON LIT (phrases, listes, liens de navigation) → 14 px mini.
       En dessous, un texte long sur fond sombre devient pénible bien avant
       d'être illisible, et c'est cette pénibilité qui fait qu'on ne le lit
       pas du tout.

     · ÉTIQUETTE (majuscules, interlettrée, deux ou trois mots) → 12 px mini.
       Les capitales ont une hauteur d'œil constante : elles tiennent plus
       bas que du bas-de-casse. Mais 11 px avec 0,14 em d'interlettrage,
       c'est le seuil où l'œil épelle au lieu de lire.

   ⚠ Le jeton `--t-label` est corrigé ICI et non dans `src/css/style.css`,
     sa source. Ce n'est pas un oubli : le build Tailwind qui produit
     `output-child.css` n'est documenté nulle part, et personne ne sait
     aujourd'hui le relancer. Modifier la source sans pouvoir compiler
     donnerait un fichier juste et un site inchangé — le pire des deux.
     À remonter dans la source le jour où la chaîne de build est rétablie.
   ══════════════════════════════════════════════════════════════════════ */

:root{
  /* 11 px → 12 px. Un seul jeton, donc toutes les étiquettes du site
     montent ensemble et gardent leur cohérence. */
  --t-label:.75rem;
}

/* ── Texte qu'on lit : plancher à 14 px ─────────────────────────────── */
.footer__org p,
.footer__org li,
.footer__org .rte,
.footer__legal,
.ov__cols a,
.ov__factn{font-size:var(--t-small)}


/* ── Mentions légales du panneau : trop petites ET trop pâles ────────
   Mesuré à 3,46:1 sur le bleu — sous le seuil AA de 4,5:1. L'opacité
   passe de .5 à .72, ce qui donne 5,55:1, et la taille de 12 à 13 px.
   Une mention légale illisible n'est pas une mention légale. */
.ov__legal{font-size:.875rem;color:rgba(247,241,228,.72)}

/* ── Étiquettes : plancher à 12 px ──────────────────────────────────── */
.header__banner-track span,
.dock__cta span,
.ov__cta span{font-size:.8125rem}

/* ── L'image de fond d'une bande de zone ─────────────────────────────
   Émise seulement si le client a déposé une photo (voir ccimx_zone_band).
   Elle passe DERRIÈRE le texte, assombrie : une bande de zone porte un
   numéro, un nom et une description qui doivent rester lisibles — une
   photo à pleine intensité les avale. */
.zs__card{position:relative;overflow:hidden;isolation:isolate}
.zs__img{position:absolute;inset:0;width:100%;height:100%;
  object-fit:cover;z-index:-1;opacity:.28;
  /* Le mélange conserve la couleur de la zone : la photo la texture,
     elle ne la remplace pas. Sans ça les six bandes se ressemblent. */
  mix-blend-mode:luminosity}

/* ══════════════════════════════════════════════════════════════════════
   PAGE DE CONTENU — contact, presse, mentions légales

   Ces pages ne sont pas faites de blocs de section : elles sont du texte
   au kilomètre, rendu par `page.php` dans un `.wrap`.

   ⚠ LE DÉCALAGE HAUT N'EST PAS DÉCORATIF. L'en-tête est en
   `position:fixed` : sans lui, la première ligne passe DERRIÈRE la barre
   de navigation. `--header-h` vaut la bande annonce plus la barre.
   ══════════════════════════════════════════════════════════════════════ */
/* ⚠ Deux réserves obligatoires : en HAUT parce que l en-tête est fixe, en
   BAS parce que la barre d action l est aussi et recouvrirait les derniers
   paragraphes. `--practica-h` est sa hauteur, déjà consommée par le hero. */
.page-body{
  padding-top:calc(var(--header-h) + var(--s-7));
  padding-bottom:var(--s-8);
  background:var(--c-cream);
  color:var(--c-ink);
}

/* Colonne de lecture : la largeur du conteneur sert les sections pleine
   largeur, pas un paragraphe. Au-delà d'environ 70 signes, l'œil perd la
   ligne suivante en revenant à la marge. */
.page-body__inner{max-width:var(--maxw)}


.page-body .rte{display:flex;flex-direction:column;gap:var(--s-4)}
.page-body .rte h2{
  font-family:var(--f-display);
  font-size:clamp(1.35rem,3vw,1.8rem);
  line-height:1.2;
  margin-top:var(--s-5);
}

/* Le formulaire de contact hérite du gabarit de Contact Form 7. Il n'a
   aucun style propre au socle : on lui donne le rythme de la page. */
.page-body .wpcf7-form{display:flex;flex-direction:column;gap:var(--s-4)}
.page-body .wpcf7-form p{margin:0}
.page-body .wpcf7-form label{
  display:flex;flex-direction:column;gap:var(--s-2);
  font-size:var(--t-small);
}
.page-body .wpcf7-form input[type=text],
.page-body .wpcf7-form input[type=email],
.page-body .wpcf7-form select,
.page-body .wpcf7-form textarea{
  font:inherit;font-size:1rem;color:inherit;
  padding:var(--s-3);
  background:#fff;
  border:1px solid var(--c-line);
  border-radius:var(--r-sm);
}
.page-body .wpcf7-form textarea{min-height:9rem;resize:vertical}
.page-body .wpcf7-form input:focus-visible,
.page-body .wpcf7-form select:focus-visible,
.page-body .wpcf7-form textarea:focus-visible{
  outline:2px solid var(--c-rojo);outline-offset:1px;
}
/* Le bouton d'envoi porte déjà `.btn .btn--primary` (déclaré dans le
   formulaire CF7) : il ne doit pas s'étirer sur toute la largeur. */
.page-body .wpcf7-form .wpcf7-submit{align-self:flex-start}

/* ══════════════════════════════════════════════════════════════════════
   BANNIÈRE DE PAGE

   Une phrase posée en grand, alignée à gauche, et une ligne de soutien.
   Ni étiquette, ni filet, ni numérotation : la page s'annonce par ce
   qu'elle dit.

   ⚠ Le décalage sous l'en-tête fixe appartient à `.page-body` : ne pas le
   redoubler ici, la bannière en hériterait deux fois.
   ══════════════════════════════════════════════════════════════════════ */

.pbanner__inner{
  max-width:var(--maxw);
  display:flex;flex-direction:column;align-items:flex-start;
  gap:var(--s-5);
}

/* ⚠ La mesure est VOLONTAIREMENT COURTE. À cette taille, une ligne de plus
   de vingt signes se lit mal : c'est elle, et non un retour forcé, qui
   décide de la coupe. `text-wrap:balance` égalise ensuite les lignes. */
.pbanner__h{
  margin:0;
  max-width:18ch;
  font-family:var(--f-display);
  font-size:clamp(2.4rem,7vw,4.6rem);
  line-height:1.02;
  letter-spacing:-.02em;
  text-wrap:balance;
}

/* L'italique d'accent du site — `*mot*` dans le champ — reste la seule
   emphase disponible dans le titre. */
.pbanner__h em{font-style:italic;color:var(--c-rojo-txt)}

.pbanner__sub{
  margin:0;
  max-width:46ch;
  font-size:clamp(1.05rem,2vw,1.25rem);
  line-height:1.5;
  color:var(--c-muted);
}

/* ── LA COLONNE DE LECTURE ─────────────────────────────────────────────
   Bornée en signes, PAS centrée : elle part du même bord que le titre de
   la bannière. Une colonne centrée sous un titre aligné à gauche laisse une
   gouttière vide qui se lit comme une erreur de gabarit. */

/* La bannière et le corps appartiennent à la même page : l'écart entre eux
   doit être plus court que celui qui sépare deux sections. */
.pbanner{padding-block:0 var(--s-6)}

/* La date de conférence de presse, sous le chapeau de la banniere. Ce n'est
   pas un fait de plus dans une liste : c'est la SEULE date qui concerne le
   lecteur de cette page-la. D'ou l'etiquette en capitales et la date en
   graisse — la meme paire cle/valeur que les faits pratiques du site. */
.pbanner__pressday{
  margin:var(--s-4) 0 0;display:flex;flex-wrap:wrap;align-items:baseline;
  gap:.35rem var(--s-3)}
.pbanner__pressday-k{
  font-size:var(--t-label);letter-spacing:var(--ls-label);
  text-transform:uppercase;font-weight:800;color:var(--c-rojo-txt)}
.pbanner__pressday time{
  font-family:var(--f-display);font-weight:800;
  font-size:clamp(1rem,1.6vw,1.15rem);color:var(--c-ink)}

/* ⚠ LA BARRE D'ACTION EST EN `position:fixed` : sans cette réserve, elle
   recouvre les derniers paragraphes de la page — le titre « Lo que
   buscamos » passait dessous. */

/* ── TEXTE + CARTE LATÉRALE ────────────────────────────────────────────
   Reprend la grille de la section FAQ de l'accueil, proportions inversées :
   là-bas le rail nomme la section et les questions défilent à côté ; ici
   c'est le TEXTE qui commande, parce qu'il continue la phrase du titre — la
   colonne de lecture doit partir du même bord que lui.

   ⚠ Sans carte, `--solo` rend la colonne unique et plus large. Une colonne
   étroite calée à gauche sous un vide à droite se lit comme un gabarit
   inachevé : c'est le seul état à ne jamais produire. */
.page-body__grid{display:grid;gap:var(--s-7)}
.page-body__text{max-width:70ch}
.page-body__grid--solo .page-body__text{max-width:82ch}

.page-aside{
  background:var(--c-ink);color:var(--c-cream);
  border-radius:var(--r-lg);
  padding:clamp(var(--s-5),3vw,var(--s-6));
  align-self:start;
  font-size:var(--t-small);
}
.page-aside .rte{display:flex;flex-direction:column;gap:var(--s-3)}
/* Sur fond sombre, le rouge de marque perd son contraste : les liens de la
   carte passent en crème souligné. */
.page-aside a{color:var(--c-cream);text-decoration:underline;text-underline-offset:3px}
.page-aside strong{color:#fff}

@media(min-width:1000px){
  .page-body__grid{
    grid-template-columns:minmax(0,1fr) minmax(0,20rem);
    gap:clamp(2.5rem,5vw,4.5rem);
    align-items:start;
  }
  .page-body__grid--solo{grid-template-columns:minmax(0,1fr)}

  /* La carte suit le lecteur : sur une page longue, un contact qui disparaît
     au premier défilement ne sert à personne. Le décalage haut dégage
     l'en-tête fixe. */
  .page-aside{position:sticky;top:calc(var(--header-h) + var(--s-5))}
}

/* Le filet tricolore de la bannière : un peu plus large que celui du hero,
   parce quil est seul ici et doit tenir sous un titre de cette taille. */
.pbanner__flag{width:120px;margin:var(--s-3) 0 0}

/* ── ALIGNEMENT CENTRÉ ─────────────────────────────────────────────────
   Réservé aux pages courtes et autonomes. La valeur par défaut reste à
   gauche : c'est ce qui aligne la colonne de lecture sur la phrase de la
   bannière, et un centrage sous un titre aligné à gauche se lit comme une
   erreur de gabarit. */
.pbanner--center .pbanner__inner{align-items:center;text-align:center}
.pbanner--center .pbanner__h{max-width:22ch}

/* Le centrage du texte ne vaut que SANS carte — le gabarit le garantit en
   n'ajoutant `--center` que dans ce cas. */
.texto--center .page-body__text{margin-inline:auto;text-align:center;max-width:62ch}
.texto--center .page-body__text .rte > *{margin-inline:auto}

/* Le bloc « chiffres » : collé sur l accueil, aéré dans une page. */
.proof-block{padding-block:0}
.page-body .proof-block{padding-block:var(--s-7) var(--s-8)}



/* ⚠ ÉTAT DE DÉPART DES CARTES, sous `.js` UNIQUEMENT — comme `.fx-up`.
   Sans JavaScript, ou en mouvement réduit, la classe `.js` n'est pas posée
   et les cartes restent visibles. C'est le garde-fou qui rend l'éditeur de
   blocs lisible, et il vaut pour toute animation d'entrée du site.
   La course est plus ample que `.fx-up` (28 px) : sur des cartes larges,
   une translation courte ne se voit pas. */

/* ══════════════════════════════════════════════════════════════════════
   CARTES ABATTUES

   Des cartes qu'on pose sur une table : fond plein, coins très ronds,
   vignette en médaillon, et une inclinaison propre à chacune. Le geste
   d'entrée est piloté par `main.js` (section épinglée) — voir « Cartes
   distribuées ».

   ⚠ L'ÉTAT DE DÉPART EST SOUS `.js`, comme toute animation d'entrée du
   site. Sans JavaScript, en mouvement réduit, ou dans l'éditeur de blocs,
   la classe n'est pas posée et les cartes s'affichent normalement.
   ══════════════════════════════════════════════════════════════════════ */

.galeria__grid{display:grid;gap:var(--s-5)}

.gcard{
  margin:0;
  display:flex;flex-direction:column;gap:var(--s-4);
  padding:var(--s-5);
  border-radius:var(--r-lg);
  background:var(--c-cream-2);
  color:var(--c-ink);
  will-change:transform;
}

/* Le cycle des fonds : trois cartes, trois valeurs de la palette. Une
   quatrième reprend la première — comme les zones, dont le CSS distribue
   déjà ses couleurs en `:nth-child`. */
.gcard:nth-child(3n+2){background:var(--c-peach);color:var(--c-ink)}
.gcard:nth-child(3n+3){background:var(--c-ink);color:var(--c-cream)}
.gcard:nth-child(3n+3) .gcard__x{color:var(--c-muted-inv)}

/* La vignette : un médaillon, pas une bannière. C'est ce qui distingue
   une carte d'une tuile d'article. */
.gcard__media{
  position:relative;display:block;align-self:flex-start;
  width:min(46%, 10.5rem);aspect-ratio:1/1;
  border-radius:var(--r-md);overflow:hidden;
  background:rgba(0,0,0,.06);
}
.gcard__img{width:100%;height:100%;object-fit:cover;display:block}

.gcard__flag{
  position:absolute;left:.5rem;top:.5rem;
  background:var(--c-rojo);color:#fff;
  font-size:.625rem;font-weight:700;
  letter-spacing:.08em;text-transform:uppercase;
  padding:.35em .6em;border-radius:var(--r-sm);
}

.gcard__cap{display:flex;flex-direction:column;gap:var(--s-3);margin-top:auto}
.gcard__t{
  font-family:var(--f-display);
  font-size:clamp(1.6rem,2.8vw,2.1rem);
  font-weight:900;
  line-height:.98;letter-spacing:-.01em;
  text-transform:uppercase;text-wrap:balance;
}
.gcard__x{font-size:var(--t-small);color:var(--c-muted);max-width:34ch}

/* État de départ : hors écran, au centre. `main.js` mesure et replace. */
.js .gcard{opacity:0;--gcard-y:56px}



/* Le titre est centré : c'est lui qui annonce la séquence, et les cartes
   arrivent du centre sous lui. Aligné à gauche, il désignait un bord que
   rien ne suit. */
.galeria__h{text-align:center;margin-inline:auto;max-width:22ch}

/* ⚠ LA SECTION ÉPINGLÉE PREND L ÉCRAN.
   ScrollTrigger la fige au bord haut de la fenêtre : si elle est plus
   courte, elle reste collée en haut avec du vide dessous, et la séquence
   se joue dans un cadre qui ne ressemble à rien. Elle doit donc occuper
   la hauteur visible et centrer son contenu — c est une SCÈNE, pas un
   paragraphe.
   `100svh` et non `100vh` : sur mobile, la barre d adresse mange la
   différence et la scène déborderait. */

/* ══════════════════════════════════════════════════════════════════════
   LA SCÈNE ÉPINGLÉE

   La section se fige le temps que les cartes soient abattues : elles
   montent du bas au centre, s'écartent et prennent leur inclinaison. Les
   positions sont pilotées par `main.js` — voir « Cartes empilées ».

   ⚠ `height` ET NON `min-height` : ScrollTrigger épingle la section au bord
   HAUT de la fenêtre, donc tout ce qui dépasse sort par le haut et se
   coupe — le titre en premier, visible seulement en remontant.

   ⚠ JAMAIS `overflow:hidden` ICI. Sur un élément épinglé, il crée un
   contexte de défilement que ScrollTrigger ne sait plus mesurer, et il
   rognerait l'éventail. Essayé, cassé, retiré.
   ══════════════════════════════════════════════════════════════════════ */
@media(min-width:900px){

  /* ⚠ LA RÉSERVE EN HAUT DÉGAGE LE HEADER, elle n'est pas décorative.

     La section s'épingle à `start:'top top'` : son bord haut vient se coller
     au bord haut de la fenêtre, donc DERRIÈRE le header fixe. Le titre, qui
     est le premier élément, est le premier recouvert.

     Le défaut ne se voit qu'en REMONTANT : le header s'efface au défilement
     descendant et réapparaît au montant (`setHeaderHidden` dans `main.js`).
     En descendant, le titre est dégagé — d'où un bug qui semble intermittent.

     ⚠ `--header-inner-h` ET NON `--header-h`. `--header-h` ajoute la bande
     annonce (34 px), mais `.header.is-solid .header__banner{height:0}` la
     replie dès qu'on a défilé — et à hauteur de galerie, on a forcément
     défilé. Réserver `--header-h` volerait 34 px à la scène pour rien.

     Repose sur le `box-sizing:border-box` global : le padding PREND sur les
     100svh au lieu de les agrandir. Sans lui, la scène dépasserait la fenêtre
     et l'épinglage rognerait de nouveau par le haut. */
  .galeria{
    height:100svh;
    display:flex;flex-direction:column;
    padding-block:var(--s-6);
    padding-block-start:calc(var(--header-inner-h) + var(--s-6));
  }

  /* Le conteneur prend la hauteur restante… */
  .galeria > .wrap{width:100%;flex:1;display:flex;flex-direction:column}

  /* …et la grille s'y centre, sous le titre. `min-height:0` est la
     condition pour qu'un enfant flex accepte de rétrécir sous la hauteur de
     son contenu : sans lui, il pousse et fait déborder la scène. */
  .galeria__grid{
    flex:1;min-height:0;margin-top:var(--s-6);
    position:relative;
    display:block;
  }

  /* ⚠ POSITIONNEMENT ABSOLU, PAS `grid-area` : l'absolu ne dépend d'aucun
     contexte de grille hérité.

     `inset:0` + `margin:auto` centre sur les DEUX axes SANS `transform` —
     et c'est la condition : GSAP pilote `transform` pour écarter les
     cartes, un `translate(-50%,-50%)` en CSS serait écrasé au premier tick.

     `max-height:100%` empêche une carte plus haute que son conteneur de
     déborder symétriquement — donc aussi vers le haut, sur le titre. */
  .gcard{
    position:absolute;
    inset:0;
    margin:auto;
    width:min(23rem, 30vw);
    height:fit-content;
    max-height:100%;
  }

  /* z-index DÉCROISSANT : la première carte reste au-dessus, comme une main
     qu'on étale. */
  .gcard:nth-child(1){z-index:1}
  .gcard:nth-child(2){z-index:1}
  .gcard:nth-child(3){z-index:1}
}

/* État de départ, sous `.js` comme toute animation d'entrée du site. */
.js .gcard{opacity:0}

/* ══════════════════════════════════════════════════════════════════════
   FICHE ATELIER — single-ccimx_workshop.php

   La section des séances réutilise `.talleres` et `.ticket` : mêmes billets
   que l'accueil, donc rien à styler ici pour la partie la plus visible.
   Ce bloc ne dessine que le corps de la fiche.
   ══════════════════════════════════════════════════════════════════════ */

.taller__grid{display:grid;gap:var(--s-6)}

.taller__media{margin:0 0 var(--s-5)}
.taller__media img{width:100%;height:auto;border-radius:var(--r-lg);display:block}

/* L'encadré des informations pratiques. `dt`/`dd` plutôt qu'une suite de
   paragraphes : c'est une liste de définitions, et les lecteurs d'écran
   annoncent alors le libellé avec sa valeur. */
.taller__facts{margin:0 0 var(--s-5);display:grid;gap:var(--s-3)}
.taller__facts dt{
  font-size:var(--t-label);letter-spacing:var(--ls-label);text-transform:uppercase;
  color:var(--c-muted);font-weight:800}
.taller__facts dd{margin:0 0 var(--s-3);font-weight:600}
.taller__facts dd:last-of-type{margin-bottom:0}

.taller__side{display:flex;flex-direction:column;align-items:flex-start;gap:var(--s-3)}

.taller__empty{color:var(--c-muted);max-width:48ch}
.taller__back{margin-top:var(--s-6)}

@media(min-width:900px){

  /* Deux colonnes : le récit à gauche, les faits à droite. Le ratio donne au
     texte sa largeur de lecture confortable sans étrangler l'encadré. */
  .taller__grid{grid-template-columns:minmax(0,1.7fr) minmax(0,1fr);gap:var(--s-7);align-items:start}
  /* ⚠ SANS IMAGE, UNE SEULE COLONNE (24/09/2026). Les deux colonnes sont
     faites pour un article illustré ; sur une fiche de trois lignes, elles
     laissaient l'encadré seul à droite d'un grand vide. Le texte garde une
     largeur de LECTURE — 34rem, environ 70 signes par ligne — et l'encadré
     passe dessous, aligné à gauche comme le titre. Il perd son `sticky` :
     il n'accompagne plus rien au défilement. */
  .taller__grid--solo{grid-template-columns:minmax(0,1fr);gap:var(--s-6)}
  .taller__grid--solo .taller__main{max-width:34rem}
  .taller__grid--solo .taller__side{position:static;max-width:34rem}

  /* ⚠ L'encadré est collant, décalé de la hauteur du header — sinon il passe
     dessous en défilant. `--header-inner-h` et non `--header-h` : la bande
     annonce est repliée dès qu'on a défilé (`.header.is-solid .header__banner`). */
  .taller__side{position:sticky;top:calc(var(--header-inner-h) + var(--s-5))}
}

/* Le titre du billet mène à la fiche de l'atelier. Souligné au survol
   seulement : sur un billet, un soulignement permanent ajouterait une
   troisième ligne graphique là où la perforation et la jauge parlent déjà. */
.ticket__title a{color:inherit;text-decoration:none}
.ticket__title a:hover,
.ticket__title a:focus-visible{text-decoration:underline;text-underline-offset:.18em}

/* ── L'emphase dans un titre découpé ───────────────────────────────────
   `*mot*` dans un champ `wordcut`. Même parti que `.display em` : italique,
   casse d'origine, couleur d'accent — le mot SORT du bloc de capitales au
   lieu de s'y fondre. C'est ce qui donne son relief à un titre posé sur
   plusieurs lignes.

   ⚠ La couleur suit la section, comme pour `.display em` : rouge sur fond
   clair, corail sur les fonds sombres où le rouge perdrait son contraste. */
.wordcut em{
  font-style:italic;font-weight:400;
  text-transform:none;letter-spacing:-.005em;
  color:var(--c-rojo-txt)}
.hz .wordcut em,.sponsors .wordcut em,.footer .wordcut em{color:var(--c-coral)}

/* Le passage vers le listing complet, sous les billets de l'accueil. */
/* ══ BLOC VIDÉO ════════════════════════════════════════════════════════
   La façade et le lecteur occupent EXACTEMENT la même boîte : au clic,
   l'iframe remplace le lien, et rien ne doit sauter sous le doigt. D'où le
   `aspect-ratio` posé sur les deux et non sur un conteneur.

   ⚠ 16/9 EN DUR, ASSUMÉ. YouTube et Vimeo servent ce format ; une vidéo
     verticale y apparaîtrait avec des bandes, ce qui est le comportement des
     lecteurs eux-mêmes. Suivre le ratio réel demanderait d'interroger leur API
     au rendu — un appel réseau à chaque page pour un cas qui ne se présente
     pas ici. */
.videoblock__facade,
.videoblock__frame{
  display:block;width:100%;aspect-ratio:16/9;
  border:0;border-radius:var(--r-lg);overflow:hidden;background:var(--c-ink)}

.videoblock__facade{position:relative;cursor:pointer;text-decoration:none}
.videoblock__poster{width:100%;height:100%;object-fit:cover;display:block}

/* ── Une vidéo, ou deux ────────────────────────────────────────────────
   ⚠ LA GRILLE EST DÉCLARÉE À UNE COLONNE ET N'EN PREND DEUX QUE SOUS
     `--pair`, posée par le gabarit quand il y a deux vidéos. L'inverse —
     `auto-fit` sur une largeur minimale — passerait à deux colonnes tout seul
     dès que la fenêtre s'élargit, y compris avec une seule vidéo : elle
     occuperait alors la moitié gauche et laisserait un vide à droite.
     La disposition suit le CONTENU, pas la place disponible. */
.videoblock__grid{display:grid;gap:var(--s-6);grid-template-columns:1fr}
.videoblock__item{margin:0;display:grid;gap:var(--s-3)}

@media (min-width:860px){
  .videoblock--pair .videoblock__grid{grid-template-columns:1fr 1fr}

  /* ⚠ LA DERNIÈRE ESSEULÉE SE CENTRE, ELLE NE S'ÉTIRE PAS. À trois ou cinq
     vidéos, la grille à deux colonnes laisse la dernière avec un demi-rang
     vide à sa droite — le même défaut d'orphelin que la fiche partenaire
     seule et la rangée des logos ont produit cette semaine.

     ⚠ ET SURTOUT PAS EN PLEINE LARGEUR, essayé le 08/09 : une vidéo 16/9 sur
       1 328 px fait 747 px de haut à elle seule. Le bloc passait de 833 px à
       1 216 à trois vidéos, et à 1 648 à cinq. On corrigeait un demi-rang vide
       en ajoutant l'équivalent de deux rangs. Centrée à sa taille normale,
       elle ne laisse plus de trou d'un côté et ne coûte rien en hauteur.

     `:last-child` seul ne suffit pas : il viserait aussi la deuxième d'une
     paire. Il faut la dernière ET à une position impaire — donc sans voisine
     sur sa ligne. */
  .videoblock__item:last-child:nth-child(odd){
    grid-column:1 / -1;
    width:calc(50% - var(--s-6) / 2);
    justify-self:center}
}

/* Le bouton se pose au centre, sur un voile qui garantit son contraste quelle
   que soit l'image dessous — une vignette claire ne doit pas l'avaler. */
.videoblock__play{
  position:absolute;inset:0;display:grid;place-items:center;
  background:rgba(10,37,64,.28);
  transition:background-color .3s var(--ease-out)}
.videoblock__play svg{
  width:clamp(3.5rem,7vw,5rem);height:clamp(3.5rem,7vw,5rem);
  color:var(--c-cream);
  filter:drop-shadow(0 2px 14px rgba(10,37,64,.5));
  transition:transform .3s var(--ease-out)}
.videoblock__facade:hover .videoblock__play,
.videoblock__facade:focus-visible .videoblock__play{background:rgba(10,37,64,.14)}
.videoblock__facade:hover .videoblock__play svg{transform:scale(1.08)}

/* ⚠ `.head` PLAFONNE À 60ch, ET C'EST LA MESURE D'UN PARAGRAPHE.
   Les autres blocs s'en accommodent parce que leurs titres sont des mots
   d'affiche — « AGENDA », « PLANO ». Celui-ci est une PHRASE, composée au même
   calibre : à 60ch elle se cassait en cinq lignes de 370 px de haut, à côté
   d'une vidéo de 1328 px de large. Le déséquilibre venait de là, pas de la
   largeur de la vidéo.

   La mesure est posée sur le titre LUI-MÊME : `ch` s'y calcule sur sa propre
   taille de corps, donc elle suit le titre quand il se réduit sur petit écran,
   sans média-requête à tenir. */
.videoblock .head{max-width:none}
.videoblock__title{margin:0;max-width:20ch}
.videoblock__note{margin:0;font-size:var(--t-small);color:var(--c-muted)}

@media (prefers-reduced-motion:reduce){
  .videoblock__play,.videoblock__play svg{transition:none}
  .videoblock__facade:hover .videoblock__play svg{transform:none}
}

/* Le numéro de stand encore provisoire : même ligne, poids en moins. Il ne
   doit pas concurrencer le numéro — juste empêcher qu'on le prenne pour acquis. */
/* ══ UNE PLAQUE EN ATTENTE DE SON LOGO ══════════════════════════════════
   ⚠ PREMIÈRE VERSION RETIRÉE LE 04/09 : elle composait le nom en `--f-display`
     graisse 700. Posé sous le titre « Marcas en proceso de confirmación » et
     au milieu de quatre logos, ce mot se lisait comme un SECOND TITRE — le
     défaut remonté le jour même.

   La forme dit maintenant ce qu'elle est : la hauteur d'une plaque voisine,
   un contour POINTILLÉ — la convention du projet pour le provisoire, déjà
   portée par `.agenda__pending` et `.agenda__vide` — et le nom en petites
   capitales neutres. On lit « il manque quelque chose ici », pas « voici une
   marque ». */
.brandrow__name{
  display:inline-grid;place-items:center;
  min-height:var(--plate-h,clamp(32px,3.6vw,44px));
  padding:0 var(--s-4);border-radius:var(--r-sm);
  border:1.5px dashed var(--c-line);
  font-family:inherit;font-weight:600;font-size:.75rem;
  letter-spacing:.06em;text-transform:uppercase;
  color:var(--c-muted);opacity:.75}

.expotile__prov{font-weight:400;opacity:.72;text-transform:none;letter-spacing:0}

/* Dans le répertoire du plan, le jeton du numéro est étroit : la mention passe
   DESSOUS plutôt qu'à côté, sinon elle force la colonne à s'élargir sur toutes
   les lignes, y compris celles dont le numéro sera confirmé. */
.stand__prov{display:block;font-weight:400;font-size:.6875rem;opacity:.7;
  text-transform:none;letter-spacing:0;line-height:1.2;margin-top:1px}

/* ── Le renvoi Instagram d'une tuile d'exposant ──────────────────────────
   Une icône seule, à 20 px, dans une cible tactile de 44 — le minimum WCAG
   2.5.8. Elle vit dans la légende, à côté du numéro de stand, et emprunte la
   couleur du texte : c'est un renvoi secondaire, pas un appel à l'action. */
/* ⚠ 28 px, PAS 44. La cible de 44 px (WCAG 2.5.5, niveau AAA) doublait la
   hauteur de la légende et déséquilibrait toutes les cartes de la grille. 28 px
   dépasse le minimum exigé par 2.5.8 — 24 px — et l'icône reste isolée dans sa
   rangée, sans voisin cliquable dont on risque de la confondre. */
/* ── La date, sous le titre de la modale ─────────────────────────────────
   Elle doit se lire AVANT le nombre de places : « quel jour » est la question
   qu'on se pose en arrivant ici, « combien de places » celle qu'on se pose
   ensuite. Même graisse que le titre, taille en dessous — c'est une précision
   du titre, pas une note. */
.modal__when{margin:2px 0 0;font-weight:700;color:var(--c-rojo-txt)}
.modal__when[hidden]{display:none}

.expotile__ig{display:inline-grid;place-items:center;width:28px;height:28px;
  flex:none;margin-left:auto;color:inherit;opacity:.62;
  transition:opacity .25s var(--ease-out)}
.expotile__ig svg{width:18px;height:18px;fill:currentColor}
.expotile__ig:hover,.expotile__ig:focus-visible{opacity:1}

.talleres__more,
/* Le bloc agenda emprunte la même respiration : l'aperçu et sa sortie sont le
   même geste d'un bloc à l'autre, et deux valeurs différentes se verraient
   quand on fait défiler de l'un vers l'autre sur l'accueil. */
.agenda__more{margin:var(--s-6) 0 0}

/* ── La section qui suit immédiatement une bannière ────────────────────
   La bannière EST l'ouverture de la page, pas un bloc autonome : la section
   qui la suit la continue. Elles s'additionnaient pourtant — 32 px sous la
   bannière plus 144 px au-dessus de la section — et le premier libellé
   flottait à près de 180 px, détaché de ce qu'il prolongeait.

   ⚠ On ne touche PAS à `--section-y` : cet espace est juste entre deux
   sections indépendantes, qui sont des chapitres différents. Ce n'est que
   l'enchaînement bannière → première section qui doit se resserrer, d'où le
   sélecteur d'adjacence plutôt qu'une valeur globale plus courte. */
.pbanner + .section{padding-block-start:var(--s-7)}

/* ══════════════════════════════════════════════════════════════════════
   LE MUR DE PARTENAIRES — plaques à gabarit fixe

   Les plaques valaient `height:auto;width:auto` : elles épousaient leur
   logo, donc six largeurs différentes sur une même rangée. L'œil lisait
   l'irrégularité avant de lire les marques.

   ⚠ SEULE LA PLAQUE EST UNIFORMISÉE, JAMAIS L'IMAGE. `--plate-h` et `--k`
   pilotent la hauteur du logo et portent l'équilibre optique — à hauteur
   égale, un mot-symbole allongé écrase un logo carré, d'où `prismatic .30`
   contre `ambassade 1`. Uniformiser aussi les images annulerait ce travail.

   La plaque devient un cadre régulier ; le logo s'y centre à sa taille
   optique. C'est la différence entre aligner des logos et aligner des boîtes.

   ⚠ LA PLAQUE RESTE BLANCHE. Les fichiers sont des `.webp` à fond blanc :
   une plaque crème ferait apparaître ce fond comme un rectangle plus clair à
   l'intérieur, ce qui est pire. Le jour où la CCI fournit des logos
   vectoriels ou monochromes, la plaque devient optionnelle — c'est écrit en
   tête du bloc d'origine.
   ══════════════════════════════════════════════════════════════════════ */

/* ⚠ RAPPORT PROCHE DE 1,7:1, PAS DE 2:1. Une carte deux fois plus large que
   haute laisse un logo carré — Aztlán, Ambassade, Renault — flotter au milieu
   de cent pixels de blanc, pendant que Prismatic et Alstom remplissent la
   leur. Le logo est déjà bridé en hauteur par le padding : c'est donc la
   LARGEUR qu'il fallait reprendre, pas la taille des images. */
.thanks__logos .brandmark[data-sponsor]{
  width:clamp(7.5rem,9.5vw,10rem);
  height:clamp(4.5rem,6vw,6rem);
  padding:clamp(.5rem,1vw,.85rem);
}

/* La rangée défilante garde sa hiérarchie : plaque plus petite, pas plus
   pâle — atténuer une plaque claire sur du bleu donne un gris sale.

   ⚠ 52 px DE PLAQUE N'EN LAISSAIENT QUE 31 AU LOGO. Le padding prenait
   20,5 px des 52, soit 39 % de la plaque, et `max-height:100%` (règle
   suivante) rabattait l'image sur ce qu'il restait. Tant que le bandeau ne
   portait que les cinq partenaires — des mot-symboles épais — ça passait.
   Branché sur les exposants, il a reçu des logos d'artisans : scripts
   déliés, filets, typographies légères. Mesuré sur blanc, huit des quinze
   tombaient sous 3:1 de contraste apparent.

   La cause n'était PAS la couleur. Kristilla descend à 4/255 sur son pixel
   le plus sombre, Mon Collectionneur à 26, Lili Carrillo à 34 : l'encre est
   noire. Ce sont les DÉLIÉS qui passaient sous le pixel et se moyennaient
   avec le fond. La taille est donc le bon remède, et le seul.

   Plaque relevée et padding resserré : le logo passe de 31,5 à ~45 px à
   1280, +42 %. La plaque reste plus petite que celle des partenaires
   (72-96 px) — la hiérarchie que ce bloc défend n'est pas entamée.

   ⚠ CE REMÈDE NE VAUT PAS POUR TOUS. Sundarata (or, pixel le plus sombre à
   109) et hilo y palma (vert sauge, 122) sont clairs par construction :
   aucune hauteur ne les rattrapera. C'est leur palette de marque, et on ne
   retouche pas le logo d'une marque pour le confort de notre fond. Ils
   resteront discrets ici, et lisibles sur la page Marcas, où leur nom est
   écrit à côté. */
.thanks__brandsrow .logo[data-sponsor]{
  width:clamp(7rem,8.6vw,8.75rem);
  height:clamp(3.75rem,4.6vw,4.75rem);
  padding:clamp(.35rem,.6vw,.55rem);
}

/* Le logo ne dépasse jamais son cadre, quelle que soit sa hauteur optique. */
.thanks__logos .brandmark[data-sponsor] img,
.thanks__brandsrow .logo[data-sponsor] img{
  max-width:100%;max-height:100%;
}

/* ⚠ LE MOT DU FOND RESTE REMPLI. Une version en contour
   (`-webkit-text-stroke` + `color:transparent`) a été essayée pour qu'il
   cesse de concurrencer les plaques : elle réglait la lisibilité mais vidait
   la bande de sa matière — le mot n'apportait plus la profondeur qui justifie
   sa présence. Le remplissage à 20 % est conservé, et c'est le gabarit fixe
   des plaques qui rend l'ensemble lisible : des boîtes régulières laissent
   voir le mot entre elles, là où des largeurs irrégulières le hachaient. */

@media(min-width:960px){
  /* ⚠ LE MOT DEVIENT UNE FORME, PLUS UN MOT.

     À 10rem, deux tiers du mot tenaient à l'écran : on en lisait des
     tranches entre les plaques — « OO KPATROCINA ». Ni texture, ni texte.
     Le tirer à 16rem ne laisse que deux ou trois lettres visibles, donc plus
     rien à déchiffrer : l'œil y voit une matière et passe aux logos.

     ⚠ L'OPACITÉ BAISSE AVEC LA TAILLE. Des lettres deux fois plus hautes
     couvrent bien plus de surface : à 20 % elles reprendraient exactement le
     poids qu'on vient de leur retirer. 16 % conserve le même gris perçu.

     `.thanks__marquee` porte déjà `overflow:hidden` : le mot est rogné par la
     bande et ne déborde pas sur le pied de page. */
  .thanks__row span{
    font-size:clamp(6rem,17vw,16rem);
    color:rgba(127,176,224,.16);
  }
}

/* ── LES PLAQUES ALLÈGENT LEUR BLANC ───────────────────────────────────
   Six aplats blancs sur du bleu nuit se lisent comme des trous découpés
   dans la bande. On ne peut pas les rendre translucides — voir ci-dessous —
   mais on peut les faire lire comme des OBJETS posés sur le fond plutôt que
   comme des trous : arête claire en haut, filet sombre imperceptible sur le
   pourtour, et une ombre plus profonde et plus douce qui les décolle.

   ⚠ LA PLAQUE DOIT RESTER D'UN TON PARFAITEMENT UNI — pas de dégradé.
   Quatre logos sur six embarquent un fond blanc opaque dans le fichier
   (Ambassade, Publicis, Alstom, et Renault une fois inversé). Le `multiply`
   plus bas fait disparaître ce fond DANS la couleur de la plaque : sur un
   dégradé, il ne pourrait se confondre qu'avec une seule de ses valeurs, et
   le rectangle réapparaîtrait ailleurs sur la carte.

   ⚠ L'EFFET VERRE DEMANDÉ EST IMPOSSIBLE AVEC CES FICHIERS, pas avec ce
   code. Vérifié pixel par pixel : seul Prismatic est transparent ; Aztlán
   est violet opaque, Renault noir opaque, les trois autres blancs opaques.
   Sur un panneau translucide, cinq logos sur six poseraient leur propre
   rectangle opaque dans le verre. Le jour où la CCI fournit des PNG ou des
   SVG détourés, la plaque devient optionnelle et le verre devient possible —
   c'est déjà demandé dans QUESTIONS-CLIENT-2026-08-14.md. */
.thanks__logos .brandmark[data-sponsor],
.thanks__brandsrow .logo[data-sponsor]{
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.95),
    inset 0 0 0 1px rgba(10,37,64,.07),
    0 18px 38px -22px rgba(0,0,0,.85);
}

/* ── LA PLAQUE PASSE DU BLANC AU CRÈME ─────────────────────────────────
   Le blanc pur n'existe nulle part ailleurs dans cette palette : c'est ce
   qui faisait détonner la rangée sur le bleu nuit. Le crème est le neutre
   du site, celui du hero, des sections claires et des étiquettes.

   ⚠ CE CHANGEMENT N'EST POSSIBLE QU'AVEC LE `multiply` CI-DESSOUS, et
   l'inverse est vrai aussi — l'un sans l'autre casse. Quatre logos sur six
   portent un fond blanc opaque dans le fichier : sur une plaque crème SANS
   multiply, ce blanc apparaîtrait comme un rectangle plus clair au milieu de
   la carte, ce qui serait pire que le blanc franc.

   Multiplier du blanc par le crème rend EXACTEMENT le crème : le fond du
   fichier se dissout dans la plaque. Vérifié par le calcul sur les six
   fichiers avant d'écrire :
     · les quatre fonds blancs      → rgb(247,241,228), soit la plaque même
     · le violet d'Aztlán            → écart 15/255, invisible à l'œil
     · le rouge Alstom, l'or Publicis → écart 7 et 8, invisible
   Le texte blanc d'Aztlán devient crème sur son violet : toujours lisible.

   ⚠ L'ORDRE COMPTE POUR RENAULT. Son fichier est blanc sur noir, corrigé
   par `filter:invert(1)` plus haut. Le navigateur applique les filtres AVANT
   le mode de fusion : il arrive donc au multiply déjà en noir sur blanc, et
   se dissout comme les autres. Retirer l'inversion casserait les deux. */
.thanks__logos .brandmark[data-sponsor],
.thanks__brandsrow .logo[data-sponsor]{
  background:var(--c-cream);
}

.thanks__logos .brandmark[data-sponsor] img,
.thanks__brandsrow .logo[data-sponsor] img{
  mix-blend-mode:multiply;
}

/* ── L'INTERTITRE DE JOUR DANS LE LISTING ──────────────────────────────
   Chaque atelier tournant les deux jours, le listing répète chaque titre.
   Sous un intertitre de jour, la répétition cesse d'être suspecte : le
   visiteur lit « le programme du samedi », puis « celui du dimanche ».

   Il occupe toute la largeur de la grille des billets et se détache par la
   casse et le filet, pas par la taille : c'est un repère, pas un titre. */
.tickets__day{
  margin:var(--s-6) 0 calc(var(--s-4) * -1);
  padding-bottom:var(--s-3);
  font-size:var(--t-label);letter-spacing:var(--ls-label);
  text-transform:uppercase;font-weight:800;
  color:var(--c-rojo-txt);
}
.tickets__day:first-child{margin-top:0}

/* « También el domingo 25 » — le lien entre deux représentations d'un même
   atelier, sur les billets du listing. Discret : c'est une information de
   secours, pas un argument de vente. Le point avant sert de puce, sans liste. */
.ticket__also{
  font-size:var(--t-small);
  color:var(--c-cream-2);
  opacity:.85;
}
.ticket__also::before{
  content:'↻';
  margin-right:.4em;
  opacity:.7;
}

/* Souche d'une séance qui ne se réserve plus — terminée, fermée, pas encore
   ouverte. Le rouge est réservé à l'action : sans action, il ment. */
.ticket__stub--off{
  background:var(--c-ink-3, #1d3a57);
  color:var(--c-muted-inv);
  cursor:default;
}
.ticket__stub--off:hover{background:var(--c-ink-3, #1d3a57)}
.ticket__stub--off .ticket__n{opacity:.45}

/* ── « MI PASE » ───────────────────────────────────────────────────────
   La réservation retrouvée par son lien. Sobre : c'est un justificatif,
   pas une page de vente. */
.pase{max-width:34rem}
.pase__state{
  font-size:var(--t-label);letter-spacing:var(--ls-label);
  text-transform:uppercase;font-weight:800;color:var(--c-rojo-txt);
  margin:0 0 var(--s-3)}
.pase__h{font-size:var(--t-h2);margin:0 0 var(--s-5)}
.pase__cancel{margin-top:var(--s-6);display:flex;flex-wrap:wrap;align-items:center;gap:var(--s-4)}
.pase__hint{font-size:var(--t-small);color:var(--c-muted)}
.pase__flash{
  margin:0 0 var(--s-6);padding:var(--s-4) var(--s-5);
  border-left:3px solid var(--c-rojo);background:rgba(220,28,36,.06);
  max-width:48ch}
.pase__empty{color:var(--c-muted);max-width:48ch}

/* Une réservation annulée reste lisible mais cesse d'être une promesse. */
.pase--off .pase__state{color:var(--c-muted)}
.pase--off .pase__h{opacity:.65}

/* Le formulaire de renvoi, sur « Mi pase » sans réservation trouvée. */
.pase__resend{max-width:26rem;margin-top:var(--s-6);
  padding-top:var(--s-6);border-top:1px solid var(--c-line)}
.pase__resend-t{font-weight:800;margin:0 0 var(--s-2)}
.pase__resend-d{color:var(--c-muted);font-size:var(--t-small);margin:0 0 var(--s-4);max-width:38ch}
.pase__resend .field{margin-bottom:var(--s-4)}

/* La sortie proposée quand la place est déjà réservée. */
.modal__exit{margin:0 0 var(--s-4)}

/* ── Le boletín du pied de page ────────────────────────────────────────
   Deux éléments côte à côte au-delà de 480 px, empilés en dessous : sur un
   téléphone, un champ de saisie partagé avec un bouton devient trop étroit
   pour lire ce qu'on tape. */
.boletin{display:grid;gap:var(--s-3);margin-top:var(--s-4);max-width:26rem}
.boletin__in{
  width:100%;padding:.7rem .9rem;
  background:rgba(247,241,228,.08);
  border:1px solid rgba(247,241,228,.28);
  border-radius:var(--r-pill);
  color:var(--c-cream);font:inherit}
.boletin__in::placeholder{color:rgba(247,241,228,.5)}
.boletin__in:focus-visible{outline:2px solid var(--c-cci-lite);outline-offset:2px}
.boletin__ok,.boletin__err{margin-top:var(--s-4);font-size:var(--t-small);max-width:26rem}
.boletin__ok{color:var(--c-cream)}
.boletin__err{color:var(--c-coral)}

@media(min-width:480px){
  .boletin{grid-template-columns:1fr auto;align-items:center}
}

/* Une journée terminée le dit sur sa puce : sans ce repère, cliquer dessus
   ouvre une liste entièrement grise sans qu'on comprenne pourquoi. */
.chip--done{opacity:.55}
.chip--done::after{content:' ·';opacity:.6}

/* ── LE BILAN D'ÉDITION ────────────────────────────────────────────────
   Il ne s'affiche qu'après le festival. Ton différent du reste du site :
   on ne vend plus rien, on remercie. D'où le calme — pas d'accent rouge,
   pas d'appel à l'action rouge sauf l'aftermovie. */
.recap__stats{
  display:flex;flex-wrap:wrap;gap:var(--s-7);
  margin:var(--s-7) 0;padding-block:var(--s-6);
  border-block:1px solid var(--c-line)}
.recap__stat{margin:0;display:flex;flex-direction:column;gap:var(--s-1)}
.recap__stat b{
  font-family:var(--f-display);font-weight:900;
  font-size:clamp(2.4rem,6vw,4rem);line-height:.9;
  color:var(--c-rojo-txt)}
.recap__stat span{
  font-size:var(--t-label);letter-spacing:var(--ls-label);
  text-transform:uppercase;color:var(--c-muted)}

.recap__text{max-width:60ch}

/* Les temps forts. Même calme que le reste du bilan : pas de carte dessinee,
   pas d'ombre — une image, un titre, un paragraphe. Ce qui les separe est
   l'espace, pas un cadre. */
.recap__hl{
  list-style:none;margin:var(--s-7) 0 0;padding:0;
  display:grid;gap:var(--s-6)}
.recap__hl-i{display:grid;gap:var(--s-3);align-content:start}
.recap__hl-img{
  width:100%;height:auto;display:block;
  border-radius:var(--r-md);aspect-ratio:4/3;object-fit:cover}
.recap__hl-t{
  margin:0;font-family:var(--f-display);font-weight:800;
  font-size:var(--t-h3);line-height:1.2;letter-spacing:-.01em}
.recap__hl-x{color:var(--c-muted);max-width:52ch}
.recap__hl-x p{margin:0}
.recap__hl-x p + p{margin-top:var(--s-3)}
@media(min-width:760px){
  .recap__hl{grid-template-columns:repeat(auto-fit,minmax(15rem,1fr));gap:var(--s-6) var(--s-5)}
}

.recap__cta{margin-top:var(--s-6)}

.recap__gallery{
  margin-top:var(--s-7);
  display:grid;gap:var(--s-4);
  grid-template-columns:repeat(2,minmax(0,1fr))}
.recap__shot{margin:0}
.recap__shot img{
  width:100%;height:auto;display:block;
  border-radius:var(--r-md);aspect-ratio:1;object-fit:cover}

@media(min-width:900px){
  .recap__gallery{grid-template-columns:repeat(4,minmax(0,1fr))}
}

/* La navigation entre éditions, en bas d'une page de bilan. */
.recap__nav{margin-top:var(--s-7);padding-top:var(--s-6);border-top:1px solid var(--c-line)}
.recap__nav .label{margin-bottom:var(--s-3)}
.recap__nav a+a{margin-left:var(--s-3)}

/* ── LE NUMÉRO DE PLACE, SUR « MI PASE » ───────────────────────────────
   Il sera lu par deux personnes à la fois — le visiteur et l'accueil — sur
   un téléphone tenu à bout de bras, dehors, en octobre. D'où la taille et
   le contraste : c'est le seul élément de la page qui doit se lire à un
   mètre. */
.pase__seat{
  display:inline-flex;align-items:center;gap:var(--s-4);
  margin:0 0 var(--s-5);padding:var(--s-4) var(--s-5);
  background:var(--c-ink);color:var(--c-cream);
  border-radius:var(--r-md)}
.pase__seat-l{
  font-size:var(--t-label);letter-spacing:var(--ls-label);
  text-transform:uppercase;color:var(--c-cci-lite);font-weight:800}
.pase__seat b{
  font-family:var(--f-display);font-weight:900;
  font-size:clamp(2rem,7vw,2.8rem);line-height:1}

/* ═══════════════════════════════════════════════════════════════════════════
   EXPOSANTS — annuaire des marques

   Trois décisions, toutes prises après un rejet du 17/08.

   ① RIEN NE BOUGE AU CLIC. La tuile s'élargissait : tout ce qui suivait se
     décalait, et en grille la ligne entière se recomposait. Ici la tuile garde
     EXACTEMENT sa boîte — c'est son contenu qui change de face. Le regard ne
     perd jamais ce qu'il visait, et il n'y a aucun reflux à absorber.

   ② DEUX TONS, PAS CINQ. Cinq teintes qui tournent faisaient un patchwork dès
     qu'on en voyait dix. L'alternance encre / crème tient la composition, et
     rend au rouge son rôle d'accent au lieu d'en faire une couleur de plus.

   ③ GRILLE SUR LA PAGE, BANDE SUR L'ACCUEIL. Une page de listing se balaie —
     on compare, on cherche, on veut l'ensemble. Une section d'accueil attire —
     elle montre et laisse deviner. Le même motif aux deux endroits desservait
     les deux.
   ═══════════════════════════════════════════════════════════════════════════ */

.expo__list{list-style:none;margin:var(--s-7,2.5rem) 0 0;padding:0}

/* ── La grille ── */
/* ⚠ SUR TÉLÉPHONE, CE N'EST PAS UNE GRILLE — C'EST UNE LISTE.
   Mesuré : à deux colonnes sur un écran de 375 px, une tuile fait ~160 px de
   large. Le nom s'y casse en trois ou quatre lignes, et la face arrière devient
   une boîte où la description doit défiler pour se lire. Une grille de carrés
   sert à comparer des IMAGES ; un annuaire, ça se balaie.

   D'où une seule colonne en bandeaux courts et larges (5/2) : le nom tient sur
   une ligne, la description a la largeur de l'écran, et la cible tactile fait
   toute la largeur — impossible à manquer au pouce. Même langage visuel que
   l'ordinateur, comportement de liste. */
.expo__list--grid{
  display:grid;
  grid-template-columns:1fr;
  gap:var(--s-4);
}
/* ⚠ LES PROPORTIONS FIGÉES SONT PARTIES LE 19/08/2026 : 5/2 puis 4/5, des
   cadres réservés pour une PHOTO (`tile_image`) que zéro marque sur treize ne
   possède. Mesuré : 314 × 393 px, un logo de 112 × 48 en haut, le nom en bas —
   soit ~250 px de vide au milieu, treize fois.

   ⚠ MAIS SUR TÉLÉPHONE LA CARTE SE COUCHE, sinon on perd au change : sans la
     proportion, chaque carte passait de ~140 à 297 px et l'annuaire gagnait
     1 549 px. Le principe posé en août tient (« sur téléphone, ce n'est pas
     une grille, c'est une LISTE ») — il s'obtient maintenant par la mise en
     page, pas par un cadre vide : logo à gauche, texte à droite. */
.expo__list--grid .expotile .expotile__box{
  grid-template-columns:auto minmax(0,1fr);
  align-items:center;justify-items:start;
  text-align:left;gap:var(--s-5);
  min-height:0;padding-block:var(--s-3);
}
.expo__list--grid .expotile .expotile__face{
  justify-items:start;text-align:left;max-width:none;
}
.expo__list--grid .expotile__name{max-width:24ch}
/* ⚠ « MARCA » NE S'AFFICHE PAS DANS L'ANNUAIRE COUCHÉ. Le bandeau dit ce qu'on
   REGARDE : utile quand la carte est un objet qu'on prend une par une, inutile
   — et bruyant — quand treize lignes portent le même mot l'une sous l'autre.
   C'était l'argument du commentaire d'août, et il vaut toujours ici. */
.expo__list--grid .expotile__kind{display:none}

/* Au-delà, la grille reprend ses droits : l'écran est assez large pour que
   plusieurs tuiles se comparent d'un regard. */
@media (min-width:640px){
  .expo__list--grid{grid-template-columns:repeat(2,1fr);gap:var(--s-5) var(--s-4)}
  /* Redressées : plusieurs cartes de front, chacune dans la composition
     centrée de l'accueil — même objet, deux arrangements. */
  .expo__list--grid .expotile .expotile__box{
    grid-template-columns:none;
    justify-items:center;text-align:center;
    gap:var(--s-4);
    min-height:clamp(8rem,18vw,10rem);padding-block:var(--s-5);
  }
  .expo__list--grid .expotile .expotile__face{
    justify-items:center;text-align:center;max-width:24ch;
  }
  .expo__list--grid .expotile__name{max-width:none}
  /* Redressées et côte à côte, elles redeviennent des objets : le bandeau
     retrouve son emploi et tient le haut de la carte. */
  .expo__list--grid .expotile__kind{display:flex}
}
@media (min-width:900px){ .expo__list--grid{grid-template-columns:repeat(3,1fr);gap:var(--s-6) var(--s-5)} }
@media (min-width:1200px){ .expo__list--grid{grid-template-columns:repeat(4,1fr)} }

/* ── Les flèches de la bande ──
   Posées en haut à droite, alignées sur l'en-tête de section : elles se
   trouvent là où le regard revient après avoir lu le titre, et elles ne
   flottent pas au-dessus des tuiles comme des pastilles rapportées. */
.expo__nav{
  display:flex;gap:var(--s-2);
  justify-content:flex-end;
  margin-block-start:var(--s-5);
}
.expo__nav[hidden]{display:none}
.expo__arrow{
  /* 3 rem = 48 px : au-dessus du plancher de 44 px, parce qu'une flèche se
     vise en marchant, d'une main. */
  width:3rem;height:3rem;
  display:flex;align-items:center;justify-content:center;
  font-size:1.05rem;line-height:1;
  color:var(--c-ink);background:none;
  border:1px solid var(--c-line);border-radius:50%;
  cursor:pointer;
  transition:background .25s,color .25s,border-color .25s,opacity .25s;
}
.expo__arrow:hover:not(:disabled){
  background:var(--c-ink);border-color:var(--c-ink);color:var(--c-cream);
}
.expo__arrow:focus-visible{outline:3px solid var(--c-rojo);outline-offset:3px}
/* Désactivé : visiblement inerte, et le clavier le saute. */
.expo__arrow:disabled{opacity:.3;cursor:default}

/* ── La bande ── */
.expo__list--strip{
  display:flex;gap:var(--s-4);
  margin-inline:calc(var(--gutter) * -1);
  padding:0 var(--gutter) var(--s-5);
  overflow-x:auto;overscroll-behavior-x:contain;
  scroll-snap-type:x mandatory;
  scrollbar-width:thin;scrollbar-color:var(--c-line) transparent;
}
.expo__list--strip:focus-visible{outline:3px solid var(--c-rojo);outline-offset:4px;border-radius:var(--r-sm)}
.expo__list--strip::-webkit-scrollbar{height:5px}
.expo__list--strip::-webkit-scrollbar-thumb{background:var(--c-line);border-radius:99px}
.expo__list--strip .expotile{
  flex:0 0 auto;
  /* 72 vw sur téléphone : la tuile est assez grande pour que le nom se lise
     d'un coup, et il reste une tranche franche de la suivante — c'est elle qui
     dit « ça continue » et qui invite le pouce. */
  width:min(72vw,15.5rem);
  scroll-snap-align:start;
}
@media (min-width:640px){ .expo__list--strip .expotile{width:15.5rem} }

/* ── La tuile ── */
/* ⚠ L'ALTERNANCE ENCRE / CRÈME EST MORTE LE 19/08/2026, ET ELLE ÉTAIT CASSÉE.
   Mesuré sur `/expositores/` : le fond de page rendait `rgb(251,247,240)` et
   la tuile de ton « b » rendait `rgb(251,247,240)` — la MÊME valeur. Sept
   tuiles sur treize n'existaient pas à l'écran ; seul leur filet d'un pixel
   les trahissait. Sur les six autres, encre, on posait `logo_dark` — un logo
   dessiné POUR FOND CLAIR (plugin `brand.php:96`), donc illisible.
   Une surface blanche unique remplace l'alternance sur les DEUX dispositions :
   une seule carte, deux arrangements. (Elle a d'abord été un cycle de trois
   teintes claires — retiré le 19/08 : voir « LA SURFACE DE CARTE » plus bas,
   un fond coloré entrerait en concurrence avec les logos de marques.) */
.expotile{--tile-line:var(--c-line)}

.expotile__box{
  position:relative;
  aspect-ratio:4/5;
  background:var(--tile-bg);color:var(--tile-fg);
  border:1px solid var(--tile-line);
  border-radius:var(--r-md);
  overflow:hidden;
}

/* ⚠ LE BOUTON DE REPLI, SA FACE ARRIÈRE, LE NUMÉRO DE STAND EN SURIMPRESSION
   ET LE LIEN DE TROIS MOTS ONT ÉTÉ BALAYÉS LE 19/08/2026. Le comportement
   avait disparu la veille (render.php et main.js le disent) ; la feuille de
   style, elle, gardait `.expotile__toggle`, `.expotile__back`, `[data-open]`,
   `.expotile__stand` et `.expotile__link` — soixante lignes qu'aucun élément
   ne pouvait plus rencontrer, et que la personne suivante aurait lues comme
   une fonctionnalité à préserver. */

/* ── La face avant ── */
.expotile__face{
  position:absolute;inset:0;z-index:1;
  display:flex;flex-direction:column;justify-content:flex-end;
  padding:var(--s-5);
  transition:opacity .35s ease,transform .45s cubic-bezier(.22,1,.36,1);
}
.expotile__img{
  position:absolute;inset:0;z-index:-1;
  width:100%;height:100%;object-fit:cover;
  transition:transform .8s cubic-bezier(.22,1,.36,1);
}
.expotile__box:hover .expotile__img{transform:scale(1.04)}

.expotile__name{
  font-weight:400;text-transform:uppercase;
  font-size:clamp(1.25rem,2.6vw,1.75rem);
  line-height:.98;letter-spacing:-.02em;text-wrap:balance;
}
/* Le filet rouge : le seul mouvement au survol, et il se lit sur les deux tons. */
.expotile__name::after{
  content:'';display:block;height:2px;width:2rem;
  margin-block-start:var(--s-3);
  background:var(--c-rojo);
  transform:scaleX(.35);transform-origin:left;
  transition:transform .5s cubic-bezier(.22,1,.36,1);
}
.expotile__box:hover .expotile__name::after{transform:scaleX(1)}

.expotile__what{margin:0;font-size:.9375rem;line-height:1.5}
.expotile__what em{font-style:italic;opacity:.7}
/* ── La légende, hors de la tuile : toujours lisible, jamais masquée ── */
.expotile__caption{
  margin:var(--s-3) 0 0;
  display:flex;flex-wrap:wrap;gap:0 .5ch;
  font-size:var(--t-label);font-weight:600;
  letter-spacing:var(--ls-label);text-transform:uppercase;
  color:var(--c-muted);font-variant-numeric:tabular-nums;
}
/* ⚠ LE POINT MÉDIAN EST POSÉ ICI, PAS DANS LA CHAÎNE PHP. La légende est en
   deux morceaux (`render.php`) pour que le paquet puisse les écarter aux deux
   bords de la carte ; en grille ils restent une ligne suivie, et c'est ce
   pseudo-élément qui les relie. `:not(:empty)` parce qu'une marque sans zone
   commencerait sinon par « · Stand S07 ». */
.expotile__zone:not(:empty) + .expotile__no::before{content:'· '}

/* ── En-tête de section ── */
.expo__count{
  margin:var(--s-4) 0 0;
  font-size:var(--t-label);font-weight:700;
  letter-spacing:var(--ls-label);text-transform:uppercase;color:var(--c-muted);
}
.expo__count b{color:var(--c-rojo-txt);font-size:1.4em;font-variant-numeric:tabular-nums}

.expo__more{margin-block-start:var(--s-6,2rem);text-align:left}
.expo__all{
  display:inline-flex;align-items:center;gap:var(--s-3);
  font-size:.9375rem;font-weight:700;color:var(--c-ink);text-decoration:none;
  border-bottom:2px solid var(--c-rojo);padding-block-end:.15rem;
  transition:gap .3s cubic-bezier(.22,1,.36,1),color .3s;
}
.expo__all::after{content:'→';font-weight:400}
.expo__all:hover{gap:var(--s-4);color:var(--c-rojo-txt)}

@media (prefers-reduced-motion:reduce){
  .expotile__face,.expotile__img,.expotile__name::after,.expo__all{transition:none}
  .expotile__box:hover .expotile__img{transform:none}
  .expo__arrow{transition:none}
  .expo__list--strip{scroll-behavior:auto}
}

/* ═══════════════════════════════════════════════════════════════════════════
   INFO PRÁCTICA · FEUILLE MONTANTE SUR TÉLÉPHONE — 18/08

   Le motif desktop transposé : là-bas le panneau occupe la colonne de sa barre
   et vient s'y accoler ; ici il prend la largeur du socle et monte depuis lui.
   Mesuré dans Chrome à 440 px : socle 440 × 52, à ras du bas — « la largeur du
   bouton », c'est donc toute la largeur.

   ⚠ ESSAI CENTRÉ ABANDONNÉ (même journée). Une boîte au milieu de l'écran
     coupait le lien avec la barre qui l'ouvre, et éloignait la sortie du
     pouce. La feuille est le bon motif ; ce qui manquait n'était pas le
     centrage mais les trois défauts ci-dessous.

   ⚠ CE BLOC EST EN FIN DE FICHIER, ET C'EST DÉLIBÉRÉ. Il doit battre
     `.ov.is-practica:not(.is-menu){pointer-events:none}` et
     `.practica-open .header__inner{opacity:1}` — deux règles de MÊME
     spécificité, déclarées plus haut. À spécificité égale c'est l'ordre qui
     tranche : posé plus haut, ce bloc s'affichait barré dans l'inspecteur.
     Ne pas le remonter.
   ═══════════════════════════════════════════════════════════════════════════ */
@media(max-width:719px){

  /* ── ① Le voile redevient cliquable ────────────────────────────────────
     `pointer-events:none` est un choix d'origine assumé : sur grand écran la
     page reste utilisable DERRIÈRE le panneau, qui n'est pas modal. Sur
     téléphone il n'y a pas de « derrière » — un appui à côté ne rencontrait
     rien, et le seul moyen de sortir était la croix. */
  .ov.is-practica:not(.is-menu){pointer-events:auto}
  .ov.is-practica:not(.is-menu) .ov__practica{pointer-events:auto}

  /* ── ② L'en-tête s'efface ──────────────────────────────────────────────
     Logo et burger flottaient au-dessus de la feuille, avec la page qui
     transparaissait entre les deux : trois plans concurrents sur 440 px. */
  .practica-open .header__inner{opacity:0;pointer-events:none}
  .practica-open .header__banner{height:0;opacity:0}

  /* ── ③ La feuille ──────────────────────────────────────────────────────
     ⚠ `.ov` DOIT COUVRIR TOUT L'ÉCRAN. Il est décalé ailleurs — `top:var(--s-6)`
       puis `top:var(--header-h)` à l'ouverture, pour laisser l'en-tête visible.
       Celui-ci étant masqué, ce décalage n'a plus d'objet — et il rognait la
       hauteur disponible de la feuille de 108 px. */
  .ov,
  .practica-open:not(.menu-open) .ov,
  .practica-open:not(.menu-open) .header.is-solid ~ .ov{inset:0;padding:0}

  .ov__practica,
  .practica-open .ov__practica,
  .menu-open .ov__practica{
    position:absolute;
    left:0;right:0;bottom:0;top:auto;
    width:100%;max-height:82svh;
    /* Coins hauts seulement : le bas est soudé au bord de l'écran, et le
       socle vient flotter par-dessus. */
    border-radius:var(--r-lg) var(--r-lg) 0 0;
    transform:translateY(100%);
    /* ⚠ COURBES ASYMÉTRIQUES, CALQUÉES SUR LA FICHE DE RÉSERVATION — c'est
       elle la référence, et la comparaison était mesurable :
         entrée  `expo.out`  (0,7 s) — arrive vite puis se pose
         sortie  `power2.in` (0,45 s) — part lentement puis accélère
       La feuille utilisait `--ease-io` dans les deux sens : elle FREINE juste
       avant de disparaître, ce qui se lit comme une hésitation. Une fermeture
       doit accélérer en partant, sinon on la regarde partir.

       ⚠ PLUS D'OPACITÉ ICI. Elle avait été ajoutée pour l'essai CENTRÉ, où la
         boîte ne parcourait que 24 px et restait visible quand le script
         reposait `hidden`. La feuille, elle, sort entièrement de l'écran :
         le retrait du DOM 300 ms plus tard ne se voit pas. La modale ne fait
         d'ailleurs rien d'autre — seul son voile s'estompe. */
    transition:transform .55s var(--ease-out);
  }
  .ov.is-practica .ov__practica{transform:none}
  .ov.is-closing .ov__practica{
    transform:translateY(100%);
    transition:transform .45s cubic-bezier(.55,.085,.68,.53);
    transition-delay:0s;
  }

  /* Le voile suit la même horloge que celui de la fiche de réservation :
     0,3 s à la fermeture, contre 0,5 s partout ailleurs. Un fond qui
     s'attarde après le départ de la feuille laisse l'écran gris une
     demi-seconde de trop, et c'est ça qu'on lit comme « pas fluide ». */
  .ov.is-closing::before{transition:opacity .3s var(--ease-out)}

  /* La poignée : le signal d'une feuille glissante. Sans elle, un panneau
     ancré en bas se lit comme un défaut de centrage. */
  .ov__sheethead{position:relative}
  .ov__sheethead::before{
    content:'';position:absolute;top:6px;left:50%;translate:-50% 0;
    width:2.25rem;height:4px;border-radius:99px;background:var(--c-line);
  }
}

/* ══════════════════════════════════════════════════════════════════════════
   LE MOUVEMENT « PRÉPARER » — UN SEUL SOL POUR TROIS SECTIONS
   Ajouté le 18/08/2026.

   ⚠ CE BLOC RESTE EN FIN DE FICHIER. Il doit battre les fonds posés par les
   règles de section plus haut, qui ont la même spécificité (0,1,0) : à égalité,
   c'est l'ORDRE qui tranche. Le remonter le rendrait inopérant sans erreur —
   piège déjà rencontré deux fois sur ce projet.

   POURQUOI. La page comptait 14 sections sur 14 en crème. L'alternance était
   pourtant l'intention d'origine — voir l'en-tête de ce fichier, « crème ·
   ROUGE · crème · SOMBRE… », et `--c-ink-2` que tokens.css nomme encore
   « bandes de contraste : zonas, patrocinadores ». Elle s'est aplatie.

   CE QUE LE FOND FAIT. Práctica, Plano et FAQ ne sont pas trois sujets à
   séparer les uns des autres : c'est UN mouvement — tout ce qu'il faut savoir
   pour venir. Le sol les relie entre elles et les sépare de Talleres, qui
   vend. La coupure visible tombe donc aux deux extrémités du groupe, jamais
   à l'intérieur.

   ⚠ PATROCINADORES N'EST PAS PASSÉ AU BLEU, ET C'EST DÉLIBÉRÉ. La section a
   été repassée au clair exprès (« ② LA SECTION ALLIÉS PASSE AU CLAIR », plus
   haut) : trois des six logos partenaires ont un fond BLANC opaque que
   `multiply` détoure gratuitement sur crème. Sur bleu, il faudrait remettre
   les plaques. Le pied de page, lui, est déjà bleu CCI — la fermeture
   institutionnelle existe, elle commence un cran plus bas.
   ══════════════════════════════════════════════════════════════════════════ */
/* ⚠ LE JETON EST DÉCLARÉ ICI, PAS DANS `assets/css/tokens.css`.
   Ce fichier-là N'EST PLUS CHARGÉ — functions.php:210 le dit en toutes
   lettres : la palette vit désormais dans le build Tailwind
   (`dist/css/output-child.css`). Y ajouter une variable ne fait donc rien,
   et `background:var(--c-sand)` non résolu ne tombe pas en erreur : il rend
   la section TRANSPARENTE. Panne silencieuse, constatée à la mesure.

   Déclarer le jeton dans `main.css`, qui est chargé APRÈS le build, le rend
   effectif sans passer par une recompilation. À remonter dans la source
   Tailwind (`src/css/style.css`) au prochain build, et à retirer d'ici. */
/* ⚠ ASSOMBRI LE 18/08 (2ᵉ passe) : #F2EBDA → #EFE6D2. Le premier ton tenait
   à l'écran mais pas au vidéoprojecteur — 10 points de luminance sous la
   crème, soit le plancher du perceptible. La conférence de presse du
   23 septembre se donne en salle : un rythme qui s'aplatit là est un rythme
   qui n'existe pas le jour où il compte. On passe à 13,7 points.

   ⚠ ET LE GRIS SECONDAIRE DESCEND AVEC. Mesuré : `--c-muted` (#5A6B7D) tombe
   à 4,41:1 sur le nouveau sable — SOUS le seuil de 4,5. Assombrir un sol sans
   assombrir le texte qui s'y pose, c'est acheter du rythme avec de la
   lisibilité. Le gris ne change QUE dans ces sections, et il garde sa teinte
   bleutée : #576779, 4,67:1. */
:root{--c-sand:#EFE6D2}
.section--prep,.tiltp{--c-muted:#576779}

.section--prep{background:var(--c-sand)}

/* Le plan est un aplat #EFE7D6 : sur le sable il n'est plus qu'à deux pas et
   sa boîte se dissout. Un filet intérieur la rend au dessin, sans inventer
   une troisième teinte. */
/* La carte suit le sol pour garder son creux : sans ça, #EFE7D6 passait au
   DESSUS du nouveau sable et l'encart s'inversait en panneau plus clair. */
.section--prep .plano__map{background:#E9DFC6;box-shadow:inset 0 0 0 1px var(--c-line)}

/* Les jetons de compte et de stand tirent leur fond du gris de l'encre ; sur
   sable ils se rapprochent du sol. On les assombrit d'un cran — même couleur,
   plus d'opacité, donc rien de neuf dans la palette. */
.section--prep .stand__no,
.section--prep .plano__legend .zn{background:rgba(20,16,14,.10)}

/* ── LE SOL QUI RÉVÈLE LE PANNEAU INCLINÉ SUIT LE MOUVEMENT ──────────────
   Ajouté le 18/08/2026, en correction du sable posé plus haut.

   `.tiltp` est volontairement clair : « à chaque degré de rotation, les angles
   découvrent du crème, et c'est ce contraste qui dessine la forme » (voir le
   commentaire de la règle d'origine). Ce sol n'est pas là pour dire à quel
   mouvement la section appartient — il est là pour RÉVÉLER L'INCLINAISON.

   Le problème introduit par `.section--prep` : le sable s'arrêtait à `faq`,
   et les ~60 px de crème qui précèdent le panneau bleu se lisaient comme un
   liseré oublié entre deux aplats. Trois tons en 60 px, après 2 785 px de
   sable — c'est exactement ce qu'on voyait.

   Le sable reprend donc ce rôle : même fonction, même contraste (les deux
   sont des sols clairs face à un panneau azul), un ton de moins. La coupure
   du récit tombe alors sur `semana`, une section pleine largeur sans
   géométrie penchée à traverser — là où une coupure se pose proprement.

   ⚠ CE N'EST PAS UNE EXTENSION DU MOUVEMENT « PRÉPARER ». D'où une règle
   séparée plutôt que la classe `.section--prep` : Embajadores appartient à
   la signature institutionnelle. Seule sa BORDURE emprunte le sol précédent,
   et elle n'est visible que sur ~130 px des 1 658 de la section — le panneau
   couvre le reste.
   ⚠ EN FIN DE FICHIER À DESSEIN : trois règles `.tiltp` posent déjà un fond,
   toutes en (0,1,0). À spécificité égale, c'est l'ordre qui tranche.

   ── LA SECTION EST UNE CHARNIÈRE ────────────────────────────────────────
   Un sol uniforme ne réglait qu'à moitié : il fallait ensuite basculer vers
   le crème quelque part, et cette coupure retombait sur `semana`.

   La section devient donc le PASSAGE lui-même. On y entre en sable — dans la
   continuité de FAQ, juste au-dessus — et on en sort en crème, prêt pour
   `semana`. Le dégradé s'opère derrière le panneau : il n'existe aucun bord
   visible entre les deux tons, nulle part sur la page.

   Les arrêts sont en `rem`, pas en `%`, et calés sur le `inset:3.5rem` du
   panneau : les 56 px de sol exposés en haut sont TOUJOURS du sable pur, les
   56 px du bas TOUJOURS du crème pur, quelle que soit la hauteur de la
   section. En pourcentage, une section courte aurait fait remonter la
   transition dans la bande visible.

   Le panneau est un trapèze incliné (`clip-path` + `rotate`) : ses bords
   laissent voir le sol sur toute la hauteur. Le glissement s'y lit comme une
   lumière qui change, jamais comme une limite. */
.tiltp{
  background:linear-gradient(to bottom,
    var(--c-sand)  0 6rem,
    var(--c-cream) calc(100% - 6rem) 100%)}

/* ══════════════════════════════════════════════════════════════════════════
   PRÁCTICA · LE PANNEAU DES TROIS FAITS
   Posé le 18/08/2026, après une première tentative ratée (voir `.fact--key`).

   ⚠ CE N'ÉTAIT PAS UN PROBLÈME DE TAILLE. Práctica ne portait AUCUN objet
   coloré, alors que toutes ses voisines en ont un. On avait d'abord grossi un
   seul fait à 44 px : la section n'est pas devenue attirante, une cellule est
   devenue bruyante.

   DEUX ANCRES, PAS UNE. La date et la gratuité pèsent le même poids : l'une est
   ce qu'on retient, l'autre ce qui décide. Le lieu reste en référence entre les
   deux — c'est exactement ce qui manquait à la version précédente, où un fait
   hurlait au milieu de deux autres restés à 17 px.

   ⚠ SUR ENCRE, LE PROBLÈME DE CONTRASTE DISPARAÎT. Un voile rouge sur le sable
   avait été écarté parce qu'il faisait tomber le gris à 4,28:1. Ici tout est
   mesuré et large : crème 13,80:1 · corail 5,56:1 · pêche 10,46:1 ·
   crème-2 10,89:1. Et le corail retrouve le rôle que tokens.css lui donne —
   « urgence », c'est-à-dire ce qui fait agir.

   ⚠ EN FIN DE FICHIER : `.practica__facts` reçoit un `repeat(3,1fr)` plus haut,
   à spécificité égale. L'ordre tranche — piège rencontré trois fois ici.
   ══════════════════════════════════════════════════════════════════════════ */
.practica__facts{
  background:var(--c-ink);color:var(--c-cream);
  border:0;border-radius:var(--r-lg);
  padding:clamp(var(--s-5),3vw,var(--s-7));
  gap:0}
@media(min-width:1000px){
  .practica__facts{grid-template-columns:1.15fr 1fr 1.15fr}
}

/* Les filets passent en version inversée ; le panneau porte ses propres bords,
   donc les cellules n'en ont plus. */
.practica__facts .fact{border-bottom:0;padding-block:var(--s-3)}
@media(min-width:761px){
  .practica__facts .fact + .fact{border-left:1px solid var(--c-line-inv)}
}
@media(max-width:760px){
  .practica__facts .fact + .fact{border-top:1px solid var(--c-line-inv);
    padding-top:var(--s-5);margin-top:var(--s-2)}
}

.practica__facts .fact__k{color:var(--c-peach)}
.practica__facts .fact__v{color:var(--c-cream)}
.practica__facts .fact__n{color:var(--c-cream-2)}

/* LES DEUX ANCRES.

   ⚠ TAILLE RABATTUE APRÈS MESURE : 2,25 rem (36 px) cassait les DEUX ancres en
   trois lignes dans une colonne de 380 px — « 24 y 25 de / octubre de / 2026 »
   et « Entrada libre / — sin / boleto ». Une accroche qui se lit en trois temps
   n'accroche plus. À 1,75 rem elles tiennent en deux lignes, et l'écart avec le
   lieu (17 px) reste franc.

   `text-wrap:balance` : à cette taille, une veuve d'un mot abîme la ligne bien
   plus que le gain de place ne la sert. */
.practica__facts .fact:first-child .fact__v,
.practica__facts .fact--key .fact__v{
  font-size:clamp(1.3rem,2.2vw,1.75rem);line-height:1.1;
  letter-spacing:-.02em;text-wrap:balance}

/* ⚠ LE CORAIL DOIT ÊTRE ÉCRIT ICI, pas sur `.fact--key .fact__v` plus haut.
   Les deux sélecteurs pèsent (0,2,0) ; `.practica__facts .fact__v{color:crème}`
   vient après et gagnait. Mesuré : la cellule ressortait en crème comme les
   autres, et l'accent disparaissait. Trois classes, (0,3,0).
   Corail sur l'encre réelle du build (#002A4F) : 5,21:1. */
.practica__facts .fact--key .fact__v{color:var(--c-coral)}

/* ── PRÁCTICA · LE BLOC LOGISTIQUE DEVIENT UN OBJET ───────────────────────
   Ajouté le 18/08/2026, après le panneau des faits.

   Le panneau encre au-dessus porte la PROMESSE ; « Cómo llegar » porte la
   LOGISTIQUE. Une fois le panneau posé, ce second bloc restait du texte nu sur
   le sable et se lisait comme une annexe — adresse, transports, accessibilité
   et boutons empilés au même rythme, sans regroupement.

   ⚠ IL NE DOIT PAS RIVALISER AVEC LE PANNEAU. D'où une carte CRÈME, pas un
   second aplat sombre : deux objets de poids différents — l'un affirme, l'autre
   organise. Aucune couleur nouvelle, aucune taille augmentée : la structure
   suffit, et c'est ce qui manquait.

   ⚠ EN FIN DE FICHIER, comme le reste du bloc : `.practica__how` reçoit ses
   règles de grille plus haut, à spécificité égale. */
.practica__how{
  background:var(--c-cream);
  border-radius:var(--r-lg);
  padding:clamp(var(--s-5),3vw,var(--s-7));
  }

/* Chaque colonne porte un filet de tête : il donne un début visible à chacune
   des deux moitiés, là où le seul écart de grille les laissait flotter. */
.practica__howcol{border-top:1px solid var(--c-line);padding-top:var(--s-4)}

/* L'adresse est la seule chose qu'on RECOPIE dans une application de course.
   Elle cesse d'être une note grise parmi d'autres. */
.practica__how-addr{color:var(--c-ink);font-weight:600}

/* Les boutons ferment le bloc sur toute sa largeur, sous un filet : ils ne
   sont plus l'appendice de la colonne « Accesibilidad ». */
.practica__how .practica__cta{
  grid-column:1/-1;margin-top:var(--s-5);
  padding-top:var(--s-5);border-top:1px solid var(--c-line)}

/* ── PRÁCTICA · LES LIGNES `CLÉ: VALEUR` S'ALIGNENT ───────────────────────
   Ajouté le 18/08/2026. Champs remplis, « Cómo llegar » devenait douze lignes
   grises identiques : rien à accrocher, rien à parcourir. La CCI écrit
   naturellement « Metro: Línea 7… » — on rend cette structure lisible au lieu
   de la noyer dans un paragraphe.

   Une grille à deux colonnes plutôt qu'un simple gras : les clés s'alignent, et
   c'est l'ALIGNEMENT qui fait qu'on parcourt une liste sans la lire. `auto`
   plafonné à 14ch — au-delà, une clé longue mangerait la valeur ; en dessous,
   « Estacionamiento PMR » se couperait. */
/* ⚠ 1ʳᵉ VERSION EN DEUX COLONNES, ABANDONNÉE LE JOUR MÊME.
   Mesuré : les colonnes tombaient à 120 px et 142 px dans une carte de 520,
   parce que `.practica__how-n` porte un `max-width:32ch` qui étranglait toute
   la grille. Résultat — des valeurs sur trois lignes et deux clés cassées en
   deux (« Sanitarios / accesibles », « Estacionamiento / PMR »). C'est ça qui
   se lisait comme du texte coupé.

   ⚠ ET LE TABLEAU N'ÉTAIT PAS LE BON MOTIF. Une colonne de clés alignées
   suppose des clés de longueur comparable ; ici elles vont de « Metro » à
   « Sanitarios accesibles ». La paire EMPILÉE — surtitre puis valeur — ne peut
   pas se casser, tient la même forme du téléphone au grand écran, et c'est
   déjà la langue de la section : le panneau encre au-dessus fonctionne
   exactement ainsi (`fact__k` puis `fact__v`). Une seule grammaire, deux
   échelles. */
.practica__line{display:block;max-width:46ch}
.practica__line b{
  display:block;
  font-size:var(--t-label);letter-spacing:var(--ls-label);
  text-transform:uppercase;font-weight:700;
  color:var(--c-muted);margin-bottom:2px}
.practica__line span{display:block;color:var(--c-ink)}

/* Les paires respirent : sans cet écart elles forment un pavé où le surtitre
   de l'une touche la valeur de la précédente. */
.practica__line + .practica__line{margin-top:var(--s-3)}

/* ── LE MOUVEMENT « SIGNER » SE REFERME EN UN SEUL BLOC ───────────────────
   Ajouté le 18/08/2026.

   `semana` est la plus petite section de la page — 343 px — coincée entre le
   panneau bleu incliné des ambassadeurs et le mur des partenaires. Trois temps
   institutionnels dont le plus faible au milieu : elle se lisait comme une
   couture entre deux blocs forts, alors qu'elle porte le seul argument
   d'échelle d'une première édition — « ce festival est un chapitre d'un
   programme national, trois villes, d'octobre à décembre ».

   Le sujet n'était pas son contenu mais son ISOLEMENT. En supprimant l'espace
   qui la sépare de Patrocinadores, les deux ne font plus qu'un mouvement : qui
   cadre, qui soutient. Elle cesse d'être un intervalle pour devenir l'ouverture
   de la signature institutionnelle.

   ⚠ ON NE TOUCHE PAS À SON ESPACE DU HAUT : la respiration après le panneau
   incliné doit rester, sinon la bande bleue vient buter sur du texte.

   ⚠ EN FIN DE FICHIER : `.semana` pose son `padding-block` plus haut, à
   spécificité égale. L'ordre tranche. */
.semana{padding-bottom:0}
.sponsors{padding-top:clamp(2rem,4vh,3rem)}

/* ── LE RACCORD SEMANA → PATROCINADORES ──────────────────────────────────
   Ajouté le 18/08/2026, en correction du simple rapprochement de la veille.

   ⚠ COLLER N'EST PAS LIER. Supprimer l'espace entre les deux sections les
   avait mises bord à bord sans rien raconter : deux ouvertures de section
   empilées — surtitre, titre, sous-titre, puis surtitre, titre, sous-titre.
   Le surtitre de Patrocinadores a été vidé (voir le contenu de la page) ; ce
   qui suit fait le reste du travail.

   Le lisené qui court d'une section à l'autre est le SEUL élément qui traverse
   la limite. C'est lui qui dit « c'est encore la même idée » là où deux titres
   disaient « nouveau chapitre ». Il reprend le filet des faits (`--c-line`),
   déjà le vocabulaire de la page pour séparer sans couper. */
.semana{position:relative}
.semana::after{
  content:'';position:absolute;left:var(--gutter);right:var(--gutter);
  bottom:0;height:1px;background:var(--c-line)}

/* Le titre reprend l'air que le surtitre disparu lui laissait : sans cet
   ajustement il remonterait contre le filet.

   ⚠ CET ÉCART SE MESURE EN VW, PAS EN VH — corrigé le 19/08/2026.
   En `vh` il valait 61 px à toute largeur, alors que le titre qu'il doit
   dégager passe de 42 px (390) à 90 px (1440). Résultat : confortable en
   bas de page mobile (1,5 fois la taille du titre), serré sur desktop
   (0,7 fois). Un grand titre réclame de l'air EN PROPORTION de son corps,
   et ce corps suit la largeur.
   Le calc reproduit la droite 45 px @390 → 83 px @1440, soit un rapport
   qui reste au-dessus de 1 partout.

   ⚠ ON NE REVIENT PAS À UN ÉCART DE SECTION (144 px) : `semana` et
   `patrocinadores` ont été SOUDÉS exprès le 18/08 en un seul mouvement —
   voir « LE MOUVEMENT SIGNER » plus haut. Rouvrir la couture rendrait à
   `semana` son statut d'orpheline entre deux blocs forts. */
.sponsors{padding-top:clamp(2.8rem,1.9rem + 3.7vw,5.2rem)}

/* ── LA TUILE EXPOSANT DIT CE QU'ELLE VEND ────────────────────────────────
   19/08/2026. Le repli est parti avec sa prémisse : les exposants d'exemple
   s'appelaient « Quesos y charcutería », le nom faisait la description. Les
   treize marques réelles s'appellent BISH, CLEA, MATRIBU — la face ne dit plus
   rien sans sa ligne.

   Le nom garde son échelle d'affiche ; la description s'installe dessous, en
   corps de lecture. Ce n'est pas une légende : avec ces noms-là, c'est
   l'information principale. */
.expotile__what{
  display:block;margin-top:var(--s-3);
  font-family:var(--f-sans);font-weight:400;text-transform:none;
  font-size:var(--t-small);line-height:1.35;letter-spacing:0;
  color:inherit;opacity:.82}

/* Le renvoi vers le site de la marque. La tuile ENTIÈRE est le lien : ceci
   n'est qu'un repère visuel, jamais une seconde cible. */
/* ══ HIÉRARCHIE DE LA CARTE D'EXPOSANT ═════════════════════════════════
   Le nom était composé en 20 à 28 px, plus gros que tout le reste — juste
   sous un logo qui dit déjà la même chose. Deux fois la même information, et
   la plus grande des deux était la redondante. La description, elle, plafonnait
   à 15 px alors qu'elle porte la seule chose qu'on ne devine pas : ce que la
   marque vend.

   ⚠ MASQUER LE NOM A ÉTÉ ESSAYÉ, PUIS ÉCARTÉ le 04/09. Tous les logos ne sont
     pas des logotypes : celui de Tierra Baldía est une image presque carrée.
     Une carte sans nom lisible oblige à reconnaître une marque à son symbole —
     et sur une page dont le métier est « trouver celle que je cherche », un mot
     se repère plus vite qu'un dessin, parce que les dessins varient en taille,
     en couleur et en lisibilité.

   Le nom RESTE donc, en légende du logo : il ne concurrence plus, il situe.
   L'ordre de lecture devient logo → nom → ce qu'ils vendent → l'action. */
/* ⚠ TROIS SÉLECTEURS, ET C'EST NÉCESSAIRE — 0,3,0 contre les 0,2,0 de
   `.expotile .expotile__name`, déclarée 300 lignes PLUS BAS. Deux tentatives
   à spécificité inférieure ou égale ont échoué : le poids et l'opacité
   prenaient, la taille non, parce qu'elle est la seule des trois à être
   redéfinie là-bas. Mesuré à chaque fois : 20,8 px pour 12 attendus.

   Monter d'un cran rend la règle indépendante de sa position dans la feuille —
   sinon toute réorganisation future la redésarmerait en silence. */
/* ⚠ .72, PAS .6 — 25/09/2026. À .6, l'encre sur blanc donne 4,2:1 : sous le
   seuil AA (4,5:1) pour un texte de 12 px, gras ou pas. Le nom de la marque
   était le texte le moins lisible de la carte, sous un mot « MARCA » à 7,3:1.
   À .72 : 6,0:1. Partagé grille + paquet, à dessein : même défaut. */
.expotile .expotile__name.expotile__name--sous{
  font-size:var(--t-label);
  letter-spacing:var(--ls-label);
  font-weight:700;
  opacity:.72;
  margin-bottom:2px}

/* Sans vrai logo, le nom REDEVIENT l'identité de la carte : la règle ci-dessus
   ne s'applique qu'aux quatre marques encore sous plaque générique par leur
   absence, jamais par une exception écrite ici. */

/* La description reprend le rôle qu'elle aurait dû garder : c'est le seul
   texte qui apprenne quelque chose. Un cran de plus, et la couleur pleine. */
.expotile__what{color:var(--c-ink)}
.expo__list--grid .expotile__what{font-size:1rem;line-height:1.55}

.expotile__go{
  display:block;margin-top:var(--s-3);
  font-size:var(--t-label);letter-spacing:var(--ls-label);
  text-transform:uppercase;font-weight:700;opacity:.75}

/* `.expotile__box` peut désormais être un `<a>`. Sans cette remise à plat il
   hériterait du soulignement et de la couleur de lien du thème parent. */
a.expotile__box{text-decoration:none;color:inherit;display:block}
a.expotile__box:hover .expotile__go{opacity:1}

/* ══════════════════════════════════════════════════════════════════════════
   EXPOSANTS · LA SCÈNE — LE TEASER DE L'ACCUEIL
   Refaite le 19/08/2026. La pile de la veille est remplacée, pas retouchée.

   ⚠ CE QUI N'ALLAIT PAS, ET QUI SE MESURE. La pile héritait de
     `.expotile__box{aspect-ratio:4/5}` sans jamais l'annuler : dans une case
     de grille en pleine largeur, la carte de devant faisait 1250 × 1560 px.
     Un pavé encre haut comme trois écrans de téléphone, sur lequel un logo
     flottait au milieu. Et les cartes du dessous, en alternance crème,
     disparaissaient dans le sol crème — il n'y avait pas de paquet, il y
     avait UNE dalle.

   ⚠ ET CE QUI ÉTAIT CASSÉ AU PREMIER CLIC. La profondeur se lisait en CSS
     par `[style*="--i:4"]`, c'est-à-dire en cherchant une SOUS-CHAÎNE dans
     l'attribut `style`. Or dès que `setProperty()` change une valeur, le
     navigateur réécrit l'attribut avec une espace — `--i: 4;` — et plus
     aucun de ces sélecteurs ne correspond. Au chargement tout semblait juste,
     et à partir du premier clic les huit cartes s'affichaient à la fois,
     aucune n'était cliquable, et toutes les légendes disparaissaient.
     La profondeur vit désormais dans `data-depth`, un attribut qu'on écrit et
     qu'on lit — plus jamais dans du texte de style.

   ⚠ TROIS COLONNES, PARCE QUE LA CARTE NE PEUT PAS TOUT PORTER. Nom,
     description, zone, stand et logo dans 22 rem se disputent la même place.
     Le nom part à gauche en corps d'affiche, la description à droite en corps
     de lecture, et la carte garde ce qu'elle sait montrer.

   ⚠ NI ÉPINGLAGE NI DÉTOURNEMENT DE DÉFILEMENT. Les cartes bougent au CLIC,
     jamais au scroll : la page ne s'arrête jamais, contrairement à la bande
     épinglée de Zonas. C'est la qualité première du motif.

   ⚠ ON N'ANIME QUE `transform` ET `opacity` — garde-fou du projet. La
     géométrie de repos est écrite DEUX FOIS, ici en CSS et dans main.js :
     le CSS tient le premier cadre, GSAP prend la main ensuite et écrit les
     mêmes nombres. Si l'un des deux change, l'autre doit suivre.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── La scène ──────────────────────────────────────────────────────────────
   Mobile d'abord : une colonne, dans l'ordre où on la lit — le nom, la carte,
   ce qu'elle vend, les flèches. */
.expostage{
  display:grid;
  gap:var(--s-5);
  margin-block-start:var(--s-7);
}

/* ⚠ SANS SCRIPT, LA SCÈNE N'EXISTE PAS. Les deux colonnes sont des miroirs de
   la carte de devant : sans le JS qui les met à jour, elles resteraient
   figées sur la première marque et mentiraient sur les sept autres. Les
   flèches, elles, seraient inertes. On les retire, la liste reste — complète.
   Même raisonnement que `.js .fx-up` : l'idiome du site. */
.expostage__lead,
.expostage__note,
.expostage .expo__nav{display:none}
.js .expostage__lead,
.js .expostage__note{display:block}
.js .expostage .expo__nav{display:flex}

/* Le rang, en surtitre. Il tient la place de l'exergue mono de la référence,
   et il dit ce qu'aucune flèche ne dit : combien il en reste. */
.expostage__pos{
  margin:0 0 var(--s-3);
  font-size:var(--t-label);font-weight:700;
  letter-spacing:var(--ls-label);text-transform:uppercase;
  color:var(--c-muted);font-variant-numeric:tabular-nums;
}
/* Le nom, en corps d'affiche — c'est LUI le titre de la carte de devant. */
.expostage__name{
  margin:0;
  font-family:var(--f-display);font-weight:400;
  font-size:clamp(2rem,7vw,3.4rem);line-height:.98;
  letter-spacing:-.02em;text-wrap:balance;
  color:var(--c-ink);
}
/* Le filet rouge sous le nom : le même accent que sur la tuile de la grille,
   pour que les deux dispositions se reconnaissent. */
.expostage__name::after{
  content:'';display:block;height:2px;width:2.5rem;
  margin-block-start:var(--s-4);
  background:var(--c-rojo);
}

/* ── Téléphone : la scène se centre ──────────────────────────────────────
   Mobile d'abord, comme partout ici : c'est l'état de BASE, et c'est la
   version large qui le défait plus bas.

   ⚠ POURQUOI CENTRÉ ICI ET ALIGNÉ À GAUCHE LÀ-BAS. Sur un grand écran les
     trois colonnes sont côte à côte : le nom et la description sont des
     LÉGENDES posées de part et d'autre de la carte, et une légende s'aligne
     sur le bord qu'elle longe. Empilées sur téléphone, elles ne longent plus
     rien — elles sont dans l'axe de la carte, qui est elle-même centrée. Un
     texte aligné à gauche sous un objet centré fait un décrochement que rien
     ne justifie.

   ⚠ LE FILET ROUGE SE CENTRE AUSSI. C'est un bloc de 2,5 rem, pas du texte :
     `text-align` ne l'atteint pas, il serait resté collé au bord gauche sous
     un nom centré. */
.expostage__lead,
.expostage__note{text-align:center}
.expostage__name::after{margin-inline:auto}
.expostage__note{margin-inline:auto}

.expostage__what{
  margin:0;
  font-size:var(--t-lead);line-height:1.5;
  color:var(--c-ink);text-wrap:pretty;
}
.expostage__what em{font-style:italic;opacity:.7}
.expostage__where{
  margin:var(--s-4) 0 0;
  font-size:var(--t-label);font-weight:700;
  letter-spacing:var(--ls-label);text-transform:uppercase;
  color:var(--c-muted);font-variant-numeric:tabular-nums;
}

/* ── Le paquet ─────────────────────────────────────────────────────────────
   Une seule case de grille : toutes les cartes s'y superposent. La hauteur est
   réservée par la case, jamais par la carte de devant — sinon la scène
   sursauterait d'une marque à l'autre selon la longueur du nom. */
.expo__list--stack{
  display:grid;
  gap:var(--s-4);
  justify-items:center;
  margin:0;padding:0;list-style:none;
}

/* ⚠ SANS SCRIPT, LE PAQUET N'EST PLUS UN PAQUET — c'est une liste, et elle
   doit être DESSINÉE comme telle. Laissée en une colonne de 20 rem, elle
   traversait un écran de 1440 px avec 500 px de vide de chaque côté : le
   repli avait l'air d'une panne plutôt que d'une version simple. Deux puis
   trois colonnes, et il redevient une planche de cartes.
   `html:not(.js)` plutôt qu'une requête média : c'est l'absence de script qui
   commande, pas la largeur. */
html:not(.js) .expo__list--stack{align-items:start}
@media (min-width:640px){
  html:not(.js) .expo__list--stack{grid-template-columns:repeat(2,1fr);gap:var(--s-5) var(--s-4)}
}
@media (min-width:900px){
  html:not(.js) .expo__list--stack{grid-template-columns:repeat(3,1fr);gap:var(--s-6) var(--s-5)}
}

.js .expo__list--stack{
  place-items:center;
  min-height:clamp(19rem,52vw,24rem);
  /* Les cartes débordent de leur case pendant le lancer : sans cette marge,
     `overflow-x:clip` du <body> les rognerait au bord de l'écran. */
  padding-block:var(--s-4);
}
.expo__list--stack .expotile{
  width:min(100%,20rem);
  margin-inline:auto;
}
.js .expo__list--stack .expotile{
  grid-area:1/1;
  /* ⚠ LE PIVOT EST POSÉ ICI, ET IL NE BOUGE PLUS JAMAIS. Le JS le déplaçait au
     coin bas de la carte pendant le lancer puis le ramenait au centre — comme
     le site de référence. Or GSAP n'INTERPOLE PAS `transform-origin` sur un
     élément HTML : il l'applique d'un coup. Recalculer une rotation de 27°
     autour d'un point déplacé de 105 px déplace la carte de 82 px en un seul
     cadre, au milieu du geste. Le bas de la carte comme pivot unique donne le
     même basculement, sans le saut. */
  transform-origin:50% 100%;
  /* LA GÉOMÉTRIE DE REPOS, premier cadre — GSAP réécrit exactement ces
     nombres juste après (main.js, fonction `assise`). */
  transform:translate3d(0,calc(var(--d,0) * 8px),0)
            rotate(var(--rot,0deg))
            scale(calc(1 - var(--d,0) * .03));
  /* Au-delà de la quatrième, une carte n'ajoute plus de profondeur : elle
     épaissit le bord sans rien montrer. `max()` fait le seuil sans sélecteur
     d'attribut — 0,1,2,3 donnent 1 ; 4 et au-delà tombent à 0. */
  opacity:calc(1 - max(0, var(--d,0) - 3));
  z-index:calc(20 - var(--d,0));
}
/* Seule la carte de devant est cliquable : les autres sont derrière, les
   proposer au pointeur ferait cliquer dans du décor. */
.js .expo__list--stack .expotile:not([data-depth="0"]) .expotile__box{pointer-events:none}

/* ── La carte ──────────────────────────────────────────────────────────────
   ⚠ DANS LE PAQUET, C'EST LE `<li>` QUI EST LA CARTE, pas `.expotile__box`.
     Il fallait que la légende « Zona · Stand » soit DEDANS, au bas du carton,
     comme la légende d'un exemple ; elle est un frère de la boîte, donc c'est
     le parent qui porte le fond, le rayon et l'ombre.

   ⚠ TROIS TEINTES CLAIRES, ET C'EST UNE CONTRAINTE, PAS UN GOÛT. Le seul
     visuel disponible est `logo_dark` — un logo dessiné POUR FOND CLAIR. Sur
     une carte encre il disparaît. Le paquet est donc un jeu de cartes de
     papier posé sur la nappe crème : c'est l'OMBRE qui le détache du sol, pas
     un contraste de fond. Le jour où `logo_light` sera rempli, une teinte
     encre pourra rejoindre le cycle. */
/* ── LA SURFACE DE CARTE : BLANC, ET UNE SEULE ────────────────────────────
   19/08/2026. Il y en avait trois — blanc, pêche, bleu pâle — cyclées par
   rang. Elles s'accordaient (c'étaient les trois directions pour s'écarter
   d'un sol crème : plus claire, plus chaude, plus froide) mais elles ne
   voulaient rien dire : habile, pas stratégique.

   ⚠ CE QUI A TRANCHÉ : CES CARTES VONT PORTER DES LOGOS DE MARQUES. Un fond
     coloré entre en concurrence avec l'identité de chacune — c'est la raison
     pour laquelle tous les kits presse demandent un logo sur blanc. Aujourd'hui
     ça ne se voit pas (treize placeholders identiques), mais le jour où BISH,
     MATRIBU et CLEA envoient leurs chartes, une carte pêche imposerait son ton
     à la leur. Le blanc est le seul fond qui ne prend pas parti.

   ⚠ LA CARTE NE PERD RIEN : sa profondeur vient de sa FORME — inclinaison
     propre à chaque carte, échelle décroissante, ombre portée, liseré. Ce sont
     ces éléments-là qui font lire un paquet ; la couleur ne faisait qu'occuper
     la place en attendant les vrais visuels.

   ⚠ ET LE BLANC EST À 1,07:1 DU SOL CRÈME — presque la même clarté. Ce n'est
     PAS la luminosité qui détache la carte, c'est le liseré et l'ombre. C'est
     exactement le rapport paper-on-paper, et c'est pour ça qu'ils sont
     renforcés ci-dessous : sans la couleur, ils portent seuls tout le travail.
     (Le bug d'août — sept tuiles crème invisibles sur sol crème — venait de ce
     qu'elles n'avaient qu'un filet d'un pixel et aucune ombre.) */
.expotile{--card-bg:#FFFFFF;--card-fg:var(--c-ink)}


.expotile{
  background:var(--card-bg);color:var(--card-fg);
  border-radius:var(--r-md);
  padding:var(--s-5);
  display:flex;flex-direction:column;
  /* Le liseré tient le bord quand deux cartes de la même teinte se suivent ;
     l'ombre porte la profondeur. */
  /* ⚠ RENFORCÉS LE 19/08 AVEC LE PASSAGE AU BLANC UNIQUE. Le blanc est à
     1,07:1 du sol crème : sans la couleur, ces deux traits portent SEULS la
     séparation entre la carte et la table. Le liseré passe de .16 à .22, et
     l'ombre s'étale un peu plus. C'est la leçon du bug d'août — sept tuiles
     crème sur sol crème, séparées par un filet d'un pixel et rien d'autre. */
  box-shadow:inset 0 0 0 1px rgba(10,37,64,.22),
             0 22px 44px -24px rgba(10,37,64,.6);
}

/* La boîte redevient un simple contenu : ni fond, ni cadre, ni proportion —
   `.expotile__box` (7551) en impose trois dont `aspect-ratio:4/5`, qui est
   exactement ce qui faisait la dalle de 1560 px. */
/* ⚠ `color:inherit` N'EST PAS DÉCORATIF. `.expotile__box` (7551) prend
   `color:var(--tile-fg)`, hérité de l'ancienne alternance encre/crème — sur
   une carte blanche, le nom devenait blanc sur blanc. Les teintes du paquet
   vivent sur le `<li>` (`--card-fg`) ; la boîte doit se contenter d'hériter. */
.expotile .expotile__box{
  color:inherit;
  aspect-ratio:auto;
  background:none;border:0;border-radius:0;overflow:visible;
  display:grid;place-items:center;align-content:center;
  gap:var(--s-4);
  flex:1 1 auto;
  min-height:clamp(9rem,22vw,11rem);
  padding-block:var(--s-5);
}

/* Le bandeau de tête — « MARCA » à gauche, le renvoi au site à droite. Il
   n'existe que dans le paquet (render.php ne le rend pas ailleurs). */
.expotile__kind{
  display:flex;align-items:flex-start;justify-content:space-between;
  gap:var(--s-3);margin:0;
  font-size:var(--t-label);font-weight:700;
  letter-spacing:var(--ls-label);text-transform:uppercase;
  /* ⚠ .55 MESURÉ À 3,3:1 sur les trois teintes — sous le plancher AA de 4,5.
     C'est du texte, pas un filet : il se lit ou il n'a rien à faire là. À .78
     il passe à 5,4:1 sur la carte la plus claire. */
  opacity:.78;
}
.expotile__kind .expotile__go{margin:0;opacity:1}

/* ⚠ LA FACE REDEVIENT DU FLUX. `.expotile__face` (7584) est
   `position:absolute;inset:0` pour la grille, où elle est une légende posée
   sur une photo. Ici elle est le contenu de la carte : laissée absolue, elle
   se superposait au logo au lieu de le suivre. */
.expotile .expotile__face{
  position:static;
  display:grid;place-items:center;text-align:center;
  gap:var(--s-2);padding:0;max-width:24ch;
}
.expotile .expotile__name{
  font-size:clamp(1.05rem,2.4vw,1.3rem);line-height:1.1;
  letter-spacing:.02em;
}
/* Le filet rouge appartient au nom de l'affiche, à gauche. Sur la carte il
   ferait un second accent à trois centimètres du premier. */
.expotile .expotile__name::after{display:none}

/* ⚠ MASQUÉE À L'ŒIL, JAMAIS RETIRÉE. La description s'affiche dans la colonne
   de droite, mais elle reste ICI dans le document : c'est la source que le JS
   recopie, et c'est ce que lit un lecteur d'écran. Même règle que `.sr`.
   Sans `.js`, le masque tombe et la liste se lit d'elle-même. */
.js .expo__list--stack .expotile__what{
  position:absolute;width:1px;height:1px;
  overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;
}

/* La légende, au bas du carton : la zone à gauche, le stand à droite, comme
   sur un billet. */
.expotile .expotile__caption{
  margin:0;padding-block-start:var(--s-4);
  /* ⚠ `center` ET NON LA VALEUR PAR DÉFAUT. En `stretch`, chaque enfant prend
     la hauteur de la rangée : le jour où l'un d'eux est plus haut que le texte
     — le renvoi Instagram, ajouté le 26/08, mesure 28 px contre 23 — la
     pastille « STAND S03 » s'allonge et devient un ovale. Elle n'a pas de
     hauteur propre, elle est dessinée par son rembourrage. */
  display:flex;align-items:center;justify-content:space-between;gap:var(--s-4);
  border-block-start:1px solid rgba(10,37,64,.14);
  color:inherit;opacity:.7;
  /* ⚠ UNE HAUTEUR PLANCHER DEPUIS QUE LE STAND EST MASQUÉ (14/09/2026). La
     pastille donnait une hauteur à chaque légende ; sans elle, une marque sans
     Instagram n'a plus rien dedans et la légende tombait de 45 à 17 px. La
     boîte du logo étant `flex:1`, ces 28 px repartaient dans la carte :
     mesuré, jusqu'à 49 px d'écart entre les logos d'une même rangée. Le
     plancher vaut la légende avec icône — 28 px + rembourrage + filet, en
     `border-box` global —, donc toutes les légendes ont la même hauteur. */
  min-height:calc(28px + var(--s-4) + 1px);
}
.expotile .expotile__zone + .expotile__no::before{content:none}

/* Le logo au centre. Boîte plafonnée : un logo carré et un mot-symbole allongé
   doivent occuper la même hauteur optique, sinon le paquet tressaute d'une
   carte à l'autre. */
/* ⚠ LE PLAFOND EST EN REM, PAS EN `max-height:100%`. Il l'était, et il ne
   servait à rien : le logo placeholder (300 × 210) sortait à 160 × 112 dans une
   boîte de 160 × 80 et venait recouvrir le nom, 30 px plus bas. Un pourcentage
   de hauteur sur un enfant de grille ne se résout pas toujours comme on
   l'attend ; une valeur absolue, si — et c'est la MÊME que celle de la boîte,
   tenue par une variable pour qu'elles ne puissent plus diverger. */
.expotile__logo{--logo-h:clamp(3.5rem,8vw,5rem);
  display:grid;place-items:center;
  width:clamp(7rem,16vw,10rem);height:var(--logo-h)}
.expotile__logo img{max-width:100%;max-height:var(--logo-h);width:auto;height:auto;object-fit:contain}
/* Dans la grille il reste discret : c'est le nom qui porte l'affiche. */
/* Le logo : petit et à gauche sur téléphone, centré au-dessus du nom dès que
   la carte se redresse. */
.expo__list--grid .expotile__logo{--logo-h:3rem;width:4.75rem;margin-bottom:0}
@media (min-width:640px){
  /* ⚠ `--logo-h` DOIT MONTER AVEC LA LARGEUR. Cette règle n'élargissait que la
     boîte ; la hauteur restait au 3 rem posé pour le téléphone, et c'est elle
     qui plafonne l'image (`max-height:var(--logo-h)`). Un mot-symbole large —
     MATRIBU fait 2,5 pour 1 — se retrouvait dessiné sur 48 px de haut dans une
     carte de 314 px, donc illisible.

     Le nom reste plus fort que le logo : 4,5 rem contre les ~2,2 rem de la
     ligne de titre, mais le titre occupe toute la largeur de la carte. */
  .expo__list--grid .expotile__logo{
    width:clamp(6rem,12vw,9rem);
    --logo-h:clamp(3.25rem,5.5vw,4.5rem);
    margin-bottom:var(--s-2)}
}

/* ══ GRILLE — LA CARTE RÉORDONNÉE (25/09/2026) ═══════════════════════════
   Ordre de lecture : univers · Instagram → identité (logo équilibré, ou le
   nom en mot-symbole) → nom en légende → ce qu'ils vendent → action.
   Prototypé sur la page en ligne et MESURÉ avant d'être écrit ici
   (Docs/maquettes-marcas-2026-09-25). Tout est scopé `.expo__list--grid` :
   le paquet de l'accueil et la bande gardent leur dessin.

   ⚠ CE BLOC VIENT APRÈS LES RÈGLES QU'IL REMPLACE, ET C'EST VOULU. Même
     spécificité (0,3,0) que les règles de grille ci-dessus : il gagne par
     l'ordre. Le remonter dans la feuille le désarmerait en silence.

   ⚠ MOBILE-FIRST : la base est la LISTE du téléphone (logo à gauche), la
     grille redressée vient sous `min-width:640px`.

   ⚠ PAS DE `display:none` SUR LA LÉGENDE. C'est le PHP qui ne la rend plus
     quand elle n'a rien à dire (stand masqué, zone en titre, Instagram
     monté). Le jour où les numéros de stand reviennent, elle revient avec
     eux, et ces règles n'ont rien à savoir. */
.expo__list--grid .expotile{
  --plate-h:56px;                             /* hauteur de référence du logo */
  --plate-box:calc(var(--plate-h) + 16px);    /* la plaque, FIXE : les rangées s'alignent dessus */
  --name-row:1.25rem;                         /* la ligne du nom sous le logo, réservée */
  padding:var(--s-4);
  transition:transform .25s ease,box-shadow .25s ease;
}
.expo__list--grid .expotile:has(a.expotile__box:hover){
  transform:translateY(-2px);
  box-shadow:inset 0 0 0 1px rgba(10,37,64,.28),0 28px 48px -22px rgba(10,37,64,.55);
}

/* 1 · La ligne de tête : l'univers à gauche, Instagram à droite. Le PHP y
   écrit le nom de la catégorie (repli « Marca ») et y rend l'icône. */
.expo__list--grid .expotile__kind{
  display:flex;align-items:center;justify-content:space-between;
  gap:var(--s-3);margin:0;min-height:28px;
  font-size:.6875rem;font-weight:700;
  letter-spacing:var(--ls-label);text-transform:uppercase;
  opacity:1;color:inherit;
}
/* ⚠ L'OPACITÉ EST SUR LE MOT, PAS SUR LA LIGNE. Posée sur le <p>, elle se
   multiplierait avec celle de l'icône — c'est exactement ce qui rendait
   l'icône de la légende à 2,6:1 (.7 × .62). .66 sur l'encre : 5,0:1. */
.expo__list--grid .expotile__kind > span:first-child{opacity:.66}
.expo__list--grid .expotile__kind .expotile__ig{
  margin-left:auto;width:28px;height:28px;color:inherit;opacity:.55} /* 3,9:1, composant ≥ 3:1 */
.expo__list--grid .expotile__kind .expotile__ig:hover,
.expo__list--grid .expotile__kind .expotile__ig:focus-visible{opacity:1;color:var(--c-rojo-txt)}

/* 2 · La boîte. Téléphone : deux colonnes, la plaque à gauche, le texte centré
   sur elle. `min-height:0` défait le plancher de la règle ≥640 plus haut. */
.expo__list--grid .expotile .expotile__box{
  display:grid;grid-template-columns:calc(var(--plate-box) + 8px) minmax(0,1fr);
  align-items:center;justify-items:start;text-align:left;
  gap:0 var(--s-4);min-height:0;padding:var(--s-2) 0 0;flex:1 1 auto;
}
.expo__list--grid .expotile--wordmark .expotile__box{grid-template-columns:minmax(0,1fr)}

/* La plaque du logo : hauteur fixe, image équilibrée par `--k-auto` que le PHP
   pose depuis les dimensions du fichier (loi « carte », inc/brands.php).
   ⚠ `max-height:none` DÉFAIT LE PLAFOND `--logo-h` DE LA RÈGLE DE BASE (plus
     haut) : sans lui, un logo carré restait bloqué à 72 px dans une plaque
     prévue pour 80. Ici c'est `height` qui gouverne, et rien d'autre. */
.expo__list--grid .expotile__logo{
  width:auto;min-width:var(--plate-box);height:var(--plate-box);margin:0;
  display:grid;place-items:center;
}
.expo__list--grid .expotile__logo img{
  height:max(18px,calc(var(--plate-h) * var(--k-auto,1)));max-height:none;
  width:auto;max-width:calc(var(--plate-box) + 8px);
  object-fit:contain;
}

/* 3 · Le nom, légende du logo. */
/* ⚠ `justify-content:flex-start` N'EST PAS REDONDANT. La face hérite d'un
   `flex-end` (règle de base : légende posée au bas d'une photo). Dans une
   carte-lien, la marge automatique du bouton absorbe l'espace et masque
   l'héritage ; dans une carte SANS site, rien ne l'absorbe et nom +
   description tombaient 34 px plus bas que sur les voisines. Mesuré. */
.expo__list--grid .expotile .expotile__face{
  display:flex;flex-direction:column;align-items:flex-start;justify-content:flex-start;
  gap:0;padding:0;max-width:none;width:100%;flex:1 1 auto;
}
.expo__list--grid .expotile .expotile__name.expotile__name--sous{
  font-size:.75rem;letter-spacing:.14em;font-weight:700;
  margin:0;line-height:var(--name-row);
}
/* Sans logo réel, le nom EST l'identité. */
.expo__list--grid .expotile--wordmark .expotile__name{
  margin:0;font-size:1.1rem;font-weight:700;
  letter-spacing:.04em;text-transform:uppercase;line-height:1.05;
  opacity:1;color:inherit;
}

/* 4 · Ce qu'ils vendent.
   ⚠ COUPÉ À DEUX LIGNES. La plus longue description en ligne (Ile de France,
     56 caractères) tient ; une plus longue finira par « … ». C'est le prix de
     l'alignement des actions dans une rangée, et il est dit ici. */
.expo__list--grid .expotile__what{
  font-size:.9375rem;line-height:1.5;color:inherit;opacity:.86;
  margin:4px 0 0;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;
}

/* 5 · L'action : la seule touche de rouge de la carte. */
.expo__list--grid .expotile__go{
  margin-top:var(--s-2);
  display:inline-flex;align-items:center;gap:.45em;
  font-size:.6875rem;letter-spacing:var(--ls-label);
  color:var(--c-rojo-txt);opacity:1;
}
.expo__list--grid .expotile__go [aria-hidden]{transition:transform .2s ease}
.expo__list--grid a.expotile__box:hover .expotile__go [aria-hidden],
.expo__list--grid a.expotile__box:focus-visible .expotile__go [aria-hidden]{transform:translate(2px,-2px)}

/* Dès que les cartes se redressent : colonne centrée, plaque pleine largeur,
   deux lignes réservées à la description, l'action calée en pied. */
@media (min-width:640px){
  .expo__list--grid .expotile{--plate-h:clamp(60px,7vw,80px);padding:var(--s-5)}
  .expo__list--grid .expotile .expotile__box,
  .expo__list--grid .expotile--wordmark .expotile__box{
    display:flex;flex-direction:column;align-items:center;justify-content:flex-start;
    text-align:center;padding:var(--s-3) 0 0;
  }
  .expo__list--grid .expotile__logo{width:100%;min-width:0}
  .expo__list--grid .expotile__logo img{max-width:min(100%,calc(var(--plate-h) * 3.4))}
  .expo__list--grid .expotile .expotile__face{align-items:center}
  .expo__list--grid .expotile .expotile__name.expotile__name--sous{font-size:.8125rem;margin-top:var(--s-2)}
  /* Le mot-symbole occupe la plaque, à la hauteur des logos voisins, et
     réserve la ligne du nom : la description tombe au même endroit partout. */
  .expo__list--grid .expotile--wordmark .expotile__name{
    display:grid;place-items:center;
    min-height:var(--plate-box);
    margin:0 0 calc(var(--name-row) + var(--s-2));
    font-size:clamp(1.15rem,1.6vw,1.45rem);
  }
  .expo__list--grid .expotile__what{margin-top:var(--s-3);max-width:26ch;min-height:3em}
  .expo__list--grid .expotile__go{margin-top:auto;padding-top:var(--s-4)}
}
@media (prefers-reduced-motion:reduce){
  .expo__list--grid .expotile,.expo__list--grid .expotile__go [aria-hidden]{transition:none}
  .expo__list--grid .expotile:has(a.expotile__box:hover){transform:none}
}

/* Une photo de tuile remplit la carte du paquet au lieu de la recouvrir. */
.expotile .expotile__img{
  position:static;width:100%;height:auto;max-height:12rem;
  object-fit:cover;border-radius:var(--r-sm)}

/* ── Les flèches du paquet ─────────────────────────────────────────────────
   Centrées SOUS les cartes : un paquet se regarde, la commande vient après ce
   qu'elle commande. La bande, elle, garde les siennes en haut à droite
   (7501) — elle se lit de gauche à droite et annonce le geste avant. */
.expostage .expo__nav{
  justify-content:center;
  gap:var(--s-4);
  margin-block-start:var(--s-6);
}
/* Le paquet est un cycle : aucune extrémité, donc aucune flèche désactivée.
   La règle `:disabled` de la bande (7523) ne peut jamais s'y appliquer. */
.expostage .expo__arrow{transition:background .25s,color .25s,border-color .25s,transform .2s}
.expostage .expo__arrow:hover:not(:disabled){transform:scale(1.05)}
/* ⚠ APRÈS le survol, ET avec `:not(:disabled)`. Écrite au-dessus et sans lui,
   cette règle pesait (0,3,0) contre (0,4,0) — `:not()` compte le poids de son
   argument : l'enfoncement ne s'est jamais affiché sous un pointeur, seulement
   au doigt, où il n'y a pas de survol. */
.expostage .expo__arrow:active:not(:disabled){transform:scale(.94)}

/* ── Trois colonnes, à partir de 900 px ────────────────────────────────────
   3 / 4 / 2,6 : la colonne du milieu doit rester la plus large — c'est elle
   qui porte l'objet. La droite est plus étroite que la gauche parce qu'un
   paragraphe court se lit mieux en colonne serrée qu'un nom d'affiche.
   Les flèches occupent la deuxième rangée, sous la seule colonne du milieu. */
@media (min-width:900px){
  /* Côte à côte, elles redeviennent des légendes : chacune s'aligne sur le
     bord de la carte qu'elle longe. */
  .expostage__lead,
  .expostage__note{text-align:left}
  .expostage__name::after{margin-inline:0}

  .js .expostage{
    grid-template-columns:minmax(0,3fr) minmax(0,4fr) minmax(0,2.6fr);
    grid-template-areas:
      'lead pile note'
      '.    nav  .';
    column-gap:clamp(2rem,4.5vw,4.5rem);
    row-gap:0;
    align-items:center;
  }
  .js .expostage__lead{grid-area:lead}
  .js .expo__list--stack{grid-area:pile}
  .js .expostage__note{grid-area:note}
  .js .expostage .expo__nav{grid-area:nav}
  .js .expostage__note{padding-block-start:var(--s-5)}
}
@media (min-width:1200px){
  .js .expostage{column-gap:clamp(3rem,5vw,5.5rem)}
  .expo__list--stack .expotile{width:min(100%,22rem)}
}

/* Le mouvement du lancer appartient à GSAP, qui ne tourne pas ici : sans
   `.js`, la scène est déjà repliée en liste (voir plus haut) et il ne reste
   rien à neutraliser côté paquet. On coupe les seules transitions posées en
   CSS sur les flèches. */
@media (prefers-reduced-motion:reduce){
  .expostage .expo__arrow{transition:none}
  .expostage .expo__arrow:hover:not(:disabled),
  .expostage .expo__arrow:active{transform:none}
}

/* ══════════════════════════════════════════════════════════════════════════
   EXPOSANTS · LA TÊTE — 19/08/2026

   ⚠ LA RANGÉE DE CHIFFRES A DÉMÉNAGÉ dans la bande `cifras`, en cinquième
     position (source « brands »). Ses règles sont parties avec elle : elles
     n'avaient plus d'élément à rencontrer, et une feuille de style qui garde
     des sélecteurs morts finit par les faire lire comme une fonctionnalité.

   ⚠ LA TÊTE PASSE À DEUX COLONNES au-delà de 900 px. Empilée, elle alignait
     quatre blocs sur une colonne de 60 caractères, laissait la moitié droite
     du canevas vide, et repayait cette largeur perdue en hauteur — 650 px
     d'introduction pour 384 px d'objet. Le titre à gauche, ce qu'il annonce à
     droite : même contenu, deux fois moins haut, et la tête annonce déjà la
     composition à trois colonnes qui la suit.
   ══════════════════════════════════════════════════════════════════════════ */

@media (min-width:900px){
  .expo--stack .head{
    display:grid;
    grid-template-columns:minmax(0,1fr) minmax(0,1fr);
    column-gap:clamp(2rem,4.5vw,4.5rem);
    align-items:start;
    max-width:none;
    gap:var(--s-3) clamp(2rem,4.5vw,4.5rem);
  }
  /* Le surtitre et le titre tiennent la colonne de gauche, l'un sous l'autre ;
     l'exergue et l'intro prennent la droite. `grid-row` explicite plutôt que
     l'auto-flow : le thème s'est déjà fait piéger par l'ordre automatique dans
     `.semana__cities`, où il posait le mauvais élément à droite. */
  /* ⚠ CHAQUE ÉLÉMENT EST DÉSIGNÉ PAR CE QU'IL EST, jamais par ce qu'il n'est
     pas. La première version plaçait l'intro avec `p.fx-up:not(.label)` — or
     le sélecteur de marques est lui aussi un `<p class="expo__jump fx-up">` :
     il tombait dans la même règle, donc dans la MÊME CELLULE que l'intro, et
     se dessinait par-dessus le texte. Un `:not()` qui énumère les exceptions
     est une liste à tenir à jour ; on ne l'utilise ici que pour isoler le
     seul élément qui n'a pas de classe à lui. */
  /* ⚠ L'EXERGUE RESTE COLLÉ AU TITRE, dans la colonne de gauche. Il l'était
     dans la version empilée, et il n'y a aucune raison de les séparer : le
     surtitre, le titre et l'exergue sont UN bloc — l'identité de la section,
     lue d'un seul mouvement. La colonne de droite prend ce qui explique et ce
     qui commande : le paragraphe, puis le sélecteur.

     L'intro couvre les rangées 1 et 2 en `align-self:start` : elle démarre donc
     au même filet haut que le surtitre, et les deux colonnes partagent un bord
     supérieur net au lieu de commencer en escalier. */
  .expo--stack .head > .label      {grid-column:1;grid-row:1}
  .expo--stack .head > .wordcut    {grid-column:1;grid-row:2}
  .expo--stack .head > .wordcut-sub{grid-column:1;grid-row:3;max-width:30ch}
  .expo--stack .head > p.fx-up:not(.label):not(.wordcut-sub):not(.expo__jump){
    grid-column:2;grid-row:1/3;align-self:start;max-width:46ch;margin-top:0}
  .expo--stack .head > .expo__jump {grid-column:2;grid-row:3;align-self:end;margin-top:0}
}

/* ── Le saut direct ───────────────────────────────────────────────────────
   Un `<select>` natif, habillé — jamais remplacé. On garde la roue du
   téléphone, la frappe au clavier et l'annonce du lecteur d'écran ; on ne
   redessine que la boîte et le chevron. */
.expo__jump{display:none}
.js .expo__jump{
  display:flex;align-items:center;flex-wrap:wrap;
  gap:var(--s-3);margin:var(--s-5) 0 0;
}
.expo__jump-l{
  font-size:var(--t-label);font-weight:700;
  letter-spacing:var(--ls-label);text-transform:uppercase;color:var(--c-muted);
}
.expo__jump-s{
  flex:1 1 14rem;max-width:24rem;
  /* 44 px : le plancher tactile du site. */
  min-height:44px;padding:0 2.5rem 0 var(--s-4);
  font-family:inherit;font-size:var(--t-small);font-weight:600;color:var(--c-ink);
  background:transparent;
  border:1px solid var(--c-line);border-radius:var(--r-pill);
  cursor:pointer;
  /* Le chevron du système est illisible d'un navigateur à l'autre : on le
     retire et on dessine le nôtre, en SVG encodé — aucune requête de plus. */
  appearance:none;-webkit-appearance:none;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9' fill='none' stroke='%230A2540' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m1 1 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat:no-repeat;
  background-position:right var(--s-4) center;
  transition:border-color .25s,background-color .25s;
}
.expo__jump-s:hover{border-color:var(--c-ink)}
.expo__jump-s:focus-visible{outline:3px solid var(--c-rojo);outline-offset:2px}
/* Le menu déroulant est dessiné par le système : on ne lui impose que ce
   qu'il accepte partout, sinon Firefox rend des libellés blancs sur blanc. */
.expo__jump-s option,
.expo__jump-s optgroup{color:var(--c-ink);background:#fff}

/* ── La barre de commandes : les flèches ET la sortie ─────────────────────
   « Ver las otras N marcas » vivait en bas à gauche de la section, à cinq
   cents pixels du regard, et sur téléphone il tombait derrière le socle
   flottant. Le seul chemin vers l'annuaire complet était le seul élément
   qu'on ne voyait pas. */
/* ⚠ LE BOUTON PASSE SOUS LES CHEVRONS, CENTRÉ — et la barre passe en GRILLE
   pour ça. Premier essai en flex avec `flex-basis:100%` : il descendait bien à
   la ligne, mais 100 % est sa TAILLE PRINCIPALE, pas une consigne de rupture —
   le bouton s'étirait donc sur les 493 px de la piste, cadre compris. En
   grille, `grid-column:1/-1` le met sur sa propre rangée et `justify-self`
   le centre à sa largeur naturelle. Deux commandes qui font tourner le paquet,
   une troisième qui quitte la section : elles ne sont pas du même rang, elles
   ne sont plus sur la même ligne. */
.js .expostage .expo__nav{
  display:flex;flex-direction:column;align-items:center;
  gap:var(--s-4);
}
.expo__arrows{display:flex;align-items:center;gap:var(--s-4)}
.expostage .expo__all{margin:0}
.expostage .expo__all{
  /* Il redevient un bouton du thème : on annule les traits du lien qu'il
     était (filet rouge sous le texte, flèche en pseudo-élément). */
  border-bottom:0;padding-block-end:0;
}
.expostage .expo__all::after{content:none}
.expostage .expo__all:hover{gap:var(--s-3)}

/* ── Téléphone : tout doit tenir au-dessus du socle ──────────────────────
   Mesuré à 390 × 844, carte centrée : la scène finissait à 826 px alors que le
   socle flottant occupe 792 → 844. « Ver las otras 5 marcas » était donc
   ENTIÈREMENT recouvert — le seul chemin vers l'annuaire complet était le seul
   élément invisible. Les flèches passaient à 29 px du socle, et se trouvaient
   161 px sous la carte : on tapait « suivant » loin de ce qui change.

   ⚠ ESSAI ABANDONNÉ LE MÊME JOUR : remonter la barre de commandes SOUS la
     carte (par `order`). Les flèches devenaient parfaites — et la description
     tombait à leur place, sous le socle. On avait déplacé le problème sur la
     seule information que le visiteur cherche : ce que la marque vend.

   Ce qui marche : garder l'ordre de lecture (nom · carte · ce qu'elle vend ·
   commandes) et RESSERRER, pour que les quatre tiennent au-dessus du socle.
   La carte se pilote de toute façon au doigt — le glissement est le vrai
   geste mobile, les flèches ne sont que le second chemin. */
@media (max-width:899px){
  .js .expostage{gap:var(--s-4)}
  .js .expostage .expo__nav{margin-block-start:var(--s-3)}
  /* La réserve du socle : 52 px de socle + son écart au bord. */
  .js .expostage{padding-block-end:var(--s-6)}
  .expostage__what{font-size:var(--t-body)}
}

/* ══════════════════════════════════════════════════════════════════════════
   EXPOSANTS · L'ANCRE — 19/08/2026

   ⚠ LE DÉFAUT, MESURÉ. Quand la pile est centrée à 1440 × 900, le titre de
     section se trouve à −357 px et l'intro à −253 : on manipule des cartes
     sans plus voir de quelle section il s'agit. Le premier réflexe — « la
     section est trop haute » — est FAUX : à 1 402 px elle est 7ᵉ sur 16, plus
     COURTE que sa voisine `zonas` (1 579) et de 535 px qu'`agenda` (1 937).

   ⚠ LE VRAI DÉFAUT est l'absence de dispositif d'ancrage. Toutes les autres
     sections hautes de la page en ont un :
       · `zonas` — colonne de gauche collante pendant tout le défilement ;
       · `agenda`, `embajadores`, le pied — le titre géant cuit dans le sol.
     La seconde variante suppose un panneau coloré où écrire ; celle-ci est
     sur le crème nu. C'est donc la première qu'on reprend — celle de la
     section qui la précède immédiatement.
   ══════════════════════════════════════════════════════════════════════════ */

/* La colonne de droite ne porte plus que ce qu'elle est seule à dire. */
.expostage__note{max-width:34ch}


/* ── LES MOTS QUI NE SE COUPENT PAS ───────────────────────────────────────
   19/08/2026. Testé plutôt que supposé : une description trois ou six fois
   plus longue ne casse rien — les cartes du paquet ne bougent même pas d'un
   pixel (la description vit hors de la carte), et l'annuaire s'allonge sans
   rogner ni déborder. Mais un SEUL mot insécable suffisait à tout défaire :

     · accueil, 1440 px — 141 px de débordement horizontal de la PAGE ;
     · annuaire, 390 px — 127 px, et 39 éléments sortis de leur carte.

   Une URL collée dans la description, un mot composé allemand, un mot-dièse :
   la CCI n'a aucune raison de savoir que c'est interdit, et rien ne le lui
   dit. C'est au gabarit de tenir.

   ⚠ `anywhere` ET PAS `break-word`. Les deux coupent le mot au rendu, mais
     seul `anywhere` compte dans le calcul de la largeur MINIMALE du contenu :
     avec `break-word`, la piste de grille est toujours dimensionnée sur le mot
     entier, la carte s'élargit, et c'est précisément le débordement qu'on
     voulait supprimer.

   ⚠ ET PAS `hyphens:auto` : la coupure serait faite selon la langue du
     document — espagnol ici — sur des noms propres et des URL qui n'ont pas
     de syllabes. Un tiret au milieu d'un nom de marque est pire qu'un retour
     à la ligne franc. */
.expotile__name,
.expotile__what,
.expotile__zone,
.expotile__no,
.expostage__name,
.expostage__what{overflow-wrap:anywhere}

/* ── LES ZONES DE L'ANNUAIRE ──────────────────────────────────────────────
   19/08/2026. La page dit « busca el número de stand antes de venir » et
   servait treize cartes à plat, sans un lien vers le plan : une consigne de
   repérage sans l'outil. Groupée, la liste EST le repère.

   L'intertitre reprend le vocabulaire du bandeau de la carte — même corps,
   même graisse, même interlettrage — pour qu'on lise « c'est le même objet,
   rangé », pas « c'est une autre section ». Le compte est déduit, jamais
   saisi, comme partout dans ce bloc. */
.expo__zones{display:grid;gap:var(--s-8);margin-block-start:var(--s-7)}
.expo__zones .expo__list{margin-block-start:var(--s-5)}

.expo__zone-h{
  display:flex;align-items:baseline;flex-wrap:wrap;gap:var(--s-3) var(--s-4);
  margin:0;padding-block-end:var(--s-3);
  border-block-end:1px solid var(--c-line);
}
.expo__zone-n{
  font-size:var(--t-label);font-weight:700;
  letter-spacing:var(--ls-label);text-transform:uppercase;
  color:var(--c-ink);
}
/* Le compte est une donnée, pas un titre : il recule d'un ton. */
.expo__zone-c{
  font-size:var(--t-label);font-weight:600;
  letter-spacing:var(--ls-label);text-transform:uppercase;
  color:var(--c-muted);font-variant-numeric:tabular-nums;
  margin-inline-start:auto;
}

/* Le renvoi vers le plan, posé là où la consigne est donnée. */
.expo__map{
  /* ⚠ `inline-flex` NE SUFFIT PAS dans une grille : l'élément est un ITEM de
     grille, donc étiré sur toute sa piste par le `stretch` par défaut — le
     filet rouge courait 422 px au-delà du texte. `justify-self` le fait
     rétrécir sur son contenu. */
  justify-self:start;
  display:inline-flex;align-items:center;gap:var(--s-3);
  margin-block-start:var(--s-4);
  font-size:.9375rem;font-weight:700;color:var(--c-ink);text-decoration:none;
  border-block-end:2px solid var(--c-rojo);padding-block-end:.15rem;
  transition:gap .3s var(--ease-out),color .3s;
}
/* ⚠ UNE FLÈCHE DROITE, PAS OBLIQUE, ET MUETTE. L'oblique est le signe « ouvre
   un onglet » du site (`.expotile__go`) ; ce lien-ci reste dans la page. Et la
   syntaxe `content:'…' / ''` donne au pseudo-élément un texte de remplacement
   VIDE : sans elle, le glyphe entre dans le nom accessible et le lien
   s'annonce « Ver el plano del festival ↗ ». */
.expo__map::after{content:'→' / '';font-weight:400}
.expo__map:hover{gap:var(--s-4);color:var(--c-rojo-txt)}
@media (prefers-reduced-motion:reduce){ .expo__map{transition:none} }

/* ⚠ LA RUSTINE ANTI-COLONNES-UNIES EST PARTIE AVEC LE CYCLE — 19/08/2026.
   Elle redistribuait les teintes entre 900 et 1199 px, où la période 3 du
   cycle divisait les 3 colonnes et peignait chaque colonne d'une seule
   couleur. Une surface unique ne peut plus entrer en collision avec un nombre
   de colonnes : le problème n'est pas corrigé, il n'existe plus. */

/* Le renvoi vers le plan depuis « Cómo llegar » : même filet que les autres
   liens de texte du thème, pas un bouton — c'est une note, pas une action. */
.practica__how-map{
  color:var(--c-ink);text-decoration:none;font-weight:700;
  border-block-end:2px solid var(--c-rojo);padding-block-end:.1rem;
}
.practica__how-map:hover{color:var(--c-rojo-txt)}

/* La pastille de l'agenda est devenue un `<button>` le 19/08/2026 (elle ouvre
   la même modale que la souche du billet). Un bouton n'hérite ni de la police,
   ni du curseur d'un lien : on remet à plat le CHROME du navigateur.

   ⚠ CETTE REMISE À PLAT AVAIT MANGÉ LE DESSIN — corrigé le 19/08/2026.
   Elle portait aussi `color:inherit` et `background:none`, en croyant « ne pas
   toucher au dessin défini plus haut ». C'est l'inverse qui se produisait :
   `button.agenda__book` pèse (0,1,1) et `.agenda__book` (0,1,0), donc les deux
   déclarations écrasaient le fond corail et le texte encre de la règle de
   base. Mesuré : `background: rgba(0,0,0,0)`, texte crème — une pastille avec
   un `border-radius` de 16 px qui ne peignait rien. Elle ne se lisait plus
   comme une action : c'est le signalement du client.
   ⚠ ET LE FOND NE REVIENT PAS ICI : `.agenda__book--full` (complet) pèse aussi
   (0,1,0) et vient après, donc il garde son fond transparent + son anneau. Le
   remettre dans cette règle-ci le condamnerait à son tour. */
button.agenda__book{
  font:inherit;cursor:pointer;
  border:0;text-align:inherit;
}
/* Le titre de la ligne mène à la fiche de l'atelier.

   ⚠ LE SOULIGNEMENT EST PERMANENT DEPUIS LE 19/08/2026. Il n'apparaissait
   qu'au `:hover` — c'est-à-dire JAMAIS sur un écran tactile, où la moitié du
   trafic d'un festival se trouve. Le titre y était un lien que rien ne
   signalait : même graisse, même couleur que le reste de la ligne. Le survol
   n'est pas une affordance, c'est une confirmation ; il lui faut quelque chose
   à confirmer.
   Le trait est posé en `text-decoration-color` atténué plutôt qu'en pleine
   couleur : il doit dire « c'est un lien » sans concurrencer la pastille
   corail, qui reste l'action principale de la ligne.

   ⚠ ET LA CIBLE EST AGRANDIE. Mesurée à 345×19 px sur desktop, sous le
   minimum de 24 px de la règle WCAG 2.5.8. Le rembourrage vertical est posé
   sur un élément EN LIGNE : il agrandit la zone cliquable sans rien décaler
   dans la mise en page. */
.agenda__link{color:inherit;
  text-decoration:underline;
  text-decoration-color:color-mix(in srgb,currentColor 40%,transparent);
  text-decoration-thickness:1.5px;text-underline-offset:.18em;
  padding-block:4px;
  transition:text-decoration-color .25s}
.agenda__link:hover{text-decoration-color:currentColor}
.agenda__link:focus-visible{outline:3px solid var(--c-rojo);outline-offset:3px;border-radius:3px}

/* ══════════════════════════════════════════════════════════════════════════
   CIFRAS · LA BANDE PASSE EN ENCRE — 19/08/2026

   ⚠ POURQUOI ICI, ET NULLE PART AILLEURS. Mesuré sur la page rendue : de la
     tête au panneau vert d'`agenda`, la seule plage SANS aucun bloc peint est
     `manifiesto` + `cifras` — 1 687 px, juste après le hero, là où l'attention
     est la plus haute. Les autres sections crème (zonas, expositores,
     talleres) portent chacune leur objet coloré.

   ⚠ ET PAS SUR `talleres`, QUI ÉTAIT LA DEMANDE INITIALE. Elle précède
     immédiatement le panneau vert incliné : deux ruptures de contraste coup
     sur coup affaiblissent la seule montée dramatique de la page — celle qui
     marque le passage de « on raconte » à « on demande ». Une bande colorée
     là lui volerait son entrée.

   ⚠ 162 px SEULEMENT, et c'est la raison pour laquelle ça marche : assez pour
     couper le plat, trop court pour concurrencer quoi que ce soit. Cinq
     nombres qui font d'« une fête » un programme — et qui sont déjà des liens
     vers leur section, donc un vrai sommaire.

   ⚠ PEAU RÉEMPLOYÉE, PAS INVENTÉE : c'est celle de `.practica__facts`, où les
     contrastes sur encre sont déjà mesurés (crème 13,80:1 · corail 5,56:1 ·
     crème-2 10,89:1). On ne rouvre pas ce dossier.
   ══════════════════════════════════════════════════════════════════════════ */
/* ⚠ CETTE PEAU EST CELLE DE L'ACCUEIL, ET D'ELLE SEULE — porté le 19/08/2026.
   Écrite sans portée, elle s'appliquait à TOUTE instance du bloc `cifras`.
   Or `ser-patrocinador` et `prensa` le posent aussi, et ces pages ont DÉJÀ
   un panneau encre : `.page-aside`, la carte latérale qui porte leur seule
   action (« Empezar · Escríbenos »). Deux panneaux encre sur un même écran,
   et c'est la carte — celle qui doit convertir — qui perd sa distinction au
   profit d'une bande de statistiques.
   Le raisonnement du pavé ci-dessus ne vaut d'ailleurs que pour l'accueil :
   il répond à 1 687 px de crème plat après le hero. Une page de contenu n'a
   pas ce problème.
   La peau suit donc un CHAMP, « Fond : clair / sombre » (`cifras_fondo`),
   que le bloc traduit en `.proof-block--panel`. Défaut : clair — une
   instance neuve est sûre, et l'accueil porte le choix explicitement.
   Hors accueil, les règles de base (« ── PROOF ── », plus haut) reprennent
   la main d'elles-mêmes : bande claire, filets, chiffres encre. */
.proof-block--panel .proof__grid{
  background:var(--c-ink);color:var(--c-cream);
  border-top:0;border-radius:var(--r-lg);
  padding:clamp(var(--s-4),2vw,var(--s-6));
  overflow:hidden;

  /* ⚠ LA RESPIRATION EST SUR LE PANNEAU, PAS SUR LA SECTION. Sur l'accueil,
     `.proof-block` porte `padding-block:0` — voulu, la bande s'y intercalait
     entre deux sections comme un filet. En bloc sombre à coins arrondis, ce
     même zéro le colle à ses deux voisines : mesuré 0 px en haut ET en bas.
     La marge vit donc ici, où elle suit l'objet ; les pages de contenu, qui
     rembourrent déjà leur section, la reprennent à zéro juste en dessous. */
  margin-block:var(--s-7);
}
/* `.page-body .proof__grid{margin-block:0}` a ete RETIRE ici meme : il
   annulait une marge qui ne s'applique plus a ces pages. */
/* Le panneau porte ses bords : les cellules n'ont plus de filet bas, et leur
   filet de séparation passe en version inversée. */
.proof-block--panel .proof__grid .proof__i{
  border-bottom:0;
  border-left:1px solid var(--c-line-inv);
  padding-block:var(--s-4);
}
.proof-block--panel .proof__grid .proof__i:first-child{border-left:0}

/* ⚠ ET LE FILET SAUTE À CHAQUE DÉBUT DE RANGÉE, pas seulement sur la première
   cellule. La règle qui le faisait déjà (`:nth-child(odd)`, deux colonnes sous
   761 px) pesait autant que celle du dessus et arrivait AVANT : elle a été
   écrasée à la bascule en encre, et les cellules 3 et 5 se sont mises à porter
   un filet qui pend dans le vide. Mesuré, puis corrigé — on la réécrit ici, à
   la même portée que le panneau. */
@media(max-width:760px){
  .proof-block--panel .proof__grid .proof__i:nth-child(odd){border-left:0}
}

/* ⚠ LE PREMIER CHIFFRE EN CORAIL, LES AUTRES EN CRÈME. `tokens.css` donne au
   corail le rôle d'URGENCE — ce qui fait agir. Cinq nombres corail, ce serait
   cinq titres : plus rien ne domine. */
.proof-block--panel .proof__grid .proof__n{color:var(--c-cream)}
.proof-block--panel .proof__grid .proof__i:first-child .proof__n{color:var(--c-coral)}
.proof-block--panel .proof__grid .proof__l{color:var(--c-cream-2)}

/* ── LES ÉTATS INTERACTIFS S'INVERSENT AUSSI ──────────────────────────────
   C'est là que ce genre de bascule casse d'habitude : le fond change, les
   états restent réglés pour l'ancien. Les trois passent en version sombre. */

/* Le voile de survol était de l'encre à 4 % — invisible sur de l'encre. */
.proof-block--panel .proof__grid .proof__i:hover{background:color-mix(in srgb,var(--c-cream) 8%,transparent)}
/* `--c-rojo-txt` (#B3001F) sur encre tombe à 1,9:1 : illisible. La pêche y est
   à 10,46:1, et c'est déjà la couleur d'accent du thème sur fond bleu. */
.proof-block--panel .proof__grid .proof__i:hover .proof__l{color:var(--c-peach)}
/* Le filet du survol : `tokens.css` interdit explicitement le rouge en filet
   sur bleu (2,16:1). Corail. */
.proof-block--panel .proof__grid .proof__n::after{background:var(--c-coral)}
/* Le contour de focus rouge disparaît sur l'encre : pêche, et posé à
   l'extérieur pour qu'il ne soit pas rogné par l'`overflow` du panneau. */
.proof-block--panel .proof__grid .proof__i:focus-visible{outline:3px solid var(--c-peach);outline-offset:-4px}

/* ⚠ LE FONDU D'ENTRÉE DANS LE SABLE A ÉTÉ RETIRÉ LE 19/08/2026. Il faisait
   passer `practica` du crème au sable sur 4,5 rem, pour absorber la bande de
   sol que le panneau incliné d'`agenda` laisse sous son arête. Techniquement
   robuste — il ne supposait rien du voisin — mais il se LISAIT comme une ombre
   portée sous l'agenda, ce qui n'est pas ce qu'on voulait dire. Le sable
   commence net. La bande, elle, est traitée sur `agenda` (voir plus haut). */
/* ── « CÓMO LLEGAR » : UNE HIÉRARCHIE, ENFIN ──────────────────────────────
   19/08/2026. Mesuré dans la colonne : le titre rouge « Cómo llegar » rendait
   12 px, l'adresse 14, et les lignes qu'il introduit 15. Le titre était donc
   PLUS PETIT que son contenu, et exactement de la même taille que les
   sous-libellés (« Metrobús », « Estacionamiento ») posés dessous. Trois rôles
   différents, deux tailles, aucun ordre lisible.

   Trois niveaux, trois tailles, et chacun garde son rôle :
     titre de colonne   20 px, gras, rouge-texte  → ce qu'on cherche
     lignes             15 px, encre              → ce qu'on lit
     sous-libellés      12 px, majuscules suivies → ce qui étiquette

   ⚠ PORTÉE LIMITÉE À `.practica__how`. `.fact__k` sert aussi au panneau encre
     des trois faits (Cuándo / Dónde / Cuánto), où 12 px est juste : c'est une
     étiquette au-dessus d'un chiffre, pas un titre de colonne. */
.practica__how .fact__k{
  font-size:clamp(1.125rem,1.5vw,1.25rem);
  font-weight:800;
  letter-spacing:-.01em;
  text-transform:none;
  margin-bottom:var(--s-3);
}
/* L'adresse rejoint le corps de lecture : elle se recopie dans une application
   de course, elle n'a aucune raison d'être plus petite que le reste. */
.practica__how-addr{font-size:var(--t-small);line-height:1.5}

/* ── LA TOUCHE DE COULEUR DE LA CARTE ─────────────────────────────────────
   19/08/2026, après le passage au blanc unique. La section y a gagné en
   discipline et perdu son dernier accent : crème, blanc, encre, et rien
   d'autre sur 1 000 px.

   ⚠ ELLE VA SUR LE NUMÉRO DE STAND, ET C'EST UN CHOIX D'INFORMATION, PAS DE
     DÉCORATION. C'est la donnée que la page demande de relever — « anota los
     números de stand antes de venir » — et la seule chose qu'on cherche des
     yeux dans une allée. La couleur y sert à trouver, pas à égayer.

   ⚠ PÊCHE, PAS ROUGE. `tokens.css` réserve le rouge aux boutons ; la pêche est
     l'accent du thème (« italiques, détails »). Et c'est une pastille pleine,
     pas un filet : sur crème, l'encre dessus tient 10,5:1.

   ⚠ ET ELLE NE TOUCHE PAS AU LOGO. C'était la raison du blanc unique — un fond
     coloré entre en concurrence avec l'identité de la marque. Une pastille de
     douze pixels en bas de carte n'a pas ce problème : elle est à côté du
     logo, pas dessous. */
.expotile__no{
  background:var(--c-peach);
  color:var(--c-ink);
  padding:.16em .6em .2em;
  border-radius:var(--r-pill);
  font-variant-numeric:tabular-nums;
}
/* La légende cesse de pâlir sur ce seul élément : la pastille porte déjà sa
   couleur, l'atténuer la ferait passer pour désactivée. */
.expotile__caption{opacity:1}
.expotile__zone{opacity:.62}
/* ══════════════════════════════════════════════════════════════════════
   LE BANDEAU DES PARTENAIRES — HERO, TÉLÉPHONE UNIQUEMENT

   Pourquoi il existe. Sur un écran de 390 px, les sept plaques se
   partageaient 350 px sur deux rangs : mesuré le 04/09, Prismatic tombait à
   13 px de haut, Webedia à 15. La cause n'est pas la mise en page mais
   l'échelle — la plaque descend à 42 px sur mobile, et le coefficient
   optique (`--k:.30` pour Prismatic) la réduit encore. Un réglage calibré
   pour une plaque de 70 px s'effondre sur une de 42.

   Un défilement continu rend à chaque logo toute la largeur : la plaque
   remonte à 64 px, soit Prismatic à 19 px et les carrés à 64. Et la ligne
   unique fait remonter le billet, que le second rang repoussait.

   ⚠ CONTINU, PAS À-COUPS. Une première version s'aimantait sur chaque logo
     (`scroll-snap`) : elle marquait un temps d'arrêt sept fois par tour, ce
     qui attire l'œil à chaque reprise — exactement ce qu'on ne veut pas à
     côté du bouton de réservation. Une dérive à vitesse constante ne
     redemande jamais l'attention : on la voit une fois, puis on l'oublie.

   ⚠ MÊME MÉCANIQUE QUE LE BANDEAU DU PIED DE PAGE. Le contenu est dupliqué
     UNE fois et la boucle parcourt exactement 50 % de la largeur : au bout,
     la seconde copie occupe la position de départ de la première et le
     raccord est invisible. Ne pas changer l'un sans l'autre.

   ⚠ L'ESPACEMENT EST PORTÉ PAR LES PLAQUES, PAS PAR `gap`. Avec un `gap` de
     conteneur, quatorze plaques comptent treize intervalles alors que sept
     en comptent six : la seconde moitié n'est plus la copie exacte de la
     première, et le raccord saute d'un intervalle à chaque tour. En marge
     de chaque plaque, la moitié fait exactement le double de la moitié.

   ⚠ 640px, PAS 900px. La règle voisine d'échelle des plaques bascule à 900
     parce qu'elle ne fait que rapetisser ; ici on change de COMPOSANT. Sur
     une tablette de 800 px les sept plaques tiennent sur un rang et un
     bandeau n'y serait qu'une gêne.
   ══════════════════════════════════════════════════════════════════════ */

/* ⚠ `display:contents` HORS TÉLÉPHONE, ET C'EST TOUT L'INTÉRÊT. Le JS pose
   ce conteneur à toutes les largeurs — il ne peut pas défaire et refaire le
   DOM à chaque rotation sans risquer de le laisser à moitié transformé. En
   `contents`, il disparaît de la mise en page : les plaques redeviennent
   les enfants directs de la rangée, et le hero de bureau ne sait même pas
   qu'il existe. */
.presented__track{display:contents}

/* La copie n'existe que pour le raccord du bandeau. Ailleurs elle
   doublerait la rangée. */
.presented__copy{display:none}

@media (max-width:640px){

  /* Le libellé passe au-dessus : la bande prend toute la largeur.

     ⚠ `width:100%` EXPLICITE, PAS `justify-items:center`. Le parent centre
       ses enfants, ce qui les dimensionne sur leur CONTENU. Or le contenu
       d'ici, c'est un rail deux fois plus large que l'écran : mesuré,
       `.presented` faisait 2 228 px sur un téléphone de 390. Le hero le
       rognait, donc rien ne se voyait — mais il tirait toute la colonne, et
       c'est ce qui envoyait la liste « Gastronomía · … » hors de l'écran.
       Une largeur déclarée l'emporte sur le dimensionnement au contenu. */
  .hero .presented{flex-direction:column;align-items:center;gap:var(--s-4);text-align:center;
    width:100%;min-width:0}

  .hero .presented__logos{
    display:block;width:100%;
    /* La fenêtre. Le mouvement se passe derrière. */
    overflow:hidden;

    /* ⚠ LE FONDU DES BORDS EST CE QUI REND LE DÉFILEMENT DOUX.
       Sans lui, un logo apparaît et disparaît en étant tranché net par le
       bord : le mouvement se lit comme un défaut d'affichage. Estompé, il
       entre et sort. C'est la moitié de l'effet demandé.

       ⚠ SANS RISQUE ICI, CONTRAIREMENT AU PANNEAU DE JUIN. Un masque avait
         alors été retiré parce qu'il rendait le FOND transparent et
         laissait voir la page au travers. Cette bande n'a pas de fond : le
         masque ne peut estomper que les logos, ce qu'on lui demande. */
    -webkit-mask-image:linear-gradient(90deg,transparent 0,#000 12%,#000 88%,transparent 100%);
    mask-image:linear-gradient(90deg,transparent 0,#000 12%,#000 88%,transparent 100%)}

  .hero .presented__track{
    display:flex;flex-wrap:nowrap;align-items:center;
    width:max-content;gap:0;
    /* Seul `transform` est animé — la règle de perf du projet. */
    will-change:transform}

  .hero .presented__copy{display:flex;flex-wrap:nowrap;align-items:center;gap:0}

  /* ⚠ LE GABARIT EST REDIT ICI, IL N'EST PAS HÉRITÉ. Le bloc de bureau qui
     l'établit est désormais borné à 641 px et plus : la bande doit porter le
     sien. C'est le prix de la garde structurelle, et il est juste — les deux
     échelles n'ont aucune raison d'être la même, une bande qui défile ne
     montre pas cinq marques d'un coup.

     ⚠ HAUTEUR COMMUNE, LARGEUR LIBRE — la même règle qu'en bureau, pour la
       même raison : à cadre entièrement fixe, un mot-symbole de 8,5:1 bute
       sur les bords et son encre retombe sous celle des autres.

     Les valeurs reproduisent EXACTEMENT ce qui est à l'écran aujourd'hui
     (cadre 56, logo 44) : ce changement ne déplace aucun pixel, il rend
     seulement le réglage lisible là où il agit. Pour donner plus de présence
     aux marques sur téléphone, c'est `--plate-h` ci-dessous qu'on touche, et
     lui seul. */
  .hero .presented__logos a[data-sponsor]{
    flex:0 0 auto;
    /* L'intervalle, porté par la plaque : voir l'avertissement en tête. */
    margin-inline-end:var(--s-6);

    height:56px;width:auto;min-width:84px;
    padding-inline:10px;padding-block:0;
    border-radius:14px;
    box-shadow:0 16px 32px -22px rgba(0,0,0,.9);

    /* Le seul bouton d'échelle du bandeau. */
    --plate-h:44px}

  /* ── Mouvement réduit ────────────────────────────────────────────────
     Le JS n'anime pas ; la bande deviendrait alors une rangée figée sur ses
     trois premiers logos. On lui rend le défilement au doigt : le contenu
     reste atteignable, sans que rien ne bouge tout seul. */
  .hero .presented__logos.is-static{
    overflow-x:auto;overscroll-behavior-x:contain;
    scrollbar-width:none;-ms-overflow-style:none}
  .hero .presented__logos.is-static::-webkit-scrollbar{display:none}
  .hero .presented__logos.is-static .presented__copy{display:none}
}

/* ══════════════════════════════════════════════════════════════════════
   LE BANDEAU SUR ORDINATEUR — seulement quand la rangée déborde

   22/09/2026. À six partenaires, la rangée de bureau ne tenait plus :
   Alstom passait seul sur une seconde ligne. Le script (`initHeroSponsors`,
   main.js) mesure si les plaques tiennent côte à côte ; sinon il pose
   `.is-marquee` et anime, exactement comme sur téléphone. Tant que tout
   tient, la classe n'est pas posée et AUCUNE de ces règles ne s'applique :
   la rangée fixe et centrée d'avant reste intacte.

   ⚠ BORNÉ À 641 px ET PLUS : le téléphone a déjà son bandeau, plus haut,
     avec ses propres réglages de plaque. Les deux ne doivent pas se
     marcher dessus.

   ⚠ L'INTERVALLE PASSE DU `gap` À UNE MARGE PORTÉE PAR CHAQUE PLAQUE, pour
     la même raison que sur téléphone : le raccord de la boucle. Avec un
     `gap`, la dernière plaque de l'original et la première de la copie
     n'auraient pas d'écart entre elles, et le bandeau sauterait à chaque
     tour. */
@media (min-width:641px){
  .hero .presented:has(.presented__logos.is-marquee){width:100%;min-width:0}

  .hero .presented__logos.is-marquee{
    display:block;width:100%;justify-self:stretch;overflow:hidden;gap:0;
    /* Même fondu des bords que sur téléphone, plus court : sur un écran
       large, 12 % mangeraient une plaque entière. */
    -webkit-mask-image:linear-gradient(90deg,transparent 0,#000 7%,#000 93%,transparent 100%);
    mask-image:linear-gradient(90deg,transparent 0,#000 7%,#000 93%,transparent 100%)}

  .hero .presented__logos.is-marquee .presented__track{
    display:flex;flex-wrap:nowrap;align-items:center;
    width:max-content;gap:0;will-change:transform}

  .hero .presented__logos.is-marquee .presented__copy{
    display:flex;flex-wrap:nowrap;align-items:center;gap:0}

  .hero .presented__logos.is-marquee a[data-sponsor]{
    flex:0 0 auto;margin-inline-end:clamp(var(--s-5),2.6vw,var(--s-7))}

  /* Mouvement réduit : le script n'anime pas ; on rend le défilement à la
     molette ou au pavé, et la copie, inutile, disparaît. */
  .hero .presented__logos.is-marquee.is-static{
    overflow-x:auto;overscroll-behavior-x:contain;
    scrollbar-width:none;-ms-overflow-style:none}
  .hero .presented__logos.is-marquee.is-static::-webkit-scrollbar{display:none}
  .hero .presented__logos.is-marquee.is-static .presented__copy{display:none}
}

/* ══════════════════════════════════════════════════════════════════════
   LE HERO SANS SON BOUTON — respiration rendue aux partenaires

   Demande client du 08/09 : trois ateliers seulement étant confirmés,
   « Reservar un taller » disparaît du hero et l'on entre par l'onglet
   « Talleres ». La place ainsi libérée revient aux marques, qui portent
   désormais seules le bas de la colonne.

   ⚠ `:has()` PLUTÔT QU'UNE CLASSE POSÉE EN PHP, PARCE QUE ÇA SE DÉFAIT
     TOUT SEUL. Le bouton reviendra — il suffit de retaper son libellé dans
     Réglages du hero — et personne ne pensera alors à retirer une classe
     `is-sans-cta` du gabarit. La feuille lit l'état réel du document :
     s'il n'y a pas de `.hero__actions`, les marques respirent ; dès qu'il
     y en a un, l'espacement d'origine revient de lui-même.

   ⚠ SI `:has()` MANQUAIT, RIEN NE CASSE : on retombe sur l'espacement
     d'aujourd'hui, qui était correct. C'est une amélioration, pas une
     béquille — d'où l'absence de repli.
   ══════════════════════════════════════════════════════════════════════ */
/* ── La composition ──────────────────────────────────────────────────
   Le bloc était une RANGÉE : libellé à gauche, logos à droite, tout sur
   une seule ligne de base. Cette forme dit « note de bas de page » — le
   libellé y est une légende qui accompagne, pas une phrase qui annonce.
   En colonne centrée, il redevient un intertitre et les logos deviennent
   ce qu'il annonce. C'est le changement qui compte ; le reste n'est que
   de l'échelle. */
.hero__main:not(:has(.hero__actions)) .presented{
  display:grid;justify-items:center;gap:var(--s-5);
  margin-block:var(--s-7) var(--s-6);
  padding-top:0;
  /* ⚠ LE FILET DU HAUT DISPARAÎT. Il séparait une note du contenu au-dessus.
     Un bloc qui s'annonce lui-même n'a pas besoin qu'on le sépare : la ligne
     le rattachait à ce qui précède au lieu de le poser. */
  border-top:0;
  text-align:center}

/* ── Le libellé ──────────────────────────────────────────────────────
   Il passe du gris de service à l'encre du titre, gagne une graisse et de
   l'interlettrage. Il reste PETIT : la mise en valeur vient de l'espace et
   du centrage, pas du corps. Un libellé de partenaires qui grossit entre en
   concurrence avec « 200 años de amistad » — et perd. */
.hero__main:not(:has(.hero__actions)) .presented__label{
  display:flex;flex-direction:column;align-items:center;gap:var(--s-4);
  font-size:.8125rem;font-weight:700;letter-spacing:.22em;
  color:var(--c-ink);opacity:1}

/* ⚠ PAS DE FILET TRICOLORE ICI — ESSAYÉ, RETIRÉ LE 08/09.
   L'idée était de refermer la page par le geste qui l'ouvre. Mais ce filet
   est DÉJÀ visible à quelques centimètres au-dessus, devant « FESTIVAL
   FRANCIA-MÉXICO » : deux fois le même signe dans un même écran ne se lit
   pas comme un rappel, il se lit comme une répétition, et le second affaiblit
   le premier. Un motif ne fait signature qu'à condition d'être rare.
   Le centrage, l'encre et l'interlettrage suffisent à poser le libellé. */

/* ── Les logos ───────────────────────────────────────────────────────
   Un tiers plus grands, et surtout plus espacés : à cinq marques la rangée
   avait de la place à revendre et ne s'en servait pas. */
.hero__main:not(:has(.hero__actions)) .presented__logos{
  gap:clamp(var(--s-5),3.4vw,var(--s-7));
  flex-wrap:wrap;justify-content:center}

/* ⚠ BORNÉ AU-DESSUS DE 640 px, ET C'EST LA LEÇON DU 08/09.
   Ce bloc a d'abord été écrit SANS media query. Il est plus spécifique
   (0,4,1) que la règle du bandeau mobile (0,3,1) et vient plus bas dans le
   fichier : il gouvernait donc aussi le téléphone et y écrasait le
   `--plate-h:64px` réglé pour la bande, ramené sans bruit à 44. Le réglage
   du bandeau était devenu du code mort — un `--plate-h` que plus rien ne
   lisait, exactement le piège que ce fichier documente déjà trois fois
   (« le réglage n'est pas le câblage »).

   Une règle destinée au bureau doit être INCAPABLE d'atteindre le téléphone,
   pas seulement défaite après coup par une règle plus basse : la seconde
   solution marche jusqu'à ce que quelqu'un insère une troisième règle entre
   les deux. Le média est une garde structurelle ; l'ordre source n'en est
   pas une. */
@media (min-width:641px){

.hero__main:not(:has(.hero__actions)) .presented__logos a[data-sponsor]{

  /* ⚠ GABARIT FIXE — LA MÊME CORRECTION QUE LE MUR DU PIED DE PAGE.
     Les plaques valaient `height:auto` : elles épousaient leur logo, donc
     cinq hauteurs et cinq largeurs différentes sur une même rangée. Tant
     qu'elles étaient à peine visibles sur le crème cela passait ; depuis
     qu'une ombre les détache, l'œil lit l'irrégularité AVANT de lire les
     marques. C'est exactement le motif pour lequel `.thanks__logos` a reçu
     son gabarit — on applique ici la décision déjà prise là-bas.

     ⚠ SEULE LA PLAQUE EST UNIFORMISÉE, JAMAIS L'IMAGE. `--plate-h` et `--k`
       portent l'équilibre optique : à hauteur égale, un mot-symbole allongé
       écrase un logo carré, d'où Prismatic à .30 contre Ambassade à 1.
       Uniformiser aussi les images annulerait ce travail. La plaque devient
       un cadre régulier ; le logo s'y centre à sa taille optique.

     ⚠ LA HAUTEUR SEULEMENT, PAS LA LARGEUR — ET C'EST MESURÉ, PAS THÉORIQUE.
       Un cadre à largeur fixe a été essayé le 08/09 : les cinq plaques
       devenaient identiques, mais les deux mots-symboles les plus allongés
       butaient sur ses bords. Prismatic fait 8,5:1 ; à 17 px de haut il lui
       faut 148 px de large, le cadre n'en offrait que 97. `object-fit` le
       ramenait alors dans la boîte et son encre tombait à 11 px — plus petit
       qu'avant l'uniformisation, alors qu'on cherchait à mettre en valeur.
       Un rapport de 8,5:1 et un carré ne peuvent pas partager un cadre qui
       flatte les deux : élargir pour Prismatic laisse Ambassade flotter dans
       du blanc, rétrécir pour Ambassade rogne Prismatic.

       La hauteur commune suffit à l'œil : c'est elle qui donne la ligne, et
       c'est elle qu'on avait remarquée. Les largeurs qui respirent avec
       chaque marque ne se lisent pas comme une irrégularité — elles se
       lisent comme cinq marques différentes, ce qu'elles sont. */
  height:clamp(56px,calc(50.8px + 1.33vw),70px);
  width:auto;
  /* Un plancher pour que la plaque d'un logo carré ne devienne pas un jeton
     étroit à côté des mots-symboles. */
  min-width:clamp(84px,calc(76.6px + 1.9vw),104px);
  padding-inline:clamp(.625rem,1.1vw,1rem);
  padding-block:0;
  border-radius:14px;

  /* Un cran plus petits qu'au premier jet : à cinq marques dans une colonne,
     84 px de logo prenaient le pas sur le sous-titre juste au-dessus.
     ⚠ TERME FLUIDE RÉELLEMENT FLUIDE : `clamp(44px,4vw,58px)` aurait valu
       44 px fixe jusqu'à 1 100 px de large — la faute déjà corrigée deux fois
       dans ce fichier. Celui-ci monte vraiment de 44 à 390 px vers 58 à
       1 440, et reste sous la hauteur du cadre, marge intérieure comprise. */
  --plate-h:clamp(44px,calc(38.6px + 1.4vw),58px);

  /* L'ombre portée se creuse d'un cran : les plaques se détachent du crème
     au lieu d'y flotter. Même direction et même douceur qu'ailleurs. */
  box-shadow:0 16px 32px -22px rgba(0,0,0,.9)}

}

/* ══════════════════════════════════════════════════════════════════════
   LE FILET TRICOLORE PASSE AU-DESSUS DU SURTITRE

   ⚠ CE N'EST PAS UN CHOIX DE GOÛT, C'EST UN ALIGNEMENT. Mesuré le 08/09,
     identique à 1440, 1024 et 390 px :

       titre « 200 años… »        écart à l'axe    0 px
       sous-titre                 écart à l'axe    0 px
       groupe surtitre entier     écart à l'axe    0 px
       → le TEXTE du surtitre     écart à l'axe  +50 px

     Le hero est centré. Un filet posé À CÔTÉ du mot fait centrer le
     GROUPE, pas le mot : « FESTIVAL FRANCIA-MÉXICO » se retrouve poussé
     vers la droite de la moitié du filet plus l'espace, soit (88+12)/2.
     C'est la seule ligne de la colonne qui ne tombe pas sur l'axe que le
     titre établit — un décalage qu'on sent sans savoir le nommer.

     Un filet en amorce est juste dans une composition ALIGNÉE À GAUCHE,
     où il se cale sur la marge et lance la lecture. Ici il n'y a pas de
     marge à gauche : il n'amorce rien, il déséquilibre.

   ⚠ ET PAS UN FILET DE CHAQUE CÔTÉ, qui centrerait aussi. Le motif est un
     TRICOLORE : le symétriser inverse l'ordre des bandes sur la copie de
     droite — vert, blanc, rouge, blanc, bleu. On n'affiche pas un drapeau
     à l'envers pour gagner une symétrie.
   ══════════════════════════════════════════════════════════════════════ */
.hero__eyebrow{flex-direction:column;gap:var(--s-3)}

/* ── Le bloc partenaires resserré sur téléphone ────────────────────────
   ⚠ 64 px DE MARGE HAUTE ÉTAIT UNE VALEUR DE BUREAU APPLIQUÉE AU TÉLÉPHONE.
     Mesuré à 390 px, les écarts du hero faisaient 32 · 32 · 44 · puis 96 —
     l'espace avant les marques valait deux à trois fois tous les autres, et
     se lisait comme un trou plutôt que comme une respiration. Sur un écran
     large ces 64 px se noient dans une colonne longue ; sur 390 px de large
     ils coûtent un huitième du premier écran.

     48 px au total (le gap de 32 de la grille plus ces 16) reste le plus
     grand écart de la colonne — le bloc s'annonce toujours — mais d'une fois
     et demie la base, non de trois.

   ⚠ BORNÉ À 640 px : LE BUREAU A ÉTÉ VALIDÉ TEL QUEL. Même seuil que le
     bandeau défilant, pour que les deux basculent ensemble : deux points de
     bascule voisins produiraient une largeur où la bande défile avec les
     marges de bureau, que personne n'a jamais regardée. */
@media (max-width:640px){
  .hero__main:not(:has(.hero__actions)) .presented{
    margin-block:var(--s-4)}
}

/* ── ALIADOS · la grille tient debout à une seule carte ─────────────────
   ⚠ DEUX COLONNES EN DUR, UN NOMBRE DE CARTES QUI VARIE. La grille était
     figée à `repeat(2,1fr)`. Elle recevait deux fiches — le lieu (Aztlán)
     et l'allié institutionnel (l'Ambassade) — et l'accord tenait tant que
     les deux existaient. Le 08/09, Aztlán est passée en brouillon à la
     demande du client : mesuré, la grille faisait toujours 656 + 656 px
     avec UNE carte dedans, et la moitié droite de la section restait vide.

     Ce n'est pas propre à Aztlán. Le nombre de fiches vient de la taxonomie
     `ccimx_tier` : il changera encore, dans les deux sens, à chaque nouveau
     partenaire principal. Une grille qui n'est juste qu'à un seul compte
     n'est pas une grille, c'est une coïncidence.

   ⚠ ON NE PASSE PAS EN `auto-fit`. Ce serait la réponse réflexe, et elle
     donnerait à la carte unique TOUTE la largeur de la section — 1 328 px
     d'une fiche de trois lignes, ce qui est un autre défaut. Ici la carte
     garde une largeur lisible et se centre : elle se lit comme un choix,
     pas comme un reste.

     `:has()` plutôt qu'une classe posée en PHP, pour la même raison
     qu'ailleurs dans ce fichier : la CCI publie et dépublie des marques
     sans nous, et personne ne pensera à changer une classe. La feuille lit
     l'état réel du document. */
/* ⚠ SEULE, LA FICHE CHANGE DE FORME — elle ne se contente pas de rétrécir.
     Trois dispositions ont été essayées avant celle-ci, et chacune ratait
     pour une raison différente :
       · à la largeur d'une colonne (656 px) — l'œil complète la seconde et
         voit un trou, puisque la grille en annonçait deux ;
       · centrée — elle commence à 448 px quand le titre de section, la
         rangée des marques et tout le reste partent de 56. Dans une
         composition alignée sur une marge, un élément centré ne se lit pas
         comme centré, il se lit comme mal placé ;
       · pleine largeur en restant verticale — un paragraphe de deux lignes
         posé sous un logo, sur 1 328 px, laisse une colonne de vide à droite.

     La quatrième tient : la fiche prend toute la largeur ET passe en BANDE.
     Le rôle et le logo tiennent la colonne étroite, le texte et son lien
     occupent le reste. Une bande large est une forme éditoriale en soi —
     elle ne fait plus référence à une grille absente.

   ⚠ PLACEMENT EXPLICITE, PAS DE FLUX AUTOMATIQUE. Le rôle porte `order:-1`
     et le lien est facultatif : un `grid-auto-flow:column` rangerait les
     éléments dans un ordre différent selon qu'une fiche a son rôle ou non.
     Chaque élément nomme donc sa case, et une case vide reste vide.

   ⚠ `> p:not([class])` VISE LE LOGO. Le paragraphe qui le porte n'a de
     classe (`scard__marks`) que sur les fiches à plusieurs logos ; sur les
     autres il n'en a aucune. Les deux sélecteurs couvrent les deux cas. */
.sponsors__grid:has(> :only-child){grid-template-columns:minmax(0,1fr)}

.sponsors__grid > .scard:only-child{
  grid-template-columns:minmax(0,15rem) minmax(0,1fr);
  column-gap:clamp(var(--s-5),4vw,var(--s-8));
  align-content:start}

.sponsors__grid > .scard:only-child .scard__role{grid-area:1 / 1}
.sponsors__grid > .scard:only-child > p:not([class]),
.sponsors__grid > .scard:only-child .scard__marks{grid-area:2 / 1}
.sponsors__grid > .scard:only-child .scard__txt{grid-area:1 / 2;max-width:56ch}
/* ⚠ `justify-self:start` — SANS LUI LE LIEN FAIT MILLE PIXELS DE LARGE.
   Un `<a>` en élément de grille s'étire sur toute sa case : le texte reste
   à gauche, mais la zone cliquable couvre la colonne entière. On survole
   du vide et le curseur devient une main à cinquante centimètres du lien. */
.sponsors__grid > .scard:only-child .arrow{grid-area:2 / 2;align-self:start;justify-self:start}

/* Sous 820 px la grille est déjà à une colonne pour tout le monde : la bande
   n'a plus la largeur de se déployer et la fiche reprend sa pile verticale. */
@media(max-width:820px){
  .sponsors__grid > .scard:only-child{grid-template-columns:minmax(0,1fr)}
  .sponsors__grid > .scard:only-child .scard__role,
  .sponsors__grid > .scard:only-child > p:not([class]),
  .sponsors__grid > .scard:only-child .scard__marks,
  .sponsors__grid > .scard:only-child .scard__txt,
  .sponsors__grid > .scard:only-child .arrow{grid-area:auto}
}

/* ⚠ ALIGNÉE À GAUCHE, PAS CENTRÉE — ESSAYÉ, C'ÉTAIT PIRE.
     Centrer la carte unique la faisait commencer à 448 px quand le titre de
     la section, la rangée des marques et tout le reste partent de 56. Elle
     flottait, décrochée de la phrase qui l'annonce. Dans une composition
     alignée sur une marge, un élément centré ne se lit pas comme centré : il
     se lit comme mal placé.

   ⚠ ET PLUS LARGE QU'UNE COLONNE. À 656 px — la largeur exacte d'une des
     deux colonnes — la carte redisait la grille absente : l'œil complétait
     la seconde colonne et voyait un trou. Un peu plus large, elle cesse
     d'être « la colonne de gauche » pour redevenir un bloc à part entière. */


/* ══════════════════════════════════════════════════════════════════════
   LA MODALE DE LECTURE VIDÉO — bureau uniquement

   Mesuré le 08/09 : à 1 440 px, une vidéo lue en place fait 648 px de large,
   en modale 1 325. Elle double. À 390 px elle passerait de 350 à 359 — neuf
   pixels —, d'où le seuil dans `initVideo()` : sur téléphone on continue de
   lire sur place, sans voile ni geste de sortie.

   ⚠ `<dialog>` DONNE LE FOND ASSOMBRI, LE PIÈGE À FOCUS ET ÉCHAP. On ne
     recrée rien de tout ça ici : `::backdrop` est un pseudo-élément du
     navigateur, il n'existe que pendant `showModal()`.
   ══════════════════════════════════════════════════════════════════════ */
.vmodal{
  /* ⚠ `margin:auto` REMET LE CENTRAGE QUE LE RESET A RETIRÉ. La feuille du
     navigateur centre un `<dialog>` modal avec `inset:0;margin:auto` — mais
     le preflight Tailwind (`dist/css/output-child.css`) pose `margin:0` sur
     `*` ET sur `::backdrop`, ce qui l'annule. Mesuré : la modale se collait
     en 0,0, et son bouton de fermeture, calé 3 rem au-dessus, se retrouvait
     à y = −48 — hors de l'écran, inatteignable. Le clic destiné à fermer
     tombait sur le lecteur, et les lecteurs s'accumulaient à chaque
     ouverture puisque rien ne refermait jamais. */
  margin:auto;
  border:0;padding:0;background:transparent;overflow:visible;
  width:min(92vw, calc((100vh - 7rem) * 16 / 9));
  max-width:1400px;
  /* `max-height` en plus du calcul de largeur : sur un écran très large mais
     court, c'est la hauteur qui borne, et l'inverse sur un écran étroit. */
  max-height:none}

.vmodal::backdrop{background:rgba(10,37,64,.82);backdrop-filter:blur(3px)}

.vmodal__stage{width:100%;aspect-ratio:16/9;border-radius:var(--r-lg);overflow:hidden;
  background:var(--c-ink);box-shadow:0 40px 90px -40px rgba(0,0,0,.9)}

.vmodal__stage > *{display:block;width:100%;height:100%;border:0}

/* ⚠ LE BOUTON EST AU-DESSUS DU CADRE, PAS DEDANS. Posé à l'intérieur il
   recouvrirait les commandes du lecteur — la barre de progression de YouTube
   passe exactement là. Au-dessus, il reste atteignable sans rien masquer, et
   sa cible fait 44 px bien qu'il n'en montre que 24. */
.vmodal__close{
  position:absolute;top:-3rem;right:0;
  display:grid;place-items:center;width:44px;height:44px;
  border:0;border-radius:50%;background:transparent;color:var(--c-cream);
  font-size:1.25rem;line-height:1;cursor:pointer;
  transition:background .3s var(--ease-out)}
.vmodal__close:hover,
.vmodal__close:focus-visible{background:rgba(255,255,255,.14)}

@media (prefers-reduced-motion:no-preference){
  .vmodal[open]{animation:vmodal-in .28s var(--ease-out)}
  @keyframes vmodal-in{from{opacity:0;transform:translateY(8px) scale(.985)}}
}

/* ── Le filtre par univers de la page Marcas ────────────────────────────
   ⚠ AUCUNE NOUVELLE APPARENCE. `.chip` est déjà défini pour le filtre du
     programme : on ne lui donne ici qu'une place dans la page. Redéfinir sa
     couleur ou son arrondi créerait deux boutons qui se ressemblent sans
     être les mêmes — le pire des deux mondes. */
.expo__filters{
  display:flex;flex-wrap:wrap;gap:var(--s-3);
  margin:0 0 var(--s-7)}

/* Une zone vidée par le filtre sort du flux avec son titre et son compteur. */
.expo__zone[hidden]{display:none}

/* ══ HERO · MODE PHOTO ═══════════════════════════════════════════════════
   15/09/2026 — la CCI veut voir le festival dès le premier écran. La photo
   passe EN FOND du hero (champ « Background photo » du bloc), pas en
   bandeau au-dessus : titre, dates et billet ne bougent pas.

   ⚠ TOUT EST SOUS `.hero--photo`, posée par le gabarit seulement quand la
     photo s'affiche. Sans elle, aucune de ces règles ne s'applique et le
     hero crème reste exactement celui d'aujourd'hui — c'est ce qui permet
     de comparer les deux versions sur la même page (`/?hero=foto`).

   ⚠ EN FIN DE FICHIER, ET C'EST VOULU. Le hero a changé de sol trois fois
     (sombre → photo tramée → crème) et chaque passage a laissé ses règles
     plus haut. À spécificité égale, c'est la dernière qui gagne : ces
     lignes-ci n'ont donc pas à surenchérir sur celles du hero crème. */

/* ── 1 · LE SOL : la photo, un voile marine ──────────────────────────── */
.hero--photo{background:var(--c-ink);color:var(--c-cream)}
/* Les filtres des anciennes versions (niveaux de gris, trame) visaient une
   photo de 490 px qu'il fallait maquiller. Celle-ci garde ses couleurs :
   les chapiteaux orange sont précisément ce qui dit « fête ». */
.hero--photo .hero__photo{filter:none;object-position:center 50%}
.hero--photo-top .hero__photo{object-position:center 18%}
.hero--photo-bottom .hero__photo{object-position:center 82%}
/* Le voile porte le contraste : plus dense au centre, où vit le texte,
   ouvert sur les bords pour que les tentes et les guirlandes se lisent. */
.hero--photo .hero__veil{
  background:
    radial-gradient(75% 62% at 50% 40%, rgba(10,37,64,.58), rgba(10,37,64,.22) 72%, transparent 100%),
    linear-gradient(to bottom, rgba(10,37,64,.46) 0%, rgba(10,37,64,.30) 40%, rgba(10,37,64,.62) 100%)}
/* Halos, silhouette et trame étaient faits pour un fond crème vide. */
.hero--photo .hero__objects,
.hero--photo .hero__bg,
.hero--photo .hero__halftone{display:none}

/* ⚠ UN BORD NET APPELLE UNE RESPIRATION — téléphone seulement.
   Sous 760 px, `.manifesto` n'a plus que 4 px de marge haute (réglage du
   14/09) : sur le hero crème, les deux sols se confondent et l'écart ne se
   voit pas. Sous une photo, le bord de l'image est une ligne dure, et
   « El festival, en pocas palabras » tombait à 32 px dessous — collé.
   Même seuil que la règle de `.manifesto`, et seulement en mode photo. */
@media (max-width:760px){.hero--photo{margin-bottom:var(--s-7)}}

/* ── 2 · LE TEXTE PASSE EN CRÈME ─────────────────────────────────────── */
.hero--photo .display,.hero--photo h1{color:var(--c-cream);text-shadow:0 2px 28px rgba(0,20,40,.45)}
/* La seconde ligne gardait le rouge foncé du fond crème : sur marine il
   tombe sous 3:1. Le corail tient le même rôle et se lit. */
.hero--photo h1 span.split:last-of-type{color:var(--c-coral)}
.hero--photo .label,.hero--photo .display em,.hero--photo .hero__lead em{color:var(--c-coral)}
.hero--photo .hero__lead{color:rgba(247,241,228,.94);text-shadow:0 1px 14px rgba(0,20,40,.5)}
.hero--photo .hero__tags,.hero--photo .hero__tags li,.hero--photo .hero__tags a{color:var(--c-cream)}
.hero--photo .hero__tags li:not(:last-child)::after{color:rgba(247,241,228,.55)}
.hero--photo .hero__tags a:hover,.hero--photo .hero__tags a:focus-visible{color:var(--c-peach)}
.hero--photo .presented{border-top-color:var(--c-line-inv)}
/* ⚠ `.hero.hero--photo .hero__main` ET PAS `.hero--photo` SEUL : le libellé
   centré est piloté par `.hero__main:not(:has(.hero__actions))`, qui pèse
   (0,3,0) et le force à l'encre. Mesuré sur la capture : bleu sur photo. */
.hero.hero--photo .hero__main .presented__label{color:rgba(247,241,228,.9)}

/* ── 2 bis · LES LOGOS PARTENAIRES RETROUVENT LEUR PLAQUE ───────────────
   Sur crème, les logos sont posés NUS en `multiply` : le blanc des fichiers
   se fond dans le sol. Sur une photo, `multiply` fond le logo dans la foule
   — Prismatic, Publicis et Alstom disparaissaient. Chaque logo retrouve
   donc une plaque crème pleine, comme au mur du pied de page. */
.hero.hero--photo .presented__logos a[data-sponsor]{
  background:var(--c-cream);border-radius:14px;
  box-shadow:0 16px 32px -20px rgba(0,0,0,.75)}
.hero.hero--photo .presented__logos a[data-sponsor] img{mix-blend-mode:normal;opacity:1}

/* ── 3 · LE BILLET ───────────────────────────────────────────────────────
   Sur ce gabarit, les encoches sont des DÉCOUPES (masque) : elles laissent
   voir la photo, ce qui est juste. Mais d'autres largeurs les dessinent en
   disques de la couleur du fond — crème posé sur une photo, ce seraient des
   pastilles. Ces disques-là sont masqués ; la perforation reste. */
.hero--photo .pass::before,.hero--photo .pass::after,
.hero--photo .pass__top::before,.hero--photo .pass__top::after,
.hero--photo .pass__stub::before,.hero--photo .pass__stub::after{visibility:hidden}

/* ── 4 · L'EN-TÊTE AU-DESSUS DE LA PHOTO ─────────────────────────────────
   La bascule claire/sombre du logotype « repart si une photo sombre
   revient » (section 4 du hero crème) : c'est le cas. Seulement tant que
   l'en-tête flotte sur le hero — dès `.is-solid`, fond crème, tout revient.

   ⚠ `:has()` et non une classe posée sur <body> : l'en-tête est écrit AVANT
     le bloc, qui seul sait si la photo s'affiche. Un navigateur sans
     `:has()` garde l'en-tête sombre — lisible sur le voile, juste moins
     élégant. */
body:has(.hero--photo) .header:not(.is-solid){color:var(--c-cream)}
body:has(.hero--photo) .header:not(.is-solid) .brand__logo--light{opacity:1}
body:has(.hero--photo) .header:not(.is-solid) .brand__logo--dark{opacity:0}
body:has(.hero--photo) .header:not(.is-solid) .nav a{
  border-color:rgba(247,241,228,.5);color:var(--c-cream);
  background:rgba(10,37,64,.22);backdrop-filter:blur(8px)}
body:has(.hero--photo) .header:not(.is-solid) .nav a:hover{background:var(--c-cream);color:var(--c-ink);border-color:var(--c-cream)}
body:has(.hero--photo) .header:not(.is-solid) .burger{border-color:rgba(247,241,228,.5);background:rgba(10,37,64,.22)}
body:has(.hero--photo) .header:not(.is-solid) .burger span{background:var(--c-cream)}
body:has(.hero--photo) .header:not(.is-solid) .burger:hover{background:var(--c-cream);border-color:var(--c-cream)}
body:has(.hero--photo) .header:not(.is-solid) .burger:hover span,
body:has(.hero--photo) .header:not(.is-solid) .burger:focus-visible span{background:var(--c-ink)}

/* ── LES TITRES DES CARTES D'ATELIER, À TOUTES LES LARGEURS ─────────────
   15/09/2026. 1,02 venait des maquettes, aux titres de deux ou trois mots.
   Les vrais titres de la CCI passent à trois lignes même sur bureau, en
   capitales, avec des accents (Ó, Ñ) et des guillemets qui mordaient sur la
   ligne du dessus. Le téléphone prend un cran de plus, plus bas. */
.ticket__title{line-height:1.12}

/* ── LA FLÈCHE DU LIEN « SEMANA DE FRANCIA » SUIT SON TEXTE ────────────────
   17/09/2026. `.arrow` est un `inline-flex` : dès que le libellé passe sur
   deux lignes, le texte occupe toute la largeur et la flèche du `::after` est
   rejetée seule, à droite, sous la dernière ligne. En `inline`, elle redevient
   un caractère du texte et colle au dernier mot. Borné à cet encadré : ailleurs
   les libellés tiennent sur une ligne, et le `inline-flex` y sert au survol. */
.tiltp__why .arrow{display:inline}

/* ══ TÉLÉPHONE · RETOURS DU 15/09/2026 ════════════════════════════════════
   Deux réglages mesurés sur 390-400 px, sans effet au-dessus de 760 px. */
@media (max-width:759px){

  /* ── LE BILLET DU HERO SE CENTRE AUSSI DEBOUT ──────────────────────────
     Le centrage n'était posé qu'à partir de 760 px (« LE CORPS DU BILLET SE
     CENTRE », plus haut). En dessous, `justify-items:start` restait : chaque
     ligne se centrait dans SA largeur, calée à gauche. Mesuré par rapport à
     l'axe de la carte : lieu −58 px, « Pase general » −24, dates −17,
     « Entrada libre » −9. Tout le hero mobile est centré ; le billet aussi. */
  .pass__top{justify-items:center;text-align:center}
  .pass__free{align-self:center}

  /* ── LES TITRES D'ATELIER RESPIRENT ─────────────────────────────────────
     1,02 tient sur une ou deux lignes de bureau. Sur téléphone, les vrais
     titres de la CCI passent à quatre ou cinq lignes en capitales, et les
     lignes se touchaient. `.taller` borne le titre de page à la FICHE
     atelier : les bandeaux des autres pages ne bougent pas. */
  .ticket__title{line-height:1.2}
  .taller .pbanner__h{line-height:1.1}
}
