/* ============================================================
   BUMMER CULTURE
   brand palette: black, blue, green, red, orange, off-white.
   each nav item + its popup gets one color (see "nav colors").
   ============================================================ */

:root{
  /* brand palette */
  --black:#0c0200;
  --blue:#4062a8;
  --green:#118c69;
  --red:#e9374c;
  --orange:#ff7d11;
  --white:#fff9e9;          /* off-white */

  /* theme tokens (dark is the default; light overrides these below) */
  --bg:var(--black);                 /* page + window surfaces */
  --ink:var(--white);                /* text + borders */
  --dim:rgba(255,249,233,.58);
  --line:rgba(255,249,233,.22);
  --wash:var(--black);               /* color of the wash over the background gif */
  --backdrop:rgba(12,2,0,.6);        /* dims the page behind popups */
  --contact:var(--white); --contact-ink:var(--black);
  --overlay:.8;             /* black wash over the background gif. 0 = none, 1 = solid */
  --bg-scale:4.8;           /* background tile size: 1 = original pixels (currently 4.8x) */
  --sky-scale:1.6;          /* light mode sky tile size: 1 = original pixels */
  --sky-speed:75s;          /* light mode sky: seconds to scroll one tile width (bigger = slower) */
  --marquee-size:50vh;      /* giant scrolling "bummer culture" banner: letter size */
  --marquee-opacity:.1;     /* banner: how visible (1 = solid off-white) */
  --marquee-opacity-light:.8;    /* banner in light mode, where it's off-white on the pale sky */
  --marquee-speed:30s;      /* banner: seconds to scroll one "bummer culture" (bigger = slower) */
  --static:.25;             /* TV static over the whole site: 0 = off, 1 = full strength */
  --ease:cubic-bezier(.22,1,.36,1);
  --font:"Xanh Mono", "Courier New", ui-monospace, monospace;   /* body text */
  --display:"Bang", "Xanh Mono", cursive;                       /* nav wheel + popup titles */
}

/* ---- light mode (toggle top right, remembered per visitor) ---- */
html[data-theme="light"]{
  --bg:var(--white);
  --ink:var(--black);
  --dim:rgba(12,2,0,.62);
  --line:rgba(12,2,0,.22);
  --wash:var(--white);
  --overlay:.35;                     /* 35% off-white over the sky gif (lighter than dark mode so the sky reads) */
  --backdrop:rgba(255,249,233,.6);
  --contact:var(--black); --contact-ink:var(--white);
}

/* Xanh Mono (Google Fonts, OFL), self-hosted so there's no third-party request */
@font-face{font-family:"Xanh Mono"; font-style:normal; font-weight:400; font-display:swap;
  src:url("../fonts/XanhMono-Regular.woff2") format("woff2")}
@font-face{font-family:"Xanh Mono"; font-style:italic; font-weight:400; font-display:swap;
  src:url("../fonts/XanhMono-Italic.woff2") format("woff2")}
/* Bang! by Taylor Penton (commercial license, see assets/fonts/README.md). Don't share the files. */
@font-face{font-family:"Bang"; font-style:normal; font-weight:400; font-display:swap;
  src:url("../fonts/TAYBang.woff2") format("woff2"), url("../fonts/TAYBang.woff") format("woff")}

*{box-sizing:border-box}
html,body{height:100%}
body{
  margin:0; color:var(--ink);
  background:var(--bg) url("../bg/night.gif") repeat;   /* default background tile */
  background-size:845px 864px;   /* the tile at 4.8x (site.js keeps this in step with --bg-scale) */
  -webkit-text-size-adjust:100%; -webkit-tap-highlight-color:transparent;
  image-rendering:pixelated;
  font-family:var(--font); font-weight:400; font-size:18px; line-height:1.5;
  -webkit-font-smoothing:antialiased;
  overflow:hidden;
}
img{image-rendering:auto}
/* only the pixel-art backgrounds (body tile, .sky, .static) stay pixelated. Everything else is reset,
   because Firefox applies an inherited "pixelated" to moving text too: the scrolling banner turned
   into big blocky pixels there. */
body > *{image-rendering:auto}
a{color:inherit}
button{font:inherit; color:inherit}
@media (pointer:fine){ body, a, button{cursor:none} }

