/* ══════════════════════════════════════════════════════════════════════════
   La Taverne · fx.css : la feuille des effets (octobre 2026)
   --------------------------------------------------------------------------
   Un bloc par module d'effets (anim.js, verre.js, feuille.js, vivant.js,
   scene.js), balisé « (bloc de …) ». Tout ce qui change la mise en page de
   la coquille est accroché à la classe que le module pose sur <html> en
   s'exécutant (`tv-anim`, `tv-verre`, `tv-feuille`) : sans le script, la
   coquille d'avant (style.css), sans saut. Les jetons sont ceux de
   style.css ; une valeur propre aux effets vit ici, dans les deux thèmes.

   Trois règles de la maison :
   1. le mouvement est joué par les scripts (WAAPI) ; la feuille ne pose que
      des ÉTATS de repos et des transitions courtes, que la règle unique
      `:root[data-anim="off"] *` de style.css ramène à rien ;
   2. aucune media query de mouvement ici (l'arbitre est TavernePrefs, relu
      par les scripts : « Toujours » doit pouvoir l'emporter sur l'appareil) ;
   3. le survol vit sous `(hover: hover) and (pointer: fine)`, et le bloc
      `@media (hover: none)` est le DERNIER de la feuille.
   ══════════════════════════════════════════════════════════════════════════ */


/* ── 1 · La scène s'allume : anim.js ───────────────────────────────────── */
/* (bloc de anim.js) */
/* Le voile de nuit posé par-dessus la scène de jeu quand elle s'ouvre : il
   se lève en vacillant (son opacité seulement), puis quitte le DOM. Son
   plan est posé par le script (un au-dessus de la scène). */
