/* =========================================================
   THE DARK/HEAVY — site stylesheet (v3)

   Direction, in Josh's words: dark. Stills pushed to full
   contrast then B&W. Full-frame autoplaying video breaking
   across the whole page. The site itself unconfined by boxes —
   a layered digital artifact, discordant yet beautiful, glitchy
   yet considered. SoundCloud meets MySpace, but for now.

   So: no card grids, no bordered panels. Full-bleed media that
   text overlaps directly. The only "box" on the page is the
   nav strip and the click-to-enter gate.
   ========================================================= */

@import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;700&display=swap');

:root{
  --bg:        #060605;
  --fg:        #f2ede2;
  --muted:     #8f887c;
  --line:      #262420;

  --hot:       #ff2f2f;   /* pulled from the LWS title-card red / laser red */
  --amber:     #ffb648;   /* low winter sun */

  --font-display: Copperplate, 'Copperplate Gothic Bold', 'Copperplate Gothic', 'Trajan Pro', Cambria, Georgia, serif;
  --font-ui:      Verdana, Tahoma, Geneva, 'DejaVu Sans', sans-serif;
  --font-body:    'Space Grotesk', system-ui, -apple-system, 'Segoe UI', sans-serif;

  --nav-h: 56px;
  --edge: 20px; /* min side gutter even on full-bleed sections */
}

*, *::before, *::after{ box-sizing: border-box; }
html{ scroll-behavior: smooth; }
body{
  margin:0;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font-body);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}
body.gated{ overflow: hidden; height: 100vh; }
::selection{ background: var(--hot); color: #060605; }
a{ color: inherit; text-decoration: none; }
img, video{ max-width: 100%; display:block; }

/* narrow readable column — used ONLY for prose, never for media */
.prose{ width: 100%; max-width: 640px; margin: 0 auto; padding: 0 24px; }

/* breakout — pulls any element edge-to-edge regardless of parent */
.bleed{
  width: 100vw;
  position: relative;
  left: 50%;
  right: 50%;
  margin-left: -50vw;
  margin-right: -50vw;
}

/* ================= AMBIENT DECAY — the "over everything, all the time" layer =================
   The old .grain was static texture, never moving. Now it flickers like
   actual analog noise (position + opacity jump on a tight loop), sits
   under a scanline pass, and both intensify briefly whenever a burst
   fires elsewhere via .signal-drop on <body>. This is the layer that
   makes the whole page feel like it's on a bad signal even when nothing
   else is actively tearing. */
.grain{
  pointer-events: none;
  position: fixed; inset: 0; z-index: 40;
  opacity: 0.07; mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='2' stitchTiles='stitch'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>");
  animation: grain-flicker 0.6s steps(2,end) infinite;
}
@keyframes grain-flicker{
  0%{ transform: translate(0,0); opacity: 0.07; }
  25%{ transform: translate(-1.5%,1%); opacity: 0.1; }
  50%{ transform: translate(1%,-1.5%); opacity: 0.06; }
  75%{ transform: translate(-1%,-1%); opacity: 0.11; }
  100%{ transform: translate(0,0); opacity: 0.07; }
}
.scanlines{
  pointer-events:none; position:fixed; inset:0; z-index:41;
  background: repeating-linear-gradient(0deg, rgba(0,0,0,0.22) 0px, rgba(0,0,0,0.22) 1px, transparent 1px, transparent 3px);
  mix-blend-mode: multiply; opacity: 0.5;
  animation: scanline-drift 7s linear infinite;
}
@keyframes scanline-drift{
  0%{ transform: translateY(0); }
  100%{ transform: translateY(120px); }
}
body.signal-drop .grain{ opacity: 0.22; animation-duration: 0.15s; }
body.signal-drop .scanlines{ opacity: 0.85; }
@media (prefers-reduced-motion: reduce){
  .grain{ animation: none; }
  .scanlines{ display:none; }
}

/* ================= NAV ================= */
.site-nav{
  position: fixed; top:0; left:0; right:0; z-index: 300;
  height: var(--nav-h);
  display:flex; align-items:center; justify-content:space-between;
  padding: 0 18px;
  background: rgba(6,6,5,0.7);
  backdrop-filter: blur(10px) saturate(1.2);
  border-bottom: 1px solid rgba(242,237,226,0.08);
}
.site-nav .brand{
  font-family: var(--font-display);
  letter-spacing: 0.06em;
  font-size: 0.95rem;
  text-transform: uppercase;
  display: flex; align-items: center;
}
.brand-mark{ height: 30px; width: auto; display:block; }
.nav-links{ display:flex; gap: 2px; list-style:none; margin:0; padding:0; font-family: var(--font-ui); }
.nav-links a{
  padding: 8px 12px;
  font-size: 0.74rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--muted);
  transition: color .2s ease;
}
.nav-links a:hover, .nav-links a.active{ color: var(--fg); }
.nav-links a.active{ background: rgba(242,237,226,0.07); }
.nav-toggle{
  display:none; background:none; border:1px solid var(--line); color: var(--fg);
  font-family: var(--font-ui); font-weight:700; font-size: 0.72rem; letter-spacing: 0.1em;
  padding: 7px 11px; cursor:pointer;
}
@media (max-width: 780px){
  .nav-toggle{ display:inline-block; }
  .nav-links{
    position: fixed; top: var(--nav-h); left:0; right:0;
    flex-direction: column; background: rgba(6,6,5,0.97);
    padding: 8px 18px 20px; border-bottom: 1px solid var(--line);
    transform: translateY(-8px); opacity: 0; pointer-events: none;
    transition: opacity .2s ease, transform .2s ease;
  }
  .nav-links.open{ opacity:1; transform: translateY(0); pointer-events:auto; }
  .nav-links a{ padding: 11px 4px; border-bottom: 1px solid rgba(255,255,255,0.06); }
}

