// Direction C — Gallery-museum
// Vertical stripes: a row of tall narrow columns. Hover one and it expands
// (widens) revealing project info; click to open a detail panel.

// Videos live on Cloudflare R2 (free tier, no egress fees). To swap host,
// only this base URL needs to change.
const R2_VIDEO_BASE = 'https://pub-1c4d58c649d044529f99cbc8bfa2ac81.r2.dev/';

// Contact form posts to /contact (Cloudflare Pages Function in functions/contact.js).
// The real Discord webhook URL lives in the DISCORD_WEBHOOK environment variable
// set in Cloudflare Pages → Settings → Environment Variables.
const CONTACT_ENDPOINT = '/contact';

const SKILLS_LIST = [
  { name: 'videography', desc: 'capturing high-quality video content to communicate a clear message.' },
  { name: 'cinematography', desc: 'using composition, lighting, camera movement, and lens choice to create visually appealing and cinematic imagery.' },
  { name: 'video editing', desc: 'transforming raw footage into a polished final video through storytelling, pacing, music, and visual refinement.' },
  { name: 'directing', desc: 'overseeing the creative direction of a project, planning scenes, guiding talent, and ensuring the final vision comes together effectively.' },
  { name: 'sound design', desc: 'enhancing videos with music, sound effects, ambience, and audio mixing to create a more immersive viewing experience.' },
  { name: 'colour grading', desc: 'adjusting colour, contrast, and tone to create a unique visual style and mood, improving the overall look and feel of a video.' },
  { name: 'motion graphics', desc: 'creating simple animated text, graphics, and visual elements that help support storytelling and improve presentation.' },
];

