Rainy Alley how it’s made

The idea

You are standing at the mouth of a narrow brick alley at night, in the rain. Two street lights stand at the intersection at the far end, and cars cross it in both directions, braking as they pass. Lit and blacked-out windows line both walls, a pink BAR sign and a cyan neon ring hang on them, and the wet asphalt mirrors everything as long, wavering streaks broken up by moving water. Rain falls in three layers at slightly different angles, rings open in the puddles, and the BAR sign stutters every few seconds, as does its reflection. The scene is one HTML file and one CSS file, with no JavaScript and no raster images.

The Controls tab has two kinds of control. The first five change the weather and the reflection. The ones marked "Performance" turn the costly effects up, down or off (with switches for the on/off ones), so you can watch the frame rate in the Stats tab and see which CSS the GPU is paying for.

Techniques

One background, painted twice

The reflection is not a copy of anything. Every light in the scene, from the lamp halos to the lit windows, is one background stack stored in a custom property, --lights, on .stage. Two elements share the class .glow, and both paint it:

.glow {
  inset: 0 0 48.6%;
  background: var(--lights);
}
.reflect {
  transform-origin: 50% 100%;
  scale: 1 calc(var(--stretch) * -1);
  filter: blur(calc(var(--blur) * 1vmin)) saturate(1.4);
}

The .glow box ends exactly at the far ground line (51.4% down the stage), so a negative scale about its bottom edge flips it onto the ground. A stretch greater than 1 pulls each light into the long vertical streak you see on a real wet road. Because both elements read the same property, adding or moving a light in --lights moves its reflection too. There is nothing to keep in sync.

The neon signs come along because they are ::before and ::after of .glow, so the same rules draw them on both elements. So do the cars (below), which are the same markup inside both elements. That includes the flicker animation: the sign and its reflection run the same flicker keyframes from the same start time, so they stutter together. The text is mirrored in the puddle, as it should be.

Why not -webkit-box-reflect or element()? The first isn't in Firefox, and the second is only in Firefox. Custom properties and individual transforms are widely available.

Trade-off: physically, a light reflects about the point where it meets the ground, not about one shared line. Using the far ground line as the mirror is right for things at the far end and only approximate for things on the near walls. The "Reflection stretch" control (--stretch) lets you tune how far the near reflections reach.

Masks for where the ground is wet

.wet holds the reflection. Its clip-path keeps it inside the ground's trapezoid, and a stack of radial masks decides how wet each part is: puddles reflect fully, and the rest of the asphalt gets a sheen that fades toward the viewer.

