// web-scenes.jsx — Web Design Process morphing animation
// Single hero layout morphing through 4 fidelity stages: wireframe → lo-fi → mid-fi → ship

const W_PALETTE = {
  cream: '#f5f0e8',
  creamDeep: '#ede6d8',
  paper: '#faf6ee',
  moss600: '#3f6b54',
  moss500: '#4d7e65',
  moss400: '#6fa37a',
  moss300: '#93b89a',
  moss200: '#b8cdbb',
  moss100: '#d7e1d4',
  amber: '#cd8a4b',
  amberSoft: '#e2a568',
  ink: '#0f1713',
  ink70: 'rgba(15,23,19,0.72)',
  ink40: 'rgba(15,23,19,0.40)',
  ink15: 'rgba(15,23,19,0.15)',
  grey900: '#2a2a2a',
  grey700: '#555555',
  grey500: '#9a9a9a',
  grey300: '#c8c8c8',
  grey200: '#d9d9d9',
  grey100: '#e8e8e8',
  grey50:  '#f2f2f2',
  wfBg:    '#ededed',   // wireframe stage bg (neutral grey-paper)
};

const W_FONTS = {
  display: '"Space Grotesk", system-ui, sans-serif',
  serif: '"Instrument Serif", Georgia, serif',
  mono: '"JetBrains Mono", ui-monospace, monospace',
};

const wSmooth = (t) => t < 0 ? 0 : t > 1 ? 1 : t * t * (3 - 2 * t);
const wMix = (a, b, t) => a + (b - a) * t;

// Stage progression: returns 0..1 blending factor across all 4 stages.
// Stage boundaries: wireframe (0–3), lo-fi (3–6), mid-fi (6–10), ship (10–14), reverse (14–15).
// Returns object with { stage1, stage2, stage3, stage4 } weights summing to 1.
// During reverse (14–15s), returns to stage1.
function stageWeights(t) {
  // Longer, smoother transitions spanning the full stage window so motion never stalls.
  const tr = 1.4;
  let s1 = 0, s2 = 0, s3 = 0, s4 = 0;

  if (t < 3 - tr) { s1 = 1; }
  else if (t < 3) { const k = wSmooth((t - (3 - tr)) / tr); s1 = 1 - k; s2 = k; }
  else if (t < 6 - tr) { s2 = 1; }
  else if (t < 6) { const k = wSmooth((t - (6 - tr)) / tr); s2 = 1 - k; s3 = k; }
  else if (t < 10 - tr) { s3 = 1; }
  else if (t < 10) { const k = wSmooth((t - (10 - tr)) / tr); s3 = 1 - k; s4 = k; }
  else if (t < 14) { s4 = 1; }
  else {
    const k = wSmooth((t - 14) / 1.0);
    s4 = 1 - k; s1 = k;
  }
  return { s1, s2, s3, s4 };
}

// Linearly blend a scalar across 4 stage values based on weights.
const blendScalar = (w, v1, v2, v3, v4) =>
  w.s1 * v1 + w.s2 * v2 + w.s3 * v3 + w.s4 * v4;

// Blend a color hex/rgba across stages (simple: pick the dominant stage color,
// but fade via css opacity when possible). For smooth color morph, convert hex→rgb.
function hexToRgb(h) {
  const m = h.replace('#', '');
  if (m.length === 3) return [parseInt(m[0]+m[0],16), parseInt(m[1]+m[1],16), parseInt(m[2]+m[2],16)];
  return [parseInt(m.slice(0,2),16), parseInt(m.slice(2,4),16), parseInt(m.slice(4,6),16)];
}
function blendColor(w, c1, c2, c3, c4) {
  const a = hexToRgb(c1), b = hexToRgb(c2), c = hexToRgb(c3), d = hexToRgb(c4);
  const r = Math.round(w.s1*a[0] + w.s2*b[0] + w.s3*c[0] + w.s4*d[0]);
  const g = Math.round(w.s1*a[1] + w.s2*b[1] + w.s3*c[1] + w.s4*d[1]);
  const bl = Math.round(w.s1*a[2] + w.s2*b[2] + w.s3*c[2] + w.s4*d[2]);
  return `rgb(${r}, ${g}, ${bl})`;
}