/* ================= ENTRY GATE =================
   The old-web "click to enter" — also the only way a browser
   lets a page start video + audio together with sound. It IS
   the MySpace-profile-song moment. */
.gate{
  position: fixed; inset: 0; z-index: 1000;
  display:flex; align-items:center; justify-content:center;
  background: #060605; cursor: pointer;
  transition: opacity .7s ease, visibility .7s ease;
}
.gate::before{
  content:""; position:absolute; inset:0;
  background-image: url('../assets/images/hero-poster-color.jpg');
  background-size: cover; background-position: center;
  filter: contrast(1.15) brightness(0.7) saturate(1.1);
}
/* Blocky low-res pass, generated client-side from the same poster image,
   sits over the sharp ::before and fades out once ready — the page's one
   deliberate "decode" moment, at the point that matters most. */
.gate-pixel{
  position:absolute; inset:0;
  background-size: cover; background-position: center;
  image-rendering: pixelated;
  filter: contrast(1.15) brightness(0.7) saturate(1.1);
  opacity: 0;
  transition: opacity 1.4s ease;
}
.gate-pixel.ready{ opacity: 1; }
.gate-pixel.resolved{ opacity: 0; }
.gate::after{
  content:""; position:absolute; inset:0;
  background: radial-gradient(ellipse at 50% 45%, rgba(6,6,5,0.4), rgba(6,6,5,0.94) 78%);
}
@media (prefers-reduced-motion: reduce){
  .gate-pixel{ display:none; }
}

/* Full-page signal-drop — fires often now (~every 4-5s), the moment the
   WHOLE page reads as fragmenting at once: redaction blocks, a colour-
   split tear, and a horizontal skew, layered together. Safe to intensify
   because it's a fixed, inset:0 overlay — nothing here ever transforms
   an ancestor of the fixed nav/ticker/now-playing, so their positioning
   can't break. .app.js briefly adds .signal-drop to <body> in step with
   this so the ambient grain/scanlines flare at the same instant. */
