/* =========================================================
   Juice (js/fx.js, js/sky.js, js/loader.js): bursts, banners and the little animations around the site.
   Everything here is decoration: with reduced motion it is switched off (end of the file).
   ========================================================= */

/* ---------- pixel bursts ---------- */
.fx-px { position: fixed; z-index: 400; pointer-events: none; box-shadow: 0 0 0 2px #000; }

/* ---------- the sky behind the map (js/sky.js) ---------- */
#sky { position: absolute; inset: 0; width: 100%; height: 100%; display: block; pointer-events: none; }
.wheel-open #sky { filter: brightness(.62); }
:root[data-skin="pixel"] { --sky-star: #f5f2ea; }
:root[data-skin="pixel"][data-theme="light"] { --sky-star: rgba(20, 20, 22, .55); }

/* ---------- "NEW AREA": a zone's name crosses the screen the first time you find it ---------- */
.zone-banner {
  position: fixed; left: 0; right: 0; top: 30%; z-index: 26; pointer-events: none;
  display: grid; justify-items: center; gap: 12px; padding: 30px 16px 34px; text-align: center;
  background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--bg) 95%, transparent) 14%, color-mix(in srgb, var(--bg) 95%, transparent) 86%, transparent);
  animation: zb-band 2.5s cubic-bezier(.2, .8, .2, 1) both;
}
.zone-banner::before, .zone-banner::after { /* pixel lines above and below, in the zone's colour */
  content: ""; position: absolute; left: 14%; right: 14%; height: 4px;
  background: repeating-linear-gradient(90deg, var(--zc, var(--yl)) 0 8px, transparent 8px 13px);
  animation: zb-line 2.5s linear both;
}
.zone-banner::before { top: 0; } .zone-banner::after { bottom: 0; animation-direction: reverse; }
.zone-banner small { font: 400 13px/1 var(--f-mono); letter-spacing: .26em; text-transform: uppercase; color: var(--zc, var(--yl)); animation: zb-in .45s .12s steps(5) both; }
.zone-banner strong {
  font: 400 clamp(54px, 8.4vw, 120px)/.85 var(--f-pixel); letter-spacing: .02em; color: var(--ink);
  text-shadow: 0 6px 0 var(--zc, var(--yl)), 0 10px 30px rgba(0, 0, 0, .45);
  animation: zb-title 2.5s cubic-bezier(.2, .8, .2, 1) both;
}
.zone-banner span { font: 700 16px/1.3 var(--f-body); color: var(--ink-dim); animation: zb-in .45s .3s both; }
.zone-banner.gold { --zc: var(--yl); }
.zone-banner.gold strong { color: var(--yl); text-shadow: 0 6px 0 var(--pk), 0 10px 30px rgba(0, 0, 0, .45); }
@keyframes zb-band { 0% { opacity: 0; clip-path: inset(50% 0 50% 0); } 12% { opacity: 1; clip-path: inset(0); } 86% { opacity: 1; clip-path: inset(0); } 100% { opacity: 0; clip-path: inset(50% 0 50% 0); } }
@keyframes zb-line { from { background-position: 0 0; } to { background-position: 130px 0; } }
@keyframes zb-title { 0% { opacity: 0; transform: scale(1.7); filter: blur(8px); } 14% { opacity: 1; transform: scale(.95); filter: none; } 22% { transform: none; } 100% { transform: scale(1.04); } }
@keyframes zb-in { from { opacity: 0; transform: translateY(10px); } }

/* ---------- a zone wakes up: its cards hop one after the other ---------- */
.card.wake { animation: card-wake .75s calc(var(--w, 0) * 90ms + 140ms) cubic-bezier(.3, 1.6, .5, 1) both; }
@keyframes card-wake { 0%, 100% { translate: 0 0; } 35% { translate: 0 -26px; } 65% { translate: 0 4px; } }

/* ---------- signposts sway gently on their trails ---------- */
#routes .sign-in { animation: sign-bob 3.6s ease-in-out infinite; animation-delay: var(--d, 0s); }
@keyframes sign-bob { 50% { translate: 0 -6px; } }

