/* Robert Saxton — animations.
   Activées page par page (body.fx) et seulement quand le script a posé html.fx-on :
   sans script, ou avec « réduire les animations », la page reste entière et immobile. */

/* ---------- L'ampleur du mouvement suit le rang ----------
   Partie : un vrai geste (chiffre, titre ligne à ligne, filet sur toute la largeur).
   Chapitre : un simple fondu, bloc après bloc.
   Infos pratiques : aucun effet, tout est là tout de suite. */
:root { --ease: cubic-bezier(.2, .7, .1, 1); }

/* Ouverture de partie. */
.fx-on .fx .part-num { opacity: 0; transform: translateY(30px); transition: opacity 1.2s ease, transform 1.4s var(--ease); }
.fx-on .fx .part-label { opacity: 0; transition: opacity .8s ease .1s; }
.fx-on .fx .part h2 .ln { overflow: hidden; padding-bottom: .06em; }
.fx-on .fx .part h2 .ln > span { display: inline-block; transform: translateY(105%); transition: transform 1.1s var(--ease); }
.fx-on .fx .part h2 .ln + .ln > span { transition-delay: .12s; }
.fx-on .fx .part-promise { opacity: 0; transform: translateY(12px); transition: opacity 1s ease .45s, transform 1.2s var(--ease) .45s; }
.fx-on .fx .part-rule { transform: scaleX(0); transition: transform 1.6s var(--ease) .3s; }
.fx-on .fx .part-head.in :is(.part-num, .part-label, .part-promise) { opacity: 1; transform: none; }
.fx-on .fx .part-head.in .ln > span { transform: none; }
.fx-on .fx .part-head.in .part-rule { transform: scaleX(1); }

/* Changement de lumière : le fond de la partie s'allume en y entrant. */
.fx-on .fx :is(.part-2, .part.readers) { transition: background-color 1.2s ease; }
.fx-on .fx .part-2:not(.lit) { background-color: var(--night); }
.fx-on .fx .part.readers:not(.lit) { background-color: var(--night); }

/* Chapitre : titre puis blocs, en fondu. */
.fx-on .fx :is(.chap-h, .story p, .margin-notes, .chars article, .era figcaption, .season figure, .season-list li, .gloss > div, .land > div:first-child,
	.author-photo, .man-quote, .facts-line, .reviews figure, .featured-quote, .chap .note, .readers .note) {
	opacity: 0; transform: translateY(14px); transition: opacity .9s ease, transform 1.1s var(--ease); }
.fx-on .fx :is(.chap.in, .readers > .inner.in) :is(.chap-h, .story p, .margin-notes, .chars article, .era figcaption, .season figure, .season-list li, .gloss > div,
	.land > div:first-child, .author-photo, .man-quote, .facts-line, .reviews figure, .featured-quote, .note) { opacity: 1; transform: none; }
.fx-on .fx .in :is(.story p, .chars article, .reviews figure, .season-list li, .gloss > div):nth-child(2) { transition-delay: .15s; }
.fx-on .fx .in :is(.story p, .chars article, .reviews figure, .season-list li, .gloss > div):nth-child(3) { transition-delay: .3s; }
.fx-on .fx .in :is(.story p, .chars article, .reviews figure, .season-list li, .gloss > div):nth-child(n+4) { transition-delay: .45s; }
.fx-on .fx .in :is(.margin-notes, .man-quote, .featured-quote + .featured-quote) { transition-delay: .3s; }
.fx-on .fx .in :is(.facts-line, .era figcaption) { transition-delay: .45s; }
.fx-on .fx .in .note { transition-delay: .6s; }

