// CaseStudy299Park.jsx — duplicate of CaseStudy23Springs for the 299 Park
// project. Wrapped in an IIFE so its internals (CS, hooks, helper
// components) don't collide with the 23.Springs file. Edit the CS object
// below to swap text/assets independently from 23.Springs.
;(function () {
// CaseStudy23Springs.jsx — Project case study, two layout directions.
// Direction A: editorial long-form. Direction B: indexed technical dossier.
// Both share the same source content; only chrome / rhythm / hierarchy differ.

const { useState: useStateCS, useEffect: useEffectCS, useRef: useRefCS } = React;

// ============ SOURCE CONTENT ============
const CS = {
  slug: '299-park',
  title: '299 Park',
  subtitle: 'Refreshed content and mediating design, dropped into the middle of Midtown’s daily noise.',
  client: 'Fisher Brothers',
  studio: 'Volvox Labs',
  year: 2025,
  location: 'Manhattan, NY',
  format: 'Permanent · Long LED format',
  duration: '24h cyclical · quarter-hour marks · hourly world transitions',
  role: 'Creative Direction / Art Direction',
  responsibilities: [
    'Visual system across six layers',
    'Direction of content design team',
    'Look development across five worlds',
    'Pipeline supervision — Houdini → TD',
  ],

  // Three-line lead — used in both directions
  lead: 'A full Volvox Labs content and hardware refresh for Fisher Brothers at 299 Park Avenue — a top-to-bottom update of the lobby experience in midtown New York.',

  // Concept body
  concept: [
    'Set within a reception space framed by a generous vase landscape, the work resets its surroundings through slow-moving, meditative content that evolves over time. Where the street moves fast, the screen breathes. Where the city demands attention, the work releases it.',
    'The composition pairs awe-inspiring digital atmospheres with the calm of natural elements, each extending and elevating the other — softening the lobby’s architectural geometry and opening the space beyond its physical bounds.',
    'Serving tenants daily while facing one of Manhattan’s busiest pedestrian corridors, the experience offers a quiet pause to anyone passing by.',
  ],

  // System spec — used in technical sections of both directions
  systemSpec: [
    { k: 'CANVAS',     v: '8K composite · single continuous surface' },
    { k: 'LAYERS',     v: '6 · independently driven, real-time composited' },
    { k: 'TIME LOGIC', v: 'quarter-hour markers · hourly world rotation' },
    { k: 'ENGINE',     v: 'TouchDesigner · master clock + compositor' },
    { k: 'CONTENT',    v: 'Houdini sims · pre-rendered loops · GPU particles' },
    { k: 'INPUT',      v: 'depth cameras · lidar · presence-driven response' },
    { k: 'PLAYBACK',   v: 'custom GPU cluster · synchronized output chain' },
    { k: 'AUDIO',      v: 'spatial composition · world-locked stems' },
  ],

  // Pipeline diagram — node-graph style
  pipelineNodes: [
    { id: 'sensors',  x: 60,  y: 280, label: 'sensors',       sub: 'lidar · depth · presence',  kind: 'input' },
    { id: 'clock',    x: 60,  y: 80,  label: 'master clock',  sub: 'q-hour · hourly cue',        kind: 'input' },
    { id: 'houdini',  x: 280, y: 60,  label: 'houdini sim',   sub: 'flip · monarchs · coral',    kind: 'gen' },
    { id: 'gpu',      x: 280, y: 200, label: 'gpu particles', sub: 'shoals · pollen · fauna',    kind: 'gen' },
    { id: 'loops',    x: 280, y: 340, label: 'pre-rendered',  sub: '4k loops · stems',           kind: 'gen' },
    { id: 'td',       x: 540, y: 200, label: 'touchdesigner', sub: '6-layer compositor',         kind: 'core' },
    { id: 'audio',    x: 540, y: 360, label: 'audio engine',  sub: 'world-locked stems',         kind: 'core' },
    { id: 'cluster',  x: 800, y: 200, label: 'gpu cluster',   sub: '8k synchronized chain',      kind: 'out' },
    { id: 'canvas',   x: 1040, y: 200, label: '8k canvas',     sub: 'lobby — granite properties', kind: 'out' },
  ],
  pipelineEdges: [
    ['clock', 'houdini'], ['clock', 'gpu'], ['clock', 'loops'],
    ['sensors', 'gpu'], ['sensors', 'td'],
    ['houdini', 'td'], ['gpu', 'td'], ['loops', 'td'],
    ['td', 'cluster'], ['audio', 'cluster'],
    ['cluster', 'canvas'],
    ['clock', 'audio'],
  ],

  // Biomes
  worlds: [
    {
      id: 'curiosities-particles',
      no: '01',
      family: 'Curiosities & Particles',
      name: 'Curiosities & Particles',
      states: [
        { st: 'essence — emergence', body: 'The beginning. Bubbles surface from blank space, tentative at first, then multiplying until they take over the field. A study in how presence arises from absence.' },
      ],
      mediaA: 'radial-gradient(ellipse at 30% 70%, #2a8a9c 0%, #0a3a4a 50%, #050b18 100%)',
      mediaB: 'linear-gradient(180deg, #4a9aa8 0%, #1a4a5a 50%, #08151a 100%)',
      video: 'site/assets/02_PROJECT/2025/299/VDO/299Scene1Solo_01.mp4',
      extraRows: [
        { before: 'site/assets/02_PROJECT/2025/299/VDO/T1_s01_250413_540p.mp4', video: 'site/assets/02_PROJECT/2025/299/VDO/299Scene2Solo_01.mp4', label: 'flow — interaction',  body: 'Particles gather into complex forms, drifting and responding to one another. Shapes merge and evolve through space and time — no longer isolated, but in dialogue. Movement becomes choreography.', flip: true },
        { before: 'site/assets/02_PROJECT/2025/299/VDO/T1_s02_540p_250225.mp4', video: 'site/assets/02_PROJECT/2025/299/VDO/299Scene3Solo_01.mp4', label: 'cosmos — unification', body: 'Particles dissolve into something larger than themselves. Individual elements give way to a single phenomenon — a collective body in motion. We no longer see the parts; we feel the whole.' },
      ],
      endVideo: 'site/assets/02_PROJECT/2025/299/VDO/T1_s03_540p_250311.mp4',
    },
    {
      id: 'ethereal-landscape',
      no: '02',
      family: 'Ethereal Landscape',
      name: 'Ethereal Landscape',
      states: [
        { st: 'gentle morning — awakening', body: 'A dreamy morning carried by a soft breeze, traced through the swaying of trees. Light shifts in intensity and color from the back of the scene, slowly tuning the world awake.' },
      ],
      mediaA: 'radial-gradient(ellipse at 30% 70%, #2a8a9c 0%, #0a3a4a 50%, #050b18 100%)',
      mediaB: 'linear-gradient(180deg, #4a9aa8 0%, #1a4a5a 50%, #08151a 100%)',
      video: 'site/assets/02_PROJECT/2025/299/VDO/299Scene4Solo_01.mp4',
      extraRows: [
        { before: 'site/assets/02_PROJECT/2025/299/VDO/T2_s1_Morning_250422_30fps_540p.mp4', video: 'site/assets/02_PROJECT/2025/299/VDO/299Scene5Solo_01.mp4', label: 'blooming — flourish', body: 'Afternoon bleeds into evening, casting dramatic color and shadow across the landscape. Flowers populate the field, and the evening breeze carries their petals through the air.', flip: false },
        { before: 'site/assets/02_PROJECT/2025/299/VDO/T2_s2_Bloom_250417_30fps_540p.mp4', video: 'site/assets/02_PROJECT/2025/299/VDO/299Scene6Solo_01.mp4', label: 'nightscape — reverie', body: 'Night arrives with a quiet surprise — flowers still in bloom, joined by fireflies drifting through the dark. A full moon rises behind, illuminating the scene in soft silver.' },
      ],
      endVideo: 'site/assets/02_PROJECT/2025/299/VDO/T2_s3_Night_250417_30fps_540.mp4',
    },
  ],

  // Process phases
  phases: [
    {
      no: '01',
      key: 'concept',
      label: 'Concept & Moodboard',
      tag: 'PHASE 01',
      body: 'Established the elevator-as-narrative spine, the clock function, and the world-rotation logic. Mood research drew from cellular biology, migration, and architectural surfaces — selected for compositional density at canvas scale.',
      bullets: ['Elevator as structural framework', 'Time-aware behavior — quarter / hour cues', 'Five worlds, three states each (sprout · growth · pollinate)'],
    },
    {
      no: '02',
      key: 'lookdev',
      label: 'Look Development',
      tag: 'PHASE 02',
      body: 'Five visual languages developed in parallel. Each world authored to read at viewer-distance and at facade-distance — a constraint that drove macro-detail selection and color hierarchy. Approved looks locked the style frames that drove production.',
      bullets: ['Macro-to-canvas legibility', 'Per-world palette and motion vocabulary', 'Style frames locked before production'],
    },
    {
      no: '03',
      key: 'rd',
      label: 'Prototyping & R&D',
      tag: 'PHASE 03',
      body: 'Six-layer compositor built and stress-tested in TouchDesigner. Houdini sim outputs round-tripped through the pipeline. Sensor input wired to live layers and validated for latency. Cluster sync verified end-to-end at 8K.',
      bullets: ['TouchDesigner six-layer compositor', 'Houdini → TD round-trip validated', 'Sensor latency budget closed', 'Cluster sync at 8K verified'],
    },
    {
      no: '04',
      key: 'production',
      label: 'Content Production',
      tag: 'PHASE 04',
      body: 'Content design team executed the locked style frames across all five worlds and three states each — fifteen primary state-deliveries, plus the quarter-hour and hour-transition layers. Direction maintained per-world consistency and cross-world rhythm.',
      bullets: ['15 primary state deliveries', 'Quarter-hour micro-cue layer', 'Hour transitions authored per pair'],
    },
    {
      no: '05',
      key: 'outcome',
      label: 'Outcome',
      tag: 'PHASE 05',
      body: 'Permanently installed in the 23.Springs lobby. The work runs continuously, its rhythm legible to a passerby, its full arc available to anyone who lingers. Sensor response surfaces gently — present but never demanding.',
      bullets: ['Permanent install · 23.Springs lobby', 'Continuous 24-hour cycle', 'Hourly world transitions live'],
    },
  ],

  credits: [
    { role: 'Client',              who: 'Fisher Brothers' },
    { role: 'Creative Direction',  who: 'Kamil Nawratil · Pasakorn Nontananadh' },
    { role: 'CG Artist',           who: 'Pasakorn Nontananadh' },
    { role: 'CG Artist',           who: 'Boning Li' },
    { role: 'CG Artist',           who: 'Preto HF' },
    { role: 'CG Artist',           who: 'Matthew Reinhardt' },
    { role: 'Technical Director',  who: 'Ben Forest' },
    { role: 'Creative Tech',       who: 'Matt Ross' },
    { role: 'Producer',            who: 'Gilad Dor · Carmen Mouynes' },
    { role: 'Pixera Programmer',   who: 'Nickon Mirsepassi' },
    { role: 'Capture and Edit',    who: 'Webb Hunt' },
  ],
};

// =====================================================================
// Shared atoms
// =====================================================================

function MonoLabel({ children, dim, size = 11, style }) {
  return (
    <div style={{
      fontFamily: "'JetBrains Mono', SFMono-Regular, Menlo, monospace",
      fontSize: size, letterSpacing: '0.16em',
      color: dim ? 'var(--ak-fg-2)' : 'var(--ak-fg)',
      textTransform: 'uppercase',
      ...style,
    }}>{children}</div>
  );
}

// Simple SVG node-graph for the pipeline diagram. Same data, two visual treatments.
function PipelineDiagram({ variant = 'A' }) {
  const W = 1100, H = 440;
  const lookup = Object.fromEntries(CS.pipelineNodes.map(n => [n.id, n]));
  const isB = variant === 'B';

  const accent = 'var(--ak-blue, #0052ff)';
  const fg = 'var(--ak-fg)';
  const fg2 = 'var(--ak-fg-2)';
  const line = 'var(--ak-line)';

  const kindColor = (kind) => {
    if (kind === 'core') return accent;
    if (kind === 'out')  return fg;
    return fg2;
  };

  return (
    <svg viewBox={`0 0 ${W} ${H}`} style={{ width: '100%', height: 'auto', display: 'block' }}>
      <defs>
        <marker id={`arr-${variant}`} viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto">
          <path d="M0,0 L10,5 L0,10 z" fill={fg2} />
        </marker>
      </defs>

      {/* grid (B only) */}
      {isB && (
        <g opacity="0.5">
          {Array.from({ length: 12 }).map((_, i) => (
            <line key={`v${i}`} x1={i * 100} y1={0} x2={i * 100} y2={H} stroke={line} strokeWidth="1" />
          ))}
          {Array.from({ length: 6 }).map((_, i) => (
            <line key={`h${i}`} x1={0} y1={i * 80} x2={W} y2={i * 80} stroke={line} strokeWidth="1" />
          ))}
        </g>
      )}

      {/* edges */}
      {CS.pipelineEdges.map(([from, to], i) => {
        const a = lookup[from], b = lookup[to];
        const x1 = a.x + 80, y1 = a.y + 30;
        const x2 = b.x, y2 = b.y + 30;
        const mx = (x1 + x2) / 2;
        const d = `M ${x1} ${y1} C ${mx} ${y1}, ${mx} ${y2}, ${x2} ${y2}`;
        return (
          <path key={i} d={d} fill="none"
            stroke={fg2} strokeWidth={isB ? 1 : 1.25}
            strokeDasharray={isB ? '4 4' : 'none'}
            opacity={0.7}
            markerEnd={`url(#arr-${variant})`} />
        );
      })}

      {/* nodes */}
      {CS.pipelineNodes.map(n => {
        const c = kindColor(n.kind);
        const w = 160, h = 60;
        return (
          <g key={n.id} transform={`translate(${n.x}, ${n.y})`}>
            <rect x="0" y="0" width={w} height={h}
              rx={isB ? 0 : 8}
              fill="var(--ak-bg)"
              stroke={c} strokeWidth={n.kind === 'core' ? 2 : 1} />
            <text x="12" y="24" fontFamily="'JetBrains Mono', monospace" fontSize="11" letterSpacing="0.12em" fill={fg} style={{ textTransform: 'uppercase' }}>
              {n.label.toUpperCase()}
            </text>
            <text x="12" y="44" fontFamily="'JetBrains Mono', monospace" fontSize="9.5" letterSpacing="0.06em" fill={fg2}>
              {n.sub}
            </text>
            {n.kind === 'core' && (
              <rect x="0" y="0" width="3" height={h} fill={c} />
            )}
          </g>
        );
      })}
    </svg>
  );
}

// =====================================================================
// DIRECTION A — Editorial long-form
// =====================================================================

function CaseStudyA({ onNavigate }) {
  return (
    <div style={{ background: 'var(--ak-bg)', color: 'var(--ak-fg)' }}>
      {/* HERO */}
      <section style={{ padding: '40px 32px 24px', borderBottom: '1px solid var(--ak-line)' }}>
        <div style={{ maxWidth: 1280, margin: '0 auto' }}>
          <Reveal y={20}>
            <button onClick={() => onNavigate && onNavigate('work')}
              style={{
                background: 'transparent', border: 'none', padding: 0, cursor: 'pointer',
                fontFamily: "'JetBrains Mono', monospace", fontSize: 12,
                letterSpacing: '0.14em', textTransform: 'uppercase',
                color: 'var(--ak-fg-2)',
                display: 'inline-flex', alignItems: 'center', gap: 8,
                marginBottom: 24,
                transition: 'color 200ms cubic-bezier(0.2,0.6,0.2,1)',
              }}
              onMouseEnter={(e) => { e.currentTarget.style.color = 'var(--ak-fg)'; }}
              onMouseLeave={(e) => { e.currentTarget.style.color = 'var(--ak-fg-2)'; }}>
              <span style={{ display: 'inline-block', transition: 'transform 240ms cubic-bezier(0.2,0.6,0.2,1)' }}>←</span>
              back to work
            </button>
          </Reveal>
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 48, alignItems: 'end', marginTop: 0 }}>
            <Reveal y={32}>
              <h1 style={{
                fontFamily: "'PP Neue Montreal', 'Geist', system-ui, sans-serif",
                fontSize: 'clamp(48px, 6vw, 88px)',
                lineHeight: 0.92,
                letterSpacing: '-0.04em',
                fontWeight: 400,
                margin: 0,
              }}>
                299 Park<span style={{ color: 'var(--ak-blue, #0052ff)' }}>.</span>
              </h1>
            </Reveal>
            <Reveal y={32} delay={120}>
              <p style={{
                fontFamily: "'PP Neue Montreal', 'Geist', system-ui, sans-serif",
                fontSize: 16, lineHeight: 1.4, letterSpacing: '-0.005em',
                color: 'var(--ak-fg)', margin: 0, maxWidth: 460,
              }}>
                {CS.subtitle}
              </p>
            </Reveal>
          </div>
        </div>
      </section>

      {/* HERO MEDIA — full bleed canvas placeholder */}
      <section style={{ padding: '0 32px', background: 'var(--ak-bg)' }}>
        <Reveal y={20}>
          <div style={{
            maxWidth: 1280, margin: '20px auto 0',
            borderRadius: 16, overflow: 'hidden',
            background: '#0a0b0d',
            border: '1px solid var(--ak-line)',
            position: 'relative',
          }}>
            <video
              src="site/assets/02_PROJECT/2025/299/Full Comp Master_01.mp4"
              autoPlay
              loop
              muted
              playsInline
              style={{
                width: '100%', height: 'auto',
                display: 'block',
              }}
            />
            <CornerMeta tl="299 PARK / RECEPTION" br={`2 x FULL HD · ${CS.year}`} />
          </div>
        </Reveal>
      </section>

      {/* AT A GLANCE STRIP */}
      <section style={{ padding: '64px 32px 32px' }}>
        <div style={{ maxWidth: 1280, margin: '0 auto',
          display: 'grid', gridTemplateColumns: 'repeat(6, 1fr)', gap: 24,
          paddingTop: 32, borderTop: '1px solid var(--ak-line)',
        }}>
          {[
            ['client',   CS.client],
            ['studio',   CS.studio],
            ['year',     CS.year],
            ['location', CS.location],
            ['format',   CS.format],
            ['role',     CS.role],
          ].map(([k, v], i) => (
            <Reveal key={k} delay={i * 50}>
              <MonoLabel dim style={{ marginBottom: 10 }}>{k}</MonoLabel>
              <div style={{ fontSize: 15, lineHeight: 1.4, letterSpacing: '-0.005em' }}>{v}</div>
            </Reveal>
          ))}
        </div>
      </section>

      {/* LEAD */}
      <section style={{ padding: '48px 32px 96px' }}>
        <div style={{ maxWidth: 920, margin: '0 auto' }}>
          <Reveal>
            <p style={{
              fontFamily: "'PP Neue Montreal', 'Geist', system-ui, sans-serif",
              fontSize: 28, lineHeight: 1.32, letterSpacing: '-0.015em',
              fontWeight: 400, color: 'var(--ak-fg)', margin: 0,
              textWrap: 'balance',
            }}>
              {CS.lead}
            </p>
          </Reveal>
        </div>
      </section>

      {/* CONCEPT */}
      <ProseSection eyebrow="01 — Concept" title="A study in contrast — a moment of stillness within the relentless rhythm of Midtown Manhattan." dark={false}>
        {CS.concept.map((p, i) => (
          <p key={i} style={proseP}>{p}</p>
        ))}
      </ProseSection>

      {/* WORLDS — full bleed alternating */}
      <section style={{ padding: '0', background: 'var(--ak-bg)', borderTop: '1px solid var(--ak-line)' }}>
        <div style={{ padding: '120px 32px 48px', maxWidth: 1280, margin: '0 auto' }}>
          <Reveal>
            <MonoLabel dim>02 — Biomes</MonoLabel>
            <h2 style={{
              fontFamily: "'PP Neue Montreal', 'Geist', system-ui, sans-serif",
              fontSize: 'clamp(40px, 5vw, 64px)', lineHeight: 1.0,
              letterSpacing: '-0.02em', fontWeight: 400, margin: '20px 0 0',
              maxWidth: 900,
            }}>
              Two biomes. Each in three states
            </h2>
          </Reveal>
        </div>
        {CS.worlds.map((w, i) => (
          <WorldPanelA key={w.id} world={w} idx={i} />
        ))}
      </section>

      {/* OUTCOME */}
      <section style={{ padding: '120px 32px 48px', background: 'var(--ak-bg-2)', borderTop: '1px solid var(--ak-line)' }}>
        <div style={{ maxWidth: 920, margin: '0 auto', textAlign: 'left' }}>
          <Reveal>
            <MonoLabel dim>03 — Outcome</MonoLabel>
          </Reveal>
          <Reveal y={32} delay={100}>
            <h2 style={{
              fontFamily: "'PP Neue Montreal', 'Geist', system-ui, sans-serif",
              fontSize: 'clamp(40px, 5vw, 64px)', lineHeight: 1.05,
              letterSpacing: '-0.02em', fontWeight: 400, margin: '20px 0 24px',
              textWrap: 'balance',
            }}>
              Permanently installed. Continuous. Quietly responsive.
            </h2>
          </Reveal>
          <Reveal delay={180}>
            <p style={proseP}>The work stands in contrast to the street’s daily chaos, easing the passerby’s experience without demanding attention. What doesn’t ask to be seen is often what’s felt most.</p>
          </Reveal>
        </div>
      </section>

      {/* OUTCOME STILLS — 2 x 2 grid above the video */}
      <section style={{ padding: '0 32px 24px', background: 'var(--ak-bg-2)' }}>
        <div style={{
          maxWidth: 1280, margin: '0 auto',
          display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: 16,
        }}>
          {[
            'site/assets/02_PROJECT/2025/299/299Still_03.jpg',
            'site/assets/02_PROJECT/2025/299/299Still_09.jpg',
            'site/assets/02_PROJECT/2025/299/299Still_13.jpg',
            'site/assets/02_PROJECT/2025/299/299Still_23.jpg',
          ].map((src, i) => (
            <Reveal key={src} y={20} delay={i * 80}>
              <div style={{
                position: 'relative', overflow: 'hidden',
                aspectRatio: '16/9', borderRadius: 12,
                background: '#0a0b0d',
                border: '1px solid var(--ak-line)',
              }}>
                <img src={src} alt={`299 Park outcome still ${i + 1}`}
                  style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }} />
              </div>
            </Reveal>
          ))}
        </div>
      </section>

      {/* OUTCOME VIDEO — Vimeo embed */}
      <section style={{ padding: '0 32px 120px', background: 'var(--ak-bg-2)' }}>
        <Reveal y={20}>
          <div style={{
            maxWidth: 1280, margin: '0 auto',
            aspectRatio: '16/9',
            borderRadius: 16, overflow: 'hidden',
            background: '#0a0b0d',
            border: '1px solid var(--ak-line)',
            position: 'relative',
          }}>
            <iframe
              src="https://player.vimeo.com/video/1131538813?h=2d1c65f5a8&autoplay=1&loop=1&muted=1&background=1"
              title="299 Park outcome"
              frameBorder="0"
              referrerPolicy="strict-origin-when-cross-origin"
              allow="autoplay; fullscreen; picture-in-picture; clipboard-write; encrypted-media; web-share"
              allowFullScreen
              style={{
                position: 'absolute', top: 0, left: 0,
                width: '100%', height: '100%',
                border: 0,
              }}
            />
            <CornerMeta tl="OUTCOME / FIG. 02" br={`${CS.year} · ${CS.location.toUpperCase()}`} />
          </div>
        </Reveal>
      </section>

      {/* CREDITS */}
      <CreditsBlock />
    </div>
  );
}