.tv-allume {
  position: fixed;
  inset: 0;
  z-index: 56;
  background: radial-gradient(120% 90% at 50% 40%, rgba(14, 9, 5, .9), #050302 72%);
  pointer-events: none;
}


/* ── 2 · Le verre liquide : verre.js ───────────────────────────────────── */
/* (bloc de verre.js) */

/* G1 · La capsule (style.css la dessine déjà) : au doigt, c'est elle qui
   reçoit le geste du glissé, pas la page ; et elle se contracte en ressort
   (G3). Pas en colonne (900 px et plus). */
@media (max-width: 899px) {
  /* `overflow: hidden` : la ceinture de la lentille. verre.js la garde déjà
     dans la course (bornée, écrasée contre le mur), mais un appui au bord
     ou un réglage futur ne doit jamais coller une bulle de verre HORS de la
     barre flottante. La capsule (rayon 999 px) découpe ses enfants, pas sa
     propre ombre ; mesuré : seule l'ombre portée de la lentille, sous le
     bord bas, perd quelques pixels presque invisibles. */
  html.tv-verre .onglets {
    overflow: hidden;
    touch-action: none;
    transition: transform var(--tv-dur-m) var(--tv-ressort);
  }
  html.tv-verre .onglets .onglet > span:not(.onglet-pastille) {
    transition: opacity var(--tv-dur-s) var(--tv-ease-out);
  }
  html.tv-verre .onglets .onglet .ico {
    transition-property: transform, color, translate;
    transition-duration: var(--tv-dur-xs), var(--tv-dur-xs), var(--tv-dur-m);
    transition-timing-function: var(--tv-ease-out), var(--tv-ease-out), var(--tv-ressort);
  }
  /* .92 et pas moins : l'onglet de 52 px passe à 47,8 px, au-dessus du
     plancher tactile. Les libellés restent dans le DOM (noms accessibles). */
  html.tv-verre .onglets.is-reduite { transform: translateY(5px) scale(.92); }
  html.tv-verre .onglets.is-reduite .onglet > span:not(.onglet-pastille) { opacity: 0; }
  html.tv-verre .onglets.is-reduite .onglet .ico { translate: 0 7px; }
}

/* L'onglet que la lentille survole pendant un glissé prend la teinte de
   l'onglet courant : on voit où le doigt va se poser. */
html.tv-verre .onglets .onglet { -webkit-tap-highlight-color: transparent; }
html.tv-verre .onglets .onglet.is-sous-lentille { color: var(--tv-ink); }
html.tv-verre .onglets .onglet.is-sous-lentille .ico { color: var(--tv-accent-texte); }

/* G2 · La lentille. Deux calques : `.tv-lentille` porte la position
   (translate, posé en ligne par le script, animé en ressort), son verre
   l'étirement (scale, WAAPI) et l'appui (la propriété `scale`, en
   transition). Sa taille est celle d'un onglet, posée par le script. */
.tv-lentille {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 0;
  border-radius: 999px;
  opacity: 0;
  pointer-events: none;
  will-change: transform;
}
.tv-lentille.is-posee { opacity: 1; }
.tv-lentille-verre {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background-color: var(--tv-lentille-fond);
  background-image: linear-gradient(180deg, var(--tv-lentille-reflet) 0, transparent 58%);
  box-shadow: var(--tv-lentille-ombre), 0 0 0 .5px var(--tv-lentille-bord), inset 0 1px 0 var(--tv-lentille-lisere);
  transform-origin: 50% 50%;
  transition: scale var(--tv-dur-s) cubic-bezier(.3, 1.6, .5, 1);
}
/* L'encoche de cuivre, sur la lentille (celle de l'onglet s'efface quand
   la lentille est posée : `.has-lentille`, style.css). */
.tv-lentille-verre::after {
  content: "";
  position: absolute;
  top: 3px;
  left: 50%;
  width: 16px;
  height: 3px;
  margin-left: -8px;
  border-radius: 2px;
  background: var(--tv-accent);
}
.tv-lentille.is-appui .tv-lentille-verre { scale: 1.1; transition-duration: var(--tv-dur-xs); }
.tv-lentille.is-glisse .tv-lentille-verre { scale: 1.14; }
/* La colonne (900 px et plus) : un rectangle doux sous la ligne de
   l'onglet, l'encoche sur le bord gauche de la colonne. */
@media (min-width: 900px) {
  .tv-lentille { border-radius: var(--tv-rayon-m); }
  .tv-lentille-verre {
    background-color: var(--tv-accent-doux);
    background-image: none;
    box-shadow: none;
  }
  .tv-lentille-verre::after { top: 50%; left: -12px; width: 3px; height: 24px; margin: -12px 0 0; border-radius: 0 2px 2px 0; }
  .tv-lentille.is-appui .tv-lentille-verre { scale: 1.03; }
  .tv-lentille.is-glisse .tv-lentille-verre { scale: 1.05; }
}

/* G4 · La barre du haut devient du verre (app.js pose `is-verre`) : le
   mot-symbole se resserre déjà (style.css), les boutons le suivent. */
html.tv-verre .barre-haut .barre-actions {
  transform-origin: 100% 50%;
  transition: scale var(--tv-dur-m) var(--tv-ressort);
}
html.tv-verre .barre-haut.is-verre .barre-actions { scale: .94; }

/* G5 · Les boutons répondent au doigt. Ceux de la barre du haut : une
   pastille de verre apparaît sous l'icône et un reflet part du point touché
   (`--tv-bx`, `--tv-by`, posés par le script). L'appui écrase par la
   propriété `scale` (en transition courte, PENDANT l'appui seulement) : le
   rebond du relâché est un ressort WAAPI, sans transition sur la même
   propriété. */
html.tv-verre #barreHaut .ico-btn .ico { position: relative; z-index: 1; }
html.tv-verre #barreHaut .ico-btn::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background:
    radial-gradient(circle at var(--tv-bx, 50%) var(--tv-by, 50%), var(--tv-verre-rim) 0, transparent 62%),
    var(--tv-line);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--tv-dur-s) var(--tv-ease-out);
}
html.tv-verre #barreHaut .ico-btn.is-presse::before { opacity: 1; transition-duration: 60ms; }
html.tv-verre #barreHaut .ico-btn.is-presse { scale: .9; transition: scale 110ms var(--tv-ease-out); }
html.tv-verre #barreHaut .ico-btn:active { transform: none; }
/* Les autres boutons de l'app, de l'accueil et des feuilles : l'appui
   écrase par `scale` (le transform de style.css est rendu au repos), le
   relâché rebondit en ressort (verre.js). La scène de jeu garde les siens. */
