/* sky.css — the moving parts. there is no javascript on this page, so everything that moves
   is computed here, by the browser's style engine.

   the sky is 96 vertical strips. each strip knows its index (--i) and solves for its own
   aurora in calc(): where the two curtains hang, how bright its ray is, where the lake mirrors
   them. time is one registered custom property (--t), animated on the strips' parent and
   inherited. the pointer is tracked by a 16x16 grid of hover targets and :has().

   every --name used in arithmetic is registered, so the browser computes each one once per
   element instead of pasting expressions into each other. the number ones are generated.   */

/*gen:props*/
@property --t { syntax: "<number>"; inherits: true; initial-value: 0 }
@property --mx { syntax: "<number>"; inherits: true; initial-value: 8 }
@property --my { syntax: "<number>"; inherits: true; initial-value: 6 }
@property --mo { syntax: "<number>"; inherits: true; initial-value: 0 }
@property --rows { syntax: "<number>"; inherits: true; initial-value: 54 }
@property --hr { syntax: "<number>"; inherits: true; initial-value: 40 }
@property --bank { syntax: "<number>"; inherits: true; initial-value: 48 }
@property --tc { syntax: "<number>"; inherits: true; initial-value: 8 }
@property --tr { syntax: "<number>"; inherits: true; initial-value: 7 }
@property --pc { syntax: "<number>"; inherits: true; initial-value: 67 }
@property --gr { syntax: "<number>"; inherits: true; initial-value: 130 }
@property --p { syntax: "<number>"; inherits: true; initial-value: 0 }
@property --k { syntax: "<number>"; inherits: true; initial-value: 0 }
@property --lx { syntax: "<number>"; inherits: true; initial-value: -0.45 }
@property --ly { syntax: "<number>"; inherits: true; initial-value: 0.7 }
@property --lz { syntax: "<number>"; inherits: true; initial-value: 0.55 }
@property --ll { syntax: "<number>"; inherits: true; initial-value: 1 }
@property --camx { syntax: "<number>"; inherits: true; initial-value: 0 }
@property --camy { syntax: "<number>"; inherits: true; initial-value: 0 }
@property --cama { syntax: "<number>"; inherits: true; initial-value: 0 }
@property --u { syntax: "<number>"; inherits: false; initial-value: 0 }
@property --dx { syntax: "<number>"; inherits: false; initial-value: 0 }
@property --g { syntax: "<number>"; inherits: false; initial-value: 0 }
@property --r1 { syntax: "<number>"; inherits: false; initial-value: 0 }
@property --r2 { syntax: "<number>"; inherits: false; initial-value: 0 }
@property --th { syntax: "<number>"; inherits: false; initial-value: 0 }
@property --fx { syntax: "<number>"; inherits: false; initial-value: 0 }
@property --ft { syntax: "<number>"; inherits: false; initial-value: 0 }
@property --md { syntax: "<number>"; inherits: false; initial-value: 0 }
@property --mh { syntax: "<number>"; inherits: false; initial-value: 0 }
@property --x { syntax: "<number>"; inherits: false; initial-value: 0 }
@property --y { syntax: "<number>"; inherits: false; initial-value: 0 }
@property --nz { syntax: "<number>"; inherits: false; initial-value: 0 }
@property --sx { syntax: "<number>"; inherits: false; initial-value: 0 }
@property --sy { syntax: "<number>"; inherits: false; initial-value: 0 }
@property --len { syntax: "<number>"; inherits: false; initial-value: 0 }
@property --rx { syntax: "<number>"; inherits: false; initial-value: 0 }
@property --ry { syntax: "<number>"; inherits: false; initial-value: 0 }
@property --rz { syntax: "<number>"; inherits: false; initial-value: 0 }
@property --bq { syntax: "<number>"; inherits: false; initial-value: 0 }
@property --disc { syntax: "<number>"; inherits: false; initial-value: 0 }
@property --hit { syntax: "<number>"; inherits: false; initial-value: 0 }
@property --tn { syntax: "<number>"; inherits: false; initial-value: 0 }
@property --nx { syntax: "<number>"; inherits: false; initial-value: 0 }
@property --ny { syntax: "<number>"; inherits: false; initial-value: 0 }
@property --nz1 { syntax: "<number>"; inherits: false; initial-value: 0 }
@property --dif { syntax: "<number>"; inherits: false; initial-value: 0 }
@property --spec { syntax: "<number>"; inherits: false; initial-value: 0 }
@property --tfl { syntax: "<number>"; inherits: false; initial-value: 0 }
@property --fx1 { syntax: "<number>"; inherits: false; initial-value: 0 }
@property --fz1 { syntax: "<number>"; inherits: false; initial-value: 0 }
@property --chk { syntax: "<number>"; inherits: false; initial-value: 0 }
@property --bs { syntax: "<number>"; inherits: false; initial-value: 0 }
@property --ds { syntax: "<number>"; inherits: false; initial-value: 0 }
@property --shd { syntax: "<number>"; inherits: false; initial-value: 0 }
@property --fog { syntax: "<number>"; inherits: false; initial-value: 0 }
@property --fl { syntax: "<number>"; inherits: false; initial-value: 0 }
@property --sk { syntax: "<number>"; inherits: false; initial-value: 0 }
@property --ang { syntax: "<number>"; inherits: false; initial-value: 0 }
@property --cdx { syntax: "<number>"; inherits: false; initial-value: 0 }
@property --cdy { syntax: "<number>"; inherits: false; initial-value: 0 }
@property --twx { syntax: "<number>"; inherits: false; initial-value: 0 }
@property --twy { syntax: "<number>"; inherits: false; initial-value: 0 }
@property --wall { syntax: "<number>"; inherits: false; initial-value: 0 }
@property --tb1 { syntax: "<number>"; inherits: false; initial-value: 0 }
@property --tb2 { syntax: "<number>"; inherits: false; initial-value: 0 }
@property --tb3 { syntax: "<number>"; inherits: false; initial-value: 0 }
@property --tb4 { syntax: "<number>"; inherits: false; initial-value: 0 }
@property --tnear { syntax: "<number>"; inherits: false; initial-value: 0 }
@property --tfar { syntax: "<number>"; inherits: false; initial-value: 0 }
@property --box { syntax: "<number>"; inherits: false; initial-value: 0 }
@property --dist { syntax: "<number>"; inherits: false; initial-value: 0 }
@property --side { syntax: "<number>"; inherits: false; initial-value: 0 }
@property --hgt { syntax: "<number>"; inherits: false; initial-value: 0 }
/*/gen:props*/
@property --off { syntax: "<integer>"; inherits: true; initial-value: 0 }
@property --cell { syntax: "<length>"; inherits: true; initial-value: 16px }
@property --sky { syntax: "<length>"; inherits: true; initial-value: 800px }
@property --hz { syntax: "<length>"; inherits: true; initial-value: 600px }
@property --Y1 { syntax: "<length>"; inherits: false; initial-value: 0px }
@property --Y2 { syntax: "<length>"; inherits: false; initial-value: 0px }
@property --H1 { syntax: "<length>"; inherits: false; initial-value: 0px }
@property --H2 { syntax: "<length>"; inherits: false; initial-value: 0px }