.glitch-flash{
  position:fixed; inset:0; z-index:500; pointer-events:none; opacity:0;
  background:
    linear-gradient(var(--fg) 0 100%) 4% 18% / 13% 6% no-repeat,
    linear-gradient(var(--fg) 0 100%) 71% 44% / 21% 4% no-repeat,
    linear-gradient(var(--fg) 0 100%) 32% 76% / 9% 8% no-repeat,
    linear-gradient(var(--fg) 0 100%) 86% 8% / 8% 5% no-repeat,
    linear-gradient(var(--hot)) 12% 62% / 30% 3% no-repeat,
    linear-gradient(#2fd0ff) 55% 30% / 24% 3% no-repeat;
  mix-blend-mode: overlay;
  animation: glitch-burst 4.6s steps(1,end) infinite;
}
@keyframes glitch-burst{
  0%, 88%{ opacity:0; transform:none; }
  88.4%{ opacity:.7; transform:translateX(-8px) skewX(-1deg); }
  88.7%{ opacity:0; transform:none; }
  89.3%{ opacity:.5; transform:translateX(6px) scaleY(1.01); }
  89.6%{ opacity:0; transform:none; }
  93%{ opacity:0; }
  93.3%{ opacity:.65; transform:translateX(-5px) skewX(1deg); }
  93.55%{ opacity:0; }
  94.5%{ opacity:.35; transform:translateY(3px); }
  94.8%{ opacity:0; }
  100%{ opacity:0; }
}
@media (prefers-reduced-motion: reduce){
  .glitch-flash{ display:none; }
}

/* ================= GLITCH-RGB — constant low hum, frequent hard tears =================
   Reusable on any element: class="glitch-rgb" + a data-text attribute
   matching its visible text. Two things happen now, not one: a faint
   permanent chromatic-aberration fringe (text-shadow, always on, so
   nothing ever reads as fully "clean"), and — several times a cycle,
   not once — the type tears into red/cyan/green bands and snaps back.
   Cycle is short (~3.5s default) with multiple burst events packed into
   it, so on any given element you rarely wait long to see one. Heavier
   elements go even shorter via --glitch-cycle. Stagger with
   --glitch-delay so instances don't sync up. */
.glitch-rgb{
  position:relative;
  text-shadow: 1px 0 0 rgba(255,47,47,0.35), -1px 0 0 rgba(47,208,255,0.3);
  animation: glitch-hue var(--glitch-cycle, 3.5s) infinite steps(1,end);
  animation-delay: var(--glitch-delay, 0s);
}
.glitch-rgb::before,
.glitch-rgb::after{
  content: attr(data-text);
  position:absolute; left:0; top:0; width:100%; height:100%;
  overflow:hidden; pointer-events:none;
  clip-path: inset(0 0 100% 0);
}
.glitch-rgb::before{
  color: var(--hot); mix-blend-mode: screen;
  animation: glitch-slice-a var(--glitch-cycle, 3.5s) infinite steps(1,end);
  animation-delay: var(--glitch-delay, 0s);
}
.glitch-rgb::after{
  color: #2fd0ff; mix-blend-mode: screen;
  animation: glitch-slice-b var(--glitch-cycle, 3.5s) infinite steps(1,end);
  animation-delay: var(--glitch-delay, 0s);
}
@keyframes glitch-hue{
  0%, 68%{ filter:none; transform:translate(0,0); }
  69%{ filter: hue-rotate(35deg); transform: translate(-2px,1px); }
  70%{ filter: hue-rotate(-25deg); transform: translate(2px,-1px); }
  71%, 82%{ filter:none; transform:translate(0,0); }
  83%{ filter: hue-rotate(-40deg); transform: translate(3px,0); }
  84%, 100%{ filter:none; transform:translate(0,0); }
}
@keyframes glitch-slice-a{
  0%, 68%{ clip-path: inset(0 0 100% 0); transform: translate(0,0); }
  69%{ clip-path: inset(18% 0 64% 0); transform: translate(-9px,0); }
  70.3%{ clip-path: inset(52% 0 12% 0); transform: translate(7px,0); }
  71.6%{ clip-path: inset(4% 0 88% 0); transform: translate(-5px,0); }
  73%, 81%{ clip-path: inset(0 0 100% 0); transform: translate(0,0); }
  82%{ clip-path: inset(30% 0 45% 0); transform: translate(8px,0); }
  83.3%{ clip-path: inset(6% 0 82% 0); transform: translate(-6px,0); }
  85%, 100%{ clip-path: inset(0 0 100% 0); transform: translate(0,0); }
}
@keyframes glitch-slice-b{
  0%, 68.5%{ clip-path: inset(0 0 100% 0); transform: translate(0,0); }
  69.5%{ clip-path: inset(60% 0 8% 0); transform: translate(8px,0); }
  70.8%{ clip-path: inset(8% 0 70% 0); transform: translate(-8px,0); }
  72.1%{ clip-path: inset(35% 0 40% 0); transform: translate(6px,0); }
  73.5%, 81.5%{ clip-path: inset(0 0 100% 0); transform: translate(0,0); }
  82.5%{ clip-path: inset(65% 0 5% 0); transform: translate(-9px,0); }
  83.8%{ clip-path: inset(12% 0 75% 0); transform: translate(7px,0); }
  85.5%, 100%{ clip-path: inset(0 0 100% 0); transform: translate(0,0); }
}
.hero-title.glitch-rgb{ --glitch-cycle: 2.6s; }
@media (prefers-reduced-motion: reduce){
  .glitch-rgb{ animation:none; text-shadow:none; }
  .glitch-rgb::before, .glitch-rgb::after{ display:none; }
}

.gate.hidden{ opacity:0; visibility:hidden; pointer-events:none; }
.gate-inner{ position:relative; z-index:2; text-align:center; padding: 20px; }
.gate-inner .eyebrow{ display:block; margin-bottom: 16px; }
.gate-inner h1{
  font-family: var(--font-display); text-transform: uppercase; letter-spacing: 0.04em;
  font-size: clamp(2.1rem, 6.5vw, 3.6rem); margin: 0 0 20px;
}
.gate-hint{
  font-family: var(--font-ui); font-size: 0.7rem; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--muted); margin-top: 14px; display:flex; align-items:center; justify-content:center; gap:10px;
}
.gate-hint b{ color: var(--fg); }
.gate-hint .pulse{ width:8px; height:8px; border-radius:50%; background: var(--hot); animation: pulse 1.4s ease-in-out infinite; }
@keyframes pulse{ 0%,100%{ opacity:.35; transform:scale(0.8);} 50%{ opacity:1; transform:scale(1.15);} }

.eyebrow{
  font-family: var(--font-ui); font-size: 0.7rem; font-weight:700; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--amber);
}

