/* ── hwic.dev motion + polish layer ─────────────────────────────────────────
   One vocabulary for every public page. Loaded LAST in <head> (after the page's own
   <style>) so a tie in specificity goes to this file. Rules:
     · every colour is a token (var(--aqua) …) or a color-mix() of one, with a plain
       fallback line first for Safari < 16.2 — never a hard-coded hex;
     · only transform / opacity animate; nothing here changes layout;
     · every entrance fails VISIBLE: no rule leaves opacity:0 as a resting state
       without a JS backstop, and reduced-motion switches all of it off.

   Vocabulary (documented for the admin/docs):
     .up .d1–.d6   page-load entrance, CSS only, 560 ms, staggered
     .rise         scroll reveal (motion.js adds .rise-wait / .rise-in)
     .lift         hover lift on cards + tiles (transform + shadow, no layout shift)
     .spot         pointer spotlight on cards (pointer:fine only, --mx/--my from JS)
     .magnet       magnetic pull on primary buttons (pointer:fine only, --mx-x/--mx-y)
     .ul           underline that grows in on hover (nav + footer links)
     .mo-leave     page-transition exit (html class, 180 ms crossfade)
     .mo-scrolled  sticky nav state once the page has scrolled
     .mo-live      the ambient canvas once it is painting (fades in)                */

:root{
  --ease:cubic-bezier(.2,.9,.25,1);
  --ease-out:cubic-bezier(.16,1,.3,1);
  --ease-spring:cubic-bezier(.34,1.45,.44,1);
  --dur-1:.16s;--dur-2:.32s;--dur-3:.56s;
  --sp-1:8px;--sp-2:16px;--sp-3:24px;--sp-4:32px;--sp-5:48px;--sp-6:64px;
  /* rgba() of the design layer's rgb triplets, never color-mix(..,transparent):
     Safari mixes that toward grey (non-premultiplied alpha) */
  --accent-tint:rgba(var(--aqua-rgb,63,208,224),.12);
  --accent-line:rgba(var(--aqua-rgb,63,208,224),.42);
  --accent-glow:rgba(var(--aqua-rgb,63,208,224),.28);
  --shadow-ink:rgba(var(--bg-rgb,7,8,10),.6);
}

/* ── typographic polish ───────────────────────────────────────────────── */
h1,h2,h3{text-wrap:balance}
p,li{text-wrap:pretty}
h1{letter-spacing:-.03em}
.eyebrow,.lb,.kicker,.sec h2{letter-spacing:.3em}
.tm,.ver,.chip b,.kv,.amt,.tile .n{font-variant-numeric:tabular-nums}
::selection{background:var(--accent-glow)}

/* touch: no 300 ms tap delay, no grey flash; the player owns its own gestures */
a,button,summary,.btn,.cta,.ghost,.copy,.chip,label{touch-action:manipulation;-webkit-tap-highlight-color:transparent}
.player,.player button,.player .seek,.player .vol input{touch-action:pan-y}
.player .seek,.player .vol input{touch-action:none}

/* keyboard focus is visible everywhere, mouse focus draws nothing */
a:focus-visible,button:focus-visible,summary:focus-visible,input:focus-visible,select:focus-visible,[tabindex]:focus-visible{
  outline:2px solid var(--aqua);outline-offset:3px;border-radius:8px}
:focus:not(:focus-visible){outline:none}

/* ── ambient canvas ───────────────────────────────────────────────────── */
/* the canvas is sized to the LARGE viewport: on iOS the toolbars collapse while you scroll,
   a 100%/100vh box would change height every few frames and the compositor's stretch of
   the tiny backing store shimmered */
#snow{position:fixed;top:0;left:0;width:100%;height:100vh;height:100lvh;z-index:0;opacity:0;transition:opacity 1.4s ease;
  pointer-events:none;transform:translateZ(0);-webkit-backface-visibility:hidden;backface-visibility:hidden;will-change:transform}
#snow.mo-live{opacity:1}
.vignette{transition:opacity .8s ease}