/* ---------------------------------------------------------------------------------- stage -- */

.hero {
  position: relative;
  height: max(100svh, 540px);
  overflow: clip;
  background: var(--bg);
}

.stage {
  --n: 96;                                                 /* strips across */
  --cell: round(up, calc(100vw / var(--n) - .02px), 1px);  /* a whole number of pixels, always */
  --sky: max(100svh, 540px);                               /* the stage height */
  --rows: tan(atan2(var(--sky), var(--cell)));             /* two lengths in, one number out */
  --hr: round(calc(var(--rows) * .74), 1);                 /* the horizon row */
  --hz: calc(var(--hr) * var(--cell));                     /* ...and as a length */
  --bank: round(calc(var(--rows) * .9), 1);                /* top of the near bank */
  --tc: 8;                                                 /* wordmark column and row */
  --tr: round(calc(var(--rows) * .13), 1);
  --pc: round(calc(var(--n) * .7), 1);                     /* where the pup sits */
  --gap: 2px;

  position: absolute;
  top: 0;
  bottom: 0;
  left: round(down, calc((100% - var(--n) * var(--cell)) / 2), 1px);
  width: calc(var(--n) * var(--cell));
  background:
    radial-gradient(110% 60% at calc((var(--pc) + 3) * var(--cell)) var(--hz), rgb(255 111 196 / .26), rgb(120 80 255 / .1) 45%, transparent 70%),
    linear-gradient(to bottom, #05040f, #0a0824 45%, #171046 var(--hz), #0b0926 var(--hz), #070615);
}

@media (max-aspect-ratio: 3/2) { .stage { --n: 72; --tc: 6; --pc: round(calc(var(--n) * .66), 1) } }
@media (max-aspect-ratio: 1/1) { .stage { --n: 56; --tc: round(calc((var(--n) - 34) / 2), 1); --pc: round(calc(var(--n) * .56), 1) } }
@media (max-aspect-ratio: 2/3) { .stage { --n: 40; --gap: 1px; --pc: round(calc(var(--n) * .5), 1) } }

.cols b:nth-child(n + 97), .land b:nth-child(n + 97) { display: none }
@media (max-aspect-ratio: 3/2) { .cols b:nth-child(n + 73), .land b:nth-child(n + 73) { display: none } }
@media (max-aspect-ratio: 1/1) { .cols b:nth-child(n + 57), .land b:nth-child(n + 57) { display: none } }
@media (max-aspect-ratio: 2/3) { .cols b:nth-child(n + 41), .land b:nth-child(n + 41) { display: none } }

/* ---------------------------------------------------------------------------------- stars -- */

.stars i {
  position: absolute;
  left: calc(round(down, calc(var(--u) * var(--n)), 1) * var(--cell));
  top: calc(round(down, calc(var(--v) * (var(--hr) - 6)), 1) * var(--cell));
  width: var(--cell);
  height: var(--cell);
  /* a star is a small lamp in the middle of its cell */
  background: radial-gradient(closest-side, #eef1ff 34%, transparent 36%);
  animation: twinkle var(--d) steps(3) var(--o) infinite alternate;   /* lamps have four levels */
}
.stars i:nth-child(3n) { background-image: radial-gradient(closest-side, #ffc9ec 34%, transparent 36%) }
.stars i:nth-child(4n) { background-image: radial-gradient(closest-side, #b5dcff 34%, transparent 36%) }
.stars i:nth-child(9n) { background-image: radial-gradient(closest-side, #fff 52%, transparent 54%) }
@keyframes twinkle { from { opacity: .1 } to { opacity: .85 } }

/* a meteor, now and then */
.stars u {
  position: absolute;
  left: calc(round(calc(var(--n) * .66), 1) * var(--cell));
  top: calc(3 * var(--cell));
  width: calc(5 * var(--cell));
  height: var(--cell);
  background: linear-gradient(to right, #fff, rgb(255 160 220 / .6) 40%, transparent);
  opacity: 0;
  animation: meteor 19s steps(12) 7s infinite;
}
@keyframes meteor {
  0% { opacity: 1; translate: 0 0 }
  5% { opacity: 1; translate: calc(-24 * var(--cell)) calc(12 * var(--cell)) }
  5.01%, 100% { opacity: 0; translate: calc(-24 * var(--cell)) calc(12 * var(--cell)) }
}

/* --------------------------------------------------------------------------------- aurora -- */

.live { display: contents }
/* the light eases after the pointer in nine steps, so the sky is never re-solved more than
   about twenty times a second however fast the pointer moves                              */
.live { transition: --mx .9s steps(9), --my .9s steps(9), --mo .8s steps(8) }
.live:has(.m:hover) { --mo: 1 }
/*gen:pointer*/
.live:has(.m p:nth-child(1):hover) { --my: 1 }
.live:has(.m p:nth-child(2):hover) { --my: 2 }
.live:has(.m p:nth-child(3):hover) { --my: 3 }
.live:has(.m p:nth-child(4):hover) { --my: 4 }
.live:has(.m p:nth-child(5):hover) { --my: 5 }
.live:has(.m p:nth-child(6):hover) { --my: 6 }
.live:has(.m p:nth-child(7):hover) { --my: 7 }
.live:has(.m p:nth-child(8):hover) { --my: 8 }
.live:has(.m p:nth-child(9):hover) { --my: 9 }
.live:has(.m p:nth-child(10):hover) { --my: 10 }
.live:has(.m p:nth-child(11):hover) { --my: 11 }
.live:has(.m p:nth-child(12):hover) { --my: 12 }
.live:has(.m p:nth-child(13):hover) { --my: 13 }
.live:has(.m p:nth-child(14):hover) { --my: 14 }
.live:has(.m p:nth-child(15):hover) { --my: 15 }
.live:has(.m p:nth-child(16):hover) { --my: 16 }
.live:has(.m i:nth-child(1):hover) { --mx: 1 }
.live:has(.m i:nth-child(2):hover) { --mx: 2 }
.live:has(.m i:nth-child(3):hover) { --mx: 3 }
.live:has(.m i:nth-child(4):hover) { --mx: 4 }
.live:has(.m i:nth-child(5):hover) { --mx: 5 }
.live:has(.m i:nth-child(6):hover) { --mx: 6 }
.live:has(.m i:nth-child(7):hover) { --mx: 7 }
.live:has(.m i:nth-child(8):hover) { --mx: 8 }
.live:has(.m i:nth-child(9):hover) { --mx: 9 }
.live:has(.m i:nth-child(10):hover) { --mx: 10 }
.live:has(.m i:nth-child(11):hover) { --mx: 11 }
.live:has(.m i:nth-child(12):hover) { --mx: 12 }
.live:has(.m i:nth-child(13):hover) { --mx: 13 }
.live:has(.m i:nth-child(14):hover) { --mx: 14 }
.live:has(.m i:nth-child(15):hover) { --mx: 15 }
.live:has(.m i:nth-child(16):hover) { --mx: 16 }
/*/gen:pointer*/

.cols, .land {
  position: absolute;
  inset: 0;
  display: flex;
}
.cols {
  --gr: calc(var(--n) * var(--n) / 70);                    /* pointer reach, squared, in strips */
  animation: sky-t 3600s steps(36000) infinite;            /* ten frames a second */
  content-visibility: auto;
  overflow: clip;
}
@keyframes sky-t { to { --t: 3600 } }

.cols b, .land b { flex: none; width: var(--cell) }

/* a strip is four gradients, drawn once: a blue curtain, a pink curtain, and each of them
   again upside down in the lake. every frame the strip solves where its curtains hang
   (--Y1, --Y2) and how tall its rays stand (--H1, --H2), then slides and stretches the
   gradients to fit. nothing is repainted that did not move                                 */
.cols b {
  --u: calc(var(--i) / var(--n));

  /* the pointer lifts the curtains under it and draws their rays up */
  --dx: calc(var(--i) + .5 - (var(--mx) - .5) / 16 * var(--n));
  --g: calc(var(--mo) * exp(-1 * var(--dx) * var(--dx) / var(--gr)));

  /* curtain one, blue: the height its lower edge hangs at, then how alive this ray is */
  --Y1: calc(var(--sky) * (.31 + .085 * sin(var(--u) * 5.1 + var(--t) * .23) + .04 * sin(var(--u) * 11.3 - var(--t) * .31) - var(--g) * .03));
  --r1: calc(.5 + .5 * sin(var(--i) * .43 + 2.4 * sin(var(--u) * 6 + var(--t) * .37) + var(--t) * .8));
  --H1: calc(var(--sky) * (.15 + .17 * var(--r1) + .07 * var(--g)));

  /* curtain two, pink: lower, and drifting the other way */
  --Y2: calc(var(--sky) * (.49 + .075 * sin(var(--u) * 4.3 - var(--t) * .19 + 2) + .045 * sin(var(--u) * 9.7 + var(--t) * .27 + 1) - var(--g) * .03));
  --r2: calc(.5 + .5 * sin(var(--i) * .37 - 2.1 * sin(var(--u) * 5 - var(--t) * .29) - var(--t) * .7));
  --H2: calc(var(--sky) * (.13 + .16 * var(--r2) + .07 * var(--g)));

  opacity: calc(.6 + .2 * (var(--r1) + var(--r2)) + .35 * var(--g));
  background-repeat: no-repeat;
  background-image:
    linear-gradient(to bottom, transparent, rgb(190 70 255 / .28) 46%, rgb(255 96 196 / .82) 86%, rgb(255 196 234 / .97) 95%, transparent),
    linear-gradient(to bottom, transparent, rgb(78 86 255 / .28) 48%, rgb(80 166 255 / .82) 87%, rgb(180 230 255 / .97) 95%, transparent),
    linear-gradient(to bottom, transparent, rgb(255 120 206 / .44) 10%, rgb(190 70 255 / .14) 55%, transparent),
    linear-gradient(to bottom, transparent, rgb(110 186 255 / .44) 10%, rgb(78 86 255 / .14) 55%, transparent);
  /* the lake mirrors each lower edge half as far below the horizon as it hangs above it */
  background-size: 100% var(--H2), 100% var(--H1), 100% calc(var(--H2) * .5), 100% calc(var(--H1) * .5);
  background-position:
    0 calc(var(--Y2) - var(--H2)),
    0 calc(var(--Y1) - var(--H1)),
    0 calc(var(--hz) * 1.5 - var(--Y2) * .5),
    0 calc(var(--hz) * 1.5 - var(--Y1) * .5);
}

/* the land never moves, so it is solved once: pines from overlapping triangle waves, hills
   from a slow sine, a clearing around the pup, and a near bank that rises under it         */
.land b {
  --u: calc(var(--i) / var(--n));
  --th: round(calc(1.3 * sin(var(--u) * 7 + .6) + .8 + 1.25 * max(
            calc(3.5 - max(calc(mod(calc(var(--i) + 1), 7) - 3.5), calc(3.5 - mod(calc(var(--i) + 1), 7)))),
            calc(5.5 - max(calc(mod(calc(var(--i) + 4), 11) - 5.5), calc(5.5 - mod(calc(var(--i) + 4), 11)))),
            calc(2.5 - max(calc(mod(var(--i), 5) - 2.5), calc(2.5 - mod(var(--i), 5)))))), 1);
  --fx: max(calc(var(--i) - var(--pc) - 9), calc(var(--pc) + 9 - var(--i)));
  --ft: calc(var(--bank) + round(calc(.011 * max(0, calc(var(--fx) - 10)) * max(0, calc(var(--fx) - 10))), 1));
  /* the moon is a circle solved a column at a time: at --md strips from its centre it stands
     sqrt(r^2 - md^2) rows above the horizon, and lays a path of light on the water below  */
  --md: calc(var(--i) + .5 - var(--pc) - 3);
  --mh: round(sqrt(max(0, calc(100 - var(--md) * var(--md)))), 1);
  background:
    linear-gradient(to bottom, transparent calc(var(--ft) * var(--cell)), #04030c 0),
    linear-gradient(to bottom,
      transparent calc(var(--hz) - round(calc(var(--th) * clamp(.12, calc((var(--fx) - 10) / 7), 1)), 1) * var(--cell)),
      #0c0822 0 var(--hz), transparent 0),
    linear-gradient(to bottom,
      transparent calc(var(--hz) - var(--mh) * var(--cell)),
      #fff3fb 0, #ffc9ea var(--hz),
      rgb(255 190 232 / calc(.006 * var(--mh) * var(--mh))) 0, transparent calc(var(--hz) + var(--mh) * .9 * var(--cell)));
}

/* ripples: one row in three of the lake is a shade darker, and the pattern drifts */
.water {
  position: absolute;
  left: 0;
  right: 0;
  top: var(--hz);
  bottom: 0;
  background: repeating-linear-gradient(to bottom, rgb(7 6 21 / .5) 0 var(--cell), transparent 0 calc(3 * var(--cell)));
  animation: ripple 2.4s steps(3) infinite;
}
@keyframes ripple { to { background-position-y: calc(3 * var(--cell)) } }

/* a soft light that eases after the pointer */
.glow {
  position: absolute;
  left: calc((var(--mx) - .5) / 16 * 100%);
  top: calc((var(--my) - .5) / 16 * 100%);
  width: calc(44 * var(--cell));
  aspect-ratio: 1;
  translate: -50% -50%;
  background: radial-gradient(closest-side, rgb(214 170 255 / .3), rgb(150 120 255 / .1) 55%, transparent);
  opacity: var(--mo);
}

.m {
  position: absolute;
  inset: 0;
  z-index: 3;
  display: grid;
  grid-template-rows: repeat(16, 1fr);
}
.m p { display: grid; grid-template-columns: repeat(16, 1fr); margin: 0 }

/* ----------------------------------------------------------------------- wordmark and pup -- */

/* the font is drawn 8 pixels to the em, so at 16 cells every font pixel is exactly 2x2 cells */
.mark {
  position: absolute;
  left: calc(var(--tc) * var(--cell));
  top: calc(var(--tr) * var(--cell));
  margin: 0;
  font: calc(16 * var(--cell)) / 1.25 var(--pixel);
  white-space: nowrap;
  filter: drop-shadow(calc(2 * var(--cell)) calc(2 * var(--cell)) 0 rgb(4 3 14 / .72));
}
.mark span {
  color: transparent;
  background:
    linear-gradient(105deg, transparent 42%, #fff 50%, transparent 58%) 130% 0 / 300% 100% no-repeat,
    linear-gradient(100deg, #9fd6ff, #f7f2ff 48%, #ffa3dc);
  -webkit-background-clip: text;
  background-clip: text;
  animation: shine 11s ease-in-out 2s infinite;
}
@keyframes shine { 0% { background-position: 130% 0, 0 0 } 14%, 100% { background-position: -30% 0, 0 0 } }

.pup {
  position: absolute;
  left: calc(var(--pc) * var(--cell));
  top: calc((var(--bank) - 16) * var(--cell));
  width: calc(19 * var(--cell));
  height: calc(16 * var(--cell));
  fill: #04030c;
  shape-rendering: crispEdges;
}
.pup .a { animation: wag 1s steps(1) infinite }
.pup .b { animation: wag 1s steps(1) -.5s infinite }
@keyframes wag { 50% { opacity: 0 } }

/* the lattice that turns all of it into a wall of lamps */
.lat {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(to right, var(--bg) var(--gap), transparent 0) 0 0 / var(--cell) var(--cell),
    linear-gradient(to bottom, var(--bg) var(--gap), transparent 0) 0 0 / var(--cell) var(--cell);
  opacity: .6;
}
.stage::after {
  content: "";
  position: absolute;
  inset: auto 0 0;
  height: 14%;
  background: linear-gradient(to bottom, transparent, var(--bg));
}
.stars, .water, .lat, .glow, .pup, .cols, .land, .stage::after { pointer-events: none }

@media (prefers-reduced-motion: reduce) {
  .cols { animation: none; --t: 47 }
  .live { transition: none }
  .stars i, .stars u, .water, .mark span, .pup .a { animation: none }
  .pup .b { animation: none; opacity: 0 }
}

/* ======================================================================== the miniatures == */
/* small live things inside the project tiles. each one is paused by content-visibility while
   it is off screen, and none of them runs faster than it needs to.                          */

/* ------------------------------------------------------------- typing (qwennie and yipsy) -- */
/* --p counts characters. every line knows how many characters come before it (--s) and how
   many it holds (--c), and shows clamp(0, p - s, c) of them.                                */

.type { --p: 0; animation: typing 12s steps(var(--total)) infinite; content-visibility: auto; font: 14px/1.7 var(--mono) }
@keyframes typing { 0%, 5% { --p: 0 } 55%, 100% { --p: var(--total) } }
.ln { display: block; white-space: nowrap }
.ln > span {
  display: inline-block;
  vertical-align: bottom;
  overflow: hidden;
  width: calc(clamp(0, round(down, calc(var(--p) - var(--s)), 1), var(--c)) * 1ch);
}
.ln::after {
  content: "";
  display: inline-block;
  width: .55em;
  height: 1.1em;
  margin-left: 1px;
  vertical-align: -.2em;
  background: var(--pink);
  opacity: calc(clamp(0, round(down, calc(var(--p) - var(--s) + 1), 1), 1) * clamp(0, round(up, calc(var(--s) + var(--c) - var(--p)), 1), 1));
}
.ln.last::after { opacity: clamp(0, round(down, calc(var(--p) - var(--s) + 1), 1), 1); animation: blink 1s steps(1) infinite }
@keyframes blink { 50% { background: transparent } }

.chat { display: grid; gap: 14px; width: min(100%, 330px) }
.chat p { margin: 0 }
.chat .you {
  justify-self: end;
  padding: 7px 14px;
  background: linear-gradient(100deg, #7d5cff, var(--pink));
  color: #fff;
  box-shadow: 3px 3px 0 #2c2468;
}
.chat .bot {
  justify-self: start;
  min-width: calc(35ch + 30px);
  min-height: calc(3.4em + 22px);
  padding: 10px 14px;
  background: #15112f;
  color: var(--ink);
  box-shadow: 3px 3px 0 #2c2468;
}
.babble { width: min(100%, 330px) }
.babble p {
  margin: 0;
  min-height: calc(6.8em + 24px);
  padding: 12px 14px;
  background: #15112f;
  color: var(--ink);
  box-shadow: 3px 3px 0 #2c2468;
}
.babble b { font-weight: 400; color: var(--pink) }
.babble .ln::after { background: var(--blue) }

/* ------------------------------------------------------------------------- diffusion.css -- */
/* a replay, not the model: each cell knows its final colour and mixes it with fresh hashed
   noise, twelve steps from all noise (--k: 1) to none.                                      */

.dif-pair { display: flex; gap: clamp(16px, 4vw, 30px); justify-content: center; width: 100% }
.dif {
  --k: 1;
  display: grid;
  grid-template-columns: repeat(16, 1fr);
  width: min(44%, 184px);
  aspect-ratio: 1;
  outline: 2px solid var(--line);
  outline-offset: 5px;
  animation: denoise 9s linear infinite;
  content-visibility: auto;
}
.dif.two { animation-delay: -4.5s }
@keyframes denoise {
  0% { --k: 1 }
  10% { --k: 1; animation-timing-function: steps(12) }
  46%, 100% { --k: 0 }
}
.dif b {
  --nz: round(calc(var(--k) * 12), 1);
  background: rgb(
    calc(var(--cr) * (1 - var(--k)) + var(--k) * 255 * mod(calc(sin(var(--i) * 12.9898 + var(--nz) * 78.233) * 437.585), 1))
    calc(var(--cg) * (1 - var(--k)) + var(--k) * 255 * mod(calc(sin(var(--i) * 39.346 + var(--nz) * 11.135) * 437.585), 1))
    calc(var(--cb) * (1 - var(--k)) + var(--k) * 255 * mod(calc(sin(var(--i) * 73.156 + var(--nz) * 52.235) * 437.585), 1)));
}
.dif-cap { margin-top: 22px; animation: denoise 9s linear infinite; counter-reset: step round(calc(var(--k) * 12), 1) }
.dif-cap sub { font-size: inherit; vertical-align: -.35em; color: var(--pink) }
.dif-cap sub::after { content: counter(step, pad2) }
.dif-cap span { margin-left: 12px }

/* ----------------------------------------------------------------------------- pawtraced -- */
/* a ray tracer, 28 x 18. every <b> is a pixel: it builds its own ray, intersects a sphere and
   a floor, casts a shadow ray at the sun, and shades itself. the sun is wherever you point.  */

.ray {
  --ll: hypot(var(--lx), var(--ly), var(--lz));
  position: relative;
  display: grid;
  grid-template-columns: repeat(28, 1fr);
  width: 100%;
  aspect-ratio: 28 / 18;
  content-visibility: auto;
}
/*gen:sun*/
.ray:has(.sun i:nth-child(8n + 1):hover) { --lx: -0.900 }
.ray:has(.sun i:nth-child(8n + 2):hover) { --lx: -0.643 }
.ray:has(.sun i:nth-child(8n + 3):hover) { --lx: -0.386 }
.ray:has(.sun i:nth-child(8n + 4):hover) { --lx: -0.129 }
.ray:has(.sun i:nth-child(8n + 5):hover) { --lx: 0.129 }
.ray:has(.sun i:nth-child(8n + 6):hover) { --lx: 0.386 }
.ray:has(.sun i:nth-child(8n + 7):hover) { --lx: 0.643 }
.ray:has(.sun i:nth-child(8n + 8):hover) { --lx: 0.900 }
.ray:has(.sun i:nth-child(n + 1):nth-child(-n + 8):hover) { --ly: 1.000 }
.ray:has(.sun i:nth-child(n + 9):nth-child(-n + 16):hover) { --ly: 0.812 }
.ray:has(.sun i:nth-child(n + 17):nth-child(-n + 24):hover) { --ly: 0.625 }
.ray:has(.sun i:nth-child(n + 25):nth-child(-n + 32):hover) { --ly: 0.438 }
.ray:has(.sun i:nth-child(n + 33):nth-child(-n + 40):hover) { --ly: 0.250 }
/*/gen:sun*/
.sun { position: absolute; inset: 0; display: grid; grid-template-columns: repeat(8, 1fr) }

.ray b {
  --x: mod(var(--i), 28);
  --y: round(down, calc(var(--i) / 28), 1);

  /* the ray through this pixel, from a camera at the origin looking down -z */
  --sx: calc((var(--x) - 13.5) / 14);
  --sy: calc((8.5 - var(--y)) / 14 - .1);
  --len: hypot(var(--sx), var(--sy), 1.25);
  --rx: calc(var(--sx) / var(--len));
  --ry: calc(var(--sy) / var(--len));
  --rz: calc(-1.25 / var(--len));

  /* a unit sphere at (0, -.05, -3): solve the quadratic, keep the near root, take the normal */
  --bq: calc(-.05 * var(--ry) - 3 * var(--rz));
  --disc: calc(var(--bq) * var(--bq) - 8.0025);
  --hit: clamp(0, calc(var(--disc) * 10000), 1);
  --tn: calc(var(--bq) - sqrt(max(0, var(--disc))));
  --nx: calc(var(--rx) * var(--tn));
  --ny: calc(var(--ry) * var(--tn) + .05);
  --nz1: calc(var(--rz) * var(--tn) + 3);

  /* lambert, and a highlight where the reflected sun points back at the camera */
  --dif: max(0, calc((var(--nx) * var(--lx) + var(--ny) * var(--ly) + var(--nz1) * var(--lz)) / var(--ll)));
  --spec: pow(max(0, calc(-1 * (
            (2 * var(--dif) * var(--nx) - var(--lx) / var(--ll)) * var(--rx)
          + (2 * var(--dif) * var(--ny) - var(--ly) / var(--ll)) * var(--ry)
          + (2 * var(--dif) * var(--nz1) - var(--lz) / var(--ll)) * var(--rz)))), 18);

  /* the floor, y = -1.05: where the ray lands, which checker tile that is, how far away */
  --tfl: calc(-1.05 / min(var(--ry), -.001));
  --fx1: calc(var(--rx) * var(--tfl));
  --fz1: calc(var(--rz) * var(--tfl));
  --chk: mod(calc(round(down, calc(var(--fx1) * 1.15 + 50), 1) + round(down, calc(var(--fz1) * 1.15), 1)), 2);
  --fog: exp(calc(-.09 * var(--tfl)));

  /* the shadow ray: from that point on the floor toward the sun. does it meet the sphere? */
  --bs: calc((var(--ly) - var(--fx1) * var(--lx) - (3 + var(--fz1)) * var(--lz)) / var(--ll));
  --ds: calc(var(--bs) * var(--bs) - var(--fx1) * var(--fx1) - (3 + var(--fz1)) * (3 + var(--fz1)));
  --shd: calc(clamp(0, calc(var(--ds) * 5), 1) * clamp(0, calc(var(--bs) * 100), 1));

  /* which of the three things this pixel sees: sphere, floor, or sky */
  --fl: calc((1 - var(--hit)) * clamp(0, calc(var(--ry) * -10000), 1));
  --sk: calc(1 - var(--hit) - var(--fl));

  background: rgb(
    calc(var(--hit) * (38 + 217 * var(--dif) + 255 * var(--spec))
       + var(--fl) * ((24 + 76 * var(--chk)) * (1 - .66 * var(--shd)) * var(--fog) + 46 * (1 - var(--fog)))
       + var(--sk) * (58 - 50 * clamp(0, calc(var(--ry) * 2.6), 1)))
    calc(var(--hit) * (26 + 85 * var(--dif) + 255 * var(--spec))
       + var(--fl) * ((20 + 162 * var(--chk)) * (1 - .66 * var(--shd)) * var(--fog) + 30 * (1 - var(--fog)))
       + var(--sk) * (36 - 30 * clamp(0, calc(var(--ry) * 2.6), 1)))
    calc(var(--hit) * (74 + 122 * var(--dif) + 255 * var(--spec))
       + var(--fl) * ((58 + 197 * var(--chk)) * (1 - .6 * var(--shd)) * var(--fog) + 104 * (1 - var(--fog)))
       + var(--sk) * (118 - 96 * clamp(0, calc(var(--ry) * 2.6), 1))));
}
.ray-cap { padding: 12px 14px 10px; color: var(--dim) }

/* ------------------------------------------------------------------------ the fluid doodle -- */

.tank {
  width: min(100%, 320px);
  aspect-ratio: 16 / 10;
  border: 2px solid var(--line);
  border-top: 0;
  background: #030208;
  overflow: hidden;
}
.goo { position: relative; height: 100%; filter: url(#goo) }
.goo i {
  position: absolute;
  bottom: -16%;
  left: calc(var(--j) * 11.5% - 9%);
  width: 27%;
  aspect-ratio: 1;
  border-radius: 50%;
  background: linear-gradient(to bottom, #9ad2ff, #4f8dff 45%, #3a54e8);
  animation: slosh 2.7s ease-in-out calc(var(--j) * -.31s) infinite alternate;
}
.goo i:nth-child(3n) { animation-duration: 3.4s }
@keyframes slosh { from { translate: 0 6% } to { translate: 0 -62% } }

/* ----------------------------------------------------------------------------- the oled -- */
/* a raycaster: 64 columns, one ray each. the room is a rectangle and there is one pillar in
   it, so every distance has a closed form: no stepping, no loop, just min() and max().      */

.oled {
  --camy: .35;
  display: flex;
  aspect-ratio: 72 / 40;
  padding: 0;
  background: #000;
  border: 12px solid #0a0913;
  outline: 2px solid var(--line);
  box-shadow: 0 0 60px rgb(98 182 255 / .12);
  /* the camera turns on the spot, ten frames a second, and sways along x as it goes */
  --camx: calc(-1.2 + .95 * sin(var(--cama) * 3));
  animation: turn 36s steps(360) infinite;
  content-visibility: auto;
}
@keyframes turn { to { --cama: 6.28319 } }

.oled b {
  flex: 1;
  --ang: calc(var(--cama) + (var(--i) - 31.5) / 64 * 1.1);
  --cdx: cos(var(--ang));
  --cdy: sin(var(--ang));
  /* the room: |x| <= 3, |y| <= 2. of each pair of walls, the one ahead has the positive t */
  --twx: max(calc((3 - var(--camx)) / var(--cdx)), calc((-3 - var(--camx)) / var(--cdx)));
  --twy: max(calc((2 - var(--camy)) / var(--cdy)), calc((-2 - var(--camy)) / var(--cdy)));
  --wall: min(var(--twx), var(--twy));
  /* the pillar, a box from (.7, -.6) to (1.5, .2): the slab test */
  --tb1: calc((.7 - var(--camx)) / var(--cdx));
  --tb2: calc((1.5 - var(--camx)) / var(--cdx));
  --tb3: calc((-.6 - var(--camy)) / var(--cdy));
  --tb4: calc((.2 - var(--camy)) / var(--cdy));
  --tnear: max(min(var(--tb1), var(--tb2)), min(var(--tb3), var(--tb4)));
  --tfar: min(max(var(--tb1), var(--tb2)), max(var(--tb3), var(--tb4)));
  --box: calc(clamp(0, calc((var(--tfar) - var(--tnear)) * 1000), 1) * clamp(0, calc(var(--tnear) * 1000), 1));
  --dist: calc(var(--box) * var(--tnear) + (1 - var(--box)) * var(--wall));
  /* which way the surface faces decides whether it is lit solid or every other line */
  --side: calc(var(--box) * clamp(0, calc((min(var(--tb1), var(--tb2)) - min(var(--tb3), var(--tb4))) * 1000), 1)
             + (1 - var(--box)) * clamp(0, calc((var(--twy) - var(--twx)) * 1000), 1));
  /* wall height on screen, with the fisheye taken out */
  --hgt: calc(.5 / (var(--dist) * cos(calc((var(--i) - 31.5) / 64 * 1.1))));
  background:
    linear-gradient(to bottom, #000 calc(50% - var(--hgt) * 100%), transparent 0 calc(50% + var(--hgt) * 100%), #000 0),
    linear-gradient(to right, #cfeaff calc(50% + 50% * var(--side)), #000 0);
}

@media (prefers-reduced-motion: reduce) {
  .type { animation: none; --p: var(--total) }
  .dif, .dif-cap { animation: none; --k: 0 }
  .goo i, .ln.last::after { animation: none }
  .oled { animation: none; --cama: .5 }
}

/* ================================================================== nothing runs unwatched == */
/* each animated block sits inside something that flies a flag, --off, while it is out of view:
   a scroll-driven animation holds it at 1 before the block enters the window and after it
   leaves, and 0 in between. a style query on that flag pauses everything underneath. where
   either feature is missing the flag stays 0 and things simply keep running.                 */

@keyframes offscreen { 0% { --off: 1 } 1%, 99% { --off: 0 } 100% { --off: 1 } }
@supports (animation-timeline: view()) {
  .hero, .screen, .device {
    animation: offscreen linear both;
    animation-timeline: view(block -12%);
  }
}
@container style(--off: 1) {
  .cols, .hud-frame, .stars i, .stars u, .water, .mark span, .pup g, .down,
  .type, .ln::after, .dif, .dif-cap, .goo i, .oled { animation-play-state: paused }
}
