MASSVOID

HOW THIS
WAS POURED

This page is the colophon for MASSVOID, site 06 of the fable-25 set. Same mix design as the gallery: two Archivos, four hexes, twelve columns, and concrete that is computed, never photographed. Everything below is quoted from the real source.

THE BRIEF

A brutalist gallery for concrete art. Aggressive order: the grid is visible, enormous, and load-bearing. Signature techniques, as issued: procedural concrete from layered feTurbulence grain plus multiplied stains, built as a small “pour” utility so no two panels match; an exposed 12-column grid with visible gridlines and A1–L12 coordinates used as navigation; massive display type set in the grid with deliberate overlaps and hard cropping; a hover slice/skew on works with a hard 120 ms snap and no easing softness.

The one thing this site must prove: that “ugly” materials and a rigid grid can produce beauty: the chaos is gridded underneath, every overlap placed, not accidental. Type: Archivo Black for display, Archivo (variable width) for everything else, vendored as latin woff2 subsets. The width axis stands in for italics everywhere; the variable file ships no italic and nothing here pretends otherwise.

RAW CONCRETE#B5B0A8
POURED#8F8A82
VOID BLACK#111110
SAFETY ORANGE#E8501A
RUST#7A4A33

THE POUR UTILITY

Every element carrying data-pour gets its own batch of concrete at runtime. A seeded PRNG (mulberry32 over an FNV-1a hash of kind, index, and batch number) drives four layer families: fine feTurbulence tooth, a pale soft-light fleck, one to four multiplied radial stains (sometimes rust-tinted), an occasional trowel-drift banding, and a large-scale cure mottle. The layers compose as CSS background-images with per-layer blend modes. Deterministic per panel, different across panels: no two match, and the RE-POUR control in the title block advances the batch number to prove it.

This panel you are reading sits on the page, not in a pour; the code panels below are poured black. On top of the texture, each work’s slab gets a small procedural SVG artwork per kind — slumped cone, board-marked wall, aggregate cube, rain streaks, post-tensioned beam, 144 tie holes — drawn from the same seed stream.

js/main.js — grain layer (feTurbulence → alpha, one seed per call)
function grainLayer(r, o) {
  var seed = 1 + ((r() * 9998) | 0);
  var bf = (o.bf * (0.8 + r() * 0.5)).toFixed(4);
  var svg = '<svg xmlns="http://www.w3.org/2000/svg" width="' + o.size + '" height="' + o.size + '">'
    + '<filter id="g"><feTurbulence type="fractalNoise" baseFrequency="' + bf
    + '" numOctaves="' + o.oct + '" seed="' + seed + '" stitchTiles="stitch"/>'
    + '<feColorMatrix type="matrix" values="0 0 0 0 ' + o.v + '  0 0 0 0 ' + o.v
    + '  0 0 0 0 ' + o.v + '  ' + o.a + ' 0 0 0 0"/></filter>'
    + '<rect width="100%" height="100%" filter="url(#g)"/></svg>';
  return { img: 'url("data:image/svg+xml,' + encodeURIComponent(svg) + '")',
           size: o.size + 'px ' + o.size + 'px', blend: o.blend || 'multiply' };
}
js/main.js — one batch of concrete per panel
function pour(el, i) {
  var kind = el.getAttribute('data-kind') || 'plain';
  var r = rng(hash(kind + '/' + i + '/' + EPOCH));   // EPOCH++ on RE-POUR
  var b = (BASE[kind] || BASE.plain).map(function (v) { return Math.round(v + r() * 10 - 5); });
  el.style.backgroundColor = 'rgb(' + b.join(',') + ')';
  var L = [];
  L.push(grainLayer(r, { bf: 0.82, oct: 2, v: 0.06, a: 0.11, size: 264 }));      // fine tooth
  L.push(grainLayer(r, { bf: 0.62, oct: 2, v: 1, a: 0.085, size: 300,
                         blend: 'soft-light' }));                                // pale fleck
  var stains = kind === 'sheet' ? 1 : 2 + ((r() * 3) | 0);
  for (var k = 0; k < stains; k++) L.push(stainLayer(r));                        // multiplied stains
  if (r() < 0.7 && kind !== 'sheet') L.push(trowelLayer(r));                     // trowel drift
  L.push(grainLayer(r, { bf: 0.012, oct: 3, v: 0.10, a: 0.20, size: 860 }));     // cure mottle
  el.style.backgroundImage     = L.map(function (x) { return x.img;   }).join(',');
  el.style.backgroundSize      = L.map(function (x) { return x.size;  }).join(',');
  el.style.backgroundBlendMode = L.map(function (x) { return x.blend; }).join(',');
  drawArt(el, kind, r);
}

