// CaseStudyBattingLab.jsx — The Batting Lab case study.
// Wrapped in IIFE so internals (CS, helper components) don't collide with
// other case studies. Same editorial layout language as the Cadillac and
// 23.Springs case studies.

;(function () {

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

// ============ SOURCE CONTENT ============
const CS = {
  slug: 'batting-lab',
  title: 'The Batting Lab',
  subtitle: 'An immersive batting cage activation — UX/UI and multi-projection-mapped content driving an interactive, AI-aware experience.',
  client: 'SAS',
  agency: 'McCann',
  studio: 'Volvox Labs / OBJ',
  year: 2022,
  format: 'Immersive · projection-mapped · interactive',
  role: 'UX/UI · Multi Projection Mapping Content',

  lead: 'A first-of-its-kind immersive batting cage. Players step inside a fully projection-mapped environment that tracks their swings, scores their data, and reflects it back as motion, light, and feedback — turning practice into spectacle and data into experience.',

  concept: [
    'The brief: build a batting cage that is also a stage. Make practice visual. Make every swing legible. Use data not as a readout but as a material — translated into surfaces, color, and motion that respond in real time.',
    'A multi-disciplinary build, fully integrated end to end: sensor data into custom software, software into rendered content, content onto five projection-mapped surfaces, and surfaces orchestrated through a UX/UI that any player can pick up.',
  ],

  blocks: [
    {
      no: '02',
      key: 'data',
      eyebrow: '02 — The Data',
      title: 'The Data.',
      body: [
        'High-frequency capture of every swing — bat speed, contact point, exit velocity, launch angle, spray direction. The raw stream of a single at-bat becomes a structured, queryable record.',
        'Data is the raw material of the experience. It feeds the visuals, drives the feedback, and persists per-player so progress is measurable session over session.',
      ],
      images: [
        'site/assets/02_PROJECT/2022/BattingLab/GraphWalkthroughIncage.gif',
        'site/assets/02_PROJECT/2022/BattingLab/IMG_0734_MADDIE.jpg',
      ],
      cornerTl: 'BLOCK 02 / DATA',
      cornerBr: 'TELEMETRY',
    },
    {
      no: '03',
      key: 'learning',
      eyebrow: '03 — Learning Experience',
      title: 'Learning Experience.',
      body: [
        'Every swing is read, scored, and reflected — turning blind repetition into visible progress. The cage answers in motion and color: a clean strike one way, a miss another.',
        'Designed for first-time visitors and serious players alike. The UX scales from drop-in fun to deliberate practice, with deeper layers available for those who want to drill into their numbers.',
      ],
      video: 'site/assets/02_PROJECT/2022/BattingLab/VDO/Header.mp4',
      cornerTl: 'BLOCK 03 / LEARNING',
      cornerBr: 'UX · FEEDBACK',
    },
  ],

  // The fourth section is a multi-discipline build summary, rendered as a 4-up grid.
  build: {
    eyebrow: '04 — Build',
    title: 'Content production. Fabrication. Software. Art direction.',
    intro: 'A fully integrated build delivered across four disciplines, every layer authored in-house and tuned to the same source data.',
    pillars: [
      {
        no: '04.01',
        label: 'Content Production',
        body: 'Generative and pre-rendered content authored to a single, composited canvas — built to read across the projection surfaces and against the geometry of the cage.',
      },
      {
        no: '04.02',
        label: 'Fabrication',
        body: 'Cage geometry, surface treatments, sensor mounts, and projector rigging — designed and fabricated in-house to spec, install-ready and tour-ready.',
      },
      {
        no: '04.03',
        label: 'Software Development',
        body: 'Sensor capture, data normalization, real-time visual triggers, and player session persistence — a custom stack tying telemetry to render in milliseconds.',
      },
      {
        no: '04.04',
        label: 'Art Direction',
        body: 'A unified visual language across UI, in-cage content, and feedback states — calibrated to read at distance, in low light, and through the parallax of projection.',
      },
    ],
  },

  credits: [
    { role: 'Client',                       who: 'SAS' },
    { role: 'Agency',                       who: 'McCann' },
    { divider: true },
    { role: 'Design Partner',               who: 'Volvox Labs / OBJ' },
    { role: 'Technology Director',          who: 'Ben Forest' },
    { role: 'Creative Technologist',        who: 'Matthew Ross' },
    { role: 'Motion Designer',              who: 'Pasakorn Nontananadth' },
    { role: 'Motion Designer',              who: 'Siriphong Tipayakesorn' },
    { role: 'Graphic Designer',             who: 'Yalan Wen' },
    { role: 'Physical Experience Designer', who: 'Zyia Zhang' },
    { role: 'Fabrication',                  who: 'Max Smith · Benjamin Mosca · AJ Sapala · Ciara Smith' },
    { role: 'Producer',                     who: 'Gilad Dor' },
  ],

  press: [
    {
      role: 'Press',
      who: 'The Today Show',
      href: 'https://www.today.com/video/how-this-high-tech-batting-cage-is-getting-kids-involved-in-stem-142680645543',
    },
    {
      role: 'Press',
      who: 'AdWeek',
      href: 'https://www.adweek.com/creativity/how-an-analytics-company-used-a-batting-cage-to-get-kids-amped-about-data/',
    },
  ],
};

// =====================================================================
// Shared atoms — scoped to this IIFE.
// =====================================================================

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>
  );
}

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(36px, 4vw, 56px)', lineHeight: 1.05,
            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>
  );
}

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 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 BlockMedia({ block }) {
  const placeholderGradient = 'radial-gradient(ellipse at 50% 50%, #1a1a1a 0%, #0a0b0d 60%, #050505 100%)';

  // Side-by-side images (equal width columns).
  if (block.images && block.images.length > 0) {
    return (
      <div style={{
        maxWidth: 1280, margin: '0 auto',
        display: 'grid',
        gridTemplateColumns: `repeat(${block.images.length}, 1fr)`,
        gap: 16,
      }}>
        {block.images.map((src, i) => (
          <Reveal key={src} y={20} delay={i * 100}>
            <div style={{
              position: 'relative', overflow: 'hidden',
              aspectRatio: '16/9', borderRadius: 12,
              background: '#0a0b0d',
              border: '1px solid var(--ak-line)',
            }}>
              <img src={src} alt={`${block.title} — ${i + 1}`}
                style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }} />
            </div>
          </Reveal>
        ))}
      </div>
    );
  }

  const hasMedia = block.image || block.video;

  return (
    <Reveal y={20}>
      <div style={{
        maxWidth: 1280, margin: '0 auto',
        borderRadius: 16, overflow: 'hidden',
        background: '#0a0b0d',
        border: '1px solid var(--ak-line)',
        position: 'relative',
        aspectRatio: hasMedia ? undefined : '16/9',
      }}>
        {block.video ? (
          <video
            src={block.video}
            autoPlay
            loop
            muted
            playsInline
            style={{ width: '100%', height: 'auto', display: 'block' }}
          />
        ) : block.image ? (
          <img src={block.image} alt={block.title}
            style={{ width: '100%', height: 'auto', display: 'block' }} />
        ) : (
          <div style={{ position: 'absolute', inset: 0, background: placeholderGradient }} />
        )}
        {!hasMedia && <PlaceholderTag>{block.key}</PlaceholderTag>}
        <CornerMeta tl={block.cornerTl} br={block.cornerBr} />
      </div>
    </Reveal>
  );
}