const C_PROJECTS = [
  // Passion
  {
    n: '01', title: 'in my dreams', kind: 'Music video', y: '2026',
    category: 'Passion',
    skills: ['Sequence', 'Sound design', 'Colour grading'],
    blurb: 'My first real creative project. I was doing freelance work at the time, heard this song, and decided to cut a short music video around it for my portfolio.',
    accent: 300,
    photo: 'Website/in my dreams/in my dreams.png',
    photoBlur: 'Website/in my dreams/in my dreams b&w blur.png',
    video: R2_VIDEO_BASE + 'In My Dreams.webm',
  },
  {
    n: '02', title: 's.k.a.t.e', kind: 'Mini documentary', y: '2026',
    category: 'Passion',
    skills: ['Sequence', 'Sound design', 'Colour grading'],
    blurb: 'A mini documentary / vlog-style video of my local skate society’s tournament. I frequently skate with this bunch, but for this session I was the dedicated videographer.',
    accent: 220,
    photo: 'Website/s.k.a.t.e/skate.png',
    photoBlur: 'Website/s.k.a.t.e/skate b&w blur.png',
    video: R2_VIDEO_BASE + 'S.K.A.T.E.webm',
  },
  {
    n: '03', title: 'man drops cigarette', kind: 'Short film', y: '2026',
    category: 'Passion',
    skills: ['Directing', 'Sound design', 'Colour grading'],
    blurb: 'A short film directed by myself and a friend — written, filmed, edited and posted all within 24 hours. The idea came after he unironically dropped a cigarette outside his house.',
    accent: 30,
    photo: 'Website/man drops cigarette/man drops cigarette.png',
    photoBlur: 'Website/man drops cigarette/man drops cigarette b&w blur.png',
    video: R2_VIDEO_BASE + 'Man Drops Cigarette.webm',
  },
  {
    n: '04', title: 'cardiff', kind: 'Edit', y: '2025',
    category: 'Passion',
    skills: ['Masking', 'Sequence', 'Visual FX'],
    blurb: 'Recreated this edit for Cardiff after seeing the same edit on TikTok, but of Berlin. One of my earlier edits — filmed entirely on iPhone 16.',
    accent: 140,
    photo: 'Website/cardiff/cardiff.png',
    photoBlur: 'Website/cardiff/cardiff b&w blur.png',
    video: R2_VIDEO_BASE + 'cardiff.webm',
  },
  {
    n: '05', title: 'volkswagen up', kind: 'Edit', y: '2025',
    category: 'Passion',
    skills: ['Sequence', 'Colour grading', 'Music'],
    blurb: 'My first car edit. After my TikTok feed became flooded with the cleanest and crispiest car videos, I took it upon myself to create my own. Unfortunately I do not own a Lamborghini, so I used my friend’s Volkswagen — thanks Cerys.',
    accent: 340,
    photo: 'Website/cerys car/cerys car.png',
    photoBlur: 'Website/cerys car/cerys car b&w blur.png',
    video: R2_VIDEO_BASE + 'Cerys Car.webm',
  },
  // Work
  {
    n: '06', title: 'chippenham chamber & town', kind: 'Organic post', y: '2026',
    category: 'Work',
    skills: ['Sequence', 'Music', 'Colour grading'],
    blurb: 'Filmed as a favour for a friend who belongs to the Chippenham Chamber & Town community — an event for local business owners to network and discuss the town’s future.',
    accent: 100,
    photo: 'Website/chippenham chamber & town/chippenham chamber & town.png',
    photoBlur: 'Website/chippenham chamber & town/chippenham chamber & town b&w blur.png',
    video: R2_VIDEO_BASE + 'Chippenham Chamber & Town.webm',
  },
  {
    n: '07', title: 'sales reps', kind: 'Hiring ad', y: '2025',
    category: 'Work',
    skills: ['Sequence', 'Motion graphics', 'Colour grading'],
    blurb: 'A hiring campaign for a leads-generation company specialising in the ECO4 grant sector. Posted to Facebook to attract candidates for field-sales positions.',
    accent: 18,
    photo: 'Website/sales reps/sales reps 2.png',
    photoBlur: 'Website/sales reps/sales reps 2 b&w blur.png',
    video: R2_VIDEO_BASE + 'Sales Rep AD.webm',
  },
  {
    n: '08', title: 'hill barn', kind: 'Customer testimonial', y: '2025',
    category: 'Work',
    skills: ['Directing', 'Captions', 'Colour grading'],
    blurb: 'A customer testimonial produced as marketing material for a solar and heating installer. The customer had recently had home upgrades the client wanted to showcase.',
    accent: 60,
    photo: 'Website/hill barn/hill barn.png',
    photoBlur: 'Website/hill barn/hill barn b&w blur.png',
    video: R2_VIDEO_BASE + 'Hill Barn.webm',
  },
  {
    n: '09', title: 'canary garden cafe', kind: 'Organic post', y: '2025',
    category: 'Work',
    skills: ['Sequence', 'Music', 'Colour grading'],
    blurb: 'A free project a friend and I filmed for a cafe in Canary Wharf. We’d gone there to work, had such a pleasant time we offered to do a shoot on the way out.',
    accent: 260,
    photo: 'Website/canary garden cafe/canary garden cafe.png',
    photoBlur: 'Website/canary garden cafe/canary garden cafe b&w blur.png',
    video: R2_VIDEO_BASE + 'canary garden cafe.webm',
  },
  {
    n: '10', title: 'solar installation', kind: 'Social media ad', y: '2025',
    category: 'Work',
    skills: ['Directing', 'Captions', 'Colour grading'],
    blurb: 'A social media ad for a company in the energy-efficiency sector, used to demonstrate the ease of installation and attract potential customers.',
    accent: 200,
    photo: 'Website/solar installation/solar installation normal.png',
    photoBlur: 'Website/solar installation/solar installation b&w blur.png',
    video: R2_VIDEO_BASE + 'solar installation.webm',
  },
  {
    n: '11', title: 'the olive clinic', kind: 'Website material', y: '2026',
    category: 'Work',
    skills: ['Sequence', 'Music', 'Colour grading'],
    blurb: 'A cinematic-style tour for a local clinic, made to showcase their establishment on the website and attract potential customers.',
    accent: 160,
    photo: 'Website/the olive clinic/the olive clinic.png',
    photoBlur: 'Website/the olive clinic/the olive clinic b&w blur.png',
    video: R2_VIDEO_BASE + 'Olive Clinic.webm',
  },
];

// Encode space and other URL-unsafe chars so url("...") references resolve.
function photoUrl(path) {
  return path ? encodeURI(path) : null;
}

function useIsMobile(maxWidth = 768) {
  const [isMobile, setIsMobile] = React.useState(
    typeof window !== 'undefined' && window.matchMedia(`(max-width: ${maxWidth}px)`).matches
  );
  React.useEffect(() => {
    const mq = window.matchMedia(`(max-width: ${maxWidth}px)`);
    const on = (e) => setIsMobile(e.matches);
    mq.addEventListener('change', on);
    return () => mq.removeEventListener('change', on);
  }, [maxWidth]);
  return isMobile;
}

// Vertical stripe texture — runs top-to-bottom inside each column.
function CStripeV({ accent, intense = false }) {
  const a = `oklch(${intense ? 0.28 : 0.22} 0.018 ${accent})`;
  const b = `oklch(${intense ? 0.22 : 0.18} 0.022 ${accent})`;
  const c = `oklch(${intense ? 0.34 : 0.26} 0.028 ${accent})`;
  return (
    <div style={{
      width: '100%', height: '100%',
      background: `repeating-linear-gradient(180deg, ${a} 0 28px, ${b} 28px 56px, ${c} 56px 60px, ${b} 60px 88px)`,
      transition: 'background 380ms ease',
    }} />
  );
}