const proseP = {
  fontFamily: "'PP Neue Montreal', 'Geist', system-ui, sans-serif",
  fontSize: 17, lineHeight: 1.55, letterSpacing: '-0.005em',
  fontWeight: 400, color: 'var(--ak-fg-2)',
  margin: '20px 0 0',
};

function ProseSection({ eyebrow, title, children, dark }) {
  return (
    <section style={{
      padding: '120px 32px',
      background: dark ? 'var(--ak-bg-3)' : 'var(--ak-bg)',
      color: dark ? 'var(--ak-fg-on3)' : 'var(--ak-fg)',
      borderTop: '1px solid var(--ak-line)',
    }}>
      <div style={{ maxWidth: 1280, margin: '0 auto', display: 'grid', gridTemplateColumns: '1fr 1.4fr', gap: 64, alignItems: 'start' }}>
        <Reveal>
          <MonoLabel dim>{eyebrow}</MonoLabel>
          <h2 style={{
            fontFamily: "'PP Neue Montreal', 'Geist', system-ui, sans-serif",
            fontSize: 'clamp(27px, 3vw, 42px)', lineHeight: 1.1,
            letterSpacing: '-0.02em', fontWeight: 400,
            margin: '20px 0 0',
            color: dark ? 'var(--ak-fg-on3)' : 'var(--ak-fg)',
            textWrap: 'balance',
          }}>
            {title}
          </h2>
        </Reveal>
        <Reveal y={28} delay={120} style={{ marginTop: 14 }}>
          <div style={{ color: dark ? 'var(--ak-fg-2-on3)' : 'var(--ak-fg-2)' }}>
            {children}
          </div>
        </Reveal>
      </div>
    </section>
  );
}