html.tv-verre :is(#app, #accueil, .feuille) .btn:active:not([disabled]) {
  transform: none;
  scale: .96;
  transition: scale 90ms var(--tv-ease-out), background-color var(--tv-dur-xs) var(--tv-ease-out),
              border-color var(--tv-dur-xs) var(--tv-ease-out);
}
html.tv-verre :is(#app, #accueil, .feuille) .ico-btn:active:not([disabled]) {
  transform: none;
  scale: .9;
  transition: scale 90ms var(--tv-ease-out);
}


/* ── 3 · Les feuilles d'iOS et l'île : feuille.js ──────────────────────── */
/* (bloc de feuille.js) */
/* Le vocabulaire. `--tv-recul-s`, `--tv-empile-s` et `--tv-empile-y` sont
   recopiés dans feuille.js (`RECUL`, `EMPILE_S`, `EMPILE_Y`) : un test
   relit les deux. Le haut du recul (`--tv-recul-t`) et la découpe sont
   mesurés au geste et posés en ligne sur la coque. */
:root {
  --tv-recul-s: .93;
  --tv-recul-rayon: 14px;
  --tv-empile-s: .94;
  --tv-empile-y: 10px;
  --tv-carte-lisere: rgba(31, 25, 18, .08);
  --tv-coque-lisere: transparent;
  --tv-ile-capsule: var(--tv-ink);
}
:root[data-theme="dark"] {
  --tv-carte-lisere: rgba(255, 240, 220, .1);
  --tv-coque-lisere: rgba(255, 240, 220, .24);
}

/* Les calques posés dans <body> par feuille.js : la coulisse noire derrière
   la coque reculée (sous tout), le voile qui descend sur elle (sous les
   feuilles, 60), et la sonde qui résout la zone sûre du haut. Aucun ne
   prend le pointeur (un toucher sur le voile tombe sur le <body>, et
   app.js y ferme la feuille du dessus). */
.tv-carte-coulisse,
.tv-carte-voile {
  position: fixed;
  inset: 0;
  pointer-events: none;
}
/* La coulisse est le PREMIER enfant du <body>, au plan 0 (le fond opaque du
   <body> couvrirait un plan négatif) : elle passe sous la coque transformée,
   qui la suit dans l'ordre du document. Seulement quand une coque recule. */
.tv-carte-coulisse { z-index: 0; display: none; background: #000; }
.tv-carte-voile { z-index: 59; display: none; opacity: 0; background: var(--tv-voile-fort); }
html.tv-recul-coque .tv-carte-coulisse,
html.tv-recul .tv-carte-voile,
html.tv-recul-retour .tv-carte-voile { display: block; }
html.tv-recul .tv-carte-voile { opacity: 1; }
.tv-sonde-sure {
  position: fixed;
  top: 0;
  left: -9999px;
  width: 1px;
  height: env(safe-area-inset-top, 0px);
  visibility: hidden;
  pointer-events: none;
}

/* Pendant un glissé au doigt, rien ne se sélectionne. */
html.tv-glisse body { -webkit-user-select: none; user-select: none; }

@media (max-width: 719.98px) {
  /* La carte : le voile n'est plus l'ombre de la carte (il glisserait avec
     elle) mais le calque qui descend sur la coque ; la carte garde un filet. */
  html.tv-feuille .feuille {
    overflow: hidden;
    box-shadow: 0 0 0 1px var(--tv-carte-lisere);
  }
  /* L'en-tête se tire au doigt (sa poignée est dessinée par style.css) ; ses
     boutons se touchent toujours. */
  html.tv-feuille .feuille-tete { touch-action: none; }
  /* Une carte ouverte par-dessus une autre : celle du dessous recule d'un
     cran (elle monte et rétrécit) et s'assombrit un peu. */
  html.tv-feuille .feuille.is-empilee {
    transform: translateY(calc(-1 * var(--tv-empile-y))) scale(var(--tv-empile-s));
    transform-origin: 50% 0;
  }
  html.tv-feuille .feuille.is-empilee::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 3;
    background: #000;
    opacity: .08;
    pointer-events: none;
  }
  html[data-theme="dark"].tv-feuille .feuille.is-empilee::after { opacity: .24; }

  /* La coque qui recule derrière la carte (l'app, ou l'accueil). L'origine
     (le haut de l'écran, au milieu) et la découpe à l'écran sont mesurées
     au geste ; la découpe arrondit les coins ; le fond est posé pour que la
     coulisse noire ne passe pas au travers. */
  html.tv-recul .tv-coque,
  html.tv-recul-retour .tv-coque {
    transform-origin: var(--tv-recul-ox, 50%) var(--tv-recul-oy, 0px);
    clip-path: inset(var(--tv-recul-haut, 0px) 0 var(--tv-recul-bas, 0px) 0 round var(--tv-recul-rayon));
    background: var(--tv-bg);
  }
  html.tv-recul .tv-coque {
    transform: translateY(var(--tv-recul-t, 26px)) scale(var(--tv-recul-s));
    pointer-events: none;
  }
  /* Le filet de la coque reculée, en sombre : sur le noir de la coulisse, le
     haut arrondi d'une app sombre ne se distinguait plus (mesuré sous le
     voile : 7, 6, 4 contre 0, 0, 0). Il épouse la découpe et vit DANS la
     coque (transformée, elle est son bloc conteneur) : il recule, suit le
     doigt et revient avec elle, image par image. En clair, l'app se détache
     seule du noir, le filet est transparent. */
  html.tv-recul .tv-coque::after,
  html.tv-recul-retour .tv-coque::after {
    content: "";
    position: absolute;
    top: var(--tv-recul-haut, 0px);
    right: 0;
    left: 0;
    height: 100vh;
    border-radius: var(--tv-recul-rayon);
    box-shadow: inset 0 0 0 1px var(--tv-coque-lisere);
    pointer-events: none;
    z-index: 50;
  }
}