/* light mode background: the pixel sky (day.gif, 428x241) scrolls left, the same way as the
   banner. It's its own layer sliding sideways (smoother than animating the page background),
   sitting under the wash. Dark mode doesn't use it and the stars don't scroll. */
.sky{
  --sky-w:calc(428px * var(--sky-scale)); --sky-h:calc(241px * var(--sky-scale));
  display:none; position:fixed; top:0; left:0; z-index:-1; pointer-events:none;
  width:calc(100% + var(--sky-w)); height:100%;
  background:url("../bg/day.gif") repeat 0 0 / var(--sky-w) var(--sky-h);
  image-rendering:pixelated; will-change:transform;
  animation:sky-drift var(--sky-speed) linear infinite;
}
@keyframes sky-drift{from{transform:translateX(0)} to{transform:translateX(calc(-1 * var(--sky-w)))}}
html[data-theme="light"] .sky{display:block}
html[data-theme="light"] body{background-image:none}

/* wash over the tiled gif so copy separates from the stars */
body::before{
  content:""; position:fixed; inset:0; z-index:0; pointer-events:none;
  background:var(--wash); opacity:var(--overlay);
  transition:background-color .4s linear, opacity .4s linear;
}

.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}

/* ---- stage ---------------------------------------------- */
.stage{position:fixed; inset:0; z-index:1; display:grid; place-items:center}
.dial{--dial:min(84vmin,860px); width:var(--dial); aspect-ratio:1/1; position:relative}
.dial svg{position:absolute; inset:0; width:100%; height:100%; overflow:visible}

/* ---- nav colors ----------------------------------------- */
.c-live, .c-tunes, .c-substack, .c-about{--c-outline:var(--white)}   /* speech bubble outline */
.c-live   {--c:var(--green);  --c-ink:var(--white)}
.c-tunes  {--c:var(--red);    --c-ink:var(--white)}
.c-contact{--c:var(--contact); --c-ink:var(--contact-ink); --c-outline:var(--contact-ink)}   /* off-white bubble gets a black outline */
.c-substack{--c:var(--orange); --c-ink:var(--black)}
.c-about  {--c:var(--blue);   --c-ink:var(--white)}

/* ---- nav wheel (Bang) -----------------------------------
   Built from plain HTML letters (site.js splits each word and sets
   each letter's angle), so it spins as one rigid wheel around its
   true center. */
.navring{position:absolute; inset:0; z-index:2; pointer-events:none;
  --nav-size:calc(var(--dial) * .103);      /* nav font size */
  --nav-r:calc(var(--dial) * .345);         /* radius letters sit on */
}
.navring .spin{position:absolute; inset:0}
.nav-item{
  position:absolute; inset:0; display:block; text-decoration:none; color:var(--c);
  font-family:var(--display); font-style:normal; font-size:var(--nav-size); line-height:1.2;
  transition:transform .45s cubic-bezier(.34,1.56,.64,1);   /* springy hover scale */
}
/* invisible hit area behind each word (sized + placed by site.js), so the
   pointer doesn't slip off between or around the letters */
.nav-item .hit{
  position:absolute; left:50%; top:50%; display:block;
  width:var(--hit-w,0); height:var(--hit-h,0);
  margin:calc(var(--hit-h,0) / -2) 0 0 calc(var(--hit-w,0) / -2);
  transform:rotate(var(--hit-a,0deg)) translateY(calc(var(--hit-r,0px) * -1));
  pointer-events:auto; background:transparent;
}
.nav-item b{
  position:absolute; left:50%; top:50%; font-weight:400;
  width:1ch; height:1.2em; margin:-1.2em 0 0 -.5ch; text-align:center;
  transform-origin:50% 100%;
  transform:rotate(var(--a)) translateY(calc(var(--nav-r) * -1));
  pointer-events:auto;
}
@media (hover:hover){ .nav-item:hover{transform:scale(1.18)} }   /* hover only on mouse, so it doesn't stick after a tap */
.nav-item:focus-visible{transform:scale(1.18)}
.nav-item:focus{outline:none}
.nav-item:focus-visible b{text-decoration:underline; text-underline-offset:.18em}