function CFrame({ accent, ratio, idx }) {
  const a = `oklch(0.22 0.018 ${accent})`;
  const b = `oklch(0.30 0.022 ${accent})`;
  return (
    <div style={{
      aspectRatio: ratio,
      background: `repeating-linear-gradient(${(idx * 31 + 90) % 180}deg, ${a} 0 22px, ${b} 22px 44px)`,
    }} />
  );
}

function CColumn({ p, i, hovered, onHover, onLeave, onClick }) {
  const isHover = hovered === i;
  const isOther = hovered != null && hovered !== i;
  return (
    <div
      onMouseEnter={() => onHover(i)}
      onMouseLeave={onLeave}
      onClick={onClick}
      style={{
        flex: isHover ? '4 1 0' : (isOther ? '0.7 1 0' : '1 1 0'),
        height: '100%',
        position: 'relative',
        cursor: 'pointer',
        overflow: 'hidden',
        borderLeft: i === 0 ? 'none' : '1px solid rgba(255,255,255,0.08)',
        transition: 'flex 480ms cubic-bezier(.2,.7,.2,1)',
      }}
    >
      {/* Background: photo if we have one, otherwise the stripe pattern */}
      {p.photo ? (
        <>
          {/* Blurred photo — visible by default */}
          <div style={{
            position: 'absolute', inset: 0,
            backgroundImage: `url("${photoUrl(p.photoBlur || p.photo)}")`,
            backgroundSize: 'cover',
            backgroundPosition: 'center',
            // If there's no dedicated blur file, fake it with CSS (and scale up
            // slightly so the soft edges don't reveal the column border).
            filter: p.photoBlur ? 'brightness(0.92)' : 'blur(14px) brightness(0.92)',
            transform: p.photoBlur ? 'none' : 'scale(1.08)',
            opacity: isHover ? 0 : 1,
            transition: 'opacity 380ms ease',
          }} />
          {/* Sharp photo — fades in on hover */}
          <div style={{
            position: 'absolute', inset: 0,
            backgroundImage: `url("${photoUrl(p.photo)}")`,
            backgroundSize: 'cover',
            backgroundPosition: 'center',
            filter: 'brightness(0.92)',
            opacity: isHover ? 1 : 0,
            transition: 'opacity 380ms ease',
          }} />
        </>
      ) : (
        <div style={{ position: 'absolute', inset: 0 }}>
          <CStripeV accent={p.accent} intense={isHover} />
        </div>
      )}

      {/* Vignette on hover for legibility */}
      <div style={{
        position: 'absolute', inset: 0,
        background: 'linear-gradient(180deg, rgba(10,10,10,0) 40%, rgba(10,10,10,0.92) 100%)',
        opacity: isHover ? 1 : 0,
        transition: 'opacity 380ms ease',
        pointerEvents: 'none',
      }} />

      {/* Number — always visible, top */}
      <div style={{
        position: 'absolute', top: 16, left: 16,
        fontSize: 11, color: '#f5f5f5',
        fontVariantNumeric: 'tabular-nums',
        background: 'rgba(10,10,10,0.65)',
        padding: '3px 7px', borderRadius: 2,
        letterSpacing: 0.3,
      }}>{p.n}</div>

      {/* Vertical title — collapsed state. Reads top→bottom inside the column. */}
      <div style={{
        position: 'absolute', top: 56, left: 0, right: 0, bottom: 24,
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        opacity: isHover ? 0 : 1,
        transition: 'opacity 240ms ease',
        pointerEvents: 'none',
      }}>
        <div style={{
          writingMode: 'vertical-rl',
          textOrientation: 'mixed',
          whiteSpace: 'nowrap',
          fontFamily: '"Roboto Mono", ui-monospace, monospace',
          fontSize: 18, fontWeight: 400, color: '#f5f5f5',
          letterSpacing: 0.2,
        }}>{p.title}</div>
      </div>

      {/* Expanded info panel — at the bottom */}
      <div style={{
        position: 'absolute', left: 20, right: 20, bottom: 20,
        opacity: isHover ? 1 : 0,
        transform: isHover ? 'translateY(0)' : 'translateY(8px)',
        transition: 'opacity 380ms ease 100ms, transform 380ms ease 100ms',
        pointerEvents: isHover ? 'auto' : 'none',
      }}>
        <div style={{ fontSize: 10, color: '#a0a0a0', letterSpacing: 0.4, marginBottom: 8 }}>
          {p.category} &nbsp;·&nbsp; {p.kind} &nbsp;·&nbsp; {p.y}
        </div>
        <h3 style={{
          fontFamily: '"Roboto Mono", ui-monospace, monospace',
          fontSize: 28, fontWeight: 400, letterSpacing: -0.6,
          margin: 0, lineHeight: 1.05, color: '#f5f5f5',
        }}>{p.title}</h3>
        <p style={{ margin: '10px 0 0', fontSize: 13, lineHeight: 1.5, color: '#cfcfcf', textWrap: 'pretty' }}>
          {p.blurb}
        </p>
        <div style={{ marginTop: 14, display: 'flex', flexWrap: 'wrap', gap: 6 }}>
          {p.skills.map((s) => (
            <span key={s} style={{
              fontSize: 10, color: '#f5f5f5',
              border: '1px solid rgba(255,255,255,0.22)',
              padding: '3px 8px', borderRadius: 999,
              letterSpacing: 0.3,
            }}>{s}</span>
          ))}
        </div>
      </div>
    </div>
  );
}

