﻿// ============================================================================
// pages.jsx — routes
//
// Home   : full-bleed cover image (Tiago Madaleno style)
// Sobre  : about with a short bio
// Trabalhos : categories with project lists (Clara de Cápua style)
// Obra   : title + small caption + vertical stack of images
// Exposições / Contacto / CV : clean lists
//
// No hairlines. No bold copy. Nav is the only place with bold text.
// ============================================================================

const { useState: useStateP, useMemo: useMemoP, useEffect: useEffectP } = React;

function Placeholder({ ratio, style: extra }) {
  return (
    <div style={{
      width: '100%', aspectRatio: ratio || '4/3',
      background: '#e8e5df',
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      ...extra,
    }}>
      <span className="mono" style={{ fontSize: 10, letterSpacing: '0.06em', color: '#c2bdb5' }}>
        imagem
      </span>
    </div>
  );
}

// ============================================================================
// HOME
// ============================================================================
function PageHome({ lang }) {
  const [openSection, setOpenSection] = useStateP(null);

  useEffectP(() => {
    document.body.style.overflow = 'hidden';
    return () => { document.body.style.overflow = ''; };
  }, []);

  const artisticSections = [
    {
      key: 'fotos',
      label: lang === 'pt' ? 'FOTOS' : 'PHOTOS',
      items: WORKS.filter(w => (w.categories || []).includes('fotoperformance')),
      getHref: w => `#/obra/${w.id}`,
      getLabel: w => w.title,
    },
    {
      key: 'video',
      label: 'VÍDEOS',
      items: WORKS.filter(w => (w.categories || []).includes('videoperformance')),
      getHref: w => `#/obra/${w.id}`,
      getLabel: w => w.title,
    },
    {
      key: 'happening',
      label: 'HAPPENING',
      items: WORKS.filter(w => (w.categories || []).includes('happening')),
      getHref: w => `#/obra/${w.id}`,
      getLabel: w => w.title,
    },
    {
      key: 'sons',
      label: lang === 'pt' ? 'SONS' : 'SOUNDS',
      items: SOUNDS,
      getHref: s => `#/som/${s.id}`,
      getLabel: s => s.title[lang],
    },
    {
      key: 'exposicoes',
      label: lang === 'pt' ? 'EXPOSIÇÕES' : 'EXHIBITIONS',
      items: EXHIBITIONS,
      getHref: e => `#/exposicao/${e.id}`,
      getLabel: e => e.title[lang],
    },
  ];

  const sobre    = { key: 'sobre',    label: lang === 'pt' ? 'SOBRE'    : 'ABOUT',   href: '#/sobre' };
  const contacto = { key: 'contacto', label: lang === 'pt' ? 'CONTACTO' : 'CONTACT', href: '#/contacto' };
  const cv       = { key: 'cv',       label: 'CV',                                   href: '#/cv' };

  // Order: FOTOS · VÍDEOS · HAPPENING · SONS · EXPOSIÇÕES · SOBRE · CONTACTO · CV
  const allSections = [
    artisticSections[0], // fotos
    artisticSections[1], // vídeos
    artisticSections[2], // happening
    artisticSections[3], // sons
    artisticSections[4], // exposicoes
    sobre,
    contacto,
    cv,
  ];

  return (
    <>
      <div className="home-cover" aria-hidden="false">
        <img
          src={HOME.coverImage}
          alt=""
          draggable="false"
          fetchpriority="high"
          loading="eager"
          decoding="async"
          style={{ objectPosition: HOME.coverPosition || 'center' }}
        />
      </div>

      {/* Big centered nav */}
      <div className="home-main-nav">
        <div className="home-main-nav-inner">
          {allSections.map(sec => (
            <div
              key={sec.key}
              className="home-nav-item"
              onMouseEnter={() => sec.items ? setOpenSection(sec.key) : setOpenSection(null)}
              onMouseLeave={() => setOpenSection(null)}
            >
              {sec.href ? (
                <a href={sec.href} data-cursor-hover className="home-nav-label">
                  {sec.label}
                </a>
              ) : (
                <span className={`home-nav-label home-nav-artistic${openSection === sec.key ? ' is-open' : ''}`}>
                  {sec.label}
                </span>
              )}

              {sec.items && (
                <div className={`home-nav-dropdown${openSection === sec.key ? ' is-open' : ''}`}>
                  <div className="home-nav-blur-bg" aria-hidden="true" />
                  {sec.items.map((item, i) => (
                    <a key={i} href={sec.getHref(item)} data-cursor-hover
                       className="home-nav-dropdown-item">
                      {sec.getLabel(item)}
                    </a>
                  ))}
                </div>
              )}
            </div>
          ))}
        </div>
      </div>

      {/* Name + tagline overlay at bottom-left */}
      <div className="home-overlay">
        <div className="home-overlay-inner">
          <p className="home-name">carolina leal</p>
          <div className="home-tagline">
            <span>{lang === 'pt' ? 'artista visual' : 'visual artist'}</span>
          </div>
        </div>
      </div>

      <style>{`
        .home-main-nav {
          position: fixed;
          inset: 0;
          z-index: 15;
          display: flex;
          align-items: center;
          justify-content: flex-start;
          pointer-events: none;
          animation: coverIn 1000ms cubic-bezier(.2,.6,.2,1) both;
          animation-delay: 200ms;
        }
        .home-main-nav-inner {
          display: flex;
          flex-wrap: nowrap;
          gap: 30px;
          align-items: flex-start;
          justify-content: flex-start;
          pointer-events: auto;
          transform: translateY(-40vh);
          padding: 0 60px 0 180px;
        }
        @media (max-width: 980px) {
          .home-main-nav-inner {
            justify-content: flex-start;
            padding: 0 34px 0 54px;
          }
        }
        .home-nav-item {
          position: relative;
          display: flex;
          flex-direction: column;
          align-items: center;
        }
        .home-nav-label {
          display: block;
          font-family: var(--sans);
          font-weight: 700;
          font-size: clamp(8px, 1.7vw, 14px);
          letter-spacing: 0.1em;
          text-transform: uppercase;
          color: #ffffff;
          text-shadow: 0 1px 12px rgba(0,0,0,0.7), 0 0 32px rgba(0,0,0,0.5);
          text-decoration: none;
          cursor: pointer;
          white-space: nowrap;
          user-select: none;
          padding-bottom: 2px;
          border-bottom: 1px solid transparent;
          transition: border-color 180ms ease;
        }
        .home-nav-label:hover {
          border-bottom-color: rgba(255,255,255,0.75);
        }
        .home-nav-artistic.is-open {
          border-bottom-color: rgba(255,255,255,0.75);
        }
        .home-nav-dropdown {
          position: absolute;
          top: 100%;
          left: 50%;
          transform: translateX(-50%);
          display: flex;
          flex-direction: column;
          align-items: center;
          gap: 2px;
          opacity: 0;
          pointer-events: none;
          transition: opacity 180ms ease;
          min-width: 160px;
          padding: 14px 24px 24px;
        }
        .home-nav-dropdown.is-open {
          opacity: 1;
          pointer-events: auto;
        }
        .home-nav-blur-bg {
          position: absolute;
          top: 10px; left: 4px; right: 4px; bottom: 4px;
          background: rgba(0, 0, 0, 0.20);
          filter: blur(14px);
          pointer-events: none;
          z-index: 0;
        }
        .home-nav-dropdown-item {
          position: relative;
          z-index: 1;
          font-family: var(--sans);
          font-weight: 700;
          font-size: clamp(9px, 0.9vw, 12px);
          letter-spacing: 0.09em;
          text-transform: uppercase;
          color: #ffffff;
          text-shadow: 0 1px 8px rgba(0,0,0,0.8);
          text-decoration: none;
          padding: 4px 6px;
          white-space: nowrap;
          transition: opacity 150ms ease;
        }
        .home-nav-dropdown-item:hover {
          opacity: 0.75;
        }
        @media (max-width: 720px) {
          .home-main-nav { display: none; }
        }
      `}</style>
    </>
  );
}