// ─── Film grain ─────────────────────────────────────────────────────────
function WFilmGrain({ opacity = 0.09 }) {
  return (
    <div style={{
      position: 'absolute', inset: 0, pointerEvents: 'none',
      opacity, mixBlendMode: 'multiply',
      backgroundImage: `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='1.6' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0.15  0 0 0 0 0.13  0 0 0 0 0.08  0 0 0 1 0'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>")`,
      backgroundSize: '200px 200px',
    }} />
  );
}

// ─── Corner chrome (logo + pulse + morphing orb) ────────────────────────
function WCornerChrome() {
  const time = useTime();
  const pulse = 0.5 + 0.5 * Math.sin(time * Math.PI * 1.2);

  // Morphing bottom-right orb
  const w = 3;
  const r1 = 50 + Math.sin(time * w * 0.7) * 14;
  const r2 = 50 + Math.cos(time * w * 0.6 + 1) * 16;
  const r3 = 50 + Math.sin(time * w * 0.9 + 2) * 12;
  const r4 = 50 + Math.cos(time * w * 0.5 + 3) * 18;

  return (
    <>
      {/* Top-left logo */}
      <img
        src="assets/digital-heroes-logo.png"
        alt=""
        style={{
          position: 'absolute', left: 56, top: 44,
          height: 54, width: 'auto',
          opacity: 0.42,
          pointerEvents: 'none',
        }}
      />

      {/* Top-right pulse + section caption */}
      <div style={{
        position: 'absolute', right: 56, top: 58,
        display: 'flex', alignItems: 'center', gap: 12,
        fontFamily: W_FONTS.mono,
        fontSize: 12,
        letterSpacing: '0.12em',
        color: W_PALETTE.ink70,
        textTransform: 'uppercase',
        opacity: 0.95,
      }}>
        <div style={{
          width: 8, height: 8, borderRadius: 4,
          background: W_PALETTE.moss500,
          opacity: 0.4 + 0.6 * pulse,
          boxShadow: `0 0 ${8 + 12 * pulse}px ${W_PALETTE.moss400}`,
        }}/>
        <span style={{ opacity: 0.45 }}>§ 03 · design process</span>
      </div>

      {/* Bottom-right morphing orb */}
      <div style={{
        position: 'absolute', right: 72, bottom: 72,
        width: 28, height: 28,
        background: `radial-gradient(circle at 38% 32%, ${W_PALETTE.moss400}, ${W_PALETTE.moss600})`,
        borderRadius: `${r1}% ${100-r1}% ${r2}% ${100-r2}% / ${r3}% ${r4}% ${100-r4}% ${100-r3}%`,
        boxShadow: 'inset -2px -3px 6px rgba(15,23,19,0.3), 0 4px 10px rgba(63,107,84,0.25)',
        opacity: 0.85,
      }}/>

      {/* Bottom-left folio */}
      <div style={{
        position: 'absolute', left: 56, bottom: 52,
        fontFamily: W_FONTS.mono,
        fontSize: 12,
        letterSpacing: '0.12em',
        color: W_PALETTE.ink40,
      }}>
        fig.&nbsp;&nbsp;03·ii — fidelity study
      </div>

      {/* Top-right (above pulse) edition line */}
      <div style={{
        position: 'absolute', right: 56, top: 86,
        fontFamily: W_FONTS.mono,
        fontSize: 11,
        letterSpacing: '0.16em',
        color: W_PALETTE.ink40,
        textTransform: 'uppercase',
      }}>
        vol. 01 — craft &amp; ship
      </div>
    </>
  );
}

