Handoff: The Daily Cyborg — V1 “Muted Phosphor” Finish


Handoff: The Daily Cyborg — V1 “Muted Phosphor” Finish

Overview

This handoff refines the look and feel of The Daily Cyborg — an existing Jekyll site. The visual direction (green-on-black, techno/matrix + Harry Potter blackletter) stays. What changes is the finish: pull the saturation and glow way down, add print-y texture, push the whole thing further into “terminal archaeology” territory — an archived, slightly decaying CRT that still feels like a newspaper.

The structure, copy, nav, layout, and content model are unchanged. This is a CSS + light-JS refresh.

About the Design Files

The files in reference/ are design references — an HTML/React prototype showing the intended look. They are not production code to copy directly.

The target codebase is a Jekyll site (see https://github.com/jasonpauljohnston/thedailycyborg). The task is to update:

  • assets/css/main.css — the main stylesheet (existing tokens are well-organized; replace values, don’t rewrite structure)
  • assets/js/main.js — add the small JS effects (glyph rain canvas + glitch hover)
  • _includes/head.html — already loads the right Google Fonts; no changes expected
  • Body markup in _includes/header.html, _includes/footer.html, _includes/post-card.html, _layouts/* — minor additions only (a <canvas> for rain, data-glitch attributes for hover scramble)

Implement the design using the existing Liquid templates + vanilla CSS/JS. Do not introduce React or a bundler.

Fidelity

High-fidelity. Exact colors, typography, spacing, and effects are specified below. Recreate pixel-perfectly.


Palette (oklch — replace old :root tokens)

The existing CSS uses HSL/hex. Replace with these. Keep variable names where they exist; add new ones where noted.

Token New value Notes
--bg-primary #070b07 slightly warmer than before
--bg-secondary #0a100a panel / ticker bg
--bg-card #0c110c card bg
--text-primary (paper) oklch(0.92 0.02 95) warm off-white for headlines
--phosphor (was --accent) oklch(0.78 0.11 142) main green. much less saturated than #39ff14
--phosphor-dim oklch(0.55 0.06 142) body copy, nav inactive
--phosphor-mute oklch(0.38 0.035 142) hairlines, separators
--amber (new) oklch(0.68 0.11 75) shadow/alert accent (ticker label, stardates)
--amber-dim (new) oklch(0.45 0.07 75)  
--rule oklch(0.55 0.06 142 / 0.55) primary hairline
--rule-faint oklch(0.55 0.06 142 / 0.18) sub-hairline
--accent-teal remove no teal in V1

Glow policy

Remove all text-shadow glow and box-shadow glow throughout the site — with one exception: the masthead title keeps a gentle residual glow:

.masthead-title {
  text-shadow:
    0 0 18px oklch(0.78 0.11 142 / 0.28),
    0 0 2px  oklch(0.78 0.11 142 / 0.6);
}

Everything else (.rule, .post-card-title:hover, .read-more:hover, .pull-quote, .section-header::before, etc.) should drop its glow. Hover states use a color/border change only.


Typography (unchanged families, tightened usage)

Families already loaded — keep them:

  • --font-masthead: 'UnifrakturMaguntia', serif; — masthead only
  • --font-body: 'Lora', Georgia, serif; — body copy, excerpts
  • --font-headline: 'Courier Prime', monospace; — headlines, nav, buttons (UPPERCASE)
  • --font-label: 'VT323', monospace; — labels, ticker, meta, tags
  • --font-mono: 'Share Tech Mono', monospace; — code blocks

Type spec changes

Element Family Size Weight Tracking Case
.masthead-title Unifraktur clamp(3rem, 8vw, 6.5rem) 400 0.01em sentence
.masthead-tagline VT323 1.1rem 0.32em UPPERCASE
wrap with em-dashes: — Dispatches from the Human-Machine Frontier —          
.masthead-meta VT323 0.95rem 0.12em sentence
.ticker-bar VT323 0.95rem 0.08em sentence
.ticker-label text VT323 bold 700 0.08em NEURAL WEATHER ∷ (use instead of :)
.site-nav a Courier Prime 0.75rem 700 0.25em UPPERCASE
.post-card-title Courier Prime 1.25rem (non-lead), 1.875rem (lead) 700 0.02em UPPERCASE
.post-card-excerpt Lora 0.95rem 400 normal sentence
.post-card-issue (stardate) VT323 0.875rem 0.18em color: --amber
.tag VT323 0.8rem 0.1em UPPERCASE, color --phosphor-dim
.read-more Courier Prime 0.7rem 700 0.22em UPPERCASE, prefix > , suffix ` _`
.footer-text VT323 0.875rem 0.12em sentence

Line-height: body copy 1.65, headlines 1.15–1.2.


Textures & Effects (the “finish”)

1. CRT scanlines — global overlay

Add once, covers the whole viewport. Non-interactive.

body::after {
  content: '';
  position: fixed; inset: 0;
  pointer-events: none;
  z-index: 9999;
  background-image: repeating-linear-gradient(
    0deg,
    rgba(0,0,0,0.22) 0 1px,
    transparent 1px 3px
  );
  mix-blend-mode: multiply;
}

2. Subtle 60Hz flicker

Apply to a wrapper around the main content (not the scanline overlay).

@keyframes v1flicker {
  0%, 100% { opacity: 0.97; }
  3%       { opacity: 0.93; }
  50%      { opacity: 1;    }
  97%      { opacity: 0.95; }
}
.page-flicker { animation: v1flicker 4.5s infinite; }

Wrap the <body> inner content (everything except the two fixed overlays) with class="page-flicker".

3. Newsprint grain — fixed overlay

Warm-toned SVG turbulence at opacity: 0.08, mix-blend-mode: overlay.

body::before {
  content: '';
  position: fixed; inset: 0;
  pointer-events: none;
  z-index: 1;
  opacity: 0.08;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='n'><feTurbulence baseFrequency='0.9' numOctaves='2' seed='5'/><feColorMatrix values='0 0 0 0 0.6  0 0 0 0 0.9  0 0 0 0 0.6  0 0 0 0.6 0'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>");
}

This replaces the current circuit-board SVG background.

4. Glyph rain — subtle canvas behind content

A sparse matrix-rain canvas, not dominant. Target: opacity: 0.22 at the canvas level, with a very low density.

Add <canvas id="glyph-rain"></canvas> as the first child of <body>. CSS:

#glyph-rain {
  position: fixed; inset: 0;
  width: 100%; height: 100%;
  z-index: 0;
  opacity: 0.22;
  pointer-events: none;
}

JS (assets/js/main.js) — see reference/shared.jsx useGlyphRain for the full algorithm. Port to vanilla JS with these params:

  • font: 13px "Share Tech Mono", monospace
  • character set: アイウエオカキクケコサシスセソタチツテトナニヌネノ01010110+-*/<>[]{}
  • head color: oklch(0.72 0.09 142 / 0.35) (new glyph this frame)
  • trail color: oklch(0.55 0.06 142 / 0.18) (glyph behind head)
  • fade rect color per frame: rgba(5,15,5,0.06) (low = long trails)
  • density multiplier: 0.7 (cols = width / fontSize * 0.7)
  • fall speed: 0.55 rows per frame
  • reset rule: when y > height && Math.random() > 0.975, reset drops[i] = 0

5. Glitch hover — character scramble

Apply to: .post-card-title a, .site-nav a, .masthead-title a.

Algorithm: on mouseenter, for each character in the element’s text, schedule a scramble window (start = random 0–8 frames, end = start + 4–14 frames). During the window, render a random character from !<>-_\\/[]{}—=+*^?#01. After end, lock in the original. Advance one requestAnimationFrame per tick.

See reference/shared.jsx GlitchText component for the reference implementation. Port to vanilla JS — select all matching elements on DOMContentLoaded and bind mouseenter.


Component Specs

Masthead (.site-header, .masthead)

  • Background: --bg-primary.
  • border-bottom: single 1px solid var(--rule) (was 3px double).
  • Top and bottom 1px gradient accents removed (.masthead::before/::after — delete).
  • Title keeps the blackletter with the residual glow spec above.
  • Tagline wrapped with em-dashes on each side: — DISPATCHES FROM THE HUMAN-MACHINE FRONTIER —.
  • Meta separator dots: use · in --phosphor-mute.

Ticker (.ticker-bar)

  • Background: --bg-secondary.
  • Borders top & bottom: 1px solid var(--rule-faint).
  • Label NEURAL WEATHER ∷ in --amber.
  • Items separated by · in --phosphor-mute.
  • Keep the existing scroll animation; speed unchanged.
  • Background: --bg-secondary.
  • Border-bottom: 1px solid var(--rule).
  • Links: padding 0.875rem 1.375rem. Dividers: 1px solid var(--rule-faint) between items (not on first-item left edge).
  • Active state: color --text-primary (paper), background oklch(0.55 0.06 142 / 0.08), no glow.
  • Hover state: color --phosphor (brighter). No text-shadow.
  • Glitch scramble fires on hover.

Section header (.section-header)

  • Rules: single 1px solid --rule (not the current 3px double with glow).
  • Title text: prefix with and suffix ` ∷. Font VT323, tracking 0.35em, color –phosphor`. No glow.

Post card (.post-card)

  • Remove all glow on hover.
  • .post-card-title a:hover → color --phosphor, no text-shadow.
  • Lead story (:first-child): keep the 2-col split layout; change the column divider from 1px solid --accent-dim to 1px solid var(--rule-faint). Remove the 3px double bottom border — use 1px solid var(--rule).
  • Stardate line: Issue No. {n} · STARDATE {date} in --amber, VT323.
  • .read-more text: "> READ FULL TRANSMISSION _" (prefix > , suffix ` _). Border 1px solid –phosphor, no hover glow — hover fills with –phosphor and switches text to –bg-primary`.

Tags (.tag)

  • Border: 1px solid var(--rule-faint).
  • Color: --phosphor-dim (was --accent).
  • No background change.

Rules (.rule, .rule-single, .article-rule)

  • Collapse all to 1px solid var(--rule), no glow, no double-line. If you want emphasis on article breaks, use width: 60% margin-auto only — no shadow.
  • Border-top: 1px solid var(--rule) (was 3px double).
  • No box-shadow.
  • Text color --phosphor-mute, VT323.

Drop cap (post body p:first-of-type::first-letter)

  • Keep Unifraktur, 5rem, float-left.
  • Color: --phosphor with no text-shadow (current version glows — remove it).

Pull quote (.pull-quote, .post-body blockquote)

  • Remove box-shadow: var(--accent-glow) and inset shadow.
  • Borders left/right: 2px solid var(--phosphor) (thinner; was 3px).
  • Inner text color: --phosphor (was accent-teal; V1 drops the teal).
  • Opening quote mark: keep Unifraktur, color --phosphor at opacity: 0.3.

Interactions & Behavior

Interaction Behavior
Nav hover Color brightens to --phosphor; background tint oklch(0.55 0.06 142 / 0.08); glitch-scramble text (~180ms)
Headline hover Glitch-scramble text; color brightens to --phosphor
Read-more hover Fills to --phosphor background, text becomes --bg-primary. No glow.
Ticker Existing horizontal scroll, unchanged
Glyph rain Autoplays on load, pauses on prefers-reduced-motion: reduce
CRT flicker Autoplays; must respect prefers-reduced-motion: reduce → disable the v1flicker animation
@media (prefers-reduced-motion: reduce) {
  .page-flicker { animation: none; }
  #glyph-rain { display: none; }
}

Design Tokens (summary — for quick reference)

:root {
  /* Backgrounds */
  --bg-primary:    #070b07;
  --bg-secondary:  #0a100a;
  --bg-card:       #0c110c;

  /* Foreground */
  --text-primary:  oklch(0.92 0.02 95);

  /* Phosphor (main green — desaturated) */
  --phosphor:      oklch(0.78 0.11 142);
  --phosphor-dim:  oklch(0.55 0.06 142);
  --phosphor-mute: oklch(0.38 0.035 142);

  /* Amber (shadow accent) */
  --amber:         oklch(0.68 0.11 75);
  --amber-dim:     oklch(0.45 0.07 75);

  /* Rules */
  --rule:          oklch(0.55 0.06 142 / 0.55);
  --rule-faint:    oklch(0.55 0.06 142 / 0.18);

  /* Aliases for back-compat with existing CSS selectors */
  --accent:        var(--phosphor);
  --accent-dim:    var(--rule-faint);
  --rule-color:    var(--phosphor-dim);

  /* Remove: --accent-teal, --accent-glow, --teal-glow */

  /* Fonts — unchanged */
  --font-masthead: 'UnifrakturMaguntia', serif;
  --font-headline: 'Courier Prime', monospace;
  --font-body:     'Lora', Georgia, serif;
  --font-label:    'VT323', monospace;
  --font-mono:     'Share Tech Mono', monospace;
}

Spacing

Unchanged from current CSS — existing scale is fine.

Border radius

Zero. Everything is hard-edged.

Shadows

None, except the residual masthead glow documented above.


Assets

No new image assets. Everything is CSS/SVG/canvas.

Fonts already loaded via Google Fonts in _includes/head.html — no changes needed.


Files to change

File Change
assets/css/main.css Token replacement + glow removal + rule simplification. Follow this doc section-by-section.
assets/js/main.js Add glyph-rain canvas loop + glitch-hover binder. Port from reference/shared.jsx.
_layouts/default.html Add <canvas id="glyph-rain"></canvas> as first child of <body>; wrap existing body contents with <div class="page-flicker">.
_includes/head.html No change (fonts already loaded).
_includes/header.html Tweak tagline text to — Dispatches from the Human-Machine Frontier —; label to NEURAL WEATHER ∷.

Reference Files

In reference/:

  • v1-preview.html — open in a browser to see the exact intended result.
  • v1-muted-phosphor.jsx — the React component source. Read this for exact spacing, structure, and inline style values.
  • shared.jsx — canonical implementations of useGlyphRain and GlitchText. Port these to vanilla JS.

To preview locally:

cd reference
python3 -m http.server 8000
# open http://localhost:8000/v1-preview.html

Done criteria

  • All teal and neon-glow are gone; only the masthead has a soft residual green glow.
  • Green hue reads as “aged phosphor,” not laser.
  • Amber appears only in stardates and the NEURAL WEATHER ∷ label.
  • CRT scanlines visible at normal viewing distance; not overpowering.
  • Glyph rain visible but subtle — a backdrop, not a feature.
  • Headlines, nav, and masthead glitch-scramble on hover.
  • prefers-reduced-motion: reduce disables the flicker animation and hides the glyph rain.
  • No layout regressions on mobile (existing @media (max-width: 768px) and 480px breakpoints still apply).