/* ================= HERO (full-bleed video, text laid over it) ================= */
.hero{
  position: relative;
  min-height: 100svh;
  display:flex; align-items:flex-end; justify-content:center;
  overflow:hidden;
  padding-bottom: 90px;
}
.hero video, .hero .hero-poster{
  position:absolute; inset:0; width:100%; height:100%; object-fit: cover; object-position: center 30%;
}
.hero::after{
  content:""; position:absolute; inset:0;
  background: linear-gradient(180deg, rgba(6,6,5,0.15) 0%, rgba(6,6,5,0.25) 45%, rgba(6,6,5,0.96) 100%);
}
.hero-content{ position:relative; z-index:2; text-align:center; padding: 0 20px; max-width: 920px; }
.hero-title{
  font-family: var(--font-display); font-weight:700; text-transform: uppercase; letter-spacing: 0.03em;
  font-size: clamp(2.3rem, 8vw, 5.4rem); line-height: 1.02; margin: 14px 0 0;
  color: var(--fg); text-shadow: 0 4px 30px rgba(0,0,0,0.75);
}
.hero-title .slash{ color: var(--hot); }
.hero-tagline{ margin: 18px auto 0; max-width: 540px; color: #d9d3c5; font-size: 1rem; text-shadow: 0 2px 12px rgba(0,0,0,0.8); }
.hero-credit{
  margin-top: 10px; font-family: var(--font-ui); font-size: 0.62rem; letter-spacing: 0.08em;
  text-transform: uppercase; color: #c9c2b2; text-shadow: 0 2px 10px rgba(0,0,0,0.85);
}

/* Minimal hero variant — the LWS reference cut already carries its own
   title cards, so our overlay stays small and out of their way rather
   than stacking a second competing headline on top of the footage. */
.hero-content--minimal{
  position: absolute;
  left: 24px; bottom: 26px;
  text-align: left;
  max-width: none;
}
.hero-title--small{
  font-size: clamp(1.3rem, 3vw, 1.9rem);
  margin: 0;
}
.hero-content--minimal .hero-credit{ margin-top: 8px; }

.now-playing{
  position: fixed; right: 16px; bottom: 16px; z-index: 250;
  font-family: var(--font-ui); font-size: 0.66rem; font-weight:700; letter-spacing: 0.06em;
  background: rgba(6,6,5,0.8); color: var(--fg); border: 1px solid var(--line);
  padding: 8px 14px; display:flex; align-items:center; gap:9px; cursor:pointer;
  backdrop-filter: blur(6px);
  max-width: calc(100vw - 32px);
}
.now-playing .bars{ display:flex; align-items:flex-end; gap:2px; height:12px; }
.now-playing .bars span{ width:2px; background: var(--hot); animation: bar 0.9s ease-in-out infinite; }
.now-playing .bars span:nth-child(1){ height:40%; animation-delay: 0s; }
.now-playing .bars span:nth-child(2){ height:100%; animation-delay: .15s; }
.now-playing .bars span:nth-child(3){ height:65%; animation-delay: .3s; }
.now-playing.muted .bars span{ animation-play-state: paused; opacity:.3; }
@keyframes bar{ 0%,100%{ transform: scaleY(0.4);} 50%{ transform: scaleY(1);} }
.now-playing span.label{ white-space: nowrap; overflow:hidden; text-overflow: ellipsis; }

/* ================= STATEMENT BREAK (photo + quote, video alongside) =================
   Two columns on wider screens: the photo carries the quote as before
   (staggered, left-aligned lines rather than one centered block — see
   .qline below), and a video panel sits next to it. Stacks on mobile. */
.statement{
  display:grid; grid-template-columns: 1fr; margin-top: 0; margin-bottom: 0;
}
@media (min-width: 900px){
  .statement{ grid-template-columns: 1.35fr 1fr; min-height: 72vh; }
}
.statement-photo{
  position: relative;
  min-height: 58vh;
  overflow:hidden;
}
@media (min-width: 900px){
  .statement-photo{ min-height: auto; }
}
.statement-video{
  position:relative; display:flex; flex-direction:column;
  background: #0c0a07; border-top: 1px solid var(--line);
  padding: 26px 24px 30px;
}
@media (min-width: 900px){
  .statement-video{ border-top:none; border-left: 1px solid var(--line); justify-content:center; }
}
.statement-video .tag{
  display:block; font-family: var(--font-ui); font-size: 0.66rem; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--hot); margin-bottom: 14px;
}
.ig-embed-wrap{ width:100%; }
.ig-embed-wrap iframe{ width:100% !important; }
.ig-empty{
  border: 1px dashed var(--line); padding: 34px 20px; text-align:center;
  font-family: var(--font-ui); font-size: 0.72rem; line-height:1.6; color: var(--muted);
}
/* Pixelation crossfade — a blocky canvas-generated pass sits over the real
   photo and fades out the moment the section scrolls into view. Generated
   client-side from whatever image is actually in place, so it never goes
   stale when you swap the photo through the CMS. */
.img-pixelate{ position:absolute; inset:0; background: var(--bg); }
.img-pixelate img, .img-pixelate video{
  position:absolute; inset:0; width:100%; height:100%; object-fit:cover; object-position: center 30%;
  filter: contrast(1.15) saturate(1.1);
}
.img-pixelate .sharp-layer{ z-index: 1; }
.img-pixelate .pixel-layer{
  z-index: 2;
  image-rendering: pixelated;
  opacity: 0;
  transition: opacity 1.3s ease;
}
.img-pixelate.ready .pixel-layer{ opacity: 1; }
.img-pixelate.resolved .pixel-layer{ opacity: 0; }
@media (prefers-reduced-motion: reduce){
  .img-pixelate .pixel-layer{ display:none; }
}

/* Datamosh strips — independent of the scroll-triggered pixel crossfade
   above, which only ever plays once. This is the "coming apart" dose:
   several independent bands per photo (count set in JS, geometry random
   per band), each invisible until its own short cycle hits, then it
   jumps sideways, pixelates and hue-shifts for an instant like a
   corrupted frame, then snaps back. Shared by the statement photo and
   every scattered photo — .buildGlitchStrips() in app.js generates the
   markup and the --h/--top/--dur/--delay/--dist/--hue custom properties
   per strip, this just defines how a strip behaves. */
.glitch-slice{
  position:absolute; left:0; width:100%; overflow:hidden; z-index:5; opacity:0; pointer-events:none;
  height: var(--h, 12%); top: var(--top, 40%);
  animation: strip-glitch var(--dur, 4s) infinite steps(1,end);
  animation-delay: var(--delay, 0s);
}
.glitch-slice img{
  position:absolute; left:0; width:100%; object-fit:cover; image-rendering:pixelated;
  filter: contrast(1.15) saturate(1.1) hue-rotate(var(--hue, 0deg));
}
@keyframes strip-glitch{
  0%, 78%{ opacity:0; transform: translateX(0); }
  79%{ opacity:1; transform: translateX(calc(var(--dist, 20px) * -1)); }
  80.4%{ opacity:1; transform: translateX(var(--dist, 20px)); }
  81.8%{ opacity:1; transform: translateX(calc(var(--dist, 20px) * -0.4)); }
  83.2%, 100%{ opacity:0; transform: translateX(0); }
}
@media (prefers-reduced-motion: reduce){
  .glitch-slice{ display:none; }
}