// ─── Stage label (bottom-left corner, changes per stage) ────────────────
function StageLabel() {
  const time = useTime();
  // Determine which label based on time
  // Label switches at each stage's MIDPOINT so corner chrome matches the dominant weight.
  let label = '§ 01 · wireframe';
  if (time >= 3.0 && time < 6.0) label = '§ 02 · low-fidelity';
  else if (time >= 6.0 && time < 10.0) label = '§ 03 · mid-fidelity';
  else if (time >= 10.0 && time < 14.0) label = '§ 04 · shipped';
  else if (time >= 14.0) label = '§ 01 · wireframe'; // reverse

  // Subtle fade-in on each change
  const boundaries = [0, 3.0, 6.0, 10.0, 14.0];
  let lastBoundary = 0;
  for (const b of boundaries) if (time >= b) lastBoundary = b;
  const sinceChange = time - lastBoundary;
  const opacity = wSmooth(Math.min(1, sinceChange / 0.45));

  return (
    <div style={{
      position: 'absolute', left: 56, bottom: 80,
      fontFamily: W_FONTS.mono,
      fontSize: 13,
      letterSpacing: '0.16em',
      color: W_PALETTE.moss600,
      textTransform: 'uppercase',
      opacity,
      fontWeight: 500,
    }}>
      {label}
    </div>
  );
}

// ─── Background stage (cream washes in at stage 3) ──────────────────────
function HeroBackground() {
  const time = useTime();
  const w = stageWeights(time);

  // Stage 1,2: neutral grey paper. Stage 3,4: cream.
  const bg = blendColor(w, W_PALETTE.wfBg, W_PALETTE.wfBg, W_PALETTE.cream, W_PALETTE.cream);

  return (
    <div style={{
      position: 'absolute',
      left: 180, top: 170,
      width: 1560, height: 780,
      background: bg,
      borderRadius: 2,
      boxShadow: w.s3 * 0.6 + w.s4 > 0.1
        ? `0 30px 80px rgba(40,30,15,${(w.s3 + w.s4) * 0.18}), 0 0 0 1px ${W_PALETTE.ink15}`
        : `0 0 0 1px ${W_PALETTE.grey300}`,
      transition: 'none',
    }}>
      {/* Grid guides visible during wireframe/lo-fi */}
      <div style={{
        position: 'absolute', inset: 0,
        opacity: (w.s1 + w.s2 * 0.7),
        backgroundImage: `
          linear-gradient(to right, rgba(0,0,0,0.04) 1px, transparent 1px),
          linear-gradient(to bottom, rgba(0,0,0,0.04) 1px, transparent 1px)
        `,
        backgroundSize: '40px 40px',
        pointerEvents: 'none',
      }}/>

      {/* Stricter grid at lo-fi */}
      <div style={{
        position: 'absolute', inset: 0,
        opacity: w.s2 * 0.5,
        backgroundImage: `
          linear-gradient(to right, rgba(0,0,0,0.10) 1px, transparent 1px)
        `,
        backgroundSize: '260px 100%',
        pointerEvents: 'none',
      }}/>

      {/* Film grain appears at stage 4 */}
      {w.s4 > 0.02 && <WFilmGrain opacity={0.11 * w.s4} />}
    </div>
  );
}

// ─── Hero layout elements ───────────────────────────────────────────────
// Layout coordinates (within the hero background box):
// Hero box: left=180, top=170, w=1560, h=780
// Inner padding ~72px. Content arranged as: top nav → h1 → subcopy → cta → image tile on right