/* La barre d'onglets est fixe DANS l'app : une app transformée devient son
   bloc conteneur, et elle sauterait au bas du document. On la recale
   d'autant le temps du recul (`--tv-recul-barre`, mesuré au geste). La
   propriété `translate` s'ajoute au reste. */
html.tv-recul #app.tv-coque .onglets,
html.tv-recul-retour #app.tv-coque .onglets { translate: 0 var(--tv-recul-barre, 0px); }

/* Les clones qui partent : jamais le pointeur. */
.tv-carte-clone,
.tv-ile-clone { pointer-events: none; }

/* L'île : la naissance efface le fond du vrai message, c'est la capsule
   décorative posée dessous qui se déploie. */
html.tv-feuille .ile.is-nait {
  background: transparent;
  box-shadow: none;
}
.tv-ile-capsule {
  position: fixed;
  z-index: 94;
  box-sizing: border-box;
  border-radius: 999px;
  background: var(--tv-ile-capsule);
  box-shadow: 0 10px 28px -10px rgba(0, 0, 0, .5);
  transform-origin: 50% 50%;
  pointer-events: none;
}
/* L'île se pose SOUS ce qu'elle recouvrirait. Dans l'app, sous la barre du
   haut (elle y masquait le mot-symbole et, à 320 px, les boutons qu'on
   vient de toucher : « Le son est coupé » cachait le bouton du son). */
html.tv-feuille body:not(.en-scene) #app:not([hidden]) ~ .ile {
  top: calc(env(safe-area-inset-top, 0px) + var(--tv-haut-h) + 6px);
}
/* Une feuille haute ouverte : sous son en-tête (le haut est mesuré par le
   script), jamais sur son titre. Même spécificité que la règle du dessus,
   et après elle : elle l'emporte. */
html.tv-feuille.tv-ile-basse body #toast.ile:not([hidden]) {
  top: var(--tv-ile-haut, calc(env(safe-area-inset-top, 0px) + 10px));
}


/* ── 4 · Les chiffres qui roulent, les étincelles : vivant.js ──────────── */
/* (bloc de vivant.js) */
/* Le compteur : la spécificité la plus basse possible (`:where`), pour que
   la feuille du lot qui pose le nombre garde la main sur sa mise en page. */
