← back out into the rain Internal — do not file under “solved”

Case No. 110 — opened by the Bureau, against the Bureau

The Website That Investigated Itself

Somebody built us a website. One page, one dying neon sign, rain that never files a complaint. We did what we always do with a suspicious object: took it apart and wrote down how it works. These are the case notes.

§1The Concept

The Midnight Bureau is a fictional two-person detective agency in a city where it never stops raining. The site draws from film noir — the 1940s/50s school of hard shadows, venetian-blind light, wet asphalt, and one neon sign doing all the emotional labor. The palette is committed: near-black #0c0d10, a range of smoke grays, aged manila paper, and exactly one accent — the red #e5484d of the agency's sign. Typography carries the fiction: Special Elite (a typewriter face) for every document, label, and rate card; Oswald (a condensed sans) for the display work, like a movie title card.

The organizing idea: information in this city must be earned. So the case-files section literally keeps its documents in the dark until you bring a light.

§2The Techniques

Exhibit A — the flashlight mask

The case files sit under a .shade element: an absolutely-positioned overlay whose background is a radial gradient that is transparent at the center and 98% opaque near-black at the edge. The gradient's center and radius are CSS custom properties, updated every frame from JavaScript. The pointer position is eased toward with a lerp inside requestAnimationFrame, so the beam has the lag and weight of a real hand-held torch. A second .beam layer in mix-blend-mode: screen adds a warm tungsten tint.

Exhibit A — shade layer, CSS
.shade {
  background: radial-gradient(
    circle calc(var(--fr, 0px) * 2.3)
    at var(--fx, -600px) var(--fy, -600px),
    rgba(6,7,10, 0)     0%,
    rgba(6,7,10, .42)  46%,
    rgba(6,7,10, .982) 76%);
}
Exhibit A2 — the lerp, JS
bx += (tx - bx) * 0.16;          // ease toward pointer
beamR += (targetR - beamR) * 0.12; // beam swells on entry
casesEl.style.setProperty('--fx', (bx - rect.left) + 'px');
casesEl.style.setProperty('--fy', (by - rect.top) + 'px');
casesEl.style.setProperty('--fr', beamR + 'px');

Exhibit B — per-word de-redaction

Certain phrases are wrapped in .redact spans — black bars with color: transparent. Each animation frame, the script measures every bar with getBoundingClientRect(), finds the nearest point of that rectangle to the beam center, and if the distance falls inside the beam's core it toggles a .lit class that turns the bar translucent red and the text legible. Leave, and it seals itself again. It reads like clearance-level magic; it's a distance check. Try it: hover this bar.

Exhibit B — nearest-point distance, JS
var wr = word.getBoundingClientRect();
var nx = Math.max(wr.left, Math.min(bx, wr.right));
var ny = Math.max(wr.top,  Math.min(by, wr.bottom));
var dx = bx - nx, dy = by - ny;
word.classList.toggle('lit',
  dx*dx + dy*dy < core * core);

Accessibility is not optional in this office: an evidence lamp button lights the whole section (and prefers-reduced-motion defaults to lit), the full text is always present in the DOM for screen readers, and touch users press-and-hold to carry the light.

Exhibit C — the rain system

A fixed, DPR-aware <canvas> behind the page draws three layers of rain — far, middle, near — each with its own speed, streak length, opacity, and wind drift, so the storm has parallax depth. Streaks are simple line segments angled along their velocity vector. Every 9–25 seconds, lightning: a two-frame white fill on the canvas plus a short-lived body.flash class that hardens the venetian-blind shadows and over-drives the neon glow. The loop pauses on visibilitychange; reduced motion gets a static misty backdrop instead.

Exhibit C — three depths, JS
var LAYERS = [
  { density:.085, speed:1550, len:24, alpha:.32, drift:-190 },
  { density:.06,  speed:1050, len:15, alpha:.19, drift:-130 },
  { density:.05,  speed:680,  len:9,  alpha:.10, drift:-85  }
];
// per streak: lineTo(x + drift/speed * len, y - len)

Exhibit D — the sign, the blinds, the grain

The neon sign is pure CSS: layered red text-shadow glow, a per-letter neonOn keyframe animation with hand-staggered delays (triggered by IntersectionObserver), and one letter — the R in BUREAU — assigned a second, infinite dying animation so it never quite holds its charge. The wet-street strip below is the same text flipped with scaleY(-1), blurred, masked, and given a slow shimmer. Venetian blinds are one repeating-linear-gradient of near-horizontal slat shadows laid across sections at 174°; film grain is an SVG feTurbulence data URI stepped through six positions.

Exhibit D — venetian blinds, CSS
.blinds::after {
  background: repeating-linear-gradient(174deg,
    rgba(0,0,0,0)               0 54px,
    rgba(0,0,0,var(--blind-a)) 54px 86px);
}
body.flash { --blind-a: 0.5; } /* lightning hardens them */

§3How It Was Made

Built by Claude (Fable 5) writing vanilla HTML, CSS, and JavaScript by hand — no frameworks, no build step, no libraries, one canvas and a flashlight. It is one of twenty-five sites in a showcase of wildly different design schools, each written the same way. The rest of the precinct is at fable-25-dhb.pages.dev.

§4Steal This