/* ─────────────────────────────────────────────────────────────────────────
   M — Cabinet
   Tokens + base. Two palettes (nocturnal default, paper alt) swap via
   a single [data-palette] attr on <html>. Accent is a CSS var, swappable
   independently. Type is two faces only: display serif + mono.
   ───────────────────────────────────────────────────────────────────────── */

:root {
  /* tar (default) — pitch black bg, bone-paper fg, electric blood-red */
  --bg:        #050505;
  --bg-2:      #0c0a0a;
  --fg:        #e8e1cf;
  --fg-2:      rgba(232, 225, 207, 0.78);
  --fg-3:      rgba(232, 225, 207, 0.50);
  --fg-4:      rgba(232, 225, 207, 0.28);
  --rule:      rgba(232, 225, 207, 0.22);
  --rule-2:    rgba(232, 225, 207, 0.10);
  --accent:    #d6261c;

  --display:   "Big Shoulders Stencil Display", "Big Shoulders Display", ui-sans-serif, sans-serif;
  --mono:      "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  --display-italic: normal;
  --display-weight: 700;
  --display-tracking: -0.01em;

  --edge:      1;
  --rule-w:    1.5px;
  --grain:     0.12;
  /* Glow tint — decoupled from --accent so changing accent color
     doesn't affect the MARIO halo. Defaults to --fg (white on dark
     palettes, dark on light). Chalk overrides to transparent because
     its dark halo is drawn via a separate filter:drop-shadow. */
  --glow-tint: var(--fg);

  --mx: 0;
  --my: 0;
  --near: 0;
  --jitter: 0;
}

html[data-palette="void"] {
  /* neutral near-black + near-white — no warmth */
  --bg:        #080808;
  --bg-2:      #111111;
  --fg:        #fafafa;
  --fg-2:      rgba(250, 250, 250, 0.78);
  --fg-3:      rgba(250, 250, 250, 0.50);
  --fg-4:      rgba(250, 250, 250, 0.26);
  --rule:      rgba(250, 250, 250, 0.22);
  --rule-2:    rgba(250, 250, 250, 0.10);
  --accent:    #fafafa;
}

html[data-palette="chalk"] {
  /* neutral near-white + near-black */
  --bg:        #fafafa;
  --bg-2:      #f0f0f0;
  --fg:        #0a0a0a;
  --fg-2:      rgba(10, 10, 10, 0.78);
  --fg-3:      rgba(10, 10, 10, 0.52);
  --fg-4:      rgba(10, 10, 10, 0.28);
  --rule:      rgba(10, 10, 10, 0.30);
  --rule-2:    rgba(10, 10, 10, 0.12);
  --accent:    #0a0a0a;
  --glow-tint: transparent;
}

html[data-palette="bone"] {
  /* photocopier zine inverse — bone bg, ink fg, blood accent */
  --bg:        #ece4d2;
  --bg-2:      #e0d7c2;
  --fg:        #0a0908;
  --fg-2:      rgba(10, 9, 8, 0.78);
  --fg-3:      rgba(10, 9, 8, 0.52);
  --fg-4:      rgba(10, 9, 8, 0.28);
  --rule:      rgba(10, 9, 8, 0.30);
  --rule-2:    rgba(10, 9, 8, 0.12);
  --accent:    #c41b0f;
}

html[data-palette="sulphur"] {
  /* hardcore-flyer crossover — black bg, bone fg, sulfuric yellow accent */
  --bg:        #060604;
  --bg-2:      #0d0c08;
  --fg:        #ece4d2;
  --fg-2:      rgba(236, 228, 210, 0.78);
  --fg-3:      rgba(236, 228, 210, 0.50);
  --fg-4:      rgba(236, 228, 210, 0.28);
  --rule:      rgba(236, 228, 210, 0.22);
  --rule-2:    rgba(236, 228, 210, 0.10);
  --accent:    #e8d013;
}

html[data-palette="nocturnal"] {
  /* refined alt — from the previous direction */
  --bg:        #0b0a08;
  --bg-2:      #0f0e0b;
  --fg:        #ece4d2;
  --fg-2:      rgba(236, 228, 210, 0.72);
  --fg-3:      rgba(236, 228, 210, 0.46);
  --fg-4:      rgba(236, 228, 210, 0.26);
  --rule:      rgba(236, 228, 210, 0.14);
  --rule-2:    rgba(236, 228, 210, 0.06);
  --accent:    #c5523f;
}

html[data-palette="paper"] {
  --bg:        #ece4d2;
  --bg-2:      #e4dcc8;
  --fg:        #15120d;
  --fg-2:      rgba(21, 18, 13, 0.74);
  --fg-3:      rgba(21, 18, 13, 0.48);
  --fg-4:      rgba(21, 18, 13, 0.26);
  --rule:      rgba(21, 18, 13, 0.18);
  --rule-2:    rgba(21, 18, 13, 0.08);
  --accent:    #8a2a1f;
}

html[data-palette="vellum"] {
  --bg:        #161310;
  --bg-2:      #1c1814;
  --fg:        #d6c9ad;
  --fg-2:      rgba(214, 201, 173, 0.72);
  --fg-3:      rgba(214, 201, 173, 0.46);
  --fg-4:      rgba(214, 201, 173, 0.24);
  --rule:      rgba(214, 201, 173, 0.16);
  --rule-2:    rgba(214, 201, 173, 0.06);
  --accent:    #b88a4a;
}

* { box-sizing: border-box; margin: 0; padding: 0; }

html, body {
  background: var(--bg);
  color: var(--fg);
  font-family: var(--mono);
  font-size: 14px;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}

body {
  min-height: 100vh;
  background: transparent;
  /* No body bg-image: keeps body transparent so the body::before/::after
     plasma layers (z-index: -1) can show against the html bg. */
}

/* photocopier grain overlay — inline SVG fractalNoise.
   The bg image is set per [data-grain-type]; mix-blend-mode flips per palette. */