// Renders a single tile inside the stills row: text card, video, or image.
function renderStillTile(entry, world, key) {
  const isText = entry && typeof entry === 'object' && !Array.isArray(entry) && entry.label;
  const isVideo = typeof entry === 'string' && /\.(mp4|webm|mov)$/i.test(entry);
  return (
    <div key={key} style={{
      position: 'relative', overflow: 'hidden',
      aspectRatio: '16/9', borderRadius: isText ? 0 : 12,
      background: isText ? 'transparent' : '#0a0b0d',
      border: isText ? 'none' : '1px solid var(--ak-line)',
    }}>
      {isText ? (
        <div style={{
          position: 'absolute', inset: 0,
          padding: '32px 36px',
          display: 'flex', alignItems: 'center', justifyContent: 'center',
        }}>
          <div style={{
            display: 'grid', gridTemplateColumns: '160px 1fr',
            gap: 12, alignItems: 'baseline',
          }}>
            <MonoLabel style={{ color: 'var(--ak-blue, #0052ff)' }}>{entry.label}</MonoLabel>
            <p style={{
              fontFamily: "'PP Neue Montreal', 'Geist', system-ui, sans-serif",
              fontSize: 14, lineHeight: 1.5,
              letterSpacing: '-0.005em', fontWeight: 400, margin: 0,
              color: 'var(--ak-fg)',
            }}>
              {entry.body}
            </p>
          </div>
        </div>
      ) : isVideo ? (
        <video src={entry} autoPlay loop muted playsInline
          style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }} />
      ) : (
        <img src={entry} alt={`${world.name} tile`}
          style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }} />
      )}
    </div>
  );
}