// Nav row
function HeroNav() {
  const time = useTime();
  const w = stageWeights(time);

  // Nav links color/text blending
  // Stage 1: 3 grey bars; Stage 2: same but with accent bar; Stage 3-4: actual nav words
  const showRealNav = w.s3 + w.s4;
  const showBars = (w.s1 + w.s2) * (1 - (w.s3 + w.s4));

  return (
    <div style={{
      position: 'absolute',
      left: 180 + 72, top: 170 + 56,
      right: 180 + 72 + (1920 - 180 - 72 - 1740),
      width: 1560 - 144,
      height: 40,
      display: 'flex', alignItems: 'center', justifyContent: 'space-between',
    }}>
      {/* Left: brand mark */}
      <div style={{ position: 'relative', width: 140, height: 22 }}>
        {/* wireframe bar */}
        <div style={{
          position: 'absolute', left: 0, top: 4, width: wMix(90, 110, w.s2), height: 14,
          background: w.s1 > 0.5 ? W_PALETTE.grey500 : W_PALETTE.grey700,
          opacity: showBars,
          borderRadius: 2,
        }}/>
        {/* real brand word */}
        <div style={{
          position: 'absolute', left: 0, top: 0,
          fontFamily: W_FONTS.display,
          fontWeight: 700,
          fontSize: 22,
          letterSpacing: '-0.03em',
          color: W_PALETTE.ink,
          opacity: showRealNav,
        }}>
          studio<span style={{ fontFamily: W_FONTS.serif, fontStyle: 'italic', fontWeight: 400, color: W_PALETTE.moss600 }}>.seventy</span>
        </div>
      </div>

      {/* Right: nav items */}
      <div style={{ display: 'flex', alignItems: 'center', gap: 40, position: 'relative', height: 22 }}>
        {['work', 'studio', 'journal', 'contact'].map((item, i) => (
          <div key={item} style={{ position: 'relative', width: 70, height: 22 }}>
            <div style={{
              position: 'absolute', left: 0, top: 6, width: wMix(60, 55, w.s2), height: 10,
              background: W_PALETTE.grey500,
              opacity: showBars,
              borderRadius: 2,
            }}/>
            <div style={{
              position: 'absolute', left: 0, top: 2,
              fontFamily: W_FONTS.display,
              fontWeight: 500,
              fontSize: 14,
              letterSpacing: '0.04em',
              color: W_PALETTE.ink70,
              textTransform: 'uppercase',
              opacity: showRealNav,
            }}>
              {item}
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

// Hero H1 (morphs: big grey bar → 2 smaller bars with hierarchy → branded type → final copy)
function HeroHeadline() {
  const time = useTime();
  const w = stageWeights(time);

  // Aggressive dampening: bars must fully vacate as real type comes in.
  const barsMask = 1 - (w.s3 + w.s4);
  const showBars = (w.s1 + w.s2) * barsMask;
  const showType = w.s3 + w.s4;

  // Stage 3 copy vs stage 4 copy (same copy, but stage 4 has amber + final polish)
  // Per brief: final reads "Editorial sites, engineered to convert."
  // Stage 3 = same with moss italic "engineered"
  // We'll just show the same final copy in stages 3 and 4 to keep one layout refining.

  return (
    <div style={{
      position: 'absolute',
      left: 180 + 72, top: 170 + 190,
      width: 920,
      height: 280,
    }}>
      {/* Wireframe: two big grey bars */}
      <div style={{ position: 'absolute', inset: 0, opacity: showBars }}>
        <div style={{
          position: 'absolute', left: 0, top: 0,
          width: wMix(820, 760, w.s2), height: wMix(54, 62, w.s2),
          background: W_PALETTE.grey700,
          borderRadius: 3,
        }}/>
        <div style={{
          position: 'absolute', left: 0, top: wMix(72, 84, w.s2),
          width: wMix(620, 540, w.s2), height: wMix(54, 62, w.s2),
          background: W_PALETTE.grey700,
          borderRadius: 3,
        }}/>
        {/* placeholder text visible through stages 1 AND 2 (lo-fi = bigger hierarchy) */}
        {(w.s1 + w.s2) > 0.02 && (
          <>
            <div style={{
              position: 'absolute', left: 16, top: wMix(15, 18, w.s2),
              fontFamily: W_FONTS.display, fontWeight: 700,
              fontSize: wMix(26, 34, w.s2), letterSpacing: '-0.02em',
              color: 'rgba(255,255,255,0.78)',
              opacity: w.s1 + w.s2,
              lineHeight: 1,
              whiteSpace: 'nowrap',
            }}>
              {w.s2 > w.s1 ? 'Headline' : 'Lorem ipsum dolor sit amet'}
            </div>
            <div style={{
              position: 'absolute', left: 16, top: wMix(88, 102, w.s2),
              fontFamily: W_FONTS.display, fontWeight: 500,
              fontSize: wMix(24, 26, w.s2), letterSpacing: '-0.01em',
              color: 'rgba(255,255,255,0.6)',
              opacity: w.s1 + w.s2,
              lineHeight: 1,
              whiteSpace: 'nowrap',
            }}>
              {w.s2 > w.s1 ? 'Subhead' : 'consectetur adipiscing elit'}
            </div>
          </>
        )}
      </div>

      {/* Real typography (stage 3/4) */}
      <div style={{
        position: 'absolute', left: 0, top: 0,
        fontFamily: W_FONTS.display,
        fontWeight: 700,
        fontSize: 82,
        lineHeight: 1.02,
        letterSpacing: '-0.04em',
        color: W_PALETTE.ink,
        opacity: showType,
        width: '100%',
      }}>
        Editorial sites, <br/>
        <span style={{
          fontFamily: W_FONTS.serif,
          fontStyle: 'italic',
          fontWeight: 400,
          color: W_PALETTE.moss600,
          letterSpacing: '-0.02em',
        }}>engineered</span> to convert.
      </div>
    </div>
  );
}

// Moss accent bar that appears at stage 2
function AccentBar() {
  const time = useTime();
  const w = stageWeights(time);
  // Appears stage 2, stays thru 3 and 4, fades on reverse
  const opacity = w.s2 + w.s3 * 0.9 + w.s4 * 0.7;
  const width = wMix(64, 88, w.s2 + w.s3 + w.s4);

  return (
    <div style={{
      position: 'absolute',
      left: 180 + 72, top: 170 + 156,
      width, height: 4,
      background: W_PALETTE.moss500,
      opacity,
      borderRadius: 2,
    }}/>
  );
}

// Eyebrow (section label above H1)
function HeroEyebrow() {
  const time = useTime();
  const w = stageWeights(time);
  const showBar = (w.s1 + w.s2 * 0.5) * (1 - (w.s3 + w.s4));
  const showText = w.s2 * 0.5 + w.s3 + w.s4;

  return (
    <div style={{
      position: 'absolute',
      left: 180 + 72, top: 170 + 152,
      width: 300, height: 16,
    }}>
      <div style={{
        position: 'absolute', left: 0, top: 2, width: 180, height: 10,
        background: W_PALETTE.grey500,
        opacity: showBar,
        borderRadius: 2,
      }}/>
      <div style={{
        position: 'absolute', left: 100, top: 0,
        fontFamily: W_FONTS.mono,
        fontSize: 12,
        letterSpacing: '0.28em',
        color: W_PALETTE.moss500,
        textTransform: 'uppercase',
        opacity: showText,
      }}>
        vol. 01 — engagement
      </div>
    </div>
  );
}

// Subcopy paragraph
function HeroSubcopy() {
  const time = useTime();
  const w = stageWeights(time);
  const showBars = (w.s1 + w.s2 * 0.7) * (1 - (w.s3 + w.s4));
  const showText = w.s2 * 0.3 + w.s3 + w.s4;

  return (
    <div style={{
      position: 'absolute',
      left: 180 + 72, top: 170 + 488,
      width: 760,
    }}>
      <div style={{ position: 'relative', height: 80, opacity: showBars }}>
        <div style={{ position: 'absolute', left: 0, top: 0, width: 720, height: 12, background: W_PALETTE.grey300, borderRadius: 2 }}/>
        <div style={{ position: 'absolute', left: 0, top: 22, width: 680, height: 12, background: W_PALETTE.grey300, borderRadius: 2 }}/>
        <div style={{ position: 'absolute', left: 0, top: 44, width: 520, height: 12, background: W_PALETTE.grey300, borderRadius: 2 }}/>
      </div>

      <div style={{
        position: 'absolute', left: 0, top: 0,
        fontFamily: W_FONTS.display,
        fontWeight: 400,
        fontSize: 22,
        lineHeight: 1.5,
        letterSpacing: '-0.01em',
        color: W_PALETTE.ink70,
        opacity: showText,
        width: 700,
      }}>
        We design brand-first websites that read like a magazine and ship like an app — built for teams who care how their work feels on the page.
      </div>
    </div>
  );
}

// CTA button — rect → rect → pill, amber glow at stage 4
function HeroCTA() {
  const time = useTime();
  const w = stageWeights(time);

  // Border radius: stage 1 = 2, stage 2 = 4, stage 3 = 999 (pill), stage 4 = 999
  const radius = blendScalar(w, 2, 4, 999, 999);
  // Background: grey → darker grey → moss → moss
  const bg = blendColor(w, W_PALETTE.grey500, W_PALETTE.grey700, W_PALETTE.moss600, W_PALETTE.moss600);
  const fg = blendColor(w, '#ffffff', '#ffffff', '#faf6ee', '#faf6ee');
  const width = blendScalar(w, 180, 200, 220, 230);
  const height = blendScalar(w, 48, 52, 60, 62);

  // Text visibility
  const showBar = (w.s1 + w.s2 * 0.6) * (1 - (w.s3 + w.s4));
  const showText = w.s2 * 0.4 + w.s3 + w.s4;

  // Amber orb glow at stage 4
  const amberT = w.s4;

  return (
    <div style={{
      position: 'absolute',
      left: 180 + 72, top: 170 + 620,
    }}>
      {/* Amber glow orb behind button (stage 4 only) */}
      {amberT > 0.02 && (
        <div style={{
          position: 'absolute',
          left: width - 14, top: height / 2,
          transform: 'translate(-50%, -50%)',
          width: 90, height: 90,
          background: `radial-gradient(circle, ${W_PALETTE.amberSoft} 0%, ${W_PALETTE.amber}55 35%, transparent 70%)`,
          opacity: amberT * 0.9,
          filter: 'blur(8px)',
          pointerEvents: 'none',
        }}/>
      )}

      <div style={{
        width, height,
        background: bg,
        borderRadius: radius,
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        position: 'relative',
        boxShadow: w.s3 + w.s4 > 0.3
          ? `0 6px 20px rgba(63,107,84,${0.2 * (w.s3 + w.s4)})`
          : 'none',
      }}>
        <div style={{
          width: 110, height: 10,
          background: 'rgba(255,255,255,0.65)',
          borderRadius: 2,
          opacity: showBar,
          position: 'absolute',
        }}/>
        <div style={{
          fontFamily: W_FONTS.display,
          fontWeight: 600,
          fontSize: 17,
          letterSpacing: '0.02em',
          color: fg,
          opacity: showText,
          position: 'relative',
        }}>
          Start a project →
        </div>

        {/* tiny amber dot on button edge (stage 4) */}
        {amberT > 0.02 && (
          <div style={{
            position: 'absolute',
            right: -6, top: '50%',
            transform: 'translateY(-50%)',
            width: 14, height: 14, borderRadius: 7,
            background: W_PALETTE.amber,
            boxShadow: `0 0 14px ${W_PALETTE.amber}, 0 0 30px ${W_PALETTE.amberSoft}`,
            opacity: amberT,
          }}/>
        )}
      </div>
    </div>
  );
}

// Image tile on the right (placeholder-X → tinted block → moss soft block → photo)
function HeroImage() {
  const time = useTime();
  const w = stageWeights(time);

  const x = 180 + 72 + 920 + 60; // right of text column
  const y = 170 + 156;
  const width = 1740 - (x - 180) - 72;
  const height = 520;

  // Background color morph
  const bg = blendColor(w,
    W_PALETTE.grey300,   // wireframe
    W_PALETTE.grey300,   // lo-fi
    W_PALETTE.moss200,   // mid-fi: soft moss block
    W_PALETTE.moss200    // ship (will be covered by photo layer)
  );

  // Photo layer opacity (stage 4)
  const photoOpacity = w.s4;

  return (
    <div style={{
      position: 'absolute',
      left: x, top: y,
      width, height,
      background: bg,
      borderRadius: w.s3 + w.s4 > 0.3 ? 4 : 2,
      overflow: 'hidden',
      boxShadow: w.s3 + w.s4 > 0.3
        ? `0 18px 40px rgba(63,107,84,${0.14 * (w.s3 + w.s4)})`
        : `0 0 0 1px ${W_PALETTE.grey500}`,
    }}>
      {/* Wireframe placeholder-X (stages 1+2) */}
      {(w.s1 + w.s2) > 0.05 && (
        <svg
          width="100%" height="100%" viewBox={`0 0 ${width} ${height}`}
          style={{ position: 'absolute', inset: 0, opacity: (w.s1 + w.s2 * 0.7) * (1 - (w.s3 + w.s4)) }}
        >
          <line x1="0" y1="0" x2={width} y2={height} stroke={W_PALETTE.grey500} strokeWidth="1.5" />
          <line x1={width} y1="0" x2="0" y2={height} stroke={W_PALETTE.grey500} strokeWidth="1.5" />
          <rect x="0.75" y="0.75" width={width - 1.5} height={height - 1.5} fill="none" stroke={W_PALETTE.grey500} strokeWidth="1.5" />
          {/* "image" label */}
          <text x={width/2} y={height/2 + 6}
            textAnchor="middle"
            fontFamily='"JetBrains Mono", monospace'
            fontSize="14"
            fill={W_PALETTE.grey700}
            letterSpacing="3"
            style={{ textTransform: 'uppercase' }}
          >
            IMAGE · 4:3
          </text>
        </svg>
      )}

      {/* Lo-fi dimension annotations (stage 2) */}
      {w.s2 > 0.3 && (
        <div style={{
          position: 'absolute', inset: 0,
          opacity: w.s2 * (1 - (w.s3 + w.s4)),
          pointerEvents: 'none',
        }}>
          <div style={{
            position: 'absolute', left: 12, top: 12,
            fontFamily: W_FONTS.mono,
            fontSize: 11, letterSpacing: '0.1em',
            color: W_PALETTE.grey700,
            textTransform: 'uppercase',
          }}>
            ratio 4:3 · editorial
          </div>
        </div>
      )}

      {/* Mid-fi: moss soft tinted block with subtle texture */}
      {w.s3 > 0.02 && (
        <div style={{
          position: 'absolute', inset: 0,
          opacity: w.s3,
          background: `radial-gradient(ellipse at 35% 30%, ${W_PALETTE.moss300} 0%, ${W_PALETTE.moss200} 60%, ${W_PALETTE.moss100} 100%)`,
        }}>
          {/* serif italic overlay caption */}
          <div style={{
            position: 'absolute', left: 40, bottom: 40,
            fontFamily: W_FONTS.serif, fontStyle: 'italic',
            fontSize: 46,
            color: W_PALETTE.moss600,
            letterSpacing: '-0.02em',
            opacity: 0.85,
          }}>
            editorial <br/>composition
          </div>
        </div>
      )}

      {/* Stage 4: photo replaces moss block */}
      {photoOpacity > 0.02 && (
        <div style={{
          position: 'absolute', inset: 0,
          opacity: photoOpacity,
          background: `
            radial-gradient(ellipse at 28% 22%, rgba(245,225,180,0.85) 0%, transparent 45%),
            radial-gradient(ellipse at 75% 65%, rgba(60,90,65,0.75) 0%, transparent 55%),
            radial-gradient(ellipse at 45% 85%, rgba(170,125,70,0.6) 0%, transparent 50%),
            linear-gradient(145deg, #d4b687 0%, #8a7a5a 35%, #4a5540 70%, #2a3028 100%)
          `,
        }}>
          {/* Overlay: editorial texture hint */}
          <div style={{
            position: 'absolute', inset: 0,
            backgroundImage: `
              linear-gradient(105deg, transparent 40%, rgba(255,240,210,0.14) 50%, transparent 60%),
              radial-gradient(circle at 20% 30%, rgba(255,255,255,0.10), transparent 40%)
            `,
          }}/>
          {/* Simulated subject: soft moss organic drop shape */}
          <div style={{
            position: 'absolute', left: '58%', top: '35%',
            width: 160, height: 220,
            background: `radial-gradient(ellipse at 40% 30%, ${W_PALETTE.moss300}, ${W_PALETTE.moss500} 80%)`,
            borderRadius: '45% 55% 40% 60% / 30% 35% 65% 70%',
            transform: 'rotate(-8deg)',
            filter: 'blur(0.5px)',
            opacity: 0.85,
            boxShadow: '0 30px 60px rgba(0,0,0,0.35)',
          }}/>
          {/* Grain on photo */}
          <div style={{
            position: 'absolute', inset: 0,
            opacity: 0.12, mixBlendMode: 'overlay',
            backgroundImage: `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='1.2' numOctaves='2' stitchTiles='stitch'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>")`,
            backgroundSize: '200px 200px',
          }}/>
          {/* figcaption mono */}
          <div style={{
            position: 'absolute', left: 16, bottom: 16,
            fontFamily: W_FONTS.mono,
            fontSize: 10, letterSpacing: '0.22em',
            color: 'rgba(245,240,232,0.78)',
            textTransform: 'uppercase',
            padding: '6px 10px',
            background: 'rgba(15,23,19,0.35)',
            backdropFilter: 'blur(4px)',
          }}>
            fig. 03 · studio seventy, spring ed.
          </div>
        </div>
      )}
    </div>
  );
}

// Small meta bar at bottom of image (dimensions etc)
function HeroMeta() {
  const time = useTime();
  const w = stageWeights(time);
  const showBar = (w.s1 + w.s2 * 0.5) * (1 - (w.s3 + w.s4));
  const showText = w.s2 * 0.5 + w.s3 + w.s4;

  return (
    <div style={{
      position: 'absolute',
      left: 180 + 72, top: 170 + 710,
      width: 760, height: 14,
    }}>
      <div style={{
        position: 'absolute', left: 0, top: 2, width: 300, height: 10,
        background: W_PALETTE.grey300,
        opacity: showBar,
        borderRadius: 2,
      }}/>
      <div style={{
        position: 'absolute', left: 0, top: 0,
        fontFamily: W_FONTS.mono,
        fontSize: 11, letterSpacing: '0.18em',
        color: W_PALETTE.ink40,
        textTransform: 'uppercase',
        opacity: showText,
      }}>
        est. 2019 · based in berlin · est. response 1d
      </div>
    </div>
  );
}

// ─── Stage pips / progress across 4 stages ──────────────────────────────
function StagePips() {
  const time = useTime();
  const w = stageWeights(time);

  const pips = [
    { label: '01', active: w.s1 },
    { label: '02', active: w.s2 },
    { label: '03', active: w.s3 },
    { label: '04', active: w.s4 },
  ];

  return (
    <div style={{
      position: 'absolute',
      left: '50%', bottom: 70,
      transform: 'translateX(-50%)',
      display: 'flex', alignItems: 'center', gap: 44,
      fontFamily: W_FONTS.mono,
      fontSize: 11, letterSpacing: '0.24em',
      textTransform: 'uppercase',
    }}>
      {pips.map((p, i) => (
        <div key={p.label} style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
          <div style={{
            width: 8, height: 8, borderRadius: 4,
            background: p.active > 0.3 ? W_PALETTE.moss500 : W_PALETTE.ink15,
            boxShadow: p.active > 0.3 ? `0 0 10px ${W_PALETTE.moss400}` : 'none',
            transition: 'none',
          }}/>
          <span style={{
            color: p.active > 0.3 ? W_PALETTE.moss600 : W_PALETTE.ink40,
            fontWeight: 500,
          }}>
            {p.label}
          </span>
          {i < pips.length - 1 && (
            <div style={{ width: 24, height: 1, background: W_PALETTE.ink15, marginLeft: 12 }}/>
          )}
        </div>
      ))}
    </div>
  );
}

// ─── Main scene ─────────────────────────────────────────────────────────
function WebDesignProcessScene() {
  return (
    <>
      <WCornerChrome />

      {/* The morphing hero layout */}
      <HeroBackground />
      <HeroNav />
      <HeroEyebrow />
      <AccentBar />
      <HeroHeadline />
      <HeroSubcopy />
      <HeroCTA />
      <HeroImage />
      <HeroMeta />

      <StagePips />
      <StageLabel />
    </>
  );
}

Object.assign(window, {
  WebDesignProcessScene, W_PALETTE, W_FONTS,
  stageWeights, blendColor, blendScalar,
});