.statement-photo::after{
  content:""; position:absolute; inset:0; background: rgba(6,6,5,0.55);
}
/* Staggered, left-aligned quote — each fragment is its own line with a
   small horizontal offset, so it reads as caught mid-fall rather than
   sitting in one centered block. Order stays top-to-bottom legible. */
.statement-photo blockquote{
  position:absolute; left:26px; right:8%; bottom:34px; z-index:2; margin:0;
  text-align:left;
  font-family: var(--font-display); text-transform: uppercase; letter-spacing: 0.02em;
  font-size: clamp(1.25rem, 3.6vw, 2.3rem); line-height: 1.3; color: var(--fg);
  text-shadow: 0 3px 20px rgba(0,0,0,0.8);
}
.statement-photo blockquote .qline{ display:block; }
.statement-photo blockquote .qline:nth-child(1){ margin-left: 0; }
.statement-photo blockquote .qline:nth-child(2){ margin-left: 11%; margin-top: 3px; }
.statement-photo blockquote .qline:nth-child(3){ margin-left: 4%; margin-top: 2px; }
.statement-photo blockquote em{ color: var(--hot); font-style: normal; }
.statement-photo blockquote cite{
  display:block; margin-top:18px; margin-left: 6%;
  font-family: var(--font-ui); font-size: 0.68rem; letter-spacing: 0.14em; color: var(--muted); font-style:normal;
}
@media (max-width: 560px){
  .statement-photo blockquote .qline:nth-child(2){ margin-left: 5%; }
  .statement-photo blockquote .qline:nth-child(3){ margin-left: 2%; }
}

/* ================= SECTION LABEL (minimal, no box) ================= */
.section-label{
  display:flex; align-items:baseline; gap: 14px; flex-wrap:wrap;
  padding: 70px 24px 30px;
  max-width: 1180px; margin: 0 auto;
}
.section-label h2{
  position: relative;
  font-family: var(--font-display); text-transform: uppercase; letter-spacing: 0.03em;
  font-size: clamp(1.8rem, 4.4vw, 2.8rem); margin: 0;
}
.section-label .section-index{ font-family: var(--font-ui); font-size: 0.7rem; color: var(--muted); letter-spacing: 0.08em; }

/* Heading redaction — fires once, when a section label scrolls into view.
   Solid bars cover the heading like a censored document, then lift away
   to reveal it, with a brief pixel-jitter on the type as they clear.
   Monochrome — the ongoing colour-split tear lives in .glitch-rgb below,
   layered on afterwards; this entrance stays a clean reveal. */
.section-label h2{ overflow: visible; }
.redact-bar{
  position:absolute; left:-2%; width:104%; height:34%;
  background: var(--bg); z-index: 3; transform-origin: left center;
  transform: scaleX(1);
}
.section-label.in .redact-bar{
  animation: redact-lift 0.55s cubic-bezier(.7,0,.2,1) forwards;
}
@keyframes redact-lift{
  0%, 30%{ transform: scaleX(1); }
  100%{ transform: scaleX(0); }
}
.section-label.in h2{ animation: heading-jitter 0.55s steps(4,end) 1; }
@keyframes heading-jitter{
  0%{ transform: translate(0,0); filter: none; }
  20%{ transform: translate(2px,0); }
  40%{ transform: translate(-3px,0); filter: blur(0.3px); }
  60%{ transform: translate(2px,0); }
  80%{ transform: translate(-1px,0); filter: none; }
  100%{ transform: translate(0,0); }
}
@media (prefers-reduced-motion: reduce){
  .redact-bar{ display:none; }
  .section-label.in h2{ animation: none; }
}

/* Inline [REDACTED] treatment — a permanent censor bar over the literal
   bracketed word wherever it appears in the copy, with a faint pixel
   texture along the edge. The word is still in the DOM underneath. */
.redact-inline{
  position:relative; display:inline-block; color:transparent; user-select:none;
}
.redact-inline::before{
  content:""; position:absolute; inset: -1px -3px;
  background: var(--fg); opacity: 0.9;
  background-image: repeating-linear-gradient(90deg, rgba(6,6,5,0.12) 0 2px, transparent 2px 4px);
}

/* ================= MANIFESTO ================= */
.manifesto{ padding-bottom: 20px; }
.manifesto .prose p{ font-size: 1.06rem; color: var(--fg); margin: 0 0 17px; }
.manifesto .prose p:first-of-type{ font-size: 1.18rem; }
.manifesto .quotes{
  font-family: var(--font-ui); font-size: 0.85rem; color: var(--muted);
  border-left: 2px solid var(--hot); padding-left: 16px;
  display:flex; flex-direction:column; gap: 8px; margin: 26px 0;
}
.manifesto .redacted{ font-family: var(--font-ui); font-size: 0.72rem; color: var(--muted); }
.manifesto .redacted b{ background: var(--fg); color: var(--fg); padding: 0 8px; user-select:none; }