.wet {
  inset: 0;
  clip-path: polygon(38% 51.4%, 62% 51.4%, 100% 100%, 0 100%);
  mask-image:
    radial-gradient(ellipse 22% 6% at 45% 69%, #000 45%, #0000),
    radial-gradient(ellipse 12% 3% at 57% 58%, #000 35%, #0000),
    radial-gradient(ellipse 28% 8% at 38% 90%, #000 45%, #0000),
    radial-gradient(ellipse 16% 5% at 72% 82%, #000 40%, #0000),
    linear-gradient(#000 52%, #0007 62%, #0004);
  /* the floor plane below is seen from the same eye as the walls */
  perspective: 100vmin;
  perspective-origin: 50% 36%;
}

The mask and clip sit on the parent, not on the transformed .reflect, so they stay in stage coordinates instead of being flipped and stretched. Over the reflection, the .floor plane (below) adds moving, broken streaks, which is what makes the reflection waver. mask-image is widely available (2023-12-07).

Perspective windows inside the light layer

A lit window on a side wall is a quadrilateral that points at the vanishing point, which a gradient can't draw. So the lit windows are paths in a small SVG, generated from the vanishing point, and that SVG is one more layer of --lights, as a data URI with viewBox='0 0 100 51.4' preserveAspectRatio='none' sized 100% 100%. Its coordinates are the stage's own, so the windows land on the walls, and they reflect like every other light. The blacked-out windows, the light poles and the fire escape are dark, so they don't need reflections. They live in plain inline SVGs over the walls, and the fire escape's SVG sits after .lights so its railings cross in front of the window behind it.

Trade-off: anything below the mirror line (51.4%) can't be in --lights, so the lowest edge of a near ground-floor window is cut off at that line.

Cars in both copies

Each car is a full-stage element moved with translate. Its body, lights and brake light are pseudo-elements and a b placed at the alley mouth, and .cars clips everything to the 38–62% opening, so a car appears from behind one wall and disappears behind the other. The markup sits inside both .glow elements, so the headlight, its beam on the road and the brake light all get a reflection that moves in step:

.cars { inset: 0; clip-path: inset(0 38%); }
@keyframes drive {
  0% { translate: 0; animation-timing-function: cubic-bezier(0.2, 0.6, 0.8, 0.4); } /* slows at the alley */
  34%, 100% { translate: var(--dx); }
}

The easing curve is steep at both ends and flat in the middle, so the car slows as it passes the alley, and the brake keyframes light the brake light over the same stretch. Each car's size and travel come from a few custom properties: --w (body length, 10.5% of the stage), and from it --x0 (a start just hidden behind the left wall) and --dx (a travel distance that ends just hidden behind the right wall). Cars going left to right use the near lane. The other lane is the same car flipped with scale: -1 1, made shorter (--w: 9%), placed a little higher and farther, drawn behind the near lane (z-index: -1), and run on a 15s cycle against 12s, so the two rarely meet.

A water plane at the alley's vanishing point

The water streaks are not drawn in screen space. .wet gets perspective with its origin at the vanishing point (50% 36%), and .floor is a real plane laid flat with rotate: x 90deg, starting at the far ground line:

.floor {
  inset: 51.4% 38% auto;
  block-size: 80vmin;
  transform-origin: 50% 0;
  rotate: x 90deg;
  overflow: clip;
}

Because the perspective origin is the same vanishing point the walls use, the plane lines up with the alley: 24% wide at the far end, it widens to the full screen at the bottom. Its child i carries an SVG tile of broken, wavy dark streaks with a few faint glints, and moves one tile toward the viewer every 2.4s. On screen the streaks come out thin and crowded near the far end and wider and sparser near you, and that perspective is what keeps the road from looking flat. The animation is still a 2D translate inside the rotated plane, so it stays on the compositor. 3D transforms are widely available.

Perspective bricks from one conic gradient

On a wall seen in perspective, the mortar courses all point at the vanishing point. A repeating-conic-gradient centred on that point draws exactly that, and each wall's clip-path cuts it to shape:

.alley::before, .alley::after {
  content: "";
  inset: 0;
  background:
    linear-gradient(var(--dir), #000a, #0000 30%),
    repeating-conic-gradient(from 0deg at 50% 36%, var(--wall) 0 var(--course), var(--mortar) 0 calc(var(--course) + 0.2deg));
}

The mortar lines get wider away from the vanishing point, which reads as depth. Trade-off: on narrow screens the courses squeeze together and alias into moiré swirls, so below 600px --course doubles to 2.2deg. The window, pole and fire-escape SVGs use the same stage coordinates (viewBox="0 0 100 100", preserveAspectRatio="none", vector-effect: non-scaling-stroke), with every course computed toward the same vanishing point.

Rain layers from one rule

Every rain layer is an i in .rain, and one rule derives everything about it from its depth, --z (its index, capped at 4): tile height, streak size, opacity, speed and angle. Nearer layers have longer streaks, move faster and lean further with the wind:

.rain i {
  --z: min(var(--i), 4);
  inset: -60%; /* overscan, so a tilted layer still covers a portrait phone */
  rotate: calc(var(--tilt) + (var(--z) - 2) * var(--spread));
}
.rain i::before {
  --h: calc(14vmin + var(--z) * 9vmin);
  --w: calc((2vmin + var(--z) * 4vmin) / var(--density));

Each layer's streaks are tiles of thin radial-gradient ellipses, and every tile in a layer is --h tall. The layer moves down exactly --h per loop (@keyframes fall { to { translate: 0 var(--h); } }), so the last frame matches the first. "Rain amount" only narrows the tiles (--w), and "Rain speed" only shortens the duration, so neither can break the loop. Because nothing is written per layer, the "Performance: rain layers" control can add layers up to eight. Layers past the fourth keep the nearest style, with their tiles offset by --i and a slightly different speed, so they don't line up into rows. The rotation goes on the i and the fall on its ::before, because translate applies before rotate, so a rotated element can't fall along its own tilt.

Ripples placed by sin() and cos()

The eight ripple rings have no positions in the markup. Each ring's index goes through sin() and cos(), which scatters the rings over the ground. Rings nearer the viewer (lower on screen) come out wider:

.ripples i {
  --r: var(--i);
  left: calc(50% + sin(var(--r) * 2.4rad) * 17%);
  top: calc(73% + cos(var(--r) * 1.9rad) * 11%);
  inline-size: calc(6vmin + cos(var(--r) * 1.9rad) * 2.4vmin);
@supports (order: sibling-index()) {
  .ripples i { --r: sibling-index(); }
}

Where sibling-index() is supported it replaces the inline --i, so you can add a ring without numbering it. The index is called --r, not --n, because the Controls' count slider writes --n (the total) on every element it clones. Trig functions are widely available; sibling-index() is newly available (2026-08-18), with the inline --i as the fallback.

Performance switches

"Off" should mean display: none or filter: none, not a zero value that still allocates a layer or a filter pass. Each Controls switch adds or removes a boolean data- attribute on .stage. All of them are in the markup, so the scene starts fully on, and :not() turns an effect off when its attribute is gone:

.stage:not([data-reflection]) .reflect { display: none; }
.stage:not([data-blur]) .reflect { filter: none; }
.stage:not([data-masks]) .wet { mask-image: none; }
.stage:not([data-water]) .floor { display: none; }

Plain attribute selectors work in every browser. Rain layers and ripples use the panel's count control instead, which clones or removes elements, and the blur amount is an ordinary slider.

How to use it: open Stats and watch the fps while you switch things off one at a time. The likely suspects are the reflection blur (a filter on a large, stretched layer), the rain layers (each larger than the viewport), and the masked, perspective-transformed water plane.

Performance notes

rawgzipbrotlibudgetok
23,595 B8,277 B7,064 B14,336 Byes

Every animation changes only translate, scale or opacity, so it can run on the compositor. The blur on .reflect is static, so it is painted once, until a car moves inside it: the cars and their lights animate inside the blurred, masked reflection, so that layer is re-filtered while a car is crossing. The rain layers are the other big cost, since each is larger than the viewport and promoted to its own layer. In a headless software-rendering check the scene ran at about 10 fps with the default settings, 6 fps with every performance control at maximum, 44 fps with them all off, and 60 fps under reduced motion. For comparison, ramen-counter ran at 46 fps in the same check. Software rendering doesn't predict GPU frame times, and real-hardware numbers are not yet measured; the Performance controls are there to find out (TODO: run perf-triage on a real device).

Accessibility

Known limitations

Reuse

<!doctype html>
<html lang="en">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="color-scheme" content="light dark">
<title>Rainy Alley</title>
<!--css-->
<main class="stage" data-reflection data-blur data-masks data-water data-bricks data-glow aria-label="A narrow city alley at night in the rain. Cars pass along the street at the far end under two street lights, and the wet asphalt mirrors their lights, the lit windows and a pink BAR sign as long, wavering streaks">
<div class="far" aria-hidden="true"></div>
<div class="street" aria-hidden="true"></div>
<div class="ground" aria-hidden="true"></div>
<div class="wet" aria-hidden="true"><div class="glow reflect"><div class="cars"><i class="car"><b></b></i><i class="car back"><b></b></i></div></div><div class="floor"><i></i></div></div>
<div class="ripples" aria-hidden="true"><i style="--i:1"></i><i style="--i:2"></i><i style="--i:3"></i><i style="--i:4"></i><i style="--i:5"></i><i style="--i:6"></i><i style="--i:7"></i><i style="--i:8"></i></div>
<div class="alley" aria-hidden="true"><i></i></div>
<svg class="walls" viewBox="0 0 100 100" preserveAspectRatio="none" aria-hidden="true"><path class="pane" d="M28.5 30L31.5 30.8L31.5 46.4L28.5 48ZM33.5 31.4L35.5 31.9L35.5 44.1L33.5 45.2ZM36.3 32.2L37.4 32.5L37.4 43.1L36.3 43.7ZM2 -29.3L9 -19.8L9 6.5L2 1.4ZM36.3 17.4L37.4 18.9L37.4 26.9L36.3 26.1ZM75 29L80 27.6L80 52.8L75 50ZM62.6 32.5L63.7 32.2L63.7 43.7L62.6 43.1ZM91 -19.8L98 -29.3L98 1.4L91 6.5ZM68.5 10.8L71.5 6.8L71.5 20.5L68.5 22.7ZM62.6 18.9L63.7 17.4L63.7 26.1L62.6 26.9ZM89 23.5L98 20.6L98 68.6L89 62.5Z"/><path class="frame" d="M30 30.4V47.2M34.5 31.7V44.7M36.8 32.3V43.4M5.5 -24.5V4M36.8 18.1V26.5M77.5 28.3V51.4M63.2 32.3V43.4M94.5 -24.5V4M70 8.8V21.6M63.2 18.1V26.5"/><path class="pole" d="M39.2 46.9V27.8H40.9M60.8 46.9V27.8H59.1M87.6 17.4L85 18.5"/><path class="head" d="M40.2 27.6h1.4v.9h-1.4zM58.4 27.6h1.4v.9h-1.4zM84.3 18.3h1.4v.8h-1.4z"/></svg>
<div class="glow lights" aria-hidden="true"><div class="cars"><i class="car"><b></b></i><i class="car back"><b></b></i></div></div>
<svg class="walls" viewBox="0 0 100 100" preserveAspectRatio="none" aria-hidden="true"><path d="M70 21.6L92 5.8M70 26.8L92 16.7M70 33.6L92 31M70 39.2L92 42.7M74 25L88 42.1"/><path class="bars" d="M70 21.6V26.8M72.2 20V25.8M74.4 18.4V24.8M76.6 16.8V23.8M78.8 15.3V22.8M81 13.7V21.7M83.2 12.1V20.7M85.4 10.5V19.7M87.6 8.9V18.7M89.8 7.3V17.7M92 5.8V16.7M70 33.6V39.2M72.2 33.3V39.6M74.4 33.1V39.9M76.6 32.8V40.3M78.8 32.5V40.6M81 32.3V41M83.2 32V41.3M85.4 31.8V41.7M87.6 31.5V42M89.8 31.2V42.4M92 31V42.7"/></svg>
<div class="mist" aria-hidden="true"></div>
<div class="rain" aria-hidden="true"><i style="--i:1"></i><i style="--i:2"></i><i style="--i:3"></i></div>
<div class="vignette" aria-hidden="true"></div>
</main>
@import "lib/stage";

/*
  Geometry (all in stage %): the vanishing point sits at 50% 36%.
  The far end of the alley spans x 38–62%, and its ground line is y 51.4%.
  That ground line is the mirror axis: .lights fills the stage above it,
  and .reflect paints the same box again, flipped about its bottom edge.
  The window, pole and fire-escape shapes are generated from the same vanishing point.
*/
@layer scene {
  .stage {
    /* Controls */
    --tilt: 8deg;     /* rain angle */
    --spread: 5deg;   /* extra angle between the three rain layers (gusts) */
    --density: 1;     /* rain amount */
    --speed: 1;       /* rain speed */
    --stretch: 1.6;   /* reflection stretch */
    --blur: 0.4;      /* reflection blur amount, in vmin */

    --sky: light-dark(#6f86b0, #2b3350);
    --sky-top: light-dark(#34405e, #0c0e18);
    --wall: light-dark(#3b4054, #1a1c26);
    --mortar: light-dark(#2c3041, #12131b);
    --far-wall: light-dark(#454a5f, #1e2130);
    --asphalt: #0d0e14;
    --win: light-dark(#ffd88acc, #ffd88a);
    --pink: #ff3d8b;
    --cyan: #3de0ff;
    --rain: light-dark(#dbe4f5aa, #b8c6e099);

    /* every light in the scene, as one background (box = stage above the ground line) */
    --lights:
      /* the two street lights at the intersection, and their pools on the road */
      radial-gradient(circle at 40.9% 55.8%, #fff6dc 0 0.35vmin, #ffc46bdd 0.9vmin, #ffb34755 4vmin, #ffb34714 13vmin, #0000 24vmin),
      radial-gradient(circle at 59.1% 55.8%, #fff6dc 0 0.35vmin, #ffc46bdd 0.9vmin, #ffb34755 4vmin, #ffb34714 13vmin, #0000 24vmin),
      radial-gradient(ellipse 4% 5% at 40.9% 94%, #ffb3474d, #0000),
      radial-gradient(ellipse 4% 5% at 59.1% 94%, #ffb3474d, #0000),
      /* bracket lamp on the right wall, close to us */
      radial-gradient(circle at 85% 38%, #fff3d6 0 0.6vmin, #ffb347cc 1.6vmin, #ffb34740 7vmin, #ffb3470c 20vmin, #0000 34vmin),
      /* cool lamp over the doorway on the left wall */
      radial-gradient(circle at 14.5% 46%, #e6fbff 0 0.3vmin, #3de0ffaa 0.8vmin, #3de0ff24 4vmin, #0000 11vmin),
      /* lit windows on both walls, drawn in perspective (inline SVG, generated) */
      url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 51.4' preserveAspectRatio='none'%3E%3Cpath fill='%23ffb34788' d='M20 27.6L25 29L25 50L20 52.8Z'/%3E%3Cpath stroke='%23000a' d='M22.5 28.3V51.4'/%3E%3Cpath fill='%238fc4ff99' d='M20 -4.8L25 2L25 18L20 14.4Z'/%3E%3Cpath stroke='%23000a' d='M22.5 -1.4V16.2'/%3E%3Cpath fill='%23ffd88a' d='M28.5 6.8L31.5 10.8L31.5 22.7L28.5 20.5Z'/%3E%3Cpath stroke='%23000a' d='M30 8.8V21.6'/%3E%3Cpath fill='%23ffc46b99' d='M33.5 13.6L35.5 16.3L35.5 25.6L33.5 24.1Z'/%3E%3Cpath stroke='%23000a' d='M34.5 14.9V24.8'/%3E%3Cpath fill='%23ffd88a' d='M75 2L80 -4.8L80 14.4L75 18Z'/%3E%3Cpath stroke='%23000a' d='M77.5 -1.4V16.2'/%3E%3Cpath fill='%23ffd88acc' d='M64.5 16.3L66.5 13.6L66.5 24.1L64.5 25.6Z'/%3E%3Cpath stroke='%23000a' d='M65.5 14.9V24.8'/%3E%3Cpath fill='%231c0710' d='M64.6 34.5L71.4 33.9L71.4 52.3L64.6 47.1Z'/%3E%3Cpath fill='none' stroke='%23ff3d8b' stroke-width='3' vector-effect='non-scaling-stroke' d='M65.6 36.6L70.4 36.8L70.4 48.6L65.6 45.7Z'/%3E%3C/svg%3E") 0 0 / 100% 100%,
      /* lit windows on the far building */
      linear-gradient(var(--win) 0 0) 41.6% 50% / 1.6vmin 2.6vmin no-repeat,
      linear-gradient(var(--win) 0 0) 47% 50% / 1.6vmin 2.6vmin no-repeat,
      linear-gradient(#ffd88a55 0 0) 53% 50% / 1.6vmin 2.6vmin no-repeat,
      linear-gradient(var(--win) 0 0) 58.4% 67% / 1.6vmin 2.6vmin no-repeat,
      linear-gradient(#9fd8ff88 0 0) 44.4% 67% / 1.6vmin 2.6vmin no-repeat,
      linear-gradient(var(--win) 0 0) 52.6% 79% / 1.6vmin 2.6vmin no-repeat;

    background: linear-gradient(var(--sky-top), var(--sky) 40%);
  }
  .stage *, .stage ::before, .stage ::after { position: absolute; }

  /* the building across the far end, and the street in front of it */
  .far {
    inset: 8% 38% 53.2%;
    background: linear-gradient(#0000 0 94%, #0006 0), var(--far-wall);
  }
  .street {
    inset: 46.8% 38% 48.6%;
    background:
      repeating-linear-gradient(90deg, #c9b88a40 0 3%, #0000 0 7%) 0 56% / 100% 4% no-repeat,
      linear-gradient(#2a2d38 0 18%, #11131b 0 88%, #2a2d38 0);
  }

  .ground {
    inset: 51.4% 0 0;
    background: linear-gradient(#1a1d2a, var(--asphalt) 40%);
  }

  /* wet areas: a thin sheen everywhere, puddles where it mirrors fully */
  .wet {
    inset: 0;
    clip-path: polygon(38% 51.4%, 62% 51.4%, 100% 100%, 0 100%);
    mask-image:
      radial-gradient(ellipse 22% 6% at 45% 69%, #000 45%, #0000),
      radial-gradient(ellipse 12% 3% at 57% 58%, #000 35%, #0000),
      radial-gradient(ellipse 28% 8% at 38% 90%, #000 45%, #0000),
      radial-gradient(ellipse 16% 5% at 72% 82%, #000 40%, #0000),
      linear-gradient(#000 52%, #0007 62%, #0004);
    /* the floor plane below is seen from the same eye as the walls */
    perspective: 100vmin;
    perspective-origin: 50% 36%;
  }

  /* the trick: one class, two elements. Both paint var(--lights), the same signs and the same cars */
  .glow {
    inset: 0 0 48.6%;
    background: var(--lights);
  }
  .reflect {
    transform-origin: 50% 100%;
    scale: 1 calc(var(--stretch) * -1);
    filter: blur(calc(var(--blur) * 1vmin)) saturate(1.4);
  }

  /* neon: a shape-only ring (::before) and a vertical BAR blade (::after) */
  .glow::before {
    content: "";
    inset: 50% auto auto 29%;
    inline-size: 3.6vmin;
    block-size: 5.4vmin;
    border: 0.45vmin solid #c8f6ff;
    border-radius: 50%;
    box-shadow: 0 0 0.6vmin var(--cyan), 0 0 2.4vmin var(--cyan), inset 0 0 1.2vmin var(--cyan);
  }
  .glow::after {
    content: "BAR";
    inset: 38% auto auto 64.5%;
    padding: 0.8vmin 0.5vmin;
    writing-mode: vertical-lr;
    text-orientation: upright;
    font: 700 3.4vmin/1 system-ui, sans-serif;
    letter-spacing: -0.2vmin;
    color: #ffe1ee;
    border: 0.3vmin solid #ffb3d1;
    border-radius: 0.6vmin;
    text-shadow: 0 0 0.4vmin var(--pink), 0 0 1.6vmin var(--pink), 0 0 4vmin var(--pink);
    box-shadow: 0 0 1vmin var(--pink), 0 0 4vmin #ff3d8b66, inset 0 0 1vmin #ff3d8b88;
    animation: flicker 7s steps(1) infinite;
  }
  @keyframes flicker {
    0%, 61%, 64%, 67%, 100% { opacity: 1; }
    62%, 65.5% { opacity: 0.25; }
  }

  /* cars on the far street: full-width layers moved by translate, seen only through the alley mouth */
  .cars { inset: 0; clip-path: inset(0 38%); }
  .car { /* left to right, in the near lane: lower and bigger */
    --w: 10.5%; /* body length */
    --top: 92%;
    --h: 7.6%;
    --x0: calc(37.5% - var(--w));  /* starts just hidden behind the left wall */
    --dx: calc(25% + var(--w));    /* and ends just hidden behind the right one */
    inset: 0;
    animation: drive 12s infinite;
  }
  .car::before, .car::after, .car b { inset-block-start: var(--top); block-size: var(--h); }
  .car::before { /* body */
    content: "";
    inset-inline-start: var(--x0);
    inline-size: var(--w);
    background: linear-gradient(#3a3f4e, #05060a 55%);
    clip-path: polygon(0 55%, 6% 38%, 24% 32%, 36% 0, 66% 0, 80% 32%, 97% 40%, 100% 58%, 100% 92%, 0 92%);
  }
  .car::after { /* tail light, headlamp, and the beam on the wet road */
    content: "";
    inset-inline-start: calc(var(--x0) - var(--w) * 0.04);
    inline-size: calc(var(--w) * 2.6);
    background:
      radial-gradient(circle at 2.4% 58%, #ff4a3a 0 0.35vmin, #ff2a2a55 1.2vmin, #0000 2.4vmin),
      radial-gradient(circle at 39.6% 55%, #fffbe8 0 0.45vmin, #ffe8b0aa 1.2vmin, #0000 2.8vmin),
      radial-gradient(ellipse 32% 60% at 62% 82%, #fff1c440, #0000);
  }
  .car b { /* brake light */
    inset-inline-start: calc(var(--x0) - var(--w) * 0.12);
    inline-size: 5vmin;
    translate: 0 -30%;
    background: radial-gradient(closest-side, #ff3b30 0 18%, #ff1e1e4d 50%, #0000);
    opacity: 0;
    animation: brake 12s infinite;
  }
  .car.back { /* right to left, in the far lane: mirrored, higher, smaller, slower, later, and behind the near lane */
    --w: 9%;
    --top: 90.1%;
    --h: 6.4%;
    z-index: -1;
    --dx: calc(-25% - var(--w));
    scale: -1 1;
    animation-duration: 15s;
    animation-delay: -7s;
  }
  .back b { animation-duration: 15s; animation-delay: -7s; }
  @keyframes drive {
    0% { translate: 0; animation-timing-function: cubic-bezier(0.2, 0.6, 0.8, 0.4); } /* slows at the alley */
    34%, 100% { translate: var(--dx); }
  }
  @keyframes brake {
    0%, 12%, 23%, 100% { opacity: 0; }
    13%, 22% { opacity: 1; }
  }

  /* the wet floor as a real plane in perspective: broken streaks that flow toward us over the reflection */
  .floor {
    inset: 51.4% 38% auto;
    block-size: 80vmin;
    transform-origin: 50% 0;
    rotate: x 90deg;
    overflow: clip;
  }
  .floor i {
    --tile: 6vmin;
    inset: calc(var(--tile) * -1) -100% 0;
    background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 60 30' fill='none' stroke-linecap='round'%3E%3Cpath stroke='%230a0b1199' stroke-width='0.5' d='M34.2 1.2q1.3 -0.1 2.5 0t2.5 0M35.3 7.4q1.4 -0.1 2.7 0t2.7 0M16.1 11.1q1.3 -0.3 2.6 0t2.6 0M5.9 19.0q2.3 -0.5 4.6 0t4.6 0M16.7 19.0q1.5 -0.5 2.9 0t2.9 0M1.0 23.0q1.1 0.5 2.1 0t2.1 0M31.8 23.0q3.1 -0.3 6.2 0t6.2 0M26.1 26.0q1.6 -0.0 3.2 0t3.2 0M17.5 29.3q2.4 0.6 4.9 0t4.9 0'/%3E%3Cpath stroke='%230a0b1199' stroke-width='0.8' d='M0.9 1.2q3.0 -0.5 5.9 0t5.9 0M48.3 5.3q2.5 0.0 5.0 0t5.0 0M1.9 11.1q3.1 0.1 6.2 0t6.2 0M23.1 11.1q3.1 0.2 6.2 0t6.2 0M1.3 13.5q1.9 0.3 3.7 0t3.7 0M10.7 13.5q2.3 0.1 4.7 0t4.7 0M26.3 13.5q1.8 -0.1 3.7 0t3.7 0M1.7 16.0q1.4 0.0 2.9 0t2.9 0M24.4 19.0q1.6 -0.1 3.2 0t3.2 0M35.7 19.0q2.4 -0.5 4.8 0t4.8 0M38.3 26.0q2.4 -0.4 4.8 0t4.8 0'/%3E%3Cpath stroke='%230a0b1199' stroke-width='1.2' d='M18.5 7.4q2.5 -0.2 5.0 0t5.0 0M14.2 16.0q3.1 0.0 6.1 0t6.1 0M32.0 16.0q2.4 0.4 4.7 0t4.7 0M49.5 19.0q1.9 -0.4 3.9 0t3.9 0M10.5 23.0q3.7 0.3 7.5 0t7.5 0M47.6 23.0q1.7 0.0 3.3 0t3.3 0M5.9 26.0q3.6 0.4 7.1 0t7.1 0'/%3E%3Cpath stroke='%23c8d6f022' stroke-width='0.5' d='M0.3 5.3q3.6 -0.3 7.2 0t7.2 0M35.0 5.3q2.1 0.1 4.2 0t4.2 0M40.5 13.5q1.5 -0.4 2.9 0t2.9 0M5.7 29.3q2.1 -0.3 4.2 0t4.2 0M45.4 29.3q2.2 0.3 4.3 0t4.3 0'/%3E%3Cpath stroke='%23c8d6f022' stroke-width='0.8' d='M17.4 1.2q3.7 -0.3 7.5 0t7.5 0M38.5 11.1q2.2 0.2 4.3 0t4.3 0'/%3E%3Cpath stroke='%23c8d6f022' stroke-width='1.2' d='M46.0 1.2q2.9 0.1 5.8 0t5.8 0M19.2 5.3q2.7 0.2 5.4 0t5.4 0M1.8 7.4q3.4 0.2 6.8 0t6.8 0M47.4 7.4q2.3 0.6 4.5 0t4.5 0M47.4 16.0q2.2 -0.1 4.4 0t4.4 0M31.4 29.3q3.0 0.4 5.9 0t5.9 0'/%3E%3C/svg%3E") 0 0 / 12vmin var(--tile);
    animation: flow 2.4s linear infinite;
  }
  @keyframes flow { to { translate: 0 var(--tile); } }

  /* brick walls; mortar courses radiate from the vanishing point */
  .alley { inset: 0; --course: 1.1deg; }
  /* narrow screens squeeze the courses together; coarser ones avoid moiré swirls */
  @media (max-width: 600px) { .alley { --course: 2.2deg; } }
  .alley::before, .alley::after {
    content: "";
    inset: 0;
    background:
      linear-gradient(var(--dir), #000a, #0000 30%),
      repeating-conic-gradient(from 0deg at 50% 36%, var(--wall) 0 var(--course), var(--mortar) 0 calc(var(--course) + 0.2deg));
  }
  .alley::before {
    --dir: 90deg;
    clip-path: polygon(0 0, 26.3% 0, 38% 17.8%, 38% 51.4%, 0 100%);
  }
  .alley::after {
    --dir: -90deg;
    clip-path: polygon(100% 0, 73.7% 0, 62% 17.8%, 62% 51.4%, 100% 100%);
  }

  /* a doorway on the left wall, lit from inside */
  .alley i {
    inset: 0;
    background:
      linear-gradient(90deg, #0000 0 18%, #3de0ff30 50%, #0000 82%),
      linear-gradient(90deg, #070b10, #10262f);
    clip-path: polygon(12% 26.9%, 17% 28.1%, 17% 77.8%, 12% 84.6%);
  }

  /* dark windows, light poles and (in a second SVG, in front of the lit windows) the fire escape: inline SVG in stage coordinates (viewBox 0 0 100 100, stretched) */
  .walls {
    inset: 0;
    inline-size: 100%;
    block-size: 100%;
    fill: none;
    stroke: #06070b;
    stroke-width: 2px;
  }
  .walls path { vector-effect: non-scaling-stroke; }
  .walls .pane { fill: #06070bd9; stroke: #2a2e3c; stroke-width: 1px; }
  .walls .frame, .walls .bars { stroke-width: 1px; }
  .walls .frame { stroke: #2a2e3c; }
  .walls .pole { stroke: #0b0c12; stroke-width: 3px; }
  .walls .head { fill: #0b0c12; stroke: none; }

  .mist {
    inset: 0;
    background:
      radial-gradient(ellipse 26% 16% at 50% 50%, light-dark(#b8c6e026, #8090b01c), #0000),
      radial-gradient(ellipse 22% 30% at 85% 20%, #ffb34714, #0000);
  }

  /* rain: layers at their own angles and depths, all from one rule. --i is the layer's depth (1 = far);
     each layer is a tile of thin ellipse streaks moved down exactly one tile per loop */
  .rain { inset: 0; }
  .rain i {
    --z: min(var(--i), 4);
    inset: -60%; /* overscan, so a tilted layer still covers a portrait phone */
    rotate: calc(var(--tilt) + (var(--z) - 2) * var(--spread));
  }
  @supports (order: sibling-index()) {
    .rain i { --i: sibling-index(); }
  }
  .rain i::before {
    --h: calc(14vmin + var(--z) * 9vmin);
    --w: calc((2vmin + var(--z) * 4vmin) / var(--density));
    --streak: calc(0.05vmin + var(--z) * 0.025vmin) calc(0.4vmin + var(--z) * 0.75vmin);
    content: "";
    inset: calc(var(--h) * -1) 0 0;
    opacity: min(1, 0.3 + var(--z) * 0.22);
    background:
      radial-gradient(var(--streak) at 25% 30%, var(--rain), #0000) calc(var(--i) * 1.7vmin) calc(var(--i) * 5vmin) / var(--w) var(--h),
      radial-gradient(var(--streak) at 70% 75%, var(--rain), #0000) calc(var(--i) * 3.1vmin) calc(var(--i) * 7.3vmin) / calc(var(--w) * 1.6) var(--h);
    /* layers past the fourth share a depth, so a small speed step keeps them from falling in lockstep */
    animation: fall calc((1.2s - var(--z) * 0.19s) * (1 + (var(--i) - var(--z)) * 0.07) / var(--speed)) linear infinite;
  }
  @keyframes fall { to { translate: 0 var(--h); } }

  /* ripples: rings scattered over the ground by sin()/cos() of their index */
  .ripples { inset: 0; }
  .ripples i {
    --r: var(--i);
    left: calc(50% + sin(var(--r) * 2.4rad) * 17%);
    top: calc(73% + cos(var(--r) * 1.9rad) * 11%);
    inline-size: calc(6vmin + cos(var(--r) * 1.9rad) * 2.4vmin);
    aspect-ratio: 4;
    translate: -50% -50%;
    border: 0.18vmin solid #c8d6f08c;
    border-radius: 50%;
    opacity: 0;
    animation: ripple 1.6s ease-out infinite;
    animation-delay: calc(var(--r) * -0.53s);
  }
  @supports (order: sibling-index()) {
    .ripples i { --r: sibling-index(); }
  }
  @keyframes ripple {
    from { scale: 0.15; opacity: 0.8; }
    to { scale: 1; opacity: 0; }
  }

  /* Performance switches. Each Controls switch adds or removes a data- attribute on .stage (all present in the
     markup, so everything starts on). Off means display: none or filter: none, so the effect costs nothing at all,
     not a zero value that still allocates a layer or a filter pass. */
  .stage:not([data-reflection]) .reflect { display: none; }
  .stage:not([data-blur]) .reflect { filter: none; }
  .stage:not([data-masks]) .wet { mask-image: none; }
  .stage:not([data-water]) .floor { display: none; }
  .stage:not([data-bricks]) .alley::before, .stage:not([data-bricks]) .alley::after { background: var(--wall); }
  .stage:not([data-glow]) .glow::before, .stage:not([data-glow]) .glow::after { box-shadow: none; text-shadow: none; }

  .vignette {
    inset: 0;
    pointer-events: none;
    background: radial-gradient(ellipse 75% 70% at 50% 45%, #0000 55%, #000a);
  }

  @media (prefers-reduced-motion: reduce) {
    .rain i::before, .floor i, .glow::after { animation: none; }
    .ripples i { animation: none; scale: 0.6; opacity: 0.35; }
    /* one car stopped at the alley mouth with its brake light on; the other lane is empty */
    .car, .car b { animation: none; }
    .car { translate: 17%; }
    .car b { opacity: 1; }
    .car.back { translate: var(--dx); }
  }
}

What ships: tokens and lib snippets inlined, lowered for the browser targets (minified in the real page).

@property --c-ink {
  syntax: "<color>";
  inherits: true;
  initial-value: #14121f;
}

@property --c-paper {
  syntax: "<color>";
  inherits: true;
  initial-value: #f4f1ff;
}

@property --c-accent {
  syntax: "<color>";
  inherits: true;
  initial-value: #6d4aff;
}

@property --c-car-wall {
  syntax: "<color>";
  inherits: true;
  initial-value: #e9e6dc;
}

@property --c-seat {
  syntax: "<color>";
  inherits: true;
  initial-value: #c9c4b4;
}

@property --c-line {
  syntax: "<color>";
  inherits: true;
  initial-value: #ff9500;
}

@property --c-tunnel {
  syntax: "<color>";
  inherits: true;
  initial-value: #05050a;
}

@property --c-lamp {
  syntax: "<color>";
  inherits: true;
  initial-value: #ffe9b0;
}

@property --c-platform {
  syntax: "<color>";
  inherits: true;
  initial-value: #dfe9ff;
}

@property --a-spin {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}

@layer tokens {
  :root {
    color-scheme: light dark;
    --c-ink: light-dark(#14121f, #f4f1ff);
    --c-paper: light-dark(#f4f1ff, #0b0a14);
    --c-accent: light-dark(#6d4aff, #9d85ff);
    --c-car-wall: light-dark(#e9e6dc, #1a1826);
    --c-seat: light-dark(#c9c4b4, #2a2638);
    --c-line: light-dark(#ff9500, #ff9f1a);
    --c-tunnel: #05050a;
    --c-lamp: #ffe9b0;
    --c-platform: light-dark(#dfe9ff, #9fb4e8);
    --t-fast: .15s;
    --t-base: .4s;
    --t-slow: 1.2s;
    --t-drift: 24s;
    --t-rush: 1.6s;
    --t-swing: 3.6s;
    --ease-out: cubic-bezier(.22, 1, .36, 1);
    --ease-spring: linear(0, .22 2.1%, .73 6.5%, 1.04 10.7%, 1.09 13%, 1.02 20%, .99 27%, 1);
  }
}

@layer reset {
  *, :before, :after {
    box-sizing: border-box;
    margin: 0;
  }

  html {
    block-size: 100%;
  }
}

@layer base {
  body {
    background: var(--c-paper);
    min-block-size: 100dvh;
    color: var(--c-ink);
    font: 1rem / 1.5 system-ui, sans-serif;
    overflow: clip;
  }

  .stage {
    contain: strict;
    position: fixed;
    inset: 0;
    overflow: clip;
  }
}

@layer scene {
  .stage {
    --tilt: 8deg;
    --spread: 5deg;
    --density: 1;
    --speed: 1;
    --stretch: 1.6;
    --blur: .4;
    --sky: light-dark(#6f86b0, #2b3350);
    --sky-top: light-dark(#34405e, #0c0e18);
    --wall: light-dark(#3b4054, #1a1c26);
    --mortar: light-dark(#2c3041, #12131b);
    --far-wall: light-dark(#454a5f, #1e2130);
    --asphalt: #0d0e14;
    --win: light-dark(#ffd88acc, #ffd88a);
    --pink: #ff3d8b;
    --cyan: #3de0ff;
    --rain: light-dark(#dbe4f5aa, #b8c6e099);
    --lights: /* the two street lights at the intersection, and their pools on the road */
      radial-gradient(circle at 40.9% 55.8%, #fff6dc 0 .35vmin, #ffc46bdd .9vmin, #ffb34755 4vmin, #ffb34714 13vmin, #0000 24vmin),
      radial-gradient(circle at 59.1% 55.8%, #fff6dc 0 .35vmin, #ffc46bdd .9vmin, #ffb34755 4vmin, #ffb34714 13vmin, #0000 24vmin),
      radial-gradient(ellipse 4% 5% at 40.9% 94%, #ffb3474d, #0000),
      radial-gradient(ellipse 4% 5% at 59.1% 94%, #ffb3474d, #0000),
      /* bracket lamp on the right wall, close to us */
      radial-gradient(circle at 85% 38%, #fff3d6 0 .6vmin, #ffb347cc 1.6vmin, #ffb34740 7vmin, #ffb3470c 20vmin, #0000 34vmin),
      /* cool lamp over the doorway on the left wall */
      radial-gradient(circle at 14.5% 46%, #e6fbff 0 .3vmin, #3de0ffaa .8vmin, #3de0ff24 4vmin, #0000 11vmin),
      /* lit windows on both walls, drawn in perspective (inline SVG, generated) */
      url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 51.4' preserveAspectRatio='none'%3E%3Cpath fill='%23ffb34788' d='M20 27.6L25 29L25 50L20 52.8Z'/%3E%3Cpath stroke='%23000a' d='M22.5 28.3V51.4'/%3E%3Cpath fill='%238fc4ff99' d='M20 -4.8L25 2L25 18L20 14.4Z'/%3E%3Cpath stroke='%23000a' d='M22.5 -1.4V16.2'/%3E%3Cpath fill='%23ffd88a' d='M28.5 6.8L31.5 10.8L31.5 22.7L28.5 20.5Z'/%3E%3Cpath stroke='%23000a' d='M30 8.8V21.6'/%3E%3Cpath fill='%23ffc46b99' d='M33.5 13.6L35.5 16.3L35.5 25.6L33.5 24.1Z'/%3E%3Cpath stroke='%23000a' d='M34.5 14.9V24.8'/%3E%3Cpath fill='%23ffd88a' d='M75 2L80 -4.8L80 14.4L75 18Z'/%3E%3Cpath stroke='%23000a' d='M77.5 -1.4V16.2'/%3E%3Cpath fill='%23ffd88acc' d='M64.5 16.3L66.5 13.6L66.5 24.1L64.5 25.6Z'/%3E%3Cpath stroke='%23000a' d='M65.5 14.9V24.8'/%3E%3Cpath fill='%231c0710' d='M64.6 34.5L71.4 33.9L71.4 52.3L64.6 47.1Z'/%3E%3Cpath fill='none' stroke='%23ff3d8b' stroke-width='3' vector-effect='non-scaling-stroke' d='M65.6 36.6L70.4 36.8L70.4 48.6L65.6 45.7Z'/%3E%3C/svg%3E") 0 0 / 100% 100%,
      /* lit windows on the far building */
      linear-gradient(var(--win) 0 0) 41.6% 50% / 1.6vmin 2.6vmin no-repeat,
      linear-gradient(var(--win) 0 0) 47% 50% / 1.6vmin 2.6vmin no-repeat,
      linear-gradient(#ffd88a55 0 0) 53% 50% / 1.6vmin 2.6vmin no-repeat,
      linear-gradient(var(--win) 0 0) 58.4% 67% / 1.6vmin 2.6vmin no-repeat,
      linear-gradient(#9fd8ff88 0 0) 44.4% 67% / 1.6vmin 2.6vmin no-repeat,
      linear-gradient(var(--win) 0 0) 52.6% 79% / 1.6vmin 2.6vmin no-repeat;
    background: linear-gradient(var(--sky-top), var(--sky) 40%);
  }

  .stage *, .stage :before, .stage :after {
    position: absolute;
  }

  .far {
    background: linear-gradient(#0000 0 94%, #0006 0), var(--far-wall);
    inset: 8% 38% 53.2%;
  }

  .street {
    background: repeating-linear-gradient(90deg, #c9b88a40 0 3%, #0000 0 7%) 0 56% / 100% 4% no-repeat, linear-gradient(#2a2d38 0 18%, #11131b 0 88%, #2a2d38 0);
    inset: 46.8% 38% 48.6%;
  }

  .ground {
    background: linear-gradient(#1a1d2a, var(--asphalt) 40%);
    inset: 51.4% 0 0;
  }

  .wet {
    clip-path: polygon(38% 51.4%, 62% 51.4%, 100% 100%, 0 100%);
    perspective: 100vmin;
    perspective-origin: 50% 36%;
    inset: 0;
    mask-image: radial-gradient(22% 6% at 45% 69%, #000 45%, #0000), radial-gradient(12% 3% at 57% 58%, #000 35%, #0000), radial-gradient(28% 8% at 38% 90%, #000 45%, #0000), radial-gradient(16% 5% at 72% 82%, #000 40%, #0000), linear-gradient(#000 52%, #0007 62%, #0004);
  }

  .glow {
    background: var(--lights);
    inset: 0 0 48.6%;
  }

  .reflect {
    transform-origin: 50% 100%;
    scale: 1 calc(var(--stretch) * -1);
    filter: blur(calc(var(--blur) * 1vmin)) saturate(1.4);
  }

  .glow:before {
    content: "";
    block-size: 5.4vmin;
    inline-size: 3.6vmin;
    box-shadow: 0 0 .6vmin var(--cyan), 0 0 2.4vmin var(--cyan), inset 0 0 1.2vmin var(--cyan);
    border: .45vmin solid #c8f6ff;
    border-radius: 50%;
    inset: 50% auto auto 29%;
  }

  .glow:after {
    content: "BAR";
    writing-mode: vertical-lr;
    text-orientation: upright;
    letter-spacing: -.2vmin;
    color: #ffe1ee;
    text-shadow: 0 0 .4vmin var(--pink), 0 0 1.6vmin var(--pink), 0 0 4vmin var(--pink);
    box-shadow: 0 0 1vmin var(--pink), 0 0 4vmin #ff3d8b66, inset 0 0 1vmin #ff3d8b88;
    border: .3vmin solid #ffb3d1;
    border-radius: .6vmin;
    padding: .8vmin .5vmin;
    font: 700 3.4vmin / 1 system-ui, sans-serif;
    animation: 7s step-end infinite flicker;
    inset: 38% auto auto 64.5%;
  }

  @keyframes flicker {
    0%, 61%, 64%, 67%, 100% {
      opacity: 1;
    }

    62%, 65.5% {
      opacity: .25;
    }
  }

  .cars {
    clip-path: inset(0 38%);
    inset: 0;
  }

  .car {
    --w: 10.5%;
    --top: 92%;
    --h: 7.6%;
    --x0: calc(37.5% - var(--w));
    --dx: calc(25% + var(--w));
    animation: 12s infinite drive;
    inset: 0;
  }

  .car:before, .car:after, .car b {
    block-size: var(--h);
    inset-block-start: var(--top);
  }

  .car:before {
    content: "";
    inline-size: var(--w);
    clip-path: polygon(0 55%, 6% 38%, 24% 32%, 36% 0, 66% 0, 80% 32%, 97% 40%, 100% 58%, 100% 92%, 0 92%);
    background: linear-gradient(#3a3f4e, #05060a 55%);
    inset-inline-start: var(--x0);
  }

  .car:after {
    content: "";
    inline-size: calc(var(--w) * 2.6);
    background: radial-gradient(circle at 2.4% 58%, #ff4a3a 0 .35vmin, #ff2a2a55 1.2vmin, #0000 2.4vmin), radial-gradient(circle at 39.6% 55%, #fffbe8 0 .45vmin, #ffe8b0aa 1.2vmin, #0000 2.8vmin), radial-gradient(32% 60% at 62% 82%, #fff1c440, #0000);
    inset-inline-start: calc(var(--x0) - var(--w) * .04);
  }

  .car b {
    opacity: 0;
    background: radial-gradient(closest-side, #ff3b30 0 18%, #ff1e1e4d 50%, #0000);
    inline-size: 5vmin;
    animation: 12s infinite brake;
    inset-inline-start: calc(var(--x0) - var(--w) * .12);
    translate: 0 -30%;
  }

  .car.back {
    --w: 9%;
    --top: 90.1%;
    --h: 6.4%;
    z-index: -1;
    --dx: calc(-25% - var(--w));
    animation-duration: 15s;
    animation-delay: -7s;
    scale: -1 1;
  }

  .back b {
    animation-duration: 15s;
    animation-delay: -7s;
  }

  @keyframes drive {
    0% {
      animation-timing-function: cubic-bezier(.2, .6, .8, .4);
      translate: 0;
    }

    34%, 100% {
      translate: var(--dx);
    }
  }

  @keyframes brake {
    0%, 12%, 23%, 100% {
      opacity: 0;
    }

    13%, 22% {
      opacity: 1;
    }
  }

  .floor {
    transform-origin: 50% 0;
    block-size: 80vmin;
    inset: 51.4% 38% auto;
    overflow: clip;
    rotate: x 90deg;
  }

  .floor i {
    --tile: 6vmin;
    inset: calc(var(--tile) * -1) -100% 0;
    background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 60 30' fill='none' stroke-linecap='round'%3E%3Cpath stroke='%230a0b1199' stroke-width='0.5' d='M34.2 1.2q1.3 -0.1 2.5 0t2.5 0M35.3 7.4q1.4 -0.1 2.7 0t2.7 0M16.1 11.1q1.3 -0.3 2.6 0t2.6 0M5.9 19.0q2.3 -0.5 4.6 0t4.6 0M16.7 19.0q1.5 -0.5 2.9 0t2.9 0M1.0 23.0q1.1 0.5 2.1 0t2.1 0M31.8 23.0q3.1 -0.3 6.2 0t6.2 0M26.1 26.0q1.6 -0.0 3.2 0t3.2 0M17.5 29.3q2.4 0.6 4.9 0t4.9 0'/%3E%3Cpath stroke='%230a0b1199' stroke-width='0.8' d='M0.9 1.2q3.0 -0.5 5.9 0t5.9 0M48.3 5.3q2.5 0.0 5.0 0t5.0 0M1.9 11.1q3.1 0.1 6.2 0t6.2 0M23.1 11.1q3.1 0.2 6.2 0t6.2 0M1.3 13.5q1.9 0.3 3.7 0t3.7 0M10.7 13.5q2.3 0.1 4.7 0t4.7 0M26.3 13.5q1.8 -0.1 3.7 0t3.7 0M1.7 16.0q1.4 0.0 2.9 0t2.9 0M24.4 19.0q1.6 -0.1 3.2 0t3.2 0M35.7 19.0q2.4 -0.5 4.8 0t4.8 0M38.3 26.0q2.4 -0.4 4.8 0t4.8 0'/%3E%3Cpath stroke='%230a0b1199' stroke-width='1.2' d='M18.5 7.4q2.5 -0.2 5.0 0t5.0 0M14.2 16.0q3.1 0.0 6.1 0t6.1 0M32.0 16.0q2.4 0.4 4.7 0t4.7 0M49.5 19.0q1.9 -0.4 3.9 0t3.9 0M10.5 23.0q3.7 0.3 7.5 0t7.5 0M47.6 23.0q1.7 0.0 3.3 0t3.3 0M5.9 26.0q3.6 0.4 7.1 0t7.1 0'/%3E%3Cpath stroke='%23c8d6f022' stroke-width='0.5' d='M0.3 5.3q3.6 -0.3 7.2 0t7.2 0M35.0 5.3q2.1 0.1 4.2 0t4.2 0M40.5 13.5q1.5 -0.4 2.9 0t2.9 0M5.7 29.3q2.1 -0.3 4.2 0t4.2 0M45.4 29.3q2.2 0.3 4.3 0t4.3 0'/%3E%3Cpath stroke='%23c8d6f022' stroke-width='0.8' d='M17.4 1.2q3.7 -0.3 7.5 0t7.5 0M38.5 11.1q2.2 0.2 4.3 0t4.3 0'/%3E%3Cpath stroke='%23c8d6f022' stroke-width='1.2' d='M46.0 1.2q2.9 0.1 5.8 0t5.8 0M19.2 5.3q2.7 0.2 5.4 0t5.4 0M1.8 7.4q3.4 0.2 6.8 0t6.8 0M47.4 7.4q2.3 0.6 4.5 0t4.5 0M47.4 16.0q2.2 -0.1 4.4 0t4.4 0M31.4 29.3q3.0 0.4 5.9 0t5.9 0'/%3E%3C/svg%3E") 0 0 / 12vmin var(--tile);
    animation: 2.4s linear infinite flow;
  }

  @keyframes flow {
    to {
      translate: 0 var(--tile);
    }
  }

  .alley {
    --course: 1.1deg;
    inset: 0;
  }

  @media (width <= 600px) {
    .alley {
      --course: 2.2deg;
    }
  }

  .alley:before, .alley:after {
    content: "";
    background: linear-gradient(var(--dir), #000a, #0000 30%),
      repeating-conic-gradient(from 0deg at 50% 36%, var(--wall) 0 var(--course), var(--mortar) 0 calc(var(--course) + .2deg));
    inset: 0;
  }

  .alley:before {
    --dir: 90deg;
    clip-path: polygon(0 0, 26.3% 0, 38% 17.8%, 38% 51.4%, 0 100%);
  }

  .alley:after {
    --dir: -90deg;
    clip-path: polygon(100% 0, 73.7% 0, 62% 17.8%, 62% 51.4%, 100% 100%);
  }

  .alley i {
    clip-path: polygon(12% 26.9%, 17% 28.1%, 17% 77.8%, 12% 84.6%);
    background: linear-gradient(90deg, #0000 0 18%, #3de0ff30 50%, #0000 82%), linear-gradient(90deg, #070b10, #10262f);
    inset: 0;
  }

  .walls {
    fill: none;
    stroke: #06070b;
    stroke-width: 2px;
    block-size: 100%;
    inline-size: 100%;
    inset: 0;
  }

  .walls path {
    vector-effect: non-scaling-stroke;
  }

  .walls .pane {
    fill: #06070bd9;
    stroke: #2a2e3c;
    stroke-width: 1px;
  }

  .walls .frame, .walls .bars {
    stroke-width: 1px;
  }

  .walls .frame {
    stroke: #2a2e3c;
  }

  .walls .pole {
    stroke: #0b0c12;
    stroke-width: 3px;
  }

  .walls .head {
    fill: #0b0c12;
    stroke: none;
  }

  .mist {
    background: radial-gradient(26% 16%, light-dark(#b8c6e026, #8090b01c), #0000), radial-gradient(22% 30% at 85% 20%, #ffb34714, #0000);
    inset: 0;
  }

  .rain {
    inset: 0;
  }

  .rain i {
    --z: min(var(--i), 4);
    rotate: calc(var(--tilt) + (var(--z) - 2) * var(--spread));
    inset: -60%;
  }

  @supports (order: sibling-index()) {
    .rain i {
      --i: sibling-index();
    }
  }

  .rain i:before {
    --h: calc(14vmin + var(--z) * 9vmin);
    --w: calc((2vmin + var(--z) * 4vmin) / var(--density));
    --streak: calc(.05vmin + var(--z) * .025vmin) calc(.4vmin + var(--z) * .75vmin);
    content: "";
    inset: calc(var(--h) * -1) 0 0;
    opacity: min(1, .3 + var(--z) * .22);
    background: radial-gradient(var(--streak) at 25% 30%, var(--rain), #0000) calc(var(--i) * 1.7vmin) calc(var(--i) * 5vmin) / var(--w) var(--h),
      radial-gradient(var(--streak) at 70% 75%, var(--rain), #0000) calc(var(--i) * 3.1vmin) calc(var(--i) * 7.3vmin) / calc(var(--w) * 1.6) var(--h);
    animation: fall calc((1.2s - var(--z) * .19s) * (1 + (var(--i) - var(--z)) * .07) / var(--speed)) linear infinite;
  }

  @keyframes fall {
    to {
      translate: 0 var(--h);
    }
  }

  .ripples {
    inset: 0;
  }

  .ripples i {
    --r: var(--i);
    left: calc(50% + sin(var(--r) * 137.51deg) * 17%);
    top: calc(73% + cos(var(--r) * 108.862deg) * 11%);
    inline-size: calc(6vmin + cos(var(--r) * 108.862deg) * 2.4vmin);
    aspect-ratio: 4;
    opacity: 0;
    animation: 1.6s ease-out infinite ripple;
    animation-delay: calc(var(--r) * -.53s);
    border: .18vmin solid #c8d6f08c;
    border-radius: 50%;
    translate: -50% -50%;
  }

  @supports (order: sibling-index()) {
    .ripples i {
      --r: sibling-index();
    }
  }

  @keyframes ripple {
    from {
      opacity: .8;
      scale: .15;
    }

    to {
      opacity: 0;
      scale: 1;
    }
  }

  .stage:not([data-reflection]) .reflect {
    display: none;
  }

  .stage:not([data-blur]) .reflect {
    filter: none;
  }

  .stage:not([data-masks]) .wet {
    mask-image: none;
  }

  .stage:not([data-water]) .floor {
    display: none;
  }

  .stage:not([data-bricks]) .alley:before, .stage:not([data-bricks]) .alley:after {
    background: var(--wall);
  }

  .stage:not([data-glow]) .glow:before, .stage:not([data-glow]) .glow:after {
    box-shadow: none;
    text-shadow: none;
  }

  .vignette {
    pointer-events: none;
    background: radial-gradient(75% 70% at 50% 45%, #0000 55%, #000a);
    inset: 0;
  }

  @media (prefers-reduced-motion: reduce) {
    .rain i:before, .floor i, .glow:after {
      animation: none;
    }

    .ripples i {
      opacity: .35;
      animation: none;
      scale: .6;
    }

    .car, .car b {
      animation: none;
    }

    .car {
      translate: 17%;
    }

    .car b {
      opacity: 1;
    }

    .car.back {
      translate: var(--dx);
    }
  }
}

@layer utilities;