function WorldPanelA({ world, idx }) {
  const flip = idx % 2 === 1;
  return (
    <section style={{ padding: '48px 32px', borderTop: '1px solid var(--ak-line)' }}>
      <div style={{ maxWidth: 1280, margin: '0 auto 28px' }}>
        <Reveal y={20}>
          <MonoLabel dim>biome {world.no}</MonoLabel>
          <h3 style={{
            fontFamily: "'PP Neue Montreal', 'Geist', system-ui, sans-serif",
            fontSize: 'clamp(28px, 3.2vw, 44px)', lineHeight: 1.0,
            letterSpacing: '-0.02em', fontWeight: 400, margin: '10px 0 0',
          }}>
            {world.name}
          </h3>
        </Reveal>
      </div>
      <div style={{ maxWidth: 1280, margin: '0 auto',
        display: 'grid', gridTemplateColumns: flip ? '1fr 2.22fr' : '2.22fr 1fr', gap: 40, alignItems: 'center' }}>
        <Reveal y={28} style={{ order: flip ? 2 : 1 }}>
          <div style={{
            position: 'relative', overflow: 'hidden',
            aspectRatio: (world.video || world.vimeo) ? '16/9' : '4/3', borderRadius: 12,
            background: (world.video || world.vimeo) ? '#0a0b0d' : world.mediaA,
            border: '1px solid var(--ak-line)',
          }}>
            {world.video ? (
              <video
                src={world.video}
                autoPlay
                loop
                muted
                playsInline
                style={{
                  position: 'absolute', top: 0, left: 0,
                  width: '100%', height: '100%',
                  objectFit: 'cover',
                  border: 0,
                }}
              />
            ) : world.vimeo ? (
              <iframe
                src={world.vimeo}
                title={world.name}
                frameBorder="0"
                referrerPolicy="strict-origin-when-cross-origin"
                allow="autoplay; fullscreen; picture-in-picture; clipboard-write; encrypted-media; web-share"
                allowFullScreen
                style={{
                  position: 'absolute', top: 0, left: 0,
                  width: '100%', height: '100%',
                  border: 0,
                }}
              />
            ) : (
              <PlaceholderTag>{world.name.toLowerCase()}</PlaceholderTag>
            )}
          </div>
        </Reveal>
        <Reveal y={28} delay={140} style={{ order: flip ? 1 : 2 }}>
          <div style={{ display: 'grid', gap: 16 }}>
            {world.states.map(s => (
              <div key={s.st} style={{ display: 'grid', gridTemplateColumns: '160px 1fr', gap: 12, alignItems: 'baseline' }}>
                <MonoLabel style={{ color: 'var(--ak-blue, #0052ff)' }}>{s.st}</MonoLabel>
                <p style={{ ...proseP, marginTop: 0, color: 'var(--ak-fg)', fontSize: 14, lineHeight: 1.5 }}>{s.body}</p>
              </div>
            ))}
          </div>
        </Reveal>
      </div>
      {world.extraRows && world.extraRows.map((row, ri) => {
        const rowFlip = row.flip != null ? row.flip : flip;
        return (
        <React.Fragment key={`extra-${ri}`}>
          {row.before && (
            <div style={{ maxWidth: 1280, margin: '40px auto 0' }}>
              <Reveal y={20}>
                <div style={{
                  position: 'relative', overflow: 'hidden',
                  borderRadius: 12,
                  background: '#0a0b0d',
                  border: '1px solid var(--ak-line)',
                }}>
                  <video src={row.before} autoPlay loop muted playsInline
                    style={{ width: '100%', height: 'auto', display: 'block' }} />
                </div>
              </Reveal>
            </div>
          )}
          <div style={{
            maxWidth: 1280, margin: '40px auto 0',
            display: 'grid', gridTemplateColumns: rowFlip ? '1fr 2.22fr' : '2.22fr 1fr',
            gap: 40, alignItems: 'center',
          }}>
            <Reveal y={28} style={{ order: rowFlip ? 2 : 1 }}>
              <div style={{
                position: 'relative', overflow: 'hidden',
                aspectRatio: '16/9', borderRadius: 12,
                background: '#0a0b0d',
                border: '1px solid var(--ak-line)',
              }}>
                <video src={row.video} autoPlay loop muted playsInline
                  style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }} />
              </div>
            </Reveal>
            <Reveal y={28} delay={140} style={{ order: rowFlip ? 1 : 2 }}>
              <div style={{ display: 'grid', gridTemplateColumns: '160px 1fr', gap: 12, alignItems: 'baseline' }}>
                <MonoLabel style={{ color: 'var(--ak-blue, #0052ff)' }}>{row.label}</MonoLabel>
                <p style={{ ...proseP, marginTop: 0, color: 'var(--ak-fg)', fontSize: 14, lineHeight: 1.5 }}>{row.body}</p>
              </div>
            </Reveal>
          </div>
        </React.Fragment>
        );
      })}
      {world.endVideo && (
        <div style={{ maxWidth: 1280, margin: '40px auto 0' }}>
          <Reveal y={20}>
            <div style={{
              position: 'relative', overflow: 'hidden',
              borderRadius: 12,
              background: '#0a0b0d',
              border: '1px solid var(--ak-line)',
            }}>
              <video src={world.endVideo} autoPlay loop muted playsInline
                style={{ width: '100%', height: 'auto', display: 'block' }} />
            </div>
          </Reveal>
        </div>
      )}
      {world.stills && world.stills.length > 0 && (
        <div style={{
          maxWidth: 1280, margin: '24px auto 0',
          display: 'grid',
          gridTemplateColumns: world.stills.length === 2
            ? '1fr 2.22fr'
            : `repeat(${Math.min(world.stills.length, 2)}, 1fr)`,
          gap: 16,
        }}>
          {world.stills.map((entry, i) => {
            const isStack = Array.isArray(entry);
            return (
              <Reveal key={`col-${i}`} y={20} delay={i * 100} style={{ height: '100%' }}>
                {isStack ? (
                  <div style={{
                    display: 'flex', flexDirection: 'column',
                    justifyContent: 'space-between',
                    height: '100%', gap: 16,
                  }}>
                    {entry.map((sub, j) => renderStillTile(sub, world, `${i}-${j}`))}
                  </div>
                ) : renderStillTile(entry, world, `${i}`)}
              </Reveal>
            );
          })}
        </div>
      )}
    </section>
  );
}