/* ---------- "Chri.": every few seconds the letters do a little wave ---------- */
.big-name .jl { animation: jl-wave 6.5s ease-in-out infinite; }
.big-name .jl:nth-child(2) { animation-delay: .09s; } .big-name .jl:nth-child(3) { animation-delay: .18s; }
.big-name .jl:nth-child(4) { animation-delay: .27s; } .big-name .jl:nth-child(5) { animation-delay: .36s; }
@keyframes jl-wave { 0%, 80%, 100% { translate: 0 0; } 84% { translate: 0 -18px; } 88% { translate: 0 2px; } 91% { translate: 0 0; } }

/* ---------- the base's text types itself like a game's dialogue box (typeIn in js/fx.js) ---------- */
.tw { opacity: 0; } .tw.on { opacity: 1; }
.tw-next { display: inline-block; margin-left: 6px; font-style: normal; font-size: .7em; color: var(--yl); animation: tw-next .9s steps(1) infinite; }
@keyframes tw-next { 50% { opacity: 0; transform: translateY(2px); } }

/* ---------- "Chri." says hi: a pixel speech bubble (hubGreeting in js/fx.js) ---------- */
.hub-hi {
  position: absolute; z-index: 2; padding: 10px 14px 9px; white-space: nowrap; cursor: var(--cur-pointer);
  background: var(--yl); color: #141416; font: 400 24px/1 var(--f-pixel); letter-spacing: .03em;
  box-shadow: 0 0 0 3px #000, 0 6px 0 3px #000; border-radius: 4px;
  transform-origin: 0 100%; animation: hi-pop .5s cubic-bezier(.3, 1.7, .5, 1) both, hi-float 2.2s .5s ease-in-out infinite;
}
.hub-hi::after { /* the tail, pointing at the name */
  content: ""; position: absolute; left: -14px; bottom: 10px; width: 14px; height: 14px; background: var(--yl);
  box-shadow: -3px 3px 0 #000; clip-path: polygon(100% 0, 100% 100%, 0 100%);
}
.hub-hi.bye { animation: hi-bye .35s ease-in both; }
@keyframes hi-pop { from { transform: scale(0) rotate(-12deg); opacity: 0; } }
@keyframes hi-float { 50% { translate: 0 -5px; } }
@keyframes hi-bye { to { transform: scale(.4) rotate(8deg); opacity: 0; } }

/* ---------- a light sweeps across a card's picture when you point at it ---------- */
.card .thumb::after {
  content: ""; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: linear-gradient(115deg, transparent 36%, rgba(255, 255, 255, .32) 46%, rgba(255, 255, 255, .08) 54%, transparent 64%);
  transform: translateX(-130%);
}
.card:hover .thumb::after, .card.reticle-hit .thumb::after { transform: translateX(130%); transition: transform .85s cubic-bezier(.2, .7, .2, 1); }

/* the featured project and the flagship catch the light by themselves now and then */
.card-featured .thumb::after, .card:has(.crown-badge) .thumb::after { animation: glint 7.5s 2.5s ease-in-out infinite; }
.card:has(.crown-badge) .thumb::after { animation-delay: 6.2s; }
@keyframes glint { 0% { transform: translateX(-130%); } 13%, 100% { transform: translateX(130%); } }
/* the same light on the Panoramica's pictures */
.ov-thumb::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(115deg, transparent 36%, rgba(255, 255, 255, .3) 46%, rgba(255, 255, 255, .08) 54%, transparent 64%);
  transform: translateX(-130%);
}
.ov-proj:hover .ov-thumb::after, .ov-proj:focus-visible .ov-thumb::after { transform: translateX(130%); transition: transform .85s cubic-bezier(.2, .7, .2, 1); }

/* ---------- pointing at a card: its tags hop one after the other ---------- */
.card:hover .chip { animation: chip-hop .45s cubic-bezier(.3, 1.6, .5, 1) both; }
.card:hover .chip:nth-child(2) { animation-delay: .05s; } .card:hover .chip:nth-child(3) { animation-delay: .1s; }
.card:hover .chip:nth-child(4) { animation-delay: .15s; } .card:hover .chip:nth-child(5) { animation-delay: .2s; }
@keyframes chip-hop { 40% { translate: 0 -5px; } }