function PressBlock() {
  if (!CS.press || CS.press.length === 0) return null;
  return (
    <section style={{
      padding: '0 32px 72px',
      background: 'var(--ak-bg)',
    }}>
      <div style={{ maxWidth: 768, margin: '0 auto' }}>
        <Reveal>
          <MonoLabel dim style={{ marginBottom: 28 }}>06 — Press</MonoLabel>
        </Reveal>
        <Reveal y={20} delay={120}>
          <table style={{
            width: '100%', borderCollapse: 'collapse',
            fontFamily: "'JetBrains Mono', monospace", fontSize: 10, letterSpacing: '0.04em',
          }}>
            <tbody>
              {CS.press.map(({ role, who, href }, 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: 220 }}>{role}</td>
                  <td style={{ padding: '10px 0', color: 'var(--ak-fg)' }}>
                    {href ? (
                      <a href={href} target="_blank" rel="noopener noreferrer"
                        className="ak-link"
                        style={{ color: 'var(--ak-fg)', textDecoration: 'none' }}>
                        {who} ↗
                      </a>
                    ) : who}
                  </td>
                </tr>
              ))}
              <tr style={{ borderTop: '1px solid var(--ak-line)' }}><td colSpan="2" style={{ height: 0 }} /></tr>
            </tbody>
          </table>
        </Reveal>
      </div>
    </section>
  );
}

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 }}>05 — 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((row, i) => {
                if (row.divider) {
                  return (
                    <tr key={i}><td colSpan="2" style={{ height: 14 }} /></tr>
                  );
                }
                return (
                  <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: 220 }}>{row.role}</td>
                    <td style={{ padding: '10px 0', color: 'var(--ak-fg)' }}>{row.who}</td>
                  </tr>
                );
              })}
              <tr style={{ borderTop: '1px solid var(--ak-line)' }}><td colSpan="2" style={{ height: 0 }} /></tr>
            </tbody>
          </table>
        </Reveal>
      </div>
    </section>
  );
}