// =====================================================================
// DIRECTION B — Indexed technical dossier
// =====================================================================

function CaseStudyB({ onNavigate }) {
  const sections = [
    { id: 'sec-meta',    label: 'Meta'      },
    { id: 'sec-concept', label: 'Concept'   },
    { id: 'sec-system',  label: 'System'    },
    { id: 'sec-pipeline',label: 'Pipeline'  },
    { id: 'sec-process', label: 'Process'   },
    { id: 'sec-worlds',  label: 'Worlds'    },
    { id: 'sec-outcome', label: 'Outcome'   },
    { id: 'sec-credits', label: 'Credits'   },
  ];
  const [active, setActive] = useStateCS('sec-meta');

  // Spy on which section is in view to highlight rail
  useEffectCS(() => {
    const els = sections.map(s => document.getElementById(s.id)).filter(Boolean);
    if (!els.length) return;
    const io = new IntersectionObserver((entries) => {
      const visible = entries.filter(e => e.isIntersecting);
      if (visible.length) {
        const top = visible.sort((a, b) => a.boundingClientRect.top - b.boundingClientRect.top)[0];
        setActive(top.target.id);
      }
    }, { rootMargin: '-20% 0px -60% 0px', threshold: 0 });
    els.forEach(el => io.observe(el));
    return () => io.disconnect();
  }, []);

  return (
    <div style={{ background: 'var(--ak-bg)', color: 'var(--ak-fg)' }}>
      {/* HEADER STRIP — file dossier feel */}
      <section style={{
        padding: '40px 32px 20px',
        borderBottom: '1px solid var(--ak-line)',
        fontFamily: "'JetBrains Mono', monospace", fontSize: 11, letterSpacing: '0.14em',
        color: 'var(--ak-fg-2)', textTransform: 'uppercase',
      }}>
        <div style={{ maxWidth: 1400, margin: '0 auto',
          display: 'grid', gridTemplateColumns: 'repeat(5, 1fr)', gap: 16, alignItems: 'center' }}>
          <button onClick={() => onNavigate && onNavigate('work')}
            style={{ background: 'transparent', border: 0, padding: 0, cursor: 'pointer',
              fontFamily: "'JetBrains Mono', monospace", fontSize: 11, letterSpacing: '0.14em',
              color: 'var(--ak-fg-2)', textTransform: 'uppercase', textAlign: 'left',
            }}>← work / index</button>
          <div>file · {CS.slug}</div>
          <div>year · {CS.year}</div>
          <div>client · {CS.client.toLowerCase()}</div>
          <div style={{ textAlign: 'right' }}>case study · v1</div>
        </div>
      </section>

      {/* HERO — typographic heavy */}
      <section id="sec-meta" style={{ padding: '64px 32px 32px' }}>
        <div style={{ maxWidth: 1400, margin: '0 auto' }}>
          <Reveal>
            <h1 style={{
              fontFamily: "'PP Neue Montreal', 'Geist', system-ui, sans-serif",
              fontSize: 'clamp(96px, 14vw, 220px)',
              lineHeight: 0.86,
              letterSpacing: '-0.05em',
              fontWeight: 400,
              margin: 0,
              textWrap: 'balance',
            }}>
              23<span style={{ color: 'var(--ak-blue, #0052ff)' }}>.</span>Springs
            </h1>
          </Reveal>
          <Reveal y={20} delay={120}>
            <div style={{
              marginTop: 24,
              display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 32,
            }}>
              <p style={{
                fontFamily: "'PP Neue Montreal', 'Geist', system-ui, sans-serif",
                fontSize: 22, lineHeight: 1.32, letterSpacing: '-0.01em',
                color: 'var(--ak-fg)', margin: 0,
              }}>
                {CS.subtitle}
              </p>
              <p style={{ ...proseP, marginTop: 0 }}>{CS.lead}</p>
            </div>
          </Reveal>

          {/* meta grid */}
          <Reveal y={20} delay={200}>
            <div style={{
              marginTop: 56, padding: '20px 0',
              borderTop: '1px solid var(--ak-line)', borderBottom: '1px solid var(--ak-line)',
              display: 'grid', gridTemplateColumns: 'repeat(6, 1fr)', gap: 16,
              fontFamily: "'JetBrains Mono', monospace", fontSize: 12, letterSpacing: '0.06em',
            }}>
              {[
                ['client',   CS.client.toLowerCase()],
                ['studio',   CS.studio.toLowerCase()],
                ['year',     CS.year],
                ['location', CS.location.toLowerCase()],
                ['format',   '8k · 6 layers · perm.'],
                ['role',     CS.role.toLowerCase()],
              ].map(([k, v]) => (
                <div key={k}>
                  <div style={{ color: 'var(--ak-fg-2)', textTransform: 'uppercase', marginBottom: 6 }}>{k}</div>
                  <div style={{ color: 'var(--ak-fg)' }}>{v}</div>
                </div>
              ))}
            </div>
          </Reveal>
        </div>
      </section>

      {/* HERO MEDIA */}
      <section style={{ padding: '0 32px 96px' }}>
        <Reveal y={20}>
          <div style={{
            maxWidth: 1400, margin: '0 auto',
            aspectRatio: '21/9',
            background: 'radial-gradient(ellipse at 50% 50%, #1a4f8a 0%, #061520 70%, #0a0b0d 100%)',
            border: '1px solid var(--ak-line)',
            position: 'relative', overflow: 'hidden',
          }}>
            <PlaceholderTag>hero capture · 8k canvas in situ</PlaceholderTag>
            <CornerMeta tl="FIG. 00 / IN SITU" br={`${CS.year} · DALLAS, TX`} />
          </div>
        </Reveal>
      </section>

      {/* MAIN BODY — sticky TOC rail + content */}
      <section style={{ padding: '0 32px 96px' }}>
        <div style={{ maxWidth: 1400, margin: '0 auto',
          display: 'grid', gridTemplateColumns: '180px 1fr', gap: 48, alignItems: 'start',
        }}>
          {/* TOC */}
          <aside style={{ position: 'sticky', top: 100 }}>
            <MonoLabel dim style={{ marginBottom: 16 }}>contents</MonoLabel>
            <nav style={{ display: 'grid', gap: 8 }}>
              {sections.map((s, i) => {
                const on = active === s.id;
                return (
                  <a key={s.id} href={`#${s.id}`}
                    style={{
                      fontFamily: "'JetBrains Mono', monospace",
                      fontSize: 12, letterSpacing: '0.06em',
                      textTransform: 'uppercase',
                      color: on ? 'var(--ak-fg)' : 'var(--ak-fg-2)',
                      textDecoration: 'none',
                      padding: '6px 0 6px 14px',
                      borderLeft: on ? '2px solid var(--ak-blue, #0052ff)' : '2px solid var(--ak-line)',
                      transition: 'color 200ms, border-color 200ms',
                    }}>
                    {String(i).padStart(2, '0')} · {s.label}
                  </a>
                );
              })}
            </nav>
          </aside>

          {/* CONTENT COLUMN */}
          <div>
            {/* CONCEPT */}
            <article id="sec-concept" style={{ paddingTop: 0 }}>
              <ChapterHead num="01" title="Concept" />
              <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 32 }}>
                {CS.concept.map((p, i) => (
                  <Reveal key={i} delay={i * 60}>
                    <MonoLabel dim style={{ marginBottom: 8 }}>{`§ 01.${i + 1}`}</MonoLabel>
                    <p style={{ ...proseP, marginTop: 0, color: 'var(--ak-fg)', fontSize: 16 }}>{p}</p>
                  </Reveal>
                ))}
              </div>
            </article>

            {/* SYSTEM */}
            <article id="sec-system" style={{ paddingTop: 96 }}>
              <ChapterHead num="02" title="System" />
              <Reveal>
                <table style={{ width: '100%', borderCollapse: 'collapse',
                  fontFamily: "'JetBrains Mono', monospace", fontSize: 12, letterSpacing: '0.04em',
                }}>
                  <tbody>
                    {CS.systemSpec.map(({ k, v }, i) => (
                      <tr key={k} style={{ borderTop: '1px solid var(--ak-line)' }}>
                        <td style={{ padding: '14px 16px 14px 0', width: 80, color: 'var(--ak-fg-2)' }}>
                          02.{String(i + 1).padStart(2, '0')}
                        </td>
                        <td style={{ padding: '14px 16px 14px 0', color: 'var(--ak-fg-2)', textTransform: 'uppercase', whiteSpace: 'nowrap' }}>{k}</td>
                        <td style={{ padding: '14px 0', color: 'var(--ak-fg)', textTransform: 'lowercase' }}>{v}</td>
                      </tr>
                    ))}
                    <tr style={{ borderTop: '1px solid var(--ak-line)' }}><td colSpan="3" style={{ height: 0 }} /></tr>
                  </tbody>
                </table>
              </Reveal>
            </article>

            {/* PIPELINE DIAGRAM */}
            <article id="sec-pipeline" style={{ paddingTop: 96 }}>
              <ChapterHead num="03" title="Pipeline" subtitle="fig. 01 — runtime data flow" />
              <Reveal y={20}>
                <div style={{
                  border: '1px solid var(--ak-line)',
                  background: 'var(--ak-bg-2)',
                  padding: 32,
                  position: 'relative',
                }}>
                  <PipelineDiagram variant="B" />
                </div>
              </Reveal>
              <Reveal delay={120}>
                <p style={{ ...proseP, fontSize: 14, marginTop: 16, fontFamily: "'JetBrains Mono', monospace", letterSpacing: '0.02em' }}>
                  master clock + sensor input feed three generative sources (houdini sims, gpu particles, pre-rendered loops). touchdesigner composites all six layers and hands off to a custom gpu cluster, which drives a single 8k canvas in the lobby.
                </p>
              </Reveal>
            </article>

            {/* PROCESS */}
            <article id="sec-process" style={{ paddingTop: 96 }}>
              <ChapterHead num="04" title="Process" subtitle="five phases · concept → install" />
              <div style={{ display: 'grid', gap: 0 }}>
                {CS.phases.map((ph, i) => (
                  <Reveal key={ph.key} y={20} delay={i * 60}>
                    <div style={{
                      display: 'grid', gridTemplateColumns: '60px 1fr', gap: 20,
                      padding: '28px 0', borderTop: '1px solid var(--ak-line)',
                    }}>
                      <MonoLabel>{`04.${ph.no}`}</MonoLabel>
                      <div>
                        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', gap: 16 }}>
                          <h3 style={{
                            fontFamily: "'PP Neue Montreal', 'Geist', system-ui, sans-serif",
                            fontSize: 24, lineHeight: 1.15, letterSpacing: '-0.01em',
                            fontWeight: 400, margin: 0,
                          }}>{ph.label}</h3>
                          <MonoLabel dim>{ph.tag.toLowerCase()}</MonoLabel>
                        </div>
                        <p style={{ ...proseP, marginTop: 12, fontSize: 15 }}>{ph.body}</p>
                        <ul style={{
                          listStyle: 'none', margin: '14px 0 0', padding: 0,
                          display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: 6,
                          fontFamily: "'JetBrains Mono', monospace", fontSize: 11,
                          color: 'var(--ak-fg-2)', letterSpacing: '0.04em', textTransform: 'uppercase',
                        }}>
                          {ph.bullets.map((b, j) => (
                            <li key={j} style={{ display: 'grid', gridTemplateColumns: '14px 1fr', gap: 6 }}>
                              <span>—</span><span>{b}</span>
                            </li>
                          ))}
                        </ul>
                      </div>
                    </div>
                  </Reveal>
                ))}
                <div style={{ borderTop: '1px solid var(--ak-line)' }} />
              </div>
            </article>

            {/* WORLDS — strict grid */}
            <article id="sec-worlds" style={{ paddingTop: 96 }}>
              <ChapterHead num="05" title="Worlds" subtitle="five environments · three states each" />
              <div style={{
                display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: 24,
              }}>
                {CS.worlds.map((w, i) => (
                  <WorldCardB key={w.id} world={w} idx={i} />
                ))}
              </div>
            </article>

            {/* OUTCOME */}
            <article id="sec-outcome" style={{ paddingTop: 96 }}>
              <ChapterHead num="06" title="Outcome" />
              <Reveal>
                <p style={{
                  fontFamily: "'PP Neue Montreal', 'Geist', system-ui, sans-serif",
                  fontSize: 24, lineHeight: 1.32, letterSpacing: '-0.01em',
                  fontWeight: 400, margin: 0, color: 'var(--ak-fg)',
                }}>
                  Permanently installed in the 23.Springs lobby. The work runs continuously — its rhythm legible to a passerby, its full arc available to anyone who lingers. Sensor response surfaces gently — present but never demanding.
                </p>
              </Reveal>
              <Reveal y={20} delay={120}>
                <div style={{
                  marginTop: 40,
                  aspectRatio: '21/9',
                  background: 'radial-gradient(ellipse at 70% 50%, #2a8a9c 0%, #0a3a4a 50%, #050b18 100%)',
                  border: '1px solid var(--ak-line)',
                  position: 'relative', overflow: 'hidden',
                }}>
                  <PlaceholderTag>install · final canvas</PlaceholderTag>
                  <CornerMeta tl="FIG. 02 / FINAL" br="DALLAS · 2025" />
                </div>
              </Reveal>
            </article>

            {/* CREDITS — inline (B variant uses table within column) */}
            <article id="sec-credits" style={{ paddingTop: 96, paddingBottom: 64 }}>
              <ChapterHead num="07" title="Credits" />
              <Reveal>
                <table style={{
                  width: '100%', borderCollapse: 'collapse',
                  fontFamily: "'JetBrains Mono', monospace", fontSize: 12, letterSpacing: '0.04em',
                }}>
                  <tbody>
                    {CS.credits.map(({ role, who }, i) => (
                      <tr key={i} style={{ borderTop: '1px solid var(--ak-line)' }}>
                        <td style={{ padding: '14px 16px 14px 0', color: 'var(--ak-fg-2)', textTransform: 'uppercase', whiteSpace: 'nowrap', verticalAlign: 'top' }}>{role}</td>
                        <td style={{ padding: '14px 0', color: 'var(--ak-fg)' }}>{who}</td>
                      </tr>
                    ))}
                    <tr style={{ borderTop: '1px solid var(--ak-line)' }}><td colSpan="2" style={{ height: 0 }} /></tr>
                  </tbody>
                </table>
              </Reveal>
            </article>
          </div>
        </div>
      </section>
    </div>
  );
}