THE LOAD-BEARING GRID

The twelve columns are drawn, not implied: a fixed layer of hairlines runs over the whole page in mix-blend-mode: difference, so one gray reads on raw concrete, on void black, and across the type. A ruler strip pins letters A–L to the columns; numbered row rules mark the twelve rows; navigation is grid references (WORKS lives at A4, the schedule at G7). Every section is the same repeat(12, 1fr), and the works are placed by coordinate, overlaps included: Kettunen’s slab is pulled up into the WORKS heading by exactly 30% of the display size.

The header readout is a survey instrument: it reports the column letter under your cursor and the row band in view, e.g. E·04 / L12. The page is a drawing; you are on it.

css/main.css — visible lines + coordinate placement
.gridlines{
  position:fixed;inset:0;z-index:60;pointer-events:none;
  display:grid;grid-template-columns:repeat(12,1fr);
  mix-blend-mode:difference;
}
.gridlines i{border-left:1px solid #45423b}

.grid{display:grid;grid-template-columns:repeat(12,1fr);position:relative}

#w-kettunen{grid-column:2/7;grid-row:2/4;margin-top:calc(var(--worksF) * -0.30)}
#w-sokol{grid-column:7/13;grid-row:2/4;margin-top:calc(var(--worksF) * 0.62)}
#w-fuchs{grid-column:1/6;grid-row:5/7;margin-top:clamp(40px,5vw,80px)}
#w-jonsdottir{grid-column:6/12;grid-row:5/7;margin-top:clamp(150px,20vw,330px)}
#w-reedik{grid-column:1/13;grid-row:8}
#w-demir{grid-column:1/7;grid-row:10}
.sched{grid-column:7/13;grid-row:10;margin-top:clamp(80px,10vw,160px)}

THE 120 MS SLICE

Hovering a work displaces it like a mis-struck form: the slab shifts, its artwork skews the other way, and a ::after that inherits the slab’s exact poured background is clipped to two horizontal bands and shoved 11 px left. Everything snaps in 120 ms on steps(2, jump-end): a mechanical detent, no easing softness. The same state serves :focus-visible for keyboards, and the harness can photograph it via ?slice=B4.

Under prefers-reduced-motion the pour-in, reveals, and all snap transitions are removed; panels arrive settled and the page stays complete. The only easing curves on the site are a custom cubic-bezier and stepped timing; the words ease and linear appear nowhere.

css/main.css — the slice
--snap:120ms;
--steps:steps(2,jump-end);

.wk-slab::after{
  content:"";position:absolute;inset:0;z-index:2;
  background:inherit;background-blend-mode:inherit;   /* same batch of concrete */
  clip-path:polygon(0 12%,100% 12%,100% 26%,0 26%,
                    0 55%,100% 55%,100% 71%,0 71%);   /* two bands */
  opacity:0;filter:brightness(.94);
  transition:opacity var(--snap) var(--steps),transform var(--snap) var(--steps);
}
.wk-slab:hover,.wk-slab:focus-visible{transform:translate(-4px,2px)}
.wk-slab:hover .art{transform:translate(9px,-4px) skewX(-1.2deg)}
.wk-slab:hover::after{opacity:1;transform:translateX(-11px)}

PROVENANCE

Every pixel on MASSVOID is procedural: the concrete is layered SVG turbulence and CSS gradients computed in the browser, the six artworks are seeded SVG drawn by the pour utility, the stamp and gridlines are CSS. No photography, no stock, no AI-generated imagery, no runtime requests beyond this folder. Fonts are Archivo Black and Archivo Variable (width axis) from the fontsource distribution of the Google Fonts originals, vendored locally as latin woff2 subsets. No frameworks, no build step; one HTML file, one stylesheet, one script. All artists, dates, and the silo itself are fictional; the writing treats them as real because that is the job.

Weight: the whole folder ships under 200 KB including both fonts, because the concrete is math, not JPEGs. The heaviest asset is Archivo Variable at 90 KB.

CRITIQUE LOG

Reduced motion was audited in CSS (all reveals, pours, and snaps removed under the media query; no continuous animation exists on the page) and the scroll-progress lerp collapses to direct assignment.