// ============================================================================
// SOBRE
// ============================================================================
function PageSobre({ lang }) {
  const t = T[lang];
  const paras = ABOUT[lang];
  return (
    <div className="page" style={{ paddingTop: 32 }}>
      <div className="sobre-grid">
        <img
          src={IMG_BASE + ABOUT.portraitImage}
          alt={ABOUT.portraitCaption[lang]}
          style={{ width: '100%', height: 'auto', display: 'block' }}
          loading="lazy"
        />
        <div>
          <h1 style={{
            fontFamily: 'var(--sans)',
            fontWeight: 400,
            fontSize: 'clamp(22px, 3vw, 38px)',
            lineHeight: 1.1,
            letterSpacing: '-0.02em',
            marginBottom: 36,
            textWrap: 'balance',
            color: 'var(--ink)',
            maxWidth: 720,
          }}>{t.sobre.heading}</h1>
          <div style={{
            display: 'flex', flexDirection: 'column', gap: 22,
            maxWidth: 600, fontSize: 13, lineHeight: 1.75,
            color: 'var(--ink)',
          }}>
            {paras.map((p, i) => (
              <p key={i} style={{ textWrap: 'pretty' }}>{p}</p>
            ))}
          </div>

          <div style={{
            marginTop: 56,
            display: 'grid', gridTemplateColumns: '1fr 1fr',
            gap: 32, maxWidth: 560,
          }}>
            <div>
              <div className="mono" style={{ color: 'var(--ink-3)', marginBottom: 10 }}>
                {t.sobre.bioTitle}
              </div>
              <div style={{ fontSize: 14.5, lineHeight: 1.65, color: 'var(--ink)' }}>
                {t.sobre.bio}
              </div>
            </div>
            <div>
              <div className="mono" style={{ color: 'var(--ink-3)', marginBottom: 10 }}>
                {t.sobre.contactsTitle}
              </div>
              <div style={{ fontSize: 14.5, lineHeight: 1.75, color: 'var(--ink)' }}>
                <a className="link" href={`mailto:${CONTACT.email}`}>{CONTACT.email}</a><br />
                {CONTACT.phone}
              </div>
            </div>
          </div>

          <div style={{ marginTop: 40, display: 'flex', gap: 24, fontSize: 15 }}>
            <a href="#/trabalhos" className="link" data-cursor-hover>
              {lang === 'pt' ? 'ver trabalhos' : 'see works'} {'\u2192'}
            </a>
            <a href="#/cv" className="link" data-cursor-hover>cv {'\u2192'}</a>
          </div>
        </div>
      </div>

      <style>{`
        .sobre-grid {
          display: grid;
          grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr);
          gap: 80px;
          align-items: start;
        }
        @media (max-width: 820px) {
          .sobre-grid { grid-template-columns: 1fr; gap: 32px; }
        }
        @media (max-width: 480px) {
          .sobre-grid { gap: 24px; }
        }
      `}</style>
    </div>
  );
}

// ============================================================================
// TRABALHOS — Clara de Cápua style: categories as plain headings, projects
// listed underneath as plain links.
// ============================================================================
function PageTrabalhos({ lang, category }) {
  const t = T[lang];

  const groups = useMemoP(() => {
    if (category) {
      const cat = CATEGORIES.find(c => c.id === category);
      if (!cat) return [];
      return [{ cat, works: WORKS.filter(w => (w.categories || []).includes(cat.id)) }];
    }
    return CATEGORIES.map(cat => ({
      cat,
      works: WORKS.filter(w => (w.categories || []).includes(cat.id)),
    }));
  }, [category]);

  return (
    <div className="page" style={{ paddingTop: 32 }}>
      <h1 style={{
        fontFamily: 'var(--sans)',
        fontWeight: 400,
        fontSize: 'clamp(22px, 3vw, 36px)',
        letterSpacing: '-0.02em',
        marginBottom: 48,
        color: 'var(--ink)',
      }}>
        {category
          ? (groups[0]?.cat?.[lang] || t.nav.trabalhos).toLowerCase()
          : t.nav.trabalhos.toLowerCase()}
      </h1>

      <div style={{ display: 'flex', flexDirection: 'column', gap: 48 }}>
        {groups.map((g) => (
          <WorksGroup key={g.cat.id} group={g} lang={lang} showHeading={!category} />
        ))}
      </div>
    </div>
  );
}

function WorksGroup({ group, lang, showHeading }) {
  if (!group.works.length) return null;
  return (
    <section style={{ marginBottom: 56 }}>
      {showHeading && (
        <div className="mono" style={{
          color: 'var(--ink-3)',
          fontSize: 11,
          letterSpacing: '0.08em',
          textTransform: 'uppercase',
          paddingBottom: 16,
          marginBottom: 0,
          borderBottom: '1px solid rgba(26,26,26,0.1)',
        }}>
          {group.cat[lang]}
        </div>
      )}
      <ul>
        {group.works.map((w) => {
          const catLabels = (w.categories || [])
            .map(id => (CATEGORIES.find(c => c.id === id) || {})[lang])
            .filter(Boolean).join(' · ');
          return (
            <li key={w.id} style={{ borderBottom: '1px solid rgba(26,26,26,0.08)' }}>
              <a href={`#/obra/${w.id}`} data-cursor-hover className="works-row">
                <span className="mono" style={{ color: 'var(--ink-3)', minWidth: '3ch', flexShrink: 0 }}>
                  {w.number}
                </span>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{
                    fontFamily: 'var(--sans)', fontWeight: 500,
                    fontSize: 'clamp(13px, 1.7vw, 17px)',
                    letterSpacing: '-0.01em', lineHeight: 1.2,
                    color: 'var(--ink)',
                  }}>
                    {w.title.toLowerCase()}
                  </div>
                  {w.subtitle && (
                    <div style={{ fontSize: 13, color: 'var(--ink-3)', marginTop: 4, fontStyle: 'italic' }}>
                      {w.subtitle[lang]}
                    </div>
                  )}
                </div>
                <div style={{ textAlign: 'right', flexShrink: 0 }}>
                  <div className="mono" style={{ color: 'var(--ink-2)', fontSize: 12 }}>{w.year}</div>
                </div>
              </a>
            </li>
          );
        })}
      </ul>
      <style>{`
        .works-row {
          display: flex; align-items: center; gap: 24px;
          padding: 20px 0; text-decoration: none;
          transition: opacity 200ms ease;
        }
        .works-row:hover { opacity: 0.6; }
        @media (max-width: 600px) {
          .works-row { gap: 14px; }
        }
      `}</style>
    </section>
  );
}

