/* flux.vision — reconstruction of the 2004–2019 site
 *
 * Values taken verbatim from the original /theme/template.css, recovered from
 * the Wayback Machine. The original used Lato from Google Fonts at weights
 * 100/300/400/700; self-hosted here.
 *
 *   body   Lato 300, #1C1B1B, 1.8em/1.5em, padding-top 50px for the fixed nav
 *   nav    #0D0C0C, no border
 *   links  #F8F8F8, hover #999
 *   #bg    fixed, 200% x 200%, offset -50% so it always covers
 *   content laid over rgba(0,0,0,.5)
 */
@font-face{font-family:Lato;font-style:normal;font-weight:100;font-display:swap;src:url(../fonts/lato-100-normal-latin.woff2) format("woff2");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;}
@font-face{font-family:Lato;font-style:normal;font-weight:100;font-display:swap;src:url(../fonts/lato-100-normal-latin-ext.woff2) format("woff2");unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;}
@font-face{font-family:Lato;font-style:normal;font-weight:300;font-display:swap;src:url(../fonts/lato-300-normal-latin.woff2) format("woff2");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;}
@font-face{font-family:Lato;font-style:normal;font-weight:300;font-display:swap;src:url(../fonts/lato-300-normal-latin-ext.woff2) format("woff2");unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;}
@font-face{font-family:Lato;font-style:normal;font-weight:400;font-display:swap;src:url(../fonts/lato-400-normal-latin.woff2) format("woff2");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;}
@font-face{font-family:Lato;font-style:normal;font-weight:400;font-display:swap;src:url(../fonts/lato-400-normal-latin-ext.woff2) format("woff2");unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;}
@font-face{font-family:Lato;font-style:normal;font-weight:700;font-display:swap;src:url(../fonts/lato-700-normal-latin.woff2) format("woff2");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;}
@font-face{font-family:Lato;font-style:normal;font-weight:700;font-display:swap;src:url(../fonts/lato-700-normal-latin-ext.woff2) format("woff2");unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;}
@font-face{font-family:Lato;font-style:normal;font-weight:900;font-display:swap;src:url(../fonts/lato-900-normal-latin.woff2) format("woff2");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;}
@font-face{font-family:Lato;font-style:normal;font-weight:900;font-display:swap;src:url(../fonts/lato-900-normal-latin-ext.woff2) format("woff2");unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;}
@font-face{font-family:Lato;font-style:italic;font-weight:400;font-display:swap;src:url(../fonts/lato-400-italic-latin.woff2) format("woff2");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;}
@font-face{font-family:Lato;font-style:italic;font-weight:400;font-display:swap;src:url(../fonts/lato-400-italic-latin-ext.woff2) format("woff2");unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;}
@font-face{font-family:Lato;font-style:italic;font-weight:700;font-display:swap;src:url(../fonts/lato-700-italic-latin.woff2) format("woff2");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;}
@font-face{font-family:Lato;font-style:italic;font-weight:700;font-display:swap;src:url(../fonts/lato-700-italic-latin-ext.woff2) format("woff2");unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;}
@font-face{font-family:Lato;font-style:italic;font-weight:900;font-display:swap;src:url(../fonts/lato-900-italic-latin.woff2) format("woff2");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;}
@font-face{font-family:Lato;font-style:italic;font-weight:900;font-display:swap;src:url(../fonts/lato-900-italic-latin-ext.woff2) format("woff2");unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;}

*,*::before,*::after { box-sizing: border-box; }
html { overflow-y: scroll; overflow-x: clip; }
:root {
  /* Dark-only site. Without this the browser canvas between documents, and
     the scrollbar, are light, which shows as a white frame on navigation. */
  color-scheme: dark;
  --panel-w: 148px; --panel-h: 196px;
  /* One gutter for everything. Never narrower than the floating panel's lane,
     so content can never slide under it; wider than that it tracks the
     original's 960px centred column. */
  --lane: 186px; --gutter: max(26px, calc(50% - 480px)); --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace; }
body {
  font-family: Lato, Helvetica, Arial, Verdana, sans-serif;
  font-weight: 300;
  background: #1C1B1B;
  color: #F8F8F8;
  font-size: 1.05rem;
  line-height: 1.5em;
  margin: 0;
}
a, a:visited, a:active { text-decoration: none; color: #F8F8F8; }
a:hover { color: #999; }

/* the oversized fixed backdrop, exactly as the original positioned it */
#bg { position: fixed; top: -50%; left: -50%; width: 200%; height: 200%;
      background-color: #1C1B1B; background-position: 50%; background-repeat: no-repeat;
      background-size: cover; z-index: 0; }
#content { position: relative; z-index: 1; min-height: 100vh;
  background-color: rgba(0,0,0,0.5); }