function CMobileCard({ p, expanded, onToggle, onOpen }) {
  return (
    <div
      onClick={onToggle}
      style={{
        position: 'relative',
        width: '100%',
        minHeight: expanded ? 280 : 96,
        borderTop: '1px solid rgba(255,255,255,0.08)',
        overflow: 'hidden',
        cursor: 'pointer',
        transition: 'min-height 360ms cubic-bezier(.2,.7,.2,1)',
      }}
    >
      {p.photo ? (
        <>
          <div style={{
            position: 'absolute', inset: 0,
            backgroundImage: `url("${photoUrl(p.photoBlur || p.photo)}")`,
            backgroundSize: 'cover',
            backgroundPosition: 'center',
            filter: p.photoBlur ? 'brightness(0.92)' : 'blur(14px) brightness(0.92)',
            transform: p.photoBlur ? 'none' : 'scale(1.08)',
            opacity: expanded ? 0 : 1,
            transition: 'opacity 380ms ease',
          }} />
          <div style={{
            position: 'absolute', inset: 0,
            backgroundImage: `url("${photoUrl(p.photo)}")`,
            backgroundSize: 'cover',
            backgroundPosition: 'center',
            filter: 'brightness(0.92)',
            opacity: expanded ? 1 : 0,
            transition: 'opacity 380ms ease',
          }} />
        </>
      ) : (
        <div style={{ position: 'absolute', inset: 0 }}>
          <CStripeV accent={p.accent} intense={expanded} />
        </div>
      )}
      {/* Scrim — always present so top text is readable on bright photos;
         deepens when the card is expanded so the blurb area is fully covered. */}
      <div style={{
        position: 'absolute', inset: 0,
        background: expanded
          ? 'linear-gradient(180deg, rgba(10,10,10,0.45) 0%, rgba(10,10,10,0.95) 100%)'
          : 'linear-gradient(180deg, rgba(10,10,10,0.55) 0%, rgba(10,10,10,0) 60%)',
        transition: 'background 280ms ease',
        pointerEvents: 'none',
      }} />
      <div style={{ position: 'relative', padding: '18px 18px 20px' }}>
        <div style={{
          display: 'flex', justifyContent: 'space-between', alignItems: 'baseline',
          fontSize: 11, color: '#f5f5f5',
          textShadow: '0 1px 4px rgba(0,0,0,0.55)',
        }}>
          <span style={{ fontVariantNumeric: 'tabular-nums', letterSpacing: 0.3 }}>{p.n}</span>
          <span style={{ color: '#d4d4d4', letterSpacing: 0.3, fontSize: 10 }}>
            {p.category} · {p.y}
          </span>
        </div>
        <h3 style={{
          fontFamily: '"Roboto Mono", ui-monospace, monospace',
          fontSize: 22, fontWeight: 400, letterSpacing: -0.4,
          margin: '8px 0 0', lineHeight: 1.1, color: '#f5f5f5',
          textShadow: '0 1px 6px rgba(0,0,0,0.6)',
        }}>{p.title}</h3>
        <div style={{
          opacity: expanded ? 1 : 0,
          maxHeight: expanded ? 400 : 0,
          transition: 'opacity 320ms ease 80ms, max-height 360ms ease',
          overflow: 'hidden',
        }}>
          <p style={{ margin: '12px 0 0', fontSize: 13, lineHeight: 1.5, color: '#e8e8e8', textWrap: 'pretty', textShadow: '0 1px 3px rgba(0,0,0,0.5)' }}>
            {p.blurb}
          </p>
          <div style={{ marginTop: 12, display: 'flex', flexWrap: 'wrap', gap: 6 }}>
            {p.skills.map((s) => (
              <span key={s} style={{
                fontSize: 10, color: '#f5f5f5',
                border: '1px solid rgba(255,255,255,0.35)',
                background: 'rgba(0,0,0,0.25)',
                padding: '3px 8px', borderRadius: 999,
                letterSpacing: 0.3,
              }}>{s}</span>
            ))}
          </div>
          <button
            onClick={(e) => { e.stopPropagation(); onOpen(); }}
            style={{
              marginTop: 14, border: '1px solid #f5f5f5', background: 'rgba(0,0,0,0.3)',
              fontFamily: 'inherit',
              fontSize: 12, color: '#f5f5f5', padding: '8px 14px', borderRadius: 2,
              letterSpacing: 0.3, cursor: 'pointer', minHeight: 36,
            }}
          >Open ↗</button>
        </div>
      </div>
    </div>
  );
}