/* Arrivée sur l'accueil : le texte monte, les livres se posent. */
.fx-on .home.fx .hero-text > * { opacity: 0; animation: rise-in 1s var(--ease) forwards; }
.fx-on .home.fx .hero-text > :nth-child(2) { animation-delay: .1s; }
.fx-on .home.fx .hero-text > :nth-child(3) { animation-delay: .35s; }
.fx-on .home.fx .hero-text > :nth-child(4) { animation-delay: .55s; }
.fx-on .home.fx .hero-text > :nth-child(5) { animation-delay: .8s; }
.fx-on .home.fx .hero-books .book3d img { animation: settle 1.6s var(--ease) .3s both; }
.fx-on .home.fx .hero-books .b2 img { animation-delay: .45s; }
@keyframes rise-in { from { opacity: 0; transform: translateY(18px); } to { opacity: 1; transform: none; } }
@keyframes settle { from { opacity: 0; transform: translateY(70px) rotate(-4deg); } }

/* Chemin de la trilogie : chaque couverture s'allume quand la ligne l'atteint. */
.fx-on .fx .path { --p: 0; }
.fx-on .fx .tome :is(.cover img, .cover-ghost) { transition: filter .9s ease, opacity .9s ease; }
.fx-on .fx .tome > :not(.cover):not(.cover-ghost) { transition: opacity .8s ease; }
.fx-on .fx .tome:not(.lit) :is(.cover img, .cover-ghost) { filter: grayscale(1) brightness(.5); opacity: .5; }
.fx-on .fx .tome:not(.lit) > :not(.cover):not(.cover-ghost) { opacity: .3; }

/* Carte : la rive se trace, puis les lieux paraissent et Grand Marais respire. */
.fx-on .fx .map .lake { stroke-dasharray: 1; stroke-dashoffset: 1; fill-opacity: 0; transition: stroke-dashoffset 2.2s ease-in-out, fill-opacity 1.2s ease 1.6s; }
.fx-on .fx .map :is(.pl, text) { opacity: 0; transition: opacity .8s ease 1.8s; }
.fx-on .fx .map.in .lake { stroke-dashoffset: 0; fill-opacity: 1; }
.fx-on .fx .map.in :is(.pl, text) { opacity: 1; }
.map .ring { fill: none; stroke: var(--red); stroke-width: 1.5; opacity: 0; transform-box: fill-box; transform-origin: center; }
.fx-on .fx .map.in .ring { animation: ping 2.6s ease-out 2.4s infinite; }
@keyframes ping { from { transform: scale(1); opacity: .9; } to { transform: scale(4); opacity: 0; } }

/* Voix : les barres se lèvent de gauche à droite, comme si Robert commençait à parler. */
.fx-on .fx .voice .wave i { transform: scaleY(.08); transition: transform .6s var(--ease); transition-delay: calc(var(--i) * 22ms); }
.fx-on .fx .voice.in .wave i { transform: none; }

/* Page d'extrait : elle se redresse comme un livre qu'on ouvre. */
.fx-on .fx .excerpt-page { opacity: 0; transform: perspective(1200px) rotateX(18deg) translateY(30px); transform-origin: bottom; transition: transform 1.4s var(--ease), opacity 1s ease; }
.fx-on .fx .excerpt-page.in { opacity: 1; transform: none; }