/* hover: magnetic. letters pull into a tight field toward the middle of the word
   (glowing in the item's color), then push back out, on a loop. letters keep
   their color (no color change). the <i> inside each letter
   moves, so the letter's hit area on the wheel stays put. --mid (the word's middle
   letter) is set by site.js. distances are in em so they scale with the nav size. */
.nav-item b i{display:inline-block; font-style:inherit}
@media (hover:hover){
  .nav-item:hover b i{animation:fx-magnetic 2.7s ease-in-out infinite; animation-delay:calc(var(--i) * .07s)}
}
.nav-item:focus-visible b i{animation:fx-magnetic 2.7s ease-in-out infinite; animation-delay:calc(var(--i) * .07s)}
@keyframes fx-magnetic{
  0%,100%{transform:translateX(0) scale(1); text-shadow:0 0 0 transparent}
  42%    {transform:translateX(calc((var(--mid) - var(--i)) * .067em)) scale(1.12);
          text-shadow:0 0 .53em color-mix(in srgb, var(--c) 70%, transparent)}
  62%    {transform:translateX(calc((var(--i) - var(--mid)) * .1em)) scale(.96)}
}

/* ---- giant scrolling banner (Bang!, lowercase) -----------
   Sits behind the wheel, above the background wash. The track holds two
   identical halves and slides left by exactly one half, so it loops with no seam. */
.marquee{
  position:fixed; left:0; right:0; top:50%; z-index:0;
  transform:translateY(calc(-50% + .09em));   /* nudges the lowercase ink to true vertical center (measured) */
  overflow:hidden; overflow-x:clip; overflow-y:visible;   /* clip sideways only, so tall swashes aren't cut off */
  pointer-events:none; user-select:none;
  font-family:var(--display); font-style:normal; font-size:var(--marquee-size); line-height:1.5;
  color:var(--ink); opacity:var(--marquee-opacity); white-space:nowrap;
  transition:opacity .25s ease-out;          /* going dark: fade down quickly as the page darkens */
}
/* going light: rise in step with the page lightening (the wash takes .4s), so there's no flash of
   bright white on a still-dark page */
html[data-theme="light"] .marquee{color:var(--white); opacity:var(--marquee-opacity-light); transition:opacity .45s ease-in}
/* each "bummer culture" slides left by its own width, then loops. They're moved one by one instead
   of as one long strip because Firefox redraws a very wide moving strip at low resolution (it
   went pixelated after the first loop). Three copies so even ultra-wide screens stay covered. */
.marquee-track{display:flex; width:max-content}
.marquee-track span{flex:none; display:block; animation:marquee var(--marquee-speed) linear infinite}
@keyframes marquee{from{transform:translateX(0)} to{transform:translateX(-100%)}}

/* ---- head ----------------------------------------------- */
.head{
  position:absolute; left:50%; top:50%; width:20%;   /* the suit cat is tall: 20% wide = ~55% of the dial tall */
  transform:translate(-50%,-50%);
  z-index:3;
  --mouth-x:38%; --mouth-y:16.5%;   /* where the speech bubble's tail points (just under the cigarette) */
}
.head-lean{width:100%}
.head-bob{position:relative; animation:bob 3.6s ease-in-out infinite}
.head img{
  display:block; width:100%; height:auto; user-select:none; -webkit-user-drag:none;
  /* the suit cat art has its own off-white sticker edge, so just a hard drop shadow here.
     (for art without one, add drop-shadow(±3px 0 0 var(--white)) etc. to fake a die-cut edge) */
  filter:drop-shadow(6px 7px 0 rgba(0,0,0,.55));
}
html[data-theme="light"] .head img{filter:none}   /* no drop shadow in light mode */
.head .cry{position:absolute; inset:0; opacity:0}
.head.crying .cry{opacity:1}
.head.crying .still{opacity:0}
@keyframes bob{0%,100%{transform:translateY(0) rotate(0deg)} 50%{transform:translateY(-2.2%) rotate(.6deg)}}