function CProjectDetail({ p, onClose }) {
  const isMobile = useIsMobile();
  React.useEffect(() => {
    const k = (e) => { if (e.key === 'Escape') onClose(); };
    document.addEventListener('keydown', k);
    return () => document.removeEventListener('keydown', k);
  }, [onClose]);

  return (
    <div
      onClick={onClose}
      style={{
        position: 'fixed', inset: 0, zIndex: 50,
        background: 'rgba(10,10,10,0.97)',
        backdropFilter: 'blur(20px)',
        overflow: 'auto',
        WebkitOverflowScrolling: 'touch',
        animation: 'cFadeIn 280ms ease',
      }}
    >
      <style>{`@keyframes cFadeIn { from { opacity: 0; } to { opacity: 1; } }`}</style>
      <div onClick={(e) => e.stopPropagation()} style={{ padding: isMobile ? '14px 18px 48px' : '20px 32px 60px' }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: 8, paddingBottom: 24, borderBottom: '1px solid rgba(255,255,255,0.12)' }}>
          <span style={{ fontSize: 11, color: '#a0a0a0', letterSpacing: 0.3 }}>
            <span style={{ color: '#f5f5f5' }}>{p.n} / {p.title}</span> &nbsp;·&nbsp; {p.category} · {p.y}
          </span>
          <button onClick={onClose} style={{ border: 'none', background: 'transparent', cursor: 'pointer', fontFamily: 'inherit', fontSize: isMobile ? 13 : 11, color: '#f5f5f5', letterSpacing: 0.3, padding: '4px 8px', minHeight: 44, minWidth: 44 }}>
            Close ✕
          </button>
        </div>

        <div style={{ marginTop: 32, display: 'grid', gridTemplateColumns: isMobile ? '1fr' : '1fr 1.1fr', gap: isMobile ? 28 : 48, alignItems: 'start' }}>
          <div style={{
            display: 'flex', justifyContent: 'center', alignItems: 'center',
            background: '#000',
            // Cap the video at viewport height so vertical TikTok-format clips
            // don't push the rest of the page off-screen.
            maxHeight: isMobile ? '60vh' : '75vh',
            overflow: 'hidden',
          }}>
            {p.video ? (
              <video
                src={photoUrl(p.video)}
                poster={p.photo ? photoUrl(p.photo) : undefined}
                controls
                playsInline
                preload="none"
                controlsList="nodownload"
                style={{
                  width: '100%', height: '100%',
                  maxHeight: isMobile ? '60vh' : '75vh',
                  objectFit: 'contain',
                  display: 'block', background: '#000',
                }}
              />
            ) : (
              <div style={{ width: '100%' }}>
                <CFrame accent={p.accent} ratio={p.kind === 'Short film' || p.kind === 'Music video' || p.kind === 'Mini documentary' ? '16/9' : '4/5'} idx={0} />
              </div>
            )}
          </div>
          <div>
            <h1 style={{ fontFamily: '"Roboto Mono", ui-monospace, monospace', fontSize: isMobile ? 32 : 44, fontWeight: 400, letterSpacing: isMobile ? -0.6 : -1, margin: 0, lineHeight: 1.05, color: '#f5f5f5' }}>{p.title}</h1>
            <p style={{ fontSize: 11, color: '#a0a0a0', letterSpacing: 0.3, marginTop: 8 }}>
              {p.category} &nbsp;·&nbsp; {p.kind} &nbsp;·&nbsp; {p.y}
            </p>
            <p style={{ fontSize: 16, lineHeight: 1.55, color: '#d6d6d6', marginTop: 24, textWrap: 'pretty' }}>{p.blurb}</p>
            <div style={{ marginTop: 28 }}>
              <div style={{ fontSize: 11, color: '#a0a0a0', letterSpacing: 0.3, marginBottom: 12 }}>Skills</div>
              {p.skills.map((s, i) => (
                <div key={i} style={{ fontSize: 13, color: '#f5f5f5', padding: '8px 0', borderBottom: '1px solid rgba(255,255,255,0.12)' }}>{s}</div>
              ))}
            </div>
          </div>
        </div>

      </div>
    </div>
  );
}