// =====================================================================
// CASE STUDY — 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,
              }}>
                The Batting Lab<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 */}
      <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/2022/BattingLab/VDO/Header.mp4"
              autoPlay
              loop
              muted
              playsInline
              style={{ width: '100%', height: 'auto', display: 'block' }}
            />
            <CornerMeta tl="BATTING LAB / IN SITU" br={`IMMERSIVE · ${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],
            ['agency', CS.agency],
            ['studio', CS.studio],
            ['year',   CS.year],
            ['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 batting cage that is also a stage. Practice as spectacle, data as material.">
        {CS.concept.map((p, i) => (
          <p key={i} style={proseP}>{p}</p>
        ))}
      </ProseSection>

      {/* CONCEPT GALLERY — 2-up of in-cage stills */}
      <section style={{ padding: '0 32px 120px', background: 'var(--ak-bg)' }}>
        <div style={{
          maxWidth: 1280, margin: '0 auto',
          display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: 16,
        }}>
          {[
            'site/assets/02_PROJECT/2022/BattingLab/batter2-1024x683.jpg',
            'site/assets/02_PROJECT/2022/BattingLab/KidHittingMontage.gif',
          ].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={`Batting Lab — concept ${i + 1}`}
                  style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }} />
              </div>
            </Reveal>
          ))}
        </div>
      </section>

      {/* INDIVIDUAL BLOCKS — The Data, Learning Experience */}
      {CS.blocks.map((b) => (
        <React.Fragment key={b.key}>
          <ProseSection eyebrow={b.eyebrow} title={b.title}>
            {b.body.map((p, i) => (
              <p key={i} style={proseP}>{p}</p>
            ))}
          </ProseSection>
          <section style={{ padding: '0 32px 120px', background: 'var(--ak-bg)' }}>
            <BlockMedia block={b} />
          </section>
        </React.Fragment>
      ))}

      {/* BUILD — 4-discipline grid (content / fabrication / software / art direction) */}
      <section style={{
        padding: '120px 32px',
        background: 'var(--ak-bg)',
        borderTop: '1px solid var(--ak-line)',
      }}>
        <div style={{ maxWidth: 1280, margin: '0 auto' }}>
          <div style={{
            display: 'grid', gridTemplateColumns: '1fr 1.4fr', gap: 64,
            alignItems: 'start', marginBottom: 64,
          }}>
            <Reveal>
              <MonoLabel dim>{CS.build.eyebrow}</MonoLabel>
              <h2 style={{
                fontFamily: "'PP Neue Montreal', 'Geist', system-ui, sans-serif",
                fontSize: 'clamp(36px, 4vw, 56px)', lineHeight: 1.05,
                letterSpacing: '-0.02em', fontWeight: 400,
                margin: '20px 0 0',
                textWrap: 'balance',
              }}>
                {CS.build.title}
              </h2>
            </Reveal>
            <Reveal y={28} delay={120} style={{ marginTop: 14 }}>
              <p style={proseP}>{CS.build.intro}</p>
            </Reveal>
          </div>

          <Reveal y={20} delay={180}>
            <ol style={{
              listStyle: 'none', padding: 0, margin: 0,
              display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 24,
              borderTop: '1px solid var(--ak-line)',
            }}>
              {CS.build.pillars.map((p) => (
                <li key={p.no} style={{
                  paddingTop: 20,
                  borderTop: '2px solid var(--ak-fg)',
                  marginTop: -1,
                }}>
                  <MonoLabel dim style={{ marginBottom: 12 }}>{p.no}</MonoLabel>
                  <h3 style={{
                    fontFamily: "'PP Neue Montreal', 'Geist', system-ui, sans-serif",
                    fontSize: 22, lineHeight: 1.15, letterSpacing: '-0.015em',
                    fontWeight: 400, margin: '0 0 12px',
                    color: 'var(--ak-fg)',
                  }}>
                    {p.label}
                  </h3>
                  <p style={{ ...proseP, marginTop: 0, fontSize: 14, lineHeight: 1.5 }}>
                    {p.body}
                  </p>
                </li>
              ))}
            </ol>
          </Reveal>
        </div>
      </section>

      {/* CREDITS */}
      <CreditsBlock />

      {/* PRESS */}
      <PressBlock />

    </div>
  );
}

window.CaseStudyBattingLab = CaseStudyA;

})();