/* ── entrance: .up ─────────────────────────────────────────────────────
   Base state is VISIBLE. The keyframes carry the hidden start, so a browser that
   never runs the animation (no CSS animations, a paused tab, an automation viewer)
   simply shows the content. style.css / page styles used to set opacity:0 here;
   this file is loaded after them and wins.                                     */
.up{opacity:1;transform:none;animation:none}
@media (prefers-reduced-motion:no-preference){
  .up{animation:mo-up var(--dur-3) var(--ease-out) both}
  .up.d1{animation-delay:.04s}.up.d2{animation-delay:.10s}.up.d3{animation-delay:.16s}
  .up.d4{animation-delay:.22s}.up.d5{animation-delay:.28s}.up.d6{animation-delay:.34s}
  /* intro pages: motion.js holds html.mo-intro while the curtain is up; the entrance
     plays the moment it is removed. No JS = no class = the entrance simply runs. */
  html.mo-intro .up{animation:none;opacity:0}
}
@keyframes mo-up{from{opacity:0;transform:translate3d(0,14px,0)}to{opacity:1;transform:none}}

/* ── scroll reveal: .rise ──────────────────────────────────────────────
   motion.js adds .rise-wait only when IntersectionObserver exists and motion is
   allowed, and clears it on a 4 s backstop whatever happens.                    */
.rise{opacity:1;transform:none;transition:none}
.rise.rise-wait{opacity:0;transform:translate3d(0,18px,0)}
.rise.rise-in{opacity:1;transform:none;
  transition:opacity .7s var(--ease-out) var(--stagger,0s),transform .7s var(--ease-out) var(--stagger,0s)}
@media (prefers-reduced-motion:reduce){.rise.rise-wait{opacity:1;transform:none}}

/* ── hover lift: cards, tiles, link rows ──────────────────────────────── */
.lift{transition:transform var(--dur-2) var(--ease-out),border-color var(--dur-2) var(--ease),
  box-shadow var(--dur-2) var(--ease),background-color var(--dur-2) var(--ease)}
@media (hover:hover){
  .lift:hover{transform:translate3d(0,-3px,0);border-color:var(--accent-line);
    box-shadow:0 18px 40px -22px var(--shadow-ink),0 0 0 1px var(--accent-tint) inset}
}
.lift:active{transform:scale(.992);transition-duration:var(--dur-1)}
/* the pages' own hover rules moved the card; the lift owns that now */
.social:hover,.linkcard:hover,.world:hover,.tile:hover,.card:hover{transform:none}
@media (hover:hover){.social.lift:hover,.linkcard.lift:hover,.world.lift:hover,.tile.lift:hover,.card.lift:hover{transform:translate3d(0,-3px,0)}}
.world.closed.lift:hover{transform:none;box-shadow:none;border-color:var(--line)}
.social .arr,.linkcard .t .arr{transition:transform var(--dur-2) var(--ease-spring),color var(--dur-2)}
@media (hover:hover){.social:hover .arr,.linkcard:hover .t .arr{transform:translate3d(3px,0,0)}}

/* ── pointer spotlight on cards ───────────────────────────────────────────
   isolation:isolate gives the card its own stacking context, so a z-index:-1
   overlay sits above the card's background and below every child.            */
.spot{position:relative;isolation:isolate}
.spot::after{content:"";position:absolute;top:0;right:0;bottom:0;left:0;z-index:-1;border-radius:inherit;pointer-events:none;opacity:0;
  background:radial-gradient(260px circle at var(--mx,50%) var(--my,50%),var(--accent-tint),rgba(var(--aqua-rgb,63,208,224),0) 62%);
  transition:opacity .45s var(--ease)}
@media (hover:hover) and (pointer:fine){.spot:hover::after{opacity:1}}