// ============================================================================
// OBRA — title + caption + vertical stack of images, with description after.
// One special case: the TV interaction for "corpo-transparente".
// ============================================================================
function PageObra({ lang, id }) {
  const t = T[lang];
  const work = WORKS.find(w => w.id === id);
  if (!work) {
    return (
      <div className="page" style={{ paddingTop: 32 }}>
        <p style={{ color: 'var(--ink-2)' }}>
          {lang === 'pt' ? 'Obra não encontrada.' : 'Work not found.'}
        </p>
        <a href="#/trabalhos" className="link" data-cursor-hover
           style={{ marginTop: 24, display: 'inline-block' }}>
          {'\u2190'} {t.works.back}
        </a>
      </div>
    );
  }

  const primaryCat = (work.categories || [])[0];
  const cat = CATEGORIES.find(c => c.id === primaryCat);
  const idx = WORKS.findIndex(w => w.id === work.id);
  const prev = idx > 0 ? WORKS[idx - 1] : null;
  const next = idx < WORKS.length - 1 ? WORKS[idx + 1] : null;

  const catLabels = (work.categories || [])
    .map(id => (CATEGORIES.find(c2 => c2.id === id) || {})[lang])
    .filter(Boolean)
    .join(' · ');

  return (
    <div className="page" style={{ paddingTop: 24 }}>
      {/* Breadcrumb */}
      <div className="mono" style={{ color: 'var(--ink-3)', display: 'flex', gap: 14, flexWrap: 'wrap' }}>
        <a href="#/trabalhos" data-cursor-hover className="link">{t.nav.trabalhos.toLowerCase()}</a>
        {cat && (
          <>
            <span>/</span>
            <a href={`#/trabalhos/${cat.id}`} data-cursor-hover className="link">
              {cat[lang].toLowerCase()}
            </a>
          </>
        )}
      </div>

      {/* Header */}
      <header style={{ marginTop: 28, marginBottom: 40 }}>
        <h1 style={{
          fontFamily: 'var(--sans)',
          fontWeight: 400,
          fontSize: 'clamp(23px, 3.5vw, 44px)',
          letterSpacing: '-0.025em',
          lineHeight: 1.05,
          textWrap: 'balance',
          color: 'var(--ink)',
          marginBottom: 14,
        }}>{work.title.toLowerCase()}</h1>
        <div style={{
          display: 'flex', flexWrap: 'wrap', gap: 18, alignItems: 'baseline',
          color: 'var(--ink-2)',
        }}>
          <span className="mono">{work.year}</span>
          {catLabels && <span className="mono" style={{ color: 'var(--ink-3)' }}>{catLabels}</span>}
        </div>
        {work.subtitle && (
          <div style={{
            marginTop: 12,
            fontFamily: 'var(--sans)',
            fontStyle: 'italic',
            fontSize: 14,
            color: 'var(--ink-2)',
            maxWidth: 720,
          }}>{work.subtitle[lang]}</div>
        )}
      </header>

      {/* Body */}
      <ObraBody work={work} lang={lang} />

      {/* Description + ficha técnica (Clara-style: simple text block, no boxes) */}
      <div className="obra-text" style={{ marginTop: 56 }}>
        <p style={{
          fontFamily: 'var(--sans)',
          fontSize: 13, lineHeight: 1.75,
          color: 'var(--ink)',
          maxWidth: 720,
          whiteSpace: 'pre-line',
          textWrap: 'pretty',
        }}>
          {work.description[lang]}
        </p>
        <dl className="obra-meta">
          {work.medium &&    <MetaRow label={t.works.medium}    value={work.medium[lang]} />}
          {work.duration &&  <MetaRow label={t.works.duration}  value={work.duration[lang]} />}
          {work.location &&  <MetaRow label={t.works.location}  value={work.location[lang]} />}
          {work.materials && <MetaRow label={t.works.materials} value={work.materials[lang]} />}
          {work.credits &&   <MetaRow label={t.works.credits}   value={work.credits[lang]} />}
          {work.mapUrl && (
            <MetaRow
              label={lang === 'pt' ? 'Mapa' : 'Map'}
              value={(
                <a href={work.mapUrl} target="_blank" rel="noreferrer noopener" className="link" data-cursor-hover
                   style={{ color: 'var(--ink)', display: 'inline-flex', alignItems: 'center', gap: 6 }}>
                  <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
                    <path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/>
                    <circle cx="12" cy="10" r="3"/>
                  </svg>
                  {lang === 'pt' ? 'Mapa interativo' : 'Interactive map'}
                </a>
              )}
            />
          )}
          {work.youtubeUrl && (
            <MetaRow
              label={lang === 'pt' ? 'Vídeos' : 'Videos'}
              value={(
                <a href={work.youtubeUrl} target="_blank" rel="noreferrer noopener" className="link" data-cursor-hover
                   style={{ color: 'var(--ink)', display: 'inline-flex', alignItems: 'center', gap: 6 }}>
                  <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
                    <polygon points="5 3 19 12 5 21 5 3"/>
                  </svg>
                  {lang === 'pt' ? 'Ver playlist no YouTube' : 'Watch playlist on YouTube'}
                </a>
              )}
            />
          )}
        </dl>
      </div>

      {/* Prev / next — text only, no rows or boxes */}
      <div style={{
        marginTop: 80,
        display: 'flex', justifyContent: 'space-between', flexWrap: 'wrap',
        gap: 24,
      }}>
        <div>
          {prev && (
            <a href={`#/obra/${prev.id}`} data-cursor-hover className="link"
               style={{ color: 'var(--ink-2)' }}>
              {'\u2190'} {prev.title.toLowerCase()}
            </a>
          )}
        </div>
        <div>
          {next && (
            <a href={`#/obra/${next.id}`} data-cursor-hover className="link"
               style={{ color: 'var(--ink-2)' }}>
              {next.title.toLowerCase()} {'\u2192'}
            </a>
          )}
        </div>
      </div>

      <style>{`
        .obra-text {
          display: grid;
          grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr);
          gap: 56px;
          align-items: start;
        }
        .obra-meta {
          margin: 0;
          display: flex; flex-direction: column; gap: 18px;
        }
        @media (max-width: 820px) {
          .obra-text { grid-template-columns: 1fr; gap: 32px; }
        }
        @media (max-width: 480px) {
          .obra-text { gap: 24px; }
        }
      `}</style>
    </div>
  );
}

function MetaRow({ label, value }) {
  return (
    <div>
      <dt className="mono" style={{ color: 'var(--ink-3)', marginBottom: 4 }}>
        {label.toLowerCase()}
      </dt>
      <dd style={{ margin: 0, fontSize: 14.5, lineHeight: 1.55, color: 'var(--ink)' }}>{value}</dd>
    </div>
  );
}

function getVimeoId(src) {
  if (!src) return null;
  const m = src.match(/vimeo\.com\/(?:video\/)?(\d+)/);
  return m ? m[1] : null;
}