/* ================= SONGS ================= */
.track-list{ list-style:none; margin: 0; padding: 0; max-width: 1180px; margin: 0 auto; border-top: 1px solid var(--line); }
.track-list li{
  display:flex; align-items:baseline; justify-content:space-between; gap: 16px;
  padding: 18px 24px; border-bottom: 1px solid var(--line);
}
.track-list .t-name{ font-family: var(--font-display); text-transform: uppercase; letter-spacing: 0.02em; font-size: 1.3rem; }
.track-list .t-meta{ font-family: var(--font-ui); font-size: 0.68rem; color: var(--muted); letter-spacing: 0.06em; white-space: nowrap; }

.sc-embed{ margin-top: 6px; }
.sc-embed iframe{ width:100%; border:0; display:block; }
.sc-label{
  font-family: var(--font-ui); font-size: 0.64rem; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--muted); padding: 10px 24px 0; max-width: 1180px; margin: 0 auto;
}
.songs-links{ display:flex; gap: 18px; flex-wrap: wrap; padding: 22px 24px 10px; max-width: 1180px; margin: 0 auto; }
.songs-links a{
  font-family: var(--font-ui); font-weight:700; font-size: 0.72rem; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--fg); border-bottom: 1px solid var(--line); padding-bottom: 3px;
}
.songs-links a:hover{ border-color: var(--hot); color: var(--hot); }

/* ================= VIDEOS — full-bleed stack, not a card grid ================= */
.video-block{ position: relative; }
.video-block .ratio{
  position:relative; width:100%; padding-top: 56.25%; background:#100f0c; overflow:hidden;
}
.video-block .ratio iframe, .video-block .ratio video{
  position:absolute; inset:0; width:100%; height:100%; border:0; object-fit:cover;
}
.video-block .play-cta{
  position:absolute; inset:0; display:flex; align-items:center; justify-content:center;
  flex-direction:column; gap:12px; background: rgba(6,6,5,0.42); cursor:pointer;
  font-family: var(--font-ui); font-weight:700; font-size: 0.72rem; letter-spacing: 0.12em; text-transform: uppercase;
}
.video-block .play-cta .ring{
  width: 62px; height:62px; border: 2px solid var(--fg); border-radius: 50%;
  display:flex; align-items:center; justify-content:center; background: rgba(6,6,5,0.35);
}
.video-block .play-cta .ring::before{
  content:""; border-style: solid; border-width: 10px 0 10px 16px;
  border-color: transparent transparent transparent var(--fg); margin-left: 4px;
}
.video-block .v-meta{
  display:flex; justify-content:space-between; align-items:baseline; gap: 10px;
  padding: 14px 24px 40px; max-width: 1180px; margin: 0 auto;
}
.video-block .v-meta h3{ font-family: var(--font-display); text-transform: uppercase; letter-spacing: 0.02em; font-size: 1.15rem; margin:0; }
.video-block .v-meta span{ font-family: var(--font-ui); font-size: 0.66rem; color: var(--muted); letter-spacing: 0.08em; }

/* ================= SCATTER — layered photo + video interludes =================
   Real shots and clips dropped between sections, each boxed in a small
   window-style frame — thin title bar, corner "lights", hard edges — and
   overlapping like stacked windows rather than tilted loose prints.
   content/scatter.json. */