.grain {
  position: fixed; inset: 0;
  pointer-events: none;
  z-index: 40;
  opacity: var(--grain);
  background-size: 320px 320px;
  mix-blend-mode: overlay;
}
html[data-grain-type="fine"]      .grain { background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='320' height='320'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>"); }
html[data-grain-type="coarse"]    .grain { background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='320' height='320'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.35' numOctaves='1' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>"); background-size: 480px 480px; }
html[data-grain-type="newsprint"] .grain { background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='1.6' numOctaves='1' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>"); background-size: 160px 160px; }
html[data-grain-type="halftone"]  .grain { background-image: radial-gradient(rgba(255,255,255,1) 0.9px, transparent 1.4px); background-size: 5px 5px; }
html[data-grain-type="scanlines"] .grain { background-image: repeating-linear-gradient(0deg, rgba(255,255,255,1) 0 1px, transparent 1px 3px); background-size: auto; }
html[data-grain-type="dither"]    .grain { background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='4' height='4' shape-rendering='crispEdges'><rect x='3' y='0' width='1' height='1' fill='white'/><rect x='0' y='1' width='1' height='1' fill='white'/><rect x='2' y='1' width='1' height='1' fill='white'/><rect x='1' y='2' width='1' height='1' fill='white'/><rect x='3' y='2' width='1' height='1' fill='white'/><rect x='0' y='3' width='1' height='1' fill='white'/><rect x='2' y='3' width='1' height='1' fill='white'/></svg>"); background-size: 4px 4px; image-rendering: pixelated; }

html[data-palette="bone"] .grain,
html[data-palette="paper"] .grain,
html[data-palette="chalk"] .grain { mix-blend-mode: multiply; }
html[data-palette="bone"][data-grain-type="halftone"] .grain,
html[data-palette="paper"][data-grain-type="halftone"] .grain,
html[data-palette="chalk"][data-grain-type="halftone"] .grain { background-image: radial-gradient(rgba(0,0,0,1) 0.9px, transparent 1.4px); }

/* Scanlines on light palettes — use BLACK lines + overlay blend, mirroring
   void's white-lines-over-black behavior. Overlay leaves pure extremes
   (white bg, dark text) untouched and only modulates mid-tones (the dark
   glow halo around MARIO). */
html[data-palette="bone"][data-grain-type="scanlines"] .grain,
html[data-palette="paper"][data-grain-type="scanlines"] .grain,
html[data-palette="chalk"][data-grain-type="scanlines"] .grain {
  mix-blend-mode: overlay;
  background-image: repeating-linear-gradient(0deg, rgba(0,0,0,1) 0 1px, transparent 1px 3px);
}
html[data-palette="bone"][data-grain-type="dither"] .grain,
html[data-palette="paper"][data-grain-type="dither"] .grain,
html[data-palette="chalk"][data-grain-type="dither"] .grain { background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='4' height='4' shape-rendering='crispEdges'><rect x='3' y='0' width='1' height='1' fill='black'/><rect x='0' y='1' width='1' height='1' fill='black'/><rect x='2' y='1' width='1' height='1' fill='black'/><rect x='1' y='2' width='1' height='1' fill='black'/><rect x='3' y='2' width='1' height='1' fill='black'/><rect x='0' y='3' width='1' height='1' fill='black'/><rect x='2' y='3' width='1' height='1' fill='black'/></svg>"); }

/* grain modulation — animates background-position so the pattern drifts/shimmers */
html[data-grain-mod="true"] .grain { animation: grain-mod 2.4s linear infinite; }
@keyframes grain-mod {
  0%   { background-position: 0px 0px; }
  100% { background-position: -64px 32px; }
}
html[data-grain-mod="true"][data-grain-type="dither"]    .grain { animation-duration: 0.6s; animation-timing-function: steps(8); }
html[data-grain-mod="true"][data-grain-type="halftone"]  .grain { animation-duration: 1.2s; }
html[data-grain-mod="true"][data-grain-type="scanlines"] .grain { animation: scan-mod 1.6s linear infinite; }
@keyframes scan-mod {
  0%   { background-position: 0 0;    }
  100% { background-position: 0 -32px; }
}

/* plasma — two flavours via data-plasma-style:
   "lava"  : multiple morphing blobs drifting like a lava lamp (default)
   "core"  : single bright orb behind a backdrop-blur frosted-glass overlay
   The opacity is driven by --plasma (0..1). */
body::before {
  content: "";
  position: fixed;
  inset: -25%;
  pointer-events: none;
  z-index: -1;
  opacity: var(--plasma, 0);
  filter: blur(55px) saturate(200%);
  background:
    radial-gradient(ellipse 44% 38% at 28% 32%, color-mix(in oklab, var(--accent) 100%, transparent), transparent 55%),
    radial-gradient(ellipse 38% 44% at 72% 62%, color-mix(in oklab, var(--accent) 95%, transparent), transparent 55%),
    radial-gradient(ellipse 30% 36% at 16% 72%, color-mix(in oklab, var(--accent) 90%, transparent), transparent 60%),
    radial-gradient(ellipse 34% 28% at 82% 20%, color-mix(in oklab, var(--accent) 95%, transparent), transparent 55%),
    radial-gradient(ellipse 28% 32% at 50% 88%, color-mix(in oklab, var(--accent) 85%, transparent), transparent 55%);
  animation: plasma 22s ease-in-out infinite;
  transition: opacity 600ms, filter 600ms, background 600ms;
}
body::after {
  content: "";
  position: fixed;
  inset: -25%;
  pointer-events: none;
  z-index: -1;
  opacity: calc(var(--plasma, 0) * 0.9);
  filter: blur(45px) saturate(180%);
  background:
    radial-gradient(ellipse 40% 42% at 55% 42%, color-mix(in oklab, var(--accent) 100%, transparent), transparent 55%),
    radial-gradient(ellipse 30% 36% at 12% 38%, color-mix(in oklab, var(--accent) 85%, transparent), transparent 60%),
    radial-gradient(ellipse 36% 32% at 88% 78%, color-mix(in oklab, var(--accent) 95%, transparent), transparent 55%),
    radial-gradient(ellipse 26% 30% at 38% 12%, color-mix(in oklab, var(--accent) 80%, transparent), transparent 60%);
  animation: plasma-2 35s ease-in-out infinite;
  mix-blend-mode: screen;
  transition: opacity 600ms, filter 600ms, background 600ms, backdrop-filter 600ms;
}

/* core flavour: bright defined orb in ::before, frosted-glass overlay in ::after */
html[data-plasma-style="core"] body::before {
  filter: blur(25px) saturate(220%);
  background:
    radial-gradient(circle 36% 36% at 50% 50%, color-mix(in oklab, var(--accent) 100%, transparent), transparent 55%);
}
html[data-plasma-style="core"] body::after {
  z-index: -1;
  background: rgba(0, 0, 0, 0.001);   /* triggers compositing for backdrop-filter */
  -webkit-backdrop-filter: blur(40px) saturate(120%);
  backdrop-filter: blur(40px) saturate(120%);
  opacity: var(--plasma, 0);
  filter: none;
  animation: plasma-2 35s ease-in-out infinite;
  mix-blend-mode: normal;
}

@keyframes plasma {
  0%, 100% { transform: translate(0%, 0%)  scale(1)    rotate(0deg); }
  20%      { transform: translate(-7%, 6%) scale(1.12) rotate(2deg); }
  40%      { transform: translate(6%, -8%) scale(0.86) rotate(-3deg); }
  60%      { transform: translate(-4%, 9%) scale(1.10) rotate(2deg); }
  80%      { transform: translate(5%, -4%) scale(0.94) rotate(-1deg); }
}
@keyframes plasma-2 {
  0%, 100% { transform: translate(0%, 0%) scale(1)    rotate(0deg); }
  25%      { transform: translate(9%, -7%) scale(1.18) rotate(-3deg); }
  50%      { transform: translate(-8%, 10%) scale(0.82) rotate(4deg); }
  75%      { transform: translate(6%, 8%)  scale(1.13) rotate(-2deg); }
}
@media (prefers-reduced-motion: reduce) {
  body::before, body::after { animation: none; }
  html[data-grain-mod="true"] .grain { animation: none; }
}

/* ─── anamorphic lens flare ───────────────────────────────────────────── */
/* Two stacked horizontal streaks: a wide blurry halo + a thin bright inner line.
   Both monochrome (fg) so they don't break the no-color brief. */
html[data-flare="true"] .mark-wrap::before {
  content: "";
  position: absolute;
  top: 50%; left: 50%;
  width: 240%;
  height: 8%;
  transform: translate(-50%, -50%);
  background: radial-gradient(ellipse 50% 50% at 50% 50%, color-mix(in oklab, var(--fg) 70%, transparent), transparent 80%);
  filter: blur(34px);
  opacity: 0.7;
  pointer-events: none;
  mix-blend-mode: screen;
  z-index: 0;
}
html[data-flare="true"] .mark-wrap::after {
  content: "";
  position: absolute;
  top: 50%; left: 50%;
  width: 180%;
  height: 1.6%;
  transform: translate(-50%, -50%);
  background: linear-gradient(90deg, transparent 6%, var(--fg) 42%, white 50%, var(--fg) 58%, transparent 94%);
  filter: blur(3.5px);
  opacity: 0.95;
  pointer-events: none;
  mix-blend-mode: screen;
  z-index: 0;
}
html[data-palette="bone"][data-flare="true"] .mark-wrap::before,
html[data-palette="paper"][data-flare="true"] .mark-wrap::before,
html[data-palette="chalk"][data-flare="true"] .mark-wrap::before {
  mix-blend-mode: multiply;
}
html[data-palette="bone"][data-flare="true"] .mark-wrap::after,
html[data-palette="paper"][data-flare="true"] .mark-wrap::after,
html[data-palette="chalk"][data-flare="true"] .mark-wrap::after {
  mix-blend-mode: multiply;
  background: linear-gradient(90deg, transparent 6%, var(--fg) 42%, black 50%, var(--fg) 58%, transparent 94%);
}

/* ─── static glitch ─────────────────────────────────────────────────────── */
/* Periodic TV-static burst that flashes across the whole page every few seconds.
   Uses html::after so it sits above everything but pointer-events: none. */
html[data-static="true"]::after {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 42;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='480' height='480'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>");
  background-size: 480px 480px;
  opacity: 0;
  mix-blend-mode: difference;
  animation: static-burst 5.2s infinite steps(1);
}
@keyframes static-burst {
  0%, 88%   { opacity: 0; background-position: 0 0; }
  89%       { opacity: 0.55; background-position: 17px 11px; }
  89.4%     { opacity: 0; }
  89.8%     { opacity: 0.35; background-position: -23px -7px; }
  90.2%     { opacity: 0; }
  96%       { opacity: 0.42; background-position: 41px -19px; }
  96.4%, 100% { opacity: 0; }
}

/* ─── decals ──────────────────────────────────────────────────────────── */
/* SVG decorative graphics. All rendered with currentColor so they stay
   monochrome with the foreground. Hidden by default; shown per data-decals. */
.decal { display: none; color: var(--fg); pointer-events: none; }
html[data-decals="thunder"] .decal-thunder,
html[data-decals="all"]     .decal-thunder { display: block; }
html[data-decals="cable"]   .decal-cable,
html[data-decals="all"]     .decal-cable   { display: inline-flex; }
html[data-decals="thorns"]  .decal-thorn,
html[data-decals="all"]     .decal-thorn   { display: block; }
html[data-decals="scrawl"]  .decal-scrawl,
html[data-decals="all"]     .decal-scrawl  { display: block; }

/* hide the default frame crosshairs when thunderbolts take their place */
html[data-decals="thunder"] .frame .x,
html[data-decals="all"]     .frame .x { display: none; }

.decal-thunder { position: absolute; width: 14px; height: 26px; }
.decal-thunder.tl { top: 14px;    left: 14px;  }
.decal-thunder.tr { top: 14px;    right: 14px; transform: scaleX(-1); }
.decal-thunder.bl { bottom: 14px; left: 14px;  transform: scaleY(-1); }
.decal-thunder.br { bottom: 14px; right: 14px; transform: scale(-1, -1); }

.decal-cable { align-items: center; gap: 10px; color: var(--fg-3); }
.decal-cable svg { width: 64px; height: 16px; flex-shrink: 0; }

.decal-thorn { width: 100%; height: 9px; color: var(--accent); margin: 14px 0 6px; }

.decal-scrawl {
  position: absolute;
  left: 0;
  bottom: -8px;
  width: 100%;
  height: 18px;
  color: var(--accent);
  pointer-events: none;
}
.motto-block {
  position: relative;
  /* Horizontal divider above REEL, plus headroom so the live-wire cursor
     canvas (inset:0) extends well above the text instead of cutting off
     right at it. */
  border-top: var(--rule-w) solid var(--rule);
  padding-top: 76px;
}

/* Custom cursor — chunky jagged arrow (Metal Mania vibe). Black fill + white
   outline so it stays visible on both void (dark) and chalk (light). The
   live-wire sections override this with cursor:none. */
html, body {
  cursor: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M2 2 L1 4 L2.4 6 L1 8 L2.4 10 L1 12 L2.4 14 L1 16 L2 18 L6.5 14 L10 22 L13 20.5 L9.5 13 L16 13 L14.87 10.84 L12.25 10.56 L11.37 8.09 L8.75 7.81 L7.87 5.34 L5.25 5.06 L4.37 2.59 L2 2 Z' fill='%230b0b0b' stroke='%23ffffff' stroke-width='1.4' stroke-linejoin='miter'/%3E%3C/svg%3E") 2 2, auto;
}

/* ─── Lunar cycle dial (hero aside widget) ────────────────────────────────
   Astrolabe-style HALF dial replacing the INDEX block. Top-semicircle
   arc + baseline + 7 phase positions + ornate pointer above + small
   pivot pin at the diameter midpoint. Today's moon sits at the apex
   (top-center) with an accent halo. Etched by SVG turbulence so nothing
   reads as vector-perfect. */
.lunarium {
  display: flex;
  flex-direction: column;
  gap: 10px;
  align-items: flex-start;
  margin-top: 3px;
}
.lunarium__dial {
  position: relative;
  /* Container exactly matches the SVG viewBox dimensions so SVG coords
     line up 1:1 with CSS pixels — moon left/top values are absolute.
     Wider + shorter than before: shallow 120° arc instead of 180°.
     Bumped top margin so the pointer pin + apex moon get breathing
     room from the "LUNAR CYCLE" header above. */
  width: 200px;
  height: 80px;
  margin: 30px 0 4px;
  color: var(--fg-3);
}
.lunarium__arc {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
}
.lunarium__pointer {
  position: absolute;
  /* Raised above the apex so it reads as a separate "now" pin rather
     than sitting on top of the moon. */
  top: -14px;
  left: 100px;
  transform: translateX(-50%);
  width: 14px;
  height: 10px;
  color: var(--accent);
  z-index: 3;
  filter:
    drop-shadow(0 0 4px  color-mix(in oklab, var(--accent) 70%, transparent))
    drop-shadow(0 0 12px color-mix(in oklab, var(--accent) 40%, transparent));
}
.lunarium__pointer svg { display: block; width: 100%; height: 100%; }
.lunarium__pivot {
  position: absolute;
  /* Sits at the midpoint of the baseline (where the two endpoint moons
     project down to) — the imagined axis. */
  left: 100px;
  top: 70px;
  transform: translate(-50%, -50%);
  width: 10px;
  height: 10px;
  color: var(--fg-3);
  z-index: 1;
}
.lunarium__pivot svg { display: block; width: 100%; height: 100%; }

/* Each moon slot is positioned at its computed (cx, cy) in the dial
   container; the translate centers the slot on that point. */
.moon-slot {
  position: absolute;
  transform: translate(-50%, -50%);
  color: var(--fg-3);
  opacity: 0.6;
  transition: opacity 240ms ease, filter 240ms ease;
  z-index: 2;
}
.moon-slot:hover { opacity: 0.95; }
.moon-slot--current {
  color: var(--fg);
  opacity: 1;
  filter:
    drop-shadow(0 0 6px  color-mix(in oklab, var(--accent) 65%, transparent))
    drop-shadow(0 0 18px color-mix(in oklab, var(--accent) 38%, transparent))
    drop-shadow(0 0 36px color-mix(in oklab, var(--accent) 20%, transparent));
}

/* Label + date sit together on the block's header row; date sits right
   after the label (separated by the gap), so it reads as part of the
   title line rather than a far-right meta strip. */
.lunarium__head {
  display: flex;
  align-items: baseline;
  justify-content: flex-start;
  gap: 10px;
}
.lunarium__date {
  font-size: 10px;
  letter-spacing: 0.22em;
  color: var(--fg-4);
  white-space: nowrap;
}

/* ─── Twinkles ────────────────────────────────────────────────────────────
   Small 4-point stars radiating from the apex moon. Invisible at rest;
   on hover of .lunarium they fade in + twinkle in staggered loops. Each
   star's left/top is set inline (in container coords); the translate
   centers it on that point, and the animation scales it from a small
   point through a bright peak back down. */
.lunarium__star {
  position: absolute;
  transform: translate(-50%, -50%) scale(0.3);
  opacity: 0;
  color: var(--fg);
  pointer-events: none;
  z-index: 1;
}
.lunarium__star svg { width: 100%; height: 100%; display: block; }

.lunarium:hover .lunarium__star {
  animation: lunarium-twinkle 2380ms ease-in-out infinite;
}
/* Faint edge stars use a lower-peak keyframe — gives a "further away"
   feel by capping max opacity around half. Same timing, same shape. */
.lunarium:hover .lunarium__star.lunarium__star--faint {
  animation: lunarium-twinkle-faint 2940ms ease-in-out infinite;
}

@keyframes lunarium-twinkle {
  0%   { opacity: 0;    transform: translate(-50%, -50%) scale(0.3); }
  18%  { opacity: 0.9;  transform: translate(-50%, -50%) scale(1); }
  42%  { opacity: 0.5;  transform: translate(-50%, -50%) scale(0.85); }
  68%  { opacity: 1;    transform: translate(-50%, -50%) scale(1.1); }
  100% { opacity: 0;    transform: translate(-50%, -50%) scale(0.3); }
}
@keyframes lunarium-twinkle-faint {
  0%   { opacity: 0;    transform: translate(-50%, -50%) scale(0.3); }
  20%  { opacity: 0.35; transform: translate(-50%, -50%) scale(0.9); }
  50%  { opacity: 0.18; transform: translate(-50%, -50%) scale(0.75); }
  72%  { opacity: 0.45; transform: translate(-50%, -50%) scale(1); }
  100% { opacity: 0;    transform: translate(-50%, -50%) scale(0.3); }
}

@media (prefers-reduced-motion: reduce) {
  .lunarium:hover .lunarium__star { animation: none; opacity: 0; }
}

a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: default; }

.mono   { font-family: var(--mono); letter-spacing: 0.02em; }
.serif  { font-family: var(--display); font-feature-settings: "ss01", "liga"; }

/* tiny label — used everywhere for marginalia */
.lbl {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--fg-3);
  font-weight: 500;
}

/* Small engraved glyph nested before a marginalia label. Inline-aligned
   with the cap height of the label text. Uses currentColor so it
   matches the label's color/opacity. */
.margin-icon {
  display: inline-block;
  width: 12px;
  height: 12px;
  margin-right: 6px;
  vertical-align: -2px;
  color: inherit;
}
.lbl-strong { color: var(--fg); font-weight: 700; }
.lbl-2 { color: var(--fg-4); }
.lbl-accent { color: var(--accent); font-weight: 700; }
/* On chalk the default accent (white) is invisible — fall back to fg so
   status labels like the header's LIVE stay readable. */
html[data-palette="chalk"] .lbl-accent { color: var(--fg); }

.rule { background: var(--rule); }

/* ─── frame: the page edge ─────────────────────────────────────────────── */

.frame {
  position: fixed; inset: 0;
  pointer-events: none;
  z-index: 50;
}
.frame::before,
.frame::after {
  content: "";
  position: absolute;
  background: var(--rule);
}
.frame::before { left: 24px;  right: 24px;  top: 24px;    height: var(--rule-w); }
.frame::after  { left: 24px;  right: 24px;  bottom: 24px; height: var(--rule-w); }
.frame .v-l, .frame .v-r {
  position: absolute; top: 24px; bottom: 24px; width: var(--rule-w);
  background: var(--rule);
}
.frame .v-l { left: 24px; }
.frame .v-r { right: 24px; }

/* corner crosshair marks */
.frame .x { position: absolute; width: 9px; height: 9px; color: var(--fg-4); }
.frame .x::before, .frame .x::after {
  content: ""; position: absolute; background: currentColor;
}
.frame .x::before { left: 0; right: 0; top: 50%; height: 1px; }
.frame .x::after  { top: 0; bottom: 0; left: 50%; width: 1px; }
.frame .x-tl { top: 20px;    left: 20px; }
.frame .x-tr { top: 20px;    right: 20px; }
.frame .x-bl { bottom: 20px; left: 20px; }
.frame .x-br { bottom: 20px; right: 20px; }

/* ─── header bar (inside frame) ───────────────────────────────────────── */

.header {
  position: fixed; top: 32px; left: 40px; right: 40px;
  display: flex; align-items: baseline; justify-content: space-between;
  z-index: 60;
}
.header .seg { display: flex; gap: 18px; align-items: baseline; }
.header .dot {
  display: inline-block; width: 8px; height: 8px;
  background: var(--accent);
  vertical-align: middle;
  animation: pulse 1.4s steps(2, end) infinite;
}
html[data-palette="chalk"] .header .dot { background: var(--fg); }
@keyframes pulse {
  0%, 100% { opacity: 0.4; }
  50%      { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .header .dot, .breath, .mark-echo { animation: none !important; }
}

/* ─── footer bar ──────────────────────────────────────────────────────── */

.footbar {
  position: fixed; bottom: 32px; left: 40px; right: 40px;
  display: flex; align-items: baseline; justify-content: space-between;
  z-index: 60;
}

/* ─── stage layout ────────────────────────────────────────────────────── */

.stage {
  position: relative;
  z-index: 2;
  padding: 88px 40px 88px;
  min-height: 100vh;
}

/* HERO — asymmetric, mark sits weighted */
.hero {
  position: relative;
  min-height: calc(100vh - 176px);
  display: grid;
  grid-template-columns: minmax(0, 1fr) 320px;
  grid-template-rows: 1fr auto;
  column-gap: 56px;
  align-items: stretch;
  /* extra vertical breathing room around the MARIO/CORTEZ lockup */
  padding-top: 9vh;
  padding-bottom: 9vh;
}

/* the mark cell */
.mark-cell {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: flex-start;
  padding-left: 4vw;
  overflow: visible;
}

.mark-wrap {
  position: relative;
  display: inline-block;
  /* sized for the 5-letter word "Mario" — narrower per-em than the old single-letter mark */
  /* --mark-size is set from Tweaks (default 13vw); no upper clamp so it can go huge. */
  font-size: max(96px, var(--mark-size, 13vw));
  line-height: 0.88;
  letter-spacing: -0.035em;
  /* tiny cursor-pull on the whole mark + tweakable offset */
  transform: translate3d(
    calc(var(--mark-x, 0px) + var(--mx) * 6px),
    calc(var(--mark-y, 0px) + var(--my) * 4px),
    0
  );
  transition: transform 600ms cubic-bezier(.2,.7,.1,1);
  will-change: transform;
}

.mark {
  font-family: var(--display);
  font-style: var(--display-italic);
  font-weight: var(--display-weight);
  /* font-size inherited from .mark-wrap so the echo stays in lockstep */
  letter-spacing: inherit;
  color: var(--fg);
  line-height: 0.88;
  white-space: nowrap;
  display: inline-block;
  position: relative;
  z-index: 2;
  text-shadow: 0 0 60px color-mix(in oklab, var(--accent) calc(var(--near) * 24%), transparent);
}

/* Balance-O: enlarge the trailing O so it counters the weight of the M.
   Inline-block keeps the rest of the word steady; tightened tracking
   tucks the optical sidebearing in so it doesn't fly away. */
.mark--balance-o .mark__o {
  display: inline-block;
  font-size: 1.28em;
  line-height: 0.7;
  vertical-align: -0.07em;
  margin-left: 0em;
  letter-spacing: -0.04em;
}

/* Matching treatment on the last char of the last name (e.g. Z in CORTEZ)
   when balance is on and the stacked variant is showing. ~13% larger,
   slight right nudge to keep optical air against the previous letter,
   small lift so it sits proud on the baseline. */
.last-stacked.last--balance .last__tail {
  display: inline-block;
  font-size: 1.13em;
  line-height: 0.7;
  vertical-align: -0.02em;
  margin-left: 0.015em;
  letter-spacing: -0.02em;
}

/* ─── Last name variants ─────────────────────────────────────────────── */
/* The last name sits inside .mark-wrap, so it scales with --mark-size (em).
   Five styles: stacked (A), stencil (B), vertical (C), annotation (D), or none. */
.last-name {
  font-family: var(--display);
  font-style: var(--display-italic);
  font-weight: var(--display-weight);
  color: var(--fg);
  letter-spacing: var(--display-tracking);
  pointer-events: none;
}

/* A — Stacked masthead. Second beat under MARIO, ~42% size, right-aligned
   so the lockup feels intentional (the O of MARIO over the Z of CORTEZ). */
.last-stacked {
  display: block;
  font-size: 0.42em;
  line-height: 0.86;
  letter-spacing: -0.025em;
  text-align: right;
  margin-top: -0.04em;
  color: var(--fg);
  text-shadow:
    0 0 calc(10px * var(--glow, 1)) color-mix(in oklab, var(--glow-tint) calc(40% * var(--glow, 1)), transparent),
    0 0 calc(30px * var(--glow, 1)) color-mix(in oklab, var(--glow-tint) calc(20% * var(--glow, 1)), transparent);
}

/* B — Wide-tracked stencil baseline. Mono tag spanning the full width of
   MARIO underneath, like a serial number on cargo. */
.last-stencil {
  display: block;
  font-family: var(--mono);
  font-style: normal;
  font-weight: 700;
  font-size: 0.065em;
  letter-spacing: 0.55em;
  text-indent: 0.55em;        /* compensate for letter-spacing pushing first char left */
  line-height: 1;
  margin-top: 0.05em;
  padding-top: 0.06em;
  border-top: 2px solid var(--rule);
  color: var(--fg-2);
  text-align: center;
  text-transform: uppercase;
}

/* C — Vertical edge. Reads bottom-to-top up the right side of MARIO. */
.last-vertical {
  position: absolute;
  top: 0;
  right: -0.16em;
  height: 0.88em;
  font-size: 0.16em;
  letter-spacing: 0.04em;
  line-height: 1;
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  color: var(--fg);
  display: flex;
  align-items: center;
  text-transform: uppercase;
}

/* D — Annotation tag. Tiny mono caption above MARIO. */
.last-annotation {
  position: absolute;
  top: -0.06em;
  right: 0.02em;
  font-family: var(--mono);
  font-style: normal;
  font-weight: 700;
  font-size: 0.05em;
  letter-spacing: 0.32em;
  line-height: 1;
  color: var(--accent);
  text-align: right;
  text-transform: uppercase;
}

/* In broadside layout the mark is centered — center the last-name lockup too */
html[data-layout="broadside"] .last-stacked { text-align: center; }

/* registration-ghost echo behind the mark — brighter on the new direction */
.mark-echo {
  position: absolute;
  inset: 0;
  font-family: var(--display);
  font-style: var(--display-italic);
  font-weight: var(--display-weight);
  font-size: inherit;
  letter-spacing: inherit;
  color: var(--accent);
  opacity: 0.18;
  mix-blend-mode: screen;
  transform: translate3d(
    calc(var(--mx) * -14px - 3px),
    calc(var(--my) * -8px + 2px),
    0
  );
  transition: transform 700ms cubic-bezier(.2,.7,.1,1), opacity 600ms;
  pointer-events: none;
  z-index: 1;
}
html[data-palette="paper"] .mark-echo,
html[data-palette="bone"] .mark-echo { mix-blend-mode: multiply; opacity: 0.22; }

/* breathing + jitter + pulse */
.breath { animation: breathe 6s ease-in-out infinite; transform-origin: center; }
@keyframes breathe {
  0%, 100% { transform: scale(1.00); }
  50%      { transform: scale(1.028); }
}
html[data-motion="pulse"] .mark.breath {
  animation: pulse-mark 4.5s cubic-bezier(.2,.7,.1,1) infinite;
}
@keyframes pulse-mark {
  0%   { transform: scale(1.000); }
  10%  { transform: scale(1.041); }
  22%  { transform: scale(1.004); }
  32%  { transform: scale(1.022); }
  44%  { transform: scale(1.000); }
  100% { transform: scale(1.000); }
}
html[data-motion="jitter"] .mark.breath {
  animation: jitter 90ms steps(1) infinite;
}
@keyframes jitter {
  0%   { transform: translate(0,    0);     }
  25%  { transform: translate(-1.5px, 1px);   }
  50%  { transform: translate(1px,  -1.5px);  }
  75%  { transform: translate(-1px, -0.5px);}
  100% { transform: translate(0.5px, 1.5px);  }
}
html[data-motion="still"] .mark.breath { animation: electric 2.6s ease-in-out infinite; }

/* Dark glow — chalk only. Renders as a drop-shadow filter on the mark.
   The grain scanline overlay (overlay-blend with black lines, see below)
   leaves the SOLID text body and the SOLID white bg untouched, only
   showing on the halo gradient — symmetric with void's white-on-black
   scanline behavior. */
html[data-palette="chalk"] .mark.breath {
  filter:
    drop-shadow(0 0 calc(18px  * var(--dark-glow, 0)) color-mix(in oklab, var(--fg) calc(55% * var(--dark-glow, 0)), transparent))
    drop-shadow(0 0 calc(50px  * var(--dark-glow, 0)) color-mix(in oklab, var(--fg) calc(28% * var(--dark-glow, 0)), transparent))
    drop-shadow(0 0 calc(130px * var(--dark-glow, 0)) color-mix(in oklab, var(--fg) calc(14% * var(--dark-glow, 0)), transparent));
}

/* Electric glow — pulsing halo behind the mark, scaled by --glow (0..2).
   Always running; --glow drives intensity (0 = off). */
.mark.breath {
  animation-name: breathe, electric;
  animation-duration: 6s, 2.6s;
  animation-timing-function: ease-in-out, ease-in-out;
  animation-iteration-count: infinite, infinite;
}
html[data-motion="pulse"] .mark.breath { animation-name: pulse-mark, electric; animation-duration: 4.5s, 2.6s; }
html[data-motion="jitter"] .mark.breath { animation-name: jitter, electric; animation-duration: 90ms, 2.6s; animation-timing-function: steps(1), ease-in-out; }

/* Hover — beat dramatically faster (motion duration quartered).
   Triggered by JS .is-hover class (glyph-precise hit test), not CSS :hover,
   so empty space inside the bounding box doesn't engage the effect. */
.mark.breath.is-hover                                  { animation-duration: 1.5s, 2.6s; }
html[data-motion="pulse"] .mark.breath.is-hover        { animation-duration: 1.125s, 2.6s; }
html[data-motion="jitter"] .mark.breath.is-hover       { animation-duration: 30ms, 2.6s; }
html[data-motion="still"] .mark.breath.is-hover        { animation-duration: 0.65s; }
@keyframes electric {
  0%, 100% {
    text-shadow:
      0 0 calc(18px  * var(--glow, 1)) color-mix(in oklab, var(--glow-tint) calc(55% * var(--glow, 1)), transparent),
      0 0 calc(50px  * var(--glow, 1)) color-mix(in oklab, var(--glow-tint) calc(28% * var(--glow, 1)), transparent),
      0 0 calc(130px * var(--glow, 1)) color-mix(in oklab, var(--glow-tint) calc(14% * var(--glow, 1)), transparent);
  }
  50% {
    text-shadow:
      0 0 calc(36px  * var(--glow, 1)) color-mix(in oklab, var(--glow-tint) calc(75% * var(--glow, 1)), transparent),
      0 0 calc(110px * var(--glow, 1)) color-mix(in oklab, var(--glow-tint) calc(45% * var(--glow, 1)), transparent),
      0 0 calc(240px * var(--glow, 1)) color-mix(in oklab, var(--glow-tint) calc(22% * var(--glow, 1)), transparent);
  }
}

/* annotation — the small mono note above the mark */
.mark-anno {
  position: absolute;
  top: -26px; /* snug on the M's apex — M asked it 20px closer */
  left: 4px;
  display: flex; align-items: baseline; gap: 10px;
  white-space: nowrap;
}
/* EKG heart-rate trace — replaces the old "SIGNAL // I ✱ CONTACT MADE"
   note. A 32-unit monitor-sweep window travels the trace once per MARIO
   pulse cycle; the dashoffset phase (119 → -13) times the R-spike to land
   at ~10% of the cycle, the moment the mark swells. Hovering the mark
   (.is-hover, glyph-precise) quadruples both — racing heart. */
/* CRT shell around the trace — hairline bezel + rolling scanlines +
   phosphor-edge vignette (diegetic monitor glass, not a focus crutch). */
.ekg-crt {
  position: relative;
  display: block;
  padding: 5px 9px;
  /* no outline (M cut the bezel) — the screen tint + graph paper + vignette
     carry the housed read on their own */
  border: none;
  border-radius: 4px;
  /* graph paper — thin vertical/horizontal rules under the trace,
     over the faint screen tint */
  background:
    repeating-linear-gradient(90deg,
      color-mix(in oklab, var(--fg) 8%, transparent) 0 1px, transparent 1px 12px),
    repeating-linear-gradient(0deg,
      color-mix(in oklab, var(--fg) 7%, transparent) 0 1px, transparent 1px 6px),
    color-mix(in oklab, var(--fg) 4%, transparent);
  overflow: hidden;
}
/* scanlines — 3px period rolling downward; one period per loop = seamless */
.ekg-crt::before {
  content: "";
  position: absolute;
  inset: -3px 0 0 0;
  background: repeating-linear-gradient(0deg,
    color-mix(in oklab, var(--bg, #0a0a0a) 55%, transparent) 0 1px,
    transparent 1px 3px);
  animation: ekg-scan 0.9s linear infinite, ekg-flicker 3.1s ease-in-out infinite;
  pointer-events: none;
}
@keyframes ekg-scan { to { transform: translateY(3px); } }
@keyframes ekg-flicker {
  0%, 100% { opacity: 0.65; }
  37%      { opacity: 0.95; }
  62%      { opacity: 0.75; }
}
/* phosphor-edge vignette — glass falloff toward the bezel */
.ekg-crt::after {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse 72% 88% at 50% 50%,
    transparent 52%,
    color-mix(in oklab, var(--bg, #0a0a0a) 72%, transparent) 100%);
  pointer-events: none;
}
.ekg {
  width: 120px; /* left 30 units cropped via viewBox — tighter shell */
  height: 16px;
  display: block;
  color: var(--fg-2);
  overflow: hidden; /* enforce the left crop (path data starts at x=0) */
}

/* Activity LED — housed in its own ring bezel right of the monitor; the
   inner dot blinks with the R-spike each heartbeat (same duration ladder
   as the trace sweep) with a soft echo for the lub-dub. */
.ekg-led {
  position: relative;
  width: 11px;
  height: 11px;
  border-radius: 50%;
  border: 2px solid color-mix(in oklab, var(--fg) 32%, transparent);
  align-self: center;
  flex-shrink: 0;
}
.ekg-led::after {
  content: "";
  position: absolute;
  inset: 1px;
  border-radius: 50%;
  background: var(--fg);
  box-shadow: 0 0 7px 1.5px color-mix(in oklab, var(--fg) 55%, transparent);
  animation: ekg-led 6s linear infinite;
}
html[data-motion="pulse"] .ekg-led::after { animation-duration: 4.5s; }
.mark-wrap:has(.mark.is-hover) .ekg-led::after { animation-duration: 1.5s; }
html[data-motion="pulse"] .mark-wrap:has(.mark.is-hover) .ekg-led::after { animation-duration: 1.125s; }
@keyframes ekg-led {
  0%, 6%   { opacity: 0.22; }
  10%      { opacity: 1; }    /* R-spike blink */
  13%      { opacity: 0.3; }
  17%      { opacity: 0.7; }  /* lub-dub echo */
  23%, 88% { opacity: 0.22; }
  93%      { opacity: 0.3; }  /* faint idle shimmer */
  100%     { opacity: 0.22; }
}
.ekg__trace {
  stroke-dasharray: 32 100;
  stroke-dashoffset: 119;
  animation: ekg-sweep 6s linear infinite; /* matches breathe (non-pulse) */
  /* phosphor glow — tight hot pass + wide soft bloom */
  filter:
    drop-shadow(0 0 1.5px color-mix(in oklab, var(--fg) 85%, transparent))
    drop-shadow(0 0 5px color-mix(in oklab, var(--fg) 50%, transparent));
}
@keyframes ekg-sweep { to { stroke-dashoffset: -13; } }
html[data-motion="pulse"] .ekg__trace { animation-duration: 4.5s; }
.mark-wrap:has(.mark.is-hover) .ekg__trace { animation-duration: 1.5s; }
html[data-motion="pulse"] .mark-wrap:has(.mark.is-hover) .ekg__trace { animation-duration: 1.125s; }
.mark-wrap:has(.mark.is-hover) .ekg { color: var(--fg); }

@media (prefers-reduced-motion: reduce) {
  /* static full trace — no sweep, no scan roll/flicker, steady LED */
  .ekg__trace { animation: none; stroke-dasharray: none; stroke-dashoffset: 0; }
  .ekg-crt::before { animation: none; opacity: 0.5; }
  .ekg-led::after { animation: none; opacity: 0.4; }
}

/* the motto — stacked shouts under the mark */
.motto {
  margin-top: 36px;
  margin-left: 4vw;
  font-family: var(--display);
  font-style: var(--display-italic);
  font-weight: var(--display-weight);
  font-size: clamp(56px, 7.5vw, 132px);
  color: var(--fg);
  letter-spacing: -0.02em;
  line-height: 0.88;
  text-transform: uppercase;
  max-width: 18ch;
  white-space: nowrap;
}
.motto em { color: var(--accent); font-style: var(--display-italic); }
/* Inside a motto link, the italicized fragment (e.g. "graphica" in
   "techno graphica") needs to follow --fg like the rest of the link so it
   stays visible on light palettes where the user's --accent may be near
   the bg color. The sizzle text-shadow on .mlink-text already covers it. */
.mlink em { color: inherit; }

/* ─── Motto hover-links (REEL / CHARACTER ART / STUDIO) ───────────────────
   Each link wraps its motto line in an <a>. On hover the inner text
   "sizzle-shakes" with a few-px jitter and a glowing accent shadow, and 8
   small bolt SVGs around the line fly outward in staggered bursts.
   Bolts reuse the same <Bolt> SVG that the MARIO mark uses but with their
   own .mlink-* classes so the data-bolts toggle on <html> doesn't affect
   them. Position absolute is scoped to .mlink (each link is its own frame). */
.mlink {
  position: relative;
  display: inline-block;
  color: inherit;
  text-decoration: none;
  cursor: pointer;
  transition: color 140ms ease;
}
.mlink:hover { z-index: 5; }
.mlink:focus-visible {
  outline: 2px solid var(--fg);
  outline-offset: 4px;
}

.mlink-text {
  position: relative;
  display: inline-block;
  will-change: transform;
}
.mlink:hover .mlink-text {
  animation: mlink-sizzle 70ms steps(2, end) infinite;
  /* Use --fg for the shadow so it stays visible on light palettes (dark
     glow on light bg, bright glow on dark bg). */
  text-shadow:
    0 0 6px  color-mix(in oklab, var(--fg) 55%, transparent),
    0 0 18px color-mix(in oklab, var(--fg) 35%, transparent),
    0 0 36px color-mix(in oklab, var(--fg) 18%, transparent);
}

@keyframes mlink-sizzle {
  0%   { transform: translate(0, 0) skewX(0); }
  20%  { transform: translate(-1.5px,  1px) skewX(-1deg); }
  40%  { transform: translate( 2px,   -1.5px) skewX(0.6deg); }
  60%  { transform: translate(-2px,   -0.5px) skewX(1.2deg); }
  80%  { transform: translate( 1.5px,  1.5px) skewX(-0.8deg); }
  100% { transform: translate(-1px,    0)    skewX(0); }
}

.mlink-bolt {
  position: absolute;
  display: block;
  width: 14px;
  height: 28px;
  pointer-events: none;
  opacity: 0;
  color: var(--fg);
}
.mlink-bolt-svg {
  display: block;
  width: 100%;
  height: 100%;
  filter:
    drop-shadow(0 0 4px  color-mix(in oklab, var(--fg) 70%, transparent))
    drop-shadow(0 0 10px color-mix(in oklab, var(--fg) 40%, transparent));
}

/* Positions around each link's text box. Frame transforms set the outward
   rotation; the inner SVG then animates translateY-negative in the local
   rotated frame, so each bolt travels along its own pointing direction. */
.mlink-bolt-1 { top: -28%;    left:  4%;  transform: rotate(-42deg); }
.mlink-bolt-2 { top: -42%;    left: 26%;  transform: rotate(-12deg); }
.mlink-bolt-3 { top: -38%;    left: 52%;  transform: rotate( 18deg); }
.mlink-bolt-4 { top: -30%;    right: 4%;  transform: rotate( 46deg); }
.mlink-bolt-5 { bottom: -30%; left:  8%;  transform: rotate(-148deg); }
.mlink-bolt-6 { bottom: -42%; left: 34%;  transform: rotate( 175deg); }
.mlink-bolt-7 { bottom: -38%; left: 62%;  transform: rotate( 158deg); }
.mlink-bolt-8 { bottom: -28%; right: 6%;  transform: rotate( 130deg); }

.mlink:hover .mlink-bolt { opacity: 1; }
.mlink:hover .mlink-bolt .mlink-bolt-svg {
  animation: mlink-bolt-fly 620ms cubic-bezier(.18, .85, .4, 1) infinite;
}
.mlink:hover .mlink-bolt-1 .mlink-bolt-svg { animation-delay:   0ms; }
.mlink:hover .mlink-bolt-2 .mlink-bolt-svg { animation-delay:  80ms; }
.mlink:hover .mlink-bolt-3 .mlink-bolt-svg { animation-delay: 160ms; }
.mlink:hover .mlink-bolt-4 .mlink-bolt-svg { animation-delay: 220ms; }
.mlink:hover .mlink-bolt-5 .mlink-bolt-svg { animation-delay:  40ms; }
.mlink:hover .mlink-bolt-6 .mlink-bolt-svg { animation-delay: 130ms; }
.mlink:hover .mlink-bolt-7 .mlink-bolt-svg { animation-delay: 190ms; }
.mlink:hover .mlink-bolt-8 .mlink-bolt-svg { animation-delay: 280ms; }

@keyframes mlink-bolt-fly {
  0%   { opacity: 0; transform: translate(0,    0)    scale(0.55); }
  12%  { opacity: 1; transform: translate(0,   -3px) scale(1.0); }
  60%  { opacity: 1; transform: translate(-2px, -30px) scale(1.05); }
  100% { opacity: 0; transform: translate(3px,  -58px) scale(0.35); }
}

@media (prefers-reduced-motion: reduce) {
  .mlink:hover .mlink-text { animation: none; }
  .mlink:hover .mlink-bolt { opacity: 0; }
  .mlink:hover .mlink-bolt .mlink-bolt-svg { animation: none; }
}

/* "// UNDER CONSTRUCTION" tag stamped next to STUDIO on click. Mono label
   voice, slight scrawl tilt, dying-neon flicker-in then a sputter-out near
   the end; React removes the node just after the animation completes.
   --fg based so it reads on chalk/light palettes too. */
.mlink-soon {
  display: inline-block;
  margin-left: 20px;
  vertical-align: 6px;
  color: var(--fg-2);
  white-space: nowrap;
  transform: rotate(-2deg);
  animation: mlink-soon-stamp 2600ms linear both;
}
@keyframes mlink-soon-stamp {
  0%   { opacity: 0; }
  3%   { opacity: 1; }
  6%   { opacity: 0.15; }
  9%   { opacity: 1; }
  12%  { opacity: 0.4; }
  15%  { opacity: 1; }
  80%  { opacity: 1; }
  86%  { opacity: 0.2; }
  90%  { opacity: 0.9; }
  100% { opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .mlink-soon { animation: none; opacity: 1; }
}

/* marginalia column on the right side of hero — single-column stack. */
.margin {
  position: relative;
  padding-top: 10px;
  display: flex; flex-direction: column; gap: 22px;
  border-left: var(--rule-w) solid var(--rule);
  padding-left: 28px;
}

.margin .block { display: flex; flex-direction: column; gap: 10px; }
.margin .block p { color: var(--fg-2); font-size: 12.5px; line-height: 1.55; }

/* Hairline dividers between blocks. */
.margin .block + .block {
  padding-top: 22px;
  border-top: 1px solid var(--rule-2);
}
.margin .block .num {
  font-family: var(--display);
  font-size: 44px;
  font-style: var(--display-italic);
  font-weight: var(--display-weight);
  color: var(--fg);
  line-height: 1;
  letter-spacing: -0.02em;
}


/* Roman-numeral marker before margin-block labels (LORE / ROLES / CONNECT /
   LUNAR CYCLE) — replaces the old micro-icons. */
.lbl-ix {
  display: inline-block;
  min-width: 1.6em;
  margin-right: 7px;
  color: var(--fg-2);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* ─── lineage strip ───────────────────────────────────────────────────── */

.lineage {
  margin-top: 64px;
  padding: 36px 0 28px;
  border-top: var(--rule-w) solid var(--rule);
  display: grid;
  grid-template-columns: 180px 1fr;
  column-gap: 56px;
  align-items: baseline;
}
.lineage .head { color: var(--fg-3); }
.lineage .head .dim { color: var(--fg-4); }
.lineage .names {
  font-family: var(--display);
  font-weight: var(--display-weight);
  font-size: clamp(28px, 3.2vw, 48px);
  line-height: 1.12;
  color: var(--fg);
  letter-spacing: var(--display-tracking);
  text-transform: uppercase;
}
.lineage .names span { white-space: nowrap; }
.lineage .names .sep {
  display: inline-block;
  width: 10px; height: 10px;
  background: var(--accent);
  vertical-align: middle;
  margin: 0 14px;
  transform: translateY(-6px) rotate(45deg);
}

/* ─── workbench (cipher circuit) ──────────────────────────────────────────
   Replaces the lineage strip. Same outer chrome (border-top, two-column
   grid with a fixed label column), but the right column is a single
   vertical schematic: one hairline "trace" runs top-to-bottom, with each
/* ─── workbench (mind / thought-bubbles) ──────────────────────────────────
   The head X-ray sits on the left; comic-book thought bubbles holding each
   tool fan up and to the right, each trailing puffs back toward the brain
   and flickering like candlelight. */
.workbench {
  margin-top: 64px;
  padding: 36px 0 28px;
  border-top: var(--rule-w) solid var(--rule);
  position: relative;
}
.workbench .head {
  position: absolute;
  top: 36px;
  left: 0;
  width: 180px;
  z-index: 5;
  color: var(--fg-3);
}
.workbench .head .dim { color: var(--fg-4); }

/* Stage — relative box the head + ring of tool-clouds position within. */
.brainbench {
  position: relative;
  width: 100%;
  min-height: 600px;
  color: var(--fg);
  /* x-ray grid line color — fg pulled toward the live-wire blue-white */
  --xray-line: color-mix(in oklab, var(--fg) 70%, rgb(150 200 255));
}

/* The CRT's "picture" — every displayed element lives here so the boot
   scale squashes the image while the glass overlays (bootline, scanlines)
   stay unscaled siblings. */
.brainbench__screen {
  position: absolute;
  inset: 0;
  /* picture opens FROM the bootline (65%, over the brain) — origins match */
  transform-origin: 50% 65%;
}

/* X-ray backdrop — faint radiograph grid behind the head + clouds: fine
   cells with a stronger major line every 5th, fading out radially so it
   reads as a screening field, not a panel. z0 keeps it under everything. */
.brainbench__screen::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background-image:
    linear-gradient(color-mix(in oklab, var(--xray-line) 9%, transparent) 1px, transparent 1px),
    linear-gradient(90deg, color-mix(in oklab, var(--xray-line) 9%, transparent) 1px, transparent 1px),
    linear-gradient(color-mix(in oklab, var(--xray-line) 5%, transparent) 1px, transparent 1px),
    linear-gradient(90deg, color-mix(in oklab, var(--xray-line) 5%, transparent) 1px, transparent 1px);
  background-size: 130px 130px, 130px 130px, 26px 26px, 26px 26px;
  /* faint phosphor tint under the grid — the lit screen itself */
  background-color: color-mix(in oklab, var(--xray-line) 3.5%, transparent);
  -webkit-mask-image: radial-gradient(ellipse 62% 58% at 50% 56%, #000 30%, transparent 76%);
  mask-image: radial-gradient(ellipse 62% 58% at 50% 56%, #000 30%, transparent 76%);
}

/* ─── CRT power-on boot ───────────────────────────────────────────────────
   The whole stage squashes to a glowing horizontal line and stretches open
   (classic TV-on). data-boot: 0 = off/dark, 1 = booting, 2 = on. The
   bootline span supplies the white-hot line; the container scale supplies
   the geometry; a brightness ramp in the keyframes is the phosphor flash. */
.brainbench[data-boot="0"] .brainbench__screen { opacity: 0; }
.brainbench[data-boot="1"] .brainbench__screen {
  animation: crt-on 255ms cubic-bezier(0.3, 0.7, 0.2, 1) both;
  will-change: transform, filter;
}
@keyframes crt-on {
  0%   { opacity: 1; transform: scaleY(0.006) scaleX(0.55); filter: brightness(4); }
  38%  { opacity: 1; transform: scaleY(0.012) scaleX(1.03); filter: brightness(3.2); }
  58%  { transform: scaleY(0.45) scaleX(1.015); filter: brightness(1.9); }
  78%  { transform: scaleY(1.05) scaleX(1); filter: brightness(1.25); }
  100% { opacity: 1; transform: scaleY(1); filter: brightness(1); }
}
.brainbench__bootline {
  position: absolute;
  left: 6%;
  right: 6%;
  top: 65%; /* over the brain itself, not the stage midline (which clipped the crown) */
  height: 2px;
  z-index: 8;
  pointer-events: none;
  opacity: 0;
  background: var(--fg);
  box-shadow:
    0 0 16px 3px color-mix(in oklab, var(--fg) 85%, transparent),
    0 0 56px 14px rgba(150, 200, 255, 0.5);
}
.brainbench[data-boot="1"] .brainbench__bootline {
  animation: crt-bootline 255ms ease-out both;
}
@keyframes crt-bootline {
  0%   { opacity: 1; transform: scaleX(0.12); }
  30%  { opacity: 1; transform: scaleX(1); }
  55%  { opacity: 0.9; }
  100% { opacity: 0; }
}

/* Rolling scanlines + slow flicker once the screen is lit — same idiom as
   the hero EKG monitor (.ekg-crt::before) but masked to the screen's
   radial falloff so the lines die out with the grid. Kept faint: dark
   bg-colored lines over the phosphor tint, never a texture blanket. */
.brainbench__scan {
  position: absolute;
  inset: 0;
  z-index: 6;
  pointer-events: none;
  opacity: 0;
  background: repeating-linear-gradient(0deg,
    color-mix(in oklab, var(--bg, #0a0a0a) 45%, transparent) 0 1px,
    transparent 1px 4px);
  -webkit-mask-image: radial-gradient(ellipse 62% 58% at 50% 56%, #000 30%, transparent 76%);
  mask-image: radial-gradient(ellipse 62% 58% at 50% 56%, #000 30%, transparent 76%);
}
.brainbench[data-boot="1"] .brainbench__scan,
.brainbench[data-boot="2"] .brainbench__scan {
  opacity: 1;
  animation: bench-scan 1.1s linear infinite, ekg-flicker 4.7s ease-in-out infinite;
}
@keyframes bench-scan { to { background-position: 0 4px; } }
/* Mobile boot runs slower (450ms vs 255) — at desktop speed, mid-scroll on
   a phone, the line-flare read as a pop. Boot completion is driven by
   animationend so the JS needs no matching timer. */
@media (pointer: coarse), (max-width: 719px) {
  .brainbench[data-boot="1"] .brainbench__screen { animation-duration: 450ms; }
  .brainbench[data-boot="1"] .brainbench__bootline { animation-duration: 450ms; }
}
/* The head is the SIGNAL, not the screen: it stays dark through the boot
   and resolves in a beat after the monitor lights (M: head and boot were
   popping in together). */
.brainbench[data-boot="0"] .brainbench__head,
.brainbench[data-boot="1"] .brainbench__head { opacity: 0; }
.brainbench[data-boot="2"] .brainbench__head { animation: head-arrive 460ms ease 170ms both; }
@keyframes head-arrive { from { opacity: 0; } to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
  .brainbench[data-boot] .brainbench__head { opacity: 1; animation: none; }
}
@media (prefers-reduced-motion: reduce) {
  .brainbench[data-boot] .brainbench__screen { animation: none; opacity: 1; transform: none; }
  .brainbench__scan { animation: none !important; opacity: 0.55; }
  .brainbench__bootline { animation: none !important; opacity: 0; }
}

/* Head shock-bolt canvas — full-stage overlay so arcs extend past the head
   box. Below the cursor canvas (z7) but above clouds; behind the head img? No
   — sits above so bolts read leaping off the head. pointer-events:none. */
.headbolt-canvas {
  position: absolute;
  inset: 0;
  z-index: 5;
  pointer-events: none;
}
.impulse-canvas {
  position: absolute;
  inset: 0;
  z-index: 7;
  pointer-events: none;
}
/* Page-wide variant: one fixed full-viewport field tracking the global
   cursor — continuous across the whole page (no per-section gap). Sits just
   above the grain (z-40) and content, below the frame/chrome. */
.impulse-canvas--page {
  position: fixed;
  inset: 0;
  width: 100vw;
  height: 100vh;
  z-index: 41;
}
/* Hide the default cursor in the section so the glowing node replaces it —
   only when motion is allowed (reduced-motion keeps the normal cursor). */
@media (prefers-reduced-motion: no-preference) {
  .brainbench--ring, .livewire { cursor: none; }
}
/* Live-wire host sections need a positioning context for the canvas. */
.livewire { position: relative; }

/* Head X-ray — centered, anchored to the bottom of the stage. Real alpha
   cutout (luminance keyed): white anatomy opaque, backdrop transparent.
   Hover (or tap) wakes it: see the shock rules below. On chalk it inverts. */
.brainbench__head {
  position: absolute;
  left: 50%;
  bottom: 0;
  transform: translateX(-50%);
  width: 30%;
  max-width: 300px;
  z-index: 2;
  cursor: pointer;
  overflow: visible;
  -webkit-user-select: none;
  user-select: none;
  /* touch: no long-press save-image callout (it cancelled the pointer and
     froze the shock mid-reveal), no double-tap zoom delay */
  -webkit-touch-callout: none;
  touch-action: manipulation;
}
.brainbench__head img {
  -webkit-touch-callout: none;
  -webkit-user-drag: none;
}
.brainbench__head img {
  position: relative;
  z-index: 1;
  display: block;
  width: 100%;
  height: auto;
  /* neck dissolves into the dark at the stage floor — fade only, full
     neck length kept (mask, not crop) */
  -webkit-mask-image: linear-gradient(to bottom, #000 72%, transparent 99%);
  mask-image: linear-gradient(to bottom, #000 72%, transparent 99%);
}
html[data-palette="chalk"] .brainbench__head img { filter: invert(1); }

/* Typewriter plea floating just above the head — monospace, bracketed.
   Sits inside the head container so it rocks with the head when shocked. */
.brainspeak {
  position: absolute;
  left: 50%;
  top: 0;
  transform: translate(-50%, calc(-100% - 14px));
  z-index: 5;
  display: flex;
  align-items: baseline;
  gap: 4px;
  white-space: nowrap;
  font-family: var(--mono);
  font-size: 13px;
  letter-spacing: 0.14em;
  color: var(--fg-3);
  pointer-events: none;
}
.brainspeak__br { color: var(--fg-4); }
.brainspeak__msg { min-width: 1ch; }
.brainspeak__caret { animation: brainspeak-blink 1s steps(1) infinite; }
@keyframes brainspeak-blink { 50% { opacity: 0; } }
.brainspeak.is-shock {
  color: var(--fg);
  font-weight: 700;
  font-size: 16px;
  letter-spacing: 0.18em;
}
.brainspeak.is-shock .brainspeak__br { color: var(--fg); }
.brainspeak.is-shock .brainspeak__caret { display: none; }

/* While shocking, the head's own silhouette glows — drop-shadow hugs the
   actual head shape (not a disc), reading as a true electric rim. Layered
   on the calm (data-neg=0) frame.
   ⚠ The glow lives on .brainbench__rock, NOT the img: the img carries the
   neck-fade mask, and a mask clips its own element's filter output at the
   border box — drop-shadow on the img got sheared into a visible square.
   On the parent the shadow wraps the already-masked silhouette, unclipped.
   (Color filters — invert/brightness — stay on the img so the plea text
   inside the rock doesn't invert with it.) */
.brainbench__head.is-shocking[data-neg="0"] .brainbench__rock {
  filter:
    drop-shadow(0 0 3px color-mix(in oklab, var(--fg) 45%, transparent))
    drop-shadow(0 0 9px color-mix(in oklab, var(--fg) 22%, transparent));
}

/* Invert on alternate shock frames — rocks back = negative with electric
   rim, rocks forward = normal. Drop-shadow on the alpha silhouette keeps
   the inverted head visible against the void (inverted white → black needs
   a bright halo to read). On chalk the base is already inverted so
   data-neg=1 un-inverts it back to the original dark scan. */
.brainbench__head[data-neg="1"] img {
  filter: invert(1) brightness(2.2) contrast(1.15);
}
.brainbench__head[data-neg="1"] .brainbench__rock {
  filter:
    drop-shadow(0 0 8px color-mix(in oklab, var(--fg) 70%, transparent))
    drop-shadow(0 0 18px color-mix(in oklab, var(--fg) 35%, transparent));
}
html[data-palette="chalk"] .brainbench__head[data-neg="1"] img {
  filter: invert(0) brightness(1.5) contrast(1.2);
}
html[data-palette="chalk"] .brainbench__head[data-neg="1"] .brainbench__rock {
  filter:
    drop-shadow(0 0 5px var(--fg))
    drop-shadow(0 0 13px var(--fg))
    drop-shadow(0 0 28px color-mix(in oklab, var(--fg) 50%, transparent));
}

/* While shocking, the head SIZZLES — a fast few-px jitter + skew (same idiom
   as the REEL / CHARACTER ART motto links) layered on top of the slow
   back-and-forth rock that the container drives per frame. No flashing —
   just a juddering, electrified head. */
.brainbench__head.is-shocking img {
  animation: head-sizzle 90ms steps(2, end) infinite;
}
@keyframes head-sizzle {
  0%   { transform: translate(0, 0)       skewX(0deg); }
  25%  { transform: translate(-2.5px, 1px)   skewX(-1.2deg); }
  50%  { transform: translate(2px, -1.5px)   skewX(1deg); }
  75%  { transform: translate(-1.5px, -1px)  skewX(0.6deg); }
  100% { transform: translate(2px, 1.5px)    skewX(-0.7deg); }
}

/* Bolts sizzle outward from the head while shocking — REPLACED by the
   canvas HeadBoltField (jagged tapered arcs). Legacy .head-bolt* SVG rules
   removed; see HeadBoltField in app.jsx + .headbolt-canvas above. */

/* ── Tool-clouds fanned around the head ──────────────────────────────────
   Each is a small hand-drawn cloud body (tailless) holding ONE tool, with a
   dot-trail rotated to aim back at the brain. Hidden until the shock pops it
   in — opacity snaps (no fade), scale punches like a stop-motion stamp. */
.tcloud {
  position: absolute;
  z-index: 4;
  width: 184px;
  transform: translate(-50%, -50%);
  opacity: 0;
  pointer-events: none;
}
.tcloud.is-shown {
  opacity: 1;                         /* snaps in — no fade on reveal */
  animation: tcloud-pop 240ms cubic-bezier(0.2, 1.5, 0.4, 1) both;
}
/* Idle dismiss: ~2s after the head is left alone, clouds drift up + fade. */
.tcloud.is-dismissing {
  animation: tcloud-vanish 620ms ease forwards;
}
@keyframes tcloud-pop {
  0%   { transform: translate(-50%, -50%) scale(0.1); }
  60%  { transform: translate(-50%, -50%) scale(1.12); }
  100% { transform: translate(-50%, -50%) scale(1); }
}
@keyframes tcloud-vanish {
  0%   { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  100% { opacity: 0; transform: translate(-50%, -64%) scale(0.78); }
}
/* Inner wrapper carries the gentle continuous float, so it composes with the
   outer pop/vanish transforms (separate element = no transform clash). */
.tcloud__float {
  position: relative;
  width: 100%;
  animation: tcloud-float 5.4s ease-in-out infinite;
  will-change: transform;
}
@keyframes tcloud-float {
  0%   { transform: translateY(0) rotate(0deg); }
  50%  { transform: translateY(-8px) rotate(0.7deg); }
  100% { transform: translateY(0) rotate(0deg); }
}
.tcloud__body {
  display: block;
  width: 100%;
  height: auto;
  filter: drop-shadow(0 2px 10px rgba(0, 0, 0, 0.28));
}
.tcloud__label {
  position: absolute;
  inset: 6% 10% 14% 10%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  text-align: center;
  font-family: var(--mono);
  text-transform: uppercase;
}
.tcloud__serial {
  font-size: 7px;
  font-weight: 700;
  letter-spacing: 0.28em;
  color: rgba(255, 255, 255, 0.55);
  text-shadow: 0 1px 3px rgba(0,0,0,0.9);
}
.tcloud__tool {
  font-size: 19px;
  font-family: var(--display);
  font-weight: 400;
  letter-spacing: 0.02em;
  line-height: 1.1;
  color: #ffffff;
  text-wrap: balance;
  -webkit-text-stroke: 0.5px rgba(0,0,0,0.5);
  text-shadow:
    0 0 3px #000,
    0 0 8px #000,
    0 0 16px #000,
    0 0 28px #000,
    0 0 44px rgba(0,0,0,0.9),
    0 2px 2px #000,
    -1px -1px 0 rgba(0,0,0,0.8),
    1px  1px 0 rgba(0,0,0,0.8);
}
.tcloud__bolt-wrap {
  pointer-events: none;
  /* pulsing electric glow around the bolt — on the wrap so it doesn't fight
     the img's screen-blend filter */
  animation: bolt-glow 1.3s ease-in-out infinite;
}
@keyframes bolt-glow {
  0%, 100% { filter: drop-shadow(0 0 4px rgba(255,255,255,0.35)); }
  50%      { filter: drop-shadow(0 0 12px rgba(255,255,255,0.9))
                     drop-shadow(0 0 22px rgba(180,210,255,0.5)); }
}
/* While the head is being shocked, the whole cloud (body + its bolt) trembles
   — the float is swapped for a tight tremor so the charge reads as running
   through the clouds. Text rides along but has no jitter of its own. */
.brainbench--ring[data-shocking="1"] .tcloud.is-shown .tcloud__float {
  animation: cloud-tremor 55ms steps(2, end) infinite;
}
@keyframes cloud-tremor {
  0%   { transform: translate(0, 0)        skewX(0deg); }
  25%  { transform: translate(-3px, 2px)   skewX(-1.4deg); }
  50%  { transform: translate(2.6px, -2.4px) skewX(1.2deg); }
  75%  { transform: translate(-2px, -1.6px)  skewX(0.7deg); }
  100% { transform: translate(2.4px, 2px)   skewX(-0.9deg); }
}
.brainbench--ring[data-shocking="1"] .tcloud:nth-child(1).is-shown .tcloud__tool { animation-delay: 0ms; }
.brainbench--ring[data-shocking="1"] .tcloud:nth-child(2).is-shown .tcloud__tool { animation-delay: 18ms; }
.brainbench--ring[data-shocking="1"] .tcloud:nth-child(3).is-shown .tcloud__tool { animation-delay: 34ms; }
.brainbench--ring[data-shocking="1"] .tcloud:nth-child(4).is-shown .tcloud__tool { animation-delay: 10ms; }
.brainbench--ring[data-shocking="1"] .tcloud:nth-child(5).is-shown .tcloud__tool { animation-delay: 28ms; }
.brainbench--ring[data-shocking="1"] .tcloud:nth-child(6).is-shown .tcloud__tool { animation-delay: 44ms; }
.brainbench--ring[data-shocking="1"] .tcloud:nth-child(7).is-shown .tcloud__tool { animation-delay: 8ms; }
   animation (no jitter) — lighter than the head's sway. */
.tcloud__bolt {
  display: block;
  pointer-events: none;
  transform-origin: 54px 0px;
  animation: bolt-rock 3.4s ease-in-out infinite alternate;
  /* screen blend: on void the dark cross-hatching drops away, only the white
     paper/highlight reads — giving a naturally glowing bolt shape. */
  mix-blend-mode: screen;
  filter: brightness(1.8)
    drop-shadow(0 0 10px rgba(255,255,255,0.85))
    drop-shadow(0 0 24px rgba(255,255,255,0.4));
}
@keyframes bolt-rock {
  0%   { transform: rotate(-5deg); }
  100% { transform: rotate(5deg); }
}
html[data-palette="chalk"] .tcloud__bolt {
  filter: brightness(1.1) drop-shadow(0 1px 4px rgba(0,0,0,0.35));
}

@media (prefers-reduced-motion: reduce) {
  /* No motion: every cloud shown statically, no sizzle, no float, no bolts. */
  .tcloud { opacity: 1; animation: none; }
  .tcloud__float { animation: none; }
  .brainbench__head.is-shocking img { animation: none; }
}

@media (max-width: 960px) {
  .brainbench { min-height: 540px; }
  .tcloud { width: 150px; }
  .brainbench__head { width: 38%; }
}
/* ─── MOBILE (≤720) — content closes into the centre ─────────────────────
   Phones/small tablets: every text section pulls inside the fixed edge
   rails (ART/REEL chevrons ≈ 56px zones) and the frame; the workbench
   gets the tighter CLOUD_SLOTS_M arc (JS swaps slots <720px). */
@media (pointer: coarse) and (max-width: 1100px), (max-width: 900px) {
  /* workbench — taller stage AND the head sinks below the floor line (the
     neck-fade mask hides the cut): head + plea ride LOW, clouds keep the
     upper field entirely */
  .brainbench { min-height: 580px; }
  .tcloud { width: 124px; }
  .tcloud__tool { font-size: 15px; }
  .brainbench__head { width: 46%; bottom: -44px; }

  /* the frame becomes a content WINDOW between the chrome bands — it
     starts below the header and ends above the footbar, so its lines can
     never cross their text. Corner ticks retire on mobile. */
  .frame::before { left: 10px; right: 10px; top: 64px; }
  .frame::after  { left: 10px; right: 10px; bottom: 64px; }
  .frame .v-l, .frame .v-r { top: 64px; bottom: 64px; }
  .frame .v-l { left: 10px; }
  .frame .v-r { right: 10px; }
  .frame .x { display: none; }
  .header { top: 20px; left: 26px; right: 26px; }
  .header .seg { gap: 10px; }
  .footbar { bottom: 24px; left: 26px; right: 26px; }
  /* clock + its slash yield the centre to the tucked-in toggle; LIVE stays */
  .header .seg:last-child span:nth-child(1),
  .header .seg:last-child span:nth-child(2) { display: none; }

  /* hero — masthead takes the width it deserves; rhythm tightens */
  .hero { min-height: auto; padding-top: 110px; }
  .ekg { width: 190px; height: 24px; }
  /* dead space below the strip: the motto divider carried 76+36px of
     desktop headroom */
  .motto-block { padding-top: 30px; }
  .motto { margin-top: 14px; }
  /* (mark-wrap font-size + broadside hero row-gap overrides live AFTER the
     broadside base rules — they were silently losing the cascade here) */
  /* (strip padding-inline lives AFTER the broadside base rules — they set
     padding-left: 0 and would override it from here; see the ≤720 block
     by the broadside grid rules) */

  /* other text sections come off the rails */
  .workbench .head, .ventures .head, .motto-block, .lineage { padding-inline: 44px; }
}

/* ─── cabinet / index ─────────────────────────────────────────────────── */

.cabinet {
  /* No divider line between Current Impulses and History, and a tighter gap —
     the 120px + 56px + rule read as a hard break with dead space. */
  margin-top: 56px;
  padding-top: 0;
}

.cabinet-head {
  display: grid;
  grid-template-columns: 180px 1fr 220px;
  column-gap: 56px;
  align-items: end;
  margin-bottom: 36px;
}
.cabinet-head h2 {
  font-family: var(--display);
  font-weight: var(--display-weight);
  font-style: var(--display-italic);
  font-size: clamp(60px, 8vw, 140px);
  line-height: 0.88;
  letter-spacing: -0.03em;
  color: var(--fg);
  text-transform: uppercase;
}
.cabinet-head .meta { text-align: right; color: var(--fg-3); }
.cabinet-head .meta .lbl-strong { color: var(--fg); }

/* ─── Graveyard (History plots) ─────────────────────────────────────────
   Each role is a headstone — domed etched slab, engraved face, planted in
   grass. Older plots lean + sink (weathered); the live role glows instead
   of reading as buried. Hover/focus lifts a stone and fills the epitaph. */
.graveyard {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: flex-end;
  gap: 18px 30px;
  padding: 34px 0 14px;
}
.grave {
  position: relative;
  width: 200px;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  color: var(--fg-3);
  cursor: pointer;
  transform: rotate(var(--lean, 0deg)) translateY(var(--sink, 0px));
  transform-origin: 50% 100%;
  transition: transform 280ms cubic-bezier(.2, .9, .2, 1), opacity 280ms;
  opacity: 0.9;
  -webkit-tap-highlight-color: transparent;
}
.grave:hover,
.grave.is-active {
  transform: rotate(0deg) translateY(-12px);
  opacity: 1;
  z-index: 4;
}
.grave__base {
  position: absolute;
  left: 50%;
  bottom: 8px;
  width: 78%;
  height: 18px;
  z-index: 0;
  transform: translateX(-50%);
  background: radial-gradient(ellipse at center, rgba(0, 0, 0, 0.6), transparent 70%);
  pointer-events: none;
}
.grave__stone {
  position: relative;
  z-index: 1;
  display: block;
  width: 100%;
  height: auto;
}
.grave__slab {
  fill: color-mix(in oklab, var(--fg) 5%, transparent);
  stroke: currentColor;
  stroke-width: 1.6;
  opacity: 0.6;
  transition: fill 280ms, opacity 280ms, stroke 280ms;
}
.grave:hover .grave__slab,
.grave.is-active .grave__slab {
  fill: color-mix(in oklab, var(--fg) 9%, transparent);
  opacity: 1;
}
.grave.is-live .grave__slab { stroke: var(--accent); opacity: 0.85; }
.grave.is-live .grave__stone {
  filter: drop-shadow(0 0 9px color-mix(in oklab, var(--fg) 35%, transparent));
}
.grave__grass {
  position: absolute;
  left: 50%;
  bottom: 1px;
  z-index: 2;
  width: 94px;
  height: auto;
  transform: translateX(-50%);
  color: var(--fg-3);
  opacity: 0.7;
  pointer-events: none;
}
/* Engraved face sits over the slab. */
.grave__face {
  position: absolute;
  inset: 0;
  z-index: 3;
  padding: 84px 18px 30px;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  pointer-events: none;
}
.grave__no {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.32em;
  color: var(--fg-4);
  margin-bottom: 9px;
}
.grave__name {
  font-family: var(--display);
  font-weight: var(--display-weight);
  font-size: 16.5px;
  line-height: 1.08;
  text-transform: uppercase;
  color: var(--fg-3);
  text-wrap: balance;
  /* debossed / carved look */
  text-shadow: 0 1px 0 rgba(255, 255, 255, 0.06), 0 -1px 1px rgba(0, 0, 0, 0.55);
  transition: color 240ms;
}
.grave:hover .grave__name,
.grave.is-active .grave__name { color: var(--fg); }
.grave__foot {
  margin-top: auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}
.grave__year {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  color: var(--fg-3);
}
.grave__status {
  display: inline-block;
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-weight: 700;
  padding: 2px 7px;
  border: 1px solid currentColor;
  border-radius: 2px;
}
.grave__status.live { color: var(--bg); background: var(--accent); border-color: var(--accent); }
.grave__status.done { color: var(--fg-4); border-color: var(--fg-4); }


/* ─── History portal — scroll-driven descent ────────────────────────────
   The .portal-track (in .stage) is a tall invisible scroll region. .portal
   is a fixed overlay OUTSIDE .stage (so the motion-blur filter can't break
   pinning); JS reads the track progress and drives the rings/entries. */
.portal-track { position: relative; height: 720vh; }

.portal {
  position: fixed;
  inset: 0;
  z-index: 30;
  overflow: hidden;
  background: var(--bg);
  visibility: hidden;
  opacity: 0;
  pointer-events: none;
  /* Shared disc size for the sun + eclipse mouth — matched to the intro's
     eclipse-smack discs (.ls-mono__earth/.ls-mono__moon) so the history
     eclipse reads as the SAME body the site opened on. */
  --portal-disc: min(56vmin, 560px);
}
/* Eclipse-formation: a bright sun a dark moon slides over to occlude it,
   forming the portal mouth as you scroll in. */
.portal__form { position: absolute; inset: 0; pointer-events: none; }
.portal__sun {
  position: absolute;
  left: 50%;
  top: 50%;
  width: var(--portal-disc);
  height: var(--portal-disc);
  border-radius: 50%;
  transform: translate(-50%, -50%);
  background: radial-gradient(circle, #fff 42%, color-mix(in oklab, #fff 55%, transparent) 72%, transparent 100%);
  box-shadow:
    0 0 0 1.5px color-mix(in oklab, var(--fg) 45%, transparent),
    0 0 40px 10px color-mix(in oklab, #fff 50%, transparent),
    0 0 100px 34px color-mix(in oklab, #fff 24%, transparent);
  transform: translate(-50%, calc(-50% - 85vh));
  opacity: 0;
}
.portal__moon {
  position: absolute;
  left: 50%;
  top: 50%;
  width: var(--portal-disc);
  height: var(--portal-disc);
  border-radius: 50%;
  transform: translate(-50%, calc(-50% - 85vh));
  background: #050505;
  box-shadow: 0 0 0 1px color-mix(in oklab, var(--fg) 26%, transparent);
  opacity: 0;
}
/* Speed-line streaks — the tunnel FX that replaced the concentric rings
   (M: "looks very looney tunes"). Radial hairlines flickering around the
   eclipse, driven scroll-locked from the descent loop. */
.portal__streaks { position: absolute; inset: 0; }
.portal__streak {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 80px;
  height: 1px;
  background: linear-gradient(90deg,
    transparent,
    color-mix(in oklab, var(--fg) 55%, transparent) 30%,
    color-mix(in oklab, var(--fg) 60%, transparent) 70%,
    transparent);
  opacity: 0;
  will-change: transform, opacity;
}
/* The eclipse mouth at the vanishing point — a glowing ring whose CENTER is
   the page bg (dark on void, light on chalk) so the focal role's text always
   reads against it. Grows slightly as you descend. */
.portal__eclipse {
  position: absolute;
  left: 50%;
  top: 50%;
  width: var(--portal-disc);
  height: var(--portal-disc);
  border-radius: 50%;
  background: #0b0b0b;
  /* intro-sized disc: growth softened (0.7 → 0.25) so the mouth doesn't
     swallow the whole viewport by the end of the descent */
  transform: translate(-50%, -50%) scale(calc(1 + var(--p, 0) * 0.25));
  box-shadow:
    0 0 0 2px color-mix(in oklab, var(--fg) 60%, transparent),
    0 0 30px 4px color-mix(in oklab, var(--fg) 44%, transparent),
    0 0 80px 16px color-mix(in oklab, var(--fg) 24%, transparent),
    0 0 150px 40px color-mix(in oklab, var(--fg) 11%, transparent);
  animation: portal-corona 3.4s ease-in-out infinite;
  will-change: transform;
}
@keyframes portal-corona {
  0%, 100% { filter: brightness(0.9); }
  50%      { filter: brightness(1.28); }
}
/* Solar-flare corona — coronagraph rays radiating from behind the eclipse.
   Two counter-rotating spike layers (repeating-conic-gradient) masked to a
   ring band so they emanate around the occulting disc and taper outward;
   each breathes on its own cadence so the rays gently dance. Grows with --p. */
.portal__flare {
  position: absolute;
  left: 50%;
  top: 50%;
  width: calc(var(--portal-disc) * 2.5);
  height: calc(var(--portal-disc) * 2.5);
  transform: translate(-50%, -50%) scale(calc(1 + var(--p, 0) * 0.25));
  pointer-events: none;
}
.portal__flare::before,
.portal__flare::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  /* band stops retuned so the ray ring hugs the intro-sized disc edge
     (disc radius ≈ 28% of this box's farthest-corner mask radius).
     Choked: reach halved + steeper falloff so the ray tails are barely
     there — the corona reads as a tight rim, not a starburst. */
  -webkit-mask: radial-gradient(circle, transparent 26%, rgba(0,0,0,0.55) 29%, rgba(0,0,0,0.10) 33.5%, rgba(0,0,0,0.015) 38%, transparent 41%);
          mask: radial-gradient(circle, transparent 26%, rgba(0,0,0,0.55) 29%, rgba(0,0,0,0.10) 33.5%, rgba(0,0,0,0.015) 38%, transparent 41%);
  filter: blur(1.6px);
  will-change: transform, opacity;
}
.portal__flare::before {
  background: repeating-conic-gradient(from 0deg,
    color-mix(in oklab, var(--fg) 26%, transparent) 0deg 2.6deg,
    transparent 2.6deg 11deg);
  animation: flare-spin 90s linear infinite, flare-breathe 6.5s ease-in-out infinite;
}
.portal__flare::after {
  background: repeating-conic-gradient(from 5.5deg,
    color-mix(in oklab, var(--fg) 16%, transparent) 0deg 2deg,
    transparent 2deg 20deg);
  animation: flare-spin 130s linear infinite reverse, flare-breathe 9s ease-in-out infinite;
}
@keyframes flare-spin { to { transform: rotate(360deg); } }
@keyframes flare-breathe { 0%, 100% { opacity: 0.4; } 50% { opacity: 0.85; } }
/* Lite devices: the flare's two huge masked conic layers stay, but their
   blur goes — blur on continuously-animating layers is a mobile GPU tax. */
@media (pointer: coarse), (max-width: 719px) {
  .portal__flare::before,
  .portal__flare::after { filter: none; }
}
.portal__field { position: absolute; inset: 0; }.pentry {
  position: absolute;
  left: 50%;
  top: 50%;
  width: min(620px, 84vw);
  transform: translate(-50%, -50%) scale(0.1);
  opacity: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  text-align: center;
  color: var(--fg);
  will-change: transform, filter, opacity;
}
.pentry__no {
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.34em;
  color: rgba(255,255,255,0.62);
}
.pentry__name {
  font-family: var(--display);
  font-weight: var(--display-weight);
  text-transform: uppercase;
  font-size: clamp(30px, 5vw, 56px);
  line-height: 0.98;
  color: #fff;
  text-wrap: balance;
  text-shadow: 0 0 24px #000, 0 0 10px #000, 0 1px 2px #000;
}
.pentry.is-live .pentry__name {
  text-shadow: 0 0 24px #000, 0 0 10px #000,
               0 0 30px color-mix(in oklab, var(--accent) 60%, transparent);
}
.pentry__desc, .pentry__kind, .pentry__year, .pentry__no {
  text-shadow: 0 0 14px #000, 0 0 6px #000;
}
.pentry__year {
  font-family: var(--mono);
  font-size: 13px;
  letter-spacing: 0.1em;
  color: rgba(255,255,255,0.7);
}
.pentry__detail {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  max-width: 60ch;
  max-height: 0;
  overflow: hidden;
  opacity: 0;
  transition: opacity 260ms ease, max-height 260ms ease;
}
.pentry.is-focus .pentry__detail { max-height: 260px; opacity: 1; margin-top: 6px; }
.pentry__kind {
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(255,255,255,0.7);
}
.pentry__desc {
  font-size: 15px;
  line-height: 1.55;
  color: rgba(255,255,255,0.88);
  text-wrap: pretty;
}
/* Portal badges on the dark eclipse mouth — fixed colours: LIVE = white rect,
   black text; DONE = white outline + white text. */
.pentry .grave__status.live { color: #000; background: #fff; border-color: #fff; }
.pentry .grave__status.done { color: #fff; border-color: #fff; background: transparent; }
/* Persistent chunky scroll chevron — the single descent cue, jagged like the
   reel side-handles, fixed bottom-centre, pulsing in brightness. */
.scroll-chevron {
  position: fixed;
  bottom: 26px;
  left: 50%;
  transform: translateX(-50%);
  width: 122px;
  height: 54px;
  z-index: 58;
  padding: 0;
  border: 0;
  background: none;
  color: var(--fg-3);
  cursor: pointer;
  animation: portal-hint 2.4s ease-in-out infinite;
  transition: color 200ms ease, opacity 300ms ease;
}
.scroll-chevron svg { display: block; width: 100%; height: 100%; }
.scroll-chevron:hover { color: var(--fg); }
/* animation:none so the portal-hint opacity pulse can't override opacity:0
   (a running keyframe beats a plain declaration). Used both when off-home
   AND when scrolled to the bottom (nothing left below to scroll to). */
.scroll-chevron.is-hidden { opacity: 0; pointer-events: none; animation: none; }
/* Gallery's own scroll chevron sits above the pane (z-70). */
.gallery-scroll-chevron { z-index: 78; }

.portal__hud {
  position: absolute;
  top: auto;
  bottom: 78px;
  left: 0;
  right: 0;
  display: flex;
  justify-content: center;
}
.portal__cue {
  width: 26px;
  height: 30px;
  color: var(--fg-3);
  animation: portal-hint 2.4s ease-in-out infinite;
}
.portal__cue svg {
  width: 100%;
  height: 100%;
  fill: none;
  stroke: currentColor;
  stroke-width: 2.4;
  stroke-linecap: round;
  stroke-linejoin: round;
}
@keyframes portal-hint { 0%, 100% { opacity: 0.35; } 50% { opacity: 0.95; } }

/* Reduced-motion fallback — a plain readable history list. */
.histlist { list-style: none; margin: 30px 0 0; padding: 0; }
.histlist__row {
  display: grid;
  grid-template-columns: 50px 1fr 200px;
  column-gap: 30px;
  padding: 20px 0;
  border-top: var(--rule-w) solid var(--rule);
}
.histlist__no { font-family: var(--mono); color: var(--fg-3); font-size: 13px; }
.histlist__name {
  font-family: var(--display);
  font-weight: var(--display-weight);
  text-transform: uppercase;
  font-size: 18px;
  color: var(--fg);
}
.histlist__kind {
  display: block;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--fg-3);
  margin-top: 4px;
}
.histlist__meta { font-family: var(--mono); font-size: 12px; color: var(--fg-3); text-align: right; }
.histlist__desc {
  grid-column: 2 / 3;
  margin: 10px 0 0;
  font-size: 14px;
  line-height: 1.5;
  color: var(--fg-2);
}

.catalog {
  display: grid;
  grid-template-columns: 180px 1fr 220px;
  column-gap: 56px;
}
.catalog .col-head {
  display: contents;
}
.catalog .col-head > div {
  padding: 10px 0;
  border-top: var(--rule-w) solid var(--rule);
  border-bottom: var(--rule-w) solid var(--rule);
  color: var(--fg-4);
  font-family: var(--mono);
  font-size: 10.5px; letter-spacing: 0.18em; text-transform: uppercase;
  font-weight: 700;
}
.catalog .col-head > div:nth-child(2) { padding-left: 0; }
.catalog .col-head > div:last-child   { text-align: right; }

.row {
  display: contents;
  cursor: default;
}
.row > .cell {
  padding: 22px 0;
  border-bottom: var(--rule-w) solid var(--rule);
  color: var(--fg);
  transition: color 240ms, background 240ms;
}
.row > .cell.no { color: var(--fg-3); font-family: var(--mono); font-size: 13px; letter-spacing: 0.06em; padding-top: 28px; font-weight: 700; position: relative; }
/* Hover marker — a small `//` appears in the left gutter of the hovered
   row, sliding in from the left. The row's existing color brightening
   is now stripped back to just the year + role title so the hover
   reads as a focused gesture instead of a wash. */
.row > .cell.no::before {
  content: "//";
  position: absolute;
  left: -28px;
  top: 28px;
  color: var(--fg);
  font-family: var(--mono);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.06em;
  opacity: 0;
  transform: translateX(-6px);
  transition: opacity 200ms ease, transform 220ms cubic-bezier(.2,.7,.1,1);
  pointer-events: none;
}
.row:hover > .cell.no::before {
  opacity: 1;
  transform: translateX(0);
}
.row > .cell.name {
  font-family: var(--display);
  font-weight: var(--display-weight);
  font-size: clamp(28px, 2.6vw, 40px);
  line-height: 1.05;
  letter-spacing: var(--display-tracking);
  text-transform: uppercase;
}
.row > .cell.name .kind {
  display: block;
  font-family: var(--mono);
  font-size: 10.5px; letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--fg-3);
  margin-top: 8px;
  font-weight: 500;
}
.row > .cell.meta {
  text-align: right;
  font-family: var(--mono);
  font-size: 11.5px;
  color: var(--fg-2);
  padding-top: 26px;
}
.row > .cell.meta .status {
  display: inline-block;
  margin-left: 10px;
  padding: 3px 10px;
  border: var(--rule-w) solid var(--rule);
  color: var(--fg);
  font-size: 10px; letter-spacing: 0.16em;
  text-transform: uppercase;
  white-space: nowrap;
  font-weight: 700;
}
.row > .cell.meta .status.live   { color: var(--bg); background: var(--accent); border-color: var(--accent); }
.row > .cell.meta .status.soon   { color: var(--accent); border-color: var(--accent); }
.row > .cell.meta .status.open   { color: var(--fg); border-color: var(--fg); }
.row > .cell.meta .status.buried { color: var(--fg-4); border-color: var(--fg-4); }
.row > .cell.meta .status.done   { color: var(--fg-3); }

/* Hover reads as a focused gesture: only the year + role title shift to
   full white. The earlier accent flashes on kind/number are removed so
   the row doesn't flash with color noise. */
.row:hover > .cell.name { color: var(--fg); }
.row:hover > .cell.meta { color: var(--fg); }

/* expanded description revealed on hover, in marginal column? we'll insert
   the description text as a third 'description' row that slides open. */
.row .desc {
  grid-column: 2 / 3;
  max-height: 0;
  overflow: hidden;
  color: var(--fg-2);
  font-family: var(--display);
  font-weight: var(--display-weight);
  font-size: 17px;
  line-height: 1.45;
  letter-spacing: 0;
  font-style: var(--display-italic);
  transition: max-height 380ms cubic-bezier(.2,.7,.1,1), padding 240ms, opacity 240ms;
  opacity: 0;
  padding: 0;
  border-bottom: 1px solid transparent;
}
.row[data-open="true"] .desc {
  max-height: 220px;
  opacity: 1;
  padding: 0 0 26px 0;
  border-bottom: 1px solid var(--rule);
}

/* ─── ventures / footer ───────────────────────────────────────────────── */

.ventures {
  margin-top: 96px;
  padding: 36px 0 0;
  border-top: var(--rule-w) solid var(--rule);
  display: grid;
  grid-template-columns: 180px 1fr;
  column-gap: 56px;
}
.ventures .ll {
  display: flex; flex-direction: column; gap: 14px;
}
.ventures .ll a {
  display: flex; align-items: baseline; gap: 18px;
  padding-bottom: 14px;
  border-bottom: var(--rule-w) solid var(--rule-2);
  font-family: var(--display);
  font-style: var(--display-italic);
  font-weight: var(--display-weight);
  font-size: clamp(28px, 2.6vw, 42px);
  color: var(--fg);
  transition: color 200ms, padding 240ms, transform 240ms;
  text-transform: uppercase;
  letter-spacing: var(--display-tracking);
}
.ventures .ll a .out {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--fg-4);
  transition: color 200ms;
  white-space: nowrap;
  flex-shrink: 0;
}
.ventures .ll a:hover { color: var(--accent); padding-left: 12px; }
.ventures .ll a:hover .out { color: var(--accent); }

/* ─── Electricity ─────────────────────────────────────────────────────── */

/* Center missile: a seething electric spear running down the middle of the
   viewport. Reverses direction (arrowhead + dash flow) on scroll-direction
   change. Turbulence + displacement filters give it constant writhing
   movement. Thickness/brightness scale with --scroll-v so it gets violent
   when you scroll fast; motion-blurred .stage content streaks past it. */
.missile {
  position: fixed;
  top: 0;
  left: 50%;
  width: clamp(120px, 14vw, 240px);
  height: 100vh;
  transform: translateX(-50%);
  pointer-events: none;
  z-index: 1;
  color: var(--fg);
  display: none;
}
.missile svg {
  width: 100%;
  height: 100%;
  display: block;
  overflow: visible;
}
html[data-missile="true"] .missile { display: block; }

.missile .halo {
  fill: none;
  stroke: var(--accent);
  stroke-width: calc(14px + var(--scroll-v, 0) * 0.003px);
  stroke-linecap: round;
  opacity: calc(0.16 + var(--scroll-v, 0) * 0.00014);
  filter: drop-shadow(0 0 24px color-mix(in oklab, var(--accent) 70%, transparent))
          drop-shadow(0 0 48px color-mix(in oklab, var(--accent) 35%, transparent));
}
.missile .core {
  fill: none;
  stroke: var(--fg);
  stroke-width: calc(3.5px + var(--scroll-v, 0) * 0.0022px);
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-dasharray: 38 24;
  stroke-dashoffset: 0;
  opacity: calc(0.78 + var(--scroll-v, 0) * 0.00018);
  animation: missile-flow 1.7s linear infinite;
  filter: drop-shadow(0 0 8px color-mix(in oklab, var(--accent) 80%, transparent));
}
.missile .ridge {
  fill: none;
  stroke: var(--accent);
  stroke-width: 1.6px;
  stroke-linecap: round;
  stroke-dasharray: 6 78;
  opacity: 0.95;
  animation: missile-flow 0.85s linear infinite;
  filter: drop-shadow(0 0 5px var(--accent));
}
/* scroll up → reverse the dash flow */
.missile.dir-up .core,
.missile.dir-up .ridge { animation-direction: reverse; }

@keyframes missile-flow {
  to { stroke-dashoffset: -240; }
}

.missile .head {
  fill: var(--accent);
  opacity: 0;
  transition: opacity 220ms ease;
  filter: drop-shadow(0 0 10px var(--accent))
          drop-shadow(0 0 22px color-mix(in oklab, var(--accent) 60%, transparent));
}
.missile.dir-down .head-down { opacity: 0.95; }
.missile.dir-up   .head-up   { opacity: 0.95; }

/* monochrome palettes: dial back accent washes so it reads as light, not color */
html[data-palette="bone"]    .missile .core,
html[data-palette="paper"]   .missile .core,
html[data-palette="chalk"]   .missile .core { stroke: var(--fg); }

@media (prefers-reduced-motion: reduce) {
  .missile .core, .missile .ridge { animation: none; }
}

/* Bolts: 4 small SVG bolts radiating outward from the mark. Each bolt sits
   inside a `.bolt-frame` that handles its position + outward rotation. The
   inner `.bolt` then animates with translate+scale in its LOCAL frame so
   the rotation maps the translation to the correct outward arc direction. */
.bolt-frame {
  position: absolute;
  pointer-events: none;
  width: 14%;
  height: 14%;
  display: none;
}
html[data-bolts="true"] .bolt-frame.b1,
html[data-bolts="true"] .bolt-frame.b2,
html[data-bolts="true"] .bolt-frame.b4,
html[data-bolts="true"] .bolt-frame.b5 { display: block; }
.bolt-frame.b1 { top: -2%;    left: -6%;   transform: rotate(-35deg); }
.bolt-frame.b2 { top: -8%;    right: -6%;  transform: rotate(35deg); }
.bolt-frame.b3 { top: -10%;   left: 30%;   transform: rotate(-5deg); }
.bolt-frame.b4 { top: 42%;    right: -14%; transform: rotate(100deg); }
.bolt-frame.b5 { bottom: -4%; left: -2%;   transform: rotate(-150deg); }
.bolt-frame.b6 { top: 22%;    right: -12%; transform: rotate(85deg); }
.bolt-frame.b7 { top: 68%;    right: -6%;  transform: rotate(145deg); }
.bolt-frame.b8 { bottom: -6%; left: 42%;   transform: rotate(170deg); }
.bolt-frame.b9 { top: 38%;    left: -16%;  transform: rotate(-95deg); }
.bolt-frame.b10 { top: -4%;   left: 18%;   transform: rotate(-22deg); }
.bolt-frame.b11 { bottom: 14%;left: -10%;  transform: rotate(-115deg); }

.bolt {
  display: block;
  width: 100%;
  height: 100%;
  color: var(--fg);
  opacity: 0;
  filter: drop-shadow(0 0 6px color-mix(in oklab, var(--accent) 75%, transparent));
}

html[data-bolts="true"][data-motion="pulse"] .bolt {
  animation: bolt-arc 3s ease-out infinite;
}
html[data-bolts="true"][data-motion="pulse"] .bolt-frame.b2 .bolt { animation-delay: 0.06s; }
html[data-bolts="true"][data-motion="pulse"] .bolt-frame.b4 .bolt { animation-delay: 0.11s; }
html[data-bolts="true"][data-motion="pulse"] .bolt-frame.b5 .bolt { animation-delay: 0.04s; }

/* Translation values are in the local rotated frame, so translateY-negative
   = travels outward along each bolt's pointing direction. Lateral wobble on
   X creates the arc; scale shrinks as energy dissipates. */
@keyframes bolt-arc {
  0%, 7%     { opacity: 0;    transform: translate(0,    0)    scale(1); }
  8%         { opacity: 0.95; transform: translate(-2px, -6px) scale(1); }
  10%        { opacity: 0.85; transform: translate(4px, -22px) scale(0.95); }
  12%        { opacity: 0.55; transform: translate(-3px, -42px) scale(0.86); }
  14%        { opacity: 0.25; transform: translate(5px, -64px) scale(0.74); }
  16%        { opacity: 0;    transform: translate(-2px, -88px) scale(0.6); }
  100%       { opacity: 0;    transform: translate(0, 0) scale(1); }
}

/* Impact bolts — chaotic 750ms burst on smack. Force-shows all 11 bolt
   frames regardless of the bolts toggle and runs a one-shot fast outward
   arc with staggered per-bolt delays so they don't fire as one flash. */
.mark.mark--impact-bolts .bolt-frame { display: block !important; }
.mark.mark--impact-bolts .bolt {
  animation: bolt-impact 600ms cubic-bezier(.2, .8, .35, 1) 1 both !important;
  opacity: 0;
  filter:
    drop-shadow(0 0 8px color-mix(in oklab, var(--fg) 80%, transparent))
    drop-shadow(0 0 16px color-mix(in oklab, var(--accent) 60%, transparent));
}
.mark.mark--impact-bolts .bolt-frame.b1  .bolt { animation-delay:   0ms; }
.mark.mark--impact-bolts .bolt-frame.b2  .bolt { animation-delay:  45ms; }
.mark.mark--impact-bolts .bolt-frame.b3  .bolt { animation-delay:  90ms; }
.mark.mark--impact-bolts .bolt-frame.b4  .bolt { animation-delay:  20ms; }
.mark.mark--impact-bolts .bolt-frame.b5  .bolt { animation-delay: 110ms; }
.mark.mark--impact-bolts .bolt-frame.b6  .bolt { animation-delay:  65ms; }
.mark.mark--impact-bolts .bolt-frame.b7  .bolt { animation-delay:  35ms; }
.mark.mark--impact-bolts .bolt-frame.b8  .bolt { animation-delay:  80ms; }
.mark.mark--impact-bolts .bolt-frame.b9  .bolt { animation-delay:  15ms; }
.mark.mark--impact-bolts .bolt-frame.b10 .bolt { animation-delay:  55ms; }
.mark.mark--impact-bolts .bolt-frame.b11 .bolt { animation-delay: 100ms; }
@keyframes bolt-impact {
  0%   { opacity: 0;    transform: translate(0,   0)    scale(1.0); }
  10%  { opacity: 1;    transform: translate(-2px, -8px) scale(1.15); }
  35%  { opacity: 0.95; transform: translate(5px, -32px) scale(1.0); }
  60%  { opacity: 0.7;  transform: translate(-4px,-58px) scale(0.85); }
  80%  { opacity: 0.3;  transform: translate(6px, -84px) scale(0.7); }
  100% { opacity: 0;    transform: translate(0, -110px) scale(0.55); }
}

/* Sparks removed in favor of impact bolts */
.sparks, .spark { display: none !important; }

/* Motion blur during scroll. JS sets --scroll-v and also drives the
   SVG #motion-vblur filter's stdDeviation so the blur is VERTICAL only
   (things streak along the direction of scroll). */
html[data-motion-blur="true"] .stage {
  filter: url(#motion-vblur);
  will-change: filter;
}
/* Lite devices: NEVER wear the scroll-velocity SVG blur — a full-viewport
   CPU Gaussian re-raster per frame (the ~1fps mobile descent culprit). */
@media (pointer: coarse), (max-width: 719px) {
  html[data-motion-blur="true"] .stage { filter: none; will-change: auto; }
}

/* signature line */
/* Cable plug — anchored to the literal bottom of the PAGE (absolute in
   .stage, not the fixed footbar), centred in the same band as the footbar
   labels when fully scrolled. Display still governed by data-decals. */
html[data-decals="cable"] .stage__cable,
html[data-decals="all"]   .stage__cable {
  position: absolute;
  left: 50%;
  bottom: 30px;
  transform: translateX(-50%);
}

/* Footbar palette glyph — moon (void) / sun (chalk) after the mode label.
   Extra breathing room after the //; sits centered on the label's optical
   band (the old 5px drop read as dipping below the text). */
.footbar__mode {
  display: inline-flex;
  align-items: center;
  gap: 13px;
}
.footbar__glyph {
  width: 11px;
  height: 11px;
  display: block;
  flex-shrink: 0;
  opacity: 0.9;
}

/* ─── entrance choreography ───────────────────────────────────────────── */

.fade-in { opacity: 0; transform: translateY(8px); animation: fadeIn 900ms ease-out forwards; }
.fade-in.d1 { animation-delay: 140ms; }
.fade-in.d2 { animation-delay: 260ms; }

/* While the loading screen is up, freeze every page-chrome fade-in at its
   start state. When `is-loading` is removed (after smack), animations
   resume from where they were paused — delay phase + fade-in. So nothing
   on the page is visible BEFORE the smack; everything reveals AFTER. */
body.is-loading .fade-in {
  animation-play-state: paused;
}

/* Also hide elements that aren't .fade-in but should still vanish during
   loading so MARIO smacks on a truly blank canvas. CORTEZ (last-name),
   the UI corner frame, the fixed sun/moon switch and side chevrons, the
   footbar all stay opacity 0 until is-loading is removed. */
body.is-loading .last-name,
body.is-loading .frame,
body.is-loading .footbar,
body.is-loading .reel-cta,
body.is-loading .codex-cta,
body.is-loading .margin,
body.is-loading .fragments {
  opacity: 0 !important;
}
.last-name,
.frame,
.footbar,
.reel-cta,
.codex-cta,
.margin,
.fragments {
  transition: opacity 800ms ease 300ms;
}
.fade-in.d3 { animation-delay: 420ms; }
.fade-in.d4 { animation-delay: 620ms; }
.fade-in.d5 { animation-delay: 820ms; }
.fade-in.d6 { animation-delay: 1020ms; }
@keyframes fadeIn {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* mark entrance — drawn from a dot */
.mark-enter { animation: markEnter 1400ms cubic-bezier(.2,.7,.1,1) both; }
@keyframes markEnter {
  0%   { letter-spacing: -0.08em; opacity: 0; transform: scale(0.94); filter: blur(8px); }
  60%  { opacity: 1; filter: blur(0); }
  100% { letter-spacing: -0.03em; transform: scale(1); }
}

/* ─── layout variants ─────────────────────────────────────────────────── */

/* MIRROR — flipped asymmetric. Marginalia on the LEFT, mark on the RIGHT,
   so the missile blazes through the negative space between them at center. */
html[data-layout="mirror"] .hero {
  grid-template-columns: 280px minmax(0, 1fr);
}
html[data-layout="mirror"] .margin {
  grid-column: 1; grid-row: 1;
  border-left: 0;
  border-right: var(--rule-w) solid var(--rule);
  padding-left: 0;
  padding-right: 28px;
  text-align: right;
}
html[data-layout="mirror"] .mark-cell {
  grid-column: 2; grid-row: 1;
  justify-content: flex-end;
  padding-left: 0;
  padding-right: 4vw;
}
html[data-layout="mirror"] .mark-anno {
  left: auto;
  right: 4vw;
}

/* TOWER — the mark as a vertical totem, sized to viewport height. */
html[data-layout="tower"] .hero {
  min-height: 100vh;
  align-items: center;
}
html[data-layout="tower"] .mark-wrap {
  /* totem-scale; honor --mark-size as a floor against the viewport-height totem size */
  font-size: max(140px, var(--mark-size, 22vh), 22vh);
  max-width: 96vw;
}
html[data-layout="tower"] .mark-cell { padding-left: 2vw; }

/* BROADSIDE — the mark becomes a masthead spanning the full width. Below it,
   marginalia lays out as a horizontal row of editorial columns. */
html[data-layout="broadside"] .hero {
  grid-template-columns: 1fr;
  grid-template-rows: auto auto;   /* was auto auto auto — the phantom 3rd row
                                      added a 32px row-gap of dead space under
                                      the info strip. */
  row-gap: 32px;
  align-items: start;
  /* pull the strip's bottom line (the motto's top rule) up close to the strip
     content — 9vh of bottom padding left a tall empty gap there. */
  padding-bottom: 2.5vh;
}
html[data-layout="broadside"] .mark-cell {
  padding-left: 0;
  justify-content: center;
}
html[data-layout="broadside"] .mark-wrap {
  font-size: max(120px, var(--mark-size, 17vw), 17vw);
  letter-spacing: -0.06em;
}
html[data-layout="broadside"] .margin {
  border-left: 0;
  border-top: var(--rule-w) solid var(--rule);
  padding-left: 0;
  padding-top: 36px;
  margin-top: 56px;
  /* even 4-up split across the full width (was left-clustered flex with a
     280px cap that left dead space on the right). */
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  column-gap: 0;
  row-gap: 28px;
}
html[data-layout="broadside"] .margin .block {
  flex: initial;
  max-width: none;
  padding-left: clamp(18px, 2.2vw, 40px);
}
@media (max-width: 900px) {
  html[data-layout="broadside"] .margin { grid-template-columns: repeat(2, 1fr); }
  /* M: CONNECT + LUNAR lead on small screens (row 1), LORE + ROLES follow */
  html[data-layout="broadside"] .margin .block:nth-child(1) { order: 3; } /* LORE */
  html[data-layout="broadside"] .margin .block:nth-child(2) { order: 4; } /* ROLES */
  html[data-layout="broadside"] .margin .block:nth-child(3) { order: 1; } /* CONNECT */
  html[data-layout="broadside"] .margin .block:nth-child(4) { order: 2; } /* LUNAR */
}
@media (max-width: 560px) {
  html[data-layout="broadside"] .margin { grid-template-columns: 1fr; }
}
@media (pointer: coarse) and (max-width: 1100px), (max-width: 900px) {
  /* strip clears the fixed edge rails; declared after the broadside base
     so its padding-left: 0 can't override */
  html[data-layout="broadside"] .margin {
    margin-top: 30px;
    padding-top: 30px;
    padding-inline: 50px;
  }
  /* (row-gap / mark-wrap / hero rhythm live in the FINAL mobile block at
     the very end of this sheet — the density/layout variant rules further
     down kept winning cascade ties against them from here) */
}

/* In broadside (row) layout, the default column-layout horizontal divider
   (padding-top + border-top on .block + .block) misaligns labels — it
   pushes blocks 2–4 down by 23px relative to LORE. Override here, and
   use a VERTICAL hairline (border-left) between row items instead. */
html[data-layout="broadside"] .margin .block + .block {
  padding-top: 0;
  border-top: 0;
  border-left: 1px solid var(--rule-2);
}

/* ─── dense-edge variant ──────────────────────────────────────────────── */

html[data-density="minimal"] .frame,
html[data-density="minimal"] .fragments,
html[data-density="minimal"] .mark-anno { display: none; }

html[data-density="dense"] .margin { gap: 22px; }
html[data-density="dense"] .margin .block.extra { display: flex; }

.margin .block.extra { display: none; }
html[data-density="dense"] .margin .block.extra { display: flex; }

/* short viewport — drop the fixed footbar, tighten hero */
@media (max-height: 760px) {
  .footbar { display: none; }
  .hero { min-height: calc(100vh - 140px); }
  .stage { padding-top: 72px; padding-bottom: 56px; }
  .mark-wrap { font-size: max(72px, var(--mark-size, 9vw)); }
}

/* responsive collapse */
@media (max-width: 900px) {
  .hero { grid-template-columns: 1fr; }
  .margin { border-left: 0; border-top: 1px solid var(--rule); padding-left: 0; padding-top: 24px; margin-top: 24px; }
  .lineage, .cabinet-head, .catalog, .ventures { grid-template-columns: 1fr; column-gap: 0; row-gap: 14px; }
  .graveyard { gap: 16px 18px; }
  .grave { width: 44%; max-width: 200px; }
  .epitaph { grid-template-columns: 1fr; row-gap: 12px; }
  .epitaph__meta { text-align: left; align-items: flex-start; flex-direction: row; gap: 14px; }
  .catalog .col-head { display: none; }
  .row > .cell.meta { text-align: left; }
  .header, .footbar { left: 20px; right: 20px; top: 18px; }
  .footbar { top: auto; bottom: 18px; }
  .stage { padding: 64px 20px 64px; }
}


/* ─── Sun · Moon toggle ──────────────────────────────────────────────────
   Hermetic palette switch. Three design variants (engraved / stamped /
   radiant) and three placements (corner / margin / inline). */
.sunmoon {
  appearance: none;
  background: transparent;
  border: 0;
  padding: 0;
  margin: 0;
  cursor: pointer;
  color: var(--fg);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  z-index: 50;
  transition: transform 280ms cubic-bezier(.2,.7,.1,1), opacity 220ms ease;
  opacity: 0.78;
}
.sunmoon svg { width: 100%; height: 100%; display: block; }
.sunmoon:hover { opacity: 1; transform: scale(1.06) rotate(-4deg); }
.sunmoon:active { transform: scale(0.96); }
.sunmoon:focus-visible {
  outline: 1.5px dashed var(--accent);
  outline-offset: 6px;
  border-radius: 4px;
}

/* Design treatments — sizing differs by complexity */
.sunmoon--engraved  { width: 44px; height: 44px; }
.sunmoon--stamped   { width: 40px; height: 40px; }
.sunmoon--radiant   { width: 52px; height: 52px; }

/* Subtle idle drift for the moon */
.sunmoon--moon       { animation: sm-drift 8s ease-in-out infinite; }
@keyframes sm-drift {
  0%, 100% { transform: translateY(0) rotate(-2deg); }
  50%      { transform: translateY(-3px) rotate(2deg); }
}
.sunmoon--moon:hover { animation: none; }

/* Placement 1 — corner: fixed top-right, tucked under the header bar */
.sunmoon--corner {
  position: fixed;
  top: 56px;
  right: 22px;
}

/* Placement 2 — margin: fixed mid-left vertical strip */
.sunmoon--margin {
  position: fixed;
  top: 50%;
  left: 18px;
  transform: translateY(-50%);
}
.sunmoon--margin:hover { transform: translateY(-50%) scale(1.06) rotate(-4deg); }
.sunmoon--margin.sunmoon--moon {
  animation: sm-drift-v 8s ease-in-out infinite;
}
@keyframes sm-drift-v {
  0%, 100% { transform: translateY(calc(-50% + 0px)) rotate(-2deg); }
  50%      { transform: translateY(calc(-50% - 3px)) rotate(2deg); }
}

/* Placement 3 — inline: fixed top-right of header area, sized to match
   the header's lbl-strength scale. Slight offset so it doesn't crowd
   the LIVE indicator. */
.sunmoon--inline {
  position: fixed;
  top: 14px;
  right: 180px;
  width: 26px;
  height: 26px;
  opacity: 0.7;
}
.sunmoon--inline.sunmoon--radiant { width: 30px; height: 30px; }

/* Responsive — pull margin/corner placement closer on small screens */
@media (pointer: coarse) and (max-width: 1100px), (max-width: 900px) {
  .sunmoon--corner { top: 48px; right: 14px; }
  .sunmoon--margin { left: 10px; }
  .sunmoon--inline { right: 140px; top: 10px; width: 22px; height: 22px; }
}


/* ─── Ember canvas ────────────────────────────────────────────────────────
   Fixed full-viewport overlay. Sits above the grain (z:40) but below the
   sun/moon toggle (z:50) so embers cross MARIO and fade behind the toggle.
   Pointer-events off so nothing interactive is blocked. */
.ember-canvas {
  position: fixed;
  inset: 0;
  width: 100vw;
  height: 100vh;
  pointer-events: none;
  z-index: 45;
}



/* ─── Loading screen ──────────────────────────────────────────────────────
   Two variants — embers (canvas-driven ember coalescence into MARIO) and
   sigils (sun/moon drift, eclipse, expand). The overlay sits ABOVE every
   other layer including the sun/moon toggle and the tweaks panel, so
   nothing can be interacted with mid-load. */
.loading-screen {
  position: fixed;
  inset: 0;
  z-index: 9999;
  background: #050505;
  color: var(--fg, #fafafa);
  pointer-events: auto;
  display: grid;
  place-items: center;
  overflow: hidden;
  transition: opacity 480ms ease, filter 480ms ease;
}
.loading-screen.ls--fading {
  opacity: 0;
  filter: blur(4px);
  pointer-events: none;
}

/* ── Sigils variant ─────────────────────────────────────────────────── */
.ls-sigils {
  position: relative;
  width: 100%; height: 100%;
  display: grid;
  place-items: center;
}
.ls-sigils__field {
  position: relative;
  width: min(80vw, 560px);
  height: min(80vw, 560px);
  display: grid;
  place-items: center;
}
.ls-sigil {
  position: absolute;
  top: 50%; left: 50%;
  width: 140px; height: 140px;
  color: var(--fg, #fafafa);
  transform: translate(-50%, -50%);
  opacity: 0;
  animation-duration: 3000ms;
  animation-timing-function: cubic-bezier(.4,.0,.2,1);
  animation-fill-mode: both;
}
.ls-sigil svg { width: 100%; height: 100%; display: block; }

.ls-sigil--sun  { animation-name: ls-drift-sun; }
.ls-sigil--moon { animation-name: ls-drift-moon; }
@keyframes ls-drift-sun {
  0%   { transform: translate(-260px, -50%) scale(0.85) rotate(-12deg); opacity: 0; }
  20%  { opacity: 0.9; }
  62%  { transform: translate(-50%,   -50%) scale(1.00) rotate(0deg);   opacity: 1; }
  75%  { transform: translate(-50%,   -50%) scale(1.05) rotate(0deg);   opacity: 1; }
  100% { transform: translate(-50%,   -50%) scale(1.05) rotate(0deg);   opacity: 0.0; }
}
@keyframes ls-drift-moon {
  0%   { transform: translate(160px, -50%) scale(0.85) rotate(12deg);  opacity: 0; }
  20%  { opacity: 0.9; }
  62%  { transform: translate(-50%,  -50%) scale(1.00) rotate(0deg);   opacity: 1; }
  75%  { transform: translate(-50%,  -50%) scale(1.05) rotate(0deg);   opacity: 1; }
  100% { transform: translate(-50%,  -50%) scale(1.05) rotate(0deg);   opacity: 0.0; }
}

/* eclipse ring that draws around the meeting point, then expands */
.ls-eclipse {
  position: absolute;
  top: 50%; left: 50%;
  width: 180px; height: 180px;
  margin: -90px 0 0 -90px;
  border: 1.5px solid var(--fg, #fafafa);
  border-radius: 50%;
  opacity: 0;
  transform: scale(0.6);
  animation: ls-eclipse 3000ms cubic-bezier(.2,.7,.1,1) both;
}
@keyframes ls-eclipse {
  0%, 55% { opacity: 0; transform: scale(0.6); border-width: 1.5px; }
  62%     { opacity: 1; transform: scale(1.0); border-width: 1.5px; }
  78%     { opacity: 1; transform: scale(3.0); border-width: 1px; }
  100%    { opacity: 0; transform: scale(20.0); border-width: 1px; }
}

/* white flash at eclipse moment */
.ls-flash {
  position: absolute; inset: -10%;
  background: radial-gradient(circle at 50% 50%, rgba(255,255,255,0.85), transparent 55%);
  opacity: 0;
  animation: ls-flash 3000ms ease-out both;
}
@keyframes ls-flash {
  0%, 60% { opacity: 0; }
  66%     { opacity: 0.9; }
  72%     { opacity: 0.55; }
  100%    { opacity: 0; }
}

/* tiny caption — mono terminal label */
.ls-caption {
  position: absolute;
  bottom: 36px; left: 0; right: 0;
  text-align: center;
  font-family: var(--mono, ui-monospace, monospace);
  font-size: 11px;
  letter-spacing: 0.32em;
  color: rgba(250, 250, 250, 0.65);
}
.ls-caption--bottom { bottom: 26px; }
.ls-caption__line {
  display: block;
  opacity: 0;
  animation: ls-caption 3200ms ease-out both;
}
.ls-caption__line--2 { animation-delay: 700ms; }
@keyframes ls-caption {
  0%   { opacity: 0; transform: translateY(8px); letter-spacing: 0.5em; }
  18%  { opacity: 1; transform: translateY(0);   letter-spacing: 0.32em; }
  85%  { opacity: 1; }
  100% { opacity: 0; }
}

/* ── Embers variant ─────────────────────────────────────────────────── */
.ls-embers {
  position: absolute;
  inset: 0;
}
.ls-embers__canvas {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  display: block;
}


/* ─── Loading: Monolith (Kubrick eclipse) ─────────────────────────────────
   White void, stationary BLACK disc center with a dark solar corona,
   WHITE moon (etched, black line work) traversing from the right at
   constant linear velocity. Inverted palette — high-contrast eerie. */
.loading-screen.ls-monolith,
.loading-screen.ls-eclipse-smack,
.loading-screen.ls-eclipse-smack-dark {
  /* palette-aware: use the active palette's background */
  background: var(--bg);
}
/* Eclipse-smack overlay clears FAST so MARIO is visible early in the
   fall, not midway through. Bypasses the default 480ms fade. */
.loading-screen.ls-eclipse-smack,
.loading-screen.ls-eclipse-smack-dark {
  transition: opacity 120ms ease, filter 120ms ease;
}
.ls-monolith {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  overflow: hidden;
}
.ls-mono__earth,
.ls-mono__moon,
.ls-mono__ring {
  position: absolute;
  top: 50%; left: 50%;
  border-radius: 50%;
  transform-origin: 50% 50%;
}

/* the stationary BLACK disc */
.ls-mono__earth {
  width:  min(56vmin, 560px);
  height: min(56vmin, 560px);
  margin-left: calc(min(56vmin, 560px) / -2);
  margin-top:  calc(min(56vmin, 560px) / -2);
  background: var(--fg);
  border-radius: 50%;
  opacity: 0;
  animation: ls-mono-earth 7000ms cubic-bezier(.4, 0, .2, 1) both;
  z-index: 1;
}
/* Dark solar corona — full-viewport radial halo behind the disc.
   HOT core CHOKED close to the rim (rapid drop from 0.95 → 0.12 across
   ~4% of gradient extent) so the disc edge gets its own contrast and no
   white outline is needed. Long faint tail uses many sub-percent stops
   with a 4% noise-dither layer on top to eliminate visible banding. */
.ls-monolith::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    /* noise dither — micro-grain breaks up banding in the long tail */
    url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='200' height='200'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2'/><feColorMatrix values='0 0 0 0 0  0 0 0 0 0  0 0 0 0 0  0 0 0 0.05 0'/></filter><rect width='200' height='200' filter='url(%23n)'/></svg>"),
    radial-gradient(circle at 50% 50%,
      color-mix(in oklab, var(--fg) 98%, transparent) 28.0%,
      color-mix(in oklab, var(--fg) 90%, transparent) 28.6%,
      color-mix(in oklab, var(--fg) 78%, transparent) 29.2%,
      color-mix(in oklab, var(--fg) 62%, transparent) 30.0%,
      color-mix(in oklab, var(--fg) 46%, transparent) 31.0%,
      color-mix(in oklab, var(--fg) 32%, transparent) 32.4%,
      color-mix(in oklab, var(--fg) 22%, transparent) 34.0%,
      color-mix(in oklab, var(--fg) 15.5%, transparent) 36.0%,
      color-mix(in oklab, var(--fg) 11.5%, transparent) 39.0%,
      color-mix(in oklab, var(--fg) 9.0%, transparent) 42%,
      color-mix(in oklab, var(--fg) 7.2%, transparent) 46%,
      color-mix(in oklab, var(--fg) 5.8%, transparent) 50%,
      color-mix(in oklab, var(--fg) 4.7%, transparent) 55%,
      color-mix(in oklab, var(--fg) 3.8%, transparent) 60%,
      color-mix(in oklab, var(--fg) 3.1%, transparent) 66%,
      color-mix(in oklab, var(--fg) 2.55%, transparent) 72%,
      color-mix(in oklab, var(--fg) 2.1%, transparent) 79%,
      color-mix(in oklab, var(--fg) 1.72%, transparent) 86%,
      color-mix(in oklab, var(--fg) 1.42%, transparent) 94%,
      transparent              100%
    );
  background-size: 200px 200px, cover;
  z-index: 0;
}
@keyframes ls-mono-earth {
  0%   { opacity: 0; }
  14%  { opacity: 1; }
  88%  { opacity: 1; }
  100% { opacity: 0; }
}

/* the slowly traversing WHITE moon — plain disc with hairline outline.
   Pushes from RIGHT at constant velocity. Linear timing intentionally for
   an uncanny mechanical glide. Lifted above the earth's z-index so the
   white disc cuts visibly across the black one. */
.ls-mono__moon {
  width:  min(56vmin, 560px);
  height: min(56vmin, 560px);
  margin-left: calc(min(56vmin, 560px) / -2);
  margin-top:  calc(min(56vmin, 560px) / -2);
  background: var(--bg);
  border-radius: 50%;
  box-shadow: 0 0 0 1px color-mix(in oklab, var(--fg) 55%, transparent);
  opacity: 0;
  animation: ls-mono-moon 7000ms linear both;
  z-index: 2;
}
@keyframes ls-mono-moon {
  0%   { opacity: 1; transform: translateX(28vmin); }
  78%  { opacity: 1; transform: translateX(  0); }
  90%  { opacity: 1; transform: translateX(  0); }
  100% { opacity: 0; transform: translateX(  0); }
}

/* expanding ring on alignment — dark on the white void */
.ls-mono__ring {
  width:  min(28vmin, 280px);
  height: min(28vmin, 280px);
  margin-left: calc(min(28vmin, 280px) / -2);
  margin-top:  calc(min(28vmin, 280px) / -2);
  border: 1px solid var(--fg);
  background: transparent;
  opacity: 0;
  transform: scale(1);
  animation: ls-mono-ring 7000ms cubic-bezier(.4, 0, .2, 1) both;
  z-index: 3;
}
@keyframes ls-mono-ring {
  0%, 77% { opacity: 0; transform: scale(1);     border-width: 1px; }
  80%     { opacity: 1; transform: scale(1.02);  border-width: 1px; }
  92%     { opacity: 0.8; transform: scale(3.5); border-width: 0.5px; }
  100%    { opacity: 0; transform: scale(40);    border-width: 0.5px; }
}
  80%     { opacity: 1; transform: scale(1.02); border-width: 1px; }
  92%     { opacity: 0.8; transform: scale(3.5);   border-width: 0.5px; }
  100%    { opacity: 0; transform: scale(40);  border-width: 0.5px; }
}


/* ─── Loading: Slam (MARIO falls onto black void) ─────────────────────────
   Black overlay. MARIO starts huge + blurred + slightly above center,
   then drops forward (toward the viewer's screen), scaling down with
   blur clearing. Impact at ~700ms: brief squash, screen shake, white
   flash. Holds for ~600ms then fades. */
.loading-screen.ls-slam {
  background: #050505;
}
.ls-slam {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  overflow: hidden;
}

/* outer shaker — runs the screen-shake animation on impact */
.ls-slam__shaker {
  position: relative;
  display: grid; place-items: center;
  animation: ls-slam-shake 2200ms ease-out both;
  will-change: transform;
}
@keyframes ls-slam-shake {
  0%, 31%   { transform: translate(0, 0); }
  /* impact at ~32% (~700ms in a 2200ms timeline) */
  32%       { transform: translate(8px,  -6px); }
  34%       { transform: translate(-10px, 8px); }
  36%       { transform: translate(6px,  -4px); }
  38%       { transform: translate(-4px,  6px); }
  41%       { transform: translate(3px,  -3px); }
  44%       { transform: translate(-2px,  2px); }
  47%       { transform: translate(1px,  -1px); }
  50%, 100% { transform: translate(0, 0); }
}

/* The mark itself — uses the project's display face, big & bold */
.ls-slam__mark {
  font-family: var(--display, "Big Shoulders Stencil Display", sans-serif);
  font-weight: var(--display-weight, 800);
  font-style: var(--display-italic, normal);
  letter-spacing: -0.035em;
  text-transform: uppercase;
  color: var(--fg, #fafafa);
  /* sized similar to the live MARIO at default settings */
  font-size: min(28vw, 26vh, 360px);
  line-height: 0.88;
  animation: ls-slam-mark 2200ms cubic-bezier(.2, .9, .2, 1) both;
  transform-origin: 50% 50%;
  text-shadow:
    0 0 24px rgba(250, 250, 250, 0.18),
    0 0 80px rgba(250, 250, 250, 0.08);
  will-change: transform, filter, opacity;
}
@keyframes ls-slam-mark {
  /* fall in from in front of the camera: HUGE + blurred + transparent */
  0%   { opacity: 0;    filter: blur(14px); transform: translateY(-22vh) scale(3.4); }
  10%  { opacity: 0.55; filter: blur(10px); transform: translateY(-15vh) scale(2.6); }
  22%  { opacity: 0.9;  filter: blur(4px);  transform: translateY(-6vh)  scale(1.6); }
  /* IMPACT at 32% — overshoots slightly to register the slam */
  32%  { opacity: 1;    filter: blur(0);    transform: translateY(0)     scale(0.96); }
  /* squash → bounce → settle */
  36%  { transform: translateY(0)            scale(1.03); }
  42%  { transform: translateY(0)            scale(0.995); }
  48%  { transform: translateY(0)            scale(1.000); }
  /* hold */
  85%  { opacity: 1; transform: translateY(0) scale(1); }
  /* exit fades with the wrapper; mark stays put */
  100% { opacity: 1; transform: translateY(0) scale(1); }
}

/* white flash + dust ring at impact */
.ls-slam__flash {
  position: absolute; inset: -20%;
  background: radial-gradient(circle at 50% 50%, rgba(255,255,255,0.85), transparent 45%);
  opacity: 0;
  pointer-events: none;
  animation: ls-slam-flash 2200ms ease-out both;
}
@keyframes ls-slam-flash {
  0%, 30%  { opacity: 0; }
  32%      { opacity: 0.85; }
  36%      { opacity: 0.45; }
  42%      { opacity: 0.18; }
  55%, 100%{ opacity: 0; }
}


/* ─── Loading: Eclipse Smack ──────────────────────────────────────────────
   Monolith eclipse runs through to alignment, then MARIO falls in from
   the viewer's POV and smacks the screen — ringing the impact instead of
   the ring-expand reveal. Single 4000ms timeline shared across all
   sub-animations; the eclipse takes ~68%, smack ~68–86%, hold + fade
   the remainder. Reuses the monolith corona, earth, and moon visuals. */

/* Hide the standard ring — eclipse-smack uses the smack instead */
.ls-eclipse-smack .ls-mono__ring { display: none; }

/* Corona fades during the eclipse so by smack moment the canvas is solid
   bg color (black in void, white in chalk) — MARIO impacts on a clean
   slate rather than against a residual glow. Holds at full glow longer
   before dimming so the falloff really reads, and brightness filter
   gives the gradient extra punch at the start. */
.ls-eclipse-smack .ls-monolith::before,
.ls-eclipse-smack-dark .ls-monolith::before {
  animation: ls-es-corona-fade 4000ms linear both;
  filter: brightness(1.3);
}
@keyframes ls-es-corona-fade {
  0%   { opacity: 1; }
  20%  { opacity: 0.85; }
  40%  { opacity: 0.6; }
  60%  { opacity: 0.25; }
  72%  { opacity: 0; }
  100% { opacity: 0; }
}

/* Earth: stays visible from ~10% through ~92%, fades with the page */
.ls-eclipse-smack .ls-mono__earth {
  animation: ls-es-earth 4000ms cubic-bezier(.4, 0, .2, 1) both;
}
@keyframes ls-es-earth {
  /* Moon starts already half-overlapping the earth, so the earth must
     be visible from frame 0 instead of fading in — otherwise the moon
     appears to float over empty space at the start. */
  0%   { opacity: 1; }
  92%  { opacity: 1; }
  100% { opacity: 0; }
}

/* Moon: starts deep inside the earth (already mostly eclipsed) and snaps
   to full alignment quickly. Combined with the halved 4000ms timeline,
   the moon barely moves before the smack — a sudden, abrupt eclipse.
   No outline on the eclipsing disc so the moon merges seamlessly into
   the bg as it leaves the earth — corona faded → solid canvas. */
.ls-eclipse-smack .ls-mono__moon,
.ls-eclipse-smack-dark .ls-mono__moon {
  animation: ls-es-moon 4000ms linear both;
  box-shadow: none;
}
@keyframes ls-es-moon {
  0%   { opacity: 1; transform: translateX(8vmin); }
  68%  { opacity: 1; transform: translateX(0); }
  92%  { opacity: 1; transform: translateX(0); }
  100% { opacity: 0; transform: translateX(  0); }
}

/* MARIO smacker — nested positioner + shaker so the smack lands at the
   real live-MARIO coordinates (set via --landing-x / --landing-y on the
   wrapper from JS) while the shake can still be applied without fighting
   the landing translate. */
.ls-es__positioner {
  position: absolute;
  top: 50%; left: 50%;
  /* offset from viewport center to the live mark center (set by JS) */
  transform: translate(
    calc(-50% + var(--landing-x, 0px)),
    calc(-50% + var(--landing-y, 0px))
  );
  pointer-events: none;
  z-index: 5;
}
.ls-es__shaker {
  display: grid;
  place-items: center;
  animation: ls-es-shake 4000ms ease-out both;
  will-change: transform;
}
@keyframes ls-es-shake {
  0%, 77%   { transform: translate(0, 0); }
  /* impact at 78% */
  78%       { transform: translate(8px,  -6px); }
  79%       { transform: translate(-10px, 8px); }
  80%       { transform: translate(6px,  -4px); }
  81%       { transform: translate(-4px,  6px); }
  82%       { transform: translate(3px,  -3px); }
  83%       { transform: translate(-2px,  2px); }
  84%       { transform: translate(1px,  -1px); }
  85%, 100% { transform: translate(0, 0); }
}

.ls-es__mark {
  font-family: var(--display, "Big Shoulders Stencil Display", sans-serif);
  font-weight: var(--display-weight, 800);
  font-style: var(--display-italic, normal);
  letter-spacing: var(--landing-letter-spacing, -0.035em);
  text-transform: uppercase;
  /* must match the live .mark — keeps the balanced O on the same line */
  white-space: nowrap;
  color: var(--fg);
  font-size: var(--landing-font-size, max(96px, var(--mark-size, 28vw)));
  line-height: 0.88;
  text-shadow: 0 0 18px color-mix(in oklab, var(--fg) 10%, transparent);
  animation: ls-es-mark 4000ms cubic-bezier(.4, 0, .85, .55) both;
  transform-origin: 50% 50%;
  will-change: transform, opacity;
  backface-visibility: hidden;
}
@keyframes ls-es-mark {
  /* hidden through the eclipse, scaled up (smaller scale than before —
     scale(8+) at the live MARIO size produces an extremely large GPU
     layer that can stall the compositor). */
  0%, 68%  { opacity: 0; transform: translate3d(0, 0, 0) scale(3.6); }
  /* fall in from the viewer's POV — single smooth curve so there are
     no intermediate waypoints to create the "sudden stop" feel. */
  70%      { opacity: 1; transform: translate3d(0, 0, 0) scale(3.0); animation-timing-function: cubic-bezier(.45, 0, .85, .55); }
  /* IMPACT at 78% — squash, then bounce and settle */
  78%      { opacity: 1; transform: translate3d(0, 0, 0) scale(0.96); animation-timing-function: cubic-bezier(.2, .8, .3, 1); }
  80%      { transform: translate3d(0, 0, 0) scale(1.03); }
  83%      { transform: translate3d(0, 0, 0) scale(0.995); }
  86%, 100%{ opacity: 1; transform: translate3d(0, 0, 0) scale(1); }
}

/* white flash + dust at the smack moment — centered on the landing point
   so the flash reads as MARIO’s impact, not a generic screen wipe. */
.ls-es__flash {
  position: absolute;
  top: 50%; left: 50%;
  width: 220vmax; height: 220vmax;
  margin: -110vmax 0 0 -110vmax;
  transform: translate(var(--landing-x, 0px), var(--landing-y, 0px));
  background:
    radial-gradient(circle at 50% 50%, rgba(255, 255, 255, 0.92), transparent 30%);
  opacity: 0;
  pointer-events: none;
  z-index: 4;
  animation: ls-es-flash 4000ms ease-out both;
}
@keyframes ls-es-flash {
  0%, 76%  { opacity: 0; }
  78%      { opacity: 0.95; }
  80%      { opacity: 0.55; }
  84%      { opacity: 0.20; }
  92%, 100%{ opacity: 0; }
}



/* ─── Sun · Moon pill switch ──────────────────────────────────────────────
   Horizontal toggle: both glyphs visible on a hairline pill track, a
   solid thumb slides between them to indicate active palette. Pure CSS;
   click anywhere on the pill to toggle. */
.sunmoon-switch {
  appearance: none;
  background: transparent;
  border: none;
  padding: 0;
  margin: 0;
  cursor: pointer;
  font: inherit;
  color: var(--fg);
  z-index: 10000;
  transition: opacity 200ms ease;
  opacity: 0.92;
}
.sunmoon-switch:hover  { opacity: 1; }
.sunmoon-switch:active { transform: scale(0.97); }
.sunmoon-switch:focus-visible {
  outline: 1.5px dashed var(--accent);
  outline-offset: 6px;
  border-radius: 999px;
}

.sunmoon-switch__track {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: space-between;
  width: 128px;
  height: 52px;
  padding: 0 8px;
  border: 1px solid color-mix(in oklab, var(--fg) 60%, transparent);
  border-radius: 999px;
  background: color-mix(in oklab, var(--bg) 70%, transparent);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}

.sunmoon-switch__glyph {
  position: relative;
  z-index: 2;
  width: 40px;
  height: 40px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--fg);
  opacity: 0.9;
  transition: opacity 280ms ease, color 280ms ease;
  pointer-events: none;
}
/* Sun glyph: nudge left to optically center under the thumb, and slightly
   larger slot than the moon so it reads at the same visual weight. */
.sunmoon-switch__glyph--sun {
  transform: translateX(-5px);
  width: 45px;
  height: 45px;
}

.sunmoon-moon-img,
.sunmoon-sun-img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

/* PNG icons — moon stays black ink on ALL palettes (pops against the
   light thumb on void). Sun stays white/inverted on ALL palettes (pops
   against the dark thumb on chalk). */
.sunmoon-moon-img {
  filter: contrast(1.8);          /* black ink, every palette */
}
.sunmoon-sun-img {
  filter: invert(1) contrast(1.8); /* white ink, every palette */
}

/* Destination glyph = full opacity; active/under-thumb = dimmed */
.sunmoon-switch--dark  .sunmoon-switch__glyph--sun  { opacity: 1;    color: var(--fg); }
.sunmoon-switch--dark  .sunmoon-switch__glyph--moon { opacity: 0.28; color: var(--bg); }
.sunmoon-switch--light .sunmoon-switch__glyph--moon { opacity: 1;    color: var(--fg); }
.sunmoon-switch--light .sunmoon-switch__glyph--sun  { opacity: 0.28; color: var(--bg); }

/* Thumb: slightly frosted so the dimmed icon reads through it */
.sunmoon-switch__thumb {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 44px;
  height: 44px;
  background: color-mix(in oklab, var(--fg) 82%, transparent);
  border-radius: 50%;
  transition: left 360ms cubic-bezier(.2, .9, .2, 1);
  z-index: 1;
}

/* states — thumb on sun (left) means light mode active; on moon (right) = dark */
.sunmoon-switch--light .sunmoon-switch__thumb { left: 4px; }
.sunmoon-switch--dark  .sunmoon-switch__thumb { left: calc(100% - 48px); }

/* the glyph the thumb sits under flips to the bg color and goes fully opaque */
.sunmoon-switch--light .sunmoon-switch__glyph--sun  { color: var(--bg); opacity: 1; }
.sunmoon-switch--dark  .sunmoon-switch__glyph--moon { color: var(--bg); opacity: 1; }

/* placements */
.sunmoon-switch--top-center {
  position: fixed;
  top: 40px;
  left: 50%;
  transform: translateX(-50%);
}
.sunmoon-switch--top-center:active { transform: translateX(-50%) scale(0.97); }

.sunmoon-switch--corner {
  position: fixed;
  top: 40px;
  right: 22px;
}
.sunmoon-switch--margin {
  position: fixed;
  top: 50%;
  left: 18px;
  transform: translateY(-50%);
}
.sunmoon-switch--margin:active { transform: translateY(-50%) scale(0.97); }
.sunmoon-switch--inline {
  position: fixed;
  top: 12px;
  right: 168px;
}

@media (pointer: coarse) and (max-width: 1100px), (max-width: 900px) {
  /* top-center toggle tucks INTO the header band (the free centre between
     the header's two segments) so scrolling content never passes under it */
  .sunmoon-switch--top-center { top: 8px; }
  .sunmoon-switch--corner     { top: 28px; right: 14px; }
  .sunmoon-switch--margin     { left: 10px; }
  .sunmoon-switch--inline     { right: 130px; top: 8px; }
  .sunmoon-switch__track      { width: 70px; height: 32px; padding: 0 5px; }
  .sunmoon-switch__thumb      { width: 25px; height: 25px; }
  .sunmoon-switch--dark .sunmoon-switch__thumb { left: calc(100% - 29px); }
}


/* ─── Mark impact pulse ───────────────────────────────────────────────────
   Triggered from the eclipse-smack loading screen at the alignment moment.
   Elastic "rubberband release" — fast initial snap, then a damped
   oscillation around the rest size, decreasing amplitude each cycle.
   Once the class is removed (~1800ms later), the default pulse-mark +
   electric animations resume naturally. */
.mark.mark--impact {
  animation: mark-impact 820ms linear both !important;
}
@keyframes mark-impact {
  /* LINEAR drop — constant velocity, no acceleration easing. Drop takes
     ~72% of the timeline (~590ms of 820ms) so it reads as a slower,
     deliberate fall. */
  0%   { transform: scale(14); animation-timing-function: linear; }
  /* Impact + single tight oscillation, sudden stop. */
  72%  { transform: scale(1);    animation-timing-function: cubic-bezier(.4, 0, .6, 1); }
  80%  { transform: scale(1.07); animation-timing-function: cubic-bezier(.4, 0, .6, 1); }
  88%  { transform: scale(0.98); animation-timing-function: cubic-bezier(.4, 0, .6, 1); }
  93%, 100% { transform: scale(1); }
}


/* ─── Mark — rest hold ────────────────────────────────────────────────────
   Suspends MARIO's animations for a beat after the impact pulse before
   the natural pulse-mark + electric resume. Pure hold; no transform
   animation. */
.mark.mark--rest {
  animation: none !important;
  transform: scale(1) !important;
}

/* ─── Spark burst ─────────────────────────────────────────────────────────
   Rendered at App level (NOT inside the loading overlay) so the overlay's
   opacity fade doesn't dim them. Particles fly outward radially from the
   impact point, scaling down + rotating + fading. Color palette-aware
   via var(--fg). */
.sparks {
  position: fixed;
  pointer-events: none;
  z-index: 9998;          /* under loading overlay (9999) but above page */
  width: 0;
  height: 0;
}
.spark {
  position: absolute;
  background: var(--fg);
  border-radius: 50%;
  box-shadow:
    0 0 8px var(--fg),
    0 0 18px color-mix(in oklab, var(--fg) 55%, transparent);
  transform: translate(-50%, -50%) scale(0.4) rotate(0deg);
  opacity: 0;
  animation: spark-fly cubic-bezier(.18, .85, .35, 1) both;
}
@keyframes spark-fly {
  0%   {
    transform: translate(-50%, -50%) scale(0.4) rotate(0deg);
    opacity: 0;
  }
  8%   {
    opacity: 1;
    transform: translate(calc(-50% + var(--dx) * .08), calc(-50% + var(--dy) * .08))
               scale(1.4) rotate(calc(var(--rot) * .15));
  }
  60%  {
    opacity: 0.85;
    transform: translate(calc(-50% + var(--dx) * .75), calc(-50% + var(--dy) * .75))
               scale(0.7) rotate(calc(var(--rot) * .7));
  }
  100% {
    transform: translate(calc(-50% + var(--dx)), calc(-50% + var(--dy)))
               scale(0.25) rotate(var(--rot));
    opacity: 0;
  }
}


/* ─── Watch Reel CTA ──────────────────────────────────────────────────────
   Fixed at the right edge of the viewport, vertically centered. Dim by
   default (darker than fg), brightens to full fg on hover and nudges
   left with a chevron pull. Hidden while the reel is open. */
/* Nudged above vertical center so it doesn't crowd the bottom footer
   (which carries the meta strip + back-to-codex CTA). */
.reel-cta {
  position: fixed;
  top: 50%;
  right: 22px;
  transform: translateY(-50%);
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  padding: 18px 6px;
  background: transparent;
  border: none;
  cursor: pointer;
  color: color-mix(in oklab, var(--fg) 35%, transparent);
  transition:
    color 240ms cubic-bezier(.2,.7,.1,1),
    transform 320ms cubic-bezier(.2,.7,.1,1),
    opacity 320ms;
  z-index: 55;
}
.reel-cta:hover { color: var(--fg); transform: translateY(-50%) translateX(-8px); }
.reel-cta:hover .reel-cta__chevron { transform: translateX(4px); }
.reel-cta:active { transform: translateY(-50%) translateX(-8px) scale(0.97); }
.reel-cta:focus-visible {
  outline: 1.5px dashed var(--accent);
  outline-offset: 6px;
  border-radius: 4px;
}
.reel-cta.is-hidden {
  opacity: 0;
  pointer-events: none;
  transform: translateY(-50%) translateX(20px);
}
/* Auto-fade when the chevron's vertical band overlaps a content section
   (cabinet / workbench / signature). Driven by JS in App's useEffect that
   toggles data-chevron-covered on <html>. */
html[data-chevron-covered="true"] .reel-cta,
html[data-chevron-covered="true"] .codex-cta {
  opacity: 0;
  pointer-events: none;
}
.reel-cta__chevron {
  width: 56px;
  height: 130px;
  display: block;
  transition: transform 280ms cubic-bezier(.2,.7,.1,1), filter 220ms ease;
}
.reel-cta__lbl {
  font-family: var(--display);
  font-weight: var(--display-weight, 700);
  font-size: 28px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  line-height: 1;
  white-space: nowrap;
  text-shadow:
    0 0 8px  color-mix(in oklab, var(--fg) 35%, transparent),
    0 0 20px color-mix(in oklab, var(--fg) 18%, transparent);
}
@media (pointer: coarse) and (max-width: 1100px), (max-width: 900px) {
  .reel-cta { right: 12px; padding: 14px 4px; }
  .reel-cta__chevron { width: 44px; height: 100px; }
  .reel-cta__lbl { font-size: 22px; }
}

/* ─── Back-to-Codex CTA ───────────────────────────────────────────────────
   Mirror of .reel-cta: fixed at left edge, chevron pointing left,
   `// CODEX` label below. Visible only when the reel pane is open. */
.codex-cta {
  position: fixed;
  top: 50%;
  left: 22px;
  transform: translateY(-50%);
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  padding: 18px 6px;
  background: transparent;
  border: none;
  cursor: pointer;
  color: color-mix(in oklab, var(--fg) 35%, transparent);
  transition:
    color 240ms cubic-bezier(.2,.7,.1,1),
    transform 320ms cubic-bezier(.2,.7,.1,1),
    opacity 320ms;
  z-index: 75;
}
.codex-cta:hover { color: var(--fg); transform: translateY(-50%) translateX(8px); }
.codex-cta:hover .codex-cta__chevron { transform: translateX(-4px); }
.codex-cta:active { transform: translateY(-50%) translateX(8px) scale(0.97); }
.codex-cta:focus-visible {
  outline: 1.5px dashed var(--accent);
  outline-offset: 6px;
  border-radius: 4px;
}
.codex-cta.is-hidden {
  opacity: 0;
  pointer-events: none;
  transform: translateY(-50%) translateX(-20px);
}
.codex-cta__chevron {
  width: 56px;
  height: 130px;
  display: block;
  transition: transform 280ms cubic-bezier(.2,.7,.1,1), filter 220ms ease;
}
.codex-cta__lbl {
  font-family: var(--display);
  font-weight: var(--display-weight, 700);
  font-size: 28px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  line-height: 1;
  white-space: nowrap;
  text-shadow:
    0 0 8px  color-mix(in oklab, var(--fg) 35%, transparent),
    0 0 20px color-mix(in oklab, var(--fg) 18%, transparent);
}
@media (pointer: coarse) and (max-width: 1100px), (max-width: 900px) {
  .codex-cta { left: 12px; padding: 14px 4px; }
  .codex-cta__chevron { width: 44px; height: 100px; }
  .codex-cta__lbl { font-size: 22px; }
}

/* ─── Slide transition between home and reel ──────────────────────────────
   Stage translates LEFT when reel opens; reel pane sits off-screen to the
   right and slides in. Both transitions share an ease for a coherent
   slot-machine-style slide. */
.stage {
  transition: transform 620ms cubic-bezier(.4, .0, .2, 1),
              opacity   620ms cubic-bezier(.4, .0, .2, 1);
  will-change: transform;
  /* Always-on horizontal blur filter; stdDeviation X is driven by JS
     to peak during reel slide transitions and settle back to 0. */
  filter: url(#motion-hblur);
}
.stage--reel-open {
  transform: translateX(-30vw);
  opacity: 0;
  pointer-events: none;
}

/* ─── Reel pane ───────────────────────────────────────────────────────────
   Full-viewport overlay slid in from the right. Header at top with back
   button. Body shows the placeholder video frame. */
.reel-pane {
  position: fixed;
  top: 76px; right: 0;
  width: 100vw;
  height: calc(100vh - 152px);
  background: var(--bg);
  color: var(--fg);
  z-index: 70;
  transform: translateX(100vw);
  transition: transform 620ms cubic-bezier(.4, .0, .2, 1);
  display: grid;
  padding: 0 clamp(20px, 4vw, 56px);
  /* Allow the reel pane to scroll internally when its content (frame +
     caption) doesn't fit within (100vh - 152px) of header/footer chrome.
     scrollbar-gutter reserves space so the bolt animations bobbing past
     the bounds don't toggle the scrollbar on/off and shift the video. */
  overflow-y: auto;
  overflow-x: hidden;
  scrollbar-gutter: stable;
  overscroll-behavior: contain;
  pointer-events: none;
}
.reel-pane.is-open {
  transform: translateX(0);
  pointer-events: auto;
}

.reel-pane__body {
  display: grid;
  place-items: center;
  align-content: center;
  gap: 22px;
  padding: 36px 0;
}

.reel-frame {
  position: relative;
  /* Sized so the 16:9 frame always fits within the available pane height,
     never overflows the viewport. The (100vh - 280px) * 16/9 term caps
     width based on remaining height after header/footer/caption chrome. */
  width: min(86vw, 1500px, calc((100vh - 280px) * 16 / 9));
  aspect-ratio: 16 / 9;
  background: color-mix(in oklab, var(--fg) 5%, transparent);
  border: 1px solid color-mix(in oklab, var(--fg) 25%, transparent);
  display: grid;
  place-items: center;
}
.reel-frame__corner {
  position: absolute;
  width: 22px; height: 22px;
  border: 1.5px solid var(--fg);
}
.reel-frame__corner--tl { top: -1px; left: -1px;  border-right: none; border-bottom: none; }
.reel-frame__corner--tr { top: -1px; right: -1px; border-left: none;  border-bottom: none; }
.reel-frame__corner--bl { bottom: -1px; left: -1px;  border-right: none; border-top: none; }
.reel-frame__corner--br { bottom: -1px; right: -1px; border-left: none;  border-top: none; }

/* YouTube iframe fill — sits above the bolts but below the corner ticks
   (which stay on z:3 so they read as crosshair brackets framing the video). */
.reel-frame__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
  z-index: 2;
  background: #000;
}

/* ─── Reel-frame thunder bolts ────────────────────────────────────────────
   12 small <Bolt> SVGs distributed around the perimeter of the reel frame.
   Each frame is rotated so the bolt's local "up" points outward (away from
   center); the inner SVG then animates translateY-negative so each bolt
   travels along its own pointing direction. Staggered delays produce a
   continuous sizzle of bolts arcing outward from behind the video.
   Sits behind the frame's content (.reel-frame__plate) via z-index. */
.reel-bolt {
  position: absolute;
  display: block;
  width: 16px;
  height: 32px;
  pointer-events: none;
  color: var(--fg);
  opacity: 0;
  z-index: 0;
}
.reel-bolt-svg {
  display: block;
  width: 100%;
  height: 100%;
  filter:
    drop-shadow(0 0 5px  color-mix(in oklab, var(--fg) 60%, transparent))
    drop-shadow(0 0 14px color-mix(in oklab, var(--fg) 35%, transparent));
}

/* Make sure the plate + corners sit ABOVE the bolts so the bolts read as
   coming from behind the video. Note: corners are absolutely positioned;
   we only adjust z-index here, not position. */
.reel-frame__plate { position: relative; z-index: 2; }
.reel-frame__corner { z-index: 3; }

/* Perimeter placement + outward rotation. Top edge → angles fanning up,
   right edge → angles fanning right, etc. Corners get diagonal rotations.
   Each bolt also gets its own size + duration to break uniform rhythm. */
.reel-bolt-1  { top: -8px;   left: 18%;   transform: rotate(-22deg); width: 14px; height: 28px; }
.reel-bolt-2  { top: -8px;   left: 42%;   transform: rotate(  4deg); width: 18px; height: 36px; }
.reel-bolt-3  { top: -8px;   left: 66%;   transform: rotate( 22deg); width: 13px; height: 26px; }
.reel-bolt-4  { top: 22%;    right: -8px; transform: rotate( 70deg); width: 17px; height: 34px; }
.reel-bolt-5  { top: 50%;    right: -8px; transform: rotate( 90deg); width: 14px; height: 28px; }
.reel-bolt-6  { top: 78%;    right: -8px; transform: rotate(110deg); width: 19px; height: 38px; }
.reel-bolt-7  { bottom: -8px; left: 66%;  transform: rotate(160deg); width: 15px; height: 30px; }
.reel-bolt-8  { bottom: -8px; left: 42%;  transform: rotate(178deg); width: 16px; height: 32px; }
.reel-bolt-9  { bottom: -8px; left: 18%;  transform: rotate(200deg); width: 13px; height: 26px; }
.reel-bolt-10 { top: 78%;    left: -8px;  transform: rotate(250deg); width: 18px; height: 36px; }
.reel-bolt-11 { top: 50%;    left: -8px;  transform: rotate(270deg); width: 14px; height: 28px; }
.reel-bolt-12 { top: 22%;    left: -8px;  transform: rotate(290deg); width: 16px; height: 32px; }

.reel-frame .reel-bolt .reel-bolt-svg {
  animation: reel-bolt-fly 1800ms cubic-bezier(.2, .9, .35, 1) infinite;
}
/* Each bolt gets its own delay AND duration so they sizzle out of phase
   instead of locked-step. LINEAR easing because the arc curve is baked
   into the keyframe waypoints — cubic-bezier across multi-waypoint frames
   produces visible inflection points ("stop and start" feel).
   Durations span a WIDE range so bolts of different rhythms phase in and
   out of each other — long-duration bolts (10–13s) fire infrequently and
   create longer "rests" between bursts of activity. */
.reel-frame .reel-bolt-1  .reel-bolt-svg { animation: reel-bolt-fly 4800ms linear  -240ms infinite; }
.reel-frame .reel-bolt-2  .reel-bolt-svg { animation: reel-bolt-fly 10800ms linear  680ms infinite; }
.reel-frame .reel-bolt-3  .reel-bolt-svg { animation: reel-bolt-fly 3800ms linear  1860ms infinite; }
.reel-frame .reel-bolt-4  .reel-bolt-svg { animation: reel-bolt-fly 7600ms linear   240ms infinite; }
.reel-frame .reel-bolt-5  .reel-bolt-svg { animation: reel-bolt-fly 12500ms linear -520ms infinite; }
.reel-frame .reel-bolt-6  .reel-bolt-svg { animation: reel-bolt-fly 4400ms linear  4240ms infinite; }
.reel-frame .reel-bolt-7  .reel-bolt-svg { animation: reel-bolt-fly 9200ms linear  1300ms infinite; }
.reel-frame .reel-bolt-8  .reel-bolt-svg { animation: reel-bolt-fly 6100ms linear  3840ms infinite; }
.reel-frame .reel-bolt-9  .reel-bolt-svg { animation: reel-bolt-fly 3500ms linear  -960ms infinite; }
.reel-frame .reel-bolt-10 .reel-bolt-svg { animation: reel-bolt-fly 11600ms linear  180ms infinite; }
.reel-frame .reel-bolt-11 .reel-bolt-svg { animation: reel-bolt-fly 5400ms linear  2640ms infinite; }
.reel-frame .reel-bolt-12 .reel-bolt-svg { animation: reel-bolt-fly 8400ms linear  -320ms infinite; }

/* Wrap opacity driver — keeps the frame container visible while inner SVG
   handles the fly-out timing. */
.reel-bolt { opacity: 1; }

@keyframes reel-bolt-fly {
  /* Active sizzle: bolt fires from the perimeter and arcs outward, scale
     ramps up then fades. The arc curve is encoded by the X/Y/scale
     progressions across many small waypoints — with linear easing this
     reads as continuous motion instead of step-and-stop.
     Active phase: 0–18% of cycle (~600–1000ms of the long durations).
     Dormant phase: 18→100% (bolt invisible, parked off-screen). */
  0%   { opacity: 0;    transform: translate( 0,    0)    scale(0.35); }
  2%   { opacity: 0.6;  transform: translate( 0,   -1px) scale(0.7); }
  4%   { opacity: 1;    transform: translate( 0,   -3px) scale(1.00); }
  6%   { opacity: 1;    transform: translate(-0.5px,-7px) scale(1.06); }
  8%   { opacity: 1;    transform: translate(-1px, -13px) scale(1.10); }
  10%  { opacity: 1;    transform: translate(-1px, -22px) scale(1.08); }
  12%  { opacity: 0.95; transform: translate( 0,   -34px) scale(1.00); }
  14%  { opacity: 0.7;  transform: translate( 1px, -48px) scale(0.85); }
  16%  { opacity: 0.35; transform: translate( 2px, -64px) scale(0.6); }
  18%  { opacity: 0;    transform: translate( 3px, -80px) scale(0.35); }
  100% { opacity: 0;    transform: translate( 3px, -80px) scale(0.35); }
}

@media (prefers-reduced-motion: reduce) {
  .reel-bolt { display: none; }
}

.reel-frame__plate {
  display: grid;
  place-items: center;
  gap: 22px;
  color: var(--fg);
}
.reel-frame__play {
  width: 88px; height: 88px;
  border-radius: 50%;
  border: 1.5px solid var(--fg);
  display: grid;
  place-items: center;
  color: var(--fg);
  transition: transform 280ms cubic-bezier(.2,.7,.1,1), background 200ms;
}
.reel-frame:hover .reel-frame__play {
  transform: scale(1.08);
  background: color-mix(in oklab, var(--fg) 8%, transparent);
}
.reel-frame__placeholder {
  font-family: var(--mono, ui-monospace, monospace);
  font-size: 12px;
  letter-spacing: 0.36em;
  color: color-mix(in oklab, var(--fg) 60%, transparent);
  text-transform: uppercase;
}

.reel-pane__caption {
  display: inline-flex;
  align-items: baseline;
  gap: 14px;
  font-family: var(--mono, ui-monospace, monospace);
  font-size: 11px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: color-mix(in oklab, var(--fg) 70%, transparent);
}

@media (prefers-reduced-motion: reduce) {
  .stage, .reel-pane { transition: none; }
}


/* ─── Footer CTAs respond to scroll progress ──────────────────────────────
   --scroll-progress is set on <html> (0 at top, 1 at bottom). As you
   scroll down, SCROLL THE CODEX brightens to full fg, WATCH REEL dims
   so the footer's emphasis shifts toward the page you're engaging with. */


/* ─── Chromatic aberration accent mode ────────────────────────────────────
   When the user picks the chromatic accent swatch, --accent is forced to
   var(--fg) (greyscale, no color change) and html[data-chromatic="true"]
   is set. These rules then apply real RGB-split fringes — red shifted
   left, cyan shifted right — to accent-using targets via text-shadow on
   text and drop-shadow filters on blocks/SVGs. */
html[data-chromatic="true"] .lbl-accent,
html[data-chromatic="true"] .last-annotation,
html[data-chromatic="true"] .motto em,
html[data-chromatic="true"] .mark-anno .tick,
html[data-chromatic="true"] .fragments .f .ix,
html[data-chromatic="true"] .ventures .ll a:hover,
html[data-chromatic="true"] .ventures .ll a:hover .out,
html[data-chromatic="true"] .row > .cell.meta .status.soon,
html[data-chromatic="true"] .row:hover > .cell.name .kind,
html[data-chromatic="true"] .row:hover > .cell.no {
  text-shadow:
    -1.5px 0 0 #ff003c,
     1.5px 0 0 #00d4ff;
}

html[data-chromatic="true"] .header .dot,
html[data-chromatic="true"] .row > .cell.meta .status.live,
html[data-chromatic="true"] .lineage .star {
  box-shadow:
    -1.5px 0 0 #ff003c,
     1.5px 0 0 #00d4ff;
}

html[data-chromatic="true"] .decal-thorn,
html[data-chromatic="true"] .decal-scrawl {
  filter:
    drop-shadow(-1.5px 0 0 #ff003c)
    drop-shadow( 1.5px 0 0 #00d4ff);
}

/* INDEX/REEL big numerals in the panels use accent color — give them
   the fringe too. */
html[data-chromatic="true"] .block span[style*="--accent"],
html[data-chromatic="true"] [style*="var(--accent)"] {
  /* (selector matches by inline style; falls through harmlessly otherwise) */
}


/* ─── Hero block content additions ────────────────────────────────────────
   ROLES list + CONTACT email + social icon row. */
.block .roles {
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
  gap: 6px;
}
.block .roles li {
  font-family: var(--mono, ui-monospace, monospace);
  font-size: 12.5px;
  letter-spacing: 0.04em;
  color: var(--fg-2, color-mix(in oklab, var(--fg) 78%, transparent));
  line-height: 1.35;
  padding-left: 16px;
  position: relative;
}
/* Electro ball marker — solid warped orb (ElectroBall SVG in the JSX)
   echoing the live-wire cursor's white core, sitting exactly where the
   old "—" dash sat. Bright like the cursor, with a soft halo that is a
   blur of itself, not a dark radial. */
.roles__spark {
  position: absolute;
  left: -1px;
  top: 3px;
  width: 11px;
  height: 11px;
  color: var(--fg);
  filter: drop-shadow(0 0 3px color-mix(in oklab, currentColor 65%, transparent));
}

.contact-email {
  font-family: var(--mono, ui-monospace, monospace);
  font-size: 12.5px;
  letter-spacing: 0.04em;
  color: var(--fg);
  text-decoration: none;
  border-bottom: 1px solid color-mix(in oklab, var(--fg) 35%, transparent);
  transition: border-color 200ms, color 200ms;
  word-break: break-all;
}
.contact-email:hover {
  color: var(--accent);
  border-bottom-color: var(--accent);
}

.social-icons {
  position: relative;
  display: flex;
  align-items: flex-end;
  gap: 10px;
  margin-top: 41px;
  /* Small padding-bottom just clears the SVG ground line; keeping it tight
     lets the tombstone bases rest near the enclosure bottom (so the horizon
     doesn't float above it, matching the lunar-cycle widget baseline). */
  padding-bottom: 2px;
}
/* Jagged SVG earth horizon — sits at the bottom of the row, beneath
   the tombstone bases (which align flex-end). Subtle, matches the
   tombstone outline vocabulary. */
.social-icons__ground {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  width: 100%;
  height: 10px;
  pointer-events: none;
  color: var(--fg-3);
  overflow: visible;
  z-index: 0;
}
/* Grass tuft — sits as a flex item with same baseline as the tombstones.
   Slightly bumped opacity from earlier subtle version. */
.social-icons__grass {
  position: relative;
  width: 18px;
  height: 12px;
  margin-left: 3px;
  pointer-events: none;
  color: var(--fg-3);
  opacity: 0.85;
  z-index: 2;
  flex-shrink: 0;
}
.social-icon {
  position: relative;
  /* lift the stones a touch off the horizon without moving the ground/grass */
  margin-bottom: 3px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 36px;
  color: var(--fg-3);
  transition: color 200ms, transform 220ms cubic-bezier(.2,.7,.1,1);
}
.social-icon:hover {
  color: var(--fg);
  transform: translateY(-3px);
}
/* Tombstone frame sits behind the glyph; outline-only. */
.social-icon .tombstone {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 0;
  color: var(--fg-3);
  transition: color 220ms ease, opacity 220ms ease;
}
.social-icon:hover .tombstone {
  color: var(--fg);
  opacity: 0.85;
}
/* Glyph sits above the tombstone, centered in the rectangular portion
   of the stone (below the dome). */
.social-icon__glyph {
  display: block;
  position: relative;
  z-index: 1;
  margin-bottom: 4px;
  color: var(--fg-3);
  transition: color 200ms ease;
}
.social-icon:hover .social-icon__glyph { color: var(--fg); }

/* Dormant flank stones — the two unrouted plots (X / YouTube once handles
   exist). Unmarked, cracked, sunk lower than the live stones and slightly
   tilted, like old graves settling into the earth. Not interactive. */
.social-icon--dormant {
  margin-bottom: 0;
  transform: translateY(3px);
  opacity: 0.5;
  pointer-events: none;
}
.social-icon--dormant .tombstone {
  transform: rotate(-2.5deg);
  transform-origin: 50% 100%;
}
.social-icon--dormant-alt .tombstone { transform: rotate(2deg); }

/* ─── Tombstone rocks ────────────────────────────────────────────────────
   Four tiny rocks per tombstone, burst outward from the base on hover —
   like the stone is being yanked from the earth. Trajectory is set per
   rock via inline CSS vars (--rx, --ry, --rot). Each rock has its own
   delay so they don't all leave at once. */
.social-icon__rock {
  position: absolute;
  bottom: -2px;
  z-index: 2;
  color: var(--fg-3);
  opacity: 0;
  pointer-events: none;
  filter: drop-shadow(0 0 1.5px color-mix(in oklab, var(--fg) 30%, transparent));
}
.social-icon__rock--left  { left:  2px; }
.social-icon__rock--right { right: 2px; }

.social-icon:hover .social-icon__rock {
  animation: tomb-rock-fly 900ms cubic-bezier(.2,.7,.2,1) forwards;
}
@keyframes tomb-rock-fly {
  0%   { opacity: 0; transform: translate(0, 0) rotate(0); }
  8%   { opacity: 1; }
  60%  { opacity: 1; }
  100% { opacity: 0; transform: translate(var(--rx, 0), var(--ry, -10px)) rotate(var(--rot, 0)); }
}

@media (prefers-reduced-motion: reduce) {
  .social-icon:hover .social-icon__rock { animation: none; opacity: 0; }
}

/* ════════════════════════════════════════════════════════════════════════
   CHARACTER-ART GALLERY  (added — portfolio migration)
   A fresh "specimen sheet" of selected character work. Slides in from the
   LEFT (mirror of the reel, which comes from the right). Not a copy of the
   old Canva gallery — native to the death-metal world.
   ═══════════════════════════════════════════════════════════════════════ */

/* Home stage slides right + fades as the gallery covers from the left. */
.stage--gallery-open {
  transform: translateX(30vw);
  opacity: 0;
  pointer-events: none;
}

.gallery-pane {
  position: fixed;
  top: 76px; left: 0;
  width: 100vw;
  height: calc(100vh - 152px);
  background: var(--bg);
  color: var(--fg);
  z-index: 70;
  transform: translateX(-100vw);
  transition: transform 620ms cubic-bezier(.4, .0, .2, 1);
  overflow-y: auto;
  overflow-x: hidden;
  scrollbar-gutter: stable;
  overscroll-behavior: contain;
  pointer-events: none;
}
.gallery-pane.is-open { transform: translateX(0); pointer-events: auto; }

.gallery-pane__body {
  padding: clamp(16px, 3vw, 40px) clamp(20px, 4vw, 64px) clamp(40px, 6vw, 80px);
  max-width: 1560px;
  margin: 0 auto;
}

/* ── header ─────────────────────────────────────────────────────────────*/
.gallery-head { position: relative; margin-bottom: clamp(20px, 3vw, 38px); }
.gallery-back {
  appearance: none; background: transparent; border: none; padding: 0;
  cursor: pointer; color: var(--fg);
  font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.32em;
  text-transform: uppercase; font-weight: 700;
  opacity: 0.62; margin-bottom: 18px;
  transition: opacity 200ms, transform 220ms cubic-bezier(.2,.7,.1,1);
}
.gallery-back:hover { opacity: 1; transform: translateX(-5px); }
.gallery-back:focus-visible { outline: 1.5px dashed var(--accent); outline-offset: 4px; }
.gallery-head__eyebrow { font-size: 11px; letter-spacing: 0.26em; opacity: 0.7; margin: 0 0 8px; }
.gallery-title {
  font-family: var(--display);
  font-weight: var(--display-weight, 700);
  font-style: var(--display-italic, normal);
  letter-spacing: var(--display-tracking, 0);
  font-size: clamp(46px, 8.5vw, 124px);
  line-height: 0.82; margin: 0 0 14px; text-transform: uppercase;
}
.gallery-head__meta {
  font-family: var(--mono); font-size: 11px; letter-spacing: 0.16em;
  opacity: 0.66; margin: 0;
}
/* Pinned to --fg (not --accent): in chalk/light palettes --accent resolves
   to white and the link vanishes against the page. --fg always contrasts;
   the always-on underline keeps it reading as a link, accent flavors hover. */
.gallery-head__meta a {
  color: var(--fg); opacity: 0.85; text-decoration: none; font-weight: 700;
  border-bottom: 1px solid color-mix(in oklab, var(--fg) 55%, transparent);
  padding-bottom: 1px; transition: color 200ms, border-bottom-color 200ms, opacity 200ms;
}
.gallery-head__meta a:hover { color: var(--accent); opacity: 1; border-bottom-color: var(--accent); }

/* ── justified row-fill grid ─────────────────────────────────────────────
   Row-ordered (numbers read 01→02→03 left-to-right) and aspect-preserving
   (cells match each image's ratio → object-fit:cover shows the FULL art, no
   crop). Row heights + cell widths are computed in JS (justifyRows). */
.gjust { display: flex; flex-direction: column; gap: 12px; }
.gjust__row { display: flex; gap: 12px; }
.gjust__cell {
  appearance: none; border: none; padding: 0; margin: 0; cursor: pointer;
  position: relative; overflow: hidden; flex: 0 0 auto;
  background: #050505; color: var(--fg); display: block;
}
.gjust__cell:focus-visible { outline: 1.5px dashed var(--accent); outline-offset: 3px; }
.gjust__img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  filter: grayscale(0.16) contrast(1.03);
  transition: transform 760ms cubic-bezier(.2,.7,.1,1), filter 520ms ease;
}
.gjust__cell:hover .gjust__img,
.gjust__cell:focus-visible .gjust__img { transform: scale(1.045); filter: grayscale(0) contrast(1.07); }
.gjust__cell::after {           /* accent hairline on hover, clipped by overflow */
  content: ""; position: absolute; inset: 0; pointer-events: none;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,0.07);
  transition: box-shadow 300ms ease;
}
.gjust__cell:hover::after { box-shadow: inset 0 0 0 1px var(--accent); }
.gjust__ix {
  position: absolute; top: 9px; left: 11px; z-index: 3;
  font-family: var(--mono); font-size: 11px; letter-spacing: 0.2em;
  font-variant-numeric: tabular-nums; font-weight: 700;
  color: #fff; mix-blend-mode: difference;
}
/* caption rides in on hover from the bottom (premium gallery idiom) */
.gjust__cap {
  position: absolute; inset: auto 0 0 0; z-index: 2; pointer-events: none;
  display: flex; flex-direction: column; gap: 2px;
  padding: 26px 12px 11px;
  background: linear-gradient(180deg, transparent, rgba(0,0,0,0.78));
  opacity: 0; transform: translateY(8px);
  transition: opacity 300ms ease, transform 360ms cubic-bezier(.2,.7,.1,1);
}
.gjust__cell:hover .gjust__cap,
.gjust__cell:focus-visible .gjust__cap { opacity: 1; transform: none; }
.gjust__title {
  font-family: var(--display); font-size: clamp(15px, 1.3vw, 20px);
  letter-spacing: 0.01em; line-height: 1; text-transform: uppercase; color: #fff;
}
.gjust__tag {
  font-family: var(--mono); font-size: 10px; letter-spacing: 0.16em;
  text-transform: uppercase; color: rgba(255,255,255,0.7);
}

/* ── caption strip ──────────────────────────────────────────────────────*/
.gallery-pane__caption {
  display: flex; flex-wrap: wrap; gap: 8px; align-items: center;
  margin-top: clamp(28px, 4vw, 48px);
  font-family: var(--mono); font-size: 10px; letter-spacing: 0.26em;
  text-transform: uppercase; opacity: 0.48;
}

/* ── left-edge handle (mirror of .reel-cta) ─────────────────────────────*/
.gallery-cta {
  position: fixed; top: 50%; left: 22px; transform: translateY(-50%);
  display: inline-flex; flex-direction: column; align-items: center; gap: 3px;
  padding: 18px 6px; background: transparent; border: none; cursor: pointer;
  color: color-mix(in oklab, var(--fg) 35%, transparent);
  transition: color 240ms cubic-bezier(.2,.7,.1,1),
              transform 320ms cubic-bezier(.2,.7,.1,1), opacity 320ms;
  z-index: 55;
}
.gallery-cta:hover { color: var(--fg); transform: translateY(-50%) translateX(8px); }
.gallery-cta:hover .gallery-cta__chevron { transform: translateX(-4px); }
.gallery-cta:active { transform: translateY(-50%) translateX(8px) scale(0.97); }
.gallery-cta:focus-visible { outline: 1.5px dashed var(--accent); outline-offset: 6px; border-radius: 4px; }
.gallery-cta.is-hidden { opacity: 0; pointer-events: none; transform: translateY(-50%) translateX(-20px); }
html[data-chevron-covered="true"] .gallery-cta { opacity: 0; pointer-events: none; }
.gallery-cta__chevron {
  width: 56px; height: 130px; display: block;
  transition: transform 280ms cubic-bezier(.2,.7,.1,1), filter 220ms ease;
}
.gallery-cta__lbl {
  font-family: var(--display); font-weight: var(--display-weight, 700);
  font-size: 28px; letter-spacing: 0.06em; text-transform: uppercase;
  writing-mode: vertical-rl; line-height: 1; white-space: nowrap;
  text-shadow: 0 0 8px color-mix(in oklab, var(--fg) 35%, transparent),
               0 0 20px color-mix(in oklab, var(--fg) 18%, transparent);
}
@media (pointer: coarse) and (max-width: 1100px), (max-width: 900px) {
  .gallery-cta { left: 12px; padding: 14px 4px; }
  .gallery-cta__chevron { width: 44px; height: 100px; }
  .gallery-cta__lbl { font-size: 22px; }
}

/* Gallery → home back handle: right edge, chevron points RIGHT (home is "back
   to the right" of the gallery). Mirror of the reel's left-edge CODEX back.
   z above the gallery pane (z-70) so it sits over the grid. */
.gallery-back-cta {
  position: fixed; top: 50%; right: 22px; transform: translateY(-50%);
  display: inline-flex; flex-direction: column; align-items: center; gap: 3px;
  padding: 18px 6px; background: transparent; border: none; cursor: pointer;
  color: color-mix(in oklab, var(--fg) 35%, transparent);
  transition: color 240ms cubic-bezier(.2,.7,.1,1),
              transform 320ms cubic-bezier(.2,.7,.1,1), opacity 320ms;
  z-index: 75;
}
.gallery-back-cta:hover { color: var(--fg); transform: translateY(-50%) translateX(8px); }
.gallery-back-cta:hover .gallery-back-cta__chevron { transform: translateX(4px); }
.gallery-back-cta:active { transform: translateY(-50%) translateX(8px) scale(0.97); }
.gallery-back-cta:focus-visible { outline: 1.5px dashed var(--accent); outline-offset: 6px; border-radius: 4px; }
.gallery-back-cta.is-hidden { opacity: 0; pointer-events: none; transform: translateY(-50%) translateX(20px); }
.gallery-back-cta__chevron {
  width: 56px; height: 130px; display: block;
  transition: transform 280ms cubic-bezier(.2,.7,.1,1), filter 220ms ease;
}
.gallery-back-cta__lbl {
  font-family: var(--display); font-weight: var(--display-weight, 700);
  font-size: 28px; letter-spacing: 0.06em; text-transform: uppercase;
  writing-mode: vertical-rl; line-height: 1; white-space: nowrap;
  text-shadow: 0 0 8px color-mix(in oklab, var(--fg) 35%, transparent),
               0 0 20px color-mix(in oklab, var(--fg) 18%, transparent);
}
@media (pointer: coarse) and (max-width: 1100px), (max-width: 900px) {
  .gallery-back-cta { right: 12px; padding: 14px 4px; }
  .gallery-back-cta__chevron { width: 44px; height: 100px; }
  .gallery-back-cta__lbl { font-size: 22px; }
}
.gallery-pane:focus, .gallery-pane:focus-visible { outline: none; }

/* Vault handle — left edge of the gallery, chevron points LEFT (vault sits
   one more step left in the spatial model), VAULT label underneath. Mirror
   of .gallery-back-cta; an <a> to /vault rather than a view switch. */
.vault-cta {
  position: fixed; top: 50%; left: 22px; transform: translateY(-50%);
  display: inline-flex; flex-direction: column; align-items: center; gap: 3px;
  padding: 18px 6px; background: transparent; border: none; cursor: pointer;
  text-decoration: none;
  color: color-mix(in oklab, var(--fg) 35%, transparent);
  transition: color 240ms cubic-bezier(.2,.7,.1,1),
              transform 320ms cubic-bezier(.2,.7,.1,1), opacity 320ms;
  z-index: 75;
}
.vault-cta:hover { color: var(--fg); transform: translateY(-50%) translateX(-8px); }
.vault-cta:hover .vault-cta__chevron { transform: translateX(-4px); }
.vault-cta:active { transform: translateY(-50%) translateX(-8px) scale(0.97); }
.vault-cta:focus-visible { outline: 1.5px dashed var(--accent); outline-offset: 6px; border-radius: 4px; }
.vault-cta.is-hidden { opacity: 0; pointer-events: none; transform: translateY(-50%) translateX(-20px); }
.vault-cta__chevron {
  width: 56px; height: 130px; display: block;
  transition: transform 280ms cubic-bezier(.2,.7,.1,1), filter 220ms ease;
}
.vault-cta__lbl {
  font-family: var(--display); font-weight: var(--display-weight, 700);
  font-size: 28px; letter-spacing: 0.06em; text-transform: uppercase;
  writing-mode: vertical-rl; line-height: 1; white-space: nowrap;
  text-shadow: 0 0 8px color-mix(in oklab, var(--fg) 35%, transparent),
               0 0 22px color-mix(in oklab, var(--fg) 18%, transparent);
}
@media (pointer: coarse) and (max-width: 1100px), (max-width: 900px) {
  .vault-cta { left: 12px; padding: 14px 4px; }
  .vault-cta__chevron { width: 44px; height: 100px; }
  .vault-cta__lbl { font-size: 22px; }
}

/* ── lightbox ───────────────────────────────────────────────────────────*/
.gallery-lightbox {
  position: fixed; inset: 0; z-index: 10001;   /* above the z-10000 sun/moon so
     the backdrop fully covers chrome and top-centre is free for the up chevron */
  display: flex; align-items: center; justify-content: center;
  padding: clamp(20px, 5vh, 64px) clamp(20px, 8vw, 120px);
}
/* Backdrop is a separate layer so closing it does NOT fade the image — the
   image stays fully visible while it reverse-morphs back to its tile. */
.gallery-lightbox::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: color-mix(in oklab, var(--bg) 88%, #000);
  backdrop-filter: blur(6px);
  animation: gallery-lb-in 200ms ease;
  transition: opacity 320ms ease;
}
@keyframes gallery-lb-in { from { opacity: 0; } to { opacity: 1; } }
/* Closing: backdrop + chrome fade while the image (JS inline transform) flies
   back to its tile. */
.gallery-lightbox.is-closing::before { opacity: 0; }
.gallery-lightbox.is-closing .gallery-lightbox__cap,
.gallery-lightbox.is-closing .gallery-lightbox__nav,
.gallery-lightbox.is-closing .gallery-lightbox__close { opacity: 0; transition: opacity 180ms ease; }
.gallery-lightbox__fig {
  position: relative; margin: 0; max-width: 100%; max-height: 100%;
  display: flex; flex-direction: column; align-items: center; gap: 14px;
}
.gallery-lightbox__img {
  object-fit: contain; box-shadow: 0 24px 80px rgba(0,0,0,0.6);
  outline: 1px solid color-mix(in oklab, var(--fg) 16%, transparent);
  will-change: transform; /* sized inline (fit rect) so the FLIP box is stable */
}
.gallery-lightbox__cap {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 16px;
  font-family: var(--mono); text-align: center; justify-content: center;
}
.gallery-lightbox__ix { font-size: 10px; letter-spacing: 0.24em; opacity: 0.5; }
.gallery-lightbox__title {
  font-family: var(--display); font-size: clamp(20px, 2vw, 30px);
  letter-spacing: 0.01em; text-transform: uppercase;
}
.gallery-lightbox__tag { font-size: 10.5px; letter-spacing: 0.18em; text-transform: uppercase; opacity: 0.55; }
.gallery-lightbox__link {
  font-size: 10.5px; letter-spacing: 0.2em; text-transform: uppercase; font-weight: 700;
  color: var(--fg); opacity: 0.85; text-decoration: none;
  border-bottom: 1px solid color-mix(in oklab, var(--fg) 55%, transparent);
  padding-bottom: 1px; transition: color 200ms, border-bottom-color 200ms, opacity 200ms;
}
.gallery-lightbox__link:hover { color: var(--accent); opacity: 1; border-bottom-color: var(--accent); }
.gallery-lightbox__close,
.gallery-lightbox__nav {
  appearance: none; background: transparent; border: none;
  color: var(--fg); cursor: pointer; opacity: 0.55;
  transition: opacity 180ms, transform 200ms cubic-bezier(.2,.7,.1,1);
}
.gallery-lightbox__close:hover, .gallery-lightbox__nav:hover { opacity: 1; }
.gallery-lightbox__close { position: absolute; top: -8px; right: -8px; font-size: 22px; line-height: 1; padding: 8px; }
/* Chunky chevrons (same jagged SVG as REEL/ART/CODEX) top- and bottom-centred:
   up = prev, down = next — the vertical sibling of the side edge-chevrons,
   clear of them and of the right-edge CODEX back handle. */
.gallery-lightbox__nav {
  position: fixed; left: 50%; transform: translateX(-50%);
  width: clamp(60px, 6.5vw, 100px); height: auto; padding: 4px;
}
.gallery-lightbox__nav svg { display: block; width: 100%; height: auto; }
/* the caption below the image shifts it up, so the top margin is the tighter
   one — keep the up chevron above the image top. */
.gallery-lightbox__nav--up   { top: clamp(22px, 3.6vh, 44px); }
.gallery-lightbox__nav--down { bottom: clamp(30px, 4.5vh, 58px); }
.gallery-lightbox__nav--up:hover   { transform: translateX(-50%) translateY(-3px); }
.gallery-lightbox__nav--down:hover { transform: translateX(-50%) translateY(3px); }
.gallery-lightbox__hint {
  width: 100%; flex-basis: 100%; text-align: center;
  font-size: 9.5px; letter-spacing: 0.26em; opacity: 0.4; margin-top: 2px;
}
@media (pointer: coarse) and (max-width: 1100px), (max-width: 900px) {
  .gallery-lightbox__nav { width: 52px; }
  .gallery-lightbox__nav--up { top: 44px; }
  .gallery-lightbox__nav--down { bottom: 26px; }
}

/* EXIT handles — left & right edge chevrons (same chunky SVG, pointing outward)
   that leave the preview and return to the specimen sheet, with an EXIT label so
   the side nav reads clearly in preview mode. Horizontal = exit, vertical = step. */
.gallery-lightbox__exit {
  position: fixed; top: 50%; transform: translateY(-50%);
  display: inline-flex; flex-direction: column; align-items: center; gap: 4px;
  padding: 18px 6px; background: transparent; border: none; cursor: pointer;
  color: color-mix(in oklab, var(--fg) 40%, transparent);
  transition: color 240ms cubic-bezier(.2,.7,.1,1),
              transform 320ms cubic-bezier(.2,.7,.1,1);
}
.gallery-lightbox__exit:hover { color: var(--fg); }
.gallery-lightbox__exit:focus-visible { outline: 1.5px dashed var(--accent); outline-offset: 6px; border-radius: 4px; }
.gallery-lightbox__exit svg {
  width: clamp(38px, 3.6vw, 54px); height: clamp(90px, 9vw, 126px); display: block;
  transition: transform 280ms cubic-bezier(.2,.7,.1,1);
}
.gallery-lightbox__exit--left  { left: 22px; }
.gallery-lightbox__exit--right { right: 22px; }
.gallery-lightbox__exit--left:hover  { transform: translateY(-50%) translateX(-6px); }
.gallery-lightbox__exit--left:hover svg  { transform: translateX(-3px); }
.gallery-lightbox__exit--right:hover { transform: translateY(-50%) translateX(6px); }
.gallery-lightbox__exit--right:hover svg { transform: translateX(3px); }
.gallery-lightbox__exit__lbl {
  font-family: var(--display); font-weight: var(--display-weight, 700);
  font-size: 24px; letter-spacing: 0.08em; text-transform: uppercase;
  writing-mode: vertical-rl; line-height: 1; white-space: nowrap;
  text-shadow: 0 0 8px color-mix(in oklab, var(--fg) 30%, transparent);
}
.gallery-lightbox__exit--left .gallery-lightbox__exit__lbl { transform: rotate(180deg); }
@media (pointer: coarse) and (max-width: 1100px), (max-width: 900px) {
  .gallery-lightbox__exit { padding: 12px 3px; }
  .gallery-lightbox__exit svg { width: 34px; height: 82px; }
  .gallery-lightbox__exit__lbl { font-size: 18px; }
}

@media (prefers-reduced-motion: reduce) {
  .stage--gallery-open, .gallery-pane { transition: none; }
  .gjust__img, .gjust__cap { transition: none; }
  .gjust__cell:hover .gjust__img { transform: none; }
  .gallery-lightbox::before { animation: none; }
}

/* ─── Graveyard (end-of-page payoff) ──────────────────────────────────────
   Layered photographic scene below the Signature, built from M's plates
   (public/mc/graveyard/). Stacking, back → front:
     moon (1) → tree (2) → hill grass (3) → tombstones (4) → fg grass (5).
   The container's aspect-ratio preserves the composition at any width;
   every layer is %-positioned against it. The three tombstones are the
   social links — hover/focus lifts the stone while the foreground grass
   stays put over its base, so it reads as rising out of the earth. */
.graveyard {
  position: relative;
  width: 100%;
  aspect-ratio: 1376 / 640;
  margin-top: 90px;
  overflow: hidden;
}
.graveyard img { user-select: none; -webkit-user-drag: none; }

.graveyard__moon {
  position: absolute;
  left: 7%;
  top: 6%;
  width: 13.5%;
  z-index: 1;
  /* Dark limb melt — the shadowed lower-left side ends in a hard black
     circle edge; this mask fades it into the void instead (lit upper-right
     side stays fully opaque). */
  -webkit-mask-image: radial-gradient(circle at 68% 30%, #000 42%, transparent 74%);
  mask-image: radial-gradient(circle at 68% 30%, #000 42%, transparent 74%);
}
.graveyard__tree {
  position: absolute;
  left: 47%;
  bottom: 30%;
  width: 31%;
  z-index: 2;
}
.graveyard__hill {
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  z-index: 3;
}
.graveyard__grass {
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  z-index: 5;
  pointer-events: none;
}

/* Tombstone links. Bases sit low enough that the fg grass overlaps them;
   bottoms vary so the row follows the hill's right-leaning slope. */
.graveyard__stone {
  position: absolute;
  z-index: 4;
  display: block;
  transition: transform 260ms cubic-bezier(.2, .7, .1, 1);
  will-change: transform;
}
.graveyard__stone img { display: block; width: 100%; height: auto; }
.graveyard__stone--1 { left: 10%; bottom: 17%; width: 9.8%; }
.graveyard__stone--2 { left: 36%; bottom: 14%; width: 9.0%; }
.graveyard__stone--3 { left: 81%; bottom: 11%; width: 9.2%; }
.graveyard__stone:hover,
.graveyard__stone:focus-visible { transform: translateY(-6%); }
.graveyard__stone:focus-visible { outline: 2px solid var(--fg); outline-offset: 6px; }

/* Epitaph label — floats in above the stone on hover/focus. */
.graveyard__epitaph {
  position: absolute;
  left: 50%;
  bottom: calc(100% + 10px);
  transform: translate(-50%, 6px);
  color: var(--fg-2);
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity 200ms ease, transform 260ms cubic-bezier(.2, .7, .1, 1);
}
.graveyard__stone:hover .graveyard__epitaph,
.graveyard__stone:focus-visible .graveyard__epitaph {
  opacity: 1;
  transform: translate(-50%, 0);
}

/* Etched glyph — faint light mark floating in each stone's dark arch so
   the stones read as the social links at a glance. Brightens on hover. */
.graveyard__glyph {
  position: absolute;
  left: 50%;
  top: 42%;
  width: 26%;
  aspect-ratio: 1 / 1;
  transform: translate(-50%, -50%);
  color: var(--fg);
  opacity: 0.38;
  pointer-events: none;
  transition: opacity 220ms ease;
}
.graveyard__stone:hover .graveyard__glyph,
.graveyard__stone:focus-visible .graveyard__glyph { opacity: 0.85; }

/* ─── Idle rocking ─────────────────────────────────────────────────────────
   Fired by JS (.is-rocking, ~every 2s on a random target): the stone/scroll
   rocks gently in the earth around its base. Hover cancels it immediately
   so the lift transform wins without waiting for the cycle to end. */
@keyframes grave-rock {
  0%, 100% { transform: rotate(0); }
  18% { transform: rotate(-1.7deg); }
  45% { transform: rotate(1.3deg); }
  70% { transform: rotate(-0.7deg); }
  88% { transform: rotate(0.3deg); }
}
.graveyard__stone.is-rocking,
.cv-scroll__link.is-rocking {
  animation: grave-rock 950ms ease-in-out;
  transform-origin: 50% 100%;
}
.graveyard__stone.is-rocking:hover,
.graveyard__stone.is-rocking:focus-visible,
.cv-scroll__link.is-rocking:hover,
.cv-scroll__link.is-rocking:focus-visible { animation: none; }

@media (prefers-reduced-motion: reduce) {
  .graveyard__stone { transition: none; }
  .graveyard__epitaph { transition: opacity 200ms ease; transform: translate(-50%, 0); }
  .graveyard__stone.is-rocking,
  .cv-scroll__link.is-rocking { animation: none; }
}

/* ─── Vault link (fixed, upper right) ─────────────────────────────────────
   Torn-hole padlock plate linking to the password-protected private
   portfolio. Tucked under the header's clock/LIVE segment. Hover lifts it
   and reveals a mono label beneath, same vocabulary as the graveyard
   epitaphs. */
.vault-link {
  position: fixed;
  top: 54px;
  right: 26px;
  /* Above the gallery pane (58) + header (60), below its scroll chevrons (78). */
  z-index: 70;
  display: block;
  width: 96px;
  transition: transform 240ms cubic-bezier(.2, .7, .1, 1), opacity 300ms ease;
}
.vault-link img {
  display: block;
  width: 100%;
  height: auto;
  user-select: none;
  -webkit-user-drag: none;
}
.vault-link:hover,
.vault-link:focus-visible { transform: translateY(-3px); }
.vault-link:focus-visible { outline: 2px solid var(--fg); outline-offset: 4px; }
.vault-link.is-hidden { opacity: 0; pointer-events: none; }

.vault-link__lbl {
  position: absolute;
  left: 50%;
  top: calc(100% + 6px);
  transform: translate(-50%, -4px);
  color: var(--fg-2);
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity 200ms ease, transform 240ms cubic-bezier(.2, .7, .1, 1);
}
.vault-link:hover .vault-link__lbl,
.vault-link:focus-visible .vault-link__lbl {
  opacity: 1;
  transform: translate(-50%, 0);
}

@media (prefers-reduced-motion: reduce) {
  .vault-link { transition: opacity 300ms ease; }
  .vault-link__lbl { transition: opacity 200ms ease; transform: translate(-50%, 0); }
}
@media (pointer: coarse) and (max-width: 1100px), (max-width: 900px) {
  .vault-link { width: 72px; top: 58px; right: 16px; }
}

/* ─── CV scroll (end of the history descent) ──────────────────────────────
   Parchment-scroll plate linking to the CV PDF. The Portal renders a
   decorative copy that slides out from UNDER the eclipse disc at the end
   of the descent; this in-flow link takes over at the unpin frame (JS
   matches position/scale) and eases into rest. An ink "CV" glyph sits on
   the parchment itself. The seam down to the graveyard stays clean black. */
.cv-scroll {
  display: flex;
  justify-content: center;
  /* Drop the parchment INTO the graveyard scene: the GRAVEYARD rises under
     the scroll via collapsed negative margin (-171 vs the yard's +90 →
     net -81px = bottom half of the 161.8px plate past the scene's top).
     ⚠ NOT a translate on the section — the descent-handoff JS reads this
     section's rect, and a transform here desynced the portal-copy unpin
     from the in-flow takeover (scroll vanished, then popped back). */
  margin: 28px 0 -171px;
  position: relative;
  z-index: 2;
}
.cv-scroll__link {
  position: relative;
  display: block;
  width: min(150px, 34vw);
  transition: transform 240ms cubic-bezier(.2, .7, .1, 1);
}
.cv-scroll__link img {
  display: block;
  width: 100%;
  height: auto;
  user-select: none;
  -webkit-user-drag: none;
  filter: drop-shadow(0 18px 28px color-mix(in oklab, var(--bg, #0a0a0a) 80%, transparent));
}
.cv-scroll__link:hover,
.cv-scroll__link:focus-visible { transform: translateY(-5px); }
.cv-scroll__link:focus-visible { outline: 2px solid var(--fg); outline-offset: 6px; }

/* Ink "CV" glyph — written ON the parchment (Metal Mania display face,
   dark ink, multiply so it sits in the paper texture — reads as a scribed
   sigil rather than typeset text). Slight rotation matches the plate's
   hang. Shared by the in-flow link and the portal's decorative copy. */
.cv-scroll__glyph {
  position: absolute;
  left: 50%;
  top: 42%;
  transform: translate(-50%, -50%) rotate(-3deg);
  font-family: "Metal Mania", ui-serif, serif;
  font-size: 26px;
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  text-indent: 0.12em; /* recenter — tracking pads the right edge only */
  color: #161616;
  mix-blend-mode: multiply;
  opacity: 0.68;
  pointer-events: none;
  transition: opacity 200ms ease;
}
.cv-scroll__link:hover .cv-scroll__glyph,
.cv-scroll__link:focus-visible .cv-scroll__glyph { opacity: 0.92; }

/* The portal's decorative copy — same width as the in-flow link so the
   handoff at scale 1 is pixel-matched. Rendered with the entries ABOVE
   the eclipse; the descent loop drives it like a final pseudo-entry
   (exponential scale + blur + fade from the vanishing point). */
.portal__scroll {
  position: absolute;
  display: block;
  left: 50%;
  top: 50%;
  width: min(150px, 34vw);
  opacity: 0;
  pointer-events: none; /* JS opts it in once it has surfaced */
  cursor: pointer;
  will-change: transform, filter;
}
.portal__scroll img {
  display: block;
  width: 100%;
  height: auto;
  filter: drop-shadow(0 18px 28px color-mix(in oklab, var(--bg, #0a0a0a) 80%, transparent));
}

@media (prefers-reduced-motion: reduce) {
  .cv-scroll__link { transition: none; }
  .portal__scroll { display: none; }
}

/* Screen-reader-only utility (used by the CV scroll link text). */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Periodic rattle — the padlock jiggles like someone's testing it every few
   seconds, to pull the eye toward the vault. On the inner img so it composes
   with the link's hover lift; paused while hovered/focused. */
.vault-link img { transform-origin: 50% 30%; animation: vault-rattle 4.2s ease-in-out infinite; }
.vault-link:hover img,
.vault-link:focus-visible img { animation-play-state: paused; }
@keyframes vault-rattle {
  0%, 86%, 100% { transform: rotate(0); }
  88%  { transform: rotate(-4deg); }
  90%  { transform: rotate(3.5deg); }
  92%  { transform: rotate(-3deg); }
  94%  { transform: rotate(2deg); }
  96%  { transform: rotate(-1deg); }
  98%  { transform: rotate(0.5deg); }
}
@media (prefers-reduced-motion: reduce) {
  .vault-link img { animation: none; }
}

/* ─── CONNECT row (email / cv) ────────────────────────────────────────────
   One line so the tombstone icons keep their original baseline. The copied
   confirmation is absolutely positioned to avoid any reflow. */
.contact-row { position: relative; }
.contact-row__sep {
  font-family: var(--mono, ui-monospace, monospace);
  font-size: 12.5px;
  color: var(--fg-3);
  margin: 0 9px;
}
/* Electro balls in the row — inline (not absolute like .roles__spark):
   one leads "email", one divides email/cv where the "/" used to sit. */
.contact-row__spark {
  display: inline-block;
  width: 11px;
  height: 11px;
  vertical-align: -1.5px;
  margin-right: 7px;
  color: var(--fg);
  filter: drop-shadow(0 0 3px color-mix(in oklab, currentColor 65%, transparent));
}
.contact-row__spark--sep { margin: 0 9px; }
.contact-row__copied {
  position: absolute;
  left: 0;
  top: calc(100% + 5px);
  color: var(--fg-2);
  white-space: nowrap;
  pointer-events: none;
  animation: contact-copied 2400ms linear both;
}
@keyframes contact-copied {
  0%   { opacity: 0; }
  4%   { opacity: 1; }
  8%   { opacity: 0.2; }
  12%  { opacity: 1; }
  82%  { opacity: 1; }
  100% { opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .contact-row__copied { animation: none; opacity: 1; }
}

/* ─── FINAL MOBILE OVERRIDES — keep this block LAST in the sheet ──────────
   Phones (incl. wide ones reporting ~750-900px CSS) + narrow windows.
   These rules lost cascade ties against the data-density / data-layout
   variant rules mid-sheet (e.g. html[data-density="dense"] .margin
   { gap: 22px } stomped the strip's row-gap) — file order is the
   tiebreaker, so they live at the end. Add future mobile overrides HERE. */
@media (pointer: coarse) and (max-width: 1100px), (max-width: 900px) {
  html[data-layout="broadside"] .mark-wrap { font-size: 36vw; }
  html[data-layout="broadside"] .hero { row-gap: 26px; padding-bottom: 0; }
  html[data-layout="broadside"] .margin { row-gap: 64px; }
  html[data-density="dense"] .margin { row-gap: 64px; }
  /* swipe paging: vertical pan + pinch stay native; horizontal pointer
     streams reach our swipe handler instead of being browser-cancelled */
  html, body { touch-action: pan-y pinch-zoom; }
}