:where(.tv-compte) { font-variant-numeric: tabular-nums; }
:where(.tv-compte.is-roule) { position: relative; }
.tv-roule {
  display: inline-flex;
  align-items: flex-start;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.tv-roule-col {
  display: block;
  overflow: hidden;
}
.tv-roule-bande { display: block; will-change: transform; }
.tv-roule-bande > span,
.tv-roule-fixe {
  display: block;
  text-align: center;
}
/* Ce qu'on lit pendant le roulement : la valeur finale, masquée à l'écran. */
.tv-roule-lu {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Les étincelles : un calque fixe (son plan est posé par le script, 65 au
   moins et toujours au-dessus de la scène), qui quitte le DOM avec la
   dernière braise. Une braise est un grain allongé, chaud, qui luit. */
.tv-eclats {
  position: fixed;
  inset: 0;
  z-index: 65;
  overflow: hidden;
  pointer-events: none;
}
.tv-braise {
  position: absolute;
  border-radius: 50% / 42%;
  box-shadow: 0 0 6px 1px rgba(255, 186, 92, .55);
  opacity: 0;
  will-change: transform, opacity;
}


/* ── 5 · L'arrivée, la lueur, le toucher : scene.js ────────────────────── */
/* (bloc de scene.js) */
/* Le logo s'encre (celui de la barre de l'app, celui du héros de
   l'accueil) : le papier quitte le dessin de gauche à droite, à bord fondu
   (le temps du geste, le logo découpe ce qui dépasse). */
.mot-symbole.is-encre,
.acc-logo.is-encre { position: relative; overflow: hidden; }
.tv-encre {
  position: absolute;
  top: 0;
  bottom: 0;
  left: -28px;
  right: -2px;
  z-index: 2;
  background: linear-gradient(90deg, transparent 0, var(--tv-bg) 28px);
  pointer-events: none;
}

/* La table de l'accueil s'allume : un voile de nuit sur le bois, qui se
   lève en vacillant (sous le bouton pause, 5). */
.tv-allume-table {
  position: absolute;
  inset: 0;
  z-index: 3;
  border-radius: inherit;
  background: radial-gradient(110% 90% at 32% 30%, rgba(12, 8, 4, .86), #060403 70%);
  pointer-events: none;
}

/* La lueur de la chandelle sur la table de l'accueil : une AMBIANCE
   (`tv-ambiant`), la seule boucle permise au repos, arrêtée hors écran,
   page cachée, dés en pause, ou mouvement réduit (elle reste alors posée,
   immobile). Elle déborde un peu de la table (qui la découpe) pour que sa
   respiration ne montre jamais un bord. */
.tv-lueur {
  position: absolute;
  inset: -6%;
  z-index: 2;
  background:
    radial-gradient(ellipse 46% 40% at 31% 27%, rgba(255, 196, 120, .26), rgba(255, 170, 80, .1) 46%, rgba(255, 160, 70, 0) 70%),
    radial-gradient(ellipse 80% 70% at 50% 50%, rgba(255, 176, 82, .05), rgba(255, 176, 82, 0) 70%);
  opacity: .86;
  pointer-events: none;
}

/* La prochaine table de la tournée s'allume à l'arrivée dans l'app. */
.tv-lueur-flash {
  position: absolute;
  inset: 0;
  z-index: 0;
  border-radius: inherit;
  background: radial-gradient(90% 70% at 50% 30%, var(--tv-chandelle), rgba(255, 176, 82, 0) 72%);
  opacity: 0;
  pointer-events: none;
}

/* L'interrupteur caché qui fait vibrer l'iPhone : hors de l'écran, jamais
   en `display: none` (il serait muet). */
.tv-haptique {
  position: fixed;
  top: 0;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
  opacity: 0;
  pointer-events: none;
}


/* ── 6 · Survol (pointeur fin), puis `hover: none` EN DERNIER ─────────── */
/* Une media query n'ajoute aucune priorité : seul l'ordre tranche. */
@media (hover: hover) and (pointer: fine) {
  html.tv-verre #barreHaut .ico-btn:hover::before { opacity: .55; }
}

@media (hover: none) {
  /* Au doigt, le reflet de verre ne vient que de l'appui, jamais d'un
     survol resté collé après un tap. */
  html.tv-verre #barreHaut .ico-btn:hover:not(.is-presse)::before { opacity: 0; }
}