.scatter-zone{ position:relative; height: 560px; overflow:hidden; }
.scatter-inner{ position:absolute; inset:0; }
@media (max-width: 780px){
  /* The cascade is laid out for a wide desktop canvas — rather than
     recompute every frame's position for narrow screens, shrink the
     whole arrangement as one unit so it keeps its layered-window feel
     instead of frames running off the right edge. */
  .scatter-zone{ height: 330px; }
  .scatter-inner{ transform: scale(0.6); transform-origin: top left; }
}
@media (max-width: 480px){
  .scatter-zone{ height: 260px; }
  .scatter-inner{ transform: scale(0.46); }
}
.scatter-photo{
  position:absolute; display:flex; flex-direction:column;
  background: #0c0a08; border: 1px solid var(--line);
  box-shadow: 0 28px 54px -16px rgba(0,0,0,0.75), 0 0 0 1px rgba(0,0,0,0.35);
}
.scatter-photo .win-bar{
  flex: 0 0 auto; display:flex; align-items:center; gap: 6px;
  height: 22px; padding: 0 9px;
  background: #141210; border-bottom: 1px solid var(--line);
}
.scatter-photo .win-dot{ width:6px; height:6px; background: var(--line); display:block; }
.scatter-photo .win-dot:first-child{ background: var(--hot); }
.scatter-photo .win-label{
  margin-left: auto; font-family: var(--font-ui); font-size: 0.55rem;
  letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.scatter-photo .win-body{ position:relative; overflow:hidden; }
.scatter-photo .win-body .img-pixelate img,
.scatter-photo .win-body .img-pixelate video{ object-position: center 30%; }
@media (prefers-reduced-motion: no-preference){
  .scatter-photo{ transition: transform .3s ease, box-shadow .3s ease; }
  .scatter-photo:hover{
    transform: translateY(-4px);
    box-shadow: 0 36px 64px -14px rgba(0,0,0,0.85), 0 0 0 1px rgba(0,0,0,0.35);
    z-index: 99 !important;
  }
}

/* ================= TICKERS =================
   Scrolling banners, woven between sections rather than parked in one
   spot — content/lyrics.json and content/truths.json. Old-web marquee,
   MySpace-adjacent, but built properly: CSS transform loop, duplicated
   content for a seamless wrap, paused for reduced-motion. */
.ticker{
  overflow: hidden; white-space: nowrap; position: relative;
  border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
}
.ticker::before{
  content:""; position:absolute; inset:0; pointer-events:none; z-index:1;
  background: repeating-linear-gradient(180deg, rgba(255,255,255,0.04) 0px, rgba(255,255,255,0.04) 1px, transparent 1px, transparent 3px);
}
.ticker-track{
  display:inline-flex; align-items:center; gap: 48px; padding: 11px 0;
  animation: ticker-scroll linear infinite; will-change: transform;
}
.ticker-item{ display:inline-flex; align-items:baseline; gap:10px; white-space:nowrap; }
@keyframes ticker-scroll{
  from{ transform: translateX(0); }
  to{ transform: translateX(-50%); }
}
@media (prefers-reduced-motion: reduce){
  .ticker-track{ animation: none; }
}

/* Stuck-record skip — the lightest dose of the glitch language, since
   these already move constantly on their own. One item at a time
   catches, stutters in place a couple of times, then keeps scrolling.
   Staggered per position so the whole marquee never skips in unison. */
.ticker-item{ animation: ticker-skip 4.2s infinite steps(1,end); filter: none; }
.ticker-item:nth-child(4n+1){ animation-delay: 0s; }
.ticker-item:nth-child(4n+2){ animation-delay: 1.05s; }
.ticker-item:nth-child(4n+3){ animation-delay: 2.1s; }
.ticker-item:nth-child(4n+4){ animation-delay: 3.15s; }
@keyframes ticker-skip{
  0%, 86%{ transform: translateX(0); opacity:1; filter:none; }
  86.6%{ transform: translateX(-13px); opacity:.35; filter: hue-rotate(30deg); }
  87.2%{ transform: translateX(7px); opacity:1; filter:none; }
  87.8%{ transform: translateX(-13px); opacity:.35; filter: hue-rotate(-30deg); }
  88.4%{ transform: translateX(7px); opacity:1; filter:none; }
  89%, 100%{ transform: translateX(0); opacity:1; filter:none; }
}
@media (prefers-reduced-motion: reduce){
  .ticker-item{ animation: none; }
}

.ticker--lyrics{ background: #0c0a07; }
.ticker--lyrics .ticker-track{ animation-duration: 46s; }
.ticker--lyrics .ticker-item{
  font-family: var(--font-display); text-transform: uppercase; letter-spacing: 0.03em;
  font-size: 0.98rem; color: var(--amber); opacity: 0.92;
}
.ticker--lyrics .ticker-item .song{
  font-family: var(--font-ui); font-size: 0.58rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--muted);
}
.ticker--lyrics .ticker-item::after{ content:"◆"; margin-left:44px; color: var(--line); font-size:0.55rem; }

.ticker--truths{ background: #150605; border-color: rgba(255,47,47,0.32); }
.ticker--truths .ticker-track{ animation-duration: 32s; }
.ticker--truths .ticker-item{
  font-family: var(--font-ui); font-weight:700; text-transform: uppercase; letter-spacing: 0.04em;
  font-size: 0.72rem; color: #ffb0ac;
}
.ticker--truths .ticker-item .tag{
  display:inline-flex; align-items:center; gap:6px; background: var(--hot); color:#0c0605;
  padding: 2px 7px; font-size:0.58rem; letter-spacing:0.08em; font-weight:700;
}
.ticker--truths .ticker-item .tag .dot{ width:5px; height:5px; border-radius:50%; background:#0c0605; animation: pulse 1.2s ease-in-out infinite; }
.ticker--truths .ticker-item a{ color: inherit; }
.ticker--truths .ticker-item a:hover{ color: var(--fg); }
.ticker--truths .ticker-item::after{ content:"—"; margin-left:36px; color: rgba(255,47,47,0.4); }

/* ================= WORDS ================= */
.words-list{ max-width: 1180px; margin: 0 auto; padding: 0 24px; }
.word-row{
  display:flex; justify-content:space-between; align-items:baseline; gap: 20px; flex-wrap:wrap;
  padding: 22px 0; border-bottom: 1px solid var(--line); cursor:pointer;
}
.word-row:hover .word-title{ color: var(--hot); }
.word-row .kind{ font-family: var(--font-ui); font-weight:700; font-size: 0.64rem; letter-spacing: 0.1em; text-transform:uppercase; color: var(--muted); }
.word-title{ font-family: var(--font-display); text-transform: uppercase; letter-spacing: 0.02em; font-size: 1.4rem; transition: color .2s ease; }
.word-row .read-more{ font-family: var(--font-ui); font-weight:700; font-size: 0.64rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted); }

.word-full{
  display:none; max-width: 900px; margin: 24px auto 0; padding: 0 24px 10px;
  white-space: pre-line; font-family: var(--font-body); font-size: 1.05rem; line-height: 1.9;
}
.word-full.open{ display:block; }
.word-full .close-word{ float:right; font-family: var(--font-ui); font-weight:700; font-size: 0.7rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted); cursor:pointer; background:none; border:none; }
.word-full .draft-flag{
  display:inline-block; margin-bottom: 16px; font-family: var(--font-ui); font-size: 0.64rem; font-weight:700;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--bg); background: var(--amber); padding: 3px 9px;
}