/* ---- speech bubble (nav hover) --------------------------
   Lives inside the character, so it moves with him. The tail's tip sits at
   --mouth-x / --mouth-y (set on .head) and drops into the top middle of the colored
   block, which is centered in the circle and capped in width (about half the
   dial) so it never runs into the nav words. Color comes from the hovered item. */
.bubble{
  /* twice the character's width (half of it sticking out each side), so the whole bubble sits
     inside this box. Safari only draws the outline filter inside the box, so a bubble that hung
     outside it got cut in half there. Positions below are in % of this wider box. */
  position:absolute; left:-50%; right:-50%; top:var(--mouth-y); z-index:5; pointer-events:none;
  font-size:clamp(13px,1.45vw,17px); line-height:1.2;
  opacity:0; transform:scale(.85); transform-origin:calc(25% + var(--mouth-x) / 2) 0;
  transition:opacity .15s linear, transform .35s cubic-bezier(.34,1.56,.64,1);
}
.bubble.on{opacity:1; transform:none}
/* every bubble gets an outline that runs around the bubble and its tail as one shape
   (stacked drop-shadows trace the combined outline). off-white by default; black on
   the off-white Contact bubble. the cat's own bubble is off-black with off-white. */
.bubble{
  filter:drop-shadow(2px 0 0 var(--cap-outline,var(--white))) drop-shadow(-2px 0 0 var(--cap-outline,var(--white)))
         drop-shadow(0 2px 0 var(--cap-outline,var(--white))) drop-shadow(0 -2px 0 var(--cap-outline,var(--white)));
}
.bubble-text em{font-style:italic}
.bubble .tail{position:absolute; left:calc(25% + var(--mouth-x) / 2 - .4em); top:0; width:1.6em; height:1.7em; display:block; fill:var(--cap-bg,var(--green))}
.bubble-text{
  /* centered with auto margins, not a translate: Mac Safari sizes the outline filter from where the
     box sits before a transform, so a translateX(-50%) here got the left half of the bubble cut off */
  position:absolute; left:0; right:0; top:1.65em; margin:0 auto;
  width:max-content; max-width:97.5%;         /* 195% of the character's width = about 40% of the dial */
  text-align:center; white-space:normal;
  padding:.35em .7em; background:var(--cap-bg,var(--green)); color:var(--cap-ink,var(--white));
}

/* ---- chrome --------------------------------------------- */
.mark,.foot,.credit{position:fixed; z-index:40; margin:0; font-size:13px; letter-spacing:0;
  text-transform:lowercase; background:none; padding:.25em .55em}
.mark{top:18px; left:22px; font-weight:400; display:flex; align-items:flex-start; gap:.25em}
.mark-name{font-family:var(--display); font-size:30px; line-height:1; text-transform:lowercase}   /* Bang */
.mark .tm{opacity:.45; font-size:13px; line-height:1}
.foot{bottom:18px; left:22px; color:var(--dim)}
.credit{bottom:18px; right:22px; color:var(--orange)}
.theme-toggle{position:fixed; z-index:40; top:18px; right:22px; margin:0; border:0;
  font-size:13px; letter-spacing:0; text-transform:lowercase; line-height:1.5;
  background:none; color:var(--ink); padding:.25em .55em; text-decoration:underline;
  text-decoration-thickness:1px; text-underline-offset:3px}
.theme-toggle:focus-visible{outline:1px solid var(--ink); outline-offset:2px}
/* hover: "lights on" (dark mode) glows like a bulb warming up; "lights off" (light mode) casts a shadow */
.theme-toggle{transition:text-shadow .25s linear}
@media (hover:hover){
  .theme-toggle:hover{text-shadow:0 0 4px rgba(255,249,233,.95), 0 0 10px rgba(255,249,233,.7), 0 0 22px rgba(255,125,17,.55)}
  html[data-theme="light"] .theme-toggle:hover{text-shadow:1px 2px 0 rgba(12,2,0,.25), 2px 4px 6px rgba(12,2,0,.45)}
}
.credit a{color:inherit; text-decoration:underline; text-decoration-thickness:1px; text-underline-offset:3px}
.credit a:focus-visible{color:var(--ink)}