/* ── magnetic buttons (pointer:fine only; JS sets the vars) ───────────── */
.magnet{transform:translate3d(var(--mx-x,0px),var(--mx-y,0px),0) scale(var(--press,1));
  transition:transform .28s var(--ease-spring),background-color var(--dur-2) var(--ease),
    box-shadow var(--dur-2) var(--ease),border-color var(--dur-2) var(--ease)}
.magnet:active,.btn.magnet:active{--press:.96;transform:translate3d(var(--mx-x,0px),var(--mx-y,0px),0) scale(.96)}
.magnet.mo-pulling{transition-duration:.12s}
@media (hover:hover){
  .btn.primary.magnet:hover,.cta.magnet:hover,.go.magnet:hover{box-shadow:0 10px 26px -12px var(--accent-glow),0 0 0 4px var(--accent-tint)}
}
/* these used to nudge on hover; the pull replaces that */
.go:hover,.cta:hover{transform:translate3d(var(--mx-x,0px),var(--mx-y,0px),0) scale(var(--press,1))}
.btn{transition:transform var(--dur-2) var(--ease-spring),border-color var(--dur-2) var(--ease),background-color var(--dur-2) var(--ease),color var(--dur-2) var(--ease),box-shadow var(--dur-2) var(--ease)}
.btn:not(.primary):hover{border-color:var(--accent-line)}
.btn:active{transform:scale(.97)}

/* ── growing underline ────────────────────────────────────────────────── */
/* buttons, cards, chips and social tiles inside a section are not text links: no underline
   for them on any preset (the "hypixel stats" button used to grow one on hover) */
.ul,.cmsnav a,.navin .cmsnav a,.nav .subtle,.top .subtle,.back,.q p a,.sec a:not(.btn):not(.go):not(.social):not(.linkcard):not(.chip):not(.cta),.steps a,ol.steps .d a{
  text-decoration:none!important;background-image:linear-gradient(currentColor,currentColor);
  background-repeat:no-repeat;background-position:0 100%;background-size:0 1px;
  transition:background-size .32s var(--ease-out),color var(--dur-2) var(--ease)}
@media (hover:hover){
  .ul:hover,.cmsnav a:hover,.navin .cmsnav a:hover,.nav .subtle:hover,.top .subtle:hover,.back:hover,
  .q p a:hover,.sec a:not(.btn):not(.go):not(.social):not(.linkcard):not(.chip):not(.cta):hover,.steps a:hover,ol.steps .d a:hover{background-size:100% 1px}
}
ol.steps .d a{border-bottom:none}
/* footer links stay visibly links at rest; only the colour eases */
.foot a,footer a{transition:color var(--dur-2) var(--ease),text-decoration-color var(--dur-2) var(--ease)}

/* ── sticky glass nav (scrolling pages) ───────────────────────────────────
   The bar spans the full width so the glass covers the whole strip; .navin keeps
   the contents on the same column as the page (override --shell-w per page). */
.page>.nav,.nav.mo-glass,.top.mo-glass{position:-webkit-sticky;position:sticky;top:0;z-index:20;
  max-width:none;width:100%;margin:0;padding:0;display:block;
  background:linear-gradient(180deg,rgba(var(--bg-rgb,7,8,10),1) 0%,rgba(var(--bg-rgb,7,8,10),1) 40%,rgba(var(--bg-rgb,7,8,10),0) 100%);
  transition:box-shadow .3s var(--ease)}
@supports ((-webkit-backdrop-filter:blur(1px)) or (backdrop-filter:blur(1px))){
  .page>.nav,.nav.mo-glass,.top.mo-glass{
    /* the fade ends in the SAME colour at alpha 0, not `transparent` (= black at 0):
       an engine that interpolates un-premultiplied paints a grey band on light presets */
    background:linear-gradient(180deg,rgba(var(--bg-rgb,7,8,10),.86),rgba(var(--bg-rgb,7,8,10),.5) 70%,rgba(var(--bg-rgb,7,8,10),0));
    -webkit-backdrop-filter:blur(8px) saturate(1.3);backdrop-filter:blur(8px) saturate(1.3)}
}