/* ---------- 8-bit scrollbars (project panel, Panoramica, trophies, case study) ---------- */
[data-skin="pixel"] :is(#inspect, .overview, .ach-panel, .case)::-webkit-scrollbar { width: 12px; }
[data-skin="pixel"] :is(#inspect, .overview, .ach-panel, .case)::-webkit-scrollbar-track { background: transparent; }
[data-skin="pixel"] :is(#inspect, .overview, .ach-panel, .case)::-webkit-scrollbar-thumb { background: var(--yl); border: 3px solid var(--bg-2); }
[data-skin="pixel"] :is(#inspect, .overview, .ach-panel, .case)::-webkit-scrollbar-thumb:hover { background: var(--pk); }
@supports (-moz-appearance: none) { [data-skin="pixel"] :is(#inspect, .overview, .ach-panel, .case) { scrollbar-color: var(--yl) transparent; } }

/* ---------- the name in the Panoramica drops in letter by letter ---------- */
.ov-name .ov-word { display: inline-block; white-space: nowrap; }
.ov-name .ch { display: inline-block; }
.overview.entering .ov-name .ch { animation: ov-ch .7s calc(var(--c) * 38ms + 520ms) cubic-bezier(.3, 1.6, .5, 1) both; }
@keyframes ov-ch { from { opacity: 0; transform: translateY(-70px) rotate(-14deg); } }
.ov-name .ch:hover { animation: ov-hop .5s cubic-bezier(.3, 1.6, .5, 1); }
@keyframes ov-hop { 40% { transform: translateY(-14px) rotate(-6deg); color: var(--yl); } }

/* ---------- loading screen: the message types itself; then the screen breaks into pixel blocks ---------- */
.ld-msg::after { content: ""; display: inline-block; width: .62em; height: 1em; margin-left: 5px; vertical-align: -.15em; background: currentColor; animation: pixel-blink .8s steps(1) infinite; }
.ld-blocks { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
.loader.dissolve { clip-path: none; transition: none; background: transparent; }
.loader.dissolve .ld-grid { opacity: 0; transition: opacity .25s; }
.loader .ld-center { z-index: 1; }

/* ---------- HUD reactions: the trophy jumps for a new achievement, the minimap and the counter ping for a new zone ---------- */
.ach-btn.bump { animation: hud-bump .55s cubic-bezier(.3, 1.6, .5, 1); }
@keyframes hud-bump { 30% { transform: scale(1.28) rotate(-8deg); } 60% { transform: scale(.94) rotate(4deg); } }
.ach-btn.bump svg { animation: hud-spin .55s cubic-bezier(.3, 1.6, .5, 1); }
@keyframes hud-spin { 50% { transform: rotate(-24deg) scale(1.2); } }
#explored.bump, #zoneName.flip { display: inline-block; animation: hud-flip .45s steps(5, end); }
@keyframes hud-flip { from { transform: translateY(-60%); opacity: 0; } }
.mini-ring.ping::after {
  content: ""; position: absolute; inset: -6px; border-radius: 50%; pointer-events: none;
  border: 3px solid var(--yl); animation: mini-ping .8s ease-out 2;
}
@keyframes mini-ping { from { transform: scale(.92); opacity: 1; } to { transform: scale(1.22); opacity: 0; } }

/* ---------- the trophy list: its items cascade in when it opens (js/achievements.js) ---------- */
.ach-panel.intro .ap-item { animation: ap-in .42s calc(var(--n, 0) * 26ms + 80ms) cubic-bezier(.2, 1.3, .4, 1) both; }
@keyframes ap-in { from { opacity: 0; transform: translateX(-18px); } }

/* ---------- Panoramica: the project cards appear one after the other as they scroll into view (js/overview.js) ---------- */
.ov-projects.reveal li { opacity: 0; translate: 0 36px; transition: opacity .5s ease, translate .65s cubic-bezier(.2, 1.3, .4, 1); transition-delay: calc(var(--k, 0) * 70ms); }
.ov-projects.reveal li.in { opacity: 1; translate: 0 0; }

/* ---------- day / night: the new theme opens as a circle from the button (js/theme.js) ---------- */
::view-transition-old(root), ::view-transition-new(root) { animation: none; mix-blend-mode: normal; }

/* ---------- everything above is decoration ---------- */
@media (prefers-reduced-motion: reduce) {
  #routes .sign-in, .big-name .jl, .card.wake, .zone-banner, .ov-name .ch, .ach-btn.bump, #explored.bump, #zoneName.flip { animation: none !important; }
  .card .thumb::after, .ov-thumb::after, .mini-ring.ping::after { display: none; }
  .card:hover .chip, .ach-panel.intro .ap-item { animation: none; }
  .tw { opacity: 1; }
  .ov-projects.reveal li { opacity: 1; translate: none; transition: none; }
}