function CAboutPopup({ onClose }) {
  const isMobile = useIsMobile();
  const [form, setForm] = React.useState({ name: '', email: '', message: '' });
  const [status, setStatus] = React.useState('idle');

  React.useEffect(() => {
    const k = (e) => { if (e.key === 'Escape') onClose(); };
    document.addEventListener('keydown', k);
    return () => document.removeEventListener('keydown', k);
  }, [onClose]);

  async function handleSubmit(e) {
    e.preventDefault();
    if (status === 'submitting') return;
    setStatus('submitting');
    try {
      const res = await fetch(CONTACT_ENDPOINT, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(form),
      });
      if (res.ok) {
        setStatus('success');
        setForm({ name: '', email: '', message: '' });
      } else {
        setStatus('error');
      }
    } catch {
      setStatus('error');
    }
  }

  const inputBase = {
    width: '100%', padding: '10px 12px',
    background: 'rgba(255,255,255,0.04)',
    border: '1px solid rgba(255,255,255,0.18)',
    color: '#f5f5f5',
    fontFamily: 'inherit', fontSize: 13,
    borderRadius: 2, outline: 'none',
    textTransform: 'none',
    boxSizing: 'border-box',
  };
  const labelStyle = { display: 'block', fontSize: 11, color: '#a0a0a0', letterSpacing: 0.3, marginBottom: 6 };
  const fieldWrap = { marginTop: 16 };

  return (
    <div
      onClick={onClose}
      style={{
        position: 'fixed', inset: 0, zIndex: 50,
        background: 'rgba(10,10,10,0.97)',
        backdropFilter: 'blur(20px)',
        overflow: 'auto',
        WebkitOverflowScrolling: 'touch',
        animation: 'cFadeIn 280ms ease',
      }}
    >
      <style>{`
        @keyframes cFadeIn { from { opacity: 0; } to { opacity: 1; } }
        .c-field:focus { border-color: rgba(255,255,255,0.5) !important; background: rgba(255,255,255,0.06) !important; }
      `}</style>
      <div onClick={(e) => e.stopPropagation()} style={{ padding: isMobile ? '14px 18px 48px' : '20px 32px 60px', maxWidth: 1100, margin: '0 auto' }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: 8, paddingBottom: 24, borderBottom: '1px solid rgba(255,255,255,0.12)' }}>
          <span style={{ fontSize: 11, color: '#a0a0a0', letterSpacing: 0.3 }}>
            <span style={{ color: '#f5f5f5' }}>about / contact</span>
          </span>
          <button onClick={onClose} style={{ border: 'none', background: 'transparent', cursor: 'pointer', fontFamily: 'inherit', fontSize: isMobile ? 13 : 11, color: '#f5f5f5', letterSpacing: 0.3, padding: '4px 8px', minHeight: 44, minWidth: 44 }}>
            Close ✕
          </button>
        </div>

        {/* Bio */}
        <section style={{ marginTop: isMobile ? 28 : 56, maxWidth: 720 }}>
          <h1 style={{ fontFamily: 'inherit', fontSize: isMobile ? 32 : 52, fontWeight: 400, letterSpacing: isMobile ? -0.8 : -1.4, margin: 0, lineHeight: 1.02, color: '#f5f5f5' }}>
            about
          </h1>
          <p style={{ fontSize: isMobile ? 15 : 16, lineHeight: 1.6, color: '#d6d6d6', marginTop: 28, textWrap: 'pretty' }}>
            i’m sam — a director, editor, colourist and filmmaker based in cardiff. my entire skill set is self taught and has been constantly developing since i started making gaming videos in 2013 (yes, i was one of those cringe youtubers). i particularly enjoy working on creative projects such as music, food, fashion and film; though, i will involve myself with anything that allows me to put my creative flair to good use.
          </p>
        </section>

        {/* Skills */}
        <section style={{ marginTop: isMobile ? 48 : 72 }}>
          <h2 style={{ fontFamily: 'inherit', fontSize: isMobile ? 22 : 28, fontWeight: 400, letterSpacing: -0.5, margin: 0, color: '#f5f5f5' }}>
            what i do
          </h2>
          <div style={{ marginTop: 24, display: 'grid', gridTemplateColumns: isMobile ? '1fr' : 'repeat(2, 1fr)', gap: isMobile ? 16 : 20 }}>
            {SKILLS_LIST.map((s) => (
              <div key={s.name} style={{
                border: '1px solid rgba(255,255,255,0.12)',
                background: 'rgba(255,255,255,0.02)',
                padding: isMobile ? '16px 18px' : '20px 22px',
                borderRadius: 2,
              }}>
                <h3 style={{ fontFamily: 'inherit', fontSize: 15, fontWeight: 500, color: '#f5f5f5', margin: 0, letterSpacing: -0.1 }}>
                  {s.name}
                </h3>
                <p style={{ fontSize: 13, lineHeight: 1.55, color: '#b0b0b0', margin: '8px 0 0', textWrap: 'pretty' }}>
                  {s.desc}
                </p>
              </div>
            ))}
          </div>
        </section>

        {/* Contact details */}
        <section style={{ marginTop: isMobile ? 48 : 72, maxWidth: 720 }}>
          <h2 style={{ fontFamily: 'inherit', fontSize: isMobile ? 22 : 28, fontWeight: 400, letterSpacing: -0.5, margin: 0, color: '#f5f5f5' }}>
            get in touch
          </h2>
          <div style={{ marginTop: 24, display: 'grid', gridTemplateColumns: isMobile ? '1fr' : 'repeat(3, 1fr)', gap: isMobile ? 14 : 24, fontSize: 12 }}>
            <div>
              <div style={{ color: '#a0a0a0', marginBottom: 4 }}>instagram</div>
              <a href="https://instagram.com/samharris.mov" style={{ color: '#f5f5f5', textDecoration: 'none', borderBottom: '1px solid #f5f5f5' }}>
                @samharris.mov
              </a>
            </div>
            <div>
              <div style={{ color: '#a0a0a0', marginBottom: 4 }}>email</div>
              <a href="mailto:sam@samharris.mov" style={{ color: '#f5f5f5', textDecoration: 'none', borderBottom: '1px solid #f5f5f5', textTransform: 'none' }}>
                sam@samharris.mov
              </a>
            </div>
            <div>
              <div style={{ color: '#a0a0a0', marginBottom: 4 }}>phone</div>
              <a href="tel:+447985574073" style={{ color: '#f5f5f5', textDecoration: 'none', borderBottom: '1px solid #f5f5f5' }}>
                +44 7985 574073
              </a>
            </div>
          </div>
        </section>

        {/* Contact form */}
        <section style={{ marginTop: isMobile ? 48 : 72, maxWidth: 580 }}>
          <h2 style={{ fontFamily: 'inherit', fontSize: isMobile ? 22 : 28, fontWeight: 400, letterSpacing: -0.5, margin: 0, color: '#f5f5f5' }}>
            send a message
          </h2>
          <form onSubmit={handleSubmit} style={{ marginTop: 20 }}>
            <div style={fieldWrap}>
              <label htmlFor="cf-name" style={labelStyle}>name</label>
              <input
                id="cf-name" className="c-field" type="text" required
                autoComplete="name" autoCapitalize="words" spellCheck={false}
                value={form.name}
                onChange={(e) => setForm({ ...form, name: e.target.value })}
                style={inputBase}
              />
            </div>
            <div style={fieldWrap}>
              <label htmlFor="cf-email" style={labelStyle}>email</label>
              <input
                id="cf-email" className="c-field" type="email" required
                inputMode="email" autoComplete="email"
                autoCapitalize="off" autoCorrect="off" spellCheck={false}
                value={form.email}
                onChange={(e) => setForm({ ...form, email: e.target.value })}
                style={inputBase}
              />
            </div>
            <div style={fieldWrap}>
              <label htmlFor="cf-message" style={labelStyle}>message</label>
              <textarea
                id="cf-message" className="c-field" required rows={5}
                autoCapitalize="sentences"
                value={form.message}
                onChange={(e) => setForm({ ...form, message: e.target.value })}
                style={{ ...inputBase, resize: 'vertical', minHeight: 120 }}
              />
            </div>
            <button
              type="submit"
              disabled={status === 'submitting'}
              style={{
                marginTop: 20,
                border: '1px solid rgba(255,255,255,0.45)',
                background: 'rgba(255,255,255,0.04)',
                fontFamily: 'inherit', fontSize: 13, color: '#f5f5f5',
                padding: '10px 22px', borderRadius: 2, letterSpacing: 0.3,
                cursor: status === 'submitting' ? 'not-allowed' : 'pointer',
                minHeight: 40,
                transition: 'background 180ms ease',
              }}
            >
              {status === 'submitting' ? 'sending…' : 'send message'}
            </button>
            {status === 'success' && (
              <p style={{ fontSize: 13, color: '#9bd17a', marginTop: 14 }}>
                thanks — i’ll reply soon.
              </p>
            )}
            {status === 'error' && (
              <p style={{ fontSize: 13, color: '#e08989', marginTop: 14 }}>
                something went wrong. please email me directly instead.
              </p>
            )}
          </form>
        </section>
      </div>
    </div>
  );
}