.navin{max-width:var(--shell-w,880px);margin:0 auto;width:100%;display:flex;align-items:center;justify-content:space-between;gap:14px;
  padding:max(18px,env(safe-area-inset-top)) var(--shell-pad,clamp(18px,5vw,44px)) 16px}
.navin .cmsnav{margin-left:auto}
.mo-scrolled .page>.nav,.mo-scrolled .nav.mo-glass,.mo-scrolled .top.mo-glass{box-shadow:0 1px 0 var(--line)}
.brand{transition:opacity var(--dur-2)}
.brand .mark{transition:transform .5s var(--ease-spring)}
@media (hover:hover){.brand:hover .mark{transform:rotate(-8deg) scale(1.06)}}

/* ── page transition ──────────────────────────────────────────────────── */
.page,main#site,.stage{transition:opacity .18s ease}
html.mo-leave .page,html.mo-leave main#site,html.mo-leave .stage{opacity:0!important}
html.mo-leave .player,html.mo-leave .plrestore{opacity:0;transition:opacity .18s ease}

/* ── intro curtain (index, movie) ─────────────────────────────────────── */
#intro{transition:opacity .55s ease .15s}
/* the wordmark flies to the nav brand (FLIP, set by motion.js); the glitch slices stop */
#intro .glitch{will-change:transform;transition:transform .72s var(--ease-out),opacity .72s ease}
/* the enter animation's forwards-fill was what held the wordmark at opacity 1 — keep it there */
#intro.mo-fly .glitch,#intro.out .glitch{animation:none!important;opacity:1}
#intro.mo-fly .glitch::before,#intro.mo-fly .glitch::after,#intro.mo-fly .scan,
#intro.out .glitch::before,#intro.out .glitch::after,#intro.out .scan{display:none}
#intro.mo-fly{background:transparent;transition:background .45s ease}
@media (prefers-reduced-motion:no-preference){
  .glitch{animation-name:mo-blur-in,jit,flick!important}
  @keyframes mo-blur-in{from{opacity:0;transform:translate3d(0,6px,0);filter:blur(6px)}to{opacity:1;transform:none;filter:blur(0)}}
}

/* ── details / FAQ ────────────────────────────────────────────────────── */
.q summary::before,.note summary::before{display:inline-block;transition:transform .35s var(--ease-spring),color var(--dur-2)}
.q[open] summary::before,.note[open] summary::before{content:"+";transform:rotate(45deg)}
@media (prefers-reduced-motion:no-preference){
  details[open]>*:not(summary){animation:mo-fade .38s var(--ease-out) both}
}
@keyframes mo-fade{from{opacity:0;transform:translate3d(0,-4px,0)}to{opacity:1;transform:none}}

/* ── inputs ───────────────────────────────────────────────────────────── */
.codebox{transition:border-color .3s var(--ease),box-shadow .3s var(--ease),transform .3s var(--ease)}
.codebox.focus{border-color:var(--accent-line);box-shadow:0 0 0 4px var(--accent-tint)}
.field{transition:border-color var(--dur-2) var(--ease),box-shadow var(--dur-2) var(--ease)}
.field:focus{border-color:var(--accent-line);box-shadow:0 0 0 4px var(--accent-tint)}
.copy{transition:color var(--dur-2),transform var(--dur-2) var(--ease-spring)}
.copy svg{transition:transform .35s var(--ease-spring)}
.copy.ok svg,.copy.mo-ok svg{transform:scale(1.18)}
@media (hover:hover){.copy:hover{transform:scale(1.08)}}
/* the loading price breathes rather than sitting dead */
@media (prefers-reduced-motion:no-preference){.amt.load{animation:mo-pulse 1.4s ease-in-out infinite}}
@keyframes mo-pulse{0%,100%{opacity:.45}50%{opacity:.9}}
/* the live-count chip ticks in */
.chip.live b{display:inline-block}
@media (prefers-reduced-motion:no-preference){.chip.live.mo-tick b{animation:mo-tick .5s var(--ease-spring)}}
@keyframes mo-tick{from{transform:translate3d(0,6px,0);opacity:0}}