function ChapterHead({ num, title, subtitle }) {
  return (
    <Reveal>
      <div style={{
        display: 'grid', gridTemplateColumns: '60px 1fr',
        gap: 20, paddingBottom: 24, marginBottom: 32,
        borderBottom: '1px solid var(--ak-line)',
      }}>
        <MonoLabel>§ {num}</MonoLabel>
        <div>
          <h2 style={{
            fontFamily: "'PP Neue Montreal', 'Geist', system-ui, sans-serif",
            fontSize: 'clamp(32px, 4vw, 48px)', lineHeight: 1.0,
            letterSpacing: '-0.02em', fontWeight: 400, margin: 0,
          }}>
            {title}
          </h2>
          {subtitle && (
            <MonoLabel dim style={{ marginTop: 8 }}>{subtitle}</MonoLabel>
          )}
        </div>
      </div>
    </Reveal>
  );
}

function WorldCardB({ world, idx }) {
  return (
    <Reveal y={20} delay={idx * 80}>
      <div style={{
        border: '1px solid var(--ak-line)',
        background: 'var(--ak-bg)',
        display: 'grid',
      }}>
        <div style={{
          aspectRatio: '4/3',
          background: world.mediaB,
          position: 'relative', overflow: 'hidden',
        }}>
          <PlaceholderTag>{world.family.toLowerCase()} · {world.name.toLowerCase()}</PlaceholderTag>
          <CornerMeta tl={`05.0${idx + 1}`} br={world.no} />
        </div>
        <div style={{ padding: 24 }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 12 }}>
            <MonoLabel dim>{world.family.toLowerCase()}</MonoLabel>
            <MonoLabel>{world.no}</MonoLabel>
          </div>
          <h3 style={{
            fontFamily: "'PP Neue Montreal', 'Geist', system-ui, sans-serif",
            fontSize: 28, lineHeight: 1.0, letterSpacing: '-0.02em',
            fontWeight: 400, margin: '0 0 16px',
          }}>
            {world.name}
          </h3>
          <div style={{ display: 'grid', gap: 10 }}>
            {world.states.map(s => (
              <details key={s.st} style={{ borderTop: '1px solid var(--ak-line)', paddingTop: 10 }}>
                <summary style={{
                  cursor: 'pointer', listStyle: 'none', display: 'flex',
                  justifyContent: 'space-between', alignItems: 'baseline',
                  fontFamily: "'JetBrains Mono', monospace", fontSize: 11,
                  letterSpacing: '0.12em', textTransform: 'uppercase', color: 'var(--ak-fg)',
                }}>
                  <span>{s.st}</span>
                  <span style={{ color: 'var(--ak-blue, #0052ff)' }}>+</span>
                </summary>
                <p style={{ ...proseP, marginTop: 8, fontSize: 14 }}>{s.body}</p>
              </details>
            ))}
          </div>
        </div>
      </div>
    </Reveal>
  );
}