/* The hero keeps the original's half-black scrim; past it the backdrop is
   pulled further down so the work, not the texture, carries the page. */
/* full-bleed: the panel sits inside the 960px column, so it has to break out
   of it or the backdrop keeps running down both margins */
.timeline { background: rgba(10,10,10,.82); padding-bottom: 4rem; }

/* nav */
/* Not a bar. A full-width header spends the whole top edge of the viewport on
 * four words, and on a site whose content is images that edge is the most
 * valuable surface there is. This is a panel floating in the corner instead,
 * the way an on-screen display sits over a picture: hairline border, backdrop
 * blur, nothing behind it hidden.
 */
.navbar { position: fixed; top: 22px; left: 22px; z-index: 30;
  display: flex; flex-direction: column; align-items: flex-start; gap: .1rem;
  padding: .7rem .95rem .6rem;
  background: rgba(8,8,8,.52); backdrop-filter: blur(16px) saturate(.9);
  border: 1px solid rgba(255,255,255,.1); }
/* The logotype was a 56px JPEG that only worked on #0D0C0C. It is Lato 700
   italic, so it can simply be set: scalable, crisp at any density, and it takes
   currentColor so it sits on any ground. */
.navbar .brand { margin: 0 0 .55rem; font-style: italic; font-weight: 700;
  font-size: 1.32rem; letter-spacing: -.01em; line-height: 1; color: #fff; }
.navbar .brand sup { font-size: .46em; font-weight: 400; vertical-align: super;
  margin-left: .04em; }
.navbar .brand:hover { opacity: 1; }
.navbar a { font-weight: 300; font-size: .76rem; letter-spacing: .1em;
  color: #c7c7c7; line-height: 1.75; }
.navbar a:hover { color: #fff; }
.slate { margin: .55rem 0 0; padding-top: .5rem; font-family: var(--mono);
  font-size: .56rem; letter-spacing: .2em; text-transform: uppercase;
  color: #7c7c7c; border-top: 1px solid rgba(255,255,255,.1);
  align-self: stretch; }

/* On narrow screens it lies back down: a corner panel would cover the picture. */
@media (max-width: 760px) {
  :root { --lane: 18px; --gutter: 18px; }
  .navbar { top: 0; left: 0; right: 0; flex-direction: row; align-items: center;
    gap: 1rem; padding: .5rem 16px; border-width: 0 0 1px; }
  .navbar .brand { margin: 0; }
  .slate { display: none; }
}
/* No negative margins anywhere. The bands are simply full width and pad
   themselves, because `vw` counts the scrollbar that `overflow-y: scroll`
   always shows, so any 50vw-based pull-out overflowed by its width and made
   the page scroll sideways. */
.container { max-width: none; margin: 0; padding: 0; }
.container > * { padding-left: max(var(--lane), var(--gutter)); padding-right: var(--gutter); }
/* Direct children of .container must set padding with the -top/-bottom longhand.
   A `padding:` shorthand has the same specificity as the rule above and comes
   later in the file, so it silently resets the gutter to zero and the text ends
   up flush against the screen edge. */

/* timeline: year bands of tiles, as the original laid them out */
.yearband { padding: 2.2rem 0 1rem; }
.yearband h1 { font-weight: 100; font-size: 5.4rem; margin: 0 0 .6rem; letter-spacing: .09em;
  line-height: 1; opacity: .92; }
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(214px, 1fr));
  grid-auto-flow: dense; gap: 1.15rem; align-items: start; }
/* Size comes from the entry itself: surviving imagery, video, how much was
   written. See plugins/tileweight.py. A ten-hour festival set and a two-line
   workshop note should not occupy the same rectangle. */
.art.md { grid-column: span 2; }
.art.lg { grid-column: span 2; }
.art.md .art-preview { aspect-ratio: 16 / 9; }
.art.lg .art-preview { aspect-ratio: 4 / 3; }
@media (max-width: 640px) { .art.md, .art.lg { grid-column: span 1; }
  .art.md .art-preview, .art.lg .art-preview { aspect-ratio: 4 / 3; } }
.art { display: block; }
.art-preview { display: block; aspect-ratio: 4/3; border: 1px solid transparent;
  background: #111; position: relative; overflow: hidden; }

/* The static, as a 12-frame sprite stepped by CSS rather than an animated GIF.
 * Every url(void.gif) instance shared one decode and so ran in lockstep; a
 * sprite driven by steps() takes an animation-delay, which lets each tile start
 * at its own frame. The delay is negative, so they begin already diverged
 * rather than staggering into life. */
.art-preview::before {
  content: ""; position: absolute; left: 0; top: 0; width: 100%; height: 1200%;
  background: url(../img/noise.png) 0 0 / 100% 100%;
  animation: static .48s steps(12) infinite;
  animation-delay: calc(var(--seed, 0) * -37ms);
}
@keyframes static { from { transform: translateY(0); } to { transform: translateY(-100%); } }
@media (prefers-reduced-motion: reduce) { .art-preview::before { animation: none; } }
/* the real image fades in over the static once it has decoded */
.art-preview.lazy::after { content: ""; position: absolute; inset: 0;
  background-image: var(--src); background-size: cover; background-position: center;
  opacity: 0; transition: opacity .45s ease; }
.art-preview.lazy.loaded::after { opacity: 1; }
.art:hover .art-preview { border-color: #6a6a6a; }
/* Hovering knocks the picture out of sync for a moment and lets the static
   behind it show through, the way a tape head does. One gesture, 260ms. No
   scanlines, no curvature, no chromatic fringing. */
.art:hover .art-preview.loaded::after { animation: tracking .26s steps(5) 1; }
@keyframes tracking {
     0% { transform: translateY(0);     opacity: 1; }
    20% { transform: translateY(-7px);  opacity: .45; }
    45% { transform: translateY(5px);   opacity: .85; }
    70% { transform: translateY(-3px);  opacity: .6; }
   100% { transform: translateY(0);     opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .art:hover .art-preview.loaded::after { animation: none; }
}
/* No image survived the recovery, so the static simply never resolves. That
   reads as signal lost, which is truer than a typographic placeholder. */
.art-preview.empty { border-color: transparent; }
.art-title { font-size: .78rem; margin: .5rem 0 0; padding: 0; text-transform: uppercase;
  letter-spacing: .08em; font-weight: 400; line-height: 1.3; }
.art-date { font-family: var(--mono); font-size: .6rem; margin-top: 3px; color: #7a7a7a;
  letter-spacing: .1em; text-transform: uppercase; }

/* article */
.hero-title h1 { font-weight: 100; font-size: 4.6rem; line-height: 1.02; margin: 0; letter-spacing: .05em; }
#article-meta { font-family: var(--mono); font-size: .68rem; color: #8a8a8a;
  letter-spacing: .06em; padding-bottom: 1.4rem; }
#article-meta .rel { color: #777; }
#article-meta a { color: #F8F8F8; }
#article { font-weight: 300; padding-bottom: 6rem; }
#article p { text-align: justify; hyphens: auto; margin: 0 0 1.1rem; }
/* Article bodies carry their own section headings. Unstyled they fell back to
   the browser's bold default, the one piece of type on the page not set in
   Lato thin. Same register as the slate and the tile titles. */
#article h1, #article h2, #article h3 {
  font-weight: 300; letter-spacing: .14em; text-transform: uppercase; color: #cfcfcf;
  margin: 2.8rem 0 1.1rem; padding-top: .7rem;
  border-top: 1px solid rgba(255,255,255,.12); }
#article h1 { font-size: 1rem; }
#article h2 { font-size: .9rem; }
#article h3 { font-size: .8rem; }
#article ul, #article ol { margin: 0 0 1.4rem; padding-left: 1.2rem; }
#article li { line-height: 1.55; }
/* Justification needs room. In a ~350px column it opens rivers between words
   wide enough to read as a column of holes, so below the tablet break the text
   is simply set ragged-right. */
@media (max-width: 700px) { #article p { text-align: left; hyphens: none; } }
#article img { width: 100%; padding: 10px 0; display: block; }
#article iframe, #article embed { width: 100%; aspect-ratio: 16/9; border: 0; padding: 10px 0; }
#article a { border-bottom: 1px solid #555; }
#article a:hover { border-bottom-color: #999; }

/* listings */
.cloud { padding-top: 1rem; padding-bottom: 6rem; line-height: 2.4; }
.cloud a { margin-right: 1.2rem; text-transform: uppercase; font-size: .9rem; }
.cloud a span { color: #777; }
.rows { list-style: none; margin: 0; padding-bottom: 6rem; }
.rows li { display: flex; gap: 1.2rem; align-items: baseline; padding: .5rem 0; border-bottom: 1px solid #2a2a2a; }
.rows .yr { font-size: .75rem; color: #999; min-width: 4.2rem; }
.rows .ti { text-transform: uppercase; font-size: .95rem; }
.pagetitle { font-weight: 100; font-size: 4.4rem; padding-top: 4.5rem; padding-bottom: 1.2rem; margin: 0;
  letter-spacing: .07em; line-height: 1; }
.hero-title { padding-top: 5.5rem; padding-bottom: 1rem; }

/* Views that begin at the top of the document still have to clear the panel
   vertically; the gutter only handles the horizontal. */
@media (min-width: 761px) {
  .pagetitle, .hero-title { padding-top: calc(var(--panel-h) + 1.5rem); }
}
.lede { color: #999; padding-bottom: 2rem; }

@media (max-width: 700px) {
  .hero-title h1 { font-size: 2.4rem; }
  .pagetitle { font-size: 2.1rem; }
  .navbar { gap: .9rem; font-size: .8rem; }
}

/* ── the landing ──────────────────────────────────────────────────────────
 * The original opened on a full-bleed hero with the wordmark, then alternating
 * bands: the flux dictionary entry on dark, the bio on light. Both run the full
 * width of the viewport, not the content column.
 */
.navbar .brand img { display: block; }
.landing { min-height: 368px; display: flex; align-items: flex-end;
  padding-bottom: 1.6rem; }
.wordmark { font-weight: 100; font-size: 4.3rem; letter-spacing: .14em;
  text-transform: uppercase; margin: 0; line-height: 1; }
.deck { padding-top: 1.6rem; padding-bottom: 2rem; font-size: 1.02rem; }
.deck p { margin: 0; }
/* The light band was a flat grey rectangle, the one unambiguously graphic-design
   object on the page. It becomes a luminance step instead: the same backdrop,
   blown out, as an overexposed frame rather than a swatch. */
.bio { position: relative; color: #161515; isolation: isolate; overflow: hidden;
  padding-top: 2.4rem; padding-bottom: 2.4rem; }
/* Inverting the backdrop straight produced legible high-contrast texture and
   the dark type sat on black streaks. Blurred hard it becomes what was wanted
   in the first place: a soft blown-out field that is still the same frame,
   rather than a flat swatch. */
.bio::before { content: ""; position: absolute; inset: 0; z-index: -1;
  background: url(../img/bg1.jpg) center / cover;
  filter: grayscale(1) invert(1) brightness(1.5) contrast(.42) blur(34px);
  transform: scale(1.1); }
.bio::after { content: ""; position: absolute; inset: 0; z-index: -1;
  background: rgba(226,224,220,.72); }
.bio strong { font-weight: 700; }
@media (max-width: 700px) {
  .wordmark { font-size: 2.2rem; letter-spacing: .1em; }
  .landing { min-height: 42vh; }
}

/* The date leads the entry meta, set like the tile dates and the slate so the
   three read as one register. Works carry month precision only: the recovery
   could recover "Dec 2013" and no more, so a day would be invented. */
#article-meta time { color: #cfcfcf; letter-spacing: .12em; text-transform: uppercase; }
#article-meta .sep { color: #555; margin: 0 .5rem; }

/* ── the reel ─────────────────────────────────────────────────────────────
 * The original index carried a video below the bio. It is the single best
 * argument the archive has, so it gets the full width of the band rather than
 * sitting inside the text column.
 */
.reel { padding-top: 3.4rem; padding-bottom: 3.8rem; background: rgba(6,6,6,.88); }
.reel-frame { position: relative; aspect-ratio: 16 / 9; background: #000;
  border: 1px solid rgba(255,255,255,.08); }
.reel-frame iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.reel-cap { margin-top: .7rem; font-family: var(--mono); font-size: .58rem;
  letter-spacing: .2em; text-transform: uppercase; color: #777; }
.reel-cap a { color: #aaa; }
.reel-cap a:hover { color: #fff; }
@media (max-width: 760px) { .reel { padding-top: 2rem; padding-bottom: 2.2rem; } }

/* The colophon. Set like the slate rather than like navigation: this is an
   ident at the end of a closed archive, not a way out of it. Vertical padding
   only, so the gutter from `.container > *` survives (see the note there). */
/* It needs its own ground. Article pages set a hero backdrop that runs the
   full height of the document, so without this the text sat straight on the
   photograph and vanished. Same treatment as the other full-bleed bands. */
.colophon { margin-top: 4.5rem; padding-top: 1.3rem; padding-bottom: 3.5rem;
  background: rgba(6,6,6,.9);
  border-top: 1px solid rgba(255,255,255,.1);
  font-family: var(--mono); font-size: .62rem; letter-spacing: .13em;
  text-transform: uppercase; color: #8a8a8a; line-height: 2.1; }
.colophon p { margin: 0; }
.colophon a { color: #cfcfcf; }
.colophon a:hover { color: #F8F8F8; }
@media (max-width: 700px) { .colophon { letter-spacing: .08em; line-height: 2.3; } }