/* ── toast ────────────────────────────────────────────────────────────── */
.toast{transition:opacity .3s var(--ease),transform .4s var(--ease-spring)}

/* ── music player micro-interactions ──────────────────────────────────── */
.player{transition:opacity .5s var(--ease-out),transform .55s var(--ease-spring),border-color .3s var(--ease),box-shadow .4s var(--ease)}
.player .pp{transition:transform .28s var(--ease-spring),background-color .2s var(--ease),box-shadow .3s var(--ease)}
@media (hover:hover){.player .pp:hover{transform:scale(1.06);box-shadow:0 0 0 5px var(--accent-tint)}}
.player .pp:active{transform:scale(.94)}
/* the breathing halo is a ring that scales and fades (transform + opacity only) - an
   animated box-shadow inside the frosted pill re-rasterised the whole pill and flickered */
.player.playing .pp::after{content:"";position:absolute;top:-2px;right:-2px;bottom:-2px;left:-2px;border-radius:50%;
  border:2px solid var(--accent-glow);opacity:0;pointer-events:none}
@media (prefers-reduced-motion:no-preference){
  .player.playing .pp::after{animation:mo-breathe 2.6s ease-in-out infinite}
}
@keyframes mo-breathe{0%,100%{transform:scale(1);opacity:.55}50%{transform:scale(1.42);opacity:0}}
.player .seek .knob{transition:opacity .2s,transform .25s var(--ease-spring);transform:scale(.6)}
.player:hover .seek .knob,.player.seeking .seek .knob{transform:scale(1)}
.player .nx,.player .pv,.player .mute{transition:color var(--dur-2),background-color var(--dur-2),transform .25s var(--ease-spring)}
.player .nx:active,.player .pv:active,.player .mute:active{transform:scale(.88)}
.plrestore{transition:opacity .3s var(--ease),transform .45s var(--ease-spring),border-color .2s}

/* ── skin stage: a slow float on the 3-D viewer ───────────────────────── */
@media (prefers-reduced-motion:no-preference) and (hover:hover){
  .skinstage canvas{animation:mo-float 6s ease-in-out infinite}
}
@keyframes mo-float{0%,100%{transform:translate3d(0,0,0)}50%{transform:translate3d(0,-5px,0)}}

/* ── steps: the number badge lights as the row reveals ────────────────── */
.steps li,ol.steps li{transition:color var(--dur-2)}

/* ── reduced motion: everything off, everything visible ───────────────── */
@media (prefers-reduced-motion:reduce){
  #snow{display:none!important}
  .up,.rise,.glitch,.glitch::before,.glitch::after{animation:none!important;opacity:1!important;transform:none!important}
  .lift,.magnet,.spot::after,.copy,.copy svg,.brand .mark,.player,.player .pp,.toast,.codebox,.q summary::before,.note summary::before{transition:none!important;animation:none!important}
  .lift:hover,.magnet,.go:hover,.cta:hover{transform:none!important}
  #intro,#intro .glitch{transition:opacity .2s ease!important}
  #intro.mo-fly .glitch{transform:none!important;opacity:0}
  html.mo-leave .page,html.mo-leave main#site,html.mo-leave .stage{opacity:1!important}
  html.mo-intro .up{opacity:1}
}

/* ── content edited in /admin stays inside the phone layout whatever is typed ── */
[data-edit]{overflow-wrap:anywhere;min-width:0}
[data-edit] img,[data-edit] video,[data-edit] iframe{max-width:100%;height:auto}
[data-edit] table{display:block;max-width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}
[data-edit] pre{max-width:100%;overflow-x:auto}

/* ── no rubber-band at the top on desktop: the overscroll bounce dragged the sticky
   bar down and let it spring back whenever a trackpad flick reached the top. Touch
   devices keep their native bounce. ── */
@media (hover:hover) and (pointer:fine){html{overscroll-behavior-y:none}}