// =====================================================================
// Shared chrome
// =====================================================================

function PlaceholderTag({ children }) {
  return (
    <div style={{
      position: 'absolute', top: 16, left: 16,
      padding: '5px 10px',
      background: 'rgba(255,255,255,0.92)', color: '#0a0b0d',
      borderRadius: 999,
      fontFamily: "'JetBrains Mono', monospace",
      fontSize: 9, letterSpacing: '0.14em', fontWeight: 600,
      textTransform: 'uppercase',
    }}>
      [ placeholder ] {children}
    </div>
  );
}

function CornerMeta({ tl, br }) {
  return (
    <>
      {tl && (
        <div style={{
          position: 'absolute', top: 16, right: 16,
          fontFamily: "'JetBrains Mono', monospace", fontSize: 10,
          letterSpacing: '0.14em', color: 'rgba(255,255,255,0.7)',
          textTransform: 'uppercase',
        }}>{tl}</div>
      )}
      {br && (
        <div style={{
          position: 'absolute', bottom: 16, right: 16,
          fontFamily: "'JetBrains Mono', monospace", fontSize: 10,
          letterSpacing: '0.14em', color: 'rgba(255,255,255,0.7)',
          textTransform: 'uppercase',
        }}>{br}</div>
      )}
    </>
  );
}

