/* ANTONY BESSALA - effects layer. Loaded after arena.css. Every rule here is optional decoration:
   without JS, in Lite mode (html.is-lite / body.lite) or with reduced motion the page is complete without this file.
   JS side: /js/fx/fx.js (all effects) and /js/fx/trophy.js (Three.js, only on /titles). Notes for the next developer sit above each block. */

/* ---- 01 Loader: name written in ember light. Hidden until JS built the letters; 3.6 s CSS safety net removes it even if JS dies. ---- */
html.fx-loader .loader-name:not(.built){opacity:0}
.loader-name span{animation-delay:calc(var(--i,0)*var(--ls,55ms))}
html.fx-loader #loader{animation:ldFail .01s 3.6s forwards}
@keyframes ldFail{to{opacity:0;visibility:hidden}}
@media (max-width:760px){.loader-name{font-size:clamp(2rem,9vw,3rem)}}
html.is-lite #loader{display:none!important}

/* ---- 02 Spotlight cursor: ring moves by transform (no layout), turns bone + grows over links and buttons ---- */
/* the ring is its own element (made by fx.js) because inside the multiply layer ember on dark would turn black */
#spot::after{display:none}
.spot-ring{position:fixed;left:0;top:0;width:14px;height:14px;border:1.5px solid var(--ember);border-radius:50%;box-shadow:0 0 18px rgba(255,77,28,.7);pointer-events:none;z-index:calc(var(--z-spot) + 1);scale:1;opacity:0;transition:scale .2s var(--ease-out),border-color .2s,opacity .3s;will-change:translate}
body.has-spot .spot-ring{opacity:1}
.spot-ring.link{scale:1.8;border-color:rgba(242,237,228,.6);box-shadow:none}
.spot-ring.off,body.lights-on .spot-ring{opacity:0!important}
#spot.off{opacity:0}
body.lite .spot-ring,body.touch .spot-ring{display:none}
body.has-spot .arena-photo{transition:none}
body.lights-on #spot{opacity:0}
body[data-off~="spot"] #spot{display:none!important}

/* ---- 03 Embers canvas: dims to 30 % in the lit Gear room, off on phones there ---- */
#embers{transition:opacity .55s var(--ease-out)}
body.lights-on #embers,body.is-gear #embers{opacity:.3}
body.touch.lights-on #embers{opacity:0}
body[data-off~="embers"] #embers{display:none!important}

/* ---- 04 Kinetic headline: letters rise in; real text is on the h1 aria-label. Safety nets: shows by itself after 3 s ---- */
html.fx-kin .kinetic:not(.kin-split){visibility:hidden;animation:kinFail .01s 3s forwards}
@keyframes kinFail{to{visibility:visible}}
.kinetic .kw{display:inline-block;white-space:nowrap}
.kinetic .kc{display:inline-block;opacity:0;transform:translateY(.4em) rotate(2deg)}
.kinetic.go .kc{animation:kc .6s var(--ease-hit) forwards;animation-delay:calc(var(--d,0s) + var(--i,0)*var(--st,40ms))}
.kinetic.kin-split:not(.go) .kc{animation:kc .01s 3.6s forwards}
.kinetic .kc.dot{color:var(--ember)}
.kinetic.go .kc.dot{animation-name:kcd}
@keyframes kc{to{opacity:1;transform:none}}
@keyframes kcd{0%{opacity:0;transform:translateY(.4em) scale(1.6)}100%{opacity:1;transform:none}}
@media (max-width:760px){.kinetic .kc{transform:translateY(.4em)}}
body.lite .kinetic .kc,body[data-off~="kinetic"] .kinetic .kc{opacity:1!important;transform:none!important;animation:none!important}

/* ---- 05 Chapter overlays: fade in once when the chapter is 15 % visible (class .in from JS); parallax comes from --ty (JS) ---- */
html.fx-on:not(.is-lite) .chapter .bg:not(.in),html.fx-on:not(.is-lite) .pagehero .bg:not(.in){opacity:0}
.chapter .bg.in,.pagehero .bg.in{animation:bgIn 1.2s var(--ease-in-out)}
@keyframes bgIn{from{opacity:0}}
body[data-off~="overlay"] .chapter .bg,body[data-off~="overlay"] .pagehero .bg{display:none}

/* ---- 06 Origin route: dots pulse as the line reaches them ---- */
.route .city{transform-box:fill-box;transform-origin:center}
.route .city.pulse{animation:cpulse .6s var(--ease-out)}
@keyframes cpulse{50%{transform:scale(1.6)}}

/* ---- 07 Counters + reveals: siblings in grids stagger by 90 ms (--rd set by JS, max 8) ---- */
html.js .reveal{transition-delay:calc(var(--rd,0)*90ms)}
.counters b{font-variant-numeric:tabular-nums}
body[data-off~="reveal"] .reveal{opacity:1!important;transform:none!important;transition:none!important}