/* ---- modal / popup pages -------------------------------- */
.modal{
  position:fixed; inset:0; z-index:50; display:grid; place-items:center;
  padding:24px; background:var(--backdrop);
  opacity:0; visibility:hidden; transition:opacity .25s linear, visibility 0s .25s;
}
.modal.open{opacity:1; visibility:visible; transition:opacity .2s linear}
.panel{
  display:none; width:min(760px,100%); max-height:min(82vh,860px);
  flex-direction:column; background:var(--bg); border:2px solid var(--ink);
  box-shadow:10px 10px 0 var(--c);
  transform:translateY(18px) scale(.98); transition:transform .45s var(--ease);
}
.panel.on{display:flex}
.modal.open .panel.on{transform:none}
.panel-bar{
  display:flex; align-items:center; justify-content:space-between; gap:12px;
  background:var(--c); color:var(--c-ink); border-bottom:2px solid var(--ink); padding:6px 8px 6px 16px;
}
.panel-bar h2{margin:0; padding-top:20px; font-family:var(--display); font-size:34px; line-height:1.1; font-weight:400; font-style:normal; letter-spacing:0}
.panel-bar .url{font-size:13px; opacity:.7; margin-left:auto; white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
.x{
  width:34px; height:34px; flex:none; border:2px solid var(--ink); background:var(--bg); color:var(--ink);
  font-size:22px; line-height:1; display:grid; place-items:center; padding:0;
  transition:background .15s linear, color .15s linear;
}
.x:focus-visible{background:var(--ink); color:var(--bg); outline:none}
.panel-body{padding:26px 28px 30px; overflow:auto; overscroll-behavior:contain; min-height:0; -webkit-overflow-scrolling:touch}
.panel-body p{margin:0 0 1em}
.panel-body a{color:var(--ink); text-decoration-color:var(--c); text-underline-offset:3px}
.panel-body a:hover{color:var(--c)}
.lede{font-size:15px; color:var(--dim); margin-bottom:1.4em !important}
.placeholder-note{
  display:inline-block; font-size:12px; letter-spacing:0; text-transform:uppercase;
  border:1px dashed var(--dim); color:var(--dim); padding:.2em .6em; margin-bottom:1.2em;
}

/* ---- live ------------------------------------------------ */
.shows-empty{border:1px dashed var(--line); padding:34px 20px; text-align:center}
.shows-empty .big{font-size:clamp(26px,4vw,38px); font-style:italic; line-height:1.1; margin:0 0 .4em}
.shows-empty p{color:var(--dim)}
.bit-slot:empty{display:none}
.has-widget .shows-empty{display:none}

/* ---- tunes ----------------------------------------------- */
.tunes-grid{
  list-style:none; margin:0; padding:0;
  display:grid; grid-template-columns:repeat(auto-fill,minmax(150px,1fr)); gap:22px;
}
.tune{
  display:block; width:100%; padding:0; border:0; background:none; text-align:left;
}
.tune .cover{
  display:block; width:100%; height:auto; aspect-ratio:1/1; object-fit:cover; background:#1a0f0c;
  border:2px solid var(--ink); transition:transform .3s var(--ease), box-shadow .3s var(--ease);
}
.tune:focus-visible .cover{transform:translate(-4px,-4px) rotate(-1.2deg); box-shadow:6px 6px 0 var(--c)}
.tune:focus{outline:none}
.tune .t{display:block; margin-top:.55em; font-style:italic; font-size:17px; line-height:1.2}
.tune .y{display:block; font-size:13px; color:var(--dim)}
.tunes-status{color:var(--dim)}

/* streaming picker (a popup inside the popup) */
.picker{
  position:fixed; inset:0; z-index:60; display:grid; place-items:center; padding:24px;
  background:var(--backdrop); opacity:0; visibility:hidden;
  transition:opacity .2s linear, visibility 0s .2s;
}
.picker.open{opacity:1; visibility:visible; transition:opacity .15s linear}
.picker-card{
  width:min(340px,100%); background:var(--bg); border:2px solid var(--ink);
  box-shadow:8px 8px 0 var(--red); padding:18px;
  transform:scale(.94); transition:transform .35s var(--ease);
}
.picker.open .picker-card{transform:none}
.picker-head{display:flex; gap:14px; align-items:center; margin-bottom:14px}
.picker-head img{width:74px; height:74px; object-fit:cover; border:2px solid var(--ink); flex:none}
.picker-head .t{font-style:italic; font-size:20px; line-height:1.15}
.picker-head .k{font-size:12px; letter-spacing:0; text-transform:uppercase; color:var(--dim)}
.services{list-style:none; margin:0 0 12px; padding:0; display:grid; gap:8px}
.services a{
  display:flex; justify-content:space-between; align-items:center; gap:.6em;
  border:1.5px solid var(--ink); padding:.55em .8em; text-decoration:none; font-size:17px;
  transition:background .15s linear, color .15s linear;
}
.services a::after{content:"\2197"; opacity:.7}
.svc{display:flex; align-items:center; gap:.6em}
.svc svg{width:1.1em; height:1.1em; flex:none; fill:currentColor}
.services a:focus-visible{background:var(--red); color:var(--white); border-color:var(--red); outline:none}
.picker .nvm{width:100%; border:0; background:none; color:var(--dim); font-size:14px; padding:.4em; text-decoration:underline}
.picker .nvm:hover{color:var(--red)}

/* ---- about ----------------------------------------------- */
.about{display:grid; grid-template-columns:minmax(0,220px) 1fr; gap:26px; align-items:start}
.about img{width:100%; height:auto; aspect-ratio:4/5; object-fit:cover; border:2px solid var(--ink); box-shadow:6px 6px 0 var(--c)}
.about .bio p:first-child{font-size:22px; font-style:italic; line-height:1.3}

/* ---- toast ----------------------------------------------- */
.toast{
  position:fixed; left:50%; bottom:24px; transform:translate(-50%,14px);
  background:var(--ink); color:var(--bg); font-size:14px; padding:.55em 1em;
  opacity:0; z-index:70; transition:opacity .2s linear, transform .35s var(--ease); pointer-events:none;
  white-space:nowrap;
}
.toast.on{opacity:1; transform:translate(-50%,0)}

/* ---- preloader ------------------------------------------
   off-black screen, head icon, "bummer culture" in Bang that fills with red
   left to right as the site loads (--p goes 0 to 1, set by the script in
   index.html), then an iris opens from the center to reveal the site. Always off-black, both themes. */
/* the iris: a round hole in the black screen that grows from the center (--iris = its radius).
   It's a mask, so everything inside the preloader (the falling head, the word) is cut off by the
   hole too. --iris is registered so the browser can animate it; browsers that can't (older
   Safari/Firefox) get a plain fade instead (.no-iris, set by the script in index.html). */
@property --iris{syntax:'<length>'; inherits:false; initial-value:-2px}
.preloader{
  position:fixed; inset:0; z-index:95; background:#0c0200; overflow:hidden;
  --iris:-2px;
  -webkit-mask-image:radial-gradient(circle at 50% 50%, transparent var(--iris), #000 calc(var(--iris) + 1px));
          mask-image:radial-gradient(circle at 50% 50%, transparent var(--iris), #000 calc(var(--iris) + 1px));
  transition:--iris .9s cubic-bezier(.7,0,.3,1);
}
.preloader-inner{
  position:absolute; inset:0;
  display:flex; flex-direction:column; align-items:center; justify-content:center; gap:2vh;
}
/* exit: the hole grows past the corners of the screen (75vmax always clears them) */
.preloader.done{--iris:75vmax; pointer-events:none}
.preloader.no-iris{-webkit-mask-image:none; mask-image:none; transition:opacity .5s linear}
.preloader.no-iris.done{opacity:0}
.preloader-icon{display:block; position:relative; z-index:1; width:113px; height:136px}   /* 68% of native 166x200 */
/* the ™ after the word, in Xanh Mono. When the fill reaches the end of the word it gets knocked off:
   it hops up and to the right at 45deg, then arcs down off the bottom of the screen, spinning
   180deg along the way (0.8s). The script in index.html adds .dropping; the iris clips it if it's
   still on screen. The stops trace a real arc: steady sideways drift, gravity-style up then down.
   Hop height is the 25px at 25% (the peak); the 50px beside it keeps the 45deg launch.
   (The head above the word stays put the whole time.) */
.preloader-tm{position:absolute; left:100%; top:.12em; margin-left:.04em;
  font-family:var(--font); font-size:.3em; line-height:1; color:#fff9e9}
.preloader.dropping .preloader-tm{animation:knock-off .8s linear forwards}
@keyframes knock-off{
  0%    {transform:translate(0, 0) rotate(0deg)}
  6.25% {transform:translate(12.5px, -10.9px) rotate(11.25deg)}
  12.5% {transform:translate(25px, -18.75px) rotate(22.5deg)}
  18.75%{transform:translate(37.5px, -23.4px) rotate(33.75deg)}
  25%   {transform:translate(50px, -25px) rotate(45deg)}                                /* peak */
  43.75%{transform:translate(87.5px, calc(6.25vh - 23.4px)) rotate(78.75deg)}
  62.5% {transform:translate(125px, calc(25vh - 18.75px)) rotate(112.5deg)}
  81.25%{transform:translate(162.5px, calc(56.25vh - 10.9px)) rotate(146.25deg)}
  100%  {transform:translate(200px, 100vh) rotate(180deg)}
}

.preloader-word{
  position:relative; font-family:var(--display); font-size:clamp(44px,9vw,112px); line-height:1.3;
  color:#fff9e9; white-space:nowrap; opacity:0; transition:opacity .25s linear;
}
.preloader.font-ready .preloader-word{opacity:1}
.preloader-word .fill{
  position:absolute; inset:0; color:var(--red);
  /* the fill edge runs from the ink's left edge (-1%) to its right edge (101%) as --p goes 0 to 1,
     so the word is fully red exactly when --p hits 1 (that's when the cat drops) */
  clip-path:inset(-30% calc(101% - var(--p, 0) * 102%) -30% -30%);
}

/* ---- TV static over everything --------------------------
   A tiled noise texture on a slightly oversized layer that jumps to a new
   spot many times a second (technique from codepen.io/iceable/pen/yLBrZOd,
   tuned to be lighter on phones; our own noise tile).
   Sits above the page, below the cursor, and never catches clicks. */
.static{
  position:fixed; inset:-128px; z-index:85; pointer-events:none;   /* one noise tile bigger than the screen on every side */
  background:url("../img/static.png") repeat 0 0;
  opacity:var(--static); image-rendering:pixelated;
  animation:static-jump .6s steps(1) infinite; will-change:transform;
}
/* jump the noise to a new spot ~16 times a second. moves stay under one tile (128px)
   so the edges never show. */
@keyframes static-jump{
  0%  {transform:translate(0,0)}          10% {transform:translate(-37px,-91px)}
  20% {transform:translate(-101px,23px)}  30% {transform:translate(29px,-113px)}
  40% {transform:translate(-64px,77px)}   50% {transform:translate(-119px,-8px)}
  60% {transform:translate(83px,-52px)}   70% {transform:translate(-11px,101px)}
  80% {transform:translate(-88px,-67px)}  90% {transform:translate(56px,41px)}
  100%{transform:translate(0,0)}
}
html[data-theme="light"] .static{opacity:calc(var(--static) * .8)}

/* ---- cursor ---------------------------------------------- */
/* the mouse follows the pointer on mouse/trackpad devices. It's a still frame
   (bc-mouse-still.png) and only animates (bc-mouse.gif, 53x102, 3 frames) over things you can
   click (.big), where it also grows a little. Shown at half size so it's sharp on retina screens.
   The hotspot (where clicks land) is the nose tip: (14, 4) in the file, (7, 2) at this size; the
   margins line it up. A few faint copies trail behind it (.cur-ghost, made by site.js). */
.cur, .cur-ghost{
  position:fixed; left:0; top:0; pointer-events:none; opacity:0;
  width:26.5px; height:51px; margin:-2px 0 0 -7px;
  background:url("../img/bc-mouse-still.png") no-repeat 0 0 / 100% 100%;
}
.cur{z-index:100; transform-origin:7px 2px; transition:scale .2s var(--ease)}
.cur::after{content:""; position:absolute; inset:0; opacity:0;
  background:url("../img/bc-mouse.gif") no-repeat 0 0 / 100% 100%}
.cur.seen{opacity:1}
/* when it's not over anything clickable, the mouse breathes (slow swell, twice) then sniffs (a quick
   wiggle). Everything pivots on the nose, so the click point never moves. */
.cur:not(.big){animation:mouse-idle 4.4s infinite}
@keyframes mouse-idle{
  0%, 50%  {transform:none; animation-timing-function:ease-in-out}
  25%, 75% {transform:scale(1.03, 1.06); animation-timing-function:ease-in-out}
  88%      {transform:none; animation-timing-function:ease-out}
  90.5%    {transform:rotate(-4deg)}
  93%      {transform:rotate(3deg)}
  95.5%    {transform:rotate(-2deg)}
  98%, 100%{transform:none}
}
.cur.big{scale:1.15; background-image:none}
.cur.big::after{opacity:1}
.cur-ghost{z-index:99; opacity:var(--ghost, .1); visibility:hidden}
.cur-ghost.seen{visibility:visible}
@media (pointer:coarse){ .cur, .cur-ghost{display:none} }

/* hover effects only where there's a real mouse (on phones :hover sticks after a tap) */
@media (hover:hover){
  .tune:hover .cover{transform:translate(-4px,-4px) rotate(-1.2deg); box-shadow:6px 6px 0 var(--c)}
  .x:hover{background:var(--ink); color:var(--bg)}
  .services a:hover{background:var(--red); color:var(--white); border-color:var(--red)}
  .credit a:hover{color:var(--ink)}
}

/* ---- small screens --------------------------------------- */
@media (max-width:640px){
  body{font-size:17px}
  .dial{--dial:min(94vmin,860px)}
  .navring{--nav-size:calc(var(--dial) * .118); --nav-r:calc(var(--dial) * .335)}
  .bubble-text{max-width:92.5%}
  .mark{top:calc(14px + env(safe-area-inset-top)); left:14px; font-size:12px}
  .mark-name{font-size:30px}
  .theme-toggle{top:calc(14px + env(safe-area-inset-top)); right:14px; font-size:12px}

  .modal{padding:0; display:block}
  .panel{width:100%; max-height:none; height:100%; border:0; box-shadow:none}
  .panel-bar .url{display:none}
  .panel-body{padding:22px 18px 40px}
  .tunes-grid{grid-template-columns:repeat(2,minmax(0,1fr)); gap:16px}
  .about{grid-template-columns:1fr}
  .about img{max-width:260px}
  .panel-bar{padding-top:calc(6px + env(safe-area-inset-top))}
  .panel-body{padding-bottom:calc(40px + env(safe-area-inset-bottom))}
}
/* portrait phones: footer + credit stack at bottom center, under the wheel */
@media (max-width:640px) and (orientation:portrait){
  .foot,.credit{left:50%; right:auto; transform:translateX(-50%); font-size:11px; white-space:nowrap}
  .credit{bottom:calc(14px + env(safe-area-inset-bottom))}
  .foot{bottom:calc(44px + env(safe-area-inset-bottom))}
}
/* landscape phones: wheel fills the height, text stays in the corners */
@media (max-height:500px) and (orientation:landscape){
  .dial{--dial:min(90vmin,860px)}
  .mark,.foot,.credit{font-size:10px; letter-spacing:0}
  .mark{top:10px; left:12px}
  .mark-name{font-size:30px}
  .theme-toggle{top:10px; right:12px; font-size:10px}
  .foot{bottom:10px; left:12px}
  .credit{bottom:10px; right:12px}
  .modal{padding:0; display:block}
  .panel{width:100%; max-height:none; height:100%; border:0; box-shadow:none}
  .panel-body{padding:18px 20px 28px}
  .picker-card{max-height:92vh; overflow:auto}
}

@media (prefers-reduced-motion:reduce){
  .preloader{-webkit-mask-image:none; mask-image:none; transition:opacity .3s linear}
  .preloader.done{opacity:0}
  .head-bob{animation:none}
  .nav-item b i{animation:none !important}
  .marquee-track span{animation:none}
  .sky{animation:none}
  .cur{animation:none !important}
  .static{animation:none}
  .panel,.picker-card{transition:none}
}