function CreditsBlock() {
  return (
    <section style={{
      padding: '72px 32px',
      background: 'var(--ak-bg)',
      borderTop: '1px solid var(--ak-line)',
    }}>
      <div style={{ maxWidth: 768, margin: '0 auto' }}>
        <Reveal>
          <MonoLabel dim style={{ marginBottom: 28 }}>04 — Credits</MonoLabel>
        </Reveal>
        <Reveal y={20} delay={120}>
          <table style={{
            width: '100%', borderCollapse: 'collapse',
            fontFamily: "'JetBrains Mono', monospace", fontSize: 10, letterSpacing: '0.04em',
          }}>
            <tbody>
              {CS.credits.map(({ role, who }, i) => (
                <tr key={i} style={{ borderTop: '1px solid var(--ak-line)' }}>
                  <td style={{ padding: '10px 10px 10px 0', color: 'var(--ak-fg-2)', textTransform: 'uppercase', whiteSpace: 'nowrap', verticalAlign: 'top', width: 168 }}>{role}</td>
                  <td style={{ padding: '10px 0', color: 'var(--ak-fg)' }}>{who}</td>
                </tr>
              ))}
              <tr style={{ borderTop: '1px solid var(--ak-line)' }}><td colSpan="2" style={{ height: 0 }} /></tr>
            </tbody>
          </table>
        </Reveal>
      </div>
    </section>
  );
}


  // Expose only the page component so this file does not pollute global scope.
  window.CaseStudy299Park = CaseStudyA;
})();