function VideoBlock({ src }) {
  const containerRef = React.useRef(null);
  const [shouldLoad, setShouldLoad] = React.useState(false);
  const vimeoId = getVimeoId(src);

  React.useEffect(() => {
    const el = containerRef.current;
    if (!el) return;
    if (!('IntersectionObserver' in window)) { setShouldLoad(true); return; }
    const observer = new IntersectionObserver(
      ([entry]) => { if (entry.isIntersecting) { setShouldLoad(true); observer.disconnect(); } },
      { rootMargin: '400px 0px' }
    );
    observer.observe(el);
    return () => observer.disconnect();
  }, []);

  return (
    <div ref={containerRef} style={{ width: '100%', background: '#0a0a0a', minHeight: 220, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
      {shouldLoad ? (
        vimeoId ? (
          <div style={{ width: '100%', aspectRatio: '16/9' }}>
            <iframe
              src={`https://player.vimeo.com/video/${vimeoId}?title=0&byline=0&portrait=0&badge=0&autopause=0&player_id=0&app_id=58479`}
              style={{ width: '100%', height: '100%', border: 'none', display: 'block' }}
              allow="autoplay; fullscreen; picture-in-picture; clipboard-write; encrypted-media; web-share"
              referrerPolicy="strict-origin-when-cross-origin"
              allowFullScreen
            />
          </div>
        ) : (
          <video
            controls
            playsInline
            preload="metadata"
            controlsList="nodownload"
            style={{ width: '100%', maxWidth: '100%', maxHeight: '72vh', height: 'auto', objectFit: 'contain', display: 'block', background: '#000' }}
          >
            <source src={src} type="video/mp4" />
          </video>
        )
      ) : (
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', width: '100%', minHeight: 220 }}>
          <div style={{ width: 44, height: 44, border: '1px solid rgba(255,255,255,0.18)', borderRadius: '50%', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
            <div style={{ width: 0, height: 0, borderStyle: 'solid', borderWidth: '8px 0 8px 15px', borderColor: 'transparent transparent transparent rgba(255,255,255,0.35)', marginLeft: 4 }} />
          </div>
        </div>
      )}
    </div>
  );
}

function SoundVisual({ id, title, size = 'large' }) {
  const variants = {
    'ecos-corporais': [4, 8, 5, 9, 3, 7, 6, 5, 9, 2, 6],
    'ciclo': [6, 3, 7, 4, 8, 5, 8, 3, 6, 9, 4],
    'a-maquina-viva': [8, 4, 9, 5, 10, 6, 8, 4, 7, 5, 9],
  };
  const bars = variants[id] || [5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5];
  const height = size === 'small' ? 110 : 220;
  return (
    <div style={{
      display: 'flex', flexDirection: 'column', gap: 16,
      background: 'rgba(26,26,26,0.04)', padding: 18,
      borderRadius: 14, minHeight: size === 'small' ? 130 : 260,
    }}>
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(11, 1fr)', gap: 6, alignItems: 'end', minHeight: height }}>
        {bars.map((bar, i) => (
          <div key={i} style={{ height: `${bar * (size === 'small' ? 8 : 10)}px`, background: 'var(--ink)', borderRadius: 999 }} />
        ))}
      </div>
      <div style={{
        fontFamily: 'var(--sans)', fontSize: size === 'small' ? 12 : 14,
        color: 'var(--ink-2)', textTransform: 'uppercase', letterSpacing: '0.08em',
      }}>
        {title}
      </div>
    </div>
  );
}

function Slider({ items, renderItem, lang }) {
  const [idx, setIdx] = useStateP(0);
  const total = items.length;
  if (total === 0) return null;
  const prev = () => setIdx(i => (i - 1 + total) % total);
  const next = () => setIdx(i => (i + 1) % total);
  return (
    <div className="slider-root">
      <div className="slider-stage">
        {renderItem(items[idx], idx)}
        {total > 1 && (
          <>
            <button className="slider-arrow slider-prev" onClick={prev} aria-label={lang === 'pt' ? 'anterior' : 'previous'}>‹</button>
            <button className="slider-arrow slider-next" onClick={next} aria-label={lang === 'pt' ? 'seguinte' : 'next'}>›</button>
          </>
        )}
      </div>
      {total > 1 && (
        <div className="mono" style={{ color: 'var(--ink-3)', fontSize: 11, textAlign: 'center', marginTop: 10, letterSpacing: '0.06em' }}>
          {idx + 1} / {total}
        </div>
      )}
      <style>{`
        .slider-root { position: relative; }
        .slider-stage { position: relative; }
        .slider-arrow {
          position: absolute; top: 50%; transform: translateY(-50%);
          background: rgba(255,255,255,0.88);
          border: 1px solid rgba(26,26,26,0.14);
          color: var(--ink);
          width: 36px; height: 36px; border-radius: 50%;
          font-size: 20px; cursor: pointer;
          display: flex; align-items: center; justify-content: center;
          z-index: 3; transition: background 180ms; padding: 0; line-height: 1;
        }
        .slider-arrow:hover { background: #fff; }
        .slider-prev { left: 10px; }
        .slider-next { right: 10px; }
      `}</style>
    </div>
  );
}

function ObraBody({ work, lang }) {
  const hasImages = work.images && work.images.length > 0;
  const hasVideos = work.videos && work.videos.length > 0;
  if (!hasImages && !hasVideos) {
    return (
      <div style={{
        padding: '32px 0', color: 'var(--ink-2)', fontStyle: 'italic',
        fontSize: 'clamp(20px, 2.4vw, 28px)',
        maxWidth: 720, textWrap: 'pretty',
      }}>
        {lang === 'pt'
          ? 'imagens em produção. documentação disponível em breve.'
          : 'images in production. documentation available soon.'}
      </div>
    );
  }

  if (work.id === 'tudo-e-nada' && hasImages) {
    const imgs = work.images.slice(0, 3);

    return (
      <div style={{ display: 'flex', flexDirection: 'column', gap: 18 }}>
        <div className="tudo-e-nada-grid">
          {imgs.map((img, i) => (
            <img
              key={i}
              src={IMG_BASE + img}
              alt=""
              style={{ width: '100%', height: '100%', display: 'block', objectFit: 'cover' }}
              loading="lazy"
            />
          ))}
        </div>

        {hasVideos && work.videos[0] && (
          <div className="tudo-video">
            <VideoBlock src={work.videos[0]} />
          </div>
        )}

        <style>{`
          .tudo-e-nada-grid {
            display: grid;
            grid-template-columns: 1fr 1fr 1fr;
            grid-auto-rows: clamp(200px, 26vw, 440px);
            gap: 0;
          }
          .tudo-e-nada-grid img {
            width: 100%;
            height: 100%;
            object-fit: cover;
            display: block;
          }
          .tudo-video {
            margin-top: 8px;
          }
          @media (max-width: 840px) {
            .tudo-e-nada-grid { grid-template-columns: 1fr; grid-auto-rows: auto; }
            .tudo-e-nada-grid img { height: auto; object-fit: contain; }
            .tudo-video video { width: 100% !important; }
          }
        `}</style>
      </div>
    );
  }

  // --- limites-corporais: two series, each a slider ---
  if (work.id === 'limites-corporais' && work.series) {
    return (
      <div style={{ display: 'flex', flexDirection: 'column', gap: 48 }}>
        {work.series.map((serie, si) => (
          <div key={si}>
            <div className="mono" style={{ color: 'var(--ink-3)', marginBottom: 14, fontSize: 11, letterSpacing: '0.08em', textTransform: 'uppercase' }}>
              {serie.label[lang]}
            </div>
            <Slider lang={lang} items={serie.images} renderItem={(img) => (
              <img src={IMG_BASE + img} alt="" loading="lazy"
                style={{ width: '100%', maxHeight: '76vh', objectFit: 'contain', display: 'block', background: 'transparent' }} />
            )} />
          </div>
        ))}
      </div>
    );
  }

  // --- ruido-quotidiano: 2 stacked images left (mapa+mapa explicar), tall cartaz right ---
  if (work.id === 'ruido-quotidiano') {
    const [cartaz, mapa, mapaExplicar] = hasImages ? work.images : [];
    return (
      <div className="ruido-layout">
        <div className="ruido-left-col">
          {mapaExplicar && <img src={IMG_BASE + mapaExplicar} alt="" loading="lazy" className="ruido-left-img" />}
          {cartaz && (work.cartazUrl ? (
            <a href={work.cartazUrl} target="_blank" rel="noreferrer noopener" data-cursor-hover style={{ display: 'block' }}>
              <img src={IMG_BASE + cartaz} alt="" loading="lazy" className="ruido-left-img" style={{ transition: 'opacity 200ms' }}
                onMouseEnter={e => e.currentTarget.style.opacity = '0.82'}
                onMouseLeave={e => e.currentTarget.style.opacity = '1'} />
            </a>
          ) : (
            <img src={IMG_BASE + cartaz} alt="" loading="lazy" className="ruido-left-img" />
          ))}
        </div>
        <div className="ruido-right-col">
          {mapa && (
            <div className="ruido-cartaz-frame">
              <img src={IMG_BASE + mapa} alt="" loading="lazy" />
            </div>
          )}
        </div>
        <style>{`
          .ruido-layout {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 8px;
            align-items: stretch;
          }
          .ruido-left-col { display: flex; flex-direction: column; gap: 8px; }
          .ruido-left-img { width: 100%; aspect-ratio: 3/2; object-fit: cover; display: block; }
          .ruido-right-col { display: flex; flex-direction: column; }
          .ruido-cartaz-frame { flex: 1; display: flex; flex-direction: column; overflow: hidden; }
          .ruido-cartaz-a { flex: 1; display: flex; flex-direction: column; }
          .ruido-cartaz-frame img { width: 100%; flex: 1; object-fit: cover; display: block; }
          .ruido-cartaz-a:hover img { opacity: 0.82; transition: opacity 200ms; }
          @media (max-width: 600px) { .ruido-layout { grid-template-columns: 1fr; } }
        `}</style>
      </div>
    );
  }

  // --- corpo-residuo: slider (video first slide, then photos) ---
  if (work.id === 'corpo-residuo') {
    const sliderItems = [
      ...(hasVideos ? work.videos.map(v => ({ type: 'video', src: v })) : []),
      ...(hasImages ? work.images.map(img => ({ type: 'image', src: img })) : []),
    ];
    return (
      <Slider lang={lang} items={sliderItems} renderItem={(item) => (
        item.type === 'video' ? (
          <VideoBlock src={item.src} />
        ) : (
          <img src={IMG_BASE + item.src} alt="" loading="lazy"
            style={{ width: '100%', maxHeight: '76vh', objectFit: 'contain', display: 'block', background: 'transparent' }} />
        )
      )} />
    );
  }

  // --- entre-est-antes: 2×2 grid → 3 flush horizontal → remaining → 2 videos side by side ---
  if (work.id === 'entre-est-antes') {
    const photos = hasImages ? work.images : [];
    const g1 = photos.slice(0, 4);
    const g2 = photos.slice(4, 7);
    const g3 = photos.slice(7);
    const vids = hasVideos ? work.videos : [];
    return (
      <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
        {g1.length > 0 && (
          <div className="entre-2x2">
            {g1.map((img, i) => <img key={i} src={IMG_BASE + img} alt="" loading="lazy" />)}
          </div>
        )}
        {g2.length > 0 && (
          <div className="entre-flush3">
            {g2.map((img, i) => <img key={i} src={IMG_BASE + img} alt="" loading="lazy" />)}
          </div>
        )}
        {g3.length > 0 && (
          <div className="img-small-grid">
            {g3.map((img, i) => <img key={i} src={IMG_BASE + img} alt="" className="img-small" loading="lazy" />)}
          </div>
        )}
        {vids.length > 0 && (
          <div className="entre-vids">
            {vids.map((v, i) => <div key={i} style={{ flex: 1, minWidth: 0 }}><VideoBlock src={v} /></div>)}
          </div>
        )}
        <style>{`
          .entre-2x2 { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
          .entre-2x2 img { width: 100%; aspect-ratio: 4/3; object-fit: cover; display: block; }
          .entre-flush3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 0; }
          .entre-flush3 img { width: 100%; aspect-ratio: 3/4; object-fit: cover; display: block; height: 100%; }
          .entre-vids { display: flex; gap: 8px; }
          @media (max-width: 720px) {
            .entre-2x2 { grid-template-columns: 1fr; }
            .entre-flush3 { grid-template-columns: 1fr; }
            .entre-flush3 img { aspect-ratio: 4/3; }
            .entre-vids { flex-direction: column; }
          }
        `}</style>
      </div>
    );
  }

  const [firstImg, secondImg, thirdImg, ...restImgs] = hasImages ? work.images : [];

  const imagesEl = hasImages ? (
    <div className="obra-images">
      {firstImg && (
        <img src={IMG_BASE + firstImg} alt="" className="img-large" loading="lazy" />
      )}
      {(secondImg || thirdImg) && (
        <div className="img-medium-row">
          {secondImg && <img src={IMG_BASE + secondImg} alt="" className="img-medium" loading="lazy" />}
          {thirdImg  && <img src={IMG_BASE + thirdImg}  alt="" className="img-medium" loading="lazy" />}
        </div>
      )}
      {restImgs.length > 0 && (
        <div className="img-small-grid">
          {restImgs.map((img, i) => (
            <img key={i} src={IMG_BASE + img} alt="" className="img-small" loading="lazy" />
          ))}
        </div>
      )}
    </div>
  ) : null;

  const videosEl = hasVideos ? work.videos.map((v, i) => <VideoBlock key={i} src={v} />) : null;
  const imagesFirst = work.id === 'num-so';

    return (
      <div style={{ display: 'flex', flexDirection: 'column', gap: 18 }}>
        {imagesFirst ? imagesEl : videosEl}
        {imagesFirst ? videosEl : imagesEl}

        <style>{`
          .obra-images { display: flex; flex-direction: column; gap: 16px; }
          .img-large, .img-medium, .img-small { width: 100%; max-width: 100%; display: block; height: auto; max-height: calc(85vh - 140px); object-fit: contain; }
          .img-medium-row { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
          .img-medium-row > img:nth-child(1):nth-last-child(1) { grid-column: 1 / -1; }
          .img-small-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 10px; }

          .tudo-e-nada-grid img { width: 100%; max-width: 100%; height: 100%; object-fit: cover; display: block; }

          @media (max-width: 980px) {
            .img-medium-row { grid-template-columns: 1fr; }
            .img-small-grid { grid-template-columns: 1fr; }
            .tudo-e-nada-grid { grid-template-columns: 1fr; }
            .tudo-video video { width: 100% !important; }
          }
        `}</style>
      </div>
    );
}

// ============================================================================
// EXPOSIÇÕES — text list grouped by year, Clara-style (no boxes)
// ============================================================================
function PageExposicoes({ lang }) {
  const t = T[lang];
  const grouped = useMemoP(() => {
    const m = new Map();
    EXHIBITIONS.forEach(e => {
      if (!m.has(e.year)) m.set(e.year, []);
      m.get(e.year).push(e);
    });
    return Array.from(m.entries()).sort((a, b) => b[0].localeCompare(a[0]));
  }, []);

  return (
    <div className="page" style={{ paddingTop: 32 }}>
      <h1 style={{
        fontFamily: 'var(--sans)',
        fontWeight: 400,
        fontSize: 'clamp(22px, 3vw, 36px)',
        letterSpacing: '-0.02em',
        marginBottom: 48,
        color: 'var(--ink)',
      }}>{t.expos.title.toLowerCase()}</h1>

      <div style={{ display: 'flex', flexDirection: 'column', gap: 40 }}>
        {grouped.map(([year, items]) => (
          <section key={year} className="expo-section">
            <h2 className="mono" style={{ color: 'var(--ink-2)' }}>{year}</h2>
            <ul style={{ display: 'flex', flexDirection: 'column', gap: 22 }}>
              {items.map((it, i) => (
                <li key={i}>
                  <a href={`#/exposicao/${it.id}`} data-cursor-hover className="expo-item">
                    <div style={{ flex: 1 }}>
                      <div style={{
                        fontFamily: 'var(--sans)', fontWeight: 500,
                        fontSize: 'clamp(14px, 1.7vw, 18px)',
                        letterSpacing: '-0.005em', lineHeight: 1.25,
                        color: 'var(--ink)',
                      }}>{it.title[lang].toLowerCase()}</div>
                      {it.venue && (
                        <div style={{ color: 'var(--ink-2)', marginTop: 4, fontSize: 14 }}>
                          {it.venue}
                        </div>
                      )}
                      {it.note && (
                        <div style={{ fontStyle: 'italic', color: 'var(--ink-3)', fontSize: 13.5, marginTop: 2 }}>
                          {it.note[lang]}
                        </div>
                      )}
                    </div>
                    <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-end', gap: 4, flexShrink: 0 }}>
                      <span className="mono" style={{ color: 'var(--ink-3)' }}>{it.role[lang]}</span>
                      <span className="mono" style={{ color: 'var(--ink-3)', opacity: 0.5 }}>→</span>
                    </div>
                  </a>
                </li>
              ))}
            </ul>
          </section>
        ))}
      </div>

      <style>{`
        .expo-section {
          display: grid;
          grid-template-columns: 100px 1fr;
          gap: 32px;
          align-items: start;
        }
        @media (max-width: 820px) {
          .expo-section { grid-template-columns: 1fr; gap: 10px; }
        }
        .expo-item {
          display: flex; align-items: baseline; justify-content: space-between;
          gap: 20; padding: 16px 0;
          text-decoration: none;
          border-bottom: 1px solid rgba(26,26,26,0.07);
          transition: opacity 200ms ease;
        }
        .expo-item:hover { opacity: 0.6; }
      `}</style>
    </div>
  );
}

// ============================================================================
// CONTACTO — clean list, no boxes
// ============================================================================
function PageContacto({ lang }) {
  const t = T[lang];
  return (
    <div className="page" style={{ paddingTop: 32 }}>
      <h1 style={{
        fontFamily: 'var(--sans)',
        fontWeight: 400,
        fontSize: 'clamp(22px, 3vw, 36px)',
        letterSpacing: '-0.02em',
        marginBottom: 48,
        color: 'var(--ink)',
      }}>{t.contact.title.toLowerCase()}</h1>

      <div className="contact-grid">
        <p style={{
          fontFamily: 'var(--sans)',
          fontSize: 'clamp(17px, 2vw, 21px)',
          lineHeight: 1.5,
          color: 'var(--ink)',
          maxWidth: 460,
          textWrap: 'pretty',
        }}>
          {lang === 'pt'
            ? 'disponível para colaborações, residências, exposições e publicações. respondo a todas as mensagens.'
            : 'available for collaborations, residencies, exhibitions and publications. i reply to every message.'}
        </p>

        <dl style={{ display: 'flex', flexDirection: 'column', gap: 22, margin: 0 }}>
          <ContactRow label={t.contact.email}>
            <a className="link" data-cursor-hover href={`mailto:${CONTACT.email}`}>{CONTACT.email}</a>
          </ContactRow>
          <ContactRow label={t.contact.phone}>
            <a className="link" data-cursor-hover href={`tel:${CONTACT.phone.replace(/\s/g, '')}`}>{CONTACT.phone}</a>
          </ContactRow>
          <ContactRow label="Instagram">
            <a className="link" data-cursor-hover href={CONTACT.instagram} target="_blank" rel="noreferrer">
              {CONTACT.instagramHandle}
            </a>
          </ContactRow>
          <ContactRow label="LinkedIn">
            <a className="link" data-cursor-hover href={CONTACT.linkedin} target="_blank" rel="noreferrer">
              {CONTACT.linkedinHandle}
            </a>
          </ContactRow>
          <ContactRow label={t.contact.workingLang}>
            <span>{CONTACT.workingLanguages[lang]}</span>
          </ContactRow>
          <ContactRow label={t.contact.cv}>
            <a className="link" data-cursor-hover href={CONTACT.cv} download>{t.cv.download.toLowerCase()}</a>
          </ContactRow>
        </dl>
      </div>

      <style>{`
        .contact-grid {
          display: grid;
          grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr);
          gap: 64px;
          align-items: start;
        }
        @media (max-width: 820px) {
          .contact-grid { grid-template-columns: 1fr; gap: 32px; }
        }
      `}</style>
    </div>
  );
}

function ContactRow({ label, children }) {
  return (
    <div style={{
      display: 'grid',
      gridTemplateColumns: '120px 1fr',
      gap: 24,
      alignItems: 'baseline',
    }} className="contact-row">
      <dt className="mono" style={{ color: 'var(--ink-3)' }}>{label.toLowerCase()}</dt>
      <dd style={{
        margin: 0,
        fontFamily: 'var(--sans)',
        fontSize: 'clamp(17px, 1.8vw, 19px)',
        lineHeight: 1.5,
        color: 'var(--ink)',
      }}>{children}</dd>
      <style>{`
        @media (max-width: 540px) {
          .contact-row { grid-template-columns: 1fr; gap: 4px; }
        }
      `}</style>
    </div>
  );
}

// ============================================================================
// CV
// ============================================================================
function PageCV({ lang }) {
  const t = T[lang];
  return (
    <div className="page" style={{ paddingTop: 32 }}>
      <div style={{
        display: 'flex', justifyContent: 'space-between',
        alignItems: 'baseline', flexWrap: 'wrap',
        gap: 18, marginBottom: 48,
      }}>
        <h1 style={{
          fontFamily: 'var(--sans)',
          fontWeight: 400,
          fontSize: 'clamp(22px, 3vw, 36px)',
          letterSpacing: '-0.02em',
          color: 'var(--ink)',
        }}>{t.cv.title.toLowerCase()}</h1>
        <a href={CONTACT.cv} download className="link" data-cursor-hover
           style={{ color: 'var(--ink)', fontSize: 15 }}>
          {t.cv.download.toLowerCase()} {'\u2193'}
        </a>
      </div>

      <div style={{ display: 'flex', flexDirection: 'column', gap: 28 }}>
        <CVLine label={lang === 'pt' ? 'nome' : 'name'} value="carolina leal" />
        <CVLine label={lang === 'pt' ? 'nascimento' : 'born'} value="2004, paredes, portugal" />
        <CVLine label={lang === 'pt' ? 'vive em' : 'lives in'} value={CONTACT.location[lang].toLowerCase()} />
      </div>

      <div style={{ marginTop: 56, display: 'flex', flexDirection: 'column', gap: 40 }}>
        {CV.map((sec, i) => (
          <section key={i} className="cv-section">
            <h2 className="mono" style={{ color: 'var(--ink-2)' }}>{sec.section[lang].toLowerCase()}</h2>
            <ul style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
              {sec.items.map((it, j) => (
                <li key={j} className="cv-item">
                  <span className="mono" style={{ color: 'var(--ink-3)' }}>{it.date || ''}</span>
                  <span style={{ fontSize: 15.5, lineHeight: 1.55, color: 'var(--ink)' }}>{it[lang]}</span>
                </li>
              ))}
            </ul>
          </section>
        ))}
      </div>

      <div style={{ marginTop: 56, color: 'var(--ink-3)' }}>
        <span className="mono">{t.cv.updated}</span>
      </div>

      <style>{`
        .cv-section {
          display: grid;
          grid-template-columns: 220px 1fr;
          gap: 32px;
          align-items: start;
        }
        .cv-item {
          display: grid;
          grid-template-columns: 140px 1fr;
          gap: 24px;
          align-items: baseline;
        }
        @media (max-width: 820px) {
          .cv-section { grid-template-columns: 1fr; gap: 12px; }
          .cv-item { grid-template-columns: 80px 1fr; gap: 16px; }
        }
        @media (max-width: 480px) {
          .cv-item { grid-template-columns: 1fr; gap: 2px; }
        }
      `}</style>
    </div>
  );
}

function CVLine({ label, value }) {
  return (
    <div style={{
      display: 'grid', gridTemplateColumns: '220px 1fr', gap: 32,
      alignItems: 'baseline',
    }} className="cv-line">
      <span className="mono" style={{ color: 'var(--ink-3)' }}>{label}</span>
      <span style={{ fontSize: 17, color: 'var(--ink)' }}>{value}</span>
      <style>{`
        @media (max-width: 720px) {
          .cv-line { grid-template-columns: 1fr; gap: 2px; }
        }
      `}</style>
    </div>
  );
}

// ============================================================================
// EXPOSIÇÃO INDIVIDUAL
// ============================================================================
function PageExposicao({ lang, id }) {
  const t = T[lang];
  const expo = EXHIBITIONS.find(e => e.id === id);
  if (!expo) {
    return (
      <div className="page" style={{ paddingTop: 32 }}>
        <p style={{ color: 'var(--ink-2)' }}>
          {lang === 'pt' ? 'Exposição não encontrada.' : 'Exhibition not found.'}
        </p>
      </div>
    );
  }

  const expoImages = Array.isArray(expo.images) ? expo.images : [];
  const [firstExpImg, secondExpImg, thirdExpImg, ...restExpImgs] = expoImages;

  return (
    <div className="page" style={{ paddingTop: 24 }}>
      {/* Breadcrumb */}
      <div className="mono" style={{ color: 'var(--ink-3)', marginBottom: 28 }}>
        <a href="#/exposicoes" data-cursor-hover className="link">
          {t.expos.title.toLowerCase()}
        </a>
        <span style={{ margin: '0 10px' }}>/</span>
        <span>{expo.year}</span>
      </div>

      {/* Header */}
      <header style={{ marginBottom: 48 }}>
        <h1 style={{
          fontFamily: 'var(--sans)', fontWeight: 400,
          fontSize: 'clamp(22px, 3.4vw, 44px)',
          letterSpacing: '-0.025em', lineHeight: 1.05,
          color: 'var(--ink)', marginBottom: 16,
          textWrap: 'balance',
        }}>{expo.title[lang].toLowerCase()}</h1>
        <div style={{ display: 'flex', flexWrap: 'wrap', gap: '8px 24px', alignItems: 'baseline' }}>
          <span className="mono" style={{ color: 'var(--ink-2)' }}>{expo.year}</span>
          <span className="mono" style={{ color: 'var(--ink-3)' }}>{expo.role[lang]}</span>
          {expo.venue && <span className="mono" style={{ color: 'var(--ink-3)' }}>{expo.venue}</span>}
        </div>
        {expo.note && (
          <div style={{ marginTop: 10, fontSize: 14.5, fontStyle: 'italic', color: 'var(--ink-2)' }}>
            {expo.note[lang]}
          </div>
        )}
      </header>

      {/* Photo grid */}
      <div className="expo-images">
        {firstExpImg && <img src={IMG_BASE + firstExpImg} alt="" className="img-large" loading="lazy" />}
        {(secondExpImg || thirdExpImg) && (
          <div className="img-medium-row">
            {secondExpImg && <img src={IMG_BASE + secondExpImg} alt="" className="img-medium" loading="lazy" />}
            {thirdExpImg && <img src={IMG_BASE + thirdExpImg} alt="" className="img-medium" loading="lazy" />}
          </div>
        )}
        {restExpImgs.length > 0 && (
          <div className="img-small-grid">
            {restExpImgs.map((img, i) => (
              <img key={i} src={IMG_BASE + img} alt="" className="img-small" loading="lazy" />
            ))}
          </div>
        )}
      </div>

      {/* Description */}
      {expo.description && (expo.description[lang]) ? (
        <p style={{
          marginTop: 56, fontFamily: 'var(--sans)',
          fontSize: 13, lineHeight: 1.755,
          color: 'var(--ink)', maxWidth: 680, textWrap: 'pretty',
        }}>{expo.description[lang]}</p>
      ) : (
        <div style={{
          marginTop: 56, padding: '24px 0',
          color: 'var(--ink-3)', fontStyle: 'italic', fontSize: 15,
          borderTop: '1px solid rgba(26,26,26,0.08)',
        }}>
          {lang === 'pt' ? 'descrição em preparação.' : 'description in preparation.'}
        </div>
      )}

      {/* Ficha técnica */}
      <dl style={{ marginTop: 48, display: 'flex', flexDirection: 'column', gap: 18 }}>
        <MetaRow label={lang === 'pt' ? 'ano' : 'year'} value={expo.year} />
        {expo.venue && <MetaRow label={lang === 'pt' ? 'local' : 'venue'} value={expo.venue} />}
        <MetaRow label={lang === 'pt' ? 'tipologia' : 'type'} value={expo.role[lang]} />
        {expo.note && <MetaRow label={lang === 'pt' ? 'no âmbito de' : 'part of'} value={expo.note[lang]} />}
      </dl>

      <style>{`
        .expo-images { display: flex; flex-direction: column; gap: 16px; }
        .expo-images img { width: 100%; max-width: 100%; display: block; height: auto; max-height: calc(85vh - 160px); object-fit: contain; }
        .expo-images .img-medium-row { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
        .expo-images .img-medium-row > img:nth-child(1):nth-last-child(1) { grid-column: 1 / -1; }
        .expo-images .img-small-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 10px; }
        @media (max-width: 980px) {
          .expo-images .img-medium-row { grid-template-columns: 1fr; }
          .expo-images .img-small-grid { grid-template-columns: 1fr; }
        }
      `}</style>
    </div>
  );
}

// ============================================================================
// SONS — list of sound pieces with inline audio players
// ============================================================================
function PageSons({ lang }) {
  const t = T[lang];
  return (
    <div className="page" style={{ paddingTop: 32 }}>
      <h1 style={{
        fontFamily: 'var(--sans)', fontWeight: 400,
        fontSize: 'clamp(22px, 3vw, 36px)',
        letterSpacing: '-0.02em', marginBottom: 48,
        color: 'var(--ink)',
      }}>{t.nav.sons.toLowerCase()}</h1>

      <div style={{ display: 'flex', flexDirection: 'column', gap: 32 }}>
        {SOUNDS.map((s) => (
          <a
            key={s.id}
            href={`#/som/${s.id}`}
            className="sound-row"
            data-cursor-hover
            style={{
              display: 'flex', flexWrap: 'wrap', alignItems: 'baseline',
              gap: '6px 20px', padding: '26px 0', textDecoration: 'none',
              borderBottom: '1px solid rgba(26,26,26,0.08)',
            }}
          >
            <span className="mono" style={{ color: 'var(--ink-3)', minWidth: '2ch' }}>{s.number}</span>
            <div style={{ flex: 1, minWidth: 0 }}>
              <h2 style={{
                fontFamily: 'var(--sans)', fontWeight: 500,
                fontSize: 'clamp(16px, 2vw, 22px)',
                letterSpacing: '-0.01em', lineHeight: 1.2,
                color: 'var(--ink)', margin: 0,
              }}>{s.title[lang].toLowerCase()}</h2>
            </div>
            <span className="mono" style={{ color: 'var(--ink-3)', fontSize: 12 }}>{s.year}</span>
          </a>
        ))}
      </div>
    </div>
  );
}

function LiveWaveform({ audioRef, seed }) {
  const canvasRef = React.useRef(null);
  const rafRef = React.useRef(null);
  const stateRef = React.useRef({ analyser: null, audioCtx: null, initialized: false });

  React.useEffect(() => {
    const audio = audioRef.current;
    if (!audio) return;
    const s = stateRef.current;

    function setup() {
      if (s.initialized) {
        if (s.audioCtx && s.audioCtx.state === 'suspended') s.audioCtx.resume();
        return;
      }
      s.initialized = true;
      const AudioCtx = window.AudioContext || window.webkitAudioContext;
      if (!AudioCtx) return;
      s.audioCtx = new AudioCtx();
      const source = s.audioCtx.createMediaElementSource(audio);
      s.analyser = s.audioCtx.createAnalyser();
      s.analyser.fftSize = 256;
      s.analyser.smoothingTimeConstant = 0.82;
      source.connect(s.analyser);
      s.analyser.connect(s.audioCtx.destination);
    }

    function drawFrame() {
      const canvas = canvasRef.current;
      if (!canvas || !s.analyser) return;
      rafRef.current = requestAnimationFrame(drawFrame);
      const W = canvas.offsetWidth || 800;
      if (canvas.width !== W) canvas.width = W;
      const H = canvas.height;
      const ctx = canvas.getContext('2d');
      ctx.clearRect(0, 0, W, H);
      const buf = new Uint8Array(s.analyser.frequencyBinCount);
      s.analyser.getByteFrequencyData(buf);
      const barCount = 80;
      const gap = 2;
      const barW = (W - gap * (barCount - 1)) / barCount;
      for (let i = 0; i < barCount; i++) {
        const idx = Math.floor(i * buf.length / barCount);
        const v = buf[idx] / 255;
        const h = Math.max(2, v * H);
        const opacity = 0.1 + v * 0.75;
        ctx.fillStyle = `rgba(26,26,26,${opacity.toFixed(2)})`;
        ctx.fillRect(i * (barW + gap), (H - h) / 2, barW, h);
      }
    }

    function drawIdle() {
      cancelAnimationFrame(rafRef.current);
      const canvas = canvasRef.current;
      if (!canvas) return;
      const W = canvas.offsetWidth || 800;
      if (canvas.width !== W) canvas.width = W;
      const H = canvas.height;
      const ctx = canvas.getContext('2d');
      ctx.clearRect(0, 0, W, H);
      const barCount = 80;
      const gap = 2;
      const barW = (W - gap * (barCount - 1)) / barCount;
      for (let i = 0; i < barCount; i++) {
        const h = 2 + Math.abs(Math.sin(i * 0.4 + seed * 2) * 10);
        ctx.fillStyle = 'rgba(26,26,26,0.12)';
        ctx.fillRect(i * (barW + gap), (H - h) / 2, barW, h);
      }
    }

    function onPlay() { setup(); drawFrame(); }
    function onPause() { cancelAnimationFrame(rafRef.current); drawIdle(); }
    function onEnded() { cancelAnimationFrame(rafRef.current); drawIdle(); }

    audio.addEventListener('play', onPlay);
    audio.addEventListener('pause', onPause);
    audio.addEventListener('ended', onEnded);
    setTimeout(drawIdle, 30);

    return () => {
      cancelAnimationFrame(rafRef.current);
      audio.removeEventListener('play', onPlay);
      audio.removeEventListener('pause', onPause);
      audio.removeEventListener('ended', onEnded);
      try { if (s.audioCtx) s.audioCtx.close(); } catch(e) {}
      s.analyser = null; s.audioCtx = null; s.initialized = false;
    };
  }, []);

  return (
    <canvas ref={canvasRef} height={70}
            style={{ width: '100%', height: 70, display: 'block' }} />
  );
}

function PageSom({ lang, id }) {
  const t = T[lang];
  const audioRef = React.useRef(null);
  const sound = SOUNDS.find(s => s.id === id);
  if (!sound) {
    return (
      <div className="page" style={{ paddingTop: 32 }}>
        <p style={{ color: 'var(--ink-2)' }}>
          {lang === 'pt' ? 'Som não encontrado.' : 'Sound not found.'}
        </p>
        <a href="#/sons" className="link" data-cursor-hover style={{ marginTop: 24, display: 'inline-block' }}>
          {"←"} {t.nav.sons}
        </a>
      </div>
    );
  }

  const seedIdx = SOUNDS.findIndex(s => s.id === id);
  const prev = seedIdx > 0 ? SOUNDS[seedIdx - 1] : null;
  const next = seedIdx < SOUNDS.length - 1 ? SOUNDS[seedIdx + 1] : null;

  return (
    <div className="page" style={{ paddingTop: 24 }}>
      <div className="mono" style={{ color: 'var(--ink-3)', marginBottom: 28 }}>
        <a href="#/sons" className="link" data-cursor-hover>{t.nav.sons.toLowerCase()}</a>
        <span style={{ margin: '0 10px' }}>/</span>
        <span>{sound.number}</span>
      </div>

      <header style={{ marginBottom: 32 }}>
        <h1 style={{
          fontFamily: 'var(--sans)', fontWeight: 400,
          fontSize: 'clamp(23px, 3.5vw, 44px)', letterSpacing: '-0.025em',
          lineHeight: 1.05, color: 'var(--ink)', marginBottom: 14,
        }}>{sound.title[lang].toLowerCase()}</h1>
        <span className="mono" style={{ color: 'var(--ink-2)' }}>{sound.year}</span>
      </header>

      <LiveWaveform audioRef={audioRef} seed={seedIdx} />

      <audio ref={audioRef} controls preload="none" style={{ width: '100%', marginBottom: 40 }}>
        <source src={sound.file} type="audio/wav" />
      </audio>

      <div className="obra-text" style={{ marginTop: 0 }}>
        <p style={{ fontFamily: 'var(--sans)', fontSize: 13, lineHeight: 1.75, color: 'var(--ink)', maxWidth: 720, textWrap: 'pretty' }}>
          {sound.description[lang]}
        </p>
        <dl style={{ display: 'flex', flexDirection: 'column', gap: 18 }}>
          {sound.duration  && <MetaRow label={t.works.duration}  value={sound.duration[lang]} />}
          {sound.materials && <MetaRow label={t.works.materials} value={sound.materials[lang]} />}
        </dl>
      </div>

      <div style={{ marginTop: 80, display: 'flex', justifyContent: 'space-between', flexWrap: 'wrap', gap: 24 }}>
        <div>
          {prev && (
            <a href={`#/som/${prev.id}`} data-cursor-hover className="link" style={{ color: 'var(--ink-2)' }}>
              ← {prev.title[lang].toLowerCase()}
            </a>
          )}
        </div>
        <div>
          {next && (
            <a href={`#/som/${next.id}`} data-cursor-hover className="link" style={{ color: 'var(--ink-2)' }}>
              {next.title[lang].toLowerCase()} →
            </a>
          )}
        </div>
      </div>
    </div>
  );
}

Object.assign(window, {
  PageHome, PageSobre, PageTrabalhos, PageObra, PageExposicoes, PageExposicao, PageContacto, PageCV, PageSons, PageSom
});
