Alex Dunmow 47776c0b6f feat(y2k): remediation — fix hero, light-mode contrast, seed media, fonts, article
- hero: read styled-text fields as `field|get:"text"|default:field` so the
  home hero renders (bare-string seed values were hard-erroring the block to
  empty output — the "weird af header" root cause; page jumped navbar→audio).
- light-mode: add a background-token radial scrim behind the hero headline
  (lightens the centre in light, darkens in dark) for legible text over the mesh.
- seed media: ship 6 licensed stock images; wire 4 as Records cover art
  (chrome-liquid-metal/holographic-foil/abstract-neon/cassette) + the waveform
  now-playing cover (neon-rave) + a chrome image on About.
- waveform: replace the "No audio source set yet." placeholder with a deliberate
  "SIDE A // live to tape" mono transport readout.
- fonts: @font-face Orbitron/Inter/Share Tech Mono from /templates/y2k/fonts/*
  + typography refs in all presets (bundled fonts were inert dead code).
- article: expand tracking-to-tape to 10 band-journal paragraphs (kills the
  ~350px blog dead space).
- SVG geometry guard: explicit width/height on every sprite <use> icon +
  replace ::icon:: shortcodes with sized <svg> (Tailwind JIT balloon guard).
- capture Wave B screenshots (home/about/article × light+dark, 2880×1800);
  preview.png = dark home (dark-canonical theme).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 20:33:51 +08:00

109 lines
7.8 KiB
Plaintext

{# hero y2k override (GO-BIG showpiece): canvas liquid-chrome metaballs behind a beveled chrome headline over a sparkle glitter field. All builtin field reads + macro + branch structure preserved from the frozen hero default. The metaball canvas shows when there is no backgroundImage; it lazy-inits on view, honors prefers-reduced-motion, degrades to a static chrome-mesh gradient with JS off, and is dependency-free (hero/landing only per motion tier). #}
{% set eyebrowText = eyebrow|get:"text"|default:eyebrow %}
{% set headlineText = headline|get:"text"|default:headline %}
{% set subText = subheadline|get:"text"|default:subheadline %}
{% set descText = description|get:"text"|default:description %}
{% set pText = ctaPrimary.text %}
{% set pUrl = ctaPrimary.url %}
{% set sText = ctaSecondary.text %}
{% set sUrl = ctaSecondary.url %}
{% set variant = template|default:"centered" %}
{% set hasImage = backgroundImage %}
{% set showOverlay = hasImage and overlayColor != "none" and overlayColor != "" %}
{% macro herocontent(align) %}
{% if eyebrowText %}<span class="y2k-mono clip-cut y2k-bevel inline-block mb-4 px-3 py-1 bg-primary/10 text-primary text-xs font-semibold uppercase tracking-widest">{{ eyebrowText }} <span aria-hidden="true" class="caret-blink">_</span></span>{% endif %}
{% if headlineText %}<h1 class="y2k-display glitch text-4xl sm:text-5xl lg:text-6xl font-bold leading-tight text-foreground" data-text="{{ headlineText }}">{{ headlineText }}</h1>{% endif %}
{% if subText %}<h2 class="mt-4 text-xl sm:text-2xl text-muted-foreground max-w-2xl{% if align == "center" %} mx-auto{% endif %}">{{ subText }}</h2>{% endif %}
{% if descText %}<p class="mt-4 max-w-xl leading-relaxed text-muted-foreground{% if align == "center" %} mx-auto{% endif %}">{{ descText }}</p>{% endif %}
{% if pText or sText %}<div class="mt-8 flex flex-col sm:flex-row gap-4{% if align == "center" %} justify-center{% endif %}">
{% if pText and pUrl %}<a href="{{ pUrl }}" class="y2k-button rgb-split">{{ pText }}</a>{% endif %}
{% if sText and sUrl %}<a href="{{ sUrl }}" class="y2k-mono clip-cut y2k-bevel inline-flex items-center justify-center px-6 py-3 font-semibold uppercase tracking-wider text-accent bg-card transition-colors hover:bg-accent/10">{{ sText }}</a>{% endif %}
</div>{% endif %}
{% if trustLogos %}<div class="mt-10">
<p class="y2k-mono text-xs uppercase tracking-widest text-muted-foreground mb-3">Heard on</p>
<div class="flex flex-wrap items-center gap-6{% if align == "center" %} justify-center{% endif %}">{% for logo in trustLogos %}{% if logo.url %}<a href="{{ logo.url }}" target="_blank" rel="noopener noreferrer" class="opacity-60 transition-opacity hover:opacity-100">{% img logo.image alt=logo.alt class="h-8 w-auto" %}</a>{% else %}{% img logo.image alt=logo.alt class="h-8 w-auto opacity-60" %}{% endif %}{% endfor %}</div>
</div>{% endif %}
{% endmacro %}
{% if variant == "split" %}
<section class="hero grid grid-cols-1 lg:grid-cols-2 overflow-hidden bg-background text-foreground{% if class %} {{ class }}{% endif %}">
<div class="relative min-h-[320px] bg-muted {% if mediaPosition == "left" %}order-1 lg:order-1{% else %}order-1 lg:order-2{% endif %}">{% if hasImage %}{% img backgroundImage alt=headlineText class="absolute inset-0 h-full w-full object-cover" layout="hero" %}{% else %}<div class="y2k-orb-wrap"><div class="y2k-orb-fallback"></div><canvas class="y2k-orb-canvas" aria-hidden="true"></canvas></div>{% endif %}<div aria-hidden="true" class="scanlines pointer-events-none absolute inset-0"></div></div>
<div class="flex flex-col justify-center px-6 py-16 lg:px-12 {% if mediaPosition == "left" %}order-2 lg:order-2{% else %}order-2 lg:order-1{% endif %}"><div class="max-w-lg text-left">{{ herocontent("left") }}</div></div>
</section>
{% else %}
{% set align = "center" %}
{% if variant == "left-aligned" %}{% set align = "left" %}{% endif %}
<section class="hero relative flex flex-col overflow-hidden bg-background text-foreground {% if minHeight == "screen" %}min-h-screen{% elif minHeight == "50vh" %}min-h-[50vh]{% elif minHeight == "auto" %}min-h-[400px]{% else %}min-h-[75vh]{% endif %} {% if verticalAlign == "top" %}justify-start pt-20{% elif verticalAlign == "bottom" %}justify-end pb-20{% else %}justify-center{% endif %}{% if class %} {{ class }}{% endif %}">
{% if hasImage %}<div class="absolute inset-0 z-0">{% img backgroundImage alt=headlineText class="h-full w-full object-cover" layout="hero" %}{% if showOverlay %}<div class="absolute inset-0 {% if overlayColor == "light" %}bg-background/60{% elif overlayColor == "primary" %}bg-primary/50{% elif overlayOpacity <= 30 %}bg-black/30{% elif overlayOpacity <= 50 %}bg-black/50{% elif overlayOpacity <= 70 %}bg-black/70{% else %}bg-black/80{% endif %}"></div>{% endif %}</div>{% else %}<div class="y2k-orb-wrap z-0"><div class="y2k-orb-fallback"></div><canvas class="y2k-orb-canvas" aria-hidden="true"></canvas></div>{% endif %}
<div aria-hidden="true" class="scanlines pointer-events-none absolute inset-0 z-0"></div>
<div aria-hidden="true" class="y2k-hero-scrim pointer-events-none absolute inset-0"></div>
<div class="relative z-10 mx-auto w-full max-w-4xl px-4 sm:px-6 lg:px-8 {% if variant == "left-aligned" %}text-left{% else %}text-center{% endif %}">{{ herocontent(align) }}</div>
</section>
{% endif %}
<script>
(function(){
if(window.__y2kOrbs)return;window.__y2kOrbs=1;
var reduce=window.matchMedia&&window.matchMedia('(prefers-reduced-motion: reduce)').matches;
function initCanvas(cv){
if(cv.__init)return;cv.__init=1;
var ctx=cv.getContext&&cv.getContext('2d');if(!ctx)return;
var wrap=cv.parentElement;
var cs=getComputedStyle(document.documentElement);
function raw(n,f){var v=cs.getPropertyValue(n).trim();return 'hsl('+(v||f)+')';}
var cols=[raw('--mesh-a','320 95% 62%'),raw('--mesh-b','190 90% 60%'),raw('--mesh-c','55 95% 68%')];
var W=0,H=0,dpr=1,blobs=[],N=6;
function seed(){
blobs=[];
for(var i=0;i<N;i++){
blobs.push({
x:Math.random(),y:Math.random(),
r:0.16+Math.random()*0.22,
vx:(Math.random()-0.5)*0.00035,
vy:(Math.random()-0.5)*0.00035,
c:cols[i%cols.length]
});
}
}
function resize(){
dpr=Math.min(window.devicePixelRatio||1,2);
W=wrap.clientWidth;H=wrap.clientHeight;
if(W===0||H===0)return;
cv.width=W*dpr;cv.height=H*dpr;ctx.setTransform(dpr,0,0,dpr,0,0);
}
seed();resize();
var last=0,fps=1000/30;
function frame(t){
requestAnimationFrame(frame);
if(t-last<fps)return;last=t;
if(W===0||H===0){resize();return;}
ctx.clearRect(0,0,W,H);
ctx.globalCompositeOperation='lighter';
var base=Math.min(W,H);
for(var i=0;i<blobs.length;i++){
var b=blobs[i];
b.x+=b.vx;b.y+=b.vy;
if(b.x<0.05||b.x>0.95)b.vx*=-1;
if(b.y<0.05||b.y>0.95)b.vy*=-1;
var cx=b.x*W,cy=b.y*H,rad=b.r*base;
var g=ctx.createRadialGradient(cx,cy,0,cx,cy,rad);
g.addColorStop(0,b.c);
g.addColorStop(1,'transparent');
ctx.fillStyle=g;
ctx.beginPath();ctx.arc(cx,cy,rad,0,Math.PI*2);ctx.fill();
}
ctx.globalCompositeOperation='source-over';
}
if(window.ResizeObserver){var ro=new ResizeObserver(resize);ro.observe(wrap);}else{window.addEventListener('resize',resize);}
requestAnimationFrame(frame);
}
function boot(){
if(reduce)return;
var cvs=document.querySelectorAll('.y2k-orb-canvas');
if(!cvs.length)return;
if(!('IntersectionObserver' in window)){cvs.forEach(initCanvas);return;}
var io=new IntersectionObserver(function(es){es.forEach(function(e){if(e.isIntersecting){initCanvas(e.target);io.unobserve(e.target);}});},{threshold:0.05});
cvs.forEach(function(c){io.observe(c);});
}
if(document.readyState!='loading')boot();else document.addEventListener('DOMContentLoaded',boot);
})();
</script>