function DirectionC() {
  const [hovered, setHovered] = React.useState(null);
  const [open, setOpen] = React.useState(null);
  const [openMobile, setOpenMobile] = React.useState(null);
  const [showAbout, setShowAbout] = React.useState(false);
  const isMobile = useIsMobile();
  // The "now showing" label below the box reflects whichever column is
  // currently hovered; falls back to the featured (first) project when idle.
  const focused = C_PROJECTS[hovered != null ? hovered : 0];

  return (
    <div style={{
      width: '100%', height: '100%', overflow: 'auto',
      background: '#0a0a0a', color: '#f5f5f5',
      fontFamily: '"Roboto Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace',
      fontSize: 13, lineHeight: 1.5,
      textTransform: 'lowercase',
      position: 'relative',
    }}>
      <header style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: 6, padding: isMobile ? '14px 18px' : '20px 32px', position: 'sticky', top: 0, background: 'rgba(10,10,10,0.86)', backdropFilter: 'blur(10px)', zIndex: 10 }}>
        <span style={{ fontSize: 13, fontWeight: 500, letterSpacing: -0.1 }}>Sam Harris</span>
        <div style={{ display: 'flex', alignItems: 'center', gap: isMobile ? 12 : 18 }}>
          <span style={{ fontSize: isMobile ? 10 : 11, color: '#a0a0a0', letterSpacing: 0.2 }}>Director · Editor · Colourist · Cardiff</span>
          <button
            onClick={() => setShowAbout(true)}
            style={{
              border: '1px solid rgba(255,255,255,0.35)', background: 'transparent',
              fontFamily: 'inherit',
              fontSize: isMobile ? 11 : 12, color: '#f5f5f5',
              padding: '6px 12px', borderRadius: 2, letterSpacing: 0.3,
              cursor: 'pointer', minHeight: isMobile ? 32 : 30,
              textTransform: 'lowercase',
            }}
          >{isMobile ? 'about' : 'about / contact'}</button>
        </div>
      </header>

      {/* Hero — quiet intro */}
      <section style={{ padding: isMobile ? '40px 18px 24px' : '72px 32px 36px', maxWidth: isMobile ? '100%' : 720 }}>
        <div style={{ fontSize: 11, color: '#a0a0a0', letterSpacing: 0.3, marginBottom: 18 }}>selected work, 2025–26</div>
        <h1 style={{ fontFamily: '"Roboto Mono", ui-monospace, monospace', fontSize: isMobile ? 32 : 52, fontWeight: 400, letterSpacing: isMobile ? -0.8 : -1.4, margin: 0, lineHeight: 1.02, textWrap: 'balance', color: '#f5f5f5' }}>
          portfolio
        </h1>
      </section>

      {/* Vertical stripe row — each column is one project */}
      {isMobile ? (
        <section style={{ padding: '0 0 4px' }}>
          {C_PROJECTS.map((p, i) => (
            <CMobileCard
              key={i}
              p={p}
              expanded={openMobile === i}
              onToggle={() => setOpenMobile(openMobile === i ? null : i)}
              onOpen={() => setOpen(i)}
            />
          ))}
        </section>
      ) : (
        <section
          onMouseLeave={() => setHovered(null)}
          style={{
            height: 460,
            display: 'flex',
            padding: '0 32px',
            gap: 0,
          }}
        >
          {C_PROJECTS.map((p, i) => (
            <CColumn
              key={i}
              p={p}
              i={i}
              hovered={hovered}
              onHover={setHovered}
              onLeave={() => {}}
              onClick={() => setOpen(i)}
            />
          ))}
        </section>
      )}

      {/* Now-showing caption below the bento — updates with hover */}
      {!isMobile && (
        <div style={{
          padding: '14px 32px 0',
          display: 'flex', justifyContent: 'space-between', alignItems: 'baseline',
          fontSize: 11, color: '#a0a0a0',
        }}>
          <span>
            <span style={{ color: '#f5f5f5' }}>{focused.n} / {focused.title}</span>
            &nbsp;·&nbsp; {focused.category} · {focused.y}
          </span>
          <span>{focused.kind} · {focused.skills.join(' · ')}</span>
        </div>
      )}

      <footer style={{ padding: isMobile ? '40px 18px 36px' : '64px 32px 48px', display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: 8, fontSize: 11, color: '#6a6a6a' }}>
        <span>© 2026, Sam Harris</span>
        <button
          onClick={() => setShowAbout(true)}
          style={{
            border: 'none', background: 'transparent', cursor: 'pointer',
            fontFamily: 'inherit',
            fontSize: 11, color: '#a0a0a0', letterSpacing: 0.3,
            textTransform: 'lowercase', padding: 0,
            borderBottom: '1px solid #6a6a6a',
          }}
        >about / contact</button>
      </footer>

      {open != null && (
        <CProjectDetail p={C_PROJECTS[open]} onClose={() => setOpen(null)} />
      )}
      {showAbout && (
        <CAboutPopup onClose={() => setShowAbout(false)} />
      )}
    </div>
  );
}

window.DirectionC = DirectionC;