/* ================= CONTACT ================= */
.contact-links{ max-width: 1180px; margin: 0 auto; padding: 0 24px; display:flex; flex-direction:column; }
.contact-links a{
  font-family: var(--font-display); text-transform: uppercase; font-size: clamp(1.6rem, 5vw, 2.6rem);
  letter-spacing: 0.02em; border-bottom: 1px solid var(--line); padding: 20px 0;
  display:flex; justify-content:space-between; align-items:baseline; gap: 14px;
  transition: color .2s ease, border-color .2s ease;
}
.contact-links a:hover{ color: var(--hot); border-color: var(--hot); }
.contact-links a small{ font-family: var(--font-ui); font-size: 0.62rem; color: var(--muted); letter-spacing: 0.06em; text-transform: none; }
.contact-note{
  font-family: var(--font-ui); font-size: 0.82rem; color: var(--muted); line-height: 1.85;
  max-width: 1180px; margin: 26px auto 0; padding: 0 24px;
}
.contact-note b{ color: var(--fg); }

/* ================= FOOTER ================= */
footer{ padding: 30px 24px 50px; text-align:center; }
.footer-mark{ height: 44px; margin: 0 auto 22px; opacity: 0.85; }
footer .foot-row{
  max-width: 1180px; margin: 0 auto; display:flex; justify-content:space-between; align-items:center;
  flex-wrap:wrap; gap: 10px; font-family: var(--font-ui); font-size: 0.64rem; letter-spacing: 0.06em;
  color: var(--muted); text-transform: uppercase; border-top: 1px solid var(--line); padding-top: 22px;
}
footer .complaints{ opacity: .55; }

/* ================= misc ================= */
.reveal{ opacity: 0; transform: translateY(14px); transition: opacity .7s ease, transform .7s ease; }
.reveal.in{ opacity:1; transform: translateY(0); }

@media (prefers-reduced-motion: reduce){
  html{ scroll-behavior:auto; }
  .reveal{ opacity:1; transform:none; transition:none; }
  .now-playing .bars span{ animation: none; }
}

/* ================= GLITCH POPUPS — layered windows springing out of pixelation =================
   Small floating cards spawned by app.js at random points on screen:
   either a photo resolving pixel -> sharp, or a lyric/truths fragment
   resolving out of a character-scramble. Fixed to the viewport (so they
   sit ON TOP of everything, genuinely layered) and pointer-events:none
   throughout (pure atmosphere — never intercepts a real click). */
.glitch-popup{
  position:fixed; z-index:600; pointer-events:none;
  width: var(--pw, 160px);
  background: #0b0a08;
  border: 1px solid rgba(242,237,226,0.45);
  box-shadow: 0 14px 44px rgba(0,0,0,0.65), 3px 0 0 rgba(255,47,47,0.4), -3px 0 0 rgba(47,208,255,0.35);
}
.glitch-popup .tag{
  display:block; padding: 5px 10px; font-family: var(--font-ui); font-size: 0.56rem;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--hot);
  border-bottom: 1px solid rgba(242,237,226,0.15);
}
.glitch-popup .txt{
  display:block; padding: 10px 12px 12px; font-family: var(--font-ui); font-size: 0.68rem;
  line-height: 1.45; color: var(--fg); letter-spacing: 0.01em;
}
.glitch-popup .frame{ position:relative; width:100%; aspect-ratio: 4 / 3; overflow:hidden; background:#111; }
.glitch-popup .frame img{
  position:absolute; inset:0; width:100%; height:100%; object-fit:cover;
}
.glitch-popup .frame .pixel-layer{ image-rendering:pixelated; }
.glitch-popup .frame .sharp-layer{ opacity:0; transition: opacity 0.6s ease; }
.glitch-popup .frame .sharp-layer.shown{ opacity:1; }

.glitch-popup.spawning{ animation: popup-spawn 0.5s steps(6,end) forwards; }
.glitch-popup.holding{ opacity:1; }
.glitch-popup.dissolving{ animation: popup-dissolve 0.4s steps(5,end) forwards; }

@keyframes popup-spawn{
  0%{ opacity:0; clip-path: inset(42% 0 42% 0); transform: translateX(0); }
  18%{ opacity:1; clip-path: inset(8% 0 62% 0); transform: translateX(-7px); }
  36%{ opacity:.65; clip-path: inset(58% 0 4% 0); transform: translateX(6px); }
  54%{ opacity:1; clip-path: inset(0 0 34% 0); transform: translateX(-4px); }
  72%{ opacity:1; clip-path: inset(22% 0 0 0); transform: translateX(3px); }
  100%{ opacity:1; clip-path: inset(0 0 0 0); transform: translateX(0); }
}
@keyframes popup-dissolve{
  0%{ opacity:1; clip-path: inset(0 0 0 0); transform: translateX(0); }
  25%{ opacity:.8; clip-path: inset(0 0 48% 0); transform: translateX(6px); }
  50%{ opacity:.45; clip-path: inset(55% 0 0 0); transform: translateX(-7px); }
  75%{ opacity:.2; clip-path: inset(20% 0 30% 0); transform: translateX(4px); }
  100%{ opacity:0; clip-path: inset(42% 0 42% 0); transform: translateX(0); }
}
@media (prefers-reduced-motion: reduce), (max-width: 640px){
  .glitch-popup{ display:none; }
}