/* La lettrine rougeoit une fois, comme une braise qu'on attise. */
.fx-on .tome-1.fx .chap.in .story .dropcap { animation: kindle 2.4s ease .3s both; }
@keyframes kindle { 0% { color: #5a1a10; text-shadow: none; } 45% { color: #ff7a45; text-shadow: 0 0 18px rgba(255, 110, 50, .7); } 100% { color: var(--red); text-shadow: 0 0 0 transparent; } }
/* Tome 1 : une braise court le long du filet de partie. */
.fx-on .tome-1.fx .part-rule { height: 2px; background: linear-gradient(90deg, var(--line) 0 40%, #ff7a45 48%, #ffd2a8 50%, #ff7a45 52%, var(--line) 60% 100%) 100% 0 / 300% 100% no-repeat; }
.fx-on .tome-1.fx .part-head.in .part-rule { animation: glint 2s ease 1.2s both; }

/* ---------- Tome 1 : traverser les 500 ans (lié au défilement, --p de 0 à 1) ---------- */
.era-img { position: relative; overflow: hidden; border-radius: var(--r); }
.era-img img { border-radius: 0; }
.fx-on .fx .eras { --p: 0; }
/* Le présent s'éloigne. */
.fx-on .fx .era.now .era-img::after { content: ""; position: absolute; inset: 0; background: #0a0b0f; opacity: calc(var(--p) * .55); pointer-events: none; }
/* Le passé sort de la brume. */
.fx-on .fx .era.past img { transform: scale(calc(1.08 - var(--p) * .08)); }
.fx-on .fx .era.past .era-img::after { content: ""; position: absolute; inset: 0; pointer-events: none;
	background: linear-gradient(180deg, rgba(222, 218, 208, .95), rgba(190, 186, 176, .9)); opacity: calc(1 - var(--p) * 1.15); }
/* Le fil entre les deux époques se remplit de braise : premier segment, puis second. */
.fx-on .fx .gate::before { background: linear-gradient(#ff7a45, var(--red)) top / 100% calc(clamp(0, var(--p) * 2, 1) * 100%) no-repeat, var(--line); }
.fx-on .fx .gate::after { background: linear-gradient(var(--red), #ff7a45) top / 100% calc(clamp(0, var(--p) * 2 - 1, 1) * 100%) no-repeat, var(--line); }
.fx-on .fx .gate::before, .fx-on .fx .gate::after { width: 2px; }
.fx-on .fx .gate-n { font-variant-numeric: tabular-nums; min-width: 3ch; text-align: center; }

/* ---------- Tome 2 : givre et aurore ---------- */
/* La lettrine se dégivre : blanc de givre, puis le vert du livre. */
.fx-on .tome-2.fx .chap.in .story .dropcap { animation: thaw 2.6s ease .3s both; }
@keyframes thaw { 0% { color: #eef8f9; text-shadow: 0 0 14px rgba(160, 225, 235, .95); } 100% { color: var(--red); text-shadow: 0 0 0 transparent; } }
/* Un reflet de givre parcourt le filet de partie quand il se trace. */
.fx-on .tome-2.fx .part-rule { height: 2px; background: linear-gradient(90deg, var(--line) 0 40%, var(--red) 46%, #dff6f8 50%, var(--red) 54%, var(--line) 60% 100%) 100% 0 / 300% 100% no-repeat; }
.fx-on .tome-2.fx .part-head.in .part-rule { animation: glint 2s ease 1.2s both; }
@keyframes glint { from { background-position: 100% 0; } to { background-position: 0 0; } }
/* Le givre gagne les bords de la photo du lac (--f de 0 à 1). */
.tome-2 .season figure { position: relative; }
.fx-on .tome-2.fx .season figure { --f: 0; }
.fx-on .tome-2.fx .season figure::after { content: ""; position: absolute; inset: 0; border-radius: var(--r); pointer-events: none; opacity: var(--f);
	background: radial-gradient(120% 90% at 50% 45%, transparent 48%, rgba(236, 246, 248, .55) 72%, rgba(248, 252, 253, .92) 100%);
	box-shadow: inset 0 0 40px 6px rgba(255, 255, 255, .5); }

@media (max-width: 899px) {
	.fx-on .fx .gate::before { background: linear-gradient(90deg, #ff7a45, var(--red)) left / calc(clamp(0, var(--p) * 2, 1) * 100%) 100% no-repeat, var(--line); }
	.fx-on .fx .gate::after { background: linear-gradient(90deg, var(--red), #ff7a45) left / calc(clamp(0, var(--p) * 2 - 1, 1) * 100%) 100% no-repeat, var(--line); }
	.fx-on .fx .gate::before, .fx-on .fx .gate::after { width: auto; height: 2px; }
}