/* ---- 08 Timeline rail: the stop nearest the centre grows to full size and turns to colour (class .center from JS) ---- */
html.fx-on:not(.is-lite) .stop{scale:.96;transition:transform var(--t-fast),scale .3s var(--ease-out),border-color .3s}
html.fx-on:not(.is-lite) .stop.center{scale:1;border-color:var(--gold-dim)}
html.fx-on:not(.is-lite) .stop.center img{filter:none}
.rail.drag .stop{pointer-events:none}

/* ---- 15 3D tilt: JS writes the transform while the pointer moves; the glare follows the pointer ---- */
.tilt .glare{position:absolute;inset:0;pointer-events:none;opacity:0;transition:opacity .15s;z-index:3;background:radial-gradient(circle at var(--gx,50%) var(--gy,50%),rgba(255,255,255,.12),transparent 55%)}
.tilt.is-tilting .glare{opacity:1}
body.lite .glare,body[data-off~="tilt"] .glare{display:none}
.tilt:focus-visible{outline:2px solid var(--gold);outline-offset:3px}

/* ---- 10 Pose scrubber ---- */
.pose-stage .pose-hint{transition:opacity .4s}
.pose-stage.touched .pose-hint{opacity:0}
body.lite .pose-stage img{transition:none}
.pose-stage:focus-visible{outline:2px solid var(--gold);outline-offset:3px}

/* ---- 11 Code accordion: panel height animates with grid rows (any text length, French too); closed panels are hidden from keyboard and readers ---- */
.cp{display:grid;grid-template-rows:0fr;visibility:hidden;transition:grid-template-rows .6s var(--ease-out),visibility 0s .6s}
.cp>div{overflow:hidden;min-height:0}
.code-list li.open .cp{grid-template-rows:1fr;visibility:visible;transition:grid-template-rows .6s var(--ease-out),visibility 0s}
html.js .code-list .cp p{display:block}
body.lite .cp,body[data-off~="code"] .cp{grid-template-rows:1fr;visibility:visible;transition:none}
/* /code page: soft vertical snap + dot navigation (JS adds html.snap-y and .codedots) */
html.snap-y{scroll-snap-type:y proximity;scroll-padding-top:72px}
html.snap-y .code-scenes .scene{scroll-snap-align:start}
.codedots{position:fixed;right:1rem;top:50%;transform:translateY(-50%);display:grid;gap:.9rem;z-index:55}
.codedots a{position:relative;width:12px;height:12px;border:1px solid var(--gold-dim);display:block}
.codedots a::after{content:"";position:absolute;inset:-9px}
.codedots a.on{background:var(--gold)}
@media (max-width:760px){.codedots{right:.5rem}}
body.lite .codedots{display:none}

/* ---- 12 Lights on: the Gear section brightens when it reaches 60 % of the viewport (class body.lights-on from JS) ---- */
html.fx-on:not(.is-lite) body:not(.is-gear) .gear{background:var(--iron);transition:background-color .55s var(--ease-out)}
html.fx-on:not(.is-lite) body:not(.is-gear).lights-on .gear{background:var(--lights-on)}
html.fx-on:not(.is-lite) body:not(.is-gear) .gear::before{opacity:0;transition:opacity .55s var(--ease-out)}
html.fx-on:not(.is-lite) body:not(.is-gear).lights-on .gear::before{opacity:1}
body.lights-on .top{background:linear-gradient(rgba(23,23,27,.92),rgba(23,23,27,0))}
body[data-off~="lightson"] #embers{opacity:.9}
/* mini cart toast + badge pop after a quick add */
.minicart{position:fixed;right:1rem;top:84px;z-index:var(--z-cart);background:var(--iron-2);border:1px solid var(--gold-dim);padding:.8rem 1.1rem;max-width:min(88vw,320px);transform:translateX(130%);transition:transform .45s var(--ease-out);display:flex;gap:1rem;align-items:center;font-size:.85rem}
.minicart.show{transform:none}
.minicart b{font-family:var(--display);font-weight:400;letter-spacing:.04em;text-transform:uppercase;color:var(--gold);display:block}
.minicart a{border-bottom:1px solid var(--gold-dim);color:var(--gold);font-size:.75rem;letter-spacing:.15em;text-transform:uppercase;white-space:nowrap}
body.lite .minicart{transition:none}
.tools .cartlink b{display:inline-block}
.tools .cartlink b.pop{animation:pop .3s var(--ease-out)}
@keyframes pop{50%{transform:scale(1.12)}}

/* ---- 13 Page transitions: View Transitions (Chrome/Edge) cross-fade through iron; other browsers get an iron curtain from fx.js ---- */
@media (prefers-reduced-motion:no-preference){@view-transition{navigation:auto}}
html::view-transition{background:var(--iron)}
::view-transition-old(root){animation:vtOut .35s var(--ease-in) both}
::view-transition-new(root){animation:vtIn .55s .1s var(--ease-out) both}
@keyframes vtOut{to{opacity:0}}
@keyframes vtIn{from{opacity:0}}
html.is-lite::view-transition-old(root),html.is-lite::view-transition-new(root){animation:none}
.top{view-transition-name:top}
html::after{content:"";position:fixed;inset:0;background:var(--iron);z-index:var(--z-transition);opacity:0;pointer-events:none}
html::before{content:"";position:fixed;left:0;top:50%;height:2px;width:100%;background:var(--ember);z-index:91;transform:translateX(-101%);opacity:0;pointer-events:none}
html.fx-leave::after{opacity:1;transition:opacity .35s var(--ease-in)}
html.fx-leave::before{opacity:1;animation:sweep .6s .1s var(--ease-in-out) forwards}
html.fx-arrive::after{opacity:1;animation:arrive .55s var(--ease-out) forwards}
@keyframes arrive{to{opacity:0}}
@keyframes sweep{to{transform:translateX(101%)}}
@media (max-width:760px){html.fx-leave::after{transition-duration:.25s}html.fx-arrive::after{animation-duration:.35s}}

/* ---- 14 Sound toggle (button only exists when the browser has Web Audio; off by default) ---- */
.tools .soundbtn{width:36px;padding:0;justify-content:center}
.tools .soundbtn[hidden],#soundMenu[hidden]{display:none}
.tools .soundbtn[aria-pressed="true"]{background:none;color:var(--gold);border-color:var(--gold-dim)}
.soundbtn:not([aria-pressed="true"]) .sw{display:none}
@media (max-width:960px){html.js .tools .soundbtn{display:none}}
body[data-off~="sound"] #sound,body[data-off~="sound"] #soundMenu{display:none}

/* ---- 09 Trophy room (Three.js, /titles). .is-3d = canvas mounted. Without it the original box + list stay. ---- */
.trophy-room.is-3d{display:block;height:min(78svh,660px);min-height:420px;overflow:hidden;cursor:grab;touch-action:pan-y;user-select:none}
.trophy-room.is-3d:active{cursor:grabbing}
.trophy-room canvas{position:absolute;inset:0;width:100%;height:100%;display:block}
.trophy-room:focus-visible{outline:2px solid var(--gold);outline-offset:3px}
.trophy-card{position:absolute;left:1rem;bottom:1rem;max-width:min(86%,380px);background:rgba(11,11,13,.74);border:1px solid var(--gold-dim);padding:.8rem 1rem;pointer-events:none}
.trophy-card .yr{display:block;font-family:var(--display);font-size:2rem;line-height:1;color:var(--gold);text-transform:uppercase}
.trophy-card b{display:block;font-family:var(--display);font-weight:400;letter-spacing:.03em;text-transform:uppercase;font-size:1.1rem;margin-top:.2rem}
.trophy-card .pl{display:block;color:var(--mute);font-size:.75rem;letter-spacing:.12em;text-transform:uppercase;margin-top:.2rem}
.trophy-card .fd{display:block;color:var(--bone-soft);font-size:.72rem;letter-spacing:.12em;text-transform:uppercase}
.trophy-card :empty{display:none}
.trophy-room.is-flat{display:block;min-height:0;padding:1rem}
.medal-strip{list-style:none;display:grid;grid-template-columns:repeat(auto-fill,minmax(170px,1fr));gap:1rem;width:100%;margin:0;padding:0}
.medal-strip img{width:100%;aspect-ratio:1;object-fit:cover;border:1px solid var(--gold-dim)}
.medal-strip figcaption{font-size:.72rem;letter-spacing:.12em;text-transform:uppercase;color:var(--mute);margin-top:.4rem}
body[data-off~="trophy3d"] .trophy-room canvas{display:none}

/* Reduced motion: everything above that moves is switched off even if the visitor forced full effects on a previous visit */
@media (prefers-reduced-motion:reduce){
  .kinetic .kc{opacity:1!important;transform:none!important;animation:none!important}
  .cp{transition:none}
  .minicart{transition:none}
}

/* Flat fallback (Lite / no WebGL): Antony cut-out centred above the medal list, with a CSS mirror under his feet. */
.flat-hero{display:flex;justify-content:center;margin:0 0 min(18svh,145px)}
.flat-hero img{display:block;width:auto;height:min(44svh,350px);max-width:70%;-webkit-box-reflect:below -2px linear-gradient(transparent 62%,rgba(255,255,255,.28));filter:drop-shadow(0 0 28px rgba(216,178,90,.18))}

/* Phone: the detail card sits UNDER the 3D canvas so it never covers Antony's feet. */
@media (max-width:640px){
  .trophy-room.is-3d{height:min(86svh,700px)}
  .trophy-room.is-3d canvas{bottom:auto;height:calc(100% - 8.5rem)}
  .trophy-room.is-3d .trophy-card{left:.6rem;right:.6rem;bottom:.6rem;max-width:none}
}
